* feat(search): replace the command palette with tabbed Search Everywhere
The palette was one flat list that every new kind of row had to be squeezed
into: tabs and SSH hosts rode along as "Switch to Tab: …" and "SSH: …"
commands, and the only way to narrow to one kind was a magic seed word.
Search Everywhere splits it into sources behind one trait — All, Actions,
Terminals, Hosts — each with its own empty-query layout and ranking. The All
tab shows each source's top rows, ordered by best match, with a row that
opens the full tab. Tab / Shift-Tab walk the tabs and keep the query.
- Terminals lists every open tab of every workspace (reusing the switcher's
tab rows) and jumps to it wherever it lives, plus the shells and agents.
- Hosts replaces the separate "Add Connection" input: a typed address or
full `ssh …` line offers to connect.
- Fixes Return doing nothing after a search that found nothing, or when the
search opens pre-filtered: gpui-component re-picks the row from a stale
frame; the delegate now re-arms the first row.
- Fixes `ssh -p 2222 me@box` being offered as a quick-connect address with
user `ssh -p 2222 me`.
The keymap action stays `TogglePalette` so custom bindings keep working.
* feat(search): a Sessions tab to resume past agent sessions
Search Everywhere gains a Sessions tab listing the Claude Code and Codex
sessions on this computer, read from ~/.claude/projects and
~/.codex/sessions ($CODEX_HOME). Sessions that ran in the focused tab's
directory lead; the All tab offers the last three of them before anything
is typed. Return opens a new tab in the session's directory and runs the
agent's resume command with its configured launch flags.
- Only each transcript's head and tail are read, off the window thread,
and cached by path, size and mtime: ~170ms cold for 50 sessions,
under 1ms warm. The search opens on the cached list and fills in.
- Titles: /rename name, then the agent's own title (ai-title, Codex's
session_index.jsonl), then the first thing typed, skipping harness
injections. Codex rollouts it ran for itself (subagent/internal) and
sessions never asked anything are left out.
- A session whose directory is gone is refused with a notice rather than
resumed where the agent cannot find it.
* feat(agents): recognise Empryo and Prime Agent
Prime Agent (PrimeIntellect-ai/prime-agent) is a Pi fork: detected as
`prime-agent`, resumes with `--resume <id>`, forks with `--fork <id>`,
`--no-session` opts out, and reports status through the shared Pi
extension bridge at ~/.prime/agent/extensions/tty7/index.ts.
Empryo is detected as `empryo` and resumes with `empryo --session <id>`.
No hook installer yet: Empryo filters TTY7* variables out of hook
processes, so `tty7-app agent-hook` would stop at the missing marker.
* feat(agents): Antigravity status hooks
Antigravity CLI (`agy`) now installs a `tty7` hook set in
~/.gemini/config/hooks.json, next to the user's own sets:
PreInvocation -> prompt-submit, PostToolUse -> tool-complete,
Stop -> stop. Only the first PreInvocation of a turn (invocationNum 0)
counts as a new prompt, later ones keep the turn working; a Stop with
fullyIdle false is ignored. conversationId and workspacePaths feed the
session id and cwd. PreToolUse is left alone because it must answer
with a decision.
* fix(agents): index Prime Agent and Antigravity in settings, scope conversationId alias
- Add settings titles, search keywords (en/ja/zh) and Agents index entries
for the two new HookAgents; agent_rows_are_in_the_search_index requires
one per HookAgent::ALL.
- Read Antigravity's conversationId as the session id only for antigravity:
the alias table is last-write-wins, so a global alias could replace
another agent's session id.
- Drop the stray .empryo/ job records.
---------
Co-authored-by: kalpak <you@example.com>
Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
Every placeholder frame in docs/ now shows a real capture of the current
build: 20 screenshots plus two short looping clips (prompt editor, pane
drag). The README and the docs home page link a one-minute tour covering
agent status across repos, one agent driving another through the CLI,
the prompt editor, diffs, pane dragging, and sessions surviving a quit.
CodeBuddy Code takes Claude Code's hooks as-is: the same nested
`hooks.<Event>[].hooks[{type, command}]` shape in ~/.codebuddy/settings.json
(or $CODEBUDDY_CONFIG_DIR/settings.json), the same event names, and the same
session_id/cwd payload fields. So it rides the shared hook-map installer and
needs no payload aliases.
The event table follows Qoder rather than Claude: CodeBuddy has a
first-class PermissionRequest and Elicitation, so it gets no Notification
hook, and StopFailure ends a turn like Stop. CodeBuddy also emits
SessionStart with source "compact" mid-turn, which is filtered out the same
way Qoder's is so the pane does not drop back to idle.
Detection covers all three npm bins (codebuddy, codebuddy-code, cbc).
Resume is `codebuddy --resume <id>` and fork appends --fork-session; stale
session and worktree flags are dropped from the replayed launch argv, and
--no-session-persistence disables both commands.
Icon, en/ja/zh names and search keywords, and docs are updated.
* feat(agents): wire hooks, resume and detection for Kimi Code
Kimi Code CLI takes its hooks as [[hooks]] entries in the same
config.toml that holds the user's providers and models, so this adds a
third install strategy — a format-preserving TOML merge on toml_edit —
beside the JSON map merge and the owned files. Like Qwen it reports
permission requests first-class, so it gets no Notification hook.
Resume rides `kimi --session <id>`; fork stays unwired, Kimi
documents none.
Closes#693
Signed-off-by: Austin Spraggins <spragginsdesigns@gmail.com>
* fix(agents): harden the Kimi Code TOML hook merge and its resume flags
The TOML merge strategy the Kimi wiring introduces round-trips a shared
config.toml cleanly, but three gaps sat behind it.
`hooks_state` counted only the marked entries that still named an event,
so a hand-edit that dropped the key off one of nine entries left the
remaining eight matching the roster exactly and the file reported
Installed with a broken entry in it. Every marked entry now counts,
which is what the JSON merge already did and what `refresh_hooks` needs
to see.
A `hooks = []` spelled as an empty inline array made install fail
outright -- toml_edit keeps an empty array and an array of tables apart,
but the two say the same thing and neither carries any configuration. It
is now promoted rather than refused. Every other wrong-shaped `hooks`
key -- a string, a table, a non-empty inline array -- still refuses with
the file left byte-for-byte alone.
`Stop` is not the only way a Kimi turn ends: its own event reference says
`Stop` does not fire on interrupts and `Interrupt` fires instead, and a
turn that dies on an error reports `StopFailure`. Without those two an
Esc or a failed turn left the pane on "working" for good and `tty7 wait`
could only ever time out. Both are observation-only events and report
the same end of turn `Stop` does.
On resume, `--agent` and `--agent-file` join the stale flags: Kimi
rejects either next to `--session` at startup, and resuming rebinds the
session agent by itself, so replaying them turned a working resume into
a launch error.
Tests cover the wrong-shaped `hooks` keys, a config.toml that does not
parse on both install and uninstall, a file that does not exist yet, a
second install being byte-for-byte the first, mangled and surplus marked
entries, an uninstall threading between the user's own entries and the
tables after them, and the `--session=<id>`, bare `--session`,
`--continue` and `--agent` spellings on the resume path.
---------
Signed-off-by: Austin Spraggins <spragginsdesigns@gmail.com>
Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
* fix(settings): split Shell Arguments like a shell, quote them on the way back (#551)
The field split on raw whitespace, so `-c "echo hi"` became four argv
fragments with the quotes still attached, and it silently rewrote config
too: `build_shell_inputs` refilled the field with `args.join(" ")`, which
cannot spell an argument containing a space, so a legal
`"args": ["-c", "echo hi"]` in config.json re-committed as three argv on
the next blur without the user typing anything.
Parse with shell-words rules instead and quote each argument on the
refill, so field text and the argv array round-trip losslessly. An
unbalanced quote cannot become argv at all, so commit refuses it and the
row explains why under the input — the proxy field's pattern. The field
description in en/zh/ja now says quoting works.
Program gets the milder half of the same treatment: a bare command that
detection (a PATH probe) never saw is almost always a typo like `pwsh7`
that today only surfaces when a pane fails to open, so the row warns
under the field. It never refuses — the field stays free-text so a shell
detection missed remains reachable — and anything spelled as a path is
taken at its word. The comparison reuses core's `same_shell_program`, so
"known" here means exactly what the new-tab menu dedup means.
shell-words was already in the tree via portable-pty, so the direct pin
adds no new code.
* fix(settings): split Shell Arguments as argv, not as POSIX source
Review pass over the #551 fix. Splitting with `shell-words` bought the
quoting contract at the price of two silent rewrites of its own, both the
same shape as the bug being fixed: a backslash outside quotes is a POSIX
escape, so `--dir C:\Users\me` committed as `C:Usersme` on the platform
where that is how a path is spelled, and `#` opens a comment, so
`--tag #1 --verbose` committed as one argument. The refill was noisier
than claimed too — `shell_words::join` quotes on `=`, `~`, `*`, `?` and
`[`, so an existing `--color=auto` came back as `'--color=auto'`.
Nothing here is a shell: the field is a text spelling of an argv array
that goes to `CommandBuilder` directly. So split and join are now a local
pair sized to exactly that job — quotes group, `\"` and `\\` inside double
quotes escape, everything else is a character — and the direct
`shell-words` pin goes away again. They are exact inverses, which is what
`config.json` needs, and the test walks the round trip over the cases a
space-join cannot spell plus the two above.
Drops the Program nudge. `shells::inventory()` inserts the *configured*
shell into the inventory it returns, so `pwsh7` is in `self.shells` from
the next refresh onward: the warning could only ever flash between the
commit and the refresh landing, and never appeared at all on a later visit
to Settings. Its test passed because it hand-built an inventory that
version of the value could not be in. Making it true needs core to say
which rows were detected rather than configured, and that is a serialized
protocol struct — too much for a nudge the issue itself called the milder
half.
Refusing the arguments no longer drops the Program typed or picked beside
them: the stored argv stays as it was, which is what "this value was not
saved" already told the user, and the shell picker works again while the
arguments field is mid-edit.
---------
Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
38 pages under docs/, written against the source rather than the README:
config keys and their clamps from core::config, default keybindings from
ui::keymap, every CLI verb and flag from tty7-cli, agent aliases and
hook/fork/resume support from core::cli_agent, and Settings paths taken
from the actual en-US strings.
docs/features.md and its zh-CN translation are retired — everything in
them now lives in a page of its own, plus the two things they carried
that nothing else did (IME input, the performance notes). README and
README.zh-CN point at docs/ instead.
Screenshots and videos are placeholders for now: docs/images/placeholder.svg
with a caption naming what each shot should be.
Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>