@wrongstack/cli 0.305.0 → 0.306.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{auth-K7CYVVKH.js → auth-GVPYLJPS.js} +7 -7
- package/dist/boot/short-circuit-flags.d.ts +7 -1
- package/dist/boot/system-prompt-menu.d.ts +151 -0
- package/dist/chimera-cascade-evidence.d.ts +102 -0
- package/dist/chimera-review-task.d.ts +7 -0
- package/dist/{chunk-C5NXM2SI.js → chunk-5JW3XCRA.js} +7 -3
- package/dist/{chunk-FJVCVZIV.js → chunk-WC3DK6BI.js} +5 -5
- package/dist/{cli-main-W3T56YCY.js → cli-main-DHNFGP3Z.js} +278 -197
- package/dist/diag-doctor-WTBHPOMB.js +329 -0
- package/dist/{execution-YMBD7YWC.js → execution-TTE7R5UY.js} +313 -53
- package/dist/execution-chimera-cascade.d.ts +7 -1
- package/dist/{hq-5G6D5YIU.js → hq-ZK6PGN3W.js} +95 -6
- package/dist/index.js +759 -546
- package/dist/{modeldiag-SIVUOX2Y.js → modeldiag-6VS5KDP5.js} +4 -4
- package/dist/subcommands/handlers/daemon-inventory.d.ts +41 -0
- package/dist/wiring/management-tools.d.ts +8 -1
- package/package.json +24 -23
- package/dist/diag-doctor-ZF5BF2MK.js +0 -169
package/dist/index.js
CHANGED
|
@@ -11,17 +11,38 @@ import {
|
|
|
11
11
|
theme
|
|
12
12
|
} from "./chunk-QD544J2B.js";
|
|
13
13
|
import {
|
|
14
|
+
fmtTok,
|
|
14
15
|
patchConfig
|
|
15
16
|
} from "./chunk-TYO2OVAD.js";
|
|
16
17
|
import {
|
|
17
18
|
parseWebuiSessionChildOptions,
|
|
18
19
|
writeWebuiSessionChildError
|
|
19
20
|
} from "./chunk-3JPTNADJ.js";
|
|
21
|
+
import {
|
|
22
|
+
helpCmd,
|
|
23
|
+
versionCmd
|
|
24
|
+
} from "./chunk-ARWGQUB3.js";
|
|
25
|
+
import {
|
|
26
|
+
CLI_VERSION
|
|
27
|
+
} from "./chunk-XJXDOF63.js";
|
|
28
|
+
import {
|
|
29
|
+
discoverAndMergeProviders
|
|
30
|
+
} from "./chunk-52C7PD2H.js";
|
|
31
|
+
import {
|
|
32
|
+
runUpdateCommand
|
|
33
|
+
} from "./chunk-53STH6CX.js";
|
|
34
|
+
import "./chunk-U3SS4NRH.js";
|
|
35
|
+
import "./chunk-NUIHRGPY.js";
|
|
36
|
+
import {
|
|
37
|
+
DEFAULT_PORT
|
|
38
|
+
} from "./chunk-RHLQT3EV.js";
|
|
39
|
+
import "./chunk-KE7E7DPX.js";
|
|
40
|
+
import "./chunk-Q5GTM25S.js";
|
|
20
41
|
import {
|
|
21
42
|
parseArgs,
|
|
22
43
|
runPicker,
|
|
23
44
|
saveToGlobalConfig
|
|
24
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-5JW3XCRA.js";
|
|
25
46
|
import {
|
|
26
47
|
isKeylessLocalProvider,
|
|
27
48
|
visibleModelIds
|
|
@@ -36,26 +57,6 @@ import "./chunk-B4JLSXZB.js";
|
|
|
36
57
|
import {
|
|
37
58
|
activeProfileConfigPath
|
|
38
59
|
} from "./chunk-YMXXOOFN.js";
|
|
39
|
-
import {
|
|
40
|
-
runUpdateCommand
|
|
41
|
-
} from "./chunk-53STH6CX.js";
|
|
42
|
-
import "./chunk-U3SS4NRH.js";
|
|
43
|
-
import "./chunk-NUIHRGPY.js";
|
|
44
|
-
import {
|
|
45
|
-
DEFAULT_PORT
|
|
46
|
-
} from "./chunk-RHLQT3EV.js";
|
|
47
|
-
import "./chunk-KE7E7DPX.js";
|
|
48
|
-
import "./chunk-Q5GTM25S.js";
|
|
49
|
-
import {
|
|
50
|
-
helpCmd,
|
|
51
|
-
versionCmd
|
|
52
|
-
} from "./chunk-ARWGQUB3.js";
|
|
53
|
-
import {
|
|
54
|
-
CLI_VERSION
|
|
55
|
-
} from "./chunk-XJXDOF63.js";
|
|
56
|
-
import {
|
|
57
|
-
discoverAndMergeProviders
|
|
58
|
-
} from "./chunk-52C7PD2H.js";
|
|
59
60
|
import "./chunk-7OCVIDC7.js";
|
|
60
61
|
|
|
61
62
|
// src/cli-context.ts
|
|
@@ -713,12 +714,58 @@ async function launchDesktop(args) {
|
|
|
713
714
|
});
|
|
714
715
|
}
|
|
715
716
|
|
|
717
|
+
// src/subcommands/index.ts
|
|
718
|
+
var loaders = {
|
|
719
|
+
acp: async () => (await import("./acp-5ZLGFHWP.js")).acpCmd,
|
|
720
|
+
init: async () => (await import("./init-E2NDDOHI.js")).initCmd,
|
|
721
|
+
auth: async () => (await import("./auth-GVPYLJPS.js")).authCmd,
|
|
722
|
+
update: async () => (await import("./update-VZSOZGYC.js")).updateCmd,
|
|
723
|
+
sessions: async () => (await import("./sessions-config-YX2ZPAFM.js")).sessionsCmd,
|
|
724
|
+
config: async () => (await import("./sessions-config-YX2ZPAFM.js")).configCmd,
|
|
725
|
+
rewind: async () => (await import("./rewind-7RE6ONIC.js")).rewindCmd,
|
|
726
|
+
replay: async () => (await import("./replay-WBRMEWQ5.js")).replayCmd,
|
|
727
|
+
audit: async () => (await import("./audit-BBR22QH3.js")).auditCmd,
|
|
728
|
+
tools: async () => (await import("./tools-skills-UNKLOB7M.js")).toolsCmd,
|
|
729
|
+
skills: async () => (await import("./tools-skills-UNKLOB7M.js")).skillsCmd,
|
|
730
|
+
providers: async () => (await import("./providers-models-VV74TRQ2.js")).providersCmd,
|
|
731
|
+
models: async () => (await import("./providers-models-VV74TRQ2.js")).modelsCmd,
|
|
732
|
+
mcp: async () => (await import("./mcp-MSARYOPN.js")).mcpCmd,
|
|
733
|
+
plugin: async () => (await import("./plugin-usage-PTEETL3J.js")).pluginCmd,
|
|
734
|
+
plugins: async () => (await import("./plugin-usage-PTEETL3J.js")).pluginCmd,
|
|
735
|
+
diag: async () => (await import("./diag-doctor-WTBHPOMB.js")).diagCmd,
|
|
736
|
+
doctor: async () => (await import("./diag-doctor-WTBHPOMB.js")).doctorCmd,
|
|
737
|
+
export: async () => (await import("./export-CCUXRJOU.js")).exportCmd,
|
|
738
|
+
usage: async () => (await import("./plugin-usage-PTEETL3J.js")).usageCmd,
|
|
739
|
+
version: async () => (await import("./version-help-ASKQYXDJ.js")).versionCmd,
|
|
740
|
+
help: async () => (await import("./version-help-ASKQYXDJ.js")).helpCmd,
|
|
741
|
+
projects: async () => (await import("./projects-XE76NFNZ.js")).projectsCmd,
|
|
742
|
+
modeldiag: async () => (await import("./modeldiag-6VS5KDP5.js")).modeldiagCmd,
|
|
743
|
+
quick: async () => (await import("./quick-VIO2BEHS.js")).quickCmd,
|
|
744
|
+
bench: async () => (await import("./bench-XNDUJOSZ.js")).benchCmd,
|
|
745
|
+
chronicle: async () => (await import("./chronicle-63MFCZN6.js")).chronicleCmd,
|
|
746
|
+
hq: async () => (await import("./hq-ZK6PGN3W.js")).hqCmd,
|
|
747
|
+
mailbox: async () => (await import("./mailbox-serve-V3QIJBDH.js")).mailboxServeCmd,
|
|
748
|
+
permissions: async () => (await import("./permissions-2PWBL4YU.js")).permissionsCmd,
|
|
749
|
+
project: async () => (await import("./project-BLQ3ALVL.js")).projectCmd,
|
|
750
|
+
governance: async () => (await import("./governance-V2Z42RBE.js")).governanceCmd
|
|
751
|
+
};
|
|
752
|
+
var subcommands = Object.fromEntries(
|
|
753
|
+
Object.entries(loaders).map(([name, load]) => [
|
|
754
|
+
name,
|
|
755
|
+
async (args, deps) => (await load())(args, deps)
|
|
756
|
+
])
|
|
757
|
+
);
|
|
758
|
+
var subcommandNames = Object.keys(loaders);
|
|
759
|
+
|
|
716
760
|
// src/boot/short-circuit-flags.ts
|
|
717
761
|
async function handleHelpVersionShortCircuit(argv) {
|
|
718
|
-
const earlyFlags = parseArgs(argv)
|
|
762
|
+
const { flags: earlyFlags, positional } = parseArgs(argv);
|
|
719
763
|
if (earlyFlags["help"] !== true && earlyFlags["version"] !== true) {
|
|
720
764
|
return null;
|
|
721
765
|
}
|
|
766
|
+
if (earlyFlags["help"] === true && positional.length > 0 && subcommandNames.includes(positional[0])) {
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
722
769
|
const stubRenderer = {
|
|
723
770
|
write: (line) => {
|
|
724
771
|
process.stdout.write(line);
|
|
@@ -920,7 +967,7 @@ ${color3.green("Cloudflare Quick Tunnel ready:")} ${browserUrl}
|
|
|
920
967
|
}
|
|
921
968
|
|
|
922
969
|
// src/boot.ts
|
|
923
|
-
import * as
|
|
970
|
+
import * as fs8 from "node:fs/promises";
|
|
924
971
|
import { createRequire as createRequire4 } from "node:module";
|
|
925
972
|
import * as os from "node:os";
|
|
926
973
|
import * as path8 from "node:path";
|
|
@@ -931,7 +978,7 @@ import { TOKENS as TOKENS2 } from "@wrongstack/core/kernel";
|
|
|
931
978
|
import { DefaultModelsRegistry } from "@wrongstack/core/models";
|
|
932
979
|
import { ToolRegistry } from "@wrongstack/core/registry";
|
|
933
980
|
import { normalizeTokenSavingTier } from "@wrongstack/core/types";
|
|
934
|
-
import { color as
|
|
981
|
+
import { color as color10, isStdinTTY as isStdinTTY2, writeErr } from "@wrongstack/core/utils";
|
|
935
982
|
import { createDefaultContainer as createDefaultContainer2 } from "@wrongstack/runtime";
|
|
936
983
|
import { registerBuiltinToolTier } from "@wrongstack/tools/tool-tier";
|
|
937
984
|
|
|
@@ -1048,84 +1095,520 @@ async function maybeRestoreDefaultProfileFromBackup(opts) {
|
|
|
1048
1095
|
}
|
|
1049
1096
|
}
|
|
1050
1097
|
|
|
1051
|
-
// src/boot-
|
|
1052
|
-
import
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
...isWebui ? { skipIdentityValidation: true } : {}
|
|
1059
|
-
};
|
|
1060
|
-
const { cwd, projectRoot, userHome, wpaths, pathResolver, config, vault } = await coreBootConfig(opts);
|
|
1061
|
-
return {
|
|
1062
|
-
paths: { cwd, projectRoot, userHome, wpaths, pathResolver },
|
|
1063
|
-
config,
|
|
1064
|
-
vault
|
|
1065
|
-
};
|
|
1066
|
-
}
|
|
1098
|
+
// src/boot/system-prompt-menu.ts
|
|
1099
|
+
import * as fs5 from "node:fs/promises";
|
|
1100
|
+
import {
|
|
1101
|
+
buildIdentityLayer,
|
|
1102
|
+
loadInstructionBundle
|
|
1103
|
+
} from "@wrongstack/core/agent";
|
|
1104
|
+
import { atomicWrite as atomicWrite4, color as color7, estimateTextTokens } from "@wrongstack/core/utils";
|
|
1067
1105
|
|
|
1068
|
-
// src/
|
|
1106
|
+
// src/pre-launch/launch-prompts.ts
|
|
1069
1107
|
import * as fs3 from "node:fs/promises";
|
|
1070
|
-
import
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
writeOut
|
|
1077
|
-
} from "@wrongstack/core/utils";
|
|
1078
|
-
var ReadlineInputReader = class {
|
|
1079
|
-
rl;
|
|
1080
|
-
historyFile;
|
|
1081
|
-
history = [];
|
|
1082
|
-
pending = false;
|
|
1083
|
-
suppressNextHistory = false;
|
|
1084
|
-
constructor(opts = {}) {
|
|
1085
|
-
this.historyFile = opts.historyFile ?? resolveWstackPaths({ projectRoot: process.cwd() }).historyFile;
|
|
1108
|
+
import { atomicWrite as atomicWrite3, color as color5 } from "@wrongstack/core/utils";
|
|
1109
|
+
var LaunchAbortedError = class extends Error {
|
|
1110
|
+
exitCode = 0;
|
|
1111
|
+
constructor() {
|
|
1112
|
+
super("Launch cancelled by user");
|
|
1113
|
+
this.name = "LaunchAbortedError";
|
|
1086
1114
|
}
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1115
|
+
};
|
|
1116
|
+
async function runLaunchPrompts(opts) {
|
|
1117
|
+
const { renderer, reader, modePinned, yoloPinned, autonomyPinned, lastChoices } = opts;
|
|
1118
|
+
if (modePinned !== void 0 && yoloPinned !== void 0 && autonomyPinned !== void 0) {
|
|
1119
|
+
return {
|
|
1120
|
+
mode: modePinned,
|
|
1121
|
+
yolo: yoloPinned,
|
|
1122
|
+
autonomy: autonomyPinned
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
if (!lastChoices) {
|
|
1126
|
+
return {
|
|
1127
|
+
mode: modePinned ?? "tui",
|
|
1128
|
+
yolo: yoloPinned ?? true,
|
|
1129
|
+
autonomy: autonomyPinned ?? "auto"
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
const hasOverride = modePinned !== void 0 && modePinned !== lastChoices.mode || yoloPinned !== void 0 && yoloPinned !== lastChoices.yolo || autonomyPinned !== void 0 && autonomyPinned !== lastChoices.autonomy;
|
|
1133
|
+
if (lastChoices && !hasOverride) {
|
|
1134
|
+
const effective = {
|
|
1135
|
+
mode: modePinned ?? lastChoices.mode,
|
|
1136
|
+
yolo: yoloPinned ?? lastChoices.yolo,
|
|
1137
|
+
autonomy: autonomyPinned ?? lastChoices.autonomy
|
|
1138
|
+
};
|
|
1139
|
+
const onOff = (v) => v ? color5.green("on") : color5.dim("off");
|
|
1140
|
+
const modeLabel = effective.mode.toUpperCase();
|
|
1141
|
+
renderer.write(
|
|
1142
|
+
`
|
|
1143
|
+
${color5.dim("Last settings:")} ${color5.bold(modeLabel)} \xB7 YOLO ${onOff(effective.yolo)} \xB7 Autonomy ${effective.autonomy === "auto" ? color5.green("auto") : color5.dim("off")}
|
|
1144
|
+
`
|
|
1145
|
+
);
|
|
1146
|
+
const answer = (await reader.readLine(
|
|
1147
|
+
` ${color5.amber("?")} Continue with these? ${color5.dim("[Y/n/q]")} ${color5.dim("(auto Y in 5s)")} `,
|
|
1148
|
+
{ timeoutMs: 5e3, defaultAnswer: "y" }
|
|
1149
|
+
)).trim().toLowerCase();
|
|
1150
|
+
if (answer === "q") {
|
|
1151
|
+
renderer.write(color5.dim(" Goodbye!\n"));
|
|
1152
|
+
throw new LaunchAbortedError();
|
|
1153
|
+
}
|
|
1154
|
+
if (answer !== "n" && answer !== "no") {
|
|
1155
|
+
const badges2 = buildBadges(effective);
|
|
1156
|
+
const badgeStr2 = badges2.length > 0 ? ` (${badges2.join(" \xB7 ")})` : "";
|
|
1157
|
+
renderer.write(
|
|
1158
|
+
`
|
|
1159
|
+
${color5.green("\u25B6")} Launching in ${color5.bold(modeLabel)} mode${badgeStr2}
|
|
1160
|
+
|
|
1161
|
+
`
|
|
1162
|
+
);
|
|
1163
|
+
return effective;
|
|
1093
1164
|
}
|
|
1094
1165
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1166
|
+
let mode;
|
|
1167
|
+
if (modePinned) {
|
|
1168
|
+
mode = modePinned;
|
|
1169
|
+
} else {
|
|
1170
|
+
const answer = (await reader.readLine(
|
|
1171
|
+
`
|
|
1172
|
+
${color5.amber("?")} Interactive mode: ${color5.bold("T")}UI / ${color5.bold("R")}EPL ${color5.dim("[T/r/q]")} `
|
|
1173
|
+
)).trim().toLowerCase();
|
|
1174
|
+
if (answer === "q") {
|
|
1175
|
+
renderer.write(color5.dim(" Goodbye!\n"));
|
|
1176
|
+
throw new LaunchAbortedError();
|
|
1100
1177
|
}
|
|
1178
|
+
mode = answer === "r" || answer === "repl" ? "repl" : "tui";
|
|
1101
1179
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1180
|
+
let yolo;
|
|
1181
|
+
if (yoloPinned !== void 0 && yoloPinned === lastChoices?.yolo) {
|
|
1182
|
+
yolo = yoloPinned;
|
|
1183
|
+
} else {
|
|
1184
|
+
const answer = (await reader.readLine(
|
|
1185
|
+
` ${color5.amber("?")} YOLO mode ${color5.dim("(auto-approve tool calls)")} ${color5.dim("[Y/n/q]")} `
|
|
1186
|
+
)).trim().toLowerCase();
|
|
1187
|
+
if (answer === "q") {
|
|
1188
|
+
renderer.write(color5.dim(" Goodbye!\n"));
|
|
1189
|
+
throw new LaunchAbortedError();
|
|
1190
|
+
}
|
|
1191
|
+
yolo = answer !== "n" && answer !== "no";
|
|
1104
1192
|
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1193
|
+
let autonomy;
|
|
1194
|
+
if (autonomyPinned !== void 0 && autonomyPinned === lastChoices?.autonomy) {
|
|
1195
|
+
autonomy = autonomyPinned;
|
|
1196
|
+
} else {
|
|
1197
|
+
const answer = (await reader.readLine(
|
|
1198
|
+
` ${color5.amber("?")} Autonomy mode ${color5.dim("(auto-continue \u2014 agent picks next step)")} ${color5.dim("[Y/n/q]")} `
|
|
1199
|
+
)).trim().toLowerCase();
|
|
1200
|
+
if (answer === "q") {
|
|
1201
|
+
renderer.write(color5.dim(" Goodbye!\n"));
|
|
1202
|
+
throw new LaunchAbortedError();
|
|
1113
1203
|
}
|
|
1114
|
-
|
|
1204
|
+
autonomy = answer !== "n" && answer !== "no" ? "auto" : "off";
|
|
1115
1205
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1206
|
+
const badges = buildBadges({ mode, yolo, autonomy });
|
|
1207
|
+
const badgeStr = badges.length > 0 ? ` (${badges.join(" \xB7 ")})` : "";
|
|
1208
|
+
renderer.write(
|
|
1209
|
+
`
|
|
1210
|
+
${color5.green("\u25B6")} Launching in ${color5.bold(mode.toUpperCase())} mode${badgeStr}
|
|
1211
|
+
|
|
1212
|
+
`
|
|
1213
|
+
);
|
|
1214
|
+
return { mode, yolo, autonomy };
|
|
1215
|
+
}
|
|
1216
|
+
function buildBadges(chosen) {
|
|
1217
|
+
const badges = [];
|
|
1218
|
+
if (chosen.yolo) badges.push(color5.yellow("YOLO"));
|
|
1219
|
+
if (chosen.autonomy !== "off")
|
|
1220
|
+
badges.push(color5.magenta(`AUTONOMY:${chosen.autonomy.toUpperCase()}`));
|
|
1221
|
+
return badges;
|
|
1222
|
+
}
|
|
1223
|
+
async function persistLaunchChoices(configPath, choices) {
|
|
1224
|
+
let fileExists = false;
|
|
1225
|
+
try {
|
|
1226
|
+
await fs3.access(configPath);
|
|
1227
|
+
fileExists = true;
|
|
1228
|
+
} catch {
|
|
1229
|
+
}
|
|
1230
|
+
let existing = {};
|
|
1231
|
+
try {
|
|
1232
|
+
const raw = await fs3.readFile(configPath, "utf8");
|
|
1233
|
+
existing = JSON.parse(raw);
|
|
1234
|
+
} catch (err) {
|
|
1235
|
+
if (fileExists) {
|
|
1236
|
+
throw new Error(
|
|
1237
|
+
`Refusing to overwrite corrupt config at ${configPath} (${err.message}). Fix or move the file aside before retrying.`,
|
|
1238
|
+
{ cause: err }
|
|
1239
|
+
);
|
|
1120
1240
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1241
|
+
existing = {};
|
|
1242
|
+
}
|
|
1243
|
+
existing.yolo = choices.yolo;
|
|
1244
|
+
existing.launch = {
|
|
1245
|
+
mode: choices.mode,
|
|
1246
|
+
autonomy: choices.autonomy
|
|
1247
|
+
};
|
|
1248
|
+
await atomicWrite3(configPath, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
// src/pre-launch/project-check.ts
|
|
1252
|
+
import * as fs4 from "node:fs/promises";
|
|
1253
|
+
import * as path5 from "node:path";
|
|
1254
|
+
import { color as color6, toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils";
|
|
1255
|
+
var MANIFESTS = [
|
|
1256
|
+
"package.json",
|
|
1257
|
+
"pyproject.toml",
|
|
1258
|
+
"Cargo.toml",
|
|
1259
|
+
"go.mod",
|
|
1260
|
+
"Makefile",
|
|
1261
|
+
"pom.xml",
|
|
1262
|
+
"build.gradle",
|
|
1263
|
+
"build.gradle.kts",
|
|
1264
|
+
"composer.json",
|
|
1265
|
+
"Gemfile"
|
|
1266
|
+
];
|
|
1267
|
+
async function detectProjectKind(projectRoot) {
|
|
1268
|
+
try {
|
|
1269
|
+
await fs4.access(path5.join(projectRoot, ".wrongstack", "AGENTS.md"));
|
|
1270
|
+
return "initialized";
|
|
1271
|
+
} catch {
|
|
1272
|
+
}
|
|
1273
|
+
for (const m of MANIFESTS) {
|
|
1274
|
+
try {
|
|
1275
|
+
await fs4.access(path5.join(projectRoot, m));
|
|
1276
|
+
return "project";
|
|
1277
|
+
} catch {
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
return "empty";
|
|
1281
|
+
}
|
|
1282
|
+
async function scaffoldAgentsMd(projectRoot) {
|
|
1283
|
+
const dir = path5.join(projectRoot, ".wrongstack");
|
|
1284
|
+
const file = path5.join(dir, "AGENTS.md");
|
|
1285
|
+
const facts = await detectProjectFacts(projectRoot);
|
|
1286
|
+
const body = renderAgentsTemplate(facts);
|
|
1287
|
+
await fs4.mkdir(dir, { recursive: true });
|
|
1288
|
+
await fs4.writeFile(file, body, "utf8");
|
|
1289
|
+
return file;
|
|
1290
|
+
}
|
|
1291
|
+
async function runProjectCheck(opts) {
|
|
1292
|
+
const { projectRoot, cwd, renderer, reader } = opts;
|
|
1293
|
+
const kind = await detectProjectKind(projectRoot);
|
|
1294
|
+
if (kind === "initialized") {
|
|
1295
|
+
renderer.write(
|
|
1296
|
+
`
|
|
1297
|
+
${color6.green("\u2713")} Project initialized ${color6.dim(`(${path5.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
|
|
1298
|
+
`
|
|
1299
|
+
);
|
|
1300
|
+
return true;
|
|
1301
|
+
}
|
|
1302
|
+
if (kind === "project") {
|
|
1303
|
+
renderer.write(
|
|
1304
|
+
`
|
|
1305
|
+
${color6.amber("\u25CF")} Project detected ${color6.dim(`(${projectRoot})`)} but ${color6.bold(".wrongstack/AGENTS.md")} is missing.
|
|
1306
|
+
`
|
|
1307
|
+
);
|
|
1308
|
+
const answer2 = (await reader.readLine(
|
|
1309
|
+
` ${color6.amber("?")} Scaffold ${color6.bold("AGENTS.md")} now? ${color6.dim("[y/N/q]")} `
|
|
1310
|
+
)).trim().toLowerCase();
|
|
1311
|
+
if (answer2 === "q") {
|
|
1312
|
+
renderer.write(color6.dim(" Cancelled.\n"));
|
|
1313
|
+
return false;
|
|
1314
|
+
}
|
|
1315
|
+
if (answer2 === "y" || answer2 === "yes") {
|
|
1316
|
+
try {
|
|
1317
|
+
const file = await scaffoldAgentsMd(projectRoot);
|
|
1318
|
+
renderer.write(` ${color6.green("\u2713")} Wrote ${color6.dim(file)}
|
|
1319
|
+
`);
|
|
1320
|
+
} catch (err) {
|
|
1321
|
+
renderer.writeError(`Failed to scaffold AGENTS.md: ${toErrorMessage2(err)}`);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
return true;
|
|
1325
|
+
}
|
|
1326
|
+
const gitDir = path5.join(projectRoot, ".git");
|
|
1327
|
+
let hasGit = false;
|
|
1328
|
+
try {
|
|
1329
|
+
await fs4.access(gitDir);
|
|
1330
|
+
hasGit = true;
|
|
1331
|
+
} catch {
|
|
1332
|
+
}
|
|
1333
|
+
if (!hasGit) {
|
|
1334
|
+
renderer.write(
|
|
1335
|
+
`
|
|
1336
|
+
${color6.dim("\u25CB")} ${color6.dim(`No project manifest in ${projectRoot} \u2014 running in a scratch directory.`)}
|
|
1337
|
+
`
|
|
1338
|
+
);
|
|
1339
|
+
const answer2 = (await reader.readLine(
|
|
1340
|
+
` ${color6.amber("?")} No git repo found. ${color6.bold("Initialize git?")} ${color6.dim("[y/N/q]")} `
|
|
1341
|
+
)).trim().toLowerCase();
|
|
1342
|
+
if (answer2 === "q") {
|
|
1343
|
+
renderer.write(color6.dim(" Cancelled.\n"));
|
|
1344
|
+
return false;
|
|
1345
|
+
}
|
|
1346
|
+
if (answer2 === "y" || answer2 === "yes") {
|
|
1347
|
+
try {
|
|
1348
|
+
const { spawn: spawn2 } = await import("node:child_process");
|
|
1349
|
+
await new Promise((resolve2, reject) => {
|
|
1350
|
+
const child = spawn2("git", ["init"], {
|
|
1351
|
+
cwd,
|
|
1352
|
+
signal: AbortSignal.timeout(1e4),
|
|
1353
|
+
windowsHide: true
|
|
1354
|
+
});
|
|
1355
|
+
child.on("error", reject);
|
|
1356
|
+
child.on(
|
|
1357
|
+
"close",
|
|
1358
|
+
(code) => code === 0 ? resolve2() : reject(new Error(`git init failed with ${code}`))
|
|
1359
|
+
);
|
|
1360
|
+
});
|
|
1361
|
+
renderer.write(` ${color6.green("\u2713")} Git repository initialized
|
|
1362
|
+
`);
|
|
1363
|
+
} catch (err) {
|
|
1364
|
+
renderer.writeError(`git init failed: ${toErrorMessage2(err)}
|
|
1365
|
+
`);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
} else {
|
|
1369
|
+
renderer.write(
|
|
1370
|
+
`
|
|
1371
|
+
${color6.dim("\u25CB")} ${color6.dim(`No project manifest in ${projectRoot} \u2014 running in a scratch directory.`)}
|
|
1372
|
+
`
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
const answer = (await reader.readLine(` ${color6.amber("?")} Continue anyway? ${color6.dim("[Y/n/q]")} `)).trim().toLowerCase();
|
|
1376
|
+
if (answer === "q" || answer === "n" || answer === "no") {
|
|
1377
|
+
renderer.write(color6.dim(" Cancelled.\n"));
|
|
1378
|
+
return false;
|
|
1379
|
+
}
|
|
1380
|
+
return true;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// src/boot/system-prompt-menu.ts
|
|
1384
|
+
var SYSTEM_PROMPT_OPTIONS = [
|
|
1385
|
+
{ variant: "lite", label: "Lite", hint: "leanest \u2014 best for small context windows" },
|
|
1386
|
+
{ variant: "default", label: "Standard", hint: "balanced \u2014 the default" },
|
|
1387
|
+
{ variant: "pro", label: "Pro", hint: "most detailed guidance \u2014 uses more tokens" }
|
|
1388
|
+
];
|
|
1389
|
+
var SYSTEM_PROMPT_VARIANTS = SYSTEM_PROMPT_OPTIONS.map((o) => o.variant);
|
|
1390
|
+
var SUMMARY_TIMEOUT_MS2 = 5e3;
|
|
1391
|
+
function flagOn(value) {
|
|
1392
|
+
if (value === true) return true;
|
|
1393
|
+
if (typeof value !== "string") return false;
|
|
1394
|
+
const normalized = value.trim().toLowerCase();
|
|
1395
|
+
return normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "on";
|
|
1396
|
+
}
|
|
1397
|
+
function isSystemPromptPinned(flags) {
|
|
1398
|
+
return flagOn(flags["system-pro"]) || flagOn(flags["system-lite"]) || typeof flags["system-prompt"] === "string";
|
|
1399
|
+
}
|
|
1400
|
+
function shouldSkipSystemPromptMenu(flags) {
|
|
1401
|
+
if (isSystemPromptPinned(flags)) return true;
|
|
1402
|
+
if (flags["no-menu"] === true) return true;
|
|
1403
|
+
if (flags["no-interactive"] === true) return true;
|
|
1404
|
+
if (flags["skip"] === true) return true;
|
|
1405
|
+
if (flags["webui"] === true) return true;
|
|
1406
|
+
return false;
|
|
1407
|
+
}
|
|
1408
|
+
async function countSystemPromptTokens(paths) {
|
|
1409
|
+
const counts = {};
|
|
1410
|
+
for (const variant of SYSTEM_PROMPT_VARIANTS) {
|
|
1411
|
+
const bundle = await loadInstructionBundle({
|
|
1412
|
+
globalDir: paths.globalDir,
|
|
1413
|
+
projectDir: paths.projectDir,
|
|
1414
|
+
systemVariant: variant
|
|
1415
|
+
});
|
|
1416
|
+
const identity = buildIdentityLayer(
|
|
1417
|
+
bundle.system?.identity,
|
|
1418
|
+
bundle.system?.identitySource,
|
|
1419
|
+
void 0
|
|
1420
|
+
);
|
|
1421
|
+
counts[variant] = estimateTextTokens(identity);
|
|
1422
|
+
}
|
|
1423
|
+
return counts;
|
|
1424
|
+
}
|
|
1425
|
+
function labelFor(variant) {
|
|
1426
|
+
return SYSTEM_PROMPT_OPTIONS.find((o) => o.variant === variant)?.label ?? variant;
|
|
1427
|
+
}
|
|
1428
|
+
var VALID_VARIANTS = new Set(SYSTEM_PROMPT_VARIANTS);
|
|
1429
|
+
async function readSavedSystemPromptVariant(configPath) {
|
|
1430
|
+
let raw;
|
|
1431
|
+
try {
|
|
1432
|
+
raw = await fs5.readFile(configPath, "utf8");
|
|
1433
|
+
} catch {
|
|
1434
|
+
return void 0;
|
|
1435
|
+
}
|
|
1436
|
+
try {
|
|
1437
|
+
const parsed = JSON.parse(raw);
|
|
1438
|
+
const variant = parsed.systemPrompt?.variant;
|
|
1439
|
+
return typeof variant === "string" && VALID_VARIANTS.has(variant) ? variant : void 0;
|
|
1440
|
+
} catch {
|
|
1441
|
+
return void 0;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
async function runSystemPromptMenu(deps) {
|
|
1445
|
+
const { renderer, reader, paths, lastVariant } = deps;
|
|
1446
|
+
const tokens = await countSystemPromptTokens(paths);
|
|
1447
|
+
const defaultVariant = lastVariant ?? "default";
|
|
1448
|
+
if (lastVariant) {
|
|
1449
|
+
const answer2 = (await reader.readLine(
|
|
1450
|
+
` ${color7.amber("?")} Continue with ${color7.bold(labelFor(lastVariant))} system prompt ${color7.dim(`(~${fmtTok(tokens[lastVariant])} tokens)`)}? ${color7.dim("[Y/n/q]")} ${color7.dim("(auto Y in 5s)")} `,
|
|
1451
|
+
{ timeoutMs: SUMMARY_TIMEOUT_MS2, defaultAnswer: "y" }
|
|
1452
|
+
)).trim().toLowerCase();
|
|
1453
|
+
if (answer2 === "q" || answer2 === "quit") throw new LaunchAbortedError();
|
|
1454
|
+
if (answer2 !== "n" && answer2 !== "no") return lastVariant;
|
|
1455
|
+
}
|
|
1456
|
+
renderer.write(`
|
|
1457
|
+
${color7.amber("?")} System prompt:
|
|
1458
|
+
`);
|
|
1459
|
+
for (const [index, opt] of SYSTEM_PROMPT_OPTIONS.entries()) {
|
|
1460
|
+
const num2 = index + 1;
|
|
1461
|
+
const isDefault = opt.variant === defaultVariant;
|
|
1462
|
+
const marker = isDefault ? color7.cyan("\u25CF") : color7.dim("\u25CB");
|
|
1463
|
+
const label = isDefault ? color7.bold(opt.label) : opt.label;
|
|
1464
|
+
renderer.write(
|
|
1465
|
+
` ${color7.dim(`${num2}.`)} ${marker} ${label.padEnd(9)} ${color7.dim(`~${fmtTok(tokens[opt.variant])} tokens`)} ${color7.dim(opt.hint)}
|
|
1466
|
+
`
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
const answer = (await reader.readLine(
|
|
1470
|
+
`
|
|
1471
|
+
${color7.amber("?")} Select system prompt ${color7.dim(`[1-${SYSTEM_PROMPT_OPTIONS.length}, Enter = ${labelFor(defaultVariant)}, q to quit]`)}: `
|
|
1472
|
+
)).trim().toLowerCase();
|
|
1473
|
+
if (answer === "q" || answer === "quit") throw new LaunchAbortedError();
|
|
1474
|
+
if (!answer) return defaultVariant;
|
|
1475
|
+
const num = Number.parseInt(answer, 10);
|
|
1476
|
+
if (!Number.isNaN(num) && num >= 1 && num <= SYSTEM_PROMPT_OPTIONS.length) {
|
|
1477
|
+
return SYSTEM_PROMPT_OPTIONS[num - 1].variant;
|
|
1478
|
+
}
|
|
1479
|
+
const byName = SYSTEM_PROMPT_OPTIONS.find(
|
|
1480
|
+
(o) => o.label.toLowerCase() === answer || o.variant === answer
|
|
1481
|
+
);
|
|
1482
|
+
if (byName) return byName.variant;
|
|
1483
|
+
return defaultVariant;
|
|
1484
|
+
}
|
|
1485
|
+
async function persistSystemPromptVariant(configPath, variant) {
|
|
1486
|
+
let fileExists = false;
|
|
1487
|
+
try {
|
|
1488
|
+
await fs5.access(configPath);
|
|
1489
|
+
fileExists = true;
|
|
1490
|
+
} catch {
|
|
1491
|
+
}
|
|
1492
|
+
let existing = {};
|
|
1493
|
+
try {
|
|
1494
|
+
const raw = await fs5.readFile(configPath, "utf8");
|
|
1495
|
+
existing = JSON.parse(raw);
|
|
1496
|
+
} catch (err) {
|
|
1497
|
+
if (fileExists) {
|
|
1498
|
+
throw new Error(
|
|
1499
|
+
`Refusing to overwrite corrupt config at ${configPath} (${err.message}). Fix or move the file aside before retrying.`,
|
|
1500
|
+
{ cause: err }
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
existing = {};
|
|
1504
|
+
}
|
|
1505
|
+
const systemPrompt = existing.systemPrompt ?? {};
|
|
1506
|
+
existing.systemPrompt = { ...systemPrompt, variant };
|
|
1507
|
+
await atomicWrite4(configPath, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
1508
|
+
}
|
|
1509
|
+
async function maybeRunSystemPromptMenu(opts) {
|
|
1510
|
+
if (!opts.isInteractiveTTY) return { aborted: false, changed: false };
|
|
1511
|
+
if (shouldSkipSystemPromptMenu(opts.flags)) return { aborted: false, changed: false };
|
|
1512
|
+
let savedVariant;
|
|
1513
|
+
try {
|
|
1514
|
+
savedVariant = await readSavedSystemPromptVariant(opts.profileConfigPath);
|
|
1515
|
+
const chosen = await runSystemPromptMenu({
|
|
1516
|
+
renderer: opts.renderer,
|
|
1517
|
+
reader: opts.reader,
|
|
1518
|
+
lastVariant: savedVariant,
|
|
1519
|
+
paths: opts.paths
|
|
1520
|
+
});
|
|
1521
|
+
if (chosen === savedVariant) return { variant: chosen, aborted: false, changed: false };
|
|
1522
|
+
try {
|
|
1523
|
+
await persistSystemPromptVariant(opts.profileConfigPath, chosen);
|
|
1524
|
+
} catch (err) {
|
|
1525
|
+
return { variant: chosen, aborted: false, changed: true, persistError: err };
|
|
1526
|
+
}
|
|
1527
|
+
return { variant: chosen, aborted: false, changed: true };
|
|
1528
|
+
} catch (err) {
|
|
1529
|
+
if (err instanceof LaunchAbortedError) return { aborted: true, changed: false };
|
|
1530
|
+
throw err;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
// src/boot-config.ts
|
|
1535
|
+
import { bootConfig as coreBootConfig } from "@wrongstack/core/infrastructure";
|
|
1536
|
+
async function bootConfig(flags) {
|
|
1537
|
+
const isWebui = !!flags["webui"];
|
|
1538
|
+
const opts = {
|
|
1539
|
+
flags,
|
|
1540
|
+
appLabel: "wstack",
|
|
1541
|
+
...isWebui ? { skipIdentityValidation: true } : {}
|
|
1542
|
+
};
|
|
1543
|
+
const { cwd, projectRoot, userHome, wpaths, pathResolver, config, vault } = await coreBootConfig(opts);
|
|
1544
|
+
return {
|
|
1545
|
+
paths: { cwd, projectRoot, userHome, wpaths, pathResolver },
|
|
1546
|
+
config,
|
|
1547
|
+
vault
|
|
1548
|
+
};
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
// src/input-reader.ts
|
|
1552
|
+
import * as fs6 from "node:fs/promises";
|
|
1553
|
+
import * as path6 from "node:path";
|
|
1554
|
+
import * as readline from "node:readline";
|
|
1555
|
+
import {
|
|
1556
|
+
resolveWstackPaths,
|
|
1557
|
+
setOutputLineGuard,
|
|
1558
|
+
setRawMode as setRawMode2,
|
|
1559
|
+
writeOut
|
|
1560
|
+
} from "@wrongstack/core/utils";
|
|
1561
|
+
var ReadlineInputReader = class {
|
|
1562
|
+
rl;
|
|
1563
|
+
historyFile;
|
|
1564
|
+
history = [];
|
|
1565
|
+
pending = false;
|
|
1566
|
+
suppressNextHistory = false;
|
|
1567
|
+
constructor(opts = {}) {
|
|
1568
|
+
this.historyFile = opts.historyFile ?? resolveWstackPaths({ projectRoot: process.cwd() }).historyFile;
|
|
1569
|
+
}
|
|
1570
|
+
async loadHistory() {
|
|
1571
|
+
try {
|
|
1572
|
+
const raw = await fs6.readFile(this.historyFile, "utf8");
|
|
1573
|
+
this.history = raw.split("\n").filter(Boolean).slice(-1e3);
|
|
1574
|
+
} catch {
|
|
1575
|
+
this.history = [];
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
async saveHistory() {
|
|
1579
|
+
try {
|
|
1580
|
+
await fs6.mkdir(path6.dirname(this.historyFile), { recursive: true });
|
|
1581
|
+
await fs6.writeFile(this.historyFile, this.history.slice(-1e3).join("\n"));
|
|
1582
|
+
} catch {
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
shouldPersistHistory(line) {
|
|
1586
|
+
return !/^\s*\/(?:telegram-setup|tg-setup)\s+\d+:[A-Za-z0-9_-]+(?:\s|$)/i.test(line);
|
|
1587
|
+
}
|
|
1588
|
+
ensure() {
|
|
1589
|
+
if (!this.rl) {
|
|
1590
|
+
this.rl = readline.createInterface({
|
|
1591
|
+
input: process.stdin,
|
|
1592
|
+
output: process.stdout,
|
|
1593
|
+
history: this.history,
|
|
1594
|
+
terminal: process.stdin.isTTY
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
return this.rl;
|
|
1598
|
+
}
|
|
1599
|
+
async readLine(prompt, timeoutOpts) {
|
|
1600
|
+
if (this.history.length === 0) await this.loadHistory();
|
|
1601
|
+
while (this.pending) {
|
|
1602
|
+
await new Promise((resolve2) => setTimeout(resolve2, 50));
|
|
1603
|
+
}
|
|
1604
|
+
this.pending = true;
|
|
1605
|
+
try {
|
|
1606
|
+
if (this.rl) {
|
|
1607
|
+
const old = this.rl;
|
|
1608
|
+
this.rl = void 0;
|
|
1609
|
+
await new Promise((resolve2) => {
|
|
1610
|
+
if (old.closed) {
|
|
1611
|
+
resolve2();
|
|
1129
1612
|
} else {
|
|
1130
1613
|
old.once("close", resolve2);
|
|
1131
1614
|
old.close();
|
|
@@ -1343,9 +1826,9 @@ function dim(s) {
|
|
|
1343
1826
|
}
|
|
1344
1827
|
|
|
1345
1828
|
// src/launch-hints.ts
|
|
1346
|
-
import * as
|
|
1347
|
-
import * as
|
|
1348
|
-
import { color as
|
|
1829
|
+
import * as fs7 from "node:fs/promises";
|
|
1830
|
+
import * as path7 from "node:path";
|
|
1831
|
+
import { color as color8 } from "@wrongstack/core/utils";
|
|
1349
1832
|
var GROUPS = [
|
|
1350
1833
|
{
|
|
1351
1834
|
title: "Autonomy",
|
|
@@ -1468,404 +1951,127 @@ var HINT_CONTINUATION_INDENT = " ";
|
|
|
1468
1951
|
var wrap = (n) => (n % GROUPS.length + GROUPS.length) % GROUPS.length;
|
|
1469
1952
|
function displayWidth(value) {
|
|
1470
1953
|
return [...value].length;
|
|
1471
|
-
}
|
|
1472
|
-
function wrapWords(value, firstWidth, continuationWidth = firstWidth) {
|
|
1473
|
-
const words = value.trim().split(/\s+/).filter(Boolean);
|
|
1474
|
-
const lines = [];
|
|
1475
|
-
let line = "";
|
|
1476
|
-
let width = Math.max(1, firstWidth);
|
|
1477
|
-
for (const sourceWord of words) {
|
|
1478
|
-
let word = sourceWord;
|
|
1479
|
-
while (word) {
|
|
1480
|
-
if (line) {
|
|
1481
|
-
if (displayWidth(line) + 1 + displayWidth(word) <= width) {
|
|
1482
|
-
line += ` ${word}`;
|
|
1483
|
-
word = "";
|
|
1484
|
-
} else {
|
|
1485
|
-
lines.push(line);
|
|
1486
|
-
line = "";
|
|
1487
|
-
width = Math.max(1, continuationWidth);
|
|
1488
|
-
}
|
|
1489
|
-
continue;
|
|
1490
|
-
}
|
|
1491
|
-
if (displayWidth(word) <= width) {
|
|
1492
|
-
line = word;
|
|
1493
|
-
word = "";
|
|
1494
|
-
} else {
|
|
1495
|
-
const characters = [...word];
|
|
1496
|
-
lines.push(characters.slice(0, width).join(""));
|
|
1497
|
-
word = characters.slice(width).join("");
|
|
1498
|
-
width = Math.max(1, continuationWidth);
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
if (line) lines.push(line);
|
|
1503
|
-
return lines;
|
|
1504
|
-
}
|
|
1505
|
-
function renderHintLines(item, termWidth) {
|
|
1506
|
-
const inline = `${HINT_INDENT}${item.key} \u2014 ${item.blurb}`;
|
|
1507
|
-
if (displayWidth(inline) <= termWidth) {
|
|
1508
|
-
return [`${HINT_INDENT}${color5.bold(item.key)} ${color5.dim("\u2014")} ${color5.dim(item.blurb)}`];
|
|
1509
|
-
}
|
|
1510
|
-
const lines = [];
|
|
1511
|
-
const keyLines = wrapWords(
|
|
1512
|
-
item.key,
|
|
1513
|
-
termWidth - displayWidth(HINT_INDENT),
|
|
1514
|
-
termWidth - displayWidth(HINT_CONTINUATION_INDENT)
|
|
1515
|
-
);
|
|
1516
|
-
keyLines.forEach((line, index) => {
|
|
1517
|
-
const indent = index === 0 ? HINT_INDENT : HINT_CONTINUATION_INDENT;
|
|
1518
|
-
lines.push(`${indent}${color5.bold(line)}`);
|
|
1519
|
-
});
|
|
1520
|
-
const blurbLines = wrapWords(
|
|
1521
|
-
`\u2014 ${item.blurb}`,
|
|
1522
|
-
termWidth - displayWidth(HINT_CONTINUATION_INDENT)
|
|
1523
|
-
);
|
|
1524
|
-
for (const line of blurbLines) lines.push(`${HINT_CONTINUATION_INDENT}${color5.dim(line)}`);
|
|
1525
|
-
return lines;
|
|
1526
|
-
}
|
|
1527
|
-
function renderHeaderLines(group, groupIndex, termWidth) {
|
|
1528
|
-
const progress = `${groupIndex + 1}/${GROUPS.length}`;
|
|
1529
|
-
const plain = ` ${group.title} ${progress}`;
|
|
1530
|
-
if (displayWidth(plain) <= termWidth) {
|
|
1531
|
-
return [
|
|
1532
|
-
` ${color5.cyan("\u25C7")} ${color5.bold(group.title)} ${color5.dim(progress)}`,
|
|
1533
|
-
` ${color5.dim("\u2500".repeat(Math.min(termWidth - 2, 48)))}`
|
|
1534
|
-
];
|
|
1535
|
-
}
|
|
1536
|
-
return [
|
|
1537
|
-
` ${color5.cyan("\u25C7")} ${color5.bold(group.title)}`,
|
|
1538
|
-
` ${color5.dim("\u2500".repeat(Math.min(termWidth - 2, 48)))}`
|
|
1539
|
-
];
|
|
1540
|
-
}
|
|
1541
|
-
function renderFooterLines(termWidth) {
|
|
1542
|
-
const footer = "/help lists everything \xB7 hide with --no-hints";
|
|
1543
|
-
if (displayWidth(` ${footer}`) <= termWidth) {
|
|
1544
|
-
return [
|
|
1545
|
-
` ${color5.dim("\u2500".repeat(Math.min(termWidth - 2, 48)))}`,
|
|
1546
|
-
` ${color5.dim(`${color5.bold("/help")} lists everything \xB7 hide with ${color5.bold("--no-hints")}`)}`
|
|
1547
|
-
];
|
|
1548
|
-
}
|
|
1549
|
-
return [...wrapWords(footer, termWidth - 2).map((line) => ` ${color5.dim(line)}`)];
|
|
1550
|
-
}
|
|
1551
|
-
async function pickGroupIndex(opts) {
|
|
1552
|
-
if (typeof opts.groupIndex === "number") return wrap(opts.groupIndex);
|
|
1553
|
-
if (opts.cursorFile) {
|
|
1554
|
-
try {
|
|
1555
|
-
let current = 0;
|
|
1556
|
-
try {
|
|
1557
|
-
const raw = await fs4.readFile(opts.cursorFile, "utf8");
|
|
1558
|
-
const parsed = Number.parseInt(raw.trim(), 10);
|
|
1559
|
-
if (Number.isFinite(parsed)) current = wrap(parsed);
|
|
1560
|
-
} catch {
|
|
1561
|
-
}
|
|
1562
|
-
await fs4.mkdir(path6.dirname(opts.cursorFile), { recursive: true });
|
|
1563
|
-
await fs4.writeFile(opts.cursorFile, String(wrap(current + 1)));
|
|
1564
|
-
return current;
|
|
1565
|
-
} catch {
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
return Math.floor(Math.random() * GROUPS.length);
|
|
1569
|
-
}
|
|
1570
|
-
async function printLaunchHints(renderer, flags, opts = {}) {
|
|
1571
|
-
if (shouldSuppress(flags)) return;
|
|
1572
|
-
const idx = await pickGroupIndex(opts);
|
|
1573
|
-
const group = GROUPS[idx];
|
|
1574
|
-
if (!group) return;
|
|
1575
|
-
const termWidth = Math.max(
|
|
1576
|
-
MIN_TERM_WIDTH,
|
|
1577
|
-
Math.floor(opts.termWidth ?? process.stdout.columns ?? DEFAULT_TERM_WIDTH)
|
|
1578
|
-
);
|
|
1579
|
-
const lines = [];
|
|
1580
|
-
lines.push("");
|
|
1581
|
-
lines.push(...renderHeaderLines(group, idx, termWidth));
|
|
1582
|
-
for (const item of group.items) lines.push(...renderHintLines(item, termWidth));
|
|
1583
|
-
lines.push("");
|
|
1584
|
-
lines.push(...renderFooterLines(termWidth));
|
|
1585
|
-
lines.push("");
|
|
1586
|
-
renderer.write(`${lines.join("\n")}
|
|
1587
|
-
`);
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
|
-
// src/pre-launch/launch-prompts.ts
|
|
1591
|
-
import * as fs5 from "node:fs/promises";
|
|
1592
|
-
import { atomicWrite as atomicWrite3, color as color6 } from "@wrongstack/core/utils";
|
|
1593
|
-
var LaunchAbortedError = class extends Error {
|
|
1594
|
-
exitCode = 0;
|
|
1595
|
-
constructor() {
|
|
1596
|
-
super("Launch cancelled by user");
|
|
1597
|
-
this.name = "LaunchAbortedError";
|
|
1598
|
-
}
|
|
1599
|
-
};
|
|
1600
|
-
async function runLaunchPrompts(opts) {
|
|
1601
|
-
const { renderer, reader, modePinned, yoloPinned, autonomyPinned, lastChoices } = opts;
|
|
1602
|
-
if (modePinned !== void 0 && yoloPinned !== void 0 && autonomyPinned !== void 0) {
|
|
1603
|
-
return {
|
|
1604
|
-
mode: modePinned,
|
|
1605
|
-
yolo: yoloPinned,
|
|
1606
|
-
autonomy: autonomyPinned
|
|
1607
|
-
};
|
|
1608
|
-
}
|
|
1609
|
-
if (!lastChoices) {
|
|
1610
|
-
return {
|
|
1611
|
-
mode: modePinned ?? "tui",
|
|
1612
|
-
yolo: yoloPinned ?? true,
|
|
1613
|
-
autonomy: autonomyPinned ?? "auto"
|
|
1614
|
-
};
|
|
1615
|
-
}
|
|
1616
|
-
const hasOverride = modePinned !== void 0 && modePinned !== lastChoices.mode || yoloPinned !== void 0 && yoloPinned !== lastChoices.yolo || autonomyPinned !== void 0 && autonomyPinned !== lastChoices.autonomy;
|
|
1617
|
-
if (lastChoices && !hasOverride) {
|
|
1618
|
-
const effective = {
|
|
1619
|
-
mode: modePinned ?? lastChoices.mode,
|
|
1620
|
-
yolo: yoloPinned ?? lastChoices.yolo,
|
|
1621
|
-
autonomy: autonomyPinned ?? lastChoices.autonomy
|
|
1622
|
-
};
|
|
1623
|
-
const onOff = (v) => v ? color6.green("on") : color6.dim("off");
|
|
1624
|
-
const modeLabel = effective.mode.toUpperCase();
|
|
1625
|
-
renderer.write(
|
|
1626
|
-
`
|
|
1627
|
-
${color6.dim("Last settings:")} ${color6.bold(modeLabel)} \xB7 YOLO ${onOff(effective.yolo)} \xB7 Autonomy ${effective.autonomy === "auto" ? color6.green("auto") : color6.dim("off")}
|
|
1628
|
-
`
|
|
1629
|
-
);
|
|
1630
|
-
const answer = (await reader.readLine(
|
|
1631
|
-
` ${color6.amber("?")} Continue with these? ${color6.dim("[Y/n/q]")} ${color6.dim("(auto Y in 5s)")} `,
|
|
1632
|
-
{ timeoutMs: 5e3, defaultAnswer: "y" }
|
|
1633
|
-
)).trim().toLowerCase();
|
|
1634
|
-
if (answer === "q") {
|
|
1635
|
-
renderer.write(color6.dim(" Goodbye!\n"));
|
|
1636
|
-
throw new LaunchAbortedError();
|
|
1637
|
-
}
|
|
1638
|
-
if (answer !== "n" && answer !== "no") {
|
|
1639
|
-
const badges2 = buildBadges(effective);
|
|
1640
|
-
const badgeStr2 = badges2.length > 0 ? ` (${badges2.join(" \xB7 ")})` : "";
|
|
1641
|
-
renderer.write(
|
|
1642
|
-
`
|
|
1643
|
-
${color6.green("\u25B6")} Launching in ${color6.bold(modeLabel)} mode${badgeStr2}
|
|
1644
|
-
|
|
1645
|
-
`
|
|
1646
|
-
);
|
|
1647
|
-
return effective;
|
|
1648
|
-
}
|
|
1649
|
-
}
|
|
1650
|
-
let mode;
|
|
1651
|
-
if (modePinned) {
|
|
1652
|
-
mode = modePinned;
|
|
1653
|
-
} else {
|
|
1654
|
-
const answer = (await reader.readLine(
|
|
1655
|
-
`
|
|
1656
|
-
${color6.amber("?")} Interactive mode: ${color6.bold("T")}UI / ${color6.bold("R")}EPL ${color6.dim("[T/r/q]")} `
|
|
1657
|
-
)).trim().toLowerCase();
|
|
1658
|
-
if (answer === "q") {
|
|
1659
|
-
renderer.write(color6.dim(" Goodbye!\n"));
|
|
1660
|
-
throw new LaunchAbortedError();
|
|
1661
|
-
}
|
|
1662
|
-
mode = answer === "r" || answer === "repl" ? "repl" : "tui";
|
|
1663
|
-
}
|
|
1664
|
-
let yolo;
|
|
1665
|
-
if (yoloPinned !== void 0 && yoloPinned === lastChoices?.yolo) {
|
|
1666
|
-
yolo = yoloPinned;
|
|
1667
|
-
} else {
|
|
1668
|
-
const answer = (await reader.readLine(
|
|
1669
|
-
` ${color6.amber("?")} YOLO mode ${color6.dim("(auto-approve tool calls)")} ${color6.dim("[Y/n/q]")} `
|
|
1670
|
-
)).trim().toLowerCase();
|
|
1671
|
-
if (answer === "q") {
|
|
1672
|
-
renderer.write(color6.dim(" Goodbye!\n"));
|
|
1673
|
-
throw new LaunchAbortedError();
|
|
1674
|
-
}
|
|
1675
|
-
yolo = answer !== "n" && answer !== "no";
|
|
1676
|
-
}
|
|
1677
|
-
let autonomy;
|
|
1678
|
-
if (autonomyPinned !== void 0 && autonomyPinned === lastChoices?.autonomy) {
|
|
1679
|
-
autonomy = autonomyPinned;
|
|
1680
|
-
} else {
|
|
1681
|
-
const answer = (await reader.readLine(
|
|
1682
|
-
` ${color6.amber("?")} Autonomy mode ${color6.dim("(auto-continue \u2014 agent picks next step)")} ${color6.dim("[Y/n/q]")} `
|
|
1683
|
-
)).trim().toLowerCase();
|
|
1684
|
-
if (answer === "q") {
|
|
1685
|
-
renderer.write(color6.dim(" Goodbye!\n"));
|
|
1686
|
-
throw new LaunchAbortedError();
|
|
1687
|
-
}
|
|
1688
|
-
autonomy = answer !== "n" && answer !== "no" ? "auto" : "off";
|
|
1689
|
-
}
|
|
1690
|
-
const badges = buildBadges({ mode, yolo, autonomy });
|
|
1691
|
-
const badgeStr = badges.length > 0 ? ` (${badges.join(" \xB7 ")})` : "";
|
|
1692
|
-
renderer.write(
|
|
1693
|
-
`
|
|
1694
|
-
${color6.green("\u25B6")} Launching in ${color6.bold(mode.toUpperCase())} mode${badgeStr}
|
|
1695
|
-
|
|
1696
|
-
`
|
|
1697
|
-
);
|
|
1698
|
-
return { mode, yolo, autonomy };
|
|
1699
|
-
}
|
|
1700
|
-
function buildBadges(chosen) {
|
|
1701
|
-
const badges = [];
|
|
1702
|
-
if (chosen.yolo) badges.push(color6.yellow("YOLO"));
|
|
1703
|
-
if (chosen.autonomy !== "off")
|
|
1704
|
-
badges.push(color6.magenta(`AUTONOMY:${chosen.autonomy.toUpperCase()}`));
|
|
1705
|
-
return badges;
|
|
1706
|
-
}
|
|
1707
|
-
async function persistLaunchChoices(configPath, choices) {
|
|
1708
|
-
let fileExists = false;
|
|
1709
|
-
try {
|
|
1710
|
-
await fs5.access(configPath);
|
|
1711
|
-
fileExists = true;
|
|
1712
|
-
} catch {
|
|
1713
|
-
}
|
|
1714
|
-
let existing = {};
|
|
1715
|
-
try {
|
|
1716
|
-
const raw = await fs5.readFile(configPath, "utf8");
|
|
1717
|
-
existing = JSON.parse(raw);
|
|
1718
|
-
} catch (err) {
|
|
1719
|
-
if (fileExists) {
|
|
1720
|
-
throw new Error(
|
|
1721
|
-
`Refusing to overwrite corrupt config at ${configPath} (${err.message}). Fix or move the file aside before retrying.`,
|
|
1722
|
-
{ cause: err }
|
|
1723
|
-
);
|
|
1954
|
+
}
|
|
1955
|
+
function wrapWords(value, firstWidth, continuationWidth = firstWidth) {
|
|
1956
|
+
const words = value.trim().split(/\s+/).filter(Boolean);
|
|
1957
|
+
const lines = [];
|
|
1958
|
+
let line = "";
|
|
1959
|
+
let width = Math.max(1, firstWidth);
|
|
1960
|
+
for (const sourceWord of words) {
|
|
1961
|
+
let word = sourceWord;
|
|
1962
|
+
while (word) {
|
|
1963
|
+
if (line) {
|
|
1964
|
+
if (displayWidth(line) + 1 + displayWidth(word) <= width) {
|
|
1965
|
+
line += ` ${word}`;
|
|
1966
|
+
word = "";
|
|
1967
|
+
} else {
|
|
1968
|
+
lines.push(line);
|
|
1969
|
+
line = "";
|
|
1970
|
+
width = Math.max(1, continuationWidth);
|
|
1971
|
+
}
|
|
1972
|
+
continue;
|
|
1973
|
+
}
|
|
1974
|
+
if (displayWidth(word) <= width) {
|
|
1975
|
+
line = word;
|
|
1976
|
+
word = "";
|
|
1977
|
+
} else {
|
|
1978
|
+
const characters = [...word];
|
|
1979
|
+
lines.push(characters.slice(0, width).join(""));
|
|
1980
|
+
word = characters.slice(width).join("");
|
|
1981
|
+
width = Math.max(1, continuationWidth);
|
|
1982
|
+
}
|
|
1724
1983
|
}
|
|
1725
|
-
existing = {};
|
|
1726
1984
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
mode: choices.mode,
|
|
1730
|
-
autonomy: choices.autonomy
|
|
1731
|
-
};
|
|
1732
|
-
await atomicWrite3(configPath, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
1985
|
+
if (line) lines.push(line);
|
|
1986
|
+
return lines;
|
|
1733
1987
|
}
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
import { color as color7, toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils";
|
|
1739
|
-
var MANIFESTS = [
|
|
1740
|
-
"package.json",
|
|
1741
|
-
"pyproject.toml",
|
|
1742
|
-
"Cargo.toml",
|
|
1743
|
-
"go.mod",
|
|
1744
|
-
"Makefile",
|
|
1745
|
-
"pom.xml",
|
|
1746
|
-
"build.gradle",
|
|
1747
|
-
"build.gradle.kts",
|
|
1748
|
-
"composer.json",
|
|
1749
|
-
"Gemfile"
|
|
1750
|
-
];
|
|
1751
|
-
async function detectProjectKind(projectRoot) {
|
|
1752
|
-
try {
|
|
1753
|
-
await fs6.access(path7.join(projectRoot, ".wrongstack", "AGENTS.md"));
|
|
1754
|
-
return "initialized";
|
|
1755
|
-
} catch {
|
|
1756
|
-
}
|
|
1757
|
-
for (const m of MANIFESTS) {
|
|
1758
|
-
try {
|
|
1759
|
-
await fs6.access(path7.join(projectRoot, m));
|
|
1760
|
-
return "project";
|
|
1761
|
-
} catch {
|
|
1762
|
-
}
|
|
1988
|
+
function renderHintLines(item, termWidth) {
|
|
1989
|
+
const inline = `${HINT_INDENT}${item.key} \u2014 ${item.blurb}`;
|
|
1990
|
+
if (displayWidth(inline) <= termWidth) {
|
|
1991
|
+
return [`${HINT_INDENT}${color8.bold(item.key)} ${color8.dim("\u2014")} ${color8.dim(item.blurb)}`];
|
|
1763
1992
|
}
|
|
1764
|
-
|
|
1993
|
+
const lines = [];
|
|
1994
|
+
const keyLines = wrapWords(
|
|
1995
|
+
item.key,
|
|
1996
|
+
termWidth - displayWidth(HINT_INDENT),
|
|
1997
|
+
termWidth - displayWidth(HINT_CONTINUATION_INDENT)
|
|
1998
|
+
);
|
|
1999
|
+
keyLines.forEach((line, index) => {
|
|
2000
|
+
const indent = index === 0 ? HINT_INDENT : HINT_CONTINUATION_INDENT;
|
|
2001
|
+
lines.push(`${indent}${color8.bold(line)}`);
|
|
2002
|
+
});
|
|
2003
|
+
const blurbLines = wrapWords(
|
|
2004
|
+
`\u2014 ${item.blurb}`,
|
|
2005
|
+
termWidth - displayWidth(HINT_CONTINUATION_INDENT)
|
|
2006
|
+
);
|
|
2007
|
+
for (const line of blurbLines) lines.push(`${HINT_CONTINUATION_INDENT}${color8.dim(line)}`);
|
|
2008
|
+
return lines;
|
|
1765
2009
|
}
|
|
1766
|
-
|
|
1767
|
-
const
|
|
1768
|
-
const
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
2010
|
+
function renderHeaderLines(group, groupIndex, termWidth) {
|
|
2011
|
+
const progress = `${groupIndex + 1}/${GROUPS.length}`;
|
|
2012
|
+
const plain = ` ${group.title} ${progress}`;
|
|
2013
|
+
if (displayWidth(plain) <= termWidth) {
|
|
2014
|
+
return [
|
|
2015
|
+
` ${color8.cyan("\u25C7")} ${color8.bold(group.title)} ${color8.dim(progress)}`,
|
|
2016
|
+
` ${color8.dim("\u2500".repeat(Math.min(termWidth - 2, 48)))}`
|
|
2017
|
+
];
|
|
2018
|
+
}
|
|
2019
|
+
return [
|
|
2020
|
+
` ${color8.cyan("\u25C7")} ${color8.bold(group.title)}`,
|
|
2021
|
+
` ${color8.dim("\u2500".repeat(Math.min(termWidth - 2, 48)))}`
|
|
2022
|
+
];
|
|
1774
2023
|
}
|
|
1775
|
-
|
|
1776
|
-
const
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
`
|
|
1781
|
-
|
|
1782
|
-
`
|
|
1783
|
-
);
|
|
1784
|
-
return true;
|
|
2024
|
+
function renderFooterLines(termWidth) {
|
|
2025
|
+
const footer = "/help lists everything \xB7 hide with --no-hints";
|
|
2026
|
+
if (displayWidth(` ${footer}`) <= termWidth) {
|
|
2027
|
+
return [
|
|
2028
|
+
` ${color8.dim("\u2500".repeat(Math.min(termWidth - 2, 48)))}`,
|
|
2029
|
+
` ${color8.dim(`${color8.bold("/help")} lists everything \xB7 hide with ${color8.bold("--no-hints")}`)}`
|
|
2030
|
+
];
|
|
1785
2031
|
}
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
` ${color7.amber("?")} Scaffold ${color7.bold("AGENTS.md")} now? ${color7.dim("[y/N/q]")} `
|
|
1794
|
-
)).trim().toLowerCase();
|
|
1795
|
-
if (answer2 === "q") {
|
|
1796
|
-
renderer.write(color7.dim(" Cancelled.\n"));
|
|
1797
|
-
return false;
|
|
1798
|
-
}
|
|
1799
|
-
if (answer2 === "y" || answer2 === "yes") {
|
|
2032
|
+
return [...wrapWords(footer, termWidth - 2).map((line) => ` ${color8.dim(line)}`)];
|
|
2033
|
+
}
|
|
2034
|
+
async function pickGroupIndex(opts) {
|
|
2035
|
+
if (typeof opts.groupIndex === "number") return wrap(opts.groupIndex);
|
|
2036
|
+
if (opts.cursorFile) {
|
|
2037
|
+
try {
|
|
2038
|
+
let current = 0;
|
|
1800
2039
|
try {
|
|
1801
|
-
const
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
} catch
|
|
1805
|
-
renderer.writeError(`Failed to scaffold AGENTS.md: ${toErrorMessage2(err)}`);
|
|
2040
|
+
const raw = await fs7.readFile(opts.cursorFile, "utf8");
|
|
2041
|
+
const parsed = Number.parseInt(raw.trim(), 10);
|
|
2042
|
+
if (Number.isFinite(parsed)) current = wrap(parsed);
|
|
2043
|
+
} catch {
|
|
1806
2044
|
}
|
|
2045
|
+
await fs7.mkdir(path7.dirname(opts.cursorFile), { recursive: true });
|
|
2046
|
+
await fs7.writeFile(opts.cursorFile, String(wrap(current + 1)));
|
|
2047
|
+
return current;
|
|
2048
|
+
} catch {
|
|
1807
2049
|
}
|
|
1808
|
-
return true;
|
|
1809
2050
|
}
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
}
|
|
1830
|
-
if (answer2 === "y" || answer2 === "yes") {
|
|
1831
|
-
try {
|
|
1832
|
-
const { spawn: spawn2 } = await import("node:child_process");
|
|
1833
|
-
await new Promise((resolve2, reject) => {
|
|
1834
|
-
const child = spawn2("git", ["init"], {
|
|
1835
|
-
cwd,
|
|
1836
|
-
signal: AbortSignal.timeout(1e4),
|
|
1837
|
-
windowsHide: true
|
|
1838
|
-
});
|
|
1839
|
-
child.on("error", reject);
|
|
1840
|
-
child.on(
|
|
1841
|
-
"close",
|
|
1842
|
-
(code) => code === 0 ? resolve2() : reject(new Error(`git init failed with ${code}`))
|
|
1843
|
-
);
|
|
1844
|
-
});
|
|
1845
|
-
renderer.write(` ${color7.green("\u2713")} Git repository initialized
|
|
1846
|
-
`);
|
|
1847
|
-
} catch (err) {
|
|
1848
|
-
renderer.writeError(`git init failed: ${toErrorMessage2(err)}
|
|
2051
|
+
return Math.floor(Math.random() * GROUPS.length);
|
|
2052
|
+
}
|
|
2053
|
+
async function printLaunchHints(renderer, flags, opts = {}) {
|
|
2054
|
+
if (shouldSuppress(flags)) return;
|
|
2055
|
+
const idx = await pickGroupIndex(opts);
|
|
2056
|
+
const group = GROUPS[idx];
|
|
2057
|
+
if (!group) return;
|
|
2058
|
+
const termWidth = Math.max(
|
|
2059
|
+
MIN_TERM_WIDTH,
|
|
2060
|
+
Math.floor(opts.termWidth ?? process.stdout.columns ?? DEFAULT_TERM_WIDTH)
|
|
2061
|
+
);
|
|
2062
|
+
const lines = [];
|
|
2063
|
+
lines.push("");
|
|
2064
|
+
lines.push(...renderHeaderLines(group, idx, termWidth));
|
|
2065
|
+
for (const item of group.items) lines.push(...renderHintLines(item, termWidth));
|
|
2066
|
+
lines.push("");
|
|
2067
|
+
lines.push(...renderFooterLines(termWidth));
|
|
2068
|
+
lines.push("");
|
|
2069
|
+
renderer.write(`${lines.join("\n")}
|
|
1849
2070
|
`);
|
|
1850
|
-
}
|
|
1851
|
-
}
|
|
1852
|
-
} else {
|
|
1853
|
-
renderer.write(
|
|
1854
|
-
`
|
|
1855
|
-
${color7.dim("\u25CB")} ${color7.dim(`No project manifest in ${projectRoot} \u2014 running in a scratch directory.`)}
|
|
1856
|
-
`
|
|
1857
|
-
);
|
|
1858
|
-
}
|
|
1859
|
-
const answer = (await reader.readLine(` ${color7.amber("?")} Continue anyway? ${color7.dim("[Y/n/q]")} `)).trim().toLowerCase();
|
|
1860
|
-
if (answer === "q" || answer === "n" || answer === "no") {
|
|
1861
|
-
renderer.write(color7.dim(" Cancelled.\n"));
|
|
1862
|
-
return false;
|
|
1863
|
-
}
|
|
1864
|
-
return true;
|
|
1865
2071
|
}
|
|
1866
2072
|
|
|
1867
2073
|
// src/renderer.ts
|
|
1868
|
-
import { color as
|
|
2074
|
+
import { color as color9 } from "@wrongstack/core/utils";
|
|
1869
2075
|
var TerminalRenderer = class _TerminalRenderer {
|
|
1870
2076
|
out;
|
|
1871
2077
|
err;
|
|
@@ -1978,7 +2184,7 @@ var TerminalRenderer = class _TerminalRenderer {
|
|
|
1978
2184
|
if (!this.lineStart) this.out.write("\n");
|
|
1979
2185
|
const arrow = theme.primary("\u2192");
|
|
1980
2186
|
const display = formatInputSummary(input);
|
|
1981
|
-
this.out.write(`${arrow} ${theme.bold(name)}${display ?
|
|
2187
|
+
this.out.write(`${arrow} ${theme.bold(name)}${display ? color9.dim(` ${display}`) : ""}
|
|
1982
2188
|
`);
|
|
1983
2189
|
this.lineStart = true;
|
|
1984
2190
|
}
|
|
@@ -1995,7 +2201,7 @@ var TerminalRenderer = class _TerminalRenderer {
|
|
|
1995
2201
|
if (isError) {
|
|
1996
2202
|
const firstLine = txt.split("\n")[0] ?? "";
|
|
1997
2203
|
const truncated = firstLine.length > 200 ? `${firstLine.slice(0, 197)}\u2026` : firstLine;
|
|
1998
|
-
this.out.write(` ${prefix} ${
|
|
2204
|
+
this.out.write(` ${prefix} ${color9.dim(truncated)}
|
|
1999
2205
|
`);
|
|
2000
2206
|
this.lineStart = true;
|
|
2001
2207
|
return;
|
|
@@ -2003,7 +2209,7 @@ var TerminalRenderer = class _TerminalRenderer {
|
|
|
2003
2209
|
const isEditLike = name === "edit" || name === "write";
|
|
2004
2210
|
const diff = extractDiff(content);
|
|
2005
2211
|
if (isEditLike && diff) {
|
|
2006
|
-
this.out.write(` ${prefix} ${
|
|
2212
|
+
this.out.write(` ${prefix} ${color9.dim(summarize(content, name))}
|
|
2007
2213
|
`);
|
|
2008
2214
|
const rendered = renderDiff(diff).split("\n").map((l) => ` ${l}`).join("\n");
|
|
2009
2215
|
this.out.write(`${rendered}
|
|
@@ -2012,9 +2218,9 @@ var TerminalRenderer = class _TerminalRenderer {
|
|
|
2012
2218
|
return;
|
|
2013
2219
|
}
|
|
2014
2220
|
const summary = summarize(content, name);
|
|
2015
|
-
const isReadLike = name === "read" || name === "grep" || name === "glob" || name === "bash";
|
|
2221
|
+
const isReadLike = name === "read" || name === "grep" || name === "glob" || name === "bash" || name === "exec";
|
|
2016
2222
|
if (renderMode === "simple") {
|
|
2017
|
-
this.out.write(` ${prefix} ${
|
|
2223
|
+
this.out.write(` ${prefix} ${color9.dim(summary || compactSingleLine(bodyText))}
|
|
2018
2224
|
`);
|
|
2019
2225
|
this.lineStart = true;
|
|
2020
2226
|
return;
|
|
@@ -2023,16 +2229,16 @@ var TerminalRenderer = class _TerminalRenderer {
|
|
|
2023
2229
|
const lines = bodyText.split("\n");
|
|
2024
2230
|
const head = lines.slice(0, previewLines).map((l) => l.replace(/\s+$/, ""));
|
|
2025
2231
|
const moreCount = Math.max(0, lines.length - head.length);
|
|
2026
|
-
this.out.write(` ${prefix} ${
|
|
2232
|
+
this.out.write(` ${prefix} ${color9.dim(summary)}
|
|
2027
2233
|
`);
|
|
2028
2234
|
for (const l of head) {
|
|
2029
2235
|
const capped = l.length > 200 ? `${l.slice(0, 197)}\u2026` : l;
|
|
2030
|
-
this.out.write(` ${
|
|
2236
|
+
this.out.write(` ${color9.dim(capped)}
|
|
2031
2237
|
`);
|
|
2032
2238
|
}
|
|
2033
2239
|
if (moreCount > 0) {
|
|
2034
2240
|
this.out.write(
|
|
2035
|
-
` ${
|
|
2241
|
+
` ${color9.dim(`+${moreCount} more line${moreCount === 1 ? "" : "s"}`)}
|
|
2036
2242
|
`
|
|
2037
2243
|
);
|
|
2038
2244
|
}
|
|
@@ -2152,7 +2358,7 @@ function renderMarkdown(s) {
|
|
|
2152
2358
|
return theme.primary(theme.bold(`${hashes} ${text}`));
|
|
2153
2359
|
});
|
|
2154
2360
|
out = out.replace(/```([a-zA-Z0-9_+-]*)\n([\s\S]*?)```/g, (_m, _lang, code) => {
|
|
2155
|
-
return
|
|
2361
|
+
return color9.gray(`
|
|
2156
2362
|
\u250C\u2500
|
|
2157
2363
|
${code.replace(/^/gm, "\u2502 ")}\u2514\u2500`);
|
|
2158
2364
|
});
|
|
@@ -2160,7 +2366,7 @@ ${code.replace(/^/gm, "\u2502 ")}\u2514\u2500`);
|
|
|
2160
2366
|
out = out.replace(/\*\*([^*]+)\*\*/g, (_m, text) => theme.bold(text));
|
|
2161
2367
|
out = out.replace(
|
|
2162
2368
|
/(^|[^*])\*([^*\n]+)\*([^*]|$)/g,
|
|
2163
|
-
(_m, l, t, r) => `${l}${
|
|
2369
|
+
(_m, l, t, r) => `${l}${color9.italic(t)}${r}`
|
|
2164
2370
|
);
|
|
2165
2371
|
return out;
|
|
2166
2372
|
}
|
|
@@ -2204,6 +2410,13 @@ function extractBodyText(value, name) {
|
|
|
2204
2410
|
const o = v;
|
|
2205
2411
|
if (name === "read" && typeof o["text"] === "string") return o["text"];
|
|
2206
2412
|
if (name === "bash") {
|
|
2413
|
+
const output = typeof o["output"] === "string" ? o["output"] : "";
|
|
2414
|
+
const error = typeof o["error"] === "string" ? o["error"] : "";
|
|
2415
|
+
if (output && error) return `${output}
|
|
2416
|
+
${error}`;
|
|
2417
|
+
return output || error || "";
|
|
2418
|
+
}
|
|
2419
|
+
if (name === "exec") {
|
|
2207
2420
|
const stdout = typeof o["stdout"] === "string" ? o["stdout"] : "";
|
|
2208
2421
|
const stderr = typeof o["stderr"] === "string" ? o["stderr"] : "";
|
|
2209
2422
|
if (stdout && stderr) return `${stdout}
|
|
@@ -2261,6 +2474,9 @@ function summarize(value, name) {
|
|
|
2261
2474
|
if (name === "bash") {
|
|
2262
2475
|
return summarizeBash(o);
|
|
2263
2476
|
}
|
|
2477
|
+
if (name === "exec") {
|
|
2478
|
+
return summarizeExec(o);
|
|
2479
|
+
}
|
|
2264
2480
|
if (typeof o["count"] === "number") {
|
|
2265
2481
|
return `${o["count"]} match${o["count"] === 1 ? "" : "es"}`;
|
|
2266
2482
|
}
|
|
@@ -2285,13 +2501,29 @@ function summarizeRead(o) {
|
|
|
2285
2501
|
return summary;
|
|
2286
2502
|
}
|
|
2287
2503
|
function summarizeBash(o) {
|
|
2288
|
-
const exit = typeof o["
|
|
2504
|
+
const exit = typeof o["exit_code"] === "number" ? o["exit_code"] : void 0;
|
|
2505
|
+
const output = typeof o["output"] === "string" ? o["output"] : "";
|
|
2506
|
+
const lines = output ? output.split("\n").length : 0;
|
|
2507
|
+
const parts = [];
|
|
2508
|
+
if (exit !== void 0) parts.push(`exit=${exit}`);
|
|
2509
|
+
else if (o["exit_code"] === null) parts.push("backgrounded");
|
|
2510
|
+
if (o["timed_out"] === true) parts.push("timed out");
|
|
2511
|
+
if (lines > 0) parts.push(`${lines} output line${lines === 1 ? "" : "s"}`);
|
|
2512
|
+
if (output.length > 0) parts.push(`${output.length}B`);
|
|
2513
|
+
return parts.join(" ");
|
|
2514
|
+
}
|
|
2515
|
+
function summarizeExec(o) {
|
|
2516
|
+
const command = typeof o["command"] === "string" ? o["command"] : "";
|
|
2517
|
+
const args = Array.isArray(o["args"]) ? o["args"].filter((a) => typeof a === "string") : [];
|
|
2518
|
+
const exit = typeof o["exitCode"] === "number" ? o["exitCode"] : void 0;
|
|
2289
2519
|
const stdout = typeof o["stdout"] === "string" ? o["stdout"] : "";
|
|
2290
2520
|
const stderr = typeof o["stderr"] === "string" ? o["stderr"] : "";
|
|
2291
2521
|
const outLines = stdout ? stdout.split("\n").length : 0;
|
|
2292
2522
|
const errLines = stderr ? stderr.split("\n").length : 0;
|
|
2293
2523
|
const totalBytes = stdout.length + stderr.length;
|
|
2294
2524
|
const parts = [];
|
|
2525
|
+
const invocation = [command, ...args].filter(Boolean).join(" ");
|
|
2526
|
+
if (invocation) parts.push(`$ ${invocation}`);
|
|
2295
2527
|
if (exit !== void 0) parts.push(`exit=${exit}`);
|
|
2296
2528
|
if (outLines > 0) parts.push(`${outLines} stdout line${outLines === 1 ? "" : "s"}`);
|
|
2297
2529
|
if (errLines > 0) parts.push(`${errLines} stderr line${errLines === 1 ? "" : "s"}`);
|
|
@@ -2304,49 +2536,6 @@ function compactSingleLine(text) {
|
|
|
2304
2536
|
return first.length > 200 ? `${first.slice(0, 197)}\u2026` : first;
|
|
2305
2537
|
}
|
|
2306
2538
|
|
|
2307
|
-
// src/subcommands/index.ts
|
|
2308
|
-
var loaders = {
|
|
2309
|
-
acp: async () => (await import("./acp-5ZLGFHWP.js")).acpCmd,
|
|
2310
|
-
init: async () => (await import("./init-E2NDDOHI.js")).initCmd,
|
|
2311
|
-
auth: async () => (await import("./auth-K7CYVVKH.js")).authCmd,
|
|
2312
|
-
update: async () => (await import("./update-VZSOZGYC.js")).updateCmd,
|
|
2313
|
-
sessions: async () => (await import("./sessions-config-YX2ZPAFM.js")).sessionsCmd,
|
|
2314
|
-
config: async () => (await import("./sessions-config-YX2ZPAFM.js")).configCmd,
|
|
2315
|
-
rewind: async () => (await import("./rewind-7RE6ONIC.js")).rewindCmd,
|
|
2316
|
-
replay: async () => (await import("./replay-WBRMEWQ5.js")).replayCmd,
|
|
2317
|
-
audit: async () => (await import("./audit-BBR22QH3.js")).auditCmd,
|
|
2318
|
-
tools: async () => (await import("./tools-skills-UNKLOB7M.js")).toolsCmd,
|
|
2319
|
-
skills: async () => (await import("./tools-skills-UNKLOB7M.js")).skillsCmd,
|
|
2320
|
-
providers: async () => (await import("./providers-models-VV74TRQ2.js")).providersCmd,
|
|
2321
|
-
models: async () => (await import("./providers-models-VV74TRQ2.js")).modelsCmd,
|
|
2322
|
-
mcp: async () => (await import("./mcp-MSARYOPN.js")).mcpCmd,
|
|
2323
|
-
plugin: async () => (await import("./plugin-usage-PTEETL3J.js")).pluginCmd,
|
|
2324
|
-
plugins: async () => (await import("./plugin-usage-PTEETL3J.js")).pluginCmd,
|
|
2325
|
-
diag: async () => (await import("./diag-doctor-ZF5BF2MK.js")).diagCmd,
|
|
2326
|
-
doctor: async () => (await import("./diag-doctor-ZF5BF2MK.js")).doctorCmd,
|
|
2327
|
-
export: async () => (await import("./export-CCUXRJOU.js")).exportCmd,
|
|
2328
|
-
usage: async () => (await import("./plugin-usage-PTEETL3J.js")).usageCmd,
|
|
2329
|
-
version: async () => (await import("./version-help-ASKQYXDJ.js")).versionCmd,
|
|
2330
|
-
help: async () => (await import("./version-help-ASKQYXDJ.js")).helpCmd,
|
|
2331
|
-
projects: async () => (await import("./projects-XE76NFNZ.js")).projectsCmd,
|
|
2332
|
-
modeldiag: async () => (await import("./modeldiag-SIVUOX2Y.js")).modeldiagCmd,
|
|
2333
|
-
quick: async () => (await import("./quick-VIO2BEHS.js")).quickCmd,
|
|
2334
|
-
bench: async () => (await import("./bench-XNDUJOSZ.js")).benchCmd,
|
|
2335
|
-
chronicle: async () => (await import("./chronicle-63MFCZN6.js")).chronicleCmd,
|
|
2336
|
-
hq: async () => (await import("./hq-5G6D5YIU.js")).hqCmd,
|
|
2337
|
-
mailbox: async () => (await import("./mailbox-serve-V3QIJBDH.js")).mailboxServeCmd,
|
|
2338
|
-
permissions: async () => (await import("./permissions-2PWBL4YU.js")).permissionsCmd,
|
|
2339
|
-
project: async () => (await import("./project-BLQ3ALVL.js")).projectCmd,
|
|
2340
|
-
governance: async () => (await import("./governance-V2Z42RBE.js")).governanceCmd
|
|
2341
|
-
};
|
|
2342
|
-
var subcommands = Object.fromEntries(
|
|
2343
|
-
Object.entries(loaders).map(([name, load]) => [
|
|
2344
|
-
name,
|
|
2345
|
-
async (args, deps) => (await load())(args, deps)
|
|
2346
|
-
])
|
|
2347
|
-
);
|
|
2348
|
-
var subcommandNames = Object.keys(loaders);
|
|
2349
|
-
|
|
2350
2539
|
// src/boot.ts
|
|
2351
2540
|
var GITHUB_PROVIDERS_OVERLAY_URL = "https://raw.githubusercontent.com/WrongStack/WrongStack/main/packages/cli/data/providers.json";
|
|
2352
2541
|
function resolveBundledOverlayFile() {
|
|
@@ -2555,8 +2744,8 @@ async function boot(argv) {
|
|
|
2555
2744
|
if (isHomeDirectory(cwd, userHome)) {
|
|
2556
2745
|
writeErr(
|
|
2557
2746
|
`
|
|
2558
|
-
${
|
|
2559
|
-
${
|
|
2747
|
+
${color10.red(color10.bold("\u26A0 This is not a working directory."))}
|
|
2748
|
+
${color10.red("Please open wstack in a project folder.")}
|
|
2560
2749
|
|
|
2561
2750
|
`
|
|
2562
2751
|
);
|
|
@@ -2617,7 +2806,7 @@ async function boot(argv) {
|
|
|
2617
2806
|
const savedStatus = await validateSavedProviderModel(config, modelsRegistry);
|
|
2618
2807
|
renderer.write(
|
|
2619
2808
|
`
|
|
2620
|
-
${
|
|
2809
|
+
${color10.dim("Last settings:")} ${color10.bold(savedProvider)} / ${color10.bold(savedModel)}
|
|
2621
2810
|
`
|
|
2622
2811
|
);
|
|
2623
2812
|
if (!savedStatus.ok) {
|
|
@@ -2627,11 +2816,11 @@ async function boot(argv) {
|
|
|
2627
2816
|
);
|
|
2628
2817
|
} else {
|
|
2629
2818
|
const answer = (await reader.readLine(
|
|
2630
|
-
` ${
|
|
2819
|
+
` ${color10.amber("?")} Continue with these? ${color10.dim("[Y/n/q]")} ${color10.dim("(auto Y in 5s)")} `,
|
|
2631
2820
|
{ timeoutMs: 5e3, defaultAnswer: "y" }
|
|
2632
2821
|
)).trim().toLowerCase();
|
|
2633
2822
|
if (answer === "q") {
|
|
2634
|
-
renderer.write(
|
|
2823
|
+
renderer.write(color10.dim(" Goodbye!\n"));
|
|
2635
2824
|
await reader.close();
|
|
2636
2825
|
return 0;
|
|
2637
2826
|
}
|
|
@@ -2639,7 +2828,7 @@ async function boot(argv) {
|
|
|
2639
2828
|
skipPicker = true;
|
|
2640
2829
|
renderer.write(
|
|
2641
2830
|
`
|
|
2642
|
-
${
|
|
2831
|
+
${color10.green("\u25B6")} ${color10.bold(savedProvider)} / ${color10.bold(savedModel)}
|
|
2643
2832
|
|
|
2644
2833
|
`
|
|
2645
2834
|
);
|
|
@@ -2702,6 +2891,30 @@ async function boot(argv) {
|
|
|
2702
2891
|
flags["no-tui"] = true;
|
|
2703
2892
|
}
|
|
2704
2893
|
if (isInteractiveTTY) {
|
|
2894
|
+
const promptMenu = await maybeRunSystemPromptMenu({
|
|
2895
|
+
isInteractiveTTY,
|
|
2896
|
+
flags,
|
|
2897
|
+
renderer,
|
|
2898
|
+
reader,
|
|
2899
|
+
profileConfigPath,
|
|
2900
|
+
paths: {
|
|
2901
|
+
globalDir: wpaths.globalInstructions,
|
|
2902
|
+
projectDir: wpaths.inProjectInstructions
|
|
2903
|
+
}
|
|
2904
|
+
});
|
|
2905
|
+
if (promptMenu.aborted) {
|
|
2906
|
+
await reader.close();
|
|
2907
|
+
return 0;
|
|
2908
|
+
}
|
|
2909
|
+
if (promptMenu.changed && promptMenu.variant) {
|
|
2910
|
+
config = patchConfig(config, { systemPrompt: { variant: promptMenu.variant } });
|
|
2911
|
+
}
|
|
2912
|
+
if (promptMenu.persistError) {
|
|
2913
|
+
renderer.writeWarning(
|
|
2914
|
+
`Could not save system prompt variant to config: ${toErrorMessage3(promptMenu.persistError)}
|
|
2915
|
+
`
|
|
2916
|
+
);
|
|
2917
|
+
}
|
|
2705
2918
|
let modePinned;
|
|
2706
2919
|
if (flags["no-tui"]) modePinned = "repl";
|
|
2707
2920
|
else if (flags["tui"]) modePinned = "tui";
|
|
@@ -2750,8 +2963,8 @@ async function boot(argv) {
|
|
|
2750
2963
|
if (shouldPrintYoloNotice(lastChoices, yoloPinned, choices.yolo)) {
|
|
2751
2964
|
writeErr(
|
|
2752
2965
|
`
|
|
2753
|
-
${
|
|
2754
|
-
${
|
|
2966
|
+
${color10.yellow("YOLO is on")}: non-denied tool calls, including shell and file writes, run without confirmation.
|
|
2967
|
+
${color10.dim("Explicit deny rules still apply. Use")} --no-yolo ${color10.dim("or")} /yolo off ${color10.dim("to require prompts.")}
|
|
2755
2968
|
|
|
2756
2969
|
`
|
|
2757
2970
|
);
|
|
@@ -2810,17 +3023,17 @@ async function checkGitInCwd(opts) {
|
|
|
2810
3023
|
const cwdGit = path8.join(cwd, ".git");
|
|
2811
3024
|
let hasCwdGit = false;
|
|
2812
3025
|
try {
|
|
2813
|
-
await
|
|
3026
|
+
await fs8.access(cwdGit);
|
|
2814
3027
|
hasCwdGit = true;
|
|
2815
3028
|
} catch {
|
|
2816
3029
|
}
|
|
2817
3030
|
if (!hasCwdGit) {
|
|
2818
3031
|
renderer.write(
|
|
2819
3032
|
`
|
|
2820
|
-
${
|
|
3033
|
+
${color10.amber("\u25CB")} This folder has no ${color10.bold(".git")} repository.
|
|
2821
3034
|
`
|
|
2822
3035
|
);
|
|
2823
|
-
const answer = (await reader.readLine(` ${
|
|
3036
|
+
const answer = (await reader.readLine(` ${color10.amber("?")} Initialize one here? ${color10.dim("[y/N]")} `)).trim().toLowerCase();
|
|
2824
3037
|
if (answer === "y" || answer === "yes") {
|
|
2825
3038
|
try {
|
|
2826
3039
|
const { spawn: spawn2 } = await import("node:child_process");
|
|
@@ -2836,7 +3049,7 @@ async function checkGitInCwd(opts) {
|
|
|
2836
3049
|
(code) => code === 0 ? resolve2() : reject(new Error(`git init failed with ${code}`))
|
|
2837
3050
|
);
|
|
2838
3051
|
});
|
|
2839
|
-
renderer.write(` ${
|
|
3052
|
+
renderer.write(` ${color10.green("\u2713")} Git repository initialized
|
|
2840
3053
|
`);
|
|
2841
3054
|
hasCwdGit = true;
|
|
2842
3055
|
} catch (err) {
|
|
@@ -2848,9 +3061,9 @@ async function checkGitInCwd(opts) {
|
|
|
2848
3061
|
const parentDir = path8.dirname(cwd);
|
|
2849
3062
|
if (parentDir !== cwd) {
|
|
2850
3063
|
try {
|
|
2851
|
-
await
|
|
3064
|
+
await fs8.access(path8.join(parentDir, ".git"));
|
|
2852
3065
|
renderer.write(
|
|
2853
|
-
` ${
|
|
3066
|
+
` ${color10.dim("\u2139")} A ${color10.bold(".git")} repo exists in the parent directory: ${color10.dim(parentDir)}
|
|
2854
3067
|
`
|
|
2855
3068
|
);
|
|
2856
3069
|
} catch {
|
|
@@ -3089,7 +3302,7 @@ async function initializeCli(argv) {
|
|
|
3089
3302
|
async function main(argv) {
|
|
3090
3303
|
const cliCtx = await initializeCli(argv);
|
|
3091
3304
|
if (typeof cliCtx === "number") return cliCtx;
|
|
3092
|
-
const { runInteractive } = await import("./cli-main-
|
|
3305
|
+
const { runInteractive } = await import("./cli-main-DHNFGP3Z.js");
|
|
3093
3306
|
return runInteractive(cliCtx);
|
|
3094
3307
|
}
|
|
3095
3308
|
|