jeo-code 0.5.4 → 0.5.6

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/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.6]** (2026-06-15) — `/model` sets only the default thinking; per-role reasoning moved to `/agents`.
154
+ - **[0.5.5]** (2026-06-15) — Full multi-line visibility — the input box scrolls to the caret and the submitted card shows every line.
153
155
  - **[0.5.4]** (2026-06-15) — Reliable multi-line input is ON by default — a paste fills the box and submits as one message.
154
156
  - **[0.5.3]** (2026-06-15) — `$` chains multiple skills in one line (all run, in order), plus multi-line prompt input — paste-merge and gated Shift+Enter.
155
157
  - **[0.5.2]** (2026-06-14) — `$skill` prompt invocation with prefix/fuzzy suggestions, and a per-session input-box hue (amber in cmd-mode).
156
- - **[0.5.1]** (2026-06-14) — cmd-mode `!<command>` shell escape — run a shell command without engaging the agent.
157
- - **[0.5.0]** (2026-06-14) — Performance: workspace-scan, workflow-state, and DNA-Claw HUD caches; plus a credential-safety fix that never wipes OAuth over an invalid config.
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.6]** (2026-06-15) — `/model` sets only the default thinking; per-role reasoning moved to `/agents`.
154
+ - **[0.5.5]** (2026-06-15) — Full multi-line visibility — the input box scrolls to the caret and the submitted card shows every line.
153
155
  - **[0.5.4]** (2026-06-15) — Reliable multi-line input is ON by default — a paste fills the box and submits as one message.
154
156
  - **[0.5.3]** (2026-06-15) — `$` chains multiple skills in one line (all run, in order), plus multi-line prompt input — paste-merge and gated Shift+Enter.
155
157
  - **[0.5.2]** (2026-06-14) — `$skill` prompt invocation with prefix/fuzzy suggestions, and a per-session input-box hue (amber in cmd-mode).
156
- - **[0.5.1]** (2026-06-14) — cmd-mode `!<command>` shell escape — run a shell command without engaging the agent.
157
- - **[0.5.0]** (2026-06-14) — Performance: workspace-scan, workflow-state, and DNA-Claw HUD caches; plus a credential-safety fix that never wipes OAuth over an invalid config.
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.6]** (2026-06-15) — `/model` sets only the default thinking; per-role reasoning moved to `/agents`.
154
+ - **[0.5.5]** (2026-06-15) — Full multi-line visibility — the input box scrolls to the caret and the submitted card shows every line.
153
155
  - **[0.5.4]** (2026-06-15) — Reliable multi-line input is ON by default — a paste fills the box and submits as one message.
154
156
  - **[0.5.3]** (2026-06-15) — `$` chains multiple skills in one line (all run, in order), plus multi-line prompt input — paste-merge and gated Shift+Enter.
155
157
  - **[0.5.2]** (2026-06-14) — `$skill` prompt invocation with prefix/fuzzy suggestions, and a per-session input-box hue (amber in cmd-mode).
156
- - **[0.5.1]** (2026-06-14) — cmd-mode `!<command>` shell escape — run a shell command without engaging the agent.
157
- - **[0.5.0]** (2026-06-14) — Performance: workspace-scan, workflow-state, and DNA-Claw HUD caches; plus a credential-safety fix that never wipes OAuth over an invalid config.
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.6]** (2026-06-15) — `/model` sets only the default thinking; per-role reasoning moved to `/agents`.
154
+ - **[0.5.5]** (2026-06-15) — Full multi-line visibility — the input box scrolls to the caret and the submitted card shows every line.
153
155
  - **[0.5.4]** (2026-06-15) — Reliable multi-line input is ON by default — a paste fills the box and submits as one message.
154
156
  - **[0.5.3]** (2026-06-15) — `$` chains multiple skills in one line (all run, in order), plus multi-line prompt input — paste-merge and gated Shift+Enter.
155
157
  - **[0.5.2]** (2026-06-14) — `$skill` prompt invocation with prefix/fuzzy suggestions, and a per-session input-box hue (amber in cmd-mode).
