# Configuration

Complete reference for `promptscript.yaml`.

## File Location

The CLI looks for configuration in this order:

1. `--config` command line option
2. `PROMPTSCRIPT_CONFIG` environment variable
3. `promptscript.yaml` in current directory
4. `promptscript.yml` in current directory
5. `.promptscriptrc.yaml` in current directory
6. `.promptscriptrc.yml` in current directory

## Full Configuration

```yaml
id: full-example
syntax: '1.4.0'

# Anonymous aggregate usage telemetry
telemetry: true

# ===================
# Input Configuration
# ===================
input:
  # Entry point file (required)
  entry: .promptscript/project.prs

  # Additional files to include
  include:
    - '.promptscript/**/*.prs'

  # Files to exclude
  exclude:
    - '**/node_modules/**'
    - '**/*.test.prs'

# ======================
# Registry Configuration
# ======================
registry:
  # Local registry path
  path: ./registry

  # Or remote HTTP registry URL
  # url: https://registry.example.com/promptscript

  # Or Git repository (recommended for teams)
  # git:
  #   url: https://github.com/org/promptscript-registry.git
  #   fallbackUrl: git@github.com:org/promptscript-registry.git  # SSH fallback
  #   ref: main                    # branch, tag, or commit
  #   path: registry/              # subdirectory in repo
  #   auth:
  #     type: token                # or "ssh"
  #     tokenEnvVar: GITHUB_TOKEN  # env var with PAT

  # Authentication for HTTP registries
  # auth:
  #   type: bearer
  #   tokenEnvVar: REGISTRY_TOKEN

  # Cache settings
  cache:
    enabled: true
    ttl: 3600000 # milliseconds (1 hour)

# =====================
# Target Configuration
# =====================
targets:
  # List format (matches prs init output)
  - github
  - claude
  - cursor
  - factory:
      version: multifile
      rulesMode: split

# =============================
# Named Build Profiles
# =============================
builds:
  plugin-factory:
    # Optional entry override for this build only
    entry: .promptscript/project.prs

    # Optional output directory for this build only
    output: plugins/logstrip

    # Optional target list for this build only
    targets:
      - factory:
          version: multifile
          skillBaseDir: skills
          includeSkills:
            - logstrip

# =========================
# Validation Configuration
# =========================
validation:
  # Require named guards to be present
  requiredGuards:
    - security

  # Maximum recursion depth for guard dependencies
  guardRequiresDepth: 3

  # Custom rules
  rules:
    empty-block: warning

  # Scan imported (registry cache / vendored) content with
  # heuristic rules. Off by default.
  scanExternalContent: false

  # Per-import rule exclusions bound to the lockfile commit.
  # excludes:
  #   - import: github.com/org/repo
  #     commit: <sha from promptscript.lock>
  #     rules: [blocked-patterns, authority-injection]

  # Pattern sources exempt from blocked-patterns detection.
  # allowedPatterns:
  #   - 'bypass\s+(your\s+)?(rules|restrictions)'

# ===================
# Model Configuration
# ===================
models:
  # Models the instructions are written and tested for (PS041)
  supported:
    - opus
    - gpt-5.3-codex

  # Custom model profiles, or overrides of built-in profiles
  # profiles:
  #   claude-opus-9:
  #     provider: anthropic
  #     family: claude-opus
  #     version: '9'
  #     displayName: Claude Opus 9

# ====================
# Watch Configuration
# ====================
watch:
  # Files to watch
  include:
    - '.promptscript/**/*.prs'
    - 'registry/**/*.prs'

  # Files to ignore
  exclude:
    - '**/node_modules/**'
    - '**/.git/**'

  # Debounce delay (ms)
  debounce: 300

  # Clear console on rebuild
  clearScreen: true

# =====================
# Output Configuration
# =====================
output:
  # Base directory for all outputs
  baseDir: '.'

  # File header comment
  header: |
    # Auto-generated by PromptScript
    # Do not edit manually

  # Overwrite existing files
  overwrite: true

# =========================
# Formatting Configuration
# =========================
formatting:
  # Auto-detect .prettierrc in project
  prettier: true

  # Or specify explicit path to .prettierrc
  # prettier: "./config/.prettierrc"

  # Or specify options directly
  # proseWrap: always    # 'always' | 'never' | 'preserve'
  # tabWidth: 4          # Number of spaces per indent
  # printWidth: 100      # Max line width for prose wrapping

# =============================
# Universal Directory & Skills
# =============================

# Universal directory for auto-discovering skills and commands.
# - true or omitted: Use default '.agents/' directory
# - false: Disable universal directory discovery
# - string: Custom path (relative to project root)
universalDir: true # default: '.agents'

# Include the bundled PromptScript language skill in compilation output.
# When enabled, a SKILL.md that teaches AI agents how to work with .prs
# files is added when the formatter exposes a bundled-skill output path.
includePromptScriptSkill: true # default: true
```

## Configuration Sections

### Top-level fields

| Field                      | Type                    | Required | Description                                     |
| -------------------------- | ----------------------- | -------- | ----------------------------------------------- |
| `id`                       | string                  | Yes      | Project identifier                              |
| `syntax`                   | string                  | Yes      | PromptScript syntax version                     |
| `targets`                  | `TargetEntry[]`         | Yes\*    | Output targets and target-specific options      |
| `description`              | string                  | No       | Project description                             |
| `telemetry`                | boolean                 | No       | Enable anonymous aggregate usage telemetry      |
| `extends`                  | string                  | No       | Base configuration file to merge                |
| `inherit`                  | string                  | No       | Registry configuration inherited by the project |
| `input`                    | object                  | No       | Entry point and source file globs               |
| `registry`                 | object                  | No       | Default local, HTTP, or Git registry            |
| `registries`               | object                  | No       | Named Git registry aliases                      |
| `watch`                    | object                  | No       | Watch mode behavior                             |
| `output`                   | object                  | No       | Global output directory, header, and overwrite  |
| `builds`                   | object                  | No       | Named build profiles                            |
| `skillTargets`             | `Record<string,string>` | No       | Per-source skill output directories             |
| `formatting`               | object                  | No       | Markdown formatting settings                    |
| `customConventions`        | object                  | No       | User-defined output conventions                 |
| `universalDir`             | string or boolean       | No       | Universal skills and commands directory         |
| `includePromptScriptSkill` | boolean                 | No       | Include the bundled language skill              |
| `validation`               | object                  | No       | Guard and validation rule settings              |
| `models`                   | object                  | No       | Supported model set and model profiles          |
| `policies`                 | array                   | No       | Policies for skill extensions and prompts       |
| `releases`                 | object                  | No       | Release environments for prompt bundles         |
| `eval`                     | object                  | No       | Providers and limits of `prs eval`              |
| `governance`               | object                  | No       | Admins, approval roles and CODEOWNERS settings  |

`id` and `syntax` are required even when `input.entry` uses its default.

