Skip to content
clankercodePublic

About

Minimal subprocess wrappers for calling coding CLIs across 8+ languages

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

233 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

call-coding-clis

Small libraries for calling coding CLIs from normal programs.

Running All Tests

./run_all_tests.sh

This 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

Running One Implementation

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 typescript

This 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.

Build

just build

This 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.

Individual Test Commands

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

Implementation Status

Implemented (runner + ccc CLI + cross-language tests)

  • Python — call_coding_clis package, real-runner override env support
  • Rust — call-coding-clis crate, concurrent streaming, real-runner override env support
  • TypeScript — runner + ccc CLI with streaming, CCC_REAL_OPENCODE support
  • C — reusable runner library (runner.c/runner.h) plus ccc binary
  • Go — go/ccc.go library with goroutine-based streaming, ccc CLI
  • Ruby — CallCodingClis module with runner/stream, ccc CLI
  • Perl — Call::Coding::Clis with runner, ccc CLI
  • C++ — C++17 with GoogleTest, cmake build, ccc CLI
  • Zig, D, F#, PHP, PureScript, x86-64 ASM, OCaml, Crystal, Haskell, Elixir, Nim — ccc implementations covered by targeted contract/harness checks

Planned / Follow-Up Tracked In PLAN.md

Implementation-specific follow-up work is tracked in each language's PLAN.md. VBScript remains planned.

Target CLIs

  • OpenCode
  • Claude / Claude Code
  • Codex
  • Kimi
  • Gemini CLI
  • Cursor Agent
  • RooCode
  • Crush
  • Qwen Code
  • Pi
  • similar terminal-first coding agents

Current Scope

  • 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

Cross-Language CLI Requirement

  • every language library should also bundle a CLI named ccc
  • the ccc interface 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 precurl can use the library layer while humans can use the same runner shape directly
  • precurl uses 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, --yolo / -y, and --fast / --no-fast
  • -v / --version prints the shared build version plus resolved client versions
  • Python and Rust runner tables in help, usage errors, and version output show the executable name (for example, cursor-agent), version/status, and version-lookup time in aligned columns. Timings use milliseconds with one decimal place; lookups over 50 ms appear red when color is enabled. Missing executables show not found and - for timing. Color follows FORCE_COLOR / NO_COLOR, then the output stream's TTY status.
  • Runner discovery searches PATH and resolves symlinks directly. Known installs read versions from package metadata, installer receipts, adjacent version files, or embedded build information instead of starting the CLI. Codex wrappers and npm/pnpm Pi launchers are followed through literal paths without evaluating shell code. See runner version discovery for supported layouts; unknown layouts retain the <cli> --version fallback.

First-Pass ccc Contract

  • 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

