jeo-code 0.5.14 → 0.5.16

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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  The README mirrors the latest 5 entries — regenerate with `bun run changelog:sync`.
8
8
 
9
+ ## [0.5.16] - 2026-06-16
10
+ _`/resume` and Ctrl+O no longer corrupt the TUI — clean screen restore + scrollback expand._
11
+
12
+ ### Fixed
13
+ - **`/resume` corrupted the screen on a TTY.** After picking a session the resumed transcript was dumped on top of whatever was on screen (picker remnants, the prior conversation, the live input frame), so replayed ANSI/forge boxes from the old session collided with the live layout. Resume now wipes the screen + scrollback and re-renders the welcome banner BEFORE replaying the transcript — the same proven path `/clear` uses — so the restored view is a single, intact screen (verified live: one input box, one status bar, no picker remnants).
14
+ - **Ctrl+O at the prompt crammed the last response into the ~10-row footer**, clipping long/CJK content with "… N more line(s)" and risking a garbled box. Ctrl+O now expands the full last assistant response into scrollback (clean `disarm → print → re-arm` path), so it is fully scrollable and the input box + typed draft restore without corruption. Removed the now-dead footer history-panel machinery (`promptHistoryLines`, `historyPreviewLines`).
15
+
16
+ ## [0.5.15] - 2026-06-16
17
+ _`jeo update` now actually upgrades — bare command installs the latest release instead of just printing a manual command._
18
+
19
+ ### Changed
20
+ - `jeo update` (bare) now performs the upgrade itself: when a newer release exists it runs the install (`bun install -g jeo-code@<latest>`, pinned to the resolved latest version so a stale global cache can't win) instead of printing "Run 'bun install -g jeo-code' to upgrade" and leaving the user to do it. `--check` is the new check-only mode; `--json` stays check-only for programmatic status (add `--install` to install in JSON mode); `--install` still forces an install. The check-only hint now points at `jeo update` itself.
21
+
9
22
  ## [0.5.14] - 2026-06-16
10
23
  _`jeo --tmux` live-verification harness — repeatable stability + behavior checks._
11
24
 
package/README.ja.md CHANGED
@@ -150,11 +150,11 @@ CI は `.github/workflows/npm-publish.yml` で公開します — GitHub リリ
150
150
  ## 変更履歴 (Changelog)
151
151
 
152
152
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
153
+ - **[0.5.16]** (2026-06-16) — `/resume` and Ctrl+O no longer corrupt the TUI — clean screen restore + scrollback expand.
154
+ - **[0.5.15]** (2026-06-16) — `jeo update` now actually upgrades — bare command installs the latest release instead of just printing a manual command.
153
155
  - **[0.5.14]** (2026-06-16) — `jeo --tmux` live-verification harness — repeatable stability + behavior checks.
154
156
  - **[0.5.13]** (2026-06-15) — Workflow `/` commands actually run — `/deep-interview`, `/team`, `/ultragoal`, `/ralplan` dispatch by name.
155
157
  - **[0.5.12]** (2026-06-15) — Yellow status animation while a process runs, and elapsed `(Nms)` on every completed tool card.
156
- - **[0.5.11]** (2026-06-15) — Backspace on an empty prompt line no longer quits jeo.
157
- - **[0.5.10]** (2026-06-15) — `/resume` transcript no longer dumps raw JSON for batched tool calls.
158
158
 
159
159
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
160
160
  <!-- CHANGELOG:END -->
package/README.ko.md CHANGED
@@ -150,11 +150,11 @@ CI는 `.github/workflows/npm-publish.yml`로 배포합니다 — GitHub 릴리
150
150
  ## 변경 이력 (Changelog)
151
151
 
152
152
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
153
+ - **[0.5.16]** (2026-06-16) — `/resume` and Ctrl+O no longer corrupt the TUI — clean screen restore + scrollback expand.
154
+ - **[0.5.15]** (2026-06-16) — `jeo update` now actually upgrades — bare command installs the latest release instead of just printing a manual command.
153
155
  - **[0.5.14]** (2026-06-16) — `jeo --tmux` live-verification harness — repeatable stability + behavior checks.
154
156
  - **[0.5.13]** (2026-06-15) — Workflow `/` commands actually run — `/deep-interview`, `/team`, `/ultragoal`, `/ralplan` dispatch by name.
155
157
  - **[0.5.12]** (2026-06-15) — Yellow status animation while a process runs, and elapsed `(Nms)` on every completed tool card.
156
- - **[0.5.11]** (2026-06-15) — Backspace on an empty prompt line no longer quits jeo.
157
- - **[0.5.10]** (2026-06-15) — `/resume` transcript no longer dumps raw JSON for batched tool calls.
158
158
 
159
159
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
160
160
  <!-- CHANGELOG:END -->
package/README.md CHANGED
@@ -150,11 +150,11 @@ Required npm token permissions (repository secret `NPM_TOKEN`):
150
150
  ## Changelog
151
151
 
152
152
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
153
+ - **[0.5.16]** (2026-06-16) — `/resume` and Ctrl+O no longer corrupt the TUI — clean screen restore + scrollback expand.
154
+ - **[0.5.15]** (2026-06-16) — `jeo update` now actually upgrades — bare command installs the latest release instead of just printing a manual command.
153
155
  - **[0.5.14]** (2026-06-16) — `jeo --tmux` live-verification harness — repeatable stability + behavior checks.
154
156
  - **[0.5.13]** (2026-06-15) — Workflow `/` commands actually run — `/deep-interview`, `/team`, `/ultragoal`, `/ralplan` dispatch by name.
155
157
  - **[0.5.12]** (2026-06-15) — Yellow status animation while a process runs, and elapsed `(Nms)` on every completed tool card.
156
- - **[0.5.11]** (2026-06-15) — Backspace on an empty prompt line no longer quits jeo.
157
- - **[0.5.10]** (2026-06-15) — `/resume` transcript no longer dumps raw JSON for batched tool calls.
158
158
 
159
159
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
160
160
  <!-- CHANGELOG:END -->
package/README.zh.md CHANGED
@@ -150,11 +150,11 @@ CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release
150
150
  ## 更新日志 (Changelog)
151
151
 
152
152
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
153
+ - **[0.5.16]** (2026-06-16) — `/resume` and Ctrl+O no longer corrupt the TUI — clean screen restore + scrollback expand.
154
+ - **[0.5.15]** (2026-06-16) — `jeo update` now actually upgrades — bare command installs the latest release instead of just printing a manual command.
153
155
  - **[0.5.14]** (2026-06-16) — `jeo --tmux` live-verification harness — repeatable stability + behavior checks.
154
156
  - **[0.5.13]** (2026-06-15) — Workflow `/` commands actually run — `/deep-interview`, `/team`, `/ultragoal`, `/ralplan` dispatch by name.
155
157
  - **[0.5.12]** (2026-06-15) — Yellow status animation while a process runs, and elapsed `(Nms)` on every completed tool card.
156
- - **[0.5.11]** (2026-06-15) — Backspace on an empty prompt line no longer quits jeo.
157
- - **[0.5.10]** (2026-06-15) — `/resume` transcript no longer dumps raw JSON for batched tool calls.
158
158
 
159
159
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
160
160
  <!-- CHANGELOG:END -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jeo-code",
3
- "version": "0.5.14",
3
+ "version": "0.5.16",
4
4
  "description": "Clean, highly optimized AI coding agent using spec-first loop",
5
5
  "type": "module",
6
6
  "main": "src/cli.ts",
package/src/cli/runner.ts CHANGED
@@ -165,7 +165,7 @@ export const COMMANDS: readonly CommandSpec[] = [
165
165
  },
166
166
  {
167
167
  name: "update",
168
- summary: "Check for (and install) a newer jeo-code release from the npm registry.",
168
+ summary: "Update jeo-code to the latest npm release (bare = install; --check only checks).",
169
169
  usage: "update [--check|--install] [--json] [--strict]",
170
170
  loader: async () => {
171
171
  const m = await import("../commands/update");
@@ -2274,7 +2274,6 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
2274
2274
  }
2275
2275
  };
2276
2276
  let previewPending = false;
2277
- let promptHistoryLines: string[] | null = null;
2278
2277
 
2279
2278
  // Inline boxed-footer rendering with a FIXED reservation (the "@-mention typing
2280
2279
  // pushes the box down" fix). The footer reserves its full `footerRows` height
@@ -2426,22 +2425,6 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
2426
2425
  const preview = (slash.length ? slash : args).map(l => chalk.gray(truncateAnsi(l, cols)));
2427
2426
  return [statusBarLine(cols), "", ...input, ...preview].slice(0, footerRows);
2428
2427
  };
