# Configuration

How to configure data stores, prefixes, and access levels.

## --store-dir CLI Syntax

`--store-dir` can be specified multiple times on any script. Each entry maps a key
prefix to a data directory.

### Format

```
--store-dir PATH                    Default store (no prefix, catches all unmatched keys)
--store-dir :PATH                   Explicit default (colon at start, same as above)
--store-dir PREFIX:PATH             Prefix-bound store (PREFIX keys route here)
```

### Examples

```bash
# One default store
--store-dir /tmp/globals

# Default + session-specific prefix
--store-dir /tmp/globals --store-dir "session:/tmp/my-agent-session"

# Three stores: default + two prefix overrides
--store-dir /tmp/globals \
--store-dir "foo:/tmp/foo-store" \
--store-dir "foo.bar:/tmp/foobar-store"
```

### Prefix Rules

Key prefixes must match: `[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)*`
- No leading, trailing, or consecutive dots
- Colons not allowed in key names or directory paths
- Each segment: letters, digits, hyphens, underscores

### Validation

After all config sources are merged, there must be at least one default (empty-prefix) store.
If not, the script exits with an error.

## Prefix Resolution (Longest Match Wins)

When multiple prefixes overlap, the most specific one wins:

```
Stores:  "" → /tmp/default,  "foo" → /tmp/foo,  "foo.bar" → /tmp/foobar

Key "foo.bar.baz" → /tmp/foobar      (longest match: "foo.bar")
Key "foo.qux"     → /tmp/foo          (longest match: "foo")
Key "other.thing" → /tmp/default      (no prefix match)
```

### Obscuring Behavior

A prefix mount hides ALL keys under that prefix from less-specific stores, even if
the mounted store doesn't have the key. This is intentional — like a filesystem mount.

```
/tmp/default has: foo/extra/value = "hidden"
--store-dir "foo:/tmp/foo-store" (which has no "extra" key)

get.ts --key foo.extra → NOT FOUND (obscured by the foo mount)
```

## Config Files (YAML)

For complex setups, use a YAML config file instead of repeated CLI flags.

### Format

```yaml
stores:
  - prefix: templates          # Keys starting with "templates." (no trailing dot needed)
    path: /app/data/templates
    access: readonly
    init:
      source: definitions/templates.yaml
      on_exists: fix

  - prefix: session
    path: /tmp/agent-session
    access: write

  - prefix: ""                 # Default catch-all (must exist)
    path: /tmp/globals
    access: write
    init:
      source: definitions/global.yaml
      on_exists: fix
```

### Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `prefix` | string | yes | Key prefix (empty string = default). No trailing dot needed. |
| `path` | string | yes | Data directory path (relative paths resolved from config file location) |
| `access` | string | no | `readonly`, `write` (default), or `admin` |
| `init.source` | string | no | YAML definition to bootstrap from |
| `init.on_exists` | string | no | `ignore` (default), `validate`, or `fix` |

### Cascading

Config files are loaded in order (later overrides earlier by prefix):

1. **Built-in default**: single store at `/tmp/templating-vault/`
2. **Repo-level**: `.opencode/skills/templating-vault/config.yaml`
3. **User-level**: `~/.config/templating-vault/config.yaml`
4. **CLI**: `--config <path>`
5. **CLI**: `--store-dir` entries

Within each level, stores are merged by prefix. If two configs define a store for
the same prefix, the later one wins. Stores for other prefixes from earlier configs
remain.

## Agent Isolation Pattern

Different agents share globals but get their own session stores:

**Config for AUTO-IMP-SUP:**
```yaml
stores:
  - prefix: session
    path: /tmp/AUTO-IMP-SUP
    access: write
  - prefix: ""
    path: /tmp/globals
    access: write
```

**Config for AUTO-MRG-SUP:**
```yaml
stores:
  - prefix: session
    path: /tmp/AUTO-MRG-SUP
    access: write
  - prefix: ""
    path: /tmp/globals
    access: write
```

Both see `global.credentials.forgejo_pat` from `/tmp/globals/` — same value.
Each sees `session.max_workers` from its own directory — independent values.

The same template `{{ max_workers }}` renders differently for each because the
config determines which data directory provides `session.max_workers`.
