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.
@@ -4,9 +4,9 @@ import { dirname as __cjsShimDirname } from 'node:path';
4
4
  const __filename = __cjsShimFileURLToPath(import.meta.url);
5
5
  const __dirname = __cjsShimDirname(__filename);
6
6
  import { i as __require, o as __toESM, r as __exportAll, t as __commonJSMin } from "./chunk-D90kvbyJ.mjs";
7
- import { A as sharedMemoStore, B as buildProjectTagCloud, C as applyConsolidation, D as parseMemoryMemos, E as buildExitExtractionPrompt, F as probeLocalEmbeddingSupport, G as join$1, H as basename$1, I as createMemoryMemo, J as relative$1, K as normalize, L as toSummary, M as clearEmbeddingModelCache, N as createFastEmbedEngine, O as stripMemoryTags, P as hasEmbeddingModelCache$1, R as generateTags, S as DreamTracker, T as EXIT_EXTRACTION_SYSTEM_PROMPT, U as dirname$2, V as rankMemos, W as isAbsolute$1, Y as resolve$2, a as isSupportedFile, b as sharedKnowledgeStoreReady, i as ingestFile, j as classifyEmbeddingFailure, k as MemoryMemoStore, q as parse$7, r as ingestDirectory, t as multiSearch, w as buildConsolidationPlan, y as sharedKnowledgeStore, z as normalizeTags } from "./src-CQwXASmd.mjs";
7
+ import { A as sharedMemoStore, B as buildProjectTagCloud, C as applyConsolidation, D as parseMemoryMemos, E as buildExitExtractionPrompt, F as probeLocalEmbeddingSupport, G as join$1, H as basename$1, I as createMemoryMemo, J as relative$1, K as normalize, L as toSummary, M as clearEmbeddingModelCache, N as createEmbeddingEngine, O as stripMemoryTags, P as hasEmbeddingModelCache$1, R as generateTags, S as DreamTracker, T as EXIT_EXTRACTION_SYSTEM_PROMPT, U as dirname$2, V as rankMemos, W as isAbsolute$1, Y as resolve$2, a as isSupportedFile, b as sharedKnowledgeStoreReady, i as ingestFile, j as classifyEmbeddingFailure, k as MemoryMemoStore, q as parse$7, r as ingestDirectory, t as multiSearch, w as buildConsolidationPlan, y as sharedKnowledgeStore, z as normalizeTags } from "./src-ClB_Hzp9.mjs";
8
8
  import { n as require_base64_js, t as XMLParser } from "./XMLParser-DFAnGqkc.mjs";
9
- import { a as assertScreamHostIdentity, i as t, n as getLocale, o as createScreamDefaultHeaders, r as setLocale, t as TextInputDialogComponent } from "./text-input-dialog-gDr3i7xz.mjs";
9
+ import { a as assertScreamHostIdentity, i as t, n as getLocale, o as createScreamDefaultHeaders, r as setLocale, t as TextInputDialogComponent } from "./text-input-dialog-DHDsHCqR.mjs";
10
10
  import { createRequire } from "node:module";
11
11
  import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
12
12
  import * as fs$1 from "node:fs/promises";
