wholestack 0.5.4 → 0.5.6
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 +18 -1
- package/dist/{chunk-FI67ER5S.js → chunk-K6MSDQJT.js} +585 -205
- package/dist/cli.js +153 -117
- package/dist/index.d.ts +8 -5
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -12,91 +12,41 @@ import {
|
|
|
12
12
|
buildTools,
|
|
13
13
|
buildWebTools,
|
|
14
14
|
c,
|
|
15
|
+
collectLocalFiles,
|
|
15
16
|
isPermissionMode,
|
|
16
17
|
killRunningApps,
|
|
17
18
|
line,
|
|
19
|
+
listProjects,
|
|
18
20
|
loadHookFiles,
|
|
19
21
|
loadMcpTools,
|
|
20
22
|
loadPlugins,
|
|
21
23
|
loadProjectMemory,
|
|
24
|
+
loadStoredEnv,
|
|
22
25
|
mergeHookSets,
|
|
23
26
|
modelContextWindow,
|
|
24
27
|
modelLabel,
|
|
28
|
+
openBuildInEditor,
|
|
29
|
+
pullWorkingTree,
|
|
30
|
+
pushWorkingTree,
|
|
31
|
+
readProjectLink,
|
|
25
32
|
resolveModel,
|
|
26
33
|
resolveModelKey,
|
|
27
34
|
runProver,
|
|
35
|
+
saveKey,
|
|
36
|
+
stateDir,
|
|
28
37
|
statusLine,
|
|
29
38
|
supportsThinking,
|
|
30
39
|
tasks,
|
|
31
40
|
userBox,
|
|
32
|
-
visionCapable
|
|
33
|
-
|
|
41
|
+
visionCapable,
|
|
42
|
+
writeProjectLink,
|
|
43
|
+
writeWorkingTree
|
|
44
|
+
} from "./chunk-K6MSDQJT.js";
|
|
34
45
|
|
|
35
46
|
// src/cli.ts
|
|
36
47
|
import { createInterface } from "readline/promises";
|
|
37
48
|
import { stdin, stdout, argv, exit, cwd } from "process";
|
|
38
49
|
|
|
39
|
-
// src/config.ts
|
|
40
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync } from "fs";
|
|
41
|
-
import { homedir } from "os";
|
|
42
|
-
import { join } from "path";
|
|
43
|
-
var CONFIG_DIR = join(homedir(), ".zeta-g");
|
|
44
|
-
var CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
45
|
-
function parseDotenv(text) {
|
|
46
|
-
const out = {};
|
|
47
|
-
for (const raw of text.split("\n")) {
|
|
48
|
-
const l = raw.trim();
|
|
49
|
-
if (!l || l.startsWith("#")) continue;
|
|
50
|
-
const eq = l.indexOf("=");
|
|
51
|
-
if (eq < 0) continue;
|
|
52
|
-
const k = l.slice(0, eq).trim();
|
|
53
|
-
let v = l.slice(eq + 1).trim();
|
|
54
|
-
if (v.startsWith('"') && v.endsWith('"') || v.startsWith("'") && v.endsWith("'")) {
|
|
55
|
-
v = v.slice(1, -1);
|
|
56
|
-
}
|
|
57
|
-
if (k) out[k] = v;
|
|
58
|
-
}
|
|
59
|
-
return out;
|
|
60
|
-
}
|
|
61
|
-
function fill(src) {
|
|
62
|
-
for (const [k, v] of Object.entries(src)) {
|
|
63
|
-
if (v && process.env[k] === void 0) process.env[k] = v;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
function loadStoredEnv() {
|
|
67
|
-
const localEnv = join(process.cwd(), ".env");
|
|
68
|
-
if (existsSync(localEnv)) {
|
|
69
|
-
try {
|
|
70
|
-
fill(parseDotenv(readFileSync(localEnv, "utf8")));
|
|
71
|
-
} catch {
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (existsSync(CONFIG_PATH)) {
|
|
75
|
-
try {
|
|
76
|
-
const json = JSON.parse(readFileSync(CONFIG_PATH, "utf8"));
|
|
77
|
-
const flat = {};
|
|
78
|
-
for (const [k, v] of Object.entries(json)) if (typeof v === "string") flat[k] = v;
|
|
79
|
-
fill(flat);
|
|
80
|
-
} catch {
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
function saveKey(name, value) {
|
|
85
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
86
|
-
let current = {};
|
|
87
|
-
if (existsSync(CONFIG_PATH)) {
|
|
88
|
-
try {
|
|
89
|
-
current = JSON.parse(readFileSync(CONFIG_PATH, "utf8"));
|
|
90
|
-
} catch {
|
|
91
|
-
current = {};
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
current[name] = value;
|
|
95
|
-
writeFileSync(CONFIG_PATH, JSON.stringify(current, null, 2) + "\n", { mode: 384 });
|
|
96
|
-
chmodSync(CONFIG_PATH, 384);
|
|
97
|
-
return CONFIG_PATH;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
50
|
// src/cli-login.ts
|
|
101
51
|
import { createServer } from "http";
|
|
102
52
|
import { spawn } from "child_process";
|
|
@@ -284,7 +234,7 @@ var CheckpointStore = class {
|
|
|
284
234
|
|
|
285
235
|
// src/mentions.ts
|
|
286
236
|
import { readFile as readFile2, stat, readdir } from "fs/promises";
|
|
287
|
-
import { resolve, relative, join
|
|
237
|
+
import { resolve, relative, join, isAbsolute } from "path";
|
|
288
238
|
import fg from "fast-glob";
|
|
289
239
|
var IGNORE = [
|
|
290
240
|
"**/node_modules/**",
|
|
@@ -341,7 +291,7 @@ async function renderTree(absDir, relDir) {
|
|
|
341
291
|
}
|
|
342
292
|
count++;
|
|
343
293
|
lines.push(`${prefix}${e.name}${e.isDirectory() ? "/" : ""}`);
|
|
344
|
-
if (e.isDirectory()) await walk(
|
|
294
|
+
if (e.isDirectory()) await walk(join(dir, e.name), prefix + " ", depth + 1);
|
|
345
295
|
}
|
|
346
296
|
}
|
|
347
297
|
await walk(absDir, "", 0);
|
|
@@ -357,7 +307,7 @@ async function expandMentions(message, cwd2, tokenBudget = DEFAULT_TOKEN_BUDGET)
|
|
|
357
307
|
let spent = 0;
|
|
358
308
|
for (const mention of mentions) {
|
|
359
309
|
if (spent >= tokenBudget) break;
|
|
360
|
-
const raw = mention.startsWith("~/") ?
|
|
310
|
+
const raw = mention.startsWith("~/") ? join(process.env.HOME ?? "", mention.slice(2)) : mention;
|
|
361
311
|
const direct = isAbsolute(raw) ? raw : resolve(cwd2, raw);
|
|
362
312
|
let isDir = false;
|
|
363
313
|
try {
|
|
@@ -484,7 +434,7 @@ function buildSubagentTool(deps) {
|
|
|
484
434
|
|
|
485
435
|
// src/images.ts
|
|
486
436
|
import { readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
487
|
-
import { resolve as resolve2, extname, relative as relative2, isAbsolute as isAbsolute2, join as
|
|
437
|
+
import { resolve as resolve2, extname, relative as relative2, isAbsolute as isAbsolute2, join as join2 } from "path";
|
|
488
438
|
import { execFile } from "child_process";
|
|
489
439
|
import { tmpdir, platform as platform2 } from "os";
|
|
490
440
|
var EXT_TO_MEDIA = {
|
|
@@ -505,7 +455,7 @@ function run(cmd, args) {
|
|
|
505
455
|
});
|
|
506
456
|
}
|
|
507
457
|
async function grabClipboardImage() {
|
|
508
|
-
const out =
|
|
458
|
+
const out = join2(tmpdir(), `zeta-clip-${process.pid}-${process.hrtime.bigint()}.png`);
|
|
509
459
|
const os = platform2();
|
|
510
460
|
if (os === "darwin") {
|
|
511
461
|
const script = `set thePath to "${out}"
|
|
@@ -568,7 +518,7 @@ async function scanImages(message, cwd2) {
|
|
|
568
518
|
}
|
|
569
519
|
}
|
|
570
520
|
for (const tok of tokens) {
|
|
571
|
-
const raw = tok.startsWith("~/") ?
|
|
521
|
+
const raw = tok.startsWith("~/") ? join2(process.env.HOME ?? "", tok.slice(2)) : tok;
|
|
572
522
|
const abs = isAbsolute2(raw) ? raw : resolve2(cwd2, raw);
|
|
573
523
|
const mediaType = EXT_TO_MEDIA[extname(abs).toLowerCase()];
|
|
574
524
|
if (!mediaType) continue;
|
|
@@ -624,7 +574,7 @@ async function fetchReadable(url) {
|
|
|
624
574
|
const res = await fetch(url, {
|
|
625
575
|
signal: ctrl.signal,
|
|
626
576
|
redirect: "follow",
|
|
627
|
-
headers: { "user-agent": "
|
|
577
|
+
headers: { "user-agent": "wholestack-cli (+web-context)" }
|
|
628
578
|
});
|
|
629
579
|
if (!res.ok) return null;
|
|
630
580
|
const ctype = res.headers.get("content-type") ?? "";
|
|
@@ -675,7 +625,7 @@ ${body}`);
|
|
|
675
625
|
}
|
|
676
626
|
|
|
677
627
|
// src/export.ts
|
|
678
|
-
import { writeFileSync
|
|
628
|
+
import { writeFileSync } from "fs";
|
|
679
629
|
import { resolve as resolve3 } from "path";
|
|
680
630
|
function renderContent(content) {
|
|
681
631
|
if (typeof content === "string") return content.trim();
|
|
@@ -692,7 +642,7 @@ function renderContent(content) {
|
|
|
692
642
|
}
|
|
693
643
|
function renderTranscript(messages, meta) {
|
|
694
644
|
const head = [
|
|
695
|
-
`#
|
|
645
|
+
`# wholestack transcript`,
|
|
696
646
|
``,
|
|
697
647
|
`- model: ${meta.model}`,
|
|
698
648
|
meta.startedAt ? `- started: ${meta.startedAt}` : "",
|
|
@@ -716,33 +666,31 @@ ${body}
|
|
|
716
666
|
}
|
|
717
667
|
function writeTranscript(messages, meta, file, cwd2) {
|
|
718
668
|
const abs = resolve3(cwd2, file);
|
|
719
|
-
|
|
669
|
+
writeFileSync(abs, renderTranscript(messages, meta), "utf8");
|
|
720
670
|
return abs;
|
|
721
671
|
}
|
|
722
672
|
|
|
723
673
|
// src/cli.ts
|
|
724
|
-
import { existsSync as
|
|
725
|
-
import {
|
|
726
|
-
import { join as join6 } from "path";
|
|
674
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
675
|
+
import { join as join5 } from "path";
|
|
727
676
|
|
|
728
677
|
// src/update-check.ts
|
|
729
|
-
import { existsSync
|
|
730
|
-
import {
|
|
731
|
-
import { join as join4 } from "path";
|
|
678
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
679
|
+
import { join as join3 } from "path";
|
|
732
680
|
var PKG = "wholestack";
|
|
733
|
-
var CACHE =
|
|
681
|
+
var CACHE = join3(stateDir(), "update-check.json");
|
|
734
682
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
735
683
|
function readCache() {
|
|
736
684
|
try {
|
|
737
|
-
return JSON.parse(
|
|
685
|
+
return JSON.parse(readFileSync(CACHE, "utf8"));
|
|
738
686
|
} catch {
|
|
739
687
|
return { lastCheck: 0, latest: null };
|
|
740
688
|
}
|
|
741
689
|
}
|
|
742
690
|
function writeCache(c2) {
|
|
743
691
|
try {
|
|
744
|
-
|
|
745
|
-
|
|
692
|
+
mkdirSync(stateDir(), { recursive: true });
|
|
693
|
+
writeFileSync2(CACHE, JSON.stringify(c2));
|
|
746
694
|
} catch {
|
|
747
695
|
}
|
|
748
696
|
}
|
|
@@ -757,7 +705,7 @@ function isNewer(a, b) {
|
|
|
757
705
|
return false;
|
|
758
706
|
}
|
|
759
707
|
async function checkForUpdate(current) {
|
|
760
|
-
if (process.env.ZETA_NO_UPDATE_CHECK || !
|
|
708
|
+
if (process.env.ZETA_NO_UPDATE_CHECK || !existsSync) return null;
|
|
761
709
|
const cache = readCache();
|
|
762
710
|
const fresh = Date.now() - cache.lastCheck < DAY_MS;
|
|
763
711
|
let latest = cache.latest;
|
|
@@ -783,22 +731,21 @@ async function checkForUpdate(current) {
|
|
|
783
731
|
}
|
|
784
732
|
|
|
785
733
|
// src/access.ts
|
|
786
|
-
import { mkdirSync as
|
|
787
|
-
import {
|
|
788
|
-
|
|
789
|
-
var CACHE2 = join5(homedir3(), ".zeta-g", "access.json");
|
|
734
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
735
|
+
import { join as join4 } from "path";
|
|
736
|
+
var CACHE2 = join4(stateDir(), "access.json");
|
|
790
737
|
var TTL_MS = 60 * 60 * 1e3;
|
|
791
738
|
function readCache2() {
|
|
792
739
|
try {
|
|
793
|
-
return JSON.parse(
|
|
740
|
+
return JSON.parse(readFileSync2(CACHE2, "utf8"));
|
|
794
741
|
} catch {
|
|
795
742
|
return null;
|
|
796
743
|
}
|
|
797
744
|
}
|
|
798
745
|
function writeCache2(c2) {
|
|
799
746
|
try {
|
|
800
|
-
|
|
801
|
-
|
|
747
|
+
mkdirSync2(stateDir(), { recursive: true });
|
|
748
|
+
writeFileSync3(CACHE2, JSON.stringify(c2));
|
|
802
749
|
} catch {
|
|
803
750
|
}
|
|
804
751
|
}
|
|
@@ -858,20 +805,20 @@ function showPaywall(loggedIn, webUrl) {
|
|
|
858
805
|
}
|
|
859
806
|
|
|
860
807
|
// src/cli.ts
|
|
861
|
-
import { readFileSync as
|
|
808
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
862
809
|
import { basename, dirname as dirname2 } from "path";
|
|
863
810
|
import { fileURLToPath } from "url";
|
|
864
811
|
var VERSION = (() => {
|
|
865
812
|
try {
|
|
866
813
|
const here = dirname2(fileURLToPath(import.meta.url));
|
|
867
|
-
return JSON.parse(
|
|
814
|
+
return JSON.parse(readFileSync3(join5(here, "..", "package.json"), "utf8")).version;
|
|
868
815
|
} catch {
|
|
869
816
|
return "0.0.0";
|
|
870
817
|
}
|
|
871
818
|
})();
|
|
872
819
|
function gitBranch(dir) {
|
|
873
820
|
try {
|
|
874
|
-
const head =
|
|
821
|
+
const head = readFileSync3(join5(dir, ".git", "HEAD"), "utf8").trim();
|
|
875
822
|
const m = head.match(/ref:\s*refs\/heads\/(.+)$/);
|
|
876
823
|
return m ? m[1] : head.slice(0, 7);
|
|
877
824
|
} catch {
|
|
@@ -925,19 +872,25 @@ function parse(raw) {
|
|
|
925
872
|
a.prompt = rest.join(" ").trim();
|
|
926
873
|
return a;
|
|
927
874
|
}
|
|
928
|
-
var HELP = `${c.cyan("
|
|
875
|
+
var HELP = `${c.cyan("wholestack")} \u2014 conversational coding agent for the Wholestack engine ${c.dim("(alias: zeta)")}
|
|
929
876
|
|
|
930
877
|
${c.bold("Usage")}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
878
|
+
wholestack start an interactive session
|
|
879
|
+
wholestack "build me a todo app" run one prompt and exit
|
|
880
|
+
wholestack -m zeta-g1-max "..." drive it with the most capable brain
|
|
881
|
+
|
|
882
|
+
${c.bold("Projects")} ${c.dim("(one spine across terminal, dashboard and the web IDE)")}
|
|
883
|
+
wholestack projects list your platform projects
|
|
884
|
+
wholestack pull <id> [dir] download a project's working tree
|
|
885
|
+
wholestack push [dir] sync local edits back to the web IDE
|
|
886
|
+
wholestack open [buildId] open a generated build in the web IDE
|
|
934
887
|
|
|
935
888
|
${c.bold("Web3 security")} ${c.dim("(forge \xB7 slither \xB7 firewall \xB7 halmos \xB7 signed cert)")}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
889
|
+
wholestack audit <dir> <Contract> full auto-detect security sweep
|
|
890
|
+
wholestack prove <dir> <Contract> --property <k> prove one invariant
|
|
891
|
+
wholestack verify <dir> <Contract> --cert c.json passthrough + signed certificate
|
|
939
892
|
${c.dim("kinds: reentrancy-safety, access-control, conservation, no-value-extraction, \u2026")}
|
|
940
|
-
|
|
893
|
+
wholestack --nzt48 "audit ./contracts" NZT-48 web3 special-ops persona
|
|
941
894
|
|
|
942
895
|
${c.bold("Session")}
|
|
943
896
|
-m, --model <key> zeta-g1-lite | zeta-g1 | zeta-g1-max | vision
|
|
@@ -957,15 +910,15 @@ ${c.bold("Extensibility")}
|
|
|
957
910
|
--no-mcp skip MCP servers --no-plugins skip plugins
|
|
958
911
|
|
|
959
912
|
${c.bold("Build engine")}
|
|
960
|
-
--zeta-url <u>
|
|
913
|
+
--zeta-url <u> build engine base URL (default https://wholestack.ai)
|
|
961
914
|
--local build via the in-repo worker (no server needed)
|
|
962
|
-
--http build via the running
|
|
915
|
+
--http build via the running engine only
|
|
963
916
|
|
|
964
|
-
${c.bold("Auth")} ${c.dim("(set once, then just run
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
917
|
+
${c.bold("Auth")} ${c.dim("(set once, then just run wholestack)")}
|
|
918
|
+
wholestack login browser sign-in (or pick member/brain/engine, paste key)
|
|
919
|
+
wholestack login --member <key> membership key (keep + deploy generated apps)
|
|
920
|
+
wholestack login --brain <key> brain key (powers the Zeta-G1.0 tiers)
|
|
921
|
+
wholestack login --build <key> build-engine key (for /build)
|
|
969
922
|
|
|
970
923
|
${c.bold("In a session")} ${c.dim("(type /help for the full list)")}
|
|
971
924
|
/model /cost /tools /undo /redo /checkpoints /compact /resume /memory /mcp /mode /think /init /doctor /clear /exit
|
|
@@ -1000,14 +953,14 @@ async function runSecuritySubcommand(raw) {
|
|
|
1000
953
|
}
|
|
1001
954
|
function maybeYoloNotice() {
|
|
1002
955
|
try {
|
|
1003
|
-
const dir =
|
|
1004
|
-
const marker =
|
|
1005
|
-
if (
|
|
956
|
+
const dir = stateDir();
|
|
957
|
+
const marker = join5(dir, "yolo-notice-seen");
|
|
958
|
+
if (existsSync2(marker)) return;
|
|
1006
959
|
line(
|
|
1007
960
|
" " + 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"
|
|
1008
961
|
);
|
|
1009
|
-
|
|
1010
|
-
|
|
962
|
+
mkdirSync3(dir, { recursive: true });
|
|
963
|
+
writeFileSync4(marker, (/* @__PURE__ */ new Date()).toISOString());
|
|
1011
964
|
} catch {
|
|
1012
965
|
}
|
|
1013
966
|
}
|
|
@@ -1062,7 +1015,7 @@ async function runLogin(raw) {
|
|
|
1062
1015
|
}
|
|
1063
1016
|
if (!name || !value) {
|
|
1064
1017
|
if (!stdin.isTTY) {
|
|
1065
|
-
line(c.red(" usage:
|
|
1018
|
+
line(c.red(" usage: wholestack login --member <key> | --brain <key> | --build <key>"));
|
|
1066
1019
|
return 1;
|
|
1067
1020
|
}
|
|
1068
1021
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
@@ -1081,7 +1034,88 @@ async function runLogin(raw) {
|
|
|
1081
1034
|
const path = saveKey(name, value);
|
|
1082
1035
|
if (name === "ZETA_API_KEY") clearAccessCache();
|
|
1083
1036
|
line(c.green(` \u2713 saved key for the ${what}`) + c.dim(` \u2192 ${path} (chmod 600)`));
|
|
1084
|
-
line(c.dim(" now just run `
|
|
1037
|
+
line(c.dim(" now just run `wholestack`."));
|
|
1038
|
+
return 0;
|
|
1039
|
+
}
|
|
1040
|
+
async function runProjectSubcommand(raw) {
|
|
1041
|
+
const verb = raw[0];
|
|
1042
|
+
if (verb !== "projects" && verb !== "pull" && verb !== "push" && verb !== "open") return null;
|
|
1043
|
+
const base = webBaseUrl();
|
|
1044
|
+
if (verb === "projects") {
|
|
1045
|
+
const r2 = await listProjects(base);
|
|
1046
|
+
if (!r2.ok) {
|
|
1047
|
+
line(c.red(` ${r2.error}`));
|
|
1048
|
+
return 1;
|
|
1049
|
+
}
|
|
1050
|
+
if (!r2.projects?.length) {
|
|
1051
|
+
line(c.dim(' no projects yet \u2014 build something (`wholestack "an idea"`) or create one in the web IDE.'));
|
|
1052
|
+
return 0;
|
|
1053
|
+
}
|
|
1054
|
+
line();
|
|
1055
|
+
for (const p of r2.projects) {
|
|
1056
|
+
const when = p.updatedAt ? new Date(p.updatedAt).toISOString().slice(0, 10) : "";
|
|
1057
|
+
line(" " + c.cyan(p.id) + " " + c.bold(p.name) + (when ? c.dim(` \xB7 ${when}`) : ""));
|
|
1058
|
+
if (p.description) line(" " + c.dim(` ${p.description}`));
|
|
1059
|
+
}
|
|
1060
|
+
line();
|
|
1061
|
+
line(c.dim(" pull one with ") + c.cyan("wholestack pull <id>") + c.dim(" \xB7 open in the IDE at ") + c.cyan(`${base}/ide/studio?projectId=<id>`));
|
|
1062
|
+
return 0;
|
|
1063
|
+
}
|
|
1064
|
+
if (verb === "pull") {
|
|
1065
|
+
const projectId = raw[1];
|
|
1066
|
+
if (!projectId) {
|
|
1067
|
+
line(c.red(" usage: wholestack pull <projectId> [dir]"));
|
|
1068
|
+
return 1;
|
|
1069
|
+
}
|
|
1070
|
+
const r2 = await pullWorkingTree(base, projectId);
|
|
1071
|
+
if (!r2.ok) {
|
|
1072
|
+
line(c.red(` ${r2.error}`));
|
|
1073
|
+
return 1;
|
|
1074
|
+
}
|
|
1075
|
+
const dest = raw[2] ?? (r2.name ? r2.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") : projectId);
|
|
1076
|
+
const written = writeWorkingTree(dest, r2.files ?? []);
|
|
1077
|
+
writeProjectLink(dest, { projectId });
|
|
1078
|
+
line(c.green(` \u2713 pulled ${written} files`) + c.dim(` \u2192 ${dest}/`));
|
|
1079
|
+
line(c.dim(" edit locally, then ") + c.cyan(`wholestack push ${dest === "." ? "" : dest}`).trimEnd() + c.dim(" to sync back to the web IDE."));
|
|
1080
|
+
return 0;
|
|
1081
|
+
}
|
|
1082
|
+
if (verb === "push") {
|
|
1083
|
+
const dir = raw[1] ?? ".";
|
|
1084
|
+
const link = readProjectLink(dir);
|
|
1085
|
+
const flagIdx = raw.indexOf("--project");
|
|
1086
|
+
const projectId = (flagIdx >= 0 ? raw[flagIdx + 1] : void 0) ?? link?.projectId;
|
|
1087
|
+
if (!projectId) {
|
|
1088
|
+
line(c.red(" no linked project \u2014 pull first, or pass --project <id>."));
|
|
1089
|
+
return 1;
|
|
1090
|
+
}
|
|
1091
|
+
const { files, skipped } = await collectLocalFiles(dir);
|
|
1092
|
+
if (!files.length) {
|
|
1093
|
+
line(c.red(" nothing to push (no readable text files found)."));
|
|
1094
|
+
return 1;
|
|
1095
|
+
}
|
|
1096
|
+
const r2 = await pushWorkingTree(base, projectId, files);
|
|
1097
|
+
if (!r2.ok) {
|
|
1098
|
+
line(c.red(` ${r2.error}`));
|
|
1099
|
+
return 1;
|
|
1100
|
+
}
|
|
1101
|
+
writeProjectLink(dir, { projectId });
|
|
1102
|
+
line(c.green(` \u2713 pushed ${files.length} files`) + c.dim(skipped ? ` (${skipped} skipped: binary/oversize)` : ""));
|
|
1103
|
+
line(c.dim(" open it: ") + c.cyan(`${base}/ide/studio?projectId=${projectId}`));
|
|
1104
|
+
return 0;
|
|
1105
|
+
}
|
|
1106
|
+
const buildId = raw[1] ?? readProjectLink(".")?.buildId;
|
|
1107
|
+
if (!buildId) {
|
|
1108
|
+
line(c.red(" usage: wholestack open <buildId> (or run it inside a delivered app dir)"));
|
|
1109
|
+
return 1;
|
|
1110
|
+
}
|
|
1111
|
+
const r = await openBuildInEditor(base, buildId);
|
|
1112
|
+
if (!r.ok) {
|
|
1113
|
+
line(c.red(` ${r.error}`));
|
|
1114
|
+
return 1;
|
|
1115
|
+
}
|
|
1116
|
+
if (r.projectId) writeProjectLink(".", { buildId, projectId: r.projectId });
|
|
1117
|
+
line(c.green(" \u2713 project ready in the web IDE"));
|
|
1118
|
+
line(c.dim(" ") + c.cyan(`${base}${r.editorUrl ?? `/ide/studio?projectId=${r.projectId}`}`));
|
|
1085
1119
|
return 0;
|
|
1086
1120
|
}
|
|
1087
1121
|
var EMPTY_PLUGINS = {
|
|
@@ -1112,6 +1146,8 @@ async function main() {
|
|
|
1112
1146
|
if (loggedIn !== null) exit(loggedIn);
|
|
1113
1147
|
const sub = await runSecuritySubcommand(rawArgs);
|
|
1114
1148
|
if (sub !== null) exit(sub);
|
|
1149
|
+
const proj = await runProjectSubcommand(rawArgs);
|
|
1150
|
+
if (proj !== null) exit(proj);
|
|
1115
1151
|
const args = parse(rawArgs);
|
|
1116
1152
|
if (args.version) {
|
|
1117
1153
|
line(`wholestack ${VERSION}`);
|
|
@@ -1303,7 +1339,7 @@ ${m.context}` : args.prompt;
|
|
|
1303
1339
|
await ic.runInterruptible((sig) => agent.send(text, sig, images));
|
|
1304
1340
|
statusLine({
|
|
1305
1341
|
model: modelLabel(modelKey),
|
|
1306
|
-
tokens: agent.usage.
|
|
1342
|
+
tokens: agent.usage.outputTokens,
|
|
1307
1343
|
tps: agent.lastTps,
|
|
1308
1344
|
elapsedMs: Date.now() - t0,
|
|
1309
1345
|
cost: agent.usage.totalCost,
|
package/dist/index.d.ts
CHANGED
|
@@ -70,7 +70,7 @@ declare class Permissions {
|
|
|
70
70
|
private readonly confirm?;
|
|
71
71
|
/**
|
|
72
72
|
* Persist a chosen mode so it survives across sessions. Wired by the CLI to
|
|
73
|
-
* write `~/.
|
|
73
|
+
* write `~/.wholestack/config.json`. Optional — when absent, choices are
|
|
74
74
|
* session-only (e.g. piped / non-TTY runs).
|
|
75
75
|
*/
|
|
76
76
|
private readonly persistMode?;
|
|
@@ -79,7 +79,7 @@ declare class Permissions {
|
|
|
79
79
|
constructor(mode: PermissionMode, confirm?: ConfirmFn | undefined,
|
|
80
80
|
/**
|
|
81
81
|
* Persist a chosen mode so it survives across sessions. Wired by the CLI to
|
|
82
|
-
* write `~/.
|
|
82
|
+
* write `~/.wholestack/config.json`. Optional — when absent, choices are
|
|
83
83
|
* session-only (e.g. piped / non-TTY runs).
|
|
84
84
|
*/
|
|
85
85
|
persistMode?: ((mode: PermissionMode) => void) | undefined);
|
|
@@ -194,6 +194,7 @@ declare function buildTools(ctx: ToolContext): {
|
|
|
194
194
|
scope: "full" | "static" | "component" | "page";
|
|
195
195
|
buildModel: "zeta-g1" | "zeta-g1-max";
|
|
196
196
|
keep: boolean;
|
|
197
|
+
themeId?: "slate-minimal" | "clean-inter" | "friendly-nunito" | "geometric-jakarta" | "techno-mono" | undefined;
|
|
197
198
|
}, BuildResult | {
|
|
198
199
|
ok: boolean;
|
|
199
200
|
liveUrl: string | undefined;
|
|
@@ -566,7 +567,7 @@ declare class HookRunner {
|
|
|
566
567
|
}
|
|
567
568
|
/** Merge hook sets (later wins on nothing — they all concatenate). */
|
|
568
569
|
declare function mergeHookSets(...sets: HookSet[]): HookSet;
|
|
569
|
-
/** Load hooks.json from the global config dir and the project's .zeta-g
|
|
570
|
+
/** Load hooks.json from the global config dir and the project's .wholestack/ (or legacy .zeta-g/). */
|
|
570
571
|
declare function loadHookFiles(cwd: string): HookSet;
|
|
571
572
|
/**
|
|
572
573
|
* Wrap every tool's execute so PreToolUse hooks can block it and PostToolUse
|
|
@@ -635,6 +636,8 @@ declare class UsageMeter {
|
|
|
635
636
|
private cost;
|
|
636
637
|
add(modelId: string, u: TokenUsage): void;
|
|
637
638
|
get totalTokens(): number;
|
|
639
|
+
/** Output tokens only — the "what did it actually generate" number. */
|
|
640
|
+
get outputTokens(): number;
|
|
638
641
|
get totalCost(): number;
|
|
639
642
|
snapshot(): {
|
|
640
643
|
input: number;
|
|
@@ -905,7 +908,7 @@ declare function loadMcpTools(opts: LoadMcpOptions): Promise<LoadedMcp>;
|
|
|
905
908
|
|
|
906
909
|
/**
|
|
907
910
|
* Slash commands — the in-session control surface. An extensible registry, not
|
|
908
|
-
* a hardcoded if/else: built-ins live here, plugins and ~/.
|
|
911
|
+
* a hardcoded if/else: built-ins live here, plugins and ~/.wholestack/commands/*.md
|
|
909
912
|
* register more. A command returns a CommandResult the REPL acts on, so the
|
|
910
913
|
* registry stays decoupled from the agent it drives.
|
|
911
914
|
*/
|
|
@@ -971,7 +974,7 @@ declare class CommandRegistry {
|
|
|
971
974
|
get(name: string): SlashCommand | undefined;
|
|
972
975
|
list(): SlashCommand[];
|
|
973
976
|
names(): string[];
|
|
974
|
-
/** Load *.md commands from ~/.
|
|
977
|
+
/** Load *.md commands from ~/.wholestack/commands and <cwd>/.wholestack/commands (or legacy .zeta-g/). */
|
|
975
978
|
loadCustom(cwd: string): void;
|
|
976
979
|
dispatch(input: string, base: Omit<CommandContext, "args" | "commands">): Promise<CommandResult | null>;
|
|
977
980
|
}
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wholestack",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
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",
|