156
- - **[0.5.1]** (2026-06-14) — cmd-mode `!<command>` shell escape — run a shell command without engaging the agent.
157
- - **[0.5.0]** (2026-06-14) — Performance: workspace-scan, workflow-state, and DNA-Claw HUD caches; plus a credential-safety fix that never wipes OAuth over an invalid config.
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.4",
3
+ "version": "0.5.6",
4
4
  "description": "Clean, highly optimized AI coding agent using spec-first loop",
5
5
  "type": "module",
6
6
  "main": "src/cli.ts",
@@ -2753,17 +2753,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
2753
2753
  disabled: true,
2754
2754
  });
2755
2755
  appendChildren([
2756
- { value: `${role.id}:keep`, label: "Set model only", hint: `keep thinking ${roleThinking}` },
2757
- {
2758
- value: `${role.id}:inherit`,
2759
- label: "thinking inherit",
2760
- hint: `follow default (${config.thinkingLevel ?? "medium"})`,
2761
- },
2762
- ...levels.map(level => ({
2763
- value: `${role.id}:${level}`,
2764
- label: `thinking ${level}`,
2765
- hint: roleThinking === level ? "current" : `~${Math.round(thinkingMaxTokens(level) / 1000)}k tokens`,
2766
- })),
2756
+ { value: `${role.id}:keep`, label: "Set model only", hint: `keep thinking ${roleThinking} · set via /agents edit` },
2767
2757
  ]);
2768
2758
  }
2769
2759
 
@@ -3276,28 +3266,58 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
3276
3266
  continue;
3277
3267
  }
