@mjasnikovs/pi-task 0.18.23 → 0.18.24
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.
|
@@ -913,13 +913,30 @@ export async function runAutoLoop(ctx, cwd, id, deps) {
|
|
|
913
913
|
// Hand the parent plan (the task list) to the gate so it can tell a
|
|
914
914
|
// served app from a CLI: the boot check requires a listener only for
|
|
915
915
|
// the former (mx5 run 10 — a CSS watcher satisfied "still alive").
|
|
916
|
+
// Trail EVERY aggregated failure entry (mx5 run 13): the gate now
|
|
917
|
+
// runs all sections and ranks the list; a single sliced reason
|
|
918
|
+
// line would re-hide everything past the first entry.
|
|
919
|
+
const trailGateFail = async (f) => {
|
|
920
|
+
const list = f.failures ?? [f.reason];
|
|
921
|
+
if (list.length <= 1) {
|
|
922
|
+
await recGate(`final-gate: FAIL — ${f.reason.slice(0, 300)}`);
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
await recGate(`final-gate: FAIL — ${list.length} failures (ranked, most load-bearing first)`);
|
|
926
|
+
for (const [i, entry] of list.entries()) {
|
|
927
|
+
await recGate(`final-gate FAIL ${i + 1}/${list.length}: ${entry.slice(0, 300)}`);
|
|
928
|
+
}
|
|
929
|
+
};
|
|
916
930
|
let fin = await deps.finalGate(cwd, body);
|
|
917
931
|
// Record the outcome symmetrically (mx5 run 10 item 7): only FAIL was
|
|
918
932
|
// ever trailed, so a PASSing gate was indistinguishable from a gate
|
|
919
933
|
// that never ran. The PASS reason names the commands that were run.
|
|
920
|
-
|
|
921
|
-
`final-gate: PASS — ${fin.reason.slice(0, 300)}`
|
|
922
|
-
|
|
934
|
+
if (fin.ok) {
|
|
935
|
+
await recGate(`final-gate: PASS — ${fin.reason.slice(0, 300)}`);
|
|
936
|
+
}
|
|
937
|
+
else {
|
|
938
|
+
await trailGateFail(fin);
|
|
939
|
+
}
|
|
923
940
|
// ACCEPT-debt re-check surfacing (mx5 run 4 B3 / run 8 TASK_0012):
|
|
924
941
|
// tasks the user accepted despite a verify-FAIL that the gate could
|
|
925
942
|
// not prove resolved against the current tree. Surface them at the
|
|
@@ -989,13 +1006,21 @@ export async function runAutoLoop(ctx, cwd, id, deps) {
|
|
|
989
1006
|
active.ui.notify(`${id}: final-gate autofix did not converge — ${fix.reason.slice(0, 140)}`, 'warning');
|
|
990
1007
|
// Work from the FRESH gate failure when the fix pass got
|
|
991
1008
|
// as far as re-running the gate; otherwise keep the last.
|
|
992
|
-
// The
|
|
993
|
-
// the
|
|
1009
|
+
// The full ranked list rides along (and is re-trailed
|
|
1010
|
+
// when fresh) so the next picker and the next fix seed
|
|
1011
|
+
// still carry every entry, not just the first. The debt
|
|
1012
|
+
// note is carried so the next picker still shows the
|
|
1013
|
+
// open claims (the seed never includes it).
|
|
994
1014
|
fin = {
|
|
995
1015
|
ok: false,
|
|
996
1016
|
reason: fix.gateReason ?? fin.reason,
|
|
1017
|
+
failures: fix.gateReason !== undefined ? fix.gateFailures : fin.failures,
|
|
997
1018
|
debtNote: fin.debtNote
|
|
998
1019
|
};
|
|
1020
|
+
if (fix.gateReason !== undefined
|
|
1021
|
+
&& (fix.gateFailures?.length ?? 0) > 1) {
|
|
1022
|
+
await trailGateFail(fin);
|
|
1023
|
+
}
|
|
999
1024
|
continue;
|
|
1000
1025
|
}
|
|
1001
1026
|
// Leave failed — the dismissal default, unchanged from the
|
|
@@ -42,7 +42,10 @@ export declare function extractFailingCommand(reason: string): string | null;
|
|
|
42
42
|
/**
|
|
43
43
|
* Build the fix child's prompt. Generic by construction: the only project facts
|
|
44
44
|
* in it are the gate's own failure text — the command comes from the project's
|
|
45
|
-
* discovered manifest, never from a hardcoded ecosystem.
|
|
45
|
+
* discovered manifest, never from a hardcoded ecosystem. The seed may carry
|
|
46
|
+
* SEVERAL failures (the gate aggregates every section since mx5 run 13, ranked
|
|
47
|
+
* most load-bearing first); convergence means the WHOLE list is empty, so the
|
|
48
|
+
* child is told to fix all of them.
|
|
46
49
|
*/
|
|
47
50
|
export declare function buildFinalFixPrompt(failReason: string): string;
|
|
48
51
|
/**
|
|
@@ -63,6 +66,9 @@ export interface FinalFixResult {
|
|
|
63
66
|
/** On a did-not-converge outcome: the FRESH gate failure, so the caller's next
|
|
64
67
|
* picker (and next fix attempt) works from the current state, not the stale one. */
|
|
65
68
|
gateReason?: string;
|
|
69
|
+
/** The fresh gate's individual ranked failures (see FinalGateOutcome.failures),
|
|
70
|
+
* so the caller can trail each entry — never just the first. */
|
|
71
|
+
gateFailures?: string[];
|
|
66
72
|
}
|
|
67
73
|
export interface FinalFixDeps {
|
|
68
74
|
cwd: string;
|
|
@@ -72,10 +78,13 @@ export interface FinalFixDeps {
|
|
|
72
78
|
failReason: string;
|
|
73
79
|
/** Run the fix child; same closure shape the other gate children use. */
|
|
74
80
|
runChild: (tools: string, prompt: string, signal?: AbortSignal) => Promise<string>;
|
|
75
|
-
/** Re-run the final integration gate — the only arbiter of convergence.
|
|
81
|
+
/** Re-run the final integration gate — the only arbiter of convergence.
|
|
82
|
+
* Converges only when the gate's FULL aggregated failure list is empty
|
|
83
|
+
* (ok=true); `failures` rides through so the caller sees every entry. */
|
|
76
84
|
gate: (cwd: string) => Promise<{
|
|
77
85
|
ok: boolean;
|
|
78
86
|
reason: string;
|
|
87
|
+
failures?: string[];
|
|
79
88
|
}>;
|
|
80
89
|
/** Labels of every currently-discoverable gate command (static + integration),
|
|
81
90
|
* for the shrink guard. Pure discovery — nothing is executed. */
|
|
@@ -96,19 +96,24 @@ export function extractFailingCommand(reason) {
|
|
|
96
96
|
/**
|
|
97
97
|
* Build the fix child's prompt. Generic by construction: the only project facts
|
|
98
98
|
* in it are the gate's own failure text — the command comes from the project's
|
|
99
|
-
* discovered manifest, never from a hardcoded ecosystem.
|
|
99
|
+
* discovered manifest, never from a hardcoded ecosystem. The seed may carry
|
|
100
|
+
* SEVERAL failures (the gate aggregates every section since mx5 run 13, ranked
|
|
101
|
+
* most load-bearing first); convergence means the WHOLE list is empty, so the
|
|
102
|
+
* child is told to fix all of them.
|
|
100
103
|
*/
|
|
101
104
|
export function buildFinalFixPrompt(failReason) {
|
|
102
105
|
return [
|
|
103
106
|
'You are a bounded fix pass for a FAILED whole-repo integration gate.',
|
|
104
107
|
'Every task in this run is complete and committed; then the project’s own',
|
|
105
|
-
'integration
|
|
108
|
+
'integration commands were run against the assembled repository and failed:',
|
|
106
109
|
'',
|
|
107
110
|
failReason.trim(),
|
|
108
111
|
'',
|
|
109
|
-
'Your ONLY job is to
|
|
112
|
+
'Your ONLY job is to fix the DEFECT(s) those failures reveal. When several',
|
|
113
|
+
'failures are listed they are ranked most load-bearing first — fix ALL of',
|
|
114
|
+
'them; the gate only converges when every one passes.',
|
|
110
115
|
'',
|
|
111
|
-
'1. Re-run
|
|
116
|
+
'1. Re-run each exact failing command first and read its full output.',
|
|
112
117
|
'2. Diagnose the root cause, then fix it with the smallest correct change.',
|
|
113
118
|
' The project’s own manifests, configs and conventions define what',
|
|
114
119
|
' correct means — follow them, do not invent new structure.',
|
|
@@ -129,9 +134,9 @@ export function buildFinalFixPrompt(failReason) {
|
|
|
129
134
|
' revert, stash, clean). The work in this repository is finished and',
|
|
130
135
|
' committed — reverting it is destroying the run, not fixing it.',
|
|
131
136
|
'',
|
|
132
|
-
'4. Re-run the failing command after your fix and confirm
|
|
133
|
-
' gate is re-run mechanically after you finish — your claim is not
|
|
134
|
-
' verdict, the real exit
|
|
137
|
+
'4. Re-run the failing command(s) after your fix and confirm they exit 0.',
|
|
138
|
+
' The gate is re-run mechanically after you finish — your claim is not',
|
|
139
|
+
' the verdict, the real exit codes are.',
|
|
135
140
|
'',
|
|
136
141
|
'End with exactly one line:',
|
|
137
142
|
' FINAL-GATE-FIX: DONE',
|
|
@@ -240,7 +245,8 @@ export async function runFinalGateAutofix(deps) {
|
|
|
240
245
|
return {
|
|
241
246
|
ok: false,
|
|
242
247
|
reason: `did not converge: ${fin.reason}`,
|
|
243
|
-
gateReason: fin.reason
|
|
248
|
+
gateReason: fin.reason,
|
|
249
|
+
gateFailures: fin.failures
|
|
244
250
|
};
|
|
245
251
|
}
|
|
246
252
|
return { ok: true, reason: fin.reason };
|
|
@@ -6,14 +6,25 @@ export interface FinalGateOutcome {
|
|
|
6
6
|
/** true → statics and every runnable integration command passed (or nothing to run). */
|
|
7
7
|
ok: boolean;
|
|
8
8
|
/**
|
|
9
|
-
* On a fail: the exact command,
|
|
10
|
-
* MECHANICAL
|
|
9
|
+
* On a fail: the exact command(s), exit code(s), and output tail(s) — the
|
|
10
|
+
* MECHANICAL failures only. The accept-debt note is deliberately NOT folded in
|
|
11
11
|
* here (mx5 run 11): this string seeds the final-gate AUTOFIX child's prompt,
|
|
12
12
|
* and a debt included there is read as an instruction — the run-11 fix child
|
|
13
13
|
* `rm`'d a sibling task's verified deliverable to satisfy a recorded claim. The
|
|
14
14
|
* child cannot act on text it never receives; debts travel in `debtNote`.
|
|
15
|
+
* With multiple failures this is the numbered, ranked list (see `failures`).
|
|
15
16
|
*/
|
|
16
17
|
reason: string;
|
|
18
|
+
/**
|
|
19
|
+
* On a fail: EVERY section failure individually, ranked most load-bearing
|
|
20
|
+
* first — boot/render ("the app does not serve/render") outranks any single
|
|
21
|
+
* test failure. The gate runs every section and aggregates rather than
|
|
22
|
+
* early-returning (mx5 run 13: a bun-test glob failure shadowed the boot +
|
|
23
|
+
* render probe, so the user accepted the FAIL having only ever seen 1 failing
|
|
24
|
+
* CT test while the shipped app 404'd on every non-API GET). Callers trail
|
|
25
|
+
* each entry and show the full list wherever an ACCEPT decision is made.
|
|
26
|
+
*/
|
|
27
|
+
failures?: string[];
|
|
17
28
|
/**
|
|
18
29
|
* Human-facing suffix listing the still-open accepted-defect claims (see
|
|
19
30
|
* buildAcceptDebtNote) — for the picker question and the trail, NEVER for the
|
|
@@ -146,6 +157,16 @@ export { taskThatIntroduced };
|
|
|
146
157
|
* Run the final gate: static analysis first, then the lockfile consistency
|
|
147
158
|
* checks, then the discovered integration commands, then one boot exercise of
|
|
148
159
|
* the start command — whole-repo, verbatim, unaided. Deterministic (no model).
|
|
149
|
-
*
|
|
160
|
+
*
|
|
161
|
+
* EVERY section runs and failures AGGREGATE (mx5 run 13): the gate used to
|
|
162
|
+
* early-return on the first failing section, and the boot + render probe — built
|
|
163
|
+
* after run 11 exactly for "app serves blank/nothing" — was ordered last, so any
|
|
164
|
+
* earlier failure shadowed the most load-bearing signal. Run 13's user accepted
|
|
165
|
+
* the FAIL having seen only 1 failing CT test while the app 404'd on every
|
|
166
|
+
* non-API GET; boot/render never executed in any attempt. Now the outcome
|
|
167
|
+
* carries the full ranked failure list (boot/render first — "the app does not
|
|
168
|
+
* serve/render" outranks any single test), the ACCEPT decision is made on all of
|
|
169
|
+
* it, and autofix converges only when the whole list is empty. Per-section
|
|
170
|
+
* env-gap/INFRA_GAP skip semantics and orphan-port recovery are unchanged.
|
|
150
171
|
*/
|
|
151
172
|
export declare function runFinalIntegrationGate(cwd: string, timeoutMs?: number, bootGraceMs?: number, bootDeps?: BootDeps, planText?: string): Promise<FinalGateOutcome>;
|
package/dist/task/final-gate.js
CHANGED
|
@@ -595,7 +595,17 @@ export { taskThatIntroduced };
|
|
|
595
595
|
* Run the final gate: static analysis first, then the lockfile consistency
|
|
596
596
|
* checks, then the discovered integration commands, then one boot exercise of
|
|
597
597
|
* the start command — whole-repo, verbatim, unaided. Deterministic (no model).
|
|
598
|
-
*
|
|
598
|
+
*
|
|
599
|
+
* EVERY section runs and failures AGGREGATE (mx5 run 13): the gate used to
|
|
600
|
+
* early-return on the first failing section, and the boot + render probe — built
|
|
601
|
+
* after run 11 exactly for "app serves blank/nothing" — was ordered last, so any
|
|
602
|
+
* earlier failure shadowed the most load-bearing signal. Run 13's user accepted
|
|
603
|
+
* the FAIL having seen only 1 failing CT test while the app 404'd on every
|
|
604
|
+
* non-API GET; boot/render never executed in any attempt. Now the outcome
|
|
605
|
+
* carries the full ranked failure list (boot/render first — "the app does not
|
|
606
|
+
* serve/render" outranks any single test), the ACCEPT decision is made on all of
|
|
607
|
+
* it, and autofix converges only when the whole list is empty. Per-section
|
|
608
|
+
* env-gap/INFRA_GAP skip semantics and orphan-port recovery are unchanged.
|
|
599
609
|
*/
|
|
600
610
|
export async function runFinalIntegrationGate(cwd, timeoutMs = 900_000, bootGraceMs = 10_000, bootDeps = {}, planText) {
|
|
601
611
|
const stat = runRepoHealthCheck(cwd);
|
|
@@ -630,8 +640,15 @@ export async function runFinalIntegrationGate(cwd, timeoutMs = 900_000, bootGrac
|
|
|
630
640
|
...(debtNote ? { debtNote } : {}),
|
|
631
641
|
openDebts
|
|
632
642
|
});
|
|
643
|
+
// Aggregated failures across ALL sections (mx5 run 13 — see the function doc).
|
|
644
|
+
// rank 0 = boot/render ("does not serve/render" is the most load-bearing
|
|
645
|
+
// signal); rank 1 = everything else, kept in execution order by stable sort.
|
|
646
|
+
const failures = [];
|
|
647
|
+
const fail = (text, rank = 1) => {
|
|
648
|
+
failures.push({ rank, text });
|
|
649
|
+
};
|
|
633
650
|
if (!stat.ok)
|
|
634
|
-
|
|
651
|
+
fail(`static checks: ${stat.reason}`);
|
|
635
652
|
// Launch-contract diff (mx5 run 10 item 4): the design declared `migrate`/`seed`
|
|
636
653
|
// scripts that fell through decompose and shipped missing, unchecked. Diff the
|
|
637
654
|
// plan-time-extracted declared scripts against the manifest; a missing one is a
|
|
@@ -640,16 +657,13 @@ export async function runFinalIntegrationGate(cwd, timeoutMs = 900_000, bootGrac
|
|
|
640
657
|
if (declared.length > 0) {
|
|
641
658
|
const missing = missingDeclaredScripts(declared, Object.keys(packageScripts(cwd)));
|
|
642
659
|
if (missing.length > 0) {
|
|
643
|
-
|
|
644
|
-
ok: false,
|
|
645
|
-
reason: `launch contract: the design declares script(s) the shipped package.json does not expose: ${missing.join(', ')} (declared: ${declared.join(', ')})`
|
|
646
|
-
});
|
|
660
|
+
fail(`launch contract: the design declares script(s) the shipped package.json does not expose: ${missing.join(', ')} (declared: ${declared.join(', ')})`);
|
|
647
661
|
}
|
|
648
662
|
}
|
|
649
663
|
const lockCmds = discoverLockfileChecks(cwd);
|
|
650
664
|
const { cmds } = discoverIntegrationCommands(cwd);
|
|
651
665
|
const boot = discoverBootCommand(cwd);
|
|
652
|
-
if (lockCmds.length === 0 && cmds.length === 0 && !boot) {
|
|
666
|
+
if (lockCmds.length === 0 && cmds.length === 0 && !boot && failures.length === 0) {
|
|
653
667
|
return withDebts({ ok: true, reason: 'no integration command found (statics passed)' });
|
|
654
668
|
}
|
|
655
669
|
const ran = [];
|
|
@@ -663,10 +677,8 @@ export async function runFinalIntegrationGate(cwd, timeoutMs = 900_000, bootGrac
|
|
|
663
677
|
if (r.outcome === 'skip')
|
|
664
678
|
continue;
|
|
665
679
|
if (r.outcome === 'fail') {
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
reason: `${prefix}\`${label}\` exited ${r.status}${r.tail ? ` — ${r.tail}` : ''}`
|
|
669
|
-
});
|
|
680
|
+
fail(`${prefix}\`${label}\` exited ${r.status}${r.tail ? ` — ${r.tail}` : ''}`);
|
|
681
|
+
continue;
|
|
670
682
|
}
|
|
671
683
|
ran.push(label);
|
|
672
684
|
}
|
|
@@ -685,7 +697,13 @@ export async function runFinalIntegrationGate(cwd, timeoutMs = 900_000, bootGrac
|
|
|
685
697
|
if (declared.length > 0) {
|
|
686
698
|
const covered = cmds.flatMap(([bin, args]) => (bin === 'bun' || bin === 'npm') && args[0] === 'run' && args[1] ? [args[1]] : []);
|
|
687
699
|
const skippedLaunch = [];
|
|
700
|
+
// A declared script the manifest doesn't expose is already a launch-contract
|
|
701
|
+
// failure above; executing it too would double-report (pre-aggregation the
|
|
702
|
+
// contract diff early-returned, so this loop could assume presence).
|
|
703
|
+
const present = new Set(Object.keys(packageScripts(cwd)).map(s => s.toLowerCase()));
|
|
688
704
|
for (const name of runnableDeclaredScripts(declared, covered)) {
|
|
705
|
+
if (!present.has(name.toLowerCase()))
|
|
706
|
+
continue;
|
|
689
707
|
const cmd = ['bun', ['run', name]];
|
|
690
708
|
const label = `${cmd[0]} ${cmd[1].join(' ')}`;
|
|
691
709
|
const r = runGateCommand(cwd, cmd, Math.min(timeoutMs, 180_000), INFRA_GAP_OUTPUT_RE);
|
|
@@ -694,10 +712,8 @@ export async function runFinalIntegrationGate(cwd, timeoutMs = 900_000, bootGrac
|
|
|
694
712
|
continue;
|
|
695
713
|
}
|
|
696
714
|
if (r.outcome === 'fail') {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
reason: `launch script: \`${label}\` exited ${r.status}${r.tail ? ` — ${r.tail}` : ''}`
|
|
700
|
-
});
|
|
715
|
+
fail(`launch script: \`${label}\` exited ${r.status}${r.tail ? ` — ${r.tail}` : ''}`);
|
|
716
|
+
continue;
|
|
701
717
|
}
|
|
702
718
|
ran.push(label);
|
|
703
719
|
}
|
|
@@ -714,6 +730,9 @@ export async function runFinalIntegrationGate(cwd, timeoutMs = 900_000, bootGrac
|
|
|
714
730
|
}
|
|
715
731
|
}
|
|
716
732
|
}
|
|
733
|
+
// Boot + render ALWAYS runs (mx5 run 13): it is independent of test results by
|
|
734
|
+
// construction, and it carries the run's most load-bearing signal — earlier
|
|
735
|
+
// failures no longer shadow it. Its failures rank FIRST in the aggregate.
|
|
717
736
|
if (boot) {
|
|
718
737
|
const label = `${boot[0]} ${boot[1].join(' ')}`;
|
|
719
738
|
const expectServer = detectsServedApp(cwd, planText);
|
|
@@ -734,21 +753,18 @@ export async function runFinalIntegrationGate(cwd, timeoutMs = 900_000, bootGrac
|
|
|
734
753
|
b = await recoverOrphanPort(cwd, boot, b, bootGraceMs, bootDepsWithRender, expectServer);
|
|
735
754
|
}
|
|
736
755
|
if (b.outcome === 'fail') {
|
|
737
|
-
|
|
756
|
+
fail(`boot check: \`${label}\` ${b.detail}`, 0);
|
|
738
757
|
}
|
|
739
|
-
if (b.outcome === 'orphan-port') {
|
|
758
|
+
else if (b.outcome === 'orphan-port') {
|
|
740
759
|
// Could not clear the port. Distinct HARNESS diagnosis, never a bare app
|
|
741
760
|
// FAIL: name the port and (when known) the process squatting on it.
|
|
742
761
|
const holder = b.port !== null ? (bootDeps.findPortHolder ?? defaultFindPortHolder)(b.port) : null;
|
|
743
762
|
const who = holder ? ` — held by an orphaned process (pid ${holder.pid}: ${holder.command})`
|
|
744
763
|
: b.port !== null ? ` — port ${b.port} is held by another process`
|
|
745
764
|
: '';
|
|
746
|
-
|
|
747
|
-
ok: false,
|
|
748
|
-
reason: `boot check: \`${label}\` could not bind: orphaned process / port already in use${who} (harness condition, not an app fault)`
|
|
749
|
-
});
|
|
765
|
+
fail(`boot check: \`${label}\` could not bind: orphaned process / port already in use${who} (harness condition, not an app fault)`, 0);
|
|
750
766
|
}
|
|
751
|
-
if (b.outcome === 'pass') {
|
|
767
|
+
else if (b.outcome === 'pass') {
|
|
752
768
|
ran.push(label);
|
|
753
769
|
// A listener that served, but whose page could not be OBSERVED to render
|
|
754
770
|
// (no browser, undeterminable port) → UNOBSERVED warning, not a silent pass.
|
|
@@ -756,6 +772,22 @@ export async function runFinalIntegrationGate(cwd, timeoutMs = 900_000, bootGrac
|
|
|
756
772
|
warnings.push(b.renderNote);
|
|
757
773
|
}
|
|
758
774
|
}
|
|
775
|
+
if (failures.length > 0) {
|
|
776
|
+
// Stable sort: boot/render (rank 0) leads, everything else keeps execution
|
|
777
|
+
// order. One failure keeps the exact single-failure wording; several become
|
|
778
|
+
// a numbered list so the trail, the ACCEPT picker, and the autofix seed all
|
|
779
|
+
// carry the complete ranked picture.
|
|
780
|
+
const texts = [...failures].sort((a, b) => a.rank - b.rank).map(f => f.text);
|
|
781
|
+
return withDebts({
|
|
782
|
+
ok: false,
|
|
783
|
+
reason: texts.length === 1 ?
|
|
784
|
+
texts[0]
|
|
785
|
+
: `${texts.length} failures (ranked, most load-bearing first):\n${texts
|
|
786
|
+
.map((t, i) => `${i + 1}. ${t}`)
|
|
787
|
+
.join('\n')}`,
|
|
788
|
+
failures: texts
|
|
789
|
+
});
|
|
790
|
+
}
|
|
759
791
|
const warningNote = warnings.length > 0 ? ` — WARNING: ${warnings.join('; WARNING: ')}` : '';
|
|
760
792
|
return withDebts({
|
|
761
793
|
ok: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.24",
|
|
4
4
|
"description": "Deterministic task planning and spec-orchestration for local models — crash-safe /task pipelines with verify/enforce gates, a real-time remote web view, and web/docs/fetch/worker subagent tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|