wholestack 0.6.0 → 0.6.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.
@@ -142,7 +142,7 @@ function fmtTokens(n) {
142
142
  }
143
143
  function scrubVendor(s) {
144
144
  return (s || "").replace(
145
- /[\w./-]*(?:anthropic|claude|sonnet|opus|cerebras|openrouter|gpt-?oss|gpt-?\d|glm|kimi|qwen|llama|mistral|deepseek|zai|openai|google\/|meta-?llama)[\w./-]*/gi,
145
+ /[\w./-]*(?:anthropic|claude|sonnet|opus|cerebras|openrouter|gpt-?oss|gpt-?\d|glm|kimi|qwen|llama|mistral|deepseek|zai|openai|google\/(?:gemini|gemma)|meta-?llama)[\w./-]*/gi,
146
146
  "Zeta"
147
147
  );
148
148
  }
@@ -830,10 +830,12 @@ function normalize(result) {
830
830
  const verify = result.verify ?? {};
831
831
  const proofVerdict = result.proof?.verdict;
832
832
  const shipped = proofVerdict ? proofVerdict === "SHIP" : !!verify.ok;
833
+ const pending = result.proof?.proofStatus === "pending" && shipped;
833
834
  return {
834
835
  ok: true,
835
836
  buildId: result.buildId ?? null,
836
837
  verdict: shipped ? "SHIP" : "NO_SHIP",
838
+ proofPending: pending,
837
839
  themeId: result.themeId ?? null,
838
840
  fileCount: files.length,
839
841
  spec: typeof result.spec === "string" ? result.spec : null,
@@ -869,6 +871,33 @@ function makeConsumer(onPhase) {
869
871
  }
870
872
  };
871
873
  }
