# ai-skillet contributor guidance

Keep the CLI synchronous and library-owned. `src/main.rs` parses process arguments and maps errors to exit codes, while
behavior belongs in `src/lib.rs` and focused modules.

The supported public surface is `map`, `doctor`, `doctor --dependencies-only`, and `--version`. Preserve the documented
exit codes and deterministic text, JSON, and DOT output contracts.

Use the nightly minimal Rust toolchain configured in `toolkit/`. Before proposing a change, run the narrowest relevant
locked Cargo check. Keep macOS and Linux compatibility. Do not add runtime services, plugin systems, config files, shell
hooks, or completion generation without an explicit product decision.

From `toolkit/`, `cargo test -p ai-skillet --locked` is the focused package gate and `just rust-check` is the aggregate
Rust gate. `just install-cli` installs every CLI supported on this OS under `~/.local`. Do not run it for ordinary
verification.

## CLI reference

`ai-skillet` inspects and maintains catalogs of agent skills.

### Status

ai-skillet provides synchronous, no-network `map` and `doctor` engines. JSON reports use the clean Rust schema
version 1. The schema preserves the Python tools' consumer contracts, but output is not byte-compatible with the Python
implementation.

### Commands

```text
ai-skillet map [OPTIONS]
ai-skillet doctor [OPTIONS]
ai-skillet --version
```

`doctor --dependencies-only` limits diagnostics to skill-dependency declarations. Repeatable `doctor --skill <NAME>`
filters diagnostics and safe fixes by canonical skill directory name.

A catalog root that exposes `skills/` must provide a `README.md` with an exact `## Skills` section and a Markdown table.
The required first column is `Skill` and lists every active skill name. Additional columns are optional, and the
inventory validator ignores them. Conventional installed roots named `.agents`, `.claude`, or `.codex` do not require a
catalog README inventory.

Skills in ordinary source-catalog `skills/<name>` paths must provide `agents/openai.yaml`. Conventional installed
exposures beneath `.agents/skills`, `.claude/skills`, or `.codex/skills` may omit that file. When an installed exposure
provides it, doctor still validates the extended `policy.allow_implicit_invocation` contract and can safely update a
mismatch.

### Map ignore policy

The `--help` epilogues summarize defaults and exit codes for agents. Keep them aligned with this section and
`src/exclusions.rs`, which owns the exact lists.

- ai-skillet skips dependency and build directories (`.git`, `node_modules`, `vendor`, `.venv`, `target`, `dist`,
  `build`, `out`, `.next`, `coverage`) everywhere, including tree hashing.
- Agent state is skipped only beneath `.claude/` or `.codex/`: Claude transcripts, plans, file history, tasks, caches,
  logs, and history, plus Codex sessions, threads, history, logs, caches, SQLite state, and backups. Authored skills
  under `.claude/skills/` and `.codex/skills/` stay scannable, and project directories named `plans`, `sessions`, or
  `backups` elsewhere are still scanned.
- The default `$HOME` scan additionally skips `~/Library` and `~/.Trash` (macOS privacy protections make traversal fail
  partway), the agent homes `~/.agents`, `~/.claude`, `~/.codex`, and `~/.local/state/skills` (installed copies and
  state are noise relative to authored sources), package-manager and toolchain caches, and the known catalog source
  checkouts (`--include-catalog-sources` keeps them). An explicit `--root` is never excluded this way, so pass one of
  these paths directly to audit it.
- `--portfolio-root` selects the containing Git repository plus present `~/.agents/skills` and `~/.claude/skills` roots
  as explicit roots. It follows a symlink only when it is a direct skill-directory entry under a recognized `skills`,
  `.agents/skills`, `.claude/skills`, or `.codex/skills` root. Tree hashing records nested symlink targets without
  following them.

### Doctor validation contract

`ai-skillet doctor --root <skill-or-catalog-root>` is the canonical deterministic, offline local validator for the
supported extended skill dialect. For an explicit targeted audit, pass a catalog root with one or more filters:

```sh
ai-skillet doctor --root '.agents/skills' --skill 'land-search'
```

A direct `skills/<name>` root is shorthand for auditing only that directory while resolving bare dependencies against
sibling skills in the owning `skills` directory. This applies equally to source catalogs and `.agents`, `.claude`, or
`.codex` installed exposures, including directory symlinks. A standalone skill outside a `skills/<name>` layout does not
infer an owning catalog. Bare dependencies must still resolve from explicitly supplied roots. Targeted audits omit
catalog-wide README inventory diagnostics, and `--fix-safe` modifies only selected skills.

Doctor accepts one union of top-level fields:

