fix: resolve Setup hook broken reference and warn on macOS-only binary (#1547) (#1696)

* fix: resolve Setup hook broken reference and warn on macOS-only binary (#1547)

On Linux ARM64, the plugin silently failed because:
1. The Setup hook called setup.sh which was removed; the hook exited 127
   (file not found), causing the plugin to appear uninstalled.
2. The committed plugin/scripts/claude-mem binary is macOS arm64 only;
   no warning was shown when it could not execute on other platforms.

Fix the Setup hook to call smart-install.js (the current setup mechanism)
and add checkBinaryPlatformCompatibility() to smart-install.js, which reads
the Mach-O magic bytes from the bundled binary and warns users on non-macOS
platforms that the JS fallback (bun-runner.js + worker-service.cjs) is active.

Generated by Claude Code
Vibe coded by ousamabenyounes

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: close fd in finally block, strengthen smart-install tests to use production function

- Wrap openSync/readSync in checkBinaryPlatformCompatibility with a finally block so the file descriptor is always closed even if readSync throws
- Export checkBinaryPlatformCompatibility with an optional binaryPath param for testability
- Refactor Mach-O detection tests to call the production function directly, mocking process.platform and passing controlled binary paths, eliminating duplicated inline logic
- Strengthen plugin-distribution test to assert at least one command hook exists before checking for smart-install.js, preventing vacuous pass

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Ben Younes
2026-04-15 09:58:29 +02:00
committed by GitHub
parent f97c50bfb9
commit 4538e686ad
4 changed files with 207 additions and 2 deletions
@@ -138,3 +138,38 @@ describe('Plugin Distribution - Build Script Verification', () => {
expect(content).toContain('plugin/.claude-plugin/plugin.json');
});
});
describe('Plugin Distribution - Setup Hook (#1547)', () => {
it('should not reference removed setup.sh in Setup hook', () => {
// setup.sh was removed; the Setup hook must not reference it or the
// plugin silently fails to install on Linux (hooks disabled on setup failure).
const hooksPath = path.join(projectRoot, 'plugin/hooks/hooks.json');
const content = readFileSync(hooksPath, 'utf-8');
expect(content).not.toContain('setup.sh');
});
it('should call smart-install.js in the Setup hook', () => {
const hooksPath = path.join(projectRoot, 'plugin/hooks/hooks.json');
const parsed = JSON.parse(readFileSync(hooksPath, 'utf-8'));
const setupHooks: any[] = parsed.hooks['Setup'] ?? [];
// Collect all command hooks from all matchers
const commandHooks = setupHooks.flatMap((matcher: any) =>
(matcher.hooks ?? []).filter((h: any) => h.type === 'command')
);
// There must be at least one command hook — otherwise the test vacuously passes
expect(commandHooks.length).toBeGreaterThan(0);
// At least one command hook must reference smart-install.js
const smartInstallHooks = commandHooks.filter((h: any) =>
h.command?.includes('smart-install.js')
);
expect(smartInstallHooks.length).toBeGreaterThan(0);
});
it('smart-install.js referenced by Setup hook should exist on disk', () => {
const smartInstallPath = path.join(projectRoot, 'plugin/scripts/smart-install.js');
expect(existsSync(smartInstallPath)).toBe(true);
});
});