Skip to content

PRD: Chassis Actions — user-wired composer buttons (MVP) #45

@pechhe

Description

@pechhe

Problem Statement

Power users want to wire their own tools onto the composer without cloning the source repo. The Desktop App ships three prebuilt composer toggles (plan/build, caveman, orchestrate), but there is no way for a user to add their own button that runs a slash command, fires a whole prompt, or triggers a favourite extension. Beyond single buttons, users want to own their composer — arrange tools on a grid, choose colors and whether titles show, ship a default, and use their own LLM to help build it.

Solution

Two concepts with a one-way dependency (see CONTEXT.md):

  • Chassis Action — a user-defined, wired behavior attached to the composer. Effects: submit (one-shot: slash command / extension command / canned prompt), wrap (sticky: frame the outgoing prompt via {{input}}), and later reminder (sticky: a session system-reminder via a companion skill).
  • Composer Layout — a user-authored arrangement and styling of composer controls (built-in controls and Chassis Actions) on a grid: position, color, title visibility. Consumes actions; actions know nothing about layout.

Delivered in phases so the model is proven before the heavy UX is built:

User Stories

Phase 1 — Chassis Actions MVP

  1. As a Desktop App user, I want to create a button that sends a slash command, so that I trigger a workflow in one click.
  2. As a user, I want a button that triggers an installed extension, so that I needn't remember its slash command.
  3. As a user, I want a button that sends a whole canned prompt, so that I kick off a repeated task instantly.
  4. As a user, I want a sticky toggle that frames whatever I type with a reusable wrapper, so that I apply a persistent stance across messages.
  5. As a user, I want to name each button and choose whether its title shows, so that my composer stays readable.
  6. As a user, I want actions defined once and available in every thread, so that I don't re-create them per conversation.
  7. As a user, I want a sticky wrap to stay on across messages until I turn it off.
  8. As a user, I want turning on one wrap to turn off any other, so that I never get nested framings.
  9. As a user, I want a sticky wrap to compose predictably with plan mode (wrap inside, plan outside), so plan mode stays authoritative.
  10. As a user, I want a one-shot button to send immediately and leave my unsent draft untouched.
  11. As a user, I want actions to persist across restarts.
  12. As a user, I want a malformed action dropped, not fatal, so a bad entry can't brick the composer.
  13. As a user, I want to add, edit, and delete actions in Settings.
  14. As a user, I want my buttons to appear across the in-thread, new-thread, and pending composer surfaces.
  15. As a user, I want one-shot actions to work whether the session is idle or running (steering).

Phase 2 — reminder, extension picker, per-folder

  1. As a user, I want a sticky action that injects a standing system reminder into the session, so that I keep a behavior active without re-typing it.
  2. As a user, I want to pick an installed extension's command from a list when creating an action, so that I don't hand-type slash text.
  3. As a user, I want different actions and active toggles per project folder, so that each project has the right tools.
  4. As a user, I want my reminder to be picked up automatically when a session starts, so that I configure it once.

Phase 3 — Composer Layout grid + builder assistant

  1. As a user, I want to drag my controls onto a grid and position them how I like, so that I build my own composer.
  2. As a user, I want to set each control's color, so that my composer is visually mine.
  3. As a user, I want to show or hide each control's title, so that I balance density and clarity.
  4. As a user, I want the app to ship with a sensible default layout, so that it's usable out of the box.
  5. As a user, I want to describe a button or layout in words and have my own LLM generate the config, so that I build tools without learning the schema.
  6. As a user, I want the generated config validated and previewed before it's saved, so that a bad generation can't break my composer.

Implementation Decisions

  • Two concepts, one-way dependency. Chassis Action = behavior only (no position/color; one showLabel flag in MVP). Composer Layout consumes actions.
  • Effects ride existing seams; no pi runtime coupling in MVP. No programmatic extension-trigger API exists in pi — extension commands are slash commands. submit uses the existing composer submit path; extension is a submit of the extension's slash command. wrap extends the plan-mode prompt-wrapping logic.
  • Schema (MVP). Definitions in ~/.pi/agent/chassis/state.json (global per-user), mirroring caveman state:
    { "version": 1, "actions": [
      { "id": "uuid", "label": "Security audit", "showLabel": true,
        "trigger": "oneShot", "effect": { "type": "submit", "text": "/security-scan" } }
      // | { ..., "trigger": "sticky", "effect": { "type": "wrap", "template": "...{{input}}..." } }
    ] }
    oneShotsubmit, stickywrap. {{input}} is the only template token.
  • Scope. Registry global per-user. Sticky activation app-global for MVP → per-project-folder later, never per-thread (ADR 0004); activation lives in app-global desktop state, not the definitions file.
  • Composition. One active wrap at a time (radio); applied inside plan mode: planModePrompt(userWrap(rawText)).
  • One render seam. Every action renders via a single ChassisActionControl in the composer control row, alongside prebuilt toggles. No generic slot/contract for built-in controls until the grid forces it.
  • Validation. Validate on load; drop malformed actions.
  • Authoring UI. Settings → Actions section for MVP. The dedicated "build your own composer" surface arrives with the Phase 3 grid.
  • Phase 2 — reminder. Adds effect: { type: "reminder", text }; a companion skill reads chassis state at session start (caveman pattern). This is the first piece with a shippable skill artifact + install step.
  • Phase 2 — extension picker. The create form lists installed runtime commands (skills/extensions) and writes the chosen command's slash text as a submit effect. No new runtime API.
  • Phase 2 — per-folder scope. Action definitions and sticky activation key off the active project folder/workspace rather than app-global.
  • Phase 3 — Composer Layout grid (design required). Open questions: grid data model and persistence, snap/drag/resize behavior, per-control color system, how built-in controls and Chassis Actions become uniform slottable units, and the shipped default layout. Needs a design pass before implementation.
  • Phase 3 — LLM builder assistant (design required). Open questions: where it lives (in the layout surface), which model it uses (user's configured model), the describe→config→validate→preview flow, and safety/validation of generated config. Needs a design pass.

Testing Decisions

Test external behavior at the highest existing seam.

  • State parse/validate — pure parseChassisState(json) -> { actions, dropped }, unit (vitest), no Electron.
  • Effect composition — pure function extending composer-mode.ts: raw text + plan on/off + active wrap → composed prompt (wrap inside plan, single-wrap radio). Unit. Prior art: buildPlanModePrompt tests.
  • One-shot submit — e2e live: payload lands in transcript, draft preserved, works idle/running.
  • Composer render + Settings CRUD — e2e core: created action appears in Settings and renders across in-thread/new-thread/pending surfaces.
  • Phase 2 reminder — verify the companion skill picks up reminder state at session start (live/runtime lane).
  • Phase 3 — testing decisions deferred to each design pass (the grid and builder need their own seams defined first).

Out of Scope

  • Stacking multiple simultaneous wraps.
  • Migrating the prebuilt toggles (plan/build, caveman, orchestrate) into the registry — they stay separate and per-thread.
  • Structured args for extension commands (payload is raw text).
  • Detailed Phase 3 implementation specs (grid, builder) — these are gated behind design issues, not specified here.

Further Notes

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentReady for an agent to implement

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions