ACE TUI User Guide¶
Overview¶
ACE (Agentic ChangeSpec Explorer) is the primary TUI for the SASE toolkit. It provides an interactive interface for navigating, managing, and operating on ChangeSpecs, agents, and the Axe daemon.
Launching¶
sase ace [QUERY] [options]
If no query is provided, ACE loads the last used query, then the first saved query, then falls back to !!! for error
suffixes.
CLI Options¶
| Option | Description |
|---|---|
QUERY (positional) |
Query string for filtering ChangeSpecs |
-m, --model-tier |
Override model tier for all LLM providers (large or small) |
-M, --model-size |
Deprecated alias for --model-tier (big or little) |
-p, --profile [PATH] |
Profile the TUI session with pyinstrument; optional output path |
-r, --refresh-interval |
Auto-refresh interval in seconds (default: 10, 0 to disable) |
-x, --no-axe |
Disable auto-starting the axe daemon on startup |
-v, --vcs-provider |
Override VCS provider (git, hg, or auto) |
-R, --restart-axe |
Restart the axe daemon on startup (shows RESTARTING indicator) |
-t, --tab |
Tab to focus on startup (changespecs, agents, axe; default: agents) |
-T, --tmux |
Launch ACE in a new tmux window and print the target for external control |
When profiling is enabled, ACE writes text output to PATH or $SASE_TMPDIR/ace_profile_<ts>.txt, prints the shortened
path on exit, and copies that path when a clipboard tool is available.
Examples¶
sase ace # Last query, first saved query, or "!!!"
sase ace '"feature" AND "Drafted"' # Filter by name and status
sase ace '+myproject' # Filter by project
sase ace -m small -r 30 '!!! OR @@@' # Small model, 30s refresh
When --profile is enabled, ACE prints a shortened profile-output path after the TUI exits and tries to copy that
shortened path to the system clipboard (pbcopy, wl-copy, xclip, or xsel when available).
Tab System¶
ACE has three tabs, cycled with Tab and Shift+Tab:
| Tab | Description |
|---|---|
| PRs (ChangeSpecs) | Browse and act on ChangeSpecs matching the current query |
| Agents | View running and completed agents, their files and prompts |
| Axe | Monitor the Axe daemon and background commands |
Keybindings: PRs Tab¶
Navigation¶
| Key | Action |
|---|---|
j / k |
Move to next / previous visible row (banner at fold < L2, PR at the leaf level) |
< / > / ~ |
Navigate to ancestor / child / sibling PR |
' |
Jump to entry by hint character (current tab); hints land on collapsed banners too |
Ctrl+O |
Fast jump: jump back if possible, otherwise jump to the first current-tab hint |
` |
Jump to entry across all tabs (see Jump All Modal) |
Ctrl+R / Ctrl+K |
Jump back / forward in PR history |
o / O |
Cycle PR grouping mode forward / reverse (BY_PROJECT ↔ BY_DATE ↔ BY_STATUS) |
g / G |
Scroll detail panel to top / bottom |
Ctrl+D / Ctrl+U |
Scroll detail panel down / up (half page) |
Note:
o/O("organize") cycles the L0 grouping bucket forward / reverse on the Agents and PRs tabs (each tab keeps its own in-session mode). On the AXE tab it is a silent no-op. See PR Grouping and Folding and the Agents-tab Grouping Modes below.
PR Actions¶
| Key | Action |
|---|---|
a |
Accept proposal (! = spec only, @ = mark ready to mail) |
b |
Rebase PR onto parent |
C / c1-c9 |
Checkout PR (primary / workspace 1-9) |
d |
Show diff |
e |
Edit spec file |
f |
Edit hooks (re-run / delete via hint input) |
M |
Mail PR |
m |
Mark / unmark current PR (auto-advances to next) |
n |
Rename PR (non-Sub/Rev PRs only) |
R |
Rewind to previous commit (! suffix skips VCS operations) |
s |
Change status (opens status modal) |
S |
Bulk status change for all marked PRs |
T |
Checkout + tmux (opens workspace input modal for number) |
u |
Clear all marks |
v |
View files (hint mode) |
w |
Reword PR description |
W |
Add tag to PR description |
Y |
Sync workspace |
PR Grouping and Folding¶
The PRs tab is always grouped — the renderer walks one of BY_PROJECT, BY_DATE, or BY_STATUS and emits a banner row
above each bucket. BY_PROJECT is the startup default; o cycles BY_PROJECT → BY_DATE → BY_STATUS for the current
session.
| Mode | L0 buckets | Notes |
|---|---|---|
BY_PROJECT |
Project name | Adds an L1 sibling-root sub-banner shared by foobar_1 / foobar_2 style suffixed siblings. Singletons suppress their L1 banner. |
BY_DATE |
Today / Yesterday / This Week / Earlier |
Bucket from the latest TIMESTAMPS entry. Today/Yesterday add 4-hour L1 windows; hourly L2 headings appear only inside 4-hour windows with 2+ PRs. This Week adds day headings; Earlier adds week headings plus (no timestamp). |
BY_STATUS |
Mailed / Ready / WIP / Draft / Submitted / Reverted / Archived |
Bucket from the literal status field; actionable buckets first (Mailed = awaiting response, Ready = next to mail), terminal states last. Adds an L1 sibling-root sub-banner shared by foobar_1 / foobar_2 style suffixed siblings inside each status bucket. Singletons suppress their L1 banner. |
In BY_DATE mode, PRs sort newest-first within each date bucket. Today and Yesterday are grouped first by compact
4-hour windows (8AM-12PM); one-hour headings (09:00) appear only when that 4-hour window contains at least two PRs.
This Week uses calendar-day subgroups; Earlier uses Monday-start week ranges. PRs without a parseable TIMESTAMPS
entry fall into (no timestamp) under Earlier.
The active grouping mode is shown in the PRs-tab info-panel header as a [group: <label>] badge.
| Key | Action |
|---|---|
l |
Expand the focused banner one level (or peel one layer of the visible tree) |
h |
Collapse the focused banner; on a collapsed L1 banner, escalate to its parent. With agent focus, collapse the deepest enclosing group. |
L |
Snap to fully expanded — all banners and PR rows visible |
H |
Snap to fully collapsed — collapse every visible banner |
Collapsed banner rows are first-class navigation stops: j/k step through them just like PR rows, and ' jump-hints
land on them too. After a fold change that hides the focused CL, focus snaps to the deepest collapsed ancestor banner so
the cursor always sits on a row the user can see.
Fold Mode (z prefix)¶
| Key | Action |
|---|---|
z c |
Cycle commits section (expand → collapse) |
z d |
Cycle deltas section (folded ↔ unfolded) |
z h |
Cycle hooks section (expand → collapse) |
z m |
Cycle mentors section (expand → collapse) |
z t |
Cycle timestamps section (expand → collapse) |
z C |
Toggle commits section (collapsed ↔ fully expanded) |
z D |
Toggle deltas section (folded ↔ unfolded) |
z H |
Toggle hooks section (collapsed ↔ fully expanded) |
z M |
Toggle mentors section (collapsed ↔ fully expanded) |
z T |
Toggle timestamps section (collapsed ↔ fully expanded) |
z z |
Cycle all sections |
z Z |
Toggle all sections (expand ↔ collapse) |
COMMITS, HOOKS, MENTORS, and TIMESTAMPS sections each cycle through three fold levels:
| Level | Behavior |
|---|---|
| Collapsed | Notes truncated to fit; multi-line body shown as [+N lines]; only latest drawers |
| Expanded | Full notes; body shown in dimmed text; all CHAT/DIFF/PLAN drawers visible |
| Fully Expanded | Everything visible including rejected proposals |
The lowercase cycle keys (z c, z h, z m, z t) step through all three levels in order. The uppercase
toggle keys (z C, z H, z M, z T) skip the intermediate Expanded state, jumping directly between
Collapsed and Fully Expanded.
When collapsed, a [folded: CHAT + DIFF + PLAN + N proposals] indicator appears on COMMITS entries with hidden content.
The indicator width is pre-calculated so that note truncation accounts for it. TIMESTAMPS shows a [folded: N]
indicator inline with the header and displays the most recent timestamp entry when collapsed, giving a quick view of the
last lifecycle event.
The DELTAS section uses two semantic states. When folded, the section renders a one-line file and line-count summary
such as DELTAS: +3 (+428) ~6 (+91 ~37 -14) -1 (-22) (10 files). When unfolded, the alphabetical entry list is
shown with colored glyphs (green +, gold ~, red -) and inline line-count tokens. Binary files display binary;
zero-count entries display 0 lines. The section is omitted entirely when the ChangeSpec has no deltas.
Workflows and Agents¶
| Key | Action |
|---|---|
r |
Run workflow on current PR |
+ |
Run a custom agent (opens project/ChangeSpec selection) |
Space |
Run agent from current PR |
If ACE cannot detect a workspace provider for the selected ChangeSpec or agent, the quick-launch actions show an error toast instead of opening a prompt with a broken VCS prefix.
Bang Mode (! prefix)¶
| Key | Action |
|---|---|
!! |
Run background command (opens hook history modal) |
!x |
Start / stop axe (or select process) |
Hook History Modal¶
Pressing !! opens the hook history modal showing previously run background commands:
| Key | Action |
|---|---|
j / k |
Navigate through hook history |
Enter |
Select and execute highlighted hook |
Ctrl+D |
Delete highlighted hook from history |
Ctrl+G |
Edit first — select hook and open in input |
Esc / q |
Cancel and close modal |
The modal supports live filtering as you type in the search box and displays last-used timestamps for each hook.
Leader Mode (, prefix)¶
| Key | Action |
|---|---|
,, |
Repeat the last leader command |
,! |
Run command using current PR context |
,A |
Open the Agent Run Log modal for the current PR |
,c |
Clear COMMENTS field (kills CRS agents, deletes CRS proposals) |
,h |
Run agent from home prompt context; bare prompts default to #git:home |
,m |
Review mentors (opens Mentor Review modal) |
,M |
Kill running mentors |
,p |
Open project lifecycle management (see Project Management Panel) |
,o |
Open model overrides (see Model Overrides) |
,R |
Show runners info |
,t |
Open task queue modal (see Task Queue Modal) |
,<space> |
Run agent from current PR (skips project selection) |
,. |
Open prompt history modal |
,> |
Open prompt history modal with cancelled prompts visible |
The ,h shortcut opens a home-context prompt directly. Project and PR launch pickers use lifecycle-aware discovery:
project entries, including home when it appears in picker lists, must have active and launchable ProjectSpecs; PR
choices come from active ProjectSpecs. Inactive projects do not appear in normal launch pickers until they are
reactivated with sase project activate <project>.
Project launch pickers also support Ctrl+D for cleanup of empty project entries. This deletes only the highlighted
project's active/archive ProjectSpec files, refuses entries whose ProjectSpec files still contain ChangeSpecs, and does
not delete workspace checkouts or other SASE state. For lifecycle changes, bulk operations, ProjectSpec editing, or
deleting the whole SASE project directory, use the ,p Project Management panel.
The repeat binding is the leader prefix followed by the configured repeat_last key. With the defaults both are comma,
so the sequence is ,,; if the leader prefix is changed but repeat_last is not, the second key remains comma. Repeat
re-dispatches the last recognized leader subkey against the current tab and selection. If no leader command has been run
yet, ACE shows a toast and does nothing.
Note:
,x(kill & edit) is only available on the Agents tab — see Agents Tab Leader Mode.
Mentor Review Modal¶
Press ,m to open the Mentor Review modal, which lets you navigate mentor comments, accept or reject suggestions, and
apply accepted changes. See docs/mentors.md for the full mentor system reference.
| Key | Action |
|---|---|
j / k |
Navigate between mentors |
n / p |
Navigate between comments within a mentor |
N / P |
Navigate between accepted comments only |
Ctrl+D / Ctrl+U |
Scroll comment details down / up |
Space |
Toggle acceptance of the current comment |
Enter |
Apply all accepted comments (launches agent) |
a |
Apply accepted comments and propose (amend with propose) |
A |
Apply accepted comments and commit |
r |
Run a mentor profile (opens profile picker) |
y |
Copy the current comment to the clipboard |
Shift+K |
Kill a running mentor |
Esc / q |
Close modal |
Copy Mode (% prefix)¶
| Key | Action |
|---|---|
%% |
Copy ChangeSpec |
%! |
Copy ChangeSpec + snapshot |
%b |
Copy bug number |
%c |
Copy PR number |
%n |
Copy PR name |
%p |
Copy project spec file |
%s |
Copy sase ace snapshot |
Keybindings: Agents Tab¶
Navigation¶
| Key | Action |
|---|---|
j / k |
Move to next / previous visible row (banner at fold < L3, agent at L3) |
J / K |
Cycle focus across tag side panels (forward / reverse) |
' |
Jump to entry by hint character (current tab); on the Agents tab, hints land on collapsed banners too |
Ctrl+O |
Fast jump: jump back if possible, otherwise jump to the first current-tab hint |
` |
Jump to entry across all tabs (see Jump All Modal) |
o / O |
Cycle grouping mode forward / reverse (STANDARD ↔ BY_DATE ↔ BY_STATUS) |
g |
Scroll to top (file, tools, or metadata panel) |
G |
Scroll to bottom (file, tools, or metadata panel) |
Ctrl+D / Ctrl+U |
Scroll file panel down / up |
Ctrl+F / Ctrl+B |
Scroll prompt panel down / up |
Note:
o/O("organize") cycles the grouping mode forward / reverse on the Agents and PRs tabs (each tab keeps its own in-session selection independently); on the AXE tab it is a silent no-op.g/Gkeep their conventional vim-style scroll-to-top/bottom meaning on every tab. See Grouping Modes below.
Agent Actions¶
| Key | Action |
|---|---|
R |
Revive a previously dismissed agent |
A |
Open completion artifacts for the focused agent; in tmux, press again to close the viewer pane |
+ |
Run custom agent |
a |
Cycle auto-approve state / answer HITL |
f |
Fork agent (by name if running, by chat file if completed) |
n |
Name agent |
r |
Edit prompt and relaunch agent (retry without killing) |
v |
View files (hint mode) |
D |
Toggle prior-attempt view (only shown when the agent has retried) |
V |
Open the Agent Run Log modal for the focused agent |
w |
Wait/unwait agent (opens WaitModal — see below) |
W |
Wait for agent (populate prompt with %w); with marks, fans out to %w:a,b,c |
m |
Mark / unmark current agent, or all top-level agents in focused collapsed group (auto-advances to next) |
s |
Save and dismiss marked agents as a revivable group (opens optional group-name modal) |
U |
Toggle the focused agent's unread marker |
u |
Clear all agent marks |
x |
Kill / dismiss agent, every marked agent, or every agent in the focused group |
X |
Open the cleanup panel for panel, global, tag, marked, group, or custom cleanup |
Enter / L |
Jump to PR (for agents with meta_new_cl/meta_new_pr) |
e |
Edit chat in editor; with marks, open all editable marked transcripts in one editor invocation |
E |
Edit panel content in editor |
t |
Open the focused agent's tmux target; agents with opened linked-workspace context show a workspace chooser |
T |
Open tmux window in the agent's primary project workspace |
N |
Open the agent tag/untag modal (input is pre-seeded with pinned for untagged agents; submit empty to clear) |
] / [ |
Cycle panels: file → tools → metadata (forward / reverse) |
p |
Toggle file / prompt layout |
Ctrl+N / Ctrl+P |
Next / previous file in panel |
- |
Reset file trim to default |
= |
Show all file lines |
ACE separates fast visible-inbox loads from full-history scans. The visible inbox is the normal Agents-tab working set:
active rows plus recent completed, non-hidden rows. Startup, manual refresh (y), and active agent search use that path
through the persistent artifact index when it is available.
If the index is missing or unhealthy, ACE falls back to a bounded source-artifact scan for the first paint and shows a
repair warning with the reason. That repair state can arm a deferred full-history reconcile after the TUI is idle, but
normal y refreshes still stay on the visible-inbox path. Use sase agent index status --json for a lightweight check
that does not scan source artifacts, sase agent index verify to compare the index with source artifacts, and
sase agent index gc to rebuild the index and dismissed projection. Use the Agents-tab leader command ,y when you
want an immediate full-history refresh from source artifacts.
The dismissed projection that hides agents from the visible inbox is rebuilt from the in-memory dismissed set unioned
with every dismissed-bundle summary. Reviving an agent now purges its dismissed bundle, so a revived agent stays
visible. For archives that accumulated stale bundles before that fix, plain sase agent index gc is not a repair on
its own -- it rebuilds the projection from those lingering bundles and re-hides the revived agents. Run
sase agent index gc --purge-revived-bundles (-r) to first delete dismissed-bundle files and summary rows for
suffixes that are no longer present in dismissed_agents.json, then rebuild the corrected projection.
When one or more agents are marked, e edits the marked set instead of only the focused row. ACE opens editable
completed transcripts in visible row order, deduplicates repeated paths, skips live marked rows that are still running
or have no chat file, and reports that live skip count. Stale marks are ignored for this action, and marks remain in
place after the editor exits.
Linked Workspace Context¶
Configured linked_repos are recorded in agent metadata at launch time. For non-terminal agents, ACE can include dirty,
existing suffix-strategy linked repo workspaces in the agent detail DELTAS section. The folded summary counts primary
and linked-repo changes together; the unfolded view groups linked entries under the workspace glyph and linked-repo
name, and file hints resolve paths relative to the linked workspace directory. Static linked repos
(workspace.strategy: none), missing workspace directories, clean repos, and completed/failed agents are not part of
this live linked-delta display.
When a SASE-launched agent opens a configured linked repo with
sase workspace open -p <linked_repo> -r "<reason>" <workspace_num>, the run records an opened-workspace marker. Here
-p/--project is the workspace CLI's project selector; configured linked repos are backed by hidden
PROJECT_STATE: sibling project records so their names can be used there. ACE shows recorded markers in the
prompt/detail SASE CONTEXT section as a WORKSPACES lane with the repo name, resolved path, open time, and reason. If
the selected agent has cached opened-linked-workspace context, pressing t opens a keyboard-first tmux chooser with
CURRENT first and one LINKED target per unique opened repository workspace. Selecting CURRENT uses the normal
agent-workspace tmux path; selecting a LINKED row opens or switches to a tmux window named after the linked workspace
directory. If no opened-workspace context is cached, t opens the normal agent tmux target directly. T always opens
the primary project workspace.
Wait Modal¶
Press w on the Agents tab to open the WaitModal. Behavior depends on the agent's status:
- WAITING agent: Enter another agent's name to wait for, or leave empty and press Enter to run immediately (unwait).
- RUNNING agent: Enter an agent name to kill the current agent and restart it with a
%w(wait) directive. This is useful for redirecting an agent to wait on a different dependency.
The modal supports readline-style keybindings (Ctrl+F/Ctrl+B/Ctrl+A/Ctrl+E) for cursor movement.
VCS Tag Resolution in Fork/Wait¶
When forking or waiting on an agent, VCS tags in the prompt (e.g., #git(ref), #gh:ref) are automatically updated to
point to the correct branch. For non-project agents, the ref is replaced with the agent's CL name (branch). For project
agents using #pr, the ref is replaced with @<name> which resolves to the agent's branch. HITL suffixes (!!, ??)
are stripped during replacement since fork scenarios should not carry over HITL overrides.
Workflow Visibility¶
Workflows launched via sase run are visible in the Agents tab alongside ACE-launched workflows. The TUI scans
artifacts/run/* directories in addition to workflow-* and ace-run directories, and writes an initial
workflow_state.json before execution so that step data appears immediately rather than showing a bare RUNNING entry.
Anonymous tmp_* workflows are included in the normal visible-inbox index when their workflow state has
appears_as_agent: true and does not set hidden: true; explicitly hidden workflow rows are omitted from the default
view. Specialized review runners launched by axe (mentor, CRS, fix-hook, and summarize-hook review agents) are also
visible and are automatically grouped under the @review tag, matching the behavior of a %group:review prompt launch.
Agent Artifacts¶
Press A on a focused agent to open the artifact panel whenever artifacts are associated with that agent. The list can
include chat transcripts, plan files, generated Markdown PDFs, generated images, prompt-referenced images from saved
prompt artifacts, and explicit files saved with sase artifact create -p <path> [-n <label>] [-k <kind>]. ACE always
opens the panel, even for a single artifact, so the label, kind, and path are visible before launching the terminal
viewer.
The prompt/detail header includes an ARTIFACTS section for non-chat entries from the same list. Paths are made
workspace-relative when possible, and hint mode assigns numbers to those paths so they can be opened with the normal
file-hint flow.
Artifact panel controls:
| Key | Action |
|---|---|
| selector | Open the artifact with that one-key selector (1-0, then letters) |
j / k |
Move through artifact rows |
m |
Mark / unmark the highlighted artifact and advance to the next row |
y |
Copy highlighted Markdown artifact contents |
Y |
Copy the highlighted artifact path, workspace-relative when possible |
Enter |
Open marked artifacts in list order, or the highlighted row if unmarked |
A |
Open all artifacts in list order, ignoring marks |
q / Esc |
Close the panel |
When ACE is running inside tmux, artifact viewing opens in a right-side tmux pane so the TUI remains visible. The Agents
list collapses while the tracked pane is live, row-changing navigation shows a warning instead of moving to a different
agent, l focuses the tracked pane, and A closes it. If the pane was already closed, A opens the artifact panel
normally. Outside tmux, ACE suspends while the terminal viewer runs in the current pane. The viewer supports image,
Markdown, and PDF artifacts: images are displayed directly with kitten icat; Markdown is first rendered to PDF; PDFs
are converted to PNG pages for paging. The viewer needs kitten for display, pdftoppm for PDF/Markdown paging, and
pandoc plus a supported PDF engine for Markdown rendering. Missing tools produce a warning instead of failing the TUI.
Viewer controls:
| Key | Action |
|---|---|
j |
Next page; wraps from the last page to the first |
k |
Previous page; wraps from the first page to last |
n |
Next artifact when viewing an artifact sequence |
p |
Previous artifact when viewing an artifact sequence |
r |
Refresh the current page |
q |
Close the viewer |
Only one plan artifact is shown for an agent. When both an archived plan and an SDD tale path are present, ACE prefers the committed SDD plan; otherwise it keeps the path that best matches the run metadata.
During successful-agent finalization, Markdown-to-PDF rendering updates workflow_state.json.pdf_status and a compact
activity label. ACE renders that label on the agent row and in the prompt/detail header, so long conversions show
progress such as PDF 2/4 <path> or PDFs done 3/4 (1 skipped) instead of looking idle.
Tag Side Panels¶
The Agents tab is laid out as a series of vertically-stacked side panels, one per agent tag. Untagged agents live in
their own (untagged) panel; each tagged group renders as #<tag> with an agent count in the panel title. Each panel
title can also show compact scoped metrics in the form [H1 R2 W1 F1 U1 D3]: H is human-in-the-loop, R is running,
W is waiting, F is failed, U is unread terminal work, and D is done/read terminal work. Zero-count metrics are
omitted. Panel heights are sized to their content and separated by a one-row gap. When the panels fit, the first panel
grows to absorb leftover vertical space while later panels stay pinned to their natural height; when the panels
overflow, space is weighted by each panel's rendered row count.
Use J / K to move focus across panels (forward / reverse). J lands on the first selectable row in the new panel;
K lands on the last selectable row, including collapsed group banners when those are visible. Per-panel actions (kill,
dismiss, expand, etc.) operate on whichever panel currently holds focus. Press X to open the cleanup panel: d
dismisses completed agents in the focused panel, D dismisses completed agents across loaded panels, k cleans the
focused panel, K cleans all loaded panels, m cleans marked agents, g cleans the focused group, t chooses a tag,
and c opens the custom selector.
Tags are set or cleared with N (see Agent Actions). When opening the modal on an untagged agent the
input is pre-seeded with pinned so a single Enter promotes the agent into the standard "pinned" panel; that default
makes tag removal discoverable too — opening the modal on a tagged agent and submitting an empty string clears the tag.
The %group <name> directive in a prompt assigns the tag at launch.
Group Banners and Folding¶
Within each tag side panel, agents are grouped into either a 2-level or 3-level banner hierarchy depending on whether any agent in the panel targets a ChangeSpec:
- 3-level layout (panel contains at least one ChangeSpec-scoped agent): project → ChangeSpec → name-root.
Project-scoped agents and agents with no
cl_namefall into a synthetic(no ChangeSpec)bucket that sorts last. - 2-level layout (no ChangeSpec anywhere in the panel): project → name-root.
Banners are rendered between agent rows and carry a summary chip (N agents · K running · M failed). Workflow children
inherit grouping identity from their parent agent so banners never appear between a parent and its workflow steps.
A single global fold level controls how much of the hierarchy is visible:
| Level | What's visible (3-level layout) | What's visible (2-level layout) |
|---|---|---|
L0 |
Project banners only | Project banners only |
L1 |
Project + ChangeSpec banners | Project + name-root banners |
L2 |
Project + ChangeSpec + name-root banners | All banners and agent rows |
L3 |
All banners and agent rows (and per-workflow folds apply) | (same as L2) |
| Key | Action |
|---|---|
l |
Step the focused group's fold one level up (L0 → L1 → L2 → L3); at L3, expand the focused workflow |
h |
Collapse the focused workflow; once it's collapsed (or no workflow is focused), step the group fold one level down |
L |
Snap to fully expanded — every banner, every agent row, every workflow step visible |
H |
Snap to fully collapsed — every per-workflow fold collapsed, then group fold to L0 (only top-level banners) |
Banners at fold levels < 3 are selectable rows. When a banner is focused, m toggles marks for all top-level agents
in that group; workflow child rows are not marked independently by the banner shortcut. x performs a bulk kill/dismiss
on every top-level agent in that group (single confirmation modal). Marked collapsed banners show [✓] when all covered
top-level agents are marked and [~] when only some are marked. Marks take priority over the group for bulk actions, so
a non-empty mark set always drives the bulk action regardless of banner focus. When a fold change hides the previously
focused agent, focus snaps to the nearest visible ancestor banner so navigation context is never lost.
Visual treatment: every row carries a fixed-width tier-guide gutter built from one │ segment per ancestor L0/L1
banner (in the parent tier's dim accent — project blue or ChangeSpec cooler accent), so nesting reads as a tree at a
glance. L0 project / bucket banners use a sky-blue ▌ left bar and a heavy ━ rule; level-2 visual headings
(ChangeSpec banners and BY_DATE 4-hour windows) get a cooler accent with a ▎ bar and a lighter ─ rule. Level-3
visual headings (name-root banners and conditional BY_DATE hourly windows) use a ▸ branch glyph with a teal label.
Singleton name-root groups suppress their banner entirely to reduce visual noise.
The currently-focused side-panel row is marked with a thick accent-colored left bar, bold text, and a translucent accent tint applied to the row background. The tint is intentionally light so per-token status colors (running cyan, failed red, waiting yellow, etc.) remain readable through the highlight — the bar and bold weight do most of the work of marking the selection.
After a kill or dismiss, focus re-anchors on the visually-next row (rather than the next row in input order) so the selection always lands somewhere meaningful in the rendered tree.
Grouping Modes¶
Press o on the Agents tab to cycle the L0 grouping bucket through three modes. The Agents tab shows a brief toast
(Grouping: by project / by date / by status) on each cycle:
| Mode | L0 buckets | Notes |
|---|---|---|
STANDARD |
Project (with optional ChangeSpec sub-level) | The "by project" default. Uses the 2-/3-level layout described above. |
BY_DATE |
Today / Yesterday / This Week / Earlier |
Date bucket at L0, then a date-aware L1 subgroup. Sorted newest-first within each bucket. |
BY_STATUS |
Stopped / Failed / Running / Waiting / Done / Starting |
Bucketed by shared status semantics; name-root and name-prefix subgroups appear only when useful. |
In BY_DATE mode, ACE chooses one L1 subgroup style from the L0 date bucket: one-hour windows (09:00) for Today and
Yesterday, calendar-day labels for This Week, and Monday-start week ranges for Earlier. The time anchor is
stop_time for terminal agents and start_time otherwise; both buckets and their subgroups sort newest-first. Workflow
children inherit the parent's anchor so they stay adjacent regardless of their own start time, and agents with no usable
timestamp fall into a (no time) subgroup that sorts last.
In BY_STATUS mode the L0 banner is the status bucket and L1 is the name-root, with the same singleton-suppression rule
as STANDARD. The bucket order is fixed: Stopped, Failed, Running, Waiting, Done, Starting. The Starting bucket sorts
last so startup-only rows do not displace active work during daemon or launch refreshes. Each mode keeps its own
per-group fold registry, so collapsing buckets in BY_STATUS doesn't affect the project layout you had in STANDARD.
BY_STATUS banners are prefixed with semantic glyphs (▲, ✗, ▶, ⏳, ✓, ◐) so the bucket title still leads
visually.
The active grouping strategy is also surfaced in the Agents tab header via a [group: <label> (o)] badge so the current
session mode is always visible after the cycle toast fades. The same header starts with a visible top-level agent metric
strip in the form N Agents [S stopped · R running · W waiting · F failed · U unread · D done], with numeric counts in
place of the letters and zero-count metrics omitted. stopped counts agents paused for plan approval, questions, or
workflow human-input steps; running excludes waiting, failed, and stopped rows; waiting is the blocked/queued
subset; failed is terminal failed work; unread counts terminal rows that still need acknowledgement; and done is
completed visible work that has already been acknowledged. During startup the metric strip renders Agents: … until the
first agent scan has loaded, avoiding a misleading zero-agent count. Each TUI launch starts in by-project grouping;
cycling only changes the current session. Waiting holds agents that are blocked but progressing on their own —
WAITING with a wait_until timer (%wait:5m, %wait:1430) or a non-empty waiting_for dependency. Stopped
keeps the strict "you need to act" semantics: a WAITING agent with neither a timer nor a dependency stays there
because it's parked waiting on the user.
Agent Row Glyphs¶
To keep rows compact, agent statuses and types are rendered as one- or two-character badges instead of verbose text:
| Glyph | Meaning |
|---|---|
▶ |
RUNNING |
✓ |
DONE |
✓P |
PLAN DONE |
▶P |
PLAN APPROVED |
★E |
EPIC CREATED |
✎ |
PLAN |
✗ |
FAILED |
⏳ |
WAITING |
? |
QUESTION |
↻ |
RETRYING (followed by attempt count, e.g. ↻2) |
≡ |
Workflow row (top-level) |
❑ |
ChangeSpec / CL row (top-level) |
⚡ |
Autonomous (%approve) agent |
◌ |
Hidden agent (visible only when . toggles them in) |
Agents launched by sase bead work also show a gold ◆ <bead_id> badge between the status glyph and the tag/name. A
phase agent named <epic_id>.<N> displays that phase bead ID; the final <epic_id> land agent displays the parent epic
bead ID. Legacy <epic_id>.land land agents keep the same badge. Dismissed agents keep the badge by stripping only the
date-prefix used for dismissal.
Each agent row also carries a per-provider emoji badge before the display name so the LLM provider behind a row is readable at a glance without scanning the right-hand model suffix:
| Badge | Provider |
|---|---|
| 🎭 | Claude |
| 🪐 | Antigravity (agy) |
| 🤖 | Codex |
| 🐼 | Qwen |
| 🐙 | OpenCode |
The same provider palette also colors the <PROVIDER>(<model>) suffix on the right edge of the row — the provider name,
the parentheses, and the model name each render in a distinct shade from that provider's palette so multi-model fan-outs
are easy to scan. Providers without a dedicated palette (anything outside the table above) fall back to a neutral purple
palette and render no emoji badge.
Workflow child rows for python and bash steps render a leading 🐍 / 🐚 glyph after the N/M step number, styled
with the matching step-type accent. The glyph is a stronger signal than the step-type color alone for colorblind users
and for rapid scanning. Agent, parallel, and prompt_part step rows are left unchanged — agent rows already carry a
meaningful display name, parallel rows fan out into structural children, and prompt_part rows are invisible by
default.
The right-hand edge of each row carries a runtime suffix (<start-timestamp> · <elapsed>) right-aligned within the
panel. Active rows that have actually started include a 🏃♂️ marker before the ticking elapsed duration; unread
completed rows use a ✅ marker in the same suffix slot, or ❌ when the agent finished in a FAILED state; and
user-paused rows (PLAN, QUESTION, WAITING INPUT) use a ✋ marker while waiting for a human response. Pre-run
WAITING rows with no BEGIN time hide the suffix so queued waits do not look like live runtime. For finished agents,
the start-timestamp half is rendered as a humanized (date_prefix, time) pair sized to fit the existing 15-cell slot:
- Same day:
HH:MM:SS - Prior day, same year:
Mon DD HH:MM(drops seconds — they're noise once a row finished hours ago) - Different year:
Mon DD 'YY(date only)
The elapsed duration starts at BEGIN when a row recorded wait-before-run metadata, otherwise at the row start time.
Completed DONE / PLAN DONE / TALE DONE workflow rows use the terminal agent stop time when one exists; plan-step
rows that finish without a subprocess stop time anchor to the latest recorded plan submission time so completed planning
rows do not keep ticking. PLAN APPROVED rows with a running follow-up show active elapsed time for the planner segment
plus the coder segment, excluding the idle approval gap between plan submission and code launch. The date prefix uses a
softer dim #8787AF while the time half keeps the standard #8787AF, giving the column internal hierarchy without
inflating the palette. Statuses not in the table fall back to (STATUS) text for forwards compatibility.
Agent Search¶
Press / on the Agents tab to open the query editor. The query language is a structured Boolean expression —
parallel to the ChangeSpec query language but with a property-key allowlist tailored to agents. Bare words are
substring-matched against an agent's cl_name, display_name, agent_name, and status, plus its xprompt, live
reply/response, chat transcript, and prior attempt replies.
Property keys (closed allowlist):
| Key | Form | Notes |
|---|---|---|
status, cl, project, name, model, provider, tag, text, type, source |
key:value (substring match) |
source is axe (workflow / step) or manual. |
pinned, hidden, attention, needs |
key:true / key:false |
Boolean keys. |
age |
age<5m, age>=2h, age:1d, etc. |
: is sugar for >=. Suffixes: s/m/h/d. |
Boolean operators: juxtaposition is implicit AND; explicit AND, OR, and NOT (with parentheses) are honored.
Precedence is NOT > AND > OR. The help modal carries an Agent Query Syntax section listing the same grammar.
Parse failures are non-fatal: the loader falls back to "no filter" for that render and surfaces a transient toast; the query-edit modal re-validates on Apply, keeping itself open and rendering the error inline (in red) on failure.
Transcript files are read lazily (only while a query is active) and cached by (path, mtime_ns) so auto-refresh stays
cheap. Per-file reads are capped at 512 KB; missing or unreadable files are skipped silently. Parsed ASTs are also
cached by raw query string so re-renders skip the parse.
Leader Mode (, prefix)¶
On the Agents tab, leader mode exposes layout and notification shortcuts for the currently loaded agent list.
Unread-completed actions operate on terminal rows that are loaded in the tab; ,j only targets visible rows.
| Key | Action |
|---|---|
,, |
Repeat the last leader command |
,h |
Run agent from home prompt context; bare prompts default to #git:home |
,I |
Toggle manual idle (shows IDLE indicator; any keypress re-activates) |
,g |
Toggle between tag-split panels and one merged agent panel |
,j |
Jump to the next visible unread completed agent, newest first, and mark it read |
,J |
Jump to the next visible stopped/terminal agent, newest first, without changing unread state |
,y |
Refresh the Agents tab from full artifact history |
,u |
Mark all loaded unread completed agents as read |
,n |
Jump to agent notification (plan or question; auto-unhides if needed) |
,p |
Open project lifecycle management (see Project Management Panel) |
,o |
Open model overrides (see Model Overrides) |
,C |
Capture an Agents-tab reproduction bundle for debugging row disappearance or duplication |
,T |
Toggle continuous Agents-tab repro invariant checks and auto-capture on violation |
,x |
Kill focused or marked agent(s) and edit their prompt(s) |
,<space> |
Run agent from current agent's PR (skips selection) |
,. |
Open prompt history modal |
,> |
Open prompt history modal with cancelled prompts visible |
Here, "stopped" means a dismissable terminal row such as DONE, FAILED, PLAN DONE, TALE DONE, PLAN REJECTED,
PLAN COMMITTED, or EPIC CREATED; it is separate from the Agents header's "stopped" attention bucket for rows paused
on user action.
If any agents are marked, ,x acts on that marked set instead of the focused row. Stale marks are ignored; if any
remaining marked agent has no recoverable prompt, ACE warns and leaves the set untouched. After confirmation, ACE kills
or dismisses the marked agents and opens a prompt stack with one editable pane per original prompt in mark order.
Embedded --- inside an individual agent prompt stays inside that agent's pane.
Agents Tab Reproduction Bundles¶
Agents-tab reproduction bundles capture the loader/apply sequence that determines which rows are visible. Use them when the Agents tab briefly drops historical rows, re-adds them, or shows duplicate workflow parents.
When you see one of these bugs in a live ACE session, switch to the Agents tab and press ,C before refreshing again.
ACE writes a commit-safe bundle to ~/.sase/repros/<timestamp>-manual-.../agents_tab_repro.json and shows a toast with
the path. "Commit-safe" means local names and paths are redacted, and prompt, response, chat, and diff bodies are
omitted. The bundle keeps the row identities, loader state, app projection state, screen text, and an SVG screenshot
needed to replay the row-list behavior.
Replay a bundle from a checkout of this repository:
sase repro replay tests/ace/tui/repro/fixtures/agents_tab_disappear_reappear_v1.json --assert-stable --json
The current expected verdict for the checked-in fixture is:
{
"result": "passed",
"failed_invariants": [],
"verdict": "current code fixed for the captured Agents-tab bug class"
}
Add --write-artifacts /tmp/sase-agents-tab-repro-artifacts to write one .txt screen dump and one .svg screenshot
per replay step. The replay JSON lists those paths in screen_paths and screenshot_paths.
Use out-of-band capture only when you need a filesystem baseline and did not have the live TUI capture running:
sase repro capture agents-tab --output /tmp/sase-agents-tab-capture --commit-safe --json
Out-of-band capture is labeled capture_mode=out_of_band because it loads the current filesystem state and cannot
reconstruct transient refreshes that already passed through the running TUI. The replay harness is scoped to the known
Agents-tab disappearance/reappearance and duplicate-parent bug class; it is not a general proof for arbitrary rendering
races.
For continuous diagnosis, press ,T on the Agents tab to enable invariant checks after each load/apply cycle. On the
first violation in a burst, ACE auto-captures one bundle under ~/.sase/repros/<timestamp>-auto-.../ and shows a
warning toast. It does not write a new bundle every refresh while the same violation remains active.
Bang Mode (! prefix)¶
| Key | Action |
|---|---|
!! |
Run background command |
!x |
Start / stop axe (or select process) |
Copy Mode (% prefix)¶
| Key | Action |
|---|---|
%c |
Copy chat file path |
%E |
Copy file path |
%n |
Copy the focused agent's agent_name (falls back to display_name; toast indicates which) |
%p |
Copy agent prompt |
%s |
Copy sase ace snapshot |
Keybindings: Axe Tab¶
Sidebar Row Taxonomy¶
The Axe sidebar renders three row types so the operational tree reads at a glance:
- Lumberjack rows are top-level sections with a solid left accent bar (
▌) in the lumberjack hue, a[*]/[!]/[·]running/error/idle marker, the lumberjack name, and an optional compactNc / Necycles/errors chip at the end. - Chop rows are child rows indented under their parent with a
└─tree connector, a per-run status icon (✓success,!failure/timeout,?missing script,●running,*agent-launched,·no runs), and the chop name in a dim-gold child hue. - Background command rows (run via
!!) live below the lumberjack tree, separated by a dim divider line when both groups are present, and use a distinct command/slot badge so they cannot be mistaken for scheduled AXE work.
Dynamic Sidebar Width and No-Wrap Rows¶
Every sidebar row is rendered as single-line Rich Text with no_wrap=True and overflow="ellipsis". After each
refresh the widget computes the widest formatted row and emits a WidthChanged message; the AXE container resizes
between a 35-cell minimum and an 80-cell maximum, clamped further so the right-hand dashboard always keeps at least 40
cells. On terminals too narrow to fit a label even at the clamped width, the row ellipsizes rather than wrapping onto a
second line.
Controlled-Output Highlighting and ANSI Fallback¶
Output in the dashboard right panel uses a semantic highlighter for sources whose shape is controlled by sase, and falls back to ANSI rendering for everything else:
- Lumberjack aggregate logs (
[YYYY-MM-DD HH:MM:SS] [lumberjack] message) get timestamp, lumberjack name, status words (success,failure,timeout,running,error, …), PIDs, durations, exit codes, and counts colored by severity and consistent with the sidebar taxonomy. - Controlled chop output — the agent-launch line
Launched agent chop '<name>' (PID <pid>)emitted foragent_launchedruns — is highlighted with the chop name and PID coherent with sidebar colors. - External chop scripts and background command output are arbitrary text and stay on the ANSI fallback
(
Text.from_ansi) with the existing capping and tail-biased caching behavior.
Render cache slots are keyed on (source_id, source_type) so the semantic and ANSI paths cannot collide for the same
numerical identity.
Navigation¶
| Key | Action |
|---|---|
j / k |
Move to next / previous sidebar row (lumberjack, chop, or background command) |
Ctrl+N / Ctrl+P |
Page through the focused chop's run history (newer / older) |
g |
Scroll to top |
G |
Scroll to bottom (pins auto-scroll) |
Commands¶
| Key | Action |
|---|---|
+ |
Run agent |
r |
Run selected chop manually, or re-run the focused completed background command (!!) row |
x |
Start / stop axe (or kill the focused background command) |
X |
Clear output |
Leader Mode (, prefix)¶
| Key | Action |
|---|---|
,, |
Repeat the last leader command |
,h |
Run agent from home prompt context; bare prompts default to #git:home |
,p |
Open project lifecycle management (see Project Management Panel) |
,o |
Open model overrides (see Model Overrides) |
,R |
Show runners info |
Bang Mode (! prefix)¶
| Key | Action |
|---|---|
!! |
Run background command |
!x |
Start / stop axe (or select process) |
Copy Mode (% prefix)¶
| Key | Action |
|---|---|
%o |
Copy visible output |
%O |
Copy full output |
%s |
Copy sase ace snapshot |
Axe Control¶
| Key | Action |
|---|---|
Q |
Stop axe and quit |
Query System¶
Editing Queries¶
Press / to open the query editor. The current canonical query is pre-filled.
To save a query, prefix with #:
#3 "myproject"-- save to slot 3# "myproject"-- save to next available slot#3(no query) -- delete slot 3
Saved Queries¶
Press 1-9 or 0 to instantly load a saved query. These also work from within the help modal (?).
Query History¶
| Key | Action |
|---|---|
^ |
Navigate to previous query in history |
_ |
Navigate to next query in history |
Query history is available on the PRs tab and tracks queries as you switch between them.
See docs/query_language.md for the full query syntax reference, including boolean expressions,
status shorthands, property filters, and searchable fields.
Global Keybindings¶
These work on all tabs:
| Key | Action |
|---|---|
Tab / Shift+Tab |
Switch between PRs, Agents, and Axe tabs |
# |
Open XPrompt Browser (see XPrompt Browser below) |
. |
Toggle visibility of hidden items (reverted PRs, non-run agents, or axe commands) |
: / ; |
Open the context-aware Command Palette |
,i |
Open Activity Dashboard modal |
i |
Show notifications inbox |
I |
Pin idle mode (IDLE stays until I is pressed again; keypresses don't clear it) |
Ctrl+G |
Open the agent editor pre-filled with the most recent VCS xprompt prefix |
Ctrl+L |
Dismiss all currently-visible toast notifications |
@ |
Open the stashed-prompt restore picker |
Q |
Stop axe daemon and quit |
y |
Refresh current tab |
q |
Quit |
? |
Show help modal |
Quit Confirmation¶
When quitting (q or Q) while background tasks are still running (task queue workers or background command slots), a
confirmation dialog appears showing the count of active tasks and asking whether to kill them and quit. Declining the
dialog cancels the quit and returns to the TUI.
Command Palette¶
Press : or ; from any tab to open the Command Palette — a context-aware modal listing every keymapped action
that is currently runnable. The palette is the discovery surface for the TUI: rather than memorizing every chord, you
can search by command label, key sequence (e.g. %n, ,t, zc), category, or alias.
Behavior:
- Only commands applicable to the current tab and selected entry are shown by default. For example, PR diff appears only when a PR is selected; AXE start/stop appears only on the AXE tab; agent-specific actions appear only when an agent row (not a group banner) is focused.
- Each row shows the keybinding, the command label, and a category badge such as
Navigation,PR Actions,Agent Actions,Copy, orLeader. - A title-bar badge (
PRs,Agents, orAXE) reflects the current tab.
Keybindings inside the palette:
| Key | Action |
|---|---|
Type |
Filter commands (case-insensitive substring) |
↑ / ↓ |
Move highlight |
Ctrl+P / Ctrl+N |
Move highlight |
Enter |
Run the highlighted command |
Esc |
Close without running anything |
The palette delegates execution to the same handlers that the keybindings use, so behavior matches pressing the chord
directly. Selecting a built-in mode subcommand (e.g. %n to copy an agent name) runs the action without forcing you
through the transient prefix mode. Custom modes defined in sase.yml are also represented per-command.
The : / ; binding follows your configured keymap. To rebind it, set ace.keymaps.app.open_command_palette in
~/.config/sase/sase.yml; comma-separated keys in that setting are treated as alternate bindings for the same action.
Project Management Panel¶
Press ,p from any tab to open the Project Management panel. It lists non-system projects across active,
sibling, and inactive lifecycle states, so hidden projects remain reachable even though normal launch and discovery
views omit them. Rows include workspace path, active claim count, launchability, and lifecycle or workspace warnings.
| Key | Action |
|---|---|
j / k |
Move selection |
/ |
Filter projects by text |
Tab |
Cycle lifecycle filter: active, sibling, inactive, all |
Enter |
Activate the highlighted project when it is not active |
m |
Mark or unmark the highlighted project |
u |
Clear all project marks |
e |
Open the highlighted ProjectSpec in $EDITOR |
a |
Activate highlighted project, or all marked projects |
d |
Deactivate highlighted project, or all marked projects |
Ctrl+D |
Delete highlighted SASE project directory, or all marked directories |
F |
Force the last blocked deactivate after confirming live-work checks |
R |
Reload project records |
q / Esc |
Close the panel |
When one or more projects are marked, a, d, and Ctrl+D target the marked set instead of only the highlighted row.
Successful lifecycle changes clear the affected marks; blocked or failed rows stay marked so you can inspect or retry
them. Marks survive filtering and are pruned on reload when their project records disappear.
Deactivating uses the same locked mutation path as sase project deactivate. If a project still has RUNNING claims or
live artifact markers, ACE shows the blocked reason and lets you retry with F when the force action is intentional.
Activating a project from this panel makes it available again in normal launch pickers.
e suspends ACE, opens the selected ProjectSpec in $EDITOR (falling back to nvim), holds the ProjectSpec edit lock
for the editor session, then reloads project records. In this panel, Ctrl+D asks for confirmation before deleting the
entire SASE project directory: ProjectSpecs, project-local config, artifacts, and related state under
~/.sase/projects/<project>/. Deletion is refused while the project still has RUNNING claims or live artifact
markers. It does not delete workspace checkouts, and system-managed projects such as home are excluded from the panel.
Model Overrides¶
Press ,o from any tab to open the Model Overrides modal. It manages temporary primary and worker provider/model
overrides for new agent launches without editing ~/.config/sase/sase.yml.
The modal shows two lanes:
- Primary — the normal default lane for launches without an explicit
%modeldirective. - Worker — the secondary lane used by delegated work such as
sase bead workphase agents that do not have an explicit per-bead model.
Each row shows the current effective model and its source: override, config <key> (the matched
llm_provider.worker_models key, e.g. config claude/opus or config codex), follows primary, or default. Set or
change the primary override with s/c, clear it with x, set or change the worker override with w, and clear the
worker override with W. Both lanes use the same provider-grouped picker and duration choices: 15m, 30m, 1h,
2h, 4h, Until cleared, or a custom duration like 45m, 1h30m, 90m.
When a worker override is active, the top bar includes a compact W ... chip next to the primary override indicator.
Permanent llm_provider.worker_models config is visible in the modal, not the top bar.
Behavior¶
- The overrides apply only to default lane selection. Explicit prompt directives (
%model:codex/o3,%model:opencode/anthropic/claude-sonnet-4-5) and an explicitprovider_nameargument always win.%model:workerexplicitly opts into the worker lane. - Already-running agents keep their current provider/model. Only new launches use the override.
- The primary override is persisted to
~/.sase/llm_override.json; the worker override is persisted to~/.sase/llm_worker_override.json. All sase processes on the same machine see those files. Reads are best-effort self-cleaning: expired or malformed state files are deleted on next access. Until clearedis a no-expiry mode — convenient, but still a temporary state, not a permanent config edit.- The temporary override is independent of
SASE_MODEL_TIER_OVERRIDE. A concrete temporary model override takes the full provider/model path; the tier override only applies when no concrete override is active.
Examples¶
codex/o3for1h— switch to Codexo3for the next hour, then revert to the configured default.opencode/anthropic/claude-sonnet-4-5for1h— switch to an OpenCode provider/model pair.sonnetfor30m— known bare model name; the provider is inferred from plugin metadata.- Worker
codex/gpt-5.5for1h— route%model:workerlaunches through Codex for the next hour. Until cleared— leave the override active across sessions; clear it later from the same,omodal.
See docs/llms.md for the resolution order and state-file format.
Notifications Modal¶
Press i (or the ,n leader chord to jump straight to an agent's notification) to open the notifications modal. See
docs/notifications.md for the full keybinding reference, tag tabs, the priority/inbox/muted
section taxonomy, and the per-notification snooze and mute affordances.
The top-bar notification indicator color reflects the highest-priority unread bucket: orange for PRIORITY notifications (plan approvals, user questions, mentor reviews, axe errors, CRS results, agent error reports), gold for regular INBOX notifications, and cyan when only MUTED notifications remain.
Notification Actions¶
Some notifications carry an action field that triggers a handler when the notification is selected. The following
notification action types are supported:
| Action | Source | Behavior |
|---|---|---|
HITL |
Workflow | Opens the workflow human-in-the-loop response modal |
JumpToAgent |
Agent/workflow | Jumps to the matching Agents-tab row |
JumpToChangeSpec |
Sync/workflow | Jumps to the referenced ChangeSpec on the PRs tab |
JumpToMentorReview |
Mentors | Jumps to the ChangeSpec and opens mentor review output when available |
PlanApproval |
Agent | Opens the plan approval modal |
Tmux |
External bridge | Runs tm <workspace-name> for the notification's action_data.workspace_dir |
UserQuestion |
Agent | Opens the structured user-question response modal |
ViewErrorReport |
Axe/agent | Opens action_data.error_report_path, or the first attached file, in $EDITOR |
memory_review |
Memory | Suspends ACE and opens the memory proposal review TUI at that proposal |
The axe error_digest chop creates ViewErrorReport notifications whose digest files live under
~/.sase/axe/error_digests/digest_<timestamp>.txt; user-agent failures can use the same action for their own attached
error reports. Memory proposal notifications created by sase memory write --notify use memory_review with
action_data.proposal_id. Selecting one opens the same review UI as sase memory review, preselected on that proposal;
approval or rejection still happens inside the review UI.
Toast Notifications¶
Each newly-arrived notification produces a short toast in the TUI. The toast text is derived per-action type (plan, question, HITL, axe error, ChangeSpec sync, agent update) so the message previews the actual event rather than a generic "N new notification(s)" line. Severity is also picked per type: plans, questions, and HITL render as warnings; axe errors (and sync failures) render as errors; everything else renders as information.
When more than 3 notifications arrive in the same poll tick, per-notification toasts are consolidated into one grouped
toast per severity bucket (e.g., 2 warnings: 1 plan, 1 question). Ordering is urgency-first: errors, then warnings,
then information. Silent notifications are excluded from this pipeline entirely.
Agent completion and failure toasts include the %name-set agent name with an @ prefix when present (e.g.,
CLAUDE(opus) @sase-q.land completed: ace(run)-...); anonymous agents (no agent_name) keep the prior format.
XPrompt Browser¶
Press # on any tab to open the XPrompt Browser modal. It displays all discovered xprompts in a two-panel layout: a
filterable list on the left and a syntax-highlighted preview on the right.
Xprompts are grouped by source (CWD .xprompts/, CWD xprompts/, Home ~/.xprompts/, Home ~/xprompts/,
project-specific, config sase.yml, plugins, built-in). Workflow xprompts (multi-step YAML) are marked with a gear
icon; standalone workflows are displayed with the #!name insertion syntax. Project-local xprompts defined in each
project's sase.yml file are also included, even though the TUI's normal config loading does not read project-local
config files.
The list rows and preview metadata show the same insertion form and visible input metadata used by Ctrl+T completion.
Step-only inputs are hidden from this user-facing surface because they are supplied by workflow execution rather than
typed by the user.
Keybindings¶
| Key | Action |
|---|---|
Ctrl+N |
Navigate to next xprompt |
Ctrl+P |
Navigate to previous xprompt |
Ctrl+D |
Scroll preview panel down |
Ctrl+U |
Scroll preview panel up / clear input |
Enter |
Edit highlighted xprompt in $EDITOR |
Ctrl+O |
Add a new xprompt |
Esc |
Close browser |
Type in the filter input to narrow the list in real time.
Editing XPrompts¶
Press Enter on any xprompt to edit it in $EDITOR. All xprompts are editable, including plugin and built-in sources —
these are copied to the highest-priority user directory (~/.xprompts/) before opening, so edits create an override
rather than modifying the original. After saving, the browser offers to commit and push changes to git if applicable.
Creating XPrompts¶
Press Ctrl+O to start the guided creation flow:
- Location modal — Choose where to save the new xprompt (CWD
.xprompts/, CWDxprompts/, Home~/.xprompts/, Home~/xprompts/, or a config file). PressCtrl+Gto open the selected config file in$EDITORinstead of proceeding with creation. - Filename modal — Enter a filename (
.mdfor prompt parts,.ymlfor workflows). Workflow files are pre-filled with a YAML template containing the workflow scaffold. - Editor — The file opens in
$EDITORfor editing. - Git commit — After saving, the browser offers to commit and push changes.
Idle Detection¶
ACE tracks user activity and displays an orange IDLE badge in the top bar when the user has been inactive for longer
than the configured threshold (ace.inactive_seconds, default: 600 seconds). The badge is also shown when the user
presses ,I (leader chord) to manually mark themselves as inactive. Any keypress re-activates the user and hides the
badge.
Pressing I (capital) activates pinned idle mode, shown as a red ■ IDLE badge. Pinned idle stays active
regardless of keypresses — only pressing I again clears it. This is useful when you want to remain marked as idle
while still interacting with the TUI. Pinned idle state is persisted to ~/.sase/tui_pinned_idle and automatically
restored when the TUI restarts, so the user remains marked as idle across sessions.
External tools (e.g., chop scripts) can call is_idle() from sase.ace.tui_activity to check idle status
programmatically.
Jump All Modal¶
Press ` (backtick) on any tab to open the Jump All Modal. It displays all entries across PRs, Agents, and Axe tabs
with single-keypress hint characters for instant navigation. Selecting an entry switches to the appropriate tab and
focuses it.
Hint characters are drawn from an extended alphabet — lowercase a–z first, then uppercase A–Z — so modals with
many entries can still fit a unique single-keypress hint per row without resorting to multi-character hints.
| Key | Action |
|---|---|
| Hint char | Jump to the corresponding entry |
Esc / q |
Close modal |
The modal groups entries by tab (PRs, Agents, Axe) and shows contextual information for each: PR names and statuses, agent names with running indicators, and Axe lumberjack/command labels.
Jump Back¶
Both jump modals support a jump-back feature for toggling between two entries:
- Backtick jump-back: Pressing
`inside the Jump All Modal returns to the previous position, enabling quick toggling between two entries across tabs. - Apostrophe jump-back: Pressing
'twice ('') in the single-tab entry jump mode jumps back to the previously jumped-from entry. The footer shows a "JUMP" mode indicator with' backwhen a target exists. - Fast jump:
Ctrl+Oruns the same current-tab jump-back path without painting hints first; when no jump-back target exists, it selects the first current-tab hint.
The single-tab variant (' apostrophe) shows entries only from the current tab with the same hint-character navigation.
Mentor Comment Stats in PR List¶
When a ChangeSpec has completed mentor reviews with comments, the PRs tab list entry shows inline stats:
- checkmark + count (e.g.,
✓3) — number of accepted comments - dot + count (e.g.,
●2) — number of unread comments
These stats are computed from the latest commit entry's finished mentors. They update as you accept or read comments in the Mentor Review modal.
Tab Bar Display¶
The tab bar renders plain tab labels (PRs, Agents, AXE). Per-bucket counts live inside each tab's body — for
example the per-panel count summaries on the Agents tab — rather than as suffixes on the tab title itself.
Background Task Indicator¶
A gear icon (⚙) with a count appears in the top bar when background tasks are running (e.g., sync, mail, accept operations). The indicator automatically hides when all background tasks complete.
Runners Modal¶
Press ,R (leader + R) to open the runners modal. It shows concurrency information including hook runners, agent
runners, and a Background Tasks section listing active and recently completed background tasks (sync, rebase,
accept, mail, add-tag). Each task entry shows its type, CL name, status, and timestamps.
File Panel Trimming¶
When viewing agent files on the Agents tab, large files are automatically trimmed to fit the visible viewport. A blue
indicator shows "N more lines below" when content is trimmed. Trim controls (-, =) are listed in the
Agent Actions keybindings above. Trim state is preserved when switching between agents or refreshing
data.
Pressing G on a trimmed file auto-expands it first, then scrolls to the bottom — so jumping to the end of a large file
never leaves you staring at a trimmed page.
Image Preview Foundation¶
ACE renders PNG, JPEG, WebP, and GIF attachments with a Pillow-backed Rich cell preview. The renderer decodes the first image frame, preserves aspect ratio within the visible panel bounds, composites transparency, and paints colored half-block cells using truecolor when the terminal advertises it and 256-color approximations otherwise.
Generated images are already attached to successful agent completion notifications and recorded in done.json as
image_paths. The Agents tab file panel and notification modal route supported raster image attachments through this
preview layer before attempting text decoding. See agent_images.md for supported image extensions,
guardrails, and current preview behavior.
Agent Auto-Naming¶
Prompts with no %name directive, or with a bare %name, use the plain auto-name template @. SASE reserves the
lowest available token from the sequence 0, 1, ..., 9, a, ..., z, 00, 01, ...; with no reserved names,
plain auto-naming yields concrete names such as 0, then 1.
An explicit %name value containing exactly one @ marker is an agent-name template. SASE substitutes the same token
sequence into the marker, so the first allocation for %name:@.cld becomes 0.cld, %name:build-@ becomes build-0,
and %name:research.@.final becomes research.0.final. Later %wait, #fork, and #resume references can use the
same template text; within a multi-agent launch, SASE rewrites those references to the concrete name already planned for
that template.
Names are permanent IDs: a name used by any existing agent state remains reserved until that agent is explicitly wiped
or deleted. This enables the fork-by-name workflow: press f on a running named agent to queue a follow-up that waits
for it to finish and then loads its conversation history.
Provider/Model Suffixes¶
When the same base name is shared by multiple co-launched agents (e.g. multi-model fan-out via the %model: directive),
the rendered display name carries a short .<provider> or .<provider>(<model>) suffix so each row is distinguishable.
Provider suffixes are supplied by the LLM provider plugins via the llm_provider_short_name hook (built-in defaults:
cld for Claude, cdx for Codex, agy for Antigravity). Additional provider plugins can contribute their own short
names. Model-name shorthands come from the llm_model_short_aliases hook (e.g. fable for claude-fable-5, gpt55
for gpt-5.5; see Model Short Aliases) and are resolved against the configured model so
the suffix stays compact regardless of how the model was spelled in the prompt or config. Single-runtime spawns omit the
suffix.
An explicit %name:<name> launch fails before spawning if <name> is already reserved. The prompt is saved as a
cancelled history entry and the error suggests the lowest free numeric suffix, such as <name>1. To deliberately reuse
a reserved name from the TUI, launch with %name:!<name>; the ! form confirms that SASE should wipe the previous
owner and then claim the name for the new agent. Reviving and dismissing agents preserve their stored names.
The durable registry lives at ~/.sase/agent_name_registry.json and is rebuilt from visible artifacts plus dismissed
bundles when missing or stale. Use sase agent names migrate-auto to run the historical auto-name migration that moves
older generated names into the permanent namespace; pass --force to rerun after the migration marker is present or
--json for machine-readable output.
Per-Step Naming for Multi-Agent Workflows¶
Plan-family workflows have a stable family name plus phase suffixes. The root row keeps the family name and acts as the
workflow container; generated follow-up rows and phase metadata use canonical double-dash suffixes. For example, if the
initial agent family is named a:
- The root workflow row keeps
a. - The planner phase uses suffix
--planand is displayed asa--planwhen ACE renders it as a phase child. - Feedback and question-continuation rounds become
a--2,a--3, etc. - Terminal follow-ups use the phase suffix, such as
a--code,a--epic,a--legend, ora--commit.
The base name (a) is reserved for the workflow as a whole, so %wait:a or @a references resolve to the family root.
New plan-family metadata stores double-dash role_suffix values (--plan, --2, --code, ...). ACE still
canonicalizes older dotted suffixes (.plan, .2, .code, etc.) and legacy single-dash suffixes (-plan, -2,
-code, etc.) when reading legacy artifacts.
Agent Statuses¶
Each agent in the Agents tab displays a status label indicating its current state. Statuses fall into two categories: active (the agent is still running or awaiting input) and completed (the agent has finished).
Active Statuses¶
| Status | Color | Description |
|---|---|---|
| RUNNING | Gold | Agent subprocess is executing |
| WAITING | Light blue | Agent is queued, waiting for another agent to succeed (%wait) |
| WAITING INPUT | Amber/orange | Workflow is paused at a human-in-the-loop (HITL) step |
| PLAN | Pink/magenta | Agent has produced a plan and is waiting for user approval |
| PLAN APPROVED | Cyan | Plan was approved; follow-up agent has been spawned |
| EPIC APPROVED | Cyan | Plan was approved as an epic; --epic follow-up is running |
| PLAN COMMITTED | Cyan | Plan was approved with auto-commit; --commit follow-up is running |
| QUESTION | Amber | Agent is asking the user a question (via /sase_questions) |
| RETRYING | Orange | Agent hit a retryable error and is in a countdown before retrying |
QUESTION status survives notification dismissal. While an agent is waiting for an answer it writes a
pending_question.json marker into its run directory and removes the marker once it resumes (whether the user replied,
the agent was killed, or it crashed). Any RUNNING row whose run directory contains the marker is shown as QUESTION,
so the "waiting on you" status keeps appearing even after you dismiss the matching question notification from the inbox.
The ,n shortcut (jump to the open question) reads the marker directly when no unread notification is left, so it can
still reopen the question modal.
QUESTION also propagates up agent families. When a completed row recorded a question (questions_times is non-empty)
but has neither a persisted question_response_path nor a later follow-up child, the parent workflow row inherits
QUESTION so the family still shows as waiting on you. Once the user response is persisted, the continued work usually
appears as the next numeric phase (--2, --3, ...); --q identifies the question phase in metadata and phase labels.
On the next status pass, the parent is re-evaluated without the stale question override. If the parent has several
active children, the most recently started one wins, so a newer RUNNING child can overtake the QUESTION override on
the parent.
The keybinding footer renders available conditional actions as non-breaking key/label chips. When the chips do not fit
on one line, the footer switches to a deterministic grid so narrow terminals and leader-mode action sets do not wrap in
the middle of a binding. Mode labels such as LEADER are pinned on the left, and the axe/status indicator remains
pinned on the right.
The footer also shows axe daemon status indicators:
| Status | Color | Description |
|---|---|---|
| RUNNING | Green | Axe daemon is running normally |
| STOPPED | Red | Axe daemon is not running |
| STARTING | Yellow | Axe daemon is starting up |
| STOPPING | Yellow | Axe daemon is shutting down |
| RESTARTING | Deep sky blue | Axe daemon is restarting (triggered by --restart-axe flag) |
During TUI startup the footer slot shows a live starting stopwatch with a rotating glyph in place of the daemon status, ticking at ~10 Hz until the TUI finishes mounting and the real axe status resolves. The background color turns from its normal tone to a slow-startup tone once the elapsed time crosses the slow threshold, giving immediate visual feedback on cold-start latency. A safety timeout forcibly retires the stopwatch if the mount signal never fires.
Completed Statuses¶
| Status | Color | Description |
|---|---|---|
| DONE | Green | Agent completed successfully |
| PLAN DONE | Green | Plan workflow fully completed (all steps) |
| TALE DONE | Green | Tale plan workflow fully completed (all follow-ups) |
| EPIC CREATED | Green | Plan workflow completed and its latest -epic follow-up finished successfully |
| FAILED | Red | Agent exited with an error |
Completed agents can be dismissed with x on a single row, or through the X cleanup panel for focused-panel, global,
tag, marked, group, and custom selections. DONE, PLAN DONE, and TALE DONE rows with a saved response path are
resumable from the Agents tab.
When a terminal agent becomes unread, ACE marks it with the completed-agent indicator and includes it in the Agents
header unread count. Selecting that row, jumping to it with ,j, or toggling it back to read with U acknowledges the
row and dismisses the matching user-agent completion notification. Manually marking a row unread with U arms it for
normal acknowledgement after you move away and return, so the marker can be used as a short-lived reminder without
leaving stale inbox entries.
If the currently focused row finishes while you are already on the Agents tab, ACE still marks it unread and keeps the completion notification active until a real navigation or selection event acknowledges it. A refresh that merely preserves focus does not silently consume the unread marker.
The unread count in the Agents header is drawn as black text on a gold pill so the "you still have unseen completed
work" signal stands out from the rest of the colored metrics. It uses the same gold tone as the top-bar notification
indicator, giving you a single color to scan for.
Switching to the Agents tab does not bulk-dismiss completion notifications. ACE projects active completion notifications
onto unread rows, then acknowledges rows one at a time when you select or navigate into a terminal unread row. Bulk
acknowledgement is explicit through ,u, which marks loaded unread completed agents read. Plan approvals and user
questions are never auto-dismissed by this flow; they always require explicit y / n confirmation from their
respective modals.
Agent Revival¶
Press R on the Agents tab to revive previously dismissed work. ACE opens the saved-group revival modal first, showing
newest saved groups with a right-hand preview of included agents, projects, PRs, statuses, provider/model labels, and
revival count. Select a group and press Enter to revive it, choose Load more saved groups... to page older groups,
or choose Custom revival search... to open the older dismissed-agent search where you choose all, home, project, or
PR scope manually.
Use m to mark related Agents-tab rows and then s to save and dismiss them as a group. The save modal accepts an
optional human name. Leaving it blank keeps the generated display title, such as "3 agents from @review" or "2 agents in
auth_retry". Saving a marked group hides the selected rows from the normal Agents tab without killing running processes.
When a marked top-level workflow row has child rows, ACE also includes the children in the saved group so revival can
restore the original tree.
Dismissed agents are saved as individual bundle files under month shards in ~/.sase/dismissed_bundles/YYYYMM/ and can
be restored later. Saved group metadata lives under ~/.sase/dismissed_agent_groups/ and stores stable references to
those bundle files plus the optional group name, status counts, projects, PRs, model/provider metadata, and agent tags.
There is no limit on the number of dismissed agents or saved groups that can be stored.
Dismiss operations are O(1) per agent: each agent is saved to its own JSON file rather than a monolithic store. Parent
workflow rows use <raw_suffix>.json; workflow children use <raw_suffix>__c<step_index>.json. ACE keeps a SQLite
summary index in the dismissed-bundle directory so the revive modal and internal lookups can list dismissed agents
without opening every bundle. Use sase agent archive verify to check that maintenance index, or
sase agent archive rebuild-index to rebuild it from bundle files. The index stores metadata such as status, name,
project, model, provider, workflow, and ChangeSpec metadata; it is not a full-text copy of agent chat contents.
Revival removes the agent identity from the dismissed set, restores enough artifact files for ACE to rediscover the
agent, and preserves the dismissed bundle as historical recovery data. Saved-group revival skips missing bundle
references with a warning and restores the remaining agents. Group metadata is not deleted after revival; ACE marks the
group with revived_at and increments times_revived so the modal can show previous use. The reload path forces a
full-history scan and can hydrate the just-revived row directly from the bundle, so agents still appear after revive
even if the persistent artifact index was empty or stale.
Every revival also writes structured events to ~/.sase/logs/events.jsonl (start, per-agent success, per-agent
failure). Read them back with sase revive-log — see Agent revival audit log for
the record schema and CLI flags.
Legacy Dismissed-Name Prefix¶
Current dismiss and revive operations preserve stored agent names, per-agent tags, and top-level/workflow-child
identity. Older dismissed bundles may still contain YYmmdd.<base> names from the previous dismissal model, and ACE
keeps compatibility helpers for reading those bundles. Bare %wait (no target) intentionally skips legacy
dismissal-prefixed candidates so it anchors on a live, visible agent.
Agents Tab Metadata Panel¶
The Agents tab metadata panel (cycled to via ]/[) shows structured information about the selected agent:
- Agent details: Name, status, model, provider, CL association, and chronologically sorted timestamps:
Bead— shown for agents launched bysase bead work, inferred from phase, exact epic, or legacy.landnamesWAIT— when the agent was spawned (waiting for a slot)BEGIN— when execution startedPLAN— each plan proposal round (multiple entries when re-planning occurs)FBACK— each time the agent requested feedback from the userQUEST— each time the agent asked the user a questionRETRY— each time the agent entered retry state (retryable error)CODE— when the agent began writing codeEPIC— when an epic follow-up agent was launched after plan approvalDONE— when execution completed- OUTPUT VARIABLES: Small string values written by the agent with
sase var set KEY=VALUE. Keys are sorted for stable display, multi-line values are indented, and the section is omitted when the agent has not published variables. These values are stored inagent_meta.json, so they are visible metadata rather than secret storage. - AGENT REPLY: The agent's live or completed reply content, streamed from
live_reply.mdduring execution and read from the artifacts directory after completion. When per-turn reply timestamps are available (recorded inlive_reply_timestamps.jsonl), the reply is displayed with timestamp dividers between each agent turn. For agents with follow-up phases (planner, feedback rounds, coder), the AGENT REPLY section consolidates replies from all phases into a single view with purple phase dividers showing each phase's label and start time. Phase labels are derived from canonical plan-familyrole_suffixvalues:--planrenders asPLANNER,--codeasCODER,--qasQUESTIONS,--epicasEPIC,--legendasLEGEND,--commitasCOMMIT, and numeric feedback suffixes such as--2asPLANNER (round 2). Legacy dotted and single-dash suffixes render the same way. - WORKFLOW VARIABLES: xprompt workflow output variables from step outputs with additional
meta_*keys are grouped under a dedicated header. The special routing keysmeta_project,meta_changespec, andmeta_workspaceare still promoted into the normal header fields; other metadata keys are title-cased and shown in this section.
When the file or tools panel is empty, the g/G keys automatically fall back to scrolling the metadata panel.
Agents Tab Tools Panel¶
The tools panel sits between the file panel and the metadata panel in the Agents-tab cycle (] advances forward, [
goes back). It shows a chronological timeline of the LLM tool calls the selected agent has made — file reads, edits,
bash invocations, web fetches, sub-agent launches, and so on.
Entries are read from the tool_calls.jsonl artifact in the agent's run directory. Each call renders as one timeline
row:
- A status label colored by outcome —
ok(success),fail(error),stop(interrupted),agent(sub-agent launch), orwait(the post-call record has not arrived yet). - The tool name, optionally followed by a compact target (such as the file path the tool acted on) and the call's duration.
- A short, length-bounded preview of the call result on the next line, when the collector captured one.
The panel header shows the total call count, the failure count, the interrupted count, and a timestamp for the most
recent reload. While a background reload is in flight (because the artifact changed on disk), (refreshing...) appears
next to that timestamp. The body shows No tools artifact available when the file does not yet exist for this agent and
No tool calls recorded when the file exists but contains zero records.
For retry chains and planner-to-coder follow-up families, the panel aggregates tool_calls.jsonl from related artifact
directories so the selected logical agent shows one ordered tool timeline. Discovery uses the persistent artifact index
when it is available; if the index is missing or stale, ACE falls back to direct lineage pointers plus a bounded scan of
nearby legacy sibling artifacts.
Records are produced by writers that share one normalized on-disk format. Claude uses the SASE tool-call hook collector
as the preferred source and keeps its stream-derived parser as a fallback when hooks are unavailable. Codex writes
equivalent rows from its codex exec --json stream with runtime: "codex" and source: "stream"; current Codex
start/completion events can show pending rows, result previews, failures, interruptions, and durations, while older
completed-only function_call rows remain readable with more limited detail. Qwen writes stream-derived rows from its
--output-format stream-json output with runtime: "qwen" and source: "stream"; start/completion (and Qwen's
tool_use / tool_result) pairs collapse into single rows the same way Codex pairs do. Antigravity (agy) runs in
plain-stdout mode; SASE never scrapes display prose, but supported Antigravity versions may contribute guarded
source: "trajectory" rows from the local trajectory DB. When that extractor is unavailable, the panel simply shows
nothing for agy runs. See LLM Providers — Claude tool-call hooks,
LLM Providers — Codex tool-call capture,
LLM Providers — Qwen tool-call capture, and
LLM Providers — Antigravity (agy) Integration for provider integration details.
Plan Workflows¶
When an agent submits a plan via /sase_plan (or sase plan propose, including the %epic path), it enters a planning
phase before executing:
- PLAN — The agent has produced a plan and is waiting for user approval. Shown in pink/magenta in the prompt panel.
- PLAN APPROVED — The plan has been approved and the follow-up agent has been spawned. Shown in cyan/turquoise.
- PLAN REJECTED — The user rejected the plan via the
rkeybinding. Treated as a terminal decision (not a failed agent run): the rejected agent remains on the Agents tab as a completed row with this status, and the redundant completion notification is suppressed.
Plan files generated by the agent are displayed in the file panel alongside other agent artifacts. Plan approval notifications include the LLM provider and model name, so users can see which model proposed the plan (visible in both the TUI notification modal and Telegram delivery).
When sase plan propose writes the plan, it also touches ~/.sase/.ace_refresh_pulse to wake any running TUI
immediately — PLAN status appears without waiting for the next auto-refresh tick. The pulse file is consumed by the
inotify artifact watcher (see Auto-Refresh) and is harmless if no TUI is open.
Root plan workflows also surface PLAN when a re-proposed plan is still awaiting review. Plan and feedback timestamps
from feedback-round children (--2, --3, ...; legacy -2, .2, etc.) propagate onto the root entry, and whenever
the root's latest plan timestamp is newer than its latest feedback timestamp the override engine restores PLAN over a
RUNNING or DONE label. This applies only to root plan workflows that have not yet spawned a terminal follow-up
(--code, --epic, ...); once a terminal follow-up is launched, the parent moves on to PLAN APPROVED (or the
matching follow-up status) instead.
The Plan Review modal title shows a provider-themed PROVIDER(model) badge between the "Plan Review" label and the plan
filename — orange for Claude, lime for Codex, Antigravity indigo (#6E5DE7) for agy, neutral muted for other providers.
The badge is omitted when provider/model metadata is absent, leaving the legacy title shape unchanged.
The same pending approvals are available from the CLI. Run sase plan to see pending proposals, recent approvals, and
inferred rejected archived plans; run sase plan approve <id-prefix> --kind approve|commit|epic|legend|tale to write
the same response protocol used by the TUI modal. If the selector is omitted, the CLI approves only when exactly one
proposal is pending. approve starts the coder without committing an SDD plan, tale commits the plan as an SDD tale
and starts the coder, epic and legend commit the matching SDD tier and launch the bead follow-up, and commit
records the approved plan in SDD without launching a coder. -m/--model picks the follow-up agent's model, while
-p/--prompt adds extra coder instructions for the approve and tale paths.
For active Agents-tab rows, a cycles through normal auto-approve, epic auto-approve, and disabled. Epic auto-approve
is the same plan-specific path as the %epic directive: the next submitted plan is accepted as an epic, SDD epic
artifacts are written, beads are initialized, and the epic follow-up agent is launched. It does not answer unrelated
HITL prompts.
Plan Approval Keybindings¶
| Key | Action |
|---|---|
a |
Approve and run coder without committing an SDD tale |
t |
Save as tale and run coder |
c |
Open Custom Approval |
r |
Reject the plan |
f |
Request feedback (send follow-up questions to the agent) |
e |
Edit the plan file in $EDITOR |
E |
Mark the plan as an epic (creates bead) |
L |
Mark the plan as a legend (creates legend-tier bead) |
y |
Copy plan content to clipboard |
Y |
Copy plan file path to clipboard |
Ctrl+D/U |
Scroll plan content down / up |
g / G |
Scroll to top / bottom |
q / Esc |
Cancel |
The question modal also supports y to copy questions and selected answers.
Custom Approval¶
Pressing c in the plan approval modal opens a custom approval dialog. Choose the approval outcome directly: Approve,
Tale, Epic, or Legend. These choices map to the same response protocol used by external approval transports: Approve
runs the coder without asking the runner to commit an SDD plan, Tale commits under sdd/tales, Epic commits under
sdd/epics, and Legend commits under sdd/legends.
| Key | Action |
|---|---|
Enter |
Choose the highlighted action |
a |
Highlight Approve |
t |
Highlight Tale |
e |
Highlight Epic |
l |
Highlight Legend |
m |
Select coder model |
p |
Edit additional coder prompt |
Ctrl+N/P |
Next / previous action |
q / Esc |
Cancel |
The dialog keeps the custom coder prompt and follow-up model controls:
- Additional prompt — Optional extra instructions for the coder follow-up. It is used by Approve and Tale; Epic and Legend generate their follow-up prompts from the bead xprompts.
- Coder model — Select an LLM model for the next follow-up agent instead of using the worker-lane default. For
Approve and Tale that agent is the coder; for Epic and Legend it is the bead follow-up. Shows all registered models
grouped by provider (Claude, Codex, Antigravity, Qwen, OpenCode) with a "Custom..." option for freeform input. Type to
filter by provider, model id, label, or short alias; use
j/kor arrows to navigate,Enterto select,Escto clear the filter or cancel, and'for jump hints over the visible selectable rows. The displayed default is the worker lane resolved from the planner's concrete provider/model — an active worker override, a matchingllm_provider.worker_modelsentry for the planner's primary lane, then the planner's own provider/model. Selecting a specific model and then re-opening the picker and choosing "Worker model (default)" resets the follow-up back to that worker default (distinct from pressingEsc, which keeps the current selection).
The custom approval dialog no longer exposes separate commit/run switches because the selected outcome determines the commit location and follow-up behavior.
Linked Chats in Multi-Step Workflows¶
When a workflow spawns multiple agents (e.g., a planner step followed by a coder step), the chat history files for each
step are cross-linked via a ## Linked Chats markdown section. This section is inserted near the top of each chat file
and lists all related agents with their roles and file paths, making it easy to trace the full workflow from any
individual agent's chat history.
For example, a plan-then-code workflow produces chat files with:
## Linked Chats
- **1. planner** — `/path/to/planner_chat.md`
- 2. coder — `/path/to/coder_chat.md`
The current agent's entry is bolded for quick identification.
Retry/Fallback Display¶
When an agent encounters a retryable error (configured via llm_provider.retry), the Agents tab shows retry state:
- RETRYING — Shown in bold orange when waiting before the next retry attempt. Includes a countdown timer:
RETRYING (45s). - ↻N — Shown after the status for running agents that have retried. The number indicates how many retries have
occurred (e.g.,
↻2means two retries so far). - ▸Model — Appended to the retry annotation when the agent has fallen back to an alternate model (e.g.,
↻3▸flash).
Prior Agent Attempts¶
Every time the axe retry loop retries an agent — context-limit retry, provider/API-error retry, user-configured retry,
or fallback-model switch — the failed attempt's partial reply, error text, timestamps, and model are snapshotted under
<artifacts_dir>/attempts/<N>/. The AGENT REPLY area in the Agents tab renders these prior attempts inline with styled
dividers before the current/final attempt, so the full arc of the agent's work stays visible in one scroll.
ACE hydrates prior-attempt history lazily. Normal Agents-tab refreshes do not enumerate every attempts/<N>/ directory;
the selected detail panel, D attempt-view toggle, and content search hydrate the needed attempt records on demand.
Press D to collapse the view to the current attempt only; press D again to re-expand. The binding only appears in
the keybinding footer when the selected agent has one or more prior attempts.
Custom Keymaps¶
All TUI keybindings are configurable via the ace.keymaps section in sase.yml. You can remap any built-in key and
define entirely new prefix-key modes.
Remapping Built-in Keys¶
Override any app-level keybinding under ace.keymaps.app:
ace:
keymaps:
app:
next_changespec: "n" # Remap j → n
prev_changespec: "p" # Remap k → p
show_notifications: "N" # Remap i → N
Custom Modes¶
Define user-defined prefix-key modes under ace.keymaps.modes. Each custom mode has a prefix key and a keys dict
where each sub-key specifies either a shell command or a built-in action:
ace:
keymaps:
modes:
my_mode:
prefix: ";"
keys:
run_tests:
key: "t"
shell: "just test"
show_log:
key: "l"
shell: "git log --oneline -20"
refresh:
key: "r"
action: "refresh"
Pressing ; activates the mode, then pressing t runs just test, l shows the git log, etc.
Validation¶
The keymap loader validates all configuration:
- Invalid keys are reverted to their defaults with a warning
- Duplicate keys (two actions bound to the same key) are detected and the conflicting override is reverted
- Prefix conflicts between custom mode prefixes and existing app bindings are warned
See docs/configuration.md for the full ace.keymaps configuration reference.
Prompt Input Widget¶
The prompt input is a multiline TextArea widget that supports two editing modes: INSERT and NORMAL. The widget provides markdown syntax highlighting for prompt content (headings, bold, italic, code blocks, lists, etc.).
When loaded prompt text contains literal top-level --- multi-agent separators, ACE renders the text as a prompt stack:
one pane per agent segment. YAML frontmatter at the start stays prompt-level metadata, and --- lines inside fenced
code blocks are left alone. A #name multi-agent xprompt invocation stays a single pane and expands only when it is
launched. During live editing, typed --- lines stay literal text; add prompt panes with g- in prompt NORMAL mode.
The detailed multi-agent parsing rules live in the XPrompt reference.
INSERT Mode (Default)¶
| Key | Action |
|---|---|
Enter |
Submit; in a prompt stack, open the submit chooser |
Ctrl+S |
Submit the whole stack top-to-bottom |
Ctrl+G Enter |
Submit only the selected pane |
Ctrl+C |
Cancel the prompt; in a prompt stack, cancel only the selected pane |
Ctrl+J |
Insert a newline |
Ctrl+A |
Move to start of line (jumps to previous line start if already at col 0) |
Ctrl+E |
Move to end of line (jumps to next line end if already at end) |
Ctrl+G |
Start the prompt-local prefix; press g or Ctrl+G again to open $EDITOR |
Ctrl+G Enter |
Submit only the selected pane |
Ctrl+G j/k |
Focus the next / previous pane and leave the target pane in INSERT mode |
Ctrl+G J/K |
Move the active pane down / up and leave it in INSERT mode |
Ctrl+G - |
Add an empty bottom pane |
Ctrl+G = |
Show/focus the xprompt frontmatter panel |
Ctrl+G s/S |
Stash the selected pane / every non-empty pane |
Ctrl+G p |
Open the stashed-prompt picker |
Ctrl+Y |
Open the workflow YAML editor |
Ctrl+K |
Open prompt history, filtered by the current single-line prompt |
Ctrl+P |
Cycle toward older workspace MRU prefixes, including a no-prefix stop before wrapping |
Ctrl+N |
Cycle toward newer workspace MRU prefixes, including a no-prefix stop before wrapping |
Ctrl+T |
Completion (directives, xprompts, slash skills, or file paths; see Completion) |
Ctrl+R |
Recursive fuzzy file finder using the same prompt-aware path root as file completion |
Tab |
Snippet expansion (see below) |
#@ |
Open XPrompt snippet picker (type # then @) |
Escape |
Switch to vim NORMAL mode |
In prompt INSERT mode, ACE auto-pairs safe openers for (), [], {}, <>, single quotes, double quotes, and
backticks. Typing the matching closer over an auto-inserted closer moves the cursor across it instead of duplicating it,
and backspace or delete removes both sides of an empty pair. Pairing is conservative: it is suppressed before token
characters, when text is selected (the typed character replaces the selection literally), for contractions or
possessives, and for repeated quotes/backticks needed to type Markdown fences or code spans.
Text automatically wraps at the terminal width, breaking at spaces (never mid-word). Line numbers appear in cyan when the text exceeds one line.
Prompt Stacks¶
Prompt stacks are the ACE editing surface for literal --- multi-agent prompts. Loading multi-agent prompt text from
history, a whole-bar editor session, or an editor buffer that returned through %edit splits top-level --- segment
separators into panes labeled agent 1, agent 2, and so on; the border title shows Prompt · N agents. Restoring
stashed prompts and using marked-agent ,x can also open a stack, but those paths load one pane per selected draft or
agent instead of re-parsing each pane's text. Panes are ordered top-to-bottom for whole-stack submission. The bottom
pane is active by default so you can keep drafting the newest segment; it is not a priority marker, and pressing Enter
immediately opens the submit chooser.
Inactive panes stay compact, and the active pane takes the available height. A --- line typed while INSERT mode is
active stays literal prompt text; use Ctrl+G - while drafting, or g- from prompt NORMAL mode, to add a new bottom
pane. Ctrl+G g and Ctrl+G Ctrl+G open the whole stack in $EDITOR when the bar already has multiple panes (a
single-pane bar opens just the current prompt). Returning from a whole-bar editor session or from %edit reloads
xprompt-style Markdown and parses --- separators into fresh panes. History loads parse only real multi-agent prompts;
a single history item with leading YAML frontmatter stays one verbatim pane instead of auto-opening the Frontmatter
Panel.
In prompt INSERT mode, pressing Ctrl+G opens the same context-aware hint row as prompt NORMAL mode's g prefix, plus
the editor continuation. Press Esc while the prefix is pending to cancel it and stay in INSERT mode.
In prompt NORMAL mode, pressing g opens a small hint row for the prompt-local g prefix actions currently available.
| Key | Action |
|---|---|
Enter |
Open the submit chooser; when one pane remains, Enter submits it normally |
Ctrl+S |
Join non-empty panes with --- separators and launch the whole stack as one multi-agent prompt |
g<enter> |
Launch the selected pane and remove it from the stack |
Ctrl+C |
Record the selected pane as cancelled history and remove it; the final remaining pane cancels normally |
Escape |
Enter NORMAL mode for stack navigation |
gj / gk |
Focus the next / previous pane in NORMAL mode; focus cycles at the stack edges |
gJ / gK |
Move the active pane down / up in NORMAL mode; reorder cycles at the stack edges |
g- |
Add an empty bottom pane in NORMAL mode and switch it to INSERT mode |
g= |
Show/focus the xprompt frontmatter panel; in the focused panel, run its deactivate/apply path |
gs |
Stash the selected pane and remove it from the stack |
gS |
Stash every non-empty pane and dismiss the prompt bar |
Submitting one pane at a time re-attaches prompt-level frontmatter to the launched pane so local xprompts and metadata
continue to resolve. Empty selected panes are dropped without launching. Whole-stack submission joins panes in
top-to-bottom order and then uses the usual multi-agent launch path, including %wait, %name, %model, and other
segment-local directives. Segment order alone does not make later agents wait; add %wait to the later pane when it
must start after an earlier agent succeeds.
The Enter submit chooser accepts a or Ctrl+S for all panes, c for the current pane, and Esc/q to cancel
without changing the stack.
Prompt stashes are a per-user draft pile stored outside prompt history. gs captures the selected non-empty pane plus
the shared prompt frontmatter; when other panes remain the bar stays open, and when the last pane is stashed the bar
closes without also recording the draft as cancelled history. gS captures all non-empty panes in their current order.
Ctrl+G p opens the unified stashed-prompt picker from the prompt bar, and @ opens the same picker from the main ACE
tabs even when the prompt bar is not active. In the picker, space marks a single-prompt row to restore and remove from
the stash, Tab marks a single-prompt row to restore while keeping it stashed, d marks any row for deletion, a
toggles all selectable single-prompt rows, and Enter confirms the marked set. With no explicit marks, Enter restores
and removes the highlighted row; this is also how to restore a bundled row created by gS. A small top-bar badge shows
how many restorable drafts are currently stashed.
Completion¶
Press Ctrl+T to activate token completion. The completion kind is determined by the token under the cursor:
- XPrompt completion: When the cursor is on a
#-prefixed token (e.g.,#my_pro), completion shows matching xprompt names from all discovery sources. Built-in workspace references such as#cdare included; use#cd:<path>to run from a specific directory without VCS workspace management. Completion rows include the xprompt kind and visible typed inputs, with required arguments shown asname: typeand optional arguments shown asname?: typeplus a default when the default is a simple scalar. Standalone workflow references use the#!nameinsertion form; typing#!filters completion to entries whose canonical insertion starts with#!. - Project/ChangeSpec completion: When the cursor is on a
#+token, or on a+token that is the first character in the prompt, completion opens a project/ChangeSpec picker. The picker contains active launchable projects plus active PR-sized ChangeSpecs inWIP,Draft,Ready, orMailedstatus; system-managedhome, inactive projects, sibling records, and non-launchable projects are excluded. Typing after the trigger filters by project name, project alias, or ChangeSpec name prefix. Accepting a row inserts the canonical workspace tag such as#gh:saseor#gh:my_change, replacing existing line-start VCS tags when present or placing the tag after leading frontmatter/directives when no tag exists. - Slash-skill completion: When the cursor is on a slash-skill token such as
/or/sase_, completion filters the same catalog to xprompts marked asskill: trueand inserts/skill_name. Packaged built-in skills are included, so/sase_plan,/sase_questions, and other bundled SASE skills are available without a project-local xprompt file. - XPrompt argument completion: When the cursor is inside a known xprompt argument position,
Ctrl+Tcompletes the active argument instead of the xprompt name. Forpathinputs it delegates to file path completion, forboolinputs it offerstrueandfalse, and inside parenthesized syntax it completes missingname=arguments without repeating names already present in the argument list. Numeric inputs keep the type hint visible but do not invent values. - Directive completion: When the cursor is on a
%-prefixed directive token (e.g.,%m), completion lists user-facing prompt directives and accepts aliases into their canonical forms. For example,%mcompletes to%modeland%wcompletes to%wait. The panel shows each directive's aliases and whether it takes an argument or is a flag. - File path completion: When the cursor is on a path-like token (starting with
/,./,../,~/, or containing/), completion shows matching filesystem entries. Tokens starting with@are also recognized — the@prefix is preserved in the completed path (useful for file-reference arguments). Relative paths use the prompt-selected base directory: a resolvable#cdreference takes precedence; without#cd, registered workspace-provider refs and known-project refs such as#git:<project>or#gh:<owner>/<repo>can root completion in that project checkout. If no prompt workspace ref resolves, ACE uses the TUI process directory. - File-history completion: When the cursor is in whitespace (or at an empty prompt prefix),
Ctrl+Topens a list of recently referenced files drawn from prompt history, ranked by recency. Project-local.sase/paths are filtered out so internal bead/plan artifacts don't pollute the suggestions. PressCtrl+Din the completion panel to delete the highlighted entry from the on-disk history.
| Key | Action |
|---|---|
Ctrl+T |
Start completion or insert shared prefix |
Ctrl+N / Down |
Next candidate |
Ctrl+P / Up |
Previous candidate |
Enter / Ctrl+L |
Accept highlighted candidate |
Escape |
Cancel completion |
Press Ctrl+R to open the recursive fuzzy file finder. With a token such as src/alp, src/ becomes the search root
and alp pre-seeds the fuzzy query; with no token, the finder starts at the prompt-selected base directory described
above. If a Ctrl+T file, recent-file, or path-argument candidate is highlighted, that highlighted path seeds the
recursive root instead. The finder uses git ls-files --cached --others --exclude-standard from the search root when
possible, falls back to a bounded filesystem walk, and inserts the selected path into the prompt position captured when
the finder opened. Inside the finder, type to filter, use Ctrl+N / Ctrl+P or arrows to move, Ctrl+U to clear the
query, Enter to insert, and Esc to cancel.
ACE also computes a non-disruptive live suggestion after a short debounce while the prompt input is in INSERT mode. The
suggestion appears in the prompt bar subtitle as [^L] accept ...; press Ctrl+L to accept it. Enter still submits
the prompt as typed, so live suggestions cannot accidentally replace text on send.
Live soft completion covers directives, xprompt names, xprompt argument names, and bool argument values. File-path soft
completion is disabled by default because it can scan the filesystem while typing; enable it with
ace.prompt_completion.auto_file_paths: true. The xprompt/skill menu also opens automatically while typing matching
#name, #!name, or /skill tokens; disable that xprompt auto-open behavior with
ace.prompt_completion.auto_xprompt_menu: false. The directive menu likewise opens automatically while typing matching
%name tokens; disable it with ace.prompt_completion.auto_directive_menu: false. Both auto-menus open only once at
least one identifier character follows the marker (bare #, /, and % stay quiet) and never auto-accept a single
match. The #+ / offset-zero + project/ChangeSpec picker opens when + completes a valid trigger and is also
available through manual Ctrl+T. Manual Ctrl+T completion still supports file paths, xprompt names, directives,
skills, and project/ChangeSpec tags regardless of those settings. Live suggestions pause while the manual completion
panel is open, while snippet tabstops are active, in NORMAL mode, and during feedback prompts.
For file completion, directories appear before files in the candidate list. Dotfiles are hidden unless the partial
prefix starts with .. Accepting a directory automatically re-opens completion for the next level (drill-down). The
completion panel shows up to 10 candidates at a time and scrolls to keep the highlight visible. When exactly one xprompt
or file candidate matches, accepting completion inserts the canonical reference immediately.
Accepting an xprompt completion, or selecting an xprompt from the #@ picker, opens an xprompt args hint panel when
the xprompt has required user-facing inputs. The panel shows the supported arguments and highlights the active one.
Press : while the accepted reference is still current to switch to colon syntax, or press ( to insert a
required-argument named snippet and use Tab to advance through the snippet fields.
The same smart insertion rules apply to #@ selections and Ctrl+T completions. A selected xprompt with no required
inputs inserts a trailing space, a single required non-text input inserts colon syntax, a single required text input
inserts double-colon shorthand, and multiple required inputs insert a parenthesized named-argument snippet.
The same hint panel appears while typing narrow, known argument forms such as #name:, #!name:, #ns/name:,
#ns__name:, #name!!:, #name??:, #name(, and #name(arg=. The hint is advisory; the backend xprompt parser still
owns expansion semantics when the prompt is submitted. Detection intentionally stays conservative, so prose shorthand,
URLs, unknown xprompt names, #name+, and completed colon text such as #name: value do not keep the prompt-bar hint
open.
Alt Brace Syntax (%{...})¶
The prompt input has dedicated highlighting and editing help for the %{A | B} alt fan-out shorthand (see the
Alt Directive reference). It distinguishes the alt delimiters from the branch separators so
a fan-out is easy to read at a glance:
- The
%{opener and}closer are styled as delimiters (bold accent). - Top-level
|branch separators use a dimmed accent so they read differently from the delimiters. - A branch name before a top-level
=(e.g.sec=in%{sec=... | perf=...}) is highlighted as a branch name. - An unmatched
%{(or stray closer) is flagged as an error span.
The alt overlay layers on top of the existing Jinja and search highlighting rather than replacing it, and it uses the same size guards, so highlighting stays responsive on large prompts.
Editing help in the ACE prompt input mirrors the Jinja auto-pair behavior and only fires for the %{...} shorthand:
- Auto-pair — typing
{immediately after a directive-valid%inserts the matching}and leaves the cursor between the braces (%{|}). - Paired delete — backspacing the
{in%{|}also removes the auto-inserted}; a forward delete on%|{}removes both braces. |separator normalization — typing|inside a live%{...}span inserts a padded|separator, keeps the cursor after the trailing space and before the closing}, and normalizes comma spacing in the current branch. For example, typing|at the end of%{foo ,bar, and bazyields%{foo, bar, and baz | }with the cursor before}.
These edits are suppressed when there is an active selection or when the cursor is not inside a directive-valid %{...}
context, so ordinary { and | typing elsewhere is unaffected. External editor integrations do not own %{}
auto-pairing or paired delete; editor-local brace-pair plugins own that lifecycle there. The
Neovim plugin still provides the same separator-normalization behavior for
prompt buffers.
NORMAL Mode¶
Press Escape in INSERT mode to enter vim-style NORMAL mode. The border title shows [NORMAL] and line numbers switch
to relative numbering (current line shows absolute, others show offset).
Motions¶
| Key | Action |
|---|---|
h / l |
Move left / right |
j / k |
Move down / up (actual lines) |
w / W |
Next word / WORD start |
e / E |
Next word / WORD end |
b / B |
Previous word / WORD start |
ge / gE |
Previous word / WORD end |
f{c} / F{c} |
Find char forward / backward |
t{c} / T{c} |
Till char forward / backward |
; / , |
Repeat / reverse last f/F/t/T |
% |
Matching bracket |
0 / $ |
Line start / end |
^ |
First non-blank character |
{ / } |
Previous / next paragraph boundary |
gg / G |
Top / bottom of document |
Ctrl+D/Ctrl+U |
Half-page down / up |
All motions accept a numeric count prefix (e.g., 3j moves down 3 lines).
Operators¶
| Key | Action |
|---|---|
d |
Delete (takes a motion, e.g. dw); copies to clipboard |
c |
Change (takes a motion, e.g. cw); cw/cW stop at the word/WORD end; copies to clipboard |
y |
Yank (takes a motion, e.g. yw); copies to clipboard |
> |
Indent lines covered by a motion by two spaces |
< |
Dedent lines covered by a motion by up to two spaces |
gu |
Lowercase text covered by a motion or text object |
gU |
Uppercase text covered by a motion or text object |
g~ |
Toggle case for text covered by a motion or text object |
D |
Delete to end of line |
C |
Change to end of line |
S |
Change entire line |
Y |
Yank entire line |
dd |
Delete entire line |
cc |
Change entire line |
yy |
Yank entire line |
>> |
Indent current line; count indents multiple lines |
<< |
Dedent current line; count dedents multiple lines |
guu |
Lowercase current line; count lowercases multiple lines |
gUU |
Uppercase current line; count uppercases multiple lines |
g~~ |
Toggle case on current line; count toggles multiple lines |
dae |
Delete entire buffer (copies to clipboard) |
cae |
Change entire buffer (copies to clipboard) |
yae |
Yank entire buffer (copies to clipboard) |
Text Objects¶
Text objects compose with d, c, and y.
| Key | Action |
|---|---|
iw / aw |
Inner / a word |
iW / aW |
Inner / a WORD |
i" / a" |
Inner / a double-quoted string |
i' / a' |
Inner / a single-quoted string |
i` / a` |
Inner / a backtick-quoted string |
i(/a(, ib/ab |
Inner / a parenthesized block |
i[ / a[ |
Inner / a square-bracket block |
i{/a{, iB/aB |
Inner / a brace block |
i< / a< |
Inner / an angle-bracket block |
ip / ap |
Inner / a paragraph; ap includes adjacent blank lines |
ae |
Entire buffer |
Other Commands¶
| Key | Action |
|---|---|
i |
Enter INSERT mode |
v |
Enter charwise VISUAL mode |
V |
Enter linewise V-LINE mode |
a |
Append after cursor |
A |
Append at end of line |
I |
Insert at line start |
o |
Open line below |
O |
Open line above |
u |
Undo |
Ctrl+R |
Redo |
x |
Delete character |
X |
Delete character before cursor |
r{c} |
Replace character(s) at cursor (supports count: 3rx) |
p |
Paste after cursor / below line from the internal register |
P |
Paste before cursor / above line from the internal register |
~ |
Toggle case of character(s) at cursor (supports count: 5~) |
. |
Repeat last mutation (supports count: 3.) |
J |
Join current line with next (supports count: 5J) |
The border subtitle shows pending operators and counts (e.g., 2d when a delete with count 2 is pending).
Visual Mode¶
Press v in NORMAL mode for charwise VISUAL mode, or V for linewise V-LINE mode. The border title shows [VISUAL] or
[V-LINE]. Escape returns to NORMAL mode, and o swaps the active selection end.
Visual mode supports the NORMAL-mode motions and counts listed above, including word motions, paragraph motions, line
motions, f/F/t/T with ;/, repeats, %, gg/G, Ctrl+D/Ctrl+U, and the NORMAL-mode text objects. v
exits charwise VISUAL mode; V exits V-LINE mode; pressing the other visual key switches selection kind.
| Key | Action |
|---|---|
d / x |
Delete selection and copy it to the internal register |
c / s |
Change selection and enter INSERT mode |
y |
Yank selection to the internal register and system clipboard |
p |
Replace selection with the internal register |
> / < |
Indent / dedent selected lines by two spaces |
u / U |
Lowercase / uppercase the selection |
~ |
Toggle case in the selection |
V-LINE operators always apply to whole selected lines regardless of the cursor column.
Prompt History Modal¶
Press Ctrl+K from the prompt input to open the prompt history modal. When the current prompt is a single logical line,
that line pre-fills the modal filter. Press ,. (leader + .) to open the same modal from the main ACE UI. The modal
displays prompts previously run in ACE, ordered by most recent use. Prompts shorter than two words are skipped when
writing to history, so trivial one-word inputs (e.g. y, ok) don't clutter the list.
Bare prompts are stored after launch normalization, so a prompt without an explicit workspace reference appears with the
default #git:home prefix. Use #cd:~ for direct home-directory runs with no VCS workspace management. Explicit
workspace prefixes, including #cd:<path>, also feed the prompt-input MRU controls. In the prompt input, the MRU ring
is ordered from most recent to oldest: Ctrl+P moves toward older launchable workspace prefixes, while Ctrl+N moves
toward newer prefixes. Each edge has a no-prefix stop that removes the first launchable workspace tag from the prompt
without touching the remaining prompt text, then wraps. When no workspace tag is present, Ctrl+P starts at the most
recent entry and Ctrl+N starts at the oldest one.
Keybindings¶
| Key | Action |
|---|---|
Enter |
Submit the highlighted prompt directly |
Ctrl+G |
Edit first - load prompt into editor |
Tab / Ctrl+I |
Load prompt into the input widget for editing |
Ctrl+X |
Toggle visibility of cancelled prompts |
Ctrl+Y |
Copy prompt to clipboard |
Esc |
Close modal |
Filtering¶
Type in the search box to filter prompts by text. Press Ctrl+X to toggle cancelled prompts on or off — when enabled,
cancelled prompts appear in the results with an x marker.
Prompt-history rows are compact single-line entries: cancelled marker, last-used timestamp (MM-DD HH:MM when
parseable), and a first-line prompt preview. The preview panel still shows the full prompt and timestamp metadata.
History writes use a sidecar lock plus atomic tempfile replacement so concurrent agent launches do not truncate the
shared ~/.sase/prompt_history.json file.
Task Queue Modal¶
Press ,t (leader + t) to open the task queue modal. It shows background tasks (hook runs, mentor executions, etc.)
with live output for running tasks and completed output for finished ones.
Layout¶
The modal uses a two-panel layout: a task list on the left and an output pane on the right. Running tasks refresh their output every second.
Task Status Icons¶
| Icon | Color | Meaning |
|---|---|---|
● |
Green | Running |
✓ |
Cyan | Success |
✗ |
Red | Error |
? |
Dim | Unknown |
Keybindings¶
| Key | Action |
|---|---|
j / k |
Navigate task list |
K |
Kill selected running task |
d |
Dismiss selected completed task |
D |
Dismiss all completed tasks |
e |
Open task output in $EDITOR |
y |
Copy task output to clipboard |
Ctrl+D / U |
Scroll output pane down / up |
q / Esc |
Close modal |
Snippets¶
The prompt input supports expandable text snippets triggered by pressing Tab. Snippets are configured in the
ace.snippets section of sase.yml as a mapping of trigger words to template strings:
ace:
snippets:
fix: "Please fix the following issue:\n$0"
review: "Review this code for correctness, performance, and style."
bug: "Bug in $1:\n\nExpected: $2\nActual: $3\n\nPlease fix.$0"
Usage¶
- Type a trigger word (e.g.,
fix) in the prompt input. - Press
Tab. If the word before the cursor matches a snippet, it is replaced with the template text. - If the template contains tabstop markers (
$1,$2, ...), the cursor jumps to$1first. PressTabagain to advance to$2, then$3, and so on.$0marks the final cursor position after all tabstops are visited. If there are no tabstop markers, the cursor moves to the end of the expanded text.
Tab priority: Snippet expansion always takes priority over tabstop advancement. If you type a trigger word at an
active tabstop and press Tab, the snippet expands rather than jumping to the next tabstop.
Multi-line indentation: When a multi-line snippet is expanded on an indented line, continuation lines automatically inherit the leading whitespace of the trigger line. Tabstop positions are adjusted accordingly.
Trigger words are matched against the alphanumeric/underscore word immediately before the cursor. If no snippet matches,
Tab advances to the next tabstop (if any are remaining from a previous expansion), or behaves normally.
XPrompt-derived snippets compose normal xprompt references before they enter the snippet registry. After xprompt-derived
snippets and ace.snippets are merged, any snippet can splice another snippet by trigger with #[trigger].
#[trigger(value)] and #[trigger:value] fill the referenced snippet's $1, $2, ... tabstops before splicing. The
final template is renumbered so tabstops from the caller and referenced snippets do not collide.
Editors using sase lsp can receive the same registry as LSP snippet completions after bare trigger words when the
client advertises completionItem.snippetSupport. The server uses the editor helper operation
sase editor helper-bridge snippet-catalog as the authoritative source and falls back to native Rust loading only for
simple snippets if the helper is unavailable. Clients without snippet support do not receive these entries, because raw
$1 / $0 markers would not behave like ACE tabstops.
XPrompt Picker (#@)¶
Typing #@ (the # character followed by @) opens the XPrompt snippet picker modal. This lists all available
xprompts (including project-local xprompts from sase.yml files) and inserts the selected reference at the cursor
position. Inline-capable xprompts and workflows insert as #name; standalone workflows insert as #!name. The picker
uses the same argument-aware skeletons as xprompt completion, so typed inputs can be filled immediately after selection.
Markdown multi-agent xprompts are inline-capable and insert as #name. This is separate from the ace.snippets
mechanism — it provides quick access to xprompt references rather than expanding static templates.
Auto-Refresh¶
ACE auto-refreshes data at a configurable interval (default: 10 seconds). The remaining time until the next refresh is
shown in the info panel. Set --refresh-interval 0 to disable.
Tab switches are instant: cached data is shown immediately while a background refresh runs asynchronously, so moving between tabs never blocks on disk I/O.
When the inotify-based artifact watcher is active, the periodic tick is event-driven: it consults per-surface dirty
flags (_dirty_changespecs, _dirty_agents, _dirty_axe) and short-circuits the whole tick when nothing has changed.
A 60-second FULL_SANITY_REFRESH_SECONDS floor still triggers a full reconcile to recover from missed inotify events,
so a quiet TUI does ~zero work between real changes without going stale.
Performance Tracing¶
For diagnosing TUI latency, set SASE_TUI_TRACE=1 before launching sase ace. Tracing is near-zero-cost when the env
var is unset; with it enabled, each instrumented hot path emits one JSONL line per span to
~/.sase/perf/tui_trace.jsonl (override via SASE_TUI_TRACE_PATH=…). See docs/perf_runbook.md for
the full span catalog, benchmark harness, and per-phase performance targets.