scream-code 0.11.6 → 0.11.7

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.
@@ -7,7 +7,7 @@ import { i as __require, o as __toESM, r as __exportAll, t as __commonJSMin } fr
7
7
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
8
8
  import { C as join$1, D as resolve$1, E as relative$1, S as isAbsolute$1, T as parse$7, a as isSupportedFile, b as basename$1, i as ingestFile, r as ingestDirectory, t as multiSearch, w as normalize, x as dirname$2, y as KnowledgeStore } from "./src-BH9W5k24.mjs";
9
9
  import { t as require_base64_js } from "./base64-js-DzVmk6Nb.mjs";
10
- import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-K1FbYyug.mjs";
10
+ import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-BdUmo73M.mjs";
11
11
  import { createRequire } from "node:module";
12
12
  import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
13
13
  import * as fs$1 from "node:fs/promises";
@@ -74934,7 +74934,7 @@ var BashTool = class {
74934
74934
  },
74935
74935
  approvalRule: literalRulePattern(this.name, args.command),
74936
74936
  matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.command),
74937
- execute: ({ signal }) => this.execution(args, signal)
74937
+ execute: (ctx) => this.execution(args, ctx)
74938
74938
  };
74939
74939
  }
74940
74940
  spawn(effectiveCwd, command) {
@@ -74954,7 +74954,8 @@ var BashTool = class {
74954
74954
  };
74955
74955
  return this.jian.execWithEnv(shellArgs, mergedEnv);
74956
74956
  }
