claude-cli-runner
Skill20 starsChanged 22 days agoMITView on GitHub
Run the local `claude` CLI in non-interactive mode (with optional retries/backoff) for arbitrary prompts; supports loading system+user prompts from files and writing output to a target file.
In this repository34 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- citation-triangulationSkill
- claim-groundingSkill
- codex-cli-runnerSkill
- deep-literature-reviewSkill
- derivation-verifySkill
- dsh-cli-runnerSkill
- figure-hygieneSkill
- gemini-cli-runnerSkill
- hep-calcSkill
- hep-mcp-usageSkill
- idea-allocationSkill
- idea-generationSkill
- idea-pairwise-matchSkill
- idea-posteriorSkill
- julia-packageSkill
- julia-perfSkill
- kimi-cli-runnerSkill
- literature-graph-builderSkill
- literature-to-packageSkill
- markdown-hygieneSkill
- numerical-reliability-gateSkill
- opencode-cli-runnerSkill
- paper-reviserSkill
- pdg-lookupSkill
- physics-diagramsSkill
- referee-reviewSkill
- research-harnessSkill
- research-integritySkill
- research-teamSkill
- research-writerSkill
- review-swarmSkill
- zotero-importSkill
The file
We split this skill into the rules it gives an agent
7 rules. About 0 are common, 2 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 8
Use this skill when you need to call Claude from the command line (any task), independent of the downstream workflow (review, drafting, …
- Very specific: line 34
Every lane starts with --safe-mode: the operator's own context stays out (see Context sources below).
- Very specific: line 37
Use --tool-mode review for the read-only review profile (Read,Glob,Grep).
- Very specific: line 68
Not measured: CLAUDE.local.md and the git snapshot (excluded per the CLI documentation).
- Distinctive: line 145
Do NOT persist/resume sessions for clean-room review rounds whose independence matters — a resumed round sees the earlier turns.
- Very specific: line 152
When using a custom ANTHROPIC_BASE_URL (configured in ~/.claude/settings.json), Claude CLI may fail with:
- Very specific: line 162
Use --strict-mcp-config to skip MCP tool loading:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Claude CLI Runner
Use this skill when you need to call Claude from the command line (any task), independent of the downstream workflow (review, drafting, translation, etc.).
If you are already running as Claude, don't use this runner to re-invoke yourself. Keep the call in-host: a native sub-agent if your host exposes one (Claude Code's Agent/Task tool), else inline in your own loop. The
claudeCLI hop adds latency, a separate auth/session, and context loss for zero gain. This runner is for reaching Claude from a DIFFERENT host (Codex / OpenCode / …) for cross-model work. Pick reasoning effort by task difficulty — quality first, not token thrift.
Preconditions
- Python 3.10 or newer for cross-process monotonic deadlines and process supervision.
claudeis installed:command -v claude- You are authenticated/configured for Claude Code CLI.
Recommended: runner script (retries + file inputs)
SKILL_DIR="${SKILL_DIR:-$(for r in "${CLAUDE_CONFIG_DIR:-$HOME/.claude}" "${CODEX_HOME:-$HOME/.codex}" "$HOME/.config/opencode" "$HOME/.kimi-code"; do [ -d "$r/skills/claude-cli-runner" ] && echo "$r/skills/claude-cli-runner" && break; done || true)}"
bash "${SKILL_DIR}/scripts/run_claude.sh" \
--system-prompt-file /path/to/system.txt \
--prompt-file /path/to/prompt.txt \
--out /path/to/output.txtNotes:
- The runner always uses
--print+--no-session-persistence. - Every lane starts with
--safe-mode: the operator's own context stays out (see Context sources below). --modelis optional. If omitted (or set todefaultupstream), the runner uses Claude CLI's configured default model.- It disables tools by default (
--tool-mode none, equivalent to--tools ""). - Use
--tool-mode reviewfor the read-only review profile (Read,Glob,Grep). --tools ...remains available as an explicit advanced override when you need a custom built-in tool list.- It enables
--strict-mcp-configby default to avoid side effects and MCP schema loading issues (disable with--no-strict-mcp-config). - It retries on failures with exponential backoff (useful for transient 5xx/overload). All calls and
backoff share an explicit
--timeout-secshard budget when supplied; the default has no hard deadline. --max-retriescaps total CLI calls (default6), including retries. Incomplete/error terminal results, confirmed inactivity, cancellation and budget exhaustion stop without an automatic retry.- Deterministic failures (usage/auth/region-ineligibility, quota/billing-cycle and permission errors, exit codes 2/126/127, and similar, detected from the exit code and diagnostics) fail immediately with the diagnostic instead of burning the retry backoff.
- It feeds the user prompt via stdin (
--input-format text+< --prompt-file) to avoid macOS/LinuxARG_MAXlimits (fixesArgument list too longwith 1–5MB prompt packets). - For offline/CI validation, use
--dry-runto print the planned invocation without calling Claude (prints only paths + size + sha256; never the full prompt).
Context sources (hermetic by default)
A lane carries only its own request: the system prompt file and the prompt file. Without further flags
the Claude CLI would also load the operator's user-level instruction file, CLAUDE.md/CLAUDE.local.md/
AGENTS.md of the working directory and its parents (and of subdirectories once a file there is read),
the project's auto memory, installed plugins, hooks and skills. Auto memory and instruction files routinely
record earlier conclusions and the author's judgment, so a lane that loads them is not independent of the
claim it checks, and their style rules change the lane's language.
- The runner passes
--safe-mode, which loads none of those sources and keeps the operator's login, and setsCLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS=1, which drops the git status snapshot (recent commit subjects of the checkout can state a conclusion). Measured with marker files: user-levelCLAUDE.md,CLAUDE.md/AGENTS.mdof the working directory and the home directory, a subdirectoryCLAUDE.mdafter a read, and auto memory all reached the lane without the flag and none did with it.--barewould also drop them but refuses the login (it needs an API key);--setting-sources ""drops the instruction files but still loads auto memory; a freshCLAUDE_CONFIG_DIRloses the login. - What still reaches the lane: the CLI's environment block (working directory, platform, model, date), its agent identity line, and the account e-mail address the login attaches. Nothing measured removes the e-mail while keeping the login.
- Not measured:
CLAUDE.local.mdand the git snapshot (excluded per the CLI documentation); managed-policy settings and policy-configured hooks, which the CLI says still apply; whether a default model,envblock orapiKeyHelperin the operator's settings still applies under--safe-mode. An operator who relies on a settings-only default model or proxy should pass--modelexplicitly and check the lane. - Each invocation writes
context_sources.jsoninto its attempt directory (OUT.attempts/run.*/, orTRACE_DIR/run.*/with--trace-dir): mechanism, excluded sources (measured, and stated by the CLI documentation), admitted sources and what was not measured. Under--live-captureeach attempt also writesattempt_N.context_observed.jsonfrom the stream's init event. A lane declared hermetic whose init event reports an auto-memory location, or that succeeds without any init event, exits78without retry and its answer is moved toOUT.not-hermetic(partial text toOUT.not-hermetic.partial). The observation lists the skills and agents the lane could use; itsplugins_installedfield lists installed plugins, which the init event names even when none is loaded. The init event does not list loadedCLAUDE.mdfiles; their exclusion rests on the CLI flag, measured with marker files when this default was introduced. --load-operator-contextrestores the CLI's default loading for interactive-style uses; the records then statehermetic: false. Independent review and derivation lanes must not pass it. Material a lane legitimately needs goes into its request.- A CLI version that does not know
--safe-modeis expected to reject it as an unknown option, so the call fails instead of running with the operator's context (not tested against an older CLI).
Progress and durable evidence
--live-capture explicitly opts into --output-format stream-json --include-partial-messages --verbose.
Raw events and stderr are written from launch under the seat-local OUT.attempts/run.* directory
(or --trace-dir DIR). Live capture does not mirror raw events into ambient legacy attempt-log
locations. OUT.partial and each attempt's partial answer contain answer text only;
private reasoning stays in the raw evidence. Progress JSON contains event kinds, growth counters,
timestamps, capture health and process/provider status, never reasoning text. Replays with the same
event identity and payload do not advance the counters; distinct event identities with repeated text
do advance. Without event identity, identical text deltas are retained as separate observed increments.
Heartbeats do not advance the counters. Text is collected by message identity and content-block index,
in original order, with snapshots and deltas reconciled without duplication. A snapshot that carries only
the block(s) just completed (one snapshot per block, e.g. thinking then text) is aligned to end at the
latest block the stream started. Incompatible block types
or text refuse completion while retaining prior partial text. OUT contains the complete assistant
text, including earlier messages; tool and thinking blocks stay in raw events. Exit 0, a successful
terminal record matching the complete text or final text message, and completed text blocks are required.
A response the provider's safeguards stopped (stop_reason refusal) never completes, even when the CLI
continues once afterwards and exits 0. Partial text never completes a review contract.
--expected-secs (default 1200) is a soft estimate only. --timeout-secs defaults to 0 (no hard
budget); any explicit positive value is a hard total cap across attempts and backoff, unaffected by
output growth. --inactivity-secs defaults to 0 (disabled). An explicit threshold records a diagnostic
with capture health, last advancing event and provider/process status, followed by
--inactivity-grace-secs (default 30, positive). Only a healthy observable stream with earlier advancing
content can terminate after this grace. Silent reasoning before any advancing event and unknown/degraded
capture cannot establish a stall. Activity establishes generation, not correctness or semantic quality.
Without live capture, successful stdout+stderr output bytes are preserved and progress remains unknown. Buffered silence never enables an inactivity kill. Cancellation and hard-budget exhaustion stop local CLI process groups and preserve existing raw/partial evidence; they do not attest remote-job cancellation. Deterministic failure classification reads stderr and typed stream error messages, never answer or thinking text. Cancellation also interrupts retry backoff without waiting for the delay to finish. Capture directories must be excluded from peer packets/workspaces by the caller's existing read-path membrane. Read-only tool access alone cannot enforce filesystem isolation.
Example:
bash /path/to/claude-cli-runner/scripts/run_claude.sh \
--system-prompt-file /path/to/system.txt --prompt-file /path/to/prompt.txt \
--out /path/to/review.txt --live-capture --expected-secs 1200 --timeout-secs 5400Session continuation (OPT-IN; the default stays a clean-room one-shot)
By default the runner passes --no-session-persistence: nothing is recorded, nothing resumable —
the right contract for independent reviews. Session features are opt-in (any of them implies
persistence; all live-validated):
--persist-session— record the conversation so a later invocation can resume it.--session-id-file FILE— also capture the new session id to FILE. Internally switches claude to--output-format json;--outthen receives ONLY the final result text (parsed from the JSON envelope), not raw stdout+stderr.--resume-session ID— send this prompt as the NEXT turn of a previously persisted session (claude --resume ID). Chain multi-turn workflows: run 1 with--session-id-file sid.txt, run 2 with--resume-session "$(cat sid.txt)". Non-racy: the id is explicit.
Do NOT persist/resume sessions for clean-room review rounds whose independence matters — a resumed round sees the earlier turns.
Troubleshooting: 400 Error with Custom API Gateway
Symptom
When using a custom ANTHROPIC_BASE_URL (configured in ~/.claude/settings.json), Claude CLI may fail with:
API Error: 400 {"error":{"message":"input_schema does not support oneOf, allOf, or anyOf at the top level"}}This happens when MCP tools are loaded and their JSON schemas contain top-level oneOf/anyOf/allOf (produced by Zod's z.discriminatedUnion or z.union).
Temporary Workaround
Use --strict-mcp-config to skip MCP tool loading:
echo "test" | claude --print --no-session-persistence --strict-mcp-configNote: This disables MCP tools entirely. For scientific research workflows, tools/MCP should remain enabled for full functionality.
Root Cause
Some API gateways (e.g., jp.duckcoding.com, openclaudecode.cn) enforce stricter JSON Schema validation than the official Anthropic API. When MCP tools use z.discriminatedUnion('mode', [...]), the resulting JSON Schema has a top-level oneOf, which these gateways reject.
Permanent Fix
Flatten z.discriminatedUnion schemas to plain z.object with z.enum for the discriminator field:
Before (problematic):
const ToolSchema = z.discriminatedUnion('mode', [
z.object({ mode: z.literal('get'), id: z.string() }),
z.object({ mode: z.literal('search'), query: z.string() }),
]);After (compatible):
const ToolSchema = z.object({
mode: z.enum(['get', 'search']),
id: z.string().optional(), // get mode
query: z.string().optional(), // search mode
});Smoke Test
A smoke test ensures no tool has top-level union constructs:
cd /path/to/nullius/packages/hep-mcp
npx vitest run tests/smoke-no-toplevel-union.test.tsAffected MCP Projects
The following MCP projects had top-level-union schema fixes (2026-01-28):
hep-mcp/@nullius/hep-mcp:inspire_literatureand related retained INSPIRE surfaceszotero-mcp:zotero_local
Historical workflow-like tool names may since have been pruned. Use the current tool catalog for live tool names rather than this historical note.
Runner dry-run
SKILL_DIR="${SKILL_DIR:-$(for r in "${CLAUDE_CONFIG_DIR:-$HOME/.claude}" "${CODEX_HOME:-$HOME/.codex}" "$HOME/.config/opencode" "$HOME/.kimi-code"; do [ -d "$r/skills/claude-cli-runner" ] && echo "$r/skills/claude-cli-runner" && break; done || true)}"
bash "${SKILL_DIR}/scripts/run_claude.sh" \
--system-prompt-file /path/to/system.txt \
--prompt-file /path/to/prompt.txt \
--out /path/to/output.txt \
--dry-runDoes it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

