scream-code 0.11.5 → 0.11.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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-DRUaPOEQ.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";
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";
@@ -74692,6 +74692,62 @@ function truncateUtf8$1(input, maxBytes) {
74692
74692
  //#region ../../packages/agent-core/src/tools/builtin/shell/bash.md
74693
74693
  var bash_default = "Execute a `{{ SHELL_NAME }}` command. Use this for shell semantics — pipes, env, processes, git, package managers, build/test runners, anything genuinely interactive or multi-step.\n\n**Translate these to a dedicated tool instead:**\n- `cat` / `head` / `tail` (known path) → `Read`\n- `sed` / `awk` (in-place edit) → `Edit`\n- `echo > file` / `cat <<EOF` → `Write`\n- `find` / recursive `ls` to locate files by name pattern → `Glob` (plain `ls <known-directory>` is fine for listing a directory)\n- `grep` / `rg` (search file contents) → `Grep`\n- `echo` / `printf` (talk to the user) → just output text directly\n\nThe dedicated tools render in the per-tool permission UI and keep raw stdout out of the conversation; that is why they are worth reaching for whenever one fits.\n\n**Output:**\nThe stdout and stderr will be combined and returned as a string. The output may be truncated if it is too long. If the command failed, the output will end with a `Command failed with exit code: N` line stating the non-zero exit code.\n\nIf `run_in_background=true`, the command will be started as a background task and this tool will return a task ID instead of waiting for command completion. When doing that, you must provide a short `description`. Background commands default to a {{ DEFAULT_BACKGROUND_TIMEOUT_S }}s timeout and `timeout` is capped at {{ MAX_BACKGROUND_TIMEOUT_S }}s; set `disable_timeout=true` only when the task should run without a timeout. You will be automatically notified when the task completes. Use `TaskOutput` for a non-blocking status/output snapshot, and only set `block=true` when you explicitly want to wait for completion. Use `TaskStop` only if the task must be cancelled. If a human user wants to inspect background tasks themselves, point them to the `/tasks` command, which opens an interactive panel; it has no subcommands.\n\n**Guidelines for safety and security:**\n- Each shell tool call will be executed in a fresh shell environment. The shell variables, current working directory changes, and the shell history is not preserved between calls.\n- The tool call will return after the command is finished. You shall not use this tool to execute an interactive command or a command that may run forever. For possibly long-running foreground commands, set the `timeout` argument in seconds. Foreground commands default to {{ DEFAULT_TIMEOUT_S }}s and allow up to {{ MAX_TIMEOUT_S }}s.\n- Avoid using `..` to access files or directories outside of the working directory.\n- Avoid modifying files outside of the working directory unless explicitly instructed to do so.\n- Never run commands that require superuser privileges unless explicitly instructed to do so.\n\n**Guidelines for efficiency:**\n- For multiple related commands, use `&&` to chain them in a single call, e.g. `cd /path && ls -la`\n- Use `;` to run commands sequentially regardless of success/failure\n- Use `||` for conditional execution (run second command only if first fails)\n- Use pipe operations (`|`) and redirections (`>`, `>>`) to chain input and output between commands\n- Always quote file paths containing spaces with double quotes (e.g., cd \"/path with spaces/\")\n- Compose multi-step logic in a single call with `if` / `case` / `for` / `while` control flows.\n- Prefer `run_in_background=true` for long-running builds, tests, watchers, or servers when you need the conversation to continue before the command finishes.\n\n**Commands available:**\nThe following common command categories are usually available. Availability still depends on the host, so when in doubt run `which <command>` first to confirm a command exists before relying on it.\n- Navigation and inspection: `ls`, `pwd`, `cd`, `stat`, `file`, `du`, `df`, `tree`\n- File and directory management: `cp`, `mv`, `rm`, `mkdir`, `touch`, `ln`, `chmod`, `chown`\n- Text and data processing: `wc`, `sort`, `uniq`, `cut`, `tr`, `diff`, `xargs`\n- Archives and compression: `tar`, `gzip`, `gunzip`, `zip`, `unzip`\n- Networking and transfer: `curl`, `wget`, `ping`, `ssh`, `scp`\n- Version control: `git`\n- Process and system: `ps`, `kill`, `top`, `env`, `date`, `uname`, `whoami`\n- Language and package toolchains: `node`, `npm`, `pnpm`, `yarn`, `python`, `pip` (use whichever the project actually relies on)\n";
74694
74694
  //#endregion
74695
+ //#region ../../packages/agent-core/src/tools/builtin/shell/background-tasks.ts
74696
+ /**
74697
+ * Lightweight store for foreground Bash commands that were moved to
74698
+ * background on timeout. The process continues running; when it exits
74699
+ * the result is stored here and surfaced to the model on the next Bash
74700
+ * call.
74701
+ *
74702
+ * This is intentionally simple - it does NOT use the full
74703
+ * BackgroundProcessManager (which assumes it owns the process streams
74704
+ * from spawn). Here the streams are already being read by the
74705
+ * foreground ToolResultBuilder, so we just wrap the pending
74706
+ * completion promise.
74707
+ */
74708
+ const pendingTasks = /* @__PURE__ */ new Map();
74709
+ const MAX_PENDING = 10;
74710
+ function createBackgroundTask(command, completion) {
74711
+ const id = randomUUID().slice(0, 8);
74712
+ const task = {
74713
+ command,
74714
+ startedAt: Date.now(),
74715
+ completion
74716
+ };
74717
+ if (pendingTasks.size >= MAX_PENDING) {
74718
+ const oldest = pendingTasks.keys().next().value;
74719
+ if (oldest !== void 0) pendingTasks.delete(oldest);
74720
+ }
74721
+ pendingTasks.set(id, task);
74722
+ task.completion.then((result) => {
74723
+ task.result = result;
74724
+ }, () => {
74725
+ task.result = {
74726
+ exitCode: -1,
74727
+ output: "Background process failed."
74728
+ };
74729
+ });
74730
+ return id;
74731
+ }
74732
+ /**
74733
+ * Collect results from background tasks that have completed since the
74734
+ * last check. Each completed task is returned once and then removed.
74735
+ */
74736
+ function drainCompletedBackgroundTasks() {
74737
+ const completed = [];
74738
+ for (const [id, task] of pendingTasks) if (task.result !== void 0) {
74739
+ completed.push({
74740
+ id,
74741
+ command: task.command,
74742
+ exitCode: task.result.exitCode,
74743
+ output: task.result.output,
74744
+ elapsedMs: Date.now() - task.startedAt
74745
+ });
74746
+ pendingTasks.delete(id);
74747
+ }
74748
+ return completed;
74749
+ }
74750
+ //#endregion
74695
74751
  //#region ../../packages/agent-core/src/tools/builtin/shell/bash.ts
74696
74752
  /**
74697
74753
  * BashTool — execute shell commands.
@@ -74899,13 +74955,15 @@ var BashTool = class {
74899
74955
  return this.jian.execWithEnv(shellArgs, mergedEnv);
74900
74956
  }
74901
74957
  async execution(args, signal) {
74958
+ const completedBg = drainCompletedBackgroundTasks();
74959
+ 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("") : "";
74902
74960
  if (signal.aborted) return {
74903
74961
  isError: true,
74904
- output: "Aborted before command started"
74962
+ output: bgPrefix + "Aborted before command started"
74905
74963
  };
74906
74964
  if (args.command.length === 0) return {
74907
74965
  isError: true,
74908
- output: "Command cannot be empty."
74966
+ output: bgPrefix + "Command cannot be empty."
74909
74967
  };
74910
74968
  const validationError = validateCommand(args.command, this.isWindowsBash);
74911
74969
  if (validationError !== null) return validationError;
@@ -74933,7 +74991,6 @@ var BashTool = class {
74933
74991
  try {
74934
74992
  proc.stdin.end();
74935
74993
  } catch {}
74936
- let timedOut = false;
74937
74994
  let aborted = false;
74938
74995
  let killed = false;
74939
74996
  const killProc = async () => {
@@ -74962,21 +75019,35 @@ var BashTool = class {
74962
75019
  killProc();
74963
75020
  };
74964
75021
  signal.addEventListener("abort", onAbort);
74965
- const timeoutHandle = setTimeout(() => {
74966
- timedOut = true;
74967
- killProc();
74968
- }, timeoutMs);
75022
+ let timeoutHandle;
75023
+ const timeoutPromise = new Promise((resolve) => {
75024
+ if (timeoutMs !== void 0) timeoutHandle = setTimeout(() => resolve({ timedOut: true }), timeoutMs);
75025
+ });
74969
75026
  try {
74970
75027
  const builder = new ToolResultBuilder({ artifactSink: async (fullOutput) => {
74971
75028
  const artifactPath = join(tmpdir(), `scream-bash-output-${randomUUID()}.log`);
74972
75029
  await writeFile(artifactPath, fullOutput, "utf8");
74973
75030
  return artifactPath;
74974
75031
  } });
74975
- const [, exitCode] = await Promise.all([Promise.all([readStreamIntoBuilder(proc.stdout, builder), readStreamIntoBuilder(proc.stderr, builder)]), proc.wait()]);
74976
- if (timedOut) {
75032
+ 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]) => ({
75034
+ timedOut: false,
75035
+ exitCode
75036
+ }));
75037
+ const raceResult = timeoutMs !== void 0 ? await Promise.race([completionPromise, timeoutPromise]) : await completionPromise;
75038
+ if (raceResult.timedOut) {
74977
75039
  const timeoutLabel = timeoutMs % 1e3 === 0 ? `${String(timeoutMs / 1e3)}s` : `${String(timeoutMs)}ms`;
74978
- return builder.error(`Command killed by timeout (${timeoutLabel})`, { brief: `Killed by timeout (${timeoutLabel})` });
75040
+ const taskId = createBackgroundTask(command, completionPromise.then(({ exitCode }) => ({
75041
+ exitCode,
75042
+ output: builder.toString()
75043
+ })));
75044
+ const outputSoFar = builder.toString();
75045
+ return {
75046
+ 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.`,
75047
+ isError: false
75048
+ };
74979
75049
  }
75050
+ const { exitCode } = raceResult;
74980
75051
  if (aborted) return builder.error("Interrupted by user", { brief: "Interrupted by user" });
74981
75052
  const isError = exitCode !== 0;
74982
75053
  if (isError && builder.nChars === 0) builder.write(`Process exited with code ${String(exitCode)}`);
@@ -74989,7 +75060,7 @@ var BashTool = class {
74989
75060
  output: error instanceof Error ? error.message : String(error)
74990
75061
  };
74991
75062
  } finally {
74992
- clearTimeout(timeoutHandle);
75063
+ if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
74993
75064
  signal.removeEventListener("abort", onAbort);
74994
75065
  }
74995
75066
  }
@@ -120531,7 +120602,7 @@ function optionalBuildString(value) {
120531
120602
  return typeof value === "string" && value.length > 0 ? value : void 0;
120532
120603
  }
120533
120604
  const SCREAM_BUILD_INFO = {
120534
- version: optionalBuildString("0.11.5"),
120605
+ version: optionalBuildString("0.11.6"),
120535
120606
  channel: optionalBuildString(""),
120536
120607
  commit: optionalBuildString(""),
120537
120608
  buildTarget: optionalBuildString("darwin-arm64")
@@ -126050,7 +126121,7 @@ async function guidedGoalSetup(host) {
126050
126121
  host.showNotice(t("goal.storm_breaker"), t("goal.conflict_loop"));
126051
126122
  return;
126052
126123
  }
126053
- const { TextInputDialogComponent } = await import("./text-input-dialog-B1ak519Y.mjs");
126124
+ const { TextInputDialogComponent } = await import("./text-input-dialog-Crp_BPkd.mjs");
126054
126125
  const initialDesc = await promptText(host, TextInputDialogComponent, {
126055
126126
  title: t("goal.setup_title_initial"),
126056
126127
  subtitle: t("goal.setup_desc_hint"),
@@ -126071,7 +126142,7 @@ async function guidedGoalSetup(host) {
126071
126142
  await showGoalConfigWizard(host, session, confirmed.trim() || objective, false);
126072
126143
  }
126073
126144
  async function showGoalConfigWizard(host, session, objective, replace) {
126074
- const { TextInputDialogComponent } = await import("./text-input-dialog-B1ak519Y.mjs");
126145
+ const { TextInputDialogComponent } = await import("./text-input-dialog-Crp_BPkd.mjs");
126075
126146
  const turnInput = await promptNumber(host, TextInputDialogComponent, {
126076
126147
  title: t("goal.wizard_title", { objective }),
126077
126148
  subtitle: t("goal.budget_turns_hint"),
@@ -129351,7 +129422,10 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
129351
129422
  const isError = result?.is_error ?? false;
129352
129423
  const isTruncated = toolCall.truncated === true && !isFinished;
129353
129424
  let bullet;
129354
- if (isFinished) bullet = isError ? chalk.hex(colors.error)("✗ ") : chalk.hex(colors.success)(STATUS_BULLET);
129425
+ if (isFinished) if (isError) {
129426
+ const output = typeof result?.output === "string" ? result.output : "";
129427
+ bullet = /\b(?:aborted|cancelled|canceled)\b/i.test(output) ? chalk.hex(colors.warning)("⊙ ") : chalk.hex(colors.error)("✗ ");
129428
+ } else bullet = chalk.hex(colors.success)(STATUS_BULLET);
129355
129429
  else if (isTruncated) bullet = chalk.hex(colors.error)("✗ ");
129356
129430
  else bullet = chalk.hex(colors.roleAssistant)(STATUS_BULLET);
129357
129431
  if (toolCall.name === "ExitPlanMode") {
@@ -130845,17 +130919,6 @@ async function handleUpdateCommand(host) {
130845
130919
  * 查看已安装的 MCP 服务器状态,一键安装推荐服务器。 */
130846
130920
  function getRecommended() {
130847
130921
  return [{
130848
- name: "peekaboo",
130849
- displayName: "Peekaboo",
130850
- description: t("mcp.desktop_desc"),
130851
- command: "npx",
130852
- args: [
130853
- "-y",
130854
- "@steipete/peekaboo",
130855
- "mcp"
130856
- ],
130857
- macOnly: true
130858
- }, {
130859
130922
  name: "chrome-devtools",
130860
130923
  displayName: "Chrome DevTools",
130861
130924
  description: t("mcp.browser_desc"),
@@ -130865,6 +130928,14 @@ function getRecommended() {
130865
130928
  "chrome-devtools-mcp@latest",
130866
130929
  "--no-usage-statistics"
130867
130930
  ]
130931
+ }, {
130932
+ name: "scream-life",
130933
+ displayName: "ScreamLife",
130934
+ description: "Decision memory system - captures and analyzes your decisions during conversations",
130935
+ command: "bun",
130936
+ args: ["{INSTALL_DIR}/Core/mcp-server.ts"],
130937
+ env: { SCREAM_LIFE_DB_PATH: "{INSTALL_DIR}/Data/scream-life.db" },
130938
+ gitUrl: "https://github.com/LIUTod/scream-life.git"
130868
130939
  }];
130869
130940
  }
130870
130941
  function getStatusLabels() {
@@ -131039,12 +131110,37 @@ async function installMcp(host, rec) {
131039
131110
  const spinner = host.showProgressSpinner(`${t("mcp.installing")} ${rec.displayName}...`);
131040
131111
  spinner.setLabel(`${t("mcp.configuring")} ${rec.displayName}...`);
131041
131112
  try {
131042
- await writeMcpConfig(host, rec.name, rec.command, rec.args);
131043
- await session.addMcpServer(rec.name, {
131113
+ let resolvedArgs = rec.args;
131114
+ let resolvedEnv = rec.env;
131115
+ if (rec.gitUrl !== void 0) {
131116
+ const installDir = join(getDataDir(), "mcp", rec.name);
131117
+ if (existsSync(join(installDir, ".git"))) {
131118
+ spinner.setLabel(`Updating ${rec.displayName}...`);
131119
+ execSync(`git pull --ff-only`, {
131120
+ cwd: installDir,
131121
+ stdio: "pipe",
131122
+ timeout: 3e4
131123
+ });
131124
+ } else {
131125
+ spinner.setLabel(`Cloning ${rec.displayName}...`);
131126
+ await mkdir(installDir, { recursive: true });
131127
+ execSync(`git clone --depth 1 ${rec.gitUrl} "${installDir}"`, {
131128
+ stdio: "pipe",
131129
+ timeout: 6e4
131130
+ });
131131
+ }
131132
+ await mkdir(join(installDir, "Data"), { recursive: true });
131133
+ resolvedArgs = rec.args.map((a) => a.replaceAll("{INSTALL_DIR}", installDir));
131134
+ if (resolvedEnv !== void 0) resolvedEnv = Object.fromEntries(Object.entries(resolvedEnv).map(([k, v]) => [k, v.replaceAll("{INSTALL_DIR}", installDir)]));
131135
+ }
131136
+ await writeMcpConfig(host, rec.name, rec.command, resolvedArgs, resolvedEnv);
131137
+ const serverConfig = {
131044
131138
  transport: "stdio",
131045
131139
  command: rec.command,
131046
- args: rec.args
131047
- });
131140
+ args: resolvedArgs
131141
+ };
131142
+ if (resolvedEnv !== void 0 && Object.keys(resolvedEnv).length > 0) serverConfig.env = resolvedEnv;
131143
+ await session.addMcpServer(rec.name, serverConfig);
131048
131144
  spinner.stop({
131049
131145
  ok: true,
131050
131146
  label: t("mcp.install_success", { name: rec.displayName })
@@ -131090,7 +131186,7 @@ async function uninstallMcp(host, name) {
131090
131186
  host.showError(t("mcp.uninstall_failed", { msg: error instanceof Error ? error.message : String(error) }));
131091
131187
  }
131092
131188
  }
131093
- async function writeMcpConfig(host, name, command, args) {
131189
+ async function writeMcpConfig(host, name, command, args, env) {
131094
131190
  const configPath = join(getDataDir(), "mcp.json");
131095
131191
  let data = {};
131096
131192
  try {
@@ -131098,12 +131194,14 @@ async function writeMcpConfig(host, name, command, args) {
131098
131194
  data = JSON.parse(text);
131099
131195
  } catch {}
131100
131196
  const servers = data["mcpServers"] ?? {};
131101
- servers[name] = {
131197
+ const config = {
131102
131198
  transport: "stdio",
131103
131199
  command,
131104
131200
  args,
131105
131201
  startupTimeoutMs: 3e5
131106
131202
  };
131203
+ if (env !== void 0 && Object.keys(env).length > 0) config["env"] = env;
131204
+ servers[name] = config;
131107
131205
  data["mcpServers"] = servers;
131108
131206
  await mkdir(dirname$1(configPath), { recursive: true });
131109
131207
  await writeFile(configPath, JSON.stringify(data, null, 2), "utf-8");
@@ -143318,6 +143416,10 @@ const DEFAULT_KEYBOARD_SHORTCUTS = [
143318
143416
  keys: "Shift-Tab",
143319
143417
  description: t("help.toggle_plan")
143320
143418
  },
143419
+ {
143420
+ keys: "Ctrl-G",
143421
+ description: t("help.external_editor")
143422
+ },
143321
143423
  {
143322
143424
  keys: "Ctrl-O",
143323
143425
  description: t("help.toggle_output")
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-GFawIzQ9.mjs")).main();
9
+ (await import("./app-D7u8KJ3h.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);
@@ -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-DRUaPOEQ.mjs";
6
+ import { t as TextInputDialogComponent } from "./text-input-dialog-K1FbYyug.mjs";
7
7
  export { TextInputDialogComponent };
@@ -489,6 +489,7 @@ const dictionaries = {
489
489
  "help.toggle_output": "切换工具输出展开",
490
490
  "help.interrupt": "中途干预 — 在流式传输中插入后续提示",
491
491
  "help.newline": "插入换行",
492
+ "help.external_editor": "在外部编辑器中编辑 ($VISUAL / $EDITOR)",
492
493
  "help.cancel_stream": "中断流 / 清空输入",
493
494
  "help.exit": "退出(空输入时)",
494
495
  "help.close_dialog": "关闭对话框 / 中断流",
@@ -1519,6 +1520,7 @@ const dictionaries = {
1519
1520
  "help.toggle_output": "Toggle tool output expansion",
1520
1521
  "help.interrupt": "Interrupt — insert follow-up prompt during streaming",
1521
1522
  "help.newline": "Insert newline",
1523
+ "help.external_editor": "Edit in external editor ($VISUAL / $EDITOR)",
1522
1524
  "help.cancel_stream": "Cancel stream / Clear input",
1523
1525
  "help.exit": "Exit (when input is empty)",
1524
1526
  "help.close_dialog": "Close dialog / Cancel stream",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.11.5",
3
+ "version": "0.11.6",
4
4
  "description": "A terminal-native AI agent for builders",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",