@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.
Files changed (2) hide show
  1. package/dist/hz.mjs +146 -71
  2. 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 pc4 from "picocolors";
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 pc3 from "picocolors";
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 pc2 from "picocolors";
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, pc2.bold(`Triage required for ${pc2.cyan(item.feature.id)}`));
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, pc2.dim("No outstanding triage items were found."));
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
- pc2.yellow(
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 ${pc2.cyan(item.feature.id)}.`);
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 ${pc2.cyan(createdFeature.id)} for ${pc2.cyan(item.feature.id)}.`
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
- pc2.yellow(
2002
- `No assertions were reassigned for ${pc2.cyan(item.feature.id)}.`
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 ${pc2.cyan(item.feature.id)}.`
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
- pc2.dim(`Skipped triage for ${item.feature.id}; it will remain unresolved.`)
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
- process.once(signal, handler);
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, handler);
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 `${pc3.cyan(missionId)} ${pc3.bold(summary.state)} \xB7 ${formatSummaryLine(summary)}`;
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 ${pc3.cyan(featureId)}.`;
2255
+ return `Backfilled ${type} for ${pc4.cyan(featureId)}.`;
2182
2256
  }
2183
2257
  if (assertionId) {
2184
- return `Backfilled ${type} for ${pc3.cyan(assertionId)}.`;
2258
+ return `Backfilled ${type} for ${pc4.cyan(assertionId)}.`;
2185
2259
  }
2186
2260
  if (milestone) {
2187
- return `Backfilled ${type} for milestone ${pc3.cyan(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 ${pc3.cyan(options.missionId)} \xB7 ${formatSummaryLine(summary)}`
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, pc3.green(message));
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, pc3.red(message));
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
- pc3.dim("No triage items required action. Resuming the orchestration loop.")
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
- pc3.green(
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
- pc3.yellow("Interrupted. Local state is saved. Remote mission continues.")
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 ${pc3.cyan(result.featureId)} (${pc3.dim(result.workerSessionId)}).`
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
- pc3.dim("No eligible work is available yet. Waiting for updates.")
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
- pc3.yellow(
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 ${pc3.cyan(state)}`);
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 ${pc3.cyan(featureId)} \u2192 ${pc3.bold(status)}`
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 ${pc3.cyan(milestone)} \u2192 ${pc3.bold(status)}`
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 ${pc3.cyan(assertionId)} \u2192 ${pc3.bold(status)}`
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 ${pc3.cyan(featureId)} (${status}).`
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
- pc3.yellow(
2479
- `Triage is required for ${pc3.cyan(featureId)}. Auto-start is paused.`
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
- pc3.yellow(`Worker timed out for ${pc3.cyan(featureId)}.`)
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
- pc3.yellow(`Budget warning \xB7 ${formatBudgetUsage(details)}`)
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
- pc3.yellow(
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
- pc3.yellow(
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, pc3.dim(buildBackfillDescription(event)));
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
- pc3.yellow(`Reconnecting to mission updates in ${delayMs} ms...`)
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, pc4.bold(title));
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: ${pc4.cyan(detectedMissionId)}`
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 ${pc4.cyan(missionId)} paused. Current state: ${pc4.bold(nextState)}.`
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 ${pc4.cyan(missionId)} cancelled. This action is irreversible. Current state: ${pc4.bold(nextState)}.`
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 ${pc4.cyan(missionId)} cannot be ${verb} because it is currently ${pc4.bold(missionState.state)}.`
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 ${pc4.cyan(missionId)} resumed. Current state: ${pc4.bold(nextState)}.`
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 ${pc4.cyan(missionId)} is currently ${pc4.bold(snapshot.state.state)}. Reconnecting to live updates if available.`
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 ${pc4.cyan(missionId)} from state ${pc4.bold(snapshot.state.state)}.`
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 pc5 from "picocolors";
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, pc5.bold(title));
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, ` ${pc5.dim(`References: ${question.references.join(", ")}`)}`);
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}. ${pc5.cyan(milestone.name)}`);
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
- pc5.yellow("Proceeding because --force is enabled.")
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
- pc5.cyan("Skipping planning because mission-draft.json already exists.")
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, pc5.yellow("Planning cancelled before upload."));
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, pc5.green("Saved approved mission draft."));
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 pc6 from "picocolors";
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, pc6.bold("Upload safety check"));
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 ? pc6.yellow("dirty") : pc6.green("clean");
4049
- const secretSummary = repo.secretFindings.length > 0 ? pc6.red(`${repo.secretFindings.length} finding(s)`) : pc6.green("no findings");
4050
- const dedupSummary = existingBlobs.has(repo.manifest.archiveSha256) ? pc6.cyan("blob already exists remotely") : "new upload";
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
- `- ${pc6.cyan(repo.manifest.repoId)} \u2014 ${repo.manifest.localPath}`
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
- pc6.cyan(`Skipping upload for ${repo.manifest.repoId}; blob already exists remotely.`)
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
- pc6.yellow("Upload cancelled before creating a remote launch.")
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
- pc6.cyan(
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
- pc6.cyan(`Skipping ${repo.manifest.repoId}; already completed in upload-state.json.`)
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
- pc6.red(
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
- pc6.green(`Upload complete. Remote launch ${remoteLaunchId} is finalized.`)
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 pc7 from "picocolors";
4624
+ import pc8 from "picocolors";
4551
4625
  function writeSection3(stdout, title) {
4552
4626
  writeLine(stdout);
4553
- writeLine(stdout, pc7.bold(title));
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
- `- ${pc7.cyan(milestone.name)} (${milestone.state}) \xB7 ${milestone.completedFeatureCount}/${milestone.featureCount} features \xB7 ${milestone.passedAssertionCount}/${milestone.assertionCount} assertions`
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coresource/hz",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "hz": "dist/hz.mjs"