Article · CLI Review
HubSpot CLI Review
hs is HubSpot's official command-line interface for the HubSpot developer platform:
CMS, projects, serverless functions, secrets, sandboxes, HubDB tables, and more. Its v8.0.0
major release landed on February 9, 2026, with v8.1.0 following on March 3. The CLI has been
in continuous development since 2021 (v3.0.0) and now spans over 60 subcommands across 15
top-level command groups.
Its design goals are different from an agent-first CLI like gws. hs
is primarily a developer onboarding and project workflow tool, getting developers set up with
credentials, scaffolding projects, uploading and watching files, and deploying builds. Many of
its commands are explicitly interactive, prompting for input when arguments are omitted.
That context matters: scripting and agent patterns don't always apply to a tool that's
intentionally built around the interactive terminal session.
What this review is
An analysis of hs --help and subcommand help output for all 15 top-level
command groups against the
WebCLI design patterns
synthesized from git, bash, curl, npm, docker, pip, grep, ssh, wget, and make.
Scoring methodology ¶
Every WebCLI review assigns a score from 0 to 100. The score is calculated across ten standard dimensions, each worth 10 points. Pass = 10 pts, Partial = 5 pts, Fail = 0 pts. Intentional tradeoff = 5 pts; these are not penalized as failures, but full credit requires a full pass.
| # | Dimension | What is checked | Points |
|---|---|---|---|
| 1 | Help system | -h/--help works at every level of the command tree; consistent renderer | 0–10 |
| 2 | --version | --version and -V both present and correct | 0–10 |
| 3 | Verbose flag | -v/--verbose intercepted and handled; not misrouted or silently dropped | 0–10 |
| 4 | Quiet flag | -q/--quiet suppresses non-data output; safe for piping and CI | 0–10 |
| 5 | Output streams | Errors go to stderr; data goes to stdout; exit codes non-zero on failure | 0–10 |
| 6 | Machine-readable output | JSON (or equivalent) output available; safely pipeable to jq | 0–10 |
| 7 | Exit codes | Distinct exit codes documented in --help; caller can branch without parsing text | 0–10 |
| 8 | Flag naming | Kebab-case flags; no camelCase leaks; short aliases consistent | 0–10 |
| 9 | Subcommand structure | Consistent verb/noun or noun/verb pattern; predictable discovery | 0–10 |
| 10 | Composability | Pipeable output, --dry-run, shell completion, or equivalent scripting options | 0–10 |
All reviewed CLIs are ranked on the CLI Rankings page.
Scorecard ¶
| Design Rule | Status | Notes |
|---|---|---|
-h / --help | Pass | Works at every level of the command tree |
--version | Pass | Prints 8.1.0 correctly |
-V short version flag | Fail | -V silently shows root help instead of version |
-v / --verbose | Fail | -v silently shows root help; no verbose mode exists anywhere |
-q / --quiet | Fail | Absent entirely; significant for CI/CD use |
-d / --debug | Pass | Consistently present with short flag on all operational commands |
-c / --config | Pass | Present on all operational commands; accepts a config file path |
-f / --force | Pass | Present on deploy, cms upload, and other destructive operations |
-o / --output | Fail | -o is repurposed as --overwrite on cms fetch; no output format flag exists |
| JSON output | Fail | No --format json or --output json anywhere; all output is human-readable text |
| Errors → stderr | Pass | [ERROR] messages go to stderr; exit code 1 on unknown args |
| Exit codes documented | Fail | Not documented in any --help output; only exit 0 and exit 1 observed |
| Subcommand structure | Pass | <tool> <noun> <verb> pattern is clear and consistent; see analysis below |
| Verb vocabulary | Pass | list, create, add, delete, upload, fetch, watch, deploy, logs, init, migrate |
| Flag naming (kebab-case) | Partial | Most flags are kebab-case; --deployLatestBuild leaks camelCase into help text |
| Flag consistency across commands | Partial | -a/--account and --use-env inconsistently present across subcommands |
| Examples in help output | Pass | Present on most leaf commands with realistic, varied examples |
hs completion | Pass | Shell completion script generation for bash/zsh |
| Interactive wizard design | Intentional tradeoff | See analysis below |
| Product-specific nouns | Intentional tradeoff | See analysis below |
[BETA] labeling | Intentional tradeoff | See analysis below |
What it does well ¶
Help system
Both hs --help and hs -h work, and the pattern cascades through the
full command tree. hs project --help, hs project deploy --help,
hs cms watch --help: every level produces well-structured output using yargs's
consistent renderer. Unlike the gws CLI which uses two different help renderers,
hs uses a single format throughout: description, subcommands, positional
arguments, options, and examples, in that order.
Verb vocabulary
The verb choices across hs's subcommands closely match the high-saturation verbs
from the design guide:
| Verb | Where used | Guide ranking |
|---|---|---|
list / ls | project list, hubdb list, secret list, account list | #6 |
create | project create, hubdb create, sandbox create | #8 |
delete | hubdb delete, sandbox delete, secret delete, account remove | #9 |
logs | project logs | #13 |
init | hs init | #14 |
upload | cms upload, filemanager upload | upload = run variant |
fetch | cms fetch, filemanager fetch, hubdb fetch | pull variant, #5 |
watch | cms watch, project watch | related to logs/start |
deploy | project deploy | run variant, #2 |
migrate | config migrate, project migrate, app migrate | update variant, #10 |
The vocabulary is consistent with git/docker/npm patterns. An LLM generating
hs project list, hs secret delete, or hs cms fetch
will get it right on the first try.
-d / --debug consistency
Every operational command in hs exposes -d, --debug as a short
flag that sets the log level to debug. The short flag is consistent throughout; no
command uses --debug without the -d alias. This matches the
medium-confidence -d pattern from the design guide (pip, wget, docker) and
is one of the few flags applied uniformly across the entire CLI.
Examples in help output
Most leaf commands include an Examples: block with multiple realistic invocations.
hs project deploy --help shows three examples covering the common cases:
default deploy, targeting a specific build ID, and deploying with a profile. This is
better practice than most of the reference tools, which either omit examples entirely
or include only a single trivial one.
hs doctor
A dedicated diagnostic command that retrieves configuration information and, with
--output-dir, saves a detailed JSON diagnosis file. This is a pattern seen
in mature CLIs (cf. npm doctor) and is genuinely useful for an LLM or developer
troubleshooting a configuration problem has a single command to run rather than having to
piece together state from multiple sources.
hs completion
Shell completion script generation is documented and functional, with a working example
(hs completion >> ~/.zshrc) directly in the help output. The design guide
identifies shell completion as a composability feature. Having it as a named top-level
subcommand rather than buried in documentation means an LLM will predict
hs completion correctly.
Real problems ¶
These are not tradeoffs. They are gaps that conflict with hs's own stated
goals and with the most fundamental conventions of the reference set.
-v and -V are silently discarded
Running hs -v or hs -V silently prints the full root help
and exits 0, as if the flag was not provided at all. There is no verbose mode, no
error, and no indication that the flag was unrecognized:
$ hs -v
The command line interface to interact with HubSpot.
Commands:
hs get-started A step-by-step command to get you started...
...
Options:
--version Show version number [boolean]
-h, --help Show help [boolean]
The design guide rates -v/--verbose as "Very high" confidence;
one of the first flags an LLM will generate. Silently treating it as a help trigger
produces a subtle failure mode: a script that passes -v gets back help text
instead of verbose output or an error, with exit code 0. This is harder to debug than
an explicit unknown-flag error. At a minimum, -v should be intercepted and
return an error or be mapped to --debug.
--quiet is absent
There is no -q/--quiet flag anywhere in the CLI. For a tool
used in CI/CD pipelines, where hs project deploy runs as part of a build,
suppressing progress output and spinner text matters. The design guide rates quiet mode
8/10 across the reference set. Without it, stdout is not safe to pipe: running
hs project list | jq would fail anyway because there is no JSON output
(see below), but the absence of --quiet closes off any path to scripting
the tool cleanly.
No machine-readable output
There is no --format json, --output json, or any equivalent
on any command. All output is human-readable text. For hs project list,
hs secret list, hs account list, and every other data-returning
command, the output cannot be piped to jq or processed by a script without
screen-scraping.
This is the most significant gap in the CLI. The design guide identifies JSON output as a
"rapidly converging" pattern across the reference set (docker, npm, pip all support it).
An LLM will instinctively try
hs project list --output json or
hs project list --format json and get an "Unknown argument" error. For a
tool with CI/CD and automation use cases, the absence of structured output is a
fundamental composability failure.
hs custom-object --help is broken
Running hs custom-object --help, hs custom-object schema --help,
and hs custom-object schema update --help all produce identical output:
hs custom-object schema update
Options:
--version Show version number [boolean]
-h, --help Show help [boolean]
The command group listing is never shown, the command description is absent, and the
options shown (--version and --help only) suggest an
unimplemented stub rather than a command that was labeled [BETA].
This help output provides no information that would allow a user or LLM to understand
what the command does or how to use it.
camelCase flag leaks into help text
hs project deploy --help exposes:
--deployLatestBuild, --deploy-latest-build Deploy the latest build...
This is a yargs artifact: when a camelCase internal property name is used, yargs automatically generates both the camelCase and kebab-case variants and lists both in help. The result is a flag that violates the kebab-case convention and exposes implementation details to the user. The fix is to define the flag with a kebab-case name internally, or explicitly suppress the camelCase alias from help output.
Exit codes are undocumented
No --help output anywhere in the CLI documents exit codes. The only
observable behavior is exit 0 on success and exit 1 on an unrecognized argument.
Whether hs project deploy exits differently on an authentication failure
versus an API error versus a local validation error is unknown from the CLI's own
documentation. The design guide identifies documented exit codes as the basis for
reliable scripting: a calling system cannot branch on exit codes it does not know exist.
Intentional tradeoffs ¶
These patterns diverge from the design guide but for clear architectural reasons
given hs's primary audience and design goals.
Interactive wizard design
Many hs commands are designed to prompt interactively when required arguments
are omitted: hs auth, hs init, hs get-started,
hs project create, hs project logs all open interactive prompts.
From a scripting perspective this is a problem: a non-interactive invocation with
missing arguments hangs waiting for input rather than failing with a usage error. From a
developer onboarding perspective, it is exactly right. hs's primary surface
is the first-time developer setting up a HubSpot project, not a CI runner. The wizard
pattern makes onboarding much easier. The correct fix is not to remove the
wizards but to add a --no-interactive or --quiet flag that
forces non-interactive mode and fails fast when required arguments are absent.
Product-specific resource nouns
The top-level command groups use HubSpot product names (hubdb,
filemanager, cms, sandbox) rather than
generic terms. This is the right call. A developer who knows HubSpot's product
vocabulary will immediately understand what each command group manages. Replacing
hubdb with database or filemanager with
storage would reduce friction for new users at the cost of alienating
the existing HubSpot developer community that the CLI is primarily serving. The design
guide's verb vocabulary applies within each group (the verbs are standard); the
noun choices are product-appropriate.
--use-env for configuration
Several commands support --use-env as an alternative to --config,
signaling that configuration should be read from environment variables rather than a
config file. This is a reasonable complement to the standard config-file pattern. In
CI/CD environments where secrets are injected as environment variables, --use-env
makes the intent explicit rather than relying on config file precedence rules. Both
--config <file> and --use-env being available on the same
command gives operators flexibility without forcing a single pattern.
[BETA] labeling
hs sandbox and hs custom-object are labeled [BETA]
in the top-level help listing. This is correct practice: it sets user expectations,
signals that the CLI contract may change, and explains why the behavior may be rougher
than stable commands. The design guide does not address stability labels, but this is
consistent with how docker buildx and similar commands handle experimental
features. The label would be more meaningful if the custom-object help
output were not broken (see above).
Summary ¶
hs is a mature CLI with a clear audience and consistent internal conventions.
The verb vocabulary, help system, --debug flag discipline, and per-command
examples are all done well. The intentional tradeoffs around interactive wizards and
product-specific nouns are appropriate for a developer-facing onboarding tool.
The main problems are a short list but they matter. The silent discard of -v and
-V is a quiet failure mode that will confuse developers and LLMs alike. The
complete absence of JSON output makes the CLI composable only through screen-scraping,
which is untenable as HubSpot pushes deeper into automation and MCP tooling; the
hs mcp command group's own existence makes the gap harder to ignore. The
broken custom-object help and the undocumented exit codes are fixable in
a single release. The --quiet and JSON output gaps are architectural
additions that would make scripting and automation viable for what v8 appears to
be building toward.