Refactor pc config to support key-based configuration and commands#90
Refactor pc config to support key-based configuration and commands#90austin-denoble wants to merge 8 commits into
pc config to support key-based configuration and commands#90Conversation
…mands Replaces the per-setting subcommands (get-api-key, set-api-key, set-color, set-environment) with a generic, registry-driven interface modeled after `gh config` and `aws configure`. A new keyDescriptor in registry.go holds each setting's getter, setter, validator, side-effect hook, short/long descriptions, and sensitivity flag. Adding a new key is supported through a registry entry rather than a command file. Adds `pc config get|set|unset|list|describe <key>`, with --json and --reveal flag support for sensitive values like API keys. The four legacy commands are maintained as hidden, deprecated aliases to maintain functionality for existing users.
`pc config unset` now invokes the onChange hook when clearing a key changes its value, fixing a bug where `unset environment` on staging silently resets to production without clearing the staging-tied OAuth session, API key, or target org/project. `pc config set` now passes the canonical stored value to onChange via a post-set getStr() read, rather than the raw user input. Hooks like environment's now see "production" instead of "prod" when the input is normalized during setStr.
Extract business logic from cobra Run blocks into run* functions that accept a ConfigService interface, enabling unit testing without touching the real viper config store. Add --json output to set and unset commands for machine-readable use. Add a Hidden field to keyDescriptor to suppress internal keys (environment) from list output and tab completion while keeping them accessible by name.
… onChange: add specific fields to keyDescriptor for validateStr, persistStr, and onChange. make sure config set|unset call validateStr, onChange, and then persistStr to avoid onChange failing after persistence has already occurred
…ually checked in ValidateStr
| case "production", "staging": | ||
| // canonical values | ||
| default: | ||
| return "", fmt.Errorf("invalid environment %q; must be one of: production, staging", value) |
There was a problem hiding this comment.
Environment prod alias dropped
Medium Severity
pc config set environment rejects prod, while the hidden legacy set-environment command still maps prod to production. The new registry path only accepts the literals production and staging, so shorthand that worked before fails on the primary command.
Reviewed by Cursor Bugbot for commit 1a6a4f9. Configure here.
| } | ||
| } | ||
| desc.persistStr(normalizedVal) | ||
| return lines, nil |
There was a problem hiding this comment.
Environment side effects before persist
Medium Severity
For environment, Set and Unset run onChange (OAuth logout, API key and target clears with immediate disk writes) before persistStr updates the environment field. If the final environment write fails or the process exits afterward, auth and targets can be wiped while the stored environment never changes.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 1a6a4f9. Configure here.
| }, | ||
| persistStr: func(value string) { | ||
| secrets.DefaultAPIKey.Set(value) | ||
| }, |
There was a problem hiding this comment.
Empty api-key allowed on set
Low Severity
The api-key registry entry has no validateStr, so pc config set api-key accepts an empty value and persists it. The deprecated set-api-key command rejects empty input, and the registry comment implies nil ValidValues means any non-empty string.
Reviewed by Cursor Bugbot for commit 1a6a4f9. Configure here.
| if lines, err = desc.onChange(ctx, oldVal, newVal); err != nil { | ||
| return nil, err | ||
| } | ||
| } |
There was a problem hiding this comment.
Env binding retriggers environment hook
Medium Severity
environment uses conf.Environment.Get() for unset no-op checks and ErrNoChange, but that getter includes the bound PINECONE_ENVIRONMENT variable. When the env var overrides the file, Unset/Set toward production can keep seeing a non-default effective value, so the destructive onChange hook runs on every attempt even after the file already holds the target value.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 1a6a4f9. Configure here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 5 potential issues.
There are 9 total unresolved issues (including 4 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8936497. Configure here.
| target organization and project are reset. You will need to re-authenticate | ||
| and re-target after switching. | ||
| `), | ||
| Hidden: true, |
There was a problem hiding this comment.
environment omitted from list
Medium Severity
The environment setting is marked Hidden, and List only includes visibleKeys(), so pc config list omits environment even though the PR describes listing all three settings including environment.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 8936497. Configure here.
| } | ||
| } | ||
| desc.persistStr(newVal) | ||
| return lines, nil |
There was a problem hiding this comment.
unset api-key skips auth state
Medium Severity
pc config unset api-key only clears the stored API key via persistStr and never reconciles state.AuthedUser, unlike pc auth clear --api-key, so the in-process auth context can stay on API-key mode after the key was removed.
Reviewed by Cursor Bugbot for commit 8936497. Configure here.
| token, err := oauth.Token(ctx) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("error retrieving oauth token: %w", err) | ||
| } |
There was a problem hiding this comment.
Token error blocks env change
Medium Severity
The environment onChange hook requires oauth.Token to succeed before set or unset can finish, so a stale or broken OAuth record can prevent changing or resetting environment even when the user only relies on an API key.
Reviewed by Cursor Bugbot for commit 8936497. Configure here.
| } | ||
|
|
||
| if opts.json { | ||
| fmt.Fprintln(os.Stdout, text.IndentJSON(setOutput{Key: keyName, Value: value})) |
There was a problem hiding this comment.
JSON set shows raw value
Low Severity
With --json, pc config set prints the user-supplied value from the CLI args instead of the normalized value persisted by the registry (for example on vs true for color).
Reviewed by Cursor Bugbot for commit 8936497. Configure here.
|
|
||
| if opts.json { | ||
| fmt.Fprintln(os.Stdout, text.IndentJSON(setOutput{Key: keyName, Value: value})) | ||
| return nil |
There was a problem hiding this comment.
JSON hides auth side effects
High Severity
With --json, successful pc config set and pc config unset return only key metadata and exit before printing onChange lines, so environment changes that log out OAuth, clear the API key, and reset targets complete silently for JSON consumers.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 8936497. Configure here.


Problem
The
pc configcommand exposed configuration through a flat set of verb-noun subcommands —get-api-key,set-api-key,set-color,set-environment— with one Cobra command file per setting. The pattern had a few problems:listequivalent.gh,aws,npm,stripe) handle config, raising the learning curve for new users.This becomes more painful as the set of configurable values grows.
Solution
Refactor
pc configto a generic, registry-driven interface modeled aftergh config. A singlekeyDescriptorininternal/pkg/cli/command/config/registry.goholds each setting's getter, setter, clear function, optionalonChangeside-effect hook, short and long descriptions, sensitivity flag, and valid values. Theget/set/unset/list/describecommands are generic and dispatch through the registry — adding a new config key is now a single registry entry, no new command file required.New commands
Registry-driven extensibility
Each setting is one entry in
configRegistry. Adding a new key — say, a default output format — would look like:No new command file. No changes to
root.go's skip-auth list. It just works acrossget,set,unset,list, anddescribe.Side-effect hooks
The
environmentkey needs to clear OAuth state, the API key, and the target org/project when it changes. That used to live inline inset-environment. It now lives in anonChangehook on the registry entry and fires consistently from bothsetandunset:Flags
--jsononget,list,describefor machine-readable output.--revealonget,list,describeto unmask sensitive values (defaults to masking head/tail forapi-key).Backwards compatibility
The four legacy commands (
get-api-key,set-api-key,set-color,set-environment) are preserved as hidden, deprecated aliases. They continue to function and print Cobra's standard deprecation noticepointing at the new equivalent:
Existing scripts and docs keep working; new users land on the new pattern.
Type of Change
The legacy commands are kept as hidden aliases, so this is non-breaking for existing users. Docs referencing
pc config set-api-keyetc. should be updated to usepc config set api-key <value>going forward.Test Plan
just test-unitpassespc config list— confirms all three keys render with descriptionspc config get api-key— confirms<not set>placeholder for empty valuespc config get api-key --reveal— confirms full value shown when revealedpc config set environment staging— confirms OAuth/API key/target wipe firespc config unset environment(from staging) — confirms same wipe fires (regression fix)pc config set environment prod— confirms canonical "production" is stored and reportedpc config describe api-key/environment/color— confirms long description rendered when present, omitted when blankpc config get-api-key/set-api-key/set-color/set-environment— confirms each still works and prints deprecation notice--jsononget,list,describe— confirms structured outputNote
Medium Risk
Environment
onChangeclears OAuth, API keys, and targets on set/unset—behavior change vs legacy paths for unset; API key handling remains sensitive but follows existing masking patterns.Overview
Replaces per-setting
pc configsubcommands with genericget,set,unset,list, anddescribethat dispatch through a centralconfigRegistry(keyDescriptorentries forapi-key,color,environment). New keys are added via registry entries instead of new command files.ConfigServicebacks the handlers with validation,ErrNoChangeidempotency, andonChangehooks (environment changes now clear OAuth, API key, and target org/project on both set and unset).get/list/describesupport--jsonand--revealfor sensitive values;set/unsetsupport--json.Legacy
get-api-key,set-api-key,set-color, andset-environmentremain as hidden, deprecated aliases; help text points users topc config set api-key.root.goadds the new subcommands to the auth-skip list. Broad unit tests cover masking, JSON output, and registry behavior.Reviewed by Cursor Bugbot for commit 8936497. Bugbot is set up for automated code reviews on this repo. Configure here.