From 18c90a7a174555d61e578825f6031fd02ba0135f Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Sun, 12 Apr 2026 23:29:45 -0700 Subject: [PATCH] fix: document supported claude hook install path --- README.md | 20 +++++- hooks/README.md | 16 +++++ scripts/install-apply.js | 2 +- tests/scripts/install-ps1.test.js | 11 +++ tests/scripts/install-sh.test.js | 9 +++ .../scripts/manual-hook-install-docs.test.js | 71 +++++++++++++++++++ 6 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 tests/scripts/manual-hook-install-docs.test.js diff --git a/README.md b/README.md index 4172e48a..b1b31e50 100644 --- a/README.md +++ b/README.md @@ -703,12 +703,28 @@ mkdir -p ~/.claude/commands cp everything-claude-code/commands/*.md ~/.claude/commands/ ``` -#### Add hooks to settings.json +#### Install hooks -Manual install only: copy the hooks from `hooks/hooks.json` to your `~/.claude/settings.json` if you are not installing ECC as a Claude plugin. +Do not copy the raw repo `hooks/hooks.json` into `~/.claude/settings.json` or `~/.claude/hooks/hooks.json`. That file is plugin/repo-oriented and still contains `${CLAUDE_PLUGIN_ROOT}` placeholders, so raw copying is not a supported manual install path. + +Use the installer to install only the Claude hook runtime so command paths are rewritten correctly: + +```bash +# macOS / Linux +bash ./install.sh --target claude --modules hooks-runtime +``` + +```powershell +# Windows PowerShell +pwsh -File .\install.ps1 --target claude --modules hooks-runtime +``` + +That writes resolved hooks to `~/.claude/hooks/hooks.json` and leaves any existing `~/.claude/settings.json` untouched. If you installed ECC via `/plugin install`, do not copy those hooks into `settings.json`. Claude Code v2.1+ already auto-loads plugin `hooks/hooks.json`, and duplicating them in `settings.json` causes duplicate execution and `${CLAUDE_PLUGIN_ROOT}` resolution failures. +Windows note: the Claude config directory is `%USERPROFILE%\\.claude`, not `~/claude`. + #### Configure MCPs Copy desired MCP server definitions from `mcp-configs/mcp-servers.json` into your official Claude Code config in `~/.claude/settings.json`, or into a project-scoped `.mcp.json` if you want repo-local MCP access. diff --git a/hooks/README.md b/hooks/README.md index 3305bb40..5382dd17 100644 --- a/hooks/README.md +++ b/hooks/README.md @@ -16,6 +16,22 @@ User request → Claude picks a tool → PreToolUse hook runs → Tool executes ## Hooks in This Plugin +## Installing These Hooks Manually + +For Claude Code manual installs, do not paste the raw repo `hooks.json` into `~/.claude/settings.json` or copy it directly into `~/.claude/hooks/hooks.json`. The checked-in file still contains `${CLAUDE_PLUGIN_ROOT}` placeholders and is meant to be installed through the ECC installer or loaded as a plugin. + +Use the installer instead so hook commands are rewritten against your actual Claude root: + +```bash +bash ./install.sh --target claude --modules hooks-runtime +``` + +```powershell +pwsh -File .\install.ps1 --target claude --modules hooks-runtime +``` + +That installs resolved hooks to `~/.claude/hooks/hooks.json`. On Windows, the Claude config root is `%USERPROFILE%\\.claude`. + ### PreToolUse Hooks | Hook | Matcher | Behavior | Exit Code | diff --git a/scripts/install-apply.js b/scripts/install-apply.js index e082c3bb..57d5e775 100755 --- a/scripts/install-apply.js +++ b/scripts/install-apply.js @@ -27,7 +27,7 @@ Usage: install.sh [--target <${LEGACY_INSTALL_TARGETS.join('|')}>] [--dry-run] [ install.sh [--dry-run] [--json] --config Targets: - claude (default) - Install rules to ~/.claude/rules/ + claude (default) - Install ECC into ~/.claude/ (hooks, commands, agents, rules, skills) cursor - Install rules, hooks, and bundled Cursor configs to ./.cursor/ antigravity - Install rules, workflows, skills, and agents to ./.agent/ diff --git a/tests/scripts/install-ps1.test.js b/tests/scripts/install-ps1.test.js index d5e1d077..3b759c6b 100644 --- a/tests/scripts/install-ps1.test.js +++ b/tests/scripts/install-ps1.test.js @@ -110,6 +110,17 @@ function runTests() { } })) passed++; else failed++; + if (!powerShellCommand) { + console.log(' - skipped help text test; PowerShell is not available in PATH'); + } else if (test('exposes the corrected Claude target help text', () => { + const result = run(powerShellCommand, ['--help']); + assert.strictEqual(result.code, 0, result.stderr); + assert.ok( + result.stdout.includes('claude (default) - Install ECC into ~/.claude/'), + 'help text should describe the Claude target as a full ~/.claude install surface' + ); + })) passed++; else failed++; + console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); process.exit(failed > 0 ? 1 : 0); } diff --git a/tests/scripts/install-sh.test.js b/tests/scripts/install-sh.test.js index 0ed881b9..b53f98bb 100644 --- a/tests/scripts/install-sh.test.js +++ b/tests/scripts/install-sh.test.js @@ -86,6 +86,15 @@ function runTests() { } })) passed++; else failed++; + if (test('exposes the corrected Claude target help text', () => { + const result = run(['--help']); + assert.strictEqual(result.code, 0, result.stderr); + assert.ok( + result.stdout.includes('claude (default) - Install ECC into ~/.claude/'), + 'help text should describe the Claude target as a full ~/.claude install surface' + ); + })) passed++; else failed++; + console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); process.exit(failed > 0 ? 1 : 0); } diff --git a/tests/scripts/manual-hook-install-docs.test.js b/tests/scripts/manual-hook-install-docs.test.js new file mode 100644 index 00000000..271c5d3f --- /dev/null +++ b/tests/scripts/manual-hook-install-docs.test.js @@ -0,0 +1,71 @@ +/** + * Regression coverage for supported manual Claude hook installation guidance. + */ + +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const README = path.join(__dirname, '..', '..', 'README.md'); +const HOOKS_README = path.join(__dirname, '..', '..', 'hooks', 'README.md'); + +function test(name, fn) { + try { + fn(); + console.log(` \u2713 ${name}`); + return true; + } catch (error) { + console.log(` \u2717 ${name}`); + console.log(` Error: ${error.message}`); + return false; + } +} + +function runTests() { + console.log('\n=== Testing manual hook install docs ===\n'); + + let passed = 0; + let failed = 0; + + const readme = fs.readFileSync(README, 'utf8'); + const hooksReadme = fs.readFileSync(HOOKS_README, 'utf8'); + + if (test('README warns against raw hook file copying', () => { + assert.ok( + readme.includes('Do not copy the raw repo `hooks/hooks.json` into `~/.claude/settings.json` or `~/.claude/hooks/hooks.json`'), + 'README should warn against unsupported raw hook copying' + ); + assert.ok( + readme.includes('bash ./install.sh --target claude --modules hooks-runtime'), + 'README should document the supported Bash hook install path' + ); + assert.ok( + readme.includes('pwsh -File .\\install.ps1 --target claude --modules hooks-runtime'), + 'README should document the supported PowerShell hook install path' + ); + assert.ok( + readme.includes('%USERPROFILE%\\\\.claude'), + 'README should call out the correct Windows Claude config root' + ); + })) passed++; else failed++; + + if (test('hooks/README mirrors supported manual install guidance', () => { + assert.ok( + hooksReadme.includes('do not paste the raw repo `hooks.json` into `~/.claude/settings.json` or copy it directly into `~/.claude/hooks/hooks.json`'), + 'hooks/README should warn against unsupported raw hook copying' + ); + assert.ok( + hooksReadme.includes('bash ./install.sh --target claude --modules hooks-runtime'), + 'hooks/README should document the supported Bash hook install path' + ); + assert.ok( + hooksReadme.includes('pwsh -File .\\install.ps1 --target claude --modules hooks-runtime'), + 'hooks/README should document the supported PowerShell hook install path' + ); + })) passed++; else failed++; + + console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); + process.exit(failed > 0 ? 1 : 0); +} + +runTests();