The Lectic Conversation Format

Lectic conversations are stored in plain markdown files, typically with a .lec extension. They use a superset of CommonMark, adding two specific conventions: a YAML frontmatter block for configuration and “container directives” for assistant responses.

1. YAML Frontmatter

Every Lectic file begins with a YAML frontmatter block, enclosed by three dashes (---). This is where you configure the conversation, defining the interlocutor(s), their models, prompts, and any tools they might use.

A minimal header looks like this:

---
interlocutor:
  name: Assistant
  prompt: You are a helpful assistant.
  provider: anthropic
  model: claude-3-haiku-20240307
  # Optional thinking controls (Anthropic/Gemini):
  # thinking_budget: 1024     # integer token budget for reasoning
---

The frontmatter can be closed with either three dashes (---) or three periods (...). For a complete guide to all available options, see the Configuration page.

2. User Messages

Anything in the file that is not part of the YAML frontmatter or an assistant response block is considered a user message. You write your prompts, questions, and instructions here as plain text or standard markdown.

This is a user message.

So is this. You can include any markdown you like, such as **bold text** or
`inline code`.

3. Assistant Responses

Lectic uses “container directives” to represent messages from the LLM. These are fenced blocks that start with a run of colons, followed immediately by the name of the interlocutor.

The canonical form is exactly three colons on open and close, like this:

:::Name

Some content.

:::

Markdown code fences inside assistant blocks can also use three backticks.

Inline Attachments

Lectic records some generated content directly into the transcript as inline attachments. This includes:

  • Output of :cmd[...] directives (kind="cmd")
  • Output of inline hooks (kind="hook")

Inline attachments appear inside the assistant’s response block as XML. They include a <command> field (the command that produced the content, or the hook’s do value) and a <content> field.

Example (:cmd[...]):

<inline-attachment kind="cmd">
<command>git diff --staged</command>
<content type="text/plain">
┆diff --git a/src/main.ts b/src/main.ts
┆...
</content>
</inline-attachment>

Example (inline hook output):

<inline-attachment kind="hook" final="true">
<command>~/.config/lectic/my-hook.sh</command>
<content type="text/plain">
┆System check complete.
</content>
</inline-attachment>

Inline attachments serve two purposes:

  1. Caching: The results are stored in the file, so re-running Lectic doesn’t re-execute old commands. Only :cmd directives in the most recent user message are executed.

  2. Context positioning: When sending the conversation to the provider, attachments are treated as if they were a user message This keeps provider caches stable and avoids token recomputation.

You’ll see inline attachments frequently when using :cmd. They’re part of the conversation record and should generally be left alone. Editor plugins typically fold them by default to reduce visual clutter.

Example

Here is a complete, simple conversation file showing all the parts together:

---
interlocutor:
  name: Oggle
  prompt: You are a skeptical assistant.
---

I'd like to know more about container directives.

:::Oggle

Are you sure? It seems like a rather niche topic. They are part of a
proposed extension to CommonMark that allows for custom block-level
elements.

Inside one of these blocks, standard markdown is still supported:

```python
# This is a regular code block
print("Hello from inside a directive!")
```

Is that all you wanted to know?

:::

When you run lectic, it reads the entire file, sends the content to the LLM, and then appends the next assistant response in a new directive block.