@c3-oss/foreach-agent
v0.1.1
Published
Terminal UI to run prompt templates across multiple AI coding agents
Readme
foreach-agent
Run Liquid templates in batch across AI coding agent CLIs (claude, cursor-agent, gemini, codex), with:
- interactive TUI mode
- headless CLI mode
- programmatic API usage
Storage
All data is persisted under ~/.foreach-agent/:
templates/: editable templatesruns/<runId>/run.json: consolidated run stateruns/<runId>/prompts/*.md: rendered prompt per taskruns/<runId>/transcripts/*.jsonl: raw transcript per taskexports/: run exports (JSON/CSV)
Run
Interactive mode (default):
cd apps/foreach-agent
pnpm startor:
pnpm start tuiBuild:
pnpm --filter @c3-oss/foreach-agent buildHeadless CLI
Main commands:
pnpm start run --template my-template.liquid --entries '[{"name":"world"}]'
pnpm start templates list --output-format table
pnpm start runs --query 'status == "failed"'
pnpm start tasks --run-id <runId> --query 'provider == "codex" and hasError'
pnpm start configs --query 'concurrency >= 10 and autoApproval'
pnpm start export --run-id <runId> --format csv --output /tmp/run.csv--output-format (tfplan-explorer style)
Supported values:
interactivetablejsoncsv
Defaults:
templates list,runs,tasks,configs:interactiverun,templates create/read/update/delete:table
Note:
--queryrequires non-interactive output (table,json, orcsv)
Examples:
pnpm start runs --output-format interactive
pnpm start runs --output-format table
pnpm start runs --output-format json --query 'failedTasks > 0'
pnpm start templates list --output-format csvTemplate CRUD subcommands
Create:
pnpm start templates create --name my-template --content 'Hello {{ name }}'Read:
pnpm start templates read --id my-template.liquid --output-format jsonUpdate:
pnpm start templates update --id my-template.liquid --content 'Hi {{ name }}'Delete:
pnpm start templates delete --id my-template.liquidContent input for create/update:
--content(inline)--content-path(file path)--content-stdin(stdin)
Run without TUI
pnpm start run \
--template my-template.liquid \
--entries-path /abs/path/entries.yaml \
--providers 'claude,codex' \
--codex-model gpt-5.3-codex \
--concurrency 10 \
--retries 3 \
--timeout-seconds infinite \
--cwd /abs/path/project \
--auto-approval trueEntries input options:
--entries(inline JSON/YAML)--entries-path(JSON/YAML file)--entries-stdin(stdin)
--query (Filtrex)
--query follows the same model used in mzi-tfplan-explorer via filtrex.
Examples:
pnpm start runs --query 'failedTasks > 0'
pnpm start templates list --query 'variablesCount >= 3 and content ~= "TODO"'
pnpm start tasks --run-id <runId> --query 'provider == "codex" and status != "success"'
pnpm start configs --query 'timeoutSeconds == -1 and autoApproval'Available query fields:
templates:id,name,filePath,updatedAt,updatedAtEpoch,content,contentLength,variablesCountruns:id,status,templateName,templateId,providers,providerCount,entryCount,taskCount,successTasks,failedTasks,concurrency,retries,timeoutSeconds,autoApproval,cwd,createdAt,startedAt,finishedAttasks:id,runId,status,provider,model,entryIndex,attempt,maxAttempts,durationMs,hasError,hasOutput,errorMessage,outputText,templateName,concurrency,retries,autoApproval,cwdconfigs:runId,runStatus,templateName,templateId,providerCount,providers,entryCount,concurrency,retries,timeoutSeconds,autoApproval,cwd,createdAt
Programmatic usage
import { executeHeadlessRun, filterWithQuery } from '@c3-oss/foreach-agent'
const run = await executeHeadlessRun({
template: 'my-template.liquid',
entriesText: '[{"name":"world"}]',
providers: ['codex'],
})
const failedTasks = filterWithQuery(
run.tasks,
'status != "success"',
(task) => ({ status: task.status, provider: task.provider }),
(task) => task.id,
)Main TUI shortcuts
Templates screen:
j/k: navigaten: create templatee: edit templated: delete templater: open run setupu: open runs historyq: quit
Run setup screen:
j/k: navigate fieldsspace: toggle provider/boolean/sourceenter: edit selected fieldenteronstart run: start execution
Run monitor screen:
j/k: navigate tasksenter: open pretty logss: status filterp: provider filter/: text filterx: export JSONX: export CSV
Multi-line editor:
Ctrl+S: saveCtrl+L: clearEsc: cancel
Notes
- Template engine:
liquidjs - Entries accept JSON/YAML inline or file path
- Default concurrency:
10 - Default retries:
3attempts per task - Default timeout: infinite (minimum configurable value:
20s) - Default auto-approval: enabled
- Default
codexmodel:gpt-5.3-codexgpt-codex-5.3was tested and did not work in this environment
