This file gives future agents and maintainers quick project context for the Perfectscale CLI repository.
This repo contains a Go CLI named pscli for querying Perfectscale's public API using a UI-generated service token.
It is deliberately scoped to the public API and does not currently support:
- user JWT auth
- browser SSO
- tenant-manager user APIs
- data-provider user-only APIs
The repo currently targets a compact, agent-friendly CLI with good defaults and strong help output.
Supported command groups:
authloginstatuslogout
clusterslistgetemission
namespaceslist
workloadslistsummarygroup-by namespacegroup-by typegroup-by optimization-policygroup-by risk-severitygroup-by labelshowexportriskylabelsmuted
Design goals:
- strong terminal UX
- clear short flags
- JSON and JSONL support for automation
- minimal config required for the happy path
- public-prod defaults out of the box
Global defaults are defined in internal/config/config.go:
- profile:
default - output:
table - public API URL:
https://api.app.perfectscale.io/public/v1
Runtime flag behavior is implemented in internal/cli/app.go and internal/cli/context.go:
- runtime flags are attached to top-level commands and leaf subcommands
- values are resolved through command lineage, so flags like
-o jsonwork before or after nested commands
Only service-token auth is supported now.
Auth flow:
- User runs
pscli auth login - CLI exchanges
client_idandclient_secretviaPOST /auth/public_auth - CLI stores the profile locally
- Later commands auto-refresh the access token by repeating the service-token exchange
Relevant files:
- internal/cli/auth.go
- internal/auth/service_token.go
- internal/auth/manager.go
- internal/profile/store.go
Local profile storage:
- base dir defaults to
os.UserConfigDir()/perfectscale-cli/profiles - files are written with
0600 - directories are created with
0700
The CLI is built around the public API and should stay self-contained.
Important current limitations:
- workload period is effectively
30donly - namespace, name, type, min-cost, and min-waste filters are client-side
- namespaces are derived from workloads
- there is no authoritative public nodegroup endpoint wired into the CLI
When proposing new features, prefer:
- exposing small additions to the existing public workload endpoint
- reusing data the backend already has
- keeping the CLI standalone, without importing sibling services as libraries
This repo keeps its own local copy of the public OpenAPI spec and generates its own public client from that file.
Relevant files:
Rules:
- do not hand-edit
internal/publicapi/client.gen.go - edit the local YAML spec first
- regenerate with
make openapi
The generated client is intentionally low-level. The handwritten adapter in internal/api/client.go remains responsible for:
- auth headers
- response validation
- mapping generated types into CLI types
- deriving enriched workload fields
Workload mapping lives in:
The CLI already enriches workloads with derived values such as:
- container count
- summed current request totals
- summed recommended request totals
- summed container p90/p95/p100 usage
- max indicator / risk / waste counts
That enrichment is used by:
- workload views
- summaries
- group-by commands
- CSV export
If adding new workload features, check whether they should be:
- part of the raw mapped API model
- part of the derived model
- exposed as a new
--view - exposed in CSV export
Output rendering is centralized in:
Modes:
tablejsonjsonl
Important behavior:
jsonlis supported only for list-like payloads- scalar commands fall back to regular JSON when needed
workloads list --view allimplicitly switches tojsonlunless output was explicitly set
workloads list supports:
defaultcapacityusagepolicyriskall
Implementation lives in:
When adding a field that should be visible in normal list output, consider whether it belongs in:
- one of the existing named views
- a new view
- only
view=all
Try to preserve the current flag scheme unless there is a very strong reason to change it.
Current conventions:
-pprofile-ooutput-upublic API URL-ddebug-ccluster-wperiod-nnamespace-mworkload name-tworkload type-ssort-rorder-Ttop-Bbottom-Cmin-cost-Wmin-waste-Vview-iid or client-id-kclient-secret or label key-Smin-severity-fformat-Ffile-vlabel value
Before adding a new short flag, make sure it does not overlap in a confusing way with nearby commands.
Main test areas already in the repo:
- API parsing
- service-token auth and refresh behavior
- profile storage
- CLI help and flag parsing
- workload filtering, sorting, limiting, summaries, and views
Run the full suite with:
go test ./...When making command-surface changes, update:
- command help text
- app-level description/examples
- relevant CLI tests
The workflow is in build.yml.
Current behavior:
- tests on every push and pull request
- cross-builds:
- darwin/arm64
- windows/amd64
- linux/amd64
- linux/arm64
- workflow artifacts on every run
- on pushes to the default branch:
- compute next version starting at
v1.0.0 - increment patch version only
- create or reuse a GitHub Release
- upload built binaries as release assets
- compute next version starting at
Pinned actions are required in this repo. Do not switch back to floating @vN refs.
If you change command behavior, also update:
- README.md
- internal/cli/app.go top-level description
- command-specific
Descriptionblocks in the relevant CLI file
The docs should stay aligned with the actual flags and defaults. Avoid aspirational docs.
If the product team asks what minimal API changes unlock more CLI value, the strongest current answers are:
- add
periodsupport to public workloads - add public server-side filters such as
namespace,type,name,node_group,node_type,reservation_type - expose workload nodegroup placement, ideally as
runningMinutesByNodeGroupor a similar authoritative field
Those changes would unlock a lot more CLI surface with limited backend churn.