A private Claude Code plugin marketplace — a single central registry so skills
don't have to be duplicated and fragmented across projects. Registering the
marketplace enables nothing; every plugin is defaultEnabled: false and is
opted into per project.
From the root of the repo you want to enable plugins in, run:
npx https://matlomax.com/claude-plugins.tgzThis installs from a prebuilt, dependency-free tarball attached to the latest
GitHub release (matlomax.com 302-redirects to it) — installing never touches
the npm registry. On start the installer checks whether a newer release
exists and, if so, relaunches itself at that version automatically, so you
always run current code without re-typing the command.
npx github:MatLomax/claude-plugins still works: it runs from main and
pulls @clack/prompts from the npm registry instead.
The interactive installer lets you tick which plugins to enable (all off by
default), then deep-merges .claude/settings.json in that repo — registering
the marketplace and enabling exactly what you picked, never clobbering existing
settings, safe to re-run. When you enable a binary-backed plugin (worklog,
mdtohtml) it checks the binary is on PATH and, if missing, offers to download
and install the latest release for your platform (SHA-256 verified) — no
language toolchain needed. For worklog it also offers to run worklog init so
the repo's task log is live rather than inert. It is interactive by default and
then needs a terminal; with --plugins it runs without prompts or a terminal
(see Non-interactive). At the end it reports what it
verified, then prints the claude plugin install commands for the plugins you
picked (see below). Then
reload Claude Code and accept the "trust this folder" dialog.
An installed binary lands in ~/.local/bin on macOS and Linux (on Linux,
mdtohtml is unpacked to ~/.local/share/mdtohtml and symlinked there; it has
no macOS release). The installer does not edit shell profiles: if that
directory is not on your PATH, the summary says so and shows the
export PATH="$HOME/.local/bin:$PATH" line to add to your shell profile. On
Windows it goes to %LOCALAPPDATA%\Programs\<tool>, which is appended to your
user PATH in the registry (keeping it a REG_EXPAND_SZ value, so %VAR%
entries survive), read back to confirm, and announced to running programs with
the same WM_SETTINGCHANGE broadcast setx sends. The summary reports whether
the directory was added, was already there, or could not be added (with the
reason and how to add it by hand). Programs that are already open keep their
old PATH: fully quit Claude Desktop (from the system tray, not just its
window) and close any open terminals and IDEs, then reopen them. Whenever a
binary was just installed, on any OS, the installer ends with a reminder to
restart Claude Code / Claude Desktop and open terminals.
If .claude/settings.json exists but is not valid JSON, is not a JSON object,
or has an enabledPlugins, extraKnownMarketplaces or
extraKnownMarketplaces.matlomax entry that is not a JSON object, the installer
stops (exit 1) before prompting or installing anything and leaves the file as it
is.
Flags:
--plugins=<name>[,<name>...]— non-interactive mode; see below.--install-tools— auto-install missing plugin binaries in non-interactive mode (requires--plugins).--no-worklog-init— skip the automaticworklog initin non-interactive mode (requires--plugins).--insecure— skip TLS certificate verification for the installer's own downloads (see troubleshooting below).--no-self-update— skip the check for a newer release and run this version as is (an escape hatch if a release is broken, or a way to run an older version from its versioned URL).--help(-h),--version— print and exit 0, whatever else is on the command line.
For scripts and CI, skip the picker entirely:
npx https://matlomax.com/claude-plugins.tgz --plugins=worklog,mdtohtml --install-tools--plugins=<name>[,<name>...] (may also be repeated) selects plugins without prompting and makes
the run non-interactive — no prompts, no terminal required. Valid names: worklog, mdtohtml,
image-to-html. Listed plugins are enabled in .claude/settings.json (and the marketplace
registered); plugins you don't list are left as they are — non-interactive mode only adds, it
never disables (unlike the interactive picker, which also turns off unticked plugins). At least
one name is required; an unknown name is an error (exit 2).
--install-tools downloads and installs the latest release for any listed binary-backed plugin
(worklog, mdtohtml) whose binary isn't on PATH — the same as answering yes in the interactive
prompt (SHA-256 verified when the release publishes a checksum; installed where described
above). Without it, a missing binary is only reported. When
worklog is listed and its binary ends up usable, worklog init runs automatically in the
current repo; --no-worklog-init skips that. --install-tools and --no-worklog-init only work
alongside --plugins — used without it, they're a usage error (exit 2).
Exit code is 1 if a listed binary-backed plugin ends up without a usable binary, if (on Windows)
a binary was installed but its directory could not be added to the user PATH (Claude Code would
not find it), or if worklog init ran and failed (settings are still written in every case, and
the reason is printed on stderr); 0 otherwise. Usage errors exit 2. Combines with --insecure and
--no-self-update.
The two PATH outcomes differ on purpose. On Windows the installer adds the install directory to
the user PATH itself, so a failed write is an error: exit 1. On macOS and Linux it only installs
into ~/.local/bin and leaves shell profiles to you, so a ~/.local/bin that is not on PATH is
not a failure: the run exits 0 and prints a warning on stderr (so CI logs show it) naming the tool
and the directory to add. On a CI runner, put ~/.local/bin on PATH for later steps (on GitHub
Actions, echo "$HOME/.local/bin" >> "$GITHUB_PATH").
Prefer to wire it by hand?
# once per machine, inside Claude Code — registers the catalogue, enables nothing
/plugin marketplace add MatLomax/claude-plugins
# in a project that wants a skill, from a terminal in that repo — writes
# .claude/settings.json (committed)
claude plugin install image-to-html@matlomax --scope project
# …or --scope local to keep it out of gitUpdate later with /plugin marketplace update matlomax.
Claude Desktop's plugin list (and claude plugin list) can say "no plugin
installed for this project" even though .claude/settings.json enables the
plugins. This marketplace's plugins are relative-path plugins: Claude loads
them straight from the marketplace because they are enabled in settings, with
no install record, and the plugin list only shows plugins that have one. An
explicit project-scope install creates the record, so the installer ends by
printing the marketplace registration plus one install line per plugin picked
in that run:
claude plugin marketplace add MatLomax/claude-plugins
claude plugin install <name>@matlomax --scope projectRun those in a terminal in the repo. (It is the claude shell command, not the
in-session /plugin install, which takes no --scope flag.) The
marketplace add line registers the marketplace with your Claude Code, which
the install needs; repeating it is harmless. In Claude Desktop, use the Code tab: + > Plugins > Add
plugin, choose the plugin, then this project.
A binary installed by the installer is on the PATH of new programs only. On
Windows, fully quit Claude Desktop from the system tray and close every
terminal and IDE, then reopen them; if they still can't find it, sign out of
Windows and back in. If the summary said the directory could not be added to
your user PATH, add it by hand: Start > "Edit environment variables for your
account" > Path > New. On macOS and Linux, add ~/.local/bin to PATH in your
shell profile and open a new terminal.
If the installer fails with SELF_SIGNED_CERT_IN_CHAIN or
UNABLE_TO_GET_ISSUER_CERT_LOCALLY, something between you and GitHub is
re-signing HTTPS traffic with its own root certificate — a corporate proxy or
an antivirus product doing TLS inspection. Node ships its own CA bundle and
doesn't consult the OS certificate store, so even though your browser (which
does use the OS store) trusts the connection fine, Node doesn't.
Proper fix — tell Node to trust that root certificate:
- Export the interceptor's root certificate. On Windows:
certmgr.msc-> Trusted Root Certification Authorities -> find the proxy/AV's certificate -> right-click -> All Tasks -> Export -> Base-64 encoded X.509 (.CER). - Point Node at it:
- Windows:
setx NODE_EXTRA_CA_CERTS "C:\path\root.cer" - macOS/Linux:
export NODE_EXTRA_CA_CERTS=/path/root.cer(add it to your shell profile to make it stick)
- Windows:
- Open a new terminal (environment variable changes don't reach already-open shells) and re-run the install command.
Quick one-off — if you just need to get unblocked right now:
npx --strict-ssl=false https://matlomax.com/claude-plugins.tgz --insecure--strict-ssl=false (before the URL) is npm's flag: npm downloads the
installer tarball itself, before the installer runs, so it needs its own
certificate checks turned off. --insecure (after the URL) is the
installer's flag: it skips certificate checks on the installer's own
downloads (the release check and the plugin binaries). Together they work
around the interception but mean any download during that run could be
tampered with in flight without detection — use the proper fix above when
you can. The same two flags go on whichever install URL you used (the GitHub
one below, or a pinned versioned one), alongside any other flags such as
--no-self-update.
If matlomax.com itself is blocked, install straight from GitHub:
npx https://github.com/MatLomax/claude-plugins/releases/latest/download/claude-plugins.tgz(Behind an intercepting proxy, add the same two flags as above:
npx --strict-ssl=false <that URL> --insecure.)
If the marketplace registration later fails to clone on Windows, git is hitting the same interception through its own TLS stack; make git use the Windows certificate store instead:
git config --global http.sslBackend schannel| Plugin | What it does |
|---|---|
image-to-html |
Reconstruct HTML from a mockup image and objectively gate the render against it — per-region SSIM, edge-XOR border detection, colour/tint sweeps, glyph measurement. Explicit-invoke only. |
worklog |
Per-project, SQLite-backed task & decision log for coding agents, remembered across sessions — task tree with blocking, GitHub-issue links, first-class decisions, and a per-session journal, over MCP. Run /worklog:init per project; /worklog:update (or the session-start auto-update) keeps the binary current. Requires the worklog binary on PATH. |
mdtohtml |
Convert extended Markdown to styled, self-contained HTML (themes, callouts, wikilinks, chips, KaTeX math, pre-rendered mermaid diagrams, a report hero). A skill teaches an agent the CLI; /mdtohtml:update (or the session-start auto-update) keeps the binary current. Requires the mdtohtml binary on PATH. |
install.mjs · package.json # the `npx` interactive installer (repo root)
lib/ # installer modules: args, flow, net, place, plugins, selfupdate, tls, userpath
scripts/build.mjs # bundles install.mjs + deps into dist/ (npm run build)
scripts/check-tag.mjs # fails a release whose tag differs from package.json
scripts/changelog-notes.mjs # extracts a version's CHANGELOG section as release notes
test/ # installer tests (npm test)
.github/workflows/test.yml # runs the tests on push and pull request
.github/workflows/release.yml # builds + publishes the release tarballs on a tag push
CHANGELOG.md # Keep a Changelog log for the installer
CLAUDE.md # repo process notes for agents working here
.claude-plugin/marketplace.json # the catalogue
plugins/<name>/
├── .claude-plugin/plugin.json # plugin manifest
└── one or more of:
├── skills/<name>/SKILL.md # a skill + its scripts/ and assets/
├── .mcp.json # an MCP server
├── hooks/hooks.json # SessionStart/Stop/… hooks
└── commands/<name>.md # a /plugin:command
Add a new plugin by dropping another plugins/<name>/ and listing it in
marketplace.json. worklog is the MCP+hooks+command shape, mdtohtml is the
skill+hook+command shape, and image-to-html is the plain skill shape. See
CONVENTIONS.md for the shared self-update contract every
binary-backed tool follows and how its plugin wires it up.
Maintainers only.
npm test— must pass.- Bump
versioninpackage.json. - In
CHANGELOG.md, move the[Unreleased]entries under a new## [X.Y.Z] - YYYY-MM-DDheading, and leave## [Unreleased]empty above it. - Commit.
git tag vX.Y.Z && git push origin vX.Y.Z.
Pushing the tag triggers .github/workflows/release.yml, which checks the
tag matches package.json, runs the tests, builds with npm run build
(esbuild bundles install.mjs and @clack/prompts into dist/, dependency-free),
and uploads claude-plugins-X.Y.Z.tgz and claude-plugins.tgz to the GitHub
release, using that version's CHANGELOG section as the release notes.
npm run build produces the same tarballs locally — useful for testing a
release before tagging it:
npm run build
npx file:./dist/claude-plugins-X.Y.Z.tgzhttps://matlomax.com/claude-plugins.tgz is a DNS record + redirect rule on
the matlomax.com Cloudflare zone, set up once:
- A proxied
AAAArecord for the apex (matlomax.com) pointing at100::(a placeholder — Cloudflare proxies the request before that address is ever dialled). - One Single Redirect rule: when URI Full equals
https://matlomax.com/claude-plugins.tgz, redirect to the static URLhttps://github.com/MatLomax/claude-plugins/releases/latest/download/claude-plugins.tgz, 302, query string not preserved.
Self-update relaunches at the versioned asset on the GitHub release
(https://github.com/MatLomax/claude-plugins/releases/download/vX.Y.Z/claude-plugins-X.Y.Z.tgz)
directly, so no versioned matlomax.com URL exists.