cyborg-skills
v2.0.3
Published
Cyborg Skills CLI: publish, install, sync, and share AI agent skills.
Maintainers
Readme
{{npmPackage}}
The {{name}} Skills CLI: publish, install, sync, and share AI agent skills.
Renamed from @floomhq/skills
This package was @floomhq/skills with the floom-skills binary. The company
behind it is {{name}}, not {{company}}, so an installer that printed "Floom
Skills" on every line named the wrong product. @floomhq/[email protected] is the
last release under the old name and still works: it forwards in-process to
{{npmPackage}}. Every published @floomhq/skills version is deprecated on
npm with a notice naming {{npmPackage}}. Existing users switch with:
npm i -g {{npmPackage}}
npm uninstall -g @floomhq/skillsDocs: https://skills.floom.dev/docs
Install
npm i -g {{npmPackage}}The package is {{npmPackage}}. It puts the {{cliBinary}} command on your PATH.
Pinned no-install form for automation:
npx -y {{npmPackage}}@2.0.0 --helpLogin
{{cliBinary}} loginThe login command starts a browser device flow. Sign in at skills.floom.dev with
Google or email, approve the device code, and the CLI stores the session at
~/.floom/auth.json.
Check the active account:
{{cliBinary}} accountFirst Skill
{{cliBinary}} push ~/.claude/skills/my-skillThe folder must contain SKILL.md. Each push creates a Library version.
Create a starter skill when the folder does not exist yet:
{{cliBinary}} new my-skill --agent claudeBulk Sync
{{cliBinary}} syncZero-arg {{cliBinary}} sync detects installed agents and supported scopes, reviews
local-to-Library and Library-to-local differences, and applies the selected
plan. Use --dry-run to preview the plan without changing files or Library
records.
Library-to-local only:
{{cliBinary}} pull
{{cliBinary}} pull --agent claude,codexLocal-to-Library only:
{{cliBinary}} pushUse the installed {{cliBinary}} command for interactive terminal work. Use the pinned
npx -y {{npmPackage}}@2.0.0 <command> form for automation and agent-run
commands that need reproducible CLI behavior.
What an install reports, and how to turn it off
After an install from a catalog link succeeds — and only after files are on
disk — {{cliBinary}} posts one short message to the catalog it installed from:
| Field | What it is |
| --- | --- |
| slug | Which Skill. The catalog resolves the Skill's current release from it; this CLI cannot name a release. |
| ref | The tracking code the link carried, if it carried one ({{origin}}/skills/<slug>?ref=cyb_…). Absent otherwise. |
| agent | Which agent's skill directory was written to — claude, codex, cursor, gemini or opencode. Absent when several were. |
| installer | What this CLI is: {{npmPackage}}/<version>. |
That is the whole message. No path, no home directory, no username, no hostname, no machine identifier, no file count, no timing, nothing about anything else on the machine.
It is a claim, and it is stored and displayed as one. Nothing on the catalog's side can observe a write to your disk, so the row it creates is marked self-reported and unverified, permanently, and does not reach any public counter. A failed or refused report never fails an install and never prints an error: the install already succeeded.
Nothing is sent for a {{cliBinary}} install fls_… share link, which has no
catalog Skill behind it, and nothing is sent for a --dry-run.
To send nothing at all:
export {{wordmark}}_NO_INSTALL_REPORT=1It is read before any request is built, so setting it means no request is made
rather than one that is discarded. {{cliBinary}} --help and
{{cliBinary}} install --help both print it.
Releasing
Releases publish the exact smoke-tested npm tarball via
.github/workflows/publish-cli.yml, triggered by a skills-v* tag.
Provenance is disabled while the source repository remains private.
Hard pre-publish smoke gate
Every npm publish is gated by packages/cli/scripts/pre-publish-smoke.sh,
which packs the tarball, installs it into an isolated $HOME, and drives
every subcommand the way a real user would. If anything fails, the workflow
fails BEFORE npm publish runs — no broken CLI can reach npm latest.
Background: in 2026-05 we shipped 3.0.0 → 3.0.1 → 3.0.2 within hours
because 3.0.0 hit npm latest with three P0s that code review missed:
--help ran the destructive body, --help | head threw an unhandled EPIPE,
and unknown commands silently rendered the dashboard. Only an out-of-band
real-terminal smoke caught them. This script is that smoke, baked into the
publish pipeline.
Before pushing a skills-v* tag
Run the same local gates from the repo root:
pnpm --filter {{npmPackage}} --fail-if-no-match lint
pnpm --filter {{npmPackage}} --fail-if-no-match test
pnpm --filter {{npmPackage}} --fail-if-no-match pack:smoke
packages/cli/scripts/pre-publish-smoke.shIf anything fails, fix the issue first. Do not push the tag.
Smoke-only workflow run
To validate the smoke script itself without publishing (e.g. after editing
scripts/pre-publish-smoke.sh):
GitHub → Actions → Publish {{npmPackage}} → Run workflow
smoke_only: trueThe workflow will build, run the smoke, and stop. No npm publish, no
Homebrew dispatch.
Manual publish (rare)
To publish without a tag (e.g. retag an existing version under a different
dist-tag), use workflow_dispatch with smoke_only: false and the desired
tag input. The smoke gate still runs first.
What the pre-publish smoke checks
See scripts/pre-publish-smoke.sh for the full assertion list. Summary:
| Stage | Asserts |
|------|---------|
| 1 | Tarball builds, packs, installs cleanly into isolated $HOME; installed {{cliBinary}} --version matches package.json |
| 2 | Every subcommand --help exits 0, starts with Usage: {{cliBinary}}, writes no ~/.floom/auth.json, prints no "Signed (in|out)" / "Waiting for browser" (P0 #1 from 3.0.0) |
| 3 | {{cliBinary}} --help \| head -3 and {{cliBinary}} push --help \| head -1 print no EPIPE / "Unhandled error event" (P0 #2 from 3.0.0) |
| 4 | {{cliBinary}} <unknown> exits non-zero with "unknown command" + "did you mean" hint (P0 #3 from 3.0.0) |
| 5 | Bare {{cliBinary}} --help exits 0 with grouped help |
| 6 | Unauth whoami / account / logout exit 0 idempotent; unauth status / sync --json / push --json exit non-zero with no stack trace |
| 7 | {{cliBinary}} logout extra-positional is rejected with non-zero exit AND ~/.floom/auth.json is preserved (no destructive body) |
| 8 | Agent detection picks up fake .claude/, .codex/, .cursor/ marker dirs in the isolated $HOME (verified by parsing {{cliBinary}} doctor --json); bare {{cliBinary}} sync / {{cliBinary}} pull with planted agents do not crash unauth (clean signedIn:false envelope). Note: fan-out behaviour itself requires real auth, so it is exercised at the unit-test layer by src/commands/sync.fanout.test.ts, which the publish workflow runs as a separate hard gate before the smoke. |
If any assertion fails, the publish job fails. The smoke script exits non-zero on the first failure and prints a summary of every failed check.
Files
packages/cli/
├── src/ # TypeScript source
│ ├── index.ts # CLI entry point (commander)
│ ├── commands/ # one file per subcommand
│ ├── lib/ # shared helpers
│ └── cli-smoke.test.ts # unit-level smoke (P0 lock-ins)
├── scripts/
│ ├── build-bundle.mjs # esbuild → dist/index.js
│ ├── verify-package.mjs # pre-pack file allowlist check
│ └── pre-publish-smoke.sh # E2E smoke driven against tarball
├── package.json
└── CHANGELOG.md