Small libraries for calling coding CLIs from normal programs.
./run_all_tests.shThis runs every implementation's unit/build checks plus the cross-language contract and mock-harness checks, then prints a color-coded checklist:
PASS python: runner + prompt_spec
PASS rust: cargo test
PASS typescript: node --test
PASS c: make test
PASS go: go test
PASS ruby: test suite
PASS perl: prove
PASS cpp: cmake build + gtest
...
SKIP vbscript: test suite (Windows only)
PASS contract: ccc CLI behavior (legacy + @name matrix)
PASS harness: mock binary behavior (16 langs × 9 cases)
Total: 22 Passed: 21 Failed: 0 Skipped: 1
When working on a single language, use the targeted wrapper instead of the full repo sweep:
./test_impl.sh <language>Examples:
./test_impl.sh c
./test_impl.sh rust
./test_impl.sh typescriptThis runs that implementation's unit tests plus the targeted cross-language contract and mock harness checks for the same language only. Use ./run_all_tests.sh only when you intentionally want the whole repository run.
just buildThis builds the Rust binary and runs a Python compile check. Both Python and Rust read the repo-root VERSION file for ccc --version, so update that file when you want to bump the reported build version.
| Language | Command |
|---|---|
| Python | PYTHONPATH=python python3 -m unittest tests.test_runner tests.test_parser_config tests.test_json_output tests.test_run_artifacts |
| Rust | cd rust && cargo test |
| TypeScript | node --test typescript/tests/runner.test.mjs |
| C | cd c && make test |
| Go | cd go && go test ./... |
| Ruby | cd ruby && ruby -Ilib -Itest test/test_*.rb |
| Perl | cd perl && prove -v t/ |
| C++ | cmake -B cpp/build -S cpp && cmake --build cpp/build --target ccc_tests && ./cpp/build/tests/ccc_tests |
| Cross-language | PYTHONPATH=. python3 tests/test_ccc_contract_impl.py <language> -v && PYTHONPATH=. python3 tests/test_harness.py <language> -v |
- Python —
call_coding_clispackage, real-runner override env support - Rust —
call-coding-cliscrate, concurrent streaming, real-runner override env support - TypeScript — runner +
cccCLI with streaming,CCC_REAL_OPENCODEsupport - C — reusable runner library (
runner.c/runner.h) pluscccbinary - Go —
go/ccc.golibrary with goroutine-based streaming,cccCLI - Ruby —
CallCodingClismodule with runner/stream,cccCLI - Perl —
Call::Coding::Cliswith runner,cccCLI - C++ — C++17 with GoogleTest, cmake build,
cccCLI - Zig, D, F#, PHP, PureScript, x86-64 ASM, OCaml, Crystal, Haskell, Elixir, Nim —
cccimplementations covered by targeted contract/harness checks
Implementation-specific follow-up work is tracked in each language's PLAN.md. VBScript remains planned.
- OpenCode
- Claude / Claude Code
- Codex
- Kimi
- Gemini CLI
- Cursor Agent
- RooCode
- Crush
- Qwen Code
- similar terminal-first coding agents
- start a CLI process with a prompt or stdin payload
- capture stdout/stderr and exit status
- expose a small streaming interface
- keep the abstraction subprocess-oriented and easy to mock in tests
- write a stable per-run artifact directory under the platform state root, with a client-prefixed run folder such as
opencode-<run-id>and a parseable stderr footer for scripts
- every language library should also bundle a CLI named
ccc - the
cccinterface should have the same shape across languages ccc "<Prompt>"must work everywhere; Python and Rust carry the reference extended CLI surface- library and CLI design should stay aligned so
precurlcan use the library layer while humans can use the same runner shape directly precurluses the Rust library layer for delegated LLM analysis — see the precurl SECURITY.md for threat model and prompt-injection mitigation details- Python and Rust also support free-order control tokens before the prompt,
--to force literal prompt text,--show-thinking/--no-show-thinking, and--yolo/-y -v/--versionprints the shared build version plus resolved client versions
ccc "<Prompt>"- initial command shape maps to
opencode run "<Prompt>" - this is intentionally narrow and likely to grow later with explicit runner/model flags
- explicit shared behavior doc:
CCC_BEHAVIOR_CONTRACT.md
- Python and Rust currently accept control tokens in any order before the prompt:
- runner selectors such as
c,cx,cc,oc,k,cu,g,rc,cr,codex,claude,opencode,kimi,cursor,gemini,roocode, andcrush +0..+4thinking levels:provider:modeland:model@namefor preset lookup; if no preset exists, runner names such as@kselect that runner before ordinary agent fallback; presets can also define a default prompt, and aliasprompt_mode = "prepend"|"append"can compose alias prompt text around an explicitly supplied prompthelp,-h, and--helpwin anywhere in argv and print help immediately- Python and Rust search project-local
.ccc.tomlfiles upward from the current directory and override the global config chain ccc configprints every existing config file path and raw contents in merge order:~/.config/ccc/config.toml,XDG_CONFIG_HOME/ccc/config.toml, then the nearest project-local.ccc.toml;CCC_CONFIGstill wins alone when it points at an existing fileccc config --editopens the selected config in$EDITOR; add--userto edit the XDG/home user config or--localto edit the nearest.ccc.toml(creating one in the current directory if none exists)ccc add [-g] <alias>starts a line-prompt wizard for writing[aliases.<name>]config; flags such as--runner,--model,--prompt, and--prompt-modecan prefill values, and--yeswrites non-interactivelyformatted,stream-formatted, andccc addmenu prompts honorFORCE_COLOR/NO_COLORbefore falling back to TTY detection- formatted modes always keep unhandled structured JSON lines in the run transcript;
CCC_FWD_UNKNOWN_JSONcontrols whether they are also forwarded to stderr and currently defaults on --print-configto print the canonical exampleconfig.toml--permission-mode safe|auto|yolo|plan--save-sessionto explicitly allow normal runner session persistence--cleanup-sessionto try post-run cleanup when a runner lacks a no-persist flag--output-log-path/--no-output-log-pathto enable or suppress the final stderr footer that points at the run artifact directory--show-thinking/--no-show-thinking--yolo/-y
- runner selectors such as
--forces the rest of argv to be treated as prompt text, even if it starts with control-like tokens- Python and Rust currently use
claude -p --no-session-persistence,codex exec --ephemeral,cursor-agent --print --trust,gemini --prompt, andcrush runfor non-interactive invocation - By default Python and Rust avoid saved sessions where the selected CLI supports it; OpenCode, Kimi, Cursor, Gemini, Crush, and RooCode warn that the runner may save a session unless
--save-sessionor--cleanup-sessionis used ccc --print-configis the source of truth for the current canonical config schema:[defaults],[abbreviations], and[aliases.<name>]ccc configis the source of truth for which config files currently resolve in the active shellccc config --edit [--user|--local]opens the selected config in$EDITOR; user config meansXDG_CONFIG_HOME/ccc/config.tomlwhen XDG is set, otherwise~/.config/ccc/config.toml, and local config means the nearest existing.ccc.tomlor a new.ccc.tomlin the current directoryccc add <alias>writes the active write target: project-local config when present, otherwise the effective global config; when no config exists it creates a new global config underXDG_CONFIG_HOME/ccc/config.tomlor~/.config/ccc/config.toml, and-gforces the effective global config instead of a project-local filecccwritesoutput.txtplus exactly one transcript file in each run directory:transcript.txtfor text and human transcript paths,transcript.jsonlfor JSON-oriented paths;textrequests that are upgraded into structured streaming still usetranscript.txt- each run directory is client-prefixed, for example
opencode-<run-id> cccprints a stable stderr footer in the form>> ccc:output-log >> /abs/path/to/run-dirunless--no-output-log-pathis set
- planned config support should eventually allow:
- broader multi-language rollout for the Python/Rust config surface
- multi-provider, multi-preset, and multi-alias routing policies
- broader multi-language rollout is still pending for the extended control-token surface
- import path:
call_coding_clis - current API:
CommandSpec,CompletedRun,Runner,render_example_config
- crate name:
call-coding-clis - library name:
call_coding_clis - current API:
CommandSpec,CompletedRun,Runner,render_example_config
- living backlog of unfinished work: TASKS.md
- implement remaining 12 languages (see
PLAN.mdin each directory) - parser and config design for planned alias, thinking, runner, and provider/model selectors:
CCC_PARSER_CONFIG_DESIGN.md - language scaffold doc:
ROADMAP_LANGUAGE_SCAFFOLDS.md - cross-language test harness design:
TEST_HARNESS_PLAN.md - broader rollout of expanded
ccctoken parsing for@name,+0..+4,:provider:model,:model, and runner selectors beyond Python/Rust - future advanced tool allow/deny design note: docs/clis/allow-deny-tool-plan.md
- shared model-thinking capability source of truth and refresh instructions: docs/clis/model-capabilities.json and docs/clis/updating-model-capabilities.md
- broader rollout of Python/Rust config-backed presets, runner abbreviations, agent defaults, and provider/model resolution
- richer stdin/cwd/env coverage and docs for every implementation
- v2: templated or user-customizable rendering for structured output
- v2: HTTP/HTTPS delivery of run artifacts and final output logs, tracked in TASKS.md
Unlicense — see UNLICENSE.