scream-code 0.12.8 → 0.12.9

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-9ywV88Lh.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-CsPSlTrQ.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";
@@ -3033,7 +3033,7 @@ const buildHeaders$2 = (newHeaders) => {
3033
3033
  function encodeURIPath$2(str) {
3034
3034
  return str.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
3035
3035
  }
3036
- const EMPTY$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
3036
+ const EMPTY$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
3037
3037
  const createPathTagFunction$2 = (pathEncoder = encodeURIPath$2) => function path(statics, ...params) {
3038
3038
  if (statics.length === 1) return statics[0];
3039
3039
  let postPath = false;
@@ -3042,7 +3042,7 @@ const createPathTagFunction$2 = (pathEncoder = encodeURIPath$2) => function path
3042
3042
  if (/[?#]/.test(currentValue)) postPath = true;
3043
3043
  const value = params[index];
3044
3044
  let encoded = (postPath ? encodeURIComponent : pathEncoder)("" + value);
3045
- if (index !== params.length && (value == null || typeof value === "object" && value.toString === Object.getPrototypeOf(Object.getPrototypeOf(value.hasOwnProperty ?? EMPTY$3) ?? EMPTY$3)?.toString)) {
3045
+ if (index !== params.length && (value == null || typeof value === "object" && value.toString === Object.getPrototypeOf(Object.getPrototypeOf(value.hasOwnProperty ?? EMPTY$4) ?? EMPTY$4)?.toString)) {
3046
3046
  encoded = value + "";
3047
3047
  invalidSegments.push({
3048
3048
  start: previousValue.length + currentValue.length,
@@ -31529,7 +31529,7 @@ APIResource$1._key = [];
31529
31529
  function encodeURIPath$1(str) {
31530
31530
  return str.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
31531
31531
  }
31532
- const EMPTY$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
31532
+ const EMPTY$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
31533
31533
  const createPathTagFunction$1 = (pathEncoder = encodeURIPath$1) => (function path(statics, ...params) {
31534
31534
  if (statics.length === 1) return statics[0];
31535
31535
  let postPath = false;
@@ -31539,7 +31539,7 @@ const createPathTagFunction$1 = (pathEncoder = encodeURIPath$1) => (function pat
31539
31539
  if (/[?#]/.test(currentValue)) postPath = true;
31540
31540
  const value = params[index];
31541
31541
  let encoded = (postPath ? encodeURIComponent : pathEncoder)("" + value);
31542
- if (index !== params.length && (value == null || typeof value === "object" && value.toString === ((_c = Object.getPrototypeOf((_b = Object.getPrototypeOf((_a = value.hasOwnProperty) !== null && _a !== void 0 ? _a : EMPTY$2)) !== null && _b !== void 0 ? _b : EMPTY$2)) === null || _c === void 0 ? void 0 : _c.toString))) {
31542
+ if (index !== params.length && (value == null || typeof value === "object" && value.toString === ((_c = Object.getPrototypeOf((_b = Object.getPrototypeOf((_a = value.hasOwnProperty) !== null && _a !== void 0 ? _a : EMPTY$3)) !== null && _b !== void 0 ? _b : EMPTY$3)) === null || _c === void 0 ? void 0 : _c.toString))) {
31543
31543
  encoded = value + "";
31544
31544
  invalidSegments.push({
31545
31545
  start: previousValue.length + currentValue.length,
@@ -36516,7 +36516,7 @@ var APIResource = class {
36516
36516
  function encodeURIPath(str) {
36517
36517
  return str.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
36518
36518
  }
36519
- const EMPTY$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
36519
+ const EMPTY$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
36520
36520
  const createPathTagFunction = (pathEncoder = encodeURIPath) => function path(statics, ...params) {
36521
36521
  if (statics.length === 1) return statics[0];
36522
36522
  let postPath = false;
@@ -36525,7 +36525,7 @@ const createPathTagFunction = (pathEncoder = encodeURIPath) => function path(sta
36525
36525
  if (/[?#]/.test(currentValue)) postPath = true;
36526
36526
  const value = params[index];
36527
36527
  let encoded = (postPath ? encodeURIComponent : pathEncoder)("" + value);
36528
- if (index !== params.length && (value == null || typeof value === "object" && value.toString === Object.getPrototypeOf(Object.getPrototypeOf(value.hasOwnProperty ?? EMPTY$1) ?? EMPTY$1)?.toString)) {
36528
+ if (index !== params.length && (value == null || typeof value === "object" && value.toString === Object.getPrototypeOf(Object.getPrototypeOf(value.hasOwnProperty ?? EMPTY$2) ?? EMPTY$2)?.toString)) {
36529
36529
  encoded = value + "";
36530
36530
  invalidSegments.push({
36531
36531
  start: previousValue.length + currentValue.length,
@@ -66924,7 +66924,7 @@ async function isDir(p) {
66924
66924
  //#region ../../packages/agent-core/src/plugin/store.ts
66925
66925
  const PLUGINS_DIR_REL = path.join("plugins");
66926
66926
  const INSTALLED_REL = path.join(PLUGINS_DIR_REL, "installed.json");
66927
- const EMPTY = {
66927
+ const EMPTY$1 = {
66928
66928
  version: 1,
66929
66929
  plugins: []
66930
66930
  };
@@ -66934,7 +66934,7 @@ async function readInstalled(screamHomeDir) {
66934
66934
  try {
66935
66935
  text = await readFile(filePath, "utf8");
66936
66936
  } catch (error) {
66937
- if (error.code === "ENOENT") return EMPTY;
66937
+ if (error.code === "ENOENT") return EMPTY$1;
66938
66938
  throw error;
66939
66939
  }
66940
66940
  try {
@@ -76556,14 +76556,14 @@ function stringifyJsonish(value) {
76556
76556
  //#endregion
76557
76557
  //#region ../../packages/agent-core/src/agent/compaction/strategy.ts
76558
76558
  const DEFAULT_COMPACTION_CONFIG = {
76559
- triggerRatio: .75,
76560
- blockRatio: .85,
76561
- reservedContextSize: 5e4,
76559
+ triggerRatio: .85,
76560
+ blockRatio: .9,
76561
+ reservedContextSize: 2e4,
76562
76562
  maxCompactionPerTurn: 3,
76563
- maxRecentMessages: 16,
76564
- maxRecentUserMessages: 8,
76565
- maxRecentTokens: 2e4,
76566
- maxRecentSizeRatio: .2,
76563
+ maxRecentMessages: 24,
76564
+ maxRecentUserMessages: 12,
76565
+ maxRecentTokens: 3e4,
76566
+ maxRecentSizeRatio: .25,
76567
76567
  minOverflowReductionRatio: .05,
76568
76568
  turnGrowthMultiplier: 2.5
76569
76569
  };
@@ -121739,6 +121739,7 @@ const SCREAM_CODE_LOG_DIR_NAME = "logs";
121739
121739
  const SCREAM_CODE_UPDATE_DIR_NAME = "updates";
121740
121740
  const SCREAM_CODE_UPDATE_STATE_FILE_NAME = "latest.json";
121741
121741
  const SCREAM_CODE_INPUT_HISTORY_DIR_NAME = "user-history";
121742
+ const SCREAM_CODE_UI_PREFERENCES_FILE_NAME = "ui-preferences.json";
121742
121743
  const DEFAULT_OAUTH_PROVIDER_NAME = "managed:scream-code";
121743
121744
  ErrorCodes.AUTH_LOGIN_REQUIRED;
121744
121745
  const SCREAM_CODE_PLUGIN_MARKETPLACE_URL_ENV = "SCREAM_CODE_PLUGIN_MARKETPLACE_URL";
@@ -121815,7 +121816,7 @@ function optionalBuildString(value) {
121815
121816
  return typeof value === "string" && value.length > 0 ? value : void 0;
121816
121817
  }
121817
121818
  const SCREAM_BUILD_INFO = {
121818
- version: optionalBuildString("0.12.8"),
121819
+ version: optionalBuildString("0.12.9"),
121819
121820
  channel: optionalBuildString(""),
121820
121821
  commit: optionalBuildString(""),
121821
121822
  buildTarget: optionalBuildString("darwin-arm64")
@@ -123211,6 +123212,8 @@ function getCtrlCHint() {
123211
123212
  }
123212
123213
  const MAIN_AGENT_ID$1 = "main";
123213
123214
  const EXIT_CONFIRM_WINDOW_MS = 1500;
123215
+ /** Partner model-provider page opened by Ctrl+F while the chat is empty. */
123216
+ const EMPTY_SESSION_HINT_URL = "https://opencode.ai/go?ref=75NKVRZQCY";
123214
123217
  function isManagedUsageProvider(providerKey) {
123215
123218
  return providerKey === DEFAULT_OAUTH_PROVIDER_NAME;
123216
123219
  }
@@ -127643,7 +127646,7 @@ async function guidedGoalSetup(host) {
127643
127646
  host.showNotice(t("goal.storm_breaker"), t("goal.conflict_loop"));
127644
127647
  return;
127645
127648
  }
127646
- const { TextInputDialogComponent } = await import("./text-input-dialog-BIXonNNb.mjs");
127649
+ const { TextInputDialogComponent } = await import("./text-input-dialog-DXLBq_rF.mjs");
127647
127650
  const initialDesc = await promptText(host, TextInputDialogComponent, {
127648
127651
  title: t("goal.setup_title_initial"),
127649
127652
  subtitle: t("goal.setup_desc_hint"),
@@ -127664,7 +127667,7 @@ async function guidedGoalSetup(host) {
127664
127667
  await showGoalConfigWizard(host, session, confirmed.trim() || objective, false);
127665
127668
  }
127666
127669
  async function showGoalConfigWizard(host, session, objective, replace) {
127667
- const { TextInputDialogComponent } = await import("./text-input-dialog-BIXonNNb.mjs");
127670
+ const { TextInputDialogComponent } = await import("./text-input-dialog-DXLBq_rF.mjs");
127668
127671
  const turnInput = await promptNumber(host, TextInputDialogComponent, {
127669
127672
  title: t("goal.wizard_title", { objective }),
127670
127673
  subtitle: t("goal.budget_turns_hint"),
@@ -137658,6 +137661,51 @@ function shellQuote(path) {
137658
137661
  return `'${path.replaceAll("'", "'\\''")}'`;
137659
137662
  }
137660
137663
  //#endregion
137664
+ //#region src/tui/utils/ui-preferences.ts
137665
+ /**
137666
+ * TUI-local UI preferences persisted to `<dataDir>/ui-preferences.json`.
137667
+ * Distinct from agent/SDK config files (which are owned by core) — this file
137668
+ * stores lightweight view-state knobs owned by the TUI itself, e.g. whether
137669
+ * the empty-session provider hint has been dismissed by the user.
137670
+ */
137671
+ const EMPTY = {};
137672
+ function getUiPreferencesPath() {
137673
+ return join(getDataDir(), SCREAM_CODE_UI_PREFERENCES_FILE_NAME);
137674
+ }
137675
+ function readUiPreferences() {
137676
+ try {
137677
+ const file = getUiPreferencesPath();
137678
+ if (!existsSync(file)) return { ...EMPTY };
137679
+ const raw = readFileSync(file, "utf8");
137680
+ const parsed = JSON.parse(raw);
137681
+ return {
137682
+ ...EMPTY,
137683
+ ...parsed
137684
+ };
137685
+ } catch {
137686
+ return { ...EMPTY };
137687
+ }
137688
+ }
137689
+ function writeUiPreferences(prefs) {
137690
+ try {
137691
+ const file = getUiPreferencesPath();
137692
+ mkdirSync(dirname$1(file), { recursive: true });
137693
+ writeFileSync(file, JSON.stringify(prefs, null, 2), "utf8");
137694
+ } catch {}
137695
+ }
137696
+ function isEmptySessionHintDismissed() {
137697
+ return readUiPreferences().emptySessionHintDismissed === true;
137698
+ }
137699
+ /** Toggle the empty-session hint on/off (Ctrl+B). Returns the new state:
137700
+ * true = hidden, false = shown. Persisted immediately. */
137701
+ function toggleEmptySessionHint() {
137702
+ const prefs = readUiPreferences();
137703
+ const dismissed = prefs.emptySessionHintDismissed !== true;
137704
+ prefs.emptySessionHintDismissed = dismissed;
137705
+ writeUiPreferences(prefs);
137706
+ return dismissed;
137707
+ }
137708
+ //#endregion
137661
137709
  //#region src/tui/controllers/editor-keyboard.ts
137662
137710
  var EditorKeyboardController = class {
137663
137711
  host;
@@ -137711,6 +137759,18 @@ var EditorKeyboardController = class {
137711
137759
  }
137712
137760
  this.armPendingExit("ctrl-d", getCtrlDHint());
137713
137761
  };
137762
+ editor.onCtrlF = () => {
137763
+ if (host.state.transcriptEntries.length > 0) return false;
137764
+ if (isEmptySessionHintDismissed()) return false;
137765
+ openUrl(EMPTY_SESSION_HINT_URL);
137766
+ return true;
137767
+ };
137768
+ editor.onCtrlB = () => {
137769
+ if (host.state.transcriptEntries.length > 0) return false;
137770
+ toggleEmptySessionHint();
137771
+ host.forceUpdateStatusBar();
137772
+ return true;
137773
+ };
137714
137774
  editor.onEscape = () => {
137715
137775
  if (this.pendingExit) this.clearPendingExit();
137716
137776
  if (host.state.activeDialog === "session-picker") {
@@ -139430,6 +139490,7 @@ var SessionReplayRenderer = class {
139430
139490
  return false;
139431
139491
  } finally {
139432
139492
  this.host.setAppState({ isReplaying: false });
139493
+ this.host.forceUpdateStatusBar();
139433
139494
  }
139434
139495
  }
139435
139496
  hydrateSnapshot(agent) {
@@ -141968,6 +142029,7 @@ var TranscriptController = class TranscriptController {
141968
142029
  state.errorBanner.clear();
141969
142030
  imageStore.clear();
141970
142031
  this.renderWelcome();
142032
+ this.host.forceUpdateStatusBar();
141971
142033
  }
141972
142034
  showStatus(message, color) {
141973
142035
  this.host.state.transcriptContainer.addChild(new StatusMessageComponent(message, this.host.state.theme.colors, color));
@@ -142074,7 +142136,13 @@ var StatusBarPaneComponent = class extends Container {
142074
142136
  * swaps which children are visible. */
142075
142137
  update(options) {
142076
142138
  this.clear();
142077
- if (options.mode === "idle") return;
142139
+ if (options.mode === "idle") {
142140
+ if (options.label.length > 0) {
142141
+ const text = options.labelColor !== void 0 ? chalk.hex(options.labelColor)(options.label) : options.label;
142142
+ this.addChild(new Text(text, 1, 0));
142143
+ }
142144
+ return;
142145
+ }
142078
142146
  if (options.mode === "waiting" || options.mode === "tool" || options.mode === "composing" || options.mode === "thinking") {
142079
142147
  if (options.pulseWave !== void 0) {
142080
142148
  if (options.label.length > 0) {
@@ -142689,10 +142757,23 @@ var LifecycleController = class LifecycleController {
142689
142757
  break;
142690
142758
  case "thinking": break;
142691
142759
  case "idle":
142692
- case "hidden": break;
142760
+ case "hidden":
142761
+ if (mode === "idle" && state.transcriptEntries.length === 0 && !isEmptySessionHintDismissed()) state.statusBarContainer.addChild(new StatusBarPaneComponent({
142762
+ mode: "idle",
142763
+ label: t("editor.empty_session_hint"),
142764
+ labelColor: state.theme.colors.textDim
142765
+ }));
142766
+ break;
142693
142767
  }
142694
142768
  state.ui.requestRender();
142695
142769
  }
142770
+ /** Force a status-bar refresh even when the mode is unchanged. Used after
142771
+ * session switches/clears: the transcript is emptied, so the empty-session
142772
+ * hint must (re)appear even though idle -> idle normally short-circuits. */
142773
+ forceUpdateStatusBar() {
142774
+ this.lastActivityMode = void 0;
142775
+ this.updateActivityPane();
142776
+ }
142696
142777
  resolveActivityPaneMode() {
142697
142778
  const { state } = this.host;
142698
142779
  if (state.activeDialog !== null) return "hidden";
@@ -143857,7 +143938,7 @@ function imagePathBoundaryEnd(payload, segmentStart, extensionEnd) {
143857
143938
  function normalizePastedImagePath(path) {
143858
143939
  const trimmed = path.trim();
143859
143940
  const first = trimmed[0];
143860
- const last = trimmed[trimmed.length - 1];
143941
+ const last = trimmed.at(-1);
143861
143942
  return (trimmed.length > 1 && (first === "\"" || first === "'") && last === first ? trimmed.slice(1, -1) : trimmed).replace(SHELL_ESCAPED_PATH_CHAR_REGEX, "$1");
143862
143943
  }
143863
143944
  /**
@@ -143955,6 +144036,11 @@ var CustomEditor = class extends Editor {
143955
144036
  onEscape;
143956
144037
  onCtrlD;
143957
144038
  onCtrlC;
144039
+ /** Return true when the key was handled (consumed); false falls through to
144040
+ * the base editor's own binding (pi-tui maps Ctrl+F to cursorRight). */
144041
+ onCtrlF;
144042
+ /** Same contract as onCtrlF: true consumes the key, false falls through. */
144043
+ onCtrlB;
143958
144044
  onToggleToolExpand;
143959
144045
  onTogglePlanExpand;
143960
144046
  onOpenExternalEditor;
@@ -144115,6 +144201,12 @@ var CustomEditor = class extends Editor {
144115
144201
  this.onCtrlC?.();
144116
144202
  return;
144117
144203
  }
144204
+ if (matchesKey(normalized, Key.ctrl("b"))) {
144205
+ if (this.onCtrlB?.() ?? false) return;
144206
+ }
144207
+ if (matchesKey(normalized, Key.ctrl("f"))) {
144208
+ if (this.onCtrlF?.() ?? false) return;
144209
+ }
144118
144210
  if (matchesKey(normalized, Key.ctrl("g"))) {
144119
144211
  this.onOpenExternalEditor?.();
144120
144212
  return;
@@ -147167,6 +147259,7 @@ var DialogManager = class {
147167
147259
  this.host.state.editorContainer.addChild(panel);
147168
147260
  this.host.state.ui.setFocus(panel);
147169
147261
  this.host.state.ui.requestRender();
147262
+ this.host.forceUpdateStatusBar();
147170
147263
  }
147171
147264
  restoreEditor() {
147172
147265
  disposeChildren(this.host.state.editorContainer);
@@ -147174,6 +147267,7 @@ var DialogManager = class {
147174
147267
  this.host.state.ui.setFocus(this.host.state.editor);
147175
147268
  this.host.state.activeDialog = null;
147176
147269
  this.host.state.ui.requestRender();
147270
+ this.host.forceUpdateStatusBar();
147177
147271
  }
147178
147272
  showHelpPanel(commands) {
147179
147273
  this.host.state.activeDialog = "help";
@@ -147912,6 +148006,9 @@ var ScreamTUI = class {
147912
148006
  showStatus(message, color) {
147913
148007
  this.transcriptController.showStatus(message, color);
147914
148008
  }
148009
+ forceUpdateStatusBar() {
148010
+ this.lifecycleController.forceUpdateStatusBar();
148011
+ }
147915
148012
  showNotice(title, detail) {
147916
148013
  this.transcriptController.showNotice(title, detail);
147917
148014
  }
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-CyJPMDaK.mjs")).main();
9
+ (await import("./app-DB0yphR2.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);
@@ -892,6 +892,7 @@ const dictionaries = {
892
892
  "textinput.empty_hint": "输入不能为空。",
893
893
  "editor.auto_detect": "自动检测 ($VISUAL / $EDITOR)",
894
894
  "editor.select_title": "选择外部编辑器",
895
+ "editor.empty_session_hint": "Tips:推荐使用 OpencodeGo 作为模型提供商,可免费调用各种模型或超低价包月计划,按 Ctrl+F 获取 API,按 Ctrl+B 切换显示",
895
896
  "editorkey.cancel_compaction_failed": "取消压缩失败: {msg}",
896
897
  "editorkey.editor_not_configured": "未配置编辑器。请设置 $VISUAL / $EDITOR,或运行 /editor <命令>。",
897
898
  "editorkey.external_editor_failed": "外部编辑器失败: {msg}",
@@ -1967,6 +1968,7 @@ const dictionaries = {
1967
1968
  "textinput.empty_hint": "Input cannot be empty.",
1968
1969
  "editor.auto_detect": "Auto detect ($VISUAL / $EDITOR)",
1969
1970
  "editor.select_title": "Select external editor",
1971
+ "editor.empty_session_hint": "Tips: Use OpencodeGo as your model provider: free models or a low-cost plan. Ctrl+F: get an API key. Ctrl+B: toggle this tip.",
1970
1972
  "editorkey.cancel_compaction_failed": "Cancel compaction failed: {msg}",
1971
1973
  "editorkey.editor_not_configured": "Editor not configured. Set $VISUAL / $EDITOR, or run /editor <command>.",
1972
1974
  "editorkey.external_editor_failed": "External editor failed: {msg}",
@@ -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-9ywV88Lh.mjs";
6
+ import { t as TextInputDialogComponent } from "./text-input-dialog-CsPSlTrQ.mjs";
7
7
  export { TextInputDialogComponent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.12.8",
3
+ "version": "0.12.9",
4
4
  "description": "A terminal-native AI agent for builders",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",