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.

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.
---

Lectic picks a default provider and model based on your environment variables, so you often don’t need to specify them. If you want to be explicit:

---
interlocutor:
  name: Assistant
  prompt: You are a helpful assistant.
  provider: anthropic
  model: claude-sonnet-4-20250514
---

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

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`.

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. These are created by:

  • The :attach[...] directive (kind="attach")
  • Inline hooks (kind="hook")

Inline attachments appear inside the assistant’s response block as XML. They include a <command> field (the hook’s do value, or empty for :attach) and a <content> field. They may also include attributes like icon and name used by editor folding UIs.

Example (:attach[...]):

<inline-attachment kind="attach">
<command></command>
<content type="text/plain">
┆some content here
</content>
</inline-attachment>

Example (:attach[:cmd[...]]):

<inline-attachment kind="attach">
<command></command>
<content type="text/plain">
┆<stdout from="git diff --staged">diff --git a/src/main.ts b/src/main.ts
┆...
┆</stdout>
</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-process old directives. Only :attach directives in the most recent user message are processed.

  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 when using :attach (often with :cmd inside) or inline hooks. They’re part of the conversation record and should generally be left alone. Editor plugins typically fold them by default to reduce visual clutter.

Tip

Inline attachments are managed by Lectic. Don’t edit them by hand — if you need to re-run something, delete the attachment and add a new directive in your latest message.

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.