Automation and Agent Usage
The CLI help and stream contracts are designed to let an agent discover capabilities without reading repository source.
Discovery protocol
Use this sequence before constructing a command:
pina --version
pina --help
pina <command> --help
For client generation, inspect the command and its ecosystems:
pina generate --help
For deployment verification, inspect the group and selected leaf:
pina verify --help
pina verify check --help
pina verify record --help
For project testing and a persistent local network:
pina lint --help
pina test --help
pina dev --help
pina rehearse --help
For project diagnostics, failed transactions, and identity:
pina doctor --help
pina explain --help
pina keys --help
For write-lock contention between instructions, and its interactive map:
pina locks --help
pina map --help
For framework and extractor constraints:
pina docs
pina docs pina-overview
pina docs pina-idl
Do not infer flags from examples alone. The command-specific --help output is the authoritative interface and includes defaults, output routing, requirements, and examples.
Machine-readable workflows
Generate and validate an IDL through stdout:
pina idl --path ./programs/counter_program --compact > /tmp/counter.json
jq -e . /tmp/counter.json
Write directly to a known artifact path:
mkdir -p ./artifacts
pina idl \
--path ./programs/counter_program \
--output ./artifacts/counter.json
Profile a binary as JSON:
pina profile ./target/deploy/counter_program.so --json > /tmp/profile.json
jq -e '.functions | type == "array"' /tmp/profile.json
Trace executed compute units from the project’s Mollusk tests as JSON. Build and test output goes to stderr, so stdout stays a single JSON document; a failing test run exits with the test runner’s status:
pina profile trace --json > /tmp/trace.json
jq -e '.schemaVersion == 1 and (.instructions | length > 0)' /tmp/trace.json
jq '.instructions[] | {name, executedInstructions, syscalls}' /tmp/trace.json
Diff the current build against a saved baseline; the exit status is 2 when a total CU regression reaches both --fail-cu and --fail-percent:
pina profile compare /tmp/profile.json --json > /tmp/comparison.json
jq -e '.status == "unchanged" or .status == "improved"' /tmp/comparison.json
Rehearse an upgrade against recent traffic. Gate on the exit status first: it is 0 only when at least one transaction was compared and none changed its outcome or written account state, 2 when behaviour changed, 3 when nothing could be compared, and 1 for an operational failure with empty stdout. A JSON check must require the same three conditions, so a report whose only changes are account state, or that compared nothing, never passes:
pina rehearse --network devnet --json > /tmp/rehearsal.json
jq -e '.schemaVersion == 1 and .summary.total > .summary.skipped and .summary.stateChanged == 0 and .summary.outcomeChanged == 0' /tmp/rehearsal.json
Report write-lock contention through the versioned JSON contract, and gate CI on hotspots that [locks] allow does not accept:
pina locks --json > /tmp/pina-locks.json
jq -e '.hotspots | map(select(.allowed | not)) | length == 0' /tmp/pina-locks.json
pina locks --deny-hotspots
Read the program map’s data, the locks document plus per-instruction and per-account-type detail, without writing its HTML page:
pina map --json > /tmp/pina-map.json
jq -e '.instructionDetails | length > 0' /tmp/pina-map.json
Diagnose project readiness through the versioned JSON contract:
pina doctor --json > /tmp/pina-doctor.json
Read-only identity inspection is also JSON-safe:
pina keys show --json > /tmp/pina-keys.json
Explain a failed transaction through the versioned JSON contract. A saved getTransaction result works offline:
pina explain --transaction-file ./failed.json --json > /tmp/pina-explain.json
jq -e '.status == "succeeded" or (.candidates | type == "array")' /tmp/pina-explain.json
These commands write no progress or ANSI styling to stdout. Doctor check IDs and statuses are stable agent inputs; do not parse the human report when JSON is available.
Inspect a deployment without executing a child process:
pina deploy \
--project ./programs/counter_program \
--cluster devnet \
--upgrade-authority ./keys/devnet-authority.json \
--payer ./keys/devnet-payer.json \
--dry-run --json > /tmp/deploy-plan.json
jq -e '.program_id and .commands' /tmp/deploy-plan.json
Rehearse the planned upgrade in the same step. --rehearse replays the target cluster’s recent traffic against the exact artifact the plan pins and adds the pina rehearse --json report under a rehearsal key; the plan’s own keys are unchanged. Gate on the exit status first: 0 when the rehearsal compared at least one transaction and none changed, 2 when behaviour changed, 3 when nothing could be compared or the program is not deployed yet (no document is printed for a first deployment), and 1 for an operational failure:
pina deploy \
--project ./programs/counter_program \
--cluster devnet \
--upgrade-authority ./keys/devnet-authority.json \
--payer ./keys/devnet-payer.json \
--rehearse --dry-run --json > /tmp/deploy-plan.json
jq -e '.rehearsal.schemaVersion == 1 and .rehearsal.summary.total > .rehearsal.summary.skipped and .rehearsal.summary.stateChanged == 0 and .rehearsal.summary.outcomeChanged == 0' /tmp/deploy-plan.json
Automation rules
- Check the exit status before consuming output.
- Run
pina lintbefore review; use--fixonly when working-tree edits are authorized and always inspect the diff. - Treat stderr as diagnostics and progress, not as part of IDL JSON.
- Use explicit paths; relative paths depend on the process working directory.
- Create the parent of an
idl --outputfile before invoking the command. - Treat Codama output roots as replaceable generated directories.
- Use repeated
--exampleflags instead of assuming comma-separated parsing. - Inspect
pina docsbefore requesting a topic. - Never use the input
.sopath as the profile output path. - Treat exit code
2fromverify checkorverify recordas a verified hash mismatch, not an operational failure. - Treat exit code
2fromrehearseas a completed rehearsal that found behaviour changes; readtransactions[].statusfrom the JSON report. Exit code3means no transaction could be compared, so nothing was verified; it is never a pass. Exit code1is an operational failure with empty stdout. Never pass--allow-changesuntil everystate_changedandoutcome_changedtransaction has been reviewed. pina rehearsesends each RPC request once and stops on the first failure. Do not loop it against a rate-limited public endpoint; use a dedicated endpoint or a smaller--limit.- Run
pina build --verifyfirst and pass its printed content-addressed JSON path topina verify record --build-record. - Never infer a repository, revision, cluster, authority, or uploader. The build record binds source provenance; every network target and signing identity remains explicit.
- Use
--yesonly for a reviewed record plan. Mainnet submissions additionally require--acknowledge-mainnet; transaction export requires neither flag. - Never put secrets in a custom RPC URL. Pina passes the RPC origin to
solana-verifyas argv. - Treat
pina explaincandidates by theirconfidence: onlyconfirmedis proven by the transaction,checked_against_current_statereads state that may have changed after the transaction, andpossibleneeds runtime values. Exit code0means an explanation was produced, including for a transaction that succeeded. pina explainqueries localnet unless--networkor--rpc-urlnames another endpoint, and never retries a request.- Use
pina test --unitwhen only native Rust or Mollusk tests are required. - Treat a missing
tests/surfpoolpackage or built.soas a failed integration setup, not a skip. pina devis offline unless--networkor a credential-free HTTP(S)--rpc-urlis explicitly supplied. The URL is visible in Surfpool’s process arguments, so never place a secret anywhere in it.- Always inspect
deploy --dry-run --jsonbefore remote automation. - Never pass
deploy --yesuntil the exact target, program ID, authority, payer, and command plan have been reviewed. - Prefer
deploy --rehearsefor upgrades of programs with traffic. Exit codes2and3mean the deployment stopped before anything was sent. Never pass--allow-rehearsal-changesuntil everystate_changedandoutcome_changedtransaction has been reviewed, and do not treat3as a pass: deploy a first version without--rehearseinstead. - Never put a secret anywhere in a custom deploy RPC URL. Pina rejects user information, queries, and fragments, but accepted hosts and paths remain visible in plan output and process listings because Solana receives the endpoint through
--url. Prefer named clusters.
Stable verification
CLI help is snapshot-tested at every command level. IDL stdout is also regression-tested as valid JSON. The book is built by verify:docs and published by the repository’s GitHub Pages workflow, so command changes should update help tests and this reference in the same change.