@coresource/hz 0.1.6 → 0.1.8
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 +138 -69
- 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
|
}
|
|
@@ -2123,7 +2189,7 @@ function formatSummaryLine(summary) {
|
|
|
2123
2189
|
return `${summary.completedFeatures}/${summary.totalFeatures} features \xB7 ${summary.passedAssertions}/${summary.totalAssertions} assertions`;
|
|
2124
2190
|
}
|
|
2125
2191
|
function formatSpinnerText(missionId, summary) {
|
|
2126
|
-
return `${
|
|
2192
|
+
return `${pc4.cyan(missionId)} ${pc4.bold(summary.state)} \xB7 ${formatSummaryLine(summary)}`;
|
|
2127
2193
|
}
|
|
2128
2194
|
function normalizeSummary(missionId, value) {
|
|
2129
2195
|
if (!isRecord4(value)) {
|
|
@@ -2186,13 +2252,13 @@ function buildBackfillDescription(event) {
|
|
|
2186
2252
|
const milestone = asNonEmptyString4(data.milestone);
|
|
2187
2253
|
const assertionId = asNonEmptyString4(data.assertionId);
|
|
2188
2254
|
if (featureId) {
|
|
2189
|
-
return `Backfilled ${type} for ${
|
|
2255
|
+
return `Backfilled ${type} for ${pc4.cyan(featureId)}.`;
|
|
2190
2256
|
}
|
|
2191
2257
|
if (assertionId) {
|
|
2192
|
-
return `Backfilled ${type} for ${
|
|
2258
|
+
return `Backfilled ${type} for ${pc4.cyan(assertionId)}.`;
|
|
2193
2259
|
}
|
|
2194
2260
|
if (milestone) {
|
|
2195
|
-
return `Backfilled ${type} for milestone ${
|
|
2261
|
+
return `Backfilled ${type} for milestone ${pc4.cyan(milestone)}.`;
|
|
2196
2262
|
}
|
|
2197
2263
|
return `Backfilled ${type}.`;
|
|
2198
2264
|
}
|
|
@@ -2253,7 +2319,7 @@ async function monitorMission(options) {
|
|
|
2253
2319
|
spinner.start();
|
|
2254
2320
|
writeLine(
|
|
2255
2321
|
options.stdout,
|
|
2256
|
-
`Monitoring mission ${
|
|
2322
|
+
`Monitoring mission ${pc4.cyan(options.missionId)} \xB7 ${formatSummaryLine(summary)}`
|
|
2257
2323
|
);
|
|
2258
2324
|
const updateSpinner = () => {
|
|
2259
2325
|
spinner.text = formatSpinnerText(options.missionId, summary);
|
|
@@ -2268,7 +2334,7 @@ async function monitorMission(options) {
|
|
|
2268
2334
|
await refreshSummary();
|
|
2269
2335
|
const message = `Mission completed \xB7 ${formatSummaryLine(summary)}`;
|
|
2270
2336
|
spinner.succeed(message);
|
|
2271
|
-
writeLine(options.stdout,
|
|
2337
|
+
writeLine(options.stdout, pc4.green(message));
|
|
2272
2338
|
return {
|
|
2273
2339
|
exitCode: 0,
|
|
2274
2340
|
reason: "completed",
|
|
@@ -2280,7 +2346,7 @@ async function monitorMission(options) {
|
|
|
2280
2346
|
await refreshSummary();
|
|
2281
2347
|
const message = `Budget exceeded \xB7 ${formatBudgetUsage(details)}`;
|
|
2282
2348
|
spinner.fail(message);
|
|
2283
|
-
writeLine(options.stdout,
|
|
2349
|
+
writeLine(options.stdout, pc4.red(message));
|
|
2284
2350
|
return {
|
|
2285
2351
|
exitCode: 1,
|
|
2286
2352
|
reason: "budget_exceeded",
|
|
@@ -2304,12 +2370,12 @@ async function monitorMission(options) {
|
|
|
2304
2370
|
if (triageResult.totalItems === 0) {
|
|
2305
2371
|
writeLine(
|
|
2306
2372
|
options.stdout,
|
|
2307
|
-
|
|
2373
|
+
pc4.dim("No triage items required action. Resuming the orchestration loop.")
|
|
2308
2374
|
);
|
|
2309
2375
|
} else {
|
|
2310
2376
|
writeLine(
|
|
2311
2377
|
options.stdout,
|
|
2312
|
-
|
|
2378
|
+
pc4.green(
|
|
2313
2379
|
`Triage complete \xB7 ${triageResult.dismissed} dismissed \xB7 ${triageResult.createdFixes} fix features \xB7 ${triageResult.movedAssertions} assertions moved \xB7 ${triageResult.skipped} skipped`
|
|
2314
2380
|
)
|
|
2315
2381
|
);
|
|
@@ -2325,7 +2391,7 @@ async function monitorMission(options) {
|
|
|
2325
2391
|
spinner.fail("Monitoring stopped.");
|
|
2326
2392
|
writeLine(
|
|
2327
2393
|
options.stdout,
|
|
2328
|
-
|
|
2394
|
+
pc4.yellow("Interrupted. Local state is saved. Remote mission continues.")
|
|
2329
2395
|
);
|
|
2330
2396
|
return {
|
|
2331
2397
|
exitCode: 130,
|
|
@@ -2344,13 +2410,13 @@ async function monitorMission(options) {
|
|
|
2344
2410
|
case "dispatched":
|
|
2345
2411
|
writeLine(
|
|
2346
2412
|
options.stdout,
|
|
2347
|
-
`Dispatching ${
|
|
2413
|
+
`Dispatching ${pc4.cyan(result.featureId)} (${pc4.dim(result.workerSessionId)}).`
|
|
2348
2414
|
);
|
|
2349
2415
|
return null;
|
|
2350
2416
|
case "no_work":
|
|
2351
2417
|
writeLine(
|
|
2352
2418
|
options.stdout,
|
|
2353
|
-
|
|
2419
|
+
pc4.dim("No eligible work is available yet. Waiting for updates.")
|
|
2354
2420
|
);
|
|
2355
2421
|
return null;
|
|
2356
2422
|
case "completed":
|
|
@@ -2391,7 +2457,7 @@ async function monitorMission(options) {
|
|
|
2391
2457
|
reconnectAttempt += 1;
|
|
2392
2458
|
writeLine(
|
|
2393
2459
|
options.stdout,
|
|
2394
|
-
|
|
2460
|
+
pc4.yellow(
|
|
2395
2461
|
`WebSocket connection failed: ${error instanceof Error ? error.message : String(error)}. Reconnecting in ${delayMs2} ms...`
|
|
2396
2462
|
)
|
|
2397
2463
|
);
|
|
@@ -2426,7 +2492,7 @@ async function monitorMission(options) {
|
|
|
2426
2492
|
switch (type) {
|
|
2427
2493
|
case "state_changed": {
|
|
2428
2494
|
const state = asNonEmptyString4(data.state) ?? "unknown";
|
|
2429
|
-
writeLine(options.stdout, `Mission state \u2192 ${
|
|
2495
|
+
writeLine(options.stdout, `Mission state \u2192 ${pc4.cyan(state)}`);
|
|
2430
2496
|
await refreshSummary();
|
|
2431
2497
|
if (state === "completed") {
|
|
2432
2498
|
return completedResult();
|
|
@@ -2444,7 +2510,7 @@ async function monitorMission(options) {
|
|
|
2444
2510
|
}
|
|
2445
2511
|
writeLine(
|
|
2446
2512
|
options.stdout,
|
|
2447
|
-
`Feature ${
|
|
2513
|
+
`Feature ${pc4.cyan(featureId)} \u2192 ${pc4.bold(status)}`
|
|
2448
2514
|
);
|
|
2449
2515
|
await refreshSummary();
|
|
2450
2516
|
return null;
|
|
@@ -2454,7 +2520,7 @@ async function monitorMission(options) {
|
|
|
2454
2520
|
const status = asNonEmptyString4(data.status) ?? "updated";
|
|
2455
2521
|
writeLine(
|
|
2456
2522
|
options.stdout,
|
|
2457
|
-
`Milestone ${
|
|
2523
|
+
`Milestone ${pc4.cyan(milestone)} \u2192 ${pc4.bold(status)}`
|
|
2458
2524
|
);
|
|
2459
2525
|
await refreshSummary();
|
|
2460
2526
|
return null;
|
|
@@ -2464,7 +2530,7 @@ async function monitorMission(options) {
|
|
|
2464
2530
|
const status = asNonEmptyString4(data.status) ?? "updated";
|
|
2465
2531
|
writeLine(
|
|
2466
2532
|
options.stdout,
|
|
2467
|
-
`Assertion ${
|
|
2533
|
+
`Assertion ${pc4.cyan(assertionId)} \u2192 ${pc4.bold(status)}`
|
|
2468
2534
|
);
|
|
2469
2535
|
await refreshSummary();
|
|
2470
2536
|
return null;
|
|
@@ -2474,7 +2540,7 @@ async function monitorMission(options) {
|
|
|
2474
2540
|
const status = asNonEmptyString4(data.status) ?? "received";
|
|
2475
2541
|
writeLine(
|
|
2476
2542
|
options.stdout,
|
|
2477
|
-
`Handoff received for ${
|
|
2543
|
+
`Handoff received for ${pc4.cyan(featureId)} (${status}).`
|
|
2478
2544
|
);
|
|
2479
2545
|
return null;
|
|
2480
2546
|
}
|
|
@@ -2483,8 +2549,8 @@ async function monitorMission(options) {
|
|
|
2483
2549
|
const featureId = asNonEmptyString4(data.featureId) ?? "feature";
|
|
2484
2550
|
writeLine(
|
|
2485
2551
|
options.stdout,
|
|
2486
|
-
|
|
2487
|
-
`Triage is required for ${
|
|
2552
|
+
pc4.yellow(
|
|
2553
|
+
`Triage is required for ${pc4.cyan(featureId)}. Auto-start is paused.`
|
|
2488
2554
|
)
|
|
2489
2555
|
);
|
|
2490
2556
|
return completeTriage();
|
|
@@ -2493,7 +2559,7 @@ async function monitorMission(options) {
|
|
|
2493
2559
|
const featureId = asNonEmptyString4(data.featureId) ?? "feature";
|
|
2494
2560
|
writeLine(
|
|
2495
2561
|
options.stdout,
|
|
2496
|
-
|
|
2562
|
+
pc4.yellow(`Worker timed out for ${pc4.cyan(featureId)}.`)
|
|
2497
2563
|
);
|
|
2498
2564
|
await refreshSummary();
|
|
2499
2565
|
return null;
|
|
@@ -2502,7 +2568,7 @@ async function monitorMission(options) {
|
|
|
2502
2568
|
const details = normalizeBudgetDetails(data);
|
|
2503
2569
|
writeLine(
|
|
2504
2570
|
options.stdout,
|
|
2505
|
-
|
|
2571
|
+
pc4.yellow(`Budget warning \xB7 ${formatBudgetUsage(details)}`)
|
|
2506
2572
|
);
|
|
2507
2573
|
await refreshSummary();
|
|
2508
2574
|
return null;
|
|
@@ -2528,7 +2594,7 @@ async function monitorMission(options) {
|
|
|
2528
2594
|
}).catch((error) => {
|
|
2529
2595
|
writeLine(
|
|
2530
2596
|
options.stdout,
|
|
2531
|
-
|
|
2597
|
+
pc4.yellow(
|
|
2532
2598
|
`Monitoring update failed: ${error instanceof Error ? error.message : String(error)}. Reconnecting...`
|
|
2533
2599
|
)
|
|
2534
2600
|
);
|
|
@@ -2539,7 +2605,7 @@ async function monitorMission(options) {
|
|
|
2539
2605
|
socket.on("error", (error) => {
|
|
2540
2606
|
writeLine(
|
|
2541
2607
|
options.stdout,
|
|
2542
|
-
|
|
2608
|
+
pc4.yellow(
|
|
2543
2609
|
`WebSocket error: ${error instanceof Error ? error.message : String(error)}`
|
|
2544
2610
|
)
|
|
2545
2611
|
);
|
|
@@ -2567,7 +2633,7 @@ async function monitorMission(options) {
|
|
|
2567
2633
|
continue;
|
|
2568
2634
|
}
|
|
2569
2635
|
lastSeen = Math.max(lastSeen, asFiniteNumber3(event.seq));
|
|
2570
|
-
writeLine(options.stdout,
|
|
2636
|
+
writeLine(options.stdout, pc4.dim(buildBackfillDescription(event)));
|
|
2571
2637
|
}
|
|
2572
2638
|
}
|
|
2573
2639
|
connectedOnce = true;
|
|
@@ -2591,7 +2657,7 @@ async function monitorMission(options) {
|
|
|
2591
2657
|
reconnectAttempt += 1;
|
|
2592
2658
|
writeLine(
|
|
2593
2659
|
options.stdout,
|
|
2594
|
-
|
|
2660
|
+
pc4.yellow(`Reconnecting to mission updates in ${delayMs} ms...`)
|
|
2595
2661
|
);
|
|
2596
2662
|
await sleep(delayMs);
|
|
2597
2663
|
}
|
|
@@ -2607,7 +2673,7 @@ function asNonEmptyString5(value) {
|
|
|
2607
2673
|
}
|
|
2608
2674
|
function writeSection(stdout, title) {
|
|
2609
2675
|
writeLine(stdout);
|
|
2610
|
-
writeLine(stdout,
|
|
2676
|
+
writeLine(stdout, pc5.bold(title));
|
|
2611
2677
|
}
|
|
2612
2678
|
function summarizeAssertions(assertions) {
|
|
2613
2679
|
return assertions.reduce(
|
|
@@ -2752,7 +2818,7 @@ async function resolveMissionId(missionId, context, command, homeDir) {
|
|
|
2752
2818
|
if (detectedMissionId) {
|
|
2753
2819
|
writeLine(
|
|
2754
2820
|
context.stdout,
|
|
2755
|
-
`Using the most recent local mission: ${
|
|
2821
|
+
`Using the most recent local mission: ${pc5.cyan(detectedMissionId)}`
|
|
2756
2822
|
);
|
|
2757
2823
|
return detectedMissionId;
|
|
2758
2824
|
}
|
|
@@ -2787,6 +2853,7 @@ async function runLifecycleMutation(action, missionIdValue, command, context, de
|
|
|
2787
2853
|
);
|
|
2788
2854
|
try {
|
|
2789
2855
|
const response = await apiClient.request({
|
|
2856
|
+
body: {},
|
|
2790
2857
|
method: "POST",
|
|
2791
2858
|
path: `/missions/${missionId}/mission/${action}`
|
|
2792
2859
|
});
|
|
@@ -2794,13 +2861,13 @@ async function runLifecycleMutation(action, missionIdValue, command, context, de
|
|
|
2794
2861
|
if (action === "pause") {
|
|
2795
2862
|
writeLine(
|
|
2796
2863
|
context.stdout,
|
|
2797
|
-
`Mission ${
|
|
2864
|
+
`Mission ${pc5.cyan(missionId)} paused. Current state: ${pc5.bold(nextState)}.`
|
|
2798
2865
|
);
|
|
2799
2866
|
return;
|
|
2800
2867
|
}
|
|
2801
2868
|
writeLine(
|
|
2802
2869
|
context.stdout,
|
|
2803
|
-
`Mission ${
|
|
2870
|
+
`Mission ${pc5.cyan(missionId)} cancelled. This action is irreversible. Current state: ${pc5.bold(nextState)}.`
|
|
2804
2871
|
);
|
|
2805
2872
|
} catch (error) {
|
|
2806
2873
|
if (!(error instanceof ApiError) || error.status !== 409) {
|
|
@@ -2814,7 +2881,7 @@ async function runLifecycleMutation(action, missionIdValue, command, context, de
|
|
|
2814
2881
|
const verb = action === "pause" ? "paused" : "cancelled";
|
|
2815
2882
|
writeLine(
|
|
2816
2883
|
context.stdout,
|
|
2817
|
-
`Mission ${
|
|
2884
|
+
`Mission ${pc5.cyan(missionId)} cannot be ${verb} because it is currently ${pc5.bold(missionState.state)}.`
|
|
2818
2885
|
);
|
|
2819
2886
|
throw new CommanderError(1, `mission-${action}`, "");
|
|
2820
2887
|
}
|
|
@@ -2844,13 +2911,14 @@ async function runMissionResume(missionIdValue, command, context, dependencies)
|
|
|
2844
2911
|
if (snapshot.state.state === "paused") {
|
|
2845
2912
|
try {
|
|
2846
2913
|
const response = await apiClient.request({
|
|
2914
|
+
body: {},
|
|
2847
2915
|
method: "POST",
|
|
2848
2916
|
path: `/missions/${missionId}/mission/resume`
|
|
2849
2917
|
});
|
|
2850
2918
|
const nextState = asNonEmptyString5(response.state) ?? "orchestrator_turn";
|
|
2851
2919
|
writeLine(
|
|
2852
2920
|
context.stdout,
|
|
2853
|
-
`Mission ${
|
|
2921
|
+
`Mission ${pc5.cyan(missionId)} resumed. Current state: ${pc5.bold(nextState)}.`
|
|
2854
2922
|
);
|
|
2855
2923
|
} catch (error) {
|
|
2856
2924
|
if (!(error instanceof ApiError) || error.status !== 409) {
|
|
@@ -2863,14 +2931,14 @@ async function runMissionResume(missionIdValue, command, context, dependencies)
|
|
|
2863
2931
|
);
|
|
2864
2932
|
writeLine(
|
|
2865
2933
|
context.stdout,
|
|
2866
|
-
`Mission ${
|
|
2934
|
+
`Mission ${pc5.cyan(missionId)} is currently ${pc5.bold(snapshot.state.state)}. Reconnecting to live updates if available.`
|
|
2867
2935
|
);
|
|
2868
2936
|
}
|
|
2869
2937
|
snapshot = await fetchMissionSnapshot(apiClient, missionId);
|
|
2870
2938
|
} else if (snapshot.state.state !== "cancelled" && snapshot.state.state !== "completed") {
|
|
2871
2939
|
writeLine(
|
|
2872
2940
|
context.stdout,
|
|
2873
|
-
`Reconnecting to mission ${
|
|
2941
|
+
`Reconnecting to mission ${pc5.cyan(missionId)} from state ${pc5.bold(snapshot.state.state)}.`
|
|
2874
2942
|
);
|
|
2875
2943
|
}
|
|
2876
2944
|
renderMissionSnapshot(context.stdout, snapshot);
|
|
@@ -2963,7 +3031,7 @@ import {
|
|
|
2963
3031
|
select as defaultSelectPrompt2
|
|
2964
3032
|
} from "@inquirer/prompts";
|
|
2965
3033
|
import ora2 from "ora";
|
|
2966
|
-
import
|
|
3034
|
+
import pc6 from "picocolors";
|
|
2967
3035
|
var DEFAULT_ANALYSIS_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
2968
3036
|
var DEFAULT_POLL_INTERVAL_MS = 5e3;
|
|
2969
3037
|
var FREE_TEXT_OPTION_ID = "free_text";
|
|
@@ -3130,18 +3198,18 @@ async function postDraftApproval(client, sessionId) {
|
|
|
3130
3198
|
}
|
|
3131
3199
|
function writeSection2(stdout, title) {
|
|
3132
3200
|
writeLine(stdout);
|
|
3133
|
-
writeLine(stdout,
|
|
3201
|
+
writeLine(stdout, pc6.bold(title));
|
|
3134
3202
|
}
|
|
3135
3203
|
function renderQuestion(stdout, question, index) {
|
|
3136
3204
|
writeLine(stdout, `${index + 1}. ${question.text}`);
|
|
3137
3205
|
if (question.references && question.references.length > 0) {
|
|
3138
|
-
writeLine(stdout, ` ${
|
|
3206
|
+
writeLine(stdout, ` ${pc6.dim(`References: ${question.references.join(", ")}`)}`);
|
|
3139
3207
|
}
|
|
3140
3208
|
}
|
|
3141
3209
|
function renderMilestones(stdout, milestones, reviewRound) {
|
|
3142
3210
|
writeSection2(stdout, `Milestone review round ${reviewRound}`);
|
|
3143
3211
|
milestones.forEach((milestone, index) => {
|
|
3144
|
-
writeLine(stdout, `${index + 1}. ${
|
|
3212
|
+
writeLine(stdout, `${index + 1}. ${pc6.cyan(milestone.name)}`);
|
|
3145
3213
|
if (milestone.description) {
|
|
3146
3214
|
writeLine(stdout, ` ${milestone.description}`);
|
|
3147
3215
|
}
|
|
@@ -3291,7 +3359,7 @@ async function resolveClarification(options, sessionId, initialPayload) {
|
|
|
3291
3359
|
}
|
|
3292
3360
|
writeLine(
|
|
3293
3361
|
options.stdout,
|
|
3294
|
-
|
|
3362
|
+
pc6.yellow("Proceeding because --force is enabled.")
|
|
3295
3363
|
);
|
|
3296
3364
|
payload = await postMilestoneConfirmation(options.client, sessionId, {});
|
|
3297
3365
|
return {
|
|
@@ -3373,7 +3441,7 @@ async function runPlanningFlow(options) {
|
|
|
3373
3441
|
if (options.existingMissionDraft) {
|
|
3374
3442
|
writeLine(
|
|
3375
3443
|
options.stdout,
|
|
3376
|
-
|
|
3444
|
+
pc6.cyan("Skipping planning because mission-draft.json already exists.")
|
|
3377
3445
|
);
|
|
3378
3446
|
return {
|
|
3379
3447
|
cancelled: false,
|
|
@@ -3429,7 +3497,7 @@ async function runPlanningFlow(options) {
|
|
|
3429
3497
|
message: "Approve this draft and continue to upload?"
|
|
3430
3498
|
});
|
|
3431
3499
|
if (!approved) {
|
|
3432
|
-
writeLine(options.stdout,
|
|
3500
|
+
writeLine(options.stdout, pc6.yellow("Planning cancelled before upload."));
|
|
3433
3501
|
return {
|
|
3434
3502
|
cancelled: true,
|
|
3435
3503
|
draft,
|
|
@@ -3438,7 +3506,7 @@ async function runPlanningFlow(options) {
|
|
|
3438
3506
|
};
|
|
3439
3507
|
}
|
|
3440
3508
|
await options.persistMissionDraft(draft);
|
|
3441
|
-
writeLine(options.stdout,
|
|
3509
|
+
writeLine(options.stdout, pc6.green("Saved approved mission draft."));
|
|
3442
3510
|
return {
|
|
3443
3511
|
cancelled: false,
|
|
3444
3512
|
draft,
|
|
@@ -3453,7 +3521,7 @@ import { createReadStream as createReadStream2 } from "fs";
|
|
|
3453
3521
|
import path5 from "path";
|
|
3454
3522
|
import { Transform as Transform2 } from "stream";
|
|
3455
3523
|
import ora3 from "ora";
|
|
3456
|
-
import
|
|
3524
|
+
import pc7 from "picocolors";
|
|
3457
3525
|
|
|
3458
3526
|
// src/snapshot.ts
|
|
3459
3527
|
import { execFile } from "child_process";
|
|
@@ -4050,15 +4118,15 @@ function isTerminalUploadStatus2(status) {
|
|
|
4050
4118
|
return status === "uploaded" || status === "blob_exists";
|
|
4051
4119
|
}
|
|
4052
4120
|
function renderSafetyGate(stdout, repos, existingBlobs) {
|
|
4053
|
-
writeLine(stdout,
|
|
4121
|
+
writeLine(stdout, pc7.bold("Upload safety check"));
|
|
4054
4122
|
writeLine(stdout, "Repos queued for upload:");
|
|
4055
4123
|
for (const repo of repos) {
|
|
4056
|
-
const cleanliness = repo.manifest.dirty ?
|
|
4057
|
-
const secretSummary = repo.secretFindings.length > 0 ?
|
|
4058
|
-
const dedupSummary = existingBlobs.has(repo.manifest.archiveSha256) ?
|
|
4124
|
+
const cleanliness = repo.manifest.dirty ? pc7.yellow("dirty") : pc7.green("clean");
|
|
4125
|
+
const secretSummary = repo.secretFindings.length > 0 ? pc7.red(`${repo.secretFindings.length} finding(s)`) : pc7.green("no findings");
|
|
4126
|
+
const dedupSummary = existingBlobs.has(repo.manifest.archiveSha256) ? pc7.cyan("blob already exists remotely") : "new upload";
|
|
4059
4127
|
writeLine(
|
|
4060
4128
|
stdout,
|
|
4061
|
-
`- ${
|
|
4129
|
+
`- ${pc7.cyan(repo.manifest.repoId)} \u2014 ${repo.manifest.localPath}`
|
|
4062
4130
|
);
|
|
4063
4131
|
writeLine(
|
|
4064
4132
|
stdout,
|
|
@@ -4202,7 +4270,7 @@ async function processRepoUpload(options, repo, remoteLaunchId) {
|
|
|
4202
4270
|
);
|
|
4203
4271
|
writeLine(
|
|
4204
4272
|
options.stdout,
|
|
4205
|
-
|
|
4273
|
+
pc7.cyan(`Skipping upload for ${repo.manifest.repoId}; blob already exists remotely.`)
|
|
4206
4274
|
);
|
|
4207
4275
|
return;
|
|
4208
4276
|
}
|
|
@@ -4296,7 +4364,7 @@ async function runUploadPipeline(options) {
|
|
|
4296
4364
|
})) {
|
|
4297
4365
|
writeLine(
|
|
4298
4366
|
options.stdout,
|
|
4299
|
-
|
|
4367
|
+
pc7.yellow("Upload cancelled before creating a remote launch.")
|
|
4300
4368
|
);
|
|
4301
4369
|
return {
|
|
4302
4370
|
finalized: false,
|
|
@@ -4313,7 +4381,7 @@ async function runUploadPipeline(options) {
|
|
|
4313
4381
|
if (existingBlobs.size > 0) {
|
|
4314
4382
|
writeLine(
|
|
4315
4383
|
options.stdout,
|
|
4316
|
-
|
|
4384
|
+
pc7.cyan(
|
|
4317
4385
|
`Remote dedup will reuse ${existingBlobs.size} existing blob${existingBlobs.size === 1 ? "" : "s"}.`
|
|
4318
4386
|
)
|
|
4319
4387
|
);
|
|
@@ -4340,7 +4408,7 @@ async function runUploadPipeline(options) {
|
|
|
4340
4408
|
if (isTerminalUploadStatus2(uploadStates.get(repo.manifest.repoId))) {
|
|
4341
4409
|
writeLine(
|
|
4342
4410
|
options.stdout,
|
|
4343
|
-
|
|
4411
|
+
pc7.cyan(`Skipping ${repo.manifest.repoId}; already completed in upload-state.json.`)
|
|
4344
4412
|
);
|
|
4345
4413
|
continue;
|
|
4346
4414
|
}
|
|
@@ -4358,7 +4426,7 @@ async function runUploadPipeline(options) {
|
|
|
4358
4426
|
failures.push(repo.manifest.repoId);
|
|
4359
4427
|
writeLine(
|
|
4360
4428
|
options.stdout,
|
|
4361
|
-
|
|
4429
|
+
pc7.red(
|
|
4362
4430
|
error instanceof Error ? error.message : `Upload failed for ${repo.manifest.repoId}.`
|
|
4363
4431
|
)
|
|
4364
4432
|
);
|
|
@@ -4380,7 +4448,7 @@ async function runUploadPipeline(options) {
|
|
|
4380
4448
|
}
|
|
4381
4449
|
writeLine(
|
|
4382
4450
|
options.stdout,
|
|
4383
|
-
|
|
4451
|
+
pc7.green(`Upload complete. Remote launch ${remoteLaunchId} is finalized.`)
|
|
4384
4452
|
);
|
|
4385
4453
|
return {
|
|
4386
4454
|
finalized: true,
|
|
@@ -4555,10 +4623,10 @@ function registerMissionRunCommand(mission, context, dependencies = {}) {
|
|
|
4555
4623
|
}
|
|
4556
4624
|
|
|
4557
4625
|
// src/commands/mission-status.ts
|
|
4558
|
-
import
|
|
4626
|
+
import pc8 from "picocolors";
|
|
4559
4627
|
function writeSection3(stdout, title) {
|
|
4560
4628
|
writeLine(stdout);
|
|
4561
|
-
writeLine(stdout,
|
|
4629
|
+
writeLine(stdout, pc8.bold(title));
|
|
4562
4630
|
}
|
|
4563
4631
|
function normalizeMilestoneOrder(milestones, features, assertions) {
|
|
4564
4632
|
const known = new Map(milestones.map((milestone) => [milestone.name, milestone]));
|
|
@@ -4658,7 +4726,7 @@ function renderMilestones2(stdout, milestones, features) {
|
|
|
4658
4726
|
for (const milestone of milestones) {
|
|
4659
4727
|
writeLine(
|
|
4660
4728
|
stdout,
|
|
4661
|
-
`- ${
|
|
4729
|
+
`- ${pc8.cyan(milestone.name)} (${milestone.state}) \xB7 ${milestone.completedFeatureCount}/${milestone.featureCount} features \xB7 ${milestone.passedAssertionCount}/${milestone.assertionCount} assertions`
|
|
4662
4730
|
);
|
|
4663
4731
|
const milestoneFeatures = features.filter(
|
|
4664
4732
|
(feature) => feature.milestone === milestone.name
|
|
@@ -4718,6 +4786,7 @@ function registerMissionCommands(program, context, dependencies = {}) {
|
|
|
4718
4786
|
const mission = program.command("mission").description("Manage missions");
|
|
4719
4787
|
registerMissionRunCommand(mission, context, dependencies);
|
|
4720
4788
|
registerMissionStatusCommand(mission, context, dependencies);
|
|
4789
|
+
registerMissionInfoCommand(mission, context, dependencies);
|
|
4721
4790
|
registerMissionListCommand(mission, context, dependencies);
|
|
4722
4791
|
registerMissionLifecycleCommands(mission, context, dependencies);
|
|
4723
4792
|
}
|