Shell Integration Patterns
Purpose: Show how to run shell commands safely inside Fractalic and merge results without uncontrolled growth.
Why it matters:
- Automate: Capture build steps, scans, generators inline.
- Reproduce: Everything is in markdown history (diffable).
- Compress: Summarize noisy outputs early to control tokens.
- Chain: Feed command output directly into later
@llmsteps.
Internal Table of Contents
- Basics (
@shell) - Minimal & Multi-Line Forms
- Selecting Where Output Goes (
mode,to) - Summarizing Outputs
- Chaining Shell → LLM
- Repeated Tasks & Refresh Patterns
- Cost & Noise Control
- Common Pitfalls
- Quick Reference
- Cross References
Basics (@shell)
Run a command and capture its stdout + stderr (interleaved) as a new block.
@shell
prompt: "echo 'Hello'"Multi-line script (use |):
@shell
prompt: |
set -e
echo "Building"
python -m pytest -qResult is inserted after the operation (default append).
Minimal & Multi-Line Forms
| Form | When |
|---|---|
| Single line (no pipe) | Short one-liner, no special chars needing YAML escaping. |
| Multi-line (` | `) |
Selecting Where Output Goes (mode, to)
Control how the output merges:
append(default) grows history.prependpositions output before existing content.replaceoverwrites a target block's body (after stabilization). Target another block:
@shell
prompt: "ls -1 src"
to: workspace-scan
mode: replace(Requires a heading with {id=workspace-scan} already defined.)
Summarizing Outputs
Large logs inflate later prompts. Pattern:
@shell
prompt: |
set -e
pytest -q --maxfail=1Then compress:
@llm
prompt: |
Summarize the latest test output into <=12 bullet points (failures first). Keep only actionable info.
block: shell-response/*
to: test-summary
mode: replaceGive the shell result block an ID (edit its heading) if you plan reuse: # Test Run Output {id=shell-response}.
Chaining Shell → LLM
Direct transformation:
@shell
prompt: "git diff HEAD~1 --name-only"
@llm
prompt: "Group the changed paths by top-level folder and note probable change types."
block: shell-responseMultiple shell outputs combined:
@shell
prompt: "git rev-parse --abbrev-ref HEAD"
@shell
prompt: "git log -1 --pretty=%B"
@llm
prompt: "Create a concise release note draft."
block:
- branch-name
- last-commit-msgAssign IDs to the generated shell output headings (after first run) so they can be referenced (branch-name, last-commit-msg).
Repeated Tasks & Refresh Patterns
Use replace when refreshing status snapshots:
@shell
prompt: "du -sh . | sort -h"
to: size-scan
mode: replaceAccumulate progressive evidence first, then prune:
@shell
prompt: "grep -R 'TODO' -n src" # exploratory
@shell
prompt: "grep -R 'FIXME' -n src"
@llm
prompt: "Merge all TODO / FIXME lines into a prioritized action list (dedupe)."
block:
- shell-todo
- shell-fixme
mode: replace
to: code-remediation-planCost & Noise Control
| Technique | Why |
|---|---|
| Add IDs early | Makes re-selection explicit; prevents wildcard overreach. |
| Summarize long logs | Shrinks context for later LLM steps. |
Use replace on stable snapshots | Prevents endless growth. |
| External file storage (future pattern) | Keep raw logs out of hot context (then import distilled form). |
| Limit breadth of scans | Narrow glob patterns to required subtrees. |
Common Pitfalls
| Pitfall | Impact | Fix |
|---|---|---|
| Forgetting ` | ` for multi-line script | YAML parse issues / truncated command |
| Overusing append for recurring scans | Token bloat | Switch to replace after first stable format. |
| No IDs on shell outputs | Hard to target later | Edit heading to add {id=...}. |
| Summarizing too late | High cost paid already | Summarize immediately if large. |
| Blindly capturing huge directories | Irrelevant noise | Scope commands narrowly. |
Quick Reference
| Need | Pattern |
|---|---|
| One-liner | prompt: "echo 'Hi'" |
| Multi-line | prompt: |
| Snapshot refresh | mode: replace + to: <id> |
| Transform shell → summary | @llm with block: shell-output-id |
| Combine multiple runs | block: [ id-a, id-b ] |
| Prune noise | Summarize + replace |