rulesync 16.2.0 → 16.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.cjs +762 -3
- package/dist/cli/index.js +762 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-CArKOPG_.js → import-BwakMyyf.js} +196 -109
- package/dist/import-BwakMyyf.js.map +1 -0
- package/dist/{import-DNfxS6QZ.cjs → import-C9wxpZey.cjs} +224 -107
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +5 -2
- package/dist/import-CArKOPG_.js.map +0 -1
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["SKILL_FILE_NAME","record","assertFrozenLockCoversSources","SKILL_FILE_NAME","parseJsonc","parseToolTarget","fsWatch","SKILL_FILE_NAME","RULESYNC_CONTENT_HASH_REGEX","computeContentHash","RULESYNC_CONTENT_HASH_REGEX","existing","logger","logger","executeConvert","buildSuccessResponse","executeGenerate","buildSuccessResponse","executeImport","logger","logger","SKILL_FILE_NAME","os","path","wrapCommand","_wrapCommand"],"sources":["../../src/utils/parse-comma-separated-list.ts","../../src/lib/feature-scaffold.ts","../../src/lib/npm-sources-lock.ts","../../src/lib/sources-lock.ts","../../src/lib/git-client.ts","../../src/types/fetch-targets.ts","../../src/types/fetch.ts","../../src/lib/github-client.ts","../../src/lib/github-utils.ts","../../src/lib/npm-client.ts","../../src/lib/npm-tar.ts","../../src/types/git-provider.ts","../../src/lib/source-parser.ts","../../src/lib/sources.ts","../../src/cli/commands/add.ts","../../src/utils/result.ts","../../src/cli/commands/convert.ts","../../src/lib/fetch.ts","../../src/cli/commands/fetch.ts","../../src/lib/watch.ts","../../src/cli/commands/generate.ts","../../src/cli/commands/gitignore-derive.ts","../../src/cli/commands/gitignore-entries.ts","../../src/cli/commands/gitignore.ts","../../src/cli/commands/import.ts","../../src/lib/init.ts","../../src/cli/commands/init.ts","../../src/lib/apm/apm-lock.ts","../../src/lib/apm/apm-manifest.ts","../../src/lib/apm/apm-install.ts","../../src/lib/gh/gh-frontmatter.ts","../../src/lib/gh/gh-lock.ts","../../src/lib/gh/gh-paths.ts","../../src/lib/gh/gh-install.ts","../../src/cli/commands/install.ts","../../src/mcp/checks.ts","../../src/mcp/commands.ts","../../src/mcp/convert.ts","../../src/mcp/generate.ts","../../src/mcp/hooks.ts","../../src/mcp/ignore.ts","../../src/mcp/import.ts","../../src/mcp/mcp.ts","../../src/mcp/permissions.ts","../../src/mcp/rules.ts","../../src/mcp/skills.ts","../../src/mcp/subagents.ts","../../src/mcp/tools.ts","../../src/cli/commands/mcp.ts","../../src/cli/commands/resolve-gitignore-targets.ts","../../src/lib/update.ts","../../src/cli/commands/update.ts","../../src/cli/wrap-command.ts","../../src/cli/program.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * Parses a comma-separated string into a trimmed, non-empty array of strings.\n *\n * Handles trailing commas and extra whitespace gracefully.\n *\n * @example\n * parseCommaSeparatedList(\"a, b, c\") // => [\"a\", \"b\", \"c\"]\n * parseCommaSeparatedList(\"a,,b,\") // => [\"a\", \"b\"]\n */\nexport const parseCommaSeparatedList = (value: string): string[] =>\n value\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n","import { join } from \"node:path\";\n\nimport { SKILL_FILE_NAME } from \"../constants/general.js\";\nimport {\n RULESYNC_MCP_SCHEMA_URL,\n RULESYNC_PERMISSIONS_SCHEMA_URL,\n} from \"../constants/rulesync-paths.js\";\nimport { RulesyncCheck } from \"../features/checks/rulesync-check.js\";\nimport { RulesyncCommand } from \"../features/commands/rulesync-command.js\";\nimport { RulesyncHooks } from \"../features/hooks/rulesync-hooks.js\";\nimport { RulesyncIgnore } from \"../features/ignore/rulesync-ignore.js\";\nimport { RulesyncMcp } from \"../features/mcp/rulesync-mcp.js\";\nimport { RulesyncPermissions } from \"../features/permissions/rulesync-permissions.js\";\nimport { RulesyncRule } from \"../features/rules/rulesync-rule.js\";\nimport { RulesyncSkill } from \"../features/skills/rulesync-skill.js\";\nimport { RulesyncSubagent } from \"../features/subagents/rulesync-subagent.js\";\nimport { getRulesyncSourceCandidates } from \"../utils/rulesync-source-path.js\";\n\nexport type ScaffoldFeature =\n | \"rule\"\n | \"command\"\n | \"subagent\"\n | \"skill\"\n | \"check\"\n | \"mcp\"\n | \"hooks\"\n | \"ignore\"\n | \"permissions\";\n\nexport type FeatureScaffold = {\n feature: ScaffoldFeature;\n relativeFilePath: string;\n candidateRelativeFilePaths: string[];\n content: string;\n};\n\nconst FEATURE_KEYWORDS = new Map<string, ScaffoldFeature>([\n [\"rule\", \"rule\"],\n [\"rules\", \"rule\"],\n [\"command\", \"command\"],\n [\"commands\", \"command\"],\n [\"subagent\", \"subagent\"],\n [\"subagents\", \"subagent\"],\n [\"skill\", \"skill\"],\n [\"skills\", \"skill\"],\n [\"check\", \"check\"],\n [\"checks\", \"check\"],\n [\"mcp\", \"mcp\"],\n [\"hook\", \"hooks\"],\n [\"hooks\", \"hooks\"],\n [\"ignore\", \"ignore\"],\n [\"permission\", \"permissions\"],\n [\"permissions\", \"permissions\"],\n]);\n\nconst NAMED_FEATURES = new Set<ScaffoldFeature>([\"rule\", \"command\", \"subagent\", \"skill\", \"check\"]);\n\nexport function parseScaffoldFeatureKeyword(value: string): ScaffoldFeature | undefined {\n return FEATURE_KEYWORDS.get(value.toLowerCase());\n}\n\nexport function isNamedScaffoldFeature(feature: ScaffoldFeature): boolean {\n return NAMED_FEATURES.has(feature);\n}\n\nexport function normalizeScaffoldName({\n feature,\n name,\n}: {\n feature: ScaffoldFeature;\n name: string | undefined;\n}): string | undefined {\n if (!isNamedScaffoldFeature(feature)) {\n if (name !== undefined) {\n throw new Error(`Feature \"${feature}\" does not accept --name.`);\n }\n return undefined;\n }\n\n if (name === undefined || name.trim() === \"\") {\n throw new Error(`Feature \"${feature}\" requires --name <name>.`);\n }\n\n const normalized = name.trim().replace(/\\.md$/i, \"\");\n if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(normalized)) {\n throw new Error(\n `Invalid ${feature} name \"${name}\". Use letters, numbers, dots, underscores, or hyphens without path separators.`,\n );\n }\n return normalized;\n}\n\nfunction titleFromName(name: string): string {\n return name\n .split(/[-_.]+/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\" \");\n}\n\nfunction namedFeatureScaffold({\n feature,\n relativeFilePath,\n content,\n}: {\n feature: ScaffoldFeature;\n relativeFilePath: string;\n content: string;\n}): FeatureScaffold {\n return {\n feature,\n relativeFilePath,\n candidateRelativeFilePaths: [relativeFilePath],\n content,\n };\n}\n\nfunction ruleTemplate(name: string): string {\n if (name === \"overview\") {\n return `---\nroot: true\ntargets: [\"*\"]\ndescription: \"Project overview and general development guidelines\"\nglobs: [\"**/*\"]\n---\n\n# Project Overview\n\n## General Guidelines\n\n- Use TypeScript for all new code\n- Follow consistent naming conventions\n- Write self-documenting code with clear variable and function names\n- Prefer composition over inheritance\n- Use meaningful comments for complex business logic\n\n## Code Style\n\n- Use 2 spaces for indentation\n- Use semicolons\n- Use double quotes for strings\n- Use trailing commas in multi-line objects and arrays\n\n## Architecture Principles\n\n- Organize code by feature, not by file type\n- Keep related files close together\n- Use dependency injection for better testability\n- Implement proper error handling\n- Follow single responsibility principle\n`;\n }\n\n const title = titleFromName(name);\n return `---\nroot: false\ntargets: [\"*\"]\ndescription: \"${title} guidelines\"\nglobs: [\"**/*\"]\n---\n\n# ${title}\n\nDescribe the project guidance that should apply when the configured globs match.\n`;\n}\n\nfunction commandTemplate(name: string): string {\n if (name === \"review-pr\") {\n return `---\ndescription: 'Review a pull request'\ntargets: [\"*\"]\n---\n\ntarget_pr = $ARGUMENTS\n\nIf target_pr is not provided, use the PR of the current branch.\n\nExecute the following in parallel:\n\n1. Check code quality and style consistency\n2. Review test coverage\n3. Verify documentation updates\n4. Check for potential bugs or security issues\n\nThen provide a summary of findings and suggestions for improvement.\n`;\n }\n\n const title = titleFromName(name);\n return `---\ndescription: \"Run the ${title} workflow\"\ntargets: [\"*\"]\n---\n\n# ${title}\n\nUse $ARGUMENTS as input and describe the steps this command should perform.\n`;\n}\n\nfunction subagentTemplate(name: string): string {\n if (name === \"planner\") {\n return `---\nname: planner\ntargets: [\"*\"]\ndescription: >-\n This is the general-purpose planner. The user asks the agent to plan to\n suggest a specification, implement a new feature, refactor the codebase, or\n fix a bug. This agent can be called by the user explicitly only.\nclaudecode:\n model: inherit\n---\n\nYou are the planner for any tasks.\n\nBased on the user's instruction, create a plan while analyzing the related files. Then, report the plan in detail. You can output files to @tmp/ if needed.\n\nAttention, again, you are just the planner, so though you can read any files and run any commands for analysis, please don't write any code.\n`;\n }\n\n const title = titleFromName(name);\n return `---\nname: ${JSON.stringify(name)}\ntargets: [\"*\"]\ndescription: \"${title} specialist\"\n---\n\nYou are the ${title} specialist. Describe the role, constraints, and expected output here.\n`;\n}\n\nfunction skillTemplate(name: string): string {\n if (name === \"project-context\") {\n return `---\nname: project-context\ndescription: \"Summarize the project context and key constraints\"\ntargets: [\"*\"]\n---\n\nSummarize the project goals, core constraints, and relevant dependencies.\nCall out any architecture decisions, shared conventions, and validation steps.\nKeep the summary concise and ready to reuse in future tasks.`;\n }\n\n const title = titleFromName(name);\n return `---\nname: ${JSON.stringify(name)}\ndescription: \"Use ${title} guidance for relevant tasks\"\ntargets: [\"*\"]\n---\n\n# ${title}\n\nDescribe when to use this skill and the workflow it should follow.\n`;\n}\n\nfunction checkTemplate(name: string): string {\n const title = titleFromName(name);\n return `---\ntargets: [\"*\"]\ndescription: \"${title} review criteria\"\nseverity: medium\n---\n\n# ${title}\n\nDescribe the conditions this check should detect and the evidence it should report.\n`;\n}\n\nfunction singletonTemplate(feature: ScaffoldFeature): string {\n switch (feature) {\n case \"mcp\":\n return `{\n \"$schema\": \"${RULESYNC_MCP_SCHEMA_URL}\",\n \"mcpServers\": {\n \"deepwiki\": {\n \"type\": \"http\",\n \"url\": \"https://mcp.deepwiki.com/mcp\",\n \"env\": {}\n },\n \"rulesync\": {\n \"type\": \"stdio\",\n \"command\": \"pnpm\",\n \"args\": [\n \"dlx\",\n \"rulesync\",\n \"mcp\"\n ],\n \"env\": {}\n },\n \"playwright\": {\n \"type\": \"stdio\",\n \"command\": \"pnpm\",\n \"args\": [\n \"dlx\",\n \"@playwright/mcp\",\n \"--headless\"\n ],\n \"env\": {}\n }\n }\n}\n`;\n case \"hooks\":\n return `{\n \"version\": 1,\n \"hooks\": {\n \"postToolUse\": [\n {\n \"matcher\": \"Write|Edit\",\n \"command\": \".rulesync/hooks/format.sh\"\n }\n ]\n }\n}\n`;\n case \"ignore\":\n return `credentials/\n`;\n case \"permissions\":\n return `{\n \"$schema\": \"${RULESYNC_PERMISSIONS_SCHEMA_URL}\",\n \"permission\": {\n \"bash\": {\n \"git status\": \"allow\",\n \"git diff\": \"allow\",\n \"ls *\": \"allow\",\n \"rm -rf *\": \"deny\",\n \"*\": \"ask\"\n },\n \"edit\": {\n \"src/**\": \"allow\"\n },\n \"read\": {\n \".env\": \"deny\",\n \"credentials/**\": \"deny\"\n }\n },\n \"codexcli\": {\n \"approval_policy\": \"on-request\",\n \"approvals_reviewer\": \"auto_review\",\n \"base_permission_profile\": \":danger-full-access\"\n }\n}\n`;\n default:\n throw new Error(`Feature \"${feature}\" requires a name.`);\n }\n}\n\nexport function createFeatureScaffold({\n feature,\n name,\n}: {\n feature: ScaffoldFeature;\n name?: string;\n}): FeatureScaffold {\n const normalizedName = normalizeScaffoldName({ feature, name });\n\n switch (feature) {\n case \"rule\":\n return namedFeatureScaffold({\n feature,\n relativeFilePath: join(\n RulesyncRule.getSettablePaths().recommended.relativeDirPath,\n `${normalizedName}.md`,\n ),\n content: ruleTemplate(normalizedName!),\n });\n case \"command\":\n return namedFeatureScaffold({\n feature,\n relativeFilePath: join(\n RulesyncCommand.getSettablePaths().relativeDirPath,\n `${normalizedName}.md`,\n ),\n content: commandTemplate(normalizedName!),\n });\n case \"subagent\":\n return namedFeatureScaffold({\n feature,\n relativeFilePath: join(\n RulesyncSubagent.getSettablePaths().relativeDirPath,\n `${normalizedName}.md`,\n ),\n content: subagentTemplate(normalizedName!),\n });\n case \"skill\":\n return namedFeatureScaffold({\n feature,\n relativeFilePath: join(\n RulesyncSkill.getSettablePaths().relativeDirPath,\n normalizedName!,\n SKILL_FILE_NAME,\n ),\n content: skillTemplate(normalizedName!),\n });\n case \"check\":\n return namedFeatureScaffold({\n feature,\n relativeFilePath: join(\n RulesyncCheck.getSettablePaths().relativeDirPath,\n `${normalizedName}.md`,\n ),\n content: checkTemplate(normalizedName!),\n });\n case \"mcp\": {\n const paths = RulesyncMcp.getSettablePaths();\n const relativeFilePath = join(\n paths.recommended.relativeDirPath,\n paths.recommended.relativeFilePath,\n );\n return {\n feature,\n relativeFilePath,\n candidateRelativeFilePaths: getRulesyncSourceCandidates({ paths }).map((candidate) =>\n join(candidate.relativeDirPath, candidate.relativeFilePath),\n ),\n content: singletonTemplate(feature),\n };\n }\n case \"hooks\": {\n const paths = RulesyncHooks.getSettablePaths();\n const relativeFilePath = join(\n paths.recommended.relativeDirPath,\n paths.recommended.relativeFilePath,\n );\n return {\n feature,\n relativeFilePath,\n candidateRelativeFilePaths: getRulesyncSourceCandidates({ paths }).map((candidate) =>\n join(candidate.relativeDirPath, candidate.relativeFilePath),\n ),\n content: singletonTemplate(feature),\n };\n }\n case \"ignore\": {\n const paths = RulesyncIgnore.getSettablePaths();\n const relativeFilePath = join(\n paths.recommended.relativeDirPath,\n paths.recommended.relativeFilePath,\n );\n return {\n feature,\n relativeFilePath,\n candidateRelativeFilePaths: [\n relativeFilePath,\n ...(paths.legacy\n ? [join(paths.legacy.relativeDirPath, paths.legacy.relativeFilePath)]\n : []),\n ],\n content: singletonTemplate(feature),\n };\n }\n case \"permissions\": {\n const paths = RulesyncPermissions.getSettablePaths();\n const relativeFilePath = join(\n paths.recommended.relativeDirPath,\n paths.recommended.relativeFilePath,\n );\n return {\n feature,\n relativeFilePath,\n candidateRelativeFilePaths: getRulesyncSourceCandidates({ paths }).map((candidate) =>\n join(candidate.relativeDirPath, candidate.relativeFilePath),\n ),\n content: singletonTemplate(feature),\n };\n }\n }\n}\n","import { join } from \"node:path\";\n\nimport { optional, z } from \"zod/mini\";\n\nimport { RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH } from \"../constants/rulesync-paths.js\";\nimport { fileExists, readFileContent, writeFileContent } from \"../utils/file.js\";\nimport type { Logger } from \"../utils/logger.js\";\n\n/**\n * Lockfile for npm-transport sources (EXPERIMENTAL), written to\n * `rulesync-npm.lock.json` at the project root. Kept separate from the main\n * `rulesync.lock` because that lockfile pins git commit SHAs, while npm\n * sources pin a resolved package version plus the registry tarball integrity.\n * Mirrors the conventions of the gh (`rulesync-gh.lock.yaml`) and apm\n * (`rulesync-apm.lock.yaml`) lockfiles, which are also mode/transport-specific.\n */\n\n/** Current npm lockfile format version. Bump when the schema changes. */\nexport const NPM_LOCKFILE_VERSION = 1;\n\nconst NpmLockedSkillSchema = z.object({\n integrity: z.string(),\n});\n\n/**\n * Schema for a single locked npm source entry.\n */\nconst NpmLockedSourceSchema = z.object({\n registry: optional(z.string()),\n requestedVersion: optional(z.string()),\n resolvedVersion: z.string(),\n /** SRI integrity of the package tarball as reported by the registry. */\n integrity: optional(z.string()),\n resolvedAt: optional(z.string()),\n skills: z.record(z.string(), NpmLockedSkillSchema),\n rules: optional(z.record(z.string(), NpmLockedSkillSchema)),\n ruleSelection: optional(z.array(z.string())),\n rulesPath: optional(z.string()),\n resolvedRuleNames: optional(z.array(z.string())),\n});\nexport type NpmLockedSource = z.infer<typeof NpmLockedSourceSchema>;\n\nconst NpmSourcesLockSchema = z.object({\n lockfileVersion: z.number(),\n sources: z.record(z.string(), NpmLockedSourceSchema),\n});\nexport type NpmSourcesLock = z.infer<typeof NpmSourcesLockSchema>;\n\n/**\n * Create an empty npm lockfile structure.\n */\nexport function createEmptyNpmLock(): NpmSourcesLock {\n return { lockfileVersion: NPM_LOCKFILE_VERSION, sources: {} };\n}\n\n/**\n * Read the npm lockfile from disk.\n * @returns The parsed lockfile, or an empty lockfile if it doesn't exist or is invalid.\n */\nexport async function readNpmLockFile(params: {\n projectRoot: string;\n logger: Logger;\n}): Promise<NpmSourcesLock> {\n const { logger } = params;\n const lockPath = join(params.projectRoot, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH);\n\n if (!(await fileExists(lockPath))) {\n logger.debug(\"No npm sources lockfile found, starting fresh.\");\n return createEmptyNpmLock();\n }\n\n try {\n const content = await readFileContent(lockPath);\n const result = NpmSourcesLockSchema.safeParse(JSON.parse(content));\n if (result.success) {\n return result.data;\n }\n logger.warn(\n `Invalid npm sources lockfile format (${RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH}). Starting fresh.`,\n );\n return createEmptyNpmLock();\n } catch {\n logger.warn(\n `Failed to read npm sources lockfile (${RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH}). Starting fresh.`,\n );\n return createEmptyNpmLock();\n }\n}\n\n/**\n * Write the npm lockfile to disk.\n */\nexport async function writeNpmLockFile(params: {\n projectRoot: string;\n lock: NpmSourcesLock;\n logger: Logger;\n}): Promise<void> {\n const { logger } = params;\n const lockPath = join(params.projectRoot, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH);\n const content = JSON.stringify(params.lock, null, 2) + \"\\n\";\n await writeFileContent(lockPath, content);\n logger.debug(`Wrote npm sources lockfile to ${lockPath}`);\n}\n\n/**\n * Normalize an npm source key (package name) for lockfile lookups.\n */\nexport function normalizeNpmSourceKey(source: string): string {\n return source.trim();\n}\n\n/**\n * Get the locked entry for an npm source key, if it exists.\n */\nexport function getNpmLockedSource(\n lock: NpmSourcesLock,\n sourceKey: string,\n): NpmLockedSource | undefined {\n const normalized = normalizeNpmSourceKey(sourceKey);\n return Object.prototype.hasOwnProperty.call(lock.sources, normalized)\n ? lock.sources[normalized]\n : undefined;\n}\n\n/**\n * Set (or update) a locked entry for an npm source key (immutable).\n */\nexport function setNpmLockedSource(\n lock: NpmSourcesLock,\n sourceKey: string,\n entry: NpmLockedSource,\n): NpmSourcesLock {\n return {\n lockfileVersion: lock.lockfileVersion,\n sources: {\n ...lock.sources,\n [normalizeNpmSourceKey(sourceKey)]: entry,\n },\n };\n}\n\n/**\n * Get the skill names from a locked npm source entry.\n */\nexport function getNpmLockedSkillNames(entry: NpmLockedSource): string[] {\n return Object.keys(entry.skills);\n}\n\n/** Get the rule names from a locked npm source entry. */\nexport function getNpmLockedRuleNames(entry: NpmLockedSource): string[] {\n return Object.keys(entry.rules ?? {});\n}\n","import { createHash } from \"node:crypto\";\nimport { join } from \"node:path\";\n\nimport { optional, refine, z } from \"zod/mini\";\n\nimport { RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH } from \"../constants/rulesync-paths.js\";\nimport { fileExists, readFileContent, writeFileContent } from \"../utils/file.js\";\nimport type { Logger } from \"../utils/logger.js\";\n\n/** Current lockfile format version. Bump when the schema changes. */\nexport const LOCKFILE_VERSION = 1;\n\n/**\n * Schema for a single locked skill entry with content integrity.\n */\nconst LockedSkillSchema = z.object({\n integrity: z.string(),\n});\nexport type LockedSkill = z.infer<typeof LockedSkillSchema>;\n\n/** Schema for a single locked rule entry with content integrity. */\nconst LockedRuleSchema = z.object({\n integrity: z.string(),\n});\nexport type LockedRule = z.infer<typeof LockedRuleSchema>;\n\n/**\n * Schema for a single locked source entry.\n */\nconst LockedSourceSchema = z.object({\n requestedRef: optional(z.string()),\n resolvedRef: z\n .string()\n .check(refine((v) => /^[0-9a-f]{40}$/.test(v), \"resolvedRef must be a 40-character hex SHA\")),\n resolvedAt: optional(z.string()),\n skills: z.record(z.string(), LockedSkillSchema),\n rules: optional(z.record(z.string(), LockedRuleSchema)),\n ruleSelection: optional(z.array(z.string())),\n rulesPath: optional(z.string()),\n resolvedRuleNames: optional(z.array(z.string())),\n});\nexport type LockedSource = z.infer<typeof LockedSourceSchema>;\n\n/**\n * Schema for the full lockfile (current version).\n */\nconst SourcesLockSchema = z.object({\n lockfileVersion: z.number(),\n sources: z.record(z.string(), LockedSourceSchema),\n});\nexport type SourcesLock = z.infer<typeof SourcesLockSchema>;\n\n/**\n * Schema for the legacy v0 lockfile format (skills as string array, no version field).\n */\nconst LegacyLockedSourceSchema = z.object({\n resolvedRef: z.string(),\n skills: z.array(z.string()),\n});\n\nconst LegacySourcesLockSchema = z.object({\n sources: z.record(z.string(), LegacyLockedSourceSchema),\n});\n\n/**\n * Migrate a legacy lockfile (string[] skills, no version) to the current format.\n * Skills get empty integrity since we can't compute it retroactively.\n */\nfunction migrateLegacyLock(params: {\n legacy: z.infer<typeof LegacySourcesLockSchema>;\n logger: Logger;\n}): SourcesLock {\n const { legacy, logger } = params;\n const sources: Record<string, LockedSource> = {};\n for (const [key, entry] of Object.entries(legacy.sources)) {\n const skills: Record<string, LockedSkill> = {};\n for (const name of entry.skills) {\n skills[name] = { integrity: \"\" };\n }\n sources[key] = {\n resolvedRef: entry.resolvedRef,\n skills,\n };\n }\n logger.info(\n \"Migrated legacy sources lockfile to version 1. Run 'rulesync install --update' to populate integrity hashes.\",\n );\n return { lockfileVersion: LOCKFILE_VERSION, sources };\n}\n\n/**\n * Create an empty lockfile structure.\n */\nexport function createEmptyLock(): SourcesLock {\n return { lockfileVersion: LOCKFILE_VERSION, sources: {} };\n}\n\n/**\n * Read the lockfile from disk.\n * @returns The parsed lockfile, or an empty lockfile if it doesn't exist or is invalid.\n */\nexport async function readLockFile(params: {\n projectRoot: string;\n logger: Logger;\n}): Promise<SourcesLock> {\n const { logger } = params;\n const lockPath = join(params.projectRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH);\n\n if (!(await fileExists(lockPath))) {\n logger.debug(\"No sources lockfile found, starting fresh.\");\n return createEmptyLock();\n }\n\n try {\n const content = await readFileContent(lockPath);\n const data = JSON.parse(content);\n\n // Try current schema first\n const result = SourcesLockSchema.safeParse(data);\n if (result.success) {\n return result.data;\n }\n\n // Try legacy schema (no lockfileVersion, skills as string[])\n const legacyResult = LegacySourcesLockSchema.safeParse(data);\n if (legacyResult.success) {\n return migrateLegacyLock({ legacy: legacyResult.data, logger });\n }\n\n logger.warn(\n `Invalid sources lockfile format (${RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH}). Starting fresh.`,\n );\n return createEmptyLock();\n } catch {\n logger.warn(\n `Failed to read sources lockfile (${RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH}). Starting fresh.`,\n );\n return createEmptyLock();\n }\n}\n\n/**\n * Write the lockfile to disk.\n */\nexport async function writeLockFile(params: {\n projectRoot: string;\n lock: SourcesLock;\n logger: Logger;\n}): Promise<void> {\n const { logger } = params;\n const lockPath = join(params.projectRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH);\n const content = JSON.stringify(params.lock, null, 2) + \"\\n\";\n await writeFileContent(lockPath, content);\n logger.debug(`Wrote sources lockfile to ${lockPath}`);\n}\n\n/**\n * Compute a SHA-256 integrity hash for a skill's contents.\n * Takes a sorted list of [relativePath, content] pairs to produce a deterministic hash.\n */\nexport function computeSkillIntegrity(files: Array<{ path: string; content: string }>): string {\n const hash = createHash(\"sha256\");\n // Sort by path for deterministic ordering\n const sorted = files.toSorted((a, b) => a.path.localeCompare(b.path));\n for (const file of sorted) {\n hash.update(file.path);\n hash.update(\"\\0\");\n hash.update(file.content);\n hash.update(\"\\0\");\n }\n return `sha256-${hash.digest(\"hex\")}`;\n}\n\n/** Compute a SHA-256 integrity hash for one rule file. */\nexport function computeRuleIntegrity(content: string): string {\n const hash = createHash(\"sha256\");\n hash.update(content);\n return `sha256-${hash.digest(\"hex\")}`;\n}\n\n/**\n * Normalize a source key for consistent lockfile lookups.\n * Strips URL prefixes, provider prefixes, trailing slashes, .git suffix, and lowercases.\n */\nexport function normalizeSourceKey(source: string): string {\n let key = source;\n\n // Strip common URL prefixes\n for (const prefix of [\n \"https://www.github.com/\",\n \"https://github.com/\",\n \"http://www.github.com/\",\n \"http://github.com/\",\n \"https://www.gitlab.com/\",\n \"https://gitlab.com/\",\n \"http://www.gitlab.com/\",\n \"http://gitlab.com/\",\n ]) {\n if (key.toLowerCase().startsWith(prefix)) {\n key = key.substring(prefix.length);\n break;\n }\n }\n\n // Strip provider prefix\n for (const provider of [\"github:\", \"gitlab:\"]) {\n if (key.startsWith(provider)) {\n key = key.substring(provider.length);\n break;\n }\n }\n\n // Remove trailing slashes\n key = key.replace(/\\/+$/, \"\");\n\n // Remove .git suffix from repo\n key = key.replace(/\\.git$/, \"\");\n\n // Lowercase for case-insensitive matching\n key = key.toLowerCase();\n\n return key;\n}\n\n/**\n * Get the locked entry for a source key, if it exists.\n */\nexport function getLockedSource(lock: SourcesLock, sourceKey: string): LockedSource | undefined {\n const normalized = normalizeSourceKey(sourceKey);\n // Look up by normalized key\n for (const [key, value] of Object.entries(lock.sources)) {\n if (normalizeSourceKey(key) === normalized) {\n return value;\n }\n }\n return undefined;\n}\n\n/**\n * Set (or update) a locked entry for a source key.\n */\nexport function setLockedSource(\n lock: SourcesLock,\n sourceKey: string,\n entry: LockedSource,\n): SourcesLock {\n const normalized = normalizeSourceKey(sourceKey);\n // Remove any existing entries with the same normalized key\n const filteredSources: Record<string, LockedSource> = {};\n for (const [key, value] of Object.entries(lock.sources)) {\n if (normalizeSourceKey(key) !== normalized) {\n filteredSources[key] = value;\n }\n }\n return {\n lockfileVersion: lock.lockfileVersion,\n sources: {\n ...filteredSources,\n [normalized]: entry,\n },\n };\n}\n\n/**\n * Get the skill names from a locked source entry.\n */\nexport function getLockedSkillNames(entry: LockedSource): string[] {\n return Object.keys(entry.skills);\n}\n\n/** Get the rule names from a locked source entry. */\nexport function getLockedRuleNames(entry: LockedSource): string[] {\n return Object.keys(entry.rules ?? {});\n}\n","import { execFile } from \"node:child_process\";\nimport { isAbsolute, join, posix, relative } from \"node:path\";\nimport { promisify } from \"node:util\";\n\nimport { MAX_FILE_SIZE } from \"../constants/rulesync-paths.js\";\nimport {\n createTempDirectory,\n directoryExists,\n getFileSize,\n isSymlink,\n listDirectoryFiles,\n readFileContent,\n removeTempDirectory,\n} from \"../utils/file.js\";\nimport type { Logger } from \"../utils/logger.js\";\nimport { findControlCharacter } from \"../utils/validation.js\";\n\nconst execFileAsync = promisify(execFile);\n\n/** Timeout for all git CLI operations (60 seconds). */\nconst GIT_TIMEOUT_MS = 60_000;\n\nconst ALLOWED_URL_SCHEMES =\n /^(https?:\\/\\/|ssh:\\/\\/|git:\\/\\/|file:\\/\\/\\/).+$|^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9.-]+:[a-zA-Z0-9_.+/~-]+$/;\n\nconst INSECURE_URL_SCHEMES = /^(git:\\/\\/|http:\\/\\/)/;\n\nexport class GitClientError extends Error {\n constructor(message: string, cause?: unknown) {\n super(message, { cause });\n this.name = \"GitClientError\";\n }\n}\n\nexport function validateGitUrl(url: string, options?: { logger?: Logger }): void {\n const ctrl = findControlCharacter(url);\n if (ctrl) {\n throw new GitClientError(\n `Git URL contains control character ${ctrl.hex} at position ${ctrl.position}`,\n );\n }\n if (!ALLOWED_URL_SCHEMES.test(url)) {\n throw new GitClientError(\n `Unsupported or unsafe git URL: \"${url}\". Use https, ssh, git, or file schemes.`,\n );\n }\n if (INSECURE_URL_SCHEMES.test(url)) {\n options?.logger?.warn(\n `URL \"${url}\" uses an unencrypted protocol. Consider using https:// or ssh:// instead.`,\n );\n }\n}\n\n/**\n * Validate a ref string before passing to git commands.\n * Rejects refs that start with \"-\" or contain control characters.\n */\nexport function validateRef(ref: string): void {\n if (ref.startsWith(\"-\")) {\n throw new GitClientError(`Ref must not start with \"-\": \"${ref}\"`);\n }\n const ctrl = findControlCharacter(ref);\n if (ctrl) {\n throw new GitClientError(\n `Ref contains control character ${ctrl.hex} at position ${ctrl.position}`,\n );\n }\n}\n\nlet gitChecked = false;\n\nexport async function checkGitAvailable(): Promise<void> {\n if (gitChecked) return;\n try {\n await execFileAsync(\"git\", [\"--version\"], { timeout: GIT_TIMEOUT_MS });\n gitChecked = true;\n } catch {\n throw new GitClientError(\"git is not installed or not found in PATH\");\n }\n}\n\n/** Reset the cached git availability check (for testing). */\nexport function resetGitCheck(): void {\n gitChecked = false;\n}\n\nexport async function resolveDefaultRef(url: string): Promise<{ ref: string; sha: string }> {\n validateGitUrl(url);\n await checkGitAvailable();\n try {\n const { stdout } = await execFileAsync(\"git\", [\"ls-remote\", \"--symref\", \"--\", url, \"HEAD\"], {\n timeout: GIT_TIMEOUT_MS,\n });\n const ref = stdout.match(/^ref: refs\\/heads\\/(.+)\\tHEAD$/m)?.[1];\n const sha = stdout.match(/^([0-9a-f]{40})\\tHEAD$/m)?.[1];\n if (!ref || !sha) throw new GitClientError(`Could not parse default branch from: ${url}`);\n validateRef(ref);\n return { ref, sha };\n } catch (error) {\n if (error instanceof GitClientError) throw error;\n throw new GitClientError(`Failed to resolve default ref for ${url}`, error);\n }\n}\n\nexport async function resolveRefToSha(url: string, ref: string): Promise<string> {\n validateGitUrl(url);\n validateRef(ref);\n await checkGitAvailable();\n try {\n const { stdout } = await execFileAsync(\"git\", [\"ls-remote\", \"--\", url, ref], {\n timeout: GIT_TIMEOUT_MS,\n });\n const sha = stdout.match(/^([0-9a-f]{40})\\t/m)?.[1];\n if (!sha) throw new GitClientError(`Ref \"${ref}\" not found in ${url}`);\n return sha;\n } catch (error) {\n if (error instanceof GitClientError) throw error;\n throw new GitClientError(`Failed to resolve ref \"${ref}\" for ${url}`, error);\n }\n}\n\n/**\n * Clone a repo at the given branch or tag and return all files under skillsPath.\n * When `resolvedRef` is provided, fetch and check out that exact commit before\n * reading files so a mutable branch cannot drift from its lockfile SHA.\n */\nexport async function fetchSkillFiles(params: {\n url: string;\n ref: string;\n resolvedRef?: string;\n skillsPath: string;\n logger?: Logger;\n}): Promise<Array<{ relativePath: string; content: string; size: number }>> {\n const { url, ref, resolvedRef, skillsPath, logger } = params;\n validateGitUrl(url, { logger });\n validateRef(ref);\n if (resolvedRef !== undefined && !/^[0-9a-f]{40}$/.test(resolvedRef)) {\n throw new GitClientError(`Invalid resolvedRef \"${resolvedRef}\": expected a commit SHA`);\n }\n if (skillsPath.split(/[/\\\\]/).includes(\"..\") || isAbsolute(skillsPath)) {\n throw new GitClientError(\n `Invalid skillsPath \"${skillsPath}\": must be a relative path without \"..\"`,\n );\n }\n const ctrl = findControlCharacter(skillsPath);\n if (ctrl) {\n throw new GitClientError(\n `skillsPath contains control character ${ctrl.hex} at position ${ctrl.position}`,\n );\n }\n await checkGitAvailable();\n const tmpDir = await createTempDirectory(\"rulesync-git-\");\n // Treat empty/\".\" paths as the repository root. Cone-mode sparse-checkout\n // with such patterns only restores the top-level files (it intentionally\n // excludes any subdirectory), so we must check out the entire working tree\n // instead. Otherwise repositories whose skills live directly at the root\n // (e.g. `<repo>/<skill-name>/SKILL.md` without a `skills/` container)\n // would only yield root-level files like README.md.\n // Normalize first so variants like \"./.\", \".//\", or Windows \".\\\\\" are also\n // recognized as the root and don't fall back to the (buggy) sparse-checkout path.\n const normalizedSkillsPath = posix.normalize(skillsPath.replace(/\\\\/g, \"/\")).replace(/\\/+$/, \"\");\n const isRootPath = normalizedSkillsPath === \"\" || normalizedSkillsPath === \".\";\n try {\n await execFileAsync(\n \"git\",\n [\n \"clone\",\n \"--depth\",\n \"1\",\n \"--branch\",\n ref,\n \"--no-checkout\",\n \"--filter=blob:none\",\n \"--\",\n url,\n tmpDir,\n ],\n { timeout: GIT_TIMEOUT_MS },\n );\n if (resolvedRef !== undefined) {\n await execFileAsync(\"git\", [\"-C\", tmpDir, \"fetch\", \"--depth\", \"1\", \"origin\", resolvedRef], {\n timeout: GIT_TIMEOUT_MS,\n });\n }\n if (isRootPath) {\n // Disable sparse-checkout and restore the full tree.\n await execFileAsync(\"git\", [\"-C\", tmpDir, \"sparse-checkout\", \"disable\"], {\n timeout: GIT_TIMEOUT_MS,\n });\n } else {\n await execFileAsync(\"git\", [\"-C\", tmpDir, \"sparse-checkout\", \"set\", \"--\", skillsPath], {\n timeout: GIT_TIMEOUT_MS,\n });\n }\n await execFileAsync(\n \"git\",\n resolvedRef === undefined\n ? [\"-C\", tmpDir, \"checkout\"]\n : [\"-C\", tmpDir, \"checkout\", \"--detach\", resolvedRef],\n { timeout: GIT_TIMEOUT_MS },\n );\n if (resolvedRef !== undefined) {\n const { stdout } = await execFileAsync(\"git\", [\"-C\", tmpDir, \"rev-parse\", \"HEAD\"], {\n timeout: GIT_TIMEOUT_MS,\n });\n if (stdout.trim() !== resolvedRef) {\n throw new GitClientError(\n `Checked out commit ${stdout.trim() || \"(unknown)\"}, expected locked commit ${resolvedRef}`,\n );\n }\n }\n const skillsDir = isRootPath ? tmpDir : join(tmpDir, skillsPath);\n if (!(await directoryExists(skillsDir))) return [];\n return await walkDirectory(skillsDir, skillsDir, 0, { totalFiles: 0, totalSize: 0 }, logger);\n } catch (error) {\n if (error instanceof GitClientError) throw error;\n throw new GitClientError(`Failed to fetch skill files from ${url}`, error);\n } finally {\n await removeTempDirectory(tmpDir);\n }\n}\n\nconst MAX_WALK_DEPTH = 20;\nconst MAX_TOTAL_FILES = 10_000;\nconst MAX_TOTAL_SIZE = 100 * 1024 * 1024; // 100 MB\n\n/** Mutable context for tracking totals across recursive walkDirectory calls. */\ntype WalkContext = { totalFiles: number; totalSize: number };\n\nasync function walkDirectory(\n dir: string,\n outputRoot: string,\n depth: number = 0,\n ctx: WalkContext = { totalFiles: 0, totalSize: 0 },\n logger?: Logger,\n): Promise<Array<{ relativePath: string; content: string; size: number }>> {\n if (depth > MAX_WALK_DEPTH) {\n throw new GitClientError(\n `Directory tree exceeds max depth of ${MAX_WALK_DEPTH}: \"${dir}\". Aborting to prevent resource exhaustion.`,\n );\n }\n const results: Array<{ relativePath: string; content: string; size: number }> = [];\n for (const name of await listDirectoryFiles(dir)) {\n if (name === \".git\") continue;\n const fullPath = join(dir, name);\n if (await isSymlink(fullPath)) {\n logger?.warn(`Skipping symlink \"${fullPath}\".`);\n continue;\n }\n if (await directoryExists(fullPath)) {\n results.push(...(await walkDirectory(fullPath, outputRoot, depth + 1, ctx, logger)));\n } else {\n const size = await getFileSize(fullPath);\n if (size > MAX_FILE_SIZE) {\n logger?.warn(\n `Skipping file \"${fullPath}\" (${(size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit).`,\n );\n continue;\n }\n ctx.totalFiles++;\n ctx.totalSize += size;\n if (ctx.totalFiles >= MAX_TOTAL_FILES) {\n throw new GitClientError(\n `Repository exceeds max file count of ${MAX_TOTAL_FILES}. Aborting to prevent resource exhaustion.`,\n );\n }\n if (ctx.totalSize >= MAX_TOTAL_SIZE) {\n throw new GitClientError(\n `Repository exceeds max total size of ${MAX_TOTAL_SIZE / 1024 / 1024}MB. Aborting to prevent resource exhaustion.`,\n );\n }\n const content = await readFileContent(fullPath);\n results.push({ relativePath: relative(outputRoot, fullPath), content, size });\n }\n }\n return results;\n}\n","import { z } from \"zod/mini\";\n\nimport { ALL_TOOL_TARGETS } from \"./tool-targets.js\";\n\n/**\n * Fetch command targets for specifying file format interpretation\n * - \"rulesync\": rulesync format with frontmatter containing targets, description, etc.\n * - Tool targets: interpreted as tool-specific format (e.g., claudecode, cursor)\n */\nconst ALL_FETCH_TARGETS = [\"rulesync\", ...ALL_TOOL_TARGETS] as const;\n\nexport const FetchTargetSchema = z.enum(ALL_FETCH_TARGETS);\n\nexport type FetchTarget = z.infer<typeof FetchTargetSchema>;\n","import { z } from \"zod/mini\";\n\nimport { ALL_FEATURES_WITH_WILDCARD } from \"./features.js\";\nimport { FetchTargetSchema } from \"./fetch-targets.js\";\nimport type { GitProvider } from \"./git-provider.js\";\n\n/**\n * Conflict resolution strategies for fetch command\n */\nconst ConflictStrategySchema = z.enum([\"skip\", \"overwrite\"]);\nexport type ConflictStrategy = z.infer<typeof ConflictStrategySchema>;\n\n/**\n * GitHub file type from API response\n */\nconst GitHubFileTypeSchema = z.enum([\"file\", \"dir\", \"symlink\", \"submodule\"]);\n\n/**\n * GitHub file/directory entry from contents API\n */\nexport const GitHubFileEntrySchema = z.looseObject({\n name: z.string(),\n path: z.string(),\n sha: z.string(),\n size: z.number(),\n type: GitHubFileTypeSchema,\n download_url: z.nullable(z.string()),\n});\nexport type GitHubFileEntry = z.infer<typeof GitHubFileEntrySchema>;\n\n/**\n * Parsed source specification for fetch command\n */\nexport type ParsedSource = {\n provider: GitProvider;\n owner: string;\n repo: string;\n ref?: string;\n path?: string;\n};\n\n/**\n * Fetch command options\n */\nconst FetchOptionsSchema = z.looseObject({\n target: z.optional(FetchTargetSchema),\n features: z.optional(z.array(z.enum(ALL_FEATURES_WITH_WILDCARD))),\n ref: z.optional(z.string()),\n path: z.optional(z.string()),\n output: z.optional(z.string()),\n conflict: z.optional(ConflictStrategySchema),\n token: z.optional(z.string()),\n verbose: z.optional(z.boolean()),\n silent: z.optional(z.boolean()),\n});\nexport type FetchOptions = z.infer<typeof FetchOptionsSchema>;\n\n/**\n * Result status for a single file fetch operation\n */\nconst FetchFileStatusSchema = z.enum([\"created\", \"overwritten\", \"skipped\"]);\ntype FetchFileStatus = z.infer<typeof FetchFileStatusSchema>;\n\n/**\n * Result of a single file fetch operation\n */\nexport type FetchFileResult = {\n relativePath: string;\n status: FetchFileStatus;\n};\n\n/**\n * Summary of fetch operation\n */\nexport type FetchSummary = {\n source: string;\n ref: string;\n files: FetchFileResult[];\n created: number;\n overwritten: number;\n skipped: number;\n};\n\n/**\n * GitHub API error response\n */\nexport type GitHubApiError = {\n message: string;\n documentation_url?: string;\n};\n\n/**\n * Configuration for GitHub client\n */\nexport type GitHubClientConfig = {\n token?: string;\n baseUrl?: string;\n};\n\n/**\n * Repository information from GitHub API\n */\nexport const GitHubRepoInfoSchema = z.looseObject({\n default_branch: z.string(),\n private: z.boolean(),\n});\nexport type GitHubRepoInfo = z.infer<typeof GitHubRepoInfoSchema>;\n\n/**\n * GitHub release asset from releases API\n */\nconst GitHubReleaseAssetSchema = z.looseObject({\n name: z.string(),\n browser_download_url: z.string(),\n size: z.number(),\n});\nexport type GitHubReleaseAsset = z.infer<typeof GitHubReleaseAssetSchema>;\n\n/**\n * GitHub release from releases API\n */\nexport const GitHubReleaseSchema = z.looseObject({\n tag_name: z.string(),\n name: z.nullable(z.string()),\n prerelease: z.boolean(),\n draft: z.boolean(),\n assets: z.array(GitHubReleaseAssetSchema),\n});\nexport type GitHubRelease = z.infer<typeof GitHubReleaseSchema>;\n","import { RequestError } from \"@octokit/request-error\";\nimport { Octokit } from \"@octokit/rest\";\n\nimport { MAX_FILE_SIZE } from \"../constants/rulesync-paths.js\";\nimport type {\n GitHubApiError,\n GitHubClientConfig,\n GitHubFileEntry,\n GitHubRelease,\n GitHubRepoInfo,\n} from \"../types/fetch.js\";\nimport {\n GitHubFileEntrySchema,\n GitHubReleaseSchema,\n GitHubRepoInfoSchema,\n} from \"../types/fetch.js\";\nimport { formatError } from \"../utils/error.js\";\nimport type { Logger } from \"../utils/logger.js\";\n\n/**\n * Error class for GitHub API errors\n */\nexport class GitHubClientError extends Error {\n constructor(\n message: string,\n public readonly statusCode?: number,\n public readonly apiError?: GitHubApiError,\n ) {\n super(message);\n this.name = \"GitHubClientError\";\n }\n}\n\n/**\n * Log GitHub auth error hints for 401/403 responses.\n */\nexport function logGitHubAuthHints(params: { error: GitHubClientError; logger: Logger }): void {\n const { error, logger } = params;\n logger.error(`GitHub API Error: ${error.message}`);\n if (error.statusCode === 401 || error.statusCode === 403) {\n logger.info(\n \"Tip: Set GITHUB_TOKEN or GH_TOKEN environment variable for private repositories or better rate limits.\",\n );\n logger.info(\n \"Tip: If you use GitHub CLI, you can use `GITHUB_TOKEN=$(gh auth token) rulesync fetch ...`\",\n );\n }\n}\n\n/**\n * Client for interacting with GitHub API using Octokit SDK\n */\nexport class GitHubClient {\n private readonly octokit: Octokit;\n private readonly hasToken: boolean;\n\n constructor(config: GitHubClientConfig = {}) {\n // Validate custom baseUrl uses HTTPS to prevent token exposure\n if (config.baseUrl && !config.baseUrl.startsWith(\"https://\")) {\n throw new GitHubClientError(\"GitHub API base URL must use HTTPS\");\n }\n\n this.hasToken = !!config.token;\n this.octokit = new Octokit({\n auth: config.token,\n baseUrl: config.baseUrl,\n });\n }\n\n /**\n * Get authentication token from various sources\n */\n static resolveToken(explicitToken?: string): string | undefined {\n if (explicitToken) {\n return explicitToken;\n }\n return process.env[\"GITHUB_TOKEN\"] ?? process.env[\"GH_TOKEN\"];\n }\n\n /**\n * Get the default branch of a repository\n */\n async getDefaultBranch(owner: string, repo: string): Promise<string> {\n const repoInfo = await this.getRepoInfo(owner, repo);\n return repoInfo.default_branch;\n }\n\n /**\n * Get repository information\n */\n async getRepoInfo(owner: string, repo: string): Promise<GitHubRepoInfo> {\n try {\n const { data } = await this.octokit.repos.get({ owner, repo });\n const parsed = GitHubRepoInfoSchema.safeParse(data);\n if (!parsed.success) {\n throw new GitHubClientError(\n `Invalid repository info response: ${formatError(parsed.error)}`,\n );\n }\n return parsed.data;\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * List contents of a directory in a repository\n */\n async listDirectory(\n owner: string,\n repo: string,\n path: string,\n ref?: string,\n ): Promise<GitHubFileEntry[]> {\n try {\n const { data } = await this.octokit.repos.getContent({\n owner,\n repo,\n path,\n ref,\n });\n\n // API returns single object for files, array for directories\n if (!Array.isArray(data)) {\n throw new GitHubClientError(`Path \"${path}\" is not a directory`);\n }\n\n const entries: GitHubFileEntry[] = [];\n for (const item of data) {\n const parsed = GitHubFileEntrySchema.safeParse(item);\n if (parsed.success) {\n entries.push(parsed.data);\n }\n }\n return entries;\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * Get raw file content from a repository\n */\n async getFileContent(owner: string, repo: string, path: string, ref?: string): Promise<string> {\n try {\n const { data } = await this.octokit.repos.getContent({\n owner,\n repo,\n path,\n ref,\n mediaType: {\n format: \"raw\",\n },\n });\n\n // When using raw format, data is returned as a string\n if (typeof data === \"string\") {\n return data;\n }\n\n // Fallback: if data is an object with content (base64 encoded)\n if (!Array.isArray(data) && \"content\" in data && data.content) {\n return Buffer.from(data.content, \"base64\").toString(\"utf-8\");\n }\n\n throw new GitHubClientError(`Unexpected response format for file content`);\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * Check if a file exists and is within size limits\n */\n async getFileInfo(\n owner: string,\n repo: string,\n path: string,\n ref?: string,\n ): Promise<GitHubFileEntry | null> {\n try {\n const { data } = await this.octokit.repos.getContent({\n owner,\n repo,\n path,\n ref,\n });\n\n // Ensure it's a file, not a directory\n if (Array.isArray(data)) {\n return null; // It's a directory\n }\n\n const parsed = GitHubFileEntrySchema.safeParse(data);\n if (!parsed.success) {\n return null;\n }\n\n if (parsed.data.size > MAX_FILE_SIZE) {\n throw new GitHubClientError(\n `File \"${path}\" exceeds maximum size limit of ${MAX_FILE_SIZE / 1024 / 1024}MB`,\n );\n }\n\n return parsed.data;\n } catch (error: unknown) {\n if (error instanceof RequestError && error.status === 404) {\n return null;\n }\n if (error instanceof GitHubClientError && error.statusCode === 404) {\n return null;\n }\n throw this.handleError(error);\n }\n }\n\n /**\n * Validate that a repository exists and is accessible\n */\n async validateRepository(owner: string, repo: string): Promise<boolean> {\n try {\n await this.getRepoInfo(owner, repo);\n return true;\n } catch (error) {\n if (error instanceof GitHubClientError && error.statusCode === 404) {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * Resolve a ref (branch, tag, or SHA) to a full commit SHA.\n */\n async resolveRefToSha(owner: string, repo: string, ref: string): Promise<string> {\n try {\n const { data } = await this.octokit.repos.getCommit({\n owner,\n repo,\n ref,\n });\n return data.sha;\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * Get the latest release from a repository\n */\n async getLatestRelease(owner: string, repo: string): Promise<GitHubRelease> {\n try {\n const { data } = await this.octokit.repos.getLatestRelease({ owner, repo });\n const parsed = GitHubReleaseSchema.safeParse(data);\n if (!parsed.success) {\n throw new GitHubClientError(`Invalid release info response: ${formatError(parsed.error)}`);\n }\n return parsed.data;\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * Handle errors from Octokit and convert to GitHubClientError\n */\n private handleError(error: unknown): GitHubClientError {\n if (error instanceof GitHubClientError) {\n return error;\n }\n\n if (error instanceof RequestError) {\n const responseData = error.response?.data;\n const message = this.extractErrorMessage(responseData, error.message);\n const apiError: GitHubApiError | undefined = message ? { message } : undefined;\n const errorMessage = this.getErrorMessage(error.status, apiError);\n return new GitHubClientError(errorMessage, error.status, apiError);\n }\n\n if (error instanceof Error) {\n return new GitHubClientError(error.message);\n }\n\n return new GitHubClientError(\"Unknown error occurred\");\n }\n\n /**\n * Extract error message from response data\n */\n private extractErrorMessage(data: unknown, fallback: string): string {\n if (typeof data === \"object\" && data !== null && \"message\" in data) {\n const record = data as Record<string, unknown>;\n const msg = record[\"message\"];\n if (typeof msg === \"string\") {\n return msg;\n }\n }\n return fallback;\n }\n\n /**\n * Get human-readable error message for HTTP status codes\n */\n private getErrorMessage(statusCode: number, apiError?: GitHubApiError): string {\n const baseMessage = apiError?.message ?? `HTTP ${statusCode}`;\n\n switch (statusCode) {\n case 401:\n return `Authentication failed: ${baseMessage}. Check your GitHub token.`;\n case 403:\n if (baseMessage.toLowerCase().includes(\"rate limit\")) {\n return `GitHub API rate limit exceeded. ${this.hasToken ? \"Try again later.\" : \"Consider using a GitHub token.\"}`;\n }\n return `Access forbidden: ${baseMessage}. Check repository permissions.`;\n case 404:\n return `Not found: ${baseMessage}`;\n case 422:\n return `Invalid request: ${baseMessage}`;\n default:\n return `GitHub API error: ${baseMessage}`;\n }\n }\n}\n","import { Semaphore } from \"es-toolkit/promise\";\n\nimport type { GitHubFileEntry } from \"../types/fetch.js\";\nimport type { GitHubClient } from \"./github-client.js\";\n\nconst MAX_RECURSION_DEPTH = 15;\n\n/**\n * Execute an async function with semaphore-controlled concurrency.\n * Ensures the semaphore permit is always released, even if the function throws.\n */\nexport async function withSemaphore<T>(semaphore: Semaphore, fn: () => Promise<T>): Promise<T> {\n await semaphore.acquire();\n try {\n return await fn();\n } finally {\n semaphore.release();\n }\n}\n\n/**\n * Recursively list all files in a GitHub directory.\n */\nexport async function listDirectoryRecursive(params: {\n client: GitHubClient;\n owner: string;\n repo: string;\n path: string;\n ref?: string;\n depth?: number;\n semaphore: Semaphore;\n}): Promise<GitHubFileEntry[]> {\n const { client, owner, repo, path, ref, depth = 0, semaphore } = params;\n\n if (depth > MAX_RECURSION_DEPTH) {\n throw new Error(\n `Maximum recursion depth (${MAX_RECURSION_DEPTH}) exceeded while listing directory: ${path}`,\n );\n }\n\n // Semaphore is released here before recursive Promise.all below to avoid deadlock\n const entries = await withSemaphore(semaphore, () =>\n client.listDirectory(owner, repo, path, ref),\n );\n\n const files: GitHubFileEntry[] = [];\n const directories: GitHubFileEntry[] = [];\n\n for (const entry of entries) {\n if (entry.type === \"file\") {\n files.push(entry);\n } else if (entry.type === \"dir\") {\n directories.push(entry);\n }\n }\n\n const subResults = await Promise.all(\n directories.map((dir) =>\n listDirectoryRecursive({\n client,\n owner,\n repo,\n path: dir.path,\n ref,\n depth: depth + 1,\n semaphore,\n }),\n ),\n );\n\n return [...files, ...subResults.flat()];\n}\n","import { createHash } from \"node:crypto\";\n\nimport type { Logger } from \"../utils/logger.js\";\nimport { findControlCharacter } from \"../utils/validation.js\";\n\n/**\n * Minimal npm-compatible registry client for the EXPERIMENTAL `npm` transport.\n * Works against any registry implementing the npm registry API (npmjs.org,\n * JFrog Artifactory, Sonatype Nexus, Verdaccio, ...). Intentionally avoids\n * `.npmrc` parsing: authentication uses a bearer token from an environment\n * variable (`NPM_TOKEN` by default, or a per-source `tokenEnv`).\n */\n\nexport const DEFAULT_NPM_REGISTRY_URL = \"https://registry.npmjs.org\";\nexport const DEFAULT_NPM_TOKEN_ENV = \"NPM_TOKEN\";\n\n/** Abbreviated packument media type (install metadata only). */\nconst PACKUMENT_ACCEPT_HEADER = \"application/vnd.npm.install-v1+json\";\n\n/** Timeout for registry HTTP requests (60 seconds). */\nconst NPM_FETCH_TIMEOUT_MS = 60_000;\n\n/** Maximum accepted tarball size (compressed), aligned with the extraction cap. */\nconst MAX_TARBALL_SIZE = 100 * 1024 * 1024;\n\n/**\n * npm package name rules (scoped or unscoped, URL-safe characters only).\n * This also guards against URL path injection into registry requests.\n */\nconst NPM_PACKAGE_NAME_REGEX = /^(@[a-z0-9][a-z0-9._~-]*\\/)?[a-z0-9][a-z0-9._~-]*$/i;\nconst MAX_NPM_PACKAGE_NAME_LENGTH = 214;\n\nconst INTEGRITY_ALGORITHM_PREFERENCE = [\"sha512\", \"sha384\", \"sha256\", \"sha1\"] as const;\ntype IntegrityAlgorithm = (typeof INTEGRITY_ALGORITHM_PREFERENCE)[number];\n\nexport class NpmClientError extends Error {\n public readonly statusCode?: number;\n\n constructor(message: string, options?: { statusCode?: number; cause?: unknown }) {\n super(message, { cause: options?.cause });\n this.name = \"NpmClientError\";\n this.statusCode = options?.statusCode;\n }\n}\n\nexport type NpmDist = {\n tarball: string;\n integrity?: string;\n shasum?: string;\n};\n\nexport type NpmPackument = {\n name?: string;\n \"dist-tags\"?: Record<string, string>;\n versions?: Record<string, { dist?: NpmDist }>;\n};\n\nexport function validateNpmPackageName(name: string): void {\n if (name.length > MAX_NPM_PACKAGE_NAME_LENGTH || !NPM_PACKAGE_NAME_REGEX.test(name)) {\n throw new NpmClientError(\n `Invalid npm package name: \"${name}\". Expected \"name\" or \"@scope/name\".`,\n );\n }\n}\n\nexport function validateNpmRegistryUrl(url: string, options?: { logger?: Logger }): void {\n const ctrl = findControlCharacter(url);\n if (ctrl) {\n throw new NpmClientError(\n `Registry URL contains control character ${ctrl.hex} at position ${ctrl.position}`,\n );\n }\n if (!url.startsWith(\"https://\") && !url.startsWith(\"http://\")) {\n throw new NpmClientError(`Unsupported registry URL: \"${url}\". Use https:// (or http://).`);\n }\n if (url.startsWith(\"http://\")) {\n options?.logger?.warn(\n `Registry URL \"${url}\" uses an unencrypted protocol. Consider using https:// instead.`,\n );\n }\n}\n\n/**\n * Resolve the registry token from the environment. When `tokenEnv` is set it\n * must name an existing environment variable; otherwise `NPM_TOKEN` is used\n * when present. The token value itself is never logged.\n */\nexport function resolveNpmToken(params: { tokenEnv?: string }): string | undefined {\n const { tokenEnv } = params;\n if (tokenEnv !== undefined) {\n const value = process.env[tokenEnv];\n if (value === undefined || value === \"\") {\n throw new NpmClientError(\n `Environment variable \"${tokenEnv}\" (from tokenEnv) is not set. Export it or remove the tokenEnv field.`,\n );\n }\n return value;\n }\n const fallback = process.env[DEFAULT_NPM_TOKEN_ENV];\n return fallback === undefined || fallback === \"\" ? undefined : fallback;\n}\n\n/** Build the packument URL for a (possibly scoped) package on a registry. */\nexport function buildPackumentUrl(params: { registryUrl: string; packageName: string }): string {\n const { registryUrl, packageName } = params;\n // Validate here too so the URL can never carry extra path segments, even if a\n // caller skips the fetch-level validation.\n validateNpmPackageName(packageName);\n const base = registryUrl.endsWith(\"/\") ? registryUrl : `${registryUrl}/`;\n // Scoped package names keep the \"@\" but encode the slash, per the npm registry API.\n const encodedName = packageName.replaceAll(\"/\", \"%2F\");\n return new URL(encodedName, base).toString();\n}\n\nasync function fetchWithTimeout(url: string, headers: Record<string, string>): Promise<Response> {\n try {\n return await fetch(url, {\n headers,\n redirect: \"follow\",\n signal: AbortSignal.timeout(NPM_FETCH_TIMEOUT_MS),\n });\n } catch (error) {\n throw new NpmClientError(`Network error while requesting ${url}`, { cause: error });\n }\n}\n\n/**\n * Fetch the (abbreviated) packument for a package from a registry.\n */\nexport async function fetchPackument(params: {\n registryUrl: string;\n packageName: string;\n token?: string;\n}): Promise<NpmPackument> {\n const { registryUrl, packageName, token } = params;\n validateNpmPackageName(packageName);\n const url = buildPackumentUrl({ registryUrl, packageName });\n\n const headers: Record<string, string> = { Accept: PACKUMENT_ACCEPT_HEADER };\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n const response = await fetchWithTimeout(url, headers);\n if (!response.ok) {\n throw new NpmClientError(\n `Failed to fetch package metadata for \"${packageName}\" from ${registryUrl}: HTTP ${response.status}`,\n { statusCode: response.status },\n );\n }\n try {\n return (await response.json()) as NpmPackument;\n } catch (error) {\n throw new NpmClientError(\n `Failed to parse package metadata for \"${packageName}\" from ${registryUrl}`,\n { cause: error },\n );\n }\n}\n\n/**\n * Resolve a requested version or dist-tag against a packument. Only exact\n * versions and dist-tags are supported — semver ranges are intentionally out\n * of scope (no semver dependency).\n */\nexport function resolvePackumentVersion(params: {\n packument: NpmPackument;\n packageName: string;\n requested: string;\n}): string {\n const { packument, packageName, requested } = params;\n const versions = packument.versions ?? {};\n if (Object.prototype.hasOwnProperty.call(versions, requested)) {\n return requested;\n }\n const distTags = packument[\"dist-tags\"] ?? {};\n const tagged = Object.prototype.hasOwnProperty.call(distTags, requested)\n ? distTags[requested]\n : undefined;\n if (tagged !== undefined && Object.prototype.hasOwnProperty.call(versions, tagged)) {\n return tagged;\n }\n throw new NpmClientError(\n `Could not resolve \"${packageName}@${requested}\": not an exact published version or dist-tag. Note: semver ranges are not supported by the npm transport.`,\n );\n}\n\n/** Get the dist metadata (tarball URL, integrity) for a resolved version. */\nexport function getPackumentVersionDist(params: {\n packument: NpmPackument;\n packageName: string;\n version: string;\n}): NpmDist {\n const { packument, packageName, version } = params;\n const versions = packument.versions ?? {};\n const entry = Object.prototype.hasOwnProperty.call(versions, version)\n ? versions[version]\n : undefined;\n const dist = entry?.dist;\n if (!dist?.tarball) {\n throw new NpmClientError(\n `Registry metadata for \"${packageName}@${version}\" is missing the dist.tarball URL.`,\n );\n }\n return dist;\n}\n\n/**\n * Download a package tarball. The Authorization header is only attached when\n * the tarball is hosted on the same origin (scheme + host) as the registry,\n * so the token never leaks to third-party CDNs or plaintext downgrades.\n */\nexport async function fetchTarball(params: {\n tarballUrl: string;\n registryUrl: string;\n token?: string;\n /** Maximum accepted tarball size in bytes. Overridable for tests only. */\n maxSize?: number;\n}): Promise<Buffer> {\n const { tarballUrl, registryUrl, token } = params;\n const maxSize = params.maxSize ?? MAX_TARBALL_SIZE;\n if (!tarballUrl.startsWith(\"https://\") && !tarballUrl.startsWith(\"http://\")) {\n throw new NpmClientError(\n `Unsupported tarball URL: \"${tarballUrl}\". Use https:// (or http://).`,\n );\n }\n\n const headers: Record<string, string> = {};\n if (token && isSameOrigin(tarballUrl, registryUrl)) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n const response = await fetchWithTimeout(tarballUrl, headers);\n if (!response.ok) {\n throw new NpmClientError(`Failed to download tarball ${tarballUrl}: HTTP ${response.status}`, {\n statusCode: response.status,\n });\n }\n const contentLength = Number.parseInt(response.headers.get(\"content-length\") ?? \"\", 10);\n if (Number.isFinite(contentLength) && contentLength > maxSize) {\n throw new NpmClientError(oversizedTarballMessage(tarballUrl, maxSize));\n }\n return await readBodyWithLimit({ response, tarballUrl, maxSize });\n}\n\nfunction oversizedTarballMessage(tarballUrl: string, maxSize: number): string {\n return `Tarball ${tarballUrl} exceeds max size of ${maxSize / 1024 / 1024}MB.`;\n}\n\n/**\n * Read a response body incrementally, aborting as soon as the size cap is\n * exceeded. content-length can be absent or forged, so the streaming check is\n * the actual enforcement of the cap.\n */\nasync function readBodyWithLimit(params: {\n response: Response;\n tarballUrl: string;\n maxSize: number;\n}): Promise<Buffer> {\n const { response, tarballUrl, maxSize } = params;\n const reader = response.body?.getReader();\n if (!reader) {\n // Responses without a body stream (e.g. some test doubles): buffer\n // with a post-hoc check.\n const arrayBuffer = await response.arrayBuffer();\n if (arrayBuffer.byteLength > maxSize) {\n throw new NpmClientError(oversizedTarballMessage(tarballUrl, maxSize));\n }\n return Buffer.from(arrayBuffer);\n }\n\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n totalBytes += value.byteLength;\n if (totalBytes > maxSize) {\n await reader.cancel();\n throw new NpmClientError(oversizedTarballMessage(tarballUrl, maxSize));\n }\n chunks.push(Buffer.from(value));\n }\n return Buffer.concat(chunks);\n}\n\nfunction isSameOrigin(urlA: string, urlB: string): boolean {\n try {\n return new URL(urlA).origin === new URL(urlB).origin;\n } catch {\n return false;\n }\n}\n\n/**\n * Convert a hex sha1 shasum to the SRI form used by `verifyTarballIntegrity`.\n * Rejects malformed shasum values so a broken value is never recorded in the\n * lockfile as a seemingly valid SRI string.\n */\nexport function shasumToSri(shasum: string): string {\n if (!/^[0-9a-f]{40}$/i.test(shasum)) {\n throw new NpmClientError(`Malformed sha1 shasum in registry metadata: \"${shasum}\"`);\n }\n return `sha1-${Buffer.from(shasum, \"hex\").toString(\"base64\")}`;\n}\n\n/**\n * Verify a downloaded tarball against registry integrity metadata.\n * Prefers the strongest supported algorithm in the SRI `integrity` string and\n * falls back to the legacy sha1 `shasum`. An `integrity` string that is\n * present but cannot be parsed fails closed; a warning is only logged when\n * the registry provides no integrity metadata at all.\n */\nexport function verifyTarballIntegrity(params: {\n tarball: Buffer;\n integrity?: string;\n shasum?: string;\n context: string;\n logger?: Logger;\n}): void {\n const { tarball, integrity, shasum, context, logger } = params;\n\n if (integrity !== undefined) {\n const sri = pickStrongestSriEntry(integrity);\n if (!sri) {\n // Fail closed: a present-but-unparseable integrity value must never\n // silently disable verification (e.g. a corrupted lockfile entry).\n throw new NpmClientError(\n `Unsupported or malformed integrity metadata for ${context}. Expected an SRI string with sha512/sha384/sha256/sha1.`,\n );\n }\n const actual = createHash(sri.algorithm).update(tarball).digest(\"base64\");\n if (actual !== sri.digest) {\n throw new NpmClientError(\n `Integrity verification failed for ${context}: expected ${sri.algorithm}-${sri.digest}, got ${sri.algorithm}-${actual}. The tarball may have been tampered with.`,\n );\n }\n return;\n }\n\n if (shasum) {\n const actual = createHash(\"sha1\").update(tarball).digest(\"hex\");\n if (actual !== shasum.toLowerCase()) {\n throw new NpmClientError(\n `Integrity verification failed for ${context}: expected sha1 ${shasum}, got ${actual}. The tarball may have been tampered with.`,\n );\n }\n return;\n }\n\n logger?.warn(`No integrity metadata available for ${context}; skipping tarball verification.`);\n}\n\nfunction pickStrongestSriEntry(\n integrity: string | undefined,\n): { algorithm: IntegrityAlgorithm; digest: string } | undefined {\n if (!integrity) {\n return undefined;\n }\n const entries = integrity\n .split(/\\s+/)\n .map((entry) => {\n const separatorIndex = entry.indexOf(\"-\");\n if (separatorIndex === -1) return undefined;\n const algorithm = entry.slice(0, separatorIndex);\n // Strip SRI options (`sha512-<digest>?opt`) from the digest.\n const digest = entry.slice(separatorIndex + 1).split(\"?\")[0] ?? \"\";\n const known = INTEGRITY_ALGORITHM_PREFERENCE.find((a) => a === algorithm);\n if (!known || digest.length === 0) return undefined;\n return { algorithm: known, digest };\n })\n .filter((entry): entry is { algorithm: IntegrityAlgorithm; digest: string } => Boolean(entry));\n\n for (const algorithm of INTEGRITY_ALGORITHM_PREFERENCE) {\n const match = entries.find((entry) => entry.algorithm === algorithm);\n if (match) {\n return match;\n }\n }\n return undefined;\n}\n\n/**\n * Log contextual hints for NpmClientError to help users troubleshoot\n * authentication problems without ever logging the token itself.\n */\nexport function logNpmAuthHints(params: { error: NpmClientError; logger: Logger }): void {\n const { error, logger } = params;\n if (error.statusCode === 401 || error.statusCode === 403) {\n logger.info(\n \"Hint: The registry rejected the request. Set NPM_TOKEN (or the per-source tokenEnv variable) to a token with read access. Note: .npmrc files are not read by the npm transport.\",\n );\n } else if (error.statusCode === 404) {\n logger.info(\n \"Hint: Package not found. Check the package name and the registry URL. Some registries also return 404 for unauthorized requests.\",\n );\n }\n}\n","import { gunzipSync } from \"node:zlib\";\n\n/**\n * Minimal, hardened tar reader for npm package tarballs (EXPERIMENTAL npm\n * transport). Intentionally supports only the subset of the (pax-extended)\n * ustar format that npm-compatible registries produce:\n *\n * - regular files (typeflag \"0\" / \"\\0\")\n * - directories (typeflag \"5\") — skipped; directories are created implicitly\n * - pax extended headers (typeflag \"x\") — only the `path` override is honored\n * - GNU long names (typeflag \"L\")\n *\n * Everything else (symlinks, hardlinks, devices, FIFOs, ...) is skipped and\n * never materialized. Extraction is bounded by file-count and total-byte caps\n * to prevent decompression bombs, and every entry path is validated against\n * traversal (absolute paths, `..` segments, backslashes).\n */\n\nconst BLOCK_SIZE = 512;\n\n/** Maximum number of files extracted from a single package tarball. */\nexport const MAX_TAR_FILES = 10_000;\n/** Maximum total extracted bytes from a single package tarball (100 MB). */\nexport const MAX_TAR_TOTAL_BYTES = 100 * 1024 * 1024;\n\nexport class NpmTarError extends Error {\n constructor(message: string, cause?: unknown) {\n super(message, { cause });\n this.name = \"NpmTarError\";\n }\n}\n\nexport type TarFileEntry = {\n /**\n * Entry path relative to the package root. The first path component of every\n * entry (conventionally `package/` in npm tarballs, but registries may use a\n * different folder name) is stripped, matching `tar --strip-components=1`.\n */\n relativePath: string;\n content: Buffer;\n};\n\n/**\n * Gunzip and extract a npm package tarball into an in-memory file list.\n * Throws {@link NpmTarError} on malformed archives, traversal attempts, or\n * resource-limit violations.\n */\nexport function extractPackageTarball(params: {\n tarball: Buffer;\n maxFiles?: number;\n maxTotalBytes?: number;\n onSkippedEntry?: (message: string) => void;\n}): TarFileEntry[] {\n const { tarball, onSkippedEntry } = params;\n const maxFiles = params.maxFiles ?? MAX_TAR_FILES;\n const maxTotalBytes = params.maxTotalBytes ?? MAX_TAR_TOTAL_BYTES;\n\n let tar: Buffer;\n try {\n // Cap the decompressed size at the gzip layer as well: the total-byte cap\n // plus generous headroom for tar headers/padding (3 blocks per file).\n tar = gunzipSync(tarball, {\n maxOutputLength: maxTotalBytes + maxFiles * 3 * BLOCK_SIZE + 2 * BLOCK_SIZE,\n });\n } catch (error) {\n throw new NpmTarError(\"Failed to gunzip package tarball\", error);\n }\n\n return parseTarBuffer({ tar, maxFiles, maxTotalBytes, onSkippedEntry });\n}\n\nfunction parseTarBuffer(params: {\n tar: Buffer;\n maxFiles: number;\n maxTotalBytes: number;\n onSkippedEntry?: (message: string) => void;\n}): TarFileEntry[] {\n const { tar, maxFiles, maxTotalBytes, onSkippedEntry } = params;\n const files: TarFileEntry[] = [];\n let totalBytes = 0;\n let offset = 0;\n let pendingLongName: string | undefined;\n let pendingPaxPath: string | undefined;\n\n while (offset + BLOCK_SIZE <= tar.length) {\n const header = tar.subarray(offset, offset + BLOCK_SIZE);\n if (isZeroBlock(header)) {\n break;\n }\n verifyHeaderChecksum(header);\n\n const size = parseOctalField(header, 124, 12, \"size\");\n const typeflag = String.fromCharCode(header[156] ?? 0);\n const dataStart = offset + BLOCK_SIZE;\n const dataEnd = dataStart + size;\n if (dataEnd > tar.length) {\n throw new NpmTarError(\"Truncated tar archive: entry data extends past end of archive\");\n }\n\n switch (typeflag) {\n case \"x\": {\n const records = parsePaxRecords(tar.subarray(dataStart, dataEnd));\n if (records.has(\"size\")) {\n // A pax size override means the octal size field is unreliable; a\n // minimal reader cannot stay aligned, so refuse instead of misparsing.\n throw new NpmTarError(\"Unsupported tar archive: pax size override is not supported\");\n }\n pendingPaxPath = records.get(\"path\") ?? pendingPaxPath;\n break;\n }\n case \"L\": {\n pendingLongName = trimAtFirstNul(tar.toString(\"utf8\", dataStart, dataEnd));\n break;\n }\n case \"g\": {\n // Global pax header. Harmless records are ignored, but size/path\n // overrides would make this reader disagree with full tar\n // implementations (parser-differential risk), so refuse them.\n const records = parsePaxRecords(tar.subarray(dataStart, dataEnd));\n if (records.has(\"size\") || records.has(\"path\")) {\n throw new NpmTarError(\n \"Unsupported tar archive: global pax size/path overrides are not supported\",\n );\n }\n break;\n }\n case \"0\":\n case \"\\0\": {\n const rawName = resolveEntryName({ header, pendingLongName, pendingPaxPath });\n pendingLongName = undefined;\n pendingPaxPath = undefined;\n const relativePath = toSafeRelativePath(rawName);\n if (relativePath !== null) {\n if (files.length + 1 > maxFiles) {\n throw new NpmTarError(\n `Package tarball exceeds max file count of ${maxFiles}. Aborting to prevent resource exhaustion.`,\n );\n }\n totalBytes += size;\n if (totalBytes > maxTotalBytes) {\n throw new NpmTarError(\n `Package tarball exceeds max total size of ${maxTotalBytes / 1024 / 1024}MB. Aborting to prevent resource exhaustion.`,\n );\n }\n files.push({\n relativePath,\n content: Buffer.from(tar.subarray(dataStart, dataEnd)),\n });\n }\n break;\n }\n case \"5\": {\n // Directory — created implicitly when files are written.\n pendingLongName = undefined;\n pendingPaxPath = undefined;\n break;\n }\n default: {\n // Symlinks, hardlinks, devices, FIFOs, ... are never materialized.\n const rawName = resolveEntryName({ header, pendingLongName, pendingPaxPath });\n pendingLongName = undefined;\n pendingPaxPath = undefined;\n onSkippedEntry?.(\n `Skipping unsupported tar entry type \"${typeflag}\" for \"${rawName}\" (only regular files are extracted).`,\n );\n break;\n }\n }\n\n offset = dataStart + Math.ceil(size / BLOCK_SIZE) * BLOCK_SIZE;\n }\n\n return files;\n}\n\nfunction isZeroBlock(block: Buffer): boolean {\n return block.every((byte) => byte === 0);\n}\n\n/** Cut a string at its first NUL character (tar fields are NUL-terminated). */\nfunction trimAtFirstNul(value: string): string {\n const nulIndex = value.indexOf(\"\\0\");\n return nulIndex === -1 ? value : value.substring(0, nulIndex);\n}\n\nfunction parseOctalField(block: Buffer, offset: number, length: number, field: string): number {\n const first = block[offset] ?? 0;\n if ((first & 0x80) !== 0) {\n throw new NpmTarError(`Unsupported tar archive: base-256 ${field} field is not supported`);\n }\n const raw = block.toString(\"latin1\", offset, offset + length);\n const text = trimAtFirstNul(raw).trim();\n if (text === \"\") {\n return 0;\n }\n if (!/^[0-7]+$/.test(text)) {\n throw new NpmTarError(`Invalid tar header: malformed octal ${field} field`);\n }\n return Number.parseInt(text, 8);\n}\n\n/**\n * Verify the ustar header checksum: the unsigned byte sum of the 512-byte\n * header with the checksum field itself treated as ASCII spaces.\n */\nfunction verifyHeaderChecksum(header: Buffer): void {\n const stored = parseOctalField(header, 148, 8, \"checksum\");\n let sum = 0;\n for (let i = 0; i < BLOCK_SIZE; i++) {\n sum += i >= 148 && i < 156 ? 0x20 : (header[i] ?? 0);\n }\n if (sum !== stored) {\n throw new NpmTarError(\"Invalid tar header: checksum mismatch\");\n }\n}\n\nfunction readCString(block: Buffer, offset: number, length: number): string {\n const end = block.indexOf(0, offset);\n const stop = end === -1 || end > offset + length ? offset + length : end;\n return block.toString(\"utf8\", offset, stop);\n}\n\nfunction resolveEntryName(params: {\n header: Buffer;\n pendingLongName: string | undefined;\n pendingPaxPath: string | undefined;\n}): string {\n const { header, pendingLongName, pendingPaxPath } = params;\n if (pendingPaxPath !== undefined) {\n return pendingPaxPath;\n }\n if (pendingLongName !== undefined) {\n return pendingLongName;\n }\n const name = readCString(header, 0, 100);\n const magic = header.toString(\"latin1\", 257, 262);\n const prefix = magic === \"ustar\" ? readCString(header, 345, 155) : \"\";\n return prefix.length > 0 ? `${prefix}/${name}` : name;\n}\n\n/**\n * Parse pax extended header records of the form `<len> <key>=<value>\\n`,\n * where `<len>` is the decimal length of the whole record.\n */\nfunction parsePaxRecords(data: Buffer): Map<string, string> {\n const records = new Map<string, string>();\n let offset = 0;\n while (offset < data.length) {\n if (data[offset] === 0) {\n break; // trailing NUL padding\n }\n const spaceIndex = data.indexOf(0x20, offset);\n if (spaceIndex === -1) {\n throw new NpmTarError(\"Invalid pax header: missing length delimiter\");\n }\n const recordLength = Number.parseInt(data.toString(\"utf8\", offset, spaceIndex), 10);\n if (\n !Number.isInteger(recordLength) ||\n recordLength <= 0 ||\n offset + recordLength > data.length\n ) {\n throw new NpmTarError(\"Invalid pax header: malformed record length\");\n }\n // Record content excludes the length prefix, the space, and the trailing newline.\n const record = data.toString(\"utf8\", spaceIndex + 1, offset + recordLength - 1);\n const equalsIndex = record.indexOf(\"=\");\n if (equalsIndex !== -1) {\n records.set(record.slice(0, equalsIndex), record.slice(equalsIndex + 1));\n }\n offset += recordLength;\n }\n return records;\n}\n\n/**\n * Validate a tar entry path and convert it to a package-root-relative path\n * with the first component stripped. Returns null for entries that resolve to\n * the package root itself (e.g. the `package/` folder entry). Throws on\n * traversal attempts.\n */\nfunction toSafeRelativePath(rawPath: string): string | null {\n if (rawPath.includes(\"\\0\")) {\n throw new NpmTarError(`Unsafe tar entry path (NUL byte): \"${rawPath}\"`);\n }\n if (rawPath.includes(\"\\\\\")) {\n throw new NpmTarError(`Unsafe tar entry path (backslash): \"${rawPath}\"`);\n }\n if (rawPath.startsWith(\"/\")) {\n throw new NpmTarError(`Unsafe tar entry path (absolute): \"${rawPath}\"`);\n }\n const segments = rawPath.split(\"/\").filter((segment) => segment !== \"\" && segment !== \".\");\n if (segments.includes(\"..\")) {\n throw new NpmTarError(`Unsafe tar entry path (\"..\" segment): \"${rawPath}\"`);\n }\n // Strip the tarball's single root folder (conventionally `package/`).\n segments.shift();\n if (segments.length === 0) {\n return null;\n }\n return segments.join(\"/\");\n}\n","import { z } from \"zod/mini\";\n\n/**\n * Supported Git providers for fetch command\n */\nexport const ALL_GIT_PROVIDERS = [\"github\", \"gitlab\"] as const;\n\nconst GitProviderSchema = z.enum(ALL_GIT_PROVIDERS);\n\nexport type GitProvider = z.infer<typeof GitProviderSchema>;\n","import type { ParsedSource } from \"../types/fetch.js\";\nimport type { GitProvider } from \"../types/git-provider.js\";\nimport { ALL_GIT_PROVIDERS } from \"../types/git-provider.js\";\n\nconst GITHUB_HOSTS = new Set([\"github.com\", \"www.github.com\"]);\nconst GITLAB_HOSTS = new Set([\"gitlab.com\", \"www.gitlab.com\"]);\n\n/**\n * Parse source specification into components\n * Supports:\n * - URL format: https://github.com/owner/repo, https://gitlab.com/owner/repo\n * - Prefix format: github:owner/repo, gitlab:owner/repo\n * - Shorthand format: owner/repo (defaults to GitHub)\n * - With ref: owner/repo@ref\n * - With path: owner/repo:path\n * - Combined: owner/repo@ref:path\n */\nexport function parseSource(source: string): ParsedSource {\n // Handle full URL format (https://...)\n if (source.startsWith(\"http://\") || source.startsWith(\"https://\")) {\n return parseUrl(source);\n }\n\n // Handle prefix format (github:owner/repo, gitlab:owner/repo)\n if (source.includes(\":\") && !source.includes(\"://\")) {\n const colonIndex = source.indexOf(\":\");\n const prefix = source.substring(0, colonIndex);\n const rest = source.substring(colonIndex + 1);\n\n // Check if prefix is a known provider using type guard\n const provider = ALL_GIT_PROVIDERS.find((p) => p === prefix);\n if (provider) {\n return { provider, ...parseShorthand(rest) };\n }\n\n // If prefix is not a known provider, treat the whole thing as shorthand\n // This handles cases like owner/repo:path where \"owner/repo\" contains no provider prefix\n return { provider: \"github\", ...parseShorthand(source) };\n }\n\n // Handle shorthand: owner/repo[@ref][:path] - defaults to GitHub\n return { provider: \"github\", ...parseShorthand(source) };\n}\n\n/**\n * Parse URL format into components\n */\nfunction parseUrl(url: string): ParsedSource {\n const urlObj = new URL(url);\n const host = urlObj.hostname.toLowerCase();\n\n let provider: GitProvider;\n if (GITHUB_HOSTS.has(host)) {\n provider = \"github\";\n } else if (GITLAB_HOSTS.has(host)) {\n provider = \"gitlab\";\n } else {\n throw new Error(\n `Unknown Git provider for host: ${host}. Supported providers: ${ALL_GIT_PROVIDERS.join(\", \")}`,\n );\n }\n\n // Split by path segments\n const segments = urlObj.pathname.split(\"/\").filter(Boolean);\n\n if (segments.length < 2) {\n throw new Error(`Invalid ${provider} URL: ${url}. Expected format: https://${host}/owner/repo`);\n }\n\n const owner = segments[0];\n const repo = segments[1]?.replace(/\\.git$/, \"\");\n\n // Check for /tree/ref/path or /blob/ref/path pattern\n if (segments.length > 2 && (segments[2] === \"tree\" || segments[2] === \"blob\")) {\n const ref = segments[3];\n const path = segments.length > 4 ? segments.slice(4).join(\"/\") : undefined;\n return {\n provider,\n owner: owner ?? \"\",\n repo: repo ?? \"\",\n ref,\n path,\n };\n }\n\n return {\n provider,\n owner: owner ?? \"\",\n repo: repo ?? \"\",\n };\n}\n\n/**\n * Parse shorthand format (without provider prefix)\n */\nfunction parseShorthand(source: string): Omit<ParsedSource, \"provider\"> {\n // Pattern: owner/repo[@ref][:path]\n let remaining = source;\n let path: string | undefined;\n let ref: string | undefined;\n\n // Extract path first (after :)\n const colonIndex = remaining.indexOf(\":\");\n if (colonIndex !== -1) {\n path = remaining.substring(colonIndex + 1);\n if (!path) {\n throw new Error(`Invalid source: ${source}. Path cannot be empty after \":\".`);\n }\n remaining = remaining.substring(0, colonIndex);\n }\n\n // Extract ref (after @)\n const atIndex = remaining.indexOf(\"@\");\n if (atIndex !== -1) {\n ref = remaining.substring(atIndex + 1);\n if (!ref) {\n throw new Error(`Invalid source: ${source}. Ref cannot be empty after \"@\".`);\n }\n remaining = remaining.substring(0, atIndex);\n }\n\n // Parse owner/repo\n const slashIndex = remaining.indexOf(\"/\");\n if (slashIndex === -1) {\n throw new Error(\n `Invalid source: ${source}. Expected format: owner/repo, owner/repo@ref, or owner/repo:path`,\n );\n }\n\n const owner = remaining.substring(0, slashIndex);\n const repo = remaining.substring(slashIndex + 1);\n\n if (!owner || !repo) {\n throw new Error(`Invalid source: ${source}. Both owner and repo are required.`);\n }\n\n return {\n owner,\n repo,\n ref,\n path,\n };\n}\n","import { join, posix, relative, resolve, sep } from \"node:path\";\n\nimport { Semaphore } from \"es-toolkit/promise\";\n\nimport type { SourceEntry } from \"../config/config.js\";\nimport { SKILL_FILE_NAME } from \"../constants/general.js\";\nimport {\n FETCH_CONCURRENCY_LIMIT,\n RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH,\n MAX_FILE_SIZE,\n RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH,\n RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH,\n RULESYNC_RULES_RELATIVE_DIR_PATH,\n RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH,\n} from \"../constants/rulesync-paths.js\";\nimport { getLocalSkillDirNames } from \"../features/skills/skills-utils.js\";\nimport type { GitHubFileEntry, ParsedSource } from \"../types/fetch.js\";\nimport { formatError } from \"../utils/error.js\";\nimport {\n assertDirectoryIfExists,\n assertTreeContainsNoSymlinks,\n assertWritablePathInsideRoot,\n checkPathTraversal,\n directoryExists,\n fileExists,\n findFilesByGlobs,\n readFileContent,\n removeFileStrict,\n removeDirectoryStrict,\n runWithDirectoryRollback,\n writeFileContent,\n} from \"../utils/file.js\";\nimport type { Logger } from \"../utils/logger.js\";\nimport {\n GitClientError,\n fetchSkillFiles,\n resolveDefaultRef,\n resolveRefToSha,\n validateRef,\n} from \"./git-client.js\";\nimport { GitHubClient, GitHubClientError, logGitHubAuthHints } from \"./github-client.js\";\nimport { listDirectoryRecursive, withSemaphore } from \"./github-utils.js\";\nimport {\n DEFAULT_NPM_REGISTRY_URL,\n fetchPackument,\n fetchTarball,\n getPackumentVersionDist,\n logNpmAuthHints,\n NpmClientError,\n resolveNpmToken,\n resolvePackumentVersion,\n shasumToSri,\n validateNpmPackageName,\n validateNpmRegistryUrl,\n verifyTarballIntegrity,\n} from \"./npm-client.js\";\nimport {\n getNpmLockedRuleNames,\n getNpmLockedSkillNames,\n getNpmLockedSource,\n type NpmLockedSource,\n type NpmSourcesLock,\n normalizeNpmSourceKey,\n readNpmLockFile,\n setNpmLockedSource,\n writeNpmLockFile,\n} from \"./npm-sources-lock.js\";\nimport { extractPackageTarball } from \"./npm-tar.js\";\nimport { parseSource } from \"./source-parser.js\";\nimport {\n type LockedSkill,\n type LockedRule,\n type LockedSource,\n type SourcesLock,\n computeRuleIntegrity,\n computeSkillIntegrity,\n getLockedRuleNames,\n getLockedSkillNames,\n getLockedSource,\n normalizeSourceKey,\n readLockFile,\n setLockedSource,\n writeLockFile,\n} from \"./sources-lock.js\";\n\nexport type ResolveAndFetchSourcesOptions = {\n /** Force re-resolve all refs, ignoring the lockfile. */\n updateSources?: boolean;\n /** Skip fetching entirely (use what's already on disk). */\n skipSources?: boolean;\n /** Fail if lockfile is missing or doesn't match sources (for CI). */\n frozen?: boolean;\n /** GitHub token for private repositories. */\n token?: string;\n /** Keep lock entries for sources omitted from this invocation. */\n preserveUnlistedLockEntries?: boolean;\n /** Treat a source resolving to no installed or locked skills as a failure. */\n requireResolvedSkills?: boolean;\n /** Treat a source resolving to no installed or locked rules as a failure. */\n requireResolvedRules?: boolean;\n /** Skill names owned by earlier sources and unavailable to this invocation. */\n reservedSkillNames?: string[];\n /** Rule names owned by earlier sources and unavailable to this invocation. */\n reservedRuleNames?: string[];\n};\n\nexport type ResolveAndFetchSourcesResult = {\n fetchedSkillCount: number;\n fetchedRuleCount: number;\n sourcesProcessed: number;\n failedSourceCount: number;\n};\n\nfunction getEarlySourcesResult(params: {\n skipSources: boolean;\n logger: Logger;\n}): ResolveAndFetchSourcesResult | undefined {\n if (!params.skipSources) {\n return undefined;\n }\n if (params.skipSources) {\n params.logger.info(\"Skipping source fetching.\");\n }\n return {\n fetchedSkillCount: 0,\n fetchedRuleCount: 0,\n sourcesProcessed: 0,\n failedSourceCount: 0,\n };\n}\n\ntype RemoteSkillFile = {\n relativePath: string;\n content: string;\n};\n\ntype RemoteRuleFile = {\n name: string;\n content: string;\n};\n\n/**\n * Resolve declared sources, fetch remote rules and skills into their curated\n * directories, and update the lockfile.\n */\nexport async function resolveAndFetchSources(params: {\n sources: SourceEntry[];\n projectRoot: string;\n options?: ResolveAndFetchSourcesOptions;\n logger: Logger;\n}): Promise<ResolveAndFetchSourcesResult> {\n const { sources, projectRoot, options = {}, logger } = params;\n const {\n updateSources = false,\n skipSources = false,\n frozen = false,\n preserveUnlistedLockEntries = false,\n requireResolvedSkills = false,\n requireResolvedRules = false,\n reservedSkillNames = [],\n reservedRuleNames = [],\n } = options;\n const earlyResult = getEarlySourcesResult({\n skipSources,\n logger,\n });\n if (earlyResult) {\n return earlyResult;\n }\n\n await assertSourceOutputPathsAreSafe(projectRoot);\n\n // Read existing lockfiles. npm-transport sources are pinned in a separate\n // lockfile (`rulesync-npm.lock.json`) because they lock a package version +\n // tarball integrity instead of a git commit SHA.\n let lock: SourcesLock = await readLockFile({ projectRoot, logger });\n let npmLock: NpmSourcesLock = await readNpmLockFile({ projectRoot, logger });\n\n // Frozen mode: validate lockfiles cover all declared sources.\n // Missing curated skills are fetched using locked refs.\n validateFrozenLockCoverage({ frozen, lock, npmLock, sources });\n\n const originalLockJson = JSON.stringify(lock);\n const originalNpmLockJson = JSON.stringify(npmLock);\n\n // Resolve GitHub token\n const token = GitHubClient.resolveToken(options.token);\n const client = new GitHubClient({ token });\n\n // Determine local skills (in .rulesync/skills/ but not in .curated/)\n const localSkillNames = await getLocalSkillDirNames(projectRoot);\n const localRuleNames = await getLocalRuleNames(projectRoot);\n\n if (!preserveUnlistedLockEntries && !frozen) {\n await cleanUnlistedSourceArtifacts({ projectRoot, lock, npmLock, sources, logger });\n lock = pruneStaleLockEntries({ lock, sources, logger });\n npmLock = pruneStaleNpmLockEntries({ npmLock, sources, logger });\n }\n\n let totalSkillCount = 0;\n let totalRuleCount = 0;\n let failedSourceCount = 0;\n const allFetchedSkillNames = new Set(reservedSkillNames);\n const allFetchedRuleNames = new Set(reservedRuleNames);\n\n for (const sourceEntry of sources) {\n try {\n const result = await runWithDirectoryRollback({\n directoryPaths: [\n join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH),\n join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH),\n ],\n action: () =>\n fetchSingleSource({\n sourceEntry,\n client,\n projectRoot,\n lock,\n npmLock,\n localSkillNames,\n localRuleNames,\n alreadyFetchedSkillNames: allFetchedSkillNames,\n alreadyFetchedRuleNames: allFetchedRuleNames,\n updateSources,\n frozen,\n logger,\n }),\n });\n\n lock = result.lock;\n npmLock = result.npmLock;\n failedSourceCount += resolvedSourceFailureCount({\n requireSkills: requireResolvedSkills,\n requireRules: requireResolvedRules,\n resolvedSkillNames: result.fetchedSkillNames,\n resolvedRuleNames: result.fetchedRuleNames,\n });\n totalSkillCount += result.skillCount;\n totalRuleCount += result.ruleCount;\n addNamesToSet({ names: result.fetchedSkillNames, target: allFetchedSkillNames });\n addNamesToSet({ names: result.fetchedRuleNames, target: allFetchedRuleNames });\n } catch (error) {\n failedSourceCount += 1;\n logSourceFetchFailure({ sourceEntry, error, logger });\n }\n }\n\n await writeLockFilesIfChanged({\n projectRoot,\n lock,\n npmLock,\n originalLockJson,\n originalNpmLockJson,\n frozen,\n logger,\n });\n\n return {\n fetchedSkillCount: totalSkillCount,\n fetchedRuleCount: totalRuleCount,\n sourcesProcessed: sources.length,\n failedSourceCount,\n };\n}\n\nasync function assertSourceOutputPathsAreSafe(projectRoot: string): Promise<void> {\n const curatedSkillsPath = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n const curatedRulesPath = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n const sourcesLockPath = join(projectRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH);\n const npmSourcesLockPath = join(projectRoot, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH);\n await Promise.all([\n assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath: curatedSkillsPath }),\n assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath: curatedRulesPath }),\n assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath: sourcesLockPath }),\n assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath: npmSourcesLockPath }),\n assertDirectoryIfExists(curatedSkillsPath),\n assertDirectoryIfExists(curatedRulesPath),\n ]);\n if (await directoryExists(curatedSkillsPath)) {\n await assertTreeContainsNoSymlinks(curatedSkillsPath);\n }\n if (await directoryExists(curatedRulesPath)) {\n await assertTreeContainsNoSymlinks(curatedRulesPath);\n }\n}\n\nfunction addNamesToSet(params: { names: string[]; target: Set<string> }): void {\n params.names.forEach((name) => params.target.add(name));\n}\n\nfunction resolvedSourceFailureCount({\n requireSkills,\n requireRules,\n resolvedSkillNames,\n resolvedRuleNames,\n}: {\n requireSkills: boolean;\n requireRules: boolean;\n resolvedSkillNames: string[];\n resolvedRuleNames: string[];\n}): number {\n return (requireSkills && resolvedSkillNames.length === 0) ||\n (requireRules && resolvedRuleNames.length === 0)\n ? 1\n : 0;\n}\n\nfunction getSourceFilters(sourceEntry: SourceEntry): {\n skills: string[] | undefined;\n rules: string[] | undefined;\n} {\n const hasExplicitFeature = sourceEntry.skills !== undefined || sourceEntry.rules !== undefined;\n return {\n skills: sourceEntry.skills ?? (hasExplicitFeature ? undefined : [\"*\"]),\n rules: sourceEntry.rules,\n };\n}\n\nasync function getLocalRuleNames(projectRoot: string): Promise<Set<string>> {\n const rulesDir = join(projectRoot, RULESYNC_RULES_RELATIVE_DIR_PATH);\n const files = await findFilesByGlobs(join(rulesDir, \"**\", \"*.md\"));\n const localNames = new Set<string>();\n for (const file of files) {\n const relativePath = relative(rulesDir, file);\n if (relativePath.startsWith(`.curated${sep}`)) {\n continue;\n }\n localNames.add(relativePath.replace(/\\.md$/i, \"\"));\n }\n return localNames;\n}\n\nexport async function getInstalledSourceSkillNames({\n sources,\n projectRoot,\n logger,\n}: {\n sources: SourceEntry[];\n projectRoot: string;\n logger: Logger;\n}): Promise<string[]> {\n const lock = await readLockFile({ projectRoot, logger });\n const npmLock = await readNpmLockFile({ projectRoot, logger });\n const curatedDir = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n const skillNames = new Set<string>();\n for (const source of sources) {\n const npmTransport = (source.transport ?? \"github\") === \"npm\";\n const entry = npmTransport\n ? getNpmLockedSource(npmLock, source.source)\n : getLockedSource(lock, source.source);\n const lockedSkillNames = entry\n ? npmTransport\n ? getNpmLockedSkillNames(entry as NpmLockedSource)\n : getLockedSkillNames(entry as LockedSource)\n : [];\n if (entry === undefined || !(await checkLockedSkillsExist(curatedDir, lockedSkillNames))) {\n throw new Error(\n `Existing source \"${source.source}\" is not fully installed. Run 'rulesync install' before adding another source.`,\n );\n }\n lockedSkillNames.forEach((skillName) => skillNames.add(skillName));\n }\n return [...skillNames];\n}\n\nexport async function getInstalledSourceRuleNames({\n sources,\n projectRoot,\n logger,\n}: {\n sources: SourceEntry[];\n projectRoot: string;\n logger: Logger;\n}): Promise<string[]> {\n const lock = await readLockFile({ projectRoot, logger });\n const npmLock = await readNpmLockFile({ projectRoot, logger });\n const curatedDir = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n const ruleNames = new Set<string>();\n for (const source of sources) {\n if (getSourceFilters(source).rules === undefined) {\n continue;\n }\n const npmTransport = (source.transport ?? \"github\") === \"npm\";\n const entry = npmTransport\n ? getNpmLockedSource(npmLock, source.source)\n : getLockedSource(lock, source.source);\n const lockedRuleNames = entry\n ? npmTransport\n ? getNpmLockedRuleNames(entry as NpmLockedSource)\n : getLockedRuleNames(entry as LockedSource)\n : [];\n if (\n entry === undefined ||\n entry.rules === undefined ||\n !lockedRuleConfigMatches({ locked: entry, sourceEntry: source }) ||\n !(await checkLockedRulesAreValid({ curatedDir, locked: entry }))\n ) {\n throw new Error(\n `Existing source \"${source.source}\" is not fully installed. Run 'rulesync install' before adding another source.`,\n );\n }\n lockedRuleNames.forEach((ruleName) => ruleNames.add(ruleName));\n }\n return [...ruleNames];\n}\n\n/**\n * Dispatch a single source to the npm fetcher or the git/github fetcher,\n * returning the (possibly) updated lock objects for both lockfiles.\n */\nasync function fetchSingleSource(params: {\n sourceEntry: SourceEntry;\n client: GitHubClient;\n projectRoot: string;\n lock: SourcesLock;\n npmLock: NpmSourcesLock;\n localSkillNames: Set<string>;\n localRuleNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n updateSources: boolean;\n frozen: boolean;\n logger: Logger;\n}): Promise<{\n skillCount: number;\n ruleCount: number;\n fetchedSkillNames: string[];\n fetchedRuleNames: string[];\n lock: SourcesLock;\n npmLock: NpmSourcesLock;\n}> {\n const { sourceEntry, lock, npmLock } = params;\n if ((sourceEntry.transport ?? \"github\") === \"npm\") {\n const result = await fetchSourceViaNpm({\n sourceEntry,\n projectRoot: params.projectRoot,\n npmLock,\n localSkillNames: params.localSkillNames,\n localRuleNames: params.localRuleNames,\n alreadyFetchedSkillNames: params.alreadyFetchedSkillNames,\n alreadyFetchedRuleNames: params.alreadyFetchedRuleNames,\n updateSources: params.updateSources,\n logger: params.logger,\n });\n return {\n skillCount: result.skillCount,\n ruleCount: result.ruleCount,\n fetchedSkillNames: result.fetchedSkillNames,\n fetchedRuleNames: result.fetchedRuleNames,\n lock,\n npmLock: result.updatedLock,\n };\n }\n const filters = getSourceFilters(sourceEntry);\n let updatedLock = lock;\n let skillCount = 0;\n let fetchedSkillNames: string[] = [];\n if (filters.skills !== undefined) {\n const result = await fetchSourceByTransport({\n sourceEntry: { ...sourceEntry, skills: filters.skills },\n client: params.client,\n projectRoot: params.projectRoot,\n lock: updatedLock,\n localSkillNames: params.localSkillNames,\n alreadyFetchedSkillNames: params.alreadyFetchedSkillNames,\n updateSources: params.updateSources,\n frozen: params.frozen,\n logger: params.logger,\n });\n updatedLock = result.updatedLock;\n skillCount = result.skillCount;\n fetchedSkillNames = result.fetchedSkillNames;\n }\n\n let ruleCount = 0;\n let fetchedRuleNames: string[] = [];\n if (filters.rules !== undefined) {\n const result = await fetchRulesByTransport({\n sourceEntry: { ...sourceEntry, rules: filters.rules },\n client: params.client,\n projectRoot: params.projectRoot,\n lock: updatedLock,\n localRuleNames: params.localRuleNames,\n alreadyFetchedRuleNames: params.alreadyFetchedRuleNames,\n // A preceding skill fetch has already resolved and locked this source.\n // Reuse that exact ref so one source cannot mix artifacts from two SHAs.\n updateSources: filters.skills === undefined ? params.updateSources : false,\n forceRefetch: filters.skills !== undefined && params.updateSources,\n frozen: params.frozen,\n logger: params.logger,\n });\n updatedLock = result.updatedLock;\n ruleCount = result.ruleCount;\n fetchedRuleNames = result.fetchedRuleNames;\n } else {\n updatedLock = await clearUndeclaredRules({\n lock: updatedLock,\n sourceEntry,\n projectRoot: params.projectRoot,\n alreadyFetchedRuleNames: params.alreadyFetchedRuleNames,\n logger: params.logger,\n });\n }\n return {\n skillCount,\n ruleCount,\n fetchedSkillNames,\n fetchedRuleNames,\n lock: updatedLock,\n npmLock,\n };\n}\n\nasync function clearUndeclaredRules(params: {\n lock: SourcesLock;\n sourceEntry: SourceEntry;\n projectRoot: string;\n alreadyFetchedRuleNames: Set<string>;\n logger: Logger;\n}): Promise<SourcesLock> {\n const { lock, sourceEntry, projectRoot, alreadyFetchedRuleNames, logger } = params;\n const locked = getLockedSource(lock, sourceEntry.source);\n if (locked?.rules === undefined) {\n return lock;\n }\n await cleanPreviousCuratedRules({\n curatedDir: join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH),\n lockedRuleNames: getLockedRuleNames(locked),\n protectedRuleNames: alreadyFetchedRuleNames,\n logger,\n });\n return setLockedSource(lock, sourceEntry.source, {\n ...locked,\n rules: undefined,\n ruleSelection: undefined,\n rulesPath: undefined,\n resolvedRuleNames: undefined,\n });\n}\n\n/** Log a per-source fetch failure with transport-specific troubleshooting hints. */\nfunction logSourceFetchFailure(params: {\n sourceEntry: SourceEntry;\n error: unknown;\n logger: Logger;\n}): void {\n const { sourceEntry, error, logger } = params;\n logger.error(`Failed to fetch source \"${sourceEntry.source}\": ${formatError(error)}`);\n if (error instanceof GitHubClientError) {\n logGitHubAuthHints({ error, logger });\n } else if (error instanceof GitClientError) {\n logGitClientHints({ error, logger });\n } else if (error instanceof NpmClientError) {\n logNpmAuthHints({ error, logger });\n }\n}\n\n/** Write each lockfile only when it changed (and never in frozen mode). */\nasync function writeLockFilesIfChanged(params: {\n projectRoot: string;\n lock: SourcesLock;\n npmLock: NpmSourcesLock;\n originalLockJson: string;\n originalNpmLockJson: string;\n frozen: boolean;\n logger: Logger;\n}): Promise<void> {\n const { projectRoot, lock, npmLock, originalLockJson, originalNpmLockJson, frozen, logger } =\n params;\n if (!frozen && JSON.stringify(lock) !== originalLockJson) {\n await writeLockFile({ projectRoot, lock, logger });\n } else {\n logger.debug(\"Lockfile unchanged, skipping write.\");\n }\n if (!frozen && JSON.stringify(npmLock) !== originalNpmLockJson) {\n await writeNpmLockFile({ projectRoot, lock: npmLock, logger });\n } else {\n logger.debug(\"npm lockfile unchanged, skipping write.\");\n }\n}\n\n/**\n * Log contextual hints for GitClientError to help users troubleshoot.\n */\nfunction logGitClientHints(params: { error: GitClientError; logger: Logger }): void {\n const { error, logger } = params;\n if (error.message.includes(\"not installed\")) {\n logger.info(\"Hint: Install git and ensure it is available on your PATH.\");\n } else {\n logger.info(\"Hint: Check your git credentials (SSH keys, credential helper, or access token).\");\n }\n}\n\n/**\n * Frozen mode: validate the lockfiles cover every declared source. Throws with\n * remediation guidance listing any uncovered source keys. npm-transport\n * sources are checked against the npm lockfile; everything else against the\n * main sources lockfile.\n */\nfunction assertFrozenLockCoversSources(params: {\n lock: SourcesLock;\n npmLock: NpmSourcesLock;\n sources: SourceEntry[];\n}): void {\n const { lock, npmLock, sources } = params;\n const missingKeys: string[] = [];\n\n for (const source of sources) {\n const locked =\n (source.transport ?? \"github\") === \"npm\"\n ? getNpmLockedSource(npmLock, source.source)\n : getLockedSource(lock, source.source);\n const rulesCovered =\n getSourceFilters(source).rules === undefined ||\n (locked !== undefined && lockedRuleConfigMatches({ locked, sourceEntry: source }));\n if (!locked || !rulesCovered) {\n missingKeys.push(source.source);\n }\n }\n if (missingKeys.length > 0) {\n throw new Error(\n `Frozen install failed: lockfile is missing entries for: ${missingKeys.join(\", \")}. Run 'rulesync install' to update the lockfile.`,\n );\n }\n}\n\nfunction validateFrozenLockCoverage(params: {\n frozen: boolean;\n lock: SourcesLock;\n npmLock: NpmSourcesLock;\n sources: SourceEntry[];\n}): void {\n if (params.frozen) {\n assertFrozenLockCoversSources(params);\n }\n}\n\n/**\n * Dispatch a single source to the transport-specific fetcher (git CLI vs.\n * GitHub REST API), preserving the original default of \"github\".\n */\nasync function fetchSourceByTransport(params: {\n sourceEntry: SourceEntry;\n client: GitHubClient;\n projectRoot: string;\n lock: SourcesLock;\n localSkillNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n updateSources: boolean;\n frozen: boolean;\n logger: Logger;\n}): Promise<{ skillCount: number; fetchedSkillNames: string[]; updatedLock: SourcesLock }> {\n const {\n sourceEntry,\n client,\n projectRoot,\n lock,\n localSkillNames,\n alreadyFetchedSkillNames,\n updateSources,\n frozen,\n logger,\n } = params;\n const transport = sourceEntry.transport ?? \"github\";\n if (transport === \"git\") {\n return fetchSourceViaGit({\n sourceEntry,\n projectRoot,\n lock,\n localSkillNames,\n alreadyFetchedSkillNames,\n updateSources,\n frozen,\n logger,\n });\n }\n return fetchSource({\n sourceEntry,\n client,\n projectRoot,\n lock,\n localSkillNames,\n alreadyFetchedSkillNames,\n updateSources,\n logger,\n });\n}\n\n/**\n * Prune stale lockfile entries whose keys are not in the current sources\n * (immutable — returns a fresh lock object).\n */\nfunction pruneStaleLockEntries(params: {\n lock: SourcesLock;\n sources: SourceEntry[];\n logger: Logger;\n}): SourcesLock {\n const { lock, sources, logger } = params;\n const sourceKeys = new Set(\n sources\n .filter((s) => (s.transport ?? \"github\") !== \"npm\")\n .map((s) => normalizeSourceKey(s.source)),\n );\n const prunedSources: typeof lock.sources = {};\n for (const [key, value] of Object.entries(lock.sources)) {\n if (sourceKeys.has(normalizeSourceKey(key))) {\n prunedSources[key] = value;\n } else {\n logger.debug(`Pruned stale lockfile entry: ${key}`);\n }\n }\n return { lockfileVersion: lock.lockfileVersion, sources: prunedSources };\n}\n\n/**\n * Prune stale npm lockfile entries whose keys are not in the current\n * npm-transport sources (immutable — returns a fresh lock object).\n */\nfunction pruneStaleNpmLockEntries(params: {\n npmLock: NpmSourcesLock;\n sources: SourceEntry[];\n logger: Logger;\n}): NpmSourcesLock {\n const { npmLock, sources, logger } = params;\n const sourceKeys = new Set(\n sources\n .filter((s) => (s.transport ?? \"github\") === \"npm\")\n .map((s) => normalizeNpmSourceKey(s.source)),\n );\n const prunedSources: typeof npmLock.sources = {};\n for (const [key, value] of Object.entries(npmLock.sources)) {\n if (sourceKeys.has(normalizeNpmSourceKey(key))) {\n prunedSources[key] = value;\n } else {\n logger.debug(`Pruned stale npm lockfile entry: ${key}`);\n }\n }\n return { lockfileVersion: npmLock.lockfileVersion, sources: prunedSources };\n}\n\nasync function cleanUnlistedSourceArtifacts(params: {\n projectRoot: string;\n lock: SourcesLock;\n npmLock: NpmSourcesLock;\n sources: SourceEntry[];\n logger: Logger;\n}): Promise<void> {\n const { projectRoot, lock, npmLock, sources, logger } = params;\n const activeGitKeys = new Set(\n sources\n .filter((source) => (source.transport ?? \"github\") !== \"npm\")\n .map((source) => normalizeSourceKey(source.source)),\n );\n const activeNpmKeys = new Set(\n sources\n .filter((source) => (source.transport ?? \"github\") === \"npm\")\n .map((source) => normalizeNpmSourceKey(source.source)),\n );\n const activeEntries = [\n ...Object.entries(lock.sources)\n .filter(([key]) => activeGitKeys.has(normalizeSourceKey(key)))\n .map(([, entry]) => entry),\n ...Object.entries(npmLock.sources)\n .filter(([key]) => activeNpmKeys.has(normalizeNpmSourceKey(key)))\n .map(([, entry]) => entry),\n ];\n const protectedSkillNames = new Set(activeEntries.flatMap((entry) => Object.keys(entry.skills)));\n const protectedRuleNames = new Set(\n activeEntries.flatMap((entry) => Object.keys(entry.rules ?? {})),\n );\n const staleEntries = [\n ...Object.entries(lock.sources)\n .filter(([key]) => !activeGitKeys.has(normalizeSourceKey(key)))\n .map(([, entry]) => entry),\n ...Object.entries(npmLock.sources)\n .filter(([key]) => !activeNpmKeys.has(normalizeNpmSourceKey(key)))\n .map(([, entry]) => entry),\n ];\n const curatedSkillsDir = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n const curatedRulesDir = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n for (const entry of staleEntries) {\n await cleanPreviousCuratedSkills({\n curatedDir: curatedSkillsDir,\n lockedSkillNames: Object.keys(entry.skills),\n protectedSkillNames,\n logger,\n });\n await cleanPreviousCuratedRules({\n curatedDir: curatedRulesDir,\n lockedRuleNames: Object.keys(entry.rules ?? {}),\n protectedRuleNames,\n logger,\n });\n }\n}\n\n/**\n * Check if all locked skills exist on disk in the curated directory.\n */\nasync function checkLockedSkillsExist(curatedDir: string, skillNames: string[]): Promise<boolean> {\n if (skillNames.length === 0) return true;\n for (const name of skillNames) {\n if (!(await directoryExists(join(curatedDir, name)))) {\n return false;\n }\n }\n return true;\n}\n\nasync function checkLockedRulesAreValid(params: {\n curatedDir: string;\n locked: Pick<LockedSource, \"rules\">;\n}): Promise<boolean> {\n for (const [name, entry] of Object.entries(params.locked.rules ?? {})) {\n const filePath = join(params.curatedDir, `${name}.md`);\n if (!(await fileExists(filePath))) {\n return false;\n }\n if (computeRuleIntegrity(await readFileContent(filePath)) !== entry.integrity) {\n return false;\n }\n }\n return true;\n}\n\nasync function canReuseLockedRules(params: {\n locked: LockedSource | NpmLockedSource;\n sourceEntry: SourceEntry;\n lockedRuleNames: string[];\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n curatedDir: string;\n}): Promise<boolean> {\n const {\n locked,\n sourceEntry,\n lockedRuleNames,\n localRuleNames,\n alreadyFetchedRuleNames,\n curatedDir,\n } = params;\n if (!lockedRuleConfigMatches({ locked, sourceEntry })) {\n return false;\n }\n if (locked.resolvedRuleNames === undefined) {\n return false;\n }\n const availableRuleNames = new Set([\n ...lockedRuleNames,\n ...localRuleNames,\n ...alreadyFetchedRuleNames,\n ]);\n if (locked.resolvedRuleNames.some((ruleName) => !availableRuleNames.has(ruleName))) {\n return false;\n }\n if (\n lockedRuleNames.some(\n (ruleName) => localRuleNames.has(ruleName) || alreadyFetchedRuleNames.has(ruleName),\n )\n ) {\n return false;\n }\n return checkLockedRulesAreValid({ curatedDir, locked });\n}\n\n// ---------------------------------------------------------------------------\n// Shared helpers for fetchSource and fetchSourceViaGit\n// ---------------------------------------------------------------------------\n\n/**\n * Remove previously curated skill directories for a source before re-fetching.\n * Validates that each path resolves within the curated directory to prevent traversal.\n */\nasync function cleanPreviousCuratedSkills(params: {\n curatedDir: string;\n lockedSkillNames: string[];\n protectedSkillNames?: Set<string>;\n logger: Logger;\n}): Promise<void> {\n const { curatedDir, lockedSkillNames, protectedSkillNames = new Set(), logger } = params;\n const resolvedCuratedDir = resolve(curatedDir);\n for (const prevSkill of lockedSkillNames) {\n if (protectedSkillNames.has(prevSkill)) {\n continue;\n }\n const prevDir = join(curatedDir, prevSkill);\n if (!resolve(prevDir).startsWith(resolvedCuratedDir + sep)) {\n logger.warn(\n `Skipping removal of \"${prevSkill}\": resolved path is outside the curated directory.`,\n );\n continue;\n }\n if (await directoryExists(prevDir)) {\n await removeDirectoryStrict(prevDir);\n }\n }\n}\n\nasync function cleanPreviousCuratedRules(params: {\n curatedDir: string;\n lockedRuleNames: string[];\n protectedRuleNames: Set<string>;\n logger: Logger;\n}): Promise<void> {\n const { curatedDir, lockedRuleNames, protectedRuleNames, logger } = params;\n const resolvedCuratedDir = resolve(curatedDir);\n for (const prevRule of lockedRuleNames) {\n if (protectedRuleNames.has(prevRule)) {\n continue;\n }\n const prevFile = join(curatedDir, `${prevRule}.md`);\n if (!resolve(prevFile).startsWith(resolvedCuratedDir + sep)) {\n logger.warn(\n `Skipping removal of \"${prevRule}\": resolved path is outside the curated directory.`,\n );\n continue;\n }\n if (await fileExists(prevFile)) {\n await removeFileStrict(prevFile);\n }\n }\n}\n\nasync function replaceCuratedRules(params: {\n rules: RemoteRuleFile[];\n curatedDir: string;\n locked: LockedSource | undefined;\n lockedRuleNames: string[];\n resolvedRef: string;\n sourceKey: string;\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n compareLockedIntegrity: boolean;\n logger: Logger;\n}): Promise<Record<string, LockedRule>> {\n const {\n rules,\n curatedDir,\n locked,\n lockedRuleNames,\n resolvedRef,\n sourceKey,\n localRuleNames,\n alreadyFetchedRuleNames,\n compareLockedIntegrity,\n logger,\n } = params;\n const protectedRuleNames = alreadyFetchedRuleNames;\n const installableRules = rules.filter(\n (rule) =>\n !shouldSkipRule({\n ruleName: rule.name,\n sourceKey,\n localRuleNames,\n alreadyFetchedRuleNames,\n logger,\n }),\n );\n const previousContents = new Map<string, string>();\n for (const name of lockedRuleNames) {\n const path = join(curatedDir, `${name}.md`);\n if (!protectedRuleNames.has(name) && (await fileExists(path))) {\n previousContents.set(name, await readFileContent(path));\n }\n }\n\n try {\n await cleanPreviousCuratedRules({\n curatedDir,\n lockedRuleNames,\n protectedRuleNames,\n logger,\n });\n const fetchedRules: Record<string, LockedRule> = {};\n for (const rule of installableRules) {\n fetchedRules[rule.name] = await writeRuleAndComputeIntegrity({\n rule,\n curatedDir,\n locked,\n resolvedRef,\n sourceKey,\n compareLockedIntegrity,\n logger,\n });\n }\n return fetchedRules;\n } catch (error) {\n for (const rule of installableRules) {\n await removeFileStrict(join(curatedDir, `${rule.name}.md`));\n }\n for (const [name, content] of previousContents) {\n await writeFileContent(join(curatedDir, `${name}.md`), content);\n }\n throw error;\n }\n}\n\n/**\n * Check whether a skill should be skipped during fetching.\n * Returns true (with appropriate logging) if the skill should be skipped.\n */\nfunction shouldSkipSkill(params: {\n skillName: string;\n sourceKey: string;\n localSkillNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n logger: Logger;\n}): boolean {\n const { skillName, sourceKey, localSkillNames, alreadyFetchedSkillNames, logger } = params;\n if (skillName.includes(\"..\") || skillName.includes(\"/\") || skillName.includes(\"\\\\\")) {\n logger.warn(\n `Skipping skill with invalid name \"${skillName}\" from ${sourceKey}: contains path traversal characters.`,\n );\n return true;\n }\n if (localSkillNames.has(skillName)) {\n logger.debug(\n `Skipping remote skill \"${skillName}\" from ${sourceKey}: local skill takes precedence.`,\n );\n return true;\n }\n if (alreadyFetchedSkillNames.has(skillName)) {\n logger.warn(\n `Skipping duplicate skill \"${skillName}\" from ${sourceKey}: already fetched from another source.`,\n );\n return true;\n }\n return false;\n}\n\nfunction shouldSkipRule(params: {\n ruleName: string;\n sourceKey: string;\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n logger: Logger;\n}): boolean {\n const { ruleName, sourceKey, localRuleNames, alreadyFetchedRuleNames, logger } = params;\n if (!isValidRuleName(ruleName)) {\n logger.warn(`Skipping rule with invalid name \"${ruleName}\" from ${sourceKey}.`);\n return true;\n }\n if (localRuleNames.has(ruleName)) {\n logger.debug(\n `Skipping remote rule \"${ruleName}\" from ${sourceKey}: local rule takes precedence.`,\n );\n return true;\n }\n if (alreadyFetchedRuleNames.has(ruleName)) {\n logger.warn(\n `Skipping duplicate rule \"${ruleName}\" from ${sourceKey}: already fetched from another source.`,\n );\n return true;\n }\n return false;\n}\n\nfunction isValidRuleName(ruleName: string): boolean {\n return !(\n ruleName.includes(\"..\") ||\n ruleName.includes(\"/\") ||\n ruleName.includes(\"\\\\\") ||\n ruleName.length === 0 ||\n [\"__proto__\", \"constructor\", \"prototype\"].includes(ruleName)\n );\n}\n\nasync function writeRuleAndComputeIntegrity(params: {\n rule: RemoteRuleFile;\n curatedDir: string;\n locked: LockedSource | undefined;\n resolvedRef: string;\n sourceKey: string;\n compareLockedIntegrity?: boolean;\n logger: Logger;\n}): Promise<LockedRule> {\n const {\n rule,\n curatedDir,\n locked,\n resolvedRef,\n sourceKey,\n compareLockedIntegrity = true,\n logger,\n } = params;\n const relativePath = `${rule.name}.md`;\n checkPathTraversal({ relativePath, intendedRootDir: curatedDir });\n await writeFileContent(join(curatedDir, relativePath), rule.content);\n const integrity = computeRuleIntegrity(rule.content);\n const lockedRuleEntry = locked?.rules?.[rule.name];\n if (\n compareLockedIntegrity &&\n lockedRuleEntry?.integrity &&\n lockedRuleEntry.integrity !== integrity &&\n resolvedRef === locked?.resolvedRef\n ) {\n logger.warn(\n `Integrity mismatch for rule \"${rule.name}\" from ${sourceKey}: expected \"${lockedRuleEntry.integrity}\", got \"${integrity}\". Content may have been tampered with.`,\n );\n }\n return { integrity };\n}\n\n/**\n * Write skill files to disk, compute integrity, and check against the lockfile.\n * Returns the computed LockedSkill entry.\n */\nasync function writeSkillAndComputeIntegrity(params: {\n skillName: string;\n files: Array<{ relativePath: string; content: string }>;\n curatedDir: string;\n locked: LockedSource | undefined;\n resolvedSha: string;\n sourceKey: string;\n logger: Logger;\n}): Promise<LockedSkill> {\n const { skillName, files, curatedDir, locked, resolvedSha, sourceKey, logger } = params;\n const written: Array<{ path: string; content: string }> = [];\n\n for (const file of files) {\n checkPathTraversal({\n relativePath: file.relativePath,\n intendedRootDir: join(curatedDir, skillName),\n });\n await writeFileContent(join(curatedDir, skillName, file.relativePath), file.content);\n written.push({ path: file.relativePath, content: file.content });\n }\n\n const integrity = computeSkillIntegrity(written);\n const lockedSkillEntry = locked?.skills[skillName];\n if (\n lockedSkillEntry?.integrity &&\n lockedSkillEntry.integrity !== integrity &&\n resolvedSha === locked?.resolvedRef\n ) {\n logger.warn(\n `Integrity mismatch for skill \"${skillName}\" from ${sourceKey}: expected \"${lockedSkillEntry.integrity}\", got \"${integrity}\". Content may have been tampered with.`,\n );\n }\n\n return { integrity };\n}\n\n/**\n * Merge back locked skills that still exist in the remote but were skipped\n * during fetching (due to local precedence, already-fetched, etc.). Skills no\n * longer present in the remote (e.g. renamed or deleted upstream) are\n * intentionally dropped. Shared by the git/github and npm lock updates.\n */\nfunction mergeFetchedWithLockedSkills(params: {\n fetchedSkills: Record<string, LockedSkill>;\n lockedSkills: Record<string, LockedSkill> | undefined;\n remoteSkillNames: string[];\n}): Record<string, LockedSkill> {\n const { fetchedSkills, lockedSkills, remoteSkillNames } = params;\n const remoteSet = new Set(remoteSkillNames);\n const mergedSkills: Record<string, LockedSkill> = { ...fetchedSkills };\n if (lockedSkills) {\n for (const [skillName, skillEntry] of Object.entries(lockedSkills)) {\n if (!(skillName in mergedSkills) && remoteSet.has(skillName)) {\n mergedSkills[skillName] = skillEntry;\n }\n }\n }\n return mergedSkills;\n}\n\nfunction assertMatchingSkillsFound({\n skillNames,\n source,\n}: {\n skillNames: string[];\n source: string;\n}): void {\n if (skillNames.length === 0) {\n throw new Error(`No matching skills found in ${source}.`);\n }\n}\n\n/**\n * Merge newly fetched skills with existing locked skills and update the lockfile.\n */\nfunction buildLockUpdate(params: {\n lock: SourcesLock;\n sourceKey: string;\n fetchedSkills: Record<string, LockedSkill>;\n locked: LockedSource | undefined;\n requestedRef: string | undefined;\n resolvedSha: string;\n remoteSkillNames: string[];\n logger: Logger;\n}): { updatedLock: SourcesLock; fetchedNames: string[] } {\n const {\n lock,\n sourceKey,\n fetchedSkills,\n locked,\n requestedRef,\n resolvedSha,\n remoteSkillNames,\n logger,\n } = params;\n const fetchedNames = Object.keys(fetchedSkills);\n\n const mergedSkills = mergeFetchedWithLockedSkills({\n fetchedSkills,\n lockedSkills: locked?.skills,\n remoteSkillNames,\n });\n\n const updatedLock = setLockedSource(lock, sourceKey, {\n requestedRef,\n resolvedRef: resolvedSha,\n resolvedAt: new Date().toISOString(),\n skills: mergedSkills,\n rules: locked?.rules ?? {},\n ruleSelection: locked?.ruleSelection,\n rulesPath: locked?.rulesPath,\n resolvedRuleNames: locked?.resolvedRuleNames,\n });\n\n logger.info(\n `Fetched ${fetchedNames.length} skill(s) from ${sourceKey}: ${fetchedNames.join(\", \") || \"(none)\"}`,\n );\n\n return { updatedLock, fetchedNames };\n}\n\nfunction buildRuleLockUpdate(params: {\n lock: SourcesLock;\n sourceKey: string;\n fetchedRules: Record<string, LockedRule>;\n locked: LockedSource | undefined;\n requestedRef: string | undefined;\n resolvedRef: string;\n ruleSelection: string[];\n rulesPath: string;\n resolvedRuleNames: string[];\n logger: Logger;\n}): { updatedLock: SourcesLock; fetchedNames: string[] } {\n const {\n lock,\n sourceKey,\n fetchedRules,\n locked,\n requestedRef,\n resolvedRef,\n ruleSelection,\n rulesPath,\n resolvedRuleNames,\n logger,\n } = params;\n const fetchedNames = Object.keys(fetchedRules);\n const updatedLock = setLockedSource(lock, sourceKey, {\n requestedRef,\n resolvedRef,\n resolvedAt: new Date().toISOString(),\n skills: locked?.skills ?? {},\n rules: fetchedRules,\n ruleSelection,\n rulesPath,\n resolvedRuleNames,\n });\n logger.info(\n `Fetched ${fetchedNames.length} rule(s) from ${sourceKey}: ${fetchedNames.join(\", \") || \"(none)\"}`,\n );\n return { updatedLock, fetchedNames };\n}\n\nfunction getFirstPathSeparatorIndex(path: string): number {\n const slashIndex = path.indexOf(\"/\");\n const backslashIndex = path.indexOf(\"\\\\\");\n if (slashIndex === -1) return backslashIndex;\n if (backslashIndex === -1) return slashIndex;\n return Math.min(slashIndex, backslashIndex);\n}\n\n/**\n * Decide whether a repository's root-level files should be installed as the\n * single requested skill (the \"root fallback\").\n *\n * A root fallback fires only when a single, non-wildcard skill was requested,\n * that skill's own directory is absent, and the repository root actually carries\n * a `SKILL.md`. Both the git transport (`groupRemoteFilesBySkillRoot`) and the\n * GitHub transport (`discoverGithubSkillDirs`) gate on these same conditions, so\n * the decision lives here to keep the two paths from drifting.\n */\nfunction shouldUseRootFallback(params: {\n skillFilter: string[];\n isWildcard: boolean;\n hasRootSkillFile: boolean;\n hasRequestedSkillDir: boolean;\n}): boolean {\n const { skillFilter, isWildcard, hasRootSkillFile, hasRequestedSkillDir } = params;\n const [singleSkillName] = skillFilter;\n return (\n !isWildcard &&\n skillFilter.length === 1 &&\n singleSkillName !== undefined &&\n hasRootSkillFile &&\n !hasRequestedSkillDir\n );\n}\n\nfunction groupRemoteFilesBySkillRoot(params: {\n remoteFiles: RemoteSkillFile[];\n skillFilter: string[];\n isWildcard: boolean;\n}): Map<string, RemoteSkillFile[]> {\n const { remoteFiles, skillFilter, isWildcard } = params;\n const grouped = new Map<string, RemoteSkillFile[]>();\n const rootLevelFiles: RemoteSkillFile[] = [];\n\n for (const file of remoteFiles) {\n const separatorIndex = getFirstPathSeparatorIndex(file.relativePath);\n if (separatorIndex === -1) {\n rootLevelFiles.push(file);\n continue;\n }\n\n const skillName = file.relativePath.substring(0, separatorIndex);\n if (skillName.length === 0) {\n continue;\n }\n\n const innerPath = file.relativePath.substring(separatorIndex + 1);\n const groupedFiles = grouped.get(skillName) ?? [];\n groupedFiles.push({ relativePath: innerPath, content: file.content });\n grouped.set(skillName, groupedFiles);\n }\n\n const [singleSkillName] = skillFilter;\n const hasRootSkillFile = rootLevelFiles.some((file) => file.relativePath === SKILL_FILE_NAME);\n if (\n singleSkillName !== undefined &&\n shouldUseRootFallback({\n skillFilter,\n isWildcard,\n hasRootSkillFile,\n hasRequestedSkillDir: grouped.has(singleSkillName),\n })\n ) {\n grouped.set(singleSkillName, rootLevelFiles);\n }\n\n return grouped;\n}\n\n// ---------------------------------------------------------------------------\n// Transport-specific fetch functions\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a GitHub source's ref to a commit SHA, preferring the locked SHA for\n * deterministic fetches and otherwise resolving the declared ref or default\n * branch. Returns the on-disk `ref` (SHA when freshly resolved, else locked\n * ref), the resolved SHA, and the requested ref.\n */\nasync function resolveGithubFetchRef(params: {\n parsed: ParsedSource;\n locked: LockedSource | undefined;\n updateSources: boolean;\n sourceKey: string;\n client: GitHubClient;\n logger: Logger;\n}): Promise<{ ref: string; resolvedSha: string; requestedRef: string | undefined }> {\n const { parsed, locked, updateSources, sourceKey, client, logger } = params;\n if (locked && !updateSources) {\n // Use the locked SHA for deterministic fetching\n logger.debug(`Using locked ref for ${sourceKey}: ${locked.resolvedRef}`);\n return {\n ref: locked.resolvedRef,\n resolvedSha: locked.resolvedRef,\n requestedRef: locked.requestedRef,\n };\n }\n // Resolve the ref (or default branch) to a SHA\n const requestedRef = parsed.ref ?? (await client.getDefaultBranch(parsed.owner, parsed.repo));\n const resolvedSha = await client.resolveRefToSha(parsed.owner, parsed.repo, requestedRef);\n logger.debug(`Resolved ${sourceKey} ref \"${requestedRef}\" to SHA: ${resolvedSha}`);\n return { ref: resolvedSha, resolvedSha, requestedRef };\n}\n\nfunction normalizeRuleFilterName(name: string): string {\n return name.replace(/\\.md$/i, \"\");\n}\n\nfunction normalizeRuleSelection(rules: string[]): string[] {\n return [...new Set(rules.map(normalizeRuleFilterName))].toSorted();\n}\n\nfunction normalizeRulesPath(rulesPath: string | undefined): string {\n return posix.normalize((rulesPath ?? \"rules\").replace(/\\\\/g, \"/\")).replace(/\\/+$/, \"\");\n}\n\nfunction lockedRuleConfigMatches(params: {\n locked: Pick<LockedSource, \"ruleSelection\" | \"rulesPath\">;\n sourceEntry: SourceEntry;\n}): boolean {\n const rules = getSourceFilters(params.sourceEntry).rules;\n if (rules === undefined || params.locked.ruleSelection === undefined) {\n return false;\n }\n const selection = normalizeRuleSelection(rules);\n return (\n selection.length === params.locked.ruleSelection.length &&\n selection.every((ruleName, index) => ruleName === params.locked.ruleSelection?.[index]) &&\n normalizeRulesPath(params.sourceEntry.rulesPath) === params.locked.rulesPath\n );\n}\n\nfunction assertMatchingRulesFound(params: { ruleNames: string[]; source: string }): void {\n if (params.ruleNames.length === 0) {\n throw new Error(`No matching rules found in ${params.source}.`);\n }\n}\n\nasync function fetchRulesByTransport(params: {\n sourceEntry: SourceEntry;\n client: GitHubClient;\n projectRoot: string;\n lock: SourcesLock;\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n updateSources: boolean;\n forceRefetch: boolean;\n frozen: boolean;\n logger: Logger;\n}): Promise<{ ruleCount: number; fetchedRuleNames: string[]; updatedLock: SourcesLock }> {\n if ((params.sourceEntry.transport ?? \"github\") === \"git\") {\n return fetchRulesViaGit(params);\n }\n return fetchRulesViaGithub(params);\n}\n\nasync function fetchRulesViaGithub(params: {\n sourceEntry: SourceEntry;\n client: GitHubClient;\n projectRoot: string;\n lock: SourcesLock;\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n updateSources: boolean;\n forceRefetch: boolean;\n logger: Logger;\n}): Promise<{ ruleCount: number; fetchedRuleNames: string[]; updatedLock: SourcesLock }> {\n const {\n sourceEntry,\n client,\n projectRoot,\n lock,\n localRuleNames,\n alreadyFetchedRuleNames,\n updateSources,\n forceRefetch,\n logger,\n } = params;\n const parsedFromSource = parseSource(sourceEntry.source);\n const parsed: ParsedSource = {\n ...parsedFromSource,\n ref: sourceEntry.ref ?? parsedFromSource.ref,\n };\n if (parsed.provider === \"gitlab\") {\n throw new Error(`GitLab sources are not yet supported: \"${sourceEntry.source}\".`);\n }\n const sourceKey = sourceEntry.source;\n const locked = getLockedSource(lock, sourceKey);\n const lockedRuleNames = locked ? getLockedRuleNames(locked) : [];\n const { ref, resolvedSha, requestedRef } = await resolveGithubFetchRef({\n parsed,\n locked,\n updateSources,\n sourceKey,\n client,\n logger,\n });\n const curatedDir = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n if (\n locked &&\n resolvedSha === locked.resolvedRef &&\n !updateSources &&\n !forceRefetch &&\n (await canReuseLockedRules({\n locked,\n sourceEntry,\n lockedRuleNames,\n localRuleNames,\n alreadyFetchedRuleNames,\n curatedDir,\n }))\n ) {\n logger.debug(`SHA unchanged for ${sourceKey} rules, skipping re-fetch.`);\n return { ruleCount: 0, fetchedRuleNames: lockedRuleNames, updatedLock: lock };\n }\n\n const ruleFilter = (sourceEntry.rules ?? []).map(normalizeRuleFilterName);\n const isWildcard = ruleFilter.length === 1 && ruleFilter[0] === \"*\";\n const rulesPath = sourceEntry.rulesPath ?? \"rules\";\n let entries: GitHubFileEntry[];\n try {\n entries = await client.listDirectory(parsed.owner, parsed.repo, rulesPath, ref);\n } catch (error) {\n if (error instanceof GitHubClientError && error.statusCode === 404) {\n throw new Error(`No ${rulesPath}/ directory found in ${sourceKey}.`, { cause: error });\n }\n throw error;\n }\n const remoteRules = entries\n .filter((entry) => entry.type === \"file\" && entry.name.toLowerCase().endsWith(\".md\"))\n .map((entry) => ({ entry, name: normalizeRuleFilterName(entry.name) }))\n .filter(({ name }) => (isWildcard || ruleFilter.includes(name)) && isValidRuleName(name));\n const remoteRuleNames = remoteRules.map(({ name }) => name);\n assertMatchingRulesFound({ ruleNames: remoteRuleNames, source: sourceKey });\n const preparedRules: RemoteRuleFile[] = [];\n for (const { entry, name } of remoteRules) {\n if (entry.size > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping rule \"${entry.path}\" (${(entry.size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit).`,\n );\n continue;\n }\n if (\n shouldSkipRule({\n ruleName: name,\n sourceKey,\n localRuleNames,\n alreadyFetchedRuleNames,\n logger,\n })\n ) {\n continue;\n }\n const content = await client.getFileContent(parsed.owner, parsed.repo, entry.path, ref);\n preparedRules.push({ name, content });\n }\n const fetchedRules = await replaceCuratedRules({\n rules: preparedRules,\n curatedDir,\n locked,\n lockedRuleNames,\n resolvedRef: resolvedSha,\n sourceKey,\n localRuleNames,\n alreadyFetchedRuleNames,\n compareLockedIntegrity: !updateSources && !forceRefetch,\n logger,\n });\n const result = buildRuleLockUpdate({\n lock,\n sourceKey,\n fetchedRules,\n locked,\n requestedRef,\n resolvedRef: resolvedSha,\n ruleSelection: normalizeRuleSelection(sourceEntry.rules ?? []),\n rulesPath: normalizeRulesPath(sourceEntry.rulesPath),\n resolvedRuleNames: remoteRuleNames,\n logger,\n });\n return {\n ruleCount: result.fetchedNames.length,\n fetchedRuleNames: result.fetchedNames,\n updatedLock: result.updatedLock,\n };\n}\n\nasync function fetchRulesViaGit(params: {\n sourceEntry: SourceEntry;\n projectRoot: string;\n lock: SourcesLock;\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n updateSources: boolean;\n forceRefetch: boolean;\n frozen: boolean;\n logger: Logger;\n}): Promise<{ ruleCount: number; fetchedRuleNames: string[]; updatedLock: SourcesLock }> {\n const {\n sourceEntry,\n projectRoot,\n lock,\n localRuleNames,\n alreadyFetchedRuleNames,\n updateSources,\n forceRefetch,\n frozen,\n logger,\n } = params;\n const sourceKey = sourceEntry.source;\n const locked = getLockedSource(lock, sourceKey);\n const lockedRuleNames = locked ? getLockedRuleNames(locked) : [];\n let resolvedRef: string;\n let requestedRef: string | undefined;\n if (locked && !updateSources) {\n resolvedRef = locked.resolvedRef;\n requestedRef = locked.requestedRef;\n if (requestedRef) validateRef(requestedRef);\n } else if (sourceEntry.ref) {\n requestedRef = sourceEntry.ref;\n resolvedRef = await resolveRefToSha(sourceKey, requestedRef);\n } else {\n const defaultRef = await resolveDefaultRef(sourceKey);\n requestedRef = defaultRef.ref;\n resolvedRef = defaultRef.sha;\n }\n const curatedDir = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n if (\n locked &&\n resolvedRef === locked.resolvedRef &&\n !updateSources &&\n !forceRefetch &&\n (await canReuseLockedRules({\n locked,\n sourceEntry,\n lockedRuleNames,\n localRuleNames,\n alreadyFetchedRuleNames,\n curatedDir,\n }))\n ) {\n return { ruleCount: 0, fetchedRuleNames: lockedRuleNames, updatedLock: lock };\n }\n if (!requestedRef) {\n if (frozen) {\n throw new Error(\n `Frozen install failed: lockfile entry for \"${sourceKey}\" is missing requestedRef. Run 'rulesync install' to update the lockfile.`,\n );\n }\n const defaultRef = await resolveDefaultRef(sourceKey);\n requestedRef = defaultRef.ref;\n resolvedRef = defaultRef.sha;\n }\n const files = await fetchSkillFiles({\n url: sourceKey,\n ref: requestedRef,\n resolvedRef,\n skillsPath: sourceEntry.rulesPath ?? \"rules\",\n logger,\n });\n const ruleFilter = (sourceEntry.rules ?? []).map(normalizeRuleFilterName);\n const isWildcard = ruleFilter.length === 1 && ruleFilter[0] === \"*\";\n const remoteRules = files\n .filter(\n (file) =>\n getFirstPathSeparatorIndex(file.relativePath) === -1 &&\n file.relativePath.toLowerCase().endsWith(\".md\"),\n )\n .map((file) => ({ name: normalizeRuleFilterName(file.relativePath), content: file.content }))\n .filter((rule) => (isWildcard || ruleFilter.includes(rule.name)) && isValidRuleName(rule.name));\n const remoteRuleNames = remoteRules.map((rule) => rule.name);\n assertMatchingRulesFound({ ruleNames: remoteRuleNames, source: sourceKey });\n const fetchedRules = await replaceCuratedRules({\n rules: remoteRules,\n curatedDir,\n locked,\n lockedRuleNames,\n resolvedRef,\n sourceKey,\n localRuleNames,\n alreadyFetchedRuleNames,\n compareLockedIntegrity: !updateSources && !forceRefetch,\n logger,\n });\n const result = buildRuleLockUpdate({\n lock,\n sourceKey,\n fetchedRules,\n locked,\n requestedRef,\n resolvedRef,\n ruleSelection: normalizeRuleSelection(sourceEntry.rules ?? []),\n rulesPath: normalizeRulesPath(sourceEntry.rulesPath),\n resolvedRuleNames: remoteRuleNames,\n logger,\n });\n return {\n ruleCount: result.fetchedNames.length,\n fetchedRuleNames: result.fetchedNames,\n updatedLock: result.updatedLock,\n };\n}\n\n/**\n * Fallback path used when an explicit single-skill source points at a flat skill\n * with root-level files. Fetches and writes that skill into `fetchedSkills`.\n * Returns whether the fallback fired and the resulting remote skill names.\n */\nasync function fetchRootLevelFallbackSkill(params: {\n entries: GitHubFileEntry[];\n parsed: ParsedSource;\n ref: string;\n resolvedSha: string;\n skillFilter: string[];\n isWildcard: boolean;\n curatedDir: string;\n locked: LockedSource | undefined;\n sourceKey: string;\n localSkillNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n client: GitHubClient;\n semaphore: Semaphore;\n fetchedSkills: Record<string, LockedSkill>;\n logger: Logger;\n}): Promise<{ handled: boolean; remoteSkillNames: string[] }> {\n const {\n entries,\n parsed,\n ref,\n resolvedSha,\n skillFilter,\n isWildcard,\n curatedDir,\n locked,\n sourceKey,\n localSkillNames,\n alreadyFetchedSkillNames,\n client,\n semaphore,\n fetchedSkills,\n logger,\n } = params;\n\n const rootFiles = entries.filter((entry) => entry.type === \"file\");\n const rootSkillFiles: RemoteSkillFile[] = [];\n\n for (const file of rootFiles) {\n if (file.size > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping file \"${file.path}\" (${(file.size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit).`,\n );\n continue;\n }\n const content = await withSemaphore(semaphore, () =>\n client.getFileContent(parsed.owner, parsed.repo, file.path, ref),\n );\n rootSkillFiles.push({ relativePath: file.name, content });\n }\n\n const groupedRootFiles = groupRemoteFilesBySkillRoot({\n remoteFiles: rootSkillFiles,\n skillFilter,\n isWildcard,\n });\n const [fallbackSkillName] = groupedRootFiles.keys();\n if (fallbackSkillName === undefined) {\n return { handled: false, remoteSkillNames: [] };\n }\n\n if (\n !shouldSkipSkill({\n skillName: fallbackSkillName,\n sourceKey,\n localSkillNames,\n alreadyFetchedSkillNames,\n logger,\n })\n ) {\n fetchedSkills[fallbackSkillName] = await writeSkillAndComputeIntegrity({\n skillName: fallbackSkillName,\n files: groupedRootFiles.get(fallbackSkillName) ?? [],\n curatedDir,\n locked,\n resolvedSha,\n sourceKey,\n logger,\n });\n logger.debug(`Fetched skill \"${fallbackSkillName}\" from ${sourceKey}`);\n }\n\n return { handled: true, remoteSkillNames: [fallbackSkillName] };\n}\n\n/**\n * Recursively fetch and write a single skill directory's files via the GitHub\n * REST API, returning its computed LockedSkill entry.\n */\nasync function fetchGithubSkillDir(params: {\n skillDir: { name: string; path: string };\n parsed: ParsedSource;\n ref: string;\n resolvedSha: string;\n curatedDir: string;\n locked: LockedSource | undefined;\n sourceKey: string;\n client: GitHubClient;\n semaphore: Semaphore;\n logger: Logger;\n}): Promise<LockedSkill> {\n const {\n skillDir,\n parsed,\n ref,\n resolvedSha,\n curatedDir,\n locked,\n sourceKey,\n client,\n semaphore,\n logger,\n } = params;\n\n // Recursively fetch all files in this skill directory\n const allFiles = await listDirectoryRecursive({\n client,\n owner: parsed.owner,\n repo: parsed.repo,\n path: skillDir.path,\n ref,\n semaphore,\n });\n\n // Filter out files exceeding MAX_FILE_SIZE\n const files = allFiles.filter((file) => {\n if (file.size > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping file \"${file.path}\" (${(file.size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit).`,\n );\n return false;\n }\n return true;\n });\n\n // Fetch all file contents\n const skillFiles: Array<{ relativePath: string; content: string }> = [];\n for (const file of files) {\n const relativeToSkill = file.path.substring(skillDir.path.length + 1);\n const content = await withSemaphore(semaphore, () =>\n client.getFileContent(parsed.owner, parsed.repo, file.path, ref),\n );\n skillFiles.push({ relativePath: relativeToSkill, content });\n }\n\n return writeSkillAndComputeIntegrity({\n skillName: skillDir.name,\n files: skillFiles,\n curatedDir,\n locked,\n resolvedSha,\n sourceKey,\n logger,\n });\n}\n\n/**\n * List the remote skills directory and apply the root-level fallback. Returns a\n * `notFound` sentinel when the directory 404s (so the caller can skip the\n * source), otherwise the discovered skill subdirectories plus any fallback skill\n * names already written into `fetchedSkills`.\n */\nasync function discoverGithubSkillDirs(params: {\n parsed: ParsedSource;\n ref: string;\n resolvedSha: string;\n skillFilter: string[];\n isWildcard: boolean;\n curatedDir: string;\n locked: LockedSource | undefined;\n sourceKey: string;\n localSkillNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n client: GitHubClient;\n semaphore: Semaphore;\n fetchedSkills: Record<string, LockedSkill>;\n logger: Logger;\n}): Promise<\n | { status: \"notFound\" }\n | {\n status: \"ok\";\n remoteSkillDirs: Array<{ name: string; path: string }>;\n fallbackHandled: boolean;\n remoteSkillNames: string[];\n }\n> {\n const {\n parsed,\n ref,\n resolvedSha,\n skillFilter,\n isWildcard,\n curatedDir,\n locked,\n sourceKey,\n localSkillNames,\n alreadyFetchedSkillNames,\n client,\n semaphore,\n fetchedSkills,\n logger,\n } = params;\n\n const skillsBasePath = parsed.path ?? \"skills\";\n try {\n const entries = await client.listDirectory(parsed.owner, parsed.repo, skillsBasePath, ref);\n const remoteSkillDirs = entries\n .filter((e) => e.type === \"dir\")\n .map((e) => ({ name: e.name, path: e.path }));\n\n const [singleSkillName] = skillFilter;\n const hasRequestedSkillDir =\n singleSkillName !== undefined && remoteSkillDirs.some((d) => d.name === singleSkillName);\n // Detect a root-level SKILL.md from the directory listing we already have, so\n // the fallback (and its full root-file fetch) is skipped when there is no\n // root skill to install — not just when the requested dir is absent.\n const hasRootSkillFile = entries.some(\n (entry) => entry.type === \"file\" && entry.name === SKILL_FILE_NAME,\n );\n if (\n shouldUseRootFallback({ skillFilter, isWildcard, hasRootSkillFile, hasRequestedSkillDir })\n ) {\n if (locked) {\n await cleanPreviousCuratedSkills({\n curatedDir,\n lockedSkillNames: Object.keys(locked.skills),\n logger,\n });\n }\n const fallback = await fetchRootLevelFallbackSkill({\n entries,\n parsed,\n ref,\n resolvedSha,\n skillFilter,\n isWildcard,\n curatedDir,\n locked,\n sourceKey,\n localSkillNames,\n alreadyFetchedSkillNames,\n client,\n semaphore,\n fetchedSkills,\n logger,\n });\n if (fallback.handled) {\n return {\n status: \"ok\",\n remoteSkillDirs,\n fallbackHandled: true,\n remoteSkillNames: fallback.remoteSkillNames,\n };\n }\n }\n\n return { status: \"ok\", remoteSkillDirs, fallbackHandled: false, remoteSkillNames: [] };\n } catch (error) {\n if (error instanceof GitHubClientError && error.statusCode === 404) {\n return { status: \"notFound\" };\n }\n throw error;\n }\n}\n\n/**\n * Fetch skills from a single source entry via the GitHub REST API.\n */\nasync function fetchSource(params: {\n sourceEntry: SourceEntry;\n client: GitHubClient;\n projectRoot: string;\n lock: SourcesLock;\n localSkillNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n updateSources: boolean;\n logger: Logger;\n}): Promise<{\n skillCount: number;\n fetchedSkillNames: string[];\n updatedLock: SourcesLock;\n}> {\n const {\n sourceEntry,\n client,\n projectRoot,\n localSkillNames,\n alreadyFetchedSkillNames,\n updateSources,\n logger,\n } = params;\n const { lock } = params;\n\n const parsedFromSource = parseSource(sourceEntry.source);\n const parsed: ParsedSource = {\n ...parsedFromSource,\n ref: sourceEntry.ref ?? parsedFromSource.ref,\n path: sourceEntry.path ?? parsedFromSource.path,\n };\n\n if (parsed.provider === \"gitlab\") {\n throw new Error(`GitLab sources are not yet supported: \"${sourceEntry.source}\".`);\n }\n\n const sourceKey = sourceEntry.source;\n const locked = getLockedSource(lock, sourceKey);\n const lockedSkillNames = locked ? getLockedSkillNames(locked) : [];\n\n // Resolve the ref to a commit SHA\n const { ref, resolvedSha, requestedRef } = await resolveGithubFetchRef({\n parsed,\n locked,\n updateSources,\n sourceKey,\n client,\n logger,\n });\n\n const curatedDir = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n\n // Skip re-fetch if SHA matches lockfile and curated skills exist on disk\n if (locked && resolvedSha === locked.resolvedRef && !updateSources) {\n const allExist = await checkLockedSkillsExist(curatedDir, lockedSkillNames);\n if (allExist) {\n logger.debug(`SHA unchanged for ${sourceKey}, skipping re-fetch.`);\n return {\n skillCount: 0,\n fetchedSkillNames: lockedSkillNames,\n updatedLock: lock,\n };\n }\n }\n\n // Determine which skills to fetch\n const skillFilter = sourceEntry.skills ?? [\"*\"];\n const isWildcard = skillFilter.length === 1 && skillFilter[0] === \"*\";\n const semaphore = new Semaphore(FETCH_CONCURRENCY_LIMIT);\n const fetchedSkills: Record<string, LockedSkill> = {};\n\n // List the skills/ directory in the remote repo.\n // If a path is given in the source URL, it points directly to the skills directory.\n // Otherwise, look for \"skills/\" at the repo root.\n const discovery = await discoverGithubSkillDirs({\n parsed,\n ref,\n resolvedSha,\n skillFilter,\n isWildcard,\n curatedDir,\n locked,\n sourceKey,\n localSkillNames,\n alreadyFetchedSkillNames,\n client,\n semaphore,\n fetchedSkills,\n logger,\n });\n if (discovery.status === \"notFound\") {\n throw new Error(`No skills/ directory found in ${sourceKey}.`);\n }\n const { remoteSkillDirs, fallbackHandled, remoteSkillNames: fallbackSkillNames } = discovery;\n\n // Filter skills by name\n const filteredDirs = isWildcard\n ? remoteSkillDirs\n : remoteSkillDirs.filter((d) => skillFilter.includes(d.name));\n const remoteSkillNames = fallbackHandled ? fallbackSkillNames : filteredDirs.map((d) => d.name);\n assertMatchingSkillsFound({ skillNames: remoteSkillNames, source: sourceKey });\n\n if (locked && !fallbackHandled) {\n await cleanPreviousCuratedSkills({ curatedDir, lockedSkillNames, logger });\n }\n\n for (const skillDir of filteredDirs) {\n if (\n shouldSkipSkill({\n skillName: skillDir.name,\n sourceKey,\n localSkillNames,\n alreadyFetchedSkillNames,\n logger,\n })\n ) {\n continue;\n }\n\n fetchedSkills[skillDir.name] = await fetchGithubSkillDir({\n skillDir,\n parsed,\n ref,\n resolvedSha,\n curatedDir,\n locked,\n sourceKey,\n client,\n semaphore,\n logger,\n });\n logger.debug(`Fetched skill \"${skillDir.name}\" from ${sourceKey}`);\n }\n\n const result = buildLockUpdate({\n lock,\n sourceKey,\n fetchedSkills,\n locked,\n requestedRef,\n resolvedSha,\n remoteSkillNames,\n logger,\n });\n\n return {\n skillCount: result.fetchedNames.length,\n fetchedSkillNames: result.fetchedNames,\n updatedLock: result.updatedLock,\n };\n}\n\n/**\n * Fetch skills from a single source using git CLI (works with any git remote).\n */\nasync function fetchSourceViaGit(params: {\n sourceEntry: SourceEntry;\n projectRoot: string;\n lock: SourcesLock;\n localSkillNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n updateSources: boolean;\n frozen: boolean;\n logger: Logger;\n}): Promise<{ skillCount: number; fetchedSkillNames: string[]; updatedLock: SourcesLock }> {\n const {\n sourceEntry,\n projectRoot,\n localSkillNames,\n alreadyFetchedSkillNames,\n updateSources,\n frozen,\n logger,\n } = params;\n const { lock } = params;\n const url = sourceEntry.source;\n const locked = getLockedSource(lock, url);\n const lockedSkillNames = locked ? getLockedSkillNames(locked) : [];\n\n let resolvedSha: string;\n let requestedRef: string | undefined;\n if (locked && !updateSources) {\n resolvedSha = locked.resolvedRef;\n requestedRef = locked.requestedRef;\n // Validate locked ref before passing to git commands\n if (requestedRef) {\n validateRef(requestedRef);\n }\n } else if (sourceEntry.ref) {\n requestedRef = sourceEntry.ref;\n resolvedSha = await resolveRefToSha(url, requestedRef);\n } else {\n const def = await resolveDefaultRef(url);\n requestedRef = def.ref;\n resolvedSha = def.sha;\n }\n\n const curatedDir = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n if (locked && resolvedSha === locked.resolvedRef && !updateSources) {\n if (await checkLockedSkillsExist(curatedDir, lockedSkillNames)) {\n return { skillCount: 0, fetchedSkillNames: lockedSkillNames, updatedLock: lock };\n }\n }\n\n // Resolve requestedRef lazily (deferred from locked path to avoid unnecessary network calls)\n if (!requestedRef) {\n if (frozen) {\n throw new Error(\n `Frozen install failed: lockfile entry for \"${url}\" is missing requestedRef. Run 'rulesync install' to update the lockfile.`,\n );\n }\n const def = await resolveDefaultRef(url);\n requestedRef = def.ref;\n resolvedSha = def.sha;\n }\n\n const skillFilter = sourceEntry.skills ?? [\"*\"];\n const isWildcard = skillFilter.length === 1 && skillFilter[0] === \"*\";\n const remoteFiles = await fetchSkillFiles({\n url,\n ref: requestedRef,\n resolvedRef: resolvedSha,\n skillsPath: sourceEntry.path ?? \"skills\",\n });\n\n const skillFileMap = groupRemoteFilesBySkillRoot({ remoteFiles, skillFilter, isWildcard });\n\n const allNames = [...skillFileMap.keys()];\n const filteredNames = isWildcard ? allNames : allNames.filter((n) => skillFilter.includes(n));\n assertMatchingSkillsFound({ skillNames: filteredNames, source: url });\n\n if (locked) {\n await cleanPreviousCuratedSkills({ curatedDir, lockedSkillNames, logger });\n }\n\n const fetchedSkills: Record<string, LockedSkill> = {};\n for (const skillName of filteredNames) {\n if (\n shouldSkipSkill({\n skillName,\n sourceKey: url,\n localSkillNames,\n alreadyFetchedSkillNames,\n logger,\n })\n ) {\n continue;\n }\n\n fetchedSkills[skillName] = await writeSkillAndComputeIntegrity({\n skillName,\n files: skillFileMap.get(skillName) ?? [],\n curatedDir,\n locked,\n resolvedSha,\n sourceKey: url,\n logger,\n });\n }\n\n const result = buildLockUpdate({\n lock,\n sourceKey: url,\n fetchedSkills,\n locked,\n requestedRef,\n resolvedSha,\n remoteSkillNames: filteredNames,\n logger,\n });\n return {\n skillCount: result.fetchedNames.length,\n fetchedSkillNames: result.fetchedNames,\n updatedLock: result.updatedLock,\n };\n}\n\n// ---------------------------------------------------------------------------\n// npm transport (EXPERIMENTAL)\n// ---------------------------------------------------------------------------\n\n/**\n * Select the skill files inside an extracted npm package, mirroring the git\n * transport's discovery: files under `skills/` (or the configured `path`) are\n * grouped per subdirectory; a package whose `SKILL.md` sits at the package\n * root is installed as a single skill (root fallback via\n * {@link shouldUseRootFallback}), named after the requested skill or, for\n * wildcard fetches, after the package's base name.\n */\nfunction selectNpmSkillFiles(params: {\n allFiles: RemoteSkillFile[];\n skillsPath: string;\n skillFilter: string[];\n isWildcard: boolean;\n packageName: string;\n}): { remoteFiles: RemoteSkillFile[]; skillFilter: string[]; isWildcard: boolean } {\n const { allFiles, skillsPath, skillFilter, isWildcard, packageName } = params;\n\n const normalizedBase = posix.normalize(skillsPath.replace(/\\\\/g, \"/\")).replace(/\\/+$/, \"\");\n const isRootPath = normalizedBase === \"\" || normalizedBase === \".\";\n if (isRootPath) {\n return { remoteFiles: allFiles, skillFilter, isWildcard };\n }\n\n const prefix = `${normalizedBase}/`;\n const filesUnderBase = allFiles\n .filter((file) => file.relativePath.startsWith(prefix))\n .map((file) => ({\n relativePath: file.relativePath.substring(prefix.length),\n content: file.content,\n }));\n if (filesUnderBase.length > 0) {\n return { remoteFiles: filesUnderBase, skillFilter, isWildcard };\n }\n\n // Root fallback: the package itself is a single skill with SKILL.md at its\n // root. For wildcard fetches the skill name is derived from the package\n // base name (scope stripped), so `@acme/my-skill` installs as `my-skill`.\n const hasRootSkillFile = allFiles.some((file) => file.relativePath === SKILL_FILE_NAME);\n const fallbackFilter = isWildcard ? [npmPackageBaseName(packageName)] : skillFilter;\n const [singleSkillName] = fallbackFilter;\n if (\n fallbackFilter.length === 1 &&\n singleSkillName !== undefined &&\n shouldUseRootFallback({\n skillFilter: fallbackFilter,\n isWildcard: false,\n hasRootSkillFile,\n hasRequestedSkillDir: false,\n })\n ) {\n return { remoteFiles: allFiles, skillFilter: fallbackFilter, isWildcard: false };\n }\n\n return { remoteFiles: filesUnderBase, skillFilter, isWildcard };\n}\n\n/** Base name of an npm package: `@scope/name` -> `name`. */\nfunction npmPackageBaseName(packageName: string): string {\n const slashIndex = packageName.indexOf(\"/\");\n return slashIndex === -1 ? packageName : packageName.substring(slashIndex + 1);\n}\n\n/**\n * Resolve the version to fetch for an npm source: the locked version when\n * available (deterministic re-fetch), otherwise the declared `ref` (exact\n * version or dist-tag, defaulting to \"latest\") resolved via the packument.\n */\nfunction resolveNpmFetchVersion(params: {\n sourceEntry: SourceEntry;\n locked: NpmLockedSource | undefined;\n updateSources: boolean;\n}): { lockedVersion: string | undefined; requestedVersion: string | undefined } {\n const { sourceEntry, locked, updateSources } = params;\n if (locked && !updateSources) {\n return { lockedVersion: locked.resolvedVersion, requestedVersion: locked.requestedVersion };\n }\n return { lockedVersion: undefined, requestedVersion: sourceEntry.ref ?? \"latest\" };\n}\n\n/**\n * Resolve the package version via the registry packument, download the\n * tarball, and verify it against the registry (and, when re-fetching a locked\n * version, the locked) integrity metadata.\n */\nasync function downloadVerifiedNpmTarball(params: {\n packageName: string;\n registryUrl: string;\n token: string | undefined;\n lockedVersion: string | undefined;\n requestedVersion: string | undefined;\n locked: NpmLockedSource | undefined;\n logger: Logger;\n}): Promise<{\n resolvedVersion: string;\n dist: { tarball: string; integrity?: string; shasum?: string };\n tarball: Buffer;\n}> {\n const { packageName, registryUrl, token, lockedVersion, requestedVersion, locked, logger } =\n params;\n\n const packument = await fetchPackument({ registryUrl, packageName, token });\n const resolvedVersion =\n lockedVersion ??\n resolvePackumentVersion({\n packument,\n packageName,\n requested: requestedVersion ?? \"latest\",\n });\n logger.debug(`Resolved ${packageName}@${requestedVersion ?? \"latest\"} to ${resolvedVersion}`);\n\n const dist = getPackumentVersionDist({ packument, packageName, version: resolvedVersion });\n const tarball = await fetchTarball({ tarballUrl: dist.tarball, registryUrl, token });\n const context = `${packageName}@${resolvedVersion}`;\n verifyTarballIntegrity({\n tarball,\n integrity: dist.integrity,\n shasum: dist.shasum,\n context,\n logger,\n });\n // Defense in depth: when re-fetching a locked version, also verify against\n // the integrity recorded at lock time so a registry-side swap is detected.\n if (locked?.integrity && locked.resolvedVersion === resolvedVersion) {\n verifyTarballIntegrity({ tarball, integrity: locked.integrity, context, logger });\n }\n\n return { resolvedVersion, dist, tarball };\n}\n\n/**\n * Extract a verified npm tarball in memory and convert its entries into\n * remote skill files, skipping any file above MAX_FILE_SIZE.\n */\nfunction extractNpmRemoteFiles(params: { tarball: Buffer; logger: Logger }): RemoteSkillFile[] {\n const { tarball, logger } = params;\n const extracted = extractPackageTarball({\n tarball,\n onSkippedEntry: (message) => logger.warn(message),\n });\n const allFiles: RemoteSkillFile[] = [];\n for (const entry of extracted) {\n if (entry.content.length > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping file \"${entry.relativePath}\" (${(entry.content.length / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit).`,\n );\n continue;\n }\n allFiles.push({ relativePath: entry.relativePath, content: entry.content.toString(\"utf8\") });\n }\n return allFiles;\n}\n\n/** Build the npm lockfile entry for a fetched source. */\nfunction buildNpmLockEntry(params: {\n sourceEntry: SourceEntry;\n requestedVersion: string | undefined;\n resolvedVersion: string;\n dist: { integrity?: string; shasum?: string };\n mergedSkills: Record<string, LockedSkill>;\n mergedRules: Record<string, LockedRule>;\n resolvedRuleNames: string[];\n}): NpmLockedSource {\n const {\n sourceEntry,\n requestedVersion,\n resolvedVersion,\n dist,\n mergedSkills,\n mergedRules,\n resolvedRuleNames,\n } = params;\n const integrity =\n dist.integrity ?? (dist.shasum !== undefined ? shasumToSri(dist.shasum) : undefined);\n return {\n ...(sourceEntry.registry !== undefined && { registry: sourceEntry.registry }),\n ...(requestedVersion !== undefined && { requestedVersion }),\n resolvedVersion,\n ...(integrity !== undefined && { integrity }),\n resolvedAt: new Date().toISOString(),\n skills: mergedSkills,\n ...(sourceEntry.rules !== undefined && {\n rules: mergedRules,\n ruleSelection: normalizeRuleSelection(sourceEntry.rules),\n rulesPath: normalizeRulesPath(sourceEntry.rulesPath),\n resolvedRuleNames,\n }),\n };\n}\n\nasync function fetchNpmSkills(params: {\n allFiles: RemoteSkillFile[];\n sourceEntry: SourceEntry;\n packageName: string;\n locked: NpmLockedSource | undefined;\n lockedForIntegrityCheck: LockedSource | undefined;\n lockedSkillNames: string[];\n curatedSkillsDir: string;\n localSkillNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n resolvedVersion: string;\n logger: Logger;\n}): Promise<{\n fetchedSkills: Record<string, LockedSkill>;\n remoteSkillNames: string[];\n}> {\n if (params.sourceEntry.skills === undefined) {\n return { fetchedSkills: {}, remoteSkillNames: [] };\n }\n const {\n allFiles,\n sourceEntry,\n packageName,\n locked,\n lockedForIntegrityCheck,\n lockedSkillNames,\n curatedSkillsDir,\n localSkillNames,\n alreadyFetchedSkillNames,\n resolvedVersion,\n logger,\n } = params;\n const skillFilter = sourceEntry.skills ?? [];\n const declaredWildcard = skillFilter.length === 1 && skillFilter[0] === \"*\";\n const selectedFiles = selectNpmSkillFiles({\n allFiles,\n skillsPath: sourceEntry.path ?? \"skills\",\n skillFilter,\n isWildcard: declaredWildcard,\n packageName,\n });\n const skillFileMap = groupRemoteFilesBySkillRoot(selectedFiles);\n const allNames = [...skillFileMap.keys()];\n const remoteSkillNames = selectedFiles.isWildcard\n ? allNames\n : allNames.filter((name) => selectedFiles.skillFilter.includes(name));\n assertMatchingSkillsFound({ skillNames: remoteSkillNames, source: packageName });\n if (locked) {\n await cleanPreviousCuratedSkills({ curatedDir: curatedSkillsDir, lockedSkillNames, logger });\n }\n const fetchedSkills: Record<string, LockedSkill> = {};\n for (const skillName of remoteSkillNames) {\n if (\n shouldSkipSkill({\n skillName,\n sourceKey: packageName,\n localSkillNames,\n alreadyFetchedSkillNames,\n logger,\n })\n ) {\n continue;\n }\n fetchedSkills[skillName] = await writeSkillAndComputeIntegrity({\n skillName,\n files: skillFileMap.get(skillName) ?? [],\n curatedDir: curatedSkillsDir,\n locked: lockedForIntegrityCheck,\n resolvedSha: resolvedVersion,\n sourceKey: packageName,\n logger,\n });\n logger.debug(`Fetched skill \"${skillName}\" from ${packageName}`);\n }\n return { fetchedSkills, remoteSkillNames };\n}\n\nasync function fetchNpmRules(params: {\n allFiles: RemoteSkillFile[];\n sourceEntry: SourceEntry;\n packageName: string;\n lockedForIntegrityCheck: LockedSource | undefined;\n lockedRuleNames: string[];\n curatedRulesDir: string;\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n resolvedVersion: string;\n updateSources: boolean;\n logger: Logger;\n}): Promise<{ fetchedRules: Record<string, LockedRule>; resolvedRuleNames: string[] }> {\n if (params.sourceEntry.rules === undefined) {\n return { fetchedRules: {}, resolvedRuleNames: [] };\n }\n const {\n allFiles,\n sourceEntry,\n packageName,\n lockedForIntegrityCheck,\n lockedRuleNames,\n curatedRulesDir,\n localRuleNames,\n alreadyFetchedRuleNames,\n resolvedVersion,\n updateSources,\n logger,\n } = params;\n const normalizedRulesPath = normalizeRulesPath(sourceEntry.rulesPath);\n const rulePrefix = normalizedRulesPath === \".\" ? \"\" : `${normalizedRulesPath}/`;\n const ruleFilter = (sourceEntry.rules ?? []).map(normalizeRuleFilterName);\n const isWildcard = ruleFilter.length === 1 && ruleFilter[0] === \"*\";\n const remoteRules = allFiles\n .filter((file) => file.relativePath.startsWith(rulePrefix))\n .map((file) => ({\n relativePath: file.relativePath.substring(rulePrefix.length),\n content: file.content,\n }))\n .filter(\n (file) =>\n getFirstPathSeparatorIndex(file.relativePath) === -1 &&\n file.relativePath.toLowerCase().endsWith(\".md\"),\n )\n .map((file) => ({ name: normalizeRuleFilterName(file.relativePath), content: file.content }))\n .filter((rule) => (isWildcard || ruleFilter.includes(rule.name)) && isValidRuleName(rule.name));\n const resolvedRuleNames = remoteRules.map((rule) => rule.name);\n assertMatchingRulesFound({ ruleNames: resolvedRuleNames, source: packageName });\n const fetchedRules = await replaceCuratedRules({\n rules: remoteRules,\n curatedDir: curatedRulesDir,\n locked: lockedForIntegrityCheck,\n lockedRuleNames,\n resolvedRef: resolvedVersion,\n sourceKey: packageName,\n localRuleNames,\n alreadyFetchedRuleNames,\n compareLockedIntegrity: !updateSources,\n logger,\n });\n return { fetchedRules, resolvedRuleNames };\n}\n\nasync function canReuseLockedNpmArtifacts(params: {\n locked: NpmLockedSource | undefined;\n sourceEntry: SourceEntry;\n filters: ReturnType<typeof getSourceFilters>;\n lockedSkillNames: string[];\n lockedRuleNames: string[];\n curatedSkillsDir: string;\n curatedRulesDir: string;\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n}): Promise<boolean> {\n const {\n locked,\n sourceEntry,\n filters,\n lockedSkillNames,\n lockedRuleNames,\n curatedSkillsDir,\n curatedRulesDir,\n localRuleNames,\n alreadyFetchedRuleNames,\n } = params;\n if (locked === undefined) {\n return false;\n }\n const skillsExist =\n filters.skills === undefined ||\n (lockedSkillNames.length > 0 &&\n (await checkLockedSkillsExist(curatedSkillsDir, lockedSkillNames)));\n if (!skillsExist) {\n return false;\n }\n if (filters.rules === undefined) {\n return locked.rules === undefined;\n }\n return canReuseLockedRules({\n locked,\n sourceEntry: { ...sourceEntry, rules: filters.rules },\n lockedRuleNames,\n localRuleNames,\n alreadyFetchedRuleNames,\n curatedDir: curatedRulesDir,\n });\n}\n\n/**\n * Fetch rules and skills from a single npm-transport source (EXPERIMENTAL): resolve the\n * package version via the registry packument, download and verify the\n * tarball, extract it in-memory with the hardened tar reader, and install the\n * discovered skills into the curated directory.\n */\nasync function fetchSourceViaNpm(params: {\n sourceEntry: SourceEntry;\n projectRoot: string;\n npmLock: NpmSourcesLock;\n localSkillNames: Set<string>;\n localRuleNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n updateSources: boolean;\n logger: Logger;\n}): Promise<{\n skillCount: number;\n ruleCount: number;\n fetchedSkillNames: string[];\n fetchedRuleNames: string[];\n updatedLock: NpmSourcesLock;\n}> {\n const {\n sourceEntry,\n projectRoot,\n npmLock,\n localSkillNames,\n localRuleNames,\n alreadyFetchedSkillNames,\n alreadyFetchedRuleNames,\n updateSources,\n logger,\n } = params;\n\n const packageName = sourceEntry.source;\n validateNpmPackageName(packageName);\n const registryUrl = sourceEntry.registry ?? DEFAULT_NPM_REGISTRY_URL;\n validateNpmRegistryUrl(registryUrl, { logger });\n const token = resolveNpmToken({ tokenEnv: sourceEntry.tokenEnv });\n\n const sourceKey = packageName;\n const locked = getNpmLockedSource(npmLock, sourceKey);\n const lockedSkillNames = locked ? getNpmLockedSkillNames(locked) : [];\n const lockedRuleNames = locked ? getNpmLockedRuleNames(locked) : [];\n const curatedSkillsDir = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n const curatedRulesDir = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n const filters = getSourceFilters(sourceEntry);\n\n const { lockedVersion, requestedVersion } = resolveNpmFetchVersion({\n sourceEntry,\n locked,\n updateSources,\n });\n\n // Skip re-fetch if the locked version's requested curated artifacts exist on disk.\n if (\n lockedVersion !== undefined &&\n (await canReuseLockedNpmArtifacts({\n locked,\n sourceEntry,\n filters,\n lockedSkillNames,\n lockedRuleNames,\n curatedSkillsDir,\n curatedRulesDir,\n localRuleNames,\n alreadyFetchedRuleNames,\n }))\n ) {\n logger.debug(`Version unchanged for ${sourceKey}, skipping re-fetch.`);\n return {\n skillCount: 0,\n ruleCount: 0,\n fetchedSkillNames: filters.skills === undefined ? [] : lockedSkillNames,\n fetchedRuleNames: filters.rules === undefined ? [] : lockedRuleNames,\n updatedLock: npmLock,\n };\n }\n\n const { resolvedVersion, dist, tarball } = await downloadVerifiedNpmTarball({\n packageName,\n registryUrl,\n token,\n lockedVersion,\n requestedVersion,\n locked,\n logger,\n });\n\n const allFiles = extractNpmRemoteFiles({ tarball, logger });\n\n // Adapter so writeSkillAndComputeIntegrity can compare per-skill integrity\n // against the npm lock entry the same way it does for git sources.\n const lockedForIntegrityCheck: LockedSource | undefined = locked\n ? { resolvedRef: locked.resolvedVersion, skills: locked.skills, rules: locked.rules }\n : undefined;\n\n const { fetchedSkills, remoteSkillNames } = await fetchNpmSkills({\n allFiles,\n sourceEntry: { ...sourceEntry, skills: filters.skills },\n packageName,\n locked,\n lockedForIntegrityCheck,\n lockedSkillNames,\n curatedSkillsDir,\n localSkillNames,\n alreadyFetchedSkillNames,\n resolvedVersion,\n logger,\n });\n\n const { fetchedRules, resolvedRuleNames } = await fetchNpmRules({\n allFiles,\n sourceEntry: { ...sourceEntry, rules: filters.rules },\n packageName,\n lockedForIntegrityCheck,\n lockedRuleNames,\n curatedRulesDir,\n localRuleNames,\n alreadyFetchedRuleNames,\n resolvedVersion,\n updateSources,\n logger,\n });\n\n if (filters.rules === undefined && locked?.rules !== undefined) {\n await cleanPreviousCuratedRules({\n curatedDir: curatedRulesDir,\n lockedRuleNames,\n protectedRuleNames: alreadyFetchedRuleNames,\n logger,\n });\n }\n\n const fetchedSkillNames = Object.keys(fetchedSkills);\n const fetchedRuleNames = Object.keys(fetchedRules);\n const mergedSkills = mergeFetchedWithLockedSkills({\n fetchedSkills,\n lockedSkills: locked?.skills,\n remoteSkillNames:\n filters.skills === undefined ? Object.keys(locked?.skills ?? {}) : remoteSkillNames,\n });\n const mergedRules = filters.rules === undefined ? {} : fetchedRules;\n\n const updatedLock = setNpmLockedSource(\n npmLock,\n sourceKey,\n buildNpmLockEntry({\n sourceEntry,\n requestedVersion,\n resolvedVersion,\n dist,\n mergedSkills,\n mergedRules,\n resolvedRuleNames,\n }),\n );\n\n logger.info(\n `Fetched ${fetchedSkillNames.length} skill(s) and ${fetchedRuleNames.length} rule(s) from ${sourceKey}.`,\n );\n\n return {\n skillCount: fetchedSkillNames.length,\n ruleCount: fetchedRuleNames.length,\n fetchedSkillNames,\n fetchedRuleNames,\n updatedLock,\n };\n}\n","import { cp, mkdtemp, realpath, rm } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, relative, sep } from \"node:path\";\nimport { createInterface } from \"node:readline/promises\";\n\nimport {\n applyEdits,\n modify,\n parse as parseJsonc,\n type FormattingOptions,\n type ParseError,\n printParseErrorCode,\n} from \"jsonc-parser\";\n\nimport { ConfigResolver } from \"../../config/config-resolver.js\";\nimport { ConfigFileSchema, type SourceEntry, SourceEntrySchema } from \"../../config/config.js\";\nimport {\n RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH,\n RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH,\n RULESYNC_CONFIG_RELATIVE_FILE_PATH,\n RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH,\n RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH,\n RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH,\n} from \"../../constants/rulesync-paths.js\";\nimport { createFeatureScaffold, parseScaffoldFeatureKeyword } from \"../../lib/feature-scaffold.js\";\nimport { normalizeNpmSourceKey } from \"../../lib/npm-sources-lock.js\";\nimport { normalizeSourceKey } from \"../../lib/sources-lock.js\";\nimport {\n getInstalledSourceRuleNames,\n getInstalledSourceSkillNames,\n resolveAndFetchSources,\n} from \"../../lib/sources.js\";\nimport {\n assertDirectoryIfExists,\n assertTreeContainsNoSymlinks,\n assertWritablePathInsideRoot,\n directoryExists,\n ensureDir,\n fileExists,\n readFileContent,\n readFileContentOrNull,\n resolvePath,\n writeFileContent,\n} from \"../../utils/file.js\";\nimport type { Logger } from \"../../utils/logger.js\";\n\nexport type AddCommandOptions = {\n source: string;\n skills?: string[];\n rules?: string[];\n transport?: SourceEntry[\"transport\"];\n ref?: string;\n path?: string;\n rulesPath?: string;\n registry?: string;\n tokenEnv?: string;\n token?: string;\n configPath?: string;\n name?: string;\n force?: boolean;\n verbose?: boolean;\n silent?: boolean;\n confirmOverwrite?: (relativeFilePath: string) => Promise<boolean>;\n};\n\nconst SOURCE_ENTRY_KEYS = [\n \"source\",\n \"skills\",\n \"rules\",\n \"transport\",\n \"ref\",\n \"path\",\n \"rulesPath\",\n \"registry\",\n \"tokenEnv\",\n \"agent\",\n \"scope\",\n] as const satisfies ReadonlyArray<keyof SourceEntry>;\n\ntype InstallSnapshot = {\n backupRoot: string;\n curatedSkillsExisted: boolean;\n curatedRulesExisted: boolean;\n sourcesLockContent: string | null;\n npmSourcesLockContent: string | null;\n};\n\nfunction pathEscapesRoot(relativePath: string): boolean {\n return relativePath === \"..\" || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath);\n}\n\nfunction assertSourceHasNoEmbeddedCredentials(source: string): void {\n if (!/^[a-z][a-z\\d+.-]*:\\/\\//i.test(source)) {\n return;\n }\n const url = new URL(source);\n if (url.username !== \"\" || url.password !== \"\") {\n throw new Error(\n \"Source URLs must not contain credentials. Use an environment variable, credential helper, or SSH authentication instead.\",\n );\n }\n}\n\nasync function createInstallSnapshot({\n projectRoot,\n manifestContent,\n}: {\n projectRoot: string;\n manifestContent: string;\n}): Promise<InstallSnapshot> {\n const backupRoot = await mkdtemp(join(projectRoot, \".rulesync-add-backup-\"));\n const curatedSkillsPath = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n const curatedRulesPath = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n const curatedSkillsExisted = await directoryExists(curatedSkillsPath);\n const curatedRulesExisted = await directoryExists(curatedRulesPath);\n if (curatedSkillsExisted) {\n await cp(curatedSkillsPath, join(backupRoot, \"curated-skills\"), { recursive: true });\n }\n if (curatedRulesExisted) {\n await cp(curatedRulesPath, join(backupRoot, \"curated-rules\"), { recursive: true });\n }\n const sourcesLockContent = await readFileContentOrNull(\n join(projectRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH),\n );\n const npmSourcesLockContent = await readFileContentOrNull(\n join(projectRoot, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH),\n );\n await writeFileContent(join(backupRoot, \"manifest.jsonc\"), manifestContent);\n if (sourcesLockContent !== null) {\n await writeFileContent(\n join(backupRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH),\n sourcesLockContent,\n );\n }\n if (npmSourcesLockContent !== null) {\n await writeFileContent(\n join(backupRoot, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH),\n npmSourcesLockContent,\n );\n }\n return {\n backupRoot,\n curatedSkillsExisted,\n curatedRulesExisted,\n sourcesLockContent,\n npmSourcesLockContent,\n };\n}\n\nasync function restoreFile({ path, content }: { path: string; content: string | null }) {\n if (content === null) {\n await rm(path, { force: true });\n return;\n }\n await writeFileContent(path, content);\n}\n\nasync function restoreInstallSnapshot({\n projectRoot,\n snapshot,\n}: {\n projectRoot: string;\n snapshot: InstallSnapshot;\n}): Promise<void> {\n const curatedSkillsPath = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n const curatedRulesPath = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n await Promise.all([\n rm(curatedSkillsPath, { recursive: true, force: true }),\n rm(curatedRulesPath, { recursive: true, force: true }),\n ]);\n if (snapshot.curatedSkillsExisted) {\n await cp(join(snapshot.backupRoot, \"curated-skills\"), curatedSkillsPath, { recursive: true });\n }\n if (snapshot.curatedRulesExisted) {\n await cp(join(snapshot.backupRoot, \"curated-rules\"), curatedRulesPath, { recursive: true });\n }\n await restoreFile({\n path: join(projectRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH),\n content: snapshot.sourcesLockContent,\n });\n await restoreFile({\n path: join(projectRoot, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH),\n content: snapshot.npmSourcesLockContent,\n });\n}\n\nasync function rollbackAdd({\n configPath,\n originalContent,\n projectRoot,\n snapshot,\n}: {\n configPath: string;\n originalContent: string;\n projectRoot: string;\n snapshot: InstallSnapshot;\n}): Promise<void> {\n await Promise.all([\n writeFileContent(configPath, originalContent),\n restoreInstallSnapshot({ projectRoot, snapshot }),\n ]);\n}\n\nfunction sourceIdentity(entry: SourceEntry): string {\n const transport = entry.transport ?? \"github\";\n const normalizedSource =\n transport === \"npm\" ? normalizeNpmSourceKey(entry.source) : normalizeSourceKey(entry.source);\n const lockfileKind = transport === \"npm\" ? \"npm\" : \"git\";\n return `${lockfileKind}:${normalizedSource}`;\n}\n\nfunction sourceEntriesEqual(left: SourceEntry, right: SourceEntry): boolean {\n return SOURCE_ENTRY_KEYS.every((key) => {\n const leftValue = left[key];\n const rightValue = right[key];\n if (Array.isArray(leftValue) && Array.isArray(rightValue)) {\n return (\n leftValue.length === rightValue.length &&\n leftValue.every((value, index) => value === rightValue[index])\n );\n }\n return leftValue === rightValue;\n });\n}\n\nfunction detectFormattingOptions(content: string): FormattingOptions {\n const eol = content.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const indentedLine = content.match(/^(\\s+)[\"}]/m)?.[1] ?? \" \";\n const insertSpaces = !indentedLine.includes(\"\\t\");\n return {\n eol,\n insertSpaces,\n tabSize: insertSpaces ? indentedLine.length : 1,\n };\n}\n\nfunction buildSourceEntry(options: AddCommandOptions): SourceEntry {\n assertSourceHasNoEmbeddedCredentials(options.source);\n return SourceEntrySchema.parse({\n source: options.source,\n skills: options.skills,\n rules: options.rules,\n transport: options.transport,\n ref: options.ref,\n path: options.path,\n rulesPath: options.rulesPath,\n registry: options.registry,\n tokenEnv: options.tokenEnv,\n });\n}\n\nfunction hasSourceOnlyOptions(options: AddCommandOptions): boolean {\n return [\n options.skills,\n options.rules,\n options.transport,\n options.ref,\n options.path,\n options.rulesPath,\n options.registry,\n options.tokenEnv,\n options.token,\n options.configPath,\n ].some((value) => value !== undefined);\n}\n\nasync function promptForOverwrite(relativeFilePath: string): Promise<boolean> {\n if (!process.stdin.isTTY || !process.stdout.isTTY) {\n throw new Error(\n `Refusing to overwrite ${relativeFilePath} in non-interactive mode. Re-run with --force to replace it.`,\n );\n }\n\n const prompt = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const answer = await prompt.question(`Overwrite ${relativeFilePath}? [y/N] `);\n return /^(?:y|yes)$/i.test(answer.trim());\n } finally {\n prompt.close();\n }\n}\n\nasync function addFeatureScaffold({\n logger,\n options,\n}: {\n logger: Logger;\n options: AddCommandOptions;\n}): Promise<void> {\n const feature = parseScaffoldFeatureKeyword(options.source);\n if (!feature) {\n throw new Error(\"--name and --force are only valid when adding a Rulesync feature file.\");\n }\n\n const scaffold = createFeatureScaffold({ feature, name: options.name });\n const projectRoot = process.cwd();\n let relativeFilePath = scaffold.relativeFilePath;\n for (const candidateRelativeFilePath of scaffold.candidateRelativeFilePaths) {\n if (await fileExists(join(projectRoot, candidateRelativeFilePath))) {\n relativeFilePath = candidateRelativeFilePath;\n break;\n }\n }\n const targetPath = join(projectRoot, relativeFilePath);\n await assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath });\n\n if ((await fileExists(targetPath)) && !options.force) {\n if (logger.jsonMode || logger.silent) {\n throw new Error(\n `Refusing to prompt before overwriting ${relativeFilePath} in JSON or silent mode. Re-run with --force to replace it.`,\n );\n }\n const confirmed = await (options.confirmOverwrite ?? promptForOverwrite)(relativeFilePath);\n if (!confirmed) {\n logger.info(`Kept ${relativeFilePath} unchanged.`);\n if (logger.jsonMode) {\n logger.captureData(\"created\", []);\n logger.captureData(\"skipped\", [relativeFilePath]);\n }\n return;\n }\n }\n\n await assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath });\n await ensureDir(dirname(targetPath));\n await assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath });\n await writeFileContent(targetPath, scaffold.content);\n logger.success(`Created ${relativeFilePath}`);\n if (logger.jsonMode) {\n logger.captureData(\"created\", [relativeFilePath]);\n logger.captureData(\"skipped\", []);\n }\n}\n\nasync function handleFeatureScaffoldRequest({\n logger,\n options,\n}: {\n logger: Logger;\n options: AddCommandOptions;\n}): Promise<boolean> {\n const feature = parseScaffoldFeatureKeyword(options.source);\n const sourceOnlyOptions = hasSourceOnlyOptions(options);\n const scaffoldOnlyOptions = options.name !== undefined || options.force === true;\n\n if (feature && scaffoldOnlyOptions && sourceOnlyOptions) {\n throw new Error(\n \"Feature scaffold options (--name, --force) cannot be combined with declarative source options.\",\n );\n }\n if ((feature && !sourceOnlyOptions) || scaffoldOnlyOptions) {\n await addFeatureScaffold({ logger, options });\n return true;\n }\n return false;\n}\n\nfunction parseConfigContent(content: string, configPath: string) {\n const errors: ParseError[] = [];\n const parsed = parseJsonc(content, errors, { allowTrailingComma: true });\n const firstError = errors[0];\n if (firstError) {\n throw new Error(\n `Failed to parse ${configPath}: ${printParseErrorCode(firstError.error)} at offset ${firstError.offset}.`,\n );\n }\n return ConfigFileSchema.parse(parsed);\n}\n\nexport async function addCommand(logger: Logger, options: AddCommandOptions): Promise<void> {\n if (await handleFeatureScaffoldRequest({ logger, options })) {\n return;\n }\n\n const projectRoot = process.cwd();\n const relativeConfigPath = options.configPath ?? RULESYNC_CONFIG_RELATIVE_FILE_PATH;\n const configPath = resolvePath(relativeConfigPath, projectRoot);\n\n if (!(await fileExists(configPath))) {\n throw new Error(\n `Configuration file not found: ${relativeConfigPath}. Run 'rulesync init' first or pass --config.`,\n );\n }\n\n const realProjectRoot = await realpath(projectRoot);\n const realConfigPath = await realpath(configPath);\n const relativeRealConfigPath = relative(realProjectRoot, realConfigPath);\n if (pathEscapesRoot(relativeRealConfigPath)) {\n throw new Error(\n `Configuration file must resolve inside the project root: ${relativeConfigPath}.`,\n );\n }\n\n const sourceEntry = buildSourceEntry(options);\n const curatedSkillsPath = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n const curatedRulesPath = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n await Promise.all([\n assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath: curatedSkillsPath }),\n assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath: curatedRulesPath }),\n assertWritablePathInsideRoot({\n rootPath: projectRoot,\n targetPath: join(projectRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH),\n }),\n assertWritablePathInsideRoot({\n rootPath: projectRoot,\n targetPath: join(projectRoot, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH),\n }),\n ]);\n await Promise.all([\n assertDirectoryIfExists(curatedSkillsPath),\n assertDirectoryIfExists(curatedRulesPath),\n ]);\n if (await directoryExists(curatedSkillsPath)) {\n await assertTreeContainsNoSymlinks(curatedSkillsPath);\n }\n if (await directoryExists(curatedRulesPath)) {\n await assertTreeContainsNoSymlinks(curatedRulesPath);\n }\n const originalContent = await readFileContent(configPath);\n const parsedConfig = parseConfigContent(originalContent, relativeConfigPath);\n const existingSources = parsedConfig.sources ?? [];\n const identity = sourceIdentity(sourceEntry);\n\n if (existingSources.some((entry) => sourceIdentity(entry) === identity)) {\n throw new Error(\n `Source \"${sourceEntry.source}\" is already declared in ${relativeConfigPath}. Edit the existing entry to change its options.`,\n );\n }\n\n const configBeforeEdit = await ConfigResolver.resolve(\n {\n configPath,\n verbose: options.verbose,\n silent: options.silent,\n },\n { logger },\n );\n if (configBeforeEdit.getSources().some((entry) => sourceIdentity(entry) === identity)) {\n throw new Error(\n `Source \"${sourceEntry.source}\" is already declared in the effective configuration. Edit the existing entry to change its options.`,\n );\n }\n const reservedSkillNames = await getInstalledSourceSkillNames({\n sources: configBeforeEdit.getSources(),\n projectRoot,\n logger,\n });\n const reservedRuleNames = await getInstalledSourceRuleNames({\n sources: configBeforeEdit.getSources(),\n projectRoot,\n logger,\n });\n\n const editPath =\n parsedConfig.sources === undefined ? [\"sources\"] : [\"sources\", existingSources.length];\n const editValue = parsedConfig.sources === undefined ? [sourceEntry] : sourceEntry;\n const formattingOptions = detectFormattingOptions(originalContent);\n const edits = modify(originalContent, editPath, editValue, { formattingOptions });\n let updatedContent = applyEdits(originalContent, edits);\n if (!updatedContent.endsWith(\"\\n\")) {\n updatedContent += formattingOptions.eol;\n }\n\n // Validate the complete edited document before replacing the user's file.\n parseConfigContent(updatedContent, relativeConfigPath);\n const snapshot = await createInstallSnapshot({ projectRoot, manifestContent: originalContent });\n let cleanupSnapshot = true;\n try {\n await writeFileContent(configPath, updatedContent);\n const config = await ConfigResolver.resolve(\n {\n configPath,\n verbose: options.verbose,\n silent: options.silent,\n },\n { logger },\n );\n const sources = config.getSources();\n if (!sources.some((entry) => sourceEntriesEqual(entry, sourceEntry))) {\n throw new Error(\n `${join(dirname(configPath), RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH)} overrides sources from ${relativeConfigPath}. Add the source to the overriding config or remove its sources key.`,\n );\n }\n\n const result = await resolveAndFetchSources({\n sources: [sourceEntry],\n projectRoot,\n options: {\n token: options.token,\n updateSources: true,\n preserveUnlistedLockEntries: true,\n requireResolvedSkills: sourceEntry.skills !== undefined || sourceEntry.rules === undefined,\n requireResolvedRules: sourceEntry.rules !== undefined,\n reservedSkillNames,\n reservedRuleNames,\n },\n logger,\n });\n\n if (logger.jsonMode) {\n logger.captureData(\"source\", sourceEntry.source);\n logger.captureData(\"configPath\", relativeConfigPath);\n logger.captureData(\"sourcesProcessed\", result.sourcesProcessed);\n logger.captureData(\"skillsFetched\", result.fetchedSkillCount);\n logger.captureData(\"rulesFetched\", result.fetchedRuleCount);\n logger.captureData(\"failedSourceCount\", result.failedSourceCount);\n }\n\n if (result.failedSourceCount > 0) {\n throw new Error(\n `Failed to install ${result.failedSourceCount} of ${result.sourcesProcessed} source(s); restored ${relativeConfigPath}. See the log above for details.`,\n );\n }\n\n logger.success(\n `Added \"${sourceEntry.source}\" to ${relativeConfigPath} and installed ${result.fetchedSkillCount} skill(s) and ${result.fetchedRuleCount} rule(s).`,\n );\n } catch (error) {\n try {\n await rollbackAdd({ configPath, originalContent, projectRoot, snapshot });\n } catch (rollbackError) {\n cleanupSnapshot = false;\n // oxlint-disable-next-line preserve-caught-error -- AggregateError retains both the operation and rollback failures.\n throw new AggregateError(\n [error, rollbackError],\n `Failed to roll back the add operation. Recovery snapshot retained at ${snapshot.backupRoot}.`,\n { cause: error },\n );\n }\n throw error;\n } finally {\n if (cleanupSnapshot) {\n await rm(snapshot.backupRoot, { recursive: true, force: true });\n }\n }\n}\n","/**\n * Result of writing AI files, including both count and file paths\n */\nexport type WriteResult = {\n count: number;\n paths: string[];\n};\n\n/**\n * Result of feature generation, extending WriteResult with hasDiff\n */\nexport type FeatureGenerateResult = WriteResult & { hasDiff: boolean };\n\n/**\n * Common count fields shared by ImportResult and GenerateResult\n */\nexport type CountableResult = {\n rulesCount: number;\n ignoreCount: number;\n mcpCount: number;\n commandsCount: number;\n subagentsCount: number;\n skillsCount: number;\n hooksCount: number;\n permissionsCount: number;\n checksCount: number;\n activationCount?: number;\n};\n\n/**\n * Calculate the total count from a result object\n */\nexport function calculateTotalCount(result: CountableResult): number {\n return (\n result.rulesCount +\n result.ignoreCount +\n result.mcpCount +\n result.commandsCount +\n result.subagentsCount +\n result.skillsCount +\n result.hooksCount +\n result.permissionsCount +\n result.checksCount +\n (result.activationCount ?? 0)\n );\n}\n","import { ConfigResolver, ConfigResolverResolveParams } from \"../../config/config-resolver.js\";\nimport { convertFromTool } from \"../../lib/convert.js\";\nimport type { RulesyncFeatures } from \"../../types/features.js\";\nimport { CLIError, ErrorCodes } from \"../../types/json-output.js\";\nimport { ALL_TOOL_TARGETS, type ToolTarget, ToolTargetSchema } from \"../../types/tool-targets.js\";\nimport type { Logger } from \"../../utils/logger.js\";\nimport { isPackagingToolTarget } from \"../../utils/plugin-root.js\";\nimport { calculateTotalCount } from \"../../utils/result.js\";\n\nexport type ConvertOptions = Omit<\n ConfigResolverResolveParams,\n \"delete\" | \"outputRoots\" | \"targets\"\n> & {\n from?: string;\n to?: string[];\n features?: RulesyncFeatures;\n};\n\nfunction parseToolTarget(value: string, label: string): ToolTarget {\n const result = ToolTargetSchema.safeParse(value);\n if (!result.success) {\n throw new CLIError(\n `Invalid ${label} tool '${value}'. Must be one of: ${ALL_TOOL_TARGETS.join(\", \")}`,\n ErrorCodes.CONVERT_FAILED,\n );\n }\n return result.data;\n}\n\nexport async function convertCommand(logger: Logger, options: ConvertOptions): Promise<void> {\n // `--from` and `--to` presence is enforced by commander's `requiredOption`\n // in `src/cli/index.ts`; here we only need to validate the tool names.\n const fromTool = parseToolTarget(options.from ?? \"\", \"source\");\n const toToolsRaw = (options.to ?? []).map((t) => parseToolTarget(t, \"destination\"));\n const toTools = Array.from(new Set(toToolsRaw));\n\n const packagingTarget = [fromTool, ...toTools].find(isPackagingToolTarget);\n if (packagingTarget) {\n throw new CLIError(\n `Plugin packaging target '${packagingTarget}' is not supported by convert. ` +\n \"Use import --output-root and generate --output-roots with an explicit plugin directory.\",\n ErrorCodes.CONVERT_FAILED,\n );\n }\n\n if (toTools.includes(fromTool)) {\n throw new CLIError(\n `Destination tools must not include the source tool '${fromTool}'. ` +\n `Converting a tool onto itself is likely a mistake and may cause lossy round-trips.`,\n ErrorCodes.CONVERT_FAILED,\n );\n }\n\n // Pass both source and destinations as `targets` so per-target feature maps\n // in `rulesync.jsonc` are honored for every tool involved. Default features\n // to `*` so every feature that both tools support is attempted.\n const config = await ConfigResolver.resolve(\n {\n ...options,\n targets: [fromTool, ...toTools],\n features: options.features ?? [\"*\"],\n },\n { logger },\n );\n\n const isPreview = config.isPreviewMode();\n const modePrefix = isPreview ? \"[DRY RUN] \" : \"\";\n\n logger.debug(`Converting files from ${fromTool} to ${toTools.join(\", \")}...`);\n\n const result = await convertFromTool({ config, fromTool, toTools, logger });\n\n const totalConverted = calculateTotalCount(result);\n\n if (totalConverted === 0) {\n const enabledFeatures = config.getFeatures(fromTool).join(\", \");\n logger.warn(`No files converted for enabled features: ${enabledFeatures}`);\n return;\n }\n\n if (logger.jsonMode) {\n logger.captureData(\"from\", fromTool);\n logger.captureData(\"to\", toTools);\n logger.captureData(\"dryRun\", isPreview);\n logger.captureData(\"features\", {\n rules: { count: result.rulesCount },\n ignore: { count: result.ignoreCount },\n mcp: { count: result.mcpCount },\n commands: { count: result.commandsCount },\n subagents: { count: result.subagentsCount },\n skills: { count: result.skillsCount },\n hooks: { count: result.hooksCount },\n permissions: { count: result.permissionsCount },\n checks: { count: result.checksCount },\n });\n logger.captureData(\"totalFiles\", totalConverted);\n }\n\n const parts: string[] = [];\n if (result.rulesCount > 0) parts.push(`${result.rulesCount} rules`);\n if (result.ignoreCount > 0) parts.push(`${result.ignoreCount} ignore files`);\n if (result.mcpCount > 0) parts.push(`${result.mcpCount} MCP files`);\n if (result.commandsCount > 0) parts.push(`${result.commandsCount} commands`);\n if (result.subagentsCount > 0) parts.push(`${result.subagentsCount} subagents`);\n if (result.skillsCount > 0) parts.push(`${result.skillsCount} skills`);\n if (result.hooksCount > 0) parts.push(`${result.hooksCount} hooks`);\n if (result.permissionsCount > 0) parts.push(`${result.permissionsCount} permissions`);\n if (result.checksCount > 0) parts.push(`${result.checksCount} checks`);\n\n const verbPhrase = isPreview ? \"Would convert\" : \"Converted\";\n const summary = `${modePrefix}${verbPhrase} ${totalConverted} file(s) total from ${fromTool} to ${toTools.join(\", \")} (${parts.join(\" + \")})`;\n\n if (isPreview) {\n logger.info(summary);\n } else {\n logger.success(summary);\n }\n}\n","import { join, posix } from \"node:path\";\n\nimport { Semaphore } from \"es-toolkit/promise\";\n\nimport {\n FETCH_CONCURRENCY_LIMIT,\n MAX_FILE_SIZE,\n RULESYNC_AIIGNORE_FILE_NAME,\n RULESYNC_HOOKS_FILE_NAME,\n RULESYNC_HOOKS_LEGACY_FILE_NAME,\n RULESYNC_MCP_FILE_NAME,\n RULESYNC_MCP_LEGACY_FILE_NAME,\n RULESYNC_PERMISSIONS_FILE_NAME,\n RULESYNC_PERMISSIONS_LEGACY_FILE_NAME,\n RULESYNC_RELATIVE_DIR_PATH,\n} from \"../constants/rulesync-paths.js\";\nimport { ChecksProcessor } from \"../features/checks/checks-processor.js\";\nimport { CommandsProcessor } from \"../features/commands/commands-processor.js\";\nimport { HooksProcessor } from \"../features/hooks/hooks-processor.js\";\nimport { IgnoreProcessor } from \"../features/ignore/ignore-processor.js\";\nimport { McpProcessor } from \"../features/mcp/mcp-processor.js\";\nimport { RulesProcessor } from \"../features/rules/rules-processor.js\";\nimport { SkillsProcessor } from \"../features/skills/skills-processor.js\";\nimport { SubagentsProcessor } from \"../features/subagents/subagents-processor.js\";\nimport type { Feature } from \"../types/features.js\";\nimport { ALL_FEATURES } from \"../types/features.js\";\nimport type { FetchTarget } from \"../types/fetch-targets.js\";\nimport type {\n ConflictStrategy,\n FetchFileResult,\n FetchOptions,\n FetchSummary,\n GitHubFileEntry,\n ParsedSource,\n} from \"../types/fetch.js\";\nimport type { ToolTarget } from \"../types/tool-targets.js\";\nimport {\n checkPathTraversal,\n createTempDirectory,\n fileExists,\n removeTempDirectory,\n toPosixPath,\n writeFileContent,\n} from \"../utils/file.js\";\nimport type { Logger } from \"../utils/logger.js\";\nimport { GitHubClient, GitHubClientError } from \"./github-client.js\";\nimport { listDirectoryRecursive, withSemaphore } from \"./github-utils.js\";\nimport { parseSource } from \"./source-parser.js\";\n\n/**\n * Feature to path mapping for filtering (rulesync format)\n */\nconst FEATURE_PATHS: Record<Feature, string[]> = {\n rules: [\"rules\"],\n commands: [\"commands\"],\n subagents: [\"subagents\"],\n skills: [\"skills\"],\n checks: [\"checks\"],\n ignore: [RULESYNC_AIIGNORE_FILE_NAME],\n mcp: [RULESYNC_MCP_FILE_NAME, RULESYNC_MCP_LEGACY_FILE_NAME],\n hooks: [RULESYNC_HOOKS_FILE_NAME, RULESYNC_HOOKS_LEGACY_FILE_NAME],\n permissions: [RULESYNC_PERMISSIONS_FILE_NAME, RULESYNC_PERMISSIONS_LEGACY_FILE_NAME],\n};\n\n/**\n * Check if target is a tool target (not rulesync)\n */\nfunction isToolTarget(target: FetchTarget): target is ToolTarget {\n return target !== \"rulesync\";\n}\n\n/**\n * Validate file size against maximum limit\n * @throws {GitHubClientError} If file size exceeds limit\n */\nfunction validateFileSize(relativePath: string, size: number): void {\n if (size > MAX_FILE_SIZE) {\n throw new GitHubClientError(\n `File \"${relativePath}\" exceeds maximum size limit (${(size / 1024 / 1024).toFixed(2)}MB > ${MAX_FILE_SIZE / 1024 / 1024}MB)`,\n );\n }\n}\n\n/**\n * Result of feature conversion\n */\ntype FeatureConversionResult = {\n converted: number;\n convertedPaths: string[];\n};\n\n/**\n * Processor type for feature conversion\n */\ntype FeatureProcessor = {\n loadToolFiles(): Promise<unknown[]>;\n convertToolFilesToRulesyncFiles(\n toolFiles: unknown[],\n ): Promise<\n Array<{ getRelativeDirPath(): string; getRelativeFilePath(): string; getFileContent(): string }>\n >;\n};\n\n/**\n * Process feature conversion for a single feature type\n * @param processor - The processor to use for loading and converting files\n * @param outputDir - Output directory for converted files\n * @returns The paths of converted files\n */\nasync function processFeatureConversion(params: {\n processor: FeatureProcessor;\n outputDir: string;\n}): Promise<{ paths: string[] }> {\n const { processor, outputDir } = params;\n const paths: string[] = [];\n\n const toolFiles = await processor.loadToolFiles();\n if (toolFiles.length === 0) {\n return { paths: [] };\n }\n\n const rulesyncFiles = await processor.convertToolFilesToRulesyncFiles(toolFiles);\n for (const file of rulesyncFiles) {\n const relativePath = join(file.getRelativeDirPath(), file.getRelativeFilePath());\n const outputPath = join(outputDir, relativePath);\n await writeFileContent(outputPath, file.getFileContent());\n paths.push(relativePath);\n }\n\n return { paths };\n}\n\n/**\n * Convert fetched tool-specific files to rulesync format\n * @param tempDir - Temporary directory containing tool-specific files\n * @param outputDir - Output directory for rulesync files\n * @param target - Tool target to convert from\n * @param features - Features to convert\n * @returns Number of converted files and their paths\n */\nasync function convertFetchedFilesToRulesync(params: {\n tempDir: string;\n outputDir: string;\n target: ToolTarget;\n features: Feature[];\n logger: Logger;\n}): Promise<FeatureConversionResult> {\n const { tempDir, outputDir, target, features, logger } = params;\n const convertedPaths: string[] = [];\n\n // Feature conversion configurations\n // Each config defines how to get supported targets and create a processor\n const featureConfigs: Array<{\n feature: Feature;\n getTargets: () => ToolTarget[];\n createProcessor: () => FeatureProcessor;\n }> = [\n {\n feature: \"rules\",\n getTargets: () => RulesProcessor.getToolTargets({ global: false }),\n createProcessor: () =>\n new RulesProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger }),\n },\n {\n feature: \"commands\",\n getTargets: () =>\n CommandsProcessor.getToolTargets({ global: false, includeSimulated: false }),\n createProcessor: () =>\n new CommandsProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger }),\n },\n {\n feature: \"subagents\",\n getTargets: () =>\n SubagentsProcessor.getToolTargets({ global: false, includeSimulated: false }),\n createProcessor: () =>\n new SubagentsProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger }),\n },\n {\n feature: \"checks\",\n getTargets: () => ChecksProcessor.getToolTargets({ global: false }),\n createProcessor: () =>\n new ChecksProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger }),\n },\n {\n feature: \"ignore\",\n getTargets: () => IgnoreProcessor.getToolTargets(),\n createProcessor: () =>\n new IgnoreProcessor({ outputRoot: tempDir, toolTarget: target, logger }),\n },\n {\n feature: \"mcp\",\n getTargets: () => McpProcessor.getToolTargets({ global: false }),\n createProcessor: () =>\n new McpProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger }),\n },\n {\n feature: \"hooks\",\n getTargets: () => HooksProcessor.getToolTargets({ global: false }),\n createProcessor: () =>\n new HooksProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger }),\n },\n ];\n\n // Process each feature using data-driven approach\n for (const config of featureConfigs) {\n if (!features.includes(config.feature)) {\n continue;\n }\n const supportedTargets = config.getTargets();\n if (!supportedTargets.includes(target)) {\n continue;\n }\n const processor = config.createProcessor();\n const result = await processFeatureConversion({ processor, outputDir });\n convertedPaths.push(...result.paths);\n }\n\n // Skills conversion is not yet supported in fetch command\n // Note: Skills are more complex as they are directory-based.\n // Users can use the import command for skills conversion.\n if (features.includes(\"skills\")) {\n logger.debug(\n \"Skills conversion is not yet supported in fetch command. Use import command instead.\",\n );\n }\n\n return { converted: convertedPaths.length, convertedPaths };\n}\n\n/**\n * Resolve features from options, defaulting to skills and handling wildcard.\n */\nfunction resolveFeatures(features?: string[]): Feature[] {\n if (features === undefined) {\n return [\"skills\"];\n }\n if (features.includes(\"*\")) {\n return [...ALL_FEATURES];\n }\n return features.filter((f): f is Feature => ALL_FEATURES.includes(f as Feature));\n}\n\n/**\n * Type guard for error objects with statusCode\n */\nfunction hasStatusCode(error: unknown): error is { statusCode: number } {\n if (typeof error !== \"object\" || error === null || !(\"statusCode\" in error)) {\n return false;\n }\n const maybeStatus = Object.getOwnPropertyDescriptor(error, \"statusCode\")?.value;\n return typeof maybeStatus === \"number\";\n}\n\n/**\n * Check if error is a 404 \"not found\" error\n */\nfunction isNotFoundError(error: unknown): boolean {\n if (error instanceof GitHubClientError && error.statusCode === 404) {\n return true;\n }\n // Also handle plain objects with statusCode property (for test mocks)\n if (hasStatusCode(error) && error.statusCode === 404) {\n return true;\n }\n return false;\n}\n\n/**\n * Parameters for fetch operation\n */\nexport type FetchParams = {\n source: string;\n options?: FetchOptions;\n outputRoot?: string;\n logger: Logger;\n};\n\n/**\n * Fetch files from a Git repository\n * Searches for feature directories (rules/, commands/, skills/, etc.) directly at the specified path\n *\n * When target is \"rulesync\" (default), files are fetched as-is.\n * When target is a tool target (e.g., \"claudecode\"), files are fetched to a temp directory,\n * converted to rulesync format, and written to the output directory.\n */\nexport async function fetchFiles(params: FetchParams): Promise<FetchSummary> {\n const { source, options = {}, outputRoot = process.cwd(), logger } = params;\n\n // Parse source\n const parsed = parseSource(source);\n\n // Check if provider is supported\n if (parsed.provider === \"gitlab\") {\n throw new Error(\n \"GitLab is not yet supported. Currently only GitHub repositories are supported.\",\n );\n }\n\n // Resolve options\n const resolvedRef = options.ref ?? parsed.ref;\n // Normalize backslashes to forward slashes for GitHub API compatibility.\n const resolvedPath = toPosixPath(options.path ?? parsed.path ?? \".\");\n const outputDir = options.output ?? RULESYNC_RELATIVE_DIR_PATH;\n const conflictStrategy: ConflictStrategy = options.conflict ?? \"overwrite\";\n const enabledFeatures = resolveFeatures(options.features);\n const target: FetchTarget = options.target ?? \"rulesync\";\n\n // Validate output directory to prevent path traversal attacks\n checkPathTraversal({\n relativePath: outputDir,\n intendedRootDir: outputRoot,\n });\n\n // Initialize GitHub client\n const token = GitHubClient.resolveToken(options.token);\n const client = new GitHubClient({ token });\n\n // Validate repository\n logger.debug(`Validating repository: ${parsed.owner}/${parsed.repo}`);\n const isValid = await client.validateRepository(parsed.owner, parsed.repo);\n if (!isValid) {\n throw new GitHubClientError(\n `Repository not found: ${parsed.owner}/${parsed.repo}. Check the repository name and your access permissions.`,\n 404,\n );\n }\n\n // Resolve ref to use\n const ref = resolvedRef ?? (await client.getDefaultBranch(parsed.owner, parsed.repo));\n logger.debug(`Using ref: ${ref}`);\n\n // If target is a tool format, use conversion flow\n if (isToolTarget(target)) {\n return fetchAndConvertToolFiles({\n client,\n parsed,\n ref,\n resolvedPath,\n enabledFeatures,\n target,\n outputDir,\n outputRoot,\n conflictStrategy,\n logger,\n });\n }\n\n // Create semaphore for concurrency control\n const semaphore = new Semaphore(FETCH_CONCURRENCY_LIMIT);\n\n // Collect all files to fetch from feature directories directly\n const filesToFetch = await collectFeatureFiles({\n client,\n owner: parsed.owner,\n repo: parsed.repo,\n basePath: resolvedPath,\n ref,\n enabledFeatures,\n semaphore,\n logger,\n });\n\n if (filesToFetch.length === 0) {\n logger.warn(`No files found matching enabled features: ${enabledFeatures.join(\", \")}`);\n return {\n source: `${parsed.owner}/${parsed.repo}`,\n ref,\n files: [],\n created: 0,\n overwritten: 0,\n skipped: 0,\n };\n }\n\n // Process files in parallel with concurrency control\n const outputBasePath = join(outputRoot, outputDir);\n\n // Validate paths and check file sizes first (synchronous checks)\n for (const { relativePath, size } of filesToFetch) {\n checkPathTraversal({\n relativePath,\n intendedRootDir: outputBasePath,\n });\n\n validateFileSize(relativePath, size);\n }\n\n // Process files in parallel with concurrency control\n // Note: Promise.all fails fast - if any promise rejects, others continue running but\n // may have already written files. This behavior is consistent with sequential execution,\n // but the window for partial writes is larger with parallel execution.\n const results = await Promise.all(\n filesToFetch.map(async ({ remotePath, relativePath }) => {\n const localPath = join(outputBasePath, relativePath);\n const exists = await fileExists(localPath);\n\n if (exists && conflictStrategy === \"skip\") {\n logger.debug(`Skipping existing file: ${relativePath}`);\n return { relativePath, status: \"skipped\" as const };\n }\n\n const content = await withSemaphore(semaphore, () =>\n client.getFileContent(parsed.owner, parsed.repo, remotePath, ref),\n );\n await writeFileContent(localPath, content);\n\n const status = exists ? (\"overwritten\" as const) : (\"created\" as const);\n logger.debug(`Wrote: ${relativePath} (${status})`);\n return { relativePath, status };\n }),\n );\n\n // Calculate summary\n const summary: FetchSummary = {\n source: `${parsed.owner}/${parsed.repo}`,\n ref,\n files: results,\n created: results.filter((r) => r.status === \"created\").length,\n overwritten: results.filter((r) => r.status === \"overwritten\").length,\n skipped: results.filter((r) => r.status === \"skipped\").length,\n };\n\n return summary;\n}\n\n/**\n * Collect files from feature directories\n */\nasync function collectFeatureFiles(params: {\n client: GitHubClient;\n owner: string;\n repo: string;\n basePath: string;\n ref: string;\n enabledFeatures: Feature[];\n semaphore: Semaphore;\n logger: Logger;\n}): Promise<Array<{ remotePath: string; relativePath: string; size: number }>> {\n const { client, owner, repo, basePath, ref, enabledFeatures, semaphore, logger } = params;\n\n // Cache directory listing results to avoid duplicate API calls\n // File-based features (ignore, mcp, hooks) all list the same basePath directory\n const dirCache = new Map<string, Promise<GitHubFileEntry[]>>();\n\n async function getCachedDirectory(path: string): Promise<GitHubFileEntry[]> {\n let promise = dirCache.get(path);\n if (promise === undefined) {\n promise = withSemaphore(semaphore, () => client.listDirectory(owner, repo, path, ref));\n dirCache.set(path, promise);\n }\n return promise;\n }\n\n const tasks = enabledFeatures.flatMap((feature) =>\n FEATURE_PATHS[feature].map((featurePath) => ({ feature, featurePath })),\n );\n\n const results = await Promise.all(\n tasks.map(async ({ featurePath }) => {\n const fullPath =\n basePath === \".\" || basePath === \"\" ? featurePath : posix.join(basePath, featurePath);\n const collected: Array<{ remotePath: string; relativePath: string; size: number }> = [];\n\n try {\n // Check if it's a file (mcp.json, .aiignore, hooks.json)\n if (featurePath.includes(\".\")) {\n // Try to get the file directly\n try {\n const entries = await getCachedDirectory(\n basePath === \".\" || basePath === \"\" ? \".\" : basePath,\n );\n const fileEntry = entries.find((e) => e.name === featurePath && e.type === \"file\");\n if (fileEntry) {\n collected.push({\n remotePath: fileEntry.path,\n relativePath: featurePath,\n size: fileEntry.size,\n });\n }\n } catch (error) {\n // Only skip 404 errors (file not found), re-throw other errors\n if (isNotFoundError(error)) {\n logger.debug(`File not found: ${fullPath}`);\n } else {\n throw error;\n }\n }\n } else {\n // It's a directory (rules/, commands/, skills/, subagents/)\n const dirFiles = await listDirectoryRecursive({\n client,\n owner,\n repo,\n path: fullPath,\n ref,\n semaphore,\n });\n\n for (const file of dirFiles) {\n // Calculate relative path from base\n const relativePath =\n basePath === \".\" || basePath === \"\"\n ? file.path\n : file.path.substring(basePath.length + 1);\n\n collected.push({\n remotePath: file.path,\n relativePath,\n size: file.size,\n });\n }\n }\n } catch (error) {\n // Check for 404 errors (feature not found)\n if (isNotFoundError(error)) {\n // Feature directory/file not found, skip silently\n logger.debug(`Feature not found: ${fullPath}`);\n return collected;\n }\n throw error;\n }\n\n return collected;\n }),\n );\n\n return results.flat();\n}\n\n/**\n * Fetch tool-specific files and convert them to rulesync format\n */\nasync function fetchAndConvertToolFiles(params: {\n client: GitHubClient;\n parsed: ParsedSource;\n ref: string;\n resolvedPath: string;\n enabledFeatures: Feature[];\n target: ToolTarget;\n outputDir: string;\n outputRoot: string;\n conflictStrategy: ConflictStrategy;\n logger: Logger;\n}): Promise<FetchSummary> {\n const {\n client,\n parsed,\n ref,\n resolvedPath,\n enabledFeatures,\n target,\n outputDir,\n outputRoot,\n conflictStrategy: _conflictStrategy,\n logger,\n } = params;\n\n // Create a unique temporary directory\n const tempDir = await createTempDirectory();\n logger.debug(`Created temp directory: ${tempDir}`);\n\n // Create semaphore for concurrency control\n const semaphore = new Semaphore(FETCH_CONCURRENCY_LIMIT);\n\n try {\n // Collect files using rulesync feature paths (rules/, commands/, etc.)\n // External repos use these paths directly without tool-specific prefixes\n const filesToFetch = await collectFeatureFiles({\n client,\n owner: parsed.owner,\n repo: parsed.repo,\n basePath: resolvedPath,\n ref,\n enabledFeatures,\n semaphore,\n logger,\n });\n\n if (filesToFetch.length === 0) {\n logger.warn(`No files found matching enabled features: ${enabledFeatures.join(\", \")}`);\n return {\n source: `${parsed.owner}/${parsed.repo}`,\n ref,\n files: [],\n created: 0,\n overwritten: 0,\n skipped: 0,\n };\n }\n\n // Validate file sizes first\n for (const { relativePath, size } of filesToFetch) {\n validateFileSize(relativePath, size);\n }\n\n // Fetch files to temp directory with tool-specific structure in parallel\n // Map rulesync paths to tool-specific paths\n const toolPaths = getToolPathMapping(target);\n\n await Promise.all(\n filesToFetch.map(async ({ remotePath, relativePath }) => {\n // Map the relative path to tool-specific structure\n const toolRelativePath = mapToToolPath(relativePath, toolPaths);\n checkPathTraversal({\n relativePath: toolRelativePath,\n intendedRootDir: tempDir,\n });\n const localPath = join(tempDir, toolRelativePath);\n\n // Fetch file content with concurrency control, then write locally\n const content = await withSemaphore(semaphore, () =>\n client.getFileContent(parsed.owner, parsed.repo, remotePath, ref),\n );\n await writeFileContent(localPath, content);\n logger.debug(`Fetched to temp: ${toolRelativePath}`);\n }),\n );\n\n // Convert fetched files to rulesync format\n const outputBasePath = join(outputRoot, outputDir);\n const { converted, convertedPaths } = await convertFetchedFilesToRulesync({\n tempDir,\n outputDir: outputBasePath,\n target,\n features: enabledFeatures,\n logger,\n });\n\n // Build results based on conversion with actual file paths\n const results: FetchFileResult[] = convertedPaths.map((relativePath) => ({\n relativePath,\n status: \"created\" as const,\n }));\n\n logger.debug(`Converted ${converted} files from ${target} format to rulesync format`);\n\n return {\n source: `${parsed.owner}/${parsed.repo}`,\n ref,\n files: results,\n created: results.filter((r) => r.status === \"created\").length,\n overwritten: results.filter((r) => r.status === \"overwritten\").length,\n skipped: results.filter((r) => r.status === \"skipped\").length,\n };\n } finally {\n // Clean up temp directory\n await removeTempDirectory(tempDir);\n }\n}\n\n/**\n * Get tool-specific path mapping for a target\n * Returns a mapping from rulesync feature paths to tool-specific paths\n */\nfunction getToolPathMapping(target: ToolTarget): {\n rules?: { root?: string; nonRoot?: string };\n commands?: string;\n subagents?: string;\n skills?: string;\n checks?: string;\n} {\n // Get tool-specific paths from each processor class\n const mapping: {\n rules?: { root?: string; nonRoot?: string };\n commands?: string;\n subagents?: string;\n skills?: string;\n checks?: string;\n } = {};\n\n // Rules paths\n const supportedRulesTargets = RulesProcessor.getToolTargets({ global: false });\n if (supportedRulesTargets.includes(target)) {\n const factory = RulesProcessor.getFactory(target);\n if (factory) {\n const paths = factory.class.getSettablePaths({ global: false });\n mapping.rules = {\n root: paths.root?.relativeFilePath,\n nonRoot: paths.nonRoot?.relativeDirPath,\n };\n }\n }\n\n // Commands paths\n const supportedCommandsTargets = CommandsProcessor.getToolTargets({\n global: false,\n includeSimulated: false,\n });\n if (supportedCommandsTargets.includes(target)) {\n const factory = CommandsProcessor.getFactory(target);\n if (factory) {\n const paths = factory.class.getSettablePaths({ global: false });\n mapping.commands = paths.relativeDirPath;\n }\n }\n\n // Subagents paths\n const supportedSubagentsTargets = SubagentsProcessor.getToolTargets({\n global: false,\n includeSimulated: false,\n });\n if (supportedSubagentsTargets.includes(target)) {\n const factory = SubagentsProcessor.getFactory(target);\n if (factory) {\n const paths = factory.class.getSettablePaths({ global: false });\n mapping.subagents = paths.relativeDirPath;\n }\n }\n\n // Skills paths\n const supportedSkillsTargets = SkillsProcessor.getToolTargets({ global: false });\n if (supportedSkillsTargets.includes(target)) {\n const factory = SkillsProcessor.getFactory(target);\n if (factory) {\n const paths = factory.class.getSettablePaths({ global: false });\n mapping.skills = paths.relativeDirPath;\n }\n }\n\n // Checks paths\n const supportedChecksTargets = ChecksProcessor.getToolTargets({ global: false });\n if (supportedChecksTargets.includes(target)) {\n const factory = ChecksProcessor.getFactory(target);\n if (factory) {\n const paths = factory.class.getSettablePaths({ global: false });\n mapping.checks = paths.relativeDirPath;\n }\n }\n\n return mapping;\n}\n\n/**\n * Map a rulesync-style relative path to tool-specific path\n */\nfunction mapToToolPath(\n relativePath: string,\n toolPaths: ReturnType<typeof getToolPathMapping>,\n): string {\n // Check if this is a rules file\n if (relativePath.startsWith(\"rules/\")) {\n const restPath = relativePath.substring(\"rules/\".length);\n if (toolPaths.rules?.nonRoot) {\n return join(toolPaths.rules.nonRoot, restPath);\n }\n }\n\n // Check if this is a root rule file (e.g., CLAUDE.md, AGENTS.md)\n if (toolPaths.rules?.root && relativePath === toolPaths.rules.root) {\n return relativePath;\n }\n\n // Check if this is a commands file\n if (relativePath.startsWith(\"commands/\")) {\n const restPath = relativePath.substring(\"commands/\".length);\n if (toolPaths.commands) {\n return join(toolPaths.commands, restPath);\n }\n }\n\n // Check if this is a subagents file\n if (relativePath.startsWith(\"subagents/\")) {\n const restPath = relativePath.substring(\"subagents/\".length);\n if (toolPaths.subagents) {\n return join(toolPaths.subagents, restPath);\n }\n }\n\n // Check if this is a skills file\n if (relativePath.startsWith(\"skills/\")) {\n const restPath = relativePath.substring(\"skills/\".length);\n if (toolPaths.skills) {\n return join(toolPaths.skills, restPath);\n }\n }\n\n // Check if this is a checks file\n if (relativePath.startsWith(\"checks/\")) {\n const restPath = relativePath.substring(\"checks/\".length);\n if (toolPaths.checks) {\n return join(toolPaths.checks, restPath);\n }\n }\n\n // Default: return as-is\n return relativePath;\n}\n\n/**\n * Format fetch summary for display\n */\nexport function formatFetchSummary(summary: FetchSummary): string {\n const lines: string[] = [];\n\n lines.push(`Fetched from ${summary.source}@${summary.ref}:`);\n\n for (const file of summary.files) {\n const icon = file.status === \"skipped\" ? \"-\" : \"\\u2713\";\n const statusText =\n file.status === \"created\"\n ? \"(created)\"\n : file.status === \"overwritten\"\n ? \"(overwritten)\"\n : \"(skipped - already exists)\";\n lines.push(` ${icon} ${file.relativePath} ${statusText}`);\n }\n\n const parts: string[] = [];\n if (summary.created > 0) parts.push(`${summary.created} created`);\n if (summary.overwritten > 0) parts.push(`${summary.overwritten} overwritten`);\n if (summary.skipped > 0) parts.push(`${summary.skipped} skipped`);\n\n lines.push(\"\");\n const summaryText = parts.length > 0 ? parts.join(\", \") : \"no files\";\n lines.push(`Summary: ${summaryText}`);\n\n return lines.join(\"\\n\");\n}\n","import { fetchFiles, formatFetchSummary } from \"../../lib/fetch.js\";\nimport { GitHubClientError } from \"../../lib/github-client.js\";\nimport type { FetchOptions } from \"../../types/fetch.js\";\nimport { CLIError, ErrorCodes } from \"../../types/json-output.js\";\nimport type { Logger } from \"../../utils/logger.js\";\n\nexport type FetchCommandOptions = FetchOptions & {\n source: string;\n};\n\nexport async function fetchCommand(logger: Logger, options: FetchCommandOptions): Promise<void> {\n const { source, ...fetchOptions } = options;\n\n logger.debug(`Fetching files from ${source}...`);\n\n try {\n const summary = await fetchFiles({\n source,\n options: fetchOptions,\n logger,\n });\n\n // Capture JSON data if in JSON mode\n if (logger.jsonMode) {\n const createdFiles = summary.files\n .filter((f) => f.status === \"created\")\n .map((f) => f.relativePath);\n const overwrittenFiles = summary.files\n .filter((f) => f.status === \"overwritten\")\n .map((f) => f.relativePath);\n const skippedFiles = summary.files\n .filter((f) => f.status === \"skipped\")\n .map((f) => f.relativePath);\n\n logger.captureData(\"source\", source);\n logger.captureData(\"path\", fetchOptions.path);\n logger.captureData(\"created\", createdFiles);\n logger.captureData(\"overwritten\", overwrittenFiles);\n logger.captureData(\"skipped\", skippedFiles);\n logger.captureData(\"totalFetched\", summary.created + summary.overwritten + summary.skipped);\n }\n\n const output = formatFetchSummary(summary);\n\n logger.success(output);\n\n // Exit with appropriate code\n if (summary.created + summary.overwritten === 0 && summary.skipped === 0) {\n logger.warn(\"No files were fetched.\");\n }\n } catch (error) {\n if (error instanceof GitHubClientError) {\n // Include auth hints in error message for JSON mode\n const authHint =\n error.statusCode === 401 || error.statusCode === 403\n ? \" Tip: Set GITHUB_TOKEN or GH_TOKEN environment variable, or use `GITHUB_TOKEN=$(gh auth token) rulesync fetch ...`\"\n : \"\";\n throw new CLIError(`GitHub API Error: ${error.message}.${authHint}`, ErrorCodes.FETCH_FAILED);\n }\n throw error;\n }\n}\n","import { existsSync, type FSWatcher, watch as fsWatch } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\n\nimport {\n RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH,\n RULESYNC_RELATIVE_DIR_PATH,\n} from \"../constants/rulesync-paths.js\";\n\n/**\n * Trailing debounce window applied to file-system events before a regeneration\n * is started. Editor save storms and `git checkout` emit many events within a\n * few milliseconds; coalescing them into a single run keeps the terminal\n * readable and avoids redundant work.\n */\nexport const DEFAULT_WATCH_DEBOUNCE_MS = 300;\n\nexport type WatchSchedulerParams = {\n /**\n * Runs one regeneration for the paths that changed since the previous run.\n */\n run: (params: { triggers: string[] }) => Promise<void>;\n /**\n * Called when `run` rejects. Watching continues afterwards, so this must not\n * rethrow.\n */\n onError: (params: { error: unknown; triggers: string[] }) => void;\n debounceMs?: number;\n};\n\n/**\n * Coalesces file-system change notifications into debounced, non-overlapping\n * runs.\n *\n * Guarantees:\n * - At most one `run` is in flight at any time.\n * - Every notified path is reported to exactly one `run` as a trigger.\n * - Notifications that arrive while a run is in flight schedule exactly one\n * follow-up run after it finishes, so a change is never lost and never\n * causes a run per event.\n */\nexport class WatchScheduler {\n private readonly run: (params: { triggers: string[] }) => Promise<void>;\n private readonly onError: (params: { error: unknown; triggers: string[] }) => void;\n private readonly debounceMs: number;\n private readonly pending = new Set<string>();\n private timer: ReturnType<typeof setTimeout> | undefined;\n private running: Promise<void> | undefined;\n private closed = false;\n\n constructor({ run, onError, debounceMs = DEFAULT_WATCH_DEBOUNCE_MS }: WatchSchedulerParams) {\n this.run = run;\n this.onError = onError;\n this.debounceMs = debounceMs;\n }\n\n public notify({ path }: { path: string }): void {\n if (this.closed) {\n return;\n }\n this.pending.add(path);\n this.schedule();\n }\n\n /**\n * Stops accepting notifications and waits for an in-flight run to settle.\n * Pending (not yet started) changes are dropped.\n */\n public async close(): Promise<void> {\n this.closed = true;\n this.clearTimer();\n this.pending.clear();\n await this.running;\n }\n\n private clearTimer(): void {\n if (this.timer !== undefined) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n }\n\n private schedule(): void {\n this.clearTimer();\n this.timer = setTimeout(() => {\n this.timer = undefined;\n void this.flush();\n }, this.debounceMs);\n }\n\n private async flush(): Promise<void> {\n // A run started by an earlier flush re-schedules itself when it finds\n // pending triggers, so bailing out here never drops a change.\n if (this.closed || this.running !== undefined || this.pending.size === 0) {\n return;\n }\n\n const triggers = [...this.pending];\n this.pending.clear();\n\n const running = (async () => {\n try {\n await this.run({ triggers });\n } catch (error) {\n this.onError({ error, triggers });\n }\n })();\n this.running = running;\n await running;\n this.running = undefined;\n\n if (!this.closed && this.pending.size > 0) {\n this.schedule();\n }\n }\n}\n\nexport type WatchTarget = {\n /** Absolute path of the directory to watch. */\n directory: string;\n recursive: boolean;\n /**\n * When set, only events whose path relative to `directory` satisfies the\n * predicate are forwarded. Used to watch a directory that also holds\n * unrelated files (e.g. the project root, which holds `rulesync.jsonc` next\n * to generated output).\n */\n include?: (relativePath: string) => boolean;\n};\n\nexport type WatchHandle = {\n close: () => void;\n};\n\n/**\n * How often a watcher whose directory disappeared polls for its return.\n */\nexport const DEFAULT_WATCH_REARM_INTERVAL_MS = 500;\n\n/**\n * Watches one directory, re-attaching the underlying `fs.watch` if the\n * directory is deleted and later recreated.\n *\n * Without this, a `git checkout` to a branch without `.rulesync/` (or any\n * tool that replaces the directory rather than its contents) would silently\n * kill the watcher: the deleted inode emits no further events and no error,\n * so watch mode would keep running while never regenerating again.\n *\n * The first attach is not guarded — a missing directory at startup is a real\n * configuration error and must surface to the caller.\n */\nfunction watchTargetWithRearm({\n target,\n onChange,\n onError,\n rearmIntervalMs,\n}: {\n target: WatchTarget;\n onChange: (params: { path: string }) => void;\n onError: (params: { error: unknown; directory: string }) => void;\n rearmIntervalMs: number;\n}): WatchHandle {\n let watcher: FSWatcher | undefined;\n let rearmTimer: ReturnType<typeof setInterval> | undefined;\n let closed = false;\n\n const attach = (): void => {\n const created = fsWatch(\n target.directory,\n { recursive: target.recursive, persistent: true },\n (_eventType, filename) => {\n // `fs.watch` reports a null filename on some platforms; treat those as\n // a change to the watched directory itself.\n if (filename === null || filename === undefined) {\n onChange({ path: target.directory });\n verifyStillWatching();\n return;\n }\n const relativePath = filename.toString();\n if (target.include && !target.include(relativePath)) {\n // Still check liveness: the final event a deleted directory emits\n // names the directory itself, which every `include` predicate here\n // rejects. Returning early would leave the dead watcher attached\n // and re-arming would never start.\n verifyStillWatching();\n return;\n }\n onChange({ path: join(target.directory, relativePath) });\n verifyStillWatching();\n },\n );\n created.on(\"error\", (error) => {\n onError({ error, directory: target.directory });\n verifyStillWatching();\n });\n watcher = created;\n };\n\n const scheduleRearm = (): void => {\n if (closed || rearmTimer !== undefined) {\n return;\n }\n rearmTimer = setInterval(() => {\n if (closed || !existsSync(target.directory)) {\n return;\n }\n clearInterval(rearmTimer);\n rearmTimer = undefined;\n try {\n attach();\n } catch (error) {\n // Lost another race with a delete; keep polling.\n onError({ error, directory: target.directory });\n scheduleRearm();\n return;\n }\n // The directory came back with unknown contents, so regenerate.\n onChange({ path: target.directory });\n }, rearmIntervalMs);\n };\n\n const verifyStillWatching = (): void => {\n if (closed || watcher === undefined || existsSync(target.directory)) {\n return;\n }\n watcher.close();\n watcher = undefined;\n scheduleRearm();\n };\n\n attach();\n\n return {\n close: () => {\n closed = true;\n if (rearmTimer !== undefined) {\n clearInterval(rearmTimer);\n rearmTimer = undefined;\n }\n watcher?.close();\n watcher = undefined;\n },\n };\n}\n\n/**\n * Starts one watcher per target and forwards matching events to `onChange` as\n * absolute paths. If any target fails to attach, the watchers started so far\n * are closed before the error propagates, so no descriptor is leaked.\n */\nexport function watchTargets({\n targets,\n onChange,\n onError,\n rearmIntervalMs = DEFAULT_WATCH_REARM_INTERVAL_MS,\n}: {\n targets: readonly WatchTarget[];\n onChange: (params: { path: string }) => void;\n onError: (params: { error: unknown; directory: string }) => void;\n rearmIntervalMs?: number;\n}): WatchHandle {\n const handles: WatchHandle[] = [];\n\n const closeAll = (): void => {\n for (const handle of handles) {\n handle.close();\n }\n };\n\n try {\n for (const target of targets) {\n handles.push(watchTargetWithRearm({ target, onChange, onError, rearmIntervalMs }));\n }\n } catch (error) {\n closeAll();\n throw error;\n }\n\n return { close: closeAll };\n}\n\n/**\n * Builds the set of directories watch mode observes: the `.rulesync/` source\n * tree (recursively) and, filtered down to the configuration files themselves,\n * the directory holding `rulesync.jsonc`.\n *\n * Only input paths are watched. Generated output lives outside `.rulesync/`, so\n * a regeneration cannot re-trigger the watcher.\n */\nexport function buildWatchTargets({\n inputRoot,\n configFilePath,\n}: {\n inputRoot: string;\n configFilePath: string;\n}): WatchTarget[] {\n const configFilePaths = buildConfigFilePaths({ configFilePath });\n\n return [\n { directory: join(inputRoot, RULESYNC_RELATIVE_DIR_PATH), recursive: true },\n {\n directory: dirname(configFilePath),\n recursive: false,\n include: (relativePath) => configFilePaths.has(join(dirname(configFilePath), relativePath)),\n },\n ];\n}\n\n/**\n * The absolute paths of the configuration files watch mode observes: the base\n * configuration file and the `rulesync.local.jsonc` sitting next to it, which\n * is exactly what `ConfigResolver` loads.\n */\nexport function buildConfigFilePaths({ configFilePath }: { configFilePath: string }): Set<string> {\n return new Set([\n configFilePath,\n join(dirname(configFilePath), RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH),\n ]);\n}\n\n/**\n * Renders trigger paths relative to `baseDir` for logging, truncating long\n * bursts so a `git checkout` does not flood the terminal.\n */\nexport function formatTriggerPaths({\n triggers,\n baseDir,\n max = 5,\n}: {\n triggers: readonly string[];\n baseDir: string;\n max?: number;\n}): string {\n const displayed = triggers.slice(0, max).map((trigger) => relative(baseDir, trigger) || trigger);\n const remaining = triggers.length - displayed.length;\n return remaining > 0 ? `${displayed.join(\", \")} (+${remaining} more)` : displayed.join(\", \");\n}\n","import { ConfigResolver, type ConfigResolverResolveParams } from \"../../config/config-resolver.js\";\nimport type { Config } from \"../../config/config.js\";\nimport { checkRulesyncDirExists, generate, type GenerateResult } from \"../../lib/generate.js\";\nimport {\n buildConfigFilePaths,\n buildWatchTargets,\n formatTriggerPaths,\n WatchScheduler,\n watchTargets,\n} from \"../../lib/watch.js\";\nimport { CLIError, ErrorCodes } from \"../../types/json-output.js\";\nimport { formatError } from \"../../utils/error.js\";\nimport type { Logger } from \"../../utils/logger.js\";\nimport { calculateTotalCount } from \"../../utils/result.js\";\n\nexport type GenerateOptions = ConfigResolverResolveParams & {\n /** Keep running and regenerate whenever a rulesync source file changes. */\n watch?: boolean;\n};\n\n/**\n * Log feature generation result with appropriate prefix based on dry run mode.\n */\nfunction logFeatureResult(\n logger: Logger,\n params: {\n count: number;\n paths: string[];\n featureName: string;\n isPreview: boolean;\n modePrefix: string;\n },\n): void {\n const { count, paths, featureName, isPreview, modePrefix } = params;\n if (count > 0) {\n if (isPreview) {\n logger.info(`${modePrefix} Would write ${count} ${featureName}`);\n } else {\n logger.success(`Written ${count} ${featureName}`);\n }\n for (const p of paths) {\n logger.info(` ${p}`);\n }\n }\n}\n\nconst FEATURE_DEBUG_MESSAGES: Record<string, string> = {\n ignore: \"Generating ignore files...\",\n mcp: \"Generating MCP files...\",\n commands: \"Generating command files...\",\n subagents: \"Generating subagent files...\",\n skills: \"Generating skill files...\",\n hooks: \"Generating hooks...\",\n checks: \"Generating check files...\",\n rules: \"Generating rule files...\",\n};\n\n// Order in which per-feature debug messages are emitted; matches the original\n// sequential `if (features.includes(...))` ladder.\nconst FEATURE_DEBUG_ORDER = [\n \"ignore\",\n \"mcp\",\n \"commands\",\n \"subagents\",\n \"skills\",\n \"hooks\",\n \"checks\",\n \"rules\",\n] as const;\n\nfunction logFeatureDebugMessages(logger: Logger, features: readonly string[]): void {\n for (const feature of FEATURE_DEBUG_ORDER) {\n if (features.includes(feature)) {\n logger.debug(FEATURE_DEBUG_MESSAGES[feature] ?? \"\");\n }\n }\n}\n\n/**\n * Build the human-readable per-feature summary fragments (e.g. \"3 rules\") for\n * features that produced at least one file. Order matches the original\n * sequential `if (count > 0) parts.push(...)` ladder.\n */\nfunction buildSummaryParts(result: GenerateResult): string[] {\n const summarySpecs: { count: number; label: string }[] = [\n { count: result.rulesCount, label: \"rules\" },\n { count: result.ignoreCount, label: \"ignore files\" },\n { count: result.mcpCount, label: \"MCP files\" },\n { count: result.commandsCount, label: \"commands\" },\n { count: result.subagentsCount, label: \"subagents\" },\n { count: result.skillsCount, label: \"skills\" },\n { count: result.hooksCount, label: \"hooks\" },\n { count: result.permissionsCount, label: \"permissions\" },\n { count: result.checksCount, label: \"checks\" },\n { count: result.activationCount, label: \"Hermes activation files\" },\n ];\n\n const parts: string[] = [];\n for (const { count, label } of summarySpecs) {\n if (count > 0) parts.push(`${count} ${label}`);\n }\n return parts;\n}\n\nexport async function generateCommand(logger: Logger, options: GenerateOptions): Promise<void> {\n if (options.watch) {\n await generateWatchCommand(logger, options);\n return;\n }\n await generateOnce(logger, options);\n}\n\n/**\n * Runs one generation. `resolvedConfig` lets a caller that already resolved\n * the configuration (watch mode's startup validation) reuse it instead of\n * paying for a second resolution — and, more importantly, instead of emitting\n * the resolver's warnings twice.\n */\nasync function generateOnce(\n logger: Logger,\n options: GenerateOptions,\n { resolvedConfig }: { resolvedConfig?: Config } = {},\n): Promise<void> {\n const config = resolvedConfig ?? (await ConfigResolver.resolve(options, { logger }));\n\n const check = config.getCheck();\n\n const isPreview = config.isPreviewMode();\n const modePrefix = isPreview ? \"[DRY RUN]\" : \"\";\n\n logger.debug(\"Generating files...\");\n\n if (!(await checkRulesyncDirExists({ inputRoot: config.getInputRoot() }))) {\n throw new CLIError(\n \".rulesync directory not found. Run 'rulesync init' first.\",\n ErrorCodes.RULESYNC_DIR_NOT_FOUND,\n );\n }\n\n logger.debug(`Output roots: ${config.getOutputRoots().join(\", \")}`);\n\n const features = config.getFeatures();\n\n logFeatureDebugMessages(logger, features);\n\n const result = await generate({ config, logger });\n\n const totalGenerated = calculateTotalCount(result);\n\n // Log feature results and capture data for JSON mode\n const featureResults = {\n ignore: { count: result.ignoreCount, paths: result.ignorePaths },\n mcp: { count: result.mcpCount, paths: result.mcpPaths },\n commands: { count: result.commandsCount, paths: result.commandsPaths },\n subagents: { count: result.subagentsCount, paths: result.subagentsPaths },\n skills: { count: result.skillsCount, paths: result.skillsPaths },\n hooks: { count: result.hooksCount, paths: result.hooksPaths },\n permissions: { count: result.permissionsCount, paths: result.permissionsPaths },\n checks: { count: result.checksCount, paths: result.checksPaths },\n rules: { count: result.rulesCount, paths: result.rulesPaths },\n activation: { count: result.activationCount, paths: result.activationPaths },\n };\n\n // Map feature keys to human-readable labels with pluralization\n const featureLabels: Record<string, (count: number) => string> = {\n rules: (count) => `${count === 1 ? \"rule\" : \"rules\"}`,\n ignore: (count) => `${count === 1 ? \"ignore file\" : \"ignore files\"}`,\n mcp: (count) => `${count === 1 ? \"MCP file\" : \"MCP files\"}`,\n commands: (count) => `${count === 1 ? \"command\" : \"commands\"}`,\n subagents: (count) => `${count === 1 ? \"subagent\" : \"subagents\"}`,\n skills: (count) => `${count === 1 ? \"skill\" : \"skills\"}`,\n hooks: (count) => `${count === 1 ? \"hooks file\" : \"hooks files\"}`,\n permissions: (count) => `${count === 1 ? \"permissions file\" : \"permissions files\"}`,\n checks: (count) => `${count === 1 ? \"check\" : \"checks\"}`,\n activation: (count) => `${count === 1 ? \"Hermes activation file\" : \"Hermes activation files\"}`,\n };\n\n for (const [feature, data] of Object.entries(featureResults)) {\n logFeatureResult(logger, {\n count: data.count,\n paths: data.paths,\n featureName: featureLabels[feature]?.(data.count) ?? feature,\n isPreview,\n modePrefix,\n });\n }\n\n // Capture JSON data if in JSON mode\n if (logger.jsonMode) {\n logger.captureData(\"features\", featureResults);\n logger.captureData(\"totalFiles\", totalGenerated);\n logger.captureData(\"hasDiff\", result.hasDiff);\n logger.captureData(\"skills\", result.skills ?? []);\n }\n\n // Check mode must fail even when the change is delete-only and no files are written.\n if (check) {\n if (result.hasDiff) {\n throw new CLIError(\n \"Files are not up to date. Run 'rulesync generate' to update.\",\n ErrorCodes.GENERATION_FAILED,\n );\n }\n\n logger.success(\"✓ All files are up to date.\");\n return;\n }\n\n if (totalGenerated === 0) {\n const enabledFeatures = features.join(\", \");\n logger.info(`✓ All files are up to date (${enabledFeatures})`);\n return;\n }\n\n const parts = buildSummaryParts(result);\n\n if (isPreview) {\n logger.info(`${modePrefix} Would write ${totalGenerated} file(s) total (${parts.join(\" + \")})`);\n } else {\n logger.success(`🎉 All done! Written ${totalGenerated} file(s) total (${parts.join(\" + \")})`);\n }\n}\n\n/**\n * Rejects flag combinations that contradict a long-running watch: `--check`\n * and `--dry-run` are one-shot verification modes (the former is meant to exit\n * non-zero), and `--json` buffers a single result document until the command\n * returns, which never happens while watching.\n */\nexport function assertWatchModeCompatible({\n isCheck,\n isDryRun,\n isJsonMode,\n}: {\n isCheck: boolean;\n isDryRun: boolean;\n isJsonMode: boolean;\n}): void {\n const conflicts = [\n isCheck ? \"--check\" : undefined,\n isDryRun ? \"--dry-run\" : undefined,\n isJsonMode ? \"--json\" : undefined,\n ].filter((flag): flag is string => flag !== undefined);\n\n if (conflicts.length > 0) {\n throw new CLIError(\n `--watch cannot be combined with ${conflicts.join(\", \")}.`,\n ErrorCodes.VALIDATION_FAILED,\n );\n }\n}\n\nasync function generateWatchCommand(logger: Logger, options: GenerateOptions): Promise<void> {\n // Resolve once up front so the incompatible-mode check also covers values\n // coming from the config file, not just CLI flags.\n const config = await ConfigResolver.resolve(options, { logger });\n assertWatchModeCompatible({\n isCheck: config.getCheck(),\n isDryRun: config.getDryRun(),\n isJsonMode: logger.jsonMode,\n });\n\n const inputRoot = config.getInputRoot();\n // Take the path the resolver actually loaded rather than re-deriving it:\n // the two differ when `inputRoot` comes from the configuration file itself.\n const configFilePath = config.getConfigFilePath();\n const configFilePaths = buildConfigFilePaths({ configFilePath });\n\n // Run once before watching so a missing `.rulesync` directory (or any other\n // configuration error) fails fast instead of starting an idle watcher.\n await generateOnce(logger, options, { resolvedConfig: config });\n\n const targets = buildWatchTargets({ inputRoot, configFilePath });\n\n const scheduler = new WatchScheduler({\n run: async ({ triggers }) => {\n logger.info(`\\nChange detected: ${formatTriggerPaths({ triggers, baseDir: inputRoot })}`);\n if (triggers.some((trigger) => configFilePaths.has(trigger))) {\n logger.warn(\n \"Configuration file changed. The set of watched paths is fixed at startup — restart 'rulesync generate --watch' if you changed 'inputRoot' or the configuration file location.\",\n );\n }\n await generateOnce(logger, options);\n },\n onError: ({ error }) => {\n logger.error(`Generation failed: ${formatError(error)}`);\n logger.info(\"Still watching for changes...\");\n },\n });\n\n const handle = watchTargets({\n targets,\n onChange: ({ path }) => {\n scheduler.notify({ path });\n },\n onError: ({ error, directory }) => {\n logger.error(`Watch error on ${directory}: ${formatError(error)}`);\n },\n });\n\n logger.info(\n `\\nWatching for changes in:\\n${targets.map((target) => ` ${target.directory}`).join(\"\\n\")}`,\n );\n logger.info(\"Press Ctrl+C to stop.\");\n\n await new Promise<void>((resolveShutdown) => {\n const shutdown = (): void => {\n process.off(\"SIGINT\", shutdown);\n process.off(\"SIGTERM\", shutdown);\n handle.close();\n void scheduler\n .close()\n .catch((error: unknown) => {\n logger.error(`Failed to stop the watcher cleanly: ${formatError(error)}`);\n })\n .finally(() => {\n logger.info(\"\\nStopped watching.\");\n resolveShutdown();\n });\n };\n process.once(\"SIGINT\", shutdown);\n process.once(\"SIGTERM\", shutdown);\n });\n}\n","import { SHARED_USER_MANAGED_CONFIG_PATHS } from \"../../constants/shared-config-paths.js\";\nimport type { ToolRuleExtraFixedFile } from \"../../features/rules/tool-rule.js\";\nimport type { Feature } from \"../../types/features.js\";\nimport { getProcessorRegistryEntry } from \"../../types/processor-registry.js\";\nimport type { ToolTarget } from \"../../types/tool-targets.js\";\n\nexport type GitignoreEntryTarget = ToolTarget | \"common\";\n\nexport type GitignoreEntryTag = {\n readonly target: GitignoreEntryTarget | ReadonlyArray<GitignoreEntryTarget>;\n readonly feature: Feature | \"general\";\n readonly entry: string;\n};\n\n// Targets excluded from derivation: they don't generate project files\n// (agentsskills) or are deprecated aliases whose outputs are covered elsewhere\n// (augmentcode-legacy → augmentcode, claudecode-legacy → claudecode).\nconst TARGETS_NOT_DERIVED: ReadonlySet<string> = new Set([\n \"agentsskills\",\n \"augmentcode-legacy\",\n \"claudecode-legacy\",\n]);\n\n// Project-scope outputs that rulesync merges into rather than fully owns\n// (user-managed settings files), so they are deliberately not gitignored even\n// though a feature emits them. The list itself lives in\n// `src/constants/shared-config-paths.ts` because the same set also decides\n// which files must not be created just to hold an empty payload.\nexport const DERIVED_PATHS_NOT_GITIGNORED: ReadonlySet<string> = new Set(\n SHARED_USER_MANAGED_CONFIG_PATHS.map((path) => `**/${path}`),\n);\n\nconst toPosix = (path: string): string => path.replace(/\\\\/g, \"/\");\n\nconst dirToGlob = (relativeDirPath: string): string =>\n `**/${toPosix(relativeDirPath).replace(/\\/$/, \"\")}/`;\n\nconst fileToGlob = (relativeDirPath: string | undefined, relativeFilePath: string): string => {\n const hasDir = relativeDirPath && relativeDirPath !== \".\";\n return `**/${toPosix(hasDir ? `${relativeDirPath}/${relativeFilePath}` : relativeFilePath)}`;\n};\n\nconst supportsProject = (factory: unknown): boolean => {\n if (typeof factory !== \"object\" || factory === null || !(\"meta\" in factory)) return true;\n const meta = (factory as { meta?: { supportsProject?: boolean } }).meta;\n return meta?.supportsProject !== false;\n};\n\ntype SettablePathsFn = (options?: { global?: boolean }) => unknown;\n\ntype FactoryMap = ReadonlyMap<ToolTarget, { readonly class: { getSettablePaths: unknown } }>;\n\nconst getProjectPaths = (factory: { class: { getSettablePaths: unknown } }): unknown =>\n (factory.class.getSettablePaths as SettablePathsFn)({ global: false });\n\nconst pushEntry = (\n entries: GitignoreEntryTag[],\n target: ToolTarget,\n feature: Feature,\n entry: string,\n): void => {\n entries.push({ target, feature, entry });\n};\n\nconst deriveDirEntries = (factories: FactoryMap, feature: Feature): GitignoreEntryTag[] => {\n const entries: GitignoreEntryTag[] = [];\n for (const [target, factory] of factories) {\n if (TARGETS_NOT_DERIVED.has(target)) continue;\n if (!supportsProject(factory)) continue;\n const paths = getProjectPaths(factory) as {\n relativeDirPath?: string;\n relativeFilePath?: string;\n };\n const dir = paths.relativeDirPath;\n if (!dir || dir === \".\") continue;\n // A tool that names a single file writes only that file, even though the\n // feature usually emits a directory tree. Ignoring the whole directory would\n // swallow the files the user hand-maintains beside it — and git cannot\n // un-ignore a path inside an ignored directory.\n if (paths.relativeFilePath) {\n pushEntry(entries, target, feature, fileToGlob(dir, paths.relativeFilePath));\n continue;\n }\n pushEntry(entries, target, feature, dirToGlob(dir));\n }\n return entries;\n};\n\nconst deriveFileEntries = (factories: FactoryMap, feature: Feature): GitignoreEntryTag[] => {\n const entries: GitignoreEntryTag[] = [];\n for (const [target, factory] of factories) {\n if (TARGETS_NOT_DERIVED.has(target)) continue;\n if (!supportsProject(factory)) continue;\n const paths = getProjectPaths(factory) as {\n relativeDirPath?: string;\n relativeFilePath?: string;\n };\n if (!paths.relativeFilePath) continue;\n pushEntry(entries, target, feature, fileToGlob(paths.relativeDirPath, paths.relativeFilePath));\n }\n return entries;\n};\n\n// Rules have a composite shape: root/alternativeRoots are files, nonRoot is a\n// directory subtree.\nconst deriveRulesEntries = (): GitignoreEntryTag[] => {\n const entries: GitignoreEntryTag[] = [];\n const factories = getProcessorRegistryEntry(\"rules\").factory as unknown as FactoryMap;\n for (const [target, factory] of factories) {\n if (TARGETS_NOT_DERIVED.has(target)) continue;\n const paths = getProjectPaths(factory) as {\n root?: { relativeDirPath: string; relativeFilePath: string };\n alternativeRoots?: ReadonlyArray<{ relativeDirPath: string; relativeFilePath: string }>;\n nonRoot?: { relativeDirPath: string } | null;\n };\n for (const root of [paths.root, ...(paths.alternativeRoots ?? [])]) {\n if (root)\n pushEntry(\n entries,\n target,\n \"rules\",\n fileToGlob(root.relativeDirPath, root.relativeFilePath),\n );\n }\n const nonRootDir = paths.nonRoot?.relativeDirPath;\n if (nonRootDir && nonRootDir !== \".\") {\n pushEntry(entries, target, \"rules\", dirToGlob(nonRootDir));\n }\n // Extra fixed-path files a tool manages beyond root/nonRoot (e.g. Pi's\n // `.pi/APPEND_SYSTEM.md`). Derived from the same hook the RulesProcessor uses.\n const classWithExtraFiles = factory.class as {\n getExtraFixedFiles?: (options?: { global?: boolean }) => ToolRuleExtraFixedFile[];\n };\n if (classWithExtraFiles.getExtraFixedFiles) {\n for (const file of classWithExtraFiles.getExtraFixedFiles({ global: false })) {\n pushEntry(\n entries,\n target,\n \"rules\",\n fileToGlob(file.relativeDirPath, file.relativeFilePath),\n );\n }\n }\n }\n return entries;\n};\n\n// commands/skills/subagents/checks emit a directory tree; mcp/hooks/permissions/ignore\n// emit a single file; rules has a composite root+nonRoot shape.\nconst DIR_FEATURES = new Set<Feature>([\"commands\", \"skills\", \"subagents\", \"checks\"]);\nconst FILE_FEATURES = new Set<Feature>([\"mcp\", \"hooks\", \"permissions\", \"ignore\"]);\n\nconst deriveFeatureGitignoreEntries = (feature: Feature): GitignoreEntryTag[] => {\n if (feature === \"rules\") return deriveRulesEntries();\n const factory = getProcessorRegistryEntry(feature).factory as unknown as FactoryMap;\n if (DIR_FEATURES.has(feature)) return deriveDirEntries(factory, feature);\n if (FILE_FEATURES.has(feature)) return deriveFileEntries(factory, feature);\n return [];\n};\n\nconst DERIVED_FEATURES: ReadonlyArray<Feature> = [\n \"rules\",\n \"commands\",\n \"skills\",\n \"subagents\",\n \"mcp\",\n \"hooks\",\n \"permissions\",\n \"ignore\",\n \"checks\",\n];\n\n// Every project-scope output path, derived from each tool's getSettablePaths,\n// BEFORE the DERIVED_PATHS_NOT_GITIGNORED exclusion is applied. Exported so\n// tests can verify each exclusion-set path still matches a real output path.\nexport const deriveAllGitignoreEntriesUnfiltered = (): GitignoreEntryTag[] =>\n DERIVED_FEATURES.flatMap((feature) => deriveFeatureGitignoreEntries(feature));\n\n// Every gitignore entry rulesync emits, derived from each tool's getSettablePaths.\nexport const deriveAllGitignoreEntries = (): GitignoreEntryTag[] =>\n deriveAllGitignoreEntriesUnfiltered().filter(\n (tag) => !DERIVED_PATHS_NOT_GITIGNORED.has(tag.entry),\n );\n","import {\n CLAUDECODE_DIR,\n CLAUDECODE_LOCAL_RULE_FILE_NAME,\n CLAUDECODE_MEMORIES_DIR_NAME,\n CLAUDECODE_SETTINGS_LOCAL_FILE_NAME,\n} from \"../../constants/claudecode-paths.js\";\nimport { CODEXCLI_BASH_RULES_FILE_NAME, CODEXCLI_DIR } from \"../../constants/codexcli-paths.js\";\nimport {\n RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH,\n RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH,\n} from \"../../constants/rulesync-paths.js\";\nimport {\n ALL_FEATURES_WITH_WILDCARD,\n type Feature,\n type RulesyncFeatures,\n} from \"../../types/features.js\";\nimport {\n ALL_TOOL_TARGETS_WITH_WILDCARD,\n PACKAGING_TOOL_TARGETS,\n} from \"../../types/tool-targets.js\";\nimport type { Logger } from \"../../utils/logger.js\";\nimport {\n deriveAllGitignoreEntries,\n type GitignoreEntryTag,\n type GitignoreEntryTarget,\n} from \"./gitignore-derive.js\";\n\nconst normalizeGitignoreEntryTargets = (\n target: GitignoreEntryTag[\"target\"],\n): ReadonlyArray<GitignoreEntryTarget> => {\n return typeof target === \"string\" ? [target] : target;\n};\n\n// Hand-maintained entries that are NOT derivable from any tool's\n// getSettablePaths, because they are not rulesync-owned generated outputs:\n// - rulesync's own meta files (`.rulesync/**`, `rulesync.local.jsonc`, the\n// `AGENTS.local.md` / `CLAUDE.local.md` local-root files, the `.aiignore`\n// un-ignore exception)\n// - third-party tool by-products rulesync never writes but gitignores as a\n// convenience (`.claude/*.lock`, `.takt/runs/`, lock files, …)\n// - the `.codexignore` ghost (codexcli has no ignore processor)\n// Everything a tool actually emits is derived below from getSettablePaths.\nexport const HAND_MAINTAINED_GITIGNORE_ENTRIES: ReadonlyArray<GitignoreEntryTag> = [\n // rulesync's own meta files (common scope).\n {\n target: \"common\",\n feature: \"general\",\n entry: `${RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH}/`,\n },\n {\n target: \"common\",\n feature: \"general\",\n entry: `${RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH}/`,\n },\n { target: \"common\", feature: \"general\", entry: \".rulesync/rules/*.local.md\" },\n { target: \"common\", feature: \"general\", entry: \"rulesync.local.jsonc\" },\n // AGENTS.local.md is placed in common scope (not rovodev-only) so that\n // local rule files are always gitignored regardless of which targets are enabled.\n { target: \"common\", feature: \"general\", entry: \"**/AGENTS.local.md\" },\n\n // Local-root rule files: materialized outside getSettablePaths.\n { target: \"claudecode\", feature: \"rules\", entry: `**/${CLAUDECODE_LOCAL_RULE_FILE_NAME}` },\n {\n target: \"claudecode\",\n feature: \"rules\",\n entry: `**/${CLAUDECODE_DIR}/${CLAUDECODE_LOCAL_RULE_FILE_NAME}`,\n },\n\n // Third-party tool by-products rulesync gitignores but never writes itself.\n { target: \"claudecode\", feature: \"general\", entry: `**/${CLAUDECODE_DIR}/*.lock` },\n {\n target: \"claudecode\",\n feature: \"general\",\n entry: `**/${CLAUDECODE_DIR}/${CLAUDECODE_SETTINGS_LOCAL_FILE_NAME}`,\n },\n {\n target: \"claudecode\",\n feature: \"general\",\n entry: `**/${CLAUDECODE_DIR}/${CLAUDECODE_MEMORIES_DIR_NAME}/`,\n },\n { target: \"opencode\", feature: \"general\", entry: \"**/.opencode/package-lock.json\" },\n { target: \"rovodev\", feature: \"general\", entry: \"**/.rovodev/.rulesync/\" },\n { target: \"takt\", feature: \"general\", entry: \"**/.takt/runs/\" },\n { target: \"takt\", feature: \"general\", entry: \"**/.takt/tasks/\" },\n { target: \"takt\", feature: \"general\", entry: \"**/.takt/.cache/\" },\n { target: \"takt\", feature: \"general\", entry: \"**/.takt/config.yaml\" },\n\n // Augment Code's legacy single-file rules path: accepted on import but never\n // generated (so not in getSettablePaths), gitignored as a convenience.\n { target: \"augmentcode\", feature: \"rules\", entry: \"**/.augment-guidelines\" },\n\n // Devin's legacy Windsurf-era workflows directory: commands are now emitted\n // onto the skills surface, but outputs generated by earlier rulesync versions\n // may still exist there, so keep them gitignored as a convenience.\n { target: \"devin\", feature: \"commands\", entry: \"**/.devin/workflows/\" },\n\n // Junie's undocumented memories directory: non-root rules are now folded\n // into the root `.junie/AGENTS.md`, but outputs generated by earlier\n // rulesync versions may still exist there, so keep them gitignored.\n { target: \"junie\", feature: \"rules\", entry: \"**/.junie/memories/\" },\n\n // Goose retired `.gooseignore` upstream (removed \"in favour of gitignore\n // etc\"), so rulesync no longer generates it — but outputs from earlier\n // versions may still exist, so keep them gitignored.\n { target: \"goose\", feature: \"ignore\", entry: \"**/.gooseignore\" },\n\n // Goose subagents moved from the inert sub-recipe YAML surface to the\n // custom-agent Markdown surface; outputs generated by earlier rulesync\n // versions may still exist under the old directory, so keep them gitignored.\n { target: \"goose\", feature: \"subagents\", entry: \"**/.goose/recipes/subagents/\" },\n\n // Junie's allowlist is user-scope only (`~/.junie/allowlist.json`), so the\n // project path left getSettablePaths — but earlier rulesync versions wrote a\n // project `.junie/allowlist.json` Junie never reads, so keep those stale\n // outputs gitignored.\n { target: \"junie\", feature: \"permissions\", entry: \"**/.junie/allowlist.json\" },\n\n // Shared trees and global-scope outputs not produced via project getSettablePaths.\n { target: \"rovodev\", feature: \"skills\", entry: \"**/.agents/skills/\" },\n // The `prompts.yml` manifest is produced via `RovodevCommand.getAuxiliaryFiles`,\n // not `getSettablePaths` (only the sibling `.rovodev/prompts/` content-file\n // directory is derived automatically), so it needs a hand-maintained entry.\n { target: \"rovodev\", feature: \"commands\", entry: \"**/.rovodev/prompts.yml\" },\n { target: \"devin\", feature: \"skills\", entry: \"**/.config/devin/skills/\" },\n { target: \"copilotcli\", feature: \"subagents\", entry: \"**/.copilot/agents/\" },\n { target: \"copilotcli\", feature: \"mcp\", entry: \"**/.copilot/mcp-config.json\" },\n { target: \"copilotcli\", feature: \"hooks\", entry: \"**/.copilot/hooks/\" },\n { target: \"deepagents\", feature: \"hooks\", entry: \"**/.deepagents/hooks.json\" },\n // Hermes project plugins include generated Python/manifest/ownership files\n // alongside the primary patterns/check specs exposed by getSettablePaths.\n { target: \"hermesagent\", feature: \"ignore\", entry: \"**/.hermes/plugins/rulesync-ignore/\" },\n { target: \"hermesagent\", feature: \"checks\", entry: \"**/.hermes/plugins/rulesync-checks/\" },\n\n // Roo aggregates subagents into a single `.roomodes` file (no settable path).\n { target: \"roo\", feature: \"subagents\", entry: \"**/.roomodes\" },\n\n // codexcli has no ignore processor; its `.codexignore` is a ghost entry.\n { target: \"codexcli\", feature: \"ignore\", entry: \"**/.codexignore\" },\n\n // Codex CLI's `rulesync.rules` bash-permission file is produced by\n // `createCodexcliBashRulesFile` in codexcli-permissions.ts. That file is\n // written outside `getSettablePaths`, so it is not derived automatically and\n // needs a hand-maintained entry. Only the single rulesync-owned file is\n // ignored: `.codex/rules/` is a general Codex rules location where users can\n // hand-author their own `*.rules` files that should stay version-controlled.\n {\n target: \"codexcli\",\n feature: \"permissions\",\n entry: `**/${CODEXCLI_DIR}/rules/${CODEXCLI_BASH_RULES_FILE_NAME}`,\n },\n];\n\nexport const GITIGNORE_ENTRY_REGISTRY: ReadonlyArray<GitignoreEntryTag> = [\n ...HAND_MAINTAINED_GITIGNORE_ENTRIES,\n\n // Every entry a tool actually emits, derived from its getSettablePaths.\n ...deriveAllGitignoreEntries(),\n\n // Keep this after ignore entries like Junie's \"**/.aiignore\" so the exception remains effective.\n { target: \"common\", feature: \"general\", entry: \"!.rulesync/.aiignore\" },\n];\n\nexport const ALL_GITIGNORE_ENTRIES: ReadonlyArray<string> = (() => {\n // The registry may register the SAME entry under multiple feature tags\n // The exported default list excludes opt-in packaging targets and dedupes\n // while preserving the original insertion order.\n const seen = new Set<string>();\n const result: string[] = [];\n for (const tag of GITIGNORE_ENTRY_REGISTRY) {\n const targets = normalizeGitignoreEntryTargets(tag.target);\n const isPackagingOnly = targets.every((target) =>\n PACKAGING_TOOL_TARGETS.includes(target as (typeof PACKAGING_TOOL_TARGETS)[number]),\n );\n if (isPackagingOnly) continue;\n if (seen.has(tag.entry)) continue;\n seen.add(tag.entry);\n result.push(tag.entry);\n }\n return result;\n})();\n\ntype FilterGitignoreEntriesParams = {\n readonly targets?: ReadonlyArray<string>;\n readonly features?: RulesyncFeatures;\n};\n\nexport type ResolvedGitignoreEntry = {\n readonly entry: string;\n readonly target: ReadonlyArray<GitignoreEntryTarget>;\n readonly feature: Feature | \"general\";\n};\n\nconst isTargetSelected = (\n target: GitignoreEntryTag[\"target\"],\n selectedTargets: ReadonlyArray<string> | undefined,\n): boolean => {\n const targets = normalizeGitignoreEntryTargets(target);\n\n if (targets.includes(\"common\")) return true;\n if (!selectedTargets || selectedTargets.length === 0 || selectedTargets.includes(\"*\")) {\n return targets.some(\n (candidate) =>\n selectedTargets?.includes(candidate) ||\n !PACKAGING_TOOL_TARGETS.includes(candidate as (typeof PACKAGING_TOOL_TARGETS)[number]),\n );\n }\n return targets.some((candidate) => selectedTargets.includes(candidate));\n};\n\nconst getSelectedGitignoreEntryTargets = (\n target: GitignoreEntryTag[\"target\"],\n selectedTargets: ReadonlyArray<string> | undefined,\n): ReadonlyArray<GitignoreEntryTarget> => {\n const targets = normalizeGitignoreEntryTargets(target);\n\n if (targets.includes(\"common\")) return [\"common\"];\n if (!selectedTargets || selectedTargets.length === 0 || selectedTargets.includes(\"*\")) {\n return targets.filter(\n (candidate) =>\n selectedTargets?.includes(candidate) ||\n !PACKAGING_TOOL_TARGETS.includes(candidate as (typeof PACKAGING_TOOL_TARGETS)[number]),\n );\n }\n\n return targets.filter((candidate) => selectedTargets.includes(candidate));\n};\n\nconst isFeatureSelected = (\n feature: Feature | \"general\",\n features: RulesyncFeatures | undefined,\n): boolean => {\n if (feature === \"general\") return true;\n if (!features) return true;\n if (features.length === 0) return true;\n if (features.includes(\"*\")) return true;\n return features.includes(feature);\n};\n\nconst warnInvalidTargets = (targets: ReadonlyArray<string>, logger?: Logger): void => {\n const validTargets = new Set<string>(ALL_TOOL_TARGETS_WITH_WILDCARD);\n for (const target of targets) {\n if (!validTargets.has(target)) {\n logger?.warn(\n `Unknown target '${target}'. Valid targets: ${ALL_TOOL_TARGETS_WITH_WILDCARD.join(\", \")}`,\n );\n }\n }\n};\n\nconst warnInvalidFeatures = (features: RulesyncFeatures, logger?: Logger): void => {\n const validFeatures = new Set<string>(ALL_FEATURES_WITH_WILDCARD);\n const warned = new Set<string>();\n for (const feature of features) {\n if (!validFeatures.has(feature) && !warned.has(feature)) {\n warned.add(feature);\n logger?.warn(\n `Unknown feature '${feature}'. Valid features: ${ALL_FEATURES_WITH_WILDCARD.join(\", \")}`,\n );\n }\n }\n};\n\nexport const filterGitignoreEntries = (\n params?: FilterGitignoreEntriesParams & { logger?: Logger },\n): string[] => {\n return resolveGitignoreEntries(params).map((entry) => entry.entry);\n};\n\nexport const resolveGitignoreEntries = (\n params?: FilterGitignoreEntriesParams & { logger?: Logger },\n): ResolvedGitignoreEntry[] => {\n const { targets, features, logger } = params ?? {};\n\n if (targets && targets.length > 0) {\n warnInvalidTargets(targets, logger);\n }\n if (features) {\n warnInvalidFeatures(features, logger);\n }\n\n const seen = new Set<string>();\n const result: ResolvedGitignoreEntry[] = [];\n\n for (const tag of GITIGNORE_ENTRY_REGISTRY) {\n if (!isTargetSelected(tag.target, targets)) continue;\n const selectedTagTargets = getSelectedGitignoreEntryTargets(tag.target, targets);\n if (!isFeatureSelected(tag.feature, features)) continue;\n if (seen.has(tag.entry)) continue;\n seen.add(tag.entry);\n result.push({\n entry: tag.entry,\n target: selectedTagTargets,\n feature: tag.feature,\n });\n }\n\n return result;\n};\n","import { join } from \"node:path\";\n\nimport { ConfigResolver } from \"../../config/config-resolver.js\";\nimport type { Feature, GitignoreDestination, RulesyncFeatures } from \"../../types/features.js\";\nimport type { ToolTarget } from \"../../types/tool-targets.js\";\nimport { fileExists, readFileContent, writeFileContent } from \"../../utils/file.js\";\nimport type { Logger } from \"../../utils/logger.js\";\nimport {\n ALL_GITIGNORE_ENTRIES,\n resolveGitignoreEntries,\n type ResolvedGitignoreEntry,\n} from \"./gitignore-entries.js\";\n\n// Start / end markers that delimit the auto-generated block. Wrapping the\n// managed entries with an explicit footer lets `removeExistingRulesyncEntries`\n// strip the block deterministically instead of guessing where it ends.\nconst RULESYNC_HEADER = \"# Generated by Rulesync\";\nconst RULESYNC_FOOTER = \"# End of Rulesync\";\nconst LEGACY_RULESYNC_HEADER = \"# Generated by rulesync - AI tool configuration files\";\n\nconst isRulesyncHeader = (line: string): boolean => {\n const trimmed = line.trim();\n return trimmed === RULESYNC_HEADER || trimmed === LEGACY_RULESYNC_HEADER;\n};\n\nconst isRulesyncFooter = (line: string): boolean => {\n return line.trim() === RULESYNC_FOOTER;\n};\n\nconst isRulesyncEntry = (line: string): boolean => {\n const trimmed = line.trim();\n if (trimmed === \"\" || isRulesyncHeader(line) || isRulesyncFooter(line)) {\n return false;\n }\n return ALL_GITIGNORE_ENTRIES.includes(trimmed);\n};\n\n// Locate the footer that closes the block opened at `start - 1`. Returns -1 when\n// no footer appears before the next header (i.e. a legacy, marker-less block).\nconst findRulesyncFooterIndex = (lines: string[], start: number): number => {\n for (let index = start; index < lines.length; index++) {\n const line = lines[index] ?? \"\";\n if (isRulesyncFooter(line)) {\n return index;\n }\n if (isRulesyncHeader(line)) {\n return -1;\n }\n }\n return -1;\n};\n\n// Legacy fallback for blocks written before the footer marker existed: skip the\n// header and its following rulesync entries, stopping at two consecutive blank\n// lines or the first line that is neither blank nor a known rulesync entry.\nconst skipLegacyRulesyncBlock = (lines: string[], headerIndex: number): number => {\n let index = headerIndex + 1;\n let consecutiveEmptyLines = 0;\n\n while (index < lines.length) {\n const line = lines[index] ?? \"\";\n\n if (line.trim() === \"\") {\n consecutiveEmptyLines++;\n index++;\n if (consecutiveEmptyLines >= 2) {\n break;\n }\n continue;\n }\n\n if (isRulesyncEntry(line)) {\n consecutiveEmptyLines = 0;\n index++;\n continue;\n }\n\n // A non-blank, non-entry line ends the legacy block; leave it untouched.\n break;\n }\n\n return index;\n};\n\nconst removeExistingRulesyncEntries = (content: string): string => {\n const lines = content.split(\"\\n\");\n const filteredLines: string[] = [];\n let index = 0;\n\n while (index < lines.length) {\n const line = lines[index] ?? \"\";\n\n if (isRulesyncHeader(line)) {\n const footerIndex = findRulesyncFooterIndex(lines, index + 1);\n if (footerIndex !== -1) {\n // Marker-delimited block: drop everything from header to footer.\n index = footerIndex + 1;\n continue;\n }\n // No footer found: this is a legacy block, remove it heuristically.\n index = skipLegacyRulesyncBlock(lines, index);\n continue;\n }\n\n // Stray rulesync entries left outside a block (e.g. legacy leftovers).\n if (isRulesyncEntry(line)) {\n index++;\n continue;\n }\n\n filteredLines.push(line);\n index++;\n }\n\n let result = filteredLines.join(\"\\n\");\n\n while (result.endsWith(\"\\n\\n\")) {\n result = result.slice(0, -1);\n }\n\n return result;\n};\n\n// Collect the entries currently sitting inside rulesync-managed blocks (plus\n// stray recognized entries outside them), so the command can report which\n// previously managed paths are about to stop being gitignored.\nconst extractRulesyncManagedEntries = (content: string): string[] => {\n const lines = content.split(\"\\n\");\n const managed: string[] = [];\n let index = 0;\n\n const collectBlockLines = (start: number, end: number): void => {\n for (const blockLine of lines.slice(start, end)) {\n const trimmed = blockLine.trim();\n if (trimmed !== \"\") {\n managed.push(trimmed);\n }\n }\n };\n\n while (index < lines.length) {\n const line = lines[index] ?? \"\";\n\n if (isRulesyncHeader(line)) {\n const footerIndex = findRulesyncFooterIndex(lines, index + 1);\n if (footerIndex !== -1) {\n collectBlockLines(index + 1, footerIndex);\n index = footerIndex + 1;\n continue;\n }\n const legacyEnd = skipLegacyRulesyncBlock(lines, index);\n collectBlockLines(index + 1, legacyEnd);\n index = legacyEnd;\n continue;\n }\n\n if (isRulesyncEntry(line)) {\n managed.push(line.trim());\n }\n index++;\n }\n\n return managed;\n};\n\nexport type GitignoreCommandOptions = {\n readonly targets?: string[];\n readonly features?: RulesyncFeatures;\n readonly verbose?: boolean;\n readonly silent?: boolean;\n};\n\nconst groupEntriesByDestination = ({\n entries,\n resolveDestination,\n}: {\n entries: ReadonlyArray<ResolvedGitignoreEntry>;\n resolveDestination: (target: ToolTarget, feature?: Feature | \"general\") => GitignoreDestination;\n}): { gitignore: string[]; gitattributes: string[] } => {\n const gitignore = new Set<string>();\n const gitattributes = new Set<string>();\n\n for (const entry of entries) {\n const selectedToolTargets = entry.target.filter(\n (target): target is ToolTarget => target !== \"common\",\n );\n const destinations = new Set<GitignoreDestination>();\n for (const target of selectedToolTargets) {\n if (entry.feature === \"general\") {\n destinations.add(resolveDestination(target));\n } else {\n destinations.add(resolveDestination(target, entry.feature));\n }\n }\n\n if (destinations.has(\"gitattributes\")) {\n gitattributes.add(entry.entry);\n }\n if (destinations.size === 0 || destinations.has(\"gitignore\")) {\n gitignore.add(entry.entry);\n }\n }\n\n return {\n gitignore: [...gitignore],\n gitattributes: [...gitattributes],\n };\n};\n\nexport const gitignoreCommand = async (\n logger: Logger,\n options?: GitignoreCommandOptions,\n): Promise<void> => {\n const gitignorePath = join(process.cwd(), \".gitignore\");\n const gitattributesPath = join(process.cwd(), \".gitattributes\");\n const config = await ConfigResolver.resolve(\n { verbose: options?.verbose, silent: options?.silent },\n { logger },\n );\n\n const resolvedEntries = resolveGitignoreEntries({\n targets: options?.targets,\n features: options?.features,\n logger,\n });\n const { gitignore: gitignoreEntries, gitattributes: gitattributesEntries } =\n groupEntriesByDestination({\n entries: resolvedEntries,\n resolveDestination: (target, feature) => {\n if (feature === undefined || feature === \"general\") {\n return config.getGitignoreDestination(target);\n }\n return config.getGitignoreDestination(target, feature);\n },\n });\n\n const updateRulesyncFile = async ({\n filePath,\n entries,\n }: {\n filePath: string;\n entries: string[];\n }): Promise<{\n updated: boolean;\n alreadyExistedEntries: string[];\n entriesToAdd: string[];\n entriesRemoved: string[];\n }> => {\n let content = \"\";\n if (await fileExists(filePath)) {\n content = await readFileContent(filePath);\n }\n const cleanedContent = removeExistingRulesyncEntries(content);\n const entrySet = new Set(entries);\n const entriesRemoved = [\n ...new Set(extractRulesyncManagedEntries(content).filter((entry) => !entrySet.has(entry))),\n ];\n\n const existingEntries = new Set(\n content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line !== \"\" && !isRulesyncHeader(line) && !isRulesyncFooter(line)),\n );\n const alreadyExistedEntries = entries.filter((entry) => existingEntries.has(entry));\n const entriesToAdd = entries.filter((entry) => !existingEntries.has(entry));\n const rulesyncBlock = [RULESYNC_HEADER, ...entries, RULESYNC_FOOTER].join(\"\\n\");\n const newContent =\n entries.length === 0\n ? cleanedContent.trim()\n ? `${cleanedContent.trimEnd()}\\n`\n : \"\"\n : cleanedContent.trim()\n ? `${cleanedContent.trimEnd()}\\n\\n${rulesyncBlock}\\n`\n : `${rulesyncBlock}\\n`;\n\n if (content === newContent) {\n return { updated: false, alreadyExistedEntries, entriesToAdd: [], entriesRemoved: [] };\n }\n await writeFileContent(filePath, newContent);\n return { updated: true, alreadyExistedEntries, entriesToAdd, entriesRemoved };\n };\n\n const gitignoreResult = await updateRulesyncFile({\n filePath: gitignorePath,\n entries: gitignoreEntries,\n });\n const gitattributesResult = await updateRulesyncFile({\n filePath: gitattributesPath,\n entries: gitattributesEntries,\n });\n\n if (!gitignoreResult.updated && !gitattributesResult.updated) {\n // Capture JSON data if in JSON mode\n if (logger.jsonMode) {\n logger.captureData(\"entriesAdded\", []);\n logger.captureData(\"gitignorePath\", gitignorePath);\n logger.captureData(\"gitattributesPath\", gitattributesPath);\n logger.captureData(\"alreadyExisted\", [...gitignoreEntries, ...gitattributesEntries]);\n }\n logger.success(\".gitignore / .gitattributes are already up to date\");\n return;\n }\n\n // Capture JSON data if in JSON mode\n if (logger.jsonMode) {\n logger.captureData(\"entriesAdded\", [\n ...gitignoreResult.entriesToAdd,\n ...gitattributesResult.entriesToAdd,\n ]);\n logger.captureData(\"gitignorePath\", gitignorePath);\n logger.captureData(\"gitattributesPath\", gitattributesPath);\n logger.captureData(\"alreadyExisted\", [\n ...gitignoreResult.alreadyExistedEntries,\n ...gitattributesResult.alreadyExistedEntries,\n ]);\n logger.captureData(\"entriesRemoved\", gitignoreResult.entriesRemoved);\n }\n\n if (gitignoreResult.entriesRemoved.length > 0) {\n logger.warn(\n \"The following entries were removed from the rulesync-managed block in .gitignore and are no longer gitignored by rulesync:\",\n );\n for (const entry of gitignoreResult.entriesRemoved) {\n logger.warn(` ${entry}`);\n }\n logger.warn(\n \"Review these paths before committing — user-managed settings files may contain secrets.\",\n );\n }\n\n if (gitignoreResult.updated) {\n logger.success(\"Updated .gitignore with rulesync entries:\");\n } else {\n logger.success(\".gitignore is already up to date\");\n }\n for (const entry of gitignoreEntries) {\n logger.info(` ${entry}`);\n }\n if (gitattributesEntries.length > 0) {\n if (gitattributesResult.updated) {\n logger.success(\"Updated .gitattributes with rulesync entries:\");\n } else {\n logger.success(\".gitattributes is already up to date\");\n }\n for (const entry of gitattributesEntries) {\n logger.info(` ${entry}`);\n }\n }\n\n logger.info(\"\");\n logger.info(\n \"💡 If you're using Google Antigravity, note that rules, workflows, and skills won't load if they're gitignored.\",\n );\n logger.info(\" You can add the following to .git/info/exclude instead:\");\n logger.info(\" **/.agents/rules/\");\n logger.info(\" **/.agents/workflows/\");\n logger.info(\" **/.agents/skills/\");\n logger.info(\" For more details: https://github.com/dyoshikawa/rulesync/issues/981\");\n};\n","import { ConfigResolver, ConfigResolverResolveParams } from \"../../config/config-resolver.js\";\nimport { importFromTool } from \"../../lib/import.js\";\nimport { CLIError, ErrorCodes } from \"../../types/json-output.js\";\nimport type { Logger } from \"../../utils/logger.js\";\nimport { calculateTotalCount } from \"../../utils/result.js\";\n\n// `inputRoot` is intentionally excluded: it only affects where source rules\n// are *read from* during `generate`, and `import` does not consume them. Keeping\n// it in the option type would be misleading. Note that this avoids surfacing\n// the \"Ignoring `global: true`\" warning on direct programmatic / CLI callers;\n// users with an `inputRoot` set in their config file (e.g. `rulesync.jsonc`) may\n// still see the warning because `ConfigResolver.resolve` reads `configByFile`\n// regardless of this `Omit`. That residual warning is actionable — it tells\n// the user their config-file `inputRoot` is being ignored during `import`.\nexport type ImportOptions = Omit<ConfigResolverResolveParams, \"delete\" | \"inputRoot\">;\n\nexport async function importCommand(logger: Logger, options: ImportOptions): Promise<void> {\n if (!options.targets) {\n throw new CLIError(\"No tools found in --targets\", ErrorCodes.IMPORT_FAILED);\n }\n\n // The CLI only provides the array form for --targets; the object form is\n // config-file-only. Defend with a runtime check so TS can narrow safely.\n if (!Array.isArray(options.targets)) {\n throw new CLIError(\n \"--targets object form is not supported on the command line\",\n ErrorCodes.IMPORT_FAILED,\n );\n }\n\n if (options.targets.length > 1) {\n throw new CLIError(\"Only one tool can be imported at a time\", ErrorCodes.IMPORT_FAILED);\n }\n\n const config = await ConfigResolver.resolve(options, { logger });\n\n const tool = config.getTargets()[0]!;\n\n logger.debug(`Importing files from ${tool}...`);\n\n const result = await importFromTool({ config, tool, logger });\n\n const totalImported = calculateTotalCount(result);\n\n if (totalImported === 0) {\n const enabledFeatures = config.getFeatures().join(\", \");\n logger.warn(`No files imported for enabled features: ${enabledFeatures}`);\n return;\n }\n\n // Capture JSON data if in JSON mode\n if (logger.jsonMode) {\n logger.captureData(\"tool\", tool);\n logger.captureData(\"features\", {\n rules: { count: result.rulesCount },\n ignore: { count: result.ignoreCount },\n mcp: { count: result.mcpCount },\n commands: { count: result.commandsCount },\n subagents: { count: result.subagentsCount },\n skills: { count: result.skillsCount },\n hooks: { count: result.hooksCount },\n permissions: { count: result.permissionsCount },\n checks: { count: result.checksCount },\n });\n logger.captureData(\"totalFiles\", totalImported);\n }\n\n const parts = [];\n if (result.rulesCount > 0) parts.push(`${result.rulesCount} rules`);\n if (result.ignoreCount > 0) parts.push(`${result.ignoreCount} ignore files`);\n if (result.mcpCount > 0) parts.push(`${result.mcpCount} MCP files`);\n if (result.commandsCount > 0) parts.push(`${result.commandsCount} commands`);\n if (result.subagentsCount > 0) parts.push(`${result.subagentsCount} subagents`);\n if (result.skillsCount > 0) parts.push(`${result.skillsCount} skills`);\n if (result.hooksCount > 0) parts.push(`${result.hooksCount} hooks`);\n if (result.permissionsCount > 0) parts.push(`${result.permissionsCount} permissions`);\n if (result.checksCount > 0) parts.push(`${result.checksCount} checks`);\n\n logger.success(`Imported ${totalImported} file(s) total (${parts.join(\" + \")})`);\n}\n","import { dirname } from \"node:path\";\n\nimport { ConfigFile } from \"../config/config.js\";\nimport {\n RULESYNC_CONFIG_RELATIVE_FILE_PATH,\n RULESYNC_CONFIG_SCHEMA_URL,\n} from \"../constants/rulesync-paths.js\";\nimport { ensureDir, fileExists, writeFileContent } from \"../utils/file.js\";\nimport { createFeatureScaffold } from \"./feature-scaffold.js\";\n\ntype InitFileResult = {\n created: boolean;\n path: string;\n};\n\nexport type InitResult = {\n configFile: InitFileResult;\n sampleFiles: InitFileResult[];\n};\n\n/**\n * Initialize rulesync configuration and sample files.\n * This is the core logic without CLI-specific logging.\n */\nexport async function init(): Promise<InitResult> {\n const sampleFiles = await createSampleFiles();\n const configFile = await createConfigFile();\n\n return {\n configFile,\n sampleFiles,\n };\n}\n\nasync function createConfigFile(): Promise<InitFileResult> {\n const path = RULESYNC_CONFIG_RELATIVE_FILE_PATH;\n\n if (await fileExists(path)) {\n return { created: false, path };\n }\n\n await writeFileContent(\n path,\n JSON.stringify(\n {\n $schema: RULESYNC_CONFIG_SCHEMA_URL,\n targets: [\"codexcli\", \"claudecode\", \"opencode\"],\n features: [\"rules\", \"mcp\", \"subagents\", \"skills\", \"hooks\", \"permissions\"],\n outputRoots: [\".\"],\n delete: true,\n verbose: false,\n silent: false,\n global: false,\n simulateCommands: false,\n simulateSubagents: false,\n simulateSkills: false,\n gitignoreTargetsOnly: true,\n } satisfies ConfigFile,\n null,\n 2,\n ),\n );\n\n return { created: true, path };\n}\n\nasync function createSampleFiles(): Promise<InitFileResult[]> {\n const samples = [\n createFeatureScaffold({ feature: \"rule\", name: \"overview\" }),\n createFeatureScaffold({ feature: \"mcp\" }),\n createFeatureScaffold({ feature: \"subagent\", name: \"planner\" }),\n createFeatureScaffold({ feature: \"skill\", name: \"project-context\" }),\n createFeatureScaffold({ feature: \"hooks\" }),\n createFeatureScaffold({ feature: \"permissions\" }),\n ];\n\n const results: InitFileResult[] = [];\n for (const sample of samples) {\n await ensureDir(dirname(sample.relativeFilePath));\n results.push(\n await writeIfNotExists({\n path: sample.relativeFilePath,\n candidatePaths: sample.candidateRelativeFilePaths,\n content: sample.content,\n }),\n );\n }\n return results;\n}\n\nasync function writeIfNotExists({\n path,\n candidatePaths,\n content,\n}: {\n path: string;\n candidatePaths: string[];\n content: string;\n}): Promise<InitFileResult> {\n for (const candidatePath of candidatePaths) {\n if (await fileExists(candidatePath)) {\n return { created: false, path: candidatePath };\n }\n }\n\n await writeFileContent(path, content);\n return { created: true, path };\n}\n","import { SKILL_FILE_NAME } from \"../../constants/general.js\";\nimport {\n RULESYNC_HOOKS_RELATIVE_FILE_PATH,\n RULESYNC_MCP_RELATIVE_FILE_PATH,\n RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH,\n RULESYNC_RELATIVE_DIR_PATH,\n} from \"../../constants/rulesync-paths.js\";\nimport { init } from \"../../lib/init.js\";\nimport { ensureDir } from \"../../utils/file.js\";\nimport type { Logger } from \"../../utils/logger.js\";\n\nexport async function initCommand(logger: Logger): Promise<void> {\n logger.debug(\"Initializing rulesync...\");\n\n await ensureDir(RULESYNC_RELATIVE_DIR_PATH);\n\n const result = await init();\n\n // Log sample file results\n const createdFiles: string[] = [];\n const skippedFiles: string[] = [];\n\n for (const file of result.sampleFiles) {\n if (file.created) {\n createdFiles.push(file.path);\n logger.success(`Created ${file.path}`);\n } else {\n skippedFiles.push(file.path);\n logger.info(`Skipped ${file.path} (already exists)`);\n }\n }\n\n // Log config file result\n if (result.configFile.created) {\n createdFiles.push(result.configFile.path);\n logger.success(`Created ${result.configFile.path}`);\n } else {\n skippedFiles.push(result.configFile.path);\n logger.info(`Skipped ${result.configFile.path} (already exists)`);\n }\n\n // Capture JSON data if in JSON mode\n if (logger.jsonMode) {\n logger.captureData(\"created\", createdFiles);\n logger.captureData(\"skipped\", skippedFiles);\n }\n\n logger.success(\"rulesync initialized successfully!\");\n logger.info(\"Next steps:\");\n logger.info(\n `1. Edit ${RULESYNC_RELATIVE_DIR_PATH}/**/*.md, ${RULESYNC_RELATIVE_DIR_PATH}/skills/*/${SKILL_FILE_NAME}, ${RULESYNC_MCP_RELATIVE_FILE_PATH}, ${RULESYNC_HOOKS_RELATIVE_FILE_PATH} and ${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}`,\n );\n logger.info(\"2. Run 'rulesync generate' to create configuration files\");\n}\n","import { join } from \"node:path\";\n\nimport { dump } from \"js-yaml\";\nimport { nonnegative, optional, refine, z } from \"zod/mini\";\n\nimport { fileExists, readFileContent, writeFileContent } from \"../../utils/file.js\";\nimport { loadYaml } from \"../../utils/yaml.js\";\n\n/**\n * Filename of the rulesync-managed apm-compatible lockfile. Rulesync uses a\n * lockfile name distinct from the upstream `apm` CLI's `apm.lock.yaml` so the\n * two tools do not fight over the same file: the schema is still the apm v1\n * lockfile format, but rulesync only reads/writes its own file.\n */\nconst APM_LOCKFILE_FILE_NAME = \"rulesync-apm.lock.yaml\";\nexport const APM_LOCKFILE_VERSION = \"1\" as const;\n\n/**\n * Shape of content_hash values that rulesync writes. Used by `--frozen`\n * integrity checks to decide whether a prior hash is comparable: any value\n * not matching this regex (e.g. written by the upstream `apm` CLI) is\n * skipped rather than throwing so that cross-tool interop works.\n */\nexport const RULESYNC_CONTENT_HASH_REGEX = /^sha256:[0-9a-f]{64}$/;\n\n/**\n * Single dependency entry in `rulesync-apm.lock.yaml`. Mirrors the subset of the\n * APM v1 lockfile schema that rulesync currently populates. Extra fields\n * from the spec (content_hash, is_dev, virtual_path, ...) are preserved\n * verbatim so that rulesync does not strip them out when re-writing a\n * lockfile produced by `apm` itself.\n */\nconst ApmLockDependencySchema = z.looseObject({\n repo_url: z.string(),\n resolved_commit: optional(\n z\n .string()\n .check(refine((v) => /^[0-9a-f]{40}$/.test(v), \"resolved_commit must be a 40-char hex SHA\")),\n ),\n resolved_ref: optional(z.string()),\n version: optional(z.string()),\n depth: z.int().check(nonnegative()),\n resolved_by: optional(z.string()),\n package_type: z.string(),\n // Intentionally loose: the upstream `apm` CLI may write content_hash values\n // that do not match the strict rulesync format. We accept any string on read\n // so that a lockfile produced by `apm` round-trips through rulesync without\n // throwing. Rulesync itself always writes values matching\n // `RULESYNC_CONTENT_HASH_REGEX`, and `--frozen` integrity checks only\n // enforce the comparison when the recorded hash matches that shape.\n content_hash: optional(z.string()),\n is_dev: optional(z.boolean()),\n deployed_files: z.array(z.string()),\n source: optional(z.string()),\n local_path: optional(z.string()),\n virtual_path: optional(z.string()),\n is_virtual: optional(z.boolean()),\n});\nexport type ApmLockDependency = z.infer<typeof ApmLockDependencySchema>;\n\nconst ApmLockSchema = z.looseObject({\n lockfile_version: z.literal(\"1\"),\n generated_at: z.string(),\n apm_version: z.string(),\n dependencies: z.array(ApmLockDependencySchema),\n mcp_servers: optional(z.array(z.string())),\n});\nexport type ApmLock = z.infer<typeof ApmLockSchema>;\n\nexport function getApmLockPath(projectRoot: string): string {\n return join(projectRoot, APM_LOCKFILE_FILE_NAME);\n}\n\n/**\n * Create an empty lockfile structure. `apm_version` is set to the rulesync\n * compatibility-marker string so downstream tooling can tell this lockfile\n * was produced by rulesync rather than the upstream `apm` CLI.\n *\n * When `existingLock` is provided, all top-level fields from that lock (e.g.\n * `mcp_servers` and any looseObject extras written by the upstream `apm`\n * CLI) are carried forward. `dependencies` is always reset to an empty array\n * and `generated_at` is refreshed; `apm_version` is overwritten by the value\n * passed in `params.apmVersion`.\n */\nexport function createEmptyApmLock(params: {\n apmVersion: string;\n existingLock?: ApmLock | null;\n}): ApmLock {\n const base = params.existingLock ? { ...params.existingLock } : {};\n return {\n ...base,\n lockfile_version: APM_LOCKFILE_VERSION,\n generated_at: new Date().toISOString(),\n apm_version: params.apmVersion,\n dependencies: [],\n };\n}\n\n/**\n * Parse `rulesync-apm.lock.yaml` content into an `ApmLock`. Returns `null` when the\n * content is absent / empty / non-YAML-object so callers can treat the lock\n * as missing. A *structurally* present lockfile that fails schema validation\n * throws a descriptive error rather than being silently dropped — silently\n * discarding a corrupt lockfile would erase previously pinned commits.\n */\nexport function parseApmLock(content: string): ApmLock | null {\n if (!content.trim()) {\n return null;\n }\n let loaded: unknown;\n try {\n loaded = loadYaml(content);\n } catch {\n return null;\n }\n if (!loaded || typeof loaded !== \"object\") {\n return null;\n }\n const parsed = ApmLockSchema.safeParse(loaded);\n if (!parsed.success) {\n const issues = parsed.error.issues\n .map((issue) => ` - ${issue.path.join(\".\") || \"<root>\"}: ${issue.message}`)\n .join(\"\\n\");\n throw new Error(`Invalid ${APM_LOCKFILE_FILE_NAME}:\\n${issues}`);\n }\n return parsed.data;\n}\n\nexport async function readApmLock(projectRoot: string): Promise<ApmLock | null> {\n const path = getApmLockPath(projectRoot);\n if (!(await fileExists(path))) {\n return null;\n }\n const content = await readFileContent(path);\n return parseApmLock(content);\n}\n\nexport async function writeApmLock(params: { projectRoot: string; lock: ApmLock }): Promise<void> {\n const path = getApmLockPath(params.projectRoot);\n const content = serializeApmLock(params.lock);\n await writeFileContent(path, content);\n}\n\nexport function serializeApmLock(lock: ApmLock): string {\n // `noRefs: true` avoids YAML anchors/aliases; `lineWidth: -1` keeps long\n // URLs on a single line so the file stays diff-friendly.\n return dump(lock, { noRefs: true, lineWidth: -1, sortKeys: false });\n}\n\n/**\n * Find the locked entry for a repo_url. GitHub routes `owner/repo` path\n * components case-insensitively, so the comparison here is case-insensitive\n * to match `apm-manifest.ts` canonicalization and avoid frozen-mode false\n * positives when users re-case their manifest.\n */\nexport function findApmLockDependency(\n lock: ApmLock,\n repoUrl: string,\n): ApmLockDependency | undefined {\n const target = repoUrl.toLowerCase();\n return lock.dependencies.find((d) => d.repo_url.toLowerCase() === target);\n}\n","import { join } from \"node:path\";\n\nimport { optional, z } from \"zod/mini\";\n\nimport { fileExists, readFileContent } from \"../../utils/file.js\";\nimport { loadYaml } from \"../../utils/yaml.js\";\n\nconst APM_MANIFEST_FILE_NAME = \"apm.yml\";\n\n/**\n * Parsed representation of a single APM `dependencies.apm` entry after\n * normalization. Every accepted input form (string shorthand, object form,\n * HTTPS URL) lands here.\n */\nexport type ApmDependency = {\n /** Canonical git URL. Always an HTTPS URL for the first iteration. */\n gitUrl: string;\n /** GitHub owner (extracted for use with the REST client). */\n owner: string;\n /** GitHub repo. */\n repo: string;\n /**\n * Optional ref (tag, branch, or commit SHA). Absent means \"resolve against\n * the repository's default branch\".\n */\n ref?: string;\n /**\n * Optional virtual sub-directory within the repository. When present the\n * install layout is rooted at this path.\n */\n path?: string;\n /**\n * Optional alias used to override the local install directory name.\n */\n alias?: string;\n};\n\nconst ApmObjectDependencySchema = z.looseObject({\n git: optional(z.string()),\n source: optional(z.string()),\n path: optional(z.string()),\n ref: optional(z.string()),\n alias: optional(z.string()),\n});\n\nconst ApmDependencyInputSchema = z.union([z.string(), ApmObjectDependencySchema]);\n\nconst ApmManifestSchema = z.looseObject({\n name: optional(z.string()),\n version: optional(z.string()),\n dependencies: optional(\n z.looseObject({\n apm: optional(z.array(ApmDependencyInputSchema)),\n }),\n ),\n});\n\nexport type ApmManifest = {\n name?: string;\n version?: string;\n dependencies: ApmDependency[];\n};\n\n/**\n * Return the absolute path to the project's `apm.yml`.\n */\nexport function getApmManifestPath(projectRoot: string): string {\n return join(projectRoot, APM_MANIFEST_FILE_NAME);\n}\n\n/**\n * True if `apm.yml` exists at the given base directory.\n */\nexport async function apmManifestExists(projectRoot: string): Promise<boolean> {\n return fileExists(getApmManifestPath(projectRoot));\n}\n\n/**\n * Parse `apm.yml` content. Throws with a descriptive error when parsing\n * or any dependency entry fails normalization.\n */\nexport function parseApmManifest(content: string): ApmManifest {\n const loaded = loadYaml(content);\n if (loaded === undefined || loaded === null) {\n return { dependencies: [] };\n }\n const parsed = ApmManifestSchema.safeParse(loaded);\n if (!parsed.success) {\n throw new Error(`Invalid apm.yml: ${parsed.error.message}`);\n }\n const raw = parsed.data;\n const rawDeps = raw.dependencies?.apm ?? [];\n const dependencies: ApmDependency[] = rawDeps.map((entry, index) =>\n normalizeDependency(entry, index),\n );\n return {\n name: raw.name,\n version: raw.version,\n dependencies,\n };\n}\n\n/**\n * Read and parse `apm.yml` from disk.\n */\nexport async function readApmManifest(projectRoot: string): Promise<ApmManifest> {\n const path = getApmManifestPath(projectRoot);\n const content = await readFileContent(path);\n return parseApmManifest(content);\n}\n\nfunction normalizeDependency(\n entry: string | z.infer<typeof ApmObjectDependencySchema>,\n index: number,\n): ApmDependency {\n if (typeof entry === \"string\") {\n return normalizeStringDependency(entry, index);\n }\n const gitUrl = entry.git ?? entry.source;\n if (!gitUrl) {\n throw new Error(\n `apm.yml dependency #${index + 1}: object form requires a \"git\" field. Received: ${JSON.stringify(entry)}.`,\n );\n }\n const parsedUrl = parseHttpsGitHubUrl(gitUrl);\n if (!parsedUrl) {\n throw new Error(\n `apm.yml dependency #${index + 1}: unsupported git URL \"${gitUrl}\". Only HTTPS GitHub URLs (https://github.com/owner/repo[.git]) are supported in this version. SSH, GitLab, Bitbucket, and other hosts are not yet supported.`,\n );\n }\n if (entry.path !== undefined) {\n validateSubPath(entry.path, index);\n }\n return {\n gitUrl: parsedUrl.gitUrl,\n owner: parsedUrl.owner,\n repo: parsedUrl.repo,\n ref: entry.ref,\n path: entry.path,\n alias: entry.alias,\n };\n}\n\n/**\n * Reject `dep.path` values that could escape the repository root or be\n * interpreted as an absolute path on the remote tree.\n */\nfunction validateSubPath(subPath: string, index: number): void {\n if (subPath === \"\" || subPath.startsWith(\"/\") || subPath.startsWith(\"\\\\\")) {\n throw new Error(\n `apm.yml dependency #${index + 1}: \"path\" must be a non-empty relative path without a leading slash. Received: ${JSON.stringify(subPath)}.`,\n );\n }\n const segments = subPath.split(/[/\\\\]/);\n if (segments.includes(\"..\")) {\n throw new Error(\n `apm.yml dependency #${index + 1}: \"path\" must not contain \"..\" segments. Received: ${JSON.stringify(subPath)}.`,\n );\n }\n}\n\nfunction normalizeStringDependency(entry: string, index: number): ApmDependency {\n const trimmed = entry.trim();\n if (!trimmed) {\n throw new Error(`apm.yml dependency #${index + 1}: entry must be a non-empty string.`);\n }\n rejectUnsupportedShorthand(trimmed, index);\n\n if (trimmed.startsWith(\"https://\")) {\n const [urlPart, refPart] = splitOnFirst(trimmed, \"#\");\n const parsed = parseHttpsGitHubUrl(urlPart);\n if (!parsed) {\n throw new Error(\n `apm.yml dependency #${index + 1}: unsupported URL \"${urlPart}\". Only HTTPS GitHub URLs (https://github.com/owner/repo[.git]) are supported in this version.`,\n );\n }\n return {\n gitUrl: parsed.gitUrl,\n owner: parsed.owner,\n repo: parsed.repo,\n ref: refPart || undefined,\n };\n }\n\n const [ownerRepo, refPart] = splitOnFirst(trimmed, \"#\");\n const slashIndex = ownerRepo.indexOf(\"/\");\n if (slashIndex === -1 || slashIndex === 0 || slashIndex === ownerRepo.length - 1) {\n throw new Error(\n `apm.yml dependency #${index + 1}: shorthand \"${entry}\" must be in the form \"owner/repo[#ref]\".`,\n );\n }\n if (ownerRepo.includes(\"/\", slashIndex + 1)) {\n throw new Error(\n `apm.yml dependency #${index + 1}: FQDN shorthand or sub-path shorthand (\"${entry}\") is not yet supported. Use the object form with an explicit \"git\" URL.`,\n );\n }\n // Canonicalize owner/repo to lower-case for case-insensitive matching.\n const owner = ownerRepo.substring(0, slashIndex).toLowerCase();\n const repo = ownerRepo.substring(slashIndex + 1).toLowerCase();\n return {\n gitUrl: `https://github.com/${owner}/${repo}.git`,\n owner,\n repo,\n ref: refPart || undefined,\n };\n}\n\nfunction rejectUnsupportedShorthand(entry: string, index: number): void {\n if (entry.startsWith(\"./\") || entry.startsWith(\"../\") || entry.startsWith(\"/\")) {\n throw new Error(\n `apm.yml dependency #${index + 1}: local path dependencies (\"${entry}\") are not yet supported by rulesync.`,\n );\n }\n if (entry.startsWith(\"git@\") || entry.startsWith(\"ssh://\")) {\n throw new Error(\n `apm.yml dependency #${index + 1}: SSH URL dependencies (\"${entry}\") are not yet supported. Use an HTTPS GitHub URL.`,\n );\n }\n if (entry.includes(\"@marketplace\")) {\n throw new Error(\n `apm.yml dependency #${index + 1}: APM marketplace dependencies (\"${entry}\") are not yet supported.`,\n );\n }\n}\n\nfunction parseHttpsGitHubUrl(url: string): { gitUrl: string; owner: string; repo: string } | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n const host = parsed.hostname.toLowerCase();\n if (host !== \"github.com\" && host !== \"www.github.com\") {\n return null;\n }\n const segments = parsed.pathname.split(\"/\").filter(Boolean);\n if (segments.length < 2) {\n return null;\n }\n const rawOwner = segments[0];\n const rawRepo = segments[1];\n if (!rawOwner || !rawRepo) {\n return null;\n }\n // GitHub treats owner/repo names case-insensitively for routing. Canonicalize\n // to lower-case so that lockfile comparisons and frozen-mode checks are not\n // tripped up by a user re-casing their manifest.\n const owner = rawOwner.toLowerCase();\n const repo = rawRepo.replace(/\\.git$/, \"\").toLowerCase();\n return {\n gitUrl: `https://github.com/${owner}/${repo}.git`,\n owner,\n repo,\n };\n}\n\nfunction splitOnFirst(input: string, separator: string): [string, string | undefined] {\n const idx = input.indexOf(separator);\n if (idx === -1) return [input, undefined];\n return [input.substring(0, idx), input.substring(idx + 1)];\n}\n","import { createHash } from \"node:crypto\";\nimport { join, posix } from \"node:path\";\n\nimport { Semaphore } from \"es-toolkit/promise\";\n\nimport { FETCH_CONCURRENCY_LIMIT, MAX_FILE_SIZE } from \"../../constants/rulesync-paths.js\";\nimport type { GitHubFileEntry } from \"../../types/fetch.js\";\nimport { formatError } from \"../../utils/error.js\";\nimport { checkPathTraversal, removeFile, toPosixPath, writeFileContent } from \"../../utils/file.js\";\nimport type { Logger } from \"../../utils/logger.js\";\nimport { GitHubClient, GitHubClientError, logGitHubAuthHints } from \"../github-client.js\";\nimport { listDirectoryRecursive, withSemaphore } from \"../github-utils.js\";\nimport {\n type ApmLock,\n type ApmLockDependency,\n createEmptyApmLock,\n findApmLockDependency,\n readApmLock,\n RULESYNC_CONTENT_HASH_REGEX,\n writeApmLock,\n} from \"./apm-lock.js\";\nimport { type ApmDependency, readApmManifest } from \"./apm-manifest.js\";\n\n/** APM compatibility marker written into `apm_version` when rulesync writes a lockfile. */\nconst RULESYNC_APM_COMPAT_VERSION = \"rulesync-compat/0.1\";\n\n/**\n * Primitives the first iteration deploys. Ordered by scan priority.\n * Each entry maps a package-relative source directory (rooted at the\n * dependency's `path` if given, else the repo root) to the on-disk\n * deployment directory. This matches the default APM layout when the\n * github-copilot host is present.\n */\nconst APM_PRIMITIVES: Array<{ sourceDir: string; deployDir: string; packageType: string }> = [\n {\n sourceDir: \".apm/instructions\",\n deployDir: \".github/instructions\",\n packageType: \"apm_package\",\n },\n {\n sourceDir: \".apm/skills\",\n deployDir: \".github/skills\",\n packageType: \"apm_package\",\n },\n];\n\nexport type ApmInstallOptions = {\n /** Force re-resolve all refs, ignoring the lockfile. */\n update?: boolean;\n /** Fail if the lockfile is missing or out of sync (for CI). */\n frozen?: boolean;\n /** GitHub token for private repositories. */\n token?: string;\n};\n\nexport type ApmInstallResult = {\n dependenciesProcessed: number;\n deployedFileCount: number;\n failedDependencyCount: number;\n};\n\n/**\n * Entry point for `rulesync install --mode apm`. Reads `apm.yml`, resolves\n * every declared APM dependency, fetches the subset of primitives rulesync\n * currently understands (Instructions and Skills), and updates `rulesync-apm.lock.yaml`.\n */\nexport async function installApm(params: {\n projectRoot: string;\n options?: ApmInstallOptions;\n logger: Logger;\n}): Promise<ApmInstallResult> {\n const { projectRoot, options = {}, logger } = params;\n\n const manifest = await readApmManifest(projectRoot);\n if (manifest.dependencies.length === 0) {\n logger.warn(\"apm.yml has no dependencies.apm entries. Nothing to install.\");\n return { dependenciesProcessed: 0, deployedFileCount: 0, failedDependencyCount: 0 };\n }\n\n const existingLock = await readApmLock(projectRoot);\n if (options.frozen) {\n assertFrozenLockCoversManifest({ existingLock, dependencies: manifest.dependencies });\n }\n\n const token = GitHubClient.resolveToken(options.token);\n const client = new GitHubClient({ token });\n const semaphore = new Semaphore(FETCH_CONCURRENCY_LIMIT);\n\n const newLock: ApmLock = createEmptyApmLock({\n apmVersion: existingLock?.apm_version ?? RULESYNC_APM_COMPAT_VERSION,\n existingLock,\n });\n\n // Dependencies are independent, so install them in parallel. The within-dep\n // tree walk is already rate-limited by the shared FETCH_CONCURRENCY_LIMIT\n // semaphore, so top-level parallelism is bounded naturally.\n //\n // Semantics:\n // frozen=true — any failure aborts the whole install (Promise.all\n // rejects on the first rejection).\n // frozen=false — each dep's promise resolves to a result object so that\n // one failing dep does not abort the others.\n type DepResult =\n | { status: \"ok\"; lockEntry: ApmLockDependency; deployedCount: number }\n | { status: \"failed\"; previous: ApmLockDependency | undefined };\n\n const frozen = options.frozen ?? false;\n\n const runOne = async (dep: ApmDependency): Promise<DepResult> => {\n const installed = await installDependency({\n dep,\n client,\n semaphore,\n projectRoot,\n existingLock,\n frozen,\n update: options.update ?? false,\n logger,\n });\n return {\n status: \"ok\",\n lockEntry: installed.lockEntry,\n deployedCount: installed.deployedFiles.length,\n };\n };\n\n const results: DepResult[] = frozen\n ? await Promise.all(manifest.dependencies.map(runOne))\n : await Promise.all(\n manifest.dependencies.map(async (dep): Promise<DepResult> => {\n try {\n return await runOne(dep);\n } catch (error) {\n logger.error(`Failed to install apm dependency \"${dep.gitUrl}\": ${formatError(error)}`);\n if (error instanceof GitHubClientError) {\n logGitHubAuthHints({ error, logger });\n }\n // Preserve the prior lock entry for failed deps so that a\n // transient network error does not destroy a previously pinned\n // commit SHA. We return it rather than pushing here so that the\n // post-loop pushes preserved entries in manifest order, not in\n // promise-completion order.\n const previous = existingLock\n ? findApmLockDependency(existingLock, canonicalRepoUrl(dep))\n : undefined;\n return { status: \"failed\", previous };\n }\n }),\n );\n\n let totalDeployed = 0;\n let failedCount = 0;\n // Iterate in manifest order to keep the lockfile deterministic regardless\n // of promise-completion timing.\n for (const result of results) {\n if (result.status === \"ok\") {\n newLock.dependencies.push(result.lockEntry);\n totalDeployed += result.deployedCount;\n } else {\n failedCount += 1;\n if (result.previous) {\n newLock.dependencies.push(result.previous);\n }\n }\n }\n\n // Remove files that were deployed by a previous install but are no longer\n // part of any current dependency's deployed_files. Without this, stale\n // artifacts would accumulate on disk forever as upstream content changes.\n //\n // SECURITY: `deployed_files` is only schema-validated as `z.array(z.string())`,\n // so a hostile lockfile (planted in a repo and processed by CI) could try\n // to make us `removeFile(\"../../etc/passwd\")`. We defense-in-depth guard\n // each entry: (a) reject absolute paths and `..` segments by shape, then\n // (b) run `checkPathTraversal` for the canonical check used on the write\n // path. Offending entries are skipped with a warn log rather than fatal so\n // that a single bad row cannot brick the install.\n if (existingLock) {\n await removeStaleApmFiles({ existingLock, newLock, projectRoot, logger });\n }\n\n // Always rewrite the lockfile (except under --frozen, which is a verify-only\n // mode). Even on a partially successful install we persist the union of\n // newly pinned entries and preserved previous entries so that first-ever\n // runs with mixed results still record the successful pins.\n if (!frozen) {\n newLock.generated_at = new Date().toISOString();\n await writeApmLock({ projectRoot, lock: newLock });\n if (failedCount === 0) {\n logger.debug(\"rulesync-apm.lock.yaml updated.\");\n } else {\n logger.warn(\n `rulesync-apm.lock.yaml written with partially successful installs (${failedCount} dep(s) failed).`,\n );\n }\n }\n\n return {\n dependenciesProcessed: manifest.dependencies.length,\n deployedFileCount: totalDeployed,\n failedDependencyCount: failedCount,\n };\n}\n\n/**\n * Frozen-mode validation: the lockfile must exist, cover every manifest\n * dependency, and not have drifted from any declared `ref`. Throws with\n * remediation guidance on the first failing check (preserving the original\n * order: missing-lock, missing-entries, then ref drift).\n */\nfunction assertFrozenLockCoversManifest(params: {\n existingLock: ApmLock | null;\n dependencies: ApmDependency[];\n}): asserts params is { existingLock: ApmLock; dependencies: ApmDependency[] } {\n const { existingLock, dependencies } = params;\n if (!existingLock) {\n throw new Error(\n \"Frozen install failed: rulesync-apm.lock.yaml is missing. Run 'rulesync install --mode apm' to create it.\",\n );\n }\n const missing = dependencies.filter(\n (dep) => !findApmLockDependency(existingLock, canonicalRepoUrl(dep)),\n );\n if (missing.length > 0) {\n const names = missing.map((d) => d.gitUrl).join(\", \");\n throw new Error(\n `Frozen install failed: rulesync-apm.lock.yaml is missing entries for: ${names}. Run 'rulesync install --mode apm' to update the lockfile.`,\n );\n }\n // Detect manifest drift: when the user edited `ref` in apm.yml without\n // re-running install, the locked ref no longer matches the declared one.\n // In frozen mode we refuse rather than silently install the locked SHA.\n const drifted = dependencies.filter((dep) => {\n if (dep.ref === undefined) return false;\n const locked = findApmLockDependency(existingLock, canonicalRepoUrl(dep));\n return locked?.resolved_ref !== undefined && locked.resolved_ref !== dep.ref;\n });\n if (drifted.length > 0) {\n const names = drifted\n .map((d) => {\n const locked = findApmLockDependency(existingLock, canonicalRepoUrl(d));\n return `${d.gitUrl} (manifest=${d.ref}, lock=${locked?.resolved_ref})`;\n })\n .join(\", \");\n throw new Error(\n `Frozen install failed: manifest ref does not match rulesync-apm.lock.yaml for: ${names}. Run 'rulesync install --mode apm' to update the lockfile.`,\n );\n }\n}\n\n/**\n * Remove files that a previous install deployed but that are no longer part of\n * any current dependency's `deployed_files`. Each entry is path-traversal\n * hardened (shape check + `checkPathTraversal`) and offending rows are skipped\n * with a warn log rather than fatal.\n */\nasync function removeStaleApmFiles(params: {\n existingLock: ApmLock;\n newLock: ApmLock;\n projectRoot: string;\n logger: Logger;\n}): Promise<void> {\n const { existingLock, newLock, projectRoot, logger } = params;\n const newDeployedFiles = new Set(newLock.dependencies.flatMap((d) => d.deployed_files));\n const toDelete: string[] = [];\n for (const prev of existingLock.dependencies) {\n for (const deployed of prev.deployed_files) {\n if (!newDeployedFiles.has(deployed)) {\n toDelete.push(deployed);\n }\n }\n }\n for (const relativePath of toDelete) {\n if (posix.isAbsolute(relativePath) || relativePath.split(/[/\\\\]/).includes(\"..\")) {\n logger.warn(`Refusing to remove stale apm file with suspicious path: \"${relativePath}\".`);\n continue;\n }\n try {\n checkPathTraversal({ relativePath, intendedRootDir: projectRoot });\n } catch {\n logger.warn(`Refusing to remove stale apm file outside projectRoot: \"${relativePath}\".`);\n continue;\n }\n const absolute = join(projectRoot, relativePath);\n // `removeFile` is best-effort and swallows ENOENT, so missing files are\n // a no-op. This keeps a corrupted partial-install from blowing up here.\n await removeFile(absolute);\n logger.debug(`Removed stale apm file: ${relativePath}`);\n }\n}\n\nasync function installDependency(params: {\n dep: ApmDependency;\n client: GitHubClient;\n semaphore: Semaphore;\n projectRoot: string;\n existingLock: ApmLock | null;\n frozen: boolean;\n update: boolean;\n logger: Logger;\n}): Promise<{ lockEntry: ApmLockDependency; deployedFiles: string[] }> {\n const { dep, client, semaphore, projectRoot, existingLock, frozen, update, logger } = params;\n const repoUrl = canonicalRepoUrl(dep);\n const locked = existingLock ? findApmLockDependency(existingLock, repoUrl) : undefined;\n\n let resolvedRef: string;\n let resolvedSha: string;\n if (locked && !update && locked.resolved_commit && locked.resolved_ref) {\n resolvedRef = locked.resolved_ref;\n resolvedSha = locked.resolved_commit;\n logger.debug(`Using locked commit for ${repoUrl}: ${resolvedSha}`);\n } else {\n resolvedRef = dep.ref ?? (await client.getDefaultBranch(dep.owner, dep.repo));\n resolvedSha = await client.resolveRefToSha(dep.owner, dep.repo, resolvedRef);\n logger.debug(`Resolved ${repoUrl} ref \"${resolvedRef}\" -> ${resolvedSha}`);\n }\n\n // Collect (path, content) pairs before writing to disk. This lets us hash\n // them up-front and, under --frozen, refuse to overwrite good files with\n // tampered bytes. Under non-frozen we still write as we go for incremental\n // progress feedback on large dep trees.\n const deployed: Array<{ path: string; content: string }> = [];\n for (const primitive of APM_PRIMITIVES) {\n const remoteBase = dep.path\n ? toPosixPath(posix.join(dep.path, primitive.sourceDir))\n : primitive.sourceDir;\n const files = await listPrimitiveFiles({\n client,\n semaphore,\n owner: dep.owner,\n repo: dep.repo,\n ref: resolvedSha,\n remoteBase,\n logger,\n });\n if (files.length === 0) continue;\n\n await collectPrimitiveDeployments({\n dep,\n client,\n semaphore,\n projectRoot,\n primitive,\n remoteBase,\n files,\n resolvedSha,\n repoUrl,\n frozen,\n deployed,\n logger,\n });\n }\n\n deployed.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));\n const deployedFiles = deployed.map((d) => d.path);\n const contentHash = computeContentHash(deployed);\n\n assertFrozenContentHashMatches({ frozen, locked, contentHash, repoUrl, logger });\n\n // Under --frozen we deferred all writes until after the hash check passed.\n if (frozen) {\n for (const { path: deployRelative, content } of deployed) {\n await writeFileContent(join(projectRoot, deployRelative), content);\n }\n }\n\n const lockEntry: ApmLockDependency = {\n repo_url: repoUrl,\n resolved_commit: resolvedSha,\n resolved_ref: resolvedRef,\n depth: 1,\n package_type: \"apm_package\",\n content_hash: contentHash,\n deployed_files: deployedFiles,\n };\n if (dep.path) {\n lockEntry.virtual_path = dep.path;\n }\n\n logger.info(`Installed ${deployedFiles.length} file(s) from ${repoUrl}@${shortSha(resolvedSha)}`);\n\n return { lockEntry, deployedFiles };\n}\n\n/**\n * Fetch and validate the files for a single primitive directory, appending the\n * deployable (path, content) pairs to `deployed`. Oversized or out-of-bounds\n * files are skipped with a warn log; under non-frozen mode bytes are written to\n * disk as they are collected.\n */\nasync function collectPrimitiveDeployments(params: {\n dep: ApmDependency;\n client: GitHubClient;\n semaphore: Semaphore;\n projectRoot: string;\n primitive: (typeof APM_PRIMITIVES)[number];\n remoteBase: string;\n files: GitHubFileEntry[];\n resolvedSha: string;\n repoUrl: string;\n frozen: boolean;\n deployed: Array<{ path: string; content: string }>;\n logger: Logger;\n}): Promise<void> {\n const {\n dep,\n client,\n semaphore,\n projectRoot,\n primitive,\n remoteBase,\n files,\n resolvedSha,\n repoUrl,\n frozen,\n deployed,\n logger,\n } = params;\n\n for (const file of files) {\n if (file.size > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping \"${file.path}\" from ${repoUrl}: ${(file.size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit.`,\n );\n continue;\n }\n const relativeToBase = posix.relative(remoteBase, toPosixPath(file.path));\n if (!relativeToBase || relativeToBase.startsWith(\"..\") || posix.isAbsolute(relativeToBase)) {\n logger.warn(`Skipping \"${file.path}\" from ${repoUrl}: resolved outside of \"${remoteBase}\".`);\n continue;\n }\n const deployRelative = toPosixPath(join(primitive.deployDir, relativeToBase));\n checkPathTraversal({\n relativePath: deployRelative,\n intendedRootDir: projectRoot,\n });\n const content = await withSemaphore(semaphore, () =>\n client.getFileContent(dep.owner, dep.repo, file.path, resolvedSha),\n );\n // The tree-listing size can lie (LFS pointers, filter-driver output),\n // so enforce the cap on the fetched bytes as well.\n const byteLength = Buffer.byteLength(content, \"utf8\");\n if (byteLength > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping \"${file.path}\" from ${repoUrl}: fetched ${(byteLength / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit.`,\n );\n continue;\n }\n deployed.push({ path: deployRelative, content });\n if (!frozen) {\n await writeFileContent(join(projectRoot, deployRelative), content);\n }\n }\n}\n\n/**\n * Verify integrity against the lockfile when running frozen and the prior\n * lock recorded a hash rulesync itself wrote. A mismatch means either the\n * upstream content moved under the same SHA (unlikely with git) or someone\n * tampered with the lockfile / deployed files. We do this *before* writing\n * anything to disk under --frozen so that tampered bytes never hit the\n * filesystem.\n *\n * If the recorded hash does not match the rulesync format (e.g. the\n * lockfile was produced by the upstream `apm` CLI which writes a different\n * shape), we skip the integrity check rather than fail — the commit SHA\n * pin is still enforced, and this preserves interop for users migrating\n * from `apm` to `rulesync install --mode apm`.\n */\nfunction assertFrozenContentHashMatches(params: {\n frozen: boolean;\n locked: ApmLockDependency | undefined;\n contentHash: string;\n repoUrl: string;\n logger: Logger;\n}): void {\n const { frozen, locked, contentHash, repoUrl, logger } = params;\n if (frozen && locked?.content_hash) {\n if (RULESYNC_CONTENT_HASH_REGEX.test(locked.content_hash)) {\n if (locked.content_hash !== contentHash) {\n throw new Error(\n `content_hash mismatch for ${repoUrl}: lock=${locked.content_hash} computed=${contentHash}. Refuse to trust the deployment under --frozen.`,\n );\n }\n } else {\n logger.debug(\n `Skipping content_hash integrity check for ${repoUrl}: recorded hash \"${locked.content_hash}\" was not written by rulesync.`,\n );\n }\n }\n}\n\n/**\n * SHA-256 over a canonical, order-independent representation of the deployed\n * files. Written into `content_hash` so that `--frozen` installs can refuse\n * to trust tampered output.\n */\nfunction computeContentHash(files: Array<{ path: string; content: string }>): string {\n const hash = createHash(\"sha256\");\n for (const { path, content } of files) {\n hash.update(path);\n hash.update(\"\\0\");\n hash.update(content);\n hash.update(\"\\0\");\n }\n return `sha256:${hash.digest(\"hex\")}`;\n}\n\nasync function listPrimitiveFiles(params: {\n client: GitHubClient;\n semaphore: Semaphore;\n owner: string;\n repo: string;\n ref: string;\n remoteBase: string;\n logger: Logger;\n}): Promise<GitHubFileEntry[]> {\n const { client, semaphore, owner, repo, ref, remoteBase, logger } = params;\n try {\n return await listDirectoryRecursive({\n client,\n owner,\n repo,\n path: remoteBase,\n ref,\n semaphore,\n });\n } catch (error) {\n if (error instanceof GitHubClientError && error.statusCode === 404) {\n logger.debug(`No ${remoteBase}/ in ${owner}/${repo}, skipping.`);\n return [];\n }\n throw error;\n }\n}\n\n/**\n * Canonical repo_url written into the lockfile. We always use the HTTPS form\n * without a trailing `.git` so that lock files round-trip deterministically\n * regardless of whether the manifest referenced the repo with or without\n * the suffix.\n */\nfunction canonicalRepoUrl(dep: ApmDependency): string {\n return `https://github.com/${dep.owner}/${dep.repo}`;\n}\n\nfunction shortSha(sha: string): string {\n return sha.substring(0, 7);\n}\n","import { dump } from \"js-yaml\";\n\nimport { loadYaml } from \"../../utils/yaml.js\";\n\nconst FRONTMATTER_FENCE = \"---\";\n\n/**\n * Parses YAML frontmatter at the head of a SKILL.md, sets/overwrites the\n * three provenance keys (`source`, `repository`, `ref`), and re-serializes.\n *\n * If the file has no frontmatter block, a fresh one is prepended with only\n * the provenance keys + the original body. All other existing frontmatter\n * keys are preserved verbatim (the merge is a shallow object spread on the\n * loaded YAML).\n *\n * Throws `Error(\"invalid frontmatter\")` when an `---` fenced block exists\n * but its body is not a YAML object (e.g. malformed YAML, or a list/scalar\n * at the top level). Callers may choose to fall back to \"prepend fresh\" on\n * this error, but the function itself does not silently rewrite — silently\n * dropping a corrupted frontmatter could destroy user metadata.\n */\nexport function injectSourceMetadata(params: {\n content: string;\n source: string;\n repository: string;\n ref: string;\n}): string {\n const { content, source, repository, ref } = params;\n const provenance = { source, repository, ref };\n\n // Detect the opening fence in both LF (`---\\n`) and CRLF (`---\\r\\n`) forms.\n // SKILL.md files authored on Windows or by editors that preserve CRLF must\n // round-trip cleanly — without this branch the existing frontmatter would\n // be buried inside a fresh provenance block on the first install.\n let openFenceLen: number;\n if (content.startsWith(`${FRONTMATTER_FENCE}\\r\\n`)) {\n openFenceLen = 5;\n } else if (content.startsWith(`${FRONTMATTER_FENCE}\\n`)) {\n openFenceLen = 4;\n } else if (content === FRONTMATTER_FENCE) {\n openFenceLen = 3;\n } else {\n // No frontmatter block. Prepend a fresh one with just the provenance keys.\n const yaml = dump(provenance, { noRefs: true, lineWidth: -1, sortKeys: false });\n return `${FRONTMATTER_FENCE}\\n${yaml}${FRONTMATTER_FENCE}\\n${content}`;\n }\n\n // Body starts immediately after the opening fence.\n const afterOpen = content.substring(openFenceLen);\n\n // Closing fence forms we accept:\n // - `---` immediately at the start of the body (i.e. `---\\n---\\n...`,\n // which yields an empty frontmatter block).\n // - `\\n---` followed by a newline OR end-of-file (the trailing newline\n // after the closing `---` is optional so the fence may sit at EOF).\n let fmBody: string;\n let rest: string;\n if (afterOpen.startsWith(\"---\\n\") || afterOpen.startsWith(\"---\\r\\n\") || afterOpen === \"---\") {\n fmBody = \"\";\n const fenceLen = afterOpen.startsWith(\"---\\r\\n\") ? 5 : afterOpen === \"---\" ? 3 : 4;\n rest = afterOpen.substring(fenceLen);\n } else {\n const match = /\\n---(\\r?\\n|$)/.exec(afterOpen);\n if (!match) {\n // The file starts with `---\\n` but there is no closing `---` line. We\n // refuse to guess where the frontmatter ends; treat as invalid so the\n // caller can decide whether to fall back.\n throw new Error(\"invalid frontmatter\");\n }\n fmBody = afterOpen.substring(0, match.index);\n rest = afterOpen.substring(match.index + match[0].length);\n }\n\n let loaded: unknown;\n try {\n loaded = loadYaml(fmBody);\n } catch {\n throw new Error(\"invalid frontmatter\");\n }\n\n if (loaded === null || loaded === undefined) {\n // Empty frontmatter block (`---\\n---\\n...`). Use only provenance.\n const yaml = dump(provenance, { noRefs: true, lineWidth: -1, sortKeys: false });\n return `${FRONTMATTER_FENCE}\\n${yaml}${FRONTMATTER_FENCE}\\n${rest}`;\n }\n if (typeof loaded !== \"object\" || Array.isArray(loaded)) {\n throw new Error(\"invalid frontmatter\");\n }\n\n // Shallow merge: existing keys preserved, provenance keys overwritten.\n const existing = loaded as Record<string, unknown>;\n const merged: Record<string, unknown> = {\n ...existing,\n ...provenance,\n };\n const yaml = dump(merged, { noRefs: true, lineWidth: -1, sortKeys: false });\n return `${FRONTMATTER_FENCE}\\n${yaml}${FRONTMATTER_FENCE}\\n${rest}`;\n}\n","import { join } from \"node:path\";\n\nimport { dump } from \"js-yaml\";\nimport { optional, refine, z } from \"zod/mini\";\n\nimport { fileExists, readFileContent, writeFileContent } from \"../../utils/file.js\";\nimport { loadYaml } from \"../../utils/yaml.js\";\n\n/**\n * Filename of the rulesync-managed gh-skill-compatible lockfile. Distinct\n * from the rulesync sources lockfile (`rulesync.lock`) and from the\n * apm-mode lockfile (`rulesync-apm.lock.yaml`) so the three install modes\n * never fight over the same file.\n */\nconst GH_LOCKFILE_FILE_NAME = \"rulesync-gh.lock.yaml\";\nexport const GH_LOCKFILE_VERSION = \"1\" as const;\n\n/**\n * Shape of content_hash values that rulesync writes for gh installs. Same\n * format as the apm-mode hash so callers can reuse the integrity check\n * conventions; under `--frozen` only values matching this regex are\n * considered comparable.\n */\nexport const RULESYNC_CONTENT_HASH_REGEX = /^sha256:[0-9a-f]{64}$/;\n\nconst ScopeSchema = z.enum([\"project\", \"user\"]);\n\n/**\n * Single installation entry in `rulesync-gh.lock.yaml`. Each entry pins one\n * skill from one source under one (agent, scope) pair — matching the gh CLI\n * model where `gh skill install` deploys exactly one skill at a time.\n */\nconst GhLockInstallationSchema = z.looseObject({\n source: z.string(),\n owner: z.string(),\n repo: z.string(),\n agent: z.string(),\n scope: ScopeSchema,\n skill: z.string(),\n requested_ref: optional(z.string()),\n resolved_ref: z.string(),\n resolved_commit: z\n .string()\n .check(refine((v) => /^[0-9a-f]{40}$/.test(v), \"resolved_commit must be a 40-char hex SHA\")),\n install_dir: z.string(),\n deployed_files: z.array(z.string()),\n content_hash: optional(z.string()),\n});\nexport type GhLockInstallation = z.infer<typeof GhLockInstallationSchema>;\n\nconst GhLockSchema = z.looseObject({\n lockfile_version: z.literal(\"1\"),\n generated_at: z.string(),\n installations: z.array(GhLockInstallationSchema),\n});\nexport type GhLock = z.infer<typeof GhLockSchema>;\n\nexport function getGhLockPath(projectRoot: string): string {\n return join(projectRoot, GH_LOCKFILE_FILE_NAME);\n}\n\n/**\n * Create an empty gh lockfile structure. When `existingLock` is provided,\n * top-level looseObject extras are carried forward so unknown fields (added\n * by future tools or other lockfile producers) round-trip cleanly.\n */\nexport function createEmptyGhLock(params?: { existingLock?: GhLock | null }): GhLock {\n const base = params?.existingLock ? { ...params.existingLock } : {};\n return {\n ...base,\n lockfile_version: GH_LOCKFILE_VERSION,\n generated_at: new Date().toISOString(),\n installations: [],\n };\n}\n\n/**\n * Parse `rulesync-gh.lock.yaml` content into a `GhLock`. Returns `null` for\n * empty / non-YAML-object content so callers can treat the lockfile as\n * missing. A *structurally* present lockfile that fails schema validation\n * throws, rather than silently dropping previously pinned entries.\n */\nexport function parseGhLock(content: string): GhLock | null {\n if (!content.trim()) {\n return null;\n }\n let loaded: unknown;\n try {\n loaded = loadYaml(content);\n } catch {\n return null;\n }\n if (!loaded || typeof loaded !== \"object\") {\n return null;\n }\n const parsed = GhLockSchema.safeParse(loaded);\n if (!parsed.success) {\n const issues = parsed.error.issues\n .map((issue) => ` - ${issue.path.join(\".\") || \"<root>\"}: ${issue.message}`)\n .join(\"\\n\");\n throw new Error(`Invalid ${GH_LOCKFILE_FILE_NAME}:\\n${issues}`);\n }\n return parsed.data;\n}\n\nexport async function readGhLock(projectRoot: string): Promise<GhLock | null> {\n const path = getGhLockPath(projectRoot);\n if (!(await fileExists(path))) {\n return null;\n }\n const content = await readFileContent(path);\n return parseGhLock(content);\n}\n\nexport async function writeGhLock(params: { projectRoot: string; lock: GhLock }): Promise<void> {\n const path = getGhLockPath(params.projectRoot);\n const content = serializeGhLock(params.lock);\n await writeFileContent(path, content);\n}\n\nexport function serializeGhLock(lock: GhLock): string {\n // `noRefs: true` avoids YAML anchors/aliases; `lineWidth: -1` keeps long\n // URLs and sha values on a single line so the file stays diff-friendly.\n return dump(lock, { noRefs: true, lineWidth: -1, sortKeys: false });\n}\n\n/**\n * Find the locked installation for a given (source, agent, scope, skill)\n * tuple. Source is matched case-insensitively because GitHub routes\n * `owner/repo` paths case-insensitively.\n */\nexport function findGhLockInstallation(\n lock: GhLock,\n params: { source: string; agent: string; scope: \"project\" | \"user\"; skill: string },\n): GhLockInstallation | undefined {\n const target = params.source.toLowerCase();\n return lock.installations.find(\n (i) =>\n i.source.toLowerCase() === target &&\n i.agent === params.agent &&\n i.scope === params.scope &&\n i.skill === params.skill,\n );\n}\n","import { join } from \"node:path\";\n\nimport { CLAUDECODE_SKILLS_DIR_PATH } from \"../../constants/claudecode-paths.js\";\nimport { getHomeDirectory } from \"../../utils/file.js\";\n\n/**\n * Agents recognized by `--mode gh`. Mirrors the agent list documented for\n * `gh skill install`. The same skill content can be deployed under multiple\n * agent-specific directories simultaneously, one entry per `(agent, scope)`\n * pair in `rulesync.jsonc`.\n */\nexport const GH_AGENTS = [\n \"github-copilot\",\n \"claude-code\",\n \"cursor\",\n \"codex\",\n \"gemini\",\n \"antigravity\",\n] as const;\nexport type GhAgent = (typeof GH_AGENTS)[number];\n\nexport type GhScope = \"project\" | \"user\";\n\n/**\n * Resolve the absolute install directory for a given agent + scope, matching\n * the layout expected by `gh skill install`.\n *\n * Project scope writes inside `projectRoot`. The `github-copilot` agent uses the\n * shared `.agents/skills` directory (the host-agnostic project layout); other\n * agents that share that location (cursor, codex, gemini, antigravity) write\n * to `.agents/skills` for project scope and to their own `.<tool>/skills`\n * directory for user scope. Claude Code is the exception: project and user\n * scope both use `.claude/skills`, just rooted at `projectRoot` vs the home\n * directory respectively.\n */\nexport function resolveGhInstallDir(params: {\n agent: GhAgent;\n scope: GhScope;\n projectRoot: string;\n}): string {\n const { agent, scope, projectRoot } = params;\n const home = scope === \"user\" ? getHomeDirectory() : projectRoot;\n const relative = relativeInstallDirFor({ agent, scope });\n return join(home, relative);\n}\n\n/**\n * Returns the install directory relative to its scope root (projectRoot for\n * project scope, home for user scope). Exposed separately so the lockfile can\n * record the same canonical relative path it deploys to.\n */\nexport function relativeInstallDirFor(params: { agent: GhAgent; scope: GhScope }): string {\n const { agent, scope } = params;\n if (scope === \"project\") {\n if (agent === \"claude-code\") {\n return CLAUDECODE_SKILLS_DIR_PATH;\n }\n // github-copilot and the rest share the shared project layout.\n return join(\".agents\", \"skills\");\n }\n // user scope\n switch (agent) {\n case \"github-copilot\":\n return join(\".copilot\", \"skills\");\n case \"claude-code\":\n return CLAUDECODE_SKILLS_DIR_PATH;\n case \"cursor\":\n return join(\".cursor\", \"skills\");\n case \"codex\":\n return join(\".agents\", \"skills\");\n case \"gemini\":\n return join(\".gemini\", \"skills\");\n case \"antigravity\":\n return join(\".gemini\", \"antigravity\", \"skills\");\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { basename, join, posix } from \"node:path\";\n\nimport { Semaphore } from \"es-toolkit/promise\";\n\nimport type { SourceEntry } from \"../../config/config.js\";\nimport { FETCH_CONCURRENCY_LIMIT, MAX_FILE_SIZE } from \"../../constants/rulesync-paths.js\";\nimport { formatError } from \"../../utils/error.js\";\nimport {\n checkPathTraversal,\n getHomeDirectory,\n removeFile,\n toPosixPath,\n writeFileContent,\n} from \"../../utils/file.js\";\nimport type { Logger } from \"../../utils/logger.js\";\nimport { GitHubClient, GitHubClientError, logGitHubAuthHints } from \"../github-client.js\";\nimport { listDirectoryRecursive, withSemaphore } from \"../github-utils.js\";\nimport { parseSource } from \"../source-parser.js\";\nimport { injectSourceMetadata } from \"./gh-frontmatter.js\";\nimport {\n createEmptyGhLock,\n findGhLockInstallation,\n type GhLock,\n type GhLockInstallation,\n readGhLock,\n RULESYNC_CONTENT_HASH_REGEX,\n writeGhLock,\n} from \"./gh-lock.js\";\nimport { type GhAgent, GH_AGENTS, type GhScope, relativeInstallDirFor } from \"./gh-paths.js\";\n\nconst SKILLS_REMOTE_DIR = \"skills\";\nconst SKILL_FILE_NAME = \"SKILL.md\";\n\nexport type GhInstallOptions = {\n /** Force re-resolve all refs, ignoring the lockfile. */\n update?: boolean;\n /** Fail if the lockfile is missing or out of sync (for CI). */\n frozen?: boolean;\n /** GitHub token for private repositories. */\n token?: string;\n};\n\nexport type GhInstallResult = {\n sourcesProcessed: number;\n installedSkillCount: number;\n failedSourceCount: number;\n};\n\ntype ResolvedSource = {\n entry: SourceEntry;\n owner: string;\n repo: string;\n ref?: string;\n agent: GhAgent;\n scope: GhScope;\n};\n\ntype DeployedFile = {\n /** Relative POSIX path under the install dir's scope root. Recorded in the lockfile. */\n relativeToScopeRoot: string;\n /** Absolute on-disk path where the bytes are written. */\n absolutePath: string;\n content: string;\n};\n\ntype SkillInstallation = {\n installation: GhLockInstallation;\n deployed: DeployedFile[];\n};\n\ntype SourceResult =\n | { status: \"ok\"; installations: SkillInstallation[] }\n | { status: \"failed\"; preserved: GhLockInstallation[] };\n\n/**\n * Entry point for `rulesync install --mode gh`. Reads `sources` from\n * `rulesync.jsonc`, resolves each one against the GitHub API, and deploys\n * each discovered `skills/<name>/` tree under the agent-specific install\n * directory recorded by `resolveGhInstallDir`. Updates `rulesync-gh.lock.yaml`\n * to pin commits and per-skill content hashes.\n */\nexport async function installGh(params: {\n projectRoot: string;\n sources: SourceEntry[];\n options?: GhInstallOptions;\n logger: Logger;\n}): Promise<GhInstallResult> {\n const { projectRoot, sources, options = {}, logger } = params;\n\n if (sources.length === 0) {\n return { sourcesProcessed: 0, installedSkillCount: 0, failedSourceCount: 0 };\n }\n\n // Pre-resolve every source's owner/repo + agent/scope defaults so the\n // frozen-mode coverage check below has a stable view of what installations\n // are required. We do not contact the API yet — that happens per-source.\n const resolvedSources: ResolvedSource[] = sources.map(resolveGhSource);\n\n const existingLock = await readGhLock(projectRoot);\n const frozen = options.frozen ?? false;\n const update = options.update ?? false;\n\n if (frozen && !existingLock) {\n throw new Error(\n \"Frozen install failed: rulesync-gh.lock.yaml is missing. Run 'rulesync install --mode gh' to create it.\",\n );\n }\n\n if (frozen && existingLock) {\n assertFrozenLockCoversSources({ existingLock, resolvedSources });\n }\n\n const token = GitHubClient.resolveToken(options.token);\n const client = new GitHubClient({ token });\n const semaphore = new Semaphore(FETCH_CONCURRENCY_LIMIT);\n\n const newLock: GhLock = createEmptyGhLock({ existingLock });\n\n const runOne = async (rs: ResolvedSource): Promise<SourceResult> => {\n const installations = await installSource({\n rs,\n client,\n semaphore,\n projectRoot,\n existingLock,\n frozen,\n update,\n logger,\n });\n return { status: \"ok\", installations };\n };\n\n const results: SourceResult[] = frozen\n ? await Promise.all(resolvedSources.map(runOne))\n : await Promise.all(\n resolvedSources.map(async (rs): Promise<SourceResult> => {\n try {\n return await runOne(rs);\n } catch (error) {\n logger.error(`Failed to install gh source \"${rs.entry.source}\": ${formatError(error)}`);\n if (error instanceof GitHubClientError) {\n logGitHubAuthHints({ error, logger });\n }\n // Preserve all prior installations for this source so that a\n // transient error does not erase previously pinned commit SHAs.\n const preserved = existingLock\n ? existingLock.installations.filter(\n (i) => i.source.toLowerCase() === rs.entry.source.toLowerCase(),\n )\n : [];\n return { status: \"failed\", preserved };\n }\n }),\n );\n\n if (frozen) {\n await writeDeferredFrozenFiles(results);\n }\n\n const { totalInstalled, failedCount } = aggregateSourceResults({ results, newLock });\n\n // Stale-file cleanup. Same hardening shape as apm-install.\n if (existingLock) {\n await removeStaleGhFiles({ existingLock, newLock, projectRoot, logger });\n }\n\n if (!frozen) {\n newLock.generated_at = new Date().toISOString();\n await writeGhLock({ projectRoot, lock: newLock });\n if (failedCount === 0) {\n logger.debug(\"rulesync-gh.lock.yaml updated.\");\n } else {\n logger.warn(\n `rulesync-gh.lock.yaml written with partially successful installs (${failedCount} source(s) failed).`,\n );\n }\n }\n\n return {\n sourcesProcessed: sources.length,\n installedSkillCount: totalInstalled,\n failedSourceCount: failedCount,\n };\n}\n\n/**\n * Validate and normalize a single declared source into a ResolvedSource without\n * contacting the API. Rejects non-GitHub providers and the gh-unsupported\n * rulesync-mode-only fields, and applies the agent/scope defaults.\n */\nfunction resolveGhSource(entry: SourceEntry): ResolvedSource {\n const parsed = parseSource(entry.source);\n if (parsed.provider !== \"github\") {\n throw new Error(\n `--mode gh only supports GitHub sources. \"${entry.source}\" resolves to provider \"${parsed.provider}\".`,\n );\n }\n // gh mode does not honor these rulesync-mode-only SourceEntry fields.\n // Silently dropping them would\n // surprise users migrating from --mode rulesync, so reject up-front\n // with a message that names the offending field.\n if (entry.transport !== undefined && entry.transport !== \"github\") {\n throw new Error(\n `--mode gh: field \"transport\" is not supported (got \"${entry.transport}\" for source \"${entry.source}\"). Drop the field or switch to --mode rulesync.`,\n );\n }\n if (entry.path !== undefined) {\n throw new Error(\n `--mode gh: field \"path\" is not supported for source \"${entry.source}\". The remote layout is fixed to \"skills/<name>/SKILL.md\".`,\n );\n }\n if (entry.rules !== undefined) {\n throw new Error(\n `--mode gh: field \"rules\" is not supported for source \"${entry.source}\". Switch to --mode rulesync to install declarative rules.`,\n );\n }\n if (entry.rulesPath !== undefined) {\n throw new Error(\n `--mode gh: field \"rulesPath\" is not supported for source \"${entry.source}\". Switch to --mode rulesync to install declarative rules.`,\n );\n }\n const agent = entry.agent ?? \"github-copilot\";\n if (!GH_AGENTS.includes(agent)) {\n throw new Error(\n `--mode gh: unknown agent \"${agent}\" for source \"${entry.source}\". Valid agents: ${GH_AGENTS.join(\", \")}.`,\n );\n }\n const scope: GhScope = entry.scope ?? \"project\";\n return {\n entry,\n owner: parsed.owner,\n repo: parsed.repo,\n ref: entry.ref ?? parsed.ref,\n agent,\n scope,\n };\n}\n\n/**\n * Frozen mode: per-source coverage check plus `ref` drift detection. A\n * brand-new source (no installations at all in the lock) must fail before we\n * contact the GitHub API — both to save quota and to prevent in-flight\n * Promise.all siblings from writing files when another source is going to\n * throw. Per-skill coverage is enforced lazily inside installSource, since that\n * requires API discovery to know which skills exist remotely.\n */\nfunction assertFrozenLockCoversSources(params: {\n existingLock: GhLock;\n resolvedSources: ResolvedSource[];\n}): void {\n const { existingLock, resolvedSources } = params;\n const uncovered: string[] = [];\n for (const rs of resolvedSources) {\n const hasAny = existingLock.installations.some(\n (i) =>\n i.source.toLowerCase() === rs.entry.source.toLowerCase() &&\n i.agent === rs.agent &&\n i.scope === rs.scope,\n );\n if (!hasAny) {\n uncovered.push(`${rs.entry.source} (agent=${rs.agent}, scope=${rs.scope})`);\n }\n }\n if (uncovered.length > 0) {\n throw new Error(\n `Frozen install failed: rulesync-gh.lock.yaml is missing entries for: ${uncovered.join(\", \")}. Run 'rulesync install --mode gh' to update the lockfile.`,\n );\n }\n\n // Detect manifest drift on `ref`: when the user edited `ref` in\n // rulesync.jsonc without re-running install, refuse rather than\n // silently install the locked SHA against a different declared ref.\n const drifted: string[] = [];\n for (const rs of resolvedSources) {\n if (!rs.ref) continue;\n const matches = existingLock.installations.filter(\n (i) => i.source.toLowerCase() === rs.entry.source.toLowerCase(),\n );\n for (const m of matches) {\n if (m.requested_ref !== undefined && m.requested_ref !== rs.ref) {\n drifted.push(`${rs.entry.source} (manifest=${rs.ref}, lock=${m.requested_ref})`);\n break;\n }\n }\n }\n if (drifted.length > 0) {\n throw new Error(\n `Frozen install failed: manifest ref does not match rulesync-gh.lock.yaml for: ${drifted.join(\", \")}. Run 'rulesync install --mode gh' to update the lockfile.`,\n );\n }\n}\n\n/**\n * Frozen-mode deferred writes. `installSource` never touches the disk under\n * --frozen — every write lands here, only after Promise.all has resolved\n * successfully for every source. Without this gate, source A could finish\n * writing its bytes before source B's coverage / integrity check throws,\n * leaving the working tree in a partially-frozen state despite the install\n * reporting failure.\n */\nasync function writeDeferredFrozenFiles(results: SourceResult[]): Promise<void> {\n for (const result of results) {\n if (result.status !== \"ok\") continue;\n for (const inst of result.installations) {\n for (const d of inst.deployed) {\n await writeFileContent(d.absolutePath, d.content);\n }\n }\n }\n}\n\n/**\n * Push each source result's installations (or preserved prior entries on\n * failure) into the new lock, returning the installed and failed counts.\n */\nfunction aggregateSourceResults(params: { results: SourceResult[]; newLock: GhLock }): {\n totalInstalled: number;\n failedCount: number;\n} {\n const { results, newLock } = params;\n let totalInstalled = 0;\n let failedCount = 0;\n for (const result of results) {\n if (result.status === \"ok\") {\n for (const inst of result.installations) {\n newLock.installations.push(inst.installation);\n }\n totalInstalled += result.installations.length;\n } else {\n failedCount += 1;\n for (const preserved of result.preserved) {\n newLock.installations.push(preserved);\n }\n }\n }\n return { totalInstalled, failedCount };\n}\n\n/**\n * Remove files deployed by a previous install that are no longer part of any\n * current installation, keyed by (scope, path) so identically-named files under\n * different scope roots are not conflated.\n */\nasync function removeStaleGhFiles(params: {\n existingLock: GhLock;\n newLock: GhLock;\n projectRoot: string;\n logger: Logger;\n}): Promise<void> {\n const { existingLock, newLock, projectRoot, logger } = params;\n const newDeployed = new Set<string>();\n for (const inst of newLock.installations) {\n for (const file of inst.deployed_files) {\n // Key by (scope, path) so a file in `<home>/.claude/skills/foo` and a\n // file at `<base>/.claude/skills/foo` are not conflated.\n newDeployed.add(`${inst.scope}::${file}`);\n }\n }\n for (const prev of existingLock.installations) {\n for (const deployed of prev.deployed_files) {\n const key = `${prev.scope}::${deployed}`;\n if (newDeployed.has(key)) continue;\n await removeStaleFile({\n relativePath: deployed,\n scope: prev.scope === \"user\" ? \"user\" : \"project\",\n projectRoot,\n logger,\n });\n }\n }\n}\n\nasync function installSource(params: {\n rs: ResolvedSource;\n client: GitHubClient;\n semaphore: Semaphore;\n projectRoot: string;\n existingLock: GhLock | null;\n frozen: boolean;\n update: boolean;\n logger: Logger;\n}): Promise<SkillInstallation[]> {\n const { rs, client, semaphore, projectRoot, existingLock, frozen, update, logger } = params;\n const { entry, owner, repo, agent, scope } = rs;\n const sourceKey = entry.source;\n\n const { resolvedRef, resolvedSha, usedTag } = await resolveGhRef({\n rs,\n client,\n owner,\n repo,\n sourceKey,\n logger,\n });\n\n // Discover skills under `skills/`.\n const validatedSkills = await discoverValidatedSkills({\n client,\n semaphore,\n owner,\n repo,\n resolvedSha,\n sourceKey,\n logger,\n });\n if (validatedSkills === null) {\n return [];\n }\n\n // Apply the explicit skill filter when provided.\n const selected = selectSkills({ validatedSkills, entry, sourceKey, logger });\n\n // Frozen-mode coverage check (per-skill). Only enforceable now that we know\n // the requested skill set.\n if (frozen && existingLock) {\n assertFrozenSkillCoverage({ selected, existingLock, sourceKey, agent, scope });\n }\n\n const results: SkillInstallation[] = [];\n const installRelDir = relativeInstallDirFor({ agent, scope });\n const scopeRoot = scope === \"user\" ? getHomeDirectory() : projectRoot;\n\n // Source URL recorded in injected frontmatter. Mirrors the canonical form\n // used by `gh skill install`.\n const sourceUrl = `https://github.com/${owner}/${repo}`;\n const repository = `${owner}/${repo}`;\n // gh records the *resolved* ref (the tag name when one was used, else the\n // commit SHA) into the SKILL.md frontmatter so the deployed file has a\n // human-readable provenance hint.\n const provenanceRef = usedTag ? resolvedRef : resolvedSha;\n\n for (const sk of selected) {\n const locked =\n existingLock && !update\n ? findGhLockInstallation(existingLock, {\n source: sourceKey,\n agent,\n scope,\n skill: sk.name,\n })\n : undefined;\n\n // Recursively list this skill's tree.\n const allFiles = await listDirectoryRecursive({\n client,\n owner,\n repo,\n path: sk.path,\n ref: resolvedSha,\n semaphore,\n });\n\n const deployed = await buildSkillDeployment({\n sk,\n allFiles,\n client,\n semaphore,\n owner,\n repo,\n resolvedSha,\n installRelDir,\n scopeRoot,\n sourceUrl,\n repository,\n provenanceRef,\n sourceKey,\n frozen,\n logger,\n });\n\n deployed.sort((a, b) =>\n a.relativeToScopeRoot < b.relativeToScopeRoot\n ? -1\n : a.relativeToScopeRoot > b.relativeToScopeRoot\n ? 1\n : 0,\n );\n const deployedFiles = deployed.map((d) => d.relativeToScopeRoot);\n const contentHash = computeContentHash(deployed);\n\n assertFrozenSkillIntegrity({\n frozen,\n locked,\n contentHash,\n sourceKey,\n skillName: sk.name,\n agent,\n scope,\n logger,\n });\n\n // Under --frozen we deliberately do NOT write here even after the\n // integrity check passes. Writes are deferred to the top-level installGh\n // so that a sibling source failing its check cannot leave partial bytes\n // on disk from a peer that already passed.\n const installation: GhLockInstallation = {\n source: sourceKey,\n owner,\n repo,\n agent,\n scope,\n skill: sk.name,\n resolved_ref: resolvedRef,\n resolved_commit: resolvedSha,\n install_dir: toPosixPath(installRelDir),\n deployed_files: deployedFiles,\n content_hash: contentHash,\n };\n if (rs.ref !== undefined) {\n installation.requested_ref = rs.ref;\n }\n results.push({ installation, deployed });\n\n logger.info(\n `Installed gh skill \"${sk.name}\" from ${sourceKey} (agent=${agent}, scope=${scope}, ref=${resolvedRef})`,\n );\n }\n\n return results;\n}\n\n/**\n * Resolve the ref for a gh source. Order: explicit `entry.ref`, then the latest\n * release's tag, then the default branch (when the repo has no releases).\n * Returns the resolved ref, its commit SHA, and whether a release tag was used.\n */\nasync function resolveGhRef(params: {\n rs: ResolvedSource;\n client: GitHubClient;\n owner: string;\n repo: string;\n sourceKey: string;\n logger: Logger;\n}): Promise<{ resolvedRef: string; resolvedSha: string; usedTag: boolean }> {\n const { rs, client, owner, repo, sourceKey, logger } = params;\n let resolvedRef: string;\n let usedTag = false;\n if (rs.ref) {\n resolvedRef = rs.ref;\n } else {\n try {\n const release = await client.getLatestRelease(owner, repo);\n resolvedRef = release.tag_name;\n usedTag = true;\n } catch (error) {\n // gh's behavior: when a repo has no releases, getLatestRelease returns\n // 404. We treat any 404 (real GitHubClientError or any thrown value\n // carrying statusCode 404) as \"no releases\" and fall back to the\n // default branch. Other errors propagate.\n if (is404(error)) {\n resolvedRef = await client.getDefaultBranch(owner, repo);\n } else {\n throw error;\n }\n }\n }\n const resolvedSha = await client.resolveRefToSha(owner, repo, resolvedRef);\n logger.debug(`Resolved ${sourceKey} -> ref=${resolvedRef} sha=${resolvedSha}`);\n return { resolvedRef, resolvedSha, usedTag };\n}\n\n/**\n * List `skills/` and validate which subdirectories are actual skills (contain a\n * SKILL.md). Returns null (with a warn log) when the `skills/` directory 404s so\n * the caller can skip the source. Validation is sequential to avoid hammering\n * the API for large monorepos beyond FETCH_CONCURRENCY_LIMIT.\n */\nasync function discoverValidatedSkills(params: {\n client: GitHubClient;\n semaphore: Semaphore;\n owner: string;\n repo: string;\n resolvedSha: string;\n sourceKey: string;\n logger: Logger;\n}): Promise<Array<{ name: string; path: string }> | null> {\n const { client, semaphore, owner, repo, resolvedSha, sourceKey, logger } = params;\n let topLevel: Awaited<ReturnType<GitHubClient[\"listDirectory\"]>>;\n try {\n topLevel = await client.listDirectory(owner, repo, SKILLS_REMOTE_DIR, resolvedSha);\n } catch (error) {\n if (is404(error)) {\n logger.warn(`No skills/ directory found in ${sourceKey}. Skipping.`);\n return null;\n }\n throw error;\n }\n\n const skillDirs = topLevel\n .filter((e) => e.type === \"dir\")\n .map((e) => ({ name: e.name, path: e.path }));\n\n const validatedSkills: Array<{ name: string; path: string }> = [];\n for (const sk of skillDirs) {\n const info = await withSemaphore(semaphore, () =>\n client.getFileInfo(owner, repo, posix.join(sk.path, SKILL_FILE_NAME), resolvedSha),\n );\n if (info) {\n validatedSkills.push(sk);\n }\n }\n return validatedSkills;\n}\n\n/**\n * Apply the explicit `entry.skills` filter to the validated skills, warning for\n * each requested name that is absent upstream. Returns all validated skills when\n * no filter is provided.\n */\nfunction selectSkills(params: {\n validatedSkills: Array<{ name: string; path: string }>;\n entry: SourceEntry;\n sourceKey: string;\n logger: Logger;\n}): Array<{ name: string; path: string }> {\n const { validatedSkills, entry, sourceKey, logger } = params;\n if (!entry.skills || entry.skills.length === 0) {\n return validatedSkills;\n }\n const requested = new Set(entry.skills);\n const selected = validatedSkills.filter((s) => requested.has(s.name));\n const presentNames = new Set(validatedSkills.map((s) => s.name));\n for (const want of entry.skills) {\n if (!presentNames.has(want)) {\n logger.warn(`Requested skill \"${want}\" not found in ${sourceKey} under skills/. Skipping.`);\n }\n }\n return selected;\n}\n\n/**\n * Frozen-mode per-skill coverage check. Throws when any selected skill has no\n * matching lock installation for the (source, agent, scope) tuple.\n */\nfunction assertFrozenSkillCoverage(params: {\n selected: Array<{ name: string; path: string }>;\n existingLock: GhLock;\n sourceKey: string;\n agent: GhAgent;\n scope: GhScope;\n}): void {\n const { selected, existingLock, sourceKey, agent, scope } = params;\n const missing: string[] = [];\n for (const sk of selected) {\n const locked = findGhLockInstallation(existingLock, {\n source: sourceKey,\n agent,\n scope,\n skill: sk.name,\n });\n if (!locked) {\n missing.push(sk.name);\n }\n }\n if (missing.length > 0) {\n throw new Error(\n `Frozen install failed: rulesync-gh.lock.yaml is missing entries for ${sourceKey} (agent=${agent}, scope=${scope}) skills: ${missing.join(\", \")}. Run 'rulesync install --mode gh' to update the lockfile.`,\n );\n }\n}\n\n/**\n * Fetch, validate, and (under non-frozen) write a single skill's file tree,\n * returning the deployable files. Oversized or out-of-bounds files are skipped\n * with a warn log; SKILL.md files have provenance frontmatter injected.\n */\nasync function buildSkillDeployment(params: {\n sk: { name: string; path: string };\n allFiles: Awaited<ReturnType<typeof listDirectoryRecursive>>;\n client: GitHubClient;\n semaphore: Semaphore;\n owner: string;\n repo: string;\n resolvedSha: string;\n installRelDir: string;\n scopeRoot: string;\n sourceUrl: string;\n repository: string;\n provenanceRef: string;\n sourceKey: string;\n frozen: boolean;\n logger: Logger;\n}): Promise<DeployedFile[]> {\n const {\n sk,\n allFiles,\n client,\n semaphore,\n owner,\n repo,\n resolvedSha,\n installRelDir,\n scopeRoot,\n sourceUrl,\n repository,\n provenanceRef,\n sourceKey,\n frozen,\n logger,\n } = params;\n\n const deployed: DeployedFile[] = [];\n for (const file of allFiles) {\n if (file.size > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping \"${file.path}\" from ${sourceKey}: ${(file.size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit.`,\n );\n continue;\n }\n // Path of the file relative to the skill directory root upstream.\n const relativeToSkill = posix.relative(sk.path, toPosixPath(file.path));\n if (!relativeToSkill || relativeToSkill.startsWith(\"..\") || posix.isAbsolute(relativeToSkill)) {\n logger.warn(`Skipping \"${file.path}\" from ${sourceKey}: resolved outside of \"${sk.path}\".`);\n continue;\n }\n\n // Path under the scope root (relative). This is the value persisted to\n // the lockfile.\n const deployRelative = toPosixPath(join(installRelDir, sk.name, relativeToSkill));\n // Path-traversal hardening rooted at the scope root, then a tighter\n // check rooted at the per-(agent,scope) install dir to refuse anything\n // that escapes the agent-specific deployment directory.\n checkPathTraversal({ relativePath: deployRelative, intendedRootDir: scopeRoot });\n const installAbs = join(scopeRoot, installRelDir);\n const withinInstallDir = toPosixPath(join(sk.name, relativeToSkill));\n checkPathTraversal({ relativePath: withinInstallDir, intendedRootDir: installAbs });\n\n let content = await withSemaphore(semaphore, () =>\n client.getFileContent(owner, repo, file.path, resolvedSha),\n );\n const byteLength = Buffer.byteLength(content, \"utf8\");\n if (byteLength > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping \"${file.path}\" from ${sourceKey}: fetched ${(byteLength / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit.`,\n );\n continue;\n }\n\n // Inject provenance frontmatter into SKILL.md files. Other files\n // (e.g. supporting markdown, scripts) pass through unchanged.\n if (basename(file.path) === SKILL_FILE_NAME) {\n try {\n content = injectSourceMetadata({\n content,\n source: sourceUrl,\n repository,\n ref: provenanceRef,\n });\n } catch {\n // Frontmatter exists but is not parseable. Fall back to a fresh\n // prepend so we still record provenance — but warn the user.\n logger.warn(\n `Frontmatter in ${file.path} (${sourceKey}) is invalid. Prepending a fresh provenance block.`,\n );\n content = `---\\nsource: ${sourceUrl}\\nrepository: ${repository}\\nref: ${provenanceRef}\\n---\\n${content}`;\n }\n }\n\n const absolutePath = join(scopeRoot, deployRelative);\n deployed.push({ relativeToScopeRoot: deployRelative, absolutePath, content });\n\n if (!frozen) {\n await writeFileContent(absolutePath, content);\n }\n }\n return deployed;\n}\n\n/**\n * Frozen integrity check: refuse to overwrite known-good bytes with tampered\n * ones when the prior content_hash matches the rulesync format. Hashes not\n * written by rulesync are skipped (debug-logged), preserving the commit-SHA pin.\n */\nfunction assertFrozenSkillIntegrity(params: {\n frozen: boolean;\n locked: GhLockInstallation | undefined;\n contentHash: string;\n sourceKey: string;\n skillName: string;\n agent: GhAgent;\n scope: GhScope;\n logger: Logger;\n}): void {\n const { frozen, locked, contentHash, sourceKey, skillName, agent, scope, logger } = params;\n if (frozen && locked?.content_hash) {\n if (RULESYNC_CONTENT_HASH_REGEX.test(locked.content_hash)) {\n if (locked.content_hash !== contentHash) {\n throw new Error(\n `content_hash mismatch for ${sourceKey} skill \"${skillName}\" (agent=${agent}, scope=${scope}): lock=${locked.content_hash} computed=${contentHash}. Refuse to trust the deployment under --frozen.`,\n );\n }\n } else {\n logger.debug(\n `Skipping content_hash integrity check for ${sourceKey} skill \"${skillName}\": recorded hash \"${locked.content_hash}\" was not written by rulesync.`,\n );\n }\n }\n}\n\nasync function removeStaleFile(params: {\n relativePath: string;\n scope: GhScope;\n projectRoot: string;\n logger: Logger;\n}): Promise<void> {\n const { relativePath, scope, projectRoot, logger } = params;\n if (posix.isAbsolute(relativePath) || relativePath.split(/[/\\\\]/).includes(\"..\")) {\n logger.warn(`Refusing to remove stale gh file with suspicious path: \"${relativePath}\".`);\n return;\n }\n const scopeRoot = scope === \"user\" ? getHomeDirectory() : projectRoot;\n try {\n checkPathTraversal({ relativePath, intendedRootDir: scopeRoot });\n } catch {\n logger.warn(`Refusing to remove stale gh file outside ${scope} root: \"${relativePath}\".`);\n return;\n }\n const absolute = join(scopeRoot, relativePath);\n await removeFile(absolute);\n logger.debug(`Removed stale gh file: ${relativePath}`);\n}\n\n/**\n * Detect a 404-like error in a way that tolerates both real `GitHubClientError`\n * instances and any other thrown value that exposes a numeric `statusCode`\n * (e.g. plain Errors raised from a test mock that does not import the real\n * client class).\n */\nfunction is404(error: unknown): boolean {\n if (error instanceof GitHubClientError && error.statusCode === 404) {\n return true;\n }\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"statusCode\" in error &&\n error.statusCode === 404\n ) {\n return true;\n }\n return false;\n}\n\n/**\n * SHA-256 over a canonical, order-independent representation of the deployed\n * files. Identical algorithm to the apm-install hash so users familiar with\n * one mode can read the other.\n */\nfunction computeContentHash(\n files: Array<{ relativeToScopeRoot: string; content: string }>,\n): string {\n const hash = createHash(\"sha256\");\n for (const { relativeToScopeRoot, content } of files) {\n hash.update(relativeToScopeRoot);\n hash.update(\"\\0\");\n hash.update(content);\n hash.update(\"\\0\");\n }\n return `sha256:${hash.digest(\"hex\")}`;\n}\n","import { ConfigResolver } from \"../../config/config-resolver.js\";\nimport { installApm } from \"../../lib/apm/apm-install.js\";\nimport { apmManifestExists } from \"../../lib/apm/apm-manifest.js\";\nimport { installGh } from \"../../lib/gh/gh-install.js\";\nimport { resolveAndFetchSources } from \"../../lib/sources.js\";\nimport type { Logger } from \"../../utils/logger.js\";\n\nexport const INSTALL_MODES = [\"rulesync\", \"apm\", \"gh\"] as const;\nexport type InstallMode = (typeof INSTALL_MODES)[number];\n\nexport type InstallCommandOptions = {\n mode?: InstallMode;\n update?: boolean;\n frozen?: boolean;\n token?: string;\n configPath?: string;\n verbose?: boolean;\n silent?: boolean;\n};\n\nexport async function installCommand(\n logger: Logger,\n options: InstallCommandOptions,\n): Promise<void> {\n const mode: InstallMode = options.mode ?? \"rulesync\";\n\n if (mode === \"gh\") {\n await runGhInstall(logger, options);\n return;\n }\n\n if (mode === \"apm\") {\n await runApmInstall(logger, options);\n return;\n }\n\n await runRulesyncInstall(logger, options);\n}\n\nasync function runRulesyncInstall(logger: Logger, options: InstallCommandOptions): Promise<void> {\n const projectRoot = process.cwd();\n\n // If both apm.yml and rulesync.jsonc sources are defined, refuse to guess.\n // `--mode apm` is required to opt into the APM layout.\n const apmExists = await apmManifestExists(projectRoot);\n\n const config = await ConfigResolver.resolve(\n {\n configPath: options.configPath,\n verbose: options.verbose,\n silent: options.silent,\n },\n { logger },\n );\n const sources = config.getSources();\n\n if (apmExists && sources.length > 0) {\n throw new Error(\n \"Both apm.yml and rulesync.jsonc `sources` are defined. Pass --mode apm or --mode rulesync to disambiguate.\",\n );\n }\n\n if (sources.length === 0) {\n if (apmExists) {\n logger.warn(\n \"No sources defined in rulesync.jsonc, but apm.yml is present. Did you mean --mode apm?\",\n );\n return;\n }\n logger.warn(\"No sources defined in configuration. Removing stale source artifacts.\");\n }\n\n logger.debug(`Installing rules and skills from ${sources.length} source(s)...`);\n\n const result = await resolveAndFetchSources({\n sources,\n projectRoot,\n options: {\n updateSources: options.update,\n frozen: options.frozen,\n token: options.token,\n },\n logger,\n });\n\n if (logger.jsonMode) {\n logger.captureData(\"sourcesProcessed\", result.sourcesProcessed);\n logger.captureData(\"skillsFetched\", result.fetchedSkillCount);\n logger.captureData(\"rulesFetched\", result.fetchedRuleCount);\n logger.captureData(\"failedSourceCount\", result.failedSourceCount);\n }\n\n if (result.failedSourceCount > 0) {\n throw new Error(\n `Failed to install ${result.failedSourceCount} of ${result.sourcesProcessed} rulesync source(s). See the log above for details.`,\n );\n }\n\n if (result.fetchedSkillCount > 0 || result.fetchedRuleCount > 0) {\n logger.success(\n `Installed ${result.fetchedSkillCount} skill(s) and ${result.fetchedRuleCount} rule(s) from ${result.sourcesProcessed} source(s).`,\n );\n } else {\n logger.success(\n `All source artifacts up to date (${result.sourcesProcessed} source(s) checked).`,\n );\n }\n}\n\nasync function runApmInstall(logger: Logger, options: InstallCommandOptions): Promise<void> {\n const projectRoot = process.cwd();\n\n if (!(await apmManifestExists(projectRoot))) {\n throw new Error(\n \"--mode apm requires an apm.yml at the project root. Create one or drop --mode apm to fall back to rulesync mode.\",\n );\n }\n\n const result = await installApm({\n projectRoot,\n options: {\n update: options.update,\n frozen: options.frozen,\n token: options.token,\n },\n logger,\n });\n\n if (logger.jsonMode) {\n logger.captureData(\"dependenciesProcessed\", result.dependenciesProcessed);\n logger.captureData(\"deployedFileCount\", result.deployedFileCount);\n logger.captureData(\"failedDependencyCount\", result.failedDependencyCount);\n }\n\n if (result.failedDependencyCount > 0) {\n throw new Error(\n `Failed to install ${result.failedDependencyCount} of ${result.dependenciesProcessed} apm dependency(ies). See the log above for details.`,\n );\n }\n\n if (result.deployedFileCount > 0) {\n logger.success(\n `Installed ${result.deployedFileCount} file(s) from ${result.dependenciesProcessed} apm dependency(ies).`,\n );\n } else {\n logger.success(`All apm dependencies up to date (${result.dependenciesProcessed} checked).`);\n }\n}\n\nasync function runGhInstall(logger: Logger, options: InstallCommandOptions): Promise<void> {\n const projectRoot = process.cwd();\n\n // gh mode reads sources from `rulesync.jsonc`, never from `apm.yml`. The\n // disambiguation between rulesync/apm modes lives in `runRulesyncInstall`;\n // here the user has already opted into gh mode explicitly.\n const config = await ConfigResolver.resolve(\n {\n configPath: options.configPath,\n verbose: options.verbose,\n silent: options.silent,\n },\n { logger },\n );\n const sources = config.getSources();\n\n if (sources.length === 0) {\n logger.warn(\"No sources defined in configuration. Nothing to install.\");\n return;\n }\n\n const result = await installGh({\n projectRoot,\n sources,\n options: {\n update: options.update,\n frozen: options.frozen,\n token: options.token,\n },\n logger,\n });\n\n if (logger.jsonMode) {\n logger.captureData(\"sourcesProcessed\", result.sourcesProcessed);\n logger.captureData(\"installedSkillCount\", result.installedSkillCount);\n logger.captureData(\"failedSourceCount\", result.failedSourceCount);\n }\n\n if (result.failedSourceCount > 0) {\n throw new Error(\n `Failed to install ${result.failedSourceCount} of ${result.sourcesProcessed} gh source(s). See the log above for details.`,\n );\n }\n\n if (result.installedSkillCount > 0) {\n logger.success(\n `Installed ${result.installedSkillCount} skill(s) from ${result.sourcesProcessed} gh source(s).`,\n );\n } else {\n logger.success(`All gh sources up to date (${result.sourcesProcessed} checked).`);\n }\n}\n","import { basename, join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { RULESYNC_CHECKS_RELATIVE_DIR_PATH } from \"../constants/rulesync-paths.js\";\nimport {\n RulesyncCheck,\n type RulesyncCheckFrontmatter,\n RulesyncCheckFrontmatterSchema,\n} from \"../features/checks/rulesync-check.js\";\nimport { formatError } from \"../utils/error.js\";\nimport {\n checkPathTraversal,\n ensureDir,\n listDirectoryFiles,\n removeFile,\n writeFileContent,\n} from \"../utils/file.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\n\nconst logger = new ConsoleLogger({ verbose: false, silent: true });\n\nconst maxCheckSizeBytes = 1024 * 1024; // 1MB\nconst maxChecksCount = 1000;\n\n/**\n * Tool to list all checks from .rulesync/checks/*.md\n */\nasync function listChecks(): Promise<\n Array<{\n relativePathFromCwd: string;\n frontmatter: RulesyncCheckFrontmatter;\n }>\n> {\n const checksDir = join(process.cwd(), RULESYNC_CHECKS_RELATIVE_DIR_PATH);\n\n try {\n const files = await listDirectoryFiles(checksDir);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const checks = await Promise.all(\n mdFiles.map(async (file) => {\n try {\n const check = await RulesyncCheck.fromFile({\n relativeFilePath: file,\n validate: true,\n });\n\n return {\n relativePathFromCwd: join(RULESYNC_CHECKS_RELATIVE_DIR_PATH, file),\n frontmatter: check.getFrontmatter(),\n };\n } catch (error) {\n logger.error(`Failed to read check file ${file}: ${formatError(error)}`);\n return null;\n }\n }),\n );\n\n // Filter out null values (failed reads)\n return checks.filter((check): check is NonNullable<typeof check> => check !== null);\n } catch (error) {\n logger.error(\n `Failed to read checks directory (${RULESYNC_CHECKS_RELATIVE_DIR_PATH}): ${formatError(error)}`,\n );\n return [];\n }\n}\n\n/**\n * Tool to get detailed information about a specific check\n */\nasync function getCheck({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncCheckFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n try {\n const check = await RulesyncCheck.fromFile({\n relativeFilePath: filename,\n validate: true,\n });\n\n return {\n relativePathFromCwd: join(RULESYNC_CHECKS_RELATIVE_DIR_PATH, filename),\n frontmatter: check.getFrontmatter(),\n body: check.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to read check file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to create or update a check (upsert operation)\n */\nasync function putCheck({\n relativePathFromCwd,\n frontmatter,\n body,\n}: {\n relativePathFromCwd: string;\n frontmatter: RulesyncCheckFrontmatter;\n body: string;\n}): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncCheckFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n // Check file size constraint\n const estimatedSize = JSON.stringify(frontmatter).length + body.length;\n if (estimatedSize > maxCheckSizeBytes) {\n throw new Error(\n `Check size ${estimatedSize} bytes exceeds maximum ${maxCheckSizeBytes} bytes (1MB) for ${relativePathFromCwd}`,\n );\n }\n\n try {\n // Check count constraint\n const existingChecks = await listChecks();\n const isUpdate = existingChecks.some(\n (check) => check.relativePathFromCwd === join(RULESYNC_CHECKS_RELATIVE_DIR_PATH, filename),\n );\n\n if (!isUpdate && existingChecks.length >= maxChecksCount) {\n throw new Error(\n `Maximum number of checks (${maxChecksCount}) reached in ${RULESYNC_CHECKS_RELATIVE_DIR_PATH}`,\n );\n }\n\n const check = new RulesyncCheck({\n outputRoot: process.cwd(),\n relativeDirPath: RULESYNC_CHECKS_RELATIVE_DIR_PATH,\n relativeFilePath: filename,\n frontmatter,\n body,\n validate: true,\n });\n\n // Ensure directory exists\n const checksDir = join(process.cwd(), RULESYNC_CHECKS_RELATIVE_DIR_PATH);\n await ensureDir(checksDir);\n\n // Write the file\n await writeFileContent(check.getFilePath(), check.getFileContent());\n\n return {\n relativePathFromCwd: join(RULESYNC_CHECKS_RELATIVE_DIR_PATH, filename),\n frontmatter: check.getFrontmatter(),\n body: check.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to write check file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to delete a check\n */\nasync function deleteCheck({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n const fullPath = join(process.cwd(), RULESYNC_CHECKS_RELATIVE_DIR_PATH, filename);\n\n try {\n await removeFile(fullPath);\n\n return {\n relativePathFromCwd: join(RULESYNC_CHECKS_RELATIVE_DIR_PATH, filename),\n };\n } catch (error) {\n throw new Error(`Failed to delete check file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Schema for check-related tool parameters\n */\nconst checkToolSchemas = {\n listChecks: z.object({}),\n getCheck: z.object({\n relativePathFromCwd: z.string(),\n }),\n putCheck: z.object({\n relativePathFromCwd: z.string(),\n frontmatter: RulesyncCheckFrontmatterSchema,\n body: z.string(),\n }),\n deleteCheck: z.object({\n relativePathFromCwd: z.string(),\n }),\n} as const;\n\n/**\n * Tool definitions for check-related operations\n */\nexport const checkTools = {\n listChecks: {\n name: \"listChecks\",\n description: `List all checks from ${join(RULESYNC_CHECKS_RELATIVE_DIR_PATH, \"*.md\")} with their frontmatter.`,\n parameters: checkToolSchemas.listChecks,\n execute: async () => {\n const checks = await listChecks();\n const output = { checks };\n return JSON.stringify(output, null, 2);\n },\n },\n getCheck: {\n name: \"getCheck\",\n description:\n \"Get detailed information about a specific check. relativePathFromCwd parameter is required.\",\n parameters: checkToolSchemas.getCheck,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await getCheck({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n putCheck: {\n name: \"putCheck\",\n description:\n \"Create or update a check (upsert operation). relativePathFromCwd, frontmatter, and body parameters are required.\",\n parameters: checkToolSchemas.putCheck,\n execute: async (args: {\n relativePathFromCwd: string;\n frontmatter: RulesyncCheckFrontmatter;\n body: string;\n }) => {\n const result = await putCheck({\n relativePathFromCwd: args.relativePathFromCwd,\n frontmatter: args.frontmatter,\n body: args.body,\n });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteCheck: {\n name: \"deleteCheck\",\n description: \"Delete a check file. relativePathFromCwd parameter is required.\",\n parameters: checkToolSchemas.deleteCheck,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await deleteCheck({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { basename, join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { RULESYNC_COMMANDS_RELATIVE_DIR_PATH } from \"../constants/rulesync-paths.js\";\nimport {\n RulesyncCommand,\n type RulesyncCommandFrontmatter,\n RulesyncCommandFrontmatterSchema,\n} from \"../features/commands/rulesync-command.js\";\nimport { formatError } from \"../utils/error.js\";\nimport {\n checkPathTraversal,\n ensureDir,\n listDirectoryFiles,\n removeFile,\n writeFileContent,\n} from \"../utils/file.js\";\nimport { stringifyFrontmatter } from \"../utils/frontmatter.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\n\nconst logger = new ConsoleLogger({ verbose: false, silent: true });\n\nconst maxCommandSizeBytes = 1024 * 1024; // 1MB\nconst maxCommandsCount = 1000;\n\n/**\n * Tool to list all commands from .rulesync/commands/*.md\n */\nasync function listCommands(): Promise<\n Array<{\n relativePathFromCwd: string;\n frontmatter: RulesyncCommandFrontmatter;\n }>\n> {\n const commandsDir = join(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);\n\n try {\n const files = await listDirectoryFiles(commandsDir);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const commands = await Promise.all(\n mdFiles.map(async (file) => {\n try {\n checkPathTraversal({\n relativePath: file,\n intendedRootDir: commandsDir,\n });\n\n const command = await RulesyncCommand.fromFile({\n relativeFilePath: file,\n });\n\n const frontmatter = command.getFrontmatter();\n\n return {\n relativePathFromCwd: join(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, file),\n frontmatter,\n };\n } catch (error) {\n logger.error(`Failed to read command file ${file}: ${formatError(error)}`);\n return null;\n }\n }),\n );\n\n // Filter out null values (failed reads)\n return commands.filter((command): command is NonNullable<typeof command> => command !== null);\n } catch (error) {\n logger.error(\n `Failed to read commands directory (${RULESYNC_COMMANDS_RELATIVE_DIR_PATH}): ${formatError(error)}`,\n );\n return [];\n }\n}\n\n/**\n * Tool to get detailed information about a specific command\n */\nasync function getCommand({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncCommandFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n try {\n const command = await RulesyncCommand.fromFile({\n relativeFilePath: filename,\n });\n\n return {\n relativePathFromCwd: join(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),\n frontmatter: command.getFrontmatter(),\n body: command.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to read command file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to create or update a command (upsert operation)\n */\nasync function putCommand({\n relativePathFromCwd,\n frontmatter,\n body,\n}: {\n relativePathFromCwd: string;\n frontmatter: RulesyncCommandFrontmatter;\n body: string;\n}): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncCommandFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n // Check file size constraint\n const estimatedSize = JSON.stringify(frontmatter).length + body.length;\n if (estimatedSize > maxCommandSizeBytes) {\n throw new Error(\n `Command size ${estimatedSize} bytes exceeds maximum ${maxCommandSizeBytes} bytes (1MB) for ${relativePathFromCwd}`,\n );\n }\n\n try {\n // Check command count constraint\n const existingCommands = await listCommands();\n const isUpdate = existingCommands.some(\n (command) =>\n command.relativePathFromCwd === join(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),\n );\n\n if (!isUpdate && existingCommands.length >= maxCommandsCount) {\n throw new Error(\n `Maximum number of commands (${maxCommandsCount}) reached in ${RULESYNC_COMMANDS_RELATIVE_DIR_PATH}`,\n );\n }\n\n // Create a new RulesyncCommand instance\n const fileContent = stringifyFrontmatter(body, frontmatter);\n const command = new RulesyncCommand({\n outputRoot: process.cwd(),\n relativeDirPath: RULESYNC_COMMANDS_RELATIVE_DIR_PATH,\n relativeFilePath: filename,\n frontmatter,\n body,\n fileContent,\n validate: true,\n });\n\n // Ensure directory exists\n const commandsDir = join(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);\n await ensureDir(commandsDir);\n\n // Write the file\n await writeFileContent(command.getFilePath(), command.getFileContent());\n\n return {\n relativePathFromCwd: join(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),\n frontmatter: command.getFrontmatter(),\n body: command.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to write command file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to delete a command\n */\nasync function deleteCommand({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n const fullPath = join(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename);\n\n try {\n await removeFile(fullPath);\n\n return {\n relativePathFromCwd: join(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),\n };\n } catch (error) {\n throw new Error(`Failed to delete command file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Schema for command-related tool parameters\n */\nconst commandToolSchemas = {\n listCommands: z.object({}),\n getCommand: z.object({\n relativePathFromCwd: z.string(),\n }),\n putCommand: z.object({\n relativePathFromCwd: z.string(),\n frontmatter: RulesyncCommandFrontmatterSchema,\n body: z.string(),\n }),\n deleteCommand: z.object({\n relativePathFromCwd: z.string(),\n }),\n} as const;\n\n/**\n * Tool definitions for command-related operations\n */\nexport const commandTools = {\n listCommands: {\n name: \"listCommands\",\n description: `List all commands from ${join(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, \"*.md\")} with their frontmatter.`,\n parameters: commandToolSchemas.listCommands,\n execute: async () => {\n const commands = await listCommands();\n const output = { commands };\n return JSON.stringify(output, null, 2);\n },\n },\n getCommand: {\n name: \"getCommand\",\n description:\n \"Get detailed information about a specific command. relativePathFromCwd parameter is required.\",\n parameters: commandToolSchemas.getCommand,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await getCommand({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n putCommand: {\n name: \"putCommand\",\n description:\n \"Create or update a command (upsert operation). relativePathFromCwd, frontmatter, and body parameters are required.\",\n parameters: commandToolSchemas.putCommand,\n execute: async (args: {\n relativePathFromCwd: string;\n frontmatter: RulesyncCommandFrontmatter;\n body: string;\n }) => {\n const result = await putCommand({\n relativePathFromCwd: args.relativePathFromCwd,\n frontmatter: args.frontmatter,\n body: args.body,\n });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteCommand: {\n name: \"deleteCommand\",\n description: \"Delete a command file. relativePathFromCwd parameter is required.\",\n parameters: commandToolSchemas.deleteCommand,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await deleteCommand({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { z } from \"zod/mini\";\n\nimport { ConfigResolver } from \"../config/config-resolver.js\";\nimport { Config } from \"../config/config.js\";\nimport { convertFromTool, type ConvertResult } from \"../lib/convert.js\";\nimport { type RulesyncFeatures } from \"../types/features.js\";\nimport { ALL_TOOL_TARGETS, type ToolTarget, ToolTargetSchema } from \"../types/tool-targets.js\";\nimport { formatError } from \"../utils/error.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\nimport { calculateTotalCount } from \"../utils/result.js\";\nimport { type McpResultCounts } from \"./types.js\";\n\n/**\n * Schema for convert options\n * Excluded parameters:\n * - outputRoots: Always use [process.cwd()] in MCP context\n * - verbose: Meaningless in MCP (no console output)\n * - silent: Meaningless in MCP\n * - configPath: Always use default path from process.cwd()\n */\nexport const convertOptionsSchema = z.object({\n from: z.string(),\n to: z.array(z.string()),\n features: z.optional(z.array(z.string())),\n global: z.optional(z.boolean()),\n dryRun: z.optional(z.boolean()),\n});\n\nexport type ConvertOptions = z.infer<typeof convertOptionsSchema>;\n\nexport type McpConvertResult = {\n success: boolean;\n result?: McpResultCounts;\n config?: {\n from: string;\n to: string[];\n features: string[];\n global: boolean;\n dryRun: boolean;\n };\n error?: string;\n};\n\nfunction parseToolTarget(value: string, label: string): ToolTarget {\n const result = ToolTargetSchema.safeParse(value);\n if (!result.success) {\n throw new Error(\n `Invalid ${label} tool '${value}'. Must be one of: ${ALL_TOOL_TARGETS.join(\", \")}`,\n );\n }\n return result.data;\n}\n\n/**\n * Execute the rulesync convert command via MCP\n * Configuration priority: MCP Parameters > rulesync.local.jsonc > rulesync.jsonc > Default values\n */\nexport async function executeConvert(options: ConvertOptions): Promise<McpConvertResult> {\n try {\n // Validate from\n if (!options.from) {\n return {\n success: false,\n error: \"from is required. Please specify a source tool to convert from.\",\n };\n }\n\n // Validate to\n if (!options.to || options.to.length === 0) {\n return {\n success: false,\n error: \"to is required and must not be empty. Please specify destination tools.\",\n };\n }\n\n const fromTool = parseToolTarget(options.from, \"source\");\n const toToolsRaw = options.to.map((t) => parseToolTarget(t, \"destination\"));\n const toTools = Array.from(new Set(toToolsRaw));\n\n if (toTools.includes(fromTool)) {\n return {\n success: false,\n error:\n `Destination tools must not include the source tool '${fromTool}'. ` +\n `Converting a tool onto itself is likely a mistake and may cause lossy round-trips.`,\n };\n }\n\n // Resolve config with MCP parameters taking precedence\n // ConfigResolver handles: CLI options > rulesync.local.jsonc > rulesync.jsonc > defaults\n // In MCP context, options act as CLI options (highest priority)\n // Pass both source and destinations as `targets` so per-target feature maps\n // in `rulesync.jsonc` are honored for every tool involved. Default features\n // to `*` so every feature that both tools support is attempted.\n const config = await ConfigResolver.resolve({\n targets: [fromTool, ...toTools],\n features: (options.features ?? [\"*\"]) as RulesyncFeatures,\n global: options.global,\n dryRun: options.dryRun,\n // Always use default outputRoots (process.cwd()) and configPath\n // verbose and silent are meaningless in MCP context\n verbose: false,\n silent: true,\n });\n\n const logger = new ConsoleLogger({ verbose: false, silent: true });\n const convertResult = await convertFromTool({ config, fromTool, toTools, logger });\n\n return buildSuccessResponse({ convertResult, config, fromTool, toTools });\n } catch (error) {\n return {\n success: false,\n error: formatError(error),\n };\n }\n}\n\nfunction buildSuccessResponse(params: {\n convertResult: ConvertResult;\n config: Config;\n fromTool: ToolTarget;\n toTools: ToolTarget[];\n}): McpConvertResult {\n const { convertResult, config, fromTool, toTools } = params;\n\n const totalCount = calculateTotalCount(convertResult);\n\n return {\n success: true,\n result: {\n rulesCount: convertResult.rulesCount,\n ignoreCount: convertResult.ignoreCount,\n mcpCount: convertResult.mcpCount,\n commandsCount: convertResult.commandsCount,\n subagentsCount: convertResult.subagentsCount,\n skillsCount: convertResult.skillsCount,\n hooksCount: convertResult.hooksCount,\n permissionsCount: convertResult.permissionsCount,\n checksCount: convertResult.checksCount,\n totalCount,\n },\n config: {\n from: fromTool,\n to: toTools,\n features: config.getFeatures(),\n global: config.getGlobal(),\n dryRun: config.isPreviewMode(),\n },\n };\n}\n\nconst convertToolSchemas = {\n executeConvert: convertOptionsSchema,\n};\n\nexport const convertTools = {\n executeConvert: {\n name: \"executeConvert\",\n description:\n \"Execute the rulesync convert command to convert configuration files between AI tools without writing intermediate .rulesync/ files. Requires a source tool (from) and one or more destination tools (to).\",\n parameters: convertToolSchemas.executeConvert,\n execute: async (options: ConvertOptions): Promise<string> => {\n const result = await executeConvert(options);\n return JSON.stringify(result, null, 2);\n },\n },\n};\n","import { z } from \"zod/mini\";\n\nimport { ConfigResolver } from \"../config/config-resolver.js\";\nimport { Config } from \"../config/config.js\";\nimport { checkRulesyncDirExists, generate, type GenerateResult } from \"../lib/generate.js\";\nimport { type RulesyncFeatures } from \"../types/features.js\";\nimport { type RulesyncTargets } from \"../types/tool-targets.js\";\nimport { formatError } from \"../utils/error.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\nimport { calculateTotalCount } from \"../utils/result.js\";\nimport { type McpResultCounts } from \"./types.js\";\n\n/**\n * Schema for generate options\n * Excluded parameters:\n * - outputRoots: Always use [process.cwd()] in MCP context\n * - verbose: Meaningless in MCP (no console output)\n * - silent: Meaningless in MCP\n * - configPath: Always use default path from process.cwd()\n */\nexport const generateOptionsSchema = z.object({\n targets: z.optional(z.array(z.string())),\n features: z.optional(z.array(z.string())),\n delete: z.optional(z.boolean()),\n global: z.optional(z.boolean()),\n simulateCommands: z.optional(z.boolean()),\n simulateSubagents: z.optional(z.boolean()),\n simulateSkills: z.optional(z.boolean()),\n});\n\nexport type GenerateOptions = z.infer<typeof generateOptionsSchema>;\n\nexport type McpGenerateResult = {\n success: boolean;\n /**\n * Human-readable summary of the outcome. Clarifies that a `totalCount` of 0\n * means \"already up to date\" (success with nothing to write) rather than a\n * failure, since `generate` is idempotent and only writes changed files.\n */\n message?: string;\n result?: McpResultCounts;\n config?: {\n targets: string[];\n features: string[];\n global: boolean;\n delete: boolean;\n simulateCommands: boolean;\n simulateSubagents: boolean;\n simulateSkills: boolean;\n };\n error?: string;\n};\n\n/**\n * Execute the rulesync generate command via MCP\n * Configuration priority: MCP Parameters > rulesync.local.jsonc > rulesync.jsonc > Default values\n */\nexport async function executeGenerate(options: GenerateOptions = {}): Promise<McpGenerateResult> {\n try {\n // Check if .rulesync directory exists\n const exists = await checkRulesyncDirExists({ inputRoot: process.cwd() });\n if (!exists) {\n return {\n success: false,\n error:\n \".rulesync directory does not exist. Please run 'rulesync init' first or create the directory manually.\",\n };\n }\n\n // Resolve config with MCP parameters taking precedence\n // ConfigResolver handles: CLI options > rulesync.local.jsonc > rulesync.jsonc > defaults\n // In MCP context, options act as CLI options (highest priority)\n const config = await ConfigResolver.resolve({\n targets: options.targets as RulesyncTargets | undefined,\n features: options.features as RulesyncFeatures | undefined,\n delete: options.delete,\n global: options.global,\n simulateCommands: options.simulateCommands,\n simulateSubagents: options.simulateSubagents,\n simulateSkills: options.simulateSkills,\n // Always use default outputRoots (process.cwd()) and configPath\n // verbose and silent are meaningless in MCP context\n verbose: false,\n silent: true,\n });\n\n const logger = new ConsoleLogger({ verbose: false, silent: true });\n const generateResult = await generate({ config, logger });\n\n return buildSuccessResponse({ generateResult, config });\n } catch (error) {\n return {\n success: false,\n error: formatError(error),\n };\n }\n}\n\n/**\n * Build a human-readable summary of a successful generation.\n *\n * `generate` is idempotent: `totalCount` reflects only files whose content\n * actually changed on disk, so a count of 0 is a normal \"nothing to update\"\n * outcome — not a failure. The message makes that explicit so MCP callers do\n * not misread a zero count as a broken generate.\n */\nfunction buildGenerateMessage(params: { totalCount: number; config: Config }): string {\n const { totalCount, config } = params;\n const targets = config.getTargets().join(\", \");\n const features = config.getFeatures().join(\", \");\n\n if (totalCount > 0) {\n return `Generated ${totalCount} file(s) for targets [${targets}] and features [${features}].`;\n }\n\n return (\n `No files needed updating for targets [${targets}] and features [${features}]. ` +\n `'generate' only writes files whose content changed, so a totalCount of 0 means the ` +\n `outputs are already up to date — this is a successful no-op, not a failure.`\n );\n}\n\nfunction buildSuccessResponse(params: {\n generateResult: GenerateResult;\n config: Config;\n}): McpGenerateResult {\n const { generateResult, config } = params;\n\n const totalCount = calculateTotalCount(generateResult);\n\n return {\n success: true,\n message: buildGenerateMessage({ totalCount, config }),\n result: {\n rulesCount: generateResult.rulesCount,\n ignoreCount: generateResult.ignoreCount,\n mcpCount: generateResult.mcpCount,\n commandsCount: generateResult.commandsCount,\n subagentsCount: generateResult.subagentsCount,\n skillsCount: generateResult.skillsCount,\n hooksCount: generateResult.hooksCount,\n permissionsCount: generateResult.permissionsCount,\n checksCount: generateResult.checksCount,\n activationCount: generateResult.activationCount,\n totalCount,\n },\n config: {\n targets: config.getTargets(),\n features: config.getFeatures(),\n global: config.getGlobal(),\n delete: config.getDelete(),\n simulateCommands: config.getSimulateCommands(),\n simulateSubagents: config.getSimulateSubagents(),\n simulateSkills: config.getSimulateSkills(),\n },\n };\n}\n\nconst generateToolSchemas = {\n executeGenerate: generateOptionsSchema,\n};\n\nexport const generateTools = {\n executeGenerate: {\n name: \"executeGenerate\",\n description:\n \"Execute the rulesync generate command to create output files for AI tools. Uses rulesync.jsonc settings by default, but options can override them. Idempotent: only files whose content changed are written, so a totalCount of 0 means the outputs are already up to date (a successful no-op), not a failure. See the 'message' field for a human-readable summary.\",\n parameters: generateToolSchemas.executeGenerate,\n execute: async (options: GenerateOptions = {}): Promise<string> => {\n const result = await executeGenerate(options);\n return JSON.stringify(result, null, 2);\n },\n },\n};\n","import { join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { RULESYNC_HOOKS_RELATIVE_FILE_PATH } from \"../constants/rulesync-paths.js\";\nimport { RulesyncHooks } from \"../features/hooks/rulesync-hooks.js\";\nimport { formatError } from \"../utils/error.js\";\nimport { ensureDir, removeFile, writeFileContent } from \"../utils/file.js\";\nimport { parseJsonc } from \"../utils/jsonc.js\";\nimport {\n getRulesyncSourceCandidates,\n resolveRulesyncSourceWritePath,\n} from \"../utils/rulesync-source-path.js\";\n\nconst maxHooksSizeBytes = 1024 * 1024; // 1MB\n\n/**\n * Tool to get the hooks configuration file\n */\nasync function getHooksFile(): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n try {\n const rulesyncHooks = await RulesyncHooks.fromFile({\n validate: true,\n });\n\n const relativePathFromCwd = join(\n rulesyncHooks.getRelativeDirPath(),\n rulesyncHooks.getRelativeFilePath(),\n );\n\n return {\n relativePathFromCwd,\n content: rulesyncHooks.getFileContent(),\n };\n } catch (error) {\n throw new Error(\n `Failed to read hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to create or update the hooks configuration file (upsert operation)\n */\nasync function putHooksFile({ content }: { content: string }): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n // Check file size constraint\n if (content.length > maxHooksSizeBytes) {\n throw new Error(\n `Hooks file size ${content.length} bytes exceeds maximum ${maxHooksSizeBytes} bytes (1MB) for ${RULESYNC_HOOKS_RELATIVE_FILE_PATH}`,\n );\n }\n\n // Validate JSONC format\n try {\n parseJsonc(content);\n } catch (error) {\n throw new Error(\n `Invalid JSONC format in hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n\n try {\n const outputRoot = process.cwd();\n const paths = RulesyncHooks.getSettablePaths();\n const { relativeDirPath, relativeFilePath } = await resolveRulesyncSourceWritePath({\n outputRoot,\n paths,\n });\n const fullPath = join(outputRoot, relativeDirPath, relativeFilePath);\n\n // Create a RulesyncHooks instance to validate the content\n const rulesyncHooks = new RulesyncHooks({\n outputRoot,\n relativeDirPath,\n relativeFilePath,\n fileContent: content,\n validate: true,\n });\n\n // Ensure directory exists\n await ensureDir(join(outputRoot, relativeDirPath));\n\n // Write the file\n await writeFileContent(fullPath, content);\n\n const relativePathFromCwd = join(relativeDirPath, relativeFilePath);\n\n return {\n relativePathFromCwd,\n content: rulesyncHooks.getFileContent(),\n };\n } catch (error) {\n throw new Error(\n `Failed to write hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to delete the hooks configuration file\n */\nasync function deleteHooksFile(): Promise<{\n relativePathFromCwd: string;\n}> {\n try {\n const outputRoot = process.cwd();\n const paths = RulesyncHooks.getSettablePaths();\n\n for (const candidate of getRulesyncSourceCandidates({ paths })) {\n await removeFile(join(outputRoot, candidate.relativeDirPath, candidate.relativeFilePath));\n }\n\n const relativePathFromCwd = join(\n paths.recommended.relativeDirPath,\n paths.recommended.relativeFilePath,\n );\n\n return {\n relativePathFromCwd,\n };\n } catch (error) {\n throw new Error(\n `Failed to delete hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Schema for hooks-related tool parameters\n */\nconst hooksToolSchemas = {\n getHooksFile: z.object({}),\n putHooksFile: z.object({\n content: z.string(),\n }),\n deleteHooksFile: z.object({}),\n} as const;\n\n/**\n * Tool definitions for hooks-related operations\n */\nexport const hooksTools = {\n getHooksFile: {\n name: \"getHooksFile\",\n description: `Get the hooks configuration file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}).`,\n parameters: hooksToolSchemas.getHooksFile,\n execute: async () => {\n const result = await getHooksFile();\n return JSON.stringify(result, null, 2);\n },\n },\n putHooksFile: {\n name: \"putHooksFile\",\n description:\n \"Create or update the hooks configuration file (upsert operation). content parameter is required and must be valid JSONC.\",\n parameters: hooksToolSchemas.putHooksFile,\n execute: async (args: { content: string }) => {\n const result = await putHooksFile({ content: args.content });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteHooksFile: {\n name: \"deleteHooksFile\",\n description: \"Delete the hooks configuration file.\",\n parameters: hooksToolSchemas.deleteHooksFile,\n execute: async () => {\n const result = await deleteHooksFile();\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport {\n RULESYNC_AIIGNORE_RELATIVE_FILE_PATH,\n RULESYNC_IGNORE_RELATIVE_FILE_PATH,\n} from \"../constants/rulesync-paths.js\";\nimport { formatError } from \"../utils/error.js\";\nimport { ensureDir, readFileContent, removeFile, writeFileContent } from \"../utils/file.js\";\n\nconst maxIgnoreFileSizeBytes = 100 * 1024; // 100KB\n\n/**\n * Tool to get the content of .rulesync/.aiignore file\n */\nasync function getIgnoreFile(): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n const ignoreFilePath = join(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);\n\n try {\n const content = await readFileContent(ignoreFilePath);\n\n return {\n relativePathFromCwd: RULESYNC_AIIGNORE_RELATIVE_FILE_PATH,\n content,\n };\n } catch (error) {\n throw new Error(\n `Failed to read ignore file (${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to create or update the .rulesync/.aiignore file (upsert operation)\n */\nasync function putIgnoreFile({ content }: { content: string }): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n const ignoreFilePath = join(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);\n\n // Check file size constraint\n const contentSizeBytes = Buffer.byteLength(content, \"utf8\");\n if (contentSizeBytes > maxIgnoreFileSizeBytes) {\n throw new Error(\n `Ignore file size ${contentSizeBytes} bytes exceeds maximum ${maxIgnoreFileSizeBytes} bytes (100KB) for ${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}`,\n );\n }\n\n try {\n // Ensure parent directory exists (should be cwd, but just to be safe)\n await ensureDir(process.cwd());\n\n // Write the file\n await writeFileContent(ignoreFilePath, content);\n\n return {\n relativePathFromCwd: RULESYNC_AIIGNORE_RELATIVE_FILE_PATH,\n content,\n };\n } catch (error) {\n throw new Error(\n `Failed to write ignore file (${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to delete the .rulesyncignore (legacy) and .rulesync/.aiignore (recommended) files\n */\nasync function deleteIgnoreFile(): Promise<{\n relativePathFromCwd: string;\n}> {\n const aiignorePath = join(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);\n const legacyIgnorePath = join(process.cwd(), RULESYNC_IGNORE_RELATIVE_FILE_PATH);\n\n try {\n // Attempt to remove both files. The removeFile helper is expected to be idempotent\n // (no-throw for non-existent files). If any real IO error happens, the Promise.all\n // will reject and we propagate an error.\n await Promise.all([removeFile(aiignorePath), removeFile(legacyIgnorePath)]);\n\n return {\n // Keep the historical return shape — point to the recommended file path\n // for backward compatibility.\n relativePathFromCwd: RULESYNC_AIIGNORE_RELATIVE_FILE_PATH,\n };\n } catch (error) {\n throw new Error(\n `Failed to delete ignore files (${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}, ${RULESYNC_IGNORE_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Schema for ignore-related tool parameters\n */\nconst ignoreToolSchemas = {\n getIgnoreFile: z.object({}),\n putIgnoreFile: z.object({\n content: z.string(),\n }),\n deleteIgnoreFile: z.object({}),\n} as const;\n\n/**\n * Tool definitions for ignore-related operations\n */\nexport const ignoreTools = {\n getIgnoreFile: {\n name: \"getIgnoreFile\",\n description: \"Get the content of the .rulesyncignore file from the project root.\",\n parameters: ignoreToolSchemas.getIgnoreFile,\n execute: async () => {\n const result = await getIgnoreFile();\n return JSON.stringify(result, null, 2);\n },\n },\n putIgnoreFile: {\n name: \"putIgnoreFile\",\n description:\n \"Create or update the .rulesync/.aiignore file (upsert operation). content parameter is required.\",\n parameters: ignoreToolSchemas.putIgnoreFile,\n execute: async (args: { content: string }) => {\n const result = await putIgnoreFile({ content: args.content });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteIgnoreFile: {\n name: \"deleteIgnoreFile\",\n description: \"Delete the .rulesyncignore and .rulesync/.aiignore files.\",\n parameters: ignoreToolSchemas.deleteIgnoreFile,\n execute: async () => {\n const result = await deleteIgnoreFile();\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { z } from \"zod/mini\";\n\nimport { ConfigResolver } from \"../config/config-resolver.js\";\nimport { Config } from \"../config/config.js\";\nimport { importFromTool, type ImportResult } from \"../lib/import.js\";\nimport { type RulesyncFeatures } from \"../types/features.js\";\nimport { type RulesyncTargets, type ToolTarget } from \"../types/tool-targets.js\";\nimport { formatError } from \"../utils/error.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\nimport { calculateTotalCount } from \"../utils/result.js\";\nimport { type McpResultCounts } from \"./types.js\";\n\n/**\n * Schema for import options\n * Note: Import requires exactly one target tool\n * Excluded parameters:\n * - outputRoots: Always use [process.cwd()] in MCP context\n * - verbose: Meaningless in MCP (no console output)\n * - silent: Meaningless in MCP\n * - configPath: Always use default path from process.cwd()\n * - delete: Not applicable to import\n * - simulateCommands/simulateSubagents/simulateSkills: Not applicable to import\n */\nexport const importOptionsSchema = z.object({\n target: z.string(),\n features: z.optional(z.array(z.string())),\n global: z.optional(z.boolean()),\n});\n\nexport type ImportOptions = z.infer<typeof importOptionsSchema>;\n\nexport type McpImportResult = {\n success: boolean;\n result?: McpResultCounts;\n config?: {\n target: string;\n features: string[];\n global: boolean;\n };\n error?: string;\n};\n\n/**\n * Execute the rulesync import command via MCP\n * Configuration priority: MCP Parameters > rulesync.local.jsonc > rulesync.jsonc > Default values\n */\nexport async function executeImport(options: ImportOptions): Promise<McpImportResult> {\n try {\n // Validate target\n if (!options.target) {\n return {\n success: false,\n error: \"target is required. Please specify a tool to import from.\",\n };\n }\n\n // Resolve config with MCP parameters taking precedence\n // ConfigResolver handles: CLI options > rulesync.local.jsonc > rulesync.jsonc > defaults\n // In MCP context, options act as CLI options (highest priority)\n const config = await ConfigResolver.resolve({\n targets: [options.target] as RulesyncTargets,\n features: options.features as RulesyncFeatures | undefined,\n global: options.global,\n // Always use default outputRoots (process.cwd()) and configPath\n // verbose and silent are meaningless in MCP context\n verbose: false,\n silent: true,\n });\n\n const tool = config.getTargets()[0] as ToolTarget;\n\n const logger = new ConsoleLogger({ verbose: false, silent: true });\n const importResult = await importFromTool({ config, tool, logger });\n\n return buildSuccessResponse({ importResult, config, tool });\n } catch (error) {\n return {\n success: false,\n error: formatError(error),\n };\n }\n}\n\nfunction buildSuccessResponse(params: {\n importResult: ImportResult;\n config: Config;\n tool: ToolTarget;\n}): McpImportResult {\n const { importResult, config, tool } = params;\n\n const totalCount = calculateTotalCount(importResult);\n\n return {\n success: true,\n result: {\n rulesCount: importResult.rulesCount,\n ignoreCount: importResult.ignoreCount,\n mcpCount: importResult.mcpCount,\n commandsCount: importResult.commandsCount,\n subagentsCount: importResult.subagentsCount,\n skillsCount: importResult.skillsCount,\n hooksCount: importResult.hooksCount,\n permissionsCount: importResult.permissionsCount,\n checksCount: importResult.checksCount,\n totalCount,\n },\n config: {\n target: tool,\n features: config.getFeatures(),\n global: config.getGlobal(),\n },\n };\n}\n\nconst importToolSchemas = {\n executeImport: importOptionsSchema,\n};\n\nexport const importTools = {\n executeImport: {\n name: \"executeImport\",\n description:\n \"Execute the rulesync import command to import configuration files from an AI tool into .rulesync directory. Requires exactly one target tool to import from.\",\n parameters: importToolSchemas.executeImport,\n execute: async (options: ImportOptions): Promise<string> => {\n const result = await executeImport(options);\n return JSON.stringify(result, null, 2);\n },\n },\n};\n","import { join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { RULESYNC_MCP_RELATIVE_FILE_PATH } from \"../constants/rulesync-paths.js\";\nimport { RulesyncMcp } from \"../features/mcp/rulesync-mcp.js\";\nimport { formatError } from \"../utils/error.js\";\nimport { ensureDir, removeFile, writeFileContent } from \"../utils/file.js\";\nimport { parseJsonc } from \"../utils/jsonc.js\";\nimport {\n getRulesyncSourceCandidates,\n resolveRulesyncSourceWritePath,\n} from \"../utils/rulesync-source-path.js\";\n\nconst maxMcpSizeBytes = 1024 * 1024; // 1MB\n\n/**\n * Tool to get the MCP configuration file\n */\nasync function getMcpFile(): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n try {\n const rulesyncMcp = await RulesyncMcp.fromFile({\n validate: true,\n });\n\n const relativePathFromCwd = join(\n rulesyncMcp.getRelativeDirPath(),\n rulesyncMcp.getRelativeFilePath(),\n );\n\n return {\n relativePathFromCwd,\n content: rulesyncMcp.getFileContent(),\n };\n } catch (error) {\n throw new Error(\n `Failed to read MCP file (${RULESYNC_MCP_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to create or update the MCP configuration file (upsert operation)\n */\nasync function putMcpFile({ content }: { content: string }): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n // Check file size constraint\n if (content.length > maxMcpSizeBytes) {\n throw new Error(\n `MCP file size ${content.length} bytes exceeds maximum ${maxMcpSizeBytes} bytes (1MB) for ${RULESYNC_MCP_RELATIVE_FILE_PATH}`,\n );\n }\n\n // Validate JSONC format\n try {\n parseJsonc(content);\n } catch (error) {\n throw new Error(\n `Invalid JSONC format in MCP file (${RULESYNC_MCP_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n\n try {\n const outputRoot = process.cwd();\n const paths = RulesyncMcp.getSettablePaths();\n const { relativeDirPath, relativeFilePath } = await resolveRulesyncSourceWritePath({\n outputRoot,\n paths,\n });\n const fullPath = join(outputRoot, relativeDirPath, relativeFilePath);\n\n // Create a RulesyncMcp instance to validate the content\n const rulesyncMcp = new RulesyncMcp({\n outputRoot,\n relativeDirPath,\n relativeFilePath,\n fileContent: content,\n validate: true,\n });\n\n // Ensure directory exists\n await ensureDir(join(outputRoot, relativeDirPath));\n\n // Write the file\n await writeFileContent(fullPath, content);\n\n const relativePathFromCwd = join(relativeDirPath, relativeFilePath);\n\n return {\n relativePathFromCwd,\n content: rulesyncMcp.getFileContent(),\n };\n } catch (error) {\n throw new Error(\n `Failed to write MCP file (${RULESYNC_MCP_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to delete the MCP configuration file\n */\nasync function deleteMcpFile(): Promise<{\n relativePathFromCwd: string;\n}> {\n try {\n const outputRoot = process.cwd();\n const paths = RulesyncMcp.getSettablePaths();\n\n for (const candidate of getRulesyncSourceCandidates({ paths })) {\n await removeFile(join(outputRoot, candidate.relativeDirPath, candidate.relativeFilePath));\n }\n\n const relativePathFromCwd = join(\n paths.recommended.relativeDirPath,\n paths.recommended.relativeFilePath,\n );\n\n return {\n relativePathFromCwd,\n };\n } catch (error) {\n throw new Error(\n `Failed to delete MCP file (${RULESYNC_MCP_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Schema for MCP-related tool parameters\n */\nconst mcpToolSchemas = {\n getMcpFile: z.object({}),\n putMcpFile: z.object({\n content: z.string(),\n }),\n deleteMcpFile: z.object({}),\n} as const;\n\n/**\n * Tool definitions for MCP-related operations\n */\nexport const mcpTools = {\n getMcpFile: {\n name: \"getMcpFile\",\n description: `Get the MCP configuration file (${RULESYNC_MCP_RELATIVE_FILE_PATH}).`,\n parameters: mcpToolSchemas.getMcpFile,\n execute: async () => {\n const result = await getMcpFile();\n return JSON.stringify(result, null, 2);\n },\n },\n putMcpFile: {\n name: \"putMcpFile\",\n description:\n \"Create or update the MCP configuration file (upsert operation). content parameter is required and must be valid JSONC.\",\n parameters: mcpToolSchemas.putMcpFile,\n execute: async (args: { content: string }) => {\n const result = await putMcpFile({ content: args.content });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteMcpFile: {\n name: \"deleteMcpFile\",\n description: \"Delete the MCP configuration file.\",\n parameters: mcpToolSchemas.deleteMcpFile,\n execute: async () => {\n const result = await deleteMcpFile();\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH } from \"../constants/rulesync-paths.js\";\nimport { RulesyncPermissions } from \"../features/permissions/rulesync-permissions.js\";\nimport { formatError } from \"../utils/error.js\";\nimport { ensureDir, removeFile, writeFileContent } from \"../utils/file.js\";\nimport { parseJsonc } from \"../utils/jsonc.js\";\nimport {\n getRulesyncSourceCandidates,\n resolveRulesyncSourceWritePath,\n} from \"../utils/rulesync-source-path.js\";\n\nconst maxPermissionsSizeBytes = 1024 * 1024; // 1MB\n\n/**\n * Tool to get the permissions configuration file\n */\nasync function getPermissionsFile(): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n try {\n const rulesyncPermissions = await RulesyncPermissions.fromFile({\n validate: true,\n });\n\n const relativePathFromCwd = join(\n rulesyncPermissions.getRelativeDirPath(),\n rulesyncPermissions.getRelativeFilePath(),\n );\n\n return {\n relativePathFromCwd,\n content: rulesyncPermissions.getFileContent(),\n };\n } catch (error) {\n throw new Error(\n `Failed to read permissions file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to create or update the permissions configuration file (upsert operation)\n */\nasync function putPermissionsFile({ content }: { content: string }): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n // Check file size constraint\n if (content.length > maxPermissionsSizeBytes) {\n throw new Error(\n `Permissions file size ${content.length} bytes exceeds maximum ${maxPermissionsSizeBytes} bytes (1MB) for ${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}`,\n );\n }\n\n // Validate JSONC format\n try {\n parseJsonc(content);\n } catch (error) {\n throw new Error(\n `Invalid JSONC format in permissions file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n\n try {\n const outputRoot = process.cwd();\n const paths = RulesyncPermissions.getSettablePaths();\n const { relativeDirPath, relativeFilePath } = await resolveRulesyncSourceWritePath({\n outputRoot,\n paths,\n });\n const fullPath = join(outputRoot, relativeDirPath, relativeFilePath);\n\n // Create a RulesyncPermissions instance to validate the content\n const rulesyncPermissions = new RulesyncPermissions({\n outputRoot,\n relativeDirPath,\n relativeFilePath,\n fileContent: content,\n validate: true,\n });\n\n // Ensure directory exists\n await ensureDir(join(outputRoot, relativeDirPath));\n\n // Write the file\n await writeFileContent(fullPath, content);\n\n const relativePathFromCwd = join(relativeDirPath, relativeFilePath);\n\n return {\n relativePathFromCwd,\n content: rulesyncPermissions.getFileContent(),\n };\n } catch (error) {\n throw new Error(\n `Failed to write permissions file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to delete the permissions configuration file\n */\nasync function deletePermissionsFile(): Promise<{\n relativePathFromCwd: string;\n}> {\n try {\n const outputRoot = process.cwd();\n const paths = RulesyncPermissions.getSettablePaths();\n\n for (const candidate of getRulesyncSourceCandidates({ paths })) {\n await removeFile(join(outputRoot, candidate.relativeDirPath, candidate.relativeFilePath));\n }\n\n const relativePathFromCwd = join(\n paths.recommended.relativeDirPath,\n paths.recommended.relativeFilePath,\n );\n\n return {\n relativePathFromCwd,\n };\n } catch (error) {\n throw new Error(\n `Failed to delete permissions file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Schema for permissions-related tool parameters\n */\nconst permissionsToolSchemas = {\n getPermissionsFile: z.object({}),\n putPermissionsFile: z.object({\n content: z.string(),\n }),\n deletePermissionsFile: z.object({}),\n} as const;\n\n/**\n * Tool definitions for permissions-related operations\n */\nexport const permissionsTools = {\n getPermissionsFile: {\n name: \"getPermissionsFile\",\n description: `Get the permissions configuration file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}).`,\n parameters: permissionsToolSchemas.getPermissionsFile,\n execute: async () => {\n const result = await getPermissionsFile();\n return JSON.stringify(result, null, 2);\n },\n },\n putPermissionsFile: {\n name: \"putPermissionsFile\",\n description:\n \"Create or update the permissions configuration file (upsert operation). content parameter is required and must be valid JSONC.\",\n parameters: permissionsToolSchemas.putPermissionsFile,\n execute: async (args: { content: string }) => {\n const result = await putPermissionsFile({ content: args.content });\n return JSON.stringify(result, null, 2);\n },\n },\n deletePermissionsFile: {\n name: \"deletePermissionsFile\",\n description: \"Delete the permissions configuration file.\",\n parameters: permissionsToolSchemas.deletePermissionsFile,\n execute: async () => {\n const result = await deletePermissionsFile();\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { basename, join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { RULESYNC_RULES_RELATIVE_DIR_PATH } from \"../constants/rulesync-paths.js\";\nimport {\n RulesyncRule,\n type RulesyncRuleFrontmatter,\n type RulesyncRuleFrontmatterInput,\n RulesyncRuleFrontmatterSchema,\n} from \"../features/rules/rulesync-rule.js\";\nimport { formatError } from \"../utils/error.js\";\nimport {\n checkPathTraversal,\n ensureDir,\n listDirectoryFiles,\n removeFile,\n writeFileContent,\n} from \"../utils/file.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\n\nconst logger = new ConsoleLogger({ verbose: false, silent: true });\n\nconst maxRuleSizeBytes = 1024 * 1024; // 1MB\nconst maxRulesCount = 1000;\n\n/**\n * Tool to list all rules from .rulesync/rules/*.md\n */\nasync function listRules(): Promise<\n Array<{\n relativePathFromCwd: string;\n frontmatter: RulesyncRuleFrontmatter;\n }>\n> {\n const rulesDir = join(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);\n\n try {\n const files = await listDirectoryFiles(rulesDir);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const rules = await Promise.all(\n mdFiles.map(async (file) => {\n try {\n // Read the rule file using RulesyncRule\n const rule = await RulesyncRule.fromFile({\n relativeFilePath: file,\n validate: true,\n });\n\n const frontmatter = rule.getFrontmatter();\n\n return {\n relativePathFromCwd: join(RULESYNC_RULES_RELATIVE_DIR_PATH, file),\n frontmatter,\n };\n } catch (error) {\n logger.error(`Failed to read rule file ${file}: ${formatError(error)}`);\n return null;\n }\n }),\n );\n\n // Filter out null values (failed reads)\n return rules.filter((rule): rule is NonNullable<typeof rule> => rule !== null);\n } catch (error) {\n logger.error(\n `Failed to read rules directory (${RULESYNC_RULES_RELATIVE_DIR_PATH}): ${formatError(error)}`,\n );\n return [];\n }\n}\n\n/**\n * Tool to get detailed information about a specific rule\n */\nasync function getRule({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncRuleFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n try {\n const rule = await RulesyncRule.fromFile({\n relativeFilePath: filename,\n validate: true,\n });\n\n return {\n relativePathFromCwd: join(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),\n frontmatter: rule.getFrontmatter(),\n body: rule.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to read rule file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to create or update a rule (upsert operation)\n */\nasync function putRule({\n relativePathFromCwd,\n frontmatter,\n body,\n}: {\n relativePathFromCwd: string;\n frontmatter: RulesyncRuleFrontmatterInput;\n body: string;\n}): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncRuleFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n // Check file size constraint\n const estimatedSize = JSON.stringify(frontmatter).length + body.length;\n if (estimatedSize > maxRuleSizeBytes) {\n throw new Error(\n `Rule size ${estimatedSize} bytes exceeds maximum ${maxRuleSizeBytes} bytes (1MB) for ${relativePathFromCwd}`,\n );\n }\n\n try {\n // Check rule count constraint\n const existingRules = await listRules();\n const isUpdate = existingRules.some(\n (rule) => rule.relativePathFromCwd === join(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),\n );\n\n if (!isUpdate && existingRules.length >= maxRulesCount) {\n throw new Error(\n `Maximum number of rules (${maxRulesCount}) reached in ${RULESYNC_RULES_RELATIVE_DIR_PATH}`,\n );\n }\n\n // Create a new RulesyncRule instance\n const rule = new RulesyncRule({\n outputRoot: process.cwd(),\n relativeDirPath: RULESYNC_RULES_RELATIVE_DIR_PATH,\n relativeFilePath: filename,\n frontmatter,\n body,\n validate: true,\n });\n\n // Ensure directory exists\n const rulesDir = join(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);\n await ensureDir(rulesDir);\n\n // Write the file\n await writeFileContent(rule.getFilePath(), rule.getFileContent());\n\n return {\n relativePathFromCwd: join(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),\n frontmatter: rule.getFrontmatter(),\n body: rule.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to write rule file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to delete a rule\n */\nasync function deleteRule({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n const fullPath = join(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH, filename);\n\n try {\n await removeFile(fullPath);\n\n return {\n relativePathFromCwd: join(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),\n };\n } catch (error) {\n throw new Error(`Failed to delete rule file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Schema for rule-related tool parameters\n */\nconst ruleToolSchemas = {\n listRules: z.object({}),\n getRule: z.object({\n relativePathFromCwd: z.string(),\n }),\n putRule: z.object({\n relativePathFromCwd: z.string(),\n frontmatter: RulesyncRuleFrontmatterSchema,\n body: z.string(),\n }),\n deleteRule: z.object({\n relativePathFromCwd: z.string(),\n }),\n} as const;\n\n/**\n * Tool definitions for rule-related operations\n */\nexport const ruleTools = {\n listRules: {\n name: \"listRules\",\n description: `List all rules from ${join(RULESYNC_RULES_RELATIVE_DIR_PATH, \"*.md\")} with their frontmatter.`,\n parameters: ruleToolSchemas.listRules,\n execute: async () => {\n const rules = await listRules();\n const output = { rules };\n return JSON.stringify(output, null, 2);\n },\n },\n getRule: {\n name: \"getRule\",\n description:\n \"Get detailed information about a specific rule. relativePathFromCwd parameter is required.\",\n parameters: ruleToolSchemas.getRule,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await getRule({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n putRule: {\n name: \"putRule\",\n description:\n \"Create or update a rule (upsert operation). relativePathFromCwd, frontmatter, and body parameters are required.\",\n parameters: ruleToolSchemas.putRule,\n execute: async (args: {\n relativePathFromCwd: string;\n frontmatter: RulesyncRuleFrontmatterInput;\n body: string;\n }) => {\n const result = await putRule({\n relativePathFromCwd: args.relativePathFromCwd,\n frontmatter: args.frontmatter,\n body: args.body,\n });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteRule: {\n name: \"deleteRule\",\n description: \"Delete a rule file. relativePathFromCwd parameter is required.\",\n parameters: ruleToolSchemas.deleteRule,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await deleteRule({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { basename, dirname, join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { SKILL_FILE_NAME } from \"../constants/general.js\";\nimport { RULESYNC_SKILLS_RELATIVE_DIR_PATH } from \"../constants/rulesync-paths.js\";\nimport {\n RulesyncSkill,\n type RulesyncSkillFrontmatter,\n RulesyncSkillFrontmatterSchema,\n} from \"../features/skills/rulesync-skill.js\";\nimport { AiDirFile } from \"../types/ai-dir.js\";\nimport { formatError } from \"../utils/error.js\";\nimport {\n checkPathTraversal,\n directoryExists,\n ensureDir,\n findFilesByGlobs,\n removeDirectory,\n writeFileContent,\n} from \"../utils/file.js\";\nimport { stringifyFrontmatter } from \"../utils/frontmatter.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\n\nconst logger = new ConsoleLogger({ verbose: false, silent: true });\n\nconst maxSkillSizeBytes = 1024 * 1024; // 1MB\nconst maxSkillsCount = 1000;\n\n/**\n * Type for other files in MCP API (string-based for easier AI agent use)\n */\ntype McpSkillFile = {\n name: string;\n body: string;\n};\n\n/**\n * Convert AiDirFile to McpSkillFile\n */\nfunction aiDirFileToMcpSkillFile(file: AiDirFile): McpSkillFile {\n return {\n name: file.relativeFilePathToDirPath,\n body: file.fileBuffer.toString(\"utf-8\"),\n };\n}\n\n/**\n * Convert McpSkillFile to AiDirFile\n */\nfunction mcpSkillFileToAiDirFile(file: McpSkillFile): AiDirFile {\n return {\n relativeFilePathToDirPath: file.name,\n fileBuffer: Buffer.from(file.body, \"utf-8\"),\n };\n}\n\n/**\n * Extract directory name from relative path\n * @example \".rulesync/skills/my-skill\" -> \"my-skill\"\n */\nfunction extractDirName(relativeDirPathFromCwd: string): string {\n const dirName = basename(relativeDirPathFromCwd);\n if (!dirName) {\n throw new Error(`Invalid path: ${relativeDirPathFromCwd}`);\n }\n return dirName;\n}\n\n/**\n * Tool to list all skills from .rulesync/skills/\\*\\/SKILL.md\n */\nasync function listSkills(): Promise<\n Array<{\n relativeDirPathFromCwd: string;\n frontmatter: RulesyncSkillFrontmatter;\n }>\n> {\n const skillsDir = join(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH);\n\n try {\n // Find all skill directories (directories containing SKILL.md)\n const skillDirPaths = await findFilesByGlobs(join(skillsDir, \"*\"), { type: \"dir\" });\n\n const skills = await Promise.all(\n skillDirPaths.map(async (dirPath) => {\n const dirName = basename(dirPath);\n if (!dirName) return null;\n try {\n // Read the skill using RulesyncSkill\n const skill = await RulesyncSkill.fromDir({\n dirName,\n });\n\n const frontmatter = skill.getFrontmatter();\n\n return {\n relativeDirPathFromCwd: join(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),\n frontmatter,\n };\n } catch (error) {\n logger.error(`Failed to read skill directory ${dirName}: ${formatError(error)}`);\n return null;\n }\n }),\n );\n\n // Filter out null values (failed reads)\n return skills.filter((skill): skill is NonNullable<typeof skill> => skill !== null);\n } catch (error) {\n logger.error(\n `Failed to read skills directory (${RULESYNC_SKILLS_RELATIVE_DIR_PATH}): ${formatError(error)}`,\n );\n return [];\n }\n}\n\n/**\n * Tool to get detailed information about a specific skill\n */\nasync function getSkill({ relativeDirPathFromCwd }: { relativeDirPathFromCwd: string }): Promise<{\n relativeDirPathFromCwd: string;\n frontmatter: RulesyncSkillFrontmatter;\n body: string;\n otherFiles: McpSkillFile[];\n}> {\n checkPathTraversal({\n relativePath: relativeDirPathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const dirName = extractDirName(relativeDirPathFromCwd);\n\n try {\n const skill = await RulesyncSkill.fromDir({\n dirName,\n });\n\n return {\n relativeDirPathFromCwd: join(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),\n frontmatter: skill.getFrontmatter(),\n body: skill.getBody(),\n otherFiles: skill.getOtherFiles().map(aiDirFileToMcpSkillFile),\n };\n } catch (error) {\n throw new Error(\n `Failed to read skill directory ${relativeDirPathFromCwd}: ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to create or update a skill (upsert operation)\n */\nasync function putSkill({\n relativeDirPathFromCwd,\n frontmatter,\n body,\n otherFiles = [],\n}: {\n relativeDirPathFromCwd: string;\n frontmatter: RulesyncSkillFrontmatter;\n body: string;\n otherFiles?: McpSkillFile[];\n}): Promise<{\n relativeDirPathFromCwd: string;\n frontmatter: RulesyncSkillFrontmatter;\n body: string;\n otherFiles: McpSkillFile[];\n}> {\n checkPathTraversal({\n relativePath: relativeDirPathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const dirName = extractDirName(relativeDirPathFromCwd);\n\n // Check file size constraint\n const estimatedSize =\n JSON.stringify(frontmatter).length +\n body.length +\n otherFiles.reduce((acc, file) => acc + file.name.length + file.body.length, 0);\n if (estimatedSize > maxSkillSizeBytes) {\n throw new Error(\n `Skill size ${estimatedSize} bytes exceeds maximum ${maxSkillSizeBytes} bytes (1MB) for ${relativeDirPathFromCwd}`,\n );\n }\n\n try {\n // Check skill count constraint\n const existingSkills = await listSkills();\n const isUpdate = existingSkills.some(\n (skill) => skill.relativeDirPathFromCwd === join(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),\n );\n\n if (!isUpdate && existingSkills.length >= maxSkillsCount) {\n throw new Error(\n `Maximum number of skills (${maxSkillsCount}) reached in ${RULESYNC_SKILLS_RELATIVE_DIR_PATH}`,\n );\n }\n\n // Convert McpSkillFile to AiDirFile for RulesyncSkill\n const aiDirFiles = otherFiles.map(mcpSkillFileToAiDirFile);\n\n // Create a new RulesyncSkill instance for validation\n const skill = new RulesyncSkill({\n outputRoot: process.cwd(),\n relativeDirPath: RULESYNC_SKILLS_RELATIVE_DIR_PATH,\n dirName,\n frontmatter,\n body,\n otherFiles: aiDirFiles,\n validate: true,\n });\n\n // Ensure skill directory exists\n const skillDirPath = join(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName);\n await ensureDir(skillDirPath);\n\n // Write the SKILL.md file\n const skillFilePath = join(skillDirPath, SKILL_FILE_NAME);\n const skillFileContent = stringifyFrontmatter(body, frontmatter);\n await writeFileContent(skillFilePath, skillFileContent);\n\n // Write other files\n for (const file of otherFiles) {\n // Validate file path to prevent path traversal\n checkPathTraversal({\n relativePath: file.name,\n intendedRootDir: skillDirPath,\n });\n const filePath = join(skillDirPath, file.name);\n // Ensure subdirectory exists if file has path separators\n const fileDir = join(skillDirPath, dirname(file.name));\n if (fileDir !== skillDirPath) {\n await ensureDir(fileDir);\n }\n await writeFileContent(filePath, file.body);\n }\n\n return {\n relativeDirPathFromCwd: join(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),\n frontmatter: skill.getFrontmatter(),\n body: skill.getBody(),\n otherFiles: skill.getOtherFiles().map(aiDirFileToMcpSkillFile),\n };\n } catch (error) {\n throw new Error(\n `Failed to write skill directory ${relativeDirPathFromCwd}: ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to delete a skill\n */\nasync function deleteSkill({\n relativeDirPathFromCwd,\n}: {\n relativeDirPathFromCwd: string;\n}): Promise<{\n relativeDirPathFromCwd: string;\n}> {\n checkPathTraversal({\n relativePath: relativeDirPathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const dirName = extractDirName(relativeDirPathFromCwd);\n const skillDirPath = join(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName);\n\n try {\n // Check if skill directory exists before attempting to delete\n if (await directoryExists(skillDirPath)) {\n await removeDirectory(skillDirPath);\n }\n\n return {\n relativeDirPathFromCwd: join(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),\n };\n } catch (error) {\n throw new Error(\n `Failed to delete skill directory ${relativeDirPathFromCwd}: ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Schema for other files in a skill directory\n */\nconst McpSkillFileSchema = z.object({\n name: z.string(),\n body: z.string(),\n});\n\n/**\n * Schema for skill-related tool parameters\n */\nconst skillToolSchemas = {\n listSkills: z.object({}),\n getSkill: z.object({\n relativeDirPathFromCwd: z.string(),\n }),\n putSkill: z.object({\n relativeDirPathFromCwd: z.string(),\n frontmatter: RulesyncSkillFrontmatterSchema,\n body: z.string(),\n otherFiles: z.optional(z.array(McpSkillFileSchema)),\n }),\n deleteSkill: z.object({\n relativeDirPathFromCwd: z.string(),\n }),\n} as const;\n\n/**\n * Tool definitions for skill-related operations\n */\nexport const skillTools = {\n listSkills: {\n name: \"listSkills\",\n description: `List all skills from ${join(RULESYNC_SKILLS_RELATIVE_DIR_PATH, \"*\", SKILL_FILE_NAME)} with their frontmatter.`,\n parameters: skillToolSchemas.listSkills,\n execute: async () => {\n const skills = await listSkills();\n const output = { skills };\n return JSON.stringify(output, null, 2);\n },\n },\n getSkill: {\n name: \"getSkill\",\n description:\n \"Get detailed information about a specific skill including SKILL.md content and other files. relativeDirPathFromCwd parameter is required.\",\n parameters: skillToolSchemas.getSkill,\n execute: async (args: { relativeDirPathFromCwd: string }) => {\n const result = await getSkill({ relativeDirPathFromCwd: args.relativeDirPathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n putSkill: {\n name: \"putSkill\",\n description:\n \"Create or update a skill (upsert operation). relativeDirPathFromCwd, frontmatter, and body parameters are required. otherFiles is optional.\",\n parameters: skillToolSchemas.putSkill,\n execute: async (args: {\n relativeDirPathFromCwd: string;\n frontmatter: RulesyncSkillFrontmatter;\n body: string;\n otherFiles?: McpSkillFile[];\n }) => {\n const result = await putSkill({\n relativeDirPathFromCwd: args.relativeDirPathFromCwd,\n frontmatter: args.frontmatter,\n body: args.body,\n otherFiles: args.otherFiles,\n });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteSkill: {\n name: \"deleteSkill\",\n description:\n \"Delete a skill directory and all its contents. relativeDirPathFromCwd parameter is required.\",\n parameters: skillToolSchemas.deleteSkill,\n execute: async (args: { relativeDirPathFromCwd: string }) => {\n const result = await deleteSkill({ relativeDirPathFromCwd: args.relativeDirPathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { basename, join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH } from \"../constants/rulesync-paths.js\";\nimport {\n RulesyncSubagent,\n type RulesyncSubagentFrontmatter,\n RulesyncSubagentFrontmatterSchema,\n} from \"../features/subagents/rulesync-subagent.js\";\nimport { formatError } from \"../utils/error.js\";\nimport {\n checkPathTraversal,\n ensureDir,\n listDirectoryFiles,\n removeFile,\n writeFileContent,\n} from \"../utils/file.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\n\nconst logger = new ConsoleLogger({ verbose: false, silent: true });\n\nconst maxSubagentSizeBytes = 1024 * 1024; // 1MB\nconst maxSubagentsCount = 1000;\n\n/**\n * Tool to list all subagents from .rulesync/subagents/*.md\n */\nasync function listSubagents(): Promise<\n Array<{\n relativePathFromCwd: string;\n frontmatter: RulesyncSubagentFrontmatter;\n }>\n> {\n const subagentsDir = join(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);\n\n try {\n const files = await listDirectoryFiles(subagentsDir);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const subagents = await Promise.all(\n mdFiles.map(async (file) => {\n try {\n // Read the subagent file using RulesyncSubagent\n const subagent = await RulesyncSubagent.fromFile({\n relativeFilePath: file,\n validate: true,\n });\n\n const frontmatter = subagent.getFrontmatter();\n\n return {\n relativePathFromCwd: join(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, file),\n frontmatter,\n };\n } catch (error) {\n logger.error(`Failed to read subagent file ${file}: ${formatError(error)}`);\n return null;\n }\n }),\n );\n\n // Filter out null values (failed reads)\n return subagents.filter(\n (subagent): subagent is NonNullable<typeof subagent> => subagent !== null,\n );\n } catch (error) {\n logger.error(\n `Failed to read subagents directory (${RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH}): ${formatError(error)}`,\n );\n return [];\n }\n}\n\n/**\n * Tool to get detailed information about a specific subagent\n */\nasync function getSubagent({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncSubagentFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n try {\n const subagent = await RulesyncSubagent.fromFile({\n relativeFilePath: filename,\n validate: true,\n });\n\n return {\n relativePathFromCwd: join(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),\n frontmatter: subagent.getFrontmatter(),\n body: subagent.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to read subagent file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to create or update a subagent (upsert operation)\n */\nasync function putSubagent({\n relativePathFromCwd,\n frontmatter,\n body,\n}: {\n relativePathFromCwd: string;\n frontmatter: RulesyncSubagentFrontmatter;\n body: string;\n}): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncSubagentFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n // Check file size constraint\n const estimatedSize = JSON.stringify(frontmatter).length + body.length;\n if (estimatedSize > maxSubagentSizeBytes) {\n throw new Error(\n `Subagent size ${estimatedSize} bytes exceeds maximum ${maxSubagentSizeBytes} bytes (1MB) for ${relativePathFromCwd}`,\n );\n }\n\n try {\n // Check subagent count constraint\n const existingSubagents = await listSubagents();\n const isUpdate = existingSubagents.some(\n (subagent) =>\n subagent.relativePathFromCwd === join(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),\n );\n\n if (!isUpdate && existingSubagents.length >= maxSubagentsCount) {\n throw new Error(\n `Maximum number of subagents (${maxSubagentsCount}) reached in ${RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH}`,\n );\n }\n\n // Create a new RulesyncSubagent instance\n const subagent = new RulesyncSubagent({\n outputRoot: process.cwd(),\n relativeDirPath: RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH,\n relativeFilePath: filename,\n frontmatter,\n body,\n validate: true,\n });\n\n // Ensure directory exists\n const subagentsDir = join(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);\n await ensureDir(subagentsDir);\n\n // Write the file\n await writeFileContent(subagent.getFilePath(), subagent.getFileContent());\n\n return {\n relativePathFromCwd: join(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),\n frontmatter: subagent.getFrontmatter(),\n body: subagent.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to write subagent file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to delete a subagent\n */\nasync function deleteSubagent({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n const fullPath = join(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename);\n\n try {\n await removeFile(fullPath);\n\n return {\n relativePathFromCwd: join(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),\n };\n } catch (error) {\n throw new Error(\n `Failed to delete subagent file ${relativePathFromCwd}: ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Schema for subagent-related tool parameters\n */\nconst subagentToolSchemas = {\n listSubagents: z.object({}),\n getSubagent: z.object({\n relativePathFromCwd: z.string(),\n }),\n putSubagent: z.object({\n relativePathFromCwd: z.string(),\n frontmatter: RulesyncSubagentFrontmatterSchema,\n body: z.string(),\n }),\n deleteSubagent: z.object({\n relativePathFromCwd: z.string(),\n }),\n} as const;\n\n/**\n * Tool definitions for subagent-related operations\n */\nexport const subagentTools = {\n listSubagents: {\n name: \"listSubagents\",\n description: `List all subagents from ${join(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, \"*.md\")} with their frontmatter.`,\n parameters: subagentToolSchemas.listSubagents,\n execute: async () => {\n const subagents = await listSubagents();\n const output = { subagents };\n return JSON.stringify(output, null, 2);\n },\n },\n getSubagent: {\n name: \"getSubagent\",\n description:\n \"Get detailed information about a specific subagent. relativePathFromCwd parameter is required.\",\n parameters: subagentToolSchemas.getSubagent,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await getSubagent({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n putSubagent: {\n name: \"putSubagent\",\n description:\n \"Create or update a subagent (upsert operation). relativePathFromCwd, frontmatter, and body parameters are required.\",\n parameters: subagentToolSchemas.putSubagent,\n execute: async (args: {\n relativePathFromCwd: string;\n frontmatter: RulesyncSubagentFrontmatter;\n body: string;\n }) => {\n const result = await putSubagent({\n relativePathFromCwd: args.relativePathFromCwd,\n frontmatter: args.frontmatter,\n body: args.body,\n });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteSubagent: {\n name: \"deleteSubagent\",\n description: \"Delete a subagent file. relativePathFromCwd parameter is required.\",\n parameters: subagentToolSchemas.deleteSubagent,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await deleteSubagent({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { z } from \"zod/mini\";\n\nimport {\n type RulesyncCheckFrontmatter,\n RulesyncCheckFrontmatterSchema,\n} from \"../features/checks/rulesync-check.js\";\nimport {\n type RulesyncCommandFrontmatter,\n RulesyncCommandFrontmatterSchema,\n} from \"../features/commands/rulesync-command.js\";\nimport {\n type RulesyncRuleFrontmatter,\n RulesyncRuleFrontmatterSchema,\n} from \"../features/rules/rulesync-rule.js\";\nimport {\n type RulesyncSkillFrontmatter,\n RulesyncSkillFrontmatterSchema,\n} from \"../features/skills/rulesync-skill.js\";\nimport {\n type RulesyncSubagentFrontmatter,\n RulesyncSubagentFrontmatterSchema,\n} from \"../features/subagents/rulesync-subagent.js\";\nimport { checkTools } from \"./checks.js\";\nimport { commandTools } from \"./commands.js\";\nimport { convertOptionsSchema, convertTools } from \"./convert.js\";\nimport { generateOptionsSchema, generateTools } from \"./generate.js\";\nimport { hooksTools } from \"./hooks.js\";\nimport { ignoreTools } from \"./ignore.js\";\nimport { importOptionsSchema, importTools } from \"./import.js\";\nimport { mcpTools } from \"./mcp.js\";\nimport { permissionsTools } from \"./permissions.js\";\nimport { ruleTools } from \"./rules.js\";\nimport { skillTools } from \"./skills.js\";\nimport { subagentTools } from \"./subagents.js\";\n\nconst rulesyncFeatureSchema = z.enum([\n \"rule\",\n \"command\",\n \"subagent\",\n \"skill\",\n \"check\",\n \"ignore\",\n \"mcp\",\n \"permissions\",\n \"hooks\",\n \"generate\",\n \"import\",\n \"convert\",\n]);\n\nconst rulesyncOperationSchema = z.enum([\"list\", \"get\", \"put\", \"delete\", \"run\"]);\n\nconst skillFileSchema = z.object({\n name: z.string(),\n body: z.string(),\n});\n\nconst rulesyncToolSchema = z.object({\n feature: rulesyncFeatureSchema,\n operation: rulesyncOperationSchema,\n targetPathFromCwd: z.optional(z.string()),\n frontmatter: z.optional(z.unknown()),\n body: z.optional(z.string()),\n otherFiles: z.optional(z.array(skillFileSchema)),\n content: z.optional(z.string()),\n generateOptions: z.optional(generateOptionsSchema),\n importOptions: z.optional(importOptionsSchema),\n convertOptions: z.optional(convertOptionsSchema),\n});\n\ntype RulesyncFeature = z.infer<typeof rulesyncFeatureSchema>;\ntype RulesyncOperation = z.infer<typeof rulesyncOperationSchema>;\ntype RulesyncToolArgs = z.infer<typeof rulesyncToolSchema>;\ntype RulesyncFrontmatterFeature = Exclude<\n RulesyncFeature,\n \"ignore\" | \"mcp\" | \"permissions\" | \"hooks\" | \"generate\" | \"import\" | \"convert\"\n>;\ntype RulesyncFrontmatterByFeature = {\n rule: RulesyncRuleFrontmatter;\n command: RulesyncCommandFrontmatter;\n subagent: RulesyncSubagentFrontmatter;\n skill: RulesyncSkillFrontmatter;\n check: RulesyncCheckFrontmatter;\n};\n\nconst supportedOperationsByFeature: Record<RulesyncFeature, RulesyncOperation[]> = {\n rule: [\"list\", \"get\", \"put\", \"delete\"],\n command: [\"list\", \"get\", \"put\", \"delete\"],\n subagent: [\"list\", \"get\", \"put\", \"delete\"],\n skill: [\"list\", \"get\", \"put\", \"delete\"],\n check: [\"list\", \"get\", \"put\", \"delete\"],\n ignore: [\"get\", \"put\", \"delete\"],\n mcp: [\"get\", \"put\", \"delete\"],\n permissions: [\"get\", \"put\", \"delete\"],\n hooks: [\"get\", \"put\", \"delete\"],\n generate: [\"run\"],\n import: [\"run\"],\n convert: [\"run\"],\n};\n\nfunction assertSupported({\n feature,\n operation,\n}: {\n feature: RulesyncFeature;\n operation: RulesyncOperation;\n}): void {\n const supportedOperations = supportedOperationsByFeature[feature];\n\n if (!supportedOperations.includes(operation)) {\n throw new Error(\n `Operation ${operation} is not supported for feature ${feature}. Supported operations: ${supportedOperations.join(\n \", \",\n )}`,\n );\n }\n}\n\nfunction requireTargetPath({ targetPathFromCwd, feature, operation }: RulesyncToolArgs): string {\n if (!targetPathFromCwd) {\n throw new Error(`targetPathFromCwd is required for ${feature} ${operation} operation`);\n }\n\n return targetPathFromCwd;\n}\n\nfunction parseFrontmatter({\n feature,\n frontmatter,\n}: {\n feature: \"rule\";\n frontmatter: unknown;\n}): RulesyncRuleFrontmatter;\nfunction parseFrontmatter({\n feature,\n frontmatter,\n}: {\n feature: \"command\";\n frontmatter: unknown;\n}): RulesyncCommandFrontmatter;\nfunction parseFrontmatter({\n feature,\n frontmatter,\n}: {\n feature: \"subagent\";\n frontmatter: unknown;\n}): RulesyncSubagentFrontmatter;\nfunction parseFrontmatter({\n feature,\n frontmatter,\n}: {\n feature: \"skill\";\n frontmatter: unknown;\n}): RulesyncSkillFrontmatter;\nfunction parseFrontmatter({\n feature,\n frontmatter,\n}: {\n feature: \"check\";\n frontmatter: unknown;\n}): RulesyncCheckFrontmatter;\nfunction parseFrontmatter<Feature extends RulesyncFrontmatterFeature>({\n feature,\n frontmatter,\n}: {\n feature: Feature;\n frontmatter: unknown;\n}): RulesyncFrontmatterByFeature[Feature];\nfunction parseFrontmatter({\n feature,\n frontmatter,\n}: {\n feature: RulesyncFrontmatterFeature;\n frontmatter: unknown;\n}): RulesyncFrontmatterByFeature[RulesyncFrontmatterFeature] {\n switch (feature) {\n case \"rule\": {\n return RulesyncRuleFrontmatterSchema.parse(frontmatter);\n }\n case \"command\": {\n return RulesyncCommandFrontmatterSchema.parse(frontmatter);\n }\n case \"subagent\": {\n return RulesyncSubagentFrontmatterSchema.parse(frontmatter);\n }\n case \"skill\": {\n return RulesyncSkillFrontmatterSchema.parse(frontmatter);\n }\n case \"check\": {\n return RulesyncCheckFrontmatterSchema.parse(frontmatter);\n }\n }\n}\n\nfunction ensureBody({ body, feature, operation }: RulesyncToolArgs): string {\n if (!body) {\n throw new Error(`body is required for ${feature} ${operation} operation`);\n }\n\n return body;\n}\n\nfunction requireContent({\n content,\n feature,\n}: {\n content: string | undefined;\n feature: string;\n}): string {\n if (!content) {\n throw new Error(`content is required for ${feature} put operation`);\n }\n\n return content;\n}\n\nfunction executeRule(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"list\") {\n return ruleTools.listRules.execute();\n }\n\n if (parsed.operation === \"get\") {\n return ruleTools.getRule.execute({ relativePathFromCwd: requireTargetPath(parsed) });\n }\n\n if (parsed.operation === \"put\") {\n return ruleTools.putRule.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n frontmatter: parseFrontmatter({\n feature: \"rule\",\n frontmatter: parsed.frontmatter ?? {},\n }),\n body: ensureBody(parsed),\n });\n }\n\n return ruleTools.deleteRule.execute({ relativePathFromCwd: requireTargetPath(parsed) });\n}\n\nfunction executeCommand(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"list\") {\n return commandTools.listCommands.execute();\n }\n\n if (parsed.operation === \"get\") {\n return commandTools.getCommand.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n });\n }\n\n if (parsed.operation === \"put\") {\n return commandTools.putCommand.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n frontmatter: parseFrontmatter({\n feature: \"command\",\n frontmatter: parsed.frontmatter ?? {},\n }),\n body: ensureBody(parsed),\n });\n }\n\n return commandTools.deleteCommand.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n });\n}\n\nfunction executeSubagent(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"list\") {\n return subagentTools.listSubagents.execute();\n }\n\n if (parsed.operation === \"get\") {\n return subagentTools.getSubagent.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n });\n }\n\n if (parsed.operation === \"put\") {\n return subagentTools.putSubagent.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n frontmatter: parseFrontmatter({\n feature: \"subagent\",\n frontmatter: parsed.frontmatter ?? {},\n }),\n body: ensureBody(parsed),\n });\n }\n\n return subagentTools.deleteSubagent.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n });\n}\n\nfunction executeSkill(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"list\") {\n return skillTools.listSkills.execute();\n }\n\n if (parsed.operation === \"get\") {\n return skillTools.getSkill.execute({ relativeDirPathFromCwd: requireTargetPath(parsed) });\n }\n\n if (parsed.operation === \"put\") {\n return skillTools.putSkill.execute({\n relativeDirPathFromCwd: requireTargetPath(parsed),\n frontmatter: parseFrontmatter({\n feature: \"skill\",\n frontmatter: parsed.frontmatter ?? {},\n }),\n body: ensureBody(parsed),\n otherFiles: parsed.otherFiles ?? [],\n });\n }\n\n return skillTools.deleteSkill.execute({\n relativeDirPathFromCwd: requireTargetPath(parsed),\n });\n}\n\nfunction executeCheck(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"list\") {\n return checkTools.listChecks.execute();\n }\n\n if (parsed.operation === \"get\") {\n return checkTools.getCheck.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n });\n }\n\n if (parsed.operation === \"put\") {\n return checkTools.putCheck.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n frontmatter: parseFrontmatter({\n feature: \"check\",\n frontmatter: parsed.frontmatter ?? {},\n }),\n body: ensureBody(parsed),\n });\n }\n\n return checkTools.deleteCheck.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n });\n}\n\nfunction executeIgnore(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"get\") {\n return ignoreTools.getIgnoreFile.execute();\n }\n\n if (parsed.operation === \"put\") {\n return ignoreTools.putIgnoreFile.execute({\n content: requireContent({ content: parsed.content, feature: \"ignore\" }),\n });\n }\n\n return ignoreTools.deleteIgnoreFile.execute();\n}\n\nfunction executeMcp(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"get\") {\n return mcpTools.getMcpFile.execute();\n }\n\n if (parsed.operation === \"put\") {\n return mcpTools.putMcpFile.execute({\n content: requireContent({ content: parsed.content, feature: \"mcp\" }),\n });\n }\n\n return mcpTools.deleteMcpFile.execute();\n}\n\nfunction executePermissions(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"get\") {\n return permissionsTools.getPermissionsFile.execute();\n }\n\n if (parsed.operation === \"put\") {\n return permissionsTools.putPermissionsFile.execute({\n content: requireContent({ content: parsed.content, feature: \"permissions\" }),\n });\n }\n\n return permissionsTools.deletePermissionsFile.execute();\n}\n\nfunction executeHooks(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"get\") {\n return hooksTools.getHooksFile.execute();\n }\n\n if (parsed.operation === \"put\") {\n return hooksTools.putHooksFile.execute({\n content: requireContent({ content: parsed.content, feature: \"hooks\" }),\n });\n }\n\n return hooksTools.deleteHooksFile.execute();\n}\n\nfunction executeGenerate(parsed: RulesyncToolArgs) {\n // Only \"run\" operation is supported for generate feature\n return generateTools.executeGenerate.execute(parsed.generateOptions ?? {});\n}\n\nfunction executeImport(parsed: RulesyncToolArgs) {\n // Only \"run\" operation is supported for import feature\n if (!parsed.importOptions) {\n throw new Error(\"importOptions is required for import feature\");\n }\n return importTools.executeImport.execute(parsed.importOptions);\n}\n\nfunction executeConvert(parsed: RulesyncToolArgs) {\n // Only \"run\" operation is supported for convert feature\n if (!parsed.convertOptions) {\n throw new Error(\"convertOptions is required for convert feature\");\n }\n return convertTools.executeConvert.execute(parsed.convertOptions);\n}\n\nconst featureExecutors: Record<RulesyncFeature, (parsed: RulesyncToolArgs) => Promise<string>> = {\n rule: executeRule,\n command: executeCommand,\n subagent: executeSubagent,\n skill: executeSkill,\n check: executeCheck,\n ignore: executeIgnore,\n mcp: executeMcp,\n permissions: executePermissions,\n hooks: executeHooks,\n generate: executeGenerate,\n import: executeImport,\n convert: executeConvert,\n};\n\nexport const rulesyncTool = {\n name: \"rulesyncTool\",\n description:\n \"Manage Rulesync files through a single MCP tool. Features: rule/command/subagent/skill/check support list/get/put/delete; ignore/mcp/permissions/hooks support get/put/delete only; generate supports run only; import supports run only; convert supports run only. Parameters: list requires no targetPathFromCwd (lists all items); get/delete require targetPathFromCwd; put requires targetPathFromCwd, frontmatter, and body (or content for ignore/mcp/permissions/hooks); generate/run uses generateOptions to configure generation; import/run uses importOptions to configure import; convert/run uses convertOptions to configure conversion.\",\n parameters: rulesyncToolSchema,\n execute: async (args: RulesyncToolArgs) => {\n const parsed = rulesyncToolSchema.parse(args);\n\n assertSupported({ feature: parsed.feature, operation: parsed.operation });\n\n const executor = featureExecutors[parsed.feature];\n if (!executor) {\n throw new Error(`Unknown feature: ${parsed.feature}`);\n }\n\n return executor(parsed);\n },\n} as const;\n","import { FastMCP } from \"fastmcp\";\n\nimport { rulesyncTool } from \"../../mcp/tools.js\";\nimport type { Logger } from \"../../utils/logger.js\";\n\n/**\n * MCP command that starts the MCP server\n */\nexport async function mcpCommand(logger: Logger, { version }: { version: string }): Promise<void> {\n const server = new FastMCP({\n name: \"Rulesync MCP Server\",\n version: version as `${number}.${number}.${number}`,\n instructions:\n \"This server handles Rulesync files including rules, commands, MCP, ignore files, subagents and skills for any AI agents. It should be used when you need those files.\",\n });\n\n server.addTool(rulesyncTool);\n\n // Start server with stdio transport (for spawned processes)\n logger.info(\"Rulesync MCP server started via stdio\");\n\n // Start the server - this blocks execution and runs the MCP server\n // The void operator explicitly marks this as intentionally not awaited\n void server.start({\n transportType: \"stdio\",\n });\n}\n","import { join } from \"node:path\";\n\nimport { ConfigResolver } from \"../../config/config-resolver.js\";\nimport {\n RULESYNC_CONFIG_RELATIVE_FILE_PATH,\n RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH,\n} from \"../../constants/rulesync-paths.js\";\nimport { fileExists } from \"../../utils/file.js\";\n\nexport type ResolveGitignoreTargetsParams = {\n readonly cliTargets: readonly string[] | undefined;\n readonly cwd?: string;\n};\n\n/**\n * Resolve the list of targets to pass to `gitignoreCommand`.\n *\n * Precedence:\n * 1. Explicit `--targets` CLI option wins.\n * 2. If neither rulesync.jsonc nor rulesync.local.jsonc exists, return\n * `undefined` so all supported tools' entries are emitted. Otherwise a\n * user without a config file would silently get only the default\n * `[\"agentsmd\"]` target, which is a surprising behavior change.\n * 3. If `gitignoreTargetsOnly` is true (the default), return the config's\n * `targets` with `agentsmd` always appended. `AGENTS.md` is a de facto\n * standard file read by many AI tools regardless of which targets the\n * user selected, so its gitignore entries must always be emitted to\n * prevent accidental commits of generated rule files.\n * 4. Otherwise return `undefined` to emit entries for every supported tool.\n */\nexport const resolveGitignoreTargets = async ({\n cliTargets,\n cwd = process.cwd(),\n}: ResolveGitignoreTargetsParams): Promise<readonly string[] | undefined> => {\n if (cliTargets !== undefined) {\n return cliTargets;\n }\n\n const baseConfigPath = join(cwd, RULESYNC_CONFIG_RELATIVE_FILE_PATH);\n const localConfigPath = join(cwd, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH);\n const [hasBase, hasLocal] = await Promise.all([\n fileExists(baseConfigPath),\n fileExists(localConfigPath),\n ]);\n\n if (!hasBase && !hasLocal) {\n return undefined;\n }\n\n const config = await ConfigResolver.resolve({});\n if (config.getGitignoreTargetsOnly()) {\n const targets = config.getTargets();\n if (targets.includes(\"agentsmd\")) {\n return targets;\n }\n return [...targets, \"agentsmd\"];\n }\n return undefined;\n};\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { Readable, Transform } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\n\nimport type { GitHubRelease, GitHubReleaseAsset } from \"../types/fetch.js\";\nimport { GitHubClient } from \"./github-client.js\";\n\nconst RULESYNC_REPO_OWNER = \"dyoshikawa\";\nconst RULESYNC_REPO_NAME = \"rulesync\";\n\n/**\n * GitHub releases URL for manual download instructions\n */\nconst RELEASES_URL = `https://github.com/${RULESYNC_REPO_OWNER}/${RULESYNC_REPO_NAME}/releases`;\n\n/**\n * Maximum download size (500MB) to prevent memory exhaustion\n */\nconst MAX_DOWNLOAD_SIZE = 500 * 1024 * 1024;\n\n/**\n * Allowed domains for downloading release assets\n */\nconst ALLOWED_DOWNLOAD_DOMAINS = [\n \"github.com\",\n \"objects.githubusercontent.com\",\n \"github-releases.githubusercontent.com\",\n \"release-assets.githubusercontent.com\",\n];\n\n/**\n * Execution environment types for rulesync\n */\nexport type ExecutionEnvironment = \"single-binary\" | \"homebrew\" | \"npm\";\n\n/**\n * Update check result\n */\nexport type UpdateCheckResult = {\n currentVersion: string;\n latestVersion: string;\n hasUpdate: boolean;\n release: GitHubRelease;\n};\n\n/**\n * Custom error for permission issues during update\n */\nexport class UpdatePermissionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"UpdatePermissionError\";\n }\n}\n\n/**\n * Detect the execution environment of rulesync.\n *\n * Uses process.execPath (the Node.js/Bun binary) and process.argv[1] (the script being executed)\n * to determine how rulesync was installed.\n */\nexport function detectExecutionEnvironment(): ExecutionEnvironment {\n const execPath = process.execPath;\n const scriptPath = process.argv[1] ?? \"\";\n\n // Single binary detection: the executable itself is named rulesync\n const isRulesyncBinary = /rulesync(-[a-z0-9]+(-[a-z0-9]+)?)?(\\.exe)?$/i.test(execPath);\n if (isRulesyncBinary) {\n // Check if the rulesync binary itself is in a Homebrew path\n if (execPath.includes(\"/homebrew/\") || execPath.includes(\"/Cellar/\")) {\n return \"homebrew\";\n }\n return \"single-binary\";\n }\n\n // Homebrew detection via script path: e.g. /opt/homebrew/lib/node_modules/rulesync/...\n if (\n (scriptPath.includes(\"/homebrew/\") || scriptPath.includes(\"/Cellar/\")) &&\n scriptPath.includes(\"rulesync\")\n ) {\n return \"homebrew\";\n }\n\n return \"npm\";\n}\n\n/**\n * Get the asset name for the current platform\n */\nexport function getPlatformAssetName(): string | null {\n const platform = os.platform();\n const arch = os.arch();\n\n // Map Node.js platform/arch to asset names\n const platformMap: Record<string, string> = {\n darwin: \"darwin\",\n linux: \"linux\",\n win32: \"windows\",\n };\n\n const archMap: Record<string, string> = {\n x64: \"x64\",\n arm64: \"arm64\",\n };\n\n const platformName = platformMap[platform];\n const archName = archMap[arch];\n\n if (!platformName || !archName) {\n return null;\n }\n\n const extension = platform === \"win32\" ? \".exe\" : \"\";\n return `rulesync-${platformName}-${archName}${extension}`;\n}\n\n/**\n * Normalize version string by removing leading 'v' and stripping pre-release suffix\n */\nexport function normalizeVersion(v: string): string {\n // Remove leading 'v' and strip pre-release suffix (e.g., \"1.2.3-beta.1\" -> \"1.2.3\")\n return v.replace(/^v/, \"\").replace(/-.*$/, \"\");\n}\n\n/**\n * Compare semantic versions\n * Returns: 1 if a > b, -1 if a < b, 0 if equal\n */\nexport function compareVersions(a: string, b: string): number {\n const aParts = normalizeVersion(a).split(\".\").map(Number);\n const bParts = normalizeVersion(b).split(\".\").map(Number);\n\n for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {\n const aNum = aParts[i] ?? 0;\n const bNum = bParts[i] ?? 0;\n if (!Number.isFinite(aNum) || !Number.isFinite(bNum)) {\n throw new Error(`Invalid version format: cannot compare \"${a}\" and \"${b}\"`);\n }\n if (aNum > bNum) return 1;\n if (aNum < bNum) return -1;\n }\n return 0;\n}\n\n/**\n * Validate that a download URL is safe (HTTPS + allowed GitHub domain + repo path for github.com)\n */\nexport function validateDownloadUrl(url: string): void {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw new Error(`Invalid download URL: ${url}`);\n }\n\n if (parsed.protocol !== \"https:\") {\n throw new Error(`Download URL must use HTTPS: ${url}`);\n }\n\n const isAllowed = ALLOWED_DOWNLOAD_DOMAINS.some((domain) => parsed.hostname === domain);\n if (!isAllowed) {\n throw new Error(\n `Download URL domain \"${parsed.hostname}\" is not in the allowed list: ${ALLOWED_DOWNLOAD_DOMAINS.join(\", \")}`,\n );\n }\n\n // For github.com URLs, validate the path starts with the expected repo\n if (parsed.hostname === \"github.com\") {\n const expectedPrefix = `/${RULESYNC_REPO_OWNER}/${RULESYNC_REPO_NAME}/`;\n if (!parsed.pathname.startsWith(expectedPrefix)) {\n throw new Error(\n `Download URL path must belong to ${RULESYNC_REPO_OWNER}/${RULESYNC_REPO_NAME}: ${url}`,\n );\n }\n }\n}\n\n/**\n * Check for updates\n */\nexport async function checkForUpdate(\n currentVersion: string,\n token?: string,\n): Promise<UpdateCheckResult> {\n const client = new GitHubClient({\n token: GitHubClient.resolveToken(token),\n });\n\n const release = await client.getLatestRelease(RULESYNC_REPO_OWNER, RULESYNC_REPO_NAME);\n const latestVersion = normalizeVersion(release.tag_name);\n const normalizedCurrentVersion = normalizeVersion(currentVersion);\n\n return {\n currentVersion: normalizedCurrentVersion,\n latestVersion,\n hasUpdate: compareVersions(latestVersion, normalizedCurrentVersion) > 0,\n release,\n };\n}\n\n/**\n * Find asset by name in release\n */\nfunction findAsset(release: GitHubRelease, assetName: string): GitHubReleaseAsset | null {\n return release.assets.find((asset) => asset.name === assetName) ?? null;\n}\n\n/**\n * Download a file from URL to a destination path using streaming to limit memory usage.\n * Validates both the initial URL and the final URL after redirects.\n */\nasync function downloadFile(url: string, destPath: string): Promise<void> {\n validateDownloadUrl(url);\n\n const response = await fetch(url, {\n redirect: \"follow\",\n });\n\n if (!response.ok) {\n throw new Error(`Failed to download ${url}: HTTP ${response.status}`);\n }\n\n // Validate the final URL after redirects to prevent redirect-based bypass\n if (response.url) {\n validateDownloadUrl(response.url);\n }\n\n const contentLength = response.headers.get(\"content-length\");\n if (contentLength && Number(contentLength) > MAX_DOWNLOAD_SIZE) {\n throw new Error(\n `Download too large: ${contentLength} bytes exceeds limit of ${MAX_DOWNLOAD_SIZE} bytes`,\n );\n }\n\n if (!response.body) {\n throw new Error(\"Response body is empty\");\n }\n\n // Stream the response to file with a size limit check\n const fileStream = fs.createWriteStream(destPath);\n let downloadedBytes = 0;\n\n const bodyReader = Readable.fromWeb(response.body as import(\"node:stream/web\").ReadableStream);\n\n const sizeChecker = new Transform({\n transform(chunk, _encoding, callback) {\n downloadedBytes += (chunk as Buffer).length;\n if (downloadedBytes > MAX_DOWNLOAD_SIZE) {\n callback(\n new Error(\n `Download too large: exceeded limit of ${MAX_DOWNLOAD_SIZE} bytes during streaming`,\n ),\n );\n return;\n }\n callback(null, chunk);\n },\n });\n\n await pipeline(bodyReader, sizeChecker, fileStream);\n}\n\n/**\n * Calculate SHA256 checksum of a file\n */\nasync function calculateSha256(filePath: string): Promise<string> {\n const content = await fs.promises.readFile(filePath);\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\n/**\n * Parse SHA256SUMS file content\n */\nexport function parseSha256Sums(content: string): Map<string, string> {\n const result = new Map<string, string>();\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n // Format: \"hash filename\" (two spaces between hash and filename)\n const match = /^([a-f0-9]{64})\\s+(.+)$/.exec(trimmed);\n if (match && match[1] && match[2]) {\n result.set(match[2].trim(), match[1]);\n }\n }\n return result;\n}\n\n/**\n * Update options\n */\nexport type UpdateOptions = {\n force?: boolean;\n token?: string;\n};\n\n/**\n * Resolve the platform binary asset and the mandatory SHA256SUMS asset from a\n * release, throwing with manual-download guidance when either is unavailable.\n */\nfunction resolveUpdateAssets(release: GitHubRelease): {\n assetName: string;\n binaryAsset: GitHubReleaseAsset;\n checksumAsset: GitHubReleaseAsset;\n} {\n // Get platform-specific asset name\n const assetName = getPlatformAssetName();\n if (!assetName) {\n throw new Error(\n `Unsupported platform: ${os.platform()} ${os.arch()}. Please download manually from ${RELEASES_URL}`,\n );\n }\n\n // Find the binary asset\n const binaryAsset = findAsset(release, assetName);\n if (!binaryAsset) {\n throw new Error(\n `Binary for ${assetName} not found in release. Please download manually from ${RELEASES_URL}`,\n );\n }\n\n // Find the SHA256SUMS asset for verification (mandatory)\n const checksumAsset = findAsset(release, \"SHA256SUMS\");\n if (!checksumAsset) {\n throw new Error(\n `SHA256SUMS not found in release. Cannot verify download integrity. Please download manually from ${RELEASES_URL}`,\n );\n }\n\n return { assetName, binaryAsset, checksumAsset };\n}\n\n/**\n * Download the binary and SHA256SUMS into the temp directory, then verify the\n * binary's checksum. Throws when the checksum entry is missing or mismatched.\n */\nasync function downloadAndVerifyBinary(params: {\n tempDir: string;\n assetName: string;\n binaryAsset: GitHubReleaseAsset;\n checksumAsset: GitHubReleaseAsset;\n}): Promise<string> {\n const { tempDir, assetName, binaryAsset, checksumAsset } = params;\n const tempBinaryPath = path.join(tempDir, assetName);\n\n // Download the binary\n await downloadFile(binaryAsset.browser_download_url, tempBinaryPath);\n\n // Verify checksum (mandatory)\n const checksumsPath = path.join(tempDir, \"SHA256SUMS\");\n await downloadFile(checksumAsset.browser_download_url, checksumsPath);\n\n const checksumsContent = await fs.promises.readFile(checksumsPath, \"utf-8\");\n const checksums = parseSha256Sums(checksumsContent);\n const expectedChecksum = checksums.get(assetName);\n\n if (!expectedChecksum) {\n throw new Error(\n `Checksum entry for \"${assetName}\" not found in SHA256SUMS. Cannot verify download integrity.`,\n );\n }\n\n const actualChecksum = await calculateSha256(tempBinaryPath);\n if (actualChecksum !== expectedChecksum) {\n throw new Error(\n `Checksum verification failed. Expected: ${expectedChecksum}, Got: ${actualChecksum}. The download may be corrupted.`,\n );\n }\n\n return tempBinaryPath;\n}\n\n/**\n * Replace the running executable at `currentExePath` with the verified binary,\n * preferring an atomic rename and falling back to a direct cross-filesystem copy.\n */\nasync function replaceCurrentBinary(params: {\n tempBinaryPath: string;\n currentExePath: string;\n currentDir: string;\n}): Promise<void> {\n const { tempBinaryPath, currentExePath, currentDir } = params;\n // Attempt atomic replacement via rename (works when on the same filesystem)\n const tempInPlace = path.join(currentDir, `.rulesync-update-${crypto.randomUUID()}`);\n try {\n await fs.promises.copyFile(tempBinaryPath, tempInPlace);\n if (os.platform() !== \"win32\") {\n await fs.promises.chmod(tempInPlace, 0o755);\n }\n await fs.promises.rename(tempInPlace, currentExePath);\n } catch {\n // Cleanup temp-in-place file on failure, then fall back to direct copy\n try {\n await fs.promises.unlink(tempInPlace);\n } catch {\n // Ignore cleanup errors\n }\n // Fallback: direct copy (non-atomic but works across filesystems)\n await fs.promises.copyFile(tempBinaryPath, currentExePath);\n if (os.platform() !== \"win32\") {\n await fs.promises.chmod(currentExePath, 0o755);\n }\n }\n}\n\n/**\n * Install the verified binary over the current executable, backing it up first\n * and restoring from backup on failure. Returns whether the restore failed so\n * the caller can preserve the temp directory for manual recovery.\n */\nasync function installVerifiedBinary(params: {\n tempDir: string;\n tempBinaryPath: string;\n currentVersion: string;\n latestVersion: string;\n}): Promise<{ message: string; restoreFailed: boolean }> {\n const { tempDir, tempBinaryPath, currentVersion, latestVersion } = params;\n\n // Resolve symlinks to get the real executable path\n const currentExePath = await fs.promises.realpath(process.execPath);\n const currentDir = path.dirname(currentExePath);\n\n // Backup current binary to temp directory (not predictable path)\n const backupPath = path.join(tempDir, \"rulesync.backup\");\n try {\n await fs.promises.copyFile(currentExePath, backupPath);\n } catch (error) {\n if (isPermissionError(error)) {\n throw new UpdatePermissionError(\n `Permission denied: Cannot read ${currentExePath}. Try running with sudo.`,\n );\n }\n throw error;\n }\n\n try {\n await replaceCurrentBinary({ tempBinaryPath, currentExePath, currentDir });\n return {\n message: `Successfully updated from ${currentVersion} to ${latestVersion}`,\n restoreFailed: false,\n };\n } catch (error) {\n // Restore from backup on failure\n try {\n await fs.promises.copyFile(backupPath, currentExePath);\n } catch {\n throw new RestoreFailedError(\n new Error(\n `Failed to replace binary and restore failed. Backup is preserved at: ${backupPath} (in ${tempDir}). ` +\n `Please manually copy it to ${currentExePath}. Original error: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n ),\n );\n }\n if (isPermissionError(error)) {\n throw new UpdatePermissionError(\n `Permission denied: Cannot write to ${path.dirname(currentExePath)}. Try running with sudo.`,\n );\n }\n throw error;\n }\n}\n\n/**\n * Internal marker wrapping the error thrown when both the binary replacement\n * and the backup restore fail. The caller unwraps it so the temp directory is\n * preserved for manual recovery (mirrors the original inline `restoreFailed`\n * flag behavior).\n */\nclass RestoreFailedError extends Error {\n override readonly cause: Error;\n constructor(cause: Error) {\n super(cause.message);\n this.name = \"RestoreFailedError\";\n this.cause = cause;\n }\n}\n\n/**\n * Perform the binary update\n */\nexport async function performBinaryUpdate(\n currentVersion: string,\n options: UpdateOptions = {},\n): Promise<string> {\n const { force = false, token } = options;\n\n // Check for updates\n const updateCheck = await checkForUpdate(currentVersion, token);\n\n if (!updateCheck.hasUpdate && !force) {\n return `Already at the latest version (${currentVersion})`;\n }\n\n const { assetName, binaryAsset, checksumAsset } = resolveUpdateAssets(updateCheck.release);\n\n // Create temporary directory for download\n const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), \"rulesync-update-\"));\n let restoreFailed = false;\n\n try {\n // Set restrictive permissions on temp directory (Unix only)\n if (os.platform() !== \"win32\") {\n await fs.promises.chmod(tempDir, 0o700);\n }\n\n const tempBinaryPath = await downloadAndVerifyBinary({\n tempDir,\n assetName,\n binaryAsset,\n checksumAsset,\n });\n\n const installed = await installVerifiedBinary({\n tempDir,\n tempBinaryPath,\n currentVersion,\n latestVersion: updateCheck.latestVersion,\n });\n restoreFailed = installed.restoreFailed;\n return installed.message;\n } catch (error) {\n if (error instanceof RestoreFailedError) {\n restoreFailed = true;\n throw error.cause;\n }\n throw error;\n } finally {\n // Skip cleanup if restore failed, so the backup is preserved for manual recovery\n if (!restoreFailed) {\n try {\n await fs.promises.rm(tempDir, { recursive: true, force: true });\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n}\n\n/**\n * Check if an error is a permission error\n */\nfunction isPermissionError(error: unknown): boolean {\n if (typeof error === \"object\" && error !== null && \"code\" in error) {\n const record = error as Record<string, unknown>;\n return record[\"code\"] === \"EACCES\" || record[\"code\"] === \"EPERM\";\n }\n return false;\n}\n\n/**\n * Get upgrade instructions for npm installation\n */\nexport function getNpmUpgradeInstructions(): string {\n return `This rulesync installation was installed via npm/npx.\n\nTo upgrade, run one of the following commands:\n\n Global installation:\n npm install -g rulesync@latest\n\n Project dependency:\n npm install rulesync@latest\n\n Or use npx to always run the latest version:\n npx rulesync@latest --version`;\n}\n\n/**\n * Get upgrade instructions for Homebrew installation\n */\nexport function getHomebrewUpgradeInstructions(): string {\n return `This rulesync installation was installed via Homebrew.\n\nTo upgrade, run:\n brew upgrade rulesync`;\n}\n","import { GitHubClientError } from \"../../lib/github-client.js\";\nimport {\n UpdatePermissionError,\n checkForUpdate,\n detectExecutionEnvironment,\n getHomebrewUpgradeInstructions,\n getNpmUpgradeInstructions,\n performBinaryUpdate,\n} from \"../../lib/update.js\";\nimport { CLIError, ErrorCodes } from \"../../types/json-output.js\";\nimport type { Logger } from \"../../utils/logger.js\";\n\n/**\n * Update command options\n */\nexport type UpdateCommandOptions = {\n check?: boolean;\n force?: boolean;\n verbose?: boolean;\n silent?: boolean;\n token?: string;\n};\n\n/**\n * Update command handler\n */\nexport async function updateCommand(\n logger: Logger,\n currentVersion: string,\n options: UpdateCommandOptions,\n): Promise<void> {\n const { check = false, force = false, token } = options;\n\n try {\n const environment = detectExecutionEnvironment();\n logger.debug(`Detected environment: ${environment}`);\n\n if (environment === \"npm\") {\n logger.info(getNpmUpgradeInstructions());\n return;\n }\n\n if (environment === \"homebrew\") {\n logger.info(getHomebrewUpgradeInstructions());\n return;\n }\n\n // Single-binary mode\n if (check) {\n // Check-only mode\n logger.info(\"Checking for updates...\");\n const updateCheck = await checkForUpdate(currentVersion, token);\n\n // Capture JSON data if in JSON mode\n if (logger.jsonMode) {\n logger.captureData(\"currentVersion\", updateCheck.currentVersion);\n logger.captureData(\"latestVersion\", updateCheck.latestVersion);\n logger.captureData(\"updateAvailable\", updateCheck.hasUpdate);\n logger.captureData(\n \"message\",\n updateCheck.hasUpdate\n ? `Update available: ${updateCheck.currentVersion} -> ${updateCheck.latestVersion}`\n : `Already at the latest version (${updateCheck.currentVersion})`,\n );\n }\n\n if (updateCheck.hasUpdate) {\n logger.success(\n `Update available: ${updateCheck.currentVersion} -> ${updateCheck.latestVersion}`,\n );\n } else {\n logger.info(`Already at the latest version (${updateCheck.currentVersion})`);\n }\n return;\n }\n\n // Perform update\n logger.info(\"Checking for updates...\");\n const message = await performBinaryUpdate(currentVersion, { force, token });\n logger.success(message);\n } catch (error) {\n if (error instanceof GitHubClientError) {\n // Include auth hints in error message for JSON mode\n const authHint =\n error.statusCode === 401 || error.statusCode === 403\n ? \" Tip: Set GITHUB_TOKEN or GH_TOKEN environment variable, or use `GITHUB_TOKEN=$(gh auth token) rulesync update ...`\"\n : \"\";\n throw new CLIError(\n `GitHub API Error: ${error.message}.${authHint}`,\n ErrorCodes.UPDATE_FAILED,\n );\n } else if (error instanceof UpdatePermissionError) {\n throw new CLIError(\n `${error.message} Tip: Run with elevated privileges (e.g., sudo rulesync update)`,\n ErrorCodes.UPDATE_FAILED,\n );\n }\n throw error;\n }\n}\n","import { Command } from \"commander\";\n\nimport { CLIError } from \"../types/json-output.js\";\nimport { formatError } from \"../utils/error.js\";\nimport {\n ConsoleLogger,\n fallbackLogger,\n JsonLogger,\n Logger,\n warnOnConflictingFlags,\n} from \"../utils/logger.js\";\n\nexport function createLogger({\n name,\n globalOpts,\n getVersion,\n}: {\n name: string;\n globalOpts: Record<string, unknown>;\n getVersion: () => string;\n}): Logger {\n return globalOpts.json\n ? new JsonLogger({ command: name, version: getVersion() })\n : new ConsoleLogger();\n}\n\nexport function wrapCommand({\n name,\n errorCode,\n handler,\n getVersion,\n loggerFactory = createLogger,\n}: {\n name: string;\n errorCode: string;\n handler: (\n logger: Logger,\n options: unknown,\n globalOpts: Record<string, unknown>,\n positionalArgs: unknown[],\n ) => Promise<void>;\n getVersion: () => string;\n loggerFactory?: (params: {\n name: string;\n globalOpts: Record<string, unknown>;\n getVersion: () => string;\n }) => Logger;\n}) {\n return async (...args: unknown[]) => {\n // Commander passes variable args based on command signature:\n // - No positional: (options, command)\n // - With positional: (arg1, arg2, ..., options, command)\n // The last two are always (options, command)\n const command = args[args.length - 1] as Command;\n const options = args[args.length - 2] as Record<string, unknown>;\n const positionalArgs = args.slice(0, -2);\n const globalOpts = command.parent?.opts() ?? {};\n const logger = loggerFactory({ name, globalOpts, getVersion });\n // Configure from CLI flags first; commands that resolve a config file\n // re-configure via `ConfigResolver.resolve` so config-file\n // `verbose`/`silent` also apply (CLI flags still win there).\n const cliLoggerOptions = {\n verbose: Boolean(globalOpts.verbose) || Boolean(options.verbose),\n silent: Boolean(globalOpts.silent) || Boolean(options.silent),\n };\n warnOnConflictingFlags({ ...cliLoggerOptions, jsonMode: logger.jsonMode });\n logger.configure(cliLoggerOptions);\n fallbackLogger.configure(cliLoggerOptions);\n\n try {\n await handler(logger, options, globalOpts, positionalArgs);\n logger.outputJson(true);\n } catch (error) {\n const code = error instanceof CLIError ? error.code : errorCode;\n const errorArg = error instanceof Error ? error : formatError(error);\n logger.error(errorArg, code);\n process.exit(error instanceof CLIError ? error.exitCode : 1);\n }\n };\n}\n","import { Command } from \"commander\";\n\nimport { ALL_FEATURES, RulesyncFeatures } from \"../types/features.js\";\nimport { FetchOptions } from \"../types/fetch.js\";\nimport type { Logger } from \"../utils/logger.js\";\nimport { parseCommaSeparatedList } from \"../utils/parse-comma-separated-list.js\";\nimport { addCommand, type AddCommandOptions } from \"./commands/add.js\";\nimport { convertCommand, ConvertOptions } from \"./commands/convert.js\";\nimport { fetchCommand } from \"./commands/fetch.js\";\nimport { generateCommand, GenerateOptions } from \"./commands/generate.js\";\nimport { gitignoreCommand } from \"./commands/gitignore.js\";\nimport { importCommand, ImportOptions } from \"./commands/import.js\";\nimport { initCommand } from \"./commands/init.js\";\nimport { INSTALL_MODES, InstallMode, installCommand } from \"./commands/install.js\";\nimport { mcpCommand } from \"./commands/mcp.js\";\nimport { resolveGitignoreTargets } from \"./commands/resolve-gitignore-targets.js\";\nimport { updateCommand, UpdateCommandOptions } from \"./commands/update.js\";\nimport { wrapCommand as _wrapCommand } from \"./wrap-command.js\";\n\nconst getVersion = () => \"16.2.0\";\nconst FEATURES_HELP = `${ALL_FEATURES.join(\",\")}; ignore is deprecated, use permissions`;\n\nfunction wrapCommand(\n name: string,\n errorCode: string,\n handler: (\n logger: Logger,\n options: unknown,\n globalOpts: Record<string, unknown>,\n positionalArgs: unknown[],\n ) => Promise<void>,\n) {\n return _wrapCommand({ name, errorCode, handler, getVersion });\n}\n\nexport function createProgram(): Command {\n const program = new Command();\n\n const version = getVersion();\n\n program\n .name(\"rulesync\")\n .description(\"Unified AI rules management CLI tool\")\n .version(version, \"-v, --version\", \"Show version\")\n .option(\"-j, --json\", \"Output results as JSON\");\n\n program\n .command(\"init\")\n .description(\"Initialize rulesync in current directory\")\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .action(\n wrapCommand(\"init\", \"INIT_FAILED\", async (logger) => {\n await initCommand(logger);\n }),\n );\n\n program\n .command(\"gitignore\")\n .description(\"Add generated files to .gitignore\")\n .option(\n \"-t, --targets <tools>\",\n \"Comma-separated list of tools to include (e.g., 'claudecode,copilot' or '*' for all)\",\n parseCommaSeparatedList,\n )\n .option(\n \"-f, --features <features>\",\n `Comma-separated list of features to include (${FEATURES_HELP}) or '*' for all`,\n parseCommaSeparatedList,\n )\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .action(\n wrapCommand(\"gitignore\", \"GITIGNORE_FAILED\", async (logger, options) => {\n const cliTargets = (options as { targets?: string[] }).targets;\n const cliFeatures = (options as { features?: RulesyncFeatures }).features;\n\n const resolvedTargets = await resolveGitignoreTargets({ cliTargets });\n\n await gitignoreCommand(logger, {\n targets: resolvedTargets ? [...resolvedTargets] : undefined,\n features: cliFeatures,\n verbose: (options as { verbose?: boolean }).verbose,\n silent: (options as { silent?: boolean }).silent,\n });\n }),\n );\n\n program\n .command(\"add <source>\")\n .description(\n \"Add a Rulesync feature file (ignore is deprecated; use permissions) or install a declarative rule or skill source\",\n )\n .option(\"--name <name>\", \"Name for a rule, command, subagent, skill, or check scaffold\")\n .option(\"-f, --force\", \"Overwrite an existing scaffold file without prompting\")\n .option(\"--skills <skills>\", \"Comma-separated skill names to install\", parseCommaSeparatedList)\n .option(\"--rules <rules>\", \"Comma-separated rule names to install\", parseCommaSeparatedList)\n .option(\"--transport <transport>\", \"Source transport: github, git, or npm\")\n .option(\"-r, --ref <ref>\", \"Git ref, npm version, or npm dist-tag\")\n .option(\"-p, --path <path>\", \"Skills path within the source\")\n .option(\"--rules-path <path>\", \"Rules path within the source\")\n .option(\"--registry <url>\", \"npm-compatible registry URL\")\n .option(\"--token-env <name>\", \"Environment variable containing the npm registry token\")\n .option(\"--token <token>\", \"GitHub token for private repositories\")\n .option(\"-c, --config <path>\", \"Path to configuration file\")\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .action(\n wrapCommand(\"add\", \"ADD_FAILED\", async (logger, options, _globalOpts, positionalArgs) => {\n const source = positionalArgs[0] as string;\n const addOptions = options as Omit<AddCommandOptions, \"source\" | \"configPath\"> & {\n config?: string;\n };\n await addCommand(logger, {\n ...addOptions,\n source,\n configPath: addOptions.config,\n });\n }),\n );\n\n program\n .command(\"fetch <source>\")\n .description(\"Fetch files from a Git repository (GitHub/GitLab)\")\n .option(\n \"-t, --target <target>\",\n \"Target format to interpret files as (e.g., 'rulesync', 'claudecode'). Default: rulesync\",\n )\n .option(\n \"-f, --features <features>\",\n `Comma-separated list of features to fetch (${FEATURES_HELP}) or '*' for all. Default: skills`,\n parseCommaSeparatedList,\n )\n .option(\"-r, --ref <ref>\", \"Branch, tag, or commit SHA to fetch from\")\n .option(\"-p, --path <path>\", \"Subdirectory path within the repository\")\n .option(\"-o, --output <dir>\", \"Output directory (default: .rulesync)\")\n .option(\n \"-c, --conflict <strategy>\",\n \"Conflict resolution strategy: skip, overwrite (default: overwrite)\",\n )\n .option(\"--token <token>\", \"Git provider token for private repositories\")\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .action(\n wrapCommand(\"fetch\", \"FETCH_FAILED\", async (logger, options, _globalOpts, positionalArgs) => {\n const source = positionalArgs[0] as string;\n await fetchCommand(logger, { ...(options as FetchOptions), source });\n }),\n );\n\n program\n .command(\"import\")\n .description(\"Import configurations from AI tools to rulesync format\")\n .option(\n \"-t, --targets <tool>\",\n \"Tool to import from (e.g., 'copilot', 'cursor', 'cline')\",\n parseCommaSeparatedList,\n )\n .option(\n \"-f, --features <features>\",\n `Comma-separated list of features to import (${FEATURES_HELP}) or '*' for all`,\n parseCommaSeparatedList,\n )\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .option(\"-g, --global\", \"Import for global(user scope) configuration files\")\n .option(\n \"-o, --output-root <path>\",\n \"Root directory containing the tool configuration to import\",\n )\n .action(\n wrapCommand(\"import\", \"IMPORT_FAILED\", async (logger, options) => {\n const { outputRoot, ...importOptions } = options as ImportOptions & {\n outputRoot?: string;\n };\n await importCommand(logger, {\n ...importOptions,\n outputRoots: outputRoot ? [outputRoot] : undefined,\n });\n }),\n );\n\n program\n .command(\"convert\")\n .description(\n \"Convert configurations from one AI tool to other AI tools without writing .rulesync/ files\",\n )\n .requiredOption(\"--from <tool>\", \"Source tool to convert from (e.g., 'cursor', 'claudecode')\")\n .requiredOption(\n \"--to <tools>\",\n \"Comma-separated list of destination tools (e.g., 'copilot,claudecode')\",\n parseCommaSeparatedList,\n )\n .option(\n \"-f, --features <features>\",\n `Comma-separated list of features to convert (${FEATURES_HELP}) or '*' for all`,\n parseCommaSeparatedList,\n )\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .option(\"-g, --global\", \"Convert for global(user scope) configuration files\")\n .option(\"--dry-run\", \"Dry run: show changes without writing files\")\n .action(\n wrapCommand(\"convert\", \"CONVERT_FAILED\", async (logger, options) => {\n await convertCommand(logger, options as ConvertOptions);\n }),\n );\n\n program\n .command(\"mcp\")\n .description(\"Start MCP server for rulesync\")\n .action(\n wrapCommand(\"mcp\", \"MCP_FAILED\", async (logger, _options) => {\n await mcpCommand(logger, { version });\n }),\n );\n\n program\n .command(\"install\")\n .description(\n \"Install rules, skills, or primitives from declarative sources (rulesync.jsonc) or apm.yml\",\n )\n .option(\n \"--mode <mode>\",\n `Install layout to produce (${INSTALL_MODES.join(\"|\")}). Default: rulesync`,\n )\n .option(\"--update\", \"Force re-resolve all source refs, ignoring lockfile\")\n .option(\n \"--frozen\",\n \"Fail if lockfile is missing or out of sync (for CI); fetches missing skills using locked refs\",\n )\n .option(\"--token <token>\", \"GitHub token for private repos\")\n .option(\"-c, --config <path>\", \"Path to configuration file\")\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .action(\n wrapCommand(\"install\", \"INSTALL_FAILED\", async (logger, options) => {\n const rawMode = (options as { mode?: string }).mode;\n const mode = parseInstallMode(rawMode);\n await installCommand(logger, {\n mode,\n update: (options as { update?: boolean }).update,\n frozen: (options as { frozen?: boolean }).frozen,\n token: (options as { token?: string }).token,\n configPath: (options as { config?: string }).config,\n verbose: (options as { verbose?: boolean }).verbose,\n silent: (options as { silent?: boolean }).silent,\n });\n }),\n );\n\n program\n .command(\"generate\")\n .description(\"Generate configuration files for AI tools\")\n .option(\n \"-t, --targets <tools>\",\n \"Comma-separated list of tools to generate for (e.g., 'copilot,cursor,cline' or '*' for all)\",\n parseCommaSeparatedList,\n )\n .option(\n \"-f, --features <features>\",\n `Comma-separated list of features to generate (${FEATURES_HELP}) or '*' for all`,\n parseCommaSeparatedList,\n )\n .option(\"--delete\", \"Delete all existing files in output directories before generating\")\n .option(\n \"-o, --output-roots <paths>\",\n \"Output root directories to generate files into (comma-separated for multiple paths)\",\n parseCommaSeparatedList,\n )\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .option(\"-c, --config <path>\", \"Path to configuration file\")\n .option(\"-g, --global\", \"Generate for global(user scope) configuration files\")\n .option(\n \"--simulate-commands\",\n \"Generate simulated commands. This feature is only available for copilot, cursor and codexcli.\",\n )\n .option(\n \"--simulate-subagents\",\n \"Generate simulated subagents. This feature is only available for copilot and codexcli.\",\n )\n .option(\n \"--simulate-skills\",\n \"Generate simulated skills. This feature is only available for copilot, cursor and codexcli.\",\n )\n .option(\n \"--input-root <path>\",\n \"Path to the directory containing .rulesync/ (parent of .rulesync/)\",\n )\n .option(\"--dry-run\", \"Dry run: show changes without writing files\")\n .option(\"--check\", \"Check if files are up to date (exits with code 1 if changes needed)\")\n .option(\n \"-w, --watch\",\n \"Keep running and regenerate whenever rulesync source files change (cannot be combined with --check, --dry-run or --json)\",\n )\n .action(\n wrapCommand(\"generate\", \"GENERATION_FAILED\", async (logger, options) => {\n await generateCommand(logger, options as GenerateOptions);\n }),\n );\n\n program\n .command(\"update\")\n .description(\"Update rulesync to the latest version\")\n .option(\"--check\", \"Check for updates without installing\")\n .option(\"--force\", \"Force update even if already at latest version\")\n .option(\"--token <token>\", \"GitHub token for API access\")\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .action(\n wrapCommand(\"update\", \"UPDATE_FAILED\", async (logger, options) => {\n await updateCommand(logger, version, options as UpdateCommandOptions);\n }),\n );\n\n return program;\n}\n\nfunction parseInstallMode(raw: string | undefined): InstallMode | undefined {\n if (raw === undefined) return undefined;\n const match = INSTALL_MODES.find((m) => m === raw);\n if (!match) {\n throw new Error(`Invalid --mode value \"${raw}\". Expected one of: ${INSTALL_MODES.join(\", \")}.`);\n }\n return match;\n}\n","#!/usr/bin/env node\n\nimport { formatError } from \"../utils/error.js\";\nimport { createProgram } from \"./program.js\";\n\nasync function main(): Promise<void> {\n createProgram().parse();\n}\n\nmain().catch((error) => {\n console.error(formatError(error));\n process.exit(1);\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,MAAa,2BAA2B,UACtC,MACG,MAAM,GAAG,CAAC,CACV,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC,CACpB,OAAO,OAAO;;;ACuBnB,MAAM,mCAAmB,IAAI,IAA6B;CACxD,CAAC,QAAQ,MAAM;CACf,CAAC,SAAS,MAAM;CAChB,CAAC,WAAW,SAAS;CACrB,CAAC,YAAY,SAAS;CACtB,CAAC,YAAY,UAAU;CACvB,CAAC,aAAa,UAAU;CACxB,CAAC,SAAS,OAAO;CACjB,CAAC,UAAU,OAAO;CAClB,CAAC,SAAS,OAAO;CACjB,CAAC,UAAU,OAAO;CAClB,CAAC,OAAO,KAAK;CACb,CAAC,QAAQ,OAAO;CAChB,CAAC,SAAS,OAAO;CACjB,CAAC,UAAU,QAAQ;CACnB,CAAC,cAAc,aAAa;CAC5B,CAAC,eAAe,aAAa;AAC/B,CAAC;AAED,MAAM,iCAAiB,IAAI,IAAqB;CAAC;CAAQ;CAAW;CAAY;CAAS;AAAO,CAAC;AAEjG,SAAgB,4BAA4B,OAA4C;CACtF,OAAO,iBAAiB,IAAI,MAAM,YAAY,CAAC;AACjD;AAEA,SAAgB,uBAAuB,SAAmC;CACxE,OAAO,eAAe,IAAI,OAAO;AACnC;AAEA,SAAgB,sBAAsB,EACpC,SACA,QAIqB;CACrB,IAAI,CAAC,uBAAuB,OAAO,GAAG;EACpC,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,YAAY,QAAQ,0BAA0B;EAEhE;CACF;CAEA,IAAI,SAAS,KAAA,KAAa,KAAK,KAAK,MAAM,IACxC,MAAM,IAAI,MAAM,YAAY,QAAQ,0BAA0B;CAGhE,MAAM,aAAa,KAAK,KAAK,CAAC,CAAC,QAAQ,UAAU,EAAE;CACnD,IAAI,CAAC,+BAA+B,KAAK,UAAU,GACjD,MAAM,IAAI,MACR,WAAW,QAAQ,SAAS,KAAK,gFACnC;CAEF,OAAO;AACT;AAEA,SAAS,cAAc,MAAsB;CAC3C,OAAO,KACJ,MAAM,QAAQ,CAAC,CACf,OAAO,OAAO,CAAC,CACf,KAAK,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAC3D,KAAK,GAAG;AACb;AAEA,SAAS,qBAAqB,EAC5B,SACA,kBACA,WAKkB;CAClB,OAAO;EACL;EACA;EACA,4BAA4B,CAAC,gBAAgB;EAC7C;CACF;AACF;AAEA,SAAS,aAAa,MAAsB;CAC1C,IAAI,SAAS,YACX,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCT,MAAM,QAAQ,cAAc,IAAI;CAChC,OAAO;;;gBAGO,MAAM;;;;IAIlB,MAAM;;;;AAIV;AAEA,SAAS,gBAAgB,MAAsB;CAC7C,IAAI,SAAS,aACX,OAAO;;;;;;;;;;;;;;;;;;CAoBT,MAAM,QAAQ,cAAc,IAAI;CAChC,OAAO;wBACe,MAAM;;;;IAI1B,MAAM;;;;AAIV;AAEA,SAAS,iBAAiB,MAAsB;CAC9C,IAAI,SAAS,WACX,OAAO;;;;;;;;;;;;;;;;;CAmBT,MAAM,QAAQ,cAAc,IAAI;CAChC,OAAO;QACD,KAAK,UAAU,IAAI,EAAE;;gBAEb,MAAM;;;cAGR,MAAM;;AAEpB;AAEA,SAAS,cAAc,MAAsB;CAC3C,IAAI,SAAS,mBACX,OAAO;;;;;;;;;CAWT,MAAM,QAAQ,cAAc,IAAI;CAChC,OAAO;QACD,KAAK,UAAU,IAAI,EAAE;oBACT,MAAM;;;;IAItB,MAAM;;;;AAIV;AAEA,SAAS,cAAc,MAAsB;CAC3C,MAAM,QAAQ,cAAc,IAAI;CAChC,OAAO;;gBAEO,MAAM;;;;IAIlB,MAAM;;;;AAIV;AAEA,SAAS,kBAAkB,SAAkC;CAC3D,QAAQ,SAAR;EACE,KAAK,OACH,OAAO;gBACG,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8BpC,KAAK,SACH,OAAO;;;;;;;;;;;;EAYT,KAAK,UACH,OAAO;;EAET,KAAK,eACH,OAAO;gBACG,gCAAgC;;;;;;;;;;;;;;;;;;;;;;;;EAwB5C,SACE,MAAM,IAAI,MAAM,YAAY,QAAQ,mBAAmB;CAC3D;AACF;AAEA,SAAgB,sBAAsB,EACpC,SACA,QAIkB;CAClB,MAAM,iBAAiB,sBAAsB;EAAE;EAAS;CAAK,CAAC;CAE9D,QAAQ,SAAR;EACE,KAAK,QACH,OAAO,qBAAqB;GAC1B;GACA,kBAAkB,KAChB,aAAa,iBAAiB,CAAC,CAAC,YAAY,iBAC5C,GAAG,eAAe,IACpB;GACA,SAAS,aAAa,cAAe;EACvC,CAAC;EACH,KAAK,WACH,OAAO,qBAAqB;GAC1B;GACA,kBAAkB,KAChB,gBAAgB,iBAAiB,CAAC,CAAC,iBACnC,GAAG,eAAe,IACpB;GACA,SAAS,gBAAgB,cAAe;EAC1C,CAAC;EACH,KAAK,YACH,OAAO,qBAAqB;GAC1B;GACA,kBAAkB,KAChB,iBAAiB,iBAAiB,CAAC,CAAC,iBACpC,GAAG,eAAe,IACpB;GACA,SAAS,iBAAiB,cAAe;EAC3C,CAAC;EACH,KAAK,SACH,OAAO,qBAAqB;GAC1B;GACA,kBAAkB,KAChB,cAAc,iBAAiB,CAAC,CAAC,iBACjC,gBACAA,iBACF;GACA,SAAS,cAAc,cAAe;EACxC,CAAC;EACH,KAAK,SACH,OAAO,qBAAqB;GAC1B;GACA,kBAAkB,KAChB,cAAc,iBAAiB,CAAC,CAAC,iBACjC,GAAG,eAAe,IACpB;GACA,SAAS,cAAc,cAAe;EACxC,CAAC;EACH,KAAK,OAAO;GACV,MAAM,QAAQ,YAAY,iBAAiB;GAK3C,OAAO;IACL;IACA,kBANuB,KACvB,MAAM,YAAY,iBAClB,MAAM,YAAY,gBAIH;IACf,4BAA4B,4BAA4B,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,cACtE,KAAK,UAAU,iBAAiB,UAAU,gBAAgB,CAC5D;IACA,SAAS,kBAAkB,OAAO;GACpC;EACF;EACA,KAAK,SAAS;GACZ,MAAM,QAAQ,cAAc,iBAAiB;GAK7C,OAAO;IACL;IACA,kBANuB,KACvB,MAAM,YAAY,iBAClB,MAAM,YAAY,gBAIH;IACf,4BAA4B,4BAA4B,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,cACtE,KAAK,UAAU,iBAAiB,UAAU,gBAAgB,CAC5D;IACA,SAAS,kBAAkB,OAAO;GACpC;EACF;EACA,KAAK,UAAU;GACb,MAAM,QAAQ,eAAe,iBAAiB;GAC9C,MAAM,mBAAmB,KACvB,MAAM,YAAY,iBAClB,MAAM,YAAY,gBACpB;GACA,OAAO;IACL;IACA;IACA,4BAA4B,CAC1B,kBACA,GAAI,MAAM,SACN,CAAC,KAAK,MAAM,OAAO,iBAAiB,MAAM,OAAO,gBAAgB,CAAC,IAClE,CAAC,CACP;IACA,SAAS,kBAAkB,OAAO;GACpC;EACF;EACA,KAAK,eAAe;GAClB,MAAM,QAAQ,oBAAoB,iBAAiB;GAKnD,OAAO;IACL;IACA,kBANuB,KACvB,MAAM,YAAY,iBAClB,MAAM,YAAY,gBAIH;IACf,4BAA4B,4BAA4B,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,cACtE,KAAK,UAAU,iBAAiB,UAAU,gBAAgB,CAC5D;IACA,SAAS,kBAAkB,OAAO;GACpC;EACF;CACF;AACF;ACtcA,MAAM,uBAAuB,EAAE,OAAO,EACpC,WAAW,EAAE,OAAO,EACtB,CAAC;;;;AAKD,MAAM,wBAAwB,EAAE,OAAO;CACrC,UAAU,SAAS,EAAE,OAAO,CAAC;CAC7B,kBAAkB,SAAS,EAAE,OAAO,CAAC;CACrC,iBAAiB,EAAE,OAAO;;CAE1B,WAAW,SAAS,EAAE,OAAO,CAAC;CAC9B,YAAY,SAAS,EAAE,OAAO,CAAC;CAC/B,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,oBAAoB;CACjD,OAAO,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,oBAAoB,CAAC;CAC1D,eAAe,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C,WAAW,SAAS,EAAE,OAAO,CAAC;CAC9B,mBAAmB,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC;AAGD,MAAM,uBAAuB,EAAE,OAAO;CACpC,iBAAiB,EAAE,OAAO;CAC1B,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,qBAAqB;AACrD,CAAC;;;;AAMD,SAAgB,qBAAqC;CACnD,OAAO;EAAE,iBAAA;EAAuC,SAAS,CAAC;CAAE;AAC9D;;;;;AAMA,eAAsB,gBAAgB,QAGV;CAC1B,MAAM,EAAE,WAAW;CACnB,MAAM,WAAW,KAAK,OAAO,aAAa,4CAA4C;CAEtF,IAAI,CAAE,MAAM,WAAW,QAAQ,GAAI;EACjC,OAAO,MAAM,gDAAgD;EAC7D,OAAO,mBAAmB;CAC5B;CAEA,IAAI;EACF,MAAM,UAAU,MAAM,gBAAgB,QAAQ;EAC9C,MAAM,SAAS,qBAAqB,UAAU,KAAK,MAAM,OAAO,CAAC;EACjE,IAAI,OAAO,SACT,OAAO,OAAO;EAEhB,OAAO,KACL,wCAAwC,6CAA6C,mBACvF;EACA,OAAO,mBAAmB;CAC5B,QAAQ;EACN,OAAO,KACL,wCAAwC,6CAA6C,mBACvF;EACA,OAAO,mBAAmB;CAC5B;AACF;;;;AAKA,eAAsB,iBAAiB,QAIrB;CAChB,MAAM,EAAE,WAAW;CACnB,MAAM,WAAW,KAAK,OAAO,aAAa,4CAA4C;CAEtF,MAAM,iBAAiB,UADP,KAAK,UAAU,OAAO,MAAM,MAAM,CAAC,IAAI,IACf;CACxC,OAAO,MAAM,iCAAiC,UAAU;AAC1D;;;;AAKA,SAAgB,sBAAsB,QAAwB;CAC5D,OAAO,OAAO,KAAK;AACrB;;;;AAKA,SAAgB,mBACd,MACA,WAC6B;CAC7B,MAAM,aAAa,sBAAsB,SAAS;CAClD,OAAO,OAAO,UAAU,eAAe,KAAK,KAAK,SAAS,UAAU,IAChE,KAAK,QAAQ,cACb,KAAA;AACN;;;;AAKA,SAAgB,mBACd,MACA,WACA,OACgB;CAChB,OAAO;EACL,iBAAiB,KAAK;EACtB,SAAS;GACP,GAAG,KAAK;IACP,sBAAsB,SAAS,IAAI;EACtC;CACF;AACF;;;;AAKA,SAAgB,uBAAuB,OAAkC;CACvE,OAAO,OAAO,KAAK,MAAM,MAAM;AACjC;;AAGA,SAAgB,sBAAsB,OAAkC;CACtE,OAAO,OAAO,KAAK,MAAM,SAAS,CAAC,CAAC;AACtC;;;;ACxIA,MAAM,oBAAoB,EAAE,OAAO,EACjC,WAAW,EAAE,OAAO,EACtB,CAAC;;AAID,MAAM,mBAAmB,EAAE,OAAO,EAChC,WAAW,EAAE,OAAO,EACtB,CAAC;;;;AAMD,MAAM,qBAAqB,EAAE,OAAO;CAClC,cAAc,SAAS,EAAE,OAAO,CAAC;CACjC,aAAa,EACV,OAAO,CAAC,CACR,MAAM,QAAQ,MAAM,iBAAiB,KAAK,CAAC,GAAG,4CAA4C,CAAC;CAC9F,YAAY,SAAS,EAAE,OAAO,CAAC;CAC/B,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,iBAAiB;CAC9C,OAAO,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,gBAAgB,CAAC;CACtD,eAAe,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C,WAAW,SAAS,EAAE,OAAO,CAAC;CAC9B,mBAAmB,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC;;;;AAMD,MAAM,oBAAoB,EAAE,OAAO;CACjC,iBAAiB,EAAE,OAAO;CAC1B,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,kBAAkB;AAClD,CAAC;;;;AAMD,MAAM,2BAA2B,EAAE,OAAO;CACxC,aAAa,EAAE,OAAO;CACtB,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC;AAC5B,CAAC;AAED,MAAM,0BAA0B,EAAE,OAAO,EACvC,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,wBAAwB,EACxD,CAAC;;;;;AAMD,SAAS,kBAAkB,QAGX;CACd,MAAM,EAAE,QAAQ,WAAW;CAC3B,MAAM,UAAwC,CAAC;CAC/C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,OAAO,GAAG;EACzD,MAAM,SAAsC,CAAC;EAC7C,KAAK,MAAM,QAAQ,MAAM,QACvB,OAAO,QAAQ,EAAE,WAAW,GAAG;EAEjC,QAAQ,OAAO;GACb,aAAa,MAAM;GACnB;EACF;CACF;CACA,OAAO,KACL,8GACF;CACA,OAAO;EAAE,iBAAA;EAAmC;CAAQ;AACtD;;;;AAKA,SAAgB,kBAA+B;CAC7C,OAAO;EAAE,iBAAA;EAAmC,SAAS,CAAC;CAAE;AAC1D;;;;;AAMA,eAAsB,aAAa,QAGV;CACvB,MAAM,EAAE,WAAW;CACnB,MAAM,WAAW,KAAK,OAAO,aAAa,wCAAwC;CAElF,IAAI,CAAE,MAAM,WAAW,QAAQ,GAAI;EACjC,OAAO,MAAM,4CAA4C;EACzD,OAAO,gBAAgB;CACzB;CAEA,IAAI;EACF,MAAM,UAAU,MAAM,gBAAgB,QAAQ;EAC9C,MAAM,OAAO,KAAK,MAAM,OAAO;EAG/B,MAAM,SAAS,kBAAkB,UAAU,IAAI;EAC/C,IAAI,OAAO,SACT,OAAO,OAAO;EAIhB,MAAM,eAAe,wBAAwB,UAAU,IAAI;EAC3D,IAAI,aAAa,SACf,OAAO,kBAAkB;GAAE,QAAQ,aAAa;GAAM;EAAO,CAAC;EAGhE,OAAO,KACL,oCAAoC,yCAAyC,mBAC/E;EACA,OAAO,gBAAgB;CACzB,QAAQ;EACN,OAAO,KACL,oCAAoC,yCAAyC,mBAC/E;EACA,OAAO,gBAAgB;CACzB;AACF;;;;AAKA,eAAsB,cAAc,QAIlB;CAChB,MAAM,EAAE,WAAW;CACnB,MAAM,WAAW,KAAK,OAAO,aAAa,wCAAwC;CAElF,MAAM,iBAAiB,UADP,KAAK,UAAU,OAAO,MAAM,MAAM,CAAC,IAAI,IACf;CACxC,OAAO,MAAM,6BAA6B,UAAU;AACtD;;;;;AAMA,SAAgB,sBAAsB,OAAyD;CAC7F,MAAM,OAAO,WAAW,QAAQ;CAEhC,MAAM,SAAS,MAAM,UAAU,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;CACpE,KAAK,MAAM,QAAQ,QAAQ;EACzB,KAAK,OAAO,KAAK,IAAI;EACrB,KAAK,OAAO,IAAI;EAChB,KAAK,OAAO,KAAK,OAAO;EACxB,KAAK,OAAO,IAAI;CAClB;CACA,OAAO,UAAU,KAAK,OAAO,KAAK;AACpC;;AAGA,SAAgB,qBAAqB,SAAyB;CAC5D,MAAM,OAAO,WAAW,QAAQ;CAChC,KAAK,OAAO,OAAO;CACnB,OAAO,UAAU,KAAK,OAAO,KAAK;AACpC;;;;;AAMA,SAAgB,mBAAmB,QAAwB;CACzD,IAAI,MAAM;CAGV,KAAK,MAAM,UAAU;EACnB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,GACE,IAAI,IAAI,YAAY,CAAC,CAAC,WAAW,MAAM,GAAG;EACxC,MAAM,IAAI,UAAU,OAAO,MAAM;EACjC;CACF;CAIF,KAAK,MAAM,YAAY,CAAC,WAAW,SAAS,GAC1C,IAAI,IAAI,WAAW,QAAQ,GAAG;EAC5B,MAAM,IAAI,UAAU,SAAS,MAAM;EACnC;CACF;CAIF,MAAM,IAAI,QAAQ,QAAQ,EAAE;CAG5B,MAAM,IAAI,QAAQ,UAAU,EAAE;CAG9B,MAAM,IAAI,YAAY;CAEtB,OAAO;AACT;;;;AAKA,SAAgB,gBAAgB,MAAmB,WAA6C;CAC9F,MAAM,aAAa,mBAAmB,SAAS;CAE/C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,OAAO,GACpD,IAAI,mBAAmB,GAAG,MAAM,YAC9B,OAAO;AAIb;;;;AAKA,SAAgB,gBACd,MACA,WACA,OACa;CACb,MAAM,aAAa,mBAAmB,SAAS;CAE/C,MAAM,kBAAgD,CAAC;CACvD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,OAAO,GACpD,IAAI,mBAAmB,GAAG,MAAM,YAC9B,gBAAgB,OAAO;CAG3B,OAAO;EACL,iBAAiB,KAAK;EACtB,SAAS;GACP,GAAG;IACF,aAAa;EAChB;CACF;AACF;;;;AAKA,SAAgB,oBAAoB,OAA+B;CACjE,OAAO,OAAO,KAAK,MAAM,MAAM;AACjC;;AAGA,SAAgB,mBAAmB,OAA+B;CAChE,OAAO,OAAO,KAAK,MAAM,SAAS,CAAC,CAAC;AACtC;;;AChQA,MAAM,gBAAgB,UAAU,QAAQ;;AAGxC,MAAM,iBAAiB;AAEvB,MAAM,sBACJ;AAEF,MAAM,uBAAuB;AAE7B,IAAa,iBAAb,cAAoC,MAAM;CACxC,YAAY,SAAiB,OAAiB;EAC5C,MAAM,SAAS,EAAE,MAAM,CAAC;EACxB,KAAK,OAAO;CACd;AACF;AAEA,SAAgB,eAAe,KAAa,SAAqC;CAC/E,MAAM,OAAO,qBAAqB,GAAG;CACrC,IAAI,MACF,MAAM,IAAI,eACR,sCAAsC,KAAK,IAAI,eAAe,KAAK,UACrE;CAEF,IAAI,CAAC,oBAAoB,KAAK,GAAG,GAC/B,MAAM,IAAI,eACR,mCAAmC,IAAI,yCACzC;CAEF,IAAI,qBAAqB,KAAK,GAAG,GAC/B,SAAS,QAAQ,KACf,QAAQ,IAAI,2EACd;AAEJ;;;;;AAMA,SAAgB,YAAY,KAAmB;CAC7C,IAAI,IAAI,WAAW,GAAG,GACpB,MAAM,IAAI,eAAe,iCAAiC,IAAI,EAAE;CAElE,MAAM,OAAO,qBAAqB,GAAG;CACrC,IAAI,MACF,MAAM,IAAI,eACR,kCAAkC,KAAK,IAAI,eAAe,KAAK,UACjE;AAEJ;AAEA,IAAI,aAAa;AAEjB,eAAsB,oBAAmC;CACvD,IAAI,YAAY;CAChB,IAAI;EACF,MAAM,cAAc,OAAO,CAAC,WAAW,GAAG,EAAE,SAAS,eAAe,CAAC;EACrE,aAAa;CACf,QAAQ;EACN,MAAM,IAAI,eAAe,2CAA2C;CACtE;AACF;AAOA,eAAsB,kBAAkB,KAAoD;CAC1F,eAAe,GAAG;CAClB,MAAM,kBAAkB;CACxB,IAAI;EACF,MAAM,EAAE,WAAW,MAAM,cAAc,OAAO;GAAC;GAAa;GAAY;GAAM;GAAK;EAAM,GAAG,EAC1F,SAAS,eACX,CAAC;EACD,MAAM,MAAM,OAAO,MAAM,iCAAiC,CAAC,GAAG;EAC9D,MAAM,MAAM,OAAO,MAAM,yBAAyB,CAAC,GAAG;EACtD,IAAI,CAAC,OAAO,CAAC,KAAK,MAAM,IAAI,eAAe,wCAAwC,KAAK;EACxF,YAAY,GAAG;EACf,OAAO;GAAE;GAAK;EAAI;CACpB,SAAS,OAAO;EACd,IAAI,iBAAiB,gBAAgB,MAAM;EAC3C,MAAM,IAAI,eAAe,qCAAqC,OAAO,KAAK;CAC5E;AACF;AAEA,eAAsB,gBAAgB,KAAa,KAA8B;CAC/E,eAAe,GAAG;CAClB,YAAY,GAAG;CACf,MAAM,kBAAkB;CACxB,IAAI;EACF,MAAM,EAAE,WAAW,MAAM,cAAc,OAAO;GAAC;GAAa;GAAM;GAAK;EAAG,GAAG,EAC3E,SAAS,eACX,CAAC;EACD,MAAM,MAAM,OAAO,MAAM,oBAAoB,CAAC,GAAG;EACjD,IAAI,CAAC,KAAK,MAAM,IAAI,eAAe,QAAQ,IAAI,iBAAiB,KAAK;EACrE,OAAO;CACT,SAAS,OAAO;EACd,IAAI,iBAAiB,gBAAgB,MAAM;EAC3C,MAAM,IAAI,eAAe,0BAA0B,IAAI,QAAQ,OAAO,KAAK;CAC7E;AACF;;;;;;AAOA,eAAsB,gBAAgB,QAMsC;CAC1E,MAAM,EAAE,KAAK,KAAK,aAAa,YAAY,WAAW;CACtD,eAAe,KAAK,EAAE,OAAO,CAAC;CAC9B,YAAY,GAAG;CACf,IAAI,gBAAgB,KAAA,KAAa,CAAC,iBAAiB,KAAK,WAAW,GACjE,MAAM,IAAI,eAAe,wBAAwB,YAAY,yBAAyB;CAExF,IAAI,WAAW,MAAM,OAAO,CAAC,CAAC,SAAS,IAAI,KAAK,WAAW,UAAU,GACnE,MAAM,IAAI,eACR,uBAAuB,WAAW,wCACpC;CAEF,MAAM,OAAO,qBAAqB,UAAU;CAC5C,IAAI,MACF,MAAM,IAAI,eACR,yCAAyC,KAAK,IAAI,eAAe,KAAK,UACxE;CAEF,MAAM,kBAAkB;CACxB,MAAM,SAAS,MAAM,oBAAoB,eAAe;CASxD,MAAM,uBAAuB,MAAM,UAAU,WAAW,QAAQ,OAAO,GAAG,CAAC,CAAC,CAAC,QAAQ,QAAQ,EAAE;CAC/F,MAAM,aAAa,yBAAyB,MAAM,yBAAyB;CAC3E,IAAI;EACF,MAAM,cACJ,OACA;GACE;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,GACA,EAAE,SAAS,eAAe,CAC5B;EACA,IAAI,gBAAgB,KAAA,GAClB,MAAM,cAAc,OAAO;GAAC;GAAM;GAAQ;GAAS;GAAW;GAAK;GAAU;EAAW,GAAG,EACzF,SAAS,eACX,CAAC;EAEH,IAAI,YAEF,MAAM,cAAc,OAAO;GAAC;GAAM;GAAQ;GAAmB;EAAS,GAAG,EACvE,SAAS,eACX,CAAC;OAED,MAAM,cAAc,OAAO;GAAC;GAAM;GAAQ;GAAmB;GAAO;GAAM;EAAU,GAAG,EACrF,SAAS,eACX,CAAC;EAEH,MAAM,cACJ,OACA,gBAAgB,KAAA,IACZ;GAAC;GAAM;GAAQ;EAAU,IACzB;GAAC;GAAM;GAAQ;GAAY;GAAY;EAAW,GACtD,EAAE,SAAS,eAAe,CAC5B;EACA,IAAI,gBAAgB,KAAA,GAAW;GAC7B,MAAM,EAAE,WAAW,MAAM,cAAc,OAAO;IAAC;IAAM;IAAQ;IAAa;GAAM,GAAG,EACjF,SAAS,eACX,CAAC;GACD,IAAI,OAAO,KAAK,MAAM,aACpB,MAAM,IAAI,eACR,sBAAsB,OAAO,KAAK,KAAK,YAAY,2BAA2B,aAChF;EAEJ;EACA,MAAM,YAAY,aAAa,SAAS,KAAK,QAAQ,UAAU;EAC/D,IAAI,CAAE,MAAM,gBAAgB,SAAS,GAAI,OAAO,CAAC;EACjD,OAAO,MAAM,cAAc,WAAW,WAAW,GAAG;GAAE,YAAY;GAAG,WAAW;EAAE,GAAG,MAAM;CAC7F,SAAS,OAAO;EACd,IAAI,iBAAiB,gBAAgB,MAAM;EAC3C,MAAM,IAAI,eAAe,oCAAoC,OAAO,KAAK;CAC3E,UAAU;EACR,MAAM,oBAAoB,MAAM;CAClC;AACF;AAEA,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AACxB,MAAM,iBAAiB,MAAM,OAAO;AAKpC,eAAe,cACb,KACA,YACA,QAAgB,GAChB,MAAmB;CAAE,YAAY;CAAG,WAAW;AAAE,GACjD,QACyE;CACzE,IAAI,QAAQ,gBACV,MAAM,IAAI,eACR,uCAAuC,eAAe,KAAK,IAAI,4CACjE;CAEF,MAAM,UAA0E,CAAC;CACjF,KAAK,MAAM,QAAQ,MAAM,mBAAmB,GAAG,GAAG;EAChD,IAAI,SAAS,QAAQ;EACrB,MAAM,WAAW,KAAK,KAAK,IAAI;EAC/B,IAAI,MAAM,UAAU,QAAQ,GAAG;GAC7B,QAAQ,KAAK,qBAAqB,SAAS,GAAG;GAC9C;EACF;EACA,IAAI,MAAM,gBAAgB,QAAQ,GAChC,QAAQ,KAAK,GAAI,MAAM,cAAc,UAAU,YAAY,QAAQ,GAAG,KAAK,MAAM,CAAE;OAC9E;GACL,MAAM,OAAO,MAAM,YAAY,QAAQ;GACvC,IAAI,OAAA,UAAsB;IACxB,QAAQ,KACN,kBAAkB,SAAS,MAAM,OAAO,OAAO,KAAA,CAAM,QAAQ,CAAC,EAAE,aAAa,gBAAgB,OAAO,KAAK,WAC3G;IACA;GACF;GACA,IAAI;GACJ,IAAI,aAAa;GACjB,IAAI,IAAI,cAAc,iBACpB,MAAM,IAAI,eACR,wCAAwC,gBAAgB,2CAC1D;GAEF,IAAI,IAAI,aAAa,gBACnB,MAAM,IAAI,eACR,wCAAwC,iBAAiB,OAAO,KAAK,6CACvE;GAEF,MAAM,UAAU,MAAM,gBAAgB,QAAQ;GAC9C,QAAQ,KAAK;IAAE,cAAc,SAAS,YAAY,QAAQ;IAAG;IAAS;GAAK,CAAC;EAC9E;CACF;CACA,OAAO;AACT;;;;;;;;AC3QA,MAAM,oBAAoB,CAAC,YAAY,GAAG,gBAAgB;AAE1D,MAAa,oBAAoB,EAAE,KAAK,iBAAiB;;;;;;ACFzD,MAAM,yBAAyB,EAAE,KAAK,CAAC,QAAQ,WAAW,CAAC;;;;AAM3D,MAAM,uBAAuB,EAAE,KAAK;CAAC;CAAQ;CAAO;CAAW;AAAW,CAAC;;;;AAK3E,MAAa,wBAAwB,EAAE,YAAY;CACjD,MAAM,EAAE,OAAO;CACf,MAAM,EAAE,OAAO;CACf,KAAK,EAAE,OAAO;CACd,MAAM,EAAE,OAAO;CACf,MAAM;CACN,cAAc,EAAE,SAAS,EAAE,OAAO,CAAC;AACrC,CAAC;AAiB0B,EAAE,YAAY;CACvC,QAAQ,EAAE,SAAS,iBAAiB;CACpC,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,0BAA0B,CAAC,CAAC;CAChE,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC;CAC1B,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC;CAC3B,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC;CAC7B,UAAU,EAAE,SAAS,sBAAsB;CAC3C,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC;CAC5B,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC;CAC/B,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;AAChC,CAAC;AAM6B,EAAE,KAAK;CAAC;CAAW;CAAe;AAAS,CAAC;;;;AA0C1E,MAAa,uBAAuB,EAAE,YAAY;CAChD,gBAAgB,EAAE,OAAO;CACzB,SAAS,EAAE,QAAQ;AACrB,CAAC;;;;AAMD,MAAM,2BAA2B,EAAE,YAAY;CAC7C,MAAM,EAAE,OAAO;CACf,sBAAsB,EAAE,OAAO;CAC/B,MAAM,EAAE,OAAO;AACjB,CAAC;;;;AAMD,MAAa,sBAAsB,EAAE,YAAY;CAC/C,UAAU,EAAE,OAAO;CACnB,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC;CAC3B,YAAY,EAAE,QAAQ;CACtB,OAAO,EAAE,QAAQ;CACjB,QAAQ,EAAE,MAAM,wBAAwB;AAC1C,CAAC;;;;;;ACzGD,IAAa,oBAAb,cAAuC,MAAM;CAGzB;CACA;CAHlB,YACE,SACA,YACA,UACA;EACA,MAAM,OAAO;EAHG,KAAA,aAAA;EACA,KAAA,WAAA;EAGhB,KAAK,OAAO;CACd;AACF;;;;AAKA,SAAgB,mBAAmB,QAA4D;CAC7F,MAAM,EAAE,OAAO,WAAW;CAC1B,OAAO,MAAM,qBAAqB,MAAM,SAAS;CACjD,IAAI,MAAM,eAAe,OAAO,MAAM,eAAe,KAAK;EACxD,OAAO,KACL,wGACF;EACA,OAAO,KACL,4FACF;CACF;AACF;;;;AAKA,IAAa,eAAb,MAA0B;CACxB;CACA;CAEA,YAAY,SAA6B,CAAC,GAAG;EAE3C,IAAI,OAAO,WAAW,CAAC,OAAO,QAAQ,WAAW,UAAU,GACzD,MAAM,IAAI,kBAAkB,oCAAoC;EAGlE,KAAK,WAAW,CAAC,CAAC,OAAO;EACzB,KAAK,UAAU,IAAI,QAAQ;GACzB,MAAM,OAAO;GACb,SAAS,OAAO;EAClB,CAAC;CACH;;;;CAKA,OAAO,aAAa,eAA4C;EAC9D,IAAI,eACF,OAAO;EAET,OAAO,QAAQ,IAAI,mBAAmB,QAAQ,IAAI;CACpD;;;;CAKA,MAAM,iBAAiB,OAAe,MAA+B;EAEnE,QAAO,MADgB,KAAK,YAAY,OAAO,IAAI,EAAA,CACnC;CAClB;;;;CAKA,MAAM,YAAY,OAAe,MAAuC;EACtE,IAAI;GACF,MAAM,EAAE,SAAS,MAAM,KAAK,QAAQ,MAAM,IAAI;IAAE;IAAO;GAAK,CAAC;GAC7D,MAAM,SAAS,qBAAqB,UAAU,IAAI;GAClD,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,kBACR,qCAAqC,YAAY,OAAO,KAAK,GAC/D;GAEF,OAAO,OAAO;EAChB,SAAS,OAAO;GACd,MAAM,KAAK,YAAY,KAAK;EAC9B;CACF;;;;CAKA,MAAM,cACJ,OACA,MACA,MACA,KAC4B;EAC5B,IAAI;GACF,MAAM,EAAE,SAAS,MAAM,KAAK,QAAQ,MAAM,WAAW;IACnD;IACA;IACA;IACA;GACF,CAAC;GAGD,IAAI,CAAC,MAAM,QAAQ,IAAI,GACrB,MAAM,IAAI,kBAAkB,SAAS,KAAK,qBAAqB;GAGjE,MAAM,UAA6B,CAAC;GACpC,KAAK,MAAM,QAAQ,MAAM;IACvB,MAAM,SAAS,sBAAsB,UAAU,IAAI;IACnD,IAAI,OAAO,SACT,QAAQ,KAAK,OAAO,IAAI;GAE5B;GACA,OAAO;EACT,SAAS,OAAO;GACd,MAAM,KAAK,YAAY,KAAK;EAC9B;CACF;;;;CAKA,MAAM,eAAe,OAAe,MAAc,MAAc,KAA+B;EAC7F,IAAI;GACF,MAAM,EAAE,SAAS,MAAM,KAAK,QAAQ,MAAM,WAAW;IACnD;IACA;IACA;IACA;IACA,WAAW,EACT,QAAQ,MACV;GACF,CAAC;GAGD,IAAI,OAAO,SAAS,UAClB,OAAO;GAIT,IAAI,CAAC,MAAM,QAAQ,IAAI,KAAK,aAAa,QAAQ,KAAK,SACpD,OAAO,OAAO,KAAK,KAAK,SAAS,QAAQ,CAAC,CAAC,SAAS,OAAO;GAG7D,MAAM,IAAI,kBAAkB,6CAA6C;EAC3E,SAAS,OAAO;GACd,MAAM,KAAK,YAAY,KAAK;EAC9B;CACF;;;;CAKA,MAAM,YACJ,OACA,MACA,MACA,KACiC;EACjC,IAAI;GACF,MAAM,EAAE,SAAS,MAAM,KAAK,QAAQ,MAAM,WAAW;IACnD;IACA;IACA;IACA;GACF,CAAC;GAGD,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO;GAGT,MAAM,SAAS,sBAAsB,UAAU,IAAI;GACnD,IAAI,CAAC,OAAO,SACV,OAAO;GAGT,IAAI,OAAO,KAAK,OAAA,UACd,MAAM,IAAI,kBACR,SAAS,KAAK,kCAAkC,gBAAgB,OAAO,KAAK,GAC9E;GAGF,OAAO,OAAO;EAChB,SAAS,OAAgB;GACvB,IAAI,iBAAiB,gBAAgB,MAAM,WAAW,KACpD,OAAO;GAET,IAAI,iBAAiB,qBAAqB,MAAM,eAAe,KAC7D,OAAO;GAET,MAAM,KAAK,YAAY,KAAK;EAC9B;CACF;;;;CAKA,MAAM,mBAAmB,OAAe,MAAgC;EACtE,IAAI;GACF,MAAM,KAAK,YAAY,OAAO,IAAI;GAClC,OAAO;EACT,SAAS,OAAO;GACd,IAAI,iBAAiB,qBAAqB,MAAM,eAAe,KAC7D,OAAO;GAET,MAAM;EACR;CACF;;;;CAKA,MAAM,gBAAgB,OAAe,MAAc,KAA8B;EAC/E,IAAI;GACF,MAAM,EAAE,SAAS,MAAM,KAAK,QAAQ,MAAM,UAAU;IAClD;IACA;IACA;GACF,CAAC;GACD,OAAO,KAAK;EACd,SAAS,OAAO;GACd,MAAM,KAAK,YAAY,KAAK;EAC9B;CACF;;;;CAKA,MAAM,iBAAiB,OAAe,MAAsC;EAC1E,IAAI;GACF,MAAM,EAAE,SAAS,MAAM,KAAK,QAAQ,MAAM,iBAAiB;IAAE;IAAO;GAAK,CAAC;GAC1E,MAAM,SAAS,oBAAoB,UAAU,IAAI;GACjD,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,kBAAkB,kCAAkC,YAAY,OAAO,KAAK,GAAG;GAE3F,OAAO,OAAO;EAChB,SAAS,OAAO;GACd,MAAM,KAAK,YAAY,KAAK;EAC9B;CACF;;;;CAKA,YAAoB,OAAmC;EACrD,IAAI,iBAAiB,mBACnB,OAAO;EAGT,IAAI,iBAAiB,cAAc;GACjC,MAAM,eAAe,MAAM,UAAU;GACrC,MAAM,UAAU,KAAK,oBAAoB,cAAc,MAAM,OAAO;GACpE,MAAM,WAAuC,UAAU,EAAE,QAAQ,IAAI,KAAA;GAErE,OAAO,IAAI,kBADU,KAAK,gBAAgB,MAAM,QAAQ,QAC3B,GAAc,MAAM,QAAQ,QAAQ;EACnE;EAEA,IAAI,iBAAiB,OACnB,OAAO,IAAI,kBAAkB,MAAM,OAAO;EAG5C,OAAO,IAAI,kBAAkB,wBAAwB;CACvD;;;;CAKA,oBAA4B,MAAe,UAA0B;EACnE,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,aAAa,MAAM;GAElE,MAAM,MAAMC,KAAO;GACnB,IAAI,OAAO,QAAQ,UACjB,OAAO;EAEX;EACA,OAAO;CACT;;;;CAKA,gBAAwB,YAAoB,UAAmC;EAC7E,MAAM,cAAc,UAAU,WAAW,QAAQ;EAEjD,QAAQ,YAAR;GACE,KAAK,KACH,OAAO,0BAA0B,YAAY;GAC/C,KAAK;IACH,IAAI,YAAY,YAAY,CAAC,CAAC,SAAS,YAAY,GACjD,OAAO,mCAAmC,KAAK,WAAW,qBAAqB;IAEjF,OAAO,qBAAqB,YAAY;GAC1C,KAAK,KACH,OAAO,cAAc;GACvB,KAAK,KACH,OAAO,oBAAoB;GAC7B,SACE,OAAO,qBAAqB;EAChC;CACF;AACF;;;AC7TA,MAAM,sBAAsB;;;;;AAM5B,eAAsB,cAAiB,WAAsB,IAAkC;CAC7F,MAAM,UAAU,QAAQ;CACxB,IAAI;EACF,OAAO,MAAM,GAAG;CAClB,UAAU;EACR,UAAU,QAAQ;CACpB;AACF;;;;AAKA,eAAsB,uBAAuB,QAQd;CAC7B,MAAM,EAAE,QAAQ,OAAO,MAAM,MAAM,KAAK,QAAQ,GAAG,cAAc;CAEjE,IAAI,QAAQ,qBACV,MAAM,IAAI,MACR,4BAA4B,oBAAoB,sCAAsC,MACxF;CAIF,MAAM,UAAU,MAAM,cAAc,iBAClC,OAAO,cAAc,OAAO,MAAM,MAAM,GAAG,CAC7C;CAEA,MAAM,QAA2B,CAAC;CAClC,MAAM,cAAiC,CAAC;CAExC,KAAK,MAAM,SAAS,SAClB,IAAI,MAAM,SAAS,QACjB,MAAM,KAAK,KAAK;MACX,IAAI,MAAM,SAAS,OACxB,YAAY,KAAK,KAAK;CAI1B,MAAM,aAAa,MAAM,QAAQ,IAC/B,YAAY,KAAK,QACf,uBAAuB;EACrB;EACA;EACA;EACA,MAAM,IAAI;EACV;EACA,OAAO,QAAQ;EACf;CACF,CAAC,CACH,CACF;CAEA,OAAO,CAAC,GAAG,OAAO,GAAG,WAAW,KAAK,CAAC;AACxC;ACzDA,MAAa,wBAAwB;;AAGrC,MAAM,0BAA0B;;AAGhC,MAAM,uBAAuB;;AAG7B,MAAM,mBAAmB,MAAM,OAAO;;;;;AAMtC,MAAM,yBAAyB;AAC/B,MAAM,8BAA8B;AAEpC,MAAM,iCAAiC;CAAC;CAAU;CAAU;CAAU;AAAM;AAG5E,IAAa,iBAAb,cAAoC,MAAM;CACxC;CAEA,YAAY,SAAiB,SAAoD;EAC/E,MAAM,SAAS,EAAE,OAAO,SAAS,MAAM,CAAC;EACxC,KAAK,OAAO;EACZ,KAAK,aAAa,SAAS;CAC7B;AACF;AAcA,SAAgB,uBAAuB,MAAoB;CACzD,IAAI,KAAK,SAAS,+BAA+B,CAAC,uBAAuB,KAAK,IAAI,GAChF,MAAM,IAAI,eACR,8BAA8B,KAAK,qCACrC;AAEJ;AAEA,SAAgB,uBAAuB,KAAa,SAAqC;CACvF,MAAM,OAAO,qBAAqB,GAAG;CACrC,IAAI,MACF,MAAM,IAAI,eACR,2CAA2C,KAAK,IAAI,eAAe,KAAK,UAC1E;CAEF,IAAI,CAAC,IAAI,WAAW,UAAU,KAAK,CAAC,IAAI,WAAW,SAAS,GAC1D,MAAM,IAAI,eAAe,8BAA8B,IAAI,8BAA8B;CAE3F,IAAI,IAAI,WAAW,SAAS,GAC1B,SAAS,QAAQ,KACf,iBAAiB,IAAI,iEACvB;AAEJ;;;;;;AAOA,SAAgB,gBAAgB,QAAmD;CACjF,MAAM,EAAE,aAAa;CACrB,IAAI,aAAa,KAAA,GAAW;EAC1B,MAAM,QAAQ,QAAQ,IAAI;EAC1B,IAAI,UAAU,KAAA,KAAa,UAAU,IACnC,MAAM,IAAI,eACR,yBAAyB,SAAS,sEACpC;EAEF,OAAO;CACT;CACA,MAAM,WAAW,QAAQ,IAAI;CAC7B,OAAO,aAAa,KAAA,KAAa,aAAa,KAAK,KAAA,IAAY;AACjE;;AAGA,SAAgB,kBAAkB,QAA8D;CAC9F,MAAM,EAAE,aAAa,gBAAgB;CAGrC,uBAAuB,WAAW;CAClC,MAAM,OAAO,YAAY,SAAS,GAAG,IAAI,cAAc,GAAG,YAAY;CAEtE,MAAM,cAAc,YAAY,WAAW,KAAK,KAAK;CACrD,OAAO,IAAI,IAAI,aAAa,IAAI,CAAC,CAAC,SAAS;AAC7C;AAEA,eAAe,iBAAiB,KAAa,SAAoD;CAC/F,IAAI;EACF,OAAO,MAAM,MAAM,KAAK;GACtB;GACA,UAAU;GACV,QAAQ,YAAY,QAAQ,oBAAoB;EAClD,CAAC;CACH,SAAS,OAAO;EACd,MAAM,IAAI,eAAe,kCAAkC,OAAO,EAAE,OAAO,MAAM,CAAC;CACpF;AACF;;;;AAKA,eAAsB,eAAe,QAIX;CACxB,MAAM,EAAE,aAAa,aAAa,UAAU;CAC5C,uBAAuB,WAAW;CAClC,MAAM,MAAM,kBAAkB;EAAE;EAAa;CAAY,CAAC;CAE1D,MAAM,UAAkC,EAAE,QAAQ,wBAAwB;CAC1E,IAAI,OACF,QAAQ,gBAAgB,UAAU;CAGpC,MAAM,WAAW,MAAM,iBAAiB,KAAK,OAAO;CACpD,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,eACR,yCAAyC,YAAY,SAAS,YAAY,SAAS,SAAS,UAC5F,EAAE,YAAY,SAAS,OAAO,CAChC;CAEF,IAAI;EACF,OAAQ,MAAM,SAAS,KAAK;CAC9B,SAAS,OAAO;EACd,MAAM,IAAI,eACR,yCAAyC,YAAY,SAAS,eAC9D,EAAE,OAAO,MAAM,CACjB;CACF;AACF;;;;;;AAOA,SAAgB,wBAAwB,QAI7B;CACT,MAAM,EAAE,WAAW,aAAa,cAAc;CAC9C,MAAM,WAAW,UAAU,YAAY,CAAC;CACxC,IAAI,OAAO,UAAU,eAAe,KAAK,UAAU,SAAS,GAC1D,OAAO;CAET,MAAM,WAAW,UAAU,gBAAgB,CAAC;CAC5C,MAAM,SAAS,OAAO,UAAU,eAAe,KAAK,UAAU,SAAS,IACnE,SAAS,aACT,KAAA;CACJ,IAAI,WAAW,KAAA,KAAa,OAAO,UAAU,eAAe,KAAK,UAAU,MAAM,GAC/E,OAAO;CAET,MAAM,IAAI,eACR,sBAAsB,YAAY,GAAG,UAAU,2GACjD;AACF;;AAGA,SAAgB,wBAAwB,QAI5B;CACV,MAAM,EAAE,WAAW,aAAa,YAAY;CAC5C,MAAM,WAAW,UAAU,YAAY,CAAC;CAIxC,MAAM,QAHQ,OAAO,UAAU,eAAe,KAAK,UAAU,OAAO,IAChE,SAAS,WACT,KAAA,EAAA,EACgB;CACpB,IAAI,CAAC,MAAM,SACT,MAAM,IAAI,eACR,0BAA0B,YAAY,GAAG,QAAQ,mCACnD;CAEF,OAAO;AACT;;;;;;AAOA,eAAsB,aAAa,QAMf;CAClB,MAAM,EAAE,YAAY,aAAa,UAAU;CAC3C,MAAM,UAAU,OAAO,WAAW;CAClC,IAAI,CAAC,WAAW,WAAW,UAAU,KAAK,CAAC,WAAW,WAAW,SAAS,GACxE,MAAM,IAAI,eACR,6BAA6B,WAAW,8BAC1C;CAGF,MAAM,UAAkC,CAAC;CACzC,IAAI,SAAS,aAAa,YAAY,WAAW,GAC/C,QAAQ,gBAAgB,UAAU;CAGpC,MAAM,WAAW,MAAM,iBAAiB,YAAY,OAAO;CAC3D,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,eAAe,8BAA8B,WAAW,SAAS,SAAS,UAAU,EAC5F,YAAY,SAAS,OACvB,CAAC;CAEH,MAAM,gBAAgB,OAAO,SAAS,SAAS,QAAQ,IAAI,gBAAgB,KAAK,IAAI,EAAE;CACtF,IAAI,OAAO,SAAS,aAAa,KAAK,gBAAgB,SACpD,MAAM,IAAI,eAAe,wBAAwB,YAAY,OAAO,CAAC;CAEvE,OAAO,MAAM,kBAAkB;EAAE;EAAU;EAAY;CAAQ,CAAC;AAClE;AAEA,SAAS,wBAAwB,YAAoB,SAAyB;CAC5E,OAAO,WAAW,WAAW,uBAAuB,UAAU,OAAO,KAAK;AAC5E;;;;;;AAOA,eAAe,kBAAkB,QAIb;CAClB,MAAM,EAAE,UAAU,YAAY,YAAY;CAC1C,MAAM,SAAS,SAAS,MAAM,UAAU;CACxC,IAAI,CAAC,QAAQ;EAGX,MAAM,cAAc,MAAM,SAAS,YAAY;EAC/C,IAAI,YAAY,aAAa,SAC3B,MAAM,IAAI,eAAe,wBAAwB,YAAY,OAAO,CAAC;EAEvE,OAAO,OAAO,KAAK,WAAW;CAChC;CAEA,MAAM,SAAmB,CAAC;CAC1B,IAAI,aAAa;CACjB,OAAO,MAAM;EACX,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;EAC1C,IAAI,MACF;EAEF,cAAc,MAAM;EACpB,IAAI,aAAa,SAAS;GACxB,MAAM,OAAO,OAAO;GACpB,MAAM,IAAI,eAAe,wBAAwB,YAAY,OAAO,CAAC;EACvE;EACA,OAAO,KAAK,OAAO,KAAK,KAAK,CAAC;CAChC;CACA,OAAO,OAAO,OAAO,MAAM;AAC7B;AAEA,SAAS,aAAa,MAAc,MAAuB;CACzD,IAAI;EACF,OAAO,IAAI,IAAI,IAAI,CAAC,CAAC,WAAW,IAAI,IAAI,IAAI,CAAC,CAAC;CAChD,QAAQ;EACN,OAAO;CACT;AACF;;;;;;AAOA,SAAgB,YAAY,QAAwB;CAClD,IAAI,CAAC,kBAAkB,KAAK,MAAM,GAChC,MAAM,IAAI,eAAe,gDAAgD,OAAO,EAAE;CAEpF,OAAO,QAAQ,OAAO,KAAK,QAAQ,KAAK,CAAC,CAAC,SAAS,QAAQ;AAC7D;;;;;;;;AASA,SAAgB,uBAAuB,QAM9B;CACP,MAAM,EAAE,SAAS,WAAW,QAAQ,SAAS,WAAW;CAExD,IAAI,cAAc,KAAA,GAAW;EAC3B,MAAM,MAAM,sBAAsB,SAAS;EAC3C,IAAI,CAAC,KAGH,MAAM,IAAI,eACR,mDAAmD,QAAQ,yDAC7D;EAEF,MAAM,SAAS,WAAW,IAAI,SAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,OAAO,QAAQ;EACxE,IAAI,WAAW,IAAI,QACjB,MAAM,IAAI,eACR,qCAAqC,QAAQ,aAAa,IAAI,UAAU,GAAG,IAAI,OAAO,QAAQ,IAAI,UAAU,GAAG,OAAO,2CACxH;EAEF;CACF;CAEA,IAAI,QAAQ;EACV,MAAM,SAAS,WAAW,MAAM,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,OAAO,KAAK;EAC9D,IAAI,WAAW,OAAO,YAAY,GAChC,MAAM,IAAI,eACR,qCAAqC,QAAQ,kBAAkB,OAAO,QAAQ,OAAO,2CACvF;EAEF;CACF;CAEA,QAAQ,KAAK,uCAAuC,QAAQ,iCAAiC;AAC/F;AAEA,SAAS,sBACP,WAC+D;CAC/D,IAAI,CAAC,WACH;CAEF,MAAM,UAAU,UACb,MAAM,KAAK,CAAC,CACZ,KAAK,UAAU;EACd,MAAM,iBAAiB,MAAM,QAAQ,GAAG;EACxC,IAAI,mBAAmB,IAAI,OAAO,KAAA;EAClC,MAAM,YAAY,MAAM,MAAM,GAAG,cAAc;EAE/C,MAAM,SAAS,MAAM,MAAM,iBAAiB,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM;EAChE,MAAM,QAAQ,+BAA+B,MAAM,MAAM,MAAM,SAAS;EACxE,IAAI,CAAC,SAAS,OAAO,WAAW,GAAG,OAAO,KAAA;EAC1C,OAAO;GAAE,WAAW;GAAO;EAAO;CACpC,CAAC,CAAC,CACD,QAAQ,UAAsE,QAAQ,KAAK,CAAC;CAE/F,KAAK,MAAM,aAAa,gCAAgC;EACtD,MAAM,QAAQ,QAAQ,MAAM,UAAU,MAAM,cAAc,SAAS;EACnE,IAAI,OACF,OAAO;CAEX;AAEF;;;;;AAMA,SAAgB,gBAAgB,QAAyD;CACvF,MAAM,EAAE,OAAO,WAAW;CAC1B,IAAI,MAAM,eAAe,OAAO,MAAM,eAAe,KACnD,OAAO,KACL,iLACF;MACK,IAAI,MAAM,eAAe,KAC9B,OAAO,KACL,kIACF;AAEJ;;;;;;;;;;;;;;;;;;AC7XA,MAAM,aAAa;AAOnB,IAAa,cAAb,cAAiC,MAAM;CACrC,YAAY,SAAiB,OAAiB;EAC5C,MAAM,SAAS,EAAE,MAAM,CAAC;EACxB,KAAK,OAAO;CACd;AACF;;;;;;AAiBA,SAAgB,sBAAsB,QAKnB;CACjB,MAAM,EAAE,SAAS,mBAAmB;CACpC,MAAM,WAAW,OAAO,YAAA;CACxB,MAAM,gBAAgB,OAAO,iBAAA;CAE7B,IAAI;CACJ,IAAI;EAGF,MAAM,WAAW,SAAS,EACxB,iBAAiB,gBAAgB,WAAW,IAAI,aAAa,IAAI,WACnE,CAAC;CACH,SAAS,OAAO;EACd,MAAM,IAAI,YAAY,oCAAoC,KAAK;CACjE;CAEA,OAAO,eAAe;EAAE;EAAK;EAAU;EAAe;CAAe,CAAC;AACxE;AAEA,SAAS,eAAe,QAKL;CACjB,MAAM,EAAE,KAAK,UAAU,eAAe,mBAAmB;CACzD,MAAM,QAAwB,CAAC;CAC/B,IAAI,aAAa;CACjB,IAAI,SAAS;CACb,IAAI;CACJ,IAAI;CAEJ,OAAO,SAAS,cAAc,IAAI,QAAQ;EACxC,MAAM,SAAS,IAAI,SAAS,QAAQ,SAAS,UAAU;EACvD,IAAI,YAAY,MAAM,GACpB;EAEF,qBAAqB,MAAM;EAE3B,MAAM,OAAO,gBAAgB,QAAQ,KAAK,IAAI,MAAM;EACpD,MAAM,WAAW,OAAO,aAAa,OAAO,QAAQ,CAAC;EACrD,MAAM,YAAY,SAAS;EAC3B,MAAM,UAAU,YAAY;EAC5B,IAAI,UAAU,IAAI,QAChB,MAAM,IAAI,YAAY,+DAA+D;EAGvF,QAAQ,UAAR;GACE,KAAK,KAAK;IACR,MAAM,UAAU,gBAAgB,IAAI,SAAS,WAAW,OAAO,CAAC;IAChE,IAAI,QAAQ,IAAI,MAAM,GAGpB,MAAM,IAAI,YAAY,6DAA6D;IAErF,iBAAiB,QAAQ,IAAI,MAAM,KAAK;IACxC;GACF;GACA,KAAK;IACH,kBAAkB,eAAe,IAAI,SAAS,QAAQ,WAAW,OAAO,CAAC;IACzE;GAEF,KAAK,KAAK;IAIR,MAAM,UAAU,gBAAgB,IAAI,SAAS,WAAW,OAAO,CAAC;IAChE,IAAI,QAAQ,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,GAC3C,MAAM,IAAI,YACR,2EACF;IAEF;GACF;GACA,KAAK;GACL,KAAK,MAAM;IACT,MAAM,UAAU,iBAAiB;KAAE;KAAQ;KAAiB;IAAe,CAAC;IAC5E,kBAAkB,KAAA;IAClB,iBAAiB,KAAA;IACjB,MAAM,eAAe,mBAAmB,OAAO;IAC/C,IAAI,iBAAiB,MAAM;KACzB,IAAI,MAAM,SAAS,IAAI,UACrB,MAAM,IAAI,YACR,6CAA6C,SAAS,2CACxD;KAEF,cAAc;KACd,IAAI,aAAa,eACf,MAAM,IAAI,YACR,6CAA6C,gBAAgB,OAAO,KAAK,6CAC3E;KAEF,MAAM,KAAK;MACT;MACA,SAAS,OAAO,KAAK,IAAI,SAAS,WAAW,OAAO,CAAC;KACvD,CAAC;IACH;IACA;GACF;GACA,KAAK;IAEH,kBAAkB,KAAA;IAClB,iBAAiB,KAAA;IACjB;GAEF,SAAS;IAEP,MAAM,UAAU,iBAAiB;KAAE;KAAQ;KAAiB;IAAe,CAAC;IAC5E,kBAAkB,KAAA;IAClB,iBAAiB,KAAA;IACjB,iBACE,wCAAwC,SAAS,SAAS,QAAQ,sCACpE;IACA;GACF;EACF;EAEA,SAAS,YAAY,KAAK,KAAK,OAAO,UAAU,IAAI;CACtD;CAEA,OAAO;AACT;AAEA,SAAS,YAAY,OAAwB;CAC3C,OAAO,MAAM,OAAO,SAAS,SAAS,CAAC;AACzC;;AAGA,SAAS,eAAe,OAAuB;CAC7C,MAAM,WAAW,MAAM,QAAQ,IAAI;CACnC,OAAO,aAAa,KAAK,QAAQ,MAAM,UAAU,GAAG,QAAQ;AAC9D;AAEA,SAAS,gBAAgB,OAAe,QAAgB,QAAgB,OAAuB;CAE7F,MADc,MAAM,WAAW,KAClB,SAAU,GACrB,MAAM,IAAI,YAAY,qCAAqC,MAAM,wBAAwB;CAG3F,MAAM,OAAO,eADD,MAAM,SAAS,UAAU,QAAQ,SAAS,MACxB,CAAC,CAAC,CAAC,KAAK;CACtC,IAAI,SAAS,IACX,OAAO;CAET,IAAI,CAAC,WAAW,KAAK,IAAI,GACvB,MAAM,IAAI,YAAY,uCAAuC,MAAM,OAAO;CAE5E,OAAO,OAAO,SAAS,MAAM,CAAC;AAChC;;;;;AAMA,SAAS,qBAAqB,QAAsB;CAClD,MAAM,SAAS,gBAAgB,QAAQ,KAAK,GAAG,UAAU;CACzD,IAAI,MAAM;CACV,KAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAC9B,OAAO,KAAK,OAAO,IAAI,MAAM,KAAQ,OAAO,MAAM;CAEpD,IAAI,QAAQ,QACV,MAAM,IAAI,YAAY,uCAAuC;AAEjE;AAEA,SAAS,YAAY,OAAe,QAAgB,QAAwB;CAC1E,MAAM,MAAM,MAAM,QAAQ,GAAG,MAAM;CACnC,MAAM,OAAO,QAAQ,MAAM,MAAM,SAAS,SAAS,SAAS,SAAS;CACrE,OAAO,MAAM,SAAS,QAAQ,QAAQ,IAAI;AAC5C;AAEA,SAAS,iBAAiB,QAIf;CACT,MAAM,EAAE,QAAQ,iBAAiB,mBAAmB;CACpD,IAAI,mBAAmB,KAAA,GACrB,OAAO;CAET,IAAI,oBAAoB,KAAA,GACtB,OAAO;CAET,MAAM,OAAO,YAAY,QAAQ,GAAG,GAAG;CAEvC,MAAM,SADQ,OAAO,SAAS,UAAU,KAAK,GAC1B,MAAM,UAAU,YAAY,QAAQ,KAAK,GAAG,IAAI;CACnE,OAAO,OAAO,SAAS,IAAI,GAAG,OAAO,GAAG,SAAS;AACnD;;;;;AAMA,SAAS,gBAAgB,MAAmC;CAC1D,MAAM,0BAAU,IAAI,IAAoB;CACxC,IAAI,SAAS;CACb,OAAO,SAAS,KAAK,QAAQ;EAC3B,IAAI,KAAK,YAAY,GACnB;EAEF,MAAM,aAAa,KAAK,QAAQ,IAAM,MAAM;EAC5C,IAAI,eAAe,IACjB,MAAM,IAAI,YAAY,8CAA8C;EAEtE,MAAM,eAAe,OAAO,SAAS,KAAK,SAAS,QAAQ,QAAQ,UAAU,GAAG,EAAE;EAClF,IACE,CAAC,OAAO,UAAU,YAAY,KAC9B,gBAAgB,KAChB,SAAS,eAAe,KAAK,QAE7B,MAAM,IAAI,YAAY,6CAA6C;EAGrE,MAAM,SAAS,KAAK,SAAS,QAAQ,aAAa,GAAG,SAAS,eAAe,CAAC;EAC9E,MAAM,cAAc,OAAO,QAAQ,GAAG;EACtC,IAAI,gBAAgB,IAClB,QAAQ,IAAI,OAAO,MAAM,GAAG,WAAW,GAAG,OAAO,MAAM,cAAc,CAAC,CAAC;EAEzE,UAAU;CACZ;CACA,OAAO;AACT;;;;;;;AAQA,SAAS,mBAAmB,SAAgC;CAC1D,IAAI,QAAQ,SAAS,IAAI,GACvB,MAAM,IAAI,YAAY,sCAAsC,QAAQ,EAAE;CAExE,IAAI,QAAQ,SAAS,IAAI,GACvB,MAAM,IAAI,YAAY,uCAAuC,QAAQ,EAAE;CAEzE,IAAI,QAAQ,WAAW,GAAG,GACxB,MAAM,IAAI,YAAY,sCAAsC,QAAQ,EAAE;CAExE,MAAM,WAAW,QAAQ,MAAM,GAAG,CAAC,CAAC,QAAQ,YAAY,YAAY,MAAM,YAAY,GAAG;CACzF,IAAI,SAAS,SAAS,IAAI,GACxB,MAAM,IAAI,YAAY,0CAA0C,QAAQ,EAAE;CAG5E,SAAS,MAAM;CACf,IAAI,SAAS,WAAW,GACtB,OAAO;CAET,OAAO,SAAS,KAAK,GAAG;AAC1B;;;;;;ACvSA,MAAa,oBAAoB,CAAC,UAAU,QAAQ;AAE1B,EAAE,KAAK,iBAAiB;;;ACHlD,MAAM,+BAAe,IAAI,IAAI,CAAC,cAAc,gBAAgB,CAAC;AAC7D,MAAM,+BAAe,IAAI,IAAI,CAAC,cAAc,gBAAgB,CAAC;;;;;;;;;;;AAY7D,SAAgB,YAAY,QAA8B;CAExD,IAAI,OAAO,WAAW,SAAS,KAAK,OAAO,WAAW,UAAU,GAC9D,OAAO,SAAS,MAAM;CAIxB,IAAI,OAAO,SAAS,GAAG,KAAK,CAAC,OAAO,SAAS,KAAK,GAAG;EACnD,MAAM,aAAa,OAAO,QAAQ,GAAG;EACrC,MAAM,SAAS,OAAO,UAAU,GAAG,UAAU;EAC7C,MAAM,OAAO,OAAO,UAAU,aAAa,CAAC;EAG5C,MAAM,WAAW,kBAAkB,MAAM,MAAM,MAAM,MAAM;EAC3D,IAAI,UACF,OAAO;GAAE;GAAU,GAAG,eAAe,IAAI;EAAE;EAK7C,OAAO;GAAE,UAAU;GAAU,GAAG,eAAe,MAAM;EAAE;CACzD;CAGA,OAAO;EAAE,UAAU;EAAU,GAAG,eAAe,MAAM;CAAE;AACzD;;;;AAKA,SAAS,SAAS,KAA2B;CAC3C,MAAM,SAAS,IAAI,IAAI,GAAG;CAC1B,MAAM,OAAO,OAAO,SAAS,YAAY;CAEzC,IAAI;CACJ,IAAI,aAAa,IAAI,IAAI,GACvB,WAAW;MACN,IAAI,aAAa,IAAI,IAAI,GAC9B,WAAW;MAEX,MAAM,IAAI,MACR,kCAAkC,KAAK,yBAAyB,kBAAkB,KAAK,IAAI,GAC7F;CAIF,MAAM,WAAW,OAAO,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO;CAE1D,IAAI,SAAS,SAAS,GACpB,MAAM,IAAI,MAAM,WAAW,SAAS,QAAQ,IAAI,6BAA6B,KAAK,YAAY;CAGhG,MAAM,QAAQ,SAAS;CACvB,MAAM,OAAO,SAAS,EAAE,EAAE,QAAQ,UAAU,EAAE;CAG9C,IAAI,SAAS,SAAS,MAAM,SAAS,OAAO,UAAU,SAAS,OAAO,SAAS;EAC7E,MAAM,MAAM,SAAS;EACrB,MAAM,OAAO,SAAS,SAAS,IAAI,SAAS,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,KAAA;EACjE,OAAO;GACL;GACA,OAAO,SAAS;GAChB,MAAM,QAAQ;GACd;GACA;EACF;CACF;CAEA,OAAO;EACL;EACA,OAAO,SAAS;EAChB,MAAM,QAAQ;CAChB;AACF;;;;AAKA,SAAS,eAAe,QAAgD;CAEtE,IAAI,YAAY;CAChB,IAAI;CACJ,IAAI;CAGJ,MAAM,aAAa,UAAU,QAAQ,GAAG;CACxC,IAAI,eAAe,IAAI;EACrB,OAAO,UAAU,UAAU,aAAa,CAAC;EACzC,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,mBAAmB,OAAO,kCAAkC;EAE9E,YAAY,UAAU,UAAU,GAAG,UAAU;CAC/C;CAGA,MAAM,UAAU,UAAU,QAAQ,GAAG;CACrC,IAAI,YAAY,IAAI;EAClB,MAAM,UAAU,UAAU,UAAU,CAAC;EACrC,IAAI,CAAC,KACH,MAAM,IAAI,MAAM,mBAAmB,OAAO,iCAAiC;EAE7E,YAAY,UAAU,UAAU,GAAG,OAAO;CAC5C;CAGA,MAAM,aAAa,UAAU,QAAQ,GAAG;CACxC,IAAI,eAAe,IACjB,MAAM,IAAI,MACR,mBAAmB,OAAO,kEAC5B;CAGF,MAAM,QAAQ,UAAU,UAAU,GAAG,UAAU;CAC/C,MAAM,OAAO,UAAU,UAAU,aAAa,CAAC;CAE/C,IAAI,CAAC,SAAS,CAAC,MACb,MAAM,IAAI,MAAM,mBAAmB,OAAO,oCAAoC;CAGhF,OAAO;EACL;EACA;EACA;EACA;CACF;AACF;;;AC7BA,SAAS,sBAAsB,QAGc;CAC3C,IAAI,CAAC,OAAO,aACV;CAEF,IAAI,OAAO,aACT,OAAO,OAAO,KAAK,2BAA2B;CAEhD,OAAO;EACL,mBAAmB;EACnB,kBAAkB;EAClB,kBAAkB;EAClB,mBAAmB;CACrB;AACF;;;;;AAgBA,eAAsB,uBAAuB,QAKH;CACxC,MAAM,EAAE,SAAS,aAAa,UAAU,CAAC,GAAG,WAAW;CACvD,MAAM,EACJ,gBAAgB,OAChB,cAAc,OACd,SAAS,OACT,8BAA8B,OAC9B,wBAAwB,OACxB,uBAAuB,OACvB,qBAAqB,CAAC,GACtB,oBAAoB,CAAC,MACnB;CACJ,MAAM,cAAc,sBAAsB;EACxC;EACA;CACF,CAAC;CACD,IAAI,aACF,OAAO;CAGT,MAAM,+BAA+B,WAAW;CAKhD,IAAI,OAAoB,MAAM,aAAa;EAAE;EAAa;CAAO,CAAC;CAClE,IAAI,UAA0B,MAAM,gBAAgB;EAAE;EAAa;CAAO,CAAC;CAI3E,2BAA2B;EAAE;EAAQ;EAAM;EAAS;CAAQ,CAAC;CAE7D,MAAM,mBAAmB,KAAK,UAAU,IAAI;CAC5C,MAAM,sBAAsB,KAAK,UAAU,OAAO;CAIlD,MAAM,SAAS,IAAI,aAAa,EAAE,OADpB,aAAa,aAAa,QAAQ,KACV,EAAE,CAAC;CAGzC,MAAM,kBAAkB,MAAM,sBAAsB,WAAW;CAC/D,MAAM,iBAAiB,MAAM,kBAAkB,WAAW;CAE1D,IAAI,CAAC,+BAA+B,CAAC,QAAQ;EAC3C,MAAM,6BAA6B;GAAE;GAAa;GAAM;GAAS;GAAS;EAAO,CAAC;EAClF,OAAO,sBAAsB;GAAE;GAAM;GAAS;EAAO,CAAC;EACtD,UAAU,yBAAyB;GAAE;GAAS;GAAS;EAAO,CAAC;CACjE;CAEA,IAAI,kBAAkB;CACtB,IAAI,iBAAiB;CACrB,IAAI,oBAAoB;CACxB,MAAM,uBAAuB,IAAI,IAAI,kBAAkB;CACvD,MAAM,sBAAsB,IAAI,IAAI,iBAAiB;CAErD,KAAK,MAAM,eAAe,SACxB,IAAI;EACF,MAAM,SAAS,MAAM,yBAAyB;GAC5C,gBAAgB,CACd,KAAK,aAAa,yCAAyC,GAC3D,KAAK,aAAa,wCAAwC,CAC5D;GACA,cACE,kBAAkB;IAChB;IACA;IACA;IACA;IACA;IACA;IACA;IACA,0BAA0B;IAC1B,yBAAyB;IACzB;IACA;IACA;GACF,CAAC;EACL,CAAC;EAED,OAAO,OAAO;EACd,UAAU,OAAO;EACjB,qBAAqB,2BAA2B;GAC9C,eAAe;GACf,cAAc;GACd,oBAAoB,OAAO;GAC3B,mBAAmB,OAAO;EAC5B,CAAC;EACD,mBAAmB,OAAO;EAC1B,kBAAkB,OAAO;EACzB,cAAc;GAAE,OAAO,OAAO;GAAmB,QAAQ;EAAqB,CAAC;EAC/E,cAAc;GAAE,OAAO,OAAO;GAAkB,QAAQ;EAAoB,CAAC;CAC/E,SAAS,OAAO;EACd,qBAAqB;EACrB,sBAAsB;GAAE;GAAa;GAAO;EAAO,CAAC;CACtD;CAGF,MAAM,wBAAwB;EAC5B;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,OAAO;EACL,mBAAmB;EACnB,kBAAkB;EAClB,kBAAkB,QAAQ;EAC1B;CACF;AACF;AAEA,eAAe,+BAA+B,aAAoC;CAChF,MAAM,oBAAoB,KAAK,aAAa,yCAAyC;CACrF,MAAM,mBAAmB,KAAK,aAAa,wCAAwC;CACnF,MAAM,kBAAkB,KAAK,aAAa,wCAAwC;CAClF,MAAM,qBAAqB,KAAK,aAAa,4CAA4C;CACzF,MAAM,QAAQ,IAAI;EAChB,6BAA6B;GAAE,UAAU;GAAa,YAAY;EAAkB,CAAC;EACrF,6BAA6B;GAAE,UAAU;GAAa,YAAY;EAAiB,CAAC;EACpF,6BAA6B;GAAE,UAAU;GAAa,YAAY;EAAgB,CAAC;EACnF,6BAA6B;GAAE,UAAU;GAAa,YAAY;EAAmB,CAAC;EACtF,wBAAwB,iBAAiB;EACzC,wBAAwB,gBAAgB;CAC1C,CAAC;CACD,IAAI,MAAM,gBAAgB,iBAAiB,GACzC,MAAM,6BAA6B,iBAAiB;CAEtD,IAAI,MAAM,gBAAgB,gBAAgB,GACxC,MAAM,6BAA6B,gBAAgB;AAEvD;AAEA,SAAS,cAAc,QAAwD;CAC7E,OAAO,MAAM,SAAS,SAAS,OAAO,OAAO,IAAI,IAAI,CAAC;AACxD;AAEA,SAAS,2BAA2B,EAClC,eACA,cACA,oBACA,qBAMS;CACT,OAAQ,iBAAiB,mBAAmB,WAAW,KACpD,gBAAgB,kBAAkB,WAAW,IAC5C,IACA;AACN;AAEA,SAAS,iBAAiB,aAGxB;CACA,MAAM,qBAAqB,YAAY,WAAW,KAAA,KAAa,YAAY,UAAU,KAAA;CACrF,OAAO;EACL,QAAQ,YAAY,WAAW,qBAAqB,KAAA,IAAY,CAAC,GAAG;EACpE,OAAO,YAAY;CACrB;AACF;AAEA,eAAe,kBAAkB,aAA2C;CAC1E,MAAM,WAAW,KAAK,aAAa,gCAAgC;CACnE,MAAM,QAAQ,MAAM,iBAAiB,KAAK,UAAU,MAAM,MAAM,CAAC;CACjE,MAAM,6BAAa,IAAI,IAAY;CACnC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,eAAe,SAAS,UAAU,IAAI;EAC5C,IAAI,aAAa,WAAW,WAAW,KAAK,GAC1C;EAEF,WAAW,IAAI,aAAa,QAAQ,UAAU,EAAE,CAAC;CACnD;CACA,OAAO;AACT;AAEA,eAAsB,6BAA6B,EACjD,SACA,aACA,UAKoB;CACpB,MAAM,OAAO,MAAM,aAAa;EAAE;EAAa;CAAO,CAAC;CACvD,MAAM,UAAU,MAAM,gBAAgB;EAAE;EAAa;CAAO,CAAC;CAC7D,MAAM,aAAa,KAAK,aAAa,yCAAyC;CAC9E,MAAM,6BAAa,IAAI,IAAY;CACnC,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,gBAAgB,OAAO,aAAa,cAAc;EACxD,MAAM,QAAQ,eACV,mBAAmB,SAAS,OAAO,MAAM,IACzC,gBAAgB,MAAM,OAAO,MAAM;EACvC,MAAM,mBAAmB,QACrB,eACE,uBAAuB,KAAwB,IAC/C,oBAAoB,KAAqB,IAC3C,CAAC;EACL,IAAI,UAAU,KAAA,KAAa,CAAE,MAAM,uBAAuB,YAAY,gBAAgB,GACpF,MAAM,IAAI,MACR,oBAAoB,OAAO,OAAO,+EACpC;EAEF,iBAAiB,SAAS,cAAc,WAAW,IAAI,SAAS,CAAC;CACnE;CACA,OAAO,CAAC,GAAG,UAAU;AACvB;AAEA,eAAsB,4BAA4B,EAChD,SACA,aACA,UAKoB;CACpB,MAAM,OAAO,MAAM,aAAa;EAAE;EAAa;CAAO,CAAC;CACvD,MAAM,UAAU,MAAM,gBAAgB;EAAE;EAAa;CAAO,CAAC;CAC7D,MAAM,aAAa,KAAK,aAAa,wCAAwC;CAC7E,MAAM,4BAAY,IAAI,IAAY;CAClC,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,iBAAiB,MAAM,CAAC,CAAC,UAAU,KAAA,GACrC;EAEF,MAAM,gBAAgB,OAAO,aAAa,cAAc;EACxD,MAAM,QAAQ,eACV,mBAAmB,SAAS,OAAO,MAAM,IACzC,gBAAgB,MAAM,OAAO,MAAM;EACvC,MAAM,kBAAkB,QACpB,eACE,sBAAsB,KAAwB,IAC9C,mBAAmB,KAAqB,IAC1C,CAAC;EACL,IACE,UAAU,KAAA,KACV,MAAM,UAAU,KAAA,KAChB,CAAC,wBAAwB;GAAE,QAAQ;GAAO,aAAa;EAAO,CAAC,KAC/D,CAAE,MAAM,yBAAyB;GAAE;GAAY,QAAQ;EAAM,CAAC,GAE9D,MAAM,IAAI,MACR,oBAAoB,OAAO,OAAO,+EACpC;EAEF,gBAAgB,SAAS,aAAa,UAAU,IAAI,QAAQ,CAAC;CAC/D;CACA,OAAO,CAAC,GAAG,SAAS;AACtB;;;;;AAMA,eAAe,kBAAkB,QAoB9B;CACD,MAAM,EAAE,aAAa,MAAM,YAAY;CACvC,KAAK,YAAY,aAAa,cAAc,OAAO;EACjD,MAAM,SAAS,MAAM,kBAAkB;GACrC;GACA,aAAa,OAAO;GACpB;GACA,iBAAiB,OAAO;GACxB,gBAAgB,OAAO;GACvB,0BAA0B,OAAO;GACjC,yBAAyB,OAAO;GAChC,eAAe,OAAO;GACtB,QAAQ,OAAO;EACjB,CAAC;EACD,OAAO;GACL,YAAY,OAAO;GACnB,WAAW,OAAO;GAClB,mBAAmB,OAAO;GAC1B,kBAAkB,OAAO;GACzB;GACA,SAAS,OAAO;EAClB;CACF;CACA,MAAM,UAAU,iBAAiB,WAAW;CAC5C,IAAI,cAAc;CAClB,IAAI,aAAa;CACjB,IAAI,oBAA8B,CAAC;CACnC,IAAI,QAAQ,WAAW,KAAA,GAAW;EAChC,MAAM,SAAS,MAAM,uBAAuB;GAC1C,aAAa;IAAE,GAAG;IAAa,QAAQ,QAAQ;GAAO;GACtD,QAAQ,OAAO;GACf,aAAa,OAAO;GACpB,MAAM;GACN,iBAAiB,OAAO;GACxB,0BAA0B,OAAO;GACjC,eAAe,OAAO;GACtB,QAAQ,OAAO;GACf,QAAQ,OAAO;EACjB,CAAC;EACD,cAAc,OAAO;EACrB,aAAa,OAAO;EACpB,oBAAoB,OAAO;CAC7B;CAEA,IAAI,YAAY;CAChB,IAAI,mBAA6B,CAAC;CAClC,IAAI,QAAQ,UAAU,KAAA,GAAW;EAC/B,MAAM,SAAS,MAAM,sBAAsB;GACzC,aAAa;IAAE,GAAG;IAAa,OAAO,QAAQ;GAAM;GACpD,QAAQ,OAAO;GACf,aAAa,OAAO;GACpB,MAAM;GACN,gBAAgB,OAAO;GACvB,yBAAyB,OAAO;GAGhC,eAAe,QAAQ,WAAW,KAAA,IAAY,OAAO,gBAAgB;GACrE,cAAc,QAAQ,WAAW,KAAA,KAAa,OAAO;GACrD,QAAQ,OAAO;GACf,QAAQ,OAAO;EACjB,CAAC;EACD,cAAc,OAAO;EACrB,YAAY,OAAO;EACnB,mBAAmB,OAAO;CAC5B,OACE,cAAc,MAAM,qBAAqB;EACvC,MAAM;EACN;EACA,aAAa,OAAO;EACpB,yBAAyB,OAAO;EAChC,QAAQ,OAAO;CACjB,CAAC;CAEH,OAAO;EACL;EACA;EACA;EACA;EACA,MAAM;EACN;CACF;AACF;AAEA,eAAe,qBAAqB,QAMX;CACvB,MAAM,EAAE,MAAM,aAAa,aAAa,yBAAyB,WAAW;CAC5E,MAAM,SAAS,gBAAgB,MAAM,YAAY,MAAM;CACvD,IAAI,QAAQ,UAAU,KAAA,GACpB,OAAO;CAET,MAAM,0BAA0B;EAC9B,YAAY,KAAK,aAAa,wCAAwC;EACtE,iBAAiB,mBAAmB,MAAM;EAC1C,oBAAoB;EACpB;CACF,CAAC;CACD,OAAO,gBAAgB,MAAM,YAAY,QAAQ;EAC/C,GAAG;EACH,OAAO,KAAA;EACP,eAAe,KAAA;EACf,WAAW,KAAA;EACX,mBAAmB,KAAA;CACrB,CAAC;AACH;;AAGA,SAAS,sBAAsB,QAItB;CACP,MAAM,EAAE,aAAa,OAAO,WAAW;CACvC,OAAO,MAAM,2BAA2B,YAAY,OAAO,KAAK,YAAY,KAAK,GAAG;CACpF,IAAI,iBAAiB,mBACnB,mBAAmB;EAAE;EAAO;CAAO,CAAC;MAC/B,IAAI,iBAAiB,gBAC1B,kBAAkB;EAAE;EAAO;CAAO,CAAC;MAC9B,IAAI,iBAAiB,gBAC1B,gBAAgB;EAAE;EAAO;CAAO,CAAC;AAErC;;AAGA,eAAe,wBAAwB,QAQrB;CAChB,MAAM,EAAE,aAAa,MAAM,SAAS,kBAAkB,qBAAqB,QAAQ,WACjF;CACF,IAAI,CAAC,UAAU,KAAK,UAAU,IAAI,MAAM,kBACtC,MAAM,cAAc;EAAE;EAAa;EAAM;CAAO,CAAC;MAEjD,OAAO,MAAM,qCAAqC;CAEpD,IAAI,CAAC,UAAU,KAAK,UAAU,OAAO,MAAM,qBACzC,MAAM,iBAAiB;EAAE;EAAa,MAAM;EAAS;CAAO,CAAC;MAE7D,OAAO,MAAM,yCAAyC;AAE1D;;;;AAKA,SAAS,kBAAkB,QAAyD;CAClF,MAAM,EAAE,OAAO,WAAW;CAC1B,IAAI,MAAM,QAAQ,SAAS,eAAe,GACxC,OAAO,KAAK,4DAA4D;MAExE,OAAO,KAAK,kFAAkF;AAElG;;;;;;;AAQA,SAASC,gCAA8B,QAI9B;CACP,MAAM,EAAE,MAAM,SAAS,YAAY;CACnC,MAAM,cAAwB,CAAC;CAE/B,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,UACH,OAAO,aAAa,cAAc,QAC/B,mBAAmB,SAAS,OAAO,MAAM,IACzC,gBAAgB,MAAM,OAAO,MAAM;EACzC,MAAM,eACJ,iBAAiB,MAAM,CAAC,CAAC,UAAU,KAAA,KAClC,WAAW,KAAA,KAAa,wBAAwB;GAAE;GAAQ,aAAa;EAAO,CAAC;EAClF,IAAI,CAAC,UAAU,CAAC,cACd,YAAY,KAAK,OAAO,MAAM;CAElC;CACA,IAAI,YAAY,SAAS,GACvB,MAAM,IAAI,MACR,2DAA2D,YAAY,KAAK,IAAI,EAAE,iDACpF;AAEJ;AAEA,SAAS,2BAA2B,QAK3B;CACP,IAAI,OAAO,QACT,gCAA8B,MAAM;AAExC;;;;;AAMA,eAAe,uBAAuB,QAUqD;CACzF,MAAM,EACJ,aACA,QACA,aACA,MACA,iBACA,0BACA,eACA,QACA,WACE;CAEJ,KADkB,YAAY,aAAa,cACzB,OAChB,OAAO,kBAAkB;EACvB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAEH,OAAO,YAAY;EACjB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;AACH;;;;;AAMA,SAAS,sBAAsB,QAIf;CACd,MAAM,EAAE,MAAM,SAAS,WAAW;CAClC,MAAM,aAAa,IAAI,IACrB,QACG,QAAQ,OAAO,EAAE,aAAa,cAAc,KAAK,CAAC,CAClD,KAAK,MAAM,mBAAmB,EAAE,MAAM,CAAC,CAC5C;CACA,MAAM,gBAAqC,CAAC;CAC5C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,OAAO,GACpD,IAAI,WAAW,IAAI,mBAAmB,GAAG,CAAC,GACxC,cAAc,OAAO;MAErB,OAAO,MAAM,gCAAgC,KAAK;CAGtD,OAAO;EAAE,iBAAiB,KAAK;EAAiB,SAAS;CAAc;AACzE;;;;;AAMA,SAAS,yBAAyB,QAIf;CACjB,MAAM,EAAE,SAAS,SAAS,WAAW;CACrC,MAAM,aAAa,IAAI,IACrB,QACG,QAAQ,OAAO,EAAE,aAAa,cAAc,KAAK,CAAC,CAClD,KAAK,MAAM,sBAAsB,EAAE,MAAM,CAAC,CAC/C;CACA,MAAM,gBAAwC,CAAC;CAC/C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,OAAO,GACvD,IAAI,WAAW,IAAI,sBAAsB,GAAG,CAAC,GAC3C,cAAc,OAAO;MAErB,OAAO,MAAM,oCAAoC,KAAK;CAG1D,OAAO;EAAE,iBAAiB,QAAQ;EAAiB,SAAS;CAAc;AAC5E;AAEA,eAAe,6BAA6B,QAM1B;CAChB,MAAM,EAAE,aAAa,MAAM,SAAS,SAAS,WAAW;CACxD,MAAM,gBAAgB,IAAI,IACxB,QACG,QAAQ,YAAY,OAAO,aAAa,cAAc,KAAK,CAAC,CAC5D,KAAK,WAAW,mBAAmB,OAAO,MAAM,CAAC,CACtD;CACA,MAAM,gBAAgB,IAAI,IACxB,QACG,QAAQ,YAAY,OAAO,aAAa,cAAc,KAAK,CAAC,CAC5D,KAAK,WAAW,sBAAsB,OAAO,MAAM,CAAC,CACzD;CACA,MAAM,gBAAgB,CACpB,GAAG,OAAO,QAAQ,KAAK,OAAO,CAAC,CAC5B,QAAQ,CAAC,SAAS,cAAc,IAAI,mBAAmB,GAAG,CAAC,CAAC,CAAC,CAC7D,KAAK,GAAG,WAAW,KAAK,GAC3B,GAAG,OAAO,QAAQ,QAAQ,OAAO,CAAC,CAC/B,QAAQ,CAAC,SAAS,cAAc,IAAI,sBAAsB,GAAG,CAAC,CAAC,CAAC,CAChE,KAAK,GAAG,WAAW,KAAK,CAC7B;CACA,MAAM,sBAAsB,IAAI,IAAI,cAAc,SAAS,UAAU,OAAO,KAAK,MAAM,MAAM,CAAC,CAAC;CAC/F,MAAM,qBAAqB,IAAI,IAC7B,cAAc,SAAS,UAAU,OAAO,KAAK,MAAM,SAAS,CAAC,CAAC,CAAC,CACjE;CACA,MAAM,eAAe,CACnB,GAAG,OAAO,QAAQ,KAAK,OAAO,CAAC,CAC5B,QAAQ,CAAC,SAAS,CAAC,cAAc,IAAI,mBAAmB,GAAG,CAAC,CAAC,CAAC,CAC9D,KAAK,GAAG,WAAW,KAAK,GAC3B,GAAG,OAAO,QAAQ,QAAQ,OAAO,CAAC,CAC/B,QAAQ,CAAC,SAAS,CAAC,cAAc,IAAI,sBAAsB,GAAG,CAAC,CAAC,CAAC,CACjE,KAAK,GAAG,WAAW,KAAK,CAC7B;CACA,MAAM,mBAAmB,KAAK,aAAa,yCAAyC;CACpF,MAAM,kBAAkB,KAAK,aAAa,wCAAwC;CAClF,KAAK,MAAM,SAAS,cAAc;EAChC,MAAM,2BAA2B;GAC/B,YAAY;GACZ,kBAAkB,OAAO,KAAK,MAAM,MAAM;GAC1C;GACA;EACF,CAAC;EACD,MAAM,0BAA0B;GAC9B,YAAY;GACZ,iBAAiB,OAAO,KAAK,MAAM,SAAS,CAAC,CAAC;GAC9C;GACA;EACF,CAAC;CACH;AACF;;;;AAKA,eAAe,uBAAuB,YAAoB,YAAwC;CAChG,IAAI,WAAW,WAAW,GAAG,OAAO;CACpC,KAAK,MAAM,QAAQ,YACjB,IAAI,CAAE,MAAM,gBAAgB,KAAK,YAAY,IAAI,CAAC,GAChD,OAAO;CAGX,OAAO;AACT;AAEA,eAAe,yBAAyB,QAGnB;CACnB,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,OAAO,OAAO,SAAS,CAAC,CAAC,GAAG;EACrE,MAAM,WAAW,KAAK,OAAO,YAAY,GAAG,KAAK,IAAI;EACrD,IAAI,CAAE,MAAM,WAAW,QAAQ,GAC7B,OAAO;EAET,IAAI,qBAAqB,MAAM,gBAAgB,QAAQ,CAAC,MAAM,MAAM,WAClE,OAAO;CAEX;CACA,OAAO;AACT;AAEA,eAAe,oBAAoB,QAOd;CACnB,MAAM,EACJ,QACA,aACA,iBACA,gBACA,yBACA,eACE;CACJ,IAAI,CAAC,wBAAwB;EAAE;EAAQ;CAAY,CAAC,GAClD,OAAO;CAET,IAAI,OAAO,sBAAsB,KAAA,GAC/B,OAAO;CAET,MAAM,qCAAqB,IAAI,IAAI;EACjC,GAAG;EACH,GAAG;EACH,GAAG;CACL,CAAC;CACD,IAAI,OAAO,kBAAkB,MAAM,aAAa,CAAC,mBAAmB,IAAI,QAAQ,CAAC,GAC/E,OAAO;CAET,IACE,gBAAgB,MACb,aAAa,eAAe,IAAI,QAAQ,KAAK,wBAAwB,IAAI,QAAQ,CACpF,GAEA,OAAO;CAET,OAAO,yBAAyB;EAAE;EAAY;CAAO,CAAC;AACxD;;;;;AAUA,eAAe,2BAA2B,QAKxB;CAChB,MAAM,EAAE,YAAY,kBAAkB,sCAAsB,IAAI,IAAI,GAAG,WAAW;CAClF,MAAM,qBAAqB,QAAQ,UAAU;CAC7C,KAAK,MAAM,aAAa,kBAAkB;EACxC,IAAI,oBAAoB,IAAI,SAAS,GACnC;EAEF,MAAM,UAAU,KAAK,YAAY,SAAS;EAC1C,IAAI,CAAC,QAAQ,OAAO,CAAC,CAAC,WAAW,qBAAqB,GAAG,GAAG;GAC1D,OAAO,KACL,wBAAwB,UAAU,mDACpC;GACA;EACF;EACA,IAAI,MAAM,gBAAgB,OAAO,GAC/B,MAAM,sBAAsB,OAAO;CAEvC;AACF;AAEA,eAAe,0BAA0B,QAKvB;CAChB,MAAM,EAAE,YAAY,iBAAiB,oBAAoB,WAAW;CACpE,MAAM,qBAAqB,QAAQ,UAAU;CAC7C,KAAK,MAAM,YAAY,iBAAiB;EACtC,IAAI,mBAAmB,IAAI,QAAQ,GACjC;EAEF,MAAM,WAAW,KAAK,YAAY,GAAG,SAAS,IAAI;EAClD,IAAI,CAAC,QAAQ,QAAQ,CAAC,CAAC,WAAW,qBAAqB,GAAG,GAAG;GAC3D,OAAO,KACL,wBAAwB,SAAS,mDACnC;GACA;EACF;EACA,IAAI,MAAM,WAAW,QAAQ,GAC3B,MAAM,iBAAiB,QAAQ;CAEnC;AACF;AAEA,eAAe,oBAAoB,QAWK;CACtC,MAAM,EACJ,OACA,YACA,QACA,iBACA,aACA,WACA,gBACA,yBACA,wBACA,WACE;CACJ,MAAM,qBAAqB;CAC3B,MAAM,mBAAmB,MAAM,QAC5B,SACC,CAAC,eAAe;EACd,UAAU,KAAK;EACf;EACA;EACA;EACA;CACF,CAAC,CACL;CACA,MAAM,mCAAmB,IAAI,IAAoB;CACjD,KAAK,MAAM,QAAQ,iBAAiB;EAClC,MAAM,OAAO,KAAK,YAAY,GAAG,KAAK,IAAI;EAC1C,IAAI,CAAC,mBAAmB,IAAI,IAAI,KAAM,MAAM,WAAW,IAAI,GACzD,iBAAiB,IAAI,MAAM,MAAM,gBAAgB,IAAI,CAAC;CAE1D;CAEA,IAAI;EACF,MAAM,0BAA0B;GAC9B;GACA;GACA;GACA;EACF,CAAC;EACD,MAAM,eAA2C,CAAC;EAClD,KAAK,MAAM,QAAQ,kBACjB,aAAa,KAAK,QAAQ,MAAM,6BAA6B;GAC3D;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EAEH,OAAO;CACT,SAAS,OAAO;EACd,KAAK,MAAM,QAAQ,kBACjB,MAAM,iBAAiB,KAAK,YAAY,GAAG,KAAK,KAAK,IAAI,CAAC;EAE5D,KAAK,MAAM,CAAC,MAAM,YAAY,kBAC5B,MAAM,iBAAiB,KAAK,YAAY,GAAG,KAAK,IAAI,GAAG,OAAO;EAEhE,MAAM;CACR;AACF;;;;;AAMA,SAAS,gBAAgB,QAMb;CACV,MAAM,EAAE,WAAW,WAAW,iBAAiB,0BAA0B,WAAW;CACpF,IAAI,UAAU,SAAS,IAAI,KAAK,UAAU,SAAS,GAAG,KAAK,UAAU,SAAS,IAAI,GAAG;EACnF,OAAO,KACL,qCAAqC,UAAU,SAAS,UAAU,sCACpE;EACA,OAAO;CACT;CACA,IAAI,gBAAgB,IAAI,SAAS,GAAG;EAClC,OAAO,MACL,0BAA0B,UAAU,SAAS,UAAU,gCACzD;EACA,OAAO;CACT;CACA,IAAI,yBAAyB,IAAI,SAAS,GAAG;EAC3C,OAAO,KACL,6BAA6B,UAAU,SAAS,UAAU,uCAC5D;EACA,OAAO;CACT;CACA,OAAO;AACT;AAEA,SAAS,eAAe,QAMZ;CACV,MAAM,EAAE,UAAU,WAAW,gBAAgB,yBAAyB,WAAW;CACjF,IAAI,CAAC,gBAAgB,QAAQ,GAAG;EAC9B,OAAO,KAAK,oCAAoC,SAAS,SAAS,UAAU,EAAE;EAC9E,OAAO;CACT;CACA,IAAI,eAAe,IAAI,QAAQ,GAAG;EAChC,OAAO,MACL,yBAAyB,SAAS,SAAS,UAAU,+BACvD;EACA,OAAO;CACT;CACA,IAAI,wBAAwB,IAAI,QAAQ,GAAG;EACzC,OAAO,KACL,4BAA4B,SAAS,SAAS,UAAU,uCAC1D;EACA,OAAO;CACT;CACA,OAAO;AACT;AAEA,SAAS,gBAAgB,UAA2B;CAClD,OAAO,EACL,SAAS,SAAS,IAAI,KACtB,SAAS,SAAS,GAAG,KACrB,SAAS,SAAS,IAAI,KACtB,SAAS,WAAW,KACpB;EAAC;EAAa;EAAe;CAAW,CAAC,CAAC,SAAS,QAAQ;AAE/D;AAEA,eAAe,6BAA6B,QAQpB;CACtB,MAAM,EACJ,MACA,YACA,QACA,aACA,WACA,yBAAyB,MACzB,WACE;CACJ,MAAM,eAAe,GAAG,KAAK,KAAK;CAClC,mBAAmB;EAAE;EAAc,iBAAiB;CAAW,CAAC;CAChE,MAAM,iBAAiB,KAAK,YAAY,YAAY,GAAG,KAAK,OAAO;CACnE,MAAM,YAAY,qBAAqB,KAAK,OAAO;CACnD,MAAM,kBAAkB,QAAQ,QAAQ,KAAK;CAC7C,IACE,0BACA,iBAAiB,aACjB,gBAAgB,cAAc,aAC9B,gBAAgB,QAAQ,aAExB,OAAO,KACL,gCAAgC,KAAK,KAAK,SAAS,UAAU,cAAc,gBAAgB,UAAU,UAAU,UAAU,wCAC3H;CAEF,OAAO,EAAE,UAAU;AACrB;;;;;AAMA,eAAe,8BAA8B,QAQpB;CACvB,MAAM,EAAE,WAAW,OAAO,YAAY,QAAQ,aAAa,WAAW,WAAW;CACjF,MAAM,UAAoD,CAAC;CAE3D,KAAK,MAAM,QAAQ,OAAO;EACxB,mBAAmB;GACjB,cAAc,KAAK;GACnB,iBAAiB,KAAK,YAAY,SAAS;EAC7C,CAAC;EACD,MAAM,iBAAiB,KAAK,YAAY,WAAW,KAAK,YAAY,GAAG,KAAK,OAAO;EACnF,QAAQ,KAAK;GAAE,MAAM,KAAK;GAAc,SAAS,KAAK;EAAQ,CAAC;CACjE;CAEA,MAAM,YAAY,sBAAsB,OAAO;CAC/C,MAAM,mBAAmB,QAAQ,OAAO;CACxC,IACE,kBAAkB,aAClB,iBAAiB,cAAc,aAC/B,gBAAgB,QAAQ,aAExB,OAAO,KACL,iCAAiC,UAAU,SAAS,UAAU,cAAc,iBAAiB,UAAU,UAAU,UAAU,wCAC7H;CAGF,OAAO,EAAE,UAAU;AACrB;;;;;;;AAQA,SAAS,6BAA6B,QAIN;CAC9B,MAAM,EAAE,eAAe,cAAc,qBAAqB;CAC1D,MAAM,YAAY,IAAI,IAAI,gBAAgB;CAC1C,MAAM,eAA4C,EAAE,GAAG,cAAc;CACrE,IAAI,cACG;OAAA,MAAM,CAAC,WAAW,eAAe,OAAO,QAAQ,YAAY,GAC/D,IAAI,EAAE,aAAa,iBAAiB,UAAU,IAAI,SAAS,GACzD,aAAa,aAAa;CAAA;CAIhC,OAAO;AACT;AAEA,SAAS,0BAA0B,EACjC,YACA,UAIO;CACP,IAAI,WAAW,WAAW,GACxB,MAAM,IAAI,MAAM,+BAA+B,OAAO,EAAE;AAE5D;;;;AAKA,SAAS,gBAAgB,QASgC;CACvD,MAAM,EACJ,MACA,WACA,eACA,QACA,cACA,aACA,kBACA,WACE;CACJ,MAAM,eAAe,OAAO,KAAK,aAAa;CAE9C,MAAM,eAAe,6BAA6B;EAChD;EACA,cAAc,QAAQ;EACtB;CACF,CAAC;CAED,MAAM,cAAc,gBAAgB,MAAM,WAAW;EACnD;EACA,aAAa;EACb,6BAAY,IAAI,KAAK,EAAA,CAAE,YAAY;EACnC,QAAQ;EACR,OAAO,QAAQ,SAAS,CAAC;EACzB,eAAe,QAAQ;EACvB,WAAW,QAAQ;EACnB,mBAAmB,QAAQ;CAC7B,CAAC;CAED,OAAO,KACL,WAAW,aAAa,OAAO,iBAAiB,UAAU,IAAI,aAAa,KAAK,IAAI,KAAK,UAC3F;CAEA,OAAO;EAAE;EAAa;CAAa;AACrC;AAEA,SAAS,oBAAoB,QAW4B;CACvD,MAAM,EACJ,MACA,WACA,cACA,QACA,cACA,aACA,eACA,WACA,mBACA,WACE;CACJ,MAAM,eAAe,OAAO,KAAK,YAAY;CAC7C,MAAM,cAAc,gBAAgB,MAAM,WAAW;EACnD;EACA;EACA,6BAAY,IAAI,KAAK,EAAA,CAAE,YAAY;EACnC,QAAQ,QAAQ,UAAU,CAAC;EAC3B,OAAO;EACP;EACA;EACA;CACF,CAAC;CACD,OAAO,KACL,WAAW,aAAa,OAAO,gBAAgB,UAAU,IAAI,aAAa,KAAK,IAAI,KAAK,UAC1F;CACA,OAAO;EAAE;EAAa;CAAa;AACrC;AAEA,SAAS,2BAA2B,MAAsB;CACxD,MAAM,aAAa,KAAK,QAAQ,GAAG;CACnC,MAAM,iBAAiB,KAAK,QAAQ,IAAI;CACxC,IAAI,eAAe,IAAI,OAAO;CAC9B,IAAI,mBAAmB,IAAI,OAAO;CAClC,OAAO,KAAK,IAAI,YAAY,cAAc;AAC5C;;;;;;;;;;;AAYA,SAAS,sBAAsB,QAKnB;CACV,MAAM,EAAE,aAAa,YAAY,kBAAkB,yBAAyB;CAC5E,MAAM,CAAC,mBAAmB;CAC1B,OACE,CAAC,cACD,YAAY,WAAW,KACvB,oBAAoB,KAAA,KACpB,oBACA,CAAC;AAEL;AAEA,SAAS,4BAA4B,QAIF;CACjC,MAAM,EAAE,aAAa,aAAa,eAAe;CACjD,MAAM,0BAAU,IAAI,IAA+B;CACnD,MAAM,iBAAoC,CAAC;CAE3C,KAAK,MAAM,QAAQ,aAAa;EAC9B,MAAM,iBAAiB,2BAA2B,KAAK,YAAY;EACnE,IAAI,mBAAmB,IAAI;GACzB,eAAe,KAAK,IAAI;GACxB;EACF;EAEA,MAAM,YAAY,KAAK,aAAa,UAAU,GAAG,cAAc;EAC/D,IAAI,UAAU,WAAW,GACvB;EAGF,MAAM,YAAY,KAAK,aAAa,UAAU,iBAAiB,CAAC;EAChE,MAAM,eAAe,QAAQ,IAAI,SAAS,KAAK,CAAC;EAChD,aAAa,KAAK;GAAE,cAAc;GAAW,SAAS,KAAK;EAAQ,CAAC;EACpE,QAAQ,IAAI,WAAW,YAAY;CACrC;CAEA,MAAM,CAAC,mBAAmB;CAC1B,MAAM,mBAAmB,eAAe,MAAM,SAAS,KAAK,iBAAiBC,iBAAe;CAC5F,IACE,oBAAoB,KAAA,KACpB,sBAAsB;EACpB;EACA;EACA;EACA,sBAAsB,QAAQ,IAAI,eAAe;CACnD,CAAC,GAED,QAAQ,IAAI,iBAAiB,cAAc;CAG7C,OAAO;AACT;;;;;;;AAYA,eAAe,sBAAsB,QAO+C;CAClF,MAAM,EAAE,QAAQ,QAAQ,eAAe,WAAW,QAAQ,WAAW;CACrE,IAAI,UAAU,CAAC,eAAe;EAE5B,OAAO,MAAM,wBAAwB,UAAU,IAAI,OAAO,aAAa;EACvE,OAAO;GACL,KAAK,OAAO;GACZ,aAAa,OAAO;GACpB,cAAc,OAAO;EACvB;CACF;CAEA,MAAM,eAAe,OAAO,OAAQ,MAAM,OAAO,iBAAiB,OAAO,OAAO,OAAO,IAAI;CAC3F,MAAM,cAAc,MAAM,OAAO,gBAAgB,OAAO,OAAO,OAAO,MAAM,YAAY;CACxF,OAAO,MAAM,YAAY,UAAU,QAAQ,aAAa,YAAY,aAAa;CACjF,OAAO;EAAE,KAAK;EAAa;EAAa;CAAa;AACvD;AAEA,SAAS,wBAAwB,MAAsB;CACrD,OAAO,KAAK,QAAQ,UAAU,EAAE;AAClC;AAEA,SAAS,uBAAuB,OAA2B;CACzD,OAAO,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,uBAAuB,CAAC,CAAC,CAAC,CAAC,SAAS;AACnE;AAEA,SAAS,mBAAmB,WAAuC;CACjE,OAAO,MAAM,WAAW,aAAa,QAAA,CAAS,QAAQ,OAAO,GAAG,CAAC,CAAC,CAAC,QAAQ,QAAQ,EAAE;AACvF;AAEA,SAAS,wBAAwB,QAGrB;CACV,MAAM,QAAQ,iBAAiB,OAAO,WAAW,CAAC,CAAC;CACnD,IAAI,UAAU,KAAA,KAAa,OAAO,OAAO,kBAAkB,KAAA,GACzD,OAAO;CAET,MAAM,YAAY,uBAAuB,KAAK;CAC9C,OACE,UAAU,WAAW,OAAO,OAAO,cAAc,UACjD,UAAU,OAAO,UAAU,UAAU,aAAa,OAAO,OAAO,gBAAgB,MAAM,KACtF,mBAAmB,OAAO,YAAY,SAAS,MAAM,OAAO,OAAO;AAEvE;AAEA,SAAS,yBAAyB,QAAuD;CACvF,IAAI,OAAO,UAAU,WAAW,GAC9B,MAAM,IAAI,MAAM,8BAA8B,OAAO,OAAO,EAAE;AAElE;AAEA,eAAe,sBAAsB,QAWoD;CACvF,KAAK,OAAO,YAAY,aAAa,cAAc,OACjD,OAAO,iBAAiB,MAAM;CAEhC,OAAO,oBAAoB,MAAM;AACnC;AAEA,eAAe,oBAAoB,QAUsD;CACvF,MAAM,EACJ,aACA,QACA,aACA,MACA,gBACA,yBACA,eACA,cACA,WACE;CACJ,MAAM,mBAAmB,YAAY,YAAY,MAAM;CACvD,MAAM,SAAuB;EAC3B,GAAG;EACH,KAAK,YAAY,OAAO,iBAAiB;CAC3C;CACA,IAAI,OAAO,aAAa,UACtB,MAAM,IAAI,MAAM,0CAA0C,YAAY,OAAO,GAAG;CAElF,MAAM,YAAY,YAAY;CAC9B,MAAM,SAAS,gBAAgB,MAAM,SAAS;CAC9C,MAAM,kBAAkB,SAAS,mBAAmB,MAAM,IAAI,CAAC;CAC/D,MAAM,EAAE,KAAK,aAAa,iBAAiB,MAAM,sBAAsB;EACrE;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CACD,MAAM,aAAa,KAAK,aAAa,wCAAwC;CAC7E,IACE,UACA,gBAAgB,OAAO,eACvB,CAAC,iBACD,CAAC,gBACA,MAAM,oBAAoB;EACzB;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,GACD;EACA,OAAO,MAAM,qBAAqB,UAAU,2BAA2B;EACvE,OAAO;GAAE,WAAW;GAAG,kBAAkB;GAAiB,aAAa;EAAK;CAC9E;CAEA,MAAM,cAAc,YAAY,SAAS,CAAC,EAAA,CAAG,IAAI,uBAAuB;CACxE,MAAM,aAAa,WAAW,WAAW,KAAK,WAAW,OAAO;CAChE,MAAM,YAAY,YAAY,aAAa;CAC3C,IAAI;CACJ,IAAI;EACF,UAAU,MAAM,OAAO,cAAc,OAAO,OAAO,OAAO,MAAM,WAAW,GAAG;CAChF,SAAS,OAAO;EACd,IAAI,iBAAiB,qBAAqB,MAAM,eAAe,KAC7D,MAAM,IAAI,MAAM,MAAM,UAAU,uBAAuB,UAAU,IAAI,EAAE,OAAO,MAAM,CAAC;EAEvF,MAAM;CACR;CACA,MAAM,cAAc,QACjB,QAAQ,UAAU,MAAM,SAAS,UAAU,MAAM,KAAK,YAAY,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,CACpF,KAAK,WAAW;EAAE;EAAO,MAAM,wBAAwB,MAAM,IAAI;CAAE,EAAE,CAAC,CACtE,QAAQ,EAAE,YAAY,cAAc,WAAW,SAAS,IAAI,MAAM,gBAAgB,IAAI,CAAC;CAC1F,MAAM,kBAAkB,YAAY,KAAK,EAAE,WAAW,IAAI;CAC1D,yBAAyB;EAAE,WAAW;EAAiB,QAAQ;CAAU,CAAC;CAC1E,MAAM,gBAAkC,CAAC;CACzC,KAAK,MAAM,EAAE,OAAO,UAAU,aAAa;EACzC,IAAI,MAAM,OAAA,UAAsB;GAC9B,OAAO,KACL,kBAAkB,MAAM,KAAK,MAAM,MAAM,OAAO,OAAO,KAAA,CAAM,QAAQ,CAAC,EAAE,aAAa,gBAAgB,OAAO,KAAK,WACnH;GACA;EACF;EACA,IACE,eAAe;GACb,UAAU;GACV;GACA;GACA;GACA;EACF,CAAC,GAED;EAEF,MAAM,UAAU,MAAM,OAAO,eAAe,OAAO,OAAO,OAAO,MAAM,MAAM,MAAM,GAAG;EACtF,cAAc,KAAK;GAAE;GAAM;EAAQ,CAAC;CACtC;CAaA,MAAM,SAAS,oBAAoB;EACjC;EACA;EACA,cAAA,MAfyB,oBAAoB;GAC7C,OAAO;GACP;GACA;GACA;GACA,aAAa;GACb;GACA;GACA;GACA,wBAAwB,CAAC,iBAAiB,CAAC;GAC3C;EACF,CAAC;EAKC;EACA;EACA,aAAa;EACb,eAAe,uBAAuB,YAAY,SAAS,CAAC,CAAC;EAC7D,WAAW,mBAAmB,YAAY,SAAS;EACnD,mBAAmB;EACnB;CACF,CAAC;CACD,OAAO;EACL,WAAW,OAAO,aAAa;EAC/B,kBAAkB,OAAO;EACzB,aAAa,OAAO;CACtB;AACF;AAEA,eAAe,iBAAiB,QAUyD;CACvF,MAAM,EACJ,aACA,aACA,MACA,gBACA,yBACA,eACA,cACA,QACA,WACE;CACJ,MAAM,YAAY,YAAY;CAC9B,MAAM,SAAS,gBAAgB,MAAM,SAAS;CAC9C,MAAM,kBAAkB,SAAS,mBAAmB,MAAM,IAAI,CAAC;CAC/D,IAAI;CACJ,IAAI;CACJ,IAAI,UAAU,CAAC,eAAe;EAC5B,cAAc,OAAO;EACrB,eAAe,OAAO;EACtB,IAAI,cAAc,YAAY,YAAY;CAC5C,OAAO,IAAI,YAAY,KAAK;EAC1B,eAAe,YAAY;EAC3B,cAAc,MAAM,gBAAgB,WAAW,YAAY;CAC7D,OAAO;EACL,MAAM,aAAa,MAAM,kBAAkB,SAAS;EACpD,eAAe,WAAW;EAC1B,cAAc,WAAW;CAC3B;CACA,MAAM,aAAa,KAAK,aAAa,wCAAwC;CAC7E,IACE,UACA,gBAAgB,OAAO,eACvB,CAAC,iBACD,CAAC,gBACA,MAAM,oBAAoB;EACzB;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,GAED,OAAO;EAAE,WAAW;EAAG,kBAAkB;EAAiB,aAAa;CAAK;CAE9E,IAAI,CAAC,cAAc;EACjB,IAAI,QACF,MAAM,IAAI,MACR,8CAA8C,UAAU,0EAC1D;EAEF,MAAM,aAAa,MAAM,kBAAkB,SAAS;EACpD,eAAe,WAAW;EAC1B,cAAc,WAAW;CAC3B;CACA,MAAM,QAAQ,MAAM,gBAAgB;EAClC,KAAK;EACL,KAAK;EACL;EACA,YAAY,YAAY,aAAa;EACrC;CACF,CAAC;CACD,MAAM,cAAc,YAAY,SAAS,CAAC,EAAA,CAAG,IAAI,uBAAuB;CACxE,MAAM,aAAa,WAAW,WAAW,KAAK,WAAW,OAAO;CAChE,MAAM,cAAc,MACjB,QACE,SACC,2BAA2B,KAAK,YAAY,MAAM,MAClD,KAAK,aAAa,YAAY,CAAC,CAAC,SAAS,KAAK,CAClD,CAAC,CACA,KAAK,UAAU;EAAE,MAAM,wBAAwB,KAAK,YAAY;EAAG,SAAS,KAAK;CAAQ,EAAE,CAAC,CAC5F,QAAQ,UAAU,cAAc,WAAW,SAAS,KAAK,IAAI,MAAM,gBAAgB,KAAK,IAAI,CAAC;CAChG,MAAM,kBAAkB,YAAY,KAAK,SAAS,KAAK,IAAI;CAC3D,yBAAyB;EAAE,WAAW;EAAiB,QAAQ;CAAU,CAAC;CAa1E,MAAM,SAAS,oBAAoB;EACjC;EACA;EACA,cAAA,MAfyB,oBAAoB;GAC7C,OAAO;GACP;GACA;GACA;GACA;GACA;GACA;GACA;GACA,wBAAwB,CAAC,iBAAiB,CAAC;GAC3C;EACF,CAAC;EAKC;EACA;EACA;EACA,eAAe,uBAAuB,YAAY,SAAS,CAAC,CAAC;EAC7D,WAAW,mBAAmB,YAAY,SAAS;EACnD,mBAAmB;EACnB;CACF,CAAC;CACD,OAAO;EACL,WAAW,OAAO,aAAa;EAC/B,kBAAkB,OAAO;EACzB,aAAa,OAAO;CACtB;AACF;;;;;;AAOA,eAAe,4BAA4B,QAgBmB;CAC5D,MAAM,EACJ,SACA,QACA,KACA,aACA,aACA,YACA,YACA,QACA,WACA,iBACA,0BACA,QACA,WACA,eACA,WACE;CAEJ,MAAM,YAAY,QAAQ,QAAQ,UAAU,MAAM,SAAS,MAAM;CACjE,MAAM,iBAAoC,CAAC;CAE3C,KAAK,MAAM,QAAQ,WAAW;EAC5B,IAAI,KAAK,OAAA,UAAsB;GAC7B,OAAO,KACL,kBAAkB,KAAK,KAAK,MAAM,KAAK,OAAO,OAAO,KAAA,CAAM,QAAQ,CAAC,EAAE,aAAa,gBAAgB,OAAO,KAAK,WACjH;GACA;EACF;EACA,MAAM,UAAU,MAAM,cAAc,iBAClC,OAAO,eAAe,OAAO,OAAO,OAAO,MAAM,KAAK,MAAM,GAAG,CACjE;EACA,eAAe,KAAK;GAAE,cAAc,KAAK;GAAM;EAAQ,CAAC;CAC1D;CAEA,MAAM,mBAAmB,4BAA4B;EACnD,aAAa;EACb;EACA;CACF,CAAC;CACD,MAAM,CAAC,qBAAqB,iBAAiB,KAAK;CAClD,IAAI,sBAAsB,KAAA,GACxB,OAAO;EAAE,SAAS;EAAO,kBAAkB,CAAC;CAAE;CAGhD,IACE,CAAC,gBAAgB;EACf,WAAW;EACX;EACA;EACA;EACA;CACF,CAAC,GACD;EACA,cAAc,qBAAqB,MAAM,8BAA8B;GACrE,WAAW;GACX,OAAO,iBAAiB,IAAI,iBAAiB,KAAK,CAAC;GACnD;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,OAAO,MAAM,kBAAkB,kBAAkB,SAAS,WAAW;CACvE;CAEA,OAAO;EAAE,SAAS;EAAM,kBAAkB,CAAC,iBAAiB;CAAE;AAChE;;;;;AAMA,eAAe,oBAAoB,QAWV;CACvB,MAAM,EACJ,UACA,QACA,KACA,aACA,YACA,QACA,WACA,QACA,WACA,WACE;CAaJ,MAAM,SAAQ,MAVS,uBAAuB;EAC5C;EACA,OAAO,OAAO;EACd,MAAM,OAAO;EACb,MAAM,SAAS;EACf;EACA;CACF,CAAC,EAAA,CAGsB,QAAQ,SAAS;EACtC,IAAI,KAAK,OAAA,UAAsB;GAC7B,OAAO,KACL,kBAAkB,KAAK,KAAK,MAAM,KAAK,OAAO,OAAO,KAAA,CAAM,QAAQ,CAAC,EAAE,aAAa,gBAAgB,OAAO,KAAK,WACjH;GACA,OAAO;EACT;EACA,OAAO;CACT,CAAC;CAGD,MAAM,aAA+D,CAAC;CACtE,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,kBAAkB,KAAK,KAAK,UAAU,SAAS,KAAK,SAAS,CAAC;EACpE,MAAM,UAAU,MAAM,cAAc,iBAClC,OAAO,eAAe,OAAO,OAAO,OAAO,MAAM,KAAK,MAAM,GAAG,CACjE;EACA,WAAW,KAAK;GAAE,cAAc;GAAiB;EAAQ,CAAC;CAC5D;CAEA,OAAO,8BAA8B;EACnC,WAAW,SAAS;EACpB,OAAO;EACP;EACA;EACA;EACA;EACA;CACF,CAAC;AACH;;;;;;;AAQA,eAAe,wBAAwB,QAuBrC;CACA,MAAM,EACJ,QACA,KACA,aACA,aACA,YACA,YACA,QACA,WACA,iBACA,0BACA,QACA,WACA,eACA,WACE;CAEJ,MAAM,iBAAiB,OAAO,QAAQ;CACtC,IAAI;EACF,MAAM,UAAU,MAAM,OAAO,cAAc,OAAO,OAAO,OAAO,MAAM,gBAAgB,GAAG;EACzF,MAAM,kBAAkB,QACrB,QAAQ,MAAM,EAAE,SAAS,KAAK,CAAC,CAC/B,KAAK,OAAO;GAAE,MAAM,EAAE;GAAM,MAAM,EAAE;EAAK,EAAE;EAE9C,MAAM,CAAC,mBAAmB;EAC1B,MAAM,uBACJ,oBAAoB,KAAA,KAAa,gBAAgB,MAAM,MAAM,EAAE,SAAS,eAAe;EAOzF,IACE,sBAAsB;GAAE;GAAa;GAAY,kBAJ1B,QAAQ,MAC9B,UAAU,MAAM,SAAS,UAAU,MAAM,SAAA,UAGsB;GAAG;EAAqB,CAAC,GACzF;GACA,IAAI,QACF,MAAM,2BAA2B;IAC/B;IACA,kBAAkB,OAAO,KAAK,OAAO,MAAM;IAC3C;GACF,CAAC;GAEH,MAAM,WAAW,MAAM,4BAA4B;IACjD;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,IAAI,SAAS,SACX,OAAO;IACL,QAAQ;IACR;IACA,iBAAiB;IACjB,kBAAkB,SAAS;GAC7B;EAEJ;EAEA,OAAO;GAAE,QAAQ;GAAM;GAAiB,iBAAiB;GAAO,kBAAkB,CAAC;EAAE;CACvF,SAAS,OAAO;EACd,IAAI,iBAAiB,qBAAqB,MAAM,eAAe,KAC7D,OAAO,EAAE,QAAQ,WAAW;EAE9B,MAAM;CACR;AACF;;;;AAKA,eAAe,YAAY,QAaxB;CACD,MAAM,EACJ,aACA,QACA,aACA,iBACA,0BACA,eACA,WACE;CACJ,MAAM,EAAE,SAAS;CAEjB,MAAM,mBAAmB,YAAY,YAAY,MAAM;CACvD,MAAM,SAAuB;EAC3B,GAAG;EACH,KAAK,YAAY,OAAO,iBAAiB;EACzC,MAAM,YAAY,QAAQ,iBAAiB;CAC7C;CAEA,IAAI,OAAO,aAAa,UACtB,MAAM,IAAI,MAAM,0CAA0C,YAAY,OAAO,GAAG;CAGlF,MAAM,YAAY,YAAY;CAC9B,MAAM,SAAS,gBAAgB,MAAM,SAAS;CAC9C,MAAM,mBAAmB,SAAS,oBAAoB,MAAM,IAAI,CAAC;CAGjE,MAAM,EAAE,KAAK,aAAa,iBAAiB,MAAM,sBAAsB;EACrE;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,MAAM,aAAa,KAAK,aAAa,yCAAyC;CAG9E,IAAI,UAAU,gBAAgB,OAAO,eAAe,CAAC,eAE/C;MAAA,MADmB,uBAAuB,YAAY,gBAAgB,GAC5D;GACZ,OAAO,MAAM,qBAAqB,UAAU,qBAAqB;GACjE,OAAO;IACL,YAAY;IACZ,mBAAmB;IACnB,aAAa;GACf;EACF;;CAIF,MAAM,cAAc,YAAY,UAAU,CAAC,GAAG;CAC9C,MAAM,aAAa,YAAY,WAAW,KAAK,YAAY,OAAO;CAClE,MAAM,YAAY,IAAI,UAAA,EAAiC;CACvD,MAAM,gBAA6C,CAAC;CAKpD,MAAM,YAAY,MAAM,wBAAwB;EAC9C;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CACD,IAAI,UAAU,WAAW,YACvB,MAAM,IAAI,MAAM,iCAAiC,UAAU,EAAE;CAE/D,MAAM,EAAE,iBAAiB,iBAAiB,kBAAkB,uBAAuB;CAGnF,MAAM,eAAe,aACjB,kBACA,gBAAgB,QAAQ,MAAM,YAAY,SAAS,EAAE,IAAI,CAAC;CAC9D,MAAM,mBAAmB,kBAAkB,qBAAqB,aAAa,KAAK,MAAM,EAAE,IAAI;CAC9F,0BAA0B;EAAE,YAAY;EAAkB,QAAQ;CAAU,CAAC;CAE7E,IAAI,UAAU,CAAC,iBACb,MAAM,2BAA2B;EAAE;EAAY;EAAkB;CAAO,CAAC;CAG3E,KAAK,MAAM,YAAY,cAAc;EACnC,IACE,gBAAgB;GACd,WAAW,SAAS;GACpB;GACA;GACA;GACA;EACF,CAAC,GAED;EAGF,cAAc,SAAS,QAAQ,MAAM,oBAAoB;GACvD;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,OAAO,MAAM,kBAAkB,SAAS,KAAK,SAAS,WAAW;CACnE;CAEA,MAAM,SAAS,gBAAgB;EAC7B;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,OAAO;EACL,YAAY,OAAO,aAAa;EAChC,mBAAmB,OAAO;EAC1B,aAAa,OAAO;CACtB;AACF;;;;AAKA,eAAe,kBAAkB,QAS0D;CACzF,MAAM,EACJ,aACA,aACA,iBACA,0BACA,eACA,QACA,WACE;CACJ,MAAM,EAAE,SAAS;CACjB,MAAM,MAAM,YAAY;CACxB,MAAM,SAAS,gBAAgB,MAAM,GAAG;CACxC,MAAM,mBAAmB,SAAS,oBAAoB,MAAM,IAAI,CAAC;CAEjE,IAAI;CACJ,IAAI;CACJ,IAAI,UAAU,CAAC,eAAe;EAC5B,cAAc,OAAO;EACrB,eAAe,OAAO;EAEtB,IAAI,cACF,YAAY,YAAY;CAE5B,OAAO,IAAI,YAAY,KAAK;EAC1B,eAAe,YAAY;EAC3B,cAAc,MAAM,gBAAgB,KAAK,YAAY;CACvD,OAAO;EACL,MAAM,MAAM,MAAM,kBAAkB,GAAG;EACvC,eAAe,IAAI;EACnB,cAAc,IAAI;CACpB;CAEA,MAAM,aAAa,KAAK,aAAa,yCAAyC;CAC9E,IAAI,UAAU,gBAAgB,OAAO,eAAe,CAAC,eAC/C;MAAA,MAAM,uBAAuB,YAAY,gBAAgB,GAC3D,OAAO;GAAE,YAAY;GAAG,mBAAmB;GAAkB,aAAa;EAAK;CAAA;CAKnF,IAAI,CAAC,cAAc;EACjB,IAAI,QACF,MAAM,IAAI,MACR,8CAA8C,IAAI,0EACpD;EAEF,MAAM,MAAM,MAAM,kBAAkB,GAAG;EACvC,eAAe,IAAI;EACnB,cAAc,IAAI;CACpB;CAEA,MAAM,cAAc,YAAY,UAAU,CAAC,GAAG;CAC9C,MAAM,aAAa,YAAY,WAAW,KAAK,YAAY,OAAO;CAQlE,MAAM,eAAe,4BAA4B;EAAE,aAAA,MAPzB,gBAAgB;GACxC;GACA,KAAK;GACL,aAAa;GACb,YAAY,YAAY,QAAQ;EAClC,CAAC;EAE+D;EAAa;CAAW,CAAC;CAEzF,MAAM,WAAW,CAAC,GAAG,aAAa,KAAK,CAAC;CACxC,MAAM,gBAAgB,aAAa,WAAW,SAAS,QAAQ,MAAM,YAAY,SAAS,CAAC,CAAC;CAC5F,0BAA0B;EAAE,YAAY;EAAe,QAAQ;CAAI,CAAC;CAEpE,IAAI,QACF,MAAM,2BAA2B;EAAE;EAAY;EAAkB;CAAO,CAAC;CAG3E,MAAM,gBAA6C,CAAC;CACpD,KAAK,MAAM,aAAa,eAAe;EACrC,IACE,gBAAgB;GACd;GACA,WAAW;GACX;GACA;GACA;EACF,CAAC,GAED;EAGF,cAAc,aAAa,MAAM,8BAA8B;GAC7D;GACA,OAAO,aAAa,IAAI,SAAS,KAAK,CAAC;GACvC;GACA;GACA;GACA,WAAW;GACX;EACF,CAAC;CACH;CAEA,MAAM,SAAS,gBAAgB;EAC7B;EACA,WAAW;EACX;EACA;EACA;EACA;EACA,kBAAkB;EAClB;CACF,CAAC;CACD,OAAO;EACL,YAAY,OAAO,aAAa;EAChC,mBAAmB,OAAO;EAC1B,aAAa,OAAO;CACtB;AACF;;;;;;;;;AAcA,SAAS,oBAAoB,QAMsD;CACjF,MAAM,EAAE,UAAU,YAAY,aAAa,YAAY,gBAAgB;CAEvE,MAAM,iBAAiB,MAAM,UAAU,WAAW,QAAQ,OAAO,GAAG,CAAC,CAAC,CAAC,QAAQ,QAAQ,EAAE;CAEzF,IADmB,mBAAmB,MAAM,mBAAmB,KAE7D,OAAO;EAAE,aAAa;EAAU;EAAa;CAAW;CAG1D,MAAM,SAAS,GAAG,eAAe;CACjC,MAAM,iBAAiB,SACpB,QAAQ,SAAS,KAAK,aAAa,WAAW,MAAM,CAAC,CAAC,CACtD,KAAK,UAAU;EACd,cAAc,KAAK,aAAa,UAAU,OAAO,MAAM;EACvD,SAAS,KAAK;CAChB,EAAE;CACJ,IAAI,eAAe,SAAS,GAC1B,OAAO;EAAE,aAAa;EAAgB;EAAa;CAAW;CAMhE,MAAM,mBAAmB,SAAS,MAAM,SAAS,KAAK,iBAAiBA,iBAAe;CACtF,MAAM,iBAAiB,aAAa,CAAC,mBAAmB,WAAW,CAAC,IAAI;CACxE,MAAM,CAAC,mBAAmB;CAC1B,IACE,eAAe,WAAW,KAC1B,oBAAoB,KAAA,KACpB,sBAAsB;EACpB,aAAa;EACb,YAAY;EACZ;EACA,sBAAsB;CACxB,CAAC,GAED,OAAO;EAAE,aAAa;EAAU,aAAa;EAAgB,YAAY;CAAM;CAGjF,OAAO;EAAE,aAAa;EAAgB;EAAa;CAAW;AAChE;;AAGA,SAAS,mBAAmB,aAA6B;CACvD,MAAM,aAAa,YAAY,QAAQ,GAAG;CAC1C,OAAO,eAAe,KAAK,cAAc,YAAY,UAAU,aAAa,CAAC;AAC/E;;;;;;AAOA,SAAS,uBAAuB,QAIgD;CAC9E,MAAM,EAAE,aAAa,QAAQ,kBAAkB;CAC/C,IAAI,UAAU,CAAC,eACb,OAAO;EAAE,eAAe,OAAO;EAAiB,kBAAkB,OAAO;CAAiB;CAE5F,OAAO;EAAE,eAAe,KAAA;EAAW,kBAAkB,YAAY,OAAO;CAAS;AACnF;;;;;;AAOA,eAAe,2BAA2B,QAYvC;CACD,MAAM,EAAE,aAAa,aAAa,OAAO,eAAe,kBAAkB,QAAQ,WAChF;CAEF,MAAM,YAAY,MAAM,eAAe;EAAE;EAAa;EAAa;CAAM,CAAC;CAC1E,MAAM,kBACJ,iBACA,wBAAwB;EACtB;EACA;EACA,WAAW,oBAAoB;CACjC,CAAC;CACH,OAAO,MAAM,YAAY,YAAY,GAAG,oBAAoB,SAAS,MAAM,iBAAiB;CAE5F,MAAM,OAAO,wBAAwB;EAAE;EAAW;EAAa,SAAS;CAAgB,CAAC;CACzF,MAAM,UAAU,MAAM,aAAa;EAAE,YAAY,KAAK;EAAS;EAAa;CAAM,CAAC;CACnF,MAAM,UAAU,GAAG,YAAY,GAAG;CAClC,uBAAuB;EACrB;EACA,WAAW,KAAK;EAChB,QAAQ,KAAK;EACb;EACA;CACF,CAAC;CAGD,IAAI,QAAQ,aAAa,OAAO,oBAAoB,iBAClD,uBAAuB;EAAE;EAAS,WAAW,OAAO;EAAW;EAAS;CAAO,CAAC;CAGlF,OAAO;EAAE;EAAiB;EAAM;CAAQ;AAC1C;;;;;AAMA,SAAS,sBAAsB,QAAgE;CAC7F,MAAM,EAAE,SAAS,WAAW;CAC5B,MAAM,YAAY,sBAAsB;EACtC;EACA,iBAAiB,YAAY,OAAO,KAAK,OAAO;CAClD,CAAC;CACD,MAAM,WAA8B,CAAC;CACrC,KAAK,MAAM,SAAS,WAAW;EAC7B,IAAI,MAAM,QAAQ,SAAA,UAAwB;GACxC,OAAO,KACL,kBAAkB,MAAM,aAAa,MAAM,MAAM,QAAQ,SAAS,OAAO,KAAA,CAAM,QAAQ,CAAC,EAAE,aAAa,gBAAgB,OAAO,KAAK,WACrI;GACA;EACF;EACA,SAAS,KAAK;GAAE,cAAc,MAAM;GAAc,SAAS,MAAM,QAAQ,SAAS,MAAM;EAAE,CAAC;CAC7F;CACA,OAAO;AACT;;AAGA,SAAS,kBAAkB,QAQP;CAClB,MAAM,EACJ,aACA,kBACA,iBACA,MACA,cACA,aACA,sBACE;CACJ,MAAM,YACJ,KAAK,cAAc,KAAK,WAAW,KAAA,IAAY,YAAY,KAAK,MAAM,IAAI,KAAA;CAC5E,OAAO;EACL,GAAI,YAAY,aAAa,KAAA,KAAa,EAAE,UAAU,YAAY,SAAS;EAC3E,GAAI,qBAAqB,KAAA,KAAa,EAAE,iBAAiB;EACzD;EACA,GAAI,cAAc,KAAA,KAAa,EAAE,UAAU;EAC3C,6BAAY,IAAI,KAAK,EAAA,CAAE,YAAY;EACnC,QAAQ;EACR,GAAI,YAAY,UAAU,KAAA,KAAa;GACrC,OAAO;GACP,eAAe,uBAAuB,YAAY,KAAK;GACvD,WAAW,mBAAmB,YAAY,SAAS;GACnD;EACF;CACF;AACF;AAEA,eAAe,eAAe,QAe3B;CACD,IAAI,OAAO,YAAY,WAAW,KAAA,GAChC,OAAO;EAAE,eAAe,CAAC;EAAG,kBAAkB,CAAC;CAAE;CAEnD,MAAM,EACJ,UACA,aACA,aACA,QACA,yBACA,kBACA,kBACA,iBACA,0BACA,iBACA,WACE;CACJ,MAAM,cAAc,YAAY,UAAU,CAAC;CAC3C,MAAM,mBAAmB,YAAY,WAAW,KAAK,YAAY,OAAO;CACxE,MAAM,gBAAgB,oBAAoB;EACxC;EACA,YAAY,YAAY,QAAQ;EAChC;EACA,YAAY;EACZ;CACF,CAAC;CACD,MAAM,eAAe,4BAA4B,aAAa;CAC9D,MAAM,WAAW,CAAC,GAAG,aAAa,KAAK,CAAC;CACxC,MAAM,mBAAmB,cAAc,aACnC,WACA,SAAS,QAAQ,SAAS,cAAc,YAAY,SAAS,IAAI,CAAC;CACtE,0BAA0B;EAAE,YAAY;EAAkB,QAAQ;CAAY,CAAC;CAC/E,IAAI,QACF,MAAM,2BAA2B;EAAE,YAAY;EAAkB;EAAkB;CAAO,CAAC;CAE7F,MAAM,gBAA6C,CAAC;CACpD,KAAK,MAAM,aAAa,kBAAkB;EACxC,IACE,gBAAgB;GACd;GACA,WAAW;GACX;GACA;GACA;EACF,CAAC,GAED;EAEF,cAAc,aAAa,MAAM,8BAA8B;GAC7D;GACA,OAAO,aAAa,IAAI,SAAS,KAAK,CAAC;GACvC,YAAY;GACZ,QAAQ;GACR,aAAa;GACb,WAAW;GACX;EACF,CAAC;EACD,OAAO,MAAM,kBAAkB,UAAU,SAAS,aAAa;CACjE;CACA,OAAO;EAAE;EAAe;CAAiB;AAC3C;AAEA,eAAe,cAAc,QAY0D;CACrF,IAAI,OAAO,YAAY,UAAU,KAAA,GAC/B,OAAO;EAAE,cAAc,CAAC;EAAG,mBAAmB,CAAC;CAAE;CAEnD,MAAM,EACJ,UACA,aACA,aACA,yBACA,iBACA,iBACA,gBACA,yBACA,iBACA,eACA,WACE;CACJ,MAAM,sBAAsB,mBAAmB,YAAY,SAAS;CACpE,MAAM,aAAa,wBAAwB,MAAM,KAAK,GAAG,oBAAoB;CAC7E,MAAM,cAAc,YAAY,SAAS,CAAC,EAAA,CAAG,IAAI,uBAAuB;CACxE,MAAM,aAAa,WAAW,WAAW,KAAK,WAAW,OAAO;CAChE,MAAM,cAAc,SACjB,QAAQ,SAAS,KAAK,aAAa,WAAW,UAAU,CAAC,CAAC,CAC1D,KAAK,UAAU;EACd,cAAc,KAAK,aAAa,UAAU,WAAW,MAAM;EAC3D,SAAS,KAAK;CAChB,EAAE,CAAC,CACF,QACE,SACC,2BAA2B,KAAK,YAAY,MAAM,MAClD,KAAK,aAAa,YAAY,CAAC,CAAC,SAAS,KAAK,CAClD,CAAC,CACA,KAAK,UAAU;EAAE,MAAM,wBAAwB,KAAK,YAAY;EAAG,SAAS,KAAK;CAAQ,EAAE,CAAC,CAC5F,QAAQ,UAAU,cAAc,WAAW,SAAS,KAAK,IAAI,MAAM,gBAAgB,KAAK,IAAI,CAAC;CAChG,MAAM,oBAAoB,YAAY,KAAK,SAAS,KAAK,IAAI;CAC7D,yBAAyB;EAAE,WAAW;EAAmB,QAAQ;CAAY,CAAC;CAa9E,OAAO;EAAE,cAAA,MAZkB,oBAAoB;GAC7C,OAAO;GACP,YAAY;GACZ,QAAQ;GACR;GACA,aAAa;GACb,WAAW;GACX;GACA;GACA,wBAAwB,CAAC;GACzB;EACF,CAAC;EACsB;CAAkB;AAC3C;AAEA,eAAe,2BAA2B,QAUrB;CACnB,MAAM,EACJ,QACA,aACA,SACA,kBACA,iBACA,kBACA,iBACA,gBACA,4BACE;CACJ,IAAI,WAAW,KAAA,GACb,OAAO;CAMT,IAAI,EAHF,QAAQ,WAAW,KAAA,KAClB,iBAAiB,SAAS,KACxB,MAAM,uBAAuB,kBAAkB,gBAAgB,IAElE,OAAO;CAET,IAAI,QAAQ,UAAU,KAAA,GACpB,OAAO,OAAO,UAAU,KAAA;CAE1B,OAAO,oBAAoB;EACzB;EACA,aAAa;GAAE,GAAG;GAAa,OAAO,QAAQ;EAAM;EACpD;EACA;EACA;EACA,YAAY;CACd,CAAC;AACH;;;;;;;AAQA,eAAe,kBAAkB,QAgB9B;CACD,MAAM,EACJ,aACA,aACA,SACA,iBACA,gBACA,0BACA,yBACA,eACA,WACE;CAEJ,MAAM,cAAc,YAAY;CAChC,uBAAuB,WAAW;CAClC,MAAM,cAAc,YAAY,YAAA;CAChC,uBAAuB,aAAa,EAAE,OAAO,CAAC;CAC9C,MAAM,QAAQ,gBAAgB,EAAE,UAAU,YAAY,SAAS,CAAC;CAEhE,MAAM,YAAY;CAClB,MAAM,SAAS,mBAAmB,SAAS,SAAS;CACpD,MAAM,mBAAmB,SAAS,uBAAuB,MAAM,IAAI,CAAC;CACpE,MAAM,kBAAkB,SAAS,sBAAsB,MAAM,IAAI,CAAC;CAClE,MAAM,mBAAmB,KAAK,aAAa,yCAAyC;CACpF,MAAM,kBAAkB,KAAK,aAAa,wCAAwC;CAClF,MAAM,UAAU,iBAAiB,WAAW;CAE5C,MAAM,EAAE,eAAe,qBAAqB,uBAAuB;EACjE;EACA;EACA;CACF,CAAC;CAGD,IACE,kBAAkB,KAAA,KACjB,MAAM,2BAA2B;EAChC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,GACD;EACA,OAAO,MAAM,yBAAyB,UAAU,qBAAqB;EACrE,OAAO;GACL,YAAY;GACZ,WAAW;GACX,mBAAmB,QAAQ,WAAW,KAAA,IAAY,CAAC,IAAI;GACvD,kBAAkB,QAAQ,UAAU,KAAA,IAAY,CAAC,IAAI;GACrD,aAAa;EACf;CACF;CAEA,MAAM,EAAE,iBAAiB,MAAM,YAAY,MAAM,2BAA2B;EAC1E;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,MAAM,WAAW,sBAAsB;EAAE;EAAS;CAAO,CAAC;CAI1D,MAAM,0BAAoD,SACtD;EAAE,aAAa,OAAO;EAAiB,QAAQ,OAAO;EAAQ,OAAO,OAAO;CAAM,IAClF,KAAA;CAEJ,MAAM,EAAE,eAAe,qBAAqB,MAAM,eAAe;EAC/D;EACA,aAAa;GAAE,GAAG;GAAa,QAAQ,QAAQ;EAAO;EACtD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,MAAM,EAAE,cAAc,sBAAsB,MAAM,cAAc;EAC9D;EACA,aAAa;GAAE,GAAG;GAAa,OAAO,QAAQ;EAAM;EACpD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,IAAI,QAAQ,UAAU,KAAA,KAAa,QAAQ,UAAU,KAAA,GACnD,MAAM,0BAA0B;EAC9B,YAAY;EACZ;EACA,oBAAoB;EACpB;CACF,CAAC;CAGH,MAAM,oBAAoB,OAAO,KAAK,aAAa;CACnD,MAAM,mBAAmB,OAAO,KAAK,YAAY;CASjD,MAAM,cAAc,mBAClB,SACA,WACA,kBAAkB;EAChB;EACA;EACA;EACA;EACA,cAhBiB,6BAA6B;GAChD;GACA,cAAc,QAAQ;GACtB,kBACE,QAAQ,WAAW,KAAA,IAAY,OAAO,KAAK,QAAQ,UAAU,CAAC,CAAC,IAAI;EACvE,CAWe;EACX,aAXgB,QAAQ,UAAU,KAAA,IAAY,CAAC,IAAI;EAYnD;CACF,CAAC,CACH;CAEA,OAAO,KACL,WAAW,kBAAkB,OAAO,gBAAgB,iBAAiB,OAAO,gBAAgB,UAAU,EACxG;CAEA,OAAO;EACL,YAAY,kBAAkB;EAC9B,WAAW,iBAAiB;EAC5B;EACA;EACA;CACF;AACF;;;AC5pFA,MAAM,oBAAoB;CACxB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAUA,SAAS,gBAAgB,cAA+B;CACtD,OAAO,iBAAiB,QAAQ,aAAa,WAAW,KAAK,KAAK,KAAK,WAAW,YAAY;AAChG;AAEA,SAAS,qCAAqC,QAAsB;CAClE,IAAI,CAAC,0BAA0B,KAAK,MAAM,GACxC;CAEF,MAAM,MAAM,IAAI,IAAI,MAAM;CAC1B,IAAI,IAAI,aAAa,MAAM,IAAI,aAAa,IAC1C,MAAM,IAAI,MACR,0HACF;AAEJ;AAEA,eAAe,sBAAsB,EACnC,aACA,mBAI2B;CAC3B,MAAM,aAAa,MAAM,QAAQ,KAAK,aAAa,uBAAuB,CAAC;CAC3E,MAAM,oBAAoB,KAAK,aAAa,yCAAyC;CACrF,MAAM,mBAAmB,KAAK,aAAa,wCAAwC;CACnF,MAAM,uBAAuB,MAAM,gBAAgB,iBAAiB;CACpE,MAAM,sBAAsB,MAAM,gBAAgB,gBAAgB;CAClE,IAAI,sBACF,MAAM,GAAG,mBAAmB,KAAK,YAAY,gBAAgB,GAAG,EAAE,WAAW,KAAK,CAAC;CAErF,IAAI,qBACF,MAAM,GAAG,kBAAkB,KAAK,YAAY,eAAe,GAAG,EAAE,WAAW,KAAK,CAAC;CAEnF,MAAM,qBAAqB,MAAM,sBAC/B,KAAK,aAAa,wCAAwC,CAC5D;CACA,MAAM,wBAAwB,MAAM,sBAClC,KAAK,aAAa,4CAA4C,CAChE;CACA,MAAM,iBAAiB,KAAK,YAAY,gBAAgB,GAAG,eAAe;CAC1E,IAAI,uBAAuB,MACzB,MAAM,iBACJ,KAAK,YAAY,wCAAwC,GACzD,kBACF;CAEF,IAAI,0BAA0B,MAC5B,MAAM,iBACJ,KAAK,YAAY,4CAA4C,GAC7D,qBACF;CAEF,OAAO;EACL;EACA;EACA;EACA;EACA;CACF;AACF;AAEA,eAAe,YAAY,EAAE,MAAM,WAAqD;CACtF,IAAI,YAAY,MAAM;EACpB,MAAM,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;EAC9B;CACF;CACA,MAAM,iBAAiB,MAAM,OAAO;AACtC;AAEA,eAAe,uBAAuB,EACpC,aACA,YAIgB;CAChB,MAAM,oBAAoB,KAAK,aAAa,yCAAyC;CACrF,MAAM,mBAAmB,KAAK,aAAa,wCAAwC;CACnF,MAAM,QAAQ,IAAI,CAChB,GAAG,mBAAmB;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC,GACtD,GAAG,kBAAkB;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC,CACvD,CAAC;CACD,IAAI,SAAS,sBACX,MAAM,GAAG,KAAK,SAAS,YAAY,gBAAgB,GAAG,mBAAmB,EAAE,WAAW,KAAK,CAAC;CAE9F,IAAI,SAAS,qBACX,MAAM,GAAG,KAAK,SAAS,YAAY,eAAe,GAAG,kBAAkB,EAAE,WAAW,KAAK,CAAC;CAE5F,MAAM,YAAY;EAChB,MAAM,KAAK,aAAa,wCAAwC;EAChE,SAAS,SAAS;CACpB,CAAC;CACD,MAAM,YAAY;EAChB,MAAM,KAAK,aAAa,4CAA4C;EACpE,SAAS,SAAS;CACpB,CAAC;AACH;AAEA,eAAe,YAAY,EACzB,YACA,iBACA,aACA,YAMgB;CAChB,MAAM,QAAQ,IAAI,CAChB,iBAAiB,YAAY,eAAe,GAC5C,uBAAuB;EAAE;EAAa;CAAS,CAAC,CAClD,CAAC;AACH;AAEA,SAAS,eAAe,OAA4B;CAClD,MAAM,YAAY,MAAM,aAAa;CACrC,MAAM,mBACJ,cAAc,QAAQ,sBAAsB,MAAM,MAAM,IAAI,mBAAmB,MAAM,MAAM;CAE7F,OAAO,GADc,cAAc,QAAQ,QAAQ,MAC5B,GAAG;AAC5B;AAEA,SAAS,mBAAmB,MAAmB,OAA6B;CAC1E,OAAO,kBAAkB,OAAO,QAAQ;EACtC,MAAM,YAAY,KAAK;EACvB,MAAM,aAAa,MAAM;EACzB,IAAI,MAAM,QAAQ,SAAS,KAAK,MAAM,QAAQ,UAAU,GACtD,OACE,UAAU,WAAW,WAAW,UAChC,UAAU,OAAO,OAAO,UAAU,UAAU,WAAW,MAAM;EAGjE,OAAO,cAAc;CACvB,CAAC;AACH;AAEA,SAAS,wBAAwB,SAAoC;CACnE,MAAM,MAAM,QAAQ,SAAS,MAAM,IAAI,SAAS;CAChD,MAAM,eAAe,QAAQ,MAAM,aAAa,CAAC,GAAG,MAAM;CAC1D,MAAM,eAAe,CAAC,aAAa,SAAS,GAAI;CAChD,OAAO;EACL;EACA;EACA,SAAS,eAAe,aAAa,SAAS;CAChD;AACF;AAEA,SAAS,iBAAiB,SAAyC;CACjE,qCAAqC,QAAQ,MAAM;CACnD,OAAO,kBAAkB,MAAM;EAC7B,QAAQ,QAAQ;EAChB,QAAQ,QAAQ;EAChB,OAAO,QAAQ;EACf,WAAW,QAAQ;EACnB,KAAK,QAAQ;EACb,MAAM,QAAQ;EACd,WAAW,QAAQ;EACnB,UAAU,QAAQ;EAClB,UAAU,QAAQ;CACpB,CAAC;AACH;AAEA,SAAS,qBAAqB,SAAqC;CACjE,OAAO;EACL,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;CACV,CAAC,CAAC,MAAM,UAAU,UAAU,KAAA,CAAS;AACvC;AAEA,eAAe,mBAAmB,kBAA4C;CAC5E,IAAI,CAAC,QAAQ,MAAM,SAAS,CAAC,QAAQ,OAAO,OAC1C,MAAM,IAAI,MACR,yBAAyB,iBAAiB,6DAC5C;CAGF,MAAM,SAAS,gBAAgB;EAAE,OAAO,QAAQ;EAAO,QAAQ,QAAQ;CAAO,CAAC;CAC/E,IAAI;EACF,MAAM,SAAS,MAAM,OAAO,SAAS,aAAa,iBAAiB,SAAS;EAC5E,OAAO,eAAe,KAAK,OAAO,KAAK,CAAC;CAC1C,UAAU;EACR,OAAO,MAAM;CACf;AACF;AAEA,eAAe,mBAAmB,EAChC,QACA,WAIgB;CAChB,MAAM,UAAU,4BAA4B,QAAQ,MAAM;CAC1D,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,wEAAwE;CAG1F,MAAM,WAAW,sBAAsB;EAAE;EAAS,MAAM,QAAQ;CAAK,CAAC;CACtE,MAAM,cAAc,QAAQ,IAAI;CAChC,IAAI,mBAAmB,SAAS;CAChC,KAAK,MAAM,6BAA6B,SAAS,4BAC/C,IAAI,MAAM,WAAW,KAAK,aAAa,yBAAyB,CAAC,GAAG;EAClE,mBAAmB;EACnB;CACF;CAEF,MAAM,aAAa,KAAK,aAAa,gBAAgB;CACrD,MAAM,6BAA6B;EAAE,UAAU;EAAa;CAAW,CAAC;CAExE,IAAK,MAAM,WAAW,UAAU,KAAM,CAAC,QAAQ,OAAO;EACpD,IAAI,OAAO,YAAY,OAAO,QAC5B,MAAM,IAAI,MACR,yCAAyC,iBAAiB,4DAC5D;EAGF,IAAI,CAAC,OADoB,QAAQ,oBAAoB,mBAAA,CAAoB,gBAAgB,GACzE;GACd,OAAO,KAAK,QAAQ,iBAAiB,YAAY;GACjD,IAAI,OAAO,UAAU;IACnB,OAAO,YAAY,WAAW,CAAC,CAAC;IAChC,OAAO,YAAY,WAAW,CAAC,gBAAgB,CAAC;GAClD;GACA;EACF;CACF;CAEA,MAAM,6BAA6B;EAAE,UAAU;EAAa;CAAW,CAAC;CACxE,MAAM,UAAU,QAAQ,UAAU,CAAC;CACnC,MAAM,6BAA6B;EAAE,UAAU;EAAa;CAAW,CAAC;CACxE,MAAM,iBAAiB,YAAY,SAAS,OAAO;CACnD,OAAO,QAAQ,WAAW,kBAAkB;CAC5C,IAAI,OAAO,UAAU;EACnB,OAAO,YAAY,WAAW,CAAC,gBAAgB,CAAC;EAChD,OAAO,YAAY,WAAW,CAAC,CAAC;CAClC;AACF;AAEA,eAAe,6BAA6B,EAC1C,QACA,WAImB;CACnB,MAAM,UAAU,4BAA4B,QAAQ,MAAM;CAC1D,MAAM,oBAAoB,qBAAqB,OAAO;CACtD,MAAM,sBAAsB,QAAQ,SAAS,KAAA,KAAa,QAAQ,UAAU;CAE5E,IAAI,WAAW,uBAAuB,mBACpC,MAAM,IAAI,MACR,gGACF;CAEF,IAAK,WAAW,CAAC,qBAAsB,qBAAqB;EAC1D,MAAM,mBAAmB;GAAE;GAAQ;EAAQ,CAAC;EAC5C,OAAO;CACT;CACA,OAAO;AACT;AAEA,SAAS,mBAAmB,SAAiB,YAAoB;CAC/D,MAAM,SAAuB,CAAC;CAC9B,MAAM,SAASC,MAAW,SAAS,QAAQ,EAAE,oBAAoB,KAAK,CAAC;CACvE,MAAM,aAAa,OAAO;CAC1B,IAAI,YACF,MAAM,IAAI,MACR,mBAAmB,WAAW,IAAI,oBAAoB,WAAW,KAAK,EAAE,aAAa,WAAW,OAAO,EACzG;CAEF,OAAO,iBAAiB,MAAM,MAAM;AACtC;AAEA,eAAsB,WAAW,QAAgB,SAA2C;CAC1F,IAAI,MAAM,6BAA6B;EAAE;EAAQ;CAAQ,CAAC,GACxD;CAGF,MAAM,cAAc,QAAQ,IAAI;CAChC,MAAM,qBAAqB,QAAQ,cAAA;CACnC,MAAM,aAAa,YAAY,oBAAoB,WAAW;CAE9D,IAAI,CAAE,MAAM,WAAW,UAAU,GAC/B,MAAM,IAAI,MACR,iCAAiC,mBAAmB,8CACtD;CAMF,IAAI,gBAD2B,SAAS,MAFV,SAAS,WAAW,GAEO,MAD5B,SAAS,UAAU,CAEP,CAAC,GACxC,MAAM,IAAI,MACR,4DAA4D,mBAAmB,EACjF;CAGF,MAAM,cAAc,iBAAiB,OAAO;CAC5C,MAAM,oBAAoB,KAAK,aAAa,yCAAyC;CACrF,MAAM,mBAAmB,KAAK,aAAa,wCAAwC;CACnF,MAAM,QAAQ,IAAI;EAChB,6BAA6B;GAAE,UAAU;GAAa,YAAY;EAAkB,CAAC;EACrF,6BAA6B;GAAE,UAAU;GAAa,YAAY;EAAiB,CAAC;EACpF,6BAA6B;GAC3B,UAAU;GACV,YAAY,KAAK,aAAa,wCAAwC;EACxE,CAAC;EACD,6BAA6B;GAC3B,UAAU;GACV,YAAY,KAAK,aAAa,4CAA4C;EAC5E,CAAC;CACH,CAAC;CACD,MAAM,QAAQ,IAAI,CAChB,wBAAwB,iBAAiB,GACzC,wBAAwB,gBAAgB,CAC1C,CAAC;CACD,IAAI,MAAM,gBAAgB,iBAAiB,GACzC,MAAM,6BAA6B,iBAAiB;CAEtD,IAAI,MAAM,gBAAgB,gBAAgB,GACxC,MAAM,6BAA6B,gBAAgB;CAErD,MAAM,kBAAkB,MAAM,gBAAgB,UAAU;CACxD,MAAM,eAAe,mBAAmB,iBAAiB,kBAAkB;CAC3E,MAAM,kBAAkB,aAAa,WAAW,CAAC;CACjD,MAAM,WAAW,eAAe,WAAW;CAE3C,IAAI,gBAAgB,MAAM,UAAU,eAAe,KAAK,MAAM,QAAQ,GACpE,MAAM,IAAI,MACR,WAAW,YAAY,OAAO,2BAA2B,mBAAmB,iDAC9E;CAGF,MAAM,mBAAmB,MAAM,eAAe,QAC5C;EACE;EACA,SAAS,QAAQ;EACjB,QAAQ,QAAQ;CAClB,GACA,EAAE,OAAO,CACX;CACA,IAAI,iBAAiB,WAAW,CAAC,CAAC,MAAM,UAAU,eAAe,KAAK,MAAM,QAAQ,GAClF,MAAM,IAAI,MACR,WAAW,YAAY,OAAO,qGAChC;CAEF,MAAM,qBAAqB,MAAM,6BAA6B;EAC5D,SAAS,iBAAiB,WAAW;EACrC;EACA;CACF,CAAC;CACD,MAAM,oBAAoB,MAAM,4BAA4B;EAC1D,SAAS,iBAAiB,WAAW;EACrC;EACA;CACF,CAAC;CAED,MAAM,WACJ,aAAa,YAAY,KAAA,IAAY,CAAC,SAAS,IAAI,CAAC,WAAW,gBAAgB,MAAM;CACvF,MAAM,YAAY,aAAa,YAAY,KAAA,IAAY,CAAC,WAAW,IAAI;CACvE,MAAM,oBAAoB,wBAAwB,eAAe;CAEjE,IAAI,iBAAiB,WAAW,iBADlB,OAAO,iBAAiB,UAAU,WAAW,EAAE,kBAAkB,CAC1B,CAAC;CACtD,IAAI,CAAC,eAAe,SAAS,IAAI,GAC/B,kBAAkB,kBAAkB;CAItC,mBAAmB,gBAAgB,kBAAkB;CACrD,MAAM,WAAW,MAAM,sBAAsB;EAAE;EAAa,iBAAiB;CAAgB,CAAC;CAC9F,IAAI,kBAAkB;CACtB,IAAI;EACF,MAAM,iBAAiB,YAAY,cAAc;EAUjD,IAAI,EADY,MARK,eAAe,QAClC;GACE;GACA,SAAS,QAAQ;GACjB,QAAQ,QAAQ;EAClB,GACA,EAAE,OAAO,CACX,EAAA,CACuB,WACZ,CAAC,CAAC,MAAM,UAAU,mBAAmB,OAAO,WAAW,CAAC,GACjE,MAAM,IAAI,MACR,GAAG,KAAK,QAAQ,UAAU,GAAG,wCAAwC,EAAE,0BAA0B,mBAAmB,qEACtH;EAGF,MAAM,SAAS,MAAM,uBAAuB;GAC1C,SAAS,CAAC,WAAW;GACrB;GACA,SAAS;IACP,OAAO,QAAQ;IACf,eAAe;IACf,6BAA6B;IAC7B,uBAAuB,YAAY,WAAW,KAAA,KAAa,YAAY,UAAU,KAAA;IACjF,sBAAsB,YAAY,UAAU,KAAA;IAC5C;IACA;GACF;GACA;EACF,CAAC;EAED,IAAI,OAAO,UAAU;GACnB,OAAO,YAAY,UAAU,YAAY,MAAM;GAC/C,OAAO,YAAY,cAAc,kBAAkB;GACnD,OAAO,YAAY,oBAAoB,OAAO,gBAAgB;GAC9D,OAAO,YAAY,iBAAiB,OAAO,iBAAiB;GAC5D,OAAO,YAAY,gBAAgB,OAAO,gBAAgB;GAC1D,OAAO,YAAY,qBAAqB,OAAO,iBAAiB;EAClE;EAEA,IAAI,OAAO,oBAAoB,GAC7B,MAAM,IAAI,MACR,qBAAqB,OAAO,kBAAkB,MAAM,OAAO,iBAAiB,uBAAuB,mBAAmB,iCACxH;EAGF,OAAO,QACL,UAAU,YAAY,OAAO,OAAO,mBAAmB,iBAAiB,OAAO,kBAAkB,gBAAgB,OAAO,iBAAiB,UAC3I;CACF,SAAS,OAAO;EACd,IAAI;GACF,MAAM,YAAY;IAAE;IAAY;IAAiB;IAAa;GAAS,CAAC;EAC1E,SAAS,eAAe;GACtB,kBAAkB;GAElB,MAAM,IAAI,eACR,CAAC,OAAO,aAAa,GACrB,wEAAwE,SAAS,WAAW,IAC5F,EAAE,OAAO,MAAM,CACjB;EACF;EACA,MAAM;CACR,UAAU;EACR,IAAI,iBACF,MAAM,GAAG,SAAS,YAAY;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CAElE;AACF;;;;;;ACtfA,SAAgB,oBAAoB,QAAiC;CACnE,OACE,OAAO,aACP,OAAO,cACP,OAAO,WACP,OAAO,gBACP,OAAO,iBACP,OAAO,cACP,OAAO,aACP,OAAO,mBACP,OAAO,eACN,OAAO,mBAAmB;AAE/B;;;AC3BA,SAASC,kBAAgB,OAAe,OAA2B;CACjE,MAAM,SAAS,iBAAiB,UAAU,KAAK;CAC/C,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,SACR,WAAW,MAAM,SAAS,MAAM,qBAAqB,iBAAiB,KAAK,IAAI,KAC/E,WAAW,cACb;CAEF,OAAO,OAAO;AAChB;AAEA,eAAsB,eAAe,QAAgB,SAAwC;CAG3F,MAAM,WAAWA,kBAAgB,QAAQ,QAAQ,IAAI,QAAQ;CAC7D,MAAM,cAAc,QAAQ,MAAM,CAAC,EAAA,CAAG,KAAK,MAAMA,kBAAgB,GAAG,aAAa,CAAC;CAClF,MAAM,UAAU,MAAM,KAAK,IAAI,IAAI,UAAU,CAAC;CAE9C,MAAM,kBAAkB,CAAC,UAAU,GAAG,OAAO,CAAC,CAAC,KAAK,qBAAqB;CACzE,IAAI,iBACF,MAAM,IAAI,SACR,4BAA4B,gBAAgB,yHAE5C,WAAW,cACb;CAGF,IAAI,QAAQ,SAAS,QAAQ,GAC3B,MAAM,IAAI,SACR,uDAAuD,SAAS,wFAEhE,WAAW,cACb;CAMF,MAAM,SAAS,MAAM,eAAe,QAClC;EACE,GAAG;EACH,SAAS,CAAC,UAAU,GAAG,OAAO;EAC9B,UAAU,QAAQ,YAAY,CAAC,GAAG;CACpC,GACA,EAAE,OAAO,CACX;CAEA,MAAM,YAAY,OAAO,cAAc;CACvC,MAAM,aAAa,YAAY,eAAe;CAE9C,OAAO,MAAM,yBAAyB,SAAS,MAAM,QAAQ,KAAK,IAAI,EAAE,IAAI;CAE5E,MAAM,SAAS,MAAM,gBAAgB;EAAE;EAAQ;EAAU;EAAS;CAAO,CAAC;CAE1E,MAAM,iBAAiB,oBAAoB,MAAM;CAEjD,IAAI,mBAAmB,GAAG;EACxB,MAAM,kBAAkB,OAAO,YAAY,QAAQ,CAAC,CAAC,KAAK,IAAI;EAC9D,OAAO,KAAK,4CAA4C,iBAAiB;EACzE;CACF;CAEA,IAAI,OAAO,UAAU;EACnB,OAAO,YAAY,QAAQ,QAAQ;EACnC,OAAO,YAAY,MAAM,OAAO;EAChC,OAAO,YAAY,UAAU,SAAS;EACtC,OAAO,YAAY,YAAY;GAC7B,OAAO,EAAE,OAAO,OAAO,WAAW;GAClC,QAAQ,EAAE,OAAO,OAAO,YAAY;GACpC,KAAK,EAAE,OAAO,OAAO,SAAS;GAC9B,UAAU,EAAE,OAAO,OAAO,cAAc;GACxC,WAAW,EAAE,OAAO,OAAO,eAAe;GAC1C,QAAQ,EAAE,OAAO,OAAO,YAAY;GACpC,OAAO,EAAE,OAAO,OAAO,WAAW;GAClC,aAAa,EAAE,OAAO,OAAO,iBAAiB;GAC9C,QAAQ,EAAE,OAAO,OAAO,YAAY;EACtC,CAAC;EACD,OAAO,YAAY,cAAc,cAAc;CACjD;CAEA,MAAM,QAAkB,CAAC;CACzB,IAAI,OAAO,aAAa,GAAG,MAAM,KAAK,GAAG,OAAO,WAAW,OAAO;CAClE,IAAI,OAAO,cAAc,GAAG,MAAM,KAAK,GAAG,OAAO,YAAY,cAAc;CAC3E,IAAI,OAAO,WAAW,GAAG,MAAM,KAAK,GAAG,OAAO,SAAS,WAAW;CAClE,IAAI,OAAO,gBAAgB,GAAG,MAAM,KAAK,GAAG,OAAO,cAAc,UAAU;CAC3E,IAAI,OAAO,iBAAiB,GAAG,MAAM,KAAK,GAAG,OAAO,eAAe,WAAW;CAC9E,IAAI,OAAO,cAAc,GAAG,MAAM,KAAK,GAAG,OAAO,YAAY,QAAQ;CACrE,IAAI,OAAO,aAAa,GAAG,MAAM,KAAK,GAAG,OAAO,WAAW,OAAO;CAClE,IAAI,OAAO,mBAAmB,GAAG,MAAM,KAAK,GAAG,OAAO,iBAAiB,aAAa;CACpF,IAAI,OAAO,cAAc,GAAG,MAAM,KAAK,GAAG,OAAO,YAAY,QAAQ;CAGrE,MAAM,UAAU,GAAG,aADA,YAAY,kBAAkB,YACN,GAAG,eAAe,sBAAsB,SAAS,MAAM,QAAQ,KAAK,IAAI,EAAE,IAAI,MAAM,KAAK,KAAK,EAAE;CAE3I,IAAI,WACF,OAAO,KAAK,OAAO;MAEnB,OAAO,QAAQ,OAAO;AAE1B;;;;;;ACjEA,MAAM,gBAA2C;CAC/C,OAAO,CAAC,OAAO;CACf,UAAU,CAAC,UAAU;CACrB,WAAW,CAAC,WAAW;CACvB,QAAQ,CAAC,QAAQ;CACjB,QAAQ,CAAC,QAAQ;CACjB,QAAQ,CAAC,2BAA2B;CACpC,KAAK,CAAC,wBAAwB,6BAA6B;CAC3D,OAAO,CAAC,0BAA0B,+BAA+B;CACjE,aAAa,CAAC,gCAAgC,qCAAqC;AACrF;;;;AAKA,SAAS,aAAa,QAA2C;CAC/D,OAAO,WAAW;AACpB;;;;;AAMA,SAAS,iBAAiB,cAAsB,MAAoB;CAClE,IAAI,OAAA,UACF,MAAM,IAAI,kBACR,SAAS,aAAa,iCAAiC,OAAO,OAAO,KAAA,CAAM,QAAQ,CAAC,EAAE,OAAO,gBAAgB,OAAO,KAAK,IAC3H;AAEJ;;;;;;;AA4BA,eAAe,yBAAyB,QAGP;CAC/B,MAAM,EAAE,WAAW,cAAc;CACjC,MAAM,QAAkB,CAAC;CAEzB,MAAM,YAAY,MAAM,UAAU,cAAc;CAChD,IAAI,UAAU,WAAW,GACvB,OAAO,EAAE,OAAO,CAAC,EAAE;CAGrB,MAAM,gBAAgB,MAAM,UAAU,gCAAgC,SAAS;CAC/E,KAAK,MAAM,QAAQ,eAAe;EAChC,MAAM,eAAe,KAAK,KAAK,mBAAmB,GAAG,KAAK,oBAAoB,CAAC;EAE/E,MAAM,iBADa,KAAK,WAAW,YACH,GAAG,KAAK,eAAe,CAAC;EACxD,MAAM,KAAK,YAAY;CACzB;CAEA,OAAO,EAAE,MAAM;AACjB;;;;;;;;;AAUA,eAAe,8BAA8B,QAMR;CACnC,MAAM,EAAE,SAAS,WAAW,QAAQ,UAAU,WAAW;CACzD,MAAM,iBAA2B,CAAC;CAIlC,MAAM,iBAID;EACH;GACE,SAAS;GACT,kBAAkB,eAAe,eAAe,EAAE,QAAQ,MAAM,CAAC;GACjE,uBACE,IAAI,eAAe;IAAE,YAAY;IAAS,YAAY;IAAQ,QAAQ;IAAO;GAAO,CAAC;EACzF;EACA;GACE,SAAS;GACT,kBACE,kBAAkB,eAAe;IAAE,QAAQ;IAAO,kBAAkB;GAAM,CAAC;GAC7E,uBACE,IAAI,kBAAkB;IAAE,YAAY;IAAS,YAAY;IAAQ,QAAQ;IAAO;GAAO,CAAC;EAC5F;EACA;GACE,SAAS;GACT,kBACE,mBAAmB,eAAe;IAAE,QAAQ;IAAO,kBAAkB;GAAM,CAAC;GAC9E,uBACE,IAAI,mBAAmB;IAAE,YAAY;IAAS,YAAY;IAAQ,QAAQ;IAAO;GAAO,CAAC;EAC7F;EACA;GACE,SAAS;GACT,kBAAkB,gBAAgB,eAAe,EAAE,QAAQ,MAAM,CAAC;GAClE,uBACE,IAAI,gBAAgB;IAAE,YAAY;IAAS,YAAY;IAAQ,QAAQ;IAAO;GAAO,CAAC;EAC1F;EACA;GACE,SAAS;GACT,kBAAkB,gBAAgB,eAAe;GACjD,uBACE,IAAI,gBAAgB;IAAE,YAAY;IAAS,YAAY;IAAQ;GAAO,CAAC;EAC3E;EACA;GACE,SAAS;GACT,kBAAkB,aAAa,eAAe,EAAE,QAAQ,MAAM,CAAC;GAC/D,uBACE,IAAI,aAAa;IAAE,YAAY;IAAS,YAAY;IAAQ,QAAQ;IAAO;GAAO,CAAC;EACvF;EACA;GACE,SAAS;GACT,kBAAkB,eAAe,eAAe,EAAE,QAAQ,MAAM,CAAC;GACjE,uBACE,IAAI,eAAe;IAAE,YAAY;IAAS,YAAY;IAAQ,QAAQ;IAAO;GAAO,CAAC;EACzF;CACF;CAGA,KAAK,MAAM,UAAU,gBAAgB;EACnC,IAAI,CAAC,SAAS,SAAS,OAAO,OAAO,GACnC;EAGF,IAAI,CADqB,OAAO,WACZ,CAAC,CAAC,SAAS,MAAM,GACnC;EAGF,MAAM,SAAS,MAAM,yBAAyB;GAAE,WAD9B,OAAO,gBAC+B;GAAG;EAAU,CAAC;EACtE,eAAe,KAAK,GAAG,OAAO,KAAK;CACrC;CAKA,IAAI,SAAS,SAAS,QAAQ,GAC5B,OAAO,MACL,sFACF;CAGF,OAAO;EAAE,WAAW,eAAe;EAAQ;CAAe;AAC5D;;;;AAKA,SAAS,gBAAgB,UAAgC;CACvD,IAAI,aAAa,KAAA,GACf,OAAO,CAAC,QAAQ;CAElB,IAAI,SAAS,SAAS,GAAG,GACvB,OAAO,CAAC,GAAG,YAAY;CAEzB,OAAO,SAAS,QAAQ,MAAoB,aAAa,SAAS,CAAY,CAAC;AACjF;;;;AAKA,SAAS,cAAc,OAAiD;CACtE,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,EAAE,gBAAgB,QACnE,OAAO;CAGT,OAAO,OADa,OAAO,yBAAyB,OAAO,YAAY,CAAC,EAAE,UAC5C;AAChC;;;;AAKA,SAAS,gBAAgB,OAAyB;CAChD,IAAI,iBAAiB,qBAAqB,MAAM,eAAe,KAC7D,OAAO;CAGT,IAAI,cAAc,KAAK,KAAK,MAAM,eAAe,KAC/C,OAAO;CAET,OAAO;AACT;;;;;;;;;AAoBA,eAAsB,WAAW,QAA4C;CAC3E,MAAM,EAAE,QAAQ,UAAU,CAAC,GAAG,aAAa,QAAQ,IAAI,GAAG,WAAW;CAGrE,MAAM,SAAS,YAAY,MAAM;CAGjC,IAAI,OAAO,aAAa,UACtB,MAAM,IAAI,MACR,gFACF;CAIF,MAAM,cAAc,QAAQ,OAAO,OAAO;CAE1C,MAAM,eAAe,YAAY,QAAQ,QAAQ,OAAO,QAAQ,GAAG;CACnE,MAAM,YAAY,QAAQ,UAAA;CAC1B,MAAM,mBAAqC,QAAQ,YAAY;CAC/D,MAAM,kBAAkB,gBAAgB,QAAQ,QAAQ;CACxD,MAAM,SAAsB,QAAQ,UAAU;CAG9C,mBAAmB;EACjB,cAAc;EACd,iBAAiB;CACnB,CAAC;CAID,MAAM,SAAS,IAAI,aAAa,EAAE,OADpB,aAAa,aAAa,QAAQ,KACV,EAAE,CAAC;CAGzC,OAAO,MAAM,0BAA0B,OAAO,MAAM,GAAG,OAAO,MAAM;CAEpE,IAAI,CAAC,MADiB,OAAO,mBAAmB,OAAO,OAAO,OAAO,IAAI,GAEvE,MAAM,IAAI,kBACR,yBAAyB,OAAO,MAAM,GAAG,OAAO,KAAK,2DACrD,GACF;CAIF,MAAM,MAAM,eAAgB,MAAM,OAAO,iBAAiB,OAAO,OAAO,OAAO,IAAI;CACnF,OAAO,MAAM,cAAc,KAAK;CAGhC,IAAI,aAAa,MAAM,GACrB,OAAO,yBAAyB;EAC9B;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAIH,MAAM,YAAY,IAAI,UAAA,EAAiC;CAGvD,MAAM,eAAe,MAAM,oBAAoB;EAC7C;EACA,OAAO,OAAO;EACd,MAAM,OAAO;EACb,UAAU;EACV;EACA;EACA;EACA;CACF,CAAC;CAED,IAAI,aAAa,WAAW,GAAG;EAC7B,OAAO,KAAK,6CAA6C,gBAAgB,KAAK,IAAI,GAAG;EACrF,OAAO;GACL,QAAQ,GAAG,OAAO,MAAM,GAAG,OAAO;GAClC;GACA,OAAO,CAAC;GACR,SAAS;GACT,aAAa;GACb,SAAS;EACX;CACF;CAGA,MAAM,iBAAiB,KAAK,YAAY,SAAS;CAGjD,KAAK,MAAM,EAAE,cAAc,UAAU,cAAc;EACjD,mBAAmB;GACjB;GACA,iBAAiB;EACnB,CAAC;EAED,iBAAiB,cAAc,IAAI;CACrC;CAMA,MAAM,UAAU,MAAM,QAAQ,IAC5B,aAAa,IAAI,OAAO,EAAE,YAAY,mBAAmB;EACvD,MAAM,YAAY,KAAK,gBAAgB,YAAY;EACnD,MAAM,SAAS,MAAM,WAAW,SAAS;EAEzC,IAAI,UAAU,qBAAqB,QAAQ;GACzC,OAAO,MAAM,2BAA2B,cAAc;GACtD,OAAO;IAAE;IAAc,QAAQ;GAAmB;EACpD;EAKA,MAAM,iBAAiB,WAAW,MAHZ,cAAc,iBAClC,OAAO,eAAe,OAAO,OAAO,OAAO,MAAM,YAAY,GAAG,CAClE,CACyC;EAEzC,MAAM,SAAS,SAAU,gBAA2B;EACpD,OAAO,MAAM,UAAU,aAAa,IAAI,OAAO,EAAE;EACjD,OAAO;GAAE;GAAc;EAAO;CAChC,CAAC,CACH;CAYA,OAAO;EARL,QAAQ,GAAG,OAAO,MAAM,GAAG,OAAO;EAClC;EACA,OAAO;EACP,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC,CAAC;EACvD,aAAa,QAAQ,QAAQ,MAAM,EAAE,WAAW,aAAa,CAAC,CAAC;EAC/D,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC,CAAC;CAG5C;AACf;;;;AAKA,eAAe,oBAAoB,QAS4C;CAC7E,MAAM,EAAE,QAAQ,OAAO,MAAM,UAAU,KAAK,iBAAiB,WAAW,WAAW;CAInF,MAAM,2BAAW,IAAI,IAAwC;CAE7D,eAAe,mBAAmB,MAA0C;EAC1E,IAAI,UAAU,SAAS,IAAI,IAAI;EAC/B,IAAI,YAAY,KAAA,GAAW;GACzB,UAAU,cAAc,iBAAiB,OAAO,cAAc,OAAO,MAAM,MAAM,GAAG,CAAC;GACrF,SAAS,IAAI,MAAM,OAAO;EAC5B;EACA,OAAO;CACT;CAEA,MAAM,QAAQ,gBAAgB,SAAS,YACrC,cAAc,QAAQ,CAAC,KAAK,iBAAiB;EAAE;EAAS;CAAY,EAAE,CACxE;CAuEA,QAAO,MArEe,QAAQ,IAC5B,MAAM,IAAI,OAAO,EAAE,kBAAkB;EACnC,MAAM,WACJ,aAAa,OAAO,aAAa,KAAK,cAAc,MAAM,KAAK,UAAU,WAAW;EACtF,MAAM,YAA+E,CAAC;EAEtF,IAAI;GAEF,IAAI,YAAY,SAAS,GAAG,GAE1B,IAAI;IAIF,MAAM,aAAY,MAHI,mBACpB,aAAa,OAAO,aAAa,KAAK,MAAM,QAC9C,EAAA,CAC0B,MAAM,MAAM,EAAE,SAAS,eAAe,EAAE,SAAS,MAAM;IACjF,IAAI,WACF,UAAU,KAAK;KACb,YAAY,UAAU;KACtB,cAAc;KACd,MAAM,UAAU;IAClB,CAAC;GAEL,SAAS,OAAO;IAEd,IAAI,gBAAgB,KAAK,GACvB,OAAO,MAAM,mBAAmB,UAAU;SAE1C,MAAM;GAEV;QACK;IAEL,MAAM,WAAW,MAAM,uBAAuB;KAC5C;KACA;KACA;KACA,MAAM;KACN;KACA;IACF,CAAC;IAED,KAAK,MAAM,QAAQ,UAAU;KAE3B,MAAM,eACJ,aAAa,OAAO,aAAa,KAC7B,KAAK,OACL,KAAK,KAAK,UAAU,SAAS,SAAS,CAAC;KAE7C,UAAU,KAAK;MACb,YAAY,KAAK;MACjB;MACA,MAAM,KAAK;KACb,CAAC;IACH;GACF;EACF,SAAS,OAAO;GAEd,IAAI,gBAAgB,KAAK,GAAG;IAE1B,OAAO,MAAM,sBAAsB,UAAU;IAC7C,OAAO;GACT;GACA,MAAM;EACR;EAEA,OAAO;CACT,CAAC,CACH,EAAA,CAEe,KAAK;AACtB;;;;AAKA,eAAe,yBAAyB,QAWd;CACxB,MAAM,EACJ,QACA,QACA,KACA,cACA,iBACA,QACA,WACA,YACA,kBAAkB,mBAClB,WACE;CAGJ,MAAM,UAAU,MAAM,oBAAoB;CAC1C,OAAO,MAAM,2BAA2B,SAAS;CAGjD,MAAM,YAAY,IAAI,UAAA,EAAiC;CAEvD,IAAI;EAGF,MAAM,eAAe,MAAM,oBAAoB;GAC7C;GACA,OAAO,OAAO;GACd,MAAM,OAAO;GACb,UAAU;GACV;GACA;GACA;GACA;EACF,CAAC;EAED,IAAI,aAAa,WAAW,GAAG;GAC7B,OAAO,KAAK,6CAA6C,gBAAgB,KAAK,IAAI,GAAG;GACrF,OAAO;IACL,QAAQ,GAAG,OAAO,MAAM,GAAG,OAAO;IAClC;IACA,OAAO,CAAC;IACR,SAAS;IACT,aAAa;IACb,SAAS;GACX;EACF;EAGA,KAAK,MAAM,EAAE,cAAc,UAAU,cACnC,iBAAiB,cAAc,IAAI;EAKrC,MAAM,YAAY,mBAAmB,MAAM;EAE3C,MAAM,QAAQ,IACZ,aAAa,IAAI,OAAO,EAAE,YAAY,mBAAmB;GAEvD,MAAM,mBAAmB,cAAc,cAAc,SAAS;GAC9D,mBAAmB;IACjB,cAAc;IACd,iBAAiB;GACnB,CAAC;GAOD,MAAM,iBANY,KAAK,SAAS,gBAMD,GAAG,MAHZ,cAAc,iBAClC,OAAO,eAAe,OAAO,OAAO,OAAO,MAAM,YAAY,GAAG,CAClE,CACyC;GACzC,OAAO,MAAM,oBAAoB,kBAAkB;EACrD,CAAC,CACH;EAIA,MAAM,EAAE,WAAW,mBAAmB,MAAM,8BAA8B;GACxE;GACA,WAHqB,KAAK,YAAY,SAGd;GACxB;GACA,UAAU;GACV;EACF,CAAC;EAGD,MAAM,UAA6B,eAAe,KAAK,kBAAkB;GACvE;GACA,QAAQ;EACV,EAAE;EAEF,OAAO,MAAM,aAAa,UAAU,cAAc,OAAO,2BAA2B;EAEpF,OAAO;GACL,QAAQ,GAAG,OAAO,MAAM,GAAG,OAAO;GAClC;GACA,OAAO;GACP,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC,CAAC;GACvD,aAAa,QAAQ,QAAQ,MAAM,EAAE,WAAW,aAAa,CAAC,CAAC;GAC/D,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC,CAAC;EACzD;CACF,UAAU;EAER,MAAM,oBAAoB,OAAO;CACnC;AACF;;;;;AAMA,SAAS,mBAAmB,QAM1B;CAEA,MAAM,UAMF,CAAC;CAIL,IAD8B,eAAe,eAAe,EAAE,QAAQ,MAAM,CACpD,CAAC,CAAC,SAAS,MAAM,GAAG;EAC1C,MAAM,UAAU,eAAe,WAAW,MAAM;EAChD,IAAI,SAAS;GACX,MAAM,QAAQ,QAAQ,MAAM,iBAAiB,EAAE,QAAQ,MAAM,CAAC;GAC9D,QAAQ,QAAQ;IACd,MAAM,MAAM,MAAM;IAClB,SAAS,MAAM,SAAS;GAC1B;EACF;CACF;CAOA,IAJiC,kBAAkB,eAAe;EAChE,QAAQ;EACR,kBAAkB;CACpB,CAC2B,CAAC,CAAC,SAAS,MAAM,GAAG;EAC7C,MAAM,UAAU,kBAAkB,WAAW,MAAM;EACnD,IAAI,SAEF,QAAQ,WADM,QAAQ,MAAM,iBAAiB,EAAE,QAAQ,MAAM,CACtC,CAAC,CAAC;CAE7B;CAOA,IAJkC,mBAAmB,eAAe;EAClE,QAAQ;EACR,kBAAkB;CACpB,CAC4B,CAAC,CAAC,SAAS,MAAM,GAAG;EAC9C,MAAM,UAAU,mBAAmB,WAAW,MAAM;EACpD,IAAI,SAEF,QAAQ,YADM,QAAQ,MAAM,iBAAiB,EAAE,QAAQ,MAAM,CACrC,CAAC,CAAC;CAE9B;CAIA,IAD+B,gBAAgB,eAAe,EAAE,QAAQ,MAAM,CACrD,CAAC,CAAC,SAAS,MAAM,GAAG;EAC3C,MAAM,UAAU,gBAAgB,WAAW,MAAM;EACjD,IAAI,SAEF,QAAQ,SADM,QAAQ,MAAM,iBAAiB,EAAE,QAAQ,MAAM,CACxC,CAAC,CAAC;CAE3B;CAIA,IAD+B,gBAAgB,eAAe,EAAE,QAAQ,MAAM,CACrD,CAAC,CAAC,SAAS,MAAM,GAAG;EAC3C,MAAM,UAAU,gBAAgB,WAAW,MAAM;EACjD,IAAI,SAEF,QAAQ,SADM,QAAQ,MAAM,iBAAiB,EAAE,QAAQ,MAAM,CACxC,CAAC,CAAC;CAE3B;CAEA,OAAO;AACT;;;;AAKA,SAAS,cACP,cACA,WACQ;CAER,IAAI,aAAa,WAAW,QAAQ,GAAG;EACrC,MAAM,WAAW,aAAa,UAAU,CAAe;EACvD,IAAI,UAAU,OAAO,SACnB,OAAO,KAAK,UAAU,MAAM,SAAS,QAAQ;CAEjD;CAGA,IAAI,UAAU,OAAO,QAAQ,iBAAiB,UAAU,MAAM,MAC5D,OAAO;CAIT,IAAI,aAAa,WAAW,WAAW,GAAG;EACxC,MAAM,WAAW,aAAa,UAAU,CAAkB;EAC1D,IAAI,UAAU,UACZ,OAAO,KAAK,UAAU,UAAU,QAAQ;CAE5C;CAGA,IAAI,aAAa,WAAW,YAAY,GAAG;EACzC,MAAM,WAAW,aAAa,UAAU,EAAmB;EAC3D,IAAI,UAAU,WACZ,OAAO,KAAK,UAAU,WAAW,QAAQ;CAE7C;CAGA,IAAI,aAAa,WAAW,SAAS,GAAG;EACtC,MAAM,WAAW,aAAa,UAAU,CAAgB;EACxD,IAAI,UAAU,QACZ,OAAO,KAAK,UAAU,QAAQ,QAAQ;CAE1C;CAGA,IAAI,aAAa,WAAW,SAAS,GAAG;EACtC,MAAM,WAAW,aAAa,UAAU,CAAgB;EACxD,IAAI,UAAU,QACZ,OAAO,KAAK,UAAU,QAAQ,QAAQ;CAE1C;CAGA,OAAO;AACT;;;;AAKA,SAAgB,mBAAmB,SAA+B;CAChE,MAAM,QAAkB,CAAC;CAEzB,MAAM,KAAK,gBAAgB,QAAQ,OAAO,GAAG,QAAQ,IAAI,EAAE;CAE3D,KAAK,MAAM,QAAQ,QAAQ,OAAO;EAChC,MAAM,OAAO,KAAK,WAAW,YAAY,MAAM;EAC/C,MAAM,aACJ,KAAK,WAAW,YACZ,cACA,KAAK,WAAW,gBACd,kBACA;EACR,MAAM,KAAK,KAAK,KAAK,GAAG,KAAK,aAAa,GAAG,YAAY;CAC3D;CAEA,MAAM,QAAkB,CAAC;CACzB,IAAI,QAAQ,UAAU,GAAG,MAAM,KAAK,GAAG,QAAQ,QAAQ,SAAS;CAChE,IAAI,QAAQ,cAAc,GAAG,MAAM,KAAK,GAAG,QAAQ,YAAY,aAAa;CAC5E,IAAI,QAAQ,UAAU,GAAG,MAAM,KAAK,GAAG,QAAQ,QAAQ,SAAS;CAEhE,MAAM,KAAK,EAAE;CACb,MAAM,cAAc,MAAM,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI;CAC1D,MAAM,KAAK,YAAY,aAAa;CAEpC,OAAO,MAAM,KAAK,IAAI;AACxB;;;ACvyBA,eAAsB,aAAa,QAAgB,SAA6C;CAC9F,MAAM,EAAE,QAAQ,GAAG,iBAAiB;CAEpC,OAAO,MAAM,uBAAuB,OAAO,IAAI;CAE/C,IAAI;EACF,MAAM,UAAU,MAAM,WAAW;GAC/B;GACA,SAAS;GACT;EACF,CAAC;EAGD,IAAI,OAAO,UAAU;GACnB,MAAM,eAAe,QAAQ,MAC1B,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC,CACrC,KAAK,MAAM,EAAE,YAAY;GAC5B,MAAM,mBAAmB,QAAQ,MAC9B,QAAQ,MAAM,EAAE,WAAW,aAAa,CAAC,CACzC,KAAK,MAAM,EAAE,YAAY;GAC5B,MAAM,eAAe,QAAQ,MAC1B,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC,CACrC,KAAK,MAAM,EAAE,YAAY;GAE5B,OAAO,YAAY,UAAU,MAAM;GACnC,OAAO,YAAY,QAAQ,aAAa,IAAI;GAC5C,OAAO,YAAY,WAAW,YAAY;GAC1C,OAAO,YAAY,eAAe,gBAAgB;GAClD,OAAO,YAAY,WAAW,YAAY;GAC1C,OAAO,YAAY,gBAAgB,QAAQ,UAAU,QAAQ,cAAc,QAAQ,OAAO;EAC5F;EAEA,MAAM,SAAS,mBAAmB,OAAO;EAEzC,OAAO,QAAQ,MAAM;EAGrB,IAAI,QAAQ,UAAU,QAAQ,gBAAgB,KAAK,QAAQ,YAAY,GACrE,OAAO,KAAK,wBAAwB;CAExC,SAAS,OAAO;EACd,IAAI,iBAAiB,mBAAmB;GAEtC,MAAM,WACJ,MAAM,eAAe,OAAO,MAAM,eAAe,MAC7C,uHACA;GACN,MAAM,IAAI,SAAS,qBAAqB,MAAM,QAAQ,GAAG,YAAY,WAAW,YAAY;EAC9F;EACA,MAAM;CACR;AACF;;;;;;;;;;;;ACrBA,IAAa,iBAAb,MAA4B;CAC1B;CACA;CACA;CACA,0BAA2B,IAAI,IAAY;CAC3C;CACA;CACA,SAAiB;CAEjB,YAAY,EAAE,KAAK,SAAS,aAAA,OAAgE;EAC1F,KAAK,MAAM;EACX,KAAK,UAAU;EACf,KAAK,aAAa;CACpB;CAEA,OAAc,EAAE,QAAgC;EAC9C,IAAI,KAAK,QACP;EAEF,KAAK,QAAQ,IAAI,IAAI;EACrB,KAAK,SAAS;CAChB;;;;;CAMA,MAAa,QAAuB;EAClC,KAAK,SAAS;EACd,KAAK,WAAW;EAChB,KAAK,QAAQ,MAAM;EACnB,MAAM,KAAK;CACb;CAEA,aAA2B;EACzB,IAAI,KAAK,UAAU,KAAA,GAAW;GAC5B,aAAa,KAAK,KAAK;GACvB,KAAK,QAAQ,KAAA;EACf;CACF;CAEA,WAAyB;EACvB,KAAK,WAAW;EAChB,KAAK,QAAQ,iBAAiB;GAC5B,KAAK,QAAQ,KAAA;GACb,KAAU,MAAM;EAClB,GAAG,KAAK,UAAU;CACpB;CAEA,MAAc,QAAuB;EAGnC,IAAI,KAAK,UAAU,KAAK,YAAY,KAAA,KAAa,KAAK,QAAQ,SAAS,GACrE;EAGF,MAAM,WAAW,CAAC,GAAG,KAAK,OAAO;EACjC,KAAK,QAAQ,MAAM;EAEnB,MAAM,WAAW,YAAY;GAC3B,IAAI;IACF,MAAM,KAAK,IAAI,EAAE,SAAS,CAAC;GAC7B,SAAS,OAAO;IACd,KAAK,QAAQ;KAAE;KAAO;IAAS,CAAC;GAClC;EACF,EAAA,CAAG;EACH,KAAK,UAAU;EACf,MAAM;EACN,KAAK,UAAU,KAAA;EAEf,IAAI,CAAC,KAAK,UAAU,KAAK,QAAQ,OAAO,GACtC,KAAK,SAAS;CAElB;AACF;;;;;;;;;;;;;AAoCA,SAAS,qBAAqB,EAC5B,QACA,UACA,SACA,mBAMc;CACd,IAAI;CACJ,IAAI;CACJ,IAAI,SAAS;CAEb,MAAM,eAAqB;EACzB,MAAM,UAAUC,MACd,OAAO,WACP;GAAE,WAAW,OAAO;GAAW,YAAY;EAAK,IAC/C,YAAY,aAAa;GAGxB,IAAI,aAAa,QAAQ,aAAa,KAAA,GAAW;IAC/C,SAAS,EAAE,MAAM,OAAO,UAAU,CAAC;IACnC,oBAAoB;IACpB;GACF;GACA,MAAM,eAAe,SAAS,SAAS;GACvC,IAAI,OAAO,WAAW,CAAC,OAAO,QAAQ,YAAY,GAAG;IAKnD,oBAAoB;IACpB;GACF;GACA,SAAS,EAAE,MAAM,KAAK,OAAO,WAAW,YAAY,EAAE,CAAC;GACvD,oBAAoB;EACtB,CACF;EACA,QAAQ,GAAG,UAAU,UAAU;GAC7B,QAAQ;IAAE;IAAO,WAAW,OAAO;GAAU,CAAC;GAC9C,oBAAoB;EACtB,CAAC;EACD,UAAU;CACZ;CAEA,MAAM,sBAA4B;EAChC,IAAI,UAAU,eAAe,KAAA,GAC3B;EAEF,aAAa,kBAAkB;GAC7B,IAAI,UAAU,CAAC,WAAW,OAAO,SAAS,GACxC;GAEF,cAAc,UAAU;GACxB,aAAa,KAAA;GACb,IAAI;IACF,OAAO;GACT,SAAS,OAAO;IAEd,QAAQ;KAAE;KAAO,WAAW,OAAO;IAAU,CAAC;IAC9C,cAAc;IACd;GACF;GAEA,SAAS,EAAE,MAAM,OAAO,UAAU,CAAC;EACrC,GAAG,eAAe;CACpB;CAEA,MAAM,4BAAkC;EACtC,IAAI,UAAU,YAAY,KAAA,KAAa,WAAW,OAAO,SAAS,GAChE;EAEF,QAAQ,MAAM;EACd,UAAU,KAAA;EACV,cAAc;CAChB;CAEA,OAAO;CAEP,OAAO,EACL,aAAa;EACX,SAAS;EACT,IAAI,eAAe,KAAA,GAAW;GAC5B,cAAc,UAAU;GACxB,aAAa,KAAA;EACf;EACA,SAAS,MAAM;EACf,UAAU,KAAA;CACZ,EACF;AACF;;;;;;AAOA,SAAgB,aAAa,EAC3B,SACA,UACA,SACA,kBAAA,OAMc;CACd,MAAM,UAAyB,CAAC;CAEhC,MAAM,iBAAuB;EAC3B,KAAK,MAAM,UAAU,SACnB,OAAO,MAAM;CAEjB;CAEA,IAAI;EACF,KAAK,MAAM,UAAU,SACnB,QAAQ,KAAK,qBAAqB;GAAE;GAAQ;GAAU;GAAS;EAAgB,CAAC,CAAC;CAErF,SAAS,OAAO;EACd,SAAS;EACT,MAAM;CACR;CAEA,OAAO,EAAE,OAAO,SAAS;AAC3B;;;;;;;;;AAUA,SAAgB,kBAAkB,EAChC,WACA,kBAIgB;CAChB,MAAM,kBAAkB,qBAAqB,EAAE,eAAe,CAAC;CAE/D,OAAO,CACL;EAAE,WAAW,KAAK,WAAW,0BAA0B;EAAG,WAAW;CAAK,GAC1E;EACE,WAAW,QAAQ,cAAc;EACjC,WAAW;EACX,UAAU,iBAAiB,gBAAgB,IAAI,KAAK,QAAQ,cAAc,GAAG,YAAY,CAAC;CAC5F,CACF;AACF;;;;;;AAOA,SAAgB,qBAAqB,EAAE,kBAA2D;CAChG,uBAAO,IAAI,IAAI,CACb,gBACA,KAAK,QAAQ,cAAc,GAAG,wCAAwC,CACxE,CAAC;AACH;;;;;AAMA,SAAgB,mBAAmB,EACjC,UACA,SACA,MAAM,KAKG;CACT,MAAM,YAAY,SAAS,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,YAAY,SAAS,SAAS,OAAO,KAAK,OAAO;CAC/F,MAAM,YAAY,SAAS,SAAS,UAAU;CAC9C,OAAO,YAAY,IAAI,GAAG,UAAU,KAAK,IAAI,EAAE,KAAK,UAAU,UAAU,UAAU,KAAK,IAAI;AAC7F;;;;;;ACxTA,SAAS,iBACP,QACA,QAOM;CACN,MAAM,EAAE,OAAO,OAAO,aAAa,WAAW,eAAe;CAC7D,IAAI,QAAQ,GAAG;EACb,IAAI,WACF,OAAO,KAAK,GAAG,WAAW,eAAe,MAAM,GAAG,aAAa;OAE/D,OAAO,QAAQ,WAAW,MAAM,GAAG,aAAa;EAElD,KAAK,MAAM,KAAK,OACd,OAAO,KAAK,OAAO,GAAG;CAE1B;AACF;AAEA,MAAM,yBAAiD;CACrD,QAAQ;CACR,KAAK;CACL,UAAU;CACV,WAAW;CACX,QAAQ;CACR,OAAO;CACP,QAAQ;CACR,OAAO;AACT;AAIA,MAAM,sBAAsB;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,wBAAwB,QAAgB,UAAmC;CAClF,KAAK,MAAM,WAAW,qBACpB,IAAI,SAAS,SAAS,OAAO,GAC3B,OAAO,MAAM,uBAAuB,YAAY,EAAE;AAGxD;;;;;;AAOA,SAAS,kBAAkB,QAAkC;CAC3D,MAAM,eAAmD;EACvD;GAAE,OAAO,OAAO;GAAY,OAAO;EAAQ;EAC3C;GAAE,OAAO,OAAO;GAAa,OAAO;EAAe;EACnD;GAAE,OAAO,OAAO;GAAU,OAAO;EAAY;EAC7C;GAAE,OAAO,OAAO;GAAe,OAAO;EAAW;EACjD;GAAE,OAAO,OAAO;GAAgB,OAAO;EAAY;EACnD;GAAE,OAAO,OAAO;GAAa,OAAO;EAAS;EAC7C;GAAE,OAAO,OAAO;GAAY,OAAO;EAAQ;EAC3C;GAAE,OAAO,OAAO;GAAkB,OAAO;EAAc;EACvD;GAAE,OAAO,OAAO;GAAa,OAAO;EAAS;EAC7C;GAAE,OAAO,OAAO;GAAiB,OAAO;EAA0B;CACpE;CAEA,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,EAAE,OAAO,WAAW,cAC7B,IAAI,QAAQ,GAAG,MAAM,KAAK,GAAG,MAAM,GAAG,OAAO;CAE/C,OAAO;AACT;AAEA,eAAsB,gBAAgB,QAAgB,SAAyC;CAC7F,IAAI,QAAQ,OAAO;EACjB,MAAM,qBAAqB,QAAQ,OAAO;EAC1C;CACF;CACA,MAAM,aAAa,QAAQ,OAAO;AACpC;;;;;;;AAQA,eAAe,aACb,QACA,SACA,EAAE,mBAAgD,CAAC,GACpC;CACf,MAAM,SAAS,kBAAmB,MAAM,eAAe,QAAQ,SAAS,EAAE,OAAO,CAAC;CAElF,MAAM,QAAQ,OAAO,SAAS;CAE9B,MAAM,YAAY,OAAO,cAAc;CACvC,MAAM,aAAa,YAAY,cAAc;CAE7C,OAAO,MAAM,qBAAqB;CAElC,IAAI,CAAE,MAAM,uBAAuB,EAAE,WAAW,OAAO,aAAa,EAAE,CAAC,GACrE,MAAM,IAAI,SACR,6DACA,WAAW,sBACb;CAGF,OAAO,MAAM,iBAAiB,OAAO,eAAe,CAAC,CAAC,KAAK,IAAI,GAAG;CAElE,MAAM,WAAW,OAAO,YAAY;CAEpC,wBAAwB,QAAQ,QAAQ;CAExC,MAAM,SAAS,MAAM,SAAS;EAAE;EAAQ;CAAO,CAAC;CAEhD,MAAM,iBAAiB,oBAAoB,MAAM;CAGjD,MAAM,iBAAiB;EACrB,QAAQ;GAAE,OAAO,OAAO;GAAa,OAAO,OAAO;EAAY;EAC/D,KAAK;GAAE,OAAO,OAAO;GAAU,OAAO,OAAO;EAAS;EACtD,UAAU;GAAE,OAAO,OAAO;GAAe,OAAO,OAAO;EAAc;EACrE,WAAW;GAAE,OAAO,OAAO;GAAgB,OAAO,OAAO;EAAe;EACxE,QAAQ;GAAE,OAAO,OAAO;GAAa,OAAO,OAAO;EAAY;EAC/D,OAAO;GAAE,OAAO,OAAO;GAAY,OAAO,OAAO;EAAW;EAC5D,aAAa;GAAE,OAAO,OAAO;GAAkB,OAAO,OAAO;EAAiB;EAC9E,QAAQ;GAAE,OAAO,OAAO;GAAa,OAAO,OAAO;EAAY;EAC/D,OAAO;GAAE,OAAO,OAAO;GAAY,OAAO,OAAO;EAAW;EAC5D,YAAY;GAAE,OAAO,OAAO;GAAiB,OAAO,OAAO;EAAgB;CAC7E;CAGA,MAAM,gBAA2D;EAC/D,QAAQ,UAAU,GAAG,UAAU,IAAI,SAAS;EAC5C,SAAS,UAAU,GAAG,UAAU,IAAI,gBAAgB;EACpD,MAAM,UAAU,GAAG,UAAU,IAAI,aAAa;EAC9C,WAAW,UAAU,GAAG,UAAU,IAAI,YAAY;EAClD,YAAY,UAAU,GAAG,UAAU,IAAI,aAAa;EACpD,SAAS,UAAU,GAAG,UAAU,IAAI,UAAU;EAC9C,QAAQ,UAAU,GAAG,UAAU,IAAI,eAAe;EAClD,cAAc,UAAU,GAAG,UAAU,IAAI,qBAAqB;EAC9D,SAAS,UAAU,GAAG,UAAU,IAAI,UAAU;EAC9C,aAAa,UAAU,GAAG,UAAU,IAAI,2BAA2B;CACrE;CAEA,KAAK,MAAM,CAAC,SAAS,SAAS,OAAO,QAAQ,cAAc,GACzD,iBAAiB,QAAQ;EACvB,OAAO,KAAK;EACZ,OAAO,KAAK;EACZ,aAAa,cAAc,QAAQ,GAAG,KAAK,KAAK,KAAK;EACrD;EACA;CACF,CAAC;CAIH,IAAI,OAAO,UAAU;EACnB,OAAO,YAAY,YAAY,cAAc;EAC7C,OAAO,YAAY,cAAc,cAAc;EAC/C,OAAO,YAAY,WAAW,OAAO,OAAO;EAC5C,OAAO,YAAY,UAAU,OAAO,UAAU,CAAC,CAAC;CAClD;CAGA,IAAI,OAAO;EACT,IAAI,OAAO,SACT,MAAM,IAAI,SACR,gEACA,WAAW,iBACb;EAGF,OAAO,QAAQ,6BAA6B;EAC5C;CACF;CAEA,IAAI,mBAAmB,GAAG;EACxB,MAAM,kBAAkB,SAAS,KAAK,IAAI;EAC1C,OAAO,KAAK,+BAA+B,gBAAgB,EAAE;EAC7D;CACF;CAEA,MAAM,QAAQ,kBAAkB,MAAM;CAEtC,IAAI,WACF,OAAO,KAAK,GAAG,WAAW,eAAe,eAAe,kBAAkB,MAAM,KAAK,KAAK,EAAE,EAAE;MAE9F,OAAO,QAAQ,wBAAwB,eAAe,kBAAkB,MAAM,KAAK,KAAK,EAAE,EAAE;AAEhG;;;;;;;AAQA,SAAgB,0BAA0B,EACxC,SACA,UACA,cAKO;CACP,MAAM,YAAY;EAChB,UAAU,YAAY,KAAA;EACtB,WAAW,cAAc,KAAA;EACzB,aAAa,WAAW,KAAA;CAC1B,CAAC,CAAC,QAAQ,SAAyB,SAAS,KAAA,CAAS;CAErD,IAAI,UAAU,SAAS,GACrB,MAAM,IAAI,SACR,mCAAmC,UAAU,KAAK,IAAI,EAAE,IACxD,WAAW,iBACb;AAEJ;AAEA,eAAe,qBAAqB,QAAgB,SAAyC;CAG3F,MAAM,SAAS,MAAM,eAAe,QAAQ,SAAS,EAAE,OAAO,CAAC;CAC/D,0BAA0B;EACxB,SAAS,OAAO,SAAS;EACzB,UAAU,OAAO,UAAU;EAC3B,YAAY,OAAO;CACrB,CAAC;CAED,MAAM,YAAY,OAAO,aAAa;CAGtC,MAAM,iBAAiB,OAAO,kBAAkB;CAChD,MAAM,kBAAkB,qBAAqB,EAAE,eAAe,CAAC;CAI/D,MAAM,aAAa,QAAQ,SAAS,EAAE,gBAAgB,OAAO,CAAC;CAE9D,MAAM,UAAU,kBAAkB;EAAE;EAAW;CAAe,CAAC;CAE/D,MAAM,YAAY,IAAI,eAAe;EACnC,KAAK,OAAO,EAAE,eAAe;GAC3B,OAAO,KAAK,sBAAsB,mBAAmB;IAAE;IAAU,SAAS;GAAU,CAAC,GAAG;GACxF,IAAI,SAAS,MAAM,YAAY,gBAAgB,IAAI,OAAO,CAAC,GACzD,OAAO,KACL,+KACF;GAEF,MAAM,aAAa,QAAQ,OAAO;EACpC;EACA,UAAU,EAAE,YAAY;GACtB,OAAO,MAAM,sBAAsB,YAAY,KAAK,GAAG;GACvD,OAAO,KAAK,+BAA+B;EAC7C;CACF,CAAC;CAED,MAAM,SAAS,aAAa;EAC1B;EACA,WAAW,EAAE,WAAW;GACtB,UAAU,OAAO,EAAE,KAAK,CAAC;EAC3B;EACA,UAAU,EAAE,OAAO,gBAAgB;GACjC,OAAO,MAAM,kBAAkB,UAAU,IAAI,YAAY,KAAK,GAAG;EACnE;CACF,CAAC;CAED,OAAO,KACL,+BAA+B,QAAQ,KAAK,WAAW,OAAO,OAAO,WAAW,CAAC,CAAC,KAAK,IAAI,GAC7F;CACA,OAAO,KAAK,uBAAuB;CAEnC,MAAM,IAAI,SAAe,oBAAoB;EAC3C,MAAM,iBAAuB;GAC3B,QAAQ,IAAI,UAAU,QAAQ;GAC9B,QAAQ,IAAI,WAAW,QAAQ;GAC/B,OAAO,MAAM;GACb,UACG,MAAM,CAAC,CACP,OAAO,UAAmB;IACzB,OAAO,MAAM,uCAAuC,YAAY,KAAK,GAAG;GAC1E,CAAC,CAAC,CACD,cAAc;IACb,OAAO,KAAK,qBAAqB;IACjC,gBAAgB;GAClB,CAAC;EACL;EACA,QAAQ,KAAK,UAAU,QAAQ;EAC/B,QAAQ,KAAK,WAAW,QAAQ;CAClC,CAAC;AACH;;;AClTA,MAAM,sCAA2C,IAAI,IAAI;CACvD;CACA;CACA;AACF,CAAC;AAOD,MAAa,+BAAoD,IAAI,IACnE,iCAAiC,KAAK,SAAS,MAAM,MAAM,CAC7D;AAEA,MAAM,WAAW,SAAyB,KAAK,QAAQ,OAAO,GAAG;AAEjE,MAAM,aAAa,oBACjB,MAAM,QAAQ,eAAe,CAAC,CAAC,QAAQ,OAAO,EAAE,EAAE;AAEpD,MAAM,cAAc,iBAAqC,qBAAqC;CAE5F,OAAO,MAAM,QADE,mBAAmB,oBAAoB,MACxB,GAAG,gBAAgB,GAAG,qBAAqB,gBAAgB;AAC3F;AAEA,MAAM,mBAAmB,YAA8B;CACrD,IAAI,OAAO,YAAY,YAAY,YAAY,QAAQ,EAAE,UAAU,UAAU,OAAO;CAEpF,OADc,QAAqD,MACtD,oBAAoB;AACnC;AAMA,MAAM,mBAAmB,YACtB,QAAQ,MAAM,iBAAqC,EAAE,QAAQ,MAAM,CAAC;AAEvE,MAAM,aACJ,SACA,QACA,SACA,UACS;CACT,QAAQ,KAAK;EAAE;EAAQ;EAAS;CAAM,CAAC;AACzC;AAEA,MAAM,oBAAoB,WAAuB,YAA0C;CACzF,MAAM,UAA+B,CAAC;CACtC,KAAK,MAAM,CAAC,QAAQ,YAAY,WAAW;EACzC,IAAI,oBAAoB,IAAI,MAAM,GAAG;EACrC,IAAI,CAAC,gBAAgB,OAAO,GAAG;EAC/B,MAAM,QAAQ,gBAAgB,OAAO;EAIrC,MAAM,MAAM,MAAM;EAClB,IAAI,CAAC,OAAO,QAAQ,KAAK;EAKzB,IAAI,MAAM,kBAAkB;GAC1B,UAAU,SAAS,QAAQ,SAAS,WAAW,KAAK,MAAM,gBAAgB,CAAC;GAC3E;EACF;EACA,UAAU,SAAS,QAAQ,SAAS,UAAU,GAAG,CAAC;CACpD;CACA,OAAO;AACT;AAEA,MAAM,qBAAqB,WAAuB,YAA0C;CAC1F,MAAM,UAA+B,CAAC;CACtC,KAAK,MAAM,CAAC,QAAQ,YAAY,WAAW;EACzC,IAAI,oBAAoB,IAAI,MAAM,GAAG;EACrC,IAAI,CAAC,gBAAgB,OAAO,GAAG;EAC/B,MAAM,QAAQ,gBAAgB,OAAO;EAIrC,IAAI,CAAC,MAAM,kBAAkB;EAC7B,UAAU,SAAS,QAAQ,SAAS,WAAW,MAAM,iBAAiB,MAAM,gBAAgB,CAAC;CAC/F;CACA,OAAO;AACT;AAIA,MAAM,2BAAgD;CACpD,MAAM,UAA+B,CAAC;CACtC,MAAM,YAAY,0BAA0B,OAAO,CAAC,CAAC;CACrD,KAAK,MAAM,CAAC,QAAQ,YAAY,WAAW;EACzC,IAAI,oBAAoB,IAAI,MAAM,GAAG;EACrC,MAAM,QAAQ,gBAAgB,OAAO;EAKrC,KAAK,MAAM,QAAQ,CAAC,MAAM,MAAM,GAAI,MAAM,oBAAoB,CAAC,CAAE,GAC/D,IAAI,MACF,UACE,SACA,QACA,SACA,WAAW,KAAK,iBAAiB,KAAK,gBAAgB,CACxD;EAEJ,MAAM,aAAa,MAAM,SAAS;EAClC,IAAI,cAAc,eAAe,KAC/B,UAAU,SAAS,QAAQ,SAAS,UAAU,UAAU,CAAC;EAI3D,MAAM,sBAAsB,QAAQ;EAGpC,IAAI,oBAAoB,oBACtB,KAAK,MAAM,QAAQ,oBAAoB,mBAAmB,EAAE,QAAQ,MAAM,CAAC,GACzE,UACE,SACA,QACA,SACA,WAAW,KAAK,iBAAiB,KAAK,gBAAgB,CACxD;CAGN;CACA,OAAO;AACT;AAIA,MAAM,+BAAe,IAAI,IAAa;CAAC;CAAY;CAAU;CAAa;AAAQ,CAAC;AACnF,MAAM,gCAAgB,IAAI,IAAa;CAAC;CAAO;CAAS;CAAe;AAAQ,CAAC;AAEhF,MAAM,iCAAiC,YAA0C;CAC/E,IAAI,YAAY,SAAS,OAAO,mBAAmB;CACnD,MAAM,UAAU,0BAA0B,OAAO,CAAC,CAAC;CACnD,IAAI,aAAa,IAAI,OAAO,GAAG,OAAO,iBAAiB,SAAS,OAAO;CACvE,IAAI,cAAc,IAAI,OAAO,GAAG,OAAO,kBAAkB,SAAS,OAAO;CACzE,OAAO,CAAC;AACV;AAEA,MAAM,mBAA2C;CAC/C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAKA,MAAa,4CACX,iBAAiB,SAAS,YAAY,8BAA8B,OAAO,CAAC;AAG9E,MAAa,kCACX,oCAAoC,CAAC,CAAC,QACnC,QAAQ,CAAC,6BAA6B,IAAI,IAAI,KAAK,CACtD;;;AC3JF,MAAM,kCACJ,WACwC;CACxC,OAAO,OAAO,WAAW,WAAW,CAAC,MAAM,IAAI;AACjD;AAyHA,MAAa,2BAA6D;CACxE,GAAG;EA7GH;GACE,QAAQ;GACR,SAAS;GACT,OAAO,GAAG,0CAA0C;EACtD;EACA;GACE,QAAQ;GACR,SAAS;GACT,OAAO,GAAG,yCAAyC;EACrD;EACA;GAAE,QAAQ;GAAU,SAAS;GAAW,OAAO;EAA6B;EAC5E;GAAE,QAAQ;GAAU,SAAS;GAAW,OAAO;EAAuB;EAGtE;GAAE,QAAQ;GAAU,SAAS;GAAW,OAAO;EAAqB;EAGpE;GAAE,QAAQ;GAAc,SAAS;GAAS,OAAO,MAAM;EAAkC;EACzF;GACE,QAAQ;GACR,SAAS;GACT,OAAO,MAAM,eAAe,GAAG;EACjC;EAGA;GAAE,QAAQ;GAAc,SAAS;GAAW,OAAO,MAAM,eAAe;EAAS;EACjF;GACE,QAAQ;GACR,SAAS;GACT,OAAO,MAAM,eAAe,GAAG;EACjC;EACA;GACE,QAAQ;GACR,SAAS;GACT,OAAO,MAAM,eAAe,GAAG,6BAA6B;EAC9D;EACA;GAAE,QAAQ;GAAY,SAAS;GAAW,OAAO;EAAiC;EAClF;GAAE,QAAQ;GAAW,SAAS;GAAW,OAAO;EAAyB;EACzE;GAAE,QAAQ;GAAQ,SAAS;GAAW,OAAO;EAAiB;EAC9D;GAAE,QAAQ;GAAQ,SAAS;GAAW,OAAO;EAAkB;EAC/D;GAAE,QAAQ;GAAQ,SAAS;GAAW,OAAO;EAAmB;EAChE;GAAE,QAAQ;GAAQ,SAAS;GAAW,OAAO;EAAuB;EAIpE;GAAE,QAAQ;GAAe,SAAS;GAAS,OAAO;EAAyB;EAK3E;GAAE,QAAQ;GAAS,SAAS;GAAY,OAAO;EAAuB;EAKtE;GAAE,QAAQ;GAAS,SAAS;GAAS,OAAO;EAAsB;EAKlE;GAAE,QAAQ;GAAS,SAAS;GAAU,OAAO;EAAkB;EAK/D;GAAE,QAAQ;GAAS,SAAS;GAAa,OAAO;EAA+B;EAM/E;GAAE,QAAQ;GAAS,SAAS;GAAe,OAAO;EAA2B;EAG7E;GAAE,QAAQ;GAAW,SAAS;GAAU,OAAO;EAAqB;EAIpE;GAAE,QAAQ;GAAW,SAAS;GAAY,OAAO;EAA0B;EAC3E;GAAE,QAAQ;GAAS,SAAS;GAAU,OAAO;EAA2B;EACxE;GAAE,QAAQ;GAAc,SAAS;GAAa,OAAO;EAAsB;EAC3E;GAAE,QAAQ;GAAc,SAAS;GAAO,OAAO;EAA8B;EAC7E;GAAE,QAAQ;GAAc,SAAS;GAAS,OAAO;EAAqB;EACtE;GAAE,QAAQ;GAAc,SAAS;GAAS,OAAO;EAA4B;EAG7E;GAAE,QAAQ;GAAe,SAAS;GAAU,OAAO;EAAsC;EACzF;GAAE,QAAQ;GAAe,SAAS;GAAU,OAAO;EAAsC;EAGzF;GAAE,QAAQ;GAAO,SAAS;GAAa,OAAO;EAAe;EAG7D;GAAE,QAAQ;GAAY,SAAS;GAAU,OAAO;EAAkB;EAQlE;GACE,QAAQ;GACR,SAAS;GACT,OAAO,MAAM,aAAa,SAAS;EACrC;CAIG;CAGH,GAAG,0BAA0B;CAG7B;EAAE,QAAQ;EAAU,SAAS;EAAW,OAAO;CAAuB;AACxE;AAEA,MAAa,+BAAsD;CAIjE,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,SAAmB,CAAC;CAC1B,KAAK,MAAM,OAAO,0BAA0B;EAK1C,IAJgB,+BAA+B,IAAI,MACrB,CAAC,CAAC,OAAO,WACrC,uBAAuB,SAAS,MAAiD,CAEjE,GAAG;EACrB,IAAI,KAAK,IAAI,IAAI,KAAK,GAAG;EACzB,KAAK,IAAI,IAAI,KAAK;EAClB,OAAO,KAAK,IAAI,KAAK;CACvB;CACA,OAAO;AACT,EAAA,CAAG;AAaH,MAAM,oBACJ,QACA,oBACY;CACZ,MAAM,UAAU,+BAA+B,MAAM;CAErD,IAAI,QAAQ,SAAS,QAAQ,GAAG,OAAO;CACvC,IAAI,CAAC,mBAAmB,gBAAgB,WAAW,KAAK,gBAAgB,SAAS,GAAG,GAClF,OAAO,QAAQ,MACZ,cACC,iBAAiB,SAAS,SAAS,KACnC,CAAC,uBAAuB,SAAS,SAAoD,CACzF;CAEF,OAAO,QAAQ,MAAM,cAAc,gBAAgB,SAAS,SAAS,CAAC;AACxE;AAEA,MAAM,oCACJ,QACA,oBACwC;CACxC,MAAM,UAAU,+BAA+B,MAAM;CAErD,IAAI,QAAQ,SAAS,QAAQ,GAAG,OAAO,CAAC,QAAQ;CAChD,IAAI,CAAC,mBAAmB,gBAAgB,WAAW,KAAK,gBAAgB,SAAS,GAAG,GAClF,OAAO,QAAQ,QACZ,cACC,iBAAiB,SAAS,SAAS,KACnC,CAAC,uBAAuB,SAAS,SAAoD,CACzF;CAGF,OAAO,QAAQ,QAAQ,cAAc,gBAAgB,SAAS,SAAS,CAAC;AAC1E;AAEA,MAAM,qBACJ,SACA,aACY;CACZ,IAAI,YAAY,WAAW,OAAO;CAClC,IAAI,CAAC,UAAU,OAAO;CACtB,IAAI,SAAS,WAAW,GAAG,OAAO;CAClC,IAAI,SAAS,SAAS,GAAG,GAAG,OAAO;CACnC,OAAO,SAAS,SAAS,OAAO;AAClC;AAEA,MAAM,sBAAsB,SAAgC,WAA0B;CACpF,MAAM,eAAe,IAAI,IAAY,8BAA8B;CACnE,KAAK,MAAM,UAAU,SACnB,IAAI,CAAC,aAAa,IAAI,MAAM,GAC1B,QAAQ,KACN,mBAAmB,OAAO,oBAAoB,+BAA+B,KAAK,IAAI,GACxF;AAGN;AAEA,MAAM,uBAAuB,UAA4B,WAA0B;CACjF,MAAM,gBAAgB,IAAI,IAAY,0BAA0B;CAChE,MAAM,yBAAS,IAAI,IAAY;CAC/B,KAAK,MAAM,WAAW,UACpB,IAAI,CAAC,cAAc,IAAI,OAAO,KAAK,CAAC,OAAO,IAAI,OAAO,GAAG;EACvD,OAAO,IAAI,OAAO;EAClB,QAAQ,KACN,oBAAoB,QAAQ,qBAAqB,2BAA2B,KAAK,IAAI,GACvF;CACF;AAEJ;AAQA,MAAa,2BACX,WAC6B;CAC7B,MAAM,EAAE,SAAS,UAAU,WAAW,UAAU,CAAC;CAEjD,IAAI,WAAW,QAAQ,SAAS,GAC9B,mBAAmB,SAAS,MAAM;CAEpC,IAAI,UACF,oBAAoB,UAAU,MAAM;CAGtC,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,SAAmC,CAAC;CAE1C,KAAK,MAAM,OAAO,0BAA0B;EAC1C,IAAI,CAAC,iBAAiB,IAAI,QAAQ,OAAO,GAAG;EAC5C,MAAM,qBAAqB,iCAAiC,IAAI,QAAQ,OAAO;EAC/E,IAAI,CAAC,kBAAkB,IAAI,SAAS,QAAQ,GAAG;EAC/C,IAAI,KAAK,IAAI,IAAI,KAAK,GAAG;EACzB,KAAK,IAAI,IAAI,KAAK;EAClB,OAAO,KAAK;GACV,OAAO,IAAI;GACX,QAAQ;GACR,SAAS,IAAI;EACf,CAAC;CACH;CAEA,OAAO;AACT;;;ACzRA,MAAM,kBAAkB;AACxB,MAAM,kBAAkB;AACxB,MAAM,yBAAyB;AAE/B,MAAM,oBAAoB,SAA0B;CAClD,MAAM,UAAU,KAAK,KAAK;CAC1B,OAAO,YAAY,mBAAmB,YAAY;AACpD;AAEA,MAAM,oBAAoB,SAA0B;CAClD,OAAO,KAAK,KAAK,MAAM;AACzB;AAEA,MAAM,mBAAmB,SAA0B;CACjD,MAAM,UAAU,KAAK,KAAK;CAC1B,IAAI,YAAY,MAAM,iBAAiB,IAAI,KAAK,iBAAiB,IAAI,GACnE,OAAO;CAET,OAAO,sBAAsB,SAAS,OAAO;AAC/C;AAIA,MAAM,2BAA2B,OAAiB,UAA0B;CAC1E,KAAK,IAAI,QAAQ,OAAO,QAAQ,MAAM,QAAQ,SAAS;EACrD,MAAM,OAAO,MAAM,UAAU;EAC7B,IAAI,iBAAiB,IAAI,GACvB,OAAO;EAET,IAAI,iBAAiB,IAAI,GACvB,OAAO;CAEX;CACA,OAAO;AACT;AAKA,MAAM,2BAA2B,OAAiB,gBAAgC;CAChF,IAAI,QAAQ,cAAc;CAC1B,IAAI,wBAAwB;CAE5B,OAAO,QAAQ,MAAM,QAAQ;EAC3B,MAAM,OAAO,MAAM,UAAU;EAE7B,IAAI,KAAK,KAAK,MAAM,IAAI;GACtB;GACA;GACA,IAAI,yBAAyB,GAC3B;GAEF;EACF;EAEA,IAAI,gBAAgB,IAAI,GAAG;GACzB,wBAAwB;GACxB;GACA;EACF;EAGA;CACF;CAEA,OAAO;AACT;AAEA,MAAM,iCAAiC,YAA4B;CACjE,MAAM,QAAQ,QAAQ,MAAM,IAAI;CAChC,MAAM,gBAA0B,CAAC;CACjC,IAAI,QAAQ;CAEZ,OAAO,QAAQ,MAAM,QAAQ;EAC3B,MAAM,OAAO,MAAM,UAAU;EAE7B,IAAI,iBAAiB,IAAI,GAAG;GAC1B,MAAM,cAAc,wBAAwB,OAAO,QAAQ,CAAC;GAC5D,IAAI,gBAAgB,IAAI;IAEtB,QAAQ,cAAc;IACtB;GACF;GAEA,QAAQ,wBAAwB,OAAO,KAAK;GAC5C;EACF;EAGA,IAAI,gBAAgB,IAAI,GAAG;GACzB;GACA;EACF;EAEA,cAAc,KAAK,IAAI;EACvB;CACF;CAEA,IAAI,SAAS,cAAc,KAAK,IAAI;CAEpC,OAAO,OAAO,SAAS,MAAM,GAC3B,SAAS,OAAO,MAAM,GAAG,EAAE;CAG7B,OAAO;AACT;AAKA,MAAM,iCAAiC,YAA8B;CACnE,MAAM,QAAQ,QAAQ,MAAM,IAAI;CAChC,MAAM,UAAoB,CAAC;CAC3B,IAAI,QAAQ;CAEZ,MAAM,qBAAqB,OAAe,QAAsB;EAC9D,KAAK,MAAM,aAAa,MAAM,MAAM,OAAO,GAAG,GAAG;GAC/C,MAAM,UAAU,UAAU,KAAK;GAC/B,IAAI,YAAY,IACd,QAAQ,KAAK,OAAO;EAExB;CACF;CAEA,OAAO,QAAQ,MAAM,QAAQ;EAC3B,MAAM,OAAO,MAAM,UAAU;EAE7B,IAAI,iBAAiB,IAAI,GAAG;GAC1B,MAAM,cAAc,wBAAwB,OAAO,QAAQ,CAAC;GAC5D,IAAI,gBAAgB,IAAI;IACtB,kBAAkB,QAAQ,GAAG,WAAW;IACxC,QAAQ,cAAc;IACtB;GACF;GACA,MAAM,YAAY,wBAAwB,OAAO,KAAK;GACtD,kBAAkB,QAAQ,GAAG,SAAS;GACtC,QAAQ;GACR;EACF;EAEA,IAAI,gBAAgB,IAAI,GACtB,QAAQ,KAAK,KAAK,KAAK,CAAC;EAE1B;CACF;CAEA,OAAO;AACT;AASA,MAAM,6BAA6B,EACjC,SACA,yBAIsD;CACtD,MAAM,4BAAY,IAAI,IAAY;CAClC,MAAM,gCAAgB,IAAI,IAAY;CAEtC,KAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,sBAAsB,MAAM,OAAO,QACtC,WAAiC,WAAW,QAC/C;EACA,MAAM,+BAAe,IAAI,IAA0B;EACnD,KAAK,MAAM,UAAU,qBACnB,IAAI,MAAM,YAAY,WACpB,aAAa,IAAI,mBAAmB,MAAM,CAAC;OAE3C,aAAa,IAAI,mBAAmB,QAAQ,MAAM,OAAO,CAAC;EAI9D,IAAI,aAAa,IAAI,eAAe,GAClC,cAAc,IAAI,MAAM,KAAK;EAE/B,IAAI,aAAa,SAAS,KAAK,aAAa,IAAI,WAAW,GACzD,UAAU,IAAI,MAAM,KAAK;CAE7B;CAEA,OAAO;EACL,WAAW,CAAC,GAAG,SAAS;EACxB,eAAe,CAAC,GAAG,aAAa;CAClC;AACF;AAEA,MAAa,mBAAmB,OAC9B,QACA,YACkB;CAClB,MAAM,gBAAgB,KAAK,QAAQ,IAAI,GAAG,YAAY;CACtD,MAAM,oBAAoB,KAAK,QAAQ,IAAI,GAAG,gBAAgB;CAC9D,MAAM,SAAS,MAAM,eAAe,QAClC;EAAE,SAAS,SAAS;EAAS,QAAQ,SAAS;CAAO,GACrD,EAAE,OAAO,CACX;CAEA,MAAM,kBAAkB,wBAAwB;EAC9C,SAAS,SAAS;EAClB,UAAU,SAAS;EACnB;CACF,CAAC;CACD,MAAM,EAAE,WAAW,kBAAkB,eAAe,yBAClD,0BAA0B;EACxB,SAAS;EACT,qBAAqB,QAAQ,YAAY;GACvC,IAAI,YAAY,KAAA,KAAa,YAAY,WACvC,OAAO,OAAO,wBAAwB,MAAM;GAE9C,OAAO,OAAO,wBAAwB,QAAQ,OAAO;EACvD;CACF,CAAC;CAEH,MAAM,qBAAqB,OAAO,EAChC,UACA,cASI;EACJ,IAAI,UAAU;EACd,IAAI,MAAM,WAAW,QAAQ,GAC3B,UAAU,MAAM,gBAAgB,QAAQ;EAE1C,MAAM,iBAAiB,8BAA8B,OAAO;EAC5D,MAAM,WAAW,IAAI,IAAI,OAAO;EAChC,MAAM,iBAAiB,CACrB,GAAG,IAAI,IAAI,8BAA8B,OAAO,CAAC,CAAC,QAAQ,UAAU,CAAC,SAAS,IAAI,KAAK,CAAC,CAAC,CAC3F;EAEA,MAAM,kBAAkB,IAAI,IAC1B,QACG,MAAM,IAAI,CAAC,CACX,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAC1B,QAAQ,SAAS,SAAS,MAAM,CAAC,iBAAiB,IAAI,KAAK,CAAC,iBAAiB,IAAI,CAAC,CACvF;EACA,MAAM,wBAAwB,QAAQ,QAAQ,UAAU,gBAAgB,IAAI,KAAK,CAAC;EAClF,MAAM,eAAe,QAAQ,QAAQ,UAAU,CAAC,gBAAgB,IAAI,KAAK,CAAC;EAC1E,MAAM,gBAAgB;GAAC;GAAiB,GAAG;GAAS;EAAe,CAAC,CAAC,KAAK,IAAI;EAC9E,MAAM,aACJ,QAAQ,WAAW,IACf,eAAe,KAAK,IAClB,GAAG,eAAe,QAAQ,EAAE,MAC5B,KACF,eAAe,KAAK,IAClB,GAAG,eAAe,QAAQ,EAAE,MAAM,cAAc,MAChD,GAAG,cAAc;EAEzB,IAAI,YAAY,YACd,OAAO;GAAE,SAAS;GAAO;GAAuB,cAAc,CAAC;GAAG,gBAAgB,CAAC;EAAE;EAEvF,MAAM,iBAAiB,UAAU,UAAU;EAC3C,OAAO;GAAE,SAAS;GAAM;GAAuB;GAAc;EAAe;CAC9E;CAEA,MAAM,kBAAkB,MAAM,mBAAmB;EAC/C,UAAU;EACV,SAAS;CACX,CAAC;CACD,MAAM,sBAAsB,MAAM,mBAAmB;EACnD,UAAU;EACV,SAAS;CACX,CAAC;CAED,IAAI,CAAC,gBAAgB,WAAW,CAAC,oBAAoB,SAAS;EAE5D,IAAI,OAAO,UAAU;GACnB,OAAO,YAAY,gBAAgB,CAAC,CAAC;GACrC,OAAO,YAAY,iBAAiB,aAAa;GACjD,OAAO,YAAY,qBAAqB,iBAAiB;GACzD,OAAO,YAAY,kBAAkB,CAAC,GAAG,kBAAkB,GAAG,oBAAoB,CAAC;EACrF;EACA,OAAO,QAAQ,oDAAoD;EACnE;CACF;CAGA,IAAI,OAAO,UAAU;EACnB,OAAO,YAAY,gBAAgB,CACjC,GAAG,gBAAgB,cACnB,GAAG,oBAAoB,YACzB,CAAC;EACD,OAAO,YAAY,iBAAiB,aAAa;EACjD,OAAO,YAAY,qBAAqB,iBAAiB;EACzD,OAAO,YAAY,kBAAkB,CACnC,GAAG,gBAAgB,uBACnB,GAAG,oBAAoB,qBACzB,CAAC;EACD,OAAO,YAAY,kBAAkB,gBAAgB,cAAc;CACrE;CAEA,IAAI,gBAAgB,eAAe,SAAS,GAAG;EAC7C,OAAO,KACL,4HACF;EACA,KAAK,MAAM,SAAS,gBAAgB,gBAClC,OAAO,KAAK,KAAK,OAAO;EAE1B,OAAO,KACL,yFACF;CACF;CAEA,IAAI,gBAAgB,SAClB,OAAO,QAAQ,2CAA2C;MAE1D,OAAO,QAAQ,kCAAkC;CAEnD,KAAK,MAAM,SAAS,kBAClB,OAAO,KAAK,KAAK,OAAO;CAE1B,IAAI,qBAAqB,SAAS,GAAG;EACnC,IAAI,oBAAoB,SACtB,OAAO,QAAQ,+CAA+C;OAE9D,OAAO,QAAQ,sCAAsC;EAEvD,KAAK,MAAM,SAAS,sBAClB,OAAO,KAAK,KAAK,OAAO;CAE5B;CAEA,OAAO,KAAK,EAAE;CACd,OAAO,KACL,iHACF;CACA,OAAO,KAAK,4DAA4D;CACxE,OAAO,KAAK,sBAAsB;CAClC,OAAO,KAAK,0BAA0B;CACtC,OAAO,KAAK,uBAAuB;CACnC,OAAO,KAAK,wEAAwE;AACtF;;;ACvVA,eAAsB,cAAc,QAAgB,SAAuC;CACzF,IAAI,CAAC,QAAQ,SACX,MAAM,IAAI,SAAS,+BAA+B,WAAW,aAAa;CAK5E,IAAI,CAAC,MAAM,QAAQ,QAAQ,OAAO,GAChC,MAAM,IAAI,SACR,8DACA,WAAW,aACb;CAGF,IAAI,QAAQ,QAAQ,SAAS,GAC3B,MAAM,IAAI,SAAS,2CAA2C,WAAW,aAAa;CAGxF,MAAM,SAAS,MAAM,eAAe,QAAQ,SAAS,EAAE,OAAO,CAAC;CAE/D,MAAM,OAAO,OAAO,WAAW,CAAC,CAAC;CAEjC,OAAO,MAAM,wBAAwB,KAAK,IAAI;CAE9C,MAAM,SAAS,MAAM,eAAe;EAAE;EAAQ;EAAM;CAAO,CAAC;CAE5D,MAAM,gBAAgB,oBAAoB,MAAM;CAEhD,IAAI,kBAAkB,GAAG;EACvB,MAAM,kBAAkB,OAAO,YAAY,CAAC,CAAC,KAAK,IAAI;EACtD,OAAO,KAAK,2CAA2C,iBAAiB;EACxE;CACF;CAGA,IAAI,OAAO,UAAU;EACnB,OAAO,YAAY,QAAQ,IAAI;EAC/B,OAAO,YAAY,YAAY;GAC7B,OAAO,EAAE,OAAO,OAAO,WAAW;GAClC,QAAQ,EAAE,OAAO,OAAO,YAAY;GACpC,KAAK,EAAE,OAAO,OAAO,SAAS;GAC9B,UAAU,EAAE,OAAO,OAAO,cAAc;GACxC,WAAW,EAAE,OAAO,OAAO,eAAe;GAC1C,QAAQ,EAAE,OAAO,OAAO,YAAY;GACpC,OAAO,EAAE,OAAO,OAAO,WAAW;GAClC,aAAa,EAAE,OAAO,OAAO,iBAAiB;GAC9C,QAAQ,EAAE,OAAO,OAAO,YAAY;EACtC,CAAC;EACD,OAAO,YAAY,cAAc,aAAa;CAChD;CAEA,MAAM,QAAQ,CAAC;CACf,IAAI,OAAO,aAAa,GAAG,MAAM,KAAK,GAAG,OAAO,WAAW,OAAO;CAClE,IAAI,OAAO,cAAc,GAAG,MAAM,KAAK,GAAG,OAAO,YAAY,cAAc;CAC3E,IAAI,OAAO,WAAW,GAAG,MAAM,KAAK,GAAG,OAAO,SAAS,WAAW;CAClE,IAAI,OAAO,gBAAgB,GAAG,MAAM,KAAK,GAAG,OAAO,cAAc,UAAU;CAC3E,IAAI,OAAO,iBAAiB,GAAG,MAAM,KAAK,GAAG,OAAO,eAAe,WAAW;CAC9E,IAAI,OAAO,cAAc,GAAG,MAAM,KAAK,GAAG,OAAO,YAAY,QAAQ;CACrE,IAAI,OAAO,aAAa,GAAG,MAAM,KAAK,GAAG,OAAO,WAAW,OAAO;CAClE,IAAI,OAAO,mBAAmB,GAAG,MAAM,KAAK,GAAG,OAAO,iBAAiB,aAAa;CACpF,IAAI,OAAO,cAAc,GAAG,MAAM,KAAK,GAAG,OAAO,YAAY,QAAQ;CAErE,OAAO,QAAQ,YAAY,cAAc,kBAAkB,MAAM,KAAK,KAAK,EAAE,EAAE;AACjF;;;;;;;ACvDA,eAAsB,OAA4B;CAChD,MAAM,cAAc,MAAM,kBAAkB;CAG5C,OAAO;EACL,YAAA,MAHuB,iBAAiB;EAIxC;CACF;AACF;AAEA,eAAe,mBAA4C;CACzD,MAAM,OAAO;CAEb,IAAI,MAAM,WAAW,IAAI,GACvB,OAAO;EAAE,SAAS;EAAO;CAAK;CAGhC,MAAM,iBACJ,MACA,KAAK,UACH;EACE,SAAS;EACT,SAAS;GAAC;GAAY;GAAc;EAAU;EAC9C,UAAU;GAAC;GAAS;GAAO;GAAa;GAAU;GAAS;EAAa;EACxE,aAAa,CAAC,GAAG;EACjB,QAAQ;EACR,SAAS;EACT,QAAQ;EACR,QAAQ;EACR,kBAAkB;EAClB,mBAAmB;EACnB,gBAAgB;EAChB,sBAAsB;CACxB,GACA,MACA,CACF,CACF;CAEA,OAAO;EAAE,SAAS;EAAM;CAAK;AAC/B;AAEA,eAAe,oBAA+C;CAC5D,MAAM,UAAU;EACd,sBAAsB;GAAE,SAAS;GAAQ,MAAM;EAAW,CAAC;EAC3D,sBAAsB,EAAE,SAAS,MAAM,CAAC;EACxC,sBAAsB;GAAE,SAAS;GAAY,MAAM;EAAU,CAAC;EAC9D,sBAAsB;GAAE,SAAS;GAAS,MAAM;EAAkB,CAAC;EACnE,sBAAsB,EAAE,SAAS,QAAQ,CAAC;EAC1C,sBAAsB,EAAE,SAAS,cAAc,CAAC;CAClD;CAEA,MAAM,UAA4B,CAAC;CACnC,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,UAAU,QAAQ,OAAO,gBAAgB,CAAC;EAChD,QAAQ,KACN,MAAM,iBAAiB;GACrB,MAAM,OAAO;GACb,gBAAgB,OAAO;GACvB,SAAS,OAAO;EAClB,CAAC,CACH;CACF;CACA,OAAO;AACT;AAEA,eAAe,iBAAiB,EAC9B,MACA,gBACA,WAK0B;CAC1B,KAAK,MAAM,iBAAiB,gBAC1B,IAAI,MAAM,WAAW,aAAa,GAChC,OAAO;EAAE,SAAS;EAAO,MAAM;CAAc;CAIjD,MAAM,iBAAiB,MAAM,OAAO;CACpC,OAAO;EAAE,SAAS;EAAM;CAAK;AAC/B;;;AChGA,eAAsB,YAAY,QAA+B;CAC/D,OAAO,MAAM,0BAA0B;CAEvC,MAAM,UAAU,0BAA0B;CAE1C,MAAM,SAAS,MAAM,KAAK;CAG1B,MAAM,eAAyB,CAAC;CAChC,MAAM,eAAyB,CAAC;CAEhC,KAAK,MAAM,QAAQ,OAAO,aACxB,IAAI,KAAK,SAAS;EAChB,aAAa,KAAK,KAAK,IAAI;EAC3B,OAAO,QAAQ,WAAW,KAAK,MAAM;CACvC,OAAO;EACL,aAAa,KAAK,KAAK,IAAI;EAC3B,OAAO,KAAK,WAAW,KAAK,KAAK,kBAAkB;CACrD;CAIF,IAAI,OAAO,WAAW,SAAS;EAC7B,aAAa,KAAK,OAAO,WAAW,IAAI;EACxC,OAAO,QAAQ,WAAW,OAAO,WAAW,MAAM;CACpD,OAAO;EACL,aAAa,KAAK,OAAO,WAAW,IAAI;EACxC,OAAO,KAAK,WAAW,OAAO,WAAW,KAAK,kBAAkB;CAClE;CAGA,IAAI,OAAO,UAAU;EACnB,OAAO,YAAY,WAAW,YAAY;EAC1C,OAAO,YAAY,WAAW,YAAY;CAC5C;CAEA,OAAO,QAAQ,oCAAoC;CACnD,OAAO,KAAK,aAAa;CACzB,OAAO,KACL,WAAW,2BAA2B,YAAY,2BAA2B,YAAYC,kBAAgB,IAAI,gCAAgC,IAAI,kCAAkC,OAAO,yCAC5L;CACA,OAAO,KAAK,0DAA0D;AACxE;;;;;;;;;ACvCA,MAAM,yBAAyB;;;;;;;AAS/B,MAAaC,gCAA8B;;;;;;;;AAS3C,MAAM,0BAA0B,EAAE,YAAY;CAC5C,UAAU,EAAE,OAAO;CACnB,iBAAiB,SACf,EACG,OAAO,CAAC,CACR,MAAM,QAAQ,MAAM,iBAAiB,KAAK,CAAC,GAAG,2CAA2C,CAAC,CAC/F;CACA,cAAc,SAAS,EAAE,OAAO,CAAC;CACjC,SAAS,SAAS,EAAE,OAAO,CAAC;CAC5B,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,YAAY,CAAC;CAClC,aAAa,SAAS,EAAE,OAAO,CAAC;CAChC,cAAc,EAAE,OAAO;CAOvB,cAAc,SAAS,EAAE,OAAO,CAAC;CACjC,QAAQ,SAAS,EAAE,QAAQ,CAAC;CAC5B,gBAAgB,EAAE,MAAM,EAAE,OAAO,CAAC;CAClC,QAAQ,SAAS,EAAE,OAAO,CAAC;CAC3B,YAAY,SAAS,EAAE,OAAO,CAAC;CAC/B,cAAc,SAAS,EAAE,OAAO,CAAC;CACjC,YAAY,SAAS,EAAE,QAAQ,CAAC;AAClC,CAAC;AAGD,MAAM,gBAAgB,EAAE,YAAY;CAClC,kBAAkB,EAAE,QAAQ,GAAG;CAC/B,cAAc,EAAE,OAAO;CACvB,aAAa,EAAE,OAAO;CACtB,cAAc,EAAE,MAAM,uBAAuB;CAC7C,aAAa,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC;AAGD,SAAgB,eAAe,aAA6B;CAC1D,OAAO,KAAK,aAAa,sBAAsB;AACjD;;;;;;;;;;;;AAaA,SAAgB,mBAAmB,QAGvB;CAEV,OAAO;EACL,GAFW,OAAO,eAAe,EAAE,GAAG,OAAO,aAAa,IAAI,CAAC;EAG/D,kBAAA;EACA,+BAAc,IAAI,KAAK,EAAA,CAAE,YAAY;EACrC,aAAa,OAAO;EACpB,cAAc,CAAC;CACjB;AACF;;;;;;;;AASA,SAAgB,aAAa,SAAiC;CAC5D,IAAI,CAAC,QAAQ,KAAK,GAChB,OAAO;CAET,IAAI;CACJ,IAAI;EACF,SAAS,SAAS,OAAO;CAC3B,QAAQ;EACN,OAAO;CACT;CACA,IAAI,CAAC,UAAU,OAAO,WAAW,UAC/B,OAAO;CAET,MAAM,SAAS,cAAc,UAAU,MAAM;CAC7C,IAAI,CAAC,OAAO,SAAS;EACnB,MAAM,SAAS,OAAO,MAAM,OACzB,KAAK,UAAU,OAAO,MAAM,KAAK,KAAK,GAAG,KAAK,SAAS,IAAI,MAAM,SAAS,CAAC,CAC3E,KAAK,IAAI;EACZ,MAAM,IAAI,MAAM,WAAW,uBAAuB,KAAK,QAAQ;CACjE;CACA,OAAO,OAAO;AAChB;AAEA,eAAsB,YAAY,aAA8C;CAC9E,MAAM,OAAO,eAAe,WAAW;CACvC,IAAI,CAAE,MAAM,WAAW,IAAI,GACzB,OAAO;CAGT,OAAO,aAAa,MADE,gBAAgB,IAAI,CACf;AAC7B;AAEA,eAAsB,aAAa,QAA+D;CAGhG,MAAM,iBAFO,eAAe,OAAO,WAET,GADV,iBAAiB,OAAO,IACL,CAAC;AACtC;AAEA,SAAgB,iBAAiB,MAAuB;CAGtD,OAAO,KAAK,MAAM;EAAE,QAAQ;EAAM,WAAW;EAAI,UAAU;CAAM,CAAC;AACpE;;;;;;;AAQA,SAAgB,sBACd,MACA,SAC+B;CAC/B,MAAM,SAAS,QAAQ,YAAY;CACnC,OAAO,KAAK,aAAa,MAAM,MAAM,EAAE,SAAS,YAAY,MAAM,MAAM;AAC1E;;;AC1JA,MAAM,yBAAyB;AA8B/B,MAAM,4BAA4B,EAAE,YAAY;CAC9C,KAAK,SAAS,EAAE,OAAO,CAAC;CACxB,QAAQ,SAAS,EAAE,OAAO,CAAC;CAC3B,MAAM,SAAS,EAAE,OAAO,CAAC;CACzB,KAAK,SAAS,EAAE,OAAO,CAAC;CACxB,OAAO,SAAS,EAAE,OAAO,CAAC;AAC5B,CAAC;AAED,MAAM,2BAA2B,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,yBAAyB,CAAC;AAEhF,MAAM,oBAAoB,EAAE,YAAY;CACtC,MAAM,SAAS,EAAE,OAAO,CAAC;CACzB,SAAS,SAAS,EAAE,OAAO,CAAC;CAC5B,cAAc,SACZ,EAAE,YAAY,EACZ,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC,EACjD,CAAC,CACH;AACF,CAAC;;;;AAWD,SAAgB,mBAAmB,aAA6B;CAC9D,OAAO,KAAK,aAAa,sBAAsB;AACjD;;;;AAKA,eAAsB,kBAAkB,aAAuC;CAC7E,OAAO,WAAW,mBAAmB,WAAW,CAAC;AACnD;;;;;AAMA,SAAgB,iBAAiB,SAA8B;CAC7D,MAAM,SAAS,SAAS,OAAO;CAC/B,IAAI,WAAW,KAAA,KAAa,WAAW,MACrC,OAAO,EAAE,cAAc,CAAC,EAAE;CAE5B,MAAM,SAAS,kBAAkB,UAAU,MAAM;CACjD,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,MAAM,oBAAoB,OAAO,MAAM,SAAS;CAE5D,MAAM,MAAM,OAAO;CAEnB,MAAM,gBADU,IAAI,cAAc,OAAO,CAAC,EAAA,CACI,KAAK,OAAO,UACxD,oBAAoB,OAAO,KAAK,CAClC;CACA,OAAO;EACL,MAAM,IAAI;EACV,SAAS,IAAI;EACb;CACF;AACF;;;;AAKA,eAAsB,gBAAgB,aAA2C;CAG/E,OAAO,iBAAiB,MADF,gBADT,mBAAmB,WACS,CAAC,CACX;AACjC;AAEA,SAAS,oBACP,OACA,OACe;CACf,IAAI,OAAO,UAAU,UACnB,OAAO,0BAA0B,OAAO,KAAK;CAE/C,MAAM,SAAS,MAAM,OAAO,MAAM;CAClC,IAAI,CAAC,QACH,MAAM,IAAI,MACR,uBAAuB,QAAQ,EAAE,kDAAkD,KAAK,UAAU,KAAK,EAAE,EAC3G;CAEF,MAAM,YAAY,oBAAoB,MAAM;CAC5C,IAAI,CAAC,WACH,MAAM,IAAI,MACR,uBAAuB,QAAQ,EAAE,yBAAyB,OAAO,8JACnE;CAEF,IAAI,MAAM,SAAS,KAAA,GACjB,gBAAgB,MAAM,MAAM,KAAK;CAEnC,OAAO;EACL,QAAQ,UAAU;EAClB,OAAO,UAAU;EACjB,MAAM,UAAU;EAChB,KAAK,MAAM;EACX,MAAM,MAAM;EACZ,OAAO,MAAM;CACf;AACF;;;;;AAMA,SAAS,gBAAgB,SAAiB,OAAqB;CAC7D,IAAI,YAAY,MAAM,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW,IAAI,GACtE,MAAM,IAAI,MACR,uBAAuB,QAAQ,EAAE,gFAAgF,KAAK,UAAU,OAAO,EAAE,EAC3I;CAGF,IADiB,QAAQ,MAAM,OACpB,CAAC,CAAC,SAAS,IAAI,GACxB,MAAM,IAAI,MACR,uBAAuB,QAAQ,EAAE,qDAAqD,KAAK,UAAU,OAAO,EAAE,EAChH;AAEJ;AAEA,SAAS,0BAA0B,OAAe,OAA8B;CAC9E,MAAM,UAAU,MAAM,KAAK;CAC3B,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,uBAAuB,QAAQ,EAAE,oCAAoC;CAEvF,2BAA2B,SAAS,KAAK;CAEzC,IAAI,QAAQ,WAAW,UAAU,GAAG;EAClC,MAAM,CAAC,SAAS,WAAW,aAAa,SAAS,GAAG;EACpD,MAAM,SAAS,oBAAoB,OAAO;EAC1C,IAAI,CAAC,QACH,MAAM,IAAI,MACR,uBAAuB,QAAQ,EAAE,qBAAqB,QAAQ,+FAChE;EAEF,OAAO;GACL,QAAQ,OAAO;GACf,OAAO,OAAO;GACd,MAAM,OAAO;GACb,KAAK,WAAW,KAAA;EAClB;CACF;CAEA,MAAM,CAAC,WAAW,WAAW,aAAa,SAAS,GAAG;CACtD,MAAM,aAAa,UAAU,QAAQ,GAAG;CACxC,IAAI,eAAe,MAAM,eAAe,KAAK,eAAe,UAAU,SAAS,GAC7E,MAAM,IAAI,MACR,uBAAuB,QAAQ,EAAE,eAAe,MAAM,0CACxD;CAEF,IAAI,UAAU,SAAS,KAAK,aAAa,CAAC,GACxC,MAAM,IAAI,MACR,uBAAuB,QAAQ,EAAE,2CAA2C,MAAM,yEACpF;CAGF,MAAM,QAAQ,UAAU,UAAU,GAAG,UAAU,CAAC,CAAC,YAAY;CAC7D,MAAM,OAAO,UAAU,UAAU,aAAa,CAAC,CAAC,CAAC,YAAY;CAC7D,OAAO;EACL,QAAQ,sBAAsB,MAAM,GAAG,KAAK;EAC5C;EACA;EACA,KAAK,WAAW,KAAA;CAClB;AACF;AAEA,SAAS,2BAA2B,OAAe,OAAqB;CACtE,IAAI,MAAM,WAAW,IAAI,KAAK,MAAM,WAAW,KAAK,KAAK,MAAM,WAAW,GAAG,GAC3E,MAAM,IAAI,MACR,uBAAuB,QAAQ,EAAE,8BAA8B,MAAM,sCACvE;CAEF,IAAI,MAAM,WAAW,MAAM,KAAK,MAAM,WAAW,QAAQ,GACvD,MAAM,IAAI,MACR,uBAAuB,QAAQ,EAAE,2BAA2B,MAAM,mDACpE;CAEF,IAAI,MAAM,SAAS,cAAc,GAC/B,MAAM,IAAI,MACR,uBAAuB,QAAQ,EAAE,mCAAmC,MAAM,0BAC5E;AAEJ;AAEA,SAAS,oBAAoB,KAAqE;CAChG,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,GAAG;CACtB,QAAQ;EACN,OAAO;CACT;CACA,MAAM,OAAO,OAAO,SAAS,YAAY;CACzC,IAAI,SAAS,gBAAgB,SAAS,kBACpC,OAAO;CAET,MAAM,WAAW,OAAO,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO;CAC1D,IAAI,SAAS,SAAS,GACpB,OAAO;CAET,MAAM,WAAW,SAAS;CAC1B,MAAM,UAAU,SAAS;CACzB,IAAI,CAAC,YAAY,CAAC,SAChB,OAAO;CAKT,MAAM,QAAQ,SAAS,YAAY;CACnC,MAAM,OAAO,QAAQ,QAAQ,UAAU,EAAE,CAAC,CAAC,YAAY;CACvD,OAAO;EACL,QAAQ,sBAAsB,MAAM,GAAG,KAAK;EAC5C;EACA;CACF;AACF;AAEA,SAAS,aAAa,OAAe,WAAiD;CACpF,MAAM,MAAM,MAAM,QAAQ,SAAS;CACnC,IAAI,QAAQ,IAAI,OAAO,CAAC,OAAO,KAAA,CAAS;CACxC,OAAO,CAAC,MAAM,UAAU,GAAG,GAAG,GAAG,MAAM,UAAU,MAAM,CAAC,CAAC;AAC3D;;;;AC7OA,MAAM,8BAA8B;;;;;;;;AASpC,MAAM,iBAAuF,CAC3F;CACE,WAAW;CACX,WAAW;CACX,aAAa;AACf,GACA;CACE,WAAW;CACX,WAAW;CACX,aAAa;AACf,CACF;;;;;;AAsBA,eAAsB,WAAW,QAIH;CAC5B,MAAM,EAAE,aAAa,UAAU,CAAC,GAAG,WAAW;CAE9C,MAAM,WAAW,MAAM,gBAAgB,WAAW;CAClD,IAAI,SAAS,aAAa,WAAW,GAAG;EACtC,OAAO,KAAK,8DAA8D;EAC1E,OAAO;GAAE,uBAAuB;GAAG,mBAAmB;GAAG,uBAAuB;EAAE;CACpF;CAEA,MAAM,eAAe,MAAM,YAAY,WAAW;CAClD,IAAI,QAAQ,QACV,+BAA+B;EAAE;EAAc,cAAc,SAAS;CAAa,CAAC;CAItF,MAAM,SAAS,IAAI,aAAa,EAAE,OADpB,aAAa,aAAa,QAAQ,KACV,EAAE,CAAC;CACzC,MAAM,YAAY,IAAI,UAAA,EAAiC;CAEvD,MAAM,UAAmB,mBAAmB;EAC1C,YAAY,cAAc,eAAe;EACzC;CACF,CAAC;CAeD,MAAM,SAAS,QAAQ,UAAU;CAEjC,MAAM,SAAS,OAAO,QAA2C;EAC/D,MAAM,YAAY,MAAM,kBAAkB;GACxC;GACA;GACA;GACA;GACA;GACA;GACA,QAAQ,QAAQ,UAAU;GAC1B;EACF,CAAC;EACD,OAAO;GACL,QAAQ;GACR,WAAW,UAAU;GACrB,eAAe,UAAU,cAAc;EACzC;CACF;CAEA,MAAM,UAAuB,SACzB,MAAM,QAAQ,IAAI,SAAS,aAAa,IAAI,MAAM,CAAC,IACnD,MAAM,QAAQ,IACZ,SAAS,aAAa,IAAI,OAAO,QAA4B;EAC3D,IAAI;GACF,OAAO,MAAM,OAAO,GAAG;EACzB,SAAS,OAAO;GACd,OAAO,MAAM,qCAAqC,IAAI,OAAO,KAAK,YAAY,KAAK,GAAG;GACtF,IAAI,iBAAiB,mBACnB,mBAAmB;IAAE;IAAO;GAAO,CAAC;GAUtC,OAAO;IAAE,QAAQ;IAAU,UAHV,eACb,sBAAsB,cAAc,iBAAiB,GAAG,CAAC,IACzD,KAAA;GACgC;EACtC;CACF,CAAC,CACH;CAEJ,IAAI,gBAAgB;CACpB,IAAI,cAAc;CAGlB,KAAK,MAAM,UAAU,SACnB,IAAI,OAAO,WAAW,MAAM;EAC1B,QAAQ,aAAa,KAAK,OAAO,SAAS;EAC1C,iBAAiB,OAAO;CAC1B,OAAO;EACL,eAAe;EACf,IAAI,OAAO,UACT,QAAQ,aAAa,KAAK,OAAO,QAAQ;CAE7C;CAcF,IAAI,cACF,MAAM,oBAAoB;EAAE;EAAc;EAAS;EAAa;CAAO,CAAC;CAO1E,IAAI,CAAC,QAAQ;EACX,QAAQ,gCAAe,IAAI,KAAK,EAAA,CAAE,YAAY;EAC9C,MAAM,aAAa;GAAE;GAAa,MAAM;EAAQ,CAAC;EACjD,IAAI,gBAAgB,GAClB,OAAO,MAAM,iCAAiC;OAE9C,OAAO,KACL,sEAAsE,YAAY,iBACpF;CAEJ;CAEA,OAAO;EACL,uBAAuB,SAAS,aAAa;EAC7C,mBAAmB;EACnB,uBAAuB;CACzB;AACF;;;;;;;AAQA,SAAS,+BAA+B,QAGuC;CAC7E,MAAM,EAAE,cAAc,iBAAiB;CACvC,IAAI,CAAC,cACH,MAAM,IAAI,MACR,2GACF;CAEF,MAAM,UAAU,aAAa,QAC1B,QAAQ,CAAC,sBAAsB,cAAc,iBAAiB,GAAG,CAAC,CACrE;CACA,IAAI,QAAQ,SAAS,GAAG;EACtB,MAAM,QAAQ,QAAQ,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,KAAK,IAAI;EACpD,MAAM,IAAI,MACR,yEAAyE,MAAM,4DACjF;CACF;CAIA,MAAM,UAAU,aAAa,QAAQ,QAAQ;EAC3C,IAAI,IAAI,QAAQ,KAAA,GAAW,OAAO;EAClC,MAAM,SAAS,sBAAsB,cAAc,iBAAiB,GAAG,CAAC;EACxE,OAAO,QAAQ,iBAAiB,KAAA,KAAa,OAAO,iBAAiB,IAAI;CAC3E,CAAC;CACD,IAAI,QAAQ,SAAS,GAAG;EACtB,MAAM,QAAQ,QACX,KAAK,MAAM;GACV,MAAM,SAAS,sBAAsB,cAAc,iBAAiB,CAAC,CAAC;GACtE,OAAO,GAAG,EAAE,OAAO,aAAa,EAAE,IAAI,SAAS,QAAQ,aAAa;EACtE,CAAC,CAAC,CACD,KAAK,IAAI;EACZ,MAAM,IAAI,MACR,kFAAkF,MAAM,4DAC1F;CACF;AACF;;;;;;;AAQA,eAAe,oBAAoB,QAKjB;CAChB,MAAM,EAAE,cAAc,SAAS,aAAa,WAAW;CACvD,MAAM,mBAAmB,IAAI,IAAI,QAAQ,aAAa,SAAS,MAAM,EAAE,cAAc,CAAC;CACtF,MAAM,WAAqB,CAAC;CAC5B,KAAK,MAAM,QAAQ,aAAa,cAC9B,KAAK,MAAM,YAAY,KAAK,gBAC1B,IAAI,CAAC,iBAAiB,IAAI,QAAQ,GAChC,SAAS,KAAK,QAAQ;CAI5B,KAAK,MAAM,gBAAgB,UAAU;EACnC,IAAI,MAAM,WAAW,YAAY,KAAK,aAAa,MAAM,OAAO,CAAC,CAAC,SAAS,IAAI,GAAG;GAChF,OAAO,KAAK,4DAA4D,aAAa,GAAG;GACxF;EACF;EACA,IAAI;GACF,mBAAmB;IAAE;IAAc,iBAAiB;GAAY,CAAC;EACnE,QAAQ;GACN,OAAO,KAAK,2DAA2D,aAAa,GAAG;GACvF;EACF;EAIA,MAAM,WAHW,KAAK,aAAa,YAGX,CAAC;EACzB,OAAO,MAAM,2BAA2B,cAAc;CACxD;AACF;AAEA,eAAe,kBAAkB,QASsC;CACrE,MAAM,EAAE,KAAK,QAAQ,WAAW,aAAa,cAAc,QAAQ,QAAQ,WAAW;CACtF,MAAM,UAAU,iBAAiB,GAAG;CACpC,MAAM,SAAS,eAAe,sBAAsB,cAAc,OAAO,IAAI,KAAA;CAE7E,IAAI;CACJ,IAAI;CACJ,IAAI,UAAU,CAAC,UAAU,OAAO,mBAAmB,OAAO,cAAc;EACtE,cAAc,OAAO;EACrB,cAAc,OAAO;EACrB,OAAO,MAAM,2BAA2B,QAAQ,IAAI,aAAa;CACnE,OAAO;EACL,cAAc,IAAI,OAAQ,MAAM,OAAO,iBAAiB,IAAI,OAAO,IAAI,IAAI;EAC3E,cAAc,MAAM,OAAO,gBAAgB,IAAI,OAAO,IAAI,MAAM,WAAW;EAC3E,OAAO,MAAM,YAAY,QAAQ,QAAQ,YAAY,OAAO,aAAa;CAC3E;CAMA,MAAM,WAAqD,CAAC;CAC5D,KAAK,MAAM,aAAa,gBAAgB;EACtC,MAAM,aAAa,IAAI,OACnB,YAAY,MAAM,KAAK,IAAI,MAAM,UAAU,SAAS,CAAC,IACrD,UAAU;EACd,MAAM,QAAQ,MAAM,mBAAmB;GACrC;GACA;GACA,OAAO,IAAI;GACX,MAAM,IAAI;GACV,KAAK;GACL;GACA;EACF,CAAC;EACD,IAAI,MAAM,WAAW,GAAG;EAExB,MAAM,4BAA4B;GAChC;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;CACH;CAEA,SAAS,MAAM,GAAG,MAAO,EAAE,OAAO,EAAE,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,IAAI,CAAE;CACxE,MAAM,gBAAgB,SAAS,KAAK,MAAM,EAAE,IAAI;CAChD,MAAM,cAAcC,qBAAmB,QAAQ;CAE/C,+BAA+B;EAAE;EAAQ;EAAQ;EAAa;EAAS;CAAO,CAAC;CAG/E,IAAI,QACF,KAAK,MAAM,EAAE,MAAM,gBAAgB,aAAa,UAC9C,MAAM,iBAAiB,KAAK,aAAa,cAAc,GAAG,OAAO;CAIrE,MAAM,YAA+B;EACnC,UAAU;EACV,iBAAiB;EACjB,cAAc;EACd,OAAO;EACP,cAAc;EACd,cAAc;EACd,gBAAgB;CAClB;CACA,IAAI,IAAI,MACN,UAAU,eAAe,IAAI;CAG/B,OAAO,KAAK,aAAa,cAAc,OAAO,gBAAgB,QAAQ,GAAG,SAAS,WAAW,GAAG;CAEhG,OAAO;EAAE;EAAW;CAAc;AACpC;;;;;;;AAQA,eAAe,4BAA4B,QAazB;CAChB,MAAM,EACJ,KACA,QACA,WACA,aACA,WACA,YACA,OACA,aACA,SACA,QACA,UACA,WACE;CAEJ,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,KAAK,OAAA,UAAsB;GAC7B,OAAO,KACL,aAAa,KAAK,KAAK,SAAS,QAAQ,KAAK,KAAK,OAAO,OAAO,KAAA,CAAM,QAAQ,CAAC,EAAE,aAAa,gBAAgB,OAAO,KAAK,UAC5H;GACA;EACF;EACA,MAAM,iBAAiB,MAAM,SAAS,YAAY,YAAY,KAAK,IAAI,CAAC;EACxE,IAAI,CAAC,kBAAkB,eAAe,WAAW,IAAI,KAAK,MAAM,WAAW,cAAc,GAAG;GAC1F,OAAO,KAAK,aAAa,KAAK,KAAK,SAAS,QAAQ,yBAAyB,WAAW,GAAG;GAC3F;EACF;EACA,MAAM,iBAAiB,YAAY,KAAK,UAAU,WAAW,cAAc,CAAC;EAC5E,mBAAmB;GACjB,cAAc;GACd,iBAAiB;EACnB,CAAC;EACD,MAAM,UAAU,MAAM,cAAc,iBAClC,OAAO,eAAe,IAAI,OAAO,IAAI,MAAM,KAAK,MAAM,WAAW,CACnE;EAGA,MAAM,aAAa,OAAO,WAAW,SAAS,MAAM;EACpD,IAAI,aAAA,UAA4B;GAC9B,OAAO,KACL,aAAa,KAAK,KAAK,SAAS,QAAQ,aAAa,aAAa,OAAO,KAAA,CAAM,QAAQ,CAAC,EAAE,aAAa,gBAAgB,OAAO,KAAK,UACrI;GACA;EACF;EACA,SAAS,KAAK;GAAE,MAAM;GAAgB;EAAQ,CAAC;EAC/C,IAAI,CAAC,QACH,MAAM,iBAAiB,KAAK,aAAa,cAAc,GAAG,OAAO;CAErE;AACF;;;;;;;;;;;;;;;AAgBA,SAAS,+BAA+B,QAM/B;CACP,MAAM,EAAE,QAAQ,QAAQ,aAAa,SAAS,WAAW;CACzD,IAAI,UAAU,QAAQ,cACpB,IAAIC,8BAA4B,KAAK,OAAO,YAAY,GAClD;MAAA,OAAO,iBAAiB,aAC1B,MAAM,IAAI,MACR,6BAA6B,QAAQ,SAAS,OAAO,aAAa,YAAY,YAAY,iDAC5F;CAAA,OAGF,OAAO,MACL,6CAA6C,QAAQ,mBAAmB,OAAO,aAAa,+BAC9F;AAGN;;;;;;AAOA,SAASD,qBAAmB,OAAyD;CACnF,MAAM,OAAO,WAAW,QAAQ;CAChC,KAAK,MAAM,EAAE,MAAM,aAAa,OAAO;EACrC,KAAK,OAAO,IAAI;EAChB,KAAK,OAAO,IAAI;EAChB,KAAK,OAAO,OAAO;EACnB,KAAK,OAAO,IAAI;CAClB;CACA,OAAO,UAAU,KAAK,OAAO,KAAK;AACpC;AAEA,eAAe,mBAAmB,QAQH;CAC7B,MAAM,EAAE,QAAQ,WAAW,OAAO,MAAM,KAAK,YAAY,WAAW;CACpE,IAAI;EACF,OAAO,MAAM,uBAAuB;GAClC;GACA;GACA;GACA,MAAM;GACN;GACA;EACF,CAAC;CACH,SAAS,OAAO;EACd,IAAI,iBAAiB,qBAAqB,MAAM,eAAe,KAAK;GAClE,OAAO,MAAM,MAAM,WAAW,OAAO,MAAM,GAAG,KAAK,YAAY;GAC/D,OAAO,CAAC;EACV;EACA,MAAM;CACR;AACF;;;;;;;AAQA,SAAS,iBAAiB,KAA4B;CACpD,OAAO,sBAAsB,IAAI,MAAM,GAAG,IAAI;AAChD;AAEA,SAAS,SAAS,KAAqB;CACrC,OAAO,IAAI,UAAU,GAAG,CAAC;AAC3B;;;AChiBA,MAAM,oBAAoB;;;;;;;;;;;;;;;;AAiB1B,SAAgB,qBAAqB,QAK1B;CACT,MAAM,EAAE,SAAS,QAAQ,YAAY,QAAQ;CAC7C,MAAM,aAAa;EAAE;EAAQ;EAAY;CAAI;CAM7C,IAAI;CACJ,IAAI,QAAQ,WAAW,GAAG,kBAAkB,KAAK,GAC/C,eAAe;MACV,IAAI,QAAQ,WAAW,GAAG,kBAAkB,GAAG,GACpD,eAAe;MACV,IAAI,YAAY,mBACrB,eAAe;MACV;EAEL,MAAM,OAAO,KAAK,YAAY;GAAE,QAAQ;GAAM,WAAW;GAAI,UAAU;EAAM,CAAC;EAC9E,OAAO,GAAG,kBAAkB,IAAI,OAAO,kBAAkB,IAAI;CAC/D;CAGA,MAAM,YAAY,QAAQ,UAAU,YAAY;CAOhD,IAAI;CACJ,IAAI;CACJ,IAAI,UAAU,WAAW,OAAO,KAAK,UAAU,WAAW,SAAS,KAAK,cAAc,OAAO;EAC3F,SAAS;EACT,MAAM,WAAW,UAAU,WAAW,SAAS,IAAI,IAAI,cAAc,QAAQ,IAAI;EACjF,OAAO,UAAU,UAAU,QAAQ;CACrC,OAAO;EACL,MAAM,QAAQ,iBAAiB,KAAK,SAAS;EAC7C,IAAI,CAAC,OAIH,MAAM,IAAI,MAAM,qBAAqB;EAEvC,SAAS,UAAU,UAAU,GAAG,MAAM,KAAK;EAC3C,OAAO,UAAU,UAAU,MAAM,QAAQ,MAAM,EAAE,CAAC,MAAM;CAC1D;CAEA,IAAI;CACJ,IAAI;EACF,SAAS,SAAS,MAAM;CAC1B,QAAQ;EACN,MAAM,IAAI,MAAM,qBAAqB;CACvC;CAEA,IAAI,WAAW,QAAQ,WAAW,KAAA,GAAW;EAE3C,MAAM,OAAO,KAAK,YAAY;GAAE,QAAQ;GAAM,WAAW;GAAI,UAAU;EAAM,CAAC;EAC9E,OAAO,GAAG,kBAAkB,IAAI,OAAO,kBAAkB,IAAI;CAC/D;CACA,IAAI,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GACpD,MAAM,IAAI,MAAM,qBAAqB;CASvC,MAAM,OAAO,KAAK;EAHhB,GAAGE;EACH,GAAG;CAEkB,GAAG;EAAE,QAAQ;EAAM,WAAW;EAAI,UAAU;CAAM,CAAC;CAC1E,OAAO,GAAG,kBAAkB,IAAI,OAAO,kBAAkB,IAAI;AAC/D;;;;;;;;;ACnFA,MAAM,wBAAwB;;;;;;;AAS9B,MAAa,8BAA8B;AAE3C,MAAM,cAAc,EAAE,KAAK,CAAC,WAAW,MAAM,CAAC;;;;;;AAO9C,MAAM,2BAA2B,EAAE,YAAY;CAC7C,QAAQ,EAAE,OAAO;CACjB,OAAO,EAAE,OAAO;CAChB,MAAM,EAAE,OAAO;CACf,OAAO,EAAE,OAAO;CAChB,OAAO;CACP,OAAO,EAAE,OAAO;CAChB,eAAe,SAAS,EAAE,OAAO,CAAC;CAClC,cAAc,EAAE,OAAO;CACvB,iBAAiB,EACd,OAAO,CAAC,CACR,MAAM,QAAQ,MAAM,iBAAiB,KAAK,CAAC,GAAG,2CAA2C,CAAC;CAC7F,aAAa,EAAE,OAAO;CACtB,gBAAgB,EAAE,MAAM,EAAE,OAAO,CAAC;CAClC,cAAc,SAAS,EAAE,OAAO,CAAC;AACnC,CAAC;AAGD,MAAM,eAAe,EAAE,YAAY;CACjC,kBAAkB,EAAE,QAAQ,GAAG;CAC/B,cAAc,EAAE,OAAO;CACvB,eAAe,EAAE,MAAM,wBAAwB;AACjD,CAAC;AAGD,SAAgB,cAAc,aAA6B;CACzD,OAAO,KAAK,aAAa,qBAAqB;AAChD;;;;;;AAOA,SAAgB,kBAAkB,QAAmD;CAEnF,OAAO;EACL,GAFW,QAAQ,eAAe,EAAE,GAAG,OAAO,aAAa,IAAI,CAAC;EAGhE,kBAAA;EACA,+BAAc,IAAI,KAAK,EAAA,CAAE,YAAY;EACrC,eAAe,CAAC;CAClB;AACF;;;;;;;AAQA,SAAgB,YAAY,SAAgC;CAC1D,IAAI,CAAC,QAAQ,KAAK,GAChB,OAAO;CAET,IAAI;CACJ,IAAI;EACF,SAAS,SAAS,OAAO;CAC3B,QAAQ;EACN,OAAO;CACT;CACA,IAAI,CAAC,UAAU,OAAO,WAAW,UAC/B,OAAO;CAET,MAAM,SAAS,aAAa,UAAU,MAAM;CAC5C,IAAI,CAAC,OAAO,SAAS;EACnB,MAAM,SAAS,OAAO,MAAM,OACzB,KAAK,UAAU,OAAO,MAAM,KAAK,KAAK,GAAG,KAAK,SAAS,IAAI,MAAM,SAAS,CAAC,CAC3E,KAAK,IAAI;EACZ,MAAM,IAAI,MAAM,WAAW,sBAAsB,KAAK,QAAQ;CAChE;CACA,OAAO,OAAO;AAChB;AAEA,eAAsB,WAAW,aAA6C;CAC5E,MAAM,OAAO,cAAc,WAAW;CACtC,IAAI,CAAE,MAAM,WAAW,IAAI,GACzB,OAAO;CAGT,OAAO,YAAY,MADG,gBAAgB,IAAI,CAChB;AAC5B;AAEA,eAAsB,YAAY,QAA8D;CAG9F,MAAM,iBAFO,cAAc,OAAO,WAER,GADV,gBAAgB,OAAO,IACJ,CAAC;AACtC;AAEA,SAAgB,gBAAgB,MAAsB;CAGpD,OAAO,KAAK,MAAM;EAAE,QAAQ;EAAM,WAAW;EAAI,UAAU;CAAM,CAAC;AACpE;;;;;;AAOA,SAAgB,uBACd,MACA,QACgC;CAChC,MAAM,SAAS,OAAO,OAAO,YAAY;CACzC,OAAO,KAAK,cAAc,MACvB,MACC,EAAE,OAAO,YAAY,MAAM,UAC3B,EAAE,UAAU,OAAO,SACnB,EAAE,UAAU,OAAO,SACnB,EAAE,UAAU,OAAO,KACvB;AACF;;;;;;;;;ACpIA,MAAa,YAAY;CACvB;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;AAiCA,SAAgB,sBAAsB,QAAoD;CACxF,MAAM,EAAE,OAAO,UAAU;CACzB,IAAI,UAAU,WAAW;EACvB,IAAI,UAAU,eACZ,OAAO;EAGT,OAAO,KAAK,WAAW,QAAQ;CACjC;CAEA,QAAQ,OAAR;EACE,KAAK,kBACH,OAAO,KAAK,YAAY,QAAQ;EAClC,KAAK,eACH,OAAO;EACT,KAAK,UACH,OAAO,KAAK,WAAW,QAAQ;EACjC,KAAK,SACH,OAAO,KAAK,WAAW,QAAQ;EACjC,KAAK,UACH,OAAO,KAAK,WAAW,QAAQ;EACjC,KAAK,eACH,OAAO,KAAK,WAAW,eAAe,QAAQ;CAClD;AACF;;;AC5CA,MAAM,oBAAoB;AAC1B,MAAM,kBAAkB;;;;;;;;AAkDxB,eAAsB,UAAU,QAKH;CAC3B,MAAM,EAAE,aAAa,SAAS,UAAU,CAAC,GAAG,WAAW;CAEvD,IAAI,QAAQ,WAAW,GACrB,OAAO;EAAE,kBAAkB;EAAG,qBAAqB;EAAG,mBAAmB;CAAE;CAM7E,MAAM,kBAAoC,QAAQ,IAAI,eAAe;CAErE,MAAM,eAAe,MAAM,WAAW,WAAW;CACjD,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,SAAS,QAAQ,UAAU;CAEjC,IAAI,UAAU,CAAC,cACb,MAAM,IAAI,MACR,yGACF;CAGF,IAAI,UAAU,cACZ,8BAA8B;EAAE;EAAc;CAAgB,CAAC;CAIjE,MAAM,SAAS,IAAI,aAAa,EAAE,OADpB,aAAa,aAAa,QAAQ,KACV,EAAE,CAAC;CACzC,MAAM,YAAY,IAAI,UAAA,EAAiC;CAEvD,MAAM,UAAkB,kBAAkB,EAAE,aAAa,CAAC;CAE1D,MAAM,SAAS,OAAO,OAA8C;EAWlE,OAAO;GAAE,QAAQ;GAAM,eAAA,MAVK,cAAc;IACxC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;EACoC;CACvC;CAEA,MAAM,UAA0B,SAC5B,MAAM,QAAQ,IAAI,gBAAgB,IAAI,MAAM,CAAC,IAC7C,MAAM,QAAQ,IACZ,gBAAgB,IAAI,OAAO,OAA8B;EACvD,IAAI;GACF,OAAO,MAAM,OAAO,EAAE;EACxB,SAAS,OAAO;GACd,OAAO,MAAM,gCAAgC,GAAG,MAAM,OAAO,KAAK,YAAY,KAAK,GAAG;GACtF,IAAI,iBAAiB,mBACnB,mBAAmB;IAAE;IAAO;GAAO,CAAC;GAStC,OAAO;IAAE,QAAQ;IAAU,WALT,eACd,aAAa,cAAc,QACxB,MAAM,EAAE,OAAO,YAAY,MAAM,GAAG,MAAM,OAAO,YAAY,CAChE,IACA,CAAC;GACgC;EACvC;CACF,CAAC,CACH;CAEJ,IAAI,QACF,MAAM,yBAAyB,OAAO;CAGxC,MAAM,EAAE,gBAAgB,gBAAgB,uBAAuB;EAAE;EAAS;CAAQ,CAAC;CAGnF,IAAI,cACF,MAAM,mBAAmB;EAAE;EAAc;EAAS;EAAa;CAAO,CAAC;CAGzE,IAAI,CAAC,QAAQ;EACX,QAAQ,gCAAe,IAAI,KAAK,EAAA,CAAE,YAAY;EAC9C,MAAM,YAAY;GAAE;GAAa,MAAM;EAAQ,CAAC;EAChD,IAAI,gBAAgB,GAClB,OAAO,MAAM,gCAAgC;OAE7C,OAAO,KACL,qEAAqE,YAAY,oBACnF;CAEJ;CAEA,OAAO;EACL,kBAAkB,QAAQ;EAC1B,qBAAqB;EACrB,mBAAmB;CACrB;AACF;;;;;;AAOA,SAAS,gBAAgB,OAAoC;CAC3D,MAAM,SAAS,YAAY,MAAM,MAAM;CACvC,IAAI,OAAO,aAAa,UACtB,MAAM,IAAI,MACR,4CAA4C,MAAM,OAAO,0BAA0B,OAAO,SAAS,GACrG;CAMF,IAAI,MAAM,cAAc,KAAA,KAAa,MAAM,cAAc,UACvD,MAAM,IAAI,MACR,uDAAuD,MAAM,UAAU,gBAAgB,MAAM,OAAO,iDACtG;CAEF,IAAI,MAAM,SAAS,KAAA,GACjB,MAAM,IAAI,MACR,wDAAwD,MAAM,OAAO,2DACvE;CAEF,IAAI,MAAM,UAAU,KAAA,GAClB,MAAM,IAAI,MACR,yDAAyD,MAAM,OAAO,2DACxE;CAEF,IAAI,MAAM,cAAc,KAAA,GACtB,MAAM,IAAI,MACR,6DAA6D,MAAM,OAAO,2DAC5E;CAEF,MAAM,QAAQ,MAAM,SAAS;CAC7B,IAAI,CAAC,UAAU,SAAS,KAAK,GAC3B,MAAM,IAAI,MACR,6BAA6B,MAAM,gBAAgB,MAAM,OAAO,mBAAmB,UAAU,KAAK,IAAI,EAAE,EAC1G;CAEF,MAAM,QAAiB,MAAM,SAAS;CACtC,OAAO;EACL;EACA,OAAO,OAAO;EACd,MAAM,OAAO;EACb,KAAK,MAAM,OAAO,OAAO;EACzB;EACA;CACF;AACF;;;;;;;;;AAUA,SAAS,8BAA8B,QAG9B;CACP,MAAM,EAAE,cAAc,oBAAoB;CAC1C,MAAM,YAAsB,CAAC;CAC7B,KAAK,MAAM,MAAM,iBAOf,IAAI,CANW,aAAa,cAAc,MACvC,MACC,EAAE,OAAO,YAAY,MAAM,GAAG,MAAM,OAAO,YAAY,KACvD,EAAE,UAAU,GAAG,SACf,EAAE,UAAU,GAAG,KAET,GACR,UAAU,KAAK,GAAG,GAAG,MAAM,OAAO,UAAU,GAAG,MAAM,UAAU,GAAG,MAAM,EAAE;CAG9E,IAAI,UAAU,SAAS,GACrB,MAAM,IAAI,MACR,wEAAwE,UAAU,KAAK,IAAI,EAAE,2DAC/F;CAMF,MAAM,UAAoB,CAAC;CAC3B,KAAK,MAAM,MAAM,iBAAiB;EAChC,IAAI,CAAC,GAAG,KAAK;EACb,MAAM,UAAU,aAAa,cAAc,QACxC,MAAM,EAAE,OAAO,YAAY,MAAM,GAAG,MAAM,OAAO,YAAY,CAChE;EACA,KAAK,MAAM,KAAK,SACd,IAAI,EAAE,kBAAkB,KAAA,KAAa,EAAE,kBAAkB,GAAG,KAAK;GAC/D,QAAQ,KAAK,GAAG,GAAG,MAAM,OAAO,aAAa,GAAG,IAAI,SAAS,EAAE,cAAc,EAAE;GAC/E;EACF;CAEJ;CACA,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,MACR,iFAAiF,QAAQ,KAAK,IAAI,EAAE,2DACtG;AAEJ;;;;;;;;;AAUA,eAAe,yBAAyB,SAAwC;CAC9E,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,OAAO,WAAW,MAAM;EAC5B,KAAK,MAAM,QAAQ,OAAO,eACxB,KAAK,MAAM,KAAK,KAAK,UACnB,MAAM,iBAAiB,EAAE,cAAc,EAAE,OAAO;CAGtD;AACF;;;;;AAMA,SAAS,uBAAuB,QAG9B;CACA,MAAM,EAAE,SAAS,YAAY;CAC7B,IAAI,iBAAiB;CACrB,IAAI,cAAc;CAClB,KAAK,MAAM,UAAU,SACnB,IAAI,OAAO,WAAW,MAAM;EAC1B,KAAK,MAAM,QAAQ,OAAO,eACxB,QAAQ,cAAc,KAAK,KAAK,YAAY;EAE9C,kBAAkB,OAAO,cAAc;CACzC,OAAO;EACL,eAAe;EACf,KAAK,MAAM,aAAa,OAAO,WAC7B,QAAQ,cAAc,KAAK,SAAS;CAExC;CAEF,OAAO;EAAE;EAAgB;CAAY;AACvC;;;;;;AAOA,eAAe,mBAAmB,QAKhB;CAChB,MAAM,EAAE,cAAc,SAAS,aAAa,WAAW;CACvD,MAAM,8BAAc,IAAI,IAAY;CACpC,KAAK,MAAM,QAAQ,QAAQ,eACzB,KAAK,MAAM,QAAQ,KAAK,gBAGtB,YAAY,IAAI,GAAG,KAAK,MAAM,IAAI,MAAM;CAG5C,KAAK,MAAM,QAAQ,aAAa,eAC9B,KAAK,MAAM,YAAY,KAAK,gBAAgB;EAC1C,MAAM,MAAM,GAAG,KAAK,MAAM,IAAI;EAC9B,IAAI,YAAY,IAAI,GAAG,GAAG;EAC1B,MAAM,gBAAgB;GACpB,cAAc;GACd,OAAO,KAAK,UAAU,SAAS,SAAS;GACxC;GACA;EACF,CAAC;CACH;AAEJ;AAEA,eAAe,cAAc,QASI;CAC/B,MAAM,EAAE,IAAI,QAAQ,WAAW,aAAa,cAAc,QAAQ,QAAQ,WAAW;CACrF,MAAM,EAAE,OAAO,OAAO,MAAM,OAAO,UAAU;CAC7C,MAAM,YAAY,MAAM;CAExB,MAAM,EAAE,aAAa,aAAa,YAAY,MAAM,aAAa;EAC/D;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAGD,MAAM,kBAAkB,MAAM,wBAAwB;EACpD;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CACD,IAAI,oBAAoB,MACtB,OAAO,CAAC;CAIV,MAAM,WAAW,aAAa;EAAE;EAAiB;EAAO;EAAW;CAAO,CAAC;CAI3E,IAAI,UAAU,cACZ,0BAA0B;EAAE;EAAU;EAAc;EAAW;EAAO;CAAM,CAAC;CAG/E,MAAM,UAA+B,CAAC;CACtC,MAAM,gBAAgB,sBAAsB;EAAE;EAAO;CAAM,CAAC;CAC5D,MAAM,YAAY,UAAU,SAAS,iBAAiB,IAAI;CAI1D,MAAM,YAAY,sBAAsB,MAAM,GAAG;CACjD,MAAM,aAAa,GAAG,MAAM,GAAG;CAI/B,MAAM,gBAAgB,UAAU,cAAc;CAE9C,KAAK,MAAM,MAAM,UAAU;EACzB,MAAM,SACJ,gBAAgB,CAAC,SACb,uBAAuB,cAAc;GACnC,QAAQ;GACR;GACA;GACA,OAAO,GAAG;EACZ,CAAC,IACD,KAAA;EAYN,MAAM,WAAW,MAAM,qBAAqB;GAC1C;GACA,UAAA,MAXqB,uBAAuB;IAC5C;IACA;IACA;IACA,MAAM,GAAG;IACT,KAAK;IACL;GACF,CAAC;GAKC;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EAED,SAAS,MAAM,GAAG,MAChB,EAAE,sBAAsB,EAAE,sBACtB,KACA,EAAE,sBAAsB,EAAE,sBACxB,IACA,CACR;EACA,MAAM,gBAAgB,SAAS,KAAK,MAAM,EAAE,mBAAmB;EAC/D,MAAM,cAAc,mBAAmB,QAAQ;EAE/C,2BAA2B;GACzB;GACA;GACA;GACA;GACA,WAAW,GAAG;GACd;GACA;GACA;EACF,CAAC;EAMD,MAAM,eAAmC;GACvC,QAAQ;GACR;GACA;GACA;GACA;GACA,OAAO,GAAG;GACV,cAAc;GACd,iBAAiB;GACjB,aAAa,YAAY,aAAa;GACtC,gBAAgB;GAChB,cAAc;EAChB;EACA,IAAI,GAAG,QAAQ,KAAA,GACb,aAAa,gBAAgB,GAAG;EAElC,QAAQ,KAAK;GAAE;GAAc;EAAS,CAAC;EAEvC,OAAO,KACL,uBAAuB,GAAG,KAAK,SAAS,UAAU,UAAU,MAAM,UAAU,MAAM,QAAQ,YAAY,EACxG;CACF;CAEA,OAAO;AACT;;;;;;AAOA,eAAe,aAAa,QAOgD;CAC1E,MAAM,EAAE,IAAI,QAAQ,OAAO,MAAM,WAAW,WAAW;CACvD,IAAI;CACJ,IAAI,UAAU;CACd,IAAI,GAAG,KACL,cAAc,GAAG;MAEjB,IAAI;EAEF,eAAc,MADQ,OAAO,iBAAiB,OAAO,IAAI,EAAA,CACnC;EACtB,UAAU;CACZ,SAAS,OAAO;EAKd,IAAI,MAAM,KAAK,GACb,cAAc,MAAM,OAAO,iBAAiB,OAAO,IAAI;OAEvD,MAAM;CAEV;CAEF,MAAM,cAAc,MAAM,OAAO,gBAAgB,OAAO,MAAM,WAAW;CACzE,OAAO,MAAM,YAAY,UAAU,UAAU,YAAY,OAAO,aAAa;CAC7E,OAAO;EAAE;EAAa;EAAa;CAAQ;AAC7C;;;;;;;AAQA,eAAe,wBAAwB,QAQmB;CACxD,MAAM,EAAE,QAAQ,WAAW,OAAO,MAAM,aAAa,WAAW,WAAW;CAC3E,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,OAAO,cAAc,OAAO,MAAM,mBAAmB,WAAW;CACnF,SAAS,OAAO;EACd,IAAI,MAAM,KAAK,GAAG;GAChB,OAAO,KAAK,iCAAiC,UAAU,YAAY;GACnE,OAAO;EACT;EACA,MAAM;CACR;CAEA,MAAM,YAAY,SACf,QAAQ,MAAM,EAAE,SAAS,KAAK,CAAC,CAC/B,KAAK,OAAO;EAAE,MAAM,EAAE;EAAM,MAAM,EAAE;CAAK,EAAE;CAE9C,MAAM,kBAAyD,CAAC;CAChE,KAAK,MAAM,MAAM,WAIf,IAAI,MAHe,cAAc,iBAC/B,OAAO,YAAY,OAAO,MAAM,MAAM,KAAK,GAAG,MAAM,eAAe,GAAG,WAAW,CACnF,GAEE,gBAAgB,KAAK,EAAE;CAG3B,OAAO;AACT;;;;;;AAOA,SAAS,aAAa,QAKoB;CACxC,MAAM,EAAE,iBAAiB,OAAO,WAAW,WAAW;CACtD,IAAI,CAAC,MAAM,UAAU,MAAM,OAAO,WAAW,GAC3C,OAAO;CAET,MAAM,YAAY,IAAI,IAAI,MAAM,MAAM;CACtC,MAAM,WAAW,gBAAgB,QAAQ,MAAM,UAAU,IAAI,EAAE,IAAI,CAAC;CACpE,MAAM,eAAe,IAAI,IAAI,gBAAgB,KAAK,MAAM,EAAE,IAAI,CAAC;CAC/D,KAAK,MAAM,QAAQ,MAAM,QACvB,IAAI,CAAC,aAAa,IAAI,IAAI,GACxB,OAAO,KAAK,oBAAoB,KAAK,iBAAiB,UAAU,0BAA0B;CAG9F,OAAO;AACT;;;;;AAMA,SAAS,0BAA0B,QAM1B;CACP,MAAM,EAAE,UAAU,cAAc,WAAW,OAAO,UAAU;CAC5D,MAAM,UAAoB,CAAC;CAC3B,KAAK,MAAM,MAAM,UAOf,IAAI,CANW,uBAAuB,cAAc;EAClD,QAAQ;EACR;EACA;EACA,OAAO,GAAG;CACZ,CACU,GACR,QAAQ,KAAK,GAAG,IAAI;CAGxB,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,MACR,uEAAuE,UAAU,UAAU,MAAM,UAAU,MAAM,YAAY,QAAQ,KAAK,IAAI,EAAE,2DAClJ;AAEJ;;;;;;AAOA,eAAe,qBAAqB,QAgBR;CAC1B,MAAM,EACJ,IACA,UACA,QACA,WACA,OACA,MACA,aACA,eACA,WACA,WACA,YACA,eACA,WACA,QACA,WACE;CAEJ,MAAM,WAA2B,CAAC;CAClC,KAAK,MAAM,QAAQ,UAAU;EAC3B,IAAI,KAAK,OAAA,UAAsB;GAC7B,OAAO,KACL,aAAa,KAAK,KAAK,SAAS,UAAU,KAAK,KAAK,OAAO,OAAO,KAAA,CAAM,QAAQ,CAAC,EAAE,aAAa,gBAAgB,OAAO,KAAK,UAC9H;GACA;EACF;EAEA,MAAM,kBAAkB,MAAM,SAAS,GAAG,MAAM,YAAY,KAAK,IAAI,CAAC;EACtE,IAAI,CAAC,mBAAmB,gBAAgB,WAAW,IAAI,KAAK,MAAM,WAAW,eAAe,GAAG;GAC7F,OAAO,KAAK,aAAa,KAAK,KAAK,SAAS,UAAU,yBAAyB,GAAG,KAAK,GAAG;GAC1F;EACF;EAIA,MAAM,iBAAiB,YAAY,KAAK,eAAe,GAAG,MAAM,eAAe,CAAC;EAIhF,mBAAmB;GAAE,cAAc;GAAgB,iBAAiB;EAAU,CAAC;EAC/E,MAAM,aAAa,KAAK,WAAW,aAAa;EAEhD,mBAAmB;GAAE,cADI,YAAY,KAAK,GAAG,MAAM,eAAe,CAChB;GAAG,iBAAiB;EAAW,CAAC;EAElF,IAAI,UAAU,MAAM,cAAc,iBAChC,OAAO,eAAe,OAAO,MAAM,KAAK,MAAM,WAAW,CAC3D;EACA,MAAM,aAAa,OAAO,WAAW,SAAS,MAAM;EACpD,IAAI,aAAA,UAA4B;GAC9B,OAAO,KACL,aAAa,KAAK,KAAK,SAAS,UAAU,aAAa,aAAa,OAAO,KAAA,CAAM,QAAQ,CAAC,EAAE,aAAa,gBAAgB,OAAO,KAAK,UACvI;GACA;EACF;EAIA,IAAI,SAAS,KAAK,IAAI,MAAM,iBAC1B,IAAI;GACF,UAAU,qBAAqB;IAC7B;IACA,QAAQ;IACR;IACA,KAAK;GACP,CAAC;EACH,QAAQ;GAGN,OAAO,KACL,kBAAkB,KAAK,KAAK,IAAI,UAAU,mDAC5C;GACA,UAAU,gBAAgB,UAAU,gBAAgB,WAAW,SAAS,cAAc,SAAS;EACjG;EAGF,MAAM,eAAe,KAAK,WAAW,cAAc;EACnD,SAAS,KAAK;GAAE,qBAAqB;GAAgB;GAAc;EAAQ,CAAC;EAE5E,IAAI,CAAC,QACH,MAAM,iBAAiB,cAAc,OAAO;CAEhD;CACA,OAAO;AACT;;;;;;AAOA,SAAS,2BAA2B,QAS3B;CACP,MAAM,EAAE,QAAQ,QAAQ,aAAa,WAAW,WAAW,OAAO,OAAO,WAAW;CACpF,IAAI,UAAU,QAAQ,cACpB,IAAI,4BAA4B,KAAK,OAAO,YAAY,GAClD;MAAA,OAAO,iBAAiB,aAC1B,MAAM,IAAI,MACR,6BAA6B,UAAU,UAAU,UAAU,WAAW,MAAM,UAAU,MAAM,UAAU,OAAO,aAAa,YAAY,YAAY,iDACpJ;CAAA,OAGF,OAAO,MACL,6CAA6C,UAAU,UAAU,UAAU,oBAAoB,OAAO,aAAa,+BACrH;AAGN;AAEA,eAAe,gBAAgB,QAKb;CAChB,MAAM,EAAE,cAAc,OAAO,aAAa,WAAW;CACrD,IAAI,MAAM,WAAW,YAAY,KAAK,aAAa,MAAM,OAAO,CAAC,CAAC,SAAS,IAAI,GAAG;EAChF,OAAO,KAAK,2DAA2D,aAAa,GAAG;EACvF;CACF;CACA,MAAM,YAAY,UAAU,SAAS,iBAAiB,IAAI;CAC1D,IAAI;EACF,mBAAmB;GAAE;GAAc,iBAAiB;EAAU,CAAC;CACjE,QAAQ;EACN,OAAO,KAAK,4CAA4C,MAAM,UAAU,aAAa,GAAG;EACxF;CACF;CAEA,MAAM,WADW,KAAK,WAAW,YACT,CAAC;CACzB,OAAO,MAAM,0BAA0B,cAAc;AACvD;;;;;;;AAQA,SAAS,MAAM,OAAyB;CACtC,IAAI,iBAAiB,qBAAqB,MAAM,eAAe,KAC7D,OAAO;CAET,IACE,OAAO,UAAU,YACjB,UAAU,QACV,gBAAgB,SAChB,MAAM,eAAe,KAErB,OAAO;CAET,OAAO;AACT;;;;;;AAOA,SAAS,mBACP,OACQ;CACR,MAAM,OAAO,WAAW,QAAQ;CAChC,KAAK,MAAM,EAAE,qBAAqB,aAAa,OAAO;EACpD,KAAK,OAAO,mBAAmB;EAC/B,KAAK,OAAO,IAAI;EAChB,KAAK,OAAO,OAAO;EACnB,KAAK,OAAO,IAAI;CAClB;CACA,OAAO,UAAU,KAAK,OAAO,KAAK;AACpC;;;ACr1BA,MAAa,gBAAgB;CAAC;CAAY;CAAO;AAAI;AAarD,eAAsB,eACpB,QACA,SACe;CACf,MAAM,OAAoB,QAAQ,QAAQ;CAE1C,IAAI,SAAS,MAAM;EACjB,MAAM,aAAa,QAAQ,OAAO;EAClC;CACF;CAEA,IAAI,SAAS,OAAO;EAClB,MAAM,cAAc,QAAQ,OAAO;EACnC;CACF;CAEA,MAAM,mBAAmB,QAAQ,OAAO;AAC1C;AAEA,eAAe,mBAAmB,QAAgB,SAA+C;CAC/F,MAAM,cAAc,QAAQ,IAAI;CAIhC,MAAM,YAAY,MAAM,kBAAkB,WAAW;CAUrD,MAAM,WAAU,MARK,eAAe,QAClC;EACE,YAAY,QAAQ;EACpB,SAAS,QAAQ;EACjB,QAAQ,QAAQ;CAClB,GACA,EAAE,OAAO,CACX,EAAA,CACuB,WAAW;CAElC,IAAI,aAAa,QAAQ,SAAS,GAChC,MAAM,IAAI,MACR,4GACF;CAGF,IAAI,QAAQ,WAAW,GAAG;EACxB,IAAI,WAAW;GACb,OAAO,KACL,wFACF;GACA;EACF;EACA,OAAO,KAAK,uEAAuE;CACrF;CAEA,OAAO,MAAM,oCAAoC,QAAQ,OAAO,cAAc;CAE9E,MAAM,SAAS,MAAM,uBAAuB;EAC1C;EACA;EACA,SAAS;GACP,eAAe,QAAQ;GACvB,QAAQ,QAAQ;GAChB,OAAO,QAAQ;EACjB;EACA;CACF,CAAC;CAED,IAAI,OAAO,UAAU;EACnB,OAAO,YAAY,oBAAoB,OAAO,gBAAgB;EAC9D,OAAO,YAAY,iBAAiB,OAAO,iBAAiB;EAC5D,OAAO,YAAY,gBAAgB,OAAO,gBAAgB;EAC1D,OAAO,YAAY,qBAAqB,OAAO,iBAAiB;CAClE;CAEA,IAAI,OAAO,oBAAoB,GAC7B,MAAM,IAAI,MACR,qBAAqB,OAAO,kBAAkB,MAAM,OAAO,iBAAiB,oDAC9E;CAGF,IAAI,OAAO,oBAAoB,KAAK,OAAO,mBAAmB,GAC5D,OAAO,QACL,aAAa,OAAO,kBAAkB,gBAAgB,OAAO,iBAAiB,gBAAgB,OAAO,iBAAiB,YACxH;MAEA,OAAO,QACL,oCAAoC,OAAO,iBAAiB,qBAC9D;AAEJ;AAEA,eAAe,cAAc,QAAgB,SAA+C;CAC1F,MAAM,cAAc,QAAQ,IAAI;CAEhC,IAAI,CAAE,MAAM,kBAAkB,WAAW,GACvC,MAAM,IAAI,MACR,kHACF;CAGF,MAAM,SAAS,MAAM,WAAW;EAC9B;EACA,SAAS;GACP,QAAQ,QAAQ;GAChB,QAAQ,QAAQ;GAChB,OAAO,QAAQ;EACjB;EACA;CACF,CAAC;CAED,IAAI,OAAO,UAAU;EACnB,OAAO,YAAY,yBAAyB,OAAO,qBAAqB;EACxE,OAAO,YAAY,qBAAqB,OAAO,iBAAiB;EAChE,OAAO,YAAY,yBAAyB,OAAO,qBAAqB;CAC1E;CAEA,IAAI,OAAO,wBAAwB,GACjC,MAAM,IAAI,MACR,qBAAqB,OAAO,sBAAsB,MAAM,OAAO,sBAAsB,qDACvF;CAGF,IAAI,OAAO,oBAAoB,GAC7B,OAAO,QACL,aAAa,OAAO,kBAAkB,gBAAgB,OAAO,sBAAsB,sBACrF;MAEA,OAAO,QAAQ,oCAAoC,OAAO,sBAAsB,WAAW;AAE/F;AAEA,eAAe,aAAa,QAAgB,SAA+C;CACzF,MAAM,cAAc,QAAQ,IAAI;CAahC,MAAM,WAAU,MARK,eAAe,QAClC;EACE,YAAY,QAAQ;EACpB,SAAS,QAAQ;EACjB,QAAQ,QAAQ;CAClB,GACA,EAAE,OAAO,CACX,EAAA,CACuB,WAAW;CAElC,IAAI,QAAQ,WAAW,GAAG;EACxB,OAAO,KAAK,0DAA0D;EACtE;CACF;CAEA,MAAM,SAAS,MAAM,UAAU;EAC7B;EACA;EACA,SAAS;GACP,QAAQ,QAAQ;GAChB,QAAQ,QAAQ;GAChB,OAAO,QAAQ;EACjB;EACA;CACF,CAAC;CAED,IAAI,OAAO,UAAU;EACnB,OAAO,YAAY,oBAAoB,OAAO,gBAAgB;EAC9D,OAAO,YAAY,uBAAuB,OAAO,mBAAmB;EACpE,OAAO,YAAY,qBAAqB,OAAO,iBAAiB;CAClE;CAEA,IAAI,OAAO,oBAAoB,GAC7B,MAAM,IAAI,MACR,qBAAqB,OAAO,kBAAkB,MAAM,OAAO,iBAAiB,8CAC9E;CAGF,IAAI,OAAO,sBAAsB,GAC/B,OAAO,QACL,aAAa,OAAO,oBAAoB,iBAAiB,OAAO,iBAAiB,eACnF;MAEA,OAAO,QAAQ,8BAA8B,OAAO,iBAAiB,WAAW;AAEpF;;;ACpLA,MAAMC,WAAS,IAAI,cAAc;CAAE,SAAS;CAAO,QAAQ;AAAK,CAAC;AAEjE,MAAM,oBAAoB,OAAO;AACjC,MAAM,iBAAiB;;;;AAKvB,eAAe,aAKb;CACA,MAAM,YAAY,KAAK,QAAQ,IAAI,GAAG,iCAAiC;CAEvE,IAAI;EAEF,MAAM,WAAU,MADI,mBAAmB,SAAS,EAAA,CAC1B,QAAQ,SAAS,KAAK,SAAS,KAAK,CAAC;EAsB3D,QAAO,MApBc,QAAQ,IAC3B,QAAQ,IAAI,OAAO,SAAS;GAC1B,IAAI;IACF,MAAM,QAAQ,MAAM,cAAc,SAAS;KACzC,kBAAkB;KAClB,UAAU;IACZ,CAAC;IAED,OAAO;KACL,qBAAqB,KAAK,mCAAmC,IAAI;KACjE,aAAa,MAAM,eAAe;IACpC;GACF,SAAS,OAAO;IACd,SAAO,MAAM,6BAA6B,KAAK,IAAI,YAAY,KAAK,GAAG;IACvE,OAAO;GACT;EACF,CAAC,CACH,EAAA,CAGc,QAAQ,UAA8C,UAAU,IAAI;CACpF,SAAS,OAAO;EACd,SAAO,MACL,oCAAoC,kCAAkC,KAAK,YAAY,KAAK,GAC9F;EACA,OAAO,CAAC;CACV;AACF;;;;AAKA,eAAe,SAAS,EAAE,uBAIvB;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,WAAW,SAAS,mBAAmB;CAE7C,IAAI;EACF,MAAM,QAAQ,MAAM,cAAc,SAAS;GACzC,kBAAkB;GAClB,UAAU;EACZ,CAAC;EAED,OAAO;GACL,qBAAqB,KAAK,mCAAmC,QAAQ;GACrE,aAAa,MAAM,eAAe;GAClC,MAAM,MAAM,QAAQ;EACtB;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,6BAA6B,oBAAoB,IAAI,YAAY,KAAK,KAAK,EACzF,OAAO,MACT,CAAC;CACH;AACF;;;;AAKA,eAAe,SAAS,EACtB,qBACA,aACA,QASC;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,WAAW,SAAS,mBAAmB;CAG7C,MAAM,gBAAgB,KAAK,UAAU,WAAW,CAAC,CAAC,SAAS,KAAK;CAChE,IAAI,gBAAgB,mBAClB,MAAM,IAAI,MACR,cAAc,cAAc,yBAAyB,kBAAkB,mBAAmB,qBAC5F;CAGF,IAAI;EAEF,MAAM,iBAAiB,MAAM,WAAW;EAKxC,IAAI,CAJa,eAAe,MAC7B,UAAU,MAAM,wBAAwB,KAAK,mCAAmC,QAAQ,CAG/E,KAAK,eAAe,UAAU,gBACxC,MAAM,IAAI,MACR,6BAA6B,eAAe,eAAe,mCAC7D;EAGF,MAAM,QAAQ,IAAI,cAAc;GAC9B,YAAY,QAAQ,IAAI;GACxB,iBAAiB;GACjB,kBAAkB;GAClB;GACA;GACA,UAAU;EACZ,CAAC;EAID,MAAM,UADY,KAAK,QAAQ,IAAI,GAAG,iCACd,CAAC;EAGzB,MAAM,iBAAiB,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC;EAElE,OAAO;GACL,qBAAqB,KAAK,mCAAmC,QAAQ;GACrE,aAAa,MAAM,eAAe;GAClC,MAAM,MAAM,QAAQ;EACtB;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,8BAA8B,oBAAoB,IAAI,YAAY,KAAK,KAAK,EAC1F,OAAO,MACT,CAAC;CACH;AACF;;;;AAKA,eAAe,YAAY,EAAE,uBAE1B;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,WAAW,SAAS,mBAAmB;CAC7C,MAAM,WAAW,KAAK,QAAQ,IAAI,GAAG,mCAAmC,QAAQ;CAEhF,IAAI;EACF,MAAM,WAAW,QAAQ;EAEzB,OAAO,EACL,qBAAqB,KAAK,mCAAmC,QAAQ,EACvE;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,+BAA+B,oBAAoB,IAAI,YAAY,KAAK,KAAK,EAC3F,OAAO,MACT,CAAC;CACH;AACF;;;;AAKA,MAAM,mBAAmB;CACvB,YAAY,EAAE,OAAO,CAAC,CAAC;CACvB,UAAU,EAAE,OAAO,EACjB,qBAAqB,EAAE,OAAO,EAChC,CAAC;CACD,UAAU,EAAE,OAAO;EACjB,qBAAqB,EAAE,OAAO;EAC9B,aAAa;EACb,MAAM,EAAE,OAAO;CACjB,CAAC;CACD,aAAa,EAAE,OAAO,EACpB,qBAAqB,EAAE,OAAO,EAChC,CAAC;AACH;;;;AAKA,MAAa,aAAa;CACxB,YAAY;EACV,MAAM;EACN,aAAa,wBAAwB,KAAK,mCAAmC,MAAM,EAAE;EACrF,YAAY,iBAAiB;EAC7B,SAAS,YAAY;GAEnB,MAAM,SAAS,EAAE,QAAA,MADI,WAAW,EACR;GACxB,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,UAAU;EACR,MAAM;EACN,aACE;EACF,YAAY,iBAAiB;EAC7B,SAAS,OAAO,SAA0C;GACxD,MAAM,SAAS,MAAM,SAAS,EAAE,qBAAqB,KAAK,oBAAoB,CAAC;GAC/E,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,UAAU;EACR,MAAM;EACN,aACE;EACF,YAAY,iBAAiB;EAC7B,SAAS,OAAO,SAIV;GACJ,MAAM,SAAS,MAAM,SAAS;IAC5B,qBAAqB,KAAK;IAC1B,aAAa,KAAK;IAClB,MAAM,KAAK;GACb,CAAC;GACD,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,aAAa;EACX,MAAM;EACN,aAAa;EACb,YAAY,iBAAiB;EAC7B,SAAS,OAAO,SAA0C;GACxD,MAAM,SAAS,MAAM,YAAY,EAAE,qBAAqB,KAAK,oBAAoB,CAAC;GAClF,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;AACF;;;ACzPA,MAAMC,WAAS,IAAI,cAAc;CAAE,SAAS;CAAO,QAAQ;AAAK,CAAC;AAEjE,MAAM,sBAAsB,OAAO;AACnC,MAAM,mBAAmB;;;;AAKzB,eAAe,eAKb;CACA,MAAM,cAAc,KAAK,QAAQ,IAAI,GAAG,mCAAmC;CAE3E,IAAI;EAEF,MAAM,WAAU,MADI,mBAAmB,WAAW,EAAA,CAC5B,QAAQ,SAAS,KAAK,SAAS,KAAK,CAAC;EA4B3D,QAAO,MA1BgB,QAAQ,IAC7B,QAAQ,IAAI,OAAO,SAAS;GAC1B,IAAI;IACF,mBAAmB;KACjB,cAAc;KACd,iBAAiB;IACnB,CAAC;IAMD,MAAM,eAAc,MAJE,gBAAgB,SAAS,EAC7C,kBAAkB,KACpB,CAAC,EAAA,CAE2B,eAAe;IAE3C,OAAO;KACL,qBAAqB,KAAK,qCAAqC,IAAI;KACnE;IACF;GACF,SAAS,OAAO;IACd,SAAO,MAAM,+BAA+B,KAAK,IAAI,YAAY,KAAK,GAAG;IACzE,OAAO;GACT;EACF,CAAC,CACH,EAAA,CAGgB,QAAQ,YAAoD,YAAY,IAAI;CAC9F,SAAS,OAAO;EACd,SAAO,MACL,sCAAsC,oCAAoC,KAAK,YAAY,KAAK,GAClG;EACA,OAAO,CAAC;CACV;AACF;;;;AAKA,eAAe,WAAW,EAAE,uBAIzB;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,WAAW,SAAS,mBAAmB;CAE7C,IAAI;EACF,MAAM,UAAU,MAAM,gBAAgB,SAAS,EAC7C,kBAAkB,SACpB,CAAC;EAED,OAAO;GACL,qBAAqB,KAAK,qCAAqC,QAAQ;GACvE,aAAa,QAAQ,eAAe;GACpC,MAAM,QAAQ,QAAQ;EACxB;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,+BAA+B,oBAAoB,IAAI,YAAY,KAAK,KAAK,EAC3F,OAAO,MACT,CAAC;CACH;AACF;;;;AAKA,eAAe,WAAW,EACxB,qBACA,aACA,QASC;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,WAAW,SAAS,mBAAmB;CAG7C,MAAM,gBAAgB,KAAK,UAAU,WAAW,CAAC,CAAC,SAAS,KAAK;CAChE,IAAI,gBAAgB,qBAClB,MAAM,IAAI,MACR,gBAAgB,cAAc,yBAAyB,oBAAoB,mBAAmB,qBAChG;CAGF,IAAI;EAEF,MAAM,mBAAmB,MAAM,aAAa;EAM5C,IAAI,CALa,iBAAiB,MAC/B,YACC,QAAQ,wBAAwB,KAAK,qCAAqC,QAAQ,CAG1E,KAAK,iBAAiB,UAAU,kBAC1C,MAAM,IAAI,MACR,+BAA+B,iBAAiB,eAAe,qCACjE;EAIF,MAAM,cAAc,qBAAqB,MAAM,WAAW;EAC1D,MAAM,UAAU,IAAI,gBAAgB;GAClC,YAAY,QAAQ,IAAI;GACxB,iBAAiB;GACjB,kBAAkB;GAClB;GACA;GACA;GACA,UAAU;EACZ,CAAC;EAID,MAAM,UADc,KAAK,QAAQ,IAAI,GAAG,mCACd,CAAC;EAG3B,MAAM,iBAAiB,QAAQ,YAAY,GAAG,QAAQ,eAAe,CAAC;EAEtE,OAAO;GACL,qBAAqB,KAAK,qCAAqC,QAAQ;GACvE,aAAa,QAAQ,eAAe;GACpC,MAAM,QAAQ,QAAQ;EACxB;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,gCAAgC,oBAAoB,IAAI,YAAY,KAAK,KAAK,EAC5F,OAAO,MACT,CAAC;CACH;AACF;;;;AAKA,eAAe,cAAc,EAAE,uBAE5B;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,WAAW,SAAS,mBAAmB;CAC7C,MAAM,WAAW,KAAK,QAAQ,IAAI,GAAG,qCAAqC,QAAQ;CAElF,IAAI;EACF,MAAM,WAAW,QAAQ;EAEzB,OAAO,EACL,qBAAqB,KAAK,qCAAqC,QAAQ,EACzE;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,iCAAiC,oBAAoB,IAAI,YAAY,KAAK,KAAK,EAC7F,OAAO,MACT,CAAC;CACH;AACF;;;;AAKA,MAAM,qBAAqB;CACzB,cAAc,EAAE,OAAO,CAAC,CAAC;CACzB,YAAY,EAAE,OAAO,EACnB,qBAAqB,EAAE,OAAO,EAChC,CAAC;CACD,YAAY,EAAE,OAAO;EACnB,qBAAqB,EAAE,OAAO;EAC9B,aAAa;EACb,MAAM,EAAE,OAAO;CACjB,CAAC;CACD,eAAe,EAAE,OAAO,EACtB,qBAAqB,EAAE,OAAO,EAChC,CAAC;AACH;;;;AAKA,MAAa,eAAe;CAC1B,cAAc;EACZ,MAAM;EACN,aAAa,0BAA0B,KAAK,qCAAqC,MAAM,EAAE;EACzF,YAAY,mBAAmB;EAC/B,SAAS,YAAY;GAEnB,MAAM,SAAS,EAAE,UAAA,MADM,aAAa,EACV;GAC1B,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,YAAY;EACV,MAAM;EACN,aACE;EACF,YAAY,mBAAmB;EAC/B,SAAS,OAAO,SAA0C;GACxD,MAAM,SAAS,MAAM,WAAW,EAAE,qBAAqB,KAAK,oBAAoB,CAAC;GACjF,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,YAAY;EACV,MAAM;EACN,aACE;EACF,YAAY,mBAAmB;EAC/B,SAAS,OAAO,SAIV;GACJ,MAAM,SAAS,MAAM,WAAW;IAC9B,qBAAqB,KAAK;IAC1B,aAAa,KAAK;IAClB,MAAM,KAAK;GACb,CAAC;GACD,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,eAAe;EACb,MAAM;EACN,aAAa;EACb,YAAY,mBAAmB;EAC/B,SAAS,OAAO,SAA0C;GACxD,MAAM,SAAS,MAAM,cAAc,EAAE,qBAAqB,KAAK,oBAAoB,CAAC;GACpF,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;AACF;;;;;;;;;;;ACpQA,MAAa,uBAAuB,EAAE,OAAO;CAC3C,MAAM,EAAE,OAAO;CACf,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;CACtB,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;CAC9B,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;AAChC,CAAC;AAiBD,SAAS,gBAAgB,OAAe,OAA2B;CACjE,MAAM,SAAS,iBAAiB,UAAU,KAAK;CAC/C,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,MACR,WAAW,MAAM,SAAS,MAAM,qBAAqB,iBAAiB,KAAK,IAAI,GACjF;CAEF,OAAO,OAAO;AAChB;;;;;AAMA,eAAsBC,iBAAe,SAAoD;CACvF,IAAI;EAEF,IAAI,CAAC,QAAQ,MACX,OAAO;GACL,SAAS;GACT,OAAO;EACT;EAIF,IAAI,CAAC,QAAQ,MAAM,QAAQ,GAAG,WAAW,GACvC,OAAO;GACL,SAAS;GACT,OAAO;EACT;EAGF,MAAM,WAAW,gBAAgB,QAAQ,MAAM,QAAQ;EACvD,MAAM,aAAa,QAAQ,GAAG,KAAK,MAAM,gBAAgB,GAAG,aAAa,CAAC;EAC1E,MAAM,UAAU,MAAM,KAAK,IAAI,IAAI,UAAU,CAAC;EAE9C,IAAI,QAAQ,SAAS,QAAQ,GAC3B,OAAO;GACL,SAAS;GACT,OACE,uDAAuD,SAAS;EAEpE;EASF,MAAM,SAAS,MAAM,eAAe,QAAQ;GAC1C,SAAS,CAAC,UAAU,GAAG,OAAO;GAC9B,UAAW,QAAQ,YAAY,CAAC,GAAG;GACnC,QAAQ,QAAQ;GAChB,QAAQ,QAAQ;GAGhB,SAAS;GACT,QAAQ;EACV,CAAC;EAKD,OAAOC,uBAAqB;GAAE,eAAA,MAFF,gBAAgB;IAAE;IAAQ;IAAU;IAAS,QAAA,IADtD,cAAc;KAAE,SAAS;KAAO,QAAQ;IAAK,CACc;GAAE,CAAC;GAEpC;GAAQ;GAAU;EAAQ,CAAC;CAC1E,SAAS,OAAO;EACd,OAAO;GACL,SAAS;GACT,OAAO,YAAY,KAAK;EAC1B;CACF;AACF;AAEA,SAASA,uBAAqB,QAKT;CACnB,MAAM,EAAE,eAAe,QAAQ,UAAU,YAAY;CAErD,MAAM,aAAa,oBAAoB,aAAa;CAEpD,OAAO;EACL,SAAS;EACT,QAAQ;GACN,YAAY,cAAc;GAC1B,aAAa,cAAc;GAC3B,UAAU,cAAc;GACxB,eAAe,cAAc;GAC7B,gBAAgB,cAAc;GAC9B,aAAa,cAAc;GAC3B,YAAY,cAAc;GAC1B,kBAAkB,cAAc;GAChC,aAAa,cAAc;GAC3B;EACF;EACA,QAAQ;GACN,MAAM;GACN,IAAI;GACJ,UAAU,OAAO,YAAY;GAC7B,QAAQ,OAAO,UAAU;GACzB,QAAQ,OAAO,cAAc;EAC/B;CACF;AACF;AAMA,MAAa,eAAe,EAC1B,gBAAgB;CACd,MAAM;CACN,aACE;CACF,YAAY,EARd,gBAAgB,qBAQF,EAAmB;CAC/B,SAAS,OAAO,YAA6C;EAC3D,MAAM,SAAS,MAAMD,iBAAe,OAAO;EAC3C,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;CACvC;AACF,EACF;;;;;;;;;;;AClJA,MAAa,wBAAwB,EAAE,OAAO;CAC5C,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;CAC9B,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;CAC9B,kBAAkB,EAAE,SAAS,EAAE,QAAQ,CAAC;CACxC,mBAAmB,EAAE,SAAS,EAAE,QAAQ,CAAC;CACzC,gBAAgB,EAAE,SAAS,EAAE,QAAQ,CAAC;AACxC,CAAC;;;;;AA6BD,eAAsBE,kBAAgB,UAA2B,CAAC,GAA+B;CAC/F,IAAI;EAGF,IAAI,CAAC,MADgB,uBAAuB,EAAE,WAAW,QAAQ,IAAI,EAAE,CAAC,GAEtE,OAAO;GACL,SAAS;GACT,OACE;EACJ;EAMF,MAAM,SAAS,MAAM,eAAe,QAAQ;GAC1C,SAAS,QAAQ;GACjB,UAAU,QAAQ;GAClB,QAAQ,QAAQ;GAChB,QAAQ,QAAQ;GAChB,kBAAkB,QAAQ;GAC1B,mBAAmB,QAAQ;GAC3B,gBAAgB,QAAQ;GAGxB,SAAS;GACT,QAAQ;EACV,CAAC;EAKD,OAAOC,uBAAqB;GAAE,gBAAA,MAFD,SAAS;IAAE;IAAQ,QAAA,IAD7B,cAAc;KAAE,SAAS;KAAO,QAAQ;IAAK,CACX;GAAE,CAAC;GAEV;EAAO,CAAC;CACxD,SAAS,OAAO;EACd,OAAO;GACL,SAAS;GACT,OAAO,YAAY,KAAK;EAC1B;CACF;AACF;;;;;;;;;AAUA,SAAS,qBAAqB,QAAwD;CACpF,MAAM,EAAE,YAAY,WAAW;CAC/B,MAAM,UAAU,OAAO,WAAW,CAAC,CAAC,KAAK,IAAI;CAC7C,MAAM,WAAW,OAAO,YAAY,CAAC,CAAC,KAAK,IAAI;CAE/C,IAAI,aAAa,GACf,OAAO,aAAa,WAAW,wBAAwB,QAAQ,kBAAkB,SAAS;CAG5F,OACE,yCAAyC,QAAQ,kBAAkB,SAAS;AAIhF;AAEA,SAASA,uBAAqB,QAGR;CACpB,MAAM,EAAE,gBAAgB,WAAW;CAEnC,MAAM,aAAa,oBAAoB,cAAc;CAErD,OAAO;EACL,SAAS;EACT,SAAS,qBAAqB;GAAE;GAAY;EAAO,CAAC;EACpD,QAAQ;GACN,YAAY,eAAe;GAC3B,aAAa,eAAe;GAC5B,UAAU,eAAe;GACzB,eAAe,eAAe;GAC9B,gBAAgB,eAAe;GAC/B,aAAa,eAAe;GAC5B,YAAY,eAAe;GAC3B,kBAAkB,eAAe;GACjC,aAAa,eAAe;GAC5B,iBAAiB,eAAe;GAChC;EACF;EACA,QAAQ;GACN,SAAS,OAAO,WAAW;GAC3B,UAAU,OAAO,YAAY;GAC7B,QAAQ,OAAO,UAAU;GACzB,QAAQ,OAAO,UAAU;GACzB,kBAAkB,OAAO,oBAAoB;GAC7C,mBAAmB,OAAO,qBAAqB;GAC/C,gBAAgB,OAAO,kBAAkB;EAC3C;CACF;AACF;AAMA,MAAa,gBAAgB,EAC3B,iBAAiB;CACf,MAAM;CACN,aACE;CACF,YAAY,EARd,iBAAiB,sBAQH,EAAoB;CAChC,SAAS,OAAO,UAA2B,CAAC,MAAuB;EACjE,MAAM,SAAS,MAAMD,kBAAgB,OAAO;EAC5C,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;CACvC;AACF,EACF;;;AC/JA,MAAM,oBAAoB,OAAO;;;;AAKjC,eAAe,eAGZ;CACD,IAAI;EACF,MAAM,gBAAgB,MAAM,cAAc,SAAS,EACjD,UAAU,KACZ,CAAC;EAOD,OAAO;GACL,qBAN0B,KAC1B,cAAc,mBAAmB,GACjC,cAAc,oBAAoB,CAIhB;GAClB,SAAS,cAAc,eAAe;EACxC;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,8BAA8B,kCAAkC,KAAK,YAAY,KAAK,KACtF,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,eAAe,aAAa,EAAE,WAG3B;CAED,IAAI,QAAQ,SAAS,mBACnB,MAAM,IAAI,MACR,mBAAmB,QAAQ,OAAO,yBAAyB,kBAAkB,mBAAmB,mCAClG;CAIF,IAAI;EACF,WAAW,OAAO;CACpB,SAAS,OAAO;EACd,MAAM,IAAI,MACR,uCAAuC,kCAAkC,KAAK,YAAY,KAAK,KAC/F,EACE,OAAO,MACT,CACF;CACF;CAEA,IAAI;EACF,MAAM,aAAa,QAAQ,IAAI;EAE/B,MAAM,EAAE,iBAAiB,qBAAqB,MAAM,+BAA+B;GACjF;GACA,OAHY,cAAc,iBAGtB;EACN,CAAC;EACD,MAAM,WAAW,KAAK,YAAY,iBAAiB,gBAAgB;EAGnE,MAAM,gBAAgB,IAAI,cAAc;GACtC;GACA;GACA;GACA,aAAa;GACb,UAAU;EACZ,CAAC;EAGD,MAAM,UAAU,KAAK,YAAY,eAAe,CAAC;EAGjD,MAAM,iBAAiB,UAAU,OAAO;EAIxC,OAAO;GACL,qBAH0B,KAAK,iBAAiB,gBAG9B;GAClB,SAAS,cAAc,eAAe;EACxC;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,+BAA+B,kCAAkC,KAAK,YAAY,KAAK,KACvF,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,eAAe,kBAEZ;CACD,IAAI;EACF,MAAM,aAAa,QAAQ,IAAI;EAC/B,MAAM,QAAQ,cAAc,iBAAiB;EAE7C,KAAK,MAAM,aAAa,4BAA4B,EAAE,MAAM,CAAC,GAC3D,MAAM,WAAW,KAAK,YAAY,UAAU,iBAAiB,UAAU,gBAAgB,CAAC;EAQ1F,OAAO,EACL,qBAN0B,KAC1B,MAAM,YAAY,iBAClB,MAAM,YAAY,gBAIA,EACpB;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,gCAAgC,kCAAkC,KAAK,YAAY,KAAK,KACxF,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,MAAM,mBAAmB;CACvB,cAAc,EAAE,OAAO,CAAC,CAAC;CACzB,cAAc,EAAE,OAAO,EACrB,SAAS,EAAE,OAAO,EACpB,CAAC;CACD,iBAAiB,EAAE,OAAO,CAAC,CAAC;AAC9B;;;;AAKA,MAAa,aAAa;CACxB,cAAc;EACZ,MAAM;EACN,aAAa,qCAAqC,kCAAkC;EACpF,YAAY,iBAAiB;EAC7B,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,aAAa;GAClC,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,cAAc;EACZ,MAAM;EACN,aACE;EACF,YAAY,iBAAiB;EAC7B,SAAS,OAAO,SAA8B;GAC5C,MAAM,SAAS,MAAM,aAAa,EAAE,SAAS,KAAK,QAAQ,CAAC;GAC3D,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,iBAAiB;EACf,MAAM;EACN,aAAa;EACb,YAAY,iBAAiB;EAC7B,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,gBAAgB;GACrC,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;AACF;;;ACjLA,MAAM,yBAAyB,MAAM;;;;AAKrC,eAAe,gBAGZ;CACD,MAAM,iBAAiB,KAAK,QAAQ,IAAI,GAAG,oCAAoC;CAE/E,IAAI;EAGF,OAAO;GACL,qBAAqB;GACrB,SAAA,MAJoB,gBAAgB,cAAc;EAKpD;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,+BAA+B,qCAAqC,KAAK,YAAY,KAAK,KAC1F,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,eAAe,cAAc,EAAE,WAG5B;CACD,MAAM,iBAAiB,KAAK,QAAQ,IAAI,GAAG,oCAAoC;CAG/E,MAAM,mBAAmB,OAAO,WAAW,SAAS,MAAM;CAC1D,IAAI,mBAAmB,wBACrB,MAAM,IAAI,MACR,oBAAoB,iBAAiB,yBAAyB,uBAAuB,qBAAqB,sCAC5G;CAGF,IAAI;EAEF,MAAM,UAAU,QAAQ,IAAI,CAAC;EAG7B,MAAM,iBAAiB,gBAAgB,OAAO;EAE9C,OAAO;GACL,qBAAqB;GACrB;EACF;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,gCAAgC,qCAAqC,KAAK,YAAY,KAAK,KAC3F,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,eAAe,mBAEZ;CACD,MAAM,eAAe,KAAK,QAAQ,IAAI,GAAG,oCAAoC;CAC7E,MAAM,mBAAmB,KAAK,QAAQ,IAAI,GAAG,kCAAkC;CAE/E,IAAI;EAIF,MAAM,QAAQ,IAAI,CAAC,WAAW,YAAY,GAAG,WAAW,gBAAgB,CAAC,CAAC;EAE1E,OAAO,EAGL,qBAAqB,qCACvB;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,kCAAkC,qCAAqC,IAAI,mCAAmC,KAAK,YAAY,KAAK,KACpI,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,MAAM,oBAAoB;CACxB,eAAe,EAAE,OAAO,CAAC,CAAC;CAC1B,eAAe,EAAE,OAAO,EACtB,SAAS,EAAE,OAAO,EACpB,CAAC;CACD,kBAAkB,EAAE,OAAO,CAAC,CAAC;AAC/B;;;;AAKA,MAAa,cAAc;CACzB,eAAe;EACb,MAAM;EACN,aAAa;EACb,YAAY,kBAAkB;EAC9B,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,cAAc;GACnC,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,eAAe;EACb,MAAM;EACN,aACE;EACF,YAAY,kBAAkB;EAC9B,SAAS,OAAO,SAA8B;GAC5C,MAAM,SAAS,MAAM,cAAc,EAAE,SAAS,KAAK,QAAQ,CAAC;GAC5D,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,kBAAkB;EAChB,MAAM;EACN,aAAa;EACb,YAAY,kBAAkB;EAC9B,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,iBAAiB;GACtC,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;AACF;;;;;;;;;;;;;;AC/HA,MAAa,sBAAsB,EAAE,OAAO;CAC1C,QAAQ,EAAE,OAAO;CACjB,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;AAChC,CAAC;;;;;AAmBD,eAAsBE,gBAAc,SAAkD;CACpF,IAAI;EAEF,IAAI,CAAC,QAAQ,QACX,OAAO;GACL,SAAS;GACT,OAAO;EACT;EAMF,MAAM,SAAS,MAAM,eAAe,QAAQ;GAC1C,SAAS,CAAC,QAAQ,MAAM;GACxB,UAAU,QAAQ;GAClB,QAAQ,QAAQ;GAGhB,SAAS;GACT,QAAQ;EACV,CAAC;EAED,MAAM,OAAO,OAAO,WAAW,CAAC,CAAC;EAKjC,OAAO,qBAAqB;GAAE,cAAA,MAFH,eAAe;IAAE;IAAQ;IAAM,QAAA,IADvC,cAAc;KAAE,SAAS;KAAO,QAAQ;IAAK,CACD;GAAE,CAAC;GAEtB;GAAQ;EAAK,CAAC;CAC5D,SAAS,OAAO;EACd,OAAO;GACL,SAAS;GACT,OAAO,YAAY,KAAK;EAC1B;CACF;AACF;AAEA,SAAS,qBAAqB,QAIV;CAClB,MAAM,EAAE,cAAc,QAAQ,SAAS;CAEvC,MAAM,aAAa,oBAAoB,YAAY;CAEnD,OAAO;EACL,SAAS;EACT,QAAQ;GACN,YAAY,aAAa;GACzB,aAAa,aAAa;GAC1B,UAAU,aAAa;GACvB,eAAe,aAAa;GAC5B,gBAAgB,aAAa;GAC7B,aAAa,aAAa;GAC1B,YAAY,aAAa;GACzB,kBAAkB,aAAa;GAC/B,aAAa,aAAa;GAC1B;EACF;EACA,QAAQ;GACN,QAAQ;GACR,UAAU,OAAO,YAAY;GAC7B,QAAQ,OAAO,UAAU;EAC3B;CACF;AACF;AAMA,MAAa,cAAc,EACzB,eAAe;CACb,MAAM;CACN,aACE;CACF,YAAY,EARd,eAAe,oBAQD,EAAkB;CAC9B,SAAS,OAAO,YAA4C;EAC1D,MAAM,SAAS,MAAMA,gBAAc,OAAO;EAC1C,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;CACvC;AACF,EACF;;;ACnHA,MAAM,kBAAkB,OAAO;;;;AAK/B,eAAe,aAGZ;CACD,IAAI;EACF,MAAM,cAAc,MAAM,YAAY,SAAS,EAC7C,UAAU,KACZ,CAAC;EAOD,OAAO;GACL,qBAN0B,KAC1B,YAAY,mBAAmB,GAC/B,YAAY,oBAAoB,CAId;GAClB,SAAS,YAAY,eAAe;EACtC;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,4BAA4B,gCAAgC,KAAK,YAAY,KAAK,KAClF,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,eAAe,WAAW,EAAE,WAGzB;CAED,IAAI,QAAQ,SAAS,iBACnB,MAAM,IAAI,MACR,iBAAiB,QAAQ,OAAO,yBAAyB,gBAAgB,mBAAmB,iCAC9F;CAIF,IAAI;EACF,WAAW,OAAO;CACpB,SAAS,OAAO;EACd,MAAM,IAAI,MACR,qCAAqC,gCAAgC,KAAK,YAAY,KAAK,KAC3F,EACE,OAAO,MACT,CACF;CACF;CAEA,IAAI;EACF,MAAM,aAAa,QAAQ,IAAI;EAE/B,MAAM,EAAE,iBAAiB,qBAAqB,MAAM,+BAA+B;GACjF;GACA,OAHY,YAAY,iBAGpB;EACN,CAAC;EACD,MAAM,WAAW,KAAK,YAAY,iBAAiB,gBAAgB;EAGnE,MAAM,cAAc,IAAI,YAAY;GAClC;GACA;GACA;GACA,aAAa;GACb,UAAU;EACZ,CAAC;EAGD,MAAM,UAAU,KAAK,YAAY,eAAe,CAAC;EAGjD,MAAM,iBAAiB,UAAU,OAAO;EAIxC,OAAO;GACL,qBAH0B,KAAK,iBAAiB,gBAG9B;GAClB,SAAS,YAAY,eAAe;EACtC;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,6BAA6B,gCAAgC,KAAK,YAAY,KAAK,KACnF,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,eAAe,gBAEZ;CACD,IAAI;EACF,MAAM,aAAa,QAAQ,IAAI;EAC/B,MAAM,QAAQ,YAAY,iBAAiB;EAE3C,KAAK,MAAM,aAAa,4BAA4B,EAAE,MAAM,CAAC,GAC3D,MAAM,WAAW,KAAK,YAAY,UAAU,iBAAiB,UAAU,gBAAgB,CAAC;EAQ1F,OAAO,EACL,qBAN0B,KAC1B,MAAM,YAAY,iBAClB,MAAM,YAAY,gBAIA,EACpB;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,8BAA8B,gCAAgC,KAAK,YAAY,KAAK,KACpF,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,MAAM,iBAAiB;CACrB,YAAY,EAAE,OAAO,CAAC,CAAC;CACvB,YAAY,EAAE,OAAO,EACnB,SAAS,EAAE,OAAO,EACpB,CAAC;CACD,eAAe,EAAE,OAAO,CAAC,CAAC;AAC5B;;;;AAKA,MAAa,WAAW;CACtB,YAAY;EACV,MAAM;EACN,aAAa,mCAAmC,gCAAgC;EAChF,YAAY,eAAe;EAC3B,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,WAAW;GAChC,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,YAAY;EACV,MAAM;EACN,aACE;EACF,YAAY,eAAe;EAC3B,SAAS,OAAO,SAA8B;GAC5C,MAAM,SAAS,MAAM,WAAW,EAAE,SAAS,KAAK,QAAQ,CAAC;GACzD,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,eAAe;EACb,MAAM;EACN,aAAa;EACb,YAAY,eAAe;EAC3B,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,cAAc;GACnC,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;AACF;;;AC9KA,MAAM,0BAA0B,OAAO;;;;AAKvC,eAAe,qBAGZ;CACD,IAAI;EACF,MAAM,sBAAsB,MAAM,oBAAoB,SAAS,EAC7D,UAAU,KACZ,CAAC;EAOD,OAAO;GACL,qBAN0B,KAC1B,oBAAoB,mBAAmB,GACvC,oBAAoB,oBAAoB,CAItB;GAClB,SAAS,oBAAoB,eAAe;EAC9C;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,oCAAoC,wCAAwC,KAAK,YAAY,KAAK,KAClG,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,eAAe,mBAAmB,EAAE,WAGjC;CAED,IAAI,QAAQ,SAAS,yBACnB,MAAM,IAAI,MACR,yBAAyB,QAAQ,OAAO,yBAAyB,wBAAwB,mBAAmB,yCAC9G;CAIF,IAAI;EACF,WAAW,OAAO;CACpB,SAAS,OAAO;EACd,MAAM,IAAI,MACR,6CAA6C,wCAAwC,KAAK,YAAY,KAAK,KAC3G,EACE,OAAO,MACT,CACF;CACF;CAEA,IAAI;EACF,MAAM,aAAa,QAAQ,IAAI;EAE/B,MAAM,EAAE,iBAAiB,qBAAqB,MAAM,+BAA+B;GACjF;GACA,OAHY,oBAAoB,iBAG5B;EACN,CAAC;EACD,MAAM,WAAW,KAAK,YAAY,iBAAiB,gBAAgB;EAGnE,MAAM,sBAAsB,IAAI,oBAAoB;GAClD;GACA;GACA;GACA,aAAa;GACb,UAAU;EACZ,CAAC;EAGD,MAAM,UAAU,KAAK,YAAY,eAAe,CAAC;EAGjD,MAAM,iBAAiB,UAAU,OAAO;EAIxC,OAAO;GACL,qBAH0B,KAAK,iBAAiB,gBAG9B;GAClB,SAAS,oBAAoB,eAAe;EAC9C;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,qCAAqC,wCAAwC,KAAK,YAAY,KAAK,KACnG,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,eAAe,wBAEZ;CACD,IAAI;EACF,MAAM,aAAa,QAAQ,IAAI;EAC/B,MAAM,QAAQ,oBAAoB,iBAAiB;EAEnD,KAAK,MAAM,aAAa,4BAA4B,EAAE,MAAM,CAAC,GAC3D,MAAM,WAAW,KAAK,YAAY,UAAU,iBAAiB,UAAU,gBAAgB,CAAC;EAQ1F,OAAO,EACL,qBAN0B,KAC1B,MAAM,YAAY,iBAClB,MAAM,YAAY,gBAIA,EACpB;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,sCAAsC,wCAAwC,KAAK,YAAY,KAAK,KACpG,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,MAAM,yBAAyB;CAC7B,oBAAoB,EAAE,OAAO,CAAC,CAAC;CAC/B,oBAAoB,EAAE,OAAO,EAC3B,SAAS,EAAE,OAAO,EACpB,CAAC;CACD,uBAAuB,EAAE,OAAO,CAAC,CAAC;AACpC;;;;AAKA,MAAa,mBAAmB;CAC9B,oBAAoB;EAClB,MAAM;EACN,aAAa,2CAA2C,wCAAwC;EAChG,YAAY,uBAAuB;EACnC,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,mBAAmB;GACxC,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,oBAAoB;EAClB,MAAM;EACN,aACE;EACF,YAAY,uBAAuB;EACnC,SAAS,OAAO,SAA8B;GAC5C,MAAM,SAAS,MAAM,mBAAmB,EAAE,SAAS,KAAK,QAAQ,CAAC;GACjE,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,uBAAuB;EACrB,MAAM;EACN,aAAa;EACb,YAAY,uBAAuB;EACnC,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,sBAAsB;GAC3C,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;AACF;;;ACvKA,MAAMC,WAAS,IAAI,cAAc;CAAE,SAAS;CAAO,QAAQ;AAAK,CAAC;AAEjE,MAAM,mBAAmB,OAAO;AAChC,MAAM,gBAAgB;;;;AAKtB,eAAe,YAKb;CACA,MAAM,WAAW,KAAK,QAAQ,IAAI,GAAG,gCAAgC;CAErE,IAAI;EAEF,MAAM,WAAU,MADI,mBAAmB,QAAQ,EAAA,CACzB,QAAQ,SAAS,KAAK,SAAS,KAAK,CAAC;EAyB3D,QAAO,MAvBa,QAAQ,IAC1B,QAAQ,IAAI,OAAO,SAAS;GAC1B,IAAI;IAOF,MAAM,eAAc,MALD,aAAa,SAAS;KACvC,kBAAkB;KAClB,UAAU;IACZ,CAAC,EAAA,CAEwB,eAAe;IAExC,OAAO;KACL,qBAAqB,KAAK,kCAAkC,IAAI;KAChE;IACF;GACF,SAAS,OAAO;IACd,SAAO,MAAM,4BAA4B,KAAK,IAAI,YAAY,KAAK,GAAG;IACtE,OAAO;GACT;EACF,CAAC,CACH,EAAA,CAGa,QAAQ,SAA2C,SAAS,IAAI;CAC/E,SAAS,OAAO;EACd,SAAO,MACL,mCAAmC,iCAAiC,KAAK,YAAY,KAAK,GAC5F;EACA,OAAO,CAAC;CACV;AACF;;;;AAKA,eAAe,QAAQ,EAAE,uBAItB;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,WAAW,SAAS,mBAAmB;CAE7C,IAAI;EACF,MAAM,OAAO,MAAM,aAAa,SAAS;GACvC,kBAAkB;GAClB,UAAU;EACZ,CAAC;EAED,OAAO;GACL,qBAAqB,KAAK,kCAAkC,QAAQ;GACpE,aAAa,KAAK,eAAe;GACjC,MAAM,KAAK,QAAQ;EACrB;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,4BAA4B,oBAAoB,IAAI,YAAY,KAAK,KAAK,EACxF,OAAO,MACT,CAAC;CACH;AACF;;;;AAKA,eAAe,QAAQ,EACrB,qBACA,aACA,QASC;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,WAAW,SAAS,mBAAmB;CAG7C,MAAM,gBAAgB,KAAK,UAAU,WAAW,CAAC,CAAC,SAAS,KAAK;CAChE,IAAI,gBAAgB,kBAClB,MAAM,IAAI,MACR,aAAa,cAAc,yBAAyB,iBAAiB,mBAAmB,qBAC1F;CAGF,IAAI;EAEF,MAAM,gBAAgB,MAAM,UAAU;EAKtC,IAAI,CAJa,cAAc,MAC5B,SAAS,KAAK,wBAAwB,KAAK,kCAAkC,QAAQ,CAG5E,KAAK,cAAc,UAAU,eACvC,MAAM,IAAI,MACR,4BAA4B,cAAc,eAAe,kCAC3D;EAIF,MAAM,OAAO,IAAI,aAAa;GAC5B,YAAY,QAAQ,IAAI;GACxB,iBAAiB;GACjB,kBAAkB;GAClB;GACA;GACA,UAAU;EACZ,CAAC;EAID,MAAM,UADW,KAAK,QAAQ,IAAI,GAAG,gCACd,CAAC;EAGxB,MAAM,iBAAiB,KAAK,YAAY,GAAG,KAAK,eAAe,CAAC;EAEhE,OAAO;GACL,qBAAqB,KAAK,kCAAkC,QAAQ;GACpE,aAAa,KAAK,eAAe;GACjC,MAAM,KAAK,QAAQ;EACrB;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,6BAA6B,oBAAoB,IAAI,YAAY,KAAK,KAAK,EACzF,OAAO,MACT,CAAC;CACH;AACF;;;;AAKA,eAAe,WAAW,EAAE,uBAEzB;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,WAAW,SAAS,mBAAmB;CAC7C,MAAM,WAAW,KAAK,QAAQ,IAAI,GAAG,kCAAkC,QAAQ;CAE/E,IAAI;EACF,MAAM,WAAW,QAAQ;EAEzB,OAAO,EACL,qBAAqB,KAAK,kCAAkC,QAAQ,EACtE;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,8BAA8B,oBAAoB,IAAI,YAAY,KAAK,KAAK,EAC1F,OAAO,MACT,CAAC;CACH;AACF;;;;AAKA,MAAM,kBAAkB;CACtB,WAAW,EAAE,OAAO,CAAC,CAAC;CACtB,SAAS,EAAE,OAAO,EAChB,qBAAqB,EAAE,OAAO,EAChC,CAAC;CACD,SAAS,EAAE,OAAO;EAChB,qBAAqB,EAAE,OAAO;EAC9B,aAAa;EACb,MAAM,EAAE,OAAO;CACjB,CAAC;CACD,YAAY,EAAE,OAAO,EACnB,qBAAqB,EAAE,OAAO,EAChC,CAAC;AACH;;;;AAKA,MAAa,YAAY;CACvB,WAAW;EACT,MAAM;EACN,aAAa,uBAAuB,KAAK,kCAAkC,MAAM,EAAE;EACnF,YAAY,gBAAgB;EAC5B,SAAS,YAAY;GAEnB,MAAM,SAAS,EAAE,OAAA,MADG,UAAU,EACP;GACvB,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,SAAS;EACP,MAAM;EACN,aACE;EACF,YAAY,gBAAgB;EAC5B,SAAS,OAAO,SAA0C;GACxD,MAAM,SAAS,MAAM,QAAQ,EAAE,qBAAqB,KAAK,oBAAoB,CAAC;GAC9E,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,SAAS;EACP,MAAM;EACN,aACE;EACF,YAAY,gBAAgB;EAC5B,SAAS,OAAO,SAIV;GACJ,MAAM,SAAS,MAAM,QAAQ;IAC3B,qBAAqB,KAAK;IAC1B,aAAa,KAAK;IAClB,MAAM,KAAK;GACb,CAAC;GACD,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,YAAY;EACV,MAAM;EACN,aAAa;EACb,YAAY,gBAAgB;EAC5B,SAAS,OAAO,SAA0C;GACxD,MAAM,SAAS,MAAM,WAAW,EAAE,qBAAqB,KAAK,oBAAoB,CAAC;GACjF,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;AACF;;;AC3PA,MAAMC,WAAS,IAAI,cAAc;CAAE,SAAS;CAAO,QAAQ;AAAK,CAAC;AAEjE,MAAM,oBAAoB,OAAO;AACjC,MAAM,iBAAiB;;;;AAavB,SAAS,wBAAwB,MAA+B;CAC9D,OAAO;EACL,MAAM,KAAK;EACX,MAAM,KAAK,WAAW,SAAS,OAAO;CACxC;AACF;;;;AAKA,SAAS,wBAAwB,MAA+B;CAC9D,OAAO;EACL,2BAA2B,KAAK;EAChC,YAAY,OAAO,KAAK,KAAK,MAAM,OAAO;CAC5C;AACF;;;;;AAMA,SAAS,eAAe,wBAAwC;CAC9D,MAAM,UAAU,SAAS,sBAAsB;CAC/C,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,iBAAiB,wBAAwB;CAE3D,OAAO;AACT;;;;AAKA,eAAe,aAKb;CACA,MAAM,YAAY,KAAK,QAAQ,IAAI,GAAG,iCAAiC;CAEvE,IAAI;EAEF,MAAM,gBAAgB,MAAM,iBAAiB,KAAK,WAAW,GAAG,GAAG,EAAE,MAAM,MAAM,CAAC;EA0BlF,QAAO,MAxBc,QAAQ,IAC3B,cAAc,IAAI,OAAO,YAAY;GACnC,MAAM,UAAU,SAAS,OAAO;GAChC,IAAI,CAAC,SAAS,OAAO;GACrB,IAAI;IAMF,MAAM,eAAc,MAJA,cAAc,QAAQ,EACxC,QACF,CAAC,EAAA,CAEyB,eAAe;IAEzC,OAAO;KACL,wBAAwB,KAAK,mCAAmC,OAAO;KACvE;IACF;GACF,SAAS,OAAO;IACd,SAAO,MAAM,kCAAkC,QAAQ,IAAI,YAAY,KAAK,GAAG;IAC/E,OAAO;GACT;EACF,CAAC,CACH,EAAA,CAGc,QAAQ,UAA8C,UAAU,IAAI;CACpF,SAAS,OAAO;EACd,SAAO,MACL,oCAAoC,kCAAkC,KAAK,YAAY,KAAK,GAC9F;EACA,OAAO,CAAC;CACV;AACF;;;;AAKA,eAAe,SAAS,EAAE,0BAKvB;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,UAAU,eAAe,sBAAsB;CAErD,IAAI;EACF,MAAM,QAAQ,MAAM,cAAc,QAAQ,EACxC,QACF,CAAC;EAED,OAAO;GACL,wBAAwB,KAAK,mCAAmC,OAAO;GACvE,aAAa,MAAM,eAAe;GAClC,MAAM,MAAM,QAAQ;GACpB,YAAY,MAAM,cAAc,CAAC,CAAC,IAAI,uBAAuB;EAC/D;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,kCAAkC,uBAAuB,IAAI,YAAY,KAAK,KAC9E,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,eAAe,SAAS,EACtB,wBACA,aACA,MACA,aAAa,CAAC,KAWb;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,UAAU,eAAe,sBAAsB;CAGrD,MAAM,gBACJ,KAAK,UAAU,WAAW,CAAC,CAAC,SAC5B,KAAK,SACL,WAAW,QAAQ,KAAK,SAAS,MAAM,KAAK,KAAK,SAAS,KAAK,KAAK,QAAQ,CAAC;CAC/E,IAAI,gBAAgB,mBAClB,MAAM,IAAI,MACR,cAAc,cAAc,yBAAyB,kBAAkB,mBAAmB,wBAC5F;CAGF,IAAI;EAEF,MAAM,iBAAiB,MAAM,WAAW;EAKxC,IAAI,CAJa,eAAe,MAC7B,UAAU,MAAM,2BAA2B,KAAK,mCAAmC,OAAO,CAGjF,KAAK,eAAe,UAAU,gBACxC,MAAM,IAAI,MACR,6BAA6B,eAAe,eAAe,mCAC7D;EAIF,MAAM,aAAa,WAAW,IAAI,uBAAuB;EAGzD,MAAM,QAAQ,IAAI,cAAc;GAC9B,YAAY,QAAQ,IAAI;GACxB,iBAAiB;GACjB;GACA;GACA;GACA,YAAY;GACZ,UAAU;EACZ,CAAC;EAGD,MAAM,eAAe,KAAK,QAAQ,IAAI,GAAG,mCAAmC,OAAO;EACnF,MAAM,UAAU,YAAY;EAK5B,MAAM,iBAFgB,KAAK,cAAcC,iBAEN,GADV,qBAAqB,MAAM,WACC,CAAC;EAGtD,KAAK,MAAM,QAAQ,YAAY;GAE7B,mBAAmB;IACjB,cAAc,KAAK;IACnB,iBAAiB;GACnB,CAAC;GACD,MAAM,WAAW,KAAK,cAAc,KAAK,IAAI;GAE7C,MAAM,UAAU,KAAK,cAAc,QAAQ,KAAK,IAAI,CAAC;GACrD,IAAI,YAAY,cACd,MAAM,UAAU,OAAO;GAEzB,MAAM,iBAAiB,UAAU,KAAK,IAAI;EAC5C;EAEA,OAAO;GACL,wBAAwB,KAAK,mCAAmC,OAAO;GACvE,aAAa,MAAM,eAAe;GAClC,MAAM,MAAM,QAAQ;GACpB,YAAY,MAAM,cAAc,CAAC,CAAC,IAAI,uBAAuB;EAC/D;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,mCAAmC,uBAAuB,IAAI,YAAY,KAAK,KAC/E,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,eAAe,YAAY,EACzB,0BAKC;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,UAAU,eAAe,sBAAsB;CACrD,MAAM,eAAe,KAAK,QAAQ,IAAI,GAAG,mCAAmC,OAAO;CAEnF,IAAI;EAEF,IAAI,MAAM,gBAAgB,YAAY,GACpC,MAAM,gBAAgB,YAAY;EAGpC,OAAO,EACL,wBAAwB,KAAK,mCAAmC,OAAO,EACzE;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,oCAAoC,uBAAuB,IAAI,YAAY,KAAK,KAChF,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,MAAM,qBAAqB,EAAE,OAAO;CAClC,MAAM,EAAE,OAAO;CACf,MAAM,EAAE,OAAO;AACjB,CAAC;;;;AAKD,MAAM,mBAAmB;CACvB,YAAY,EAAE,OAAO,CAAC,CAAC;CACvB,UAAU,EAAE,OAAO,EACjB,wBAAwB,EAAE,OAAO,EACnC,CAAC;CACD,UAAU,EAAE,OAAO;EACjB,wBAAwB,EAAE,OAAO;EACjC,aAAa;EACb,MAAM,EAAE,OAAO;EACf,YAAY,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;CACpD,CAAC;CACD,aAAa,EAAE,OAAO,EACpB,wBAAwB,EAAE,OAAO,EACnC,CAAC;AACH;;;;AAKA,MAAa,aAAa;CACxB,YAAY;EACV,MAAM;EACN,aAAa,wBAAwB,KAAK,mCAAmC,KAAKA,iBAAe,EAAE;EACnG,YAAY,iBAAiB;EAC7B,SAAS,YAAY;GAEnB,MAAM,SAAS,EAAE,QAAA,MADI,WAAW,EACR;GACxB,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,UAAU;EACR,MAAM;EACN,aACE;EACF,YAAY,iBAAiB;EAC7B,SAAS,OAAO,SAA6C;GAC3D,MAAM,SAAS,MAAM,SAAS,EAAE,wBAAwB,KAAK,uBAAuB,CAAC;GACrF,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,UAAU;EACR,MAAM;EACN,aACE;EACF,YAAY,iBAAiB;EAC7B,SAAS,OAAO,SAKV;GACJ,MAAM,SAAS,MAAM,SAAS;IAC5B,wBAAwB,KAAK;IAC7B,aAAa,KAAK;IAClB,MAAM,KAAK;IACX,YAAY,KAAK;GACnB,CAAC;GACD,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,aAAa;EACX,MAAM;EACN,aACE;EACF,YAAY,iBAAiB;EAC7B,SAAS,OAAO,SAA6C;GAC3D,MAAM,SAAS,MAAM,YAAY,EAAE,wBAAwB,KAAK,uBAAuB,CAAC;GACxF,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;AACF;;;ACrWA,MAAM,SAAS,IAAI,cAAc;CAAE,SAAS;CAAO,QAAQ;AAAK,CAAC;AAEjE,MAAM,uBAAuB,OAAO;AACpC,MAAM,oBAAoB;;;;AAK1B,eAAe,gBAKb;CACA,MAAM,eAAe,KAAK,QAAQ,IAAI,GAAG,oCAAoC;CAE7E,IAAI;EAEF,MAAM,WAAU,MADI,mBAAmB,YAAY,EAAA,CAC7B,QAAQ,SAAS,KAAK,SAAS,KAAK,CAAC;EAyB3D,QAAO,MAvBiB,QAAQ,IAC9B,QAAQ,IAAI,OAAO,SAAS;GAC1B,IAAI;IAOF,MAAM,eAAc,MALG,iBAAiB,SAAS;KAC/C,kBAAkB;KAClB,UAAU;IACZ,CAAC,EAAA,CAE4B,eAAe;IAE5C,OAAO;KACL,qBAAqB,KAAK,sCAAsC,IAAI;KACpE;IACF;GACF,SAAS,OAAO;IACd,OAAO,MAAM,gCAAgC,KAAK,IAAI,YAAY,KAAK,GAAG;IAC1E,OAAO;GACT;EACF,CAAC,CACH,EAAA,CAGiB,QACd,aAAuD,aAAa,IACvE;CACF,SAAS,OAAO;EACd,OAAO,MACL,uCAAuC,qCAAqC,KAAK,YAAY,KAAK,GACpG;EACA,OAAO,CAAC;CACV;AACF;;;;AAKA,eAAe,YAAY,EAAE,uBAI1B;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,WAAW,SAAS,mBAAmB;CAE7C,IAAI;EACF,MAAM,WAAW,MAAM,iBAAiB,SAAS;GAC/C,kBAAkB;GAClB,UAAU;EACZ,CAAC;EAED,OAAO;GACL,qBAAqB,KAAK,sCAAsC,QAAQ;GACxE,aAAa,SAAS,eAAe;GACrC,MAAM,SAAS,QAAQ;EACzB;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,gCAAgC,oBAAoB,IAAI,YAAY,KAAK,KAAK,EAC5F,OAAO,MACT,CAAC;CACH;AACF;;;;AAKA,eAAe,YAAY,EACzB,qBACA,aACA,QASC;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,WAAW,SAAS,mBAAmB;CAG7C,MAAM,gBAAgB,KAAK,UAAU,WAAW,CAAC,CAAC,SAAS,KAAK;CAChE,IAAI,gBAAgB,sBAClB,MAAM,IAAI,MACR,iBAAiB,cAAc,yBAAyB,qBAAqB,mBAAmB,qBAClG;CAGF,IAAI;EAEF,MAAM,oBAAoB,MAAM,cAAc;EAM9C,IAAI,CALa,kBAAkB,MAChC,aACC,SAAS,wBAAwB,KAAK,sCAAsC,QAAQ,CAG5E,KAAK,kBAAkB,UAAU,mBAC3C,MAAM,IAAI,MACR,gCAAgC,kBAAkB,eAAe,sCACnE;EAIF,MAAM,WAAW,IAAI,iBAAiB;GACpC,YAAY,QAAQ,IAAI;GACxB,iBAAiB;GACjB,kBAAkB;GAClB;GACA;GACA,UAAU;EACZ,CAAC;EAID,MAAM,UADe,KAAK,QAAQ,IAAI,GAAG,oCACd,CAAC;EAG5B,MAAM,iBAAiB,SAAS,YAAY,GAAG,SAAS,eAAe,CAAC;EAExE,OAAO;GACL,qBAAqB,KAAK,sCAAsC,QAAQ;GACxE,aAAa,SAAS,eAAe;GACrC,MAAM,SAAS,QAAQ;EACzB;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,iCAAiC,oBAAoB,IAAI,YAAY,KAAK,KAAK,EAC7F,OAAO,MACT,CAAC;CACH;AACF;;;;AAKA,eAAe,eAAe,EAAE,uBAE7B;CACD,mBAAmB;EACjB,cAAc;EACd,iBAAiB,QAAQ,IAAI;CAC/B,CAAC;CAED,MAAM,WAAW,SAAS,mBAAmB;CAC7C,MAAM,WAAW,KAAK,QAAQ,IAAI,GAAG,sCAAsC,QAAQ;CAEnF,IAAI;EACF,MAAM,WAAW,QAAQ;EAEzB,OAAO,EACL,qBAAqB,KAAK,sCAAsC,QAAQ,EAC1E;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,kCAAkC,oBAAoB,IAAI,YAAY,KAAK,KAC3E,EACE,OAAO,MACT,CACF;CACF;AACF;;;;AAKA,MAAM,sBAAsB;CAC1B,eAAe,EAAE,OAAO,CAAC,CAAC;CAC1B,aAAa,EAAE,OAAO,EACpB,qBAAqB,EAAE,OAAO,EAChC,CAAC;CACD,aAAa,EAAE,OAAO;EACpB,qBAAqB,EAAE,OAAO;EAC9B,aAAa;EACb,MAAM,EAAE,OAAO;CACjB,CAAC;CACD,gBAAgB,EAAE,OAAO,EACvB,qBAAqB,EAAE,OAAO,EAChC,CAAC;AACH;;;;AAKA,MAAa,gBAAgB;CAC3B,eAAe;EACb,MAAM;EACN,aAAa,2BAA2B,KAAK,sCAAsC,MAAM,EAAE;EAC3F,YAAY,oBAAoB;EAChC,SAAS,YAAY;GAEnB,MAAM,SAAS,EAAE,WAAA,MADO,cAAc,EACX;GAC3B,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,aAAa;EACX,MAAM;EACN,aACE;EACF,YAAY,oBAAoB;EAChC,SAAS,OAAO,SAA0C;GACxD,MAAM,SAAS,MAAM,YAAY,EAAE,qBAAqB,KAAK,oBAAoB,CAAC;GAClF,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,aAAa;EACX,MAAM;EACN,aACE;EACF,YAAY,oBAAoB;EAChC,SAAS,OAAO,SAIV;GACJ,MAAM,SAAS,MAAM,YAAY;IAC/B,qBAAqB,KAAK;IAC1B,aAAa,KAAK;IAClB,MAAM,KAAK;GACb,CAAC;GACD,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;CACA,gBAAgB;EACd,MAAM;EACN,aAAa;EACb,YAAY,oBAAoB;EAChC,SAAS,OAAO,SAA0C;GACxD,MAAM,SAAS,MAAM,eAAe,EAAE,qBAAqB,KAAK,oBAAoB,CAAC;GACrF,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;EACvC;CACF;AACF;;;ACrPA,MAAM,wBAAwB,EAAE,KAAK;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,MAAM,0BAA0B,EAAE,KAAK;CAAC;CAAQ;CAAO;CAAO;CAAU;AAAK,CAAC;AAE9E,MAAM,kBAAkB,EAAE,OAAO;CAC/B,MAAM,EAAE,OAAO;CACf,MAAM,EAAE,OAAO;AACjB,CAAC;AAED,MAAM,qBAAqB,EAAE,OAAO;CAClC,SAAS;CACT,WAAW;CACX,mBAAmB,EAAE,SAAS,EAAE,OAAO,CAAC;CACxC,aAAa,EAAE,SAAS,EAAE,QAAQ,CAAC;CACnC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC;CAC3B,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;CAC/C,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC;CAC9B,iBAAiB,EAAE,SAAS,qBAAqB;CACjD,eAAe,EAAE,SAAS,mBAAmB;CAC7C,gBAAgB,EAAE,SAAS,oBAAoB;AACjD,CAAC;AAiBD,MAAM,+BAA6E;CACjF,MAAM;EAAC;EAAQ;EAAO;EAAO;CAAQ;CACrC,SAAS;EAAC;EAAQ;EAAO;EAAO;CAAQ;CACxC,UAAU;EAAC;EAAQ;EAAO;EAAO;CAAQ;CACzC,OAAO;EAAC;EAAQ;EAAO;EAAO;CAAQ;CACtC,OAAO;EAAC;EAAQ;EAAO;EAAO;CAAQ;CACtC,QAAQ;EAAC;EAAO;EAAO;CAAQ;CAC/B,KAAK;EAAC;EAAO;EAAO;CAAQ;CAC5B,aAAa;EAAC;EAAO;EAAO;CAAQ;CACpC,OAAO;EAAC;EAAO;EAAO;CAAQ;CAC9B,UAAU,CAAC,KAAK;CAChB,QAAQ,CAAC,KAAK;CACd,SAAS,CAAC,KAAK;AACjB;AAEA,SAAS,gBAAgB,EACvB,SACA,aAIO;CACP,MAAM,sBAAsB,6BAA6B;CAEzD,IAAI,CAAC,oBAAoB,SAAS,SAAS,GACzC,MAAM,IAAI,MACR,aAAa,UAAU,gCAAgC,QAAQ,0BAA0B,oBAAoB,KAC3G,IACF,GACF;AAEJ;AAEA,SAAS,kBAAkB,EAAE,mBAAmB,SAAS,aAAuC;CAC9F,IAAI,CAAC,mBACH,MAAM,IAAI,MAAM,qCAAqC,QAAQ,GAAG,UAAU,WAAW;CAGvF,OAAO;AACT;AA4CA,SAAS,iBAAiB,EACxB,SACA,eAI2D;CAC3D,QAAQ,SAAR;EACE,KAAK,QACH,OAAO,8BAA8B,MAAM,WAAW;EAExD,KAAK,WACH,OAAO,iCAAiC,MAAM,WAAW;EAE3D,KAAK,YACH,OAAO,kCAAkC,MAAM,WAAW;EAE5D,KAAK,SACH,OAAO,+BAA+B,MAAM,WAAW;EAEzD,KAAK,SACH,OAAO,+BAA+B,MAAM,WAAW;CAE3D;AACF;AAEA,SAAS,WAAW,EAAE,MAAM,SAAS,aAAuC;CAC1E,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,wBAAwB,QAAQ,GAAG,UAAU,WAAW;CAG1E,OAAO;AACT;AAEA,SAAS,eAAe,EACtB,SACA,WAIS;CACT,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,2BAA2B,QAAQ,eAAe;CAGpE,OAAO;AACT;AAEA,SAAS,YAAY,QAA0B;CAC7C,IAAI,OAAO,cAAc,QACvB,OAAO,UAAU,UAAU,QAAQ;CAGrC,IAAI,OAAO,cAAc,OACvB,OAAO,UAAU,QAAQ,QAAQ,EAAE,qBAAqB,kBAAkB,MAAM,EAAE,CAAC;CAGrF,IAAI,OAAO,cAAc,OACvB,OAAO,UAAU,QAAQ,QAAQ;EAC/B,qBAAqB,kBAAkB,MAAM;EAC7C,aAAa,iBAAiB;GAC5B,SAAS;GACT,aAAa,OAAO,eAAe,CAAC;EACtC,CAAC;EACD,MAAM,WAAW,MAAM;CACzB,CAAC;CAGH,OAAO,UAAU,WAAW,QAAQ,EAAE,qBAAqB,kBAAkB,MAAM,EAAE,CAAC;AACxF;AAEA,SAAS,eAAe,QAA0B;CAChD,IAAI,OAAO,cAAc,QACvB,OAAO,aAAa,aAAa,QAAQ;CAG3C,IAAI,OAAO,cAAc,OACvB,OAAO,aAAa,WAAW,QAAQ,EACrC,qBAAqB,kBAAkB,MAAM,EAC/C,CAAC;CAGH,IAAI,OAAO,cAAc,OACvB,OAAO,aAAa,WAAW,QAAQ;EACrC,qBAAqB,kBAAkB,MAAM;EAC7C,aAAa,iBAAiB;GAC5B,SAAS;GACT,aAAa,OAAO,eAAe,CAAC;EACtC,CAAC;EACD,MAAM,WAAW,MAAM;CACzB,CAAC;CAGH,OAAO,aAAa,cAAc,QAAQ,EACxC,qBAAqB,kBAAkB,MAAM,EAC/C,CAAC;AACH;AAEA,SAAS,gBAAgB,QAA0B;CACjD,IAAI,OAAO,cAAc,QACvB,OAAO,cAAc,cAAc,QAAQ;CAG7C,IAAI,OAAO,cAAc,OACvB,OAAO,cAAc,YAAY,QAAQ,EACvC,qBAAqB,kBAAkB,MAAM,EAC/C,CAAC;CAGH,IAAI,OAAO,cAAc,OACvB,OAAO,cAAc,YAAY,QAAQ;EACvC,qBAAqB,kBAAkB,MAAM;EAC7C,aAAa,iBAAiB;GAC5B,SAAS;GACT,aAAa,OAAO,eAAe,CAAC;EACtC,CAAC;EACD,MAAM,WAAW,MAAM;CACzB,CAAC;CAGH,OAAO,cAAc,eAAe,QAAQ,EAC1C,qBAAqB,kBAAkB,MAAM,EAC/C,CAAC;AACH;AAEA,SAAS,aAAa,QAA0B;CAC9C,IAAI,OAAO,cAAc,QACvB,OAAO,WAAW,WAAW,QAAQ;CAGvC,IAAI,OAAO,cAAc,OACvB,OAAO,WAAW,SAAS,QAAQ,EAAE,wBAAwB,kBAAkB,MAAM,EAAE,CAAC;CAG1F,IAAI,OAAO,cAAc,OACvB,OAAO,WAAW,SAAS,QAAQ;EACjC,wBAAwB,kBAAkB,MAAM;EAChD,aAAa,iBAAiB;GAC5B,SAAS;GACT,aAAa,OAAO,eAAe,CAAC;EACtC,CAAC;EACD,MAAM,WAAW,MAAM;EACvB,YAAY,OAAO,cAAc,CAAC;CACpC,CAAC;CAGH,OAAO,WAAW,YAAY,QAAQ,EACpC,wBAAwB,kBAAkB,MAAM,EAClD,CAAC;AACH;AAEA,SAAS,aAAa,QAA0B;CAC9C,IAAI,OAAO,cAAc,QACvB,OAAO,WAAW,WAAW,QAAQ;CAGvC,IAAI,OAAO,cAAc,OACvB,OAAO,WAAW,SAAS,QAAQ,EACjC,qBAAqB,kBAAkB,MAAM,EAC/C,CAAC;CAGH,IAAI,OAAO,cAAc,OACvB,OAAO,WAAW,SAAS,QAAQ;EACjC,qBAAqB,kBAAkB,MAAM;EAC7C,aAAa,iBAAiB;GAC5B,SAAS;GACT,aAAa,OAAO,eAAe,CAAC;EACtC,CAAC;EACD,MAAM,WAAW,MAAM;CACzB,CAAC;CAGH,OAAO,WAAW,YAAY,QAAQ,EACpC,qBAAqB,kBAAkB,MAAM,EAC/C,CAAC;AACH;AAEA,SAAS,cAAc,QAA0B;CAC/C,IAAI,OAAO,cAAc,OACvB,OAAO,YAAY,cAAc,QAAQ;CAG3C,IAAI,OAAO,cAAc,OACvB,OAAO,YAAY,cAAc,QAAQ,EACvC,SAAS,eAAe;EAAE,SAAS,OAAO;EAAS,SAAS;CAAS,CAAC,EACxE,CAAC;CAGH,OAAO,YAAY,iBAAiB,QAAQ;AAC9C;AAEA,SAAS,WAAW,QAA0B;CAC5C,IAAI,OAAO,cAAc,OACvB,OAAO,SAAS,WAAW,QAAQ;CAGrC,IAAI,OAAO,cAAc,OACvB,OAAO,SAAS,WAAW,QAAQ,EACjC,SAAS,eAAe;EAAE,SAAS,OAAO;EAAS,SAAS;CAAM,CAAC,EACrE,CAAC;CAGH,OAAO,SAAS,cAAc,QAAQ;AACxC;AAEA,SAAS,mBAAmB,QAA0B;CACpD,IAAI,OAAO,cAAc,OACvB,OAAO,iBAAiB,mBAAmB,QAAQ;CAGrD,IAAI,OAAO,cAAc,OACvB,OAAO,iBAAiB,mBAAmB,QAAQ,EACjD,SAAS,eAAe;EAAE,SAAS,OAAO;EAAS,SAAS;CAAc,CAAC,EAC7E,CAAC;CAGH,OAAO,iBAAiB,sBAAsB,QAAQ;AACxD;AAEA,SAAS,aAAa,QAA0B;CAC9C,IAAI,OAAO,cAAc,OACvB,OAAO,WAAW,aAAa,QAAQ;CAGzC,IAAI,OAAO,cAAc,OACvB,OAAO,WAAW,aAAa,QAAQ,EACrC,SAAS,eAAe;EAAE,SAAS,OAAO;EAAS,SAAS;CAAQ,CAAC,EACvE,CAAC;CAGH,OAAO,WAAW,gBAAgB,QAAQ;AAC5C;AAEA,SAAS,gBAAgB,QAA0B;CAEjD,OAAO,cAAc,gBAAgB,QAAQ,OAAO,mBAAmB,CAAC,CAAC;AAC3E;AAEA,SAAS,cAAc,QAA0B;CAE/C,IAAI,CAAC,OAAO,eACV,MAAM,IAAI,MAAM,8CAA8C;CAEhE,OAAO,YAAY,cAAc,QAAQ,OAAO,aAAa;AAC/D;AAEA,SAAS,eAAe,QAA0B;CAEhD,IAAI,CAAC,OAAO,gBACV,MAAM,IAAI,MAAM,gDAAgD;CAElE,OAAO,aAAa,eAAe,QAAQ,OAAO,cAAc;AAClE;AAEA,MAAM,mBAA2F;CAC/F,MAAM;CACN,SAAS;CACT,UAAU;CACV,OAAO;CACP,OAAO;CACP,QAAQ;CACR,KAAK;CACL,aAAa;CACb,OAAO;CACP,UAAU;CACV,QAAQ;CACR,SAAS;AACX;AAEA,MAAa,eAAe;CAC1B,MAAM;CACN,aACE;CACF,YAAY;CACZ,SAAS,OAAO,SAA2B;EACzC,MAAM,SAAS,mBAAmB,MAAM,IAAI;EAE5C,gBAAgB;GAAE,SAAS,OAAO;GAAS,WAAW,OAAO;EAAU,CAAC;EAExE,MAAM,WAAW,iBAAiB,OAAO;EACzC,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,oBAAoB,OAAO,SAAS;EAGtD,OAAO,SAAS,MAAM;CACxB;AACF;;;;;;AC/bA,eAAsB,WAAW,QAAgB,EAAE,WAA+C;CAChG,MAAM,SAAS,IAAI,QAAQ;EACzB,MAAM;EACG;EACT,cACE;CACJ,CAAC;CAED,OAAO,QAAQ,YAAY;CAG3B,OAAO,KAAK,uCAAuC;CAInD,OAAY,MAAM,EAChB,eAAe,QACjB,CAAC;AACH;;;;;;;;;;;;;;;;;;;ACIA,MAAa,0BAA0B,OAAO,EAC5C,YACA,MAAM,QAAQ,IAAI,QACyD;CAC3E,IAAI,eAAe,KAAA,GACjB,OAAO;CAGT,MAAM,iBAAiB,KAAK,KAAK,kCAAkC;CACnE,MAAM,kBAAkB,KAAK,KAAK,wCAAwC;CAC1E,MAAM,CAAC,SAAS,YAAY,MAAM,QAAQ,IAAI,CAC5C,WAAW,cAAc,GACzB,WAAW,eAAe,CAC5B,CAAC;CAED,IAAI,CAAC,WAAW,CAAC,UACf;CAGF,MAAM,SAAS,MAAM,eAAe,QAAQ,CAAC,CAAC;CAC9C,IAAI,OAAO,wBAAwB,GAAG;EACpC,MAAM,UAAU,OAAO,WAAW;EAClC,IAAI,QAAQ,SAAS,UAAU,GAC7B,OAAO;EAET,OAAO,CAAC,GAAG,SAAS,UAAU;CAChC;AAEF;;;AChDA,MAAM,sBAAsB;AAC5B,MAAM,qBAAqB;;;;AAK3B,MAAM,eAAe,sBAAsB,oBAAoB,GAAG,mBAAmB;;;;AAKrF,MAAM,oBAAoB,MAAM,OAAO;;;;AAKvC,MAAM,2BAA2B;CAC/B;CACA;CACA;CACA;AACF;;;;AAoBA,IAAa,wBAAb,cAA2C,MAAM;CAC/C,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;;;;AAQA,SAAgB,6BAAmD;CACjE,MAAM,WAAW,QAAQ;CACzB,MAAM,aAAa,QAAQ,KAAK,MAAM;CAItC,IADyB,+CAA+C,KAAK,QAC1D,GAAG;EAEpB,IAAI,SAAS,SAAS,YAAY,KAAK,SAAS,SAAS,UAAU,GACjE,OAAO;EAET,OAAO;CACT;CAGA,KACG,WAAW,SAAS,YAAY,KAAK,WAAW,SAAS,UAAU,MACpE,WAAW,SAAS,UAAU,GAE9B,OAAO;CAGT,OAAO;AACT;;;;AAKA,SAAgB,uBAAsC;CACpD,MAAM,WAAWC,KAAG,SAAS;CAC7B,MAAM,OAAOA,KAAG,KAAK;CAGrB,MAAM,cAAsC;EAC1C,QAAQ;EACR,OAAO;EACP,OAAO;CACT;CAEA,MAAM,UAAkC;EACtC,KAAK;EACL,OAAO;CACT;CAEA,MAAM,eAAe,YAAY;CACjC,MAAM,WAAW,QAAQ;CAEzB,IAAI,CAAC,gBAAgB,CAAC,UACpB,OAAO;CAIT,OAAO,YAAY,aAAa,GAAG,WADjB,aAAa,UAAU,SAAS;AAEpD;;;;AAKA,SAAgB,iBAAiB,GAAmB;CAElD,OAAO,EAAE,QAAQ,MAAM,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;AAC/C;;;;;AAMA,SAAgB,gBAAgB,GAAW,GAAmB;CAC5D,MAAM,SAAS,iBAAiB,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;CACxD,MAAM,SAAS,iBAAiB,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;CAExD,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,IAAI,OAAO,QAAQ,OAAO,MAAM,GAAG,KAAK;EAC/D,MAAM,OAAO,OAAO,MAAM;EAC1B,MAAM,OAAO,OAAO,MAAM;EAC1B,IAAI,CAAC,OAAO,SAAS,IAAI,KAAK,CAAC,OAAO,SAAS,IAAI,GACjD,MAAM,IAAI,MAAM,2CAA2C,EAAE,SAAS,EAAE,EAAE;EAE5E,IAAI,OAAO,MAAM,OAAO;EACxB,IAAI,OAAO,MAAM,OAAO;CAC1B;CACA,OAAO;AACT;;;;AAKA,SAAgB,oBAAoB,KAAmB;CACrD,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,GAAG;CACtB,QAAQ;EACN,MAAM,IAAI,MAAM,yBAAyB,KAAK;CAChD;CAEA,IAAI,OAAO,aAAa,UACtB,MAAM,IAAI,MAAM,gCAAgC,KAAK;CAIvD,IAAI,CADc,yBAAyB,MAAM,WAAW,OAAO,aAAa,MACnE,GACX,MAAM,IAAI,MACR,wBAAwB,OAAO,SAAS,gCAAgC,yBAAyB,KAAK,IAAI,GAC5G;CAIF,IAAI,OAAO,aAAa,cAAc;EACpC,MAAM,iBAAiB,IAAI,oBAAoB,GAAG,mBAAmB;EACrE,IAAI,CAAC,OAAO,SAAS,WAAW,cAAc,GAC5C,MAAM,IAAI,MACR,oCAAoC,oBAAoB,GAAG,mBAAmB,IAAI,KACpF;CAEJ;AACF;;;;AAKA,eAAsB,eACpB,gBACA,OAC4B;CAK5B,MAAM,UAAU,MAAM,IAJH,aAAa,EAC9B,OAAO,aAAa,aAAa,KAAK,EACxC,CAE2B,CAAC,CAAC,iBAAiB,qBAAqB,kBAAkB;CACrF,MAAM,gBAAgB,iBAAiB,QAAQ,QAAQ;CACvD,MAAM,2BAA2B,iBAAiB,cAAc;CAEhE,OAAO;EACL,gBAAgB;EAChB;EACA,WAAW,gBAAgB,eAAe,wBAAwB,IAAI;EACtE;CACF;AACF;;;;AAKA,SAAS,UAAU,SAAwB,WAA8C;CACvF,OAAO,QAAQ,OAAO,MAAM,UAAU,MAAM,SAAS,SAAS,KAAK;AACrE;;;;;AAMA,eAAe,aAAa,KAAa,UAAiC;CACxE,oBAAoB,GAAG;CAEvB,MAAM,WAAW,MAAM,MAAM,KAAK,EAChC,UAAU,SACZ,CAAC;CAED,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,sBAAsB,IAAI,SAAS,SAAS,QAAQ;CAItE,IAAI,SAAS,KACX,oBAAoB,SAAS,GAAG;CAGlC,MAAM,gBAAgB,SAAS,QAAQ,IAAI,gBAAgB;CAC3D,IAAI,iBAAiB,OAAO,aAAa,IAAI,mBAC3C,MAAM,IAAI,MACR,uBAAuB,cAAc,0BAA0B,kBAAkB,OACnF;CAGF,IAAI,CAAC,SAAS,MACZ,MAAM,IAAI,MAAM,wBAAwB;CAI1C,MAAM,aAAa,GAAG,kBAAkB,QAAQ;CAChD,IAAI,kBAAkB;CAmBtB,MAAM,SAjBa,SAAS,QAAQ,SAAS,IAiBrB,GAAG,IAfH,UAAU,EAChC,UAAU,OAAO,WAAW,UAAU;EACpC,mBAAoB,MAAiB;EACrC,IAAI,kBAAkB,mBAAmB;GACvC,yBACE,IAAI,MACF,yCAAyC,kBAAkB,wBAC7D,CACF;GACA;EACF;EACA,SAAS,MAAM,KAAK;CACtB,EACF,CAEqC,GAAG,UAAU;AACpD;;;;AAKA,eAAe,gBAAgB,UAAmC;CAChE,MAAM,UAAU,MAAM,GAAG,SAAS,SAAS,QAAQ;CACnD,OAAO,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,OAAO,KAAK;AACjE;;;;AAKA,SAAgB,gBAAgB,SAAsC;CACpE,MAAM,yBAAS,IAAI,IAAoB;CACvC,KAAK,MAAM,QAAQ,QAAQ,MAAM,IAAI,GAAG;EACtC,MAAM,UAAU,KAAK,KAAK;EAC1B,IAAI,CAAC,SAAS;EAEd,MAAM,QAAQ,0BAA0B,KAAK,OAAO;EACpD,IAAI,SAAS,MAAM,MAAM,MAAM,IAC7B,OAAO,IAAI,MAAM,EAAE,CAAC,KAAK,GAAG,MAAM,EAAE;CAExC;CACA,OAAO;AACT;;;;;AAcA,SAAS,oBAAoB,SAI3B;CAEA,MAAM,YAAY,qBAAqB;CACvC,IAAI,CAAC,WACH,MAAM,IAAI,MACR,yBAAyBA,KAAG,SAAS,EAAE,GAAGA,KAAG,KAAK,EAAE,kCAAkC,cACxF;CAIF,MAAM,cAAc,UAAU,SAAS,SAAS;CAChD,IAAI,CAAC,aACH,MAAM,IAAI,MACR,cAAc,UAAU,uDAAuD,cACjF;CAIF,MAAM,gBAAgB,UAAU,SAAS,YAAY;CACrD,IAAI,CAAC,eACH,MAAM,IAAI,MACR,oGAAoG,cACtG;CAGF,OAAO;EAAE;EAAW;EAAa;CAAc;AACjD;;;;;AAMA,eAAe,wBAAwB,QAKnB;CAClB,MAAM,EAAE,SAAS,WAAW,aAAa,kBAAkB;CAC3D,MAAM,iBAAiBC,OAAK,KAAK,SAAS,SAAS;CAGnD,MAAM,aAAa,YAAY,sBAAsB,cAAc;CAGnE,MAAM,gBAAgBA,OAAK,KAAK,SAAS,YAAY;CACrD,MAAM,aAAa,cAAc,sBAAsB,aAAa;CAIpE,MAAM,mBADY,gBAAgB,MADH,GAAG,SAAS,SAAS,eAAe,OAAO,CAEzC,CAAC,CAAC,IAAI,SAAS;CAEhD,IAAI,CAAC,kBACH,MAAM,IAAI,MACR,uBAAuB,UAAU,6DACnC;CAGF,MAAM,iBAAiB,MAAM,gBAAgB,cAAc;CAC3D,IAAI,mBAAmB,kBACrB,MAAM,IAAI,MACR,2CAA2C,iBAAiB,SAAS,eAAe,iCACtF;CAGF,OAAO;AACT;;;;;AAMA,eAAe,qBAAqB,QAIlB;CAChB,MAAM,EAAE,gBAAgB,gBAAgB,eAAe;CAEvD,MAAM,cAAcA,OAAK,KAAK,YAAY,oBAAoB,OAAO,WAAW,GAAG;CACnF,IAAI;EACF,MAAM,GAAG,SAAS,SAAS,gBAAgB,WAAW;EACtD,IAAID,KAAG,SAAS,MAAM,SACpB,MAAM,GAAG,SAAS,MAAM,aAAa,GAAK;EAE5C,MAAM,GAAG,SAAS,OAAO,aAAa,cAAc;CACtD,QAAQ;EAEN,IAAI;GACF,MAAM,GAAG,SAAS,OAAO,WAAW;EACtC,QAAQ,CAER;EAEA,MAAM,GAAG,SAAS,SAAS,gBAAgB,cAAc;EACzD,IAAIA,KAAG,SAAS,MAAM,SACpB,MAAM,GAAG,SAAS,MAAM,gBAAgB,GAAK;CAEjD;AACF;;;;;;AAOA,eAAe,sBAAsB,QAKoB;CACvD,MAAM,EAAE,SAAS,gBAAgB,gBAAgB,kBAAkB;CAGnE,MAAM,iBAAiB,MAAM,GAAG,SAAS,SAAS,QAAQ,QAAQ;CAClE,MAAM,aAAaC,OAAK,QAAQ,cAAc;CAG9C,MAAM,aAAaA,OAAK,KAAK,SAAS,iBAAiB;CACvD,IAAI;EACF,MAAM,GAAG,SAAS,SAAS,gBAAgB,UAAU;CACvD,SAAS,OAAO;EACd,IAAI,kBAAkB,KAAK,GACzB,MAAM,IAAI,sBACR,kCAAkC,eAAe,yBACnD;EAEF,MAAM;CACR;CAEA,IAAI;EACF,MAAM,qBAAqB;GAAE;GAAgB;GAAgB;EAAW,CAAC;EACzE,OAAO;GACL,SAAS,6BAA6B,eAAe,MAAM;GAC3D,eAAe;EACjB;CACF,SAAS,OAAO;EAEd,IAAI;GACF,MAAM,GAAG,SAAS,SAAS,YAAY,cAAc;EACvD,QAAQ;GACN,MAAM,IAAI,mBACR,IAAI,MACF,wEAAwE,WAAW,OAAO,QAAQ,gCAClE,eAAe,oBAAoB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACxH,EAAE,OAAO,MAAM,CACjB,CACF;EACF;EACA,IAAI,kBAAkB,KAAK,GACzB,MAAM,IAAI,sBACR,sCAAsCA,OAAK,QAAQ,cAAc,EAAE,yBACrE;EAEF,MAAM;CACR;AACF;;;;;;;AAQA,IAAM,qBAAN,cAAiC,MAAM;CACrC;CACA,YAAY,OAAc;EACxB,MAAM,MAAM,OAAO;EACnB,KAAK,OAAO;EACZ,KAAK,QAAQ;CACf;AACF;;;;AAKA,eAAsB,oBACpB,gBACA,UAAyB,CAAC,GACT;CACjB,MAAM,EAAE,QAAQ,OAAO,UAAU;CAGjC,MAAM,cAAc,MAAM,eAAe,gBAAgB,KAAK;CAE9D,IAAI,CAAC,YAAY,aAAa,CAAC,OAC7B,OAAO,kCAAkC,eAAe;CAG1D,MAAM,EAAE,WAAW,aAAa,kBAAkB,oBAAoB,YAAY,OAAO;CAGzF,MAAM,UAAU,MAAM,GAAG,SAAS,QAAQA,OAAK,KAAKD,KAAG,OAAO,GAAG,kBAAkB,CAAC;CACpF,IAAI,gBAAgB;CAEpB,IAAI;EAEF,IAAIA,KAAG,SAAS,MAAM,SACpB,MAAM,GAAG,SAAS,MAAM,SAAS,GAAK;EAUxC,MAAM,YAAY,MAAM,sBAAsB;GAC5C;GACA,gBAAA,MAT2B,wBAAwB;IACnD;IACA;IACA;IACA;GACF,CAAC;GAKC;GACA,eAAe,YAAY;EAC7B,CAAC;EACD,gBAAgB,UAAU;EAC1B,OAAO,UAAU;CACnB,SAAS,OAAO;EACd,IAAI,iBAAiB,oBAAoB;GACvC,gBAAgB;GAChB,MAAM,MAAM;EACd;EACA,MAAM;CACR,UAAU;EAER,IAAI,CAAC,eACH,IAAI;GACF,MAAM,GAAG,SAAS,GAAG,SAAS;IAAE,WAAW;IAAM,OAAO;GAAK,CAAC;EAChE,QAAQ,CAER;CAEJ;AACF;;;;AAKA,SAAS,kBAAkB,OAAyB;CAClD,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,UAAU,OAAO;EAClE,MAAM,SAAS;EACf,OAAO,OAAO,YAAY,YAAY,OAAO,YAAY;CAC3D;CACA,OAAO;AACT;;;;AAKA,SAAgB,4BAAoC;CAClD,OAAO;;;;;;;;;;;;AAYT;;;;AAKA,SAAgB,iCAAyC;CACvD,OAAO;;;;AAIT;;;;;;ACxiBA,eAAsB,cACpB,QACA,gBACA,SACe;CACf,MAAM,EAAE,QAAQ,OAAO,QAAQ,OAAO,UAAU;CAEhD,IAAI;EACF,MAAM,cAAc,2BAA2B;EAC/C,OAAO,MAAM,yBAAyB,aAAa;EAEnD,IAAI,gBAAgB,OAAO;GACzB,OAAO,KAAK,0BAA0B,CAAC;GACvC;EACF;EAEA,IAAI,gBAAgB,YAAY;GAC9B,OAAO,KAAK,+BAA+B,CAAC;GAC5C;EACF;EAGA,IAAI,OAAO;GAET,OAAO,KAAK,yBAAyB;GACrC,MAAM,cAAc,MAAM,eAAe,gBAAgB,KAAK;GAG9D,IAAI,OAAO,UAAU;IACnB,OAAO,YAAY,kBAAkB,YAAY,cAAc;IAC/D,OAAO,YAAY,iBAAiB,YAAY,aAAa;IAC7D,OAAO,YAAY,mBAAmB,YAAY,SAAS;IAC3D,OAAO,YACL,WACA,YAAY,YACR,qBAAqB,YAAY,eAAe,MAAM,YAAY,kBAClE,kCAAkC,YAAY,eAAe,EACnE;GACF;GAEA,IAAI,YAAY,WACd,OAAO,QACL,qBAAqB,YAAY,eAAe,MAAM,YAAY,eACpE;QAEA,OAAO,KAAK,kCAAkC,YAAY,eAAe,EAAE;GAE7E;EACF;EAGA,OAAO,KAAK,yBAAyB;EACrC,MAAM,UAAU,MAAM,oBAAoB,gBAAgB;GAAE;GAAO;EAAM,CAAC;EAC1E,OAAO,QAAQ,OAAO;CACxB,SAAS,OAAO;EACd,IAAI,iBAAiB,mBAAmB;GAEtC,MAAM,WACJ,MAAM,eAAe,OAAO,MAAM,eAAe,MAC7C,wHACA;GACN,MAAM,IAAI,SACR,qBAAqB,MAAM,QAAQ,GAAG,YACtC,WAAW,aACb;EACF,OAAO,IAAI,iBAAiB,uBAC1B,MAAM,IAAI,SACR,GAAG,MAAM,QAAQ,kEACjB,WAAW,aACb;EAEF,MAAM;CACR;AACF;;;ACvFA,SAAgB,aAAa,EAC3B,MACA,YACA,cAKS;CACT,OAAO,WAAW,OACd,IAAI,WAAW;EAAE,SAAS;EAAM,SAAS,WAAW;CAAE,CAAC,IACvD,IAAI,cAAc;AACxB;AAEA,SAAgBE,cAAY,EAC1B,MACA,WACA,SACA,YACA,gBAAgB,gBAgBf;CACD,OAAO,OAAO,GAAG,SAAoB;EAKnC,MAAM,UAAU,KAAK,KAAK,SAAS;EACnC,MAAM,UAAU,KAAK,KAAK,SAAS;EACnC,MAAM,iBAAiB,KAAK,MAAM,GAAG,EAAE;EACvC,MAAM,aAAa,QAAQ,QAAQ,KAAK,KAAK,CAAC;EAC9C,MAAM,SAAS,cAAc;GAAE;GAAM;GAAY;EAAW,CAAC;EAI7D,MAAM,mBAAmB;GACvB,SAAS,QAAQ,WAAW,OAAO,KAAK,QAAQ,QAAQ,OAAO;GAC/D,QAAQ,QAAQ,WAAW,MAAM,KAAK,QAAQ,QAAQ,MAAM;EAC9D;EACA,uBAAuB;GAAE,GAAG;GAAkB,UAAU,OAAO;EAAS,CAAC;EACzE,OAAO,UAAU,gBAAgB;EACjC,eAAe,UAAU,gBAAgB;EAEzC,IAAI;GACF,MAAM,QAAQ,QAAQ,SAAS,YAAY,cAAc;GACzD,OAAO,WAAW,IAAI;EACxB,SAAS,OAAO;GACd,MAAM,OAAO,iBAAiB,WAAW,MAAM,OAAO;GACtD,MAAM,WAAW,iBAAiB,QAAQ,QAAQ,YAAY,KAAK;GACnE,OAAO,MAAM,UAAU,IAAI;GAC3B,QAAQ,KAAK,iBAAiB,WAAW,MAAM,WAAW,CAAC;EAC7D;CACF;AACF;;;AC5DA,MAAM,mBAAmB;AACzB,MAAM,gBAAgB,GAAG,aAAa,KAAK,GAAG,EAAE;AAEhD,SAAS,YACP,MACA,WACA,SAMA;CACA,OAAOC,cAAa;EAAE;EAAM;EAAW;EAAS;CAAW,CAAC;AAC9D;AAEA,SAAgB,gBAAyB;CACvC,MAAM,UAAU,IAAI,QAAQ;CAE5B,MAAM,UAAU,WAAW;CAE3B,QACG,KAAK,UAAU,CAAC,CAChB,YAAY,sCAAsC,CAAC,CACnD,QAAQ,SAAS,iBAAiB,cAAc,CAAC,CACjD,OAAO,cAAc,wBAAwB;CAEhD,QACG,QAAQ,MAAM,CAAC,CACf,YAAY,0CAA0C,CAAC,CACvD,OAAO,iBAAiB,gBAAgB,CAAC,CACzC,OAAO,gBAAgB,qBAAqB,CAAC,CAC7C,OACC,YAAY,QAAQ,eAAe,OAAO,WAAW;EACnD,MAAM,YAAY,MAAM;CAC1B,CAAC,CACH;CAEF,QACG,QAAQ,WAAW,CAAC,CACpB,YAAY,mCAAmC,CAAC,CAChD,OACC,yBACA,wFACA,uBACF,CAAC,CACA,OACC,6BACA,gDAAgD,cAAc,mBAC9D,uBACF,CAAC,CACA,OAAO,iBAAiB,gBAAgB,CAAC,CACzC,OAAO,gBAAgB,qBAAqB,CAAC,CAC7C,OACC,YAAY,aAAa,oBAAoB,OAAO,QAAQ,YAAY;EACtE,MAAM,aAAc,QAAmC;EACvD,MAAM,cAAe,QAA4C;EAEjE,MAAM,kBAAkB,MAAM,wBAAwB,EAAE,WAAW,CAAC;EAEpE,MAAM,iBAAiB,QAAQ;GAC7B,SAAS,kBAAkB,CAAC,GAAG,eAAe,IAAI,KAAA;GAClD,UAAU;GACV,SAAU,QAAkC;GAC5C,QAAS,QAAiC;EAC5C,CAAC;CACH,CAAC,CACH;CAEF,QACG,QAAQ,cAAc,CAAC,CACvB,YACC,mHACF,CAAC,CACA,OAAO,iBAAiB,8DAA8D,CAAC,CACvF,OAAO,eAAe,uDAAuD,CAAC,CAC9E,OAAO,qBAAqB,0CAA0C,uBAAuB,CAAC,CAC9F,OAAO,mBAAmB,yCAAyC,uBAAuB,CAAC,CAC3F,OAAO,2BAA2B,uCAAuC,CAAC,CAC1E,OAAO,mBAAmB,uCAAuC,CAAC,CAClE,OAAO,qBAAqB,+BAA+B,CAAC,CAC5D,OAAO,uBAAuB,8BAA8B,CAAC,CAC7D,OAAO,oBAAoB,6BAA6B,CAAC,CACzD,OAAO,sBAAsB,wDAAwD,CAAC,CACtF,OAAO,mBAAmB,uCAAuC,CAAC,CAClE,OAAO,uBAAuB,4BAA4B,CAAC,CAC3D,OAAO,iBAAiB,gBAAgB,CAAC,CACzC,OAAO,gBAAgB,qBAAqB,CAAC,CAC7C,OACC,YAAY,OAAO,cAAc,OAAO,QAAQ,SAAS,aAAa,mBAAmB;EACvF,MAAM,SAAS,eAAe;EAC9B,MAAM,aAAa;EAGnB,MAAM,WAAW,QAAQ;GACvB,GAAG;GACH;GACA,YAAY,WAAW;EACzB,CAAC;CACH,CAAC,CACH;CAEF,QACG,QAAQ,gBAAgB,CAAC,CACzB,YAAY,mDAAmD,CAAC,CAChE,OACC,yBACA,yFACF,CAAC,CACA,OACC,6BACA,8CAA8C,cAAc,oCAC5D,uBACF,CAAC,CACA,OAAO,mBAAmB,0CAA0C,CAAC,CACrE,OAAO,qBAAqB,yCAAyC,CAAC,CACtE,OAAO,sBAAsB,uCAAuC,CAAC,CACrE,OACC,6BACA,oEACF,CAAC,CACA,OAAO,mBAAmB,6CAA6C,CAAC,CACxE,OAAO,iBAAiB,gBAAgB,CAAC,CACzC,OAAO,gBAAgB,qBAAqB,CAAC,CAC7C,OACC,YAAY,SAAS,gBAAgB,OAAO,QAAQ,SAAS,aAAa,mBAAmB;EAC3F,MAAM,SAAS,eAAe;EAC9B,MAAM,aAAa,QAAQ;GAAE,GAAI;GAA0B;EAAO,CAAC;CACrE,CAAC,CACH;CAEF,QACG,QAAQ,QAAQ,CAAC,CACjB,YAAY,wDAAwD,CAAC,CACrE,OACC,wBACA,4DACA,uBACF,CAAC,CACA,OACC,6BACA,+CAA+C,cAAc,mBAC7D,uBACF,CAAC,CACA,OAAO,iBAAiB,gBAAgB,CAAC,CACzC,OAAO,gBAAgB,qBAAqB,CAAC,CAC7C,OAAO,gBAAgB,mDAAmD,CAAC,CAC3E,OACC,4BACA,4DACF,CAAC,CACA,OACC,YAAY,UAAU,iBAAiB,OAAO,QAAQ,YAAY;EAChE,MAAM,EAAE,YAAY,GAAG,kBAAkB;EAGzC,MAAM,cAAc,QAAQ;GAC1B,GAAG;GACH,aAAa,aAAa,CAAC,UAAU,IAAI,KAAA;EAC3C,CAAC;CACH,CAAC,CACH;CAEF,QACG,QAAQ,SAAS,CAAC,CAClB,YACC,4FACF,CAAC,CACA,eAAe,iBAAiB,4DAA4D,CAAC,CAC7F,eACC,gBACA,0EACA,uBACF,CAAC,CACA,OACC,6BACA,gDAAgD,cAAc,mBAC9D,uBACF,CAAC,CACA,OAAO,iBAAiB,gBAAgB,CAAC,CACzC,OAAO,gBAAgB,qBAAqB,CAAC,CAC7C,OAAO,gBAAgB,oDAAoD,CAAC,CAC5E,OAAO,aAAa,6CAA6C,CAAC,CAClE,OACC,YAAY,WAAW,kBAAkB,OAAO,QAAQ,YAAY;EAClE,MAAM,eAAe,QAAQ,OAAyB;CACxD,CAAC,CACH;CAEF,QACG,QAAQ,KAAK,CAAC,CACd,YAAY,+BAA+B,CAAC,CAC5C,OACC,YAAY,OAAO,cAAc,OAAO,QAAQ,aAAa;EAC3D,MAAM,WAAW,QAAQ,EAAE,QAAQ,CAAC;CACtC,CAAC,CACH;CAEF,QACG,QAAQ,SAAS,CAAC,CAClB,YACC,2FACF,CAAC,CACA,OACC,iBACA,8BAA8B,cAAc,KAAK,GAAG,EAAE,qBACxD,CAAC,CACA,OAAO,YAAY,qDAAqD,CAAC,CACzE,OACC,YACA,+FACF,CAAC,CACA,OAAO,mBAAmB,gCAAgC,CAAC,CAC3D,OAAO,uBAAuB,4BAA4B,CAAC,CAC3D,OAAO,iBAAiB,gBAAgB,CAAC,CACzC,OAAO,gBAAgB,qBAAqB,CAAC,CAC7C,OACC,YAAY,WAAW,kBAAkB,OAAO,QAAQ,YAAY;EAClE,MAAM,UAAW,QAA8B;EAE/C,MAAM,eAAe,QAAQ;GAC3B,MAFW,iBAAiB,OAEzB;GACH,QAAS,QAAiC;GAC1C,QAAS,QAAiC;GAC1C,OAAQ,QAA+B;GACvC,YAAa,QAAgC;GAC7C,SAAU,QAAkC;GAC5C,QAAS,QAAiC;EAC5C,CAAC;CACH,CAAC,CACH;CAEF,QACG,QAAQ,UAAU,CAAC,CACnB,YAAY,2CAA2C,CAAC,CACxD,OACC,yBACA,+FACA,uBACF,CAAC,CACA,OACC,6BACA,iDAAiD,cAAc,mBAC/D,uBACF,CAAC,CACA,OAAO,YAAY,mEAAmE,CAAC,CACvF,OACC,8BACA,uFACA,uBACF,CAAC,CACA,OAAO,iBAAiB,gBAAgB,CAAC,CACzC,OAAO,gBAAgB,qBAAqB,CAAC,CAC7C,OAAO,uBAAuB,4BAA4B,CAAC,CAC3D,OAAO,gBAAgB,qDAAqD,CAAC,CAC7E,OACC,uBACA,+FACF,CAAC,CACA,OACC,wBACA,wFACF,CAAC,CACA,OACC,qBACA,6FACF,CAAC,CACA,OACC,uBACA,oEACF,CAAC,CACA,OAAO,aAAa,6CAA6C,CAAC,CAClE,OAAO,WAAW,qEAAqE,CAAC,CACxF,OACC,eACA,0HACF,CAAC,CACA,OACC,YAAY,YAAY,qBAAqB,OAAO,QAAQ,YAAY;EACtE,MAAM,gBAAgB,QAAQ,OAA0B;CAC1D,CAAC,CACH;CAEF,QACG,QAAQ,QAAQ,CAAC,CACjB,YAAY,uCAAuC,CAAC,CACpD,OAAO,WAAW,sCAAsC,CAAC,CACzD,OAAO,WAAW,gDAAgD,CAAC,CACnE,OAAO,mBAAmB,6BAA6B,CAAC,CACxD,OAAO,iBAAiB,gBAAgB,CAAC,CACzC,OAAO,gBAAgB,qBAAqB,CAAC,CAC7C,OACC,YAAY,UAAU,iBAAiB,OAAO,QAAQ,YAAY;EAChE,MAAM,cAAc,QAAQ,SAAS,OAA+B;CACtE,CAAC,CACH;CAEF,OAAO;AACT;AAEA,SAAS,iBAAiB,KAAkD;CAC1E,IAAI,QAAQ,KAAA,GAAW,OAAO,KAAA;CAC9B,MAAM,QAAQ,cAAc,MAAM,MAAM,MAAM,GAAG;CACjD,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,yBAAyB,IAAI,sBAAsB,cAAc,KAAK,IAAI,EAAE,EAAE;CAEhG,OAAO;AACT;;;ACjUA,eAAe,OAAsB;CACnC,cAAc,CAAC,CAAC,MAAM;AACxB;AAEA,KAAK,CAAC,CAAC,OAAO,UAAU;CACtB,QAAQ,MAAM,YAAY,KAAK,CAAC;CAChC,QAAQ,KAAK,CAAC;AAChB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["SKILL_FILE_NAME","record","assertFrozenLockCoversSources","SKILL_FILE_NAME","parseJsonc","parseToolTarget","parseJsonc","fsWatch","SKILL_FILE_NAME","RULESYNC_CONTENT_HASH_REGEX","computeContentHash","RULESYNC_CONTENT_HASH_REGEX","existing","logger","logger","executeConvert","buildSuccessResponse","executeGenerate","buildSuccessResponse","executeImport","logger","logger","SKILL_FILE_NAME","os","path","wrapCommand","_wrapCommand"],"sources":["../../src/utils/parse-comma-separated-list.ts","../../src/lib/feature-scaffold.ts","../../src/lib/npm-sources-lock.ts","../../src/lib/sources-lock.ts","../../src/lib/git-client.ts","../../src/types/fetch-targets.ts","../../src/types/fetch.ts","../../src/lib/github-client.ts","../../src/lib/github-utils.ts","../../src/lib/npm-client.ts","../../src/lib/npm-tar.ts","../../src/types/git-provider.ts","../../src/lib/source-parser.ts","../../src/lib/sources.ts","../../src/cli/commands/add.ts","../../src/utils/result.ts","../../src/cli/commands/convert.ts","../../src/generated/docs-content.ts","../../src/cli/commands/docs.ts","../../src/cli/commands/doctor.ts","../../src/lib/fetch.ts","../../src/cli/commands/fetch.ts","../../src/lib/watch.ts","../../src/cli/commands/generate.ts","../../src/cli/commands/gitignore-derive.ts","../../src/cli/commands/gitignore-entries.ts","../../src/cli/commands/gitignore.ts","../../src/cli/commands/import.ts","../../src/lib/init.ts","../../src/cli/commands/init.ts","../../src/lib/apm/apm-lock.ts","../../src/lib/apm/apm-manifest.ts","../../src/lib/apm/apm-install.ts","../../src/lib/gh/gh-frontmatter.ts","../../src/lib/gh/gh-lock.ts","../../src/lib/gh/gh-paths.ts","../../src/lib/gh/gh-install.ts","../../src/cli/commands/install.ts","../../src/mcp/checks.ts","../../src/mcp/commands.ts","../../src/mcp/convert.ts","../../src/mcp/generate.ts","../../src/mcp/hooks.ts","../../src/mcp/ignore.ts","../../src/mcp/import.ts","../../src/mcp/mcp.ts","../../src/mcp/permissions.ts","../../src/mcp/rules.ts","../../src/mcp/skills.ts","../../src/mcp/subagents.ts","../../src/mcp/tools.ts","../../src/cli/commands/mcp.ts","../../src/cli/commands/resolve-gitignore-targets.ts","../../src/lib/update.ts","../../src/cli/commands/update.ts","../../src/cli/wrap-command.ts","../../src/cli/program.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * Parses a comma-separated string into a trimmed, non-empty array of strings.\n *\n * Handles trailing commas and extra whitespace gracefully.\n *\n * @example\n * parseCommaSeparatedList(\"a, b, c\") // => [\"a\", \"b\", \"c\"]\n * parseCommaSeparatedList(\"a,,b,\") // => [\"a\", \"b\"]\n */\nexport const parseCommaSeparatedList = (value: string): string[] =>\n value\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n","import { join } from \"node:path\";\n\nimport { SKILL_FILE_NAME } from \"../constants/general.js\";\nimport {\n RULESYNC_MCP_SCHEMA_URL,\n RULESYNC_PERMISSIONS_SCHEMA_URL,\n} from \"../constants/rulesync-paths.js\";\nimport { RulesyncCheck } from \"../features/checks/rulesync-check.js\";\nimport { RulesyncCommand } from \"../features/commands/rulesync-command.js\";\nimport { RulesyncHooks } from \"../features/hooks/rulesync-hooks.js\";\nimport { RulesyncIgnore } from \"../features/ignore/rulesync-ignore.js\";\nimport { RulesyncMcp } from \"../features/mcp/rulesync-mcp.js\";\nimport { RulesyncPermissions } from \"../features/permissions/rulesync-permissions.js\";\nimport { RulesyncRule } from \"../features/rules/rulesync-rule.js\";\nimport { RulesyncSkill } from \"../features/skills/rulesync-skill.js\";\nimport { RulesyncSubagent } from \"../features/subagents/rulesync-subagent.js\";\nimport { getRulesyncSourceCandidates } from \"../utils/rulesync-source-path.js\";\n\nexport type ScaffoldFeature =\n | \"rule\"\n | \"command\"\n | \"subagent\"\n | \"skill\"\n | \"check\"\n | \"mcp\"\n | \"hooks\"\n | \"ignore\"\n | \"permissions\";\n\nexport type FeatureScaffold = {\n feature: ScaffoldFeature;\n relativeFilePath: string;\n candidateRelativeFilePaths: string[];\n content: string;\n};\n\nconst FEATURE_KEYWORDS = new Map<string, ScaffoldFeature>([\n [\"rule\", \"rule\"],\n [\"rules\", \"rule\"],\n [\"command\", \"command\"],\n [\"commands\", \"command\"],\n [\"subagent\", \"subagent\"],\n [\"subagents\", \"subagent\"],\n [\"skill\", \"skill\"],\n [\"skills\", \"skill\"],\n [\"check\", \"check\"],\n [\"checks\", \"check\"],\n [\"mcp\", \"mcp\"],\n [\"hook\", \"hooks\"],\n [\"hooks\", \"hooks\"],\n [\"ignore\", \"ignore\"],\n [\"permission\", \"permissions\"],\n [\"permissions\", \"permissions\"],\n]);\n\nconst NAMED_FEATURES = new Set<ScaffoldFeature>([\"rule\", \"command\", \"subagent\", \"skill\", \"check\"]);\n\nexport function parseScaffoldFeatureKeyword(value: string): ScaffoldFeature | undefined {\n return FEATURE_KEYWORDS.get(value.toLowerCase());\n}\n\nexport function isNamedScaffoldFeature(feature: ScaffoldFeature): boolean {\n return NAMED_FEATURES.has(feature);\n}\n\nexport function normalizeScaffoldName({\n feature,\n name,\n}: {\n feature: ScaffoldFeature;\n name: string | undefined;\n}): string | undefined {\n if (!isNamedScaffoldFeature(feature)) {\n if (name !== undefined) {\n throw new Error(`Feature \"${feature}\" does not accept --name.`);\n }\n return undefined;\n }\n\n if (name === undefined || name.trim() === \"\") {\n throw new Error(`Feature \"${feature}\" requires --name <name>.`);\n }\n\n const normalized = name.trim().replace(/\\.md$/i, \"\");\n if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(normalized)) {\n throw new Error(\n `Invalid ${feature} name \"${name}\". Use letters, numbers, dots, underscores, or hyphens without path separators.`,\n );\n }\n return normalized;\n}\n\nfunction titleFromName(name: string): string {\n return name\n .split(/[-_.]+/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\" \");\n}\n\nfunction namedFeatureScaffold({\n feature,\n relativeFilePath,\n content,\n}: {\n feature: ScaffoldFeature;\n relativeFilePath: string;\n content: string;\n}): FeatureScaffold {\n return {\n feature,\n relativeFilePath,\n candidateRelativeFilePaths: [relativeFilePath],\n content,\n };\n}\n\nfunction ruleTemplate(name: string): string {\n if (name === \"overview\") {\n return `---\nroot: true\ntargets: [\"*\"]\ndescription: \"Project overview and general development guidelines\"\nglobs: [\"**/*\"]\n---\n\n# Project Overview\n\n## General Guidelines\n\n- Use TypeScript for all new code\n- Follow consistent naming conventions\n- Write self-documenting code with clear variable and function names\n- Prefer composition over inheritance\n- Use meaningful comments for complex business logic\n\n## Code Style\n\n- Use 2 spaces for indentation\n- Use semicolons\n- Use double quotes for strings\n- Use trailing commas in multi-line objects and arrays\n\n## Architecture Principles\n\n- Organize code by feature, not by file type\n- Keep related files close together\n- Use dependency injection for better testability\n- Implement proper error handling\n- Follow single responsibility principle\n`;\n }\n\n const title = titleFromName(name);\n return `---\nroot: false\ntargets: [\"*\"]\ndescription: \"${title} guidelines\"\nglobs: [\"**/*\"]\n---\n\n# ${title}\n\nDescribe the project guidance that should apply when the configured globs match.\n`;\n}\n\nfunction commandTemplate(name: string): string {\n if (name === \"review-pr\") {\n return `---\ndescription: 'Review a pull request'\ntargets: [\"*\"]\n---\n\ntarget_pr = $ARGUMENTS\n\nIf target_pr is not provided, use the PR of the current branch.\n\nExecute the following in parallel:\n\n1. Check code quality and style consistency\n2. Review test coverage\n3. Verify documentation updates\n4. Check for potential bugs or security issues\n\nThen provide a summary of findings and suggestions for improvement.\n`;\n }\n\n const title = titleFromName(name);\n return `---\ndescription: \"Run the ${title} workflow\"\ntargets: [\"*\"]\n---\n\n# ${title}\n\nUse $ARGUMENTS as input and describe the steps this command should perform.\n`;\n}\n\nfunction subagentTemplate(name: string): string {\n if (name === \"planner\") {\n return `---\nname: planner\ntargets: [\"*\"]\ndescription: >-\n This is the general-purpose planner. The user asks the agent to plan to\n suggest a specification, implement a new feature, refactor the codebase, or\n fix a bug. This agent can be called by the user explicitly only.\nclaudecode:\n model: inherit\n---\n\nYou are the planner for any tasks.\n\nBased on the user's instruction, create a plan while analyzing the related files. Then, report the plan in detail. You can output files to @tmp/ if needed.\n\nAttention, again, you are just the planner, so though you can read any files and run any commands for analysis, please don't write any code.\n`;\n }\n\n const title = titleFromName(name);\n return `---\nname: ${JSON.stringify(name)}\ntargets: [\"*\"]\ndescription: \"${title} specialist\"\n---\n\nYou are the ${title} specialist. Describe the role, constraints, and expected output here.\n`;\n}\n\nfunction skillTemplate(name: string): string {\n if (name === \"project-context\") {\n return `---\nname: project-context\ndescription: \"Summarize the project context and key constraints\"\ntargets: [\"*\"]\n---\n\nSummarize the project goals, core constraints, and relevant dependencies.\nCall out any architecture decisions, shared conventions, and validation steps.\nKeep the summary concise and ready to reuse in future tasks.`;\n }\n\n const title = titleFromName(name);\n return `---\nname: ${JSON.stringify(name)}\ndescription: \"Use ${title} guidance for relevant tasks\"\ntargets: [\"*\"]\n---\n\n# ${title}\n\nDescribe when to use this skill and the workflow it should follow.\n`;\n}\n\nfunction checkTemplate(name: string): string {\n const title = titleFromName(name);\n return `---\ntargets: [\"*\"]\ndescription: \"${title} review criteria\"\nseverity: medium\n---\n\n# ${title}\n\nDescribe the conditions this check should detect and the evidence it should report.\n`;\n}\n\nfunction singletonTemplate(feature: ScaffoldFeature): string {\n switch (feature) {\n case \"mcp\":\n return `{\n \"$schema\": \"${RULESYNC_MCP_SCHEMA_URL}\",\n \"mcpServers\": {\n \"deepwiki\": {\n \"type\": \"http\",\n \"url\": \"https://mcp.deepwiki.com/mcp\",\n \"env\": {}\n },\n \"rulesync\": {\n \"type\": \"stdio\",\n \"command\": \"pnpm\",\n \"args\": [\n \"dlx\",\n \"rulesync\",\n \"mcp\"\n ],\n \"env\": {}\n },\n \"playwright\": {\n \"type\": \"stdio\",\n \"command\": \"pnpm\",\n \"args\": [\n \"dlx\",\n \"@playwright/mcp\",\n \"--headless\"\n ],\n \"env\": {}\n }\n }\n}\n`;\n case \"hooks\":\n return `{\n \"version\": 1,\n \"hooks\": {\n \"postToolUse\": [\n {\n \"matcher\": \"Write|Edit\",\n \"command\": \".rulesync/hooks/format.sh\"\n }\n ]\n }\n}\n`;\n case \"ignore\":\n return `credentials/\n`;\n case \"permissions\":\n return `{\n \"$schema\": \"${RULESYNC_PERMISSIONS_SCHEMA_URL}\",\n \"permission\": {\n \"bash\": {\n \"git status\": \"allow\",\n \"git diff\": \"allow\",\n \"ls *\": \"allow\",\n \"rm -rf *\": \"deny\",\n \"*\": \"ask\"\n },\n \"edit\": {\n \"src/**\": \"allow\"\n },\n \"read\": {\n \".env\": \"deny\",\n \"credentials/**\": \"deny\"\n }\n },\n \"codexcli\": {\n \"approval_policy\": \"on-request\",\n \"approvals_reviewer\": \"auto_review\",\n \"base_permission_profile\": \":danger-full-access\"\n }\n}\n`;\n default:\n throw new Error(`Feature \"${feature}\" requires a name.`);\n }\n}\n\nexport function createFeatureScaffold({\n feature,\n name,\n}: {\n feature: ScaffoldFeature;\n name?: string;\n}): FeatureScaffold {\n const normalizedName = normalizeScaffoldName({ feature, name });\n\n switch (feature) {\n case \"rule\":\n return namedFeatureScaffold({\n feature,\n relativeFilePath: join(\n RulesyncRule.getSettablePaths().recommended.relativeDirPath,\n `${normalizedName}.md`,\n ),\n content: ruleTemplate(normalizedName!),\n });\n case \"command\":\n return namedFeatureScaffold({\n feature,\n relativeFilePath: join(\n RulesyncCommand.getSettablePaths().relativeDirPath,\n `${normalizedName}.md`,\n ),\n content: commandTemplate(normalizedName!),\n });\n case \"subagent\":\n return namedFeatureScaffold({\n feature,\n relativeFilePath: join(\n RulesyncSubagent.getSettablePaths().relativeDirPath,\n `${normalizedName}.md`,\n ),\n content: subagentTemplate(normalizedName!),\n });\n case \"skill\":\n return namedFeatureScaffold({\n feature,\n relativeFilePath: join(\n RulesyncSkill.getSettablePaths().relativeDirPath,\n normalizedName!,\n SKILL_FILE_NAME,\n ),\n content: skillTemplate(normalizedName!),\n });\n case \"check\":\n return namedFeatureScaffold({\n feature,\n relativeFilePath: join(\n RulesyncCheck.getSettablePaths().relativeDirPath,\n `${normalizedName}.md`,\n ),\n content: checkTemplate(normalizedName!),\n });\n case \"mcp\": {\n const paths = RulesyncMcp.getSettablePaths();\n const relativeFilePath = join(\n paths.recommended.relativeDirPath,\n paths.recommended.relativeFilePath,\n );\n return {\n feature,\n relativeFilePath,\n candidateRelativeFilePaths: getRulesyncSourceCandidates({ paths }).map((candidate) =>\n join(candidate.relativeDirPath, candidate.relativeFilePath),\n ),\n content: singletonTemplate(feature),\n };\n }\n case \"hooks\": {\n const paths = RulesyncHooks.getSettablePaths();\n const relativeFilePath = join(\n paths.recommended.relativeDirPath,\n paths.recommended.relativeFilePath,\n );\n return {\n feature,\n relativeFilePath,\n candidateRelativeFilePaths: getRulesyncSourceCandidates({ paths }).map((candidate) =>\n join(candidate.relativeDirPath, candidate.relativeFilePath),\n ),\n content: singletonTemplate(feature),\n };\n }\n case \"ignore\": {\n const paths = RulesyncIgnore.getSettablePaths();\n const relativeFilePath = join(\n paths.recommended.relativeDirPath,\n paths.recommended.relativeFilePath,\n );\n return {\n feature,\n relativeFilePath,\n candidateRelativeFilePaths: [\n relativeFilePath,\n ...(paths.legacy\n ? [join(paths.legacy.relativeDirPath, paths.legacy.relativeFilePath)]\n : []),\n ],\n content: singletonTemplate(feature),\n };\n }\n case \"permissions\": {\n const paths = RulesyncPermissions.getSettablePaths();\n const relativeFilePath = join(\n paths.recommended.relativeDirPath,\n paths.recommended.relativeFilePath,\n );\n return {\n feature,\n relativeFilePath,\n candidateRelativeFilePaths: getRulesyncSourceCandidates({ paths }).map((candidate) =>\n join(candidate.relativeDirPath, candidate.relativeFilePath),\n ),\n content: singletonTemplate(feature),\n };\n }\n }\n}\n","import { join } from \"node:path\";\n\nimport { optional, z } from \"zod/mini\";\n\nimport { RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH } from \"../constants/rulesync-paths.js\";\nimport { fileExists, readFileContent, writeFileContent } from \"../utils/file.js\";\nimport type { Logger } from \"../utils/logger.js\";\n\n/**\n * Lockfile for npm-transport sources (EXPERIMENTAL), written to\n * `rulesync-npm.lock.json` at the project root. Kept separate from the main\n * `rulesync.lock` because that lockfile pins git commit SHAs, while npm\n * sources pin a resolved package version plus the registry tarball integrity.\n * Mirrors the conventions of the gh (`rulesync-gh.lock.yaml`) and apm\n * (`rulesync-apm.lock.yaml`) lockfiles, which are also mode/transport-specific.\n */\n\n/** Current npm lockfile format version. Bump when the schema changes. */\nexport const NPM_LOCKFILE_VERSION = 1;\n\nconst NpmLockedSkillSchema = z.object({\n integrity: z.string(),\n});\n\n/**\n * Schema for a single locked npm source entry.\n */\nconst NpmLockedSourceSchema = z.object({\n registry: optional(z.string()),\n requestedVersion: optional(z.string()),\n resolvedVersion: z.string(),\n /** SRI integrity of the package tarball as reported by the registry. */\n integrity: optional(z.string()),\n resolvedAt: optional(z.string()),\n skills: z.record(z.string(), NpmLockedSkillSchema),\n rules: optional(z.record(z.string(), NpmLockedSkillSchema)),\n ruleSelection: optional(z.array(z.string())),\n rulesPath: optional(z.string()),\n resolvedRuleNames: optional(z.array(z.string())),\n});\nexport type NpmLockedSource = z.infer<typeof NpmLockedSourceSchema>;\n\nconst NpmSourcesLockSchema = z.object({\n lockfileVersion: z.number(),\n sources: z.record(z.string(), NpmLockedSourceSchema),\n});\nexport type NpmSourcesLock = z.infer<typeof NpmSourcesLockSchema>;\n\n/**\n * Create an empty npm lockfile structure.\n */\nexport function createEmptyNpmLock(): NpmSourcesLock {\n return { lockfileVersion: NPM_LOCKFILE_VERSION, sources: {} };\n}\n\n/**\n * Read the npm lockfile from disk.\n * @returns The parsed lockfile, or an empty lockfile if it doesn't exist or is invalid.\n */\nexport async function readNpmLockFile(params: {\n projectRoot: string;\n logger: Logger;\n}): Promise<NpmSourcesLock> {\n const { logger } = params;\n const lockPath = join(params.projectRoot, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH);\n\n if (!(await fileExists(lockPath))) {\n logger.debug(\"No npm sources lockfile found, starting fresh.\");\n return createEmptyNpmLock();\n }\n\n try {\n const content = await readFileContent(lockPath);\n const result = NpmSourcesLockSchema.safeParse(JSON.parse(content));\n if (result.success) {\n return result.data;\n }\n logger.warn(\n `Invalid npm sources lockfile format (${RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH}). Starting fresh.`,\n );\n return createEmptyNpmLock();\n } catch {\n logger.warn(\n `Failed to read npm sources lockfile (${RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH}). Starting fresh.`,\n );\n return createEmptyNpmLock();\n }\n}\n\n/**\n * Write the npm lockfile to disk.\n */\nexport async function writeNpmLockFile(params: {\n projectRoot: string;\n lock: NpmSourcesLock;\n logger: Logger;\n}): Promise<void> {\n const { logger } = params;\n const lockPath = join(params.projectRoot, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH);\n const content = JSON.stringify(params.lock, null, 2) + \"\\n\";\n await writeFileContent(lockPath, content);\n logger.debug(`Wrote npm sources lockfile to ${lockPath}`);\n}\n\n/**\n * Normalize an npm source key (package name) for lockfile lookups.\n */\nexport function normalizeNpmSourceKey(source: string): string {\n return source.trim();\n}\n\n/**\n * Get the locked entry for an npm source key, if it exists.\n */\nexport function getNpmLockedSource(\n lock: NpmSourcesLock,\n sourceKey: string,\n): NpmLockedSource | undefined {\n const normalized = normalizeNpmSourceKey(sourceKey);\n return Object.prototype.hasOwnProperty.call(lock.sources, normalized)\n ? lock.sources[normalized]\n : undefined;\n}\n\n/**\n * Set (or update) a locked entry for an npm source key (immutable).\n */\nexport function setNpmLockedSource(\n lock: NpmSourcesLock,\n sourceKey: string,\n entry: NpmLockedSource,\n): NpmSourcesLock {\n return {\n lockfileVersion: lock.lockfileVersion,\n sources: {\n ...lock.sources,\n [normalizeNpmSourceKey(sourceKey)]: entry,\n },\n };\n}\n\n/**\n * Get the skill names from a locked npm source entry.\n */\nexport function getNpmLockedSkillNames(entry: NpmLockedSource): string[] {\n return Object.keys(entry.skills);\n}\n\n/** Get the rule names from a locked npm source entry. */\nexport function getNpmLockedRuleNames(entry: NpmLockedSource): string[] {\n return Object.keys(entry.rules ?? {});\n}\n","import { createHash } from \"node:crypto\";\nimport { join } from \"node:path\";\n\nimport { optional, refine, z } from \"zod/mini\";\n\nimport { RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH } from \"../constants/rulesync-paths.js\";\nimport { fileExists, readFileContent, writeFileContent } from \"../utils/file.js\";\nimport type { Logger } from \"../utils/logger.js\";\n\n/** Current lockfile format version. Bump when the schema changes. */\nexport const LOCKFILE_VERSION = 1;\n\n/**\n * Schema for a single locked skill entry with content integrity.\n */\nconst LockedSkillSchema = z.object({\n integrity: z.string(),\n});\nexport type LockedSkill = z.infer<typeof LockedSkillSchema>;\n\n/** Schema for a single locked rule entry with content integrity. */\nconst LockedRuleSchema = z.object({\n integrity: z.string(),\n});\nexport type LockedRule = z.infer<typeof LockedRuleSchema>;\n\n/**\n * Schema for a single locked source entry.\n */\nconst LockedSourceSchema = z.object({\n requestedRef: optional(z.string()),\n resolvedRef: z\n .string()\n .check(refine((v) => /^[0-9a-f]{40}$/.test(v), \"resolvedRef must be a 40-character hex SHA\")),\n resolvedAt: optional(z.string()),\n skills: z.record(z.string(), LockedSkillSchema),\n rules: optional(z.record(z.string(), LockedRuleSchema)),\n ruleSelection: optional(z.array(z.string())),\n rulesPath: optional(z.string()),\n resolvedRuleNames: optional(z.array(z.string())),\n});\nexport type LockedSource = z.infer<typeof LockedSourceSchema>;\n\n/**\n * Schema for the full lockfile (current version).\n */\nconst SourcesLockSchema = z.object({\n lockfileVersion: z.number(),\n sources: z.record(z.string(), LockedSourceSchema),\n});\nexport type SourcesLock = z.infer<typeof SourcesLockSchema>;\n\n/**\n * Schema for the legacy v0 lockfile format (skills as string array, no version field).\n */\nconst LegacyLockedSourceSchema = z.object({\n resolvedRef: z.string(),\n skills: z.array(z.string()),\n});\n\nconst LegacySourcesLockSchema = z.object({\n sources: z.record(z.string(), LegacyLockedSourceSchema),\n});\n\n/**\n * Migrate a legacy lockfile (string[] skills, no version) to the current format.\n * Skills get empty integrity since we can't compute it retroactively.\n */\nfunction migrateLegacyLock(params: {\n legacy: z.infer<typeof LegacySourcesLockSchema>;\n logger: Logger;\n}): SourcesLock {\n const { legacy, logger } = params;\n const sources: Record<string, LockedSource> = {};\n for (const [key, entry] of Object.entries(legacy.sources)) {\n const skills: Record<string, LockedSkill> = {};\n for (const name of entry.skills) {\n skills[name] = { integrity: \"\" };\n }\n sources[key] = {\n resolvedRef: entry.resolvedRef,\n skills,\n };\n }\n logger.info(\n \"Migrated legacy sources lockfile to version 1. Run 'rulesync install --update' to populate integrity hashes.\",\n );\n return { lockfileVersion: LOCKFILE_VERSION, sources };\n}\n\n/**\n * Create an empty lockfile structure.\n */\nexport function createEmptyLock(): SourcesLock {\n return { lockfileVersion: LOCKFILE_VERSION, sources: {} };\n}\n\n/**\n * Read the lockfile from disk.\n * @returns The parsed lockfile, or an empty lockfile if it doesn't exist or is invalid.\n */\nexport async function readLockFile(params: {\n projectRoot: string;\n logger: Logger;\n}): Promise<SourcesLock> {\n const { logger } = params;\n const lockPath = join(params.projectRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH);\n\n if (!(await fileExists(lockPath))) {\n logger.debug(\"No sources lockfile found, starting fresh.\");\n return createEmptyLock();\n }\n\n try {\n const content = await readFileContent(lockPath);\n const data = JSON.parse(content);\n\n // Try current schema first\n const result = SourcesLockSchema.safeParse(data);\n if (result.success) {\n return result.data;\n }\n\n // Try legacy schema (no lockfileVersion, skills as string[])\n const legacyResult = LegacySourcesLockSchema.safeParse(data);\n if (legacyResult.success) {\n return migrateLegacyLock({ legacy: legacyResult.data, logger });\n }\n\n logger.warn(\n `Invalid sources lockfile format (${RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH}). Starting fresh.`,\n );\n return createEmptyLock();\n } catch {\n logger.warn(\n `Failed to read sources lockfile (${RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH}). Starting fresh.`,\n );\n return createEmptyLock();\n }\n}\n\n/**\n * Write the lockfile to disk.\n */\nexport async function writeLockFile(params: {\n projectRoot: string;\n lock: SourcesLock;\n logger: Logger;\n}): Promise<void> {\n const { logger } = params;\n const lockPath = join(params.projectRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH);\n const content = JSON.stringify(params.lock, null, 2) + \"\\n\";\n await writeFileContent(lockPath, content);\n logger.debug(`Wrote sources lockfile to ${lockPath}`);\n}\n\n/**\n * Compute a SHA-256 integrity hash for a skill's contents.\n * Takes a sorted list of [relativePath, content] pairs to produce a deterministic hash.\n */\nexport function computeSkillIntegrity(files: Array<{ path: string; content: string }>): string {\n const hash = createHash(\"sha256\");\n // Sort by path for deterministic ordering\n const sorted = files.toSorted((a, b) => a.path.localeCompare(b.path));\n for (const file of sorted) {\n hash.update(file.path);\n hash.update(\"\\0\");\n hash.update(file.content);\n hash.update(\"\\0\");\n }\n return `sha256-${hash.digest(\"hex\")}`;\n}\n\n/** Compute a SHA-256 integrity hash for one rule file. */\nexport function computeRuleIntegrity(content: string): string {\n const hash = createHash(\"sha256\");\n hash.update(content);\n return `sha256-${hash.digest(\"hex\")}`;\n}\n\n/**\n * Normalize a source key for consistent lockfile lookups.\n * Strips URL prefixes, provider prefixes, trailing slashes, .git suffix, and lowercases.\n */\nexport function normalizeSourceKey(source: string): string {\n let key = source;\n\n // Strip common URL prefixes\n for (const prefix of [\n \"https://www.github.com/\",\n \"https://github.com/\",\n \"http://www.github.com/\",\n \"http://github.com/\",\n \"https://www.gitlab.com/\",\n \"https://gitlab.com/\",\n \"http://www.gitlab.com/\",\n \"http://gitlab.com/\",\n ]) {\n if (key.toLowerCase().startsWith(prefix)) {\n key = key.substring(prefix.length);\n break;\n }\n }\n\n // Strip provider prefix\n for (const provider of [\"github:\", \"gitlab:\"]) {\n if (key.startsWith(provider)) {\n key = key.substring(provider.length);\n break;\n }\n }\n\n // Remove trailing slashes\n key = key.replace(/\\/+$/, \"\");\n\n // Remove .git suffix from repo\n key = key.replace(/\\.git$/, \"\");\n\n // Lowercase for case-insensitive matching\n key = key.toLowerCase();\n\n return key;\n}\n\n/**\n * Get the locked entry for a source key, if it exists.\n */\nexport function getLockedSource(lock: SourcesLock, sourceKey: string): LockedSource | undefined {\n const normalized = normalizeSourceKey(sourceKey);\n // Look up by normalized key\n for (const [key, value] of Object.entries(lock.sources)) {\n if (normalizeSourceKey(key) === normalized) {\n return value;\n }\n }\n return undefined;\n}\n\n/**\n * Set (or update) a locked entry for a source key.\n */\nexport function setLockedSource(\n lock: SourcesLock,\n sourceKey: string,\n entry: LockedSource,\n): SourcesLock {\n const normalized = normalizeSourceKey(sourceKey);\n // Remove any existing entries with the same normalized key\n const filteredSources: Record<string, LockedSource> = {};\n for (const [key, value] of Object.entries(lock.sources)) {\n if (normalizeSourceKey(key) !== normalized) {\n filteredSources[key] = value;\n }\n }\n return {\n lockfileVersion: lock.lockfileVersion,\n sources: {\n ...filteredSources,\n [normalized]: entry,\n },\n };\n}\n\n/**\n * Get the skill names from a locked source entry.\n */\nexport function getLockedSkillNames(entry: LockedSource): string[] {\n return Object.keys(entry.skills);\n}\n\n/** Get the rule names from a locked source entry. */\nexport function getLockedRuleNames(entry: LockedSource): string[] {\n return Object.keys(entry.rules ?? {});\n}\n","import { execFile } from \"node:child_process\";\nimport { isAbsolute, join, posix, relative } from \"node:path\";\nimport { promisify } from \"node:util\";\n\nimport { MAX_FILE_SIZE } from \"../constants/rulesync-paths.js\";\nimport {\n createTempDirectory,\n directoryExists,\n getFileSize,\n isSymlink,\n listDirectoryFiles,\n readFileContent,\n removeTempDirectory,\n} from \"../utils/file.js\";\nimport type { Logger } from \"../utils/logger.js\";\nimport { findControlCharacter } from \"../utils/validation.js\";\n\nconst execFileAsync = promisify(execFile);\n\n/** Timeout for all git CLI operations (60 seconds). */\nconst GIT_TIMEOUT_MS = 60_000;\n\nconst ALLOWED_URL_SCHEMES =\n /^(https?:\\/\\/|ssh:\\/\\/|git:\\/\\/|file:\\/\\/\\/).+$|^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9.-]+:[a-zA-Z0-9_.+/~-]+$/;\n\nconst INSECURE_URL_SCHEMES = /^(git:\\/\\/|http:\\/\\/)/;\n\nexport class GitClientError extends Error {\n constructor(message: string, cause?: unknown) {\n super(message, { cause });\n this.name = \"GitClientError\";\n }\n}\n\nexport function validateGitUrl(url: string, options?: { logger?: Logger }): void {\n const ctrl = findControlCharacter(url);\n if (ctrl) {\n throw new GitClientError(\n `Git URL contains control character ${ctrl.hex} at position ${ctrl.position}`,\n );\n }\n if (!ALLOWED_URL_SCHEMES.test(url)) {\n throw new GitClientError(\n `Unsupported or unsafe git URL: \"${url}\". Use https, ssh, git, or file schemes.`,\n );\n }\n if (INSECURE_URL_SCHEMES.test(url)) {\n options?.logger?.warn(\n `URL \"${url}\" uses an unencrypted protocol. Consider using https:// or ssh:// instead.`,\n );\n }\n}\n\n/**\n * Validate a ref string before passing to git commands.\n * Rejects refs that start with \"-\" or contain control characters.\n */\nexport function validateRef(ref: string): void {\n if (ref.startsWith(\"-\")) {\n throw new GitClientError(`Ref must not start with \"-\": \"${ref}\"`);\n }\n const ctrl = findControlCharacter(ref);\n if (ctrl) {\n throw new GitClientError(\n `Ref contains control character ${ctrl.hex} at position ${ctrl.position}`,\n );\n }\n}\n\nlet gitChecked = false;\n\nexport async function checkGitAvailable(): Promise<void> {\n if (gitChecked) return;\n try {\n await execFileAsync(\"git\", [\"--version\"], { timeout: GIT_TIMEOUT_MS });\n gitChecked = true;\n } catch {\n throw new GitClientError(\"git is not installed or not found in PATH\");\n }\n}\n\n/** Reset the cached git availability check (for testing). */\nexport function resetGitCheck(): void {\n gitChecked = false;\n}\n\nexport async function resolveDefaultRef(url: string): Promise<{ ref: string; sha: string }> {\n validateGitUrl(url);\n await checkGitAvailable();\n try {\n const { stdout } = await execFileAsync(\"git\", [\"ls-remote\", \"--symref\", \"--\", url, \"HEAD\"], {\n timeout: GIT_TIMEOUT_MS,\n });\n const ref = stdout.match(/^ref: refs\\/heads\\/(.+)\\tHEAD$/m)?.[1];\n const sha = stdout.match(/^([0-9a-f]{40})\\tHEAD$/m)?.[1];\n if (!ref || !sha) throw new GitClientError(`Could not parse default branch from: ${url}`);\n validateRef(ref);\n return { ref, sha };\n } catch (error) {\n if (error instanceof GitClientError) throw error;\n throw new GitClientError(`Failed to resolve default ref for ${url}`, error);\n }\n}\n\nexport async function resolveRefToSha(url: string, ref: string): Promise<string> {\n validateGitUrl(url);\n validateRef(ref);\n await checkGitAvailable();\n try {\n const { stdout } = await execFileAsync(\"git\", [\"ls-remote\", \"--\", url, ref], {\n timeout: GIT_TIMEOUT_MS,\n });\n const sha = stdout.match(/^([0-9a-f]{40})\\t/m)?.[1];\n if (!sha) throw new GitClientError(`Ref \"${ref}\" not found in ${url}`);\n return sha;\n } catch (error) {\n if (error instanceof GitClientError) throw error;\n throw new GitClientError(`Failed to resolve ref \"${ref}\" for ${url}`, error);\n }\n}\n\n/**\n * Clone a repo at the given branch or tag and return all files under skillsPath.\n * When `resolvedRef` is provided, fetch and check out that exact commit before\n * reading files so a mutable branch cannot drift from its lockfile SHA.\n */\nexport async function fetchSkillFiles(params: {\n url: string;\n ref: string;\n resolvedRef?: string;\n skillsPath: string;\n logger?: Logger;\n}): Promise<Array<{ relativePath: string; content: string; size: number }>> {\n const { url, ref, resolvedRef, skillsPath, logger } = params;\n validateGitUrl(url, { logger });\n validateRef(ref);\n if (resolvedRef !== undefined && !/^[0-9a-f]{40}$/.test(resolvedRef)) {\n throw new GitClientError(`Invalid resolvedRef \"${resolvedRef}\": expected a commit SHA`);\n }\n if (skillsPath.split(/[/\\\\]/).includes(\"..\") || isAbsolute(skillsPath)) {\n throw new GitClientError(\n `Invalid skillsPath \"${skillsPath}\": must be a relative path without \"..\"`,\n );\n }\n const ctrl = findControlCharacter(skillsPath);\n if (ctrl) {\n throw new GitClientError(\n `skillsPath contains control character ${ctrl.hex} at position ${ctrl.position}`,\n );\n }\n await checkGitAvailable();\n const tmpDir = await createTempDirectory(\"rulesync-git-\");\n // Treat empty/\".\" paths as the repository root. Cone-mode sparse-checkout\n // with such patterns only restores the top-level files (it intentionally\n // excludes any subdirectory), so we must check out the entire working tree\n // instead. Otherwise repositories whose skills live directly at the root\n // (e.g. `<repo>/<skill-name>/SKILL.md` without a `skills/` container)\n // would only yield root-level files like README.md.\n // Normalize first so variants like \"./.\", \".//\", or Windows \".\\\\\" are also\n // recognized as the root and don't fall back to the (buggy) sparse-checkout path.\n const normalizedSkillsPath = posix.normalize(skillsPath.replace(/\\\\/g, \"/\")).replace(/\\/+$/, \"\");\n const isRootPath = normalizedSkillsPath === \"\" || normalizedSkillsPath === \".\";\n try {\n await execFileAsync(\n \"git\",\n [\n \"clone\",\n \"--depth\",\n \"1\",\n \"--branch\",\n ref,\n \"--no-checkout\",\n \"--filter=blob:none\",\n \"--\",\n url,\n tmpDir,\n ],\n { timeout: GIT_TIMEOUT_MS },\n );\n if (resolvedRef !== undefined) {\n await execFileAsync(\"git\", [\"-C\", tmpDir, \"fetch\", \"--depth\", \"1\", \"origin\", resolvedRef], {\n timeout: GIT_TIMEOUT_MS,\n });\n }\n if (isRootPath) {\n // Disable sparse-checkout and restore the full tree.\n await execFileAsync(\"git\", [\"-C\", tmpDir, \"sparse-checkout\", \"disable\"], {\n timeout: GIT_TIMEOUT_MS,\n });\n } else {\n await execFileAsync(\"git\", [\"-C\", tmpDir, \"sparse-checkout\", \"set\", \"--\", skillsPath], {\n timeout: GIT_TIMEOUT_MS,\n });\n }\n await execFileAsync(\n \"git\",\n resolvedRef === undefined\n ? [\"-C\", tmpDir, \"checkout\"]\n : [\"-C\", tmpDir, \"checkout\", \"--detach\", resolvedRef],\n { timeout: GIT_TIMEOUT_MS },\n );\n if (resolvedRef !== undefined) {\n const { stdout } = await execFileAsync(\"git\", [\"-C\", tmpDir, \"rev-parse\", \"HEAD\"], {\n timeout: GIT_TIMEOUT_MS,\n });\n if (stdout.trim() !== resolvedRef) {\n throw new GitClientError(\n `Checked out commit ${stdout.trim() || \"(unknown)\"}, expected locked commit ${resolvedRef}`,\n );\n }\n }\n const skillsDir = isRootPath ? tmpDir : join(tmpDir, skillsPath);\n if (!(await directoryExists(skillsDir))) return [];\n return await walkDirectory(skillsDir, skillsDir, 0, { totalFiles: 0, totalSize: 0 }, logger);\n } catch (error) {\n if (error instanceof GitClientError) throw error;\n throw new GitClientError(`Failed to fetch skill files from ${url}`, error);\n } finally {\n await removeTempDirectory(tmpDir);\n }\n}\n\nconst MAX_WALK_DEPTH = 20;\nconst MAX_TOTAL_FILES = 10_000;\nconst MAX_TOTAL_SIZE = 100 * 1024 * 1024; // 100 MB\n\n/** Mutable context for tracking totals across recursive walkDirectory calls. */\ntype WalkContext = { totalFiles: number; totalSize: number };\n\nasync function walkDirectory(\n dir: string,\n outputRoot: string,\n depth: number = 0,\n ctx: WalkContext = { totalFiles: 0, totalSize: 0 },\n logger?: Logger,\n): Promise<Array<{ relativePath: string; content: string; size: number }>> {\n if (depth > MAX_WALK_DEPTH) {\n throw new GitClientError(\n `Directory tree exceeds max depth of ${MAX_WALK_DEPTH}: \"${dir}\". Aborting to prevent resource exhaustion.`,\n );\n }\n const results: Array<{ relativePath: string; content: string; size: number }> = [];\n for (const name of await listDirectoryFiles(dir)) {\n if (name === \".git\") continue;\n const fullPath = join(dir, name);\n if (await isSymlink(fullPath)) {\n logger?.warn(`Skipping symlink \"${fullPath}\".`);\n continue;\n }\n if (await directoryExists(fullPath)) {\n results.push(...(await walkDirectory(fullPath, outputRoot, depth + 1, ctx, logger)));\n } else {\n const size = await getFileSize(fullPath);\n if (size > MAX_FILE_SIZE) {\n logger?.warn(\n `Skipping file \"${fullPath}\" (${(size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit).`,\n );\n continue;\n }\n ctx.totalFiles++;\n ctx.totalSize += size;\n if (ctx.totalFiles >= MAX_TOTAL_FILES) {\n throw new GitClientError(\n `Repository exceeds max file count of ${MAX_TOTAL_FILES}. Aborting to prevent resource exhaustion.`,\n );\n }\n if (ctx.totalSize >= MAX_TOTAL_SIZE) {\n throw new GitClientError(\n `Repository exceeds max total size of ${MAX_TOTAL_SIZE / 1024 / 1024}MB. Aborting to prevent resource exhaustion.`,\n );\n }\n const content = await readFileContent(fullPath);\n results.push({ relativePath: relative(outputRoot, fullPath), content, size });\n }\n }\n return results;\n}\n","import { z } from \"zod/mini\";\n\nimport { ALL_TOOL_TARGETS } from \"./tool-targets.js\";\n\n/**\n * Fetch command targets for specifying file format interpretation\n * - \"rulesync\": rulesync format with frontmatter containing targets, description, etc.\n * - Tool targets: interpreted as tool-specific format (e.g., claudecode, cursor)\n */\nconst ALL_FETCH_TARGETS = [\"rulesync\", ...ALL_TOOL_TARGETS] as const;\n\nexport const FetchTargetSchema = z.enum(ALL_FETCH_TARGETS);\n\nexport type FetchTarget = z.infer<typeof FetchTargetSchema>;\n","import { z } from \"zod/mini\";\n\nimport { ALL_FEATURES_WITH_WILDCARD } from \"./features.js\";\nimport { FetchTargetSchema } from \"./fetch-targets.js\";\nimport type { GitProvider } from \"./git-provider.js\";\n\n/**\n * Conflict resolution strategies for fetch command\n */\nconst ConflictStrategySchema = z.enum([\"skip\", \"overwrite\"]);\nexport type ConflictStrategy = z.infer<typeof ConflictStrategySchema>;\n\n/**\n * GitHub file type from API response\n */\nconst GitHubFileTypeSchema = z.enum([\"file\", \"dir\", \"symlink\", \"submodule\"]);\n\n/**\n * GitHub file/directory entry from contents API\n */\nexport const GitHubFileEntrySchema = z.looseObject({\n name: z.string(),\n path: z.string(),\n sha: z.string(),\n size: z.number(),\n type: GitHubFileTypeSchema,\n download_url: z.nullable(z.string()),\n});\nexport type GitHubFileEntry = z.infer<typeof GitHubFileEntrySchema>;\n\n/**\n * Parsed source specification for fetch command\n */\nexport type ParsedSource = {\n provider: GitProvider;\n owner: string;\n repo: string;\n ref?: string;\n path?: string;\n};\n\n/**\n * Fetch command options\n */\nconst FetchOptionsSchema = z.looseObject({\n target: z.optional(FetchTargetSchema),\n features: z.optional(z.array(z.enum(ALL_FEATURES_WITH_WILDCARD))),\n ref: z.optional(z.string()),\n path: z.optional(z.string()),\n output: z.optional(z.string()),\n conflict: z.optional(ConflictStrategySchema),\n token: z.optional(z.string()),\n verbose: z.optional(z.boolean()),\n silent: z.optional(z.boolean()),\n});\nexport type FetchOptions = z.infer<typeof FetchOptionsSchema>;\n\n/**\n * Result status for a single file fetch operation\n */\nconst FetchFileStatusSchema = z.enum([\"created\", \"overwritten\", \"skipped\"]);\ntype FetchFileStatus = z.infer<typeof FetchFileStatusSchema>;\n\n/**\n * Result of a single file fetch operation\n */\nexport type FetchFileResult = {\n relativePath: string;\n status: FetchFileStatus;\n};\n\n/**\n * Summary of fetch operation\n */\nexport type FetchSummary = {\n source: string;\n ref: string;\n files: FetchFileResult[];\n created: number;\n overwritten: number;\n skipped: number;\n};\n\n/**\n * GitHub API error response\n */\nexport type GitHubApiError = {\n message: string;\n documentation_url?: string;\n};\n\n/**\n * Configuration for GitHub client\n */\nexport type GitHubClientConfig = {\n token?: string;\n baseUrl?: string;\n};\n\n/**\n * Repository information from GitHub API\n */\nexport const GitHubRepoInfoSchema = z.looseObject({\n default_branch: z.string(),\n private: z.boolean(),\n});\nexport type GitHubRepoInfo = z.infer<typeof GitHubRepoInfoSchema>;\n\n/**\n * GitHub release asset from releases API\n */\nconst GitHubReleaseAssetSchema = z.looseObject({\n name: z.string(),\n browser_download_url: z.string(),\n size: z.number(),\n});\nexport type GitHubReleaseAsset = z.infer<typeof GitHubReleaseAssetSchema>;\n\n/**\n * GitHub release from releases API\n */\nexport const GitHubReleaseSchema = z.looseObject({\n tag_name: z.string(),\n name: z.nullable(z.string()),\n prerelease: z.boolean(),\n draft: z.boolean(),\n assets: z.array(GitHubReleaseAssetSchema),\n});\nexport type GitHubRelease = z.infer<typeof GitHubReleaseSchema>;\n","import { RequestError } from \"@octokit/request-error\";\nimport { Octokit } from \"@octokit/rest\";\n\nimport { MAX_FILE_SIZE } from \"../constants/rulesync-paths.js\";\nimport type {\n GitHubApiError,\n GitHubClientConfig,\n GitHubFileEntry,\n GitHubRelease,\n GitHubRepoInfo,\n} from \"../types/fetch.js\";\nimport {\n GitHubFileEntrySchema,\n GitHubReleaseSchema,\n GitHubRepoInfoSchema,\n} from \"../types/fetch.js\";\nimport { formatError } from \"../utils/error.js\";\nimport type { Logger } from \"../utils/logger.js\";\n\n/**\n * Error class for GitHub API errors\n */\nexport class GitHubClientError extends Error {\n constructor(\n message: string,\n public readonly statusCode?: number,\n public readonly apiError?: GitHubApiError,\n ) {\n super(message);\n this.name = \"GitHubClientError\";\n }\n}\n\n/**\n * Log GitHub auth error hints for 401/403 responses.\n */\nexport function logGitHubAuthHints(params: { error: GitHubClientError; logger: Logger }): void {\n const { error, logger } = params;\n logger.error(`GitHub API Error: ${error.message}`);\n if (error.statusCode === 401 || error.statusCode === 403) {\n logger.info(\n \"Tip: Set GITHUB_TOKEN or GH_TOKEN environment variable for private repositories or better rate limits.\",\n );\n logger.info(\n \"Tip: If you use GitHub CLI, you can use `GITHUB_TOKEN=$(gh auth token) rulesync fetch ...`\",\n );\n }\n}\n\n/**\n * Client for interacting with GitHub API using Octokit SDK\n */\nexport class GitHubClient {\n private readonly octokit: Octokit;\n private readonly hasToken: boolean;\n\n constructor(config: GitHubClientConfig = {}) {\n // Validate custom baseUrl uses HTTPS to prevent token exposure\n if (config.baseUrl && !config.baseUrl.startsWith(\"https://\")) {\n throw new GitHubClientError(\"GitHub API base URL must use HTTPS\");\n }\n\n this.hasToken = !!config.token;\n this.octokit = new Octokit({\n auth: config.token,\n baseUrl: config.baseUrl,\n });\n }\n\n /**\n * Get authentication token from various sources\n */\n static resolveToken(explicitToken?: string): string | undefined {\n if (explicitToken) {\n return explicitToken;\n }\n return process.env[\"GITHUB_TOKEN\"] ?? process.env[\"GH_TOKEN\"];\n }\n\n /**\n * Get the default branch of a repository\n */\n async getDefaultBranch(owner: string, repo: string): Promise<string> {\n const repoInfo = await this.getRepoInfo(owner, repo);\n return repoInfo.default_branch;\n }\n\n /**\n * Get repository information\n */\n async getRepoInfo(owner: string, repo: string): Promise<GitHubRepoInfo> {\n try {\n const { data } = await this.octokit.repos.get({ owner, repo });\n const parsed = GitHubRepoInfoSchema.safeParse(data);\n if (!parsed.success) {\n throw new GitHubClientError(\n `Invalid repository info response: ${formatError(parsed.error)}`,\n );\n }\n return parsed.data;\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * List contents of a directory in a repository\n */\n async listDirectory(\n owner: string,\n repo: string,\n path: string,\n ref?: string,\n ): Promise<GitHubFileEntry[]> {\n try {\n const { data } = await this.octokit.repos.getContent({\n owner,\n repo,\n path,\n ref,\n });\n\n // API returns single object for files, array for directories\n if (!Array.isArray(data)) {\n throw new GitHubClientError(`Path \"${path}\" is not a directory`);\n }\n\n const entries: GitHubFileEntry[] = [];\n for (const item of data) {\n const parsed = GitHubFileEntrySchema.safeParse(item);\n if (parsed.success) {\n entries.push(parsed.data);\n }\n }\n return entries;\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * Get raw file content from a repository\n */\n async getFileContent(owner: string, repo: string, path: string, ref?: string): Promise<string> {\n try {\n const { data } = await this.octokit.repos.getContent({\n owner,\n repo,\n path,\n ref,\n mediaType: {\n format: \"raw\",\n },\n });\n\n // When using raw format, data is returned as a string\n if (typeof data === \"string\") {\n return data;\n }\n\n // Fallback: if data is an object with content (base64 encoded)\n if (!Array.isArray(data) && \"content\" in data && data.content) {\n return Buffer.from(data.content, \"base64\").toString(\"utf-8\");\n }\n\n throw new GitHubClientError(`Unexpected response format for file content`);\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * Check if a file exists and is within size limits\n */\n async getFileInfo(\n owner: string,\n repo: string,\n path: string,\n ref?: string,\n ): Promise<GitHubFileEntry | null> {\n try {\n const { data } = await this.octokit.repos.getContent({\n owner,\n repo,\n path,\n ref,\n });\n\n // Ensure it's a file, not a directory\n if (Array.isArray(data)) {\n return null; // It's a directory\n }\n\n const parsed = GitHubFileEntrySchema.safeParse(data);\n if (!parsed.success) {\n return null;\n }\n\n if (parsed.data.size > MAX_FILE_SIZE) {\n throw new GitHubClientError(\n `File \"${path}\" exceeds maximum size limit of ${MAX_FILE_SIZE / 1024 / 1024}MB`,\n );\n }\n\n return parsed.data;\n } catch (error: unknown) {\n if (error instanceof RequestError && error.status === 404) {\n return null;\n }\n if (error instanceof GitHubClientError && error.statusCode === 404) {\n return null;\n }\n throw this.handleError(error);\n }\n }\n\n /**\n * Validate that a repository exists and is accessible\n */\n async validateRepository(owner: string, repo: string): Promise<boolean> {\n try {\n await this.getRepoInfo(owner, repo);\n return true;\n } catch (error) {\n if (error instanceof GitHubClientError && error.statusCode === 404) {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * Resolve a ref (branch, tag, or SHA) to a full commit SHA.\n */\n async resolveRefToSha(owner: string, repo: string, ref: string): Promise<string> {\n try {\n const { data } = await this.octokit.repos.getCommit({\n owner,\n repo,\n ref,\n });\n return data.sha;\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * Get the latest release from a repository\n */\n async getLatestRelease(owner: string, repo: string): Promise<GitHubRelease> {\n try {\n const { data } = await this.octokit.repos.getLatestRelease({ owner, repo });\n const parsed = GitHubReleaseSchema.safeParse(data);\n if (!parsed.success) {\n throw new GitHubClientError(`Invalid release info response: ${formatError(parsed.error)}`);\n }\n return parsed.data;\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * Handle errors from Octokit and convert to GitHubClientError\n */\n private handleError(error: unknown): GitHubClientError {\n if (error instanceof GitHubClientError) {\n return error;\n }\n\n if (error instanceof RequestError) {\n const responseData = error.response?.data;\n const message = this.extractErrorMessage(responseData, error.message);\n const apiError: GitHubApiError | undefined = message ? { message } : undefined;\n const errorMessage = this.getErrorMessage(error.status, apiError);\n return new GitHubClientError(errorMessage, error.status, apiError);\n }\n\n if (error instanceof Error) {\n return new GitHubClientError(error.message);\n }\n\n return new GitHubClientError(\"Unknown error occurred\");\n }\n\n /**\n * Extract error message from response data\n */\n private extractErrorMessage(data: unknown, fallback: string): string {\n if (typeof data === \"object\" && data !== null && \"message\" in data) {\n const record = data as Record<string, unknown>;\n const msg = record[\"message\"];\n if (typeof msg === \"string\") {\n return msg;\n }\n }\n return fallback;\n }\n\n /**\n * Get human-readable error message for HTTP status codes\n */\n private getErrorMessage(statusCode: number, apiError?: GitHubApiError): string {\n const baseMessage = apiError?.message ?? `HTTP ${statusCode}`;\n\n switch (statusCode) {\n case 401:\n return `Authentication failed: ${baseMessage}. Check your GitHub token.`;\n case 403:\n if (baseMessage.toLowerCase().includes(\"rate limit\")) {\n return `GitHub API rate limit exceeded. ${this.hasToken ? \"Try again later.\" : \"Consider using a GitHub token.\"}`;\n }\n return `Access forbidden: ${baseMessage}. Check repository permissions.`;\n case 404:\n return `Not found: ${baseMessage}`;\n case 422:\n return `Invalid request: ${baseMessage}`;\n default:\n return `GitHub API error: ${baseMessage}`;\n }\n }\n}\n","import { Semaphore } from \"es-toolkit/promise\";\n\nimport type { GitHubFileEntry } from \"../types/fetch.js\";\nimport type { GitHubClient } from \"./github-client.js\";\n\nconst MAX_RECURSION_DEPTH = 15;\n\n/**\n * Execute an async function with semaphore-controlled concurrency.\n * Ensures the semaphore permit is always released, even if the function throws.\n */\nexport async function withSemaphore<T>(semaphore: Semaphore, fn: () => Promise<T>): Promise<T> {\n await semaphore.acquire();\n try {\n return await fn();\n } finally {\n semaphore.release();\n }\n}\n\n/**\n * Recursively list all files in a GitHub directory.\n */\nexport async function listDirectoryRecursive(params: {\n client: GitHubClient;\n owner: string;\n repo: string;\n path: string;\n ref?: string;\n depth?: number;\n semaphore: Semaphore;\n}): Promise<GitHubFileEntry[]> {\n const { client, owner, repo, path, ref, depth = 0, semaphore } = params;\n\n if (depth > MAX_RECURSION_DEPTH) {\n throw new Error(\n `Maximum recursion depth (${MAX_RECURSION_DEPTH}) exceeded while listing directory: ${path}`,\n );\n }\n\n // Semaphore is released here before recursive Promise.all below to avoid deadlock\n const entries = await withSemaphore(semaphore, () =>\n client.listDirectory(owner, repo, path, ref),\n );\n\n const files: GitHubFileEntry[] = [];\n const directories: GitHubFileEntry[] = [];\n\n for (const entry of entries) {\n if (entry.type === \"file\") {\n files.push(entry);\n } else if (entry.type === \"dir\") {\n directories.push(entry);\n }\n }\n\n const subResults = await Promise.all(\n directories.map((dir) =>\n listDirectoryRecursive({\n client,\n owner,\n repo,\n path: dir.path,\n ref,\n depth: depth + 1,\n semaphore,\n }),\n ),\n );\n\n return [...files, ...subResults.flat()];\n}\n","import { createHash } from \"node:crypto\";\n\nimport type { Logger } from \"../utils/logger.js\";\nimport { findControlCharacter } from \"../utils/validation.js\";\n\n/**\n * Minimal npm-compatible registry client for the EXPERIMENTAL `npm` transport.\n * Works against any registry implementing the npm registry API (npmjs.org,\n * JFrog Artifactory, Sonatype Nexus, Verdaccio, ...). Intentionally avoids\n * `.npmrc` parsing: authentication uses a bearer token from an environment\n * variable (`NPM_TOKEN` by default, or a per-source `tokenEnv`).\n */\n\nexport const DEFAULT_NPM_REGISTRY_URL = \"https://registry.npmjs.org\";\nexport const DEFAULT_NPM_TOKEN_ENV = \"NPM_TOKEN\";\n\n/** Abbreviated packument media type (install metadata only). */\nconst PACKUMENT_ACCEPT_HEADER = \"application/vnd.npm.install-v1+json\";\n\n/** Timeout for registry HTTP requests (60 seconds). */\nconst NPM_FETCH_TIMEOUT_MS = 60_000;\n\n/** Maximum accepted tarball size (compressed), aligned with the extraction cap. */\nconst MAX_TARBALL_SIZE = 100 * 1024 * 1024;\n\n/**\n * npm package name rules (scoped or unscoped, URL-safe characters only).\n * This also guards against URL path injection into registry requests.\n */\nconst NPM_PACKAGE_NAME_REGEX = /^(@[a-z0-9][a-z0-9._~-]*\\/)?[a-z0-9][a-z0-9._~-]*$/i;\nconst MAX_NPM_PACKAGE_NAME_LENGTH = 214;\n\nconst INTEGRITY_ALGORITHM_PREFERENCE = [\"sha512\", \"sha384\", \"sha256\", \"sha1\"] as const;\ntype IntegrityAlgorithm = (typeof INTEGRITY_ALGORITHM_PREFERENCE)[number];\n\nexport class NpmClientError extends Error {\n public readonly statusCode?: number;\n\n constructor(message: string, options?: { statusCode?: number; cause?: unknown }) {\n super(message, { cause: options?.cause });\n this.name = \"NpmClientError\";\n this.statusCode = options?.statusCode;\n }\n}\n\nexport type NpmDist = {\n tarball: string;\n integrity?: string;\n shasum?: string;\n};\n\nexport type NpmPackument = {\n name?: string;\n \"dist-tags\"?: Record<string, string>;\n versions?: Record<string, { dist?: NpmDist }>;\n};\n\nexport function validateNpmPackageName(name: string): void {\n if (name.length > MAX_NPM_PACKAGE_NAME_LENGTH || !NPM_PACKAGE_NAME_REGEX.test(name)) {\n throw new NpmClientError(\n `Invalid npm package name: \"${name}\". Expected \"name\" or \"@scope/name\".`,\n );\n }\n}\n\nexport function validateNpmRegistryUrl(url: string, options?: { logger?: Logger }): void {\n const ctrl = findControlCharacter(url);\n if (ctrl) {\n throw new NpmClientError(\n `Registry URL contains control character ${ctrl.hex} at position ${ctrl.position}`,\n );\n }\n if (!url.startsWith(\"https://\") && !url.startsWith(\"http://\")) {\n throw new NpmClientError(`Unsupported registry URL: \"${url}\". Use https:// (or http://).`);\n }\n if (url.startsWith(\"http://\")) {\n options?.logger?.warn(\n `Registry URL \"${url}\" uses an unencrypted protocol. Consider using https:// instead.`,\n );\n }\n}\n\n/**\n * Resolve the registry token from the environment. When `tokenEnv` is set it\n * must name an existing environment variable; otherwise `NPM_TOKEN` is used\n * when present. The token value itself is never logged.\n */\nexport function resolveNpmToken(params: { tokenEnv?: string }): string | undefined {\n const { tokenEnv } = params;\n if (tokenEnv !== undefined) {\n const value = process.env[tokenEnv];\n if (value === undefined || value === \"\") {\n throw new NpmClientError(\n `Environment variable \"${tokenEnv}\" (from tokenEnv) is not set. Export it or remove the tokenEnv field.`,\n );\n }\n return value;\n }\n const fallback = process.env[DEFAULT_NPM_TOKEN_ENV];\n return fallback === undefined || fallback === \"\" ? undefined : fallback;\n}\n\n/** Build the packument URL for a (possibly scoped) package on a registry. */\nexport function buildPackumentUrl(params: { registryUrl: string; packageName: string }): string {\n const { registryUrl, packageName } = params;\n // Validate here too so the URL can never carry extra path segments, even if a\n // caller skips the fetch-level validation.\n validateNpmPackageName(packageName);\n const base = registryUrl.endsWith(\"/\") ? registryUrl : `${registryUrl}/`;\n // Scoped package names keep the \"@\" but encode the slash, per the npm registry API.\n const encodedName = packageName.replaceAll(\"/\", \"%2F\");\n return new URL(encodedName, base).toString();\n}\n\nasync function fetchWithTimeout(url: string, headers: Record<string, string>): Promise<Response> {\n try {\n return await fetch(url, {\n headers,\n redirect: \"follow\",\n signal: AbortSignal.timeout(NPM_FETCH_TIMEOUT_MS),\n });\n } catch (error) {\n throw new NpmClientError(`Network error while requesting ${url}`, { cause: error });\n }\n}\n\n/**\n * Fetch the (abbreviated) packument for a package from a registry.\n */\nexport async function fetchPackument(params: {\n registryUrl: string;\n packageName: string;\n token?: string;\n}): Promise<NpmPackument> {\n const { registryUrl, packageName, token } = params;\n validateNpmPackageName(packageName);\n const url = buildPackumentUrl({ registryUrl, packageName });\n\n const headers: Record<string, string> = { Accept: PACKUMENT_ACCEPT_HEADER };\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n const response = await fetchWithTimeout(url, headers);\n if (!response.ok) {\n throw new NpmClientError(\n `Failed to fetch package metadata for \"${packageName}\" from ${registryUrl}: HTTP ${response.status}`,\n { statusCode: response.status },\n );\n }\n try {\n return (await response.json()) as NpmPackument;\n } catch (error) {\n throw new NpmClientError(\n `Failed to parse package metadata for \"${packageName}\" from ${registryUrl}`,\n { cause: error },\n );\n }\n}\n\n/**\n * Resolve a requested version or dist-tag against a packument. Only exact\n * versions and dist-tags are supported — semver ranges are intentionally out\n * of scope (no semver dependency).\n */\nexport function resolvePackumentVersion(params: {\n packument: NpmPackument;\n packageName: string;\n requested: string;\n}): string {\n const { packument, packageName, requested } = params;\n const versions = packument.versions ?? {};\n if (Object.prototype.hasOwnProperty.call(versions, requested)) {\n return requested;\n }\n const distTags = packument[\"dist-tags\"] ?? {};\n const tagged = Object.prototype.hasOwnProperty.call(distTags, requested)\n ? distTags[requested]\n : undefined;\n if (tagged !== undefined && Object.prototype.hasOwnProperty.call(versions, tagged)) {\n return tagged;\n }\n throw new NpmClientError(\n `Could not resolve \"${packageName}@${requested}\": not an exact published version or dist-tag. Note: semver ranges are not supported by the npm transport.`,\n );\n}\n\n/** Get the dist metadata (tarball URL, integrity) for a resolved version. */\nexport function getPackumentVersionDist(params: {\n packument: NpmPackument;\n packageName: string;\n version: string;\n}): NpmDist {\n const { packument, packageName, version } = params;\n const versions = packument.versions ?? {};\n const entry = Object.prototype.hasOwnProperty.call(versions, version)\n ? versions[version]\n : undefined;\n const dist = entry?.dist;\n if (!dist?.tarball) {\n throw new NpmClientError(\n `Registry metadata for \"${packageName}@${version}\" is missing the dist.tarball URL.`,\n );\n }\n return dist;\n}\n\n/**\n * Download a package tarball. The Authorization header is only attached when\n * the tarball is hosted on the same origin (scheme + host) as the registry,\n * so the token never leaks to third-party CDNs or plaintext downgrades.\n */\nexport async function fetchTarball(params: {\n tarballUrl: string;\n registryUrl: string;\n token?: string;\n /** Maximum accepted tarball size in bytes. Overridable for tests only. */\n maxSize?: number;\n}): Promise<Buffer> {\n const { tarballUrl, registryUrl, token } = params;\n const maxSize = params.maxSize ?? MAX_TARBALL_SIZE;\n if (!tarballUrl.startsWith(\"https://\") && !tarballUrl.startsWith(\"http://\")) {\n throw new NpmClientError(\n `Unsupported tarball URL: \"${tarballUrl}\". Use https:// (or http://).`,\n );\n }\n\n const headers: Record<string, string> = {};\n if (token && isSameOrigin(tarballUrl, registryUrl)) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n const response = await fetchWithTimeout(tarballUrl, headers);\n if (!response.ok) {\n throw new NpmClientError(`Failed to download tarball ${tarballUrl}: HTTP ${response.status}`, {\n statusCode: response.status,\n });\n }\n const contentLength = Number.parseInt(response.headers.get(\"content-length\") ?? \"\", 10);\n if (Number.isFinite(contentLength) && contentLength > maxSize) {\n throw new NpmClientError(oversizedTarballMessage(tarballUrl, maxSize));\n }\n return await readBodyWithLimit({ response, tarballUrl, maxSize });\n}\n\nfunction oversizedTarballMessage(tarballUrl: string, maxSize: number): string {\n return `Tarball ${tarballUrl} exceeds max size of ${maxSize / 1024 / 1024}MB.`;\n}\n\n/**\n * Read a response body incrementally, aborting as soon as the size cap is\n * exceeded. content-length can be absent or forged, so the streaming check is\n * the actual enforcement of the cap.\n */\nasync function readBodyWithLimit(params: {\n response: Response;\n tarballUrl: string;\n maxSize: number;\n}): Promise<Buffer> {\n const { response, tarballUrl, maxSize } = params;\n const reader = response.body?.getReader();\n if (!reader) {\n // Responses without a body stream (e.g. some test doubles): buffer\n // with a post-hoc check.\n const arrayBuffer = await response.arrayBuffer();\n if (arrayBuffer.byteLength > maxSize) {\n throw new NpmClientError(oversizedTarballMessage(tarballUrl, maxSize));\n }\n return Buffer.from(arrayBuffer);\n }\n\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n totalBytes += value.byteLength;\n if (totalBytes > maxSize) {\n await reader.cancel();\n throw new NpmClientError(oversizedTarballMessage(tarballUrl, maxSize));\n }\n chunks.push(Buffer.from(value));\n }\n return Buffer.concat(chunks);\n}\n\nfunction isSameOrigin(urlA: string, urlB: string): boolean {\n try {\n return new URL(urlA).origin === new URL(urlB).origin;\n } catch {\n return false;\n }\n}\n\n/**\n * Convert a hex sha1 shasum to the SRI form used by `verifyTarballIntegrity`.\n * Rejects malformed shasum values so a broken value is never recorded in the\n * lockfile as a seemingly valid SRI string.\n */\nexport function shasumToSri(shasum: string): string {\n if (!/^[0-9a-f]{40}$/i.test(shasum)) {\n throw new NpmClientError(`Malformed sha1 shasum in registry metadata: \"${shasum}\"`);\n }\n return `sha1-${Buffer.from(shasum, \"hex\").toString(\"base64\")}`;\n}\n\n/**\n * Verify a downloaded tarball against registry integrity metadata.\n * Prefers the strongest supported algorithm in the SRI `integrity` string and\n * falls back to the legacy sha1 `shasum`. An `integrity` string that is\n * present but cannot be parsed fails closed; a warning is only logged when\n * the registry provides no integrity metadata at all.\n */\nexport function verifyTarballIntegrity(params: {\n tarball: Buffer;\n integrity?: string;\n shasum?: string;\n context: string;\n logger?: Logger;\n}): void {\n const { tarball, integrity, shasum, context, logger } = params;\n\n if (integrity !== undefined) {\n const sri = pickStrongestSriEntry(integrity);\n if (!sri) {\n // Fail closed: a present-but-unparseable integrity value must never\n // silently disable verification (e.g. a corrupted lockfile entry).\n throw new NpmClientError(\n `Unsupported or malformed integrity metadata for ${context}. Expected an SRI string with sha512/sha384/sha256/sha1.`,\n );\n }\n const actual = createHash(sri.algorithm).update(tarball).digest(\"base64\");\n if (actual !== sri.digest) {\n throw new NpmClientError(\n `Integrity verification failed for ${context}: expected ${sri.algorithm}-${sri.digest}, got ${sri.algorithm}-${actual}. The tarball may have been tampered with.`,\n );\n }\n return;\n }\n\n if (shasum) {\n const actual = createHash(\"sha1\").update(tarball).digest(\"hex\");\n if (actual !== shasum.toLowerCase()) {\n throw new NpmClientError(\n `Integrity verification failed for ${context}: expected sha1 ${shasum}, got ${actual}. The tarball may have been tampered with.`,\n );\n }\n return;\n }\n\n logger?.warn(`No integrity metadata available for ${context}; skipping tarball verification.`);\n}\n\nfunction pickStrongestSriEntry(\n integrity: string | undefined,\n): { algorithm: IntegrityAlgorithm; digest: string } | undefined {\n if (!integrity) {\n return undefined;\n }\n const entries = integrity\n .split(/\\s+/)\n .map((entry) => {\n const separatorIndex = entry.indexOf(\"-\");\n if (separatorIndex === -1) return undefined;\n const algorithm = entry.slice(0, separatorIndex);\n // Strip SRI options (`sha512-<digest>?opt`) from the digest.\n const digest = entry.slice(separatorIndex + 1).split(\"?\")[0] ?? \"\";\n const known = INTEGRITY_ALGORITHM_PREFERENCE.find((a) => a === algorithm);\n if (!known || digest.length === 0) return undefined;\n return { algorithm: known, digest };\n })\n .filter((entry): entry is { algorithm: IntegrityAlgorithm; digest: string } => Boolean(entry));\n\n for (const algorithm of INTEGRITY_ALGORITHM_PREFERENCE) {\n const match = entries.find((entry) => entry.algorithm === algorithm);\n if (match) {\n return match;\n }\n }\n return undefined;\n}\n\n/**\n * Log contextual hints for NpmClientError to help users troubleshoot\n * authentication problems without ever logging the token itself.\n */\nexport function logNpmAuthHints(params: { error: NpmClientError; logger: Logger }): void {\n const { error, logger } = params;\n if (error.statusCode === 401 || error.statusCode === 403) {\n logger.info(\n \"Hint: The registry rejected the request. Set NPM_TOKEN (or the per-source tokenEnv variable) to a token with read access. Note: .npmrc files are not read by the npm transport.\",\n );\n } else if (error.statusCode === 404) {\n logger.info(\n \"Hint: Package not found. Check the package name and the registry URL. Some registries also return 404 for unauthorized requests.\",\n );\n }\n}\n","import { gunzipSync } from \"node:zlib\";\n\n/**\n * Minimal, hardened tar reader for npm package tarballs (EXPERIMENTAL npm\n * transport). Intentionally supports only the subset of the (pax-extended)\n * ustar format that npm-compatible registries produce:\n *\n * - regular files (typeflag \"0\" / \"\\0\")\n * - directories (typeflag \"5\") — skipped; directories are created implicitly\n * - pax extended headers (typeflag \"x\") — only the `path` override is honored\n * - GNU long names (typeflag \"L\")\n *\n * Everything else (symlinks, hardlinks, devices, FIFOs, ...) is skipped and\n * never materialized. Extraction is bounded by file-count and total-byte caps\n * to prevent decompression bombs, and every entry path is validated against\n * traversal (absolute paths, `..` segments, backslashes).\n */\n\nconst BLOCK_SIZE = 512;\n\n/** Maximum number of files extracted from a single package tarball. */\nexport const MAX_TAR_FILES = 10_000;\n/** Maximum total extracted bytes from a single package tarball (100 MB). */\nexport const MAX_TAR_TOTAL_BYTES = 100 * 1024 * 1024;\n\nexport class NpmTarError extends Error {\n constructor(message: string, cause?: unknown) {\n super(message, { cause });\n this.name = \"NpmTarError\";\n }\n}\n\nexport type TarFileEntry = {\n /**\n * Entry path relative to the package root. The first path component of every\n * entry (conventionally `package/` in npm tarballs, but registries may use a\n * different folder name) is stripped, matching `tar --strip-components=1`.\n */\n relativePath: string;\n content: Buffer;\n};\n\n/**\n * Gunzip and extract a npm package tarball into an in-memory file list.\n * Throws {@link NpmTarError} on malformed archives, traversal attempts, or\n * resource-limit violations.\n */\nexport function extractPackageTarball(params: {\n tarball: Buffer;\n maxFiles?: number;\n maxTotalBytes?: number;\n onSkippedEntry?: (message: string) => void;\n}): TarFileEntry[] {\n const { tarball, onSkippedEntry } = params;\n const maxFiles = params.maxFiles ?? MAX_TAR_FILES;\n const maxTotalBytes = params.maxTotalBytes ?? MAX_TAR_TOTAL_BYTES;\n\n let tar: Buffer;\n try {\n // Cap the decompressed size at the gzip layer as well: the total-byte cap\n // plus generous headroom for tar headers/padding (3 blocks per file).\n tar = gunzipSync(tarball, {\n maxOutputLength: maxTotalBytes + maxFiles * 3 * BLOCK_SIZE + 2 * BLOCK_SIZE,\n });\n } catch (error) {\n throw new NpmTarError(\"Failed to gunzip package tarball\", error);\n }\n\n return parseTarBuffer({ tar, maxFiles, maxTotalBytes, onSkippedEntry });\n}\n\nfunction parseTarBuffer(params: {\n tar: Buffer;\n maxFiles: number;\n maxTotalBytes: number;\n onSkippedEntry?: (message: string) => void;\n}): TarFileEntry[] {\n const { tar, maxFiles, maxTotalBytes, onSkippedEntry } = params;\n const files: TarFileEntry[] = [];\n let totalBytes = 0;\n let offset = 0;\n let pendingLongName: string | undefined;\n let pendingPaxPath: string | undefined;\n\n while (offset + BLOCK_SIZE <= tar.length) {\n const header = tar.subarray(offset, offset + BLOCK_SIZE);\n if (isZeroBlock(header)) {\n break;\n }\n verifyHeaderChecksum(header);\n\n const size = parseOctalField(header, 124, 12, \"size\");\n const typeflag = String.fromCharCode(header[156] ?? 0);\n const dataStart = offset + BLOCK_SIZE;\n const dataEnd = dataStart + size;\n if (dataEnd > tar.length) {\n throw new NpmTarError(\"Truncated tar archive: entry data extends past end of archive\");\n }\n\n switch (typeflag) {\n case \"x\": {\n const records = parsePaxRecords(tar.subarray(dataStart, dataEnd));\n if (records.has(\"size\")) {\n // A pax size override means the octal size field is unreliable; a\n // minimal reader cannot stay aligned, so refuse instead of misparsing.\n throw new NpmTarError(\"Unsupported tar archive: pax size override is not supported\");\n }\n pendingPaxPath = records.get(\"path\") ?? pendingPaxPath;\n break;\n }\n case \"L\": {\n pendingLongName = trimAtFirstNul(tar.toString(\"utf8\", dataStart, dataEnd));\n break;\n }\n case \"g\": {\n // Global pax header. Harmless records are ignored, but size/path\n // overrides would make this reader disagree with full tar\n // implementations (parser-differential risk), so refuse them.\n const records = parsePaxRecords(tar.subarray(dataStart, dataEnd));\n if (records.has(\"size\") || records.has(\"path\")) {\n throw new NpmTarError(\n \"Unsupported tar archive: global pax size/path overrides are not supported\",\n );\n }\n break;\n }\n case \"0\":\n case \"\\0\": {\n const rawName = resolveEntryName({ header, pendingLongName, pendingPaxPath });\n pendingLongName = undefined;\n pendingPaxPath = undefined;\n const relativePath = toSafeRelativePath(rawName);\n if (relativePath !== null) {\n if (files.length + 1 > maxFiles) {\n throw new NpmTarError(\n `Package tarball exceeds max file count of ${maxFiles}. Aborting to prevent resource exhaustion.`,\n );\n }\n totalBytes += size;\n if (totalBytes > maxTotalBytes) {\n throw new NpmTarError(\n `Package tarball exceeds max total size of ${maxTotalBytes / 1024 / 1024}MB. Aborting to prevent resource exhaustion.`,\n );\n }\n files.push({\n relativePath,\n content: Buffer.from(tar.subarray(dataStart, dataEnd)),\n });\n }\n break;\n }\n case \"5\": {\n // Directory — created implicitly when files are written.\n pendingLongName = undefined;\n pendingPaxPath = undefined;\n break;\n }\n default: {\n // Symlinks, hardlinks, devices, FIFOs, ... are never materialized.\n const rawName = resolveEntryName({ header, pendingLongName, pendingPaxPath });\n pendingLongName = undefined;\n pendingPaxPath = undefined;\n onSkippedEntry?.(\n `Skipping unsupported tar entry type \"${typeflag}\" for \"${rawName}\" (only regular files are extracted).`,\n );\n break;\n }\n }\n\n offset = dataStart + Math.ceil(size / BLOCK_SIZE) * BLOCK_SIZE;\n }\n\n return files;\n}\n\nfunction isZeroBlock(block: Buffer): boolean {\n return block.every((byte) => byte === 0);\n}\n\n/** Cut a string at its first NUL character (tar fields are NUL-terminated). */\nfunction trimAtFirstNul(value: string): string {\n const nulIndex = value.indexOf(\"\\0\");\n return nulIndex === -1 ? value : value.substring(0, nulIndex);\n}\n\nfunction parseOctalField(block: Buffer, offset: number, length: number, field: string): number {\n const first = block[offset] ?? 0;\n if ((first & 0x80) !== 0) {\n throw new NpmTarError(`Unsupported tar archive: base-256 ${field} field is not supported`);\n }\n const raw = block.toString(\"latin1\", offset, offset + length);\n const text = trimAtFirstNul(raw).trim();\n if (text === \"\") {\n return 0;\n }\n if (!/^[0-7]+$/.test(text)) {\n throw new NpmTarError(`Invalid tar header: malformed octal ${field} field`);\n }\n return Number.parseInt(text, 8);\n}\n\n/**\n * Verify the ustar header checksum: the unsigned byte sum of the 512-byte\n * header with the checksum field itself treated as ASCII spaces.\n */\nfunction verifyHeaderChecksum(header: Buffer): void {\n const stored = parseOctalField(header, 148, 8, \"checksum\");\n let sum = 0;\n for (let i = 0; i < BLOCK_SIZE; i++) {\n sum += i >= 148 && i < 156 ? 0x20 : (header[i] ?? 0);\n }\n if (sum !== stored) {\n throw new NpmTarError(\"Invalid tar header: checksum mismatch\");\n }\n}\n\nfunction readCString(block: Buffer, offset: number, length: number): string {\n const end = block.indexOf(0, offset);\n const stop = end === -1 || end > offset + length ? offset + length : end;\n return block.toString(\"utf8\", offset, stop);\n}\n\nfunction resolveEntryName(params: {\n header: Buffer;\n pendingLongName: string | undefined;\n pendingPaxPath: string | undefined;\n}): string {\n const { header, pendingLongName, pendingPaxPath } = params;\n if (pendingPaxPath !== undefined) {\n return pendingPaxPath;\n }\n if (pendingLongName !== undefined) {\n return pendingLongName;\n }\n const name = readCString(header, 0, 100);\n const magic = header.toString(\"latin1\", 257, 262);\n const prefix = magic === \"ustar\" ? readCString(header, 345, 155) : \"\";\n return prefix.length > 0 ? `${prefix}/${name}` : name;\n}\n\n/**\n * Parse pax extended header records of the form `<len> <key>=<value>\\n`,\n * where `<len>` is the decimal length of the whole record.\n */\nfunction parsePaxRecords(data: Buffer): Map<string, string> {\n const records = new Map<string, string>();\n let offset = 0;\n while (offset < data.length) {\n if (data[offset] === 0) {\n break; // trailing NUL padding\n }\n const spaceIndex = data.indexOf(0x20, offset);\n if (spaceIndex === -1) {\n throw new NpmTarError(\"Invalid pax header: missing length delimiter\");\n }\n const recordLength = Number.parseInt(data.toString(\"utf8\", offset, spaceIndex), 10);\n if (\n !Number.isInteger(recordLength) ||\n recordLength <= 0 ||\n offset + recordLength > data.length\n ) {\n throw new NpmTarError(\"Invalid pax header: malformed record length\");\n }\n // Record content excludes the length prefix, the space, and the trailing newline.\n const record = data.toString(\"utf8\", spaceIndex + 1, offset + recordLength - 1);\n const equalsIndex = record.indexOf(\"=\");\n if (equalsIndex !== -1) {\n records.set(record.slice(0, equalsIndex), record.slice(equalsIndex + 1));\n }\n offset += recordLength;\n }\n return records;\n}\n\n/**\n * Validate a tar entry path and convert it to a package-root-relative path\n * with the first component stripped. Returns null for entries that resolve to\n * the package root itself (e.g. the `package/` folder entry). Throws on\n * traversal attempts.\n */\nfunction toSafeRelativePath(rawPath: string): string | null {\n if (rawPath.includes(\"\\0\")) {\n throw new NpmTarError(`Unsafe tar entry path (NUL byte): \"${rawPath}\"`);\n }\n if (rawPath.includes(\"\\\\\")) {\n throw new NpmTarError(`Unsafe tar entry path (backslash): \"${rawPath}\"`);\n }\n if (rawPath.startsWith(\"/\")) {\n throw new NpmTarError(`Unsafe tar entry path (absolute): \"${rawPath}\"`);\n }\n const segments = rawPath.split(\"/\").filter((segment) => segment !== \"\" && segment !== \".\");\n if (segments.includes(\"..\")) {\n throw new NpmTarError(`Unsafe tar entry path (\"..\" segment): \"${rawPath}\"`);\n }\n // Strip the tarball's single root folder (conventionally `package/`).\n segments.shift();\n if (segments.length === 0) {\n return null;\n }\n return segments.join(\"/\");\n}\n","import { z } from \"zod/mini\";\n\n/**\n * Supported Git providers for fetch command\n */\nexport const ALL_GIT_PROVIDERS = [\"github\", \"gitlab\"] as const;\n\nconst GitProviderSchema = z.enum(ALL_GIT_PROVIDERS);\n\nexport type GitProvider = z.infer<typeof GitProviderSchema>;\n","import type { ParsedSource } from \"../types/fetch.js\";\nimport type { GitProvider } from \"../types/git-provider.js\";\nimport { ALL_GIT_PROVIDERS } from \"../types/git-provider.js\";\n\nconst GITHUB_HOSTS = new Set([\"github.com\", \"www.github.com\"]);\nconst GITLAB_HOSTS = new Set([\"gitlab.com\", \"www.gitlab.com\"]);\n\n/**\n * Parse source specification into components\n * Supports:\n * - URL format: https://github.com/owner/repo, https://gitlab.com/owner/repo\n * - Prefix format: github:owner/repo, gitlab:owner/repo\n * - Shorthand format: owner/repo (defaults to GitHub)\n * - With ref: owner/repo@ref\n * - With path: owner/repo:path\n * - Combined: owner/repo@ref:path\n */\nexport function parseSource(source: string): ParsedSource {\n // Handle full URL format (https://...)\n if (source.startsWith(\"http://\") || source.startsWith(\"https://\")) {\n return parseUrl(source);\n }\n\n // Handle prefix format (github:owner/repo, gitlab:owner/repo)\n if (source.includes(\":\") && !source.includes(\"://\")) {\n const colonIndex = source.indexOf(\":\");\n const prefix = source.substring(0, colonIndex);\n const rest = source.substring(colonIndex + 1);\n\n // Check if prefix is a known provider using type guard\n const provider = ALL_GIT_PROVIDERS.find((p) => p === prefix);\n if (provider) {\n return { provider, ...parseShorthand(rest) };\n }\n\n // If prefix is not a known provider, treat the whole thing as shorthand\n // This handles cases like owner/repo:path where \"owner/repo\" contains no provider prefix\n return { provider: \"github\", ...parseShorthand(source) };\n }\n\n // Handle shorthand: owner/repo[@ref][:path] - defaults to GitHub\n return { provider: \"github\", ...parseShorthand(source) };\n}\n\n/**\n * Parse URL format into components\n */\nfunction parseUrl(url: string): ParsedSource {\n const urlObj = new URL(url);\n const host = urlObj.hostname.toLowerCase();\n\n let provider: GitProvider;\n if (GITHUB_HOSTS.has(host)) {\n provider = \"github\";\n } else if (GITLAB_HOSTS.has(host)) {\n provider = \"gitlab\";\n } else {\n throw new Error(\n `Unknown Git provider for host: ${host}. Supported providers: ${ALL_GIT_PROVIDERS.join(\", \")}`,\n );\n }\n\n // Split by path segments\n const segments = urlObj.pathname.split(\"/\").filter(Boolean);\n\n if (segments.length < 2) {\n throw new Error(`Invalid ${provider} URL: ${url}. Expected format: https://${host}/owner/repo`);\n }\n\n const owner = segments[0];\n const repo = segments[1]?.replace(/\\.git$/, \"\");\n\n // Check for /tree/ref/path or /blob/ref/path pattern\n if (segments.length > 2 && (segments[2] === \"tree\" || segments[2] === \"blob\")) {\n const ref = segments[3];\n const path = segments.length > 4 ? segments.slice(4).join(\"/\") : undefined;\n return {\n provider,\n owner: owner ?? \"\",\n repo: repo ?? \"\",\n ref,\n path,\n };\n }\n\n return {\n provider,\n owner: owner ?? \"\",\n repo: repo ?? \"\",\n };\n}\n\n/**\n * Parse shorthand format (without provider prefix)\n */\nfunction parseShorthand(source: string): Omit<ParsedSource, \"provider\"> {\n // Pattern: owner/repo[@ref][:path]\n let remaining = source;\n let path: string | undefined;\n let ref: string | undefined;\n\n // Extract path first (after :)\n const colonIndex = remaining.indexOf(\":\");\n if (colonIndex !== -1) {\n path = remaining.substring(colonIndex + 1);\n if (!path) {\n throw new Error(`Invalid source: ${source}. Path cannot be empty after \":\".`);\n }\n remaining = remaining.substring(0, colonIndex);\n }\n\n // Extract ref (after @)\n const atIndex = remaining.indexOf(\"@\");\n if (atIndex !== -1) {\n ref = remaining.substring(atIndex + 1);\n if (!ref) {\n throw new Error(`Invalid source: ${source}. Ref cannot be empty after \"@\".`);\n }\n remaining = remaining.substring(0, atIndex);\n }\n\n // Parse owner/repo\n const slashIndex = remaining.indexOf(\"/\");\n if (slashIndex === -1) {\n throw new Error(\n `Invalid source: ${source}. Expected format: owner/repo, owner/repo@ref, or owner/repo:path`,\n );\n }\n\n const owner = remaining.substring(0, slashIndex);\n const repo = remaining.substring(slashIndex + 1);\n\n if (!owner || !repo) {\n throw new Error(`Invalid source: ${source}. Both owner and repo are required.`);\n }\n\n return {\n owner,\n repo,\n ref,\n path,\n };\n}\n","import { join, posix, relative, resolve, sep } from \"node:path\";\n\nimport { Semaphore } from \"es-toolkit/promise\";\n\nimport type { SourceEntry } from \"../config/config.js\";\nimport { SKILL_FILE_NAME } from \"../constants/general.js\";\nimport {\n FETCH_CONCURRENCY_LIMIT,\n RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH,\n MAX_FILE_SIZE,\n RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH,\n RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH,\n RULESYNC_RULES_RELATIVE_DIR_PATH,\n RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH,\n} from \"../constants/rulesync-paths.js\";\nimport { getLocalSkillDirNames } from \"../features/skills/skills-utils.js\";\nimport type { GitHubFileEntry, ParsedSource } from \"../types/fetch.js\";\nimport { formatError } from \"../utils/error.js\";\nimport {\n assertDirectoryIfExists,\n assertTreeContainsNoSymlinks,\n assertWritablePathInsideRoot,\n checkPathTraversal,\n directoryExists,\n fileExists,\n findFilesByGlobs,\n readFileContent,\n removeFileStrict,\n removeDirectoryStrict,\n runWithDirectoryRollback,\n writeFileContent,\n} from \"../utils/file.js\";\nimport type { Logger } from \"../utils/logger.js\";\nimport {\n GitClientError,\n fetchSkillFiles,\n resolveDefaultRef,\n resolveRefToSha,\n validateRef,\n} from \"./git-client.js\";\nimport { GitHubClient, GitHubClientError, logGitHubAuthHints } from \"./github-client.js\";\nimport { listDirectoryRecursive, withSemaphore } from \"./github-utils.js\";\nimport {\n DEFAULT_NPM_REGISTRY_URL,\n fetchPackument,\n fetchTarball,\n getPackumentVersionDist,\n logNpmAuthHints,\n NpmClientError,\n resolveNpmToken,\n resolvePackumentVersion,\n shasumToSri,\n validateNpmPackageName,\n validateNpmRegistryUrl,\n verifyTarballIntegrity,\n} from \"./npm-client.js\";\nimport {\n getNpmLockedRuleNames,\n getNpmLockedSkillNames,\n getNpmLockedSource,\n type NpmLockedSource,\n type NpmSourcesLock,\n normalizeNpmSourceKey,\n readNpmLockFile,\n setNpmLockedSource,\n writeNpmLockFile,\n} from \"./npm-sources-lock.js\";\nimport { extractPackageTarball } from \"./npm-tar.js\";\nimport { parseSource } from \"./source-parser.js\";\nimport {\n type LockedSkill,\n type LockedRule,\n type LockedSource,\n type SourcesLock,\n computeRuleIntegrity,\n computeSkillIntegrity,\n getLockedRuleNames,\n getLockedSkillNames,\n getLockedSource,\n normalizeSourceKey,\n readLockFile,\n setLockedSource,\n writeLockFile,\n} from \"./sources-lock.js\";\n\nexport type ResolveAndFetchSourcesOptions = {\n /** Force re-resolve all refs, ignoring the lockfile. */\n updateSources?: boolean;\n /** Skip fetching entirely (use what's already on disk). */\n skipSources?: boolean;\n /** Fail if lockfile is missing or doesn't match sources (for CI). */\n frozen?: boolean;\n /** GitHub token for private repositories. */\n token?: string;\n /** Keep lock entries for sources omitted from this invocation. */\n preserveUnlistedLockEntries?: boolean;\n /** Treat a source resolving to no installed or locked skills as a failure. */\n requireResolvedSkills?: boolean;\n /** Treat a source resolving to no installed or locked rules as a failure. */\n requireResolvedRules?: boolean;\n /** Skill names owned by earlier sources and unavailable to this invocation. */\n reservedSkillNames?: string[];\n /** Rule names owned by earlier sources and unavailable to this invocation. */\n reservedRuleNames?: string[];\n};\n\nexport type ResolveAndFetchSourcesResult = {\n fetchedSkillCount: number;\n fetchedRuleCount: number;\n sourcesProcessed: number;\n failedSourceCount: number;\n};\n\nfunction getEarlySourcesResult(params: {\n skipSources: boolean;\n logger: Logger;\n}): ResolveAndFetchSourcesResult | undefined {\n if (!params.skipSources) {\n return undefined;\n }\n if (params.skipSources) {\n params.logger.info(\"Skipping source fetching.\");\n }\n return {\n fetchedSkillCount: 0,\n fetchedRuleCount: 0,\n sourcesProcessed: 0,\n failedSourceCount: 0,\n };\n}\n\ntype RemoteSkillFile = {\n relativePath: string;\n content: string;\n};\n\ntype RemoteRuleFile = {\n name: string;\n content: string;\n};\n\n/**\n * Resolve declared sources, fetch remote rules and skills into their curated\n * directories, and update the lockfile.\n */\nexport async function resolveAndFetchSources(params: {\n sources: SourceEntry[];\n projectRoot: string;\n options?: ResolveAndFetchSourcesOptions;\n logger: Logger;\n}): Promise<ResolveAndFetchSourcesResult> {\n const { sources, projectRoot, options = {}, logger } = params;\n const {\n updateSources = false,\n skipSources = false,\n frozen = false,\n preserveUnlistedLockEntries = false,\n requireResolvedSkills = false,\n requireResolvedRules = false,\n reservedSkillNames = [],\n reservedRuleNames = [],\n } = options;\n const earlyResult = getEarlySourcesResult({\n skipSources,\n logger,\n });\n if (earlyResult) {\n return earlyResult;\n }\n\n await assertSourceOutputPathsAreSafe(projectRoot);\n\n // Read existing lockfiles. npm-transport sources are pinned in a separate\n // lockfile (`rulesync-npm.lock.json`) because they lock a package version +\n // tarball integrity instead of a git commit SHA.\n let lock: SourcesLock = await readLockFile({ projectRoot, logger });\n let npmLock: NpmSourcesLock = await readNpmLockFile({ projectRoot, logger });\n\n // Frozen mode: validate lockfiles cover all declared sources.\n // Missing curated skills are fetched using locked refs.\n validateFrozenLockCoverage({ frozen, lock, npmLock, sources });\n\n const originalLockJson = JSON.stringify(lock);\n const originalNpmLockJson = JSON.stringify(npmLock);\n\n // Resolve GitHub token\n const token = GitHubClient.resolveToken(options.token);\n const client = new GitHubClient({ token });\n\n // Determine local skills (in .rulesync/skills/ but not in .curated/)\n const localSkillNames = await getLocalSkillDirNames(projectRoot);\n const localRuleNames = await getLocalRuleNames(projectRoot);\n\n if (!preserveUnlistedLockEntries && !frozen) {\n await cleanUnlistedSourceArtifacts({ projectRoot, lock, npmLock, sources, logger });\n lock = pruneStaleLockEntries({ lock, sources, logger });\n npmLock = pruneStaleNpmLockEntries({ npmLock, sources, logger });\n }\n\n let totalSkillCount = 0;\n let totalRuleCount = 0;\n let failedSourceCount = 0;\n const allFetchedSkillNames = new Set(reservedSkillNames);\n const allFetchedRuleNames = new Set(reservedRuleNames);\n\n for (const sourceEntry of sources) {\n try {\n const result = await runWithDirectoryRollback({\n directoryPaths: [\n join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH),\n join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH),\n ],\n action: () =>\n fetchSingleSource({\n sourceEntry,\n client,\n projectRoot,\n lock,\n npmLock,\n localSkillNames,\n localRuleNames,\n alreadyFetchedSkillNames: allFetchedSkillNames,\n alreadyFetchedRuleNames: allFetchedRuleNames,\n updateSources,\n frozen,\n logger,\n }),\n });\n\n lock = result.lock;\n npmLock = result.npmLock;\n failedSourceCount += resolvedSourceFailureCount({\n requireSkills: requireResolvedSkills,\n requireRules: requireResolvedRules,\n resolvedSkillNames: result.fetchedSkillNames,\n resolvedRuleNames: result.fetchedRuleNames,\n });\n totalSkillCount += result.skillCount;\n totalRuleCount += result.ruleCount;\n addNamesToSet({ names: result.fetchedSkillNames, target: allFetchedSkillNames });\n addNamesToSet({ names: result.fetchedRuleNames, target: allFetchedRuleNames });\n } catch (error) {\n failedSourceCount += 1;\n logSourceFetchFailure({ sourceEntry, error, logger });\n }\n }\n\n await writeLockFilesIfChanged({\n projectRoot,\n lock,\n npmLock,\n originalLockJson,\n originalNpmLockJson,\n frozen,\n logger,\n });\n\n return {\n fetchedSkillCount: totalSkillCount,\n fetchedRuleCount: totalRuleCount,\n sourcesProcessed: sources.length,\n failedSourceCount,\n };\n}\n\nasync function assertSourceOutputPathsAreSafe(projectRoot: string): Promise<void> {\n const curatedSkillsPath = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n const curatedRulesPath = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n const sourcesLockPath = join(projectRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH);\n const npmSourcesLockPath = join(projectRoot, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH);\n await Promise.all([\n assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath: curatedSkillsPath }),\n assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath: curatedRulesPath }),\n assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath: sourcesLockPath }),\n assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath: npmSourcesLockPath }),\n assertDirectoryIfExists(curatedSkillsPath),\n assertDirectoryIfExists(curatedRulesPath),\n ]);\n if (await directoryExists(curatedSkillsPath)) {\n await assertTreeContainsNoSymlinks(curatedSkillsPath);\n }\n if (await directoryExists(curatedRulesPath)) {\n await assertTreeContainsNoSymlinks(curatedRulesPath);\n }\n}\n\nfunction addNamesToSet(params: { names: string[]; target: Set<string> }): void {\n params.names.forEach((name) => params.target.add(name));\n}\n\nfunction resolvedSourceFailureCount({\n requireSkills,\n requireRules,\n resolvedSkillNames,\n resolvedRuleNames,\n}: {\n requireSkills: boolean;\n requireRules: boolean;\n resolvedSkillNames: string[];\n resolvedRuleNames: string[];\n}): number {\n return (requireSkills && resolvedSkillNames.length === 0) ||\n (requireRules && resolvedRuleNames.length === 0)\n ? 1\n : 0;\n}\n\nfunction getSourceFilters(sourceEntry: SourceEntry): {\n skills: string[] | undefined;\n rules: string[] | undefined;\n} {\n const hasExplicitFeature = sourceEntry.skills !== undefined || sourceEntry.rules !== undefined;\n return {\n skills: sourceEntry.skills ?? (hasExplicitFeature ? undefined : [\"*\"]),\n rules: sourceEntry.rules,\n };\n}\n\nasync function getLocalRuleNames(projectRoot: string): Promise<Set<string>> {\n const rulesDir = join(projectRoot, RULESYNC_RULES_RELATIVE_DIR_PATH);\n const files = await findFilesByGlobs(join(rulesDir, \"**\", \"*.md\"));\n const localNames = new Set<string>();\n for (const file of files) {\n const relativePath = relative(rulesDir, file);\n if (relativePath.startsWith(`.curated${sep}`)) {\n continue;\n }\n localNames.add(relativePath.replace(/\\.md$/i, \"\"));\n }\n return localNames;\n}\n\nexport async function getInstalledSourceSkillNames({\n sources,\n projectRoot,\n logger,\n}: {\n sources: SourceEntry[];\n projectRoot: string;\n logger: Logger;\n}): Promise<string[]> {\n const lock = await readLockFile({ projectRoot, logger });\n const npmLock = await readNpmLockFile({ projectRoot, logger });\n const curatedDir = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n const skillNames = new Set<string>();\n for (const source of sources) {\n const npmTransport = (source.transport ?? \"github\") === \"npm\";\n const entry = npmTransport\n ? getNpmLockedSource(npmLock, source.source)\n : getLockedSource(lock, source.source);\n const lockedSkillNames = entry\n ? npmTransport\n ? getNpmLockedSkillNames(entry as NpmLockedSource)\n : getLockedSkillNames(entry as LockedSource)\n : [];\n if (entry === undefined || !(await checkLockedSkillsExist(curatedDir, lockedSkillNames))) {\n throw new Error(\n `Existing source \"${source.source}\" is not fully installed. Run 'rulesync install' before adding another source.`,\n );\n }\n lockedSkillNames.forEach((skillName) => skillNames.add(skillName));\n }\n return [...skillNames];\n}\n\nexport async function getInstalledSourceRuleNames({\n sources,\n projectRoot,\n logger,\n}: {\n sources: SourceEntry[];\n projectRoot: string;\n logger: Logger;\n}): Promise<string[]> {\n const lock = await readLockFile({ projectRoot, logger });\n const npmLock = await readNpmLockFile({ projectRoot, logger });\n const curatedDir = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n const ruleNames = new Set<string>();\n for (const source of sources) {\n if (getSourceFilters(source).rules === undefined) {\n continue;\n }\n const npmTransport = (source.transport ?? \"github\") === \"npm\";\n const entry = npmTransport\n ? getNpmLockedSource(npmLock, source.source)\n : getLockedSource(lock, source.source);\n const lockedRuleNames = entry\n ? npmTransport\n ? getNpmLockedRuleNames(entry as NpmLockedSource)\n : getLockedRuleNames(entry as LockedSource)\n : [];\n if (\n entry === undefined ||\n entry.rules === undefined ||\n !lockedRuleConfigMatches({ locked: entry, sourceEntry: source }) ||\n !(await checkLockedRulesAreValid({ curatedDir, locked: entry }))\n ) {\n throw new Error(\n `Existing source \"${source.source}\" is not fully installed. Run 'rulesync install' before adding another source.`,\n );\n }\n lockedRuleNames.forEach((ruleName) => ruleNames.add(ruleName));\n }\n return [...ruleNames];\n}\n\n/**\n * Dispatch a single source to the npm fetcher or the git/github fetcher,\n * returning the (possibly) updated lock objects for both lockfiles.\n */\nasync function fetchSingleSource(params: {\n sourceEntry: SourceEntry;\n client: GitHubClient;\n projectRoot: string;\n lock: SourcesLock;\n npmLock: NpmSourcesLock;\n localSkillNames: Set<string>;\n localRuleNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n updateSources: boolean;\n frozen: boolean;\n logger: Logger;\n}): Promise<{\n skillCount: number;\n ruleCount: number;\n fetchedSkillNames: string[];\n fetchedRuleNames: string[];\n lock: SourcesLock;\n npmLock: NpmSourcesLock;\n}> {\n const { sourceEntry, lock, npmLock } = params;\n if ((sourceEntry.transport ?? \"github\") === \"npm\") {\n const result = await fetchSourceViaNpm({\n sourceEntry,\n projectRoot: params.projectRoot,\n npmLock,\n localSkillNames: params.localSkillNames,\n localRuleNames: params.localRuleNames,\n alreadyFetchedSkillNames: params.alreadyFetchedSkillNames,\n alreadyFetchedRuleNames: params.alreadyFetchedRuleNames,\n updateSources: params.updateSources,\n logger: params.logger,\n });\n return {\n skillCount: result.skillCount,\n ruleCount: result.ruleCount,\n fetchedSkillNames: result.fetchedSkillNames,\n fetchedRuleNames: result.fetchedRuleNames,\n lock,\n npmLock: result.updatedLock,\n };\n }\n const filters = getSourceFilters(sourceEntry);\n let updatedLock = lock;\n let skillCount = 0;\n let fetchedSkillNames: string[] = [];\n if (filters.skills !== undefined) {\n const result = await fetchSourceByTransport({\n sourceEntry: { ...sourceEntry, skills: filters.skills },\n client: params.client,\n projectRoot: params.projectRoot,\n lock: updatedLock,\n localSkillNames: params.localSkillNames,\n alreadyFetchedSkillNames: params.alreadyFetchedSkillNames,\n updateSources: params.updateSources,\n frozen: params.frozen,\n logger: params.logger,\n });\n updatedLock = result.updatedLock;\n skillCount = result.skillCount;\n fetchedSkillNames = result.fetchedSkillNames;\n }\n\n let ruleCount = 0;\n let fetchedRuleNames: string[] = [];\n if (filters.rules !== undefined) {\n const result = await fetchRulesByTransport({\n sourceEntry: { ...sourceEntry, rules: filters.rules },\n client: params.client,\n projectRoot: params.projectRoot,\n lock: updatedLock,\n localRuleNames: params.localRuleNames,\n alreadyFetchedRuleNames: params.alreadyFetchedRuleNames,\n // A preceding skill fetch has already resolved and locked this source.\n // Reuse that exact ref so one source cannot mix artifacts from two SHAs.\n updateSources: filters.skills === undefined ? params.updateSources : false,\n forceRefetch: filters.skills !== undefined && params.updateSources,\n frozen: params.frozen,\n logger: params.logger,\n });\n updatedLock = result.updatedLock;\n ruleCount = result.ruleCount;\n fetchedRuleNames = result.fetchedRuleNames;\n } else {\n updatedLock = await clearUndeclaredRules({\n lock: updatedLock,\n sourceEntry,\n projectRoot: params.projectRoot,\n alreadyFetchedRuleNames: params.alreadyFetchedRuleNames,\n logger: params.logger,\n });\n }\n return {\n skillCount,\n ruleCount,\n fetchedSkillNames,\n fetchedRuleNames,\n lock: updatedLock,\n npmLock,\n };\n}\n\nasync function clearUndeclaredRules(params: {\n lock: SourcesLock;\n sourceEntry: SourceEntry;\n projectRoot: string;\n alreadyFetchedRuleNames: Set<string>;\n logger: Logger;\n}): Promise<SourcesLock> {\n const { lock, sourceEntry, projectRoot, alreadyFetchedRuleNames, logger } = params;\n const locked = getLockedSource(lock, sourceEntry.source);\n if (locked?.rules === undefined) {\n return lock;\n }\n await cleanPreviousCuratedRules({\n curatedDir: join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH),\n lockedRuleNames: getLockedRuleNames(locked),\n protectedRuleNames: alreadyFetchedRuleNames,\n logger,\n });\n return setLockedSource(lock, sourceEntry.source, {\n ...locked,\n rules: undefined,\n ruleSelection: undefined,\n rulesPath: undefined,\n resolvedRuleNames: undefined,\n });\n}\n\n/** Log a per-source fetch failure with transport-specific troubleshooting hints. */\nfunction logSourceFetchFailure(params: {\n sourceEntry: SourceEntry;\n error: unknown;\n logger: Logger;\n}): void {\n const { sourceEntry, error, logger } = params;\n logger.error(`Failed to fetch source \"${sourceEntry.source}\": ${formatError(error)}`);\n if (error instanceof GitHubClientError) {\n logGitHubAuthHints({ error, logger });\n } else if (error instanceof GitClientError) {\n logGitClientHints({ error, logger });\n } else if (error instanceof NpmClientError) {\n logNpmAuthHints({ error, logger });\n }\n}\n\n/** Write each lockfile only when it changed (and never in frozen mode). */\nasync function writeLockFilesIfChanged(params: {\n projectRoot: string;\n lock: SourcesLock;\n npmLock: NpmSourcesLock;\n originalLockJson: string;\n originalNpmLockJson: string;\n frozen: boolean;\n logger: Logger;\n}): Promise<void> {\n const { projectRoot, lock, npmLock, originalLockJson, originalNpmLockJson, frozen, logger } =\n params;\n if (!frozen && JSON.stringify(lock) !== originalLockJson) {\n await writeLockFile({ projectRoot, lock, logger });\n } else {\n logger.debug(\"Lockfile unchanged, skipping write.\");\n }\n if (!frozen && JSON.stringify(npmLock) !== originalNpmLockJson) {\n await writeNpmLockFile({ projectRoot, lock: npmLock, logger });\n } else {\n logger.debug(\"npm lockfile unchanged, skipping write.\");\n }\n}\n\n/**\n * Log contextual hints for GitClientError to help users troubleshoot.\n */\nfunction logGitClientHints(params: { error: GitClientError; logger: Logger }): void {\n const { error, logger } = params;\n if (error.message.includes(\"not installed\")) {\n logger.info(\"Hint: Install git and ensure it is available on your PATH.\");\n } else {\n logger.info(\"Hint: Check your git credentials (SSH keys, credential helper, or access token).\");\n }\n}\n\n/**\n * Frozen mode: validate the lockfiles cover every declared source. Throws with\n * remediation guidance listing any uncovered source keys. npm-transport\n * sources are checked against the npm lockfile; everything else against the\n * main sources lockfile.\n */\nfunction assertFrozenLockCoversSources(params: {\n lock: SourcesLock;\n npmLock: NpmSourcesLock;\n sources: SourceEntry[];\n}): void {\n const { lock, npmLock, sources } = params;\n const missingKeys: string[] = [];\n\n for (const source of sources) {\n const locked =\n (source.transport ?? \"github\") === \"npm\"\n ? getNpmLockedSource(npmLock, source.source)\n : getLockedSource(lock, source.source);\n const rulesCovered =\n getSourceFilters(source).rules === undefined ||\n (locked !== undefined && lockedRuleConfigMatches({ locked, sourceEntry: source }));\n if (!locked || !rulesCovered) {\n missingKeys.push(source.source);\n }\n }\n if (missingKeys.length > 0) {\n throw new Error(\n `Frozen install failed: lockfile is missing entries for: ${missingKeys.join(\", \")}. Run 'rulesync install' to update the lockfile.`,\n );\n }\n}\n\nfunction validateFrozenLockCoverage(params: {\n frozen: boolean;\n lock: SourcesLock;\n npmLock: NpmSourcesLock;\n sources: SourceEntry[];\n}): void {\n if (params.frozen) {\n assertFrozenLockCoversSources(params);\n }\n}\n\n/**\n * Dispatch a single source to the transport-specific fetcher (git CLI vs.\n * GitHub REST API), preserving the original default of \"github\".\n */\nasync function fetchSourceByTransport(params: {\n sourceEntry: SourceEntry;\n client: GitHubClient;\n projectRoot: string;\n lock: SourcesLock;\n localSkillNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n updateSources: boolean;\n frozen: boolean;\n logger: Logger;\n}): Promise<{ skillCount: number; fetchedSkillNames: string[]; updatedLock: SourcesLock }> {\n const {\n sourceEntry,\n client,\n projectRoot,\n lock,\n localSkillNames,\n alreadyFetchedSkillNames,\n updateSources,\n frozen,\n logger,\n } = params;\n const transport = sourceEntry.transport ?? \"github\";\n if (transport === \"git\") {\n return fetchSourceViaGit({\n sourceEntry,\n projectRoot,\n lock,\n localSkillNames,\n alreadyFetchedSkillNames,\n updateSources,\n frozen,\n logger,\n });\n }\n return fetchSource({\n sourceEntry,\n client,\n projectRoot,\n lock,\n localSkillNames,\n alreadyFetchedSkillNames,\n updateSources,\n logger,\n });\n}\n\n/**\n * Prune stale lockfile entries whose keys are not in the current sources\n * (immutable — returns a fresh lock object).\n */\nfunction pruneStaleLockEntries(params: {\n lock: SourcesLock;\n sources: SourceEntry[];\n logger: Logger;\n}): SourcesLock {\n const { lock, sources, logger } = params;\n const sourceKeys = new Set(\n sources\n .filter((s) => (s.transport ?? \"github\") !== \"npm\")\n .map((s) => normalizeSourceKey(s.source)),\n );\n const prunedSources: typeof lock.sources = {};\n for (const [key, value] of Object.entries(lock.sources)) {\n if (sourceKeys.has(normalizeSourceKey(key))) {\n prunedSources[key] = value;\n } else {\n logger.debug(`Pruned stale lockfile entry: ${key}`);\n }\n }\n return { lockfileVersion: lock.lockfileVersion, sources: prunedSources };\n}\n\n/**\n * Prune stale npm lockfile entries whose keys are not in the current\n * npm-transport sources (immutable — returns a fresh lock object).\n */\nfunction pruneStaleNpmLockEntries(params: {\n npmLock: NpmSourcesLock;\n sources: SourceEntry[];\n logger: Logger;\n}): NpmSourcesLock {\n const { npmLock, sources, logger } = params;\n const sourceKeys = new Set(\n sources\n .filter((s) => (s.transport ?? \"github\") === \"npm\")\n .map((s) => normalizeNpmSourceKey(s.source)),\n );\n const prunedSources: typeof npmLock.sources = {};\n for (const [key, value] of Object.entries(npmLock.sources)) {\n if (sourceKeys.has(normalizeNpmSourceKey(key))) {\n prunedSources[key] = value;\n } else {\n logger.debug(`Pruned stale npm lockfile entry: ${key}`);\n }\n }\n return { lockfileVersion: npmLock.lockfileVersion, sources: prunedSources };\n}\n\nasync function cleanUnlistedSourceArtifacts(params: {\n projectRoot: string;\n lock: SourcesLock;\n npmLock: NpmSourcesLock;\n sources: SourceEntry[];\n logger: Logger;\n}): Promise<void> {\n const { projectRoot, lock, npmLock, sources, logger } = params;\n const activeGitKeys = new Set(\n sources\n .filter((source) => (source.transport ?? \"github\") !== \"npm\")\n .map((source) => normalizeSourceKey(source.source)),\n );\n const activeNpmKeys = new Set(\n sources\n .filter((source) => (source.transport ?? \"github\") === \"npm\")\n .map((source) => normalizeNpmSourceKey(source.source)),\n );\n const activeEntries = [\n ...Object.entries(lock.sources)\n .filter(([key]) => activeGitKeys.has(normalizeSourceKey(key)))\n .map(([, entry]) => entry),\n ...Object.entries(npmLock.sources)\n .filter(([key]) => activeNpmKeys.has(normalizeNpmSourceKey(key)))\n .map(([, entry]) => entry),\n ];\n const protectedSkillNames = new Set(activeEntries.flatMap((entry) => Object.keys(entry.skills)));\n const protectedRuleNames = new Set(\n activeEntries.flatMap((entry) => Object.keys(entry.rules ?? {})),\n );\n const staleEntries = [\n ...Object.entries(lock.sources)\n .filter(([key]) => !activeGitKeys.has(normalizeSourceKey(key)))\n .map(([, entry]) => entry),\n ...Object.entries(npmLock.sources)\n .filter(([key]) => !activeNpmKeys.has(normalizeNpmSourceKey(key)))\n .map(([, entry]) => entry),\n ];\n const curatedSkillsDir = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n const curatedRulesDir = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n for (const entry of staleEntries) {\n await cleanPreviousCuratedSkills({\n curatedDir: curatedSkillsDir,\n lockedSkillNames: Object.keys(entry.skills),\n protectedSkillNames,\n logger,\n });\n await cleanPreviousCuratedRules({\n curatedDir: curatedRulesDir,\n lockedRuleNames: Object.keys(entry.rules ?? {}),\n protectedRuleNames,\n logger,\n });\n }\n}\n\n/**\n * Check if all locked skills exist on disk in the curated directory.\n */\nasync function checkLockedSkillsExist(curatedDir: string, skillNames: string[]): Promise<boolean> {\n if (skillNames.length === 0) return true;\n for (const name of skillNames) {\n if (!(await directoryExists(join(curatedDir, name)))) {\n return false;\n }\n }\n return true;\n}\n\nasync function checkLockedRulesAreValid(params: {\n curatedDir: string;\n locked: Pick<LockedSource, \"rules\">;\n}): Promise<boolean> {\n for (const [name, entry] of Object.entries(params.locked.rules ?? {})) {\n const filePath = join(params.curatedDir, `${name}.md`);\n if (!(await fileExists(filePath))) {\n return false;\n }\n if (computeRuleIntegrity(await readFileContent(filePath)) !== entry.integrity) {\n return false;\n }\n }\n return true;\n}\n\nasync function canReuseLockedRules(params: {\n locked: LockedSource | NpmLockedSource;\n sourceEntry: SourceEntry;\n lockedRuleNames: string[];\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n curatedDir: string;\n}): Promise<boolean> {\n const {\n locked,\n sourceEntry,\n lockedRuleNames,\n localRuleNames,\n alreadyFetchedRuleNames,\n curatedDir,\n } = params;\n if (!lockedRuleConfigMatches({ locked, sourceEntry })) {\n return false;\n }\n if (locked.resolvedRuleNames === undefined) {\n return false;\n }\n const availableRuleNames = new Set([\n ...lockedRuleNames,\n ...localRuleNames,\n ...alreadyFetchedRuleNames,\n ]);\n if (locked.resolvedRuleNames.some((ruleName) => !availableRuleNames.has(ruleName))) {\n return false;\n }\n if (\n lockedRuleNames.some(\n (ruleName) => localRuleNames.has(ruleName) || alreadyFetchedRuleNames.has(ruleName),\n )\n ) {\n return false;\n }\n return checkLockedRulesAreValid({ curatedDir, locked });\n}\n\n// ---------------------------------------------------------------------------\n// Shared helpers for fetchSource and fetchSourceViaGit\n// ---------------------------------------------------------------------------\n\n/**\n * Remove previously curated skill directories for a source before re-fetching.\n * Validates that each path resolves within the curated directory to prevent traversal.\n */\nasync function cleanPreviousCuratedSkills(params: {\n curatedDir: string;\n lockedSkillNames: string[];\n protectedSkillNames?: Set<string>;\n logger: Logger;\n}): Promise<void> {\n const { curatedDir, lockedSkillNames, protectedSkillNames = new Set(), logger } = params;\n const resolvedCuratedDir = resolve(curatedDir);\n for (const prevSkill of lockedSkillNames) {\n if (protectedSkillNames.has(prevSkill)) {\n continue;\n }\n const prevDir = join(curatedDir, prevSkill);\n if (!resolve(prevDir).startsWith(resolvedCuratedDir + sep)) {\n logger.warn(\n `Skipping removal of \"${prevSkill}\": resolved path is outside the curated directory.`,\n );\n continue;\n }\n if (await directoryExists(prevDir)) {\n await removeDirectoryStrict(prevDir);\n }\n }\n}\n\nasync function cleanPreviousCuratedRules(params: {\n curatedDir: string;\n lockedRuleNames: string[];\n protectedRuleNames: Set<string>;\n logger: Logger;\n}): Promise<void> {\n const { curatedDir, lockedRuleNames, protectedRuleNames, logger } = params;\n const resolvedCuratedDir = resolve(curatedDir);\n for (const prevRule of lockedRuleNames) {\n if (protectedRuleNames.has(prevRule)) {\n continue;\n }\n const prevFile = join(curatedDir, `${prevRule}.md`);\n if (!resolve(prevFile).startsWith(resolvedCuratedDir + sep)) {\n logger.warn(\n `Skipping removal of \"${prevRule}\": resolved path is outside the curated directory.`,\n );\n continue;\n }\n if (await fileExists(prevFile)) {\n await removeFileStrict(prevFile);\n }\n }\n}\n\nasync function replaceCuratedRules(params: {\n rules: RemoteRuleFile[];\n curatedDir: string;\n locked: LockedSource | undefined;\n lockedRuleNames: string[];\n resolvedRef: string;\n sourceKey: string;\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n compareLockedIntegrity: boolean;\n logger: Logger;\n}): Promise<Record<string, LockedRule>> {\n const {\n rules,\n curatedDir,\n locked,\n lockedRuleNames,\n resolvedRef,\n sourceKey,\n localRuleNames,\n alreadyFetchedRuleNames,\n compareLockedIntegrity,\n logger,\n } = params;\n const protectedRuleNames = alreadyFetchedRuleNames;\n const installableRules = rules.filter(\n (rule) =>\n !shouldSkipRule({\n ruleName: rule.name,\n sourceKey,\n localRuleNames,\n alreadyFetchedRuleNames,\n logger,\n }),\n );\n const previousContents = new Map<string, string>();\n for (const name of lockedRuleNames) {\n const path = join(curatedDir, `${name}.md`);\n if (!protectedRuleNames.has(name) && (await fileExists(path))) {\n previousContents.set(name, await readFileContent(path));\n }\n }\n\n try {\n await cleanPreviousCuratedRules({\n curatedDir,\n lockedRuleNames,\n protectedRuleNames,\n logger,\n });\n const fetchedRules: Record<string, LockedRule> = {};\n for (const rule of installableRules) {\n fetchedRules[rule.name] = await writeRuleAndComputeIntegrity({\n rule,\n curatedDir,\n locked,\n resolvedRef,\n sourceKey,\n compareLockedIntegrity,\n logger,\n });\n }\n return fetchedRules;\n } catch (error) {\n for (const rule of installableRules) {\n await removeFileStrict(join(curatedDir, `${rule.name}.md`));\n }\n for (const [name, content] of previousContents) {\n await writeFileContent(join(curatedDir, `${name}.md`), content);\n }\n throw error;\n }\n}\n\n/**\n * Check whether a skill should be skipped during fetching.\n * Returns true (with appropriate logging) if the skill should be skipped.\n */\nfunction shouldSkipSkill(params: {\n skillName: string;\n sourceKey: string;\n localSkillNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n logger: Logger;\n}): boolean {\n const { skillName, sourceKey, localSkillNames, alreadyFetchedSkillNames, logger } = params;\n if (skillName.includes(\"..\") || skillName.includes(\"/\") || skillName.includes(\"\\\\\")) {\n logger.warn(\n `Skipping skill with invalid name \"${skillName}\" from ${sourceKey}: contains path traversal characters.`,\n );\n return true;\n }\n if (localSkillNames.has(skillName)) {\n logger.debug(\n `Skipping remote skill \"${skillName}\" from ${sourceKey}: local skill takes precedence.`,\n );\n return true;\n }\n if (alreadyFetchedSkillNames.has(skillName)) {\n logger.warn(\n `Skipping duplicate skill \"${skillName}\" from ${sourceKey}: already fetched from another source.`,\n );\n return true;\n }\n return false;\n}\n\nfunction shouldSkipRule(params: {\n ruleName: string;\n sourceKey: string;\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n logger: Logger;\n}): boolean {\n const { ruleName, sourceKey, localRuleNames, alreadyFetchedRuleNames, logger } = params;\n if (!isValidRuleName(ruleName)) {\n logger.warn(`Skipping rule with invalid name \"${ruleName}\" from ${sourceKey}.`);\n return true;\n }\n if (localRuleNames.has(ruleName)) {\n logger.debug(\n `Skipping remote rule \"${ruleName}\" from ${sourceKey}: local rule takes precedence.`,\n );\n return true;\n }\n if (alreadyFetchedRuleNames.has(ruleName)) {\n logger.warn(\n `Skipping duplicate rule \"${ruleName}\" from ${sourceKey}: already fetched from another source.`,\n );\n return true;\n }\n return false;\n}\n\nfunction isValidRuleName(ruleName: string): boolean {\n return !(\n ruleName.includes(\"..\") ||\n ruleName.includes(\"/\") ||\n ruleName.includes(\"\\\\\") ||\n ruleName.length === 0 ||\n [\"__proto__\", \"constructor\", \"prototype\"].includes(ruleName)\n );\n}\n\nasync function writeRuleAndComputeIntegrity(params: {\n rule: RemoteRuleFile;\n curatedDir: string;\n locked: LockedSource | undefined;\n resolvedRef: string;\n sourceKey: string;\n compareLockedIntegrity?: boolean;\n logger: Logger;\n}): Promise<LockedRule> {\n const {\n rule,\n curatedDir,\n locked,\n resolvedRef,\n sourceKey,\n compareLockedIntegrity = true,\n logger,\n } = params;\n const relativePath = `${rule.name}.md`;\n checkPathTraversal({ relativePath, intendedRootDir: curatedDir });\n await writeFileContent(join(curatedDir, relativePath), rule.content);\n const integrity = computeRuleIntegrity(rule.content);\n const lockedRuleEntry = locked?.rules?.[rule.name];\n if (\n compareLockedIntegrity &&\n lockedRuleEntry?.integrity &&\n lockedRuleEntry.integrity !== integrity &&\n resolvedRef === locked?.resolvedRef\n ) {\n logger.warn(\n `Integrity mismatch for rule \"${rule.name}\" from ${sourceKey}: expected \"${lockedRuleEntry.integrity}\", got \"${integrity}\". Content may have been tampered with.`,\n );\n }\n return { integrity };\n}\n\n/**\n * Write skill files to disk, compute integrity, and check against the lockfile.\n * Returns the computed LockedSkill entry.\n */\nasync function writeSkillAndComputeIntegrity(params: {\n skillName: string;\n files: Array<{ relativePath: string; content: string }>;\n curatedDir: string;\n locked: LockedSource | undefined;\n resolvedSha: string;\n sourceKey: string;\n logger: Logger;\n}): Promise<LockedSkill> {\n const { skillName, files, curatedDir, locked, resolvedSha, sourceKey, logger } = params;\n const written: Array<{ path: string; content: string }> = [];\n\n for (const file of files) {\n checkPathTraversal({\n relativePath: file.relativePath,\n intendedRootDir: join(curatedDir, skillName),\n });\n await writeFileContent(join(curatedDir, skillName, file.relativePath), file.content);\n written.push({ path: file.relativePath, content: file.content });\n }\n\n const integrity = computeSkillIntegrity(written);\n const lockedSkillEntry = locked?.skills[skillName];\n if (\n lockedSkillEntry?.integrity &&\n lockedSkillEntry.integrity !== integrity &&\n resolvedSha === locked?.resolvedRef\n ) {\n logger.warn(\n `Integrity mismatch for skill \"${skillName}\" from ${sourceKey}: expected \"${lockedSkillEntry.integrity}\", got \"${integrity}\". Content may have been tampered with.`,\n );\n }\n\n return { integrity };\n}\n\n/**\n * Merge back locked skills that still exist in the remote but were skipped\n * during fetching (due to local precedence, already-fetched, etc.). Skills no\n * longer present in the remote (e.g. renamed or deleted upstream) are\n * intentionally dropped. Shared by the git/github and npm lock updates.\n */\nfunction mergeFetchedWithLockedSkills(params: {\n fetchedSkills: Record<string, LockedSkill>;\n lockedSkills: Record<string, LockedSkill> | undefined;\n remoteSkillNames: string[];\n}): Record<string, LockedSkill> {\n const { fetchedSkills, lockedSkills, remoteSkillNames } = params;\n const remoteSet = new Set(remoteSkillNames);\n const mergedSkills: Record<string, LockedSkill> = { ...fetchedSkills };\n if (lockedSkills) {\n for (const [skillName, skillEntry] of Object.entries(lockedSkills)) {\n if (!(skillName in mergedSkills) && remoteSet.has(skillName)) {\n mergedSkills[skillName] = skillEntry;\n }\n }\n }\n return mergedSkills;\n}\n\nfunction assertMatchingSkillsFound({\n skillNames,\n source,\n}: {\n skillNames: string[];\n source: string;\n}): void {\n if (skillNames.length === 0) {\n throw new Error(`No matching skills found in ${source}.`);\n }\n}\n\n/**\n * Merge newly fetched skills with existing locked skills and update the lockfile.\n */\nfunction buildLockUpdate(params: {\n lock: SourcesLock;\n sourceKey: string;\n fetchedSkills: Record<string, LockedSkill>;\n locked: LockedSource | undefined;\n requestedRef: string | undefined;\n resolvedSha: string;\n remoteSkillNames: string[];\n logger: Logger;\n}): { updatedLock: SourcesLock; fetchedNames: string[] } {\n const {\n lock,\n sourceKey,\n fetchedSkills,\n locked,\n requestedRef,\n resolvedSha,\n remoteSkillNames,\n logger,\n } = params;\n const fetchedNames = Object.keys(fetchedSkills);\n\n const mergedSkills = mergeFetchedWithLockedSkills({\n fetchedSkills,\n lockedSkills: locked?.skills,\n remoteSkillNames,\n });\n\n const updatedLock = setLockedSource(lock, sourceKey, {\n requestedRef,\n resolvedRef: resolvedSha,\n resolvedAt: new Date().toISOString(),\n skills: mergedSkills,\n rules: locked?.rules ?? {},\n ruleSelection: locked?.ruleSelection,\n rulesPath: locked?.rulesPath,\n resolvedRuleNames: locked?.resolvedRuleNames,\n });\n\n logger.info(\n `Fetched ${fetchedNames.length} skill(s) from ${sourceKey}: ${fetchedNames.join(\", \") || \"(none)\"}`,\n );\n\n return { updatedLock, fetchedNames };\n}\n\nfunction buildRuleLockUpdate(params: {\n lock: SourcesLock;\n sourceKey: string;\n fetchedRules: Record<string, LockedRule>;\n locked: LockedSource | undefined;\n requestedRef: string | undefined;\n resolvedRef: string;\n ruleSelection: string[];\n rulesPath: string;\n resolvedRuleNames: string[];\n logger: Logger;\n}): { updatedLock: SourcesLock; fetchedNames: string[] } {\n const {\n lock,\n sourceKey,\n fetchedRules,\n locked,\n requestedRef,\n resolvedRef,\n ruleSelection,\n rulesPath,\n resolvedRuleNames,\n logger,\n } = params;\n const fetchedNames = Object.keys(fetchedRules);\n const updatedLock = setLockedSource(lock, sourceKey, {\n requestedRef,\n resolvedRef,\n resolvedAt: new Date().toISOString(),\n skills: locked?.skills ?? {},\n rules: fetchedRules,\n ruleSelection,\n rulesPath,\n resolvedRuleNames,\n });\n logger.info(\n `Fetched ${fetchedNames.length} rule(s) from ${sourceKey}: ${fetchedNames.join(\", \") || \"(none)\"}`,\n );\n return { updatedLock, fetchedNames };\n}\n\nfunction getFirstPathSeparatorIndex(path: string): number {\n const slashIndex = path.indexOf(\"/\");\n const backslashIndex = path.indexOf(\"\\\\\");\n if (slashIndex === -1) return backslashIndex;\n if (backslashIndex === -1) return slashIndex;\n return Math.min(slashIndex, backslashIndex);\n}\n\n/**\n * Decide whether a repository's root-level files should be installed as the\n * single requested skill (the \"root fallback\").\n *\n * A root fallback fires only when a single, non-wildcard skill was requested,\n * that skill's own directory is absent, and the repository root actually carries\n * a `SKILL.md`. Both the git transport (`groupRemoteFilesBySkillRoot`) and the\n * GitHub transport (`discoverGithubSkillDirs`) gate on these same conditions, so\n * the decision lives here to keep the two paths from drifting.\n */\nfunction shouldUseRootFallback(params: {\n skillFilter: string[];\n isWildcard: boolean;\n hasRootSkillFile: boolean;\n hasRequestedSkillDir: boolean;\n}): boolean {\n const { skillFilter, isWildcard, hasRootSkillFile, hasRequestedSkillDir } = params;\n const [singleSkillName] = skillFilter;\n return (\n !isWildcard &&\n skillFilter.length === 1 &&\n singleSkillName !== undefined &&\n hasRootSkillFile &&\n !hasRequestedSkillDir\n );\n}\n\nfunction groupRemoteFilesBySkillRoot(params: {\n remoteFiles: RemoteSkillFile[];\n skillFilter: string[];\n isWildcard: boolean;\n}): Map<string, RemoteSkillFile[]> {\n const { remoteFiles, skillFilter, isWildcard } = params;\n const grouped = new Map<string, RemoteSkillFile[]>();\n const rootLevelFiles: RemoteSkillFile[] = [];\n\n for (const file of remoteFiles) {\n const separatorIndex = getFirstPathSeparatorIndex(file.relativePath);\n if (separatorIndex === -1) {\n rootLevelFiles.push(file);\n continue;\n }\n\n const skillName = file.relativePath.substring(0, separatorIndex);\n if (skillName.length === 0) {\n continue;\n }\n\n const innerPath = file.relativePath.substring(separatorIndex + 1);\n const groupedFiles = grouped.get(skillName) ?? [];\n groupedFiles.push({ relativePath: innerPath, content: file.content });\n grouped.set(skillName, groupedFiles);\n }\n\n const [singleSkillName] = skillFilter;\n const hasRootSkillFile = rootLevelFiles.some((file) => file.relativePath === SKILL_FILE_NAME);\n if (\n singleSkillName !== undefined &&\n shouldUseRootFallback({\n skillFilter,\n isWildcard,\n hasRootSkillFile,\n hasRequestedSkillDir: grouped.has(singleSkillName),\n })\n ) {\n grouped.set(singleSkillName, rootLevelFiles);\n }\n\n return grouped;\n}\n\n// ---------------------------------------------------------------------------\n// Transport-specific fetch functions\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a GitHub source's ref to a commit SHA, preferring the locked SHA for\n * deterministic fetches and otherwise resolving the declared ref or default\n * branch. Returns the on-disk `ref` (SHA when freshly resolved, else locked\n * ref), the resolved SHA, and the requested ref.\n */\nasync function resolveGithubFetchRef(params: {\n parsed: ParsedSource;\n locked: LockedSource | undefined;\n updateSources: boolean;\n sourceKey: string;\n client: GitHubClient;\n logger: Logger;\n}): Promise<{ ref: string; resolvedSha: string; requestedRef: string | undefined }> {\n const { parsed, locked, updateSources, sourceKey, client, logger } = params;\n if (locked && !updateSources) {\n // Use the locked SHA for deterministic fetching\n logger.debug(`Using locked ref for ${sourceKey}: ${locked.resolvedRef}`);\n return {\n ref: locked.resolvedRef,\n resolvedSha: locked.resolvedRef,\n requestedRef: locked.requestedRef,\n };\n }\n // Resolve the ref (or default branch) to a SHA\n const requestedRef = parsed.ref ?? (await client.getDefaultBranch(parsed.owner, parsed.repo));\n const resolvedSha = await client.resolveRefToSha(parsed.owner, parsed.repo, requestedRef);\n logger.debug(`Resolved ${sourceKey} ref \"${requestedRef}\" to SHA: ${resolvedSha}`);\n return { ref: resolvedSha, resolvedSha, requestedRef };\n}\n\nfunction normalizeRuleFilterName(name: string): string {\n return name.replace(/\\.md$/i, \"\");\n}\n\nfunction normalizeRuleSelection(rules: string[]): string[] {\n return [...new Set(rules.map(normalizeRuleFilterName))].toSorted();\n}\n\nfunction normalizeRulesPath(rulesPath: string | undefined): string {\n return posix.normalize((rulesPath ?? \"rules\").replace(/\\\\/g, \"/\")).replace(/\\/+$/, \"\");\n}\n\nfunction lockedRuleConfigMatches(params: {\n locked: Pick<LockedSource, \"ruleSelection\" | \"rulesPath\">;\n sourceEntry: SourceEntry;\n}): boolean {\n const rules = getSourceFilters(params.sourceEntry).rules;\n if (rules === undefined || params.locked.ruleSelection === undefined) {\n return false;\n }\n const selection = normalizeRuleSelection(rules);\n return (\n selection.length === params.locked.ruleSelection.length &&\n selection.every((ruleName, index) => ruleName === params.locked.ruleSelection?.[index]) &&\n normalizeRulesPath(params.sourceEntry.rulesPath) === params.locked.rulesPath\n );\n}\n\nfunction assertMatchingRulesFound(params: { ruleNames: string[]; source: string }): void {\n if (params.ruleNames.length === 0) {\n throw new Error(`No matching rules found in ${params.source}.`);\n }\n}\n\nasync function fetchRulesByTransport(params: {\n sourceEntry: SourceEntry;\n client: GitHubClient;\n projectRoot: string;\n lock: SourcesLock;\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n updateSources: boolean;\n forceRefetch: boolean;\n frozen: boolean;\n logger: Logger;\n}): Promise<{ ruleCount: number; fetchedRuleNames: string[]; updatedLock: SourcesLock }> {\n if ((params.sourceEntry.transport ?? \"github\") === \"git\") {\n return fetchRulesViaGit(params);\n }\n return fetchRulesViaGithub(params);\n}\n\nasync function fetchRulesViaGithub(params: {\n sourceEntry: SourceEntry;\n client: GitHubClient;\n projectRoot: string;\n lock: SourcesLock;\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n updateSources: boolean;\n forceRefetch: boolean;\n logger: Logger;\n}): Promise<{ ruleCount: number; fetchedRuleNames: string[]; updatedLock: SourcesLock }> {\n const {\n sourceEntry,\n client,\n projectRoot,\n lock,\n localRuleNames,\n alreadyFetchedRuleNames,\n updateSources,\n forceRefetch,\n logger,\n } = params;\n const parsedFromSource = parseSource(sourceEntry.source);\n const parsed: ParsedSource = {\n ...parsedFromSource,\n ref: sourceEntry.ref ?? parsedFromSource.ref,\n };\n if (parsed.provider === \"gitlab\") {\n throw new Error(`GitLab sources are not yet supported: \"${sourceEntry.source}\".`);\n }\n const sourceKey = sourceEntry.source;\n const locked = getLockedSource(lock, sourceKey);\n const lockedRuleNames = locked ? getLockedRuleNames(locked) : [];\n const { ref, resolvedSha, requestedRef } = await resolveGithubFetchRef({\n parsed,\n locked,\n updateSources,\n sourceKey,\n client,\n logger,\n });\n const curatedDir = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n if (\n locked &&\n resolvedSha === locked.resolvedRef &&\n !updateSources &&\n !forceRefetch &&\n (await canReuseLockedRules({\n locked,\n sourceEntry,\n lockedRuleNames,\n localRuleNames,\n alreadyFetchedRuleNames,\n curatedDir,\n }))\n ) {\n logger.debug(`SHA unchanged for ${sourceKey} rules, skipping re-fetch.`);\n return { ruleCount: 0, fetchedRuleNames: lockedRuleNames, updatedLock: lock };\n }\n\n const ruleFilter = (sourceEntry.rules ?? []).map(normalizeRuleFilterName);\n const isWildcard = ruleFilter.length === 1 && ruleFilter[0] === \"*\";\n const rulesPath = sourceEntry.rulesPath ?? \"rules\";\n let entries: GitHubFileEntry[];\n try {\n entries = await client.listDirectory(parsed.owner, parsed.repo, rulesPath, ref);\n } catch (error) {\n if (error instanceof GitHubClientError && error.statusCode === 404) {\n throw new Error(`No ${rulesPath}/ directory found in ${sourceKey}.`, { cause: error });\n }\n throw error;\n }\n const remoteRules = entries\n .filter((entry) => entry.type === \"file\" && entry.name.toLowerCase().endsWith(\".md\"))\n .map((entry) => ({ entry, name: normalizeRuleFilterName(entry.name) }))\n .filter(({ name }) => (isWildcard || ruleFilter.includes(name)) && isValidRuleName(name));\n const remoteRuleNames = remoteRules.map(({ name }) => name);\n assertMatchingRulesFound({ ruleNames: remoteRuleNames, source: sourceKey });\n const preparedRules: RemoteRuleFile[] = [];\n for (const { entry, name } of remoteRules) {\n if (entry.size > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping rule \"${entry.path}\" (${(entry.size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit).`,\n );\n continue;\n }\n if (\n shouldSkipRule({\n ruleName: name,\n sourceKey,\n localRuleNames,\n alreadyFetchedRuleNames,\n logger,\n })\n ) {\n continue;\n }\n const content = await client.getFileContent(parsed.owner, parsed.repo, entry.path, ref);\n preparedRules.push({ name, content });\n }\n const fetchedRules = await replaceCuratedRules({\n rules: preparedRules,\n curatedDir,\n locked,\n lockedRuleNames,\n resolvedRef: resolvedSha,\n sourceKey,\n localRuleNames,\n alreadyFetchedRuleNames,\n compareLockedIntegrity: !updateSources && !forceRefetch,\n logger,\n });\n const result = buildRuleLockUpdate({\n lock,\n sourceKey,\n fetchedRules,\n locked,\n requestedRef,\n resolvedRef: resolvedSha,\n ruleSelection: normalizeRuleSelection(sourceEntry.rules ?? []),\n rulesPath: normalizeRulesPath(sourceEntry.rulesPath),\n resolvedRuleNames: remoteRuleNames,\n logger,\n });\n return {\n ruleCount: result.fetchedNames.length,\n fetchedRuleNames: result.fetchedNames,\n updatedLock: result.updatedLock,\n };\n}\n\nasync function fetchRulesViaGit(params: {\n sourceEntry: SourceEntry;\n projectRoot: string;\n lock: SourcesLock;\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n updateSources: boolean;\n forceRefetch: boolean;\n frozen: boolean;\n logger: Logger;\n}): Promise<{ ruleCount: number; fetchedRuleNames: string[]; updatedLock: SourcesLock }> {\n const {\n sourceEntry,\n projectRoot,\n lock,\n localRuleNames,\n alreadyFetchedRuleNames,\n updateSources,\n forceRefetch,\n frozen,\n logger,\n } = params;\n const sourceKey = sourceEntry.source;\n const locked = getLockedSource(lock, sourceKey);\n const lockedRuleNames = locked ? getLockedRuleNames(locked) : [];\n let resolvedRef: string;\n let requestedRef: string | undefined;\n if (locked && !updateSources) {\n resolvedRef = locked.resolvedRef;\n requestedRef = locked.requestedRef;\n if (requestedRef) validateRef(requestedRef);\n } else if (sourceEntry.ref) {\n requestedRef = sourceEntry.ref;\n resolvedRef = await resolveRefToSha(sourceKey, requestedRef);\n } else {\n const defaultRef = await resolveDefaultRef(sourceKey);\n requestedRef = defaultRef.ref;\n resolvedRef = defaultRef.sha;\n }\n const curatedDir = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n if (\n locked &&\n resolvedRef === locked.resolvedRef &&\n !updateSources &&\n !forceRefetch &&\n (await canReuseLockedRules({\n locked,\n sourceEntry,\n lockedRuleNames,\n localRuleNames,\n alreadyFetchedRuleNames,\n curatedDir,\n }))\n ) {\n return { ruleCount: 0, fetchedRuleNames: lockedRuleNames, updatedLock: lock };\n }\n if (!requestedRef) {\n if (frozen) {\n throw new Error(\n `Frozen install failed: lockfile entry for \"${sourceKey}\" is missing requestedRef. Run 'rulesync install' to update the lockfile.`,\n );\n }\n const defaultRef = await resolveDefaultRef(sourceKey);\n requestedRef = defaultRef.ref;\n resolvedRef = defaultRef.sha;\n }\n const files = await fetchSkillFiles({\n url: sourceKey,\n ref: requestedRef,\n resolvedRef,\n skillsPath: sourceEntry.rulesPath ?? \"rules\",\n logger,\n });\n const ruleFilter = (sourceEntry.rules ?? []).map(normalizeRuleFilterName);\n const isWildcard = ruleFilter.length === 1 && ruleFilter[0] === \"*\";\n const remoteRules = files\n .filter(\n (file) =>\n getFirstPathSeparatorIndex(file.relativePath) === -1 &&\n file.relativePath.toLowerCase().endsWith(\".md\"),\n )\n .map((file) => ({ name: normalizeRuleFilterName(file.relativePath), content: file.content }))\n .filter((rule) => (isWildcard || ruleFilter.includes(rule.name)) && isValidRuleName(rule.name));\n const remoteRuleNames = remoteRules.map((rule) => rule.name);\n assertMatchingRulesFound({ ruleNames: remoteRuleNames, source: sourceKey });\n const fetchedRules = await replaceCuratedRules({\n rules: remoteRules,\n curatedDir,\n locked,\n lockedRuleNames,\n resolvedRef,\n sourceKey,\n localRuleNames,\n alreadyFetchedRuleNames,\n compareLockedIntegrity: !updateSources && !forceRefetch,\n logger,\n });\n const result = buildRuleLockUpdate({\n lock,\n sourceKey,\n fetchedRules,\n locked,\n requestedRef,\n resolvedRef,\n ruleSelection: normalizeRuleSelection(sourceEntry.rules ?? []),\n rulesPath: normalizeRulesPath(sourceEntry.rulesPath),\n resolvedRuleNames: remoteRuleNames,\n logger,\n });\n return {\n ruleCount: result.fetchedNames.length,\n fetchedRuleNames: result.fetchedNames,\n updatedLock: result.updatedLock,\n };\n}\n\n/**\n * Fallback path used when an explicit single-skill source points at a flat skill\n * with root-level files. Fetches and writes that skill into `fetchedSkills`.\n * Returns whether the fallback fired and the resulting remote skill names.\n */\nasync function fetchRootLevelFallbackSkill(params: {\n entries: GitHubFileEntry[];\n parsed: ParsedSource;\n ref: string;\n resolvedSha: string;\n skillFilter: string[];\n isWildcard: boolean;\n curatedDir: string;\n locked: LockedSource | undefined;\n sourceKey: string;\n localSkillNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n client: GitHubClient;\n semaphore: Semaphore;\n fetchedSkills: Record<string, LockedSkill>;\n logger: Logger;\n}): Promise<{ handled: boolean; remoteSkillNames: string[] }> {\n const {\n entries,\n parsed,\n ref,\n resolvedSha,\n skillFilter,\n isWildcard,\n curatedDir,\n locked,\n sourceKey,\n localSkillNames,\n alreadyFetchedSkillNames,\n client,\n semaphore,\n fetchedSkills,\n logger,\n } = params;\n\n const rootFiles = entries.filter((entry) => entry.type === \"file\");\n const rootSkillFiles: RemoteSkillFile[] = [];\n\n for (const file of rootFiles) {\n if (file.size > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping file \"${file.path}\" (${(file.size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit).`,\n );\n continue;\n }\n const content = await withSemaphore(semaphore, () =>\n client.getFileContent(parsed.owner, parsed.repo, file.path, ref),\n );\n rootSkillFiles.push({ relativePath: file.name, content });\n }\n\n const groupedRootFiles = groupRemoteFilesBySkillRoot({\n remoteFiles: rootSkillFiles,\n skillFilter,\n isWildcard,\n });\n const [fallbackSkillName] = groupedRootFiles.keys();\n if (fallbackSkillName === undefined) {\n return { handled: false, remoteSkillNames: [] };\n }\n\n if (\n !shouldSkipSkill({\n skillName: fallbackSkillName,\n sourceKey,\n localSkillNames,\n alreadyFetchedSkillNames,\n logger,\n })\n ) {\n fetchedSkills[fallbackSkillName] = await writeSkillAndComputeIntegrity({\n skillName: fallbackSkillName,\n files: groupedRootFiles.get(fallbackSkillName) ?? [],\n curatedDir,\n locked,\n resolvedSha,\n sourceKey,\n logger,\n });\n logger.debug(`Fetched skill \"${fallbackSkillName}\" from ${sourceKey}`);\n }\n\n return { handled: true, remoteSkillNames: [fallbackSkillName] };\n}\n\n/**\n * Recursively fetch and write a single skill directory's files via the GitHub\n * REST API, returning its computed LockedSkill entry.\n */\nasync function fetchGithubSkillDir(params: {\n skillDir: { name: string; path: string };\n parsed: ParsedSource;\n ref: string;\n resolvedSha: string;\n curatedDir: string;\n locked: LockedSource | undefined;\n sourceKey: string;\n client: GitHubClient;\n semaphore: Semaphore;\n logger: Logger;\n}): Promise<LockedSkill> {\n const {\n skillDir,\n parsed,\n ref,\n resolvedSha,\n curatedDir,\n locked,\n sourceKey,\n client,\n semaphore,\n logger,\n } = params;\n\n // Recursively fetch all files in this skill directory\n const allFiles = await listDirectoryRecursive({\n client,\n owner: parsed.owner,\n repo: parsed.repo,\n path: skillDir.path,\n ref,\n semaphore,\n });\n\n // Filter out files exceeding MAX_FILE_SIZE\n const files = allFiles.filter((file) => {\n if (file.size > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping file \"${file.path}\" (${(file.size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit).`,\n );\n return false;\n }\n return true;\n });\n\n // Fetch all file contents\n const skillFiles: Array<{ relativePath: string; content: string }> = [];\n for (const file of files) {\n const relativeToSkill = file.path.substring(skillDir.path.length + 1);\n const content = await withSemaphore(semaphore, () =>\n client.getFileContent(parsed.owner, parsed.repo, file.path, ref),\n );\n skillFiles.push({ relativePath: relativeToSkill, content });\n }\n\n return writeSkillAndComputeIntegrity({\n skillName: skillDir.name,\n files: skillFiles,\n curatedDir,\n locked,\n resolvedSha,\n sourceKey,\n logger,\n });\n}\n\n/**\n * List the remote skills directory and apply the root-level fallback. Returns a\n * `notFound` sentinel when the directory 404s (so the caller can skip the\n * source), otherwise the discovered skill subdirectories plus any fallback skill\n * names already written into `fetchedSkills`.\n */\nasync function discoverGithubSkillDirs(params: {\n parsed: ParsedSource;\n ref: string;\n resolvedSha: string;\n skillFilter: string[];\n isWildcard: boolean;\n curatedDir: string;\n locked: LockedSource | undefined;\n sourceKey: string;\n localSkillNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n client: GitHubClient;\n semaphore: Semaphore;\n fetchedSkills: Record<string, LockedSkill>;\n logger: Logger;\n}): Promise<\n | { status: \"notFound\" }\n | {\n status: \"ok\";\n remoteSkillDirs: Array<{ name: string; path: string }>;\n fallbackHandled: boolean;\n remoteSkillNames: string[];\n }\n> {\n const {\n parsed,\n ref,\n resolvedSha,\n skillFilter,\n isWildcard,\n curatedDir,\n locked,\n sourceKey,\n localSkillNames,\n alreadyFetchedSkillNames,\n client,\n semaphore,\n fetchedSkills,\n logger,\n } = params;\n\n const skillsBasePath = parsed.path ?? \"skills\";\n try {\n const entries = await client.listDirectory(parsed.owner, parsed.repo, skillsBasePath, ref);\n const remoteSkillDirs = entries\n .filter((e) => e.type === \"dir\")\n .map((e) => ({ name: e.name, path: e.path }));\n\n const [singleSkillName] = skillFilter;\n const hasRequestedSkillDir =\n singleSkillName !== undefined && remoteSkillDirs.some((d) => d.name === singleSkillName);\n // Detect a root-level SKILL.md from the directory listing we already have, so\n // the fallback (and its full root-file fetch) is skipped when there is no\n // root skill to install — not just when the requested dir is absent.\n const hasRootSkillFile = entries.some(\n (entry) => entry.type === \"file\" && entry.name === SKILL_FILE_NAME,\n );\n if (\n shouldUseRootFallback({ skillFilter, isWildcard, hasRootSkillFile, hasRequestedSkillDir })\n ) {\n if (locked) {\n await cleanPreviousCuratedSkills({\n curatedDir,\n lockedSkillNames: Object.keys(locked.skills),\n logger,\n });\n }\n const fallback = await fetchRootLevelFallbackSkill({\n entries,\n parsed,\n ref,\n resolvedSha,\n skillFilter,\n isWildcard,\n curatedDir,\n locked,\n sourceKey,\n localSkillNames,\n alreadyFetchedSkillNames,\n client,\n semaphore,\n fetchedSkills,\n logger,\n });\n if (fallback.handled) {\n return {\n status: \"ok\",\n remoteSkillDirs,\n fallbackHandled: true,\n remoteSkillNames: fallback.remoteSkillNames,\n };\n }\n }\n\n return { status: \"ok\", remoteSkillDirs, fallbackHandled: false, remoteSkillNames: [] };\n } catch (error) {\n if (error instanceof GitHubClientError && error.statusCode === 404) {\n return { status: \"notFound\" };\n }\n throw error;\n }\n}\n\n/**\n * Fetch skills from a single source entry via the GitHub REST API.\n */\nasync function fetchSource(params: {\n sourceEntry: SourceEntry;\n client: GitHubClient;\n projectRoot: string;\n lock: SourcesLock;\n localSkillNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n updateSources: boolean;\n logger: Logger;\n}): Promise<{\n skillCount: number;\n fetchedSkillNames: string[];\n updatedLock: SourcesLock;\n}> {\n const {\n sourceEntry,\n client,\n projectRoot,\n localSkillNames,\n alreadyFetchedSkillNames,\n updateSources,\n logger,\n } = params;\n const { lock } = params;\n\n const parsedFromSource = parseSource(sourceEntry.source);\n const parsed: ParsedSource = {\n ...parsedFromSource,\n ref: sourceEntry.ref ?? parsedFromSource.ref,\n path: sourceEntry.path ?? parsedFromSource.path,\n };\n\n if (parsed.provider === \"gitlab\") {\n throw new Error(`GitLab sources are not yet supported: \"${sourceEntry.source}\".`);\n }\n\n const sourceKey = sourceEntry.source;\n const locked = getLockedSource(lock, sourceKey);\n const lockedSkillNames = locked ? getLockedSkillNames(locked) : [];\n\n // Resolve the ref to a commit SHA\n const { ref, resolvedSha, requestedRef } = await resolveGithubFetchRef({\n parsed,\n locked,\n updateSources,\n sourceKey,\n client,\n logger,\n });\n\n const curatedDir = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n\n // Skip re-fetch if SHA matches lockfile and curated skills exist on disk\n if (locked && resolvedSha === locked.resolvedRef && !updateSources) {\n const allExist = await checkLockedSkillsExist(curatedDir, lockedSkillNames);\n if (allExist) {\n logger.debug(`SHA unchanged for ${sourceKey}, skipping re-fetch.`);\n return {\n skillCount: 0,\n fetchedSkillNames: lockedSkillNames,\n updatedLock: lock,\n };\n }\n }\n\n // Determine which skills to fetch\n const skillFilter = sourceEntry.skills ?? [\"*\"];\n const isWildcard = skillFilter.length === 1 && skillFilter[0] === \"*\";\n const semaphore = new Semaphore(FETCH_CONCURRENCY_LIMIT);\n const fetchedSkills: Record<string, LockedSkill> = {};\n\n // List the skills/ directory in the remote repo.\n // If a path is given in the source URL, it points directly to the skills directory.\n // Otherwise, look for \"skills/\" at the repo root.\n const discovery = await discoverGithubSkillDirs({\n parsed,\n ref,\n resolvedSha,\n skillFilter,\n isWildcard,\n curatedDir,\n locked,\n sourceKey,\n localSkillNames,\n alreadyFetchedSkillNames,\n client,\n semaphore,\n fetchedSkills,\n logger,\n });\n if (discovery.status === \"notFound\") {\n throw new Error(`No skills/ directory found in ${sourceKey}.`);\n }\n const { remoteSkillDirs, fallbackHandled, remoteSkillNames: fallbackSkillNames } = discovery;\n\n // Filter skills by name\n const filteredDirs = isWildcard\n ? remoteSkillDirs\n : remoteSkillDirs.filter((d) => skillFilter.includes(d.name));\n const remoteSkillNames = fallbackHandled ? fallbackSkillNames : filteredDirs.map((d) => d.name);\n assertMatchingSkillsFound({ skillNames: remoteSkillNames, source: sourceKey });\n\n if (locked && !fallbackHandled) {\n await cleanPreviousCuratedSkills({ curatedDir, lockedSkillNames, logger });\n }\n\n for (const skillDir of filteredDirs) {\n if (\n shouldSkipSkill({\n skillName: skillDir.name,\n sourceKey,\n localSkillNames,\n alreadyFetchedSkillNames,\n logger,\n })\n ) {\n continue;\n }\n\n fetchedSkills[skillDir.name] = await fetchGithubSkillDir({\n skillDir,\n parsed,\n ref,\n resolvedSha,\n curatedDir,\n locked,\n sourceKey,\n client,\n semaphore,\n logger,\n });\n logger.debug(`Fetched skill \"${skillDir.name}\" from ${sourceKey}`);\n }\n\n const result = buildLockUpdate({\n lock,\n sourceKey,\n fetchedSkills,\n locked,\n requestedRef,\n resolvedSha,\n remoteSkillNames,\n logger,\n });\n\n return {\n skillCount: result.fetchedNames.length,\n fetchedSkillNames: result.fetchedNames,\n updatedLock: result.updatedLock,\n };\n}\n\n/**\n * Fetch skills from a single source using git CLI (works with any git remote).\n */\nasync function fetchSourceViaGit(params: {\n sourceEntry: SourceEntry;\n projectRoot: string;\n lock: SourcesLock;\n localSkillNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n updateSources: boolean;\n frozen: boolean;\n logger: Logger;\n}): Promise<{ skillCount: number; fetchedSkillNames: string[]; updatedLock: SourcesLock }> {\n const {\n sourceEntry,\n projectRoot,\n localSkillNames,\n alreadyFetchedSkillNames,\n updateSources,\n frozen,\n logger,\n } = params;\n const { lock } = params;\n const url = sourceEntry.source;\n const locked = getLockedSource(lock, url);\n const lockedSkillNames = locked ? getLockedSkillNames(locked) : [];\n\n let resolvedSha: string;\n let requestedRef: string | undefined;\n if (locked && !updateSources) {\n resolvedSha = locked.resolvedRef;\n requestedRef = locked.requestedRef;\n // Validate locked ref before passing to git commands\n if (requestedRef) {\n validateRef(requestedRef);\n }\n } else if (sourceEntry.ref) {\n requestedRef = sourceEntry.ref;\n resolvedSha = await resolveRefToSha(url, requestedRef);\n } else {\n const def = await resolveDefaultRef(url);\n requestedRef = def.ref;\n resolvedSha = def.sha;\n }\n\n const curatedDir = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n if (locked && resolvedSha === locked.resolvedRef && !updateSources) {\n if (await checkLockedSkillsExist(curatedDir, lockedSkillNames)) {\n return { skillCount: 0, fetchedSkillNames: lockedSkillNames, updatedLock: lock };\n }\n }\n\n // Resolve requestedRef lazily (deferred from locked path to avoid unnecessary network calls)\n if (!requestedRef) {\n if (frozen) {\n throw new Error(\n `Frozen install failed: lockfile entry for \"${url}\" is missing requestedRef. Run 'rulesync install' to update the lockfile.`,\n );\n }\n const def = await resolveDefaultRef(url);\n requestedRef = def.ref;\n resolvedSha = def.sha;\n }\n\n const skillFilter = sourceEntry.skills ?? [\"*\"];\n const isWildcard = skillFilter.length === 1 && skillFilter[0] === \"*\";\n const remoteFiles = await fetchSkillFiles({\n url,\n ref: requestedRef,\n resolvedRef: resolvedSha,\n skillsPath: sourceEntry.path ?? \"skills\",\n });\n\n const skillFileMap = groupRemoteFilesBySkillRoot({ remoteFiles, skillFilter, isWildcard });\n\n const allNames = [...skillFileMap.keys()];\n const filteredNames = isWildcard ? allNames : allNames.filter((n) => skillFilter.includes(n));\n assertMatchingSkillsFound({ skillNames: filteredNames, source: url });\n\n if (locked) {\n await cleanPreviousCuratedSkills({ curatedDir, lockedSkillNames, logger });\n }\n\n const fetchedSkills: Record<string, LockedSkill> = {};\n for (const skillName of filteredNames) {\n if (\n shouldSkipSkill({\n skillName,\n sourceKey: url,\n localSkillNames,\n alreadyFetchedSkillNames,\n logger,\n })\n ) {\n continue;\n }\n\n fetchedSkills[skillName] = await writeSkillAndComputeIntegrity({\n skillName,\n files: skillFileMap.get(skillName) ?? [],\n curatedDir,\n locked,\n resolvedSha,\n sourceKey: url,\n logger,\n });\n }\n\n const result = buildLockUpdate({\n lock,\n sourceKey: url,\n fetchedSkills,\n locked,\n requestedRef,\n resolvedSha,\n remoteSkillNames: filteredNames,\n logger,\n });\n return {\n skillCount: result.fetchedNames.length,\n fetchedSkillNames: result.fetchedNames,\n updatedLock: result.updatedLock,\n };\n}\n\n// ---------------------------------------------------------------------------\n// npm transport (EXPERIMENTAL)\n// ---------------------------------------------------------------------------\n\n/**\n * Select the skill files inside an extracted npm package, mirroring the git\n * transport's discovery: files under `skills/` (or the configured `path`) are\n * grouped per subdirectory; a package whose `SKILL.md` sits at the package\n * root is installed as a single skill (root fallback via\n * {@link shouldUseRootFallback}), named after the requested skill or, for\n * wildcard fetches, after the package's base name.\n */\nfunction selectNpmSkillFiles(params: {\n allFiles: RemoteSkillFile[];\n skillsPath: string;\n skillFilter: string[];\n isWildcard: boolean;\n packageName: string;\n}): { remoteFiles: RemoteSkillFile[]; skillFilter: string[]; isWildcard: boolean } {\n const { allFiles, skillsPath, skillFilter, isWildcard, packageName } = params;\n\n const normalizedBase = posix.normalize(skillsPath.replace(/\\\\/g, \"/\")).replace(/\\/+$/, \"\");\n const isRootPath = normalizedBase === \"\" || normalizedBase === \".\";\n if (isRootPath) {\n return { remoteFiles: allFiles, skillFilter, isWildcard };\n }\n\n const prefix = `${normalizedBase}/`;\n const filesUnderBase = allFiles\n .filter((file) => file.relativePath.startsWith(prefix))\n .map((file) => ({\n relativePath: file.relativePath.substring(prefix.length),\n content: file.content,\n }));\n if (filesUnderBase.length > 0) {\n return { remoteFiles: filesUnderBase, skillFilter, isWildcard };\n }\n\n // Root fallback: the package itself is a single skill with SKILL.md at its\n // root. For wildcard fetches the skill name is derived from the package\n // base name (scope stripped), so `@acme/my-skill` installs as `my-skill`.\n const hasRootSkillFile = allFiles.some((file) => file.relativePath === SKILL_FILE_NAME);\n const fallbackFilter = isWildcard ? [npmPackageBaseName(packageName)] : skillFilter;\n const [singleSkillName] = fallbackFilter;\n if (\n fallbackFilter.length === 1 &&\n singleSkillName !== undefined &&\n shouldUseRootFallback({\n skillFilter: fallbackFilter,\n isWildcard: false,\n hasRootSkillFile,\n hasRequestedSkillDir: false,\n })\n ) {\n return { remoteFiles: allFiles, skillFilter: fallbackFilter, isWildcard: false };\n }\n\n return { remoteFiles: filesUnderBase, skillFilter, isWildcard };\n}\n\n/** Base name of an npm package: `@scope/name` -> `name`. */\nfunction npmPackageBaseName(packageName: string): string {\n const slashIndex = packageName.indexOf(\"/\");\n return slashIndex === -1 ? packageName : packageName.substring(slashIndex + 1);\n}\n\n/**\n * Resolve the version to fetch for an npm source: the locked version when\n * available (deterministic re-fetch), otherwise the declared `ref` (exact\n * version or dist-tag, defaulting to \"latest\") resolved via the packument.\n */\nfunction resolveNpmFetchVersion(params: {\n sourceEntry: SourceEntry;\n locked: NpmLockedSource | undefined;\n updateSources: boolean;\n}): { lockedVersion: string | undefined; requestedVersion: string | undefined } {\n const { sourceEntry, locked, updateSources } = params;\n if (locked && !updateSources) {\n return { lockedVersion: locked.resolvedVersion, requestedVersion: locked.requestedVersion };\n }\n return { lockedVersion: undefined, requestedVersion: sourceEntry.ref ?? \"latest\" };\n}\n\n/**\n * Resolve the package version via the registry packument, download the\n * tarball, and verify it against the registry (and, when re-fetching a locked\n * version, the locked) integrity metadata.\n */\nasync function downloadVerifiedNpmTarball(params: {\n packageName: string;\n registryUrl: string;\n token: string | undefined;\n lockedVersion: string | undefined;\n requestedVersion: string | undefined;\n locked: NpmLockedSource | undefined;\n logger: Logger;\n}): Promise<{\n resolvedVersion: string;\n dist: { tarball: string; integrity?: string; shasum?: string };\n tarball: Buffer;\n}> {\n const { packageName, registryUrl, token, lockedVersion, requestedVersion, locked, logger } =\n params;\n\n const packument = await fetchPackument({ registryUrl, packageName, token });\n const resolvedVersion =\n lockedVersion ??\n resolvePackumentVersion({\n packument,\n packageName,\n requested: requestedVersion ?? \"latest\",\n });\n logger.debug(`Resolved ${packageName}@${requestedVersion ?? \"latest\"} to ${resolvedVersion}`);\n\n const dist = getPackumentVersionDist({ packument, packageName, version: resolvedVersion });\n const tarball = await fetchTarball({ tarballUrl: dist.tarball, registryUrl, token });\n const context = `${packageName}@${resolvedVersion}`;\n verifyTarballIntegrity({\n tarball,\n integrity: dist.integrity,\n shasum: dist.shasum,\n context,\n logger,\n });\n // Defense in depth: when re-fetching a locked version, also verify against\n // the integrity recorded at lock time so a registry-side swap is detected.\n if (locked?.integrity && locked.resolvedVersion === resolvedVersion) {\n verifyTarballIntegrity({ tarball, integrity: locked.integrity, context, logger });\n }\n\n return { resolvedVersion, dist, tarball };\n}\n\n/**\n * Extract a verified npm tarball in memory and convert its entries into\n * remote skill files, skipping any file above MAX_FILE_SIZE.\n */\nfunction extractNpmRemoteFiles(params: { tarball: Buffer; logger: Logger }): RemoteSkillFile[] {\n const { tarball, logger } = params;\n const extracted = extractPackageTarball({\n tarball,\n onSkippedEntry: (message) => logger.warn(message),\n });\n const allFiles: RemoteSkillFile[] = [];\n for (const entry of extracted) {\n if (entry.content.length > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping file \"${entry.relativePath}\" (${(entry.content.length / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit).`,\n );\n continue;\n }\n allFiles.push({ relativePath: entry.relativePath, content: entry.content.toString(\"utf8\") });\n }\n return allFiles;\n}\n\n/** Build the npm lockfile entry for a fetched source. */\nfunction buildNpmLockEntry(params: {\n sourceEntry: SourceEntry;\n requestedVersion: string | undefined;\n resolvedVersion: string;\n dist: { integrity?: string; shasum?: string };\n mergedSkills: Record<string, LockedSkill>;\n mergedRules: Record<string, LockedRule>;\n resolvedRuleNames: string[];\n}): NpmLockedSource {\n const {\n sourceEntry,\n requestedVersion,\n resolvedVersion,\n dist,\n mergedSkills,\n mergedRules,\n resolvedRuleNames,\n } = params;\n const integrity =\n dist.integrity ?? (dist.shasum !== undefined ? shasumToSri(dist.shasum) : undefined);\n return {\n ...(sourceEntry.registry !== undefined && { registry: sourceEntry.registry }),\n ...(requestedVersion !== undefined && { requestedVersion }),\n resolvedVersion,\n ...(integrity !== undefined && { integrity }),\n resolvedAt: new Date().toISOString(),\n skills: mergedSkills,\n ...(sourceEntry.rules !== undefined && {\n rules: mergedRules,\n ruleSelection: normalizeRuleSelection(sourceEntry.rules),\n rulesPath: normalizeRulesPath(sourceEntry.rulesPath),\n resolvedRuleNames,\n }),\n };\n}\n\nasync function fetchNpmSkills(params: {\n allFiles: RemoteSkillFile[];\n sourceEntry: SourceEntry;\n packageName: string;\n locked: NpmLockedSource | undefined;\n lockedForIntegrityCheck: LockedSource | undefined;\n lockedSkillNames: string[];\n curatedSkillsDir: string;\n localSkillNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n resolvedVersion: string;\n logger: Logger;\n}): Promise<{\n fetchedSkills: Record<string, LockedSkill>;\n remoteSkillNames: string[];\n}> {\n if (params.sourceEntry.skills === undefined) {\n return { fetchedSkills: {}, remoteSkillNames: [] };\n }\n const {\n allFiles,\n sourceEntry,\n packageName,\n locked,\n lockedForIntegrityCheck,\n lockedSkillNames,\n curatedSkillsDir,\n localSkillNames,\n alreadyFetchedSkillNames,\n resolvedVersion,\n logger,\n } = params;\n const skillFilter = sourceEntry.skills ?? [];\n const declaredWildcard = skillFilter.length === 1 && skillFilter[0] === \"*\";\n const selectedFiles = selectNpmSkillFiles({\n allFiles,\n skillsPath: sourceEntry.path ?? \"skills\",\n skillFilter,\n isWildcard: declaredWildcard,\n packageName,\n });\n const skillFileMap = groupRemoteFilesBySkillRoot(selectedFiles);\n const allNames = [...skillFileMap.keys()];\n const remoteSkillNames = selectedFiles.isWildcard\n ? allNames\n : allNames.filter((name) => selectedFiles.skillFilter.includes(name));\n assertMatchingSkillsFound({ skillNames: remoteSkillNames, source: packageName });\n if (locked) {\n await cleanPreviousCuratedSkills({ curatedDir: curatedSkillsDir, lockedSkillNames, logger });\n }\n const fetchedSkills: Record<string, LockedSkill> = {};\n for (const skillName of remoteSkillNames) {\n if (\n shouldSkipSkill({\n skillName,\n sourceKey: packageName,\n localSkillNames,\n alreadyFetchedSkillNames,\n logger,\n })\n ) {\n continue;\n }\n fetchedSkills[skillName] = await writeSkillAndComputeIntegrity({\n skillName,\n files: skillFileMap.get(skillName) ?? [],\n curatedDir: curatedSkillsDir,\n locked: lockedForIntegrityCheck,\n resolvedSha: resolvedVersion,\n sourceKey: packageName,\n logger,\n });\n logger.debug(`Fetched skill \"${skillName}\" from ${packageName}`);\n }\n return { fetchedSkills, remoteSkillNames };\n}\n\nasync function fetchNpmRules(params: {\n allFiles: RemoteSkillFile[];\n sourceEntry: SourceEntry;\n packageName: string;\n lockedForIntegrityCheck: LockedSource | undefined;\n lockedRuleNames: string[];\n curatedRulesDir: string;\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n resolvedVersion: string;\n updateSources: boolean;\n logger: Logger;\n}): Promise<{ fetchedRules: Record<string, LockedRule>; resolvedRuleNames: string[] }> {\n if (params.sourceEntry.rules === undefined) {\n return { fetchedRules: {}, resolvedRuleNames: [] };\n }\n const {\n allFiles,\n sourceEntry,\n packageName,\n lockedForIntegrityCheck,\n lockedRuleNames,\n curatedRulesDir,\n localRuleNames,\n alreadyFetchedRuleNames,\n resolvedVersion,\n updateSources,\n logger,\n } = params;\n const normalizedRulesPath = normalizeRulesPath(sourceEntry.rulesPath);\n const rulePrefix = normalizedRulesPath === \".\" ? \"\" : `${normalizedRulesPath}/`;\n const ruleFilter = (sourceEntry.rules ?? []).map(normalizeRuleFilterName);\n const isWildcard = ruleFilter.length === 1 && ruleFilter[0] === \"*\";\n const remoteRules = allFiles\n .filter((file) => file.relativePath.startsWith(rulePrefix))\n .map((file) => ({\n relativePath: file.relativePath.substring(rulePrefix.length),\n content: file.content,\n }))\n .filter(\n (file) =>\n getFirstPathSeparatorIndex(file.relativePath) === -1 &&\n file.relativePath.toLowerCase().endsWith(\".md\"),\n )\n .map((file) => ({ name: normalizeRuleFilterName(file.relativePath), content: file.content }))\n .filter((rule) => (isWildcard || ruleFilter.includes(rule.name)) && isValidRuleName(rule.name));\n const resolvedRuleNames = remoteRules.map((rule) => rule.name);\n assertMatchingRulesFound({ ruleNames: resolvedRuleNames, source: packageName });\n const fetchedRules = await replaceCuratedRules({\n rules: remoteRules,\n curatedDir: curatedRulesDir,\n locked: lockedForIntegrityCheck,\n lockedRuleNames,\n resolvedRef: resolvedVersion,\n sourceKey: packageName,\n localRuleNames,\n alreadyFetchedRuleNames,\n compareLockedIntegrity: !updateSources,\n logger,\n });\n return { fetchedRules, resolvedRuleNames };\n}\n\nasync function canReuseLockedNpmArtifacts(params: {\n locked: NpmLockedSource | undefined;\n sourceEntry: SourceEntry;\n filters: ReturnType<typeof getSourceFilters>;\n lockedSkillNames: string[];\n lockedRuleNames: string[];\n curatedSkillsDir: string;\n curatedRulesDir: string;\n localRuleNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n}): Promise<boolean> {\n const {\n locked,\n sourceEntry,\n filters,\n lockedSkillNames,\n lockedRuleNames,\n curatedSkillsDir,\n curatedRulesDir,\n localRuleNames,\n alreadyFetchedRuleNames,\n } = params;\n if (locked === undefined) {\n return false;\n }\n const skillsExist =\n filters.skills === undefined ||\n (lockedSkillNames.length > 0 &&\n (await checkLockedSkillsExist(curatedSkillsDir, lockedSkillNames)));\n if (!skillsExist) {\n return false;\n }\n if (filters.rules === undefined) {\n return locked.rules === undefined;\n }\n return canReuseLockedRules({\n locked,\n sourceEntry: { ...sourceEntry, rules: filters.rules },\n lockedRuleNames,\n localRuleNames,\n alreadyFetchedRuleNames,\n curatedDir: curatedRulesDir,\n });\n}\n\n/**\n * Fetch rules and skills from a single npm-transport source (EXPERIMENTAL): resolve the\n * package version via the registry packument, download and verify the\n * tarball, extract it in-memory with the hardened tar reader, and install the\n * discovered skills into the curated directory.\n */\nasync function fetchSourceViaNpm(params: {\n sourceEntry: SourceEntry;\n projectRoot: string;\n npmLock: NpmSourcesLock;\n localSkillNames: Set<string>;\n localRuleNames: Set<string>;\n alreadyFetchedSkillNames: Set<string>;\n alreadyFetchedRuleNames: Set<string>;\n updateSources: boolean;\n logger: Logger;\n}): Promise<{\n skillCount: number;\n ruleCount: number;\n fetchedSkillNames: string[];\n fetchedRuleNames: string[];\n updatedLock: NpmSourcesLock;\n}> {\n const {\n sourceEntry,\n projectRoot,\n npmLock,\n localSkillNames,\n localRuleNames,\n alreadyFetchedSkillNames,\n alreadyFetchedRuleNames,\n updateSources,\n logger,\n } = params;\n\n const packageName = sourceEntry.source;\n validateNpmPackageName(packageName);\n const registryUrl = sourceEntry.registry ?? DEFAULT_NPM_REGISTRY_URL;\n validateNpmRegistryUrl(registryUrl, { logger });\n const token = resolveNpmToken({ tokenEnv: sourceEntry.tokenEnv });\n\n const sourceKey = packageName;\n const locked = getNpmLockedSource(npmLock, sourceKey);\n const lockedSkillNames = locked ? getNpmLockedSkillNames(locked) : [];\n const lockedRuleNames = locked ? getNpmLockedRuleNames(locked) : [];\n const curatedSkillsDir = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n const curatedRulesDir = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n const filters = getSourceFilters(sourceEntry);\n\n const { lockedVersion, requestedVersion } = resolveNpmFetchVersion({\n sourceEntry,\n locked,\n updateSources,\n });\n\n // Skip re-fetch if the locked version's requested curated artifacts exist on disk.\n if (\n lockedVersion !== undefined &&\n (await canReuseLockedNpmArtifacts({\n locked,\n sourceEntry,\n filters,\n lockedSkillNames,\n lockedRuleNames,\n curatedSkillsDir,\n curatedRulesDir,\n localRuleNames,\n alreadyFetchedRuleNames,\n }))\n ) {\n logger.debug(`Version unchanged for ${sourceKey}, skipping re-fetch.`);\n return {\n skillCount: 0,\n ruleCount: 0,\n fetchedSkillNames: filters.skills === undefined ? [] : lockedSkillNames,\n fetchedRuleNames: filters.rules === undefined ? [] : lockedRuleNames,\n updatedLock: npmLock,\n };\n }\n\n const { resolvedVersion, dist, tarball } = await downloadVerifiedNpmTarball({\n packageName,\n registryUrl,\n token,\n lockedVersion,\n requestedVersion,\n locked,\n logger,\n });\n\n const allFiles = extractNpmRemoteFiles({ tarball, logger });\n\n // Adapter so writeSkillAndComputeIntegrity can compare per-skill integrity\n // against the npm lock entry the same way it does for git sources.\n const lockedForIntegrityCheck: LockedSource | undefined = locked\n ? { resolvedRef: locked.resolvedVersion, skills: locked.skills, rules: locked.rules }\n : undefined;\n\n const { fetchedSkills, remoteSkillNames } = await fetchNpmSkills({\n allFiles,\n sourceEntry: { ...sourceEntry, skills: filters.skills },\n packageName,\n locked,\n lockedForIntegrityCheck,\n lockedSkillNames,\n curatedSkillsDir,\n localSkillNames,\n alreadyFetchedSkillNames,\n resolvedVersion,\n logger,\n });\n\n const { fetchedRules, resolvedRuleNames } = await fetchNpmRules({\n allFiles,\n sourceEntry: { ...sourceEntry, rules: filters.rules },\n packageName,\n lockedForIntegrityCheck,\n lockedRuleNames,\n curatedRulesDir,\n localRuleNames,\n alreadyFetchedRuleNames,\n resolvedVersion,\n updateSources,\n logger,\n });\n\n if (filters.rules === undefined && locked?.rules !== undefined) {\n await cleanPreviousCuratedRules({\n curatedDir: curatedRulesDir,\n lockedRuleNames,\n protectedRuleNames: alreadyFetchedRuleNames,\n logger,\n });\n }\n\n const fetchedSkillNames = Object.keys(fetchedSkills);\n const fetchedRuleNames = Object.keys(fetchedRules);\n const mergedSkills = mergeFetchedWithLockedSkills({\n fetchedSkills,\n lockedSkills: locked?.skills,\n remoteSkillNames:\n filters.skills === undefined ? Object.keys(locked?.skills ?? {}) : remoteSkillNames,\n });\n const mergedRules = filters.rules === undefined ? {} : fetchedRules;\n\n const updatedLock = setNpmLockedSource(\n npmLock,\n sourceKey,\n buildNpmLockEntry({\n sourceEntry,\n requestedVersion,\n resolvedVersion,\n dist,\n mergedSkills,\n mergedRules,\n resolvedRuleNames,\n }),\n );\n\n logger.info(\n `Fetched ${fetchedSkillNames.length} skill(s) and ${fetchedRuleNames.length} rule(s) from ${sourceKey}.`,\n );\n\n return {\n skillCount: fetchedSkillNames.length,\n ruleCount: fetchedRuleNames.length,\n fetchedSkillNames,\n fetchedRuleNames,\n updatedLock,\n };\n}\n","import { cp, mkdtemp, realpath, rm } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, relative, sep } from \"node:path\";\nimport { createInterface } from \"node:readline/promises\";\n\nimport {\n applyEdits,\n modify,\n parse as parseJsonc,\n type FormattingOptions,\n type ParseError,\n printParseErrorCode,\n} from \"jsonc-parser\";\n\nimport { ConfigResolver } from \"../../config/config-resolver.js\";\nimport { ConfigFileSchema, type SourceEntry, SourceEntrySchema } from \"../../config/config.js\";\nimport {\n RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH,\n RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH,\n RULESYNC_CONFIG_RELATIVE_FILE_PATH,\n RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH,\n RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH,\n RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH,\n} from \"../../constants/rulesync-paths.js\";\nimport { createFeatureScaffold, parseScaffoldFeatureKeyword } from \"../../lib/feature-scaffold.js\";\nimport { normalizeNpmSourceKey } from \"../../lib/npm-sources-lock.js\";\nimport { normalizeSourceKey } from \"../../lib/sources-lock.js\";\nimport {\n getInstalledSourceRuleNames,\n getInstalledSourceSkillNames,\n resolveAndFetchSources,\n} from \"../../lib/sources.js\";\nimport {\n assertDirectoryIfExists,\n assertTreeContainsNoSymlinks,\n assertWritablePathInsideRoot,\n directoryExists,\n ensureDir,\n fileExists,\n readFileContent,\n readFileContentOrNull,\n resolvePath,\n writeFileContent,\n} from \"../../utils/file.js\";\nimport type { Logger } from \"../../utils/logger.js\";\n\nexport type AddCommandOptions = {\n source: string;\n skills?: string[];\n rules?: string[];\n transport?: SourceEntry[\"transport\"];\n ref?: string;\n path?: string;\n rulesPath?: string;\n registry?: string;\n tokenEnv?: string;\n token?: string;\n configPath?: string;\n name?: string;\n force?: boolean;\n verbose?: boolean;\n silent?: boolean;\n confirmOverwrite?: (relativeFilePath: string) => Promise<boolean>;\n};\n\nconst SOURCE_ENTRY_KEYS = [\n \"source\",\n \"skills\",\n \"rules\",\n \"transport\",\n \"ref\",\n \"path\",\n \"rulesPath\",\n \"registry\",\n \"tokenEnv\",\n \"agent\",\n \"scope\",\n] as const satisfies ReadonlyArray<keyof SourceEntry>;\n\ntype InstallSnapshot = {\n backupRoot: string;\n curatedSkillsExisted: boolean;\n curatedRulesExisted: boolean;\n sourcesLockContent: string | null;\n npmSourcesLockContent: string | null;\n};\n\nfunction pathEscapesRoot(relativePath: string): boolean {\n return relativePath === \"..\" || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath);\n}\n\nfunction assertSourceHasNoEmbeddedCredentials(source: string): void {\n if (!/^[a-z][a-z\\d+.-]*:\\/\\//i.test(source)) {\n return;\n }\n const url = new URL(source);\n if (url.username !== \"\" || url.password !== \"\") {\n throw new Error(\n \"Source URLs must not contain credentials. Use an environment variable, credential helper, or SSH authentication instead.\",\n );\n }\n}\n\nasync function createInstallSnapshot({\n projectRoot,\n manifestContent,\n}: {\n projectRoot: string;\n manifestContent: string;\n}): Promise<InstallSnapshot> {\n const backupRoot = await mkdtemp(join(projectRoot, \".rulesync-add-backup-\"));\n const curatedSkillsPath = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n const curatedRulesPath = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n const curatedSkillsExisted = await directoryExists(curatedSkillsPath);\n const curatedRulesExisted = await directoryExists(curatedRulesPath);\n if (curatedSkillsExisted) {\n await cp(curatedSkillsPath, join(backupRoot, \"curated-skills\"), { recursive: true });\n }\n if (curatedRulesExisted) {\n await cp(curatedRulesPath, join(backupRoot, \"curated-rules\"), { recursive: true });\n }\n const sourcesLockContent = await readFileContentOrNull(\n join(projectRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH),\n );\n const npmSourcesLockContent = await readFileContentOrNull(\n join(projectRoot, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH),\n );\n await writeFileContent(join(backupRoot, \"manifest.jsonc\"), manifestContent);\n if (sourcesLockContent !== null) {\n await writeFileContent(\n join(backupRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH),\n sourcesLockContent,\n );\n }\n if (npmSourcesLockContent !== null) {\n await writeFileContent(\n join(backupRoot, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH),\n npmSourcesLockContent,\n );\n }\n return {\n backupRoot,\n curatedSkillsExisted,\n curatedRulesExisted,\n sourcesLockContent,\n npmSourcesLockContent,\n };\n}\n\nasync function restoreFile({ path, content }: { path: string; content: string | null }) {\n if (content === null) {\n await rm(path, { force: true });\n return;\n }\n await writeFileContent(path, content);\n}\n\nasync function restoreInstallSnapshot({\n projectRoot,\n snapshot,\n}: {\n projectRoot: string;\n snapshot: InstallSnapshot;\n}): Promise<void> {\n const curatedSkillsPath = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n const curatedRulesPath = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n await Promise.all([\n rm(curatedSkillsPath, { recursive: true, force: true }),\n rm(curatedRulesPath, { recursive: true, force: true }),\n ]);\n if (snapshot.curatedSkillsExisted) {\n await cp(join(snapshot.backupRoot, \"curated-skills\"), curatedSkillsPath, { recursive: true });\n }\n if (snapshot.curatedRulesExisted) {\n await cp(join(snapshot.backupRoot, \"curated-rules\"), curatedRulesPath, { recursive: true });\n }\n await restoreFile({\n path: join(projectRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH),\n content: snapshot.sourcesLockContent,\n });\n await restoreFile({\n path: join(projectRoot, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH),\n content: snapshot.npmSourcesLockContent,\n });\n}\n\nasync function rollbackAdd({\n configPath,\n originalContent,\n projectRoot,\n snapshot,\n}: {\n configPath: string;\n originalContent: string;\n projectRoot: string;\n snapshot: InstallSnapshot;\n}): Promise<void> {\n await Promise.all([\n writeFileContent(configPath, originalContent),\n restoreInstallSnapshot({ projectRoot, snapshot }),\n ]);\n}\n\nfunction sourceIdentity(entry: SourceEntry): string {\n const transport = entry.transport ?? \"github\";\n const normalizedSource =\n transport === \"npm\" ? normalizeNpmSourceKey(entry.source) : normalizeSourceKey(entry.source);\n const lockfileKind = transport === \"npm\" ? \"npm\" : \"git\";\n return `${lockfileKind}:${normalizedSource}`;\n}\n\nfunction sourceEntriesEqual(left: SourceEntry, right: SourceEntry): boolean {\n return SOURCE_ENTRY_KEYS.every((key) => {\n const leftValue = left[key];\n const rightValue = right[key];\n if (Array.isArray(leftValue) && Array.isArray(rightValue)) {\n return (\n leftValue.length === rightValue.length &&\n leftValue.every((value, index) => value === rightValue[index])\n );\n }\n return leftValue === rightValue;\n });\n}\n\nfunction detectFormattingOptions(content: string): FormattingOptions {\n const eol = content.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const indentedLine = content.match(/^(\\s+)[\"}]/m)?.[1] ?? \" \";\n const insertSpaces = !indentedLine.includes(\"\\t\");\n return {\n eol,\n insertSpaces,\n tabSize: insertSpaces ? indentedLine.length : 1,\n };\n}\n\nfunction buildSourceEntry(options: AddCommandOptions): SourceEntry {\n assertSourceHasNoEmbeddedCredentials(options.source);\n return SourceEntrySchema.parse({\n source: options.source,\n skills: options.skills,\n rules: options.rules,\n transport: options.transport,\n ref: options.ref,\n path: options.path,\n rulesPath: options.rulesPath,\n registry: options.registry,\n tokenEnv: options.tokenEnv,\n });\n}\n\nfunction hasSourceOnlyOptions(options: AddCommandOptions): boolean {\n return [\n options.skills,\n options.rules,\n options.transport,\n options.ref,\n options.path,\n options.rulesPath,\n options.registry,\n options.tokenEnv,\n options.token,\n options.configPath,\n ].some((value) => value !== undefined);\n}\n\nasync function promptForOverwrite(relativeFilePath: string): Promise<boolean> {\n if (!process.stdin.isTTY || !process.stdout.isTTY) {\n throw new Error(\n `Refusing to overwrite ${relativeFilePath} in non-interactive mode. Re-run with --force to replace it.`,\n );\n }\n\n const prompt = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const answer = await prompt.question(`Overwrite ${relativeFilePath}? [y/N] `);\n return /^(?:y|yes)$/i.test(answer.trim());\n } finally {\n prompt.close();\n }\n}\n\nasync function addFeatureScaffold({\n logger,\n options,\n}: {\n logger: Logger;\n options: AddCommandOptions;\n}): Promise<void> {\n const feature = parseScaffoldFeatureKeyword(options.source);\n if (!feature) {\n throw new Error(\"--name and --force are only valid when adding a Rulesync feature file.\");\n }\n\n const scaffold = createFeatureScaffold({ feature, name: options.name });\n const projectRoot = process.cwd();\n let relativeFilePath = scaffold.relativeFilePath;\n for (const candidateRelativeFilePath of scaffold.candidateRelativeFilePaths) {\n if (await fileExists(join(projectRoot, candidateRelativeFilePath))) {\n relativeFilePath = candidateRelativeFilePath;\n break;\n }\n }\n const targetPath = join(projectRoot, relativeFilePath);\n await assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath });\n\n if ((await fileExists(targetPath)) && !options.force) {\n if (logger.jsonMode || logger.silent) {\n throw new Error(\n `Refusing to prompt before overwriting ${relativeFilePath} in JSON or silent mode. Re-run with --force to replace it.`,\n );\n }\n const confirmed = await (options.confirmOverwrite ?? promptForOverwrite)(relativeFilePath);\n if (!confirmed) {\n logger.info(`Kept ${relativeFilePath} unchanged.`);\n if (logger.jsonMode) {\n logger.captureData(\"created\", []);\n logger.captureData(\"skipped\", [relativeFilePath]);\n }\n return;\n }\n }\n\n await assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath });\n await ensureDir(dirname(targetPath));\n await assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath });\n await writeFileContent(targetPath, scaffold.content);\n logger.success(`Created ${relativeFilePath}`);\n if (logger.jsonMode) {\n logger.captureData(\"created\", [relativeFilePath]);\n logger.captureData(\"skipped\", []);\n }\n}\n\nasync function handleFeatureScaffoldRequest({\n logger,\n options,\n}: {\n logger: Logger;\n options: AddCommandOptions;\n}): Promise<boolean> {\n const feature = parseScaffoldFeatureKeyword(options.source);\n const sourceOnlyOptions = hasSourceOnlyOptions(options);\n const scaffoldOnlyOptions = options.name !== undefined || options.force === true;\n\n if (feature && scaffoldOnlyOptions && sourceOnlyOptions) {\n throw new Error(\n \"Feature scaffold options (--name, --force) cannot be combined with declarative source options.\",\n );\n }\n if ((feature && !sourceOnlyOptions) || scaffoldOnlyOptions) {\n await addFeatureScaffold({ logger, options });\n return true;\n }\n return false;\n}\n\nfunction parseConfigContent(content: string, configPath: string) {\n const errors: ParseError[] = [];\n const parsed = parseJsonc(content, errors, { allowTrailingComma: true });\n const firstError = errors[0];\n if (firstError) {\n throw new Error(\n `Failed to parse ${configPath}: ${printParseErrorCode(firstError.error)} at offset ${firstError.offset}.`,\n );\n }\n return ConfigFileSchema.parse(parsed);\n}\n\nexport async function addCommand(logger: Logger, options: AddCommandOptions): Promise<void> {\n if (await handleFeatureScaffoldRequest({ logger, options })) {\n return;\n }\n\n const projectRoot = process.cwd();\n const relativeConfigPath = options.configPath ?? RULESYNC_CONFIG_RELATIVE_FILE_PATH;\n const configPath = resolvePath(relativeConfigPath, projectRoot);\n\n if (!(await fileExists(configPath))) {\n throw new Error(\n `Configuration file not found: ${relativeConfigPath}. Run 'rulesync init' first or pass --config.`,\n );\n }\n\n const realProjectRoot = await realpath(projectRoot);\n const realConfigPath = await realpath(configPath);\n const relativeRealConfigPath = relative(realProjectRoot, realConfigPath);\n if (pathEscapesRoot(relativeRealConfigPath)) {\n throw new Error(\n `Configuration file must resolve inside the project root: ${relativeConfigPath}.`,\n );\n }\n\n const sourceEntry = buildSourceEntry(options);\n const curatedSkillsPath = join(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);\n const curatedRulesPath = join(projectRoot, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH);\n await Promise.all([\n assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath: curatedSkillsPath }),\n assertWritablePathInsideRoot({ rootPath: projectRoot, targetPath: curatedRulesPath }),\n assertWritablePathInsideRoot({\n rootPath: projectRoot,\n targetPath: join(projectRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH),\n }),\n assertWritablePathInsideRoot({\n rootPath: projectRoot,\n targetPath: join(projectRoot, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH),\n }),\n ]);\n await Promise.all([\n assertDirectoryIfExists(curatedSkillsPath),\n assertDirectoryIfExists(curatedRulesPath),\n ]);\n if (await directoryExists(curatedSkillsPath)) {\n await assertTreeContainsNoSymlinks(curatedSkillsPath);\n }\n if (await directoryExists(curatedRulesPath)) {\n await assertTreeContainsNoSymlinks(curatedRulesPath);\n }\n const originalContent = await readFileContent(configPath);\n const parsedConfig = parseConfigContent(originalContent, relativeConfigPath);\n const existingSources = parsedConfig.sources ?? [];\n const identity = sourceIdentity(sourceEntry);\n\n if (existingSources.some((entry) => sourceIdentity(entry) === identity)) {\n throw new Error(\n `Source \"${sourceEntry.source}\" is already declared in ${relativeConfigPath}. Edit the existing entry to change its options.`,\n );\n }\n\n const configBeforeEdit = await ConfigResolver.resolve(\n {\n configPath,\n verbose: options.verbose,\n silent: options.silent,\n },\n { logger },\n );\n if (configBeforeEdit.getSources().some((entry) => sourceIdentity(entry) === identity)) {\n throw new Error(\n `Source \"${sourceEntry.source}\" is already declared in the effective configuration. Edit the existing entry to change its options.`,\n );\n }\n const reservedSkillNames = await getInstalledSourceSkillNames({\n sources: configBeforeEdit.getSources(),\n projectRoot,\n logger,\n });\n const reservedRuleNames = await getInstalledSourceRuleNames({\n sources: configBeforeEdit.getSources(),\n projectRoot,\n logger,\n });\n\n const editPath =\n parsedConfig.sources === undefined ? [\"sources\"] : [\"sources\", existingSources.length];\n const editValue = parsedConfig.sources === undefined ? [sourceEntry] : sourceEntry;\n const formattingOptions = detectFormattingOptions(originalContent);\n const edits = modify(originalContent, editPath, editValue, { formattingOptions });\n let updatedContent = applyEdits(originalContent, edits);\n if (!updatedContent.endsWith(\"\\n\")) {\n updatedContent += formattingOptions.eol;\n }\n\n // Validate the complete edited document before replacing the user's file.\n parseConfigContent(updatedContent, relativeConfigPath);\n const snapshot = await createInstallSnapshot({ projectRoot, manifestContent: originalContent });\n let cleanupSnapshot = true;\n try {\n await writeFileContent(configPath, updatedContent);\n const config = await ConfigResolver.resolve(\n {\n configPath,\n verbose: options.verbose,\n silent: options.silent,\n },\n { logger },\n );\n const sources = config.getSources();\n if (!sources.some((entry) => sourceEntriesEqual(entry, sourceEntry))) {\n throw new Error(\n `${join(dirname(configPath), RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH)} overrides sources from ${relativeConfigPath}. Add the source to the overriding config or remove its sources key.`,\n );\n }\n\n const result = await resolveAndFetchSources({\n sources: [sourceEntry],\n projectRoot,\n options: {\n token: options.token,\n updateSources: true,\n preserveUnlistedLockEntries: true,\n requireResolvedSkills: sourceEntry.skills !== undefined || sourceEntry.rules === undefined,\n requireResolvedRules: sourceEntry.rules !== undefined,\n reservedSkillNames,\n reservedRuleNames,\n },\n logger,\n });\n\n if (logger.jsonMode) {\n logger.captureData(\"source\", sourceEntry.source);\n logger.captureData(\"configPath\", relativeConfigPath);\n logger.captureData(\"sourcesProcessed\", result.sourcesProcessed);\n logger.captureData(\"skillsFetched\", result.fetchedSkillCount);\n logger.captureData(\"rulesFetched\", result.fetchedRuleCount);\n logger.captureData(\"failedSourceCount\", result.failedSourceCount);\n }\n\n if (result.failedSourceCount > 0) {\n throw new Error(\n `Failed to install ${result.failedSourceCount} of ${result.sourcesProcessed} source(s); restored ${relativeConfigPath}. See the log above for details.`,\n );\n }\n\n logger.success(\n `Added \"${sourceEntry.source}\" to ${relativeConfigPath} and installed ${result.fetchedSkillCount} skill(s) and ${result.fetchedRuleCount} rule(s).`,\n );\n } catch (error) {\n try {\n await rollbackAdd({ configPath, originalContent, projectRoot, snapshot });\n } catch (rollbackError) {\n cleanupSnapshot = false;\n // oxlint-disable-next-line preserve-caught-error -- AggregateError retains both the operation and rollback failures.\n throw new AggregateError(\n [error, rollbackError],\n `Failed to roll back the add operation. Recovery snapshot retained at ${snapshot.backupRoot}.`,\n { cause: error },\n );\n }\n throw error;\n } finally {\n if (cleanupSnapshot) {\n await rm(snapshot.backupRoot, { recursive: true, force: true });\n }\n }\n}\n","/**\n * Result of writing AI files, including both count and file paths\n */\nexport type WriteResult = {\n count: number;\n paths: string[];\n};\n\n/**\n * Result of feature generation, extending WriteResult with hasDiff\n */\nexport type FeatureGenerateResult = WriteResult & { hasDiff: boolean };\n\n/**\n * Common count fields shared by ImportResult and GenerateResult\n */\nexport type CountableResult = {\n rulesCount: number;\n ignoreCount: number;\n mcpCount: number;\n commandsCount: number;\n subagentsCount: number;\n skillsCount: number;\n hooksCount: number;\n permissionsCount: number;\n checksCount: number;\n activationCount?: number;\n};\n\n/**\n * Calculate the total count from a result object\n */\nexport function calculateTotalCount(result: CountableResult): number {\n return (\n result.rulesCount +\n result.ignoreCount +\n result.mcpCount +\n result.commandsCount +\n result.subagentsCount +\n result.skillsCount +\n result.hooksCount +\n result.permissionsCount +\n result.checksCount +\n (result.activationCount ?? 0)\n );\n}\n","import { ConfigResolver, ConfigResolverResolveParams } from \"../../config/config-resolver.js\";\nimport { convertFromTool } from \"../../lib/convert.js\";\nimport type { RulesyncFeatures } from \"../../types/features.js\";\nimport { CLIError, ErrorCodes } from \"../../types/json-output.js\";\nimport { ALL_TOOL_TARGETS, type ToolTarget, ToolTargetSchema } from \"../../types/tool-targets.js\";\nimport type { Logger } from \"../../utils/logger.js\";\nimport { isPackagingToolTarget } from \"../../utils/plugin-root.js\";\nimport { calculateTotalCount } from \"../../utils/result.js\";\n\nexport type ConvertOptions = Omit<\n ConfigResolverResolveParams,\n \"delete\" | \"outputRoots\" | \"targets\"\n> & {\n from?: string;\n to?: string[];\n features?: RulesyncFeatures;\n};\n\nfunction parseToolTarget(value: string, label: string): ToolTarget {\n const result = ToolTargetSchema.safeParse(value);\n if (!result.success) {\n throw new CLIError(\n `Invalid ${label} tool '${value}'. Must be one of: ${ALL_TOOL_TARGETS.join(\", \")}`,\n ErrorCodes.CONVERT_FAILED,\n );\n }\n return result.data;\n}\n\nexport async function convertCommand(logger: Logger, options: ConvertOptions): Promise<void> {\n // `--from` and `--to` presence is enforced by commander's `requiredOption`\n // in `src/cli/index.ts`; here we only need to validate the tool names.\n const fromTool = parseToolTarget(options.from ?? \"\", \"source\");\n const toToolsRaw = (options.to ?? []).map((t) => parseToolTarget(t, \"destination\"));\n const toTools = Array.from(new Set(toToolsRaw));\n\n const packagingTarget = [fromTool, ...toTools].find(isPackagingToolTarget);\n if (packagingTarget) {\n throw new CLIError(\n `Plugin packaging target '${packagingTarget}' is not supported by convert. ` +\n \"Use import --output-root and generate --output-roots with an explicit plugin directory.\",\n ErrorCodes.CONVERT_FAILED,\n );\n }\n\n if (toTools.includes(fromTool)) {\n throw new CLIError(\n `Destination tools must not include the source tool '${fromTool}'. ` +\n `Converting a tool onto itself is likely a mistake and may cause lossy round-trips.`,\n ErrorCodes.CONVERT_FAILED,\n );\n }\n\n // Pass both source and destinations as `targets` so per-target feature maps\n // in `rulesync.jsonc` are honored for every tool involved. Default features\n // to `*` so every feature that both tools support is attempted.\n const config = await ConfigResolver.resolve(\n {\n ...options,\n targets: [fromTool, ...toTools],\n features: options.features ?? [\"*\"],\n },\n { logger },\n );\n\n const isPreview = config.isPreviewMode();\n const modePrefix = isPreview ? \"[DRY RUN] \" : \"\";\n\n logger.debug(`Converting files from ${fromTool} to ${toTools.join(\", \")}...`);\n\n const result = await convertFromTool({ config, fromTool, toTools, logger });\n\n const totalConverted = calculateTotalCount(result);\n\n if (totalConverted === 0) {\n const enabledFeatures = config.getFeatures(fromTool).join(\", \");\n logger.warn(`No files converted for enabled features: ${enabledFeatures}`);\n return;\n }\n\n if (logger.jsonMode) {\n logger.captureData(\"from\", fromTool);\n logger.captureData(\"to\", toTools);\n logger.captureData(\"dryRun\", isPreview);\n logger.captureData(\"features\", {\n rules: { count: result.rulesCount },\n ignore: { count: result.ignoreCount },\n mcp: { count: result.mcpCount },\n commands: { count: result.commandsCount },\n subagents: { count: result.subagentsCount },\n skills: { count: result.skillsCount },\n hooks: { count: result.hooksCount },\n permissions: { count: result.permissionsCount },\n checks: { count: result.checksCount },\n });\n logger.captureData(\"totalFiles\", totalConverted);\n }\n\n const parts: string[] = [];\n if (result.rulesCount > 0) parts.push(`${result.rulesCount} rules`);\n if (result.ignoreCount > 0) parts.push(`${result.ignoreCount} ignore files`);\n if (result.mcpCount > 0) parts.push(`${result.mcpCount} MCP files`);\n if (result.commandsCount > 0) parts.push(`${result.commandsCount} commands`);\n if (result.subagentsCount > 0) parts.push(`${result.subagentsCount} subagents`);\n if (result.skillsCount > 0) parts.push(`${result.skillsCount} skills`);\n if (result.hooksCount > 0) parts.push(`${result.hooksCount} hooks`);\n if (result.permissionsCount > 0) parts.push(`${result.permissionsCount} permissions`);\n if (result.checksCount > 0) parts.push(`${result.checksCount} checks`);\n\n const verbPhrase = isPreview ? \"Would convert\" : \"Converted\";\n const summary = `${modePrefix}${verbPhrase} ${totalConverted} file(s) total from ${fromTool} to ${toTools.join(\", \")} (${parts.join(\" + \")})`;\n\n if (isPreview) {\n logger.info(summary);\n } else {\n logger.success(summary);\n }\n}\n","// Auto-generated by scripts/generate-docs-content.ts. Do not edit manually.\n// Regenerate with `pnpm run generate:docs-content`.\n\n/**\n * The bundled documentation tree, keyed by document identifier (the path\n * under `docs/` without the `.md` extension, e.g. `guide/configuration`).\n */\nexport const DOCS_CONTENT: Record<string, string> = {\n \"api/programmatic-api\":\n '# Programmatic API\\n\\nRulesync can be used as a library in your Node.js/TypeScript projects. The `generate`, `importFromTool`, and `convertFromTool` functions are available as named exports.\\n\\n```typescript\\nimport { convertFromTool, generate, importFromTool } from \"rulesync\";\\n\\n// Generate configurations\\nconst result = await generate({\\n targets: [\"claudecode\", \"cursor\"],\\n features: [\"rules\", \"mcp\"],\\n});\\nconsole.log(`Generated ${result.rulesCount} rules, ${result.mcpCount} MCP configs`);\\n\\n// Import existing tool configurations into .rulesync/\\nconst importResult = await importFromTool({\\n target: \"claudecode\",\\n features: [\"rules\", \"commands\"],\\n});\\nconsole.log(`Imported ${importResult.rulesCount} rules`);\\n\\n// Convert configurations between AI tools without writing intermediate .rulesync/ files\\ntry {\\n const convertResult = await convertFromTool({\\n from: \"claudecode\",\\n to: [\"cursor\", \"copilot\"],\\n features: [\"rules\"],\\n });\\n console.log(`Converted ${convertResult.rulesCount} rule file(s)`);\\n} catch (error) {\\n // Thrown when `from` is empty, `to` is empty, `to` includes `from`,\\n // a source file cannot be parsed, or write fails.\\n console.error(\"convert failed:\", error);\\n}\\n```\\n\\n## `generate(options?)`\\n\\nGenerates configuration files for the specified targets and features.\\n\\n| Option | Type | Default | Description |\\n| ------------------- | -------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\\n| `targets` | `ToolTarget[]` | from config file | Tools to generate configurations for |\\n| `features` | `Feature[]` | from config file | Features to generate |\\n| `outputRoots` | `string[]` | `[process.cwd()]` | Output root directories to generate files into |\\n| `inputRoot` | `string` | `process.cwd()` | Directory containing the `.rulesync/` source files. Output still goes to each `outputRoots` entry; only the input source root is redirected. Mirrors the CLI\\'s `--input-root`. |\\n| `configPath` | `string` | auto-detected | Path to `rulesync.jsonc` |\\n| `verbose` | `boolean` | `false` | Enable verbose logging |\\n| `silent` | `boolean` | `true` | Suppress all output |\\n| `delete` | `boolean` | from config file | Delete existing files before generating |\\n| `global` | `boolean` | `false` | Generate global (user scope) configurations |\\n| `simulateCommands` | `boolean` | `false` | Generate simulated commands |\\n| `simulateSubagents` | `boolean` | `false` | Generate simulated subagents |\\n| `simulateSkills` | `boolean` | `false` | Generate simulated skills |\\n| `dryRun` | `boolean` | `false` | Show changes without writing files |\\n| `check` | `boolean` | `false` | Exit with code 1 if files are not up to date |\\n\\n## `importFromTool(options)`\\n\\nImports existing tool configurations into `.rulesync/` directory.\\n\\n| Option | Type | Default | Description |\\n| ------------ | ------------ | ---------------- | ----------------------------------------- |\\n| `target` | `ToolTarget` | (required) | Tool to import configurations from |\\n| `features` | `Feature[]` | from config file | Features to import |\\n| `configPath` | `string` | auto-detected | Path to `rulesync.jsonc` |\\n| `verbose` | `boolean` | `false` | Enable verbose logging |\\n| `silent` | `boolean` | `true` | Suppress all output |\\n| `global` | `boolean` | `false` | Import global (user scope) configurations |\\n\\n## `convertFromTool(options)`\\n\\nConverts configuration files between AI tools without writing intermediate `.rulesync/` files to disk.\\n\\n| Option | Type | Default | Description |\\n| ------------ | -------------- | ------------- | ------------------------------------------------------------------------------------------- |\\n| `from` | `ToolTarget` | (required) | Source tool to convert configurations from |\\n| `to` | `ToolTarget[]` | (required) | Destination tools to convert to |\\n| `features` | `Feature[]` | `[\"*\"]` | Features to convert. Matches CLI behavior and overrides any `features` in `rulesync.jsonc`. |\\n| `configPath` | `string` | auto-detected | Path to `rulesync.jsonc` |\\n| `verbose` | `boolean` | `false` | Enable verbose logging |\\n| `silent` | `boolean` | `true` | Suppress all output |\\n| `global` | `boolean` | `false` | Convert global (user scope) configurations |\\n| `dryRun` | `boolean` | `false` | Show changes without writing files |\\n',\n faq: '# FAQ\\n\\n## `rulesync generate` doesn\\'t produce what I expect\\n\\nRun `rulesync doctor` first. It performs read-only diagnostics on `rulesync.jsonc` and `rulesync.local.jsonc` and reports problems the generator silently tolerates — most importantly misspelled or unknown configuration keys (the config schema is non-strict, so a typo like `\"target\"` instead of `\"targets\"` is otherwise ignored and generation quietly falls back to defaults). See the [Doctor Command](./reference/cli-commands.md#doctor-command) reference for the full list of checks.\\n\\n## The generated `.mcp.json` doesn\\'t work properly in Claude Code\\n\\nYou can try adding the following to `.claude/settings.json` or `.claude/settings.local.json`:\\n\\n```diff\\n{\\n+ \"enableAllProjectMcpServers\": true\\n}\\n```\\n\\nAccording to [the documentation](https://code.claude.com/docs/en/settings), this means:\\n\\n> Automatically approve all MCP servers defined in project .mcp.json files\\n\\n## Google Antigravity doesn\\'t load rules when `.agents` directories are in `.gitignore`\\n\\nGoogle Antigravity has a known limitation where it won\\'t load rules, workflows, and skills if the `.agents/rules/`, `.agents/workflows/`, and `.agents/skills/` directories are listed in `.gitignore`, even with \"Agent Gitignore Access\" enabled.\\n\\n> **Note:** Antigravity 2.0 uses the plural `.agents/` directory by default (the `antigravity-ide` and `antigravity-cli` targets).\\n\\n**Workaround:** Instead of adding these directories to `.gitignore`, add them to `.git/info/exclude`:\\n\\n```bash\\n# Remove from .gitignore (if present)\\n# **/.agents/rules/\\n# **/.agents/workflows/\\n# **/.agents/skills/\\n\\n# Add to .git/info/exclude\\necho \"**/.agents/rules/\" >> .git/info/exclude\\necho \"**/.agents/workflows/\" >> .git/info/exclude\\necho \"**/.agents/skills/\" >> .git/info/exclude\\n```\\n\\n`.git/info/exclude` works like `.gitignore` but is local-only, so it won\\'t affect Antigravity\\'s ability to load the rules while still excluding these directories from Git.\\n\\nNote: `.git/info/exclude` can\\'t be shared with your team since it\\'s not committed to the repository.\\n\\n## Codex CLI denies SSH agent access, temp-dir writes, or reading its own config with a generated permissions profile\\n\\nThe `[permissions.rulesync]` profile that rulesync generates into `.codex/config.toml` extends Codex CLI\\'s `:workspace` baseline. That baseline is deliberately conservative, so day-to-day development can still hit permission denials: `git push`/`git fetch` over SSH cannot reach the SSH agent socket, some build tools fail without a writable temp dir, and Codex may be blocked from reading its own `~/.codex` configuration.\\n\\nrulesync emits the `.git` write carve-out for you (`\".git/**\" = \"write\"` under `:workspace_roots`; opt out with the `codexcli.git_write_rules: false` override). The whole subtree — including `.git/config` — is writable, because everyday commands such as `git remote add`, `git push -u`, and local-scope `git config` write to the repository config; users who want stricter isolation can add their own `read` override (e.g. `read: { \".git/config\": \"allow\" }`) in the canonical permissions. Everything below, however, depends on your environment or workflow, so rulesync does not add it by default. Where to put each piece differs, because the two tables are managed differently:\\n\\n**Network settings: edit `.codex/config.toml` directly.** Network settings are out of rulesync\\'s management scope by design, keeping you free to edit them. rulesync preserves user-authored network keys when it regenerates the file — `network.enabled` (as long as the profile carries no rulesync-managed allow domains) and unknown keys such as `dangerously_allow_all_unix_sockets` are carried forward verbatim, with a warning so they stay visible:\\n\\n```toml\\n[permissions.rulesync.network]\\nenabled = true\\n# Simplest option: allow all unix sockets. Codex names this \"dangerously_*\"\\n# because it is broad, but it avoids hardcoding an env-dependent socket path.\\ndangerously_allow_all_unix_sockets = true\\n\\n# Stricter alternative: allow only the SSH agent socket.\\n# Replace the path with the actual value of $SSH_AUTH_SOCK on your machine;\\n# Codex does not expand environment variables in these keys.\\n# [permissions.rulesync.network.unix_sockets]\\n# \"/path/to/ssh-agent.sock\" = \"allow\"\\n```\\n\\n**Filesystem entries: author them in `.rulesync/permissions.jsonc`, not in `config.toml`.** The profile\\'s `filesystem` table is fully managed — hand-written entries there are replaced on the next `rulesync generate`. Add the rules to the canonical config instead (use the tool-scoped `codexcli.permission` block so they do not leak into other tools\\' outputs) and regenerate:\\n\\n```jsonc\\n{\\n \"permission\": {\\n // ...your shared rules...\\n },\\n \"codexcli\": {\\n \"permission\": {\\n \"write\": {\\n \".\": \"allow\",\\n \".git/**\": \"allow\",\\n \".agents/**\": \"allow\",\\n \".codex/**\": \"allow\",\\n \":root\": \"allow\",\\n \":minimal\": \"allow\",\\n \":tmpdir\": \"allow\",\\n \":slash_tmp\": \"allow\",\\n },\\n \"read\": { \"~/.codex/**\": \"allow\", \"~/.codex/auth.json\": \"deny\" },\\n },\\n },\\n}\\n```\\n\\nNote that this example is intentionally permissive: the `\":root\"` + `\":minimal\"` write pair grants the sandbox full disk write access — see the trade-off in the entry list below for narrower alternatives.\\n\\nThis generates into the profile as `\".\" = \"write\"`, `\".git/**\" = \"write\"`, `\".agents/**\" = \"write\"`, and `\".codex/**\" = \"write\"` under `:workspace_roots`, plus `\":root\" = \"write\"`, `\":minimal\" = \"write\"`, `\":tmpdir\" = \"write\"`, `\":slash_tmp\" = \"write\"`, `\"~/.codex/**\" = \"read\"`, and `\"~/.codex/auth.json\" = \"deny\"`, and round-trips through `rulesync import` — with two exceptions. First, `\".git/**\" = \"write\"` matches rulesync\\'s default carve-out exactly, so import skips it (it is re-added on every generate); if you later opt out with `codexcli.git_write_rules: false` after an import, re-author the `\".git/**\": \"allow\"` write rule in the canonical config. Second, `\":minimal\"` is never imported regardless of its value — rulesync treats it as its fixed `\"read\"` baseline — so after an import, re-author the `\":minimal\": \"allow\"` write rule as well or the next generate silently drops back to `\":minimal\" = \"read\"`. Note that a tool-scoped category replaces the shared one wholesale for Codex CLI: if your shared `permission` block already has `read`/`write` rules that should also apply to Codex CLI, repeat them inside `codexcli.permission`.\\n\\nWhat each entry does:\\n\\n- **Unix socket access**: `git push`/`git fetch` over SSH needs the agent socket. `dangerously_allow_all_unix_sockets = true` is the simple, environment-independent option; a per-socket `unix_sockets` allow entry with the resolved `$SSH_AUTH_SOCK` path is the stricter one.\\n- **`.` / `.git/**` / `.agents/**` / `.codex/**` write**: the practical write set for the workspace itself. `\".\"` spells out the workspace-subtree write access the `:workspace` baseline already grants (a tool-scoped category replaces the shared block wholesale, so keeping it explicit avoids surprises), and `\".git/**\"` matches the carve-out rulesync emits by default anyway. `.agents/**` and `.codex/**` genuinely add access: Codex\\'s `:workspace` baseline keeps `.git`, `.agents`, and `.codex` read-only inside workspace roots, so without these rules a Codex session cannot update agent files or its own project-level config — for example, running `rulesync generate` inside a session would be denied when writing `.agents/` or `.codex/` outputs. Trade-off: the baseline keeps those two directories read-only precisely so a sandboxed session cannot rewrite its own configuration — with `.codex/**` writable, a compromised or prompt-injected session could relax `.codex/config.toml` (approval policy, permission profiles, MCP servers) for its next run, and with `.agents/**` writable it could persist injected instructions into rule/skill files. Drop these two entries if your workflow does not need in-session writes there.\\n- **`:root` / `:minimal` write**: package runners such as `npx {package}` unpack into the npm cache under the home directory (`~/.npm/_npx`), and many dev tools write to home-directory caches (`~/.cache`, `~/.local`, corepack/pnpm stores); the `:workspace` baseline denies these writes, which breaks the commands outright. `\":root\" = \"write\"` alone is not enough: rulesync emits `\":minimal\" = \"read\"` by default (the platform-default system paths needed for sandboxed command execution), and Codex treats that entry as narrowing the broader `:root` grant — the policy no longer qualifies for full disk write access, so writes that `:root` appears to allow can still be denied. Raise `\":minimal\"` to write alongside `\":root\"` to get the intended effect. Trade-off: the pair grants the sandbox full disk write access, including platform system paths — a compromised or prompt-injected session could then modify shell startup files, `PATH` binaries, or system configuration outside the workspace, effectively neutralizing the sandbox\\'s write isolation. Prefer narrower home-directory patterns instead (e.g. `\"~/.npm/**\"`, `\"~/.cache/**\"`) unless you specifically need system-wide writes, at the cost of chasing each tool\\'s cache path.\\n- **`:tmpdir` / `:slash_tmp` write**: many build tools require a writable temp directory (`$TMPDIR` and `/tmp` respectively).\\n- **`~/.codex/**` read with `auth.json` deny**: Codex can read its own configuration tree while your credentials stay protected. Tilde paths are expanded by Codex itself, so no manual `$HOME` resolution is needed.\\n- **`glob_scan_max_depth`**: no need to add it — rulesync emits the Codex default (`8`) automatically whenever the generated workspace-root rules contain unbounded `**` patterns (the default `.git/**` carve-out already is one).\\n\\nSee the [Codex permissions reference](https://developers.openai.com/codex/permissions) for the full path and network syntax.\\n\\n## Generated rule files create noise in pull request diffs\\n\\nBecause many AI coding tools (Claude Code, Cursor, Copilot, Antigravity, etc.) need to read their rule files directly from the working tree, the files rulesync generates are intentionally not `.gitignore`d. On repositories with many targets, the generated files can dominate a pull request diff and make code review harder.\\n\\n**Workaround:** Add the generated paths to `.gitattributes` with the [`linguist-generated`](https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github#marking-files-as-generated) attribute. GitHub\\'s PR UI will then collapse those files by default while still keeping them visible and loadable by the tools themselves.\\n\\nExample `.gitattributes` for a repo that uses `.agent/`, Claude Code, Cursor, and Copilot targets:\\n\\n```\\n.agent/rules/** linguist-generated\\n.agent/skills/** linguist-generated\\n.agent/workflows/** linguist-generated\\nCLAUDE.md linguist-generated\\n.cursor/rules/** linguist-generated\\n.github/copilot-instructions.md linguist-generated\\n```\\n\\nAdjust the list to match the targets you have configured. These entries only affect how GitHub displays the files in diffs — they don\\'t change how Git tracks them, and they don\\'t interfere with the tools reading the rules.\\n',\n \"getting-started/installation\":\n '# Installation\\n\\n## Package Managers\\n\\n```bash\\nnpm install -g rulesync\\n\\n# And then\\nrulesync --version\\nrulesync --help\\n```\\n\\n## Homebrew (macOS and Linux)\\n\\nrulesync ships a self-contained [Homebrew](https://brew.sh/) tap inside this\\nrepository. Because the repository is not named `homebrew-rulesync`, you must use\\nthe two-argument `brew tap <name> <url>` form to add it — the auto-tap shorthand\\n`brew install dyoshikawa/rulesync/rulesync` cannot resolve it on its own:\\n\\n```bash\\nbrew tap dyoshikawa/rulesync https://github.com/dyoshikawa/rulesync\\nbrew install rulesync\\n\\n# And then\\nrulesync --version\\n```\\n\\nThe formula installs the prebuilt binary for your platform (macOS/Linux, arm64\\nand x64), so it does not depend on a Node.js runtime. It is updated as part of\\neach release. Homebrew does not support Windows; use npm or the\\nsingle-binary download below there.\\n\\n## Single Binary\\n\\nDownload pre-built binaries from the [latest release](https://github.com/dyoshikawa/rulesync/releases/latest). These binaries are built using [Bun\\'s single-file executable bundler](https://bun.sh/docs/bundler/executables).\\n\\n**Quick Install (Linux/macOS - No sudo required):**\\n\\n```bash\\ncurl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/install.sh | bash\\n```\\n\\nOptions:\\n\\n- Install specific version: `curl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/install.sh | bash -s -- v6.4.0`\\n- Custom directory: `RULESYNC_HOME=~/.local curl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/install.sh | bash`\\n\\n::: details Manual installation (requires sudo)\\n\\n### Linux (x64)\\n\\n```bash\\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-linux-x64 -o rulesync && \\\\\\n chmod +x rulesync && \\\\\\n sudo mv rulesync /usr/local/bin/\\n```\\n\\n### Linux (ARM64)\\n\\n```bash\\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-linux-arm64 -o rulesync && \\\\\\n chmod +x rulesync && \\\\\\n sudo mv rulesync /usr/local/bin/\\n```\\n\\n### macOS (Apple Silicon)\\n\\n```bash\\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-darwin-arm64 -o rulesync && \\\\\\n chmod +x rulesync && \\\\\\n sudo mv rulesync /usr/local/bin/\\n```\\n\\n### macOS (Intel)\\n\\n```bash\\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-darwin-x64 -o rulesync && \\\\\\n chmod +x rulesync && \\\\\\n sudo mv rulesync /usr/local/bin/\\n```\\n\\n### Windows (x64)\\n\\n```powershell\\nInvoke-WebRequest -Uri \"https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-windows-x64.exe\" -OutFile \"rulesync.exe\"; `\\n Move-Item rulesync.exe C:\\\\Windows\\\\System32\\\\\\n```\\n\\nOr using curl (if available):\\n\\n```bash\\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-windows-x64.exe -o rulesync.exe && \\\\\\n mv rulesync.exe /path/to/your/bin/\\n```\\n\\n### Verify checksums\\n\\n```bash\\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/SHA256SUMS -o SHA256SUMS\\n\\n# Linux/macOS\\nsha256sum -c SHA256SUMS\\n\\n# Windows (PowerShell)\\n# Download SHA256SUMS file first, then verify:\\nGet-FileHash rulesync.exe -Algorithm SHA256 | ForEach-Object {\\n $actual = $_.Hash.ToLower()\\n $expected = (Get-Content SHA256SUMS | Select-String \"rulesync-windows-x64.exe\").ToString().Split()[0]\\n if ($actual -eq $expected) { \"✓ Checksum verified\" } else { \"✗ Checksum mismatch\" }\\n}\\n```\\n\\n### Verify build provenance\\n\\nRelease binaries carry [GitHub Artifact Attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations), so you can check that the file you downloaded really was built by this repository\\'s release workflow. This needs the [GitHub CLI](https://cli.github.com/) v2.49.0 or later, which is where `gh attestation` was introduced, and a signed-in CLI (`gh auth login`) — verification queries the API even for a public repository.\\n\\n```bash\\n# Linux/macOS — the path the steps above installed the binary to\\ngh attestation verify /usr/local/bin/rulesync \\\\\\n --repo dyoshikawa/rulesync \\\\\\n --signer-workflow dyoshikawa/rulesync/.github/workflows/publish-assets.yml\\n```\\n\\n```powershell\\n# Windows\\ngh attestation verify C:\\\\Windows\\\\System32\\\\rulesync.exe `\\n --repo dyoshikawa/rulesync `\\n --signer-workflow dyoshikawa/rulesync/.github/workflows/publish-assets.yml\\n```\\n\\nPass the path you actually installed the binary to. The command identifies the file by its contents, not by its name, so renaming it during installation — which the steps above do — does not affect verification; a binary installed by `install.sh` or Homebrew is the same file and verifies the same way. `--repo` alone only proves the attestation came from this repository, so `--signer-workflow` is included to pin the workflow that signed it.\\n\\nThis covers the release binaries. The npm package carries npm\\'s own provenance attestation instead, which is checked with `npm audit signatures` rather than `gh attestation verify`.\\n\\n:::\\n',\n \"getting-started/quick-start\":\n '# Quick Start\\n\\n## New Project\\n\\n```bash\\n# Install rulesync globally\\nnpm install -g rulesync\\n\\n# Create necessary directories, sample rule files, and configuration file\\nrulesync init\\n\\n# Install official skills (recommended)\\nrulesync fetch dyoshikawa/rulesync\\n\\n# Or add skill sources to rulesync.jsonc and run \\'rulesync install\\' (see \"Declarative Skill Sources\")\\n```\\n\\n## Existing AI Tool Configurations\\n\\nIf you already have AI tool configurations:\\n\\n```bash\\n# Import existing files (to .rulesync/**/*)\\nrulesync import --targets claudecode # From CLAUDE.md\\nrulesync import --targets cursor # From .cursorrules\\nrulesync import --targets copilot # From .github/copilot-instructions.md\\nrulesync import --targets claudecode --features rules,mcp,commands,subagents\\n\\n# And more tool supports\\n\\n# Generate unified configurations with all features\\nrulesync generate --targets \"*\" --features \"*\"\\n```\\n\\n## Quick Commands\\n\\nFor a comprehensive list of all commands and options, see [CLI Commands](/reference/cli-commands).\\n',\n \"guide/case-studies\":\n '# Case Studies\\n\\nRulesync is trusted by leading companies and recognized by the industry:\\n\\n- **Anthropic Official Customer Story**: [Classmethod Inc. - Improving AI coding tool consistency with Rulesync](https://claude.com/customers/classmethod)\\n- **Asoview Inc.**: [Adopting Rulesync for unified AI development rules](https://tech.asoview.co.jp/entry/2025/12/06/100000)\\n- **KAKEHASHI Tech Blog**: [Building multilingual systems for the LLM era with a monorepo and a \"living specification\"](https://kakehashi-dev.hatenablog.com/entry/2025/12/08/110000)\\n- **Cloudflare**: [Adopting Rulesync for AI coding assistant configuration](https://github.com/cloudflare/cloudflare-docs/pull/28232)\\n- **Ripple**: [Migrating agent rule management to Rulesync](https://github.com/Ripple-TS/ripple/commit/114bcf791c957ab5d43fcc6369515b59b866ce80)\\n- **VOICEVOX**: [Adding Rulesync to unify AI coding assistant rules](https://github.com/VOICEVOX/voicevox/pull/2918)\\n- **Effect**: [Managing agent rules with Rulesync](https://github.com/Effect-TS/effect-smol/pull/986)\\n- **AG Grid**: [Syncing shared AI rules via Rulesync](https://github.com/ag-grid/ag-grid/pull/13044)\\n- **Red Hat Developer Hub**: [Adding Rulesync to synchronize AI Assistant rules](https://github.com/redhat-developer/rhdh/pull/3707)\\n',\n \"guide/configuration\":\n '# Configuration\\n\\nYou can configure Rulesync by creating a `rulesync.jsonc` file in the root of your project.\\n\\n## JSON Schema Support\\n\\nRulesync provides a JSON Schema for editor validation and autocompletion. Add the `$schema` property to your `rulesync.jsonc`:\\n\\n```jsonc\\n// rulesync.jsonc\\n{\\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/config-schema.json\",\\n \"targets\": [\"claudecode\"],\\n \"features\": [\"rules\"],\\n}\\n```\\n\\n## Configuration Options\\n\\nExample:\\n\\n```jsonc\\n// rulesync.jsonc\\n{\\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/config-schema.json\",\\n\\n // List of tools to generate configurations for. You can specify \"*\" to generate all tools.\\n \"targets\": [\"cursor\", \"claudecode\", \"opencode\", \"codexcli\"],\\n\\n // Features to generate. You can specify \"*\" to generate all features.\\n \"features\": [\"rules\", \"mcp\", \"commands\", \"subagents\", \"hooks\", \"permissions\"],\\n\\n // Output root directories to generate files into.\\n // Basically, you can specify `[\".\"]` only.\\n // However, for example, if your project is a monorepo and you have to launch the AI agent at each package directory, you can specify multiple output roots.\\n \"outputRoots\": [\".\"],\\n\\n // Delete existing files before generating\\n \"delete\": true,\\n\\n // Verbose output\\n \"verbose\": false,\\n\\n // Silent mode - suppress all output (except errors)\\n \"silent\": false,\\n\\n // Advanced options\\n \"global\": false, // Generate for global(user scope) configuration files\\n \"simulateCommands\": false, // Generate simulated commands\\n \"simulateSubagents\": false, // Generate simulated subagents\\n \"simulateSkills\": false, // Generate simulated skills\\n\\n // Naming for command files flattened for tools without subdirectory\\n // command support (e.g. Cursor): \"basename\" (default) keeps only the\\n // filename, so `pj/test.md` and `ops/test.md` collide and the last one\\n // wins; \"path\" joins the directory segments into the filename\\n // (`pj/test.md` -> `pj-test.md`), which reduces collisions but cannot\\n // rule them out (a literal `pj-test.md` also maps to `pj-test.md`); the\\n // collision warning still applies.\\n // Tools that support subdirectories (e.g. Claude Code) are unaffected.\\n // Note: switching from \"basename\" to \"path\" renames the generated files\\n // (e.g. `test.md` -> `pj-test.md`); run `rulesync generate` with\\n // `delete: true` (or `--delete`) once after switching, otherwise the\\n // stale old flat-named files remain alongside the new ones.\\n \"flattenedCommandNaming\": \"basename\",\\n\\n // When true (default), `rulesync gitignore` only emits entries for the\\n // tools listed in `targets`. Set to false to emit entries for all supported\\n // tools regardless of `targets`.\\n //\\n // Note: Entries for `agentsmd` (AGENTS.md and related paths) are always\\n // appended even when `gitignoreTargetsOnly` is true and `agentsmd` is\\n // absent from `targets`. AGENTS.md is a de facto standard read by many AI\\n // tools regardless of the target set, so its gitignore entries are emitted\\n // unconditionally to prevent accidental commits of generated rule files.\\n \"gitignoreTargetsOnly\": true,\\n\\n // Declarative rule and skill sources — installed via \\'rulesync install\\'\\n // See the \"Declarative Sources\" section for details.\\n // \"sources\": [\\n // { \"source\": \"owner/repo\" },\\n // { \"source\": \"org/repo\", \"skills\": [\"specific-skill\"] },\\n // { \"source\": \"org/standards\", \"rules\": [\"testing-guidelines\"] },\\n // ],\\n}\\n```\\n\\n## Per-Target Features\\n\\nThe `targets` option accepts both an array and an object format. Use the\\nobject format when you want to declare per-target feature configuration in\\na single place — the object keys are the target tools, and each value\\ncarries the features to generate for that tool:\\n\\n```jsonc\\n// rulesync.jsonc\\n{\\n \"targets\": {\\n \"claudecode\": [\"rules\", \"commands\"],\\n \"cursor\": [\"rules\", \"mcp\"],\\n \"copilot\": [\"rules\", \"subagents\"],\\n },\\n}\\n```\\n\\nIn this example:\\n\\n- `claudecode` generates rules and commands\\n- `cursor` generates rules and MCP configuration\\n- `copilot` generates rules and subagents\\n\\n> **Important:** When `targets` is in object form, the top-level `features`\\n> field must be omitted. Declaring both would double-define the target\\n> set, so the config loader rejects that combination.\\n\\nYou can also use `*` (wildcard) inside a target\\'s value to enable every\\nfeature for that tool:\\n\\n```jsonc\\n{\\n \"targets\": {\\n \"claudecode\": [\"*\"], // Generate all features for Claude Code\\n \"cursor\": [\"rules\"], // Only rules for Cursor\\n },\\n}\\n```\\n\\n### Per-feature options\\n\\nSome features accept additional configuration. To pass options through, use\\nthe object form for a target\\'s value instead of an array. Each feature key\\nmaps to either `true`/`false` (enable/disable) or an options object.\\n\\n```jsonc\\n{\\n \"gitignoreDestination\": \"gitignore\",\\n \"targets\": {\\n \"claudecode\": {\\n \"gitignoreDestination\": \"gitattributes\",\\n \"rules\": { \"ruleDiscoveryMode\": \"explicit\" },\\n \"ignore\": {\\n \"fileMode\": \"local\",\\n \"gitignoreDestination\": \"gitignore\",\\n },\\n },\\n },\\n}\\n```\\n\\n`gitignoreDestination` controls where `rulesync gitignore` writes path entries.\\nYou can set it:\\n\\n- at **root level** (`gitignoreDestination`)\\n- at **tool level** (`targets.<tool>.gitignoreDestination`)\\n- or at **tool × feature level**\\n (`targets.<tool>.<feature>.gitignoreDestination`)\\n\\nAllowed values:\\n\\n- `\"gitignore\"` (default)\\n- `\"gitattributes\"`\\n\\nPriority is **more specific wins**:\\n\\n1. tool × feature level\\n2. tool level\\n3. root level\\n4. default (`\"gitignore\"`)\\n\\nThe current per-feature options are:\\n\\n| Target | Feature | Option | Values | Default |\\n| ------------ | -------- | ---------------------- | ------------------------------------------------------------------------------ | ------------- |\\n| `claudecode` | `rules` | `ruleDiscoveryMode` | `\"none\"` / `\"explicit\"` | tool default |\\n| any | `rules` | `includeLocalRoot` | `true` / `false` (when `false`, `localRoot` rules are skipped for this target) | `true` |\\n| `claudecode` | `ignore` | `fileMode` | `\"shared\"` (settings.json) / `\"local\"` (settings.local.json) | `\"shared\"` |\\n| any | any | `gitignoreDestination` | `\"gitignore\"` / `\"gitattributes\"` | `\"gitignore\"` |\\n\\nSee [`docs/reference/file-formats.md`](../reference/file-formats.md#where-ignore-patterns-are-written-per-tool)\\nfor the rationale behind the Claude Code default and when to switch to\\n`\"local\"`.\\n\\n## Local Configuration\\n\\nRulesync supports a local configuration file (`rulesync.local.jsonc`) for machine-specific or developer-specific settings. This file is automatically added to `.gitignore` by `rulesync gitignore` and should not be committed to the repository.\\n\\n**Configuration Priority** (highest to lowest):\\n\\n1. CLI options\\n2. `rulesync.local.jsonc`\\n3. `rulesync.jsonc`\\n4. Default values\\n\\nExample usage:\\n\\n```jsonc\\n// rulesync.local.jsonc (not committed to git)\\n{\\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/config-schema.json\",\\n // Override targets for local development\\n \"targets\": [\"claudecode\"],\\n // Enable verbose output for debugging\\n \"verbose\": true,\\n}\\n```\\n\\n## Target Order and File Conflicts\\n\\nWhen multiple targets write to the same output file, **the last target in the array wins**. This is the \"last-wins\" behavior.\\n\\nFor example, both `agentsmd` and `opencode` generate `AGENTS.md`:\\n\\n```jsonc\\n{\\n // opencode wins because it comes last\\n \"targets\": [\"agentsmd\", \"opencode\"],\\n \"features\": [\"rules\"],\\n}\\n```\\n\\nIn this case:\\n\\n1. `agentsmd` generates `AGENTS.md` first\\n2. `opencode` generates `AGENTS.md` second, overwriting the previous file\\n\\nIf you want `agentsmd`\\'s output instead, reverse the order:\\n\\n```jsonc\\n{\\n // agentsmd wins because it comes last\\n \"targets\": [\"opencode\", \"agentsmd\"],\\n \"features\": [\"rules\"],\\n}\\n```\\n',\n \"guide/declarative-sources\":\n '# Declarative Sources\\n\\nRulesync can fetch rules and skills from external repositories using the `install` command. Instead of manually running `fetch` for each source, declare it in your `rulesync.jsonc` and run `rulesync install` to resolve and fetch its selected artifacts. Then `rulesync generate` processes them as curated inputs. Typical workflow: `rulesync install && rulesync generate`.\\n\\nTo add one source without editing JSONC by hand, run `rulesync add <source>`. It preserves existing comments, appends the source entry, installs it, and updates the appropriate lockfile:\\n\\n```bash\\nrulesync add anthropics/skills --skills skill-creator\\n\\n# Add one rule without selecting any skills\\nrulesync add acme/ai-standards --rules testing-guidelines\\n```\\n\\nThe command fetches only the source being added. Existing sources must already be locked and installed; run `rulesync install` first when they are not. If the new source fails, Rulesync restores the manifest, source lockfiles, curated rules, and curated skills to their previous state.\\n\\n## Configuration\\n\\nAdd a `sources` array to your `rulesync.jsonc`:\\n\\n```jsonc\\n{\\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/config-schema.json\",\\n \"targets\": [\"copilot\", \"claudecode\"],\\n \"features\": [\"rules\", \"skills\"],\\n \"sources\": [\\n // Fetch all skills from a GitHub repository (default transport)\\n { \"source\": \"owner/repo\" },\\n\\n // Fetch only specific skills by name\\n { \"source\": \"anthropics/skills\", \"skills\": [\"skill-creator\"] },\\n\\n // Fetch only specific .md rules from rules/ (no skills)\\n {\\n \"source\": \"acme/ai-standards\",\\n \"rules\": [\"testing-guidelines\", \"typescript-conventions\"],\\n },\\n\\n // Rules and skills can be selected from the same source\\n {\\n \"source\": \"acme/ai-assets\",\\n \"rules\": [\"*\"],\\n \"rulesPath\": \"exports/rules\",\\n \"skills\": [\"review-pr\"],\\n \"path\": \"exports/skills\",\\n },\\n\\n // With ref pinning and subdirectory path (same syntax as fetch command)\\n { \"source\": \"owner/repo@v1.0.0:path/to/skills\" },\\n\\n // Git transport — works with any git remote (Azure DevOps, Bitbucket, etc.)\\n {\\n \"source\": \"https://dev.azure.com/org/project/_git/repo\",\\n \"transport\": \"git\",\\n \"ref\": \"main\",\\n \"path\": \"exports/skills\",\\n },\\n\\n // Git transport with a local repository\\n { \"source\": \"file:///path/to/local/repo\", \"transport\": \"git\" },\\n\\n // Git transport against a single-skill repo whose SKILL.md is at the root\\n {\\n \"source\": \"https://github.com/feature-sliced/skills\",\\n \"transport\": \"git\",\\n \"path\": \".\",\\n },\\n\\n // npm transport (EXPERIMENTAL) — fetch a package from an npm-compatible\\n // registry (npmjs.org, JFrog Artifactory, Sonatype Nexus, Verdaccio, ...)\\n {\\n \"source\": \"@acme/skill-package\",\\n \"transport\": \"npm\",\\n \"registry\": \"https://acme.jfrog.io/artifactory/api/npm/npm-local/\",\\n \"tokenEnv\": \"ACME_REGISTRY_TOKEN\",\\n },\\n ],\\n}\\n```\\n\\nEach entry in `sources` accepts:\\n\\n| Property | Type | Description |\\n| ----------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\\n| `source` | `string` | Repository source. For GitHub transport: `owner/repo` or `owner/repo@ref:path`. For git transport: a full git URL. For npm transport: a package name (`pkg` or `@scope/pkg`). |\\n| `skills` | `string[]` | Optional skill names to fetch. `\"*\"` selects all skills. When both `skills` and `rules` are omitted, all skills are fetched for backward compatibility. |\\n| `rules` | `string[]` | Optional rule names to fetch. Names may include or omit `.md`; `\"*\"` selects every direct `.md` file under `rulesPath`. Setting only `rules` fetches no skills. |\\n| `transport` | `string` | `\"github\"` (default) uses the GitHub REST API. `\"git\"` uses git CLI and works with any git remote. `\"npm\"` (experimental) fetches a package from an npm-compatible registry. |\\n| `ref` | `string` | Branch, tag, or ref to fetch from. Defaults to the remote\\'s default branch. For GitHub transport, use the `@ref` source syntax. For npm transport: an exact version or dist-tag (defaults to `latest`). |\\n| `path` | `string` | Path to the skills directory within the repository. Defaults to `\"skills\"`. Set to `\"\"`, `\".\"`, or `\"./\"` to target the entire repository root (see note below). For GitHub transport, use the `:path` source syntax. |\\n| `rulesPath` | `string` | Path to the rules directory within the repository or package. Defaults to `\"rules\"`. This is independent from the skills-only `path` field. |\\n| `registry` | `string` | npm transport only. Base URL of the npm-compatible registry. Defaults to `https://registry.npmjs.org`. |\\n| `tokenEnv` | `string` | npm transport only. Name of the environment variable holding the registry token. Defaults to `NPM_TOKEN`. |\\n\\nRules are flat source files: only direct `.md` children of `rulesPath` are discovered. Nested rule files are not installed. Fetched rules are written to `.rulesync/rules/.curated/<rule-name>.md`; during generation they behave as if they were ordinary files directly under `.rulesync/rules/`.\\n\\n> **Repository-root paths (`path: \".\"`):** When `path` is `\"\"`, `\".\"`, or `\"./\"` (with the `git` transport), rulesync disables sparse-checkout and fetches the **entire** repository tree, then groups each top-level directory as a skill. This is useful for single-skill repositories whose `SKILL.md` lives at the repo root (`<repo>/SKILL.md`) rather than under a `skills/` container. Because the whole tree is fetched, prefer a narrower `path` for large repositories; the fetch is still bounded by rulesync\\'s file-count, total-size, and depth limits.\\n\\n## npm Transport (Experimental)\\n\\n> [!WARNING]\\n> The `npm` transport is **experimental**. Its configuration surface and lockfile format may change in a future release.\\n\\nThe `npm` transport fetches skills from any registry that implements the npm registry API. Because JFrog Artifactory, Sonatype Nexus, Verdaccio, GitHub Packages, and similar private registries all expose an npm-compatible API, a single transport with a configurable `registry` URL covers them all. This lets enterprises whose build environments cannot reach public GitHub distribute skills internally as npm packages.\\n\\nHow a package is fetched:\\n\\n1. The package metadata (packument) is fetched from `<registry>/<package>` using the abbreviated `application/vnd.npm.install-v1+json` form.\\n2. The declared `ref` (an **exact version** or a **dist-tag** such as `latest` or `beta` — semver ranges are not supported) is resolved to a concrete version.\\n3. The version\\'s tarball is downloaded and verified against the registry\\'s `dist.integrity` / `dist.shasum` metadata.\\n4. The tarball is extracted **in memory** with a hardened minimal tar reader: only regular files are materialized (symlinks, hardlinks, and device entries are skipped), path traversal is rejected, and extraction is capped at 10,000 files / 100 MB to prevent decompression bombs.\\n\\nPackage layout: skills are discovered the same way as for the git transports. Skill directories under `skills/` (or the configured `path`) are installed as `.rulesync/skills/.curated/<name>/`. Direct `.md` files under `rules/` (or the configured `rulesPath`) can be selected with `rules` and are installed under `.rulesync/rules/.curated/`. A single-skill package with `SKILL.md` at the package root is installed as one skill named after the package\\'s base name (`@acme/my-skill` installs as `my-skill`); note that this root fallback installs the package\\'s root-level files only, so prefer the `skills/<name>/` layout for skills that carry subdirectories such as `references/`.\\n\\nAuthentication uses a bearer token from an environment variable: `NPM_TOKEN` by default, or the variable named by the per-source `tokenEnv` field. The token is sent as `Authorization: Bearer <token>` to the registry (and to the tarball host only when it matches the registry host). `.npmrc` files are intentionally **not** read.\\n\\nResolved versions are pinned in `rulesync-npm.lock.json` (next to `rulesync.lock`), which records the resolved version, the tarball integrity, and per-artifact content hashes. Commit it for reproducible installs; `--update` and `--frozen` behave the same as for git sources.\\n\\n## How It Works\\n\\nWhen `rulesync install` runs and `sources` is configured:\\n\\n1. **Lockfile resolution** — Each source\\'s ref is resolved to a commit SHA and stored in `rulesync.lock` (at the project root). On subsequent runs the exact locked SHA is checked out for deterministic builds. npm-transport sources are pinned in a separate `rulesync-npm.lock.json` (resolved version + tarball integrity).\\n2. **Remote artifact listing** — The configured skills and rules directories are listed from the remote source.\\n3. **Filtering** — Only the names selected by `skills` and `rules` are fetched. Omitting both fields retains the historical behavior of fetching all skills.\\n4. **Precedence rules**:\\n - **Local inputs always win** — Rules and skills outside `.curated/` take precedence over a remote artifact with the same name.\\n - **First-declared source wins** — If two sources provide an artifact with the same name, the one declared first in the `sources` array is used.\\n5. **Output** — Fetched rules are written to `.rulesync/rules/.curated/<rule-name>.md`; fetched skills are written to `.rulesync/skills/.curated/<skill-name>/`. Both directories are automatically added to `.gitignore` by `rulesync gitignore`.\\n\\n## Install Modes\\n\\n`rulesync install` supports three install modes via `--mode <mode>`:\\n\\n| Mode | Manifest input | Lockfile | Output layout |\\n| ---------- | ---------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ |\\n| `rulesync` | `rulesync.jsonc` `sources` | `rulesync.lock` (+ `rulesync-npm.lock.json` for npm sources) | `.rulesync/rules/.curated/<name>.md`, `.rulesync/skills/.curated/<name>/` (then re-emitted by `rulesync generate`) |\\n| `apm` | `apm.yml` `dependencies.apm` | `rulesync-apm.lock.yaml` | `.github/instructions/`, `.github/skills/` (APM v1 layout) |\\n| `gh` | `rulesync.jsonc` `sources` | `rulesync-gh.lock.yaml` | Per-agent / per-scope dirs (matching `gh skill install`) |\\n\\nWhen `--mode` is omitted, rulesync defaults to `rulesync` mode. If `apm.yml` is present and `sources` is also defined, you must pass `--mode apm` or `--mode rulesync` to disambiguate.\\n\\n### `--mode gh` — gh-skill-install–compatible layout\\n\\n`--mode gh` reads the same `sources` array from `rulesync.jsonc` but writes each discovered skill into the agent-specific directory expected by `gh skill install`. Each source supports two extra fields:\\n\\n| Property | Type | Default | Description |\\n| -------- | -------- | ---------------- | ----------------------------------------------------------------------------------------- |\\n| `agent` | `string` | `github-copilot` | One of `github-copilot`, `claude-code`, `cursor`, `codex`, `gemini`, `antigravity`. |\\n| `scope` | `string` | `project` | `project` writes inside the project root; `user` writes inside the user\\'s home directory. |\\n\\nAgent → install directory mapping:\\n\\n| Agent | Project scope (relative to project root) | User scope (relative to home) |\\n| ---------------- | ---------------------------------------- | ----------------------------- |\\n| `github-copilot` | `.agents/skills` | `.copilot/skills` |\\n| `claude-code` | `.claude/skills` | `.claude/skills` |\\n| `cursor` | `.agents/skills` | `.cursor/skills` |\\n| `codex` | `.agents/skills` | `.agents/skills` |\\n| `gemini` | `.agents/skills` | `.gemini/skills` |\\n| `antigravity` | `.agents/skills` | `.gemini/antigravity/skills` |\\n\\nFor each skill discovered as `skills/<name>/SKILL.md` in the remote repository, rulesync deploys the entire skill directory to `<install-dir>/<name>/` and injects a provenance frontmatter block (`source`, `repository`, `ref`) into the deployed `SKILL.md`. The lockfile `rulesync-gh.lock.yaml` records one entry per `(source, agent, scope, skill)` tuple.\\n\\nPer-source field support in `--mode gh`:\\n\\n| Field | Status |\\n| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- |\\n| `source` | Required. Must resolve to a GitHub repository (`owner/repo`, `owner/repo@ref`, or an `https://github.com/...` URL). |\\n| `skills` | Optional. When set, only the listed skill names are installed; remote skills not in the list are skipped, and missing names log a warning. |\\n| `rules` | **Rejected.** Declarative rules are supported only in `--mode rulesync`. |\\n| `rulesPath` | **Rejected.** Declarative rules are supported only in `--mode rulesync`. |\\n| `ref` | Optional. Pins a tag, branch, or commit SHA. When omitted, gh mode resolves to the latest release\\'s tag, falling back to the default branch. |\\n| `agent` | Optional. Defaults to `github-copilot`. See the agent table above. |\\n| `scope` | Optional. Defaults to `project`. |\\n| `transport` | **Rejected.** gh mode is GitHub-only and does not honor the `git` transport. Drop the field or switch to `--mode rulesync`. |\\n| `path` | **Rejected.** The remote layout is fixed to `skills/<name>/SKILL.md`. Repositories that store skills elsewhere are not supported in gh mode. |\\n\\nThe remote repository must use the layout `skills/<name>/SKILL.md` (one directory per skill, each containing a `SKILL.md`). Other layouts are not auto-discovered.\\n\\nExample `rulesync.jsonc`:\\n\\n```jsonc\\n{\\n \"targets\": [\"claudecode\"],\\n \"features\": [\"rules\"],\\n \"sources\": [\\n // Default: agent=github-copilot, scope=project -> .agents/skills/git-commit/\\n { \"source\": \"acme/skills\", \"skills\": [\"git-commit\"] },\\n\\n // Same source, deployed for Claude Code at user scope -> ~/.claude/skills/git-commit/\\n {\\n \"source\": \"acme/skills\",\\n \"skills\": [\"git-commit\"],\\n \"agent\": \"claude-code\",\\n \"scope\": \"user\",\\n },\\n ],\\n}\\n```\\n\\nRun with `npx rulesync install --mode gh`.\\n\\n## CLI Options\\n\\nThe `install` command accepts these flags:\\n\\n| Flag | Description |\\n| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\\n| `--mode <mode>` | Install mode: `rulesync` (default), `apm`, or `gh`. See **Install Modes** above. |\\n| `--update` | Force re-resolve all source refs, ignoring the lockfile (useful to pull new updates). |\\n| `--frozen` | Fail if a lockfile is missing or does not cover declared sources and rule selections. Fetches missing locked artifacts without updating the lockfile. Useful for CI. |\\n| `--token <token>` | GitHub token for private repositories. |\\n\\n```bash\\n# Install rules and skills using locked refs\\nrulesync install\\n\\n# Force update to latest refs\\nrulesync install --update\\n\\n# Strict CI mode — fail if lockfile doesn\\'t cover all sources and selections\\nrulesync install --frozen\\n\\n# Install then generate\\nrulesync install && rulesync generate\\n\\n# Skip source installation — just don\\'t run install\\nrulesync generate\\n```\\n\\n## Lockfile\\n\\nThe lockfile at `rulesync.lock` (at the project root) records the resolved commit SHA, rule selection metadata, and per-artifact integrity hashes for each source so that builds are reproducible. Rulesync verifies cached rule content against these hashes before reusing it. It is safe to commit this file. An example:\\n\\n```json\\n{\\n \"lockfileVersion\": 1,\\n \"sources\": {\\n \"owner/skill-repo\": {\\n \"requestedRef\": \"main\",\\n \"resolvedRef\": \"abc123def456...\",\\n \"resolvedAt\": \"2025-01-15T12:00:00.000Z\",\\n \"skills\": {\\n \"my-skill\": { \"integrity\": \"sha256-abcdef...\" },\\n \"another-skill\": { \"integrity\": \"sha256-123456...\" }\\n },\\n \"rules\": {\\n \"testing-guidelines\": { \"integrity\": \"sha256-789abc...\" }\\n },\\n \"ruleSelection\": [\"*\"],\\n \"rulesPath\": \"rules\",\\n \"resolvedRuleNames\": [\"testing-guidelines\"]\\n }\\n }\\n}\\n```\\n\\nTo update locked refs, run `rulesync install --update`.\\n\\nnpm-transport sources (experimental) are pinned in a separate `rulesync-npm.lock.json`, because they lock a resolved package version and tarball integrity instead of a commit SHA:\\n\\n```json\\n{\\n \"lockfileVersion\": 1,\\n \"sources\": {\\n \"@acme/skill-package\": {\\n \"registry\": \"https://acme.jfrog.io/artifactory/api/npm/npm-local\",\\n \"requestedVersion\": \"latest\",\\n \"resolvedVersion\": \"1.2.3\",\\n \"integrity\": \"sha512-...\",\\n \"resolvedAt\": \"2026-01-15T12:00:00.000Z\",\\n \"skills\": {\\n \"my-skill\": { \"integrity\": \"sha256-abcdef...\" }\\n },\\n \"rules\": {\\n \"testing-guidelines\": { \"integrity\": \"sha256-789abc...\" }\\n },\\n \"ruleSelection\": [\"testing-guidelines\"],\\n \"rulesPath\": \"rules\",\\n \"resolvedRuleNames\": [\"testing-guidelines\"]\\n }\\n }\\n}\\n```\\n\\nIt is safe (and recommended) to commit this file as well.\\n\\n## Authentication\\n\\nGitHub transport uses the `GITHUB_TOKEN` or `GH_TOKEN` environment variable for authentication. This is required for private repositories and recommended for better rate limits. Git transport relies on your local git credential configuration (SSH keys, credential helpers, etc.). npm transport (experimental) uses the `NPM_TOKEN` environment variable, or the variable named by the per-source `tokenEnv` field; `.npmrc` files are not read.\\n\\n```bash\\n# Using environment variable\\nexport GITHUB_TOKEN=ghp_xxxx\\nnpx rulesync install\\n\\n# Or using GitHub CLI\\nGITHUB_TOKEN=$(gh auth token) npx rulesync install\\n```\\n\\n> [!TIP]\\n> The `install` command also accepts a `--token` flag for explicit authentication: `rulesync install --token ghp_xxxx`.\\n\\n## Curated vs Local Inputs\\n\\n| Location | Type | Precedence | Committed to Git |\\n| ------------------------------------ | ------- | ---------- | ---------------- |\\n| `.rulesync/skills/<name>/` | Local | Highest | Yes |\\n| `.rulesync/skills/.curated/<name>/` | Curated | Lower | No (gitignored) |\\n| `.rulesync/rules/<name>.md` | Local | Highest | Yes |\\n| `.rulesync/rules/.curated/<name>.md` | Curated | Lower | No (gitignored) |\\n\\nWhen a local and curated artifact share the same name, the local artifact is used and the remote one is not fetched.\\n',\n \"guide/dry-run\":\n '# Dry Run\\n\\nRulesync provides two dry run options for the `generate` command that allow you to see what changes would be made without actually writing files:\\n\\n## `--dry-run`\\n\\nShow what would be written or deleted without actually writing any files. Changes are displayed with a `[DRY RUN]` prefix.\\n\\n```bash\\nrulesync generate --dry-run --targets claudecode --features rules\\n```\\n\\n## `--check`\\n\\nSame as `--dry-run`, but exits with code 1 if files are not up to date. This is useful for CI/CD pipelines to verify that generated files are committed.\\n\\n```bash\\n# In your CI pipeline\\nrulesync generate --check --targets \"*\" --features \"*\"\\necho $? # 0 if up to date, 1 if changes needed\\n```\\n\\n> [!NOTE]\\n> `--dry-run` and `--check` cannot be used together.\\n',\n \"guide/global-mode\":\n '# Global Mode\\n\\nYou can use global mode via Rulesync by enabling `--global` option. It can also be called as user scope mode.\\n\\nCurrently, supports rules generation for Claude Code, GitHub Copilot, and OpenCode. Import for global files is supported for rules and commands. Command generation in global mode remains Claude Code only.\\n\\n1. Create an any name directory. For example, if you prefer `~/.aiglobal`, run the following command.\\n\\n ```bash\\n mkdir -p ~/.aiglobal\\n ```\\n\\n2. Initialize files for global files in the directory.\\n\\n ```bash\\n cd ~/.aiglobal\\n rulesync init\\n ```\\n\\n3. Edit `~/.aiglobal/rulesync.jsonc` to enable global mode.\\n\\n ```jsonc\\n {\\n \"global\": true,\\n }\\n ```\\n\\n4. Edit `~/.aiglobal/.rulesync/rules/overview.md` to your preferences.\\n\\n ```md\\n ---\\n root: true\\n ---\\n\\n # The Project Overview\\n\\n ...\\n ```\\n\\n5. Generate rules for global settings.\\n\\n ```bash\\n # Run in the `~/.aiglobal` directory\\n rulesync generate\\n ```\\n\\n> [!NOTE]\\n> Currently, when in the directory enabled global mode:\\n>\\n> - `rulesync.jsonc` only supports `global`, `features`, `delete` and `verbose`. `Features` can be set `\"rules\"` and `\"commands\"`. Other parameters are ignored.\\n> - Multiple `root: true` files can target the same tool in project and global modes. Compatible root or plain-Markdown single-file outputs are combined with a blank line between files. Local rules are composed first in lexicographic source file path order, followed by non-overridden `.curated/` rules in lexicographic order, so filename prefixes control composition order within each set. Distinct native paths remain separate. Explicitly supported plain-Markdown modular path collisions are combined (fragments whose generated output carries its own frontmatter block stay separate), unsafe collisions involving a source root rule fail, and other exact or case-insensitive modular collisions warn that the last write wins wherever their paths collide.\\n> - Only Claude Code is supported for global mode commands.\\n',\n \"guide/official-skills\":\n \"# Official Skills\\n\\nRulesync provides official skills that you can install using the fetch command or declarative sources:\\n\\n```bash\\n# One-time fetch\\nrulesync fetch dyoshikawa/rulesync\\n\\n# Or declare in rulesync.jsonc and run 'rulesync install'\\n```\\n\\nThis will install the Rulesync documentation skill to your project.\\n\",\n \"guide/plugin-packaging\":\n '# Plugin Packaging\\n\\nRulesync can generate and import configuration components inside existing Claude Code and Google Antigravity plugin directories. Use the packaging targets when the files are distributed as a plugin instead of being installed directly as project or user configuration:\\n\\n- `claudecode-plugin`\\n- `antigravity-plugin`\\n\\nPackaging targets are project-scope only and are intentionally excluded from `--targets \"*\"`. Their component directories, such as `skills/` and `rules/`, live directly under the output root and could otherwise collide with ordinary project directories.\\n\\n## Generate into a plugin\\n\\nPoint `--output-roots` at the plugin root:\\n\\n```bash\\nrulesync generate \\\\\\n --targets claudecode-plugin \\\\\\n --features mcp,commands,subagents,skills,hooks \\\\\\n --output-roots ./plugins/review-tools\\n\\nrulesync generate \\\\\\n --targets antigravity-plugin \\\\\\n --features rules,mcp,skills,hooks \\\\\\n --output-roots ./plugins/review-tools\\n```\\n\\nThe same configuration can be persisted in `rulesync.jsonc`:\\n\\n```jsonc\\n{\\n \"outputRoots\": {\\n \"claudecode-plugin\": \"./plugins/claude-review-tools\",\\n \"antigravity-plugin\": \"./plugins/antigravity-review-tools\",\\n },\\n \"targets\": {\\n \"claudecode-plugin\": [\"mcp\", \"commands\", \"subagents\", \"skills\", \"hooks\"],\\n \"antigravity-plugin\": [\"rules\", \"mcp\", \"skills\", \"hooks\"],\\n },\\n}\\n```\\n\\nRulesync manages the selected component files but does not create or modify plugin metadata, marketplace catalogs, scripts, or other package assets. Keep the required upstream manifest in the plugin directory:\\n\\n- Claude Code: `.claude-plugin/plugin.json` when the plugin uses a manifest\\n- Antigravity: `plugin.json`\\n\\nThe plugin root must already exist. Rulesync rejects symbolic links anywhere in the plugin tree before importing, generating, or deleting files so package components cannot escape the selected root.\\n\\n`--delete` reconciles the selected Rulesync-managed component trees, so do not mix hand-authored files into a component tree that Rulesync owns.\\n\\n## Import from a plugin\\n\\nUse `--output-root` to identify the plugin directory to read. Imported canonical files are written to `.rulesync/` in the current working directory:\\n\\n```bash\\nrulesync import \\\\\\n --targets claudecode-plugin \\\\\\n --features mcp,commands,subagents,skills,hooks \\\\\\n --output-root ./plugins/review-tools\\n\\nrulesync import \\\\\\n --targets antigravity-plugin \\\\\\n --features rules,mcp,skills,hooks \\\\\\n --output-root ./plugins/review-tools\\n```\\n\\nThe `convert` command does not accept packaging targets because it has no separate source and destination plugin roots. Import from the source plugin first, then generate into the destination plugin.\\n\\n## Component paths\\n\\n| Target | Rules | MCP | Commands | Subagents | Skills | Hooks |\\n| -------------------- | ------------ | ----------------- | --------------- | ------------- | ------------------- | ------------------ |\\n| `claudecode-plugin` | — | `.mcp.json` | `commands/*.md` | `agents/*.md` | `skills/*/SKILL.md` | `hooks/hooks.json` |\\n| `antigravity-plugin` | `rules/*.md` | `mcp_config.json` | — | — | `skills/*/SKILL.md` | `hooks.json` |\\n\\nClaude-specific frontmatter and hook overrides continue to use the `claudecode` sections in Rulesync source files. Antigravity plugin output uses the `antigravity-ide` conversion model and override sections because its plugin components follow the Antigravity IDE format.\\n\\n## Installing a `claudecode-plugin` bundle in JetBrains Junie\\n\\n[Junie CLI Extensions](https://junie.jetbrains.com/docs/junie-cli-extensions.html) — Junie\\'s bundle system for skills, MCP servers, subagents, slash commands, and guidelines — accept two marketplace manifest formats: the native `.junie-extension/marketplace.json` and Claude Code\\'s `.claude-plugin/marketplace.json`. A plugin generated with the `claudecode-plugin` target and published in a Claude-compatible plugin marketplace is therefore installable in Junie via `/extensions`, without a Junie-specific rulesync target.\\n\\nAs with Claude Code, rulesync manages only the component files (`commands/`, `agents/`, `skills/`, `.mcp.json`, `hooks/hooks.json`); the `.claude-plugin/plugin.json` and marketplace catalog remain hand-authored. Junie\\'s documentation confirms the manifest-format compatibility but does not enumerate a directory-level mapping for Claude plugin contents, so verify the components you care about after installing.\\n',\n \"guide/separate-input-root\":\n '# Separate Input Root\\n\\nThe `--input-root <path>` flag lets you point `rulesync generate` at a `.rulesync/` source directory that is different from the current working directory. This decouples where your rule definitions live from where the generated tool configuration files are written.\\n\\n> **Currently supported on `generate` only.** At present, `--input-root` is wired into the `rulesync generate` command only. Other commands (`import`, `convert`, `gitignore`, `install`, `fetch`, `init`) still read `.rulesync/` from the current working directory. To use the same source directory with those commands, `cd` into the input-root directory first.\\n\\n## Primary use case: centralized rules across all repos\\n\\nA common workflow is to keep a single set of AI rules in a shared directory (e.g. `~/.aiglobal`) and apply them to every project without switching directories:\\n\\n```bash\\n# In any project directory — rules are read from ~/.aiglobal/.rulesync/\\nrulesync generate --input-root ~/.aiglobal --targets \"*\" --features rules\\n```\\n\\nWithout `--input-root`, you would have to `cd ~/.aiglobal && rulesync generate` and then `cd -` back, and the output files would land in `~/.aiglobal` instead of the current project.\\n\\n## Step-by-step setup\\n\\n1. Create and initialize a shared rules directory:\\n\\n ```bash\\n mkdir -p ~/.aiglobal\\n cd ~/.aiglobal\\n rulesync init\\n ```\\n\\n2. Edit your shared rules (`~/.aiglobal/.rulesync/rules/overview.md`, etc.) to your preferences.\\n\\n3. From any project, generate configurations using the shared rules:\\n\\n ```bash\\n # In your project directory\\n rulesync generate --input-root ~/.aiglobal --targets claudecode --features rules\\n ```\\n\\n## Comparison with `--global`\\n\\nThese two flags serve different but complementary purposes:\\n\\n| | `--input-root` | `--global` |\\n| ------------ | ------------------------------------------------- | ---------------------------------------------------------------------- |\\n| **Changes** | Source location (where `.rulesync/` is read from) | Output location (writes to user-scope config paths, e.g. `~/.claude/`) |\\n| **Use when** | Your rule definitions live in a non-CWD directory | You want the output to go to the tool\\'s global (user-scope) config |\\n\\nThey can be combined. For example, to read rules from `~/.aiglobal` and write them to Claude Code\\'s global settings:\\n\\n```bash\\nrulesync generate --input-root ~/.aiglobal --global --targets claudecode --features rules\\n```\\n\\n> **`--input-root` does not enable `--global`.** When `--input-root` is explicitly provided, Rulesync reads `.rulesync/` from that directory, but output scope still follows the CLI flags: use `--global` for user-scope output, and omit it for project-scope output. A `\"global\": true` setting in the `rulesync.jsonc` under `--input-root` is **not** applied unless you also pass `--global`, and Rulesync will emit a warning when dropping it so the override is visible.\\n\\n## Symlinks and trust\\n\\nRulesync follows symbolic links during file discovery. A symlink inside `.rulesync/` that points outside the directory will be followed transparently, and the resolved file content will be copied into the generated output. This is intentional: it lets you centralize shared skills or rules in one place and reference them via symlinks from multiple project directories without duplication.\\n\\nThe trust boundary is the directory you point Rulesync at. `--input-root` is `resolve()`-ed to an absolute path before use, but there is no `realpath`-based boundary check on individual symlinks inside it. Only run Rulesync against trees you control. Directory symlink cycles are handled safely — discovery results are deduplicated by real path, so a cycle does not produce duplicated output. See the [File Formats § Symlinks](../reference/file-formats.md#symlinks) note for the behavior that applies across all features.\\n',\n \"guide/simulated-features\":\n \"# Simulated Commands, Subagents and Skills\\n\\nSimulated commands, subagents and skills allow you to generate simulated features for cursor, codexcli and etc. This is useful for shortening your prompts.\\n\\n1. Prepare `.rulesync/commands/*.md`, `.rulesync/subagents/*.md` and `.rulesync/skills/*/SKILL.md` for your purposes.\\n2. Generate simulated commands, subagents and skills for specific tools that are included in cursor, codexcli and etc.\\n\\n ```bash\\n rulesync generate \\\\\\n --targets copilot,cursor,codexcli \\\\\\n --features commands,subagents,skills \\\\\\n --simulate-commands \\\\\\n --simulate-subagents \\\\\\n --simulate-skills\\n ```\\n\\n3. Use simulated commands, subagents and skills in your prompts.\\n - Prompt examples:\\n\\n ```txt\\n # Execute simulated commands. By the way, `s/` stands for `simulate/`.\\n s/your-command\\n\\n # Execute simulated subagents\\n Call your-subagent to achieve something.\\n\\n # Use simulated skills\\n Use the skill your-skill to achieve something.\\n ```\\n\",\n \"guide/why-rulesync\":\n \"# Why Rulesync?\\n\\n## Single Source of Truth\\n\\nAuthor rules once, generate everywhere. Rulesync turns a unified ruleset into tool-native formats so teams stop duplicating instructions across multiple AI assistants.\\n\\n## Tool Freedom Without Friction\\n\\nLet developers pick the assistant that fits their flow—Copilot, Cursor, Cline, Claude Code, and more—without rewriting team standards.\\n\\n## Clean, Auditable Outputs\\n\\nRulesync emits plain configuration files you can commit, review, and ship. If you ever uninstall Rulesync, your generated files keep working.\\n\\n## Fast Onboarding & Consistency\\n\\nNew team members get the same conventions, context, and guardrails immediately, keeping code style and quality consistent across tools.\\n\\n## Multi-Tool & Modular Workflows\\n\\nCompose rules, MCP configs, commands, and subagents for different tools or scopes (project vs. global) without fragmenting your workflow.\\n\\n## Ready for What's Next\\n\\nAI tool ecosystems evolve quickly. Rulesync helps you add, switch, or retire tools while keeping your rules intact.\\n\",\n \"reference/cli-commands\":\n '# CLI Commands\\n\\n## Quick Commands\\n\\n```bash\\n# Initialize new project (recommended: organized rules structure)\\nrulesync init\\n\\n# Import existing configurations (to .rulesync/rules/ by default)\\nrulesync import --targets claudecode --features rules,mcp,commands,subagents,skills,permissions\\n\\n# Import components from an existing plugin directory\\nrulesync import --targets claudecode-plugin --features skills,hooks --output-root ./plugins/review-tools\\n\\n# Convert configurations from one tool to other tools (skips .rulesync/)\\nrulesync convert --from cursor --to copilot,claudecode\\nrulesync convert --from cursor --to copilot,claudecode --features rules,mcp\\n\\n# Fetch configurations from a Git repository\\nrulesync fetch owner/repo\\nrulesync fetch owner/repo@v1.0.0 --features rules,commands\\nrulesync fetch https://github.com/owner/repo --conflict skip\\n\\n# Generate all features for all tools (new preferred syntax)\\nrulesync generate --targets \"*\" --features \"*\"\\n\\n# Generate specific features for specific tools\\nrulesync generate --targets copilot,cursor,cline --features rules,mcp\\nrulesync generate --targets claudecode --features rules,subagents\\n\\n# Generate components inside an existing plugin directory\\nrulesync generate --targets antigravity-plugin --features rules,mcp,skills,hooks --output-roots ./plugins/review-tools\\n\\n# Generate only rules (no MCP, permissions, commands, or subagents)\\nrulesync generate --targets \"*\" --features rules\\n\\n# Generate simulated commands and subagents\\nrulesync generate --targets copilot,cursor,codexcli --features commands,subagents --simulate-commands --simulate-subagents\\n\\n# Dry run: show changes without writing files\\nrulesync generate --dry-run --targets claudecode --features rules\\n\\n# Check if files are up to date (for CI/CD pipelines)\\nrulesync generate --check --targets \"*\" --features \"*\"\\n\\n# Generate from a shared rules directory (without cd-ing into it)\\nrulesync generate --input-root ~/.aiglobal --targets \"*\" --features rules\\n\\n# Install rules and skills from declarative sources in rulesync.jsonc\\nrulesync install\\n\\n# Add a source to rulesync.jsonc, update the lockfile, and install it\\nrulesync add anthropics/skills --skills skill-creator\\n\\n# Add a rule source without selecting skills\\nrulesync add acme/ai-standards --rules testing-guidelines\\n\\n# Force re-resolve all source refs (ignore lockfile)\\nrulesync install --update\\n\\n# Fail if lockfile is missing or out of sync (for CI); fetch missing artifacts using locked refs\\nrulesync install --frozen\\n\\n# Install then generate (typical workflow)\\nrulesync install && rulesync generate\\n\\n# Add generated files to .gitignore\\nrulesync gitignore\\n\\n# Add only specific tool entries to .gitignore\\nrulesync gitignore --targets claudecode,copilot\\n\\n# Add only specific feature entries to .gitignore\\nrulesync gitignore --targets copilot --features rules,commands\\n\\n# Diagnose the configuration files for common problems (read-only)\\nrulesync doctor\\n\\n# Diagnose and fail CI on warnings too\\nrulesync doctor --strict\\n\\n# Update rulesync to the latest version (single-binary installs)\\nrulesync update\\n\\n# Check for updates without installing\\nrulesync update --check\\n\\n# Force update even if already at latest version\\nrulesync update --force\\n```\\n\\n> **Deprecated feature:** `ignore` remains available to existing projects throughout Rulesync 14.x, but new projects should use `permissions`. Any removal will be decided separately and will not occur before a future major release.\\n\\n## Generate Command\\n\\nThe `generate` command reads source files from `.rulesync/` and writes AI tool configuration files to the output directories.\\n\\n### Options\\n\\n| Option | Description | Default |\\n| --------------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------- |\\n| `--targets, -t <tools>` | Comma-separated list of tools (e.g. `claudecode,copilot` or `*`) | From `rulesync.jsonc` |\\n| `--features, -f <features>` | Comma-separated list of features (rules, commands, subagents, skills, mcp, hooks, permissions, checks; deprecated: ignore) | From `rulesync.jsonc` |\\n| `--input-root <path>` | Path to the directory containing `.rulesync/` source files (currently `generate` only) | CWD |\\n| `--dry-run` | Show what would change without writing files | `false` |\\n| `--check` | Like `--dry-run` but exits with code 1 if files are not up to date | `false` |\\n| `--global` | Generate for global (user-scope) configuration files | `false` |\\n| `--simulate-commands` | Generate simulated commands for tools that do not support them natively | `false` |\\n| `--simulate-subagents` | Generate simulated subagents for tools that do not support them natively | `false` |\\n| `--simulate-skills` | Generate simulated skills for tools that do not support them natively | `false` |\\n| `--delete` | Delete existing generated files before writing | From `rulesync.jsonc` |\\n| `--watch, -w` | Keep running and regenerate whenever rulesync source files change | `false` |\\n\\n### Examples\\n\\n```bash\\n# Generate all features for all configured tools\\nrulesync generate\\n\\n# Generate rules for all tools\\nrulesync generate --targets \"*\" --features rules\\n\\n# Generate from a shared directory without cd-ing into it\\nrulesync generate --input-root ~/.aiglobal --targets \"*\" --features rules\\n\\n# Dry run: preview changes without writing\\nrulesync generate --dry-run --targets claudecode --features rules\\n\\n# CI check: fail if generated files are not up to date\\nrulesync generate --check --targets \"*\" --features \"*\"\\n\\n# Watch mode: regenerate on every change to the sources\\nrulesync generate --watch\\n```\\n\\n### Watch mode\\n\\n`generate --watch` runs one generation immediately and then keeps running, regenerating whenever the rulesync sources change. It is meant for iterating on rules, commands, subagents or skills without re-running the command by hand.\\n\\n- **What is watched**: the `.rulesync/` source tree (recursively) plus the configuration files next to it (`rulesync.jsonc` and `rulesync.local.jsonc`, or the file passed to `--config`). Generated output is never watched, so a regeneration cannot re-trigger the watcher.\\n- **Debouncing**: bursts of file-system events (editor save storms, `git checkout` switching many files) are coalesced into a single regeneration after a short quiet period. Changes that arrive while a generation is running trigger exactly one follow-up run.\\n- **Errors keep the watcher alive**: a failing generation (e.g. invalid frontmatter saved mid-edit) is reported and watching continues; the process does not exit.\\n- **Configuration changes**: editing the configuration file triggers a regeneration, and the new values apply to it because the configuration is re-resolved on every run. The **set of watched paths is fixed at startup**, so changing `inputRoot` (or the location of the configuration file itself) requires restarting the command. A warning is printed whenever the configuration file changes as a reminder.\\n- **Incompatible flags**: `--watch` cannot be combined with `--check`, `--dry-run` or `--json`. The first two are one-shot verification modes and `--json` emits a single result document when the command exits, which never happens while watching.\\n- **Stopping**: `Ctrl+C` (`SIGINT`) or `SIGTERM` closes the watchers and exits normally.\\n\\n### Tool home overrides win over the output root in global scope\\n\\nTwo tools read their profile location from an environment variable: Hermes Agent (`HERMES_HOME`) and Kimi Code (`KIMI_CODE_HOME`). When one of them is set, `generate --global` and `convert --global` write that tool\\'s output under it, **overriding both `outputRoots` and an explicit `--output-roots`** for that target. See [Supported Tools](./supported-tools.md) for what each profile root contains. This is deliberate — the variable names where the tool itself looks, so honoring the flag instead would produce files the tool never reads. Every other target still uses the configured output root.\\n\\nThe override must be a usable directory: an empty value is ignored (the default profile location applies), and a value that is the filesystem root or an unnormalized path is rejected with an error naming the variable.\\n\\n### Shared config files are never created empty\\n\\nSome outputs are files Rulesync merges into rather than owns, because the tool (or you) keeps unrelated settings there: `.amp/settings.json(c)`, `.antigravity/settings.json`, `.claude/settings.json`, `.claude/settings.local.json`, `.codex/config.toml`, `.devin/config.json`, `.factory/settings.json`, `.grok/config.toml`, `.vibe/config.toml`, `.vscode/settings.json`, `.zed/settings.json`, `kilo.json(c)`, `opencode.json(c)`, and `reasonix.toml`. These are deliberately **not** added to `.gitignore` by `rulesync gitignore`, so that settings you hand-author in them stay version-controlled.\\n\\nBecause they stay committable, `generate` will not **create** one of them just to hold an empty payload: if Rulesync has nothing to contribute (e.g. no permissions map to that tool), the file is left absent instead of being written as `{}`. A file that already exists is always rewritten as usual, so nothing you authored is dropped. Every other generated file is written even when empty, since for a file Rulesync owns its existence is part of the output.\\n\\n## Gitignore Command\\n\\nThe `gitignore` command adds generated AI tool configuration files to `.gitignore`. By default, it emits entries only for the tools listed in the `targets` of your `rulesync.jsonc` (controlled by the `gitignoreTargetsOnly` option, which defaults to `true`). Set `gitignoreTargetsOnly` to `false` to emit entries for all supported tools instead. You can also filter the output per-invocation with `--targets` / `--features`, which take precedence over the config.\\n\\nYou can route entries to `.gitattributes` instead by setting `gitignoreDestination` to `\"gitattributes\"` at root, tool, or tool × feature level. More specific settings take precedence.\\n\\n> **No `rulesync.jsonc` in the project?** Entries for all supported tools are emitted. `gitignoreTargetsOnly` is only applied when a config file exists, so users without a config still get useful `.gitignore` coverage.\\n\\n> **`agentsmd` entries are always included.** Even when `gitignoreTargetsOnly` is `true` and `agentsmd` is not listed in `targets`, entries for `AGENTS.md` (and related paths) are appended automatically. Because `AGENTS.md` is a de facto standard file read by many AI tools regardless of the target set, its gitignore entries are emitted unconditionally to prevent accidental commits of generated rule files. To opt out of this behavior, pass an explicit `--targets` option that omits `agentsmd`.\\n\\n### Options\\n\\n| Option | Description | Default |\\n| --------------------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------- |\\n| `--targets, -t <tools>` | Comma-separated list of tools to include (e.g., `claudecode,copilot` or `*` for all) | Derived from `targets` / `gitignoreTargetsOnly` |\\n| `--features, -f <features>` | Comma-separated list of features to include (rules, commands, subagents, skills, ignore, mcp, hooks, checks) | `*` (all) |\\n\\n### Examples\\n\\n```bash\\n# Add all entries (default)\\nrulesync gitignore\\n\\n# Add entries for Claude Code only\\nrulesync gitignore --targets claudecode\\n\\n# Add entries for multiple tools\\nrulesync gitignore --targets claudecode,copilot,cursor\\n\\n# Add only rules and commands entries for Copilot\\nrulesync gitignore --targets copilot --features rules,commands\\n```\\n\\n### Behavior\\n\\n- **Common entries** (e.g., `.rulesync/rules/.curated/`, `.rulesync/skills/.curated/`, `rulesync.local.jsonc`) are always included regardless of filters.\\n- **General entries** (e.g., memories, settings) are always included when their target is selected.\\n- When re-running, all previously generated rulesync entries are removed before writing the new filtered set.\\n\\n## Add Command\\n\\nThe `add` command can scaffold one Rulesync feature file or append one declarative source to `rulesync.jsonc`.\\n\\n### Feature scaffolding\\n\\nUse a feature keyword to create a valid, editable starter file:\\n\\n```bash\\n# Named Markdown features\\nrulesync add rule --name overview\\nrulesync add command --name review-pr.md\\nrulesync add subagent --name planner\\nrulesync add skill --name project-context\\nrulesync add check --name security\\n\\n# Singleton features\\nrulesync add mcp\\nrulesync add hooks\\nrulesync add permissions\\n\\n# Deprecated compatibility scaffold; prefer permissions\\nrulesync add ignore\\n```\\n\\nNamed features accept a name with or without the `.md` suffix. Skills use the directory layout `.rulesync/skills/<name>/SKILL.md`; the other named features create `<name>.md` in their canonical Rulesync directory. Names cannot contain path separators.\\n\\nWhen the target file exists, interactive execution asks before replacing it. Declining leaves the file unchanged. JSON, silent, and non-interactive execution fail safely; pass `--force` to overwrite explicitly. Singleton scaffolds recognize supported JSONC and legacy variants and replace the effective existing file instead of creating a shadowed canonical file.\\n\\nFeature keywords are reserved when no source-specific option is present. To add a source whose identifier is also a feature keyword, provide a source option that makes the intent explicit, such as `rulesync add skill --transport npm`.\\n\\n### Declarative sources\\n\\nFor any other source identifier, `add` appends one source to `rulesync.jsonc` and immediately runs the declarative source resolver. It preserves JSONC comments, installs selected rules into `.rulesync/rules/.curated/`, installs selected skills into `.rulesync/skills/.curated/`, and updates `rulesync.lock` or `rulesync-npm.lock.json`.\\n\\n```bash\\n# GitHub source (default transport)\\nrulesync add anthropics/skills --skills skill-creator\\n\\n# Rules only; direct .md files are selected from rules/\\nrulesync add acme/ai-standards --rules testing-guidelines,typescript-conventions\\n\\n# Rules and skills from separate paths in one source\\nrulesync add acme/ai-assets --rules \"*\" --rules-path exports/rules --skills review-pr --path exports/skills\\n\\n# Any Git remote through the git CLI\\nrulesync add https://example.com/team/skills.git --transport git --ref main --path skills\\n\\n# npm-compatible registry\\nrulesync add @acme/skill-package --transport npm --registry https://registry.npmjs.org\\n```\\n\\nThe selected configuration file must already exist. Run `rulesync init` first, or pass `--config <path>`. Adding a source whose normalized source identity is already present fails instead of silently creating duplicate lockfile entries; edit the existing entry when changing its options.\\n\\nThe operation fetches only the source being added; existing declarations are not re-fetched. Existing sources must already be locked and installed, otherwise run `rulesync install` first. The operation is transactional: if the new source fails to install, Rulesync restores the manifest, source lockfiles, curated rules, and curated skills to their pre-command state.\\n\\n| Option | Description |\\n| --------------------- | ------------------------------------------------------------------------------------------------- |\\n| `--name <name>` | Name for a rule, command, subagent, skill, or check scaffold |\\n| `--force` | Replace an existing scaffold file without prompting |\\n| `--skills <skills>` | Comma-separated skill names. `*` selects all skills. |\\n| `--rules <rules>` | Comma-separated rule names. Names may omit `.md`; `*` selects direct `.md` files under rulesPath. |\\n| `--transport <type>` | `github` (default), `git`, or experimental `npm` |\\n| `--ref <ref>` | Git ref, npm version, or npm dist-tag |\\n| `--path <path>` | Skills path within the source; defaults to `skills` |\\n| `--rules-path <path>` | Rules path within the source; defaults to `rules` |\\n| `--registry <url>` | npm-compatible registry URL |\\n| `--token-env <name>` | Environment variable containing the npm registry token |\\n| `--token <token>` | GitHub token for private repositories |\\n| `--config <path>` | Configuration file to edit (default: `rulesync.jsonc`) |\\n\\nWhen neither `--skills` nor `--rules` is provided, all skills are installed for backward compatibility. Providing only `--rules` installs no skills.\\n\\n## Fetch Command\\n\\nThe `fetch` command allows you to fetch configuration files directly from a Git repository (GitHub/GitLab).\\n\\n> [!NOTE]\\n> This feature is in development and may change in future releases.\\n\\n**Note:** The fetch command searches for feature directories (`rules/`, `commands/`, `skills/`, `subagents/`, etc.) directly at the specified path, without requiring a `.rulesync/` directory structure. This allows fetching from external repositories like `vercel-labs/agent-skills` or `anthropics/skills`.\\n\\n### Source Formats\\n\\n```bash\\n# Full URL format\\nrulesync fetch https://github.com/owner/repo\\nrulesync fetch https://github.com/owner/repo/tree/branch\\nrulesync fetch https://github.com/owner/repo/tree/branch/path/to/subdir\\nrulesync fetch https://gitlab.com/owner/repo # GitLab (planned)\\n\\n# Prefix format\\nrulesync fetch github:owner/repo\\nrulesync fetch gitlab:owner/repo # GitLab (planned)\\n\\n# Shorthand format (defaults to GitHub)\\nrulesync fetch owner/repo\\nrulesync fetch owner/repo@ref # Specify branch/tag/commit\\nrulesync fetch owner/repo:path # Specify subdirectory\\nrulesync fetch owner/repo@ref:path # Both ref and path\\n```\\n\\n### Options\\n\\n| Option | Description | Default |\\n| ----------------------- | --------------------------------------------------------------------------------------------------------------- | -------------------------------- |\\n| `--target, -t <target>` | Target format to interpret files as (e.g., \\'rulesync\\', \\'claudecode\\') | `rulesync` |\\n| `--features <features>` | Comma-separated features to fetch (rules, commands, subagents, skills, ignore, mcp, hooks, permissions, checks) | `skills` |\\n| `--output <dir>` | Output directory relative to project root | `.rulesync` |\\n| `--conflict <strategy>` | Conflict resolution: `overwrite` or `skip` | `overwrite` |\\n| `--ref <ref>` | Git ref (branch/tag/commit) to fetch from | Default branch |\\n| `--path <path>` | Subdirectory in the repository | `.` (root) |\\n| `--token <token>` | Git provider token for private repositories | `GITHUB_TOKEN` or `GH_TOKEN` env |\\n\\n### Examples\\n\\n```bash\\n# Fetch skills from external repositories\\nrulesync fetch vercel-labs/agent-skills\\nrulesync fetch anthropics/skills\\n\\n# Fetch all features from a public repository\\nrulesync fetch dyoshikawa/rulesync --path .rulesync --features \"*\"\\n\\n# Fetch only rules and commands from a specific tag\\nrulesync fetch owner/repo@v1.0.0 --features rules,commands\\n\\n# Fetch from a private repository (uses GITHUB_TOKEN env var)\\nexport GITHUB_TOKEN=ghp_xxxx\\nrulesync fetch owner/private-repo\\n\\n# Or use GitHub CLI to get the token\\nGITHUB_TOKEN=$(gh auth token) rulesync fetch owner/private-repo\\n\\n# Preserve existing files (skip conflicts)\\nrulesync fetch owner/repo --conflict skip\\n\\n# Fetch from a monorepo subdirectory\\nrulesync fetch owner/repo:packages/my-package\\n```\\n\\n## Convert Command\\n\\nThe `convert` command converts configuration files from one AI tool directly to one or more destination tools **without creating `.rulesync/` files on disk**. The intermediate rulesync representation is kept in memory only.\\n\\nThis is useful when you want to translate a one-shot tool-to-tool conversion (e.g., \"I have Cursor rules, give me Claude Code and Copilot equivalents\") without adopting rulesync\\'s managed source-of-truth workflow.\\n\\n### Options\\n\\n| Option | Description | Default |\\n| --------------------------- | ------------------------------------------------------------------------------------------------------------------------- | --------- |\\n| `--from <tool>` | Source tool to convert from (single tool, e.g., `cursor`, `claudecode`) | Required |\\n| `--to <tools>` | Comma-separated list of destination tools (e.g., `copilot,claudecode`) | Required |\\n| `--features, -f <features>` | Comma-separated list of features to convert (rules, commands, subagents, skills, ignore, mcp, hooks, permissions, checks) | `*` (all) |\\n| `--verbose, -V` | Verbose output | `false` |\\n| `--silent, -s` | Suppress all output | `false` |\\n| `--global, -g` | Convert for global (user scope) configuration files | `false` |\\n| `--dry-run` | Show changes without writing files | `false` |\\n\\n### Examples\\n\\n```bash\\n# Convert Cursor rules to Copilot and Claude Code\\nrulesync convert --from cursor --to copilot,claudecode --features rules\\n\\n# Convert all features Cursor and Copilot both support\\nrulesync convert --from cursor --to copilot\\n\\n# Convert MCP configuration from Claude Code to Cursor\\nrulesync convert --from claudecode --to cursor --features mcp\\n\\n# Dry run to preview the conversion\\nrulesync convert --from cursor --to copilot,claudecode --dry-run\\n```\\n\\n### Behavior\\n\\n- The intermediate rulesync files produced during conversion are **never** written to disk. Only destination tool files are written.\\n- Features that exist for the source tool but are not supported by a given destination tool are skipped with a warning.\\n- When `--features` is omitted, the command attempts every feature the source tool supports.\\n- Passing the source tool inside `--to` is rejected, because converting a tool onto itself is lossy.\\n- With `--dry-run`, no destination files are written; the command prints a summary prefixed with `[DRY RUN]` listing what would have been converted.\\n\\n## Doctor Command\\n\\nThe `doctor` command runs read-only diagnostics against the configuration files (`rulesync.jsonc` and `rulesync.local.jsonc`) and reports problems grouped by severity (`error` / `warning` / `info`). It never writes files, which makes it a safe first step when generation does not behave as expected, and a cheap CI guard.\\n\\nIt is especially useful for catching **silently ignored configuration**: the config schema is non-strict, so a misspelled key such as `\"target\"` instead of `\"targets\"` is normally swallowed without any error. `doctor` reports every unknown key with a \"did you mean\" suggestion.\\n\\n### Checks\\n\\n- JSONC parse errors, reported with line and column.\\n- Unknown or misspelled top-level keys, with a \"did you mean\" suggestion.\\n- Unknown tool targets and features (array and object forms), with the nearest valid name suggested.\\n- Deprecated features (`ignore`, superseded by `permissions`).\\n- Object-form `targets` combined with `features` — including the case where the conflict only appears after merging `rulesync.jsonc` with `rulesync.local.jsonc`.\\n- Conflicting target pairs (e.g. `claudecode` + `claudecode-legacy`).\\n- `$schema` presence and whether it points at the current config schema URL.\\n- Structural schema violations on any other key (wrong types, malformed `sources` entries).\\n- `sources[].tokenEnv` naming an environment variable that is not set.\\n- `inputRoot` pointing at a directory that does not exist.\\n\\n### Options\\n\\n| Option | Description | Default |\\n| --------------------- | --------------------------------- | ---------------- |\\n| `--config, -c <path>` | Path to configuration file | `rulesync.jsonc` |\\n| `--strict` | Treat warnings as errors (exit 1) | `false` |\\n| `--verbose, -V` | Verbose output | `false` |\\n| `--silent, -s` | Suppress all output | `false` |\\n\\n### Examples\\n\\n```bash\\n# Diagnose the project configuration\\nrulesync doctor\\n\\n# Fail CI on warnings too\\nrulesync doctor --strict\\n\\n# Machine-readable output for editors and CI\\nrulesync --json doctor\\n\\n# Diagnose a configuration file at a custom location\\nrulesync doctor --config ./configs/rulesync.jsonc\\n```\\n\\n### Behavior\\n\\n- Exits with code `1` when any `error`-severity diagnostic is present (or any `warning` with `--strict`), and `0` otherwise.\\n- With the global `--json` flag, diagnostics and a severity summary are emitted as structured JSON: in `data` on success (exit 0), and in `error.details` of the standard error document (code `DOCTOR_FAILED`) on failure.\\n- A missing configuration file is reported as `info` only — rulesync runs fine with built-in defaults.\\n\\n## Docs Command\\n\\nThe `docs` command prints the bundled Rulesync documentation to standard output, so both humans and coding agents can retrieve it directly in the terminal without browsing the repository or website. The documentation is embedded in the CLI at build time, so it works in installed npm distributions and compiled binaries alike.\\n\\nDocument identifiers follow the `docs/` hierarchy without the `docs/` prefix or the `.md` extension (both are accepted and stripped when supplied). Identifiers that try to escape the bundled tree — absolute paths, drive letters, `..` segments — are rejected.\\n\\n### Usage\\n\\n```bash\\n# List every available document identifier\\nrulesync docs\\n\\n# Print a document (top-level or nested)\\nrulesync docs faq\\nrulesync docs guide/configuration\\n\\n# Ranked full-text search across the bundled documentation\\nrulesync docs --search \"global mode\"\\n```\\n\\n### Search\\n\\n`--search <text>` builds an in-memory BM25+ index (via MiniSearch) over document paths, titles, headings, and body content, with stronger boosts for titles and headings. Up to 10 results are printed, one per line, as `<document> — <matching context>`. Matching is exact-term; no prefix or fuzzy expansion is applied.\\n\\n### Behavior\\n\\n- `rulesync docs` with no argument lists all document identifiers, one per line, sorted.\\n- A missing document, an invalid identifier, an empty search text, a search with no matches, or combining a document argument with `--search` each exit with code 1 and an explanatory error.\\n- Document output is printed verbatim, so it can be piped to other tools.\\n- The global `--json` flag is not supported (the command\\'s output is raw Markdown, not a JSON document) and exits with code 1.\\n',\n \"reference/command-syntax\":\n '# Command Syntax\\n\\nSlash commands authored under `.rulesync/commands/*.md` use a **universal syntax** that mirrors Claude Code\\'s command placeholders. When rulesync generates a tool-specific command file, it rewrites these placeholders into the syntax that the target tool understands. The reverse rewrite happens on import, so a rulesync ↔ tool round-trip preserves the original universal form.\\n\\n## Universal placeholders\\n\\n| Placeholder | Meaning |\\n| ------------ | ------------------------------------------------------------------------ |\\n| `$ARGUMENTS` | The full argument string the user supplied when invoking the command. |\\n| `` !`cmd` `` | Inline shell expansion. The agent runs `cmd` and substitutes its output. |\\n\\nThese are written exactly as Claude Code accepts them, so writing a rulesync command body is the same as writing a Claude Code command body.\\n\\n## Per-tool translation\\n\\nThe table below shows how each placeholder is translated for the supported tools. \"pass-through\" means the placeholder is emitted verbatim because the target tool already understands the universal form.\\n\\n| Tool | `$ARGUMENTS` | `` !`cmd` `` |\\n| ----------------- | ---------------------- | --------------------------- |\\n| Claude Code | pass-through | pass-through |\\n| Codex CLI[^codex] | pass-through (literal) | pass-through (literal) |\\n| Pi | pass-through | pass-through (literal)[^pi] |\\n| Other tools[^1] | pass-through (literal) | pass-through (literal) |\\n\\n[^1]: Tools not listed do not have a documented translation; their command body is emitted as-is.\\n\\n[^codex]: Codex CLI prompt files are forwarded to the LLM verbatim; the placeholders are passed to the model as literal text rather than being substituted by the engine.\\n\\n[^pi]: Pi natively expands `$ARGUMENTS` (along with `$1`, `$2`, `$@`), so `$ARGUMENTS` is a real pass-through there. rulesync still emits `` !`cmd` `` verbatim for Pi, but does not assume Pi expands inline shell snippets — treat that placeholder as literal text on Pi\\'s side.\\n\\nThe translation also runs in reverse when you import an existing tool command file via `rulesync import`, so a tool-native placeholder is rewritten back to the universal form in the generated `.rulesync/commands/*.md`.\\n\\n## Example\\n\\nGiven the following rulesync command:\\n\\n```md\\n---\\ntargets: [\"claudecode\"]\\ndescription: \"Summarize git diff\"\\n---\\n\\nSummarize the diff:\\n!`git diff`\\n\\nFocus on $ARGUMENTS.\\n```\\n\\nrulesync generates `.claude/commands/summarize.md`, passing the placeholders through verbatim because Claude Code already understands the universal form.\\n\\n## Notes\\n\\n- If you author a command with explicit tool-specific syntax (e.g. you write a tool-native placeholder directly in a rulesync command body), rulesync does **not** re-translate the already-tool-native form. Stick to the universal placeholders to keep commands portable across tools.\\n- The translation is purely textual and is applied to the entire body. It does not skip fenced or inline code blocks, so ` ```js\\\\n$ARGUMENTS\\\\n``` ` in a rulesync body will still be rewritten when generating tool output. There is **no escape syntax** for the universal placeholders — backslashes are not consumed by the regex, so `\\\\$ARGUMENTS` is rewritten alongside the placeholder rather than producing a literal `$ARGUMENTS`.\\n- The shell expansion regex matches a single backtick-delimited segment without embedded backticks or newlines (`` !`...` ``). Multi-line shell snippets are not supported, and a backtick inside the command body is not allowed.\\n',\n \"reference/file-formats\":\n '# File Formats\\n\\n## Symlinks\\n\\nRulesync follows symbolic links when it discovers source files, whether you use a plain `.rulesync/` directory or a separate `--input-root`. Glob-based discovery (rules, commands, subagents, skills) follows symlinked files and directories; single fixed-path files such as `.rulesyncignore`, `.rulesync/mcp.jsonc`, and `.rulesync/permissions.jsonc` are likewise resolved transparently by the OS when read. A symlink inside the input tree that points elsewhere is followed transparently, and the resolved file content is copied into the generated output. This is intentional: it lets you centralize shared skills or rules in one place and reference them via symlinks without duplication (see [issue #1707](https://github.com/dyoshikawa/rulesync/issues/1707)).\\n\\nThe trust boundary is the directory you point Rulesync at. There is **no** `realpath`-based containment check on individual symlinks, so a link may resolve to a target outside the input root — enforcing containment would break the shared-file use case above. Only run Rulesync against trees you control. Directory symlink **cycles** are handled safely: results are deduplicated by real path, so a cycle does not produce duplicated output. Note that the remote-fetch path (`rulesync fetch` from a Git repository) is a separate, hardened code path that **skips** symlinks entirely, so untrusted remote content never has its symlinks followed.\\n\\nOne discovery pass is deliberately excluded from the follow-symlinks rule: the scan for nested `AGENTS.md` files (see the `agentsmd` note below). Unlike every other glob above, it walks the whole project rather than a rulesync-owned directory, so a symlink committed to a repository you cloned could otherwise pull a file from outside the project into version-controlled `.rulesync/`. That scan does not follow symlinks.\\n\\n## `rulesync/rules/*.md`\\n\\nExample:\\n\\n```md\\n---\\nroot: true # true for root-level rules, false for details such as `.agents/memories/*.md`\\nlocalRoot: false # (optional, default: false) true for project-specific local rules. Claude Code: CLAUDE.local.md; Rovodev (Rovo Dev CLI) and Roo Code: AGENTS.local.md; Others: append to root file\\ntargets: [\"*\"] # * = all, or specific tools\\ndescription: \"Rulesync project overview and development guidelines for unified AI rules management CLI tool\"\\nglobs: [\"**/*\"] # file patterns to match (e.g., [\"*.md\", \"*.txt\"])\\nagentsmd: # agentsmd and codexcli specific parameters\\n # Support for using nested AGENTS.md files for subprojects in a large monorepo.\\n # This option is available only if root is false.\\n # If subprojectPath is provided, the file is located in `${subprojectPath}/AGENTS.md`.\\n # If subprojectPath is not provided and root is false, the file is located in `.agents/memories/*.md`.\\n subprojectPath: \"path/to/subproject\"\\ncursor: # cursor specific parameters\\n alwaysApply: true\\n description: \"Rulesync project overview and development guidelines for unified AI rules management CLI tool\"\\n globs: [\"*\"]\\ncopilot: # copilot specific parameters (non-root `*.instructions.md` files only)\\n name: \"TypeScript Style\" # (optional) display name shown in the VS Code UI; defaults to the file name\\n excludeAgent: \"code-review\" # (optional) \"code-review\" or \"cloud-agent\": skip this file for that agent\\nantigravity: # antigravity specific parameters\\n trigger: \"always_on\" # always_on, glob, manual, or model_decision\\n globs: [\"**/*\"] # (optional) file patterns to match when trigger is \"glob\"\\n description: \"When to apply this rule\" # (optional) used with \"model_decision\" trigger\\ndevin: # devin (Devin Desktop, formerly Windsurf) specific parameters\\n trigger: \"always_on\" # always_on, glob, manual, or model_decision\\n globs: [\"**/*\"] # (optional) file patterns to match when trigger is \"glob\"\\n description: \"When to apply this rule\" # (optional) used with \"model_decision\" trigger\\naugmentcode: # augmentcode specific parameters\\n type: \"always_apply\" # always_apply, manual, or agent_requested\\n description: \"When to apply this rule\" # (optional) used with \"agent_requested\" type\\nkiro: # kiro specific parameters (steering inclusion)\\n inclusion: \"fileMatch\" # always, fileMatch, manual, or auto\\n fileMatchPattern: [\"src/components/**/*.tsx\"] # (optional) glob string or array of globs, used when inclusion is \"fileMatch\"\\n name: \"api-design\" # (optional) required when inclusion is \"auto\"; the steering entry key\\n description: \"REST API design patterns. Use when creating or modifying API endpoints.\" # (optional) required when inclusion is \"auto\"; Kiro auto-includes the file when a request matches this\\ntakt: # takt specific parameters (optional; emitted under .takt/facets/policies/ — frontmatter is dropped on emit)\\n name: \"renamed-stem\" # (optional) override the emitted filename stem (no path separators or \"..\")\\n extends: \"base\" # (optional) emit a leading `{extends:<parent>}` facet-inheritance directive (Takt 0.39.0+)\\n facet: \"output-contracts\" # (optional) \"policies\" (default) or \"output-contracts\": redirect this rule to Takt\\'s output-structure/report-template facet\\n---\\n\\n# Rulesync Project Overview\\n\\nThis is Rulesync, a Node.js CLI tool that automatically generates configuration files for various AI development tools from unified AI rule files. The project enables teams to maintain consistent AI coding assistant rules across multiple tools.\\n\\n...\\n```\\n\\nMultiple files can set `root: true` for the same target in project and global modes. Rulesync renders each file through the target adapter, then combines compatible root or plain-Markdown single-file outputs in deterministic source-discovery order with one blank line between fragments. Local rules are ordered lexicographically by source file path and composed before non-overridden `.curated/` rules, which are also ordered lexicographically; filename prefixes such as `10-` and `20-` control composition order within each set. Targets that map source rules to distinct native paths keep those files separate. Explicitly supported plain-Markdown modular rules that normalize to the same output path are combined; a fragment whose generated output carries its own frontmatter block (such as Amp\\'s `globs:` gate) is never composed and stays a separate file instead. Unsafe collisions involving a source `root: true` rule fail. Other exact or case-insensitive modular collisions remain separate and produce a warning that the last write wins wherever the filesystem treats their paths as the same.\\n\\n> **AGENTS.md standard note (`agentsmd`):** Nested `AGENTS.md` files are the standard\\'s only scoping mechanism — agents read the nearest file in the directory tree, so the closest one wins. Rulesync writes them from `agentsmd.subprojectPath` and, on **import**, discovers them by scanning the project for `**/AGENTS.md`. Hidden directories (other tools\\' generated output, including rulesync\\'s own) are skipped at any depth, as are `node_modules/` and `__pycache__/`. Build, vendoring and scratch directories (`vendor/`, `third_party/`, `dist/`, `build/`, `out/`, `target/`, `coverage/`, `tmp/`, `temp/`, `venv/`) are skipped **at the project root only**, because a top-level `build/` is a build directory while `packages/build/` is a real subproject. Beyond those names, the scan honors your `.gitignore`: a file git does not track is not your project\\'s source, and copying a vendored dependency\\'s rule file into version-controlled `.rulesync/rules/` would hand third-party instructions to every tool — including the ones that concatenate non-root rules into a single always-loaded file. (Ignore rules come from the `.gitignore` files at and below the output root — a parent repository\\'s rules are not consulted, so running against a subdirectory only sees that subdirectory\\'s own. The test is applied to the _directories_ above each file, not the file itself, so the `**/AGENTS.md` entry that `rulesync gitignore` writes for its own output does not disable the scan; the flip side is that ignoring one individual `AGENTS.md` no longer keeps it out of the import.) Symbolic links are not followed, so a link committed to a repository cannot pull a file from outside the project into version-controlled `.rulesync/`. The scan is import-only: a nested file rulesync did not write is never removed by `--delete`. That means deleting the rulesync rule stops the reference from being listed in the root `AGENTS.md`, but leaves the subproject file itself on disk — where agents still read it, since the nearest file wins. Remove it by hand. Each discovered file is imported to `.rulesync/rules/<directory-with-hyphens>.md` (e.g. `packages/api/AGENTS.md` → `packages-api.md`) carrying `agentsmd.subprojectPath`, so the next generate puts it back where it came from. A subproject that would claim the reserved `overview.md` name gets an `-agents` suffix instead, so the root rule is never overwritten; any other pair of sources deriving the same name is reported at import time, since only the last one survives. Import always rewrites `.rulesync/rules/`, so a subproject whose derived name matches a rule file you wrote by hand replaces it — pick distinct names, or keep hand-written rules out of the derived namespace. See <https://agents.md/>.\\n\\n> **Kiro note:** Kiro reads steering files from `.kiro/steering/*.md` and uses an `inclusion` frontmatter block to decide when each is loaded (`always`, `fileMatch` with a `fileMatchPattern`, `manual`, or `auto` — which auto-includes the file when a request matches its companion `description`, keyed by `name`). Rulesync derives this for non-root steering files: an explicit `kiro.inclusion` block round-trips as-is (carrying `name`/`description` through for `auto`); otherwise specific (non-wildcard) `globs` map to `inclusion: fileMatch` (a single glob is written as a string and multiple as a YAML array, both of which Kiro accepts), so the rule applies only to matching files instead of always; otherwise the file stays always-on and is written without a frontmatter block (Kiro\\'s no-frontmatter default). The root overview index is always written plain so Kiro always loads it. In **global** mode (`--global`), steering is written to `~/.kiro/steering/` with the root rule as `~/.kiro/steering/product.md` (Kiro does not read `~/AGENTS.md`, so the project-scope root `AGENTS.md` is not used at the home level), and global MCP is written to `~/.kiro/settings/mcp.json`.\\n\\n> **Grok CLI note:** Grok Build writes the root rule to the auto-loaded `AGENTS.md` (project) / `~/.grok/AGENTS.md` (global, via `--global`), and non-root rules to `.grok/rules/*.md` (project) / `~/.grok/rules/*.md` (global). Grok scans that directory flat and in name order, alongside the AGENTS.md family — earlier Rulesync versions folded every topic rule into the single root file, which matched Grok 0.2.54 but not the current release, so regenerate to split them back out. Non-root files carry no frontmatter. Because this is a directory Grok defines rather than one Rulesync invented, a project may already have hand-written files there: Rulesync owns it from now on, so `--delete` removes anything in it — `~/.grok/rules/` included, in global mode — that `.rulesync/rules/` does not produce. Move those files into `.rulesync/rules/` first.\\n\\n> **Kilo Code note:** Kilo writes the root rule to the auto-loaded `AGENTS.md` and non-root rules to `.kilo/rules/*.md`. Because Kilo v7 does not auto-load files under `.kilo/rules/`, Rulesync also registers each generated non-root rule file in the `instructions` array of the shared `kilo.jsonc` (the root `AGENTS.md` is auto-loaded and is therefore not registered). This merge is non-destructive: existing keys such as `mcp`, `tools`, and `permission` are preserved, and the `instructions` list is deduped and sorted.\\n\\n> In global mode (`--global`), Kilo\\'s own layout is asymmetric: the root rule goes to `~/.config/kilo/AGENTS.md`, while non-root rules go to `~/.kilo/rules/*.md` — the same `.kilo`-relative path the skills adapter uses in both scopes. Global rules need no `instructions` registration, because Kilo auto-discovers every `~/.kilo/rules/*.md` on config load; writing the files is enough, and no global `kilo.jsonc` is touched by the rules feature.\\n\\n> **Kimi Code note:** Kimi Code reads `.kimi-code/AGENTS.md` at project scope and `~/.kimi-code/AGENTS.md` at user scope. When `KIMI_CODE_HOME` is set, Rulesync follows Kimi and resolves every global Kimi-specific file (`AGENTS.md`, `mcp.json`, `config.toml`, `skills/`, and `agents/`) under that custom data root; the shared `~/.agents/skills/` and `~/.agents/agents/` discovery roots remain under the user\\'s real home directory. Because Kimi has no dedicated directory for topic-based instruction files, Rulesync folds every non-root rule body into that single file. See the [Kimi Code agents and instruction-files docs](https://moonshotai.github.io/kimi-code/en/customization/agents.html) and [environment-variable docs](https://moonshotai.github.io/kimi-code/en/configuration/env-vars.html).\\n\\n> **OpenCode note:** OpenCode writes the root rule to the auto-loaded `AGENTS.md` and non-root rules to `.opencode/memories/*.md`. Because OpenCode auto-loads only the root `AGENTS.md` plus files explicitly listed in the `instructions` array of `opencode.json` (it does not auto-discover a rules directory), Rulesync also registers each generated non-root rule file in the `instructions` array of the shared `opencode.json`/`opencode.jsonc` (the root `AGENTS.md` is auto-loaded and is therefore not registered). This merge is non-destructive: existing keys such as `mcp`, `tools`, and `permission` are preserved, and the `instructions` list is deduped and sorted.\\n\\n> **Qwen Code note:** Qwen Code writes the root rule to the auto-loaded `QWEN.md` (project) / `~/.qwen/QWEN.md` (global, via `--global`) as plain Markdown, and non-root rules to its path-based context-rule directory `.qwen/rules/` (project) / `~/.qwen/rules/` (global). Each non-root rule is a Markdown file with optional YAML frontmatter: Rulesync maps `globs` ⇄ Qwen\\'s `paths` (a picomatch glob array) and `description` ⇄ `description`. A rule **with** specific `paths` is _conditional_ — Qwen lazily injects it only when the model touches a matching file — while a rule **without** `paths` (empty or wildcard `**/*`/`*` globs) is a _baseline_ rule loaded at session start and is written as plain Markdown with no frontmatter block. The `.qwen/rules/` directory supersedes the legacy `.qwen/memories/` import surface, so each rule is emitted to exactly one location; the root `QWEN.md` is unchanged. A `localRoot: true` rule is emitted to `.qwen/QWEN.local.md` (project scope only) — Qwen Code v0.16.2\\'s personal project context file, loaded after the shared `QWEN.md` so it can override team instructions; the file is covered by the derived `.gitignore` since Qwen Code does not gitignore it for you. See the [Qwen Code memory/context docs](https://github.com/QwenLM/qwen-code).\\n\\n> **Cline note:** Cline writes the root rule to the auto-loaded `AGENTS.md` (project) as plain Markdown, and non-root rules to its flat `.clinerules/` directory. Each non-root rule is a Markdown file with optional YAML frontmatter for conditional activation: Rulesync maps `globs` ⇄ Cline\\'s `paths` (a glob array; the rule loads only when a matching file is in context) and `description` ⇄ `description`. A rule with **specific** `globs` emits `paths`; a rule with **universal** globs (`**/*` or `*`) emits `alwaysApply: true` (always load); a rule **without** globs is written as plain Markdown with no frontmatter block (always active). In global mode (via `--global`), the root rule is written to the cross-tool `~/.agents/AGENTS.md` (Cline CLI v3.0.15+) as plain Markdown, and non-root rules go to `~/Documents/Cline/Rules/*.md` — the global modular-rules directory both the VS Code extension and the SDK/CLI read — with the same conditional-frontmatter conversion project rules get. See the [Cline rules docs](https://docs.cline.bot/customization/cline-rules).\\n\\n> **Warp note (rules):** Warp reads project rules from the root `AGENTS.md` (or the back-compat `WARP.md`) and does not scan a modular rules directory, so non-root rule bodies are folded into the single root `./AGENTS.md`. In global mode (via `--global`), the root rule is written to the cross-tool `~/.agents/AGENTS.md` — Warp\\'s third rule source alongside project and Warp Drive rules, also used from remote hosts in SSH sessions — with the same folding. Other targets (e.g. Cline) own the same global path; as with the shared project-root `AGENTS.md`, each target regenerates the file per its own semantics. See the [Warp rules docs](https://docs.warp.dev/agent-platform/capabilities/rules/) and [file locations](https://docs.warp.dev/terminal/settings/file-locations/).\\n\\n> **Pi note:** Pi writes the root rule to the auto-loaded `AGENTS.md` (project) / `~/.pi/agent/AGENTS.md` (global, via `--global`) as plain Markdown, and folds non-root rules into that single file (Pi has no modular rules directory). Pi additionally loads two system-prompt instruction files. `.pi/APPEND_SYSTEM.md` (project) / `~/.pi/agent/APPEND_SYSTEM.md` (global) **appends** to the default system prompt, and Rulesync emits it from any rule that opts in via a `pi.systemPrompt: append` frontmatter block — those rule bodies are routed to `APPEND_SYSTEM.md` instead of `AGENTS.md`, multiple opted-in rules concatenate in source order, and the file is managed by generate/import/delete like the root file (note: if you hand-authored `.pi/APPEND_SYSTEM.md` before this feature existed, `generate --delete` for the `pi` target now treats it as a managed path and removes it unless a rule opts in — import it first to convert it into a canonical rule). The opt-in is ignored on the `root: true` rule, which always stays on `AGENTS.md` (routing the root away would leave the context file without a merge target). `.pi/SYSTEM.md` (project) / `~/.pi/agent/SYSTEM.md` (global) **replaces** the default system prompt entirely — which silently disables Pi\\'s built-in tool instructions — so Rulesync deliberately never emits it and leaves it to be authored by hand. Example:\\n>\\n> ```yaml\\n> ---\\n> targets: [\"pi\"]\\n> description: \"House style for the system prompt\"\\n> pi:\\n> systemPrompt: append # routes this rule\\'s body to .pi/APPEND_SYSTEM.md / ~/.pi/agent/APPEND_SYSTEM.md\\n> ---\\n> ```\\n>\\n> See the [Pi usage docs](https://pi.dev/docs/latest/usage).\\n\\n> **Devin note:** The root rule is emitted to the project-root `AGENTS.md` — the file [Devin CLI / Devin Local actually reads](https://docs.devin.ai/cli/extensibility/rules) (its rules page does not list `.devin/rules/` among its sources) — as plain markdown, while non-root rules keep going to `.devin/rules/*.md`, the Devin Desktop Cascade directory whose `trigger` activation modes (`always_on`, `glob`, `manual`, `model_decision`) are driven by the `devin` frontmatter block. Global mode is unchanged (`~/.config/devin/AGENTS.md`).\\n\\n> **Amp note:** Amp gates an @-mentioned guidance file on `globs:` YAML frontmatter — the file is loaded only after Amp has read a file matching one of the globs, and **without** the frontmatter it is always loaded. Rulesync therefore emits each non-root rule\\'s `globs` as that frontmatter on the generated `.agents/memories/*.md` file (in addition to the advisory `applyTo` value in the root file\\'s TOON table, which Amp does not enforce), and restores it into the canonical `globs` on import. Amp implicitly prefixes each glob with `**/` unless it starts with `./` or `../`, so canonical globs pass through verbatim. See [Globs in AGENTS.md](https://ampcode.com/news/globs-in-AGENTS.md).\\n\\n> **Junie note:** Junie CLI resolves project guidelines **first-match-wins** — `.junie/AGENTS.md` → root `AGENTS.md` → the legacy `.junie/guidelines.md` / `.junie/guidelines/` — and documents no file-inclusion mechanism, so Rulesync writes the root rule to `.junie/AGENTS.md` (project) / `~/.junie/AGENTS.md` (global, via `--global`) and folds non-root rules into that single file. The legacy `.junie/guidelines.md` is still accepted as an import fallback. Earlier Rulesync versions emitted non-root rules to `.junie/memories/*.md`, which is not a documented Junie read path; those files are no longer generated (stale outputs stay gitignored but are not cleaned up automatically). See the [Junie guidelines docs](https://junie.jetbrains.com/docs/guidelines-and-memory.html).\\n\\n> **Reasonix note:** Reasonix auto-injects a hierarchical instruction document, reading its vendor-specific `REASONIX.md` (alongside the cross-tool `AGENTS.md`/`CLAUDE.md`) by walking user-home → ancestors → project root/local. Rulesync writes the vendor `REASONIX.md` at the project root (project) / `~/.reasonix/REASONIX.md` (global, via `--global`) and folds non-root rules into that single file, since Reasonix has no modular rules directory. See the [Reasonix GUIDE](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/GUIDE.md).\\n\\n## `.rulesync/hooks.jsonc`\\n\\n`.rulesync/hooks.jsonc` is the recommended source path and accepts comments and trailing commas. The legacy `.rulesync/hooks.json` path remains readable for existing projects. When both files exist, the JSONC file takes precedence; write flows update the existing source instead of creating a second variant.\\n\\nHermes Agent accepts native snake-case events under `hermesagent.hooks`: `pre_tool_call`, `post_tool_call`, `transform_terminal_output`, `transform_tool_result`, `transform_llm_output`, `pre_llm_call`, `post_llm_call`, `pre_verify`, `pre_api_request`, `post_api_request`, `api_request_error`, `on_session_start`, `on_session_end`, `on_session_finalize`, `on_session_reset`, `subagent_start`, `subagent_stop`, `pre_gateway_dispatch`, `pre_approval_request`, `post_approval_response`, `kanban_task_claimed`, `kanban_task_completed`, and `kanban_task_blocked`. Rulesync maps shared canonical events first, applies canonical keys from `hermesagent.hooks` next, then applies exact native keys last. An exact native key therefore wins when both forms resolve to the same Hermes event. Native-only events remain under `hermesagent.hooks` on import instead of leaking into other targets.\\n\\nHooks run scripts at lifecycle events (e.g. session start, before tool use). Events use **canonical camelCase** in this file, and Rulesync translates them per tool: Cursor uses them as-is; Claude Code, Factory Droid, Codex CLI, Gemini CLI, and Goose get PascalCase (with a few tool-specific name mappings) in their settings files; OpenCode and Kilo hooks are emitted as JavaScript plugins (`.opencode/plugins/rulesync-hooks.js`, `.kilo/plugins/rulesync-hooks.js`) — both share one event surface, in which `preToolUse`/`postToolUse` become named `tool.execute.before`/`tool.execute.after` hooks, `preCompact` becomes the named `experimental.session.compacting` hook (which receives `(input, output)` and exposes nothing to match on, so a `matcher` on it is dropped), and the rest are `event.type` dispatches — `sessionStart` → `session.created`, `stop` → `session.idle`, `afterFileEdit` → `file.edited`, `afterShellExecution` → `command.executed`, `permissionRequest` → `permission.asked`, `postCompact` → `session.compacted`, `afterError` → `session.error`, `fileChanged` → `file.watcher.updated`; Amp hooks are emitted as a TypeScript plugin (`.amp/plugins/rulesync-hooks.ts`, or `~/.config/amp/plugins/rulesync-hooks.ts` in global mode) using `session.start`, `tool.call`, `tool.result`, `agent.start`, and `agent.end`; Pi Coding Agent hooks are emitted as a Rulesync-owned TypeScript extension (`.pi/extensions/rulesync-hooks.ts`, or `~/.pi/agent/extensions/rulesync-hooks.ts` in global mode) that subscribes to Pi\\'s snake_case extension events (`sessionStart` → `session_start`, `stop` → `agent_end`, `preToolUse` → `tool_call` with the matcher tested as a regex against the tool name, `preCompact` → `session_before_compact`, `postCompact` → `session_compact`, `postModelInvocation` → `message_end` gated on assistant messages so it runs once per finalized model response) and observes events only — command hooks run but cannot block or mutate Pi events; Copilot and Copilot CLI map event names to their own camelCase (e.g. `beforeSubmitPrompt` → `userPromptSubmitted`, `stop` → `agentStop`, `afterError` → `errorOccurred`) and use `powershell`/`bash` command fields — Copilot CLI additionally covers a wider event set and supports `prompt` and `http` hook types beyond `command`; deepagents-cli uses a dot-notation (e.g. `session.start`, `tool.error`); Kiro emits hooks into `.kiro/agents/default.json` using Kiro\\'s CLI event names (`agentSpawn`, `userPromptSubmit`, `preToolUse`, `postToolUse`, `stop`); Qwen Code emits PascalCase events into the `hooks` key of `.qwen/settings.json` (its supported event set differs from Gemini CLI\\'s).\\n\\nExample:\\n\\n```json\\n{\\n \"version\": 1,\\n \"hooks\": {\\n \"sessionStart\": [{ \"type\": \"command\", \"command\": \".rulesync/hooks/session-start.sh\" }],\\n \"preToolUse\": [{ \"matcher\": \"Bash\", \"command\": \".rulesync/hooks/confirm.sh\" }],\\n \"postToolUse\": [{ \"matcher\": \"Write|Edit\", \"command\": \".rulesync/hooks/format.sh\" }],\\n \"stop\": [{ \"command\": \".rulesync/hooks/audit.sh\" }]\\n },\\n \"cursor\": {\\n \"hooks\": {\\n \"afterFileEdit\": [{ \"command\": \".cursor/hooks/format.sh\" }]\\n }\\n },\\n \"claudecode\": {\\n \"hooks\": {\\n \"notification\": [\\n {\\n \"matcher\": \"permission_prompt\",\\n \"command\": \"$CLAUDE_PROJECT_DIR/.claude/hooks/notify.sh\"\\n }\\n ]\\n }\\n },\\n \"opencode\": {\\n \"hooks\": {\\n \"afterShellExecution\": [{ \"command\": \".rulesync/hooks/post-shell.sh\" }]\\n }\\n },\\n \"copilot\": {\\n \"hooks\": {\\n \"afterError\": [{ \"command\": \".rulesync/hooks/report-error.sh\" }]\\n }\\n }\\n}\\n```\\n\\n**Top-level keys:**\\n\\n- `version`: Schema version (currently `1`).\\n- `hooks`: Map of canonical event names to an array of hook entries. These are dispatched to every tool that supports the given event.\\n- `amp.hooks`, `cursor.hooks`, `claudecode.hooks`, `opencode.hooks`, `kilo.hooks`, `copilot.hooks`, `copilotcli.hooks`, `factorydroid.hooks`, `codexcli.hooks`, `goose.hooks`, `deepagents.hooks`, `kiro.hooks`, `kiro-ide.hooks`, `qwencode.hooks`, `grokcli.hooks`: Tool-specific **override keys**. Entries under these keys are emitted only for the corresponding tool, so tool-only events (e.g. `afterFileEdit` for Cursor/OpenCode/Kilo, `worktreeCreate` for Claude Code, `afterError` for Copilot/Copilot CLI, `PostFileSave`/`PreTaskExec` for Kiro IDE) can coexist with shared ones without leaking to other tools. `copilotcli.hooks` falls back to `copilot.hooks`, which in turn falls back to the shared `hooks` block.\\n\\n**Hook entry keys:**\\n\\n- `command` (required): Shell command to execute when the event fires.\\n- `type` (optional): One of `\"command\"` (default), `\"prompt\"`, `\"http\"`, `\"agent\"`, `\"mcp_tool\"`, or `\"function\"` — the union of the hook types accepted across supported tools. Each tool supports a subset (most support only `command`); hooks with a type a tool does not support are skipped for that tool with a warning. See notes below.\\n- `matcher` (optional): Regex used by tools that scope hooks to specific tool names (e.g. `preToolUse`, `postToolUse`, `notification`). Ignored by events that do not take a matcher (e.g. `sessionStart`, `worktreeCreate`, `worktreeRemove`).\\n- `timeout` (optional): Per-hook timeout in seconds, forwarded to tools that support it.\\n- `cacheTtl` (optional): Number of seconds to cache a successful hook result. Forwarded to Kiro CLI as `cache_ttl_seconds`; `0` disables caching and Kiro never caches `AgentSpawn` hooks.\\n- `failClosed` (optional): Boolean. When `true`, a hook failure (crash, timeout, invalid JSON) blocks the action instead of allowing it through. Passed through to Cursor\\'s `.cursor/hooks.json` and to JetBrains Junie\\'s `~/.junie/config.json` (as Junie\\'s equivalently-named `blockOnError` flag).\\n- `async` (optional): Boolean. When `true`, the hook command runs in the background without blocking. Forwarded to Qwen Code (`.qwen/settings.json`) and JetBrains Junie (`~/.junie/config.json`, same field name).\\n- `shell` (optional): Either `\"bash\"` or `\"powershell\"` — the only two interpreter values any tool accepts. Forwarded to Qwen Code and Claude Code command hooks. Like `args`, `async` and `asyncRewake`, it is documented on command hooks only, so it is not emitted on a hook of another type.\\n- `url` / `headers` / `allowedEnvVars` (optional, `http` hooks): the POST target URL, request headers (values support `$VAR` interpolation), and the env-var allowlist for that interpolation. Forwarded to Claude Code and Qwen Code http hooks.\\n- `server` / `tool` / `input` (optional, `mcp_tool` hooks): the configured MCP server name, the tool to call on it, and the (arbitrary JSON) arguments, whose string values support `${path}` substitution from the hook input. Forwarded to Claude Code mcp_tool hooks.\\n- `model` (optional, `prompt` / `agent` hooks): the model used for evaluation (defaults to a fast model). Forwarded to Claude Code prompt/agent hooks and to Qwen Code prompt hooks.\\n- `args` (optional, `command` hooks): an argument list. When present — an empty list counts, and is the form the Claude Code docs use — the tool spawns `command` directly as an executable with these arguments. There is no shell, so Rulesync writes the project-directory prefix as the braced placeholder `${CLAUDE_PROJECT_DIR}/…` that Claude Code substitutes itself, rather than the quoted shell form. Forwarded to Claude Code and AugmentCode. Only `command` is prefixed; entries of `args` are passed through exactly as written.\\n- `asyncRewake` (optional): boolean. Like `async`, but wakes Claude when the hook exits with code 2. Forwarded to Claude Code command hooks.\\n- `once` (optional): boolean. Run the hook once per session, then remove it. Forwarded to Claude Code (honored in skill frontmatter; accepted but ignored in settings files) and Qwen Code http hooks.\\n- `continueOnBlock` (optional): boolean. Feed a blocking hook\\'s rejection reason back to the model and continue the turn instead of ending it. Forwarded to Claude Code.\\n- `commandWindows` (optional): a Windows-only override for `command`, so one hook set can be cross-platform. Forwarded to Codex CLI command hooks (`.codex/hooks.json`), which is the only tool that accepts it.\\n- `statusMessage` (optional): the progress text shown while the hook runs. Forwarded to Qwen Code (command and http hooks) and to Codex CLI command hooks.\\n- `if` (optional): a single permission rule (same syntax as `settings.json` permission rules, e.g. `\"Bash(rm *)\"`) that filters a hook by tool arguments in addition to the tool name. Forwarded to Claude Code, where it is evaluated only on tool events (`preToolUse`, `postToolUse`, `postToolUseFailure`, `permissionRequest`, `permissionDenied`); it round-trips as an opaque string.\\n\\nTop-level `hooks` keys must be canonical event names; unknown event names are rejected at parse time. Tool-specific override blocks (e.g. `kiro-ide.hooks`) additionally accept tool-native event keys, which pass through verbatim.\\n\\nEvents present in the shared `hooks` block but unsupported by a given tool are skipped for that tool (a warning is logged at generate time). The canonical `notification` event maps to deepagents-cli\\'s `input.required` (human-in-the-loop interrupt).\\n\\n### Hook event × tool matrix\\n\\n| Event | Cursor | Claude Code | OpenCode | Kilo | Copilot | Copilot CLI | Factory Droid | Gemini CLI | Codex CLI | deepagents | Kiro | Antigravity IDE | Antigravity CLI | Devin | AugmentCode | Goose |\\n| ---------------------- | :----: | :---------: | :------: | :--: | :-----: | :---------: | :-----------: | :--------: | :-------: | :--------: | :--: | :-------------: | :-------------: | :---: | :---------: | :---: |\\n| `sessionStart` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | — | — | — | ✅ | ✅ |\\n| `sessionEnd` | ✅ | ✅ | — | — | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | — | — | — | ✅ | ✅ |\\n| `beforeSubmitPrompt` | ✅ | ✅ | — | — | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | — | — | ✅ | ✅ | ✅ |\\n| `preToolUse` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | — | ✅ | ✅ |\\n| `postToolUse` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | — | ✅ | ✅ |\\n| `preModelInvocation` | — | — | — | — | — | — | — | — | — | — | — | ✅ | ✅ | — | — | — |\\n| `postModelInvocation` | — | — | — | — | — | — | — | — | — | — | — | ✅ | ✅ | — | — | — |\\n| `postToolUseFailure` | ✅ | ✅ | — | — | — | ✅ | — | — | — | ✅ | — | — | — | — | — | ✅ |\\n| `stop` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | — | ✅ | ✅ |\\n| `subagentStart` | ✅ | ✅ | — | — | — | ✅ | — | — | ✅ | — | — | — | — | — | — | — |\\n| `subagentStop` | ✅ | ✅ | — | — | ✅ | ✅ | ✅ | — | ✅ | — | — | — | — | — | — | — |\\n| `preCompact` | ✅ | ✅ | ✅ | ✅ | — | ✅ | ✅ | ✅ | ✅ | ✅ | — | — | — | — | — | — |\\n| `postCompact` | — | ✅ | ✅ | ✅ | — | — | — | — | ✅ | — | — | — | — | — | — | — |\\n| `afterFileEdit` | ✅ | — | ✅ | ✅ | — | — | — | — | — | — | — | — | — | ✅ | — | ✅ |\\n| `beforeShellExecution` | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | ✅ | — | ✅ |\\n| `afterShellExecution` | ✅ | — | ✅ | ✅ | — | — | — | — | — | — | — | — | — | ✅ | — | ✅ |\\n| `beforeMCPExecution` | ✅ | — | — | — | — | ✅ | — | — | — | — | — | — | — | ✅ | — | — |\\n| `afterMCPExecution` | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | ✅ | — | — |\\n| `beforeReadFile` | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | ✅ | — | ✅ |\\n| `beforeAgentResponse` | — | — | — | — | — | — | — | ✅ | — | — | — | — | — | ✅ | — | — |\\n| `afterAgentResponse` | ✅ | — | — | — | — | — | — | ✅ | — | — | — | — | — | ✅ | — | — |\\n| `afterAgentThought` | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `beforeTabFileRead` | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | ✅ | — | — |\\n| `afterTabFileEdit` | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | ✅ | — | — |\\n| `beforeToolSelection` | — | — | — | — | — | — | — | ✅ | — | — | — | — | — | — | — | — |\\n| `permissionRequest` | — | ✅ | ✅ | ✅ | — | ✅ | — | — | ✅ | ✅ | — | — | — | — | — | — |\\n| `notification` | — | ✅ | — | — | — | ✅ | ✅ | ✅ | — | ✅ | — | — | — | — | — | — |\\n| `setup` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `worktreeCreate` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | ✅ | — | — |\\n| `worktreeRemove` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `workspaceOpen` | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `messageDisplay` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `afterError` | — | — | ✅ | ✅ | ✅ | ✅ | — | — | — | — | — | — | — | — | — | — |\\n| `instructionsLoaded` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `userPromptExpansion` | — | ✅ | — | — | — | ✅ | — | — | — | — | — | — | — | — | — | — |\\n| `postToolBatch` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `permissionDenied` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `taskCreated` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `taskCompleted` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `stopFailure` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `teammateIdle` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `configChange` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `cwdChanged` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `fileChanged` | — | ✅ | ✅ | ✅ | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `directoryAdded` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `elicitation` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n| `elicitationResult` | — | ✅ | — | — | — | — | — | — | — | — | — | — | — | — | — | — |\\n\\n> **Note:** `beforeSubmitPrompt`, `stop`, `worktreeCreate`, `worktreeRemove`, `messageDisplay`, `postToolBatch`, `taskCreated`, `taskCompleted`, `teammateIdle`, and `cwdChanged` are the Claude Code events the [matcher table](https://code.claude.com/docs/en/hooks) lists as not supporting the `matcher` field (they fire on every occurrence). A matcher authored on one of them is dropped with a warning rather than written into `settings.json` to be ignored. `directoryAdded` is treated the same way for now: the event is announced in the 2.1.219 changelog but has no row in the docs\\' event table yet, so its matcher support is unknown.\\n\\n> **Note:** Rulesync implements OpenCode hooks as a plugin at `.opencode/plugins/rulesync-hooks.js` and Kilo hooks as a plugin at `.kilo/plugins/rulesync-hooks.js`, so importing from OpenCode/Kilo to rulesync is not supported. Both only support command-type hooks (not prompt-type).\\n\\n> **Note:** Rulesync implements Amp hooks as a generated TypeScript plugin at `.amp/plugins/rulesync-hooks.ts` (project) or `~/.config/amp/plugins/rulesync-hooks.ts` (global), so importing arbitrary Amp plugin code is not supported. Amp supports command hooks for `sessionStart` → `session.start`, `preToolUse` → `tool.call`, `postToolUse` → `tool.result`, `beforeSubmitPrompt` → `agent.start`, and `stop` → `agent.end`. Tool-event matchers are regular expressions against the Amp tool name; definitions with a matcher on any lifecycle event are skipped with a warning. A failing `preToolUse` command rejects the tool call and lets the agent continue; other mapped events observe the command result.\\n\\n> **Amp command syntax:** Amp executes plugin commands with [Bun Shell](https://bun.com/docs/runtime/shell), whose syntax differs slightly from POSIX shells. Use `$VAR` for environment expansion (`${VAR}` remains literal) and `$(command)` for command substitution (backticks remain literal). Rulesync passes the authored command through unchanged so quoting and escaped operators retain their Bun Shell meaning.\\n\\n> **Note:** GitHub Copilot\\'s format uses separate `powershell` and `bash` fields for hooks. Rulesync supports only a single `command` field and resolves this by emitting the command under the `powershell` key on Windows, and under the `bash` key on all other platforms.\\n\\n> **Note:** Hook file paths per tool:\\n>\\n> - **Copilot (cloud agent / VS Code)** — project: `<project>/.github/hooks/copilot-hooks.json`; global: `~/.copilot/hooks/copilot-ide-hooks.json`. VS Code and the coding agent both document `~/.copilot/hooks` as the user scope and load every `*.json` in that folder; the Copilot CLI\\'s global file already occupies `copilot-hooks.json` there, so the VS Code target uses a distinct filename and the two never overwrite each other. Note the flip side of \"every `*.json` is loaded\": generating **both** `copilot` and `copilotcli` in global mode leaves two files in that one folder, and a reader of the folder runs the hooks from both — so a command present in your canonical config fires twice per event. Generate only one of the two globally unless you want that.\\n> - **Copilot CLI** — project: `<project>/.github/hooks/copilotcli-hooks.json`; global: `~/.copilot/hooks/copilot-hooks.json`. The Copilot CLI docs let you choose any filename inside `.github/hooks/`, so Rulesync uses the CLI-specific name to avoid colliding with the cloud-agent file when both targets are enabled. The global path is a Rulesync convention; the official Copilot CLI documentation does not currently enumerate a global hooks location, so this placement may change if the spec later mandates an alternate layout. Copilot CLI uses a **wider event surface** than the shared cloud-agent set (`sessionStart`, `sessionEnd`, `userPromptSubmitted`, `preToolUse`, `postToolUse`, `postToolUseFailure`, `agentStop` ← `stop`, `subagentStart`, `subagentStop`, `errorOccurred` ← `afterError`, `preCompact`, `permissionRequest`, `notification`, `userPromptTransformed` ← `userPromptExpansion`, `preMcpToolCall` ← `beforeMCPExecution`) and supports three hook types: **`command`** (`bash`/`powershell` with optional `timeoutSec`, plus pass-through `cwd`/`env`; on import the portable `command` field is read as the cross-platform fallback when neither shell field is present, and `timeout` is honored as an alias for `timeoutSec` when `timeoutSec` is absent. On generate the canonical `shell` selector chooses `bash` or `powershell`; without it the portable `command` field is written, so the generated file does not depend on the machine Rulesync ran on), **`prompt`** (a `prompt` string — Copilot CLI only honors prompt hooks on `sessionStart`, so prompt hooks on other events are dropped), and **`http`** (`url`/`headers`/`allowedEnvVars` with optional `timeoutSec`). An entry\\'s optional `matcher` field is emitted and round-tripped on the six events the hooks reference documents as matcher-aware — `preToolUse` and `postToolUse` (regex on the tool name), `permissionRequest` (tool name), `notification` (notification type), `preCompact` (the trigger, `manual` or `auto`) and `subagentStart` (agent name); on any other event a matcher is dropped with a warning because the CLI does not honor it there. See the [hooks reference](https://docs.github.com/en/copilot/reference/hooks-reference).\\n> - **Antigravity IDE / Antigravity CLI** — project: `<project>/.agents/hooks.json`; global: `~/.gemini/config/hooks.json`. Both targets share the same dedicated `hooks.json` (a Claude-Code-style matcher map nested under a generated `rulesync` hook name), so enabling both writes the same file.\\n> - **Devin Desktop (formerly Windsurf)** — project: `<project>/.windsurf/hooks.json`; global: `~/.codeium/windsurf/hooks.json`. The Cascade Hooks file location is unchanged by the Devin Desktop rebrand.\\n> - **AugmentCode** — project: `<project>/.augment/settings.json`; global: `~/.augment/settings.json`. Hooks are merged under the top-level `hooks` key of the shared settings file (which also holds `toolPermissions`).\\n> - **Kimi Code** — global only: `~/.kimi-code/config.toml`. Hooks are merged into the shared `[[hooks]]` array without replacing unrelated model, provider, or permission settings.\\n> - **Vibe Code** — project: `<project>/.vibe/hooks.toml`; global: `~/.vibe/hooks.toml`. Stable since v2.21.0, which removed the `enable_experimental_hooks` flag: declaring a hook is enough, so Rulesync writes nothing into `.vibe/config.toml` for hooks.\\n\\n> **Note:** Because each AI tool evolves its own hook surface at its own pace, the matrix above reflects the events Rulesync currently translates. When a tool ships a new event that Rulesync does not yet support, the most reliable path is to open an issue — the matrix is the intended baseline to compare against.\\n\\n> **Note:** Kiro hooks are emitted into `.kiro/agents/default.json` under the `hooks` field, merging with any existing agent configuration (tools, allowedTools, etc.). Both `sessionEnd` and `stop` canonical events map to Kiro CLI\\'s `stop` event. Only `command`-type hooks are supported; `prompt`-type hooks are silently skipped. Kiro CLI uses `timeout_ms` (in milliseconds) for per-hook timeouts and `cache_ttl_seconds` for successful-result caching; Rulesync maps the latter to the canonical `cacheTtl` field in both directions.\\n\\n> **Note:** Antigravity (IDE and CLI) writes a dedicated `hooks.json` keyed by a **named hook** whose value holds the event map, e.g. `{ \"rulesync\": { \"PreToolUse\": [ { \"matcher\": \"...\", \"hooks\": [...] } ], \"Stop\": [ { \"hooks\": [...] } ] } }`. Rulesync emits a single generated hook under the stable name `rulesync`. It supports five lifecycle events — `preToolUse` ⇄ `PreToolUse`, `postToolUse` ⇄ `PostToolUse`, `preModelInvocation` ⇄ `PreInvocation`, `postModelInvocation` ⇄ `PostInvocation`, and `stop` ⇄ `Stop` — where `PreInvocation`/`PostInvocation`/`Stop` are matcher-less handler lists. On import, both the named-hook wrapper and a legacy flat top-level event map are accepted, and the optional per-hook `enabled` flag is ignored.\\n\\n> **Note:** Devin Desktop (formerly Windsurf) Cascade Hooks (GA) are written to a dedicated `hooks.json` whose top-level `hooks` key maps each Cascade event name to a **flat array** of hook objects (no `matcher`, no `type`, no inner `hooks` wrapper, and no `timeout`). Each object carries `command` and/or `powershell`, plus optional `show_output` and `working_directory`. Rulesync splits the generic tool lifecycle into Devin\\'s file/command/MCP-specific events, so the canonical events map bijectively: `beforeReadFile` ⇄ `pre_read_code`, `beforeTabFileRead` ⇄ `post_read_code`, `afterTabFileEdit` ⇄ `pre_write_code`, `afterFileEdit` ⇄ `post_write_code`, `beforeShellExecution` ⇄ `pre_run_command`, `afterShellExecution` ⇄ `post_run_command`, `beforeMCPExecution` ⇄ `pre_mcp_tool_use`, `afterMCPExecution` ⇄ `post_mcp_tool_use`, `beforeSubmitPrompt` ⇄ `pre_user_prompt`, `afterAgentResponse` ⇄ `post_cascade_response`, `beforeAgentResponse` ⇄ `post_cascade_response_with_transcript`, and `worktreeCreate` ⇄ `post_setup_worktree`. Canonical events with no Devin equivalent (e.g. `sessionStart`, `stop`) are dropped with a logged warning. The Cascade Hooks file location (`.windsurf/hooks.json` / `~/.codeium/windsurf/hooks.json`) is retained from the Windsurf era and is unaffected by the rebrand.\\n\\n> **Note:** AugmentCode (Auggie CLI) hooks are merged under the top-level `hooks` key of the shared `.augment/settings.json` (project) / `~/.augment/settings.json` (global), mirroring Claude Code\\'s per-event matcher arrays (`{ \"EventName\": [ { \"matcher\": \"...\", \"hooks\": [ { \"type\": \"command\", \"command\": \"...\", \"timeout\": ... } ] } ] }`). The `hooks` block is merged in place so it coexists with the `toolPermissions` block from the permissions feature. Seven lifecycle events are supported — `preToolUse` ⇄ `PreToolUse`, `postToolUse` ⇄ `PostToolUse`, `sessionStart` ⇄ `SessionStart`, `sessionEnd` ⇄ `SessionEnd`, `stop` ⇄ `Stop`, `notification` ⇄ `Notification`, and `beforeSubmitPrompt` ⇄ `PromptSubmit` (added in Auggie 0.27.0). The `matcher` field (a case-sensitive regex, default `.*`, with `mcp:*` support) applies only to the tool events `PreToolUse`/`PostToolUse`; any matcher on the session events (including `Notification` and `PromptSubmit`) is dropped with a logged warning. Two Auggie-specific fields round-trip as well: a command hook\\'s `args` (extra argv the runner appends, authored as `args` on the canonical hook) and the matcher group\\'s `metadata` (`includeConversationData` / `includeMCPMetadata` / `includeUserContext`, which select what the runner puts in the JSON payload the script receives). `metadata` belongs to the group upstream, so it is authored on any hook of the group and re-applied to every hook of that group on import. Both matter because the `hooks` key is owned in the shared settings file: a value not written here is erased from a hand-written `settings.json` on the next generate. Commands are emitted verbatim — Auggie exposes `AUGMENT_PROJECT_DIR` as a runtime environment variable, not as an inline command substitution, so no directory prefix is added. Only `command`-type hooks are supported. On **import** (project scope), Rulesync also reads the layered overrides file `<workspace>/.augment/settings.local.json` — a gitignored, machine-specific file that Auggie merges on top of `settings.json` — and combines it over the base settings before importing, following Auggie\\'s documented layering (simple values take the local override, `mcpServers`/`plugins` replace wholesale, and other objects/lists — including the `hooks` events — are combined across tiers), so personal hook overrides are picked up without dropping base events. This overlay is **import-only and project-only**: Rulesync never writes `settings.local.json`, AugmentCode documents no global `~/.augment/settings.local.json`, so the overlay is skipped in global mode.\\n\\n> **Note:** Vibe Code (mistral-vibe) hooks are written to a dedicated `.vibe/hooks.toml` (project) / `~/.vibe/hooks.toml` (global) as a flat `[[hooks]]` TOML array. Each entry carries its own event `type`, a `command`, and optional `name`, `timeout` (seconds, default 60), and `description`. Tool-hook entries (`pre_tool` / `post_tool`) additionally carry a tool-name `match` (an fnmatch glob like `bash`/`mcp_*` or a `re:`-prefixed regex, case-insensitive — the canonical `matcher` field; `*` means \"any tool\") and an optional `strict` flag; `post_agent` carries neither. Three events are supported — `preToolUse` ⇄ `pre_tool`, `postToolUse` ⇄ `post_tool`, and `stop` ⇄ `post_agent` (fires after every assistant turn that ends without pending tool calls). Only `command`-type hooks are emitted. Vibe v2.21.0 graduated hooks from experimental: it renamed all three types (`before_tool` → `pre_tool`, `after_tool` → `post_tool`, `post_agent_turn` → `post_agent`) and removed the `enable_experimental_hooks` flag, so declaring a hook is enough and Rulesync no longer writes an auxiliary `.vibe/config.toml`. `HookType` is a strict enum upstream, so an entry using an old name is rejected outright.\\n\\n> **Note:** Goose hooks follow the Open Plugins spec: Rulesync writes a plugin directory `hooks/hooks.json` that Goose auto-discovers at startup. Locations are `<project>/.agents/plugins/rulesync/hooks/hooks.json` (project) and `~/.agents/plugins/rulesync/hooks/hooks.json` (global). The JSON shape matches Claude Code\\'s (`{ \"hooks\": { \"EventName\": [ { \"matcher\": \"...\", \"hooks\": [ { \"type\": \"command\", \"command\": \"...\" } ] } ] } }`). Eleven lifecycle events are supported — `sessionStart` ⇄ `SessionStart`, `sessionEnd` ⇄ `SessionEnd`, `stop` ⇄ `Stop`, `beforeSubmitPrompt` ⇄ `UserPromptSubmit`, `preToolUse` ⇄ `PreToolUse`, `postToolUse` ⇄ `PostToolUse`, `postToolUseFailure` ⇄ `PostToolUseFailure`, `beforeReadFile` ⇄ `BeforeReadFile`, `afterFileEdit` ⇄ `AfterFileEdit`, `beforeShellExecution` ⇄ `BeforeShellExecution`, and `afterShellExecution` ⇄ `AfterShellExecution` — matching Goose\\'s `HookEvent` enum exactly (it has no `SubagentStart`/`SubagentStop`). The `matcher` regex is preserved, commands are emitted verbatim (Goose exposes `PLUGIN_ROOT` as a runtime environment variable), and only `command`-type hooks are supported.\\n\\n> **Note:** Qwen Code hooks are written under the top-level `hooks` key of `.qwen/settings.json` (project) / `~/.qwen/settings.json` (global), using Claude-style PascalCase per-matcher arrays (`{ \"EventName\": [ { \"matcher\": \"...\", \"sequential\": false, \"hooks\": [ { \"type\": \"command\", \"command\": \"...\", \"timeout\": ... } ] } ] }`). Qwen\\'s supported event set **differs from Gemini CLI\\'s**, so rulesync defines a Qwen-specific mapping. Twenty-one lifecycle events are supported — `sessionStart` ⇄ `SessionStart`, `sessionEnd` ⇄ `SessionEnd`, `preToolUse` ⇄ `PreToolUse`, `postToolUse` ⇄ `PostToolUse`, `postToolUseFailure` ⇄ `PostToolUseFailure`, `postToolBatch` ⇄ `PostToolBatch`, `beforeSubmitPrompt` ⇄ `UserPromptSubmit`, `userPromptExpansion` ⇄ `UserPromptExpansion`, `stop` ⇄ `Stop`, `stopFailure` ⇄ `StopFailure`, `subagentStart` ⇄ `SubagentStart`, `subagentStop` ⇄ `SubagentStop`, `preCompact` ⇄ `PreCompact`, `postCompact` ⇄ `PostCompact`, `permissionRequest` ⇄ `PermissionRequest`, `permissionDenied` ⇄ `PermissionDenied`, `notification` ⇄ `Notification`, `instructionsLoaded` ⇄ `InstructionsLoaded`, `todoCreated` ⇄ `TodoCreated`, `todoCompleted` ⇄ `TodoCompleted`, and `messageDisplay` ⇄ `MessageDisplay` (fires repeatedly as the reply streams; added in Qwen Code v0.19.10). Commands are emitted verbatim (no `$GEMINI_PROJECT_DIR` rewriting). Qwen\\'s four hook types are supported: `command`, `prompt` (which carries the required `prompt` body — with `$ARGUMENTS` interpolation — and an optional `model` override, both round-tripped; a prompt hook without a `prompt` is warned about at generate time since Qwen Code loads it and fails it at runtime), `http` (which carries a `url` and POSTs JSON to it; the type and URL round-trip), and `function`. Per-hook fields added in [Qwen Code PR #2827](https://github.com/QwenLM/qwen-code/pull/2827) round-trip as well: command hooks carry `async` (run in the background), `env` (extra subprocess environment variables), and `shell` (`bash`/`powershell`); http hooks carry `headers` (with `${VAR}` interpolation), `allowedEnvVars` (the env-var allowlist), and `once` (single execution per event per session); `statusMessage` (progress text) applies to both. Command-only fields are emitted only on command hooks and http-only fields only on http hooks. The group-level `sequential` flag (parallel by default) and the top-level `disableAllHooks` switch are both round-tripped, and other top-level keys in `settings.json` are preserved. See the [Qwen Code hooks docs](https://github.com/QwenLM/qwen-code/blob/main/docs/users/features/hooks.md).\\n\\n> **Note:** Reasonix hooks are written to a dedicated `.reasonix/settings.json` (project) / `~/.reasonix/settings.json` (global) — a Claude-Code-style but standalone JSON file, separate from the `[permissions]`/`[[plugins]]` TOML config. Unlike Claude Code, each event key maps directly to a **flat array** of hook objects (no `matcher`/`hooks` wrapper): `{ \"EventName\": [ { \"match\": \"...\", \"command\": \"...\", \"description\": \"...\", \"timeout\": ... } ] }`. All ten of Reasonix\\'s documented events are mapped — `preToolUse` ⇄ `PreToolUse`, `postToolUse` ⇄ `PostToolUse`, `beforeSubmitPrompt` ⇄ `UserPromptSubmit`, `stop` ⇄ `Stop`, `sessionStart` ⇄ `SessionStart`, `sessionEnd` ⇄ `SessionEnd`, `subagentStop` ⇄ `SubagentStop`, `postModelInvocation` ⇄ `PostLLMCall`, `notification` ⇄ `Notification`, and `preCompact` ⇄ `PreCompact`. `match` (Reasonix\\'s matcher field name) is honored only on `PreToolUse`/`PostToolUse`; a matcher on any other event is dropped with a warning. The canonical `timeout` field is documented in seconds, while Reasonix\\'s `timeout` is milliseconds, so rulesync converts (`× 1000` on generate, `÷ 1000` on import). Only `command`-type hooks are supported. The `settings.json` file is not documented as holding anything besides hooks today, but rulesync merges non-destructively and never deletes it, in case a future Reasonix version adds other keys. See the [Reasonix Hooks guide](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md).\\n\\n> **Note:** Grok CLI (xAI Grok Build) hooks are written to a dedicated, standalone `rulesync.json` that Grok auto-discovers from `.grok/hooks/*.json` (project) / `~/.grok/hooks/*.json` (global). The JSON shape is Claude-Code-compatible: each event nests under the top-level `hooks` key as a per-matcher array (`{ \"hooks\": { \"EventName\": [ { \"matcher\": \"...\", \"hooks\": [ { \"type\": \"command\", \"command\": \"...\", \"timeout\": ... } ] } ] } }`). All fourteen documented events map 1:1 onto canonical arms — `sessionStart` ⇄ `SessionStart`, `sessionEnd` ⇄ `SessionEnd`, `beforeSubmitPrompt` ⇄ `UserPromptSubmit`, `preToolUse` ⇄ `PreToolUse`, `postToolUse` ⇄ `PostToolUse`, `postToolUseFailure` ⇄ `PostToolUseFailure`, `permissionDenied` ⇄ `PermissionDenied`, `stop` ⇄ `Stop`, `stopFailure` ⇄ `StopFailure`, `notification` ⇄ `Notification`, `subagentStart` ⇄ `SubagentStart`, `subagentStop` ⇄ `SubagentStop`, `preCompact` ⇄ `PreCompact`, and `postCompact` ⇄ `PostCompact`. A `matcher` (a regex tested against the tool name) is honored on the tool-name events (`PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionDenied`), matching Claude Code\\'s semantics; a matcher on any other event is dropped with a warning. Commands are emitted verbatim (Grok documents no project-directory variable). See the [Grok hooks docs](https://docs.x.ai/build/features/hooks). Both handler types Grok defines round-trip: a `command` hook runs a command, and an `http` hook POSTs the payload to its `url`. Note that a `.rulesync/hooks.*` obtained with `rulesync fetch` can therefore point a Grok hook at any URL — read it before generating.\\n\\n> **Note:** Kimi Code hooks are global-only and written as flat `[[hooks]]` entries in `~/.kimi-code/config.toml`, with `event`, `command`, and optional `matcher`/`timeout`. Rulesync maps fourteen canonical lifecycle events to Kimi\\'s PascalCase names: `sessionStart`, `sessionEnd`, `beforeSubmitPrompt`, `preToolUse`, `postToolUse`, `postToolUseFailure`, `permissionRequest`, `stop`, `stopFailure`, `notification`, `subagentStart`, `subagentStop`, `preCompact`, and `postCompact`. Kimi\\'s native `PermissionResult` and `Interrupt` events have no canonical equivalents, but they can be preserved in the `kimi-code.hooks` override. Only `command` hooks are emitted. Kimi normally runs these user-level hooks with each current session project as the working directory, which would let an unrelated repository substitute a relative script or influence commands such as `npm test`. Rulesync therefore wraps every generated command so it first changes to the trusted absolute directory containing the source `.rulesync/hooks.jsonc`; relative paths and project-aware commands consistently resolve against that source rather than whichever repository Kimi later opens. Kimi requires `timeout` to be an integer from 1 to 600 seconds; invalid canonical values are omitted with a warning so Kimi can still load the config. The shared TOML file is merged in place and never deleted. See the [Kimi Code hooks docs](https://moonshotai.github.io/kimi-code/en/customization/hooks.html).\\n\\n## `.github/mcp.json` and `.copilot/mcp-config.json`\\n\\nExample:\\n\\n```json\\n{\\n \"mcpServers\": {\\n \"serena\": {\\n \"type\": \"stdio\",\\n \"command\": \"uvx\",\\n \"args\": [\"--from\", \"git+https://github.com/oraios/serena\", \"serena\", \"start-mcp-server\"]\\n },\\n \"github\": {\\n \"type\": \"http\",\\n \"url\": \"http://localhost:3000/mcp\"\\n },\\n \"local-dev\": {\\n \"type\": \"local\",\\n \"command\": \"node\",\\n \"args\": [\"scripts/start-local-mcp.js\"]\\n }\\n }\\n}\\n```\\n\\nThis file is used by the GitHub Copilot CLI for MCP server configuration. Rulesync manages it by converting from the unified `.rulesync/mcp.jsonc` format. Both scopes use the same `{ \"mcpServers\": {...} }` shape but write to different paths:\\n\\n- **Project mode:** `.github/mcp.json` (relative to project root) — the Copilot CLI auto-loads MCP servers from this workspace config file ([changelog v1.0.61, 2026-06-09](https://github.com/github/copilot-cli)).\\n- **Global mode:** `~/.copilot/mcp-config.json` (relative to home directory) — the personal/global MCP configuration.\\n\\n> **Migration note:** earlier Rulesync versions wrote the **project-mode** Copilot CLI MCP config to `.copilot/mcp-config.json` (the same path used for global mode). Project mode now writes the dedicated workspace file `.github/mcp.json` instead, so a previously generated project-scope `.copilot/mcp-config.json` is no longer managed and can be removed by hand.\\n\\nRulesync preserves explicit `type` values for `http`, `sse`, and `local` servers. For command-based servers that omit a transport type, Rulesync emits the mandatory `\"type\": \"stdio\"` field required by the Copilot CLI. `streamable-http` is written as `http`, the transport it names, and the canonical `httpUrl` alias is normalized to the `url` Copilot CLI reads. A server the Copilot CLI config cannot express is skipped with a warning rather than failing the run: one that declares no transport at all (the shape a Kilo `{\"enabled\": …}` toggle imports as, which switches off a server some other config layer defines — every entry here defines a server), one that names a remote transport but no `url`/`httpUrl`, one that names a local transport but no `command`, and a `ws` server, since Copilot CLI has no WebSocket transport.\\n\\n## `rulesync/commands/*.md`\\n\\nExample:\\n\\n```md\\n---\\ndescription: \"Review a pull request\" # command description\\ntargets: [\"*\"] # * = all, or specific tools\\ncopilot: # copilot specific parameters (optional)\\n description: \"Review a pull request\"\\n agent: \"agent\" # (optional) VS Code prompt-file agent: \"ask\", \"agent\", \"plan\", or a custom agent name (replaces the deprecated \"mode\")\\nantigravity: # antigravity specific parameters\\n trigger: \"/review\" # Specific trigger for workflow (renames file to review.md)\\n turbo: true # (Optional, default: true) Append // turbo for auto-execution\\ntakt: # takt specific parameters (optional; emitted under .takt/facets/instructions/)\\n name: \"renamed-stem\" # (optional) override the emitted filename stem (no path separators or \"..\")\\n extends: \"base\" # (optional) emit a leading `{extends:<parent>}` facet-inheritance directive (Takt 0.39.0+)\\npi: # pi coding agent specific parameters (optional)\\n argument-hint: \"[message]\" # Hint shown in Pi\\'s command palette\\ncodexcli: # Codex CLI custom-prompt specific parameters (optional)\\n argument-hint: \"[message]\" # Hint shown for the custom prompt\\'s arguments\\nroo: # Roo Code specific parameters (optional)\\n mode: \"architect\" # (optional) mode slug to switch to before running the command body (e.g. \"code\", \"architect\")\\n---\\n\\ntarget_pr = $ARGUMENTS\\n\\nIf target_pr is not provided, use the PR of the current branch.\\n\\nExecute the following in parallel:\\n\\n...\\n```\\n\\nThe command body itself uses a Claude Code-compatible **universal syntax** (e.g. `$ARGUMENTS`, `` !`cmd` ``). When a target tool expects a different placeholder syntax, rulesync translates it automatically on generation and reverses the translation on import. See [Command Syntax](./command-syntax.md) for the full mapping.\\n\\n> **Codex CLI deprecation note:** Codex CLI\\'s own docs now state \"Custom prompts are deprecated. Use skills for reusable instructions\" (see [Custom Prompts](https://developers.openai.com/codex/custom-prompts)). Rulesync\\'s `codexcli` commands still generate the global-only `~/.codex/prompts/*.md` custom-prompt files described above — they remain functional and no removal date has been announced, so this behavior is unchanged for now. For new reusable instructions, prefer rulesync\\'s `codexcli` skills support (see `.rulesync/skills/*/SKILL.md` below) instead.\\n\\n> **Warp note:** Warp documents skills as its custom slash-command surface — any skill is invocable as `/{skill-name}` with `$ARGUMENTS` / `$ARGUMENTS[N]` / `$N` argument substitution — so rulesync emits each command onto the native skills surface as `.warp/skills/<name>/SKILL.md` (project) / `~/.warp/skills/<name>/SKILL.md` (global, via `--global`), with `name`/`description` frontmatter derived from the command file. Warp\\'s `.warp/workflows/` YAML files are parameterized shell-command templates, not agent prompts, and are deliberately not used. Commands import and `--delete` are no-ops for `warp` because the skills feature owns the `.warp/skills/` tree (importing it as commands would double-import every skill) — mirrors the Devin note below. Keep command and skill names distinct for this target, since a command and a skill sharing a name write the same `SKILL.md` path. See the [Warp skills docs](https://docs.warp.dev/agent-platform/capabilities/skills/).\\n\\n> **Devin note:** Devin\\'s extensibility docs no longer document a standalone workflows/commands component — reusable prompts invoked as slash commands are [Skills](https://docs.devin.ai/cli/extensibility/skills/overview) (`/name`). Rulesync therefore emits each command onto the native skills surface as `.devin/skills/<name>/SKILL.md` (project) / `~/.config/devin/skills/<name>/SKILL.md` (global, via `--global`), with `name`/`description` frontmatter derived from the command file. The legacy Windsurf/Cascade-era `.devin/workflows/` and `~/.codeium/windsurf/global_workflows/` locations are no longer emitted (stale outputs there stay gitignored but are not cleaned up automatically). Commands import and `--delete` are no-ops for `devin` because the skills feature owns the `.devin/skills/` tree (importing it as commands would double-import every skill). Note that a command and a skill sharing the same name write the same `SKILL.md` path, so keep command and skill names distinct for this target.\\n\\n> **Agent Skills standard note (`agentsskills`):** Rulesync accepts the legacy rulesync spellings on input but always **emits** the shapes the [specification](https://agentskills.io/specification) requires: `allowed-tools` becomes a space-separated scalar (a YAML list is joined), `compatibility` becomes a string (an object is flattened to `key: value` pairs), and `metadata` values are stringified so the block stays a string→string map. Generation also checks the normative constraints and warns — without failing the run — when `name` is empty, longer than 64 characters, contains anything but lowercase letters, digits and single hyphens, or does not match its parent directory name; when `description` is empty or longer than 1024 characters; when `compatibility` exceeds 500 characters; or when an `allowed-tools` list entry contains whitespace, which the space-separated form cannot represent. These are warnings rather than errors because a conformant client only _skips_ such a skill, and because import stays lenient as the [client-implementation guide](https://agentskills.io/client-implementation/adding-skills-support) advises. A value that normalizes to the empty string (`compatibility: {}`, `allowed-tools: []`) is dropped rather than written, since the spec requires `compatibility` to be 1–500 characters when present. On **import**, `allowed-tools` is normalized back to the canonical rulesync list, so a generate → import round trip leaves `.rulesync/skills/**` in the shape it started in (the `compatibility` and `metadata` coercions are one-way, because the legacy object/number forms have no conformant equivalent). `hermesagent` reads the same `agentsskills` block and applies the same normalization in both directions, so one rulesync source never produces two different on-disk spellings — except for `metadata`, which stays structured there because Hermes reads `metadata.hermes.*` as YAML. A `hermesagent:` override still wins over the shared block (as for every tool-specific section), so a list or mapping written there is emitted as-is and reported as a spec violation rather than rewritten. Validate the result with the spec\\'s own [`skills-ref validate`](https://github.com/agentskills/agentskills/tree/main/skills-ref).\\n\\n> **Replit note:** Replit\\'s skills page states conformance to the [Agent Skills specification](https://agentskills.io/specification), so `replit.allowed-tools` accepts either the spec\\'s space-separated string or a canonical rulesync list and is always **emitted** as the string; `replit.compatibility` likewise accepts the spec\\'s string alongside the legacy object form. On import, `allowed-tools` is normalized back to the list, mirroring `deepagents` — so keep list entries free of whitespace, since the space-separated form cannot represent an entry such as `Bash(git commit:*)` and a client would read it back as two. An object `compatibility` is emitted unchanged rather than flattened: unlike the join, that conversion would be one-way, so the legacy form stays as-is and is simply not spec-conformant on disk.\\n\\n> **Vibe skills note:** Vibe discovers skills under `.vibe/skills/` (project) and `~/.vibe/skills/` (global), plus the shared `.agents/skills/` root at **both** scopes — Vibe\\'s `user_skills_dirs` returns `~/.vibe/skills` and `~/.agents/skills` alike. Rulesync registers the shared root as an import fallback at either scope; it is import-only and is never removed by Vibe-target orphan deletion.\\n\\n> **Pi skills note:** Pi implements the [Agent Skills specification](https://agentskills.io/specification), so `pi.allowed-tools` accepts either the spec\\'s space-delimited string or a canonical rulesync list and is always **emitted** as the string; `pi.compatibility` likewise accepts the spec\\'s string alongside the legacy object form. Importing a spec-conformant `SKILL.md` used to fail outright. On import, `allowed-tools` is normalized back to the list, mirroring `deepagents`; keep list entries free of whitespace, since the space-delimited form cannot represent an entry such as `Bash(git commit:*)`. An `allowed-tools` value that normalizes to the empty string (an empty list) is dropped rather than written. An object `compatibility` is emitted unchanged rather than flattened, because that conversion would be one-way.\\n\\n> **Hermes Agent note:** Commands are global-only and remain distinct from skills. Rulesync writes JSON command specs to `~/.hermes/rulesync/commands/<name>.json`, installs the `rulesync-commands` plugin under `~/.hermes/plugins/`, and enables it in `~/.hermes/config.yaml`. The plugin registers each spec with Hermes\\'s [`ctx.register_command()` plugin API](https://hermes-agent.nousresearch.com/docs/user-guide/features/plugins/) and dispatches the prompt through `delegate_task`; invocation arguments are appended to the prompt. `.rulesync/skills/<name>/SKILL.md` still generates a full [Hermes Agent Skill](https://hermes-agent.nousresearch.com/docs/user-guide/features/skills/) under `~/.hermes/skills/<name>/SKILL.md`, which Hermes also exposes as a dynamic slash command. Rulesync rejects command/skill names that would collide in Hermes\\'s slash-command namespace, and rejects nested command paths that flatten to the same name. Generate commands with `rulesync generate --targets hermesagent --features commands --global`.\\n>\\n> Releases before this native plugin transport emitted Hermes commands as `~/.hermes/skills/<name>/SKILL.md`. Rulesync cannot distinguish those files from real user-authored skills safely, so remove an obsolete legacy file manually after confirming that `.rulesync/skills/<name>/SKILL.md` does not own it.\\n\\n> **Qwen Code note:** Custom commands are emitted as **Markdown** files (not TOML — TOML is deprecated upstream) under `.qwen/commands/` (project) and `~/.qwen/commands/` (global, via `--global`). The file is an optional YAML frontmatter block followed by the prompt body; besides `description`, Qwen Code\\'s command loader reads `when_to_use` (invocation guidance), `argument-hint` (completion hint), and `disable-model-invocation`, all typed and round-tripped. Subdirectory namespacing is supported: `.qwen/commands/git/commit.md` becomes the `/git:commit` command. Any extra fields are preserved on round-trip under the `qwencode:` block.\\n\\n> **OpenCode import note:** OpenCode lets commands live both as Markdown files under `.opencode/commands/*.md` **and** inline in `opencode.json`/`opencode.jsonc` under the top-level `command` key. On import, rulesync reads both: each inline entry\\'s `template` becomes the command body and its `description`/`agent`/`model`/`subtask` fields become frontmatter. A Markdown file takes precedence over an inline entry with the same name.\\n\\n> **AugmentCode note:** Commands are written to `.augment/commands/<name>.md` (project) / `~/.augment/commands/<name>.md` (global, via `--global`). Subdirectories are namespaces — `.augment/commands/git/commit.md` is `/git:commit` — so nested rulesync commands keep their nesting rather than being flattened to a basename. If you generated AugmentCode commands with an earlier Rulesync, the flattened files it wrote are still on disk under their old names; `--delete` removes them. Auggie also discovers commands under the cross-tool `.agents/commands/` root, so **import** reads that root too and treats a command found there as if it lived under `.augment/commands/` — the command\\'s name is its path under whichever root it came from. Generation stays on `.augment/commands/`, and `.agents/commands/` is never written to or swept for orphans, since the files there may belong to another tool — Rulesync itself writes that root for the `agentsmd` target, so a command already imported from `.augment/commands/` is not imported again from there under a flattened name. Auggie\\'s other shared root, `.claude/commands/`, is deliberately not read: it is Claude Code\\'s own output, which Rulesync already imports as that target. Importing from a shared root is announced, because the result is a Rulesync command written for every target on the next generate. See the [custom commands docs](https://docs.augmentcode.com/cli/custom-commands).\\n\\n> **Reasonix note:** Custom slash commands are Markdown files under `.reasonix/commands/` (project) / `~/.reasonix/commands/` (global, via `--global`) — directly analogous to Claude Code\\'s `.claude/commands/`, since Reasonix explicitly mirrors Claude Code\\'s conventions. Frontmatter supports `description` and `argument-hint`, and the body uses the same `$ARGUMENTS` / `$1`…`$N` placeholder syntax. Subdirectory namespacing is supported (`git/commit.md` → `/git:commit`). Any extra fields are preserved on round-trip under the `reasonix:` block. See the [Reasonix GUIDE](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/GUIDE.md#slash-commands).\\n\\n> **Grok CLI note:** Custom slash commands are Markdown files under `.grok/commands/` (project) / `~/.grok/commands/` (global, via `--global`), read by the same Claude-Code-compatible frontmatter parser Grok uses for skills. Rulesync emits `description` plus, from the `grokcli:` block, `argument-hint`, `user-invocable` (default true) and `disable-model-invocation` (default false) — the same invocation-control pair Grok skills honor. Two upstream constraints are worth knowing. Grok\\'s command scan is **flat and non-recursive**, so subdirectory namespacing is not supported: a nested `git/commit.md` is flattened onto `commit.md`, and two nested commands with the same basename collide (rulesync warns and the last one wins). And Grok collects skills before commands, letting **skills win name collisions** — a `.grok/skills/<name>/` shadows `.grok/commands/<name>.md`, so avoid giving a rulesync skill and a rulesync command the same name when targeting Grok. Any extra frontmatter keys are preserved on round-trip under the `grokcli:` block. See the [skills, plugins and marketplaces docs](https://docs.x.ai/build/features/skills-plugins-marketplaces).\\n\\n> **Rovo Dev CLI note:** Rovo Dev\\'s \"saved prompts\" are a file-based custom-command surface made of a `prompts.yml` manifest plus per-prompt Markdown content files, invoked via `/prompts [title] [extra]`. Rulesync writes the content (no frontmatter) to `.rovodev/prompts/<name>.md` (project) / `~/.rovodev/prompts/<name>.md` (global, via `--global`), and rebuilds the sibling `.rovodev/prompts.yml` / `~/.rovodev/prompts.yml` manifest with one `{ name, description, content_file }` entry per prompt, `content_file` pointing at `prompts/<name>.md` (resolved relative to `prompts.yml`, matching Rovo Dev\\'s own resolution order). The `prompts` array is fully replaced from the current rulesync commands on each generate (mirrors the Rovodev MCP adapter fully replacing `mcpServers`); any other top-level key in an existing manifest is preserved, and the manifest is never deleted. See the [saved prompts](https://support.atlassian.com/rovo/docs/save-and-reuse-a-prompt-in-rovo-dev-cli/) and [CLI commands](https://support.atlassian.com/rovo/docs/rovo-dev-cli-commands/) docs.\\n\\n## `rulesync/subagents/*.md`\\n\\nExample:\\n\\n```md\\n---\\nname: planner # subagent name\\ntargets: [\"*\"] # * = all, or specific tools\\ndescription: >- # subagent description\\n This is the general-purpose planner. The user asks the agent to plan to\\n suggest a specification, implement a new feature, refactor the codebase, or\\n fix a bug. This agent can be called by the user explicitly only.\\nclaudecode: # for claudecode-specific parameters\\n model: inherit # opus, sonnet, haiku, fable, a full model id, or inherit (default)\\n tools: [\"Read\", \"Write\"] # (optional) allowed tools (string or list)\\n disallowedTools: [\"Bash\"] # (optional) tools to remove (string or list)\\n permissionMode: default # (optional) default | acceptEdits | bypassPermissions | plan\\n maxTurns: 20 # (optional) maximum agentic turns\\n skills: [\"skill-creator\"] # (optional) Agent Skills to utilize (string or list)\\n color: cyan # (optional) UI color (e.g. red, blue, green, cyan, ...)\\n memory: project # (optional) user | project | local\\n effort: high # (optional) low | medium | high | xhigh | max\\n isolation: worktree # (optional) run the subagent in an isolated git worktree\\n background: false # (optional) run the subagent in the background\\n initialPrompt: \"Start by reading the spec.\" # (optional) seed prompt for the subagent\\n mcpServers: {} # (optional) MCP server config (passed through verbatim)\\n hooks: {} # (optional) hook config (passed through verbatim)\\ncopilot: # for GitHub Copilot specific parameters\\n tools:\\n # Listed tools are emitted verbatim; omit `tools` entirely to grant the agent\\n # all tools. `agent/runSubagent` is opt-in — add it explicitly only when this\\n # subagent needs to orchestrate other subagents.\\n - web/fetch\\n - agent/runSubagent\\nopencode: # for OpenCode-specific parameters\\n mode: subagent # (optional, defaults to \"subagent\") OpenCode agent mode\\n model: anthropic/claude-sonnet-4-20250514\\n temperature: 0.1\\n tools:\\n write: false\\n edit: false\\n bash: false\\n permission:\\n bash:\\n \"git diff\": allow\\nkilo: # for Kilo-specific parameters\\n mode: all # (optional, defaults to \"all\") use \"subagent\" for hidden/subagent-only agents\\ncursor: # for Cursor-specific parameters (generated to .cursor/agents/*.md)\\n model: inherit # (optional, defaults to \"inherit\") model id, or \"inherit\" to use the parent\\'s model\\n readonly: false # (optional, defaults to false) restrict the subagent to read-only tools\\n is_background: false # (optional, defaults to false) run the subagent as a background agent\\njunie: # for JetBrains Junie CLI specific parameters (generated to .junie/agents/*.md; also imported from .agents/*.md)\\n tools: [\"Read\", \"Grep\", \"Edit\"] # allowed tools\\n disallowedTools: [\"Bash\", \"WebSearch\"] # disallowed tools\\n mcpServers: [\"github\"] # MCP servers the subagent may use\\n model: sonnet # model id\\n reasoningLevel: high # low | medium | high\\n maxTurns: 20 # max agentic turns\\n skills: [\"kotlin\", \"writerside\"] # Agent Skills to utilize\\n allowPromptArgument: true # whether the subagent accepts a prompt argument\\ntakt: # takt specific parameters (optional; emitted under .takt/facets/personas/)\\n name: \"renamed-stem\" # (optional) override the emitted filename stem (no path separators or \"..\")\\nroo: # for Roo Code specific parameters (optional; aggregated into the root .roomodes file)\\n slug: planner # (optional) custom mode slug (^[a-zA-Z0-9-]+$); defaults to the sanitized file name\\n whenToUse: \"When planning a task\" # (optional) guidance for automated mode selection\\n customInstructions: \"Be concise.\" # (optional) extra behavioral guidelines\\n roleDefinition: \"You are the planner.\" # (optional) overrides the body as the mode\\'s roleDefinition\\n groups: # (optional, defaults to [\"read\", \"edit\", \"command\", \"mcp\"]) tool access\\n - read\\n - [\"edit\", { fileRegex: \"\\\\\\\\.md$\", description: \"Markdown files\" }]\\n---\\n\\nYou are the planner for any tasks.\\n\\nBased on the user\\'s instruction, create a plan while analyzing the related files. Then, report the plan in detail. You can output files to @tmp/ if needed.\\n\\nAttention, again, you are just the planner, so though you can read any files and run any commands for analysis, please don\\'t write any code.\\n```\\n\\n> **Antigravity note:** Antigravity custom agents (CLI v1.1.6+, shared by the IDE and the CLI) are emitted as Markdown + YAML frontmatter to `.agents/agents/<name>.md` (project) and `~/.gemini/config/agents/<name>.md` (global, via `--global`); the body after the frontmatter is the agent\\'s system prompt. Both `antigravity-ide` and `antigravity-cli` read the same two locations, so enabling both writes the same file — the same way they already share `.agents/hooks.json`. Antigravity also accepts a directory form (`<name>/agent.md`); Rulesync emits and imports the flat file form only. `name` and `description` are **required** upstream, so a canonical subagent without a description gets a minimal generated fallback rather than a file Antigravity refuses to load. Because the two share that file, every Antigravity target reads the `antigravity-ide` and `antigravity-cli` blocks merged in a fixed order (the CLI block wins) — the same rule the MCP feature uses for the same shared-output reason — so generation order never changes the file\\'s content; the `antigravity-plugin` block is layered on top for the plugin bundle only. Besides the shared `name`/`description`, those blocks accept these optional fields (all preserved on round-trip): `tools` (string list), `mainAgent` (boolean, default `true`), `subagent` (boolean, default `true`), `model` (`inherit` | `flash` | `pro`), `commandExecutionPolicy` (`off` | `auto` | `eager` | `sandbox`), `mcpServers`, `skills`, and `plugins`. `hidden` and `inheritMcp` appear in the v1.1.6 release notes but not in the documented frontmatter table, so they pass through verbatim with no behavior modeled around them; the schema is loose, so any extra keys survive the round-trip too. The `antigravity-plugin` target writes the same file format into a plugin bundle\\'s `agents/` directory (project scope only). See the [Antigravity subagents docs](https://antigravity.google/docs/subagents) and the [plugin bundle layout](https://antigravity.google/docs/cli/plugins).\\n\\n> **Qwen Code note:** Subagents are emitted as Markdown + YAML frontmatter under `.qwen/agents/` (project) and `~/.qwen/agents/` (user/global, via `--global`); the body is the subagent\\'s system prompt. Besides the shared `name`/`description`, the `qwencode:` block accepts these optional fields (all preserved on round-trip): `model`, `approvalMode` (`default` | `plan` | `auto-edit` | `yolo` | `bubble`), `tools` (allowlist), `disallowedTools` (denylist), `maxTurns`, `color`, `mcpServers` (per-agent MCP overrides — accepts both a record of server specs, matching Qwen\\'s documented shape, and a plain array of server names), and `hooks` (per-agent hook registrations). See the [Qwen Code sub-agents docs](https://github.com/QwenLM/qwen-code/blob/main/docs/users/features/sub-agents.md).\\n\\n> **Kimi Code note:** Subagents are emitted as Markdown files under `.kimi-code/agents/` (project) and `~/.kimi-code/agents/` (global). The shared `name` and required `description` fields are written to YAML frontmatter; Kimi-specific `whenToUse`, `override`, `tools`, `disallowedTools`, and `subagents` fields can be authored under the `kimi-code:` block and round-trip unchanged. Kimi recursively scans both its Kimi-specific agents directory and the shared `.agents/agents/` directory, so Rulesync imports nested Markdown files from both locations and flattens them into `.rulesync/subagents/<name>.md` using the validated kebab-case agent name. The Kimi-specific root has precedence over `.agents/agents/`; if multiple source files resolve to the same logical agent name, the first one wins and Rulesync warns about the duplicate. The shared root is import-only and is never removed by Kimi-target orphan deletion. See the [Kimi Code custom-agents docs](https://moonshotai.github.io/kimi-code/en/customization/agents.html).\\n\\n> **Kiro CLI note:** Subagents are emitted as JSON agent configurations under `.kiro/agents/` (project) and `~/.kiro/agents/` (global). Kiro allows the JSON `name` field to be omitted, in which case the filename stem is the agent name; Rulesync accepts that form on import and writes the derived name into the Rulesync frontmatter. Imports through the `kiro-cli` target retain `targets: [\"kiro-cli\"]`, so they can be generated back to the same target without changing the target metadata.\\n\\n> **Cline note:** Cline file-based agents are emitted as YAML files (`<name>.yaml`) into `.cline/agents/` (project) and `~/.cline/agents/` (global, via `--global`). The file is a YAML frontmatter block followed by the system prompt body, matching Cline\\'s agent config loader: `name` and `description` are **required** (Cline cli-v3.0.23+ refuses to load an agent whose `description` is missing or empty — a canonical subagent without one gets a minimal generated fallback rather than a file Cline cannot load), and the typed optional fields `tools`, `skills`, `providerId`, `modelId`, and `maxIterations` round-trip through the `cline:` section. Import reads `.yml` alongside `.yaml`, matching Cline\\'s `isYamlFile()`.\\n\\n> **Devin note:** Devin Local custom subagent profiles are emitted as `AGENT.md` files in a **directory-per-agent** layout: `.devin/agents/<name>/AGENT.md` (project) and `~/.config/devin/agents/<name>/AGENT.md` (global, via `--global`). The directory name `<name>` is the profile id (derived from the rulesync subagent file name). The `AGENT.md` is a YAML frontmatter block followed by the subagent\\'s system prompt. Besides the shared `name`/`description`, the `devin` subagent block accepts these optional fields (all preserved on round-trip): `model` (string, override the subagent LLM), `allowed-tools` (list of strings, restrict available tools), `permissions` (object with `allow`/`deny`/`ask` string lists, override tool permissions), and `max-nesting` (integer, enable nested subagent spawning up to the given depth). See the [Devin subagents docs](https://docs.devin.ai/cli/subagents).\\n\\n> **Reasonix note:** Reasonix native subagents are Skill profiles emitted as `SKILL.md` files in a **directory-per-agent** layout: `.reasonix/skills/<name>/SKILL.md` (project) and `~/.reasonix/skills/<name>/SKILL.md` (global, via `--global`). The directory name `<name>` is the profile id (derived from the rulesync subagent file name). A subagent is a Skill whose YAML frontmatter declares `invocation: manual` and `runAs: subagent` — Rulesync always injects both markers so the SKILL.md is recognized as a manually invoked subagent rather than an auto-discovered skill. Besides the shared `name`/`description`, the `reasonix` subagent block accepts these optional fields (all preserved on round-trip): `model` (string, subagent LLM), `effort` (string, reasoning effort), `allowed-tools` (list of strings, restrict available tools), and `color` (string, display color). The schema is loose, so any extra keys survive the round-trip. See the [Reasonix subagent profiles docs](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/SUBAGENT_PROFILES.md).\\n\\n> **Roo skills/commands note (final v3.54.0 state — Roo Code is EOL and its repository archived):** Commands are generated to `.roo/commands/` (project) and `~/.roo/commands/` (global, via `--global`; project wins on a name collision). Skill frontmatter beyond `name`/`description` — most usefully `modeSlugs: string[]` for mode targeting — is authored via the `roo:` section of `.rulesync/skills/*/SKILL.md` and lifted back into it on import, so it survives the round-trip. A localRoot rule is emitted as `AGENTS.local.md`, the personal, gitignored override file Roo loads alongside `AGENTS.md`.\\n\\n> **Roo note (as of 2026-06-16):** Roo Code reads project custom modes from a single aggregated `.roomodes` file at the workspace root (YAML; JSON also accepted). Rulesync therefore collapses every Roo-targeted subagent into that file\\'s `customModes` array — each subagent becomes one mode whose `slug` is derived from the file name (sanitized to `^[a-zA-Z0-9-]+$`), `name`/`description` come from the shared frontmatter, and `roleDefinition` is the subagent body. The optional `roo:` block supplies `groups` (defaults to `[\"read\", \"edit\", \"command\", \"mcp\"]`), `whenToUse`, `customInstructions`, an explicit `slug`, and a `roleDefinition` override. (Roo\\'s previous `.roo/subagents/` output was inert — Roo Code never read it.) See the [Roo custom-modes docs](https://roocodeinc.github.io/Roo-Code/features/custom-modes).\\n\\n> **OpenCode import note:** OpenCode lets agents live both as Markdown files under `.opencode/agents/*.md` **and** inline in `opencode.json`/`opencode.jsonc` under the top-level `agent` key. On import, rulesync reads both: each inline entry\\'s `prompt` becomes the subagent body (a `\"{file:./path}\"` reference is resolved relative to the config file\\'s location, as OpenCode does), and the remaining fields (`description`/`mode`/`model`/`tools`/`permission`/...) become frontmatter under the `opencode:` block. A Markdown file takes precedence over an inline entry with the same name.\\n\\n> **Kilo note (as of 2026-05-13):** Kilo\\'s documented default for user-defined agents is `mode: all`, which makes the agent available both as a top-level pick and as a subagent. Set `kilo.mode: subagent` to opt into hidden/subagent-only behavior.\\n\\nBesides `mode`, the `kilo` subagent block accepts these optional fields (all preserved on round-trip):\\n\\n| Field | Type | Notes |\\n| ------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\\n| `displayName` | string | Human-friendly name shown in pickers |\\n| `model` | string | Model id |\\n| `variant` | string | Model variant |\\n| `temperature` | number | Sampling temperature |\\n| `top_p` | number | Nucleus-sampling parameter |\\n| `permission` | string \\\\| object | Permission profile name, or a per-tool `{ <tool>: { allow, deny, ask } }` object |\\n| `prompt` | string | Inline system prompt |\\n| `color` | string | UI color |\\n| `native` | boolean | Native (built-in) agent flag |\\n| `hidden` | boolean | Hide from top-level picker |\\n| `disable` | boolean | Disable the agent |\\n| `deprecated` | boolean | Mark as deprecated |\\n| `steps` | positive integer | Maximum agentic iterations before a text-only response is forced (an explicit `null` is accepted and round-trips as-is, so a file that already carries one still imports; earlier Rulesync versions took a list of step objects here, which Kilo never accepted) |\\n| `options` | object | Free-form key/value options |\\n\\n> **Migration note (`steps`):** earlier Rulesync versions typed `steps` as a list of step objects, which Kilo never accepted — a subagent authored that way produced a file Kilo ignored. It is now the iteration count Kilo documents, so a `kilo` block (or a `.kilo/agents/*.md` file) still carrying the list form fails validation with the offending file named, and the run stops rather than writing a file that would not work. Replace the list with the number of iterations you want, or drop the field.\\n\\n> **Hermes Agent note:** Project generation writes subagent JSON specs under `.hermes/rulesync/subagents/` and installs `.hermes/plugins/rulesync-subagents/`. The plugin resolves specs relative to its own installation, so the same code works in project and global scope. For project scope, Rulesync also enables `rulesync-subagents` in `$HERMES_HOME/config.yaml`. Run Hermes from the trusted project root with `HERMES_ENABLE_PROJECT_PLUGINS=true`; Rulesync deliberately does not persist that global trust gate.\\n\\n## `.rulesync/checks/*.md`\\n\\nCode review checks are per-check instructions an agent runs during code review. Each check is a single Markdown file with YAML frontmatter (the source of the check identity is the file name — e.g. `.rulesync/checks/security.md` defines the `security` check).\\n\\nExample:\\n\\n```md\\n---\\ntargets: [\"*\"] # * = all, or specific tools\\ndescription: Flags common security issues # (optional) short summary of the check\\nseverity: high # (optional) low | medium | high | critical\\ntools: [\"Read\", \"Grep\"] # (optional) tool names the check may use\\n---\\n\\nReview the diff for injection vulnerabilities, hardcoded secrets, and unsafe\\ndeserialization. Report each finding with a file and line reference.\\n```\\n\\nAmp, Cursor, Hermes Agent, Rovo Dev CLI and Takt consume checks. Amp receives one Markdown file per check:\\n\\n- **Project scope:** `.agents/checks/<name>.md`\\n- **Global scope** (`--global`): `~/.config/amp/checks/<name>.md`\\n\\nFor Cursor, checks are [Bugbot](https://cursor.com/docs/bugbot) code review instructions, and Bugbot reads one aggregated instruction file per directory rather than a file per check — so every check targeting Cursor collapses into the repository-root `.cursor/BUGBOT.md`. Each check becomes one section: an HTML-comment marker carrying the check name, an `## <name>` heading, and the check body as the instruction text (the `description` is used when the body is empty). Bugbot reads the file as free prose, so a check\\'s `severity` and `tools` have no equivalent there — they are not written and do not come back on import, and neither is `description` whenever the check also has a body. Project scope only: Bugbot reads repository files and there is no user-level instruction file. Example output:\\n\\n```md\\n<!-- rulesync:check:security -->\\n\\n## security\\n\\nReview the diff for injection vulnerabilities.\\n```\\n\\nOn import the markers split the file back into one check per section, each with `targets: [\"*\"]` because Bugbot instructions are plain prose that applies anywhere. Content sitting ahead of the first marker — and a hand-written `BUGBOT.md` with no markers at all — is imported as a single `bugbot` check, so nothing in the file is dropped. A check body that contains a marker line of its own (a quoted rulesync doc fragment, say) is written as `<!-- rulesync:literal-check:… -->` and restored on import, so it cannot split the check it belongs to. Bugbot also merges nested `<dir>/.cursor/BUGBOT.md` files found while traversing upward from changed files, but rulesync check sources carry no directory-placement semantics, so only the root file is generated.\\n\\nGenerating checks for Cursor replaces `.cursor/BUGBOT.md`, so run `rulesync import --targets cursor --features checks` first if the repository already has a hand-written one — generation warns when it is about to replace instructions rulesync did not write. Deletion is guarded: a `BUGBOT.md` holding anything rulesync did not write — no marker at all, or hand-written text ahead of the first marker — is never removed, so dropping the last check that targets Cursor takes rulesync\\'s own output with it and nothing else.\\n\\nFor Rovo Dev CLI, checks are [code-review custom instructions](https://support.atlassian.com/rovo/docs/set-custom-instructions-for-code-reviews/), and Rovo Dev reads one plain-Markdown file rather than a file per check — so every check targeting Rovo Dev collapses into `.rovodev/.review-agent.md` (note the leading dot in the file name). The file takes **no frontmatter**. Everything else works exactly as it does for Cursor Bugbot above, because the two surfaces are the same shape: one marked-up section per check, `severity`/`tools` dropped, `description` used only when the body is empty, markers splitting the file back on import (with a hand-written file importing as a single `review-agent` check), the same `<!-- rulesync:literal-check:… -->` escaping, the same replace-and-warn on generate, and the same deletion guard for a file holding anything rulesync did not write. Project scope only — these are per-repository review instructions and Rovo Dev documents no user-level equivalent, which is the opposite of the Rovo Dev permissions surface (global only).\\n\\nFor Hermes Agent, Rulesync writes project-local JSON specs under `.hermes/plugins/rulesync-checks/checks/` and a `rulesync-checks` plugin beside them. Its one-shot [`pre_verify` hook](https://hermes-agent.nousresearch.com/docs/user-guide/features/hooks/#pre-verify) fires only for coding turns with changed paths and `attempt == 0`, then asks Hermes to run all configured checks before finishing. `tools` is preserved as advisory guidance because Hermes does not enforce an Amp-style per-check tool allowlist. Run Hermes with the project plugin explicitly trusted for that invocation:\\n\\n```sh\\nHERMES_ENABLE_PROJECT_PLUGINS=1 hermes\\n```\\n\\nRulesync adds `rulesync-checks` to `plugins.enabled` in `$HERMES_HOME/config.yaml` but deliberately leaves `$HERMES_HOME/.env` unchanged, preserving Hermes\\'s global trust boundary. Existing plugin configuration is preserved; an explicit `plugins.disabled` conflict fails generation.\\n\\nFor Takt, checks are **quality gates**, and they live in the `workflow_overrides` block of the shared `.takt/config.yaml` (project) / `~/.takt/config.yaml` (global) rather than in files of their own — so every check targeting Takt collapses into that one file. A check becomes one gate: by default a **string gate**, the body text, which Takt injects into the agent step prompt as a completion directive (the `description` is used when the body is empty, and the file stem when neither is set); with `command` in the check\\'s `takt` frontmatter block, a **command gate** (`{type: command, name, command, cwd, timeout_ms}`), which Takt runs after the step and fails on a non-zero exit code. `name` defaults to the file stem so Takt\\'s logs identify the gate. `name`, `cwd` and `timeout_ms` belong to a command gate, so they are ignored on a check that states no `command`. `steps` and `personas` in that block scope a gate to named workflow steps or personas (`workflow_overrides.steps.<step>.quality_gates`); an unscoped gate applies everywhere, and a gate naming both is written to both. `quality_gates_edit_only` is a property of the block as a whole, so one check setting it turns it on for all of them. It reaches only the unscoped gates — Takt runs a `steps`/`personas`-scoped gate whether or not the step may edit files — so the reach it narrows is the other checks\\' unscoped gates, which is warned about when there are any. Takt gates carry no severity or tool allowlist, so a check\\'s `severity` and `tools` are not written and do not come back on import. Takt merges quality gates additively and dedupes them (project over global over the workflow YAML\\'s own gates). Example:\\n\\n```md\\n---\\ntargets: [\"takt\"]\\ntakt:\\n command: ./.takt/quality-gates/check.sh # omit for a string gate\\n timeout_ms: 300000\\n steps: [\"review\"] # (optional) scope to named workflow steps\\n personas: [\"coder\"] # (optional) scope to named personas\\n---\\n```\\n\\nA command gate\\'s `command` is run by Takt with no further gating — Takt\\'s default-deny `workflow_command_gates.custom_scripts` policy applies to gates declared in workflow YAML, not to these — so read the frontmatter of any check you obtain with `rulesync fetch` before generating. The body of a check that carries a `command` is not used. `workflow_overrides` is owned by the checks feature: it is rewritten from `.rulesync/checks/` on every generate, so a gate deleted there disappears from `config.yaml` too, while every other key of the file is preserved and the file is never deleted. When checks remain but none of them target Takt — every one names other tools — the block is retracted with a warning, whether an earlier generate or a hand edit put it there; that is what owning the key means, so author gates as checks rather than in `config.yaml`. A project with no `config.yaml` does not get one. Emptying `.rulesync/checks/` altogether is different: the feature has no source to generate from, so nothing runs and the gates already in `config.yaml` stay. Delete the block by hand in that case — a command gate left behind keeps running after every step. On import, each gate becomes its own check file, named from the gate text or the command gate\\'s `name`. A string gate is prose that applies anywhere, so it imports with `targets: [\"*\"]` like an Amp check; a command gate imports as `targets: [\"takt\"]`, since its body is empty and would generate an empty check for every other tool. A gate scoped to both a step and a persona becomes two checks, and a command gate carrying a field of the wrong type is left in `config.yaml` rather than imported. The default-deny `workflow_command_gates.custom_scripts` policy is **not** written here — Takt validates it against gates declared in workflow YAML, not against these, and it is authorable through the `takt` block of `.rulesync/permissions.*`, which owns the security policies. See the [Takt workflows docs](https://github.com/nrslib/takt/blob/main/docs/workflows.md).\\n\\nThe emitted Amp frontmatter is derived from the source as follows:\\n\\n| Amp field | Source |\\n| ------------------ | -------------------------------------------------------- |\\n| `name` | the source file basename without `.md` (required by Amp) |\\n| `description` | `description` |\\n| `severity-default` | `severity` |\\n| `tools` | `tools` |\\n\\nThe frontmatter schema is loose, so extra Amp-specific keys survive a generate/import round-trip (except keys that collide with a rulesync tool-target name such as `cursor` — those are treated as tool-scoped sections and are not re-emitted). A tool-scoped section (e.g. `amp: { \"severity-default\": \"critical\" }`) overrides the canonical values for that tool — the tool-specific value takes precedence, and the section itself is not emitted (except `name`, which always comes from the file name). On import, `severity-default` maps back to the generic `severity` field, and the `name` field is dropped because it is re-derived from the file name on the next generate.\\n\\n> **v1 limitation:** Amp also discovers subtree-scoped checks (e.g. `api/.agents/checks/`), but rulesync sources carry no directory-placement semantics, so those subtree-scoped checks are not generated. See the [Amp manual](https://ampcode.com/manual).\\n\\n## `.rulesync/skills/*/SKILL.md`\\n\\nExample:\\n\\n```md\\n---\\nname: example-skill # skill name\\ndescription: >- # skill description\\n A sample skill that demonstrates the skill format\\ntargets: [\"*\"] # * = all, or specific tools\\n# (optional) shared default for tools that support the flag — claudecode, cursor,\\n# zed, pi, qwencode, grokcli, and factorydroid. Any of those tool sections can\\n# override it by setting their own `disable-model-invocation` value below.\\ndisable-model-invocation: true\\n# (optional) shared default for tools that support the flag — claudecode, qwencode,\\n# vibe, grokcli, and factorydroid. Any of those tool sections can override it by\\n# setting their own `user-invocable` value below.\\nuser-invocable: false\\nclaudecode: # for claudecode-specific parameters\\n model: sonnet # opus, sonnet, haiku, or any string\\n when_to_use: When the user asks to review a PR # (optional) extra trigger context appended to description\\n allowed-tools: # (optional) tools usable without asking; accepts a string or a list\\n - \"Bash\"\\n - \"Read\"\\n - \"Write\"\\n - \"Grep\"\\n disallowed-tools: # (optional) removes these tools while the skill is active (string or list)\\n - \"WebFetch\"\\n effort: high # (optional) effort while active: low | medium | high | xhigh | max\\n argument-hint: \"[pr-number]\" # (optional) autocomplete hint for expected arguments\\n arguments: # (optional) named positional arguments for $name substitution (string or list)\\n - \"pr_number\"\\n context: fork # (optional) set to \"fork\" to run the skill in a forked subagent context\\n agent: code-reviewer # (optional) subagent type to use when context: fork\\n background: false # (optional, context: fork only) wait for the forked subagent in the invoking turn instead of backgrounding it (default true)\\n shell: bash # (optional) shell for ! command blocks: bash (default) or powershell\\n hooks: # (optional) hooks scoped to the skill\\'s lifecycle (free-form per the Claude Code docs)\\n PreToolUse:\\n - matcher: \"Bash\"\\n disable-model-invocation: true # (optional) disable model invocation for this skill\\n user-invocable: false # (optional) hide from the / menu while keeping model access\\n scheduled-task: true # (optional) emit to .claude/scheduled-tasks/<name>/SKILL.md instead of .claude/skills/<name>/SKILL.md\\n # paths (optional) limits auto-activation to matching globs. Accepts a\\n # comma-separated string, e.g. paths: \"src/**/*.ts,test/**/*.ts\", or a list:\\n paths:\\n - \"src/**/*.ts\"\\n - \"test/**/*.ts\"\\ncodexcli: # for codexcli-specific parameters\\n short-description: A brief user-facing description\\n # The following sections are emitted to the agents/openai.yaml sidecar next to SKILL.md.\\n # See https://developers.openai.com/codex/skills.md\\n interface: # (optional) UI metadata\\n display_name: Example Skill\\n short_description: A brief user-facing description\\n default_prompt: Do the thing\\n policy: # (optional) invocation policy\\n allow_implicit_invocation: false # only invoke explicitly via $skill\\n dependencies: # (optional) tool dependencies\\n tools:\\n - type: mcp\\n value: example\\n description: Example MCP tool\\npi: # for Pi Coding Agent-specific parameters (optional; Agent Skills standard)\\n # Authored either as a canonical list or as the spec\\'s space-delimited string;\\n # emitted to SKILL.md as the string, and imported back as the list.\\n allowed-tools:\\n - \"Bash\"\\n - \"Read\"\\n disable-model-invocation: true # (optional) disable model invocation for this skill\\n license: MIT # (optional)\\n compatibility: \"Requires git and jq\" # (optional) free-form string, 1–500 chars (an object is also accepted for back-compat)\\n metadata: # (optional) free-form metadata\\n author: rulesync\\nreplit: # for Replit Agent-specific parameters (optional; Agent Skills standard)\\n # Authored either as a canonical list or as the spec\\'s space-separated string;\\n # emitted to SKILL.md as the string, and imported back as the list.\\n allowed-tools:\\n - \"Bash\"\\n - \"Read\"\\n license: MIT # (optional)\\n compatibility: \"Requires git and docker\" # (optional) free-form string, 1–500 chars (an object is also accepted for back-compat)\\n metadata: # (optional) free-form metadata\\n author: rulesync\\ndeepagents: # for deepagents-cli (dcode)-specific parameters (optional; Agent Skills standard)\\n # Authored as a canonical list; emitted to SKILL.md as a space-delimited string\\n # (e.g. \"Bash Read\") because dcode rejects a YAML list at runtime.\\n allowed-tools:\\n - \"Bash\"\\n - \"Read\"\\n license: MIT # (optional)\\n compatibility: # (optional) free-form compatibility metadata\\n deepagents-version: \">=0.1.0\"\\n metadata: # (optional) free-form metadata\\n author: rulesync\\nopencode: # for OpenCode-specific parameters (optional)\\n license: MIT # (optional)\\n compatibility: # (optional) free-form compatibility metadata\\n opencode-version: \">=1.16.0\"\\n metadata: # (optional) free-form metadata\\n author: rulesync\\n allowed-tools: # (optional) Anthropic-spec passthrough; OpenCode ignores unknown fields\\n - \"Bash\"\\n - \"Read\"\\nkilo: # for Kilo Code-specific parameters (optional)\\n license: MIT # (optional)\\n compatibility: # (optional) free-form compatibility metadata\\n kilo-version: \">=7.0.0\"\\n metadata: # (optional) free-form metadata\\n author: rulesync\\n allowed-tools: # (optional) backward-compat passthrough; not part of Kilo\\'s official SKILL.md frontmatter\\n - \"Bash\"\\n - \"Read\"\\nkimi-code: # for Kimi Code-specific parameters (optional; project/global .kimi-code/skills/)\\n type: inline # (optional) prompt, inline, or flow\\n whenToUse: \"When reviewing pull requests\" # (optional) model invocation hint\\n disableModelInvocation: false # (optional) prevent automatic model invocation\\n arguments: [\"pull_request\"] # (optional) named arguments, also accepts a whitespace-separated string\\nagentsskills: # for the Agent Skills standard target (optional; supports project + global ~/.agents/skills/)\\n license: MIT # (optional)\\n compatibility: \"Requires Python 3.14+ and uv\" # (optional) free-form string, 1–500 chars (an object is also accepted for back-compat)\\n metadata: # (optional) free-form metadata (spec-recommended place for skill versioning)\\n version: \"1.0.0\"\\n allowed-tools: \"shell\" # (optional, experimental) space-separated string or list\\ncopilot: # for GitHub Copilot-specific parameters (optional; project .github/skills/, global ~/.copilot/skills/)\\n license: MIT # (optional)\\n allowed-tools: \"shell\" # (optional) tools pre-approved without per-use confirmation\\ncopilotcli: # for GitHub Copilot CLI-specific parameters (optional; project .github/skills/, global ~/.copilot/skills/)\\n license: MIT # (optional)\\n allowed-tools: \"shell\" # (optional) tools pre-approved without per-use confirmation\\n argument-hint: \"[message]\" # (optional) hint shown for the skill\\'s expected arguments\\n user-invocable: true # (optional, default true) whether users can run it with /SKILL-NAME\\n disable-model-invocation: false # (optional, default false) stop the agent from invoking it on its own\\nrovodev: # for Rovo Dev CLI-specific parameters (optional; Agent Skills standard)\\n allowed-tools: \"grep bash\" # (optional) space-separated string (a YAML list is also accepted)\\n license: MIT # (optional)\\n compatibility: \"Requires Python 3.14+ and uv\" # (optional) free-form string (object form also accepted)\\n metadata: # (optional) free-form metadata\\n author: rulesync\\nzed: # for Zed-specific parameters (optional)\\n disable-model-invocation: true # (optional) prevent the model from auto-invoking this skill\\ncursor: # for Cursor-specific parameters (optional)\\n paths: # (optional) glob patterns (string or list) scoping the skill to matching files\\n - \"src/**/*.ts\"\\n disable-model-invocation: true # (optional) only include the skill when invoked via /skill-name\\n metadata: # (optional) free-form metadata\\n author: rulesync\\nfactorydroid: # for Factory Droid-specific parameters (optional)\\n disable-model-invocation: true # (optional) prevent the model from auto-invoking this skill\\n user-invocable: false # (optional) hide from the slash-command menu, keep model access\\ntakt: # takt specific parameters (optional; emitted under .takt/facets/knowledge/ — frontmatter is dropped on emit)\\n name: \"renamed-stem\" # (optional) override the emitted filename stem (no path separators or \"..\")\\n extends: \"base\" # (optional) emit a leading `{extends:<parent>}` facet-inheritance directive (Takt 0.39.0+)\\ndevin: # for Devin-specific parameters (optional; project .devin/skills/, global ~/.config/devin/skills/)\\n argument-hint: \"[environment]\" # (optional) hint shown after the slash-command name\\n model: \"fast\" # (optional) model override while the skill runs\\n subagent: true # (optional) run the skill in a subagent (string or boolean per Devin\\'s docs)\\n agent: \"deployer\" # (optional) named agent profile to run the skill with\\n allowed-tools: # (optional) tools available while the skill runs (string or list)\\n - \"Bash(git status:*)\"\\n permissions: {} # (optional) auto-approval rules applied while the skill runs (load-bearing since Devin CLI v3000.1.23)\\n triggers: [\"user\"] # (optional) invocation gating; omitted = user + model. The shared disable-model-invocation / user-invocable flags map onto this when unset.\\nqwencode: # for Qwen Code-specific parameters (optional; project .qwen/skills/, global ~/.qwen/skills/)\\n priority: 10 # (optional) higher values appear earlier in /skills listings\\n paths: # (optional) glob patterns gating model discovery to matching files (a scalar is coerced to the array Qwen Code requires)\\n - \"src/**/*.ts\"\\n user-invocable: false # (optional) hide from slash-command invocation, keep model access\\n disable-model-invocation: true # (optional) hide from the model but allow direct user invocation\\n allowedTools: # (optional) permissions.allow-syntax rules auto-approved while the skill is active\\n - \"Shell(git status:*)\"\\n model: \"fast\" # (optional) model override while the skill runs (model id, fast, authType:modelId, inherit)\\n hooks: {} # (optional) session-scoped hooks registered while the skill runs (settings.json shape)\\n when_to_use: \"Use when deploying\" # (optional) invocation guidance surfaced in the SkillTool description\\n argument-hint: \"[environment]\" # (optional) hint shown after the slash-command name in completion\\ngrokcli: # for Grok CLI-specific parameters (optional)\\n user-invocable: false # (optional) hide from the skill tool, keep model access\\n disable-model-invocation: true # (optional) block auto-invocation, keep the slash command\\nvibe: # for Vibe Code-specific parameters (optional)\\n user-invocable: false # (optional) hide from slash-command invocation, keep model access\\n allowed-tools: \"Bash Read\" # (optional) space-delimited or list of allowed tool names\\n---\\n\\nThis is the skill body content.\\n\\nYou can provide instructions, context, or any information that helps the AI agent understand and execute this skill effectively.\\n\\nThe skill can include:\\n\\n- Step-by-step instructions\\n- Code examples\\n- Best practices\\n- Any relevant context\\n\\nSkills are directory-based and can include additional files alongside SKILL.md.\\n\\nWhen `claudecode.scheduled-task: true` is set, that skill is emitted only as a Claude Code scheduled task and is not emitted to other tools even if `targets` contains `\"*\"`.\\n```\\n\\n> **`.agents/skills/` ownership note:** `.agents/skills/` is not an AGENTS.md convention — the AGENTS.md standard defines only `AGENTS.md` itself. It is the [Agent Skills](https://agentskills.io/specification) project location, which the native `agentsskills` target writes. Several targets write there — `agentsskills`, `agentsmd`, `aiassistant`, `codexcli`, `amp`, `zed`, `replit` and both Antigravity targets — because they all implement the same convention. Each native target writes its own documented frontmatter, so enabling more than one and reordering `--targets` can change which optional keys end up in the file; that is inherent to several tools sharing one path and is not specific to any of them.\\n\\n> The **simulated** `agentsmd` writer is the exception that is fixed: it has no frontmatter model of its own (the AGENTS.md standard defines no skills at all), so it used to overwrite the native output with a bare `name`/`description` pair and silently drop `license`, `compatibility`, `metadata` and `allowed-tools`. It now emits exactly what `agentsskills` emits, so a simulated writer can never degrade the file a native target owns.\\n\\n> **Note:** `claudecode.disallowed-tools` (a space/comma-separated string or a YAML list) removes the listed tools from the model while the skill is active. The same field is available on Claude Code slash commands. Both round-trip through the `claudecode` frontmatter section.\\n\\n> **Note:** Codex CLI reads UI metadata, invocation policy, and tool dependencies from an `agents/openai.yaml` sidecar next to `SKILL.md` (Codex\\'s `SKILL.md` frontmatter only carries `name` and `description`). When `codexcli.interface`, `codexcli.policy`, or `codexcli.dependencies` is present, Rulesync emits `.agents/skills/<name>/agents/openai.yaml` and reads it back on import. If the sidecar is emitted and `interface.short_description` is absent, the legacy `codexcli.short-description` is routed there. See the [Codex skills docs](https://developers.openai.com/codex/skills.md).\\n\\n> **Reasonix note:** Reasonix discovers Anthropic-style directory-layout skills (`<name>/SKILL.md`) under `.reasonix/skills/` (project) / `~/.reasonix/skills/` (global, via `--global`). Rulesync emits the portable `name`/`description` frontmatter (Reasonix supports additional optional keys, but only that pair is modeled); the schema is loose, so any extra keys on an imported `SKILL.md` survive the round-trip. See the [Reasonix GUIDE](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/GUIDE.md).\\n\\n> **Hermes Agent note:** Hermes skills are global-only under `~/.hermes/skills/<name>/SKILL.md`. Standard Agent Skills fields (`license`, `compatibility`, and `allowed-tools`) round-trip through `agentsskills` and are normalized to the Agent Skills spec shapes described above (so `allowed-tools` is written and imported the same way as for `agentsskills`); Hermes-native fields such as `version`, `author`, `platforms`, `environments`, `required_environment_variables`, `required_credential_files`, and `metadata.hermes` round-trip through `hermesagent`. Canonical `name` and `description` always own those two frontmatter keys.\\n\\n> **Kimi Code note:** Kimi Code discovers skills under `.kimi-code/skills/` (project) and `~/.kimi-code/skills/` (global), plus the shared `.agents/skills/` root at either scope. Rulesync generates the recommended directory layout (`<name>/SKILL.md`) and imports both that layout and flat `<name>.md` skills; for flat files, a missing `name` comes from the filename and a missing `description` falls back to the first non-empty body line (up to 240 characters), matching Kimi. Imported skills are written to `.rulesync/skills/<logical-name>/SKILL.md`, using the normalized logical frontmatter name rather than the source directory or filename. Duplicate precedence follows Kimi\\'s case-insensitive logical frontmatter `name`: the Kimi-specific root takes precedence over `.agents/skills/`, and a directory skill takes precedence over a same-named flat file within one root. Shared roots are import-only and are never removed by Kimi-target orphan deletion. Besides `name`/`description`, Rulesync maps Kimi\\'s `type`, `whenToUse`, `disableModelInvocation`, and `arguments` frontmatter through the `kimi-code:` block and preserves supporting files beside directory-layout `SKILL.md`. The shared top-level `disable-model-invocation` value supplies the Kimi flag unless the tool-specific block overrides it. See the [Kimi Code Agent Skills docs](https://moonshotai.github.io/kimi-code/en/customization/skills.html).\\n\\n## `.rulesync/mcp.jsonc`\\n\\n`.rulesync/mcp.jsonc` is the recommended source path and accepts comments and trailing commas. The legacy `.rulesync/mcp.json` path remains readable for existing projects. When both files exist, the JSONC file takes precedence; write flows update the existing source instead of creating a second variant.\\n\\nExample:\\n\\n```json\\n{\\n \"mcpServers\": {\\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/mcp-schema.json\",\\n \"serena\": {\\n \"description\": \"Code analysis and semantic search MCP server\",\\n \"type\": \"stdio\",\\n \"command\": \"uvx\",\\n \"args\": [\\n \"--from\",\\n \"git+https://github.com/oraios/serena\",\\n \"serena\",\\n \"start-mcp-server\",\\n \"--context\",\\n \"ide-assistant\",\\n \"--enable-web-dashboard\",\\n \"false\",\\n \"--project\",\\n \".\"\\n ],\\n \"env\": {}\\n },\\n \"context7\": {\\n \"description\": \"Library documentation search server\",\\n \"type\": \"stdio\",\\n \"command\": \"npx\",\\n \"args\": [\"-y\", \"@upstash/context7-mcp\"],\\n \"env\": {}\\n }\\n }\\n}\\n```\\n\\n### Tool-scoped server blocks (`{toolname}.mcpServers`)\\n\\nServers under the shared `mcpServers` key are emitted to every targeted tool. To scope a server to a single tool, add a tool-scoped `{toolname}` block alongside it — mirroring `{toolname}.hooks` in `.rulesync/hooks.jsonc` and `{toolname}.permission` in `.rulesync/permissions.jsonc`:\\n\\n```jsonc\\n{\\n \"mcpServers\": {\\n \"shared-server\": { \"type\": \"stdio\", \"command\": \"echo\" },\\n },\\n \"claudecode\": {\\n \"mcpServers\": {\\n // Added only to Claude Code\\'s MCP config.\\n \"claude-only-server\": { \"type\": \"http\", \"url\": \"https://example.com/mcp\" },\\n // `null` removes a shared server for Claude Code only.\\n \"shared-server\": null,\\n },\\n },\\n}\\n```\\n\\n- A tool-scoped entry with the same name as a shared server **replaces it wholesale** for that tool (no field-level merge).\\n- A tool-scoped entry set to `null` **removes** the shared server for that tool.\\n- Any MCP-capable `--targets` name is accepted as a block key (`claudecode`, `cursor`, `codexcli`, ...). Targets that share one output file resolve identically so the shared file never depends on generation order: the deprecated `claudecode-legacy` target reads the `claudecode` block; the `kiro-cli` / `kiro-ide` targets read the `kiro` block (all three write the same `.kiro/settings/mcp.json`); and the `antigravity-ide` / `antigravity-cli` targets both apply both `antigravity-*` blocks in a fixed order (`antigravity-ide` first, then `antigravity-cli` — the CLI block wins per server) because they share their output file at both scopes (`.agents/mcp_config.json` in project mode, `~/.gemini/config/mcp_config.json` in global mode).\\n\\n> **Generation filter: per-server `enabled`.** Set `\"enabled\": false` on a server (in the shared map or a tool-scoped block) to keep the definition in the source file while emitting it to **no** tool config at all — a temporary off switch that does not lose the entry. Omitted means enabled, so existing configs keep generating everything; writing `\"enabled\": true` is opt-in clarity. This is distinct from the canonical `disabled`, which is a **pass-through** field the tools read (written as `disabled: true`, or translated to each tool\\'s own spelling): `enabled: false` wins and drops the server entirely, while `disabled` only matters for servers still emitted. The field is rulesync-source-only and never reaches generated output — several tools (OpenCode, Kilo, Grok CLI, Goose) have a native `enabled` field with different semantics — and import never invents it: a tool\\'s native enabled/disabled state keeps mapping to the canonical `disabled` (though a stray hand-written `enabled` in a passthrough-imported tool file does come back as the canonical filter). Two edges to know: a tool-scoped entry **replaces the shared entry wholesale**, so a same-named tool-scoped entry without `enabled: false` re-emits the server for that tool (per-tool re-enabling); and on merge-style shared configs (e.g. Hermes Agent\\'s `config.yaml`), disabling a previously generated server stops writing it but does not remove the already-written entry — same as deleting the definition.\\n\\n> **Deprecated: per-server `targets`.** The older per-server `\"targets\": [\"tool\", ...]` array is still honored as a filter (a missing value or `[\"*\"]` means every tool), but it is deprecated and logs a warning at generate time. Migrate by moving the server into the matching `{toolname}.mcpServers` block(s).\\n\\n> **JetBrains AI Assistant note:** Rulesync writes the native `{ \"mcpServers\": { ... } }` configuration to `.ai/mcp/mcp.json` in project mode and `~/.ai/mcp/mcp.json` in global mode. Both scopes support STDIO and remote server entries using the shape documented in [JetBrains AI Assistant\\'s MCP guide](https://www.jetbrains.com/help/ai-assistant/mcp.html).\\n\\n#### JSON Schema Support\\n\\nRulesync provides a JSON Schema for editor validation and autocompletion. Add the `$schema` property to your `.rulesync/mcp.jsonc`:\\n\\n```json\\n{\\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/mcp-schema.json\",\\n \"mcpServers\": {}\\n}\\n```\\n\\n### Transport types (`type` / `transport`)\\n\\nThe `type` (and the equivalent `transport`) field accepts `local`, `stdio`, `sse`, `http`, `ws`, and `streamable-http`. `streamable-http` is the MCP specification\\'s name for the HTTP transport and is accepted as an alias of `http`, so configurations copied from a server\\'s documentation work unchanged. `ws` is the WebSocket transport (a persistent bidirectional connection) and accepts the same `url`/`headers`/`headersHelper`/`timeout` fields as `http`. Tools that do not recognize a given transport keep it on round-trip but may ignore it at runtime.\\n\\n> **OpenCode skills note:** on import, Rulesync also reads the `skills.paths` array of `opencode.json` / `opencode.jsonc` (\"Additional paths to skill folders\") and scans each entry as an extra skill root, so skills a project keeps outside `.opencode/skills/` are no longer invisible to `rulesync import`. These roots are import-only — generation keeps writing to Rulesync\\'s own managed root — and a skill of the same name found in a managed root still wins. Each entry is resolved against the directory the config was read from — the project root in project mode, `~/.config/opencode/` in global mode — which is what OpenCode itself does. An absolute path, or one that escapes that directory, is ignored, and a directory under a configured root that is not a skill is skipped with a warning rather than failing the run, since a configured root is arbitrary user territory. `skills.urls` is a remote-fetch surface and is out of scope for a file-based generator.\\n\\n> **Kilo Code note:** Kilo\\'s MCP config uses its own native shape in `kilo.jsonc` (`type: \"local\" | \"remote\"`, `environment`, `enabled`, `command` as an array). Rulesync maps `stdio`/`local` ⇄ Kilo `local` and `http`/`sse` ⇄ Kilo `remote`; on import, Kilo `remote` is normalized to the canonical `http` transport (the deprecated `sse` is no longer emitted). The Kilo-specific `timeout` (local + remote, a positive integer in milliseconds) and `oauth` (remote only — either an OAuth-config object or `false` to disable auto-detection) fields are preserved on round-trip. The `kilo.jsonc` `skills` config key (`skills.paths` for extra skill locations and `skills.urls` for remote skill manifests) is likewise preserved when Rulesync writes the file. A bare `{\"enabled\": false}` entry — Kilo\\'s way of switching off a server another config layer defines, such as the global config or a marketplace — round-trips as itself: it imports as a canonical server carrying only `disabled: true`/`disabled: false` and no transport, and a server in that shape is written back as `{\"enabled\": …}` rather than as a local server with an empty command it cannot start. The enabled state has to be stated outright in both directions: for a transport-less server that says nothing about `disabled`, a toggle already in `kilo.jsonc` is left exactly as it is, and if there is none the server is skipped with a warning — a toggle overrides the layer that defines the server, so writing `enabled: true` for it would switch back on what you turned off there. Kilo\\'s per-tool `enabledTools`/`disabledTools` reach the generated file at all now — they used to be stripped before this adapter saw them, so a filter read out of `kilo.jsonc` was deleted from it on the next generate. A skipped server\\'s filters are written to the `tools` map either way, since that map is keyed by server name and reaches servers `mcp` does not list; on import, a `tools` entry naming no listed server comes back as a server carrying nothing but the filters, so it survives the round-trip. A server with no transport — a toggle, or one of those filter-only entries — is imported into the tool-scoped `kilo.mcpServers` block rather than the shared `mcpServers` map, because an entry with no command and no url is a server the other tools\\' configs cannot start. All of this applies equally to OpenCode: its published schema carries the same bare-toggle union member, it round-trips a toggle as itself under the same explicit-state rule, its `tools` map works the same way, and its transport-less servers land in `opencode.mcpServers`. The entry must carry no field of a local or remote server (`type`, `command`, `url`, `headers`, `environment`, `cwd`, `timeout`, `oauth`); an entry that is malformed in some other way still fails loudly rather than being quietly read as a toggle and written back with its command, headers, or OAuth secrets gone, while an unrelated key Kilo adds later is accepted rather than failing the run (it is not carried across the round-trip, though — a toggle imports as its enabled state and nothing else). Since a toggle keeps nothing but its enabled state, a canonical server that declares no transport but still carries fields such as `args` or `env` is written as a toggle with those fields dropped and a warning naming them. A server that names a transport it cannot reach — a `type` with no `command`, an `http` with no `url` — is skipped with a warning instead, because `{\"type\": \"local\", \"command\": []}` is a server Kilo cannot start. An existing `kilo.jsonc` carrying that shape (earlier Rulesync versions wrote it) imports as a server with no transport rather than failing the run. The same applies to OpenCode, whose config uses the same shape. Rejecting it used to fail the whole `--targets kilo` run rather than the MCP feature alone, because `kilo.jsonc` is the file the rules feature writes too.\\n\\n> **Zed note:** Zed configures MCP servers under `context_servers` in its shared settings file (`.zed/settings.json` project, `~/.config/zed/settings.json` global — `%APPDATA%\\\\Zed\\\\settings.json` on Windows), whose value is an untagged shape with no `type` field: a stdio server is `{\"command\": <string>, \"args\", \"env\", \"timeout\"}`, a remote one `{\"url\", \"headers\", \"timeout\"}`, and an extension-provided one neither. Rulesync translates the canonical fields into those shapes instead of forwarding them verbatim (which used to hand Zed keys it silently ignores — most seriously `disabled: true`, which left the server **enabled**): `disabled: true` becomes `enabled: false` (and imports back as `disabled: true`), the `httpUrl` alias is normalized to `url`, an array `command` is flattened to Zed\\'s single command string with the rest prepended to `args`, and canonical-only fields (`type`/`transport`, `alwaysAllow`, `trust`, `cwd`, `networkTimeout`, the Kiro lists) are dropped. Fields rulesync does not model — a remote server\\'s `oauth` block, an extension server\\'s `settings` — pass through untouched, so they are best authored in the tool-scoped `zed.mcpServers` block. A server Zed cannot start is skipped with a warning rather than written broken: an `sse` or `ws` server (Zed has neither transport), a remote server with no `url`, a local one with no `command`. A server with no transport at all is written as Zed\\'s extension-provided variant, and on import such an entry lands in the tool-scoped `zed.mcpServers` block rather than the shared `mcpServers` map, since other tools cannot start it.\\n\\n> **Kimi Code note:** MCP servers are written to `.kimi-code/mcp.json` (project) and `~/.kimi-code/mcp.json` (global). Kimi Code supports stdio, HTTP, and SSE plus `env`, `cwd`, `headers`, `bearerTokenEnvVar`, `enabled`, `startupTimeoutMs`, `toolTimeoutMs`, `enabledTools`, and `disabledTools`; Rulesync preserves the canonical fields that Kimi accepts. Canonical `local` maps to stdio and `streamable-http` maps to HTTP. WebSocket servers are skipped with a warning because Kimi has no WebSocket transport. A `kimi-code` block may also carry `startupTimeoutMs` / `toolTimeoutMs`, which are **not** per-server: they become Kimi\\'s `[mcp] startup_timeout_ms` / `tool_timeout_ms` defaults in the shared global `~/.kimi-code/config.toml`, applying to every MCP server including ones Rulesync did not write (a per-server value in `mcp.json` still wins). Global scope only, since `config.toml` has no project counterpart, and merged in place so the `hooks` and `permission` sections of the same file survive. The merge is per key: authoring only one of the two timeouts leaves a hand-written sibling alone, and dropping the override entirely leaves the section as it stands rather than deleting it — remove the keys from `config.toml` by hand if you want them gone. See the [Kimi Code MCP docs](https://moonshotai.github.io/kimi-code/en/customization/mcp.html) and [config-files reference](https://moonshotai.github.io/kimi-code/en/configuration/config-files.html#mcp).\\n\\n> **Hermes Agent note:** Hermes MCP servers live under `mcp_servers` in the shared `~/.hermes/config.yaml`. Rulesync preserves OAuth fields (`redirect_uri`, `redirect_host`, `redirect_port`, `client_id`, `client_secret`, and `scopes`) plus `idle_timeout_seconds`, `max_lifetime_seconds`, `ssl_verify` (`true`/`false` or a PEM CA-bundle path), `skip_preflight`, and the `sampling` mapping (carried as an opaque object so new sub-keys keep working). On import, portable server fields remain in shared `mcpServers`; Hermes-only fields are isolated in the full `hermesagent.mcpServers.<name>` replacement block so they cannot leak to other targets.\\n\\n> **Devin note:** Since Devin v3000.3 (the Local 3.6 release), MCP servers live in a dedicated `mcpServers`-keyed file: `.devin/mcp_config.json` (project) and `~/.config/devin/mcp_config.json` (global, via `--global`). The file is MCP-only and rulesync-owned (rewritten whole, deletable), unlike the shared `.devin/config.json` that permissions and hooks keep patching in place. Rulesync no longer writes the legacy `config.json` `mcpServers` key — Devin auto-migrates it away on startup, so re-seeding it would fight the migration — but import still falls back to that key when no `mcp_config.json` exists, so pre-v3000.3 repos migrate cleanly. The gitignored personal override `.devin/mcp_config.local.json` is never read or written (it is covered by the derived `.gitignore`). See the [Devin MCP configuration docs](https://docs.devin.ai/cli/extensibility/mcp/configuration).\\n\\n> **Warp note:** Warp reads file-based MCP servers from `.warp/.mcp.json` (project) and `~/.warp/.mcp.json` (global). Warp spells the working directory `working_directory` (used for resolving relative paths), so the canonical `cwd` is translated to it on generate and back on import; a tool-native `working_directory` already on the server wins over `cwd`. See the [Warp MCP docs](https://docs.warp.dev/agent-platform/capabilities/mcp/).\\n\\n> **Takt note (partial / transport-allowlist only):** Takt does **not** have a project- or global-level registry of MCP server _definitions_. The concrete `mcp_servers` map (`command`/`args`/`env` or `type`/`url`/`headers`) is declared **per workflow step** inside individual workflow YAML files; there is no top-level `mcp_servers` key in `config.yaml`, and Takt\\'s config loader hard-rejects unknown top-level keys (introduced with MCP support in [Takt v0.21.0](https://github.com/nrslib/takt/blob/main/CHANGELOG.md)). What `config.yaml` _does_ hold is the **default-deny transport allowlist** `workflow_mcp_servers: { stdio, sse, http }` — without it, workflow-defined MCP servers are refused regardless of how they are declared. So Rulesync emits **only** this allowlist into the shared `.takt/config.yaml` (project) / `~/.takt/config.yaml` (global), enabling exactly the transports your `.rulesync/mcp.jsonc` servers use (`local`/`stdio` ⇒ `stdio`; `sse` ⇒ `sse`; `http`/`streamable-http`/`ws` ⇒ `http`). The merge is in place — every other top-level key (`provider`, `provider_profiles`, …) is preserved and the file is never deleted. **Documented lossiness:** per-server names, commands, env, URLs, and headers are not representable in `config.yaml` and are intentionally **not** written; you still declare the concrete servers in your workflow YAML steps, and Rulesync only opens the transport gate that permits them. As a corollary, **import** cannot reconstruct server definitions from a transport allowlist and yields an empty `mcpServers` map. See the [Takt configuration docs](https://github.com/nrslib/takt/blob/main/docs/configuration.md).\\n\\n### MCP Tool Config (`enabledTools` / `disabledTools`)\\n\\nYou can control which individual tools from an MCP server are enabled or disabled using `enabledTools` and `disabledTools` arrays per server.\\n\\n```json\\n{\\n \"mcpServers\": {\\n \"serena\": {\\n \"type\": \"stdio\",\\n \"command\": \"uvx\",\\n \"args\": [\"--from\", \"git+https://github.com/oraios/serena\", \"serena\", \"start-mcp-server\"],\\n \"enabledTools\": [\"search_symbols\", \"find_references\"],\\n \"disabledTools\": [\"rename_symbol\"]\\n }\\n }\\n}\\n```\\n\\n- `enabledTools`: An array of tool names that should be explicitly enabled for this server.\\n- `disabledTools`: An array of tool names that should be explicitly disabled for this server.\\n\\n> **Kiro note:** Kiro MCP servers are written under `mcpServers` in `.kiro/settings/mcp.json` (project) and `~/.kiro/settings/mcp.json` (global). Kiro supports `disabledTools` natively and Rulesync preserves it on generate and import. Kiro does not expose a corresponding per-server `enabledTools` allowlist, so that field is omitted for Kiro targets.\\n\\n> **Qwen Code note:** MCP servers are written to the `mcpServers` key of `.qwen/settings.json` (project) / `~/.qwen/settings.json` (global, via `--global`). Qwen supports stdio (`command`/`args`), SSE (`url`), and HTTP (`httpUrl`) transports. Rulesync maps the canonical per-server `enabledTools` ⇄ Qwen\\'s `includeTools` (allowlist) and `disabledTools` ⇄ Qwen\\'s `excludeTools` (denylist). Other top-level keys in `settings.json` are preserved on round-trip.\\n\\n> **Codex CLI server-name note:** Codex requires MCP server names matching `[a-zA-Z0-9_-]+`, so Rulesync auto-normalizes non-conforming names on generate (lowercase, runs of other characters become `_`, leading/trailing `_` trimmed) — e.g. `Postgres MCP - Production - Read Only` becomes `postgres_mcp_production_read_only`. If two names normalize to the same Codex name, the last processed server overwrites the earlier one (with a warning). A name with no representable characters at all (e.g. a fully Japanese name) falls back to a stable hash-derived name like `mcp_1a2b3c4d` instead of being dropped; rename the server in `.rulesync/mcp.jsonc` to pick a readable Codex name. This normalization is one-way: importing back from the generated `config.toml` yields the normalized name, not the original.\\n\\n### Codex-specific: pass shell env vars to MCP servers (`envVars`)\\n\\nCodex CLI supports a per-server array of shell env var names to inherit when launching the MCP server process. The source schema uses `envVars` (camelCase, matching the project convention used by sibling fields like `enabledTools`/`disabledTools`); the codex generator renames it to `env_vars` (snake_case) for codex\\'s native `config.toml` format.\\n\\nThis is distinct from `env` (which is a literal `{name: value}` map) — `envVars` is a list of names whose **values come from the user\\'s environment at runtime**. Both fields may coexist on the same server.\\n\\n```json\\n{\\n \"mcpServers\": {\\n \"pal\": {\\n \"type\": \"stdio\",\\n \"command\": \"uvx\",\\n \"args\": [\\n \"--from\",\\n \"git+https://github.com/BeehiveInnovations/pal-mcp-server.git\",\\n \"pal-mcp-server\"\\n ],\\n \"envVars\": [\"OPENAI_API_KEY\", \"OPENROUTER_API_KEY\", \"GEMINI_API_KEY\"]\\n }\\n }\\n}\\n```\\n\\nGenerated `~/.codex/config.toml`:\\n\\n```toml\\n[mcp_servers.pal]\\ntype = \"stdio\"\\ncommand = \"uvx\"\\nargs = [\"--from\", \"git+https://github.com/BeehiveInnovations/pal-mcp-server.git\", \"pal-mcp-server\"]\\nenv_vars = [\"OPENAI_API_KEY\", \"OPENROUTER_API_KEY\", \"GEMINI_API_KEY\"]\\n```\\n\\nAn entry may also be an object naming the environment to read the variable from: `{ \"name\": \"REMOTE_TOKEN\", \"source\": \"remote\" }` reads it from the remote executor environment (and requires remote MCP stdio support), while a bare name and `\"source\": \"local\"` read from Codex\\'s own environment. The object form is written to `config.toml` as an inline table, matching Codex\\'s documented shape. Only `name` and `source` are accepted in that object — Codex rejects an unknown key there, and rejecting one server\\'s entry would take the whole `config.toml` down with it, so Rulesync fails on the canonical file instead. For the same reason an entry that a `config.toml` already holds in some other shape is dropped with a warning on import rather than written into a `.rulesync/mcp.jsonc` the next generate would refuse.\\n\\n- Emitted only into the codex CLI output. Stripped from `RulesyncMcp.getMcpServers()` so it does not appear in other tools\\' generated configs (Claude Code, Kilo, OpenCode, Gemini CLI, Cursor, Cline, Junie, Factorydroid, Rovodev, etc.).\\n- Use this for secrets and API keys you do not want literal-encoded into a committed `mcp.json`.\\n- Precedence: codex CLI resolves these names from the user\\'s runtime shell environment. If a name is also set in `env` (literal value), the codex CLI behavior is upstream-defined; see the [Codex configuration reference](https://developers.openai.com/codex/config-reference#mcp_serversid-env_vars) (last checked 2026-05-13) for the exact resolution rule.\\n\\n### Codex-specific: run a stdio server remotely (`experimentalEnvironment`)\\n\\nFor stdio servers, `experimentalEnvironment: \"remote\"` starts the server through a remote executor environment when one is available. It is written as `experimental_environment` in `config.toml`. Like `envVars`, it is stripped before every other tool\\'s MCP config is written, so it cannot leak into a config that would not understand it — and for the same reason, a server config copied straight out of a `config.toml` may spell it `experimental_environment`, which is accepted and normalized on the way to Codex.\\n\\nSee the [Codex MCP reference](https://learn.chatgpt.com/docs/extend/mcp) for both fields.\\n\\n#### Codex-specific: OAuth client id (`oauth.clientId` → `client_id`)\\n\\nA server\\'s `oauth` block is preserved in the canonical Claude Code shape (camelCase `clientId`), but Codex CLI reads the OAuth client id from snake_case `oauth.client_id`. Without it, `codex mcp login <server>` falls back to dynamic client registration and fails for providers that do not support it (e.g. Slack). The codex generator therefore **duplicates** `clientId` into a sibling `client_id`, keeping the camelCase key so tools that expect it keep working:\\n\\n```toml\\n[mcp_servers.slack.oauth]\\nclientId = \"1601185624273.8899143856786\"\\nclient_id = \"1601185624273.8899143856786\"\\ncallbackPort = 3118\\n```\\n\\nOnly a string `clientId` is duplicated (a non-string value would not be a usable OAuth client id), and an explicit `client_id` already present in the source is left untouched. On import, `client_id` collapses back to the canonical `clientId` (and is dropped when both are present) so the round-trip stays stable.\\n\\n> **Grok CLI note:** MCP servers are written to a `[mcp_servers.<name>]` table in `.grok/config.toml` (project) / `~/.grok/config.toml` (global, via `--global`). The file is treated as shared Grok config: Rulesync only replaces the `mcp_servers` key and preserves every other table on round-trip, and it is never deleted. Unlike Codex CLI, Grok uses a literal `env` table (it does not support the `env_vars` runtime-passthrough list) and has no per-server tool allow/deny lists, so the only field rename is `disabled` (rulesync) ⇄ `enabled = false` (grok); an active server simply omits `enabled`. Servers with no environment variables are emitted without a dangling `[mcp_servers.<name>.env]` table (empty nested tables are stripped), and a server whose entire configuration would be empty is dropped with a warning.\\n\\n### Goose-specific: MCP servers as `extensions` (global) and open-plugin manifest (project)\\n\\nGoose configures MCP servers in two locations depending on scope:\\n\\n- **Global (`--global`):** MCP servers are written as **extensions** in the shared user config `~/.config/goose/config.yaml`. The schema is non-standard, so Rulesync maps canonical MCP fields to Goose\\'s: `command` → `cmd` (an array `command` folds its tail into `args`), `env` → `envs`, `url`/`httpUrl` → `uri`, and `disabled: true` → `enabled: false`. The `type` is derived — `command` ⇒ `stdio`, a remote `url` ⇒ `streamable_http` (or `sse` when the canonical `type` is `sse`). Each extension also carries its own `name`. Generation merges the `extensions:` block into the existing `config.yaml`, preserving other Goose settings (model, provider, ...), and the file is never deleted. This location supports **both stdio and remote** (http/sse) servers.\\n- **Project:** Goose v1.39.0+ discovers MCP extensions in **open plugins** at `<project>/.agents/plugins/<name>/.mcp.json` (and `~/.agents/plugins/<name>/.mcp.json` at user scope). Rulesync emits `.agents/plugins/rulesync/.mcp.json`, reusing the same `.agents/plugins/rulesync/` tree already used for Goose hooks. The manifest uses the **Claude-style** `{ \"mcpServers\": { \"<name>\": { \"command\", \"args\", \"env\", \"cwd\" } } }` shape. This manifest is **stdio-only** — it cannot express `url`/`headers`, so **remote (http/sse) servers are skipped with a warning** in project mode; sync them with `--global` to `~/.config/goose/config.yaml` instead. The `.mcp.json` manifest is owned by Rulesync and is deleted when no servers remain.\\n\\nSee the [Goose extensions docs](https://block.github.io/goose/docs/getting-started/using-extensions/) and [open-plugins MCP PR #9471](https://github.com/block/goose/pull/9471).\\n\\n### Goose-specific: commands as recipes, subagents as custom agents\\n\\nGoose [recipes](https://block.github.io/goose/docs/guides/recipes/recipe-reference/) are reusable YAML workflow files. **Commands** map to top-level recipes at `.goose/recipes/<name>.yaml` (project) and `~/.config/goose/recipes/<name>.yaml` (global); the command body becomes the recipe `prompt`, `title` defaults to the file name and `description` to the rulesync `description` (falling back to `title`), `version` defaults to `1.0.0`, and any other recipe field round-trips through the rulesync `goose` section of a command.\\n\\n**Subagents** map to Goose\\'s [custom agents](https://block.github.io/goose/docs/guides/context-engineering/custom-agents/) (v1.34.0+): Markdown files with `name` (required) / `description` / `model` frontmatter whose body is the agent instructions, invocable via `@name` or delegation. They are emitted to the goose-specific discovery dirs `.goose/agents/<name>.md` (project) and `~/.config/goose/agents/<name>.md` (global), so the output cannot collide with a future shared `.agents/agents/` target; `model` and unknown future fields round-trip through the rulesync `goose` subagent section. Earlier rulesync versions emitted subagents as sub-recipe YAML under `.goose/recipes/subagents/` — a location Goose\\'s agent discovery never scans, so those files were inert; they are no longer generated (stale outputs stay gitignored but are not cleaned up automatically).\\n\\n### Vibe-specific: stdio `cwd` and MCP `[auth]` block\\n\\nVibe (mistral-vibe) MCP servers live in `[[mcp_servers]]` arrays of the shared `.vibe/config.toml`. In addition to the flat fields, Rulesync passes through the stdio `cwd` (working directory), a structured per-server `auth` block (Vibe v2.15.0+), and the four keys Vibe\\'s `/mcp` panel writes back when you toggle a server or one of its tools — `prompt`, `sampling_enabled`, `disabled` and `disabled_tools`. Because `mcp_servers` is replaced as a whole array on each generate, a server Rulesync writes is seeded from the on-disk entry of the same name for exactly those keys, so a toggle you made in the TUI survives — unless your `.rulesync/mcp.json` states the value itself, which wins. `disabled_tools` is the canonical `disabledTools` under Vibe\\'s spelling; `prompt` and `sampling_enabled` have no canonical equivalent and pass through as-is. The `auth` table is discriminated on `type`: `static` (`headers`, `api_key_env`, `api_key_header`, `api_key_format`) and `oauth` (`scopes`, `client_id` / `client_metadata_url`, `redirect_port`). Because Vibe rejects mixing legacy top-level static-auth keys with an explicit `[auth]` block, Rulesync suppresses the legacy keys (`headers`/`api_key_env`/`api_key_header`/`api_key_format`) whenever a server carries an `auth` block. See [mistral-vibe](https://github.com/mistralai/mistral-vibe) (`vibe/core/config/models.py`).\\n\\n> **GitHub Copilot (VS Code) MCP note:** the `copilot` target writes `.vscode/mcp.json`, which has three documented top-level sections: `servers`, `inputs` (secret prompts referenced as `${input:id}`) and `sandbox` (filesystem/network rules for sandboxed servers, added in VS Code v1.112). Rulesync owns and replaces only `servers`; the rest of the document — including any future top-level section — is read back and preserved on each generate. VS Code recommends committing this file, so dropping an `inputs` entry would leave `${input:…}` unresolvable and the affected servers would fail to start. If the existing file cannot be parsed, generate fails with an error rather than overwriting it. See the [MCP configuration reference](https://code.visualstudio.com/docs/agents/reference/mcp-configuration).\\n\\n> **Rovo Dev CLI MCP note:** Rovo Dev documents the per-server transport key as `transport` (`stdio` | `http` | `sse`), not the canonical `type`. Rulesync translates on the way out (`local` → `stdio`, `streamable-http` → `http`) and back on import; `ws` has no Rovo Dev equivalent, so those servers are skipped with a warning, and a `transport` value outside Rovo Dev\\'s vocabulary is dropped on import rather than written into the canonical config, whose transport field is a strict enum. A server marked `disabled` is also skipped, because Rovo Dev turns servers off through `mcp.disabledMcpServers` in `config.yml` rather than through a flag in `mcp.json`, and writing the entry anyway would leave it running; `disabled` is stripped from the servers that are written, too, since `mcp.json` is not where a server is switched on and off. A server Rovo Dev has disabled through `config.yml` is still imported as an ordinary enabled server, because `mcp.json` carries no trace of it. `mcp.json` is global-only. See the [Rovo Dev MCP docs](https://support.atlassian.com/rovo/docs/connect-to-an-mcp-server-in-rovo-dev-cli/).\\n\\n> **Reasonix note:** MCP servers are written as `[[plugins]]` array-of-tables entries (Reasonix\\'s MCP-compatible external plugins) in `reasonix.toml` (project) / `~/.reasonix/config.toml` (global, via `--global`). Each entry carries a `name` plus the standard transport fields: `type` selects the transport (`stdio` default — `command`/`args`/`env`; `http`, a.k.a. `streamable-http` — `url`/`headers`; `sse`, the legacy 2024-11-05 HTTP+SSE transport, written verbatim — Reasonix re-implemented it in v1.17.18, and collapsing it onto `http` pointed the client at Streamable HTTP so the server could not connect). The file is treated as shared Reasonix config: Rulesync only replaces the `plugins` key and preserves every other table (providers, ui, agent, …) on round-trip, and it is never deleted. Reasonix has no per-server tool allow/deny lists. The `trusted_read_only_tools` array (raw MCP tool names pre-seeded as trusted for planner/read-only use) is neither written nor imported: v1.17.18 retired it along with `default_tools_approval_mode`, `tools.<raw>.approval_mode` and `approvals_reviewer` — installing a server is the authorization decision now, and Reasonix ignores the key on load and strips it the next time it saves that entry. Importing it would put a Reasonix-only dead key into the canonical `mcpServers` that every MCP target writes out, so it would surface in `.mcp.json` and the rest. Note that Rulesync owns the `plugins` key, so the next generate drops the key from an older `reasonix.toml` as well; nothing is lost that Reasonix still reads. An MCP server whose transport Reasonix does not implement (`ws`, including a `ws://`/`wss://` URL that states no transport at all) is skipped with a warning rather than written as a `type` its loader rejects. Each entry also supports `call_timeout_seconds` (a per-server MCP call timeout) and `tool_timeout_seconds` (a per-tool inline table keyed by raw MCP tool name). None of these have a deep canonical mapping, so they round-trip as passthrough fields on the canonical MCP server object. See the [Reasonix plugins guide](https://github.com/esengine/deepseek-reasonix/blob/main-v2/docs/GUIDE.md#plugins-mcp) and [SPEC.md](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/SPEC.md) (`[[plugins]]` schema).\\n\\n## `.rulesync/.aiignore` or `.rulesyncignore` (deprecated)\\n\\n> **Deprecation notice:** The `ignore` feature is deprecated in favor of the more expressive [`permissions` feature](#rulesync-permissions-jsonc). Existing ignore configurations, generation, import, conversion, and explicit `rulesync add ignore` scaffolding remain supported throughout Rulesync 14.x. Removal, if any, will be decided separately and will not occur before a future major release. `rulesync init` no longer enables or scaffolds ignore for new projects.\\n\\nRulesync continues to support a single legacy ignore list in either location:\\n\\n- `.rulesync/.aiignore` (preferred legacy location)\\n- `.rulesyncignore` (older project-root location)\\n\\nRules and behavior:\\n\\n- You may use either location.\\n- When both exist, Rulesync prefers `.rulesync/.aiignore` over `.rulesyncignore` when reading.\\n- Explicitly running `rulesync add ignore` creates `.rulesync/.aiignore` when neither location exists.\\n\\nExample:\\n\\n```ignore\\ntmp/\\ncredentials/\\n```\\n\\n### Migrating to permissions\\n\\nMove each ignore pattern into the `read` category of `.rulesync/permissions.jsonc` with the `deny` action:\\n\\n```jsonc\\n{\\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/permissions-schema.json\",\\n \"permission\": {\\n \"read\": {\\n \"tmp/**\": \"deny\",\\n \"credentials/**\": \"deny\",\\n },\\n },\\n}\\n```\\n\\nThis is the closest replacement for preventing an agent from reading ignored paths. If the old policy was also intended to prevent changes, repeat the patterns under `edit` and `write`. Target tools differ in the permission categories they can represent, so review the [Supported Tools and Features](./supported-tools.md) table and the tool-specific permission notes below before removing the old ignore feature from a multi-tool project.\\n\\n### Where ignore patterns are written per tool\\n\\nMost tools get a dedicated ignore file (for example `.cursorignore`,\\n`.geminiignore`, `.clineignore`). Antigravity CLI is built on the same engine\\nas Gemini CLI, so it reads the project-root `.geminiignore` file. Claude Code is the exception: it does not\\nread a separate ignore file, so Rulesync writes the deny list into Claude\\nCode\\'s settings file as `permissions.deny` entries (`Read(<pattern>)`).\\n\\nReasonix has no ignore file either, so its deny list goes into the `[permissions]` table of the shared `reasonix.toml` (project) / `~/.reasonix/config.toml` (global, via `--global`) as `Read(<pattern>)` entries — the same Claude-Code-style rule syntax the permissions feature writes there. `deny` is used rather than `[sandbox].forbid_read` because deny rules take glob specifiers and are documented as \"a hard block in every mode\", while `forbid_read` takes absolute paths with no documented glob support. The file is shared with the MCP and permissions features: only `Read(...)` deny entries are replaced, every other table and deny entry is preserved, and the file is never deleted. When the permissions feature also manages the `Read` category its explicit rules win, and the overwrite is warned about. As with the MCP and permissions features, the file is re-serialized on write, so hand-written comments, blank lines, and key ordering in `reasonix.toml` are not preserved.\\n\\nKiro reads `.kiroignore` in project scope and `~/.kiro/settings/kiroignore` in user scope. The `kiro`, `kiro-cli`, and `kiro-ide` targets therefore support `--global` for the deprecated ignore feature, as do `reasonix` and `zed` (whose config files exist in both scopes); the remaining ignore targets are project-only.\\n\\nZed has no ignore file: its deny list is the `private_files` array inside the shared settings file — `.zed/settings.json` in project scope and `~/.config/zed/settings.json` in global scope (`%APPDATA%\\\\Zed\\\\settings.json` on Windows). `private_files` is a worktree setting, and Zed layers default → user → project, so the key is honored in the user settings file too. The array is **owned wholesale by Rulesync**: it is replaced with the patterns from `.rulesync/.aiignore` on every generation, so a pattern deleted there is retracted from `settings.json` instead of surviving forever. When no patterns remain at all, the key is removed rather than written as `[]` — Zed ships a populated default `private_files` (`**/.env*`, `**/*.pem`, …) that any user or project value replaces wholesale, so an empty array would switch its secret redaction off. Every other key in the file — including the MCP `context_servers` and permissions `agent` blocks and unrelated editor settings — is preserved, and the file is never deleted.\\n\\nGoose retired `.gooseignore` upstream (\"removed some time ago in favour of other ignore things like gitignore etc\" — [goose#10343](https://github.com/aaif-goose/goose/issues/10343)), so rulesync no longer generates it; the replacement guidance is `.gitignore` plus tool permissions. Stale `.gooseignore` files from earlier versions stay gitignored but are not cleaned up automatically.\\n\\nCline\\'s `.clineignore` is still emitted, but its own docs now title it \"deprecate soon\" and state it is not a security or access-control boundary — upstream\\'s replacement direction is a Cline plugin enforcing via a `beforeTool` hook. Treat the matrix ✅ as a deprecated surface.\\n\\nHermes Agent uses a project-local `rulesync-ignore` plugin under `.hermes/plugins/`. It applies the canonical gitignore-style patterns through [`pre_tool_call`](https://hermes-agent.nousresearch.com/docs/user-guide/features/hooks/#pre-tool-call) to `read_file`, `write_file`, and `patch` before execution, and filters ignored paths from `search_files` results through `transform_tool_result`. This is defense in depth around Hermes file tools; terminal commands and paths already present in conversation context are outside the plugin\\'s enforcement surface. Hermes deliberately requires [explicit trust for project plugins](https://hermes-agent.nousresearch.com/docs/user-guide/features/plugins/), so run it from the trusted project root with that invocation opted in:\\n\\n```sh\\nHERMES_ENABLE_PROJECT_PLUGINS=1 hermes\\n```\\n\\nRulesync adds `rulesync-ignore` to `plugins.enabled` in `$HERMES_HOME/config.yaml` but deliberately leaves `$HERMES_HOME/.env` unchanged. Existing configuration is preserved, explicit `plugins.disabled` conflicts fail, and `--delete` retains the additive user-level activation.\\n\\nFor Cursor, Rulesync emits only `.cursorignore` — the file that **blocks access\\nentirely** (semantic search, Tab, Agent, Inline Edit, and `@`-mentions). Cursor\\nalso supports a second file, `.cursorindexingignore`, which excludes files from\\n**indexing only** while keeping them accessible to the AI on demand. These two\\nfiles mean _different_ things, and Rulesync\\'s `ignore` feature models a single\\ncanonical ignore list per tool with no per-pattern distinction between\\n\"block access\" and \"exclude from indexing only\". Emitting the same patterns to\\nboth files would be incorrect, so `.cursorindexingignore` is intentionally **not\\ngenerated** (an intentional non-goal). Author it by hand if you need\\nindexing-only excludes.\\n\\nBy default, Claude Code\\'s deny list is written to the **shared**\\n`.claude/settings.json` so that the policy can be committed and reviewed by\\nthe team. This is intentional (see issue #1094), but it means that running\\n`rulesync gitignore` will not add `.claude/settings.json` to `.gitignore` —\\nthat file may also contain other shared Claude config you actively want to\\ncommit.\\n\\nIf you would rather keep the deny list out of version control, opt into the\\n**local** mode using the per-feature options object form:\\n\\n```jsonc\\n// rulesync.jsonc\\n{\\n \"targets\": [\"claudecode\"],\\n \"features\": {\\n \"claudecode\": {\\n \"ignore\": { \"fileMode\": \"local\" },\\n },\\n },\\n}\\n```\\n\\n| `fileMode` | Output file | Tracked by git by default |\\n| -------------------- | ----------------------------- | ----------------------------------------------------- |\\n| `\"shared\"` (default) | `.claude/settings.json` | Yes — meant to be committed and shared with the team. |\\n| `\"local\"` | `.claude/settings.local.json` | No — `rulesync gitignore` already excludes this file. |\\n\\n## `.rulesync/permissions.jsonc`\\n\\n`.rulesync/permissions.jsonc` is the recommended source path and accepts comments and trailing commas. The legacy `.rulesync/permissions.json` path remains readable for existing projects. When both files exist, the JSONC file takes precedence; write flows update the existing source instead of creating a second variant.\\n\\nFor Hermes Agent imports, Rulesync treats a valid private `permissions.rulesync` block as provenance, then reconciles it with current native settings. `command_allowlist`, `approvals.deny`, and an enabled `security.website_blocklist` are authoritative for their mapped canonical rules, so hand edits replace stale generated values. A config with no private block still imports those native rules. Unmodeled `approvals`, `security`, `skills`, and `memory` settings remain under the `hermes` override; unrelated root settings such as `model` are not imported.\\n\\n`rulesync init` scaffolds a `codexcli` block with `approval_policy: \"on-request\"`, `approvals_reviewer: \"auto_review\"`, and `base_permission_profile: \":danger-full-access\"`. On generation, the profile value becomes Codex\\'s top-level `default_permissions`.\\n\\nPermissions define which tool actions are allowed, require confirmation, or are denied. The canonical format uses **lowercase tool category names** and **glob patterns** mapped to permission actions.\\n\\n**Permission actions:**\\n\\n- `allow` -- Automatically permitted without user confirmation\\n- `ask` -- Requires user confirmation before execution\\n- `deny` -- Blocked from execution\\n\\n**Supported tool categories:** `bash`, `read`, `edit`, `write`, `webfetch`, `websearch`, `grep`, `glob`, `notebookedit`, `agent`, and MCP-specific tool names (e.g., `mcp__puppeteer__puppeteer_navigate`)\\n\\nExample:\\n\\n```json\\n{\\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/permissions-schema.json\",\\n \"permission\": {\\n \"bash\": {\\n \"git *\": \"allow\",\\n \"npm run *\": \"allow\",\\n \"rm -rf *\": \"deny\",\\n \"*\": \"ask\"\\n },\\n \"edit\": {\\n \"src/**\": \"allow\"\\n },\\n \"read\": {\\n \".env\": \"deny\",\\n \"credentials/**\": \"deny\"\\n }\\n }\\n}\\n```\\n\\n### Tool-scoped permission blocks (`{toolname}.permission`)\\n\\nThe shared `permission` block applies to every targeted tool. To scope rules to a single tool, add a tool-scoped `{toolname}` block with a `permission` record of the same shape — mirroring `{toolname}.hooks` in `.rulesync/hooks.jsonc` and `{toolname}.mcpServers` in `.rulesync/mcp.jsonc`:\\n\\n```jsonc\\n{\\n \"permission\": {\\n \"bash\": { \"git *\": \"allow\", \"*\": \"ask\" },\\n },\\n \"claudecode\": {\\n \"permission\": {\\n // Replaces the shared `bash` category for Claude Code only.\\n \"bash\": { \"git *\": \"allow\", \"git push *\": \"deny\", \"*\": \"ask\" },\\n },\\n },\\n}\\n```\\n\\n- Categories are merged **per category**: a tool-scoped category replaces the shared category wholesale for that tool; shared categories it does not name still apply.\\n- Any permissions-capable `--targets` name is accepted as a block key. `kiro-cli`/`kiro-ide` alias to the `kiro` key and `hermesagent` to `hermes` (matching the shared output file each writes).\\n- OpenCode, Kilo, and Vibe keep their existing tool-native `permission` override semantics (bare action strings / tool-only categories / `sensitive_patterns` — see the tool-specific callouts below); their blocks are consumed by their translators instead of the central merge.\\n\\n#### JSON Schema Support\\n\\nRulesync provides a JSON Schema for editor validation and autocompletion. Add the `$schema` property to your `.rulesync/permissions.jsonc`:\\n\\n```json\\n{\\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/permissions-schema.json\",\\n \"permission\": {}\\n}\\n```\\n\\nFor Claude Code, this generates `permissions.allow`, `permissions.ask`, and `permissions.deny` arrays in `.claude/settings.json` (project mode) or `~/.claude/settings.json` (global mode) using PascalCase tool names (e.g., `Bash(git *)`, `Edit(src/**)`, `Read(.env)`).\\n\\nClaude Code\\'s file permission checks match only `Edit(path)` and `Read(path)` rules: a `Write(path)`, `NotebookEdit(path)` or `Glob(path)` rule \"is accepted but never matched by those checks, so Claude Code warns at startup for each allow, deny, or ask rule in one of these unmatched forms\" ([permissions docs](https://code.claude.com/docs/en/permissions), v2.1.210+). Rulesync therefore writes a canonical `write` or `notebookedit` rule that carries a pattern as `Edit(pattern)`, and a `glob` rule as `Read(pattern)`. A rule whose pattern is `*` is a tool-name rule with no path — it matches the tool everywhere and produces no warning — so it is still written as the bare `Write` / `NotebookEdit` / `Glob`. Entries an earlier Rulesync wrote in the warned form are replaced on the next generate, and so is a rewritten entry whose action changed, so flipping a rule from deny to allow never leaves the old deny behind to win. Rewriting a rule does **not** make Rulesync claim the `Edit` or `Read` namespace as a whole: a `Read(...)` deny the [ignore feature](#rulesyncignore) wrote, or an `Edit(...)` rule you added to `settings.json` by hand, is left alone unless the canonical config manages that category itself. Import stays tolerant of both forms, so an existing `settings.json` still round-trips; a rewritten rule comes back under `edit` or `read` rather than the category it was authored in, since that is the rule Claude Code actually applies. Note that this widens a `glob` **allow** rule: `Read(pattern)` permits reading the files\\' contents, not just listing their names — the docs prescribe the substitution, but author `glob` allow rules with that in mind. When two categories resolve to the same entry with different actions (`edit` allowing what `write` denies, say) both are written and Rulesync warns — Claude Code applies deny first, then ask, then allow.\\n\\n> **Claude Code-only override (`claudecode` key):** Claude Code\\'s `permissions` object also carries non-list fields with no canonical permission category — notably `defaultMode` (the session-start permission mode: `default` | `acceptEdits` | `plan` | `bypassPermissions`) and `additionalDirectories` (extra working directories). Add a tool-scoped `claudecode` override key alongside the shared block to author them: the fields under `claudecode.permissions` are merged into the settings `permissions` object and emitted **only** for Claude Code, while the shared `permission` block continues to drive the managed `allow`/`ask`/`deny` arrays. The block is a verbatim passthrough (so other/future `permissions` fields such as the org locks `disableBypassPermissionsMode`/`disableAutoMode` can be set too), but any `allow`/`ask`/`deny` placed inside it is ignored — rulesync owns those arrays. On import, the non-list `permissions` fields round-trip back into the `claudecode` override. Note that these fields are merged **additively** into the existing `settings.json` (so hand-added settings survive): removing a field from the `claudecode` override does not delete a value already written to `settings.json` — clear it there by hand.\\n>\\n> ```json\\n> {\\n> \"permission\": { \"bash\": { \"git *\": \"allow\" } },\\n> \"claudecode\": {\\n> \"permissions\": { \"defaultMode\": \"acceptEdits\", \"additionalDirectories\": [\"../shared\"] },\\n> \"sandbox\": { \"network\": { \"allowedDomains\": [\"example.com\"], \"strictAllowlist\": true } }\\n> }\\n> }\\n> ```\\n>\\n> The same override key also carries `sandbox`, the sibling top-level settings subtree governing the sandbox commands run in (`sandbox.network.*`, `sandbox.filesystem.*`, `sandbox.credentials`, `sandbox.allowAppleEvents`, ...). It has no canonical permission category either — it constrains _how_ a permitted command runs rather than which commands are permitted — so it is a verbatim passthrough on the same terms, merged into the top level of `settings.json` and round-tripped back on import. The merge is recursive, unlike the flat `permissions` fields above: `sandbox` subtrees carry restriction lists (`network.deniedDomains`, `filesystem.denyRead`), so setting one flag under `network` must not drop the denials beside it. A sibling key at any depth survives; a list you author replaces the existing list rather than being appended to. See the [sandboxing docs](https://code.claude.com/docs/en/sandboxing).\\n\\nFor OpenCode, this generates the `permission` object in `opencode.json` / `opencode.jsonc` (project mode) or `.config/opencode/opencode.json` / `.config/opencode/opencode.jsonc` (global mode), preserving other existing OpenCode config fields. OpenCode\\'s `webfetch`, `websearch`, `todowrite`, `question`, and `doom_loop` keys accept only a single action string, so Rulesync emits their canonical `{ \"*\": \"allow\" }` form as `\"allow\"`. If one of these categories contains pattern-specific rules, Rulesync collapses them to the most restrictive action (`deny` > `ask` > `allow`) and logs a warning because OpenCode cannot represent those patterns; a map without `*` includes an implicit `ask` fallback so a narrow allowlist never becomes blanket `allow`, while an empty map becomes `deny` instead of falling through to OpenCode\\'s default allow behavior.\\n\\n> **OpenCode-only override (`opencode` key):** OpenCode exposes permission categories that other tools do not understand (e.g. `external_directory`). Placing these in the shared `permission` block would push meaningless entries into Claude Code, Codex, etc. To scope them to OpenCode, add a tool-scoped `opencode` override key alongside the shared block — mirroring the tool-scoped override keys used by [hooks](#hooks) (`opencode.hooks`) and rules frontmatter. Categories under `opencode.permission` are merged on top of the shared block **per category** (the override wins) and are emitted **only** into `opencode.json` / `opencode.jsonc`; every other tool ignores them. Values may use a bare action string (`\"deny\"`) or, for OpenCode keys that support fine-grained matching, a pattern map (`{ \"*\": \"ask\" }`).\\n>\\n> ```jsonc\\n> {\\n> \"permission\": {\\n> \"bash\": { \"git *\": \"allow\", \"*\": \"ask\" },\\n> },\\n> // Emitted only into opencode.json\\'s `permission`; never leaks to other tools.\\n> \"opencode\": {\\n> \"permission\": {\\n> \"external_directory\": \"deny\",\\n> },\\n> },\\n> }\\n> ```\\n>\\n> On **import**, any OpenCode category that is not a shared canonical rulesync category (`bash`, `read`, `edit`, `write`, `webfetch`, `websearch`, `grep`, `glob`, `notebookedit`, `agent`, the all-tools key `*`, or an `mcp__*` tool name) is routed into the `opencode` override rather than the shared block, so a subsequent `rulesync generate` does not leak it into other tools.\\n>\\n> You may also override a **shared** category for OpenCode specifically (e.g. put `webfetch` under `opencode.permission` to give OpenCode a different value than the shared block sends to other tools). On generate this works as expected, but note the override is not round-trip stable for shared categories: re-importing the generated `opencode.json` classifies a shared category back into the shared block, so prefer expressing OpenCode-only categories here and keeping cross-tool categories in the shared block.\\n\\nFor Hermes Agent, permissions are written into the shared `~/.hermes/config.yaml` (global only). Canonical rules map onto the structures Hermes\\'s runtime actually enforces:\\n\\n- `allow` patterns (all categories) → `command_allowlist`.\\n- `bash` `deny` patterns → `approvals.deny` — Hermes\\'s hard denylist, evaluated **before** `--yolo` / `approvals.mode: off`.\\n- `webfetch` `deny` patterns → `security.website_blocklist.domains`.\\n- Every `ask` rule, and `deny` rules in categories other than `bash`/`webfetch`, have no native per-pattern Hermes primitive; they survive only for round-trip (Rulesync also stores the full canonical config under a private `permissions.rulesync` key so `.rulesync/permissions.jsonc` reconstructs losslessly).\\n\\n> **Hermes-only override (`hermes` key):** Hermes exposes approval/security controls with no canonical permission category — e.g. `approvals` (`mode`, `cron_mode`, `mcp_reload_confirm`, ...), `security` (`allow_private_urls`, ...), `skills.write_approval`, `memory.write_approval`. Add a tool-scoped `hermes` override key alongside the shared block to author them; its contents are **deep-merged** into `config.yaml` (so an `approvals.mode` here coexists with the `approvals.deny` derived from canonical deny rules) and are emitted **only** for Hermes. The block is a verbatim passthrough, so any current or future Hermes config key can be set without Rulesync modeling each one. Note that the deep merge replaces **arrays** wholesale, so setting `hermes.approvals.deny` or `hermes.security.website_blocklist.domains` overrides (does not append to) the list derived from the shared `permission` block — use it only when you intend to replace the canonical-derived deny list for Hermes. The top-level `permissions` key is reserved by Rulesync for the round-trip blob, so a `permissions` key inside the `hermes` override is ignored.\\n>\\n> ```json\\n> {\\n> \"permission\": { \"bash\": { \"rm -rf *\": \"deny\" } },\\n> \"hermes\": { \"approvals\": { \"mode\": \"smart\" }, \"security\": { \"allow_private_urls\": false } }\\n> }\\n> ```\\n\\nFor Codex CLI, this generates a `rulesync` named profile in `.codex/config.toml` under `[permissions.rulesync]` and sets `default_permissions = \"rulesync\"` (project/global depending on mode). It also generates `.codex/rules/rulesync.rules` from `permission.bash` entries using `prefix_rule(...)`. Current Rulesync-to-Codex mapping supports `bash`, `read`, `edit`/`write`, and `webfetch` categories:\\n\\n- `bash`: generates one `prefix_rule(...)` per command pattern in `.codex/rules/rulesync.rules` (`allow` → `allow`, `ask` → `prompt`, `deny` → `forbidden`)\\n- `read`: `allow` → `read`, `ask`/`deny` → `deny` in `permissions.<profile>.filesystem`\\n- `edit` / `write`: `allow` → `write`, `ask`/`deny` → `deny` in `permissions.<profile>.filesystem`\\n- `webfetch`: `allow`/`deny` map to `permissions.<profile>.network.domains` (Codex does not support `ask` for domain rules); `network.enabled = true` is emitted only when at least one `allow` rule is present. Deny-only domain sets are emitted without `enabled`, which Codex treats as restricted (its default) while the deny entries still round-trip back into Rulesync rules. Codex rejects the global wildcard `*` in denied domains at config load time, so `webfetch: { \"*\": \"deny\" }` is skipped with a warning (unlisted domains are denied by Codex\\'s allowlist-first policy anyway); `webfetch: { \"*\": \"allow\" }` is emitted as a regular `\"*\" = \"allow\"` domain entry, which Codex accepts for denylist-only setups ([openai/codex#15549](https://github.com/openai/codex/pull/15549)). On import, `deny` entries are always taken, while `allow` entries are imported only when `enabled = true` is explicit — Codex treats a missing `enabled` as restricted, so importing an allow entry from a disabled profile would activate a grant Codex never had. A Codex profile with `network.enabled = true` but no `domains` is imported as `webfetch: { \"*\": \"allow\" }`, which reflects Codex\\'s default semantics where `enabled = true` grants sandbox-wide network access (under Codex\\'s experimental `network_proxy` feature, `enabled = true` without an allowlist blocks requests instead, and the regenerated `\"*\" = \"allow\"` entry is the closest equivalent).\\n\\nRelative filesystem globs such as `src/**` or `**/*.tf` are emitted under `permissions.<profile>.filesystem.\":workspace_roots\"` instead of the top-level filesystem table, because Codex expects top-level filesystem keys to be absolute paths, `~/...`, or named roots. Rulesync also sets `glob_scan_max_depth = 8` when generated workspace-root rules contain unbounded `**` patterns.\\n\\nThe `:workspace_roots` table also receives a default `.git` carve-out: `\".git/**\" = \"write\"`. Codex\\'s `:workspace` baseline keeps `.git` read-only inside workspace roots, which denies basic git workflows (commit/stage operations write to `.git/index`, `.git/objects`, refs, and logs; everyday commands such as `git remote add`, `git push -u`, and local-scope `git config` write to `.git/config`). The write rule reopens the whole subtree, including `.git/config` — an earlier `\".git/config\" = \"read\"` security guard (a writable `.git/config` lets a sandboxed process set keys like `core.fsmonitor` or `core.hooksPath` that execute code outside the sandbox) was dropped because it blocked those everyday commands while the protection it added was already partial (`.git/hooks/`, and `.git/modules/**` for submodules, remains writable so hook managers such as lefthook and simple-git-hooks keep working; a sandboxed process could still install a hook directly). Users who want stricter isolation can author a more specific rule (e.g. `read: { \".git/config\": \"allow\" }` or `read: { \".git/hooks/**\": \"allow\" }`) in the canonical permissions, which wins over the default (Codex resolves the more specific path with priority). Because `.git/**` is an unbounded `**` pattern, the carve-out also means `glob_scan_max_depth = 8` is effectively always emitted unless it is suppressed.\\n\\nThe carve-out is skipped in three cases: a user rule for the same pattern always wins per key; the `codexcli.git_write_rules` override set to `false` suppresses it entirely (only an explicit `false` does; the default is `true`); and it is not injected when `codexcli.base_permission_profile` is `\":read-only\"` (it would grant `.git` write access inside a sandbox the user explicitly chose to keep read-only) or when the canonical rules contain a direct `\":workspace_roots\"` pattern (a whole-tree access decision that the defaults must not override). Like `:minimal`, the default-valued carve-out is not imported into the Rulesync model on `rulesync import` — it is re-added on every generate — while customized `.git` values import normally. One limitation: the `git_write_rules` flag itself cannot be recovered from `config.toml`, so it does not round-trip through `rulesync import`; if you opted out with `false`, re-add the flag to the canonical permissions config after importing (and if you want the same `.git` rules while opted out, author them as canonical `read`/`write` rules rather than hand-writing them in `config.toml` — though note that import cannot tell a user-authored `\".git/**\" = \"write\"` from the default carve-out, so that exact pattern/value pair is still skipped on import and must be re-authored in the canonical config afterwards). Migration note: configs generated before the `\".git/config\" = \"read\"` default was removed still carry that entry, and `rulesync import` now treats it as a user-authored rule — it lands in the canonical config as `read: { \".git/config\": \"allow\" }` and, because Codex gives the more specific path priority, keeps `.git/config` read-only on every regenerate. If you want the current writable default instead, delete that rule from the canonical permissions after importing.\\n\\nThe generated `[permissions.rulesync]` profile always extends one of Codex\\'s built-in permission profiles via `extends`. The baseline is chosen with the `codexcli.base_permission_profile` override key (`\":read-only\"` | `\":workspace\"` | `\":danger-full-access\"`) and defaults to `\":workspace\"` when unspecified. Codex\\'s built-in `:workspace` baseline grants read access to the whole filesystem and write access to the entire workspace root plus `/tmp` and `$TMPDIR` (with carve-outs protecting `.git`, `.codex`, and `.agents`), while `:read-only` keeps command execution read-only; the generated `filesystem` entries then grant or deny access on top of the chosen baseline. Codex\\'s third built-in profile, `:danger-full-access`, is rejected by `extends` at Codex config load time — so selecting it works differently: Rulesync emits `default_permissions = \":danger-full-access\"` directly and skips the managed `[permissions.rulesync]` profile entirely (with the sandbox removed there is nothing for filesystem/network rules to refine; canonical `read`/`edit`/`write`/`webfetch` rules are ignored for Codex CLI with a warning, and any stale managed profile from a previous generate is pruned while sibling hand-written profiles are preserved). On import, a profile\\'s `extends` value round-trips back into `codexcli.base_permission_profile` when it names one of the two extendable built-ins, and a top-level `default_permissions = \":danger-full-access\"` round-trips the same way; a custom parent profile is skipped and replaced by the managed baseline on regeneration (with a warning).\\n\\nRulesync emits `\":minimal\" = \"read\"` in the generated filesystem table by default. This enables `include_platform_defaults()` ([FileSystemSpecialPath::Minimal](https://github.com/openai/codex/pull/13434)), which provides the platform/runtime read access needed for basic sandboxed command execution on macOS, Linux, and Windows. `:minimal` is the only special path treated as a fixed baseline: it is always present in the generated table and is never imported into Rulesync\\'s own permission model, regardless of its value. A canonical rule for `:minimal` still overrides the emitted value on generate (e.g. a `write: { \":minimal\": \"allow\" }` rule emits `\":minimal\" = \"write\"` — see the [FAQ](../faq.md#codex-cli-denies-ssh-agent-access-temp-dir-writes-or-reading-its-own-config-with-a-generated-permissions-profile) for when that is needed), but because import always skips `:minimal`, such a customization does not round-trip: after `rulesync import`, re-author the rule or the next generate falls back to `\"read\"`. The other special paths `:root`, `:tmpdir`, and `:slash_tmp` are user-managed access rules that are imported into the Rulesync model and re-emitted from it like any ordinary filesystem entry (`:root = \"deny\"` becomes a read/edit deny, `:tmpdir = \"write\"` becomes an edit allow, and so on). Because they round-trip through `.rulesync/permissions.jsonc` rather than relying on an existing `.codex/config.toml`, a restrictive value such as `:root = \"deny\"` survives a fresh-clone `rulesync generate` with no pre-existing Codex config.\\n\\n`network.mode`, `network.unix_sockets`, and `description` have no equivalent in Rulesync\\'s canonical permissions model and are not generated. If an existing `.codex/config.toml` already contains these fields on the `rulesync` profile, Rulesync preserves them on regeneration — as it does any other network key it does not model (e.g. `dangerously_allow_all_unix_sockets` or Codex\\'s proxy keys), since network settings are user territory by design. `network.enabled` is only half-managed: Rulesync sets `enabled = true` itself when the canonical model contains an allow domain, but when a regeneration computes no `enabled` value, a user-authored `enabled` is preserved (with a warning) instead of being deleted — see the [FAQ](../faq.md#codex-cli-denies-ssh-agent-access-temp-dir-writes-or-reading-its-own-config-with-a-generated-permissions-profile) for the recommended user-managed entries. The preservation applies only when the existing profile carries no allow domain: an existing `enabled` next to allow domains is Rulesync\\'s own managed output, so removing every webfetch allow rule from the canonical model removes `enabled` too (falling back to Codex\\'s restricted default) instead of leaving an unscoped `enabled = true` behind. Note that `filesystem`, `network.domains`, and `extends` are always managed by Rulesync (`filesystem`/`network.domains` derived from `edit`/`write`/`webfetch` rules, `extends` from `codexcli.base_permission_profile`), so hand-authored values in those fields will be replaced on regeneration.\\n\\n> **Codex CLI-only override (`codexcli` key):** Codex CLI\\'s permission surface is richer than the canonical allow/ask/deny model — its approval workflow, permission-profile baseline, and per-app tool gating have no canonical category. Add a tool-scoped `codexcli` override to author them: except for `base_permission_profile`, its fields are written verbatim as **top-level `.codex/config.toml` keys** (the override wins per key; existing sibling keys the user set directly are preserved, and table values are shallow-merged) while the shared `permission` block keeps driving the managed `[permissions.rulesync]` profile and `default_permissions`. Supported keys: `base_permission_profile` (`:read-only` | `:workspace` | `:danger-full-access`, default `:workspace` — not a top-level key; it becomes the managed profile\\'s `extends` baseline, or with `:danger-full-access` the directly-selected `default_permissions` value, see above), `approval_policy` (`untrusted` | `on-request` (legacy alias `on-failure`) | `never`, or a `{ granular = { … } }` table kept verbatim; defaults to `on-request` when neither the override nor the existing config sets it), `apps` (per-app tool gating — `apps.<id>.tools.<tool>.approval_mode` / `.enabled`, `apps.<id>.default_tools_approval_mode`), `approvals_reviewer` (`user` | `auto_review` (legacy alias `guardian_subagent`), or a table; defaults to `auto_review` when neither the override nor the existing config sets it), and `git_write_rules` (boolean, default `true` — like `base_permission_profile` it is not a top-level key: it controls whether the managed profile\\'s `:workspace_roots` table emits the default `.git` carve-out described above; only an explicit `false` suppresses it). **Deprecated:** `sandbox_mode` (`read-only` | `workspace-write` | `danger-full-access`) with the sibling `sandbox_workspace_write` table (`network_access`, `writable_roots`, …) belong to Codex\\'s classic sandbox system, which permission profiles supersede — Codex prioritizes these legacy keys over permission profiles when both are present, so authoring them disables the generated `[permissions.rulesync]` profile; they are still accepted (with a warning) so existing configs round-trip, but use `base_permission_profile` and the shared `permission` block instead. On import, the top-level keys round-trip back into the `codexcli` override, and the managed profile\\'s `extends` round-trips into `base_permission_profile`. It is a `looseObject`, so future top-level Codex config keys can be authored here (merged verbatim on generate; only the listed keys are re-extracted on import). Example: `{ \"permission\": { … }, \"codexcli\": { \"base_permission_profile\": \":workspace\", \"approval_policy\": \"on-request\", \"approvals_reviewer\": \"auto_review\" } }`. **Out of scope:** `mcp_servers.*` per-MCP gating is **not** authorable here — it is owned by the MCP feature (`codexcli-mcp.ts` writes the `mcp_servers` tables in the same `config.toml`), and `permissions` / `default_permissions` are owned by the canonical model; any such key placed in the override is skipped with a warning. See the [Codex configuration reference](https://developers.openai.com/codex/config-reference) and [permissions docs](https://developers.openai.com/codex/permissions).\\n\\nFor Kiro, this generates tool permission settings in `.kiro/agents/default.json` (project mode):\\n\\n- `bash` maps to `toolsSettings.shell.allowedCommands` / `toolsSettings.shell.deniedCommands`\\n- `read` maps to `toolsSettings.read.allowedPaths` / `toolsSettings.read.deniedPaths`\\n- `edit` / `write` map to `toolsSettings.write.allowedPaths` / `toolsSettings.write.deniedPaths`\\n- `grep` maps to `toolsSettings.grep.allowedPaths` / `toolsSettings.grep.deniedPaths`\\n- `glob` maps to `toolsSettings.glob.allowedPaths` / `toolsSettings.glob.deniedPaths` (both emitted only when a rule is present, so existing configs do not gain empty tables)\\n- `webfetch` / `websearch` with pattern `*` map to `allowedTools` entries (`web_fetch` / `web_search`)\\n- `ask` rules are skipped with a warning (Kiro config does not support explicit ask entries)\\n\\n> **Kiro-only override (`kiro` key):** Kiro\\'s agent config exposes per-tool `toolsSettings` knobs with no canonical allow/ask/deny category. Author them through a tool-scoped `kiro` override under `toolsSettings`: the shell auto-trust flags `shell.autoAllowReadonly` / `shell.denyByDefault`, the `aws` built-in tool\\'s `allowedServices` / `deniedServices` (+ `autoAllowReadonly`), and the `web_fetch` domain trust arrays `trusted` / `blocked` (regex host patterns; Kiro documents these for `web_fetch` only — `web_search` has no domain-trust surface). Example: `{ \"permission\": { … }, \"kiro\": { \"toolsSettings\": { \"shell\": { \"autoAllowReadonly\": true }, \"aws\": { \"allowedServices\": [\"s3\"], \"deniedServices\": [\"eks\"] }, \"web_fetch\": { \"trusted\": [\".*github\\\\\\\\.com.*\"] } } } }`. The override is **deep-merged per `toolsSettings` key** (the override wins at the leaf) so authoring `shell.autoAllowReadonly` keeps the canonical-generated `shell.allowedCommands`; the shared `permission` block keeps driving `shell.{allowed,denied}Commands`, `read`/`write`/`grep`/`glob` paths, and the `web_fetch`/`web_search` `allowedTools` toggles. Existing non-canonical `shell` flags are preserved across regenerate even without an override. On **import**, these Kiro-specific surfaces are lifted into the `kiro` override so they round-trip. It is a `looseObject` at every level, so future Kiro `toolsSettings` fields pass through verbatim. Kiro MCP `disabledTools` lives in the separate `.kiro/settings/mcp.json` file and is modeled by the MCP feature; MCP `autoApprove` remains outside this permissions translator. See the [Kiro built-in tools](https://kiro.dev/docs/cli/reference/built-in-tools/) and [configuration reference](https://kiro.dev/docs/cli/custom-agents/configuration-reference/) docs.\\n\\nFor Cursor CLI, this generates `permissions` entries in `.cursor/cli.json` (project mode) or `~/.cursor/cli-config.json` (global mode). Cursor CLI only supports `allow` and `deny` decisions, so `ask` rules are skipped with a warning. Tool categories are mapped to PascalCase Cursor tool names (`bash` → `Shell`, `read` → `Read`, `edit`/`write` → `Write`, `webfetch` → `WebFetch`, `mcp__*` → `Mcp`). Existing Cursor-specific entries that Rulesync does not manage (for example, MCP entries with extra fields) are preserved on round-trip.\\n\\n> **Cursor-only override (`cursor` key):** Cursor\\'s `cli.json` carries scalar autonomy settings with no canonical permission category — `approvalMode` (`allowlist` | `auto-review` | `unrestricted`) and a `sandbox` object (`mode`/`networkAccess`). Add a tool-scoped `cursor` override to author them: its fields are merged into the top level of `cli.json` while the shared `permission` block keeps driving the `permissions.allow`/`permissions.deny` arrays (the override cannot clobber that managed block). On import, `approvalMode` and `sandbox` round-trip back into the `cursor` override. It is a `looseObject`, so `sandbox`\\'s (currently undocumented) value set passes through verbatim and extra `cli.json` keys can be authored here (they are merged verbatim on generate); note that only `approvalMode` and `sandbox` are re-extracted on import.\\n>\\n> ```json\\n> {\\n> \"permission\": { \"bash\": { \"git *\": \"allow\" } },\\n> \"cursor\": { \"approvalMode\": \"auto-review\" }\\n> }\\n> ```\\n>\\n> The separate Cursor **IDE** `permissions.json` (`mcpAllowlist`, `terminalAllowlist`, `autoRun.*`) is a different file and is not targeted by this translator.\\n\\nFor GitHub Copilot (`copilot`), this manages the three `chat.tools.*.autoApprove` maps in the workspace `.vscode/settings.json` (project mode only). VS Code has no standalone, environment-agnostic Copilot policy file, so project-level auto-approvals are configured through VS Code Copilot Chat\\'s workspace settings. Three canonical categories have a clean, non-lossy mapping and are emitted: `bash` → `chat.tools.terminal.autoApprove` (command patterns), `edit` → `chat.tools.edits.autoApprove` (file globs) and `webfetch` → `chat.tools.urls.autoApprove` (URL patterns). In all three, `allow` → `true` (auto-approve) and `deny` → `false` (never auto-approve); an `ask` rule is represented by **omitting** the entry, so VS Code falls through to its default in-chat approval prompt. The canonical `read` category has no VS Code approval surface, and `write` is deliberately **not** folded into the edits map alongside `edit` — doing so would make the two indistinguishable on import — so neither is emitted. VS Code also accepts a `{ \"approveRequest\": …, \"approveResponse\": … }` object per URL pattern; that form has no canonical equivalent, so it is skipped on import, and because Rulesync owns the key outright it is replaced whenever the canonical config carries any `webfetch` rule. `.vscode/settings.json` is a general workspace file (JSONC), so Rulesync merges only those three keys non-destructively and never deletes the file; every unrelated setting is preserved. VS Code\\'s user-scope `settings.json` lives at a platform-dependent path outside Rulesync\\'s home-relative global model, so only project scope is supported. The all-or-nothing `chat.tools.global.autoApprove` boolean and the registry-allowlist `chat.mcp.access` setting are intentionally **not** mapped, since collapsing per-pattern rules into them would misrepresent what was configured. See the [VS Code agent approvals docs](https://code.visualstudio.com/docs/agents/approvals) and the [edit-approval docs](https://code.visualstudio.com/docs/copilot/chat/review-code-edits).\\n\\nFor Kilo Code, this generates the `permission` object in `kilo.jsonc` (project mode) or `~/.config/kilo/kilo.jsonc` (global mode). The shape is identical to OpenCode\\'s (Kilo is an OpenCode fork), so categories like `bash`, `read`, `edit`, `write`, `webfetch`, and `mcp` accept either a string catch-all (`\"allow\" | \"ask\" | \"deny\"`) or a `{ <pattern>: <action> }` map. Other top-level keys in `kilo.jsonc` are preserved on round-trip. **The `permission` object is merged per top-level tool key**: for each tool key present in the rulesync output, that key is replaced entirely from rulesync (rulesync owns its managed keys; manual edits inside a managed key will be overwritten on the next generation). Tool keys that exist in the existing `kilo.jsonc` but are NOT in the rulesync output are preserved verbatim so user-added Kilo-only categories survive regeneration. When a regenerate replaces a key whose existing value contained `deny` patterns that disappear from the new rulesync output, an aggregated `logger.warn` enumerates the dropped patterns (matching the project convention used by every other permissions translator). Edits to other top-level keys (e.g. `model`) are preserved. **Malformed `kilo.jsonc` aborts the run**: the `jsonc-parser` library would otherwise silently coerce a syntax error to `{}` and overwrite the corrupted file with an empty `permission`, dropping the user\\'s existing `deny` rules. Rulesync now surfaces parse errors so the run aborts before any destructive write — matching the strict `JSON.parse` behavior used by every other permissions translator.\\n\\n> **Kilo-only override (`kilo` key):** Kilo\\'s `permission` object carries tool-specific keys with no canonical permission category — OpenCode-inherited ones (`external_directory`, `doom_loop`, `lsp`, `question`, `todowrite`, `skill`, `task`, `list`) and Kilo-unique ones (`agent_manager`, `notebook_read`, `notebook_edit`, `notebook_execute`, `repo_clone`, `repo_overview`). Add a tool-scoped `kilo` override key alongside the shared block (mirroring the `opencode` override) to author these; entries under `kilo.permission` are merged on top of the shared block **per key** (the override wins) and are emitted **only** into `kilo.jsonc`. Each value may be a bare action string or a pattern map. On **import**, any Kilo key that is not a shared canonical category (`bash`, `read`, `edit`, `webfetch`, `websearch`, `grep`, `glob`, the all-tools key `*`, or an `mcp__*` tool name) is routed into the `kilo` override rather than the shared block, so a subsequent `rulesync generate` does not leak it into other tools.\\n>\\n> **Kilo-only override (`kilo.sandbox`):** the `sandbox` block Kilo runs commands in is a security surface orthogonal to per-tool allow/ask/deny, with no canonical category, so it is authored under the same tool-scoped `kilo` override: `enabled` (boolean), `network` (e.g. `\"deny\"`), `allowed_hosts` (a list of `host` / `host:port` destination exceptions) and `writable_paths`. It is shallow-merged into the top-level `sandbox` key of `kilo.jsonc` — the override\\'s keys win, unrelated sibling keys you set directly are preserved — and the whole block round-trips back into `kilo.sandbox` on import. **Scope matters here.** Kilo honors `allowed_hosts` and `writable_paths` from the _global_ config only, and lets a project config merely tighten (`enabled: true`, `network: \"deny\"`); a project-level network denial even clears the global destination exceptions. Rulesync mirrors that rather than writing config Kilo would ignore: at project scope only `enabled` and `network` are emitted, and any other key is dropped with a warning telling you to author it with `--global`. See the [sandboxing docs](https://kilo.ai/docs/getting-started/settings/sandboxing).\\n\\n> **Name-mismatch traps.** Canonical category names do not always match Kilo\\'s key names: Kilo folds **`write` into `edit`** (there is no `write` key), uses **`notebook_edit`** (not the canonical `notebookedit`) and **`task`/`agent_manager`** (not `agent`), and has **no `mcp` key** (MCP is addressed via `mcp__*` tool-name keys). Rulesync passes key names through verbatim, so author Kilo keys using Kilo\\'s own names (e.g. put a `notebook_edit` rule under `kilo.permission`, not the canonical `notebookedit`). Kilo also treats a `null` action as a delete sentinel; Rulesync does not model `null` and only round-trips `allow`/`ask`/`deny`.\\n\\nFor AugmentCode CLI, this generates `toolPermissions` entries in `.augment/settings.json` (project mode) or `~/.augment/settings.json` (global mode). Each entry has `toolName`, an optional `shellInputRegex` (only for shell commands), and `permission.type` ∈ `\"allow\" | \"deny\" | \"ask-user\"`. Tool category mapping: `bash` → `launch-process`, `read` → `view`, `edit` → `str-replace-editor`, `write` → `save-file`, `webfetch` → `web-fetch`, `websearch` → `web-search`. Action mapping: rulesync `ask` → AugmentCode `ask-user`. For `bash` patterns other than `*`, the glob pattern is converted to a regex and emitted as `shellInputRegex`. The glob → regex conversion maps `*` to `.*`, `?` to `.`, escapes `\\\\^$.|+(){}[]`, and anchors at both ends; characters outside that set (notably `-`, `/`, `:`, `,`) are emitted verbatim, so Augment will match them literally. Generated entries are sorted **deny first, ask second, allow last**, with more specific patterns (those carrying `shellInputRegex`) before catch-alls — this is required because Augment\\'s `toolPermissions` is evaluated **first-match-wins**. Existing `toolPermissions` entries whose `toolName` is NOT in the rulesync-managed set are preserved on round-trip; existing **`deny` entries for ANY managed `toolName`** (`launch-process`, `view`, `str-replace-editor`, `save-file`, `web-fetch`, `web-search`) are also preserved (fail-closed) so a user-added deny rule on any managed tool cannot be silently downgraded by regeneration. Existing managed-tool `allow` / `ask-user` entries are still replaced (rulesync owns the permissive surface for managed namespaces). **Non-bash categories do not have a documented per-input matcher in AugmentCode**, so Rulesync emits at most one catch-all entry per tool: if the rulesync category contains any `deny` rule, Rulesync emits a single `deny` entry for the entire tool (fail-closed) and warns; otherwise only `*`-pattern allow/ask rules are emitted and any non-`*` allow/ask patterns are dropped with a warning. Importing AugmentCode entries back into rulesync recovers `bash` patterns from `shellInputRegex` but the other categories always import as the catch-all `*` pattern. **The import direction also applies fail-closed precedence** when multiple existing entries collapse to the same `(canonical, \"*\")` key (e.g. `[{view: deny}, {view: allow}]`): the most restrictive action wins regardless of iteration order (precedence: `deny` > `ask` > `allow`), so a user-added deny in the source file is never silently dropped by import order. The `launch-process` (bash) path is unchanged because each entry has its own `shellInputRegex`-derived pattern with no `\"*\"` collapse. On **import** (project scope), Rulesync also reads the layered overrides file `<workspace>/.augment/settings.local.json` — a gitignored, machine-specific file that Auggie merges on top of `settings.json` — and combines it over the base settings before converting to the canonical model, following Auggie\\'s documented layering (simple values take the local override, `mcpServers`/`plugins` replace wholesale, and other objects/lists — including `toolPermissions`, which Auggie concatenates local-first under first-match — are combined across tiers), so personal permission overrides are picked up without dropping a committed base `deny`. This overlay is **import-only and project-only**: Rulesync never writes `settings.local.json` (it stays a user-owned, gitignored file), and AugmentCode documents no global `~/.augment/settings.local.json`, so the overlay is skipped in global mode. An unknown top-level key such as `recommendedMarketplaces` (added in Auggie CLI 0.20.0) is preserved verbatim through the generate round-trip via the `{...settings}` merge.\\n\\n> **AugmentCode-only override (`augmentcode` key):** AugmentCode\\'s `toolPermissions[]` supports \"custom policy\" entries the canonical allow/ask/deny model cannot express — `permission.type` of `webhook-policy` / `script-policy` (delegating the decision to a `webhookUrl` / `script`) and an `eventType` of `tool-response` (a post-execution check rather than the default pre-execution `tool-call`). Author these through a tool-scoped `augmentcode` override with a `toolPermissions` array of verbatim entries: `{ \"permission\": { … }, \"augmentcode\": { \"toolPermissions\": [ { \"toolName\": \"github-api\", \"permission\": { \"type\": \"webhook-policy\", \"webhookUrl\": \"https://api.example.com/validate\" } }, { \"toolName\": \"view\", \"eventType\": \"tool-response\", \"permission\": { \"type\": \"allow\" } } ] } }`. Authored entries are **prepended** — ahead of the canonical-generated basic rules — so a webhook/script gate or tool-response check is never shadowed by a regenerated allow/deny/ask entry under first-match-wins. When the override authors `toolPermissions` it becomes the source of truth for the special entries (the existing file\\'s specials are no longer separately preserved, avoiding a double-emit); without an override, any special entries already present in `settings.json` are preserved verbatim as before. On **import**, special entries are lifted verbatim into the `augmentcode` override (rather than being skipped with a warning) so they round-trip and become user-authorable; basic entries continue to drive the shared `permission` block. The entry objects stay a loose passthrough so `shellInputRegex`, `webhookUrl`, `script`, and future non-policy fields survive untouched, while the documented bounded fields are validated as enums: `permission.type` (`allow` | `deny` | `ask-user` | `webhook-policy` | `script-policy`) and `eventType` (`tool-call` | `tool-response`). Both project and global scope are supported.\\n\\nFor Factory Droid, this generates `commandAllowlist` / `commandDenylist` arrays in `.factory/settings.json` (project mode) or `~/.factory/settings.json` (global mode). Factory Droid only gates **shell commands** through these two lists, so only the rulesync `bash` category is translated: `allow` patterns become `commandAllowlist` entries (run without confirmation) and `deny` patterns become `commandDenylist` entries (always require confirmation; the denylist wins when a command is in both). Factory Droid has **no separate `ask` list** — any command not in the allowlist already prompts — so rulesync `ask` rules are dropped. Categories other than `bash` cannot be represented in the command allow/deny model and are skipped, with a `logger.warn` when a skipped category carries a `deny` rule (to surface the gap). rulesync owns the `commandAllowlist` / `commandDenylist` keys (they are replaced from the rulesync output), while every other key in `settings.json` (e.g. `hooks`) is preserved verbatim on round-trip — except the Factory-specific security keys covered by the `factorydroid` override below, which are lifted into that override on import. Importing reads the two lists back into the `bash` category.\\n\\n> **Factory Droid-only override (`factorydroid` key):** Factory Droid has security controls that do not fit the per-command `allow`/`ask`/`deny` model — the hard-block `commandBlocklist` tier (commands that can **never** run, not even under full autonomy — distinct from an approvable `deny`), plus `networkPolicy` (`allowedIps`), `sandbox` (`enabled`/`mode`/`filesystem`/`network`), `mcpPolicy`, `enableDroidShield`, autonomy settings (`sessionDefaultSettings`, `maxAutonomyLevel`, `interactionMode`), the plugin-bootstrap keys `extraKnownMarketplaces` / `enabledPlugins` (Droid auto-registers those marketplaces and installs those plugins on start — the upstream distribution path for the same artifacts rulesync generates), and the `hooksDisabled` kill-switch. Add a tool-scoped `factorydroid` override to author them: its keys are merged into `settings.json` (the override wins) while the shared `permission` block keeps driving `commandAllowlist`/`commandDenylist`. On **import**, these keys are lifted into the `factorydroid` override — so `commandBlocklist` now round-trips faithfully (its never-runs guarantee is preserved) rather than being collapsed onto an approvable `deny`.\\n>\\n> ```json\\n> {\\n> \"permission\": { \"bash\": { \"git *\": \"allow\" } },\\n> \"factorydroid\": { \"commandBlocklist\": [\"curl *\"], \"sandbox\": { \"enabled\": true } }\\n> }\\n> ```\\n\\nFor Cline CLI, this generates `.cline/command-permissions.json` (project mode only). Cline reads this file via the `CLINE_COMMAND_PERMISSIONS` environment variable; you can wire it up with `export CLINE_COMMAND_PERMISSIONS=$(cat .cline/command-permissions.json)`. The schema is `{ \"allow\": [...], \"deny\": [...], \"allowRedirects\": false }`. Cline only supports shell commands and only `allow`/`deny`. Non-`bash` categories are dropped and rulesync `ask` rules for `bash` are **translated to `deny`** (fail-closed safety, since Cline lacks `ask` semantics); both translation notices are surfaced via a single aggregated `logger.warn` per generation (matching the project convention used by every other permissions translator) so the translation stays visible without tripping CI gates that treat error lines as failures. **The `allow` array is wholesale-replaced by rulesync** — user-added entries inside `allow` are not preserved on regenerate. **The `deny` array is additive** — user-added denies in the existing file are preserved on every generation alongside the rulesync-derived denies (fail-closed standard). The `allowRedirects` field (a single global boolean gating shell redirection operators `>`/`>>`/`<`) can be authored from rulesync via a tool-scoped **`cline` override** — add `\"cline\": { \"allowRedirects\": true }` alongside the shared `permission` block. Precedence: the `cline` override wins, otherwise the existing file value is preserved, otherwise it defaults to `false`. On import, a `true` value round-trips back into the `cline` override (the default `false` emits no override). Cline does not have a stable per-user file location for command permissions, so global mode is not supported. If a pattern ends up in **both** `allow` and `deny` (defensive check; not reachable from a single rulesync config), Rulesync emits a warning because Cline does not document a deterministic deny-priority.\\n\\nFor Zed, this generates the `agent.tool_permissions` object in `.zed/settings.json` (project mode) or `~/.config/zed/settings.json` (global mode — `%APPDATA%\\\\Zed\\\\settings.json` on Windows). Each canonical category becomes a key under `agent.tool_permissions.tools.<tool>` (tool-name mapping: `bash` → `terminal`, `read` → `read_file`, `edit` → `edit_file`, `write` → `write_file`, `webfetch` → `fetch`, `websearch` → `search_web`; unknown categories, including `mcp:<server>:<tool>` keys, pass through unchanged). The canonical `*` category is the exception: its catch-all `*` rule sets the top-level `agent.tool_permissions.default` — rung 6 of Zed\\'s precedence ladder, and the mechanism Zed documents for MCP tools — rather than an inert `tools[\"*\"]` entry (`*` is not a Zed tool name; a stale `tools[\"*\"]` entry written by an earlier version is cleaned up when the canonical config carries a `*` category, and the `default` imports back as `*: { \"*\": <action> }`). Pattern-scoped rules in the `*` category have no Zed counterpart and are dropped with a warning. Within every other category, the catch-all `*` pattern sets the per-tool `default`, while specific patterns become `always_allow` / `always_deny` / `always_confirm` entries of the form `{ \"pattern\": <regex>, \"case_sensitive\": false }`. Action mapping: rulesync `ask` ⇄ Zed `confirm` (`allow`/`deny` are shared). Because Zed matches with regular expressions, patterns are emitted verbatim — author canonical patterns as regexes when targeting Zed. The settings file is shared with the MCP (`context_servers`) and ignore (`private_files`) features, so writes merge non-destructively: unrelated settings, a user-set `agent.tool_permissions.default` (when the canonical config has no `*` category), and any `tools.<tool>` entries NOT managed by rulesync are preserved on round-trip. The canonical model has no slot for per-pattern case sensitivity, so rulesync always emits `case_sensitive: false`; a hand-authored `case_sensitive: true` on a rulesync-managed tool is overwritten on the next generate.\\n\\nFor Qwen Code, this generates `permissions.allow`, `permissions.ask`, and `permissions.deny` arrays in `.qwen/settings.json` (project mode) or `~/.qwen/settings.json` (global mode). The format mirrors Claude Code\\'s: entries are `Bash(<pattern>)`, `Read(<pattern>)`, `Edit(<pattern>)`, `Write(<pattern>)`, `WebFetch(<pattern>)`, `WebSearch(<pattern>)`, `Grep(<pattern>)`, `Glob(<pattern>)`, `Agent(<pattern>)`, etc. Other top-level keys in `settings.json` are preserved on round-trip. Patterns may contain nested parentheses (e.g. `Bash(echo (a))`); Rulesync uses the **last** `)` as the closing delimiter when parsing, so inner parens round-trip. Malformed entries (missing closing paren, trailing characters) emit a warning; for **`deny`** they fall back to the catch-all pattern `*` (fail-closed: broadening a deny is the safer direction), but for **`allow` / `ask`** they are **dropped** rather than broadened — silently turning a narrow user rule into `*` would be a fail-open round-trip. Generation does not create the `.qwen/` directory until `writeAiFiles` runs, so dry-run is side-effect-free.\\n\\nFor Kimi Code, permissions are global-only and generate `[[permission.rules]]` entries in `~/.kimi-code/config.toml`. Canonical categories map to Kimi tool patterns (`bash` → `Bash`, `read` → `Read`, `write` → `Write`, `edit` → `Edit`, `grep` → `Grep`, `glob` → `Glob`, `websearch` → `WebSearch`, `webfetch` → `FetchURL`, `agent` → `Agent`, and `mcp__…` passes through as the MCP tool name); a `*` canonical pattern emits the bare tool name and a specific pattern emits `Tool(pattern)`. Actions map 1:1 to Kimi\\'s `allow` / `ask` / `deny`, and generated rules use `scope = \"user\"`. Kimi evaluates rules first-match-wins, so Rulesync sorts canonical output fail-closed: all `deny` rules precede `ask`, all `ask` rules precede `allow`, and more-specific patterns precede broader patterns within each action. Kimi does not match MCP tool arguments; an argument-specific MCP `allow`/`ask` is skipped with a warning rather than broadened, while an argument-specific `deny` becomes a whole-tool deny with a warning. The optional `kimi-code.defaultPermissionMode` override writes Kimi\\'s top-level `default_permission_mode` (`manual` / `yolo` / `auto`), while `kimi-code.rules` accepts native rules that canonical categories cannot express and emits them first in their authored order. On import, Rulesync preserves the complete ordered rule list under `kimi-code.rules`, including rules that could otherwise fit the shared permission model, so regeneration cannot change Kimi\\'s first-match behavior. A `kimi-code.tools` override writes Kimi\\'s `[tools] enabled` / `disabled` lists — a separate enforcement layer from `[[permission.rules]]`, since a rule prompts while these remove the tool from every agent in every session. Entries pass through verbatim because the section uses agent-file tool syntax (exact built-in names, `mcp__server__*` globs) rather than the canonical category/pattern shape. Note that Kimi registers `[tools]` in its v2 engine, so today it applies under `kimi web` and experimental `kimi -p` rather than the interactive TUI. Like the MCP defaults, the section merges per key: authoring only `enabled` leaves a hand-written `disabled` list alone, and dropping the override leaves the section as it stands. Values are carried through exactly as written, empty lists included — `enabled = []` is an allowlist admitting _nothing_, the strictest setting there is, while an absent `enabled` means no allowlist at all, so the two are never interchanged. The TOML file is shared with hooks, the MCP timeout defaults and other Kimi settings, so updates merge in place and never delete the file. See the [Kimi Code permission docs](https://moonshotai.github.io/kimi-code/en/configuration/config-files.html).\\n\\n> **Qwen-only override (`qwencode` key):** Qwen\\'s `settings.json` exposes autonomy/sandbox controls with no canonical permission category — under `tools` (`approvalMode` = `plan`/`default`/`auto-edit`/`auto`/`yolo`, `autoAccept`, `sandbox`, `sandboxImage`, `disabled`, `visible` — the deferred-tool startup visibility list, union-merged by Qwen across scopes), `security` (`folderTrust`), and `permissions.autoMode` (the Auto Mode classifier config: `hints.{allow,softDeny,hardDeny}`, `environment`, `classifyAllShell`). Add a tool-scoped `qwencode` override to author them: `qwencode.tools` and `qwencode.security` are shallow-merged into the matching `settings.json` group at the **top level of that group** (an unrelated sibling key such as `tools.core` is preserved, an override key wins, and a nested object the override supplies such as `security.folderTrust` replaces the existing one wholesale rather than being deep-merged), while `qwencode.autoMode` is emitted as `permissions.autoMode` (replacing the existing `autoMode` wholesale) and the shared `permission` block keeps driving the `permissions.allow`/`ask`/`deny` arrays. On import, the documented autonomy keys (`tools.{approvalMode,autoAccept,sandbox,sandboxImage,disabled,visible}`, `security.folderTrust`, and `permissions.autoMode`) round-trip back into the override; other `tools`/`security` keys are left in `settings.json` and not extracted.\\n>\\n> ```json\\n> {\\n> \"permission\": { \"bash\": { \"*\": \"allow\" } },\\n> \"qwencode\": {\\n> \"tools\": { \"approvalMode\": \"auto-edit\" },\\n> \"security\": { \"folderTrust\": { \"enabled\": true } },\\n> \"autoMode\": { \"hints\": { \"allow\": [\"Running tests\"] }, \"classifyAllShell\": true }\\n> }\\n> }\\n> ```\\n>\\n> **Alias overlap:** Qwen\\'s `Read` is a meta-tool that also covers grep/glob/list, so canonical `grep`/`glob` rules are emitted as their own `Grep(...)`/`Glob(...)` entries but overlap Qwen\\'s `Read` category at runtime; and Qwen folds web search into `web_fetch`, so a canonical `websearch` rule (`WebSearch(...)`) may not correspond to a distinct Qwen tool. `tools.disabled` is a hard whole-tool disable (stronger than `deny`) and is only authorable via the override, not the canonical `deny`.\\n\\nFor Warp, this generates the command allow/deny regex lists in Warp\\'s global user `settings.toml` (**global mode only** — Warp has no project-scoped permissions file). Since Warp promoted file-backed execution profiles to Stable (2026-07-28), the surface runtime enforcement actually reads is the `command_allowlist` / `command_denylist` arrays of the `default` record under `[agents.execution_profiles.<id>]`; rulesync merges the lists into that `default` profile **in place** whenever the collection exists, preserving every other profile key and every other profile ID. The legacy `agent_mode_command_execution_allowlist` / `agent_mode_command_execution_denylist` keys under `[agents.profiles]` are still written for un-migrated installs and old clients — but on a migrated install they are inert (Warp consumes them only once during its one-shot migration). When the `[agents.execution_profiles]` collection does not exist yet, rulesync deliberately does **not** create it: on such an un-migrated install the legacy keys are still live, and creating the collection would mark Warp\\'s migration complete early and strand the user\\'s other legacy settings. Note that rulesync manages only the `default` profile — if a different execution profile is active in Warp, the generated lists (including `deny` rules) are not enforced until the user switches back to `default`. The settings file path differs per platform: macOS `~/.warp/settings.toml`, Linux `~/.config/warp-terminal/settings.toml`, Windows `%LOCALAPPDATA%\\\\warp\\\\Warp\\\\config\\\\settings.toml`. Only the `bash` category maps (`allow` → allowlist, `deny` → denylist); Warp matches commands with **regular expressions**, so patterns are emitted verbatim — author canonical `bash` patterns as regexes when targeting Warp (mirrors Zed). Warp has no per-command `ask` list, so `ask` rules are dropped, and non-`bash` categories are skipped (with a warning when they carry `deny` rules). On import, the `default` execution profile\\'s lists are preferred (falling back to the legacy keys when no collection exists), and a pattern present in both lists resolves to `deny` (Warp\\'s denylist wins). Both blocks are merged into the existing `settings.toml`, preserving other Warp settings, and the file is never deleted. **rulesync owns the command lists** (it is the source of truth): they are replaced from the rulesync config on each `--global` generate, so a manually curated Warp allowlist/denylist not mirrored in `.rulesync/permissions.jsonc` is overwritten — keep command permissions in rulesync (run `rulesync import` first to capture an existing hand-curated list). MCP allow/deny is a separate Warp surface not modeled here. See the [Warp agent profiles & permissions docs](https://docs.warp.dev/agent-platform/capabilities/agent-profiles-permissions/).\\n\\n> **Warp-only override (`warp` key):** Warp\\'s `[agents.profiles]` table also exposes file-read/read-only autonomy knobs that do not fit the per-command `allow`/`ask`/`deny` model — `agent_mode_coding_permissions` (`always_ask_before_reading` / `always_allow_reading` / `allow_reading_specific_files`), `agent_mode_coding_file_read_allowlist` (an array of paths the agent may read), and `agent_mode_execute_readonly_commands` (a boolean auto-executing read-only commands). Add a tool-scoped `warp` override to author them: its keys are merged into `[agents.profiles]` (the override wins) while the shared `permission` block keeps driving the command lists. On **import**, these keys are lifted from `settings.toml` into the `warp` override, so they round-trip faithfully instead of being dropped. **Known limitation:** these legacy autonomy keys are also part of Warp\\'s one-shot migration, so on a migrated install they are inert — their execution-profile counterparts (`read_files`, `directory_allowlist`, `execute_commands`, `mcp_allowlist`/`mcp_denylist`) are not yet authorable through rulesync. Example:\\n>\\n> ```json\\n> {\\n> \"permission\": { \"bash\": { \"git .*\": \"allow\" } },\\n> \"warp\": {\\n> \"agent_mode_coding_permissions\": \"always_allow_reading\",\\n> \"agent_mode_execute_readonly_commands\": true\\n> }\\n> }\\n> ```\\n>\\n> See the [Warp settings reference](https://docs.warp.dev/terminal/settings/all-settings/).\\n\\nFor the Antigravity IDE, this generates `permissions.allow`, `permissions.ask`, and `permissions.deny` arrays in the committable workspace `.antigravity/settings.json` (**project mode only**). Antigravity 2.0 evaluates these `Deny > Ask > Allow` and uses `action(target)` entries; rulesync maps canonical categories onto the IDE action vocabulary: `read` → `read_file`, `edit`/`write` → `write_file`, `bash` → `command`, `webfetch`/`websearch` → `read_url`, `mcp` → `mcp` (the IDE-only `execute_url` / `unsandboxed` actions have no canonical equivalent and pass through verbatim). Because `edit`/`write` collapse to `write_file` and `webfetch`/`websearch` collapse to `read_url`, importing normalizes back to `write` / `webfetch` (a documented, lossy mapping). The `settings.json` file holds other workspace settings, so the `permissions` block is merged in place — entries for unmanaged actions are preserved — and the file is never deleted. The User-scope settings file is a platform-dependent VS-Code-style path outside rulesync\\'s home-relative global model, so **global mode is not supported**; the workspace file is intended to be checked into git. See the [Antigravity permissions docs](https://antigravity.google/docs/permissions).\\n\\nFor the Antigravity CLI (`agy`), this generates `permissions.allow`, `permissions.ask`, and `permissions.deny` arrays in the global `~/.gemini/antigravity-cli/settings.json` (**global mode only**). The CLI shares Antigravity 2.0\\'s Fine-Grained Permissions Engine with the IDE, so the same `action(target)` vocabulary and `Deny > Ask > Allow` precedence apply: `read` → `read_file`, `edit`/`write` → `write_file`, `bash` → `command`, `webfetch`/`websearch` → `read_url`, `mcp` → `mcp` (the engine-only `execute_url` / `unsandboxed` actions pass through verbatim). Because `edit`/`write` collapse to `write_file` and `webfetch`/`websearch` collapse to `read_url`, importing normalizes back to `write` / `webfetch` (a documented, lossy mapping). The `settings.json` holds other CLI settings, so the `permissions` block is merged in place — entries for unmanaged actions are preserved — and the file is never deleted. Four CLI-only autonomy/sandbox knobs outside the allow/ask/deny arrays can be authored (and round-trip) through an optional `antigravity-cli` override block in `.rulesync/permissions.jsonc`: `toolPermission` (the global autonomy preset — `request-review` (default) / `proceed-in-sandbox` / `always-proceed` / `strict`), `enableTerminalSandbox` (a boolean confining agent-run commands to OS containment), `artifactReviewPolicy` (whether the agent\\'s artifact changes are gated on a review prompt — `asks-for-review` (default) / `agent-decides` / `always-proceed`) and `allowNonWorkspaceAccess` (a boolean, off by default, letting the agent read or write files outside the active workspace roots). Antigravity applies the allow/deny lists as per-rule exceptions to the preset at runtime, so rulesync authors these keys verbatim as top-level siblings of `permissions` with no precedence modeling. This override is **CLI-only** — the Antigravity IDE exposes the same concepts through a GUI with no documented JSON schema, so it does not apply to `antigravity-ide`. Example: `{ \"permission\": { … }, \"antigravity-cli\": { \"toolPermission\": \"strict\", \"enableTerminalSandbox\": true, \"artifactReviewPolicy\": \"agent-decides\", \"allowNonWorkspaceAccess\": false } }`. Verified against the [Antigravity CLI reference](https://antigravity.google/docs/cli/reference), [sandbox docs](https://antigravity.google/docs/cli/sandbox) and [settings reference](https://antigravity.google/docs/cli/settings). See the [Antigravity CLI permissions docs](https://antigravity.google/docs/cli-permissions).\\n\\nFor Rovo Dev CLI, this generates the `toolPermissions` block of the global `~/.rovodev/config.yml` (**global mode only** — Rovo Dev has no project-scoped permissions file, mirroring the Rovodev MCP adapter). Rovo Dev\\'s three levels (`allow`/`ask`/`deny`) are an exact 1:1 with rulesync\\'s canonical actions, so action values pass through verbatim. The `bash` category maps the catch-all `*` pattern to `bash.default` and every other pattern to a `bash.commands[]` entry `{ command: <pattern as regex>, permission }` (Rovo Dev matches commands as regexes, so author `bash` patterns accordingly). The `read` category maps to the inspection tools (`open_files`, `expand_code_chunks`, `expand_folder`, `grep`) and `edit`/`write` to the mutation tools (`find_and_replace_code`, `create_file`, `delete_file`, `move_file`), written under **`toolPermissions.tools`** — the depth Rovo Dev documents. (Earlier Rulesync versions wrote them one level up, directly under `toolPermissions`, where Rovo Dev ignores them; import still reads that legacy shape as a fallback for keys the nested block says nothing about, so an old file is not lost, and a regenerate deletes the stale copies.) Because these per-tool keys hold a single level (no per-pattern rules), only the catch-all `*` of each category sets the level. Rovo Dev rewrites a single tool key when the user answers \"always allow\" to one prompt, so the four keys of a category can disagree; import collapses them back onto one catch-all by taking the strictest level (`deny` > `ask` > `allow`) rather than whichever key is read last. Rovo Dev\\'s planning and Atlassian tools split the same way, so they ride the same two categories rather than getting one of their own: `read` also reaches `getJiraIssue` and `getConfluencePage`, and `edit`/`write` also reach `createJiraIssue`, `updateJiraIssue`, `createConfluencePage`, `updateConfluencePage` and `createTechnicalPlan` (grouped with the mutating tools because it is the planning tool that produces an artifact rather than reading one). Bear that in mind when authoring: an `edit: deny` reaches Jira and Confluence, not just the working tree. Because `edit` and `write` both map onto the same mutation tools, a conflicting catch-all between them cannot be represented; the stricter of the two levels is kept — the same `deny` > `ask` > `allow` rule import uses — and a warning is logged. Non-catch-all `allow` paths in those categories are surfaced as `allowedExternalPaths` so explicit grants are not dropped; non-`allow` non-catch-all rules cannot be expressed per-path and are skipped with a warning. Categories without a clean Rovo Dev target (e.g. `webfetch`) are skipped with a warning. `config.yml` holds all of Rovo Dev\\'s settings (`agent`, `sessions`, `mcp`, etc.), so the `toolPermissions` block is merged in place — every other top-level key is preserved, as is any key inside `toolPermissions` that Rulesync does not manage — including tools inside `toolPermissions.tools` that no canonical category maps to. On **import**, a tool key the file is silent about counts as the implicit fallback level (`toolPermissions.default`, or Rovo Dev\\'s own `ask`) rather than as absent, and the category still collapses to the strictest of the set. That matters because Rovo Dev writes a single key when the user answers \"always allow\" to one prompt: without the fallback, one such answer about `create_file` would import as a blanket `edit: allow`, and the next generate would hand that grant to every other tool of the category — Jira and Confluence writes included. A category the file says nothing about at all is still skipped rather than invented.\\n\\n**Migration note.** `toolPermissions.default` and the seven planning/Atlassian keys became Rulesync-owned in the release that added them. Ownership means the first generate after upgrading removes a hand-written value for one of them unless `.rulesync/permissions.*` produces it — a hand-written `tools.createJiraIssue: deny` or `default: deny` with no matching rule in the rulesync source is dropped (with a warning naming each key), falling back to Rovo Dev\\'s `ask`. Run `rulesync import --targets rovodev --features permissions` before the first generate to carry those values into the rulesync source.\\n\\nThe canonical all-tools category `*` maps to `toolPermissions.default`, the level Rovo Dev falls back to for any tool with no more specific setting (Rovo Dev\\'s own default is `ask`) — derived from its catch-all exactly as `bash.default` is derived from `bash`\\'s, and round-tripped back on import. The default is a single level, so a pattern rule inside the `*` category has no counterpart and is skipped with a warning. The keys Rulesync does manage (`default`, `bash`, `allowedExternalPaths`, and the per-tool keys above) are owned rather than merged: each generate rewrites them from `.rulesync/permissions.*`, so removing a rule there removes it from `config.yml` too (a source stating no rule at all clears them; one whose rules simply have no Rovo Dev counterpart keeps the block\\'s restrictions but strips its grants — an `allow` there is normally a leftover of an earlier generate, and dropping one falls back to Rovo Dev\\'s stricter default, whereas clearing the whole block would relax every level), logging a warning naming each owned key it removes — per-tool levels and `allowedExternalPaths` are written from inside a Rovo Dev session too, by an \"always allow\" prompt answer and the `/directories` command, and a hand-edit to one of those keys — including a path added with the in-session `/directories` command, which writes to `allowedExternalPaths` — is replaced on the next generate (values only — YAML comments and formatting in the existing file are not retained on rewrite) — and the file is never deleted. See the [Rovo Dev CLI settings](https://support.atlassian.com/rovo/docs/manage-rovo-dev-cli-settings/) and [tool permissions](https://support.atlassian.com/rovo/docs/use-tools-in-rovo-dev-cli/) docs.\\n\\nFor Goose, this generates the `user` block of the global `~/.config/goose/permission.yaml` (**global mode only** — Goose persists per-tool permission overrides only under the home directory and has no project-scoped permissions file). Goose stores permissions as a YAML map of mode key → `{ always_allow, ask_before, never_allow }`, where each field is a list of tool-name strings; rulesync writes the user-set decisions under the `user` key. Action mapping is a 1:1: `allow` → `always_allow`, `ask` → `ask_before`, `deny` → `never_allow`. Tool-name mapping: `bash` → `developer__shell`, `edit` → `developer__text_editor`; every other category passes through verbatim as the Goose tool name (so namespaced tools like `developer__text_editor` or `developer__image_processor` round-trip). Because Goose permission lists hold **whole tool names** rather than per-command/per-path globs, only a category\\'s catch-all `*` pattern is representable — non-catch-all patterns are skipped with a warning. `write` collapses onto `developer__text_editor` too, so a conflicting `edit`/`write` catch-all cannot be represented; `edit` takes precedence and a warning is logged. The `permission.yaml` file is merged in place: the `user` block is owned by rulesync, while every other top-level key (notably the `smart_approve` LLM-decision cache) is preserved, and the file is never deleted. See the [Goose tool permissions docs](https://goose-docs.ai/docs/guides/managing-tools/tool-permissions/).\\n\\nFor the Grok Build CLI (`grokcli`), this generates Grok\\'s Claude-style `[permission]` rule arrays — `allow` / `deny` / `ask` — in the project `./.grok/config.toml` (project mode) or the user `~/.grok/config.toml` (global mode, via `--global`). Grok documents that \"Project configs are limited to MCP servers, plugins, and permission rules, not full user configs\" ([settings docs](https://docs.x.ai/build/settings)), so the fine-grained `[permission]` rules are valid at both scopes. Each canonical `permission.<category>.<pattern>` becomes a Grok entry bucketed into the matching array: `bash`→`Bash`, `read`→`Read`, `edit`→`Edit`, `grep`→`Grep`, `webfetch`→`WebFetch`, and `mcp__<server>__<tool>`→`MCPTool(<server>__<tool>)`; a `*` pattern emits the bare tool name (e.g. `Bash`) and a concrete pattern emits `Tool(pattern)` (e.g. `Bash(git *)`). `write` collapses onto `Edit` (Grok has no separate `Write` tool — a documented lossy mapping), and categories with no Grok tool (`websearch`, `glob`, `notebookedit`, `agent`) are skipped, with a warning when a skipped category carries a `deny` rule. Grok evaluates the arrays with precedence `deny > ask > allow`, which import mirrors (a tool listed in multiple arrays resolves to the strictest action). The coarse `[ui] permission_mode` toggle (`\"ask\"` / `\"always-approve\"`) is still written as a backward-compatible fallback for older Grok versions: `always-approve` when the config is pure-`allow`, otherwise `ask` (conservative — never `always-approve` while any `deny`/`ask` rule exists, so it never contradicts the fine-grained arrays). On import, the `[permission]` arrays are parsed back into canonical categories when present; only when no `[permission]` section exists do we fall back to the coarse mode (`always-approve` ⇄ `bash: { \"*\": \"allow\" }`, `ask`/unset ⇄ `bash: { \"*\": \"ask\" }`). `config.toml` is shared with the MCP feature, so rulesync owns the `[permission]` `allow`/`deny`/`ask` arrays and `[ui] permission_mode` while every other key (e.g. `[mcp_servers]`, verbose `[permission] rules`, `[sandbox]`) is preserved, and the file is never deleted. See the [Grok CLI settings reference](https://docs.x.ai/build/settings/reference) and [modes docs](https://docs.x.ai/build/modes-and-commands).\\n\\nFor Vibe (mistral-vibe), this generates per-tool `[tools.<tool>]` tables in the shared `.vibe/config.toml` (project mode) or `~/.vibe/config.toml` (global mode). Tool-name mapping: `bash` → `bash`, `read` → `read_file`, `edit` → `edit`, `write` → `write_file`, `webfetch` → `web_fetch`, `websearch` → `web_search`, `agent` → `task`. These are Vibe\\'s builtin tool names (`BaseTool.get_name()`, the snake_case of each tool class); `edit` and `write_file` are distinct tools — `write_file` has been create-only since v2.14.0 — so the two canonical categories no longer collapse onto one name. **Migration:** a `config.toml` written by an earlier Rulesync may still carry `write_file` entries derived from the `edit` category, or inert `[tools.fetch]` / `[tools.search_web]` / `[tools.agent]` blocks. Rulesync only rewrites the names it now emits, so remove those stale entries by hand — a leftover `disabled_tools = [\"write_file\"]` keeps Vibe\\'s `write_file` disabled even though no canonical rule asks for it. Within a category, the catch-all `*` pattern sets the per-tool `permission` (`allow` → `always`, `ask` → `ask`) and also toggles the top-level `enabled_tools` / `disabled_tools` filters; specific patterns become **`allowlist` / `denylist`** entries — these are the keys Vibe\\'s permission engine actually reads (`BaseToolConfig`), so the legacy `allow` / `deny` keys are dropped on generate (still honored as a fallback on import). Vibe has no per-pattern `ask`, so pattern-level `ask` rules are skipped with a warning. The `config.toml` file is shared with the MCP feature, so writes merge non-destructively and the file is never deleted. See [mistral-vibe](https://github.com/mistralai/mistral-vibe) (`vibe/core/tools/base.py`).\\n\\n> **Vibe-only override (`vibe` key):** Vibe\\'s `BaseToolConfig` also carries a `sensitive_patterns` list — patterns that escalate to **ASK even when the base permission is ALWAYS** (allow). The canonical model can only set a pattern to a single `allow`/`ask`/`deny`, so an \"allow by default but ask on these patterns\" escalation cannot be expressed in the shared block. Add a tool-scoped `vibe` override to author it: `vibe.permission.<category>.sensitive_patterns` carries the list per canonical category (e.g. `bash`, `edit`), while the shared `permission` block still sets the base permission and allow/deny lists. On import, a tool\\'s `sensitive_patterns` round-trips back into the `vibe` override (the base allow stays in the shared block). rulesync owns the list for any category named in the override (a present list is set, an empty one clears it); categories not named keep whatever the existing `config.toml` had.\\n>\\n> ```json\\n> {\\n> \"permission\": { \"bash\": { \"*\": \"allow\" } },\\n> \"vibe\": { \"permission\": { \"bash\": { \"sensitive_patterns\": [\"rm *\", \"sudo *\"] } } }\\n> }\\n> ```\\n\\nFor Takt, this generates the `default_permission_mode` under `provider_profiles.<provider>` in the shared `.takt/config.yaml` (project mode) or `~/.takt/config.yaml` (global mode). Takt does not have per-tool / per-pattern rules; tool gating is a single coarse mode per provider profile, ordered `readonly` < `edit` < `full` (`readonly` may only read, `edit` may also edit/write files, `full` may also run shell commands). The active provider is named by the top-level `provider:` key (defaulting to `claude`). The mapping is therefore **lossy**: on generate, a single mode is derived with this precedence — (1) any `deny` rule anywhere ⇒ `readonly` (conservative — keep the narrowest mode whenever the user expressed any restriction); (2) else any `edit`/`write` category `allow` rule ⇒ `edit`; (3) else any `bash` category `allow` rule ⇒ `full`; (4) else ⇒ `readonly` (safe default). On import, `full` ⇄ `bash: { \"*\": \"allow\" }`, `edit` ⇄ `edit: { \"*\": \"allow\" }`, and `readonly` (or an unset/unknown mode) ⇄ `bash: { \"*\": \"deny\" }`. `config.yaml` is shared with other Takt settings, so the mode is merged in place — every other provider profile and all other top-level keys are preserved — and the file is never deleted. Takt\\'s default-deny **workflow security policies** — `workflow_arpeggio` (`custom_data_source_modules`, `custom_merge_inline_js`, `custom_merge_files`), `workflow_runtime_prepare.custom_scripts`, `workflow_command_gates.custom_scripts`, `sync_conflict_resolver.auto_approve_tools`, and the `allow_git_hooks` / `allow_git_filters` booleans — have no canonical permission category, so they are authored through the `takt` override block of `.rulesync/permissions.*` and round-trip on import. Each admits one class of user-supplied code, so only the exact shapes Takt itself accepts are written: a sub-key Takt does not declare is dropped with a warning rather than passed through, since Takt\\'s schemas are strict and reject the whole file on an unknown key, while a value of the wrong type fails when `.rulesync/permissions.*` is read. Removing one of these keys from `config.yaml` because the source no longer states it is warned about too — including a key put there by hand, which owning them implies. Deleting `.rulesync/permissions.*` altogether is different: the feature has no source to generate from, so nothing runs and whatever is in `config.yaml` stays. These keys are also authoritative rather than merged — revoking one in `.rulesync/permissions.*` removes it from `config.yaml`, instead of leaving the capability switched on. `workflow_mcp_servers` stays with the MCP feature, which derives it from the transports in use.\\n\\nTwo Takt-specific surfaces with no canonical category can be authored (and round-trip) through an optional `takt` override block in `.rulesync/permissions.jsonc`: `step_permission_overrides` (a per-workflow-step map `<step>` ⇒ `readonly`/`edit`/`full`, written inside the active provider profile and layered by Takt on top of `default_permission_mode`) and `provider_options` (a top-level, per-provider table of sandbox/network knobs orthogonal to the mode, e.g. `codex.network_access`, `claude.sandbox.allow_unsandboxed_commands`, `opencode.allowed_tools`). Example: `{ \"permission\": { … }, \"takt\": { \"step_permission_overrides\": { \"ai_review\": \"readonly\" }, \"provider_options\": { \"codex\": { \"network_access\": true } } } }`. Note the workflow-step `required_permission_mode` floor is a field of the **workflow YAML**, not `config.yaml`, so it is intentionally out of scope (Takt\\'s config loader hard-rejects unknown top-level keys). See the [Takt configuration docs](https://github.com/nrslib/takt/blob/main/docs/configuration.md).\\n\\nFor Amp, this writes to the shared `.amp/settings.json` (project mode) or `~/.config/amp/settings.json` (global mode), using **two** permission surfaces. In rulesync\\'s canonical model the category name **is** the Amp tool name. A **whole-tool deny** (pattern `*`) is written to the bare `amp.tools.disable` array (the tool name is pushed verbatim, preserving `builtin:` prefixes and the `*` glob) for backwards compatibility. Every **lossy** case is written to the ordered `amp.permissions` array instead of being dropped: an **argument-specific deny** (pattern `!== \"*\"`) becomes `{ tool, action: \"reject\", matches: { cmd: <pattern> } }`, and every `allow` / `ask` rule becomes `{ tool, action, matches?: { cmd } }` (the `matches` object is omitted for the `*` catch-all). Amp evaluates `amp.permissions` **first-match-wins**, so generated entries are ordered deterministically and fail-closed: sorted by tool name, then entries **with** `matches.cmd` (more specific) before catch-alls, then by action priority **`reject` < `ask` < `allow`**, then by `cmd`. `amp.permissions` is Amp\\'s documented **legacy / backwards-compatibility** surface — it remains functional and is the only place to express `allow`/`ask` and argument-specific `reject` rules. **Ownership:** rulesync OWNS and wholesale-replaces the `allow`/`ask`/`reject` entries on every generate, but **preserves any existing `action: \"delegate\"` entry** (rulesync\\'s canonical model has no `delegate` equivalent); preserved `delegate` entries are placed **after** the rulesync-generated entries (so the regenerated rules take precedence under first-match-wins). On **import**, both keys are read and merged into one canonical config: `amp.tools.disable[tool]` → `{ tool: { \"*\": \"deny\" } }`, and each `amp.permissions` entry → `{ tool: { (matches?.cmd ?? \"*\"): mapped } }` (`reject` → `deny`, `allow` → `allow`, `ask` → `ask`; `delegate` is skipped). When both sources target the same tool+pattern, the **most restrictive action wins** (`deny` > `ask` > `allow`). The settings file is shared with the MCP feature (`amp.mcpServers`), so all other keys are preserved on round-trip and the file is never deleted. Tool names and `cmd` patterns that are prototype-pollution keys (`__proto__`, `constructor`, `prototype`) are skipped defensively.\\n\\nAmp shapes with no canonical category are authored (and round-trip) through an optional `amp` override block in `.rulesync/permissions.jsonc`: `permissions` — extra `amp.permissions` entries with non-`cmd` matchers (`path`/`url`/`query`/…), regex/array match values, `context` (`thread`/`subagent`), `delegate` (+`to`), or `reject` (+`message`), appended **after** the canonical-generated entries (so generated allow/ask/reject rules take precedence under first-match-wins, with authored entries as later fallbacks); `mcpPermissions` — Amp\\'s `amp.mcpPermissions` array; `guardedFiles` — `amp.guardedFiles.allowlist` (globs allowed without confirmation); and `dangerouslyAllowAll` — `amp.dangerouslyAllowAll`. When the override authors `permissions` it becomes the source of truth for the extra entries; otherwise any hand-authored `delegate` entry in the existing file is preserved. On import, `amp.permissions` entries that are **not** canonical-expressible (non-`cmd` matcher, `delegate`, `reject`+`message`, `context`) are lifted verbatim into `amp.permissions` of the override rather than dropped. Example: `{ \"permission\": { … }, \"amp\": { \"dangerouslyAllowAll\": false, \"guardedFiles\": { \"allowlist\": [\"docs/**\"] }, \"permissions\": [{ \"tool\": \"Bash\", \"action\": \"delegate\", \"to\": \"approve.sh\" }] } }`. See the [Amp manual](https://ampcode.com/manual).\\n\\nFor JetBrains Junie CLI, this generates the Action Allowlist `rules` object in `~/.junie/allowlist.json` (**global mode only** — Junie CLI resolves exactly one allowlist path under its home directory and never reads a project-scope `.junie/allowlist.json`; verified against release `2383.10`). Junie evaluates the allowlist top-to-bottom (first match wins) and groups rules into buckets, onto which rulesync categories map: `bash` → `executables`, `edit`/`write` → `fileEditing`, `read` → `readOutsideProject`, `mcp` → `mcpTools`. Every rule group is written as Junie\\'s `AllowListRuleSet` **object** — `{ \"default\"?: \"allow\"|\"ask\", \"rules\": [ … ] }` — never a bare array: Junie\\'s parser rejects the array form for the **whole file** and then discards and overwrites `allowlist.json`, so the shape matters. Earlier rulesync versions emitted the array form; it is still tolerated on import, but only the object form is generated. Each rule carries an `action` plus either a literal `prefix` (matches commands that start with it) or a glob `pattern` (`*`, `**`, `?`, `[abc]`, `[!abc]`); rulesync emits `pattern` when the canonical pattern contains a glob metacharacter (`*`, `?`, `[`) and `prefix` otherwise. Junie accepts only `allow` and `ask` as actions — there is **no `deny`** (a `deny` fails the whole-file parse) — so a canonical `deny` is downgraded to the nearest valid action, `ask` (which still withholds auto-approval), with a warning (`allow`/`ask` map 1:1). Categories Junie cannot represent (e.g. `webfetch`, `websearch`) are skipped with a warning when they carry rules. rulesync **owns each mapped group\\'s rule list** (replaced on each generate), while a per-group `default` and the whole `readSecretFile` group — which restricts what Junie may read — are preserved from the existing file when not authored via the `junie` override below. Because `edit`/`write` both collapse onto `fileEditing`, importing normalizes back to `edit` (a documented, lossy mapping). The `allowlist.json` file is never deleted. See the [Junie Action Allowlist docs](https://junie.jetbrains.com/docs/action-allowlist-junie-cli.html).\\n\\n> **Junie-only override (`junie` key):** Junie\\'s `allowlist.json` has settings with no canonical per-glob slot — the top-level autonomy knobs `allowReadonlyCommands` (a boolean auto-allowing read-only commands) and `defaultBehavior` (the fallback action when no rule matches; an `allow`/`ask` enum — Junie\\'s `AllowListDecision` accepts nothing else, and an invalid value fails the whole-file parse), plus two group-shaped settings: `readSecretFile` (the fifth rule group, restricting reads of secret files — canonical `read` is already taken by `readOutsideProject`, so this group is authored whole as `{ \"default\"?, \"rules\": [ … ] }`) and `ruleDefaults` (each mapped group\\'s own fallback action, e.g. `{ \"executables\": \"ask\" }`). Add a tool-scoped `junie` override to author them: the scalar knobs are merged onto the top level of `allowlist.json` (the override wins) while the shared `permission` block keeps driving the mapped groups\\' rule lists, and the group-shaped settings land inside the `rules` object. On **import**, all of these are lifted from `allowlist.json` into the `junie` override, so they are authorable and portable instead of only round-trip-preserved. Any other unmodeled top-level key is preserved verbatim. Example:\\n>\\n> ```json\\n> {\\n> \"permission\": { \"bash\": { \"git \": \"allow\" } },\\n> \"junie\": {\\n> \"allowReadonlyCommands\": true,\\n> \"defaultBehavior\": \"ask\",\\n> \"ruleDefaults\": { \"executables\": \"ask\" },\\n> \"readSecretFile\": { \"rules\": [{ \"pattern\": \"**/.env\", \"action\": \"ask\" }] }\\n> }\\n> }\\n> ```\\n\\nFor Reasonix, this generates `permissions.allow`, `permissions.ask`, and `permissions.deny` arrays in the `[permissions]` table of the shared `reasonix.toml` (project mode) or `~/.reasonix/config.toml` (global mode) — the same TOML file the MCP feature\\'s `[[plugins]]` array-of-tables lives in. The rule syntax mirrors Claude Code\\'s: entries are `Bash(<pattern>)`, `Read(<pattern>)`, `Edit(<pattern>)`, `Write(<pattern>)`, `WebFetch(<pattern>)`, `WebSearch(<pattern>)`, `Grep(<pattern>)`, `Glob(<pattern>)`, `NotebookEdit(<pattern>)`, `Agent(<pattern>)`, etc. (Reasonix\\'s SPEC.md documents these as \"Claude Code-style\" families; `agent` → `Agent` is the one lower-confidence mapping, since Reasonix\\'s own delegation tool is internally named `task`). `[permissions].mode` (the writer fallback: `ask`/`allow`/`deny`) has no canonical rulesync equivalent and is preserved untouched. The TOML file is shared with the MCP feature, so writes only replace the `permissions` table — every other table (`[[plugins]]`, `[agent]`, `[ui]`, …) is preserved on round-trip, and the file is never deleted. See [SPEC.md §3.7 Permissions](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/SPEC.md).\\n\\n> **Reasonix-only override (`reasonix` key):** Reasonix has security axes orthogonal to per-tool allow/ask/deny with no canonical category — the `[sandbox]` enforcement table (`workspace_root`, `allow_write`, `forbid_read`, `bash` = `enforce`/`off`, `network`) and the plan-mode read-only command list under `[agent]` (`plan_mode_read_only_commands`, which upstream keeps for legacy compatibility only — Plan bash goes through Permissions now). Its sibling `plan_mode_allowed_tools` left the documented config surface in v1.17.18: an existing value is still lifted out of `[agent]` on import, so it does not vanish from an imported config, but whenever the override writes `[agent]` the key is removed from the file with a warning — including a value already there, since leaving that one alone would mean narrowing the list is the one edit that never lands. Add a tool-scoped `reasonix` override to author them: `reasonix.sandbox` and `reasonix.agent` are shallow-merged into the matching `reasonix.toml` table at its top level (override keys win, unrelated sibling keys such as `[agent].model` are preserved), while the shared `permission` block keeps driving `[permissions].allow`/`ask`/`deny`. On import, the whole `[sandbox]` table round-trips (it is a dedicated security surface) and only the plan-mode keys are lifted from `[agent]`.\\n>\\n> ```json\\n> {\\n> \"permission\": { \"bash\": { \"git status*\": \"allow\" } },\\n> \"reasonix\": {\\n> \"sandbox\": { \"bash\": \"enforce\", \"network\": false },\\n> \"agent\": { \"plan_mode_read_only_commands\": [\"gh pr diff\"] }\\n> }\\n> }\\n> ```\\n>\\n> The retired `[[plugins]].trusted_read_only_tools` MCP read-only trust list is per-plugin (an array-of-tables shared with the MCP feature) and is not covered by this override.\\n\\n> **Note: Interaction with deprecated ignore feature.** Both the ignore feature and the permissions feature can manage `Read` tool deny entries in `.claude/settings.json`. When both features configure the `Read` tool, the **permissions feature takes precedence** and a warning is emitted. Migrate the ignore patterns to `read` deny rules in `.rulesync/permissions.jsonc`, then remove `ignore` from the project features and delete the obsolete ignore source.\\n',\n \"reference/mcp-server\":\n '# Rulesync MCP Server\\n\\nRulesync provides an MCP (Model Context Protocol) server that enables AI agents to manage your Rulesync files. This allows AI agents to discover, read, create, update, and delete files dynamically.\\n\\n> [!NOTE]\\n> The MCP server exposes the only one tool to minimize your agent\\'s token usage. Approximately less than 1k tokens for the tool definition.\\n\\n## Supported Features and Operations\\n\\nThe single `rulesyncTool` multiplexes by `feature` and `operation`:\\n\\n- `rule`, `command`, `subagent`, `skill`: `list`, `get`, `put`, `delete`\\n- `ignore`, `mcp`, `permissions`, `hooks`: `get`, `put`, `delete`\\n- `generate`: `run`\\n- `import`: `run`\\n- `convert`: `run`\\n\\nThe `permissions` feature operates on `.rulesync/permissions.jsonc` and the `hooks` feature operates on `.rulesync/hooks.jsonc`. Both accept a `content` string (valid JSONC) on `put`.\\n\\n### `convert` / `run` options\\n\\nWhen invoking `feature: \"convert\"` with `operation: \"run\"`, pass `convertOptions` with the following shape:\\n\\n| Option | Type | Required | Description |\\n| ---------- | ---------- | -------- | ---------------------------------------------------------------------------------- |\\n| `from` | `string` | Yes | Source tool name (e.g. `\"claudecode\"`). Must be a valid `ToolTarget`. |\\n| `to` | `string[]` | Yes | One or more destination tool names. Must not be empty and must not include `from`. |\\n| `features` | `string[]` | No | Features to convert (e.g. `[\"rules\", \"commands\"]`). Defaults to `[\"*\"]`. |\\n| `global` | `boolean` | No | Convert global (user-scope) configurations. Defaults to `false`. |\\n| `dryRun` | `boolean` | No | Preview changes without writing files. Defaults to `false`. |\\n\\n## Usage\\n\\n### Starting the MCP Server\\n\\n```bash\\nrulesync mcp\\n```\\n\\nThis starts an MCP server using stdio transport that AI agents can communicate with.\\n\\n### Configuration\\n\\nAdd the Rulesync MCP server to your `.rulesync/mcp.jsonc`:\\n\\n```json\\n{\\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/mcp-schema.json\",\\n \"mcpServers\": {\\n \"rulesync-mcp\": {\\n \"type\": \"stdio\",\\n \"command\": \"npx\",\\n \"args\": [\"-y\", \"rulesync\", \"mcp\"],\\n \"env\": {}\\n }\\n }\\n}\\n```\\n',\n \"reference/supported-tools\":\n '# Supported Tools and Features\\n\\nRulesync supports both **generation** and **import** for All of the major AI coding tools:\\n\\n<!-- SUPPORTED_TOOLS_DOCS:BEGIN -->\\n\\n| Tool | --targets | rules | ignore | mcp | commands | subagents | skills | hooks | permissions | checks |\\n| ------------------------- | ------------------ | :---: | :----: | :------: | :------: | :-------: | :----: | :---: | :---------: | :----: |\\n| AGENTS.md | agentsmd | ✅ | | | 🎮 | 🎮 | 🎮 | | | |\\n| AgentsSkills | agentsskills | | | | | | ✅ 🌏 | | | |\\n| Amp | amp | ✅ 🌏 | | ✅ 🌏 | | | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 |\\n| Claude Code | claudecode | ✅ 🌏 | ✅ | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | |\\n| Claude Code plugin | claudecode-plugin | | | ✅ | ✅ | ✅ | ✅ | ✅ | | |\\n| Codex CLI | codexcli | ✅ 🌏 | | ✅ 🌏 🔧 | 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | |\\n| GitHub Copilot | copilot | ✅ 🌏 | | ✅ | ✅ | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ | |\\n| GitHub Copilot CLI | copilotcli | ✅ 🌏 | | ✅ 🌏 | | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | | |\\n| Goose | goose | ✅ 🌏 | | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ | ✅ 🌏 | 🌏 | |\\n| Hermes Agent | hermesagent | ✅ | ✅ | 🌏 🔧 | 🌏 | ✅ 🌏 | 🌏 | 🌏 | 🌏 | ✅ |\\n| Grok CLI | grokcli | ✅ 🌏 | | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | |\\n| Cursor | cursor | ✅ | ✅ | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ |\\n| deepagents-cli | deepagents | ✅ 🌏 | | ✅ 🌏 | | ✅ 🌏 | ✅ 🌏 | 🌏 | | |\\n| Factory Droid | factorydroid | ✅ 🌏 | | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | |\\n| OpenCode | opencode | ✅ 🌏 | | ✅ 🌏 🔧 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | |\\n| Cline | cline | ✅ 🌏 | ✅ | 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | | ✅ | |\\n| Kilo Code | kilo | ✅ 🌏 | ✅ | ✅ 🌏 🔧 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | |\\n| Kimi Code | kimi-code | ✅ 🌏 | | ✅ 🌏 🔧 | | ✅ 🌏 | ✅ 🌏 | 🌏 | 🌏 | |\\n| Roo Code | roo | ✅ 🌏 | ✅ | ✅ | ✅ 🌏 | ✅ | ✅ 🌏 | | | |\\n| Rovodev (Atlassian) | rovodev | ✅ 🌏 | | 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | | 🌏 | ✅ |\\n| Takt | takt | ✅ 🌏 | | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | | ✅ 🌏 | ✅ 🌏 |\\n| Vibe Code | vibe | ✅ 🌏 | ✅ | ✅ 🌏 🔧 | | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | |\\n| Qwen Code | qwencode | ✅ 🌏 | ✅ | ✅ 🌏 🔧 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | |\\n| Reasonix | reasonix | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | |\\n| Kiro ⚠️ | kiro | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 🔧 | ✅ | ✅ | ✅ | ✅ | ✅ | |\\n| Kiro CLI | kiro-cli | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 🔧 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ | ✅ | |\\n| Kiro IDE | kiro-ide | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 🔧 | ✅ | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ | |\\n| Google Antigravity IDE | antigravity-ide | ✅ 🌏 | | ✅ 🌏 🔧 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ | |\\n| Google Antigravity CLI | antigravity-cli | ✅ 🌏 | ✅ | ✅ 🌏 🔧 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | 🌏 | |\\n| Google Antigravity plugin | antigravity-plugin | ✅ | | ✅ 🔧 | | ✅ | ✅ | ✅ | | |\\n| JetBrains AI Assistant | aiassistant | ✅ | ✅ | ✅ 🌏 | | | ✅ | | | |\\n| JetBrains Junie | junie | ✅ 🌏 | ✅ | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | 🌏 | 🌏 | |\\n| AugmentCode | augmentcode | ✅ 🌏 | ✅ | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | |\\n| Devin Desktop | devin | ✅ 🌏 | ✅ | ✅ 🌏 🔧 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | |\\n| Warp | warp | ✅ 🌏 | ✅ | ✅ 🌏 | ✅ 🌏 | | ✅ 🌏 | | 🌏 | |\\n| Replit | replit | ✅ | | | | | ✅ 🌏 | | | |\\n| Pi Coding Agent | pi | ✅ 🌏 | | | ✅ 🌏 | | ✅ 🌏 | ✅ 🌏 | | |\\n| Zed | zed | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | | | ✅ 🌏 | | ✅ 🌏 | |\\n\\n<!-- SUPPORTED_TOOLS_DOCS:END -->\\n\\n- ✅: Supports project mode\\n- 🌏: Supports global mode\\n- 🎮: Supports simulated commands/subagents/skills (Project mode only)\\n- 🔧: Supports MCP tool config (`enabledTools`/`disabledTools`)\\n- ⚠️: Deprecated — still supported, but see the note below\\n\\n## Hermes Agent compatibility\\n\\nThe `hermesagent` target is validated against Hermes Agent v0.19.0 (release\\n`v2026.7.20`). The supported contract covers project rules, ignore patterns,\\nsubagents, and checks, plus global MCP servers, commands, subagents, skills,\\nhooks, and permissions. Generation, `--check`, and import round-trips are\\ncovered for both advertised scopes.\\n\\nRulesync honors Hermes profiles through `HERMES_HOME`. When it is set, its value\\nis the profile root itself: global configuration is read and written directly\\nunder `$HERMES_HOME` (`config.yaml`, `skills/`, `plugins/`, and `rulesync/`),\\nwithout appending `.hermes`. When it is unset, Rulesync follows Hermes\\'s own\\nplatform default: `~/.hermes` everywhere except Windows, where it is\\n`%LOCALAPPDATA%\\\\hermes`. Because `HERMES_HOME` names where Hermes itself reads\\nthe profile, it also takes precedence over `--output-roots` in global scope.\\nProject-scoped paths remain rooted in the project.\\n\\nProject plugins are registered by adding their names to\\n`$HERMES_HOME/config.yaml`, but Rulesync does not persist Hermes\\'s global\\nproject-plugin trust gate. Run Hermes from a trusted project root with\\n`HERMES_ENABLE_PROJECT_PLUGINS=true` for an explicit, session-scoped opt-in. A\\nfuture Hermes release that changes its loaders, schemas, or plugin API requires\\na new compatibility validation.\\n\\n## Deprecation notes\\n\\n- **Google Antigravity (`antigravity-ide` / `antigravity-cli`)** — Antigravity 2.0 splits into two products: the desktop **`antigravity-ide`** and the **`antigravity-cli`** (`agy`). As of Antigravity 2.0 the IDE reads its global MCP config and skills from the shared `~/.gemini/config/` tree — `~/.gemini/config/mcp_config.json` and `~/.gemini/config/skills/`, matching the current [MCP](https://antigravity.google/docs/mcp) and [Skills](https://antigravity.google/docs/skills) docs. The `antigravity-cli` global MCP config also lives in the shared `~/.gemini/config/mcp_config.json`, while the CLI keeps its own global skills tree at `~/.gemini/antigravity-cli/skills/`. Both targets also intentionally **share** the global rule file `~/.gemini/GEMINI.md` and the global hooks file `~/.gemini/config/hooks.json` — enabling both targets in `--global` mode writes those shared files once. For project-scope rules, **both `antigravity-ide` and `antigravity-cli`** emit the root rule as a plain cross-tool **`AGENTS.md`** at the project root (the Gemini-lineage discovery order is `AGENTS.md`, `CONTEXT.md`, `GEMINI.md`; the IDE has read `AGENTS.md` since v1.20.3) and non-root rules under `.agents/rules/` (the IDE adds trigger frontmatter to non-root rules; the CLI keeps them as plain markdown). For **commands (workflows)**, both targets share the project `.agents/workflows/` directory (invoked as `/workflow-name`); in `--global` mode the IDE writes to `~/.gemini/antigravity/global_workflows/` while the CLI keeps its own `~/.gemini/antigravity-cli/global_workflows/` tree (mirroring the CLI\\'s global skills tree).\\n- **Kiro (`kiro`)** — Kiro ships as two products with diverging config formats: the **Kiro IDE** reads Markdown subagents (`.kiro/agents/*.md`) and structured JSON hooks (`.kiro/hooks/*.json`, format `{ \"version\": \"v1\", \"hooks\": [ ... ] }`), while the **Kiro CLI** reads JSON agent-config subagents (`.kiro/agents/*.json`) and agent hooks in `.kiro/agents/default.json`. A single target cannot emit both faithfully, so `kiro` is split into **`kiro-cli`** and **`kiro-ide`**. The legacy `kiro` target is kept as a **deprecated alias** (its current mixed output is unchanged for backward compatibility). Shared surfaces (steering rules with `inclusion`, `.kiro/settings/mcp.json`, `.kiro/prompts/` commands, `.kiro/skills/`, `.kiroignore`, permissions) are identical between the two; they differ in **subagents** (`.md` vs `.json`) and **hooks**. Kiro IDE **hooks** are emitted as a single `.kiro/hooks/rulesync.json` (whose `hooks` array holds every generated hook) in both project (`.kiro/hooks/`) and global (`~/.kiro/hooks/`) scope, mapping canonical lifecycle events to the IDE\\'s PascalCase triggers (`SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, `Stop`) and supporting both `agent` (prompt) and `command` actions; the Kiro CLI continues to emit agent hooks in `.kiro/agents/default.json`, including `cacheTtl` ⇄ `cache_ttl_seconds`. Global **skills** (`~/.kiro/skills/`), global **ignore** (`~/.kiro/settings/kiroignore`), and global Kiro IDE **subagents** (`~/.kiro/agents/`) are also supported, as are global Kiro CLI **commands** (`~/.kiro/prompts/`) and **subagents** (`~/.kiro/agents/`). Kiro\\'s shared MCP file preserves per-server `disabledTools`.\\n',\n \"tools/takt\":\n \"# Takt\\n\\n[Takt](https://github.com/nrslib/takt) is a faceted-prompting AI coding workflow tool. Rulesync generates plain-Markdown facet files into Takt's `.takt/facets/` layout (or `~/.takt/facets/` in global mode).\\n\\n## Output mapping\\n\\nEach rulesync feature maps onto a dedicated Takt facet directory. The target directory is fixed per feature, except that **rules** may opt into Takt's fifth facet — `output-contracts` — via the `takt.facet` override (see below).\\n\\n| Rulesync feature | Takt facet directory |\\n| ---------------- | --------------------------------------------------------------------------------------- |\\n| `rules` | `.takt/facets/policies/` (default) or `.takt/facets/output-contracts/` via `takt.facet` |\\n| `commands` | `.takt/facets/instructions/` |\\n| `subagents` | `.takt/facets/personas/` |\\n| `skills` | `.takt/facets/knowledge/` |\\n\\nTakt-specific frontmatter knobs:\\n\\n```yaml\\n---\\ntakt:\\n name: my-renamed-stem # rename the emitted filename stem\\n extends: base # emit a leading {extends:base} facet-inheritance directive\\n facet: output-contracts # \\\"policies\\\" (default) or \\\"output-contracts\\\"\\n---\\n```\\n\\n- `takt.name` is **optional**; the source filename stem is used by default. Unsafe values (path separators, `..` segments, etc.) raise a hard validation error at `generate` time.\\n- `takt.facet` is **optional** and defaults to `policies`. Setting it to `output-contracts` redirects the rule to Takt's output-structure / report-template facet, which has no dedicated rulesync feature. Both `policies` and `output-contracts` support `{extends:...}` inheritance. The other facets (`instructions`, `personas`, `knowledge`) are owned by the commands, subagents, and skills features and are not selectable via `takt.facet`.\\n- Like `takt.name` and `takt.extends`, `takt.facet` is a generate-side authoring control. Because Takt facet files are plain Markdown with no frontmatter, the facet selection cannot be recovered on import (see [Importing](#importing-existing-takt-files-into-rulesync) below).\\n\\nOutput files are **plain Markdown** — the source frontmatter is dropped entirely and the body is written verbatim:\\n\\n```\\n.rulesync/rules/style.md → .takt/facets/policies/style.md\\n.rulesync/rules/review-format.md → .takt/facets/output-contracts/review-format.md (with takt.facet: output-contracts)\\n.rulesync/commands/review.md → .takt/facets/instructions/review.md\\n.rulesync/subagents/coder.md → .takt/facets/personas/coder.md\\n.rulesync/skills/oncall/SKILL.md → .takt/facets/knowledge/oncall.md\\n```\\n\\n## MCP (partial — transport allowlist only)\\n\\nTakt has no project- or global-level registry of MCP server _definitions_: the concrete `mcp_servers` map (`command`/`args`/`env` or `type`/`url`/`headers`) is declared **per workflow step** inside individual workflow YAML files, and Takt's `config.yaml` loader rejects unknown top-level keys. The one MCP knob `config.yaml` does expose is the **default-deny transport allowlist** `workflow_mcp_servers: { stdio, sse, http }`; until a transport is enabled there, every workflow-defined MCP server using it is refused.\\n\\nRulesync therefore emits **only** this allowlist into the shared `.takt/config.yaml` (project) / `~/.takt/config.yaml` (global), turning on exactly the transports the servers in `.rulesync/mcp.jsonc` use (`local`/`stdio` → `stdio`, `sse` → `sse`, `http`/`streamable-http`/`ws` → `http`). The merge is in place, so the active provider, provider profiles, and all other config keys are preserved; the file is never deleted.\\n\\n**Lossiness:** the per-server names, commands, env, URLs, and headers are not representable in `config.yaml` and are intentionally not written — you still declare the concrete servers in your workflow YAML steps; Rulesync only opens the transport gate that permits them. Because of this, reverse import cannot reconstruct server definitions and yields an empty `mcpServers` map.\\n\\n## Checks — quality gates\\n\\n`.rulesync/checks/*.md` become TAKT **quality gates** in the `workflow_overrides` block of the shared `config.yaml`. A check's body is a string gate — a completion directive TAKT injects into the agent step prompt — unless the check's `takt` frontmatter block names a `command`, which makes it a command gate TAKT runs after the step, failing the gate on a non-zero exit.\\n\\n**A command gate runs unconditionally.** TAKT's default-deny `workflow_command_gates.custom_scripts` policy applies to gates declared in workflow YAML, not to gates coming from `workflow_overrides`, so a `takt.command` in a check is executed after every step it applies to with no further gating. Read the frontmatter of any check you obtain with `rulesync fetch` before generating.\\n\\n**Lossiness:** TAKT gates carry no severity or tool allowlist, so a check's `severity` and `tools` fields are not written and do not come back on import.\\n\\n`quality_gates_edit_only` in a check's `takt` block applies to the whole block, and reaches only the gates with no `steps` / `personas` scope — TAKT runs a scoped gate whether or not the step may edit files.\\n\\nThe block is owned by the checks feature: it is rewritten from `.rulesync/checks/` on every generate, and retracted when checks remain but none target TAKT. Emptying `.rulesync/checks/` altogether leaves the gates in place — the feature has no source to generate from — so delete them by hand in that case. See [file formats](../reference/file-formats.md) for the frontmatter reference.\\n\\n## Scope\\n\\nBoth project mode (`.takt/facets/...`, `.takt/config.yaml`) and global mode (`~/.takt/facets/...`, `~/.takt/config.yaml`) are supported.\\n\\n## Importing existing TAKT files into rulesync\\n\\nImporting the **facet** features (rules, commands, subagents, skills) is **not supported**. TAKT facet files are plain Markdown with no frontmatter, so the original skill / command / subagent metadata cannot be recovered. Attempting to import a TAKT skill raises a clear error rather than silently producing a stub that round-trips badly.\\n\\nThe `config.yaml` features do import: `rulesync import --targets takt --features checks` reads the quality gates back into `.rulesync/checks/`, and `--features permissions` reads the permission mode and the Takt-specific override keys. MCP is the exception noted above — the allowlist carries no server definitions to reconstruct.\\n\",\n};\n","import MiniSearch from \"minisearch\";\n\nimport { DOCS_CONTENT } from \"../../generated/docs-content.js\";\nimport type { Logger } from \"../../utils/logger.js\";\n\nexport type DocsOptions = {\n search?: string;\n};\n\n/**\n * The command's product is its stdout (piped to other tools, read by agents),\n * so it is written directly rather than through the logger — logger output is\n * suppressed under --silent and in test environments, which must not swallow\n * the requested document.\n */\nfunction printLine(line: string): void {\n process.stdout.write(`${line}\\n`);\n}\n\n/**\n * Exit quietly when the consumer closes the pipe early (`rulesync docs faq |\n * head`); every other stream error keeps its default crash behavior.\n */\nlet closedPipeTolerated = false;\nfunction tolerateClosedPipe(): void {\n if (closedPipeTolerated) {\n return;\n }\n closedPipeTolerated = true;\n process.stdout.once(\"error\", (error: NodeJS.ErrnoException) => {\n if (error.code === \"EPIPE\") {\n process.exit(0);\n }\n throw error;\n });\n}\n\n/** Maximum number of search results printed. */\nconst SEARCH_RESULT_LIMIT = 10;\n\n/**\n * Normalize a user-supplied document identifier to the bundled-content key:\n * forward slashes, no leading `docs/`, no trailing `.md`. Returns null for\n * identifiers that try to escape the bundled tree (absolute paths, `..`\n * segments, drive letters) — the command only ever serves the embedded\n * Markdown map, but rejecting these keeps the contract explicit and the\n * error message honest.\n */\nexport function normalizeDocId(input: string): string | null {\n const slashed = input.replaceAll(\"\\\\\", \"/\").trim();\n if (slashed === \"\" || slashed.startsWith(\"/\") || /^[A-Za-z]:/.test(slashed)) {\n return null;\n }\n const segments = slashed.split(\"/\").filter((segment) => segment !== \"\" && segment !== \".\");\n if (segments.some((segment) => segment === \"..\")) {\n return null;\n }\n if (segments[0] === \"docs\") {\n segments.shift();\n }\n if (segments.length === 0) {\n return null;\n }\n const joined = segments.join(\"/\");\n return joined.endsWith(\".md\") ? joined.slice(0, -\".md\".length) : joined;\n}\n\n/** First `# ` heading of a document, or its identifier when none exists. */\nfunction titleOf(id: string, content: string): string {\n const match = content.match(/^#\\s+(.+)$/m);\n return match?.[1]?.trim() ?? id;\n}\n\n/** All `##`+ headings of a document, joined for indexing. */\nfunction headingsOf(content: string): string {\n return [...content.matchAll(/^#{2,6}\\s+(.+)$/gm)]\n .map((match) => match[1]?.trim() ?? \"\")\n .join(\"\\n\");\n}\n\n/**\n * First line containing any of the search terms, trimmed as a snippet, so a\n * result identifies where the match lives. Falls back to the title line.\n */\nfunction contextSnippet(content: string, terms: string[]): string {\n const lowerTerms = terms.map((term) => term.toLowerCase()).filter((term) => term.length > 0);\n for (const line of content.split(\"\\n\")) {\n const lowerLine = line.toLowerCase();\n if (lowerTerms.some((term) => lowerLine.includes(term))) {\n const trimmed = line.trim();\n return trimmed.length > 160 ? `${trimmed.slice(0, 157)}...` : trimmed;\n }\n }\n return content.split(\"\\n\")[0]?.trim() ?? \"\";\n}\n\nfunction buildSearchIndex(): MiniSearch<{\n id: string;\n title: string;\n headings: string;\n body: string;\n}> {\n const miniSearch = new MiniSearch({\n fields: [\"id\", \"title\", \"headings\", \"body\"],\n searchOptions: {\n // Titles and headings identify a document better than a body mention.\n boost: { title: 4, headings: 3, id: 2 },\n },\n });\n miniSearch.addAll(\n Object.entries(DOCS_CONTENT).map(([id, content]) => ({\n id,\n title: titleOf(id, content),\n headings: headingsOf(content),\n body: content,\n })),\n );\n return miniSearch;\n}\n\n/**\n * `rulesync docs` — print bundled documentation.\n *\n * - `rulesync docs` lists every available document identifier.\n * - `rulesync docs <document>` prints the document's Markdown verbatim.\n * - `rulesync docs --search <text>` prints ranked matches, one per line, as\n * `<document> — <matching context>`.\n *\n * Missing documents, empty/matchless searches, and combining a document\n * argument with `--search` are errors (non-zero exit via the thrown Error).\n */\nexport async function docsCommand(\n logger: Logger,\n document: string | undefined,\n options: DocsOptions,\n): Promise<void> {\n if (options.search !== undefined && document !== undefined) {\n throw new Error(\"Specify either a document or --search <text>, not both.\");\n }\n tolerateClosedPipe();\n\n if (options.search !== undefined) {\n const query = options.search.trim();\n if (query === \"\") {\n throw new Error(\"--search requires a non-empty search text.\");\n }\n const results = buildSearchIndex().search(query).slice(0, SEARCH_RESULT_LIMIT);\n if (results.length === 0) {\n throw new Error(`No documents match '${query}'. Run 'rulesync docs' to list documents.`);\n }\n const terms = query.split(/\\s+/);\n for (const result of results) {\n const content = Object.hasOwn(DOCS_CONTENT, result.id) ? (DOCS_CONTENT[result.id] ?? \"\") : \"\";\n printLine(`${result.id} — ${contextSnippet(content, terms)}`);\n }\n logger.debug(`Found ${results.length} matching document(s).`);\n return;\n }\n\n if (document === undefined) {\n for (const id of Object.keys(DOCS_CONTENT).toSorted()) {\n printLine(id);\n }\n return;\n }\n\n const id = normalizeDocId(document);\n if (id === null) {\n throw new Error(`Invalid document identifier: '${document}'.`);\n }\n // `Object.hasOwn` keeps prototype keys such as `constructor` from leaking\n // inherited values past the undefined check.\n const content = Object.hasOwn(DOCS_CONTENT, id) ? DOCS_CONTENT[id] : undefined;\n if (content === undefined) {\n throw new Error(`Unknown document '${id}'. Run 'rulesync docs' to list documents.`);\n }\n // Print verbatim (the embedded content already ends with a newline) so the\n // output can be piped to other tools.\n process.stdout.write(content);\n}\n","import { dirname, join, relative, resolve } from \"node:path\";\n\nimport { type ParseError, parse as parseJsonc, printParseErrorCode } from \"jsonc-parser\";\n\nimport {\n CONFLICTING_TARGET_PAIRS,\n ConfigFileSchema,\n GITIGNORE_DESTINATION_KEY,\n} from \"../../config/config.js\";\nimport {\n RULESYNC_CONFIG_RELATIVE_FILE_PATH,\n RULESYNC_CONFIG_SCHEMA_URL,\n RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH,\n} from \"../../constants/rulesync-paths.js\";\nimport { ALL_FEATURES, DEPRECATED_FEATURE_REPLACEMENTS } from \"../../types/features.js\";\nimport { CLIError, ErrorCodes } from \"../../types/json-output.js\";\nimport { ALL_TOOL_TARGETS } from \"../../types/tool-targets.js\";\nimport { directoryExists, fileExists, readFileContent, resolvePath } from \"../../utils/file.js\";\nimport type { Logger } from \"../../utils/logger.js\";\n\nexport type DoctorSeverity = \"error\" | \"warning\" | \"info\";\n\n/**\n * A single diagnostic produced by `rulesync doctor`. Checks stay small and\n * independently testable by returning arrays of these instead of logging or\n * throwing directly.\n */\nexport type DoctorDiagnostic = {\n severity: DoctorSeverity;\n /** Stable machine-readable code, e.g. \"config/unknown-key\". */\n code: string;\n /** Path of the offending file, relative to the working directory. */\n file: string;\n message: string;\n /** Concrete fix suggestion, when one is known. */\n hint?: string;\n /** 1-based position, present when the JSONC parser reports an offset. */\n line?: number;\n column?: number;\n};\n\nexport type DoctorOptions = {\n config?: string;\n strict?: boolean;\n verbose?: boolean;\n silent?: boolean;\n};\n\n/** Per-feature object form also accepts a gitignore destination override. */\nconst PER_FEATURE_EXTRA_KEYS = [GITIGNORE_DESTINATION_KEY] as const;\n\nconst KNOWN_CONFIG_KEYS = Object.keys(ConfigFileSchema.shape);\n\n/**\n * Classic dynamic-programming Levenshtein distance; inputs are short config\n * keys and tool names, so the O(a.b) cost is negligible.\n */\nexport function levenshteinDistance({ a, b }: { a: string; b: string }): number {\n const rows = a.length + 1;\n const cols = b.length + 1;\n let previous = Array.from({ length: cols }, (_, i) => i);\n for (let i = 1; i < rows; i++) {\n const current = [i, ...Array.from({ length: cols - 1 }, () => 0)];\n for (let j = 1; j < cols; j++) {\n const substitutionCost = a[i - 1] === b[j - 1] ? 0 : 1;\n current[j] = Math.min(\n (previous[j] ?? 0) + 1,\n (current[j - 1] ?? 0) + 1,\n (previous[j - 1] ?? 0) + substitutionCost,\n );\n }\n previous = current;\n }\n return previous[cols - 1] ?? 0;\n}\n\n/**\n * Returns the closest candidate to `input`, or undefined when nothing is close\n * enough to be a plausible typo. The threshold scales with input length so\n * short keys don't produce far-fetched suggestions.\n */\nexport function suggestNearest({\n input,\n candidates,\n}: {\n input: string;\n candidates: readonly string[];\n}): string | undefined {\n const maxDistance = Math.max(2, Math.floor(input.length / 3));\n let best: string | undefined;\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const candidate of candidates) {\n const distance = levenshteinDistance({ a: input.toLowerCase(), b: candidate.toLowerCase() });\n if (distance < bestDistance) {\n bestDistance = distance;\n best = candidate;\n }\n }\n return bestDistance <= maxDistance ? best : undefined;\n}\n\nfunction didYouMean({\n input,\n candidates,\n}: {\n input: string;\n candidates: readonly string[];\n}): string | undefined {\n const suggestion = suggestNearest({ input, candidates });\n return suggestion === undefined ? undefined : `Did you mean '${suggestion}'?`;\n}\n\n/** Converts a character offset into a 1-based line/column pair. */\nexport function offsetToPosition({ content, offset }: { content: string; offset: number }): {\n line: number;\n column: number;\n} {\n let line = 1;\n let lineStart = 0;\n const end = Math.min(offset, content.length);\n for (let i = 0; i < end; i++) {\n if (content[i] === \"\\n\") {\n line++;\n lineStart = i + 1;\n }\n }\n return { line, column: end - lineStart + 1 };\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction checkTargetName({\n name,\n file,\n context,\n}: {\n name: string;\n file: string;\n context: string;\n}): DoctorDiagnostic | undefined {\n if ((ALL_TOOL_TARGETS as readonly string[]).includes(name)) return undefined;\n return {\n severity: \"error\",\n code: \"config/unknown-target\",\n file,\n message: `Unknown tool target '${name}' in ${context}.`,\n hint:\n didYouMean({ input: name, candidates: ALL_TOOL_TARGETS }) ??\n `Valid targets: ${ALL_TOOL_TARGETS.join(\", \")}.`,\n };\n}\n\nfunction checkFeatureName({\n name,\n file,\n context,\n}: {\n name: string;\n file: string;\n context: string;\n}): DoctorDiagnostic | undefined {\n const replacement = DEPRECATED_FEATURE_REPLACEMENTS[name];\n if (replacement !== undefined) {\n return {\n severity: \"warning\",\n code: \"config/deprecated-feature\",\n file,\n message: `Feature '${name}' in ${context} is deprecated.`,\n hint: `Use the '${replacement}' feature instead.`,\n };\n }\n if ((ALL_FEATURES as readonly string[]).includes(name)) return undefined;\n return {\n severity: \"error\",\n code: \"config/unknown-feature\",\n file,\n message: `Unknown feature '${name}' in ${context}.`,\n hint:\n didYouMean({ input: name, candidates: ALL_FEATURES }) ??\n `Valid features: ${ALL_FEATURES.join(\", \")}.`,\n };\n}\n\nfunction checkTargetsValue({\n targets,\n file,\n}: {\n targets: unknown;\n file: string;\n}): DoctorDiagnostic[] {\n const diagnostics: DoctorDiagnostic[] = [];\n if (Array.isArray(targets)) {\n for (const entry of targets) {\n if (typeof entry !== \"string\") {\n diagnostics.push({\n severity: \"error\",\n code: \"config/invalid-value\",\n file,\n message: `'targets' entries must be strings, found ${JSON.stringify(entry)}.`,\n });\n continue;\n }\n if (entry === \"*\") continue;\n const diagnostic = checkTargetName({ name: entry, file, context: \"'targets'\" });\n if (diagnostic) diagnostics.push(diagnostic);\n }\n return diagnostics;\n }\n if (isPlainObject(targets)) {\n for (const [key, value] of Object.entries(targets)) {\n if (key === \"*\") {\n diagnostics.push({\n severity: \"error\",\n code: \"config/invalid-value\",\n file,\n message:\n \"Wildcard '*' is not supported as a key in the object form of 'targets'; \" +\n \"per-target options cannot be attached to a wildcard.\",\n hint: 'Use the array form `\"targets\": [\"*\"]` instead.',\n });\n continue;\n }\n const diagnostic = checkTargetName({\n name: key,\n file,\n context: \"the 'targets' object\",\n });\n if (diagnostic) {\n diagnostics.push(diagnostic);\n continue;\n }\n diagnostics.push(...checkPerTargetFeaturesValue({ target: key, value, file }));\n }\n return diagnostics;\n }\n if (targets !== undefined) {\n diagnostics.push({\n severity: \"error\",\n code: \"config/invalid-value\",\n file,\n message: `'targets' must be an array of tool names or a per-target object, found ${JSON.stringify(targets)}.`,\n });\n }\n return diagnostics;\n}\n\nfunction checkPerTargetFeaturesValue({\n target,\n value,\n file,\n}: {\n target: string;\n value: unknown;\n file: string;\n}): DoctorDiagnostic[] {\n const diagnostics: DoctorDiagnostic[] = [];\n if (Array.isArray(value)) {\n for (const entry of value) {\n if (typeof entry !== \"string\") {\n diagnostics.push({\n severity: \"error\",\n code: \"config/invalid-value\",\n file,\n message: `Features for target '${target}' must be strings, found ${JSON.stringify(entry)}.`,\n });\n continue;\n }\n if (entry === \"*\") continue;\n const diagnostic = checkFeatureName({\n name: entry,\n file,\n context: `'targets.${target}'`,\n });\n if (diagnostic) diagnostics.push(diagnostic);\n }\n return diagnostics;\n }\n if (isPlainObject(value)) {\n for (const key of Object.keys(value)) {\n if (key === \"*\" || (PER_FEATURE_EXTRA_KEYS as readonly string[]).includes(key)) continue;\n const diagnostic = checkFeatureName({\n name: key,\n file,\n context: `'targets.${target}'`,\n });\n if (diagnostic) diagnostics.push(diagnostic);\n }\n return diagnostics;\n }\n diagnostics.push({\n severity: \"error\",\n code: \"config/invalid-value\",\n file,\n message: `Value for target '${target}' must be a feature array or a per-feature object, found ${JSON.stringify(value)}.`,\n });\n return diagnostics;\n}\n\nfunction checkFeaturesValue({\n features,\n file,\n}: {\n features: unknown;\n file: string;\n}): DoctorDiagnostic[] {\n const diagnostics: DoctorDiagnostic[] = [];\n if (features === undefined) return diagnostics;\n if (!Array.isArray(features)) {\n diagnostics.push({\n severity: \"error\",\n code: \"config/invalid-value\",\n file,\n message: `'features' must be an array of feature names, found ${JSON.stringify(features)}.`,\n hint: \"To configure features per target, use the object form of 'targets' instead.\",\n });\n return diagnostics;\n }\n for (const entry of features) {\n if (typeof entry !== \"string\") {\n diagnostics.push({\n severity: \"error\",\n code: \"config/invalid-value\",\n file,\n message: `'features' entries must be strings, found ${JSON.stringify(entry)}.`,\n });\n continue;\n }\n if (entry === \"*\") continue;\n const diagnostic = checkFeatureName({ name: entry, file, context: \"'features'\" });\n if (diagnostic) diagnostics.push(diagnostic);\n }\n return diagnostics;\n}\n\nfunction checkConflictingTargets({\n targets,\n file,\n}: {\n targets: unknown;\n file: string;\n}): DoctorDiagnostic[] {\n const has = (target: string): boolean => {\n if (Array.isArray(targets)) return targets.includes(target);\n if (isPlainObject(targets)) return Object.prototype.hasOwnProperty.call(targets, target);\n return false;\n };\n const diagnostics: DoctorDiagnostic[] = [];\n for (const [target1, target2] of CONFLICTING_TARGET_PAIRS) {\n if (has(target1) && has(target2)) {\n diagnostics.push({\n severity: \"error\",\n code: \"config/conflicting-targets\",\n file,\n message: `Targets '${target1}' and '${target2}' cannot be used together.`,\n hint: \"Remove one of the two from 'targets'.\",\n });\n }\n }\n return diagnostics;\n}\n\nfunction checkSchemaProperty({\n config,\n file,\n}: {\n config: Record<string, unknown>;\n file: string;\n}): DoctorDiagnostic[] {\n const schema = config.$schema;\n if (schema === undefined) {\n return [\n {\n severity: \"info\",\n code: \"config/missing-schema\",\n file,\n message: \"No '$schema' property; editors cannot offer completion and validation.\",\n hint: `Add \"$schema\": \"${RULESYNC_CONFIG_SCHEMA_URL}\".`,\n },\n ];\n }\n if (typeof schema === \"string\" && schema !== RULESYNC_CONFIG_SCHEMA_URL) {\n return [\n {\n severity: \"warning\",\n code: \"config/outdated-schema\",\n file,\n message: `'$schema' does not point at the current rulesync config schema.`,\n hint: `Update it to \"${RULESYNC_CONFIG_SCHEMA_URL}\".`,\n },\n ];\n }\n return [];\n}\n\nfunction checkUnknownTopLevelKeys({\n config,\n file,\n}: {\n config: Record<string, unknown>;\n file: string;\n}): DoctorDiagnostic[] {\n const diagnostics: DoctorDiagnostic[] = [];\n for (const key of Object.keys(config)) {\n if (KNOWN_CONFIG_KEYS.includes(key)) continue;\n diagnostics.push({\n severity: \"error\",\n code: \"config/unknown-key\",\n file,\n message: `Unknown key '${key}'. It is silently ignored by 'rulesync generate'.`,\n hint:\n didYouMean({ input: key, candidates: KNOWN_CONFIG_KEYS }) ??\n `Known keys: ${KNOWN_CONFIG_KEYS.join(\", \")}.`,\n });\n }\n return diagnostics;\n}\n\nfunction checkTargetsFeaturesExclusivity({\n config,\n file,\n}: {\n config: Record<string, unknown>;\n file: string;\n}): DoctorDiagnostic[] {\n if (!isPlainObject(config.targets) || config.features === undefined) return [];\n return [\n {\n severity: \"error\",\n code: \"config/targets-features-conflict\",\n file,\n message: \"When 'targets' is in object form, 'features' must be omitted.\",\n hint: \"Declare per-target features inside the 'targets' object instead.\",\n },\n ];\n}\n\nfunction checkTokenEnvVars({\n config,\n file,\n env,\n}: {\n config: Record<string, unknown>;\n file: string;\n env: Record<string, string | undefined>;\n}): DoctorDiagnostic[] {\n if (!Array.isArray(config.sources)) return [];\n const diagnostics: DoctorDiagnostic[] = [];\n for (const source of config.sources) {\n if (!isPlainObject(source)) continue;\n const tokenEnv = source.tokenEnv;\n if (typeof tokenEnv !== \"string\" || tokenEnv.length === 0) continue;\n if (env[tokenEnv] === undefined || env[tokenEnv] === \"\") {\n diagnostics.push({\n severity: \"warning\",\n code: \"config/token-env-not-set\",\n file,\n message: `Source '${String(source.source ?? \"<unnamed>\")}' references environment variable '${tokenEnv}', which is not set.`,\n hint: `Export ${tokenEnv} before running commands that fetch from this source.`,\n });\n }\n }\n return diagnostics;\n}\n\n/**\n * Structural validation via the same Zod schema `ConfigResolver` uses.\n * `targets` / `features` issues are skipped because the dedicated checks above\n * already reported them with better messages and suggestions.\n */\nfunction checkAgainstConfigFileSchema({\n config,\n file,\n}: {\n config: Record<string, unknown>;\n file: string;\n}): DoctorDiagnostic[] {\n const result = ConfigFileSchema.safeParse(config);\n if (result.success) return [];\n const diagnostics: DoctorDiagnostic[] = [];\n for (const issue of result.error.issues) {\n const topLevelKey = issue.path[0];\n if (topLevelKey === \"targets\" || topLevelKey === \"features\") continue;\n const path = issue.path.length > 0 ? issue.path.join(\".\") : \"(root)\";\n diagnostics.push({\n severity: \"error\",\n code: \"config/invalid-value\",\n file,\n message: `Invalid value at '${path}': ${issue.message}`,\n });\n }\n return diagnostics;\n}\n\n/**\n * Runs every per-file check against one configuration file's raw content.\n * Pure with respect to the filesystem so each check is unit-testable; only the\n * `tokenEnv` check consults the provided environment map.\n */\nexport function collectConfigFileDiagnostics({\n file,\n content,\n env = process.env,\n}: {\n file: string;\n content: string;\n env?: Record<string, string | undefined>;\n}): DoctorDiagnostic[] {\n if (content.trim() === \"\") {\n return [\n {\n severity: \"warning\",\n code: \"config/empty-file\",\n file,\n message: \"Configuration file is empty; rulesync will run with built-in defaults.\",\n },\n ];\n }\n const parseErrors: ParseError[] = [];\n const parsed: unknown = parseJsonc(content, parseErrors, {\n allowTrailingComma: true,\n });\n if (parseErrors.length > 0) {\n return parseErrors.map((parseError) => {\n const { line, column } = offsetToPosition({ content, offset: parseError.offset });\n return {\n severity: \"error\" as const,\n code: \"config/parse-error\",\n file,\n message: `JSONC parse error: ${printParseErrorCode(parseError.error)}.`,\n line,\n column,\n };\n });\n }\n if (!isPlainObject(parsed)) {\n return [\n {\n severity: \"error\",\n code: \"config/not-an-object\",\n file,\n message: `Configuration file must contain a JSON object, found ${JSON.stringify(parsed)}.`,\n },\n ];\n }\n\n return [\n ...checkUnknownTopLevelKeys({ config: parsed, file }),\n ...checkSchemaProperty({ config: parsed, file }),\n ...checkTargetsValue({ targets: parsed.targets, file }),\n ...checkFeaturesValue({ features: parsed.features, file }),\n ...checkTargetsFeaturesExclusivity({ config: parsed, file }),\n ...checkConflictingTargets({ targets: parsed.targets, file }),\n ...checkTokenEnvVars({ config: parsed, file, env }),\n ...checkAgainstConfigFileSchema({ config: parsed, file }),\n ];\n}\n\n/**\n * A base file and a local file can each be valid in isolation yet merge into\n * the invalid `{ targets: object, features: array }` state — the same\n * cross-file rule `ConfigResolver` enforces at generate time.\n */\nexport function collectMergedConfigDiagnostics({\n baseConfig,\n localConfig,\n baseFile,\n localFile,\n}: {\n baseConfig: Record<string, unknown> | undefined;\n localConfig: Record<string, unknown> | undefined;\n baseFile: string;\n localFile: string;\n}): DoctorDiagnostic[] {\n if (baseConfig === undefined || localConfig === undefined) return [];\n // The merged object-form-`targets` + `features` state is the same invalid\n // combination `assertTargetsFeaturesExclusive` rejects at generate time,\n // detected here on the post-merge (local ?? base) values.\n const mergedTargets = localConfig.targets ?? baseConfig.targets;\n const mergedFeatures = localConfig.features ?? baseConfig.features;\n if (!isPlainObject(mergedTargets) || mergedFeatures === undefined) return [];\n // Skip when a single file already carries the conflict — the per-file check\n // reported it there.\n const conflictWithinOneFile =\n (isPlainObject(baseConfig.targets) && baseConfig.features !== undefined) ||\n (isPlainObject(localConfig.targets) && localConfig.features !== undefined);\n if (conflictWithinOneFile) return [];\n return [\n {\n severity: \"error\",\n code: \"config/targets-features-conflict\",\n file: localFile,\n message:\n `Merging '${baseFile}' with '${localFile}' combines object-form 'targets' ` +\n \"with 'features', which is invalid.\",\n hint: \"Remove the conflicting field from one of the two files.\",\n },\n ];\n}\n\nfunction severityRank(severity: DoctorSeverity): number {\n return severity === \"error\" ? 0 : severity === \"warning\" ? 1 : 2;\n}\n\n/**\n * Strips control characters (including ANSI escape sequences) so key names and\n * values copied out of an untrusted config file cannot inject terminal escape\n * codes into the diagnostic output.\n */\nfunction stripControlCharacters(text: string): string {\n // oxlint-disable-next-line no-control-regex\n return text.replace(/[\\u0000-\\u0008\\u000B-\\u001F\\u007F]/g, \"\");\n}\n\nfunction formatDiagnostic(diagnostic: DoctorDiagnostic): string {\n const position =\n diagnostic.line !== undefined\n ? `:${diagnostic.line}${diagnostic.column !== undefined ? `:${diagnostic.column}` : \"\"}`\n : \"\";\n const label =\n diagnostic.severity === \"error\" ? \"✖\" : diagnostic.severity === \"warning\" ? \"⚠\" : \"ℹ\";\n const hint =\n diagnostic.hint === undefined ? \"\" : `\\n ↳ ${stripControlCharacters(diagnostic.hint)}`;\n return `${label} ${stripControlCharacters(diagnostic.file)}${position} [${diagnostic.code}] ${stripControlCharacters(diagnostic.message)}${hint}`;\n}\n\n/**\n * Re-parses an already-read config file's content for the cross-file checks.\n * Returns undefined when the content is unparseable or not an object — the\n * per-file checks have already reported those states.\n */\nfunction parseConfigObjectForMerge(\n content: string | undefined,\n): Record<string, unknown> | undefined {\n if (content === undefined) return undefined;\n const errors: ParseError[] = [];\n const parsed: unknown = parseJsonc(content, errors, { allowTrailingComma: true });\n if (errors.length > 0 || !isPlainObject(parsed)) return undefined;\n return parsed;\n}\n\n/**\n * Path sanity: an `inputRoot` that does not exist means every command will\n * fail (or silently read nothing); local config wins, mirroring the merge\n * order in `ConfigResolver`.\n */\nasync function checkInputRootExists({\n baseConfig,\n localConfig,\n baseFile,\n localFile,\n}: {\n baseConfig: Record<string, unknown> | undefined;\n localConfig: Record<string, unknown> | undefined;\n baseFile: string;\n localFile: string;\n}): Promise<DoctorDiagnostic[]> {\n const mergedInputRoot = localConfig?.inputRoot ?? baseConfig?.inputRoot;\n if (typeof mergedInputRoot !== \"string\" || mergedInputRoot.length === 0) return [];\n if (await directoryExists(resolve(mergedInputRoot))) return [];\n return [\n {\n severity: \"error\",\n code: \"config/input-root-not-found\",\n file: localConfig?.inputRoot !== undefined ? localFile : baseFile,\n message: `'inputRoot' points at '${mergedInputRoot}', which is not an existing directory.`,\n hint: \"Create the directory or fix the 'inputRoot' path.\",\n },\n ];\n}\n\nfunction reportDiagnostics({\n logger,\n diagnostics,\n}: {\n logger: Logger;\n diagnostics: DoctorDiagnostic[];\n}): void {\n // In JSON mode the diagnostics travel via `captureData`; going through\n // `logger.error` here would emit the error document early (JsonLogger\n // prints on the first error call) with a generic code.\n if (logger.jsonMode) return;\n for (const diagnostic of diagnostics) {\n const formatted = formatDiagnostic(diagnostic);\n if (diagnostic.severity === \"error\") {\n logger.error(formatted);\n } else if (diagnostic.severity === \"warning\") {\n logger.warn(formatted);\n } else {\n logger.info(formatted);\n }\n }\n}\n\n/**\n * `rulesync doctor` — read-only diagnostics for the configuration files.\n * Never writes; exits non-zero when errors (or, with --strict, warnings) are\n * found.\n */\nexport async function doctorCommand(logger: Logger, options: DoctorOptions): Promise<void> {\n const cwd = process.cwd();\n const configPath = options.config ?? RULESYNC_CONFIG_RELATIVE_FILE_PATH;\n const validatedConfigPath = resolvePath(configPath, cwd);\n const localConfigPath = join(\n dirname(validatedConfigPath),\n RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH,\n );\n\n const toDisplayPath = (absolutePath: string): string => {\n const relativePath = relative(cwd, absolutePath);\n return relativePath === \"\" || relativePath.startsWith(\"..\") ? absolutePath : relativePath;\n };\n\n const diagnostics: DoctorDiagnostic[] = [];\n\n const baseExists = await fileExists(validatedConfigPath);\n if (!baseExists) {\n diagnostics.push({\n severity: \"info\",\n code: \"config/no-config-file\",\n file: toDisplayPath(validatedConfigPath),\n message: \"No configuration file found; rulesync will run with built-in defaults.\",\n hint: \"Run 'rulesync init' to scaffold one.\",\n });\n }\n\n // Read each file once; the same content feeds the per-file checks and the\n // cross-file merge checks below.\n const fileContents = new Map<string, string>();\n for (const filePath of [validatedConfigPath, localConfigPath]) {\n if (!(await fileExists(filePath))) continue;\n const content = await readFileContent(filePath);\n fileContents.set(filePath, content);\n diagnostics.push(...collectConfigFileDiagnostics({ file: toDisplayPath(filePath), content }));\n }\n\n const baseConfig = parseConfigObjectForMerge(fileContents.get(validatedConfigPath));\n const localConfig = parseConfigObjectForMerge(fileContents.get(localConfigPath));\n diagnostics.push(\n ...collectMergedConfigDiagnostics({\n baseConfig,\n localConfig,\n baseFile: toDisplayPath(validatedConfigPath),\n localFile: toDisplayPath(localConfigPath),\n }),\n );\n\n diagnostics.push(\n ...(await checkInputRootExists({\n baseConfig,\n localConfig,\n baseFile: toDisplayPath(validatedConfigPath),\n localFile: toDisplayPath(localConfigPath),\n })),\n );\n\n diagnostics.sort((a, b) => severityRank(a.severity) - severityRank(b.severity));\n\n const errorCount = diagnostics.filter((d) => d.severity === \"error\").length;\n const warningCount = diagnostics.filter((d) => d.severity === \"warning\").length;\n const infoCount = diagnostics.filter((d) => d.severity === \"info\").length;\n\n reportDiagnostics({ logger, diagnostics });\n\n if (logger.jsonMode) {\n logger.captureData(\"diagnostics\", diagnostics);\n logger.captureData(\"summary\", {\n errors: errorCount,\n warnings: warningCount,\n infos: infoCount,\n });\n }\n\n const summary = `${errorCount} error(s), ${warningCount} warning(s), ${infoCount} info(s)`;\n if (errorCount > 0 || (options.strict === true && warningCount > 0)) {\n // Attach the diagnostics as structured details so `--json` consumers still\n // receive them on failure — the JSON error document drops captured data.\n throw new CLIError(`Doctor found problems: ${summary}.`, ErrorCodes.DOCTOR_FAILED, 1, {\n diagnostics,\n summary: { errors: errorCount, warnings: warningCount, infos: infoCount },\n });\n }\n if (warningCount > 0) {\n logger.warn(`Doctor finished with ${summary}.`);\n return;\n }\n logger.success(`✓ No problems found (${summary}).`);\n}\n","import { join, posix } from \"node:path\";\n\nimport { Semaphore } from \"es-toolkit/promise\";\n\nimport {\n FETCH_CONCURRENCY_LIMIT,\n MAX_FILE_SIZE,\n RULESYNC_AIIGNORE_FILE_NAME,\n RULESYNC_HOOKS_FILE_NAME,\n RULESYNC_HOOKS_LEGACY_FILE_NAME,\n RULESYNC_MCP_FILE_NAME,\n RULESYNC_MCP_LEGACY_FILE_NAME,\n RULESYNC_PERMISSIONS_FILE_NAME,\n RULESYNC_PERMISSIONS_LEGACY_FILE_NAME,\n RULESYNC_RELATIVE_DIR_PATH,\n} from \"../constants/rulesync-paths.js\";\nimport { ChecksProcessor } from \"../features/checks/checks-processor.js\";\nimport { CommandsProcessor } from \"../features/commands/commands-processor.js\";\nimport { HooksProcessor } from \"../features/hooks/hooks-processor.js\";\nimport { IgnoreProcessor } from \"../features/ignore/ignore-processor.js\";\nimport { McpProcessor } from \"../features/mcp/mcp-processor.js\";\nimport { RulesProcessor } from \"../features/rules/rules-processor.js\";\nimport { SkillsProcessor } from \"../features/skills/skills-processor.js\";\nimport { SubagentsProcessor } from \"../features/subagents/subagents-processor.js\";\nimport type { Feature } from \"../types/features.js\";\nimport { ALL_FEATURES } from \"../types/features.js\";\nimport type { FetchTarget } from \"../types/fetch-targets.js\";\nimport type {\n ConflictStrategy,\n FetchFileResult,\n FetchOptions,\n FetchSummary,\n GitHubFileEntry,\n ParsedSource,\n} from \"../types/fetch.js\";\nimport type { ToolTarget } from \"../types/tool-targets.js\";\nimport {\n checkPathTraversal,\n createTempDirectory,\n fileExists,\n removeTempDirectory,\n toPosixPath,\n writeFileContent,\n} from \"../utils/file.js\";\nimport type { Logger } from \"../utils/logger.js\";\nimport { GitHubClient, GitHubClientError } from \"./github-client.js\";\nimport { listDirectoryRecursive, withSemaphore } from \"./github-utils.js\";\nimport { parseSource } from \"./source-parser.js\";\n\n/**\n * Feature to path mapping for filtering (rulesync format)\n */\nconst FEATURE_PATHS: Record<Feature, string[]> = {\n rules: [\"rules\"],\n commands: [\"commands\"],\n subagents: [\"subagents\"],\n skills: [\"skills\"],\n checks: [\"checks\"],\n ignore: [RULESYNC_AIIGNORE_FILE_NAME],\n mcp: [RULESYNC_MCP_FILE_NAME, RULESYNC_MCP_LEGACY_FILE_NAME],\n hooks: [RULESYNC_HOOKS_FILE_NAME, RULESYNC_HOOKS_LEGACY_FILE_NAME],\n permissions: [RULESYNC_PERMISSIONS_FILE_NAME, RULESYNC_PERMISSIONS_LEGACY_FILE_NAME],\n};\n\n/**\n * Check if target is a tool target (not rulesync)\n */\nfunction isToolTarget(target: FetchTarget): target is ToolTarget {\n return target !== \"rulesync\";\n}\n\n/**\n * Validate file size against maximum limit\n * @throws {GitHubClientError} If file size exceeds limit\n */\nfunction validateFileSize(relativePath: string, size: number): void {\n if (size > MAX_FILE_SIZE) {\n throw new GitHubClientError(\n `File \"${relativePath}\" exceeds maximum size limit (${(size / 1024 / 1024).toFixed(2)}MB > ${MAX_FILE_SIZE / 1024 / 1024}MB)`,\n );\n }\n}\n\n/**\n * Result of feature conversion\n */\ntype FeatureConversionResult = {\n converted: number;\n convertedPaths: string[];\n};\n\n/**\n * Processor type for feature conversion\n */\ntype FeatureProcessor = {\n loadToolFiles(): Promise<unknown[]>;\n convertToolFilesToRulesyncFiles(\n toolFiles: unknown[],\n ): Promise<\n Array<{ getRelativeDirPath(): string; getRelativeFilePath(): string; getFileContent(): string }>\n >;\n};\n\n/**\n * Process feature conversion for a single feature type\n * @param processor - The processor to use for loading and converting files\n * @param outputDir - Output directory for converted files\n * @returns The paths of converted files\n */\nasync function processFeatureConversion(params: {\n processor: FeatureProcessor;\n outputDir: string;\n}): Promise<{ paths: string[] }> {\n const { processor, outputDir } = params;\n const paths: string[] = [];\n\n const toolFiles = await processor.loadToolFiles();\n if (toolFiles.length === 0) {\n return { paths: [] };\n }\n\n const rulesyncFiles = await processor.convertToolFilesToRulesyncFiles(toolFiles);\n for (const file of rulesyncFiles) {\n const relativePath = join(file.getRelativeDirPath(), file.getRelativeFilePath());\n const outputPath = join(outputDir, relativePath);\n await writeFileContent(outputPath, file.getFileContent());\n paths.push(relativePath);\n }\n\n return { paths };\n}\n\n/**\n * Convert fetched tool-specific files to rulesync format\n * @param tempDir - Temporary directory containing tool-specific files\n * @param outputDir - Output directory for rulesync files\n * @param target - Tool target to convert from\n * @param features - Features to convert\n * @returns Number of converted files and their paths\n */\nasync function convertFetchedFilesToRulesync(params: {\n tempDir: string;\n outputDir: string;\n target: ToolTarget;\n features: Feature[];\n logger: Logger;\n}): Promise<FeatureConversionResult> {\n const { tempDir, outputDir, target, features, logger } = params;\n const convertedPaths: string[] = [];\n\n // Feature conversion configurations\n // Each config defines how to get supported targets and create a processor\n const featureConfigs: Array<{\n feature: Feature;\n getTargets: () => ToolTarget[];\n createProcessor: () => FeatureProcessor;\n }> = [\n {\n feature: \"rules\",\n getTargets: () => RulesProcessor.getToolTargets({ global: false }),\n createProcessor: () =>\n new RulesProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger }),\n },\n {\n feature: \"commands\",\n getTargets: () =>\n CommandsProcessor.getToolTargets({ global: false, includeSimulated: false }),\n createProcessor: () =>\n new CommandsProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger }),\n },\n {\n feature: \"subagents\",\n getTargets: () =>\n SubagentsProcessor.getToolTargets({ global: false, includeSimulated: false }),\n createProcessor: () =>\n new SubagentsProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger }),\n },\n {\n feature: \"checks\",\n getTargets: () => ChecksProcessor.getToolTargets({ global: false }),\n createProcessor: () =>\n new ChecksProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger }),\n },\n {\n feature: \"ignore\",\n getTargets: () => IgnoreProcessor.getToolTargets(),\n createProcessor: () =>\n new IgnoreProcessor({ outputRoot: tempDir, toolTarget: target, logger }),\n },\n {\n feature: \"mcp\",\n getTargets: () => McpProcessor.getToolTargets({ global: false }),\n createProcessor: () =>\n new McpProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger }),\n },\n {\n feature: \"hooks\",\n getTargets: () => HooksProcessor.getToolTargets({ global: false }),\n createProcessor: () =>\n new HooksProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger }),\n },\n ];\n\n // Process each feature using data-driven approach\n for (const config of featureConfigs) {\n if (!features.includes(config.feature)) {\n continue;\n }\n const supportedTargets = config.getTargets();\n if (!supportedTargets.includes(target)) {\n continue;\n }\n const processor = config.createProcessor();\n const result = await processFeatureConversion({ processor, outputDir });\n convertedPaths.push(...result.paths);\n }\n\n // Skills conversion is not yet supported in fetch command\n // Note: Skills are more complex as they are directory-based.\n // Users can use the import command for skills conversion.\n if (features.includes(\"skills\")) {\n logger.debug(\n \"Skills conversion is not yet supported in fetch command. Use import command instead.\",\n );\n }\n\n return { converted: convertedPaths.length, convertedPaths };\n}\n\n/**\n * Resolve features from options, defaulting to skills and handling wildcard.\n */\nfunction resolveFeatures(features?: string[]): Feature[] {\n if (features === undefined) {\n return [\"skills\"];\n }\n if (features.includes(\"*\")) {\n return [...ALL_FEATURES];\n }\n return features.filter((f): f is Feature => ALL_FEATURES.includes(f as Feature));\n}\n\n/**\n * Type guard for error objects with statusCode\n */\nfunction hasStatusCode(error: unknown): error is { statusCode: number } {\n if (typeof error !== \"object\" || error === null || !(\"statusCode\" in error)) {\n return false;\n }\n const maybeStatus = Object.getOwnPropertyDescriptor(error, \"statusCode\")?.value;\n return typeof maybeStatus === \"number\";\n}\n\n/**\n * Check if error is a 404 \"not found\" error\n */\nfunction isNotFoundError(error: unknown): boolean {\n if (error instanceof GitHubClientError && error.statusCode === 404) {\n return true;\n }\n // Also handle plain objects with statusCode property (for test mocks)\n if (hasStatusCode(error) && error.statusCode === 404) {\n return true;\n }\n return false;\n}\n\n/**\n * Parameters for fetch operation\n */\nexport type FetchParams = {\n source: string;\n options?: FetchOptions;\n outputRoot?: string;\n logger: Logger;\n};\n\n/**\n * Fetch files from a Git repository\n * Searches for feature directories (rules/, commands/, skills/, etc.) directly at the specified path\n *\n * When target is \"rulesync\" (default), files are fetched as-is.\n * When target is a tool target (e.g., \"claudecode\"), files are fetched to a temp directory,\n * converted to rulesync format, and written to the output directory.\n */\nexport async function fetchFiles(params: FetchParams): Promise<FetchSummary> {\n const { source, options = {}, outputRoot = process.cwd(), logger } = params;\n\n // Parse source\n const parsed = parseSource(source);\n\n // Check if provider is supported\n if (parsed.provider === \"gitlab\") {\n throw new Error(\n \"GitLab is not yet supported. Currently only GitHub repositories are supported.\",\n );\n }\n\n // Resolve options\n const resolvedRef = options.ref ?? parsed.ref;\n // Normalize backslashes to forward slashes for GitHub API compatibility.\n const resolvedPath = toPosixPath(options.path ?? parsed.path ?? \".\");\n const outputDir = options.output ?? RULESYNC_RELATIVE_DIR_PATH;\n const conflictStrategy: ConflictStrategy = options.conflict ?? \"overwrite\";\n const enabledFeatures = resolveFeatures(options.features);\n const target: FetchTarget = options.target ?? \"rulesync\";\n\n // Validate output directory to prevent path traversal attacks\n checkPathTraversal({\n relativePath: outputDir,\n intendedRootDir: outputRoot,\n });\n\n // Initialize GitHub client\n const token = GitHubClient.resolveToken(options.token);\n const client = new GitHubClient({ token });\n\n // Validate repository\n logger.debug(`Validating repository: ${parsed.owner}/${parsed.repo}`);\n const isValid = await client.validateRepository(parsed.owner, parsed.repo);\n if (!isValid) {\n throw new GitHubClientError(\n `Repository not found: ${parsed.owner}/${parsed.repo}. Check the repository name and your access permissions.`,\n 404,\n );\n }\n\n // Resolve ref to use\n const ref = resolvedRef ?? (await client.getDefaultBranch(parsed.owner, parsed.repo));\n logger.debug(`Using ref: ${ref}`);\n\n // If target is a tool format, use conversion flow\n if (isToolTarget(target)) {\n return fetchAndConvertToolFiles({\n client,\n parsed,\n ref,\n resolvedPath,\n enabledFeatures,\n target,\n outputDir,\n outputRoot,\n conflictStrategy,\n logger,\n });\n }\n\n // Create semaphore for concurrency control\n const semaphore = new Semaphore(FETCH_CONCURRENCY_LIMIT);\n\n // Collect all files to fetch from feature directories directly\n const filesToFetch = await collectFeatureFiles({\n client,\n owner: parsed.owner,\n repo: parsed.repo,\n basePath: resolvedPath,\n ref,\n enabledFeatures,\n semaphore,\n logger,\n });\n\n if (filesToFetch.length === 0) {\n logger.warn(`No files found matching enabled features: ${enabledFeatures.join(\", \")}`);\n return {\n source: `${parsed.owner}/${parsed.repo}`,\n ref,\n files: [],\n created: 0,\n overwritten: 0,\n skipped: 0,\n };\n }\n\n // Process files in parallel with concurrency control\n const outputBasePath = join(outputRoot, outputDir);\n\n // Validate paths and check file sizes first (synchronous checks)\n for (const { relativePath, size } of filesToFetch) {\n checkPathTraversal({\n relativePath,\n intendedRootDir: outputBasePath,\n });\n\n validateFileSize(relativePath, size);\n }\n\n // Process files in parallel with concurrency control\n // Note: Promise.all fails fast - if any promise rejects, others continue running but\n // may have already written files. This behavior is consistent with sequential execution,\n // but the window for partial writes is larger with parallel execution.\n const results = await Promise.all(\n filesToFetch.map(async ({ remotePath, relativePath }) => {\n const localPath = join(outputBasePath, relativePath);\n const exists = await fileExists(localPath);\n\n if (exists && conflictStrategy === \"skip\") {\n logger.debug(`Skipping existing file: ${relativePath}`);\n return { relativePath, status: \"skipped\" as const };\n }\n\n const content = await withSemaphore(semaphore, () =>\n client.getFileContent(parsed.owner, parsed.repo, remotePath, ref),\n );\n await writeFileContent(localPath, content);\n\n const status = exists ? (\"overwritten\" as const) : (\"created\" as const);\n logger.debug(`Wrote: ${relativePath} (${status})`);\n return { relativePath, status };\n }),\n );\n\n // Calculate summary\n const summary: FetchSummary = {\n source: `${parsed.owner}/${parsed.repo}`,\n ref,\n files: results,\n created: results.filter((r) => r.status === \"created\").length,\n overwritten: results.filter((r) => r.status === \"overwritten\").length,\n skipped: results.filter((r) => r.status === \"skipped\").length,\n };\n\n return summary;\n}\n\n/**\n * Collect files from feature directories\n */\nasync function collectFeatureFiles(params: {\n client: GitHubClient;\n owner: string;\n repo: string;\n basePath: string;\n ref: string;\n enabledFeatures: Feature[];\n semaphore: Semaphore;\n logger: Logger;\n}): Promise<Array<{ remotePath: string; relativePath: string; size: number }>> {\n const { client, owner, repo, basePath, ref, enabledFeatures, semaphore, logger } = params;\n\n // Cache directory listing results to avoid duplicate API calls\n // File-based features (ignore, mcp, hooks) all list the same basePath directory\n const dirCache = new Map<string, Promise<GitHubFileEntry[]>>();\n\n async function getCachedDirectory(path: string): Promise<GitHubFileEntry[]> {\n let promise = dirCache.get(path);\n if (promise === undefined) {\n promise = withSemaphore(semaphore, () => client.listDirectory(owner, repo, path, ref));\n dirCache.set(path, promise);\n }\n return promise;\n }\n\n const tasks = enabledFeatures.flatMap((feature) =>\n FEATURE_PATHS[feature].map((featurePath) => ({ feature, featurePath })),\n );\n\n const results = await Promise.all(\n tasks.map(async ({ featurePath }) => {\n const fullPath =\n basePath === \".\" || basePath === \"\" ? featurePath : posix.join(basePath, featurePath);\n const collected: Array<{ remotePath: string; relativePath: string; size: number }> = [];\n\n try {\n // Check if it's a file (mcp.json, .aiignore, hooks.json)\n if (featurePath.includes(\".\")) {\n // Try to get the file directly\n try {\n const entries = await getCachedDirectory(\n basePath === \".\" || basePath === \"\" ? \".\" : basePath,\n );\n const fileEntry = entries.find((e) => e.name === featurePath && e.type === \"file\");\n if (fileEntry) {\n collected.push({\n remotePath: fileEntry.path,\n relativePath: featurePath,\n size: fileEntry.size,\n });\n }\n } catch (error) {\n // Only skip 404 errors (file not found), re-throw other errors\n if (isNotFoundError(error)) {\n logger.debug(`File not found: ${fullPath}`);\n } else {\n throw error;\n }\n }\n } else {\n // It's a directory (rules/, commands/, skills/, subagents/)\n const dirFiles = await listDirectoryRecursive({\n client,\n owner,\n repo,\n path: fullPath,\n ref,\n semaphore,\n });\n\n for (const file of dirFiles) {\n // Calculate relative path from base\n const relativePath =\n basePath === \".\" || basePath === \"\"\n ? file.path\n : file.path.substring(basePath.length + 1);\n\n collected.push({\n remotePath: file.path,\n relativePath,\n size: file.size,\n });\n }\n }\n } catch (error) {\n // Check for 404 errors (feature not found)\n if (isNotFoundError(error)) {\n // Feature directory/file not found, skip silently\n logger.debug(`Feature not found: ${fullPath}`);\n return collected;\n }\n throw error;\n }\n\n return collected;\n }),\n );\n\n return results.flat();\n}\n\n/**\n * Fetch tool-specific files and convert them to rulesync format\n */\nasync function fetchAndConvertToolFiles(params: {\n client: GitHubClient;\n parsed: ParsedSource;\n ref: string;\n resolvedPath: string;\n enabledFeatures: Feature[];\n target: ToolTarget;\n outputDir: string;\n outputRoot: string;\n conflictStrategy: ConflictStrategy;\n logger: Logger;\n}): Promise<FetchSummary> {\n const {\n client,\n parsed,\n ref,\n resolvedPath,\n enabledFeatures,\n target,\n outputDir,\n outputRoot,\n conflictStrategy: _conflictStrategy,\n logger,\n } = params;\n\n // Create a unique temporary directory\n const tempDir = await createTempDirectory();\n logger.debug(`Created temp directory: ${tempDir}`);\n\n // Create semaphore for concurrency control\n const semaphore = new Semaphore(FETCH_CONCURRENCY_LIMIT);\n\n try {\n // Collect files using rulesync feature paths (rules/, commands/, etc.)\n // External repos use these paths directly without tool-specific prefixes\n const filesToFetch = await collectFeatureFiles({\n client,\n owner: parsed.owner,\n repo: parsed.repo,\n basePath: resolvedPath,\n ref,\n enabledFeatures,\n semaphore,\n logger,\n });\n\n if (filesToFetch.length === 0) {\n logger.warn(`No files found matching enabled features: ${enabledFeatures.join(\", \")}`);\n return {\n source: `${parsed.owner}/${parsed.repo}`,\n ref,\n files: [],\n created: 0,\n overwritten: 0,\n skipped: 0,\n };\n }\n\n // Validate file sizes first\n for (const { relativePath, size } of filesToFetch) {\n validateFileSize(relativePath, size);\n }\n\n // Fetch files to temp directory with tool-specific structure in parallel\n // Map rulesync paths to tool-specific paths\n const toolPaths = getToolPathMapping(target);\n\n await Promise.all(\n filesToFetch.map(async ({ remotePath, relativePath }) => {\n // Map the relative path to tool-specific structure\n const toolRelativePath = mapToToolPath(relativePath, toolPaths);\n checkPathTraversal({\n relativePath: toolRelativePath,\n intendedRootDir: tempDir,\n });\n const localPath = join(tempDir, toolRelativePath);\n\n // Fetch file content with concurrency control, then write locally\n const content = await withSemaphore(semaphore, () =>\n client.getFileContent(parsed.owner, parsed.repo, remotePath, ref),\n );\n await writeFileContent(localPath, content);\n logger.debug(`Fetched to temp: ${toolRelativePath}`);\n }),\n );\n\n // Convert fetched files to rulesync format\n const outputBasePath = join(outputRoot, outputDir);\n const { converted, convertedPaths } = await convertFetchedFilesToRulesync({\n tempDir,\n outputDir: outputBasePath,\n target,\n features: enabledFeatures,\n logger,\n });\n\n // Build results based on conversion with actual file paths\n const results: FetchFileResult[] = convertedPaths.map((relativePath) => ({\n relativePath,\n status: \"created\" as const,\n }));\n\n logger.debug(`Converted ${converted} files from ${target} format to rulesync format`);\n\n return {\n source: `${parsed.owner}/${parsed.repo}`,\n ref,\n files: results,\n created: results.filter((r) => r.status === \"created\").length,\n overwritten: results.filter((r) => r.status === \"overwritten\").length,\n skipped: results.filter((r) => r.status === \"skipped\").length,\n };\n } finally {\n // Clean up temp directory\n await removeTempDirectory(tempDir);\n }\n}\n\n/**\n * Get tool-specific path mapping for a target\n * Returns a mapping from rulesync feature paths to tool-specific paths\n */\nfunction getToolPathMapping(target: ToolTarget): {\n rules?: { root?: string; nonRoot?: string };\n commands?: string;\n subagents?: string;\n skills?: string;\n checks?: string;\n} {\n // Get tool-specific paths from each processor class\n const mapping: {\n rules?: { root?: string; nonRoot?: string };\n commands?: string;\n subagents?: string;\n skills?: string;\n checks?: string;\n } = {};\n\n // Rules paths\n const supportedRulesTargets = RulesProcessor.getToolTargets({ global: false });\n if (supportedRulesTargets.includes(target)) {\n const factory = RulesProcessor.getFactory(target);\n if (factory) {\n const paths = factory.class.getSettablePaths({ global: false });\n mapping.rules = {\n root: paths.root?.relativeFilePath,\n nonRoot: paths.nonRoot?.relativeDirPath,\n };\n }\n }\n\n // Commands paths\n const supportedCommandsTargets = CommandsProcessor.getToolTargets({\n global: false,\n includeSimulated: false,\n });\n if (supportedCommandsTargets.includes(target)) {\n const factory = CommandsProcessor.getFactory(target);\n if (factory) {\n const paths = factory.class.getSettablePaths({ global: false });\n mapping.commands = paths.relativeDirPath;\n }\n }\n\n // Subagents paths\n const supportedSubagentsTargets = SubagentsProcessor.getToolTargets({\n global: false,\n includeSimulated: false,\n });\n if (supportedSubagentsTargets.includes(target)) {\n const factory = SubagentsProcessor.getFactory(target);\n if (factory) {\n const paths = factory.class.getSettablePaths({ global: false });\n mapping.subagents = paths.relativeDirPath;\n }\n }\n\n // Skills paths\n const supportedSkillsTargets = SkillsProcessor.getToolTargets({ global: false });\n if (supportedSkillsTargets.includes(target)) {\n const factory = SkillsProcessor.getFactory(target);\n if (factory) {\n const paths = factory.class.getSettablePaths({ global: false });\n mapping.skills = paths.relativeDirPath;\n }\n }\n\n // Checks paths\n const supportedChecksTargets = ChecksProcessor.getToolTargets({ global: false });\n if (supportedChecksTargets.includes(target)) {\n const factory = ChecksProcessor.getFactory(target);\n if (factory) {\n const paths = factory.class.getSettablePaths({ global: false });\n mapping.checks = paths.relativeDirPath;\n }\n }\n\n return mapping;\n}\n\n/**\n * Map a rulesync-style relative path to tool-specific path\n */\nfunction mapToToolPath(\n relativePath: string,\n toolPaths: ReturnType<typeof getToolPathMapping>,\n): string {\n // Check if this is a rules file\n if (relativePath.startsWith(\"rules/\")) {\n const restPath = relativePath.substring(\"rules/\".length);\n if (toolPaths.rules?.nonRoot) {\n return join(toolPaths.rules.nonRoot, restPath);\n }\n }\n\n // Check if this is a root rule file (e.g., CLAUDE.md, AGENTS.md)\n if (toolPaths.rules?.root && relativePath === toolPaths.rules.root) {\n return relativePath;\n }\n\n // Check if this is a commands file\n if (relativePath.startsWith(\"commands/\")) {\n const restPath = relativePath.substring(\"commands/\".length);\n if (toolPaths.commands) {\n return join(toolPaths.commands, restPath);\n }\n }\n\n // Check if this is a subagents file\n if (relativePath.startsWith(\"subagents/\")) {\n const restPath = relativePath.substring(\"subagents/\".length);\n if (toolPaths.subagents) {\n return join(toolPaths.subagents, restPath);\n }\n }\n\n // Check if this is a skills file\n if (relativePath.startsWith(\"skills/\")) {\n const restPath = relativePath.substring(\"skills/\".length);\n if (toolPaths.skills) {\n return join(toolPaths.skills, restPath);\n }\n }\n\n // Check if this is a checks file\n if (relativePath.startsWith(\"checks/\")) {\n const restPath = relativePath.substring(\"checks/\".length);\n if (toolPaths.checks) {\n return join(toolPaths.checks, restPath);\n }\n }\n\n // Default: return as-is\n return relativePath;\n}\n\n/**\n * Format fetch summary for display\n */\nexport function formatFetchSummary(summary: FetchSummary): string {\n const lines: string[] = [];\n\n lines.push(`Fetched from ${summary.source}@${summary.ref}:`);\n\n for (const file of summary.files) {\n const icon = file.status === \"skipped\" ? \"-\" : \"\\u2713\";\n const statusText =\n file.status === \"created\"\n ? \"(created)\"\n : file.status === \"overwritten\"\n ? \"(overwritten)\"\n : \"(skipped - already exists)\";\n lines.push(` ${icon} ${file.relativePath} ${statusText}`);\n }\n\n const parts: string[] = [];\n if (summary.created > 0) parts.push(`${summary.created} created`);\n if (summary.overwritten > 0) parts.push(`${summary.overwritten} overwritten`);\n if (summary.skipped > 0) parts.push(`${summary.skipped} skipped`);\n\n lines.push(\"\");\n const summaryText = parts.length > 0 ? parts.join(\", \") : \"no files\";\n lines.push(`Summary: ${summaryText}`);\n\n return lines.join(\"\\n\");\n}\n","import { fetchFiles, formatFetchSummary } from \"../../lib/fetch.js\";\nimport { GitHubClientError } from \"../../lib/github-client.js\";\nimport type { FetchOptions } from \"../../types/fetch.js\";\nimport { CLIError, ErrorCodes } from \"../../types/json-output.js\";\nimport type { Logger } from \"../../utils/logger.js\";\n\nexport type FetchCommandOptions = FetchOptions & {\n source: string;\n};\n\nexport async function fetchCommand(logger: Logger, options: FetchCommandOptions): Promise<void> {\n const { source, ...fetchOptions } = options;\n\n logger.debug(`Fetching files from ${source}...`);\n\n try {\n const summary = await fetchFiles({\n source,\n options: fetchOptions,\n logger,\n });\n\n // Capture JSON data if in JSON mode\n if (logger.jsonMode) {\n const createdFiles = summary.files\n .filter((f) => f.status === \"created\")\n .map((f) => f.relativePath);\n const overwrittenFiles = summary.files\n .filter((f) => f.status === \"overwritten\")\n .map((f) => f.relativePath);\n const skippedFiles = summary.files\n .filter((f) => f.status === \"skipped\")\n .map((f) => f.relativePath);\n\n logger.captureData(\"source\", source);\n logger.captureData(\"path\", fetchOptions.path);\n logger.captureData(\"created\", createdFiles);\n logger.captureData(\"overwritten\", overwrittenFiles);\n logger.captureData(\"skipped\", skippedFiles);\n logger.captureData(\"totalFetched\", summary.created + summary.overwritten + summary.skipped);\n }\n\n const output = formatFetchSummary(summary);\n\n logger.success(output);\n\n // Exit with appropriate code\n if (summary.created + summary.overwritten === 0 && summary.skipped === 0) {\n logger.warn(\"No files were fetched.\");\n }\n } catch (error) {\n if (error instanceof GitHubClientError) {\n // Include auth hints in error message for JSON mode\n const authHint =\n error.statusCode === 401 || error.statusCode === 403\n ? \" Tip: Set GITHUB_TOKEN or GH_TOKEN environment variable, or use `GITHUB_TOKEN=$(gh auth token) rulesync fetch ...`\"\n : \"\";\n throw new CLIError(`GitHub API Error: ${error.message}.${authHint}`, ErrorCodes.FETCH_FAILED);\n }\n throw error;\n }\n}\n","import { existsSync, type FSWatcher, watch as fsWatch, statSync } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\n\nimport {\n RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH,\n RULESYNC_RELATIVE_DIR_PATH,\n} from \"../constants/rulesync-paths.js\";\n\n/**\n * Trailing debounce window applied to file-system events before a regeneration\n * is started. Editor save storms and `git checkout` emit many events within a\n * few milliseconds; coalescing them into a single run keeps the terminal\n * readable and avoids redundant work.\n */\nexport const DEFAULT_WATCH_DEBOUNCE_MS = 300;\n\nexport type WatchSchedulerParams = {\n /**\n * Runs one regeneration for the paths that changed since the previous run.\n */\n run: (params: { triggers: string[] }) => Promise<void>;\n /**\n * Called when `run` rejects. Watching continues afterwards, so this must not\n * rethrow.\n */\n onError: (params: { error: unknown; triggers: string[] }) => void;\n debounceMs?: number;\n};\n\n/**\n * Coalesces file-system change notifications into debounced, non-overlapping\n * runs.\n *\n * Guarantees:\n * - At most one `run` is in flight at any time.\n * - Every notified path is reported to exactly one `run` as a trigger.\n * - Notifications that arrive while a run is in flight schedule exactly one\n * follow-up run after it finishes, so a change is never lost and never\n * causes a run per event.\n */\nexport class WatchScheduler {\n private readonly run: (params: { triggers: string[] }) => Promise<void>;\n private readonly onError: (params: { error: unknown; triggers: string[] }) => void;\n private readonly debounceMs: number;\n private readonly pending = new Set<string>();\n private timer: ReturnType<typeof setTimeout> | undefined;\n private running: Promise<void> | undefined;\n private closed = false;\n\n constructor({ run, onError, debounceMs = DEFAULT_WATCH_DEBOUNCE_MS }: WatchSchedulerParams) {\n this.run = run;\n this.onError = onError;\n this.debounceMs = debounceMs;\n }\n\n public notify({ path }: { path: string }): void {\n if (this.closed) {\n return;\n }\n this.pending.add(path);\n this.schedule();\n }\n\n /**\n * Stops accepting notifications and waits for an in-flight run to settle.\n * Pending (not yet started) changes are dropped.\n */\n public async close(): Promise<void> {\n this.closed = true;\n this.clearTimer();\n this.pending.clear();\n await this.running;\n }\n\n private clearTimer(): void {\n if (this.timer !== undefined) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n }\n\n private schedule(): void {\n this.clearTimer();\n this.timer = setTimeout(() => {\n this.timer = undefined;\n void this.flush();\n }, this.debounceMs);\n }\n\n private async flush(): Promise<void> {\n // A run started by an earlier flush re-schedules itself when it finds\n // pending triggers, so bailing out here never drops a change.\n if (this.closed || this.running !== undefined || this.pending.size === 0) {\n return;\n }\n\n const triggers = [...this.pending];\n this.pending.clear();\n\n const running = (async () => {\n try {\n await this.run({ triggers });\n } catch (error) {\n this.onError({ error, triggers });\n }\n })();\n this.running = running;\n await running;\n this.running = undefined;\n\n if (!this.closed && this.pending.size > 0) {\n this.schedule();\n }\n }\n}\n\nexport type WatchTarget = {\n /** Absolute path of the directory to watch. */\n directory: string;\n recursive: boolean;\n /**\n * When set, only events whose path relative to `directory` satisfies the\n * predicate are forwarded. Used to watch a directory that also holds\n * unrelated files (e.g. the project root, which holds `rulesync.jsonc` next\n * to generated output).\n */\n include?: (relativePath: string) => boolean;\n};\n\nexport type WatchHandle = {\n close: () => void;\n};\n\n/**\n * How often a watcher whose directory disappeared polls for its return.\n */\nexport const DEFAULT_WATCH_REARM_INTERVAL_MS = 500;\n\n/**\n * Inode of the path, or undefined when it is missing, unreadable, or the\n * platform reports no usable inode (Windows file systems without file IDs\n * report 0). Bigint stats avoid inode truncation on platforms with 64-bit\n * inode numbers.\n */\nfunction statIno(path: string): bigint | undefined {\n try {\n const ino = statSync(path, { bigint: true }).ino;\n return ino === 0n ? undefined : ino;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Watches one directory, re-attaching the underlying `fs.watch` if the\n * directory is deleted and later recreated.\n *\n * Without this, a `git checkout` to a branch without `.rulesync/` (or any\n * tool that replaces the directory rather than its contents) would silently\n * kill the watcher: the deleted inode emits no further events and no error,\n * so watch mode would keep running while never regenerating again.\n *\n * The first attach is not guarded — a missing directory at startup is a real\n * configuration error and must surface to the caller.\n */\nfunction watchTargetWithRearm({\n target,\n onChange,\n onError,\n rearmIntervalMs,\n}: {\n target: WatchTarget;\n onChange: (params: { path: string }) => void;\n onError: (params: { error: unknown; directory: string }) => void;\n rearmIntervalMs: number;\n}): WatchHandle {\n let watcher: FSWatcher | undefined;\n let watchedIno: bigint | undefined;\n let rearmTimer: ReturnType<typeof setInterval> | undefined;\n let closed = false;\n\n const attach = (): void => {\n // Stat before watching so a delete+recreate between the two calls leaves\n // `watchedIno` on the old inode: the next liveness check then sees a\n // mismatch and self-heals with one extra re-attach. The opposite order\n // would record the new inode for a watcher bound to the dead one,\n // silencing the watch permanently.\n const ino = statIno(target.directory);\n const created = fsWatch(\n target.directory,\n { recursive: target.recursive, persistent: true },\n (_eventType, filename) => {\n // `fs.watch` reports a null filename on some platforms; treat those as\n // a change to the watched directory itself.\n if (filename === null || filename === undefined) {\n onChange({ path: target.directory });\n verifyStillWatching();\n return;\n }\n const relativePath = filename.toString();\n if (target.include && !target.include(relativePath)) {\n // Still check liveness: the final event a deleted directory emits\n // names the directory itself, which every `include` predicate here\n // rejects. Returning early would leave the dead watcher attached\n // and re-arming would never start.\n verifyStillWatching();\n return;\n }\n onChange({ path: join(target.directory, relativePath) });\n verifyStillWatching();\n },\n );\n created.on(\"error\", (error) => {\n onError({ error, directory: target.directory });\n verifyStillWatching();\n });\n watcher = created;\n watchedIno = ino;\n };\n\n const scheduleRearm = (): void => {\n if (closed || rearmTimer !== undefined) {\n return;\n }\n rearmTimer = setInterval(() => {\n if (closed || !existsSync(target.directory)) {\n return;\n }\n clearInterval(rearmTimer);\n rearmTimer = undefined;\n try {\n attach();\n } catch (error) {\n // Lost another race with a delete; keep polling.\n onError({ error, directory: target.directory });\n scheduleRearm();\n return;\n }\n // The directory came back with unknown contents, so regenerate.\n onChange({ path: target.directory });\n }, rearmIntervalMs);\n };\n\n const verifyStillWatching = (): void => {\n if (closed || watcher === undefined) {\n return;\n }\n if (existsSync(target.directory)) {\n // A bare existence check is not enough: when the directory is deleted\n // and recreated before the delete event is delivered (fast branch\n // switches, slow CI event queues), the path exists again but the watch\n // is still bound to the dead inode and would never fire again. Compare\n // inodes to detect the replacement; an unreadable stat on either side\n // falls back to treating the watcher as alive, matching the previous\n // behavior.\n const currentIno = statIno(target.directory);\n if (currentIno === undefined || watchedIno === undefined || currentIno === watchedIno) {\n return;\n }\n }\n watcher.close();\n watcher = undefined;\n // Report the disappearance the same way an OS delete event would have —\n // liveness may have detected it purely by polling, with no event ever\n // delivered. The scheduler debounces, so an extra notification after an\n // event-driven detection is harmless.\n onChange({ path: target.directory });\n scheduleRearm();\n };\n\n attach();\n\n // Event-driven liveness checks alone are not enough: OS event delivery for\n // a deleted watched directory can be arbitrarily late or dropped entirely\n // (observed on loaded CI runners), leaving a dead watcher attached forever.\n // A periodic sweep runs the same inode-based check on a timer, so a\n // replaced or removed directory is detected within `rearmIntervalMs` even\n // when no event ever arrives. `unref()` keeps the interval from holding the\n // process open on its own.\n const livenessTimer = setInterval(() => {\n verifyStillWatching();\n }, rearmIntervalMs);\n livenessTimer.unref?.();\n\n return {\n close: () => {\n closed = true;\n clearInterval(livenessTimer);\n if (rearmTimer !== undefined) {\n clearInterval(rearmTimer);\n rearmTimer = undefined;\n }\n watcher?.close();\n watcher = undefined;\n },\n };\n}\n\n/**\n * Starts one watcher per target and forwards matching events to `onChange` as\n * absolute paths. If any target fails to attach, the watchers started so far\n * are closed before the error propagates, so no descriptor is leaked.\n */\nexport function watchTargets({\n targets,\n onChange,\n onError,\n rearmIntervalMs = DEFAULT_WATCH_REARM_INTERVAL_MS,\n}: {\n targets: readonly WatchTarget[];\n onChange: (params: { path: string }) => void;\n onError: (params: { error: unknown; directory: string }) => void;\n rearmIntervalMs?: number;\n}): WatchHandle {\n const handles: WatchHandle[] = [];\n\n const closeAll = (): void => {\n for (const handle of handles) {\n handle.close();\n }\n };\n\n try {\n for (const target of targets) {\n handles.push(watchTargetWithRearm({ target, onChange, onError, rearmIntervalMs }));\n }\n } catch (error) {\n closeAll();\n throw error;\n }\n\n return { close: closeAll };\n}\n\n/**\n * Builds the set of directories watch mode observes: the `.rulesync/` source\n * tree (recursively) and, filtered down to the configuration files themselves,\n * the directory holding `rulesync.jsonc`.\n *\n * Only input paths are watched. Generated output lives outside `.rulesync/`, so\n * a regeneration cannot re-trigger the watcher.\n */\nexport function buildWatchTargets({\n inputRoot,\n configFilePath,\n}: {\n inputRoot: string;\n configFilePath: string;\n}): WatchTarget[] {\n const configFilePaths = buildConfigFilePaths({ configFilePath });\n\n return [\n { directory: join(inputRoot, RULESYNC_RELATIVE_DIR_PATH), recursive: true },\n {\n directory: dirname(configFilePath),\n recursive: false,\n include: (relativePath) => configFilePaths.has(join(dirname(configFilePath), relativePath)),\n },\n ];\n}\n\n/**\n * The absolute paths of the configuration files watch mode observes: the base\n * configuration file and the `rulesync.local.jsonc` sitting next to it, which\n * is exactly what `ConfigResolver` loads.\n */\nexport function buildConfigFilePaths({ configFilePath }: { configFilePath: string }): Set<string> {\n return new Set([\n configFilePath,\n join(dirname(configFilePath), RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH),\n ]);\n}\n\n/**\n * Renders trigger paths relative to `baseDir` for logging, truncating long\n * bursts so a `git checkout` does not flood the terminal.\n */\nexport function formatTriggerPaths({\n triggers,\n baseDir,\n max = 5,\n}: {\n triggers: readonly string[];\n baseDir: string;\n max?: number;\n}): string {\n const displayed = triggers.slice(0, max).map((trigger) => relative(baseDir, trigger) || trigger);\n const remaining = triggers.length - displayed.length;\n return remaining > 0 ? `${displayed.join(\", \")} (+${remaining} more)` : displayed.join(\", \");\n}\n","import { ConfigResolver, type ConfigResolverResolveParams } from \"../../config/config-resolver.js\";\nimport type { Config } from \"../../config/config.js\";\nimport { checkRulesyncDirExists, generate, type GenerateResult } from \"../../lib/generate.js\";\nimport {\n buildConfigFilePaths,\n buildWatchTargets,\n formatTriggerPaths,\n WatchScheduler,\n watchTargets,\n} from \"../../lib/watch.js\";\nimport { CLIError, ErrorCodes } from \"../../types/json-output.js\";\nimport { formatError } from \"../../utils/error.js\";\nimport type { Logger } from \"../../utils/logger.js\";\nimport { calculateTotalCount } from \"../../utils/result.js\";\n\nexport type GenerateOptions = ConfigResolverResolveParams & {\n /** Keep running and regenerate whenever a rulesync source file changes. */\n watch?: boolean;\n};\n\n/**\n * Log feature generation result with appropriate prefix based on dry run mode.\n */\nfunction logFeatureResult(\n logger: Logger,\n params: {\n count: number;\n paths: string[];\n featureName: string;\n isPreview: boolean;\n modePrefix: string;\n },\n): void {\n const { count, paths, featureName, isPreview, modePrefix } = params;\n if (count > 0) {\n if (isPreview) {\n logger.info(`${modePrefix} Would write ${count} ${featureName}`);\n } else {\n logger.success(`Written ${count} ${featureName}`);\n }\n for (const p of paths) {\n logger.info(` ${p}`);\n }\n }\n}\n\nconst FEATURE_DEBUG_MESSAGES: Record<string, string> = {\n ignore: \"Generating ignore files...\",\n mcp: \"Generating MCP files...\",\n commands: \"Generating command files...\",\n subagents: \"Generating subagent files...\",\n skills: \"Generating skill files...\",\n hooks: \"Generating hooks...\",\n checks: \"Generating check files...\",\n rules: \"Generating rule files...\",\n};\n\n// Order in which per-feature debug messages are emitted; matches the original\n// sequential `if (features.includes(...))` ladder.\nconst FEATURE_DEBUG_ORDER = [\n \"ignore\",\n \"mcp\",\n \"commands\",\n \"subagents\",\n \"skills\",\n \"hooks\",\n \"checks\",\n \"rules\",\n] as const;\n\nfunction logFeatureDebugMessages(logger: Logger, features: readonly string[]): void {\n for (const feature of FEATURE_DEBUG_ORDER) {\n if (features.includes(feature)) {\n logger.debug(FEATURE_DEBUG_MESSAGES[feature] ?? \"\");\n }\n }\n}\n\n/**\n * Build the human-readable per-feature summary fragments (e.g. \"3 rules\") for\n * features that produced at least one file. Order matches the original\n * sequential `if (count > 0) parts.push(...)` ladder.\n */\nfunction buildSummaryParts(result: GenerateResult): string[] {\n const summarySpecs: { count: number; label: string }[] = [\n { count: result.rulesCount, label: \"rules\" },\n { count: result.ignoreCount, label: \"ignore files\" },\n { count: result.mcpCount, label: \"MCP files\" },\n { count: result.commandsCount, label: \"commands\" },\n { count: result.subagentsCount, label: \"subagents\" },\n { count: result.skillsCount, label: \"skills\" },\n { count: result.hooksCount, label: \"hooks\" },\n { count: result.permissionsCount, label: \"permissions\" },\n { count: result.checksCount, label: \"checks\" },\n { count: result.activationCount, label: \"Hermes activation files\" },\n ];\n\n const parts: string[] = [];\n for (const { count, label } of summarySpecs) {\n if (count > 0) parts.push(`${count} ${label}`);\n }\n return parts;\n}\n\nexport async function generateCommand(logger: Logger, options: GenerateOptions): Promise<void> {\n if (options.watch) {\n await generateWatchCommand(logger, options);\n return;\n }\n await generateOnce(logger, options);\n}\n\n/**\n * Runs one generation. `resolvedConfig` lets a caller that already resolved\n * the configuration (watch mode's startup validation) reuse it instead of\n * paying for a second resolution — and, more importantly, instead of emitting\n * the resolver's warnings twice.\n */\nasync function generateOnce(\n logger: Logger,\n options: GenerateOptions,\n { resolvedConfig }: { resolvedConfig?: Config } = {},\n): Promise<void> {\n const config = resolvedConfig ?? (await ConfigResolver.resolve(options, { logger }));\n\n const check = config.getCheck();\n\n const isPreview = config.isPreviewMode();\n const modePrefix = isPreview ? \"[DRY RUN]\" : \"\";\n\n logger.debug(\"Generating files...\");\n\n if (!(await checkRulesyncDirExists({ inputRoot: config.getInputRoot() }))) {\n throw new CLIError(\n \".rulesync directory not found. Run 'rulesync init' first.\",\n ErrorCodes.RULESYNC_DIR_NOT_FOUND,\n );\n }\n\n logger.debug(`Output roots: ${config.getOutputRoots().join(\", \")}`);\n\n const features = config.getFeatures();\n\n logFeatureDebugMessages(logger, features);\n\n const result = await generate({ config, logger });\n\n const totalGenerated = calculateTotalCount(result);\n\n // Log feature results and capture data for JSON mode\n const featureResults = {\n ignore: { count: result.ignoreCount, paths: result.ignorePaths },\n mcp: { count: result.mcpCount, paths: result.mcpPaths },\n commands: { count: result.commandsCount, paths: result.commandsPaths },\n subagents: { count: result.subagentsCount, paths: result.subagentsPaths },\n skills: { count: result.skillsCount, paths: result.skillsPaths },\n hooks: { count: result.hooksCount, paths: result.hooksPaths },\n permissions: { count: result.permissionsCount, paths: result.permissionsPaths },\n checks: { count: result.checksCount, paths: result.checksPaths },\n rules: { count: result.rulesCount, paths: result.rulesPaths },\n activation: { count: result.activationCount, paths: result.activationPaths },\n };\n\n // Map feature keys to human-readable labels with pluralization\n const featureLabels: Record<string, (count: number) => string> = {\n rules: (count) => `${count === 1 ? \"rule\" : \"rules\"}`,\n ignore: (count) => `${count === 1 ? \"ignore file\" : \"ignore files\"}`,\n mcp: (count) => `${count === 1 ? \"MCP file\" : \"MCP files\"}`,\n commands: (count) => `${count === 1 ? \"command\" : \"commands\"}`,\n subagents: (count) => `${count === 1 ? \"subagent\" : \"subagents\"}`,\n skills: (count) => `${count === 1 ? \"skill\" : \"skills\"}`,\n hooks: (count) => `${count === 1 ? \"hooks file\" : \"hooks files\"}`,\n permissions: (count) => `${count === 1 ? \"permissions file\" : \"permissions files\"}`,\n checks: (count) => `${count === 1 ? \"check\" : \"checks\"}`,\n activation: (count) => `${count === 1 ? \"Hermes activation file\" : \"Hermes activation files\"}`,\n };\n\n for (const [feature, data] of Object.entries(featureResults)) {\n logFeatureResult(logger, {\n count: data.count,\n paths: data.paths,\n featureName: featureLabels[feature]?.(data.count) ?? feature,\n isPreview,\n modePrefix,\n });\n }\n\n // Capture JSON data if in JSON mode\n if (logger.jsonMode) {\n logger.captureData(\"features\", featureResults);\n logger.captureData(\"totalFiles\", totalGenerated);\n logger.captureData(\"hasDiff\", result.hasDiff);\n logger.captureData(\"skills\", result.skills ?? []);\n }\n\n // Check mode must fail even when the change is delete-only and no files are written.\n if (check) {\n if (result.hasDiff) {\n throw new CLIError(\n \"Files are not up to date. Run 'rulesync generate' to update.\",\n ErrorCodes.GENERATION_FAILED,\n );\n }\n\n logger.success(\"✓ All files are up to date.\");\n return;\n }\n\n if (totalGenerated === 0) {\n const enabledFeatures = features.join(\", \");\n logger.info(`✓ All files are up to date (${enabledFeatures})`);\n return;\n }\n\n const parts = buildSummaryParts(result);\n\n if (isPreview) {\n logger.info(`${modePrefix} Would write ${totalGenerated} file(s) total (${parts.join(\" + \")})`);\n } else {\n logger.success(`🎉 All done! Written ${totalGenerated} file(s) total (${parts.join(\" + \")})`);\n }\n}\n\n/**\n * Rejects flag combinations that contradict a long-running watch: `--check`\n * and `--dry-run` are one-shot verification modes (the former is meant to exit\n * non-zero), and `--json` buffers a single result document until the command\n * returns, which never happens while watching.\n */\nexport function assertWatchModeCompatible({\n isCheck,\n isDryRun,\n isJsonMode,\n}: {\n isCheck: boolean;\n isDryRun: boolean;\n isJsonMode: boolean;\n}): void {\n const conflicts = [\n isCheck ? \"--check\" : undefined,\n isDryRun ? \"--dry-run\" : undefined,\n isJsonMode ? \"--json\" : undefined,\n ].filter((flag): flag is string => flag !== undefined);\n\n if (conflicts.length > 0) {\n throw new CLIError(\n `--watch cannot be combined with ${conflicts.join(\", \")}.`,\n ErrorCodes.VALIDATION_FAILED,\n );\n }\n}\n\nasync function generateWatchCommand(logger: Logger, options: GenerateOptions): Promise<void> {\n // Resolve once up front so the incompatible-mode check also covers values\n // coming from the config file, not just CLI flags.\n const config = await ConfigResolver.resolve(options, { logger });\n assertWatchModeCompatible({\n isCheck: config.getCheck(),\n isDryRun: config.getDryRun(),\n isJsonMode: logger.jsonMode,\n });\n\n const inputRoot = config.getInputRoot();\n // Take the path the resolver actually loaded rather than re-deriving it:\n // the two differ when `inputRoot` comes from the configuration file itself.\n const configFilePath = config.getConfigFilePath();\n const configFilePaths = buildConfigFilePaths({ configFilePath });\n\n // Run once before watching so a missing `.rulesync` directory (or any other\n // configuration error) fails fast instead of starting an idle watcher.\n await generateOnce(logger, options, { resolvedConfig: config });\n\n const targets = buildWatchTargets({ inputRoot, configFilePath });\n\n const scheduler = new WatchScheduler({\n run: async ({ triggers }) => {\n logger.info(`\\nChange detected: ${formatTriggerPaths({ triggers, baseDir: inputRoot })}`);\n if (triggers.some((trigger) => configFilePaths.has(trigger))) {\n logger.warn(\n \"Configuration file changed. The set of watched paths is fixed at startup — restart 'rulesync generate --watch' if you changed 'inputRoot' or the configuration file location.\",\n );\n }\n await generateOnce(logger, options);\n },\n onError: ({ error }) => {\n logger.error(`Generation failed: ${formatError(error)}`);\n logger.info(\"Still watching for changes...\");\n },\n });\n\n const handle = watchTargets({\n targets,\n onChange: ({ path }) => {\n scheduler.notify({ path });\n },\n onError: ({ error, directory }) => {\n logger.error(`Watch error on ${directory}: ${formatError(error)}`);\n },\n });\n\n logger.info(\n `\\nWatching for changes in:\\n${targets.map((target) => ` ${target.directory}`).join(\"\\n\")}`,\n );\n logger.info(\"Press Ctrl+C to stop.\");\n\n await new Promise<void>((resolveShutdown) => {\n const shutdown = (): void => {\n process.off(\"SIGINT\", shutdown);\n process.off(\"SIGTERM\", shutdown);\n handle.close();\n void scheduler\n .close()\n .catch((error: unknown) => {\n logger.error(`Failed to stop the watcher cleanly: ${formatError(error)}`);\n })\n .finally(() => {\n logger.info(\"\\nStopped watching.\");\n resolveShutdown();\n });\n };\n process.once(\"SIGINT\", shutdown);\n process.once(\"SIGTERM\", shutdown);\n });\n}\n","import { SHARED_USER_MANAGED_CONFIG_PATHS } from \"../../constants/shared-config-paths.js\";\nimport type { ToolRuleExtraFixedFile } from \"../../features/rules/tool-rule.js\";\nimport type { Feature } from \"../../types/features.js\";\nimport { getProcessorRegistryEntry } from \"../../types/processor-registry.js\";\nimport type { ToolTarget } from \"../../types/tool-targets.js\";\n\nexport type GitignoreEntryTarget = ToolTarget | \"common\";\n\nexport type GitignoreEntryTag = {\n readonly target: GitignoreEntryTarget | ReadonlyArray<GitignoreEntryTarget>;\n readonly feature: Feature | \"general\";\n readonly entry: string;\n};\n\n// Targets excluded from derivation: they don't generate project files\n// (agentsskills) or are deprecated aliases whose outputs are covered elsewhere\n// (augmentcode-legacy → augmentcode, claudecode-legacy → claudecode).\nconst TARGETS_NOT_DERIVED: ReadonlySet<string> = new Set([\n \"agentsskills\",\n \"augmentcode-legacy\",\n \"claudecode-legacy\",\n]);\n\n// Project-scope outputs that rulesync merges into rather than fully owns\n// (user-managed settings files), so they are deliberately not gitignored even\n// though a feature emits them. The list itself lives in\n// `src/constants/shared-config-paths.ts` because the same set also decides\n// which files must not be created just to hold an empty payload.\nexport const DERIVED_PATHS_NOT_GITIGNORED: ReadonlySet<string> = new Set(\n SHARED_USER_MANAGED_CONFIG_PATHS.map((path) => `**/${path}`),\n);\n\nconst toPosix = (path: string): string => path.replace(/\\\\/g, \"/\");\n\nconst dirToGlob = (relativeDirPath: string): string =>\n `**/${toPosix(relativeDirPath).replace(/\\/$/, \"\")}/`;\n\nconst fileToGlob = (relativeDirPath: string | undefined, relativeFilePath: string): string => {\n const hasDir = relativeDirPath && relativeDirPath !== \".\";\n return `**/${toPosix(hasDir ? `${relativeDirPath}/${relativeFilePath}` : relativeFilePath)}`;\n};\n\nconst supportsProject = (factory: unknown): boolean => {\n if (typeof factory !== \"object\" || factory === null || !(\"meta\" in factory)) return true;\n const meta = (factory as { meta?: { supportsProject?: boolean } }).meta;\n return meta?.supportsProject !== false;\n};\n\ntype SettablePathsFn = (options?: { global?: boolean }) => unknown;\n\ntype FactoryMap = ReadonlyMap<ToolTarget, { readonly class: { getSettablePaths: unknown } }>;\n\nconst getProjectPaths = (factory: { class: { getSettablePaths: unknown } }): unknown =>\n (factory.class.getSettablePaths as SettablePathsFn)({ global: false });\n\nconst pushEntry = (\n entries: GitignoreEntryTag[],\n target: ToolTarget,\n feature: Feature,\n entry: string,\n): void => {\n entries.push({ target, feature, entry });\n};\n\nconst deriveDirEntries = (factories: FactoryMap, feature: Feature): GitignoreEntryTag[] => {\n const entries: GitignoreEntryTag[] = [];\n for (const [target, factory] of factories) {\n if (TARGETS_NOT_DERIVED.has(target)) continue;\n if (!supportsProject(factory)) continue;\n const paths = getProjectPaths(factory) as {\n relativeDirPath?: string;\n relativeFilePath?: string;\n };\n const dir = paths.relativeDirPath;\n if (!dir || dir === \".\") continue;\n // A tool that names a single file writes only that file, even though the\n // feature usually emits a directory tree. Ignoring the whole directory would\n // swallow the files the user hand-maintains beside it — and git cannot\n // un-ignore a path inside an ignored directory.\n if (paths.relativeFilePath) {\n pushEntry(entries, target, feature, fileToGlob(dir, paths.relativeFilePath));\n continue;\n }\n pushEntry(entries, target, feature, dirToGlob(dir));\n }\n return entries;\n};\n\nconst deriveFileEntries = (factories: FactoryMap, feature: Feature): GitignoreEntryTag[] => {\n const entries: GitignoreEntryTag[] = [];\n for (const [target, factory] of factories) {\n if (TARGETS_NOT_DERIVED.has(target)) continue;\n if (!supportsProject(factory)) continue;\n const paths = getProjectPaths(factory) as {\n relativeDirPath?: string;\n relativeFilePath?: string;\n };\n if (!paths.relativeFilePath) continue;\n pushEntry(entries, target, feature, fileToGlob(paths.relativeDirPath, paths.relativeFilePath));\n }\n return entries;\n};\n\n// Rules have a composite shape: root/alternativeRoots are files, nonRoot is a\n// directory subtree.\nconst deriveRulesEntries = (): GitignoreEntryTag[] => {\n const entries: GitignoreEntryTag[] = [];\n const factories = getProcessorRegistryEntry(\"rules\").factory as unknown as FactoryMap;\n for (const [target, factory] of factories) {\n if (TARGETS_NOT_DERIVED.has(target)) continue;\n const paths = getProjectPaths(factory) as {\n root?: { relativeDirPath: string; relativeFilePath: string };\n alternativeRoots?: ReadonlyArray<{ relativeDirPath: string; relativeFilePath: string }>;\n nonRoot?: { relativeDirPath: string } | null;\n };\n for (const root of [paths.root, ...(paths.alternativeRoots ?? [])]) {\n if (root)\n pushEntry(\n entries,\n target,\n \"rules\",\n fileToGlob(root.relativeDirPath, root.relativeFilePath),\n );\n }\n const nonRootDir = paths.nonRoot?.relativeDirPath;\n if (nonRootDir && nonRootDir !== \".\") {\n pushEntry(entries, target, \"rules\", dirToGlob(nonRootDir));\n }\n // Extra fixed-path files a tool manages beyond root/nonRoot (e.g. Pi's\n // `.pi/APPEND_SYSTEM.md`). Derived from the same hook the RulesProcessor uses.\n const classWithExtraFiles = factory.class as {\n getExtraFixedFiles?: (options?: { global?: boolean }) => ToolRuleExtraFixedFile[];\n };\n if (classWithExtraFiles.getExtraFixedFiles) {\n for (const file of classWithExtraFiles.getExtraFixedFiles({ global: false })) {\n pushEntry(\n entries,\n target,\n \"rules\",\n fileToGlob(file.relativeDirPath, file.relativeFilePath),\n );\n }\n }\n }\n return entries;\n};\n\n// commands/skills/subagents/checks emit a directory tree; mcp/hooks/permissions/ignore\n// emit a single file; rules has a composite root+nonRoot shape.\nconst DIR_FEATURES = new Set<Feature>([\"commands\", \"skills\", \"subagents\", \"checks\"]);\nconst FILE_FEATURES = new Set<Feature>([\"mcp\", \"hooks\", \"permissions\", \"ignore\"]);\n\nconst deriveFeatureGitignoreEntries = (feature: Feature): GitignoreEntryTag[] => {\n if (feature === \"rules\") return deriveRulesEntries();\n const factory = getProcessorRegistryEntry(feature).factory as unknown as FactoryMap;\n if (DIR_FEATURES.has(feature)) return deriveDirEntries(factory, feature);\n if (FILE_FEATURES.has(feature)) return deriveFileEntries(factory, feature);\n return [];\n};\n\nconst DERIVED_FEATURES: ReadonlyArray<Feature> = [\n \"rules\",\n \"commands\",\n \"skills\",\n \"subagents\",\n \"mcp\",\n \"hooks\",\n \"permissions\",\n \"ignore\",\n \"checks\",\n];\n\n// Every project-scope output path, derived from each tool's getSettablePaths,\n// BEFORE the DERIVED_PATHS_NOT_GITIGNORED exclusion is applied. Exported so\n// tests can verify each exclusion-set path still matches a real output path.\nexport const deriveAllGitignoreEntriesUnfiltered = (): GitignoreEntryTag[] =>\n DERIVED_FEATURES.flatMap((feature) => deriveFeatureGitignoreEntries(feature));\n\n// Every gitignore entry rulesync emits, derived from each tool's getSettablePaths.\nexport const deriveAllGitignoreEntries = (): GitignoreEntryTag[] =>\n deriveAllGitignoreEntriesUnfiltered().filter(\n (tag) => !DERIVED_PATHS_NOT_GITIGNORED.has(tag.entry),\n );\n","import {\n CLAUDECODE_DIR,\n CLAUDECODE_LOCAL_RULE_FILE_NAME,\n CLAUDECODE_MEMORIES_DIR_NAME,\n CLAUDECODE_SETTINGS_LOCAL_FILE_NAME,\n} from \"../../constants/claudecode-paths.js\";\nimport { CODEXCLI_BASH_RULES_FILE_NAME, CODEXCLI_DIR } from \"../../constants/codexcli-paths.js\";\nimport { QWENCODE_DIR, QWENCODE_LOCAL_RULE_FILE_NAME } from \"../../constants/qwencode-paths.js\";\nimport {\n RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH,\n RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH,\n} from \"../../constants/rulesync-paths.js\";\nimport {\n ALL_FEATURES_WITH_WILDCARD,\n type Feature,\n type RulesyncFeatures,\n} from \"../../types/features.js\";\nimport {\n ALL_TOOL_TARGETS_WITH_WILDCARD,\n PACKAGING_TOOL_TARGETS,\n} from \"../../types/tool-targets.js\";\nimport type { Logger } from \"../../utils/logger.js\";\nimport {\n deriveAllGitignoreEntries,\n type GitignoreEntryTag,\n type GitignoreEntryTarget,\n} from \"./gitignore-derive.js\";\n\nconst normalizeGitignoreEntryTargets = (\n target: GitignoreEntryTag[\"target\"],\n): ReadonlyArray<GitignoreEntryTarget> => {\n return typeof target === \"string\" ? [target] : target;\n};\n\n// Hand-maintained entries that are NOT derivable from any tool's\n// getSettablePaths, because they are not rulesync-owned generated outputs:\n// - rulesync's own meta files (`.rulesync/**`, `rulesync.local.jsonc`, the\n// `AGENTS.local.md` / `CLAUDE.local.md` local-root files, the `.aiignore`\n// un-ignore exception)\n// - third-party tool by-products rulesync never writes but gitignores as a\n// convenience (`.claude/*.lock`, `.takt/runs/`, lock files, …)\n// - the `.codexignore` ghost (codexcli has no ignore processor)\n// Everything a tool actually emits is derived below from getSettablePaths.\nexport const HAND_MAINTAINED_GITIGNORE_ENTRIES: ReadonlyArray<GitignoreEntryTag> = [\n // rulesync's own meta files (common scope).\n {\n target: \"common\",\n feature: \"general\",\n entry: `${RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH}/`,\n },\n {\n target: \"common\",\n feature: \"general\",\n entry: `${RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH}/`,\n },\n { target: \"common\", feature: \"general\", entry: \".rulesync/rules/*.local.md\" },\n { target: \"common\", feature: \"general\", entry: \"rulesync.local.jsonc\" },\n // AGENTS.local.md is placed in common scope (not rovodev-only) so that\n // local rule files are always gitignored regardless of which targets are enabled.\n { target: \"common\", feature: \"general\", entry: \"**/AGENTS.local.md\" },\n\n // Local-root rule files: materialized outside getSettablePaths.\n { target: \"claudecode\", feature: \"rules\", entry: `**/${CLAUDECODE_LOCAL_RULE_FILE_NAME}` },\n // Qwen Code's personal project context file (v0.16.2): emitted for localRoot\n // rules but must not be committed (Qwen Code does not gitignore it itself).\n {\n target: \"qwencode\",\n feature: \"rules\",\n entry: `**/${QWENCODE_DIR}/${QWENCODE_LOCAL_RULE_FILE_NAME}`,\n },\n {\n target: \"claudecode\",\n feature: \"rules\",\n entry: `**/${CLAUDECODE_DIR}/${CLAUDECODE_LOCAL_RULE_FILE_NAME}`,\n },\n\n // Third-party tool by-products rulesync gitignores but never writes itself.\n { target: \"claudecode\", feature: \"general\", entry: `**/${CLAUDECODE_DIR}/*.lock` },\n {\n target: \"claudecode\",\n feature: \"general\",\n entry: `**/${CLAUDECODE_DIR}/${CLAUDECODE_SETTINGS_LOCAL_FILE_NAME}`,\n },\n {\n target: \"claudecode\",\n feature: \"general\",\n entry: `**/${CLAUDECODE_DIR}/${CLAUDECODE_MEMORIES_DIR_NAME}/`,\n },\n { target: \"opencode\", feature: \"general\", entry: \"**/.opencode/package-lock.json\" },\n // Devin's personal MCP override (documented as gitignored; never emitted by\n // rulesync). https://docs.devin.ai/cli/extensibility/mcp/configuration\n { target: \"devin\", feature: \"mcp\", entry: \"**/.devin/mcp_config.local.json\" },\n { target: \"rovodev\", feature: \"general\", entry: \"**/.rovodev/.rulesync/\" },\n { target: \"takt\", feature: \"general\", entry: \"**/.takt/runs/\" },\n { target: \"takt\", feature: \"general\", entry: \"**/.takt/tasks/\" },\n { target: \"takt\", feature: \"general\", entry: \"**/.takt/.cache/\" },\n { target: \"takt\", feature: \"general\", entry: \"**/.takt/config.yaml\" },\n\n // Augment Code's legacy single-file rules path: accepted on import but never\n // generated (so not in getSettablePaths), gitignored as a convenience.\n { target: \"augmentcode\", feature: \"rules\", entry: \"**/.augment-guidelines\" },\n\n // Devin's legacy Windsurf-era workflows directory: commands are now emitted\n // onto the skills surface, but outputs generated by earlier rulesync versions\n // may still exist there, so keep them gitignored as a convenience.\n { target: \"devin\", feature: \"commands\", entry: \"**/.devin/workflows/\" },\n\n // Junie's undocumented memories directory: non-root rules are now folded\n // into the root `.junie/AGENTS.md`, but outputs generated by earlier\n // rulesync versions may still exist there, so keep them gitignored.\n { target: \"junie\", feature: \"rules\", entry: \"**/.junie/memories/\" },\n\n // Goose retired `.gooseignore` upstream (removed \"in favour of gitignore\n // etc\"), so rulesync no longer generates it — but outputs from earlier\n // versions may still exist, so keep them gitignored.\n { target: \"goose\", feature: \"ignore\", entry: \"**/.gooseignore\" },\n\n // Goose subagents moved from the inert sub-recipe YAML surface to the\n // custom-agent Markdown surface; outputs generated by earlier rulesync\n // versions may still exist under the old directory, so keep them gitignored.\n { target: \"goose\", feature: \"subagents\", entry: \"**/.goose/recipes/subagents/\" },\n\n // Junie's allowlist is user-scope only (`~/.junie/allowlist.json`), so the\n // project path left getSettablePaths — but earlier rulesync versions wrote a\n // project `.junie/allowlist.json` Junie never reads, so keep those stale\n // outputs gitignored.\n { target: \"junie\", feature: \"permissions\", entry: \"**/.junie/allowlist.json\" },\n\n // Shared trees and global-scope outputs not produced via project getSettablePaths.\n { target: \"rovodev\", feature: \"skills\", entry: \"**/.agents/skills/\" },\n // The `prompts.yml` manifest is produced via `RovodevCommand.getAuxiliaryFiles`,\n // not `getSettablePaths` (only the sibling `.rovodev/prompts/` content-file\n // directory is derived automatically), so it needs a hand-maintained entry.\n { target: \"rovodev\", feature: \"commands\", entry: \"**/.rovodev/prompts.yml\" },\n { target: \"devin\", feature: \"skills\", entry: \"**/.config/devin/skills/\" },\n { target: \"copilotcli\", feature: \"subagents\", entry: \"**/.copilot/agents/\" },\n { target: \"copilotcli\", feature: \"mcp\", entry: \"**/.copilot/mcp-config.json\" },\n { target: \"copilotcli\", feature: \"hooks\", entry: \"**/.copilot/hooks/\" },\n { target: \"deepagents\", feature: \"hooks\", entry: \"**/.deepagents/hooks.json\" },\n // Hermes project plugins include generated Python/manifest/ownership files\n // alongside the primary patterns/check specs exposed by getSettablePaths.\n { target: \"hermesagent\", feature: \"ignore\", entry: \"**/.hermes/plugins/rulesync-ignore/\" },\n { target: \"hermesagent\", feature: \"checks\", entry: \"**/.hermes/plugins/rulesync-checks/\" },\n\n // Roo aggregates subagents into a single `.roomodes` file (no settable path).\n { target: \"roo\", feature: \"subagents\", entry: \"**/.roomodes\" },\n\n // codexcli has no ignore processor; its `.codexignore` is a ghost entry.\n { target: \"codexcli\", feature: \"ignore\", entry: \"**/.codexignore\" },\n\n // Codex CLI's `rulesync.rules` bash-permission file is produced by\n // `createCodexcliBashRulesFile` in codexcli-permissions.ts. That file is\n // written outside `getSettablePaths`, so it is not derived automatically and\n // needs a hand-maintained entry. Only the single rulesync-owned file is\n // ignored: `.codex/rules/` is a general Codex rules location where users can\n // hand-author their own `*.rules` files that should stay version-controlled.\n {\n target: \"codexcli\",\n feature: \"permissions\",\n entry: `**/${CODEXCLI_DIR}/rules/${CODEXCLI_BASH_RULES_FILE_NAME}`,\n },\n];\n\nexport const GITIGNORE_ENTRY_REGISTRY: ReadonlyArray<GitignoreEntryTag> = [\n ...HAND_MAINTAINED_GITIGNORE_ENTRIES,\n\n // Every entry a tool actually emits, derived from its getSettablePaths.\n ...deriveAllGitignoreEntries(),\n\n // Keep this after ignore entries like Junie's \"**/.aiignore\" so the exception remains effective.\n { target: \"common\", feature: \"general\", entry: \"!.rulesync/.aiignore\" },\n];\n\nexport const ALL_GITIGNORE_ENTRIES: ReadonlyArray<string> = (() => {\n // The registry may register the SAME entry under multiple feature tags\n // The exported default list excludes opt-in packaging targets and dedupes\n // while preserving the original insertion order.\n const seen = new Set<string>();\n const result: string[] = [];\n for (const tag of GITIGNORE_ENTRY_REGISTRY) {\n const targets = normalizeGitignoreEntryTargets(tag.target);\n const isPackagingOnly = targets.every((target) =>\n PACKAGING_TOOL_TARGETS.includes(target as (typeof PACKAGING_TOOL_TARGETS)[number]),\n );\n if (isPackagingOnly) continue;\n if (seen.has(tag.entry)) continue;\n seen.add(tag.entry);\n result.push(tag.entry);\n }\n return result;\n})();\n\ntype FilterGitignoreEntriesParams = {\n readonly targets?: ReadonlyArray<string>;\n readonly features?: RulesyncFeatures;\n};\n\nexport type ResolvedGitignoreEntry = {\n readonly entry: string;\n readonly target: ReadonlyArray<GitignoreEntryTarget>;\n readonly feature: Feature | \"general\";\n};\n\nconst isTargetSelected = (\n target: GitignoreEntryTag[\"target\"],\n selectedTargets: ReadonlyArray<string> | undefined,\n): boolean => {\n const targets = normalizeGitignoreEntryTargets(target);\n\n if (targets.includes(\"common\")) return true;\n if (!selectedTargets || selectedTargets.length === 0 || selectedTargets.includes(\"*\")) {\n return targets.some(\n (candidate) =>\n selectedTargets?.includes(candidate) ||\n !PACKAGING_TOOL_TARGETS.includes(candidate as (typeof PACKAGING_TOOL_TARGETS)[number]),\n );\n }\n return targets.some((candidate) => selectedTargets.includes(candidate));\n};\n\nconst getSelectedGitignoreEntryTargets = (\n target: GitignoreEntryTag[\"target\"],\n selectedTargets: ReadonlyArray<string> | undefined,\n): ReadonlyArray<GitignoreEntryTarget> => {\n const targets = normalizeGitignoreEntryTargets(target);\n\n if (targets.includes(\"common\")) return [\"common\"];\n if (!selectedTargets || selectedTargets.length === 0 || selectedTargets.includes(\"*\")) {\n return targets.filter(\n (candidate) =>\n selectedTargets?.includes(candidate) ||\n !PACKAGING_TOOL_TARGETS.includes(candidate as (typeof PACKAGING_TOOL_TARGETS)[number]),\n );\n }\n\n return targets.filter((candidate) => selectedTargets.includes(candidate));\n};\n\nconst isFeatureSelected = (\n feature: Feature | \"general\",\n features: RulesyncFeatures | undefined,\n): boolean => {\n if (feature === \"general\") return true;\n if (!features) return true;\n if (features.length === 0) return true;\n if (features.includes(\"*\")) return true;\n return features.includes(feature);\n};\n\nconst warnInvalidTargets = (targets: ReadonlyArray<string>, logger?: Logger): void => {\n const validTargets = new Set<string>(ALL_TOOL_TARGETS_WITH_WILDCARD);\n for (const target of targets) {\n if (!validTargets.has(target)) {\n logger?.warn(\n `Unknown target '${target}'. Valid targets: ${ALL_TOOL_TARGETS_WITH_WILDCARD.join(\", \")}`,\n );\n }\n }\n};\n\nconst warnInvalidFeatures = (features: RulesyncFeatures, logger?: Logger): void => {\n const validFeatures = new Set<string>(ALL_FEATURES_WITH_WILDCARD);\n const warned = new Set<string>();\n for (const feature of features) {\n if (!validFeatures.has(feature) && !warned.has(feature)) {\n warned.add(feature);\n logger?.warn(\n `Unknown feature '${feature}'. Valid features: ${ALL_FEATURES_WITH_WILDCARD.join(\", \")}`,\n );\n }\n }\n};\n\nexport const filterGitignoreEntries = (\n params?: FilterGitignoreEntriesParams & { logger?: Logger },\n): string[] => {\n return resolveGitignoreEntries(params).map((entry) => entry.entry);\n};\n\nexport const resolveGitignoreEntries = (\n params?: FilterGitignoreEntriesParams & { logger?: Logger },\n): ResolvedGitignoreEntry[] => {\n const { targets, features, logger } = params ?? {};\n\n if (targets && targets.length > 0) {\n warnInvalidTargets(targets, logger);\n }\n if (features) {\n warnInvalidFeatures(features, logger);\n }\n\n const seen = new Set<string>();\n const result: ResolvedGitignoreEntry[] = [];\n\n for (const tag of GITIGNORE_ENTRY_REGISTRY) {\n if (!isTargetSelected(tag.target, targets)) continue;\n const selectedTagTargets = getSelectedGitignoreEntryTargets(tag.target, targets);\n if (!isFeatureSelected(tag.feature, features)) continue;\n if (seen.has(tag.entry)) continue;\n seen.add(tag.entry);\n result.push({\n entry: tag.entry,\n target: selectedTagTargets,\n feature: tag.feature,\n });\n }\n\n return result;\n};\n","import { join } from \"node:path\";\n\nimport { ConfigResolver } from \"../../config/config-resolver.js\";\nimport type { Feature, GitignoreDestination, RulesyncFeatures } from \"../../types/features.js\";\nimport type { ToolTarget } from \"../../types/tool-targets.js\";\nimport { fileExists, readFileContent, writeFileContent } from \"../../utils/file.js\";\nimport type { Logger } from \"../../utils/logger.js\";\nimport {\n ALL_GITIGNORE_ENTRIES,\n resolveGitignoreEntries,\n type ResolvedGitignoreEntry,\n} from \"./gitignore-entries.js\";\n\n// Start / end markers that delimit the auto-generated block. Wrapping the\n// managed entries with an explicit footer lets `removeExistingRulesyncEntries`\n// strip the block deterministically instead of guessing where it ends.\nconst RULESYNC_HEADER = \"# Generated by Rulesync\";\nconst RULESYNC_FOOTER = \"# End of Rulesync\";\nconst LEGACY_RULESYNC_HEADER = \"# Generated by rulesync - AI tool configuration files\";\n\nconst isRulesyncHeader = (line: string): boolean => {\n const trimmed = line.trim();\n return trimmed === RULESYNC_HEADER || trimmed === LEGACY_RULESYNC_HEADER;\n};\n\nconst isRulesyncFooter = (line: string): boolean => {\n return line.trim() === RULESYNC_FOOTER;\n};\n\nconst isRulesyncEntry = (line: string): boolean => {\n const trimmed = line.trim();\n if (trimmed === \"\" || isRulesyncHeader(line) || isRulesyncFooter(line)) {\n return false;\n }\n return ALL_GITIGNORE_ENTRIES.includes(trimmed);\n};\n\n// Locate the footer that closes the block opened at `start - 1`. Returns -1 when\n// no footer appears before the next header (i.e. a legacy, marker-less block).\nconst findRulesyncFooterIndex = (lines: string[], start: number): number => {\n for (let index = start; index < lines.length; index++) {\n const line = lines[index] ?? \"\";\n if (isRulesyncFooter(line)) {\n return index;\n }\n if (isRulesyncHeader(line)) {\n return -1;\n }\n }\n return -1;\n};\n\n// Legacy fallback for blocks written before the footer marker existed: skip the\n// header and its following rulesync entries, stopping at two consecutive blank\n// lines or the first line that is neither blank nor a known rulesync entry.\nconst skipLegacyRulesyncBlock = (lines: string[], headerIndex: number): number => {\n let index = headerIndex + 1;\n let consecutiveEmptyLines = 0;\n\n while (index < lines.length) {\n const line = lines[index] ?? \"\";\n\n if (line.trim() === \"\") {\n consecutiveEmptyLines++;\n index++;\n if (consecutiveEmptyLines >= 2) {\n break;\n }\n continue;\n }\n\n if (isRulesyncEntry(line)) {\n consecutiveEmptyLines = 0;\n index++;\n continue;\n }\n\n // A non-blank, non-entry line ends the legacy block; leave it untouched.\n break;\n }\n\n return index;\n};\n\nconst removeExistingRulesyncEntries = (content: string): string => {\n const lines = content.split(\"\\n\");\n const filteredLines: string[] = [];\n let index = 0;\n\n while (index < lines.length) {\n const line = lines[index] ?? \"\";\n\n if (isRulesyncHeader(line)) {\n const footerIndex = findRulesyncFooterIndex(lines, index + 1);\n if (footerIndex !== -1) {\n // Marker-delimited block: drop everything from header to footer.\n index = footerIndex + 1;\n continue;\n }\n // No footer found: this is a legacy block, remove it heuristically.\n index = skipLegacyRulesyncBlock(lines, index);\n continue;\n }\n\n // Stray rulesync entries left outside a block (e.g. legacy leftovers).\n if (isRulesyncEntry(line)) {\n index++;\n continue;\n }\n\n filteredLines.push(line);\n index++;\n }\n\n let result = filteredLines.join(\"\\n\");\n\n while (result.endsWith(\"\\n\\n\")) {\n result = result.slice(0, -1);\n }\n\n return result;\n};\n\n// Collect the entries currently sitting inside rulesync-managed blocks (plus\n// stray recognized entries outside them), so the command can report which\n// previously managed paths are about to stop being gitignored.\nconst extractRulesyncManagedEntries = (content: string): string[] => {\n const lines = content.split(\"\\n\");\n const managed: string[] = [];\n let index = 0;\n\n const collectBlockLines = (start: number, end: number): void => {\n for (const blockLine of lines.slice(start, end)) {\n const trimmed = blockLine.trim();\n if (trimmed !== \"\") {\n managed.push(trimmed);\n }\n }\n };\n\n while (index < lines.length) {\n const line = lines[index] ?? \"\";\n\n if (isRulesyncHeader(line)) {\n const footerIndex = findRulesyncFooterIndex(lines, index + 1);\n if (footerIndex !== -1) {\n collectBlockLines(index + 1, footerIndex);\n index = footerIndex + 1;\n continue;\n }\n const legacyEnd = skipLegacyRulesyncBlock(lines, index);\n collectBlockLines(index + 1, legacyEnd);\n index = legacyEnd;\n continue;\n }\n\n if (isRulesyncEntry(line)) {\n managed.push(line.trim());\n }\n index++;\n }\n\n return managed;\n};\n\nexport type GitignoreCommandOptions = {\n readonly targets?: string[];\n readonly features?: RulesyncFeatures;\n readonly verbose?: boolean;\n readonly silent?: boolean;\n};\n\nconst groupEntriesByDestination = ({\n entries,\n resolveDestination,\n}: {\n entries: ReadonlyArray<ResolvedGitignoreEntry>;\n resolveDestination: (target: ToolTarget, feature?: Feature | \"general\") => GitignoreDestination;\n}): { gitignore: string[]; gitattributes: string[] } => {\n const gitignore = new Set<string>();\n const gitattributes = new Set<string>();\n\n for (const entry of entries) {\n const selectedToolTargets = entry.target.filter(\n (target): target is ToolTarget => target !== \"common\",\n );\n const destinations = new Set<GitignoreDestination>();\n for (const target of selectedToolTargets) {\n if (entry.feature === \"general\") {\n destinations.add(resolveDestination(target));\n } else {\n destinations.add(resolveDestination(target, entry.feature));\n }\n }\n\n if (destinations.has(\"gitattributes\")) {\n gitattributes.add(entry.entry);\n }\n if (destinations.size === 0 || destinations.has(\"gitignore\")) {\n gitignore.add(entry.entry);\n }\n }\n\n return {\n gitignore: [...gitignore],\n gitattributes: [...gitattributes],\n };\n};\n\nexport const gitignoreCommand = async (\n logger: Logger,\n options?: GitignoreCommandOptions,\n): Promise<void> => {\n const gitignorePath = join(process.cwd(), \".gitignore\");\n const gitattributesPath = join(process.cwd(), \".gitattributes\");\n const config = await ConfigResolver.resolve(\n { verbose: options?.verbose, silent: options?.silent },\n { logger },\n );\n\n const resolvedEntries = resolveGitignoreEntries({\n targets: options?.targets,\n features: options?.features,\n logger,\n });\n const { gitignore: gitignoreEntries, gitattributes: gitattributesEntries } =\n groupEntriesByDestination({\n entries: resolvedEntries,\n resolveDestination: (target, feature) => {\n if (feature === undefined || feature === \"general\") {\n return config.getGitignoreDestination(target);\n }\n return config.getGitignoreDestination(target, feature);\n },\n });\n\n const updateRulesyncFile = async ({\n filePath,\n entries,\n }: {\n filePath: string;\n entries: string[];\n }): Promise<{\n updated: boolean;\n alreadyExistedEntries: string[];\n entriesToAdd: string[];\n entriesRemoved: string[];\n }> => {\n let content = \"\";\n if (await fileExists(filePath)) {\n content = await readFileContent(filePath);\n }\n const cleanedContent = removeExistingRulesyncEntries(content);\n const entrySet = new Set(entries);\n const entriesRemoved = [\n ...new Set(extractRulesyncManagedEntries(content).filter((entry) => !entrySet.has(entry))),\n ];\n\n const existingEntries = new Set(\n content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line !== \"\" && !isRulesyncHeader(line) && !isRulesyncFooter(line)),\n );\n const alreadyExistedEntries = entries.filter((entry) => existingEntries.has(entry));\n const entriesToAdd = entries.filter((entry) => !existingEntries.has(entry));\n const rulesyncBlock = [RULESYNC_HEADER, ...entries, RULESYNC_FOOTER].join(\"\\n\");\n const newContent =\n entries.length === 0\n ? cleanedContent.trim()\n ? `${cleanedContent.trimEnd()}\\n`\n : \"\"\n : cleanedContent.trim()\n ? `${cleanedContent.trimEnd()}\\n\\n${rulesyncBlock}\\n`\n : `${rulesyncBlock}\\n`;\n\n if (content === newContent) {\n return { updated: false, alreadyExistedEntries, entriesToAdd: [], entriesRemoved: [] };\n }\n await writeFileContent(filePath, newContent);\n return { updated: true, alreadyExistedEntries, entriesToAdd, entriesRemoved };\n };\n\n const gitignoreResult = await updateRulesyncFile({\n filePath: gitignorePath,\n entries: gitignoreEntries,\n });\n const gitattributesResult = await updateRulesyncFile({\n filePath: gitattributesPath,\n entries: gitattributesEntries,\n });\n\n if (!gitignoreResult.updated && !gitattributesResult.updated) {\n // Capture JSON data if in JSON mode\n if (logger.jsonMode) {\n logger.captureData(\"entriesAdded\", []);\n logger.captureData(\"gitignorePath\", gitignorePath);\n logger.captureData(\"gitattributesPath\", gitattributesPath);\n logger.captureData(\"alreadyExisted\", [...gitignoreEntries, ...gitattributesEntries]);\n }\n logger.success(\".gitignore / .gitattributes are already up to date\");\n return;\n }\n\n // Capture JSON data if in JSON mode\n if (logger.jsonMode) {\n logger.captureData(\"entriesAdded\", [\n ...gitignoreResult.entriesToAdd,\n ...gitattributesResult.entriesToAdd,\n ]);\n logger.captureData(\"gitignorePath\", gitignorePath);\n logger.captureData(\"gitattributesPath\", gitattributesPath);\n logger.captureData(\"alreadyExisted\", [\n ...gitignoreResult.alreadyExistedEntries,\n ...gitattributesResult.alreadyExistedEntries,\n ]);\n logger.captureData(\"entriesRemoved\", gitignoreResult.entriesRemoved);\n }\n\n if (gitignoreResult.entriesRemoved.length > 0) {\n logger.warn(\n \"The following entries were removed from the rulesync-managed block in .gitignore and are no longer gitignored by rulesync:\",\n );\n for (const entry of gitignoreResult.entriesRemoved) {\n logger.warn(` ${entry}`);\n }\n logger.warn(\n \"Review these paths before committing — user-managed settings files may contain secrets.\",\n );\n }\n\n if (gitignoreResult.updated) {\n logger.success(\"Updated .gitignore with rulesync entries:\");\n } else {\n logger.success(\".gitignore is already up to date\");\n }\n for (const entry of gitignoreEntries) {\n logger.info(` ${entry}`);\n }\n if (gitattributesEntries.length > 0) {\n if (gitattributesResult.updated) {\n logger.success(\"Updated .gitattributes with rulesync entries:\");\n } else {\n logger.success(\".gitattributes is already up to date\");\n }\n for (const entry of gitattributesEntries) {\n logger.info(` ${entry}`);\n }\n }\n\n logger.info(\"\");\n logger.info(\n \"💡 If you're using Google Antigravity, note that rules, workflows, and skills won't load if they're gitignored.\",\n );\n logger.info(\" You can add the following to .git/info/exclude instead:\");\n logger.info(\" **/.agents/rules/\");\n logger.info(\" **/.agents/workflows/\");\n logger.info(\" **/.agents/skills/\");\n logger.info(\" For more details: https://github.com/dyoshikawa/rulesync/issues/981\");\n};\n","import { ConfigResolver, ConfigResolverResolveParams } from \"../../config/config-resolver.js\";\nimport { importFromTool } from \"../../lib/import.js\";\nimport { CLIError, ErrorCodes } from \"../../types/json-output.js\";\nimport type { Logger } from \"../../utils/logger.js\";\nimport { calculateTotalCount } from \"../../utils/result.js\";\n\n// `inputRoot` is intentionally excluded: it only affects where source rules\n// are *read from* during `generate`, and `import` does not consume them. Keeping\n// it in the option type would be misleading. Note that this avoids surfacing\n// the \"Ignoring `global: true`\" warning on direct programmatic / CLI callers;\n// users with an `inputRoot` set in their config file (e.g. `rulesync.jsonc`) may\n// still see the warning because `ConfigResolver.resolve` reads `configByFile`\n// regardless of this `Omit`. That residual warning is actionable — it tells\n// the user their config-file `inputRoot` is being ignored during `import`.\nexport type ImportOptions = Omit<ConfigResolverResolveParams, \"delete\" | \"inputRoot\">;\n\nexport async function importCommand(logger: Logger, options: ImportOptions): Promise<void> {\n if (!options.targets) {\n throw new CLIError(\"No tools found in --targets\", ErrorCodes.IMPORT_FAILED);\n }\n\n // The CLI only provides the array form for --targets; the object form is\n // config-file-only. Defend with a runtime check so TS can narrow safely.\n if (!Array.isArray(options.targets)) {\n throw new CLIError(\n \"--targets object form is not supported on the command line\",\n ErrorCodes.IMPORT_FAILED,\n );\n }\n\n if (options.targets.length > 1) {\n throw new CLIError(\"Only one tool can be imported at a time\", ErrorCodes.IMPORT_FAILED);\n }\n\n const config = await ConfigResolver.resolve(options, { logger });\n\n const tool = config.getTargets()[0]!;\n\n logger.debug(`Importing files from ${tool}...`);\n\n const result = await importFromTool({ config, tool, logger });\n\n const totalImported = calculateTotalCount(result);\n\n if (totalImported === 0) {\n const enabledFeatures = config.getFeatures().join(\", \");\n logger.warn(`No files imported for enabled features: ${enabledFeatures}`);\n return;\n }\n\n // Capture JSON data if in JSON mode\n if (logger.jsonMode) {\n logger.captureData(\"tool\", tool);\n logger.captureData(\"features\", {\n rules: { count: result.rulesCount },\n ignore: { count: result.ignoreCount },\n mcp: { count: result.mcpCount },\n commands: { count: result.commandsCount },\n subagents: { count: result.subagentsCount },\n skills: { count: result.skillsCount },\n hooks: { count: result.hooksCount },\n permissions: { count: result.permissionsCount },\n checks: { count: result.checksCount },\n });\n logger.captureData(\"totalFiles\", totalImported);\n }\n\n const parts = [];\n if (result.rulesCount > 0) parts.push(`${result.rulesCount} rules`);\n if (result.ignoreCount > 0) parts.push(`${result.ignoreCount} ignore files`);\n if (result.mcpCount > 0) parts.push(`${result.mcpCount} MCP files`);\n if (result.commandsCount > 0) parts.push(`${result.commandsCount} commands`);\n if (result.subagentsCount > 0) parts.push(`${result.subagentsCount} subagents`);\n if (result.skillsCount > 0) parts.push(`${result.skillsCount} skills`);\n if (result.hooksCount > 0) parts.push(`${result.hooksCount} hooks`);\n if (result.permissionsCount > 0) parts.push(`${result.permissionsCount} permissions`);\n if (result.checksCount > 0) parts.push(`${result.checksCount} checks`);\n\n logger.success(`Imported ${totalImported} file(s) total (${parts.join(\" + \")})`);\n}\n","import { dirname } from \"node:path\";\n\nimport { ConfigFile } from \"../config/config.js\";\nimport {\n RULESYNC_CONFIG_RELATIVE_FILE_PATH,\n RULESYNC_CONFIG_SCHEMA_URL,\n} from \"../constants/rulesync-paths.js\";\nimport { ensureDir, fileExists, writeFileContent } from \"../utils/file.js\";\nimport { createFeatureScaffold } from \"./feature-scaffold.js\";\n\ntype InitFileResult = {\n created: boolean;\n path: string;\n};\n\nexport type InitResult = {\n configFile: InitFileResult;\n sampleFiles: InitFileResult[];\n};\n\n/**\n * Initialize rulesync configuration and sample files.\n * This is the core logic without CLI-specific logging.\n */\nexport async function init(): Promise<InitResult> {\n const sampleFiles = await createSampleFiles();\n const configFile = await createConfigFile();\n\n return {\n configFile,\n sampleFiles,\n };\n}\n\nasync function createConfigFile(): Promise<InitFileResult> {\n const path = RULESYNC_CONFIG_RELATIVE_FILE_PATH;\n\n if (await fileExists(path)) {\n return { created: false, path };\n }\n\n await writeFileContent(\n path,\n JSON.stringify(\n {\n $schema: RULESYNC_CONFIG_SCHEMA_URL,\n targets: [\"codexcli\", \"claudecode\", \"opencode\"],\n features: [\"rules\", \"mcp\", \"subagents\", \"skills\", \"hooks\", \"permissions\"],\n outputRoots: [\".\"],\n delete: true,\n verbose: false,\n silent: false,\n global: false,\n simulateCommands: false,\n simulateSubagents: false,\n simulateSkills: false,\n gitignoreTargetsOnly: true,\n } satisfies ConfigFile,\n null,\n 2,\n ),\n );\n\n return { created: true, path };\n}\n\nasync function createSampleFiles(): Promise<InitFileResult[]> {\n const samples = [\n createFeatureScaffold({ feature: \"rule\", name: \"overview\" }),\n createFeatureScaffold({ feature: \"mcp\" }),\n createFeatureScaffold({ feature: \"subagent\", name: \"planner\" }),\n createFeatureScaffold({ feature: \"skill\", name: \"project-context\" }),\n createFeatureScaffold({ feature: \"hooks\" }),\n createFeatureScaffold({ feature: \"permissions\" }),\n ];\n\n const results: InitFileResult[] = [];\n for (const sample of samples) {\n await ensureDir(dirname(sample.relativeFilePath));\n results.push(\n await writeIfNotExists({\n path: sample.relativeFilePath,\n candidatePaths: sample.candidateRelativeFilePaths,\n content: sample.content,\n }),\n );\n }\n return results;\n}\n\nasync function writeIfNotExists({\n path,\n candidatePaths,\n content,\n}: {\n path: string;\n candidatePaths: string[];\n content: string;\n}): Promise<InitFileResult> {\n for (const candidatePath of candidatePaths) {\n if (await fileExists(candidatePath)) {\n return { created: false, path: candidatePath };\n }\n }\n\n await writeFileContent(path, content);\n return { created: true, path };\n}\n","import { SKILL_FILE_NAME } from \"../../constants/general.js\";\nimport {\n RULESYNC_HOOKS_RELATIVE_FILE_PATH,\n RULESYNC_MCP_RELATIVE_FILE_PATH,\n RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH,\n RULESYNC_RELATIVE_DIR_PATH,\n} from \"../../constants/rulesync-paths.js\";\nimport { init } from \"../../lib/init.js\";\nimport { ensureDir } from \"../../utils/file.js\";\nimport type { Logger } from \"../../utils/logger.js\";\n\nexport async function initCommand(logger: Logger): Promise<void> {\n logger.debug(\"Initializing rulesync...\");\n\n await ensureDir(RULESYNC_RELATIVE_DIR_PATH);\n\n const result = await init();\n\n // Log sample file results\n const createdFiles: string[] = [];\n const skippedFiles: string[] = [];\n\n for (const file of result.sampleFiles) {\n if (file.created) {\n createdFiles.push(file.path);\n logger.success(`Created ${file.path}`);\n } else {\n skippedFiles.push(file.path);\n logger.info(`Skipped ${file.path} (already exists)`);\n }\n }\n\n // Log config file result\n if (result.configFile.created) {\n createdFiles.push(result.configFile.path);\n logger.success(`Created ${result.configFile.path}`);\n } else {\n skippedFiles.push(result.configFile.path);\n logger.info(`Skipped ${result.configFile.path} (already exists)`);\n }\n\n // Capture JSON data if in JSON mode\n if (logger.jsonMode) {\n logger.captureData(\"created\", createdFiles);\n logger.captureData(\"skipped\", skippedFiles);\n }\n\n logger.success(\"rulesync initialized successfully!\");\n logger.info(\"Next steps:\");\n logger.info(\n `1. Edit ${RULESYNC_RELATIVE_DIR_PATH}/**/*.md, ${RULESYNC_RELATIVE_DIR_PATH}/skills/*/${SKILL_FILE_NAME}, ${RULESYNC_MCP_RELATIVE_FILE_PATH}, ${RULESYNC_HOOKS_RELATIVE_FILE_PATH} and ${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}`,\n );\n logger.info(\"2. Run 'rulesync generate' to create configuration files\");\n}\n","import { join } from \"node:path\";\n\nimport { dump } from \"js-yaml\";\nimport { nonnegative, optional, refine, z } from \"zod/mini\";\n\nimport { fileExists, readFileContent, writeFileContent } from \"../../utils/file.js\";\nimport { loadYaml } from \"../../utils/yaml.js\";\n\n/**\n * Filename of the rulesync-managed apm-compatible lockfile. Rulesync uses a\n * lockfile name distinct from the upstream `apm` CLI's `apm.lock.yaml` so the\n * two tools do not fight over the same file: the schema is still the apm v1\n * lockfile format, but rulesync only reads/writes its own file.\n */\nconst APM_LOCKFILE_FILE_NAME = \"rulesync-apm.lock.yaml\";\nexport const APM_LOCKFILE_VERSION = \"1\" as const;\n\n/**\n * Shape of content_hash values that rulesync writes. Used by `--frozen`\n * integrity checks to decide whether a prior hash is comparable: any value\n * not matching this regex (e.g. written by the upstream `apm` CLI) is\n * skipped rather than throwing so that cross-tool interop works.\n */\nexport const RULESYNC_CONTENT_HASH_REGEX = /^sha256:[0-9a-f]{64}$/;\n\n/**\n * Single dependency entry in `rulesync-apm.lock.yaml`. Mirrors the subset of the\n * APM v1 lockfile schema that rulesync currently populates. Extra fields\n * from the spec (content_hash, is_dev, virtual_path, ...) are preserved\n * verbatim so that rulesync does not strip them out when re-writing a\n * lockfile produced by `apm` itself.\n */\nconst ApmLockDependencySchema = z.looseObject({\n repo_url: z.string(),\n resolved_commit: optional(\n z\n .string()\n .check(refine((v) => /^[0-9a-f]{40}$/.test(v), \"resolved_commit must be a 40-char hex SHA\")),\n ),\n resolved_ref: optional(z.string()),\n version: optional(z.string()),\n depth: z.int().check(nonnegative()),\n resolved_by: optional(z.string()),\n package_type: z.string(),\n // Intentionally loose: the upstream `apm` CLI may write content_hash values\n // that do not match the strict rulesync format. We accept any string on read\n // so that a lockfile produced by `apm` round-trips through rulesync without\n // throwing. Rulesync itself always writes values matching\n // `RULESYNC_CONTENT_HASH_REGEX`, and `--frozen` integrity checks only\n // enforce the comparison when the recorded hash matches that shape.\n content_hash: optional(z.string()),\n is_dev: optional(z.boolean()),\n deployed_files: z.array(z.string()),\n source: optional(z.string()),\n local_path: optional(z.string()),\n virtual_path: optional(z.string()),\n is_virtual: optional(z.boolean()),\n});\nexport type ApmLockDependency = z.infer<typeof ApmLockDependencySchema>;\n\nconst ApmLockSchema = z.looseObject({\n lockfile_version: z.literal(\"1\"),\n generated_at: z.string(),\n apm_version: z.string(),\n dependencies: z.array(ApmLockDependencySchema),\n mcp_servers: optional(z.array(z.string())),\n});\nexport type ApmLock = z.infer<typeof ApmLockSchema>;\n\nexport function getApmLockPath(projectRoot: string): string {\n return join(projectRoot, APM_LOCKFILE_FILE_NAME);\n}\n\n/**\n * Create an empty lockfile structure. `apm_version` is set to the rulesync\n * compatibility-marker string so downstream tooling can tell this lockfile\n * was produced by rulesync rather than the upstream `apm` CLI.\n *\n * When `existingLock` is provided, all top-level fields from that lock (e.g.\n * `mcp_servers` and any looseObject extras written by the upstream `apm`\n * CLI) are carried forward. `dependencies` is always reset to an empty array\n * and `generated_at` is refreshed; `apm_version` is overwritten by the value\n * passed in `params.apmVersion`.\n */\nexport function createEmptyApmLock(params: {\n apmVersion: string;\n existingLock?: ApmLock | null;\n}): ApmLock {\n const base = params.existingLock ? { ...params.existingLock } : {};\n return {\n ...base,\n lockfile_version: APM_LOCKFILE_VERSION,\n generated_at: new Date().toISOString(),\n apm_version: params.apmVersion,\n dependencies: [],\n };\n}\n\n/**\n * Parse `rulesync-apm.lock.yaml` content into an `ApmLock`. Returns `null` when the\n * content is absent / empty / non-YAML-object so callers can treat the lock\n * as missing. A *structurally* present lockfile that fails schema validation\n * throws a descriptive error rather than being silently dropped — silently\n * discarding a corrupt lockfile would erase previously pinned commits.\n */\nexport function parseApmLock(content: string): ApmLock | null {\n if (!content.trim()) {\n return null;\n }\n let loaded: unknown;\n try {\n loaded = loadYaml(content);\n } catch {\n return null;\n }\n if (!loaded || typeof loaded !== \"object\") {\n return null;\n }\n const parsed = ApmLockSchema.safeParse(loaded);\n if (!parsed.success) {\n const issues = parsed.error.issues\n .map((issue) => ` - ${issue.path.join(\".\") || \"<root>\"}: ${issue.message}`)\n .join(\"\\n\");\n throw new Error(`Invalid ${APM_LOCKFILE_FILE_NAME}:\\n${issues}`);\n }\n return parsed.data;\n}\n\nexport async function readApmLock(projectRoot: string): Promise<ApmLock | null> {\n const path = getApmLockPath(projectRoot);\n if (!(await fileExists(path))) {\n return null;\n }\n const content = await readFileContent(path);\n return parseApmLock(content);\n}\n\nexport async function writeApmLock(params: { projectRoot: string; lock: ApmLock }): Promise<void> {\n const path = getApmLockPath(params.projectRoot);\n const content = serializeApmLock(params.lock);\n await writeFileContent(path, content);\n}\n\nexport function serializeApmLock(lock: ApmLock): string {\n // `noRefs: true` avoids YAML anchors/aliases; `lineWidth: -1` keeps long\n // URLs on a single line so the file stays diff-friendly.\n return dump(lock, { noRefs: true, lineWidth: -1, sortKeys: false });\n}\n\n/**\n * Find the locked entry for a repo_url. GitHub routes `owner/repo` path\n * components case-insensitively, so the comparison here is case-insensitive\n * to match `apm-manifest.ts` canonicalization and avoid frozen-mode false\n * positives when users re-case their manifest.\n */\nexport function findApmLockDependency(\n lock: ApmLock,\n repoUrl: string,\n): ApmLockDependency | undefined {\n const target = repoUrl.toLowerCase();\n return lock.dependencies.find((d) => d.repo_url.toLowerCase() === target);\n}\n","import { join } from \"node:path\";\n\nimport { optional, z } from \"zod/mini\";\n\nimport { fileExists, readFileContent } from \"../../utils/file.js\";\nimport { loadYaml } from \"../../utils/yaml.js\";\n\nconst APM_MANIFEST_FILE_NAME = \"apm.yml\";\n\n/**\n * Parsed representation of a single APM `dependencies.apm` entry after\n * normalization. Every accepted input form (string shorthand, object form,\n * HTTPS URL) lands here.\n */\nexport type ApmDependency = {\n /** Canonical git URL. Always an HTTPS URL for the first iteration. */\n gitUrl: string;\n /** GitHub owner (extracted for use with the REST client). */\n owner: string;\n /** GitHub repo. */\n repo: string;\n /**\n * Optional ref (tag, branch, or commit SHA). Absent means \"resolve against\n * the repository's default branch\".\n */\n ref?: string;\n /**\n * Optional virtual sub-directory within the repository. When present the\n * install layout is rooted at this path.\n */\n path?: string;\n /**\n * Optional alias used to override the local install directory name.\n */\n alias?: string;\n};\n\nconst ApmObjectDependencySchema = z.looseObject({\n git: optional(z.string()),\n source: optional(z.string()),\n path: optional(z.string()),\n ref: optional(z.string()),\n alias: optional(z.string()),\n});\n\nconst ApmDependencyInputSchema = z.union([z.string(), ApmObjectDependencySchema]);\n\nconst ApmManifestSchema = z.looseObject({\n name: optional(z.string()),\n version: optional(z.string()),\n dependencies: optional(\n z.looseObject({\n apm: optional(z.array(ApmDependencyInputSchema)),\n }),\n ),\n});\n\nexport type ApmManifest = {\n name?: string;\n version?: string;\n dependencies: ApmDependency[];\n};\n\n/**\n * Return the absolute path to the project's `apm.yml`.\n */\nexport function getApmManifestPath(projectRoot: string): string {\n return join(projectRoot, APM_MANIFEST_FILE_NAME);\n}\n\n/**\n * True if `apm.yml` exists at the given base directory.\n */\nexport async function apmManifestExists(projectRoot: string): Promise<boolean> {\n return fileExists(getApmManifestPath(projectRoot));\n}\n\n/**\n * Parse `apm.yml` content. Throws with a descriptive error when parsing\n * or any dependency entry fails normalization.\n */\nexport function parseApmManifest(content: string): ApmManifest {\n const loaded = loadYaml(content);\n if (loaded === undefined || loaded === null) {\n return { dependencies: [] };\n }\n const parsed = ApmManifestSchema.safeParse(loaded);\n if (!parsed.success) {\n throw new Error(`Invalid apm.yml: ${parsed.error.message}`);\n }\n const raw = parsed.data;\n const rawDeps = raw.dependencies?.apm ?? [];\n const dependencies: ApmDependency[] = rawDeps.map((entry, index) =>\n normalizeDependency(entry, index),\n );\n return {\n name: raw.name,\n version: raw.version,\n dependencies,\n };\n}\n\n/**\n * Read and parse `apm.yml` from disk.\n */\nexport async function readApmManifest(projectRoot: string): Promise<ApmManifest> {\n const path = getApmManifestPath(projectRoot);\n const content = await readFileContent(path);\n return parseApmManifest(content);\n}\n\nfunction normalizeDependency(\n entry: string | z.infer<typeof ApmObjectDependencySchema>,\n index: number,\n): ApmDependency {\n if (typeof entry === \"string\") {\n return normalizeStringDependency(entry, index);\n }\n const gitUrl = entry.git ?? entry.source;\n if (!gitUrl) {\n throw new Error(\n `apm.yml dependency #${index + 1}: object form requires a \"git\" field. Received: ${JSON.stringify(entry)}.`,\n );\n }\n const parsedUrl = parseHttpsGitHubUrl(gitUrl);\n if (!parsedUrl) {\n throw new Error(\n `apm.yml dependency #${index + 1}: unsupported git URL \"${gitUrl}\". Only HTTPS GitHub URLs (https://github.com/owner/repo[.git]) are supported in this version. SSH, GitLab, Bitbucket, and other hosts are not yet supported.`,\n );\n }\n if (entry.path !== undefined) {\n validateSubPath(entry.path, index);\n }\n return {\n gitUrl: parsedUrl.gitUrl,\n owner: parsedUrl.owner,\n repo: parsedUrl.repo,\n ref: entry.ref,\n path: entry.path,\n alias: entry.alias,\n };\n}\n\n/**\n * Reject `dep.path` values that could escape the repository root or be\n * interpreted as an absolute path on the remote tree.\n */\nfunction validateSubPath(subPath: string, index: number): void {\n if (subPath === \"\" || subPath.startsWith(\"/\") || subPath.startsWith(\"\\\\\")) {\n throw new Error(\n `apm.yml dependency #${index + 1}: \"path\" must be a non-empty relative path without a leading slash. Received: ${JSON.stringify(subPath)}.`,\n );\n }\n const segments = subPath.split(/[/\\\\]/);\n if (segments.includes(\"..\")) {\n throw new Error(\n `apm.yml dependency #${index + 1}: \"path\" must not contain \"..\" segments. Received: ${JSON.stringify(subPath)}.`,\n );\n }\n}\n\nfunction normalizeStringDependency(entry: string, index: number): ApmDependency {\n const trimmed = entry.trim();\n if (!trimmed) {\n throw new Error(`apm.yml dependency #${index + 1}: entry must be a non-empty string.`);\n }\n rejectUnsupportedShorthand(trimmed, index);\n\n if (trimmed.startsWith(\"https://\")) {\n const [urlPart, refPart] = splitOnFirst(trimmed, \"#\");\n const parsed = parseHttpsGitHubUrl(urlPart);\n if (!parsed) {\n throw new Error(\n `apm.yml dependency #${index + 1}: unsupported URL \"${urlPart}\". Only HTTPS GitHub URLs (https://github.com/owner/repo[.git]) are supported in this version.`,\n );\n }\n return {\n gitUrl: parsed.gitUrl,\n owner: parsed.owner,\n repo: parsed.repo,\n ref: refPart || undefined,\n };\n }\n\n const [ownerRepo, refPart] = splitOnFirst(trimmed, \"#\");\n const slashIndex = ownerRepo.indexOf(\"/\");\n if (slashIndex === -1 || slashIndex === 0 || slashIndex === ownerRepo.length - 1) {\n throw new Error(\n `apm.yml dependency #${index + 1}: shorthand \"${entry}\" must be in the form \"owner/repo[#ref]\".`,\n );\n }\n if (ownerRepo.includes(\"/\", slashIndex + 1)) {\n throw new Error(\n `apm.yml dependency #${index + 1}: FQDN shorthand or sub-path shorthand (\"${entry}\") is not yet supported. Use the object form with an explicit \"git\" URL.`,\n );\n }\n // Canonicalize owner/repo to lower-case for case-insensitive matching.\n const owner = ownerRepo.substring(0, slashIndex).toLowerCase();\n const repo = ownerRepo.substring(slashIndex + 1).toLowerCase();\n return {\n gitUrl: `https://github.com/${owner}/${repo}.git`,\n owner,\n repo,\n ref: refPart || undefined,\n };\n}\n\nfunction rejectUnsupportedShorthand(entry: string, index: number): void {\n if (entry.startsWith(\"./\") || entry.startsWith(\"../\") || entry.startsWith(\"/\")) {\n throw new Error(\n `apm.yml dependency #${index + 1}: local path dependencies (\"${entry}\") are not yet supported by rulesync.`,\n );\n }\n if (entry.startsWith(\"git@\") || entry.startsWith(\"ssh://\")) {\n throw new Error(\n `apm.yml dependency #${index + 1}: SSH URL dependencies (\"${entry}\") are not yet supported. Use an HTTPS GitHub URL.`,\n );\n }\n if (entry.includes(\"@marketplace\")) {\n throw new Error(\n `apm.yml dependency #${index + 1}: APM marketplace dependencies (\"${entry}\") are not yet supported.`,\n );\n }\n}\n\nfunction parseHttpsGitHubUrl(url: string): { gitUrl: string; owner: string; repo: string } | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n const host = parsed.hostname.toLowerCase();\n if (host !== \"github.com\" && host !== \"www.github.com\") {\n return null;\n }\n const segments = parsed.pathname.split(\"/\").filter(Boolean);\n if (segments.length < 2) {\n return null;\n }\n const rawOwner = segments[0];\n const rawRepo = segments[1];\n if (!rawOwner || !rawRepo) {\n return null;\n }\n // GitHub treats owner/repo names case-insensitively for routing. Canonicalize\n // to lower-case so that lockfile comparisons and frozen-mode checks are not\n // tripped up by a user re-casing their manifest.\n const owner = rawOwner.toLowerCase();\n const repo = rawRepo.replace(/\\.git$/, \"\").toLowerCase();\n return {\n gitUrl: `https://github.com/${owner}/${repo}.git`,\n owner,\n repo,\n };\n}\n\nfunction splitOnFirst(input: string, separator: string): [string, string | undefined] {\n const idx = input.indexOf(separator);\n if (idx === -1) return [input, undefined];\n return [input.substring(0, idx), input.substring(idx + 1)];\n}\n","import { createHash } from \"node:crypto\";\nimport { join, posix } from \"node:path\";\n\nimport { Semaphore } from \"es-toolkit/promise\";\n\nimport { FETCH_CONCURRENCY_LIMIT, MAX_FILE_SIZE } from \"../../constants/rulesync-paths.js\";\nimport type { GitHubFileEntry } from \"../../types/fetch.js\";\nimport { formatError } from \"../../utils/error.js\";\nimport { checkPathTraversal, removeFile, toPosixPath, writeFileContent } from \"../../utils/file.js\";\nimport type { Logger } from \"../../utils/logger.js\";\nimport { GitHubClient, GitHubClientError, logGitHubAuthHints } from \"../github-client.js\";\nimport { listDirectoryRecursive, withSemaphore } from \"../github-utils.js\";\nimport {\n type ApmLock,\n type ApmLockDependency,\n createEmptyApmLock,\n findApmLockDependency,\n readApmLock,\n RULESYNC_CONTENT_HASH_REGEX,\n writeApmLock,\n} from \"./apm-lock.js\";\nimport { type ApmDependency, readApmManifest } from \"./apm-manifest.js\";\n\n/** APM compatibility marker written into `apm_version` when rulesync writes a lockfile. */\nconst RULESYNC_APM_COMPAT_VERSION = \"rulesync-compat/0.1\";\n\n/**\n * Primitives the first iteration deploys. Ordered by scan priority.\n * Each entry maps a package-relative source directory (rooted at the\n * dependency's `path` if given, else the repo root) to the on-disk\n * deployment directory. This matches the default APM layout when the\n * github-copilot host is present.\n */\nconst APM_PRIMITIVES: Array<{ sourceDir: string; deployDir: string; packageType: string }> = [\n {\n sourceDir: \".apm/instructions\",\n deployDir: \".github/instructions\",\n packageType: \"apm_package\",\n },\n {\n sourceDir: \".apm/skills\",\n deployDir: \".github/skills\",\n packageType: \"apm_package\",\n },\n];\n\nexport type ApmInstallOptions = {\n /** Force re-resolve all refs, ignoring the lockfile. */\n update?: boolean;\n /** Fail if the lockfile is missing or out of sync (for CI). */\n frozen?: boolean;\n /** GitHub token for private repositories. */\n token?: string;\n};\n\nexport type ApmInstallResult = {\n dependenciesProcessed: number;\n deployedFileCount: number;\n failedDependencyCount: number;\n};\n\n/**\n * Entry point for `rulesync install --mode apm`. Reads `apm.yml`, resolves\n * every declared APM dependency, fetches the subset of primitives rulesync\n * currently understands (Instructions and Skills), and updates `rulesync-apm.lock.yaml`.\n */\nexport async function installApm(params: {\n projectRoot: string;\n options?: ApmInstallOptions;\n logger: Logger;\n}): Promise<ApmInstallResult> {\n const { projectRoot, options = {}, logger } = params;\n\n const manifest = await readApmManifest(projectRoot);\n if (manifest.dependencies.length === 0) {\n logger.warn(\"apm.yml has no dependencies.apm entries. Nothing to install.\");\n return { dependenciesProcessed: 0, deployedFileCount: 0, failedDependencyCount: 0 };\n }\n\n const existingLock = await readApmLock(projectRoot);\n if (options.frozen) {\n assertFrozenLockCoversManifest({ existingLock, dependencies: manifest.dependencies });\n }\n\n const token = GitHubClient.resolveToken(options.token);\n const client = new GitHubClient({ token });\n const semaphore = new Semaphore(FETCH_CONCURRENCY_LIMIT);\n\n const newLock: ApmLock = createEmptyApmLock({\n apmVersion: existingLock?.apm_version ?? RULESYNC_APM_COMPAT_VERSION,\n existingLock,\n });\n\n // Dependencies are independent, so install them in parallel. The within-dep\n // tree walk is already rate-limited by the shared FETCH_CONCURRENCY_LIMIT\n // semaphore, so top-level parallelism is bounded naturally.\n //\n // Semantics:\n // frozen=true — any failure aborts the whole install (Promise.all\n // rejects on the first rejection).\n // frozen=false — each dep's promise resolves to a result object so that\n // one failing dep does not abort the others.\n type DepResult =\n | { status: \"ok\"; lockEntry: ApmLockDependency; deployedCount: number }\n | { status: \"failed\"; previous: ApmLockDependency | undefined };\n\n const frozen = options.frozen ?? false;\n\n const runOne = async (dep: ApmDependency): Promise<DepResult> => {\n const installed = await installDependency({\n dep,\n client,\n semaphore,\n projectRoot,\n existingLock,\n frozen,\n update: options.update ?? false,\n logger,\n });\n return {\n status: \"ok\",\n lockEntry: installed.lockEntry,\n deployedCount: installed.deployedFiles.length,\n };\n };\n\n const results: DepResult[] = frozen\n ? await Promise.all(manifest.dependencies.map(runOne))\n : await Promise.all(\n manifest.dependencies.map(async (dep): Promise<DepResult> => {\n try {\n return await runOne(dep);\n } catch (error) {\n logger.error(`Failed to install apm dependency \"${dep.gitUrl}\": ${formatError(error)}`);\n if (error instanceof GitHubClientError) {\n logGitHubAuthHints({ error, logger });\n }\n // Preserve the prior lock entry for failed deps so that a\n // transient network error does not destroy a previously pinned\n // commit SHA. We return it rather than pushing here so that the\n // post-loop pushes preserved entries in manifest order, not in\n // promise-completion order.\n const previous = existingLock\n ? findApmLockDependency(existingLock, canonicalRepoUrl(dep))\n : undefined;\n return { status: \"failed\", previous };\n }\n }),\n );\n\n let totalDeployed = 0;\n let failedCount = 0;\n // Iterate in manifest order to keep the lockfile deterministic regardless\n // of promise-completion timing.\n for (const result of results) {\n if (result.status === \"ok\") {\n newLock.dependencies.push(result.lockEntry);\n totalDeployed += result.deployedCount;\n } else {\n failedCount += 1;\n if (result.previous) {\n newLock.dependencies.push(result.previous);\n }\n }\n }\n\n // Remove files that were deployed by a previous install but are no longer\n // part of any current dependency's deployed_files. Without this, stale\n // artifacts would accumulate on disk forever as upstream content changes.\n //\n // SECURITY: `deployed_files` is only schema-validated as `z.array(z.string())`,\n // so a hostile lockfile (planted in a repo and processed by CI) could try\n // to make us `removeFile(\"../../etc/passwd\")`. We defense-in-depth guard\n // each entry: (a) reject absolute paths and `..` segments by shape, then\n // (b) run `checkPathTraversal` for the canonical check used on the write\n // path. Offending entries are skipped with a warn log rather than fatal so\n // that a single bad row cannot brick the install.\n if (existingLock) {\n await removeStaleApmFiles({ existingLock, newLock, projectRoot, logger });\n }\n\n // Always rewrite the lockfile (except under --frozen, which is a verify-only\n // mode). Even on a partially successful install we persist the union of\n // newly pinned entries and preserved previous entries so that first-ever\n // runs with mixed results still record the successful pins.\n if (!frozen) {\n newLock.generated_at = new Date().toISOString();\n await writeApmLock({ projectRoot, lock: newLock });\n if (failedCount === 0) {\n logger.debug(\"rulesync-apm.lock.yaml updated.\");\n } else {\n logger.warn(\n `rulesync-apm.lock.yaml written with partially successful installs (${failedCount} dep(s) failed).`,\n );\n }\n }\n\n return {\n dependenciesProcessed: manifest.dependencies.length,\n deployedFileCount: totalDeployed,\n failedDependencyCount: failedCount,\n };\n}\n\n/**\n * Frozen-mode validation: the lockfile must exist, cover every manifest\n * dependency, and not have drifted from any declared `ref`. Throws with\n * remediation guidance on the first failing check (preserving the original\n * order: missing-lock, missing-entries, then ref drift).\n */\nfunction assertFrozenLockCoversManifest(params: {\n existingLock: ApmLock | null;\n dependencies: ApmDependency[];\n}): asserts params is { existingLock: ApmLock; dependencies: ApmDependency[] } {\n const { existingLock, dependencies } = params;\n if (!existingLock) {\n throw new Error(\n \"Frozen install failed: rulesync-apm.lock.yaml is missing. Run 'rulesync install --mode apm' to create it.\",\n );\n }\n const missing = dependencies.filter(\n (dep) => !findApmLockDependency(existingLock, canonicalRepoUrl(dep)),\n );\n if (missing.length > 0) {\n const names = missing.map((d) => d.gitUrl).join(\", \");\n throw new Error(\n `Frozen install failed: rulesync-apm.lock.yaml is missing entries for: ${names}. Run 'rulesync install --mode apm' to update the lockfile.`,\n );\n }\n // Detect manifest drift: when the user edited `ref` in apm.yml without\n // re-running install, the locked ref no longer matches the declared one.\n // In frozen mode we refuse rather than silently install the locked SHA.\n const drifted = dependencies.filter((dep) => {\n if (dep.ref === undefined) return false;\n const locked = findApmLockDependency(existingLock, canonicalRepoUrl(dep));\n return locked?.resolved_ref !== undefined && locked.resolved_ref !== dep.ref;\n });\n if (drifted.length > 0) {\n const names = drifted\n .map((d) => {\n const locked = findApmLockDependency(existingLock, canonicalRepoUrl(d));\n return `${d.gitUrl} (manifest=${d.ref}, lock=${locked?.resolved_ref})`;\n })\n .join(\", \");\n throw new Error(\n `Frozen install failed: manifest ref does not match rulesync-apm.lock.yaml for: ${names}. Run 'rulesync install --mode apm' to update the lockfile.`,\n );\n }\n}\n\n/**\n * Remove files that a previous install deployed but that are no longer part of\n * any current dependency's `deployed_files`. Each entry is path-traversal\n * hardened (shape check + `checkPathTraversal`) and offending rows are skipped\n * with a warn log rather than fatal.\n */\nasync function removeStaleApmFiles(params: {\n existingLock: ApmLock;\n newLock: ApmLock;\n projectRoot: string;\n logger: Logger;\n}): Promise<void> {\n const { existingLock, newLock, projectRoot, logger } = params;\n const newDeployedFiles = new Set(newLock.dependencies.flatMap((d) => d.deployed_files));\n const toDelete: string[] = [];\n for (const prev of existingLock.dependencies) {\n for (const deployed of prev.deployed_files) {\n if (!newDeployedFiles.has(deployed)) {\n toDelete.push(deployed);\n }\n }\n }\n for (const relativePath of toDelete) {\n if (posix.isAbsolute(relativePath) || relativePath.split(/[/\\\\]/).includes(\"..\")) {\n logger.warn(`Refusing to remove stale apm file with suspicious path: \"${relativePath}\".`);\n continue;\n }\n try {\n checkPathTraversal({ relativePath, intendedRootDir: projectRoot });\n } catch {\n logger.warn(`Refusing to remove stale apm file outside projectRoot: \"${relativePath}\".`);\n continue;\n }\n const absolute = join(projectRoot, relativePath);\n // `removeFile` is best-effort and swallows ENOENT, so missing files are\n // a no-op. This keeps a corrupted partial-install from blowing up here.\n await removeFile(absolute);\n logger.debug(`Removed stale apm file: ${relativePath}`);\n }\n}\n\nasync function installDependency(params: {\n dep: ApmDependency;\n client: GitHubClient;\n semaphore: Semaphore;\n projectRoot: string;\n existingLock: ApmLock | null;\n frozen: boolean;\n update: boolean;\n logger: Logger;\n}): Promise<{ lockEntry: ApmLockDependency; deployedFiles: string[] }> {\n const { dep, client, semaphore, projectRoot, existingLock, frozen, update, logger } = params;\n const repoUrl = canonicalRepoUrl(dep);\n const locked = existingLock ? findApmLockDependency(existingLock, repoUrl) : undefined;\n\n let resolvedRef: string;\n let resolvedSha: string;\n if (locked && !update && locked.resolved_commit && locked.resolved_ref) {\n resolvedRef = locked.resolved_ref;\n resolvedSha = locked.resolved_commit;\n logger.debug(`Using locked commit for ${repoUrl}: ${resolvedSha}`);\n } else {\n resolvedRef = dep.ref ?? (await client.getDefaultBranch(dep.owner, dep.repo));\n resolvedSha = await client.resolveRefToSha(dep.owner, dep.repo, resolvedRef);\n logger.debug(`Resolved ${repoUrl} ref \"${resolvedRef}\" -> ${resolvedSha}`);\n }\n\n // Collect (path, content) pairs before writing to disk. This lets us hash\n // them up-front and, under --frozen, refuse to overwrite good files with\n // tampered bytes. Under non-frozen we still write as we go for incremental\n // progress feedback on large dep trees.\n const deployed: Array<{ path: string; content: string }> = [];\n for (const primitive of APM_PRIMITIVES) {\n const remoteBase = dep.path\n ? toPosixPath(posix.join(dep.path, primitive.sourceDir))\n : primitive.sourceDir;\n const files = await listPrimitiveFiles({\n client,\n semaphore,\n owner: dep.owner,\n repo: dep.repo,\n ref: resolvedSha,\n remoteBase,\n logger,\n });\n if (files.length === 0) continue;\n\n await collectPrimitiveDeployments({\n dep,\n client,\n semaphore,\n projectRoot,\n primitive,\n remoteBase,\n files,\n resolvedSha,\n repoUrl,\n frozen,\n deployed,\n logger,\n });\n }\n\n deployed.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));\n const deployedFiles = deployed.map((d) => d.path);\n const contentHash = computeContentHash(deployed);\n\n assertFrozenContentHashMatches({ frozen, locked, contentHash, repoUrl, logger });\n\n // Under --frozen we deferred all writes until after the hash check passed.\n if (frozen) {\n for (const { path: deployRelative, content } of deployed) {\n await writeFileContent(join(projectRoot, deployRelative), content);\n }\n }\n\n const lockEntry: ApmLockDependency = {\n repo_url: repoUrl,\n resolved_commit: resolvedSha,\n resolved_ref: resolvedRef,\n depth: 1,\n package_type: \"apm_package\",\n content_hash: contentHash,\n deployed_files: deployedFiles,\n };\n if (dep.path) {\n lockEntry.virtual_path = dep.path;\n }\n\n logger.info(`Installed ${deployedFiles.length} file(s) from ${repoUrl}@${shortSha(resolvedSha)}`);\n\n return { lockEntry, deployedFiles };\n}\n\n/**\n * Fetch and validate the files for a single primitive directory, appending the\n * deployable (path, content) pairs to `deployed`. Oversized or out-of-bounds\n * files are skipped with a warn log; under non-frozen mode bytes are written to\n * disk as they are collected.\n */\nasync function collectPrimitiveDeployments(params: {\n dep: ApmDependency;\n client: GitHubClient;\n semaphore: Semaphore;\n projectRoot: string;\n primitive: (typeof APM_PRIMITIVES)[number];\n remoteBase: string;\n files: GitHubFileEntry[];\n resolvedSha: string;\n repoUrl: string;\n frozen: boolean;\n deployed: Array<{ path: string; content: string }>;\n logger: Logger;\n}): Promise<void> {\n const {\n dep,\n client,\n semaphore,\n projectRoot,\n primitive,\n remoteBase,\n files,\n resolvedSha,\n repoUrl,\n frozen,\n deployed,\n logger,\n } = params;\n\n for (const file of files) {\n if (file.size > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping \"${file.path}\" from ${repoUrl}: ${(file.size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit.`,\n );\n continue;\n }\n const relativeToBase = posix.relative(remoteBase, toPosixPath(file.path));\n if (!relativeToBase || relativeToBase.startsWith(\"..\") || posix.isAbsolute(relativeToBase)) {\n logger.warn(`Skipping \"${file.path}\" from ${repoUrl}: resolved outside of \"${remoteBase}\".`);\n continue;\n }\n const deployRelative = toPosixPath(join(primitive.deployDir, relativeToBase));\n checkPathTraversal({\n relativePath: deployRelative,\n intendedRootDir: projectRoot,\n });\n const content = await withSemaphore(semaphore, () =>\n client.getFileContent(dep.owner, dep.repo, file.path, resolvedSha),\n );\n // The tree-listing size can lie (LFS pointers, filter-driver output),\n // so enforce the cap on the fetched bytes as well.\n const byteLength = Buffer.byteLength(content, \"utf8\");\n if (byteLength > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping \"${file.path}\" from ${repoUrl}: fetched ${(byteLength / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit.`,\n );\n continue;\n }\n deployed.push({ path: deployRelative, content });\n if (!frozen) {\n await writeFileContent(join(projectRoot, deployRelative), content);\n }\n }\n}\n\n/**\n * Verify integrity against the lockfile when running frozen and the prior\n * lock recorded a hash rulesync itself wrote. A mismatch means either the\n * upstream content moved under the same SHA (unlikely with git) or someone\n * tampered with the lockfile / deployed files. We do this *before* writing\n * anything to disk under --frozen so that tampered bytes never hit the\n * filesystem.\n *\n * If the recorded hash does not match the rulesync format (e.g. the\n * lockfile was produced by the upstream `apm` CLI which writes a different\n * shape), we skip the integrity check rather than fail — the commit SHA\n * pin is still enforced, and this preserves interop for users migrating\n * from `apm` to `rulesync install --mode apm`.\n */\nfunction assertFrozenContentHashMatches(params: {\n frozen: boolean;\n locked: ApmLockDependency | undefined;\n contentHash: string;\n repoUrl: string;\n logger: Logger;\n}): void {\n const { frozen, locked, contentHash, repoUrl, logger } = params;\n if (frozen && locked?.content_hash) {\n if (RULESYNC_CONTENT_HASH_REGEX.test(locked.content_hash)) {\n if (locked.content_hash !== contentHash) {\n throw new Error(\n `content_hash mismatch for ${repoUrl}: lock=${locked.content_hash} computed=${contentHash}. Refuse to trust the deployment under --frozen.`,\n );\n }\n } else {\n logger.debug(\n `Skipping content_hash integrity check for ${repoUrl}: recorded hash \"${locked.content_hash}\" was not written by rulesync.`,\n );\n }\n }\n}\n\n/**\n * SHA-256 over a canonical, order-independent representation of the deployed\n * files. Written into `content_hash` so that `--frozen` installs can refuse\n * to trust tampered output.\n */\nfunction computeContentHash(files: Array<{ path: string; content: string }>): string {\n const hash = createHash(\"sha256\");\n for (const { path, content } of files) {\n hash.update(path);\n hash.update(\"\\0\");\n hash.update(content);\n hash.update(\"\\0\");\n }\n return `sha256:${hash.digest(\"hex\")}`;\n}\n\nasync function listPrimitiveFiles(params: {\n client: GitHubClient;\n semaphore: Semaphore;\n owner: string;\n repo: string;\n ref: string;\n remoteBase: string;\n logger: Logger;\n}): Promise<GitHubFileEntry[]> {\n const { client, semaphore, owner, repo, ref, remoteBase, logger } = params;\n try {\n return await listDirectoryRecursive({\n client,\n owner,\n repo,\n path: remoteBase,\n ref,\n semaphore,\n });\n } catch (error) {\n if (error instanceof GitHubClientError && error.statusCode === 404) {\n logger.debug(`No ${remoteBase}/ in ${owner}/${repo}, skipping.`);\n return [];\n }\n throw error;\n }\n}\n\n/**\n * Canonical repo_url written into the lockfile. We always use the HTTPS form\n * without a trailing `.git` so that lock files round-trip deterministically\n * regardless of whether the manifest referenced the repo with or without\n * the suffix.\n */\nfunction canonicalRepoUrl(dep: ApmDependency): string {\n return `https://github.com/${dep.owner}/${dep.repo}`;\n}\n\nfunction shortSha(sha: string): string {\n return sha.substring(0, 7);\n}\n","import { dump } from \"js-yaml\";\n\nimport { loadYaml } from \"../../utils/yaml.js\";\n\nconst FRONTMATTER_FENCE = \"---\";\n\n/**\n * Parses YAML frontmatter at the head of a SKILL.md, sets/overwrites the\n * three provenance keys (`source`, `repository`, `ref`), and re-serializes.\n *\n * If the file has no frontmatter block, a fresh one is prepended with only\n * the provenance keys + the original body. All other existing frontmatter\n * keys are preserved verbatim (the merge is a shallow object spread on the\n * loaded YAML).\n *\n * Throws `Error(\"invalid frontmatter\")` when an `---` fenced block exists\n * but its body is not a YAML object (e.g. malformed YAML, or a list/scalar\n * at the top level). Callers may choose to fall back to \"prepend fresh\" on\n * this error, but the function itself does not silently rewrite — silently\n * dropping a corrupted frontmatter could destroy user metadata.\n */\nexport function injectSourceMetadata(params: {\n content: string;\n source: string;\n repository: string;\n ref: string;\n}): string {\n const { content, source, repository, ref } = params;\n const provenance = { source, repository, ref };\n\n // Detect the opening fence in both LF (`---\\n`) and CRLF (`---\\r\\n`) forms.\n // SKILL.md files authored on Windows or by editors that preserve CRLF must\n // round-trip cleanly — without this branch the existing frontmatter would\n // be buried inside a fresh provenance block on the first install.\n let openFenceLen: number;\n if (content.startsWith(`${FRONTMATTER_FENCE}\\r\\n`)) {\n openFenceLen = 5;\n } else if (content.startsWith(`${FRONTMATTER_FENCE}\\n`)) {\n openFenceLen = 4;\n } else if (content === FRONTMATTER_FENCE) {\n openFenceLen = 3;\n } else {\n // No frontmatter block. Prepend a fresh one with just the provenance keys.\n const yaml = dump(provenance, { noRefs: true, lineWidth: -1, sortKeys: false });\n return `${FRONTMATTER_FENCE}\\n${yaml}${FRONTMATTER_FENCE}\\n${content}`;\n }\n\n // Body starts immediately after the opening fence.\n const afterOpen = content.substring(openFenceLen);\n\n // Closing fence forms we accept:\n // - `---` immediately at the start of the body (i.e. `---\\n---\\n...`,\n // which yields an empty frontmatter block).\n // - `\\n---` followed by a newline OR end-of-file (the trailing newline\n // after the closing `---` is optional so the fence may sit at EOF).\n let fmBody: string;\n let rest: string;\n if (afterOpen.startsWith(\"---\\n\") || afterOpen.startsWith(\"---\\r\\n\") || afterOpen === \"---\") {\n fmBody = \"\";\n const fenceLen = afterOpen.startsWith(\"---\\r\\n\") ? 5 : afterOpen === \"---\" ? 3 : 4;\n rest = afterOpen.substring(fenceLen);\n } else {\n const match = /\\n---(\\r?\\n|$)/.exec(afterOpen);\n if (!match) {\n // The file starts with `---\\n` but there is no closing `---` line. We\n // refuse to guess where the frontmatter ends; treat as invalid so the\n // caller can decide whether to fall back.\n throw new Error(\"invalid frontmatter\");\n }\n fmBody = afterOpen.substring(0, match.index);\n rest = afterOpen.substring(match.index + match[0].length);\n }\n\n let loaded: unknown;\n try {\n loaded = loadYaml(fmBody);\n } catch {\n throw new Error(\"invalid frontmatter\");\n }\n\n if (loaded === null || loaded === undefined) {\n // Empty frontmatter block (`---\\n---\\n...`). Use only provenance.\n const yaml = dump(provenance, { noRefs: true, lineWidth: -1, sortKeys: false });\n return `${FRONTMATTER_FENCE}\\n${yaml}${FRONTMATTER_FENCE}\\n${rest}`;\n }\n if (typeof loaded !== \"object\" || Array.isArray(loaded)) {\n throw new Error(\"invalid frontmatter\");\n }\n\n // Shallow merge: existing keys preserved, provenance keys overwritten.\n const existing = loaded as Record<string, unknown>;\n const merged: Record<string, unknown> = {\n ...existing,\n ...provenance,\n };\n const yaml = dump(merged, { noRefs: true, lineWidth: -1, sortKeys: false });\n return `${FRONTMATTER_FENCE}\\n${yaml}${FRONTMATTER_FENCE}\\n${rest}`;\n}\n","import { join } from \"node:path\";\n\nimport { dump } from \"js-yaml\";\nimport { optional, refine, z } from \"zod/mini\";\n\nimport { fileExists, readFileContent, writeFileContent } from \"../../utils/file.js\";\nimport { loadYaml } from \"../../utils/yaml.js\";\n\n/**\n * Filename of the rulesync-managed gh-skill-compatible lockfile. Distinct\n * from the rulesync sources lockfile (`rulesync.lock`) and from the\n * apm-mode lockfile (`rulesync-apm.lock.yaml`) so the three install modes\n * never fight over the same file.\n */\nconst GH_LOCKFILE_FILE_NAME = \"rulesync-gh.lock.yaml\";\nexport const GH_LOCKFILE_VERSION = \"1\" as const;\n\n/**\n * Shape of content_hash values that rulesync writes for gh installs. Same\n * format as the apm-mode hash so callers can reuse the integrity check\n * conventions; under `--frozen` only values matching this regex are\n * considered comparable.\n */\nexport const RULESYNC_CONTENT_HASH_REGEX = /^sha256:[0-9a-f]{64}$/;\n\nconst ScopeSchema = z.enum([\"project\", \"user\"]);\n\n/**\n * Single installation entry in `rulesync-gh.lock.yaml`. Each entry pins one\n * skill from one source under one (agent, scope) pair — matching the gh CLI\n * model where `gh skill install` deploys exactly one skill at a time.\n */\nconst GhLockInstallationSchema = z.looseObject({\n source: z.string(),\n owner: z.string(),\n repo: z.string(),\n agent: z.string(),\n scope: ScopeSchema,\n skill: z.string(),\n requested_ref: optional(z.string()),\n resolved_ref: z.string(),\n resolved_commit: z\n .string()\n .check(refine((v) => /^[0-9a-f]{40}$/.test(v), \"resolved_commit must be a 40-char hex SHA\")),\n install_dir: z.string(),\n deployed_files: z.array(z.string()),\n content_hash: optional(z.string()),\n});\nexport type GhLockInstallation = z.infer<typeof GhLockInstallationSchema>;\n\nconst GhLockSchema = z.looseObject({\n lockfile_version: z.literal(\"1\"),\n generated_at: z.string(),\n installations: z.array(GhLockInstallationSchema),\n});\nexport type GhLock = z.infer<typeof GhLockSchema>;\n\nexport function getGhLockPath(projectRoot: string): string {\n return join(projectRoot, GH_LOCKFILE_FILE_NAME);\n}\n\n/**\n * Create an empty gh lockfile structure. When `existingLock` is provided,\n * top-level looseObject extras are carried forward so unknown fields (added\n * by future tools or other lockfile producers) round-trip cleanly.\n */\nexport function createEmptyGhLock(params?: { existingLock?: GhLock | null }): GhLock {\n const base = params?.existingLock ? { ...params.existingLock } : {};\n return {\n ...base,\n lockfile_version: GH_LOCKFILE_VERSION,\n generated_at: new Date().toISOString(),\n installations: [],\n };\n}\n\n/**\n * Parse `rulesync-gh.lock.yaml` content into a `GhLock`. Returns `null` for\n * empty / non-YAML-object content so callers can treat the lockfile as\n * missing. A *structurally* present lockfile that fails schema validation\n * throws, rather than silently dropping previously pinned entries.\n */\nexport function parseGhLock(content: string): GhLock | null {\n if (!content.trim()) {\n return null;\n }\n let loaded: unknown;\n try {\n loaded = loadYaml(content);\n } catch {\n return null;\n }\n if (!loaded || typeof loaded !== \"object\") {\n return null;\n }\n const parsed = GhLockSchema.safeParse(loaded);\n if (!parsed.success) {\n const issues = parsed.error.issues\n .map((issue) => ` - ${issue.path.join(\".\") || \"<root>\"}: ${issue.message}`)\n .join(\"\\n\");\n throw new Error(`Invalid ${GH_LOCKFILE_FILE_NAME}:\\n${issues}`);\n }\n return parsed.data;\n}\n\nexport async function readGhLock(projectRoot: string): Promise<GhLock | null> {\n const path = getGhLockPath(projectRoot);\n if (!(await fileExists(path))) {\n return null;\n }\n const content = await readFileContent(path);\n return parseGhLock(content);\n}\n\nexport async function writeGhLock(params: { projectRoot: string; lock: GhLock }): Promise<void> {\n const path = getGhLockPath(params.projectRoot);\n const content = serializeGhLock(params.lock);\n await writeFileContent(path, content);\n}\n\nexport function serializeGhLock(lock: GhLock): string {\n // `noRefs: true` avoids YAML anchors/aliases; `lineWidth: -1` keeps long\n // URLs and sha values on a single line so the file stays diff-friendly.\n return dump(lock, { noRefs: true, lineWidth: -1, sortKeys: false });\n}\n\n/**\n * Find the locked installation for a given (source, agent, scope, skill)\n * tuple. Source is matched case-insensitively because GitHub routes\n * `owner/repo` paths case-insensitively.\n */\nexport function findGhLockInstallation(\n lock: GhLock,\n params: { source: string; agent: string; scope: \"project\" | \"user\"; skill: string },\n): GhLockInstallation | undefined {\n const target = params.source.toLowerCase();\n return lock.installations.find(\n (i) =>\n i.source.toLowerCase() === target &&\n i.agent === params.agent &&\n i.scope === params.scope &&\n i.skill === params.skill,\n );\n}\n","import { join } from \"node:path\";\n\nimport { CLAUDECODE_SKILLS_DIR_PATH } from \"../../constants/claudecode-paths.js\";\nimport { getHomeDirectory } from \"../../utils/file.js\";\n\n/**\n * Agents recognized by `--mode gh`. Mirrors the agent list documented for\n * `gh skill install`. The same skill content can be deployed under multiple\n * agent-specific directories simultaneously, one entry per `(agent, scope)`\n * pair in `rulesync.jsonc`.\n */\nexport const GH_AGENTS = [\n \"github-copilot\",\n \"claude-code\",\n \"cursor\",\n \"codex\",\n \"gemini\",\n \"antigravity\",\n] as const;\nexport type GhAgent = (typeof GH_AGENTS)[number];\n\nexport type GhScope = \"project\" | \"user\";\n\n/**\n * Resolve the absolute install directory for a given agent + scope, matching\n * the layout expected by `gh skill install`.\n *\n * Project scope writes inside `projectRoot`. The `github-copilot` agent uses the\n * shared `.agents/skills` directory (the host-agnostic project layout); other\n * agents that share that location (cursor, codex, gemini, antigravity) write\n * to `.agents/skills` for project scope and to their own `.<tool>/skills`\n * directory for user scope. Claude Code is the exception: project and user\n * scope both use `.claude/skills`, just rooted at `projectRoot` vs the home\n * directory respectively.\n */\nexport function resolveGhInstallDir(params: {\n agent: GhAgent;\n scope: GhScope;\n projectRoot: string;\n}): string {\n const { agent, scope, projectRoot } = params;\n const home = scope === \"user\" ? getHomeDirectory() : projectRoot;\n const relative = relativeInstallDirFor({ agent, scope });\n return join(home, relative);\n}\n\n/**\n * Returns the install directory relative to its scope root (projectRoot for\n * project scope, home for user scope). Exposed separately so the lockfile can\n * record the same canonical relative path it deploys to.\n */\nexport function relativeInstallDirFor(params: { agent: GhAgent; scope: GhScope }): string {\n const { agent, scope } = params;\n if (scope === \"project\") {\n if (agent === \"claude-code\") {\n return CLAUDECODE_SKILLS_DIR_PATH;\n }\n // github-copilot and the rest share the shared project layout.\n return join(\".agents\", \"skills\");\n }\n // user scope\n switch (agent) {\n case \"github-copilot\":\n return join(\".copilot\", \"skills\");\n case \"claude-code\":\n return CLAUDECODE_SKILLS_DIR_PATH;\n case \"cursor\":\n return join(\".cursor\", \"skills\");\n case \"codex\":\n return join(\".agents\", \"skills\");\n case \"gemini\":\n return join(\".gemini\", \"skills\");\n case \"antigravity\":\n return join(\".gemini\", \"antigravity\", \"skills\");\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { basename, join, posix } from \"node:path\";\n\nimport { Semaphore } from \"es-toolkit/promise\";\n\nimport type { SourceEntry } from \"../../config/config.js\";\nimport { FETCH_CONCURRENCY_LIMIT, MAX_FILE_SIZE } from \"../../constants/rulesync-paths.js\";\nimport { formatError } from \"../../utils/error.js\";\nimport {\n checkPathTraversal,\n getHomeDirectory,\n removeFile,\n toPosixPath,\n writeFileContent,\n} from \"../../utils/file.js\";\nimport type { Logger } from \"../../utils/logger.js\";\nimport { GitHubClient, GitHubClientError, logGitHubAuthHints } from \"../github-client.js\";\nimport { listDirectoryRecursive, withSemaphore } from \"../github-utils.js\";\nimport { parseSource } from \"../source-parser.js\";\nimport { injectSourceMetadata } from \"./gh-frontmatter.js\";\nimport {\n createEmptyGhLock,\n findGhLockInstallation,\n type GhLock,\n type GhLockInstallation,\n readGhLock,\n RULESYNC_CONTENT_HASH_REGEX,\n writeGhLock,\n} from \"./gh-lock.js\";\nimport { type GhAgent, GH_AGENTS, type GhScope, relativeInstallDirFor } from \"./gh-paths.js\";\n\nconst SKILLS_REMOTE_DIR = \"skills\";\nconst SKILL_FILE_NAME = \"SKILL.md\";\n\nexport type GhInstallOptions = {\n /** Force re-resolve all refs, ignoring the lockfile. */\n update?: boolean;\n /** Fail if the lockfile is missing or out of sync (for CI). */\n frozen?: boolean;\n /** GitHub token for private repositories. */\n token?: string;\n};\n\nexport type GhInstallResult = {\n sourcesProcessed: number;\n installedSkillCount: number;\n failedSourceCount: number;\n};\n\ntype ResolvedSource = {\n entry: SourceEntry;\n owner: string;\n repo: string;\n ref?: string;\n agent: GhAgent;\n scope: GhScope;\n};\n\ntype DeployedFile = {\n /** Relative POSIX path under the install dir's scope root. Recorded in the lockfile. */\n relativeToScopeRoot: string;\n /** Absolute on-disk path where the bytes are written. */\n absolutePath: string;\n content: string;\n};\n\ntype SkillInstallation = {\n installation: GhLockInstallation;\n deployed: DeployedFile[];\n};\n\ntype SourceResult =\n | { status: \"ok\"; installations: SkillInstallation[] }\n | { status: \"failed\"; preserved: GhLockInstallation[] };\n\n/**\n * Entry point for `rulesync install --mode gh`. Reads `sources` from\n * `rulesync.jsonc`, resolves each one against the GitHub API, and deploys\n * each discovered `skills/<name>/` tree under the agent-specific install\n * directory recorded by `resolveGhInstallDir`. Updates `rulesync-gh.lock.yaml`\n * to pin commits and per-skill content hashes.\n */\nexport async function installGh(params: {\n projectRoot: string;\n sources: SourceEntry[];\n options?: GhInstallOptions;\n logger: Logger;\n}): Promise<GhInstallResult> {\n const { projectRoot, sources, options = {}, logger } = params;\n\n if (sources.length === 0) {\n return { sourcesProcessed: 0, installedSkillCount: 0, failedSourceCount: 0 };\n }\n\n // Pre-resolve every source's owner/repo + agent/scope defaults so the\n // frozen-mode coverage check below has a stable view of what installations\n // are required. We do not contact the API yet — that happens per-source.\n const resolvedSources: ResolvedSource[] = sources.map(resolveGhSource);\n\n const existingLock = await readGhLock(projectRoot);\n const frozen = options.frozen ?? false;\n const update = options.update ?? false;\n\n if (frozen && !existingLock) {\n throw new Error(\n \"Frozen install failed: rulesync-gh.lock.yaml is missing. Run 'rulesync install --mode gh' to create it.\",\n );\n }\n\n if (frozen && existingLock) {\n assertFrozenLockCoversSources({ existingLock, resolvedSources });\n }\n\n const token = GitHubClient.resolveToken(options.token);\n const client = new GitHubClient({ token });\n const semaphore = new Semaphore(FETCH_CONCURRENCY_LIMIT);\n\n const newLock: GhLock = createEmptyGhLock({ existingLock });\n\n const runOne = async (rs: ResolvedSource): Promise<SourceResult> => {\n const installations = await installSource({\n rs,\n client,\n semaphore,\n projectRoot,\n existingLock,\n frozen,\n update,\n logger,\n });\n return { status: \"ok\", installations };\n };\n\n const results: SourceResult[] = frozen\n ? await Promise.all(resolvedSources.map(runOne))\n : await Promise.all(\n resolvedSources.map(async (rs): Promise<SourceResult> => {\n try {\n return await runOne(rs);\n } catch (error) {\n logger.error(`Failed to install gh source \"${rs.entry.source}\": ${formatError(error)}`);\n if (error instanceof GitHubClientError) {\n logGitHubAuthHints({ error, logger });\n }\n // Preserve all prior installations for this source so that a\n // transient error does not erase previously pinned commit SHAs.\n const preserved = existingLock\n ? existingLock.installations.filter(\n (i) => i.source.toLowerCase() === rs.entry.source.toLowerCase(),\n )\n : [];\n return { status: \"failed\", preserved };\n }\n }),\n );\n\n if (frozen) {\n await writeDeferredFrozenFiles(results);\n }\n\n const { totalInstalled, failedCount } = aggregateSourceResults({ results, newLock });\n\n // Stale-file cleanup. Same hardening shape as apm-install.\n if (existingLock) {\n await removeStaleGhFiles({ existingLock, newLock, projectRoot, logger });\n }\n\n if (!frozen) {\n newLock.generated_at = new Date().toISOString();\n await writeGhLock({ projectRoot, lock: newLock });\n if (failedCount === 0) {\n logger.debug(\"rulesync-gh.lock.yaml updated.\");\n } else {\n logger.warn(\n `rulesync-gh.lock.yaml written with partially successful installs (${failedCount} source(s) failed).`,\n );\n }\n }\n\n return {\n sourcesProcessed: sources.length,\n installedSkillCount: totalInstalled,\n failedSourceCount: failedCount,\n };\n}\n\n/**\n * Validate and normalize a single declared source into a ResolvedSource without\n * contacting the API. Rejects non-GitHub providers and the gh-unsupported\n * rulesync-mode-only fields, and applies the agent/scope defaults.\n */\nfunction resolveGhSource(entry: SourceEntry): ResolvedSource {\n const parsed = parseSource(entry.source);\n if (parsed.provider !== \"github\") {\n throw new Error(\n `--mode gh only supports GitHub sources. \"${entry.source}\" resolves to provider \"${parsed.provider}\".`,\n );\n }\n // gh mode does not honor these rulesync-mode-only SourceEntry fields.\n // Silently dropping them would\n // surprise users migrating from --mode rulesync, so reject up-front\n // with a message that names the offending field.\n if (entry.transport !== undefined && entry.transport !== \"github\") {\n throw new Error(\n `--mode gh: field \"transport\" is not supported (got \"${entry.transport}\" for source \"${entry.source}\"). Drop the field or switch to --mode rulesync.`,\n );\n }\n if (entry.path !== undefined) {\n throw new Error(\n `--mode gh: field \"path\" is not supported for source \"${entry.source}\". The remote layout is fixed to \"skills/<name>/SKILL.md\".`,\n );\n }\n if (entry.rules !== undefined) {\n throw new Error(\n `--mode gh: field \"rules\" is not supported for source \"${entry.source}\". Switch to --mode rulesync to install declarative rules.`,\n );\n }\n if (entry.rulesPath !== undefined) {\n throw new Error(\n `--mode gh: field \"rulesPath\" is not supported for source \"${entry.source}\". Switch to --mode rulesync to install declarative rules.`,\n );\n }\n const agent = entry.agent ?? \"github-copilot\";\n if (!GH_AGENTS.includes(agent)) {\n throw new Error(\n `--mode gh: unknown agent \"${agent}\" for source \"${entry.source}\". Valid agents: ${GH_AGENTS.join(\", \")}.`,\n );\n }\n const scope: GhScope = entry.scope ?? \"project\";\n return {\n entry,\n owner: parsed.owner,\n repo: parsed.repo,\n ref: entry.ref ?? parsed.ref,\n agent,\n scope,\n };\n}\n\n/**\n * Frozen mode: per-source coverage check plus `ref` drift detection. A\n * brand-new source (no installations at all in the lock) must fail before we\n * contact the GitHub API — both to save quota and to prevent in-flight\n * Promise.all siblings from writing files when another source is going to\n * throw. Per-skill coverage is enforced lazily inside installSource, since that\n * requires API discovery to know which skills exist remotely.\n */\nfunction assertFrozenLockCoversSources(params: {\n existingLock: GhLock;\n resolvedSources: ResolvedSource[];\n}): void {\n const { existingLock, resolvedSources } = params;\n const uncovered: string[] = [];\n for (const rs of resolvedSources) {\n const hasAny = existingLock.installations.some(\n (i) =>\n i.source.toLowerCase() === rs.entry.source.toLowerCase() &&\n i.agent === rs.agent &&\n i.scope === rs.scope,\n );\n if (!hasAny) {\n uncovered.push(`${rs.entry.source} (agent=${rs.agent}, scope=${rs.scope})`);\n }\n }\n if (uncovered.length > 0) {\n throw new Error(\n `Frozen install failed: rulesync-gh.lock.yaml is missing entries for: ${uncovered.join(\", \")}. Run 'rulesync install --mode gh' to update the lockfile.`,\n );\n }\n\n // Detect manifest drift on `ref`: when the user edited `ref` in\n // rulesync.jsonc without re-running install, refuse rather than\n // silently install the locked SHA against a different declared ref.\n const drifted: string[] = [];\n for (const rs of resolvedSources) {\n if (!rs.ref) continue;\n const matches = existingLock.installations.filter(\n (i) => i.source.toLowerCase() === rs.entry.source.toLowerCase(),\n );\n for (const m of matches) {\n if (m.requested_ref !== undefined && m.requested_ref !== rs.ref) {\n drifted.push(`${rs.entry.source} (manifest=${rs.ref}, lock=${m.requested_ref})`);\n break;\n }\n }\n }\n if (drifted.length > 0) {\n throw new Error(\n `Frozen install failed: manifest ref does not match rulesync-gh.lock.yaml for: ${drifted.join(\", \")}. Run 'rulesync install --mode gh' to update the lockfile.`,\n );\n }\n}\n\n/**\n * Frozen-mode deferred writes. `installSource` never touches the disk under\n * --frozen — every write lands here, only after Promise.all has resolved\n * successfully for every source. Without this gate, source A could finish\n * writing its bytes before source B's coverage / integrity check throws,\n * leaving the working tree in a partially-frozen state despite the install\n * reporting failure.\n */\nasync function writeDeferredFrozenFiles(results: SourceResult[]): Promise<void> {\n for (const result of results) {\n if (result.status !== \"ok\") continue;\n for (const inst of result.installations) {\n for (const d of inst.deployed) {\n await writeFileContent(d.absolutePath, d.content);\n }\n }\n }\n}\n\n/**\n * Push each source result's installations (or preserved prior entries on\n * failure) into the new lock, returning the installed and failed counts.\n */\nfunction aggregateSourceResults(params: { results: SourceResult[]; newLock: GhLock }): {\n totalInstalled: number;\n failedCount: number;\n} {\n const { results, newLock } = params;\n let totalInstalled = 0;\n let failedCount = 0;\n for (const result of results) {\n if (result.status === \"ok\") {\n for (const inst of result.installations) {\n newLock.installations.push(inst.installation);\n }\n totalInstalled += result.installations.length;\n } else {\n failedCount += 1;\n for (const preserved of result.preserved) {\n newLock.installations.push(preserved);\n }\n }\n }\n return { totalInstalled, failedCount };\n}\n\n/**\n * Remove files deployed by a previous install that are no longer part of any\n * current installation, keyed by (scope, path) so identically-named files under\n * different scope roots are not conflated.\n */\nasync function removeStaleGhFiles(params: {\n existingLock: GhLock;\n newLock: GhLock;\n projectRoot: string;\n logger: Logger;\n}): Promise<void> {\n const { existingLock, newLock, projectRoot, logger } = params;\n const newDeployed = new Set<string>();\n for (const inst of newLock.installations) {\n for (const file of inst.deployed_files) {\n // Key by (scope, path) so a file in `<home>/.claude/skills/foo` and a\n // file at `<base>/.claude/skills/foo` are not conflated.\n newDeployed.add(`${inst.scope}::${file}`);\n }\n }\n for (const prev of existingLock.installations) {\n for (const deployed of prev.deployed_files) {\n const key = `${prev.scope}::${deployed}`;\n if (newDeployed.has(key)) continue;\n await removeStaleFile({\n relativePath: deployed,\n scope: prev.scope === \"user\" ? \"user\" : \"project\",\n projectRoot,\n logger,\n });\n }\n }\n}\n\nasync function installSource(params: {\n rs: ResolvedSource;\n client: GitHubClient;\n semaphore: Semaphore;\n projectRoot: string;\n existingLock: GhLock | null;\n frozen: boolean;\n update: boolean;\n logger: Logger;\n}): Promise<SkillInstallation[]> {\n const { rs, client, semaphore, projectRoot, existingLock, frozen, update, logger } = params;\n const { entry, owner, repo, agent, scope } = rs;\n const sourceKey = entry.source;\n\n const { resolvedRef, resolvedSha, usedTag } = await resolveGhRef({\n rs,\n client,\n owner,\n repo,\n sourceKey,\n logger,\n });\n\n // Discover skills under `skills/`.\n const validatedSkills = await discoverValidatedSkills({\n client,\n semaphore,\n owner,\n repo,\n resolvedSha,\n sourceKey,\n logger,\n });\n if (validatedSkills === null) {\n return [];\n }\n\n // Apply the explicit skill filter when provided.\n const selected = selectSkills({ validatedSkills, entry, sourceKey, logger });\n\n // Frozen-mode coverage check (per-skill). Only enforceable now that we know\n // the requested skill set.\n if (frozen && existingLock) {\n assertFrozenSkillCoverage({ selected, existingLock, sourceKey, agent, scope });\n }\n\n const results: SkillInstallation[] = [];\n const installRelDir = relativeInstallDirFor({ agent, scope });\n const scopeRoot = scope === \"user\" ? getHomeDirectory() : projectRoot;\n\n // Source URL recorded in injected frontmatter. Mirrors the canonical form\n // used by `gh skill install`.\n const sourceUrl = `https://github.com/${owner}/${repo}`;\n const repository = `${owner}/${repo}`;\n // gh records the *resolved* ref (the tag name when one was used, else the\n // commit SHA) into the SKILL.md frontmatter so the deployed file has a\n // human-readable provenance hint.\n const provenanceRef = usedTag ? resolvedRef : resolvedSha;\n\n for (const sk of selected) {\n const locked =\n existingLock && !update\n ? findGhLockInstallation(existingLock, {\n source: sourceKey,\n agent,\n scope,\n skill: sk.name,\n })\n : undefined;\n\n // Recursively list this skill's tree.\n const allFiles = await listDirectoryRecursive({\n client,\n owner,\n repo,\n path: sk.path,\n ref: resolvedSha,\n semaphore,\n });\n\n const deployed = await buildSkillDeployment({\n sk,\n allFiles,\n client,\n semaphore,\n owner,\n repo,\n resolvedSha,\n installRelDir,\n scopeRoot,\n sourceUrl,\n repository,\n provenanceRef,\n sourceKey,\n frozen,\n logger,\n });\n\n deployed.sort((a, b) =>\n a.relativeToScopeRoot < b.relativeToScopeRoot\n ? -1\n : a.relativeToScopeRoot > b.relativeToScopeRoot\n ? 1\n : 0,\n );\n const deployedFiles = deployed.map((d) => d.relativeToScopeRoot);\n const contentHash = computeContentHash(deployed);\n\n assertFrozenSkillIntegrity({\n frozen,\n locked,\n contentHash,\n sourceKey,\n skillName: sk.name,\n agent,\n scope,\n logger,\n });\n\n // Under --frozen we deliberately do NOT write here even after the\n // integrity check passes. Writes are deferred to the top-level installGh\n // so that a sibling source failing its check cannot leave partial bytes\n // on disk from a peer that already passed.\n const installation: GhLockInstallation = {\n source: sourceKey,\n owner,\n repo,\n agent,\n scope,\n skill: sk.name,\n resolved_ref: resolvedRef,\n resolved_commit: resolvedSha,\n install_dir: toPosixPath(installRelDir),\n deployed_files: deployedFiles,\n content_hash: contentHash,\n };\n if (rs.ref !== undefined) {\n installation.requested_ref = rs.ref;\n }\n results.push({ installation, deployed });\n\n logger.info(\n `Installed gh skill \"${sk.name}\" from ${sourceKey} (agent=${agent}, scope=${scope}, ref=${resolvedRef})`,\n );\n }\n\n return results;\n}\n\n/**\n * Resolve the ref for a gh source. Order: explicit `entry.ref`, then the latest\n * release's tag, then the default branch (when the repo has no releases).\n * Returns the resolved ref, its commit SHA, and whether a release tag was used.\n */\nasync function resolveGhRef(params: {\n rs: ResolvedSource;\n client: GitHubClient;\n owner: string;\n repo: string;\n sourceKey: string;\n logger: Logger;\n}): Promise<{ resolvedRef: string; resolvedSha: string; usedTag: boolean }> {\n const { rs, client, owner, repo, sourceKey, logger } = params;\n let resolvedRef: string;\n let usedTag = false;\n if (rs.ref) {\n resolvedRef = rs.ref;\n } else {\n try {\n const release = await client.getLatestRelease(owner, repo);\n resolvedRef = release.tag_name;\n usedTag = true;\n } catch (error) {\n // gh's behavior: when a repo has no releases, getLatestRelease returns\n // 404. We treat any 404 (real GitHubClientError or any thrown value\n // carrying statusCode 404) as \"no releases\" and fall back to the\n // default branch. Other errors propagate.\n if (is404(error)) {\n resolvedRef = await client.getDefaultBranch(owner, repo);\n } else {\n throw error;\n }\n }\n }\n const resolvedSha = await client.resolveRefToSha(owner, repo, resolvedRef);\n logger.debug(`Resolved ${sourceKey} -> ref=${resolvedRef} sha=${resolvedSha}`);\n return { resolvedRef, resolvedSha, usedTag };\n}\n\n/**\n * List `skills/` and validate which subdirectories are actual skills (contain a\n * SKILL.md). Returns null (with a warn log) when the `skills/` directory 404s so\n * the caller can skip the source. Validation is sequential to avoid hammering\n * the API for large monorepos beyond FETCH_CONCURRENCY_LIMIT.\n */\nasync function discoverValidatedSkills(params: {\n client: GitHubClient;\n semaphore: Semaphore;\n owner: string;\n repo: string;\n resolvedSha: string;\n sourceKey: string;\n logger: Logger;\n}): Promise<Array<{ name: string; path: string }> | null> {\n const { client, semaphore, owner, repo, resolvedSha, sourceKey, logger } = params;\n let topLevel: Awaited<ReturnType<GitHubClient[\"listDirectory\"]>>;\n try {\n topLevel = await client.listDirectory(owner, repo, SKILLS_REMOTE_DIR, resolvedSha);\n } catch (error) {\n if (is404(error)) {\n logger.warn(`No skills/ directory found in ${sourceKey}. Skipping.`);\n return null;\n }\n throw error;\n }\n\n const skillDirs = topLevel\n .filter((e) => e.type === \"dir\")\n .map((e) => ({ name: e.name, path: e.path }));\n\n const validatedSkills: Array<{ name: string; path: string }> = [];\n for (const sk of skillDirs) {\n const info = await withSemaphore(semaphore, () =>\n client.getFileInfo(owner, repo, posix.join(sk.path, SKILL_FILE_NAME), resolvedSha),\n );\n if (info) {\n validatedSkills.push(sk);\n }\n }\n return validatedSkills;\n}\n\n/**\n * Apply the explicit `entry.skills` filter to the validated skills, warning for\n * each requested name that is absent upstream. Returns all validated skills when\n * no filter is provided.\n */\nfunction selectSkills(params: {\n validatedSkills: Array<{ name: string; path: string }>;\n entry: SourceEntry;\n sourceKey: string;\n logger: Logger;\n}): Array<{ name: string; path: string }> {\n const { validatedSkills, entry, sourceKey, logger } = params;\n if (!entry.skills || entry.skills.length === 0) {\n return validatedSkills;\n }\n const requested = new Set(entry.skills);\n const selected = validatedSkills.filter((s) => requested.has(s.name));\n const presentNames = new Set(validatedSkills.map((s) => s.name));\n for (const want of entry.skills) {\n if (!presentNames.has(want)) {\n logger.warn(`Requested skill \"${want}\" not found in ${sourceKey} under skills/. Skipping.`);\n }\n }\n return selected;\n}\n\n/**\n * Frozen-mode per-skill coverage check. Throws when any selected skill has no\n * matching lock installation for the (source, agent, scope) tuple.\n */\nfunction assertFrozenSkillCoverage(params: {\n selected: Array<{ name: string; path: string }>;\n existingLock: GhLock;\n sourceKey: string;\n agent: GhAgent;\n scope: GhScope;\n}): void {\n const { selected, existingLock, sourceKey, agent, scope } = params;\n const missing: string[] = [];\n for (const sk of selected) {\n const locked = findGhLockInstallation(existingLock, {\n source: sourceKey,\n agent,\n scope,\n skill: sk.name,\n });\n if (!locked) {\n missing.push(sk.name);\n }\n }\n if (missing.length > 0) {\n throw new Error(\n `Frozen install failed: rulesync-gh.lock.yaml is missing entries for ${sourceKey} (agent=${agent}, scope=${scope}) skills: ${missing.join(\", \")}. Run 'rulesync install --mode gh' to update the lockfile.`,\n );\n }\n}\n\n/**\n * Fetch, validate, and (under non-frozen) write a single skill's file tree,\n * returning the deployable files. Oversized or out-of-bounds files are skipped\n * with a warn log; SKILL.md files have provenance frontmatter injected.\n */\nasync function buildSkillDeployment(params: {\n sk: { name: string; path: string };\n allFiles: Awaited<ReturnType<typeof listDirectoryRecursive>>;\n client: GitHubClient;\n semaphore: Semaphore;\n owner: string;\n repo: string;\n resolvedSha: string;\n installRelDir: string;\n scopeRoot: string;\n sourceUrl: string;\n repository: string;\n provenanceRef: string;\n sourceKey: string;\n frozen: boolean;\n logger: Logger;\n}): Promise<DeployedFile[]> {\n const {\n sk,\n allFiles,\n client,\n semaphore,\n owner,\n repo,\n resolvedSha,\n installRelDir,\n scopeRoot,\n sourceUrl,\n repository,\n provenanceRef,\n sourceKey,\n frozen,\n logger,\n } = params;\n\n const deployed: DeployedFile[] = [];\n for (const file of allFiles) {\n if (file.size > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping \"${file.path}\" from ${sourceKey}: ${(file.size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit.`,\n );\n continue;\n }\n // Path of the file relative to the skill directory root upstream.\n const relativeToSkill = posix.relative(sk.path, toPosixPath(file.path));\n if (!relativeToSkill || relativeToSkill.startsWith(\"..\") || posix.isAbsolute(relativeToSkill)) {\n logger.warn(`Skipping \"${file.path}\" from ${sourceKey}: resolved outside of \"${sk.path}\".`);\n continue;\n }\n\n // Path under the scope root (relative). This is the value persisted to\n // the lockfile.\n const deployRelative = toPosixPath(join(installRelDir, sk.name, relativeToSkill));\n // Path-traversal hardening rooted at the scope root, then a tighter\n // check rooted at the per-(agent,scope) install dir to refuse anything\n // that escapes the agent-specific deployment directory.\n checkPathTraversal({ relativePath: deployRelative, intendedRootDir: scopeRoot });\n const installAbs = join(scopeRoot, installRelDir);\n const withinInstallDir = toPosixPath(join(sk.name, relativeToSkill));\n checkPathTraversal({ relativePath: withinInstallDir, intendedRootDir: installAbs });\n\n let content = await withSemaphore(semaphore, () =>\n client.getFileContent(owner, repo, file.path, resolvedSha),\n );\n const byteLength = Buffer.byteLength(content, \"utf8\");\n if (byteLength > MAX_FILE_SIZE) {\n logger.warn(\n `Skipping \"${file.path}\" from ${sourceKey}: fetched ${(byteLength / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit.`,\n );\n continue;\n }\n\n // Inject provenance frontmatter into SKILL.md files. Other files\n // (e.g. supporting markdown, scripts) pass through unchanged.\n if (basename(file.path) === SKILL_FILE_NAME) {\n try {\n content = injectSourceMetadata({\n content,\n source: sourceUrl,\n repository,\n ref: provenanceRef,\n });\n } catch {\n // Frontmatter exists but is not parseable. Fall back to a fresh\n // prepend so we still record provenance — but warn the user.\n logger.warn(\n `Frontmatter in ${file.path} (${sourceKey}) is invalid. Prepending a fresh provenance block.`,\n );\n content = `---\\nsource: ${sourceUrl}\\nrepository: ${repository}\\nref: ${provenanceRef}\\n---\\n${content}`;\n }\n }\n\n const absolutePath = join(scopeRoot, deployRelative);\n deployed.push({ relativeToScopeRoot: deployRelative, absolutePath, content });\n\n if (!frozen) {\n await writeFileContent(absolutePath, content);\n }\n }\n return deployed;\n}\n\n/**\n * Frozen integrity check: refuse to overwrite known-good bytes with tampered\n * ones when the prior content_hash matches the rulesync format. Hashes not\n * written by rulesync are skipped (debug-logged), preserving the commit-SHA pin.\n */\nfunction assertFrozenSkillIntegrity(params: {\n frozen: boolean;\n locked: GhLockInstallation | undefined;\n contentHash: string;\n sourceKey: string;\n skillName: string;\n agent: GhAgent;\n scope: GhScope;\n logger: Logger;\n}): void {\n const { frozen, locked, contentHash, sourceKey, skillName, agent, scope, logger } = params;\n if (frozen && locked?.content_hash) {\n if (RULESYNC_CONTENT_HASH_REGEX.test(locked.content_hash)) {\n if (locked.content_hash !== contentHash) {\n throw new Error(\n `content_hash mismatch for ${sourceKey} skill \"${skillName}\" (agent=${agent}, scope=${scope}): lock=${locked.content_hash} computed=${contentHash}. Refuse to trust the deployment under --frozen.`,\n );\n }\n } else {\n logger.debug(\n `Skipping content_hash integrity check for ${sourceKey} skill \"${skillName}\": recorded hash \"${locked.content_hash}\" was not written by rulesync.`,\n );\n }\n }\n}\n\nasync function removeStaleFile(params: {\n relativePath: string;\n scope: GhScope;\n projectRoot: string;\n logger: Logger;\n}): Promise<void> {\n const { relativePath, scope, projectRoot, logger } = params;\n if (posix.isAbsolute(relativePath) || relativePath.split(/[/\\\\]/).includes(\"..\")) {\n logger.warn(`Refusing to remove stale gh file with suspicious path: \"${relativePath}\".`);\n return;\n }\n const scopeRoot = scope === \"user\" ? getHomeDirectory() : projectRoot;\n try {\n checkPathTraversal({ relativePath, intendedRootDir: scopeRoot });\n } catch {\n logger.warn(`Refusing to remove stale gh file outside ${scope} root: \"${relativePath}\".`);\n return;\n }\n const absolute = join(scopeRoot, relativePath);\n await removeFile(absolute);\n logger.debug(`Removed stale gh file: ${relativePath}`);\n}\n\n/**\n * Detect a 404-like error in a way that tolerates both real `GitHubClientError`\n * instances and any other thrown value that exposes a numeric `statusCode`\n * (e.g. plain Errors raised from a test mock that does not import the real\n * client class).\n */\nfunction is404(error: unknown): boolean {\n if (error instanceof GitHubClientError && error.statusCode === 404) {\n return true;\n }\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"statusCode\" in error &&\n error.statusCode === 404\n ) {\n return true;\n }\n return false;\n}\n\n/**\n * SHA-256 over a canonical, order-independent representation of the deployed\n * files. Identical algorithm to the apm-install hash so users familiar with\n * one mode can read the other.\n */\nfunction computeContentHash(\n files: Array<{ relativeToScopeRoot: string; content: string }>,\n): string {\n const hash = createHash(\"sha256\");\n for (const { relativeToScopeRoot, content } of files) {\n hash.update(relativeToScopeRoot);\n hash.update(\"\\0\");\n hash.update(content);\n hash.update(\"\\0\");\n }\n return `sha256:${hash.digest(\"hex\")}`;\n}\n","import { ConfigResolver } from \"../../config/config-resolver.js\";\nimport { installApm } from \"../../lib/apm/apm-install.js\";\nimport { apmManifestExists } from \"../../lib/apm/apm-manifest.js\";\nimport { installGh } from \"../../lib/gh/gh-install.js\";\nimport { resolveAndFetchSources } from \"../../lib/sources.js\";\nimport type { Logger } from \"../../utils/logger.js\";\n\nexport const INSTALL_MODES = [\"rulesync\", \"apm\", \"gh\"] as const;\nexport type InstallMode = (typeof INSTALL_MODES)[number];\n\nexport type InstallCommandOptions = {\n mode?: InstallMode;\n update?: boolean;\n frozen?: boolean;\n token?: string;\n configPath?: string;\n verbose?: boolean;\n silent?: boolean;\n};\n\nexport async function installCommand(\n logger: Logger,\n options: InstallCommandOptions,\n): Promise<void> {\n const mode: InstallMode = options.mode ?? \"rulesync\";\n\n if (mode === \"gh\") {\n await runGhInstall(logger, options);\n return;\n }\n\n if (mode === \"apm\") {\n await runApmInstall(logger, options);\n return;\n }\n\n await runRulesyncInstall(logger, options);\n}\n\nasync function runRulesyncInstall(logger: Logger, options: InstallCommandOptions): Promise<void> {\n const projectRoot = process.cwd();\n\n // If both apm.yml and rulesync.jsonc sources are defined, refuse to guess.\n // `--mode apm` is required to opt into the APM layout.\n const apmExists = await apmManifestExists(projectRoot);\n\n const config = await ConfigResolver.resolve(\n {\n configPath: options.configPath,\n verbose: options.verbose,\n silent: options.silent,\n },\n { logger },\n );\n const sources = config.getSources();\n\n if (apmExists && sources.length > 0) {\n throw new Error(\n \"Both apm.yml and rulesync.jsonc `sources` are defined. Pass --mode apm or --mode rulesync to disambiguate.\",\n );\n }\n\n if (sources.length === 0) {\n if (apmExists) {\n logger.warn(\n \"No sources defined in rulesync.jsonc, but apm.yml is present. Did you mean --mode apm?\",\n );\n return;\n }\n logger.warn(\"No sources defined in configuration. Removing stale source artifacts.\");\n }\n\n logger.debug(`Installing rules and skills from ${sources.length} source(s)...`);\n\n const result = await resolveAndFetchSources({\n sources,\n projectRoot,\n options: {\n updateSources: options.update,\n frozen: options.frozen,\n token: options.token,\n },\n logger,\n });\n\n if (logger.jsonMode) {\n logger.captureData(\"sourcesProcessed\", result.sourcesProcessed);\n logger.captureData(\"skillsFetched\", result.fetchedSkillCount);\n logger.captureData(\"rulesFetched\", result.fetchedRuleCount);\n logger.captureData(\"failedSourceCount\", result.failedSourceCount);\n }\n\n if (result.failedSourceCount > 0) {\n throw new Error(\n `Failed to install ${result.failedSourceCount} of ${result.sourcesProcessed} rulesync source(s). See the log above for details.`,\n );\n }\n\n if (result.fetchedSkillCount > 0 || result.fetchedRuleCount > 0) {\n logger.success(\n `Installed ${result.fetchedSkillCount} skill(s) and ${result.fetchedRuleCount} rule(s) from ${result.sourcesProcessed} source(s).`,\n );\n } else {\n logger.success(\n `All source artifacts up to date (${result.sourcesProcessed} source(s) checked).`,\n );\n }\n}\n\nasync function runApmInstall(logger: Logger, options: InstallCommandOptions): Promise<void> {\n const projectRoot = process.cwd();\n\n if (!(await apmManifestExists(projectRoot))) {\n throw new Error(\n \"--mode apm requires an apm.yml at the project root. Create one or drop --mode apm to fall back to rulesync mode.\",\n );\n }\n\n const result = await installApm({\n projectRoot,\n options: {\n update: options.update,\n frozen: options.frozen,\n token: options.token,\n },\n logger,\n });\n\n if (logger.jsonMode) {\n logger.captureData(\"dependenciesProcessed\", result.dependenciesProcessed);\n logger.captureData(\"deployedFileCount\", result.deployedFileCount);\n logger.captureData(\"failedDependencyCount\", result.failedDependencyCount);\n }\n\n if (result.failedDependencyCount > 0) {\n throw new Error(\n `Failed to install ${result.failedDependencyCount} of ${result.dependenciesProcessed} apm dependency(ies). See the log above for details.`,\n );\n }\n\n if (result.deployedFileCount > 0) {\n logger.success(\n `Installed ${result.deployedFileCount} file(s) from ${result.dependenciesProcessed} apm dependency(ies).`,\n );\n } else {\n logger.success(`All apm dependencies up to date (${result.dependenciesProcessed} checked).`);\n }\n}\n\nasync function runGhInstall(logger: Logger, options: InstallCommandOptions): Promise<void> {\n const projectRoot = process.cwd();\n\n // gh mode reads sources from `rulesync.jsonc`, never from `apm.yml`. The\n // disambiguation between rulesync/apm modes lives in `runRulesyncInstall`;\n // here the user has already opted into gh mode explicitly.\n const config = await ConfigResolver.resolve(\n {\n configPath: options.configPath,\n verbose: options.verbose,\n silent: options.silent,\n },\n { logger },\n );\n const sources = config.getSources();\n\n if (sources.length === 0) {\n logger.warn(\"No sources defined in configuration. Nothing to install.\");\n return;\n }\n\n const result = await installGh({\n projectRoot,\n sources,\n options: {\n update: options.update,\n frozen: options.frozen,\n token: options.token,\n },\n logger,\n });\n\n if (logger.jsonMode) {\n logger.captureData(\"sourcesProcessed\", result.sourcesProcessed);\n logger.captureData(\"installedSkillCount\", result.installedSkillCount);\n logger.captureData(\"failedSourceCount\", result.failedSourceCount);\n }\n\n if (result.failedSourceCount > 0) {\n throw new Error(\n `Failed to install ${result.failedSourceCount} of ${result.sourcesProcessed} gh source(s). See the log above for details.`,\n );\n }\n\n if (result.installedSkillCount > 0) {\n logger.success(\n `Installed ${result.installedSkillCount} skill(s) from ${result.sourcesProcessed} gh source(s).`,\n );\n } else {\n logger.success(`All gh sources up to date (${result.sourcesProcessed} checked).`);\n }\n}\n","import { basename, join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { RULESYNC_CHECKS_RELATIVE_DIR_PATH } from \"../constants/rulesync-paths.js\";\nimport {\n RulesyncCheck,\n type RulesyncCheckFrontmatter,\n RulesyncCheckFrontmatterSchema,\n} from \"../features/checks/rulesync-check.js\";\nimport { formatError } from \"../utils/error.js\";\nimport {\n checkPathTraversal,\n ensureDir,\n listDirectoryFiles,\n removeFile,\n writeFileContent,\n} from \"../utils/file.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\n\nconst logger = new ConsoleLogger({ verbose: false, silent: true });\n\nconst maxCheckSizeBytes = 1024 * 1024; // 1MB\nconst maxChecksCount = 1000;\n\n/**\n * Tool to list all checks from .rulesync/checks/*.md\n */\nasync function listChecks(): Promise<\n Array<{\n relativePathFromCwd: string;\n frontmatter: RulesyncCheckFrontmatter;\n }>\n> {\n const checksDir = join(process.cwd(), RULESYNC_CHECKS_RELATIVE_DIR_PATH);\n\n try {\n const files = await listDirectoryFiles(checksDir);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const checks = await Promise.all(\n mdFiles.map(async (file) => {\n try {\n const check = await RulesyncCheck.fromFile({\n relativeFilePath: file,\n validate: true,\n });\n\n return {\n relativePathFromCwd: join(RULESYNC_CHECKS_RELATIVE_DIR_PATH, file),\n frontmatter: check.getFrontmatter(),\n };\n } catch (error) {\n logger.error(`Failed to read check file ${file}: ${formatError(error)}`);\n return null;\n }\n }),\n );\n\n // Filter out null values (failed reads)\n return checks.filter((check): check is NonNullable<typeof check> => check !== null);\n } catch (error) {\n logger.error(\n `Failed to read checks directory (${RULESYNC_CHECKS_RELATIVE_DIR_PATH}): ${formatError(error)}`,\n );\n return [];\n }\n}\n\n/**\n * Tool to get detailed information about a specific check\n */\nasync function getCheck({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncCheckFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n try {\n const check = await RulesyncCheck.fromFile({\n relativeFilePath: filename,\n validate: true,\n });\n\n return {\n relativePathFromCwd: join(RULESYNC_CHECKS_RELATIVE_DIR_PATH, filename),\n frontmatter: check.getFrontmatter(),\n body: check.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to read check file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to create or update a check (upsert operation)\n */\nasync function putCheck({\n relativePathFromCwd,\n frontmatter,\n body,\n}: {\n relativePathFromCwd: string;\n frontmatter: RulesyncCheckFrontmatter;\n body: string;\n}): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncCheckFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n // Check file size constraint\n const estimatedSize = JSON.stringify(frontmatter).length + body.length;\n if (estimatedSize > maxCheckSizeBytes) {\n throw new Error(\n `Check size ${estimatedSize} bytes exceeds maximum ${maxCheckSizeBytes} bytes (1MB) for ${relativePathFromCwd}`,\n );\n }\n\n try {\n // Check count constraint\n const existingChecks = await listChecks();\n const isUpdate = existingChecks.some(\n (check) => check.relativePathFromCwd === join(RULESYNC_CHECKS_RELATIVE_DIR_PATH, filename),\n );\n\n if (!isUpdate && existingChecks.length >= maxChecksCount) {\n throw new Error(\n `Maximum number of checks (${maxChecksCount}) reached in ${RULESYNC_CHECKS_RELATIVE_DIR_PATH}`,\n );\n }\n\n const check = new RulesyncCheck({\n outputRoot: process.cwd(),\n relativeDirPath: RULESYNC_CHECKS_RELATIVE_DIR_PATH,\n relativeFilePath: filename,\n frontmatter,\n body,\n validate: true,\n });\n\n // Ensure directory exists\n const checksDir = join(process.cwd(), RULESYNC_CHECKS_RELATIVE_DIR_PATH);\n await ensureDir(checksDir);\n\n // Write the file\n await writeFileContent(check.getFilePath(), check.getFileContent());\n\n return {\n relativePathFromCwd: join(RULESYNC_CHECKS_RELATIVE_DIR_PATH, filename),\n frontmatter: check.getFrontmatter(),\n body: check.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to write check file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to delete a check\n */\nasync function deleteCheck({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n const fullPath = join(process.cwd(), RULESYNC_CHECKS_RELATIVE_DIR_PATH, filename);\n\n try {\n await removeFile(fullPath);\n\n return {\n relativePathFromCwd: join(RULESYNC_CHECKS_RELATIVE_DIR_PATH, filename),\n };\n } catch (error) {\n throw new Error(`Failed to delete check file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Schema for check-related tool parameters\n */\nconst checkToolSchemas = {\n listChecks: z.object({}),\n getCheck: z.object({\n relativePathFromCwd: z.string(),\n }),\n putCheck: z.object({\n relativePathFromCwd: z.string(),\n frontmatter: RulesyncCheckFrontmatterSchema,\n body: z.string(),\n }),\n deleteCheck: z.object({\n relativePathFromCwd: z.string(),\n }),\n} as const;\n\n/**\n * Tool definitions for check-related operations\n */\nexport const checkTools = {\n listChecks: {\n name: \"listChecks\",\n description: `List all checks from ${join(RULESYNC_CHECKS_RELATIVE_DIR_PATH, \"*.md\")} with their frontmatter.`,\n parameters: checkToolSchemas.listChecks,\n execute: async () => {\n const checks = await listChecks();\n const output = { checks };\n return JSON.stringify(output, null, 2);\n },\n },\n getCheck: {\n name: \"getCheck\",\n description:\n \"Get detailed information about a specific check. relativePathFromCwd parameter is required.\",\n parameters: checkToolSchemas.getCheck,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await getCheck({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n putCheck: {\n name: \"putCheck\",\n description:\n \"Create or update a check (upsert operation). relativePathFromCwd, frontmatter, and body parameters are required.\",\n parameters: checkToolSchemas.putCheck,\n execute: async (args: {\n relativePathFromCwd: string;\n frontmatter: RulesyncCheckFrontmatter;\n body: string;\n }) => {\n const result = await putCheck({\n relativePathFromCwd: args.relativePathFromCwd,\n frontmatter: args.frontmatter,\n body: args.body,\n });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteCheck: {\n name: \"deleteCheck\",\n description: \"Delete a check file. relativePathFromCwd parameter is required.\",\n parameters: checkToolSchemas.deleteCheck,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await deleteCheck({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { basename, join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { RULESYNC_COMMANDS_RELATIVE_DIR_PATH } from \"../constants/rulesync-paths.js\";\nimport {\n RulesyncCommand,\n type RulesyncCommandFrontmatter,\n RulesyncCommandFrontmatterSchema,\n} from \"../features/commands/rulesync-command.js\";\nimport { formatError } from \"../utils/error.js\";\nimport {\n checkPathTraversal,\n ensureDir,\n listDirectoryFiles,\n removeFile,\n writeFileContent,\n} from \"../utils/file.js\";\nimport { stringifyFrontmatter } from \"../utils/frontmatter.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\n\nconst logger = new ConsoleLogger({ verbose: false, silent: true });\n\nconst maxCommandSizeBytes = 1024 * 1024; // 1MB\nconst maxCommandsCount = 1000;\n\n/**\n * Tool to list all commands from .rulesync/commands/*.md\n */\nasync function listCommands(): Promise<\n Array<{\n relativePathFromCwd: string;\n frontmatter: RulesyncCommandFrontmatter;\n }>\n> {\n const commandsDir = join(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);\n\n try {\n const files = await listDirectoryFiles(commandsDir);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const commands = await Promise.all(\n mdFiles.map(async (file) => {\n try {\n checkPathTraversal({\n relativePath: file,\n intendedRootDir: commandsDir,\n });\n\n const command = await RulesyncCommand.fromFile({\n relativeFilePath: file,\n });\n\n const frontmatter = command.getFrontmatter();\n\n return {\n relativePathFromCwd: join(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, file),\n frontmatter,\n };\n } catch (error) {\n logger.error(`Failed to read command file ${file}: ${formatError(error)}`);\n return null;\n }\n }),\n );\n\n // Filter out null values (failed reads)\n return commands.filter((command): command is NonNullable<typeof command> => command !== null);\n } catch (error) {\n logger.error(\n `Failed to read commands directory (${RULESYNC_COMMANDS_RELATIVE_DIR_PATH}): ${formatError(error)}`,\n );\n return [];\n }\n}\n\n/**\n * Tool to get detailed information about a specific command\n */\nasync function getCommand({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncCommandFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n try {\n const command = await RulesyncCommand.fromFile({\n relativeFilePath: filename,\n });\n\n return {\n relativePathFromCwd: join(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),\n frontmatter: command.getFrontmatter(),\n body: command.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to read command file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to create or update a command (upsert operation)\n */\nasync function putCommand({\n relativePathFromCwd,\n frontmatter,\n body,\n}: {\n relativePathFromCwd: string;\n frontmatter: RulesyncCommandFrontmatter;\n body: string;\n}): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncCommandFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n // Check file size constraint\n const estimatedSize = JSON.stringify(frontmatter).length + body.length;\n if (estimatedSize > maxCommandSizeBytes) {\n throw new Error(\n `Command size ${estimatedSize} bytes exceeds maximum ${maxCommandSizeBytes} bytes (1MB) for ${relativePathFromCwd}`,\n );\n }\n\n try {\n // Check command count constraint\n const existingCommands = await listCommands();\n const isUpdate = existingCommands.some(\n (command) =>\n command.relativePathFromCwd === join(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),\n );\n\n if (!isUpdate && existingCommands.length >= maxCommandsCount) {\n throw new Error(\n `Maximum number of commands (${maxCommandsCount}) reached in ${RULESYNC_COMMANDS_RELATIVE_DIR_PATH}`,\n );\n }\n\n // Create a new RulesyncCommand instance\n const fileContent = stringifyFrontmatter(body, frontmatter);\n const command = new RulesyncCommand({\n outputRoot: process.cwd(),\n relativeDirPath: RULESYNC_COMMANDS_RELATIVE_DIR_PATH,\n relativeFilePath: filename,\n frontmatter,\n body,\n fileContent,\n validate: true,\n });\n\n // Ensure directory exists\n const commandsDir = join(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);\n await ensureDir(commandsDir);\n\n // Write the file\n await writeFileContent(command.getFilePath(), command.getFileContent());\n\n return {\n relativePathFromCwd: join(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),\n frontmatter: command.getFrontmatter(),\n body: command.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to write command file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to delete a command\n */\nasync function deleteCommand({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n const fullPath = join(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename);\n\n try {\n await removeFile(fullPath);\n\n return {\n relativePathFromCwd: join(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),\n };\n } catch (error) {\n throw new Error(`Failed to delete command file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Schema for command-related tool parameters\n */\nconst commandToolSchemas = {\n listCommands: z.object({}),\n getCommand: z.object({\n relativePathFromCwd: z.string(),\n }),\n putCommand: z.object({\n relativePathFromCwd: z.string(),\n frontmatter: RulesyncCommandFrontmatterSchema,\n body: z.string(),\n }),\n deleteCommand: z.object({\n relativePathFromCwd: z.string(),\n }),\n} as const;\n\n/**\n * Tool definitions for command-related operations\n */\nexport const commandTools = {\n listCommands: {\n name: \"listCommands\",\n description: `List all commands from ${join(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, \"*.md\")} with their frontmatter.`,\n parameters: commandToolSchemas.listCommands,\n execute: async () => {\n const commands = await listCommands();\n const output = { commands };\n return JSON.stringify(output, null, 2);\n },\n },\n getCommand: {\n name: \"getCommand\",\n description:\n \"Get detailed information about a specific command. relativePathFromCwd parameter is required.\",\n parameters: commandToolSchemas.getCommand,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await getCommand({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n putCommand: {\n name: \"putCommand\",\n description:\n \"Create or update a command (upsert operation). relativePathFromCwd, frontmatter, and body parameters are required.\",\n parameters: commandToolSchemas.putCommand,\n execute: async (args: {\n relativePathFromCwd: string;\n frontmatter: RulesyncCommandFrontmatter;\n body: string;\n }) => {\n const result = await putCommand({\n relativePathFromCwd: args.relativePathFromCwd,\n frontmatter: args.frontmatter,\n body: args.body,\n });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteCommand: {\n name: \"deleteCommand\",\n description: \"Delete a command file. relativePathFromCwd parameter is required.\",\n parameters: commandToolSchemas.deleteCommand,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await deleteCommand({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { z } from \"zod/mini\";\n\nimport { ConfigResolver } from \"../config/config-resolver.js\";\nimport { Config } from \"../config/config.js\";\nimport { convertFromTool, type ConvertResult } from \"../lib/convert.js\";\nimport { type RulesyncFeatures } from \"../types/features.js\";\nimport { ALL_TOOL_TARGETS, type ToolTarget, ToolTargetSchema } from \"../types/tool-targets.js\";\nimport { formatError } from \"../utils/error.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\nimport { calculateTotalCount } from \"../utils/result.js\";\nimport { type McpResultCounts } from \"./types.js\";\n\n/**\n * Schema for convert options\n * Excluded parameters:\n * - outputRoots: Always use [process.cwd()] in MCP context\n * - verbose: Meaningless in MCP (no console output)\n * - silent: Meaningless in MCP\n * - configPath: Always use default path from process.cwd()\n */\nexport const convertOptionsSchema = z.object({\n from: z.string(),\n to: z.array(z.string()),\n features: z.optional(z.array(z.string())),\n global: z.optional(z.boolean()),\n dryRun: z.optional(z.boolean()),\n});\n\nexport type ConvertOptions = z.infer<typeof convertOptionsSchema>;\n\nexport type McpConvertResult = {\n success: boolean;\n result?: McpResultCounts;\n config?: {\n from: string;\n to: string[];\n features: string[];\n global: boolean;\n dryRun: boolean;\n };\n error?: string;\n};\n\nfunction parseToolTarget(value: string, label: string): ToolTarget {\n const result = ToolTargetSchema.safeParse(value);\n if (!result.success) {\n throw new Error(\n `Invalid ${label} tool '${value}'. Must be one of: ${ALL_TOOL_TARGETS.join(\", \")}`,\n );\n }\n return result.data;\n}\n\n/**\n * Execute the rulesync convert command via MCP\n * Configuration priority: MCP Parameters > rulesync.local.jsonc > rulesync.jsonc > Default values\n */\nexport async function executeConvert(options: ConvertOptions): Promise<McpConvertResult> {\n try {\n // Validate from\n if (!options.from) {\n return {\n success: false,\n error: \"from is required. Please specify a source tool to convert from.\",\n };\n }\n\n // Validate to\n if (!options.to || options.to.length === 0) {\n return {\n success: false,\n error: \"to is required and must not be empty. Please specify destination tools.\",\n };\n }\n\n const fromTool = parseToolTarget(options.from, \"source\");\n const toToolsRaw = options.to.map((t) => parseToolTarget(t, \"destination\"));\n const toTools = Array.from(new Set(toToolsRaw));\n\n if (toTools.includes(fromTool)) {\n return {\n success: false,\n error:\n `Destination tools must not include the source tool '${fromTool}'. ` +\n `Converting a tool onto itself is likely a mistake and may cause lossy round-trips.`,\n };\n }\n\n // Resolve config with MCP parameters taking precedence\n // ConfigResolver handles: CLI options > rulesync.local.jsonc > rulesync.jsonc > defaults\n // In MCP context, options act as CLI options (highest priority)\n // Pass both source and destinations as `targets` so per-target feature maps\n // in `rulesync.jsonc` are honored for every tool involved. Default features\n // to `*` so every feature that both tools support is attempted.\n const config = await ConfigResolver.resolve({\n targets: [fromTool, ...toTools],\n features: (options.features ?? [\"*\"]) as RulesyncFeatures,\n global: options.global,\n dryRun: options.dryRun,\n // Always use default outputRoots (process.cwd()) and configPath\n // verbose and silent are meaningless in MCP context\n verbose: false,\n silent: true,\n });\n\n const logger = new ConsoleLogger({ verbose: false, silent: true });\n const convertResult = await convertFromTool({ config, fromTool, toTools, logger });\n\n return buildSuccessResponse({ convertResult, config, fromTool, toTools });\n } catch (error) {\n return {\n success: false,\n error: formatError(error),\n };\n }\n}\n\nfunction buildSuccessResponse(params: {\n convertResult: ConvertResult;\n config: Config;\n fromTool: ToolTarget;\n toTools: ToolTarget[];\n}): McpConvertResult {\n const { convertResult, config, fromTool, toTools } = params;\n\n const totalCount = calculateTotalCount(convertResult);\n\n return {\n success: true,\n result: {\n rulesCount: convertResult.rulesCount,\n ignoreCount: convertResult.ignoreCount,\n mcpCount: convertResult.mcpCount,\n commandsCount: convertResult.commandsCount,\n subagentsCount: convertResult.subagentsCount,\n skillsCount: convertResult.skillsCount,\n hooksCount: convertResult.hooksCount,\n permissionsCount: convertResult.permissionsCount,\n checksCount: convertResult.checksCount,\n totalCount,\n },\n config: {\n from: fromTool,\n to: toTools,\n features: config.getFeatures(),\n global: config.getGlobal(),\n dryRun: config.isPreviewMode(),\n },\n };\n}\n\nconst convertToolSchemas = {\n executeConvert: convertOptionsSchema,\n};\n\nexport const convertTools = {\n executeConvert: {\n name: \"executeConvert\",\n description:\n \"Execute the rulesync convert command to convert configuration files between AI tools without writing intermediate .rulesync/ files. Requires a source tool (from) and one or more destination tools (to).\",\n parameters: convertToolSchemas.executeConvert,\n execute: async (options: ConvertOptions): Promise<string> => {\n const result = await executeConvert(options);\n return JSON.stringify(result, null, 2);\n },\n },\n};\n","import { z } from \"zod/mini\";\n\nimport { ConfigResolver } from \"../config/config-resolver.js\";\nimport { Config } from \"../config/config.js\";\nimport { checkRulesyncDirExists, generate, type GenerateResult } from \"../lib/generate.js\";\nimport { type RulesyncFeatures } from \"../types/features.js\";\nimport { type RulesyncTargets } from \"../types/tool-targets.js\";\nimport { formatError } from \"../utils/error.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\nimport { calculateTotalCount } from \"../utils/result.js\";\nimport { type McpResultCounts } from \"./types.js\";\n\n/**\n * Schema for generate options\n * Excluded parameters:\n * - outputRoots: Always use [process.cwd()] in MCP context\n * - verbose: Meaningless in MCP (no console output)\n * - silent: Meaningless in MCP\n * - configPath: Always use default path from process.cwd()\n */\nexport const generateOptionsSchema = z.object({\n targets: z.optional(z.array(z.string())),\n features: z.optional(z.array(z.string())),\n delete: z.optional(z.boolean()),\n global: z.optional(z.boolean()),\n simulateCommands: z.optional(z.boolean()),\n simulateSubagents: z.optional(z.boolean()),\n simulateSkills: z.optional(z.boolean()),\n});\n\nexport type GenerateOptions = z.infer<typeof generateOptionsSchema>;\n\nexport type McpGenerateResult = {\n success: boolean;\n /**\n * Human-readable summary of the outcome. Clarifies that a `totalCount` of 0\n * means \"already up to date\" (success with nothing to write) rather than a\n * failure, since `generate` is idempotent and only writes changed files.\n */\n message?: string;\n result?: McpResultCounts;\n config?: {\n targets: string[];\n features: string[];\n global: boolean;\n delete: boolean;\n simulateCommands: boolean;\n simulateSubagents: boolean;\n simulateSkills: boolean;\n };\n error?: string;\n};\n\n/**\n * Execute the rulesync generate command via MCP\n * Configuration priority: MCP Parameters > rulesync.local.jsonc > rulesync.jsonc > Default values\n */\nexport async function executeGenerate(options: GenerateOptions = {}): Promise<McpGenerateResult> {\n try {\n // Check if .rulesync directory exists\n const exists = await checkRulesyncDirExists({ inputRoot: process.cwd() });\n if (!exists) {\n return {\n success: false,\n error:\n \".rulesync directory does not exist. Please run 'rulesync init' first or create the directory manually.\",\n };\n }\n\n // Resolve config with MCP parameters taking precedence\n // ConfigResolver handles: CLI options > rulesync.local.jsonc > rulesync.jsonc > defaults\n // In MCP context, options act as CLI options (highest priority)\n const config = await ConfigResolver.resolve({\n targets: options.targets as RulesyncTargets | undefined,\n features: options.features as RulesyncFeatures | undefined,\n delete: options.delete,\n global: options.global,\n simulateCommands: options.simulateCommands,\n simulateSubagents: options.simulateSubagents,\n simulateSkills: options.simulateSkills,\n // Always use default outputRoots (process.cwd()) and configPath\n // verbose and silent are meaningless in MCP context\n verbose: false,\n silent: true,\n });\n\n const logger = new ConsoleLogger({ verbose: false, silent: true });\n const generateResult = await generate({ config, logger });\n\n return buildSuccessResponse({ generateResult, config });\n } catch (error) {\n return {\n success: false,\n error: formatError(error),\n };\n }\n}\n\n/**\n * Build a human-readable summary of a successful generation.\n *\n * `generate` is idempotent: `totalCount` reflects only files whose content\n * actually changed on disk, so a count of 0 is a normal \"nothing to update\"\n * outcome — not a failure. The message makes that explicit so MCP callers do\n * not misread a zero count as a broken generate.\n */\nfunction buildGenerateMessage(params: { totalCount: number; config: Config }): string {\n const { totalCount, config } = params;\n const targets = config.getTargets().join(\", \");\n const features = config.getFeatures().join(\", \");\n\n if (totalCount > 0) {\n return `Generated ${totalCount} file(s) for targets [${targets}] and features [${features}].`;\n }\n\n return (\n `No files needed updating for targets [${targets}] and features [${features}]. ` +\n `'generate' only writes files whose content changed, so a totalCount of 0 means the ` +\n `outputs are already up to date — this is a successful no-op, not a failure.`\n );\n}\n\nfunction buildSuccessResponse(params: {\n generateResult: GenerateResult;\n config: Config;\n}): McpGenerateResult {\n const { generateResult, config } = params;\n\n const totalCount = calculateTotalCount(generateResult);\n\n return {\n success: true,\n message: buildGenerateMessage({ totalCount, config }),\n result: {\n rulesCount: generateResult.rulesCount,\n ignoreCount: generateResult.ignoreCount,\n mcpCount: generateResult.mcpCount,\n commandsCount: generateResult.commandsCount,\n subagentsCount: generateResult.subagentsCount,\n skillsCount: generateResult.skillsCount,\n hooksCount: generateResult.hooksCount,\n permissionsCount: generateResult.permissionsCount,\n checksCount: generateResult.checksCount,\n activationCount: generateResult.activationCount,\n totalCount,\n },\n config: {\n targets: config.getTargets(),\n features: config.getFeatures(),\n global: config.getGlobal(),\n delete: config.getDelete(),\n simulateCommands: config.getSimulateCommands(),\n simulateSubagents: config.getSimulateSubagents(),\n simulateSkills: config.getSimulateSkills(),\n },\n };\n}\n\nconst generateToolSchemas = {\n executeGenerate: generateOptionsSchema,\n};\n\nexport const generateTools = {\n executeGenerate: {\n name: \"executeGenerate\",\n description:\n \"Execute the rulesync generate command to create output files for AI tools. Uses rulesync.jsonc settings by default, but options can override them. Idempotent: only files whose content changed are written, so a totalCount of 0 means the outputs are already up to date (a successful no-op), not a failure. See the 'message' field for a human-readable summary.\",\n parameters: generateToolSchemas.executeGenerate,\n execute: async (options: GenerateOptions = {}): Promise<string> => {\n const result = await executeGenerate(options);\n return JSON.stringify(result, null, 2);\n },\n },\n};\n","import { join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { RULESYNC_HOOKS_RELATIVE_FILE_PATH } from \"../constants/rulesync-paths.js\";\nimport { RulesyncHooks } from \"../features/hooks/rulesync-hooks.js\";\nimport { formatError } from \"../utils/error.js\";\nimport { ensureDir, removeFile, writeFileContent } from \"../utils/file.js\";\nimport { parseJsonc } from \"../utils/jsonc.js\";\nimport {\n getRulesyncSourceCandidates,\n resolveRulesyncSourceWritePath,\n} from \"../utils/rulesync-source-path.js\";\n\nconst maxHooksSizeBytes = 1024 * 1024; // 1MB\n\n/**\n * Tool to get the hooks configuration file\n */\nasync function getHooksFile(): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n try {\n const rulesyncHooks = await RulesyncHooks.fromFile({\n validate: true,\n });\n\n const relativePathFromCwd = join(\n rulesyncHooks.getRelativeDirPath(),\n rulesyncHooks.getRelativeFilePath(),\n );\n\n return {\n relativePathFromCwd,\n content: rulesyncHooks.getFileContent(),\n };\n } catch (error) {\n throw new Error(\n `Failed to read hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to create or update the hooks configuration file (upsert operation)\n */\nasync function putHooksFile({ content }: { content: string }): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n // Check file size constraint\n if (content.length > maxHooksSizeBytes) {\n throw new Error(\n `Hooks file size ${content.length} bytes exceeds maximum ${maxHooksSizeBytes} bytes (1MB) for ${RULESYNC_HOOKS_RELATIVE_FILE_PATH}`,\n );\n }\n\n // Validate JSONC format\n try {\n parseJsonc(content);\n } catch (error) {\n throw new Error(\n `Invalid JSONC format in hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n\n try {\n const outputRoot = process.cwd();\n const paths = RulesyncHooks.getSettablePaths();\n const { relativeDirPath, relativeFilePath } = await resolveRulesyncSourceWritePath({\n outputRoot,\n paths,\n });\n const fullPath = join(outputRoot, relativeDirPath, relativeFilePath);\n\n // Create a RulesyncHooks instance to validate the content\n const rulesyncHooks = new RulesyncHooks({\n outputRoot,\n relativeDirPath,\n relativeFilePath,\n fileContent: content,\n validate: true,\n });\n\n // Ensure directory exists\n await ensureDir(join(outputRoot, relativeDirPath));\n\n // Write the file\n await writeFileContent(fullPath, content);\n\n const relativePathFromCwd = join(relativeDirPath, relativeFilePath);\n\n return {\n relativePathFromCwd,\n content: rulesyncHooks.getFileContent(),\n };\n } catch (error) {\n throw new Error(\n `Failed to write hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to delete the hooks configuration file\n */\nasync function deleteHooksFile(): Promise<{\n relativePathFromCwd: string;\n}> {\n try {\n const outputRoot = process.cwd();\n const paths = RulesyncHooks.getSettablePaths();\n\n for (const candidate of getRulesyncSourceCandidates({ paths })) {\n await removeFile(join(outputRoot, candidate.relativeDirPath, candidate.relativeFilePath));\n }\n\n const relativePathFromCwd = join(\n paths.recommended.relativeDirPath,\n paths.recommended.relativeFilePath,\n );\n\n return {\n relativePathFromCwd,\n };\n } catch (error) {\n throw new Error(\n `Failed to delete hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Schema for hooks-related tool parameters\n */\nconst hooksToolSchemas = {\n getHooksFile: z.object({}),\n putHooksFile: z.object({\n content: z.string(),\n }),\n deleteHooksFile: z.object({}),\n} as const;\n\n/**\n * Tool definitions for hooks-related operations\n */\nexport const hooksTools = {\n getHooksFile: {\n name: \"getHooksFile\",\n description: `Get the hooks configuration file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}).`,\n parameters: hooksToolSchemas.getHooksFile,\n execute: async () => {\n const result = await getHooksFile();\n return JSON.stringify(result, null, 2);\n },\n },\n putHooksFile: {\n name: \"putHooksFile\",\n description:\n \"Create or update the hooks configuration file (upsert operation). content parameter is required and must be valid JSONC.\",\n parameters: hooksToolSchemas.putHooksFile,\n execute: async (args: { content: string }) => {\n const result = await putHooksFile({ content: args.content });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteHooksFile: {\n name: \"deleteHooksFile\",\n description: \"Delete the hooks configuration file.\",\n parameters: hooksToolSchemas.deleteHooksFile,\n execute: async () => {\n const result = await deleteHooksFile();\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport {\n RULESYNC_AIIGNORE_RELATIVE_FILE_PATH,\n RULESYNC_IGNORE_RELATIVE_FILE_PATH,\n} from \"../constants/rulesync-paths.js\";\nimport { formatError } from \"../utils/error.js\";\nimport { ensureDir, readFileContent, removeFile, writeFileContent } from \"../utils/file.js\";\n\nconst maxIgnoreFileSizeBytes = 100 * 1024; // 100KB\n\n/**\n * Tool to get the content of .rulesync/.aiignore file\n */\nasync function getIgnoreFile(): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n const ignoreFilePath = join(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);\n\n try {\n const content = await readFileContent(ignoreFilePath);\n\n return {\n relativePathFromCwd: RULESYNC_AIIGNORE_RELATIVE_FILE_PATH,\n content,\n };\n } catch (error) {\n throw new Error(\n `Failed to read ignore file (${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to create or update the .rulesync/.aiignore file (upsert operation)\n */\nasync function putIgnoreFile({ content }: { content: string }): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n const ignoreFilePath = join(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);\n\n // Check file size constraint\n const contentSizeBytes = Buffer.byteLength(content, \"utf8\");\n if (contentSizeBytes > maxIgnoreFileSizeBytes) {\n throw new Error(\n `Ignore file size ${contentSizeBytes} bytes exceeds maximum ${maxIgnoreFileSizeBytes} bytes (100KB) for ${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}`,\n );\n }\n\n try {\n // Ensure parent directory exists (should be cwd, but just to be safe)\n await ensureDir(process.cwd());\n\n // Write the file\n await writeFileContent(ignoreFilePath, content);\n\n return {\n relativePathFromCwd: RULESYNC_AIIGNORE_RELATIVE_FILE_PATH,\n content,\n };\n } catch (error) {\n throw new Error(\n `Failed to write ignore file (${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to delete the .rulesyncignore (legacy) and .rulesync/.aiignore (recommended) files\n */\nasync function deleteIgnoreFile(): Promise<{\n relativePathFromCwd: string;\n}> {\n const aiignorePath = join(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);\n const legacyIgnorePath = join(process.cwd(), RULESYNC_IGNORE_RELATIVE_FILE_PATH);\n\n try {\n // Attempt to remove both files. The removeFile helper is expected to be idempotent\n // (no-throw for non-existent files). If any real IO error happens, the Promise.all\n // will reject and we propagate an error.\n await Promise.all([removeFile(aiignorePath), removeFile(legacyIgnorePath)]);\n\n return {\n // Keep the historical return shape — point to the recommended file path\n // for backward compatibility.\n relativePathFromCwd: RULESYNC_AIIGNORE_RELATIVE_FILE_PATH,\n };\n } catch (error) {\n throw new Error(\n `Failed to delete ignore files (${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}, ${RULESYNC_IGNORE_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Schema for ignore-related tool parameters\n */\nconst ignoreToolSchemas = {\n getIgnoreFile: z.object({}),\n putIgnoreFile: z.object({\n content: z.string(),\n }),\n deleteIgnoreFile: z.object({}),\n} as const;\n\n/**\n * Tool definitions for ignore-related operations\n */\nexport const ignoreTools = {\n getIgnoreFile: {\n name: \"getIgnoreFile\",\n description: \"Get the content of the .rulesyncignore file from the project root.\",\n parameters: ignoreToolSchemas.getIgnoreFile,\n execute: async () => {\n const result = await getIgnoreFile();\n return JSON.stringify(result, null, 2);\n },\n },\n putIgnoreFile: {\n name: \"putIgnoreFile\",\n description:\n \"Create or update the .rulesync/.aiignore file (upsert operation). content parameter is required.\",\n parameters: ignoreToolSchemas.putIgnoreFile,\n execute: async (args: { content: string }) => {\n const result = await putIgnoreFile({ content: args.content });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteIgnoreFile: {\n name: \"deleteIgnoreFile\",\n description: \"Delete the .rulesyncignore and .rulesync/.aiignore files.\",\n parameters: ignoreToolSchemas.deleteIgnoreFile,\n execute: async () => {\n const result = await deleteIgnoreFile();\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { z } from \"zod/mini\";\n\nimport { ConfigResolver } from \"../config/config-resolver.js\";\nimport { Config } from \"../config/config.js\";\nimport { importFromTool, type ImportResult } from \"../lib/import.js\";\nimport { type RulesyncFeatures } from \"../types/features.js\";\nimport { type RulesyncTargets, type ToolTarget } from \"../types/tool-targets.js\";\nimport { formatError } from \"../utils/error.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\nimport { calculateTotalCount } from \"../utils/result.js\";\nimport { type McpResultCounts } from \"./types.js\";\n\n/**\n * Schema for import options\n * Note: Import requires exactly one target tool\n * Excluded parameters:\n * - outputRoots: Always use [process.cwd()] in MCP context\n * - verbose: Meaningless in MCP (no console output)\n * - silent: Meaningless in MCP\n * - configPath: Always use default path from process.cwd()\n * - delete: Not applicable to import\n * - simulateCommands/simulateSubagents/simulateSkills: Not applicable to import\n */\nexport const importOptionsSchema = z.object({\n target: z.string(),\n features: z.optional(z.array(z.string())),\n global: z.optional(z.boolean()),\n});\n\nexport type ImportOptions = z.infer<typeof importOptionsSchema>;\n\nexport type McpImportResult = {\n success: boolean;\n result?: McpResultCounts;\n config?: {\n target: string;\n features: string[];\n global: boolean;\n };\n error?: string;\n};\n\n/**\n * Execute the rulesync import command via MCP\n * Configuration priority: MCP Parameters > rulesync.local.jsonc > rulesync.jsonc > Default values\n */\nexport async function executeImport(options: ImportOptions): Promise<McpImportResult> {\n try {\n // Validate target\n if (!options.target) {\n return {\n success: false,\n error: \"target is required. Please specify a tool to import from.\",\n };\n }\n\n // Resolve config with MCP parameters taking precedence\n // ConfigResolver handles: CLI options > rulesync.local.jsonc > rulesync.jsonc > defaults\n // In MCP context, options act as CLI options (highest priority)\n const config = await ConfigResolver.resolve({\n targets: [options.target] as RulesyncTargets,\n features: options.features as RulesyncFeatures | undefined,\n global: options.global,\n // Always use default outputRoots (process.cwd()) and configPath\n // verbose and silent are meaningless in MCP context\n verbose: false,\n silent: true,\n });\n\n const tool = config.getTargets()[0] as ToolTarget;\n\n const logger = new ConsoleLogger({ verbose: false, silent: true });\n const importResult = await importFromTool({ config, tool, logger });\n\n return buildSuccessResponse({ importResult, config, tool });\n } catch (error) {\n return {\n success: false,\n error: formatError(error),\n };\n }\n}\n\nfunction buildSuccessResponse(params: {\n importResult: ImportResult;\n config: Config;\n tool: ToolTarget;\n}): McpImportResult {\n const { importResult, config, tool } = params;\n\n const totalCount = calculateTotalCount(importResult);\n\n return {\n success: true,\n result: {\n rulesCount: importResult.rulesCount,\n ignoreCount: importResult.ignoreCount,\n mcpCount: importResult.mcpCount,\n commandsCount: importResult.commandsCount,\n subagentsCount: importResult.subagentsCount,\n skillsCount: importResult.skillsCount,\n hooksCount: importResult.hooksCount,\n permissionsCount: importResult.permissionsCount,\n checksCount: importResult.checksCount,\n totalCount,\n },\n config: {\n target: tool,\n features: config.getFeatures(),\n global: config.getGlobal(),\n },\n };\n}\n\nconst importToolSchemas = {\n executeImport: importOptionsSchema,\n};\n\nexport const importTools = {\n executeImport: {\n name: \"executeImport\",\n description:\n \"Execute the rulesync import command to import configuration files from an AI tool into .rulesync directory. Requires exactly one target tool to import from.\",\n parameters: importToolSchemas.executeImport,\n execute: async (options: ImportOptions): Promise<string> => {\n const result = await executeImport(options);\n return JSON.stringify(result, null, 2);\n },\n },\n};\n","import { join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { RULESYNC_MCP_RELATIVE_FILE_PATH } from \"../constants/rulesync-paths.js\";\nimport { RulesyncMcp } from \"../features/mcp/rulesync-mcp.js\";\nimport { formatError } from \"../utils/error.js\";\nimport { ensureDir, removeFile, writeFileContent } from \"../utils/file.js\";\nimport { parseJsonc } from \"../utils/jsonc.js\";\nimport {\n getRulesyncSourceCandidates,\n resolveRulesyncSourceWritePath,\n} from \"../utils/rulesync-source-path.js\";\n\nconst maxMcpSizeBytes = 1024 * 1024; // 1MB\n\n/**\n * Tool to get the MCP configuration file\n */\nasync function getMcpFile(): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n try {\n const rulesyncMcp = await RulesyncMcp.fromFile({\n validate: true,\n });\n\n const relativePathFromCwd = join(\n rulesyncMcp.getRelativeDirPath(),\n rulesyncMcp.getRelativeFilePath(),\n );\n\n return {\n relativePathFromCwd,\n content: rulesyncMcp.getFileContent(),\n };\n } catch (error) {\n throw new Error(\n `Failed to read MCP file (${RULESYNC_MCP_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to create or update the MCP configuration file (upsert operation)\n */\nasync function putMcpFile({ content }: { content: string }): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n // Check file size constraint\n if (content.length > maxMcpSizeBytes) {\n throw new Error(\n `MCP file size ${content.length} bytes exceeds maximum ${maxMcpSizeBytes} bytes (1MB) for ${RULESYNC_MCP_RELATIVE_FILE_PATH}`,\n );\n }\n\n // Validate JSONC format\n try {\n parseJsonc(content);\n } catch (error) {\n throw new Error(\n `Invalid JSONC format in MCP file (${RULESYNC_MCP_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n\n try {\n const outputRoot = process.cwd();\n const paths = RulesyncMcp.getSettablePaths();\n const { relativeDirPath, relativeFilePath } = await resolveRulesyncSourceWritePath({\n outputRoot,\n paths,\n });\n const fullPath = join(outputRoot, relativeDirPath, relativeFilePath);\n\n // Create a RulesyncMcp instance to validate the content\n const rulesyncMcp = new RulesyncMcp({\n outputRoot,\n relativeDirPath,\n relativeFilePath,\n fileContent: content,\n validate: true,\n });\n\n // Ensure directory exists\n await ensureDir(join(outputRoot, relativeDirPath));\n\n // Write the file\n await writeFileContent(fullPath, content);\n\n const relativePathFromCwd = join(relativeDirPath, relativeFilePath);\n\n return {\n relativePathFromCwd,\n content: rulesyncMcp.getFileContent(),\n };\n } catch (error) {\n throw new Error(\n `Failed to write MCP file (${RULESYNC_MCP_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to delete the MCP configuration file\n */\nasync function deleteMcpFile(): Promise<{\n relativePathFromCwd: string;\n}> {\n try {\n const outputRoot = process.cwd();\n const paths = RulesyncMcp.getSettablePaths();\n\n for (const candidate of getRulesyncSourceCandidates({ paths })) {\n await removeFile(join(outputRoot, candidate.relativeDirPath, candidate.relativeFilePath));\n }\n\n const relativePathFromCwd = join(\n paths.recommended.relativeDirPath,\n paths.recommended.relativeFilePath,\n );\n\n return {\n relativePathFromCwd,\n };\n } catch (error) {\n throw new Error(\n `Failed to delete MCP file (${RULESYNC_MCP_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Schema for MCP-related tool parameters\n */\nconst mcpToolSchemas = {\n getMcpFile: z.object({}),\n putMcpFile: z.object({\n content: z.string(),\n }),\n deleteMcpFile: z.object({}),\n} as const;\n\n/**\n * Tool definitions for MCP-related operations\n */\nexport const mcpTools = {\n getMcpFile: {\n name: \"getMcpFile\",\n description: `Get the MCP configuration file (${RULESYNC_MCP_RELATIVE_FILE_PATH}).`,\n parameters: mcpToolSchemas.getMcpFile,\n execute: async () => {\n const result = await getMcpFile();\n return JSON.stringify(result, null, 2);\n },\n },\n putMcpFile: {\n name: \"putMcpFile\",\n description:\n \"Create or update the MCP configuration file (upsert operation). content parameter is required and must be valid JSONC.\",\n parameters: mcpToolSchemas.putMcpFile,\n execute: async (args: { content: string }) => {\n const result = await putMcpFile({ content: args.content });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteMcpFile: {\n name: \"deleteMcpFile\",\n description: \"Delete the MCP configuration file.\",\n parameters: mcpToolSchemas.deleteMcpFile,\n execute: async () => {\n const result = await deleteMcpFile();\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH } from \"../constants/rulesync-paths.js\";\nimport { RulesyncPermissions } from \"../features/permissions/rulesync-permissions.js\";\nimport { formatError } from \"../utils/error.js\";\nimport { ensureDir, removeFile, writeFileContent } from \"../utils/file.js\";\nimport { parseJsonc } from \"../utils/jsonc.js\";\nimport {\n getRulesyncSourceCandidates,\n resolveRulesyncSourceWritePath,\n} from \"../utils/rulesync-source-path.js\";\n\nconst maxPermissionsSizeBytes = 1024 * 1024; // 1MB\n\n/**\n * Tool to get the permissions configuration file\n */\nasync function getPermissionsFile(): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n try {\n const rulesyncPermissions = await RulesyncPermissions.fromFile({\n validate: true,\n });\n\n const relativePathFromCwd = join(\n rulesyncPermissions.getRelativeDirPath(),\n rulesyncPermissions.getRelativeFilePath(),\n );\n\n return {\n relativePathFromCwd,\n content: rulesyncPermissions.getFileContent(),\n };\n } catch (error) {\n throw new Error(\n `Failed to read permissions file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to create or update the permissions configuration file (upsert operation)\n */\nasync function putPermissionsFile({ content }: { content: string }): Promise<{\n relativePathFromCwd: string;\n content: string;\n}> {\n // Check file size constraint\n if (content.length > maxPermissionsSizeBytes) {\n throw new Error(\n `Permissions file size ${content.length} bytes exceeds maximum ${maxPermissionsSizeBytes} bytes (1MB) for ${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}`,\n );\n }\n\n // Validate JSONC format\n try {\n parseJsonc(content);\n } catch (error) {\n throw new Error(\n `Invalid JSONC format in permissions file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n\n try {\n const outputRoot = process.cwd();\n const paths = RulesyncPermissions.getSettablePaths();\n const { relativeDirPath, relativeFilePath } = await resolveRulesyncSourceWritePath({\n outputRoot,\n paths,\n });\n const fullPath = join(outputRoot, relativeDirPath, relativeFilePath);\n\n // Create a RulesyncPermissions instance to validate the content\n const rulesyncPermissions = new RulesyncPermissions({\n outputRoot,\n relativeDirPath,\n relativeFilePath,\n fileContent: content,\n validate: true,\n });\n\n // Ensure directory exists\n await ensureDir(join(outputRoot, relativeDirPath));\n\n // Write the file\n await writeFileContent(fullPath, content);\n\n const relativePathFromCwd = join(relativeDirPath, relativeFilePath);\n\n return {\n relativePathFromCwd,\n content: rulesyncPermissions.getFileContent(),\n };\n } catch (error) {\n throw new Error(\n `Failed to write permissions file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to delete the permissions configuration file\n */\nasync function deletePermissionsFile(): Promise<{\n relativePathFromCwd: string;\n}> {\n try {\n const outputRoot = process.cwd();\n const paths = RulesyncPermissions.getSettablePaths();\n\n for (const candidate of getRulesyncSourceCandidates({ paths })) {\n await removeFile(join(outputRoot, candidate.relativeDirPath, candidate.relativeFilePath));\n }\n\n const relativePathFromCwd = join(\n paths.recommended.relativeDirPath,\n paths.recommended.relativeFilePath,\n );\n\n return {\n relativePathFromCwd,\n };\n } catch (error) {\n throw new Error(\n `Failed to delete permissions file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Schema for permissions-related tool parameters\n */\nconst permissionsToolSchemas = {\n getPermissionsFile: z.object({}),\n putPermissionsFile: z.object({\n content: z.string(),\n }),\n deletePermissionsFile: z.object({}),\n} as const;\n\n/**\n * Tool definitions for permissions-related operations\n */\nexport const permissionsTools = {\n getPermissionsFile: {\n name: \"getPermissionsFile\",\n description: `Get the permissions configuration file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}).`,\n parameters: permissionsToolSchemas.getPermissionsFile,\n execute: async () => {\n const result = await getPermissionsFile();\n return JSON.stringify(result, null, 2);\n },\n },\n putPermissionsFile: {\n name: \"putPermissionsFile\",\n description:\n \"Create or update the permissions configuration file (upsert operation). content parameter is required and must be valid JSONC.\",\n parameters: permissionsToolSchemas.putPermissionsFile,\n execute: async (args: { content: string }) => {\n const result = await putPermissionsFile({ content: args.content });\n return JSON.stringify(result, null, 2);\n },\n },\n deletePermissionsFile: {\n name: \"deletePermissionsFile\",\n description: \"Delete the permissions configuration file.\",\n parameters: permissionsToolSchemas.deletePermissionsFile,\n execute: async () => {\n const result = await deletePermissionsFile();\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { basename, join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { RULESYNC_RULES_RELATIVE_DIR_PATH } from \"../constants/rulesync-paths.js\";\nimport {\n RulesyncRule,\n type RulesyncRuleFrontmatter,\n type RulesyncRuleFrontmatterInput,\n RulesyncRuleFrontmatterSchema,\n} from \"../features/rules/rulesync-rule.js\";\nimport { formatError } from \"../utils/error.js\";\nimport {\n checkPathTraversal,\n ensureDir,\n listDirectoryFiles,\n removeFile,\n writeFileContent,\n} from \"../utils/file.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\n\nconst logger = new ConsoleLogger({ verbose: false, silent: true });\n\nconst maxRuleSizeBytes = 1024 * 1024; // 1MB\nconst maxRulesCount = 1000;\n\n/**\n * Tool to list all rules from .rulesync/rules/*.md\n */\nasync function listRules(): Promise<\n Array<{\n relativePathFromCwd: string;\n frontmatter: RulesyncRuleFrontmatter;\n }>\n> {\n const rulesDir = join(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);\n\n try {\n const files = await listDirectoryFiles(rulesDir);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const rules = await Promise.all(\n mdFiles.map(async (file) => {\n try {\n // Read the rule file using RulesyncRule\n const rule = await RulesyncRule.fromFile({\n relativeFilePath: file,\n validate: true,\n });\n\n const frontmatter = rule.getFrontmatter();\n\n return {\n relativePathFromCwd: join(RULESYNC_RULES_RELATIVE_DIR_PATH, file),\n frontmatter,\n };\n } catch (error) {\n logger.error(`Failed to read rule file ${file}: ${formatError(error)}`);\n return null;\n }\n }),\n );\n\n // Filter out null values (failed reads)\n return rules.filter((rule): rule is NonNullable<typeof rule> => rule !== null);\n } catch (error) {\n logger.error(\n `Failed to read rules directory (${RULESYNC_RULES_RELATIVE_DIR_PATH}): ${formatError(error)}`,\n );\n return [];\n }\n}\n\n/**\n * Tool to get detailed information about a specific rule\n */\nasync function getRule({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncRuleFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n try {\n const rule = await RulesyncRule.fromFile({\n relativeFilePath: filename,\n validate: true,\n });\n\n return {\n relativePathFromCwd: join(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),\n frontmatter: rule.getFrontmatter(),\n body: rule.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to read rule file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to create or update a rule (upsert operation)\n */\nasync function putRule({\n relativePathFromCwd,\n frontmatter,\n body,\n}: {\n relativePathFromCwd: string;\n frontmatter: RulesyncRuleFrontmatterInput;\n body: string;\n}): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncRuleFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n // Check file size constraint\n const estimatedSize = JSON.stringify(frontmatter).length + body.length;\n if (estimatedSize > maxRuleSizeBytes) {\n throw new Error(\n `Rule size ${estimatedSize} bytes exceeds maximum ${maxRuleSizeBytes} bytes (1MB) for ${relativePathFromCwd}`,\n );\n }\n\n try {\n // Check rule count constraint\n const existingRules = await listRules();\n const isUpdate = existingRules.some(\n (rule) => rule.relativePathFromCwd === join(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),\n );\n\n if (!isUpdate && existingRules.length >= maxRulesCount) {\n throw new Error(\n `Maximum number of rules (${maxRulesCount}) reached in ${RULESYNC_RULES_RELATIVE_DIR_PATH}`,\n );\n }\n\n // Create a new RulesyncRule instance\n const rule = new RulesyncRule({\n outputRoot: process.cwd(),\n relativeDirPath: RULESYNC_RULES_RELATIVE_DIR_PATH,\n relativeFilePath: filename,\n frontmatter,\n body,\n validate: true,\n });\n\n // Ensure directory exists\n const rulesDir = join(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);\n await ensureDir(rulesDir);\n\n // Write the file\n await writeFileContent(rule.getFilePath(), rule.getFileContent());\n\n return {\n relativePathFromCwd: join(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),\n frontmatter: rule.getFrontmatter(),\n body: rule.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to write rule file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to delete a rule\n */\nasync function deleteRule({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n const fullPath = join(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH, filename);\n\n try {\n await removeFile(fullPath);\n\n return {\n relativePathFromCwd: join(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),\n };\n } catch (error) {\n throw new Error(`Failed to delete rule file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Schema for rule-related tool parameters\n */\nconst ruleToolSchemas = {\n listRules: z.object({}),\n getRule: z.object({\n relativePathFromCwd: z.string(),\n }),\n putRule: z.object({\n relativePathFromCwd: z.string(),\n frontmatter: RulesyncRuleFrontmatterSchema,\n body: z.string(),\n }),\n deleteRule: z.object({\n relativePathFromCwd: z.string(),\n }),\n} as const;\n\n/**\n * Tool definitions for rule-related operations\n */\nexport const ruleTools = {\n listRules: {\n name: \"listRules\",\n description: `List all rules from ${join(RULESYNC_RULES_RELATIVE_DIR_PATH, \"*.md\")} with their frontmatter.`,\n parameters: ruleToolSchemas.listRules,\n execute: async () => {\n const rules = await listRules();\n const output = { rules };\n return JSON.stringify(output, null, 2);\n },\n },\n getRule: {\n name: \"getRule\",\n description:\n \"Get detailed information about a specific rule. relativePathFromCwd parameter is required.\",\n parameters: ruleToolSchemas.getRule,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await getRule({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n putRule: {\n name: \"putRule\",\n description:\n \"Create or update a rule (upsert operation). relativePathFromCwd, frontmatter, and body parameters are required.\",\n parameters: ruleToolSchemas.putRule,\n execute: async (args: {\n relativePathFromCwd: string;\n frontmatter: RulesyncRuleFrontmatterInput;\n body: string;\n }) => {\n const result = await putRule({\n relativePathFromCwd: args.relativePathFromCwd,\n frontmatter: args.frontmatter,\n body: args.body,\n });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteRule: {\n name: \"deleteRule\",\n description: \"Delete a rule file. relativePathFromCwd parameter is required.\",\n parameters: ruleToolSchemas.deleteRule,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await deleteRule({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { basename, dirname, join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { SKILL_FILE_NAME } from \"../constants/general.js\";\nimport { RULESYNC_SKILLS_RELATIVE_DIR_PATH } from \"../constants/rulesync-paths.js\";\nimport {\n RulesyncSkill,\n type RulesyncSkillFrontmatter,\n RulesyncSkillFrontmatterSchema,\n} from \"../features/skills/rulesync-skill.js\";\nimport { AiDirFile } from \"../types/ai-dir.js\";\nimport { formatError } from \"../utils/error.js\";\nimport {\n checkPathTraversal,\n directoryExists,\n ensureDir,\n findFilesByGlobs,\n removeDirectory,\n writeFileContent,\n} from \"../utils/file.js\";\nimport { stringifyFrontmatter } from \"../utils/frontmatter.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\n\nconst logger = new ConsoleLogger({ verbose: false, silent: true });\n\nconst maxSkillSizeBytes = 1024 * 1024; // 1MB\nconst maxSkillsCount = 1000;\n\n/**\n * Type for other files in MCP API (string-based for easier AI agent use)\n */\ntype McpSkillFile = {\n name: string;\n body: string;\n};\n\n/**\n * Convert AiDirFile to McpSkillFile\n */\nfunction aiDirFileToMcpSkillFile(file: AiDirFile): McpSkillFile {\n return {\n name: file.relativeFilePathToDirPath,\n body: file.fileBuffer.toString(\"utf-8\"),\n };\n}\n\n/**\n * Convert McpSkillFile to AiDirFile\n */\nfunction mcpSkillFileToAiDirFile(file: McpSkillFile): AiDirFile {\n return {\n relativeFilePathToDirPath: file.name,\n fileBuffer: Buffer.from(file.body, \"utf-8\"),\n };\n}\n\n/**\n * Extract directory name from relative path\n * @example \".rulesync/skills/my-skill\" -> \"my-skill\"\n */\nfunction extractDirName(relativeDirPathFromCwd: string): string {\n const dirName = basename(relativeDirPathFromCwd);\n if (!dirName) {\n throw new Error(`Invalid path: ${relativeDirPathFromCwd}`);\n }\n return dirName;\n}\n\n/**\n * Tool to list all skills from .rulesync/skills/\\*\\/SKILL.md\n */\nasync function listSkills(): Promise<\n Array<{\n relativeDirPathFromCwd: string;\n frontmatter: RulesyncSkillFrontmatter;\n }>\n> {\n const skillsDir = join(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH);\n\n try {\n // Find all skill directories (directories containing SKILL.md)\n const skillDirPaths = await findFilesByGlobs(join(skillsDir, \"*\"), { type: \"dir\" });\n\n const skills = await Promise.all(\n skillDirPaths.map(async (dirPath) => {\n const dirName = basename(dirPath);\n if (!dirName) return null;\n try {\n // Read the skill using RulesyncSkill\n const skill = await RulesyncSkill.fromDir({\n dirName,\n });\n\n const frontmatter = skill.getFrontmatter();\n\n return {\n relativeDirPathFromCwd: join(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),\n frontmatter,\n };\n } catch (error) {\n logger.error(`Failed to read skill directory ${dirName}: ${formatError(error)}`);\n return null;\n }\n }),\n );\n\n // Filter out null values (failed reads)\n return skills.filter((skill): skill is NonNullable<typeof skill> => skill !== null);\n } catch (error) {\n logger.error(\n `Failed to read skills directory (${RULESYNC_SKILLS_RELATIVE_DIR_PATH}): ${formatError(error)}`,\n );\n return [];\n }\n}\n\n/**\n * Tool to get detailed information about a specific skill\n */\nasync function getSkill({ relativeDirPathFromCwd }: { relativeDirPathFromCwd: string }): Promise<{\n relativeDirPathFromCwd: string;\n frontmatter: RulesyncSkillFrontmatter;\n body: string;\n otherFiles: McpSkillFile[];\n}> {\n checkPathTraversal({\n relativePath: relativeDirPathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const dirName = extractDirName(relativeDirPathFromCwd);\n\n try {\n const skill = await RulesyncSkill.fromDir({\n dirName,\n });\n\n return {\n relativeDirPathFromCwd: join(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),\n frontmatter: skill.getFrontmatter(),\n body: skill.getBody(),\n otherFiles: skill.getOtherFiles().map(aiDirFileToMcpSkillFile),\n };\n } catch (error) {\n throw new Error(\n `Failed to read skill directory ${relativeDirPathFromCwd}: ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to create or update a skill (upsert operation)\n */\nasync function putSkill({\n relativeDirPathFromCwd,\n frontmatter,\n body,\n otherFiles = [],\n}: {\n relativeDirPathFromCwd: string;\n frontmatter: RulesyncSkillFrontmatter;\n body: string;\n otherFiles?: McpSkillFile[];\n}): Promise<{\n relativeDirPathFromCwd: string;\n frontmatter: RulesyncSkillFrontmatter;\n body: string;\n otherFiles: McpSkillFile[];\n}> {\n checkPathTraversal({\n relativePath: relativeDirPathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const dirName = extractDirName(relativeDirPathFromCwd);\n\n // Check file size constraint\n const estimatedSize =\n JSON.stringify(frontmatter).length +\n body.length +\n otherFiles.reduce((acc, file) => acc + file.name.length + file.body.length, 0);\n if (estimatedSize > maxSkillSizeBytes) {\n throw new Error(\n `Skill size ${estimatedSize} bytes exceeds maximum ${maxSkillSizeBytes} bytes (1MB) for ${relativeDirPathFromCwd}`,\n );\n }\n\n try {\n // Check skill count constraint\n const existingSkills = await listSkills();\n const isUpdate = existingSkills.some(\n (skill) => skill.relativeDirPathFromCwd === join(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),\n );\n\n if (!isUpdate && existingSkills.length >= maxSkillsCount) {\n throw new Error(\n `Maximum number of skills (${maxSkillsCount}) reached in ${RULESYNC_SKILLS_RELATIVE_DIR_PATH}`,\n );\n }\n\n // Convert McpSkillFile to AiDirFile for RulesyncSkill\n const aiDirFiles = otherFiles.map(mcpSkillFileToAiDirFile);\n\n // Create a new RulesyncSkill instance for validation\n const skill = new RulesyncSkill({\n outputRoot: process.cwd(),\n relativeDirPath: RULESYNC_SKILLS_RELATIVE_DIR_PATH,\n dirName,\n frontmatter,\n body,\n otherFiles: aiDirFiles,\n validate: true,\n });\n\n // Ensure skill directory exists\n const skillDirPath = join(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName);\n await ensureDir(skillDirPath);\n\n // Write the SKILL.md file\n const skillFilePath = join(skillDirPath, SKILL_FILE_NAME);\n const skillFileContent = stringifyFrontmatter(body, frontmatter);\n await writeFileContent(skillFilePath, skillFileContent);\n\n // Write other files\n for (const file of otherFiles) {\n // Validate file path to prevent path traversal\n checkPathTraversal({\n relativePath: file.name,\n intendedRootDir: skillDirPath,\n });\n const filePath = join(skillDirPath, file.name);\n // Ensure subdirectory exists if file has path separators\n const fileDir = join(skillDirPath, dirname(file.name));\n if (fileDir !== skillDirPath) {\n await ensureDir(fileDir);\n }\n await writeFileContent(filePath, file.body);\n }\n\n return {\n relativeDirPathFromCwd: join(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),\n frontmatter: skill.getFrontmatter(),\n body: skill.getBody(),\n otherFiles: skill.getOtherFiles().map(aiDirFileToMcpSkillFile),\n };\n } catch (error) {\n throw new Error(\n `Failed to write skill directory ${relativeDirPathFromCwd}: ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Tool to delete a skill\n */\nasync function deleteSkill({\n relativeDirPathFromCwd,\n}: {\n relativeDirPathFromCwd: string;\n}): Promise<{\n relativeDirPathFromCwd: string;\n}> {\n checkPathTraversal({\n relativePath: relativeDirPathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const dirName = extractDirName(relativeDirPathFromCwd);\n const skillDirPath = join(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName);\n\n try {\n // Check if skill directory exists before attempting to delete\n if (await directoryExists(skillDirPath)) {\n await removeDirectory(skillDirPath);\n }\n\n return {\n relativeDirPathFromCwd: join(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),\n };\n } catch (error) {\n throw new Error(\n `Failed to delete skill directory ${relativeDirPathFromCwd}: ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Schema for other files in a skill directory\n */\nconst McpSkillFileSchema = z.object({\n name: z.string(),\n body: z.string(),\n});\n\n/**\n * Schema for skill-related tool parameters\n */\nconst skillToolSchemas = {\n listSkills: z.object({}),\n getSkill: z.object({\n relativeDirPathFromCwd: z.string(),\n }),\n putSkill: z.object({\n relativeDirPathFromCwd: z.string(),\n frontmatter: RulesyncSkillFrontmatterSchema,\n body: z.string(),\n otherFiles: z.optional(z.array(McpSkillFileSchema)),\n }),\n deleteSkill: z.object({\n relativeDirPathFromCwd: z.string(),\n }),\n} as const;\n\n/**\n * Tool definitions for skill-related operations\n */\nexport const skillTools = {\n listSkills: {\n name: \"listSkills\",\n description: `List all skills from ${join(RULESYNC_SKILLS_RELATIVE_DIR_PATH, \"*\", SKILL_FILE_NAME)} with their frontmatter.`,\n parameters: skillToolSchemas.listSkills,\n execute: async () => {\n const skills = await listSkills();\n const output = { skills };\n return JSON.stringify(output, null, 2);\n },\n },\n getSkill: {\n name: \"getSkill\",\n description:\n \"Get detailed information about a specific skill including SKILL.md content and other files. relativeDirPathFromCwd parameter is required.\",\n parameters: skillToolSchemas.getSkill,\n execute: async (args: { relativeDirPathFromCwd: string }) => {\n const result = await getSkill({ relativeDirPathFromCwd: args.relativeDirPathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n putSkill: {\n name: \"putSkill\",\n description:\n \"Create or update a skill (upsert operation). relativeDirPathFromCwd, frontmatter, and body parameters are required. otherFiles is optional.\",\n parameters: skillToolSchemas.putSkill,\n execute: async (args: {\n relativeDirPathFromCwd: string;\n frontmatter: RulesyncSkillFrontmatter;\n body: string;\n otherFiles?: McpSkillFile[];\n }) => {\n const result = await putSkill({\n relativeDirPathFromCwd: args.relativeDirPathFromCwd,\n frontmatter: args.frontmatter,\n body: args.body,\n otherFiles: args.otherFiles,\n });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteSkill: {\n name: \"deleteSkill\",\n description:\n \"Delete a skill directory and all its contents. relativeDirPathFromCwd parameter is required.\",\n parameters: skillToolSchemas.deleteSkill,\n execute: async (args: { relativeDirPathFromCwd: string }) => {\n const result = await deleteSkill({ relativeDirPathFromCwd: args.relativeDirPathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { basename, join } from \"node:path\";\n\nimport { z } from \"zod/mini\";\n\nimport { RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH } from \"../constants/rulesync-paths.js\";\nimport {\n RulesyncSubagent,\n type RulesyncSubagentFrontmatter,\n RulesyncSubagentFrontmatterSchema,\n} from \"../features/subagents/rulesync-subagent.js\";\nimport { formatError } from \"../utils/error.js\";\nimport {\n checkPathTraversal,\n ensureDir,\n listDirectoryFiles,\n removeFile,\n writeFileContent,\n} from \"../utils/file.js\";\nimport { ConsoleLogger } from \"../utils/logger.js\";\n\nconst logger = new ConsoleLogger({ verbose: false, silent: true });\n\nconst maxSubagentSizeBytes = 1024 * 1024; // 1MB\nconst maxSubagentsCount = 1000;\n\n/**\n * Tool to list all subagents from .rulesync/subagents/*.md\n */\nasync function listSubagents(): Promise<\n Array<{\n relativePathFromCwd: string;\n frontmatter: RulesyncSubagentFrontmatter;\n }>\n> {\n const subagentsDir = join(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);\n\n try {\n const files = await listDirectoryFiles(subagentsDir);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const subagents = await Promise.all(\n mdFiles.map(async (file) => {\n try {\n // Read the subagent file using RulesyncSubagent\n const subagent = await RulesyncSubagent.fromFile({\n relativeFilePath: file,\n validate: true,\n });\n\n const frontmatter = subagent.getFrontmatter();\n\n return {\n relativePathFromCwd: join(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, file),\n frontmatter,\n };\n } catch (error) {\n logger.error(`Failed to read subagent file ${file}: ${formatError(error)}`);\n return null;\n }\n }),\n );\n\n // Filter out null values (failed reads)\n return subagents.filter(\n (subagent): subagent is NonNullable<typeof subagent> => subagent !== null,\n );\n } catch (error) {\n logger.error(\n `Failed to read subagents directory (${RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH}): ${formatError(error)}`,\n );\n return [];\n }\n}\n\n/**\n * Tool to get detailed information about a specific subagent\n */\nasync function getSubagent({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncSubagentFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n try {\n const subagent = await RulesyncSubagent.fromFile({\n relativeFilePath: filename,\n validate: true,\n });\n\n return {\n relativePathFromCwd: join(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),\n frontmatter: subagent.getFrontmatter(),\n body: subagent.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to read subagent file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to create or update a subagent (upsert operation)\n */\nasync function putSubagent({\n relativePathFromCwd,\n frontmatter,\n body,\n}: {\n relativePathFromCwd: string;\n frontmatter: RulesyncSubagentFrontmatter;\n body: string;\n}): Promise<{\n relativePathFromCwd: string;\n frontmatter: RulesyncSubagentFrontmatter;\n body: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n\n // Check file size constraint\n const estimatedSize = JSON.stringify(frontmatter).length + body.length;\n if (estimatedSize > maxSubagentSizeBytes) {\n throw new Error(\n `Subagent size ${estimatedSize} bytes exceeds maximum ${maxSubagentSizeBytes} bytes (1MB) for ${relativePathFromCwd}`,\n );\n }\n\n try {\n // Check subagent count constraint\n const existingSubagents = await listSubagents();\n const isUpdate = existingSubagents.some(\n (subagent) =>\n subagent.relativePathFromCwd === join(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),\n );\n\n if (!isUpdate && existingSubagents.length >= maxSubagentsCount) {\n throw new Error(\n `Maximum number of subagents (${maxSubagentsCount}) reached in ${RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH}`,\n );\n }\n\n // Create a new RulesyncSubagent instance\n const subagent = new RulesyncSubagent({\n outputRoot: process.cwd(),\n relativeDirPath: RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH,\n relativeFilePath: filename,\n frontmatter,\n body,\n validate: true,\n });\n\n // Ensure directory exists\n const subagentsDir = join(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);\n await ensureDir(subagentsDir);\n\n // Write the file\n await writeFileContent(subagent.getFilePath(), subagent.getFileContent());\n\n return {\n relativePathFromCwd: join(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),\n frontmatter: subagent.getFrontmatter(),\n body: subagent.getBody(),\n };\n } catch (error) {\n throw new Error(`Failed to write subagent file ${relativePathFromCwd}: ${formatError(error)}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Tool to delete a subagent\n */\nasync function deleteSubagent({ relativePathFromCwd }: { relativePathFromCwd: string }): Promise<{\n relativePathFromCwd: string;\n}> {\n checkPathTraversal({\n relativePath: relativePathFromCwd,\n intendedRootDir: process.cwd(),\n });\n\n const filename = basename(relativePathFromCwd);\n const fullPath = join(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename);\n\n try {\n await removeFile(fullPath);\n\n return {\n relativePathFromCwd: join(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),\n };\n } catch (error) {\n throw new Error(\n `Failed to delete subagent file ${relativePathFromCwd}: ${formatError(error)}`,\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Schema for subagent-related tool parameters\n */\nconst subagentToolSchemas = {\n listSubagents: z.object({}),\n getSubagent: z.object({\n relativePathFromCwd: z.string(),\n }),\n putSubagent: z.object({\n relativePathFromCwd: z.string(),\n frontmatter: RulesyncSubagentFrontmatterSchema,\n body: z.string(),\n }),\n deleteSubagent: z.object({\n relativePathFromCwd: z.string(),\n }),\n} as const;\n\n/**\n * Tool definitions for subagent-related operations\n */\nexport const subagentTools = {\n listSubagents: {\n name: \"listSubagents\",\n description: `List all subagents from ${join(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, \"*.md\")} with their frontmatter.`,\n parameters: subagentToolSchemas.listSubagents,\n execute: async () => {\n const subagents = await listSubagents();\n const output = { subagents };\n return JSON.stringify(output, null, 2);\n },\n },\n getSubagent: {\n name: \"getSubagent\",\n description:\n \"Get detailed information about a specific subagent. relativePathFromCwd parameter is required.\",\n parameters: subagentToolSchemas.getSubagent,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await getSubagent({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n putSubagent: {\n name: \"putSubagent\",\n description:\n \"Create or update a subagent (upsert operation). relativePathFromCwd, frontmatter, and body parameters are required.\",\n parameters: subagentToolSchemas.putSubagent,\n execute: async (args: {\n relativePathFromCwd: string;\n frontmatter: RulesyncSubagentFrontmatter;\n body: string;\n }) => {\n const result = await putSubagent({\n relativePathFromCwd: args.relativePathFromCwd,\n frontmatter: args.frontmatter,\n body: args.body,\n });\n return JSON.stringify(result, null, 2);\n },\n },\n deleteSubagent: {\n name: \"deleteSubagent\",\n description: \"Delete a subagent file. relativePathFromCwd parameter is required.\",\n parameters: subagentToolSchemas.deleteSubagent,\n execute: async (args: { relativePathFromCwd: string }) => {\n const result = await deleteSubagent({ relativePathFromCwd: args.relativePathFromCwd });\n return JSON.stringify(result, null, 2);\n },\n },\n} as const;\n","import { z } from \"zod/mini\";\n\nimport {\n type RulesyncCheckFrontmatter,\n RulesyncCheckFrontmatterSchema,\n} from \"../features/checks/rulesync-check.js\";\nimport {\n type RulesyncCommandFrontmatter,\n RulesyncCommandFrontmatterSchema,\n} from \"../features/commands/rulesync-command.js\";\nimport {\n type RulesyncRuleFrontmatter,\n RulesyncRuleFrontmatterSchema,\n} from \"../features/rules/rulesync-rule.js\";\nimport {\n type RulesyncSkillFrontmatter,\n RulesyncSkillFrontmatterSchema,\n} from \"../features/skills/rulesync-skill.js\";\nimport {\n type RulesyncSubagentFrontmatter,\n RulesyncSubagentFrontmatterSchema,\n} from \"../features/subagents/rulesync-subagent.js\";\nimport { checkTools } from \"./checks.js\";\nimport { commandTools } from \"./commands.js\";\nimport { convertOptionsSchema, convertTools } from \"./convert.js\";\nimport { generateOptionsSchema, generateTools } from \"./generate.js\";\nimport { hooksTools } from \"./hooks.js\";\nimport { ignoreTools } from \"./ignore.js\";\nimport { importOptionsSchema, importTools } from \"./import.js\";\nimport { mcpTools } from \"./mcp.js\";\nimport { permissionsTools } from \"./permissions.js\";\nimport { ruleTools } from \"./rules.js\";\nimport { skillTools } from \"./skills.js\";\nimport { subagentTools } from \"./subagents.js\";\n\nconst rulesyncFeatureSchema = z.enum([\n \"rule\",\n \"command\",\n \"subagent\",\n \"skill\",\n \"check\",\n \"ignore\",\n \"mcp\",\n \"permissions\",\n \"hooks\",\n \"generate\",\n \"import\",\n \"convert\",\n]);\n\nconst rulesyncOperationSchema = z.enum([\"list\", \"get\", \"put\", \"delete\", \"run\"]);\n\nconst skillFileSchema = z.object({\n name: z.string(),\n body: z.string(),\n});\n\nconst rulesyncToolSchema = z.object({\n feature: rulesyncFeatureSchema,\n operation: rulesyncOperationSchema,\n targetPathFromCwd: z.optional(z.string()),\n frontmatter: z.optional(z.unknown()),\n body: z.optional(z.string()),\n otherFiles: z.optional(z.array(skillFileSchema)),\n content: z.optional(z.string()),\n generateOptions: z.optional(generateOptionsSchema),\n importOptions: z.optional(importOptionsSchema),\n convertOptions: z.optional(convertOptionsSchema),\n});\n\ntype RulesyncFeature = z.infer<typeof rulesyncFeatureSchema>;\ntype RulesyncOperation = z.infer<typeof rulesyncOperationSchema>;\ntype RulesyncToolArgs = z.infer<typeof rulesyncToolSchema>;\ntype RulesyncFrontmatterFeature = Exclude<\n RulesyncFeature,\n \"ignore\" | \"mcp\" | \"permissions\" | \"hooks\" | \"generate\" | \"import\" | \"convert\"\n>;\ntype RulesyncFrontmatterByFeature = {\n rule: RulesyncRuleFrontmatter;\n command: RulesyncCommandFrontmatter;\n subagent: RulesyncSubagentFrontmatter;\n skill: RulesyncSkillFrontmatter;\n check: RulesyncCheckFrontmatter;\n};\n\nconst supportedOperationsByFeature: Record<RulesyncFeature, RulesyncOperation[]> = {\n rule: [\"list\", \"get\", \"put\", \"delete\"],\n command: [\"list\", \"get\", \"put\", \"delete\"],\n subagent: [\"list\", \"get\", \"put\", \"delete\"],\n skill: [\"list\", \"get\", \"put\", \"delete\"],\n check: [\"list\", \"get\", \"put\", \"delete\"],\n ignore: [\"get\", \"put\", \"delete\"],\n mcp: [\"get\", \"put\", \"delete\"],\n permissions: [\"get\", \"put\", \"delete\"],\n hooks: [\"get\", \"put\", \"delete\"],\n generate: [\"run\"],\n import: [\"run\"],\n convert: [\"run\"],\n};\n\nfunction assertSupported({\n feature,\n operation,\n}: {\n feature: RulesyncFeature;\n operation: RulesyncOperation;\n}): void {\n const supportedOperations = supportedOperationsByFeature[feature];\n\n if (!supportedOperations.includes(operation)) {\n throw new Error(\n `Operation ${operation} is not supported for feature ${feature}. Supported operations: ${supportedOperations.join(\n \", \",\n )}`,\n );\n }\n}\n\nfunction requireTargetPath({ targetPathFromCwd, feature, operation }: RulesyncToolArgs): string {\n if (!targetPathFromCwd) {\n throw new Error(`targetPathFromCwd is required for ${feature} ${operation} operation`);\n }\n\n return targetPathFromCwd;\n}\n\nfunction parseFrontmatter({\n feature,\n frontmatter,\n}: {\n feature: \"rule\";\n frontmatter: unknown;\n}): RulesyncRuleFrontmatter;\nfunction parseFrontmatter({\n feature,\n frontmatter,\n}: {\n feature: \"command\";\n frontmatter: unknown;\n}): RulesyncCommandFrontmatter;\nfunction parseFrontmatter({\n feature,\n frontmatter,\n}: {\n feature: \"subagent\";\n frontmatter: unknown;\n}): RulesyncSubagentFrontmatter;\nfunction parseFrontmatter({\n feature,\n frontmatter,\n}: {\n feature: \"skill\";\n frontmatter: unknown;\n}): RulesyncSkillFrontmatter;\nfunction parseFrontmatter({\n feature,\n frontmatter,\n}: {\n feature: \"check\";\n frontmatter: unknown;\n}): RulesyncCheckFrontmatter;\nfunction parseFrontmatter<Feature extends RulesyncFrontmatterFeature>({\n feature,\n frontmatter,\n}: {\n feature: Feature;\n frontmatter: unknown;\n}): RulesyncFrontmatterByFeature[Feature];\nfunction parseFrontmatter({\n feature,\n frontmatter,\n}: {\n feature: RulesyncFrontmatterFeature;\n frontmatter: unknown;\n}): RulesyncFrontmatterByFeature[RulesyncFrontmatterFeature] {\n switch (feature) {\n case \"rule\": {\n return RulesyncRuleFrontmatterSchema.parse(frontmatter);\n }\n case \"command\": {\n return RulesyncCommandFrontmatterSchema.parse(frontmatter);\n }\n case \"subagent\": {\n return RulesyncSubagentFrontmatterSchema.parse(frontmatter);\n }\n case \"skill\": {\n return RulesyncSkillFrontmatterSchema.parse(frontmatter);\n }\n case \"check\": {\n return RulesyncCheckFrontmatterSchema.parse(frontmatter);\n }\n }\n}\n\nfunction ensureBody({ body, feature, operation }: RulesyncToolArgs): string {\n if (!body) {\n throw new Error(`body is required for ${feature} ${operation} operation`);\n }\n\n return body;\n}\n\nfunction requireContent({\n content,\n feature,\n}: {\n content: string | undefined;\n feature: string;\n}): string {\n if (!content) {\n throw new Error(`content is required for ${feature} put operation`);\n }\n\n return content;\n}\n\nfunction executeRule(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"list\") {\n return ruleTools.listRules.execute();\n }\n\n if (parsed.operation === \"get\") {\n return ruleTools.getRule.execute({ relativePathFromCwd: requireTargetPath(parsed) });\n }\n\n if (parsed.operation === \"put\") {\n return ruleTools.putRule.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n frontmatter: parseFrontmatter({\n feature: \"rule\",\n frontmatter: parsed.frontmatter ?? {},\n }),\n body: ensureBody(parsed),\n });\n }\n\n return ruleTools.deleteRule.execute({ relativePathFromCwd: requireTargetPath(parsed) });\n}\n\nfunction executeCommand(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"list\") {\n return commandTools.listCommands.execute();\n }\n\n if (parsed.operation === \"get\") {\n return commandTools.getCommand.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n });\n }\n\n if (parsed.operation === \"put\") {\n return commandTools.putCommand.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n frontmatter: parseFrontmatter({\n feature: \"command\",\n frontmatter: parsed.frontmatter ?? {},\n }),\n body: ensureBody(parsed),\n });\n }\n\n return commandTools.deleteCommand.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n });\n}\n\nfunction executeSubagent(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"list\") {\n return subagentTools.listSubagents.execute();\n }\n\n if (parsed.operation === \"get\") {\n return subagentTools.getSubagent.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n });\n }\n\n if (parsed.operation === \"put\") {\n return subagentTools.putSubagent.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n frontmatter: parseFrontmatter({\n feature: \"subagent\",\n frontmatter: parsed.frontmatter ?? {},\n }),\n body: ensureBody(parsed),\n });\n }\n\n return subagentTools.deleteSubagent.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n });\n}\n\nfunction executeSkill(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"list\") {\n return skillTools.listSkills.execute();\n }\n\n if (parsed.operation === \"get\") {\n return skillTools.getSkill.execute({ relativeDirPathFromCwd: requireTargetPath(parsed) });\n }\n\n if (parsed.operation === \"put\") {\n return skillTools.putSkill.execute({\n relativeDirPathFromCwd: requireTargetPath(parsed),\n frontmatter: parseFrontmatter({\n feature: \"skill\",\n frontmatter: parsed.frontmatter ?? {},\n }),\n body: ensureBody(parsed),\n otherFiles: parsed.otherFiles ?? [],\n });\n }\n\n return skillTools.deleteSkill.execute({\n relativeDirPathFromCwd: requireTargetPath(parsed),\n });\n}\n\nfunction executeCheck(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"list\") {\n return checkTools.listChecks.execute();\n }\n\n if (parsed.operation === \"get\") {\n return checkTools.getCheck.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n });\n }\n\n if (parsed.operation === \"put\") {\n return checkTools.putCheck.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n frontmatter: parseFrontmatter({\n feature: \"check\",\n frontmatter: parsed.frontmatter ?? {},\n }),\n body: ensureBody(parsed),\n });\n }\n\n return checkTools.deleteCheck.execute({\n relativePathFromCwd: requireTargetPath(parsed),\n });\n}\n\nfunction executeIgnore(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"get\") {\n return ignoreTools.getIgnoreFile.execute();\n }\n\n if (parsed.operation === \"put\") {\n return ignoreTools.putIgnoreFile.execute({\n content: requireContent({ content: parsed.content, feature: \"ignore\" }),\n });\n }\n\n return ignoreTools.deleteIgnoreFile.execute();\n}\n\nfunction executeMcp(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"get\") {\n return mcpTools.getMcpFile.execute();\n }\n\n if (parsed.operation === \"put\") {\n return mcpTools.putMcpFile.execute({\n content: requireContent({ content: parsed.content, feature: \"mcp\" }),\n });\n }\n\n return mcpTools.deleteMcpFile.execute();\n}\n\nfunction executePermissions(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"get\") {\n return permissionsTools.getPermissionsFile.execute();\n }\n\n if (parsed.operation === \"put\") {\n return permissionsTools.putPermissionsFile.execute({\n content: requireContent({ content: parsed.content, feature: \"permissions\" }),\n });\n }\n\n return permissionsTools.deletePermissionsFile.execute();\n}\n\nfunction executeHooks(parsed: RulesyncToolArgs) {\n if (parsed.operation === \"get\") {\n return hooksTools.getHooksFile.execute();\n }\n\n if (parsed.operation === \"put\") {\n return hooksTools.putHooksFile.execute({\n content: requireContent({ content: parsed.content, feature: \"hooks\" }),\n });\n }\n\n return hooksTools.deleteHooksFile.execute();\n}\n\nfunction executeGenerate(parsed: RulesyncToolArgs) {\n // Only \"run\" operation is supported for generate feature\n return generateTools.executeGenerate.execute(parsed.generateOptions ?? {});\n}\n\nfunction executeImport(parsed: RulesyncToolArgs) {\n // Only \"run\" operation is supported for import feature\n if (!parsed.importOptions) {\n throw new Error(\"importOptions is required for import feature\");\n }\n return importTools.executeImport.execute(parsed.importOptions);\n}\n\nfunction executeConvert(parsed: RulesyncToolArgs) {\n // Only \"run\" operation is supported for convert feature\n if (!parsed.convertOptions) {\n throw new Error(\"convertOptions is required for convert feature\");\n }\n return convertTools.executeConvert.execute(parsed.convertOptions);\n}\n\nconst featureExecutors: Record<RulesyncFeature, (parsed: RulesyncToolArgs) => Promise<string>> = {\n rule: executeRule,\n command: executeCommand,\n subagent: executeSubagent,\n skill: executeSkill,\n check: executeCheck,\n ignore: executeIgnore,\n mcp: executeMcp,\n permissions: executePermissions,\n hooks: executeHooks,\n generate: executeGenerate,\n import: executeImport,\n convert: executeConvert,\n};\n\nexport const rulesyncTool = {\n name: \"rulesyncTool\",\n description:\n \"Manage Rulesync files through a single MCP tool. Features: rule/command/subagent/skill/check support list/get/put/delete; ignore/mcp/permissions/hooks support get/put/delete only; generate supports run only; import supports run only; convert supports run only. Parameters: list requires no targetPathFromCwd (lists all items); get/delete require targetPathFromCwd; put requires targetPathFromCwd, frontmatter, and body (or content for ignore/mcp/permissions/hooks); generate/run uses generateOptions to configure generation; import/run uses importOptions to configure import; convert/run uses convertOptions to configure conversion.\",\n parameters: rulesyncToolSchema,\n execute: async (args: RulesyncToolArgs) => {\n const parsed = rulesyncToolSchema.parse(args);\n\n assertSupported({ feature: parsed.feature, operation: parsed.operation });\n\n const executor = featureExecutors[parsed.feature];\n if (!executor) {\n throw new Error(`Unknown feature: ${parsed.feature}`);\n }\n\n return executor(parsed);\n },\n} as const;\n","import { FastMCP } from \"fastmcp\";\n\nimport { rulesyncTool } from \"../../mcp/tools.js\";\nimport type { Logger } from \"../../utils/logger.js\";\n\n/**\n * MCP command that starts the MCP server\n */\nexport async function mcpCommand(logger: Logger, { version }: { version: string }): Promise<void> {\n const server = new FastMCP({\n name: \"Rulesync MCP Server\",\n version: version as `${number}.${number}.${number}`,\n instructions:\n \"This server handles Rulesync files including rules, commands, MCP, ignore files, subagents and skills for any AI agents. It should be used when you need those files.\",\n });\n\n server.addTool(rulesyncTool);\n\n // Start server with stdio transport (for spawned processes)\n logger.info(\"Rulesync MCP server started via stdio\");\n\n // Start the server - this blocks execution and runs the MCP server\n // The void operator explicitly marks this as intentionally not awaited\n void server.start({\n transportType: \"stdio\",\n });\n}\n","import { join } from \"node:path\";\n\nimport { ConfigResolver } from \"../../config/config-resolver.js\";\nimport {\n RULESYNC_CONFIG_RELATIVE_FILE_PATH,\n RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH,\n} from \"../../constants/rulesync-paths.js\";\nimport { fileExists } from \"../../utils/file.js\";\n\nexport type ResolveGitignoreTargetsParams = {\n readonly cliTargets: readonly string[] | undefined;\n readonly cwd?: string;\n};\n\n/**\n * Resolve the list of targets to pass to `gitignoreCommand`.\n *\n * Precedence:\n * 1. Explicit `--targets` CLI option wins.\n * 2. If neither rulesync.jsonc nor rulesync.local.jsonc exists, return\n * `undefined` so all supported tools' entries are emitted. Otherwise a\n * user without a config file would silently get only the default\n * `[\"agentsmd\"]` target, which is a surprising behavior change.\n * 3. If `gitignoreTargetsOnly` is true (the default), return the config's\n * `targets` with `agentsmd` always appended. `AGENTS.md` is a de facto\n * standard file read by many AI tools regardless of which targets the\n * user selected, so its gitignore entries must always be emitted to\n * prevent accidental commits of generated rule files.\n * 4. Otherwise return `undefined` to emit entries for every supported tool.\n */\nexport const resolveGitignoreTargets = async ({\n cliTargets,\n cwd = process.cwd(),\n}: ResolveGitignoreTargetsParams): Promise<readonly string[] | undefined> => {\n if (cliTargets !== undefined) {\n return cliTargets;\n }\n\n const baseConfigPath = join(cwd, RULESYNC_CONFIG_RELATIVE_FILE_PATH);\n const localConfigPath = join(cwd, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH);\n const [hasBase, hasLocal] = await Promise.all([\n fileExists(baseConfigPath),\n fileExists(localConfigPath),\n ]);\n\n if (!hasBase && !hasLocal) {\n return undefined;\n }\n\n const config = await ConfigResolver.resolve({});\n if (config.getGitignoreTargetsOnly()) {\n const targets = config.getTargets();\n if (targets.includes(\"agentsmd\")) {\n return targets;\n }\n return [...targets, \"agentsmd\"];\n }\n return undefined;\n};\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { Readable, Transform } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\n\nimport type { GitHubRelease, GitHubReleaseAsset } from \"../types/fetch.js\";\nimport { GitHubClient } from \"./github-client.js\";\n\nconst RULESYNC_REPO_OWNER = \"dyoshikawa\";\nconst RULESYNC_REPO_NAME = \"rulesync\";\n\n/**\n * GitHub releases URL for manual download instructions\n */\nconst RELEASES_URL = `https://github.com/${RULESYNC_REPO_OWNER}/${RULESYNC_REPO_NAME}/releases`;\n\n/**\n * Maximum download size (500MB) to prevent memory exhaustion\n */\nconst MAX_DOWNLOAD_SIZE = 500 * 1024 * 1024;\n\n/**\n * Allowed domains for downloading release assets\n */\nconst ALLOWED_DOWNLOAD_DOMAINS = [\n \"github.com\",\n \"objects.githubusercontent.com\",\n \"github-releases.githubusercontent.com\",\n \"release-assets.githubusercontent.com\",\n];\n\n/**\n * Execution environment types for rulesync\n */\nexport type ExecutionEnvironment = \"single-binary\" | \"homebrew\" | \"npm\";\n\n/**\n * Update check result\n */\nexport type UpdateCheckResult = {\n currentVersion: string;\n latestVersion: string;\n hasUpdate: boolean;\n release: GitHubRelease;\n};\n\n/**\n * Custom error for permission issues during update\n */\nexport class UpdatePermissionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"UpdatePermissionError\";\n }\n}\n\n/**\n * Detect the execution environment of rulesync.\n *\n * Uses process.execPath (the Node.js/Bun binary) and process.argv[1] (the script being executed)\n * to determine how rulesync was installed.\n */\nexport function detectExecutionEnvironment(): ExecutionEnvironment {\n const execPath = process.execPath;\n const scriptPath = process.argv[1] ?? \"\";\n\n // Single binary detection: the executable itself is named rulesync\n const isRulesyncBinary = /rulesync(-[a-z0-9]+(-[a-z0-9]+)?)?(\\.exe)?$/i.test(execPath);\n if (isRulesyncBinary) {\n // Check if the rulesync binary itself is in a Homebrew path\n if (execPath.includes(\"/homebrew/\") || execPath.includes(\"/Cellar/\")) {\n return \"homebrew\";\n }\n return \"single-binary\";\n }\n\n // Homebrew detection via script path: e.g. /opt/homebrew/lib/node_modules/rulesync/...\n if (\n (scriptPath.includes(\"/homebrew/\") || scriptPath.includes(\"/Cellar/\")) &&\n scriptPath.includes(\"rulesync\")\n ) {\n return \"homebrew\";\n }\n\n return \"npm\";\n}\n\n/**\n * Get the asset name for the current platform\n */\nexport function getPlatformAssetName(): string | null {\n const platform = os.platform();\n const arch = os.arch();\n\n // Map Node.js platform/arch to asset names\n const platformMap: Record<string, string> = {\n darwin: \"darwin\",\n linux: \"linux\",\n win32: \"windows\",\n };\n\n const archMap: Record<string, string> = {\n x64: \"x64\",\n arm64: \"arm64\",\n };\n\n const platformName = platformMap[platform];\n const archName = archMap[arch];\n\n if (!platformName || !archName) {\n return null;\n }\n\n const extension = platform === \"win32\" ? \".exe\" : \"\";\n return `rulesync-${platformName}-${archName}${extension}`;\n}\n\n/**\n * Normalize version string by removing leading 'v' and stripping pre-release suffix\n */\nexport function normalizeVersion(v: string): string {\n // Remove leading 'v' and strip pre-release suffix (e.g., \"1.2.3-beta.1\" -> \"1.2.3\")\n return v.replace(/^v/, \"\").replace(/-.*$/, \"\");\n}\n\n/**\n * Compare semantic versions\n * Returns: 1 if a > b, -1 if a < b, 0 if equal\n */\nexport function compareVersions(a: string, b: string): number {\n const aParts = normalizeVersion(a).split(\".\").map(Number);\n const bParts = normalizeVersion(b).split(\".\").map(Number);\n\n for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {\n const aNum = aParts[i] ?? 0;\n const bNum = bParts[i] ?? 0;\n if (!Number.isFinite(aNum) || !Number.isFinite(bNum)) {\n throw new Error(`Invalid version format: cannot compare \"${a}\" and \"${b}\"`);\n }\n if (aNum > bNum) return 1;\n if (aNum < bNum) return -1;\n }\n return 0;\n}\n\n/**\n * Validate that a download URL is safe (HTTPS + allowed GitHub domain + repo path for github.com)\n */\nexport function validateDownloadUrl(url: string): void {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw new Error(`Invalid download URL: ${url}`);\n }\n\n if (parsed.protocol !== \"https:\") {\n throw new Error(`Download URL must use HTTPS: ${url}`);\n }\n\n const isAllowed = ALLOWED_DOWNLOAD_DOMAINS.some((domain) => parsed.hostname === domain);\n if (!isAllowed) {\n throw new Error(\n `Download URL domain \"${parsed.hostname}\" is not in the allowed list: ${ALLOWED_DOWNLOAD_DOMAINS.join(\", \")}`,\n );\n }\n\n // For github.com URLs, validate the path starts with the expected repo\n if (parsed.hostname === \"github.com\") {\n const expectedPrefix = `/${RULESYNC_REPO_OWNER}/${RULESYNC_REPO_NAME}/`;\n if (!parsed.pathname.startsWith(expectedPrefix)) {\n throw new Error(\n `Download URL path must belong to ${RULESYNC_REPO_OWNER}/${RULESYNC_REPO_NAME}: ${url}`,\n );\n }\n }\n}\n\n/**\n * Check for updates\n */\nexport async function checkForUpdate(\n currentVersion: string,\n token?: string,\n): Promise<UpdateCheckResult> {\n const client = new GitHubClient({\n token: GitHubClient.resolveToken(token),\n });\n\n const release = await client.getLatestRelease(RULESYNC_REPO_OWNER, RULESYNC_REPO_NAME);\n const latestVersion = normalizeVersion(release.tag_name);\n const normalizedCurrentVersion = normalizeVersion(currentVersion);\n\n return {\n currentVersion: normalizedCurrentVersion,\n latestVersion,\n hasUpdate: compareVersions(latestVersion, normalizedCurrentVersion) > 0,\n release,\n };\n}\n\n/**\n * Find asset by name in release\n */\nfunction findAsset(release: GitHubRelease, assetName: string): GitHubReleaseAsset | null {\n return release.assets.find((asset) => asset.name === assetName) ?? null;\n}\n\n/**\n * Download a file from URL to a destination path using streaming to limit memory usage.\n * Validates both the initial URL and the final URL after redirects.\n */\nasync function downloadFile(url: string, destPath: string): Promise<void> {\n validateDownloadUrl(url);\n\n const response = await fetch(url, {\n redirect: \"follow\",\n });\n\n if (!response.ok) {\n throw new Error(`Failed to download ${url}: HTTP ${response.status}`);\n }\n\n // Validate the final URL after redirects to prevent redirect-based bypass\n if (response.url) {\n validateDownloadUrl(response.url);\n }\n\n const contentLength = response.headers.get(\"content-length\");\n if (contentLength && Number(contentLength) > MAX_DOWNLOAD_SIZE) {\n throw new Error(\n `Download too large: ${contentLength} bytes exceeds limit of ${MAX_DOWNLOAD_SIZE} bytes`,\n );\n }\n\n if (!response.body) {\n throw new Error(\"Response body is empty\");\n }\n\n // Stream the response to file with a size limit check\n const fileStream = fs.createWriteStream(destPath);\n let downloadedBytes = 0;\n\n const bodyReader = Readable.fromWeb(response.body as import(\"node:stream/web\").ReadableStream);\n\n const sizeChecker = new Transform({\n transform(chunk, _encoding, callback) {\n downloadedBytes += (chunk as Buffer).length;\n if (downloadedBytes > MAX_DOWNLOAD_SIZE) {\n callback(\n new Error(\n `Download too large: exceeded limit of ${MAX_DOWNLOAD_SIZE} bytes during streaming`,\n ),\n );\n return;\n }\n callback(null, chunk);\n },\n });\n\n await pipeline(bodyReader, sizeChecker, fileStream);\n}\n\n/**\n * Calculate SHA256 checksum of a file\n */\nasync function calculateSha256(filePath: string): Promise<string> {\n const content = await fs.promises.readFile(filePath);\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\n/**\n * Parse SHA256SUMS file content\n */\nexport function parseSha256Sums(content: string): Map<string, string> {\n const result = new Map<string, string>();\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n // Format: \"hash filename\" (two spaces between hash and filename)\n const match = /^([a-f0-9]{64})\\s+(.+)$/.exec(trimmed);\n if (match && match[1] && match[2]) {\n result.set(match[2].trim(), match[1]);\n }\n }\n return result;\n}\n\n/**\n * Update options\n */\nexport type UpdateOptions = {\n force?: boolean;\n token?: string;\n};\n\n/**\n * Resolve the platform binary asset and the mandatory SHA256SUMS asset from a\n * release, throwing with manual-download guidance when either is unavailable.\n */\nfunction resolveUpdateAssets(release: GitHubRelease): {\n assetName: string;\n binaryAsset: GitHubReleaseAsset;\n checksumAsset: GitHubReleaseAsset;\n} {\n // Get platform-specific asset name\n const assetName = getPlatformAssetName();\n if (!assetName) {\n throw new Error(\n `Unsupported platform: ${os.platform()} ${os.arch()}. Please download manually from ${RELEASES_URL}`,\n );\n }\n\n // Find the binary asset\n const binaryAsset = findAsset(release, assetName);\n if (!binaryAsset) {\n throw new Error(\n `Binary for ${assetName} not found in release. Please download manually from ${RELEASES_URL}`,\n );\n }\n\n // Find the SHA256SUMS asset for verification (mandatory)\n const checksumAsset = findAsset(release, \"SHA256SUMS\");\n if (!checksumAsset) {\n throw new Error(\n `SHA256SUMS not found in release. Cannot verify download integrity. Please download manually from ${RELEASES_URL}`,\n );\n }\n\n return { assetName, binaryAsset, checksumAsset };\n}\n\n/**\n * Download the binary and SHA256SUMS into the temp directory, then verify the\n * binary's checksum. Throws when the checksum entry is missing or mismatched.\n */\nasync function downloadAndVerifyBinary(params: {\n tempDir: string;\n assetName: string;\n binaryAsset: GitHubReleaseAsset;\n checksumAsset: GitHubReleaseAsset;\n}): Promise<string> {\n const { tempDir, assetName, binaryAsset, checksumAsset } = params;\n const tempBinaryPath = path.join(tempDir, assetName);\n\n // Download the binary\n await downloadFile(binaryAsset.browser_download_url, tempBinaryPath);\n\n // Verify checksum (mandatory)\n const checksumsPath = path.join(tempDir, \"SHA256SUMS\");\n await downloadFile(checksumAsset.browser_download_url, checksumsPath);\n\n const checksumsContent = await fs.promises.readFile(checksumsPath, \"utf-8\");\n const checksums = parseSha256Sums(checksumsContent);\n const expectedChecksum = checksums.get(assetName);\n\n if (!expectedChecksum) {\n throw new Error(\n `Checksum entry for \"${assetName}\" not found in SHA256SUMS. Cannot verify download integrity.`,\n );\n }\n\n const actualChecksum = await calculateSha256(tempBinaryPath);\n if (actualChecksum !== expectedChecksum) {\n throw new Error(\n `Checksum verification failed. Expected: ${expectedChecksum}, Got: ${actualChecksum}. The download may be corrupted.`,\n );\n }\n\n return tempBinaryPath;\n}\n\n/**\n * Replace the running executable at `currentExePath` with the verified binary,\n * preferring an atomic rename and falling back to a direct cross-filesystem copy.\n */\nasync function replaceCurrentBinary(params: {\n tempBinaryPath: string;\n currentExePath: string;\n currentDir: string;\n}): Promise<void> {\n const { tempBinaryPath, currentExePath, currentDir } = params;\n // Attempt atomic replacement via rename (works when on the same filesystem)\n const tempInPlace = path.join(currentDir, `.rulesync-update-${crypto.randomUUID()}`);\n try {\n await fs.promises.copyFile(tempBinaryPath, tempInPlace);\n if (os.platform() !== \"win32\") {\n await fs.promises.chmod(tempInPlace, 0o755);\n }\n await fs.promises.rename(tempInPlace, currentExePath);\n } catch {\n // Cleanup temp-in-place file on failure, then fall back to direct copy\n try {\n await fs.promises.unlink(tempInPlace);\n } catch {\n // Ignore cleanup errors\n }\n // Fallback: direct copy (non-atomic but works across filesystems)\n await fs.promises.copyFile(tempBinaryPath, currentExePath);\n if (os.platform() !== \"win32\") {\n await fs.promises.chmod(currentExePath, 0o755);\n }\n }\n}\n\n/**\n * Install the verified binary over the current executable, backing it up first\n * and restoring from backup on failure. Returns whether the restore failed so\n * the caller can preserve the temp directory for manual recovery.\n */\nasync function installVerifiedBinary(params: {\n tempDir: string;\n tempBinaryPath: string;\n currentVersion: string;\n latestVersion: string;\n}): Promise<{ message: string; restoreFailed: boolean }> {\n const { tempDir, tempBinaryPath, currentVersion, latestVersion } = params;\n\n // Resolve symlinks to get the real executable path\n const currentExePath = await fs.promises.realpath(process.execPath);\n const currentDir = path.dirname(currentExePath);\n\n // Backup current binary to temp directory (not predictable path)\n const backupPath = path.join(tempDir, \"rulesync.backup\");\n try {\n await fs.promises.copyFile(currentExePath, backupPath);\n } catch (error) {\n if (isPermissionError(error)) {\n throw new UpdatePermissionError(\n `Permission denied: Cannot read ${currentExePath}. Try running with sudo.`,\n );\n }\n throw error;\n }\n\n try {\n await replaceCurrentBinary({ tempBinaryPath, currentExePath, currentDir });\n return {\n message: `Successfully updated from ${currentVersion} to ${latestVersion}`,\n restoreFailed: false,\n };\n } catch (error) {\n // Restore from backup on failure\n try {\n await fs.promises.copyFile(backupPath, currentExePath);\n } catch {\n throw new RestoreFailedError(\n new Error(\n `Failed to replace binary and restore failed. Backup is preserved at: ${backupPath} (in ${tempDir}). ` +\n `Please manually copy it to ${currentExePath}. Original error: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n ),\n );\n }\n if (isPermissionError(error)) {\n throw new UpdatePermissionError(\n `Permission denied: Cannot write to ${path.dirname(currentExePath)}. Try running with sudo.`,\n );\n }\n throw error;\n }\n}\n\n/**\n * Internal marker wrapping the error thrown when both the binary replacement\n * and the backup restore fail. The caller unwraps it so the temp directory is\n * preserved for manual recovery (mirrors the original inline `restoreFailed`\n * flag behavior).\n */\nclass RestoreFailedError extends Error {\n override readonly cause: Error;\n constructor(cause: Error) {\n super(cause.message);\n this.name = \"RestoreFailedError\";\n this.cause = cause;\n }\n}\n\n/**\n * Perform the binary update\n */\nexport async function performBinaryUpdate(\n currentVersion: string,\n options: UpdateOptions = {},\n): Promise<string> {\n const { force = false, token } = options;\n\n // Check for updates\n const updateCheck = await checkForUpdate(currentVersion, token);\n\n if (!updateCheck.hasUpdate && !force) {\n return `Already at the latest version (${currentVersion})`;\n }\n\n const { assetName, binaryAsset, checksumAsset } = resolveUpdateAssets(updateCheck.release);\n\n // Create temporary directory for download\n const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), \"rulesync-update-\"));\n let restoreFailed = false;\n\n try {\n // Set restrictive permissions on temp directory (Unix only)\n if (os.platform() !== \"win32\") {\n await fs.promises.chmod(tempDir, 0o700);\n }\n\n const tempBinaryPath = await downloadAndVerifyBinary({\n tempDir,\n assetName,\n binaryAsset,\n checksumAsset,\n });\n\n const installed = await installVerifiedBinary({\n tempDir,\n tempBinaryPath,\n currentVersion,\n latestVersion: updateCheck.latestVersion,\n });\n restoreFailed = installed.restoreFailed;\n return installed.message;\n } catch (error) {\n if (error instanceof RestoreFailedError) {\n restoreFailed = true;\n throw error.cause;\n }\n throw error;\n } finally {\n // Skip cleanup if restore failed, so the backup is preserved for manual recovery\n if (!restoreFailed) {\n try {\n await fs.promises.rm(tempDir, { recursive: true, force: true });\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n}\n\n/**\n * Check if an error is a permission error\n */\nfunction isPermissionError(error: unknown): boolean {\n if (typeof error === \"object\" && error !== null && \"code\" in error) {\n const record = error as Record<string, unknown>;\n return record[\"code\"] === \"EACCES\" || record[\"code\"] === \"EPERM\";\n }\n return false;\n}\n\n/**\n * Get upgrade instructions for npm installation\n */\nexport function getNpmUpgradeInstructions(): string {\n return `This rulesync installation was installed via npm/npx.\n\nTo upgrade, run one of the following commands:\n\n Global installation:\n npm install -g rulesync@latest\n\n Project dependency:\n npm install rulesync@latest\n\n Or use npx to always run the latest version:\n npx rulesync@latest --version`;\n}\n\n/**\n * Get upgrade instructions for Homebrew installation\n */\nexport function getHomebrewUpgradeInstructions(): string {\n return `This rulesync installation was installed via Homebrew.\n\nTo upgrade, run:\n brew upgrade rulesync`;\n}\n","import { GitHubClientError } from \"../../lib/github-client.js\";\nimport {\n UpdatePermissionError,\n checkForUpdate,\n detectExecutionEnvironment,\n getHomebrewUpgradeInstructions,\n getNpmUpgradeInstructions,\n performBinaryUpdate,\n} from \"../../lib/update.js\";\nimport { CLIError, ErrorCodes } from \"../../types/json-output.js\";\nimport type { Logger } from \"../../utils/logger.js\";\n\n/**\n * Update command options\n */\nexport type UpdateCommandOptions = {\n check?: boolean;\n force?: boolean;\n verbose?: boolean;\n silent?: boolean;\n token?: string;\n};\n\n/**\n * Update command handler\n */\nexport async function updateCommand(\n logger: Logger,\n currentVersion: string,\n options: UpdateCommandOptions,\n): Promise<void> {\n const { check = false, force = false, token } = options;\n\n try {\n const environment = detectExecutionEnvironment();\n logger.debug(`Detected environment: ${environment}`);\n\n if (environment === \"npm\") {\n logger.info(getNpmUpgradeInstructions());\n return;\n }\n\n if (environment === \"homebrew\") {\n logger.info(getHomebrewUpgradeInstructions());\n return;\n }\n\n // Single-binary mode\n if (check) {\n // Check-only mode\n logger.info(\"Checking for updates...\");\n const updateCheck = await checkForUpdate(currentVersion, token);\n\n // Capture JSON data if in JSON mode\n if (logger.jsonMode) {\n logger.captureData(\"currentVersion\", updateCheck.currentVersion);\n logger.captureData(\"latestVersion\", updateCheck.latestVersion);\n logger.captureData(\"updateAvailable\", updateCheck.hasUpdate);\n logger.captureData(\n \"message\",\n updateCheck.hasUpdate\n ? `Update available: ${updateCheck.currentVersion} -> ${updateCheck.latestVersion}`\n : `Already at the latest version (${updateCheck.currentVersion})`,\n );\n }\n\n if (updateCheck.hasUpdate) {\n logger.success(\n `Update available: ${updateCheck.currentVersion} -> ${updateCheck.latestVersion}`,\n );\n } else {\n logger.info(`Already at the latest version (${updateCheck.currentVersion})`);\n }\n return;\n }\n\n // Perform update\n logger.info(\"Checking for updates...\");\n const message = await performBinaryUpdate(currentVersion, { force, token });\n logger.success(message);\n } catch (error) {\n if (error instanceof GitHubClientError) {\n // Include auth hints in error message for JSON mode\n const authHint =\n error.statusCode === 401 || error.statusCode === 403\n ? \" Tip: Set GITHUB_TOKEN or GH_TOKEN environment variable, or use `GITHUB_TOKEN=$(gh auth token) rulesync update ...`\"\n : \"\";\n throw new CLIError(\n `GitHub API Error: ${error.message}.${authHint}`,\n ErrorCodes.UPDATE_FAILED,\n );\n } else if (error instanceof UpdatePermissionError) {\n throw new CLIError(\n `${error.message} Tip: Run with elevated privileges (e.g., sudo rulesync update)`,\n ErrorCodes.UPDATE_FAILED,\n );\n }\n throw error;\n }\n}\n","import { Command } from \"commander\";\n\nimport { CLIError } from \"../types/json-output.js\";\nimport { formatError } from \"../utils/error.js\";\nimport {\n ConsoleLogger,\n fallbackLogger,\n JsonLogger,\n Logger,\n warnOnConflictingFlags,\n} from \"../utils/logger.js\";\n\nexport function createLogger({\n name,\n globalOpts,\n getVersion,\n}: {\n name: string;\n globalOpts: Record<string, unknown>;\n getVersion: () => string;\n}): Logger {\n return globalOpts.json\n ? new JsonLogger({ command: name, version: getVersion() })\n : new ConsoleLogger();\n}\n\nexport function wrapCommand({\n name,\n errorCode,\n handler,\n getVersion,\n loggerFactory = createLogger,\n}: {\n name: string;\n errorCode: string;\n handler: (\n logger: Logger,\n options: unknown,\n globalOpts: Record<string, unknown>,\n positionalArgs: unknown[],\n ) => Promise<void>;\n getVersion: () => string;\n loggerFactory?: (params: {\n name: string;\n globalOpts: Record<string, unknown>;\n getVersion: () => string;\n }) => Logger;\n}) {\n return async (...args: unknown[]) => {\n // Commander passes variable args based on command signature:\n // - No positional: (options, command)\n // - With positional: (arg1, arg2, ..., options, command)\n // The last two are always (options, command)\n const command = args[args.length - 1] as Command;\n const options = args[args.length - 2] as Record<string, unknown>;\n const positionalArgs = args.slice(0, -2);\n const globalOpts = command.parent?.opts() ?? {};\n const logger = loggerFactory({ name, globalOpts, getVersion });\n // Configure from CLI flags first; commands that resolve a config file\n // re-configure via `ConfigResolver.resolve` so config-file\n // `verbose`/`silent` also apply (CLI flags still win there).\n const cliLoggerOptions = {\n verbose: Boolean(globalOpts.verbose) || Boolean(options.verbose),\n silent: Boolean(globalOpts.silent) || Boolean(options.silent),\n };\n warnOnConflictingFlags({ ...cliLoggerOptions, jsonMode: logger.jsonMode });\n logger.configure(cliLoggerOptions);\n fallbackLogger.configure(cliLoggerOptions);\n\n try {\n await handler(logger, options, globalOpts, positionalArgs);\n logger.outputJson(true);\n } catch (error) {\n const code = error instanceof CLIError ? error.code : errorCode;\n const errorArg = error instanceof Error ? error : formatError(error);\n logger.error(errorArg, code);\n process.exit(error instanceof CLIError ? error.exitCode : 1);\n }\n };\n}\n","import { Command } from \"commander\";\n\nimport { ALL_FEATURES, RulesyncFeatures } from \"../types/features.js\";\nimport { FetchOptions } from \"../types/fetch.js\";\nimport type { Logger } from \"../utils/logger.js\";\nimport { parseCommaSeparatedList } from \"../utils/parse-comma-separated-list.js\";\nimport { addCommand, type AddCommandOptions } from \"./commands/add.js\";\nimport { convertCommand, ConvertOptions } from \"./commands/convert.js\";\nimport { docsCommand, type DocsOptions } from \"./commands/docs.js\";\nimport { doctorCommand, type DoctorOptions } from \"./commands/doctor.js\";\nimport { fetchCommand } from \"./commands/fetch.js\";\nimport { generateCommand, GenerateOptions } from \"./commands/generate.js\";\nimport { gitignoreCommand } from \"./commands/gitignore.js\";\nimport { importCommand, ImportOptions } from \"./commands/import.js\";\nimport { initCommand } from \"./commands/init.js\";\nimport { INSTALL_MODES, InstallMode, installCommand } from \"./commands/install.js\";\nimport { mcpCommand } from \"./commands/mcp.js\";\nimport { resolveGitignoreTargets } from \"./commands/resolve-gitignore-targets.js\";\nimport { updateCommand, UpdateCommandOptions } from \"./commands/update.js\";\nimport { wrapCommand as _wrapCommand } from \"./wrap-command.js\";\n\nconst getVersion = () => \"16.4.0\";\nconst FEATURES_HELP = `${ALL_FEATURES.join(\",\")}; ignore is deprecated, use permissions`;\n\nfunction wrapCommand(\n name: string,\n errorCode: string,\n handler: (\n logger: Logger,\n options: unknown,\n globalOpts: Record<string, unknown>,\n positionalArgs: unknown[],\n ) => Promise<void>,\n) {\n return _wrapCommand({ name, errorCode, handler, getVersion });\n}\n\nexport function createProgram(): Command {\n const program = new Command();\n\n const version = getVersion();\n\n program\n .name(\"rulesync\")\n .description(\"Unified AI rules management CLI tool\")\n .version(version, \"-v, --version\", \"Show version\")\n .option(\"-j, --json\", \"Output results as JSON\");\n\n program\n .command(\"init\")\n .description(\"Initialize rulesync in current directory\")\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .action(\n wrapCommand(\"init\", \"INIT_FAILED\", async (logger) => {\n await initCommand(logger);\n }),\n );\n\n program\n .command(\"gitignore\")\n .description(\"Add generated files to .gitignore\")\n .option(\n \"-t, --targets <tools>\",\n \"Comma-separated list of tools to include (e.g., 'claudecode,copilot' or '*' for all)\",\n parseCommaSeparatedList,\n )\n .option(\n \"-f, --features <features>\",\n `Comma-separated list of features to include (${FEATURES_HELP}) or '*' for all`,\n parseCommaSeparatedList,\n )\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .action(\n wrapCommand(\"gitignore\", \"GITIGNORE_FAILED\", async (logger, options) => {\n const cliTargets = (options as { targets?: string[] }).targets;\n const cliFeatures = (options as { features?: RulesyncFeatures }).features;\n\n const resolvedTargets = await resolveGitignoreTargets({ cliTargets });\n\n await gitignoreCommand(logger, {\n targets: resolvedTargets ? [...resolvedTargets] : undefined,\n features: cliFeatures,\n verbose: (options as { verbose?: boolean }).verbose,\n silent: (options as { silent?: boolean }).silent,\n });\n }),\n );\n\n program\n .command(\"add <source>\")\n .description(\n \"Add a Rulesync feature file (ignore is deprecated; use permissions) or install a declarative rule or skill source\",\n )\n .option(\"--name <name>\", \"Name for a rule, command, subagent, skill, or check scaffold\")\n .option(\"-f, --force\", \"Overwrite an existing scaffold file without prompting\")\n .option(\"--skills <skills>\", \"Comma-separated skill names to install\", parseCommaSeparatedList)\n .option(\"--rules <rules>\", \"Comma-separated rule names to install\", parseCommaSeparatedList)\n .option(\"--transport <transport>\", \"Source transport: github, git, or npm\")\n .option(\"-r, --ref <ref>\", \"Git ref, npm version, or npm dist-tag\")\n .option(\"-p, --path <path>\", \"Skills path within the source\")\n .option(\"--rules-path <path>\", \"Rules path within the source\")\n .option(\"--registry <url>\", \"npm-compatible registry URL\")\n .option(\"--token-env <name>\", \"Environment variable containing the npm registry token\")\n .option(\"--token <token>\", \"GitHub token for private repositories\")\n .option(\"-c, --config <path>\", \"Path to configuration file\")\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .action(\n wrapCommand(\"add\", \"ADD_FAILED\", async (logger, options, _globalOpts, positionalArgs) => {\n const source = positionalArgs[0] as string;\n const addOptions = options as Omit<AddCommandOptions, \"source\" | \"configPath\"> & {\n config?: string;\n };\n await addCommand(logger, {\n ...addOptions,\n source,\n configPath: addOptions.config,\n });\n }),\n );\n\n program\n .command(\"fetch <source>\")\n .description(\"Fetch files from a Git repository (GitHub/GitLab)\")\n .option(\n \"-t, --target <target>\",\n \"Target format to interpret files as (e.g., 'rulesync', 'claudecode'). Default: rulesync\",\n )\n .option(\n \"-f, --features <features>\",\n `Comma-separated list of features to fetch (${FEATURES_HELP}) or '*' for all. Default: skills`,\n parseCommaSeparatedList,\n )\n .option(\"-r, --ref <ref>\", \"Branch, tag, or commit SHA to fetch from\")\n .option(\"-p, --path <path>\", \"Subdirectory path within the repository\")\n .option(\"-o, --output <dir>\", \"Output directory (default: .rulesync)\")\n .option(\n \"-c, --conflict <strategy>\",\n \"Conflict resolution strategy: skip, overwrite (default: overwrite)\",\n )\n .option(\"--token <token>\", \"Git provider token for private repositories\")\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .action(\n wrapCommand(\"fetch\", \"FETCH_FAILED\", async (logger, options, _globalOpts, positionalArgs) => {\n const source = positionalArgs[0] as string;\n await fetchCommand(logger, { ...(options as FetchOptions), source });\n }),\n );\n\n program\n .command(\"import\")\n .description(\"Import configurations from AI tools to rulesync format\")\n .option(\n \"-t, --targets <tool>\",\n \"Tool to import from (e.g., 'copilot', 'cursor', 'cline')\",\n parseCommaSeparatedList,\n )\n .option(\n \"-f, --features <features>\",\n `Comma-separated list of features to import (${FEATURES_HELP}) or '*' for all`,\n parseCommaSeparatedList,\n )\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .option(\"-g, --global\", \"Import for global(user scope) configuration files\")\n .option(\n \"-o, --output-root <path>\",\n \"Root directory containing the tool configuration to import\",\n )\n .action(\n wrapCommand(\"import\", \"IMPORT_FAILED\", async (logger, options) => {\n const { outputRoot, ...importOptions } = options as ImportOptions & {\n outputRoot?: string;\n };\n await importCommand(logger, {\n ...importOptions,\n outputRoots: outputRoot ? [outputRoot] : undefined,\n });\n }),\n );\n\n program\n .command(\"convert\")\n .description(\n \"Convert configurations from one AI tool to other AI tools without writing .rulesync/ files\",\n )\n .requiredOption(\"--from <tool>\", \"Source tool to convert from (e.g., 'cursor', 'claudecode')\")\n .requiredOption(\n \"--to <tools>\",\n \"Comma-separated list of destination tools (e.g., 'copilot,claudecode')\",\n parseCommaSeparatedList,\n )\n .option(\n \"-f, --features <features>\",\n `Comma-separated list of features to convert (${FEATURES_HELP}) or '*' for all`,\n parseCommaSeparatedList,\n )\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .option(\"-g, --global\", \"Convert for global(user scope) configuration files\")\n .option(\"--dry-run\", \"Dry run: show changes without writing files\")\n .action(\n wrapCommand(\"convert\", \"CONVERT_FAILED\", async (logger, options) => {\n await convertCommand(logger, options as ConvertOptions);\n }),\n );\n\n program\n .command(\"mcp\")\n .description(\"Start MCP server for rulesync\")\n .action(\n wrapCommand(\"mcp\", \"MCP_FAILED\", async (logger, _options) => {\n await mcpCommand(logger, { version });\n }),\n );\n\n program\n .command(\"install\")\n .description(\n \"Install rules, skills, or primitives from declarative sources (rulesync.jsonc) or apm.yml\",\n )\n .option(\n \"--mode <mode>\",\n `Install layout to produce (${INSTALL_MODES.join(\"|\")}). Default: rulesync`,\n )\n .option(\"--update\", \"Force re-resolve all source refs, ignoring lockfile\")\n .option(\n \"--frozen\",\n \"Fail if lockfile is missing or out of sync (for CI); fetches missing skills using locked refs\",\n )\n .option(\"--token <token>\", \"GitHub token for private repos\")\n .option(\"-c, --config <path>\", \"Path to configuration file\")\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .action(\n wrapCommand(\"install\", \"INSTALL_FAILED\", async (logger, options) => {\n const rawMode = (options as { mode?: string }).mode;\n const mode = parseInstallMode(rawMode);\n await installCommand(logger, {\n mode,\n update: (options as { update?: boolean }).update,\n frozen: (options as { frozen?: boolean }).frozen,\n token: (options as { token?: string }).token,\n configPath: (options as { config?: string }).config,\n verbose: (options as { verbose?: boolean }).verbose,\n silent: (options as { silent?: boolean }).silent,\n });\n }),\n );\n\n program\n .command(\"generate\")\n .description(\"Generate configuration files for AI tools\")\n .option(\n \"-t, --targets <tools>\",\n \"Comma-separated list of tools to generate for (e.g., 'copilot,cursor,cline' or '*' for all)\",\n parseCommaSeparatedList,\n )\n .option(\n \"-f, --features <features>\",\n `Comma-separated list of features to generate (${FEATURES_HELP}) or '*' for all`,\n parseCommaSeparatedList,\n )\n .option(\"--delete\", \"Delete all existing files in output directories before generating\")\n .option(\n \"-o, --output-roots <paths>\",\n \"Output root directories to generate files into (comma-separated for multiple paths)\",\n parseCommaSeparatedList,\n )\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .option(\"-c, --config <path>\", \"Path to configuration file\")\n .option(\"-g, --global\", \"Generate for global(user scope) configuration files\")\n .option(\n \"--simulate-commands\",\n \"Generate simulated commands. This feature is only available for copilot, cursor and codexcli.\",\n )\n .option(\n \"--simulate-subagents\",\n \"Generate simulated subagents. This feature is only available for copilot and codexcli.\",\n )\n .option(\n \"--simulate-skills\",\n \"Generate simulated skills. This feature is only available for copilot, cursor and codexcli.\",\n )\n .option(\n \"--input-root <path>\",\n \"Path to the directory containing .rulesync/ (parent of .rulesync/)\",\n )\n .option(\"--dry-run\", \"Dry run: show changes without writing files\")\n .option(\"--check\", \"Check if files are up to date (exits with code 1 if changes needed)\")\n .option(\n \"-w, --watch\",\n \"Keep running and regenerate whenever rulesync source files change (cannot be combined with --check, --dry-run or --json)\",\n )\n .action(\n wrapCommand(\"generate\", \"GENERATION_FAILED\", async (logger, options) => {\n await generateCommand(logger, options as GenerateOptions);\n }),\n );\n\n program\n .command(\"doctor\")\n .description(\n \"Diagnose the rulesync configuration for common problems (read-only, never writes files)\",\n )\n .option(\"-c, --config <path>\", \"Path to configuration file\")\n .option(\"--strict\", \"Treat warnings as errors (exit with code 1)\")\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .action(\n wrapCommand(\"doctor\", \"DOCTOR_FAILED\", async (logger, options) => {\n await doctorCommand(logger, options as DoctorOptions);\n }),\n );\n\n program\n .command(\"docs [document]\")\n .description(\n \"Print bundled documentation: a document by identifier (e.g. guide/configuration), the list of documents, or --search results\",\n )\n .option(\"--search <text>\", \"Search the bundled documentation and print ranked matches\")\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .action(\n wrapCommand(\"docs\", \"DOCS_FAILED\", async (logger, options, globalOpts, positionalArgs) => {\n // The command's product is raw Markdown on stdout; mixing it with the\n // global --json envelope would break both consumers.\n if (globalOpts.json) {\n throw new Error(\"The docs command prints raw Markdown and does not support --json.\");\n }\n const document = positionalArgs[0] as string | undefined;\n await docsCommand(logger, document, options as DocsOptions);\n }),\n );\n\n program\n .command(\"update\")\n .description(\"Update rulesync to the latest version\")\n .option(\"--check\", \"Check for updates without installing\")\n .option(\"--force\", \"Force update even if already at latest version\")\n .option(\"--token <token>\", \"GitHub token for API access\")\n .option(\"-V, --verbose\", \"Verbose output\")\n .option(\"-s, --silent\", \"Suppress all output\")\n .action(\n wrapCommand(\"update\", \"UPDATE_FAILED\", async (logger, options) => {\n await updateCommand(logger, version, options as UpdateCommandOptions);\n }),\n );\n\n return program;\n}\n\nfunction parseInstallMode(raw: string | undefined): InstallMode | undefined {\n if (raw === undefined) return undefined;\n const match = INSTALL_MODES.find((m) => m === raw);\n if (!match) {\n throw new Error(`Invalid --mode value \"${raw}\". Expected one of: ${INSTALL_MODES.join(\", \")}.`);\n }\n return match;\n}\n","#!/usr/bin/env node\n\nimport { formatError } from \"../utils/error.js\";\nimport { createProgram } from \"./program.js\";\n\nasync function main(): Promise<void> {\n createProgram().parse();\n}\n\nmain().catch((error) => {\n console.error(formatError(error));\n process.exit(1);\n});\n"],"mappings":"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"}
|