mirror of
https://github.com/JuliusBrussee/caveman.git
synced 2026-09-14 18:02:35 +08:00
5429b5da47
Whole-repo review of the surfaces that mutate a user's agent config, plus a cross-platform sweep focused on Windows. Hooks (Windows): - caveman-mode-tracker finished its work in ~50ms then blocked until the host killed it at the 5s budget: the 'data' listener REFERENCES the stdin handle, so pause() alone keeps the event loop alive until the host closes the write end, and on Windows that close lags arbitrarily. Reproduced off-Windows. This is the shape of #819. Fixed with process.stdin.unref(). - caveman-activate used readFileSync(0), which blocks INSIDE the read syscall until EOF — no deadline can interrupt it. The #729/#833 fix had been applied to the tracker and never to its sibling. Rewritten event-driven, acting on the first complete payload, with a watchdog so a payload that never arrives still activates instead of forfeiting the session. - hookCommand emitted PowerShell call-operator syntax into settings.json, but Claude Code's Windows hook shell is Git Bash, which rejects it outright: `syntax error near unexpected token '&'`. Every standalone Windows install had two dead hooks (#835). Both emitters now route through one bash-safe shape. - activate now threads the payload cwd, so a checked-in .caveman.json defaultMode "off" is honored for the session's directory (#634), and a JSONC settings.json no longer suppresses the statusline nudge. Config ownership: - src/hooks/uninstall.{sh,ps1} matched hooks by the substring "caveman" and filtered whole entries, deleting user hooks that merely mention the word and dropping foreign handlers that shared a matcher group (#593). Ported the exact-basename matcher from bin/lib/settings.js and switched to filtering the inner handler list. Verified against real PowerShell: the old code left zero user hooks standing. - install.{sh,ps1} backed up settings.json unguarded, so a --force reinstall overwrote the only pre-caveman copy with the merged file. Back up once. - OpenClaw overwrote a hand-edited workspace skill with no backup. - caveman-init appended unfenced blocks to tracked repo files, so they could be neither refreshed nor removed; now marker-fenced and written atomically. - opencode AGENTS.md and OpenClaw SOUL.md were presence-checked, so a ruleset edit never reached anyone already installed. Both refresh in place. - opencode's system-prompt hook had no idempotency guard; it now rewrites its line rather than stacking another onto the context window. - readSettings reports when it stripped comments so both write paths warn and point at the backup instead of silently dropping a user's annotations. Skills: skills/ is auto-discovered wholesale by the plugin loader, so 13 undocumented skills would have shipped into every user's agent on the next release. Documented them and added a verify_repo check that fails when a skills/ directory is named in neither README.md nor CLAUDE.md. CI: a windows-powershell job covering the surface engine-ci never exercised — the PowerShell installer round trip, and a bash -n gate over every hook command the installer writes. That gate alone catches #835. Tests: every fix above is pinned by a test that fails against the old code, including tests/test_hook_stdin_lifecycle.js, which reproduces the Windows lagging-close condition on any platform. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ez7w7BBtKrMZCiZz9ea4cb
46 lines
1.7 KiB
JavaScript
46 lines
1.7 KiB
JavaScript
'use strict';
|
|
|
|
const path = require('path');
|
|
|
|
function powershellQuote(value) {
|
|
return `'${String(value).replace(/'/g, "''")}'`;
|
|
}
|
|
|
|
// RESOLVED (#835): Claude Code runs hook commands through BASH on every
|
|
// platform — Git Bash on Windows — unless the hook opts into `"shell":
|
|
// "powershell"`. Confirmed from the shipped binary, which carries both
|
|
// 'Hook "…" requires bash but Git Bash was not found. … or add
|
|
// "shell": "powershell" to this hook'"'"'s config.'
|
|
// and the inverse message for a missing PowerShell.
|
|
//
|
|
// The old win32 branch emitted PowerShell call-operator syntax
|
|
// (`& 'C:\\…\\node.exe' 'C:\\…\\hook.js'`), which bash rejects outright:
|
|
// bash: syntax error near unexpected token `&'
|
|
// That made both caveman hooks fail on every SessionStart and
|
|
// UserPromptSubmit for every standalone Windows install.
|
|
//
|
|
// So both branches emit POSIX quoting. win32 additionally normalizes `\` to
|
|
// `/`: Windows Node accepts forward slashes, and it keeps bash from eating
|
|
// backslashes as escapes inside the double quotes.
|
|
function hookCommand(executable, args, platform = process.platform) {
|
|
const normalize = platform === 'win32'
|
|
? (value) => String(value).replace(/\\/g, '/')
|
|
: (value) => String(value);
|
|
return [executable, ...args]
|
|
.map(value => `"${normalize(value).replace(/(["\\$`])/g, '\\$1')}"`)
|
|
.join(' ');
|
|
}
|
|
|
|
function jetbrainsRoots(home, env = process.env) {
|
|
const roots = [
|
|
path.join(home, 'Library/Application Support/JetBrains'),
|
|
path.join(home, '.config/JetBrains'),
|
|
];
|
|
for (const key of ['APPDATA', 'LOCALAPPDATA']) {
|
|
if (env[key]) roots.push(path.join(env[key], 'JetBrains'));
|
|
}
|
|
return [...new Set(roots)];
|
|
}
|
|
|
|
module.exports = { hookCommand, jetbrainsRoots, powershellQuote };
|