mix cantrip.familiar (Cantrip v1.3.3)

Copy Markdown View Source

Run the Familiar in REPL mode (interactive), single-shot mode, or ACP server mode.

mix cantrip.familiar                          # REPL mode
mix cantrip.familiar "explain this codebase"  # single-shot
mix cantrip.familiar --acp                    # ACP stdio server

Options

  • --acp — start as an ACP stdio server instead of REPL
  • --diagnostics — print the cookie + remsh attach command on stderr (the BEAM is named regardless; this flag just makes the attach affordance visible)
  • --json — output events as JSONL stream (for piping/scripting)
  • --loom-path PATH — store the loom as JSONL at this path. When omitted, the loom is workspace-keyed Mnesia (BEAM-native).
  • --max-turns N — maximum turns per episode (default: 20)
  • --help — show this help

Loom backend

REPL and single-shot promote the BEAM to a workspace-stable named node and use Mnesia (disc_copies) keyed to the workspace as the loom backend. The same workspace re-summons the same loom across restarts, with prior turns visible as loom.turns.

Pass --loom-path PATH to use JSONL instead, when you want a portable, exportable, human-readable trace.

Summary

Functions

Build the Familiar from launcher opts. Pure construction — no process is started, no LLM call is made.

Workspace-stable node name. Two distinct workspaces produce two distinct names (so they don't share a Mnesia schema); the same workspace produces the same name across launches (so Mnesia's per-node disc_copies find the prior data).

Parses the task arguments into a routing decision.

Functions

build_familiar(opts)

@spec build_familiar(keyword()) ::
  {:ok, Cantrip.t()} | {:error, String.t()} | no_return()

Build the Familiar from launcher opts. Pure construction — no process is started, no LLM call is made.

Storage policy:

  • :loom_path set → JSONL at that path (caller's explicit portable-trace choice)
  • otherwise → workspace-keyed Mnesia, via Cantrip.Familiar.new/1's Mnesia-by-:root default (which the launcher always sets)

No defaulted JSONL — the launcher's job is to enable the BEAM-native posture the substrate documents, not to ship past it.

Raises KeyError if :llm is missing from opts. The launcher always passes :llm; a missing one is a programmer error, not a runtime condition.

node_name_for_workspace(root)

@spec node_name_for_workspace(String.t()) :: atom()

Workspace-stable node name. Two distinct workspaces produce two distinct names (so they don't share a Mnesia schema); the same workspace produces the same name across launches (so Mnesia's per-node disc_copies find the prior data).

parse_args(args)

@spec parse_args([String.t()]) ::
  {:help, %{opts: keyword()}}
  | {:acp, %{opts: keyword(), diagnostics: boolean()}}
  | {:repl,
     %{opts: keyword(), intent: nil | String.t(), diagnostics: boolean()}}

Parses the task arguments into a routing decision.

Pure function returning one of:

  • {:help, %{opts: opts}} — print usage and exit
  • {:acp, %{opts: opts, diagnostics: bool}} — run as ACP stdio server
  • {:repl, %{opts: opts, intent: nil | binary, diagnostics: bool}} — run interactive REPL (when intent is nil) or single-shot

diagnostics is mode-agnostic: any mode (REPL, single-shot, ACP) may request the remsh-attach affordance via --diagnostics. ACP, REPL, and CLI are projections of the same runtime; the diagnostic node is part of that runtime, not an ACP-specific concern.