Files
ruvnet__ruflo/plugins/ruflo-cost-tracker/scripts/ruflo-hook.cjs
ruv 147145c8b8 fix(cost-tracker): add Windows shim ruflo-hook.cjs (#1902/#1903/#1904, #2132)
The Plugin package install-safety smoke (#1902/#1903/#1904) checks that
every plugin marked _platform=posix in its hooks.json has a corresponding
plugins/<name>/scripts/ruflo-hook.cjs sibling for Windows users.

plugins/ruflo-cost-tracker/hooks/hooks.json declares "_platform": "posix"
(its Stop hook is a /bin/bash one-liner) but had no Node shim. The
audit-plugin-hooks-cross-platform.mjs gate fails with:

  plugins/ruflo-cost-tracker/hooks/hooks.json:0  POSIX-exempt but Windows shim missing
  cmd: Expected plugins/ruflo-cost-tracker/scripts/ruflo-hook.cjs

This shim is the Windows-portable equivalent of the existing POSIX hook:
spawns scripts/track.mjs with TRACK_QUIET=1, swallows output, times out
at 30s, always exits 0 (telemetry is best-effort). When ruflo init runs
on Windows, it writes a .claude/settings.json that points Stop at this
file instead of bash — same behaviour, no /bin/bash dependency.

Local audit:
  $ node scripts/audit-plugin-hooks-cross-platform.mjs
  ok: all POSIX-exempt files have a corresponding Windows Node shim

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-06-17 10:04:20 -04:00

57 lines
1.7 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* ruflo-hook.cjs — cross-platform Windows shim for cost-tracker's Stop hook (#2132)
*
* The cost-tracker hooks.json declares `"_platform": "posix"` because its
* Stop hook uses bash:
*
* /bin/bash -c 'TRACK_QUIET=1 node "${CLAUDE_PLUGIN_ROOT}/scripts/track.mjs" >/dev/null 2>&1 || true'
*
* On Windows, `ruflo init` writes a `.claude/settings.json` that overrides
* the Stop hook to invoke this shim instead — `node ruflo-hook.cjs` —
* so cost tracking continues to capture session data without bash.
*
* Behaviour mirrors the bash hook:
* 1. Reads hook event payload from stdin (best effort — discarded).
* 2. Spawns track.mjs with TRACK_QUIET=1 and swallows all output.
* 3. Always exits 0 — telemetry is best-effort, must NEVER block a turn.
* 4. Times out at 30s so a hung track.mjs can't stall session shutdown.
*
* Usage: node ruflo-hook.cjs [hook-args ignored]
*/
'use strict';
const { spawnSync } = require('child_process');
const fs = require('fs');
const path = require('path');
function done() {
process.exit(0);
}
// Best-effort drain of stdin so the pipe doesn't EPIPE on the parent
try {
fs.readFileSync(0);
} catch {
/* ignore */
}
// CLAUDE_PLUGIN_ROOT is set by Claude Code; fall back to this script's parent
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT
|| path.resolve(__dirname, '..');
const trackScript = path.join(pluginRoot, 'scripts', 'track.mjs');
if (!fs.existsSync(trackScript)) {
// Plugin layout drifted — exit clean, never block the turn
done();
}
spawnSync(process.execPath, [trackScript], {
env: { ...process.env, TRACK_QUIET: '1' },
stdio: 'ignore',
timeout: 30_000,
});
done();