Skip to main content

CLI (jentrix)

jentrix is a command-line client for the Jentrix MCP surface. It turns every MCP tool into a shell command — jentrix task list --board X --json | jq … — so agents and humans can drive boards from bash with no bespoke API.

The CLI is a thin client over the same MCP endpoint the agent platform documents. It sends nothing the MCP server doesn't already accept, so it inherits scopes, workspace pinning, rate limits, idempotency, expectedUpdatedAt freshness, and agent attribution by construction. Anything you can do over MCP you can do here, and the invariants are identical.

Guides

This page is the overview; the details live in four companion guides:

GuideWhat it covers
Install & configurationInstall (npm, npx, tarball, source), Node requirement, token/endpoint precedence, config file, jentrix login (OAuth), and jentrix whoami.
Command referenceThe jentrix tool escape hatch, generated noun verb commands, command groups, flag derivation, cross-cutting flags, output/--json, and the frozen exit codes.
Recipes & scriptingIdempotency keys, the CONFLICT merge loop, rate-limit retry, pagination, jq patterns, agent usage, and CI.
Releasing & distributionThe go-live runbook: publishing @jentrix/cli to npm, the tag-triggered release CI, version bumps, and troubleshooting.

Quickstart

# 1. install (Node >= 20) — CLI only; for connected sessions + the Claude Code
#    plugin, use the full-toolchain line instead:
#    npm install -g @jentrix/cli @jentrix/runner && jentrix plugin install
npm install -g @jentrix/cli

# 2. point it at a server with a token (mint a PAT at /account/tokens)
export STACKS_TOKEN=tm_your_token_here
export STACKS_MCP_URL=https://tm.jentrix.ai/api/mcp   # default; override for dev

# 3. confirm what the token can prove
jentrix whoami

# 4. drive a board
jentrix task list --board board_1 --json | jq -r '.tasks[] | "\(.key)\t\(.title)"'

Token and endpoint resolve in a fixed precedence — --token/--url flag → STACKS_TOKEN/STACKS_MCP_URL env → config file. Full detail, including the config file and browser login, is in Install & configuration.

Ready to apply the CLI to a real repository? Set up your first project provides a copy/paste prompt for a coding agent to analyze the repository, propose the Jentrix structure, and create it only after you approve the plan.

Local workflow runner

Two different things share one package. @jentrix/runner provides the session host that jentrix session needs — every deployment requires it, it is a plain npm install, and nothing below applies to it. It also provides jentrix runner setup / up, which enrols this machine as a worker node against the control plane so it can claim governed workflow jobs. That second half needs the agent-operations planes and does not work against a product deployment: runner setup there fails with CONTROL_PLANE_UNAVAILABLE. This section is about the second half.

Install matching packages on the machine that will execute work (Node 20+):

claude auth login     # Claude Code subscription
codex login           # ChatGPT subscription
npm install -g @jentrix/cli @jentrix/runner && jentrix login --scope read write admin && jentrix runner setup --workspace my-workspace --runtime claude

Install and sign in to at least one provider tool yourself. Setup never installs provider software or automates provider login.

jentrix runner doctor [--json] is read-only. It finds absolute claude and codex executables, checks versions, and uses prompt-less SDK initialization or read-only local RPC to report authentication and models. It creates no provider conversation and makes no model request. Account identity and raw provider output are neither printed nor persisted.

jentrix runner setup [--workspace ID-OR-SLUG] [--runtime claude|codex|all] always prints the full no-write plan first. --runtime configures only that locally authenticated runtime; all is the CLI default. One selected runtime can fill every role when its adapter supports them, so Claude and Codex are not both required. An interactive terminal asks once before apply; automation must pass --yes. Apply reuses unambiguous subscription connections, model profiles, onboarding agent profiles, and the current worker node. It writes the local config atomically at mode 0600, enrolls through the existing worker identity, and renews signed node-local attestations. It does not publish a release, approve a gate, or launch work. Publish and activate the reviewed worker release separately before jobs can use a newly-created draft binding.

