scream-code 0.18.7 → 0.18.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/dist/{app-IU7G9gs0.mjs → app-Db7f8Kja.mjs} +249 -193
- package/dist/{dispatch-7Mas_Tvb.mjs → dispatch-BPfUTccZ.mjs} +378 -20
- package/dist/{dispatch-BqBnrIMj.mjs → dispatch-zsFzFFoY.mjs} +1 -1
- package/dist/main.mjs +1 -1
- package/dist/{src-CQwXASmd.mjs → src-ClB_Hzp9.mjs} +280 -107
- package/dist/{src-ChTWl9Cn.mjs → src-DA34cXSv.mjs} +1 -1
- package/dist/{text-input-dialog-D7dm6kak.mjs → text-input-dialog-CFRUSCi6.mjs} +1 -1
- package/dist/{text-input-dialog-gDr3i7xz.mjs → text-input-dialog-DHDsHCqR.mjs} +58 -4
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -31,7 +31,8 @@ npm install -g scream-code
|
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
> [!NOTE]
|
|
34
|
-
> Requires **Node.js >= 22**. Git recommended. Same command installs and updates.
|
|
34
|
+
> Requires **Node.js >= 22**. Git recommended. Same command installs and updates. No native build steps and no dependency install scripts: the local embedding engine runs on WebAssembly.
|
|
35
|
+
> The first use of the knowledge/memory vector features downloads a ~53MB model into `~/.scream-code/cache` (progress is shown, and the download verifies its checksum before use).
|
|
35
36
|
|
|
36
37
|
### Quick Start
|
|
37
38
|
|
|
@@ -42,7 +43,7 @@ scream --auto # auto permission mode
|
|
|
42
43
|
scream -y # auto-approve mode
|
|
43
44
|
```
|
|
44
45
|
|
|
45
|
-
On first launch Scream walks you through model setup (`/config`). 130+ providers built in — or point it at any OpenAI-compatible endpoint with `/config diy`. Switch models anytime with `/model`, no restart needed — or use `/model diy` to give each sub-agent its own model, letting every model do what it does best. Generate or edit images the same way: run `/config image` once, then just ask for a picture (the `/image` skill).
|
|
46
|
+
On first launch Scream walks you through model setup (`/config` — pick the shortcut provider directory, a custom DIY setup, or image generation). 130+ providers built in — or point it at any OpenAI-compatible endpoint with `/config diy`. Switch models anytime with `/model`, no restart needed — or use `/model diy` to give each sub-agent its own model, letting every model do what it does best. Generate or edit images the same way: run `/config image` once, then just ask for a picture (the `/image` skill).
|
|
46
47
|
|
|
47
48
|
When Scream wants to modify files or run commands, an approval panel pops up — pick a number, press Enter.
|
|
48
49
|
|
|
@@ -103,7 +104,7 @@ Type `/` in the input to browse. All 55 commands:
|
|
|
103
104
|
| `/knowledge` (`/know`) `[query]` | Manage local knowledge base |
|
|
104
105
|
| **Models & configuration** | |
|
|
105
106
|
| `/model [alias]` | Switch LLM model |
|
|
106
|
-
| `/config` (`/login`) |
|
|
107
|
+
| `/config` (`/login`) | Configure model services: quick provider pick, custom DIY setup, or image generation |
|
|
107
108
|
| `/config image` | Configure image generation (provider, URL, key, model) |
|
|
108
109
|
| `/logout` (`/disconnect`) | Remove configured models |
|
|
109
110
|
| `/language` (`/lang`) | Switch interface language |
|
|
@@ -136,6 +137,8 @@ Type `/` in the input to browse. All 55 commands:
|
|
|
136
137
|
| `/cron` | View and manage scheduled tasks — list, create (pick a time + a task), delete |
|
|
137
138
|
| `/like` | Set your preferences |
|
|
138
139
|
| `/help` (`/h`) | Show commands and shortcuts |
|
|
140
|
+
| `/hotkeys` (`/keys`) | Keyboard shortcut reference |
|
|
141
|
+
| `/copy` (`/yank`) | Copy the last reply to the clipboard |
|
|
139
142
|
| `/exit` (`/quit`, `/q`) | Exit application |
|
|
140
143
|
|
|
141
144
|
</details>
|
|
@@ -4,15 +4,15 @@ import { dirname as __cjsShimDirname } from 'node:path';
|
|
|
4
4
|
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
5
5
|
const __dirname = __cjsShimDirname(__filename);
|
|
6
6
|
import "./suppress-sqlite-warning-C2VB0doZ.mjs";
|
|
7
|
-
import { A as sharedMemoStore } from "./src-
|
|
8
|
-
import { $ as
|
|
9
|
-
import { i as t, n as getLocale, r as setLocale } from "./text-input-dialog-
|
|
7
|
+
import { A as sharedMemoStore } from "./src-ClB_Hzp9.mjs";
|
|
8
|
+
import { $ as clearGoalState, $n as fetchCatalog, $t as isStreaming, A as renderDiffLines, An as isExperimentalFlagEnabled, At as createMarkdownTheme, B as SkillActivationComponent, Bn as TuiLikePreferencesSchema, Bt as DISABLE_TERMINAL_THEME_REPORTING, C as appendJsonlLine, Cn as getCtrlCHint, Ct as refreshProviderBalance, Dn as buildSkillSlashCommands, E as getDaemonInstructions, En as getNoActiveSessionMessage, F as ThinkingComponent, Fn as hasEmbeddingModelCache, Ft as contrastTextHex, G as getSharedSpeedTracker, Gn as getInputHistoryFile, Gt as OSC11_RESPONSE_PREFIX, H as ActivityGroupComponent, Hn as saveTuiConfig, Ht as ENABLE_TERMINAL_THEME_REPORTING, I as NoticeMessageComponent, In as startManualEmbeddingDownload, It as getColorPalette, J as WelcomeComponent, Jn as CLI_COMMAND_NAME, Jt as TERMINAL_FOCUS_IN, K as BackgroundAgentStatusComponent, Kn as getLogDir, Kt as OSC11_RESPONSE_PREFIX_NO_ESC, L as StatusMessageComponent, Ln as PIXEL_PULSE_FRAMES, Lt as detectTerminalTheme, M as highlightLines, Mn as embeddingProgressLabel, Mt as isTurnElapsedEnabled, N as langFromPath, Nn as embeddingFailureDetail, Nt as toggleEmptySessionHint, O as UserMessageComponent, On as BUILTIN_SLASH_COMMANDS, Ot as resolveThemeSync, P as CachedContainer, Pn as getKnowledgeStore, Pt as createThemeStyles, Q as CompactionComponent, Qn as DEFAULT_CATALOG_URL, Qt as isBusy, R as replaceTabs, Rn as PULSE_WAVE_FRAMES, Rt as parseOsc11BackgroundTheme, S as readUpdateCache, Sn as TIP_ROTATION_INTERVAL_MS, Tn as getLlmNotSetMessage, Tt as clearInfoPanelState, U as charsForTokenBudget, Un as detectShellEnvironment, Ut as OSC11_QUERY, V as AssistantMessageComponent, Vn as loadTuiConfig, Vt as ENABLE_TERMINAL_FOCUS_REPORTING, W as estimateTokens, Wn as getDataDir, Wt as OSC11_RESPONSE, X as getBreathingFrame, Xn as CLI_USER_AGENT_PRODUCT, Xt as TERMINAL_THEME_DARK, Y as BREATHE_CYCLE_MS, Yn as CLI_UI_MODE, Yt as TERMINAL_FOCUS_OUT, Z as resetBreathingClock, Zn as PRODUCT_NAME, Zt as TERMINAL_THEME_LIGHT, _n as truncateErrorMessage, _r as SCREAM_ERROR_INFO, a as buildRoleAdditionalText, ar as readSessionIndex, b as selectUpdateTarget, bn as MAIN_AGENT_ID, cn as STATUS_BULLET, cr as isRealUserPrompt, ct as changeThinkingLevel, d as hasDispose, dn as argsRecord, dr as flushDiagnosticLogs, en as FooterComponent, er as saveCatalogCache, et as refineGoal, f as isExpandable, fn as formatErrorMessage, fr as log, gn as stringValue, gr as ErrorCodes, gt as handlePlanCommand, h as formatMemoryMemoForInjection, hn as serializeToolResultOutput, hr as isOrphanedToolCallError, i as openUrl, ir as appendSessionIndexEntry, j as renderDiffLinesClustered, jn as setExperimentalFlags, jt as isEmptySessionHintDismissed, k as ToolCallComponent, kn as sortSlashCommands, kt as createEditorTheme, ln as appendSessionHeaderHint, lr as parseRlmMaxDepthArg, lt as getModelCycleLevel, m as MoonLoader, mn as parseStreamingArgs, mr as isScreamError, mt as handleFusionPlanCommand, n as clearEvalPanelState, nn as SubagentSlots, nr as encodeWorkDirKey, o as writeTextClipboard, or as removeSessionIndexEntry, ot as isInternalMessage, p as isPlanExpandable, pn as isTodoItemShape, pr as resolveGlobalLogPath, q as AgentGroupComponent, qn as detectInstallSource, qt as QUERY_TERMINAL_THEME, rr as normalizeWorkDir, s as syncDiagramCapabilityPrompt, sn as printableChar, st as toTerminalHyperlink, t as dispatchInput, tn as lerpGradient, tr as ScreamHarness, u as disposeChildren, un as appendStreamingArgsPreview, ur as resolveScreamHome, w as readJsonlFile, wn as getCtrlDHint, wt as supportsBalance, x as refreshUpdateCache, xn as SESSION_TIPS, yn as EXIT_CONFIRM_WINDOW_MS, z as sanitizeShellOutput, zn as TuiConfigParseError, zt as DISABLE_TERMINAL_FOCUS_REPORTING } from "./dispatch-BPfUTccZ.mjs";
|
|
9
|
+
import { i as t, n as getLocale, r as setLocale } from "./text-input-dialog-DHDsHCqR.mjs";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import { randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
|
|
12
12
|
import { access, appendFile, chmod, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
13
13
|
import { Writable } from "node:stream";
|
|
14
14
|
import { WebSocket, WebSocketServer } from "ws";
|
|
15
|
-
import { appendFileSync, constants, existsSync, mkdirSync, readFileSync, realpathSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { appendFileSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
16
16
|
import * as path$1 from "node:path";
|
|
17
17
|
import path, { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
18
18
|
import { homedir, networkInterfaces, tmpdir } from "node:os";
|
|
@@ -33,7 +33,7 @@ function optionalBuildString(value) {
|
|
|
33
33
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
34
34
|
}
|
|
35
35
|
const SCREAM_BUILD_INFO = {
|
|
36
|
-
version: optionalBuildString("0.18.
|
|
36
|
+
version: optionalBuildString("0.18.8"),
|
|
37
37
|
channel: optionalBuildString(""),
|
|
38
38
|
commit: optionalBuildString(""),
|
|
39
39
|
buildTarget: optionalBuildString("darwin-arm64")
|
|
@@ -948,6 +948,91 @@ function wrapText$1(text, width) {
|
|
|
948
948
|
return lines.length > 0 ? lines : [""];
|
|
949
949
|
}
|
|
950
950
|
//#endregion
|
|
951
|
+
//#region src/tui/utils/runtime-marker.ts
|
|
952
|
+
const MARKER_VERSION = 1;
|
|
953
|
+
/** Directory holding one marker per live TUI process. */
|
|
954
|
+
function runtimeMarkerDir(homeDir) {
|
|
955
|
+
return join(homeDir, "runtime", "instances");
|
|
956
|
+
}
|
|
957
|
+
function markerPath(homeDir, pid) {
|
|
958
|
+
return join(runtimeMarkerDir(homeDir), `${pid}.json`);
|
|
959
|
+
}
|
|
960
|
+
/** True when `pid` exists and we may signal it (EPERM means it is alive). */
|
|
961
|
+
function isProcessAlive(pid) {
|
|
962
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
963
|
+
try {
|
|
964
|
+
process.kill(pid, 0);
|
|
965
|
+
return true;
|
|
966
|
+
} catch (error) {
|
|
967
|
+
return error?.code === "EPERM";
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Register this process as running. Written atomically (tmp + rename) so a
|
|
972
|
+
* crash mid-write cannot leave a torn JSON file behind.
|
|
973
|
+
*/
|
|
974
|
+
function writeRuntimeMarker(homeDir, info) {
|
|
975
|
+
mkdirSync(runtimeMarkerDir(homeDir), { recursive: true });
|
|
976
|
+
const payload = {
|
|
977
|
+
version: MARKER_VERSION,
|
|
978
|
+
...info
|
|
979
|
+
};
|
|
980
|
+
const target = markerPath(homeDir, info.pid);
|
|
981
|
+
const staged = `${target}.tmp`;
|
|
982
|
+
writeFileSync(staged, JSON.stringify(payload), "utf-8");
|
|
983
|
+
renameSync(staged, target);
|
|
984
|
+
}
|
|
985
|
+
/** Remove this process's marker. Best effort: never blocks shutdown. */
|
|
986
|
+
function clearRuntimeMarker(homeDir, pid = process.pid) {
|
|
987
|
+
try {
|
|
988
|
+
rmSync(markerPath(homeDir, pid), { force: true });
|
|
989
|
+
} catch {}
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Collect markers left by processes that are gone, newest first, and delete
|
|
993
|
+
* them. Unreadable files are removed too: they can only be leftovers, and
|
|
994
|
+
* keeping them would make the hint repeat forever.
|
|
995
|
+
*/
|
|
996
|
+
function collectStaleMarkers(homeDir) {
|
|
997
|
+
const dir = runtimeMarkerDir(homeDir);
|
|
998
|
+
if (!existsSync(dir)) return [];
|
|
999
|
+
const stale = [];
|
|
1000
|
+
const now = Date.now();
|
|
1001
|
+
for (const fileName of readdirSync(dir)) {
|
|
1002
|
+
if (fileName.endsWith(".json.tmp")) {
|
|
1003
|
+
const draft = join(dir, fileName);
|
|
1004
|
+
try {
|
|
1005
|
+
if (now - statSync(draft).mtimeMs > 5 * 6e4) rmSync(draft, { force: true });
|
|
1006
|
+
} catch {
|
|
1007
|
+
rmSync(draft, { force: true });
|
|
1008
|
+
}
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
if (!fileName.endsWith(".json")) continue;
|
|
1012
|
+
const filePath = join(dir, fileName);
|
|
1013
|
+
let marker;
|
|
1014
|
+
try {
|
|
1015
|
+
marker = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
1016
|
+
} catch {
|
|
1017
|
+
rmSync(filePath, { force: true });
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
if (marker === null || typeof marker !== "object" || typeof marker.pid !== "number" || marker.version !== MARKER_VERSION) {
|
|
1021
|
+
rmSync(filePath, { force: true });
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
if (isProcessAlive(marker.pid)) continue;
|
|
1025
|
+
stale.push({
|
|
1026
|
+
pid: marker.pid,
|
|
1027
|
+
startedAt: typeof marker.startedAt === "number" ? marker.startedAt : 0,
|
|
1028
|
+
...typeof marker.sessionId === "string" ? { sessionId: marker.sessionId } : {},
|
|
1029
|
+
...typeof marker.title === "string" ? { title: marker.title } : {}
|
|
1030
|
+
});
|
|
1031
|
+
rmSync(filePath, { force: true });
|
|
1032
|
+
}
|
|
1033
|
+
return stale.toSorted((a, b) => b.startedAt - a.startedAt);
|
|
1034
|
+
}
|
|
1035
|
+
//#endregion
|
|
951
1036
|
//#region src/tui/controllers/auth-flow.ts
|
|
952
1037
|
var AuthFlowController = class {
|
|
953
1038
|
host;
|
|
@@ -10368,116 +10453,6 @@ var QuestionController = class extends ReverseRpcController {
|
|
|
10368
10453
|
}
|
|
10369
10454
|
};
|
|
10370
10455
|
//#endregion
|
|
10371
|
-
//#region src/utils/clipboard/clipboard-text.ts
|
|
10372
|
-
/**
|
|
10373
|
-
* Write plain text to the system clipboard with platform-specific commands.
|
|
10374
|
-
*
|
|
10375
|
-
* scream-core's clipboard layer is read-only (clipboard-native / clipboard-image),
|
|
10376
|
-
* so the TUI's "copy selected text" needs its own writer. Used to back the
|
|
10377
|
-
* pi-tui `copySelection` hook — return true/false so the TUI can flash
|
|
10378
|
-
* "Copied!" or "Copy failed".
|
|
10379
|
-
*
|
|
10380
|
-
* Platform mapping:
|
|
10381
|
-
* macOS -> pbcopy
|
|
10382
|
-
* Windows-> powershell Set-Clipboard (base64 payload, avoids quoting issues)
|
|
10383
|
-
* Linux -> wl-copy (Wayland) / xclip or xsel (X11), tried in a candidate
|
|
10384
|
-
* chain so a missing tool (Ubuntu ships neither by default)
|
|
10385
|
-
* falls through to the next one instead of failing outright.
|
|
10386
|
-
*/
|
|
10387
|
-
var ClipboardTextError = class extends Error {
|
|
10388
|
-
constructor(message) {
|
|
10389
|
-
super(message);
|
|
10390
|
-
this.name = "ClipboardTextError";
|
|
10391
|
-
}
|
|
10392
|
-
};
|
|
10393
|
-
const defaultRunCommand = (command, args, input) => {
|
|
10394
|
-
const result = spawnSync(command, args, {
|
|
10395
|
-
input,
|
|
10396
|
-
encoding: "utf8",
|
|
10397
|
-
timeout: 3e3
|
|
10398
|
-
});
|
|
10399
|
-
if (result.status === 0) return { ok: true };
|
|
10400
|
-
return {
|
|
10401
|
-
ok: false,
|
|
10402
|
-
error: result.error?.message ?? result.stderr?.toString() ?? `exit ${result.status}`
|
|
10403
|
-
};
|
|
10404
|
-
};
|
|
10405
|
-
function detectPlatform() {
|
|
10406
|
-
if (process.platform === "darwin") return "macos";
|
|
10407
|
-
if (process.platform === "win32") return "windows";
|
|
10408
|
-
if (process.platform === "linux") return process.env["WAYLAND_DISPLAY"] ? "linux-wayland" : "linux-x11";
|
|
10409
|
-
return "unsupported";
|
|
10410
|
-
}
|
|
10411
|
-
function linuxCandidates(platform) {
|
|
10412
|
-
const wl = {
|
|
10413
|
-
command: "wl-copy",
|
|
10414
|
-
args: []
|
|
10415
|
-
};
|
|
10416
|
-
const xclip = {
|
|
10417
|
-
command: "xclip",
|
|
10418
|
-
args: [
|
|
10419
|
-
"-selection",
|
|
10420
|
-
"clipboard",
|
|
10421
|
-
"-i"
|
|
10422
|
-
]
|
|
10423
|
-
};
|
|
10424
|
-
const xsel = {
|
|
10425
|
-
command: "xsel",
|
|
10426
|
-
args: ["--clipboard", "--input"]
|
|
10427
|
-
};
|
|
10428
|
-
return platform === "linux-wayland" ? [
|
|
10429
|
-
wl,
|
|
10430
|
-
xclip,
|
|
10431
|
-
xsel
|
|
10432
|
-
] : [
|
|
10433
|
-
xclip,
|
|
10434
|
-
xsel,
|
|
10435
|
-
wl
|
|
10436
|
-
];
|
|
10437
|
-
}
|
|
10438
|
-
/**
|
|
10439
|
-
* Write `text` to the system clipboard. Throws ClipboardTextError when the
|
|
10440
|
-
* platform is unsupported or every candidate command fails, so callers can
|
|
10441
|
-
* surface a "Copy failed" indicator.
|
|
10442
|
-
*/
|
|
10443
|
-
async function writeTextClipboard(text, runCommand = defaultRunCommand) {
|
|
10444
|
-
const platform = detectPlatform();
|
|
10445
|
-
let command;
|
|
10446
|
-
let args;
|
|
10447
|
-
let input;
|
|
10448
|
-
switch (platform) {
|
|
10449
|
-
case "macos":
|
|
10450
|
-
command = "pbcopy";
|
|
10451
|
-
args = [];
|
|
10452
|
-
input = text;
|
|
10453
|
-
break;
|
|
10454
|
-
case "windows": {
|
|
10455
|
-
const base64 = Buffer.from(text, "utf8").toString("base64");
|
|
10456
|
-
command = "powershell";
|
|
10457
|
-
args = [
|
|
10458
|
-
"-NoProfile",
|
|
10459
|
-
"-Command",
|
|
10460
|
-
`[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${base64}')) | Set-Clipboard`
|
|
10461
|
-
];
|
|
10462
|
-
input = "";
|
|
10463
|
-
break;
|
|
10464
|
-
}
|
|
10465
|
-
case "linux-wayland":
|
|
10466
|
-
case "linux-x11": {
|
|
10467
|
-
const failures = [];
|
|
10468
|
-
for (const candidate of linuxCandidates(platform)) {
|
|
10469
|
-
const result = runCommand(candidate.command, candidate.args, text);
|
|
10470
|
-
if (result.ok) return;
|
|
10471
|
-
failures.push(`${candidate.command}: ${result.error ?? "unknown error"}`);
|
|
10472
|
-
}
|
|
10473
|
-
throw new ClipboardTextError(`failed to copy to clipboard (${failures.join(" | ")}). Install wl-clipboard (Wayland: sudo apt install wl-clipboard) or xclip/xsel (X11: sudo apt install xclip xsel)`);
|
|
10474
|
-
}
|
|
10475
|
-
default: throw new ClipboardTextError(`unsupported clipboard platform: ${process.platform}`);
|
|
10476
|
-
}
|
|
10477
|
-
const result = runCommand(command, args, input);
|
|
10478
|
-
if (!result.ok) throw new ClipboardTextError(`failed to copy to clipboard: ${command} ${result.error ?? "unknown error"}`);
|
|
10479
|
-
}
|
|
10480
|
-
//#endregion
|
|
10481
10456
|
//#region src/tui/utils/render-batcher.ts
|
|
10482
10457
|
function createRenderBatcher(doRender) {
|
|
10483
10458
|
let scheduled = false;
|
|
@@ -12998,69 +12973,86 @@ function stripLeadingSpace(s) {
|
|
|
12998
12973
|
* it as the focused component, and tears it down on the `onClose`
|
|
12999
12974
|
* callback (fired on Esc / Enter / q).
|
|
13000
12975
|
*/
|
|
13001
|
-
/**
|
|
13002
|
-
|
|
13003
|
-
|
|
13004
|
-
|
|
13005
|
-
|
|
13006
|
-
|
|
13007
|
-
|
|
13008
|
-
|
|
13009
|
-
|
|
13010
|
-
|
|
13011
|
-
|
|
13012
|
-
|
|
13013
|
-
|
|
13014
|
-
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
|
|
13020
|
-
|
|
13021
|
-
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
|
|
13025
|
-
|
|
13026
|
-
|
|
13027
|
-
|
|
13028
|
-
|
|
13029
|
-
|
|
13030
|
-
|
|
13031
|
-
|
|
13032
|
-
|
|
13033
|
-
|
|
13034
|
-
|
|
13035
|
-
|
|
13036
|
-
|
|
13037
|
-
|
|
13038
|
-
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
|
|
13051
|
-
|
|
13052
|
-
|
|
13053
|
-
|
|
13054
|
-
|
|
13055
|
-
|
|
13056
|
-
|
|
13057
|
-
|
|
13058
|
-
|
|
13059
|
-
|
|
13060
|
-
|
|
13061
|
-
|
|
13062
|
-
|
|
13063
|
-
|
|
12976
|
+
/**
|
|
12977
|
+
* Keyboard shortcut reference — keep in sync with the global editor bindings.
|
|
12978
|
+
*
|
|
12979
|
+
* A function, not a constant: `t()` must run on every call. As a module-level
|
|
12980
|
+
* constant the descriptions were evaluated once at import time and froze to
|
|
12981
|
+
* whichever locale happened to be active then, so a Chinese session showed an
|
|
12982
|
+
* English shortcut list (and vice versa).
|
|
12983
|
+
*/
|
|
12984
|
+
function defaultKeyboardShortcuts() {
|
|
12985
|
+
return [
|
|
12986
|
+
{
|
|
12987
|
+
keys: "Shift-Tab",
|
|
12988
|
+
description: t("help.toggle_plan")
|
|
12989
|
+
},
|
|
12990
|
+
{
|
|
12991
|
+
keys: "Tab",
|
|
12992
|
+
description: t("help.cycle_thinking")
|
|
12993
|
+
},
|
|
12994
|
+
{
|
|
12995
|
+
keys: "Ctrl-G",
|
|
12996
|
+
description: t("help.external_editor")
|
|
12997
|
+
},
|
|
12998
|
+
{
|
|
12999
|
+
keys: "Ctrl-O",
|
|
13000
|
+
description: t("help.toggle_output")
|
|
13001
|
+
},
|
|
13002
|
+
{
|
|
13003
|
+
keys: "Ctrl-E",
|
|
13004
|
+
description: t("help.toggle_plan_view")
|
|
13005
|
+
},
|
|
13006
|
+
{
|
|
13007
|
+
keys: "Ctrl-S",
|
|
13008
|
+
description: t("help.interrupt")
|
|
13009
|
+
},
|
|
13010
|
+
{
|
|
13011
|
+
keys: "Ctrl-X",
|
|
13012
|
+
description: t("help.toggle_sidebar")
|
|
13013
|
+
},
|
|
13014
|
+
{
|
|
13015
|
+
keys: "Ctrl-B",
|
|
13016
|
+
description: t("help.toggle_hint")
|
|
13017
|
+
},
|
|
13018
|
+
{
|
|
13019
|
+
keys: "Ctrl-W",
|
|
13020
|
+
description: t("help.cancel_memory")
|
|
13021
|
+
},
|
|
13022
|
+
{
|
|
13023
|
+
keys: "↑",
|
|
13024
|
+
description: t("help.recall_queued")
|
|
13025
|
+
},
|
|
13026
|
+
{
|
|
13027
|
+
keys: "Ctrl-V / Alt-V",
|
|
13028
|
+
description: t("help.paste_image")
|
|
13029
|
+
},
|
|
13030
|
+
{
|
|
13031
|
+
keys: "Shift-Enter / Ctrl-J",
|
|
13032
|
+
description: t("help.newline")
|
|
13033
|
+
},
|
|
13034
|
+
{
|
|
13035
|
+
keys: "Ctrl-C",
|
|
13036
|
+
description: t("help.cancel_stream")
|
|
13037
|
+
},
|
|
13038
|
+
{
|
|
13039
|
+
keys: "Ctrl-D",
|
|
13040
|
+
description: t("help.exit")
|
|
13041
|
+
},
|
|
13042
|
+
{
|
|
13043
|
+
keys: "Esc",
|
|
13044
|
+
description: t("help.close_dialog")
|
|
13045
|
+
},
|
|
13046
|
+
{
|
|
13047
|
+
keys: "↑ / ↓",
|
|
13048
|
+
description: t("help.browse_history")
|
|
13049
|
+
},
|
|
13050
|
+
{
|
|
13051
|
+
keys: "Enter",
|
|
13052
|
+
description: t("help.submit")
|
|
13053
|
+
}
|
|
13054
|
+
];
|
|
13055
|
+
}
|
|
13064
13056
|
var HelpPanelComponent = class extends Container {
|
|
13065
13057
|
focused = false;
|
|
13066
13058
|
opts;
|
|
@@ -13096,33 +13088,42 @@ var HelpPanelComponent = class extends Container {
|
|
|
13096
13088
|
const muted = chalk.hex(colors.textMuted);
|
|
13097
13089
|
const kbdColor = chalk.hex(colors.warning);
|
|
13098
13090
|
const slashColor = chalk.hex(colors.primary);
|
|
13099
|
-
const shortcuts = this.opts.shortcuts ??
|
|
13100
|
-
const
|
|
13091
|
+
const shortcuts = this.opts.shortcuts ?? defaultKeyboardShortcuts();
|
|
13092
|
+
const padToWidth = (text, width) => {
|
|
13093
|
+
const pad = width - visibleWidth(text);
|
|
13094
|
+
return pad > 0 ? text + " ".repeat(pad) : text;
|
|
13095
|
+
};
|
|
13096
|
+
const kbdWidth = Math.max(8, ...shortcuts.map((s) => visibleWidth(s.keys)));
|
|
13101
13097
|
const sortedCmds = [...this.opts.commands].toSorted((a, b) => a.name.localeCompare(b.name));
|
|
13102
13098
|
const cmdLabels = sortedCmds.map((c) => {
|
|
13103
13099
|
const aliases = c.aliases.length > 0 ? ` (${c.aliases.map((a) => "/" + a).join(", ")})` : "";
|
|
13104
13100
|
return `/${c.name}${aliases}`;
|
|
13105
13101
|
});
|
|
13106
13102
|
const cmdWidth = Math.max(12, ...cmdLabels.map((l) => l.length));
|
|
13103
|
+
const hasCommands = sortedCmds.length > 0;
|
|
13107
13104
|
const lines = [
|
|
13108
13105
|
accent("─".repeat(width)),
|
|
13109
|
-
accent.bold(t("help.title")) + muted(` · ${t("help.close_hint")}`),
|
|
13110
|
-
"",
|
|
13111
|
-
` ${dim(t("help.welcome_msg"))}`,
|
|
13112
|
-
` ${muted(t("help.hidden_commands"))}`,
|
|
13106
|
+
accent.bold(this.opts.title ?? t("help.title")) + muted(` · ${t("help.close_hint")}`),
|
|
13113
13107
|
"",
|
|
13108
|
+
...hasCommands ? [
|
|
13109
|
+
` ${dim(t("help.welcome_msg"))}`,
|
|
13110
|
+
` ${muted(t("help.hidden_commands"))}`,
|
|
13111
|
+
""
|
|
13112
|
+
] : [],
|
|
13114
13113
|
` ${chalk.bold(t("help.shortcuts"))}`,
|
|
13115
|
-
...shortcuts.map((s) => ` ${kbdColor(s.keys
|
|
13116
|
-
"",
|
|
13117
|
-
` ${chalk.bold(t("help.slash_commands"))}`,
|
|
13118
|
-
...sortedCmds.map((cmd, i) => {
|
|
13119
|
-
return ` ${slashColor((cmdLabels[i] ?? `/${cmd.name}`).padEnd(cmdWidth))} ${dim(t(cmd.description))}`;
|
|
13120
|
-
}),
|
|
13114
|
+
...shortcuts.map((s) => ` ${kbdColor(padToWidth(s.keys, kbdWidth))} ${dim(s.description)}`),
|
|
13121
13115
|
"",
|
|
13116
|
+
...hasCommands ? [
|
|
13117
|
+
` ${chalk.bold(t("help.slash_commands"))}`,
|
|
13118
|
+
...sortedCmds.map((cmd, i) => {
|
|
13119
|
+
return ` ${slashColor((cmdLabels[i] ?? `/${cmd.name}`).padEnd(cmdWidth))} ${dim(t(cmd.description))}`;
|
|
13120
|
+
}),
|
|
13121
|
+
""
|
|
13122
|
+
] : [],
|
|
13122
13123
|
accent("─".repeat(width))
|
|
13123
13124
|
];
|
|
13124
13125
|
const content = lines.slice(1, lines.length - 1);
|
|
13125
|
-
const maxVisible = Math.max(5, this.opts.maxVisible ??
|
|
13126
|
+
const maxVisible = Math.max(5, this.opts.maxVisible ?? 30);
|
|
13126
13127
|
if (content.length > maxVisible) {
|
|
13127
13128
|
this.scrollTop = Math.max(0, Math.min(this.scrollTop, content.length - maxVisible));
|
|
13128
13129
|
const slice = content.slice(this.scrollTop, this.scrollTop + maxVisible);
|
|
@@ -14477,6 +14478,22 @@ var DialogManager = class {
|
|
|
14477
14478
|
this.host.state.activeDialog = null;
|
|
14478
14479
|
this.restoreEditor();
|
|
14479
14480
|
}
|
|
14481
|
+
showHotkeysPanel() {
|
|
14482
|
+
this.host.state.activeDialog = "hotkeys";
|
|
14483
|
+
this.mountEditorReplacement(new HelpPanelComponent({
|
|
14484
|
+
commands: [],
|
|
14485
|
+
shortcuts: defaultKeyboardShortcuts(),
|
|
14486
|
+
colors: this.host.state.theme.colors,
|
|
14487
|
+
title: t("help.hotkeys_title"),
|
|
14488
|
+
onClose: () => {
|
|
14489
|
+
this.hideHotkeysPanel();
|
|
14490
|
+
}
|
|
14491
|
+
}));
|
|
14492
|
+
}
|
|
14493
|
+
hideHotkeysPanel() {
|
|
14494
|
+
this.host.state.activeDialog = null;
|
|
14495
|
+
this.restoreEditor();
|
|
14496
|
+
}
|
|
14480
14497
|
async showSessionPicker() {
|
|
14481
14498
|
await this.host.fetchSessions();
|
|
14482
14499
|
this.host.state.activeDialog = "session-picker";
|
|
@@ -14750,6 +14767,7 @@ var ScreamTUI = class {
|
|
|
14750
14767
|
deferUserMessages = false;
|
|
14751
14768
|
aborted = false;
|
|
14752
14769
|
isShuttingDown = false;
|
|
14770
|
+
startedAt = Date.now();
|
|
14753
14771
|
reverseRpcDisposers = [];
|
|
14754
14772
|
startupNotice;
|
|
14755
14773
|
/** Interval handle for the periodic provider-balance refresh. */
|
|
@@ -14856,6 +14874,8 @@ var ScreamTUI = class {
|
|
|
14856
14874
|
this.lifecycleController.startEventLoop();
|
|
14857
14875
|
try {
|
|
14858
14876
|
await this.finishStartup(shouldReplayHistory);
|
|
14877
|
+
this.refreshRuntimeMarker();
|
|
14878
|
+
this.reportStaleSessions();
|
|
14859
14879
|
this.lifecycleController.startCcConnectPolling();
|
|
14860
14880
|
this.startBalancePolling();
|
|
14861
14881
|
} catch (error) {
|
|
@@ -14940,6 +14960,34 @@ var ScreamTUI = class {
|
|
|
14940
14960
|
throw error;
|
|
14941
14961
|
}
|
|
14942
14962
|
}
|
|
14963
|
+
/** Keep this instance's crash marker in sync with the running session. */
|
|
14964
|
+
refreshRuntimeMarker() {
|
|
14965
|
+
try {
|
|
14966
|
+
writeRuntimeMarker(getDataDir(), {
|
|
14967
|
+
pid: process.pid,
|
|
14968
|
+
startedAt: this.startedAt,
|
|
14969
|
+
...this.state.appState.sessionId ? { sessionId: this.state.appState.sessionId } : {},
|
|
14970
|
+
...this.state.appState.sessionTitle ? { title: this.state.appState.sessionTitle } : {}
|
|
14971
|
+
});
|
|
14972
|
+
} catch {}
|
|
14973
|
+
}
|
|
14974
|
+
/** Report a previous instance that never shut down, exactly once. */
|
|
14975
|
+
reportStaleSessions() {
|
|
14976
|
+
let stale = [];
|
|
14977
|
+
try {
|
|
14978
|
+
stale = collectStaleMarkers(getDataDir());
|
|
14979
|
+
} catch {
|
|
14980
|
+
return;
|
|
14981
|
+
}
|
|
14982
|
+
const latest = stale[0];
|
|
14983
|
+
if (latest === void 0) return;
|
|
14984
|
+
const label = latest.title ?? (latest.sessionId !== void 0 ? latest.sessionId.slice(0, 8) : "");
|
|
14985
|
+
if (label.length === 0) {
|
|
14986
|
+
this.showNotice(t("tui.crash_recovery_title"));
|
|
14987
|
+
return;
|
|
14988
|
+
}
|
|
14989
|
+
this.showNotice(t("tui.crash_recovery_title"), t("tui.crash_recovery_detail", { title: label }));
|
|
14990
|
+
}
|
|
14943
14991
|
async stop(exitCode) {
|
|
14944
14992
|
if (this.isShuttingDown) return;
|
|
14945
14993
|
this.isShuttingDown = true;
|
|
@@ -14960,6 +15008,7 @@ var ScreamTUI = class {
|
|
|
14960
15008
|
this.inputController.dispose();
|
|
14961
15009
|
this.state.footer.dispose();
|
|
14962
15010
|
this.state.sidebarContainer.dispose();
|
|
15011
|
+
clearRuntimeMarker(getDataDir());
|
|
14963
15012
|
await this.closeSession();
|
|
14964
15013
|
await this.harness.close();
|
|
14965
15014
|
this.sessionEventHandler.stopAllMcpServerStatusSpinners();
|
|
@@ -14990,6 +15039,7 @@ var ScreamTUI = class {
|
|
|
14990
15039
|
onEmergencyExit(exitCode = 129) {
|
|
14991
15040
|
this.isShuttingDown = true;
|
|
14992
15041
|
this.lifecycleController.uninstallSignalHandlers();
|
|
15042
|
+
clearRuntimeMarker(getDataDir());
|
|
14993
15043
|
process.exit(exitCode);
|
|
14994
15044
|
}
|
|
14995
15045
|
stopMemoryIdleTimer() {
|
|
@@ -15139,6 +15189,7 @@ var ScreamTUI = class {
|
|
|
15139
15189
|
const planModeChanged = "planMode" in patch && prevPlanMode !== this.state.appState.planMode;
|
|
15140
15190
|
const sessionChanged = "sessionId" in patch && this.state.appState.sessionId !== prevSessionId;
|
|
15141
15191
|
if (sessionChanged) {
|
|
15192
|
+
this.refreshRuntimeMarker();
|
|
15142
15193
|
if (!("sessionUsage" in patch)) this.state.appState.sessionUsage = {
|
|
15143
15194
|
inputOther: 0,
|
|
15144
15195
|
output: 0,
|
|
@@ -15270,7 +15321,7 @@ var ScreamTUI = class {
|
|
|
15270
15321
|
const card = new OnboardingCardComponent({
|
|
15271
15322
|
colors: this.state.theme.colors,
|
|
15272
15323
|
onConfigure: () => {
|
|
15273
|
-
import("./dispatch-
|
|
15324
|
+
import("./dispatch-zsFzFFoY.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
|
|
15274
15325
|
this.restoreEditor();
|
|
15275
15326
|
onDone?.();
|
|
15276
15327
|
});
|
|
@@ -15308,7 +15359,9 @@ var ScreamTUI = class {
|
|
|
15308
15359
|
let error;
|
|
15309
15360
|
let failureKind;
|
|
15310
15361
|
try {
|
|
15311
|
-
({ok, error, failureKind} = await startManualEmbeddingDownload()
|
|
15362
|
+
({ok, error, failureKind} = await startManualEmbeddingDownload((progress) => {
|
|
15363
|
+
spinner.setLabel(embeddingProgressLabel(progress));
|
|
15364
|
+
}));
|
|
15312
15365
|
} catch (error) {
|
|
15313
15366
|
error = error instanceof Error ? error.message : String(error);
|
|
15314
15367
|
}
|
|
@@ -15396,6 +15449,9 @@ var ScreamTUI = class {
|
|
|
15396
15449
|
showHelpPanel() {
|
|
15397
15450
|
this.dialogManager.showHelpPanel(this.getSlashCommands());
|
|
15398
15451
|
}
|
|
15452
|
+
showHotkeysPanel() {
|
|
15453
|
+
this.dialogManager.showHotkeysPanel();
|
|
15454
|
+
}
|
|
15399
15455
|
hideHelpPanel() {
|
|
15400
15456
|
this.dialogManager.hideHelpPanel();
|
|
15401
15457
|
}
|