Skip to content

Sinaris CLI Protocol Reference

This document is the canonical reference for Sinaris's structured CLI surface. The entire command tree is defined declaratively via [CliCommand] attributes and lives behind a framework-agnostic CommandRegistry — System.CommandLine is just an adapter. Anything documented here is machine-discoverable at runtime via sinaris meta list --format json and sinaris meta info --format json.

Self-describing surface

The protocol layer is the single source of truth:

Discovery methodOutputIntended consumer
sinaris --version<version> (built YYYY-MM-DD) — stamped at compile timehumans, release verification
sinaris meta info --format json{version, buildDate, commandCount, executableCount}health checks, CI
sinaris meta list --format json[{id, name, path, parentId, executable, description}]scripts, grep-style filters
sinaris meta listOne line per command: <id> (<full path>)humans
sinaris meta schemaFull nested schema (commands, options, args, stdin, enum allowedValues, metadata)AI / MCP tools, code generators

--version and the version / buildDate fields all come from a single CliToolInfo DI singleton — the build date is stamped into the assembly via <AssemblyMetadata Include="BuildDate" /> in Sinaris.Cli.csproj and may be overridden by CI/release pipelines (set the BuildDate MSBuild property) to guarantee deterministic timestamps across reproducible builds.

Every command described below also appears in meta schema — so when this doc drifts from reality, meta schema is correct.

Output format convention

All commands that produce structured data accept --format text|json (alias -f). text is the human default; json is the machine-stable contract and is what AI agents, hook scripts, and MCP tools should request.

bash
sinaris task list --format json
sinaris meta info -f json
sinaris meta schema           # defaults to --format json

There is exactly one way to ask for JSON output (--format json); no command exposes a legacy --json flag — the protocol enum OutputFormat ({Text, Json}) is the single source of truth across the platform.

Error envelope

Failures under --format json emit a structured envelope on stdout with a non-zero exit code, so AI agents and MCP tools always parse JSON regardless of success/failure:

json
{
  "ok": false,
  "error": {
    "code": "notFound",
    "message": "task 'missing-id' not found.",
    "details": { "id": "missing-id" }
  }
}

details is optional and omitted when empty. code is one of the protocol taxonomy values defined by CliErrorCode:

CodeMeaning
validationInput validation failed (missing/empty/malformed argument or stdin).
notFoundRequested entity does not exist.
conflictOperation rejected because of conflicting state (already claimed, wrong status, ...).
internalUnhandled exception bubbled out of the handler.
cancelledCommand was cancelled (Ctrl+C / SIGTERM).
unknownFallback when the failure mode is not classified.

The envelope is also produced by the adapter for failures that happen before a handler runs (workspace not initialized, cancellation, uncaught exceptions). The adapter recovers --format from the parsed token stream so AI agents always observe the same shape regardless of the failure origin.

Successful commands under --format json emit on stdout in two shapes:

  • Data commands (list / show / create / claim / ...): the existing JSON payload (e.g. [{...},{...}] for task list --format json) — unchanged by this contract so existing AI clients keep working. The schema is the contract.
  • State-changing commands without a data payload (task complete, task delete, agent register, ...): a success envelope {"ok":true,"message":"...","data":{"id":"..."}}data is optional.

--format text (the human default) keeps the long-standing Error: ... line on stderr for failures, and the prior plain-text message on stdout for successes. Exit codes are the same in both formats.

Cancellation

Long-running commands such as hub bind their work to a CancellationToken supplied by the adapter, so Ctrl+C / SIGTERM always exits cleanly without dropping in-flight state.

Streaming output

Long-running commands write progress through ICommandOutput (stdout/stderr). This is the same interface tests use, which means subcutaneous tests assert on the exact bytes a user would see — no Console.WriteLine shortcuts allowed.

Exit codes

Exit codeMeaning
0success
1user-facing failure (validation, not-found, conflicting state)
2internal failure (logged to .sinaris/logs/error-*.log)

Command tree

Root-leaf commands

CommandPurposeStdin?
initInitialize the workspace (assets + database + templates)
hubStart the local web hub

init

  • In an interactive shell, first-time init asks Select target platform: 1) Copilot / 2) Cursor (default).
  • Re-running init keeps the installed platform and regenerates missing or outdated managed assets.
  • --force regenerates managed hook, command, skill, and instruction assets, but only after explicit interactive confirmation.
  • If the workspace database cannot be migrated, init backs it up under the user-level Sinaris backup directory for the current workspace and rebuilds it only after explicit interactive confirmation.
  • In non-interactive environments (CI, piped stdin), first-time init and every destructive path fail loudly with validation. No platform default, asset overwrite, or database rebuild is inferred silently.

