Skip to content

Reimplement the engine in Node for native cross-platform support - #17

Closed
chris-peterson wants to merge 2 commits into
mainfrom
node-rewrite
Closed

chris-peterson wants to merge 2 commits into
mainfrom
node-rewrite

Conversation

@chris-peterson

Copy link
Copy Markdown
Owner

Why

ClaudeWatch targeted Unix only: hooks.json invoked the engine as python3 …, which doesn't resolve on a vanilla native-Windows install (no python3 on PATH, no bash), so a Windows user got no safety net — silently (#15). A portable-launcher approach was tried first (#16) but hooks.json has no per-OS command syntax, so a .sh/.ps1 pair can't be selected per platform. The supported cross-platform pattern is to invoke node, which ships with Claude Code and is always on PATH. So the engine is reimplemented in Node: node scripts/watchdog.mjs runs on every platform Claude Code runs on, and the interpreter-resolution problem disappears.

What changed

  • Engine ported to scripts/watchdog.mjs (Node, stdlib-only, ESM) — behaviorally equivalent to the former watchdog.py, proven by the existing 20-file decision corpus re-pointed at it (assertions unchanged) plus tests/test-node-port.sh pinning port-specific behaviors.
  • Analyzer + reset ported to scripts/analyze.mjs / scripts/reset.mjs; analyze.mjs imports commandShape from watchdog.mjs so writer and reader keep one shared shape definition.
  • SessionStart scripts ported to hooks/emit-rules.mjs / hooks/cli-freshness.mjs.
  • hooks.json rewired: all four hook commands invoke node ….
  • Python engine + bash scripts deleted (watchdog.py, analyze-decisions.py, reset-decisions.py, emit-rules.sh, cli-freshness.sh) — one engine, and the tested engine is now the shipped engine.
  • build/gen-rules-doc.py had imported the rules parser from the engine; the parser is inlined into it (generated docs verified byte-identical). Release/CI-only Python tooling (gen-plugin-json.py, gen-rules-doc.py, changelog-prepend.py) stays Python — it never runs on a user's machine.
  • .github/workflows/ci.yml added: Ubuntu runs the corpus; a windows-latest job exercises the Node engine directly (deny, allow-by-silence, SessionStart emit) — the native-Windows evidence that can't be produced from a Mac.

Accepted v1.0.0 limitations (documented in SPEC.md)

JS RegExp semantics differ from Python re at two edges, surfaced by a high-effort review and accepted rather than translated (the rules target ASCII shell tokens; a Unicode-faithful \b rewrite would risk new divergence): \w/\d/\b are ASCII ([RL-03b]), and trailing $ matches only at true end-of-string. Invalid-UTF-8 Edits are handled gracefully ([EN-13a]) where the Python engine crashed.

Verification

  • bash tests/test-watchdog.sh — full corpus green (incl. the Node-ported analyze/reset/ambient suites and test-node-port).
  • just plugin-json-check — passes.
  • Generated docs byte-identical to pre-cutover.
  • Native-Windows behavior is verified by the windows-latest CI job on this PR.

Release

Intended as the 1.0.0 release (runtime change). The version is written from the release tag, not in this PR; cut v1.0.0 on merge.

Closes #15.

Reimplement the PreToolUse engine in Node so it runs natively on every
platform Claude Code runs on, removing the Python interpreter-resolution
problem that blocks native Windows support (#15). watchdog.py stays in place
and remains the engine hooks.json invokes; the cutover and its removal are a
later step.

The 20-file test corpus is re-pointed at the Node engine (invocations only,
assertions unchanged) and passes, plus a new tests/test-node-port.sh pinning
the port-specific behaviors a high-effort review surfaced:

- flush stdout fully before exit, so a deny reason larger than the OS pipe
  buffer is not truncated into an unparseable decision
- Python-faithful empty-needle Edit reconstruction and command_shape basename
- first-line-only schema read on the logging hot path
- microsecond ts format matching datetime.isoformat
- documented JS RegExp semantics: ASCII \w \d \b and end-of-string $ as an
  accepted v1.0.0 limitation (SPEC [RL-03a]/[RL-03b]), and graceful
  invalid-UTF-8 Edit handling ([EN-13a]), an improvement over the Python engine
Wire hooks.json to invoke the Node engine and the ported Node hooks (node
scripts/watchdog.mjs, hooks/emit-rules.mjs, hooks/cli-freshness.mjs), so the
engine that runs on a user's machine no longer depends on a Python interpreter
being resolvable — closing the native cross-platform gap (#15).

- Port the decision-log analyzer and reset tool to Node (scripts/analyze.mjs,
  scripts/reset.mjs); analyze.mjs imports commandShape from watchdog.mjs so the
  engine (writer) and analyzer (reader) keep one shared shape definition.
- Port the SessionStart scripts (emit-rules.mjs, cli-freshness.mjs).
- Export commandShape from watchdog.mjs and guard main() so importing the module
  for the shared helper neither reads stdin nor emits a decision.
- Delete the Python engine and the bash SessionStart scripts (watchdog.py,
  analyze-decisions.py, reset-decisions.py, emit-rules.sh, cli-freshness.sh) now
  that Node replaces them.
- Inline the rules-YAML parser into build/gen-rules-doc.py, which had imported
  it from the deleted engine; generated docs are byte-identical.
- Add .github/workflows/ci.yml: the Ubuntu job runs the corpus; a windows-latest
  job exercises the Node engine directly (deny, allow-by-silence, SessionStart
  emit) as the cross-platform evidence.
- Re-point test-analyze / test-reset-decisions / test-ambient at the Node ports;
  full corpus green. Update SPEC/AGENTS/README/SCHEMA/STATUS for the Node engine.

Build/release tooling that runs only in CI on Linux (gen-plugin-json.py,
gen-rules-doc.py, changelog-prepend.py) stays Python.
@chris-peterson chris-peterson self-assigned this Jun 28, 2026
@chris-peterson

Copy link
Copy Markdown
Owner Author

closing -- this can serve as a reference as/when committing to this direction

@chris-peterson
chris-peterson deleted the node-rewrite branch August 20, 2026 15:08
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.

Reimplement engine in Node for native cross-platform support

1 participant