From b749f5d7720d26c6ea085707565d77dc72f694a4 Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Sun, 12 Apr 2026 22:47:25 -0700 Subject: [PATCH] fix: clean up hook install docs and tests --- docs/zh-TW/README.md | 4 +- tests/scripts/install-apply.test.js | 66 +++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/docs/zh-TW/README.md b/docs/zh-TW/README.md index fc6f76e4..bb0e3360 100644 --- a/docs/zh-TW/README.md +++ b/docs/zh-TW/README.md @@ -318,7 +318,9 @@ cp -r everything-claude-code/skills/* ~/.claude/skills/ #### 將鉤子新增到 settings.json -將 `hooks/hooks.json` 中的鉤子複製到您的 `~/.claude/settings.json`。 +僅在手動安裝時,才將 `hooks/hooks.json` 中的鉤子複製到您的 `~/.claude/settings.json`。 + +如果您是透過 `/plugin install` 安裝 ECC,請不要再把這些鉤子複製到 `settings.json`。Claude Code v2.1+ 會自動載入外掛中的 `hooks/hooks.json`,重複註冊會導致重複執行以及 `${CLAUDE_PLUGIN_ROOT}` 無法解析。 #### 設定 MCP diff --git a/tests/scripts/install-apply.test.js b/tests/scripts/install-apply.test.js index b9eb0033..7fb184f0 100644 --- a/tests/scripts/install-apply.test.js +++ b/tests/scripts/install-apply.test.js @@ -580,26 +580,64 @@ function runTests() { })) passed++; else failed++; if (test('fails when source hooks.json root is not an object before copying files', () => { - const homeDir = createTempDir('install-apply-home-'); - const projectDir = createTempDir('install-apply-project-'); - const sourceHooksPath = path.join(REPO_ROOT, 'hooks', 'hooks.json'); - const originalHooks = fs.readFileSync(sourceHooksPath, 'utf8'); + const tempDir = createTempDir('install-apply-invalid-hooks-'); + const targetRoot = path.join(tempDir, '.claude'); + const installStatePath = path.join(targetRoot, 'ecc', 'install-state.json'); + const sourceHooksPath = path.join(tempDir, 'hooks.json'); try { fs.writeFileSync(sourceHooksPath, '[]\n'); - const result = run(['--profile', 'core'], { cwd: projectDir, homeDir }); - assert.strictEqual(result.code, 1); - assert.ok(result.stderr.includes('Invalid hooks config at')); - assert.ok(result.stderr.includes('expected a JSON object')); + assert.throws(() => { + applyInstallPlan({ + targetRoot, + installStatePath, + statePreview: { + schemaVersion: 'ecc.install.v1', + installedAt: new Date().toISOString(), + target: { + id: 'claude-home', + kind: 'home', + root: targetRoot, + installStatePath, + }, + request: { + profile: 'core', + modules: [], + includeComponents: [], + excludeComponents: [], + legacyLanguages: [], + legacyMode: false, + }, + resolution: { + selectedModules: ['hooks-runtime'], + skippedModules: [], + }, + source: { + repoVersion: null, + repoCommit: null, + manifestVersion: 1, + }, + operations: [], + }, + adapter: { target: 'claude' }, + operations: [{ + kind: 'copy-file', + moduleId: 'hooks-runtime', + sourcePath: sourceHooksPath, + sourceRelativePath: 'hooks/hooks.json', + destinationPath: path.join(targetRoot, 'hooks', 'hooks.json'), + strategy: 'preserve-relative-path', + ownership: 'managed', + scaffoldOnly: false, + }], + }); + }, /Invalid hooks config at .*expected a JSON object/); - const claudeRoot = path.join(homeDir, '.claude'); - assert.ok(!fs.existsSync(path.join(claudeRoot, 'hooks', 'hooks.json')), 'hooks.json should not be copied when source hooks are invalid'); - assert.ok(!fs.existsSync(path.join(claudeRoot, 'ecc', 'install-state.json')), 'install state should not be written when source hooks are invalid'); + assert.ok(!fs.existsSync(path.join(targetRoot, 'hooks', 'hooks.json')), 'hooks.json should not be copied when source hooks are invalid'); + assert.ok(!fs.existsSync(installStatePath), 'install state should not be written when source hooks are invalid'); } finally { - fs.writeFileSync(sourceHooksPath, originalHooks); - cleanup(homeDir); - cleanup(projectDir); + cleanup(tempDir); } })) passed++; else failed++;