safeword 0.2.4 → 0.2.5
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-3NGQ4NR5.js +129 -0
- package/dist/check-3NGQ4NR5.js.map +1 -0
- package/dist/chunk-2XWIUEQK.js +190 -0
- package/dist/chunk-2XWIUEQK.js.map +1 -0
- package/dist/chunk-GZRQL3SX.js +146 -0
- package/dist/chunk-GZRQL3SX.js.map +1 -0
- package/dist/chunk-ORQHKDT2.js +10 -0
- package/dist/chunk-ORQHKDT2.js.map +1 -0
- package/dist/chunk-W66Z3C5H.js +21 -0
- package/dist/chunk-W66Z3C5H.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +34 -0
- package/dist/cli.js.map +1 -0
- package/dist/diff-Y6QTAW4O.js +166 -0
- package/dist/diff-Y6QTAW4O.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/reset-3ACTIYYE.js +143 -0
- package/dist/reset-3ACTIYYE.js.map +1 -0
- package/dist/setup-RR4M334C.js +266 -0
- package/dist/setup-RR4M334C.js.map +1 -0
- package/dist/upgrade-6AR3DHUV.js +134 -0
- package/dist/upgrade-6AR3DHUV.js.map +1 -0
- package/package.json +44 -19
- package/{.safeword → templates}/hooks/agents-md-check.sh +0 -0
- package/{.safeword → templates}/hooks/post-tool.sh +0 -0
- package/{.safeword → templates}/hooks/pre-commit.sh +0 -0
- package/.claude/commands/arch-review.md +0 -32
- package/.claude/commands/lint.md +0 -6
- package/.claude/commands/quality-review.md +0 -13
- package/.claude/commands/setup-linting.md +0 -6
- package/.claude/hooks/auto-lint.sh +0 -6
- package/.claude/hooks/auto-quality-review.sh +0 -170
- package/.claude/hooks/check-linting-sync.sh +0 -17
- package/.claude/hooks/inject-timestamp.sh +0 -6
- package/.claude/hooks/question-protocol.sh +0 -12
- package/.claude/hooks/run-linters.sh +0 -8
- package/.claude/hooks/run-quality-review.sh +0 -76
- package/.claude/hooks/version-check.sh +0 -10
- package/.claude/mcp/README.md +0 -96
- package/.claude/mcp/arcade.sample.json +0 -9
- package/.claude/mcp/context7.sample.json +0 -7
- package/.claude/mcp/playwright.sample.json +0 -7
- package/.claude/settings.json +0 -62
- package/.claude/skills/quality-reviewer/SKILL.md +0 -190
- package/.claude/skills/safeword-quality-reviewer/SKILL.md +0 -13
- package/.env.arcade.example +0 -4
- package/.env.example +0 -11
- package/.gitmodules +0 -4
- package/.safeword/SAFEWORD.md +0 -33
- package/.safeword/eslint/eslint-base.mjs +0 -101
- package/.safeword/guides/architecture-guide.md +0 -404
- package/.safeword/guides/code-philosophy.md +0 -174
- package/.safeword/guides/context-files-guide.md +0 -405
- package/.safeword/guides/data-architecture-guide.md +0 -183
- package/.safeword/guides/design-doc-guide.md +0 -165
- package/.safeword/guides/learning-extraction.md +0 -515
- package/.safeword/guides/llm-instruction-design.md +0 -239
- package/.safeword/guides/llm-prompting.md +0 -95
- package/.safeword/guides/tdd-best-practices.md +0 -570
- package/.safeword/guides/test-definitions-guide.md +0 -243
- package/.safeword/guides/testing-methodology.md +0 -573
- package/.safeword/guides/user-story-guide.md +0 -237
- package/.safeword/guides/zombie-process-cleanup.md +0 -214
- package/.safeword/planning/002-user-story-quality-evaluation.md +0 -1840
- package/.safeword/planning/003-langsmith-eval-setup-prompt.md +0 -363
- package/.safeword/planning/004-llm-eval-test-cases.md +0 -3226
- package/.safeword/planning/005-architecture-enforcement-system.md +0 -169
- package/.safeword/planning/006-reactive-fix-prevention-research.md +0 -135
- package/.safeword/planning/011-cli-ux-vision.md +0 -330
- package/.safeword/planning/012-project-structure-cleanup.md +0 -154
- package/.safeword/planning/README.md +0 -39
- package/.safeword/planning/automation-plan-v2.md +0 -1225
- package/.safeword/planning/automation-plan-v3.md +0 -1291
- package/.safeword/planning/automation-plan.md +0 -3058
- package/.safeword/planning/design/005-cli-implementation.md +0 -343
- package/.safeword/planning/design/013-cli-self-contained-templates.md +0 -596
- package/.safeword/planning/design/013a-eslint-plugin-suite.md +0 -256
- package/.safeword/planning/design/013b-implementation-snippets.md +0 -385
- package/.safeword/planning/design/013c-config-isolation-strategy.md +0 -242
- package/.safeword/planning/design/code-philosophy-improvements.md +0 -60
- package/.safeword/planning/mcp-analysis.md +0 -545
- package/.safeword/planning/phase2-subagents-vs-skills-analysis.md +0 -451
- package/.safeword/planning/settings-improvements.md +0 -970
- package/.safeword/planning/test-definitions/005-cli-implementation.md +0 -1301
- package/.safeword/planning/test-definitions/cli-self-contained-templates.md +0 -205
- package/.safeword/planning/user-stories/001-guides-review-user-stories.md +0 -1381
- package/.safeword/planning/user-stories/003-reactive-fix-prevention.md +0 -132
- package/.safeword/planning/user-stories/004-technical-constraints.md +0 -86
- package/.safeword/planning/user-stories/005-cli-implementation.md +0 -311
- package/.safeword/planning/user-stories/cli-self-contained-templates.md +0 -172
- package/.safeword/planning/versioned-distribution.md +0 -740
- package/.safeword/prompts/arch-review.md +0 -43
- package/.safeword/prompts/quality-review.md +0 -11
- package/.safeword/scripts/arch-review.sh +0 -235
- package/.safeword/scripts/check-linting-sync.sh +0 -58
- package/.safeword/scripts/setup-linting.sh +0 -559
- package/.safeword/templates/architecture-template.md +0 -136
- package/.safeword/templates/ci/architecture-check.yml +0 -79
- package/.safeword/templates/design-doc-template.md +0 -127
- package/.safeword/templates/test-definitions-feature.md +0 -100
- package/.safeword/templates/ticket-template.md +0 -74
- package/.safeword/templates/user-stories-template.md +0 -82
- package/.safeword/tickets/001-guides-review-user-stories.md +0 -83
- package/.safeword/tickets/002-architecture-enforcement.md +0 -211
- package/.safeword/tickets/003-reactive-fix-prevention.md +0 -57
- package/.safeword/tickets/004-technical-constraints-in-user-stories.md +0 -39
- package/.safeword/tickets/005-cli-implementation.md +0 -248
- package/.safeword/tickets/006-flesh-out-skills.md +0 -43
- package/.safeword/tickets/007-flesh-out-questioning.md +0 -44
- package/.safeword/tickets/008-upgrade-questioning.md +0 -58
- package/.safeword/tickets/009-naming-conventions.md +0 -41
- package/.safeword/tickets/010-safeword-md-cleanup.md +0 -34
- package/.safeword/tickets/011-cursor-setup.md +0 -86
- package/.safeword/tickets/README.md +0 -73
- package/.safeword/version +0 -1
- package/AGENTS.md +0 -59
- package/CLAUDE.md +0 -12
- package/README.md +0 -347
- package/docs/001-cli-implementation-plan.md +0 -856
- package/docs/elite-dx-implementation-plan.md +0 -1034
- package/framework/README.md +0 -131
- package/framework/mcp/README.md +0 -96
- package/framework/mcp/arcade.sample.json +0 -8
- package/framework/mcp/context7.sample.json +0 -6
- package/framework/mcp/playwright.sample.json +0 -6
- package/framework/scripts/arch-review.sh +0 -235
- package/framework/scripts/check-linting-sync.sh +0 -58
- package/framework/scripts/load-env.sh +0 -49
- package/framework/scripts/setup-claude.sh +0 -223
- package/framework/scripts/setup-linting.sh +0 -559
- package/framework/scripts/setup-quality.sh +0 -477
- package/framework/scripts/setup-safeword.sh +0 -550
- package/framework/templates/ci/architecture-check.yml +0 -78
- package/learnings/ai-sdk-v5-breaking-changes.md +0 -178
- package/learnings/e2e-test-zombie-processes.md +0 -231
- package/learnings/milkdown-crepe-editor-property.md +0 -96
- package/learnings/prosemirror-fragment-traversal.md +0 -119
- package/packages/cli/AGENTS.md +0 -1
- package/packages/cli/ARCHITECTURE.md +0 -279
- package/packages/cli/package.json +0 -51
- package/packages/cli/src/cli.ts +0 -63
- package/packages/cli/src/commands/check.ts +0 -166
- package/packages/cli/src/commands/diff.ts +0 -209
- package/packages/cli/src/commands/reset.ts +0 -190
- package/packages/cli/src/commands/setup.ts +0 -325
- package/packages/cli/src/commands/upgrade.ts +0 -163
- package/packages/cli/src/index.ts +0 -3
- package/packages/cli/src/templates/config.ts +0 -58
- package/packages/cli/src/templates/content.ts +0 -18
- package/packages/cli/src/templates/index.ts +0 -12
- package/packages/cli/src/utils/agents-md.ts +0 -66
- package/packages/cli/src/utils/fs.ts +0 -179
- package/packages/cli/src/utils/git.ts +0 -124
- package/packages/cli/src/utils/hooks.ts +0 -29
- package/packages/cli/src/utils/output.ts +0 -60
- package/packages/cli/src/utils/project-detector.test.ts +0 -185
- package/packages/cli/src/utils/project-detector.ts +0 -44
- package/packages/cli/src/utils/version.ts +0 -28
- package/packages/cli/src/version.ts +0 -6
- package/packages/cli/templates/SAFEWORD.md +0 -776
- package/packages/cli/templates/doc-templates/architecture-template.md +0 -136
- package/packages/cli/templates/doc-templates/design-doc-template.md +0 -134
- package/packages/cli/templates/doc-templates/test-definitions-feature.md +0 -131
- package/packages/cli/templates/doc-templates/ticket-template.md +0 -82
- package/packages/cli/templates/doc-templates/user-stories-template.md +0 -92
- package/packages/cli/templates/guides/architecture-guide.md +0 -423
- package/packages/cli/templates/guides/code-philosophy.md +0 -195
- package/packages/cli/templates/guides/context-files-guide.md +0 -457
- package/packages/cli/templates/guides/data-architecture-guide.md +0 -200
- package/packages/cli/templates/guides/design-doc-guide.md +0 -171
- package/packages/cli/templates/guides/learning-extraction.md +0 -552
- package/packages/cli/templates/guides/llm-instruction-design.md +0 -248
- package/packages/cli/templates/guides/llm-prompting.md +0 -102
- package/packages/cli/templates/guides/tdd-best-practices.md +0 -615
- package/packages/cli/templates/guides/test-definitions-guide.md +0 -334
- package/packages/cli/templates/guides/testing-methodology.md +0 -618
- package/packages/cli/templates/guides/user-story-guide.md +0 -256
- package/packages/cli/templates/guides/zombie-process-cleanup.md +0 -219
- package/packages/cli/templates/hooks/agents-md-check.sh +0 -27
- package/packages/cli/templates/hooks/post-tool.sh +0 -4
- package/packages/cli/templates/hooks/pre-commit.sh +0 -10
- package/packages/cli/templates/prompts/arch-review.md +0 -43
- package/packages/cli/templates/prompts/quality-review.md +0 -10
- package/packages/cli/templates/skills/safeword-quality-reviewer/SKILL.md +0 -207
- package/packages/cli/tests/commands/check.test.ts +0 -129
- package/packages/cli/tests/commands/cli.test.ts +0 -89
- package/packages/cli/tests/commands/diff.test.ts +0 -115
- package/packages/cli/tests/commands/reset.test.ts +0 -310
- package/packages/cli/tests/commands/self-healing.test.ts +0 -170
- package/packages/cli/tests/commands/setup-blocking.test.ts +0 -71
- package/packages/cli/tests/commands/setup-core.test.ts +0 -135
- package/packages/cli/tests/commands/setup-git.test.ts +0 -139
- package/packages/cli/tests/commands/setup-hooks.test.ts +0 -334
- package/packages/cli/tests/commands/setup-linting.test.ts +0 -189
- package/packages/cli/tests/commands/setup-noninteractive.test.ts +0 -80
- package/packages/cli/tests/commands/setup-templates.test.ts +0 -181
- package/packages/cli/tests/commands/upgrade.test.ts +0 -215
- package/packages/cli/tests/helpers.ts +0 -243
- package/packages/cli/tests/npm-package.test.ts +0 -83
- package/packages/cli/tests/technical-constraints.test.ts +0 -96
- package/packages/cli/tsconfig.json +0 -25
- package/packages/cli/tsup.config.ts +0 -11
- package/packages/cli/vitest.config.ts +0 -23
- package/promptfoo.yaml +0 -3270
- /package/{framework → templates}/SAFEWORD.md +0 -0
- /package/{packages/cli/templates → templates}/commands/arch-review.md +0 -0
- /package/{packages/cli/templates → templates}/commands/lint.md +0 -0
- /package/{packages/cli/templates → templates}/commands/quality-review.md +0 -0
- /package/{framework/templates → templates/doc-templates}/architecture-template.md +0 -0
- /package/{framework/templates → templates/doc-templates}/design-doc-template.md +0 -0
- /package/{framework/templates → templates/doc-templates}/test-definitions-feature.md +0 -0
- /package/{framework/templates → templates/doc-templates}/ticket-template.md +0 -0
- /package/{framework/templates → templates/doc-templates}/user-stories-template.md +0 -0
- /package/{framework → templates}/guides/architecture-guide.md +0 -0
- /package/{framework → templates}/guides/code-philosophy.md +0 -0
- /package/{framework → templates}/guides/context-files-guide.md +0 -0
- /package/{framework → templates}/guides/data-architecture-guide.md +0 -0
- /package/{framework → templates}/guides/design-doc-guide.md +0 -0
- /package/{framework → templates}/guides/learning-extraction.md +0 -0
- /package/{framework → templates}/guides/llm-instruction-design.md +0 -0
- /package/{framework → templates}/guides/llm-prompting.md +0 -0
- /package/{framework → templates}/guides/tdd-best-practices.md +0 -0
- /package/{framework → templates}/guides/test-definitions-guide.md +0 -0
- /package/{framework → templates}/guides/testing-methodology.md +0 -0
- /package/{framework → templates}/guides/user-story-guide.md +0 -0
- /package/{framework → templates}/guides/zombie-process-cleanup.md +0 -0
- /package/{packages/cli/templates → templates}/hooks/inject-timestamp.sh +0 -0
- /package/{packages/cli/templates → templates}/lib/common.sh +0 -0
- /package/{packages/cli/templates → templates}/lib/jq-fallback.sh +0 -0
- /package/{packages/cli/templates → templates}/markdownlint.jsonc +0 -0
- /package/{framework → templates}/prompts/arch-review.md +0 -0
- /package/{framework → templates}/prompts/quality-review.md +0 -0
- /package/{framework/skills/quality-reviewer → templates/skills/safeword-quality-reviewer}/SKILL.md +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/setup.ts","../src/utils/project-detector.ts"],"sourcesContent":["/**\n * Setup command - Initialize safeword in a project\n */\n\nimport { join } from 'node:path';\nimport { VERSION } from '../version.js';\nimport {\n exists,\n ensureDir,\n writeFile,\n readJson,\n writeJson,\n updateJson,\n copyDir,\n copyFile,\n getTemplatesDir,\n makeScriptsExecutable,\n} from '../utils/fs.js';\nimport { info, success, warn, error, header, listItem } from '../utils/output.js';\nimport { isGitRepo, installGitHook } from '../utils/git.js';\nimport { detectProjectType } from '../utils/project-detector.js';\nimport { filterOutSafewordHooks } from '../utils/hooks.js';\nimport { ensureAgentsMdLink } from '../utils/agents-md.js';\nimport { PRETTIERRC, getEslintConfig, SETTINGS_HOOKS } from '../templates/index.js';\n\nexport interface SetupOptions {\n yes?: boolean;\n}\n\ninterface PackageJson {\n name?: string;\n version?: string;\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nexport async function setup(options: SetupOptions): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n\n // Check if already configured\n if (exists(safewordDir)) {\n error('Already configured. Run `safeword upgrade` to update.');\n process.exit(1);\n }\n\n // Check for package.json\n const packageJsonPath = join(cwd, 'package.json');\n if (!exists(packageJsonPath)) {\n error('No package.json found. Run this command in a Node.js project.');\n process.exit(1);\n }\n\n const isNonInteractive = options.yes || !process.stdin.isTTY;\n\n header('Safeword Setup');\n info(`Version: ${VERSION}`);\n\n // Track created files for summary\n const created: string[] = [];\n const modified: string[] = [];\n\n try {\n const templatesDir = getTemplatesDir();\n\n // 1. Create .safeword directory structure and copy templates\n info('\\nCreating .safeword directory...');\n\n ensureDir(safewordDir);\n ensureDir(join(safewordDir, 'learnings'));\n ensureDir(join(safewordDir, 'planning', 'user-stories'));\n ensureDir(join(safewordDir, 'planning', 'design'));\n ensureDir(join(safewordDir, 'tickets', 'completed'));\n\n // Copy full SAFEWORD.md from templates\n copyFile(join(templatesDir, 'SAFEWORD.md'), join(safewordDir, 'SAFEWORD.md'));\n writeFile(join(safewordDir, 'version'), VERSION);\n\n // Copy methodology guides\n copyDir(join(templatesDir, 'guides'), join(safewordDir, 'guides'));\n\n // Copy document templates (to 'templates' to match links in SAFEWORD.md)\n copyDir(join(templatesDir, 'doc-templates'), join(safewordDir, 'templates'));\n\n // Copy review prompts\n copyDir(join(templatesDir, 'prompts'), join(safewordDir, 'prompts'));\n\n // Copy lib scripts and make executable\n copyDir(join(templatesDir, 'lib'), join(safewordDir, 'lib'));\n makeScriptsExecutable(join(safewordDir, 'lib'));\n\n // Copy hook scripts and make executable\n copyDir(join(templatesDir, 'hooks'), join(safewordDir, 'hooks'));\n makeScriptsExecutable(join(safewordDir, 'hooks'));\n\n created.push('.safeword/');\n success('Created .safeword directory');\n\n // 2. Handle AGENTS.md\n info('\\nConfiguring AGENTS.md...');\n const agentsMdResult = ensureAgentsMdLink(cwd);\n if (agentsMdResult === 'created') {\n created.push('AGENTS.md');\n success('Created AGENTS.md');\n } else if (agentsMdResult === 'modified') {\n modified.push('AGENTS.md');\n success('Prepended link to AGENTS.md');\n } else {\n info('AGENTS.md already has safeword link');\n }\n\n // 3. Register Claude Code hooks\n info('\\nRegistering Claude Code hooks...');\n\n const claudeDir = join(cwd, '.claude');\n const settingsPath = join(claudeDir, 'settings.json');\n\n ensureDir(claudeDir);\n\n try {\n updateJson<{ hooks?: Record<string, unknown[]> }>(settingsPath, existing => {\n const hooks = existing?.hooks ?? {};\n\n // Merge hooks, preserving existing non-safeword hooks\n for (const [event, newHooks] of Object.entries(SETTINGS_HOOKS)) {\n const existingHooks = (hooks[event] as unknown[]) ?? [];\n const nonSafewordHooks = filterOutSafewordHooks(existingHooks);\n hooks[event] = [...nonSafewordHooks, ...newHooks];\n }\n\n return { ...existing, hooks };\n });\n\n if (exists(settingsPath)) {\n modified.push('.claude/settings.json');\n } else {\n created.push('.claude/settings.json');\n }\n success('Registered hooks in .claude/settings.json');\n } catch (err) {\n error(`Failed to register hooks: ${err instanceof Error ? err.message : 'Unknown error'}`);\n process.exit(1);\n }\n\n // 4. Copy skills\n info('\\nInstalling skills...');\n\n const skillsDir = join(claudeDir, 'skills');\n copyDir(join(templatesDir, 'skills'), skillsDir);\n\n created.push('.claude/skills/safeword-quality-reviewer/');\n success('Installed skills');\n\n // 5. Copy slash commands\n info('\\nInstalling slash commands...');\n\n const commandsDir = join(claudeDir, 'commands');\n copyDir(join(templatesDir, 'commands'), commandsDir);\n\n created.push('.claude/commands/');\n success('Installed slash commands');\n\n // 6. Setup MCP servers\n info('\\nConfiguring MCP servers...');\n\n const mcpConfigPath = join(cwd, '.mcp.json');\n\n updateJson<{ mcpServers?: Record<string, unknown> }>(mcpConfigPath, existing => {\n const mcpServers = existing?.mcpServers ?? {};\n\n // Add safeword MCP servers (context7 and playwright)\n mcpServers.context7 = {\n command: 'npx',\n args: ['-y', '@upstash/context7-mcp@latest'],\n };\n mcpServers.playwright = {\n command: 'npx',\n args: ['@playwright/mcp@latest'],\n };\n\n return { ...existing, mcpServers };\n });\n\n if (exists(mcpConfigPath)) {\n modified.push('.mcp.json');\n } else {\n created.push('.mcp.json');\n }\n success('Configured MCP servers');\n\n // 7. Setup linting\n info('\\nConfiguring linting...');\n\n const packageJson = readJson<PackageJson>(packageJsonPath);\n if (!packageJson) {\n error('Failed to read package.json');\n process.exit(1);\n }\n\n const projectType = detectProjectType(packageJson);\n\n // Create ESLint config\n const eslintConfigPath = join(cwd, 'eslint.config.mjs');\n if (!exists(eslintConfigPath)) {\n writeFile(eslintConfigPath, getEslintConfig(projectType));\n created.push('eslint.config.mjs');\n success('Created eslint.config.mjs');\n } else {\n info('eslint.config.mjs already exists');\n }\n\n // Create Prettier config\n const prettierrcPath = join(cwd, '.prettierrc');\n if (!exists(prettierrcPath)) {\n writeFile(prettierrcPath, PRETTIERRC);\n created.push('.prettierrc');\n success('Created .prettierrc');\n } else {\n info('.prettierrc already exists');\n }\n\n // Create markdownlint config\n const markdownlintPath = join(cwd, '.markdownlint.jsonc');\n if (!exists(markdownlintPath)) {\n copyFile(join(templatesDir, 'markdownlint.jsonc'), markdownlintPath);\n created.push('.markdownlint.jsonc');\n success('Created .markdownlint.jsonc');\n } else {\n info('.markdownlint.jsonc already exists');\n }\n\n // Add scripts to package.json\n try {\n const scripts = packageJson.scripts ?? {};\n let scriptsModified = false;\n\n if (!scripts.lint) {\n scripts.lint = 'eslint .';\n scriptsModified = true;\n }\n\n if (!scripts['lint:md']) {\n scripts['lint:md'] = 'markdownlint-cli2 \"**/*.md\" \"#node_modules\"';\n scriptsModified = true;\n }\n\n if (!scripts.format) {\n scripts.format = 'prettier --write .';\n scriptsModified = true;\n }\n\n if (!scripts['format:check']) {\n scripts['format:check'] = 'prettier --check .';\n scriptsModified = true;\n }\n\n if (scriptsModified) {\n packageJson.scripts = scripts;\n writeJson(packageJsonPath, packageJson);\n modified.push('package.json');\n success('Added lint and format scripts');\n }\n } catch (err) {\n error(\n `Failed to update package.json: ${err instanceof Error ? err.message : 'Unknown error'}`,\n );\n process.exit(1);\n }\n\n // 8. Handle git repository\n info('\\nConfiguring git...');\n\n if (isGitRepo(cwd)) {\n installGitHook(cwd);\n modified.push('.git/hooks/pre-commit');\n success('Installed git pre-commit hook');\n } else if (isNonInteractive) {\n warn('Skipped git initialization (non-interactive mode)');\n warn('Git hooks not installed (no repository)');\n } else {\n // Interactive mode - would prompt here\n // For now, skip in all cases\n warn('Skipped git initialization (no .git directory)');\n warn('Git hooks not installed (no repository)');\n }\n\n // 9. Note about dependencies\n info('\\nNote: Install linting dependencies manually:');\n listItem('npm install -D eslint prettier @eslint/js');\n if (projectType.typescript) {\n listItem('npm install -D typescript-eslint');\n }\n if (projectType.react) {\n listItem('npm install -D eslint-plugin-react eslint-plugin-react-hooks');\n }\n\n // Print summary\n header('Setup Complete');\n\n if (created.length > 0) {\n info('\\nCreated:');\n for (const file of created) {\n listItem(file);\n }\n }\n\n if (modified.length > 0) {\n info('\\nModified:');\n for (const file of modified) {\n listItem(file);\n }\n }\n\n info('\\nNext steps:');\n listItem('Install linting dependencies (see above)');\n listItem('Run `safeword check` to verify setup');\n listItem('Commit the new files to git');\n\n success(`\\nSafeword ${VERSION} installed successfully!`);\n } catch (err) {\n error(`Setup failed: ${err instanceof Error ? err.message : 'Unknown error'}`);\n process.exit(1);\n }\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\nexport interface PackageJson {\n name?: string;\n version?: 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 electron: boolean;\n}\n\n/**\n * Detects project type from package.json contents\n */\nexport function detectProjectType(packageJson: PackageJson): ProjectType {\n const deps = packageJson.dependencies || {};\n const devDeps = packageJson.devDependencies || {};\n const allDeps = { ...deps, ...devDeps };\n\n const hasTypescript = 'typescript' in allDeps;\n const hasReact = 'react' in deps || 'react' in devDeps;\n const hasNextJs = 'next' in deps;\n const hasAstro = 'astro' in deps || 'astro' in devDeps;\n const hasElectron = 'electron' in deps || 'electron' in devDeps;\n\n return {\n typescript: hasTypescript,\n react: hasReact || hasNextJs, // Next.js implies React\n nextjs: hasNextJs,\n astro: hasAstro,\n electron: hasElectron,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compareVersions
|
|
3
|
+
} from "./chunk-W66Z3C5H.js";
|
|
4
|
+
import {
|
|
5
|
+
VERSION
|
|
6
|
+
} from "./chunk-ORQHKDT2.js";
|
|
7
|
+
import {
|
|
8
|
+
SETTINGS_HOOKS,
|
|
9
|
+
ensureAgentsMdLink,
|
|
10
|
+
filterOutSafewordHooks,
|
|
11
|
+
installGitHook,
|
|
12
|
+
isGitRepo
|
|
13
|
+
} from "./chunk-2XWIUEQK.js";
|
|
14
|
+
import {
|
|
15
|
+
copyDir,
|
|
16
|
+
copyFile,
|
|
17
|
+
ensureDir,
|
|
18
|
+
error,
|
|
19
|
+
exists,
|
|
20
|
+
getTemplatesDir,
|
|
21
|
+
header,
|
|
22
|
+
info,
|
|
23
|
+
listItem,
|
|
24
|
+
makeScriptsExecutable,
|
|
25
|
+
readFileSafe,
|
|
26
|
+
success,
|
|
27
|
+
updateJson,
|
|
28
|
+
writeFile
|
|
29
|
+
} from "./chunk-GZRQL3SX.js";
|
|
30
|
+
|
|
31
|
+
// src/commands/upgrade.ts
|
|
32
|
+
import { join } from "path";
|
|
33
|
+
async function upgrade() {
|
|
34
|
+
const cwd = process.cwd();
|
|
35
|
+
const safewordDir = join(cwd, ".safeword");
|
|
36
|
+
if (!exists(safewordDir)) {
|
|
37
|
+
error("Not configured. Run `safeword setup` first.");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
const versionPath = join(safewordDir, "version");
|
|
41
|
+
const projectVersion = readFileSafe(versionPath)?.trim() ?? "0.0.0";
|
|
42
|
+
if (compareVersions(VERSION, projectVersion) < 0) {
|
|
43
|
+
error(`CLI v${VERSION} is older than project v${projectVersion}.`);
|
|
44
|
+
error("Update the CLI first: npm install -g safeword");
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
header("Safeword Upgrade");
|
|
48
|
+
info(`Upgrading from v${projectVersion} to v${VERSION}`);
|
|
49
|
+
const updated = [];
|
|
50
|
+
const unchanged = [];
|
|
51
|
+
try {
|
|
52
|
+
const templatesDir = getTemplatesDir();
|
|
53
|
+
info("\nUpdating .safeword directory...");
|
|
54
|
+
copyFile(join(templatesDir, "SAFEWORD.md"), join(safewordDir, "SAFEWORD.md"));
|
|
55
|
+
writeFile(join(safewordDir, "version"), VERSION);
|
|
56
|
+
updated.push(".safeword/SAFEWORD.md");
|
|
57
|
+
updated.push(".safeword/version");
|
|
58
|
+
copyDir(join(templatesDir, "guides"), join(safewordDir, "guides"));
|
|
59
|
+
copyDir(join(templatesDir, "doc-templates"), join(safewordDir, "templates"));
|
|
60
|
+
copyDir(join(templatesDir, "prompts"), join(safewordDir, "prompts"));
|
|
61
|
+
copyDir(join(templatesDir, "lib"), join(safewordDir, "lib"));
|
|
62
|
+
makeScriptsExecutable(join(safewordDir, "lib"));
|
|
63
|
+
copyDir(join(templatesDir, "hooks"), join(safewordDir, "hooks"));
|
|
64
|
+
makeScriptsExecutable(join(safewordDir, "hooks"));
|
|
65
|
+
updated.push(".safeword/guides/");
|
|
66
|
+
updated.push(".safeword/templates/");
|
|
67
|
+
updated.push(".safeword/prompts/");
|
|
68
|
+
updated.push(".safeword/hooks/");
|
|
69
|
+
success("Updated .safeword directory");
|
|
70
|
+
info("\nVerifying AGENTS.md...");
|
|
71
|
+
const agentsMdResult = ensureAgentsMdLink(cwd);
|
|
72
|
+
if (agentsMdResult === "created") {
|
|
73
|
+
updated.push("AGENTS.md");
|
|
74
|
+
success("Created AGENTS.md");
|
|
75
|
+
} else if (agentsMdResult === "modified") {
|
|
76
|
+
updated.push("AGENTS.md");
|
|
77
|
+
success("Restored link to AGENTS.md");
|
|
78
|
+
} else {
|
|
79
|
+
unchanged.push("AGENTS.md");
|
|
80
|
+
info("AGENTS.md link is present");
|
|
81
|
+
}
|
|
82
|
+
info("\nUpdating Claude Code hooks...");
|
|
83
|
+
const claudeDir = join(cwd, ".claude");
|
|
84
|
+
const settingsPath = join(claudeDir, "settings.json");
|
|
85
|
+
ensureDir(claudeDir);
|
|
86
|
+
updateJson(settingsPath, (existing) => {
|
|
87
|
+
const hooks = existing?.hooks ?? {};
|
|
88
|
+
for (const [event, newHooks] of Object.entries(SETTINGS_HOOKS)) {
|
|
89
|
+
const existingHooks = hooks[event] ?? [];
|
|
90
|
+
const nonSafewordHooks = filterOutSafewordHooks(existingHooks);
|
|
91
|
+
hooks[event] = [...nonSafewordHooks, ...newHooks];
|
|
92
|
+
}
|
|
93
|
+
return { ...existing, hooks };
|
|
94
|
+
});
|
|
95
|
+
updated.push(".claude/settings.json");
|
|
96
|
+
success("Updated hooks in .claude/settings.json");
|
|
97
|
+
info("\nUpdating skills and commands...");
|
|
98
|
+
copyDir(join(templatesDir, "skills"), join(claudeDir, "skills"));
|
|
99
|
+
copyDir(join(templatesDir, "commands"), join(claudeDir, "commands"));
|
|
100
|
+
updated.push(".claude/skills/");
|
|
101
|
+
updated.push(".claude/commands/");
|
|
102
|
+
success("Updated skills and commands");
|
|
103
|
+
if (isGitRepo(cwd)) {
|
|
104
|
+
info("\nUpdating git hooks...");
|
|
105
|
+
installGitHook(cwd);
|
|
106
|
+
updated.push(".git/hooks/pre-commit");
|
|
107
|
+
success("Updated git pre-commit hook");
|
|
108
|
+
}
|
|
109
|
+
header("Upgrade Complete");
|
|
110
|
+
info(`
|
|
111
|
+
Version: v${projectVersion} \u2192 v${VERSION}`);
|
|
112
|
+
if (updated.length > 0) {
|
|
113
|
+
info("\nUpdated:");
|
|
114
|
+
for (const file of updated) {
|
|
115
|
+
listItem(file);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (unchanged.length > 0) {
|
|
119
|
+
info("\nUnchanged:");
|
|
120
|
+
for (const file of unchanged) {
|
|
121
|
+
listItem(file);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
success(`
|
|
125
|
+
Safeword upgraded to v${VERSION}`);
|
|
126
|
+
} catch (err) {
|
|
127
|
+
error(`Upgrade failed: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
export {
|
|
132
|
+
upgrade
|
|
133
|
+
};
|
|
134
|
+
//# sourceMappingURL=upgrade-6AR3DHUV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/upgrade.ts"],"sourcesContent":["/**\n * Upgrade command - Update safeword configuration to latest version\n */\n\nimport { join } from 'node:path';\nimport { VERSION } from '../version.js';\nimport {\n exists,\n ensureDir,\n writeFile,\n readFileSafe,\n updateJson,\n copyDir,\n copyFile,\n getTemplatesDir,\n makeScriptsExecutable,\n} from '../utils/fs.js';\nimport { info, success, error, header, listItem } from '../utils/output.js';\nimport { isGitRepo, installGitHook } from '../utils/git.js';\nimport { compareVersions } from '../utils/version.js';\nimport { filterOutSafewordHooks } from '../utils/hooks.js';\nimport { ensureAgentsMdLink } from '../utils/agents-md.js';\nimport { SETTINGS_HOOKS } from '../templates/index.js';\n\nexport async function upgrade(): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n\n // Check if configured\n if (!exists(safewordDir)) {\n error('Not configured. Run `safeword setup` first.');\n process.exit(1);\n }\n\n // Read project version\n const versionPath = join(safewordDir, 'version');\n const projectVersion = readFileSafe(versionPath)?.trim() ?? '0.0.0';\n\n // Check for downgrade\n if (compareVersions(VERSION, projectVersion) < 0) {\n error(`CLI v${VERSION} is older than project v${projectVersion}.`);\n error('Update the CLI first: npm install -g safeword');\n process.exit(1);\n }\n\n header('Safeword Upgrade');\n info(`Upgrading from v${projectVersion} to v${VERSION}`);\n\n const updated: string[] = [];\n const unchanged: string[] = [];\n\n try {\n const templatesDir = getTemplatesDir();\n\n // 1. Update .safeword directory\n info('\\nUpdating .safeword directory...');\n\n // Update core files from templates\n copyFile(join(templatesDir, 'SAFEWORD.md'), join(safewordDir, 'SAFEWORD.md'));\n writeFile(join(safewordDir, 'version'), VERSION);\n updated.push('.safeword/SAFEWORD.md');\n updated.push('.safeword/version');\n\n // Update guides, templates, prompts from templates\n copyDir(join(templatesDir, 'guides'), join(safewordDir, 'guides'));\n copyDir(join(templatesDir, 'doc-templates'), join(safewordDir, 'templates'));\n copyDir(join(templatesDir, 'prompts'), join(safewordDir, 'prompts'));\n\n // Update lib scripts and make executable\n copyDir(join(templatesDir, 'lib'), join(safewordDir, 'lib'));\n makeScriptsExecutable(join(safewordDir, 'lib'));\n\n // Update hook scripts and make executable\n copyDir(join(templatesDir, 'hooks'), join(safewordDir, 'hooks'));\n makeScriptsExecutable(join(safewordDir, 'hooks'));\n\n updated.push('.safeword/guides/');\n updated.push('.safeword/templates/');\n updated.push('.safeword/prompts/');\n updated.push('.safeword/hooks/');\n success('Updated .safeword directory');\n\n // 2. Verify AGENTS.md link\n info('\\nVerifying AGENTS.md...');\n const agentsMdResult = ensureAgentsMdLink(cwd);\n if (agentsMdResult === 'created') {\n updated.push('AGENTS.md');\n success('Created AGENTS.md');\n } else if (agentsMdResult === 'modified') {\n updated.push('AGENTS.md');\n success('Restored link to AGENTS.md');\n } else {\n unchanged.push('AGENTS.md');\n info('AGENTS.md link is present');\n }\n\n // 3. Update Claude Code hooks\n info('\\nUpdating Claude Code hooks...');\n\n const claudeDir = join(cwd, '.claude');\n const settingsPath = join(claudeDir, 'settings.json');\n\n ensureDir(claudeDir);\n\n updateJson<{ hooks?: Record<string, unknown[]> }>(settingsPath, existing => {\n const hooks = existing?.hooks ?? {};\n\n // Merge hooks, preserving existing non-safeword hooks\n for (const [event, newHooks] of Object.entries(SETTINGS_HOOKS)) {\n const existingHooks = (hooks[event] as unknown[]) ?? [];\n const nonSafewordHooks = filterOutSafewordHooks(existingHooks);\n hooks[event] = [...nonSafewordHooks, ...newHooks];\n }\n\n return { ...existing, hooks };\n });\n\n updated.push('.claude/settings.json');\n success('Updated hooks in .claude/settings.json');\n\n // 4. Update skills and commands\n info('\\nUpdating skills and commands...');\n\n copyDir(join(templatesDir, 'skills'), join(claudeDir, 'skills'));\n copyDir(join(templatesDir, 'commands'), join(claudeDir, 'commands'));\n\n updated.push('.claude/skills/');\n updated.push('.claude/commands/');\n success('Updated skills and commands');\n\n // 5. Update git hooks if repo exists\n if (isGitRepo(cwd)) {\n info('\\nUpdating git hooks...');\n installGitHook(cwd);\n updated.push('.git/hooks/pre-commit');\n success('Updated git pre-commit hook');\n }\n\n // Print summary\n header('Upgrade Complete');\n\n info(`\\nVersion: v${projectVersion} → v${VERSION}`);\n\n if (updated.length > 0) {\n info('\\nUpdated:');\n for (const file of updated) {\n listItem(file);\n }\n }\n\n if (unchanged.length > 0) {\n info('\\nUnchanged:');\n for (const file of unchanged) {\n listItem(file);\n }\n }\n\n success(`\\nSafeword upgraded to v${VERSION}`);\n } catch (err) {\n error(`Upgrade failed: ${err instanceof Error ? err.message : 'Unknown error'}`);\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,SAAS,YAAY;AAoBrB,eAAsB,UAAyB;AAC7C,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,cAAc,KAAK,KAAK,WAAW;AAGzC,MAAI,CAAC,OAAO,WAAW,GAAG;AACxB,UAAM,6CAA6C;AACnD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,cAAc,KAAK,aAAa,SAAS;AAC/C,QAAM,iBAAiB,aAAa,WAAW,GAAG,KAAK,KAAK;AAG5D,MAAI,gBAAgB,SAAS,cAAc,IAAI,GAAG;AAChD,UAAM,QAAQ,OAAO,2BAA2B,cAAc,GAAG;AACjE,UAAM,+CAA+C;AACrD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAO,kBAAkB;AACzB,OAAK,mBAAmB,cAAc,QAAQ,OAAO,EAAE;AAEvD,QAAM,UAAoB,CAAC;AAC3B,QAAM,YAAsB,CAAC;AAE7B,MAAI;AACF,UAAM,eAAe,gBAAgB;AAGrC,SAAK,mCAAmC;AAGxC,aAAS,KAAK,cAAc,aAAa,GAAG,KAAK,aAAa,aAAa,CAAC;AAC5E,cAAU,KAAK,aAAa,SAAS,GAAG,OAAO;AAC/C,YAAQ,KAAK,uBAAuB;AACpC,YAAQ,KAAK,mBAAmB;AAGhC,YAAQ,KAAK,cAAc,QAAQ,GAAG,KAAK,aAAa,QAAQ,CAAC;AACjE,YAAQ,KAAK,cAAc,eAAe,GAAG,KAAK,aAAa,WAAW,CAAC;AAC3E,YAAQ,KAAK,cAAc,SAAS,GAAG,KAAK,aAAa,SAAS,CAAC;AAGnE,YAAQ,KAAK,cAAc,KAAK,GAAG,KAAK,aAAa,KAAK,CAAC;AAC3D,0BAAsB,KAAK,aAAa,KAAK,CAAC;AAG9C,YAAQ,KAAK,cAAc,OAAO,GAAG,KAAK,aAAa,OAAO,CAAC;AAC/D,0BAAsB,KAAK,aAAa,OAAO,CAAC;AAEhD,YAAQ,KAAK,mBAAmB;AAChC,YAAQ,KAAK,sBAAsB;AACnC,YAAQ,KAAK,oBAAoB;AACjC,YAAQ,KAAK,kBAAkB;AAC/B,YAAQ,6BAA6B;AAGrC,SAAK,0BAA0B;AAC/B,UAAM,iBAAiB,mBAAmB,GAAG;AAC7C,QAAI,mBAAmB,WAAW;AAChC,cAAQ,KAAK,WAAW;AACxB,cAAQ,mBAAmB;AAAA,IAC7B,WAAW,mBAAmB,YAAY;AACxC,cAAQ,KAAK,WAAW;AACxB,cAAQ,4BAA4B;AAAA,IACtC,OAAO;AACL,gBAAU,KAAK,WAAW;AAC1B,WAAK,2BAA2B;AAAA,IAClC;AAGA,SAAK,iCAAiC;AAEtC,UAAM,YAAY,KAAK,KAAK,SAAS;AACrC,UAAM,eAAe,KAAK,WAAW,eAAe;AAEpD,cAAU,SAAS;AAEnB,eAAkD,cAAc,cAAY;AAC1E,YAAM,QAAQ,UAAU,SAAS,CAAC;AAGlC,iBAAW,CAAC,OAAO,QAAQ,KAAK,OAAO,QAAQ,cAAc,GAAG;AAC9D,cAAM,gBAAiB,MAAM,KAAK,KAAmB,CAAC;AACtD,cAAM,mBAAmB,uBAAuB,aAAa;AAC7D,cAAM,KAAK,IAAI,CAAC,GAAG,kBAAkB,GAAG,QAAQ;AAAA,MAClD;AAEA,aAAO,EAAE,GAAG,UAAU,MAAM;AAAA,IAC9B,CAAC;AAED,YAAQ,KAAK,uBAAuB;AACpC,YAAQ,wCAAwC;AAGhD,SAAK,mCAAmC;AAExC,YAAQ,KAAK,cAAc,QAAQ,GAAG,KAAK,WAAW,QAAQ,CAAC;AAC/D,YAAQ,KAAK,cAAc,UAAU,GAAG,KAAK,WAAW,UAAU,CAAC;AAEnE,YAAQ,KAAK,iBAAiB;AAC9B,YAAQ,KAAK,mBAAmB;AAChC,YAAQ,6BAA6B;AAGrC,QAAI,UAAU,GAAG,GAAG;AAClB,WAAK,yBAAyB;AAC9B,qBAAe,GAAG;AAClB,cAAQ,KAAK,uBAAuB;AACpC,cAAQ,6BAA6B;AAAA,IACvC;AAGA,WAAO,kBAAkB;AAEzB,SAAK;AAAA,YAAe,cAAc,YAAO,OAAO,EAAE;AAElD,QAAI,QAAQ,SAAS,GAAG;AACtB,WAAK,YAAY;AACjB,iBAAW,QAAQ,SAAS;AAC1B,iBAAS,IAAI;AAAA,MACf;AAAA,IACF;AAEA,QAAI,UAAU,SAAS,GAAG;AACxB,WAAK,cAAc;AACnB,iBAAW,QAAQ,WAAW;AAC5B,iBAAS,IAAI;AAAA,MACf;AAAA,IACF;AAEA,YAAQ;AAAA,wBAA2B,OAAO,EAAE;AAAA,EAC9C,SAAS,KAAK;AACZ,UAAM,mBAAmB,eAAe,QAAQ,IAAI,UAAU,eAAe,EAAE;AAC/E,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,26 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "safeword",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
|
+
"description": "CLI for setting up and managing safeword development environments",
|
|
4
5
|
"type": "module",
|
|
5
|
-
"
|
|
6
|
+
"bin": {
|
|
7
|
+
"safeword": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"templates"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
6
22
|
"scripts": {
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"dev": "tsup --watch",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"test:coverage": "vitest run --coverage",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"lint": "eslint src tests",
|
|
30
|
+
"clean": "rm -rf dist",
|
|
31
|
+
"prepublishOnly": "npm audit --audit-level=high && npm run build && npm test"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"commander": "^12.1.0"
|
|
12
35
|
},
|
|
13
36
|
"devDependencies": {
|
|
14
|
-
"@
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
|
|
37
|
+
"@types/node": "^20.10.0",
|
|
38
|
+
"tsup": "^8.0.0",
|
|
39
|
+
"typescript": "^5.3.0",
|
|
40
|
+
"vitest": "^2.0.0"
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"cli",
|
|
44
|
+
"safeword",
|
|
45
|
+
"developer-experience",
|
|
46
|
+
"linting",
|
|
47
|
+
"claude-code"
|
|
48
|
+
],
|
|
49
|
+
"author": "",
|
|
50
|
+
"license": "MIT"
|
|
26
51
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
Run an architecture review on staged or recent changes.
|
|
2
|
-
|
|
3
|
-
## Steps:
|
|
4
|
-
|
|
5
|
-
1. Get changed files:
|
|
6
|
-
```bash
|
|
7
|
-
git diff --cached --name-only --diff-filter=ACM 2>/dev/null || git diff HEAD~1 --name-only
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
2. Read the project's `ARCHITECTURE.md` if it exists
|
|
11
|
-
|
|
12
|
-
3. Read the changed files
|
|
13
|
-
|
|
14
|
-
4. Review for these issues:
|
|
15
|
-
- **Misplaced logic** - Business rules in wrong layer?
|
|
16
|
-
- **God module** - Too many responsibilities (>10 dependents or >500 lines)?
|
|
17
|
-
- **Leaky abstraction** - Implementation details exposed to callers?
|
|
18
|
-
- **Tight coupling** - Changes would cascade unnecessarily?
|
|
19
|
-
- **Boundary violation** - Import from disallowed layer?
|
|
20
|
-
|
|
21
|
-
5. Return verdict:
|
|
22
|
-
```json
|
|
23
|
-
{
|
|
24
|
-
"issues": [],
|
|
25
|
-
"verdict": "clean | minor | refactor_needed"
|
|
26
|
-
}
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Verdicts:
|
|
30
|
-
- **clean**: No architectural issues
|
|
31
|
-
- **minor**: Small issues, doesn't block commit
|
|
32
|
-
- **refactor_needed**: Address before committing
|
package/.claude/commands/lint.md
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
Trigger a quality review to double-check your work.
|
|
2
|
-
|
|
3
|
-
Execute:
|
|
4
|
-
```bash
|
|
5
|
-
bash .claude/hooks/run-quality-review.sh
|
|
6
|
-
```
|
|
7
|
-
|
|
8
|
-
This will prompt you to:
|
|
9
|
-
- Verify correctness
|
|
10
|
-
- Check elegance
|
|
11
|
-
- Validate against latest docs and best practices
|
|
12
|
-
- Consider asking clarifying questions
|
|
13
|
-
- Think deeply and avoid bloat
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# PostToolUse hook - auto-lint changed files
|
|
3
|
-
input=$(cat)
|
|
4
|
-
file_path=$(echo "$input" | jq -r '.tool_input.file_path // .tool_input.notebook_path // empty')
|
|
5
|
-
[ -n "$file_path" ] && [ -f "$file_path" ] && "$CLAUDE_PROJECT_DIR/.claude/hooks/run-linters.sh" "$file_path"
|
|
6
|
-
exit 0
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Generated by setup-quality.sh v1.0.0
|
|
3
|
-
# To upgrade: Re-run setup script in this project
|
|
4
|
-
#
|
|
5
|
-
# Auto Quality Review Stop Hook
|
|
6
|
-
# Triggers quality review when changes are proposed or made
|
|
7
|
-
# Only runs for projects with SAFEWORD.md or CLAUDE.md (searches upward)
|
|
8
|
-
# Looks for {"proposedChanges": ..., "madeChanges": ...} JSON blob
|
|
9
|
-
|
|
10
|
-
# Debug logging (persistent across reboots, user-specific)
|
|
11
|
-
DEBUG_DIR="$HOME/.cache/claude-hooks"
|
|
12
|
-
mkdir -p "$DEBUG_DIR" 2>/dev/null || DEBUG_DIR="/tmp"
|
|
13
|
-
DEBUG_LOG="$DEBUG_DIR/auto-quality-review-debug.log"
|
|
14
|
-
echo "=== Hook triggered at $(date) ===" >> "$DEBUG_LOG"
|
|
15
|
-
echo "PWD: $PWD" >> "$DEBUG_LOG"
|
|
16
|
-
|
|
17
|
-
# Read hook input from stdin
|
|
18
|
-
input=$(cat)
|
|
19
|
-
echo "Input: $input" >> "$DEBUG_LOG"
|
|
20
|
-
|
|
21
|
-
# Check for project-level SAFEWORD.md or CLAUDE.md
|
|
22
|
-
# Search upward from current directory (supports monorepos)
|
|
23
|
-
current_dir="$PWD"
|
|
24
|
-
echo "Searching for SAFEWORD.md or CLAUDE.md..." >> "$DEBUG_LOG"
|
|
25
|
-
while true; do
|
|
26
|
-
if [ -f "$current_dir/SAFEWORD.md" ] || [ -f "$current_dir/CLAUDE.md" ]; then
|
|
27
|
-
echo "Found context file in: $current_dir" >> "$DEBUG_LOG"
|
|
28
|
-
break # Found context file
|
|
29
|
-
fi
|
|
30
|
-
|
|
31
|
-
# Reached root without finding context file
|
|
32
|
-
if [ "$current_dir" = "/" ]; then
|
|
33
|
-
echo "No SAFEWORD.md or CLAUDE.md found, exiting" >> "$DEBUG_LOG"
|
|
34
|
-
exit 0
|
|
35
|
-
fi
|
|
36
|
-
|
|
37
|
-
current_dir=$(dirname "$current_dir")
|
|
38
|
-
done
|
|
39
|
-
|
|
40
|
-
# Read project config (defaults: enabled=true, ask_questions=true)
|
|
41
|
-
config_file="$current_dir/.auto-quality-review.config"
|
|
42
|
-
enabled=true
|
|
43
|
-
ask_questions=true
|
|
44
|
-
|
|
45
|
-
if [ -f "$config_file" ]; then
|
|
46
|
-
while IFS='=' read -r key value; do
|
|
47
|
-
# Skip empty lines and comments
|
|
48
|
-
[[ -z "$key" || "$key" =~ ^# ]] && continue
|
|
49
|
-
# Trim whitespace
|
|
50
|
-
key=$(echo "$key" | xargs)
|
|
51
|
-
value=$(echo "$value" | xargs)
|
|
52
|
-
|
|
53
|
-
case "$key" in
|
|
54
|
-
enabled)
|
|
55
|
-
enabled="$value"
|
|
56
|
-
;;
|
|
57
|
-
ask_questions)
|
|
58
|
-
ask_questions="$value"
|
|
59
|
-
;;
|
|
60
|
-
esac
|
|
61
|
-
done < "$config_file"
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
# Exit if disabled for this project
|
|
65
|
-
echo "Config: enabled=$enabled, ask_questions=$ask_questions" >> "$DEBUG_LOG"
|
|
66
|
-
if [ "$enabled" != "true" ]; then
|
|
67
|
-
echo "Hook disabled for this project, exiting" >> "$DEBUG_LOG"
|
|
68
|
-
exit 0
|
|
69
|
-
fi
|
|
70
|
-
|
|
71
|
-
# Get transcript path
|
|
72
|
-
if ! transcript_path=$(echo "$input" | jq -r '.transcript_path // empty' 2>/dev/null); then
|
|
73
|
-
echo "ERROR: jq failed to extract transcript_path from hook input." >&2
|
|
74
|
-
echo "ERROR: jq failed to parse input" >> "$DEBUG_LOG"
|
|
75
|
-
exit 2
|
|
76
|
-
fi
|
|
77
|
-
|
|
78
|
-
echo "Transcript path: $transcript_path" >> "$DEBUG_LOG"
|
|
79
|
-
if [ -z "$transcript_path" ] || [ ! -f "$transcript_path" ]; then
|
|
80
|
-
echo "No transcript or file doesn't exist, exiting" >> "$DEBUG_LOG"
|
|
81
|
-
exit 0
|
|
82
|
-
fi
|
|
83
|
-
|
|
84
|
-
# Extract last assistant message from transcript
|
|
85
|
-
# Transcript is JSONL format - each line is a message
|
|
86
|
-
last_assistant_msg=$(grep '"role":"assistant"' "$transcript_path" | tail -1)
|
|
87
|
-
|
|
88
|
-
echo "Last assistant message length: ${#last_assistant_msg}" >> "$DEBUG_LOG"
|
|
89
|
-
if [ -z "$last_assistant_msg" ]; then
|
|
90
|
-
# Normal case: No assistant messages yet
|
|
91
|
-
echo "No assistant messages found, exiting" >> "$DEBUG_LOG"
|
|
92
|
-
exit 0
|
|
93
|
-
fi
|
|
94
|
-
|
|
95
|
-
# Extract the text content from the message
|
|
96
|
-
# Looking for the final JSON blob: {"proposedChanges": ..., "madeChanges": ...}
|
|
97
|
-
if ! msg_text=$(echo "$last_assistant_msg" | jq -r '.message.content[]? | select(.type == "text") | .text' 2>/dev/null); then
|
|
98
|
-
echo "ERROR: jq failed to parse assistant message structure. Transcript format may have changed." >&2
|
|
99
|
-
echo "DEBUG: Message structure: $(echo "$last_assistant_msg" | jq -r '.message.content[].type' 2>/dev/null)" >> "$DEBUG_LOG"
|
|
100
|
-
exit 2
|
|
101
|
-
fi
|
|
102
|
-
|
|
103
|
-
if [ -z "$msg_text" ]; then
|
|
104
|
-
# Normal case: Message has no text content
|
|
105
|
-
exit 0
|
|
106
|
-
fi
|
|
107
|
-
|
|
108
|
-
# Extract the JSON blob from the end of the message
|
|
109
|
-
# Pattern: {"proposedChanges": boolean, "madeChanges": boolean, "askedQuestion": boolean}
|
|
110
|
-
json_blob=$(echo "$msg_text" | grep -oE '\{"proposedChanges":\s*(true|false)\s*,\s*"madeChanges":\s*(true|false)\s*,\s*"askedQuestion":\s*(true|false)\s*\}' | tail -1)
|
|
111
|
-
|
|
112
|
-
echo "JSON blob: $json_blob" >> "$DEBUG_LOG"
|
|
113
|
-
if [ -z "$json_blob" ]; then
|
|
114
|
-
# Check if this looks like a substantive response (>100 chars)
|
|
115
|
-
# If so, remind agent to include JSON payload
|
|
116
|
-
msg_length=${#msg_text}
|
|
117
|
-
echo "No JSON blob found, message length: $msg_length" >> "$DEBUG_LOG"
|
|
118
|
-
|
|
119
|
-
if [ "$msg_length" -gt 100 ]; then
|
|
120
|
-
echo "Substantive response without JSON payload, sending reminder" >> "$DEBUG_LOG"
|
|
121
|
-
echo "Missing required JSON response format. Please include: {\"proposedChanges\": bool, \"madeChanges\": bool, \"askedQuestion\": bool}" >&2
|
|
122
|
-
exit 2
|
|
123
|
-
fi
|
|
124
|
-
|
|
125
|
-
# Short message without JSON is fine (acknowledgments, etc)
|
|
126
|
-
echo "Short message without JSON, exiting" >> "$DEBUG_LOG"
|
|
127
|
-
exit 0
|
|
128
|
-
fi
|
|
129
|
-
|
|
130
|
-
# Parse the boolean values
|
|
131
|
-
if ! proposed_changes=$(echo "$json_blob" | jq -r '.proposedChanges // false' 2>/dev/null); then
|
|
132
|
-
echo "ERROR: jq failed to parse proposedChanges field from JSON blob." >&2
|
|
133
|
-
exit 2
|
|
134
|
-
fi
|
|
135
|
-
|
|
136
|
-
if ! made_changes=$(echo "$json_blob" | jq -r '.madeChanges // false' 2>/dev/null); then
|
|
137
|
-
echo "ERROR: jq failed to parse madeChanges field from JSON blob." >&2
|
|
138
|
-
exit 2
|
|
139
|
-
fi
|
|
140
|
-
|
|
141
|
-
if ! asked_question=$(echo "$json_blob" | jq -r '.askedQuestion // false' 2>/dev/null); then
|
|
142
|
-
echo "ERROR: jq failed to parse askedQuestion field from JSON blob." >&2
|
|
143
|
-
exit 2
|
|
144
|
-
fi
|
|
145
|
-
|
|
146
|
-
# If asked question, skip quality review (waiting for user response)
|
|
147
|
-
echo "proposedChanges=$proposed_changes, madeChanges=$made_changes, askedQuestion=$asked_question" >> "$DEBUG_LOG"
|
|
148
|
-
if [ "$asked_question" = "true" ]; then
|
|
149
|
-
echo "Agent asked question, skipping quality review" >> "$DEBUG_LOG"
|
|
150
|
-
exit 0
|
|
151
|
-
fi
|
|
152
|
-
|
|
153
|
-
# If either proposed or made changes, trigger quality review
|
|
154
|
-
if [ "$proposed_changes" = "true" ] || [ "$made_changes" = "true" ]; then
|
|
155
|
-
# Block and request quality review
|
|
156
|
-
echo "TRIGGERING QUALITY REVIEW" >> "$DEBUG_LOG"
|
|
157
|
-
|
|
158
|
-
# Find the shared quality review script (same directory as this hook)
|
|
159
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
160
|
-
|
|
161
|
-
# Call shared quality review script
|
|
162
|
-
if [ "$ask_questions" = "true" ]; then
|
|
163
|
-
exec "$SCRIPT_DIR/run-quality-review.sh"
|
|
164
|
-
else
|
|
165
|
-
exec "$SCRIPT_DIR/run-quality-review.sh" --no-questions
|
|
166
|
-
fi
|
|
167
|
-
fi
|
|
168
|
-
|
|
169
|
-
# No changes proposed or made - allow continuation
|
|
170
|
-
exit 0
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# SessionStart hook - reminds user to re-run setup if frameworks changed
|
|
3
|
-
[ ! -f ".safeword/eslint/eslint-base.mjs" ] || [ ! -f "package.json" ] && exit 0
|
|
4
|
-
DEPS=$(jq -r '(.dependencies//{}),(.devDependencies//{}) | keys[]' package.json 2>/dev/null || grep -oE '"[^"]+":' package.json | tr -d '":')
|
|
5
|
-
HAS_TS=false; HAS_REACT=false; HAS_ASTRO=false
|
|
6
|
-
{ [ -f "tsconfig.json" ] || echo "$DEPS" | grep -qx "typescript"; } && HAS_TS=true
|
|
7
|
-
echo "$DEPS" | grep -qx "react" && HAS_REACT=true
|
|
8
|
-
echo "$DEPS" | grep -qx "astro" && HAS_ASTRO=true
|
|
9
|
-
CFG_TS=$(grep -q "typescript-eslint" .safeword/eslint/eslint-base.mjs && echo true || echo false)
|
|
10
|
-
CFG_REACT=$(grep -q "@eslint-react" .safeword/eslint/eslint-base.mjs && echo true || echo false)
|
|
11
|
-
CFG_ASTRO=$(grep -q "eslint-plugin-astro" .safeword/eslint/eslint-base.mjs && echo true || echo false)
|
|
12
|
-
MSG=""
|
|
13
|
-
[ "$HAS_TS" != "$CFG_TS" ] && MSG+="TypeScript "
|
|
14
|
-
[ "$HAS_REACT" != "$CFG_REACT" ] && MSG+="React "
|
|
15
|
-
[ "$HAS_ASTRO" != "$CFG_ASTRO" ] && MSG+="Astro "
|
|
16
|
-
[ -n "$MSG" ] && echo "⚠️ ESLint config out of sync (${MSG% }changed). Run: bash .safeword/scripts/setup-linting.sh"
|
|
17
|
-
exit 0
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Inject Timestamp - Notification Hook
|
|
3
|
-
# Outputs current Unix timestamp at session start for Claude's context awareness
|
|
4
|
-
# Helps with accurate ticket timestamps and time-based reasoning
|
|
5
|
-
|
|
6
|
-
echo "Current time: $(date +%s) ($(date -u +%Y-%m-%dT%H:%M:%SZ))"
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Pre-hook: Question-asking protocol
|
|
3
|
-
# Triggers on UserPromptSubmit (20+ chars) to guide clarifying questions
|
|
4
|
-
|
|
5
|
-
cat <<'EOF'
|
|
6
|
-
{
|
|
7
|
-
"hookSpecificOutput": {
|
|
8
|
-
"hookEventName": "UserPromptSubmit",
|
|
9
|
-
"additionalContext": "<question-protocol>Research first. Ask 1-3 questions ONLY about what to build (not how) that only user knows. Skip searchable things. Unsure? Ask.</question-protocol>"
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
EOF
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Shared Linting Script - runs Prettier + ESLint on files
|
|
3
|
-
if [ -n "$CLAUDE_PROJECT_DIR" ]; then cd "$CLAUDE_PROJECT_DIR" || exit 1; fi
|
|
4
|
-
for target in "$@"; do
|
|
5
|
-
[ -e "$target" ] || continue
|
|
6
|
-
npx prettier --write "$target" 2>&1 || true
|
|
7
|
-
npx eslint --fix "$target" 2>&1 || true
|
|
8
|
-
done
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Generated by setup-quality.sh v1.0.0
|
|
3
|
-
# To upgrade: Re-run setup script in this project
|
|
4
|
-
#
|
|
5
|
-
# Shared Quality Review Script
|
|
6
|
-
#
|
|
7
|
-
# Prompts for a quality review with configurable questions.
|
|
8
|
-
# Used by both Stop hooks and manual /quality-review command.
|
|
9
|
-
#
|
|
10
|
-
# Usage:
|
|
11
|
-
# run-quality-review.sh # With questions enabled
|
|
12
|
-
# run-quality-review.sh --no-questions # Without "ask me" prompt
|
|
13
|
-
#
|
|
14
|
-
# Environment variables:
|
|
15
|
-
# CLAUDE_PROJECT_DIR - Project root (for finding config and prompts)
|
|
16
|
-
|
|
17
|
-
# Default: ask questions enabled
|
|
18
|
-
ask_questions=true
|
|
19
|
-
|
|
20
|
-
# Parse command line arguments
|
|
21
|
-
for arg in "$@"; do
|
|
22
|
-
case "$arg" in
|
|
23
|
-
--no-questions)
|
|
24
|
-
ask_questions=false
|
|
25
|
-
;;
|
|
26
|
-
esac
|
|
27
|
-
done
|
|
28
|
-
|
|
29
|
-
# Try to read project config if CLAUDE_PROJECT_DIR is set
|
|
30
|
-
if [ -n "$CLAUDE_PROJECT_DIR" ]; then
|
|
31
|
-
config_file="$CLAUDE_PROJECT_DIR/.auto-quality-review.config"
|
|
32
|
-
|
|
33
|
-
if [ -f "$config_file" ]; then
|
|
34
|
-
while IFS='=' read -r key value; do
|
|
35
|
-
# Skip empty lines and comments
|
|
36
|
-
[[ -z "$key" || "$key" =~ ^# ]] && continue
|
|
37
|
-
# Trim whitespace
|
|
38
|
-
key=$(echo "$key" | xargs)
|
|
39
|
-
value=$(echo "$value" | xargs)
|
|
40
|
-
|
|
41
|
-
case "$key" in
|
|
42
|
-
ask_questions)
|
|
43
|
-
ask_questions="$value"
|
|
44
|
-
;;
|
|
45
|
-
esac
|
|
46
|
-
done < "$config_file"
|
|
47
|
-
fi
|
|
48
|
-
fi
|
|
49
|
-
|
|
50
|
-
# Try to read prompt from .safeword/prompts/quality-review.md
|
|
51
|
-
PROMPT_FILE=""
|
|
52
|
-
if [ -n "$CLAUDE_PROJECT_DIR" ] && [ -f "$CLAUDE_PROJECT_DIR/.safeword/prompts/quality-review.md" ]; then
|
|
53
|
-
PROMPT_FILE="$CLAUDE_PROJECT_DIR/.safeword/prompts/quality-review.md"
|
|
54
|
-
fi
|
|
55
|
-
|
|
56
|
-
if [ -n "$PROMPT_FILE" ]; then
|
|
57
|
-
# Read prompt from file, skip the header line
|
|
58
|
-
tail -n +3 "$PROMPT_FILE" >&2
|
|
59
|
-
else
|
|
60
|
-
# Fallback to hardcoded prompt
|
|
61
|
-
echo "Double check and critique your work just in case." >&2
|
|
62
|
-
echo "" >&2
|
|
63
|
-
echo "- Is it correct?" >&2
|
|
64
|
-
echo "- Is it elegant?" >&2
|
|
65
|
-
echo "- Does it adhere to the latest documentation and best practices for the relevant stack items, UX principles, domain requirements, and testing practices?" >&2
|
|
66
|
-
|
|
67
|
-
if [ "$ask_questions" = "true" ]; then
|
|
68
|
-
echo "- Ask me any non-obvious questions you can't research yourself in the codebase or online." >&2
|
|
69
|
-
fi
|
|
70
|
-
|
|
71
|
-
echo "- Think hard." >&2
|
|
72
|
-
echo "- Avoid bloat." >&2
|
|
73
|
-
fi
|
|
74
|
-
|
|
75
|
-
# Exit with code 2 to block (Stop hook behavior)
|
|
76
|
-
exit 2
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Generated by setup-quality.sh v1.0.0
|
|
3
|
-
# To upgrade: Re-run setup script in this project
|
|
4
|
-
#
|
|
5
|
-
# SAFE WORD Claude Config - SessionStart Hook
|
|
6
|
-
# Injects version and status into Claude's context (silent - not displayed to user)
|
|
7
|
-
|
|
8
|
-
echo "SAFE WORD Claude Config v1.0.0 installed - auto-linting and quality review active"
|
|
9
|
-
|
|
10
|
-
exit 0
|