jeo-code 0.9.14 → 0.9.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
@@ -1,6 +1,7 @@
1
1
  # Changelog
2
2
 
3
3
  All notable changes to **jeo-code** are documented here.
4
+
4
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
7
 
@@ -8,6 +9,20 @@ The README mirrors the latest 5 entries — regenerate with `bun run changelog:s
8
9
 
9
10
  ## [Unreleased]
10
11
 
12
+ ## [0.9.16] - 2026-08-18
13
+ _Routing looked broken while it was actually working: the idle status bar and `/compact`/`/handoff` never reflected which model `routePrompt` actually routed to._
14
+
15
+ ### Fixed
16
+ - **`/route on`'s footer/status bar and `/compact`/`/handoff` model budget kept showing the pre-routing default model instead of what was actually routed** (`src/commands/launch.ts`) — the idle status bar (`statusBarLine`) and `/compact`'s/`/handoff`'s `activeModel` all recomputed `sessionModel || defaultModel` from scratch every render, which has no idea what `routePrompt` actually picked for the last turn. With routing on, a turn would genuinely route to (and reply from) e.g. `claude-sonnet-5`, but the footer kept showing `claude-opus-5` (the unrouted default) the whole time — indistinguishable from routing silently doing nothing. Fixed by tracking `lastActiveModel` (the model that actually served the most recently completed turn) and reading it first everywhere the idle/compaction UI previously assumed `sessionModel || defaultModel` was the truth.
17
+
18
+
19
+ ## [0.9.15] - 2026-08-18
20
+ _The 0.9.14 fix wired `promptInput` into the `$a $b …` one-shot skill-chain's `io.input`, but that chain runs before the interactive REPL's own `readline.Interface` exists — so a bundled workflow skill (`$deep-interview`, with or without `--tmux`) invoked directly from the command line still froze forever on its first question._
21
+
22
+ ### Fixed
23
+ - **One-shot `$skill` workflow invocations (`jeo launch "$deep-interview …"`, incl. under `--tmux`) still hung on the first question** (`src/commands/launch.ts`) — `runOneSkillShot`'s pre-REPL dispatch (used for `jeo launch "$a $b … [intent]"` at startup, BEFORE the interactive REPL's `promptInput`/`readline.Interface` are constructed) referenced `promptInput` inside its `io.input` closure. The instant the workflow engine asked its first question, that reference threw a TDZ `ReferenceError: Cannot access 'promptInput' before initialization` — caught internally and stashed into session history (never printed to the terminal), so the visible symptom was a frozen prompt with no error, indistinguishable from the 0.9.14 hang. Fixed by omitting `input` from this pre-REPL `io` block entirely: no other `readline.Interface` exists yet at this point, so `deep-interview.ts`'s own fallback `createInterface({input: process.stdin, ...})` (used whenever `opts.io?.input` is absent) has nothing to fight over raw mode with — the same fallback the interactive `runSkillInvocation` sibling path never needed to touch, since it always ran after `promptInput` was already declared.
24
+
25
+
11
26
  ## [0.9.14] - 2026-08-04
12
27
  _A `$a $b … [intent]` skill-chain invocation of a bundled workflow skill (deep-interview/ralplan/team/ultragoal) hung forever with no error and no prompt._
13
28
 
package/README.ja.md CHANGED
@@ -287,10 +287,10 @@ CI は `.github/workflows/npm-publish.yml` で公開します — GitHub リリ
287
287
 
288
288
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
289
289
  - **[Unreleased]**
290
+ - **[0.9.16]** (2026-08-18) — Routing looked broken while it was actually working: the idle status bar and `/compact`/`/handoff` never reflected which model `routePrompt` actually routed to.
291
+ - **[0.9.15]** (2026-08-18) — The 0.9.14 fix wired `promptInput` into the `$a $b …` one-shot skill-chain's `io.input`, but that chain runs before the interactive REPL's own `readline.Interface` exists — so a bundled workflow skill (`$deep-interview`, with or without `--tmux`) invoked directly from the command line still froze forever on its first question.
290
292
  - **[0.9.14]** (2026-08-04) — A `$a $b … [intent]` skill-chain invocation of a bundled workflow skill (deep-interview/ralplan/team/ultragoal) hung forever with no error and no prompt.
291
293
  - **[0.9.13]** (2026-08-03) — The provider-login browser tab now actually goes away — by itself on macOS, by a working button everywhere else.
292
- - **[0.9.12]** (2026-08-02) — Fixes the boxed prompt's stolen keystrokes, drifting caret, and paste/attachment spacing under `jeo --tmux`.
293
- - **[0.9.11]** (2026-07-30) — Imports GJC v5 sessions safely, exposes bounded slash controls, and gates the release artifact before publication.
294
294
 
295
295
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
296
296
  <!-- CHANGELOG:END -->
package/README.ko.md CHANGED
@@ -288,10 +288,10 @@ CI는 `.github/workflows/npm-publish.yml`로 배포합니다 — GitHub 릴리
288
288
 
289
289
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
290
290
  - **[Unreleased]**
291
+ - **[0.9.16]** (2026-08-18) — Routing looked broken while it was actually working: the idle status bar and `/compact`/`/handoff` never reflected which model `routePrompt` actually routed to.
292
+ - **[0.9.15]** (2026-08-18) — The 0.9.14 fix wired `promptInput` into the `$a $b …` one-shot skill-chain's `io.input`, but that chain runs before the interactive REPL's own `readline.Interface` exists — so a bundled workflow skill (`$deep-interview`, with or without `--tmux`) invoked directly from the command line still froze forever on its first question.
291
293
  - **[0.9.14]** (2026-08-04) — A `$a $b … [intent]` skill-chain invocation of a bundled workflow skill (deep-interview/ralplan/team/ultragoal) hung forever with no error and no prompt.
292
294
  - **[0.9.13]** (2026-08-03) — The provider-login browser tab now actually goes away — by itself on macOS, by a working button everywhere else.
293
- - **[0.9.12]** (2026-08-02) — Fixes the boxed prompt's stolen keystrokes, drifting caret, and paste/attachment spacing under `jeo --tmux`.
294
- - **[0.9.11]** (2026-07-30) — Imports GJC v5 sessions safely, exposes bounded slash controls, and gates the release artifact before publication.
295
295
 
296
296
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
297
297
  <!-- CHANGELOG:END -->
package/README.md CHANGED
@@ -295,10 +295,10 @@ Huge thanks to [gajae-code](https://github.com/Yeachan-Heo/gajae-code) for the i
295
295
 
296
296
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
297
297
  - **[Unreleased]**
298
+ - **[0.9.16]** (2026-08-18) — Routing looked broken while it was actually working: the idle status bar and `/compact`/`/handoff` never reflected which model `routePrompt` actually routed to.
299
+ - **[0.9.15]** (2026-08-18) — The 0.9.14 fix wired `promptInput` into the `$a $b …` one-shot skill-chain's `io.input`, but that chain runs before the interactive REPL's own `readline.Interface` exists — so a bundled workflow skill (`$deep-interview`, with or without `--tmux`) invoked directly from the command line still froze forever on its first question.
298
300
  - **[0.9.14]** (2026-08-04) — A `$a $b … [intent]` skill-chain invocation of a bundled workflow skill (deep-interview/ralplan/team/ultragoal) hung forever with no error and no prompt.
299
301
  - **[0.9.13]** (2026-08-03) — The provider-login browser tab now actually goes away — by itself on macOS, by a working button everywhere else.
300
- - **[0.9.12]** (2026-08-02) — Fixes the boxed prompt's stolen keystrokes, drifting caret, and paste/attachment spacing under `jeo --tmux`.
301
- - **[0.9.11]** (2026-07-30) — Imports GJC v5 sessions safely, exposes bounded slash controls, and gates the release artifact before publication.
302
302
 
303
303
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
304
304
  <!-- CHANGELOG:END -->
package/README.zh.md CHANGED
@@ -287,10 +287,10 @@ CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release
287
287
 
288
288
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
289
289
  - **[Unreleased]**
290
+ - **[0.9.16]** (2026-08-18) — Routing looked broken while it was actually working: the idle status bar and `/compact`/`/handoff` never reflected which model `routePrompt` actually routed to.
291
+ - **[0.9.15]** (2026-08-18) — The 0.9.14 fix wired `promptInput` into the `$a $b …` one-shot skill-chain's `io.input`, but that chain runs before the interactive REPL's own `readline.Interface` exists — so a bundled workflow skill (`$deep-interview`, with or without `--tmux`) invoked directly from the command line still froze forever on its first question.
290
292
  - **[0.9.14]** (2026-08-04) — A `$a $b … [intent]` skill-chain invocation of a bundled workflow skill (deep-interview/ralplan/team/ultragoal) hung forever with no error and no prompt.
291
293
  - **[0.9.13]** (2026-08-03) — The provider-login browser tab now actually goes away — by itself on macOS, by a working button everywhere else.
292
- - **[0.9.12]** (2026-08-02) — Fixes the boxed prompt's stolen keystrokes, drifting caret, and paste/attachment spacing under `jeo --tmux`.
293
- - **[0.9.11]** (2026-07-30) — Imports GJC v5 sessions safely, exposes bounded slash controls, and gates the release artifact before publication.
294
294
 
295
295
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
296
296
  <!-- CHANGELOG:END -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jeo-code",
3
- "version": "0.9.14",
3
+ "version": "0.9.16",
4
4
  "description": "Clean, highly optimized AI coding agent using spec-first loop",
5
5
  "type": "module",
6
6
  "main": "src/cli.ts",
@@ -687,6 +687,19 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
687
687
  let sessionThinking: "low" | "medium" | "high" | "xhigh" | undefined = flags.thinking ?? cfg.thinkingLevel;
688
688
  // PromptRouter session override: undefined = follow config.routing.enabled, true/false = /route on|off wins this session.
689
689
  let sessionRouteOverride: boolean | undefined;
690
+ // Last model that actually served a completed turn (routed or pinned) — the idle
691
+ // status bar (`statusBarLine`) and `/compact`/`/handoff` read THIS instead of
692
+ // recomputing `sessionModel || defaultModel`, which ignored routing entirely and
693
+ // kept showing the pre-routing default even while routing was actively picking a
694
+ // different model turn-to-turn (e.g. "/route on" routing every prompt to
695
+ // claude-sonnet-5 while the footer kept showing claude-opus-5 — looked exactly
696
+ // like routing "wasn't working" even though it was). `undefined` until the first
697
+ // turn completes, so the initial idle render still falls back to `sessionModel ||
698
+ // defaultModel` unchanged.
699
+ let lastActiveModel: string | undefined;
700
+
701
+
702
+
690
703
  // Last routing decision (or the reason none applied) — /route why reads this.
691
704
  let lastRouteDecision: RouteDecision | { note: string } | null = null;
692
705
  // Bounded FIFO of this session's routing decisions — /route history reads this.
@@ -1929,7 +1942,9 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
1929
1942
  }
1930
1943
  }
1931
1944
 
1945
+ lastActiveModel = activeModel;
1932
1946
  return { done: result.done, steps: result.steps, reply, rendered: !!tui, usage };
1947
+
1933
1948
  };
1934
1949
 
1935
1950
 
@@ -2094,18 +2109,26 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
2094
2109
  output: (line: string) => {
2095
2110
  console.log(line);
2096
2111
  },
2097
- // Reuse the SAME readline interface the interactive REPL already owns
2098
- // (via promptInput) instead of letting the workflow engine spin up a
2099
- // second `readline.Interface` on process.stdin — deep-interview.ts only
2100
- // creates its own internal rl when `opts.io?.input` is absent, and two
2101
- // concurrent readline interfaces on one stdin silently fight over raw
2102
- // mode: no error, no visible prompt, the workflow just hangs forever.
2103
- // The sibling `runSkillInvocation` path already wires `promptInput`
2104
- // here; this `$a $b …` chain path must match it.
2105
- input: () => promptInput("")
2112
+ // This `$a $b …` one-shot chain runs BEFORE the interactive REPL's
2113
+ // `promptInput`/`rl` exist (this whole dispatch is reached and returns
2114
+ // long before the `const promptInput = …` further down the function
2115
+ // executes) — referencing `promptInput` here throws a TDZ
2116
+ // ReferenceError ("Cannot access 'promptInput' before initialization")
2117
+ // the instant the workflow engine asks its first question. That error
2118
+ // is caught and stashed into session history (never printed to the
2119
+ // terminal), so the visible symptom is just a frozen prompt forever
2120
+ // (the "jeo --tmux $deep-interview hangs" / "$deep-interview hangs"
2121
+ // report). Omitting `input` here is safe and correct: no other
2122
+ // readline exists yet at this point, so deep-interview.ts's own
2123
+ // fallback `createInterface({input: process.stdin, ...})` (used
2124
+ // whenever `opts.io?.input` is absent) has nothing to fight over raw
2125
+ // mode with. The INTERACTIVE `runSkillInvocation` sibling path below
2126
+ // runs AFTER `promptInput` is declared and correctly wires it — do not
2127
+ // "fix" this block by copying that wiring here again.
2106
2128
  },
2107
2129
 
2108
2130
  args: inv.skill.name === "deep-interview" ? (inv.intent ? inv.intent.split(/\s+/) : []) : undefined
2131
+
2109
2132
  };
2110
2133
 
2111
2134
  let ok = false;
@@ -3009,7 +3032,8 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
3009
3032
  // The gjc-layout status bar pinned directly ABOVE the input box: bg-gradient
3010
3033
  // identity block (model · thinking / branch / cwd) left, live ctx% right.
3011
3034
  const statusBarLine = (cols: number): string => {
3012
- const activeModel = sessionModel || defaultModel;
3035
+ const activeModel = lastActiveModel ?? (sessionModel || defaultModel);
3036
+
3013
3037
  const meta = catalogMetadata(activeModel);
3014
3038
  const used = historyTokens(history);
3015
3039
  const theme = uiTheme;
@@ -4313,10 +4337,16 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
4313
4337
  continue;
4314
4338
  }
4315
4339
  if (input === "/compact") {
4340
+
4316
4341
  // Session-start default snapshot (not the live global default) — keeps a
4317
4342
  // concurrent session's `/model` write from changing this session's context budget.
4318
- const activeModel = sessionModel || defaultModel;
4343
+ // Compaction budget should reflect the model that actually served the last
4344
+ // turn (routing may have picked something other than sessionModel/defaultModel),
4345
+ // not the pre-routing session-start snapshot — otherwise a routed session could
4346
+ // compact against the wrong context window.
4347
+ const activeModel = lastActiveModel ?? (sessionModel || defaultModel);
4319
4348
  const contextTokens = catalogMetadata(activeModel)?.contextTokens;
4349
+
4320
4350
  const res = await maybeCompact(history, { model: activeModel, force: true, contextTokens });
4321
4351
  if (res.error) {
4322
4352
  console.error(chalk.red(res.error));
@@ -4338,7 +4368,8 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
4338
4368
  // has no ACP/SDK-broker managed-session boundary to hand off INTO, so this
4339
4369
  // NEVER mutates history (see buildHandoffDocument's doc comment).
4340
4370
  const parsed = parseHandoffCommand(input) ?? {};
4341
- const activeModel = sessionModel || defaultModel;
4371
+ const activeModel = lastActiveModel ?? (sessionModel || defaultModel);
4372
+
4342
4373
  const cfg = await readGlobalConfig();
4343
4374
  // `Config` (state.ts) predates config-schema.ts's `compaction` field and is
4344
4375
  // out of this change's scope — read the passthrough-preserved value via a