<!-- Generated from the Command Code docs: https://commandcode.ai/docs -->

# Packaging and install

A mod starts life as a loose file in `~/.commandcode/mods/`. When it should be shared - with a team, or the world - it becomes a **package**: an npm package, a git repo, or a local directory that `cmd mods add` installs and Command Code loads on every session.

## Install, remove, list, update, open

```bash
cmd mods add cmd-mod-hi                     # bare name = npm package
cmd mods add @team/review-mod@1.2.0         # scoped npm name, pinned (npm: prefix optional)
cmd mods add owner/repo@v1                  # GitHub shorthand (any git host via git:<host>/…)
cmd mods add ./tools/local-mod              # local path, referenced in place
cmd mods add -g owner/repo                  # user scope instead of project
cmd mods list                               # alias: cmd mods ls
cmd mods update                             # reinstall missing, reconcile pinned refs (alias: up)
cmd mods remove owner/repo                  # alias: cmd mods rm
cmd mods remove fun-stuff                   # by the name `mods list` prints
cmd mods enable <name>                      # enable a mod; -g for user level
cmd mods disable <name>                     # disable a mod; -g for user level
cmd mods open                               # open the mods dir (project); --user for ~
```

`cmd mods list` prints one line per mod - name, scope, origin - project mods first, then user, then package-provided:

```
Mods (2)
  guard · project · .commandcode/mods/guard.ts
  cmd-mod-hi · user · from npm:cmd-mod-hi
```

The origin is the file for a drop-in mod and `from <source>` for a package mod (whose file lives under `.registry/`, which you never edit by hand). Built-in mods are Command Code's own internals and are never listed. Configured package sources stay out of the output entirely unless one needs attention - not installed, or installed but contributing no mods.

`remove` (`rm`) takes **either** a source or the mod NAME `cmd mods list` prints, and removes whatever backs that name: a drop-in file or directory is deleted from the mods dir, a package-provided mod removes its source (and says which sibling mods went with it), a `mods.paths` mod drops its settings entry. Scope follows where the mod actually lives, so a user-scope mod does not need `-g`; a source that is configured in neither scope reports that instead of a silent success.

`cmd mods open` opens the drop-in mods directory - `<project>/.commandcode/mods` by default, `~/.commandcode/mods` with `--user` (`-g`) - creating it if it does not exist yet. `--path` prints the directory instead of opening it.

A source with no slash is an npm package name, and so is a `@scope/name` - git shorthand always carries an `owner/repo` slash, so `npm:` is optional (an explicit `git:` prefix always wins). Sources persist in the `mods.sources` settings key (project scope writes `.commandcode/settings.json`, `-g` writes `~/.commandcode/settings.json`). Identity is version/ref-agnostic - `owner/repo@v1` and `https://github.com/owner/repo` are the same package, and a project entry shadows the same identity at user scope. Installs land in `<scope>/.commandcode/mods/.registry/{npm,git}/…`; startup never runs npm/git on its own - a configured-but-missing package is a warning pointing at `cmd mods update`.

## What a package ships

A package declares what it ships via `package.json` - exact paths, directories, or globs (expanded against the package root; entries escaping the root are dropped):

```json
{
	"name": "@team/review-mod",
	"commandcode": {"mods": ["./src/review.ts", "src/checks/*.ts"]}
}
```

No manifest → the `mods/` convention directory → a root `index.ts`, in that order.

## Filtering a source's mods

A `mods.sources` entry can be the object form to load only part of a package:

```json
{
	"mods": {
		"sources": [
			{"source": "owner/review-pack", "mods": ["review/*.ts", "!review/slow.ts"]}
		]
	}
}
```

Four pattern kinds, applied in precedence order: `-path` force-exclude (exact, beats everything) → `+path` force-include (exact, restores what globs dropped) → `!glob` exclude → plain-glob include (when any includes exist, an entry must match one). Patterns match the entry's package-relative path or its mod name. `cmd mods add` / `remove` preserve hand-written object entries - they never flatten your filters.

## Enable/Disable a mod

`cmd mods disable <name>` disables a mod without removing it, and `cmd mods enable <name>` enables it again. Both write the project level (`.commandcode/settings.json`) by default, or the user level (`~/.commandcode/settings.json`) with `-g` flag, and take effect on the next session or `/reload`:

```bash
cmd mods disable review-guard        # disable in this project
cmd mods enable review-guard -g      # enable at the user level
```

They work for discovered files, installed packages, and the disable-able built-ins (`provider-copilot`, `update-notice`, `titling`, `learning` etc). The `cmd mods disable` command adds the name to `mods.disabled`; `cmd mods enable` removes it from there and, for an opt-in built-in, adds it to `mods.enabled`:

```json
{"mods": {"disabled": ["review-guard"], "enabled": ["<opt-in-built-in>"]}}
```

Disabling a mod anywhere will keep a mod disabled. Command Code reads the user, project, and project-local (`.commandcode/settings.local.json`) settings file, and a `mods.disabled` entry in any of them keeps the mod off, even when another file enables it. So when another settings file still disables the mod you enable, the command does not report success: it names that file, gives the one fix, and exits 1 so a script's `&&` chain stops:

```bash
$ cmd mods enable review-guard
⚠ review-guard is still disabled
  Your user settings (~/.commandcode/settings.json) disable it.
  To enable it at the user level, run: cmd mods enable review-guard -g
```

A disable in `settings.local.json` has to be removed by hand; no command writes to that file.

**A project you have not used yet:** Project settings are read only once Command Code has run in that project, the same gate project mods follow. Enabling or disabling there still saves the setting, which applies once you start Command Code in the project; use `-g` to apply it at the user level right away.

## Trust and safety

- **There is no sandbox** - a mod is arbitrary code; install packages you trust.
- **Project mods are trust-gated** like project skills: they load only after the workspace trust prompt. User-scope and `--mod` mods always load.
- **Package installs run npm with `--ignore-scripts`** - mods are jiti-loaded TypeScript; they need no build step, so lifecycle scripts are pure attack surface.
- **Print mode loads user-scope and `--mod` mods only.** Project mods stay out of headless runs because print never shows a trust prompt; pass `--dangerously-skip-permissions` to opt a repo's own mods into a headless run (CI).