- Portable [Agent Skills](https://agentskills.io/specification): `name`, `description`, `license`, `compatibility`,
  `metadata`, and `allowed-tools`.
- [Claude Code extensions](https://code.claude.com/docs/en/skills#frontmatter-reference): `when_to_use`,
  `argument-hint`, `arguments`, `disable-model-invocation`, `user-invocable`, `disallowed-tools`, `model`, `effort`,
  `context`, `agent`, `background`, `hooks`, `paths`, and `shell`.
- Repository extensions: `coordination` and `skill-dependencies`.

Unknown top-level fields are errors. `metadata` must be a string-to-string mapping. `metadata.install-targets`
additionally accepts only `claude-code`, `codex`, or `claude-code codex`.

Tool, argument, and path fields accept a string or a list of strings. `hooks` must be a mapping. Claude Boolean fields
accept `true`/`false`, `yes`/`no`, `on`/`off`, or `1`/`0`. Doctor does not coerce other YAML shapes.

`context` accepts only `fork`. `effort` accepts `low`, `medium`, `high`, `xhigh`, or `max`. `shell` accepts `bash` or
`powershell`. `agent` and `background` require `context: fork`.

`coordination: exempt` requires this exact sentence in ordinary Markdown body prose:

```text
This skill is coordination-exempt: skip the ai-coord gate for its declared work.
```

Inline code, fenced or indented code, blockquotes, and clearly headed `Example` or `Examples` sections do not count as
declarations and do not trigger missing-frontmatter errors.

New schema diagnostics use these stable codes:

- Unknown field: `FRONTMATTER_UNKNOWN_FIELD`.
- Invalid types: `LICENSE_INVALID_TYPE`, `ALLOWED_TOOLS_INVALID_TYPE`, `WHEN_TO_USE_INVALID_TYPE`,
  `ARGUMENTS_INVALID_TYPE`, `DISALLOWED_TOOLS_INVALID_TYPE`, `MODEL_INVALID_TYPE`, `EFFORT_INVALID_TYPE`,
  `BACKGROUND_INVALID_TYPE`, `HOOKS_INVALID_TYPE`, `PATHS_INVALID_TYPE`, `SHELL_INVALID_TYPE`, and
  `METADATA_VALUE_INVALID_TYPE`. Existing field-specific type codes remain unchanged.
- Invalid values: `EFFORT_INVALID_VALUE` and `SHELL_INVALID_VALUE`, alongside the retained compatibility, context,
  coordination, and install-target codes.
- Cross-field errors: `AGENT_CONTEXT_REQUIRED` and `BACKGROUND_CONTEXT_REQUIRED`.
- Explicit defaults: `disable-model-invocation: false` and `user-invocable: true` restate the effective defaults and are
  accepted without findings, so catalogs that require explicit booleans stay clean.

These diagnostics add findings without changing JSON schema version 1, deterministic finding order, exit codes, or the
existing `--fix-safe` boundary.

### Conformance contract

| Area         | Required contract                                                                                                                                                              | Intentional version 1 behavior                                                                                       |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| CLI          | Usage and operational errors exit 2. Doctor findings exit 1. Safe-fix failures exit 3.                                                                                         | Operational errors appear once, without a generic duplicate.                                                         |
| Map output   | Text, JSON, and DOT are deterministic. Skills, roots, edges, duplicates, unresolved references, hashes, and portfolio exposures remain available.                              | Declared and inferred evidence remain independent. ai-skillet warns for missing filters and returns an empty report. |
| Discovery    | ai-skillet supports explicit roots, broad-root exclusions, portfolio roots, ignored entries requested directly, symlink exposures, and paths containing newlines.              | Local dependencies resolve across every scanned root.                                                                |
| Streaming    | The scanner uses bounded buffers for large files and newline-free lines. Snippets contain bounded match text.                                                                  | No ripgrep child process or cancellation lifecycle is required.                                                      |
| Doctor       | The validator checks the complete supported frontmatter union plus every metadata, dependency, coordination, resource, README, prompt-hygiene, and CLI-version finding family. | YAML and OpenAI policy diagnostics are structural. Safe fixes are isolated and atomic.                               |
| Dependencies | The validator checks bare and external identifiers, uniqueness, self-reference, resolution, and target-name ordering.                                                          | External owner/repository case is preserved. Repository names ending in `.git` are rejected.                         |

The integration tests in `tests/conformance.rs`, `tests/map.rs`, `tests/doctor.rs`, and `tests/catalog.rs` are the
executable contract. Python captures are migration evidence, not golden output fixtures.

### Development

The toolkit workspace selects nightly Rust with the minimal profile plus `clippy` and `rustfmt`.

```sh
cargo test -p ai-skillet --locked
just rust-check
```

### License

MIT. See [LICENSE.md](../LICENSE.md).
