Skip to content

Latest commit

 

History

History
367 lines (263 loc) · 28.4 KB

cli.md

File metadata and controls

367 lines (263 loc) · 28.4 KB

CLI Reference

Complete command reference for mcs. For a quick introduction, see the README.

mcs sync

The primary command. Configures a project by selecting packs and installing their artifacts. Running without flags opens interactive multi-select.

mcs sync [path]                  # Interactive project sync (default command)
mcs sync --pack <name>           # Non-interactive: add/update specific pack(s), keeping the rest (repeatable)
mcs sync --pack <name> --prune   # Make the named pack(s) the exact set — removes the rest after confirmation
mcs sync --all                   # Apply all registered packs without prompts
mcs sync --dry-run               # Preview what would change
mcs sync --global                # Install to global scope (~/.claude/)
Flag Description
[path] Project directory (defaults to current directory)
-p, --pack <name> Add or update a specific pack non-interactively. Repeatable for multiple packs. Packs already configured in the scope are kept.
-a, --all Apply all registered packs without interactive selection.
--prune With --pack: remove configured packs not named with --pack. Prompts before removing unless --yes is passed.
-y, --yes Skip the removal-confirmation prompt. Only meaningful with --prune.
--dry-run Preview changes without writing any files.
-g, --global Sync global-scope components (brew packages, plugins, MCP servers to ~/.claude/).

mcs sync is also the default command — running mcs alone is equivalent to mcs sync.

A selected pack always installs every component it declares. Components that an older release's --customize excluded are installed on the next sync, which names them before installing them.

--pack is additive. Naming a pack adds or updates it and leaves every other configured pack in place; a footer lists the packs that were kept. A configured pack that is missing from the registry stops the run rather than being removed — re-add it with mcs pack add, or pass --prune to remove it. With --prune, the named packs become the exact set, matching mcs bootstrap --prune.

mcs bootstrap

Apply a declarative mcs.yaml from the current directory. Bootstrap installs any packs the file declares, seeds prompt values, and syncs the project.

mcs bootstrap                    # Read ./mcs.yaml, install declared packs, keep any others
mcs bootstrap --prune            # Also remove packs configured here but absent from mcs.yaml
mcs bootstrap --dry-run          # Preview what would change
mcs bootstrap --prune --yes      # Prune without the removal-confirmation prompt (CI)
mcs bootstrap --trust-all        # Trust declared packs without prompting
Flag Description
--dry-run Preview without making changes. For un-registered packs, prints would fetch <source>@<ref> without cloning.
--prune Remove packs configured in this project but absent from mcs.yaml. Prompts before removing unless --yes is passed.
-y, --yes Skip the removal-confirmation prompt. Only meaningful with --prune.
--trust-all Approve each declared pack's executable content without prompting, so the trust prompts need no TTY. Other prompts are unaffected — see --yes for removals and values: for pack prompts. No-op with --dry-run, which returns before any pack is fetched.

File format (./mcs.yaml)

schemaVersion: 1
packs:
  - source: mcs-cli/dev              # git URL, GitHub shorthand, or local path
    ref: main                        # optional (git only)
    values:                          # optional: seeds prompt priors
      BRANCH_PREFIX: feature
  - source: mcs-cli/memory
  • source is required. ref, values, and scope are optional.
  • values seeds ProjectState.resolvedValues so the keys it covers do not re-prompt. A key counts when a declared pack declares it as a prompt or references it as a __KEY__ placeholder in a file, template, settings file, or MCP config. A key that matches neither is ignored with a warning (usually a typo). Any key not seeded here falls through to normal interactive resolution.
  • scope is reserved for a future release; v1 accepts only project (or omitted).

