CliSubprocessCore is the shared runtime foundation for first-party CLI
providers. It owns the raw subprocess transport, the normalized session/event
model above that transport, and the built-in provider profiles that turn
provider-specific JSONL streams into a stable core vocabulary.
The library is designed for two consumers:
- callers that only need process ownership and mailbox delivery through
CliSubprocessCore.Transport - callers that want provider resolution, command construction, parsing, event
sequencing, and normalized payloads through
CliSubprocessCore.Session
CliSubprocessCore.EventandCliSubprocessCore.Payload.*define the shared runtime event vocabulary.CliSubprocessCore.ProviderProfileandCliSubprocessCore.ProviderRegistrydefine and manage provider profile modules.CliSubprocessCore.ProviderProfiles.*ships first-party profiles for Claude, Codex, Gemini, and Amp.CliSubprocessCore.TransportandCliSubprocessCore.Transport.Erlexecown subprocess lifecycle, stdout/stderr dispatch, interrupt, close, and force-close behavior.CliSubprocessCore.Sessionadds provider-aware parsing, sequencing, and subscriber fan-out on top of the raw transport.CliSubprocessCore.Runtime,CliSubprocessCore.LineFraming,CliSubprocessCore.ProcessExit, andCliSubprocessCore.TaskSupportsupport the transport and session layers.
Add the dependency and start the application normally:
def deps do
[
{:cli_subprocess_core, path: "../cli_subprocess_core"}
]
endUse the raw transport when you only need subprocess IO:
ref = make_ref()
{:ok, transport} =
CliSubprocessCore.Transport.start(
command: CliSubprocessCore.Command.new("sh", ["-c", "cat"]),
subscriber: {self(), ref}
)
:ok = CliSubprocessCore.Transport.send(transport, "hello")
:ok = CliSubprocessCore.Transport.end_input(transport)
receive do
{:cli_subprocess_core, ^ref, {:message, "hello"}} -> :ok
endUse the session layer when you want provider command building and normalized events:
ref = make_ref()
{:ok, _session, info} =
CliSubprocessCore.Session.start_session(
provider: :claude,
prompt: "Summarize this repository",
subscriber: {self(), ref},
metadata: %{lane: :core}
)
IO.inspect(info.capabilities)
receive do
{:cli_subprocess_core_session, ^ref, {:event, event}} ->
IO.inspect({event.sequence, event.kind})
endThe default registry starts with these ids:
:claude:codex:gemini:amp
You can append additional built-in modules through application config:
config :cli_subprocess_core,
built_in_profile_modules: [MyApp.ProviderProfiles.Example]Ad hoc profiles can also be registered at runtime with
CliSubprocessCore.ProviderRegistry.register/2.
guides/getting-started.mdguides/event-and-payload-model.mdguides/provider-profile-contract.mdguides/custom-provider-profiles.mdguides/built-in-provider-profiles.mdguides/raw-transport.mdguides/session-api.mdguides/testing-and-conformance.mdguides/shutdown-and-timeouts.md
- Hex:
https://hex.pm/packages/cli_subprocess_core - HexDocs:
https://hexdocs.pm/cli_subprocess_core - GitHub:
https://github.com/nshkrdotcom/cli_subprocess_core - Changelog:
CHANGELOG.md - License:
LICENSE
The repo-local quality gate is:
mix format --check-formatted
mix test
mix credo --strict
mix dialyzer
mix docs