sandstream-kit 1.8.0 → 1.11.0

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/cli.js CHANGED
@@ -87,7 +87,10 @@ async function cmdHeal() {
87
87
  console.log(`${c.bold}${c.cyan}kit heal${c.reset}${dryRun ? `${c.dim} (dry-run)${c.reset}` : ""}`);
88
88
  console.log(`${c.dim}${"─".repeat(50)}${c.reset}\n`);
89
89
  const { runHeal } = await import("./heal.js");
90
- const res = await runHeal({ dryRun });
90
+ // Progress goes to stderr: live feedback for a human watching, without
91
+ // polluting the machine-readable proposals on stdout (--agent).
92
+ const res = await runHeal({ dryRun, onProgress: (m) => console.error(`${c.dim}${m}${c.reset}`) });
93
+ console.log();
91
94
  if (dryRun) {
92
95
  if (res.plannedSafe.length > 0) {
93
96
  console.log(`${c.bold}Would auto-fix (safe):${c.reset}`);
@@ -322,6 +325,25 @@ async function cmdCheck() {
322
325
  console.log();
323
326
  }
324
327
  printSummary(toolResults, serviceResults, secretResults.keys, securityResults);
328
+ // Surface a stale kit (a newer published version) as a warn — a stale CLI
329
+ // can carry already-fixed bugs. Gated by [update].check; checkForUpdate
330
+ // also self-skips in CI and with KIT_NO_UPDATE_CHECK=1, and returns null
331
+ // when already on latest or the check fails.
332
+ if (config.update?.check !== false) {
333
+ const { checkForUpdate } = await import("./update-check.js");
334
+ const u = await checkForUpdate(KIT_VERSION);
335
+ if (u) {
336
+ if (config.update?.auto === true) {
337
+ // Opt-in auto-update — still WATERTIGHT: selfUpgrade triages kit's own
338
+ // package and installs ONLY on a triage PASS. Never installs on fail.
339
+ console.log(`${c.yellow}! kit ${u.current} → ${u.latest} — auto-update on, triaging before install…${c.reset}`);
340
+ await selfUpgrade();
341
+ }
342
+ else {
343
+ console.log(`${c.yellow}! kit ${u.current} → ${u.latest} available${c.reset} ${c.dim}— run ${c.reset}${c.bold}kit upgrade --self${c.reset}${c.dim} (triages before installing)${c.reset}\n`);
344
+ }
345
+ }
346
+ }
325
347
  return allOk;
326
348
  });
327
349
  }
@@ -425,27 +447,46 @@ async function cmdInstall() {
425
447
  return true;
426
448
  }
427
449
  const toolsConfig = config.tools;
450
+ // WATERTIGHT: kit triages every third-party tool before installing it. The
451
+ // `--no-triage` override is a deliberate, audited security action — it must
452
+ // hold a one-shot elevation, or the install is refused.
453
+ let skipTriage = false;
454
+ if (hasFlag(process.argv, "--no-triage")) {
455
+ const elev = await consumeElevation("tools.install.no-triage");
456
+ if (!elev.ok) {
457
+ console.error(`${c.red}✗ --no-triage refused: ${elev.reason}${c.reset}`);
458
+ console.error(`${c.dim}Run 'kit auth elevate --scope tools.install.no-triage' first, or drop --no-triage to let triage run.${c.reset}`);
459
+ return false;
460
+ }
461
+ skipTriage = true;
462
+ console.log(`${c.yellow}⚠ --no-triage: triage gate bypassed (elevation consumed, audit-logged)${c.reset}`);
463
+ }
428
464
  console.log(`${c.bold}${c.cyan}Installing tools via mise...${c.reset}\n`);
429
465
  return await withGovernance(config, {
430
466
  operation: "tools.install",
431
467
  operationType: "write",
432
468
  metadata: {
433
469
  tools: Object.keys(toolsConfig),
470
+ skipTriage,
434
471
  },
435
472
  }, async () => {
436
- const results = await installTools(toolsConfig);
473
+ const results = await installTools(toolsConfig, undefined, { skipTriage });
437
474
  let allOk = true;
438
475
  for (const r of results) {
439
476
  const icon = r.action === "failed"
440
477
  ? `${c.red}✗${c.reset}`
441
- : `${c.green}✓${c.reset}`;
478
+ : r.action === "blocked"
479
+ ? `${c.yellow}⛔${c.reset}`
480
+ : `${c.green}✓${c.reset}`;
442
481
  const label = r.action === "already_ok"
443
482
  ? `${c.dim}already installed${c.reset}`
444
483
  : r.action === "installed"
445
484
  ? `${c.green}installed${c.reset}`
446
- : `${c.red}failed${c.reset}`;
485
+ : r.action === "blocked"
486
+ ? `${c.yellow}blocked by triage${c.reset}`
487
+ : `${c.red}failed${c.reset}`;
447
488
  console.log(` ${icon} ${r.name} ${label} ${c.dim}${r.detail}${c.reset}`);
448
- if (r.action === "failed")
489
+ if (r.action === "failed" || r.action === "blocked")
449
490
  allOk = false;
450
491
  }
451
492
  console.log();
@@ -1033,9 +1074,43 @@ async function cmdGovernance() {
1033
1074
  console.log();
1034
1075
  return true;
1035
1076
  }
1077
+ /**
1078
+ * Governed self-upgrade: kit triages its OWN npm package before installing a new
1079
+ * version of itself. WATERTIGHT — an untriaged kit is never installed; offline /
1080
+ * triage-unavailable → blocked (fail-closed). The raw `npm i -g sandstream-kit`
1081
+ * is still available to the user, but that path is outside kit's governance.
1082
+ */
1083
+ async function selfUpgrade() {
1084
+ const { gateInstall } = await import("./triage-gate.js");
1085
+ console.log(`${c.dim}Triaging sandstream-kit before upgrading itself…${c.reset}`);
1086
+ const verdict = await gateInstall("npm:sandstream-kit");
1087
+ if (verdict.decision === "blocked") {
1088
+ console.error(`${c.red}✗ self-upgrade blocked: ${verdict.reason}${c.reset}`);
1089
+ console.error(`${c.dim}kit will not install an untriaged version of itself. Get online with the triage skill installed, then retry.${c.reset}`);
1090
+ return false;
1091
+ }
1092
+ console.log(`${c.green}✓${c.reset} ${verdict.reason} — upgrading…\n`);
1093
+ try {
1094
+ const { exec } = await import("./utils/exec.js");
1095
+ await exec("npm", ["install", "-g", "sandstream-kit@latest"], {
1096
+ timeout: 180_000,
1097
+ env: { ...process.env },
1098
+ });
1099
+ console.log(`\n${c.green}${c.bold}✓ kit upgraded${c.reset} — run ${c.bold}kit --version${c.reset} to confirm.`);
1100
+ return true;
1101
+ }
1102
+ catch (err) {
1103
+ const msg = err instanceof Error ? err.message : String(err);
1104
+ console.error(`${c.red}✗ npm install failed: ${msg.split("\n")[0]}${c.reset}`);
1105
+ return false;
1106
+ }
1107
+ }
1036
1108
  async function cmdUpgrade() {
1037
1109
  console.log(`${c.bold}${c.cyan}kit upgrade${c.reset}`);
1038
1110
  console.log(`${c.dim}${"─".repeat(50)}${c.reset}\n`);
1111
+ if (hasFlag(process.argv, "--self")) {
1112
+ return await selfUpgrade();
1113
+ }
1039
1114
  const config = await loadConfig(resolveConfigPath());
1040
1115
  // Update skills lock
1041
1116
  const skills = {};