From 7d5c60d291b119b62eecb6b335a7a9397eaac82f Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Wed, 19 Aug 2026 09:28:28 -0700 Subject: [PATCH 1/2] Remove stale Claude MultiEdit matcher Claude Code now folds multi-edit behavior into Edit, so keep generated and repaired hook manifests aligned with the current Edit and Write tools. Grok keeps its compatibility matcher unchanged. AI assistance was used to implement and validate this change. --- .claude/settings.json | 4 ++-- plugin/hooks/hooks.json | 2 +- scripts/lib/transformers/hooks.js | 6 +++--- skill/reference/hooks.md | 2 +- skill/scripts/hook-admin.mjs | 4 ++-- tests/hook-build.test.mjs | 5 +++-- tests/hook.test.mjs | 4 ++++ 7 files changed, 16 insertions(+), 11 deletions(-) diff --git a/.claude/settings.json b/.claude/settings.json index 3fd3a722e..e7b27c9ea 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,9 +1,9 @@ { - "description": "Impeccable design detector: immediate-tier checks after Edit/Write/MultiEdit on UI files, full-rule deep pass on Stop.", + "description": "Impeccable design detector: immediate-tier checks after Edit/Write on UI files, full-rule deep pass on Stop.", "hooks": { "PostToolUse": [ { - "matcher": "Edit|Write|MultiEdit", + "matcher": "Edit|Write", "hooks": [ { "type": "command", diff --git a/plugin/hooks/hooks.json b/plugin/hooks/hooks.json index ae71a3106..ca87389b1 100644 --- a/plugin/hooks/hooks.json +++ b/plugin/hooks/hooks.json @@ -2,7 +2,7 @@ "hooks": { "PostToolUse": [ { - "matcher": "Edit|Write|MultiEdit", + "matcher": "Edit|Write", "hooks": [ { "type": "command", diff --git a/scripts/lib/transformers/hooks.js b/scripts/lib/transformers/hooks.js index e0fc046d6..51828a56b 100644 --- a/scripts/lib/transformers/hooks.js +++ b/scripts/lib/transformers/hooks.js @@ -137,9 +137,9 @@ const GROK_PROJECT_HOOK = '.grok/skills/impeccable/scripts/hook.mjs'; export function buildClaudeSettingsManifest() { return { - description: 'Impeccable design detector: immediate-tier checks after Edit/Write/MultiEdit on UI files, full-rule deep pass on Stop.', + description: 'Impeccable design detector: immediate-tier checks after Edit/Write on UI files, full-rule deep pass on Stop.', hooks: buildClaudeCompatibleHooks( - 'Edit|Write|MultiEdit', + 'Edit|Write', CLAUDE_PROJECT_HOOK, SYSTEM_MESSAGE_NOTICE, ), @@ -155,7 +155,7 @@ export function buildClaudeSettingsManifest() { export function buildClaudePluginHooksManifest() { return { hooks: buildClaudeCompatibleHooks( - 'Edit|Write|MultiEdit', + 'Edit|Write', CLAUDE_PLUGIN_HOOK, SYSTEM_MESSAGE_NOTICE, ), diff --git a/skill/reference/hooks.md b/skill/reference/hooks.md index d6df929de..e990b1641 100644 --- a/skill/reference/hooks.md +++ b/skill/reference/hooks.md @@ -44,7 +44,7 @@ The first argument is the action. Defaults to `status`. ``` 3. If `` is `off`, follow up with a one-line note: "Done. New edits will not trigger the design hook in this project until you run `{{command_prefix}}impeccable hooks on`." -4. If `` is `on`, follow up with: "Done. The design hook will fire after the next Edit/Write/MultiEdit on a UI file." +4. If `` is `on`, follow up with: "Done. The design hook will fire after the next Edit/Write on a UI file." 5. If `` is `ignore-value`, `ignore-file`, or `ignore-rule`, just print the script output. The default scope is shared `.impeccable/config.json`; add `--local` only when the user explicitly asks for a private exception. 6. If `` is `status`, just print the script output. Do not add commentary unless the user asked a follow-up question. diff --git a/skill/scripts/hook-admin.mjs b/skill/scripts/hook-admin.mjs index e8d9e2ada..0d8cbaf94 100644 --- a/skill/scripts/hook-admin.mjs +++ b/skill/scripts/hook-admin.mjs @@ -75,11 +75,11 @@ const HOOK_MANIFEST_TARGETS = [ destRel: '.claude/settings.local.json', sharedDestRel: '.claude/settings.json', manifest: () => ({ - description: 'Impeccable design detector: immediate-tier checks after Edit/Write/MultiEdit on UI files, full-rule deep pass on Stop.', + description: 'Impeccable design detector: immediate-tier checks after Edit/Write on UI files, full-rule deep pass on Stop.', hooks: { PostToolUse: [ { - matcher: 'Edit|Write|MultiEdit', + matcher: 'Edit|Write', hooks: [ { type: 'command', diff --git a/tests/hook-build.test.mjs b/tests/hook-build.test.mjs index 0928bde25..e82987979 100644 --- a/tests/hook-build.test.mjs +++ b/tests/hook-build.test.mjs @@ -72,7 +72,8 @@ describe('hook manifest builders', () => { const group = manifest.hooks.PostToolUse[0]; const handler = group.hooks[0]; - assert.equal(group.matcher, 'Edit|Write|MultiEdit'); + assert.equal(group.matcher, 'Edit|Write'); + assert.doesNotMatch(manifest.description, /MultiEdit/); assert.equal(handler.type, 'command'); assert.equal(handler.timeout, 5); assert.equal(handler.statusMessage, 'Checking UI changes'); @@ -356,7 +357,7 @@ describe('generated hook artifacts in repo', () => { assert.equal(manifest.description, undefined); const handler = manifest.hooks.PostToolUse[0].hooks[0]; - assert.equal(manifest.hooks.PostToolUse[0].matcher, 'Edit|Write|MultiEdit'); + assert.equal(manifest.hooks.PostToolUse[0].matcher, 'Edit|Write'); expectCommand(handler.command, 'skills/impeccable/scripts/hook.mjs'); // Resolves relative to the installed plugin, not a `.claude/skills/` layout. assert.ok(handler.command.includes('${CLAUDE_PLUGIN_ROOT}'), diff --git a/tests/hook.test.mjs b/tests/hook.test.mjs index 412c04ccb..1a3d08e57 100644 --- a/tests/hook.test.mjs +++ b/tests/hook.test.mjs @@ -947,6 +947,10 @@ describe('hook-admin.mjs', () => { // impeccable entry must have been stripped, not accumulated. assert.equal(claude.split('skills/impeccable/scripts/hook.mjs').length - 1, 2); assert.match(claude, /"Stop"/); + const claudeManifest = JSON.parse(claude); + const impeccableGroup = claudeManifest.hooks.PostToolUse.find((group) => + group.hooks?.some((hook) => hook.command?.includes('skills/impeccable/scripts/hook.mjs'))); + assert.equal(impeccableGroup.matcher, 'Edit|Write'); const codex = fs.readFileSync(path.join(cwd, '.codex', 'hooks.json'), 'utf-8'); assert.match(codex, /\.agents\/skills\/impeccable\/scripts\/hook\.mjs/); From 611147a333ab309c8d6eba634028ac4c30b02129 Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Wed, 19 Aug 2026 10:04:14 -0700 Subject: [PATCH 2/2] Sync marketplace Claude hook repair Keep the committed marketplace repair script aligned with Claude Code's supported Edit and Write tools, and strengthen regression coverage after automated review.\n\nThis change was prepared with AI assistance under maintainer authorization. --- plugin/skills/impeccable/reference/hooks.md | 2 +- plugin/skills/impeccable/scripts/hook-admin.mjs | 4 ++-- tests/hook-build.test.mjs | 9 +++++++++ tests/hook.test.mjs | 1 + 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/plugin/skills/impeccable/reference/hooks.md b/plugin/skills/impeccable/reference/hooks.md index 28fa8b424..0cd15c91d 100644 --- a/plugin/skills/impeccable/reference/hooks.md +++ b/plugin/skills/impeccable/reference/hooks.md @@ -44,7 +44,7 @@ The first argument is the action. Defaults to `status`. ``` 3. If `` is `off`, follow up with a one-line note: "Done. New edits will not trigger the design hook in this project until you run `/impeccable hooks on`." -4. If `` is `on`, follow up with: "Done. The design hook will fire after the next Edit/Write/MultiEdit on a UI file." +4. If `` is `on`, follow up with: "Done. The design hook will fire after the next Edit/Write on a UI file." 5. If `` is `ignore-value`, `ignore-file`, or `ignore-rule`, just print the script output. The default scope is shared `.impeccable/config.json`; add `--local` only when the user explicitly asks for a private exception. 6. If `` is `status`, just print the script output. Do not add commentary unless the user asked a follow-up question. diff --git a/plugin/skills/impeccable/scripts/hook-admin.mjs b/plugin/skills/impeccable/scripts/hook-admin.mjs index e8d9e2ada..0d8cbaf94 100644 --- a/plugin/skills/impeccable/scripts/hook-admin.mjs +++ b/plugin/skills/impeccable/scripts/hook-admin.mjs @@ -75,11 +75,11 @@ const HOOK_MANIFEST_TARGETS = [ destRel: '.claude/settings.local.json', sharedDestRel: '.claude/settings.json', manifest: () => ({ - description: 'Impeccable design detector: immediate-tier checks after Edit/Write/MultiEdit on UI files, full-rule deep pass on Stop.', + description: 'Impeccable design detector: immediate-tier checks after Edit/Write on UI files, full-rule deep pass on Stop.', hooks: { PostToolUse: [ { - matcher: 'Edit|Write|MultiEdit', + matcher: 'Edit|Write', hooks: [ { type: 'command', diff --git a/tests/hook-build.test.mjs b/tests/hook-build.test.mjs index e82987979..11ad34cbb 100644 --- a/tests/hook-build.test.mjs +++ b/tests/hook-build.test.mjs @@ -376,6 +376,15 @@ describe('generated hook artifacts in repo', () => { assert.ok(fs.existsSync(path.join(REPO_ROOT, 'plugin/skills/impeccable/scripts/hook-lib.mjs'))); }); + it('keeps the marketplace hook repair matcher aligned with Claude Code', () => { + const hookAdmin = fs.readFileSync( + path.join(REPO_ROOT, 'plugin/skills/impeccable/scripts/hook-admin.mjs'), + 'utf8', + ); + assert.match(hookAdmin, /matcher: 'Edit\|Write'/); + assert.doesNotMatch(hookAdmin, /matcher: 'Edit\|Write\|MultiEdit'/); + }); + it('generated hook runtime can import the bundled detector', async () => { for (const scriptDir of [ '.claude/skills/impeccable/scripts', diff --git a/tests/hook.test.mjs b/tests/hook.test.mjs index 1a3d08e57..cfcbf26dc 100644 --- a/tests/hook.test.mjs +++ b/tests/hook.test.mjs @@ -950,6 +950,7 @@ describe('hook-admin.mjs', () => { const claudeManifest = JSON.parse(claude); const impeccableGroup = claudeManifest.hooks.PostToolUse.find((group) => group.hooks?.some((hook) => hook.command?.includes('skills/impeccable/scripts/hook.mjs'))); + assert.ok(impeccableGroup, 'repaired Claude settings should contain the Impeccable PostToolUse group'); assert.equal(impeccableGroup.matcher, 'Edit|Write'); const codex = fs.readFileSync(path.join(cwd, '.codex', 'hooks.json'), 'utf-8');