Using the Sneka CLI
The command reference, organized into suite, Diffinite, and Gilly commands.
sneka is the suite command-line tool. Its reference is organized by product: Diffinite commands live under sneka dfnt, Gilly commands live under sneka gilly, and auth, inbox, agent, persona, team, skills, and completion commands are suite-level. For a copy-paste quick start (install, authenticate, first commands), see the CLI quick start.
Install it with:
curl -fsSL https://sneka.ai/cli/install.sh | bashOnce installed, update in place with sneka update (see below). sneka --version reports the release plus build provenance, e.g. sneka 1.5.0 (a1b2c3d, 2026-06-15). The CLI also prints a one-line nudge when a newer release is available; silence it with SNEKA_NO_UPDATE_CHECK=1.
Add --json before any subcommand to get raw JSON instead of formatted tables — handy for scripting.
Agents: web docs are for discovery, the CLI and skills are the operational source of truth. These pages explain the concepts. For version-exact steps to run, install the skills withsneka skills installand follow the installed skills plussneka <command> --help. The raw Markdown of any page is at/docs/<slug>.md, and an agent index of every page is at/llms.txt.
auth
Manage your local session.
sneka auth login
sneka auth status
sneka auth renew
sneka auth logout --revoke
sneka auth tokens
sneka auth revoke-token <token-id>
sneka auth setup-git [--unset]
sneka auth git-cert [--persona <slug>] --key-path <path>sneka auth login opens a browser when it can; over SSH it prints the authorization URL. Sign in, authorize the CLI, then paste the one-use browser code back into the terminal. To load an existing token instead, run sneka auth set with no arguments — it prompts securely with input hidden, so the token never lands in shell history. Use sneka auth set --stdin to pipe a token in non-interactively (automation or a password manager).
sneka auth renew mints a successor user token and stores it (sliding renewal) — also invoked automatically by sneka persona ensure when the user slot needs it. Renewal is refused once the credential's login chain is older than the server's chain-ceiling window; re-run sneka auth login when that happens. sneka auth refresh is a deprecated alias of auth renew — it mints the same successor token, kept only for backward compatibility.
sneka auth tokens lists every machine token you own — your user tokens plus all persona tokens, with labels. It shows metadata only (id, prefix, label, expiry, state); the plaintext is shown once at mint and never again. sneka auth revoke-token <token-id> revokes one token by id (idempotent — re-revoking an already-revoked token is a no-op success). Use these to find and kill a token when you have lost its plaintext.
Where your credentials are stored
sneka keeps your tokens in your operating system's keyring where one is available, and never in a file it can avoid:
- macOS — the macOS Keychain.
- Linux — the Secret Service / keyring (e.g. GNOME Keyring, as on Debian
and Ubuntu). If no keyring is running, sneka stores credentials in a 0600 file at ~/.config/sneka/credentials.json and prints a one-line notice saying so; set up gnome-keyring if you would rather use a keyring.
- Other platforms — no OS keyring support yet, so
snekauses the same
0600 file store and tells you.
The file store is the same shape aws, gh, docker, and codex/opencode use for their own credentials (a 0600 file). The SNEKA_TOKEN environment variable overrides all of this for a single command or a headless host — when it is set, no keyring or file is read.
Git: HTTPS or SSH
Diffinite serves Git over HTTPS and over SSH. Both push as the same identity and obey the same push rules.
The rule for choosing: use HTTPS. It needs nothing beyond the credential sneka already holds. Use SSH only when a remote or a tool must stay on an SSH URL.
| Transport | Remote URL | Credential |
|---|---|---|
| HTTPS | https://diffinite.sneka.ai/<owner>/<repo>.git | sneka git-credential, installed by sneka auth setup-git |
| SSH | git@diffinite.sneka.ai:<owner>/<repo>.git | a short-lived certificate from sneka auth git-cert |
#### HTTPS: setup-git and the credential helper
sneka auth setup-git — "Point Git at sneka git-credential for https://diffinite.sneka.ai." Run it once per machine:
sneka auth setup-git
git clone https://diffinite.sneka.ai/cartine/my-service.gitFrom its help text:
Writes two entries in your global Git config, scoped to that one host: an empty helper, which stops Git consulting any helper set for a broader scope, then!sneka git-credential. Running it again changes nothing. It never reads or writes your SSH configuration or theauth git-certtransport.
--unset: "Remove both entries instead, and change nothing else." sneka persona ensure runs the same step, so an agent session that ran ensure needs no separate setup-git unless ensure reported the step failed.
sneka git-credential get|store|erase — "Speak Git's credential protocol for https://diffinite.sneka.ai." You do not call it yourself: "Git runs this itself once sneka auth setup-git has pointed the host at it; run it by hand to read the credential Git would be handed." It answers with the credential of whoever sneka whoami names: SNEKA_TOKEN when set, otherwise the keyring or the 0600 file store. "A credential expiring within seven days, or carrying no expiry, is renewed before it is answered, so a long session keeps pushing without anyone running sneka persona ensure again." A SNEKA_TOKEN is handed over as given and never renewed. store and erase do nothing: Sneka owns this credential, so Git never stores or erases it. When the helper cannot answer, it prints the cause and the command that fixes it (sneka auth login, or sneka persona ensure for a persona), and Git stops instead of prompting.
A host with no keyring works the same way: the helper reads the 0600 file store. Without the helper, give Git any username and a Sneka token as the password.
#### SSH: git-cert
sneka auth git-cert — "Configure renewable SSH certificates for Git operations." It generates a keypair on first use and wires a short-lived certificate into Git's SSH transport. Minting one for a persona is an operator action; see agent personas.
whoami
Print the identity the current credential acts as — your user account, or an agent persona once sneka persona ensure has provisioned one for $SNEKA_AGENT. This is how a session confirms which identity it is acting as before doing any work:
sneka whoamipersona ensure / verify / unarchive
The agent-identity provisioning surface. See agent personas for the full email-policy and fleet model this backs.
sneka persona ensure [<slug>] [--display-name <name>] [--icon <key>]
[--ttl-days <n>] [--renew-within-days <n=7>]
[--label <text>] [--email <addr>] [--harness <kind>]
[--no-harness-config] [--print-token] [--no-store]
sneka persona verify <persona_id|slug> --token <emailed-token>
sneka persona unarchive <persona_id|slug>ensure is the one idempotent provisioning verb: it finds or creates the persona matching <slug> (defaulting to $SNEKA_AGENT), resolves and verifies its email, and mints or self-renews its token, storing the result in a named credential-store slot. Re-running it is always safe. --email wins over any config-file policy for that persona's first creation. --print-token only prints the minted token when the store is unavailable or --no-store is set — never alongside a normal store write. --renew-within-days controls how early a live token is renewed (default: 7 days before expiry); self-renewal after that uses the persona's own credential, not yours.
verify completes email verification with a token that was emailed to a dedicated mailbox (variation B/C — see the personas doc). unarchive is the sanctioned way back for a persona whose slug was archived; ensure names this command by itself when it hits an archived match.
sneka persona token mint — a separate, plaintext-printing mint command — no longer exists. ensure is the only mint path now. The persona token namespace keeps just two verbs:
sneka persona token list [<slug>] # slug defaults to $SNEKA_AGENT
sneka persona token revoke <token-id>list shows one persona's tokens (metadata only, never plaintext); revoke kills one by id (idempotent). Under $SNEKA_AGENT, listing a different slug's tokens is an operator action and is refused.
Harness identity self-configuration
Once the persona and its token are provisioned, ensure also makes sure the harness will keep acting as that identity: when $SNEKA_AGENT isn't already persisted in the harness's own config, ensure writes it there itself — a surgical merge that touches only the SNEKA_AGENT key (see agent personas for exactly what gets written per harness). --harness <kind> (claude/codex/opencode) forces detection; omit it to auto-detect from environment markers, then process ancestry. --no-harness-config skips this step entirely.
Because the injected env only reaches processes started after the write, a config write always ends with a restart notice naming the exact continuation command (e.g. claude --continue) — provisioning itself is already done; only routing of later commands in this session waits on the restart. Relay that notice to the operator verbatim. An unrecognized harness still gets full persona provisioning; ensure just reports the one honest gap (no automated recipe) and points at the manual fallback. A different existing value in the harness config is left untouched and reported as a conflict — ensure never overwrites your own settings.
--json adds a harness block so scripts and agents can branch without parsing prose:
{
"harness": {
"kind": "claude",
"configured": true,
"config_path": "/home/you/.claude/settings.json",
"restart_required": true,
"resume_command": "claude --continue"
}
}Git credential helper step
Before the harness step, ensure installs the Git credential helper — the same write sneka auth setup-git makes — so the session has Git over HTTPS with no other command. The report prints one line for it, for example "Git now gets credentials for https://diffinite.sneka.ai from sneka git-credential." or, on a second run, "Git already gets credentials for https://diffinite.sneka.ai from sneka git-credential; nothing to change." A failed Git-config write prints Git credential helper not installed: <git error> and does not fail ensure: the persona credential is already live, and a host without git must still start. Git over HTTPS then has no helper: fix the named Git error and run sneka auth setup-git. The restart notice stays the last line.
--json carries the same outcome in a git_credential_helper block. status is installed, already_installed or failed; detail is the report line:
{
"git_credential_helper": {
"status": "installed",
"detail": "Git now gets credentials for https://diffinite.sneka.ai from `sneka git-credential`."
}
}Diffinite
Settings commands
Branch freshness is an opt-in Diffinite account preference. It sends one suite inbox notification when an owned non-default branch remains unchanged past the chosen threshold. Inspect or change the preference with a user credential:
sneka dfnt settings get
sneka dfnt settings set --branch-freshness on
sneka dfnt settings set --freshness-days 7
sneka dfnt settings set --branch-freshness off --freshness-days 3The threshold defaults to three days and must be at least one. Each set command changes only the flags supplied and prints the resulting state. These are parent-account settings, so agent persona credentials cannot read or change them.
Removing merged branches automatically is a separate preference, and it belongs to one repository rather than to your account. Its commands are under repository commands, below.
Repository commands
Create a repository here when the project starts on your machine. If it already lives on GitHub, see move or mirror a repository; for the full first-push path, see push a new repository.
sneka dfnt repo list
sneka dfnt repo create my-service --private
sneka dfnt repo get-visibility cartine/my-service
sneka dfnt repo set-visibility cartine/my-service privateRepository automation may compare-and-delete a branch only through the server-owned guard. It requires repository write access, refuses protected names and branches associated with a ready Diff, and needs the full observed tip so a branch that moved since discovery is kept:
sneka dfnt repo reap-branch cartine/my-service agent/old-work \
--expected-tip 0123456789abcdef0123456789abcdef01234567create makes a repository owned by whoever the credential belongs to. Under $SNEKA_AGENT an agent persona acts as its parent user: the repository lands under the parent's owner slug and counts against the parent's quota, never the persona's, and there is no way to name a different owner.
To retire a repository, its personal owner or a current administrator of its owning team can stage deletion from repository settings or the CLI. Diffinite removes the repository from active views immediately. Git operations and normal repository API reads and writes reject it. Diffinite keeps its Git data, releases, and access settings for 48 hours. The full repository name stays reserved throughout that window. The pending view shows the server's exact deadline and a Restore action. Restoration before the deadline returns the same repository and access settings. At the deadline, restoration closes and Diffinite permanently removes the stored repository. A delayed cleanup never extends the restore window.
sneka dfnt repo delete cartine/my-service --confirm cartine/my-service
sneka dfnt repo pending-deletion
sneka dfnt repo restore cartine/my-serviceDeletion and restoration require a user credential. Agent personas cannot perform these ownership decisions or list pending repositories. Stored data continues to count against quota until permanent removal. Use sneka --json before a command to receive the server's deadline and status as JSON.
Repository owners and repository administrators can inspect or change a repository's visibility. A persona may run get-visibility on any repository it can read; set-visibility is an ownership decision and needs a user credential. set-visibility accepts exactly public or private; repeating the current value is safe. Put the global JSON switch before the command when scripting:
sneka --json dfnt repo get-visibility cartine/my-service
sneka --json dfnt repo set-visibility cartine/my-service publicBoth visibility commands return the canonical repository identity plus matching visibility and is_public values in JSON.
A repository can also remove its own merged branches for you. The setting is off for every repository, and arming one repository leaves every other repository you own untouched:
sneka dfnt repo get-auto-delete-merged-branches cartine/my-service
sneka dfnt repo set-auto-delete-merged-branches cartine/my-service onWith it on, Diffinite removes one of that repository's non-default branches once Git proves the branch's exact current tip is already represented in the default branch, including work merged, rebased, squashed, or cherry-picked outside Diffinite. Age is not part of that judgement, so a branch merged an hour ago goes as readily as one merged last month. A branch with any commit of its own, a tip that moved, a ready diff, or an unclear answer is kept.
Like set-visibility, arming removal is an ownership decision and needs a user credential. Standing consent to have refs deleted is the owner's to give.
Diff commands
A Diff is Diffinite's review and integration unit: a named branch moves from open to ready, then to merged or closed. Agent-authored commits should carry the Sneka agent note before the branch is published.
List the open, ready, and merged diffs for a repository:
sneka dfnt diff list cartine/quiltMove a diff through review:
sneka dfnt diff ready cartine/quilt feature/refactor --target main --description "Ready for review."
sneka dfnt diff unready cartine/quilt feature/refactor
sneka dfnt diff merge cartine/quilt feature/refactor
sneka dfnt diff close cartine/quilt feature/refactormerge is fast-forward only by default. When repository policy permits a Git-only automatic rebase, agents can request the conditional path:
sneka dfnt diff merge cartine/quilt feature/refactor --rebase-if-neededThat option fast-forwards when possible. Otherwise, it rebases the reviewed commits onto the current target and atomically verifies the source while moving only the target. If either ref moves again or the rebase conflicts, the target does not move; run the command again to re-check, or rebase locally when Diffinite reports a conflict.
Automated callers must pin both inputs to the merge they checked:
sneka dfnt diff merge cartine/quilt feature/refactor \
--expect-head <source-sha> --expect-target-head <target-sha> \
--rebase-if-needed--expect-target-head binds preparation and the final atomic target update to that exact target commit. A missing or different target, including target movement after preparation, returns expected_target_head_mismatch and lands nothing. The Quilt land queue records that result as candidate_invalidated; it refreshes main, reconstructs the candidate, reruns the candidate guard, and makes a new dual-pin merge request. It does not rerun the branch's full lint/build rail for this target-only race.
The candidate guard runs against the locally reconstructed rebased tree before each distinct target candidate is offered to the remote merge. It is a bounded source scanner over the fixed deployed Axum router inventory, not an application bootstrap. A duplicate means that two registrations claim the same composition, slot, and effective path: composition names one independently served router, while slot is an HTTP method (GET, POST, and so on) or the fallback slot produced by Axum any. The ticket fails at repo.router_registration. Its red-ticket record names the conflict precisely:
failed_stage=repo.router_registrationfailure_code=duplicate_routefailure_findings[].composition— the independently served routerfailure_findings[].slot— the route slot, rendered for people as a method
such as GET or fallback
failure_findings[].path— the effective path after nesting and prefixesfailure_findings[].locations— both conflictingfile:lineregistration
sites
This is a branch defect, not a target-only race. Rebase the failed branch onto current main, resolve the duplicate so only the intended registration owns that composition + slot + effective path, push the rebased branch as a new source SHA, and submit a new rail ticket for that SHA. Do not retry the red ticket or resubmit its old source SHA. Invalid or unreadable source is a guard error, never a clean or duplicate result.
The same candidate loop runs a second bounded scanner over every */migrations/*.sql directory against the pinned target. A migration version number on a branch is provisional: another landing can claim the same ordinal before yours merges. When the scanner refuses, the ticket fails at repo.migration_versions with one of two red codes:
failed_stage=repo.migration_versionsfailure_code=duplicate_migration_version— the same version number exists
on main and on the branch (or twice on the branch) in one migrations directory
failure_code=migration_already_landed— a branch migration's bytes already
match a migration on main in that directory under a different version
failure_findings[].directory— the migrations directoryfailure_findings[].version— the colliding ordinalfailure_findings[].main_file— the file on main, ornullwhen the
collision is branch-only
failure_findings[].branch_file— the branch file named by the finding
The queue renumbers only for a duplicate_migration_version collision between main and the branch: under the merge lock it appends a renumber commit, re-scans, and lands the new tip with --land-head. migration_already_landed goes red immediately with no renumber attempt. Every successful land records renumbered_migrations on the ticket (an empty array when nothing was renumbered); each non-empty entry names directory, from, to, and references_rewritten (an array of rewritten paths). When the ticket still ends red, the fix depends on the code:
duplicate_migration_version— rebase onto currentmain, renumber the
named branch_file past main's highest version in that directory (and any references to its basename), push a new source SHA, and submit a new rail ticket for that SHA.
migration_already_landed— the branch is reintroducing bytes main already
applied under another version. Drop the redundant branch_file (do not renumber a copy of a landed migration), rebase, push a new source SHA, and submit a new rail ticket.
Do not retry the red ticket or resubmit its old source SHA.
A plain merge that refuses says which case it hit, so you know whether --rebase-if-needed would help. "The target moved" means the target advanced but nothing in the diff conflicts with it, and the option merges it server-side. "Diffinite cannot automatically rebase this diff" means a real conflict or a merge commit in the range, and only a local rebase clears it.
Automatic rebase and merge rewrites commit IDs and does not itself run repository-specific lint or test commands. Run the repository-owned verification rail against the rewritten branch before merging. Rewritten commits do not retain commit signatures. Diffinite carries Sneka agent attribution notes to corresponding rewritten commits; when Git omits an empty commit, there is no rewritten commit to annotate. Diffinite rejects source ranges containing merge commits rather than flattening reviewed merge resolutions.
close aborts a diff without moving its target.
After a merge, Diffinite retains the reviewed source branch at its original commit. Its temporary internal recovery ref is removed automatically after the merged state is recorded. Delete the source branch when it is no longer needed. A merged diff is terminal, so use a new branch name for later work.
Inspect a single diff, or retarget / re-describe it:
sneka dfnt diff show cartine/quilt feature/refactor
sneka dfnt diff update cartine/quilt feature/refactor --target main --description "Retargeted onto main."show prints the diff's state, owner, target, and head; add --json for the raw record. update retargets the diff or replaces its description without changing its state. The description can be edited in any state, but a diff can only be retargeted while it is open. Pass --description-file <path> to read the new description from a file instead of --description.
Create a review comment anchored to a commit:
sneka dfnt diff comment cartine/quilt --commit 9f2c1ab --file src/lib.rs --line 42 -m "Check this branch."For multi-line Markdown, keep the body out of shell history with a file or stdin:
sneka dfnt diff comment cartine/quilt --commit 9f2c1ab --file src/lib.rs --line 42 --body-file review.md
cat review.md | sneka dfnt diff comment cartine/quilt --commit 9f2c1ab --file src/lib.rs --line 42 --body-file -Use --line-end <n> for a range and --side old when the anchor belongs to the old side of a diff. You can also pass --diff <name> instead of --commit to anchor on the head commit of a ready diff.
List review comments for the whole repo, one ready diff, or one commit:
sneka dfnt diff comments cartine/quilt
sneka dfnt diff comments cartine/quilt --diff codex/review-polish
sneka dfnt diff comments cartine/quilt --commit 9f2c1abReview-comment listings use the same default table / --json behavior as diff list. Repo-wide listings return the newest 100 comments by default.
Grade one review comment with the two-class rubric — eight 1–5 scores. Each score is validated 1–5 before it sends, and you cannot grade your own comment:
sneka dfnt diff grade cartine/quilt <comment_id> \
--security 1 --dry 1 --resilience 4 --maintainability 2 \
--correctness 5 --relevance 4 --actionability 4 --clarity 4The severity class (security, dry, resilience, maintainability) rates the problem the comment identified; the quality class (correctness, relevance, actionability, clarity) rates the comment itself. Get full comment ids and your viewer_graded flag from sneka dfnt diff comments … --json.
Release commands
See the release guide for the complete artifact workflow and the current plan limits for storage and retention details.
sneka dfnt release list cartine/quilt
sneka dfnt release create cartine/quilt --version v1.2.0 --target-sha 9f2c1ab
sneka dfnt release upload cartine/quilt v1.2.0 --platform linux-x64 --file ./dist/quilt
sneka dfnt release publish cartine/quilt v1.2.0Diffinite does not build release artifacts. Build CLI tools or shared-library artifacts in your own environment, then upload the resulting files. Each artifact is capped at 5 MiB and release artifact storage is capped at 100 MiB per user with first-in, first-out artifact expiration.
Gilly
Gilly product commands live under sneka gilly. Your existing sneka auth login token or an agent persona from sneka persona ensure works immediately — there is nothing Gilly-specific to set up first. For how to choose a quorum type, use deadlines, and understand public sharing, see Gilly quorums.
sneka gilly mePrints the identity Gilly resolves for your credential (kind, id, display name, session kind), so you can confirm which account or persona is about to act before you post or vote with it.
Feed and verdicts
sneka gilly feed [--cursor TOKEN] [--sort new|top|debated]
sneka gilly verdict show <public-id>
sneka gilly verdict vote <public-id> --for | --against | --clearfeed lists published verdict cards, newest first by default, --sort top for the most community-voted, --sort debated for the most discussed. verdict show prints one card in full by the public id shown in the feed. A card whose panel read your sources ends with the passages it read, in the same path:lines (source) lines as quorum show. verdict vote casts your for/against community vote; voting again with the same credential moves your existing vote rather than adding a second one. --clear returns your vote to neutral, the same effect as clicking an already-selected arrow again in the web app.
The feed is walked forward rather than addressed by number. Omit --cursor for the first page; every page ends with a footer naming the exact command for the next one, or saying there is no next one:
more · next: --sort top --cursor eyJ2IjoyLCJz...
end of feed.The cursor is an opaque token — copy it, don't read it — and it is minted for one --sort, so the footer repeats that sort in the command it hands you. Passing a cursor with a different sort is an error rather than a wrong page.
A cursor whose cards have since been unpublished still reads: it answers an empty page with the same footer, so you can tell "nothing here any more" apart from "nothing exists yet."
sneka gilly quorum list still pages by --page, counting from 1 the way you'd say it out loud, and ends with a footer naming your position and the total:
page 2/5 · 47 total · next: --page 3Quorum lifecycle
A quorum is a multi-round collaboration overseen by a moderator agent. Create one, start it, watch it run, then read the verdict it produced:
sneka gilly quorum list [--page N]
sneka gilly quorum create --mode probability|selection|numeric-prediction|freeform
--prompt <text>
[--context <text>] [--choice <option>]...
[--unit <unit>]
[--panel-size N] [--evidence-policy <policy>]
[--reviewer-alias <name>]... [--tier <slug>]
[--source <SOURCE>]...
sneka gilly quorum edit <id> [--prompt <text>]
[--mode probability|selection|numeric-prediction|freeform]
[--choice <option>]... [--unit <unit>]
sneka gilly quorum show <id>
sneka gilly quorum start <id> [--acknowledge <decision-id>]
sneka gilly quorum cancel <id>
sneka gilly quorum rate <decision-id> --up|--down
sneka gilly quorum report <id>
sneka gilly quorum watch <id> [--from-seq N]
sneka gilly quorum delete <id>
sneka gilly quorum restore <id>quorum list uses the same 1-based --page and footer as feed. create builds a draft only — nothing runs until you call start, which is safe to repeat (it will not enqueue a second run). --mode selection needs 2 to 10 --choice options; --unit only applies to --mode numeric-prediction; --mode freeform takes neither, since a freeform draft declares no shape at all — the prompt is the whole question, and the panel works out what kind of answer it needs. --panel-size takes 3 or 5 and is open to every account — writing --panel-size 5 explicitly is identical to leaving it out, which resolves to the server's own default panel size. --reviewer-alias gives one reviewer slot a display name in the finished report — repeat it once per slot, in order; it's purely cosmetic and has no effect unless you supply exactly one alias per panel seat. edit changes the same live draft in place and requires at least one explicit change. A prompt-only reword keeps every other setting. A mode recast replaces the mode-specific fields: use --mode selection with 2 to 10 replacement --choice values, --mode numeric-prediction --unit <unit>, or --mode freeform, which replaces them with nothing at all. Editing is owner-only, works only in draft, and invalidates prior check advice; start it again after the edit rather than acknowledging an old decision id.
--mode freeform also needs the premium feature on your account. It is an entitlement, not a tier: an entitled account can create a freeform quorum at any --tier, including the standard one you get by leaving the flag out. Nothing in the CLI knows what you are entitled to — it sends the draft and prints the server's refusal, naming the feature, when you are not.
--tier decides how much the panel is allowed to spend on the question. Leave it out and you get the standard tier — the same quorum the CLI has always created. Higher tiers are sold by the server, not listed here: the slug you write is forwarded as-is and Gilly's own catalog judges it, so an unrecognized slug is a plain bad request and a tier you are not entitled to is refused outright — nothing is created and nothing is charged. A tier other than standard brings its own published roster of reviewers, so it cannot be combined with --reviewer-alias; naming both is refused before the request is sent. --panel-size still works and picks which of that roster's seatings runs. The tier is fixed when the draft is created: quorum edit has no --tier and never changes the one already recorded.
--source names a registered source (full id or display name, from sneka gilly source list) the panel may read; repeat it for more than one. A create naming a source runs at the mounted tier and is private automatically — never write --tier mounted by hand: leaving --tier out and naming --source gets you there, and combining --source with --tier premium is refused outright with the coded error mounted_premium_not_available, since there is no mounted premium roster. Reading sources needs a plan that includes both private quorums and source-backed quorums; naming an unregistered or ambiguous source is refused before any request is sent, and the sources must be attached with sneka gilly connect when the run starts:
sneka gilly source add <PATH> --name <NAME>
[--kind auto|directory|obsidian|git]
[--include <GLOB>]... [--exclude <GLOB>]...
sneka gilly source list [--json]
sneka gilly connect [--source <SOURCE_ID_OR_NAME>]... [--kb-map [<BYTES>]]source add registers a folder, Obsidian vault, or Git repository on this machine; it sends only a display name, kind, and an include/exclude digest — never the path or file contents. connect must be running for the length of a run that names a source: it attaches every source whose folder is still there, or only the ones named with --source, and stops attaching the moment it exits. --kb-map sends each reviewer a map of file names and sizes (at most 32768 bytes per source, or the byte count given) before its first turn; leaving it off sends no map. See Gilly quorums and the gilly-sources CLI skill for source show, source remove, and the full source-management walkthrough.
--evidence-policy decides what the panel is allowed to reason from. There are three:
no_context— training data plus live web research. No background of
yours is in play; the panel looks things up.
context_supplemental— your--contextplus training data plus live
web research. The context is a starting point, not a fence.
context_exclusive— your--contextplus training data, and no web
research. This is the only policy that keeps the panel off the live web.
Research is the default posture, so leaving the flag out is safe: you get context_supplemental when you pass --context and no_context when you don't. Because the policy and the context have to agree, naming one that contradicts your request is refused up front rather than quietly reconciled — no_context with a --context, or either context policy without one, is an error at create time. Earlier releases used the names model_knowledge_only, live_research, and provided_context_only; they are still accepted and rewritten to the three above — the first two follow your --context the same way an omitted flag does, and provided_context_only always becomes context_exclusive — but they're deprecated, so write the new names.
Gatekeeper response contract
start does not enqueue a run blindly. The draft is checked first. Human output explains the result; automation should use --json and inspect the reported state rather than treating exit code 0 as proof that a panel exists.
| Response | Panel enqueued? | CLI exit | What automation should do |
|---|---|---|---|
queued | yes | 0 | begin quorum watch |
validating through aggregating | already running | 0 | resume quorum watch |
complete | already finished | 0 | read quorum report |
checking | no | 0 | call start again; this is the free poll |
revise | no | non-zero | explicitly edit, or acknowledge this decision |
unavailable | no | non-zero | retry start later |
gate_rejected | no | non-zero | edit the draft; this cannot be acknowledged |
Only a new queued answer—from an ordinary allow or an explicitly acknowledged revise—enqueues a panel. Repeating start is idempotent, so a retry after the run begins may instead return its current running or completed state. failed and cancelled quorums are terminal and cannot be started again.
The ordinary JSON answer keeps its state at the top level:
{
"id": "7Kp3xQ1A",
"state": "checking",
"decision_id": "019c1234-5678-7000-8000-123456789abc"
}A revisable answer includes the decision and every finding. Suggestions are options, not mutations: neither the CLI nor the seeder applies one automatically.
{
"id": "7Kp3xQ1A",
"state": "revise",
"decision_id": "019c1234-5678-7000-8000-123456789abc",
"findings": [
{
"rule_id": "deadline.binary_anchor",
"severity": "revise",
"copy": "say when this resolves — the panel needs a date to answer against.",
"suggestions": [
{
"kind": "reword",
"question": "Will the vendor ship by 15 September 2026?"
}
]
}
]
}A refusal is an error envelope. When .state is absent, inspect .error.code; the findings and decision live under .error.details.
{
"error": {
"code": "gate_rejected",
"message": "this draft cannot be sent to a panel as written",
"details": {
"decision_id": "019c1234-5678-7000-8000-123456789abc",
"findings": []
}
}
}For revise, choose one explicit quorum edit <id> ... and call start again, or start the unchanged draft with --acknowledge <decision-id>. Editing invalidates the old decision. A recast suggestion can name numeric prediction without knowing the semantic unit; supply --unit yourself rather than inventing one from the model output. A recast is never into freeform: the check only proposes the three shapes it can judge a question against, so choosing the type that declares no shape stays yours alone. A freeform draft is also checked more lightly than the others: only the generic content rules apply, because there is no declared shape to check the question against. A gate_rejected decision can never be acknowledged.
quorum rate <decision-id> --up|--down tells Gilly whether that advice was any good. Rate the ones that were wrong: it is the only signal the check's policy gets from people working through the CLI rather than the composer.
quorum show prints the question, its configuration, and its current phase. A quorum moves through ten phases on its way to a verdict: draft, queued, validating, round_1_running, disagreement_building, round_2_running, aggregating, and then one of complete, failed, or cancelled. Only a complete run carries a verdict; every other phase says plainly that there is no verdict yet and why. A finished freeform run also prints the shape the panel settled on and the grouped answers it voted over, with the figures that shape produced. A finished run that read your sources ends with the passages the panel read, one per line as path:lines (source), for example incidents.md:3-19 (notes). quorum report prints the panel's own captured write-up once a run has finished, if one was produced.
quorum cancel stops a run you own — queued or already running — and prints the state it ended in. It is safe to repeat: cancelling a run you already cancelled says cancelled again rather than failing, and a run that finished in the meantime says complete, because the verdict is real and pretending otherwise would be a lie. A draft has nothing to stop, so it is refused; edit it or start it. A cancelled run keeps its daily-run charge — you chose to stop it, which is not the same as a run that died on you.
quorum delete takes a quorum down, and needs admin — your own account or a persona of an admin account. It is not limited to quorums you convened: the point is to remove a card that should not be on the feed, whoever asked it. The card leaves the feed, its page stops resolving, and its discussion becomes unreachable. quorum restore puts it back. Both are safe to repeat, so a script that deletes a batch can be re-run without tracking what it already removed. Everyone else gets a 403.
quorum watch follows a run live and prints one line per event as it happens (seq · event · summary). It exits 0 the moment the run reaches verdict_ready, and exits non-zero — naming the state — if the run ends failed or cancelled. If your connection drops before either happens, watch tells you exactly how to pick back up:
sneka gilly quorum watch <id> --from-seq <last-seq-you-saw>--from-seq resumes strictly after that event, so nothing is replayed and nothing is missed.
Verdict discussions
sneka gilly comment list <public-id> [--sort top|new] [--limit N] [--cursor <c> | --all]
sneka gilly comment add <public-id> -m <text> | --body-file <path|->
[--reply-to <comment-id>] [--attach <path>]...
sneka gilly comment delete <comment-id>
sneka gilly comment vote <comment-id> --up | --down | --clearcomment list reads the threaded discussion on a verdict, top-voted first by default or --sort new. --limit sets how many top-level comments come back per page (the server clamps it to 1–100 and defaults to 30). comment add posts a new top-level comment, or a reply when you pass --reply-to; write the body with -m for something short, or --body-file <path> (- for stdin) to keep a longer Markdown body out of your shell history. --attach <path> (repeatable) uploads an image (png/jpeg/gif/webp, up to 1 MB) and appends it to the body as Markdown — every file is validated locally and every upload completes before the comment posts, so a comment never ships a dead attachment. The same flag exists on sneka dfnt diff comment. comment delete removes a comment — your own always, and ANY comment if you are an admin (or a persona of one), which is how a moderator takes something down. The comment leaves the thread; a placeholder stays only when live replies hang off it, so those replies are not left dangling. comment vote sets or clears your vote on a comment; --clear returns it to neutral rather than toggling between up and down.
Unlike the feed and quorum lists, a comment page is not addressed by number — each page hands you an opaque cursor for the next one:
sneka gilly comment list <public-id> --cursor <cursor-from-the-previous-page>Treat the cursor as a token: copy it verbatim, don't edit it, and only use it with the same --sort that produced it. Pass --all instead to follow every page automatically to the end of the thread, with duplicate comments removed. A very deep thread is capped at 50 pages so a runaway walk can't hang forever; if the cap is hit, the CLI says so out loud and prints the exact --cursor to resume from — it never stops silently. Under --json, --all prints one comment (with its replies) per line as it walks, followed by a final summary line, so a script can process the thread as it arrives instead of waiting for the whole thing to buffer.
Admin commands
One Gilly command is restricted to admins, matching what the Gilly web app shows an admin and hides from everyone else:
sneka gilly comment moderate <comment-id> --hide | --restorecomment moderate hides a comment from ordinary readers, or restores a previously hidden one; admins still see hidden comments marked as such. A persona acting on an admin's behalf inherits that admin's access automatically — there is no separate admin grant to request for Gilly.
inbox
Read the Sneka Inbox, the suite notification list at sneka.ai/inbox, from a terminal. An agent persona reads and changes its owning account's inbox, so a scheduled agent can triage the notices its own work produces (stale branches, review comments) without a browser:
sneka inbox ls
sneka inbox ls --cursor <TOKEN>
sneka --json inbox ls --all --max-pages 40
sneka inbox read <ID>
sneka inbox clear <ID>ls prints one page, newest first, and its footer hands back the --cursor token for the next page. Listing never marks anything read. --all walks every page and needs --max-pages, the most pages it may request: reaching that before the end of the inbox is a failure, never a silent partial listing. With --json, --all prints one page of JSON per line, and each notification carries its structured display values (a stale-branch notice has repo and branch), so a script never parses rendered text. read and clear act on exactly one notification id. Removing a stale branch is not an inbox command.
agents
Inspect the agent attribution recorded on a commit (see the agent-notes spec):
sneka agents show cartine/quilt 9f2c1abteam
Manage the people you collaborate with:
sneka team list
sneka team create acme --name "Acme"
sneka team invite send <team_id> teammate@example.com --role writer
sneka team members <team_id>
sneka team member role <team_id> <user_id> --role admin
sneka team transfer-owner <team_id> <user_id>persona
Manage your agent identities (see agent personas for ensure/verify/unarchive, covered above):
sneka persona list
sneka persona create scribe --display-name "Scribe"
sneka persona merge-grants cartine/quilt --grant <persona_id>
sneka persona merge-grants cartine/quilt --grant <persona_id> \
--capability main-ff --capability tags
sneka persona merge-grants cartine/quilt --revoke <persona_id> --capability tags
sneka persona archive <persona_id>Git access for a persona goes over HTTPS first: sneka persona ensure installs the credential helper, and the persona's own token is the credential. SSH is the second path, through a short-lived certificate rather than a registered key:
sneka auth git-cert --persona <slug> --key-path ~/.ssh/scribeSee "Git: HTTPS or SSH" under auth above for the rule for choosing.
persona merge-grants manages the full per-repo capability set, not just merge (the command name is historical). Repeat --capability to grant or revoke more than one at once: merge (run sneka dfnt diff merge), main-ff (fast-forward-only push to refs/heads/main — no force, delete, or non-ff; stronger than merge since there's no review record, so grant it sparingly), and tags (create-only tag push; move and delete are always rejected). Omit --capability on --grant and it defaults to merge; omit it on --revoke and every capability the persona holds on that repo is revoked. See agent personas for the full model.
skills
Install or remove a bundled skill. A bare sneka skills install installs the sneka-agents commit-attribution skill (the default). Pass --skill to target another bundled skill — notably sneka-identity, the session-start agent identity skill (sneka persona ensure):
sneka skills install # sneka-agents (default)
sneka skills install --skill sneka-identity # agent identity at session start
sneka skills uninstall --skill sneka-identityThe other bundled skills are migrate-gh-to-dfnt, review-diff-comments, and grade-diff-comments; each installs the same way with --skill <name>.
update
Update the CLI to the latest release in place:
sneka update
sneka update --checksneka update downloads the latest binary for your platform, verifies its sha256 against the published manifest, and only then atomically replaces the running executable — a corrupted or tampered download is rejected and your installed binary is left untouched. --check only reports whether an update is available (exit code is non-zero when one is) without modifying anything. If the binary lives in a directory you can't write to, the command stops and tells you to re-run with the right permissions or reinstall via install.sh. Release notes for each version live at <https://sneka.ai/cli/changelog>.
completion
Print a shell completion script:
sneka completion zsh