chore(ask): reorganize docs and fix repo checks

This commit is contained in:
Erik Kotsiuba
2026-04-18 21:31:39 +02:00
parent 3ec7af2fd8
commit 884c5fcf0d
38 changed files with 704 additions and 519 deletions
+17
View File
@@ -0,0 +1,17 @@
# Docs
This folder keeps only the documentation needed to understand and maintain the extension without turning docs into a second copy of the code.
## Files
- `contract.md` — external behavior, result shape, and UX guarantees
- `architecture.md` — module boundaries and invariants
## Reading order
- start with `contract.md` for behavior
- read `architecture.md` for code layout
## Rule of thumb
If a detail is about implementation mechanics, it should usually live in `src/` or `tests/`, not here.
+65
View File
@@ -0,0 +1,65 @@
# 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 entrypoint
- `src/ask-tool.ts` — tool registration, non-interactive fallback, transcript rendering
- `src/schema.ts` — TypeBox schema
- `src/types.ts` — shared types
### State
- `src/state/normalize.ts` — normalize incoming questions
- `src/state/answers.ts` — mutate and serialize answers
- `src/state/selectors.ts` — read-only selectors
- `src/state/transitions.ts` — navigation, selection, notes, input, submit, cancel
- `src/state/result.ts` — convert UI state to `AskResult`
- `src/state/view.ts` — view-mode helpers
- `src/state.ts` — state barrel used by UI and tests
### UI
- `src/ui/controller.ts` — connects key input, editor lifecycle, and pure state transitions
- `src/ui/input.ts` — raw input to commands
- `src/ui/render.ts` and `src/ui/render-*.ts` — screen rendering
- `src/ui/constants.ts` and `src/ui/render-types.ts` — rendering constants/contracts
- `src/ask-component.ts` — thin custom UI export
### Result formatting
- `src/result-format.ts` — shared summary/result line formatting
- `src/result.ts` — final result rendering
- `src/text.ts` / `src/constants.ts` — shared display strings
### Tests
- `tests/state.test.ts` — state transitions and serialization
- `tests/input.test.ts` — editing/navigation key behavior
- `tests/result.test.ts` — summaries and transcript output
- `tests/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
- free-form answers replace selected options for that question
- preview questions do not get 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
## Documentation rule
Docs should explain contracts, responsibilities, and invariants.
Code and tests should explain the rest.
+115
View File
@@ -0,0 +1,115 @@
# Ask tool contract
`ask_user` is a pi-native clarification tool for cases where implementation depends on user preference or missing requirements.
This document defines the stable external behavior. It does not explain internal helper-by-helper implementation.
## Input
```ts
{
title?: string;
questions: Array<{
id: string;
label?: string;
prompt: string;
type?: "single" | "multi" | "preview";
required?: boolean;
options: Array<{
value: string;
label: string;
description?: string;
preview?: string;
}>;
}>;
}
```
## Input rules
- at least one question is required
- `label` falls back to `Q1`, `Q2`, ...
- `type` defaults to `single`
- `required` is metadata only; it never blocks submission
- non-`preview` questions get an internal `Type your own` option
## Output
```ts
{
content: [{ type: "text"; text: string }];
details: {
title?: string;
cancelled: boolean;
questions: Array<{
id: string;
label: string;
prompt: string;
type: "single" | "multi" | "preview";
}>;
answers: Record<
string,
{
values: string[];
labels: string[];
indices: number[];
customText?: string;
note?: string;
optionNotes?: Record<string, string>;
}
>;
};
}
```
## Output rules
- `cancelled: true` means the user dismissed the flow or UI was unavailable
- single-select answers still use arrays
- `indices` are 1-based rendered option positions
- `customText` stores the free-form answer
- `note` stores a question-level note
- `optionNotes` includes only notes for selected options
- question notes may exist without a selected answer
## Supported UX
- tabbed multi-question flow
- single-select, multi-select, and preview questions
- inline free-form answers for non-preview questions
- question notes via `Ctrl+N`
- option notes via `N`
- number-key quick selection
- submit/cancel review tab
- transcript-friendly call and result rendering
## Keyboard behavior
Main flow:
- `Tab`, `Shift+Tab`, `Left`, `Right`: move between tabs
- `Up`, `Down`: move between options
- `Enter`: confirm or submit
- `Esc`: cancel the flow
- `1..9`: select or toggle the matching option
- `Space`: toggle the active option
- `N`: edit the active option note
- `Ctrl+N`: edit the current question note
Editing flow:
- `Enter`: save and submit the current input
- `Esc`: save draft and close the editor
- `Up`, `Down`, `Tab`, `Shift+Tab`: save draft and continue navigation
## Non-interactive mode
If `ctx.hasUI === false`, the tool returns an error message in `content` and a cancelled result in `details`.
## Source of truth
Behavior should be verified against:
1. `src/types.ts` and exported state/result helpers
2. `tests/*.test.ts`
3. this contract