thedotmack/claude-mem · critical · Error
Hand-edited shell string detected in ${filePath} (${dottedPa
Error message
Hand-edited shell string detected in ${filePath} (${dottedPath}). It no longer matches src/build/hook-shell-template.ts. Regenerate via `node scripts/build-hooks.js --write-shell-templates` after an intentional generator change. What it means
Same Rule A canonicality guard as [2], but for hook entries of kind other than 'mcp'. For each dottedPath (event.groupIdx.hookIdx) in the manifest, it resolves the hook via hookEntryByPath and compares entry.command to the expected command string from the generator. A mismatch on the Unix/macOS command field throws this. Fix is identical: regenerate or revert.
Source
Thrown at scripts/build-hooks.js:195
const actual = parsed.mcpServers?.['mcp-search']?.args?.[1] ?? '';
if (actual !== spec.command) {
if (!writeMode) {
throw new Error(
`Hand-edited shell string detected in ${filePath} (mcp-search). It no longer matches src/build/hook-shell-template.ts. ` +
`Regenerate via \`node scripts/build-hooks.js --write-shell-templates\` after an intentional generator change.`
);
}
parsed.mcpServers['mcp-search'].args[1] = spec.command;
dirty = true;
}
} else {
for (const [dottedPath, expected] of Object.entries(spec.commands)) {
const entry = hookEntryByPath(parsed, dottedPath);
const expectedCommand = typeof expected === 'string' ? expected : expected.command;
const actual = entry?.command ?? null;
if (actual !== expectedCommand) {
if (!writeMode || !entry) {
throw new Error(
`Hand-edited shell string detected in ${filePath} (${dottedPath}). It no longer matches src/build/hook-shell-template.ts. ` +
`Regenerate via \`node scripts/build-hooks.js --write-shell-templates\` after an intentional generator change.`
);
}
entry.command = expectedCommand;
dirty = true;
}
if (typeof expected !== 'string') {
const actualWindows = entry?.commandWindows ?? null;
if (actualWindows !== expected.commandWindows) {
if (!writeMode || !entry) {
throw new Error(
`Hand-edited Windows shell string detected in ${filePath} (${dottedPath}). It no longer matches src/build/hook-shell-template.ts. ` +
`Regenerate via \`node scripts/build-hooks.js --write-shell-templates\` after an intentional generator change.`
);
}
entry.commandWindows = expected.commandWindows;
dirty = true;View on GitHub (pinned to d768ba3643)
Solutions
- Run node scripts/build-hooks.js --write-shell-templates to rewrite entry.command from the generator.
- If writeMode reports the entry is missing (!entry branch), restore the hook entry in the JSON first (it must exist for the regenerator to populate), then re-run.
- If unintentional, git checkout -- <filePath> to restore the canonical committed launcher.
- Re-run the build to confirm '✓ Rule A shell templates match the canonical generator'.
Example fix
# before: hook command drifted from generator "command": "/old/launcher/path script.js" node scripts/build-hooks.js --write-shell-templates # after: command rewritten to canonical src/build/hook-shell-template.ts output
Defensive patterns
Strategy: validation
Validate before calling
// Pre-commit verification: run the same verifier the build runs. // node scripts/build-hooks.js -> exits non-zero if any hook command drifted.
Type guard
function hasCommand(entry): entry is { command: string } {
return entry != null && typeof entry.command === 'string';
} Try / catch
// Build-time only. On drift either: // node scripts/build-hooks.js --write-shell-templates (accept generator) // git checkout -- <file> (reject hand edit) // Do not suppress in CI.
Prevention
- Edit src/build/hook-shell-template.ts, never the generated hook JSONs.
- When adding a hook, update the manifest generator so it appears in shellTemplateManifest.
- Run the build locally before pushing so CI never sees drift.
When it happens
Trigger: A committed hooks JSON (e.g. plugin/hooks/settings.json, .claude/settings.json) has a hooks[event][group].hooks[i].command string that differs from what buildShellCommand produces for that hook. Triggered during build verification. Also thrown when writeMode is on but the hook entry is missing entirely (entry is null), because there's nothing to write into.
Common situations: Hand-editing a hook command to point at a different script. Adding a new hook in the manifest generator but forgetting --write-shell-templates. A hook entry deleted from the JSON so hookEntryByPath returns null while the manifest still lists its dottedPath.
Related errors
- Hand-edited shell string detected in ${filePath} (mcp-search
- Hand-edited Windows shell string detected in ${filePath} (${
- plugin/hooks/codex-hooks.json contains unsupported Codex roo
- plugin/hooks/codex-hooks.json contains unknown Codex hook ev
- plugin/scripts/bun-runner.js is missing fixBrokenScriptPath
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/c5626d62f17832d3.
Report an issue: GitHub.