2429
- const historyPreviewLines = (detail: string[]): string[] => {
2430
- const cols = Math.max(24, (process.stdout.columns ?? 80) - 1);
2431
- const title = `${uiAccent("history")} ${chalk.dim("· Ctrl+O closes")}`;
2432
- const budget = Math.max(0, footerRows - 2);
2433
- const physical = detail.flatMap(line => line.split("\n")).map(line => truncateAnsi(line, cols));
2434
- let body = physical;
2435
- if (physical.length > budget) {
2436
- const keep = Math.max(0, budget - 1);
2437
- body = physical.slice(0, keep);
2438
- body.push(chalk.gray(`… ${physical.length - keep} more line(s)`));
2439
- } else {
2440
- body = physical.slice(0, budget);
2441
- }
2442
- footerCursor = { row: Math.min(1, footerRows - 1), col: 1 };
2443
- return [statusBarLine(cols), title, ...body].slice(0, footerRows);
2444
- };
2445
2428
  const drawFooter = (lines: string[]) => {
2446
2429
  if (!previewArmed || footerRendered === 0) return;
2447
2430
  // ALWAYS paint exactly footerRendered rows so the reservation is fully covered
@@ -2888,23 +2871,24 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
2888
2871
  return;
2889
2872
  }
2890
2873
  if (!previewArmed || pickerActive) return;
