Rename proboctl CLI binary to prb

Shorter CLI name for faster typing. Renames the binary, build targets,
goreleaser config, command examples, and documentation.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2026-03-16 15:10:01 +01:00
parent 1f1df63676
commit 7e86d2c89f
30 changed files with 82 additions and 82 deletions

View File

@@ -46,9 +46,9 @@ builds:
ignore:
- goos: windows
goarch: arm64
- id: proboctl
main: ./cmd/proboctl/main.go
binary: proboctl
- id: prb
main: ./cmd/prb/main.go
binary: prb
ldflags:
- -s -w
- -X 'main.version={{.Version}}'
@@ -96,9 +96,9 @@ builds:
goarch:
- amd64
- arm64
- id: proboctl-docker
main: ./cmd/proboctl/main.go
binary: proboctl
- id: prb-docker
main: ./cmd/prb/main.go
binary: prb
ldflags:
- -s -w
- -X 'main.version={{.Version}}'
@@ -116,7 +116,7 @@ archives:
- builds:
- probod
- probod-bootstrap
- proboctl
- prb
name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_

View File

@@ -46,8 +46,8 @@ PROBOD_BIN_EXTRA_DEPS=
PROBOD_BIN= bin/probod
PROBOD_SRC= cmd/probod/main.go
PROBOCTL_BIN= bin/proboctl
PROBOCTL_SRC= cmd/proboctl/main.go
PRB_BIN= bin/prb
PRB_SRC= cmd/prb/main.go
ifndef SKIP_APPS
PROBOD_BIN_EXTRA_DEPS += \
@@ -122,7 +122,7 @@ coverage-combined: coverage-report test-e2e-coverage ## Generate combined covera
$(GO) tool cover -html=coverage-combined.out -o=coverage-combined.html
.PHONY: build
build: bin/probod bin/proboctl
build: bin/probod bin/prb
.PHONY: sbom-docker
sbom-docker: docker-build
@@ -179,9 +179,9 @@ bin/probod: pkg/server/api/connect/v1/schema/schema.go \
@probo/emails
$(GO_BUILD) -o $(PROBOD_BIN) $(PROBOD_SRC)
.PHONY: bin/proboctl
bin/proboctl:
$(GO_BUILD) -o $(PROBOCTL_BIN) $(PROBOCTL_SRC)
.PHONY: bin/prb
bin/prb:
$(GO_BUILD) -o $(PRB_BIN) $(PRB_SRC)
.PHONY: @probo/emails
@probo/emails:

View File

