File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1010 * - Always handles errors gracefully — never crashes.
1111 */
1212
13+ /**
14+ * Output formats:
15+ * - Exit 0: Allow task completion (all gates passed)
16+ * - Exit 2 + stderr: Block completion — gates failed, agent must fix before completing
17+ * - JSON stdout { continue: false, stopReason: "..." } + exit 0: Stop the teammate entirely
18+ *
19+ * Currently uses exit 2 (block + report) when test/build/lint gates fail.
20+ * Use stopTeammate() from shared.ts for permanent stop scenarios.
21+ */
22+
1323import * as fs from 'node:fs' ;
1424import * as path from 'node:path' ;
1525import { spawnSync } from 'node:child_process' ;
Original file line number Diff line number Diff line change 99 * - Always handles errors gracefully — never crashes.
1010 */
1111
12+ /**
13+ * Output formats:
14+ * - Exit 0: Allow the teammate to remain idle (no pending tasks)
15+ * - Exit 2 + stderr: Block idle and redirect — "Pick up the next available task."
16+ * - JSON stdout { continue: false, stopReason: "..." } + exit 0: Stop the teammate entirely
17+ *
18+ * Currently uses exit 2 (block + redirect) when pending tasks exist.
19+ * Use stopTeammate() from shared.ts for permanent stop scenarios.
20+ */
21+
1222import * as fs from 'node:fs' ;
1323import * as os from 'node:os' ;
1424import * as path from 'node:path' ;
Original file line number Diff line number Diff line change @@ -103,13 +103,15 @@ export function playSound(soundFile: string): void {
103103 // For named system sounds (no extension) fall back to rundll32.
104104 const isWav = soundFile . toLowerCase ( ) . endsWith ( '.wav' ) ;
105105 if ( isWav ) {
106+ // Use double-quoted string which handles spaces and most special chars
107+ const escaped = soundFile . replace ( / " / g, '\\"' ) ;
106108 spawnSync (
107109 'powershell' ,
108110 [
109111 '-NoProfile' ,
110112 '-NonInteractive' ,
111113 '-Command' ,
112- `$p=' ${ soundFile . replace ( / ' / g , "''" ) } ' ; (New-Object System.Media.SoundPlayer $p).PlaySync()` ,
114+ `$p=" ${ escaped } " ; (New-Object System.Media.SoundPlayer $p).PlaySync()` ,
113115 ] ,
114116 { stdio : 'ignore' } ,
115117 ) ;
@@ -135,3 +137,17 @@ export function playSound(soundFile: string): void {
135137 // Never crash on sound failure
136138 }
137139}
140+
141+ /**
142+ * Send a JSON stop signal to terminate a teammate.
143+ * Outputs the stop payload to stdout and exits cleanly.
144+ * Use this when a teammate should be permanently stopped (not just blocked).
145+ *
146+ * For blocking (retry behavior), use: process.stderr.write(msg); process.exit(2);
147+ * For stopping (permanent), use: stopTeammate(reason);
148+ */
149+ export function stopTeammate ( reason : string ) : never {
150+ const payload = JSON . stringify ( { continue : false , stopReason : reason } ) ;
151+ process . stdout . write ( `${ payload } \n` ) ;
152+ process . exit ( 0 ) ;
153+ }
You can’t perform that action at this time.
0 commit comments