2891
- // Ctrl+O: toggle a reversible history/detail panel. The live-turn TUI path
2892
- // uses LaunchTui.showDetail(); this idle-prompt path paints the same content
2893
- // into the fixed footer reservation, so the second Ctrl+O can close it.
2874
+ // Ctrl+O: expand the last assistant response into scrollback for a full,
2875
+ // scrollable read. The live-turn TUI path uses LaunchTui.showDetail(); this
2876
+ // idle-prompt path prints the same content above a cleanly re-armed footer.
2894
2877
  // (Cmd+O is intercepted by macOS/terminal and never reaches the app.)
2895
2878
  if (key?.ctrl && key.name === "o") {
2896
- if (promptHistoryLines) {
2897
- promptHistoryLines = null;
2898
- drawFooter(previewLines(typedLine, navIdx));
2899
- return;
2900
- }
2901
2879
  const detail = composeDetailLines();
2902
2880
  if (detail.length === 0) return;
2903
- promptHistoryLines = detail;
2904
- drawFooter(historyPreviewLines(detail));
2881
+ // Expand the last response into scrollback CLEANLY instead of cramming it
2882
+ // into the ~10-row footer reservation (which clipped long/CJK content and
2883
+ // could garble the box). Disarm → print full detail → re-arm + repaint is
2884
+ // the same path the resize handler/main loop use, so the input box and the
2885
+ // typed draft restore without corruption.
2886
+ disarmPreview();
2887
+ logLines(detail);
2888
+ armPreview();
2889
+ drawFooter(previewLines(typedLine, navIdx));
2905
2890
  return;
2906
2891
  }
2907
- if (promptHistoryLines) promptHistoryLines = null;
2908
2892
  // Ctrl+V: attach a clipboard IMAGE to the next message. Terminal text paste
2909
2893
  // never arrives as a ctrl+v keypress (it streams as plain stdin data), so this
2910
2894
  // binding is image-only; when the clipboard holds no image it's a silent no-op.
@@ -2947,7 +2931,6 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
2947
2931
  if (!previewArmed) return;
2948
2932
  try {
2949
2933
  if (key && (key.name === "return" || key.name === "enter")) {
2950
- promptHistoryLines = null;
2951
2934
  drawFooter([]);
2952
2935
  return;
2953
2936
  }
@@ -3008,7 +2991,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
3008
2991
  try {
3009
2992
  disarmPreview();
3010
2993
  armPreview();
3011
- drawFooter(promptHistoryLines ? historyPreviewLines(promptHistoryLines) : previewLines(typedLine, navIdx));
2994
+ drawFooter(previewLines(typedLine, navIdx));
3012
2995
  } catch { /* ignore resize render races */ }
3013
2996
  };
3014
2997
  process.stdout.on("resize", idleResizeHandler);
@@ -3221,6 +3204,16 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
3221
3204
  if (rli.history[0] !== p) rli.history.unshift(p);
3222
3205
  }
3223
3206
  }
3207
+ // Clean restore: wipe the screen + scrollback BEFORE replaying the
3208
+ // transcript so it can't collide with picker remnants, the prior
3209
+ // conversation, or the live input frame — the "/resume corrupts the
3210
+ // TUI" fix. Same proven path as /clear; re-render the welcome banner
3211
+ // so the resumed view reads like a fresh, intact screen.
3212
+ if (process.stdout.isTTY) {
3213
+ disarmPreview();
3214
+ process.stdout.write("\x1b[2J\x1b[3J\x1b[H");
3215
+ console.log(renderWelcome(welcomeData).join("\n"));
3216
+ }
3224
3217
  const sep = "─".repeat(Math.min(48, Math.max(20, (process.stdout.columns ?? 80) - 1)));
