fix: extract inline SessionStart bootstrap to separate file (#1035)

Inline `node -e "..."` in hooks.json contained `!` characters (e.g.
`!org.isDirectory()`) that bash history expansion in certain shell
environments would misinterpret, producing syntax errors and the
"SessionStart:startup hook error" banner in the Claude Code CLI header.

Extract the bootstrap logic to `scripts/hooks/session-start-bootstrap.js`
so the shell never sees the JS source. Behaviour is identical: the script
reads stdin, resolves the ECC plugin root via CLAUDE_PLUGIN_ROOT or a set
of well-known fallback paths, then delegates to run-with-flags.js.

Update the test that asserted the old inline pattern to verify the new
file-based approach instead.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Apptah
2026-04-01 05:05:23 +08:00
committed by GitHub
parent fade657338
commit 30ab9e2cd7
3 changed files with 167 additions and 6 deletions

View File

@@ -1958,13 +1958,25 @@ async function runTests() {
const sessionStartHook = hooks.hooks.SessionStart?.[0]?.hooks?.[0];
assert.ok(sessionStartHook, 'Should define a SessionStart hook');
assert.ok(sessionStartHook.command.startsWith('node -e "'), 'SessionStart should use inline node resolver');
assert.ok(sessionStartHook.command.includes('session:start'), 'SessionStart should invoke the session:start profile');
assert.ok(sessionStartHook.command.includes('run-with-flags.js'), 'SessionStart should resolve the runner script');
assert.ok(sessionStartHook.command.includes('CLAUDE_PLUGIN_ROOT'), 'SessionStart should consult CLAUDE_PLUGIN_ROOT');
assert.ok(sessionStartHook.command.includes('plugins'), 'SessionStart should probe known plugin roots');
// The bootstrap was extracted to a standalone file to avoid shell history
// expansion of `!` characters that caused startup hook errors when the
// logic was embedded as an inline `node -e "..."` string.
assert.ok(
sessionStartHook.command.includes('session-start-bootstrap.js'),
'SessionStart should delegate to the extracted bootstrap script'
);
assert.ok(sessionStartHook.command.includes('CLAUDE_PLUGIN_ROOT'), 'SessionStart should use CLAUDE_PLUGIN_ROOT');
assert.ok(!sessionStartHook.command.includes('find '), 'Should not scan arbitrary plugin paths with find');
assert.ok(!sessionStartHook.command.includes('head -n 1'), 'Should not pick the first matching plugin path');
// Verify the bootstrap script itself contains the expected logic
const bootstrapPath = path.join(__dirname, '..', '..', 'scripts', 'hooks', 'session-start-bootstrap.js');
assert.ok(fs.existsSync(bootstrapPath), 'Bootstrap script should exist at scripts/hooks/session-start-bootstrap.js');
const bootstrapSrc = fs.readFileSync(bootstrapPath, 'utf8');
assert.ok(bootstrapSrc.includes('session:start'), 'Bootstrap should invoke the session:start profile');
assert.ok(bootstrapSrc.includes('run-with-flags.js'), 'Bootstrap should resolve the runner script');
assert.ok(bootstrapSrc.includes('CLAUDE_PLUGIN_ROOT'), 'Bootstrap should consult CLAUDE_PLUGIN_ROOT');
assert.ok(bootstrapSrc.includes('plugins'), 'Bootstrap should probe known plugin roots');
})
)
passed++;