874
+ async function pollProofStatus(zetaApiUrl, buildId, authHeaders8, opts = {}) {
875
+ const interval = opts.intervalMs ?? 5e3;
876
+ const timeout = opts.timeoutMs ?? 10 * 60 * 1e3;
877
+ const started = Date.now();
878
+ let last = { proofStatus: "pending", verdict: null, dirty: false };
879
+ while (Date.now() - started < timeout) {
880
+ try {
881
+ const resp = await fetch(
882
+ `${zetaApiUrl}/api/zeta/build/${encodeURIComponent(buildId)}/proof-status`,
883
+ { headers: { ...authHeaders8 } }
884
+ );
885
+ if (resp.ok) {
886
+ const j = await resp.json();
887
+ last = {
888
+ proofStatus: j.proofStatus ?? null,
889
+ verdict: j.verdict ?? null,
890
+ dirty: j.dirty === true
891
+ };
892
+ if (last.proofStatus && last.proofStatus !== "pending") return last;
893
+ }
894
+ } catch {
895
+ }
896
+ opts.onTick?.(Date.now() - started);
897
+ await new Promise((r) => setTimeout(r, interval));
898
+ }
899
+ return last;
900
+ }
872
901
  async function httpBuild(zetaApiUrl, body, onPhase) {
873
902
  let resp;
874
903
  try {
@@ -929,13 +958,15 @@ async function readSse(resp, onEvent) {
929
958
  }
930
959
  }
931
960
  async function demoBootBuild(zetaApiUrl, body, onPhase) {
961
+ const apiKey = process.env.ZETA_API_KEY?.trim();
962
+ const authHeaders8 = apiKey ? { authorization: `Bearer ${apiKey}` } : {};
932
963
  let spec2 = "";
933
964
  let draftErr = "";
934
965
  let draftResp = null;
935
966
  try {
936
967
  draftResp = await fetch(`${zetaApiUrl}/api/zeta/build`, {
937
968
  method: "POST",
938
- headers: { "content-type": "application/json" },
969
+ headers: { "content-type": "application/json", ...authHeaders8 },
939
970
  body: JSON.stringify({ idea: body.idea, mode: "draft", buildModel: body.buildModel })
940
971
  });
941
972
  } catch (e) {
@@ -958,7 +989,7 @@ async function demoBootBuild(zetaApiUrl, body, onPhase) {
958
989
  try {
959
990
  bootResp = await fetch(`${zetaApiUrl}/api/zeta/demo/boot`, {
960
991
  method: "POST",
961
- headers: { "content-type": "application/json" },
992
+ headers: { "content-type": "application/json", ...authHeaders8 },
962
993
  body: JSON.stringify({
963
994
  spec: spec2,
964
995
  idea: body.idea,
@@ -2468,6 +2499,24 @@ function buildTools(ctx) {
2468
2499
  viaHosted = true;
2469
2500
  }
2470
2501
  }
2502
+ if (viaHosted && result.ok && result.proofPending && result.buildId) {
2503
+ const bid = String(result.buildId);
2504
+ line(c.dim(" \u21B3 shipped fast \u2014 runtime proofs running in the background\u2026"));
2505
+ const final = await pollProofStatus(ctx.zetaApiUrl, bid, {}, {
2506
+ timeoutMs: 6 * 60 * 1e3,
2507
+ onTick: (ms) => line(c.dim(` proving\u2026 (${Math.round(ms / 1e3)}s)`))
2508
+ });
2509
+ if (final.proofStatus === "proven") {
2510
+ line(c.green(` \u2713 proven \u2014 ${final.verdict ?? "SHIP"}`));
2511
+ result = { ...result, verdict: final.verdict ?? result.verdict, proofPending: false };
2512
+ } else if (final.proofStatus === "failed") {
2513
+ line(c.red(` \u2717 proof FAILED after ship \u2014 your app is live but UNPROVEN (${final.verdict ?? "NO_SHIP"}).`));
2514
+ line(c.red(` Roll it back: zeta rollback ${bid}`));
2515
+ result = { ...result, verdict: "NO_SHIP", proofPending: false };
2516
+ } else {
2517
+ line(c.dim(` still proving \u2014 check later: zeta proof ${bid}`));
2518
+ }
2519
+ }
2471
2520
  if (keep !== false && viaHosted && result.ok && result.buildId && !result.paywalled) {
2472
2521
  const dest = opts.destDir ? join5(ctx.cwd, opts.destDir) : join5(ctx.cwd, `zeta-${String(result.buildId).slice(-8)}`);
2473
2522
  line(c.dim(" \u21B3 saving your code\u2026"));
@@ -4285,6 +4334,13 @@ var ROLE_NZT_48 = [
4285
4334
  " control, overflow, oracle/price manipulation, unchecked calls, value",
4286
4335
  " extraction, proxy traps). Report severity + location + exploit path + fix.",
4287
4336
  " Hunt weaknesses to CLOSE them; never weaponize.",
4337
+ " The prover now carries dedicated property kinds you can discharge",
4338
+ " mechanically on the same harness: oracle-manipulation-resistance (proves the",
4339
+ " contract survives an adversarial on-chain price; does NOT vouch for off-chain",
4340
+ " oracle honesty), governance safety (timelock-enforcement, flash-loan-",
4341
+ " resistant snapshot-voting-safety, quorum-enforcement), and MEV defenses",
4342
+ " (slippage-bound \u2014 the user's slippage bound is honored; deadline-enforcement).",
4343
+ " Treat these as provable mechanical properties, not heuristics.",
4288
4344
  "3. BUILD & SHIP \u2014 idea \u2192 ISL \u2192 Solidity \u2192 proven \u2192 deploy-ready, inside the",
4289
4345
  " deterministic provable fragment. Run forge build/test, Slither, Halmos.",
4290
4346
  " Never weaken a gate to force green \u2014 self-heal the spec instead.",
@@ -4299,9 +4355,13 @@ var ROLE_NZT_48 = [
4299
4355
  " then the recommendation. Never fabricate a result \u2014 relay a down feed.",
4300
4356
  "",
4301
4357
  "RULES OF ENGAGEMENT: truth over optics (a refuted invariant honestly reported",
4302
- "is a win; fake-success is the one unforgivable error). One contract per proof",
4303
- "file; honor non-vacuity. Defensive only \u2014 refuse mass targeting, live attacks,",
4304
- "or anything outside an authorized engagement."
4358
+ "is a win; fake-success is the one unforgivable error). Economic soundness",
4359
+ "(incentives, peg, liquidation cascades) is SIMULATED under bounded scenarios",
4360
+ "and reported as TESTED, NEVER proven, and never emits a certificate; MEV that",
4361
+ "lives in cross-transaction ORDERING is out of scope \u2014 only contract-level",
4362
+ "defenses are proven. One contract per proof file; honor non-vacuity. Defensive",
4363
+ "only \u2014 refuse mass targeting, live attacks, or anything outside an authorized",
4364
+ "engagement."
4305
4365
  ].join("\n");
4306
4366
  var DEFAULT_REGISTRY = (() => {
4307
4367
  const r = new PromptRegistry();
@@ -9933,6 +9993,9 @@ export {
9933
9993
  writeProjectLink,
9934
9994
  writeWorkingTree,
9935
9995
  collectLocalFiles,
9996
+ httpBuild,
9997
+ demoBootBuild,
9998
+ deliverBuild,
9936
9999
  deployBuild,
9937
10000
  promoteBuild,
9938
10001
  rollbackDeployment,
@@ -9946,6 +10009,8 @@ export {
9946
10009
  PROPERTY_KINDS,
9947
10010
  runProver,
9948
10011
  killRunningApps,
10012
+ isolateFromWorkspace,
10013
+ installDeps,
9949
10014
  fetchGithubInstallations,
9950
10015
  fetchGithubRepos,
9951
10016
  runRepoAuditStream,
package/dist/cli.js CHANGED
@@ -20,6 +20,8 @@ import {
20
20
  buildWebTools,
21
21
  c,
22
22
  collectLocalFiles,
23
+ deliverBuild,
24
+ demoBootBuild,
23
25
  deployBuild,
24
26
  drawCanvas,
25
27
  escalationBus,
@@ -28,8 +30,11 @@ import {
28
30
  generateLegal,
29
31
  getBusiness,
30
32
  getLaunchKit,
33
+ httpBuild,
31
34
  inspectUrl,
35
+ installDeps,
32
36
  isPermissionMode,
37
+ isolateFromWorkspace,
33
38
  killRunningApps,
34
39
  line,
35
40
  listBusinesses,
@@ -75,11 +80,11 @@ import {
75
80
  webBaseUrl,
76
81
  writeProjectLink,
77
82
  writeWorkingTree
78
- } from "./chunk-C56OI76J.js";
83
+ } from "./chunk-UBDW5N5G.js";
79
84
 
80
85
  // src/cli.ts
81
86
  import { createInterface } from "readline/promises";
82
- import { stdin, stdout as stdout3, argv, exit, cwd as cwd2 } from "process";
87
+ import { stdin, stdout as stdout4, argv, exit, cwd as cwd3 } from "process";
83
88
 
84
89
  // src/cli-login.ts
85
90
  import { createServer } from "http";
@@ -107,7 +112,7 @@ async function loginBrowser(opts) {
107
112
  const state = randomBytes(16).toString("hex");
108
113
  const device = hostname().slice(0, 60);
109
114
  const timeoutMs = (opts.timeoutSec ?? 240) * 1e3;
110
- return new Promise((resolve4) => {
115
+ return new Promise((resolve5) => {
111
116
  let settled = false;
112
117
  const finish = (token) => {
113
118
  if (settled) return;
@@ -117,7 +122,7 @@ async function loginBrowser(opts) {
117
122
  server.close();
118
123
  } catch {
119
124
  }
120
- resolve4(token);
125
+ resolve5(token);
121
126
  };
122
127
  const server = createServer((req, res) => {
123
128
  const url = new URL(req.url ?? "/", "http://127.0.0.1");
@@ -334,7 +339,7 @@ async function renderTree(absDir, relDir) {
334
339
  return `${relDir || "."}/ (directory tree)
335
340
  ${lines.join("\n")}`;
336
341
  }
337
- async function expandMentions(message, cwd3, tokenBudget = DEFAULT_TOKEN_BUDGET) {
342
+ async function expandMentions(message, cwd4, tokenBudget = DEFAULT_TOKEN_BUDGET) {
338
343
  const mentions = extractMentions(message);
339
344
  if (mentions.length === 0) return { context: "", notice: null };
340
345
  const blocks = [];
@@ -344,7 +349,7 @@ async function expandMentions(message, cwd3, tokenBudget = DEFAULT_TOKEN_BUDGET)
344
349
  for (const mention of mentions) {
345
350
  if (spent >= tokenBudget) break;
346
351
  const raw = mention.startsWith("~/") ? join(process.env.HOME ?? "", mention.slice(2)) : mention;
347
- const direct = isAbsolute(raw) ? raw : resolve(cwd3, raw);
352
+ const direct = isAbsolute(raw) ? raw : resolve(cwd4, raw);
348
353
  let isDir = false;
349
354
  try {
350
355
  isDir = (await stat(direct)).isDirectory();
@@ -352,7 +357,7 @@ async function expandMentions(message, cwd3, tokenBudget = DEFAULT_TOKEN_BUDGET)
352
357
  isDir = false;
353
358
  }
354
359
  if (isDir) {
355
- const rel = relative(cwd3, direct);
360
+ const rel = relative(cwd4, direct);
356
361
  const tree = await renderTree(direct, rel);
357
362
  const cost = estTokens(tree);
358
363
  if (spent + cost <= tokenBudget) {
@@ -365,7 +370,7 @@ async function expandMentions(message, cwd3, tokenBudget = DEFAULT_TOKEN_BUDGET)
365
370
  let files = [];
366
371
  try {
367
372
  files = await fg(raw, {
368
- cwd: cwd3,
373
+ cwd: cwd4,
369
374
  ignore: IGNORE,
370
375
  onlyFiles: true,
371
376
  dot: true,
@@ -377,7 +382,7 @@ async function expandMentions(message, cwd3, tokenBudget = DEFAULT_TOKEN_BUDGET)
377
382
  }
378
383
  if (files.length === 0) {
379
384
  try {
380
- if ((await stat(direct)).isFile()) files = [relative(cwd3, direct)];
385
+ if ((await stat(direct)).isFile()) files = [relative(cwd4, direct)];
381
386
  } catch {
382
387
  }
383
388
  }
@@ -388,7 +393,7 @@ async function expandMentions(message, cwd3, tokenBudget = DEFAULT_TOKEN_BUDGET)
388
393
  seen.add(rel);
389
394
  let buf;
390
395
  try {
391
- buf = await readFile2(resolve(cwd3, rel), "utf8");
396
+ buf = await readFile2(resolve(cwd4, rel), "utf8");
392
397
  } catch {
393
398
  continue;
394
399
  }
@@ -563,9 +568,9 @@ function workerToolset(worktreeCtx) {
563
568
  return out;
564
569
  }
565
570
  var WORKER_SYSTEM = "You are an autonomous engineer spawned by a lead engineer to complete ONE focused, self-contained slice of a larger task. You have a full coding toolset (read_file, write_file, edit_file, multi_edit, list_dir, glob, grep, run_command) and you are working in your OWN isolated git worktree \u2014 your edits cannot affect anyone else, so edit freely. Approach: (1) explore the relevant files, (2) make the change, (3) verify it (build/test/typecheck where it makes sense), (4) report. Stay strictly within your assigned slice \u2014 do not refactor unrelated code. When done, return a tight summary: what you changed, which files, and how you verified it.";
566
- function git(args, cwd3, timeoutMs = 3e4) {
571
+ function git(args, cwd4, timeoutMs = 3e4) {
567
572
  return new Promise((res) => {
568
- const child = spawn2("git", args, { cwd: cwd3, shell: false });
573
+ const child = spawn2("git", args, { cwd: cwd4, shell: false });
569
574
  let out = "";
570
575
  const timer = setTimeout(() => child.kill("SIGKILL"), timeoutMs);
571
576
  const sink = (b) => {
@@ -846,6 +851,309 @@ async function runOpsSubcommand(raw) {
846
851
  return 1;
847
852
  }
848
853
 
854
+ // src/factory.ts
855
+ import { existsSync, mkdirSync, readFileSync } from "fs";
856
+ import { join as join3, resolve as resolve2 } from "path";
857
+ import { cwd as cwd2, stderr, stdout as stdout2 } from "process";
858
+ var HELP = `${c.bold("wholestack make")} \u2014 the app factory. One idea in, a full-stack app out.
859
+
860
+ ${c.bold("USAGE")}
861
+ wholestack make ${c.dim('"<idea>"')} make one app from an idea
862
+ wholestack make --batch ${c.dim("<file>")} make one app per non-blank line of a file
863
+
864
+ ${c.bold("OPTIONS")}
865
+ --out ${c.dim("<dir>")} parent dir for delivered apps ${c.dim("(default: ./out)")}
866
+ --batch ${c.dim("<file>")} a file of ideas, one per line (# comments allowed)
867
+ --concurrency ${c.dim("<n>")} build N apps at once ${c.dim("(default: 1; engine may rate-limit)")}
868
+ --json emit NDJSON results to stdout, human chrome to stderr
869
+ --boot boot a free live URL instead of delivering code ${c.dim("(no paywall)")}
870
+ --install run pnpm install in each delivered app
871
+ --max use the zeta-g1-max build model ${c.dim("(default: zeta-g1)")}
872
+ --theme ${c.dim("<id>")} force a theme id
873
+ --style ${c.dim("<id>")} force a Launch Style id (e.g. mono-noir, voltage)
874
+ --color ${c.dim("<d|l>")} force dark or light
875
+ --verbose stream the engine's build phases
876
+ -h, --help show this help
877
+
878
+ ${c.bold("NOTES")}
879
+ \u2022 Delivering code requires a Wholestack membership and spends one build credit per
880
+ app. Run ${c.cyan("wholestack login")} first. ${c.dim("--boot is free but keeps no code.")}
881
+ \u2022 Exit code is 0 only when every requested app shipped.`;
882
+ var KNOWN = /* @__PURE__ */ new Set([
883
+ "--out",
884
+ "--batch",
885
+ "--concurrency",
886
+ "--json",
887
+ "--boot",
888
+ "--install",
889
+ "--max",
890
+ "--theme",
891
+ "--style",
892
+ "--color",
893
+ "--verbose",
894
+ "-h",
895
+ "--help"
896
+ ]);
897
+ function parseFlags(rest) {
898
+ const f = {
899
+ out: "out",
900
+ concurrency: 1,
901
+ json: false,
902
+ boot: false,
903
+ install: false,
904
+ buildModel: "zeta-g1",
905
+ verbose: false,
906
+ help: false,
907
+ ideaParts: []
908
+ };
909
+ for (let i = 0; i < rest.length; i++) {
910
+ const a = rest[i];
911
+ const need = (label) => {
912
+ const v = rest[++i];
913
+ if (v == null || v.startsWith("-")) throw new Error(`${label} needs a value`);
914
+ return v;
915
+ };
916
+ switch (a) {
917
+ case "-h":
918
+ case "--help":
919
+ f.help = true;
920
+ break;
921
+ case "--json":
922
+ f.json = true;
923
+ break;
924
+ case "--boot":
925
+ f.boot = true;
926
+ break;
927
+ case "--install":
928
+ f.install = true;
929
+ break;
930
+ case "--max":
931
+ f.buildModel = "zeta-g1-max";
932
+ break;
933
+ case "--verbose":
934
+ f.verbose = true;
935
+ break;
936
+ case "--out":
937
+ f.out = need("--out");
938
+ break;
939
+ case "--batch":
940
+ f.batch = need("--batch");
941
+ break;
942
+ case "--concurrency": {
943
+ const n = Number(need("--concurrency"));
944
+ if (!Number.isInteger(n) || n < 1 || n > 16)
945
+ throw new Error("--concurrency must be an integer 1\u201316");
946
+ f.concurrency = n;
947
+ break;
948
+ }
949
+ case "--theme":
950
+ f.themeId = need("--theme");
951
+ break;
952
+ case "--style":
953
+ f.styleId = need("--style");
954
+ break;
955
+ case "--color": {
956
+ const v = need("--color").toLowerCase();
957
+ if (v === "d" || v === "dark") f.colorScheme = "dark";
958
+ else if (v === "l" || v === "light") f.colorScheme = "light";
959
+ else throw new Error("--color must be dark or light");
960
+ break;
961
+ }
962
+ default:
963
+ if (a.startsWith("-") && !KNOWN.has(a)) throw new Error(`unknown flag: ${a}`);
964
+ f.ideaParts.push(a);
965
+ }
966
+ }
967
+ return f;
968
+ }
969
+ function slugify(idea) {
970
+ const s = idea.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").split("-").filter(Boolean).slice(0, 5).join("-");
971
+ return s || "app";
972
+ }
973
+ function readBatch(file) {
974
+ const path = resolve2(cwd2(), file);
975
+ if (!existsSync(path)) throw new Error(`batch file not found: ${file}`);
976
+ return readFileSync(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
977
+ }
978
+ function destFor(outRoot, idea, buildId) {
979
+ const base2 = join3(outRoot, slugify(idea));
980
+ if (!existsSync(base2)) return base2;
981
+ return `${base2}-${buildId.slice(-6)}`;
982
+ }
983
+ async function buildOne(engine, idea, flags, outRoot, onPhase) {
984
+ const slug2 = slugify(idea);
985
+ try {
986
+ if (flags.boot) {
987
+ const booted = await demoBootBuild(
988
+ engine,
989
+ { idea, buildModel: flags.buildModel, ...flags.themeId ? { themeId: flags.themeId } : {}, lean: true },
990
+ onPhase
991
+ );
992
+ if (booted.ok && booted.liveUrl) {
993
+ return {
994
+ idea,
995
+ slug: slug2,
996
+ ok: true,
997
+ verdict: booted.verdict,
998
+ files: booted.files,
999
+ liveUrl: booted.liveUrl,
1000
+ buildId: booted.buildId
1001
+ };
1002
+ }
1003
+ return { idea, slug: slug2, ok: false, error: booted.error ?? "boot failed" };
1004
+ }
1005
+ const result = await httpBuild(
1006
+ engine,
1007
+ {
1008
+ idea,
1009
+ buildModel: flags.buildModel,
1010
+ install: false,
1011
+ scope: "full",
1012
+ ...flags.themeId ? { themeId: flags.themeId } : {},
1013
+ ...flags.styleId ? { styleId: flags.styleId } : {},
1014
+ ...flags.colorScheme ? { colorScheme: flags.colorScheme } : {},
1015
+ lean: true
1016
+ },
1017
+ onPhase
1018
+ );
1019
+ if (!result.ok) return { idea, slug: slug2, ok: false, error: result.error ?? "build failed" };
1020
+ if (result.paywalled || !result.buildId) {
1021
+ return {
1022
+ idea,
1023
+ slug: slug2,
1024
+ ok: false,
1025
+ error: `preview only \u2014 subscribe to keep code: ${result.upgradeUrl ?? `${engine.replace(/\/$/, "")}/pricing`}`
1026
+ };
1027
+ }
1028
+ const dest = destFor(outRoot, idea, String(result.buildId));
1029
+ mkdirSync(dest, { recursive: true });
1030
+ const delivered = await deliverBuild(engine, String(result.buildId), dest, onPhase);
1031
+ if (!delivered.ok) {
1032
+ const why = delivered.paywalled ? `subscribe to keep code: ${delivered.upgradeUrl ?? "/pricing"}` : delivered.error ?? "delivery failed";
1033
+ return { idea, slug: slug2, ok: false, buildId: String(result.buildId), error: why };
1034
+ }
1035
+ isolateFromWorkspace(dest);
1036
+ if (flags.install) {
1037
+ const inst = await installDeps(dest, onPhase);
1038
+ if (!inst.ok) onPhase(`install failed \u2014 fix package.json then run pnpm install`);
1039
+ }
1040
+ return {
1041
+ idea,
1042
+ slug: slug2,
1043
+ ok: true,
1044
+ verdict: result.verdict,
1045
+ files: result.fileCount,
1046
+ dir: dest,
1047
+ buildId: String(result.buildId)
1048
+ };
1049
+ } catch (e) {
1050
+ return { idea, slug: slug2, ok: false, error: e.message };
1051
+ }
1052
+ }
1053
+ async function runPool(items, n, worker, onDone) {
1054
+ const results = new Array(items.length);
1055
+ let next = 0;
1056
+ async function lane() {
1057
+ for (; ; ) {
1058
+ const i = next++;
1059
+ if (i >= items.length) return;
1060
+ const r = await worker(items[i], i);
1061
+ results[i] = r;
1062
+ onDone(r, i);
1063
+ }
1064
+ }
1065
+ await Promise.all(Array.from({ length: Math.min(n, items.length) }, () => lane()));
1066
+ return results;
1067
+ }
1068
+ async function runMakeSubcommand(rawArgs) {
1069
+ const verb = rawArgs[0];
1070
+ if (verb !== "make" && verb !== "factory") return null;
1071
+ let flags;
1072
+ try {
1073
+ flags = parseFlags(rawArgs.slice(1));
1074
+ } catch (e) {
1075
+ line(c.red(` ${e.message}`));
1076
+ line(c.dim(" wholestack make --help"));
1077
+ return 1;
1078
+ }
1079
+ if (flags.help) {
1080
+ line(HELP);
1081
+ return 0;
1082
+ }
1083
+ const chrome = flags.json ? (s = "") => stderr.write(s + "\n") : line;
1084
+ let ideas;
1085
+ try {
1086
+ if (flags.batch) {
1087
+ if (flags.ideaParts.length) throw new Error("pass an idea OR --batch, not both");
1088
+ ideas = readBatch(flags.batch);
1089
+ } else {
1090
+ const inline = flags.ideaParts.join(" ").trim();
1091
+ ideas = inline ? [inline] : [];
1092
+ }
1093
+ } catch (e) {
1094
+ chrome(c.red(` ${e.message}`));
1095
+ return 1;
1096
+ }
1097
+ if (!ideas.length) {
1098
+ chrome(c.red(" no idea given."));
1099
+ chrome(c.dim(' wholestack make "a CRM for dog groomers" \xB7 wholestack make --batch ideas.txt'));
1100
+ return 1;
1101
+ }
1102
+ if (!process.env.ZETA_API_KEY?.trim()) {
1103
+ chrome(c.red(" login required."));
1104
+ chrome(c.dim(" run `wholestack login` \u2014 the factory uses your membership and build credits."));
1105
+ return 1;
1106
+ }
1107
+ const engine = webBaseUrl();
1108
+ const outRoot = resolve2(cwd2(), flags.out);
1109
+ const conc = Math.min(flags.concurrency, ideas.length);
1110
+ chrome(
1111
+ c.dim(
1112
+ ` factory \xB7 ${ideas.length} app${ideas.length > 1 ? "s" : ""} \xB7 ${flags.boot ? "boot (live URL)" : `deliver \u2192 ${flags.out}/`} \xB7 ${flags.buildModel}${conc > 1 ? ` \xB7 ${conc}\xD7 parallel` : ""}`
1113
+ )
1114
+ );
1115
+ const render = (r, idx) => {
1116
+ if (flags.json) {
1117
+ stdout2.write(JSON.stringify(r) + "\n");
1118
+ return;
1119
+ }
1120
+ const tag = ideas.length > 1 ? c.dim(`[${idx + 1}/${ideas.length}] `) : "";
1121
+ if (r.ok) {
1122
+ const verdict = r.verdict === "SHIP" ? c.green("SHIP") : c.yellow(r.verdict ?? "?");
1123
+ const where = r.liveUrl ? c.cyan(r.liveUrl) : r.dir ? c.cyan(relOut(flags.out, outRoot, r.dir)) : "";
1124
+ const bits = [`${r.files ?? 0} files`, verdict, where].filter(Boolean);
1125
+ chrome(` ${tag}${c.green("\u2713")} ${r.slug} \xB7 ${bits.join(" \xB7 ")}`);
1126
+ } else {
1127
+ chrome(` ${tag}${c.red("\u2717")} ${r.slug} \xB7 ${r.error ?? "failed"}`);
1128
+ }
1129
+ };
1130
+ const results = await runPool(
1131
+ ideas,
1132
+ conc,
1133
+ (idea, i) => {
1134
+ if (conc === 1 && !flags.json) {
1135
+ const short = idea.length > 56 ? idea.slice(0, 55) + "\u2026" : idea;
1136
+ chrome(`${c.bold("\u2192")} ${short}`);
1137
+ }
1138
+ const tagged = (m) => flags.verbose ? chrome(c.dim(` \u21B3 ${conc > 1 ? `${slugify(idea)}: ` : ""}${m}`)) : void 0;
1139
+ return buildOne(engine, idea, flags, outRoot, tagged);
1140
+ },
1141
+ render
1142
+ );
1143
+ const failures = results.filter((r) => !r.ok).length;
1144
+ if (ideas.length > 1) {
1145
+ const made = ideas.length - failures;
1146
+ chrome(
1147
+ ` ${c.bold("factory done")} \xB7 ${made}/${ideas.length} shipped` + (failures ? c.red(` \xB7 ${failures} failed`) : "")
1148
+ );
1149
+ }
1150
+ return failures ? 1 : 0;
1151
+ }
1152
+ function relOut(outFlag, outRoot, dest) {
1153
+ const tail = dest.startsWith(outRoot) ? dest.slice(outRoot.length).replace(/^[\\/]/, "") : dest;
1154
+ return join3(outFlag, tail);
1155
+ }
1156
+
849
1157
  // src/learned-tools.ts
850
1158
  import { tool as tool3 } from "ai";
851
1159
  import { z as z3 } from "zod";
@@ -931,7 +1239,7 @@ function buildLearnedMemoryTools(deps) {
931
1239
  }
932
1240
 
933
1241
  // src/skills-cli.ts
934
- import { stdout as stdout2 } from "process";
1242
+ import { stdout as stdout3 } from "process";
935
1243
  async function runSkillsSubcommand(raw) {
936
1244
  if (raw[0] !== "skills") return null;
937
1245
  const rest = raw.slice(1);
@@ -951,7 +1259,7 @@ async function runSkillsSubcommand(raw) {
951
1259
  return 1;
952
1260
  }
953
1261
  if (rest.includes("--json")) {
954
- stdout2.write(
1262
+ stdout3.write(
955
1263
  JSON.stringify(
956
1264
  {
957
1265
  name: skill.name,
@@ -974,7 +1282,7 @@ async function runSkillsSubcommand(raw) {
974
1282
  }
975
1283
  const skills = registry.list();
976
1284
  if (rest.includes("--json")) {
977
- stdout2.write(
1285
+ stdout3.write(
978
1286
  JSON.stringify(
979
1287
  skills.map((s) => ({
980
1288
  name: s.name,
@@ -1005,7 +1313,7 @@ async function runSkillsSubcommand(raw) {
1005
1313
 
1006
1314
  // src/images.ts
1007
1315
  import { readFile as readFile3, stat as stat2 } from "fs/promises";
1008
- import { resolve as resolve2, extname, relative as relative2, isAbsolute as isAbsolute2, join as join3 } from "path";
1316
+ import { resolve as resolve3, extname, relative as relative2, isAbsolute as isAbsolute2, join as join4 } from "path";
1009
1317
  import { execFile } from "child_process";
1010
1318
  import { tmpdir as tmpdir2, platform as platform2 } from "os";
1011
1319
  var EXT_TO_MEDIA = {
@@ -1026,7 +1334,7 @@ function run(cmd, args) {
1026
1334
  });
1027
1335
  }
1028
1336
  async function grabClipboardImage() {
1029
- const out = join3(tmpdir2(), `zeta-clip-${process.pid}-${process.hrtime.bigint()}.png`);
1337
+ const out = join4(tmpdir2(), `zeta-clip-${process.pid}-${process.hrtime.bigint()}.png`);
1030
1338
  const os = platform2();
1031
1339
  if (os === "darwin") {
1032
1340
  const script = `set thePath to "${out}"
@@ -1060,7 +1368,7 @@ function imageTokens(message) {
1060
1368
  for (let m = re.exec(message); m; m = re.exec(message)) out.add(m[1]);
1061
1369
  return [...out];
1062
1370
  }
1063
- async function scanImages(message, cwd3) {
1371
+ async function scanImages(message, cwd4) {
1064
1372
  const tokens = imageTokens(message);
1065
1373
  const images = [];
1066
1374
  const skipped = [];
@@ -1089,8 +1397,8 @@ async function scanImages(message, cwd3) {
1089
1397
  }
1090
1398
  }
1091
1399
  for (const tok of tokens) {
1092
- const raw = tok.startsWith("~/") ? join3(process.env.HOME ?? "", tok.slice(2)) : tok;
1093
- const abs = isAbsolute2(raw) ? raw : resolve2(cwd3, raw);
1400
+ const raw = tok.startsWith("~/") ? join4(process.env.HOME ?? "", tok.slice(2)) : tok;
1401
+ const abs = isAbsolute2(raw) ? raw : resolve3(cwd4, raw);
1094
1402
  const mediaType = EXT_TO_MEDIA[extname(abs).toLowerCase()];
1095
1403
  if (!mediaType) continue;
1096
1404
  let bytes;
@@ -1101,7 +1409,7 @@ async function scanImages(message, cwd3) {
1101
1409
  } catch {
1102
1410
  continue;
1103
1411
  }
1104
- const label = isAbsolute2(raw) ? raw : relative2(cwd3, abs);
1412
+ const label = isAbsolute2(raw) ? raw : relative2(cwd4, abs);
1105
1413
  if (bytes > PER_IMAGE_BYTES || total + bytes > TOTAL_BYTES) {
1106
1414
  skipped.push(`${label} (${Math.round(bytes / 1024)}KB \u2014 over the size cap)`);
1107
1415
  continue;
@@ -1197,7 +1505,7 @@ ${body}`);
1197
1505
 
1198
1506
  // src/export.ts
1199
1507
  import { writeFileSync } from "fs";
1200
- import { resolve as resolve3 } from "path";
1508
+ import { resolve as resolve4 } from "path";
1201
1509
  function renderContent(content) {
1202
1510
  if (typeof content === "string") return content.trim();
1203
1511
  if (!Array.isArray(content)) return "";
@@ -1235,32 +1543,32 @@ ${text}`;
1235
1543
  ${body}
1236
1544
  `;
1237
1545
  }
1238
- function writeTranscript(messages, meta, file, cwd3) {
1239
- const abs = resolve3(cwd3, file);
1546
+ function writeTranscript(messages, meta, file, cwd4) {
1547
+ const abs = resolve4(cwd4, file);
1240
1548
  writeFileSync(abs, renderTranscript(messages, meta), "utf8");
1241
1549
  return abs;
1242
1550
  }
1243
1551
 
1244
1552
  // src/cli.ts
1245
- import { existsSync as existsSync2, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
1246
- import { join as join6 } from "path";
1553
+ import { existsSync as existsSync3, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
1554
+ import { join as join7 } from "path";
1247
1555
 
1248
1556
  // src/update-check.ts
1249
- import { existsSync, mkdirSync, readFileSync, writeFileSync as writeFileSync2 } from "fs";
1250
- import { join as join4 } from "path";
1557
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
1558
+ import { join as join5 } from "path";
1251
1559
  var PKG = "wholestack";
1252
- var CACHE = join4(stateDir(), "update-check.json");
1560
+ var CACHE = join5(stateDir(), "update-check.json");
1253
1561
  var DAY_MS = 24 * 60 * 60 * 1e3;
1254
1562
  function readCache() {
1255
1563
  try {
1256
- return JSON.parse(readFileSync(CACHE, "utf8"));
1564
+ return JSON.parse(readFileSync2(CACHE, "utf8"));
1257
1565
  } catch {
1258
1566
  return { lastCheck: 0, latest: null };
1259
1567
  }
1260
1568
  }
1261
1569
  function writeCache(c2) {
1262
1570
  try {
1263
- mkdirSync(stateDir(), { recursive: true });
1571
+ mkdirSync2(stateDir(), { recursive: true });
1264
1572
  writeFileSync2(CACHE, JSON.stringify(c2));
1265
1573
  } catch {
1266
1574
  }
@@ -1276,7 +1584,7 @@ function isNewer(a, b) {
1276
1584
  return false;
1277
1585
  }
1278
1586
  async function checkForUpdate(current) {
1279
- if (process.env.ZETA_NO_UPDATE_CHECK || !existsSync) return null;
1587
+ if (process.env.ZETA_NO_UPDATE_CHECK || !existsSync2) return null;
1280
1588
  const cache = readCache();
1281
1589
  const fresh = Date.now() - cache.lastCheck < DAY_MS;
1282
1590
  let latest = cache.latest;
@@ -1302,20 +1610,20 @@ async function checkForUpdate(current) {
1302
1610
  }
1303
1611
 
1304
1612
  // src/access.ts
1305
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync3 } from "fs";
1306
- import { join as join5 } from "path";
1307
- var CACHE2 = join5(stateDir(), "access.json");
1613
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync3 } from "fs";
1614
+ import { join as join6 } from "path";
1615
+ var CACHE2 = join6(stateDir(), "access.json");
1308
1616
  var TTL_MS = 60 * 60 * 1e3;
1309
1617
  function readCache2() {
1310
1618
  try {
1311
- return JSON.parse(readFileSync2(CACHE2, "utf8"));
1619
+ return JSON.parse(readFileSync3(CACHE2, "utf8"));
1312
1620
  } catch {
1313
1621
  return null;
1314
1622
  }
1315
1623
  }
1316
1624
  function writeCache2(c2) {
1317
1625
  try {
1318
- mkdirSync2(stateDir(), { recursive: true });
1626
+ mkdirSync3(stateDir(), { recursive: true });
1319
1627
  writeFileSync3(CACHE2, JSON.stringify(c2));
1320
1628
  } catch {
1321
1629
  }
@@ -1412,20 +1720,20 @@ function showPaywall(loggedIn, webUrl) {
1412
1720
  }
1413
1721
 
1414
1722
  // src/cli.ts
1415
- import { readFileSync as readFileSync3 } from "fs";
1723
+ import { readFileSync as readFileSync4 } from "fs";
1416
1724
  import { basename as basename2, dirname as dirname2 } from "path";
1417
1725
  import { fileURLToPath } from "url";
1418
1726
  var VERSION = (() => {
1419
1727
  try {
1420
1728
  const here = dirname2(fileURLToPath(import.meta.url));
1421
- return JSON.parse(readFileSync3(join6(here, "..", "package.json"), "utf8")).version;
1729
+ return JSON.parse(readFileSync4(join7(here, "..", "package.json"), "utf8")).version;
1422
1730
  } catch {
1423
1731
  return "0.0.0";
1424
1732
  }
1425
1733
  })();
1426
1734
  function gitBranch(dir) {
1427
1735
  try {
1428
- const head = readFileSync3(join6(dir, ".git", "HEAD"), "utf8").trim();
1736
+ const head = readFileSync4(join7(dir, ".git", "HEAD"), "utf8").trim();
1429
1737
  const m = head.match(/ref:\s*refs\/heads\/(.+)$/);
1430
1738
  return m ? m[1] : head.slice(0, 7);
1431
1739
  } catch {
@@ -1480,13 +1788,19 @@ function parse(raw) {
1480
1788
  a.prompt = rest.join(" ").trim();
1481
1789
  return a;
1482
1790
  }
1483
- var HELP = `${c.cyan("wholestack")} \u2014 conversational coding agent for the Wholestack engine ${c.dim("(alias: zeta)")}
1791
+ var HELP2 = `${c.cyan("wholestack")} \u2014 conversational coding agent for the Wholestack engine ${c.dim("(alias: zeta)")}
1484
1792
 
1485
1793
  ${c.bold("Usage")}
1486
1794
  wholestack start an interactive session
1487
1795
  wholestack "build me a todo app" run one prompt and exit
1488
1796
  wholestack -m zeta-g1-max "..." drive it with the most capable brain
1489
1797
 
1798
+ ${c.bold("Factory")} ${c.dim("(strict \xB7 no chat \xB7 ~0 client tokens \xB7 one idea in, app out)")}
1799
+ wholestack make "a barbershop booking app" make one app \u2192 ./out/<slug>
1800
+ wholestack make --batch ideas.txt one app per line of a file
1801
+ wholestack make "..." --boot free live URL instead of code
1802
+ ${c.dim("wholestack make --help for all flags (--out, --install, --style, --max, \u2026)")}
1803
+
1490
1804
  ${c.bold("Projects")} ${c.dim("(one spine across terminal, dashboard and the web IDE)")}
1491
1805
  wholestack projects list your platform projects
1492
1806
  wholestack pull <id> [dir] download a project's working tree
@@ -1728,7 +2042,7 @@ async function runProveSubcommand(raw) {
1728
2042
  }
1729
2043
  const data = await resp.json().catch(() => null);
1730
2044
  if (wantsJson) {
1731
- stdout3.write(JSON.stringify(data) + "\n");
2045
+ stdout4.write(JSON.stringify(data) + "\n");
1732
2046
  return data?.verdict === "SHIP" ? 0 : 1;
1733
2047
  }
1734
2048
  if (resp.status === 401 || resp.status === 402 || resp.status === 503 || !data) {
@@ -1744,14 +2058,14 @@ async function runProveSubcommand(raw) {
1744
2058
  );
1745
2059
  return data.verdict === "SHIP" ? 0 : 1;
1746
2060
  }
1747
- const path = rest.find((a) => !a.startsWith("-")) ?? cwd2();
1748
- if (!existsSync2(path)) {
2061
+ const path = rest.find((a) => !a.startsWith("-")) ?? cwd3();
2062
+ if (!existsSync3(path)) {
1749
2063
  line(c.red(` path not found: ${path}`));
1750
2064
  return 1;
1751
2065
  }
1752
2066
  const result = proveLocalRepo(path);
1753
2067
  if (wantsJson) {
1754
- stdout3.write(JSON.stringify(result) + "\n");
2068
+ stdout4.write(JSON.stringify(result) + "\n");
1755
2069
  return result.verdict === "SHIP" ? 0 : 1;
1756
2070
  }
1757
2071
  line();
@@ -1796,7 +2110,7 @@ async function runSecuritySubcommand(raw) {
1796
2110
  line();
1797
2111
  line(c.cyan(` \u259F ShipGate ${verb}`) + c.dim(" \xB7 forge \xB7 slither \xB7 firewall \xB7 halmos"));
1798
2112
  line();
1799
- const r = await runProver(proverArgs, { cwd: cwd2(), inherit: true });
2113
+ const r = await runProver(proverArgs, { cwd: cwd3(), inherit: true });
1800
2114
  if (r.missing) {
1801
2115
  line(c.red(` ${r.out}`));
1802
2116
  return 1;
@@ -1808,12 +2122,12 @@ async function runSecuritySubcommand(raw) {
1808
2122
  function maybeYoloNotice() {
1809
2123
  try {
1810
2124
  const dir = stateDir();
1811
- const marker = join6(dir, "yolo-notice-seen");
1812
- if (existsSync2(marker)) return;
2125
+ const marker = join7(dir, "yolo-notice-seen");
2126
+ if (existsSync3(marker)) return;
1813
2127
  line(
1814
2128
  " " + c.yellow("\u26A1 yolo mode") + c.dim(" \u2014 actions run without confirmation. ") + c.cyan("/mode default") + c.dim(" to require approvals \xB7 /undo reverts.") + "\n"
1815
2129
  );
1816
- mkdirSync3(dir, { recursive: true });
2130
+ mkdirSync4(dir, { recursive: true });
1817
2131
  writeFileSync4(marker, (/* @__PURE__ */ new Date()).toISOString());
1818
2132
  } catch {
1819
2133
  }
@@ -1869,7 +2183,7 @@ async function runLogin(raw) {
1869
2183
  line(c.red(" usage: wholestack login --member <key> | --brain <key> | --build <key>"));
1870
2184
  return 1;
1871
2185
  }
1872
- const rl = createInterface({ input: stdin, output: stdout3 });
2186
+ const rl = createInterface({ input: stdin, output: stdout4 });
1873
2187
  if (!name) {
1874
2188
  const which = (await rl.question(c.cyan(" key for [member/brain/engine]: "))).trim().toLowerCase();
1875
2189
  name = which.startsWith("m") ? "ZETA_API_KEY" : "CEREBRAS_API_KEY";
@@ -2051,7 +2365,7 @@ async function runLaunchSubcommand(raw) {
2051
2365
  return 1;
2052
2366
  }
2053
2367
  if (wantsJson) {
2054
- stdout3.write(JSON.stringify(r2.plan, null, 2) + "\n");
2368
+ stdout4.write(JSON.stringify(r2.plan, null, 2) + "\n");
2055
2369
  return 0;
2056
2370
  }
2057
2371
  for (const ln of renderGtmPlanLines(r2.plan)) line(ln);
@@ -2067,7 +2381,7 @@ async function runLaunchSubcommand(raw) {
2067
2381
  return 1;
2068
2382
  }
2069
2383
  if (wantsJson) {
2070
- stdout3.write(JSON.stringify(r.kit, null, 2) + "\n");
2384
+ stdout4.write(JSON.stringify(r.kit, null, 2) + "\n");
2071
2385
  return 0;
2072
2386
  }
2073
2387
  for (const ln of renderLaunchKitLines(r.kit)) line(ln);
@@ -2093,7 +2407,7 @@ async function runHqSubcommand(raw) {
2093
2407
  line(c.red(` \u2717 ${r2.error ?? "no document produced"}`));
2094
2408
  return 1;
2095
2409
  }
2096
- stdout3.write(r2.markdown.trimEnd() + "\n");
2410
+ stdout4.write(r2.markdown.trimEnd() + "\n");
2097
2411
  return 0;
2098
2412
  }
2099
2413
  if (verb === "styles") {
@@ -2110,7 +2424,7 @@ async function runHqSubcommand(raw) {
2110
2424
  return 1;
2111
2425
  }
2112
2426
  if (wantsJson) {
2113
- stdout3.write(JSON.stringify(data, null, 2) + "\n");
2427
+ stdout4.write(JSON.stringify(data, null, 2) + "\n");
2114
2428
  return 0;
2115
2429
  }
2116
2430
  if (data.recommended) {
@@ -2138,7 +2452,7 @@ async function runHqSubcommand(raw) {
2138
2452
  return 1;
2139
2453
  }
2140
2454
  if (wantsJson) {
2141
- stdout3.write(JSON.stringify(r2.metrics, null, 2) + "\n");
2455
+ stdout4.write(JSON.stringify(r2.metrics, null, 2) + "\n");
2142
2456
  return 0;
2143
2457
  }
2144
2458
  for (const ln of renderMetricsLines(r2.metrics)) line(ln);
@@ -2166,7 +2480,7 @@ async function runHqSubcommand(raw) {
2166
2480
  return 1;
2167
2481
  }
2168
2482
  if (wantsJson) {
2169
- stdout3.write(JSON.stringify(r2.businesses ?? [], null, 2) + "\n");
2483
+ stdout4.write(JSON.stringify(r2.businesses ?? [], null, 2) + "\n");
2170
2484
  return 0;
2171
2485
  }
2172
2486
  const rows = r2.businesses ?? [];
@@ -2189,7 +2503,7 @@ async function runHqSubcommand(raw) {
2189
2503
  line(c.red(` \u2717 ${r.error}`));
2190
2504
  return 1;
2191
2505
  }
2192
- stdout3.write(JSON.stringify({ business: r.business, readiness: r.readiness }, null, 2) + "\n");
2506
+ stdout4.write(JSON.stringify({ business: r.business, readiness: r.readiness }, null, 2) + "\n");
2193
2507
  return 0;
2194
2508
  }
2195
2509
  var EMPTY_PLUGINS = {
@@ -2220,6 +2534,8 @@ async function main() {
2220
2534
  const rawArgs = argv.slice(2);
2221
2535
  const loggedIn = await runLogin(rawArgs);
2222
2536
  if (loggedIn !== null) exit(loggedIn);
2537
+ const made = await runMakeSubcommand(rawArgs);
2538
+ if (made !== null) exit(made);
2223
2539
  const gh = await runGithubSubcommand(rawArgs);
2224
2540
  if (gh !== null) exit(gh);
2225
2541
  const provePath = await runProveSubcommand(rawArgs);
@@ -2246,7 +2562,7 @@ async function main() {
2246
2562
  return;
2247
2563
  }
2248
2564
  if (args.help) {
2249
- line(HELP);
2565
+ line(HELP2);
2250
2566
  return;
2251
2567
  }
2252
2568
  {
@@ -2260,7 +2576,7 @@ async function main() {
2260
2576
  token ? "Your session expired." : "The agent is free \u2014 make, edit & preview unlimited. Sign in to start."
2261
2577
  )
2262
2578
  );
2263
- const rl = createInterface({ input: stdin, output: stdout3 });
2579
+ const rl = createInterface({ input: stdin, output: stdout4 });
2264
2580
  const ans = (await rl.question(
2265
2581
  " " + c.dim("Open the browser to sign in with Google? ") + c.dim("[Y/n] ")
2266
2582
  )).trim().toLowerCase();
@@ -2312,7 +2628,7 @@ async function main() {
2312
2628
  }
2313
2629
  const isTty = !!stdin.isTTY;
2314
2630
  const oneShot = args.prompt.length > 0;
2315
- const workdir = cwd2();
2631
+ const workdir = cwd3();
2316
2632
  const persistedMode = isPermissionMode(
2317
2633
  process.env.ZETA_PERMISSION_MODE
2318
2634
  ) ? process.env.ZETA_PERMISSION_MODE : void 0;
package/dist/index.d.ts CHANGED
@@ -173,6 +173,12 @@ interface BuildResult {
173
173
  error?: string;
174
174
  buildId?: unknown;
175
175
  verdict?: "SHIP" | "NO_SHIP";
176
+ /**
177
+ * True when `verdict` is PROVISIONAL — a deferProofs build that shipped fast with its
178
+ * runtime proofs running in a durable background job. The CLI renders "proving…" and
179
+ * polls {@link pollProofStatus} to flip to the real verdict.
180
+ */
181
+ proofPending?: boolean;
176
182
  themeId?: unknown;
177
183
  fileCount?: number;
178
184
  spec?: string | null;
@@ -491,6 +497,29 @@ declare function buildTools(ctx: ToolContext): {
491
497
  error?: string;
492
498
  buildId?: unknown;
493
499
  verdict?: "SHIP" | "NO_SHIP";
500
+ proofPending?: boolean;
501
+ themeId?: unknown;
502
+ fileCount?: number;
503
+ spec?: string | null;
504
+ verifyErrors?: string[];
505
+ fileList?: (string | null | undefined)[];
506
+ paywalled?: boolean;
507
+ upgradeUrl?: string;
508
+ liveUrl?: undefined;
509
+ files?: undefined;
510
+ loc?: undefined;
511
+ trust?: undefined;
512
+ note?: undefined;
513
+ } | {
514
+ delivered: DeliverResult;
515
+ dir: string;
516
+ installed: boolean;
517
+ nextStep: string;
518
+ verdict: string;
519
+ proofPending: boolean;
520
+ ok: boolean;
521
+ error?: string;
522
+ buildId?: unknown;
494
523
  themeId?: unknown;
495
524
  fileCount?: number;
496
525
  spec?: string | null;
@@ -510,6 +539,45 @@ declare function buildTools(ctx: ToolContext): {
510
539
  error?: string;
511
540
  buildId?: unknown;
512
541
  verdict?: "SHIP" | "NO_SHIP";
542
+ proofPending?: boolean;
543
+ themeId?: unknown;
544
+ fileCount?: number;
545
+ spec?: string | null;
546
+ verifyErrors?: string[];
547
+ fileList?: (string | null | undefined)[];
548
+ paywalled?: boolean;
549
+ upgradeUrl?: string;
550
+ liveUrl?: undefined;
551
+ files?: undefined;
552
+ loc?: undefined;
553
+ trust?: undefined;
554
+ note?: undefined;
555
+ } | {
556
+ delivered: DeliverResult;
557
+ dir: string;
558
+ verdict: string;
559
+ proofPending: boolean;
560
+ ok: boolean;
561
+ error?: string;
562
+ buildId?: unknown;
563
+ themeId?: unknown;
564
+ fileCount?: number;
565
+ spec?: string | null;
566
+ verifyErrors?: string[];
567
+ fileList?: (string | null | undefined)[];
568
+ paywalled?: boolean;
569
+ upgradeUrl?: string;
570
+ liveUrl?: undefined;
571
+ files?: undefined;
572
+ loc?: undefined;
573
+ trust?: undefined;
574
+ note?: undefined;
575
+ } | {
576
+ verdict: string;
577
+ proofPending: boolean;
578
+ ok: boolean;
579
+ error?: string;
580
+ buildId?: unknown;
513
581
  themeId?: unknown;
514
582
  fileCount?: number;
515
583
  spec?: string | null;
package/dist/index.js CHANGED
@@ -49,7 +49,7 @@ import {
49
49
  resolveModelKey,
50
50
  severityRank,
51
51
  supportsThinking
52
- } from "./chunk-C56OI76J.js";
52
+ } from "./chunk-UBDW5N5G.js";
53
53
 
54
54
  // src/prompts/template.ts
55
55
  var PLACEHOLDER = /\{\{([A-Za-z0-9_.]+)\}\}/g;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wholestack",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Wholestack — a pro-grade conversational terminal agent for the Wholestack codegen engine. Talk to it in plain language: it writes ISL, generates full-stack or Solidity apps, and proves them with ShipGate. Browser login, membership-gated builds, slash commands, sessions, plan mode, diffs, MCP, plugins.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",