safeword 0.15.4 → 0.15.6
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/{check-RGWLYICL.js → check-TI6D5FLM.js} +3 -3
- package/dist/{chunk-IOBHSIOZ.js → chunk-4IXCKHSI.js} +2 -2
- package/dist/{chunk-ETUKZM5C.js → chunk-OPR33S5K.js} +12 -2
- package/dist/chunk-OPR33S5K.js.map +1 -0
- package/dist/cli.js +5 -5
- package/dist/{diff-SNLBLQL4.js → diff-3ROXA7D3.js} +2 -2
- package/dist/{reset-S2MIQQBA.js → reset-DZ5QUVBK.js} +2 -2
- package/dist/{setup-TWZ3H3LR.js → setup-LS4T3KNU.js} +3 -3
- package/dist/{upgrade-UJGAYG34.js → upgrade-CWBPH7AS.js} +3 -3
- package/package.json +1 -2
- package/templates/SAFEWORD.md +59 -8
- package/templates/cursor/rules/safeword-bdd-orchestrating.mdc +148 -51
- package/templates/doc-templates/ticket-template.md +9 -0
- package/templates/hooks/post-tool-guide-check.ts +59 -0
- package/templates/skills/safeword-bdd-orchestrating/SKILL.md +148 -51
- package/dist/chunk-ETUKZM5C.js.map +0 -1
- /package/dist/{check-RGWLYICL.js.map → check-TI6D5FLM.js.map} +0 -0
- /package/dist/{chunk-IOBHSIOZ.js.map → chunk-4IXCKHSI.js.map} +0 -0
- /package/dist/{diff-SNLBLQL4.js.map → diff-3ROXA7D3.js.map} +0 -0
- /package/dist/{reset-S2MIQQBA.js.map → reset-DZ5QUVBK.js.map} +0 -0
- /package/dist/{setup-TWZ3H3LR.js.map → setup-LS4T3KNU.js.map} +0 -0
- /package/dist/{upgrade-UJGAYG34.js.map → upgrade-CWBPH7AS.js.map} +0 -0
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
} from "./chunk-FJYRWU2V.js";
|
|
4
4
|
import {
|
|
5
5
|
getMissingPacks
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-4IXCKHSI.js";
|
|
7
7
|
import {
|
|
8
8
|
SAFEWORD_SCHEMA,
|
|
9
9
|
createProjectContext,
|
|
10
10
|
reconcile
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-OPR33S5K.js";
|
|
12
12
|
import {
|
|
13
13
|
VERSION
|
|
14
14
|
} from "./chunk-ORQHKDT2.js";
|
|
@@ -186,4 +186,4 @@ async function check(options) {
|
|
|
186
186
|
export {
|
|
187
187
|
check
|
|
188
188
|
};
|
|
189
|
-
//# sourceMappingURL=check-
|
|
189
|
+
//# sourceMappingURL=check-TI6D5FLM.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
setupPythonTooling
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OPR33S5K.js";
|
|
4
4
|
import {
|
|
5
5
|
VERSION
|
|
6
6
|
} from "./chunk-ORQHKDT2.js";
|
|
@@ -121,4 +121,4 @@ export {
|
|
|
121
121
|
detectLanguages,
|
|
122
122
|
getMissingPacks
|
|
123
123
|
};
|
|
124
|
-
//# sourceMappingURL=chunk-
|
|
124
|
+
//# sourceMappingURL=chunk-4IXCKHSI.js.map
|
|
@@ -1155,6 +1155,15 @@ var SETTINGS_HOOKS = {
|
|
|
1155
1155
|
command: 'bun "$CLAUDE_PROJECT_DIR"/.safeword/hooks/post-tool-lint.ts'
|
|
1156
1156
|
}
|
|
1157
1157
|
]
|
|
1158
|
+
},
|
|
1159
|
+
{
|
|
1160
|
+
matcher: "Write|Edit|MultiEdit|NotebookEdit",
|
|
1161
|
+
hooks: [
|
|
1162
|
+
{
|
|
1163
|
+
type: "command",
|
|
1164
|
+
command: 'bun "$CLAUDE_PROJECT_DIR"/.safeword/hooks/post-tool-guide-check.ts'
|
|
1165
|
+
}
|
|
1166
|
+
]
|
|
1158
1167
|
}
|
|
1159
1168
|
]
|
|
1160
1169
|
};
|
|
@@ -1559,13 +1568,14 @@ var SAFEWORD_SCHEMA = {
|
|
|
1559
1568
|
// Hooks shared library (2 files) - TypeScript with Bun runtime
|
|
1560
1569
|
".safeword/hooks/lib/lint.ts": { template: "hooks/lib/lint.ts" },
|
|
1561
1570
|
".safeword/hooks/lib/quality.ts": { template: "hooks/lib/quality.ts" },
|
|
1562
|
-
// Hooks (
|
|
1571
|
+
// Hooks (8 files) - TypeScript with Bun runtime
|
|
1563
1572
|
".safeword/hooks/session-verify-agents.ts": { template: "hooks/session-verify-agents.ts" },
|
|
1564
1573
|
".safeword/hooks/session-version.ts": { template: "hooks/session-version.ts" },
|
|
1565
1574
|
".safeword/hooks/session-lint-check.ts": { template: "hooks/session-lint-check.ts" },
|
|
1566
1575
|
".safeword/hooks/prompt-timestamp.ts": { template: "hooks/prompt-timestamp.ts" },
|
|
1567
1576
|
".safeword/hooks/prompt-questions.ts": { template: "hooks/prompt-questions.ts" },
|
|
1568
1577
|
".safeword/hooks/post-tool-lint.ts": { template: "hooks/post-tool-lint.ts" },
|
|
1578
|
+
".safeword/hooks/post-tool-guide-check.ts": { template: "hooks/post-tool-guide-check.ts" },
|
|
1569
1579
|
".safeword/hooks/stop-quality.ts": { template: "hooks/stop-quality.ts" },
|
|
1570
1580
|
// Guides (11 files)
|
|
1571
1581
|
".safeword/guides/architecture-guide.md": { template: "guides/architecture-guide.md" },
|
|
@@ -1960,4 +1970,4 @@ export {
|
|
|
1960
1970
|
detectLanguages,
|
|
1961
1971
|
createProjectContext
|
|
1962
1972
|
};
|
|
1963
|
-
//# sourceMappingURL=chunk-
|
|
1973
|
+
//# sourceMappingURL=chunk-OPR33S5K.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/packs/python/setup.ts","../src/reconcile.ts","../src/utils/install.ts","../src/packs/golang/files.ts","../src/packs/python/files.ts","../src/templates/config.ts","../src/packs/typescript/files.ts","../src/templates/content.ts","../src/utils/hooks.ts","../src/schema.ts","../src/utils/project-detector.ts","../src/utils/context.ts","../src/utils/git.ts"],"sourcesContent":["/**\n * Python-specific Setup Utilities\n *\n * Setup logic for Python projects.\n * Config generators are in files.ts (same pattern as TypeScript and Go).\n *\n * This file contains:\n * - Layer detection for architecture boundaries\n * - Package manager detection for install guidance\n */\n\nimport { execSync } from 'node:child_process';\nimport nodePath from 'node:path';\n\nimport { exists, readFileSafe } from '../../utils/fs.js';\nimport type { SetupResult } from '../types.js';\n\n/**\n * Python layer patterns for architecture detection.\n * Mirrors boundaries.ts ARCHITECTURE_LAYERS pattern.\n *\n * @see .safeword/planning/design/phase2-python-tooling.md → Layer detection heuristic\n */\nconst PYTHON_LAYERS: Record<string, string[]> = {\n domain: ['domain', 'models', 'entities', 'core'],\n services: ['services', 'usecases', 'application'],\n infra: ['infra', 'infrastructure', 'adapters', 'repositories'],\n api: ['api', 'routes', 'handlers', 'views', 'controllers'],\n};\n\n/**\n * Detect Python layers in a project directory.\n * Looks for common Python layer directory patterns.\n *\n * @param cwd - Project root directory\n * @returns Array of detected layer names in dependency order (domain first)\n */\nexport function detectPythonLayers(cwd: string): string[] {\n const detected: string[] = [];\n\n for (const [layer, patterns] of Object.entries(PYTHON_LAYERS)) {\n for (const pattern of patterns) {\n // Check common locations: src/{pattern}, {pattern}\n const srcPath = nodePath.join(cwd, 'src', pattern);\n const rootPath = nodePath.join(cwd, pattern);\n\n if (exists(srcPath) || exists(rootPath)) {\n detected.push(layer);\n break; // Only add layer once\n }\n }\n }\n\n // Return in correct dependency order (domain → services → infra → api)\n const layerOrder = ['domain', 'services', 'infra', 'api'];\n return layerOrder.filter(layer => detected.includes(layer));\n}\n\n/**\n * Detect the root package name from pyproject.toml or directory structure.\n *\n * @param cwd - Project root directory\n * @returns Package name or 'src' as fallback\n */\nexport function detectRootPackage(cwd: string): string {\n const pyprojectPath = nodePath.join(cwd, 'pyproject.toml');\n const content = readFileSafe(pyprojectPath);\n\n if (content) {\n // Try to extract name from [project] section\n // Using simple pattern to avoid regex backtracking\n const nameMatch = /^name\\s*=\\s*\"([^\"]+)\"/m.exec(content);\n if (nameMatch) {\n // Convert kebab-case to snake_case for Python imports\n return nameMatch[1].replaceAll('-', '_');\n }\n }\n\n // Fallback: check for src/ directory\n if (exists(nodePath.join(cwd, 'src'))) {\n return 'src';\n }\n\n // Last resort: use directory name\n return nodePath.basename(cwd).replaceAll('-', '_');\n}\n\ntype PythonPackageManager = 'uv' | 'poetry' | 'pipenv' | 'pip';\n\n/**\n * Check if ruff is already declared as a dependency in pyproject.toml.\n * Only checks dependency sections, not [tool.ruff] config.\n */\nexport function hasRuffDependency(cwd: string): boolean {\n const pyprojectPath = nodePath.join(cwd, 'pyproject.toml');\n const content = readFileSafe(pyprojectPath);\n if (!content) return false;\n\n // Check for ruff in dependency arrays or Poetry table format:\n // - PEP 621: \"ruff\", \"ruff>=0.1\", 'ruff' (quoted in array)\n // - Poetry: ruff = \"^0.8.0\" (unquoted key)\n // Does NOT match: [tool.ruff]\n return /[\"']ruff[^\"']*[\"']/.test(content) || /^ruff\\s*=/m.test(content);\n}\n\n/**\n * Detect the Python package manager used by the project.\n */\nexport function detectPythonPackageManager(cwd: string): PythonPackageManager {\n // Check for uv (uv.lock or .python-version with uv markers)\n if (exists(nodePath.join(cwd, 'uv.lock'))) {\n return 'uv';\n }\n\n // Check for Poetry\n if (exists(nodePath.join(cwd, 'poetry.lock'))) {\n return 'poetry';\n }\n\n // Check for poetry in pyproject.toml\n const pyprojectPath = nodePath.join(cwd, 'pyproject.toml');\n const pyprojectContent = readFileSafe(pyprojectPath);\n if (pyprojectContent?.includes('[tool.poetry]')) {\n return 'poetry';\n }\n\n // Check for Pipenv\n if (exists(nodePath.join(cwd, 'Pipfile'))) {\n return 'pipenv';\n }\n\n // Default to pip\n return 'pip';\n}\n\n/**\n * Get the install command for Python tools based on package manager.\n *\n * @param cwd - Project root directory\n * @param tools - Tools to install (defaults to ['ruff'])\n */\nexport function getPythonInstallCommand(cwd: string, tools: string[] = ['ruff']): string {\n const pm = detectPythonPackageManager(cwd);\n const toolList = tools.join(' ');\n\n switch (pm) {\n case 'uv': {\n return `uv add --dev ${toolList}`;\n }\n case 'poetry': {\n return `poetry add --group dev ${toolList}`;\n }\n case 'pipenv': {\n return `pipenv install --dev ${toolList}`;\n }\n case 'pip':\n default: {\n return `pip install ${toolList}`;\n }\n }\n}\n\n/**\n * Install Python development dependencies using detected package manager.\n * Matches TypeScript parity where we auto-install ESLint/Prettier.\n *\n * @param cwd - Project root directory\n * @param tools - Tools to install (e.g., ['ruff', 'mypy', 'import-linter'])\n * @returns true if installation succeeded, false otherwise\n */\nexport function installPythonDependencies(cwd: string, tools: string[]): boolean {\n if (tools.length === 0) return true;\n\n // pip projects need manual install due to PEP 668\n const pm = detectPythonPackageManager(cwd);\n if (pm === 'pip') return false;\n\n try {\n execSync(getPythonInstallCommand(cwd, tools), { cwd, stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Set up Python tooling configuration.\n *\n * Note: Config files (ruff.toml, mypy.ini, .importlinter) are now created\n * by the schema system (managedFiles) for full reconciliation support.\n * This function exists for future Python-specific setup logic.\n *\n * @returns Empty result (schema handles file creation)\n */\nexport function setupPythonTooling(): SetupResult {\n // Config files are created by schema.ts managedFiles\n // Future: Add any Python-specific setup logic here\n return { files: [] };\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 nodePath from 'node:path';\n\nimport type {\n FileDefinition,\n JsonMergeDefinition,\n ProjectContext,\n SafewordSchema,\n TextPatchDefinition,\n} from './schema.js';\nimport {\n ensureDirectory,\n exists,\n getTemplatesDirectory,\n makeScriptsExecutable,\n readFile,\n readFileSafe,\n readJson,\n remove,\n removeIfEmpty,\n writeFile,\n writeJson,\n} from './utils/fs.js';\nimport type { ProjectType } from './utils/project-detector.js';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst HUSKY_DIR = '.husky';\n\n/**\n * Directories containing executable scripts that need chmod +x.\n * Used by both install and upgrade plans.\n */\nconst CHMOD_PATHS = [\n '.safeword/hooks',\n '.safeword/hooks/cursor',\n '.safeword/lib',\n '.safeword/scripts',\n];\n\n/**\n * Prettier-related packages that should be skipped for projects with existing formatter.\n */\nconst PRETTIER_PACKAGES = new Set([\n 'prettier',\n 'prettier-plugin-astro',\n 'prettier-plugin-tailwindcss',\n 'prettier-plugin-sh',\n]);\n\n/**\n * Get conditional packages based on project type.\n * Handles the \"standard\" key and prettier filtering for existing formatters.\n */\nfunction getConditionalPackages(\n conditionalPackages: Record<string, string[]>,\n projectType: ProjectType,\n): string[] {\n const packages: string[] = [];\n\n for (const [key, deps] of Object.entries(conditionalPackages)) {\n // \"standard\" means !existingFormatter - only for projects without existing formatter\n if (key === 'standard') {\n if (!projectType.existingFormatter) {\n packages.push(...deps);\n }\n continue;\n }\n\n // Check if this condition is met\n if (projectType[key as keyof ProjectType]) {\n // For projects with existing formatter, skip prettier-related packages\n if (projectType.existingFormatter) {\n packages.push(...deps.filter(pkg => !PRETTIER_PACKAGES.has(pkg)));\n } else {\n packages.push(...deps);\n }\n }\n }\n\n return packages;\n}\n\n/**\n * Check if path should be skipped in non-git repos (husky files)\n * @param path\n * @param isGitRepo\n */\nfunction shouldSkipForNonGit(path: string, isGitRepo: boolean): boolean {\n return path.startsWith(HUSKY_DIR) && !isGitRepo;\n}\n\n/**\n * Plan mkdir actions for directories that don't exist\n * @param dirs\n * @param cwd\n * @param isGitRepo\n */\nfunction planMissingDirectories(\n directories: 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 directories) {\n if (shouldSkipForNonGit(dir, isGitRepo)) continue;\n if (!exists(nodePath.join(cwd, dir))) {\n actions.push({ type: 'mkdir', path: dir });\n created.push(dir);\n }\n }\n return { actions, created };\n}\n\n/**\n * Plan text-patch actions for files missing the marker\n * @param patches\n * @param cwd\n * @param isGitRepo\n */\nfunction planTextPatches(\n patches: Record<string, TextPatchDefinition>,\n cwd: string,\n isGitRepo: boolean,\n): Action[] {\n const actions: Action[] = [];\n for (const [filePath, definition] of Object.entries(patches)) {\n if (shouldSkipForNonGit(filePath, isGitRepo)) continue;\n const content = readFileSafe(nodePath.join(cwd, filePath)) ?? '';\n if (!content.includes(definition.marker)) {\n actions.push({ type: 'text-patch', path: filePath, definition });\n }\n }\n return actions;\n}\n\n/**\n * Generic file write planner with configurable skip condition.\n */\nfunction planFileWrites(\n files: Record<string, FileDefinition>,\n ctx: ProjectContext,\n shouldSkip: (filePath: string, ctx: ProjectContext) => boolean,\n): { actions: Action[]; created: string[] } {\n const actions: Action[] = [];\n const created: string[] = [];\n for (const [filePath, definition] of Object.entries(files)) {\n if (shouldSkip(filePath, ctx)) continue;\n const content = resolveFileContent(definition, ctx);\n // Skip files where generator returned undefined (e.g., non-JS projects)\n if (content === undefined) continue;\n actions.push({ type: 'write', path: filePath, content });\n created.push(filePath);\n }\n return { actions, created };\n}\n\n/** Owned files: skip husky files in non-git repos */\nfunction planOwnedFileWrites(\n files: Record<string, FileDefinition>,\n ctx: ProjectContext,\n): { actions: Action[]; created: string[] } {\n return planFileWrites(files, ctx, (filePath, c) => shouldSkipForNonGit(filePath, c.isGitRepo));\n}\n\n/** Managed files: skip if file already exists */\nfunction planManagedFileWrites(\n files: Record<string, FileDefinition>,\n ctx: ProjectContext,\n): { actions: Action[]; created: string[] } {\n return planFileWrites(files, ctx, (filePath, c) => exists(nodePath.join(c.cwd, filePath)));\n}\n\nfunction planTextPatchesWithCreation(\n patches: Record<string, TextPatchDefinition>,\n ctx: ProjectContext,\n): { actions: Action[]; created: string[] } {\n const actions: Action[] = [];\n const created: string[] = [];\n for (const [filePath, definition] of Object.entries(patches)) {\n if (shouldSkipForNonGit(filePath, ctx.isGitRepo)) continue;\n actions.push({ type: 'text-patch', path: filePath, definition });\n if (definition.createIfMissing && !exists(nodePath.join(ctx.cwd, filePath))) {\n created.push(filePath);\n }\n }\n return { actions, created };\n}\n\n/**\n * Plan rmdir actions for directories that exist\n * @param dirs\n * @param cwd\n */\nfunction planExistingDirectoriesRemoval(\n directories: string[],\n cwd: string,\n): { actions: Action[]; removed: string[] } {\n const actions: Action[] = [];\n const removed: string[] = [];\n for (const dir of directories) {\n if (exists(nodePath.join(cwd, dir))) {\n actions.push({ type: 'rmdir', path: dir });\n removed.push(dir);\n }\n }\n return { actions, removed };\n}\n\n/**\n * Plan rm actions for files that exist\n * @param files\n * @param cwd\n */\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(nodePath.join(cwd, filePath))) {\n actions.push({ type: 'rm', path: filePath });\n removed.push(filePath);\n }\n }\n return { actions, removed };\n}\n\n/**\n * Check if a .claude path needs parent dir cleanup\n * @param filePath\n */\nfunction getClaudeParentDirectoryForCleanup(filePath: string): string | undefined {\n if (!filePath.startsWith('.claude/')) return undefined;\n const parentDirectory = filePath.slice(0, Math.max(0, filePath.lastIndexOf('/')));\n if (\n !parentDirectory ||\n parentDirectory === '.claude' ||\n parentDirectory === '.claude/skills' ||\n parentDirectory === '.claude/commands'\n ) {\n return undefined;\n }\n return parentDirectory;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\ntype 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\ninterface ReconcileOptions {\n dryRun?: boolean;\n}\n\n// ============================================================================\n// Main reconcile function\n// ============================================================================\n\n/**\n *\n * @param schema\n * @param mode\n * @param ctx\n * @param options\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\n/**\n *\n * @param schema\n * @param mode\n * @param ctx\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 default: {\n // Exhaustive check - TypeScript ensures all cases are handled\n const _exhaustiveCheck: never = mode;\n return _exhaustiveCheck;\n }\n }\n}\n\n/**\n *\n * @param schema\n * @param ctx\n */\nfunction computeInstallPlan(schema: SafewordSchema, ctx: ProjectContext): ReconcilePlan {\n const actions: Action[] = [];\n const wouldCreate: string[] = [];\n\n // 1. Create all directories\n const allDirectories = [...schema.ownedDirs, ...schema.sharedDirs, ...schema.preservedDirs];\n const directories = planMissingDirectories(allDirectories, ctx.cwd, ctx.isGitRepo);\n actions.push(...directories.actions);\n wouldCreate.push(...directories.created);\n\n // 2. Write owned files\n const owned = planOwnedFileWrites(schema.ownedFiles, ctx);\n actions.push(...owned.actions);\n wouldCreate.push(...owned.created);\n\n // 3. Write managed files (only if missing)\n const managed = planManagedFileWrites(schema.managedFiles, ctx);\n actions.push(...managed.actions);\n wouldCreate.push(...managed.created);\n\n // 4. chmod hook/lib/scripts directories\n const chmodPaths = [...CHMOD_PATHS];\n if (ctx.isGitRepo) chmodPaths.push(HUSKY_DIR);\n actions.push({ type: 'chmod', paths: chmodPaths });\n\n // 5. JSON merges\n for (const [filePath, definition] of Object.entries(schema.jsonMerges)) {\n actions.push({ type: 'json-merge', path: filePath, definition });\n }\n\n // 6. Text patches\n const patches = planTextPatchesWithCreation(schema.textPatches, ctx);\n actions.push(...patches.actions);\n wouldCreate.push(...patches.created);\n\n // 7. Compute packages to install\n const packagesToInstall = computePackagesToInstall(\n schema,\n ctx.projectType,\n ctx.developmentDeps,\n ctx.isGitRepo,\n );\n\n return {\n actions,\n wouldCreate,\n wouldUpdate: [],\n wouldRemove: [],\n packagesToInstall,\n packagesToRemove: [],\n };\n}\n\n/**\n *\n * @param schema\n * @param ctx\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 allDirectories = [...schema.ownedDirs, ...schema.sharedDirs, ...schema.preservedDirs];\n const missingDirectories = planMissingDirectories(allDirectories, ctx.cwd, ctx.isGitRepo);\n actions.push(...missingDirectories.actions);\n wouldCreate.push(...missingDirectories.created);\n\n // 2. Update owned files if content changed (skip .husky files if not a git repo)\n for (const [filePath, definition] of Object.entries(schema.ownedFiles)) {\n if (shouldSkipForNonGit(filePath, ctx.isGitRepo)) continue;\n\n const fullPath = nodePath.join(ctx.cwd, filePath);\n const newContent = resolveFileContent(definition, ctx);\n\n // Skip files where generator returned undefined (e.g., non-JS projects)\n if (newContent === undefined) continue;\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, definition] of Object.entries(schema.managedFiles)) {\n const fullPath = nodePath.join(ctx.cwd, filePath);\n const newContent = resolveFileContent(definition, ctx);\n\n // Skip files where generator returned undefined (e.g., non-JS projects)\n if (newContent === undefined) continue;\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 = planExistingFilesRemoval(schema.deprecatedFiles, ctx.cwd);\n actions.push(...deprecatedFiles.actions);\n const wouldRemove = deprecatedFiles.removed;\n\n // 4b. Remove deprecated directories (no longer managed by safeword)\n const deprecatedDirectories = planExistingDirectoriesRemoval(schema.deprecatedDirs, ctx.cwd);\n actions.push(...deprecatedDirectories.actions);\n wouldRemove.push(...deprecatedDirectories.removed);\n\n // 5. chmod\n actions.push({ type: 'chmod', paths: CHMOD_PATHS });\n\n // 6. JSON merges (always apply to ensure keys are present)\n for (const [filePath, definition] of Object.entries(schema.jsonMerges)) {\n actions.push({ type: 'json-merge', path: filePath, definition });\n }\n\n // 7. Text patches (only if marker missing, skip .husky in non-git repos)\n actions.push(...planTextPatches(schema.textPatches, ctx.cwd, ctx.isGitRepo));\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.developmentDeps,\n ctx.isGitRepo,\n );\n\n // 9. Compute deprecated packages to remove (only those actually installed)\n const packagesToRemove = schema.deprecatedPackages.filter(pkg => pkg in ctx.developmentDeps);\n\n return {\n actions,\n wouldCreate,\n wouldUpdate,\n wouldRemove,\n packagesToInstall,\n packagesToRemove,\n };\n}\n\n/**\n *\n * @param schema\n * @param ctx\n * @param full\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 directoriesToCleanup = new Set<string>();\n for (const filePath of ownedFiles.removed) {\n const parentDirectory = getClaudeParentDirectoryForCleanup(filePath);\n if (parentDirectory) directoriesToCleanup.add(parentDirectory);\n }\n const cleanupDirectories = planExistingDirectoriesRemoval([...directoriesToCleanup], ctx.cwd);\n actions.push(...cleanupDirectories.actions);\n wouldRemove.push(...cleanupDirectories.removed);\n\n // 2. JSON unmerges\n for (const [filePath, definition] of Object.entries(schema.jsonMerges)) {\n actions.push({ type: 'json-unmerge', path: filePath, definition });\n }\n\n // 3. Text unpatches\n for (const [filePath, definition] of Object.entries(schema.textPatches)) {\n const fullPath = nodePath.join(ctx.cwd, filePath);\n if (exists(fullPath)) {\n const content = readFileSafe(fullPath) ?? '';\n if (content.includes(definition.marker)) {\n actions.push({ type: 'text-unpatch', path: filePath, definition });\n }\n }\n }\n\n // 4. Remove preserved directories first (reverse order, only if empty)\n const preserved = planExistingDirectoriesRemoval(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 = planExistingDirectoriesRemoval(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.developmentDeps)\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\n/**\n *\n * @param plan\n * @param ctx\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\n/**\n *\n * @param action\n * @param ctx\n * @param result\n */\nfunction executeChmod(cwd: string, paths: string[]): void {\n for (const path of paths) {\n const fullPath = nodePath.join(cwd, path);\n if (exists(fullPath)) makeScriptsExecutable(fullPath);\n }\n}\n\nfunction executeRmdir(cwd: string, path: string, result: ExecutionResult): void {\n if (removeIfEmpty(nodePath.join(cwd, path))) result.removed.push(path);\n}\n\nfunction executeAction(action: Action, ctx: ProjectContext, result: ExecutionResult): void {\n switch (action.type) {\n case 'mkdir': {\n ensureDirectory(nodePath.join(ctx.cwd, action.path));\n result.created.push(action.path);\n break;\n }\n case 'rmdir': {\n executeRmdir(ctx.cwd, action.path, result);\n break;\n }\n case 'write': {\n executeWrite(ctx.cwd, action.path, action.content, result);\n break;\n }\n case 'rm': {\n remove(nodePath.join(ctx.cwd, action.path));\n result.removed.push(action.path);\n break;\n }\n case 'chmod': {\n executeChmod(ctx.cwd, action.paths);\n break;\n }\n case 'json-merge': {\n executeJsonMerge(ctx.cwd, action.path, action.definition, ctx);\n break;\n }\n case 'json-unmerge': {\n executeJsonUnmerge(ctx.cwd, action.path, action.definition);\n break;\n }\n case 'text-patch': {\n executeTextPatch(ctx.cwd, action.path, action.definition);\n break;\n }\n case 'text-unpatch': {\n executeTextUnpatch(ctx.cwd, action.path, action.definition);\n break;\n }\n }\n}\n\n/**\n *\n * @param cwd\n * @param path\n * @param content\n * @param result\n */\nfunction executeWrite(cwd: string, path: string, content: string, result: ExecutionResult): void {\n const fullPath = nodePath.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\n/**\n *\n * @param definition\n * @param ctx\n */\nfunction resolveFileContent(definition: FileDefinition, ctx: ProjectContext): string | undefined {\n if (definition.template) {\n const templatesDirectory = getTemplatesDirectory();\n return readFile(nodePath.join(templatesDirectory, definition.template));\n }\n\n if (definition.content) {\n return typeof definition.content === 'function' ? definition.content() : definition.content;\n }\n\n if (definition.generator) {\n // Generator can return null to skip file creation\n return definition.generator(ctx);\n }\n\n throw new Error('FileDefinition must have template, content, or generator');\n}\n\n/**\n *\n * @param installedPath\n * @param newContent\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\n/**\n *\n * @param schema\n * @param projectType\n * @param installedDevDeps\n * @param isGitRepo\n */\nexport function computePackagesToInstall(\n schema: SafewordSchema,\n projectType: ProjectType,\n installedDevelopmentDeps: 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 // Add conditional packages based on project type\n needed.push(...getConditionalPackages(schema.packages.conditional, projectType));\n\n return needed.filter(pkg => !(pkg in installedDevelopmentDeps));\n}\n\n/**\n *\n * @param schema\n * @param projectType\n * @param installedDevDeps\n */\nfunction computePackagesToRemove(\n schema: SafewordSchema,\n projectType: ProjectType,\n installedDevelopmentDeps: Record<string, string>,\n): string[] {\n const safewordPackages = [\n ...schema.packages.base,\n ...getConditionalPackages(schema.packages.conditional, projectType),\n ];\n\n // Only remove packages that are actually installed\n return safewordPackages.filter(pkg => pkg in installedDevelopmentDeps);\n}\n\n/**\n *\n * @param cwd\n * @param path\n * @param definition\n * @param ctx\n */\nfunction executeJsonMerge(\n cwd: string,\n path: string,\n definition: JsonMergeDefinition,\n ctx: ProjectContext,\n): void {\n const fullPath = nodePath.join(cwd, path);\n const rawExisting = readJson(fullPath) as Record<string, unknown> | undefined;\n\n // Skip if file doesn't exist and skipIfMissing is set\n if (!rawExisting && definition.skipIfMissing) return;\n\n const existing = rawExisting ?? {};\n const merged = definition.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\n/**\n *\n * @param cwd\n * @param path\n * @param definition\n */\nfunction executeJsonUnmerge(cwd: string, path: string, definition: JsonMergeDefinition): void {\n const fullPath = nodePath.join(cwd, path);\n if (!exists(fullPath)) return;\n\n const existing = readJson(fullPath) as Record<string, unknown> | undefined;\n if (!existing) return;\n\n const unmerged = definition.unmerge(existing);\n\n // Check if file should be removed\n if (definition.removeFileIfEmpty) {\n const remainingKeys = Object.keys(unmerged).filter(k => unmerged[k] !== undefined);\n if (remainingKeys.length === 0) {\n remove(fullPath);\n return;\n }\n }\n\n writeJson(fullPath, unmerged);\n}\n\n/**\n *\n * @param cwd\n * @param path\n * @param definition\n */\nfunction executeTextPatch(cwd: string, path: string, definition: TextPatchDefinition): void {\n const fullPath = nodePath.join(cwd, path);\n let content = readFileSafe(fullPath) ?? '';\n\n // Check if already patched\n if (content.includes(definition.marker)) return;\n\n // Apply patch\n content =\n definition.operation === 'prepend'\n ? definition.content + content\n : content + definition.content;\n\n writeFile(fullPath, content);\n}\n\n/**\n *\n * @param cwd\n * @param path\n * @param definition\n */\nfunction executeTextUnpatch(cwd: string, path: string, definition: TextPatchDefinition): void {\n const fullPath = nodePath.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(definition.content, '');\n\n // If full content wasn't found but marker exists, remove lines containing the marker\n if (unpatched === content && content.includes(definition.marker)) {\n // Remove lines containing the marker\n const lines = content.split('\\n');\n const filtered = lines.filter(line => !line.includes(definition.marker));\n unpatched = filtered.join('\\n').replace(/^\\n+/, ''); // Remove leading empty lines\n }\n\n writeFile(fullPath, unpatched);\n}\n","/**\n * Shared installation utilities\n *\n * Package manager detection and MCP server constants.\n * Operations are handled by reconcile() in src/reconcile.ts.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { info, listItem, success, warn } from './output.js';\n\ntype PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';\n\n/**\n * Detect package manager by lockfile (bun > pnpm > yarn > npm)\n */\nexport function detectPackageManager(cwd: string): PackageManager {\n if (existsSync(join(cwd, 'bun.lockb')) || existsSync(join(cwd, 'bun.lock'))) return 'bun';\n if (existsSync(join(cwd, 'pnpm-lock.yaml'))) return 'pnpm';\n if (existsSync(join(cwd, 'yarn.lock'))) return 'yarn';\n if (existsSync(join(cwd, 'package-lock.json'))) return 'npm';\n return 'npm';\n}\n\n/**\n * Get install command args for package manager\n */\nfunction getInstallArguments(pm: PackageManager): string[] {\n const args: Record<PackageManager, string[]> = {\n npm: ['install', '-D'],\n yarn: ['add', '-D'],\n pnpm: ['add', '-D'],\n bun: ['add', '-D'],\n };\n return args[pm];\n}\n\n/**\n * Get display command for logging\n */\nfunction getInstallCommand(pm: PackageManager, packages: string[]): string {\n const cmds: Record<PackageManager, string> = {\n npm: `npm install -D ${packages.join(' ')}`,\n yarn: `yarn add -D ${packages.join(' ')}`,\n pnpm: `pnpm add -D ${packages.join(' ')}`,\n bun: `bun add -D ${packages.join(' ')}`,\n };\n return cmds[pm];\n}\n\n/**\n * Get uninstall command for package manager\n */\nexport function getUninstallCommand(pm: PackageManager, packages: string[]): string {\n const cmds: Record<PackageManager, string> = {\n npm: `npm uninstall ${packages.join(' ')}`,\n yarn: `yarn remove ${packages.join(' ')}`,\n pnpm: `pnpm remove ${packages.join(' ')}`,\n bun: `bun remove ${packages.join(' ')}`,\n };\n return cmds[pm];\n}\n\n/**\n * Install packages using detected package manager\n */\nexport function installDependencies(cwd: string, packages: string[], label = 'packages'): void {\n if (packages.length === 0) return;\n\n const pm = detectPackageManager(cwd);\n const displayCmd = getInstallCommand(pm, packages);\n\n info(`\\nInstalling ${label}...`);\n info(`Running: ${displayCmd}`);\n\n try {\n const args = [...getInstallArguments(pm), ...packages];\n execFileSync(pm, args, { cwd, stdio: 'inherit' });\n success(`Installed ${label}`);\n } catch {\n warn(`Failed to install ${label}. Run manually:`);\n listItem(displayCmd);\n }\n}\n\n/**\n * MCP servers installed by safeword\n */\nexport const MCP_SERVERS = {\n context7: {\n command: 'bunx',\n args: ['@upstash/context7-mcp@latest'],\n },\n playwright: {\n command: 'bunx',\n args: ['@playwright/mcp@latest'],\n },\n} as const;\n","/**\n * Go Language Pack - Schema Definitions\n *\n * All Go-specific file definitions and config generators.\n * Imported by schema.ts and spread into SAFEWORD_SCHEMA.\n *\n * Mirrors the structure of typescript/files.ts and python/files.ts for consistency.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport YAML from 'yaml';\n\nimport type { FileDefinition, ManagedFileDefinition } from '../types.js';\n\n// ============================================================================\n// Shared Configuration Constants\n// ============================================================================\n\n/**\n * Core golangci-lint v2 config structure (without header comment).\n * Used by both generateGolangciConfig() and getSafewordGolangciStandalone().\n */\nconst GOLANGCI_CONFIG_CORE = `version: \"2\"\n\nlinters:\n default: all\n disable:\n - forbidigo # Too strict for defaults - blocks all print statements\n - wsl # Deprecated in v2.2.0, replaced by wsl_v5\n exclusions:\n presets:\n - std-error-handling\n - common-false-positives\n settings:\n nestif:\n min-complexity: 4\n\nformatters:\n enable:\n - gofumpt\n`;\n\n// ============================================================================\n// Config Generators\n// ============================================================================\n\n/**\n * Generate golangci-lint v2 configuration.\n *\n * Uses ALL linters for maximum strictness, matching our philosophy\n * for TypeScript (ESLint) and Python (ruff).\n *\n * Key constraints (via included linters):\n * - cyclop: max-complexity 10 (default)\n * - nestif: min-complexity 4 (matches ESLint max-depth)\n * - funlen: 60 lines, 40 statements (defaults)\n */\nfunction generateGolangciConfig(): string {\n return `# Generated by safeword - DO NOT EDIT\n# To customize, add overrides below the formatters section\n\n${GOLANGCI_CONFIG_CORE}`;\n}\n\n/**\n * Generate safeword golangci-lint configuration for .safeword/.golangci.yml.\n *\n * If project has existing config, merges it with safeword rules (safeword wins).\n * Handles both v1 and v2 config formats.\n *\n * @param existingConfig - Path to existing config (e.g., '.golangci.yml') or null\n * @param cwd - Project directory\n */\nfunction generateSafewordGolangciConfig(existingConfig: string | undefined, cwd: string): string {\n if (!existingConfig) {\n // No existing config - generate standalone\n return getSafewordGolangciStandalone();\n }\n\n const configPath = nodePath.join(cwd, existingConfig);\n if (!existsSync(configPath)) {\n return getSafewordGolangciStandalone();\n }\n\n try {\n const projectContent = readFileSync(configPath, 'utf8');\n const projectConfig = YAML.parse(projectContent) as Record<string, unknown> | undefined;\n\n if (!projectConfig) {\n return getSafewordGolangciStandalone();\n }\n\n // Detect if project uses v1 or v2 format\n const isV2 = projectConfig.version === '2';\n\n return isV2\n ? getSafewordGolangciMergedV2(projectConfig)\n : getSafewordGolangciMergedV1(projectConfig);\n } catch {\n // YAML parse error - fall back to standalone\n console.warn(`Safeword: Could not parse ${existingConfig}, using standalone config`);\n return getSafewordGolangciStandalone();\n }\n}\n\n/**\n * Standalone safeword golangci-lint config (v2 format)\n */\nfunction getSafewordGolangciStandalone(): string {\n return `# Safeword golangci-lint config - standalone (no project config to merge)\n# Used by hooks for LLM enforcement.\n\n${GOLANGCI_CONFIG_CORE}`;\n}\n\n/**\n * Merge project v2 config with safeword rules (safeword wins)\n */\nfunction getSafewordGolangciMergedV2(projectConfig: Record<string, unknown>): string {\n // Start with project config, override with safeword settings\n const merged: Record<string, unknown> = {\n ...projectConfig,\n version: '2',\n };\n\n // Merge linters section - safeword rules take precedence\n const projectLinters = (projectConfig.linters as Record<string, unknown>) || {};\n const safewordLinters: Record<string, unknown> = {\n default: 'all',\n disable: ['forbidigo', 'wsl'], // Too strict / deprecated\n exclusions: {\n presets: ['std-error-handling', 'common-false-positives'],\n },\n settings: {\n nestif: { 'min-complexity': 4 },\n },\n };\n\n // Deep merge linters (safeword wins on conflict)\n merged.linters = deepMerge(projectLinters, safewordLinters);\n\n // Ensure formatters includes gofumpt\n const projectFormatters = (projectConfig.formatters as Record<string, unknown>) || {};\n const projectFormatterEnable = (projectFormatters.enable as string[]) || [];\n const formatterEnable = projectFormatterEnable.includes('gofumpt')\n ? projectFormatterEnable\n : [...projectFormatterEnable, 'gofumpt'];\n\n merged.formatters = { ...projectFormatters, enable: formatterEnable };\n\n const header = `# Safeword golangci-lint config - merged with project config\n# Used by hooks for LLM enforcement. Human pre-commits use project config.\n# Source: ${projectConfig.version === '2' ? 'v2' : 'v1'} project config + safeword rules\n# Re-run \\`safeword upgrade\\` to regenerate after project config changes.\n\n`;\n\n return header + YAML.stringify(merged);\n}\n\n/**\n * Merge project v1 config with safeword rules (safeword wins), output v1 format\n */\nfunction getSafewordGolangciMergedV1(projectConfig: Record<string, unknown>): string {\n // For v1, we generate v1-compatible output\n const merged: Record<string, unknown> = { ...projectConfig };\n\n // Merge linters section - safeword rules take precedence\n const projectLinters = (projectConfig.linters as Record<string, unknown>) || {};\n merged.linters = {\n ...projectLinters,\n 'enable-all': true,\n // Keep any project disable list, but ensure our strict rules\n };\n\n // Merge linters-settings\n const projectSettings = (projectConfig['linters-settings'] as Record<string, unknown>) || {};\n merged['linters-settings'] = deepMerge(projectSettings, {\n nestif: { 'min-complexity': 4 },\n gofumpt: { 'extra-rules': true },\n });\n\n const header = `# Safeword golangci-lint config - merged with project config (v1 format)\n# Used by hooks for LLM enforcement. Human pre-commits use project config.\n# NOTE: Consider migrating to golangci-lint v2 format.\n# Re-run \\`safeword upgrade\\` to regenerate after project config changes.\n\n`;\n\n return header + YAML.stringify(merged);\n}\n\n/**\n * Deep merge objects where second object wins on conflict.\n */\nfunction deepMerge(\n base: Record<string, unknown>,\n override: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = { ...base };\n\n for (const [key, value] of Object.entries(override)) {\n result[key] =\n value &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n result[key] &&\n typeof result[key] === 'object' &&\n !Array.isArray(result[key])\n ? deepMerge(result[key] as Record<string, unknown>, value as Record<string, unknown>)\n : value;\n }\n\n return result;\n}\n\n// ============================================================================\n// Owned Files (overwritten on upgrade)\n// ============================================================================\n\n/**\n * Go owned files for .safeword/ directory.\n * These are overwritten on upgrade if content changed.\n */\nexport const golangOwnedFiles: Record<string, FileDefinition> = {\n // golangci-lint config for hooks (merges with project config if exists)\n '.safeword/.golangci.yml': {\n generator: ctx =>\n ctx.languages?.golang\n ? generateSafewordGolangciConfig(ctx.projectType.existingGolangciConfig, ctx.cwd)\n : undefined,\n },\n};\n\n// ============================================================================\n// Managed Files (create if missing, update if matches template)\n// ============================================================================\n\nexport const golangManagedFiles: Record<string, ManagedFileDefinition> = {\n // Project-level Go linter config (created only if no existing golangci config)\n '.golangci.yml': {\n generator: ctx => {\n // Skip if project already has golangci config (safeword will use .safeword/.golangci.yml)\n if (ctx.projectType.existingGolangciConfig) return;\n if (!ctx.languages?.golang) return;\n return generateGolangciConfig();\n },\n },\n};\n","/**\n * Python Language Pack - Schema Definitions\n *\n * All Python-specific file definitions and config generators.\n * Imported by schema.ts and spread into SAFEWORD_SCHEMA.\n *\n * Mirrors the structure of typescript/files.ts and golang/files.ts for consistency.\n */\n\nimport type { FileDefinition, ManagedFileDefinition } from '../types.js';\nimport { detectPythonLayers, detectRootPackage } from './setup.js';\n\n// ============================================================================\n// Shared Ruff Configuration\n// ============================================================================\n\n/**\n * Shared Ruff lint rules for .safeword/ruff.toml.\n * Used by both standalone and extending configs.\n */\nconst RUFF_LINT_RULES = `[lint]\nselect = [\"ALL\"]\nignore = [\n \"D\", # pydocstyle - too noisy for LLM code\n \"ANN\", # flake8-annotations - mypy handles this\n \"COM812\", # missing trailing comma - conflicts with formatter\n \"ISC001\", # single-line-implicit-string-concatenation - conflicts with formatter\n \"E501\", # line too long - formatter handles this\n]\n\n[lint.per-file-ignores]\n\"tests/**\" = [\"S101\"] # allow assert in tests\n\n[lint.mccabe]\nmax-complexity = 10`;\n\n// ============================================================================\n// Config Generators for .safeword/ (ownedFiles)\n// ============================================================================\n\n/**\n * Generate Ruff configuration for .safeword/ruff.toml.\n *\n * If project has existing ruff config, extends it with stricter rules.\n * Otherwise generates standalone config.\n *\n * Used by hooks via --config .safeword/ruff.toml flag.\n *\n * @param existingRuffConfig - True if project has existing ruff config\n */\nfunction generateRuffBaseConfig(existingRuffConfig = false): string {\n if (existingRuffConfig) {\n // Extend project's existing Ruff config with safeword's stricter rules\n // Note: Ruff's extend works with both ruff.toml and pyproject.toml\n // Using pyproject.toml as the extend target covers both cases since:\n // - If [tool.ruff] exists in pyproject.toml, it will be extended\n // - If ruff.toml exists, ruff reads it first, then pyproject.toml is a no-op fallback\n return `# Safeword Ruff config - extends project config with stricter rules\n# Used by hooks for LLM enforcement. Human pre-commits use project config.\n# Re-run \\`safeword upgrade\\` to regenerate after project config changes.\n\n# Inherit from project's pyproject.toml [tool.ruff] section\nextend = \"../pyproject.toml\"\n\n# Safeword overrides (stricter than project defaults)\nline-length = 100\n\n${RUFF_LINT_RULES}\n`;\n }\n\n // Standalone config (no project config to extend)\n return `# Generated by safeword - DO NOT EDIT\n# Customize by creating ruff.toml at project root\n\nline-length = 100\n\n${RUFF_LINT_RULES}\n`;\n}\n\n// ============================================================================\n// Owned Files (overwritten on upgrade)\n// ============================================================================\n\n/**\n * Python owned files for .safeword/ directory.\n * These are overwritten on upgrade if content changed.\n */\nexport const pythonOwnedFiles: Record<string, FileDefinition> = {\n // Ruff config for hooks (extends project config if exists)\n '.safeword/ruff.toml': {\n generator: ctx =>\n ctx.languages?.python\n ? generateRuffBaseConfig(ctx.projectType.existingRuffConfig)\n : undefined,\n },\n};\n\n// ============================================================================\n// Config Generators for project root (managedFiles)\n// ============================================================================\n\n/**\n * Generate standalone ruff.toml for project root.\n * Extends .safeword/ruff.toml to get strict rules.\n */\nfunction generateProjectRuffConfig(): string {\n return `# Generated by safeword\n# Add your project-specific overrides here.\n# Safeword's strict rules are in .safeword/ruff.toml\n\nextend = \".safeword/ruff.toml\"\n\n# Project overrides (add your customizations below)\n`;\n}\n\n/**\n * Generate standalone mypy.ini for project root.\n * Strict config for LLM agents - enforces type annotations.\n */\nfunction generateProjectMypyConfig(): string {\n return `# Generated by safeword\n# Strict config for LLM agents - type annotations enforced.\n\n[mypy]\nstrict = True\nwarn_unreachable = True\nignore_missing_imports = True\nshow_error_codes = True\npretty = True\n`;\n}\n\n/**\n * Generate standalone .importlinter for project root.\n *\n * @param layers - Detected layers (e.g., ['domain', 'services', 'api'])\n * @param rootPackage - Root package name for the project\n */\nfunction generateProjectImportLinterConfig(layers: string[], rootPackage: string): string {\n if (layers.length < 2) return '';\n\n const layerList = layers.map(l => ` ${rootPackage}.${l}`).join('\\n');\n\n return `# Generated by safeword\n# Layer architecture contracts - enforces clean dependencies\n\n[importlinter]\nroot_packages = ${rootPackage}\n\n[importlinter:contract:layers]\nname = Layer architecture\ntype = layers\nlayers =\n${layerList}\n`;\n}\n\n// ============================================================================\n// Managed Files (create if missing, reconciled on reset/upgrade)\n// ============================================================================\n\nexport const pythonManagedFiles: Record<string, ManagedFileDefinition> = {\n // Project-level ruff config (created only if no existing ruff config)\n 'ruff.toml': {\n generator: ctx => {\n // Skip if not a Python project\n if (!ctx.languages?.python) return;\n // Skip if project already has ruff config\n if (ctx.projectType.existingRuffConfig) return;\n return generateProjectRuffConfig();\n },\n },\n\n // Project-level mypy config (created only if no existing mypy config)\n 'mypy.ini': {\n generator: ctx => {\n // Skip if not a Python project\n if (!ctx.languages?.python) return;\n // Skip if project already has mypy config\n if (ctx.projectType.existingMypyConfig) return;\n return generateProjectMypyConfig();\n },\n },\n\n // Project-level import-linter config (created only if layers detected and no existing config)\n '.importlinter': {\n generator: ctx => {\n // Skip if not a Python project\n if (!ctx.languages?.python) return;\n // Skip if project already has import-linter config\n if (ctx.projectType.existingImportLinterConfig) return;\n\n // Detect layers\n const layers = detectPythonLayers(ctx.cwd);\n if (layers.length < 2) return;\n\n const rootPackage = detectRootPackage(ctx.cwd);\n return generateProjectImportLinterConfig(layers, rootPackage);\n },\n },\n};\n","/**\n * Configuration templates - ESLint config generation and hook settings\n *\n * ESLint flat config (v9+) using eslint-plugin-safeword for all rules.\n * Framework detection uses safeword.detect utilities at runtime.\n *\n * See: https://eslint.org/docs/latest/use/configure/configuration-files\n */\n\n// ============================================================================\n// Shared ESLint Rule Definitions (for template interpolation)\n// ============================================================================\n\n/**\n * Full strict rules for safeword ESLint configs that extend existing project configs.\n * These rules are applied after project rules (safeword wins on conflict).\n * Used by: getSafewordEslintConfigExtending, getSafewordEslintConfigLegacy\n */\n/**\n * Get Prettier-related imports and config entries based on whether project has existing formatter.\n * Avoids repetition across ESLint config generators.\n */\nfunction getPrettierConfig(hasExistingFormatter: boolean): {\n import: string;\n configEntry: string;\n} {\n if (hasExistingFormatter) {\n return { import: '', configEntry: '' };\n }\n return {\n import: 'import eslintConfigPrettier from \"eslint-config-prettier\";',\n configEntry: ' eslintConfigPrettier,',\n };\n}\n\nconst SAFEWORD_STRICT_RULES_FULL = `// Safeword strict rules - applied after project rules (win on conflict)\nconst safewordStrictRules = {\n rules: {\n // Prevent common LLM mistakes\n \"no-unused-vars\": [\"error\", { argsIgnorePattern: \"^_\", varsIgnorePattern: \"^_\" }],\n \"no-undef\": \"error\",\n \"no-unreachable\": \"error\",\n \"no-constant-condition\": \"error\",\n \"no-empty\": \"error\",\n \"no-extra-semi\": \"error\",\n \"no-func-assign\": \"error\",\n \"no-import-assign\": \"error\",\n \"no-invalid-regexp\": \"error\",\n \"no-irregular-whitespace\": \"error\",\n \"no-loss-of-precision\": \"error\",\n \"no-misleading-character-class\": \"error\",\n \"no-prototype-builtins\": \"error\",\n \"no-unexpected-multiline\": \"error\",\n \"no-unsafe-finally\": \"error\",\n \"no-unsafe-negation\": \"error\",\n \"use-isnan\": \"error\",\n \"valid-typeof\": \"error\",\n // Strict code quality\n \"eqeqeq\": [\"error\", \"always\", { null: \"ignore\" }],\n \"no-var\": \"error\",\n \"prefer-const\": \"error\",\n },\n};`;\n\n/**\n * Generates an ESLint config using eslint-plugin-safeword.\n *\n * The generated config uses safeword.detect utilities to detect frameworks\n * and select the appropriate config at lint time.\n * @param hasExistingFormatter - If true, generates a minimal config without Prettier\n * @returns ESLint config file content as a string\n */\nexport function getEslintConfig(hasExistingFormatter = false): string {\n if (hasExistingFormatter) {\n return getFormatterAgnosticEslintConfig();\n }\n return getStandardEslintConfig();\n}\n\n/**\n * Standard ESLint config - full linting with Prettier\n */\nfunction getStandardEslintConfig(): string {\n return `import { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport safeword from \"eslint-plugin-safeword\";\nimport eslintConfigPrettier from \"eslint-config-prettier\";\n\nconst { detect, configs } = safeword;\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst deps = detect.collectAllDeps(__dirname);\nconst framework = detect.detectFramework(deps);\n\n// Map framework to base config\n// Note: Astro config only lints .astro files, so we combine it with TypeScript config\n// to also lint .ts files in Astro projects\nconst baseConfigs = {\n next: configs.recommendedTypeScriptNext,\n react: configs.recommendedTypeScriptReact,\n astro: [...configs.recommendedTypeScript, ...configs.astro],\n typescript: configs.recommendedTypeScript,\n javascript: configs.recommended,\n};\n\nexport default [\n { ignores: detect.getIgnores(deps) },\n ...baseConfigs[framework],\n ...(detect.hasVitest(deps) ? configs.vitest : []),\n ...(detect.hasPlaywright(deps) ? configs.playwright : []),\n ...(detect.hasTailwind(deps) ? configs.tailwind : []),\n ...(detect.hasTanstackQuery(deps) ? configs.tanstackQuery : []),\n eslintConfigPrettier,\n];\n`;\n}\n\n/**\n * Formatter-agnostic ESLint config - minimal config for projects with existing formatter.\n * Used alongside external formatters (Biome, dprint, etc.) that handle formatting.\n * Does not include eslint-config-prettier since another tool handles formatting.\n */\nfunction getFormatterAgnosticEslintConfig(): string {\n return `import { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport safeword from \"eslint-plugin-safeword\";\n\nconst { detect, configs } = safeword;\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst deps = detect.collectAllDeps(__dirname);\nconst framework = detect.detectFramework(deps);\n\n// Map framework to base config\n// Note: Astro config only lints .astro files, so we combine it with TypeScript config\n// to also lint .ts files in Astro projects\nconst baseConfigs = {\n next: configs.recommendedTypeScriptNext,\n react: configs.recommendedTypeScriptReact,\n astro: [...configs.recommendedTypeScript, ...configs.astro],\n typescript: configs.recommendedTypeScript,\n javascript: configs.recommended,\n};\n\nexport default [\n { ignores: detect.getIgnores(deps) },\n ...baseConfigs[framework],\n ...(detect.hasVitest(deps) ? configs.vitest : []),\n ...(detect.hasPlaywright(deps) ? configs.playwright : []),\n ...(detect.hasTailwind(deps) ? configs.tailwind : []),\n ...(detect.hasTanstackQuery(deps) ? configs.tanstackQuery : []),\n];\n`;\n}\n\n/**\n * Generates an ESLint config for .safeword/eslint.config.mjs\n * This config is used by hooks for LLM enforcement with stricter rules.\n *\n * @param existingConfig - Path to existing ESLint config (e.g., 'eslint.config.mjs' or '.eslintrc.json')\n * @param hasExistingFormatter - If true, skip eslint-config-prettier\n * @returns ESLint config file content as a string\n */\nexport function getSafewordEslintConfig(\n existingConfig: string | undefined,\n hasExistingFormatter = false,\n): string {\n if (existingConfig) {\n // Check if it's a legacy config (.eslintrc.*)\n if (existingConfig.startsWith('.eslintrc')) {\n return getSafewordEslintConfigLegacy(existingConfig, hasExistingFormatter);\n }\n return getSafewordEslintConfigExtending(existingConfig, hasExistingFormatter);\n }\n\n // No existing config - generate standalone (same as project-level)\n return getSafewordEslintConfigStandalone(hasExistingFormatter);\n}\n\n/**\n * Safeword ESLint config that extends a flat config (eslint.config.mjs)\n */\nfunction getSafewordEslintConfigExtending(\n existingConfig: string,\n hasExistingFormatter: boolean,\n): string {\n const prettier = getPrettierConfig(hasExistingFormatter);\n\n return `// Safeword ESLint config - extends project config with stricter rules\n// Used by hooks for LLM enforcement. Human pre-commits use project config.\n// Re-run \\`safeword upgrade\\` to regenerate after project config changes.\n${prettier.import}\n\nlet projectConfig = [];\ntry {\n projectConfig = (await import(\"../${existingConfig}\")).default;\n // Ensure it's an array\n if (!Array.isArray(projectConfig)) {\n projectConfig = [projectConfig];\n }\n} catch (e) {\n console.warn(\"Safeword: Could not load project ESLint config, using defaults only\");\n}\n\n${SAFEWORD_STRICT_RULES_FULL}\n\nexport default [\n ...projectConfig,\n safewordStrictRules,\n${prettier.configEntry}\n];\n`;\n}\n\n/**\n * Safeword ESLint config that extends a legacy config (.eslintrc.*)\n */\nfunction getSafewordEslintConfigLegacy(\n existingConfig: string,\n hasExistingFormatter: boolean,\n): string {\n const prettier = getPrettierConfig(hasExistingFormatter);\n\n return `// Safeword ESLint config - extends legacy project config with stricter rules\n// Used by hooks for LLM enforcement. Human pre-commits use project config.\n// NOTE: Legacy .eslintrc.* format is deprecated. Consider migrating to eslint.config.mjs\nimport { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { FlatCompat } from \"@eslint/eslintrc\";\n${prettier.import}\n\nconsole.warn(\"Safeword: Legacy .eslintrc.* detected. Consider migrating to eslint.config.mjs\");\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n// baseDirectory is .safeword/, so ../${existingConfig} resolves to project root\nconst compat = new FlatCompat({ baseDirectory: __dirname });\n\nlet projectConfig = [];\ntry {\n projectConfig = compat.extends(\"../${existingConfig}\");\n} catch (e) {\n console.warn(\"Safeword: Could not load project ESLint config, using defaults only\");\n}\n\n${SAFEWORD_STRICT_RULES_FULL}\n\nexport default [\n ...projectConfig,\n safewordStrictRules,\n${prettier.configEntry}\n];\n`;\n}\n\n/**\n * Standalone safeword ESLint config (no project config to extend)\n */\nfunction getSafewordEslintConfigStandalone(hasExistingFormatter: boolean): string {\n const prettier = getPrettierConfig(hasExistingFormatter);\n\n return `// Safeword ESLint config - standalone (no project config to extend)\n// Used by hooks for LLM enforcement.\nimport { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport safeword from \"eslint-plugin-safeword\";\n${prettier.import}\n\nconst { detect, configs } = safeword;\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n// Look in parent directory for deps (this file is in .safeword/)\nconst deps = detect.collectAllDeps(dirname(__dirname));\nconst framework = detect.detectFramework(deps);\n\nconst baseConfigs = {\n next: configs.recommendedTypeScriptNext,\n react: configs.recommendedTypeScriptReact,\n astro: [...configs.recommendedTypeScript, ...configs.astro],\n typescript: configs.recommendedTypeScript,\n javascript: configs.recommended,\n};\n\n${SAFEWORD_STRICT_RULES_FULL}\n\nexport default [\n { ignores: detect.getIgnores(deps) },\n ...baseConfigs[framework],\n ...(detect.hasVitest(deps) ? configs.vitest : []),\n ...(detect.hasPlaywright(deps) ? configs.playwright : []),\n ...(detect.hasTailwind(deps) ? configs.tailwind : []),\n ...(detect.hasTanstackQuery(deps) ? configs.tanstackQuery : []),\n safewordStrictRules,\n${prettier.configEntry}\n];\n`;\n}\n\n// Cursor hooks configuration (.cursor/hooks.json format)\n// See: https://cursor.com/docs/agent/hooks\nexport const CURSOR_HOOKS = {\n afterFileEdit: [{ command: 'bun ./.safeword/hooks/cursor/after-file-edit.ts' }],\n stop: [{ command: 'bun ./.safeword/hooks/cursor/stop.ts' }],\n};\n\n// Claude Code hooks configuration (.claude/settings.json format)\nexport const SETTINGS_HOOKS = {\n SessionStart: [\n {\n hooks: [\n {\n type: 'command',\n command: 'bun \"$CLAUDE_PROJECT_DIR\"/.safeword/hooks/session-verify-agents.ts',\n },\n ],\n },\n {\n hooks: [\n {\n type: 'command',\n command: 'bun \"$CLAUDE_PROJECT_DIR\"/.safeword/hooks/session-version.ts',\n },\n ],\n },\n {\n hooks: [\n {\n type: 'command',\n command: 'bun \"$CLAUDE_PROJECT_DIR\"/.safeword/hooks/session-lint-check.ts',\n },\n ],\n },\n ],\n UserPromptSubmit: [\n {\n hooks: [\n {\n type: 'command',\n command: 'bun \"$CLAUDE_PROJECT_DIR\"/.safeword/hooks/prompt-timestamp.ts',\n },\n ],\n },\n {\n hooks: [\n {\n type: 'command',\n command: 'bun \"$CLAUDE_PROJECT_DIR\"/.safeword/hooks/prompt-questions.ts',\n },\n ],\n },\n ],\n Stop: [\n {\n hooks: [\n {\n type: 'command',\n command: 'bun \"$CLAUDE_PROJECT_DIR\"/.safeword/hooks/stop-quality.ts',\n },\n ],\n },\n ],\n PostToolUse: [\n {\n matcher: 'Write|Edit|MultiEdit|NotebookEdit',\n hooks: [\n {\n type: 'command',\n command: 'bun \"$CLAUDE_PROJECT_DIR\"/.safeword/hooks/post-tool-lint.ts',\n },\n ],\n },\n {\n matcher: 'Write|Edit|MultiEdit|NotebookEdit',\n hooks: [\n {\n type: 'command',\n command: 'bun \"$CLAUDE_PROJECT_DIR\"/.safeword/hooks/post-tool-guide-check.ts',\n },\n ],\n },\n ],\n};\n","/**\n * TypeScript/JavaScript Language Pack - Schema Definitions\n *\n * All JS/TS specific file definitions, JSON merges, and packages.\n * Imported by schema.ts and spread into SAFEWORD_SCHEMA.\n */\n\nimport { getEslintConfig, getSafewordEslintConfig } from '../../templates/config.js';\nimport type { FileDefinition, JsonMergeDefinition, ManagedFileDefinition } from '../types.js';\n\n// ============================================================================\n// Shared Definitions\n// ============================================================================\n\n/**\n * Prettier styling defaults - shared between .safeword/.prettierrc and root .prettierrc\n */\nconst PRETTIER_DEFAULTS = {\n semi: true,\n singleQuote: true,\n tabWidth: 2,\n trailingComma: 'all',\n printWidth: 100,\n endOfLine: 'lf',\n useTabs: false,\n bracketSpacing: true,\n arrowParens: 'avoid',\n} as const;\n\n/**\n * Get Prettier plugins based on project type.\n * Tailwind plugin must be last for proper class sorting.\n */\nfunction getPrettierPlugins(projectType: {\n astro?: boolean;\n shell?: boolean;\n tailwind?: boolean;\n}): string[] {\n const plugins: string[] = [];\n if (projectType.astro) plugins.push('prettier-plugin-astro');\n if (projectType.shell) plugins.push('prettier-plugin-sh');\n if (projectType.tailwind) plugins.push('prettier-plugin-tailwindcss');\n return plugins;\n}\n\n/**\n * Biome config merge - adds safeword files to excludes list.\n * Biome v2 uses `includes` with `!` prefix for exclusions.\n */\nconst BIOME_JSON_MERGE: JsonMergeDefinition = {\n keys: ['files.includes'],\n skipIfMissing: true, // Only modify if project already uses Biome\n merge: existing => {\n const files = (existing.files as Record<string, unknown>) ?? {};\n const existingIncludes = Array.isArray(files.includes) ? files.includes : [];\n\n // Add safeword exclusions (! prefix) if not already present\n // Note: Biome v2.2.0+ doesn't need /** for folders\n const safewordExcludes = ['!eslint.config.mjs', '!.safeword'];\n const newIncludes = [...existingIncludes];\n for (const exclude of safewordExcludes) {\n if (!newIncludes.includes(exclude)) {\n newIncludes.push(exclude);\n }\n }\n\n return {\n ...existing,\n files: {\n ...files,\n includes: newIncludes,\n },\n };\n },\n unmerge: existing => {\n const files = (existing.files as Record<string, unknown>) ?? {};\n const existingIncludes = Array.isArray(files.includes) ? files.includes : [];\n\n // Remove safeword exclusions from includes list\n const safewordExcludes = new Set(['!eslint.config.mjs', '!.safeword', '!.safeword/**']);\n const cleanedIncludes = existingIncludes.filter(\n (entry: string) => !safewordExcludes.has(entry),\n );\n\n // Build cleaned files object\n const cleanedFiles = { ...files };\n if (cleanedIncludes.length > 0) {\n cleanedFiles.includes = cleanedIncludes;\n } else {\n delete cleanedFiles.includes;\n }\n\n // Remove files key entirely if empty\n if (Object.keys(cleanedFiles).length === 0) {\n const { files: _, ...rest } = existing;\n return rest;\n }\n\n return { ...existing, files: cleanedFiles };\n },\n};\n\n// ============================================================================\n// Owned Files (overwritten on upgrade)\n// ============================================================================\n\nexport const typescriptOwnedFiles: Record<string, FileDefinition> = {\n // Language-specific safeword configs for hooks (extend project configs if they exist)\n // These configs are used by hooks for LLM enforcement with stricter rules\n '.safeword/eslint.config.mjs': {\n generator: ctx =>\n ctx.languages?.javascript\n ? getSafewordEslintConfig(\n ctx.projectType.existingEslintConfig,\n ctx.projectType.existingFormatter,\n )\n : undefined,\n },\n '.safeword/.prettierrc': {\n generator: ctx => {\n // Skip for non-JS projects or projects with existing formatter (they use Biome, etc.)\n if (!ctx.languages?.javascript) return;\n if (ctx.projectType.existingFormatter) return;\n // Add plugins based on project type\n const plugins = getPrettierPlugins(ctx.projectType);\n const config = plugins.length > 0 ? { ...PRETTIER_DEFAULTS, plugins } : PRETTIER_DEFAULTS;\n return JSON.stringify(config, undefined, 2);\n },\n },\n};\n\n// ============================================================================\n// Managed Files (create if missing, update if matches template)\n// ============================================================================\n\nexport const typescriptManagedFiles: Record<string, ManagedFileDefinition> = {\n // Project-level ESLint config (created only if no existing ESLint config)\n 'eslint.config.mjs': {\n generator: ctx => {\n // Skip if project already has ESLint config (safeword will use .safeword/eslint.config.mjs)\n if (ctx.projectType.existingEslintConfig) return;\n if (!ctx.languages?.javascript) return;\n return getEslintConfig(ctx.projectType.existingFormatter);\n },\n },\n // Minimal tsconfig for ESLint type-checked linting (only if missing)\n 'tsconfig.json': {\n generator: ctx => {\n // Skip for non-JS projects (Python-only)\n if (!ctx.languages?.javascript) return;\n // Only create for TypeScript projects\n if (!ctx.developmentDeps.typescript && !ctx.developmentDeps['typescript-eslint']) {\n return;\n }\n return JSON.stringify(\n {\n compilerOptions: {\n target: 'ES2022',\n module: 'NodeNext',\n moduleResolution: 'NodeNext',\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n noEmit: true,\n },\n include: ['**/*.ts', '**/*.tsx'],\n exclude: ['node_modules', 'dist', 'build'],\n },\n undefined,\n 2,\n );\n },\n },\n // Knip config for dead code detection (used by /audit)\n 'knip.json': {\n generator: ctx =>\n ctx.languages?.javascript\n ? JSON.stringify(\n {\n ignore: ['.safeword/**'],\n ignoreDependencies: ['eslint-plugin-safeword'],\n },\n undefined,\n 2,\n )\n : undefined,\n },\n // Project-level Prettier config (created only if no existing formatter)\n '.prettierrc': {\n generator: ctx => {\n // Skip for non-JS projects or projects with existing formatter\n if (!ctx.languages?.javascript) return;\n if (ctx.projectType.existingFormatter) return;\n // Create base config with styling defaults (no plugins - those are in .safeword/.prettierrc)\n return JSON.stringify(PRETTIER_DEFAULTS, undefined, 2);\n },\n },\n};\n\n// ============================================================================\n// JSON Merges\n// ============================================================================\n\nexport const typescriptJsonMerges: Record<string, JsonMergeDefinition> = {\n 'package.json': {\n keys: ['scripts.lint', 'scripts.format', 'scripts.format:check', 'scripts.knip'],\n skipIfMissing: true, // Don't create for Python-only projects (no JS tooling)\n conditionalKeys: {\n existingLinter: ['scripts.lint:eslint'], // Projects with existing linter get separate ESLint script\n publishableLibrary: ['scripts.publint'],\n shell: ['scripts.lint:sh'],\n },\n merge: (existing, ctx) => {\n const scripts = { ...(existing.scripts as Record<string, string>) };\n const result = { ...existing };\n\n if (ctx.projectType.existingLinter) {\n // Project with existing linter: add lint:eslint for safeword-specific rules\n if (!scripts['lint:eslint']) scripts['lint:eslint'] = 'eslint .';\n // Don't touch their existing lint script\n } else {\n // No existing linter: ESLint is the primary linter\n if (!scripts.lint) scripts.lint = 'eslint .';\n }\n\n if (!ctx.projectType.existingFormatter) {\n // No existing formatter: add Prettier\n if (!scripts.format) scripts.format = 'prettier --write .';\n if (!scripts['format:check']) scripts['format:check'] = 'prettier --check .';\n }\n\n // Always add knip for dead code detection\n if (!scripts.knip) scripts.knip = 'knip';\n\n // Conditional: publint for publishable libraries\n if (ctx.projectType.publishableLibrary && !scripts.publint) {\n scripts.publint = 'publint';\n }\n\n // Conditional: lint:sh for projects with shell scripts\n if (ctx.projectType.shell && !scripts['lint:sh']) {\n scripts['lint:sh'] = 'shellcheck **/*.sh';\n }\n\n result.scripts = scripts;\n\n return result;\n },\n unmerge: existing => {\n const result = { ...existing };\n const scripts = { ...(existing.scripts as Record<string, string>) };\n\n // Remove safeword-specific scripts but preserve lint/format (useful standalone)\n delete scripts['lint:eslint']; // Biome hybrid mode\n delete scripts['lint:sh'];\n delete scripts['format:check'];\n delete scripts.knip;\n delete scripts.publint;\n\n if (Object.keys(scripts).length > 0) {\n result.scripts = scripts;\n } else {\n delete result.scripts;\n }\n\n return result;\n },\n },\n\n // Prettier config - add defaults while preserving user customizations\n '.prettierrc': {\n keys: ['plugins'],\n skipIfMissing: true,\n merge: (existing, ctx) => {\n const result = { ...existing } as Record<string, unknown>;\n\n // Add defaults for missing styling options (preserves user customizations)\n for (const [key, value] of Object.entries(PRETTIER_DEFAULTS)) {\n if (result[key] === undefined) {\n result[key] = value;\n }\n }\n\n // Always update plugins based on project type (safeword owns this)\n const plugins = getPrettierPlugins(ctx.projectType);\n if (plugins.length > 0) {\n result.plugins = plugins;\n } else {\n delete result.plugins;\n }\n\n return result;\n },\n unmerge: existing => {\n const result = { ...existing } as Record<string, unknown>;\n delete result.plugins; // Remove plugins on uninstall (packages being removed)\n return result;\n },\n },\n\n // Biome excludes - add safeword files so they don't get linted by Biome/Ultracite\n // Biome v2 uses `includes` with `!` prefix for exclusions (not a separate `ignore` key)\n // Support both biome.json and biome.jsonc\n 'biome.json': BIOME_JSON_MERGE,\n 'biome.jsonc': BIOME_JSON_MERGE,\n};\n\n// ============================================================================\n// Packages\n// ============================================================================\n\nexport const typescriptPackages = {\n base: [\n // Core tools (always needed for JS/TS)\n 'eslint',\n // Safeword plugin (bundles eslint-config-prettier + all ESLint plugins)\n 'eslint-plugin-safeword',\n // Architecture and dead code tools (used by /audit)\n 'dependency-cruiser',\n 'knip',\n ],\n conditional: {\n // Prettier (only for projects without existing formatter)\n standard: ['prettier'], // \"standard\" = !existingFormatter\n // Prettier plugins (only for projects without existing formatter that need them)\n astro: ['prettier-plugin-astro'],\n tailwind: ['prettier-plugin-tailwindcss'],\n shell: ['prettier-plugin-sh'],\n // Non-ESLint tools\n publishableLibrary: ['publint'],\n shellcheck: ['shellcheck'], // Renamed from shell to avoid conflict with prettier-plugin-sh\n // Legacy ESLint config compat (needed when extending .eslintrc.* configs)\n legacyEslint: ['@eslint/eslintrc'],\n },\n};\n","/**\n * Content templates - static string content\n *\n * Note: Most templates (SAFEWORD.md, hooks, skills, guides, etc.) are now\n * file-based in the templates/ directory. This file contains only small\n * string constants that are used inline.\n */\n\nexport const AGENTS_MD_LINK = `**⚠️ ALWAYS READ FIRST:** \\`.safeword/SAFEWORD.md\\`\n\nThe SAFEWORD.md file contains core development patterns, workflows, and conventions.\nRead it BEFORE working on any task in this project.\n\n---`;\n","/**\n * Hook utilities for Claude Code settings\n */\n\ninterface HookCommand {\n type: string;\n command: string;\n}\n\ninterface HookEntry {\n matcher?: string;\n hooks: HookCommand[];\n}\n\n/**\n * Type guard to check if a value is a hook entry with hooks array\n * @param h\n */\nfunction isHookEntry(h: unknown): h is HookEntry {\n return (\n typeof h === 'object' && h !== null && 'hooks' in h && Array.isArray((h as HookEntry).hooks)\n );\n}\n\n/**\n * Check if a hook entry contains a safeword hook (command contains '.safeword')\n * @param h\n */\nfunction isSafewordHook(h: unknown): boolean {\n if (!isHookEntry(h)) return false;\n return h.hooks.some(cmd => typeof cmd.command === 'string' && cmd.command.includes('.safeword'));\n}\n\n/**\n * Filter out safeword hooks from an array of hook entries\n * @param hooks\n */\nexport function filterOutSafewordHooks(hooks: unknown[]): unknown[] {\n return hooks.filter(h => !isSafewordHook(h));\n}\n","/**\n * SAFEWORD Schema - Single Source of Truth\n *\n * All files, directories, configurations, and packages managed by safeword\n * are defined here. Commands use this schema via the reconciliation engine.\n *\n * Adding a new file? Add it here and it will be handled by setup/upgrade/reset.\n */\n\nimport { golangManagedFiles, golangOwnedFiles } from './packs/golang/files.js';\nimport { pythonManagedFiles, pythonOwnedFiles } from './packs/python/files.js';\nimport {\n typescriptJsonMerges,\n typescriptManagedFiles,\n typescriptOwnedFiles,\n typescriptPackages,\n} from './packs/typescript/files.js';\n// Re-export shared types from packs/types.ts (breaks circular dependency)\nexport type { FileDefinition, JsonMergeDefinition, ProjectContext } from './packs/types.js';\nimport type { FileDefinition, JsonMergeDefinition, ManagedFileDefinition } from './packs/types.js';\nimport { CURSOR_HOOKS, SETTINGS_HOOKS } from './templates/config.js';\nimport { AGENTS_MD_LINK } from './templates/content.js';\nimport { filterOutSafewordHooks } from './utils/hooks.js';\nimport { MCP_SERVERS } from './utils/install.js';\nimport { VERSION } from './version.js';\n\nexport interface TextPatchDefinition {\n operation: 'prepend' | 'append';\n content: string;\n marker: string; // Used to detect if already applied & for removal\n createIfMissing: boolean;\n}\n\nexport interface SafewordSchema {\n version: string;\n ownedDirs: string[]; // Fully owned - create on setup, delete on reset\n sharedDirs: string[]; // We add to but don't own\n preservedDirs: string[]; // Created on setup, NOT deleted on reset (user data)\n deprecatedFiles: string[]; // Files to delete on upgrade (renamed or removed)\n deprecatedPackages: string[]; // Packages to uninstall on upgrade (consolidated into safeword plugin)\n deprecatedDirs: string[]; // Directories to delete on upgrade (no longer managed)\n ownedFiles: Record<string, FileDefinition>; // Overwrite on upgrade (if changed)\n managedFiles: Record<string, ManagedFileDefinition>; // Create if missing, update if safeword content\n jsonMerges: Record<string, JsonMergeDefinition>;\n textPatches: Record<string, TextPatchDefinition>;\n packages: {\n base: string[];\n conditional: Record<string, string[]>;\n };\n}\n\n// ============================================================================\n// Shared JSON Merge Definitions\n// ============================================================================\n\n/**\n * MCP servers JSON merge - shared between .mcp.json and .cursor/mcp.json\n */\nconst MCP_JSON_MERGE: JsonMergeDefinition = {\n keys: ['mcpServers.context7', 'mcpServers.playwright'],\n removeFileIfEmpty: true,\n merge: existing => {\n const mcpServers = (existing.mcpServers as Record<string, unknown>) ?? {};\n return {\n ...existing,\n mcpServers: {\n ...mcpServers,\n context7: MCP_SERVERS.context7,\n playwright: MCP_SERVERS.playwright,\n },\n };\n },\n unmerge: existing => {\n const result = { ...existing };\n const mcpServers = { ...(existing.mcpServers as Record<string, unknown>) };\n\n delete mcpServers.context7;\n delete mcpServers.playwright;\n\n if (Object.keys(mcpServers).length > 0) {\n result.mcpServers = mcpServers;\n } else {\n delete result.mcpServers;\n }\n\n return result;\n },\n};\n\n// ============================================================================\n// SAFEWORD_SCHEMA - The Single Source of Truth\n// ============================================================================\n\nexport const SAFEWORD_SCHEMA: SafewordSchema = {\n version: VERSION,\n\n // Directories fully owned by safeword (created on setup, deleted on reset)\n ownedDirs: [\n '.safeword',\n '.safeword/hooks',\n '.safeword/hooks/cursor',\n '.safeword/hooks/lib',\n '.safeword/guides',\n '.safeword/templates',\n '.safeword/prompts',\n '.safeword/planning',\n '.safeword/planning/specs',\n '.safeword/planning/test-definitions',\n '.safeword/planning/design',\n '.safeword/planning/issues',\n '.safeword/planning/plans',\n '.safeword/scripts',\n '.cursor',\n '.cursor/rules',\n '.cursor/commands',\n ],\n\n // Directories we add to but don't own (not deleted on reset)\n sharedDirs: ['.claude', '.claude/skills', '.claude/commands'],\n\n // Created on setup but NOT deleted on reset (preserves user data)\n preservedDirs: [\n '.safeword/learnings',\n '.safeword/tickets',\n '.safeword/tickets/completed',\n '.safeword/logs',\n ],\n\n // Files to delete on upgrade (renamed or removed in newer versions)\n deprecatedFiles: [\n '.safeword/templates/user-stories-template.md',\n // Consolidated into planning-guide.md and testing-guide.md (v0.8.0)\n '.safeword/guides/development-workflow.md',\n '.safeword/guides/tdd-best-practices.md',\n '.safeword/guides/user-story-guide.md',\n '.safeword/guides/test-definitions-guide.md',\n // Boundaries config now project-specific (v0.9.0)\n '.safeword/eslint-boundaries.config.mjs',\n // Shell hooks replaced with TypeScript/Bun (v0.13.0)\n '.safeword/hooks/session-verify-agents.sh',\n '.safeword/hooks/session-version.sh',\n '.safeword/hooks/session-lint-check.sh',\n '.safeword/hooks/prompt-timestamp.sh',\n '.safeword/hooks/prompt-questions.sh',\n '.safeword/hooks/post-tool-lint.sh',\n '.safeword/hooks/stop-quality.sh',\n '.safeword/hooks/cursor/after-file-edit.sh',\n '.safeword/hooks/cursor/stop.sh',\n // Shell libraries no longer needed with Bun\n '.safeword/lib/common.sh',\n '.safeword/lib/jq-fallback.sh',\n // Skill renamed from enforcing-tdd to tdd-enforcing (v0.16.0)\n '.claude/skills/safeword-enforcing-tdd/SKILL.md',\n '.cursor/rules/safeword-enforcing-tdd.mdc',\n ],\n\n // Packages to uninstall on upgrade (now bundled in eslint-plugin-safeword)\n deprecatedPackages: [\n // Individual ESLint plugins now bundled in eslint-plugin-safeword\n '@eslint/js',\n 'eslint-plugin-import-x',\n 'eslint-import-resolver-typescript',\n 'eslint-plugin-sonarjs',\n 'eslint-plugin-unicorn',\n 'eslint-plugin-boundaries',\n 'eslint-plugin-playwright',\n 'eslint-plugin-promise',\n 'eslint-plugin-regexp',\n 'eslint-plugin-jsdoc',\n 'eslint-plugin-simple-import-sort',\n 'eslint-plugin-security',\n // Conditional ESLint plugins now in safeword\n 'typescript-eslint',\n 'eslint-plugin-react',\n 'eslint-plugin-react-hooks',\n 'eslint-plugin-jsx-a11y',\n '@next/eslint-plugin-next',\n 'eslint-plugin-astro',\n '@vitest/eslint-plugin',\n // Pre-commit hooks no longer managed by safeword\n 'husky',\n 'lint-staged',\n ],\n\n // Directories to delete on upgrade (no longer managed by safeword)\n deprecatedDirs: [\n '.husky', // Pre-commit hooks no longer managed by safeword\n '.safeword/lib', // Shell libraries no longer needed with Bun (v0.13.0)\n '.claude/skills/safeword-enforcing-tdd', // Renamed to safeword-tdd-enforcing (v0.16.0)\n ],\n\n // Files owned by safeword (overwritten on upgrade if content changed)\n ownedFiles: {\n // Core files\n '.safeword/AGENTS.md': { template: 'AGENTS.md' },\n '.safeword/SAFEWORD.md': { template: 'SAFEWORD.md' },\n '.safeword/version': { content: () => VERSION },\n // config.json is created by packs system but needs to be registered for cleanup on uninstall\n // Generator returns undefined = never created/updated by schema, but still deleted on uninstall\n '.safeword/config.json': { generator: (): undefined => undefined },\n\n // Language-specific safeword configs for hooks (extend project configs if they exist)\n ...typescriptOwnedFiles,\n ...pythonOwnedFiles,\n ...golangOwnedFiles,\n\n // Hooks shared library (2 files) - TypeScript with Bun runtime\n '.safeword/hooks/lib/lint.ts': { template: 'hooks/lib/lint.ts' },\n '.safeword/hooks/lib/quality.ts': { template: 'hooks/lib/quality.ts' },\n\n // Hooks (8 files) - TypeScript with Bun runtime\n '.safeword/hooks/session-verify-agents.ts': { template: 'hooks/session-verify-agents.ts' },\n '.safeword/hooks/session-version.ts': { template: 'hooks/session-version.ts' },\n '.safeword/hooks/session-lint-check.ts': { template: 'hooks/session-lint-check.ts' },\n '.safeword/hooks/prompt-timestamp.ts': { template: 'hooks/prompt-timestamp.ts' },\n '.safeword/hooks/prompt-questions.ts': { template: 'hooks/prompt-questions.ts' },\n '.safeword/hooks/post-tool-lint.ts': { template: 'hooks/post-tool-lint.ts' },\n '.safeword/hooks/post-tool-guide-check.ts': { template: 'hooks/post-tool-guide-check.ts' },\n '.safeword/hooks/stop-quality.ts': { template: 'hooks/stop-quality.ts' },\n\n // Guides (11 files)\n '.safeword/guides/architecture-guide.md': { template: 'guides/architecture-guide.md' },\n '.safeword/guides/cli-reference.md': { template: 'guides/cli-reference.md' },\n '.safeword/guides/code-philosophy.md': { template: 'guides/code-philosophy.md' },\n '.safeword/guides/context-files-guide.md': { template: 'guides/context-files-guide.md' },\n '.safeword/guides/data-architecture-guide.md': {\n template: 'guides/data-architecture-guide.md',\n },\n '.safeword/guides/design-doc-guide.md': { template: 'guides/design-doc-guide.md' },\n '.safeword/guides/learning-extraction.md': { template: 'guides/learning-extraction.md' },\n '.safeword/guides/llm-guide.md': { template: 'guides/llm-guide.md' },\n '.safeword/guides/planning-guide.md': { template: 'guides/planning-guide.md' },\n '.safeword/guides/testing-guide.md': { template: 'guides/testing-guide.md' },\n '.safeword/guides/zombie-process-cleanup.md': { template: 'guides/zombie-process-cleanup.md' },\n\n // Templates (7 files)\n '.safeword/templates/architecture-template.md': {\n template: 'doc-templates/architecture-template.md',\n },\n '.safeword/templates/design-doc-template.md': {\n template: 'doc-templates/design-doc-template.md',\n },\n '.safeword/templates/task-spec-template.md': {\n template: 'doc-templates/task-spec-template.md',\n },\n '.safeword/templates/test-definitions-feature.md': {\n template: 'doc-templates/test-definitions-feature.md',\n },\n '.safeword/templates/ticket-template.md': { template: 'doc-templates/ticket-template.md' },\n '.safeword/templates/feature-spec-template.md': {\n template: 'doc-templates/feature-spec-template.md',\n },\n '.safeword/templates/work-log-template.md': { template: 'doc-templates/work-log-template.md' },\n\n // Prompts (2 files)\n '.safeword/prompts/architecture.md': { template: 'prompts/architecture.md' },\n '.safeword/prompts/quality-review.md': { template: 'prompts/quality-review.md' },\n\n // Scripts (3 files)\n '.safeword/scripts/bisect-test-pollution.sh': { template: 'scripts/bisect-test-pollution.sh' },\n '.safeword/scripts/bisect-zombie-processes.sh': {\n template: 'scripts/bisect-zombie-processes.sh',\n },\n '.safeword/scripts/cleanup-zombies.sh': { template: 'scripts/cleanup-zombies.sh' },\n\n // Claude skills and commands (9 files)\n '.claude/skills/safeword-brainstorming/SKILL.md': {\n template: 'skills/safeword-brainstorming/SKILL.md',\n },\n '.claude/skills/safeword-debugging/SKILL.md': {\n template: 'skills/safeword-debugging/SKILL.md',\n },\n '.claude/skills/safeword-tdd-enforcing/SKILL.md': {\n template: 'skills/safeword-tdd-enforcing/SKILL.md',\n },\n '.claude/skills/safeword-quality-reviewing/SKILL.md': {\n template: 'skills/safeword-quality-reviewing/SKILL.md',\n },\n '.claude/skills/safeword-refactoring/SKILL.md': {\n template: 'skills/safeword-refactoring/SKILL.md',\n },\n '.claude/skills/safeword-writing-plans/SKILL.md': {\n template: 'skills/safeword-writing-plans/SKILL.md',\n },\n '.claude/skills/safeword-bdd-orchestrating/SKILL.md': {\n template: 'skills/safeword-bdd-orchestrating/SKILL.md',\n },\n '.claude/commands/bdd.md': { template: 'commands/bdd.md' },\n '.claude/commands/tdd.md': { template: 'commands/tdd.md' },\n '.claude/commands/drift.md': { template: 'commands/drift.md' },\n '.claude/commands/audit.md': { template: 'commands/audit.md' },\n '.claude/commands/cleanup-zombies.md': { template: 'commands/cleanup-zombies.md' },\n '.claude/commands/lint.md': { template: 'commands/lint.md' },\n '.claude/commands/quality-review.md': { template: 'commands/quality-review.md' },\n '.claude/commands/refactor.md': { template: 'commands/refactor.md' },\n\n // Cursor rules (7 files)\n '.cursor/rules/safeword-core.mdc': { template: 'cursor/rules/safeword-core.mdc' },\n '.cursor/rules/safeword-brainstorming.mdc': {\n template: 'cursor/rules/safeword-brainstorming.mdc',\n },\n '.cursor/rules/safeword-debugging.mdc': {\n template: 'cursor/rules/safeword-debugging.mdc',\n },\n '.cursor/rules/safeword-tdd-enforcing.mdc': {\n template: 'cursor/rules/safeword-tdd-enforcing.mdc',\n },\n '.cursor/rules/safeword-quality-reviewing.mdc': {\n template: 'cursor/rules/safeword-quality-reviewing.mdc',\n },\n '.cursor/rules/safeword-refactoring.mdc': {\n template: 'cursor/rules/safeword-refactoring.mdc',\n },\n '.cursor/rules/safeword-writing-plans.mdc': {\n template: 'cursor/rules/safeword-writing-plans.mdc',\n },\n '.cursor/rules/safeword-bdd-orchestrating.mdc': {\n template: 'cursor/rules/safeword-bdd-orchestrating.mdc',\n },\n\n // Cursor commands (8 files - same as Claude)\n '.cursor/commands/bdd.md': { template: 'commands/bdd.md' },\n '.cursor/commands/tdd.md': { template: 'commands/tdd.md' },\n '.cursor/commands/drift.md': { template: 'commands/drift.md' },\n '.cursor/commands/audit.md': { template: 'commands/audit.md' },\n '.cursor/commands/cleanup-zombies.md': { template: 'commands/cleanup-zombies.md' },\n '.cursor/commands/lint.md': { template: 'commands/lint.md' },\n '.cursor/commands/quality-review.md': { template: 'commands/quality-review.md' },\n '.cursor/commands/refactor.md': { template: 'commands/refactor.md' },\n\n // Cursor hooks adapters (2 files) - TypeScript with Bun runtime\n '.safeword/hooks/cursor/after-file-edit.ts': { template: 'hooks/cursor/after-file-edit.ts' },\n '.safeword/hooks/cursor/stop.ts': { template: 'hooks/cursor/stop.ts' },\n },\n\n // Files created if missing, updated only if content matches current template\n managedFiles: {\n // TypeScript/JavaScript managed files (ESLint, tsconfig, Knip, Prettier configs)\n ...typescriptManagedFiles,\n // Python managed files (ruff.toml, mypy.ini, .importlinter)\n ...pythonManagedFiles,\n // Go managed files (.golangci.yml)\n ...golangManagedFiles,\n },\n\n // JSON files where we merge specific keys\n jsonMerges: {\n // TypeScript/JavaScript JSON merges (package.json, .prettierrc, biome.json)\n ...typescriptJsonMerges,\n\n // Language-agnostic JSON merges\n '.claude/settings.json': {\n keys: ['hooks'],\n merge: existing => {\n // Preserve non-safeword hooks while adding/updating safeword hooks\n const existingHooks = (existing.hooks as Record<string, unknown[]>) ?? {};\n const mergedHooks: Record<string, unknown[]> = { ...existingHooks };\n\n for (const [event, newHooks] of Object.entries(SETTINGS_HOOKS)) {\n const eventHooks = mergedHooks[event] ?? [];\n const nonSafewordHooks = filterOutSafewordHooks(eventHooks);\n mergedHooks[event] = [...nonSafewordHooks, ...newHooks];\n }\n\n return { ...existing, hooks: mergedHooks };\n },\n unmerge: existing => {\n // Remove only safeword hooks, preserve custom hooks\n const existingHooks = (existing.hooks as Record<string, unknown[]>) ?? {};\n const cleanedHooks: Record<string, unknown[]> = {};\n\n for (const [event, eventHooks] of Object.entries(existingHooks)) {\n const nonSafewordHooks = filterOutSafewordHooks(eventHooks);\n if (nonSafewordHooks.length > 0) {\n cleanedHooks[event] = nonSafewordHooks;\n }\n }\n\n const result = { ...existing };\n if (Object.keys(cleanedHooks).length > 0) {\n result.hooks = cleanedHooks;\n } else {\n delete result.hooks;\n }\n return result;\n },\n },\n\n '.mcp.json': MCP_JSON_MERGE,\n '.cursor/mcp.json': MCP_JSON_MERGE,\n\n '.cursor/hooks.json': {\n keys: ['version', 'hooks.afterFileEdit', 'hooks.stop'],\n removeFileIfEmpty: true,\n merge: existing => {\n const hooks = (existing.hooks as Record<string, unknown[]>) ?? {};\n return {\n ...existing,\n version: 1, // Required by Cursor\n hooks: {\n ...hooks,\n ...CURSOR_HOOKS,\n },\n };\n },\n unmerge: existing => {\n const result = { ...existing };\n const hooks = { ...(existing.hooks as Record<string, unknown[]>) };\n\n delete hooks.afterFileEdit;\n delete hooks.stop;\n\n if (Object.keys(hooks).length > 0) {\n result.hooks = hooks;\n } else {\n delete result.hooks;\n delete result.version;\n }\n\n return result;\n },\n },\n },\n\n // Text files where we patch specific content\n textPatches: {\n 'AGENTS.md': {\n operation: 'prepend',\n content: AGENTS_MD_LINK,\n marker: '.safeword/SAFEWORD.md',\n createIfMissing: true,\n },\n 'CLAUDE.md': {\n operation: 'prepend',\n content: AGENTS_MD_LINK,\n marker: '.safeword/SAFEWORD.md',\n createIfMissing: false, // Only patch if exists, don't create (AGENTS.md is primary)\n },\n },\n\n // NPM packages to install (JS/TS specific packages from typescript pack)\n packages: typescriptPackages,\n};\n","/**\n * Project type detection from package.json\n *\n * Detects frameworks and tools used in the project to configure\n * appropriate linting rules.\n */\n\nimport { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport { detect } from 'eslint-plugin-safeword';\n\n// Re-export detection constants from eslint-plugin-safeword (single source of truth)\nexport const {\n TAILWIND_PACKAGES,\n TANSTACK_QUERY_PACKAGES,\n\n hasExistingLinter,\n hasExistingFormatter,\n} = detect;\n\n// Python project file markers\nconst PYPROJECT_TOML = 'pyproject.toml';\nconst REQUIREMENTS_TXT = 'requirements.txt';\nconst UV_LOCK = 'uv.lock';\n\n// Go project file markers\nconst GO_MOD = 'go.mod';\n\n// ESLint config file markers (flat config and legacy)\nconst ESLINT_CONFIG_FILES = [\n 'eslint.config.mjs',\n 'eslint.config.js',\n 'eslint.config.cjs',\n '.eslintrc.js',\n '.eslintrc.cjs',\n '.eslintrc.json',\n '.eslintrc.yml',\n '.eslintrc.yaml',\n] as const;\n\n// golangci-lint config file markers\nconst GOLANGCI_CONFIG_FILES = [\n '.golangci.yml',\n '.golangci.yaml',\n '.golangci.toml',\n '.golangci.json',\n];\n\n// Python frameworks to detect (order matters - first match wins)\nconst PYTHON_FRAMEWORKS = ['django', 'flask', 'fastapi'] as const;\n\nexport interface PackageJson {\n name?: string;\n version?: string;\n private?: boolean;\n main?: string;\n module?: string;\n exports?: unknown;\n types?: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nexport interface ProjectType {\n typescript: boolean;\n react: boolean;\n nextjs: boolean;\n astro: boolean;\n vitest: boolean;\n playwright: boolean;\n tailwind: boolean;\n tanstackQuery: boolean;\n publishableLibrary: boolean;\n shell: boolean;\n /** True if project has existing lint script or linter config */\n existingLinter: boolean;\n /** True if project has existing format script or formatter config */\n existingFormatter: boolean;\n /** Path to existing ESLint config if present (e.g., 'eslint.config.mjs' or '.eslintrc.json') */\n existingEslintConfig: string | undefined;\n /** True if existing ESLint config is legacy format (.eslintrc.*) requiring FlatCompat */\n legacyEslint: boolean;\n /** True if project has [tool.ruff] in pyproject.toml or ruff.toml */\n existingRuffConfig: boolean;\n /** True if project has [tool.mypy] in pyproject.toml or mypy.ini */\n existingMypyConfig: boolean;\n /** True if project has [tool.importlinter] in pyproject.toml or .importlinter */\n existingImportLinterConfig: boolean;\n /** Path to existing golangci-lint config if present (e.g., '.golangci.yml') */\n existingGolangciConfig: string | undefined;\n}\n\n/**\n * Language detection result\n * @see ARCHITECTURE.md → Language Detection\n */\nexport interface Languages {\n javascript: boolean; // package.json exists\n python: boolean; // pyproject.toml OR requirements.txt exists\n golang: boolean; // go.mod exists\n}\n\n/**\n * Python-specific detection (returned only if languages.python)\n * @see ARCHITECTURE.md → Language Detection\n */\nexport interface PythonProjectType {\n framework: 'django' | 'flask' | 'fastapi' | undefined;\n packageManager: 'poetry' | 'uv' | 'pip';\n}\n\n/**\n * Detects which languages are used in the project\n * @param cwd - Working directory to scan\n * @returns Languages object indicating which languages are present\n * @see ARCHITECTURE.md → Language Detection\n */\nexport function detectLanguages(cwd: string): Languages {\n const hasPackageJson = existsSync(nodePath.join(cwd, 'package.json'));\n const hasPyproject = existsSync(nodePath.join(cwd, PYPROJECT_TOML));\n const hasRequirements = existsSync(nodePath.join(cwd, REQUIREMENTS_TXT));\n const hasGoModule = existsSync(nodePath.join(cwd, GO_MOD));\n\n return {\n javascript: hasPackageJson,\n python: hasPyproject || hasRequirements,\n golang: hasGoModule,\n };\n}\n\n/**\n * Detects Python project type (framework and package manager)\n * @param cwd - Working directory to scan\n * @returns PythonProjectType or undefined if not a Python project\n * @see ARCHITECTURE.md → Language Detection\n */\nexport function detectPythonType(cwd: string): PythonProjectType | undefined {\n const pyprojectPath = nodePath.join(cwd, PYPROJECT_TOML);\n const requirementsPath = nodePath.join(cwd, REQUIREMENTS_TXT);\n const uvLockPath = nodePath.join(cwd, UV_LOCK);\n\n const hasPyproject = existsSync(pyprojectPath);\n const hasRequirements = existsSync(requirementsPath);\n\n if (!hasPyproject && !hasRequirements) {\n return undefined;\n }\n\n // Read project file for dependency/tool detection\n const content = hasPyproject\n ? readFileSync(pyprojectPath, 'utf8')\n : readFileSync(requirementsPath, 'utf8');\n\n // Detect package manager (priority: poetry > uv > pip)\n let packageManager: PythonProjectType['packageManager'] = 'pip';\n if (hasPyproject && content.includes('[tool.poetry]')) {\n packageManager = 'poetry';\n } else if (existsSync(uvLockPath)) {\n packageManager = 'uv';\n }\n\n // Detect framework (first match wins)\n const contentLower = content.toLowerCase();\n const framework = PYTHON_FRAMEWORKS.find(fw => contentLower.includes(fw));\n\n return { framework, packageManager };\n}\n\n/**\n * Checks if a directory contains any .sh files up to specified depth.\n * Excludes node_modules and .git directories.\n * @param cwd\n * @param maxDepth\n */\nfunction hasShellScripts(cwd: string, maxDepth = 4): boolean {\n const excludeDirectories = new Set(['node_modules', '.git', '.safeword']);\n\n /**\n *\n * @param dir\n * @param depth\n */\n function scan(dir: string, depth: number): boolean {\n if (depth > maxDepth) return false;\n\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isFile() && entry.name.endsWith('.sh')) {\n return true;\n }\n if (\n entry.isDirectory() &&\n !excludeDirectories.has(entry.name) &&\n scan(nodePath.join(dir, entry.name), depth + 1)\n ) {\n return true;\n }\n }\n } catch {\n // Ignore permission errors\n }\n return false;\n }\n\n return scan(cwd, 0);\n}\n\ninterface PackageJsonWithScripts extends PackageJson {\n scripts?: Record<string, string>;\n}\n\n/**\n * Check if project has existing ESLint config.\n * @param cwd - Working directory to scan\n * @returns The config file path if found, undefined otherwise.\n */\nfunction findExistingEslintConfig(cwd: string): string | undefined {\n for (const config of ESLINT_CONFIG_FILES) {\n if (existsSync(nodePath.join(cwd, config))) {\n return config;\n }\n }\n return undefined;\n}\n\n/**\n * Check if project has existing Ruff config.\n * @param cwd - Working directory to scan\n * @returns True if ruff.toml exists OR [tool.ruff] in pyproject.toml\n */\nfunction hasExistingRuffConfig(cwd: string): boolean {\n // Check for standalone ruff.toml\n if (existsSync(nodePath.join(cwd, 'ruff.toml'))) return true;\n\n // Check for [tool.ruff] in pyproject.toml\n const pyprojectPath = nodePath.join(cwd, PYPROJECT_TOML);\n if (!existsSync(pyprojectPath)) return false;\n try {\n const content = readFileSync(pyprojectPath, 'utf8');\n return content.includes('[tool.ruff]');\n } catch {\n return false;\n }\n}\n\n/**\n * Check if project has existing mypy config.\n * @param cwd - Working directory to scan\n * @returns True if mypy.ini/.mypy.ini exists OR [tool.mypy] in pyproject.toml\n */\nfunction hasExistingMypyConfig(cwd: string): boolean {\n // Check for standalone mypy config files\n if (existsSync(nodePath.join(cwd, 'mypy.ini'))) return true;\n if (existsSync(nodePath.join(cwd, '.mypy.ini'))) return true;\n\n // Check for [tool.mypy] in pyproject.toml\n const pyprojectPath = nodePath.join(cwd, PYPROJECT_TOML);\n if (!existsSync(pyprojectPath)) return false;\n try {\n const content = readFileSync(pyprojectPath, 'utf8');\n return content.includes('[tool.mypy]');\n } catch {\n return false;\n }\n}\n\n/**\n * Check if project has existing import-linter config.\n * @param cwd - Working directory to scan\n * @returns True if .importlinter exists OR [tool.importlinter] in pyproject.toml\n */\nfunction hasExistingImportLinterConfig(cwd: string): boolean {\n // Check for standalone .importlinter file\n if (existsSync(nodePath.join(cwd, '.importlinter'))) return true;\n\n // Check for [tool.importlinter] in pyproject.toml\n const pyprojectPath = nodePath.join(cwd, PYPROJECT_TOML);\n if (!existsSync(pyprojectPath)) return false;\n try {\n const content = readFileSync(pyprojectPath, 'utf8');\n return content.includes('[tool.importlinter]');\n } catch {\n return false;\n }\n}\n\n/**\n * Check if project has existing golangci-lint config.\n * @param cwd - Working directory to scan\n * @returns The config file path if found, undefined otherwise.\n */\nfunction findExistingGolangciConfig(cwd: string): string | undefined {\n for (const config of GOLANGCI_CONFIG_FILES) {\n if (existsSync(nodePath.join(cwd, config))) {\n return config;\n }\n }\n return undefined;\n}\n\n/**\n * Detects project type from package.json contents and optional file scanning\n * @param packageJson - Package.json contents including scripts\n * @param cwd - Working directory for file-based detection\n */\nexport function detectProjectType(packageJson: PackageJsonWithScripts, cwd?: string): ProjectType {\n const deps = packageJson.dependencies || {};\n const developmentDeps = packageJson.devDependencies || {};\n const allDeps = { ...deps, ...developmentDeps };\n const scripts = packageJson.scripts || {};\n\n const hasTypescript = 'typescript' in allDeps;\n const hasReact = 'react' in deps || 'react' in developmentDeps;\n const hasNextJs = 'next' in deps;\n const hasAstro = 'astro' in deps || 'astro' in developmentDeps;\n const hasVitest = 'vitest' in developmentDeps;\n const hasPlaywright = '@playwright/test' in developmentDeps;\n // Tailwind v4 can be installed via tailwindcss, @tailwindcss/vite, or @tailwindcss/postcss\n const hasTailwind = TAILWIND_PACKAGES.some(pkg => pkg in allDeps);\n\n // TanStack Query detection\n const hasTanstackQuery = TANSTACK_QUERY_PACKAGES.some(pkg => pkg in allDeps);\n\n // Publishable library: has entry points and is not marked private\n const hasEntryPoints = !!(packageJson.main || packageJson.module || packageJson.exports);\n const isPublishable = hasEntryPoints && packageJson.private !== true;\n\n // Shell scripts: detected by scanning for .sh files\n const hasShell = cwd ? hasShellScripts(cwd) : false;\n\n // Generic tooling detection: detect intent, not specific tools\n const hasLinter = hasExistingLinter(scripts);\n const hasFormatter = cwd ? hasExistingFormatter(cwd, scripts) : 'format' in scripts;\n\n // Detect existing ESLint config and whether it's legacy format\n const eslintConfig = cwd ? findExistingEslintConfig(cwd) : undefined;\n const isLegacyEslint = eslintConfig?.startsWith('.eslintrc') ?? false;\n\n return {\n typescript: hasTypescript,\n react: hasReact || hasNextJs, // Next.js implies React\n nextjs: hasNextJs,\n astro: hasAstro,\n vitest: hasVitest,\n playwright: hasPlaywright,\n tailwind: hasTailwind,\n tanstackQuery: hasTanstackQuery,\n publishableLibrary: isPublishable,\n shell: hasShell,\n existingLinter: hasLinter,\n existingFormatter: hasFormatter,\n existingEslintConfig: eslintConfig,\n legacyEslint: isLegacyEslint,\n existingRuffConfig: cwd ? hasExistingRuffConfig(cwd) : false,\n existingMypyConfig: cwd ? hasExistingMypyConfig(cwd) : false,\n existingImportLinterConfig: cwd ? hasExistingImportLinterConfig(cwd) : false,\n existingGolangciConfig: cwd ? findExistingGolangciConfig(cwd) : undefined,\n };\n}\n","/**\n * Project Context Utilities\n *\n * Shared helpers for creating ProjectContext objects used by reconcile().\n */\n\nimport nodePath from 'node:path';\n\nimport type { ProjectContext } from '../schema.js';\nimport { readJson } from './fs.js';\nimport { isGitRepo } from './git.js';\nimport { detectLanguages, detectProjectType, type PackageJson } from './project-detector.js';\n\n/**\n * Create a ProjectContext from the current working directory.\n *\n * Reads package.json, detects project type and languages for use with reconcile().\n * @param cwd - Working directory\n */\nexport function createProjectContext(cwd: string): ProjectContext {\n const packageJson = readJson(nodePath.join(cwd, 'package.json')) as PackageJson | undefined;\n\n return {\n cwd,\n projectType: detectProjectType(packageJson ?? {}, cwd),\n developmentDeps: packageJson?.devDependencies ?? {},\n isGitRepo: isGitRepo(cwd),\n languages: detectLanguages(cwd),\n };\n}\n","/**\n * Git utilities for CLI operations\n */\n\nimport nodePath from 'node:path';\n\nimport { exists } from './fs.js';\n\n/**\n * Check if directory is a git repository\n * @param cwd\n */\nexport function isGitRepo(cwd: string): boolean {\n return exists(nodePath.join(cwd, '.git'));\n}\n"],"mappings":"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","names":["nodePath","isGitRepo","nodePath","existsSync","nodePath","hasExistingFormatter","existsSync","readFileSync","nodePath","existsSync","nodePath","nodePath","existsSync","readFileSync","nodePath","nodePath","nodePath","nodePath"]}
|
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-
|
|
11
|
+
const { setup } = await import("./setup-LS4T3KNU.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-
|
|
15
|
+
const { check } = await import("./check-TI6D5FLM.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-
|
|
19
|
+
const { upgrade } = await import("./upgrade-CWBPH7AS.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-
|
|
23
|
+
const { diff } = await import("./diff-3ROXA7D3.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 packages").action(async (options) => {
|
|
27
|
-
const { reset } = await import("./reset-
|
|
27
|
+
const { reset } = await import("./reset-DZ5QUVBK.js");
|
|
28
28
|
await reset(options);
|
|
29
29
|
});
|
|
30
30
|
program.command("sync-config").description("Regenerate depcruise config from current project structure").action(async () => {
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
SAFEWORD_SCHEMA,
|
|
3
3
|
createProjectContext,
|
|
4
4
|
reconcile
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-OPR33S5K.js";
|
|
6
6
|
import {
|
|
7
7
|
VERSION
|
|
8
8
|
} from "./chunk-ORQHKDT2.js";
|
|
@@ -163,4 +163,4 @@ Packages to install: ${result.packagesToInstall.length}`);
|
|
|
163
163
|
export {
|
|
164
164
|
diff
|
|
165
165
|
};
|
|
166
|
-
//# sourceMappingURL=diff-
|
|
166
|
+
//# sourceMappingURL=diff-3ROXA7D3.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
detectPackageManager,
|
|
5
5
|
getUninstallCommand,
|
|
6
6
|
reconcile
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-OPR33S5K.js";
|
|
8
8
|
import "./chunk-ORQHKDT2.js";
|
|
9
9
|
import {
|
|
10
10
|
error,
|
|
@@ -76,4 +76,4 @@ async function reset(options) {
|
|
|
76
76
|
export {
|
|
77
77
|
reset
|
|
78
78
|
};
|
|
79
|
-
//# sourceMappingURL=reset-
|
|
79
|
+
//# sourceMappingURL=reset-DZ5QUVBK.js.map
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
addInstalledPack,
|
|
8
8
|
detectLanguages,
|
|
9
9
|
setupGoTooling
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-4IXCKHSI.js";
|
|
11
11
|
import {
|
|
12
12
|
SAFEWORD_SCHEMA,
|
|
13
13
|
createProjectContext,
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
installDependencies,
|
|
20
20
|
installPythonDependencies,
|
|
21
21
|
reconcile
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-OPR33S5K.js";
|
|
23
23
|
import {
|
|
24
24
|
VERSION
|
|
25
25
|
} from "./chunk-ORQHKDT2.js";
|
|
@@ -242,4 +242,4 @@ Added format scripts to ${workspaceUpdates.length} workspace package(s)`);
|
|
|
242
242
|
export {
|
|
243
243
|
setup
|
|
244
244
|
};
|
|
245
|
-
//# sourceMappingURL=setup-
|
|
245
|
+
//# sourceMappingURL=setup-LS4T3KNU.js.map
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
addInstalledPack,
|
|
7
7
|
getMissingPacks,
|
|
8
8
|
isPackInstalled
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-4IXCKHSI.js";
|
|
10
10
|
import {
|
|
11
11
|
SAFEWORD_SCHEMA,
|
|
12
12
|
createProjectContext,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
installDependencies,
|
|
15
15
|
isGitRepo,
|
|
16
16
|
reconcile
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-OPR33S5K.js";
|
|
18
18
|
import {
|
|
19
19
|
VERSION
|
|
20
20
|
} from "./chunk-ORQHKDT2.js";
|
|
@@ -109,4 +109,4 @@ async function upgrade() {
|
|
|
109
109
|
export {
|
|
110
110
|
upgrade
|
|
111
111
|
};
|
|
112
|
-
//# sourceMappingURL=upgrade-
|
|
112
|
+
//# sourceMappingURL=upgrade-CWBPH7AS.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "safeword",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.6",
|
|
4
4
|
"description": "CLI for setting up and managing safeword development environments",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -44,7 +44,6 @@
|
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^20.10.0",
|
|
46
46
|
"@vitest/coverage-v8": "^4.0.16",
|
|
47
|
-
"dependency-cruiser": "^17.3.6",
|
|
48
47
|
"eslint": "^9.39.2",
|
|
49
48
|
"eslint-plugin-safeword": "^0.7.0",
|
|
50
49
|
"knip": "^5.79.0",
|