\* `targets` is required unless every target is declared inside a named [build
profile](#builds). Such builds-only projects compile with `prs compile --build <name>` or
`prs compile --all-builds`; a bare `prs compile` fails with the list of available profiles because
there is no default target list to use.

See the [Policy Engine guide](https://getpromptscript.dev/guides/policy-engine/index.md) for `policies` syntax.

### telemetry

Anonymous aggregate usage telemetry is enabled by default. Set `telemetry:
false` to disable every telemetry network request for this project:

```yaml
telemetry: false
```

Project and user opt-outs are hard vetoes. Environment opt-in cannot override
them. See [Anonymous Usage Telemetry](https://getpromptscript.dev/reference/telemetry/index.md) for collected fields,
delivery behavior, and all opt-out controls.

### input

Configures source file handling.

```yaml
input:
  entry: .promptscript/project.prs
  include:
    - '.promptscript/**/*.prs'
  exclude:
    - '**/*.test.prs'
```

| Field     | Type     | Default        | Description              |
| --------- | -------- | -------------- | ------------------------ |
| `entry`   | string   | Required       | Main entry point         |
| `include` | string[] | `["**/*.prs"]` | Glob patterns to include |
| `exclude` | string[] | `[]`           | Glob patterns to exclude |

### registry

Configures the inheritance registry. PromptScript supports three registry types:

1. **Local filesystem** - for development or monorepos
2. **HTTP/HTTPS** - for simple remote registries
3. **Git repository** - recommended for teams (supports versioning via tags)

#### Local Registry

```yaml
registry:
  path: ./registry
```

#### HTTP Registry

```yaml
registry:
  url: https://registry.example.com/promptscript
  auth:
    type: bearer
    tokenEnvVar: REGISTRY_TOKEN
```

#### Git Registry

```yaml
registry:
  git:
    url: https://github.com/org/promptscript-registry.git
    ref: main
    path: registry/
    auth:
      type: token
      tokenEnvVar: GITHUB_TOKEN
  cache:
    enabled: true
    ttl: 3600000
```

**Registry Fields:**

| Field                  | Type    | Default   | Description                          |
| ---------------------- | ------- | --------- | ------------------------------------ |
| `path`                 | string  | -         | Local registry path                  |
| `url`                  | string  | -         | HTTP registry URL                    |
| `git.url`              | string  | -         | Git repository URL                   |
| `git.fallbackUrl`      | string  | -         | Fallback URL (tried on auth failure) |
| `git.ref`              | string  | `main`    | Branch, tag, or commit               |
| `git.path`             | string  | -         | Subdirectory within the repo         |
| `git.timeout`          | number  | `60000`   | Git operation timeout (ms)           |
| `git.auth.type`        | string  | -         | Auth type: `token` or `ssh`          |
| `git.auth.token`       | string  | -         | Personal access token (direct)       |
| `git.auth.tokenEnvVar` | string  | -         | Env var containing the token         |
| `git.auth.sshKeyPath`  | string  | -         | Path to SSH key (for SSH auth)       |
| `cache.enabled`        | boolean | `true`    | Enable caching                       |
| `cache.ttl`            | number  | `3600000` | Cache TTL in milliseconds            |

:::tip[Version-tagged imports]
With Git registry, you can pin imports to specific versions using Git tags:
:::

    ```
    @inherit @company/base@1.0.0
    @use @company/security@2.1.0 as sec
    ```

    This checkouts the specified tag before fetching the file.

### registries

Defines short aliases for Git repository URLs. Aliases are used as scope prefixes in `@use` and `@inherit` paths, resolving to their full Git URL at compile time.

#### Simple Form

```yaml
registries:
  '@company': github.com/acme/promptscript-base
  '@team': github.com/acme/team-frontend
  '@shared': gitlab.com/acme/shared-libs
```

#### Extended Form

```yaml
registries:
  '@company':
    url: github.com/acme/promptscript-base
  '@internal':
    url: git@gitlab.internal.acme.com:platform/prs-registry
    root: packages/promptscript
```

#### Extended Form (with fallback URL)

When registry files reference HTTPS URLs but you authenticate via SSH (or vice
versa), set `fallbackUrl` so the clone is retried with the alternative URL
on auth failures:

```yaml
registries:
  '@acme':
    url: https://github.com/acme/standards.git
    fallbackUrl: git@github.com:acme/standards.git
  '@internal':
    url: https://gitlab.internal.com/team/monorepo.git
    fallbackUrl: git@gitlab.internal.com:team/monorepo.git
    root: packages/promptscript
```

**Alias Fields:**

| Field           | Type   | Description                                    |
| --------------- | ------ | ---------------------------------------------- |
| (simple string) | string | Bare Git host path, e.g. `github.com/org/repo` |
| `url`           | string | Bare Git host path (extended form)             |
| `fallbackUrl`   | string | Fallback Git URL (tried on auth failure)       |
| `root`          | string | Base path within the repository                |
| `timeout`       | number | Git operation timeout in ms (default 60000)    |

#### Three-Level Merge

The `registries` field is resolved by merging three sources in priority order:

| Priority | Source                          | Scope         |
| -------- | ------------------------------- | ------------- |
| Highest  | `promptscript.yaml` in project  | Project-level |
| Middle   | `~/.promptscript/config.yaml`   | User-level    |
| Lowest   | `/etc/promptscript/config.yaml` | System-level  |

Project aliases override user aliases, which override system aliases. This allows IT to provision company-wide aliases (system level) while teams or projects can add or override specific aliases. On Windows the system file is `%ProgramData%\promptscript\config.yaml`.

#### User-Level Config (`~/.promptscript/config.yaml`)

```yaml
# ~/.promptscript/config.yaml
version: '1'

registries:
  '@company': github.com/acme/promptscript-base
  '@shared': github.com/acme/shared-libs

registry:
  git:
    url: https://github.com/acme/promptscript-base.git
    auth:
      type: token
      tokenEnvVar: GITHUB_TOKEN
```

Setting aliases in user config makes them available in every project on the machine without adding
them to individual `promptscript.yaml` files. Alias entries do not contain credentials. Use
`registry.git.auth`, SSH configuration, or the Git credential helper for authentication.

#### Usage in .prs Files

Once aliases are configured, use them in any import path:

```promptscript
# Resolves @company to github.com/acme/promptscript-base
@inherit @company/@org/base

# Versioned alias import
@use @company/@stacks/react@^1.0.0
```

<!-- playground-link-start -->
<a href="https://getpromptscript.dev/playground/?s=N4IgZglgNgpgziAXAbVABwIYBcAWSQwAeGAtmrAHRoBOCANCAMYD2AdljO-gMQAEAAiRhYMvDABNxMcbzjNeuCHF5hqGAOZD2vFmWjxebXhCzLmAd1bGruGL3IYAnuurMArq3EAdVoOGjgH15jcUReLwJiMlgIoNlHdgxCMIiARgoANgoABljWAF8fHz4AJXhmKAA3A35dTFZHBXl1Exw3ACMKXQB6DEYhbppmMlNGagg0LABadow4GB9+CFYcGHGsATqMBu7+Zmp1btn5otY+ADU1uAg2aTEoCDnjMn2sRbd5zeH6x124EUYAGs4N1qDA+lh+AA9dLZHIgfJ0dDYPCIEC1b7bX57A5HOYwKi0EAMFjsThYHi8ADKbjQ5AgdzkClWvCIpHIdmoHmUqzBFF4ADlmBtMNQNswwMy7OJmIw3FoRFgbqw6GJPLxWMKxLwwRgoPY+oCNATFkIRLxAlYQil0VsdjjDscFiA4nAEiJkuEQOksrkXVaSBBCMswlguQsCot-ttxBhqOJlJbgiwpGFkBEAApQPowHAVKTUWQiTxx7wgAC6cQ4-2W6jTmezjFz+bWRZjpYilcjvjB-3GjCVbETcSmXqzObzUALOvgYYgA+VeXyCKRIEwuHwGLIWL+AOBoPBA+hsJyhPoTDYHC4aL4NLpD0Z8lsrKiHJ13N4vIJgq1ovFkufGU5QVbBlVVGMNS1URdX1TAgWNChTX8C04ggUIvS3H5d0NEFdSPGFTz9V13SSG0fRyPJgkDYNWFDcMfEKXxoxLeNhytFMYHrEBxybSdp2Y2N407KtZ1rLieObKdWwEjsKwYxZeznBchxQq1RwbCcW0LRT+0HVglwRcsGHJahHHwNlogJGhz2qWhlXwVIESAA" target="_blank" rel="noopener noreferrer">
  <img src="https://img.shields.io/badge/Try_in-Playground-blue?style=flat-square" alt="Try in Playground" />
</a>
<!-- playground-link-end -->

### targets

Configures output targets. Targets can be specified as a simple list of names or with detailed configuration.

**List format (recommended, matches `prs init` output):**

```yaml
targets:
  - github
  - claude
  - cursor
```

**Object format (advanced, for custom options):**

```yaml
targets:
  - github:
      convention: xml
      output: custom/path/instructions.md
  - claude:
      convention: markdown
  - cursor:
      version: legacy
```

**Available Targets:**

PromptScript ships 50 coding agent targets and two prompt targets. See
[Target Platforms](https://getpromptscript.dev/features/target-platforms/index.md) for platform families and
[Supported Formatters](https://getpromptscript.dev/reference/formatters/index.md) for the full capability matrix.

| Target         | Default Output                    | Default Convention | Supported Versions                             |
| -------------- | --------------------------------- | ------------------ | ---------------------------------------------- |
| `github`       | `.github/copilot-instructions.md` | `markdown`         | simple / multifile / full                      |
| `claude`       | `CLAUDE.md`                       | `markdown`         | simple / multifile / full                      |
| `cursor`       | `.cursor/rules/project.mdc`       | `markdown`         | modern / multifile / legacy / agents-md / full |
| `antigravity`  | `.agent/rules/project.md`         | `markdown`         | simple / frontmatter / agents-md               |
| `factory`      | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `opencode`     | `OPENCODE.md`                     | `markdown`         | simple / multifile / full                      |
| `gemini`       | `GEMINI.md`                       | `markdown`         | simple / multifile / full                      |
| `codex`        | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `grok`         | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `windsurf`     | `.windsurf/rules/project.md`      | `markdown`         | simple / multifile / full                      |
| `cline`        | `.clinerules`                     | `markdown`         | simple / multifile / full                      |
| `roo`          | `.roorules`                       | `markdown`         | simple / multifile / full                      |
| `continue`     | `.continue/rules/project.md`      | `markdown`         | simple / multifile / full                      |
| `amp`          | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `augment`      | `.augment/rules/project.md`       | `markdown`         | simple / multifile / full                      |
| `goose`        | `.goosehints`                     | `markdown`         | simple / multifile / full                      |
| `kilo`         | `.kilocode/rules/project.md`      | `markdown`         | simple / multifile / full                      |
| `trae`         | `.trae/rules/project_rules.md`    | `markdown`         | simple / multifile / full                      |
| `junie`        | `.junie/guidelines.md`            | `markdown`         | simple / multifile / full                      |
| `kiro`         | `.kiro/steering/project.md`       | `markdown`         | simple / multifile / full                      |
| `cortex`       | `.cortex/rules/project.md`        | `markdown`         | simple / multifile / full                      |
| `crush`        | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `command-code` | `.commandcode/rules/project.md`   | `markdown`         | simple / multifile / full                      |
| `kode`         | `.kode/rules/project.md`          | `markdown`         | simple / multifile / full                      |
| `mcpjam`       | `.mcpjam/rules/project.md`        | `markdown`         | simple / multifile / full                      |
| `mistral-vibe` | `.vibe/rules/project.md`          | `markdown`         | simple / multifile / full                      |
| `mux`          | `.mux/rules/project.md`           | `markdown`         | simple / multifile / full                      |
| `openhands`    | `.openhands/rules/project.md`     | `markdown`         | simple / multifile / full                      |
| `pi`           | `.pi/rules/project.md`            | `markdown`         | simple / multifile / full                      |
| `qoder`        | `.qoder/rules/project.md`         | `markdown`         | simple / multifile / full                      |
| `qwen-code`    | `.qwen/rules/project.md`          | `markdown`         | simple / multifile / full                      |
| `zencoder`     | `.zencoder/rules/project.md`      | `markdown`         | simple / multifile / full                      |
| `neovate`      | `.neovate/rules/project.md`       | `markdown`         | simple / multifile / full                      |
| `pochi`        | `.pochi/rules/project.md`         | `markdown`         | simple / multifile / full                      |
| `adal`         | `.adal/rules/project.md`          | `markdown`         | simple / multifile / full                      |
| `iflow`        | `.iflow/rules/project.md`         | `markdown`         | simple / multifile / full                      |
| `openclaw`     | `INSTRUCTIONS.md`                 | `markdown`         | simple / multifile / full                      |
| `codebuddy`    | `.codebuddy/rules/project.md`     | `markdown`         | simple / multifile / full                      |
| `gitlab-duo`   | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `aider`        | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `amazon-q`     | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `warp`         | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `zed`          | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `jules`        | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `devin`        | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `kimi`         | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `mimo`         | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `deep-agents`  | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `forgecode`    | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `hermes`       | `AGENTS.md`                       | `markdown`         | simple / multifile / full                      |
| `bundle`       | `prompts/index.json`              | -                  | v1                                             |
| `prompt-text`  | `prompts/README.md`               | -                  | v1                                             |

`bundle` and `prompt-text` compile `@prompts` entries for applications, not instructions for a
coding agent. They are opt-in: no default selection includes them, so name them in `targets`. A
target `output` moves the primary file, and the other files sit next to it. The target owns that
directory, so `output` must name one. They write nothing for a program without prompts and need
syntax `1.6.0`. See [Application Prompts](https://getpromptscript.dev/guides/application-prompts/index.md).

### MCP, Hooks, and Plugins Support

The `@mcpServers`, `@hooks`, and `@plugins` blocks (syntax 1.4.0+) are emitted to target-native config files. Not all targets support all features.

| Target        | MCP Config File                  | Hooks Config                      | Plugins Config          |
| ------------- | -------------------------------- | --------------------------------- | ----------------------- |
| `claude`      | `.mcp.json`                      | `.claude/settings.json`           | -                       |
| `cursor`      | `.cursor/mcp.json`               | `.cursor/hooks.json`              | `.cursor/plugins.json`  |
| `factory`     | `.factory/mcp.json`              | `.factory/hooks.json`             | `.factory/plugins.json` |
| `codex`       | `.codex/mcp.json`                | `.codex/hooks.json`               | `.codex/plugins.json`   |
| `grok`        | `.mcp.json` (via Claude)         | `.grok/hooks/promptscript.json`   | `.grok/plugins.json`    |
| `github`      | `.vscode/mcp.json`               | `.github/hooks/promptscript.json` | -                       |
| `antigravity` | `.agents/mcp_config.json`        | -                                 | -                       |
| `gemini`      | `.gemini/mcp_config.json`        | `.gemini/settings.json`           | -                       |
| `windsurf`    | `.windsurf/mcp_config.json`      | `.windsurf/hooks.json`            | -                       |
| `cline`       | `.cline/cline_mcp_settings.json` | -                                 | -                       |
| `roo`         | `.roo/mcp_settings.json`         | -                                 | -                       |
| `continue`    | `.continue/config.json`          | -                                 | -                       |
| `goose`       | `.goose/mcp_config.json`         | -                                 | -                       |
| `kilo`        | `.kilocode/mcp_settings.json`    | -                                 | -                       |
| `openhands`   | `.openhands/mcp_config.toml`     | -                                 | -                       |
| `qwen-code`   | `.qwen/mcp.json`                 | -                                 | -                       |
| `zed`         | `.zed/settings.json`             | -                                 | -                       |
| `crush`       | `.crush/mcp.json`                | -                                 | -                       |

Hook files are emitted in `multifile` and `full` target modes. `simple` keeps
its single-file contract and reports a compatibility warning when `@hooks` is
present.

MCP and plugin files depend on the mode too. `github`, `claude`, `cursor`, and
`grok` write MCP config only in `full` mode, `antigravity` in every mode, and the
other MCP targets in `multifile` and `full`. `cursor` and `grok` write plugin
config only in `full` mode, `factory` and `codex` in `multifile` and `full`. A
target or mode that cannot write `@skills`, `@mcpServers`, or `@plugins` reports
`PS4002` instead of dropping the block silently.

**Cursor Versions:**

| Version     | Output Files                                                                                        |
| ----------- | --------------------------------------------------------------------------------------------------- |
| `modern`    | `.cursor/rules/project.mdc` + `.cursor/commands/*.md` for multi-line shortcuts                      |
| `multifile` | Main + `.cursor/rules/*.mdc` (glob-based) + `.cursor/rules/shortcuts.mdc` + `.cursor/commands/*.md` |
| `legacy`    | `.cursorrules` (deprecated, no slash commands)                                                      |
| `agents-md` | `AGENTS.md` (Cursor 2.4+, no frontmatter required)                                                  |
| `full`      | Multifile + `.agents/skills/<name>/SKILL.md` + `.cursor/agents/<name>.md` (Cursor 2.5+)             |

:::tip[Cursor Slash Commands]
Multi-line `@shortcuts` are automatically converted to executable slash commands in `.cursor/commands/`.
Type `/` in Cursor chat (1.6+) to invoke them. See [Language Reference](https://getpromptscript.dev/reference/language/index.md#cursor-slash-commands-16).
:::

:::note[Cursor Identity Handling]
The Cursor formatter handles `@identity` blocks intelligently:
:::

    - If the identity starts with "You are...", the **full content** is used as the intro
    - Otherwise, a generated intro like "You are working on {project}" is created
    - Multiline strings are automatically dedented to remove source indentation

    This means your `@identity` content appears exactly as written (without extra indentation).

**Target Configuration:**

Targets can be specified as simple names (list format) or with configuration (object format).
The list format is recommended for most projects and matches `prs init` output:

```yaml
targets:
  # List format (recommended)
  - github
  - claude
  - cursor
  - antigravity

  # Object format (for custom options)
  - github:
      convention: xml
      output: custom/path/instructions.md
  - claude:
      convention: markdown

  # With version for format variants
  - cursor:
      version: legacy # Use .cursorrules for Cursor < 0.45
  - antigravity:
      version: frontmatter # Use YAML frontmatter with activation types

  # GitHub Copilot versions
  - github:
      version: simple # Single file
  - github:
      version: multifile # Main + path-specific instructions + prompts
  - github:
      version: full # Multifile + skills + AGENTS.md (default)

  # Claude Code versions
  - claude:
      version: simple # Single CLAUDE.md
  - claude:
      version: multifile # Main + modular rules in .claude/rules/
  - claude:
      version: full # Multifile + skills + CLAUDE.local.md (default)

  # Factory AI versions and split always-on rules
  - factory:
      version: multifile
      rulesMode: split

  # OpenCode versions
  - opencode:
      version: simple # Single OPENCODE.md
  - opencode:
      version: multifile # Main + commands
  - opencode:
      version: full # Multifile + skills + agents

  # Gemini CLI versions
  - gemini:
      version: simple # Single GEMINI.md
  - gemini:
      version: multifile # Main + commands + skills
  - gemini:
      version: full # Same as multifile (no agent concept)
```

**GitHub Copilot Versions:**

| Version     | Output Files                                                                    |
| ----------- | ------------------------------------------------------------------------------- |
| `simple`    | `.github/copilot-instructions.md` (single file)                                 |
| `multifile` | Main + `.github/instructions/*.instructions.md` + `.github/prompts/*.prompt.md` |
| `full`      | Multifile + `.github/skills/<name>/SKILL.md` + `AGENTS.md`                      |

**Claude Code Versions:**

| Version     | Output Files                                                                                      |
| ----------- | ------------------------------------------------------------------------------------------------- |
| `simple`    | `CLAUDE.md` (single file)                                                                         |
| `multifile` | Main + `.claude/rules/*.md` with path-specific rules (from `@guards.globs`)                       |
| `full`      | Multifile + `.claude/skills/<name>/SKILL.md` (from `@skills`) + `CLAUDE.local.md` (from `@local`) |

**Factory AI Versions:**

| Version     | Output Files                                               |
| ----------- | ---------------------------------------------------------- |
| `simple`    | `AGENTS.md` only                                           |
| `multifile` | `AGENTS.md` + commands + `.factory/skills/<name>/SKILL.md` |
| `full`      | Multifile output + `.factory/droids/<name>.md`             |

Factory defaults to `rulesMode: monolith`, which preserves the existing
single-file rules content in `AGENTS.md`. Set `rulesMode: split` with
`multifile` or `full` to emit always-on files under `.factory/rules/` and a
discoverability index in `AGENTS.md`. `rulesMode: split` with `simple` is an
error.

**OpenCode Versions:**

| Version     | Output Files                                                                  |
| ----------- | ----------------------------------------------------------------------------- |
| `simple`    | `OPENCODE.md` (single file)                                                   |
| `multifile` | Main + `.opencode/commands/<name>.md`                                         |
| `full`      | Multifile + `.opencode/skills/<name>/SKILL.md` + `.opencode/agents/<name>.md` |

**Gemini CLI Versions:**

| Version     | Output Files                                                             |
| ----------- | ------------------------------------------------------------------------ |
| `simple`    | `GEMINI.md` (single file)                                                |
| `multifile` | Main + `.gemini/commands/<name>.toml` + `.agents/skills/<name>/skill.md` |
| `full`      | Same as multifile (Gemini CLI has no agent concept)                      |

**Target Options:**

| Field           | Type                    | Default         | Description                                            |
| --------------- | ----------------------- | --------------- | ------------------------------------------------------ |
| `enabled`       | boolean                 | `true`          | Whether target is enabled                              |
| `output`        | string                  | (see above)     | Custom output path, relative to the output directory   |
| `convention`    | string                  | `markdown`      | Output convention (`xml` or `markdown`)                |
| `version`       | string                  | `full`          | Format version (varies by target, see below)           |
| `rulesMode`     | `monolith` or `split`   | `monolith`      | Factory always-on rule layout                          |
| `skillBaseDir`  | string                  | target-specific | Custom base directory for generated skill files        |
| `includeSkills` | boolean or string array | `true`          | Emit all skills, no skills, or only listed skill names |

:::caution[Output paths stay inside the output directory]
A target `output` that resolves outside the output directory (`../`, or an
absolute path pointing elsewhere, including through a symlink) aborts the
compile before any file is written. Use `--output` or `output.baseDir` to
change where the whole build lands; a base directory outside the project is
allowed and reported as a warning.
:::

:::tip[Disabling Targets]
Setting `enabled: false` skips the target during compilation.
This is equivalent to removing the target from the list.
:::

    ```yaml
    targets:
      - github               # Will compile
      - claude:
          enabled: false     # Will NOT compile (skipped)
      - cursor               # Will compile
    ```

See [Formatters API](https://getpromptscript.dev/api-reference/formatters/src/index.md) for more details.

:::tip[Building skills into plugin or library folders]
Use `skillBaseDir` when you want a target to place generated skills outside
its default directory. For example, this writes Factory skills under
`plugins/logstrip/skills` when combined with a build profile output:
:::

    ```yaml
    builds:
      logstrip-factory:
        output: plugins/logstrip
        targets:
          - factory:
              version: multifile
              skillBaseDir: skills
              includeSkills:
                - logstrip
    ```

    Run it with:

    ```bash
    prs compile --build logstrip-factory
    # or
    prs build logstrip-factory
    ```

**Built-in Conventions:**

| Convention | Section Format                         | Description               |
| ---------- | -------------------------------------- | ------------------------- |
| `markdown` | `## Section Name`                      | Markdown headers          |
| `xml`      | `<section-name>content</section-name>` | XML tags wrapping content |

**Target-Specific Options:**

| Option              | Targets           | Type   | Description                                                                                  |
| ------------------- | ----------------- | ------ | -------------------------------------------------------------------------------------------- |
| `maxThreads`        | `codex`           | number | Max parallel agent threads (positive integer, maps to .codex/config.toml)                    |
| `maxDepth`          | `codex`           | number | Max nesting depth for agent delegation (positive integer)                                    |
| `agentsFile`        | `codex`           | string | Override agents file name (default: AGENTS.md, scoped builds: AGENTS.override.md)            |
| `agentsFrontmatter` | AGENTS.md targets | string | `experimental` - emit AGENTS.md v1.1 YAML frontmatter (description, tags from @meta)         |
| `autoMode`          | `claude`          | string | Claude auto mode: `acceptEdits`, `plan`, `bypassPermissions` (maps to .claude/settings.json) |

**Codex Native Output:**

The `codex` target emits native Codex agent TOML files in addition to `AGENTS.md`:

```
AGENTS.md                          # Root instructions
.codex/agents/<name>.toml          # Agent definitions (TOML)
.codex/config.toml                 # Project config (maxThreads, maxDepth, agentsFile)
.codex/hooks.json                  # Project hooks
.agents/skills/<name>/SKILL.md     # Skills (interoperable path)
```

Agent field mapping (PRS -> Codex TOML):

| PRS Field            | Codex TOML Field         |
| -------------------- | ------------------------ |
| `content`            | `developer_instructions` |
| `reasoningEffort`    | `model_reasoning_effort` |
| `sandboxMode`        | `sandbox_mode`           |
| `nicknameCandidates` | `nickname_candidates`    |
| `mcpServers`         | `mcp_servers`            |
| `skills`             | `skills.config`          |

### customConventions

Define custom output conventions for specialized formatting needs.

```yaml
id: custom-convention-example
syntax: '1.4.0'

customConventions:
  my-format:
    name: my-format
    description: Custom Markdown headings and fenced code
    section:
      start: '### {{name}}'
      end: ''
      nameTransform: none
      indent: ''
    subsection:
      start: '#### {{name}}'
      end: ''
      nameTransform: none
      indent: ''
    listStyle: bullet
    codeBlockDelimiter: '~~~'

# Use custom convention in target
targets:
  - github:
      convention: my-format
```

**Custom Convention Structure:**

| Field                   | Type   | Description                                        |
| ----------------------- | ------ | -------------------------------------------------- |
| `name`                  | string | Convention identifier                              |
| `description`           | string | Human-readable purpose                             |
| `section.start`         | string | Section opening template, supports `{{name}}`      |
| `section.end`           | string | Optional section closing template                  |
| `section.nameTransform` | string | `kebab-case`, `camelCase`, `PascalCase`, or `none` |
| `section.indent`        | string | Indentation per nesting level                      |
| `subsection`            | object | Optional subsection renderer                       |
| `listStyle`             | string | `dash`, `asterisk`, `bullet`, or `numbered`        |
| `codeBlockDelimiter`    | string | Code fence delimiter                               |
| `rootWrapper`           | object | Optional `start` and `end` templates               |

### builds

Defines named build profiles for compiling a specific entry point to a
specific set of targets and output directories. Build profiles are useful when
one repository needs to generate extra artifacts for subpackages, plugins, or
tool-specific distribution folders without changing the default project build.

```yaml
builds:
  logstrip-factory:
    entry: .promptscript/project.prs
    output: plugins/logstrip
    targets:
      - factory:
          version: multifile
          skillBaseDir: skills
          includeSkills:
            - logstrip
```

Run a profile with either command:

```bash
prs compile --build logstrip-factory
prs build logstrip-factory

# Compile all named build profiles in deterministic order
prs compile --all-builds
```

A profile without its own `targets` key falls back to the top-level `targets` list. When a project
keeps every target inside profiles and omits top-level `targets`, a bare `prs compile` fails and
lists the available profiles instead of compiling nothing. Inspect profile output with
`prs diff --build <name>`.

**Nested AGENTS.md via Build Profiles:**

Build profiles with `output` directories produce scoped AGENTS.md files. This enables nested instruction files for monorepo packages:

```yaml
builds:
  api:
    entry: .promptscript/packages/api.prs
    output: packages/api
    targets:
      - factory
  codex-api-override:
    entry: .promptscript/packages/api-override.prs
    output: packages/api
    targets:
      - codex:
          agentsFile: AGENTS.override.md
```

Output paths are validated against project root - path traversal (`..`) is rejected. Codex-only `AGENTS.override.md` is supported via the `agentsFile` option.

**Build Profile Fields:**

| Field     | Type          | Default          | Description                             |
| --------- | ------------- | ---------------- | --------------------------------------- |
| `entry`   | string        | `input.entry`    | Entry file to compile for this profile  |
| `output`  | string        | `output.baseDir` | Base output directory for this profile  |
| `targets` | TargetEntry[] | `targets`        | Target list to compile for this profile |

CLI flags still take precedence: `--target`/`--format` override the profile's
target list, and `--output` overrides the profile's `output`.

### validation

Configures validation behavior.

```yaml
validation:
  requiredGuards:
    - security
  guardRequiresDepth: 3
  rules:
    empty-block: warning
  scanExternalContent: false
  excludes:
    - import: github.com/cloudflare/skills
      commit: 1a2b3c4d5e6f7890abcdef1234567890abcdef12
      rules: [blocked-patterns, authority-injection]
  allowedPatterns:
    - 'bypass\s+(your\s+)?(rules|restrictions)'
```

| Field                 | Type     | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| --------------------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `requiredGuards`      | string[] | `[]`    | Guards every resolved project must define                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `rules`               | object   | `{}`    | Rule severity overrides                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `guardRequiresDepth`  | number   | `3`     | Maximum guard dependency recursion depth                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `scanExternalContent` | boolean  | `false` | Scan imported (registry cache / vendored) content with heuristic rules. Heuristic findings in third-party skills are skipped by default because the importing project cannot fix them; concrete security findings (decoded payloads, suspicious URLs) always scan.                                                                                                                                                                                                                                                                                                |
| `excludes`            | array    | `[]`    | Per-import rule exclusions bound to the lockfile. Each entry needs `import` (the import source, optionally with a sub-path), `commit` (the SHA pinned in `promptscript.lock` when the content was reviewed), and `rules` (rule names or IDs to skip). Excludes apply only to imported content, never to local project files, and overlapping or repeated entries union their rules. When the lockfile pins a different commit, the entry stops suppressing (the original findings reappear) and PS040 fails the build so the consumer re-reviews the new content. |
| `allowedPatterns`     | string[] | `[]`    | Pattern sources exempt from `blocked-patterns` detection. A blocked pattern is subtracted from the active set when its source text matches an entry exactly; other patterns keep scanning.                                                                                                                                                                                                                                                                                                                                                                        |

Consumed by `blocked-patterns` (PS005) and `authority-injection` (PS011), the
two heuristic text rules that always scan imported content. Excludes are
declared by the consumer, never by the scanned file: a disable comment inside
an imported skill cannot mute its own scan.

### models

Configures the model catalog. PromptScript uses it to write the native model
name each target expects in agent and skill `model` fields, and to check model
references (PS041).

```yaml
models:
  supported:
    - opus
    - claude-sonnet-5
    - gpt-5.3-codex
  profiles:
    claude-opus-9:
      provider: anthropic
      family: claude-opus
      version: '9'
      displayName: Claude Opus 9
      apiId: claude-opus-9
      releaseDate: '2027-01-15'
      targets:
        github: Claude Opus 9 (Preview)
```

| Field       | Type     | Default | Description                                                                                                                                                                                    |
| ----------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `supported` | string[] | -       | Model set the instructions are written and tested for. Entries are profile ids, aliases, API ids, display names, or floating aliases. When set, PS041 reports model references outside the set |
| `profiles`  | object   | `{}`    | Custom model profiles, or overrides of built-in profiles, keyed by profile id                                                                                                                  |

#### Model references

PromptScript ships a built-in catalog of Anthropic, OpenAI, Google, and xAI
models. Each profile records the provider, family, version, display name, API
id, lifecycle status, successor, and release and retirement dates. The
[Model Catalog](https://getpromptscript.dev/reference/models/index.md) lists every built-in profile.

A `model` or `specModel` value in `@agents`, and `model` in `@skills`, matches
a profile by id, alias, API id, or display name, ignoring case. Two kinds of
values are special:

- `inherit` keeps the model already selected in the tool.
- `opus`, `sonnet`, `haiku`, and `fable` are floating aliases. Each one points
  at the newest release of its Claude family that is not retired, listed in
  [Floating Aliases](https://getpromptscript.dev/reference/models/index.md#floating-aliases).

Names that are not in the catalog, such as gateway model ids, BYOK model ids,
or Claude Code's `opusplan`, are written unchanged on every target. The
exception is a value the target spells its own way: GitHub Copilot writes
`auto` as `Auto`.

#### Target model names

| Target                  | Written value                                                                | `inherit` | Providers |
| ----------------------- | ---------------------------------------------------------------------------- | --------- | --------- |
| Claude Code, Grok Build | Floating aliases as written, other models as API id                          | Written   | Anthropic |
| GitHub Copilot          | Display name, such as `Claude Sonnet 5` or `GPT-5.3-Codex`; `auto` is `Auto` | Omitted   | All       |
| Factory AI              | API id                                                                       | Written   | All       |
| Codex                   | API id                                                                       | Omitted   | OpenAI    |
| Cursor                  | Profile id, which has no snapshot date, such as `claude-sonnet-4-5`          | Written   | All       |

The [Model Catalog](https://getpromptscript.dev/reference/models/index.md#target-model-names) shows what each target
writes for common values.

For older releases the API id is a dated snapshot, such as
`claude-sonnet-4-5-20250929`. GitHub Copilot and Factory AI map `specModel`
the same way, and Claude Code and Grok Build also map the `model` field of
skills. A model from a provider the target cannot run is omitted from that
target's output and reported with a PS4004 warning during compilation. So is
a name with a line break or control character, whether it comes from the
source or from a profile. A per-target name in `profiles.<id>.targets` always wins, including for
providers the target would otherwise omit. Its keys are the target names from
the table (`claude`, `grok`, `github`, `factory`, `codex`, `cursor`), matched
without regard to case.

#### Model profiles

A `profiles` key that matches a built-in profile id changes only the fields it
sets. Its `targets` entries merge with the built-in ones, and every other field
it sets, including `aliases`, replaces the built-in value. Any other key adds a
custom profile, so a team can use a model before PromptScript ships it. Unset
fields of a custom profile default to the key (`family`, `displayName`, and
`apiId`), `provider: custom`, and `status: current`.

| Field            | Description                                                                                          |
| ---------------- | ---------------------------------------------------------------------------------------------------- |
| `provider`       | Model provider, such as `anthropic`, `openai`, `google`, or `xai`                                    |
| `family`         | Model line that groups versions, such as `claude-opus`                                               |
| `version`        | Version inside the family, such as `5.5`                                                             |
| `displayName`    | Human-readable name, also the GitHub Copilot model name                                              |
| `apiId`          | Provider API model id                                                                                |
| `aliases`        | Additional names that resolve to the profile                                                         |
| `status`         | `current`, `legacy` (superseded but still served), `deprecated` (retirement announced), or `retired` |
| `successor`      | Profile id of the recommended replacement                                                            |
| `releaseDate`    | Release date in `YYYY-MM-DD` format                                                                  |
| `retirementDate` | Date the provider stops serving the model, in `YYYY-MM-DD` format                                    |
| `targets`        | Native model name per target, such as `github: Claude Opus 9`                                        |

A custom profile that joins a Claude family with a higher version also moves
the floating alias. With the example above, `opus` resolves to Claude Opus 9,
so GitHub Copilot agents that use `opus` get `Claude Opus 9 (Preview)`.

#### Model validation

PS041 (`valid-model-reference`) is a warning. It reports:

- references to `deprecated` or `retired` models, with the replacement found
  through the successor chain
- when `supported` is set: references outside the set, references that are
  not in the catalog, and `supported` entries that are not in the catalog
- problems in `profiles`: a name used by two profiles, a profile or alias
  named after a floating alias or `inherit`, a successor that is unknown or
  loops back, a date not in `YYYY-MM-DD` format, and a `targets` key for a
  target that does not write model names

`legacy` models are still served, so they pass. Without `supported`, names
that are not in the catalog are not reported, because tools accept many names
no catalog can list.

### watch

Configures watch mode.

```yaml
watch:
  include:
    - '.promptscript/**/*.prs'
  debounce: 300
  clearScreen: true
```

| Field         | Type     | Default                    | Description         |
| ------------- | -------- | -------------------------- | ------------------- |
| `include`     | string[] | See default patterns below | Patterns to watch   |
| `exclude`     | string[] | `[]`                       | Patterns to ignore  |
| `debounce`    | number   | `300`                      | Debounce delay (ms) |
| `clearScreen` | boolean  | `true`                     | Clear on rebuild    |

Default include patterns:

- `**/*.prs`
- `.promptscript/**/*`
- `.agents/**/*`
- `registry/**/*`
- `promptscript.yaml`
- `promptscript.yml`
- `.promptscriptrc.yaml`
- `.promptscriptrc.yml`
- `promptscript.lock`

### output

Configures global output behavior.

```yaml
output:
  baseDir: '.'
  header: Managed by the platform team.
  overwrite: false
  resources:
    - agents
    - skills
```

| Field       | Type     | Default | Description                                                                                                                           |
| ----------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `baseDir`   | string   | `.`     | Base directory for generated output                                                                                                   |
| `header`    | string   | -       | Header added to Markdown after PromptScript metadata and frontmatter                                                                  |
| `overwrite` | boolean  | `false` | Overwrite conflicting user files without prompting, like `--force`                                                                    |
| `resources` | string[] | -       | Compile only these resource kinds (`agents`, `skills`, `commands`, `mcp`, `hooks`, `plugins`, `main`); `prs compile --resources` wins |

`resources` selects what a compile emits. A selection without `main` omits
root instruction files, so `baseDir: '${HOME}'` with `resources: [agents,
skills]` installs only generated agent and skill directories into the global
root. Protected personal files such as `~/.factory/AGENTS.md` are never
written, and resource-only runs skip managed cleanup.

### formatting

Configures output formatting to match your project's Prettier configuration.

```yaml
formatting:
  # Option 1: Auto-detect .prettierrc in project
  prettier: true

  # Option 2: Explicit path to Prettier config
  prettier: './config/.prettierrc'

  # Option 3: Inline options (overrides .prettierrc)
  proseWrap: always
  tabWidth: 4
  printWidth: 100
```

**Prettier Detection:**

When `prettier: true`, PromptScript searches for Prettier configuration files in this order:

1. `.prettierrc`
2. `.prettierrc.json`
3. `.prettierrc.yaml`
4. `.prettierrc.yml`

**Configuration Fields:**

| Field        | Type                                    | Default      | Description                          |
| ------------ | --------------------------------------- | ------------ | ------------------------------------ |
| `prettier`   | boolean \| string \| object             | -            | Enable auto-detect, path, or options |
| `proseWrap`  | `'always'` \| `'never'` \| `'preserve'` | `'preserve'` | How to wrap prose in markdown        |
| `tabWidth`   | number                                  | `2`          | Number of spaces per indentation     |
| `printWidth` | number                                  | `80`         | Maximum line width for wrapping      |

**ProseWrap Options:**

| Value        | Description                                 |
| ------------ | ------------------------------------------- |
| `'always'`   | Wrap prose at `printWidth`                  |
| `'never'`    | Do not wrap prose (single long lines)       |
| `'preserve'` | Preserve original line wrapping from source |

:::tip[Option Priority]
Explicit options (`proseWrap`, `tabWidth`, `printWidth`) override values from `.prettierrc`.
This allows you to use your project's Prettier config as a base while customizing specific options.
:::

**Example: Match Project Prettier Config**

If your project already has a `.prettierrc`:

```yaml
formatting:
  prettier: true
```

**Example: Custom Formatting Without Prettier File**

```yaml
formatting:
  tabWidth: 4
  proseWrap: always
  printWidth: 100
```

### universalDir

Configures the universal directory for auto-discovering skills and commands. Skills are discovered from `<universalDir>/skills/` and commands from `<universalDir>/commands/`.

```yaml
# Use default .agents/ directory
universalDir: true

# Custom directory path
universalDir: '.my-agents'

# Disable universal directory discovery
universalDir: false
```

| Value    | Type    | Default     | Description                          |
| -------- | ------- | ----------- | ------------------------------------ |
| `true`   | boolean | `true`      | Use default `.agents/` directory     |
| `false`  | boolean | -           | Disable universal directory          |
| (string) | string  | `'.agents'` | Custom path relative to project root |

### releases

Opts in to `prs release`. Lists the release environments in promotion order, first to last.

```yaml
releases:
  environments: [staging, prod]
```

| Field          | Type     | Required | Description                               |
| -------------- | -------- | -------- | ----------------------------------------- |
| `environments` | string[] | Yes      | Environment names, first to last, 1 to 16 |

Each name matches `^[a-z][a-z0-9-]{0,31}$` and appears once. The names `index` and `manifest`, and
the Windows device names `con`, `prn`, `aux`, `nul`, `com1` to `com9` and `lpt1` to `lpt9`, are
refused because each environment is a file named `<name>.json`. Without `releases` every
`prs release` command fails with `PS5001`. See the [releases guide](https://getpromptscript.dev/guides/releases/index.md).

### eval

Settings of [`prs eval`](https://getpromptscript.dev/reference/cli/index.md#prs-eval). They name the providers and set limits. They never
turn on a live run: the cache mode, the budget and extra hosts come from command-line flags only.

```yaml
eval:
  providers:
    anthropic: { kind: anthropic, apiKeyEnvVar: ANTHROPIC_API_KEY }
    gateway:
      { kind: openai-compatible, baseUrl: 'https://llm.corp.example/v1', apiKeyEnvVar: GATEWAY_KEY }
  timeoutMs: 60000
  maxOutputTokens: 1024
  maxCases: 1000
  reportDir: .promptscript/eval-reports
```

| Field             | Type    | Required | Description                                                                   |
| ----------------- | ------- | -------- | ----------------------------------------------------------------------------- |
| `providers`       | object  | No       | Providers by name, matching the `provider` of a model profile                 |
| `timeoutMs`       | integer | No       | Time limit of one model call, response body included. Default 60000           |
| `maxOutputTokens` | integer | No       | Output token cap when a prompt sets no `maxTokens`. Default 1024              |
| `maxCases`        | integer | No       | Largest number of dataset rows a suite runs. Default 1000. `--max-cases` wins |
| `reportDir`       | string  | No       | Directory for reports, relative to the project root. `--report-dir` wins      |

A provider entry has `kind` (`anthropic` or `openai-compatible`), `baseUrl` and `apiKeyEnvVar`, the
name of the environment variable that holds the key. `anthropic` and `openai` have built-in URLs
and key variables, so they need no entry. Any other provider needs one. The config never holds a
key: a run stops with `PS6307` when the value of a key variable, or of a variable named like a
secret, appears anywhere in `eval` or in a model profile. A provider key goes only to
`api.anthropic.com`, `api.openai.com` and the hosts you pass with `--allow-host`, so review a change
to `apiKeyEnvVar` like code.

### governance

Who owns the setup and which teams review each approval role. `prs governance codeowners` turns it
into CODEOWNERS lines, and PS047 checks prompt owners and data classes against it.

```yaml
governance:
  admins: ['@acme/prompt-platform']
  roles:
    legal: ['@acme/legal']
  dataClasses: [pii, phi, financial, credentials, internal, public]
  codeowners:
    output: .github/CODEOWNERS
    protect: [.github/workflows]
    prompts: true
```

| Field                | Type     | Required | Description                                                                           |
| -------------------- | -------- | -------- | ------------------------------------------------------------------------------------- |
| `admins`             | string[] | No       | Owners of the config, the approvals root, the releases and the CODEOWNERS files       |
| `roles`              | object   | No       | Approval roles, each with a non-empty list of owners. At most 32 roles                |
| `dataClasses`        | string[] | No       | Data class names the project uses (PS047, and the data classes of policies)           |
| `codeowners.output`  | string   | No       | `.github/CODEOWNERS` (default), `CODEOWNERS`, `docs/CODEOWNERS`, `.gitlab/CODEOWNERS` |
| `codeowners.protect` | string[] | No       | More paths the admins own, relative to the repository root                            |
| `codeowners.prompts` | boolean  | No       | Write CODEOWNERS lines for the `.prs` files that define prompts. Default `true`       |

Owners are a user (`@jane`; `.`, `_` and `-` allowed between letters or digits, like `@jane.doe`), a team (`@acme/legal`) or an email address of at most 254 characters.
Role names match `^[a-z][a-z0-9-]{0,31}$` and are not a Windows device name, because a role names a
directory. Unknown keys are refused with the closest key. `prs governance codeowners` needs
`admins`; a problem in this section is `PS5101`. See
[CODEOWNERS from Governance](https://getpromptscript.dev/guides/codeowners/index.md).

### includePromptScriptSkill

Controls whether the bundled PromptScript language skill is included in compilation output. When
enabled, the compiler adds it only when the formatter exposes a bundled-skill output path.

```yaml
# Include the PromptScript skill (default)
includePromptScriptSkill: true

# Disable the PromptScript skill
includePromptScriptSkill: false
```

| Field                      | Type    | Default | Description                                 |
| -------------------------- | ------- | ------- | ------------------------------------------- |
| `includePromptScriptSkill` | boolean | `true`  | Include bundled PromptScript language skill |

## Environment Variables

Configuration values can reference environment variables:

```yaml
id: environment-example
syntax: '1.4.0'

registry:
  auth:
    type: bearer
    token: ${REGISTRY_TOKEN}

targets:
  - github:
      output: ${OUTPUT_DIR:-.github}/copilot-instructions.md
```

| Syntax            | Description           |
| ----------------- | --------------------- |
| `${VAR}`          | Required variable     |
| `${VAR:-default}` | Variable with default |

## Minimal Configuration

The simplest valid configuration:

```yaml
id: minimal-project
syntax: '1.4.0'

input:
  entry: .promptscript/project.prs

targets:
  - github
```

## Extending Configuration

You can extend another configuration file, for example one shared by every project in a monorepo:

```yaml
# shared/base.config.yaml
syntax: '1.4.0'
registries:
  '@company': github.com/acme/promptscript-base
targets:
  - claude
```

```yaml
# app/promptscript.yaml
id: extended-project
extends: ../shared/base.config.yaml

targets:
  - github:
      output: custom/path.md
```

How the merge works:

- The `extends` path is relative to the file that declares it. A base file can extend another file. A circular chain is an error.
- Objects merge deeply and the extending file wins. Arrays such as `targets` are replaced, not joined, so the example above compiles only `github`.
- A `__proto__`, `constructor` or `prototype` key in an object that both files set is a config error (`PS3002`) that names the key. The merge reads and writes own keys only, so no value goes through a prototype.
- `policies` is joined: the base policies come first and the extending file's policies are appended. A base policy cannot be removed or redefined, so a policy with the same `name` as a base policy is a config error, and so is a `policies` value that is not a list, such as `policies: null` (a bare `policies:` key is accepted only when the base has no policies). Setting `validation.rules.policy-compliance` to `off` still turns PS030 off, including from an extending file.
- `governance.admins` and each owner list under `governance.roles` are joined the same way: base owners come first, the extending file's new owners are appended and duplicates are dropped. The extending file can add roles and owners, but cannot remove one. `governance.codeowners.protect` is joined the same way, so a protected path set in a base file stays protected. A value of the wrong type for `governance`, `governance.admins`, `governance.roles`, one role, `governance.codeowners` or `governance.codeowners.protect` is a config error, and so is `null` when the base sets entries there (a bare key is accepted only when the base has none). A `__proto__` key under `governance`, `governance.roles` or `governance.codeowners` is refused. Other keys under `governance` merge like the rest of the file.
- `registry` merges like the other config levels: if the extending file picks another backend (for example `registry.path` over a base `registry.git`), the base backend is dropped.
- Paths inside a base file, like `input.entry` or `registry.path`, stay relative to the project, not to the base file.
- `prs migrate` and `prs serve` read only the project file.

## Schema Validation

The configuration file is validated against a JSON schema. Enable editor support by adding the schema reference at the top of your `promptscript.yaml`:

```yaml
# yaml-language-server: $schema=https://getpromptscript.dev/schema/config.json

id: schema-example
syntax: '1.4.0'
input:
  entry: .promptscript/project.prs
targets:
  - github
```

This provides:

- **Autocomplete** - suggestions for all configuration options
- **Validation** - errors for invalid fields or values
- **Documentation** - hover tooltips with field descriptions

:::tip[Schema is the source of truth]
The JSON schema is automatically generated from TypeScript types in `@promptscript/core`.
When in doubt about configuration options, the schema reflects the current implementation.
:::

    Schema URL: [`schema/config.json`](https://getpromptscript.dev/schema/config.json)

## Examples

### Minimal Project

```yaml
id: minimal-project
syntax: '1.4.0'

input:
  entry: .promptscript/project.prs

targets:
  - github
```

### Multi-Team Setup

```yaml
id: multi-team-project
syntax: '1.4.0'

input:
  entry: .promptscript/${TEAM}/project.prs

registry:
  path: ./shared-registry

targets:
  - github:
      output: .github/copilot-instructions.md
  - claude:
      output: CLAUDE.md
```

### CI/CD Configuration

```yaml
id: ci-project
syntax: '1.4.0'

input:
  entry: .promptscript/project.prs

registry:
  git:
    url: https://github.com/org/promptscript-registry.git
    ref: main
    auth:
      type: token
      tokenEnvVar: GITHUB_TOKEN
  cache:
    enabled: true
    ttl: 3600000

targets:
  - github
  - claude
  - cursor
  - antigravity
  - opencode
  - gemini
```

### Private Git Registry with SSH

```yaml
registry:
  git:
    url: git@github.com:org/private-registry.git
    ref: v1.0.0 # Pin to specific version
    auth:
      type: ssh
      sshKeyPath: *****************
```

### HTTPS Registry with SSH Fallback

When registry references use HTTPS URLs but you authenticate via SSH keys,
configure a `fallbackUrl` so PromptScript automatically retries the clone
with SSH on auth failure:

```yaml
registry:
  git:
    url: https://github.com/org/registry.git
    fallbackUrl: git@github.com:org/registry.git
    ref: main
```

The same pattern works for registry aliases:

```yaml
registries:
  '@acme':
    url: https://github.com/acme/standards.git
    fallbackUrl: git@github.com:acme/standards.git
```