sandstream-kit 1.4.0 → 1.4.2

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/README.md CHANGED
@@ -6,8 +6,28 @@ For AI agents and humans. Manages tools, auth, secrets, and project setup. Zero
6
6
 
7
7
  🌐 [sandstre.am/kit](https://sandstre.am/kit)
8
8
 
9
+ ## Quick start
10
+
11
+ **Prerequisites:** Node.js 22+, git, and [mise](https://mise.jdx.dev) for installing tools (`brew install mise`, or `curl https://mise.run | sh`).
12
+
9
13
  ```bash
14
+ # zero install (also sidesteps npm -g permission issues):
10
15
  npx sandstream-kit setup
16
+
17
+ # or install globally:
18
+ npm i -g sandstream-kit
19
+ # if npm -g is permission-blocked, use a user-owned prefix instead of sudo:
20
+ # npm config set prefix ~/.npm-global
21
+ # echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc
22
+ ```
23
+
24
+ Then, in a repo:
25
+
26
+ ```bash
27
+ kit init # detect the stack → generate .kit.toml
28
+ kit check # what's set up vs missing (tools, services, secrets, hooks, security)
29
+ kit setup # install tools (via mise), git hooks, logins, secrets
30
+ kit context check # lock each CLI to the declared account + project (no wrong-org pushes)
11
31
  ```
12
32
 
13
33
  ## Problem
package/dist/cli.js CHANGED
@@ -55,7 +55,7 @@ import { promptConfirm } from "./utils/prompt.js";
55
55
  import { c } from "./utils/colors.js";
56
56
  import { gatherStatus } from "./status.js";
57
57
  import { KIT_FILE, resolveConfigPath } from "./cli-shared.js";
58
- import { checkContext, applyContext, contextPrompt } from "./context-lock.js";
58
+ import { checkContext, applyContext, contextPrompt, gatherLive, suggestContextToml } from "./context-lock.js";
59
59
  import { cmdEnv } from "./commands/env.js";
60
60
  import { cmdAuth } from "./commands/auth.js";
61
61
  import { cmdAudit } from "./commands/audit.js";
@@ -122,7 +122,9 @@ async function cmdCheck() {
122
122
  const testsOk = testResults.every((t) => t.status !== "fail");
123
123
  const lockOk = lockResults.every((l) => l.inSync);
124
124
  const allOk = toolResults.every((t) => t.ok) &&
125
- serviceResults.every((s) => s.authenticated) &&
125
+ // Informational services (no CLI login — `#`-documented, e.g. resend
126
+ // env keys) are manual setup, not a failed gate. They surface as warns.
127
+ serviceResults.every((s) => s.authenticated || s.informational) &&
126
128
  secretResults.keys.every((s) => s.available) &&
127
129
  skillResults.filter((s) => s.required).every((s) => s.installed) &&
128
130
  hookResults.every((h) => h.installed && h.upToDate) &&
@@ -139,8 +141,10 @@ async function cmdCheck() {
139
141
  })),
140
142
  ...serviceResults.map((s) => ({
141
143
  name: s.name,
142
- status: (s.authenticated ? "pass" : "fail"),
143
- detail: s.output ?? (s.authenticated ? "authenticated" : "not authenticated"),
144
+ status: (s.authenticated ? "pass" : s.informational ? "warn" : "fail"),
145
+ detail: s.informational
146
+ ? (s.output || "manual setup (no CLI login)")
147
+ : (s.output ?? (s.authenticated ? "authenticated" : "not authenticated")),
144
148
  category: "services",
145
149
  })),
146
150
  ...secretResults.keys.map((s) => ({
@@ -516,14 +520,18 @@ async function cmdLogin() {
516
520
  ? `${c.red}✗${c.reset}`
517
521
  : r.action === "login_unverified"
518
522
  ? `${c.yellow}?${c.reset}`
519
- : `${c.green}✓${c.reset}`;
523
+ : r.action === "manual"
524
+ ? `${c.yellow}!${c.reset}`
525
+ : `${c.green}✓${c.reset}`;
520
526
  const label = r.action === "already_authenticated"
521
527
  ? `${c.dim}already authenticated${c.reset}`
522
528
  : r.action === "logged_in"
523
529
  ? `${c.green}logged in${c.reset}`
524
530
  : r.action === "login_unverified"
525
531
  ? `${c.yellow}login unverified${c.reset}`
526
- : `${c.red}failed${c.reset}`;
532
+ : r.action === "manual"
533
+ ? `${c.yellow}manual${c.reset}`
534
+ : `${c.red}failed${c.reset}`;
527
535
  console.log(` ${icon} ${r.name} ${label} ${c.dim}${r.detail}${c.reset}`);
528
536
  if (r.action === "failed" || r.action === "login_unverified")
529
537
  allOk = false;
@@ -599,7 +607,7 @@ async function cmdSecrets() {
599
607
  template: secretsConfig.template,
600
608
  },
601
609
  }, async () => {
602
- const { results, written, fromTemplate } = await generateSecrets(secretsConfig);
610
+ const { results, written, fromTemplate, skipped } = await generateSecrets(secretsConfig);
603
611
  let allOk = true;
604
612
  for (const r of results) {
605
613
  const icon = r.resolved
@@ -618,6 +626,9 @@ async function cmdSecrets() {
618
626
  : "from keys";
619
627
  console.log(`\n ${c.green}✓${c.reset} Wrote .env.local ${c.dim}(${source})${c.reset}`);
620
628
  }
629
+ else if (skipped === "nothing-resolved") {
630
+ console.log(`\n ${c.yellow}!${c.reset} Skipped .env.local ${c.dim}— no secrets resolved (vault empty/unauthed); existing file left intact${c.reset}`);
631
+ }
621
632
  console.log();
622
633
  return allOk;
623
634
  });
@@ -1846,8 +1857,12 @@ async function cmdCi() {
1846
1857
  })),
1847
1858
  ...serviceResults.map((s) => ({
1848
1859
  name: s.name,
1849
- status: (s.authenticated ? "pass" : "fail"),
1850
- detail: s.output ?? (s.authenticated ? "authenticated" : "not authenticated"),
1860
+ // Informational services (no CLI login) are a manual-setup warning,
1861
+ // not a CI failure.
1862
+ status: (s.authenticated ? "pass" : s.informational ? "warn" : "fail"),
1863
+ detail: s.informational
1864
+ ? (s.output || "manual setup (no CLI login)")
1865
+ : (s.output ?? (s.authenticated ? "authenticated" : "not authenticated")),
1851
1866
  category: "services",
1852
1867
  })),
1853
1868
  ...secretResults.keys.map((s) => ({
@@ -2728,7 +2743,16 @@ async function cmdContextCheck() {
2728
2743
  return findings.every((f) => f.status !== "mismatch");
2729
2744
  }
2730
2745
  if (!config.context) {
2731
- console.log(`${c.dim}No [context] declared in .kit.toml. Add one to lock each CLI to its account + project.${c.reset}`);
2746
+ console.log(`${c.dim}No [context] declared in .kit.toml each CLI is unlocked from its account + project.${c.reset}`);
2747
+ const suggestion = suggestContextToml(await gatherLive(process.cwd()));
2748
+ if (suggestion) {
2749
+ console.log(`\nDetected here — add a ${c.bold}[context]${c.reset} block to .kit.toml to lock it:\n`);
2750
+ console.log(suggestion);
2751
+ console.log(`\n${c.yellow}Verify each value is correct for THIS repo before trusting it.${c.reset} ${c.dim}kit detected the currently-active CLI state, which is exactly what the lock exists to question. Then re-run kit context check.${c.reset}`);
2752
+ }
2753
+ else {
2754
+ console.log(`${c.dim}Add one to lock each CLI to its account + project (gcloud/vercel/github/git/npm).${c.reset}`);
2755
+ }
2732
2756
  return true;
2733
2757
  }
2734
2758
  console.log(`${c.bold}Context lock${c.reset}\n`);
@@ -2937,6 +2961,9 @@ function cmdVersion() {
2937
2961
  const COMMAND_HELP = {
2938
2962
  status: "Adoption checklist — what's set up across kit + the next step for each gap",
2939
2963
  check: "Check status of all tools, services, secrets, and lock files",
2964
+ review: "Full repo audit — runs check + design in one gate (for agents / PR checks)",
2965
+ design: "Check design quality (a11y, design tokens) against the baseline",
2966
+ baseline: "Freeze current warnings into .kit-baseline.json so future runs gate only net-new findings",
2940
2967
  memory: "Local conversation memory — index transcripts + show stats",
2941
2968
  "memory index": "Index ~/.claude transcripts into the SQLite memory store",
2942
2969
  "memory search": "Full-text search memory (current project; --global for all)",
@@ -3279,6 +3306,8 @@ function cmdHelp(subcommand) {
3279
3306
  }
3280
3307
  const bold = c.bold, cyan = c.cyan, dim = c.dim, reset = c.reset, green = c.green;
3281
3308
  console.log(`${bold}kit${reset} ${dim}v${KIT_VERSION}${reset} — developer environment manager\n`);
3309
+ console.log(`${bold}Get going:${reset} ${dim}npx sandstream-kit setup${reset} ${dim}or${reset} ${green}kit init${reset} ${dim}→${reset} ${green}kit check${reset} ${dim}→${reset} ${green}kit setup${reset}`);
3310
+ console.log(`${dim}Prereqs: Node 22+, git, and mise (brew install mise) for installing tools.${reset}\n`);
3282
3311
  console.log(`${bold}Usage:${reset} kit ${cyan}<command>${reset} ${dim}[options]${reset}\n`);
3283
3312
  console.log(`${bold}Commands:${reset}`);
3284
3313
  const maxLen = Math.max(...Object.keys(COMMAND_HELP).map((k) => k.length));
@@ -3479,6 +3508,16 @@ async function main() {
3479
3508
  process.exitCode = 0;
3480
3509
  return;
3481
3510
  }
3511
+ // A `--help`/`-h` anywhere after the command means "show that command's
3512
+ // help" — NEVER execute the command. Critical for side-effectful commands
3513
+ // (agent-config, fix, secrets, hooks add): `kit <cmd> --help` previously
3514
+ // fell through to the dispatch and ran <cmd>. (Generalizes the 1.4.0 fix
3515
+ // that only covered `kit memory <sub> --help`.)
3516
+ if (command && command !== "help" && (hasFlag(args, "--help") || hasFlag(args, "-h"))) {
3517
+ cmdHelp(command);
3518
+ process.exitCode = 0;
3519
+ return;
3520
+ }
3482
3521
  // version/help/completions need bespoke handling; everything else is a flat
3483
3522
  // command->fn dispatch (was a ~40-case switch — the main complexity driver).
3484
3523
  if (command === "version") {