Commit Graph
14 Commits
Author SHA1 Message Date
Shen HaoandShen Hao d1d517f374 fix(slash_cmds): /update pins git commands to the repo root (#681)
The agent's default cwd is the temp/ subdirectory, where git resolves
'-- <file>' pathspecs relative to cwd. Fed the root-relative paths that
'git diff --name-only' prints, 'git checkout upstream/main -- <file>'
fails with "pathspec did not match", breaking the working-tree
reconcile steps of /update. The prompt now tells the agent to run every
git command from the repo root (_ROOT), in both EN and ZH.

Co-authored-by: Shen Hao <shenhao-stu@users.noreply.github.com>
2026-07-13 19:28:03 +08:00
Shen Hao 8395962197 fix(slash_cmds): merge additive-overlay files on /update, simplify prompt
The /update prompt reconciles the working tree "upstream-first" file by
file. That silently drops fork-local additions to files BOTH sides edit —
most painfully .gitignore, whose local ignore rules vanish, so a fork's
private dirs resurface as untracked noise (and can get committed by
accident).

Treat files carrying a `Fork-only local overlay` marker (e.g. .gitignore)
as a MERGE: take upstream as the base, then re-append everything under the
marker verbatim, and verify the marker survived. Along the way, tighten
both the EN and ZH prompts (75 -> 58 lines) without dropping any operative
step.
2026-07-11 01:39:54 +08:00
Liang Jiaqing 99954324bc feat: generic extra_sys_prompts slot replaces hardcoded commit_signature module 2026-06-20 21:03:38 +08:00
Shen HaoandShen Hao 1bcfeeca1b fix(slash_cmds): /update reconciles working tree, not just git history (#624)
Co-authored-by: Shen Hao <shenhao-stu@users.noreply.github.com>
2026-06-18 01:02:30 +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 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
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 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
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 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 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