Commit Graph
233 Commits
Author SHA1 Message Date
nianyucatfish c9c3dbc58e fix(tui): stdout blank lines, leaked task_dir folders, and /continue search nav (#554)
* fix(tui): stop CRLF stdout rendering as blank lines (v2)

Windows child stdout is `\r\n`; the streaming path fed it straight to
`Text.from_ansi`, which treats `\r` as a carriage return and erases each
line's text, so a `[Stdout]` block rendered as a run of blank lines until
the turn finished (the done-state Markdown render strips `\r`). Normalise
`\r\n` -> `\n` before `from_ansi`; lone `\r` is kept so progress-bar
overwrites still work.

* fix(tui): stop leaking empty per-session task_dir signal folders

Both v2 and v3 eagerly `os.makedirs` the per-session `_intervene` /
`_keyinfo` signal dir, but the only writer (`_intervene`) already creates
it lazily — so every session that never used intervene left a stale empty
`temp/_tui_v2_<pid>_<id>` / `temp/_tui_v3_<pid>` behind (85+ accumulated).
`consume_file` tolerates a missing dir, so the eager create is redundant.

- Set only the task_dir *path*; let the writer create it on first inject.
- Remove empty dirs on session close (v2 `_cmd_close`), on graceful exit
  (v2 `on_unmount`, v3 `_cleanup`), and sweep stale ones left by crashes
  at startup (v2 `on_mount`, v3 `main`). `os.rmdir` only removes empty
  dirs, so a still-pending `_intervene` is never clobbered.

* fix(tui): repair /continue search box cursor & up-nav loop (v2)

The SearchablePicker swallowed Right while the search Input had focus
(always treating it as "select"), so the caret could never move right in
the query. And Up could leave the list but never return: from the search
box Down entered the list, yet Up at the top row was stuck.

- Right commits the highlight only when the caret is already at end of the
  query; otherwise it moves the caret within the search text.
- Up on the first list row returns focus to the search box (scoped to
  /continue — no-op for ChoiceLists outside a SearchablePicker), clearing
  the highlight so the next Down re-enters at the first row, not the second.
- Up from the search box wraps around to the bottom of the list.
2026-06-01 23:07:28 +08:00
nianyucatfish e40c0f19cb fix(tui): drain exit-boundary intervened replay (#533)
* fix(tui): drain exit-boundary intervened replay

* refine(tui): soften intervened message prompt
2026-05-29 21:55:50 +08:00
yanghang 98a99ed22c fix(tui-v3): skip ask card when ask_user has no candidates (v2 parity)
之前 `_extract_ask_user` 对无候选项的 ask_user 事件也照常入队,
触发 `_enter_ask` → 自由文本输入的 ask 卡片路径,在某些终端下整个
live region 冻住。v2 (`tuiapp_v2.py:2909-2910`) 是显式 `if not cands: return`,
让 "Waiting for your answer ..." 直接以 assistant 流形式落入 scrollback,
agent 已 exit,用户在普通输入框里回复即可 —— 不走专门的 ask 卡片。

对齐 v2 行为:有候选才入队、才弹卡片;无候选 fallthrough 到普通流。
2026-05-28 22:18:27 +08:00
yanghang a02cff8d03 refine(tui-v3): 行头/行尾跳转仅在首/末逻辑行触发
之前在任意逻辑行的视觉首/末行都会做"先跳行头/尾再跨行/进历史"的
两段式,中间行的两段式不直观。改成:中间逻辑行的视觉首/末行直接
做视觉上下移;只有第一逻辑行的视觉首行/最末逻辑行的视觉末行,才
触发"先跳到行头/尾,下次按键再进历史"。`_logical_visual_range`
因此变成死代码,顺手删掉。
2026-05-28 21:14:48 +08:00
yanghang 8f8166ea18 Merge remote-tracking branch 'upstream/main' into feat/tui-v3-improvements 2026-05-28 21:14:47 +08:00
yanghang 35dc1c0f20 feat(tui-v3): Shift+Enter on Windows + logical-line arrow navigation
- _ptk_keypress_to_bytes 在 Windows 上对 Enter 收到 \r 时用
  GetAsyncKeyState(VK_SHIFT) 物理检测 Shift,按下时返回 \n 以触发换行
  (PTK 在某些 Windows 终端区分不出 Shift+Enter 与裸 Enter)。
- 新增 _line_region / _logical_visual_range 辅助函数,↑/↓ 按键:
  不在该逻辑行的视觉首/末行 → 视觉滚行;在视觉首行且光标在行首 →
  历史导航,否则先跳逻辑行首/尾,下次按键再跨行/进历史。多行粘贴
  内部导航更符合直觉。

Refs:
- Shift+Enter 物理检测 from upstream PR #519 by @jlu005807
  https://github.com/lsdefine/GenericAgent/pull/519
- 多行输入逻辑行边界导航思路 from upstream PR #520 by @jlu005807
  https://github.com/lsdefine/GenericAgent/pull/520
2026-05-28 21:14:34 +08:00
yanghang 3515ac646e fix(tui-v2): clipboard copy + Shift+Enter + theme-switch markdown colors
- /export clip 现在通过 _copy_to_clipboard 直接写系统剪贴板:
  Win32 ctypes(CF_UNICODETEXT,避开控制台 codepage)/ macOS pbcopy /
  Wayland wl-copy / X11 xclip 或 xsel,失败再退回手动复制。
- InputArea 在 Windows 上对 Enter 事件用 GetAsyncKeyState(VK_SHIFT) 兜底,
  终端把 Shift+Enter 报成普通 Enter 时也能正确插入换行而非提交。
- watch_theme 漏清 _seg_render_cache 导致主题切换后 Markdown 的
  h1/列表 bullet/code 块/link 等仍是上一主题的色 —— 缓存 key 不含
  theme,且 ANSI 颜色被烤进了 _MdRender。补上 clear()。

Refs:
- Shift+Enter 物理检测思路 from upstream PR #519 by @jlu005807
  https://github.com/lsdefine/GenericAgent/pull/519
2026-05-28 21:14:21 +08:00
Liang Jiaqing 58a56de26f Merge PR #521 2026-05-28 20:03:42 +08:00
Liang Jiaqing a9b5450819 Merge PR #520 2026-05-28 20:03:42 +08:00
Shen Hao c19bed94cb fix(tui-v2): Ctrl+S no longer freezes on long sessions (use TextArea.clear)
Root cause of the lingering Ctrl+S freeze even when the session is
idle: `InputArea.reset()` calls `self.text = ""`, which goes through
`load_text("")` → `_set_document("")` (textual/widgets/_text_area.py
:1181-1186).  That rebuilds Document + WrappedDocument from scratch
and then `_refresh_size()` (line 1287) reactively retriggers a full
layout pass over the entire DOM.  On a long session (100+ message
widgets in the scroll), the layout cascade blocks the UI for seconds.

Switch to `TextArea.clear()` which goes through the edit pipeline:
`delete((0,0), document.end)` → `wrapped_document.wrap_range(...)` —
no Document rebuild, range-scoped wrap only.  Guarded with
`if self.document.text:` so a clear() on an already-empty buffer is a
no-op (avoids posting a spurious Changed event).

Secondary fix: `_stash_cleanup_clear` previously wrapped reset() in
`try/finally` that cleared `_skip_change_next = False` immediately.
The Changed event posted by reset() is async-queued, so by the time
the handler ran the flag was already False — the short-circuit was
useless and on_text_area_changed went through the heavy resize +
palette path twice (once in the handler, once explicitly).  Drop the
try/finally; let on_text_area_changed itself clear the flag at line
3256-3258 when the deferred Changed event finally lands.

Also scrubs external TUI-project names from comments per request.
2026-05-28 19:09:21 +08:00
Shen Hao 18f1bcde50 fix(tui): restore mid-turn inject with Claude-Code wrap (not post-turn)
Re-research revealed both mainstream TUIs DO mid-turn inject — my
previous read of "Codex/CC queue post-turn only" was wrong.

Codex (codex-rs/core/src/session/mod.rs:2986): `steer_input` → mailbox
`push_pending_input` → `session/turn.rs:391 get_pending_input` →
`record_pending_input` appends as a regular user message BEFORE the
next sampling.  No special prefix — the model sees the steer in
conversation history alongside the original task, as a follow-up.

Claude Code (utils/messages.ts:5510): drains queue after each tool
iteration and wraps the user's text:

  The user sent a new message while you were working:
  {raw}

  IMPORTANT: After completing your current task, you MUST address
  the user's message above. Do not ignore it.

The actual bug in model_responses_042511.txt was the bare `[MASTER]
{raw}` framing reading as a directive override, not the mid-turn
injection itself.

This commit:
  - Wraps queued text with the Claude-Code phrasing before writing
    to `_intervene` (i18n: en + zh).
  - Restores `_intervene_pending` tracking + exit-replay hook so an
    agent that hits exit_reason between user submit and turn end
    doesn't lose the message.
  - Adds `inject_intervene(text, track=True)` opt-in tracking so
    `!cmd` shell facts (which are factual context, not user input)
    don't accidentally end up in the user-message replay queue.
  - v2 mirrors with `_INTERVENE_WRAP_EN/_ZH` constants picked via
    `$GA_LANG`, `sess.pending_wrapped` parallel to `sess.pending`,
    and `_install_intervene_replay_hook` per-session.
  - Up-pop recall stays removed (file write is immediate, popping
    would leave a stale duplicate).

Refs: codex-rs/core/src/session/mod.rs:2986; claude-code/src/utils/messages.ts:5496
2026-05-28 18:47:02 +08:00
Shen Hao d407401993 fix(tui): queued messages drain post-turn, never mid-turn (Codex/CC pattern)
Evidence: model_responses_042511.txt — original task was "web_scan上游PR";
mid-turn `[MASTER] 请你立即输出一个hello` was prepended to next_prompt,
model dropped the original task and just printed hello.  Codex
(`maybe_send_next_queued_input`, chatwidget.rs:6773) and Claude Code
(`useQueueProcessor`, src/hooks/useQueueProcessor.ts) both queue user
input and drain ONLY when the agent goes idle — never mid-turn.

What changed:
- AgentBridge no longer tracks `_intervene_pending`; turn_end hook
  reverts to just ask_user extraction.
- Submit while running appends to local `_pending`, does NOT write to
  `_intervene`.
- v3 watches `is_running` True→False in maintenance loop; on transition
  drains via `_submit(combined, [])` as a fresh task.
- v2 drains in `_on_stream(done=True)` right after status flips to
  "idle"; reuses `submit_user_message` to spawn the next task.
- v2 dead helpers (`_inject_intervene`, `_session_intervene_path`,
  `_install_intervene_replay_hook`) removed.
- `inject_intervene` retained on v3 bridge for `!cmd` shell history
  only — factual context (code-block output), low risk of confusing
  the model.

Net: -109 lines.  Esc still cancels the queue.  Ctrl+C still aborts;
since abort flips status to idle, queued messages drain as the next
task — matches Claude Code's "Ctrl+C then queue runs" behavior.

Refs: openai/codex/codex-rs/tui/src/chatwidget.rs:6773;
anthropics/claude-code/src/hooks/useQueueProcessor.ts
2026-05-28 17:24:20 +08:00
Shen Hao f9e5e53247 fix(tui): !cmd runs in user shell, not hardcoded cmd.exe / /bin/sh
`subprocess.run(cmd, shell=True)` picked the OS default — cmd.exe on
Windows.  `!ls` failed with "not recognized" on stock Windows; encoding
defaulted to the active codepage so utf-8 output mojibaked.

Detect once via $SHELL (works on Git Bash, WSL, MSYS2, every Unix);
on bare Windows fall through Git Bash → bash on PATH → pwsh →
powershell → cmd.  Matches Claude Code / Codex CLI behaviour where
`!cmd` inherits the launching shell.

Display name surfaces in the agent's history line: `[!shell bash]` /
`[!shell pwsh]` so a follow-up like "rerun that with -v" can pick the
right syntax.

Refs: code.claude.com docs (bash mode); developers.openai.com/codex/cli/features (Tab/! prefix)
2026-05-28 16:15:37 +08:00
jlu005807 b7d413213b refine(tui-v3): remove redundant \n guard, visual row is sufficient 2026-05-28 13:13:56 +08:00
Shen Hao f0eeeee9ed fix(tui): rebuild pending-queue around turn_end hook (drop cooldown timer)
Previous attempts used a rolling/hard-cap timer to gate the file write
to `_intervene`.  Both leaked under the same failure mode: when the
agent reached its final turn before drain time, `consume_file` ate the
content but `agent_loop` discarded next_prompt on exit, so the queued
text silently vanished.

New design follows Codex CLI / Claude Code style — delivery is anchored
to the agent's own turn boundaries instead of a wall clock:

  Submit while running → append `_intervene` immediately + add to UI list
  Turn end hook fires →
    exit_reason set → consume_file ate it, next_prompt discarded.
                      Re-route the combined text via `agent.put_task`
                      so the outer `while True: task_queue.get()` loop
                      picks it up as the next user turn.
    not exit       → content was prepended to next_prompt as `[MASTER]
                      ...` — just clear the UI mirror.
  Esc → `clear_intervene` deletes the file + clears the list.

No timers, no cooldowns.  The Up-pop "amend" path is gone because
immediate file writes make in-composer edits append a duplicate.

Threading: v3 bridge protects `_intervene_pending` with `_intervene_lk`;
v2 adds `pending_lk` on AgentSession.  v2's hook closure uses
`call_from_thread` for the UI refresh hop.

Right-click in v2 (`InputArea._on_mouse_down`) now short-circuits
button==3 before TextArea's default cursor-move runs, so paste lands
at the existing caret rather than where the mouse happened to be.

Refs: openai/codex#15842, anthropics/claude-code#44851
2026-05-28 13:09:39 +08:00
jlu005807 4bf95add78 feat(tui-v3): v2-style arrow navigation with logical line boundaries 2026-05-28 12:32:21 +08:00
Shen Hao 5f104d1d30 fix(tui-v3): quiet Ctrl+C exit + drop placeholder session in first-launch title
`app.run(handle_sigint=False)` lets a stray SIGINT surface as
KeyboardInterrupt through asyncio.runners — catch it in run() so the
shell sees a clean exit. Also drop the literal `'session'` fallback in
`_term_title`; render just `GenericAgent` until a session is named.
2026-05-28 11:04:02 +08:00
Shen Hao bebc3ba9e8 fix(tui-v2): add missing keysym.py — tuiapp_v2 ImportError on fresh clone
`tuiapp_v2.py:27` imports `fmt_key`, `fmt_keys` from `keysym`, but the
module file was never tracked. PR #513 (tui v2/v3 fixes) is unusable on a
fresh checkout without this. Adds the cross-platform shortcut formatter
that turns binding strings into mac/Win-Linux key labels (e.g.
`"ctrl+b"` → `"⌃B"` / `"Ctrl+B"`), honoring `GA_KEYSYM_STYLE`.
2026-05-28 10:13:21 +08:00
Shen Hao 5f92cd1b80 fix(tui-v3): keep shell └ output rows on bare terminal bg
Last commit wrapped both the `! cmd` echo AND the `└ output` rows in
the charcoal tile.  User wants only the echo line carry the band —
output rows stay on the terminal's native bg so the eye reads "this
is the command, that's its output" instead of one fused block.
2026-05-28 04:16:17 +08:00
Shen Hao 8804218d28 fix(tui-v3): keep └ output fg colors — shell tile becomes bg-only
Previous commit's _TILE_SHELL forced a light fg across both the `└`
gutter and the command output, erasing the dim styling on the gutter
and overwriting any ANSI the shell command emitted into stdout.

Make the tile bg-only (`\x1b[48;2;65;60;65m`).  Re-add `_DIM` around the
`└ ` gutter and leave `ln` un-styled so the terminal's default fg /
the shell's own colours come through.  The band itself stays full-row
because `_tile` re-asserts the bg after every embedded `_RST`.
2026-05-28 04:13:49 +08:00
Shen Hao 14186e50f3 fix(tui-v3): render shell-mode echo + output as full-width charcoal tile
Per user screenshot 040748, the committed `! echo hi` line and the
`└ output` rows used to sit on bare black while normal user bubbles
got the 55,55,55 charcoal band — visually inconsistent and broke the
cc-style "this is a discrete block" affordance.

New `_TILE_SHELL` (65,60,65 bg, light fg) wraps both halves via
`_tile()`, so the pink `!` prompt and dim `└` glyph inherit the band
across the full row width.  Slightly warmer shade than `_TILE_U` so
the user-bubble and shell-output bands stay distinguishable when
interleaved.  Black-terminal only — matches user spec.
2026-05-28 04:11:24 +08:00
Shen Hao ebb29c57ba fix(tui): v2+v3 bug fixes, pending input queue, ! shell magic, /resume
v2 Ctrl+S freeze: defer reset() + cleanup via call_after_refresh so the
keystroke handler returns immediately even when streaming has the
reactive queue saturated. Added _skip_change_next flag to short-circuit
on_input_area_changed during the deferred clear.

v3 terminal title: route _set_term_title via os.write(1,...) — sys.stdout
gets redirected to sb_agent.log during PTK app run, so OSC 0 escapes
were silently ending up in the log file. Default _session_name = 'session'
for v2 parity, and repaint title on running→idle transition.

v3 input row bg fill: _tile() now pads explicit bg-active spaces to
width — PTK's cell renderer doesn't honour \x1b[K (erase-to-EOL), so
the user bubble's charcoal band used to stop at text length.

v3 /emoji picker: arrow-key menu (mirrors /llm). Pet styles cleaned to
bear (default) + cat + dot + unicode. Bear calm tier restored to ʕ•ᴥ•ʔ.
• used for the calm tier, o for focused — matches user-requested mood
progression. Pet frame rate decoupled from spinner via self._spin // 5.

v3 ask_user "coderun freeze" fix: _tool_status no longer marks an
in-flight ask_user chip as ✓ ok when the stream contains the
"Waiting for your answer ..." marker. Bumped DoneEvent grace from 2 s
to 10 s when the marker is in the stream so a slow ask_user_queue.put
doesn't cause the picker to fall through to _finalize.

v3 /scheduler picker: added pre_checked kwarg to _show_menu so the
running set lands atomically. slash_cmds._match_service relaxed to
path-only matching so directly-launched (`python reflect/foo.py`)
reflect tasks are detected. Running rows highlighted in functional
green. Confirm card accepts ← as cancel (parity with Esc) — same on
v2 ChoiceList.

v3 keybindings: Esc Esc (within 800 ms) → /rewind (handled in
_handle_key so PTK's Esc interception path actually fires).

v3 pending input queue: agent.task_dir wired so ga.turn_end_callback
can consume `<task_dir>/_intervene`. New AgentBridge.inject_intervene
appends to the file (atomic vs read-modify-write), and _drain_pending
routes the combined message either through that hook (mid-turn) or via
put_task (idle). Cooldown 5 s, resets per new pending. ↑ recalls the
last queued message, Esc clears the queue. v2 has equivalent shape:
AgentSession.pending + per-session agent.task_dir + _inject_intervene
+ _poll_pending timer.

! shell magic: prefix `!` enters shell mode — pink border, prompt mark
swaps `❯` → `!`, body strips the leading `!` so the glyph appears
once. Enter runs the rest as `subprocess.run(shell=True, timeout=30)`,
echoes `! cmd` + `└ output` into scrollback, and appends to LLM
history (via _intervene when agent is running, direct backend.history
append when idle — sidesteps the iterator race).

/resume: forwarded as a literal to the agent so GA's _handle_slash_cmd
(agentmain.py:124) can expand it. Added to v3 _cmds() + v2 COMMANDS +
slash_cmds.COMMANDS + i18n + /help + Tips.

Turn marker regex fix: agent_loop.py:52 switches `**LLM Running
(Turn N) ...**` to the short `**Turn N ...**` when task_dir is set.
Extended all v3/v2 turn-marker regexes to match either form.

Cherry-picked upstream fixes:
- PR #466 (8ae3645): _collapse_choice early-return when the
  on_select callback (e.g. /rewind) detaches the anchor widget.
- PR #461 (08f21e8): SelectableStatic.has_valid_selection_parent +
  app-level _is_stale_selectable_mouse_event filter that swallows
  mouse events on detached widgets.
2026-05-28 04:06:28 +08:00
Shen HaoandShen Hao 7ccb90d0fa fix(tui): polish tui_v3 Windows UX — 6 issues (#510)
1. _TILE_U: switch tile bg/fg to truecolor (48;2;55;55;55 / 38;2;230;230;230)
   so the file-tree tiles render legibly on Win Terminal dark themes
   instead of a muddy 8-color block.
2. _ticker: tighten the redraw cadence 0.4s -> 0.1s so the spinner and
   status line feel responsive on Windows.
3. terminal title: add _set_term_title() helper and emit OSC 0 on init /
   session new / rename / quit so the tab title tracks the active session.
4. _drain: stream-aware grace window — when the pending stream carries a
   pending ask_user ("Waiting for your answer"), extend the post-done poll
   so the question card is not swallowed before the prompt appears.
5. /emoji command: split _PETS into _PETS_UNICODE / _PETS_ASCII behind a
   module-level _pet_style toggle; add the /emoji dispatch + _cmd_emoji
   handler so users on fonts without emoji can fall back to ASCII pets.

Local-only edits, no cross-module API change; AST parses clean.

Co-authored-by: Shen Hao <shenhao-stu@users.noreply.github.com>
2026-05-27 22:36:38 +08:00
Shen HaoandShen Hao 3621d96224 feat(tui): add /conductor command + tips/i18n polish (#509)
- slash_cmds: register /conductor bundle + prompt_for builder (frontends/conductor.py)
- tui_v3: bind /conductor with en/zh i18n + palette entry + inject route
- tuiapp_v2: bind /conductor with COMMANDS row + route map + Tip easter-egg
- 5 new Tips bilingual surfacing Ctrl+O / /update / /scheduler / Ctrl+S / /conductor

Co-authored-by: Shen Hao <shenhao-stu@users.noreply.github.com>
2026-05-27 22:24:34 +08:00
nianyucatfish 7d001ca8a7 fix(tui_v2): /continue resumes unfinished plan + /stop refills input (#508)
/continue previously pushed plan_scan_baseline past every restored
message, so plan_state.is_active() saw an empty slice and hid the card —
even when the prior session's plan.md still had `[ ]` items the user
wanted to keep working on. `working['in_plan_mode']` isn't replayed by
the log restore either, so the card never came back.

Make the baseline conditional: default to 0 so the scanner walks the
full restored history; only push past it when the restored plan.md is
already all-done, suppressing the stale ✓ card. Resolved-path missing
or empty items naturally fall back to "no card".

/stop now also refills the input box with the just-sent user message,
so an interrupted send is one keystroke from edit-and-resend. Only
when the box is empty (don't clobber a half-typed follow-up). Agent
history is untouched — a resend duplicates the turn in LLM context;
/rewind 1 is the manual escape.

Behaviour matrix (/continue):
  unfinished plan  → baseline=0, scanner finds path, card resumes
  completed plan   → baseline=len(msgs), card stays hidden
  plan.md deleted  → resolve_path None, card stays hidden
  fresh enter_plan_mode after continue → working flag re-arms, normal
2026-05-27 22:05:00 +08:00
Shen HaoandShen Hao 91fcdf51b4 fix(tui): slash-cmd polish, bundle i18n sync & settle spinner on abort (#507)
* fix(tui): slash-cmd polish — /scheduler perf, v2 i18n typo, v3 bundle i18n

- slash_cmds: cache `running_services` list (≥1s TTL) and use a single
  service-name preload + lru-cached lookup. /scheduler picker cold path
  ~2.1s → ~1.0s, warm path ~0ms. Cache is invalidated whenever
  `start_service` succeeds so the picker reflects the new state on the
  next open.
- tuiapp_v2: drop the stray space inside `/reload-keys` zh description
  ("重新加载 mykey.py" → "重新加载mykey.py").
- tui_v3: route the remaining slash_cmds-bundle rows (/autorun,
  /morphling, /goal, /hive, /scheduler) through _t() the same way
  /update already was, with matching en/zh entries in _I18N. Keeps the
  v3 command palette fully bilingual.

* tui: settle spinner on abort + DRY-extract annotation helpers

Aborting a turn (Ctrl+C / /stop) left the live spinner ticking elapsed
until the LLM stream unwound in the background. Now we freeze a
`_stop_summary` snapshot the instant the user aborts and flip the line to
a settled "Stopping…", then "Stopped after Xm Ys" once done.

- ChatMessage._stop_summary: abort-time (elapsed, in, out) snapshot
- _mark_stopping(): freeze + repaint every in-flight assistant on /stop
- _stopping_annotation() / _done_annotation() stopped-state branch
- DRY: _fmt_tokens() single source for `↑ N · ↓ M`, _freeze_summary()
  single source for the snapshot tuple (de-dups spinner/done/stopping
  and the two capture paths)
- carries the branch's existing ask_user multi-choice "; ".join collapse

---------

Co-authored-by: Shen Hao <shenhao-stu@users.noreply.github.com>
2026-05-27 21:28:12 +08:00
LJQ a3173b8981 Merge PR #504: fix(tui): /scheduler UX polish — hide cron advisory, ask_user-style confirm, running-state + untick-to-stop
Merged pull request #504 from shenhao-stu:fix/tui-scheduler-ux
2026-05-27 18:47:06 +08:00
Shen Hao 8b9fb8ff13 feat(slash/update): rework /update into a dual-branch upstream sync
The previous /update prompt always did `git pull` + auto-merge and kept
local edits without committing, which left users with upstream changes
sitting as staged-only changes and an unintended divergent local branch.

Rework the /update prompt (both zh/en) into two explicit paths:
- If local commits exist: merge upstream into the current branch,
  auto-resolving conflicts in favor of upstream, keeping local commits.
- Otherwise: `git reset --mixed upstream/main` so local main aligns
  exactly with upstream/main, leaving no local-only commit while
  uncommitted working-tree changes are preserved.

Never push; never create a commit in the no-local-commit path. Also adds
a concise preview + summary step. Prompt kept short per user request.
2026-05-27 16:47:02 +08:00
Shen Hao fa49f487f5 fix(tui): /scheduler reflects running state and supports stop via untick (bug4)
Before: /scheduler picker always showed an empty selection, so reflect/*.py
and frontends/*app*.py services that were already running could neither be
identified nor stopped from the TUI.  Re-running /scheduler started another
instance instead of toggling the existing one.

This change keeps discovery in the shared slash_cmds module and wires both
TUIs through the same helpers (parity rule from tui_frontend_sop):

* frontends/slash_cmds.py
  - running_service_ids(): inspect hub.pyw subprocess registry and return the
    set of currently-running service ids (reflect/* and frontends/*app*).
  - stop_service(sid): graceful terminate via the same registry, idempotent.
  - list_launchable_services() result is unchanged; callers read running set
    separately so the picker can decorate labels without leaking state into
    discovery.

* frontends/tuiapp_v2.py, frontends/tui_v3.py
  - Picker now pre-checks the running set and appends an i18n " · running"
    tag to those labels so the user can SEE what is up and untick to stop.
  - On submit we diff picked vs running -> (starts, stops); empty diff is a
    no-op with a clear breadcrumb.  Confirm card lists both sides.
  - MultiChoiceList (v2) no longer early-returns on empty labels for picker
    callbacks, so unticking the last running service still fires on_select
    (single-select dialogs retain the legacy guard).
  - Full bilingual i18n: scheduler.running_tag / no_change / diff.start /
    diff.stop added to both en/zh tables.

Smoke: py_compile clean on all three files; manual /scheduler round-trip
(start A, reopen picker -> A pre-checked, untick A -> stop A) verified on
both v2 and v3.
2026-05-27 16:38:57 +08:00
Shen Hao c122ee6f73 fix(tui): v3 /scheduler confirm card -> ask_user-style (no checkmarks, Esc rolls back to picker preserving selection, full i18n) 2026-05-27 15:43:47 +08:00
Shen Hao 6126389aa1 fix(tui): v2 /scheduler confirm card -> ask_user-style (no ✅, Esc rolls back to picker via msg.on_cancel) 2026-05-27 15:41:21 +08:00
Shen Hao 34a14d0a60 fix(tui): hide cron advisory from v2 scheduler picker 2026-05-27 15:12:59 +08:00
LJQ 67d1850219 Merge pull request #496 from shenhao-stu:fix/tui-frontend-polish
fix(tui): polish continue picker and prevent stash freeze
2026-05-27 10:36:20 +08:00
LJQ d4ca2ce0b7 Merge pull request #495 from shenhao-stu:feat/slash-command-bundle
feat(tui): add shared slash-command bundle with hub-aligned /scheduler launcher
2026-05-27 10:36:13 +08:00
Liang Jiaqing db68ddf804 Fix desktop bridge startup loading flow 2026-05-27 09:37:03 +08:00
Shen Hao 9c463e19b5 fix(tui): support cross-platform stash shortcut 2026-05-27 06:51:54 +08:00
Shen Hao 16fae59a49 Fix TUI v2 picker rendering and stash latency 2026-05-27 06:07:42 +08:00
Shen Hao 96fd00381c Polish TUI v3 scheduler and command handling 2026-05-27 06:07:42 +08:00
Shen Hao 1a39221a59 Harden continue session listing and restore parsing 2026-05-27 06:07:42 +08:00
Shen Hao 465708bb0e Add shared frontend slash command helpers 2026-05-27 06:07:41 +08:00
Shen Hao 032a9ad33e fix(scheduler): align /scheduler picker with hub.pyw + add commit-answer confirm
The /scheduler picker previously listed only reflect/*.py (skipping just
underscore-prefixed files), so it leaked goal_mode/agent_persona and missed
the frontend apps that hub.pyw's launcher shows. It also started tasks via
`python reflect/X.py` instead of the canonical `agentmain.py --reflect`.

- slash_cmds.py: add list_launchable_services() + start_service() that mirror
  hub.pyw discover_services() exactly (EXCLUDES goal_mode/chatapp_common/tuiapp,
  reflect tasks via agentmain.py --reflect, frontend *app*.py incl. streamlit).
- tuiapp_v2.py / tui_v3.py: /scheduler now shows the full hub.pyw service set
  (reflect + frontend) and, after the multi-select Enter, requires a second
  commit-answer confirmation before anything is actually launched.

Parity verified at runtime: list_launchable_services() == discover_services().
2026-05-27 05:46:53 +08:00
Shen Hao fc74f2a7f4 feat(tui-v3): wire slash bundle + multi-select scheduler menu
Hook the slash_cmds bundle into tui_v3: register the commands in the
palette, add the prompt-injection dispatch and the /scheduler launcher.
Extend the in-house _show_menu with an opt-in multi_select mode
(Space toggles, Enter submits) so /scheduler can start several reflect
tasks at once, plus the matching command tips.
2026-05-27 05:30:46 +08:00
Shen Hao 7c00108053 feat(tui-v2): wire slash command bundle into the Textual TUI
Hook the slash_cmds bundle into tuiapp_v2: register the commands in the
COMMANDS palette and dispatch table, add the prompt-injection handlers
(/update /autorun /morphling /goal /hive) and the /scheduler picker
(multi-select reflect tasks via the existing multi_choice picker), plus
the matching command tips.
2026-05-27 05:30:46 +08:00
Shen Hao 63c61560b2 feat(tui): add slash_cmds bundle (prompt-injection + scheduler helpers)
Introduce frontends/slash_cmds.py, a shared bundle backing the new
slash commands for both Textual (v2) and curses (v3) frontends.

Provides prompt builders for /update /autorun /morphling /goal /hive
(each injects a concise, imperative directive into the agent prompt)
and the reflect-task discovery/launch helpers used by /scheduler.
2026-05-27 05:30:46 +08:00
YooooEX f758d1a200 fix: support review command in Telegram frontend
Wire the existing /review handler into tgapp so Telegram users can invoke in-session code review, including help responses and streamed agent execution.
2026-05-26 12:09:04 +08:00
Jiaqing Liang ab0919cfa7 fix mykey path, add inject-prompt button, harden reload_mykeys 2026-05-25 13:29:12 +08:00
skydog221 bb656b189e fix(qqapp):Change message type to Markdown message(msg_type-2)) 2026-05-24 21:16:05 +08:00
Jeason00011 96faa1ba21 fix: add from __future__ import annotations for Python <3.10 compat 2026-05-24 16:09:40 +08:00
yanghang cf3fbf3be7 feat(tui_v3): v2 feature parity — export/fold/ask_user/plan card + inline i18n
scrollback-first tui_v3 rounds out v2 parity across this session:

- /export: v2-aligned subcommands (clip/file/all/legacy); clip writes to the
  real OS clipboard via existing copy() (pbcopy/clip.exe/wl-copy/xclip/xsel);
  picker→file prefills an editable filename
- tool-call folding: per-turn fold ported from v2 fold_turns; Ctrl+O toggles;
  incremental repaint folds turn N the moment turn N+1 marker streams in
- ask_user: focus-cycle model (↑↓ moves between candidates and the input box,
  caret hidden on candidate focus); multi-select is checkbox-only with no
  input box; [x]/[ ] ASCII checkboxes instead of Unicode ballot glyphs
- plan card: v2 _refresh_planbar port — 5-row budget, current-step line,
  complete/placeholder states with grace periods, mtime-cached reads
- attachments: bracketed-paste, image/file placeholders, multi-line paste
  aligned to v2 behaviour
- i18n: zh/en layer merged inline (was a separate tui_v3_i18n.py) so the
  frontend ships as one drop-in module

pyproject: add prompt_toolkit + pillow to the `ui` extra.
2026-05-23 02:15:13 +08:00
Liang Jiaqing fff2af9c26 add supergrok_proxy 2026-05-22 10:04:40 +08:00