74957
- async execution(args, signal) {
74957
+ async execution(args, ctx) {
74958
+ const { signal, onUpdate } = ctx;
74958
74959
  const completedBg = drainCompletedBackgroundTasks();
74959
74960
  const bgPrefix = completedBg.length > 0 ? completedBg.map((t) => `[Background task ${t.id} completed] Command: ${t.command}\nExit code: ${String(t.exitCode)} (${(t.elapsedMs / 1e3).toFixed(1)}s)\nOutput:\n${t.output}\n---\n`).join("") : "";
74960
74961
  if (signal.aborted) return {
@@ -74993,6 +74994,14 @@ var BashTool = class {
74993
74994
  } catch {}
74994
74995
  let aborted = false;
74995
74996
  let killed = false;
74997
+ let streaming = true;
74998
+ const forwardChunk = (kind, text) => {
74999
+ if (!streaming || signal.aborted || onUpdate === void 0) return;
75000
+ onUpdate({
75001
+ kind,
75002
+ text
75003
+ });
75004
+ };
74996
75005
  const killProc = async () => {
74997
75006
  if (killed) return;
74998
75007
  killed = true;
@@ -75030,17 +75039,39 @@ var BashTool = class {
75030
75039
  return artifactPath;
75031
75040
  } });
75032
75041
  if (bgPrefix.length > 0) builder.write(bgPrefix);
75033
- const completionPromise = Promise.all([Promise.all([readStreamIntoBuilder(proc.stdout, builder), readStreamIntoBuilder(proc.stderr, builder)]), proc.wait()]).then(([, exitCode]) => ({
75042
+ const completionPromise = Promise.all([Promise.all([readStreamIntoBuilder(proc.stdout, builder, (text) => forwardChunk("stdout", text)), readStreamIntoBuilder(proc.stderr, builder, (text) => forwardChunk("stderr", text))]), proc.wait()]).then(([, exitCode]) => ({
75034
75043
  timedOut: false,
75035
75044
  exitCode
75036
75045
  }));
75037
75046
  const raceResult = timeoutMs !== void 0 ? await Promise.race([completionPromise, timeoutPromise]) : await completionPromise;
75038
75047
  if (raceResult.timedOut) {
75048
+ streaming = false;
75039
75049
  const timeoutLabel = timeoutMs % 1e3 === 0 ? `${String(timeoutMs / 1e3)}s` : `${String(timeoutMs)}ms`;
75040
75050
  const taskId = createBackgroundTask(command, completionPromise.then(({ exitCode }) => ({
75041
75051
  exitCode,
75042
75052
  output: builder.toString()
75043
75053
  })));
75054
+ if (onUpdate !== void 0) completionPromise.then(({ exitCode }) => {
75055
+ onUpdate({
75056
+ kind: "custom",
75057
+ customKind: "background.task.terminated",
75058
+ customData: {
75059
+ id: taskId,
75060
+ command,
75061
+ exitCode
75062
+ }
75063
+ });
75064
+ }, () => {
75065
+ onUpdate({
75066
+ kind: "custom",
75067
+ customKind: "background.task.terminated",
75068
+ customData: {
75069
+ id: taskId,
75070
+ command,
75071
+ exitCode: -1
75072
+ }
75073
+ });
75074
+ });
75044
75075
  const outputSoFar = builder.toString();
75045
75076
  return {
75046
75077
  output: `Command timed out after ${timeoutLabel} but is still running in the background (task: ${taskId}).\nOutput so far (${String(builder.nChars)} chars):\n${outputSoFar}\n---\nThe command will complete in the background. The result will be included in your next Bash call.`,
@@ -75138,13 +75169,33 @@ human_shell_hint: Tell the human to run /tasks to open the interactive backgroun
75138
75169
  return builder.ok("Background task started", { brief: `Started ${taskId}` });
75139
75170
  }
75140
75171
  };
75141
- async function readStreamIntoBuilder(stream, builder) {
75172
+ const LIVE_OUTPUT_FLUSH_MS = 100;
75173
+ async function readStreamIntoBuilder(stream, builder, onChunk) {
75142
75174
  const decoder = new StringDecoder("utf8");
75175
+ let pending = "";
75176
+ let lastFlush = 0;
75177
+ const flush = (force) => {
75178
+ if (pending.length === 0) return;
75179
+ if (!force && Date.now() - lastFlush < LIVE_OUTPUT_FLUSH_MS) return;
75180
+ onChunk?.(pending);
75181
+ pending = "";
75182
+ lastFlush = Date.now();
75183
+ };
75143
75184
  for await (const chunk of stream) {
75144
75185
  const buf = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
75145
- builder.write(decoder.write(buf));
75186
+ const text = decoder.write(buf);
75187
+ builder.write(text);
75188
+ if (onChunk !== void 0) {
75189
+ pending += text;
75190
+ flush(false);
75191
+ }
75192
+ }
75193
+ const tail = decoder.end();
75194
+ builder.write(tail);
75195
+ if (onChunk !== void 0) {
75196
+ pending += tail;
75197
+ flush(true);
75146
75198
  }
75147
- builder.write(decoder.end());
75148
75199
  }
75149
75200
  function shellQuote$1(s) {
75150
75201
  return `'${s.replaceAll("'", "'\\''")}'`;
@@ -120602,7 +120653,7 @@ function optionalBuildString(value) {
120602
120653
  return typeof value === "string" && value.length > 0 ? value : void 0;
120603
120654
  }
120604
120655
  const SCREAM_BUILD_INFO = {
120605
- version: optionalBuildString("0.11.6"),
120656
+ version: optionalBuildString("0.11.7"),
120606
120657
  channel: optionalBuildString(""),
120607
120658
  commit: optionalBuildString(""),
120608
120659
  buildTarget: optionalBuildString("darwin-arm64")
@@ -120872,6 +120923,8 @@ const TuiLikePreferencesSchema = z.object({
120872
120923
  const TuiConfigFileSchema = z.object({
120873
120924
  theme: TuiThemeSchema.optional(),
120874
120925
  language: z.enum(["zh", "en"]).optional(),
120926
+ /** Auto-enter the welcome page after the loading splash finishes. */
120927
+ autoStart: z.boolean().optional(),
120875
120928
  editor: z.object({ command: z.string().optional() }).optional(),
120876
120929
  notifications: z.object({
120877
120930
  enabled: z.boolean().optional(),
@@ -120887,6 +120940,7 @@ const TuiConfigFileSchema = z.object({
120887
120940
  const TuiConfigSchema = z.object({
120888
120941
  theme: TuiThemeSchema,
120889
120942
  language: z.enum(["zh", "en"]),
120943
+ autoStart: z.boolean(),
120890
120944
  editorCommand: z.string().nullable(),
120891
120945
  notifications: NotificationsConfigSchema,
120892
120946
  like: TuiLikePreferencesSchema,
@@ -120903,6 +120957,7 @@ const DEFAULT_NOTIFICATIONS_CONFIG = {
120903
120957
  const DEFAULT_TUI_CONFIG = TuiConfigSchema.parse({
120904
120958
  theme: "auto",
120905
120959
  language: getLocale(),
120960
+ autoStart: false,
120906
120961
  editorCommand: null,
120907
120962
  notifications: DEFAULT_NOTIFICATIONS_CONFIG,
120908
120963
  like: {},
@@ -120955,6 +121010,7 @@ function normalizeTuiConfig(config) {
120955
121010
  return TuiConfigSchema.parse({
120956
121011
  theme: config.theme ?? DEFAULT_TUI_CONFIG.theme,
120957
121012
  language: config.language ?? DEFAULT_TUI_CONFIG.language,
121013
+ autoStart: config.autoStart ?? DEFAULT_TUI_CONFIG.autoStart,
120958
121014
  editorCommand: command === void 0 || command.length === 0 ? null : command,
120959
121015
  notifications: {
120960
121016
  enabled: config.notifications?.enabled ?? DEFAULT_NOTIFICATIONS_CONFIG.enabled,
@@ -120998,6 +121054,7 @@ function renderTuiConfig(config) {
120998
121054
 
120999
121055
  theme = "${config.theme}" # "auto" | "dark" | "light"
121000
121056
  language = "${config.language}" # "zh" | "en"
121057
+ autoStart = ${String(config.autoStart)} # true = auto-enter welcome after loading
121001
121058
 
121002
121059
  [editor]
121003
121060
  command = "${escapeTomlBasicString(config.editorCommand ?? "")}" # Empty uses $VISUAL / $EDITOR
@@ -123813,14 +123870,28 @@ function formatTokenCount(n) {
123813
123870
  function safeUsage(usage) {
123814
123871
  return safeUsageRatio(usage);
123815
123872
  }
123873
+ const CONTEXT_BAR_WIDTH = 10;
123874
+ const CONTEXT_BAR_FILLED = "▰";
123875
+ const CONTEXT_BAR_EMPTY = "▱";
123876
+ /**
123877
+ * Half-block progress bar for context usage: `▰▰▰▱▱▱▱▱▱▱` (10 cells).
123878
+ * Filled cells are rounded from the clamped ratio, so 0% is all-empty and
123879
+ * >=100% is all-filled; NaN/undefined coerce through safeUsageRatio first.
123880
+ */
123881
+ function formatContextBar(usage, width = CONTEXT_BAR_WIDTH) {
123882
+ const clamped = Math.min(1, Math.max(0, safeUsageRatio(usage)));
123883
+ const filled = Math.round(clamped * width);
123884
+ return CONTEXT_BAR_FILLED.repeat(filled) + CONTEXT_BAR_EMPTY.repeat(width - filled);
123885
+ }
123816
123886
  function formatContextStatus(usage, tokens, maxTokens) {
123817
123887
  const pct = `${(safeUsage(usage) * 100).toFixed(1)}%`;
123888
+ const barAndPct = `${formatContextBar(usage)} ${pct}`;
123818
123889
  if (maxTokens && maxTokens > 0 && tokens !== void 0) return t("footer.context", {
123819
- pct,
123890
+ pct: barAndPct,
123820
123891
  tokens: formatTokenCount(tokens),
123821
123892
  maxTokens: formatTokenCount(maxTokens)
123822
123893
  });
123823
- return t("footer.context_short", { pct });
123894
+ return t("footer.context_short", { pct: barAndPct });
123824
123895
  }
123825
123896
  /** Format goal wall-clock duration compactly: `3m`, `1m30s`, `45s`. */
123826
123897
  function formatGoalDuration(ms) {
@@ -124093,6 +124164,7 @@ async function applyLanguageChoice(host, locale) {
124093
124164
  setLocale(locale);
124094
124165
  host.state.appState.language = locale;
124095
124166
  await saveTuiConfig({
124167
+ ...await loadTuiConfig(),
124096
124168
  theme: host.state.appState.theme,
124097
124169
  language: locale,
124098
124170
  editorCommand: host.state.appState.editorCommand,
@@ -125159,6 +125231,7 @@ async function applyEditorChoice(host, value) {
125159
125231
  const editorCommand = value.length > 0 ? value : null;
125160
125232
  try {
125161
125233
  await saveTuiConfig({
125234
+ ...await loadTuiConfig(),
125162
125235
  theme: host.state.appState.theme,
125163
125236
  language: host.state.appState.language,
125164
125237
  editorCommand,
@@ -125344,6 +125417,7 @@ async function applyThemeChoice(host, theme) {
125344
125417
  }
125345
125418
  try {
125346
125419
  await saveTuiConfig({
125420
+ ...await loadTuiConfig(),
125347
125421
  theme,
125348
125422
  language: host.state.appState.language,
125349
125423
  editorCommand: host.state.appState.editorCommand,
@@ -126121,7 +126195,7 @@ async function guidedGoalSetup(host) {
126121
126195
  host.showNotice(t("goal.storm_breaker"), t("goal.conflict_loop"));
126122
126196
  return;
126123
126197
  }
126124
- const { TextInputDialogComponent } = await import("./text-input-dialog-Crp_BPkd.mjs");
126198
+ const { TextInputDialogComponent } = await import("./text-input-dialog-Ct-Yn67a.mjs");
126125
126199
  const initialDesc = await promptText(host, TextInputDialogComponent, {
126126
126200
  title: t("goal.setup_title_initial"),
126127
126201
  subtitle: t("goal.setup_desc_hint"),
@@ -126142,7 +126216,7 @@ async function guidedGoalSetup(host) {
126142
126216
  await showGoalConfigWizard(host, session, confirmed.trim() || objective, false);
126143
126217
  }
126144
126218
  async function showGoalConfigWizard(host, session, objective, replace) {
126145
- const { TextInputDialogComponent } = await import("./text-input-dialog-Crp_BPkd.mjs");
126219
+ const { TextInputDialogComponent } = await import("./text-input-dialog-Ct-Yn67a.mjs");
126146
126220
  const turnInput = await promptNumber(host, TextInputDialogComponent, {
126147
126221
  title: t("goal.wizard_title", { objective }),
126148
126222
  subtitle: t("goal.budget_turns_hint"),
@@ -128529,7 +128603,7 @@ const globGlance = (_toolCall, result, colors) => {
128529
128603
  const more = names.length - GLANCE_SAMPLES;
128530
128604
  return `${head}${shown}${more > 0 ? dim(` (+${String(more)})`) : ""}`;
128531
128605
  }).join(dim(" "));
128532
- const extLine = [...countExtensions(lines).entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_EXTENSION_COUNTS).map(([ext, count]) => `${dim(ext)}:${dim(` ${String(count)}`)}`).join(dim(", "));
128606
+ const extLine = [...countExtensions(lines).entries()].toSorted((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_EXTENSION_COUNTS).map(([ext, count]) => `${dim(ext)}:${dim(` ${String(count)}`)}`).join(dim(", "));
128533
128607
  if (extLine.length === 0) return dirLine;
128534
128608
  return `${dirLine}${dim(" ")}${extLine}`;
128535
128609
  };
@@ -128559,8 +128633,56 @@ const readGlance = (toolCall, result, colors) => {
128559
128633
  if (parts.length === 0) return "";
128560
128634
  return parts.join(dim(" · "));
128561
128635
  };
128636
+ /**
128637
+ * Parse the fixed WebSearch output protocol emitted by
128638
+ * `web-search.ts` (`Title: …` / `Date: …` / `URL: …` / `Snippet: …`,
128639
+ * entries separated by `---`) into structured entries so the collapsed
128640
+ * card can show a title/URL glance instead of a bare "N results" chip.
128641
+ */
128642
+ function parseWebSearchOutput(output) {
128643
+ const entries = [];
128644
+ let title;
128645
+ let url;
128646
+ let inSnippet = false;
128647
+ for (const line of output.split("\n")) {
128648
+ if (line.startsWith("---")) {
128649
+ if (title !== void 0 || url !== void 0) entries.push({
128650
+ title: title ?? "",
128651
+ url: url ?? ""
128652
+ });
128653
+ title = void 0;
128654
+ url = void 0;
128655
+ inSnippet = false;
128656
+ continue;
128657
+ }
128658
+ if (inSnippet) continue;
128659
+ if (line.startsWith("Snippet: ")) inSnippet = true;
128660
+ else if (line.startsWith("Title: ")) title = line.slice(7);
128661
+ else if (line.startsWith("URL: ")) url = line.slice(5);
128662
+ }
128663
+ if (title !== void 0 || url !== void 0) entries.push({
128664
+ title: title ?? "",
128665
+ url: url ?? ""
128666
+ });
128667
+ return entries;
128668
+ }
128669
+ function truncateText(text, max = 60) {
128670
+ return text.length <= max ? text : `${text.slice(0, max - 1)}…`;
128671
+ }
128672
+ const webSearchGlance = (_toolCall, result, colors) => {
128673
+ const entries = parseWebSearchOutput(result.output);
128674
+ if (entries.length === 0) return "";
128675
+ const titleColor = chalk.hex(colors.roleTool);
128676
+ const dim = chalk.dim;
128677
+ const lines = entries.slice(0, GLANCE_SAMPLES).map((e) => {
128678
+ return [e.title.length > 0 ? titleColor(truncateText(e.title)) : "", e.url.length > 0 ? dim(truncateText(e.url)) : ""].filter((s) => s.length > 0).join(dim(" — "));
128679
+ });
128680
+ const remaining = entries.length - GLANCE_SAMPLES;
128681
+ if (remaining > 0) lines.push(dim(`+${String(remaining)} more`));
128682
+ return lines.join("\n");
128683
+ };
128562
128684
  const fetchSummary = withGlance(null);
128563
- const webSearchSummary = withGlance(null);
128685
+ const webSearchSummary = withGlance(webSearchGlance);
128564
128686
  const thinkSummary = withGlance(null);
128565
128687
  const editSummary = withGlance(null);
128566
128688
  const writeSummary = withGlance(null);
@@ -128608,6 +128730,7 @@ const STREAMING_PROGRESS_INTERVAL_MS = 1e3;
128608
128730
  const SUBAGENT_ELAPSED_INTERVAL_MS = 1e3;
128609
128731
  const PROGRESS_URL_RE = /https?:\/\/\S+/g;
128610
128732
  const MAX_PROGRESS_LINE_CHARS = 1e4;
128733
+ const MAX_LIVE_OUTPUT_CHARS = 5e4;
128611
128734
  function backgroundFailureMessage(status) {
128612
128735
  switch (status) {
128613
128736
  case "lost": return t("toolcall.bg_agent_lost");
@@ -128926,6 +129049,10 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
128926
129049
  subagentEndedAtMs;
128927
129050
  progressLines = [];
128928
129051
  static MAX_PROGRESS_LINES = 24;
129052
+ /** Live stdout/stderr accumulated via appendLiveOutput while the tool runs. */
129053
+ liveOutput = "";
129054
+ /** Session permission mode at card creation; drives ExitPlanMode chip wording. */
129055
+ permissionMode;
128929
129056
  writeStreamContentStart = -1;
128930
129057
  writeStreamNlScanOffset = 0;
128931
129058
  writeStreamNlCount = 0;
@@ -128988,6 +129115,7 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
128988
129115
  setResult(result) {
128989
129116
  this.result = result;
128990
129117
  this.progressLines = [];
129118
+ this.liveOutput = "";
128991
129119
  this.finalizeSubagentElapsedIfNeeded();
128992
129120
  this.syncStreamingProgressTimer();
128993
129121
  this.syncSubagentElapsedTimer();
@@ -129018,6 +129146,31 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
129018
129146
  this.notifySnapshotChange();
129019
129147
  this.ui?.requestRender();
129020
129148
  }
129149
+ /**
129150
+ * Append live stdout/stderr from a running tool (Bash). Kept separate
129151
+ * from appendProgress so streaming command output renders with the same
129152
+ * tail-preview styling as the final result. The buffer is capped and
129153
+ * tail-preserving so a runaway command cannot grow the box unboundedly;
129154
+ * the block is dropped entirely once the real result lands.
129155
+ */
129156
+ appendLiveOutput(text) {
129157
+ if (this.result !== void 0 || text.length === 0) return;
129158
+ this.liveOutput += text;
129159
+ if (this.liveOutput.length > MAX_LIVE_OUTPUT_CHARS) this.liveOutput = `[...truncated]\n${this.liveOutput.slice(this.liveOutput.length - MAX_LIVE_OUTPUT_CHARS)}`;
129160
+ this.rebuildContent();
129161
+ this.notifySnapshotChange();
129162
+ this.ui?.requestRender();
129163
+ }
129164
+ /**
129165
+ * Records the session permission mode so ExitPlanMode can render an
129166
+ * honest "auto-approved" chip when the plan passed without user review.
129167
+ */
129168
+ setPermissionMode(mode) {
129169
+ if (this.permissionMode === mode) return;
129170
+ this.permissionMode = mode;
129171
+ this.headerText.setText(this.buildHeader());
129172
+ this.ui?.requestRender();
129173
+ }
129021
129174
  dispose() {
129022
129175
  if (this.disposed) return;
129023
129176
  this.disposed = true;
@@ -129433,6 +129586,7 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
129433
129586
  if (!isFinished || result === void 0 || result.is_error === true) return label;
129434
129587
  const outcome = interpretExitPlanModeOutcome(result.output);
129435
129588
  if (outcome.kind === "approved") {
129589
+ if (this.permissionMode === "auto" && outcome.chosen === void 0) return `${label}${chalk.hex(colors.warning)(` · ${t("toolcall.auto_approved")}`)}`;
129436
129590
  const chipText = outcome.chosen !== void 0 && outcome.chosen.length > 0 ? t("toolcall.approved", { chosen: outcome.chosen }) : t("toolcall.approved_label");
129437
129591
  return `${label}${chalk.hex(colors.success)(` · ${chipText}`)}`;
129438
129592
  }
@@ -129465,6 +129619,7 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
129465
129619
  this.markDirty();
129466
129620
  while (this.children.length > this.callPreviewEndIndex) this.children.pop();
129467
129621
  this.buildProgressBlock();
129622
+ this.buildLiveOutputBlock();
129468
129623
  this.buildContent();
129469
129624
  this.buildSubagentBlock();
129470
129625
  }
@@ -129474,6 +129629,7 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
129474
129629
  this.buildCallPreview();
129475
129630
  this.callPreviewEndIndex = this.children.length;
129476
129631
  this.buildProgressBlock();
129632
+ this.buildLiveOutputBlock();
129477
129633
  this.buildContent();
129478
129634
  this.buildSubagentBlock();
129479
129635
  }
@@ -129503,6 +129659,25 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
129503
129659
  this.addChild(new Text(styled, 2, 0));
129504
129660
  }
129505
129661
  }
129662
+ /**
129663
+ * Render live stdout/stderr while the tool is still running. Reuses the
129664
+ * shell result renderer so the streaming tail matches the final output's
129665
+ * preview styling (including ctrl+o expansion); the block is skipped once
129666
+ * the real result has landed.
129667
+ */
129668
+ buildLiveOutputBlock() {
129669
+ if (this.result !== void 0) return;
129670
+ if (this.liveOutput.length === 0) return;
129671
+ const components = shellExecutionResultRenderer(this.toolCall, {
129672
+ tool_call_id: this.toolCall.id,
129673
+ output: this.liveOutput,
129674
+ is_error: false
129675
+ }, {
129676
+ expanded: this.expanded,
129677
+ colors: this.colors
129678
+ });
129679
+ for (const component of components) this.addChild(component);
129680
+ }
129506
129681
  buildSubagentBlock() {
129507
129682
  if (this.subagentAgentId === void 0 && this.ongoingSubCalls.size === 0 && this.finishedSubCalls.length === 0 && this.subagentText.length === 0 && this.subagentPhase === void 0 && this.backgroundTaskTerminalPhase === void 0) return;
129508
129683
  if (this.isSingleSubagentView()) {
@@ -136525,12 +136700,32 @@ var SessionEventHandler = class {
136525
136700
  streamingUI.scheduleFlush();
136526
136701
  }
136527
136702
  handleToolProgress(event) {
136528
- if (event.update.kind !== "status") return;
136703
+ if (event.update.kind === "custom" && event.update.customKind === "background.task.terminated") {
136704
+ const data = event.update.customData;
136705
+ const id = typeof data?.id === "string" ? data.id : "";
136706
+ const command = typeof data?.command === "string" ? data.command : "";
136707
+ const exitCode = typeof data?.exitCode === "number" ? data.exitCode : -1;
136708
+ const preview = command.length > 60 ? `${command.slice(0, 59)}…` : command;
136709
+ if (exitCode === 0) this.host.showNotice(t("bash.background_completed", {
136710
+ id,
136711
+ command: preview
136712
+ }));
136713
+ else this.host.showNotice(t("bash.background_failed", {
136714
+ id,
136715
+ command: preview,
136716
+ exitCode: String(exitCode)
136717
+ }));
136718
+ return;
136719
+ }
136529
136720
  const text = event.update.text;
136530
136721
  if (text === void 0 || text.length === 0) return;
136531
136722
  const tc = this.host.streamingUI.getToolComponent(event.toolCallId);
136532
136723
  if (tc === void 0) return;
136533
- tc.appendProgress(text);
136724
+ if (event.update.kind === "status") {
136725
+ tc.appendProgress(text);
136726
+ return;
136727
+ }
136728
+ if (event.update.kind === "stdout" || event.update.kind === "stderr") tc.appendLiveOutput(text);
136534
136729
  }
136535
136730
  handleToolResult(event) {
136536
136731
  const { streamingUI } = this.host;
@@ -138146,6 +138341,7 @@ var StreamingUIController = class {
138146
138341
  }
138147
138342
  const { state } = this.host;
138148
138343
  const tc = new ToolCallComponent(toolCall, void 0, state.theme.colors, state.ui, state.theme.markdownTheme, state.appState.workDir);
138344
+ tc.setPermissionMode(state.appState.permissionMode);
138149
138345
  const entry = {
138150
138346
  id: nextTranscriptId(),
138151
138347
  kind: "tool_call",
@@ -138231,6 +138427,7 @@ var StreamingUIController = class {
138231
138427
  }
138232
138428
  if (matchedCall?.name === "AskUserQuestion") {
138233
138429
  const completed = new ToolCallComponent(matchedCall, result, state.theme.colors, state.ui, state.theme.markdownTheme, state.appState.workDir);
138430
+ completed.setPermissionMode(state.appState.permissionMode);
138234
138431
  if (state.toolOutputExpanded) completed.setExpanded(true);
138235
138432
  if (state.planExpanded) completed.setPlanExpanded(true);
138236
138433
  const entry = {
@@ -145768,14 +145965,20 @@ function supportsAnsi() {
145768
145965
  ansiSupported = false;
145769
145966
  return false;
145770
145967
  }
145771
- function runLoadingAnimation(theme = "dark") {
145968
+ async function runLoadingAnimation(theme = "dark") {
145772
145969
  if (!supportsAnsi()) {
145773
145970
  const { cols } = getTerminalSize();
145774
145971
  if (cols >= FULL_LOGO_MIN_COLS) for (const line of LOGO) stdout.write(`${fg(...LOGO_RGB)}${line}${RESET}\n`);
145775
145972
  else for (const line of COMPACT_LOGO) stdout.write(`${fg(...BLOCK_RGB)}${line}${RESET}\n`);
145776
145973
  stdout.write(`${BOLD}${fg(...THEME_PRIMARY[theme])}${t("loading.waking")}${RESET}\n`);
145777
- return Promise.resolve();
145974
+ return;
145778
145975
  }
145976
+ let autoStart = false;
145977
+ try {
145978
+ autoStart = (await loadTuiConfig()).autoStart;
145979
+ } catch {}
145980
+ const autoStartAtBoot = autoStart;
145981
+ let toggled = false;
145779
145982
  return new Promise((resolve) => {
145780
145983
  if (process$1.platform !== "win32") stdout.write("\x1B[?1049h");
145781
145984
  stdout.write("\x1B[2J");
@@ -145809,7 +146012,9 @@ function runLoadingAnimation(theme = "dark") {
145809
146012
  else lines.push(centerPad(`${BOLD}${fg(...breatheColor)}${t("loading.press_enter")}${RESET}`, cols));
145810
146013
  lines.push("");
145811
146014
  lines.push("");
145812
- lines.push(centerPad(`${fg(...DIM_RGB)}${t("loading.quit_hint")}${RESET}`, cols));
146015
+ const rightText = toggled ? autoStart ? t("loading.auto_start_on") : t("loading.auto_start_off") : t("loading.auto_start_hint");
146016
+ const hint = `${t("loading.quit_hint")} ${rightText}`;
146017
+ lines.push(centerPad(`${fg(...DIM_RGB)}${hint}${RESET}`, cols));
145813
146018
  while (lines.length < rows) lines.push("");
145814
146019
  stdout.write("\x1B[H");
145815
146020
  stdout.write(lines.join("\n"));
@@ -145830,6 +146035,20 @@ function runLoadingAnimation(theme = "dark") {
145830
146035
  interrupt();
145831
146036
  return;
145832
146037
  }
146038
+ if (key === "") {
146039
+ toggled = true;
146040
+ autoStart = !autoStart;
146041
+ (async () => {
146042
+ try {
146043
+ await saveTuiConfig({
146044
+ ...await loadTuiConfig(),
146045
+ autoStart
146046
+ });
146047
+ } catch {}
146048
+ })();
146049
+ render();
146050
+ return;
146051
+ }
145833
146052
  if ((key === "\r" || key === "\n") && phase === "ready") {
145834
146053
  cleanup();
145835
146054
  resolve();
@@ -145875,6 +146094,10 @@ function runLoadingAnimation(theme = "dark") {
145875
146094
  }).then(() => {
145876
146095
  phase = "ready";
145877
146096
  render();
146097
+ if (autoStartAtBoot) setTimeout(() => {
146098
+ cleanup();
146099
+ resolve();
146100
+ }, 350);
145878
146101
  });
145879
146102
  });
145880
146103
  }
package/dist/main.mjs CHANGED
@@ -6,7 +6,7 @@ const __dirname = __cjsShimDirname(__filename);
6
6
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
7
7
  //#region src/main.ts
8
8
  try {
9
- (await import("./app-D7u8KJ3h.mjs")).main();
9
+ (await import("./app-D95_TREY.mjs")).main();
10
10
  } catch (error) {
11
11
  process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
12
12
  process.exit(1);
@@ -221,6 +221,9 @@ const dictionaries = {
221
221
  "loading.waking": "正在唤醒核心...",
222
222
  "loading.press_enter": "按下 ENTER 唤醒核心",
223
223
  "loading.quit_hint": "按住 Ctrl+C 即可退出 Scream Code",
224
+ "loading.auto_start_hint": "Ctrl+E 即可切换一键启动",
225
+ "loading.auto_start_on": "Ctrl+E 即可切换一键启动(默认启动开)",
226
+ "loading.auto_start_off": "Ctrl+E 即可切换一键启动(默认启动关)",
224
227
  "language.picker_title": "语言 / Language",
225
228
  "language.picker_hint": "↑↓ 选择 · Enter 确认 · Esc 取消",
226
229
  "language.unchanged": "语言未更改:\"{locale}\"。",
@@ -593,6 +596,7 @@ const dictionaries = {
593
596
  "toolcall.current_plan": "当前计划",
594
597
  "toolcall.approved": "已批准:{chosen}",
595
598
  "toolcall.approved_label": "已批准",
599
+ "toolcall.auto_approved": "自动批准",
596
600
  "toolcall.input_unavailable": "无法收集你的输入",
597
601
  "toolcall.input_collected": "已收集你的答案",
598
602
  "toolcall.waiting_input": "等待你的输入",
@@ -803,6 +807,8 @@ const dictionaries = {
803
807
  "export.session_title": "# Scream 会话导出",
804
808
  "bgtask.agent_task": "代理任务",
805
809
  "bgtask.bash_task": "bash 任务",
810
+ "bash.background_completed": "后台任务 {id} 已完成:{command}",
811
+ "bash.background_failed": "后台任务 {id} 执行失败(退出码 {exitCode}):{command}",
806
812
  "bgtask.started_bg": "{subject} 已在后台启动",
807
813
  "bgtask.awaiting_approval": "{subject} 等待审批",
808
814
  "bgtask.completed_bg": "{subject} 已在后台完成",
@@ -1252,6 +1258,9 @@ const dictionaries = {
1252
1258
  "loading.waking": "Waking core...",
1253
1259
  "loading.press_enter": "Press ENTER to wake core",
1254
1260
  "loading.quit_hint": "Hold Ctrl+C to quit Scream Code",
1261
+ "loading.auto_start_hint": "Ctrl+E to toggle auto-start",
1262
+ "loading.auto_start_on": "Ctrl+E to toggle auto-start (ON)",
1263
+ "loading.auto_start_off": "Ctrl+E to toggle auto-start (OFF)",
1255
1264
  "language.picker_title": "Language / 语言",
1256
1265
  "language.picker_hint": "↑↓ Select · Enter confirm · Esc cancel",
1257
1266
  "language.unchanged": "Language unchanged: \"{locale}\".",
@@ -1624,6 +1633,7 @@ const dictionaries = {
1624
1633
  "toolcall.current_plan": "Current Plan",
1625
1634
  "toolcall.approved": "Approved: {chosen}",
1626
1635
  "toolcall.approved_label": "Approved",
1636
+ "toolcall.auto_approved": "Auto-approved",
1627
1637
  "toolcall.input_unavailable": "Could not collect your input",
1628
1638
  "toolcall.input_collected": "Collected your answer",
1629
1639
  "toolcall.waiting_input": "Waiting for your input",
@@ -1834,6 +1844,8 @@ const dictionaries = {
1834
1844
  "export.session_title": "# Scream Session Export",
1835
1845
  "bgtask.agent_task": "Agent task",
1836
1846
  "bgtask.bash_task": "Bash task",
1847
+ "bash.background_completed": "Background task {id} completed: {command}",
1848
+ "bash.background_failed": "Background task {id} failed (exit {exitCode}): {command}",
1837
1849
  "bgtask.started_bg": "{subject} started in background",
1838
1850
  "bgtask.awaiting_approval": "{subject} awaiting approval",
1839
1851
  "bgtask.completed_bg": "{subject} completed in background",
@@ -3,5 +3,5 @@ import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
3
3
  import { dirname as __cjsShimDirname } from 'node:path';
4
4
  const __filename = __cjsShimFileURLToPath(import.meta.url);
5
5
  const __dirname = __cjsShimDirname(__filename);
6
- import { t as TextInputDialogComponent } from "./text-input-dialog-K1FbYyug.mjs";
6
+ import { t as TextInputDialogComponent } from "./text-input-dialog-BdUmo73M.mjs";
7
7
  export { TextInputDialogComponent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.11.6",
3
+ "version": "0.11.7",
4
4
  "description": "A terminal-native AI agent for builders",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",