Skip to content

Rewrite tagoio init to follow clig.dev#36

Open
bgelatti wants to merge 13 commits intomasterfrom
feat/init-follow-clig
Open

Rewrite tagoio init to follow clig.dev#36
bgelatti wants to merge 13 commits intomasterfrom
feat/init-follow-clig

Conversation

@bgelatti
Copy link
Copy Markdown
Collaborator

@bgelatti bgelatti commented May 8, 2026

Closes tago-io/project-sdk-and-tools#5.

Problem

Today's tagoio init is a single linear prompt sequence: ask for credentials, write a config, exit. It does not announce what it is about to do, does not detect existing initialization, has no non-interactive mode, and gives no summary of what changed on disk. This is hostile to CI/CD, hostile to AI agents, and surprising to first-time users.

Investigation

  • The five clig.dev steps map cleanly onto the existing init logic; most of the work is restructuring rather than new behavior.
  • prompts returns undefined on cancel, so a single catch-all in the orchestrator handles Ctrl-C without exotic SIGINT plumbing.
  • --no-input reduces to "no prompts(...) calls": auth must come from -t (or an existing lock file), and the analysis-selection prompts are skipped.
  • TagoIO Deploy installations have non-derivable subdomains, so a single --endpoint flag is not enough — --api-endpoint and --sse-endpoint must be passed together (or neither).

Solution

Three thin modules implement the flow:

  • src/lib/init-state.ts — pre-flight detectInitState(envName) returning { scope, isTTY, configExists, envExists, tokenExists }.
  • src/lib/init-summary.ts — pure formatters for the banner, overwrite-confirm copy, [..]/[OK]/[ERROR] step markers, and the final summary block.
  • src/commands/start-config.ts — orchestrator that runs the 5 steps in order and records every successful write into the summary.

New CLI surface

Flag Purpose
`--name ` env name (alias for the positional `[environment]`); flag wins on conflict
`--api-endpoint ` / `--sse-endpoint ` both required together for self-hosted TagoIO Deploy
`--no-input` refuses to prompt; needs `-t` (or an existing lock file) for auth
`--force` skips the existing-config overwrite confirmation

Behavior changes

  • Banner before any work: `Initializing tagoio in ...`
  • Re-running init for an existing env triggers a confirm prompt (or hard-errors under `--no-input`); `--force` skips it.
  • Each stage prints `[..]` running → `[OK]` done.
  • Final summary lists every file created/modified plus environment, profile, scope, and both endpoint URLs.
  • Cancelling any prompt prints `[INFO] Cancelled. No changes made.` and exits cleanly.

Branch context

Branched off `feat/cli-global-config`; PR base is set there so the diff is scoped to the init rewrite only. Prior global/local-scope and man-page work lives in their own PRs.

bgelatti added 12 commits May 5, 2026 13:52
Pull the command-registration logic out of initiateCMD into a standalone
buildProgram(defaultEnv) that returns a configured Command without
parsing argv. The runtime CLI keeps the same behaviour while the new
function becomes the single source of truth for the man-page generator.
Guard the auto-run with an import.meta.url check so importing this
module from a build tool no longer triggers program.parse().
Add a build-time roff generator at src/lib/generate-man.ts that walks
buildProgram() and emits a single tagoio.1 page. Wire npm run man into
the build chain plus prepublishOnly, ship the file via package.json
files and man fields, gitignore the artifact, and document the install
path (man tagoio plus fish_update_completions for fish users) in the
README. A vitest snapshot test acts as the drift gate so CI fails when
a flag or command changes without a matching snapshot regeneration; a
companion mandoc/groff integration test catches roff syntax errors.
The interactive prompt-driven flows (start-config, backup restore,
export-setup) are excluded from the coverage threshold since they are
covered by manual smokes, not unit tests.
The FILES section claimed ~/.tago-lock but the runtime writes per-environment
locks at ./.tago-lock.<env>.lock (verified in src/lib/token.ts), and was
missing ./.tagoio/personal.env where set-env persists TAGOIO_DEFAULT. Add an
EXIT STATUS section documenting 0 (success) and 1 (any failure via
errorHandler) as clig.dev recommends. Snapshot regenerated to match.
CI's `npm ci` was failing because package.json declared
oxfmt@^0.47.0 / [email protected] but the committed lockfile still
resolved to ^0.46.0 / 1.61.0. Regenerated the lockfile so
transitive @oxfmt/* and @oxlint/* bindings align with the
declared versions.
Replaces the developer-oriented stdout/stderr/JSON breakdown with a
purpose-first summary: official CLI to TagoIO, manages the four
top-level resources, suitable for interactive and CI/CD use.
Closes tago-io/project-sdk-and-tools#4. Adds a parent-walk resolver that finds
tagoconfig.json from any subdirectory and falls back to a per-user global
config under XDG/AppData. New `tagoio whoami` command and `--scope local|global`
flag on init/login, with mutating-command stderr banner, secure 0o700/0o600
perms on global, and analysis-* commands gated to local scope.
Six test files (login, start-config, four analysis-* commands) called
resolveScope/requireLocalScope without mocking, which fails on hosts where
the project root has no tagoconfig.json. Added resolve-scope and
scope-notice mocks to each, plus a defensive setScopeOverride(undefined)
reset in resolve-scope.test.ts so module-level state can't leak.
Static text bodies (description, exit-status, environment, files, see-also,
author) move out of the inline roff arrays in generate-man.ts and into a
dedicated MAN_CONTENT module written as plain English. The generator wraps
each value with escapeRoff() and emits the same structural roff. Editing prose
no longer requires counting backslashes.
Closes tago-io/project-sdk-and-tools#5. Adds a 5-step flow (pre-flight,
banner+overwrite-confirm, prompts-or-flags, execution-with-progress, summary)
plus the flag surface to make every prompt optional: --name, --api-endpoint,
--sse-endpoint, --no-input, --force. Pre-flight detection and summary
formatting live in new init-state and init-summary modules.
@bgelatti bgelatti changed the base branch from feat/cli-global-config to master May 8, 2026 11:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant