Skip to content

fix(installer): use relative paths for hook commands in settings.local.json#549

Closed
riaworks wants to merge 1 commit intoSynkraAI:mainfrom
riaworks:fix/hook-settings-path-simplification
Closed

fix(installer): use relative paths for hook commands in settings.local.json#549
riaworks wants to merge 1 commit intoSynkraAI:mainfrom
riaworks:fix/hook-settings-path-simplification

Conversation

@riaworks
Copy link
Contributor

@riaworks riaworks commented Mar 3, 2026

Summary

  • Replace absolute/platform-specific paths with simple relative paths (node .claude/hooks/<file>) for hook commands in settings.local.json
  • Remove explicit timeout from hook config — Claude Code manages timeouts natively (default 60s); low overrides (10s) caused premature kills on Windows
  • Eliminate Windows-specific workaround ($CLAUDE_PROJECT_DIR bug GH #6023/#5814) since relative paths work cross-platform

Files changed

  • packages/installer/src/wizard/ide-config-generator.js — simplify hook command generation
  • packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js — update assertions

Test plan

  • Unit tests updated to reflect removed timeout field
  • Verify npx aios-core install generates correct settings.local.json on Windows
  • Verify npx aios-core install generates correct settings.local.json on macOS/Linux

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • Refactor

    • Simplified hook configuration by removing timeout defaults; timeouts are now managed by Claude Code.
    • Streamlined Windows-specific path handling for hook commands to use consistent relative paths.
  • Tests

    • Updated test expectations to reflect changes in hook timeout configuration defaults.

…l.json

The installer generated platform-specific hook commands that caused issues:
- Windows: absolute paths with escaped backslashes (machine-dependent, fragile)
- Unix: $CLAUDE_PROJECT_DIR variable (known bugs GH #6023/#5814)

Both approaches led to UserPromptSubmit hook errors in installed projects.

Changes:
- Use relative path `node .claude/hooks/<file>` on all platforms
- Remove hardcoded `timeout: 10` from HOOK_EVENT_MAP and generated settings
  (Claude Code manages hook timeouts natively, each hook has internal safety)
- Remove unused `isWindows` and `hookFilePath` variables
- Update tests to reflect timeout removal

The HOOK_EVENT_MAP event routing (MIS-3.1) remains unchanged — precompact
is correctly mapped to PreCompact, not UserPromptSubmit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Mar 3, 2026

@riaworks is attempting to deploy a commit to the Pedro Valério Lopez's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

Walkthrough

Removed timeout properties from hook configurations (HOOK_EVENT_MAP and DEFAULT_HOOK_CONFIG) and simplified hookCommand path construction from conditional Windows-specific absolute paths to uniform relative paths across all platforms.

Changes

Cohort / File(s) Summary
Hook Configuration Updates
packages/installer/src/wizard/ide-config-generator.js
Removed timeout property from HOOK_EVENT_MAP entries (synapse-engine.cjs, code-intel-pretool.cjs, precompact-session-digest.cjs) and DEFAULT_HOOK_CONFIG. Simplified hookCommand construction to use uniform relative path (node .claude/hooks/<hookFileName>) instead of conditional Windows-based absolute path logic.
Test Expectations
packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js
Updated test assertions to reflect timeout removal, expecting undefined instead of 10 for hook configuration timeout properties.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • SynkraAI/aios-core#524: Directly related—introduced per-hook timeouts and event routing in HOOK_EVENT_MAP and DEFAULT_HOOK_CONFIG, which are now being removed in this PR.

Suggested labels

installer, area: installer, tests

Suggested reviewers

  • oalanicolas
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title focuses on using relative paths for hook commands, which is one aspect of the changes, but omits the primary change of removing timeout configurations that was causing Windows issues.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/installer/src/wizard/ide-config-generator.js`:
- Around line 791-795: hookCommand currently interpolates hookFileName directly
(const hookCommand = `node .claude/hooks/${hookFileName}`), which allows
filenames with spaces or shell metacharacters to slip through; validate
hookFileName against a strict allowlist regex (e.g., /^[A-Za-z0-9._-]+\.cjs$/)
in the code that constructs hookCommand and reject or sanitize anything that
doesn't match, then safely quote or escape the path when composing hookCommand
(e.g., wrap the relative path in single quotes and escape contained single
quotes) so the final string cannot inject shell metacharacters before it is
passed to the Claude Code hook runner.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1fcfd4c and d1f22fd.

📒 Files selected for processing (2)
  • packages/installer/src/wizard/ide-config-generator.js
  • packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js

Comment on lines +791 to 795
// Use relative path — works on all platforms, no $CLAUDE_PROJECT_DIR bugs
// (GH #6023/#5814), no fragile absolute Windows paths with escaped backslashes.
// Claude Code resolves relative paths from the project root (cwd).
const hookCommand = `node .claude/hooks/${hookFileName}`;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# First, let's verify the file exists and check the content around lines 791-795
echo "=== Checking file existence and content ==="
wc -l packages/installer/src/wizard/ide-config-generator.js

echo -e "\n=== Content around lines 791-795 ==="
sed -n '785,805p' packages/installer/src/wizard/ide-config-generator.js

Repository: SynkraAI/aios-core

Length of output: 1069


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for hook filename filtering and validation
echo "=== Searching for hook file filtering and validation ==="
rg -n -B5 -A5 "hookFiles.*filter|hookFileName|\.cjs" packages/installer/src/wizard/ide-config-generator.js | head -100

Repository: SynkraAI/aios-core

Length of output: 3110


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check what happens with hookCommand after it's created
echo "=== Searching for hookCommand usage ==="
rg -n -A10 "const hookCommand" packages/installer/src/wizard/ide-config-generator.js

Repository: SynkraAI/aios-core

Length of output: 614


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for hookCommand usage downstream
echo "=== Looking for hookCommand usage ==="
rg -n "hookCommand" packages/installer/src/wizard/ide-config-generator.js

Repository: SynkraAI/aios-core

Length of output: 203


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Get more context after line 794
echo "=== Content from line 794 to end of function ==="
sed -n '794,850p' packages/installer/src/wizard/ide-config-generator.js

Repository: SynkraAI/aios-core

Length of output: 1871


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if this file uses shell execution functions and how
echo "=== Searching for shell execution patterns ==="
rg -n "exec|spawn|child_process|shell.*true" packages/installer/src/wizard/ide-config-generator.js

Repository: SynkraAI/aios-core

Length of output: 583


Escape and validate hook filenames before composing command

Line 794 interpolates hookFileName directly into a command string without escaping or validation. Although .cjs files are filtered by extension (line 757), this alone is insufficient to prevent shell injection. A filename containing spaces or shell metacharacters (e.g., hook$(whoami).cjs or hook; cmd.cjs) would pass the filter and be embedded in the command string. Even though hookCommand is stored as a string for later execution rather than directly executed here, the command is eventually passed to Claude Code's hook runner, where it may be executed in a shell context.

Harden by validating filenames against an allowlist pattern and safely quoting the path:

-    const hookCommand = `node .claude/hooks/${hookFileName}`;
+    if (!/^[a-zA-Z0-9._-]+\.cjs$/.test(hookFileName)) {
+      continue;
+    }
+    const safeHookPath = JSON.stringify(path.posix.join('.claude', 'hooks', hookFileName));
+    const hookCommand = `node ${safeHookPath}`;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Use relative path — works on all platforms, no $CLAUDE_PROJECT_DIR bugs
// (GH #6023/#5814), no fragile absolute Windows paths with escaped backslashes.
// Claude Code resolves relative paths from the project root (cwd).
const hookCommand = `node .claude/hooks/${hookFileName}`;
// Use relative path — works on all platforms, no $CLAUDE_PROJECT_DIR bugs
// (GH `#6023/`#5814), no fragile absolute Windows paths with escaped backslashes.
// Claude Code resolves relative paths from the project root (cwd).
if (!/^[a-zA-Z0-9._-]+\.cjs$/.test(hookFileName)) {
continue;
}
const safeHookPath = JSON.stringify(path.posix.join('.claude', 'hooks', hookFileName));
const hookCommand = `node ${safeHookPath}`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/installer/src/wizard/ide-config-generator.js` around lines 791 -
795, hookCommand currently interpolates hookFileName directly (const hookCommand
= `node .claude/hooks/${hookFileName}`), which allows filenames with spaces or
shell metacharacters to slip through; validate hookFileName against a strict
allowlist regex (e.g., /^[A-Za-z0-9._-]+\.cjs$/) in the code that constructs
hookCommand and reject or sanitize anything that doesn't match, then safely
quote or escape the path when composing hookCommand (e.g., wrap the relative
path in single quotes and escape contained single quotes) so the final string
cannot inject shell metacharacters before it is passed to the Claude Code hook
runner.

@codecov
Copy link

codecov bot commented Mar 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@riaworks
Copy link
Contributor Author

riaworks commented Mar 4, 2026

Superseded by #551 which includes the complete hook fix (8 bugs resolved, not just the generator).

@riaworks riaworks closed this Mar 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant