Managing Context: External Content

Lectic aims to make it easy to pull external information into the conversation, providing the LLM with the context it needs to answer questions, analyze data, or perform tasks.

This is done in two primary ways: by referencing files and URIs using standard Markdown links, and by executing shell commands with the :cmd directive.

Command Output via :cmd

Use :cmd[...] to execute a shell command and insert its output directly into your message. Think of it as a built-in macro that runs a command and pastes in the result.

What can you tell me about my system? :cmd[uname -a]

When Lectic processes this, it runs uname -a and replaces the :cmd[...] directive with the command’s output wrapped in XML:

<stdout from="uname -a">Linux myhost 6.1.0 ...</stdout>

If the command fails (non-zero exit code), you get an error wrapper instead:

<error>Something went wrong when executing a command:<stdout from="bad-cmd">
</stdout><stderr from="bad-cmd">bad-cmd: command not found</stderr></error>

When :cmd runs

:cmd directives are expanded at the beginning of each user turn. You can also expand them with an LSP code action. If you want a :cmd directive to expand only once, you can wrap it in :attach[..] (see below) which will store the results in the lectic document as an attachment, or you can implement some other caching mechanism using the macro system.

Execution environment

  • :cmd runs with Bun’s $ shell in the current working directory.
  • Standard Lectic environment variables like LECTIC_FILE are available.
  • Line breaks inside :cmd[...] are ignored, so wrapped commands work:
:cmd[find . -name "*.ts" 
     | head -20]

Use cases

  • System information: What can you tell me about my system? :cmd[uname -a]
  • Project state: Write a commit message: :cmd[git diff --staged]
  • Data snippets: Analyze this: :cmd[head -50 data.csv]

Inline Attachments with :attach

While :cmd inserts command output directly into your message text, sometimes you want to provide context that appears as a separate attachment — for example, to keep the conversation transcript cleaner or to control caching behavior.

The :attach[...] directive creates an inline attachment. The content inside the brackets is stored in the assistant’s response block (after macro expansion) and sent to the LLM as additional user context.

You can optionally set metadata with directive attributes:

  • icon: custom fold icon in LSP/editor UIs
  • name: custom fold label in LSP/editor UIs
Here's the current state of the config:

:attach[:config_status_macro[]]

What do you think of this configuration?

With custom fold display metadata:

:attach[:cmd[git diff --staged]]{name="staged diff" icon=""}

When Lectic processes this, it creates an inline attachment that appears at the top of the assistant’s response:

<inline-attachment kind="attach">
<command></command>
<content type="text/plain">
┆server:
┆  port: 8080
┆  host: localhost
</content>
</inline-attachment>

Combining :attach with :cmd

You can compose :attach and :cmd to get the best of both worlds — run a command and store its output as an attachment:

Review this diff: :attach[:cmd[git diff --staged]]

This executes git diff --staged, then wraps the result as an inline attachment. The attachment is cached in the transcript, so re-running Lectic won’t re-execute the command.

TipWhen to use :cmd vs :attach[:cmd[...]]

Use :cmd[...] when you want command output inlined directly in your message. The output becomes part of the message text.

Use :attach[:cmd[...]] when you want the output stored as a cached attachment. This is useful for large outputs or when you want to preserve provider cache efficiency (for example, if the output of :cmd might change in between user turns).

How inline attachments work

For details on how inline attachments are stored in the .lec file and how they interact with the provider, see Inline Attachments in the Conversation Format guide.