jentrix runner up [--once] re-probes the configured absolute executables, heartbeats and renews attestations, then runs the existing workflow runner in the foreground. --once drains available work and exits after two idle polls. SIGINT/SIGTERM stop new claims, drain active work, and exit; no background service is installed. Packaged polling startup binds the inherited webhook listener to loopback. The configured role binding is presented at claim, the server's durable release route must match exactly, and subscription work uses the existing single-use node-bound credential claim. There is no provider fallback after setup.

Protocol 1 requires @jentrix/cli and @jentrix/runner at the same version. A mismatch fails before setup or work; install them together and let them stay in lockstep — jentrix plugin install auto-installs the runner at the CLI's own version for exactly this reason.

Runner blockers

Rows marked (enrolment) can only occur during runner setup / up against a full deployment; the rest apply to any deployment.

CodeRecovery
RUNNER_NOT_INSTALLEDRun the exact npm install -g … command printed by the CLI.
RUNNER_VERSION_MISMATCHInstall matching CLI and runner versions.
CONTROL_PLANE_UNAVAILABLE (enrolment)Deploy the matching Jentrix control plane or point STACKS_MCP_URL at one that supports runner setup.
CONTROL_PLANE_TIMEOUTRerun the same setup command. Apply is idempotent and may already have completed some steps.
BINARY_NOT_FOUNDInstall the named provider CLI manually, then rerun doctor.
VERSION_PROBE_FAILEDUpdate or repair the named provider CLI, then rerun doctor.
AUTH_REQUIREDRun claude auth login or codex login.
AUTH_UNVERIFIEDRepair/update the provider CLI; the runner will not prove auth by making a model call.
RUNTIME_INCOMPATIBLEUpdate the provider CLI/SDK, then rerun doctor.
MODEL_UNAVAILABLE (enrolment)Choose or publish a model available to the local runtime.
STACKS_LOGIN_REQUIREDRun jentrix login.
RATE_LIMITEDWait for the reported retry interval, then rerun the same command.
WORKSPACE_REQUIREDPass an unambiguous --workspace.
WORKSPACE_ROLE_REQUIREDUse an admin-scoped login with workspace ADMIN access.
CONNECTION_AMBIGUOUS (enrolment)Resolve competing subscription connections in Providers.
ROLE_PLAN_UNSATISFIED (enrolment)Make every durable role runtime/model available; no fallback is attempted.
NODE_ENROLLMENT_FAILED (enrolment)Rerun the same setup; persisted identity makes later-step retries converge.
ATTESTATION_FAILED (enrolment)Re-login locally, rerun doctor, then setup/up to renew proof.
BOOTSTRAP_REQUIRED (enrolment)Rerun setup to mint the existing self-hosted bootstrap path.
RUNNER_CONFIG_INVALIDFix permissions (chmod 600) or rerun setup.
ORGANIZATION_MEMBERSHIP_REQUIREDYour account is not a member of the target workspace — ask an owner for an invitation.
PROJECT_REQUIREDConfirm a project (--project in non-interactive mode); no unbound session exists.
PROJECT_REPO_MISMATCHThe selected project does not link the checkout repository. Interactive sessions offer to add the REPO link on the spot (workspace ADMIN role; write scope suffices) and retry; otherwise add it in project settings → Linked items or via add_project_link (a pasted GitHub URL is accepted and normalized to owner/name).
PROJECT_DISCOVERY_REQUIRES_UNPINNED_LOGINNon-interactive only: use an unpinned human login for cross-workspace discovery, or pass an exact --project inside the credential's pin. An interactive terminal instead gets the normal project picker scoped to the pinned workspace, behind an explicit scope banner.
SESSION_NOT_ACTIVEStart a new session or resume an eligible interrupted one.
SESSION_ALREADY_BOUNDThe provider thread already has an active Jentrix session — reconnect to it or end it first.
SESSION_RESUME_CONFLICTProject/repository/operator/provider continuity failed; start a new session.
PROVIDER_SESSION_UNAVAILABLEThe provider surface exposed no thread id; start fresh with jentrix session <provider>.
ARTIFACT_UPLOAD_REFUSEDSize/MIME/checksum/visibility/session binding failed — read the message.
ARTIFACT_PART_REDACTEDThe trace-part slot was redacted and is terminal; keep the local spool — the gap is recorded.

