@molecule/api-ai-tools 1.9.1 → 1.9.2
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.
- package/README.md +25 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/pipeline-exit-note.d.ts +37 -0
- package/dist/pipeline-exit-note.d.ts.map +1 -0
- package/dist/pipeline-exit-note.js +154 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +14 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
|
3
3
|
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
4
|
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
5
|
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
-
Generated: 2026-10-
|
|
6
|
+
Generated: 2026-10-17T09:38:26.898Z
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
# @molecule/api-ai-tools
|
|
@@ -1227,6 +1227,30 @@ function commandRunsNoTests(command: string): boolean
|
|
|
1227
1227
|
|
|
1228
1228
|
**Returns:** True when the command invokes a runner only to print.
|
|
1229
1229
|
|
|
1230
|
+
#### `pipelineExitCodeNote(command, exitCode, output)`
|
|
1231
|
+
|
|
1232
|
+
The sentence an `exec_command` result carries beside `exitCode` when the code belongs to a
|
|
1233
|
+
pipeline's last stage rather than to the check that printed the output.
|
|
1234
|
+
|
|
1235
|
+
Two shapes, both from a hand-run check with an unguarded pipe:
|
|
1236
|
+
|
|
1237
|
+
- the code says 0 and the output carries failures (`tsc | head` reports `head`'s 0);
|
|
1238
|
+
- the code is non-zero and the checker printed no failures (`tsc | head && tail missing.log`
|
|
1239
|
+
reports `tail`'s 1, and the checker was clean).
|
|
1240
|
+
|
|
1241
|
+
A command with no pipe, a pipe guarded by `pipefail`, a pipeline where no checker runs, or a
|
|
1242
|
+
code and an output that agree all return null.
|
|
1243
|
+
|
|
1244
|
+
```typescript
|
|
1245
|
+
function pipelineExitCodeNote(command: string, exitCode: number, output: string): string | null
|
|
1246
|
+
```
|
|
1247
|
+
|
|
1248
|
+
- `command` — The command as the executor sent it.
|
|
1249
|
+
- `exitCode` — The code the shell reported (the pipeline's last stage's).
|
|
1250
|
+
- `output` — The command's raw stdout and stderr, for the checker's own text.
|
|
1251
|
+
|
|
1252
|
+
**Returns:** One sentence naming the stage the code came from, or null.
|
|
1253
|
+
|
|
1230
1254
|
#### `leadingSleepPeek(command)`
|
|
1231
1255
|
|
|
1232
1256
|
The leading `sleep` of a command, when the first statement is one and it asks for at least
|
package/dist/index.d.ts
CHANGED
|
@@ -137,6 +137,7 @@ export * from './cut-run.js';
|
|
|
137
137
|
export * from './failure-kind.js';
|
|
138
138
|
export * from './input-normalizer.js';
|
|
139
139
|
export * from './no-test-run.js';
|
|
140
|
+
export * from './pipeline-exit-note.js';
|
|
140
141
|
export * from './schemas.js';
|
|
141
142
|
export * from './sleep-peek.js';
|
|
142
143
|
export * from './suite-overlap.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkIG;AAEH,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAC1B,cAAc,sBAAsB,CAAA;AACpC,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkIG;AAEH,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,yBAAyB,CAAA;AACvC,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAC1B,cAAc,sBAAsB,CAAA;AACpC,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -137,6 +137,7 @@ export * from './cut-run.js';
|
|
|
137
137
|
export * from './failure-kind.js';
|
|
138
138
|
export * from './input-normalizer.js';
|
|
139
139
|
export * from './no-test-run.js';
|
|
140
|
+
export * from './pipeline-exit-note.js';
|
|
140
141
|
export * from './schemas.js';
|
|
141
142
|
export * from './sleep-peek.js';
|
|
142
143
|
export * from './suite-overlap.js';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The exit code a hand-run check's result carries is the pipeline's LAST stage's, and a weak
|
|
3
|
+
* executor reads the number as the check's verdict.
|
|
4
|
+
*
|
|
5
|
+
* `npx tsc --noEmit 2>&1 | head -40` reports `head`'s 0 while the output above it carries a type
|
|
6
|
+
* error, and `… | head -40 && tail -30 missing.log` reports `tail`'s 1 while the checker was clean —
|
|
7
|
+
* both measured in xr-molecule-flash-m117 (+04:06, +08:05). A model that trusts the code concludes
|
|
8
|
+
* "clean" and ships a build that does not compile, or hunts an error that is not there. This module
|
|
9
|
+
* says which stage produced the code, and what the output actually carries, in one sentence beside
|
|
10
|
+
* `exitCode`.
|
|
11
|
+
*
|
|
12
|
+
* The parse is the package's own shell reader ({@link locatedCommandSegments}), so a `|` inside
|
|
13
|
+
* quotes or a heredoc is not a stage, and `set -o pipefail` (where it really runs) turns the note
|
|
14
|
+
* off: then the code IS the check's.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* The sentence an `exec_command` result carries beside `exitCode` when the code belongs to a
|
|
20
|
+
* pipeline's last stage rather than to the check that printed the output.
|
|
21
|
+
*
|
|
22
|
+
* Two shapes, both from a hand-run check with an unguarded pipe:
|
|
23
|
+
*
|
|
24
|
+
* - the code says 0 and the output carries failures (`tsc | head` reports `head`'s 0);
|
|
25
|
+
* - the code is non-zero and the checker printed no failures (`tsc | head && tail missing.log`
|
|
26
|
+
* reports `tail`'s 1, and the checker was clean).
|
|
27
|
+
*
|
|
28
|
+
* A command with no pipe, a pipe guarded by `pipefail`, a pipeline where no checker runs, or a
|
|
29
|
+
* code and an output that agree all return null.
|
|
30
|
+
*
|
|
31
|
+
* @param command - The command as the executor sent it.
|
|
32
|
+
* @param exitCode - The code the shell reported (the pipeline's last stage's).
|
|
33
|
+
* @param output - The command's raw stdout and stderr, for the checker's own text.
|
|
34
|
+
* @returns One sentence naming the stage the code came from, or null.
|
|
35
|
+
*/
|
|
36
|
+
export declare function pipelineExitCodeNote(command: string, exitCode: number, output: string): string | null;
|
|
37
|
+
//# sourceMappingURL=pipeline-exit-note.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline-exit-note.d.ts","sourceRoot":"","sources":["../src/pipeline-exit-note.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA2GH;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,MAAM,GAAG,IAAI,CAsCf"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The exit code a hand-run check's result carries is the pipeline's LAST stage's, and a weak
|
|
3
|
+
* executor reads the number as the check's verdict.
|
|
4
|
+
*
|
|
5
|
+
* `npx tsc --noEmit 2>&1 | head -40` reports `head`'s 0 while the output above it carries a type
|
|
6
|
+
* error, and `… | head -40 && tail -30 missing.log` reports `tail`'s 1 while the checker was clean —
|
|
7
|
+
* both measured in xr-molecule-flash-m117 (+04:06, +08:05). A model that trusts the code concludes
|
|
8
|
+
* "clean" and ships a build that does not compile, or hunts an error that is not there. This module
|
|
9
|
+
* says which stage produced the code, and what the output actually carries, in one sentence beside
|
|
10
|
+
* `exitCode`.
|
|
11
|
+
*
|
|
12
|
+
* The parse is the package's own shell reader ({@link locatedCommandSegments}), so a `|` inside
|
|
13
|
+
* quotes or a heredoc is not a stage, and `set -o pipefail` (where it really runs) turns the note
|
|
14
|
+
* off: then the code IS the check's.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import { locatedCommandSegments } from './command-position.js';
|
|
19
|
+
/** The shells whose `-o pipefail` makes a pipeline report its first failing stage. */
|
|
20
|
+
const PIPEFAIL_SHELLS = new Set(['ash', 'bash', 'dash', 'ksh', 'sh', 'zsh']);
|
|
21
|
+
/**
|
|
22
|
+
* The programs a pipeline may end with when all they do is read what was printed and print it
|
|
23
|
+
* again — the stages that hand back their own status in place of the check's.
|
|
24
|
+
*/
|
|
25
|
+
const OUTPUT_ONLY_FILTERS = new Set([
|
|
26
|
+
'awk',
|
|
27
|
+
'cat',
|
|
28
|
+
'column',
|
|
29
|
+
'cut',
|
|
30
|
+
'egrep',
|
|
31
|
+
'fgrep',
|
|
32
|
+
'fmt',
|
|
33
|
+
'fold',
|
|
34
|
+
'grep',
|
|
35
|
+
'head',
|
|
36
|
+
'jq',
|
|
37
|
+
'nl',
|
|
38
|
+
'rg',
|
|
39
|
+
'sed',
|
|
40
|
+
'sort',
|
|
41
|
+
'tail',
|
|
42
|
+
'tr',
|
|
43
|
+
'uniq',
|
|
44
|
+
'wc',
|
|
45
|
+
]);
|
|
46
|
+
/** `1 type error` / `2 type errors`. */
|
|
47
|
+
const plural = (count, one, many) => `${count} ${count === 1 ? one : many}`;
|
|
48
|
+
/**
|
|
49
|
+
* The failure count a runner's tally names. Vitest prints two tallies — `Test Files 1 failed`
|
|
50
|
+
* above `Tests 2 failed` — and the last one is the tests, which is the number the note means.
|
|
51
|
+
*
|
|
52
|
+
* @param output - The runner's output.
|
|
53
|
+
* @returns The count in the last `N failed` tally, or 0.
|
|
54
|
+
*/
|
|
55
|
+
const failedTally = (output) => {
|
|
56
|
+
const tallies = [...output.matchAll(/\b(\d+) failed\b/g)];
|
|
57
|
+
return Number(tallies.at(-1)?.[1] ?? 0);
|
|
58
|
+
};
|
|
59
|
+
/** The checkers whose output says pass or fail in text of its own. */
|
|
60
|
+
const CHECKERS = [
|
|
61
|
+
{
|
|
62
|
+
name: 'tsc',
|
|
63
|
+
runs: (program) => program === 'tsc' || program === 'vue-tsc',
|
|
64
|
+
failures: (output) => {
|
|
65
|
+
const errors = output.match(/\berror TS\d+/g)?.length ?? 0;
|
|
66
|
+
if (errors > 0)
|
|
67
|
+
return errors;
|
|
68
|
+
const found = /\bFound (\d+) errors?/.exec(output);
|
|
69
|
+
return found ? Number(found[1]) : 0;
|
|
70
|
+
},
|
|
71
|
+
label: (count) => plural(count, 'type error', 'type errors'),
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'eslint',
|
|
75
|
+
runs: (program) => program === 'eslint',
|
|
76
|
+
failures: (output) => {
|
|
77
|
+
const problems = /✖\s*(\d+)\s+problems?/.exec(output);
|
|
78
|
+
return problems ? Number(problems[1]) : 0;
|
|
79
|
+
},
|
|
80
|
+
label: (count) => plural(count, 'lint problem', 'lint problems'),
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'vitest',
|
|
84
|
+
runs: (program) => program === 'vitest',
|
|
85
|
+
failures: failedTally,
|
|
86
|
+
label: (count) => plural(count, 'failing test', 'failing tests'),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'playwright',
|
|
90
|
+
runs: (_program, text) => /^playwright\s+test\b/.test(text),
|
|
91
|
+
failures: failedTally,
|
|
92
|
+
label: (count) => plural(count, 'failing test', 'failing tests'),
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
/** Whether the command turns on `pipefail`, so a pipeline reports the check's own status. */
|
|
96
|
+
function guardedByPipefail(segments) {
|
|
97
|
+
return segments.some(({ program, args }) => (program === 'set' || PIPEFAIL_SHELLS.has(program)) && args.includes('pipefail'));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The sentence an `exec_command` result carries beside `exitCode` when the code belongs to a
|
|
101
|
+
* pipeline's last stage rather than to the check that printed the output.
|
|
102
|
+
*
|
|
103
|
+
* Two shapes, both from a hand-run check with an unguarded pipe:
|
|
104
|
+
*
|
|
105
|
+
* - the code says 0 and the output carries failures (`tsc | head` reports `head`'s 0);
|
|
106
|
+
* - the code is non-zero and the checker printed no failures (`tsc | head && tail missing.log`
|
|
107
|
+
* reports `tail`'s 1, and the checker was clean).
|
|
108
|
+
*
|
|
109
|
+
* A command with no pipe, a pipe guarded by `pipefail`, a pipeline where no checker runs, or a
|
|
110
|
+
* code and an output that agree all return null.
|
|
111
|
+
*
|
|
112
|
+
* @param command - The command as the executor sent it.
|
|
113
|
+
* @param exitCode - The code the shell reported (the pipeline's last stage's).
|
|
114
|
+
* @param output - The command's raw stdout and stderr, for the checker's own text.
|
|
115
|
+
* @returns One sentence naming the stage the code came from, or null.
|
|
116
|
+
*/
|
|
117
|
+
export function pipelineExitCodeNote(command, exitCode, output) {
|
|
118
|
+
const running = locatedCommandSegments(command).filter((entry) => entry.segment !== null);
|
|
119
|
+
if (running.length < 2)
|
|
120
|
+
return null;
|
|
121
|
+
if (!running.some((entry) => entry.separator.trim() === '|'))
|
|
122
|
+
return null;
|
|
123
|
+
const segments = running.map((entry) => entry.segment);
|
|
124
|
+
if (guardedByPipefail(segments))
|
|
125
|
+
return null;
|
|
126
|
+
let checkerAt = -1;
|
|
127
|
+
for (let i = segments.length - 1; i >= 0; i -= 1) {
|
|
128
|
+
const segment = segments[i];
|
|
129
|
+
if (CHECKERS.some((checker) => checker.runs(segment.program, segment.text))) {
|
|
130
|
+
checkerAt = i;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (checkerAt < 0)
|
|
135
|
+
return null;
|
|
136
|
+
// The check's own status is lost only when a stage of the SAME pipeline follows it; a `&&` after
|
|
137
|
+
// the check already reports a real failure of its own, and a check followed by nothing reports
|
|
138
|
+
// its own code.
|
|
139
|
+
if (running[checkerAt + 1]?.separator.trim() !== '|')
|
|
140
|
+
return null;
|
|
141
|
+
const checkerSegment = segments[checkerAt];
|
|
142
|
+
const checker = CHECKERS.find((candidate) => candidate.runs(checkerSegment.program, checkerSegment.text));
|
|
143
|
+
const last = segments[segments.length - 1];
|
|
144
|
+
const failures = checker.failures(output);
|
|
145
|
+
if (exitCode === 0 && failures > 0) {
|
|
146
|
+
return (`the exit code 0 is the last stage's (${last.program}), not ${checker.name}'s — ` +
|
|
147
|
+
`the output carries ${checker.label(failures)}`);
|
|
148
|
+
}
|
|
149
|
+
if (exitCode !== 0 && failures === 0 && OUTPUT_ONLY_FILTERS.has(last.program)) {
|
|
150
|
+
return (`the exit code ${exitCode} is the last stage's (${last.program}), not ${checker.name}'s — ` +
|
|
151
|
+
'the checker printed no errors');
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAiJ9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAahG;AAiOD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAmEnE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAEA,CAAA;AAE9B;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,QAI6C,CAAA;AAmBlF;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUhE;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,8FAA8F;IAC9F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAA;AAoBpF;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,CAAC,GACxE,qBAAqB,CAkBvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,qBAAqB,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiCxB;AAgDD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAy3ExF"}
|
package/dist/tools.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { commandCutRun, CUT_AT_BUDGET, cutRunSentence, suiteRunExitCode } from './cut-run.js';
|
|
10
10
|
import { addChangeSpan, changedSpan, countLines, renderEditExcerpt, renderSmallFile, } from './edit-excerpt.js';
|
|
11
11
|
import { guardToolExecute, missingParamError, ToolInputError } from './input-normalizer.js';
|
|
12
|
+
import { pipelineExitCodeNote } from './pipeline-exit-note.js';
|
|
12
13
|
import { TOOL_SCHEMAS } from './schemas.js';
|
|
13
14
|
import { leadingSleepPeek } from './sleep-peek.js';
|
|
14
15
|
/**
|
|
@@ -2172,6 +2173,12 @@ export function buildTools(backend, config) {
|
|
|
2172
2173
|
const placement = suiteRunPipelinePlacement(streaming.command);
|
|
2173
2174
|
const reported = suiteRunExitCode(code, output, placement.piped);
|
|
2174
2175
|
const exitNote = suiteExitCodeNote(placement, reported);
|
|
2176
|
+
// The same question the foreground path asks: when the code is a filter's, say
|
|
2177
|
+
// which stage produced it (and what the output carries) rather than leaving a
|
|
2178
|
+
// 0 beside a printed error.
|
|
2179
|
+
const pipeNote = reported === null
|
|
2180
|
+
? null
|
|
2181
|
+
: pipelineExitCodeNote(streaming.command, reported, output);
|
|
2175
2182
|
const notes = [
|
|
2176
2183
|
...(sleepNote ? [sleepNote] : []),
|
|
2177
2184
|
...streaming.changes,
|
|
@@ -2181,6 +2188,7 @@ export function buildTools(backend, config) {
|
|
|
2181
2188
|
stdout,
|
|
2182
2189
|
stderr: '',
|
|
2183
2190
|
exitCode: reported,
|
|
2191
|
+
...(pipeNote === null ? {} : { exitCodeNote: pipeNote }),
|
|
2184
2192
|
...(output.length > MAX_OUTPUT_SIZE ? { truncated: true, fullOutput: log } : {}),
|
|
2185
2193
|
...(notes.length ? { note: notes.join(' ') } : {}),
|
|
2186
2194
|
};
|
|
@@ -2305,10 +2313,16 @@ export function buildTools(backend, config) {
|
|
|
2305
2313
|
(isSuiteRunCommand(command) ? SUITE_OVER_BUDGET_ADVICE : SPLIT_WORK_ADVICE),
|
|
2306
2314
|
};
|
|
2307
2315
|
}
|
|
2316
|
+
// A pipeline reports its LAST stage's code: `tsc | head` says 0 beside a printed type
|
|
2317
|
+
// error, and `tsc | head && tail missing.log` says 1 beside a clean check. Name the
|
|
2318
|
+
// stage the number came from (pipeline-exit-note.ts) — the field the executor trusts
|
|
2319
|
+
// most must not contradict the text it can read.
|
|
2320
|
+
const exitCodeNote = pipelineExitCodeNote(command, result.exitCode, `${result.stdout}\n${result.stderr}`);
|
|
2308
2321
|
return {
|
|
2309
2322
|
stdout,
|
|
2310
2323
|
stderr,
|
|
2311
2324
|
exitCode: result.exitCode,
|
|
2325
|
+
...(exitCodeNote === null ? {} : { exitCodeNote }),
|
|
2312
2326
|
...(peekedTask?.slice.trim() ? { outputSoFar: sanitizeOutput(peekedTask.slice) } : {}),
|
|
2313
2327
|
...(sleepNote || ceilingNote || traced.change
|
|
2314
2328
|
? {
|