Semantics

  • Project-scoped convergence. Bootstrap's pack set applies to the current project; use mcs sync --global to manage packs in ~/.claude/. Like mcs sync, bootstrap does refresh the shared user-level update-check hook in ~/.claude/settings.json on non-dry-run runs — that hook is per-user, not per-project.
  • Additive by default. Packs listed in mcs.yaml are installed / updated; any pack configured in the project but not listed is preserved. After sync, a note lists any such extras so the divergence stays visible.
  • --prune opts into authoritative mode. With --prune, mcs.yaml becomes the exact desired set — extras are unconfigured. A confirmation prompt lists what will be removed (skip with --yes).
  • Idempotent. Re-running converges — no work if nothing changed.
  • Missing checkouts are restored. A git pack still in the registry whose ~/.mcs/packs/ checkout was deleted is re-cloned at its recorded ref (or the declared one, when mcs.yaml changes it), with the usual trust check.
  • Fail fast. A fetch, validate, trust, or sync error stops bootstrap immediately with the failing pack and reason. When bootstrap aborts partway through, an epilogue lists which packs already registered — re-run after fixing the failing entry to continue.
  • Trust prompts need a TTY unless --trust-all is passed. Both trust surfaces bootstrap can reach — adding a pack it has never seen, and advancing a pack's ref: to a revision with changed scripts — end at an interactive confirmation. Without a terminal that confirmation reads as "no" and bootstrap aborts, so any unattended run against a fresh pack needs --trust-all.
  • --trust-all grants code execution without review. It approves everything the declared packs can execute: install shell commands, MCP servers, hook files and the interpreters they run under, command files, configure and prompt scripts, and doctor check/fix commands. All run with your privileges; hooks and MCP servers run on every Claude Code session. Brew packages and plugins are outside the trust surface entirely, with or without this flag — see Architecture for that boundary. Each auto-trusted pack logs a warning naming what it was granted, and the approval persists: ~/.mcs/registry.yaml records the hashes, so later interactive runs treat that content as reviewed. Point mcs.yaml at sources you control or have already reviewed, and prefer a ref: naming a tag over a branch so the content a run trusts is not a moving target.

Duplicate identifier handling

Existing entry New entry Behaviour
Same sourceURL + same ref matches file silent no-op
Same sourceURL + different ref matches file PackUpdater moves the checkout
Different sourceURL matches file auto-replace with warning naming both URLs

Dry-run limitation

--dry-run cannot fully preview a sync for a pack that has not yet been fetched — v1 prints the intended add operation without cloning. Fetch-and-preview is planned for a future release.

mcs update

Refresh already-configured packs across every scope they're installed in. Fetches the latest pack contents (with trust verification) and re-applies the existing pack set in both the global scope and the current project's scope. A git pack whose local checkout was deleted is re-cloned at its recorded ref.

mcs update                       # Refresh all configured packs in every scope (global + current project)
mcs update --global              # Only refresh the global scope
mcs update --project             # Only refresh the current project's scope
mcs update --all-projects        # Refresh global + every project in the index (machine-wide)
mcs update --dry-run             # Preview without making changes
mcs update --trust-all           # Trust changed executable content without prompting (no TTY required)
Flag Description
[path] Project directory (defaults to current directory)
-g, --global Only refresh the global scope. Mutually exclusive with --project and --all-projects.
-p, --project Only refresh the current project's scope. Mutually exclusive with --global and --all-projects.
-a, --all-projects Refresh the global scope plus every project tracked in ~/.mcs/projects.yaml. Asks for confirmation in interactive mode and lists the affected projects first. Mutually exclusive with --global and --project.
--dry-run Preview changes without writing any files.
--trust-all Approve each updated pack's new or changed executable content without prompting, so the trust prompt needs no TTY. Other prompts are unaffected. No-op with --dry-run, which returns before any pack is fetched. Grants code execution without review, and the approval persists — see the --trust-all caveat under mcs bootstrap.

mcs update always refreshes the full configured set of every selected scope. To advance a single pack's registry pointer without applying anywhere, use mcs pack update <name>.

About --all-projects: this fans out machine-wide, running each pack's configureProject hook with the corresponding project as cwd. Uncommitted changes in those projects to managed files (settings, hooks, skills) may be overwritten. Always interactive-confirm in a terminal; pair with --dry-run first if unsure.

Differences from mcs sync:

  • Refresh-only — does not add or remove packs. Use mcs sync to change the configured set.
  • Multi-scope by default — when configured packs exist in both global and project scopes, both are refreshed in one command (this was the original pain point that motivated the verb).

Plugins: after re-applying, mcs update moves every plugin mcs installed to its marketplace's current version: one claude plugin marketplace update per marketplace, then claude plugin update <id> -s <scope> in each scope that recorded it. Version changes are printed as <id> old → new; --dry-run lists what would be updated. A plugin the user installed at the same scope before mcs did is left alone. This tracks the marketplace's latest release, not the pack's ref:. mcs sync and mcs doctor --fix never update plugins.

Trust prompts: when a pack's executable content has changed, mcs update prompts for trust. Denying the prompt skips the pack for this run: the registry stays at the old SHA, the pack's checkout returns to that commit, and the pack is excluded from re-apply so untrusted scripts don't auto-install. Any other update that doesn't end trusted (an invalid manifest, a failed analysis) returns the checkout the same way. The prompt re-fires on the next mcs update run. Without a terminal the prompt reads as "no", so an unattended run needs --trust-all to take updates that change a pack's executable content. An unattended run that tracks a branch trusts whatever that branch next contains; pin packs to a tag (mcs pack add <url> --ref <tag>) where that matters.

