great-cto 3.28.4 → 3.28.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "great-cto",
|
|
3
3
|
"description": "You already have the agent. This is everything around it. great_cto runs Claude Code as a pipeline of 70 specialist agents \u2014 an independent model checks each stage before the next builds on it, spending caps refuse rather than warn, and three decisions stay yours: what gets built, how, and whether it ships.",
|
|
4
|
-
"version": "3.28.
|
|
4
|
+
"version": "3.28.5",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Alexander Velikiy",
|
|
7
7
|
"url": "https://hashnode.com/@Greatcto"
|
|
@@ -85,24 +85,43 @@ export function runCodexExec({
|
|
|
85
85
|
if (cwd) args.push('-C', cwd);
|
|
86
86
|
args.push(...extraArgs, '-');
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
// Its own process group, so a timeout stops the CLI AND whatever it started.
|
|
89
|
+
// Killing only the CLI left its children running — the same orphan that kept
|
|
90
|
+
// a release gate's test runner alive for a day (2026-09-10).
|
|
91
|
+
const group = process.platform !== 'win32';
|
|
92
|
+
const proc = spawn(bin, args, { cwd, stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env }, detached: group });
|
|
89
93
|
let out = '';
|
|
90
94
|
let err = '';
|
|
91
|
-
|
|
95
|
+
let timedOut = false;
|
|
96
|
+
const killGroup = () => {
|
|
97
|
+
try {
|
|
98
|
+
if (group && proc.pid) process.kill(-proc.pid, 'SIGKILL');
|
|
99
|
+
else proc.kill('SIGKILL');
|
|
100
|
+
} catch { try { proc.kill('SIGKILL'); } catch { /* gone */ } }
|
|
101
|
+
};
|
|
102
|
+
const timer = setTimeout(() => { timedOut = true; killGroup(); }, timeoutMs);
|
|
92
103
|
|
|
93
104
|
proc.stdout.on('data', (b) => { out += String(b); });
|
|
94
105
|
proc.stderr.on('data', (b) => { err += String(b); });
|
|
95
106
|
proc.on('error', (e) => {
|
|
96
107
|
clearTimeout(timer);
|
|
97
|
-
resolve({ state: 'unreadable', text: null, usage: null, errors: [String(e.message || e)], code: null, model });
|
|
108
|
+
resolve({ state: 'unreadable', text: null, usage: null, errors: [String(e.message || e)], code: null, model, timedOut });
|
|
98
109
|
});
|
|
99
110
|
proc.on('close', (code) => {
|
|
100
111
|
clearTimeout(timer);
|
|
112
|
+
// Best effort: a child the CLI left behind must not outlive the review.
|
|
113
|
+
if (group && proc.pid) { try { process.kill(-proc.pid, 'SIGKILL'); } catch { /* group already gone */ } }
|
|
101
114
|
const parsed = parseCodexStream(out);
|
|
102
115
|
// stderr is kept even on success: Codex writes warnings there that change
|
|
103
116
|
// how a result should be read.
|
|
104
117
|
if (err.trim()) parsed.errors.push(err.trim().slice(0, 500));
|
|
105
|
-
|
|
118
|
+
// A run cut off by the clock may have printed a verdict before it was done.
|
|
119
|
+
// That is a truncated answer, and a truncated answer is not an answer.
|
|
120
|
+
if (timedOut) {
|
|
121
|
+
parsed.errors.push(`timed out after ${timeoutMs}ms`);
|
|
122
|
+
if (parsed.state === 'ok') parsed.state = 'unreadable';
|
|
123
|
+
}
|
|
124
|
+
resolve({ ...parsed, code, model, timedOut });
|
|
106
125
|
});
|
|
107
126
|
|
|
108
127
|
proc.stdin.write(prompt);
|
package/package.json
CHANGED