CLI Reference
Every hams command fits the same shape:
hams [global-flags] <command> [args] [--hams-flags] [-- <passthrough>]Three rules cover most confusion:
- Global flags sit between
hamsand the command name. Put them anywhere else and they stop being global. --hams-flags belong to hams. They never reach the underlying tool.- Anything after
--is handed straight to the underlying tool, untouched.
Commands by category
Core commands
| Command | What it does |
|---|---|
hams apply | Reconcile your machine with the Hamsfiles (install/remove/update) |
hams refresh | Re-scan machine reality and update .state/. Doesn’t touch anything else |
hams list | List everything hams is managing |
hams config | Read/write hams’s own config |
hams store | Manage the store repo (init, clone, push, pull) |
hams self-upgrade | Upgrade the hams binary |
Provider commands
Provider commands all follow hams <provider> <verb> [args]. The verbs map to whatever the
underlying tool exposes:
hams brew install htop
hams pnpm add serve
hams npm install --global prettier
hams brew remove ripgrep
hams brew list
hams brew enrich htopThe verb rulebook lives in Provider Commands. Per-provider specifics live in the Providers chapter.
Global flags
| Flag | Type | Default | What it does |
|---|---|---|---|
--debug | bool | false | Turn on debug logging (great for figuring out what a provider is actually doing) |
--dry-run | bool | false | Print the plan, don’t do the work |
--json | bool | false | Machine-readable output, for scripts and AI agents |
--no-color | bool | false | Kill ANSI colors (for file redirects, plain terminals) |
--config=<path> | string | ~/.config/hams/hams.config.yaml | Use a different config file |
--store=<path> | string | from config | Override the store directory |
--profile=<tag> | string | from config | Switch profile for this run |
--help | bool | false | Print help |
--version | bool | false | Print version info |
Full details in Global Flags.
Exit codes
| Code | Meaning |
|---|---|
0 | Success |
1 | Generic error |
2 | Bad usage (wrong flags, missing args) |
3 | Lock conflict — another hams process is already running |
4 | Partial failure — some resources made it, some didn’t |
10 | Needed sudo, didn’t get it |
11–19 | Provider-specific errors |
126 | Command not executable (POSIX) |
127 | Command not found (POSIX) |
Errors that scripts and agents can read
Every error is structured output by design. Plain mode looks like this:
ERROR LOCK_CONFLICT: Another hams session is running.
PID: 42567 command: apply started: 20260412T143022
Suggestions:
- Wait for the existing session to complete
- Run: kill 42567 (if the process is stuck)With --json:
{
"code": "LOCK_CONFLICT",
"message": "Another hams session is running. PID: 42567, command: apply, started: 20260412T143022.",
"suggestions": [
"Wait for the existing session to complete",
"kill 42567"
]
}The --hams- prefix is how you pass hams-specific flags in the same command as flags for the
wrapped tool. Example: hams brew install htop --hams-tag=cli records htop tagged cli, while
brew has no idea that flag ever existed.
--hams- flags at a glance
| Flag | What it does |
|---|---|
--hams-tag=<tags> | Attach tags to the resource. Comma-separated |
--hams-intro=<text> | One-line description saved into the Hamsfile |
--hams-local | Write to <Provider>.hams.local.yaml (gitignored) instead of the main Hamsfile |
# Install ripgrep tagged 'search,cli' with a description
hams brew install ripgrep --hams-tag=search,cli --hams-intro="Fast recursive grep alternative"
# Install a tool that's only for this machine, not the shared store
hams brew install zoom --hams-localLast updated on