Documentation
¶
Overview ¶
Package cli wires subcommands to the underlying packages. It owns no business logic of its own - only argument parsing, dispatching, and printing. This keeps the rest of the codebase testable in isolation.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type App ¶
type App struct {
Version string
In io.Reader
Out io.Writer
Err io.Writer
Paths config.Paths
// EditDraftFunc lets the apply command hand a draft off to a user's
// editor. When nil, defaultEditDraft is used (shells out via os/exec).
// Tests inject a deterministic fake so they never spawn a real editor.
EditDraftFunc func(current string) (string, error)
// NLClientFunc lets tests inject a fake LLM client for the natural-
// language REPL dispatcher. When nil, the dispatcher reads config.json
// via llm.New and falls back to Noop on misconfig.
NLClientFunc func() llm.Client
// CalendarClientFunc lets tests inject a fake calendar.Client so the
// add-interview / add-deadline flows can be exercised without
// hitting Google. When nil, the production path runs the real OAuth
// + Calendar API.
CalendarClientFunc func(ctx context.Context, a *App) (calendar.Client, error)
// InboxClientFunc lets tests inject a fake inbox.Client (no real
// IMAP, no real network) so the poll flow can be exercised
// end-to-end. When nil, the production path constructs a real
// IMAP client.
InboxClientFunc func(ctx context.Context, a *App) (inbox.Client, error)
}
App holds the CLI's I/O and resolved data paths. Construction does not touch disk - that happens inside individual subcommands so `help` and `version` always work even on a broken install.
func New ¶
New builds an App with default Paths. If the config dir cannot be located, we fall back to a relative ".jobforge" folder rather than blowing up.
func (*App) Run ¶
Run dispatches the chosen subcommand. Returning an error here causes main to exit non-zero and print the message. With no args, we drop into the REPL on a real TTY; pipes/CI/tests get plain help instead so they don't hang waiting for stdin.
func (*App) RunREPL ¶
RunREPL drops into an interactive prompt that dispatches each line through App.Run. Exits on EOF, `exit`/`quit`/`:q`, or context cancellation (Ctrl+C in the parent process). Errors from individual commands are printed and the loop continues — only EOF/quit ends the session.
Two backends:
- On a real TTY we use the Bubble Tea palette prompt (live `/` filter, arrow-key selection). See readLineWithPalette.
- On pipes / tests / CI we fall back to a plain line-buffered scanner so the loop is testable with strings.NewReader.