3225
3218
  logLines([
3226
3219
  sep,
@@ -35,7 +35,7 @@ export function compareVersions(a: string, b: string): -1 | 0 | 1 {
35
35
  export interface UpdateDeps {
36
36
  fetchJson: (url: string, options?: { signal?: AbortSignal }) => Promise<any>;
37
37
  localVersion: () => string;
38
- install: () => Promise<{ success: boolean; stdout?: string; stderr?: string }>;
38
+ install: (version?: string) => Promise<{ success: boolean; stdout?: string; stderr?: string }>;
39
39
  /** Display release notes after a successful self-update (best-effort, no-op in tests). */
40
40
  showWhatsNew?: () => void;
41
41
  }
@@ -58,8 +58,12 @@ export const defaultDeps: UpdateDeps = {
58
58
  localVersion: () => {
59
59
  return pkg.version;
60
60
  },
61
- install: async () => {
62
- const proc = Bun.spawnSync(["bun", "install", "-g", "jeo-code"], {
61
+ install: async (version?: string) => {
62
+ // Self-update the global install. jeo runs on Bun (see the `#!/usr/bin/env bun`
63
+ // shebang), so Bun is always present; `@<version>` (default `latest`) forces the
64
+ // newest publish even if a stale global is cached.
65
+ const target = `jeo-code@${version ?? "latest"}`;
66
+ const proc = Bun.spawnSync(["bun", "install", "-g", target], {
63
67
  stdout: "inherit",
64
68
  stderr: "inherit",
65
69
  });
@@ -83,6 +87,7 @@ export async function runUpdateCommand(args: string[] = []): Promise<void> {
83
87
  export async function runUpdateCommandWith(args: string[], deps: UpdateDeps): Promise<void> {
84
88
  const isHelp = args.includes("--help") || args.includes("-h");
85
89
  const hasInstall = args.includes("--install");
90
+ const hasCheck = args.includes("--check");
86
91
  const hasJson = args.includes("--json");
87
92
  const hasStrict = args.includes("--strict");
88
93
 
@@ -154,8 +159,13 @@ export async function runUpdateCommandWith(args: string[], deps: UpdateDeps): Pr
154
159
  }
155
160
  }
156
161
 
162
+ // Default action is INSTALL (bare `jeo update` upgrades). `--check` forces a
163
+ // check-only run; `--json` stays check-only too (programmatic status polling must
164
+ // not trigger an install) unless `--install` is given explicitly.
165
+ const shouldInstall = hasInstall || (!hasCheck && !hasJson);
166
+
157
167
  // We got the version successfully
158
- if (hasInstall) {
168
+ if (shouldInstall) {
159
169
  if (upToDate) {
160
170
  if (hasJson) {
161
171
  console.log(JSON.stringify({
@@ -165,14 +175,14 @@ export async function runUpdateCommandWith(args: string[], deps: UpdateDeps): Pr
165
175
  installed: false
166
176
  }));
167
177
  } else {
168
- console.log(`jeo-code is up-to-date (${current}). Skipping installation.`);
178
+ console.log(`jeo-code is already up-to-date (${current}).`);
169
179
  }
170
180
  } else {
171
181
  if (!hasJson) {
172
182
  console.log(`Installing update: ${current} -> ${latest}...`);
173
183
  }
174
184
  try {
175
- const result = await deps.install();
185
+ const result = await deps.install(latest ?? undefined);
176
186
  if (result.success) {
177
187
  if (hasJson) {
178
188
  console.log(JSON.stringify({
@@ -236,7 +246,7 @@ export async function runUpdateCommandWith(args: string[], deps: UpdateDeps): Pr
236
246
  }));
237
247
  } else {
238
248
  console.log(`Newer version available: ${latest} (current: ${current}).`);
239
- console.log("Run 'bun install -g jeo-code' to upgrade.");
249
+ console.log("Run 'jeo update' to install it.");
240
250
  }
241
251
  }
242
252
  }
@@ -248,8 +258,9 @@ function printUsage() {
248
258
  console.log("Check for and install updates for jeo-code.");
249
259
  console.log("");
250
260
  console.log("Options:");
251
- console.log(" --check Check for updates (default)");
252
- console.log(" --install Check and install if newer");
261
+ console.log(" (default) Check and install if a newer version is available");
262
+ console.log(" --check Only check; do not install");
263
+ console.log(" --install Force install if newer (also used with --json)");
253
264
  console.log(" --json Output result in JSON format");
254
265
  console.log(" --strict Exit with code 1 on network/registry errors");
255
266
  console.log(" -h, --help Show this help message");