Runner config defaults to ~/.config/stacks/runner.json. It contains revocable node/bootstrap credentials, so never copy it into a repository or logs. Setup grants travel over stdin, not argv, and orchestration credentials are scrubbed from Claude/Codex child environments. Provider subscription credentials remain owned by Claude Code/Codex and are never stored by Jentrix.

Setup identity phase (M20.1)

jentrix runner setup begins with an explicit identity phase: with no usable credential on an interactive terminal it runs the existing in-process OAuth login (scopes read write admin; the consent screen stays the authority), then displays the SERVER-derived operator — Jentrix account, workspace, membership role, and whether runner administration is allowed. The CLI never submits an identity claim; --yes/non-interactive runs never initiate OAuth and require an existing credential. A MEMBER preview still proves who is signed in before WORKSPACE_ROLE_REQUIRED refuses structural changes; OWNER/ADMIN with admin scope may apply.

Connected sessions (M20.1)

jentrix session binds interactive Claude Code / Codex work to ONE confirmed project (the project locks the workspace) and records the observable path as redacted TRACE artifacts plus a deterministic RUN_SUMMARY:

jentrix session claude [--project <id-or-slug>] [--resume <jentrix-session-id>]
jentrix session codex  [--project <id-or-slug>] [--resume <jentrix-session-id>]
jentrix session attach [--provider claude|codex] [--project <id-or-slug>]
                      [--provider-session <id>] [--transcript-path <path>]
                      [--import-history] [--watch]
jentrix session doctor [--project <id-or-slug>] [--json]
jentrix session status [<jentrix-session-id>] [--json]
jentrix session end    [<jentrix-session-id>] [--json]
  • Start inspects the checkout (normalized owner/name, branch, HEAD, dirty — never an absolute path server-side). A checkout with no usable origin remote is supported: it is identified as local/<directory-name>, which is stable while the folder keeps its name — everything below then behaves exactly as it does for a remote-backed repository. Start resolves matching projects cross-workspace (resolve_projects_for_repo), always confirms the project, creates the Jentrix session BEFORE the provider launches, then hands off to the jentrix-runner session host (the CLI itself never loads provider SDKs). A workspace-pinned credential still gets the same confirmation picker — scoped to the pinned workspace behind an explicit scope banner (other workspaces are not searched); non-interactive pinned runs must pass --project. If the confirmed project does not link the checkout repository, an interactive session offers to add the REPO link right there (via add_project_link; workspace ADMIN role — write scope suffices) and retries. A workspace with no explicit repo links can instead rely on its workspace default repository, which sessions inherit.
  • Attach binds the CURRENT provider session: the id comes from trusted provider lifecycle context (the provider plugin's hooks or process-owned provider id), never from model-authored text. Both plugins ship bundled with the npm package — jentrix plugin install claude|codex registers one (see Install & configuration); repeating attach converges on the one active session. Pre-attach history stays honestly unavailable unless imported through a supported provider surface. When a trusted transcript path is available, a Claude attach STARTS the capture host — foreground with --watch, detached in the background otherwise (the output names the host pid). Codex watches the shared lifecycle ledger filtered by the exact task id; its hooks expose no token receipts, so token fields remain honestly unavailable. With neither a trusted Claude transcript nor a Codex hook ledger, the CLI prints SESSION BOUND BUT NOT RECORDING.
  • jentrix session end defers to a live local capture host: the host flushes the spool, submits the verified manifest, and completes the session itself (the CLI reports its verdict). Without a live host, end completes server-side directly — submitting the host's last spooled usage snapshot when one exists, so a dead host does not erase the session's telemetry.
  • Capture spools locally at mode 0600, redacted BEFORE anything durable is written, uploads with retry, and heartbeats at most every 30 seconds. Network loss keeps the spool and prints capture pending; end exits 8 (capture INCOMPLETE (recorded)) until every part is acknowledged. That is a successful close with recorded capture debt, distinct from a failed close — those keep their own codes (5 conflict, 7 transport, …), and a clean or capture-off close is 0. From --json, read captureStatus (OFF_BY_DESIGN/COMPLETE = clean, PENDING/ERROR = debt) — it decides the same thing the exit code does, with status: "COMPLETED" alongside it. The local spool is deleted only on genuine server acknowledgement — never on an audit stub.
  • Lifecycle and capture integrity are separate: a normal end with missing evidence is COMPLETED with captureComplete=false. The sweep interrupts abandoned sessions after the five-minute liveness window.
  • jentrix session doctor is the one-shot preflight: it checks the credential and its workspace pin, the runner install, spool-root writability, the repository identity, project resolution, the repo link, and the project's board link (a warn, not a blocker — align self-provisions a board when a new task is requested) — then reports EVERY failure at once with its fix, instead of one blocker per session-start round trip. Read-only; exits 0 only when nothing blocks. With --project, a missing explicit repo link is verified against repository discovery (the server's own matching): a project discovery disowns is a FAILURE predicting the PROJECT_REPO_MISMATCH refusal at session start, with the same add_project_link fix; only an unprovable case (a workspace-pinned credential, where discovery cannot run) stays a warning.
  • jentrix session status also reports LOCAL capture liveness (the host process and when the spool last grew) from this machine's spool directory — the server cannot see the local capture leg, so a session that is bound but recording nothing is visible immediately, not after the abandonment window. If the host exits nonzero while the server still shows the session open, the CLI says so loudly and names the remedy (jentrix session end <id>).

Alignment & typed pushes (Jentrix MVP)

jentrix align upgrades the connect flow into the MVP alignment wizard (MVP surface): it attaches the CURRENT provider session (same trusted lifecycle context as jentrix session attach), asks the fixed choice-selection question set — workspace, project, work item, owner, confirmation — and submits one server-confirmed alignment. The server builds the snapshot; the CLI echoes it verbatim. Claude Code drives it through /jentrix-align; Codex uses $jentrix-align; plain terminals get the CLI's numbered pickers. Re-running shows the current alignment and offers re-align (always visible in the activity feed). First-run setup is self-service on the default read+write login: the wizard's "create a new project" path seeds a board plus the REPO and BOARD links in the same pass, and an existing project with no board gets one auto-provisioned (and linked) when a new task is requested — no operator intervention. TRACE capture stays OFF for aligned sessions unless --capture re-enables it — the session host still heartbeats and attests telemetry from provider receipts, follows the login credential's OAuth rotation through the config file (a long session survives token refresh), and keeps the rollup spool-durable so even a killed host loses no telemetry.

Flush-before-align (task-performance-monitoring PRD §6): a re-align that CHANGES the task first asks the live session host to post a usage receipt on the previous alignment, so spend observed so far lands on the task that was actually in progress before its interval closes. The CLI prints which arm happened — acknowledged, or a bounded timeout ("mid-switch spend stays bounded by one heartbeat window"). Host-side ordering only: no new question, no new flag, and a dead host never blocks the align.

jentrix align [--project <id-or-slug>] [--task <id> | --new-task "<title>" | --session-level]
             [--owner <userId>] [--agent <name>] [--agent-emoji <e>]
             [--budget <tokens>] [--capture] [--questions --json] [--json --yes]
             [--provider-session <id>] [--transcript-path <file>]
jentrix push plan|decision|findings|report|diff|deliverable|learning|prompt|goal|prd [file]
             [--title <t>] [--session <id> | --task <id>]
             [--agent <name>] [--agent-emoji <e>]
             [--ref <artifactId>] [--yes] [--json]
jentrix session snapshot [--event <name>] [--json]     # driven by the PreCompact hook

jentrix push reads a file or stdin, redacts locally, and stores a typed artifact linked to the aligned session's project/task (re-redacted server-side). An unaligned checkout is refused with the align command named.

How the push is addressed, in order: --session <id>, then --task <id>, then this checkout's alignment marker. --task needs no session, no alignment marker, and no running host — the artifact lands on that task directly:

jentrix push findings notes.md --task task_1

Both addressings go through one server-side ingestion function: the same two-pass redaction over the full body before the checksum, the same sha256 dedupe, the same object store, the same activity and events.

An artifact is a durable fact about a task, so it does not require liveness. A session swept INTERRUPTED (the host stopped heartbeating — a closed laptop, a lost network) still accepts pushes. A session a human endedCOMPLETED or CANCELLED — is sealed and refuses with SESSION_NOT_ACTIVE; when that happens to the marker's session, jentrix push falls back to the marker's own task, says so on stderr, and exits 0 rather than losing the artifact.

A push that lands on no task (a --session-level alignment) prints a caution and still exits 0 — a disclosure, not a refusal.

--agent labels the producer (control room 04-1). On align it labels the session, and the confirmation card gains one line naming it — the wizard's seven questions and their order are unchanged; disclosure lines may be added, questions never. On push it labels one contribution and OVERRIDES the session's label, which is what makes a fan-out of subagents through a single session legible as separate producers:

jentrix align --project proj_1 --new-task "Triage the backlog" --agent triage-bot --agent-emoji 🐛
jentrix push findings notes.md --agent subagent-3

--budget <tokens> arms a per-session token budget (control room 04-3), and adds one line to the confirmation card naming the number. Crossing it records an activity row and badges the anchored task's card — it never stops the session, and the disclosure says so. Omitting the flag on a later re-align leaves an existing budget armed.

--ref <artifactId> --task <id> references an existing artifact instead of uploading bytes (control room 04-6): one artifact row, N tasks, and provenance stays singleArtifact.taskId still names the task that produced it, and the destination card shows the row marked linked. A reference needs no alignment and reads no file; a cross-workspace reference is refused server-side. Linking an already-linked pair is a no-op, not an error.

--ref is addressing, not an eighth kind — the seven push kinds are unchanged.

Two offers ride an ordinary push, and both only ever OFFER:

  • When the same bytes are already stored on another task, the push says so and prints the --ref command that would replace the second row with a reference. The report's only cross-task handoff was exactly this — one checksum, two rows that disagree the moment one is revised.
  • After a findings push, the CLI offers to mint a card on the workspace's Issues (BUGS) board — creating the task, referencing the artifact onto it, and adding RELATES_TO back to the anchor. Pass --yes to accept it in a non-interactive run. With no BUGS board it says so and skips; it never creates a board or a column. A failed offer never costs the operator their push: the artifact is already durable, so the failure is a note and the exit code stays 0.

The label is a loose string with no registry lookup — under the 2026-08-13 product split the agent registry is the parent deployment's plane. Absent, the contribution is the operator's own and renders as such; it is never rendered as an unknown agent. A --task push with no --agent inherits nothing, because there is no session and therefore no producer to inherit from. The model the provider actually ran is recorded separately by the session host from the transcript it already tails, and needs no flag.

The alignment marker is keyed by provider session id, so concurrent provider sessions in one checkout each resolve to their own alignment instead of whichever aligned last. A named session resolves only its own entry: with no entry it has no alignment and is told to run jentrix align, because inheriting the neighbour's is how one checkout came to look like it holds a single alignment. A caller that cannot name its session (a plain shell) never inherits a marker, even when only one exists; pass --session <id> or --task <id>. A marker written by an older CLI is likewise unreadable without trusted identity and is migrated in place on the next write.

jentrix session end follows the same rule: with no id it closes this session's aligned session, falling back to the one active session this machine bound to the checkout and refusing when several are open.

