aboutsummaryrefslogtreecommitdiffstats
path: root/pi-ext (follow)
Commit message (Collapse)AuthorAge
* installers: sync scripts/ CLI helpers into ~/binuser2026-08-12
| | | | | | | | | | | | | | | | | | | | | | The repo already treated extensions as a deliberate snapshot: copy every *.ts into ~/.pi/agent/extensions, skip files already identical. Standalone helpers in scripts/ had no such path, so pir had to be copied by hand and then drifted from the checkout. CLI_TOOLS is a "<source in scripts/>:<installed name>" list, so the installed command name can differ from the filename (pi-resume -> pir). Same copy-not-symlink policy as the extensions, for the same reason: the global set should not track in-progress edits. Copies are chmod +x, the destination is PI_BIN_DIR-overridable like PI_EXT_DIR, a missing source is reported and skipped rather than aborting the whole install, and the entries participate in the existing --dry-run output and the "nothing changed" tally. Also records why answer-picker.ts is absent from the list: it needed a helper binary in its socket+separate-pane form (c88fe0d), and does not any more now that zellij draws and hit-tests the buttons itself and reports clicks on pi's stdin. That implementation is parked in attic/, which this script does not glob -- top-level *.ts only -- so nothing there installs.
* add pir: fuzzy-find and resume pi sessions for the current diruser2026-08-12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `pi -c` blindly grabs the newest session, and the alternative is digging through ~/.pi/agent/sessions/--home-user-dev-foo--/ for a UUID by hand. This lists every session for the cwd in an fzf picker with a live transcript preview and execs `pi --session <file>` on the pick. pir pick a session for this dir pir -a pick across every project dir pir <query...> start fzf with an initial query pir -g TEXT only sessions whose full transcript contains TEXT The -g pass exists because the fzf query matches the list line -- title and id -- not deep history, so "the session where I fixed the mouse bug" is unfindable without it. It shells out to rg when available and falls back to grep. In the picker: enter resumes, ctrl-f forks instead (execs --fork, leaving the original untouched), ctrl-o prints the path and exits without launching, ctrl-a toggles current-dir vs all-dirs, ctrl-s toggles recent vs longest. The toggles are fzf --bind=reload of this same script, which is what --list and --preview are for -- they are internal modes invoked by fzf, not user entry points. Session directory resolution mirrors pi's own getDefaultSessionDirPath() encoding, and honours PI_APP_NAME / <APP>_CODING_AGENT_DIR, so a renamed or relocated agent dir still resolves. Forked sessions are detected from the header's parentSession and marked in the list. Requires fzf; checked up front with a clear error rather than failing inside a pipeline.
* add answer-picker: clickable preset answers in the pane frame (OSC 5577)user2026-08-12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Puts the follow-ups you retype constantly ("continue", "yes", "run the tests") into the bottom frame row of pi's own pane, using the sl0p zellij fork's OSC 5577 pane-status-bar protocol. Click one or press alt+N and it is sent as a real user message. ╰ alt+N · ready ──[continue]─[yes]─[no]─[explain]───╯ WHY THE THIRD DESIGN. v1 drew its own button row and enabled terminal mouse reporting inside pi. It worked and cost too much: mouse reporting is per-terminal, not per-region, the wheel is just another button in that protocol (SGR bit 64), and the DECSET modes only select how much MOTION is reported -- there is no bit that selects which buttons. So pi's pane traded native wheel scrolling and click-drag selection for clickable buttons, and because pi renders inline into the main screen it cannot scroll the emulator's scrollback back to compensate. v2 moved the buttons to a second pane to confine the tracking; that restored scrolling but cost a pane, a picker process and a unix socket. OSC 5577 removes the tradeoff instead of relocating it: zellij draws the buttons in the frame it already owns, hit-tests them itself, and reports clicks on our stdin. No mouse reporting anywhere, no extra pane, and the frame row does not shrink the content area so nothing resizes the PTY. v2 is parked in attic/ (not globbed by install.sh) as the only non-zellij path, should one be wanted. ANSWERS come from DEFAULTS until <cwd>/.pi-answers.md exists, after which the file is authoritative. It is SEEDED with the defaults on first write, which makes "the file wins" and "you still get the defaults" one rule instead of a merge order to remember -- and it means deleting a line genuinely removes a default, which an additive model could not express. /answers add|rm persists in one step; hand-edit + /answers reload also works. Labels are backtick-delimited in the markdown so a body may contain the em-dash separator (as "no -- don't do that" does). /answers list renders a table via appendEntry + registerEntryRenderer, so it is scrollable and copyable in the transcript but never enters LLM context. BUTTON IDS ARE DERIVED FROM THE LABEL (ans-ship), not positional. With a mutable answer list, positional ids are a live bug: change the answers and an in-flight click for a vanished button resolves to whatever now occupies that index and fires the wrong prompt. Label-derived ids fail the allow-list instead. BUTTONS EXIST ONLY BETWEEN TURNS. While a run is in flight they are withdrawn -- so a click cannot queue a second answer behind the one already working -- and the status text stays up to advertise that alt+N still does. Withdrawing sends `buttons` with an empty payload, NOT `clear`: the fork's buttons verb assigns parse_status_bar_buttons(payload), which filter_maps away every field lacking an id:label, while `clear` is the one verb that would also wipe the status text. Restoring keys off agent_settled as much as agent_end, since agent_end never fires on the abort/error paths -- an escape cancel or a provider blowup would otherwise leave the frame permanently bare. SECURITY. These are in-band escapes, so anything that writes to the pane (a cat of a hostile file) can declare buttons and draw a misleading bar. A click report is a hint that a user pressed something, not proof we put it there: ids are validated against what we actually declared, and the action is deliberately non-destructive -- it sends a prompt, it does not deploy. Click reports arrive interleaved with ordinary stdin and are stripped, or they would be typed into the editor; pi-tui's StdinBuffer holds an ESC ] sequence until its ST/BEL terminator, so we always see whole reports. Caps match the fork exactly (8 buttons, 512-char status, 32-char ids), so we never rely on zellij silently dropping our excess. Answers past the 8th stay on alt+N and the status line says so.
* last-prompt: call it a turn, not a sl0perationuser2026-08-12
| | | | | | | | | | | | | | | | | | | | The counter increments once per user text block, so what it counts is exactly a turn in the ordinary prompt -> response sense. It also matches the vocabulary pi already uses internally (turnBudget, maxTurns), so the label no longer invents a word for a thing that has one. "round" was the other candidate and is worse: it implies a symmetric game with fixed phases, and it collides with the rounded-border language used throughout these widgets. "prompt #N" is the most literal reading of the counter, but it undersells the box -- the spinner and timer measure the whole agent run, not just the prompt. No layout change was needed. The label is 7 columns narrower, and the top border derives its dash fill from visibleWidth(leftLabel) rather than any hardcoded width, so the frame absorbs it. The cross-reference in slop-tool-boxes.ts ships in 36e422b, whose hunk it shares.
* slop-tool-boxes: green heartbeat pulse; unfreeze top border when it fitsuser2026-08-12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Two changes that both come back to "what should an in-flight box look like", plus the icon that was left out of it. PULSE: single hue, brightness only. The old pulse interpolated a 4-color synthwave ring (amber -> pink -> violet -> cyan) over 4s, which at that period reads as a slow hue *drift*, not a pulse -- you notice the color has changed, never that it is beating. Now it is one hue (green, i.e. "in progress") modulated dim <-> bright: #0d4023 to #3bff7a, via a raised cosine so there is no direction-reversal snap at the turn, then gamma ^1.7 to bias it toward the dim end. The border rests dark and spikes bright, which is what makes it read as a heartbeat. CYCLE_MS 4000 -> 800 (1.25Hz), and 800 is now the only pulse-speed knob. There is a hard floor of ~400ms documented at the constant: a brightness pulse IS a flicker, and 3-30Hz is the photosensitive-seizure band -- this repo has already shipped one seizure-grade flicker bug (NOTES.md), so the constant carries the warning rather than relying on memory. The upper bound is render cadence, not taste: the pulse never forces a repaint, it samples wall-clock time on renders that were happening anyway (~10fps while the spinner runs), so 800ms is ~8 samples per beat. Much faster and consecutive renders land on distant brightnesses -- the beat degrades into jitter. TOP BORDER: freeze only when we actually must. 0d671ce stopped the full-redraw flicker by freezing the call slot's top border for the whole time the result streams. That was broader than the bug requires. The bug is specifically "recolor a line that sits ABOVE the viewport": tui.js does `if (firstChanged < prevViewportTop) fullRender(true)`, so an off-screen recolor repaints everything, every tick. While the box still fits on screen, firstChanged lands inside the viewport and tui diffs from there down -- cheap. So canPulseTopBorder() compares callH + resultH against rows - RESERVED_ROWS and only freezes past that, which gives short tool calls their top and bottom borders beating in unison again. resultH is one frame stale (the call slot renders before the result slot). Harmless, because a box only grows: worst case is one extra full redraw on the frame it crosses the threshold, and then it is frozen. RESERVED_ROWS (14, for input box + footer + topbar) is deliberately generous in the same direction -- over-estimating freezes a box that could have animated, which nobody can see; under-estimating buys back the flicker. A frozen border now holds HOLD_PAINT, a fixed mid-beat green (pulseRGB(0.5)), instead of whatever phase the pulse happened to be at when content last changed. Same number of writes, but it looks deliberately held rather than randomly stuck. Resuming the pulse invalidates callActiveW so the frozen lines cannot be served stale. ICON: the pending ◉ takes the border's paint, so it beats with the frame instead of sitting there as a static amber dot. This is why the label is built inside build() now -- it has to be rebuilt per paint. It costs nothing extra: the icon shares the top border's line, which is already being rewritten. Also picks up the sl0peration -> turn doc-comment rename in the header, which is unified into the same hunk as the pulse comment above it.
* chrome: indent the input behind a ❯ prompt glyphuser2026-08-12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Rhymes with the ❯ chip user-prompt-frame stamps on the message once it is submitted, so what you type and what it becomes read as the same object: ❯ your input text that wraps here ────────────────────────────────────────────── The file's own comment warned that content lines must pass through verbatim or the cursor desyncs. That is true but fixable: the TUI derives the cursor column as visibleWidth(line.slice(0, markerIndex)) from the FINAL rendered line, so a prefix inserted BEFORE the marker shifts the reported column by exactly its own width -- which is where the text moved to. Verified against native: the same text reports col 11 unpatched and col 13 patched, and empty input parks the cursor at col 2, against the glyph. Nothing may ever be inserted after the marker. Layout now runs at (width - INDENT_W) instead of the full width, otherwise the editor wraps two columns too late and the indent pushes every line past the terminal edge. The rules it draws at that width are still discarded and rebuilt at full width. The glyph is suppressed when the input is scrolled: the first VISIBLE line is then not the first line of the prompt, so the glyph would lie about where the input begins. A scroll indicator in the top rule is exactly that condition. Glyph uses the instance's own borderColor, so it keeps reflecting bash-mode and thinking-level like the rule does. The slash/autocomplete menu is left at column 0 (INDENT_MENU): it draws full-width selection bars that would otherwise sit offset against the rule above them.
* chrome: reframe user prompts as a labelled chip + railuser2026-08-12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The header was lost in 77f1d64: dropping the side rails also dropped frameTop() from the return, leaving a bare closing rule under the message. A rule *underneath* reads as an opener for the reply as much as a terminator for the prompt, and it shared its glyph vocabulary with every other box, so prompts were distinguished only by hue. Replace it with a reverse-video chip and a left gutter: █ ❯ PROMPT ▌────────────────────────────────── ▌ the prompt text ▌ wrapped continuation The chip is the only filled element in the chrome and the rail the only vertical (rails were removed everywhere else), so prompts are now identifiable at a glance without adding glyphs to the rest of the UI. The left cap is a FULL block, not ▐: the rail fills the left half of column 0, so a right-half cap would sit in the opposite half of the same cell and read as detached. No closing rule; the reply terminates the block. The native component wraps text in Box(pad, 1, bg), i.e. a blank padded line above and below with OSC 133 markers on the first and last line. paddingY is hardcoded and not configurable, so strip both pad lines and re-home the markers: ZONE_START onto the header, ZONE_END+ZONE_FINAL APPENDED to the last text line (prefixing would put the final line of the prompt outside the semantic zone). Terminal prompt-jumping keeps working. Note the OSC payload is not numeric-only -- pi emits 133;A / 133;B / 133;C -- so the matcher runs to the terminator rather than a [0-9;] class. An earlier numeric-only class silently matched nothing and left the pad lines in place. No timestamp: UserMessageComponent receives only `text`, and stamping at first render would relabel every restored message with the restore time under `pi -c`.
* input-frame: remove header rule and rails, plain separatoruser2026-08-12
| | | | | | | | | | | | | | The input box now renders as bare text: no top header (the "╭─ ❯ ───╮" rule is dropped), no side rails, and the bottom is a plain edge-to-edge rule rather than a ├──┤ divider, so nothing around the input reads as a box. Content is laid out at the real full width and passed through verbatim, which keeps the cursor/IME marker exactly where the editor put it. Scroll indicators ("↑ N more") still render, since they carry information rather than chrome; set KEEP_SCROLL_INDICATOR to false to drop those too. Removed the now-dead corner constants and railWrap, and renamed buildBorder to buildRule to match what it emits.
* chrome: drop side rails across boxes (zellij draws the pane border)user2026-08-12
| | | | | | | | | | | | We run the agent inside a zellij fork that already draws a border around the pane, so per-box verticals were redundant chrome. Body lines are now indented into the box interior instead of railed, and the top/bottom rules use straight end caps (a corner would curve toward a vertical that is no longer there). Each file keeps the original glyphs documented so the rounded box can be restored by flipping the cap constants back. Backported from the installed set in ~/.pi/agent/extensions, which had drifted ahead of this repo.
* slop-tool-boxes: stop full-redraw flicker on tall, top-clipped active boxesuser2026-08-12
| | | | | | | | | | | | | | | | | | | An in-flight tool box taller than the viewport (top scrolled off-screen) flickered violently the whole time it was active. Cause: the working spinner (and pi's default spinner) drive ui.requestRender() at ~10-16fps, and the active call slot recomputed a wall-clock pulse color every render, recoloring the TOP border. tui.js falls back to a full-screen redraw when the first changed line is above the viewport (firstChanged < prevViewportTop), so every spinner tick = one full clear+repaint. Fix: once the result slot is streaming (box can be tall), freeze the call slot pulse color, caching built lines per (width, inner). The header/command is static, so the top border is byte-identical between render ticks and never triggers an above-viewport change. The pre-output phase (box still fully visible) keeps the live pulse + ticking runtime; the result slot still pulses its in-viewport bottom border. NOTES.md documents this as flicker #4. Confirmed by repro: massive flicker with the fix reverted, calm with it in.
* add obnoxious-working: loud spinners + sl0p-swag status messagesuser2026-08-12
| | | | | | | | | | | | | Replace pi's static "Working..." during streaming with a random loud spinner per agent run and a rotating pool of goofy sl0p-swag status messages ("warming up the h4x...", "computering extremely hard...", "0day o'clock...", ...). All via the official UI hooks: setWorkingIndicator (custom frames rendered verbatim, colors baked in) + setWorkingMessage, both updating the live indicator. Pick on agent_start, rotate the message (and sometimes swap the spinner) on a ~2.2s timer, restore defaults on agent_settled/shutdown. No-op without a TUI.
* remove audit-fanout extensionuser2026-08-12
| | | | | Drop the parallel audit-fanout subagent extension from the repo and the global extensions dir.
* installers: exclude experimental sixel-images.ts from global + dev loaduser2026-08-12
| | | | | | | | | | | | Add sixel-images.ts (WIP): renders inline images as sixel via chafa by forcing image capability on under tmux and patching pi-tui Image.render. It works end-to-end (sixel draws inside the read box, width-guard bypassed via an empty Kitty APC marker) but has rough edges (double-render, spacing), so it is parked for now. install.sh and dev.sh now carry an EXCLUDE list so this extension is neither installed globally nor loaded in the isolated dev session. The slop-tool-boxes read-image injection that paired with it was reverted separately.
* sl0p-footer + attached input boxuser2026-08-12
| | | | | | | | | | | | | | | | | | | | | Add sl0p-footer.ts: replace the built-in 2-3 line footer with one dense single line via ctx.ui.setFooter — ↑/↓ tokens, context %/window (pressure colored), cwd (+git branch), and model + thinking effort right-aligned. Data via the live extension context (sessionManager/model/getContextUsage) and footerData; repaints on branch/model/thinking changes. Make the footer look attached to the input box: - input-frame.ts now ends its box in a divider (├──┤) instead of a closing corner, so the footer reads as a continuation. - the footer wraps its line in matching side rails + the real closing bottom (╰──╯), content inset to align with the input. - footer frame is colored with theme.getThinkingBorderColor(level), tracked via thinking_level_select (seeded from the branch/model default), so the seam matches the input box border at every thinking level. Bash-mode border color is the one unmirrored case (editor state not exposed to extensions). Two stacked components styled to look like one box — no cross-component theme/state sharing, and the autocomplete menu is left to render between them.
* input-frame: wrap the input editor in a real rounded boxuser2026-08-12
| | | | | | | | | | | | | | | | | | | | | | | | pi's input area is just two full-width horizontal rules (no side rails) drawn by the pi-tui Editor. Add input-frame.ts, which patches CustomEditor.prototype.render (the only Editor subclass, and the class the main input uses, so extension/selector editors keep native chrome) to lay content out at the inner width and wrap it in a rounded, side-railed box: ╭─ ❯ ───────╮ │ input ... │ ╰───────────╯ - content laid out at width-4 so word-wrap / lastWidth / cursor nav match - cursor/IME marker stays aligned: the TUI derives the column from visibleWidth(textBeforeMarker), and the uniform "│ " prefix shifts it by the same 2 cols as the visible cursor; content lines are padded, never truncated, so the marker is never dropped - scroll indicators keep their "↑/↓ N more" text with rounded end caps - autocomplete/slash menu (lines after the bottom rule) left as-is, indented to align under the box interior - reuses the instance borderColor, so bash-mode / thinking-level colors carry - idempotent/reload-safe, feature-detected, falls back to native render on any error, restores original on shutdown
* slop-tool-boxes: syntax-highlighted diffs with gutter + bg banduser2026-08-12
| | | | | | | | | | | | | | | | | Replace the post-hoc applyDiffBackgrounds hack (which recolored the native flat green/red diff lines) with a real diff renderer built from the raw diff string (result.details.diff, and the live preview.diff on the call component). Each code line is syntax-highlighted via highlightCode/getLanguageFromPath, drawn with a `± lineNum` gutter, and add/remove rows get a subtle dark bg band across the full width. The band is re-asserted after fg resets so syntax colors do not punch holes in it. Context rows are highlighted with no band; skipped-context collapses to a dim ellipsis. Applies to both slots: the live edit preview (renderCall) busts its width cache on preview identity change, and the settled result (renderResult) falls through to native output when there is no diff (e.g. errors).
* slop-tool-boxes: stamp bash runtime in bottom border (right-aligned)user2026-08-12
| | | | | | | | | | | | Track per-toolCallId execution timing via tool_execution_start/end and render the elapsed duration into the bottom border of bash rows, aligned to the right and kept one dash clear of the corner. Respects the no-forced-repaint invariant: the live timer is `end ?? Date.now()`, so it only advances on renders that already happen (bash output streaming re-renders the result slot border); the final duration is stamped once the row settles. Muted time text sits outside the pulsing dash paint. Gated to bash via RUNTIME_TOOLS (extensible).
* Add audit-fanout.ts: parallel read-only auditor subagents with live statususer2026-08-12
| | | | | | | | | | | | | | | | Fresh-context 'pi --print --mode json' child per audit task, run in parallel, read-only (--exclude-tools edit,write), lean (-ne --no-session). Auditors inherit the parent session model+thinking by default; override via model param. Registers both an 'audit' tool (LLM-callable) and a /audit command. Key fixes from live testing: - stdio:['ignore','pipe','pipe'] — pi --print blocks on an open non-TTY stdin pipe, which hung every child until timeout. Closing stdin fixes it. - Incremental JSON-stream parsing surfaces real per-auditor activity (reading X / grep Y / composing Nc) plus a 1fps elapsed heartbeat via ctx.ui.setStatus, so a slow model never looks hung (the reason ESC got hit). Raw reports written to .pi-audit/<ts>/; parent synthesizes.
* slop-tool-boxes: dotted gray rails in-flight, solidify on finalizeuser2026-08-12
| | | | | | | | | Bring back body side rails with an active/settled distinction: - in-flight: gray dotted (┊) rails + pulsing top/bottom borders - finalized: solid status-colored (│) rails + solid borders Rails are a STATIC per-render color (never carry the pulse), so streaming output can't recolor existing body lines -> stays flicker-safe.
* slop-tool-boxes: remove animation timer; time-based pulse (fix streaming ↵user2026-08-12
| | | | | | | | | | | | | | | | | | | flicker) Third flicker bug: a taller-than-screen bash box flickered because the 10fps timer forced a full updateDisplay() repaint on top of the stream's own re-renders. Root fix: NEVER force a repaint. - Pulse color is now a pure function of wall-clock time (Date.now() % CYCLE_MS); no tick counter, no setInterval. Any render shows the current color. - The pulse advances only on renders that already happen: streaming output ('the stream hydrates the pulse', irregular cadence) or start/end nudges. - Silent long-running tools hold a static active color (flicker-free) rather than animating. - Also drop body side rails entirely (open-sided box); only top/bottom pulse. Self-test: restored rows never pulse, active top border advances with time, settles solid on end.
* slop-tool-boxes: fix startup render storm + streaming flickeruser2026-08-12
| | | | | | | | | | | | | | | | | Two bugs from the pending-tool border pulse: 1. Streaming flicker: pulsing the side rails recolored every body line each frame, forcing a full-box repaint per streamed token. Now only top/bottom borders pulse; rails/body use a static status color. 2. Startup render storm (pi -c never accepts input): 'pending' was inferred from the render-time default status, so every RESTORED tool row rendered as pending -> all added to the 10fps animation set -> unbounded invalidate loop. Animation is now driven strictly by live tool_execution_start/end events keyed by toolCallId; restored rows fire no start event so they never animate. Self-test covers: restored rows don't pulse, live rows pulse only between start/end, rails static during a run.
* Add .gitignore (audit/subagent artifacts) + NOTES.md flicker safety buguser2026-08-12
| | | | | | Record the redraw-flicker-during-write report as a tracked safety issue. Prime suspect: the 10fps pending-tool border pulse (slop-tool-boxes d3454b8) driving full-frame repaints ~10Hz (photosensitive-seizure risk band).
* dev.sh: also load globally-installed pi packages (subagents, goal, ...)user2026-08-12
| | | | | | | | -ne disables package extension discovery, so pi-subagents' subagent tool never loaded under dev.sh. Add scripts/resolve-package-extensions.mjs to resolve each settings.json package's extension entry (file/index-dir/discovery-dir, mirroring pi's loader) and re-add them via -e. Package skills/prompts already load under -ne. Opt out per package with PI_DEV_SKIP_PACKAGES.
* slop-tool-boxes: pulse the border of in-progress (pending) toolsuser2026-08-12
| | | | | | | | | | | | | | Apply the last-prompt palette pulse to tool rows while they're running: - module-level 10fps timer re-paints only PENDING rows via context.invalidate() (the tool render context's rerender trigger), and stops when none are pending - render() reuses width+content-cached inner lines (persisted on the row state so they survive the timer's updateDisplay rebuilds) and only re-frames cheap strings with the pulse color; native tool render is NOT re-run per tick - completed rows keep the existing width-memoized full-output cache untouched, so no perf regression for finished tools - frame builders take a Paint fn (solid theme color when idle, truecolor pulse when pending); label/icon stay static, only the border rails pulse - agent_settled/session_shutdown safety net guarantees the timer stops
* reload-hotkey: prefill /reload instead of broken sendUserMessage commanduser2026-08-12
| | | | | | | | | The prior approach couldn't work: shortcut ctx has no reload(), and sendUserMessage() uses prompt({expandPromptTemplates:false}) which skips slash-command handling, so "/reload-runtime" went to the model as literal text. No extension API can invoke reload directly. Best available: F5 drops the built-in /reload into the editor (guarded against clobbering in-progress text); user presses Enter. Documented the source-confirmed blockers.
* Add reload-hotkey.ts: bind a key (F5) to /reloaduser2026-08-12
| | | | | | | | registerShortcut handlers only get a plain ExtensionContext (no ctx.reload, which is command-context only), and keybindings.json has no reload action. So register a /reload-runtime command that owns ctx.reload() and have the shortcut queue it as a follow-up message — pi's documented reload pattern. Key configurable via RELOAD_KEY.
* user-prompt-frame: minimal glyphic corner (❯ ▁ prompt+cursor)user2026-08-12
| | | | | | | Replace the "you" label with a shell-prompt-style glyph in the frame corner: - PROMPT_GLYPH (❯) + spaced CURSOR (▁) reads like a command prompt - LABEL now optional (empty = glyph only); set a word for "❯ ▁ usr" style - CURSOR/GLYPH/LABEL exposed as easy style knobs
* last-prompt: replace scanline with palette pulse on border railsuser2026-08-12
| | | | | | | | | | Drop the sweeping scanline (looked janky) in favor of a smooth full-frame border color pulse while running: - interpolate truecolor RGB across a curated palette (amber/pink/violet/cyan), ~4s per cycle, since theme.fg() semantic tokens looked too steppy - pulse only the border rails + prompt marker; the sl0peration label stays static accent so it reads clearly against the moving frame - settles to a static accent border when idle
* last-prompt: reintroduce animated live duration + spinner + scanlineuser2026-08-12
| | | | | | | | | | The original regression wasn't the timer itself but full-frame re-renders recomputing every tool box from scratch. That's now fixed at the source (slop-tool-boxes width-memoization), so a bounded animation timer is safe: - runs ONLY during active runs (piggybacks pi's own streaming renders) - drives frames via captured tui.requestRender() instead of setWidget/tick - 10fps live elapsed timer + spinner in the top border - subtle scanline sweep along the bottom border while running
* install.sh: skip files already identical at destinationuser2026-08-12
|
* Add install.sh: copy extensions into global pi extensions diruser2026-08-12
|
* Add dev.sh: run pi with only this repo's extensions (isolated)user2026-08-12
|
* Initial commit: pi agent extensionsuser2026-08-12