@@ -62231,7 +62231,7 @@ var KnowledgeLookupTool = class {
62231
62231
  const llm = { generate: async (systemPrompt, userPrompt) => {
62232
62232
  return this.agent.generateText(systemPrompt, userPrompt);
62233
62233
  } };
62234
- const { multiSearchWithTrace } = await import("./src-ChTWl9Cn.mjs");
62234
+ const { multiSearchWithTrace } = await import("./src-DA34cXSv.mjs");
62235
62235
  const { results, trace } = await multiSearchWithTrace(store, llm, query, {
62236
62236
  topK,
62237
62237
  skipLlm: args.deep !== true
@@ -70622,6 +70622,37 @@ var FsScanCache = class {
70622
70622
  */
70623
70623
  const scanCache = new FsScanCache();
70624
70624
  //#endregion
70625
+ //#region ../../packages/agent-core/src/tools/builtin/file/mutation-queue.ts
70626
+ /**
70627
+ * Per-path serialization for file mutations.
70628
+ *
70629
+ * Within one turn a tool batch runs concurrently (`runToolCallBatch` settles
70630
+ * it with `Promise.allSettled`), and sub-agents share this process. Two writes
70631
+ * to the same file can therefore interleave their read→modify→write steps and
70632
+ * lose an update — most visibly two edits whose `old_string` windows overlap.
70633
+ * Mutations against the same resolved path run one after another; different
70634
+ * paths still run in parallel, so throughput is unchanged in the common case.
70635
+ *
70636
+ * Scope: structured write tools only (`Write`, `Edit`). Shell commands that
70637
+ * write files themselves (redirection, scripts) are outside the reach of an
70638
+ * in-process queue, exactly like every other file-tool guarantee.
70639
+ *
70640
+ * Keys are `path.resolve`d so relative and absolute spellings of the same
70641
+ * target share one chain. Symlinked aliases of one target are not merged; the
70642
+ * tools resolve access paths before calling this anyway.
70643
+ */
70644
+ const tails = /* @__PURE__ */ new Map();
70645
+ function withFileMutationLock(filePath, task) {
70646
+ const key = path$8.resolve(filePath);
70647
+ const result = (tails.get(key) ?? Promise.resolve()).then(task, task);
70648
+ const tail = result.then(() => void 0, () => void 0);
70649
+ tails.set(key, tail);
70650
+ tail.then(() => {
70651
+ if (tails.get(key) === tail) tails.delete(key);
70652
+ });
70653
+ return result;
70654
+ }
70655
+ //#endregion
70625
70656
  //#region ../../packages/agent-core/src/tools/builtin/file/conflict-detect.ts
70626
70657
  /**
70627
70658
  * Detect unresolved git merge conflict markers in read output.
@@ -70953,7 +70984,7 @@ var EditTool = class {
70953
70984
  pathClass: this.jian.pathClass(),
70954
70985
  homeDir: this.jian.gethome()
70955
70986
  }),
70956
- execute: ({ signal }) => this.execution(args, path, signal)
70987
+ execute: ({ signal }) => withFileMutationLock(path, () => this.execution(args, path, signal))
70957
70988
  };
70958
70989
  }
70959
70990
  async execution(args, safePath, signal) {
@@ -76914,7 +76945,7 @@ var WriteTool = class {
76914
76945
  pathClass: this.jian.pathClass(),
76915
76946
  homeDir: this.jian.gethome()
76916
76947
  }),
76917
- execute: ({ signal }) => this.execution(args, path, signal)
76948
+ execute: ({ signal }) => withFileMutationLock(path, () => this.execution(args, path, signal))
76918
76949
  };
76919
76950
  }
76920
76951
  async execution(args, safePath, signal) {
@@ -107549,7 +107580,7 @@ var Agent = class {
107549
107580
  this.eventBus = new EventSubscriptionBus();
107550
107581
  const embedCacheDir = options.screamHomeDir !== void 0 ? join$1(options.screamHomeDir, "cache", "fastembed") : void 0;
107551
107582
  this.embeddingCacheDir = embedCacheDir;
107552
- this.sharedEmbeddingEngine = createFastEmbedEngine(embedCacheDir);
107583
+ this.sharedEmbeddingEngine = createEmbeddingEngine(embedCacheDir);
107553
107584
  this.log = options.log ?? log;
107554
107585
  this.blobStore = options.homedir ? new BlobStore({ blobsDir: join$1(options.homedir, "blobs") }) : void 0;
107555
107586
  this.records = new AgentRecords(this, options.persistence ?? (options.homedir ? new FileSystemAgentRecordPersistence(join$1(options.homedir, "wire.jsonl"), {
@@ -133525,12 +133556,12 @@ function getEmbeddingCacheDir() {
133525
133556
  return dir;
133526
133557
  }
133527
133558
  /**
133528
- * Any usable cache: the model directory with its weights present.
133559
+ * Any usable cache: every file the embedding runtime needs is on disk.
133529
133560
  *
133530
- * Such a cache can still be missing sidecars, which FlagEmbedding repairs from
133531
- * the Hub while loading — so callers must not treat it as "no model here".
133532
- * Flagging it as missing would block the very path that repairs it, and
133533
- * fastembed itself skips downloading whenever the model directory exists.
133561
+ * Loading then never downloads. A cache that only looks installed (weights
133562
+ * without the tokenizer, for instance) is deliberately *not* counted as ready:
133563
+ * the setup flow repairs it through one clean re-download instead of failing
133564
+ * halfway through a load, and reporting it as present would block that repair.
133534
133565
  * The existence check itself lives in @scream-code/memory next to the cache
133535
133566
  * layout it inspects.
133536
133567
  */
@@ -133575,15 +133606,21 @@ function getEmbeddingStatus() {
133575
133606
  * Concurrent calls join the in-flight download and share its result instead
133576
133607
  * of failing — the startup warm-up and a user-initiated download must never
133577
133608
  * race into a spurious "download already in progress" error.
133609
+ *
133610
+ * `onProgress` observes the attempt (byte progress, then verify/load phases)
133611
+ * so the caller can keep a spinner honest; it is detached when the attempt
133612
+ * settles either way.
133578
133613
  */
133579
- async function startManualEmbeddingDownload() {
133614
+ async function startManualEmbeddingDownload(onProgress) {
133580
133615
  if (embeddingEngineInstance === void 0) return failureResult("embedding engine not initialized");
133581
133616
  if (embeddingEngineInstance.available) return {
133582
133617
  ok: true,
133583
133618
  alreadyReady: true
133584
133619
  };
133585
133620
  if (downloadPromise !== void 0) return downloadPromise;
133621
+ embeddingEngineInstance.setProgressListener?.(onProgress);
133586
133622
  downloadPromise = performDownload().finally(() => {
133623
+ embeddingEngineInstance?.setProgressListener?.(void 0);
133587
133624
  downloadPromise = void 0;
133588
133625
  });
133589
133626
  return downloadPromise;
@@ -133610,7 +133647,7 @@ async function performDownload() {
133610
133647
  let ok = await embeddingEngineInstance.ensureReady();
133611
133648
  let error = ok ? void 0 : embeddingEngineInstance.lastError;
133612
133649
  if (!ok) {
133613
- if (classifyEmbeddingFailure(error) !== "platform") {
133650
+ if (classifyEmbeddingFailure(error) === "archive") {
133614
133651
  clearEmbeddingModelCache(getEmbeddingCacheDir());
133615
133652
  ok = await embeddingEngineInstance.ensureReady();
133616
133653
  error = ok ? void 0 : embeddingEngineInstance.lastError;
@@ -133636,7 +133673,7 @@ async function performDownload() {
133636
133673
  */
133637
133674
  function embeddingFailureHint(kind) {
133638
133675
  switch (kind) {
133639
- case "platform": return t("knowledge.embedding_platform_hint");
133676
+ case "install": return t("knowledge.embedding_install_hint");
133640
133677
  case "archive": return t("knowledge.embedding_cache_corrupt_hint");
133641
133678
  case "network": return t("knowledge.embedding_network_hint");
133642
133679
  default: return t("knowledge.embedding_unknown_hint");
@@ -133654,6 +133691,27 @@ function embeddingFailureDetail(kind, error) {
133654
133691
  return [embeddingFailureHint(kind), error?.split("\n")[0]].filter(Boolean).join("\n");
133655
133692
  }
133656
133693
  //#endregion
133694
+ //#region src/tui/utils/embedding-progress-label.ts
133695
+ /**
133696
+ * Spinner label for an in-flight model load: byte progress while downloading
133697
+ * ("12.3 MB / 52.9 MB (23%)"), then the verify and load phases.
133698
+ *
133699
+ * Without a content-length the byte count is still reported, so the number
133700
+ * visibly moves on a slow link instead of looking frozen — the failure mode
133701
+ * that used to leave users staring at a generic spinner.
133702
+ */
133703
+ function embeddingProgressLabel(progress) {
133704
+ if (progress.phase === "verify") return t("kw.embedding_verifying");
133705
+ if (progress.phase === "load") return t("kw.embedding_loading");
133706
+ const done = `${(progress.received / 1048576).toFixed(1)} MB`;
133707
+ if (progress.total === void 0) return t("kw.embedding_progress_unknown", { done });
133708
+ return t("kw.embedding_progress", {
133709
+ done,
133710
+ total: `${(progress.total / 1048576).toFixed(1)} MB`,
133711
+ percent: `${Math.min(100, Math.floor(progress.received / progress.total * 100))}`
133712
+ });
133713
+ }
133714
+ //#endregion
133657
133715
  //#region src/tui/commands/experimental-flags.ts
133658
133716
  let snapshot = {};
133659
133717
  /** Replace the cached flag snapshot. Call once after fetching via `harness.getExperimentalFlags()`. */
@@ -133917,6 +133975,20 @@ const BUILTIN_SLASH_COMMANDS = [
133917
133975
  priority: 198,
133918
133976
  availability: "always"
133919
133977
  },
133978
+ {
133979
+ name: "hotkeys",
133980
+ aliases: ["keys"],
133981
+ description: "registry.hotkeys_desc",
133982
+ priority: 197,
133983
+ availability: "always"
133984
+ },
133985
+ {
133986
+ name: "copy",
133987
+ aliases: ["yank"],
133988
+ description: "registry.copy_desc",
133989
+ priority: 196,
133990
+ availability: "always"
133991
+ },
133920
133992
  {
133921
133993
  name: "make-skill",
133922
133994
  aliases: ["makeskill", "craftskill"],
@@ -134555,6 +134627,10 @@ var ChoicePickerComponent = class extends Container {
134555
134627
  searchable: opts.searchable === true
134556
134628
  });
134557
134629
  }
134630
+ /** Panel replaced by another one before a choice was made. */
134631
+ dispose() {
134632
+ this.opts.onDispose?.();
134633
+ }
134558
134634
  handleInput(data) {
134559
134635
  if (matchesKey(data, Key.escape)) {
134560
134636
  if (this.list.clearQuery()) return;
@@ -134632,9 +134708,10 @@ function optionLabelStyle(option, selected, colors) {
134632
134708
  //#region src/tui/utils/connect-catalog.ts
134633
134709
  /**
134634
134710
  * Resolve the catalog request for /config.
134635
- * - /config → remote-first catalog browser
134636
134711
  * - /config diy → hidden manual provider setup (not shown in help)
134637
134712
  * - /config image → hidden image-generation API setup (not shown in help)
134713
+ * - anything else → the caller must ask first: a bare `/config` opens the
134714
+ * entry menu (shortcut directory / DIY / image) before any flow starts.
134638
134715
  */
134639
134716
  function resolveConnectCatalogRequest(args) {
134640
134717
  const trimmed = args.trim().toLowerCase();
@@ -135257,6 +135334,71 @@ async function handleConnectCommand(host, args) {
135257
135334
  await handleDiyConfig(host);
135258
135335
  return;
135259
135336
  }
135337
+ const target = await promptConfigEntry(host);
135338
+ if (target === void 0) return;
135339
+ if (target === "diy") {
135340
+ await handleDiyConfig(host);
135341
+ return;
135342
+ }
135343
+ if (target === "image") {
135344
+ await handleImageConfig(host);
135345
+ return;
135346
+ }
135347
+ await runCatalogConnectFlow(host, url);
135348
+ }
135349
+ /**
135350
+ * Entry menu for a bare `/config`. Mirrors `promptImageProviderPreset`: mount
135351
+ * the picker, resolve on select/cancel, and hand the editor back in both cases.
135352
+ */
135353
+ function promptConfigEntry(host) {
135354
+ return new Promise((resolve) => {
135355
+ let settled = false;
135356
+ const settle = (value) => {
135357
+ if (settled) return;
135358
+ settled = true;
135359
+ resolve(value);
135360
+ };
135361
+ const picker = new ChoicePickerComponent({
135362
+ title: t("config.entry_title"),
135363
+ hint: t("config.entry_hint"),
135364
+ colors: host.state.theme.colors,
135365
+ options: [
135366
+ {
135367
+ value: "catalog",
135368
+ label: t("config.entry_quick"),
135369
+ description: t("config.entry_quick_desc")
135370
+ },
135371
+ {
135372
+ value: "diy",
135373
+ label: t("config.entry_diy"),
135374
+ description: t("config.entry_diy_desc")
135375
+ },
135376
+ {
135377
+ value: "image",
135378
+ label: t("config.entry_image"),
135379
+ description: t("config.entry_image_desc")
135380
+ }
135381
+ ],
135382
+ onSelect: (value) => {
135383
+ settle(value);
135384
+ host.restoreEditor();
135385
+ },
135386
+ onCancel: () => {
135387
+ settle(void 0);
135388
+ host.restoreEditor();
135389
+ },
135390
+ onDispose: () => {
135391
+ settle(void 0);
135392
+ }
135393
+ });
135394
+ host.mountEditorReplacement(picker);
135395
+ });
135396
+ }
135397
+ /**
135398
+ * The shortcut flow: remote catalog (with cache and built-in fallbacks) →
135399
+ * provider → model → API key → apply, then refresh the running config.
135400
+ */
135401
+ async function runCatalogConnectFlow(host, url) {
135260
135402
  let catalog;
135261
135403
  const controller = new AbortController();
135262
135404
  const cancel = () => {
@@ -140610,7 +140752,7 @@ async function guidedGoalSetup(host) {
140610
140752
  host.showNotice(t("goal.storm_breaker"), t("goal.conflict_loop"));
140611
140753
  return;
140612
140754
  }
140613
- const { TextInputDialogComponent } = await import("./text-input-dialog-D7dm6kak.mjs");
140755
+ const { TextInputDialogComponent } = await import("./text-input-dialog-CFRUSCi6.mjs");
140614
140756
  const initialDesc = await promptText(host, TextInputDialogComponent, {
140615
140757
  title: t("goal.setup_title_initial"),
140616
140758
  subtitle: t("goal.setup_desc_hint"),
@@ -140631,7 +140773,7 @@ async function guidedGoalSetup(host) {
140631
140773
  await showGoalConfigWizard(host, session, confirmed.trim() || objective, false);
140632
140774
  }
140633
140775
  async function showGoalConfigWizard(host, session, objective, replace) {
140634
- const { TextInputDialogComponent } = await import("./text-input-dialog-D7dm6kak.mjs");
140776
+ const { TextInputDialogComponent } = await import("./text-input-dialog-CFRUSCi6.mjs");
140635
140777
  const turnInput = await promptNumber(host, TextInputDialogComponent, {
140636
140778
  title: t("goal.wizard_title", { objective }),
140637
140779
  subtitle: t("goal.budget_turns_hint"),
@@ -147461,6 +147603,7 @@ function buildRows(servers) {
147461
147603
  const capLabels = getCapabilityLabels();
147462
147604
  const capInfo = (s.capabilities ?? []).map((c) => capLabels[c] ?? c).join(" ");
147463
147605
  const errorInfo = s.error ? ` — ${sanitizeDesc(s.error)}` : "";
147606
+ const pluginInfo = pluginIdFromMcpRuntimeName(s.name) !== void 0 ? t("mcp.plugin_owned") : "";
147464
147607
  rows.push({
147465
147608
  kind: "installed",
147466
147609
  name: s.name,
@@ -147472,6 +147615,7 @@ function buildRows(servers) {
147472
147615
  statusLabel,
147473
147616
  toolInfo,
147474
147617
  capInfo,
147618
+ pluginInfo,
147475
147619
  errorInfo
147476
147620
  ].filter(Boolean).join(" ")
147477
147621
  });
@@ -147515,6 +147659,14 @@ async function handleEnter(host, row) {
147515
147659
  }
147516
147660
  async function handleDelete$1(host, row) {
147517
147661
  if (row.kind !== "installed" || !row.status || row.status === "__section" || row.status === "__empty") return;
147662
+ const pluginId = pluginIdFromMcpRuntimeName(row.name);
147663
+ if (pluginId !== void 0) {
147664
+ host.showError(t("mcp.uninstall_plugin_blocked", {
147665
+ name: row.label,
147666
+ plugin: pluginId
147667
+ }));
147668
+ return;
147669
+ }
147518
147670
  if (!await confirmAction(host, t("mcp.uninstall_confirm", { name: row.label }))) return;
147519
147671
  await uninstallMcp(host, row.name);
147520
147672
  }
@@ -147613,7 +147765,8 @@ async function disableMcp(host, name) {
147613
147765
  if (!session) return;
147614
147766
  try {
147615
147767
  await session.stopMcpServer(name);
147616
- host.showStatus(t("mcp.disabled_done", { name }));
147768
+ const note = pluginIdFromMcpRuntimeName(name) !== void 0 ? ` ${t("mcp.plugin_owned_stop_note")}` : "";
147769
+ host.showStatus(t("mcp.disabled_done", { name }) + note);
147617
147770
  } catch (error) {
147618
147771
  host.showError(t("mcp.disable_failed", { msg: error instanceof Error ? error.message : String(error) }));
147619
147772
  }
@@ -147628,12 +147781,68 @@ async function enableMcp(host, name) {
147628
147781
  host.showError(t("mcp.start_failed", { msg: error instanceof Error ? error.message : String(error) }));
147629
147782
  }
147630
147783
  }
147784
+ /**
147785
+ * Project-level mcp.json files (cwd + parent chain) that declare `name`.
147786
+ *
147787
+ * The runtime list cannot tell where a server came from, but a server declared
147788
+ * by a project config is re-loaded on every start no matter what we delete from
147789
+ * the user-level file — removing it there is the same silent lie as removing a
147790
+ * plugin-owned server. Paths come from the core's own resolver so the rule that
147791
+ * decides "which files count" cannot drift.
147792
+ */
147793
+ function projectConfigFilesDeclaring(name) {
147794
+ let parents;
147795
+ let project;
147796
+ try {
147797
+ const resolved = resolveMcpJsonPaths({
147798
+ cwd: process.cwd(),
147799
+ homeDir: getDataDir()
147800
+ });
147801
+ parents = resolved.parents;
147802
+ project = resolved.project;
147803
+ } catch {
147804
+ return [];
147805
+ }
147806
+ return [...parents, project].filter((file) => {
147807
+ if (!existsSync(file)) return false;
147808
+ try {
147809
+ const parsed = JSON.parse(readFileSync(file, "utf-8"));
147810
+ return parsed !== null && typeof parsed === "object" && parsed.mcpServers !== void 0 && Object.hasOwn(parsed.mcpServers, name);
147811
+ } catch {
147812
+ return false;
147813
+ }
147814
+ });
147815
+ }
147631
147816
  async function uninstallMcp(host, name) {
147632
147817
  const session = host.session;
147633
147818
  if (!session) return;
147819
+ const pluginId = pluginIdFromMcpRuntimeName(name);
147820
+ if (pluginId !== void 0) {
147821
+ host.showError(t("mcp.uninstall_plugin_blocked", {
147822
+ name,
147823
+ plugin: pluginId
147824
+ }));
147825
+ return;
147826
+ }
147827
+ const projectFiles = projectConfigFilesDeclaring(name);
147828
+ if (projectFiles.length > 0) {
147829
+ host.showError(t("mcp.uninstall_project_owned", {
147830
+ name,
147831
+ files: projectFiles.join(", ")
147832
+ }));
147833
+ return;
147834
+ }
147634
147835
  try {
147635
147836
  await session.removeMcpServer(name);
147636
147837
  await removeMcpConfig(host, name);
147838
+ let stillListed = false;
147839
+ try {
147840
+ stillListed = (await session.listMcpServers()).some((server) => server.name === name);
147841
+ } catch {}
147842
+ if (stillListed) {
147843
+ host.showError(t("mcp.uninstall_verify_failed", { name }));
147844
+ return;
147845
+ }
147637
147846
  host.showStatus(t("mcp.uninstalled", { name }));
147638
147847
  } catch (error) {
147639
147848
  host.showError(t("mcp.uninstall_failed", { msg: error instanceof Error ? error.message : String(error) }));
@@ -149675,6 +149884,147 @@ async function handleMermaidCommand(host, args) {
149675
149884
  }));
149676
149885
  }
149677
149886
  //#endregion
149887
+ //#region src/utils/clipboard/clipboard-text.ts
149888
+ /**
149889
+ * Write plain text to the system clipboard with platform-specific commands.
149890
+ *
149891
+ * scream-core's clipboard layer is read-only (clipboard-native / clipboard-image),
149892
+ * so the TUI's "copy selected text" needs its own writer. Used to back the
149893
+ * pi-tui `copySelection` hook — return true/false so the TUI can flash
149894
+ * "Copied!" or "Copy failed".
149895
+ *
149896
+ * Platform mapping:
149897
+ * macOS -> pbcopy
149898
+ * Windows-> powershell Set-Clipboard (base64 payload, avoids quoting issues)
149899
+ * Linux -> wl-copy (Wayland) / xclip or xsel (X11), tried in a candidate
149900
+ * chain so a missing tool (Ubuntu ships neither by default)
149901
+ * falls through to the next one instead of failing outright.
149902
+ */
149903
+ var ClipboardTextError = class extends Error {
149904
+ constructor(message) {
149905
+ super(message);
149906
+ this.name = "ClipboardTextError";
149907
+ }
149908
+ };
149909
+ const defaultRunCommand = (command, args, input) => {
149910
+ const result = spawnSync(command, args, {
149911
+ input,
149912
+ encoding: "utf8",
149913
+ timeout: 3e3
149914
+ });
149915
+ if (result.status === 0) return { ok: true };
149916
+ return {
149917
+ ok: false,
149918
+ error: result.error?.message ?? result.stderr?.toString() ?? `exit ${result.status}`
149919
+ };
149920
+ };
149921
+ function detectPlatform() {
149922
+ if (process.platform === "darwin") return "macos";
149923
+ if (process.platform === "win32") return "windows";
149924
+ if (process.platform === "linux") return process.env["WAYLAND_DISPLAY"] ? "linux-wayland" : "linux-x11";
149925
+ return "unsupported";
149926
+ }
149927
+ function linuxCandidates(platform) {
149928
+ const wl = {
149929
+ command: "wl-copy",
149930
+ args: []
149931
+ };
149932
+ const xclip = {
149933
+ command: "xclip",
149934
+ args: [
149935
+ "-selection",
149936
+ "clipboard",
149937
+ "-i"
149938
+ ]
149939
+ };
149940
+ const xsel = {
149941
+ command: "xsel",
149942
+ args: ["--clipboard", "--input"]
149943
+ };
149944
+ return platform === "linux-wayland" ? [
149945
+ wl,
149946
+ xclip,
149947
+ xsel
149948
+ ] : [
149949
+ xclip,
149950
+ xsel,
149951
+ wl
149952
+ ];
149953
+ }
149954
+ /**
149955
+ * Write `text` to the system clipboard. Throws ClipboardTextError when the
149956
+ * platform is unsupported or every candidate command fails, so callers can
149957
+ * surface a "Copy failed" indicator.
149958
+ */
149959
+ async function writeTextClipboard(text, runCommand = defaultRunCommand) {
149960
+ const platform = detectPlatform();
149961
+ let command;
149962
+ let args;
149963
+ let input;
149964
+ switch (platform) {
149965
+ case "macos":
149966
+ command = "pbcopy";
149967
+ args = [];
149968
+ input = text;
149969
+ break;
149970
+ case "windows": {
149971
+ const base64 = Buffer.from(text, "utf8").toString("base64");
149972
+ command = "powershell";
149973
+ args = [
149974
+ "-NoProfile",
149975
+ "-Command",
149976
+ `[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${base64}')) | Set-Clipboard`
149977
+ ];
149978
+ input = "";
149979
+ break;
149980
+ }
149981
+ case "linux-wayland":
149982
+ case "linux-x11": {
149983
+ const failures = [];
149984
+ for (const candidate of linuxCandidates(platform)) {
149985
+ const result = runCommand(candidate.command, candidate.args, text);
149986
+ if (result.ok) return;
149987
+ failures.push(`${candidate.command}: ${result.error ?? "unknown error"}`);
149988
+ }
149989
+ 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)`);
149990
+ }
149991
+ default: throw new ClipboardTextError(`unsupported clipboard platform: ${process.platform}`);
149992
+ }
149993
+ const result = runCommand(command, args, input);
149994
+ if (!result.ok) throw new ClipboardTextError(`failed to copy to clipboard: ${command} ${result.error ?? "unknown error"}`);
149995
+ }
149996
+ //#endregion
149997
+ //#region src/tui/commands/copy.ts
149998
+ /**
149999
+ * `/copy` — put the most recent assistant reply on the system clipboard.
150000
+ *
150001
+ * Only `assistant` transcript entries are considered: tool output and notices
150002
+ * are not "the reply", and an empty/whitespace entry (a cancelled turn) would
150003
+ * otherwise count as copyable. Clipboard failures are reported, never swallowed:
150004
+ * the user asked for a side effect and must know whether it landed.
150005
+ */
150006
+ async function handleCopyCommand(host) {
150007
+ const entries = host.state.transcriptEntries;
150008
+ let latest;
150009
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
150010
+ const entry = entries[index];
150011
+ if (entry.kind !== "assistant") continue;
150012
+ if (entry.content.trim().length === 0) continue;
150013
+ latest = entry.content;
150014
+ break;
150015
+ }
150016
+ if (latest === void 0) {
150017
+ host.showStatus(t("tui.copy_empty"));
150018
+ return;
150019
+ }
150020
+ try {
150021
+ await writeTextClipboard(latest);
150022
+ host.showStatus(t("tui.copy_done"));
150023
+ } catch (error) {
150024
+ host.showError(t("tui.copy_failed", { msg: error instanceof Error ? error.message : String(error) }));
150025
+ }
150026
+ }
150027
+ //#endregion
149678
150028
  //#region src/tui/commands/like.ts
149679
150029
  function promptTextInput$1(host, title, opts) {
149680
150030
  const { promise, resolve } = Promise.withResolvers();
@@ -151666,7 +152016,9 @@ async function ensureEmbeddingReadyInteractive(host) {
151666
152016
  return false;
151667
152017
  }
151668
152018
  const spinner = host.showProgressSpinner(t("knowledge.loading_model"));
151669
- const { ok, error, failureKind } = await startManualEmbeddingDownload();
152019
+ const { ok, error, failureKind } = await startManualEmbeddingDownload((progress) => {
152020
+ spinner.setLabel(embeddingProgressLabel(progress));
152021
+ });
151670
152022
  spinner.stop({
151671
152023
  ok,
151672
152024
  label: ok ? t("kw.embedding_ready") : t("kw.embedding_failed")
@@ -152589,6 +152941,12 @@ async function handleBuiltInSlashCommand(host, name, args) {
152589
152941
  case "help":
152590
152942
  host.showHelpPanel();
152591
152943
  return;
152944
+ case "hotkeys":
152945
+ host.showHotkeysPanel();
152946
+ return;
152947
+ case "copy":
152948
+ await handleCopyCommand(host);
152949
+ return;
152592
152950
  case "version":
152593
152951
  host.showStatus(`Scream Code v${host.state.appState.version}`);
152594
152952
  return;
@@ -152766,4 +153124,4 @@ async function handleBuiltInSlashCommand(host, name, args) {
152766
153124
  }
152767
153125
  }
152768
153126
  //#endregion
152769
- export { refineGoal as $, ScreamHarness as $n, FooterComponent as $t, renderDiffLinesClustered as A, setExperimentalFlags as An, isEmptySessionHintDismissed as At, AssistantMessageComponent as B, saveTuiConfig as Bn, ENABLE_TERMINAL_FOCUS_REPORTING as Bt, readJsonlFile as C, getCtrlDHint as Cn, supportsBalance as Ct, UserMessageComponent as D, BUILTIN_SLASH_COMMANDS as Dn, resolveThemeSync as Dt, handleRevokeCommand as E, buildSkillSlashCommands as En, showUsage as Et, NoticeMessageComponent as F, PIXEL_PULSE_FRAMES as Fn, getColorPalette as Ft, BackgroundAgentStatusComponent as G, detectInstallSource as Gn, OSC11_RESPONSE_PREFIX_NO_ESC as Gt, charsForTokenBudget as H, getDataDir as Hn, OSC11_QUERY as Ht, StatusMessageComponent as I, PULSE_WAVE_FRAMES as In, detectTerminalTheme as It, BREATHE_CYCLE_MS as J, CLI_USER_AGENT_PRODUCT as Jn, TERMINAL_FOCUS_OUT as Jt, AgentGroupComponent as K, CLI_COMMAND_NAME as Kn, QUERY_TERMINAL_THEME as Kt, replaceTabs as L, TuiConfigParseError as Ln, parseOsc11BackgroundTheme as Lt, langFromPath as M, getKnowledgeStore as Mn, toggleEmptySessionHint as Mt, CachedContainer as N, hasEmbeddingModelCache as Nn, createThemeStyles as Nt, ToolCallComponent as O, sortSlashCommands as On, createEditorTheme as Ot, ThinkingComponent as P, startManualEmbeddingDownload as Pn, contrastTextHex as Pt, clearGoalState as Q, saveCatalogCache as Qn, isStreaming as Qt, sanitizeShellOutput as R, TuiLikePreferencesSchema as Rn, DISABLE_TERMINAL_FOCUS_REPORTING as Rt, appendJsonlLine as S, getCtrlCHint as Sn, refreshProviderBalance as St, getDaemonInstructions as T, getNoActiveSessionMessage as Tn, showStatusReport as Tt, estimateTokens as U, getInputHistoryFile as Un, OSC11_RESPONSE as Ut, ActivityGroupComponent as V, detectShellEnvironment as Vn, ENABLE_TERMINAL_THEME_REPORTING as Vt, getSharedSpeedTracker as W, getLogDir as Wn, OSC11_RESPONSE_PREFIX as Wt, resetBreathingClock as X, DEFAULT_CATALOG_URL as Xn, TERMINAL_THEME_LIGHT as Xt, getBreathingFrame as Y, PRODUCT_NAME as Yn, TERMINAL_THEME_DARK as Yt, CompactionComponent as Z, fetchCatalog as Zn, isBusy as Zt, handleMcpCommand as _, EMPTY_SESSION_HINT_URL as _n, handleWolfpackCommand as _t, buildRoleAdditionalText as a, handleLogoutCommand as an, MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE as ar, isInternalMessage as at, refreshUpdateCache as b, SESSION_TIPS as bn, showPermissionPicker as bt, handleSkillCommand as c, appendSessionHeaderHint as cn, resolveScreamHome as cr, getModelCycleLevel as ct, isExpandable as d, formatErrorMessage as dn, resolveGlobalLogPath as dr, handleCompactCommand as dt, lerpGradient as en, encodeWorkDirKey as er, handleExportDebugZipCommand as et, isPlanExpandable as f, isTodoItemShape as fn, isScreamError as fr, handleEditorCommand as ft, handleChannelCommand as g, truncateErrorMessage as gn, handleThemeCommand as gt, handleMemoryCommand as h, stringValue as hn, SCREAM_ERROR_INFO as hr, handlePlanCommand as ht, openUrl as i, handleConnectCommand as in, removeSessionIndexEntry as ir, handleTitleCommand as it, highlightLines as j, embeddingFailureDetail as jn, isTurnElapsedEnabled as jt, renderDiffLines as k, isExperimentalFlagEnabled as kn, createMarkdownTheme as kt, disposeChildren as l, appendStreamingArgsPreview as ln, flushDiagnosticLogs as lr, handleAutoCommand as lt, formatMemoryMemoForInjection as m, serializeToolResultOutput as mn, ErrorCodes as mr, handleModelCommand as mt, clearEvalPanelState as n, handleTraceCommand as nn, appendSessionIndexEntry as nr, handleForkCommand as nt, syncDiagramCapabilityPrompt as o, printableChar as on, isRealUserPrompt as or, toTerminalHyperlink as ot, MoonLoader as p, parseStreamingArgs as pn, isOrphanedToolCallError as pr, handleFusionPlanCommand as pt, WelcomeComponent as q, CLI_UI_MODE as qn, TERMINAL_FOCUS_IN as qt, handleGrantsCommand as r, handleSearchCommand as rn, readSessionIndex as rr, handleInitCommand as rt, handleExtensionCommand as s, STATUS_BULLET as sn, parseRlmMaxDepthArg as sr, changeThinkingLevel as st, dispatchInput as t, SubagentSlots as tn, normalizeWorkDir as tr, handleExportMdCommand as tt, hasDispose as u, argsRecord as un, log as ur, handleBotCommand as ut, handleUpdateCommand as v, EXIT_CONFIRM_WINDOW_MS as vn, handleYoloCommand as vt, handleCcCommand as w, getLlmNotSetMessage as wn, clearInfoPanelState as wt, readUpdateCache as x, TIP_ROTATION_INTERVAL_MS as xn, showSettingsSelector as xt, selectUpdateTarget as y, MAIN_AGENT_ID$1 as yn, showModelPicker as yt, SkillActivationComponent as z, loadTuiConfig as zn, DISABLE_TERMINAL_THEME_REPORTING as zt };
153127
+ export { clearGoalState as $, fetchCatalog as $n, isStreaming as $t, renderDiffLines as A, isExperimentalFlagEnabled as An, createMarkdownTheme as At, SkillActivationComponent as B, TuiLikePreferencesSchema as Bn, DISABLE_TERMINAL_THEME_REPORTING as Bt, appendJsonlLine as C, getCtrlCHint as Cn, refreshProviderBalance as Ct, handleRevokeCommand as D, buildSkillSlashCommands as Dn, showUsage as Dt, getDaemonInstructions as E, getNoActiveSessionMessage as En, showStatusReport as Et, ThinkingComponent as F, hasEmbeddingModelCache as Fn, contrastTextHex as Ft, getSharedSpeedTracker as G, getInputHistoryFile as Gn, OSC11_RESPONSE_PREFIX as Gt, ActivityGroupComponent as H, saveTuiConfig as Hn, ENABLE_TERMINAL_THEME_REPORTING as Ht, NoticeMessageComponent as I, startManualEmbeddingDownload as In, getColorPalette as It, WelcomeComponent as J, CLI_COMMAND_NAME as Jn, TERMINAL_FOCUS_IN as Jt, BackgroundAgentStatusComponent as K, getLogDir as Kn, OSC11_RESPONSE_PREFIX_NO_ESC as Kt, StatusMessageComponent as L, PIXEL_PULSE_FRAMES as Ln, detectTerminalTheme as Lt, highlightLines as M, embeddingProgressLabel as Mn, isTurnElapsedEnabled as Mt, langFromPath as N, embeddingFailureDetail as Nn, toggleEmptySessionHint as Nt, UserMessageComponent as O, BUILTIN_SLASH_COMMANDS as On, resolveThemeSync as Ot, CachedContainer as P, getKnowledgeStore as Pn, createThemeStyles as Pt, CompactionComponent as Q, DEFAULT_CATALOG_URL as Qn, isBusy as Qt, replaceTabs as R, PULSE_WAVE_FRAMES as Rn, parseOsc11BackgroundTheme as Rt, readUpdateCache as S, TIP_ROTATION_INTERVAL_MS as Sn, showSettingsSelector as St, handleCcCommand as T, getLlmNotSetMessage as Tn, clearInfoPanelState as Tt, charsForTokenBudget as U, detectShellEnvironment as Un, OSC11_QUERY as Ut, AssistantMessageComponent as V, loadTuiConfig as Vn, ENABLE_TERMINAL_FOCUS_REPORTING as Vt, estimateTokens as W, getDataDir as Wn, OSC11_RESPONSE as Wt, getBreathingFrame as X, CLI_USER_AGENT_PRODUCT as Xn, TERMINAL_THEME_DARK as Xt, BREATHE_CYCLE_MS as Y, CLI_UI_MODE as Yn, TERMINAL_FOCUS_OUT as Yt, resetBreathingClock as Z, PRODUCT_NAME as Zn, TERMINAL_THEME_LIGHT as Zt, handleChannelCommand as _, truncateErrorMessage as _n, SCREAM_ERROR_INFO as _r, handleThemeCommand as _t, buildRoleAdditionalText as a, handleConnectCommand as an, readSessionIndex as ar, handleTitleCommand as at, selectUpdateTarget as b, MAIN_AGENT_ID$1 as bn, showModelPicker as bt, handleExtensionCommand as c, STATUS_BULLET as cn, isRealUserPrompt as cr, changeThinkingLevel as ct, hasDispose as d, argsRecord as dn, flushDiagnosticLogs as dr, handleBotCommand as dt, FooterComponent as en, saveCatalogCache as er, refineGoal as et, isExpandable as f, formatErrorMessage as fn, log as fr, handleCompactCommand as ft, handleMemoryCommand as g, stringValue as gn, ErrorCodes as gr, handlePlanCommand as gt, formatMemoryMemoForInjection as h, serializeToolResultOutput as hn, isOrphanedToolCallError as hr, handleModelCommand as ht, openUrl as i, handleSearchCommand as in, appendSessionIndexEntry as ir, handleInitCommand as it, renderDiffLinesClustered as j, setExperimentalFlags as jn, isEmptySessionHintDismissed as jt, ToolCallComponent as k, sortSlashCommands as kn, createEditorTheme as kt, handleSkillCommand as l, appendSessionHeaderHint as ln, parseRlmMaxDepthArg as lr, getModelCycleLevel as lt, MoonLoader as m, parseStreamingArgs as mn, isScreamError as mr, handleFusionPlanCommand as mt, clearEvalPanelState as n, SubagentSlots as nn, encodeWorkDirKey as nr, handleExportMdCommand as nt, writeTextClipboard as o, handleLogoutCommand as on, removeSessionIndexEntry as or, isInternalMessage as ot, isPlanExpandable as p, isTodoItemShape as pn, resolveGlobalLogPath as pr, handleEditorCommand as pt, AgentGroupComponent as q, detectInstallSource as qn, QUERY_TERMINAL_THEME as qt, handleGrantsCommand as r, handleTraceCommand as rn, normalizeWorkDir as rr, handleForkCommand as rt, syncDiagramCapabilityPrompt as s, printableChar as sn, MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE as sr, toTerminalHyperlink as st, dispatchInput as t, lerpGradient as tn, ScreamHarness as tr, handleExportDebugZipCommand as tt, disposeChildren as u, appendStreamingArgsPreview as un, resolveScreamHome as ur, handleAutoCommand as ut, handleMcpCommand as v, EMPTY_SESSION_HINT_URL as vn, handleWolfpackCommand as vt, readJsonlFile as w, getCtrlDHint as wn, supportsBalance as wt, refreshUpdateCache as x, SESSION_TIPS as xn, showPermissionPicker as xt, handleUpdateCommand as y, EXIT_CONFIRM_WINDOW_MS as yn, handleYoloCommand as yt, sanitizeShellOutput as z, TuiConfigParseError as zn, DISABLE_TERMINAL_FOCUS_REPORTING as zt };
@@ -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 { in as handleConnectCommand } from "./dispatch-7Mas_Tvb.mjs";
6
+ import { an as handleConnectCommand } from "./dispatch-BPfUTccZ.mjs";
7
7
  export { handleConnectCommand };
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-IU7G9gs0.mjs")).main();
9
+ (await import("./app-Db7f8Kja.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);