Documentation
¶
Overview ¶
Package prompt is a generic, reusable question/prompt manager that mirrors the task manager: a Go API whose pending-prompt state streams to ready-made UI components. Every prompt is described by a JSON Schema (rendered by clicky-ui's JsonSchemaForm), state lives behind a swappable cache-backed Store (in-memory by default, valkey in the sibling submodule), and the global Manager doubles as the "interactive sink" the clicky root routes TTY-less prompts to.
Producers (commit checks, tool-permission approvals, AI elicitation) call Manager.Ask and block; consumers (the dashboard via SSE/JSON, or an AI model via a validated tool result) resolve the prompt by id.
Index ¶
- func ConfirmSchema(title string) json.RawMessage
- func ConfirmValue(ans Answer) bool
- func HasInteractiveSink() bool
- func MultiSelectSchema(title string, options []string, maxItems int) json.RawMessage
- func PreferSink(ctx context.Context) bool
- func SelectSchema(title string, options []string) json.RawMessage
- func SelectedIndex(ans Answer) int
- func SelectedIndexes(ans Answer) []int
- func SetDefault(m *Manager)
- func TextSchema(title string, secret bool) json.RawMessage
- func TextValue(ans Answer) string
- func Validate(schema json.RawMessage, values map[string]any) error
- func WithScope(ctx context.Context, s Scope) context.Context
- type Answer
- type Filter
- type Manager
- func (m *Manager) Ask(ctx context.Context, p Prompt) (Answer, error)
- func (m *Manager) JSONHandler() http.Handler
- func (m *Manager) List(filter Filter) []PromptSnapshot
- func (m *Manager) Pending(id string) (PromptSnapshot, bool)
- func (m *Manager) RegisterHandlers(mux *http.ServeMux, prefix string)
- func (m *Manager) Resolve(id string, ans Answer) error
- func (m *Manager) ResolveHandler() http.Handler
- func (m *Manager) SSEHandler() http.Handler
- func (m *Manager) Select(ctx context.Context, title string, labels []string, opts SelectOptions) ([]int, bool)
- func (m *Manager) Snapshot(id string) (PromptSnapshot, bool)
- func (m *Manager) Text(ctx context.Context, title, def string, secret bool) (string, bool)
- type MemoryConfig
- type Prompt
- type PromptSnapshot
- type Scope
- type SelectOptions
- type State
- type Store
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ConfirmSchema ¶
func ConfirmSchema(title string) json.RawMessage
ConfirmSchema builds an object schema with a single boolean "value".
func ConfirmValue ¶
ConfirmValue extracts the boolean from a ConfirmSchema answer.
func HasInteractiveSink ¶
func HasInteractiveSink() bool
HasInteractiveSink reports whether a Manager is installed — i.e. whether a TTY-less prompt has somewhere (the dashboard) to go instead of failing.
func MultiSelectSchema ¶
func MultiSelectSchema(title string, options []string, maxItems int) json.RawMessage
MultiSelectSchema builds an object schema whose "choice" is an array of option indices. uniqueItems rejects a repeated selection; maxItems (when > 0) caps the number of selections so the sink/dashboard path enforces the same limit as the terminal multi-select.
func PreferSink ¶
PreferSink reports that a prompt under ctx should be routed to the interactive sink in preference to any attached TTY: a manager is installed and ctx carries a Scope, meaning the prompt was raised on behalf of a UI-driven operation (e.g. the dashboard's auto-commit). Without this, a dashboard server launched from a terminal would render its prompts on that terminal instead of the dashboard.
func SelectSchema ¶
func SelectSchema(title string, options []string) json.RawMessage
SelectSchema builds an object schema with a single required radio "choice" whose value is the selected option's index.
func SelectedIndex ¶
SelectedIndex extracts the chosen option index from a SelectSchema answer, or -1 if absent/unparseable.
func SelectedIndexes ¶
SelectedIndexes extracts the chosen option indices from a MultiSelectSchema answer.
func SetDefault ¶
func SetDefault(m *Manager)
SetDefault installs the process-wide Manager that GlobalManager returns and that HasInteractiveSink reports on. A host installs one to route TTY-less prompts to the dashboard.
func TextSchema ¶
func TextSchema(title string, secret bool) json.RawMessage
TextSchema builds an object schema with a single required free-text "value".
Types ¶
type Answer ¶
Answer is the resolution of a Prompt. Values must satisfy the prompt's Schema (validated on Resolve) unless Cancelled is set.
type Filter ¶
Filter scopes a snapshot listing. Empty fields match everything; Labels entries must all match (AND).
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager brokers prompts between producers (which Ask and block) and consumers (which Resolve by id). It mirrors the approval-registry channel handoff but is generic and backed by a Store so the UI can list/stream pending prompts. The process-wide Manager (GlobalManager) also serves as the interactive sink the clicky root routes TTY-less PromptSelect/PromptText calls to.
func GlobalManager ¶
func GlobalManager() *Manager
GlobalManager returns the installed process-wide Manager, or nil.
func NewManager ¶
NewManager returns a Manager backed by store (use NewMemory for the default).
func (*Manager) Ask ¶
Ask registers p as a pending prompt and blocks until it is resolved or ctx is cancelled. A blank ID is assigned a unique one. The snapshot is always left in a terminal state (answered/cancelled/expired) and the pending channel removed before returning.
func (*Manager) JSONHandler ¶
JSONHandler serves the current prompt snapshots (filtered) as JSON — the poll fallback and reconnect path for the UI.
func (*Manager) List ¶
func (m *Manager) List(filter Filter) []PromptSnapshot
List returns snapshots matching filter (newest first).
func (*Manager) Pending ¶
func (m *Manager) Pending(id string) (PromptSnapshot, bool)
Pending returns the prompt's snapshot if it is still awaiting an answer.
func (*Manager) RegisterHandlers ¶
RegisterHandlers wires the prompt API under prefix:
GET {prefix}/prompts snapshot listing (?owner=&kind=&state=&label=k=v)
GET {prefix}/prompts/stream SSE stream of snapshots (same filters)
GET {prefix}/prompts/{id} single snapshot
POST {prefix}/prompts/{id}/answer resolve a prompt
The static "stream" route is registered before "{id}" so it is not parsed as an id (Go 1.22 path-value routing).
func (*Manager) Resolve ¶
Resolve delivers an answer to a pending prompt, unblocking its Ask. A non- cancelled answer is validated against the prompt's schema first; an invalid answer is rejected (and the prompt stays pending) so the producer never receives a malformed value.
func (*Manager) ResolveHandler ¶
ResolveHandler resolves a prompt by id from a POSTed answer body. The id is read from the {id} path value. A schema-invalid answer yields 400; an unknown or already-resolved prompt yields 409.
func (*Manager) SSEHandler ¶
SSEHandler streams prompt snapshots (filtered) as Server-Sent Events, re-emitting only when the snapshot set changes. Unlike the task stream it never sends a terminal event: a dashboard stays subscribed to observe new prompts for the life of the page.
func (*Manager) Select ¶
func (m *Manager) Select(ctx context.Context, title string, labels []string, opts SelectOptions) ([]int, bool)
Select asks the user to choose among labels and returns the chosen indices. ok is false when the prompt was cancelled. The prompt inherits any Scope on ctx. This is the entry point the clicky root routes PromptSelect/PromptMultiSelect to when no TTY is attached.
type MemoryConfig ¶
MemoryConfig tunes the in-process Store. Zero Retention -> 10m: resolved prompts are kept that long so a reconnecting UI still sees the outcome, then GC'd. Pending prompts are never GC'd.
type Prompt ¶
type Prompt struct {
ID string
Kind string
Title string
Description string
// Schema is a JSON Schema (object) describing the shape of Answer.Values. It is
// what clicky-ui renders and what Resolve validates the answer against.
Schema json.RawMessage
Default map[string]any
Owner string
Labels map[string]string
CreatedAt time.Time
}
Prompt is a question awaiting an answer, described by a JSON Schema form. Owner and Labels are the scoping keys the UI filters on (e.g. Owner=todoID, Labels{"session": id}) — the same role kind/labels/owner play for task RunMeta.
type PromptSnapshot ¶
type PromptSnapshot struct {
ID string `json:"id"`
Kind string `json:"kind,omitempty"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
Schema json.RawMessage `json:"schema"`
State string `json:"state"`
Value map[string]any `json:"value,omitempty"`
Cancelled bool `json:"cancelled,omitempty"`
Owner string `json:"owner,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
CreatedAt string `json:"createdAt,omitempty"` // RFC3339
ResolvedAt string `json:"resolvedAt,omitempty"` // RFC3339
}
PromptSnapshot is the JSON-serializable state of a prompt for UI/SSE consumption. It mirrors task.TaskSnapshot: a flat, omitempty-friendly wire shape.
type Scope ¶
Scope tags prompts a producer raises so the UI can filter them to a todo (Owner) or a session (Labels["session"]). It travels on the context because the generic clicky PromptSelect/PromptText entry points carry no domain identifiers; a host (e.g. gavel's auto-commit) wraps its context with WithScope so prompts it triggers inherit the right Owner/Labels/Kind.
type SelectOptions ¶
SelectOptions configures Manager.Select. Multi toggles checkbox vs radio; MaxItems (> 0, multi only) caps the number of selections so the sink path enforces the same limit as the terminal multi-select.
type Store ¶
type Store interface {
Set(snap PromptSnapshot) error
Get(id string) (PromptSnapshot, bool)
Delete(id string) error
// List returns snapshots matching filter, newest first.
List(filter Filter) []PromptSnapshot
}
Store persists prompt snapshots so the manager's state survives a UI reconnect and can (with a valkey-backed Store) be shared across processes. It mirrors the metrics.Timeseries split: NewMemory here, valkey.New in the submodule. Implementations must be safe for concurrent use.
func NewMemory ¶
func NewMemory(cfg MemoryConfig) Store
NewMemory returns an in-process Store. It needs no backend and is the zero-config default for CLIs, tests, and single-process servers.