Documentation
¶
Index ¶
- func Dispatch(ctx context.Context, workDir string, streams domain.IOStreams, ...) error
- func HandleProactive(ctx context.Context, workDir string, streams domain.IOStreams, ...) error
- func HandleReactive(ctx context.Context, workDir string, streams domain.IOStreams, ...) error
- func IsInteractiveTTY(streams domain.IOStreams) bool
- func MapCommitType(ccType string) string
- func RelativeAge(d time.Duration) string
- func ResolvePending(ctx context.Context, workDir string, streams domain.IOStreams, ...) error
- func SavePending(workDir string, record PendingRecord) error
- type Answers
- type DetectOpts
- type DetectionResult
- type LineRenderer
- type Option
- type PendingAnswers
- type PendingItem
- type PendingRecord
- type ProactiveOpts
- type ProgressRenderer
- type QuestionFlow
- func (q *QuestionFlow) AskAlternatives(ctx context.Context) (string, error)
- func (q *QuestionFlow) AskImpact(ctx context.Context) (string, error)
- func (q *QuestionFlow) AskQuestions(ctx context.Context, opts QuestionOpts) (Answers, error)
- func (q *QuestionFlow) AskType(ctx context.Context, defaultType string) (string, error)
- func (q *QuestionFlow) AskWhat(ctx context.Context, defaultWhat string) (string, error)
- func (q *QuestionFlow) AskWhy(ctx context.Context) (string, error)
- func (q *QuestionFlow) RunFlow(ctx context.Context, commit *domain.CommitInfo) (Answers, error)
- type QuestionOpts
- type Renderer
- type ResolveOpts
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Dispatch ¶
func Dispatch(ctx context.Context, workDir string, streams domain.IOStreams, gitAdapter domain.GitAdapter) error
Dispatch is the central router for the post-commit hook workflow. All contextual detection (non-TTY, doc-skip, merge, rebase, cherry-pick, amend) is delegated to Detect() inside HandleReactive (Story 2.5).
func HandleProactive ¶
func HandleProactive(ctx context.Context, workDir string, streams domain.IOStreams, opts ProactiveOpts) error
HandleProactive runs the manual or retroactive documentation flow for `lore new`. When opts.Commit is non-nil, retroactive mode pre-fills Type/What from the commit and sets generated_by to "retroactive" with the commit hash in front matter.
func HandleReactive ¶
func HandleReactive(ctx context.Context, workDir string, streams domain.IOStreams, gitAdapter domain.GitAdapter) error
HandleReactive runs the full interactive post-commit flow:
- Detects context (merge, rebase, cherry-pick, amend, non-TTY, doc-skip).
- Reads HEAD commit info via gitAdapter.
- Presents the question flow on streams.
- Generates and persists the document under .lore/docs/.
- Prints "Captured {filename}" on stderr.
On context cancellation (Ctrl+C forwarded via signal.NotifyContext in main), any partial answers collected before the interruption are saved to .lore/pending/{hash}.yaml (silent best-effort). HandleReactive runs the full interactive post-commit flow with default detection options. Use handleReactiveWithOpts for injection in tests.
func IsInteractiveTTY ¶
IsInteractiveTTY reports whether the session is running in an interactive TTY. Detection order (deterministic, not timer-based):
- TERM=dumb → false (Emacs shell-mode, IDEs)
- LORE_LINE_MODE=1 → false (forced plain output)
- stdin or stderr not TTY → false
- Otherwise → true
Story 2.5 reuses this helper in detection.go without duplication.
func MapCommitType ¶
MapCommitType converts a Conventional Commit type to a Lore document type. Falls back to "note" for unknown types.
func RelativeAge ¶
RelativeAge formats a duration into a human-readable relative age string.
func ResolvePending ¶
func ResolvePending(ctx context.Context, workDir string, streams domain.IOStreams, item PendingItem, gitAdapter domain.GitAdapter, opts ResolveOpts) error
ResolvePending resolves a pending item: displays commit context, asks only remaining questions (preserving partial answers), generates the document via the standard pipeline, and deletes the pending file.
func SavePending ¶
func SavePending(workDir string, record PendingRecord) error
SavePending writes partial answers to .lore/pending/{hash}.yaml. The directory is created with os.MkdirAll if absent (per NOTE m19). Relative paths work when CWD is the git work tree (item L19).
Types ¶
type Answers ¶
type Answers struct {
Type string
What string
Why string
Alternatives string // empty if express mode skipped
Impact string // empty if express mode skipped
}
Answers holds the user's responses to the interactive question flow.
func (Answers) ToGenerateInput ¶
func (a Answers) ToGenerateInput(commit *domain.CommitInfo, generatedBy string) generator.GenerateInput
ToGenerateInput converts Answers + CommitInfo into a generator.GenerateInput. The conversion happens in workflow/ to avoid circular deps (generator → workflow). generatedBy distinguishes hook-triggered ("hook") from manual ("manual") flows so that the front-matter field is correct for both reactive.go and proactive.go.
type DetectOpts ¶
type DetectOpts struct {
// GetEnv reads an environment variable. Defaults to os.Getenv.
GetEnv func(string) string
// IsTTY reports whether the given streams represent an interactive TTY.
// Defaults to IsInteractiveTTY (which delegates to ui.IsTerminal).
// M2 fix: replaces the ForceInteractive bool antipattern — tests inject
// func(_ domain.IOStreams) bool { return true } to bypass TTY detection
// without polluting production code with a test-only flag.
IsTTY func(domain.IOStreams) bool
// Corpus provides doc existence checks for cherry-pick (AC-5) and amend
// (AC-4) detection. When nil, these checks are skipped and the old
// unconditional skip/amend behavior applies (backward compat for tests
// that don't need doc existence verification).
Corpus domain.CorpusReader
}
DetectOpts holds injectable dependencies for testability (NOTE m22).
type DetectionResult ¶
type DetectionResult struct {
Action string // "proceed", "skip", "defer", "amend"
Reason string // "doc-skip", "non-tty", "rebase", "merge", "cherry-pick", "amend"
Message string // human-readable message for stderr (empty = silent)
}
DetectionResult describes how the hook should handle the current commit.
func Detect ¶
func Detect(ctx context.Context, ref string, git domain.GitAdapter, streams domain.IOStreams, opts DetectOpts) (DetectionResult, error)
Detect determines the appropriate action for the current commit context.
Detection order (first match wins — priority is deterministic per Dev Notes):
- [doc-skip] marker → skip silently (explicit developer intent, exit 0)
- Non-TTY / TERM=dumb → defer pending (CI must never block)
- Rebase in progress → defer pending (avoid questionnaire per replay)
- Merge commit → skip with 1-line stderr message
- Cherry-pick → skip silently (CHERRY_PICK_HEAD present)
- Amend → propose modification of existing doc
- Otherwise → proceed with normal interactive flow
GitAdapter methods do NOT accept ctx (per architecture.md NOTE C2). Cancellation is managed at the workflow/cobra level.
type LineRenderer ¶
type LineRenderer struct {
// contains filtered or unexported fields
}
LineRenderer is the non-TTY renderer for CI/pipe environments. One line per event, no ANSI rewriting — CI-compatible.
func NewLineRenderer ¶
func NewLineRenderer(streams domain.IOStreams) *LineRenderer
func (*LineRenderer) ExpressSkip ¶
func (r *LineRenderer) ExpressSkip(skipped int)
func (*LineRenderer) Progress ¶
func (r *LineRenderer) Progress(current, total int, label string)
func (*LineRenderer) QuestionConfirm ¶
func (r *LineRenderer) QuestionConfirm(question string, answer string)
func (*LineRenderer) QuestionStart ¶
func (r *LineRenderer) QuestionStart(question string, defaultVal string)
type Option ¶
type Option func(*flowOptions)
Option is a functional option for QuestionFlow.
func WithExpressThreshold ¶
WithExpressThreshold sets the cumulative time threshold for express mode.
type PendingAnswers ¶
type PendingAnswers struct {
Type string `yaml:"type"`
What string `yaml:"what"`
Why string `yaml:"why,omitempty"`
Alternatives string `yaml:"alternatives,omitempty"`
Impact string `yaml:"impact,omitempty"`
}
PendingAnswers holds the question responses collected before interruption.
type PendingItem ¶
type PendingItem struct {
Filename string // e.g. "abc1234.yaml"
CommitHash string // short hash (from filename / record.Commit)
CommitMessage string // from record.Message
CommitDate time.Time // parsed from record.Date
Answers PendingAnswers
Progress string // "3/5"
RelativeAge string // "2 days ago"
}
PendingItem is the view-model for a single pending entry, used by ListPending and the cmd layer.
func ListPending ¶
func ListPending(ctx context.Context, pendingDir string, warnWriter func(string)) ([]PendingItem, error)
ListPending reads and parses all YAML files in pendingDir, returning them sorted by date descending (most recent first). Corrupt files are skipped with a warning written to warnWriter (if non-nil).
func SkipPending ¶
SkipPending deletes the pending file matching the given commit hash without creating a document. The hash must match exactly or be a unique prefix among all pending items.
type PendingRecord ¶
type PendingRecord struct {
Commit string `yaml:"commit"`
Date string `yaml:"date"`
Message string `yaml:"message"`
Answers PendingAnswers `yaml:"answers"`
Status string `yaml:"status"` // "partial" | "deferred"
Reason string `yaml:"reason"` // "interrupted" | "non-tty" | "rebase"
}
PendingRecord is the YAML structure written to .lore/pending/{hash}.yaml on Ctrl+C or non-TTY deferral. The file is retained for manual inspection until `lore pending` (Epic 10) processes it.
func BuildPendingRecord ¶
func BuildPendingRecord(answers Answers, commitHash, commitMsg, reason, status string) PendingRecord
BuildPendingRecord converts partial answers into a PendingRecord. commitHash / commitMsg may be empty if the commit could not be read. status must be "partial" (interrupted mid-flow) or "deferred" (non-TTY / rebase batch).
type ProactiveOpts ¶
type ProactiveOpts struct {
Type string // pre-filled type (may be empty)
What string // pre-filled what (may be empty)
Why string // pre-filled why (may be empty)
Commit *domain.CommitInfo // retroactive mode: resolved commit info (nil → manual mode)
IsTTY func(domain.IOStreams) bool // N4 fix: optional TTY override for testing (nil → IsInteractiveTTY)
}
ProactiveOpts holds pre-filled arguments from the CLI for lore new.
type ProgressRenderer ¶
type ProgressRenderer struct {
// contains filtered or unexported fields
}
ProgressRenderer is the TTY renderer that condenses confirmed answers via ANSI. Budget: ~7 lines stderr max.
func NewProgressRenderer ¶
func NewProgressRenderer(streams domain.IOStreams) *ProgressRenderer
func (*ProgressRenderer) ExpressSkip ¶
func (r *ProgressRenderer) ExpressSkip(skipped int)
ExpressSkip prints the express skip feedback.
func (*ProgressRenderer) Progress ¶
func (r *ProgressRenderer) Progress(current, total int, label string)
Progress renders the progress bar [##·] N+ label.
func (*ProgressRenderer) QuestionConfirm ¶
func (r *ProgressRenderer) QuestionConfirm(question string, answer string)
QuestionConfirm updates the confirmed bar and redraws.
func (*ProgressRenderer) QuestionStart ¶
func (r *ProgressRenderer) QuestionStart(question string, defaultVal string)
QuestionStart prints the progress bar + question prompt. Overwrites previous lines via cursor-up escape.
type QuestionFlow ¶
type QuestionFlow struct {
// contains filtered or unexported fields
}
QuestionFlow orchestrates the inverse funnel question sequence.
func NewQuestionFlow ¶
func NewQuestionFlow(streams domain.IOStreams, renderer Renderer, opts ...Option) *QuestionFlow
NewQuestionFlow creates a QuestionFlow with the given renderer and options.
func (*QuestionFlow) AskAlternatives ¶
func (q *QuestionFlow) AskAlternatives(ctx context.Context) (string, error)
AskAlternatives prompts for alternatives considered (optional).
func (*QuestionFlow) AskImpact ¶
func (q *QuestionFlow) AskImpact(ctx context.Context) (string, error)
AskImpact prompts for impact (optional).
func (*QuestionFlow) AskQuestions ¶
func (q *QuestionFlow) AskQuestions(ctx context.Context, opts QuestionOpts) (Answers, error)
AskQuestions is the unified question flow for all documentation paths. It handles pre-filled answers, express mode, and commit-based defaults.
On error (including context cancellation), AskQuestions returns the partial answers collected so far. Callers are responsible for saving these as pending via BuildPendingRecord + SavePending — AskQuestions does not persist state because it lacks the commit hash and context needed for the pending record.
Behavior per field:
- Pre-filled + valid → confirm and skip (no prompt)
- Pre-filled but invalid type → interactive with "note" default
- Empty → interactive prompt with commit-derived defaults when available
- Express mode → if first 3 Qs answered within expressThreshold, skip Alternatives+Impact
func (*QuestionFlow) AskType ¶
AskType prompts for document type with a pre-filled default. Enter confirms, any input replaces.
func (*QuestionFlow) AskWhy ¶
func (q *QuestionFlow) AskWhy(ctx context.Context) (string, error)
AskWhy prompts for the reason — the single true question of the flow.
func (*QuestionFlow) RunFlow ¶
func (q *QuestionFlow) RunFlow(ctx context.Context, commit *domain.CommitInfo) (Answers, error)
RunFlow orchestrates all 5 questions with express mode and returns Answers. commitInfo is used to pre-fill Type and What defaults. Package-internal: called by runDocumentationFlow in reactive.go.
type QuestionOpts ¶
type QuestionOpts struct {
PreFilled Answers // partial or full pre-filled answers (resolve/proactive)
Express bool // enable express mode — timer-based skip for Alternatives+Impact (reactive only)
CommitInfo *domain.CommitInfo // for pre-fill defaults (type from MapCommitType, what from Subject)
}
QuestionOpts controls the behavior of AskQuestions for all 4 documentation paths.
type Renderer ¶
type Renderer interface {
// QuestionStart displays a question before the user types.
QuestionStart(question string, defaultVal string)
// QuestionConfirm condenses a confirmed answer into the summary bar.
QuestionConfirm(question string, answer string)
// Progress shows the current question position and label.
Progress(current, total int, label string)
// ExpressSkip announces that optional questions were skipped.
ExpressSkip(skipped int)
}
Renderer abstracts TTY vs non-TTY output during the question flow. Implementations: ProgressRenderer (TTY) and LineRenderer (non-TTY/CI).
func NewRenderer ¶
NewRenderer returns the appropriate Renderer for the given streams.
type ResolveOpts ¶
ResolveOpts holds options for ResolvePending.