npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

magus-cli

v7.9.1

Published

TUI tool for managing Claude Code plugins, MCPs, and configuration

Readme

magus

Manage a Claude Code setup — plugins, MCP servers, skills, CLI tools and the binaries plugins depend on — from a TUI, a CLI, or a committed team manifest.

Install

bun add -g magus-cli      # recommended
npm install -g magus-cli  # also works

Updates go through magus itself, not the package manager:

magus upgrade

upgrade advances magus. update advances your dependencies — the plugins, CLI tools and skills your profile declares. Same split as every package manager, so the two are never confused.

You get a self-contained binary for your platform (an optionalDependencies package gated on os/cpu). On a platform without a prebuilt binary the launcher falls back to running from source under Bun, so the install still works.

Two ways to use it

Interactive — run magus with no arguments for the TUI. Good for exploring what is available and toggling things on one machine.

Declarative — commit a .claude/profiles.json and run magus install. Good for making a teammate's machine match yours. This is the team configuration guide.

Commands

magus                    Open the interactive TUI

install [profile]           Install a profile's plugins, binaries, skills and
                            env at the manifest's PINNED versions, then activate
                            it. No argument = every profile.
  --check                   Report drift only, write nothing (CI gate)
  --yes, -y                 Skip the confirmation prompt
update [profile]            Bring the ACTIVE profile up to date: install what it
                            declares but this machine lacks, and advance every
                            "latest" pin to the newest published version.
  --check                   Report only, write nothing (exits 1 if anything is
                            missing or known to be behind)
  --yes, -y                 Skip the confirmation prompt
profile list                Show every profile; ● marks the active one
profile show <name>         Print a profile's fully resolved closure
profile switch <name>       Make <name> active: render its settings.json,
                            .mcp.json and models.json, install its skills
profile init                Write .claude/profiles.json from this project's setup
doctor [--fix]              Check binary deps, the active profile, conventions

claude [args...]            Check for plugin updates (1h cache), then run claude
  --force, -f               Force the update check
upgrade                     Update magus itself
--version, -v               Version + update check
--help, -h                  This list
--theme light|dark          Force the palette. Otherwise: MAGUS_THEME, then
                            TERM_THEME, then a 250 ms terminal query, then
                            COLORFGBG, then dark. Only the exact words
                            "light"/"dark" count.

Light or dark

magus decides once at startup, first answer wins: --theme, then the MAGUS_THEME variable, then TERM_THEME, then a bounded (250 ms) wait for the terminal's OSC 10/11 reply — only when stdin and stdout are both TTYs — then COLORFGBG, then dark. A word that is not exactly light or dark (so auto, Light, an empty value) is no opinion and the next step runs; when the flag is given a bad word, that is an error naming both accepted values.

TERM_THEME and MAGUS_THEME are read from the process environment, never from a project's .env. Running from source needs --no-env-file, because Bun otherwise autoloads the cwd's .env before any code runs; the package scripts and the bin/magus.js source fallback already pass it, and the compiled binary never autoloads. The OSC 10/11 query itself is emitted by OpenTUI (0.1.107) when the renderer is created, regardless of the outcome; when an earlier step answered, magus never awaits or uses the reply, so first paint is not delayed and the terminal's answer has no influence.

install vs update

They differ in one thing: whether the pins are allowed to move.

| | install | update | |---|---|---| | Versions | exactly what the manifest pins | advances every "latest" pin | | Scope | every profile (or the one named) | the active profile | | Also does | materializes + activates the profile | nothing to the manifest |

Both install whatever the profile declares and this machine lacks, so update covers the "I ticked it but never installed it" case on its own.

This is dependency management, not a package manager. There is no resolver, no lockfile and no version negotiation. The job is: everything the profile declares is installed, and current.

That matters for exact pins. claude plugin install takes no version argument — it installs whatever the marketplace publishes now — so a pin naming anything else cannot be satisfied. update plans against the version it can actually get, notes the shortfall, and moves on:

  dev@magus   = 9.9.9  (manifest pins 4.6.1; the marketplace offers only 9.9.9)