3278
3268
  if (input === "/resume" || input.startsWith("/resume ")) {
3279
- const id = input.substring(7).trim();
3280
- if (!id) {
3281
- const sessions = await listSessions(cwd);
3282
- if (sessions.length === 0) {
3283
- console.log("(no saved sessions)");
3284
- continue;
3285
- }
3286
- console.log("Saved sessions — resume with /resume <id>:");
3287
- for (const s of sessions.slice(0, 15)) {
3288
- const marker = s.id === sessionId ? "*" : " ";
3289
- console.log(` ${marker}${s.id} (${s.messageCount} msgs) ${s.title ? `[${s.title}] ` : ""}${s.preview}`);
3269
+ const arg = input.substring(7).trim();
3270
+ // Load a session into history and print its transcript so the resume is visible.
3271
+ const applyResume = async (rid: string): Promise<void> => {
3272
+ try {
3273
+ const { messages } = await loadSession(rid, cwd);
3274
+ history.length = 1;
3275
+ for (const m of messages) history.push(m);
3276
+ sessionId = rid;
3277
+ // Seed /retry + reply marker from the last user/assistant turn.
3278
+ lastUserInput = ""; lastReply = "";
3279
+ for (let k = history.length - 1; k >= 1; k--) {
3280
+ if (history[k]!.role === "user" && !lastUserInput) lastUserInput = String(history[k]!.content ?? "");
3281
+ if (history[k]!.role === "assistant" && !lastReply) lastReply = String(history[k]!.content ?? "");
3282
+ if (lastUserInput && lastReply) break;
3283
+ }
3284
+ const sep = "─".repeat(Math.min(48, Math.max(20, (process.stdout.columns ?? 80) - 1)));
3285
+ logLines([
3286
+ sep,
3287
+ `resumed session ${rid} · ${messages.length} message(s) (/history all for the full transcript)`,
3288
+ sep,
3289
+ ...formatTranscript(history, { maxTurns: 6, color: true, unicode: true }),
3290
+ sep,
3291
+ ]);
3292
+ } catch (err) {
3293
+ console.log(`! ${(err as Error).message}`);
3290
3294
  }
3295
+ };
3296
+ if (arg) { await applyResume(arg); continue; }
3297
+ // No id → only sessions with a real conversation are resumable (every launch
3298
+ // creates an empty session; those are noise).
3299
+ const sessions = (await listSessions(cwd)).filter(s => s.messageCount > 0);
3300
+ if (sessions.length === 0) {
3301
+ console.log("(no saved sessions with history)");
3291
3302
  continue;
3292
3303
  }
3293
- try {
3294
- const { messages } = await loadSession(id, cwd);
3295
- history.length = 1;
3296
- for (const m of messages) history.push(m);
3297
- sessionId = id;
3298
- console.log(`Resumed session ${id} (${messages.length} messages).`);
3299
- } catch (err) {
3300
- console.log(`! ${(err as Error).message}`);
3304
+ // Interactive arrow-key picker on a TTY: ↑↓ to move, Enter to resume, Esc cancels.
3305
+ if (process.stdin.isTTY && process.stdout.isTTY) {
3306
+ const items: SelectItem<string>[] = sessions.slice(0, 50).map(s => ({
3307
+ value: s.id,
3308
+ label: `${s.title ? `[${s.title}] ` : ""}${(s.preview || s.id).replace(/\s+/g, " ")}`.slice(0, 76) || s.id,
3309
+ hint: `${s.messageCount} msgs${s.id === sessionId ? " · current" : ""}`,
3310
+ }));
3311
+ const picked = await pickFromOptions("Resume a session ↑↓ move · Enter resume · Esc cancel", items);
3312
+ if (picked) await applyResume(picked);
3313
+ else console.log("(resume cancelled)");
3314
+ continue;
3315
+ }
3316
+ // Non-TTY fallback: static list (resume with /resume <id>).
3317
+ console.log("Saved sessions — resume with /resume <id>:");
3318
+ for (const s of sessions.slice(0, 15)) {
3319
+ const marker = s.id === sessionId ? "*" : " ";
3320
+ console.log(` ${marker}${s.id} (${s.messageCount} msgs) ${s.title ? `[${s.title}] ` : ""}${s.preview}`);
3301
3321
  }
3302
3322
  continue;
3303
3323
  }
@@ -3992,10 +4012,10 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
3992
4012
  let roleModelArg = (roleMatch[3] ?? "").trim();
3993
4013
  const roleThinking = /^(?:thinking|think)(?:\s+(\S+))?$/i.exec(roleModelArg);
3994
4014
  if (roleThinking) {
3995
- await setRoleThinking(role.id, roleThinking[1]);
4015
+ console.log(`Subagent thinking is set via /agents — try: /agents ${role.id} thinking ${roleThinking[1] ?? "<level|inherit>"} (or /agents edit). /model only sets the default thinking.`);
3996
4016
  continue;
3997
4017
  }
3998
- let roleModelPickedFromSelector = false;
4018
+
3999
4019
  if (!roleModelArg && process.stdin.isTTY && process.stdout.isTTY) {
4000
4020
  const live = await getLiveModels();
4001
4021
  lastPickIndex = flattenModels(live);
@@ -4007,7 +4027,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
4007
4027
  continue;
4008
4028
  }
4009
4029
  roleModelArg = qualifyModelId(picked.model, picked.provider);
4010
- roleModelPickedFromSelector = true;
4030
+
4011
4031
  }
4012
4032
  }
4013
4033
  if (roleModelArg && lastPickIndex.length) {
@@ -4019,7 +4039,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
4019
4039
  continue;
4020
4040
  }
4021
4041
  roleModelArg = qualifyModelId(sel.entry.model, sel.entry.provider);
4022
- roleModelPickedFromSelector = true;
4042
+
4023
4043
  } else if (sel.kind === "ambiguous") {
4024
4044
  console.log(`'${roleModelArg}' matches ${sel.matches.length} models — be more specific:`);
4025
4045
  for (const e of sel.matches.slice(0, 12)) console.log(` #${e.index} ${e.model} (${e.provider})`);
@@ -4033,20 +4053,9 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
4033
4053
  continue;
4034
4054
  }
