safeword 0.8.3 → 0.8.4

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.
@@ -9,7 +9,7 @@ import {
9
9
  reconcile,
10
10
  success,
11
11
  warn
12
- } from "./chunk-VLNT6YSH.js";
12
+ } from "./chunk-TP334635.js";
13
13
  import {
14
14
  SAFEWORD_SCHEMA,
15
15
  exists,
@@ -162,4 +162,4 @@ async function check(options) {
162
162
  export {
163
163
  check
164
164
  };
165
- //# sourceMappingURL=check-Y3AQWR4Y.js.map
165
+ //# sourceMappingURL=check-OUVIK2O6.js.map
@@ -421,6 +421,7 @@ function executeJsonMerge(cwd, path, def, ctx) {
421
421
  const fullPath = join3(cwd, path);
422
422
  const existing = readJson(fullPath) ?? {};
423
423
  const merged = def.merge(existing, ctx);
424
+ if (JSON.stringify(existing) === JSON.stringify(merged)) return;
424
425
  writeJson(fullPath, merged);
425
426
  }
426
427
  function executeJsonUnmerge(cwd, path, def) {
@@ -472,4 +473,4 @@ export {
472
473
  createProjectContext,
473
474
  reconcile
474
475
  };
475
- //# sourceMappingURL=chunk-VLNT6YSH.js.map
476
+ //# sourceMappingURL=chunk-TP334635.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/output.ts","../src/utils/git.ts","../src/utils/context.ts","../src/reconcile.ts"],"sourcesContent":["/**\n * Console output utilities for consistent CLI messaging\n */\n\n/**\n * Print info message\n */\nexport function info(message: string): void {\n console.log(message);\n}\n\n/**\n * Print success message\n */\nexport function success(message: string): void {\n console.log(`✓ ${message}`);\n}\n\n/**\n * Print warning message\n */\nexport function warn(message: string): void {\n console.warn(`⚠ ${message}`);\n}\n\n/**\n * Print error message to stderr\n */\nexport function error(message: string): void {\n console.error(`✗ ${message}`);\n}\n\n/**\n * Print a blank line\n */\nexport function blank(): void {\n console.log('');\n}\n\n/**\n * Print a section header\n */\nexport function header(title: string): void {\n console.log(`\\n${title}`);\n console.log('─'.repeat(title.length));\n}\n\n/**\n * Print a list item\n */\nexport function listItem(item: string, indent = 2): void {\n console.log(`${' '.repeat(indent)}• ${item}`);\n}\n\n/**\n * Print key-value pair\n */\nexport function keyValue(key: string, value: string): void {\n console.log(` ${key}: ${value}`);\n}\n","/**\n * Git utilities for CLI operations\n */\n\nimport { join } from 'node:path';\nimport { exists } from './fs.js';\n\n/**\n * Check if directory is a git repository\n */\nexport function isGitRepo(cwd: string): boolean {\n return exists(join(cwd, '.git'));\n}\n","/**\n * Project Context Utilities\n *\n * Shared helpers for creating ProjectContext objects used by reconcile().\n */\n\nimport { join } from 'node:path';\nimport { readJson } from './fs.js';\nimport { isGitRepo } from './git.js';\nimport { detectProjectType, type PackageJson } from './project-detector.js';\nimport type { ProjectContext } from '../schema.js';\n\n/**\n * Create a ProjectContext from the current working directory.\n *\n * Reads package.json and detects project type for use with reconcile().\n */\nexport function createProjectContext(cwd: string): ProjectContext {\n const packageJson = readJson<PackageJson>(join(cwd, 'package.json'));\n\n return {\n cwd,\n projectType: detectProjectType(packageJson ?? {}, cwd),\n devDeps: packageJson?.devDependencies ?? {},\n isGitRepo: isGitRepo(cwd),\n };\n}\n","/**\n * Reconciliation Engine\n *\n * Computes and executes plans based on SAFEWORD_SCHEMA and project state.\n * This is the single source of truth for all file/dir/config operations.\n */\n\nimport { join } from 'node:path';\nimport {\n exists,\n ensureDir,\n writeFile,\n readFile,\n readFileSafe,\n readJson,\n writeJson,\n remove,\n removeIfEmpty,\n makeScriptsExecutable,\n getTemplatesDir,\n} from './utils/fs.js';\nimport type {\n SafewordSchema,\n ProjectContext,\n FileDefinition,\n JsonMergeDefinition,\n TextPatchDefinition,\n} from './schema.js';\nimport type { ProjectType } from './utils/project-detector.js';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst HUSKY_DIR = '.husky';\n\n/** Check if path should be skipped in non-git repos (husky files) */\nfunction shouldSkipForNonGit(path: string, isGitRepo: boolean): boolean {\n return path.startsWith(HUSKY_DIR) && !isGitRepo;\n}\n\n/** Plan mkdir actions for directories that don't exist */\nfunction planMissingDirs(\n dirs: string[],\n cwd: string,\n isGitRepo: boolean,\n): { actions: Action[]; created: string[] } {\n const actions: Action[] = [];\n const created: string[] = [];\n for (const dir of dirs) {\n if (shouldSkipForNonGit(dir, isGitRepo)) continue;\n if (!exists(join(cwd, dir))) {\n actions.push({ type: 'mkdir', path: dir });\n created.push(dir);\n }\n }\n return { actions, created };\n}\n\n/** Plan text-patch actions for files missing the marker */\nfunction planTextPatches(patches: Record<string, TextPatchDefinition>, cwd: string): Action[] {\n const actions: Action[] = [];\n for (const [filePath, def] of Object.entries(patches)) {\n const content = readFileSafe(join(cwd, filePath)) ?? '';\n if (!content.includes(def.marker)) {\n actions.push({ type: 'text-patch', path: filePath, definition: def });\n }\n }\n return actions;\n}\n\n/** Plan rmdir actions for directories that exist */\nfunction planExistingDirsRemoval(\n dirs: string[],\n cwd: string,\n): { actions: Action[]; removed: string[] } {\n const actions: Action[] = [];\n const removed: string[] = [];\n for (const dir of dirs) {\n if (exists(join(cwd, dir))) {\n actions.push({ type: 'rmdir', path: dir });\n removed.push(dir);\n }\n }\n return { actions, removed };\n}\n\n/** Plan rm actions for files that exist */\nfunction planExistingFilesRemoval(\n files: string[],\n cwd: string,\n): { actions: Action[]; removed: string[] } {\n const actions: Action[] = [];\n const removed: string[] = [];\n for (const filePath of files) {\n if (exists(join(cwd, filePath))) {\n actions.push({ type: 'rm', path: filePath });\n removed.push(filePath);\n }\n }\n return { actions, removed };\n}\n\n/** Check if a .claude path needs parent dir cleanup */\nfunction getClaudeParentDirForCleanup(filePath: string): string | null {\n if (!filePath.startsWith('.claude/')) return null;\n const parentDir = filePath.slice(0, Math.max(0, filePath.lastIndexOf('/')));\n if (\n !parentDir ||\n parentDir === '.claude' ||\n parentDir === '.claude/skills' ||\n parentDir === '.claude/commands'\n ) {\n return null;\n }\n return parentDir;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ReconcileMode = 'install' | 'upgrade' | 'uninstall' | 'uninstall-full';\n\nexport type Action =\n | { type: 'mkdir'; path: string }\n | { type: 'rmdir'; path: string }\n | { type: 'write'; path: string; content: string }\n | { type: 'rm'; path: string }\n | { type: 'chmod'; paths: string[] }\n | { type: 'json-merge'; path: string; definition: JsonMergeDefinition }\n | { type: 'json-unmerge'; path: string; definition: JsonMergeDefinition }\n | { type: 'text-patch'; path: string; definition: TextPatchDefinition }\n | { type: 'text-unpatch'; path: string; definition: TextPatchDefinition };\n\nexport interface ReconcileResult {\n actions: Action[];\n applied: boolean;\n created: string[];\n updated: string[];\n removed: string[];\n packagesToInstall: string[];\n packagesToRemove: string[];\n}\n\nexport interface ReconcileOptions {\n dryRun?: boolean;\n}\n\n// ============================================================================\n// Main reconcile function\n// ============================================================================\n\nexport async function reconcile(\n schema: SafewordSchema,\n mode: ReconcileMode,\n ctx: ProjectContext,\n options?: ReconcileOptions,\n): Promise<ReconcileResult> {\n const dryRun = options?.dryRun ?? false;\n\n const plan = computePlan(schema, mode, ctx);\n\n if (dryRun) {\n return {\n actions: plan.actions,\n applied: false,\n created: plan.wouldCreate,\n updated: plan.wouldUpdate,\n removed: plan.wouldRemove,\n packagesToInstall: plan.packagesToInstall,\n packagesToRemove: plan.packagesToRemove,\n };\n }\n\n const result = executePlan(plan, ctx);\n\n return {\n actions: plan.actions,\n applied: true,\n created: result.created,\n updated: result.updated,\n removed: result.removed,\n packagesToInstall: plan.packagesToInstall,\n packagesToRemove: plan.packagesToRemove,\n };\n}\n\n// ============================================================================\n// Plan computation\n// ============================================================================\n\ninterface ReconcilePlan {\n actions: Action[];\n wouldCreate: string[];\n wouldUpdate: string[];\n wouldRemove: string[];\n packagesToInstall: string[];\n packagesToRemove: string[];\n}\n\nfunction planDeprecatedFilesRemoval(\n deprecatedFiles: string[],\n cwd: string,\n): { actions: Action[]; removed: string[] } {\n const actions: Action[] = [];\n const removed: string[] = [];\n for (const filePath of deprecatedFiles) {\n if (exists(join(cwd, filePath))) {\n actions.push({ type: 'rm', path: filePath });\n removed.push(filePath);\n }\n }\n return { actions, removed };\n}\n\nfunction computePlan(\n schema: SafewordSchema,\n mode: ReconcileMode,\n ctx: ProjectContext,\n): ReconcilePlan {\n switch (mode) {\n case 'install': {\n return computeInstallPlan(schema, ctx);\n }\n case 'upgrade': {\n return computeUpgradePlan(schema, ctx);\n }\n case 'uninstall': {\n return computeUninstallPlan(schema, ctx, false);\n }\n case 'uninstall-full': {\n return computeUninstallPlan(schema, ctx, true);\n }\n }\n}\n\nfunction computeInstallPlan(schema: SafewordSchema, ctx: ProjectContext): ReconcilePlan {\n const actions: Action[] = [];\n const wouldCreate: string[] = [];\n\n // 1. Create all directories (skip .husky if not a git repo)\n const allDirs = [...schema.ownedDirs, ...schema.sharedDirs, ...schema.preservedDirs];\n const missingDirs = planMissingDirs(allDirs, ctx.cwd, ctx.isGitRepo);\n actions.push(...missingDirs.actions);\n wouldCreate.push(...missingDirs.created);\n\n // 2. Write all owned files (skip .husky files if not a git repo)\n for (const [filePath, def] of Object.entries(schema.ownedFiles)) {\n if (shouldSkipForNonGit(filePath, ctx.isGitRepo)) continue;\n\n const content = resolveFileContent(def, ctx);\n actions.push({ type: 'write', path: filePath, content });\n wouldCreate.push(filePath);\n }\n\n // 3. Write managed files (only if missing)\n for (const [filePath, def] of Object.entries(schema.managedFiles)) {\n const fullPath = join(ctx.cwd, filePath);\n if (!exists(fullPath)) {\n const content = resolveFileContent(def, ctx);\n actions.push({ type: 'write', path: filePath, content });\n wouldCreate.push(filePath);\n }\n }\n\n // 4. chmod hook/lib directories (only .husky if git repo)\n const chmodPaths = ['.safeword/hooks', '.safeword/hooks/cursor', '.safeword/lib'];\n if (ctx.isGitRepo) chmodPaths.push(HUSKY_DIR);\n actions.push({ type: 'chmod', paths: chmodPaths });\n\n // 5. JSON merges\n for (const [filePath, def] of Object.entries(schema.jsonMerges)) {\n actions.push({ type: 'json-merge', path: filePath, definition: def });\n }\n\n // 6. Text patches\n for (const [filePath, def] of Object.entries(schema.textPatches)) {\n actions.push({ type: 'text-patch', path: filePath, definition: def });\n if (def.createIfMissing && !exists(join(ctx.cwd, filePath))) {\n wouldCreate.push(filePath);\n }\n }\n\n // 7. Compute packages to install (husky/lint-staged skipped if no git repo)\n const packagesToInstall = computePackagesToInstall(\n schema,\n ctx.projectType,\n ctx.devDeps,\n ctx.isGitRepo,\n );\n\n return {\n actions,\n wouldCreate,\n wouldUpdate: [],\n wouldRemove: [],\n packagesToInstall,\n packagesToRemove: [],\n };\n}\n\nfunction computeUpgradePlan(schema: SafewordSchema, ctx: ProjectContext): ReconcilePlan {\n const actions: Action[] = [];\n const wouldCreate: string[] = [];\n const wouldUpdate: string[] = [];\n\n // 1. Ensure directories exist (skip .husky if not a git repo)\n const allDirs = [...schema.ownedDirs, ...schema.sharedDirs, ...schema.preservedDirs];\n const missingDirs = planMissingDirs(allDirs, ctx.cwd, ctx.isGitRepo);\n actions.push(...missingDirs.actions);\n wouldCreate.push(...missingDirs.created);\n\n // 2. Update owned files if content changed (skip .husky files if not a git repo)\n for (const [filePath, def] of Object.entries(schema.ownedFiles)) {\n if (shouldSkipForNonGit(filePath, ctx.isGitRepo)) continue;\n\n const fullPath = join(ctx.cwd, filePath);\n const newContent = resolveFileContent(def, ctx);\n\n if (!fileNeedsUpdate(fullPath, newContent)) continue;\n\n actions.push({ type: 'write', path: filePath, content: newContent });\n if (exists(fullPath)) {\n wouldUpdate.push(filePath);\n } else {\n wouldCreate.push(filePath);\n }\n }\n\n // 3. Update managed files only if content matches current template\n for (const [filePath, def] of Object.entries(schema.managedFiles)) {\n const fullPath = join(ctx.cwd, filePath);\n const newContent = resolveFileContent(def, ctx);\n\n if (!exists(fullPath)) {\n // Missing - create it\n actions.push({ type: 'write', path: filePath, content: newContent });\n wouldCreate.push(filePath);\n }\n // If file exists, don't update during upgrade - user may have customized it\n }\n\n // 4. Remove deprecated files (renamed or removed in newer versions)\n const deprecatedFiles = planDeprecatedFilesRemoval(schema.deprecatedFiles, ctx.cwd);\n actions.push(...deprecatedFiles.actions);\n const wouldRemove = deprecatedFiles.removed;\n\n // 5. chmod (only .husky if git repo)\n const chmodPathsUpgrade = ['.safeword/hooks', '.safeword/hooks/cursor', '.safeword/lib'];\n if (ctx.isGitRepo) chmodPathsUpgrade.push(HUSKY_DIR);\n actions.push({ type: 'chmod', paths: chmodPathsUpgrade });\n\n // 6. JSON merges (always apply to ensure keys are present)\n for (const [filePath, def] of Object.entries(schema.jsonMerges)) {\n actions.push({ type: 'json-merge', path: filePath, definition: def });\n }\n\n // 7. Text patches (only if marker missing)\n actions.push(...planTextPatches(schema.textPatches, ctx.cwd));\n\n // 8. Compute packages to install (husky/lint-staged skipped if no git repo)\n const packagesToInstall = computePackagesToInstall(\n schema,\n ctx.projectType,\n ctx.devDeps,\n ctx.isGitRepo,\n );\n\n return {\n actions,\n wouldCreate,\n wouldUpdate,\n wouldRemove,\n packagesToInstall,\n packagesToRemove: [],\n };\n}\n\nfunction computeUninstallPlan(\n schema: SafewordSchema,\n ctx: ProjectContext,\n full: boolean,\n): ReconcilePlan {\n const actions: Action[] = [];\n const wouldRemove: string[] = [];\n\n // 1. Remove all owned files and track parent dirs for cleanup\n const ownedFiles = planExistingFilesRemoval(Object.keys(schema.ownedFiles), ctx.cwd);\n actions.push(...ownedFiles.actions);\n wouldRemove.push(...ownedFiles.removed);\n\n // Collect parent dirs that need cleanup (for .claude/* skill dirs)\n const dirsToCleanup = new Set<string>();\n for (const filePath of ownedFiles.removed) {\n const parentDir = getClaudeParentDirForCleanup(filePath);\n if (parentDir) dirsToCleanup.add(parentDir);\n }\n const cleanupDirs = planExistingDirsRemoval([...dirsToCleanup], ctx.cwd);\n actions.push(...cleanupDirs.actions);\n wouldRemove.push(...cleanupDirs.removed);\n\n // 2. JSON unmerges\n for (const [filePath, def] of Object.entries(schema.jsonMerges)) {\n actions.push({ type: 'json-unmerge', path: filePath, definition: def });\n }\n\n // 3. Text unpatches\n for (const [filePath, def] of Object.entries(schema.textPatches)) {\n const fullPath = join(ctx.cwd, filePath);\n if (exists(fullPath)) {\n const content = readFileSafe(fullPath) ?? '';\n if (content.includes(def.marker)) {\n actions.push({ type: 'text-unpatch', path: filePath, definition: def });\n }\n }\n }\n\n // 4. Remove preserved directories first (reverse order, only if empty)\n const preserved = planExistingDirsRemoval(schema.preservedDirs.toReversed(), ctx.cwd);\n actions.push(...preserved.actions);\n wouldRemove.push(...preserved.removed);\n\n // 5. Remove owned directories (reverse order ensures children before parents)\n const owned = planExistingDirsRemoval(schema.ownedDirs.toReversed(), ctx.cwd);\n actions.push(...owned.actions);\n wouldRemove.push(...owned.removed);\n\n // 6. Full uninstall: remove managed files\n if (full) {\n const managed = planExistingFilesRemoval(Object.keys(schema.managedFiles), ctx.cwd);\n actions.push(...managed.actions);\n wouldRemove.push(...managed.removed);\n }\n\n // 7. Compute packages to remove (full only)\n const packagesToRemove = full\n ? computePackagesToRemove(schema, ctx.projectType, ctx.devDeps)\n : [];\n\n return {\n actions,\n wouldCreate: [],\n wouldUpdate: [],\n wouldRemove,\n packagesToInstall: [],\n packagesToRemove,\n };\n}\n\n// ============================================================================\n// Plan execution\n// ============================================================================\n\ninterface ExecutionResult {\n created: string[];\n updated: string[];\n removed: string[];\n}\n\nfunction executePlan(plan: ReconcilePlan, ctx: ProjectContext): ExecutionResult {\n const created: string[] = [];\n const updated: string[] = [];\n const removed: string[] = [];\n const result = { created, updated, removed };\n\n for (const action of plan.actions) {\n executeAction(action, ctx, result);\n }\n\n return result;\n}\n\nfunction executeAction(action: Action, ctx: ProjectContext, result: ExecutionResult): void {\n switch (action.type) {\n case 'mkdir': {\n ensureDir(join(ctx.cwd, action.path));\n result.created.push(action.path);\n break;\n }\n\n case 'rmdir': {\n // Use removeIfEmpty to preserve directories with user content\n if (removeIfEmpty(join(ctx.cwd, action.path))) {\n result.removed.push(action.path);\n }\n break;\n }\n\n case 'write': {\n executeWrite(ctx.cwd, action.path, action.content, result);\n break;\n }\n\n case 'rm': {\n remove(join(ctx.cwd, action.path));\n result.removed.push(action.path);\n break;\n }\n\n case 'chmod': {\n for (const path of action.paths) {\n const fullPath = join(ctx.cwd, path);\n if (exists(fullPath)) makeScriptsExecutable(fullPath);\n }\n break;\n }\n\n case 'json-merge': {\n executeJsonMerge(ctx.cwd, action.path, action.definition, ctx);\n break;\n }\n\n case 'json-unmerge': {\n executeJsonUnmerge(ctx.cwd, action.path, action.definition);\n break;\n }\n\n case 'text-patch': {\n executeTextPatch(ctx.cwd, action.path, action.definition);\n break;\n }\n\n case 'text-unpatch': {\n executeTextUnpatch(ctx.cwd, action.path, action.definition);\n break;\n }\n }\n}\n\nfunction executeWrite(cwd: string, path: string, content: string, result: ExecutionResult): void {\n const fullPath = join(cwd, path);\n const existed = exists(fullPath);\n writeFile(fullPath, content);\n (existed ? result.updated : result.created).push(path);\n}\n\n// ============================================================================\n// Helper functions\n// ============================================================================\n\nfunction resolveFileContent(def: FileDefinition, ctx: ProjectContext): string {\n if (def.template) {\n const templatesDir = getTemplatesDir();\n return readFile(join(templatesDir, def.template));\n }\n\n if (def.content) {\n return typeof def.content === 'function' ? def.content() : def.content;\n }\n\n if (def.generator) {\n return def.generator(ctx);\n }\n\n throw new Error('FileDefinition must have template, content, or generator');\n}\n\nfunction fileNeedsUpdate(installedPath: string, newContent: string): boolean {\n if (!exists(installedPath)) return true;\n const currentContent = readFileSafe(installedPath);\n return currentContent?.trim() !== newContent.trim();\n}\n\n// Packages that require git repo\nconst GIT_ONLY_PACKAGES = new Set(['husky', 'lint-staged']);\n\nexport function computePackagesToInstall(\n schema: SafewordSchema,\n projectType: ProjectType,\n installedDevDeps: Record<string, string>,\n isGitRepo = true,\n): string[] {\n let needed = [...schema.packages.base];\n\n // Filter out git-only packages when not in a git repo\n if (!isGitRepo) {\n needed = needed.filter(pkg => !GIT_ONLY_PACKAGES.has(pkg));\n }\n\n for (const [key, deps] of Object.entries(schema.packages.conditional)) {\n if (projectType[key as keyof ProjectType]) {\n needed.push(...deps);\n }\n }\n\n return needed.filter(pkg => !(pkg in installedDevDeps));\n}\n\nfunction computePackagesToRemove(\n schema: SafewordSchema,\n projectType: ProjectType,\n installedDevDeps: Record<string, string>,\n): string[] {\n const safewordPackages = [...schema.packages.base];\n\n for (const [key, deps] of Object.entries(schema.packages.conditional)) {\n if (projectType[key as keyof ProjectType]) {\n safewordPackages.push(...deps);\n }\n }\n\n // Only remove packages that are actually installed\n return safewordPackages.filter(pkg => pkg in installedDevDeps);\n}\n\nfunction executeJsonMerge(\n cwd: string,\n path: string,\n def: JsonMergeDefinition,\n ctx: ProjectContext,\n): void {\n const fullPath = join(cwd, path);\n const existing = readJson<Record<string, unknown>>(fullPath) ?? {};\n const merged = def.merge(existing, ctx);\n writeJson(fullPath, merged);\n}\n\nfunction executeJsonUnmerge(cwd: string, path: string, def: JsonMergeDefinition): void {\n const fullPath = join(cwd, path);\n if (!exists(fullPath)) return;\n\n const existing = readJson<Record<string, unknown>>(fullPath);\n if (!existing) return;\n\n const unmerged = def.unmerge(existing);\n\n // Check if file should be removed\n if (def.removeFileIfEmpty) {\n const remainingKeys = Object.keys(unmerged).filter(\n k => unmerged[k] !== undefined && unmerged[k] !== null,\n );\n if (remainingKeys.length === 0) {\n remove(fullPath);\n return;\n }\n }\n\n writeJson(fullPath, unmerged);\n}\n\nfunction executeTextPatch(cwd: string, path: string, def: TextPatchDefinition): void {\n const fullPath = join(cwd, path);\n let content = readFileSafe(fullPath) ?? '';\n\n // Check if already patched\n if (content.includes(def.marker)) return;\n\n // Apply patch\n content = def.operation === 'prepend' ? def.content + content : content + def.content;\n\n writeFile(fullPath, content);\n}\n\nfunction executeTextUnpatch(cwd: string, path: string, def: TextPatchDefinition): void {\n const fullPath = join(cwd, path);\n const content = readFileSafe(fullPath);\n if (!content) return;\n\n // Remove the patched content\n // First try to remove the full content block\n let unpatched = content.replace(def.content, '');\n\n // If full content wasn't found but marker exists, remove lines containing the marker\n if (unpatched === content && content.includes(def.marker)) {\n // Remove lines containing the marker\n const lines = content.split('\\n');\n const filtered = lines.filter(line => !line.includes(def.marker));\n unpatched = filtered.join('\\n').replace(/^\\n+/, ''); // Remove leading empty lines\n }\n\n writeFile(fullPath, unpatched);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAOO,SAAS,KAAK,SAAuB;AAC1C,UAAQ,IAAI,OAAO;AACrB;AAKO,SAAS,QAAQ,SAAuB;AAC7C,UAAQ,IAAI,UAAK,OAAO,EAAE;AAC5B;AAKO,SAAS,KAAK,SAAuB;AAC1C,UAAQ,KAAK,UAAK,OAAO,EAAE;AAC7B;AAKO,SAAS,MAAM,SAAuB;AAC3C,UAAQ,MAAM,UAAK,OAAO,EAAE;AAC9B;AAYO,SAAS,OAAO,OAAqB;AAC1C,UAAQ,IAAI;AAAA,EAAK,KAAK,EAAE;AACxB,UAAQ,IAAI,SAAI,OAAO,MAAM,MAAM,CAAC;AACtC;AAKO,SAAS,SAAS,MAAc,SAAS,GAAS;AACvD,UAAQ,IAAI,GAAG,IAAI,OAAO,MAAM,CAAC,UAAK,IAAI,EAAE;AAC9C;AAKO,SAAS,SAAS,KAAa,OAAqB;AACzD,UAAQ,IAAI,KAAK,GAAG,KAAK,KAAK,EAAE;AAClC;;;ACvDA,SAAS,YAAY;AAMd,SAAS,UAAU,KAAsB;AAC9C,SAAO,OAAO,KAAK,KAAK,MAAM,CAAC;AACjC;;;ACNA,SAAS,QAAAA,aAAY;AAWd,SAAS,qBAAqB,KAA6B;AAChE,QAAM,cAAc,SAAsBC,MAAK,KAAK,cAAc,CAAC;AAEnE,SAAO;AAAA,IACL;AAAA,IACA,aAAa,kBAAkB,eAAe,CAAC,GAAG,GAAG;AAAA,IACrD,SAAS,aAAa,mBAAmB,CAAC;AAAA,IAC1C,WAAW,UAAU,GAAG;AAAA,EAC1B;AACF;;;ACnBA,SAAS,QAAAC,aAAY;AA2BrB,IAAM,YAAY;AAGlB,SAAS,oBAAoB,MAAcC,YAA6B;AACtE,SAAO,KAAK,WAAW,SAAS,KAAK,CAACA;AACxC;AAGA,SAAS,gBACP,MACA,KACAA,YAC0C;AAC1C,QAAM,UAAoB,CAAC;AAC3B,QAAM,UAAoB,CAAC;AAC3B,aAAW,OAAO,MAAM;AACtB,QAAI,oBAAoB,KAAKA,UAAS,EAAG;AACzC,QAAI,CAAC,OAAOC,MAAK,KAAK,GAAG,CAAC,GAAG;AAC3B,cAAQ,KAAK,EAAE,MAAM,SAAS,MAAM,IAAI,CAAC;AACzC,cAAQ,KAAK,GAAG;AAAA,IAClB;AAAA,EACF;AACA,SAAO,EAAE,SAAS,QAAQ;AAC5B;AAGA,SAAS,gBAAgB,SAA8C,KAAuB;AAC5F,QAAM,UAAoB,CAAC;AAC3B,aAAW,CAAC,UAAU,GAAG,KAAK,OAAO,QAAQ,OAAO,GAAG;AACrD,UAAM,UAAU,aAAaA,MAAK,KAAK,QAAQ,CAAC,KAAK;AACrD,QAAI,CAAC,QAAQ,SAAS,IAAI,MAAM,GAAG;AACjC,cAAQ,KAAK,EAAE,MAAM,cAAc,MAAM,UAAU,YAAY,IAAI,CAAC;AAAA,IACtE;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,wBACP,MACA,KAC0C;AAC1C,QAAM,UAAoB,CAAC;AAC3B,QAAM,UAAoB,CAAC;AAC3B,aAAW,OAAO,MAAM;AACtB,QAAI,OAAOA,MAAK,KAAK,GAAG,CAAC,GAAG;AAC1B,cAAQ,KAAK,EAAE,MAAM,SAAS,MAAM,IAAI,CAAC;AACzC,cAAQ,KAAK,GAAG;AAAA,IAClB;AAAA,EACF;AACA,SAAO,EAAE,SAAS,QAAQ;AAC5B;AAGA,SAAS,yBACP,OACA,KAC0C;AAC1C,QAAM,UAAoB,CAAC;AAC3B,QAAM,UAAoB,CAAC;AAC3B,aAAW,YAAY,OAAO;AAC5B,QAAI,OAAOA,MAAK,KAAK,QAAQ,CAAC,GAAG;AAC/B,cAAQ,KAAK,EAAE,MAAM,MAAM,MAAM,SAAS,CAAC;AAC3C,cAAQ,KAAK,QAAQ;AAAA,IACvB;AAAA,EACF;AACA,SAAO,EAAE,SAAS,QAAQ;AAC5B;AAGA,SAAS,6BAA6B,UAAiC;AACrE,MAAI,CAAC,SAAS,WAAW,UAAU,EAAG,QAAO;AAC7C,QAAM,YAAY,SAAS,MAAM,GAAG,KAAK,IAAI,GAAG,SAAS,YAAY,GAAG,CAAC,CAAC;AAC1E,MACE,CAAC,aACD,cAAc,aACd,cAAc,oBACd,cAAc,oBACd;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAqCA,eAAsB,UACpB,QACA,MACA,KACA,SAC0B;AAC1B,QAAM,SAAS,SAAS,UAAU;AAElC,QAAM,OAAO,YAAY,QAAQ,MAAM,GAAG;AAE1C,MAAI,QAAQ;AACV,WAAO;AAAA,MACL,SAAS,KAAK;AAAA,MACd,SAAS;AAAA,MACT,SAAS,KAAK;AAAA,MACd,SAAS,KAAK;AAAA,MACd,SAAS,KAAK;AAAA,MACd,mBAAmB,KAAK;AAAA,MACxB,kBAAkB,KAAK;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,SAAS,YAAY,MAAM,GAAG;AAEpC,SAAO;AAAA,IACL,SAAS,KAAK;AAAA,IACd,SAAS;AAAA,IACT,SAAS,OAAO;AAAA,IAChB,SAAS,OAAO;AAAA,IAChB,SAAS,OAAO;AAAA,IAChB,mBAAmB,KAAK;AAAA,IACxB,kBAAkB,KAAK;AAAA,EACzB;AACF;AAeA,SAAS,2BACP,iBACA,KAC0C;AAC1C,QAAM,UAAoB,CAAC;AAC3B,QAAM,UAAoB,CAAC;AAC3B,aAAW,YAAY,iBAAiB;AACtC,QAAI,OAAOA,MAAK,KAAK,QAAQ,CAAC,GAAG;AAC/B,cAAQ,KAAK,EAAE,MAAM,MAAM,MAAM,SAAS,CAAC;AAC3C,cAAQ,KAAK,QAAQ;AAAA,IACvB;AAAA,EACF;AACA,SAAO,EAAE,SAAS,QAAQ;AAC5B;AAEA,SAAS,YACP,QACA,MACA,KACe;AACf,UAAQ,MAAM;AAAA,IACZ,KAAK,WAAW;AACd,aAAO,mBAAmB,QAAQ,GAAG;AAAA,IACvC;AAAA,IACA,KAAK,WAAW;AACd,aAAO,mBAAmB,QAAQ,GAAG;AAAA,IACvC;AAAA,IACA,KAAK,aAAa;AAChB,aAAO,qBAAqB,QAAQ,KAAK,KAAK;AAAA,IAChD;AAAA,IACA,KAAK,kBAAkB;AACrB,aAAO,qBAAqB,QAAQ,KAAK,IAAI;AAAA,IAC/C;AAAA,EACF;AACF;AAEA,SAAS,mBAAmB,QAAwB,KAAoC;AACtF,QAAM,UAAoB,CAAC;AAC3B,QAAM,cAAwB,CAAC;AAG/B,QAAM,UAAU,CAAC,GAAG,OAAO,WAAW,GAAG,OAAO,YAAY,GAAG,OAAO,aAAa;AACnF,QAAM,cAAc,gBAAgB,SAAS,IAAI,KAAK,IAAI,SAAS;AACnE,UAAQ,KAAK,GAAG,YAAY,OAAO;AACnC,cAAY,KAAK,GAAG,YAAY,OAAO;AAGvC,aAAW,CAAC,UAAU,GAAG,KAAK,OAAO,QAAQ,OAAO,UAAU,GAAG;AAC/D,QAAI,oBAAoB,UAAU,IAAI,SAAS,EAAG;AAElD,UAAM,UAAU,mBAAmB,KAAK,GAAG;AAC3C,YAAQ,KAAK,EAAE,MAAM,SAAS,MAAM,UAAU,QAAQ,CAAC;AACvD,gBAAY,KAAK,QAAQ;AAAA,EAC3B;AAGA,aAAW,CAAC,UAAU,GAAG,KAAK,OAAO,QAAQ,OAAO,YAAY,GAAG;AACjE,UAAM,WAAWA,MAAK,IAAI,KAAK,QAAQ;AACvC,QAAI,CAAC,OAAO,QAAQ,GAAG;AACrB,YAAM,UAAU,mBAAmB,KAAK,GAAG;AAC3C,cAAQ,KAAK,EAAE,MAAM,SAAS,MAAM,UAAU,QAAQ,CAAC;AACvD,kBAAY,KAAK,QAAQ;AAAA,IAC3B;AAAA,EACF;AAGA,QAAM,aAAa,CAAC,mBAAmB,0BAA0B,eAAe;AAChF,MAAI,IAAI,UAAW,YAAW,KAAK,SAAS;AAC5C,UAAQ,KAAK,EAAE,MAAM,SAAS,OAAO,WAAW,CAAC;AAGjD,aAAW,CAAC,UAAU,GAAG,KAAK,OAAO,QAAQ,OAAO,UAAU,GAAG;AAC/D,YAAQ,KAAK,EAAE,MAAM,cAAc,MAAM,UAAU,YAAY,IAAI,CAAC;AAAA,EACtE;AAGA,aAAW,CAAC,UAAU,GAAG,KAAK,OAAO,QAAQ,OAAO,WAAW,GAAG;AAChE,YAAQ,KAAK,EAAE,MAAM,cAAc,MAAM,UAAU,YAAY,IAAI,CAAC;AACpE,QAAI,IAAI,mBAAmB,CAAC,OAAOA,MAAK,IAAI,KAAK,QAAQ,CAAC,GAAG;AAC3D,kBAAY,KAAK,QAAQ;AAAA,IAC3B;AAAA,EACF;AAGA,QAAM,oBAAoB;AAAA,IACxB;AAAA,IACA,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,EACN;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,aAAa,CAAC;AAAA,IACd,aAAa,CAAC;AAAA,IACd;AAAA,IACA,kBAAkB,CAAC;AAAA,EACrB;AACF;AAEA,SAAS,mBAAmB,QAAwB,KAAoC;AACtF,QAAM,UAAoB,CAAC;AAC3B,QAAM,cAAwB,CAAC;AAC/B,QAAM,cAAwB,CAAC;AAG/B,QAAM,UAAU,CAAC,GAAG,OAAO,WAAW,GAAG,OAAO,YAAY,GAAG,OAAO,aAAa;AACnF,QAAM,cAAc,gBAAgB,SAAS,IAAI,KAAK,IAAI,SAAS;AACnE,UAAQ,KAAK,GAAG,YAAY,OAAO;AACnC,cAAY,KAAK,GAAG,YAAY,OAAO;AAGvC,aAAW,CAAC,UAAU,GAAG,KAAK,OAAO,QAAQ,OAAO,UAAU,GAAG;AAC/D,QAAI,oBAAoB,UAAU,IAAI,SAAS,EAAG;AAElD,UAAM,WAAWA,MAAK,IAAI,KAAK,QAAQ;AACvC,UAAM,aAAa,mBAAmB,KAAK,GAAG;AAE9C,QAAI,CAAC,gBAAgB,UAAU,UAAU,EAAG;AAE5C,YAAQ,KAAK,EAAE,MAAM,SAAS,MAAM,UAAU,SAAS,WAAW,CAAC;AACnE,QAAI,OAAO,QAAQ,GAAG;AACpB,kBAAY,KAAK,QAAQ;AAAA,IAC3B,OAAO;AACL,kBAAY,KAAK,QAAQ;AAAA,IAC3B;AAAA,EACF;AAGA,aAAW,CAAC,UAAU,GAAG,KAAK,OAAO,QAAQ,OAAO,YAAY,GAAG;AACjE,UAAM,WAAWA,MAAK,IAAI,KAAK,QAAQ;AACvC,UAAM,aAAa,mBAAmB,KAAK,GAAG;AAE9C,QAAI,CAAC,OAAO,QAAQ,GAAG;AAErB,cAAQ,KAAK,EAAE,MAAM,SAAS,MAAM,UAAU,SAAS,WAAW,CAAC;AACnE,kBAAY,KAAK,QAAQ;AAAA,IAC3B;AAAA,EAEF;AAGA,QAAM,kBAAkB,2BAA2B,OAAO,iBAAiB,IAAI,GAAG;AAClF,UAAQ,KAAK,GAAG,gBAAgB,OAAO;AACvC,QAAM,cAAc,gBAAgB;AAGpC,QAAM,oBAAoB,CAAC,mBAAmB,0BAA0B,eAAe;AACvF,MAAI,IAAI,UAAW,mBAAkB,KAAK,SAAS;AACnD,UAAQ,KAAK,EAAE,MAAM,SAAS,OAAO,kBAAkB,CAAC;AAGxD,aAAW,CAAC,UAAU,GAAG,KAAK,OAAO,QAAQ,OAAO,UAAU,GAAG;AAC/D,YAAQ,KAAK,EAAE,MAAM,cAAc,MAAM,UAAU,YAAY,IAAI,CAAC;AAAA,EACtE;AAGA,UAAQ,KAAK,GAAG,gBAAgB,OAAO,aAAa,IAAI,GAAG,CAAC;AAG5D,QAAM,oBAAoB;AAAA,IACxB;AAAA,IACA,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,EACN;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB,CAAC;AAAA,EACrB;AACF;AAEA,SAAS,qBACP,QACA,KACA,MACe;AACf,QAAM,UAAoB,CAAC;AAC3B,QAAM,cAAwB,CAAC;AAG/B,QAAM,aAAa,yBAAyB,OAAO,KAAK,OAAO,UAAU,GAAG,IAAI,GAAG;AACnF,UAAQ,KAAK,GAAG,WAAW,OAAO;AAClC,cAAY,KAAK,GAAG,WAAW,OAAO;AAGtC,QAAM,gBAAgB,oBAAI,IAAY;AACtC,aAAW,YAAY,WAAW,SAAS;AACzC,UAAM,YAAY,6BAA6B,QAAQ;AACvD,QAAI,UAAW,eAAc,IAAI,SAAS;AAAA,EAC5C;AACA,QAAM,cAAc,wBAAwB,CAAC,GAAG,aAAa,GAAG,IAAI,GAAG;AACvE,UAAQ,KAAK,GAAG,YAAY,OAAO;AACnC,cAAY,KAAK,GAAG,YAAY,OAAO;AAGvC,aAAW,CAAC,UAAU,GAAG,KAAK,OAAO,QAAQ,OAAO,UAAU,GAAG;AAC/D,YAAQ,KAAK,EAAE,MAAM,gBAAgB,MAAM,UAAU,YAAY,IAAI,CAAC;AAAA,EACxE;AAGA,aAAW,CAAC,UAAU,GAAG,KAAK,OAAO,QAAQ,OAAO,WAAW,GAAG;AAChE,UAAM,WAAWA,MAAK,IAAI,KAAK,QAAQ;AACvC,QAAI,OAAO,QAAQ,GAAG;AACpB,YAAM,UAAU,aAAa,QAAQ,KAAK;AAC1C,UAAI,QAAQ,SAAS,IAAI,MAAM,GAAG;AAChC,gBAAQ,KAAK,EAAE,MAAM,gBAAgB,MAAM,UAAU,YAAY,IAAI,CAAC;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AAGA,QAAM,YAAY,wBAAwB,OAAO,cAAc,WAAW,GAAG,IAAI,GAAG;AACpF,UAAQ,KAAK,GAAG,UAAU,OAAO;AACjC,cAAY,KAAK,GAAG,UAAU,OAAO;AAGrC,QAAM,QAAQ,wBAAwB,OAAO,UAAU,WAAW,GAAG,IAAI,GAAG;AAC5E,UAAQ,KAAK,GAAG,MAAM,OAAO;AAC7B,cAAY,KAAK,GAAG,MAAM,OAAO;AAGjC,MAAI,MAAM;AACR,UAAM,UAAU,yBAAyB,OAAO,KAAK,OAAO,YAAY,GAAG,IAAI,GAAG;AAClF,YAAQ,KAAK,GAAG,QAAQ,OAAO;AAC/B,gBAAY,KAAK,GAAG,QAAQ,OAAO;AAAA,EACrC;AAGA,QAAM,mBAAmB,OACrB,wBAAwB,QAAQ,IAAI,aAAa,IAAI,OAAO,IAC5D,CAAC;AAEL,SAAO;AAAA,IACL;AAAA,IACA,aAAa,CAAC;AAAA,IACd,aAAa,CAAC;AAAA,IACd;AAAA,IACA,mBAAmB,CAAC;AAAA,IACpB;AAAA,EACF;AACF;AAYA,SAAS,YAAY,MAAqB,KAAsC;AAC9E,QAAM,UAAoB,CAAC;AAC3B,QAAM,UAAoB,CAAC;AAC3B,QAAM,UAAoB,CAAC;AAC3B,QAAM,SAAS,EAAE,SAAS,SAAS,QAAQ;AAE3C,aAAW,UAAU,KAAK,SAAS;AACjC,kBAAc,QAAQ,KAAK,MAAM;AAAA,EACnC;AAEA,SAAO;AACT;AAEA,SAAS,cAAc,QAAgB,KAAqB,QAA+B;AACzF,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK,SAAS;AACZ,gBAAUA,MAAK,IAAI,KAAK,OAAO,IAAI,CAAC;AACpC,aAAO,QAAQ,KAAK,OAAO,IAAI;AAC/B;AAAA,IACF;AAAA,IAEA,KAAK,SAAS;AAEZ,UAAI,cAAcA,MAAK,IAAI,KAAK,OAAO,IAAI,CAAC,GAAG;AAC7C,eAAO,QAAQ,KAAK,OAAO,IAAI;AAAA,MACjC;AACA;AAAA,IACF;AAAA,IAEA,KAAK,SAAS;AACZ,mBAAa,IAAI,KAAK,OAAO,MAAM,OAAO,SAAS,MAAM;AACzD;AAAA,IACF;AAAA,IAEA,KAAK,MAAM;AACT,aAAOA,MAAK,IAAI,KAAK,OAAO,IAAI,CAAC;AACjC,aAAO,QAAQ,KAAK,OAAO,IAAI;AAC/B;AAAA,IACF;AAAA,IAEA,KAAK,SAAS;AACZ,iBAAW,QAAQ,OAAO,OAAO;AAC/B,cAAM,WAAWA,MAAK,IAAI,KAAK,IAAI;AACnC,YAAI,OAAO,QAAQ,EAAG,uBAAsB,QAAQ;AAAA,MACtD;AACA;AAAA,IACF;AAAA,IAEA,KAAK,cAAc;AACjB,uBAAiB,IAAI,KAAK,OAAO,MAAM,OAAO,YAAY,GAAG;AAC7D;AAAA,IACF;AAAA,IAEA,KAAK,gBAAgB;AACnB,yBAAmB,IAAI,KAAK,OAAO,MAAM,OAAO,UAAU;AAC1D;AAAA,IACF;AAAA,IAEA,KAAK,cAAc;AACjB,uBAAiB,IAAI,KAAK,OAAO,MAAM,OAAO,UAAU;AACxD;AAAA,IACF;AAAA,IAEA,KAAK,gBAAgB;AACnB,yBAAmB,IAAI,KAAK,OAAO,MAAM,OAAO,UAAU;AAC1D;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,aAAa,KAAa,MAAc,SAAiB,QAA+B;AAC/F,QAAM,WAAWA,MAAK,KAAK,IAAI;AAC/B,QAAM,UAAU,OAAO,QAAQ;AAC/B,YAAU,UAAU,OAAO;AAC3B,GAAC,UAAU,OAAO,UAAU,OAAO,SAAS,KAAK,IAAI;AACvD;AAMA,SAAS,mBAAmB,KAAqB,KAA6B;AAC5E,MAAI,IAAI,UAAU;AAChB,UAAM,eAAe,gBAAgB;AACrC,WAAO,SAASA,MAAK,cAAc,IAAI,QAAQ,CAAC;AAAA,EAClD;AAEA,MAAI,IAAI,SAAS;AACf,WAAO,OAAO,IAAI,YAAY,aAAa,IAAI,QAAQ,IAAI,IAAI;AAAA,EACjE;AAEA,MAAI,IAAI,WAAW;AACjB,WAAO,IAAI,UAAU,GAAG;AAAA,EAC1B;AAEA,QAAM,IAAI,MAAM,0DAA0D;AAC5E;AAEA,SAAS,gBAAgB,eAAuB,YAA6B;AAC3E,MAAI,CAAC,OAAO,aAAa,EAAG,QAAO;AACnC,QAAM,iBAAiB,aAAa,aAAa;AACjD,SAAO,gBAAgB,KAAK,MAAM,WAAW,KAAK;AACpD;AAGA,IAAM,oBAAoB,oBAAI,IAAI,CAAC,SAAS,aAAa,CAAC;AAEnD,SAAS,yBACd,QACA,aACA,kBACAD,aAAY,MACF;AACV,MAAI,SAAS,CAAC,GAAG,OAAO,SAAS,IAAI;AAGrC,MAAI,CAACA,YAAW;AACd,aAAS,OAAO,OAAO,SAAO,CAAC,kBAAkB,IAAI,GAAG,CAAC;AAAA,EAC3D;AAEA,aAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,OAAO,SAAS,WAAW,GAAG;AACrE,QAAI,YAAY,GAAwB,GAAG;AACzC,aAAO,KAAK,GAAG,IAAI;AAAA,IACrB;AAAA,EACF;AAEA,SAAO,OAAO,OAAO,SAAO,EAAE,OAAO,iBAAiB;AACxD;AAEA,SAAS,wBACP,QACA,aACA,kBACU;AACV,QAAM,mBAAmB,CAAC,GAAG,OAAO,SAAS,IAAI;AAEjD,aAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,OAAO,SAAS,WAAW,GAAG;AACrE,QAAI,YAAY,GAAwB,GAAG;AACzC,uBAAiB,KAAK,GAAG,IAAI;AAAA,IAC/B;AAAA,EACF;AAGA,SAAO,iBAAiB,OAAO,SAAO,OAAO,gBAAgB;AAC/D;AAEA,SAAS,iBACP,KACA,MACA,KACA,KACM;AACN,QAAM,WAAWC,MAAK,KAAK,IAAI;AAC/B,QAAM,WAAW,SAAkC,QAAQ,KAAK,CAAC;AACjE,QAAM,SAAS,IAAI,MAAM,UAAU,GAAG;AACtC,YAAU,UAAU,MAAM;AAC5B;AAEA,SAAS,mBAAmB,KAAa,MAAc,KAAgC;AACrF,QAAM,WAAWA,MAAK,KAAK,IAAI;AAC/B,MAAI,CAAC,OAAO,QAAQ,EAAG;AAEvB,QAAM,WAAW,SAAkC,QAAQ;AAC3D,MAAI,CAAC,SAAU;AAEf,QAAM,WAAW,IAAI,QAAQ,QAAQ;AAGrC,MAAI,IAAI,mBAAmB;AACzB,UAAM,gBAAgB,OAAO,KAAK,QAAQ,EAAE;AAAA,MAC1C,OAAK,SAAS,CAAC,MAAM,UAAa,SAAS,CAAC,MAAM;AAAA,IACpD;AACA,QAAI,cAAc,WAAW,GAAG;AAC9B,aAAO,QAAQ;AACf;AAAA,IACF;AAAA,EACF;AAEA,YAAU,UAAU,QAAQ;AAC9B;AAEA,SAAS,iBAAiB,KAAa,MAAc,KAAgC;AACnF,QAAM,WAAWA,MAAK,KAAK,IAAI;AAC/B,MAAI,UAAU,aAAa,QAAQ,KAAK;AAGxC,MAAI,QAAQ,SAAS,IAAI,MAAM,EAAG;AAGlC,YAAU,IAAI,cAAc,YAAY,IAAI,UAAU,UAAU,UAAU,IAAI;AAE9E,YAAU,UAAU,OAAO;AAC7B;AAEA,SAAS,mBAAmB,KAAa,MAAc,KAAgC;AACrF,QAAM,WAAWA,MAAK,KAAK,IAAI;AAC/B,QAAM,UAAU,aAAa,QAAQ;AACrC,MAAI,CAAC,QAAS;AAId,MAAI,YAAY,QAAQ,QAAQ,IAAI,SAAS,EAAE;AAG/C,MAAI,cAAc,WAAW,QAAQ,SAAS,IAAI,MAAM,GAAG;AAEzD,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,UAAM,WAAW,MAAM,OAAO,UAAQ,CAAC,KAAK,SAAS,IAAI,MAAM,CAAC;AAChE,gBAAY,SAAS,KAAK,IAAI,EAAE,QAAQ,QAAQ,EAAE;AAAA,EACpD;AAEA,YAAU,UAAU,SAAS;AAC/B;","names":["join","join","join","isGitRepo","join"]}
1
+ {"version":3,"sources":["../src/utils/output.ts","../src/utils/git.ts","../src/utils/context.ts","../src/reconcile.ts"],"sourcesContent":["/**\n * Console output utilities for consistent CLI messaging\n */\n\n/**\n * Print info message\n */\nexport function info(message: string): void {\n console.log(message);\n}\n\n/**\n * Print success message\n */\nexport function success(message: string): void {\n console.log(`✓ ${message}`);\n}\n\n/**\n * Print warning message\n */\nexport function warn(message: string): void {\n console.warn(`⚠ ${message}`);\n}\n\n/**\n * Print error message to stderr\n */\nexport function error(message: string): void {\n console.error(`✗ ${message}`);\n}\n\n/**\n * Print a blank line\n */\nexport function blank(): void {\n console.log('');\n}\n\n/**\n * Print a section header\n */\nexport function header(title: string): void {\n console.log(`\\n${title}`);\n console.log('─'.repeat(title.length));\n}\n\n/**\n * Print a list item\n */\nexport function listItem(item: string, indent = 2): void {\n console.log(`${' '.repeat(indent)}• ${item}`);\n}\n\n/**\n * Print key-value pair\n */\nexport function keyValue(key: string, value: string): void {\n console.log(` ${key}: ${value}`);\n}\n","/**\n * Git utilities for CLI operations\n */\n\nimport { join } from 'node:path';\nimport { exists } from './fs.js';\n\n/**\n * Check if directory is a git repository\n */\nexport function isGitRepo(cwd: string): boolean {\n return exists(join(cwd, '.git'));\n}\n","/**\n * Project Context Utilities\n *\n * Shared helpers for creating ProjectContext objects used by reconcile().\n */\n\nimport { join } from 'node:path';\nimport { readJson } from './fs.js';\nimport { isGitRepo } from './git.js';\nimport { detectProjectType, type PackageJson } from './project-detector.js';\nimport type { ProjectContext } from '../schema.js';\n\n/**\n * Create a ProjectContext from the current working directory.\n *\n * Reads package.json and detects project type for use with reconcile().\n */\nexport function createProjectContext(cwd: string): ProjectContext {\n const packageJson = readJson<PackageJson>(join(cwd, 'package.json'));\n\n return {\n cwd,\n projectType: detectProjectType(packageJson ?? {}, cwd),\n devDeps: packageJson?.devDependencies ?? {},\n isGitRepo: isGitRepo(cwd),\n };\n}\n","/**\n * Reconciliation Engine\n *\n * Computes and executes plans based on SAFEWORD_SCHEMA and project state.\n * This is the single source of truth for all file/dir/config operations.\n */\n\nimport { join } from 'node:path';\nimport {\n exists,\n ensureDir,\n writeFile,\n readFile,\n readFileSafe,\n readJson,\n writeJson,\n remove,\n removeIfEmpty,\n makeScriptsExecutable,\n getTemplatesDir,\n} from './utils/fs.js';\nimport type {\n SafewordSchema,\n ProjectContext,\n FileDefinition,\n JsonMergeDefinition,\n TextPatchDefinition,\n} from './schema.js';\nimport type { ProjectType } from './utils/project-detector.js';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst HUSKY_DIR = '.husky';\n\n/** Check if path should be skipped in non-git repos (husky files) */\nfunction shouldSkipForNonGit(path: string, isGitRepo: boolean): boolean {\n return path.startsWith(HUSKY_DIR) && !isGitRepo;\n}\n\n/** Plan mkdir actions for directories that don't exist */\nfunction planMissingDirs(\n dirs: string[],\n cwd: string,\n isGitRepo: boolean,\n): { actions: Action[]; created: string[] } {\n const actions: Action[] = [];\n const created: string[] = [];\n for (const dir of dirs) {\n if (shouldSkipForNonGit(dir, isGitRepo)) continue;\n if (!exists(join(cwd, dir))) {\n actions.push({ type: 'mkdir', path: dir });\n created.push(dir);\n }\n }\n return { actions, created };\n}\n\n/** Plan text-patch actions for files missing the marker */\nfunction planTextPatches(patches: Record<string, TextPatchDefinition>, cwd: string): Action[] {\n const actions: Action[] = [];\n for (const [filePath, def] of Object.entries(patches)) {\n const content = readFileSafe(join(cwd, filePath)) ?? '';\n if (!content.includes(def.marker)) {\n actions.push({ type: 'text-patch', path: filePath, definition: def });\n }\n }\n return actions;\n}\n\n/** Plan rmdir actions for directories that exist */\nfunction planExistingDirsRemoval(\n dirs: string[],\n cwd: string,\n): { actions: Action[]; removed: string[] } {\n const actions: Action[] = [];\n const removed: string[] = [];\n for (const dir of dirs) {\n if (exists(join(cwd, dir))) {\n actions.push({ type: 'rmdir', path: dir });\n removed.push(dir);\n }\n }\n return { actions, removed };\n}\n\n/** Plan rm actions for files that exist */\nfunction planExistingFilesRemoval(\n files: string[],\n cwd: string,\n): { actions: Action[]; removed: string[] } {\n const actions: Action[] = [];\n const removed: string[] = [];\n for (const filePath of files) {\n if (exists(join(cwd, filePath))) {\n actions.push({ type: 'rm', path: filePath });\n removed.push(filePath);\n }\n }\n return { actions, removed };\n}\n\n/** Check if a .claude path needs parent dir cleanup */\nfunction getClaudeParentDirForCleanup(filePath: string): string | null {\n if (!filePath.startsWith('.claude/')) return null;\n const parentDir = filePath.slice(0, Math.max(0, filePath.lastIndexOf('/')));\n if (\n !parentDir ||\n parentDir === '.claude' ||\n parentDir === '.claude/skills' ||\n parentDir === '.claude/commands'\n ) {\n return null;\n }\n return parentDir;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ReconcileMode = 'install' | 'upgrade' | 'uninstall' | 'uninstall-full';\n\nexport type Action =\n | { type: 'mkdir'; path: string }\n | { type: 'rmdir'; path: string }\n | { type: 'write'; path: string; content: string }\n | { type: 'rm'; path: string }\n | { type: 'chmod'; paths: string[] }\n | { type: 'json-merge'; path: string; definition: JsonMergeDefinition }\n | { type: 'json-unmerge'; path: string; definition: JsonMergeDefinition }\n | { type: 'text-patch'; path: string; definition: TextPatchDefinition }\n | { type: 'text-unpatch'; path: string; definition: TextPatchDefinition };\n\nexport interface ReconcileResult {\n actions: Action[];\n applied: boolean;\n created: string[];\n updated: string[];\n removed: string[];\n packagesToInstall: string[];\n packagesToRemove: string[];\n}\n\nexport interface ReconcileOptions {\n dryRun?: boolean;\n}\n\n// ============================================================================\n// Main reconcile function\n// ============================================================================\n\nexport async function reconcile(\n schema: SafewordSchema,\n mode: ReconcileMode,\n ctx: ProjectContext,\n options?: ReconcileOptions,\n): Promise<ReconcileResult> {\n const dryRun = options?.dryRun ?? false;\n\n const plan = computePlan(schema, mode, ctx);\n\n if (dryRun) {\n return {\n actions: plan.actions,\n applied: false,\n created: plan.wouldCreate,\n updated: plan.wouldUpdate,\n removed: plan.wouldRemove,\n packagesToInstall: plan.packagesToInstall,\n packagesToRemove: plan.packagesToRemove,\n };\n }\n\n const result = executePlan(plan, ctx);\n\n return {\n actions: plan.actions,\n applied: true,\n created: result.created,\n updated: result.updated,\n removed: result.removed,\n packagesToInstall: plan.packagesToInstall,\n packagesToRemove: plan.packagesToRemove,\n };\n}\n\n// ============================================================================\n// Plan computation\n// ============================================================================\n\ninterface ReconcilePlan {\n actions: Action[];\n wouldCreate: string[];\n wouldUpdate: string[];\n wouldRemove: string[];\n packagesToInstall: string[];\n packagesToRemove: string[];\n}\n\nfunction planDeprecatedFilesRemoval(\n deprecatedFiles: string[],\n cwd: string,\n): { actions: Action[]; removed: string[] } {\n const actions: Action[] = [];\n const removed: string[] = [];\n for (const filePath of deprecatedFiles) {\n if (exists(join(cwd, filePath))) {\n actions.push({ type: 'rm', path: filePath });\n removed.push(filePath);\n }\n }\n return { actions, removed };\n}\n\nfunction computePlan(\n schema: SafewordSchema,\n mode: ReconcileMode,\n ctx: ProjectContext,\n): ReconcilePlan {\n switch (mode) {\n case 'install': {\n return computeInstallPlan(schema, ctx);\n }\n case 'upgrade': {\n return computeUpgradePlan(schema, ctx);\n }\n case 'uninstall': {\n return computeUninstallPlan(schema, ctx, false);\n }\n case 'uninstall-full': {\n return computeUninstallPlan(schema, ctx, true);\n }\n }\n}\n\nfunction computeInstallPlan(schema: SafewordSchema, ctx: ProjectContext): ReconcilePlan {\n const actions: Action[] = [];\n const wouldCreate: string[] = [];\n\n // 1. Create all directories (skip .husky if not a git repo)\n const allDirs = [...schema.ownedDirs, ...schema.sharedDirs, ...schema.preservedDirs];\n const missingDirs = planMissingDirs(allDirs, ctx.cwd, ctx.isGitRepo);\n actions.push(...missingDirs.actions);\n wouldCreate.push(...missingDirs.created);\n\n // 2. Write all owned files (skip .husky files if not a git repo)\n for (const [filePath, def] of Object.entries(schema.ownedFiles)) {\n if (shouldSkipForNonGit(filePath, ctx.isGitRepo)) continue;\n\n const content = resolveFileContent(def, ctx);\n actions.push({ type: 'write', path: filePath, content });\n wouldCreate.push(filePath);\n }\n\n // 3. Write managed files (only if missing)\n for (const [filePath, def] of Object.entries(schema.managedFiles)) {\n const fullPath = join(ctx.cwd, filePath);\n if (!exists(fullPath)) {\n const content = resolveFileContent(def, ctx);\n actions.push({ type: 'write', path: filePath, content });\n wouldCreate.push(filePath);\n }\n }\n\n // 4. chmod hook/lib directories (only .husky if git repo)\n const chmodPaths = ['.safeword/hooks', '.safeword/hooks/cursor', '.safeword/lib'];\n if (ctx.isGitRepo) chmodPaths.push(HUSKY_DIR);\n actions.push({ type: 'chmod', paths: chmodPaths });\n\n // 5. JSON merges\n for (const [filePath, def] of Object.entries(schema.jsonMerges)) {\n actions.push({ type: 'json-merge', path: filePath, definition: def });\n }\n\n // 6. Text patches\n for (const [filePath, def] of Object.entries(schema.textPatches)) {\n actions.push({ type: 'text-patch', path: filePath, definition: def });\n if (def.createIfMissing && !exists(join(ctx.cwd, filePath))) {\n wouldCreate.push(filePath);\n }\n }\n\n // 7. Compute packages to install (husky/lint-staged skipped if no git repo)\n const packagesToInstall = computePackagesToInstall(\n schema,\n ctx.projectType,\n ctx.devDeps,\n ctx.isGitRepo,\n );\n\n return {\n actions,\n wouldCreate,\n wouldUpdate: [],\n wouldRemove: [],\n packagesToInstall,\n packagesToRemove: [],\n };\n}\n\nfunction computeUpgradePlan(schema: SafewordSchema, ctx: ProjectContext): ReconcilePlan {\n const actions: Action[] = [];\n const wouldCreate: string[] = [];\n const wouldUpdate: string[] = [];\n\n // 1. Ensure directories exist (skip .husky if not a git repo)\n const allDirs = [...schema.ownedDirs, ...schema.sharedDirs, ...schema.preservedDirs];\n const missingDirs = planMissingDirs(allDirs, ctx.cwd, ctx.isGitRepo);\n actions.push(...missingDirs.actions);\n wouldCreate.push(...missingDirs.created);\n\n // 2. Update owned files if content changed (skip .husky files if not a git repo)\n for (const [filePath, def] of Object.entries(schema.ownedFiles)) {\n if (shouldSkipForNonGit(filePath, ctx.isGitRepo)) continue;\n\n const fullPath = join(ctx.cwd, filePath);\n const newContent = resolveFileContent(def, ctx);\n\n if (!fileNeedsUpdate(fullPath, newContent)) continue;\n\n actions.push({ type: 'write', path: filePath, content: newContent });\n if (exists(fullPath)) {\n wouldUpdate.push(filePath);\n } else {\n wouldCreate.push(filePath);\n }\n }\n\n // 3. Update managed files only if content matches current template\n for (const [filePath, def] of Object.entries(schema.managedFiles)) {\n const fullPath = join(ctx.cwd, filePath);\n const newContent = resolveFileContent(def, ctx);\n\n if (!exists(fullPath)) {\n // Missing - create it\n actions.push({ type: 'write', path: filePath, content: newContent });\n wouldCreate.push(filePath);\n }\n // If file exists, don't update during upgrade - user may have customized it\n }\n\n // 4. Remove deprecated files (renamed or removed in newer versions)\n const deprecatedFiles = planDeprecatedFilesRemoval(schema.deprecatedFiles, ctx.cwd);\n actions.push(...deprecatedFiles.actions);\n const wouldRemove = deprecatedFiles.removed;\n\n // 5. chmod (only .husky if git repo)\n const chmodPathsUpgrade = ['.safeword/hooks', '.safeword/hooks/cursor', '.safeword/lib'];\n if (ctx.isGitRepo) chmodPathsUpgrade.push(HUSKY_DIR);\n actions.push({ type: 'chmod', paths: chmodPathsUpgrade });\n\n // 6. JSON merges (always apply to ensure keys are present)\n for (const [filePath, def] of Object.entries(schema.jsonMerges)) {\n actions.push({ type: 'json-merge', path: filePath, definition: def });\n }\n\n // 7. Text patches (only if marker missing)\n actions.push(...planTextPatches(schema.textPatches, ctx.cwd));\n\n // 8. Compute packages to install (husky/lint-staged skipped if no git repo)\n const packagesToInstall = computePackagesToInstall(\n schema,\n ctx.projectType,\n ctx.devDeps,\n ctx.isGitRepo,\n );\n\n return {\n actions,\n wouldCreate,\n wouldUpdate,\n wouldRemove,\n packagesToInstall,\n packagesToRemove: [],\n };\n}\n\nfunction computeUninstallPlan(\n schema: SafewordSchema,\n ctx: ProjectContext,\n full: boolean,\n): ReconcilePlan {\n const actions: Action[] = [];\n const wouldRemove: string[] = [];\n\n // 1. Remove all owned files and track parent dirs for cleanup\n const ownedFiles = planExistingFilesRemoval(Object.keys(schema.ownedFiles), ctx.cwd);\n actions.push(...ownedFiles.actions);\n wouldRemove.push(...ownedFiles.removed);\n\n // Collect parent dirs that need cleanup (for .claude/* skill dirs)\n const dirsToCleanup = new Set<string>();\n for (const filePath of ownedFiles.removed) {\n const parentDir = getClaudeParentDirForCleanup(filePath);\n if (parentDir) dirsToCleanup.add(parentDir);\n }\n const cleanupDirs = planExistingDirsRemoval([...dirsToCleanup], ctx.cwd);\n actions.push(...cleanupDirs.actions);\n wouldRemove.push(...cleanupDirs.removed);\n\n // 2. JSON unmerges\n for (const [filePath, def] of Object.entries(schema.jsonMerges)) {\n actions.push({ type: 'json-unmerge', path: filePath, definition: def });\n }\n\n // 3. Text unpatches\n for (const [filePath, def] of Object.entries(schema.textPatches)) {\n const fullPath = join(ctx.cwd, filePath);\n if (exists(fullPath)) {\n const content = readFileSafe(fullPath) ?? '';\n if (content.includes(def.marker)) {\n actions.push({ type: 'text-unpatch', path: filePath, definition: def });\n }\n }\n }\n\n // 4. Remove preserved directories first (reverse order, only if empty)\n const preserved = planExistingDirsRemoval(schema.preservedDirs.toReversed(), ctx.cwd);\n actions.push(...preserved.actions);\n wouldRemove.push(...preserved.removed);\n\n // 5. Remove owned directories (reverse order ensures children before parents)\n const owned = planExistingDirsRemoval(schema.ownedDirs.toReversed(), ctx.cwd);\n actions.push(...owned.actions);\n wouldRemove.push(...owned.removed);\n\n // 6. Full uninstall: remove managed files\n if (full) {\n const managed = planExistingFilesRemoval(Object.keys(schema.managedFiles), ctx.cwd);\n actions.push(...managed.actions);\n wouldRemove.push(...managed.removed);\n }\n\n // 7. Compute packages to remove (full only)\n const packagesToRemove = full\n ? computePackagesToRemove(schema, ctx.projectType, ctx.devDeps)\n : [];\n\n return {\n actions,\n wouldCreate: [],\n wouldUpdate: [],\n wouldRemove,\n packagesToInstall: [],\n packagesToRemove,\n };\n}\n\n// ============================================================================\n// Plan execution\n// ============================================================================\n\ninterface ExecutionResult {\n created: string[];\n updated: string[];\n removed: string[];\n}\n\nfunction executePlan(plan: ReconcilePlan, ctx: ProjectContext): ExecutionResult {\n const created: string[] = [];\n const updated: string[] = [];\n const removed: string[] = [];\n const result = { created, updated, removed };\n\n for (const action of plan.actions) {\n executeAction(action, ctx, result);\n }\n\n return result;\n}\n\nfunction executeAction(action: Action, ctx: ProjectContext, result: ExecutionResult): void {\n switch (action.type) {\n case 'mkdir': {\n ensureDir(join(ctx.cwd, action.path));\n result.created.push(action.path);\n break;\n }\n\n case 'rmdir': {\n // Use removeIfEmpty to preserve directories with user content\n if (removeIfEmpty(join(ctx.cwd, action.path))) {\n result.removed.push(action.path);\n }\n break;\n }\n\n case 'write': {\n executeWrite(ctx.cwd, action.path, action.content, result);\n break;\n }\n\n case 'rm': {\n remove(join(ctx.cwd, action.path));\n result.removed.push(action.path);\n break;\n }\n\n case 'chmod': {\n for (const path of action.paths) {\n const fullPath = join(ctx.cwd, path);\n if (exists(fullPath)) makeScriptsExecutable(fullPath);\n }\n break;\n }\n\n case 'json-merge': {\n executeJsonMerge(ctx.cwd, action.path, action.definition, ctx);\n break;\n }\n\n case 'json-unmerge': {\n executeJsonUnmerge(ctx.cwd, action.path, action.definition);\n break;\n }\n\n case 'text-patch': {\n executeTextPatch(ctx.cwd, action.path, action.definition);\n break;\n }\n\n case 'text-unpatch': {\n executeTextUnpatch(ctx.cwd, action.path, action.definition);\n break;\n }\n }\n}\n\nfunction executeWrite(cwd: string, path: string, content: string, result: ExecutionResult): void {\n const fullPath = join(cwd, path);\n const existed = exists(fullPath);\n writeFile(fullPath, content);\n (existed ? result.updated : result.created).push(path);\n}\n\n// ============================================================================\n// Helper functions\n// ============================================================================\n\nfunction resolveFileContent(def: FileDefinition, ctx: ProjectContext): string {\n if (def.template) {\n const templatesDir = getTemplatesDir();\n return readFile(join(templatesDir, def.template));\n }\n\n if (def.content) {\n return typeof def.content === 'function' ? def.content() : def.content;\n }\n\n if (def.generator) {\n return def.generator(ctx);\n }\n\n throw new Error('FileDefinition must have template, content, or generator');\n}\n\nfunction fileNeedsUpdate(installedPath: string, newContent: string): boolean {\n if (!exists(installedPath)) return true;\n const currentContent = readFileSafe(installedPath);\n return currentContent?.trim() !== newContent.trim();\n}\n\n// Packages that require git repo\nconst GIT_ONLY_PACKAGES = new Set(['husky', 'lint-staged']);\n\nexport function computePackagesToInstall(\n schema: SafewordSchema,\n projectType: ProjectType,\n installedDevDeps: Record<string, string>,\n isGitRepo = true,\n): string[] {\n let needed = [...schema.packages.base];\n\n // Filter out git-only packages when not in a git repo\n if (!isGitRepo) {\n needed = needed.filter(pkg => !GIT_ONLY_PACKAGES.has(pkg));\n }\n\n for (const [key, deps] of Object.entries(schema.packages.conditional)) {\n if (projectType[key as keyof ProjectType]) {\n needed.push(...deps);\n }\n }\n\n return needed.filter(pkg => !(pkg in installedDevDeps));\n}\n\nfunction computePackagesToRemove(\n schema: SafewordSchema,\n projectType: ProjectType,\n installedDevDeps: Record<string, string>,\n): string[] {\n const safewordPackages = [...schema.packages.base];\n\n for (const [key, deps] of Object.entries(schema.packages.conditional)) {\n if (projectType[key as keyof ProjectType]) {\n safewordPackages.push(...deps);\n }\n }\n\n // Only remove packages that are actually installed\n return safewordPackages.filter(pkg => pkg in installedDevDeps);\n}\n\nfunction executeJsonMerge(\n cwd: string,\n path: string,\n def: JsonMergeDefinition,\n ctx: ProjectContext,\n): void {\n const fullPath = join(cwd, path);\n const existing = readJson<Record<string, unknown>>(fullPath) ?? {};\n const merged = def.merge(existing, ctx);\n\n // Skip write if content is unchanged (avoids formatting churn)\n if (JSON.stringify(existing) === JSON.stringify(merged)) return;\n\n writeJson(fullPath, merged);\n}\n\nfunction executeJsonUnmerge(cwd: string, path: string, def: JsonMergeDefinition): void {\n const fullPath = join(cwd, path);\n if (!exists(fullPath)) return;\n\n const existing = readJson<Record<string, unknown>>(fullPath);\n if (!existing) return;\n\n const unmerged = def.unmerge(existing);\n\n // Check if file should be removed\n if (def.removeFileIfEmpty) {\n const remainingKeys = Object.keys(unmerged).filter(\n k => unmerged[k] !== undefined && unmerged[k] !== null,\n );\n if (remainingKeys.length === 0) {\n remove(fullPath);\n return;\n }\n }\n\n writeJson(fullPath, unmerged);\n}\n\nfunction executeTextPatch(cwd: string, path: string, def: TextPatchDefinition): void {\n const fullPath = join(cwd, path);\n let content = readFileSafe(fullPath) ?? '';\n\n // Check if already patched\n if (content.includes(def.marker)) return;\n\n // Apply patch\n content = def.operation === 'prepend' ? def.content + content : content + def.content;\n\n writeFile(fullPath, content);\n}\n\nfunction executeTextUnpatch(cwd: string, path: string, def: TextPatchDefinition): void {\n const fullPath = join(cwd, path);\n const content = readFileSafe(fullPath);\n if (!content) return;\n\n // Remove the patched content\n // First try to remove the full content block\n let unpatched = content.replace(def.content, '');\n\n // If full content wasn't found but marker exists, remove lines containing the marker\n if (unpatched === content && content.includes(def.marker)) {\n // Remove lines containing the marker\n const lines = content.split('\\n');\n const filtered = lines.filter(line => !line.includes(def.marker));\n unpatched = filtered.join('\\n').replace(/^\\n+/, ''); // Remove leading empty lines\n }\n\n writeFile(fullPath, unpatched);\n}\n"],"mappings":"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","names":["join","join","join","isGitRepo","join"]}
package/dist/cli.js CHANGED
@@ -8,23 +8,23 @@ import { Command } from "commander";
8
8
  var program = new Command();
9
9
  program.name("safeword").description("CLI for setting up and managing safeword development environments").version(VERSION);
10
10
  program.command("setup").description("Set up safeword in the current project").option("-y, --yes", "Accept all defaults (non-interactive mode)").action(async (options) => {
11
- const { setup } = await import("./setup-DW7OG2QD.js");
11
+ const { setup } = await import("./setup-URK77YMR.js");
12
12
  await setup(options);
13
13
  });
14
14
  program.command("check").description("Check project health and versions").option("--offline", "Skip remote version check").action(async (options) => {
15
- const { check } = await import("./check-Y3AQWR4Y.js");
15
+ const { check } = await import("./check-OUVIK2O6.js");
16
16
  await check(options);
17
17
  });
18
18
  program.command("upgrade").description("Upgrade safeword configuration to latest version").action(async () => {
19
- const { upgrade } = await import("./upgrade-FDZLLLT2.js");
19
+ const { upgrade } = await import("./upgrade-IDR2ZALG.js");
20
20
  await upgrade();
21
21
  });
22
22
  program.command("diff").description("Preview changes that would be made by upgrade").option("-v, --verbose", "Show full diff output").action(async (options) => {
23
- const { diff } = await import("./diff-OITRHARU.js");
23
+ const { diff } = await import("./diff-ASRWAPYJ.js");
24
24
  await diff(options);
25
25
  });
26
26
  program.command("reset").description("Remove safeword configuration from project").option("-y, --yes", "Skip confirmation prompt").option("--full", "Also remove linting config and uninstall npm packages").action(async (options) => {
27
- const { reset } = await import("./reset-D6HUKH4Z.js");
27
+ const { reset } = await import("./reset-4G5DEEMY.js");
28
28
  await reset(options);
29
29
  });
30
30
  program.command("sync").description("Sync linting plugins with project dependencies").option("-q, --quiet", "Suppress output except errors").option("-s, --stage", "Stage modified files (for pre-commit hooks)").action(async (options) => {
@@ -6,7 +6,7 @@ import {
6
6
  listItem,
7
7
  reconcile,
8
8
  success
9
- } from "./chunk-VLNT6YSH.js";
9
+ } from "./chunk-TP334635.js";
10
10
  import {
11
11
  SAFEWORD_SCHEMA,
12
12
  exists,
@@ -163,4 +163,4 @@ Packages to install: ${result.packagesToInstall.length}`);
163
163
  export {
164
164
  diff
165
165
  };
166
- //# sourceMappingURL=diff-OITRHARU.js.map
166
+ //# sourceMappingURL=diff-ASRWAPYJ.js.map
@@ -7,7 +7,7 @@ import {
7
7
  reconcile,
8
8
  success,
9
9
  warn
10
- } from "./chunk-VLNT6YSH.js";
10
+ } from "./chunk-TP334635.js";
11
11
  import {
12
12
  SAFEWORD_SCHEMA,
13
13
  exists
@@ -71,4 +71,4 @@ async function reset(options) {
71
71
  export {
72
72
  reset
73
73
  };
74
- //# sourceMappingURL=reset-D6HUKH4Z.js.map
74
+ //# sourceMappingURL=reset-4G5DEEMY.js.map
@@ -8,7 +8,7 @@ import {
8
8
  reconcile,
9
9
  success,
10
10
  warn
11
- } from "./chunk-VLNT6YSH.js";
11
+ } from "./chunk-TP334635.js";
12
12
  import {
13
13
  SAFEWORD_SCHEMA,
14
14
  exists,
@@ -97,4 +97,4 @@ Safeword ${VERSION} installed successfully!`);
97
97
  export {
98
98
  setup
99
99
  };
100
- //# sourceMappingURL=setup-DW7OG2QD.js.map
100
+ //# sourceMappingURL=setup-URK77YMR.js.map
@@ -12,7 +12,7 @@ import {
12
12
  listItem,
13
13
  reconcile,
14
14
  success
15
- } from "./chunk-VLNT6YSH.js";
15
+ } from "./chunk-TP334635.js";
16
16
  import {
17
17
  SAFEWORD_SCHEMA,
18
18
  exists,
@@ -73,4 +73,4 @@ Safeword upgraded to v${VERSION}`);
73
73
  export {
74
74
  upgrade
75
75
  };
76
- //# sourceMappingURL=upgrade-FDZLLLT2.js.map
76
+ //# sourceMappingURL=upgrade-IDR2ZALG.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "safeword",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "description": "CLI for setting up and managing safeword development environments",
5
5
  "type": "module",
6
6
  "bin": {