hub

  • -p / --port <int> (default 8394).
  • Prints the URL on startup; keeps running until Ctrl+C.

meta — protocol introspection

Sub-commandPurpose
meta infoVersion + command counts
meta listEnumerate every command Id
meta schemaExport the full nested schema (defaults to JSON)

All three accept --format text|json (alias -f). meta list also accepts --executable-only to filter out group nodes. meta schema additionally accepts --indented false for a compact single-line JSON payload.

task — task management (16 leaves)

Sub-commandStdin?Notes
task create-t <title> required; -p priority, --parent, --phase optional (--plan deprecated alias)
task list-s <status>, --tree
task nextpreview without claiming
task queue-statusinspect claimable count, next task, and auto-claim session state
task claim--session <id> to attribute the claim
task complete <id>--session <id> required for owned tasks; -m <message> must include Criteria: and Risk: when context snapshots are active
task fail <id>--session <id> required for owned tasks; -m <message> must include Criteria: and Risk: when context snapshots are active
task release <id>back to Todo
task retry <id>reset failed task
task decision-request <id>pause a task for a user decision; -m, optional -c, optional --session
task decision-resume <id>resume a paused task with a user decision; -d, optional -c, optional --session
task batchyesJSON array piped on stdin; --phase <phase-id> optional (--plan deprecated alias)
task show <id>
task edit <id>-t, -d, -p
task delete <id>only Todo / Failed tasks
task log <id>activity log

Every task sub-command accepts --format json (alias -f json) for machine output.

proposal — demand proposals