4035
4055
  if (roleModelArg) {
4036
- let thinkPatch: { thinking?: ThinkLevel } = {};
4037
- if (roleModelPickedFromSelector && process.stdin.isTTY && process.stdout.isTTY) {
4038
- const cfgForRole = await readGlobalConfig();
4039
- const lvl = await pickThinkingLevel(
4040
- `Reasoning for ${role.title}: ${roleModelArg}`,
4041
- cfgForRole.subagents?.[role.id]?.thinking,
4042
- `inherit — follow default (${cfgForRole.thinkingLevel ?? "medium"})`,
4043
- );
4044
- thinkPatch = lvl === "inherit" ? { thinking: undefined } : lvl ? { thinking: lvl } : {};
4045
- }
4046
- await saveConfigPatch(raw => ({ subagents: withSubagentSetting(raw, role.id, { model: roleModelArg, ...thinkPatch }) }));
4056
+ await saveConfigPatch(raw => ({ subagents: withSubagentSetting(raw, role.id, { model: roleModelArg }) }));
4047
4057
  const { provider } = await describeModel(roleModelArg);
4048
- const thinkNote = thinkPatch.thinking ? ` · thinking ${thinkPatch.thinking}` : "";
4049
- console.log(`${role.title} model set to ${roleModelArg} (${provider})${thinkNote} — saved to ~/.jeo/config.json`);
4058
+ console.log(`${role.title} model set to ${roleModelArg} (${provider}) — saved to ~/.jeo/config.json. Set its thinking via /agents ${role.id} thinking <level> (or /agents edit).`);
4050
4059
  } else {
4051
4060
  const current = resolveSubagentModel(role.id, await readGlobalConfig());
4052
4061
  const { resolved, provider } = await describeModel(current);
package/src/tui/app.ts CHANGED
@@ -567,14 +567,12 @@ export class LaunchTui {
567
567
  const border = this.theme.color && uc ? chalk.hex(uc.border) : (s: string) => s;
568
568
  const shadow = this.theme.color && uc ? chalk.hex(uc.shadow) : border;
569
569
  const fill = this.theme.color && uc ? (s: string) => chalk.bgHex(uc.fill)(s) : (s: string) => s;
570
+ // Show ALL wrapped lines — the card lives in scrollback (not a bounded live frame),
571
+ // so a long submitted query stays fully visible (no truncation).
570
572
  const body = text
571
573
  .split("\n")
572
- .flatMap(line => wrapTextWithAnsi(line, Math.max(8, inner - 2)))
573
- .slice(0, 6);
574
- const clipped = body.length === 6 && text.split("\n").length > 6
575
- ? [...body.slice(0, 5), this.unicode ? "…" : "..."]
576
- : body;
577
- const rows = clipped.length ? clipped : [""];
574
+ .flatMap(line => wrapTextWithAnsi(line, Math.max(8, inner - 2)));
575
+ const rows = body.length ? body : [""];
578
576
  const top = border(g.tl + g.h.repeat(inner) + g.tr);
579
577
  const bottom = shadow(g.bl + g.h.repeat(inner) + g.br);
580
578
  const mid = rows.map(line => {
@@ -101,17 +101,23 @@ export function renderInputFrame(line: string, opts: InputBoxOptions = {}): Inpu
101
101
  ccol = wrapped.col;
102
102
  }
103
103
 
104
- // Tail-truncate to maxBodyRows (caret usually edits near the end); the first
105
- // visible row carries an `…` marker when earlier rows are hidden.
104
+ // Scroll the visible window so the caret row stays in view cursor movement reveals
105
+ // every line (no content is unreachable). `…` markers flag rows hidden above/below.
106
106
  const maxBodyRows = Math.max(1, Math.trunc(opts.maxBodyRows ?? rows.length));
107
+ const totalRows = rows.length;
107
108
  let hidden = 0;
108
- if (rows.length > maxBodyRows) {
109
- hidden = rows.length - maxBodyRows;
110
- rows = rows.slice(hidden);
111
- rows[0] = `…${rows[0] ?? ""}`.slice(0, textWidth);
109
+ if (totalRows > maxBodyRows) {
110
+ hidden = Math.min(Math.max(0, crow - maxBodyRows + 1), totalRows - maxBodyRows);
111
+ if (crow < hidden) hidden = crow; // caret above the window → scroll up to it
112
+ rows = rows.slice(hidden, hidden + maxBodyRows);
113
+ if (hidden > 0) rows[0] = `…${rows[0] ?? ""}`.slice(0, textWidth);
114
+ if (hidden + maxBodyRows < totalRows) {
115
+ const last = rows.length - 1;
116
+ rows[last] = `${rows[last] ?? ""}…`.slice(0, textWidth);
117
+ }
112
118
  }
113
119
  let visRow = Math.max(0, Math.min(crow - hidden, rows.length - 1));
114
- if (hidden > 0 && crow - hidden === 0) ccol += 1; // shifted by the `…` marker
120
+ if (hidden > 0 && crow - hidden === 0) ccol += 1; // shifted by the leading `…`
115
121
  if (crow - hidden < 0) { visRow = 0; ccol = 0; }
116
122
 
117
123
  const promptMark = "> ";