@coresource/hz 0.1.5 → 0.1.7
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/dist/hz.mjs +146 -71
- package/package.json +1 -1
package/dist/hz.mjs
CHANGED
|
@@ -505,6 +505,9 @@ function registerAuthCommands(program, context) {
|
|
|
505
505
|
});
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
+
// src/commands/mission-info.ts
|
|
509
|
+
import pc2 from "picocolors";
|
|
510
|
+
|
|
508
511
|
// src/commands/mission-support.ts
|
|
509
512
|
import pc from "picocolors";
|
|
510
513
|
function isRecord(value) {
|
|
@@ -720,6 +723,69 @@ function renderTable(stdout, headers, rows) {
|
|
|
720
723
|
}
|
|
721
724
|
}
|
|
722
725
|
|
|
726
|
+
// src/commands/mission-info.ts
|
|
727
|
+
function statusColor(status) {
|
|
728
|
+
switch (status) {
|
|
729
|
+
case "passed":
|
|
730
|
+
case "completed":
|
|
731
|
+
return pc2.green(status);
|
|
732
|
+
case "failed":
|
|
733
|
+
case "needs_triage":
|
|
734
|
+
return pc2.red(status);
|
|
735
|
+
case "running":
|
|
736
|
+
case "dispatched":
|
|
737
|
+
return pc2.cyan(status);
|
|
738
|
+
case "pending":
|
|
739
|
+
return pc2.dim(status);
|
|
740
|
+
default:
|
|
741
|
+
return status;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
function renderFeatures(stdout, features) {
|
|
745
|
+
if (features.length === 0) {
|
|
746
|
+
writeLine(stdout, "No features.");
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
renderTable(
|
|
750
|
+
stdout,
|
|
751
|
+
["Feature", "Milestone", "Status"],
|
|
752
|
+
features.map((f) => [
|
|
753
|
+
plainCell(f.id),
|
|
754
|
+
plainCell(f.milestone ?? "\u2014"),
|
|
755
|
+
coloredCell(statusColor(f.status), f.status)
|
|
756
|
+
])
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
function renderAssertions(stdout, assertions) {
|
|
760
|
+
if (assertions.length === 0) {
|
|
761
|
+
writeLine(stdout, "No assertions.");
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
renderTable(
|
|
765
|
+
stdout,
|
|
766
|
+
["Assertion", "Milestone", "Status"],
|
|
767
|
+
assertions.map((a) => [
|
|
768
|
+
plainCell(a.id),
|
|
769
|
+
plainCell(a.milestone ?? "\u2014"),
|
|
770
|
+
coloredCell(statusColor(a.status), a.status)
|
|
771
|
+
])
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
function registerMissionInfoCommand(mission, context, dependencies = {}) {
|
|
775
|
+
mission.command("info").description("Show features and assertions for a mission").argument("<missionId>").action(async (missionId, command) => {
|
|
776
|
+
const apiClient = await createMissionApiClient(context, command, dependencies);
|
|
777
|
+
const [features, assertions] = await Promise.all([
|
|
778
|
+
apiClient.request({ path: `/missions/${missionId}/features` }).then((value) => normalizeMissionFeatures(value)),
|
|
779
|
+
apiClient.request({ path: `/missions/${missionId}/assertions` }).then((value) => normalizeMissionAssertions(value))
|
|
780
|
+
]);
|
|
781
|
+
writeLine(context.stdout, pc2.bold("Features"));
|
|
782
|
+
renderFeatures(context.stdout, features);
|
|
783
|
+
writeLine(context.stdout);
|
|
784
|
+
writeLine(context.stdout, pc2.bold("Assertions"));
|
|
785
|
+
renderAssertions(context.stdout, assertions);
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
|
|
723
789
|
// src/commands/mission-list.ts
|
|
724
790
|
function registerMissionListCommand(mission, context, dependencies = {}) {
|
|
725
791
|
mission.command("list").description("List recent missions").action(async (_options, command) => {
|
|
@@ -758,12 +824,12 @@ function registerMissionListCommand(mission, context, dependencies = {}) {
|
|
|
758
824
|
import { readdir as readdir2, readFile as readFile3, stat } from "fs/promises";
|
|
759
825
|
import path3 from "path";
|
|
760
826
|
import { CommanderError } from "commander";
|
|
761
|
-
import
|
|
827
|
+
import pc5 from "picocolors";
|
|
762
828
|
|
|
763
829
|
// src/monitor.ts
|
|
764
830
|
import { setTimeout as delay2 } from "timers/promises";
|
|
765
831
|
import ora from "ora";
|
|
766
|
-
import
|
|
832
|
+
import pc4 from "picocolors";
|
|
767
833
|
import WebSocket from "ws";
|
|
768
834
|
|
|
769
835
|
// src/state.ts
|
|
@@ -1530,7 +1596,7 @@ import {
|
|
|
1530
1596
|
input as defaultInputPrompt,
|
|
1531
1597
|
select as defaultSelectPrompt
|
|
1532
1598
|
} from "@inquirer/prompts";
|
|
1533
|
-
import
|
|
1599
|
+
import pc3 from "picocolors";
|
|
1534
1600
|
var TRIAGE_PROGRESS_PAGE_SIZE = 200;
|
|
1535
1601
|
var AUTO_DISMISS_RATIONALE = "Auto-dismissed by `hz mission run --yes`.";
|
|
1536
1602
|
function isRecord3(value) {
|
|
@@ -1745,7 +1811,7 @@ function buildTriageItem(feature, allAssertions, suggestedActionsByFeature) {
|
|
|
1745
1811
|
}
|
|
1746
1812
|
function writeItemBlock(stdout, item) {
|
|
1747
1813
|
writeLine(stdout);
|
|
1748
|
-
writeLine(stdout,
|
|
1814
|
+
writeLine(stdout, pc3.bold(`Triage required for ${pc3.cyan(item.feature.id)}`));
|
|
1749
1815
|
writeLine(stdout, `Failure: ${item.failureDescription}`);
|
|
1750
1816
|
writeLine(
|
|
1751
1817
|
stdout,
|
|
@@ -1897,7 +1963,7 @@ async function runTriageInteraction(options) {
|
|
|
1897
1963
|
]);
|
|
1898
1964
|
const triageItems = features.filter((feature) => feature.status === "needs_triage").map((feature) => buildTriageItem(feature, assertions, suggestedActionsByFeature));
|
|
1899
1965
|
if (triageItems.length === 0) {
|
|
1900
|
-
writeLine(options.stdout,
|
|
1966
|
+
writeLine(options.stdout, pc3.dim("No outstanding triage items were found."));
|
|
1901
1967
|
return {
|
|
1902
1968
|
createdFixes: 0,
|
|
1903
1969
|
dismissed: 0,
|
|
@@ -1910,7 +1976,7 @@ async function runTriageInteraction(options) {
|
|
|
1910
1976
|
if (options.autoApprove) {
|
|
1911
1977
|
writeLine(
|
|
1912
1978
|
options.stdout,
|
|
1913
|
-
|
|
1979
|
+
pc3.yellow(
|
|
1914
1980
|
`Auto-dismissing triage items: ${triageItems.map((item) => item.feature.id).join(", ")}`
|
|
1915
1981
|
)
|
|
1916
1982
|
);
|
|
@@ -1955,7 +2021,7 @@ async function runTriageInteraction(options) {
|
|
|
1955
2021
|
});
|
|
1956
2022
|
await dismissItem(options.apiClient, options.missionId, item, rationale);
|
|
1957
2023
|
result.dismissed += 1;
|
|
1958
|
-
writeLine(options.stdout, `Dismissed triage for ${
|
|
2024
|
+
writeLine(options.stdout, `Dismissed triage for ${pc3.cyan(item.feature.id)}.`);
|
|
1959
2025
|
break;
|
|
1960
2026
|
}
|
|
1961
2027
|
case "create-fix": {
|
|
@@ -1982,7 +2048,7 @@ async function runTriageInteraction(options) {
|
|
|
1982
2048
|
result.createdFixes += 1;
|
|
1983
2049
|
writeLine(
|
|
1984
2050
|
options.stdout,
|
|
1985
|
-
`Created fix feature ${
|
|
2051
|
+
`Created fix feature ${pc3.cyan(createdFeature.id)} for ${pc3.cyan(item.feature.id)}.`
|
|
1986
2052
|
);
|
|
1987
2053
|
break;
|
|
1988
2054
|
}
|
|
@@ -1998,8 +2064,8 @@ async function runTriageInteraction(options) {
|
|
|
1998
2064
|
result.skipped += 1;
|
|
1999
2065
|
writeLine(
|
|
2000
2066
|
options.stdout,
|
|
2001
|
-
|
|
2002
|
-
`No assertions were reassigned for ${
|
|
2067
|
+
pc3.yellow(
|
|
2068
|
+
`No assertions were reassigned for ${pc3.cyan(item.feature.id)}.`
|
|
2003
2069
|
)
|
|
2004
2070
|
);
|
|
2005
2071
|
break;
|
|
@@ -2007,7 +2073,7 @@ async function runTriageInteraction(options) {
|
|
|
2007
2073
|
result.movedAssertions += moved;
|
|
2008
2074
|
writeLine(
|
|
2009
2075
|
options.stdout,
|
|
2010
|
-
`Reassigned ${moved} assertion${moved === 1 ? "" : "s"} for ${
|
|
2076
|
+
`Reassigned ${moved} assertion${moved === 1 ? "" : "s"} for ${pc3.cyan(item.feature.id)}.`
|
|
2011
2077
|
);
|
|
2012
2078
|
break;
|
|
2013
2079
|
}
|
|
@@ -2016,7 +2082,7 @@ async function runTriageInteraction(options) {
|
|
|
2016
2082
|
result.skipped += 1;
|
|
2017
2083
|
writeLine(
|
|
2018
2084
|
options.stdout,
|
|
2019
|
-
|
|
2085
|
+
pc3.dim(`Skipped triage for ${item.feature.id}; it will remain unresolved.`)
|
|
2020
2086
|
);
|
|
2021
2087
|
break;
|
|
2022
2088
|
}
|
|
@@ -2083,9 +2149,17 @@ function defaultCreateWebSocket(url, options) {
|
|
|
2083
2149
|
return new WebSocket(url, { headers: options.headers });
|
|
2084
2150
|
}
|
|
2085
2151
|
function defaultRegisterSignalHandler(signal, handler) {
|
|
2086
|
-
|
|
2152
|
+
let fired = false;
|
|
2153
|
+
const wrapper = () => {
|
|
2154
|
+
if (fired) {
|
|
2155
|
+
process.exit(130);
|
|
2156
|
+
}
|
|
2157
|
+
fired = true;
|
|
2158
|
+
handler();
|
|
2159
|
+
};
|
|
2160
|
+
process.on(signal, wrapper);
|
|
2087
2161
|
return () => {
|
|
2088
|
-
process.off(signal,
|
|
2162
|
+
process.off(signal, wrapper);
|
|
2089
2163
|
};
|
|
2090
2164
|
}
|
|
2091
2165
|
function createMissionWebSocketUrl(endpoint, missionId) {
|
|
@@ -2115,7 +2189,7 @@ function formatSummaryLine(summary) {
|
|
|
2115
2189
|
return `${summary.completedFeatures}/${summary.totalFeatures} features \xB7 ${summary.passedAssertions}/${summary.totalAssertions} assertions`;
|
|
2116
2190
|
}
|
|
2117
2191
|
function formatSpinnerText(missionId, summary) {
|
|
2118
|
-
return `${
|
|
2192
|
+
return `${pc4.cyan(missionId)} ${pc4.bold(summary.state)} \xB7 ${formatSummaryLine(summary)}`;
|
|
2119
2193
|
}
|
|
2120
2194
|
function normalizeSummary(missionId, value) {
|
|
2121
2195
|
if (!isRecord4(value)) {
|
|
@@ -2178,13 +2252,13 @@ function buildBackfillDescription(event) {
|
|
|
2178
2252
|
const milestone = asNonEmptyString4(data.milestone);
|
|
2179
2253
|
const assertionId = asNonEmptyString4(data.assertionId);
|
|
2180
2254
|
if (featureId) {
|
|
2181
|
-
return `Backfilled ${type} for ${
|
|
2255
|
+
return `Backfilled ${type} for ${pc4.cyan(featureId)}.`;
|
|
2182
2256
|
}
|
|
2183
2257
|
if (assertionId) {
|
|
2184
|
-
return `Backfilled ${type} for ${
|
|
2258
|
+
return `Backfilled ${type} for ${pc4.cyan(assertionId)}.`;
|
|
2185
2259
|
}
|
|
2186
2260
|
if (milestone) {
|
|
2187
|
-
return `Backfilled ${type} for milestone ${
|
|
2261
|
+
return `Backfilled ${type} for milestone ${pc4.cyan(milestone)}.`;
|
|
2188
2262
|
}
|
|
2189
2263
|
return `Backfilled ${type}.`;
|
|
2190
2264
|
}
|
|
@@ -2245,7 +2319,7 @@ async function monitorMission(options) {
|
|
|
2245
2319
|
spinner.start();
|
|
2246
2320
|
writeLine(
|
|
2247
2321
|
options.stdout,
|
|
2248
|
-
`Monitoring mission ${
|
|
2322
|
+
`Monitoring mission ${pc4.cyan(options.missionId)} \xB7 ${formatSummaryLine(summary)}`
|
|
2249
2323
|
);
|
|
2250
2324
|
const updateSpinner = () => {
|
|
2251
2325
|
spinner.text = formatSpinnerText(options.missionId, summary);
|
|
@@ -2260,7 +2334,7 @@ async function monitorMission(options) {
|
|
|
2260
2334
|
await refreshSummary();
|
|
2261
2335
|
const message = `Mission completed \xB7 ${formatSummaryLine(summary)}`;
|
|
2262
2336
|
spinner.succeed(message);
|
|
2263
|
-
writeLine(options.stdout,
|
|
2337
|
+
writeLine(options.stdout, pc4.green(message));
|
|
2264
2338
|
return {
|
|
2265
2339
|
exitCode: 0,
|
|
2266
2340
|
reason: "completed",
|
|
@@ -2272,7 +2346,7 @@ async function monitorMission(options) {
|
|
|
2272
2346
|
await refreshSummary();
|
|
2273
2347
|
const message = `Budget exceeded \xB7 ${formatBudgetUsage(details)}`;
|
|
2274
2348
|
spinner.fail(message);
|
|
2275
|
-
writeLine(options.stdout,
|
|
2349
|
+
writeLine(options.stdout, pc4.red(message));
|
|
2276
2350
|
return {
|
|
2277
2351
|
exitCode: 1,
|
|
2278
2352
|
reason: "budget_exceeded",
|
|
@@ -2296,12 +2370,12 @@ async function monitorMission(options) {
|
|
|
2296
2370
|
if (triageResult.totalItems === 0) {
|
|
2297
2371
|
writeLine(
|
|
2298
2372
|
options.stdout,
|
|
2299
|
-
|
|
2373
|
+
pc4.dim("No triage items required action. Resuming the orchestration loop.")
|
|
2300
2374
|
);
|
|
2301
2375
|
} else {
|
|
2302
2376
|
writeLine(
|
|
2303
2377
|
options.stdout,
|
|
2304
|
-
|
|
2378
|
+
pc4.green(
|
|
2305
2379
|
`Triage complete \xB7 ${triageResult.dismissed} dismissed \xB7 ${triageResult.createdFixes} fix features \xB7 ${triageResult.movedAssertions} assertions moved \xB7 ${triageResult.skipped} skipped`
|
|
2306
2380
|
)
|
|
2307
2381
|
);
|
|
@@ -2317,7 +2391,7 @@ async function monitorMission(options) {
|
|
|
2317
2391
|
spinner.fail("Monitoring stopped.");
|
|
2318
2392
|
writeLine(
|
|
2319
2393
|
options.stdout,
|
|
2320
|
-
|
|
2394
|
+
pc4.yellow("Interrupted. Local state is saved. Remote mission continues.")
|
|
2321
2395
|
);
|
|
2322
2396
|
return {
|
|
2323
2397
|
exitCode: 130,
|
|
@@ -2336,13 +2410,13 @@ async function monitorMission(options) {
|
|
|
2336
2410
|
case "dispatched":
|
|
2337
2411
|
writeLine(
|
|
2338
2412
|
options.stdout,
|
|
2339
|
-
`Dispatching ${
|
|
2413
|
+
`Dispatching ${pc4.cyan(result.featureId)} (${pc4.dim(result.workerSessionId)}).`
|
|
2340
2414
|
);
|
|
2341
2415
|
return null;
|
|
2342
2416
|
case "no_work":
|
|
2343
2417
|
writeLine(
|
|
2344
2418
|
options.stdout,
|
|
2345
|
-
|
|
2419
|
+
pc4.dim("No eligible work is available yet. Waiting for updates.")
|
|
2346
2420
|
);
|
|
2347
2421
|
return null;
|
|
2348
2422
|
case "completed":
|
|
@@ -2383,7 +2457,7 @@ async function monitorMission(options) {
|
|
|
2383
2457
|
reconnectAttempt += 1;
|
|
2384
2458
|
writeLine(
|
|
2385
2459
|
options.stdout,
|
|
2386
|
-
|
|
2460
|
+
pc4.yellow(
|
|
2387
2461
|
`WebSocket connection failed: ${error instanceof Error ? error.message : String(error)}. Reconnecting in ${delayMs2} ms...`
|
|
2388
2462
|
)
|
|
2389
2463
|
);
|
|
@@ -2418,7 +2492,7 @@ async function monitorMission(options) {
|
|
|
2418
2492
|
switch (type) {
|
|
2419
2493
|
case "state_changed": {
|
|
2420
2494
|
const state = asNonEmptyString4(data.state) ?? "unknown";
|
|
2421
|
-
writeLine(options.stdout, `Mission state \u2192 ${
|
|
2495
|
+
writeLine(options.stdout, `Mission state \u2192 ${pc4.cyan(state)}`);
|
|
2422
2496
|
await refreshSummary();
|
|
2423
2497
|
if (state === "completed") {
|
|
2424
2498
|
return completedResult();
|
|
@@ -2436,7 +2510,7 @@ async function monitorMission(options) {
|
|
|
2436
2510
|
}
|
|
2437
2511
|
writeLine(
|
|
2438
2512
|
options.stdout,
|
|
2439
|
-
`Feature ${
|
|
2513
|
+
`Feature ${pc4.cyan(featureId)} \u2192 ${pc4.bold(status)}`
|
|
2440
2514
|
);
|
|
2441
2515
|
await refreshSummary();
|
|
2442
2516
|
return null;
|
|
@@ -2446,7 +2520,7 @@ async function monitorMission(options) {
|
|
|
2446
2520
|
const status = asNonEmptyString4(data.status) ?? "updated";
|
|
2447
2521
|
writeLine(
|
|
2448
2522
|
options.stdout,
|
|
2449
|
-
`Milestone ${
|
|
2523
|
+
`Milestone ${pc4.cyan(milestone)} \u2192 ${pc4.bold(status)}`
|
|
2450
2524
|
);
|
|
2451
2525
|
await refreshSummary();
|
|
2452
2526
|
return null;
|
|
@@ -2456,7 +2530,7 @@ async function monitorMission(options) {
|
|
|
2456
2530
|
const status = asNonEmptyString4(data.status) ?? "updated";
|
|
2457
2531
|
writeLine(
|
|
2458
2532
|
options.stdout,
|
|
2459
|
-
`Assertion ${
|
|
2533
|
+
`Assertion ${pc4.cyan(assertionId)} \u2192 ${pc4.bold(status)}`
|
|
2460
2534
|
);
|
|
2461
2535
|
await refreshSummary();
|
|
2462
2536
|
return null;
|
|
@@ -2466,7 +2540,7 @@ async function monitorMission(options) {
|
|
|
2466
2540
|
const status = asNonEmptyString4(data.status) ?? "received";
|
|
2467
2541
|
writeLine(
|
|
2468
2542
|
options.stdout,
|
|
2469
|
-
`Handoff received for ${
|
|
2543
|
+
`Handoff received for ${pc4.cyan(featureId)} (${status}).`
|
|
2470
2544
|
);
|
|
2471
2545
|
return null;
|
|
2472
2546
|
}
|
|
@@ -2475,8 +2549,8 @@ async function monitorMission(options) {
|
|
|
2475
2549
|
const featureId = asNonEmptyString4(data.featureId) ?? "feature";
|
|
2476
2550
|
writeLine(
|
|
2477
2551
|
options.stdout,
|
|
2478
|
-
|
|
2479
|
-
`Triage is required for ${
|
|
2552
|
+
pc4.yellow(
|
|
2553
|
+
`Triage is required for ${pc4.cyan(featureId)}. Auto-start is paused.`
|
|
2480
2554
|
)
|
|
2481
2555
|
);
|
|
2482
2556
|
return completeTriage();
|
|
@@ -2485,7 +2559,7 @@ async function monitorMission(options) {
|
|
|
2485
2559
|
const featureId = asNonEmptyString4(data.featureId) ?? "feature";
|
|
2486
2560
|
writeLine(
|
|
2487
2561
|
options.stdout,
|
|
2488
|
-
|
|
2562
|
+
pc4.yellow(`Worker timed out for ${pc4.cyan(featureId)}.`)
|
|
2489
2563
|
);
|
|
2490
2564
|
await refreshSummary();
|
|
2491
2565
|
return null;
|
|
@@ -2494,7 +2568,7 @@ async function monitorMission(options) {
|
|
|
2494
2568
|
const details = normalizeBudgetDetails(data);
|
|
2495
2569
|
writeLine(
|
|
2496
2570
|
options.stdout,
|
|
2497
|
-
|
|
2571
|
+
pc4.yellow(`Budget warning \xB7 ${formatBudgetUsage(details)}`)
|
|
2498
2572
|
);
|
|
2499
2573
|
await refreshSummary();
|
|
2500
2574
|
return null;
|
|
@@ -2520,7 +2594,7 @@ async function monitorMission(options) {
|
|
|
2520
2594
|
}).catch((error) => {
|
|
2521
2595
|
writeLine(
|
|
2522
2596
|
options.stdout,
|
|
2523
|
-
|
|
2597
|
+
pc4.yellow(
|
|
2524
2598
|
`Monitoring update failed: ${error instanceof Error ? error.message : String(error)}. Reconnecting...`
|
|
2525
2599
|
)
|
|
2526
2600
|
);
|
|
@@ -2531,7 +2605,7 @@ async function monitorMission(options) {
|
|
|
2531
2605
|
socket.on("error", (error) => {
|
|
2532
2606
|
writeLine(
|
|
2533
2607
|
options.stdout,
|
|
2534
|
-
|
|
2608
|
+
pc4.yellow(
|
|
2535
2609
|
`WebSocket error: ${error instanceof Error ? error.message : String(error)}`
|
|
2536
2610
|
)
|
|
2537
2611
|
);
|
|
@@ -2559,7 +2633,7 @@ async function monitorMission(options) {
|
|
|
2559
2633
|
continue;
|
|
2560
2634
|
}
|
|
2561
2635
|
lastSeen = Math.max(lastSeen, asFiniteNumber3(event.seq));
|
|
2562
|
-
writeLine(options.stdout,
|
|
2636
|
+
writeLine(options.stdout, pc4.dim(buildBackfillDescription(event)));
|
|
2563
2637
|
}
|
|
2564
2638
|
}
|
|
2565
2639
|
connectedOnce = true;
|
|
@@ -2583,7 +2657,7 @@ async function monitorMission(options) {
|
|
|
2583
2657
|
reconnectAttempt += 1;
|
|
2584
2658
|
writeLine(
|
|
2585
2659
|
options.stdout,
|
|
2586
|
-
|
|
2660
|
+
pc4.yellow(`Reconnecting to mission updates in ${delayMs} ms...`)
|
|
2587
2661
|
);
|
|
2588
2662
|
await sleep(delayMs);
|
|
2589
2663
|
}
|
|
@@ -2599,7 +2673,7 @@ function asNonEmptyString5(value) {
|
|
|
2599
2673
|
}
|
|
2600
2674
|
function writeSection(stdout, title) {
|
|
2601
2675
|
writeLine(stdout);
|
|
2602
|
-
writeLine(stdout,
|
|
2676
|
+
writeLine(stdout, pc5.bold(title));
|
|
2603
2677
|
}
|
|
2604
2678
|
function summarizeAssertions(assertions) {
|
|
2605
2679
|
return assertions.reduce(
|
|
@@ -2744,7 +2818,7 @@ async function resolveMissionId(missionId, context, command, homeDir) {
|
|
|
2744
2818
|
if (detectedMissionId) {
|
|
2745
2819
|
writeLine(
|
|
2746
2820
|
context.stdout,
|
|
2747
|
-
`Using the most recent local mission: ${
|
|
2821
|
+
`Using the most recent local mission: ${pc5.cyan(detectedMissionId)}`
|
|
2748
2822
|
);
|
|
2749
2823
|
return detectedMissionId;
|
|
2750
2824
|
}
|
|
@@ -2786,13 +2860,13 @@ async function runLifecycleMutation(action, missionIdValue, command, context, de
|
|
|
2786
2860
|
if (action === "pause") {
|
|
2787
2861
|
writeLine(
|
|
2788
2862
|
context.stdout,
|
|
2789
|
-
`Mission ${
|
|
2863
|
+
`Mission ${pc5.cyan(missionId)} paused. Current state: ${pc5.bold(nextState)}.`
|
|
2790
2864
|
);
|
|
2791
2865
|
return;
|
|
2792
2866
|
}
|
|
2793
2867
|
writeLine(
|
|
2794
2868
|
context.stdout,
|
|
2795
|
-
`Mission ${
|
|
2869
|
+
`Mission ${pc5.cyan(missionId)} cancelled. This action is irreversible. Current state: ${pc5.bold(nextState)}.`
|
|
2796
2870
|
);
|
|
2797
2871
|
} catch (error) {
|
|
2798
2872
|
if (!(error instanceof ApiError) || error.status !== 409) {
|
|
@@ -2806,7 +2880,7 @@ async function runLifecycleMutation(action, missionIdValue, command, context, de
|
|
|
2806
2880
|
const verb = action === "pause" ? "paused" : "cancelled";
|
|
2807
2881
|
writeLine(
|
|
2808
2882
|
context.stdout,
|
|
2809
|
-
`Mission ${
|
|
2883
|
+
`Mission ${pc5.cyan(missionId)} cannot be ${verb} because it is currently ${pc5.bold(missionState.state)}.`
|
|
2810
2884
|
);
|
|
2811
2885
|
throw new CommanderError(1, `mission-${action}`, "");
|
|
2812
2886
|
}
|
|
@@ -2842,7 +2916,7 @@ async function runMissionResume(missionIdValue, command, context, dependencies)
|
|
|
2842
2916
|
const nextState = asNonEmptyString5(response.state) ?? "orchestrator_turn";
|
|
2843
2917
|
writeLine(
|
|
2844
2918
|
context.stdout,
|
|
2845
|
-
`Mission ${
|
|
2919
|
+
`Mission ${pc5.cyan(missionId)} resumed. Current state: ${pc5.bold(nextState)}.`
|
|
2846
2920
|
);
|
|
2847
2921
|
} catch (error) {
|
|
2848
2922
|
if (!(error instanceof ApiError) || error.status !== 409) {
|
|
@@ -2855,14 +2929,14 @@ async function runMissionResume(missionIdValue, command, context, dependencies)
|
|
|
2855
2929
|
);
|
|
2856
2930
|
writeLine(
|
|
2857
2931
|
context.stdout,
|
|
2858
|
-
`Mission ${
|
|
2932
|
+
`Mission ${pc5.cyan(missionId)} is currently ${pc5.bold(snapshot.state.state)}. Reconnecting to live updates if available.`
|
|
2859
2933
|
);
|
|
2860
2934
|
}
|
|
2861
2935
|
snapshot = await fetchMissionSnapshot(apiClient, missionId);
|
|
2862
2936
|
} else if (snapshot.state.state !== "cancelled" && snapshot.state.state !== "completed") {
|
|
2863
2937
|
writeLine(
|
|
2864
2938
|
context.stdout,
|
|
2865
|
-
`Reconnecting to mission ${
|
|
2939
|
+
`Reconnecting to mission ${pc5.cyan(missionId)} from state ${pc5.bold(snapshot.state.state)}.`
|
|
2866
2940
|
);
|
|
2867
2941
|
}
|
|
2868
2942
|
renderMissionSnapshot(context.stdout, snapshot);
|
|
@@ -2955,7 +3029,7 @@ import {
|
|
|
2955
3029
|
select as defaultSelectPrompt2
|
|
2956
3030
|
} from "@inquirer/prompts";
|
|
2957
3031
|
import ora2 from "ora";
|
|
2958
|
-
import
|
|
3032
|
+
import pc6 from "picocolors";
|
|
2959
3033
|
var DEFAULT_ANALYSIS_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
2960
3034
|
var DEFAULT_POLL_INTERVAL_MS = 5e3;
|
|
2961
3035
|
var FREE_TEXT_OPTION_ID = "free_text";
|
|
@@ -3122,18 +3196,18 @@ async function postDraftApproval(client, sessionId) {
|
|
|
3122
3196
|
}
|
|
3123
3197
|
function writeSection2(stdout, title) {
|
|
3124
3198
|
writeLine(stdout);
|
|
3125
|
-
writeLine(stdout,
|
|
3199
|
+
writeLine(stdout, pc6.bold(title));
|
|
3126
3200
|
}
|
|
3127
3201
|
function renderQuestion(stdout, question, index) {
|
|
3128
3202
|
writeLine(stdout, `${index + 1}. ${question.text}`);
|
|
3129
3203
|
if (question.references && question.references.length > 0) {
|
|
3130
|
-
writeLine(stdout, ` ${
|
|
3204
|
+
writeLine(stdout, ` ${pc6.dim(`References: ${question.references.join(", ")}`)}`);
|
|
3131
3205
|
}
|
|
3132
3206
|
}
|
|
3133
3207
|
function renderMilestones(stdout, milestones, reviewRound) {
|
|
3134
3208
|
writeSection2(stdout, `Milestone review round ${reviewRound}`);
|
|
3135
3209
|
milestones.forEach((milestone, index) => {
|
|
3136
|
-
writeLine(stdout, `${index + 1}. ${
|
|
3210
|
+
writeLine(stdout, `${index + 1}. ${pc6.cyan(milestone.name)}`);
|
|
3137
3211
|
if (milestone.description) {
|
|
3138
3212
|
writeLine(stdout, ` ${milestone.description}`);
|
|
3139
3213
|
}
|
|
@@ -3283,7 +3357,7 @@ async function resolveClarification(options, sessionId, initialPayload) {
|
|
|
3283
3357
|
}
|
|
3284
3358
|
writeLine(
|
|
3285
3359
|
options.stdout,
|
|
3286
|
-
|
|
3360
|
+
pc6.yellow("Proceeding because --force is enabled.")
|
|
3287
3361
|
);
|
|
3288
3362
|
payload = await postMilestoneConfirmation(options.client, sessionId, {});
|
|
3289
3363
|
return {
|
|
@@ -3365,7 +3439,7 @@ async function runPlanningFlow(options) {
|
|
|
3365
3439
|
if (options.existingMissionDraft) {
|
|
3366
3440
|
writeLine(
|
|
3367
3441
|
options.stdout,
|
|
3368
|
-
|
|
3442
|
+
pc6.cyan("Skipping planning because mission-draft.json already exists.")
|
|
3369
3443
|
);
|
|
3370
3444
|
return {
|
|
3371
3445
|
cancelled: false,
|
|
@@ -3421,7 +3495,7 @@ async function runPlanningFlow(options) {
|
|
|
3421
3495
|
message: "Approve this draft and continue to upload?"
|
|
3422
3496
|
});
|
|
3423
3497
|
if (!approved) {
|
|
3424
|
-
writeLine(options.stdout,
|
|
3498
|
+
writeLine(options.stdout, pc6.yellow("Planning cancelled before upload."));
|
|
3425
3499
|
return {
|
|
3426
3500
|
cancelled: true,
|
|
3427
3501
|
draft,
|
|
@@ -3430,7 +3504,7 @@ async function runPlanningFlow(options) {
|
|
|
3430
3504
|
};
|
|
3431
3505
|
}
|
|
3432
3506
|
await options.persistMissionDraft(draft);
|
|
3433
|
-
writeLine(options.stdout,
|
|
3507
|
+
writeLine(options.stdout, pc6.green("Saved approved mission draft."));
|
|
3434
3508
|
return {
|
|
3435
3509
|
cancelled: false,
|
|
3436
3510
|
draft,
|
|
@@ -3445,7 +3519,7 @@ import { createReadStream as createReadStream2 } from "fs";
|
|
|
3445
3519
|
import path5 from "path";
|
|
3446
3520
|
import { Transform as Transform2 } from "stream";
|
|
3447
3521
|
import ora3 from "ora";
|
|
3448
|
-
import
|
|
3522
|
+
import pc7 from "picocolors";
|
|
3449
3523
|
|
|
3450
3524
|
// src/snapshot.ts
|
|
3451
3525
|
import { execFile } from "child_process";
|
|
@@ -4042,15 +4116,15 @@ function isTerminalUploadStatus2(status) {
|
|
|
4042
4116
|
return status === "uploaded" || status === "blob_exists";
|
|
4043
4117
|
}
|
|
4044
4118
|
function renderSafetyGate(stdout, repos, existingBlobs) {
|
|
4045
|
-
writeLine(stdout,
|
|
4119
|
+
writeLine(stdout, pc7.bold("Upload safety check"));
|
|
4046
4120
|
writeLine(stdout, "Repos queued for upload:");
|
|
4047
4121
|
for (const repo of repos) {
|
|
4048
|
-
const cleanliness = repo.manifest.dirty ?
|
|
4049
|
-
const secretSummary = repo.secretFindings.length > 0 ?
|
|
4050
|
-
const dedupSummary = existingBlobs.has(repo.manifest.archiveSha256) ?
|
|
4122
|
+
const cleanliness = repo.manifest.dirty ? pc7.yellow("dirty") : pc7.green("clean");
|
|
4123
|
+
const secretSummary = repo.secretFindings.length > 0 ? pc7.red(`${repo.secretFindings.length} finding(s)`) : pc7.green("no findings");
|
|
4124
|
+
const dedupSummary = existingBlobs.has(repo.manifest.archiveSha256) ? pc7.cyan("blob already exists remotely") : "new upload";
|
|
4051
4125
|
writeLine(
|
|
4052
4126
|
stdout,
|
|
4053
|
-
`- ${
|
|
4127
|
+
`- ${pc7.cyan(repo.manifest.repoId)} \u2014 ${repo.manifest.localPath}`
|
|
4054
4128
|
);
|
|
4055
4129
|
writeLine(
|
|
4056
4130
|
stdout,
|
|
@@ -4194,7 +4268,7 @@ async function processRepoUpload(options, repo, remoteLaunchId) {
|
|
|
4194
4268
|
);
|
|
4195
4269
|
writeLine(
|
|
4196
4270
|
options.stdout,
|
|
4197
|
-
|
|
4271
|
+
pc7.cyan(`Skipping upload for ${repo.manifest.repoId}; blob already exists remotely.`)
|
|
4198
4272
|
);
|
|
4199
4273
|
return;
|
|
4200
4274
|
}
|
|
@@ -4288,7 +4362,7 @@ async function runUploadPipeline(options) {
|
|
|
4288
4362
|
})) {
|
|
4289
4363
|
writeLine(
|
|
4290
4364
|
options.stdout,
|
|
4291
|
-
|
|
4365
|
+
pc7.yellow("Upload cancelled before creating a remote launch.")
|
|
4292
4366
|
);
|
|
4293
4367
|
return {
|
|
4294
4368
|
finalized: false,
|
|
@@ -4305,7 +4379,7 @@ async function runUploadPipeline(options) {
|
|
|
4305
4379
|
if (existingBlobs.size > 0) {
|
|
4306
4380
|
writeLine(
|
|
4307
4381
|
options.stdout,
|
|
4308
|
-
|
|
4382
|
+
pc7.cyan(
|
|
4309
4383
|
`Remote dedup will reuse ${existingBlobs.size} existing blob${existingBlobs.size === 1 ? "" : "s"}.`
|
|
4310
4384
|
)
|
|
4311
4385
|
);
|
|
@@ -4332,7 +4406,7 @@ async function runUploadPipeline(options) {
|
|
|
4332
4406
|
if (isTerminalUploadStatus2(uploadStates.get(repo.manifest.repoId))) {
|
|
4333
4407
|
writeLine(
|
|
4334
4408
|
options.stdout,
|
|
4335
|
-
|
|
4409
|
+
pc7.cyan(`Skipping ${repo.manifest.repoId}; already completed in upload-state.json.`)
|
|
4336
4410
|
);
|
|
4337
4411
|
continue;
|
|
4338
4412
|
}
|
|
@@ -4350,7 +4424,7 @@ async function runUploadPipeline(options) {
|
|
|
4350
4424
|
failures.push(repo.manifest.repoId);
|
|
4351
4425
|
writeLine(
|
|
4352
4426
|
options.stdout,
|
|
4353
|
-
|
|
4427
|
+
pc7.red(
|
|
4354
4428
|
error instanceof Error ? error.message : `Upload failed for ${repo.manifest.repoId}.`
|
|
4355
4429
|
)
|
|
4356
4430
|
);
|
|
@@ -4372,7 +4446,7 @@ async function runUploadPipeline(options) {
|
|
|
4372
4446
|
}
|
|
4373
4447
|
writeLine(
|
|
4374
4448
|
options.stdout,
|
|
4375
|
-
|
|
4449
|
+
pc7.green(`Upload complete. Remote launch ${remoteLaunchId} is finalized.`)
|
|
4376
4450
|
);
|
|
4377
4451
|
return {
|
|
4378
4452
|
finalized: true,
|
|
@@ -4547,10 +4621,10 @@ function registerMissionRunCommand(mission, context, dependencies = {}) {
|
|
|
4547
4621
|
}
|
|
4548
4622
|
|
|
4549
4623
|
// src/commands/mission-status.ts
|
|
4550
|
-
import
|
|
4624
|
+
import pc8 from "picocolors";
|
|
4551
4625
|
function writeSection3(stdout, title) {
|
|
4552
4626
|
writeLine(stdout);
|
|
4553
|
-
writeLine(stdout,
|
|
4627
|
+
writeLine(stdout, pc8.bold(title));
|
|
4554
4628
|
}
|
|
4555
4629
|
function normalizeMilestoneOrder(milestones, features, assertions) {
|
|
4556
4630
|
const known = new Map(milestones.map((milestone) => [milestone.name, milestone]));
|
|
@@ -4650,7 +4724,7 @@ function renderMilestones2(stdout, milestones, features) {
|
|
|
4650
4724
|
for (const milestone of milestones) {
|
|
4651
4725
|
writeLine(
|
|
4652
4726
|
stdout,
|
|
4653
|
-
`- ${
|
|
4727
|
+
`- ${pc8.cyan(milestone.name)} (${milestone.state}) \xB7 ${milestone.completedFeatureCount}/${milestone.featureCount} features \xB7 ${milestone.passedAssertionCount}/${milestone.assertionCount} assertions`
|
|
4654
4728
|
);
|
|
4655
4729
|
const milestoneFeatures = features.filter(
|
|
4656
4730
|
(feature) => feature.milestone === milestone.name
|
|
@@ -4710,6 +4784,7 @@ function registerMissionCommands(program, context, dependencies = {}) {
|
|
|
4710
4784
|
const mission = program.command("mission").description("Manage missions");
|
|
4711
4785
|
registerMissionRunCommand(mission, context, dependencies);
|
|
4712
4786
|
registerMissionStatusCommand(mission, context, dependencies);
|
|
4787
|
+
registerMissionInfoCommand(mission, context, dependencies);
|
|
4713
4788
|
registerMissionListCommand(mission, context, dependencies);
|
|
4714
4789
|
registerMissionLifecycleCommands(mission, context, dependencies);
|
|
4715
4790
|
}
|