Sub-commandStdin?Notes
proposal templateprint proposal markdown template
proposal nextallocate canonical proposal ID/path; --slug required
proposal checkvalidate proposal markdown; --path, optional `--stage draft
proposal publishregister proposal markdown into the workspace database
proposal status--detail includes gate-derived next action and blockers
proposal prepare-design <id>verify proposal can enter phase design
proposal assign-milestone <id>--milestone <id>
proposal reject <id>optional --reason

Every proposal sub-command accepts --format json (alias -f json) for machine output.

milestone — project milestones

Sub-commandStdin?Notes
milestone nextallocate canonical milestone ID/path; --slug, optional --after
milestone saveregister authored milestone markdown; --path, optional --source-proposal
milestone list
milestone show <id>

Every milestone sub-command accepts --format json (alias -f json) for machine output.

phase — project phases

Sub-commandStdin?Notes
phase saveregister authored phase markdown; --path, --milestone, optional --proposal, --title

phase save accepts --format json (alias -f json) for machine output.

agent — agent session lifecycle

Sub-commandStdin?Notes
agent register--session, --type (copilot/cursor/windsurf/aider/...)
agent alias--session, --alias
agent auto-claim--session, --enabled; toggles automatic task claiming for a recorded session
agent register-copilot-sessionoptionalresolves the current Copilot CLI session; --cwd, --payload-file, --source
agent diagnose-hookshook health
agent heartbeat--session <id>
agent disconnect--session <id>
agent release-tasksrelease everything claimed by a session
agent list
agent resumeexecute platform-specific resume command; --latest, --dry-run

Every agent sub-command accepts --format json (alias -f json) for machine output.

guide — guided design workflow

Sub-commandStdin?Notes
guide template <kind>goal / context / design / milestones / decision / open-question
guide contextProjectContext bundle for agent consumption
guide checkcandidate ProjectContext markdown checked against confirmed goals; --candidate <file>
guide validatevalidates .sinaris/context files
guide publishregisters validated guide context into execution context
guide statussummary of context state

Every guide sub-command accepts --format json (alias -f json) for machine output.

evaluator — delivery confidence

Sub-commandStdin?Notes
evaluator listlist evaluator journeys; optional --tags
evaluator statusdelivery status summary
evaluator validate [journeyId]validate journey markdown files
evaluator enable <journeyId>enable a journey
evaluator disable <journeyId>disable a journey
evaluator mark-needs-update <journeyId>mark stale; optional --reason
evaluator mark-current <journeyId>mark current; optional --reason
evaluator retire <journeyId>retire journey; optional --reason
evaluator runrun journeys; --scope, optional --agent, --delivery-gate
evaluator runslist runs; optional --journey-id, --limit, --offset
evaluator show <runId>show run detail
evaluator explain <runId>explain run failure
evaluator submit <runId>submit result JSON from --file
evaluator cancel <runId>cancel running run; optional --reason
evaluator abandon <runId>abandon stale running run; optional --reason
evaluator retry <runId>retry run; optional --agent
evaluator confirm <runId>confirm human-review run; optional --journey, --summary
evaluator reject <runId>reject human-review run; optional --journey, --checkpoint, --summary

Every evaluator sub-command accepts --format json (alias -f json) for machine output.

hook — host hook runner

Sub-commandStdin?Notes
hook run--platform, --hook, --payload-file; no JSON output option because it streams hook decisions for the host

recovery — agent-facing workspace recovery inspection

Sub-commandNotes
recovery statuscompare active DB resources, current .sinaris/ documents, and available backups
recovery inspectinspect --backup latest or a named/path backup and emit a recovery manifest for sinaris-recovery

Every recovery sub-command accepts --format json (alias -f json) for machine output. These commands are intended for the sinaris-recovery Agent command/SKILL; init should tell users to run the Agent command, not hand-enter recovery CLI arguments.

AI / MCP integration patterns

Pattern 1: discover the surface

bash
sinaris meta list --format json | jq -r '.[] | select(.executable) | "\(.path)\t\(.description)"'

This is the recommended way for AI agents to bootstrap themselves — no hard-coded command list, no scraping --help.

Pattern 2: pipe in JSON, parse JSON out

bash
cat tasks.json | sinaris task batch --phase phase-001 --format json | jq '.[].id'

Commands accept stdin only where it is semantically meaningful: task batch and agent register-copilot-session.

Pattern 3: drive an MCP / code-gen tool from the full schema

bash
sinaris meta schema > sinaris-schema.json
# or pipe directly:
sinaris meta schema | jq '.commands[] | select(.name=="task") | .commands[].name'

meta schema is the recommended entry point for any tool that needs the complete option / argument / stdin / enum surface for every command — it is the same schema the generator emits at compile time, only serialized at runtime so external consumers stay in lock-step with the actual binary.

Commands that consume structured JSON on stdin (task batch) self-describe their payload in the schema. Example shape:

json
{
  "stdin": {
    "parameter": "stdin",
    "description": "JSON array of task descriptions",
    "required": false,
    "schema": {
      "kind": "array",
      "items": {
        "kind": "object",
        "type": "BatchTaskRequest",
        "properties": [
          { "name": "title", "type": "string", "required": true },
          { "name": "description", "type": "string", "required": false },
          { "name": "priority", "type": "int", "required": true }
        ]
      }
    }
  }
}

AI agents should treat the presence of stdin.schema as the signal that a typed JSON payload is expected and use the property list to construct the body.

Implementation contract

A new command is added by writing a single attributed class — the source generator handles everything else:

csharp
[CliCommand("Manage widgets", Category = "Widgets")]
public sealed partial class WidgetCommands
{
    private readonly WidgetCommandHandler _handler;
    public WidgetCommands(WidgetCommandHandler handler) => _handler = handler;

    [CliCommand("Create a widget")]
    public Task<CliCommandResult> CreateAsync(
        [CliOption("-n", Description = "Widget name")] string name,
        [CliOption(Description = "Output format")] OutputFormat format = OutputFormat.Text,
        CancellationToken cancellationToken = default)
        => _handler.CreateAsync(name, format, cancellationToken);

    [CliCommand("Load widgets from JSON on stdin")]
    public Task<CliCommandResult> LoadAsync(
        [CliOption("-f", Description = "Output format")] OutputFormat format = OutputFormat.Text,
        [CliStandardInput("JSON array of widget descriptions", typeof(WidgetDto[]))] string stdin = "")
        => _handler.LoadAsync(stdin, format);
}

Handlers compose results through CliResults:

csharp
return CliResults.Ok($"Widget '{id}' created.", format,
    data: new Dictionary<string, string> { ["id"] = id });

return widget is null
    ? CliResults.NotFound($"widget '{id}' not found.", format)
    : CliCommandResult.Success(OutputFormatter.FormatWidget(widget, format));

Conventions enforced by the generator (diagnostics SINARIS001SINARIS009):

  • XxxCommands (plural) → command group; one leaf per [CliCommand] method.
  • XxxCommand (singular) → root-level leaf; exactly one [CliCommand] method.
  • Method names: CreateAsynccreate, RegisterCopilotSessionAsyncregister-copilot-session.
  • Parameter types: bool / int / long / double / float / byte / char / string, any enum, nullable variants of the above. Type-injected by the runtime: ICommandOutput, CancellationToken.
  • Async return: every method must be Task<CliCommandResult>.

Everything else (option name kebab-casing, --help text, default values, enum AllowedValues, args-builder for tests) is generated automatically.

Released under the MIT License.