It records what actually landed, never the pin, and it does not fail over the difference — an unsatisfiable pin is a standing property of the manifest, not something a given run did wrong. Pin drift is gated where it always was: magus install --check with strictVersions in the manifest.

update only ever moves forward. Nothing here can downgrade — the installer takes no version — so an install that is already ahead of what the marketplace publishes is left alone rather than dragged back. A plan that tried would emit a step that runs, reports success, changes nothing, and reappears on every subsequent run.

--check fails on things that are missing or known to be behind. It deliberately does not fail on the two items that get re-fetched every run regardless: an already-installed skill (a manifest skill ref carries no content hash, so there is nothing to compare) and a present unpinned binary (no version probe exists, only present/absent). A gate that can never pass gets switched off, so it only fires on real drift.

Neither command needs a manifest to exist first. In a repo without .claude/profiles.json, both offer to write one describing the project as it stands — that is magus profile init, which you can also run on its own. Adoption reads the project's own .claude/settings.json; if that enables nothing it falls back to your user-scope settings and says so, because the file is meant to be committed and one machine's global set is not a team contract. Machine-only keys are stripped, and any settings value naming a path under your home directory is flagged for review.

--yes alone will not create a manifest. It means "don't ask me about the command I ran", and someone running magus update --yes in CI did not ask to author their team's committed contract. Creating it needs a human at the prompt, or magus profile init.

magus doctor exits non-zero when it finds a problem, so it works as a CI check. So does magus install --check.

What the TUI covers

