Skip to content

feat(cli): add @humanjs/cli — demo and run commands - #118

Open
totigm wants to merge 1 commit into
mainfrom
feat/cli-package
Open

totigm wants to merge 1 commit into
mainfrom
feat/cli-package

Conversation

@totigm

@totigm totigm commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Evaluating HumanJS currently requires writing code first. This closes that gap:

npx @humanjs/cli demo https://your-site.com

A browser opens, lands, reads the heading, scrolls in stages, and drifts the cursor over a link — the way a person skims. Ten seconds, nothing to set up.

Two commands

demo <url> — the look-at-it command. run <script> — executes a HumanJS flow with the browser and Human already wired, so the file is only the flow:

export default async (human) => {
  await human.goto('https://example.com');
  await human.click('text=Sign in');
};

.ts runs directly through tsx, no build step. Both take --record <file> (extension picks the format, including .spec.ts for a committable Playwright test), --personality, --speed, --seed, --viewport, --headless.

npx humanjs will not work, and that changes the pitch

The unscoped humanjs name on npm is taken — an unrelated CLI from 2022, v1.0.1, last published June 2022. npx humanjs fetches that, not this. So the entry point is npx @humanjs/cli; installed globally, the binary is plain humanjs.

This is called out in the help text and the README because people will try it. If the shorter form matters, claiming the dormant name through npm's dispute process is a separate decision and yours to make.

Design notes

demo runs on other people's sites. Every step degrades instead of failing — no heading, nothing to scroll, no links each just means fewer steps. And it hovers, never clicks: it must not navigate away, submit a form, or fire a side effect on a site it was pointed at.

Selector lookups return locators already narrowed with .first(). The first real run against a live page hung: a bare a[href] matches dozens of elements, trips Playwright's strict mode, and waits out the timeout. Returning a narrowed locator rather than a selector string means a caller cannot reintroduce that.

The version is injected at build time via tsup's define. Reading package.json at runtime means resolving a path out of dist/, which differs between the ESM and CJS outputs — and a CLI that misreports its own version is a support ticket waiting to happen.

Arg parsing is hand-rolled and unit-tested. On a CLI the error messages are the interface, so every rejection names the bad value and lists what was expected. --viewport accepts 1440×900 as well as 1440x800, because that is what gets pasted from a design tool.

Package checklist (per CLAUDE.md)

version: 0.0.0 + publishConfig.access: public so the first changeset publishes 0.1.0; tsconfig and tsup config mirrored from packages/mcp; badge row on the README; regular dependencies rather than peers, since a npx-launched package has no host app to supply them; Packages table updated.

Verification

31 unit tests. Whole suite green with the package wired in: lint, typecheck (11/11), test (10/10), build (8/8), check:exports (15/15).

Smoke-tested from dist/: help, --version, a usage error (clean message, exit 2, no stack), and demo https://example.com --record t.gif --headless, which recorded 6 actions to a GIF.

Found while dogfooding

demo against https://humanjs.dev fails — but upstream, not here. A single transient Page.captureScreenshot protocol error permanently stops the capture loop in packages/playwright/src/recording/capture.ts, so zero frames are captured and the export dies with "No frames were captured". A dropped frame should not cost the whole recording. Fix coming in its own PR.

Evaluating HumanJS required writing code first. `humanjs demo <url>`
removes that: it drives any page the way a person skims it, so the
motion can be judged in ten seconds with nothing to set up.

`humanjs run <script>` wires the browser and the Human instance so a
script is only the flow. TypeScript runs directly through tsx.

Two things worth knowing:

The unscoped `humanjs` name on npm belongs to an unrelated 2022 package,
so `npx humanjs` does not reach this CLI -- it is `npx @humanjs/cli`.
The help text and README both say so, because people will try.

`demo` runs against pages nobody here has seen, so every step degrades
instead of failing, and it hovers rather than clicks -- it must not
navigate away or fire a side effect on someone else's site. Selector
lookups return locators already narrowed with .first(); handing a bare
'a[href]' to a primitive trips Playwright's strict mode and hangs, which
is exactly what happened the first time this was run for real.
@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
humanjs Ready Ready Preview Sep 1, 2026 1:28pm UTC

This branch was successfully deployed

1 active deployment
Preview — 092ce665 Deployed Sep 1, 2026 by vercel[bot]
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