@@ -104,7 +104,7 @@ func (c *Client) DoRaw(
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+c.token)
req.Header.Set("User-Agent", version.UserAgent("proboctl"))
req.Header.Set("User-Agent", version.UserAgent("prb"))
resp, err := c.httpClient.Do(req)
if err != nil {
@@ -120,7 +120,7 @@ func (c *Client) DoRaw(
if resp.StatusCode != http.StatusOK {
switch resp.StatusCode {
case http.StatusUnauthorized:
return nil, fmt.Errorf("authentication failed (HTTP 401): token may be invalid or expired, try 'proboctl auth login'")
return nil, fmt.Errorf("authentication failed (HTTP 401): token may be invalid or expired, try 'prb auth login'")
case http.StatusForbidden:
return nil, fmt.Errorf("access denied (HTTP 403): you do not have permission to perform this action")
default:

View File

@@ -113,7 +113,7 @@ func configDir() (string, error) {
if err != nil {
return "", fmt.Errorf("cannot determine config directory: %w", err)
}
return filepath.Join(dir, "proboctl"), nil
return filepath.Join(dir, "prb"), nil
}
func configPath() (string, error) {
@@ -188,7 +188,7 @@ func (c *Config) DefaultHost() (string, *HostConfig, error) {
if token := os.Getenv("PROBO_TOKEN"); token != "" {
if len(hosts) == 0 {
return "", nil, fmt.Errorf("PROBO_TOKEN is set but no host configured; run 'proboctl auth login' first")
return "", nil, fmt.Errorf("PROBO_TOKEN is set but no host configured; run 'prb auth login' first")
}
host := hosts[0]
@@ -215,5 +215,5 @@ func (c *Config) DefaultHost() (string, *HostConfig, error) {
return host, c.Hosts[host], nil
}
return "", nil, fmt.Errorf("not logged in; run 'proboctl auth login' first")
return "", nil, fmt.Errorf("not logged in; run 'prb auth login' first")
}

View File

@@ -1,14 +1,14 @@
# AGENTS.md — proboctl CLI
# AGENTS.md — prb CLI
## Overview
`proboctl` is the Probo CLI built with [cobra](https://github.com/spf13/cobra). Entry point: `cmd/proboctl/main.go`.
`prb` is the Probo CLI built with [cobra](https://github.com/spf13/cobra). Entry point: `cmd/prb/main.go`.
## Package layout
| Package | Purpose |
|---|---|
| `cmd/proboctl` | Binary entry point — creates `Factory`, root command, and runs it |
| `cmd/prb` | Binary entry point — creates `Factory`, root command, and runs it |
| `pkg/cmd/root` | Root command — registers all top-level subcommands |
| `pkg/cmd/<resource>` | Command group (e.g. `risk`, `framework`, `webhook`) — wires subcommands |
| `pkg/cmd/<resource>/<verb>` | Leaf command (e.g. `risk/create`, `risk/list`) — owns the `RunE` |
@@ -41,7 +41,7 @@ func NewCmd<Verb>(f *cmdutil.Factory) *cobra.Command {
Use: "<verb>",
Short: "One-line description",
Aliases: []string{"..."}, // optional, e.g. "ls" for list
Example: ` proboctl <resource> <verb> ...`,
Example: ` prb <resource> <verb> ...`,
RunE: func(cmd *cobra.Command, args []string) error {
// 1. Validate output flags (for list commands)
// 2. Load config, get host + token
@@ -64,7 +64,7 @@ func NewCmd<Verb>(f *cmdutil.Factory) *cobra.Command {
- **GraphQL queries/mutations** are `const` strings declared at package level in the leaf command file.
- **Response types** are unexported structs in the leaf command file, shaped to match the GraphQL response.
- **Organization resolution**: every command that needs an org checks `--org` flag first, then falls back to `hc.Organization` from config. If both are empty, return an error telling the user to pass `--org` or run `proboctl auth login`.
- **Organization resolution**: every command that needs an org checks `--org` flag first, then falls back to `hc.Organization` from config. If both are empty, return an error telling the user to pass `--org` or run `prb auth login`.
- **Interactive prompts** use `github.com/charmbracelet/huh`. Gate them behind `f.IOStreams.IsInteractive()`. Always support full non-interactive use via flags.
- **Output format**: list commands support `--output json|table` via `cmdutil.AddOutputFlag` / `cmdutil.ValidateOutputFlag`. Default is table.
- **Pagination**: list commands use `api.Paginate[T]` with a `--limit` / `-L` flag (default 30). Show "Showing X of Y" on stderr when results are truncated.

View File

@@ -44,17 +44,17 @@ func NewCmdAPI(f *cmdutil.Factory) *cobra.Command {
Short: "Make an authenticated GraphQL request",
Long: "Send a GraphQL query or mutation to the Probo API and print the response.",
Example: ` # Run a query against the console schema (default)
proboctl api 'query { viewer { id email } }'
prb api 'query { viewer { id email } }'
# Run a mutation with variables
proboctl api 'mutation($input: CreateRiskInput!) { createRisk(input: $input) { riskEdge { node { id } } } }' \
prb api 'mutation($input: CreateRiskInput!) { createRisk(input: $input) { riskEdge { node { id } } } }' \
-f input='{"organizationId":"...","name":"Test","category":"Operational","treatment":"ACCEPTED","inherentLikelihood":3,"inherentImpact":3}'
# Query the connect schema
proboctl api --schema connect 'query { viewer { id email } }'
prb api --schema connect 'query { viewer { id email } }'
# Read query from stdin
echo '{ viewer { id } }' | proboctl api -`,
echo '{ viewer { id } }' | prb api -`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
endpoint, ok := schemaEndpoints[flagSchema]

View File

@@ -25,7 +25,7 @@ import (
func NewCmdAuth(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "auth <command>",
Short: "Authenticate proboctl with Probo",
Short: "Authenticate prb with Probo",
}
cmd.AddCommand(login.NewCmdLogin(f))

View File

@@ -34,10 +34,10 @@ func NewCmdLogin(f *cmdutil.Factory) *cobra.Command {
Use: "login",
Short: "Authenticate with a Probo host",
Example: ` # Interactive login (prompts for hostname, token, and org)
proboctl auth login
prb auth login
# Non-interactive login
proboctl auth login --hostname app.getprobo.com --token <token> --org <org-id>`,
prb auth login --hostname app.getprobo.com --token <token> --org <org-id>`,
RunE: func(cmd *cobra.Command, args []string) error {
if f.IOStreams.IsInteractive() {
if flagHost == "" {

View File

@@ -31,10 +31,10 @@ func NewCmdLogout(f *cmdutil.Factory) *cobra.Command {
Use: "logout",
Short: "Log out of a Probo host",
Example: ` # Log out of the active host
proboctl auth logout
prb auth logout
# Log out of a specific host
proboctl auth logout --hostname app.getprobo.com`,
prb auth logout --hostname app.getprobo.com`,
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := f.Config()
if err != nil {

View File

@@ -27,7 +27,7 @@ func NewCmdStatus(f *cmdutil.Factory) *cobra.Command {
Use: "status",
Short: "View authentication status",
Example: ` # Show all configured hosts and their authentication status
proboctl auth status`,
prb auth status`,
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := f.Config()
if err != nil {

View File

@@ -35,13 +35,13 @@ func NewCmdBrowse(f *cmdutil.Factory) *cobra.Command {
Long: "Open the Probo console in the default web browser.",
Aliases: []string{"open"},
Example: ` # Open the Probo console in the browser
proboctl browse
prb browse
# Print the URL without opening the browser
proboctl browse --no-browser
prb browse --no-browser
# Open a specific organization
proboctl browse --org <org-id>`,
prb browse --org <org-id>`,
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := f.Config()
if err != nil {

View File

@@ -25,32 +25,32 @@ func NewCmdCompletion(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "completion <shell>",
Short: "Generate shell completion scripts",
Long: `Generate shell completion scripts for proboctl.
Long: `Generate shell completion scripts for prb.
To load completions:
Bash:
$ source <(proboctl completion bash)
$ source <(prb completion bash)
# To load completions for each session, execute once:
# Linux:
$ proboctl completion bash > /etc/bash_completion.d/proboctl
$ prb completion bash > /etc/bash_completion.d/prb
# macOS:
$ proboctl completion bash > $(brew --prefix)/etc/bash_completion.d/proboctl
$ prb completion bash > $(brew --prefix)/etc/bash_completion.d/prb
Zsh:
$ source <(proboctl completion zsh)
$ source <(prb completion zsh)
# To load completions for each session, execute once:
$ proboctl completion zsh > "${fpath[1]}/_proboctl"
$ prb completion zsh > "${fpath[1]}/_prb"
Fish:
$ proboctl completion fish | source
$ prb completion fish | source
# To load completions for each session, execute once:
$ proboctl completion fish > ~/.config/fish/completions/proboctl.fish
$ prb completion fish > ~/.config/fish/completions/prb.fish
PowerShell:
PS> proboctl completion powershell | Out-String | Invoke-Expression
PS> prb completion powershell | Out-String | Invoke-Expression
# To load completions for each session, execute once:
PS> proboctl completion powershell > proboctl.ps1`,
PS> prb completion powershell > prb.ps1`,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
DisableFlagsInUseLine: true,

View File

@@ -25,7 +25,7 @@ import (
func NewCmdConfig(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "config <command>",
Short: "Manage configuration for proboctl",
Short: "Manage configuration for prb",
}
cmd.AddCommand(configget.NewCmdConfigGet(f))

View File

@@ -27,10 +27,10 @@ func NewCmdConfigGet(f *cmdutil.Factory) *cobra.Command {
Short: "Print the value of a given configuration key",
Long: "Print the value of a given configuration key.\n\nAvailable keys: editor, browser, pager, prompt, http_timeout",
Example: ` # Get the configured editor
proboctl config get editor
prb config get editor
# Get the HTTP timeout
proboctl config get http_timeout`,
prb config get http_timeout`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := f.Config()

View File

@@ -27,13 +27,13 @@ func NewCmdConfigSet(f *cmdutil.Factory) *cobra.Command {
Short: "Update configuration with a value for the given key",
Long: "Update configuration with a value for the given key.\n\nAvailable keys: editor, browser, pager, prompt, http_timeout",
Example: ` # Set the default editor
proboctl config set editor vim
prb config set editor vim
# Set the HTTP timeout to 60 seconds
proboctl config set http_timeout 60s
prb config set http_timeout 60s
# Disable interactive prompts
proboctl config set prompt disabled`,
prb config set prompt disabled`,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := f.Config()

View File

@@ -66,7 +66,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
Use: "create",
Short: "Create a new control",
Example: ` # Create a control
proboctl control create --framework FW_ID --section-title "A.5" --name "Information security policies"`,
prb control create --framework FW_ID --section-title "A.5" --name "Information security policies"`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := f.Config()

View File

@@ -72,10 +72,10 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
Short: "List controls in a framework",
Aliases: []string{"ls"},
Example: ` # List controls in a framework
proboctl control list --framework <framework-id>
prb control list --framework <framework-id>
# Filter and output as JSON
proboctl control ls --framework <framework-id> --filter "access" --json`,
prb control ls --framework <framework-id> --filter "access" --json`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil {

View File

@@ -60,7 +60,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
Use: "create",
Short: "Create a new framework",
Example: ` # Create a framework
proboctl framework create --name "ISO 27001:2022" --description "Information security standard"`,
prb framework create --name "ISO 27001:2022" --description "Information security standard"`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := f.Config()
@@ -85,7 +85,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
}
if flagOrg == "" {
return fmt.Errorf("organization is required; pass --org or set a default with 'proboctl auth login'")
return fmt.Errorf("organization is required; pass --org or set a default with 'prb auth login'")
}
input := map[string]any{

View File

@@ -94,7 +94,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
}
if flagOrg == "" {
return fmt.Errorf("organization is required; pass --org or set a default with 'proboctl auth login'")
return fmt.Errorf("organization is required; pass --org or set a default with 'prb auth login'")
}
variables := map[string]any{

View File

@@ -79,10 +79,10 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
Short: "List organizations you have access to",
Aliases: []string{"ls"},
Example: ` # List all organizations
proboctl org list
prb org list
# Output as JSON
proboctl org ls --json`,
prb org ls --json`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil {

View File

@@ -83,10 +83,10 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
Use: "create",
Short: "Create a new risk",
Example: ` # Create a risk interactively
proboctl risk create --inherent-likelihood 3 --inherent-impact 4
prb risk create --inherent-likelihood 3 --inherent-impact 4
# Create a risk non-interactively
proboctl risk create --name "Data breach" --category "Security" --treatment MITIGATED --inherent-likelihood 3 --inherent-impact 4`,
prb risk create --name "Data breach" --category "Security" --treatment MITIGATED --inherent-likelihood 3 --inherent-impact 4`,
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := f.Config()
if err != nil {
@@ -110,7 +110,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
}
if flagOrg == "" {
return fmt.Errorf("organization is required; pass --org or set a default with 'proboctl auth login'")
return fmt.Errorf("organization is required; pass --org or set a default with 'prb auth login'")
}
if f.IOStreams.IsInteractive() {

View File

@@ -74,13 +74,13 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
Short: "List risks in an organization",
Aliases: []string{"ls"},
Example: ` # List risks in the default organization
proboctl risk list
prb risk list
# Filter risks by name
proboctl risk list --filter "data breach"
prb risk list --filter "data breach"
# List risks sorted by inherent score
proboctl risk ls --order-by INHERENT_RISK_SCORE --json`,
prb risk ls --order-by INHERENT_RISK_SCORE --json`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil {
@@ -109,7 +109,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
}
if flagOrg == "" {
return fmt.Errorf("organization is required; pass --org or set a default with 'proboctl auth login'")
return fmt.Errorf("organization is required; pass --org or set a default with 'prb auth login'")
}
variables := map[string]any{

View File

@@ -34,9 +34,9 @@ import (
func NewCmdRoot(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "proboctl <command> [flags]",
Use: "prb <command> [flags]",
Short: "Probo CLI",
Long: "proboctl is a command-line tool for interacting with the Probo platform.",
Long: "prb is a command-line tool for interacting with the Probo platform.",
SilenceUsage: true,
SilenceErrors: true,
PersistentPreRun: func(cmd *cobra.Command, args []string) {

View File

@@ -60,7 +60,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
Use: "create",
Short: "Create a new statement of applicability",
Example: ` # Create a statement of applicability
proboctl soa create --name "ISO 27001 SoA" --owner PROFILE_ID`,
prb soa create --name "ISO 27001 SoA" --owner PROFILE_ID`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := f.Config()
@@ -85,7 +85,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
}
if flagOrg == "" {
return fmt.Errorf("organization is required; pass --org or set a default with 'proboctl auth login'")
return fmt.Errorf("organization is required; pass --org or set a default with 'prb auth login'")
}
input := map[string]any{

View File

@@ -96,7 +96,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
}
if flagOrg == "" {
return fmt.Errorf("organization is required; pass --org or set a default with 'proboctl auth login'")
return fmt.Errorf("organization is required; pass --org or set a default with 'prb auth login'")
}
variables := map[string]any{

View File

@@ -73,10 +73,10 @@ func NewCmdAdd(f *cmdutil.Factory) *cobra.Command {
Use: "add",
Short: "Add an applicability statement to a SoA",
Example: ` # Add a control as applicable
proboctl soa statement add --soa SOA_ID --control CTRL_ID --applicable
prb soa statement add --soa SOA_ID --control CTRL_ID --applicable
# Add a control as not applicable with justification
proboctl soa statement add --soa SOA_ID --control CTRL_ID --not-applicable --justification "Not in scope"`,
prb soa statement add --soa SOA_ID --control CTRL_ID --not-applicable --justification "Not in scope"`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
if flagApplicable && flagNotApplicable {

View File

@@ -74,10 +74,10 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
Short: "List users in an organization",
Aliases: []string{"ls"},
Example: ` # List users in the default organization
proboctl user list
prb user list
# List only active users as JSON
proboctl user ls --active --json`,
prb user ls --active --json`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil {
@@ -106,7 +106,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
}
if flagOrg == "" {
return fmt.Errorf("organization is required; pass --org or set a default with 'proboctl auth login'")
return fmt.Errorf("organization is required; pass --org or set a default with 'prb auth login'")
}
variables := map[string]any{

View File

@@ -25,7 +25,7 @@ import (
func NewCmdVersion(f *cmdutil.Factory) *cobra.Command {
return &cobra.Command{
Use: "version",
Short: "Print the version of proboctl",
Short: "Print the version of prb",
RunE: func(cmd *cobra.Command, args []string) error {
info := version.GetBuildInfo()
@@ -36,7 +36,7 @@ func NewCmdVersion(f *cmdutil.Factory) *cobra.Command {
_, _ = fmt.Fprintf(
f.IOStreams.Out,
"proboctl version %s (commit: %s, built: %s, go: %s)\n",
"prb version %s (commit: %s, built: %s, go: %s)\n",
v,
info.Commit,
info.BuildDate,

View File

@@ -62,10 +62,10 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
Use: "create",
Short: "Create a webhook subscription",
Example: ` # Create a webhook for all meeting events
proboctl webhook create --url https://example.com/webhook --event MEETING_CREATED --event MEETING_UPDATED
prb webhook create --url https://example.com/webhook --event MEETING_CREATED --event MEETING_UPDATED
# Create a webhook for all supported events
proboctl webhook create --url https://example.com/webhook --event MEETING_CREATED --event MEETING_UPDATED --event MEETING_DELETED --event VENDOR_CREATED --event VENDOR_UPDATED --event VENDOR_DELETED`,
prb webhook create --url https://example.com/webhook --event MEETING_CREATED --event MEETING_UPDATED --event MEETING_DELETED --event VENDOR_CREATED --event VENDOR_UPDATED --event VENDOR_DELETED`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
for _, e := range flagEvents {
@@ -103,7 +103,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
}
if flagOrg == "" {
return fmt.Errorf("organization is required; pass --org or set a default with 'proboctl auth login'")
return fmt.Errorf("organization is required; pass --org or set a default with 'prb auth login'")
}
input := map[string]any{