# `PtcRunner.Lisp.LanguageSpec`
[🔗](https://github.com/andreasronge/ptc_runner/blob/main/lib/ptc_runner/lisp/language_spec.ex#L1)

Language specification compositions for PTC-Lisp.

Provides pre-composed language specs built from two axes plus optional capabilities:

- **Behavior axis**: `:single_shot`, `:explicit_return`
- **Reference**: Optional language reference (tool syntax, Java interop, restrictions)
- **Capabilities**: `:journal` (task caching, semantic progress)

Compositions include the language reference by default. Use
`{:profile, behavior, reference: :none}` to omit it for capable models.

## Compositions

| Key | Description |
|-----|-------------|
| `:single_shot` | Last expr = answer, one turn |
| `:explicit_return` | Multi-turn, must call `(return ...)`/`(fail ...)` |
| `:explicit_journal` | Explicit return + journal (task/step-done) |

## Structured Profiles

For programmatic composition, use `resolve_profile/1` with a tuple:

    # Omit language reference for capable models
    LanguageSpec.resolve_profile({:profile, :explicit_return, reference: :none})

    # Add journal capability
    LanguageSpec.resolve_profile({:profile, :explicit_return, journal: true})

## Usage

    # For single-turn queries
    PtcRunner.Lisp.LanguageSpec.get(:single_shot)

    # For multi-turn conversations
    PtcRunner.Lisp.LanguageSpec.get(:explicit_return)

    # Raw snippets for custom compositions
    PtcRunner.Lisp.LanguageSpec.get(:reference) <> my_custom_addon

# `get`

```elixir
@spec get(atom()) :: String.t() | nil
```

Get a prompt by key.

Supports both raw snippets (`:reference`, `:behavior_multi_turn`) and compositions
(`:single_shot`, `:explicit_return`).

## Examples

    iex> prompt = PtcRunner.Lisp.LanguageSpec.get(:single_shot)
    iex> is_binary(prompt)
    true

    iex> prompt = PtcRunner.Lisp.LanguageSpec.get(:explicit_return)
    iex> String.contains?(prompt, "<state>")
    true

# `get!`

```elixir
@spec get!(atom()) :: String.t()
```

Get a prompt by key, raising if not found.

## Examples

    iex> prompt = PtcRunner.Lisp.LanguageSpec.get!(:single_shot)
    iex> is_binary(prompt)
    true

# `list`

```elixir
@spec list() :: [atom()]
```

List all available prompt keys.

## Examples

    iex> keys = PtcRunner.Lisp.LanguageSpec.list()
    iex> :single_shot in keys
    true
    iex> :explicit_return in keys
    true

# `list_with_descriptions`

```elixir
@spec list_with_descriptions() :: [{atom(), String.t()}]
```

List all prompts with descriptions.

Returns a list of `{key, description}` tuples.

## Examples

    iex> list = PtcRunner.Lisp.LanguageSpec.list_with_descriptions()
    iex> Enum.any?(list, fn {k, _} -> k == :single_shot end)
    true

# `metadata`

```elixir
@spec metadata(atom()) :: map()
```

Get full metadata for a prompt.

Returns a map with metadata keys like `:version`, `:date`, `:changes`.
For compositions, returns metadata of the first component.

## Examples

    iex> meta = PtcRunner.Lisp.LanguageSpec.metadata(:reference)
    iex> is_map(meta)
    true

# `resolve_profile`

```elixir
@spec resolve_profile(atom() | {:profile, atom()} | {:profile, atom(), keyword()}) ::
  String.t()
```

Resolve a structured prompt profile to a composed prompt string.

Accepts either an atom (delegates to `get!/1`) or a tuple for structured composition:

    # Atom form (existing)
    resolve_profile(:explicit_return)

    # Tuple form with options
    resolve_profile({:profile, :explicit_return, reference: :full, journal: true})

## Options

- `:reference` - `:full` (default) or `:none`
- `:journal` - `true` or `false` (default)

## Validation

Raises `ArgumentError` for:
- Unknown behavior (must be `:single_shot` or `:explicit_return`)
- `journal: true` with `:single_shot` (single-shot skips the loop)
- Unknown reference value (must be `:full` or `:none`)
- Unknown option keys

# `version`

```elixir
@spec version(atom()) :: pos_integer()
```

Get the version number for a prompt.

Returns the version from the prompt's metadata, or 1 if not specified.
For compositions, returns the version of the first component.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
