cli

package
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: MIT Imports: 53 Imported by: 0

Documentation

Overview

Package cli holds the ccdad command tree and the process-wide exit contract.

Index

Constants

View Source
const ProfilesDirName = "profiles"

ProfilesDirName is the directory under the ccdad store that holds one persistent config home per account for `ccdad run --full-profile`.

Persistent, and per account rather than per invocation, because a clone is not cheap: measured on the machine this was written on, a live CLAUDE_CONFIG_DIR is 3.0 GB across 19,741 files, 97% of it `projects/`. A per-invocation copy of that is not a command anyone would wait for.

View Source
const SessionsDirName = "sessions"

SessionsDirName is the directory under the ccdad store that holds one credential home per `ccdad run` session. It is exported because `ccdad doctor` reports on what is left in it and, by the rule uninstall.go states, a top-level name belongs to the package that creates it.

Deliberately under CCDAD_HOME rather than the system temp directory: /tmp is age-cleaned (30 days on this machine, and cleared at boot on many), and the OS deleting a live session's credentials underneath a running claude is not a failure mode worth having.

Variables

This section is empty.

Functions

func ExecuteCmd

func ExecuteCmd(root *cobra.Command) error

ExecuteCmd runs an already-built root command and maps Cobra's raw errors into ccdad's exit taxonomy. Execute and the tests both go through it, so the mapping cannot depend on which caller ran the command — Cobra reports an unknown subcommand from Find(), before the root's own RunE is ever reached.

func IsUsageError

func IsUsageError(err error) bool

IsUsageError reports whether err, or anything it wraps, is a usage error.

func NewRootCmd

func NewRootCmd() *cobra.Command

NewRootCmd builds the ccdad command tree. It is a constructor rather than a package-level var so tests can build an isolated tree with its own output buffers and argument list.

func UsageError

func UsageError(format string, a ...any) error

UsageError builds an error that maps to ExitUsage.

func WithCode

func WithCode(err error, code ExitCode) error

WithCode tags err with an explicit exit code.

Types

type ExitCode

type ExitCode int

ExitCode is the process exit status. The contract is global: every command uses these and only these, so a caller can branch on the code without knowing which command produced it.

const (
	// ExitOK means the requested action was taken.
	ExitOK ExitCode = 0
	// ExitFailure is a runtime failure: network, I/O, lock contention, token refresh.
	ExitFailure ExitCode = 1
	// ExitUsage is reserved exclusively for usage errors: a bad flag, a bad flag
	// combination, an unknown account reference, a missing argument. Keeping it
	// exclusive is what lets a cron job tell a typo from a no-op.
	ExitUsage ExitCode = 2
	// ExitNothingToDo means the world is already how the caller asked for it.
	ExitNothingToDo ExitCode = 3
	// ExitBlocked means the action was wanted but no viable target exists, so
	// the caller should do something about it. Alert on this; ignore ExitNothingToDo.
	ExitBlocked ExitCode = 4
	// ExitProbeNegative is a negative answer to a probe, not a failure: no daemon
	// is running, nothing is attributable. It exists so a supervisor loop can tell
	// "no daemon" from "cannot determine", which is ExitFailure.
	ExitProbeNegative ExitCode = 5
	// ExitInterrupted is SIGINT.
	ExitInterrupted ExitCode = 130
)

func CodeFor

func CodeFor(err error) ExitCode

CodeFor maps an error to its exit code. An explicit tag wins; an interrupted store write is next; then a usage error; anything else is a runtime failure.

func Execute

func Execute() ExitCode

Execute builds the command tree, runs it, and returns the process exit code.

SIGINT is deliberately NOT trapped here. Trapping it process-wide removes its default terminating disposition for every command, and only the commands that actually watch the context can then do anything about it — which turned Ctrl-C into a no-op on `switch` waiting for a credential lock and on `add-token` blocked reading stdin, where the process had to be killed outright.

The refusal is about DURATION AND REACH, not about the mechanism, and three places in the tree hold SIGINT for a bounded span on exactly that reading. `add` holds it for its own blocking login, `auto` for its tick — both because the thing they are in the middle of is worse to abandon than to unwind — and internal/store holds it for the span of one transaction's write, because a process killed between the credential file and the document it belongs to leaves a live refresh token nothing on the machine can find. None of the three outlives what it is protecting, and none of them turns Ctrl-C into a no-op: each one stops, and each one exits 130.

Everywhere else — which is almost everywhere, since a store write is a handful of file operations — Ctrl-C keeps its default meaning, and the shell reports the same 130 without this binary being involved at all.

func ExecuteWith

func ExecuteWith(root *cobra.Command, errOut io.Writer) ExitCode

ExecuteWith runs an already-built root command and reports the exit code, writing any error to errOut. Execute is a thin wrapper over it so the error-to-exit-code mapping — the contract every command shares — is testable without touching os.Stderr or os.Args.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL