scream-code 0.12.1 → 0.12.2

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.
@@ -54,7 +54,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
54
54
  import { Command, Option } from "commander";
55
55
  import { createInterface } from "node:readline/promises";
56
56
  import chalk, { chalkStderr } from "chalk";
57
- import { CombinedAutocompleteProvider, Container, Editor, Image, Input, Key, Markdown, ProcessTerminal, Spacer, TUI, Text, decodeKittyPrintable, deleteAllKittyImages, fuzzyFilter, fuzzyMatch, getCapabilities, getImageDimensions, isKeyRelease, matchesKey, setTightMode, sliceByColumn, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@liutod-scream/pi-tui";
57
+ import { CombinedAutocompleteProvider, Container, Editor, FlexSpacer, Image, Input, Key, Markdown, ProcessTerminal, Spacer, TUI, Text, decodeKittyPrintable, deleteAllKittyImages, fuzzyFilter, fuzzyMatch, getCapabilities, getImageDimensions, isKeyRelease, matchesKey, setTightMode, sliceByColumn, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@liutod-scream/pi-tui";
58
58
  import { highlight, supportsLanguage } from "cli-highlight";
59
59
  import { diffWords } from "diff";
60
60
  import { gt, valid } from "semver";
@@ -120898,7 +120898,7 @@ function optionalBuildString(value) {
120898
120898
  return typeof value === "string" && value.length > 0 ? value : void 0;
120899
120899
  }
120900
120900
  const SCREAM_BUILD_INFO = {
120901
- version: optionalBuildString("0.12.1"),
120901
+ version: optionalBuildString("0.12.2"),
120902
120902
  channel: optionalBuildString(""),
120903
120903
  commit: optionalBuildString(""),
120904
120904
  buildTarget: optionalBuildString("darwin-arm64")
@@ -124105,7 +124105,7 @@ function formatTokenCount$1(n) {
124105
124105
  return String(Math.round(n));
124106
124106
  }
124107
124107
  /**
124108
- * Build a `[███░░░░░░░]` style bar. Returns a plain-ASCII string with
124108
+ * Build a `███░░░░░░░` style bar. Returns a plain-ASCII string with
124109
124109
  * `filled`/`empty` glyphs — colouring is the caller's responsibility.
124110
124110
  */
124111
124111
  function renderProgressBar(ratio, width = 20, filled = "█", empty = "░") {
@@ -124153,20 +124153,18 @@ function currencySymbol(currency) {
124153
124153
  return `${currency} `;
124154
124154
  }
124155
124155
  /**
124156
- * Water-level progress bar for context usage: `[█████▓▒░░░]` (10 cells).
124156
+ * Water-level progress bar for context usage: `█████▓▒░░░` (10 cells).
124157
124157
  * Used cells read as water — solid depth (█) with a foam transition (▓▒)
124158
124158
  * hugging the water line; unused cells read as air (░). Cell count is
124159
124159
  * rounded from the clamped ratio, so 0% is all-air and >=100% is all-water;
124160
- * NaN/undefined coerce through safeUsageRatio first. The closing bracket is
124161
- * part of the returned string so a trailing background cell is never
124162
- * swallowed by the terminal.
124160
+ * NaN/undefined coerce through safeUsageRatio first.
124163
124161
  */
124164
124162
  function formatContextBar(usage, width = CONTEXT_BAR_WIDTH) {
124165
124163
  const clamped = Math.min(1, Math.max(0, safeUsageRatio(usage)));
124166
124164
  const filled = Math.round(clamped * width);
124167
124165
  const foam = Math.min(2, filled);
124168
124166
  const deep = Math.max(0, filled - foam);
124169
- return `[${CONTEXT_BAR_DEEP.repeat(deep) + CONTEXT_BAR_FOAM.slice(0, foam)}${CONTEXT_BAR_AIR.repeat(width - filled)}]`;
124167
+ return `${CONTEXT_BAR_DEEP.repeat(deep) + CONTEXT_BAR_FOAM.slice(0, foam)}${CONTEXT_BAR_AIR.repeat(width - filled)}`;
124170
124168
  }
124171
124169
  function formatContextStatus(usage, tokens, maxTokens, barWidth = CONTEXT_BAR_WIDTH) {
124172
124170
  const pct = `${(safeUsage(usage) * 100).toFixed(1)}%`;
@@ -140983,6 +140981,7 @@ var LifecycleController = class LifecycleController {
140983
140981
  ui.addChild(this.host.state.queueContainer);
140984
140982
  ui.addChild(this.host.state.errorBannerContainer);
140985
140983
  ui.addChild(this.host.state.planModeBannerContainer);
140984
+ ui.addChild(new FlexSpacer());
140986
140985
  ui.addChild(this.host.state.editorContainer);
140987
140986
  }
140988
140987
  mountFooter() {
@@ -143359,6 +143358,24 @@ function adaptQuestionAnswers(event, response) {
143359
143358
  //#endregion
143360
143359
  //#region src/tui/managers/session-manager.ts
143361
143360
  /**
143361
+ * How recently a session must have been touched for the empty-session pruner
143362
+ * to leave it alone. Guards against sweeping a fresh empty session that
143363
+ * another terminal is about to use.
143364
+ */
143365
+ const PRUNE_EMPTY_SESSION_GRACE_MS = 300 * 1e3;
143366
+ /**
143367
+ * A session is prunable when it never received a user prompt and never got a
143368
+ * title (auto-generated or custom) — i.e. an empty shell left behind by a
143369
+ * one-off startup. Archived sessions and sessions touched within the grace
143370
+ * window are kept.
143371
+ */
143372
+ function isPrunableEmptySession(summary, now, graceMs = PRUNE_EMPTY_SESSION_GRACE_MS) {
143373
+ if (summary.archived) return false;
143374
+ if (summary.lastPrompt !== void 0) return false;
143375
+ if (summary.title) return false;
143376
+ return now - summary.updatedAt > graceMs;
143377
+ }
143378
+ /**
143362
143379
  * Encapsulates all session lifecycle operations:
143363
143380
  * create / resume / switch / close / sync state / reset runtime.
143364
143381
  */
@@ -143412,12 +143429,34 @@ var SessionManager$1 = class {
143412
143429
  await this.syncRuntimeState(session);
143413
143430
  if (startup.wolfpack && !isResumeStartup) await session.setWolfpackMode(true);
143414
143431
  this.host.state.startupState = "ready";
143432
+ await this.pruneEmptySessions(workDir, session.id);
143415
143433
  this.host.sessionEventHandler.startSubscription();
143416
143434
  return {
143417
143435
  session,
143418
143436
  shouldReplay: shouldReplayHistory
143419
143437
  };
143420
143438
  }
143439
+ /**
143440
+ * Deletes sessions in this workdir that never received a user prompt and
143441
+ * were never renamed — repeated startups otherwise leave a growing pile of
143442
+ * one-off empty session shells. The session about to be used is skipped, as
143443
+ * are archived sessions, any session that produced a prompt or a title, and
143444
+ * any session touched within the grace window (protects a fresh empty
143445
+ * session being used from another terminal). Best-effort: cleanup failures
143446
+ * never block startup.
143447
+ */
143448
+ async pruneEmptySessions(workDir, currentSessionId) {
143449
+ try {
143450
+ const now = Date.now();
143451
+ const summaries = await this.host.harness.listSessions({ workDir });
143452
+ for (const summary of summaries) {
143453
+ if (summary.id === currentSessionId) continue;
143454
+ if (isPrunableEmptySession(summary, now)) await this.host.harness.deleteSession(summary.id);
143455
+ }
143456
+ } catch (error) {
143457
+ this.host.showStatus(`Session cleanup skipped: ${String(error)}`);
143458
+ }
143459
+ }
143421
143460
  async setSession(session) {
143422
143461
  await this.unloadCurrentSession("switching session")?.close({ extractMemories: false });
143423
143462
  this.host.session = session;
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-DZPgQXq3.mjs")).main();
9
+ (await import("./app-1A238nr4.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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "description": "A terminal-native AI agent for builders",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",
@@ -59,7 +59,7 @@
59
59
  "smoke": "node dist/main.mjs --version"
60
60
  },
61
61
  "dependencies": {
62
- "@liutod-scream/pi-tui": "^0.80.36",
62
+ "@liutod-scream/pi-tui": "^0.80.39",
63
63
  "@mariozechner/clipboard": "^0.3.2",
64
64
  "chalk": "^5.4.1",
65
65
  "cli-highlight": "^2.1.11",