WebCLI

Article · CLI Review

HubSpot CLI Review

52
WebCLI Score
Good structure and verb vocabulary; machine-readable output and quiet mode are the main gaps.
How scores work →

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.

#DimensionWhat is checkedPoints
1Help system-h/--help works at every level of the command tree; consistent renderer0–10
2--version--version and -V both present and correct0–10
3Verbose flag-v/--verbose intercepted and handled; not misrouted or silently dropped0–10
4Quiet flag-q/--quiet suppresses non-data output; safe for piping and CI0–10
5Output streamsErrors go to stderr; data goes to stdout; exit codes non-zero on failure0–10
6Machine-readable outputJSON (or equivalent) output available; safely pipeable to jq0–10
7Exit codesDistinct exit codes documented in --help; caller can branch without parsing text0–10
8Flag namingKebab-case flags; no camelCase leaks; short aliases consistent0–10
9Subcommand structureConsistent verb/noun or noun/verb pattern; predictable discovery0–10
10ComposabilityPipeable output, --dry-run, shell completion, or equivalent scripting options0–10

All reviewed CLIs are ranked on the CLI Rankings page.


Scorecard

Design RuleStatusNotes
-h / --helpPassWorks at every level of the command tree
--versionPassPrints 8.1.0 correctly
-V short version flagFail-V silently shows root help instead of version
-v / --verboseFail-v silently shows root help; no verbose mode exists anywhere
-q / --quietFailAbsent entirely; significant for CI/CD use
-d / --debugPassConsistently present with short flag on all operational commands
-c / --configPassPresent on all operational commands; accepts a config file path
-f / --forcePassPresent on deploy, cms upload, and other destructive operations
-o / --outputFail-o is repurposed as --overwrite on cms fetch; no output format flag exists
JSON outputFailNo --format json or --output json anywhere; all output is human-readable text
Errors → stderrPass[ERROR] messages go to stderr; exit code 1 on unknown args
Exit codes documentedFailNot documented in any --help output; only exit 0 and exit 1 observed
Subcommand structurePass<tool> <noun> <verb> pattern is clear and consistent; see analysis below
Verb vocabularyPasslist, create, add, delete, upload, fetch, watch, deploy, logs, init, migrate
Flag naming (kebab-case)PartialMost flags are kebab-case; --deployLatestBuild leaks camelCase into help text
Flag consistency across commandsPartial-a/--account and --use-env inconsistently present across subcommands
Examples in help outputPassPresent on most leaf commands with realistic, varied examples
hs completionPassShell completion script generation for bash/zsh
Interactive wizard designIntentional tradeoffSee analysis below
Product-specific nounsIntentional tradeoffSee analysis below
[BETA] labelingIntentional tradeoffSee 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:

VerbWhere usedGuide ranking
list / lsproject list, hubdb list, secret list, account list#6
createproject create, hubdb create, sandbox create#8
deletehubdb delete, sandbox delete, secret delete, account remove#9
logsproject logs#13
iniths init#14
uploadcms upload, filemanager uploadupload = run variant
fetchcms fetch, filemanager fetch, hubdb fetchpull variant, #5
watchcms watch, project watchrelated to logs/start
deployproject deployrun variant, #2
migrateconfig migrate, project migrate, app migrateupdate 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.

WebCLI Spec · Draft v0.1 · March 2026 · GitHub · Released under CC BY 4.0 · Authored by @chandler212