diff --git a/tools/router-prehook.mjs b/tools/router-prehook.mjs index 748ad6ca..5fd623be 100644 --- a/tools/router-prehook.mjs +++ b/tools/router-prehook.mjs @@ -20,6 +20,7 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; import { join, dirname } from 'path'; import { homedir } from 'os'; import { fileURLToPath } from 'url'; +import { readStdinAsUtf8 } from './router-stdin-helper.mjs'; const ENFORCEMENT_TYPES = new Set(['feature', 'planning', 'bugfix', 'refactor', 'cleanup', 'marketing', 'security', 'analysis', 'monitoring']); @@ -54,8 +55,7 @@ function stateFilePath(sessionId) { } async function main() { - let input = ''; - for await (const chunk of process.stdin) input += chunk; + const input = await readStdinAsUtf8(process.stdin); const event = JSON.parse(input || '{}'); const sessionId = event.session_id || 'unknown'; const userPrompt = event.prompt || event.user_prompt || ''; diff --git a/tools/router-prehook.test.mjs b/tools/router-prehook.test.mjs index 3dc1ae49..f77df3a5 100644 --- a/tools/router-prehook.test.mjs +++ b/tools/router-prehook.test.mjs @@ -48,3 +48,10 @@ describe('isEnforcementRequired', () => { expect(isEnforcementRequired({ taskType: 'memory-sync', micro: false, recommendedNode: '#33' })).toBe(false); }); }); + +describe('UTF-8 cyrillic stdin (regression — Stage 3 fix 1)', () => { + it('module loads with UTF-8 helper wired (smoke)', async () => { + const mod = await import('./router-prehook.mjs'); + expect(typeof mod.buildStateFromClassification).toBe('function'); + }); +}); diff --git a/tools/router-stop-gate.mjs b/tools/router-stop-gate.mjs index 5f1ac9ac..9bdc0d90 100644 --- a/tools/router-stop-gate.mjs +++ b/tools/router-stop-gate.mjs @@ -11,6 +11,7 @@ import { readFileSync, writeFileSync, existsSync } from 'fs'; import { join } from 'path'; import { homedir } from 'os'; import { fileURLToPath } from 'url'; +import { readStdinAsUtf8 } from './router-stdin-helper.mjs'; export function extractSkillInvocations(events) { return (events || []) @@ -46,8 +47,7 @@ export function updateChainProgress(state, skillsInvoked, chains) { } async function main() { - let input = ''; - for await (const chunk of process.stdin) input += chunk; + const input = await readStdinAsUtf8(process.stdin); const event = JSON.parse(input || '{}'); const sessionId = event.session_id || 'unknown'; const events = event.turn_events || []; diff --git a/tools/router-stop-gate.test.mjs b/tools/router-stop-gate.test.mjs index ba2de30e..49d8bc1a 100644 --- a/tools/router-stop-gate.test.mjs +++ b/tools/router-stop-gate.test.mjs @@ -57,3 +57,10 @@ describe('updateChainProgress', () => { expect(updated.chainProgress).toEqual(['brainstorming', 'writing-plans']); }); }); + +describe('UTF-8 cyrillic stdin (regression — Stage 3 fix 1)', () => { + it('module loads with UTF-8 helper wired (smoke)', async () => { + const mod = await import('./router-stop-gate.mjs'); + expect(typeof mod.updateChainProgress).toBe('function'); + }); +}); diff --git a/tools/router-tool-gate.mjs b/tools/router-tool-gate.mjs index 6efabfe4..0de58591 100644 --- a/tools/router-tool-gate.mjs +++ b/tools/router-tool-gate.mjs @@ -86,8 +86,7 @@ function readState(sessionId) { } async function main() { - let input = ''; - for await (const chunk of process.stdin) input += chunk; + const input = await readStdinAsUtf8(process.stdin); const event = JSON.parse(input || '{}'); const sessionId = event.session_id || 'unknown'; const tool = event.tool_name; @@ -109,4 +108,5 @@ async function main() { // CLI guard — Windows-cyrillic quirk: use fileURLToPath(import.meta.url) import { fileURLToPath } from 'url'; +import { readStdinAsUtf8 } from './router-stdin-helper.mjs'; if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) { main(); } diff --git a/tools/router-tool-gate.test.mjs b/tools/router-tool-gate.test.mjs index 689b94b3..476c691b 100644 --- a/tools/router-tool-gate.test.mjs +++ b/tools/router-tool-gate.test.mjs @@ -97,3 +97,10 @@ describe('decideDecision', () => { expect(r.warning).toMatch(/#19/); }); }); + +describe('UTF-8 cyrillic stdin (regression — Stage 3 fix 1)', () => { + it('module loads with UTF-8 helper wired (smoke)', async () => { + const mod = await import('./router-tool-gate.mjs'); + expect(typeof mod.shouldBlock).toBe('function'); + }); +});