Publishing this toolkit
September 23, 2026 · View on GitHub
Maintainer document, public on purpose: a fork should be able to release under its own npm/PyPI names without reverse-engineering this layout. Nothing here is secret — no credentials, no private infrastructure, no deploy internals.
Three registry artifacts can be published from this repository, plus the
Claude Code plugin that is served straight from the git repo. All are versioned
in lockstep; tests/test_v03.py::TestVersionLockstep fails if they drift:
| Artifact | Registry | Command |
|---|---|---|
macos-computer-use-kit | PyPI | python -m build && twine upload dist/* |
pi-macos-computer-use | npm | cd packages/pi && npm publish |
dsh-macos-computer-use | npm | cd packages/dsh && npm publish |
macos-computer-use plugin | git (Claude Code marketplace) | push to main; users run /plugin marketplace add Sur-Cai/macos-computer-use-kit |
Before the first publish
-
Never commit a key. The Jev key lives in
~/.config/typesafe/api_keyand the environment. Checkgit statusandgit diff --cachedbefore pushing;.gitignorealready excludesnode_modules/, build output, and*.png. -
Bump every version together. The manifests are independent:
pyproject.toml,src/macos_computer_use/__init__.py,packages/pi/package.json,packages/dsh/package.json,plugins/claude-code/.claude-plugin/plugin.jsonand the plugin entry in.claude-plugin/marketplace.json. The skill copies are refreshed withscripts/sync-skill.sh. -
Add GitHub topics for discoverability (Repository → About → Topics):
mcp mcp-server claude-code computer-use macos accessibility ai-agents agent-skills gui-automation desktop-automation ocr dsh-plugin pi-package opencodedsh-pluginis how https://github.com/topics/dsh-plugin indexes plugins; thepi-packagenpm keyword is what https://pi.dev/packages indexes.
PyPI (the CLI)
python -m pip install --upgrade build twine
python -m build # sdist + wheel in dist/
twine check dist/*
TWINE_USERNAME=__token__ TWINE_PASSWORD="$PYPI_TOKEN" twine upload dist/*
Verify the install path in a clean venv before tagging:
python -m venv /tmp/verify && /tmp/verify/bin/pip install dist/*.whl
/tmp/verify/bin/macos-cu doctor
npm: the pi package
cd packages/pi
npm run typecheck
npm pack --dry-run # confirm the tarball contents
npm publish # unscoped; use --access public inside a scope
The pi manifest (extensions, skills) plus the pi-package keyword are what
make the catalog pick it up. The extension is loaded through pi's TypeScript
loader, so it ships sources — no build step, and peerDependencies stay "*".
npm: the dsh bundle
cd packages/dsh
npm run build # emits lib/ ; `prepare` runs this on git installs
npm pack --dry-run
npm publish
The bundle contract is dsh.bundle.patch → cordis.patch.yml. Because git
installs fetch sources, the prepare script must stay self-contained (no
monorepo project references); publishing a tarball or npm package ships the
built lib/ and needs no build permission from the user.
Users install with:
dsh plugin --profile <name> add dsh-macos-computer-use # from npm (recommended)
dsh plugin --profile <name> add ./dsh-macos-computer-use-<version>.tgz # from a packed tarball
The npm and tarball forms ship the prebuilt lib/, so no build permission is
needed. Installing straight from git fetches sources and runs prepare, which
pnpm blocks until the user allowlists it — and because the bundle lives in
packages/dsh/ of this monorepo, the git form is only convenient for a repo
that root-mounts the package. Prefer npm or the tarball for distribution.
Catalog listings
-
pi: publishing to npm is enough — the gallery at https://pi.dev/packages lists packages tagged
pi-package. This is confirmed working: the page forpi-macos-computer-useappeared shortly after the npm publish. Optionally add api.imageorpi.videopreview topackages/pi/package.jsonfor a richer card. -
dsh: add the
dsh-pluginGitHub topic, then open a PR against awesome-dsh-plugin. Submissions are one YAML file per plugin underdata/plugins/— never edit the generated READMEs. For this monorepo the file isdata/plugins/Sur-Cai__macos-computer-use-kit--packages-dsh.yml, withurlpointing at.../tree/main/packages/dshandnamewritten asSur-Cai/macos-computer-use-kit#dsh(their convention isowner/repo#<basename>, e.g.#dsh-cortexforworkspace/plugins/dsh-cortex— not#packages/dsh).Their CI enforces a 1-day minimum repository age (ours was created 2026-09-22T10:22Z, so the earliest valid submission is the next day at the same time) and at most 3 entries per PR. Descriptions are checked against the code, so keep numbers and tool names exact. There is also dsh-market for in-app discovery.
Release checklist
pytest -q
scripts/sync-skill.sh --check
claude plugin validate . && claude plugin validate plugins/claude-code
macos-cu mcp --list-tools # 18 tools
macos-cu doctor # permissions still granted after reinstalling
(cd packages/pi && npm ci && npm run typecheck)
(cd packages/dsh && npm ci && npm run typecheck && npm run build)
Tokens come from the environment for the one command that needs them
(TWINE_PASSWORD, NODE_AUTH_TOKEN via a temporary .npmrc outside the repo).
Never write them into the repository, and revoke any token that was pasted into
a chat or a log.
Then publish the artifacts and finish the release on GitHub:
twine upload dist/* # PyPI
(cd packages/pi && npm publish) # npm
(cd packages/dsh && npm publish)
git tag -a v<version> <release-commit> -m "<version>: ..."
git push origin v<version>
gh release create v<version> --title "<version>" --notes-file /tmp/notes.md --verify-tag
The release body is the CHANGELOG section for that version — extract it rather than writing it twice:
python3 - <<'PY'
import re, pathlib
v = "0.3.1" # <- version
text = pathlib.Path("CHANGELOG.md").read_text()
m = re.search(rf"^## {re.escape(v)}\n(.*?)(?=^## )", text, re.S | re.M)
pathlib.Path("/tmp/notes.md").write_text(f"## {v}\n{m.group(1)}".rstrip() + "\n")
PY
Tagging is not releasing. A version can be live on PyPI and npm while
gh release list still shows the previous one — the registries and the GitHub
Release are separate steps, and the release is the one that shows up on the repo
page and in watchers' feeds. Tag the release commit, not the follow-up docs
commit, so the tag and the published artifacts are the same source.
Current release state
| Artifact | Published | Notes |
|---|---|---|
macos-computer-use-kit | PyPI 0.3.1 | pip install macos-computer-use-kit |
pi-macos-computer-use | npm 0.3.1 | listed on pi.dev/packages |
dsh-macos-computer-use | npm 0.3.1 | 11 tools; awesome-dsh-plugin PR #5753 |
| Claude Code plugin | 0.3.1 | served from this repository |
Discoverability keywords are part of the release, not an edit: npm and PyPI
metadata is immutable per version, so adding jev / typesafe-ai /
system-one-models required cutting 0.2.2. Keep that in mind when tweaking
keywords — batch them with something else.
Known follow-ups: the awesome-dsh-plugin catalog PR is submitted (#5753); the gate's age check self-clears when the repo crosses 1 day (2026-09-23T10:22Z) — no resubmit needed per their own message.