CLI client for goodissues.dev — manage projects and track bugs and feature requests from the command line.
Built in Rust. Distributed as a single executable for Linux, macOS, and Windows (amd64 and arm64). HTTPS uses Rustls; no system OpenSSL installation is required.
macOS / Linux:
curl -fsSL https://raw.githubusercontent.com/agoodway/goodissues_cli/main/install.sh | shWindows (PowerShell):
irm https://raw.githubusercontent.com/agoodway/goodissues_cli/main/install.ps1 | iexFrom source:
cargo build --release --locked
cp target/release/goodissues /usr/local/bin/# Configure your API key
goodissues configure --url https://goodissues.dev --api-key sk_your_api_key
# List all projects
goodissues projects list
# Create a project (prefix is required by the API)
goodissues projects create --name "My App" --prefix MA
# File a bug
goodissues issues create --project <project-id> --title "Login broken on Safari" --type bug --priority high
# See all issues
goodissues issues listConfig is stored at ~/.goodissues.json (Windows: %USERPROFILE%\.goodissues.json).
# Set up your default environment
goodissues configure --url https://goodissues.dev --api-key sk_live_abc123
# Add a local dev environment
goodissues configure --env dev --url http://localhost:4000 --api-key sk_test_xyz
# Add a staging environment
goodissues configure --env staging --url https://staging.goodissues.dev --api-key sk_staging_789
# Show all configured environments (keys are masked)
goodissues configure show
# Show a specific environment
goodissues configure show --env productionThe first configured environment becomes the default. Use --env <name> on any command to switch environments.
Manage projects within your account.
# List all projects
goodissues projects list
# List projects as JSON (useful for scripting)
goodissues projects list --json
# Get a single project by ID
goodissues projects get <project-id>
# Create a new project
goodissues projects create --name "Backend API" --prefix API
goodissues projects create --name "Mobile App" --prefix MOB --description "iOS and Android client"
# Delete a project
goodissues projects delete <project-id>Track bugs, incidents, and feature requests.
# List all issues across projects
goodissues issues list
# Filter issues by project
goodissues issues list --project <project-id>
# Filter issues by status
goodissues issues list --status new
goodissues issues list --status in_progress
goodissues issues list --status archived
# Combine filters, including type
goodissues issues list --project <project-id> --status new --type bug
# Get a single issue by ID
goodissues issues get <issue-id>
# Create a bug report
goodissues issues create \
--project <project-id> \
--title "Crash on file upload" \
--type bug \
--priority critical \
--description "App crashes when uploading files larger than 10MB"
# Create a feature request
goodissues issues create \
--project <project-id> \
--title "Add dark mode support" \
--type feature_request \
--priority medium
# Report an incident
goodissues issues create \
--project <project-id> \
--title "API returning 503 errors" \
--type incident \
--priority critical
# Update an issue
goodissues issues update <issue-id> --status in_progress
# Delete an issue
goodissues issues delete <issue-id>Issue types: bug, incident, feature_request
Priorities: low, medium (default), high, critical
Statuses: new (default), in_progress, archived
List, search, report, and update error groups.
# List unresolved errors
goodissues errors list --status unresolved --muted false
# Search by stacktrace. At least one of these filters is required.
goodissues errors search --module MyApp.Worker --function do_work --file lib/my_app/worker.ex
# Get a readable report, including the first five stack frames
goodissues errors get <id>
# Report an error
goodissues errors report --body '{"project_id":"<id>","kind":"exception","reason":"NullPointerException","fingerprint":"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"}'
# Update an error group
goodissues errors update <id> --status resolved --muted trueError fingerprints must be 64 characters (typically a SHA-256 hex digest). If
provided, trace_id must be a 32-character hexadecimal trace ID.
List, report, mute, and resolve incidents. Reports require project_id, title,
fingerprint, and source; severity is info, warning, or critical.
Updates accept muted; use resolve to resolve an incident.
# List all incidents
goodissues incidents list
# Get a single incident by ID
goodissues incidents get <id>
# Report an incident
goodissues incidents report --body '{"project_id":"<id>","title":"API returning 503 errors","fingerprint":"api-503","source":"cli","severity":"critical"}'
# Mute an incident
goodissues incidents update <id> --body '{"muted":true}'
# Resolve an incident
goodissues incidents resolve <id>Set up API connection and manage environments.
# Set URL and API key in one command
goodissues configure --url https://goodissues.dev --api-key sk_live_abc123
# Update just the API key for an existing environment
goodissues configure --api-key sk_new_key_456
# Set up a named environment
goodissues configure --env production --url https://goodissues.dev --api-key sk_live_abc123
# Show current configuration
goodissues configure show
# Show a specific environment
goodissues configure show --env devAll commands support these options:
| Flag | Description |
|---|---|
--env <name> |
Use a specific configured environment |
--json |
Print the raw API response instead of a formatted view |
--help, -h |
Show help for the current command |
--version, -v |
Print version and exit |
sk_*keys are read-write (required for create and delete operations)pk_*keys are read-only (sufficient for listing and viewing)
Get your API key at goodissues.dev.
Projects, issues, and errors get print formatted tables and details by default; add --json to get the raw API response instead. Every other command always prints the raw JSON response. Useful for piping to jq or other tools:
# Get all projects as JSON
goodissues projects list --json
# Pipe to jq for filtering
goodissues issues list --json | jq '.data[] | select(.priority == "critical")'
# Get a single issue as JSON
goodissues issues get <issue-id> --jsonManage separate configurations for dev, staging, and production:
# Set up environments
goodissues configure --env dev --url http://localhost:4000 --api-key sk_test_local
goodissues configure --env staging --url https://staging.goodissues.dev --api-key sk_staging_abc
goodissues configure --env production --url https://goodissues.dev --api-key sk_live_xyz
# Use a specific environment for any command
goodissues projects list --env production
goodissues issues create --env staging --project <id> --title "Test issue" --type bug
# Check which environments are configured
goodissues configure showThese resources require --project <project-id> on every command.
goodissues checks list --project <project-id>
goodissues checks get <id> --project <project-id>
goodissues checks create --project <project-id> --body '{"name":"API","url":"https://example.com"}'
goodissues checks update <id> --project <project-id> --body '{"name":"API health"}'
goodissues checks delete <id> --project <project-id>
goodissues checks results <id> --project <project-id> --query 'page=2'
goodissues heartbeats list --project <project-id>
goodissues heartbeats get <id> --project <project-id>
goodissues heartbeats create --project <project-id> --body '<json>'
goodissues heartbeats update <id> --project <project-id> --body '<json>'
goodissues heartbeats delete <id> --project <project-id>
goodissues heartbeats pings <id> --project <project-id>
goodissues heartbeats ping <token> --project <project-id>
goodissues heartbeats start <token> --project <project-id> --body '{}'
goodissues heartbeats fail <token> --project <project-id>Signals accept an optional --body. They authenticate using the heartbeat token,
so an environment configured with only --url can send ping, start, and fail.
Managing heartbeats and reading their history still requires an API key.
Lists and results accept --query.
goodissues cloud-ip-ranges list --snapshot-id <id> --page 2 --per-page 100
goodissues cloud-ip-ranges sync-stateThe Rust CLI preserves the earlier Zig CLI's command set, typed flags, raw --body and
--query overrides, readable output, and raw JSON output. create is also
an alias for report on errors and incidents. Run goodissues help <command>
for every option, including project updates and issue pagination.
Existing .goodissues.json files work unchanged. When it is absent, the
legacy ~/.goodissues/config.yaml is imported once. Run goodissues configure
without flags for interactive setup. Unix configuration files use mode 0600.
Configuration updates replace the file atomically; invalid existing JSON is
reported without overwriting it. Trailing slashes in base URLs are ignored.
Path IDs are percent-encoded; empty IDs and dot-only path segments are rejected.
Table cells occupy one line and are truncated at 60 display columns. Use get
or --json to see complete values.
Intentional fixes: bodyless heartbeat signals and incident resolution send
POST requests successfully; the Zig 0.15.2 implementation panics on these.
Cloud range filter values are percent-encoded to preserve special characters.
Missing option values and unknown or unsupported options now fail before any
request is sent; this prevents a missing --env value from selecting the default
environment. Use --description=--literal for values beginning with --.
When an option is repeated, the last value wins. Extra positional arguments and
unknown subcommands are rejected.
Formatted reads (list and get for projects and issues, and errors get) fail
with Error: unexpected API response followed by the response body when the API
returns an unexpected shape. Creates and updates have already succeeded at that
point, so they print the raw response instead.
Interactive configure does not echo the API key when run in a terminal.
Bodyless POST requests send Content-Length: 0. Requests time out after 30 seconds.
Install the stable Rust toolchain (1.88 or newer), then:
cargo build --locked
cargo test --locked
cargo fmt --check
cargo clippy --locked --all-targets -- -D warnings
cargo build --release --locked
cargo run -- --helpTests use temporary configuration directories and local HTTP servers. No API key or live service is needed.
cli-rust/ in agoodway/goodissues is the source of the standalone
agoodway/goodissues_cli repository. Commit changes in the monorepo, then run
just sync from cli-rust/ (or bash cli-rust/scripts/sync-subtree.sh). This
uses a normal fast-forward subtree push and preserves the repository history.
If standalone main has diverged, reconcile those commits before retrying;
do not force-push. The Zig publish recipe is retired.
In a standalone clone, update the version in Cargo.toml, run cargo check
to update Cargo.lock, commit and push main, then run just publish vX.Y.Z
(or bash scripts/publish.sh vX.Y.Z). The tag must match the package version.
A pushed tag triggers .github/workflows/release.yml, which tests and builds:
| Asset | GitHub runner |
|---|---|
goodissues-linux-amd64 |
ubuntu-latest |
goodissues-linux-arm64 |
ubuntu-24.04-arm |
goodissues-darwin-arm64 |
macos-latest |
goodissues-darwin-amd64 |
macos-15-intel |
goodissues-windows-amd64.exe |
windows-latest |
goodissues-windows-arm64.exe |
windows-11-arm |
All six builds must succeed before a GitHub release is created with the
binaries and checksums.txt. Main pushes, pull requests and manual workflow
runs build the same artifacts without publishing a release. Linux builds
use the runner's glibc; they are not musl/static binaries. Installers retain
the existing release asset names and environment overrides.