6.5 KiB
6.5 KiB
Architecture
The codebase is split so the implementation reads through file boundaries and names, not through large explanatory docs.
Design goals
- thin pi-specific wiring
- pure, testable state transitions
- rendering separated from decision logic
- a small stable tool contract
Module map
Tool surface
src/index.ts— extension entrypointsrc/ask-tool.ts— tool registration, non-interactive fallback, transcript rendering, ask payload capturesrc/answer-commands.ts—/answer,/answer:again, and/ask:replaycommand wiringsrc/answer-extraction.ts— configured extraction model selection, syntheticask_usertool-call extraction, and tolerant text fallbacksrc/ask-payload-store.ts— branch-aware persisted ask payload lookupsrc/pending-ask.ts— active-branch unresolved tool-call detection and recovery dismissal persistencesrc/resume-pending-ask.ts— detached lifecycle wiring and recovered answer deliverysrc/notifications.ts— best-effort ask notification payload rendering and channel executionsrc/remote-ask.ts— package-prefixed local event contract, active-flow registry, and explicit remote submission validationsrc/schema.ts— TypeBox schemasrc/types.ts— shared types
State
src/state/normalize.ts— normalize incoming questionssrc/state/answers.ts— mutate and serialize answerssrc/state/selectors.ts— read-only selectorssrc/state/transitions.ts— navigation, selection, notes, input, submit, cancelsrc/state/result.ts— convert UI state toAskResultsrc/state/view.ts— view-mode helperssrc/state.ts— state barrel used by UI and tests
Config
src/config/defaults.ts— canonical runtime defaultssrc/config/schema.ts— persisted config schema and runtime typesrc/config/migrate.ts— persisted-file validation boundary, including schema migration and context-aware keymap normalization/fallbacksrc/config/migrations/— ordered schema-version migration framework for persisted config shape changessrc/config/store.ts— non-destructive current/legacy config discovery, load, save, notice, and runtime subscription store
UI
src/ui/controller.ts— connects key input, editor lifecycle, live config subscription, dirty-dismiss confirmation, and pure state transitionssrc/ui/input.ts— raw input to commands using resolved context-aware config-backed keymapssrc/ui/dismiss-guard.ts— pure helpers for dirty-flow exit confirmation behaviorsrc/ui/render.tsandsrc/ui/render-*.ts— screen rendering, including config-backed footer/keymap hintssrc/ui/settings-list.ts/src/ui/show-settings.ts— lightweight ask settings list rendering and launchersrc/ui/constants.tsandsrc/ui/render-types.ts— rendering constants/contracts
Result formatting
src/result-format.ts— shared summary/result line formattingsrc/result.ts— final result renderingsrc/text.ts/src/constants.ts— shared display strings
Tests
tests/state.test.ts— state transitions and serializationtests/input.test.ts— editing/navigation key behaviortests/result.test.ts— summaries and transcript outputtests/render-*.test.ts/tests/text.test.ts— rendering helpers
Invariants worth preserving
- submit is never blocked by unanswered questions
- single-select answers serialize as arrays
- when
behaviour.presentSingleAsMultiis enabled, future single-select questions are handled with multi-select state semantics while result metadata preserves the requestedtypeand addspresentedType - active-flow question type changes are per-question runtime overrides handled in state/controller logic; they do not mutate the stored source payload or global config
- single-select free-form answers replace selected options for that question
- multi-select free-form answers augment selected options instead of clearing them
- preview questions keep their preview-pane behavior while also supporting the synthetic custom-answer option
- deselected option notes stay in UI state
- only selected option notes are emitted in the final result
- editor lifecycle stays in the controller, not in the reducers
- persisted ask settings are migrated to the current schema version in memory, validated, and normalized before use without rewriting the config file on load
- config schema migrations preserve user-provided values and add new defaults only when fields are absent
- replay payload lookup scans only the current session branch and revalidates payloads before use
- interrupted-ask recovery selects only the newest unresolved
ask_usercall on the active branch, prefers its valid persisted payload, and validates original arguments as fallback - recovered forms open only for TUI
startup,resume, andforkevents; the flow stays detached from the lifecycle handler - recovered submit and cancel both persist dismissal before the same call can reopen; manual
/ask:replayignores that recovery marker - invalid persisted keymaps fall back to default keymaps for the current session without discarding valid behaviour, notification, or answer settings
- invalid notification channels are skipped and fall back to the default bell channel if none are valid
- ask settings behaviour and notification enabled changes attempt to persist immediately from the settings list; save failures revert the change and show an error; config reset is guarded by a short double-press confirmation
presentSingleAsMultiis applied at ask-flow creation; toggling it does not rewrite already-normalized questions in an open flowmain.changeQuestionTypechanges the active question type live (non-preview:single <-> multi; preview:preview <-> multi) and may require confirmation before destructive multi-to-single conversion- when the ask config file is missing, the first ask use attempts to write a default persisted config snapshot under
~/.pi/agent/extensions/; if writing fails, built-in defaults are used for the session - legacy root config files are read as a fallback only when the current config file is absent; disk is left untouched
- invalid config files are left untouched; defaults are loaded for the session with a notice
- live config updates can affect an in-progress ask flow immediately
- remote ask submissions must be explicit
answerorcancelresponses; pi-ask validates ids/values but never infers approve/deny semantics from labels or option values
Documentation rule
Docs should explain contracts, responsibilities, and invariants. Code and tests should explain the rest.