Current Python/Rust Extended ccc Syntax

  • Python and Rust currently accept control tokens in any order before the prompt:
    • runner selectors such as c, cx, cc, oc, k, cu, g, p, gb, rc, cr, codex, claude, opencode, kimi, cursor, gemini, pi, grok, roocode, and crush
    • +0..+6 thinking levels (0 none, 1 low, 2 medium, 3 high, 4 xhigh, 5 max, 6 ultra); +ultra maps to 6, +max to 5, and +xhigh to 4. Codex maps each level to -c model_reasoning_effort=<none|low|medium|high|xhigh|max|ultra> and defaults to medium; runners with fewer tiers clamp higher requests to their top tier
    • :provider:model and :model
    • @name for preset lookup; if no preset exists, runner names such as @k select that runner before ordinary agent fallback; presets can also define a default prompt, and alias prompt_mode = "prepend"|"append" can compose alias prompt text around an explicitly supplied prompt
    • alias fallback_models = ["model-a", "glm-5.3"] retries once per listed model when the run fails with a capacity or overload error before any assistant output. Mapped models (glm-5.3, llmp-glm-5-3, glm-5.3-flash) are retried on grok; other models stay on the same runner. JSON and formatted output from that retry is parsed with the fallback runner's schema. ccc says which model ran on stderr and in the run transcript. Authentication errors and failures after output has started do not fall back. Claude runners are not retried, and MiniMax, Claude, or Astra model ids are skipped. Primary use of those models, including @astra, stays allowed. Cursor auto presets use runner = "cursor" and model = "auto" (@cursor, @cu-auto).
    • help, -h, and --help win anywhere in argv and print help immediately
    • ccc --help lists configured aliases visible from the current directory and includes short agent tips for checking config, choosing aliases, and using -- for literal prompt text
    • Python and Rust search project-local .ccc.toml files upward from the current directory and override the global config chain
    • ccc config prints 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; an existing CCC_CONFIG file overrides this chain for execution, aliases, help, and config display
    • ccc config --edit opens the selected config in $EDITOR; add --user to edit the XDG/home user config or --local to 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-mode can prefill values, and --yes writes non-interactively
    • formatted, stream-formatted, and ccc add menu prompts honor FORCE_COLOR / NO_COLOR before falling back to TTY detection
    • formatted modes always keep unhandled structured JSON lines in the run transcript; CCC_FWD_UNKNOWN_JSON controls whether they are also forwarded to stderr and currently defaults on
    • --print-config to print the canonical example config.toml
    • --permission-mode safe|auto|yolo|plan
    • --save-session to explicitly allow normal runner session persistence
    • --cleanup-session to try post-run cleanup when a runner lacks a no-persist flag
    • --output-log-path / --no-output-log-path to enable or suppress the final stderr footer that points at the run artifact directory
    • --show-thinking / --no-show-thinking
    • --timeout-secs <N> kills the wrapped runner after N seconds, prints warning: timed out after N seconds; killed runner to stderr, and exits with status 124
    • --yolo / -y
    • --service-tier <default|fast|priority|ultrafast> selects Codex service priority. --fast / --no-fast select fast / default; other runners warn and ignore these controls. The last CLI tier control wins
    • --standalone / --no-daemon requests execution without the shared background daemon: Codex receives codex --no-daemon exec, and OpenCode receives opencode run --standalone. --no-standalone allows upstream defaults. Config defaults and aliases can set standalone = true|false; CLI overrides aliases, then defaults
  • -- 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, pi -p --no-session, grok --no-auto-update -p, and crush run for non-interactive invocation
  • By default Python and Rust avoid saved sessions where the selected CLI supports it; OpenCode, Kimi, Cursor, Gemini, Grok, Crush, and RooCode warn that the runner may save a session unless --save-session or --cleanup-session is used
  • ccc --print-config is the source of truth for the current canonical config schema: [defaults], [update], [abbreviations], and [aliases.<name>]
  • Python and Rust accept service_tier in [defaults] and [aliases.<name>]. CLI tier controls override the alias, which overrides defaults. Omit the field to inherit Codex's own configuration; default explicitly selects its standard tier. Fast and Ultrafast availability depends on the model and account
  • Python and Rust optionally check after each run whether a newer ccc is available (crates.io, GitHub releases fallback) and warn on stderr; set [update] check = false or CCC_UPDATE_CHECK=0 to disable, or [update] auto_update = true / CCC_AUTO_UPDATE=1 to background cargo install ccc when the install is cargo-based
  • ccc config is the source of truth for which config files currently resolve in the active shell
  • ccc config --edit [--user|--local] opens the selected config in $EDITOR; user config means XDG_CONFIG_HOME/ccc/config.toml when XDG is set, otherwise ~/.config/ccc/config.toml, and local config means the nearest existing .ccc.toml or a new .ccc.toml in the current directory
  • ccc 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 under XDG_CONFIG_HOME/ccc/config.toml or ~/.config/ccc/config.toml, and -g forces the effective global config instead of a project-local file
ccc c --service-tier ultrafast "Review these changes"
ccc c +ultra --service-tier ultrafast "Review these changes"
ccc c +ultra --service-tier ultrafast --standalone "Review these changes"
ccc @reviewer --no-fast "Review these changes"
ccc add quick --runner c --service-tier fast --yes

The alias wizard includes a service-tier menu with separate inherit and default choices. To remove a saved tier, run ccc add quick --unset service-tier --yes.

Codex ultra effort is +ultra / +6. Profiles use thinking = 6, and ccc add quick --thinking ultra --yes or the thinking menu can save it. Effort and service tier are independent controls.

  • ccc writes output.txt plus exactly one transcript file in each run directory: transcript.txt for text and human transcript paths, transcript.jsonl for JSON-oriented paths; text requests that are upgraded into structured streaming still use transcript.txt
  • each run directory is client-prefixed, for example opencode-<run-id>
  • ccc prints a stable stderr footer in the form >> ccc:output-log >> /abs/path/to/run-dir unless --no-output-log-path is set

Planned ccc Syntax Growth (design notes only, not fully rolled out yet)

  • 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

Python Package

  • import path: call_coding_clis
  • current API: CommandSpec, CompletedRun, Runner, render_example_config

Rust Crate

  • crate name: call-coding-clis
  • library name: call_coding_clis
  • current API: CommandSpec, CompletedRun, Runner, render_example_config

Planned Roadmap

  • living backlog of unfinished work: TASKS.md
  • implement remaining 12 languages (see PLAN.md in 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 ccc token parsing for @name, +0..+6, :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

Missing / Possible Future Features

  • 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

Licensing

Unlicense — see UNLICENSE.

About

Minimal subprocess wrappers for calling coding CLIs across 8+ languages

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages