pl8-cli
Agent-friendly command line for PL8,
a lightweight issue tracker backed by DynamoDB. It invokes the
pl8-interface Lambda from
pl8-services through the
Lambda Invoke API, authenticated with your AWS credentials. PL8 has no UI;
this is how people and agents use it.
Getting started
Run it without installing, with uv:
uvx pl8-cli --env dev space list
or install the shorter pl8 command:
uv tool install pl8-cli
pl8 --env dev space list
To run an unreleased revision, point uvx at the repo:
uvx --from git+https://github.com/dchenstealth/pl8-cli pl8 ....
Configuration
| Setting | Flag | Environment variable |
|---|---|---|
| Function to invoke | --env ENV (invokes <ENV>-pl8-interface) or --function-name NAME |
PL8_ENV or PL8_FUNCTION_NAME |
| Default space for bare issue ids | --space SPACE |
PL8_SPACE |
| AWS credentials and region | --profile, --region |
the standard AWS chain (AWS_PROFILE, ~/.aws/config, ...) |
Flags beat environment variables, and a function name beats an env. Connection flags may go before or after the command.
Your credentials need lambda:InvokeFunction on the function. pl8-services
tags it Type=PL8Interface for granting that; the grant itself is managed
outside these repos. aws login sessions work.
Output contract
Every command prints exactly one JSON document on stdout, pl8-interface's response envelope:
{"ok": true, "data": {"issue_id": "abc123", "status": "TODO", ...}}
{"ok": false, "error": {"type": "DDBStillBlockedError", "message": "..."}}
Failures on the CLI side use the same shape. --pretty indents it. The exit
status says what kind of failure it was:
| Exit | Meaning | error.type |
|---|---|---|
| 0 | Success | |
| 1 | PL8 rejected the request. Fix it; retrying unchanged won't help. | other DDB* errors, InvalidParams, UnknownOperation, InvalidRequest |
| 2 | The command line was wrong, or --profile names no profile; nothing was sent. |
UsageError |
| 3 | No trustworthy answer: transport failure or server fault. A write may or may not have been applied, so re-read before retrying it. | InvokeError, FunctionError, DDBInternalError, DDBCorruptedError |
| 4 | Transient contention, already retried by PL8. Nothing was applied; retry the same request. | DDBTransactionConflictError, DDBIdCollisionError |
Writes are never retried automatically, since a write that timed out may already have been applied. Reads are retried on transient errors.
See pl8-interface's
contract
and pl8-base's
errors
for what each DDB* error means.
Commands
Every command has --help, which spells out the rules it enforces.
pl8 space create SPACE_ID --name NAME --description TEXT
pl8 space get SPACE_ID
pl8 space list
pl8 space update SPACE_ID --name NAME --description TEXT [--if-version N]
pl8 space delete SPACE_ID
pl8 issue create --title TITLE --description TEXT [--status STATUS]
pl8 issue get ISSUE
pl8 issue list --status STATUS
pl8 issue update ISSUE --title TITLE --description TEXT [--if-version N]
pl8 issue transition ISSUE --status STATUS [--if-version N]
pl8 issue delete ISSUE
pl8 blocker add --blocking ISSUE --blocked ISSUE
pl8 blocker remove --blocking ISSUE --blocked ISSUE
pl8 blocker list (--blocked ISSUE | --blocking ISSUE)
pl8 invoke OPERATION [--params JSON | --params-file PATH]
- Spaces must exist before you create Issues in them, and must have no Issues left before you delete them.
- Issues are named
SPACE/ISSUE_ID, e.g.ENG/abc123. A bareISSUE_IDtakes its space from--spaceorPL8_SPACE; a space in the reference always wins, so cross-space blockers need nothing extra. - Descriptions can come from a file with
--description-file PATH, or from stdin with--description-file -, which avoids shell quoting. - Lists return
{"items": [...], "cursor": ...}. Pass--cursorback for the next page, set the page size with--limit(1-100), or use--allto fetch every page at once. - Updates replace both fields.
--if-version Nmakes the write fail withDDBVersionConflictErrorif the item changed since you read versionN. invokesends any operation and params unchanged, for operations that don't have a subcommand yet.
Example
export PL8_ENV=dev PL8_SPACE=ENG
id=$(pl8 issue create --title "Fix login" --description-file notes.md | jq -r .data.issue_id)
pl8 blocker add --blocking OPS/k8s123 --blocked "$id"
pl8 issue list --status BLOCKED --all
An Issue that gains a blocker moves to BLOCKED. It returns to TODO in the background once every Issue blocking it is DONE or deleted, or its blockers are removed.
Development
uv sync
uv run ruff check .
uv run pytest
tests/test_contract.py checks every subcommand against a pinned copy of
pl8-interface's operations.yaml; see
tests/contract/README.md to refresh it.
Releases publish to PyPI when a vX.Y.Z tag matching pyproject.toml's
version is pushed.
Release files for pl8-cli 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pl8_cli-0.0.2.tar.gz | 10.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pl8_cli-0.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 23.1 kB
Release files / pl8_cli-0.0.2.tar.gz
| Download URL | pl8_cli-0.0.2.tar.gz |
|---|---|
| Size | 10.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
54aa6251f8125ab12aff2d64a17cb99138a1068d2a298b60b23f8242fc8eefb5
|
|
BLAKE2b-256 checksum How to use checksums |
5d51913c8239db7b501864207cf3c8e63ab2ae011ae1b04a5b4711b4a9aa29f0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.
Transparency logRelease files / pl8_cli-0.0.2-py3-none-any.whl
| Download URL | pl8_cli-0.0.2-py3-none-any.whl |
|---|---|
| Size | 12.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2b7bc68401a69804e63cbf90b880004572f70ae7e81b5558ae78ac999a0df483
|
|
BLAKE2b-256 checksum How to use checksums |
a911b272a125f7dc28f736358ae7508fdc2f4a08712af757e71e1615766ac44d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.
Transparency log