Documentation
¶
Index ¶
- Variables
- func CountDiffLines(diff string) (added, deleted int)
- func Dispatch(ctx context.Context, workDir string, streams domain.IOStreams, ...) error
- func DispatchFull(ctx context.Context, workDir string, streams domain.IOStreams, ...) error
- func DispatchWithNotifyConfig(ctx context.Context, workDir string, streams domain.IOStreams, ...) error
- func ExtractFilesFromDiff(diff string) []string
- func FlushOnInterrupt()
- func HandleProactive(ctx context.Context, workDir string, streams domain.IOStreams, ...) error
- func HandleReactive(ctx context.Context, workDir string, streams domain.IOStreams, ...) error
- func HandleReactiveWithEngine(ctx context.Context, workDir string, streams domain.IOStreams, ...) error
- func IsInteractiveTTY(streams domain.IOStreams) bool
- func MapCommitType(ccType string) string
- func PreflightCheck(workDir string) error
- func RegisterInterruptState(workDir, commitHash, commitMsg string, answers *Answers)
- 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 DetectionAction
- type DetectionResult
- type DispatchConfig
- 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)
- func (q *QuestionFlow) RunFlowWithMode(ctx context.Context, commit *domain.CommitInfo, detection *DetectionResult) (Answers, error)
- type QuestionMode
- type QuestionOpts
- type Renderer
- type ResolveOpts
Constants ¶
This section is empty.
Variables ¶
var ErrInterrupted = fmt.Errorf("interrupted")
ErrInterrupted signals that the user pressed Ctrl+C during an interactive prompt.
Functions ¶
func CountDiffLines ¶ added in v1.0.0
CountDiffLines counts added and deleted lines from unified diff output. Limited to first 10000 lines for performance on very large diffs.
func Dispatch ¶
func Dispatch(ctx context.Context, workDir string, streams domain.IOStreams, gitAdapter domain.GitAdapter, engine *decision.Engine, store domain.LoreStore) error
Dispatch is the central router for the post-commit hook workflow. engine and store may be nil (backward compat — graceful degradation).
func DispatchFull ¶ added in v1.1.0
func DispatchFull(ctx context.Context, workDir string, streams domain.IOStreams, gitAdapter domain.GitAdapter, engine *decision.Engine, store domain.LoreStore, cfg DispatchConfig) error
DispatchFull is the full dispatch with all configuration options.
func DispatchWithNotifyConfig ¶ added in v1.0.0
func DispatchWithNotifyConfig(ctx context.Context, workDir string, streams domain.IOStreams, gitAdapter domain.GitAdapter, engine *decision.Engine, store domain.LoreStore, notifyCfg *notify.NotifyConfig) error
DispatchWithNotifyConfig is Dispatch with explicit notification configuration (ADR-023). notifyCfg may be nil (defaults to DefaultNotifyConfig).
func ExtractFilesFromDiff ¶ added in v1.0.0
ExtractFilesFromDiff parses file names from unified diff output (+++ b/ lines).
func FlushOnInterrupt ¶ added in v1.1.0
func FlushOnInterrupt()
FlushOnInterrupt saves the current flow state as pending. Called from the signal handler in root.go. Safe to call multiple times or with no state.
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 HandleReactiveWithEngine ¶ added in v1.0.0
func HandleReactiveWithEngine(ctx context.Context, workDir string, streams domain.IOStreams, gitAdapter domain.GitAdapter, engine *decision.Engine, store domain.LoreStore) error
HandleReactiveWithEngine runs the full interactive post-commit flow with optional Decision Engine and store.
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
This helper is reused 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 PreflightCheck ¶ added in v1.1.0
PreflightCheck validates that the documentation pipeline can succeed BEFORE asking the user any questions. Returns nil if everything is OK, or a descriptive error if the pipeline would fail post-questions.
Why: the user should never spend 90 seconds answering questions only to hit a template or filesystem error at the end. This preserves the "90 seconds or nothing" contract.
func RegisterInterruptState ¶ added in v1.1.0
RegisterInterruptState sets the current flow state so FlushOnInterrupt can save a pending record. Call with nil answers to clear.
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
// Store provides O(1) doc lookup by commit hash. When nil, falls back to corpus scan.
Store domain.LoreStore
// Engine is the Decision Engine for multi-signal scoring.
// When nil, step 7 is skipped and fallback proceed applies (backward compat).
Engine *decision.Engine
// SignalCtx holds pre-built signal context for the Decision Engine.
// Only used when Engine is non-nil.
SignalCtx *decision.SignalContext
// NotifyConfig holds notification preferences from .lorerc.
// Used by handleDetectionResult to configure non-TTY notifications (ADR-023).
// When nil, DefaultNotifyConfig() is used.
NotifyConfig *notify.NotifyConfig
// AmendPrompt controls whether to ask "Document this change?" (Question 0)
// before the amend flow in TTY mode. Defaults to true.
// Set to false via hooks.amend_prompt=false in .lorerc to skip the prompt.
AmendPrompt *bool
}
DetectOpts holds injectable dependencies for testability (NOTE m22).
type DetectionAction ¶ added in v1.0.0
type DetectionAction = string
DetectionAction represents the possible outcomes of commit detection.
const ( ActionProceed DetectionAction = "proceed" ActionSkip DetectionAction = "skip" ActionDefer DetectionAction = "defer" ActionAmend DetectionAction = "amend" ActionAutoSkip DetectionAction = "auto-skip" ActionSuggestSkip DetectionAction = "suggest-skip" ActionAskReduced DetectionAction = "ask-reduced" ActionAskFull DetectionAction = "ask-full" )
type DetectionResult ¶
type DetectionResult struct {
Action string // "proceed", "skip", "defer", "amend", "auto-skip", "suggest-skip", "ask-reduced", "ask-full"
Reason string
Message string // human-readable message for stderr (empty = silent)
Score int // 0-100 from Decision Engine (0 if no scoring)
QuestionMode string // full, reduced, confirm, none
PrefilledWhat string
PrefilledWhy string
PrefilledWhyConfidence float64
}
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 DispatchConfig ¶ added in v1.1.0
type DispatchConfig struct {
NotifyConfig *notify.NotifyConfig
AmendPrompt *bool // nil = default (true); set to false to skip Question 0
}
DispatchConfig holds optional configuration for the post-commit dispatch.
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` 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 using an interactive arrow-key selector (TTY) or a text input with validation (non-TTY). Invalid types are rejected with a retry loop.
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.
func (*QuestionFlow) RunFlowWithMode ¶ added in v1.0.0
func (q *QuestionFlow) RunFlowWithMode(ctx context.Context, commit *domain.CommitInfo, detection *DetectionResult) (Answers, error)
RunFlowWithMode orchestrates the question flow with Decision Engine context. In "reduced" mode: Type and What are pre-filled, only Why is asked interactively. In "confirm" mode: all 3 are pre-filled, user confirms with Enter. If detection is nil, behaves like RunFlow (full mode).
type QuestionMode ¶ added in v1.0.0
type QuestionMode = string
QuestionMode controls the depth of interactive questioning.
const ( QModeFull QuestionMode = "full" QModeReduced QuestionMode = "reduced" QModeConfirm QuestionMode = "confirm" QModeNone QuestionMode = "none" )
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 ¶
type ResolveOpts struct {
IsTTY func(domain.IOStreams) bool // optional TTY override for testing
// Batch fields — when Type, What, and Why are all non-empty,
// skip interactive prompts and generate directly (ADR-023 AC-12).
Type string
What string
Why string
Alternatives string
Impact string
}
ResolveOpts holds options for ResolvePending.