| Screen | What it manages | |---|---| | Plugins | Install, enable, disable; shows version changes and newly installed plugins | | Skills | Browse and install skills from configured skill repos | | MCP | Add MCP servers from a curated catalog | | Settings | Claude Code settings, from a catalog of known keys | | Profiles | Saved plugin sets (the TUI's own, older profile concept) | | CLI Tools | Install and update CLI tools via the right package manager; a tool a plugin installs from a release shows as managed by that plugin | | Git State | Gitignore conventions and repo hygiene | | Alias | Shell alias + flag management for launching claude | | Styles | Communication style presets, composed into one native output style |

Navigate with ↑/↓ or j/k, Enter to select, r to refresh, ? for help, q/Escape to go back. Number keys 1–9 jump straight to a screen.

Styles

Claude Code activates exactly one output style at a time. The Styles screen (9) composes several rule blocks into that one style, so choices that would otherwise compete for the single slot end up in one file.

One verbosity axis — pick exactly one of direct, explanatory, or terse — plus any number of free modifiers:

| Preset | Effect | |---|---| | direct | Lead with the answer, minimal preamble | | explanatory | Teach as you go, more context per step | | terse | Shortest useful output | | no-slop | Bans filler, hype, and AI vocabulary | | asd-ste100 | ASD-STE100 Simplified Technical English: sentences that survive one read | | evidence-first | Claims must carry a citation, command output, or file reference | | calibrated | State confidence honestly; no false certainty | | plain-language | Prefer plain words over jargon | | structured | Headings, tables, and lists over long paragraphs | | terminology | Enforce project-specific vocabulary, filled in from your codebase |

Two verbosity presets is the one combination that does not work: terse says "do not explain unless asked" and explanatory says "explain the specific choice", so together they cancel and the model resolves the contradiction differently every turn. Everything else stacks.

The presets live in src/data/styles/*.md and are compiled into the binary, so they are always the version this magus was built from.

What gets written

.claude/output-styles/composed.md    the composed rules (generated — do not hand-edit)
.claude/settings.json                "outputStyle": "composed"
.claude/style.json                   the declaration — commit this to share it

With a profile active the file is named composed-<profile> and the choice is also recorded in .claude/profiles.json, because magus install rebuilds settings.json from the manifest and would otherwise drop it.

style.json is the reviewable statement of what the project wants; composed.md is the artifact Claude Code reads. A teammate who clones the repo gets the declaration, and magus tells them when their local artifact does not match it.

Styles you already have

Output styles you wrote yourself compose too — magus finds them in ~/.claude/output-styles/ and .claude/output-styles/ and lists them alongside the presets. Community styles can be fetched from their source repository on an explicit action, and are cached outside ~/.claude/output-styles/ so a re-fetch never overwrites something you wrote.

Built-in styles ship inside the Claude Code binary rather than on disk, so there is no file to import until you capture one. --discover names the set the installed version actually has — it was Concise, Explanatory, Learning and Proactive on Claude Code 2.1.239, and Anthropic adds to it:

bun scripts/capture-builtin.ts --discover   # what Anthropic ships today
bun scripts/capture-builtin.ts --all        # capture every built-in
bun scripts/capture-builtin.ts --check      # what fell behind after an upgrade

Each capture runs one real claude -p round trip behind a transparent proxy and records the system prompt Claude Code actually sent, writing ~/.claude/output-styles/builtin-<name>.md. Captures are per machine and stamped with the Claude Code version they came from — re-run --check after every upgrade. Nothing is committed to this repo: the text is Anthropic's, and it changes on their release schedule, not ours.

Coding rules stay on

Every generated style sets keep-coding-instructions: true. Without that flag Claude Code drops its own coding-discipline rules from the system prompt, and a setting about how to communicate has no business switching off how code gets written. force-for-plugin is never set either — that would override your own /output-style choice.

Files magus touches

| Path | Owner | What magus does | |---|---|---| | .claude/profiles.json | you, committed | the one source of truth; records every change to the live config | | .claude/profiles/active.json | magus, gitignored | which profile is active, and what magus last wrote | | .claude/settings.json | magus + Claude Code, gitignored | in profile mode, rendered from the active profile; Claude Code's edits are recorded back into it | | .claude/models.json | magus, gitignored | in profile mode, rendered from the active profile's routing | | .claude/skills/<name>/ | magus or you | a skill the active profile lists is installed and gitignored by magus; any other folder is yours and is never touched | | .claude/settings.local.json | you, gitignored | env values collected during install | | .claude/style.json | you, committed | the project's declared style — written on apply | | .claude/output-styles/composed*.md | magus, committed | the generated style Claude Code reads | | .mcp.json | magus + Claude Code, gitignored | in profile mode, rendered from the active profile; Claude Code's edits are recorded back into it | | ~/.claude/plugins/* | Claude Code | never written directly — always via the claude CLI |

magus never hand-edits Claude Code's own registries (installed_plugins.json, known_marketplaces.json, the plugin cache). Those go through claude plugin ... so Claude Code stays the single writer.

Development

bun install
bun test                # full suite
bun run typecheck
bun run lint
bun run src/main.tsx    # run from source
bun run build:binaries  # cross-compile all platform binaries + their packages

Tests run per-file isolated in CI (bun run test:ci) because mock.module leaks across files in a shared process.

Do not bump @opentui past 0.1.x. 0.4.x breaks bun build --compile: it resolves tree-sitter workers eagerly, bypassing the OTUI_ASSET_ROOT override, so a bare import "@opentui/core" crashes at import time in the compiled binary. Re-run the compile spike before changing that pin.

Releasing

Tag-driven. .github/workflows/magus-release.yml fires on tools/magus/v*:

  1. verify — install, typecheck, test:ci, version and optional-deps gates
  2. binaries — a matrix building each target on its native runner (bun --compile cannot cross-compile in CI because opentui ships a per-platform native library)
  3. publish — platform packages first, main package last, so its optionalDependencies already resolve
# bump "version" AND the three optionalDependencies to match
bun run check:optional-deps
git commit -am "feat(magus): vX.Y.Z - description"
git tag -a tools/magus/vX.Y.Z -m "Release message"
git push origin main --tags

Auth is npm OIDC trusted publishing — the workflow references no secrets. Adding a new platform target needs a one-time manual publish to create the package name (OIDC can only authenticate against a package that already exists); see docs/npm-trusted-publishing-setup.md.

License

MIT