wholestack 0.5.2 → 0.5.4
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/{chunk-TDSLCPQL.js → chunk-FI67ER5S.js} +197 -48
- package/dist/cli.js +11 -43
- package/dist/index.d.ts +25 -1
- package/dist/index.js +1 -1
- package/package.json +11 -11
|
@@ -801,10 +801,10 @@ ${e.message}` });
|
|
|
801
801
|
}
|
|
802
802
|
|
|
803
803
|
// src/app-runner.ts
|
|
804
|
-
import { spawn as spawn3 } from "child_process";
|
|
804
|
+
import { spawn as spawn3, spawnSync } from "child_process";
|
|
805
805
|
import { readFile } from "fs/promises";
|
|
806
|
-
import { existsSync as existsSync3 } from "fs";
|
|
807
|
-
import { join as join3 } from "path";
|
|
806
|
+
import { existsSync as existsSync3, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
807
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
808
808
|
var running = [];
|
|
809
809
|
function killRunningApps() {
|
|
810
810
|
for (const a of running.splice(0)) {
|
|
@@ -834,13 +834,98 @@ async function detectRun(dir) {
|
|
|
834
834
|
}
|
|
835
835
|
return { error: "no dev/start/serve script in package.json" };
|
|
836
836
|
}
|
|
837
|
+
var _pnpmOnPath;
|
|
838
|
+
function pnpmAvailable() {
|
|
839
|
+
if (_pnpmOnPath !== void 0) return _pnpmOnPath;
|
|
840
|
+
try {
|
|
841
|
+
_pnpmOnPath = spawnSync("pnpm", ["--version"], { stdio: "ignore" }).status === 0;
|
|
842
|
+
} catch {
|
|
843
|
+
_pnpmOnPath = false;
|
|
844
|
+
}
|
|
845
|
+
return _pnpmOnPath;
|
|
846
|
+
}
|
|
847
|
+
function installPmFor(dir) {
|
|
848
|
+
if (existsSync3(join3(dir, "yarn.lock"))) return "yarn";
|
|
849
|
+
if (existsSync3(join3(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
850
|
+
return pnpmAvailable() ? "pnpm" : "npm";
|
|
851
|
+
}
|
|
852
|
+
function ensureNpmrc(dir, lines) {
|
|
853
|
+
try {
|
|
854
|
+
const npmrc = join3(dir, ".npmrc");
|
|
855
|
+
const prev = existsSync3(npmrc) ? readFileSync(npmrc, "utf8") : "";
|
|
856
|
+
const missing = lines.filter((l) => !prev.includes(l.split("=")[0]));
|
|
857
|
+
if (missing.length === 0) return;
|
|
858
|
+
const sep3 = prev && !prev.endsWith("\n") ? "\n" : "";
|
|
859
|
+
writeFileSync2(npmrc, prev + sep3 + missing.join("\n") + "\n");
|
|
860
|
+
} catch {
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
function isolateFromWorkspace(dir) {
|
|
864
|
+
if (existsSync3(join3(dir, "pnpm-workspace.yaml"))) return;
|
|
865
|
+
let cur = dirname2(dir);
|
|
866
|
+
let nested = false;
|
|
867
|
+
for (let i = 0; i < 12; i++) {
|
|
868
|
+
if (existsSync3(join3(cur, "pnpm-workspace.yaml"))) {
|
|
869
|
+
nested = true;
|
|
870
|
+
break;
|
|
871
|
+
}
|
|
872
|
+
const up = dirname2(cur);
|
|
873
|
+
if (up === cur) break;
|
|
874
|
+
cur = up;
|
|
875
|
+
}
|
|
876
|
+
if (!nested) return;
|
|
877
|
+
try {
|
|
878
|
+
writeFileSync2(join3(dir, "pnpm-workspace.yaml"), "packages:\n - .\n");
|
|
879
|
+
ensureNpmrc(dir, ["ignore-workspace-root-check=true"]);
|
|
880
|
+
} catch {
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
function installDeps(dir, onLog, signal) {
|
|
884
|
+
const pm = installPmFor(dir);
|
|
885
|
+
if (pm === "pnpm" && !existsSync3(join3(dir, "pnpm-lock.yaml"))) {
|
|
886
|
+
ensureNpmrc(dir, ["shamefully-hoist=true", "prefer-offline=true"]);
|
|
887
|
+
}
|
|
888
|
+
const args = pm === "pnpm" ? (
|
|
889
|
+
// --prefer-offline reuses the shared content-addressed store (hard-links
|
|
890
|
+
// the heavy common deps instead of re-fetching); --ignore-workspace keeps
|
|
891
|
+
// a nested app from walking up into an outer monorepo.
|
|
892
|
+
["install", "--ignore-workspace", "--no-frozen-lockfile", "--prefer-offline", "--config.confirmModulesPurge=false"]
|
|
893
|
+
) : pm === "yarn" ? ["install"] : ["install", "--legacy-peer-deps", "--no-audit", "--no-fund", "--prefer-offline"];
|
|
894
|
+
onLog?.(`installing dependencies (${pm})\u2026`);
|
|
895
|
+
return new Promise((resolve3) => {
|
|
896
|
+
const child = spawn3(pm, args, { cwd: dir, shell: false, env: process.env });
|
|
897
|
+
let log = "";
|
|
898
|
+
const timer = setTimeout(() => child.kill("SIGKILL"), 8 * 6e4);
|
|
899
|
+
const onAbort = () => child.kill("SIGKILL");
|
|
900
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
901
|
+
const sink = (b) => {
|
|
902
|
+
log += b.toString();
|
|
903
|
+
};
|
|
904
|
+
child.stdout.on("data", sink);
|
|
905
|
+
child.stderr.on("data", sink);
|
|
906
|
+
child.on("error", (e) => {
|
|
907
|
+
clearTimeout(timer);
|
|
908
|
+
resolve3({ ok: false, log: log + `
|
|
909
|
+
${e.message}` });
|
|
910
|
+
});
|
|
911
|
+
child.on("close", (code) => {
|
|
912
|
+
clearTimeout(timer);
|
|
913
|
+
signal?.removeEventListener("abort", onAbort);
|
|
914
|
+
resolve3({ ok: code === 0, log: log.slice(-4e3) });
|
|
915
|
+
});
|
|
916
|
+
});
|
|
917
|
+
}
|
|
837
918
|
async function runApp(dir, opts = {}) {
|
|
838
919
|
if (!existsSync3(join3(dir, "node_modules"))) {
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
920
|
+
isolateFromWorkspace(dir);
|
|
921
|
+
const inst = await installDeps(dir, void 0, opts.signal);
|
|
922
|
+
if (!inst.ok) {
|
|
923
|
+
return {
|
|
924
|
+
ok: false,
|
|
925
|
+
log: tail(inst.log),
|
|
926
|
+
error: `dependency install failed in ${dir} \u2014 see log`
|
|
927
|
+
};
|
|
928
|
+
}
|
|
844
929
|
}
|
|
845
930
|
let command;
|
|
846
931
|
let cmdArgs;
|
|
@@ -1007,7 +1092,7 @@ import { tool } from "ai";
|
|
|
1007
1092
|
import { z } from "zod";
|
|
1008
1093
|
import { spawn as spawn5 } from "child_process";
|
|
1009
1094
|
import { readFile as readFile2, writeFile, mkdir, readdir, stat } from "fs/promises";
|
|
1010
|
-
import { resolve as resolve2, dirname as
|
|
1095
|
+
import { resolve as resolve2, dirname as dirname3, relative, join as join4, sep as sep2 } from "path";
|
|
1011
1096
|
import fg from "fast-glob";
|
|
1012
1097
|
var IGNORE = ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/.next/**", "**/build/**", "**/.turbo/**"];
|
|
1013
1098
|
function inside(ctx, p) {
|
|
@@ -1018,9 +1103,19 @@ function inside(ctx, p) {
|
|
|
1018
1103
|
}
|
|
1019
1104
|
return abs;
|
|
1020
1105
|
}
|
|
1021
|
-
function
|
|
1106
|
+
function shQuote(s) {
|
|
1107
|
+
return "'" + s.replace(/'/g, "'\\''") + "'";
|
|
1108
|
+
}
|
|
1109
|
+
var CWD_MARK = "__ZCWD__";
|
|
1110
|
+
function runShellLine(cmdLine, opts) {
|
|
1111
|
+
const shell = process.env.SHELL || "/bin/bash";
|
|
1112
|
+
const script = `cd ${shQuote(opts.cwd)} 2>/dev/null || cd ${shQuote(opts.root)} 2>/dev/null
|
|
1113
|
+
${cmdLine}
|
|
1114
|
+
__zrc=$?
|
|
1115
|
+
printf '\\n${CWD_MARK}:%s\\n' "$PWD"
|
|
1116
|
+
exit $__zrc`;
|
|
1022
1117
|
return new Promise((res) => {
|
|
1023
|
-
const child = spawn5(
|
|
1118
|
+
const child = spawn5(shell, ["-lc", script], { cwd: opts.cwd, shell: false });
|
|
1024
1119
|
let out = "";
|
|
1025
1120
|
const timer = setTimeout(() => child.kill("SIGKILL"), opts.timeoutMs);
|
|
1026
1121
|
const onAbort = () => child.kill("SIGKILL");
|
|
@@ -1033,12 +1128,26 @@ function runShell(cmd, args, opts) {
|
|
|
1033
1128
|
child.on("error", (e) => {
|
|
1034
1129
|
clearTimeout(timer);
|
|
1035
1130
|
res({ code: 1, out: out + `
|
|
1036
|
-
${e.message}
|
|
1131
|
+
${e.message}`, cwd: opts.cwd });
|
|
1037
1132
|
});
|
|
1038
1133
|
child.on("close", (code) => {
|
|
1039
1134
|
clearTimeout(timer);
|
|
1040
1135
|
opts.signal?.removeEventListener("abort", onAbort);
|
|
1041
|
-
|
|
1136
|
+
let nextCwd = opts.cwd;
|
|
1137
|
+
const idx = out.lastIndexOf(`
|
|
1138
|
+
${CWD_MARK}:`);
|
|
1139
|
+
if (idx >= 0) {
|
|
1140
|
+
const rest = out.slice(idx + 1 + CWD_MARK.length + 1);
|
|
1141
|
+
const nl = rest.indexOf("\n");
|
|
1142
|
+
const p = (nl >= 0 ? rest.slice(0, nl) : rest).trim();
|
|
1143
|
+
out = out.slice(0, idx);
|
|
1144
|
+
if (p) {
|
|
1145
|
+
const cand = resolve2(p);
|
|
1146
|
+
const rel = relative(opts.root, cand);
|
|
1147
|
+
nextCwd = rel === "" || rel !== ".." && !rel.startsWith(".." + sep2) ? cand : opts.root;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
res({ code: code ?? 1, out, cwd: nextCwd });
|
|
1042
1151
|
});
|
|
1043
1152
|
});
|
|
1044
1153
|
}
|
|
@@ -1102,6 +1211,7 @@ async function search(ctx, pattern, searchPath, globPat, ignoreCase, maxResults,
|
|
|
1102
1211
|
}
|
|
1103
1212
|
function buildTools(ctx) {
|
|
1104
1213
|
let todos = [];
|
|
1214
|
+
let sessionCwd = ctx.cwd;
|
|
1105
1215
|
const { permissions } = ctx;
|
|
1106
1216
|
return {
|
|
1107
1217
|
todo_write: tool({
|
|
@@ -1194,7 +1304,28 @@ function buildTools(ctx) {
|
|
|
1194
1304
|
dest,
|
|
1195
1305
|
(m) => line(c.dim(` ${m}`))
|
|
1196
1306
|
);
|
|
1197
|
-
|
|
1307
|
+
const rel = relative(ctx.cwd, dest) || ".";
|
|
1308
|
+
if (delivered.ok) {
|
|
1309
|
+
isolateFromWorkspace(dest);
|
|
1310
|
+
if (install) {
|
|
1311
|
+
const inst = await installDeps(dest, (m) => line(c.dim(` ${m}`)));
|
|
1312
|
+
return {
|
|
1313
|
+
...result,
|
|
1314
|
+
delivered,
|
|
1315
|
+
dir: rel,
|
|
1316
|
+
installed: inst.ok,
|
|
1317
|
+
nextStep: inst.ok ? `App ready in "${rel}". Boot it: run_app with dir "${rel}".` : `Code saved to "${rel}" but install failed \u2014 read the log, fix package.json, then run_app.`
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
return {
|
|
1321
|
+
...result,
|
|
1322
|
+
delivered,
|
|
1323
|
+
dir: rel,
|
|
1324
|
+
installed: false,
|
|
1325
|
+
nextStep: `Code saved to "${rel}". Boot it with run_app (dir "${rel}") \u2014 it installs deps automatically.`
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
return { ...result, delivered, dir: rel };
|
|
1198
1329
|
}
|
|
1199
1330
|
return result;
|
|
1200
1331
|
} catch (e) {
|
|
@@ -1281,7 +1412,7 @@ function buildTools(ctx) {
|
|
|
1281
1412
|
ctx.checkpoints?.begin();
|
|
1282
1413
|
ctx.checkpoints?.capture(abs, before);
|
|
1283
1414
|
ctx.checkpoints?.commit(before == null ? `create ${path}` : `write ${path}`);
|
|
1284
|
-
await mkdir(
|
|
1415
|
+
await mkdir(dirname3(abs), { recursive: true });
|
|
1285
1416
|
await writeFile(abs, content, "utf8");
|
|
1286
1417
|
toolLine("write_file", c.dim(path));
|
|
1287
1418
|
return { ok: true, path, bytes: Buffer.byteLength(content) };
|
|
@@ -1464,13 +1595,13 @@ function buildTools(ctx) {
|
|
|
1464
1595
|
}
|
|
1465
1596
|
}),
|
|
1466
1597
|
run_command: tool({
|
|
1467
|
-
description: "Run a shell command in the workspace (e.g. pnpm install, forge test, git status). Use for builds, tests, and inspection. Gated by the permission layer.",
|
|
1598
|
+
description: "Run a shell command in the workspace (e.g. pnpm install, forge test, git status). Runs through a real shell, so chaining (&&), pipes (|), redirects (>) and `cd` all work \u2014 and `cd` persists to the next call. Use for builds, tests, and inspection. Gated by the permission layer.",
|
|
1468
1599
|
inputSchema: z.object({
|
|
1469
|
-
command: z.string().describe("The executable, e.g. pnpm, git, forge, node."),
|
|
1600
|
+
command: z.string().describe("The executable, e.g. pnpm, git, forge, node \u2014 or a full shell line."),
|
|
1470
1601
|
args: z.array(z.string()).default([])
|
|
1471
1602
|
}),
|
|
1472
1603
|
execute: async ({ command, args }, { abortSignal }) => {
|
|
1473
|
-
const display = [command, ...args].join(" ");
|
|
1604
|
+
const display = [command, ...args].join(" ").trim();
|
|
1474
1605
|
if (await permissions.requestCommand(display) === "deny") {
|
|
1475
1606
|
return {
|
|
1476
1607
|
ok: false,
|
|
@@ -1479,16 +1610,23 @@ function buildTools(ctx) {
|
|
|
1479
1610
|
};
|
|
1480
1611
|
}
|
|
1481
1612
|
toolLine("run_command", c.dim(display));
|
|
1482
|
-
const { code, out } = await
|
|
1483
|
-
cwd:
|
|
1613
|
+
const { code, out, cwd: nextCwd } = await runShellLine(display, {
|
|
1614
|
+
cwd: sessionCwd,
|
|
1615
|
+
root: ctx.cwd,
|
|
1484
1616
|
timeoutMs: 5 * 6e4,
|
|
1485
1617
|
signal: abortSignal
|
|
1486
1618
|
});
|
|
1487
|
-
|
|
1619
|
+
sessionCwd = nextCwd;
|
|
1620
|
+
return {
|
|
1621
|
+
ok: code === 0,
|
|
1622
|
+
exitCode: code,
|
|
1623
|
+
cwd: relative(ctx.cwd, sessionCwd) || ".",
|
|
1624
|
+
output: out.slice(-6e3)
|
|
1625
|
+
};
|
|
1488
1626
|
}
|
|
1489
1627
|
}),
|
|
1490
1628
|
run_app: tool({
|
|
1491
|
-
description: "Boot a generated app and watch it come up. Spawns its dev server (pnpm/npm run dev|start), then returns either the live localhost URL (success \u2014 the server keeps running) or the boot log (failure). Use it after generate_app delivers code to disk, or anytime you need to PROVE the app actually runs \u2014 then read runtime/compile errors from the log and fix them.
|
|
1629
|
+
description: "Boot a generated app and watch it come up. Spawns its dev server (pnpm/npm run dev|start), then returns either the live localhost URL (success \u2014 the server keeps running) or the boot log (failure). Use it after generate_app delivers code to disk, or anytime you need to PROVE the app actually runs \u2014 then read runtime/compile errors from the log and fix them. Installs dependencies automatically if node_modules is missing.",
|
|
1492
1630
|
inputSchema: z.object({
|
|
1493
1631
|
dir: z.string().describe("App directory, relative to the workspace (e.g. the delivered zeta-xxxx folder)."),
|
|
1494
1632
|
script: z.string().optional().describe("package.json script to run. Omit to auto-detect dev/start/serve."),
|
|
@@ -1582,7 +1720,7 @@ function buildTools(ctx) {
|
|
|
1582
1720
|
|
|
1583
1721
|
// src/hooks.ts
|
|
1584
1722
|
import { spawn as spawn6 } from "child_process";
|
|
1585
|
-
import { readFileSync, existsSync as existsSync4 } from "fs";
|
|
1723
|
+
import { readFileSync as readFileSync2, existsSync as existsSync4 } from "fs";
|
|
1586
1724
|
import { homedir } from "os";
|
|
1587
1725
|
import { join as join5 } from "path";
|
|
1588
1726
|
var HOOK_TIMEOUT_MS = 15e3;
|
|
@@ -1675,7 +1813,7 @@ function loadHookFiles(cwd) {
|
|
|
1675
1813
|
for (const f of files) {
|
|
1676
1814
|
if (!existsSync4(f)) continue;
|
|
1677
1815
|
try {
|
|
1678
|
-
sets.push(JSON.parse(
|
|
1816
|
+
sets.push(JSON.parse(readFileSync2(f, "utf8")));
|
|
1679
1817
|
} catch {
|
|
1680
1818
|
}
|
|
1681
1819
|
}
|
|
@@ -2366,9 +2504,14 @@ var detectExfiltration = (text) => {
|
|
|
2366
2504
|
note: "fetch( call in untrusted data \u2014 possible network egress."
|
|
2367
2505
|
},
|
|
2368
2506
|
{
|
|
2507
|
+
// A bare process.env reference is ubiquitous in ordinary Node/Next source
|
|
2508
|
+
// (config, feature flags, NODE_ENV checks) — flagging it "high" blocked a
|
|
2509
|
+
// coding agent from reading its own generated files. The real leak risk is
|
|
2510
|
+
// a literal credential VALUE, which detectSecrets already catches; this
|
|
2511
|
+
// stays as a low signal so it warns without withholding the content.
|
|
2369
2512
|
re: /process\.env\b[^\n]{0,40}/,
|
|
2370
|
-
severity: "
|
|
2371
|
-
note: "
|
|
2513
|
+
severity: "low",
|
|
2514
|
+
note: "References environment variables."
|
|
2372
2515
|
},
|
|
2373
2516
|
{
|
|
2374
2517
|
re: /~\/\.ssh\b[^\n]{0,40}/,
|
|
@@ -2718,6 +2861,7 @@ var UNTRUSTED_NATIVE = /* @__PURE__ */ new Set([
|
|
|
2718
2861
|
"verify_contract"
|
|
2719
2862
|
]);
|
|
2720
2863
|
var MCP_DISPATCH = /* @__PURE__ */ new Set(["mcp_call_tool", "mcp_list_tools"]);
|
|
2864
|
+
var LOCAL_SOURCE = /* @__PURE__ */ new Set(["read_file", "grep", "list_dir", "glob"]);
|
|
2721
2865
|
var TEXT_FIELDS = ["content", "output"];
|
|
2722
2866
|
var ARRAY_FIELDS = ["matches", "entries", "files", "results"];
|
|
2723
2867
|
function isUntrustedTool(name) {
|
|
@@ -2738,6 +2882,10 @@ function payloadOf(result) {
|
|
|
2738
2882
|
return JSON.stringify(result).replace(/\\n/g, "\n");
|
|
2739
2883
|
}
|
|
2740
2884
|
function applyFirewall(tools, opts) {
|
|
2885
|
+
const relaxed = new PolicyEngine({
|
|
2886
|
+
...opts.policy.config,
|
|
2887
|
+
onInjection: { ...opts.policy.config.onInjection, high: "strip" }
|
|
2888
|
+
});
|
|
2741
2889
|
const out = {};
|
|
2742
2890
|
for (const [name, t] of Object.entries(tools)) {
|
|
2743
2891
|
if (!isUntrustedTool(name)) {
|
|
@@ -2751,6 +2899,7 @@ function applyFirewall(tools, opts) {
|
|
|
2751
2899
|
continue;
|
|
2752
2900
|
}
|
|
2753
2901
|
const kind = kindForTool(name);
|
|
2902
|
+
const policy = LOCAL_SOURCE.has(name) ? relaxed : opts.policy;
|
|
2754
2903
|
out[name] = {
|
|
2755
2904
|
...anyTool,
|
|
2756
2905
|
execute: async (input, o) => {
|
|
@@ -2759,7 +2908,7 @@ function applyFirewall(tools, opts) {
|
|
|
2759
2908
|
if (text == null) return result;
|
|
2760
2909
|
const verdict = opts.firewall.guard(
|
|
2761
2910
|
{ kind, authority: channelAuthority(kind), source: name, content: text, trusted: false },
|
|
2762
|
-
|
|
2911
|
+
policy
|
|
2763
2912
|
);
|
|
2764
2913
|
opts.onFinding?.(verdict, name);
|
|
2765
2914
|
if (typeof result === "string") return verdict.safe;
|
|
@@ -3492,7 +3641,7 @@ function announcePlanMode() {
|
|
|
3492
3641
|
import { readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
3493
3642
|
import { existsSync as existsSync5 } from "fs";
|
|
3494
3643
|
import { homedir as homedir2 } from "os";
|
|
3495
|
-
import { join as join6, dirname as
|
|
3644
|
+
import { join as join6, dirname as dirname4 } from "path";
|
|
3496
3645
|
var FILE_NAMES = ["ZETA.md", "AGENTS.md", "CLAUDE.md"];
|
|
3497
3646
|
var MAX_TOTAL = 14e3;
|
|
3498
3647
|
var MAX_PER_FILE = 8e3;
|
|
@@ -3500,7 +3649,7 @@ function repoRootFrom(start) {
|
|
|
3500
3649
|
let dir = start;
|
|
3501
3650
|
for (let i = 0; i < 24; i++) {
|
|
3502
3651
|
if (existsSync5(join6(dir, ".git"))) return dir;
|
|
3503
|
-
const up =
|
|
3652
|
+
const up = dirname4(dir);
|
|
3504
3653
|
if (up === dir) break;
|
|
3505
3654
|
dir = up;
|
|
3506
3655
|
}
|
|
@@ -3525,7 +3674,7 @@ async function loadProjectMemory(cwd) {
|
|
|
3525
3674
|
for (let i = 0; i < 24; i++) {
|
|
3526
3675
|
chain.unshift(dir);
|
|
3527
3676
|
if (dir === root) break;
|
|
3528
|
-
const up =
|
|
3677
|
+
const up = dirname4(dir);
|
|
3529
3678
|
if (up === dir) break;
|
|
3530
3679
|
dir = up;
|
|
3531
3680
|
}
|
|
@@ -3560,7 +3709,7 @@ ${r.text.trim()}
|
|
|
3560
3709
|
import {
|
|
3561
3710
|
appendFileSync,
|
|
3562
3711
|
mkdirSync as mkdirSync2,
|
|
3563
|
-
readFileSync as
|
|
3712
|
+
readFileSync as readFileSync3,
|
|
3564
3713
|
readdirSync,
|
|
3565
3714
|
existsSync as existsSync6,
|
|
3566
3715
|
statSync
|
|
@@ -3605,7 +3754,7 @@ var Session = class _Session {
|
|
|
3605
3754
|
static resume(id) {
|
|
3606
3755
|
const path = fileFor(id);
|
|
3607
3756
|
if (!existsSync6(path)) return null;
|
|
3608
|
-
const lines =
|
|
3757
|
+
const lines = readFileSync3(path, "utf8").split("\n").filter(Boolean);
|
|
3609
3758
|
let meta = null;
|
|
3610
3759
|
const messages = [];
|
|
3611
3760
|
for (const l of lines) {
|
|
@@ -3637,7 +3786,7 @@ var Session = class _Session {
|
|
|
3637
3786
|
let preview = "";
|
|
3638
3787
|
let turns = 0;
|
|
3639
3788
|
try {
|
|
3640
|
-
const lines =
|
|
3789
|
+
const lines = readFileSync3(path, "utf8").split("\n").filter(Boolean);
|
|
3641
3790
|
for (const l of lines) {
|
|
3642
3791
|
const rec = JSON.parse(l);
|
|
3643
3792
|
if (rec.kind === "meta") meta = { id: rec.id, cwd: rec.cwd, model: rec.model, startedAt: rec.startedAt };
|
|
@@ -3670,7 +3819,7 @@ var Session = class _Session {
|
|
|
3670
3819
|
};
|
|
3671
3820
|
|
|
3672
3821
|
// src/commands.ts
|
|
3673
|
-
import { writeFileSync as
|
|
3822
|
+
import { writeFileSync as writeFileSync3, existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
|
|
3674
3823
|
import { homedir as homedir4 } from "os";
|
|
3675
3824
|
import { join as join8 } from "path";
|
|
3676
3825
|
|
|
@@ -4004,7 +4153,7 @@ var BUILTINS = [
|
|
|
4004
4153
|
return { type: "handled" };
|
|
4005
4154
|
}
|
|
4006
4155
|
try {
|
|
4007
|
-
|
|
4156
|
+
writeFileSync3(path, ZETA_MD_TEMPLATE, "utf8");
|
|
4008
4157
|
ctx.print(" " + c.green(`\u2713 wrote ${path}`) + c.dim(" \xB7 edit it, then /clear to reload"));
|
|
4009
4158
|
} catch (e) {
|
|
4010
4159
|
ctx.print(" " + c.red(e.message));
|
|
@@ -4206,7 +4355,7 @@ function loadMdCommands(dir, source) {
|
|
|
4206
4355
|
for (const file of readdirSync2(dir)) {
|
|
4207
4356
|
if (!file.endsWith(".md")) continue;
|
|
4208
4357
|
try {
|
|
4209
|
-
const body =
|
|
4358
|
+
const body = readFileSync4(join8(dir, file), "utf8");
|
|
4210
4359
|
cmds.push(parseCustom(file.replace(/\.md$/, ""), body, source));
|
|
4211
4360
|
} catch {
|
|
4212
4361
|
}
|
|
@@ -4262,7 +4411,7 @@ var CommandRegistry = class {
|
|
|
4262
4411
|
};
|
|
4263
4412
|
|
|
4264
4413
|
// src/plugins.ts
|
|
4265
|
-
import { readFileSync as
|
|
4414
|
+
import { readFileSync as readFileSync5, existsSync as existsSync8, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
|
|
4266
4415
|
import { homedir as homedir5 } from "os";
|
|
4267
4416
|
import { join as join9 } from "path";
|
|
4268
4417
|
function pluginRoots(cwd) {
|
|
@@ -4272,7 +4421,7 @@ function resolveSystemPrompt(dir, value) {
|
|
|
4272
4421
|
const asFile = join9(dir, value);
|
|
4273
4422
|
if (value.length < 200 && existsSync8(asFile)) {
|
|
4274
4423
|
try {
|
|
4275
|
-
return
|
|
4424
|
+
return readFileSync5(asFile, "utf8").trim();
|
|
4276
4425
|
} catch {
|
|
4277
4426
|
return "";
|
|
4278
4427
|
}
|
|
@@ -4304,7 +4453,7 @@ function loadPlugins(cwd) {
|
|
|
4304
4453
|
const manifestPath = join9(dir, "zeta-plugin.json");
|
|
4305
4454
|
if (!existsSync8(manifestPath)) continue;
|
|
4306
4455
|
try {
|
|
4307
|
-
const manifest = JSON.parse(
|
|
4456
|
+
const manifest = JSON.parse(readFileSync5(manifestPath, "utf8"));
|
|
4308
4457
|
const name = manifest.name ?? entry;
|
|
4309
4458
|
if (manifest.systemPrompt) {
|
|
4310
4459
|
const text = resolveSystemPrompt(dir, manifest.systemPrompt);
|
|
@@ -4340,15 +4489,15 @@ import { dynamicTool, jsonSchema } from "ai";
|
|
|
4340
4489
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4341
4490
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
4342
4491
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
4343
|
-
import { readFileSync as
|
|
4492
|
+
import { readFileSync as readFileSync6, existsSync as existsSync9 } from "fs";
|
|
4344
4493
|
import { homedir as homedir6 } from "os";
|
|
4345
|
-
import { join as join10, dirname as
|
|
4494
|
+
import { join as join10, dirname as dirname5 } from "path";
|
|
4346
4495
|
function isHttp(c2) {
|
|
4347
4496
|
return typeof c2.url === "string";
|
|
4348
4497
|
}
|
|
4349
4498
|
function readConfigFile(path) {
|
|
4350
4499
|
try {
|
|
4351
|
-
const json = JSON.parse(
|
|
4500
|
+
const json = JSON.parse(readFileSync6(path, "utf8"));
|
|
4352
4501
|
return json.mcpServers ?? {};
|
|
4353
4502
|
} catch {
|
|
4354
4503
|
return {};
|
|
@@ -4365,7 +4514,7 @@ function discoverConfigs(cwd) {
|
|
|
4365
4514
|
Object.assign(merged, readConfigFile(p));
|
|
4366
4515
|
break;
|
|
4367
4516
|
}
|
|
4368
|
-
const up =
|
|
4517
|
+
const up = dirname5(dir);
|
|
4369
4518
|
if (up === dir) break;
|
|
4370
4519
|
dir = up;
|
|
4371
4520
|
}
|
|
@@ -4618,14 +4767,14 @@ function buildWebTools() {
|
|
|
4618
4767
|
import { createInterface } from "readline/promises";
|
|
4619
4768
|
import { emitKeypressEvents } from "readline";
|
|
4620
4769
|
import { stdin, stdout } from "process";
|
|
4621
|
-
import { readFileSync as
|
|
4770
|
+
import { readFileSync as readFileSync7, appendFileSync as appendFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync10, readdirSync as readdirSync4 } from "fs";
|
|
4622
4771
|
import { homedir as homedir7 } from "os";
|
|
4623
|
-
import { join as join11, dirname as
|
|
4772
|
+
import { join as join11, dirname as dirname6, basename } from "path";
|
|
4624
4773
|
var HISTORY_FILE = join11(homedir7(), ".zeta-g", "history");
|
|
4625
4774
|
var HISTORY_MAX = 1e3;
|
|
4626
4775
|
function loadHistory() {
|
|
4627
4776
|
try {
|
|
4628
|
-
return
|
|
4777
|
+
return readFileSync7(HISTORY_FILE, "utf8").split("\n").filter(Boolean).slice(-HISTORY_MAX);
|
|
4629
4778
|
} catch {
|
|
4630
4779
|
return [];
|
|
4631
4780
|
}
|
|
@@ -4661,7 +4810,7 @@ var InputController = class {
|
|
|
4661
4810
|
const at = line2.lastIndexOf("@");
|
|
4662
4811
|
if (at >= 0 && at >= line2.lastIndexOf(" ")) {
|
|
4663
4812
|
const partial = line2.slice(at + 1);
|
|
4664
|
-
const dir = partial.includes("/") ?
|
|
4813
|
+
const dir = partial.includes("/") ? dirname6(partial) : ".";
|
|
4665
4814
|
const base = basename(partial);
|
|
4666
4815
|
try {
|
|
4667
4816
|
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
@@ -4927,7 +5076,7 @@ var InputController = class {
|
|
|
4927
5076
|
}
|
|
4928
5077
|
saveHistory(entry) {
|
|
4929
5078
|
try {
|
|
4930
|
-
mkdirSync3(
|
|
5079
|
+
mkdirSync3(dirname6(HISTORY_FILE), { recursive: true });
|
|
4931
5080
|
if (!existsSync10(HISTORY_FILE) || entry !== loadHistory().slice(-1)[0]) {
|
|
4932
5081
|
appendFileSync2(HISTORY_FILE, entry.replace(/\n/g, " ") + "\n", "utf8");
|
|
4933
5082
|
}
|
package/dist/cli.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
tasks,
|
|
31
31
|
userBox,
|
|
32
32
|
visionCapable
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-FI67ER5S.js";
|
|
34
34
|
|
|
35
35
|
// src/cli.ts
|
|
36
36
|
import { createInterface } from "readline/promises";
|
|
@@ -857,40 +857,18 @@ function showPaywall(loggedIn, webUrl) {
|
|
|
857
857
|
line();
|
|
858
858
|
}
|
|
859
859
|
|
|
860
|
-
// src/art.ts
|
|
861
|
-
var ART_PACK = [
|
|
862
|
-
[" \u2588\u2588\u2588\u2588\u2588\u2588\u2588", " \u2588\u2588\u2554\u255D", " \u2588\u2588\u2554\u255D ", " \u2588\u2588\u2554\u255D ", " \u2588\u2588\u2588\u2588\u2588\u2588\u2588 "],
|
|
863
|
-
[" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E", " \u2502 \u2571\u2571 \u2502", " \u2502 \u2571\u2571 \u2502", " \u2502\u2571\u2571 \u2502", " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"],
|
|
864
|
-
[" \u259F\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2599", " \u259F\u2588\u259B ", " \u259F\u2588\u259B ", " \u259F\u2588\u259B ", " \u259C\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u259B "],
|
|
865
|
-
[" \u250C\u2500\u2510\u250C\u2500\u2510\u250C\u252C\u2510\u250C\u2500\u2510", " \u250C\u2500\u2518\u251C\u2524 \u2502 \u251C\u2500\u2524", " \u2514\u2500\u2518\u2514\u2500\u2518 \u2534 \u2534 \u2534"],
|
|
866
|
-
[" \u2571\u2572 ", " \u2571\u2500\u2500\u2572 ", " \u2571 \u2571\u2571 \u2572 ", "\u2571 \u2571\u2571 \u2572 ", "\u2572\u2571\u2571\u2500\u2500\u2500\u2500\u2500\u2572"]
|
|
867
|
-
];
|
|
868
|
-
function randomArt(seed) {
|
|
869
|
-
const i = seed != null ? seed % ART_PACK.length : Math.floor(Math.random() * ART_PACK.length);
|
|
870
|
-
return ART_PACK[(i % ART_PACK.length + ART_PACK.length) % ART_PACK.length];
|
|
871
|
-
}
|
|
872
|
-
var vlen = (s) => s.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
873
|
-
function fetchPanel(fields, art = randomArt()) {
|
|
874
|
-
const shown = fields.filter((f) => f.value);
|
|
875
|
-
const labelW = Math.max(0, ...shown.map((f) => f.label.length));
|
|
876
|
-
const artW = Math.max(0, ...art.map(vlen));
|
|
877
|
-
const rows = Math.max(art.length, shown.length);
|
|
878
|
-
const out = [];
|
|
879
|
-
out.push("");
|
|
880
|
-
for (let i = 0; i < rows; i++) {
|
|
881
|
-
const left = (art[i] ?? "").padEnd(artW + 2);
|
|
882
|
-
const f = shown[i];
|
|
883
|
-
const right = f ? c.dim(f.label.padStart(labelW) + " ") + c.bold(f.value) : "";
|
|
884
|
-
out.push(" " + c.cyan(left) + right);
|
|
885
|
-
}
|
|
886
|
-
out.push("");
|
|
887
|
-
for (const l of out) process.stdout.write(l + "\n");
|
|
888
|
-
}
|
|
889
|
-
|
|
890
860
|
// src/cli.ts
|
|
891
861
|
import { readFileSync as readFileSync4 } from "fs";
|
|
892
|
-
import { basename } from "path";
|
|
893
|
-
|
|
862
|
+
import { basename, dirname as dirname2 } from "path";
|
|
863
|
+
import { fileURLToPath } from "url";
|
|
864
|
+
var VERSION = (() => {
|
|
865
|
+
try {
|
|
866
|
+
const here = dirname2(fileURLToPath(import.meta.url));
|
|
867
|
+
return JSON.parse(readFileSync4(join6(here, "..", "package.json"), "utf8")).version;
|
|
868
|
+
} catch {
|
|
869
|
+
return "0.0.0";
|
|
870
|
+
}
|
|
871
|
+
})();
|
|
894
872
|
function gitBranch(dir) {
|
|
895
873
|
try {
|
|
896
874
|
const head = readFileSync4(join6(dir, ".git", "HEAD"), "utf8").trim();
|
|
@@ -1295,16 +1273,6 @@ ${m.context}` : args.prompt;
|
|
|
1295
1273
|
return;
|
|
1296
1274
|
}
|
|
1297
1275
|
await banner();
|
|
1298
|
-
fetchPanel([
|
|
1299
|
-
{ label: "model", value: modelLabel(modelKey) },
|
|
1300
|
-
{ label: "mode", value: mode },
|
|
1301
|
-
{ label: "dir", value: basename(workdir) },
|
|
1302
|
-
{ label: "git", value: gitBranch(workdir) },
|
|
1303
|
-
{ label: "node", value: process.version },
|
|
1304
|
-
{ label: "memory", value: memory.sources.length ? `${memory.sources.length} file(s)` : "" },
|
|
1305
|
-
{ label: "mcp", value: mcp.mounted.length ? `${mcp.mounted.length} mounted` : "" },
|
|
1306
|
-
{ label: "zeta", value: VERSION }
|
|
1307
|
-
]);
|
|
1308
1276
|
bootSummary(memory, plugins, mcp, mode, thinking);
|
|
1309
1277
|
if (mode === "plan") announcePlanMode();
|
|
1310
1278
|
else if (yoloIsDefault) maybeYoloNotice();
|
package/dist/index.d.ts
CHANGED
|
@@ -190,9 +190,9 @@ declare function buildTools(ctx: ToolContext): {
|
|
|
190
190
|
}>;
|
|
191
191
|
generate_app: ai.Tool<{
|
|
192
192
|
idea: string;
|
|
193
|
+
install: boolean;
|
|
193
194
|
scope: "full" | "static" | "component" | "page";
|
|
194
195
|
buildModel: "zeta-g1" | "zeta-g1-max";
|
|
195
|
-
install: boolean;
|
|
196
196
|
keep: boolean;
|
|
197
197
|
}, BuildResult | {
|
|
198
198
|
ok: boolean;
|
|
@@ -205,6 +205,28 @@ declare function buildTools(ctx: ToolContext): {
|
|
|
205
205
|
note: string;
|
|
206
206
|
} | {
|
|
207
207
|
delivered: DeliverResult;
|
|
208
|
+
dir: string;
|
|
209
|
+
installed: boolean;
|
|
210
|
+
nextStep: string;
|
|
211
|
+
ok: boolean;
|
|
212
|
+
error?: string;
|
|
213
|
+
buildId?: unknown;
|
|
214
|
+
verdict?: "SHIP" | "NO_SHIP";
|
|
215
|
+
themeId?: unknown;
|
|
216
|
+
fileCount?: number;
|
|
217
|
+
spec?: string | null;
|
|
218
|
+
verifyErrors?: string[];
|
|
219
|
+
fileList?: (string | null | undefined)[];
|
|
220
|
+
paywalled?: boolean;
|
|
221
|
+
upgradeUrl?: string;
|
|
222
|
+
liveUrl?: undefined;
|
|
223
|
+
files?: undefined;
|
|
224
|
+
loc?: undefined;
|
|
225
|
+
trust?: undefined;
|
|
226
|
+
note?: undefined;
|
|
227
|
+
} | {
|
|
228
|
+
delivered: DeliverResult;
|
|
229
|
+
dir: string;
|
|
208
230
|
ok: boolean;
|
|
209
231
|
error?: string;
|
|
210
232
|
buildId?: unknown;
|
|
@@ -404,10 +426,12 @@ declare function buildTools(ctx: ToolContext): {
|
|
|
404
426
|
declined: boolean;
|
|
405
427
|
error: string;
|
|
406
428
|
exitCode?: undefined;
|
|
429
|
+
cwd?: undefined;
|
|
407
430
|
output?: undefined;
|
|
408
431
|
} | {
|
|
409
432
|
ok: boolean;
|
|
410
433
|
exitCode: number;
|
|
434
|
+
cwd: string;
|
|
411
435
|
output: string;
|
|
412
436
|
declined?: undefined;
|
|
413
437
|
error?: undefined;
|
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.4",
|
|
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",
|
|
@@ -47,12 +47,6 @@
|
|
|
47
47
|
"engines": {
|
|
48
48
|
"node": ">=20.0.0"
|
|
49
49
|
},
|
|
50
|
-
"scripts": {
|
|
51
|
-
"build": "tsup src/index.ts src/cli.ts --format esm --dts --clean && node -e \"require('fs').chmodSync('dist/cli.js','755')\"",
|
|
52
|
-
"dev": "tsx src/cli.ts",
|
|
53
|
-
"typecheck": "tsc --noEmit",
|
|
54
|
-
"clean": "rimraf dist"
|
|
55
|
-
},
|
|
56
50
|
"dependencies": {
|
|
57
51
|
"@ai-sdk/openai": "^3.0.64",
|
|
58
52
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
@@ -62,15 +56,21 @@
|
|
|
62
56
|
"zod": "^3.25.76"
|
|
63
57
|
},
|
|
64
58
|
"devDependencies": {
|
|
65
|
-
"@isl-lang/zeta-models": "workspace:*",
|
|
66
59
|
"@types/diff": "^5.2.0",
|
|
67
60
|
"@types/node": "^20.10.0",
|
|
68
61
|
"tsup": "^8.0.1",
|
|
69
62
|
"tsx": "^4.7.0",
|
|
70
63
|
"typescript": "^5.3.3",
|
|
71
|
-
"rimraf": "^5.0.5"
|
|
64
|
+
"rimraf": "^5.0.5",
|
|
65
|
+
"@isl-lang/zeta-models": "1.0.0"
|
|
72
66
|
},
|
|
73
67
|
"author": "Wholestack",
|
|
74
68
|
"license": "MIT",
|
|
75
|
-
"sideEffects": false
|
|
76
|
-
|
|
69
|
+
"sideEffects": false,
|
|
70
|
+
"scripts": {
|
|
71
|
+
"build": "tsup src/index.ts src/cli.ts --format esm --dts --clean && node -e \"require('fs').chmodSync('dist/cli.js','755')\"",
|
|
72
|
+
"dev": "tsx src/cli.ts",
|
|
73
|
+
"typecheck": "tsc --noEmit",
|
|
74
|
+
"clean": "rimraf dist"
|
|
75
|
+
}
|
|
76
|
+
}
|