Prompt value reuse: unlike mcs sync, mcs update does not show the interactive "Reuse these values? [Y/n]" gate when a pack's prompts already have stored answers. Refresh implies "keep what I have," so existing values are reused silently. New prompts introduced by a pack update still execute normally. To revisit stored values, use mcs sync (answer "No" at the gate to re-enter values one by one, with the existing answer as the default).

Exit status: a hard failure (failed fetch, unreadable checkout, invalid fetched manifest, or a checkout that can't return to its trusted commit while it hosts a linked plugin) exits non-zero when running non-interactively (CI), or when every attempted pack failed — so a transient outage in CI is a detectable failure, not a silent zero exit. In an interactive terminal a partial failure exits zero (the per-pack warnings are visible); the packs that updated cleanly are still re-applied first. A declined trust prompt is not a failure and always exits zero (it re-prompts next run).

mcs pack

Manage registered tech packs.

mcs pack add <source>

Add a tech pack from a git URL, GitHub shorthand, or local path.

mcs pack add <source>            # Git URL, GitHub shorthand, or local path
mcs pack add user/repo           # GitHub shorthand → https://github.com/user/repo.git
mcs pack add /path/to/pack       # Local pack (read in-place, no clone)
mcs pack add <url> --ref <tag>   # Pin to a specific tag or branch
mcs pack add <url> --preview     # Preview pack contents without installing
Flag Description
-r, --ref <tag> Pin to a specific git tag or branch (git packs only). Commit SHAs are not supported — the pack is cloned with git clone --branch, which only resolves branch and tag names.
-p, --preview Preview the pack's contents without installing.

Source resolution order: URL schemes → filesystem paths → GitHub shorthand.

mcs pack remove <name>

Remove a registered pack.

mcs pack remove <name>           # Remove with confirmation
mcs pack remove <name> --force   # Remove without confirmation
Flag Description
-f, --force Skip the confirmation prompt.

Removal is federated: mcs discovers all projects using the pack (via the project index) and runs convergence cleanup for each scope. A pack hosting its own plugin then has its generated marketplace unregistered; if that fails, the command fails before the registry entry or checkout is removed, and re-running it finishes the job.

mcs pack list

mcs pack list                    # List registered packs with status
mcs pack list --json             # Machine-readable JSON output
Flag Description
--json Print the registered packs as a JSON array instead of human-readable text.

In the text output, a git pack shows as source@ref (short-sha), with the source's .git suffix dropped; @ref appears only when the pack was added with --ref.

With --json, each array element has these fields:

Field Type Description
identifier string Pack identifier.
source string Git clone URL, or the original path for a local pack.
ref string or null Tag or branch as declared with --ref; null when none was given (always for local packs).
commitSHA string Checked-out commit, or "local" for a local pack.
isLocal boolean Whether the pack is read in-place from a local path.
status string "ok", "missing" (checkout or local directory not on disk), or "invalid" (path escapes the packs directory, or a git checkout has no techpack.yaml; local packs are not checked for a manifest).
scopes array of strings Where the pack is configured: "global" first if synced globally, then project paths from ~/.mcs/projects.yaml, sorted. Projects whose directory no longer exists are left out.

An empty registry prints [] and exits 0. If ~/.mcs/registry.yaml or ~/.mcs/projects.yaml can't be read, the error goes to stderr, nothing is printed to stdout, and the command exits 1. The command never modifies either file.

mcs pack update [name]

mcs pack update [name]           # Update pack(s) to latest version (registry only, no re-apply)
mcs pack update --trust-all      # Trust changed executable content without prompting (no TTY required)

Fetches the latest commits from the remote and updates the local checkout. Local packs are skipped (they are read in-place and pick up changes automatically). A checkout that was deleted is re-cloned at the pack's recorded ref.

This is a low-level fetch — useful for pack authors testing upstream changes without applying them, or in CI workflows that handle the apply step separately. For most users, mcs update is the right command: it does the same fetch and re-applies across every configured scope.

Exits non-zero on a hard failure when running non-interactively (CI) or when every attempted pack failed, matching mcs update. A declined trust prompt is not a failure. Pass --trust-all to approve changed executable content when no terminal can answer the prompt; it grants code execution without review, and the approval persists — see the --trust-all caveat under mcs bootstrap.

mcs pack validate [source]

Validate a tech pack for structural correctness and best practices. Designed for pack authors to catch issues locally before submitting to the registry.

mcs pack validate                # Validate pack in current directory
mcs pack validate /path/to/pack  # Validate pack at a specific path
mcs pack validate ios            # Validate an installed pack by identifier
Argument Description
[source] Directory path or installed pack identifier. Defaults to current directory.

Two-stage validation pipeline:

  1. Structural validation — manifest existence, YAML parsing, schema validation, minMCSVersion check, referenced file existence
  2. Heuristic checks — best-practice analysis with severity levels

Severity levels:

Severity Exit code Examples
Error 1 Empty pack (no components/templates/configure), source: "." copying entire pack root, missing settings file sources
Warning 0 Unreferenced files in subdirectories, unreferenced root-level content files, MCP server using python/node without brew component, missing python module directory, brew: package installed from a third-party tap

Structural errors always cause exit code 1 and stop further analysis. Heuristic errors also cause exit code 1. Warnings are advisory and do not affect the exit code.

When unreferenced-file warnings appear, the command also emits a remediation hint pointing authors at the manifest's ignore: field (see Schema Reference) — adding intentional non-material paths there silences both the validation warnings and downstream mcs check-updates notifications for commits limited to those paths.

mcs doctor

Diagnose installation health with multi-layer checks.

mcs doctor                       # Diagnose all packs (project + global)
mcs doctor --fix                 # Diagnose, then repair what it can after one confirmation
mcs doctor --pack <name>         # Check a specific pack only
mcs doctor --global              # Check globally-configured packs only
Flag Description
-f, --fix Repair failed checks after one confirmation: run the check's own fix (pack fixCommand, gitignore entries, stale index entries, missing state file, duplicate scope), or re-sync the scope a failed install check belongs to.
-y, --yes Skip confirmation prompt before applying fixes (use with --fix).
-p, --pack <name> Only check a specific pack.
-g, --global Only check globally-configured packs.

Doctor resolves packs from: explicit --pack flag → project .mcs-project state → CLAUDE.local.md section markers → global manifest.

How --fix repairs. When an install check fails (a missing MCP server, brew package, plugin, file, hook entry, settings key or gitignore entry), --fix re-syncs that scope onto the packs already configured there — the same re-apply mcs update runs, so no pack is ever added or removed. The prompt lists each scope to re-sync; re-syncing resets managed files you edited in that scope. Afterwards the failed checks run again and are reported as fixed or still failing. Checks a pack author wrote, and a missing hook interpreter, are never repaired by a re-sync — doctor prints their hint instead. Drift warnings (edited files or settings) are left alone. Without --fix, doctor ends with a line saying how many issues --fix could repair.

Exit status: doctor exits 1 when a check is still failing — with --fix, a failure counts unless its fix was reported as applied, so a declined prompt or a hint-only failure still exits 1 — or when a pack named in --pack is not registered or failed to load. Warnings and checks skipped during diagnosis never change the exit status. A brew package a pack declares is expected to be installed, so a missing one is a failure.

mcs cleanup

Find and delete timestamped backup files created during sync.

mcs cleanup                      # List backups and confirm before deleting
mcs cleanup --force              # Delete backups without confirmation
mcs cleanup --all-projects       # Also scan every project tracked in the index
Flag Description
-f, --force Delete without confirmation.
-a, --all-projects Also scan every project tracked in ~/.mcs/projects.yaml.

Without --all-projects, cleanup scans ~/.claude/ and the current directory. With it, each tracked project is scanned at its root (top level only) plus <project>/.claude/ in full — the two places sync writes backups. Index entries whose directory no longer exists are skipped.

mcs export

Export your current Claude Code configuration as a reusable tech pack.

mcs export <dir>                 # Export current config as a tech pack
mcs export <dir> --global        # Export global scope (~/.claude/)
mcs export <dir> --identifier id # Set pack identifier (prompted if omitted)
mcs export <dir> --non-interactive  # Include everything without prompts
mcs export <dir> --dry-run       # Preview what would be exported
Flag Description
<dir> Output directory for the generated pack.
-g, --global Export global scope (~/.claude/) instead of the current project.
-i, --identifier id Set the pack identifier (prompted interactively if omitted).
-y, --non-interactive Include all discovered artifacts without prompting for selection.
--dry-run Preview what would be exported without writing files.

The export wizard discovers MCP servers, hooks, skills, commands, agents, plugins, CLAUDE.md sections, gitignore entries (global only), and settings. Sensitive env vars are replaced with __PLACEHOLDER__ tokens and corresponding prompts: entries are generated.

mcs check-updates

Check for available tech pack and CLI updates. Designed to be lightweight and non-intrusive.

mcs check-updates                # Check for updates (always fetches from remote)
mcs check-updates --hook         # Run as SessionStart hook (respects 24-hour cooldown and config)
mcs check-updates --json         # Machine-readable JSON output
mcs check-updates --snooze       # Silence the SessionStart update notice until tomorrow (local time)
Flag Description
--hook Run as a Claude Code SessionStart hook. Respects the 24-hour cooldown and config keys. Without this flag, always fetches from remote.
-j, --json Output results as JSON instead of human-readable text.

How it works:

  • Pack checks: Runs git ls-remote per pack to compare the remote HEAD against the local commit SHA. Local packs are skipped.
  • Noise filter: When the remote SHA differs, runs a shallow git fetch + git diff --name-only in the local pack clone and classifies the changed-path list. If every path is deny-listed infrastructure (README, LICENSE, CHANGELOG, .github/, node_modules/, .build/, Makefile, .gitignore, etc.), the notification is suppressed and ~/.mcs/registry.yaml's commitSHA advances to the new remote SHA — the same infrastructure-only commits won't re-trigger on future cooldown windows. Any non-deny-listed path makes the update material and surfaces the notification.
  • techpack.yaml always surfaces: manifest edits can change the install surface entirely (new hook scripts, swapped MCP server commands, different component actions), so the filter never suppresses a commit that touches the manifest — supply-chain safety takes precedence over noise reduction.
  • Never-hide on filter failure: if git fetch or git diff errors (offline, access revoked, repo corrupt), the notification surfaces unfiltered. The filter can only suppress; it can't manufacture silence on error.
  • CLI version check: Queries git ls-remote --tags on the mcs repository and compares the latest CalVer tag against the installed version.
  • Cache: Results are cached in ~/.mcs/update-check.json (timestamp + results). Cached results are served if less than 24 hours old (no network request). When the cache is stale, a fresh network check runs and the results are cached. mcs check-updates (without --hook) always forces a fresh check; mcs sync and mcs doctor respect the cache.
  • Cache invalidation: mcs pack update deletes the cache so the next hook re-checks. CLI version cache self-invalidates when the user upgrades mcs.
  • Scope: Checks global packs plus packs configured in the current project (detected via project root). Packs not relevant to the current context are skipped.
  • Offline resilience: Network failures are silently ignored — the command never errors on connectivity issues.

Note: mcs sync and mcs doctor check for updates at the end of execution, respecting the 24-hour cache. The config keys below only control the automatic SessionStart hook that runs when you start a Claude Code session.

mcs config

Manage mcs user preferences stored at ~/.mcs/config.yaml.

mcs config list                  # Show all settings with current values
mcs config get <key>             # Get a specific value
mcs config set <key> <value>     # Set a value (true/false)

Available Keys

Key Description Default
update-check Show tech-pack and mcs CLI update notifications on session start (opt-out) true

These keys control a SessionStart hook in ~/.claude/settings.json that runs mcs check-updates when you start a Claude Code session. The hook's output is injected into Claude's context so Claude can inform you about available updates.

  • Enabled (either key true): A synchronous SessionStart hook is registered. It respects the 24-hour cooldown.
  • Disabled (both keys false): No hook is registered. You can still check manually with mcs check-updates or rely on mcs sync / mcs doctor which check using the 24-hour cache.

When either key changes, mcs config set immediately adds or removes the hook from ~/.claude/settings.json — no re-sync needed. The same hook is also converged during mcs sync.

On first interactive sync, mcs prompts whether to enable automatic update notifications (sets both keys at once). Fine-tune later with mcs config set.

When updates are pending, Claude tells you before it starts on your request and asks once, through a question prompt, whether to apply them; for pack updates that question also warns that --trust-all approves the packs' changed executable content without review (see the --trust-all caveat). The question offers three answers: Update now, Not now (asked again next session) and Remind me tomorrow, which runs mcs check-updates --snooze so no session asks again until the local date changes. Claude runs no update command without Update now, and continues with your request once every question is answered. The commands run in this order: brew update and brew upgrade mcs-cli/tap/mcs for a CLI update, then mcs update -a --trust-all for pack updates. After a pack update Claude runs mcs doctor (global scope and current project only). If every check passes it asks, separately, whether to delete all mcs backups across tracked projects with mcs cleanup -af; if a check fails it keeps the backups and asks, separately, whether to run mcs doctor --fix --yes. If you answer Not now to a pack update, run mcs update --all-projects in your own terminal to review each trust prompt. To turn the notice off entirely, run mcs config set update-check false.


Next: Learn to build packs from scratch in Creating Tech Packs.


Home | CLI Reference | Creating Tech Packs | Schema | Architecture | Troubleshooting