Help us shape the future of Go! Take the 2026 Go Developer Survey

hoist

module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2026 License: Apache-2.0

README ยถ

hoist

๐Ÿค– AI Agents: read AGENTS.md instead. This README is for humans.

A terminal UI that promotes container images between environments in an Argo CD GitOps repo, and drives the whole path from edit to rollout.

hoist reads a repo laid out as <apps-root>/<env>/<family>/*.yaml, shows every environment's images side by side, and moves an environment's image set to the next environment as a block โ€” staging โ†’ production for web, worker, queue and friends in one reviewable PR. It can also write one fresh build straight from the registry into a single environment, or roll an environment's Deployments without changing anything they declare.

The interesting part is what happens after you press enter: hoist commits to a worktree, opens the PR, waits for CI, waits for a person to comment hoist approve <id> (production only, by default), squash-merges, asks Argo CD to refresh, and follows the rollout โ€” telling you at every step what it is doing and what it is waiting for. Kill it once it is under way and hoist resume <id> (or hoist resume --env <target>) picks up where the world actually is, not where a log file says it was. (The one narrow exception is the first second or so before its state file exists: a process killed there leaves a claim file naming the target env, and the next attempt tells you where it is so you can delete it.)

What it looks like

The matrix is the entry screen: one row per family, one column per environment, every cell the reference that environment declares and its state as a word. Under it, whatever is promoting right now โ€” re-observed against GitHub and the cluster, not read from a log โ€” with the one thing that is yours to do.

โ•ญโ”€ hoist ยท matrix ยท my-gitops โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚my-gitops ยท base main                                                         โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚
โ”‚ FAMILY      โ”‚ โ–ธ APP-STAGING                 โ”‚ APP-PRODUCTION โš                โ”‚
โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚
โ”‚ marketing   โ”‚ sha256:aaaaaaaaaaaa    pinned โ”‚ sha256:aaaaaaaaaaaa    pinned  โ”‚
โ”‚ โ–ธ orders    โ”‚ v202602201200          pinned โ”‚ v202601151010          pinned  โ”‚
โ”‚ temporal    โ”‚ 2 images             external โ”‚ 2 images             external  โ”‚
โ”‚ web         โ”‚ 2 versions              split โ”‚ v202601010101          pinned  โ”‚
โ”‚ worker      โ”‚ v3                   unpinned โ”‚ v2                   unpinned  โ”‚
โ”‚             โ”‚                               โ”‚                                โ”‚
โ”‚             โ”‚                               โ”‚                                โ”‚
โ”‚             โ”‚                               โ”‚                                โ”‚
โ”‚             โ”‚                               โ”‚                                โ”‚
โ”‚             โ”‚                               โ”‚                                โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚in flight ยท 1                                                                 โ”‚
โ”‚  5pr6sd333t   app-staging โ†’ app-production   started 12m ago                 โ”‚
โ”‚โœ“ branch  โœ“ commit  โœ“ push  โœ“ PR #103  โœ“ CI  โธ approval  ยท merge              โ”‚
โ”‚ยท argo refresh  ยท argo sync  ยท rollout                                        โ”‚
โ”‚waiting for an approver to comment `hoist approve 5pr6sd333t` on PR #103      โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
  enter actions ยท p promote into ยท t tag ยท w watch ยท r refresh ยท q quit ยท ? more

(Copied verbatim from testdata/golden/matrix-mockup-80x24.txt, built with realistic names for docs; a real repo's columns are its Argo destination namespaces, and a production column is marked โš . See the guide for every key.)

Before any write, the confirm screen leads with what is being shipped โ€” the commits between the build an environment declares and the one about to be written, and which of them migrate the database โ€” with the YAML diff one key away:

โ•ญโ”€ hoist ยท deploy ยท confirm โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ghcr.io/example/app:v3   โ†’   app-production          mode: PR ยท shift+d directโ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚rolling out 14 commits ยท 2 migrations ยท replacing v1, declared 4 weeks        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  4a1c2ef  Add rate limiting to the public API                                โ”‚
โ”‚  e9b0d31  Fix N+1 query when resolving digests                               โ”‚
โ”‚  77c0ffe  db: add index on events.created_at  migration                      โ”‚
โ”‚  1b2d3e4  Bump temporal SDK to 1.31                                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚2 migrations run on this deploy:                                              โ”‚
โ”‚  db/migrate/20260225T101500_add_events_created_at_index.rb                   โ”‚
โ”‚  db/migrate/20260301T090200_backfill_events_tenant_id.rb                     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚writes 3 image references in 1 file ยท digest 333333333333      d  see the yamlโ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
      enter deploy ยท d yaml ยท shift+d direct ยท โ†‘/โ†“ ยท r fresh ยท esc back ยท ? more

(Abridged from testdata/golden/deploy-mockup-80x24.txt โ€” commit rows and the footer are trimmed for the page; the screen's own golden test is the byte-exact version.)

Install

go install github.com/abradner/hoist/cmd/hoist@latest

@latest builds the newest tagged release โ€” v0.1.0 today, pre-alpha; main moves faster than tags do (a merge is not a release), so go install github.com/abradner/hoist/cmd/hoist@main is the way to get what's actually landed if you want to track development rather than the tag. Prebuilt binaries for macOS and Linux, amd64 and arm64, are attached to every release with a checksums.txt; hoist --version names what you are running. git on PATH and gh auth status logged in is the whole prerequisite: hoist runs git directly, and reaches GitHub through go-gh, which reads your gh login (an environment token first if you have set one, then gh's config, then gh itself for a token kept in the system keyring โ€” the one case where gh has to be on PATH at runtime too).

The three operations

Every write hoist makes is one of these. The user guide walks each screen and key, says what to do when a promotion stops, and covers resuming, registry credentials and direct mode. Each is a subcommand and a key on the matrix, with the same gates either way, and each has a read-only form that prints what would happen and touches nothing: plan for a promotion, --dry-run on deploy and restart.

Promote a pair โ€” copy what one environment runs into the next, every first-party image the target already carries (the promotable prefixes; anything else is listed as untouched):

hoist plan --from app-staging --to app-production --dry-run   # the diff, nothing written
hoist promote --from app-staging --to app-production           # branch, commit, push, PR, CI โ€ฆ

promote resolves each image to a digest โ€” by default what the source environment's pods are running, else the manifest's own pin, else the registry, in the order digest_sources gives โ€” rewrites only the image lines in the target environment, and opens one PR carrying all of them. Then it waits: for CI, for hoist approve <id> on the PR when the target is production, for the merge, for Argo to sync, for every Deployment it touched to roll out. On the matrix this is p.

Deploy a build โ€” write one named image into one environment, through the same pipeline:

hoist deploy --env app-staging --image ghcr.io/me/web:v3@sha256:โ€ฆ

On the plan screen e does the same job for one repo inside a promotion โ€” the TUI's --digest, overriding what the resolver chose for the row under the cursor. Which sources the resolver asks, and which registry credentials it tries, can be set for a whole session with the root --digest-sources, --registry-auth, --cluster-secret and --op-ref flags.

The reference must carry its digest; hoist will not resolve a bare tag on the way in. On the matrix this is t: a picker lists the registry's tags with the build age of each, whether the paired staging environment has committed it, and the commits and migrations between the build the environment declares and the one under the cursor. Releases lead the list; tags named after a digest and moving tags like latest or a branch name sit below their own dividers, so a handful of real builds are not buried among them โ€” nothing is hidden, and / filters across all three.

Restart a family โ€” roll an environment's Deployments, StatefulSets and DaemonSets without changing what they declare:

hoist restart --env app-staging --family web

This is the one operation that writes to the cluster instead of to git: it stamps the same annotation kubectl rollout restart does, so there is no branch, no PR and nothing to resume. Before rolling anything it names every target with its replica count and strategy and warns where the restart will not be graceful. On the matrix this is shift+r โ€” shift, because it's a write (a legacy terminal can't tell a real shift from caps lock, so hoist accepts a bare capital there too, charitably; only a kitty-protocol terminal, which can tell the difference, rejects caps lock pressed alone with no real shift).

Rules hoist enforces

When hoist refuses to do something, it is one of these. They are enforced in code, not by convention, and the refusal says which one:

  • Production always goes through a PR. Any env listed under envs.production is refused direct mode outright, whatever a flag or a keypress asked for.
  • Production waits for a person โ€” a hoist approve <id> comment on the PR โ€” unless that env is explicitly set to approval: auto.
  • Nothing written is a bare tag. Every image reference hoist writes is tag@sha256:โ€ฆ, and a tag with no digest is refused rather than resolved on the fly. The tag picker will not let you select a build whose digest it could not read, for the same reason.
  • A direct commit takes the env's name twice โ€” --confirm-direct=<env> at the CLI, a keypress and a confirmation in the TUI โ€” so a write with no PR is never one flag or one key.
  • A restart changes no declared reference, and warns where it may still not be a no-op: an unpinned tag can pull a different build when the replacement pod lands. Restarting production takes --confirm-production=<env>.
  • One promotion per target environment until it lands. While an env's promotion is still before its merge (or its direct push), a second one for that env is refused and named; hoist promotions lists them and hoist resume <id> continues one. Once the change has landed the guard lifts, even if Argo is still converging.

Three read-only screens sit beside them, each also a subcommand: w on the matrix watches one Application converge outside any promotion (hoist watch --app), c shows the effective config and the file it came from (hoist config show/path), and the in-flight pane under the matrix lists what is promoting right now (hoist promotions), with tab then enter reopening one and o opening its PR. On that reopened flight screen, shift+c is the one override the TUI offers: when your ci.none policy is prompt and a PR reports no checks at all, it treats them as green for that promotion after a confirmation โ€” the same thing --override-ci-none does at the CLI, and never a default.

These, by contrast, are warnings and never refusals: a digest the source's pods and manifest disagree on, a promotion that jumps straight to production, a build staging has never committed, a migration in the delta, a restart that will not be graceful. hoist says so and lets you decide.

What hoist is not

It is not a controller, an operator, or a platform. It runs on a laptop, holds no state a person cannot delete (~/.local/state/hoist/, an index of where to look, never a record of what happened), and stops when you close it โ€” a promotion it started is a branch, a PR and Argo doing what Argo does, all of which are there whether hoist is running or not. It is not Kargo or Argo Rollouts: it drives your GitOps repo through your PR review and lets Argo deploy, rather than replacing either.

Configuration

hoist reads $XDG_CONFIG_HOME/hoist/config.yaml (usually ~/.config/hoist/config.yaml). Nothing cluster-specific lives in the GitOps repo itself. A minimal example:

repos:
  - path: ~/src/my-gitops
    github: me/my-gitops
    apps_root: cluster/apps
    envs:
      production: [app-production]
      pairs: { app-staging: app-production }
    approvers: [me]
    kube: { context: my-cluster }
    apps: { ghcr.io/me/web: me/web }        # image repo โ†’ app repo, for commit history
registries:
  - prefix: ghcr.io/me/
    auth: [env, keychain]

docs/config.example.yaml is the annotated schema: every key, its default, and why. Unknown keys are errors, and hoist config show prints the effective config with defaults filled in.

License

Apache-2.0. See LICENSE.

Directories ยถ

Path Synopsis
cmd
hoist command
Command hoist is a terminal UI that promotes container images between environments in an Argo CD GitOps repository and follows the change through PR, merge and rollout.
Command hoist is a terminal UI that promotes container images between environments in an Argo CD GitOps repository and follows the change through PR, merge and rollout.
internal
app
Package app is the root Bubble Tea model for the hoist TUI.
Package app is the root Bubble Tea model for the hoist TUI.
app/activity
Package activity is the root's own record of what has happened this session โ€” every promotion started, landed, blocked or failed, every abandon, every browser-launch outcome โ€” kept as an append-only, capped Log (AGENTS.md ยง4.8) rather than the single transient "notice" string app.Model carried before this package existed.
Package activity is the root's own record of what has happened this session โ€” every promotion started, landed, blocked or failed, every abandon, every browser-launch outcome โ€” kept as an append-only, capped Log (AGENTS.md ยง4.8) rather than the single transient "notice" string app.Model carried before this package existed.
app/config
Package config is the read-only config screen (#104): what `hoist config show` and `hoist config path` print, on the TUI.
Package config is the read-only config screen (#104): what `hoist config show` and `hoist config path` print, on the TUI.
app/deploy
Package deploy is the confirm screen for writing one named image into one env โ€” the "image bump" half of hoist's problem statement, reached with d on the matrix and a tag chosen in internal/app/tags.
Package deploy is the confirm screen for writing one named image into one env โ€” the "image bump" half of hoist's problem statement, reached with d on the matrix and a tag chosen in internal/app/tags.
app/flight
Package flight is the flight screen: shown once a promotion starts driving through engine.AllSteps (branch, commit, push, PR, CI green, approved, merged, then (M5) Argo refresh, Argo sync, rollout), it lists every step with a glyph for its current state, the active step's own human detail text, a stopwatch since the promotion started, and a togglable scrollback of PromotionState.History.
Package flight is the flight screen: shown once a promotion starts driving through engine.AllSteps (branch, commit, push, PR, CI green, approved, merged, then (M5) Argo refresh, Argo sync, rollout), it lists every step with a glyph for its current state, the active step's own human detail text, a stopwatch since the promotion started, and a togglable scrollback of PromotionState.History.
app/history
Package history holds the function types through which screens ask about commit history and migration deltas โ€” types only, no Bubble Tea, no adaptor construction.
Package history holds the function types through which screens ask about commit history and migration deltas โ€” types only, no Bubble Tea, no adaptor construction.
app/matrix
Package matrix is the env ร— family screen: one row per family, one column per env, the family's image tag in each cell with its state spelled out โ€” pinned, unpinned, split, external, drifted โ€” as a word an operator can read without a legend.
Package matrix is the env ร— family screen: one row per family, one column per env, the family's image tag in each cell with its state spelled out โ€” pinned, unpinned, split, external, drifted โ€” as a word an operator can read without a legend.
app/plan
Package plan is the plan/confirm screen: it runs discovery + digest resolution + gitops.BuildPlan for one source/target env pair, shows a tickable list of image repos on the left and, on the right, what the promotion ships for the repo under the cursor โ€” the commits between what the target declares and what the source resolved to, and the migrations among them (M10, #85 screen 04/12) โ€” with the unified diff one key away.
Package plan is the plan/confirm screen: it runs discovery + digest resolution + gitops.BuildPlan for one source/target env pair, shows a tickable list of image repos on the left and, on the right, what the promotion ships for the repo under the cursor โ€” the commits between what the target declares and what the source resolved to, and the migrations among them (M10, #85 screen 04/12) โ€” with the unified diff one key away.
app/restart
Package restart is the matrix's shift+r key: the screen that shows what a restart would roll, takes the confirmation, and follows the rollout.
Package restart is the matrix's shift+r key: the screen that shows what a restart would roll, takes the confirmation, and follows the rollout.
app/scope
Package scope stamps an async command's result with the screen instance that issued it, so Update can tell a request its own current instance is still waiting on from one an earlier instance of the same kind of screen never got to see land.
Package scope stamps an async command's result with the screen instance that issued it, so Update can tell a request its own current instance is still waiting on from one an earlier instance of the same kind of screen never got to see land.
app/session
Package session is the controller that owns every drive a promotion or deploy goes through once the operator confirms it: starting it (internal/service.Service.StartPromotion), resuming one already on disk (Resume), stepping it to completion one poll at a time, abandoning it, and keeping the matrix's in-flight pane listed โ€” all of it re-observed, never trusted from a screen's own memory (AGENTS.md ยง4.1).
Package session is the controller that owns every drive a promotion or deploy goes through once the operator confirms it: starting it (internal/service.Service.StartPromotion), resuming one already on disk (Resume), stepping it to completion one poll at a time, abandoning it, and keeping the matrix's in-flight pane listed โ€” all of it re-observed, never trusted from a screen's own memory (AGENTS.md ยง4.1).
app/tags
Package tags is the tag-picker screen: given one image repo, it lists the registry's own tags and, once each row's metadata loads, its created time and digest โ€” sorted per AGENTS.md's M6 brief invariant 3 (prefer the app repo's own git tags for ordering when the image repo is mapped; fall back to the registry's own Created metadata otherwise).
Package tags is the tag-picker screen: given one image repo, it lists the registry's own tags and, once each row's metadata loads, its created time and digest โ€” sorted per AGENTS.md's M6 brief invariant 3 (prefer the app repo's own git tags for ordering when the image repo is mapped; fall back to the registry's own Created metadata otherwise).
app/watch
Package watch is the matrix's w key: one Argo Application's sync/health/revision and the rollout state of every workload its family declares, re-read on a cadence โ€” the TUI face of `hoist watch --app <name>` (#101).
Package watch is the matrix's w key: one Argo Application's sync/health/revision and the rollout state of every workload its family declares, re-read on a cadence โ€” the TUI face of `hoist watch --app <name>` (#101).
app/writepath
Package writepath holds what the plan and deploy confirm screens share about the write they are about to make: the Mode values their StartMsgs carry and the diff both render.
Package writepath holds what the plan and deploy confirm screens share about the write they are about to make: the Mode values their StartMsgs carry and the diff both render.
config
Package config loads hoist's config file: $XDG_CONFIG_HOME/hoist/config.yaml, or ~/.config/hoist/config.yaml when XDG_CONFIG_HOME is unset โ€” the same rule on every platform, so the docs stay one sentence โ€” overridable with --config <path>.
Package config loads hoist's config file: $XDG_CONFIG_HOME/hoist/config.yaml, or ~/.config/hoist/config.yaml when XDG_CONFIG_HOME is unset โ€” the same rule on every platform, so the docs stay one sentence โ€” overridable with --config <path>.
engine
Package engine drives one promotion's four steps โ€” branch, commit, push, PR โ€” to completion, re-observing the remote before every action (AGENTS.md ยง4.1: "the world is the state").
Package engine drives one promotion's four steps โ€” branch, commit, push, PR โ€” to completion, re-observing the remote before every action (AGENTS.md ยง4.1: "the world is the state").
restart
Package restart is the shared core of `hoist restart` and the matrix's R key: what a restart targets, what is worth warning about before it runs, how the write is made safely, and how the rollout that follows is observed.
Package restart is the shared core of `hoist restart` and the matrix's R key: what a restart targets, what is worth warning about before it runs, how the write is made safely, and how the rollout that follows is observed.
service
Package service is the one use-case layer both cmd/hoist and the TUI call โ€” application logic that used to live only in package main, duplicated (and quietly diverging) between the CLI's flags-to-render path and the TUI's own wiring.
Package service is the one use-case layer both cmd/hoist and the TUI call โ€” application logic that used to live only in package main, duplicated (and quietly diverging) between the CLI's flags-to-render path and the TUI's own wiring.
ui
Package ui holds what every hoist screen shares: one Styles palette built from a light/dark flag, the frame and pane chrome every screen is drawn in (frame.go), the dialog compositor (dialog.go), relative-time wording (time.go) and the status-bar line helper.
Package ui holds what every hoist screen shares: one Styles palette built from a light/dark flag, the frame and pane chrome every screen is drawn in (frame.go), the dialog compositor (dialog.go), relative-time wording (time.go) and the status-bar line helper.
ui/confirm
Package confirm builds the standalone huh.Confirm every screen raises inside a ui.Dialog.
Package confirm builds the standalone huh.Confirm every screen raises inside a ui.Dialog.
ui/keys
Package keys is the one registry of the whole approved screen ร— key table (docs/audit/2026-09-ux-arch-audit.md, "Proposed keymap"), plus the two things every screen needs to act on it: a write-binding matcher that tells legacy-shift from caps lock (ยง9 entry 13), and the footer helper that lays out hints under ยง9 entry 10's "the frame owns every row" rule.
Package keys is the one registry of the whole approved screen ร— key table (docs/audit/2026-09-ux-arch-audit.md, "Proposed keymap"), plus the two things every screen needs to act on it: a write-binding matcher that tells legacy-shift from caps lock (ยง9 entry 13), and the footer helper that lays out hints under ยง9 entry 10's "the frame owns every row" rule.
ui/uitest
Package uitest is the one harness every screen's tests render through (AGENTS.md ยง4.8, M10): a golden comparison at a stated terminal size that also asserts the shape a terminal would actually show โ€” exactly height lines, none wider than width โ€” and a way to drive a screen with real keypresses, so a test can never pass by setting the field a key would have set.
Package uitest is the one harness every screen's tests render through (AGENTS.md ยง4.8, M10): a golden comparison at a stated terminal size that also asserts the shape a terminal would actually show โ€” exactly height lines, none wider than width โ€” and a way to drive a screen with real keypresses, so a test can never pass by setting the field a key would have set.
pkg
argo
Package argo drives Argo CD entirely through the Kubernetes API: the dynamic client against the Application custom resource (argoproj.io/v1alpha1), never Argo's own REST/gRPC server and never an Argo API token (AGENTS.md ยง4.7, docs/repo-map.md's Kubernetes API row).
Package argo drives Argo CD entirely through the Kubernetes API: the dynamic client against the Application custom resource (argoproj.io/v1alpha1), never Argo's own REST/gRPC server and never an Argo API token (AGENTS.md ยง4.7, docs/repo-map.md's Kubernetes API row).
forge
Package forge is the code-host boundary (GitHub today; AGENTS.md ยง11 leaves GitLab as an interface with no adaptor until a GitLab repo needs one).
Package forge is the code-host boundary (GitHub today; AGENTS.md ยง11 leaves GitLab as an interface with no adaptor until a GitLab repo needs one).
forge/github
Package github implements pkg/forge.Forge against the real GitHub REST API, using github.com/cli/go-gh/v2 (AGENTS.md ยง4.7's one sanctioned new dependency for M3 โ€” not google/go-github, not a hand-rolled REST client, not the full gh CLI as a library).
Package github implements pkg/forge.Forge against the real GitHub REST API, using github.com/cli/go-gh/v2 (AGENTS.md ยง4.7's one sanctioned new dependency for M3 โ€” not google/go-github, not a hand-rolled REST client, not the full gh CLI as a library).
git
Package git drives the git binary โ€” never go-git (AGENTS.md ยง4.6) โ€” so that a commit made on the user's behalf inherits the user's own signing configuration (gpg.format=ssh, user.signingkey, 1Password's gpg.ssh.program, includeIf) exactly as if the user had typed the command themselves.
Package git drives the git binary โ€” never go-git (AGENTS.md ยง4.6) โ€” so that a commit made on the user's behalf inherits the user's own signing configuration (gpg.format=ssh, user.signingkey, 1Password's gpg.ssh.program, includeIf) exactly as if the user had typed the command themselves.
gitops
Package gitops reads an Argo CD GitOps repository and plans, applies and verifies byte-minimal image promotions between its environments.
Package gitops reads an Argo CD GitOps repository and plans, applies and verifies byte-minimal image promotions between its environments.
image
Package image parses, prints and identifies container image references.
Package image parses, prints and identifies container image references.
k8s
Package k8s reads what a namespace is running and, opt-in, one image pull secret.
Package k8s reads what a namespace is running and, opt-in, one image pull secret.
migrate
Package migrate answers the question the confirm screens lead with: between the build an env is running and the build about to be written into it, which commits ship, and does any of them migrate the database (R-005 in docs/repo-map.md โ€” on the target repo every Application auto-syncs and the app entrypoint runs db:prepare, so a merge can migrate production as a side effect).
Package migrate answers the question the confirm screens lead with: between the build an env is running and the build about to be written into it, which commits ship, and does any of them migrate the database (R-005 in docs/repo-map.md โ€” on the target repo every Application auto-syncs and the app entrypoint runs db:prepare, so a merge can migrate production as a side effect).
redact
Package redact turns transport errors into messages that carry no address and no secret.
Package redact turns transport errors into messages that carry no address and no secret.
registry
Package registry resolves tags to digests and lists tags, behind an ordered, explicit credential chain.
Package registry resolves tags to digests and lists tags, behind an ordered, explicit credential chain.
resolve
Package resolve turns the source env's image occurrences into the pinned references a promotion writes, from what the env is running first, then its manifests, then the registry โ€” never a guess (AGENTS.md ยง4.2, principle 3).
Package resolve turns the source env's image occurrences into the pinned references a promotion writes, from what the env is running first, then its manifests, then the registry โ€” never a guess (AGENTS.md ยง4.2, principle 3).
rollout
Package rollout reads Deployment rollout completeness, and reports current Job/CronJob state, through the typed client-go clientset (k8s.io/api/apps/v1, k8s.io/api/batch/v1) โ€” no dynamic client needed, since these are stable, already-vendored Go types โ€” following the same client-go adaptor shape pkg/k8s established (cluster.go/kubeconfig.go/fake.go): a thin wrapper over kubernetes.Interface, redaction at every error boundary, a fake clientset for tests.
Package rollout reads Deployment rollout completeness, and reports current Job/CronJob state, through the typed client-go clientset (k8s.io/api/apps/v1, k8s.io/api/batch/v1) โ€” no dynamic client needed, since these are stable, already-vendored Go types โ€” following the same client-go adaptor shape pkg/k8s established (cluster.go/kubeconfig.go/fake.go): a thin wrapper over kubernetes.Interface, redaction at every error boundary, a fake clientset for tests.

Jump to

Keyboard shortcuts

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