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:
| Guide | What it covers |
|---|---|
| Install & configuration | Install (npm, npx, tarball, source), Node requirement, token/endpoint precedence, config file, jentrix login (OAuth), and jentrix whoami. |
| Command reference | The jentrix tool escape hatch, generated noun verb commands, command groups, flag derivation, cross-cutting flags, output/--json, and the frozen exit codes. |
| Recipes & scripting | Idempotency keys, the CONFLICT merge loop, rate-limit retry, pagination, jq patterns, agent usage, and CI. |
| Releasing & distribution | The 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/runnerprovides the session host thatjentrix sessionneeds — every deployment requires it, it is a plain npm install, and nothing below applies to it. It also providesjentrix 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 setupthere fails withCONTROL_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.
| Code | Recovery |
|---|---|
RUNNER_NOT_INSTALLED | Run the exact npm install -g … command printed by the CLI. |
RUNNER_VERSION_MISMATCH | Install 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_TIMEOUT | Rerun the same setup command. Apply is idempotent and may already have completed some steps. |
BINARY_NOT_FOUND | Install the named provider CLI manually, then rerun doctor. |
VERSION_PROBE_FAILED | Update or repair the named provider CLI, then rerun doctor. |
AUTH_REQUIRED | Run claude auth login or codex login. |
AUTH_UNVERIFIED | Repair/update the provider CLI; the runner will not prove auth by making a model call. |
RUNTIME_INCOMPATIBLE | Update the provider CLI/SDK, then rerun doctor. |
MODEL_UNAVAILABLE (enrolment) | Choose or publish a model available to the local runtime. |
STACKS_LOGIN_REQUIRED | Run jentrix login. |
RATE_LIMITED | Wait for the reported retry interval, then rerun the same command. |
WORKSPACE_REQUIRED | Pass an unambiguous --workspace. |
WORKSPACE_ROLE_REQUIRED | Use 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_INVALID | Fix permissions (chmod 600) or rerun setup. |
ORGANIZATION_MEMBERSHIP_REQUIRED | Your account is not a member of the target workspace — ask an owner for an invitation. |
PROJECT_REQUIRED | Confirm a project (--project in non-interactive mode); no unbound session exists. |
PROJECT_REPO_MISMATCH | The 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_LOGIN | Non-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_ACTIVE | Start a new session or resume an eligible interrupted one. |
SESSION_ALREADY_BOUND | The provider thread already has an active Jentrix session — reconnect to it or end it first. |
SESSION_RESUME_CONFLICT | Project/repository/operator/provider continuity failed; start a new session. |
PROVIDER_SESSION_UNAVAILABLE | The provider surface exposed no thread id; start fresh with jentrix session <provider>. |
ARTIFACT_UPLOAD_REFUSED | Size/MIME/checksum/visibility/session binding failed — read the message. |
ARTIFACT_PART_REDACTED | The 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 usableoriginremote is supported: it is identified aslocal/<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 thejentrix-runnersession 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 theREPOlink right there (viaadd_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|codexregisters 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 printsSESSION BOUND BUT NOT RECORDING. jentrix session enddefers 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,endcompletes 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;endexits8(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 (5conflict,7transport, …), and a clean or capture-off close is0. From--json, readcaptureStatus(OFF_BY_DESIGN/COMPLETE= clean,PENDING/ERROR= debt) — it decides the same thing the exit code does, withstatus: "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
COMPLETEDwithcaptureComplete=false. The sweep interrupts abandoned sessions after the five-minute liveness window. jentrix session doctoris 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 thePROJECT_REPO_MISMATCHrefusal at session start, with the sameadd_project_linkfix; only an unprovable case (a workspace-pinned credential, where discovery cannot run) stays a warning.jentrix session statusalso 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 ended — COMPLETED
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 single — Artifact.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
--refcommand 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
findingspush, the CLI offers to mint a card on the workspace's Issues (BUGS) board — creating the task, referencing the artifact onto it, and addingRELATES_TOback to the anchor. Pass--yesto 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 (/mcp → jentrix →
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: .log → LOG, .csv → CSV,
.png → SCREENSHOT, … 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.