--transcript-path is --provider-session's twin. The session id and the transcript the host watches are ONE decision: correcting the id while the transcript still came from a directory-matched hook record produces a host that looks healthy and records no telemetry at all. An explicit id resolves the transcript its own hook record names; --transcript-path overrides both, and a transcript proven to belong to another session refuses the host launch rather than starting it. The --questions payload carries a session precondition naming which session resolved, how confidently, and the fix.

Submitting also writes ./.mcp.json so the checkout's agent tools reach the same deployment (merge-not-clobber; never a credential; it applies from the next session, since MCP servers load at session start). By default it writes no Authorization header: a header suppresses the client's OAuth discovery, so the agent authenticates with the deployment itself and needs no second token — approve the consent screen once (/mcpjentrix → Authenticate). Any header already in the file is removed, and the removal is disclosed. --pat restores the Bearer ${STACKS_TOKEN} template for headless contexts that cannot approve a consent screen. See MVP surface for the full rules.

jentrix session snapshot is the compaction handler. Claude can preserve the approved transcript range; Codex exposes no transcript in lifecycle hooks, so its watch host records the compaction boundary but snapshot uploads no bytes. It preserves; it does not distil (a hook has no model turn) — the provider's Jentrix checkpoint skill is the distillation half. Every failure exits 0 so a snapshot cannot break the operator's /compact.

Artifacts

jentrix artifact upload <file> stores a local file as a first-class artifact through the M20.1 upload-grant flow — the CLI sha256s the file, requests a single-use grant from POST /api/artifacts/upload-grant (the server chooses the object key; the grant binds workspace, checksum, MIME type, size, and a 15-minute expiry), PUTs the bytes to the presigned URL, then calls attach_artifact with the consumed grant:

jentrix artifact upload report.html --workspace ws_1 --project proj_1 --task task_1
jentrix artifact upload plan.md --task task_1 --kind plan

--task alone is enough: the grant route resolves the workspace from the task and returns it. (jentrix artifact attach stays the generated wrapper over the raw attach_artifact tool — external URLs and pre-issued grants.)

--kind <plan|decision|findings|report|diff|deliverable|learning|prompt|goal|prd> files the upload under the same ArtifactType — hence the same work layer — as jentrix push, so one vocabulary covers both writes. --type overrides everything (extension-inferred otherwise: .logLOG, .csvCSV, .pngSCREENSHOT, … default DOC); --title overrides the file name; --session attributes the artifact to a connected session; --run, --work-order, and --decision link the other subjects. A refused grant (unconfigured object storage, workspace pin) or a failed upload attaches nothing — the grant simply expires unused.

Two ways to call a tool

Generated noun verb commands — ergonomic, with real flags derived from each tool's schema:

jentrix task create --column-id col_1 --title "Ship the CLI"
jentrix task get --task task_123 --json
jentrix board list --workspace ws_1 --json

The jentrix tool escape hatch — any tool by name, including tools added to the server after this CLI was built, so a lagging CLI never blocks you:

jentrix tool list_workspaces --json
jentrix tool get_task --args '{"taskId":"task_123"}' --json

jentrix --help lists every command; jentrix <noun> <verb> --help shows a tool's flags and the scope it needs. See the command reference for the full model.

Output and exit codes

Pass --json for the tool's structuredContent as stable, key-sorted JSON (the machine contract) — pipe it straight into jq. Every failure maps to a frozen exit code (0 ok · 2 bad input · 3 forbidden · 4 not found · 5 conflict · 6 rate-limited · 7 transport/auth · 8 session end closed the session but recorded a capture gap), so scripts branch on $? without parsing text. Both are detailed in the command reference, and the scripting patterns that lean on them — idempotency, conflict merges, rate limits — are in recipes.

See also

  • MCP tool reference — the tool catalog, scopes, tokens, error envelope, idempotency, and OAuth the CLI sits on top of.
  • Agent setup variants — connecting Claude Code, Codex, Cursor, and the bundled reference agents (the CLI is a peer of these).
  • cli-standup — a bash-only reference agent that drives Jentrix entirely through this CLI, the worked example of the CLI as an agent substrate.
  • Getting started — running a Jentrix server locally to point the CLI at.