Summary
On fresh installs run from Git Bash on Windows, the `node -e` block at `install.sh:222-239` that creates `~/.claude/settings.json` from `core/settings.template.json` crashes with `ENOENT`. On upgrade the block is skipped (`settings.json` already exists), so this only affects fresh Windows installs.
Environment
- Windows 11 Pro 10.0.26200
- Git Bash (bundled with Git for Windows)
- Node.js v24.11.1
Reproduction
```bash
SANDBOX=$(mktemp -d)
export HOME="$SANDBOX"
cd /path/to/sinapsis-upstream
bash install.sh
```
Observed
```
node:fs:440
return binding.readFileUtf8(path, stringToFlags(options.flag));
^
Error: ENOENT: no such file or directory, open 'C:\tmp\tmp.Tz11Lcs5qK.claude\settings.json'
at Object.readFileSync (node:fs:440:20)
at [eval]:1:34
...
```
The installer continues (the error is not fatal because the node block is followed by `echo OK settings.json created`, which runs regardless). `~/.claude/settings.json` is never written, so the user gets no hook wiring on a fresh Windows install.
Root cause
The code block (`install.sh:222-239`):
```bash
node -e '
const fs = require("fs");
const template = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
...
fs.writeFileSync(process.argv[2], JSON.stringify(strip(template), null, 2));
' "$SCRIPT_DIR/core/settings.template.json" "$SETTINGS_FILE"
```
When `node -e 'script' arg1 arg2` runs, `process.argv` layout is `[node, '[eval]', arg1, arg2]` — so `argv[1]` is the eval placeholder, not the template path. The script reads `argv[1]` as if it were the template (gets `[eval]` — treated as literal path that doesn't exist on Unix) or on Windows the path-mangling produces the target `settings.json` path resulting in the ENOENT shown.
Fix
Use `argv[2]` and `argv[3]` instead of `argv[1]` and `argv[2]`. Or (cleaner) bind named args:
```bash
node -e '
const fs = require("fs");
const [,, templatePath, outputPath] = process.argv;
const template = JSON.parse(fs.readFileSync(templatePath, "utf8"));
function strip(obj) { ... }
fs.writeFileSync(outputPath, JSON.stringify(strip(template), null, 2));
' "$SCRIPT_DIR/core/settings.template.json" "$SETTINGS_FILE"
```
Impact
- Fresh install on Windows: silently broken — no hooks wired, Sinapsis never activates.
- Fresh install on Linux/macOS: may also be affected (needs verification) — same argv indexing bug, just different path resolution.
- Upgrade path: unaffected (settings.json already exists, block is skipped).
Discovery
Found while developing PR #9 (Laws tier). The laws tier itself works correctly — this bug is pre-existing and unrelated. Out of scope for PR #9; filing separately.
Suggested priority
P1 / high — fresh install on Windows is a common onboarding path and the failure is invisible to the user (no traceback shown in the installer output's `tail -15`, only surfaces when reading full stderr).
Summary
On fresh installs run from Git Bash on Windows, the `node -e` block at `install.sh:222-239` that creates `~/.claude/settings.json` from `core/settings.template.json` crashes with `ENOENT`. On upgrade the block is skipped (`settings.json` already exists), so this only affects fresh Windows installs.
Environment
Reproduction
```bash
SANDBOX=$(mktemp -d)
export HOME="$SANDBOX"
cd /path/to/sinapsis-upstream
bash install.sh
```
Observed
```
node:fs:440
return binding.readFileUtf8(path, stringToFlags(options.flag));
^
Error: ENOENT: no such file or directory, open 'C:\tmp\tmp.Tz11Lcs5qK.claude\settings.json'
at Object.readFileSync (node:fs:440:20)
at [eval]:1:34
...
```
The installer continues (the error is not fatal because the node block is followed by `echo OK settings.json created`, which runs regardless). `~/.claude/settings.json` is never written, so the user gets no hook wiring on a fresh Windows install.
Root cause
The code block (`install.sh:222-239`):
```bash
node -e '
const fs = require("fs");
const template = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
...
fs.writeFileSync(process.argv[2], JSON.stringify(strip(template), null, 2));
' "$SCRIPT_DIR/core/settings.template.json" "$SETTINGS_FILE"
```
When `node -e 'script' arg1 arg2` runs, `process.argv` layout is `[node, '[eval]', arg1, arg2]` — so `argv[1]` is the eval placeholder, not the template path. The script reads `argv[1]` as if it were the template (gets `[eval]` — treated as literal path that doesn't exist on Unix) or on Windows the path-mangling produces the target `settings.json` path resulting in the ENOENT shown.
Fix
Use `argv[2]` and `argv[3]` instead of `argv[1]` and `argv[2]`. Or (cleaner) bind named args:
```bash
node -e '
const fs = require("fs");
const [,, templatePath, outputPath] = process.argv;
const template = JSON.parse(fs.readFileSync(templatePath, "utf8"));
function strip(obj) { ... }
fs.writeFileSync(outputPath, JSON.stringify(strip(template), null, 2));
' "$SCRIPT_DIR/core/settings.template.json" "$SETTINGS_FILE"
```
Impact
Discovery
Found while developing PR #9 (Laws tier). The laws tier itself works correctly — this bug is pre-existing and unrelated. Out of scope for PR #9; filing separately.
Suggested priority
P1 / high — fresh install on Windows is a common onboarding path and the failure is invisible to the user (no traceback shown in the installer output's `tail -15`, only surfaces when reading full stderr).