#2063

Add config.toml profiles and hoop config use-context

Openreysravangawants to merge intofrom

IMPORTANT

Every PR MUST have exactly one of these labels, the merge is blocked otherwise:

  • major / minor / patch publishes a new release on merge with the corresponding semver bump.
  • skip-release merges without publishing a release (use for docs, CI changes, refactors, etc.).

Label: minor

📝 Description

~/.hoop/config.toml can hold more than one account. Each account is a [profile <name>] section. current selects the account. When current is absent, the first profile is active.

hoop config use-context <name> switches the account in that same file. hoop login and hoop config create update the active profile. The other profiles stay in the file. A file with top-level token, api_url, and grpc_url and no profiles still loads.

HOOP_CONFIG selects the config file. An empty value means unset. The default path is still $HOME/.hoop/config.toml.

📣 User-facing impact

The CLI can keep more than one Hoop account in ~/.hoop/config.toml and switch with hoop config use-context <name>.

🔗 Related Issue

None.

🚀 Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📚 Documentation update
  • 🎨 Style/UI update
  • ♻️ Code refactor
  • ⚡ Performance improvement
  • ✅ Test update
  • 🔧 Build configuration change
  • 🧹 Chore

📋 Changes Made

  • Parse and write [profile <name>] and [profile.<name>] sections in one config.toml, including current.
  • Add hoop config get-contexts, current-context, use-context, set-context, and delete-context. get-contexts aliases are contexts and ls. use-context alias is use.
  • Update only the active profile on save. Refuse to delete the active profile.
  • Honor HOOP_CONFIG without creating that file when it is missing.
  • Document the commands in client/cmd/config/README.md and in hoop config help.

🧪 Testing

Test Configuration:

  • Browser(s): N/A
  • OS: macOS (darwin arm64)

Tests performed:

  • Unit tests pass
  • Integration tests pass
  • Manual testing completed

go test ./clientconfig/ ./cmd/hoop/ in common/ passed. go test of ./client/config and ./client/cmd/config did not run: the client module fetches private github.com/hoophq/libhoop.

Manual checks: hoop config, hoop config get-contexts, hoop config current-context, and hoop config use-context --help. A config file with two profiles was not rewritten by help or by listing profiles.

📸 Screenshots (if applicable)

N/A. CLI only.

✅ Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes
  • I have checked my code and corrected any misspellings

📄 Additional Notes

This is local CLI state. A gateway feature flag cannot gate it.

common/cmd/hoop is a small front binary. It runs the profile commands and execs the installed release for every other command, because this environment cannot fetch libhoop. Drop that directory from the PR if the review should contain only the client config change. The profile commands in client/cmd/config are the change to ship.

Activity

20
github-actions[bot]4d ago


Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA


Sravan Kumar seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

qodo-code-review[bot]4d ago

PR Summary by Qodo

Add multi-account config profiles and context commands

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

AI Description

• Store multiple accounts in one config.toml and select the active profile with context commands.
• Preserve other profiles when login or config create saves credentials; continue loading flat
 configs.
• Support HOOP_CONFIG paths and document and test profile management.
Diagram

graph TD
  C["Context commands"] --> P["Path resolver"] --> M["Profile manager"] --> F[("Config file")]
  L["Client load/save"] --> P
  L --> M
  E["HOOP_CONFIG"] --> P
High-Level Assessment

The following are alternative approaches to this PR:

1. Normalize headers and use the existing TOML decoder
  • ➕ Relies on a mature parser for TOML values and syntax.
  • ➕ Reduces the amount of custom parsing to maintain.
  • ➖ The documented spaced profile headers are not standard TOML and would need preprocessing.
  • ➖ Preserving unknown fields through rewrites would require additional handling.

Recommendation: The dedicated profile manager fits the documented spaced-header format and keeps selection and writes in one place. Before extending the supported file syntax, consider header normalization and the existing TOML decoder to limit long-term parser maintenance.

Files changed (9) +1538 / -14

Enhancement (5) +954 / -14
config.goRegister context commands and expose active config details +32/-4

Register context commands and expose active config details

• Adds the context subcommands and profile guidance to config help. Config view can display the selected context and config file path.

client/cmd/config/config.go

context.goImplement profile management commands +126/-0

Implement profile management commands

• Adds commands to list, inspect, select, save, and delete profiles, including aliases and an active-profile marker. Uses the shared config path and warns when HOOP_CONFIG overrides it.

client/cmd/config/context.go

config.goLoad and save active-profile credentials +68/-8

Load and save active-profile credentials

• Loads the selected named profile while retaining flat-config support, and saves credential changes to the active profile without replacing other accounts. Also exposes the loaded path and makes removal tolerate a missing file.

client/config/config.go

clientconfig.goResolve HOOP_CONFIG client paths +46/-2

Resolve HOOP_CONFIG client paths

• Adds trimmed HOOP_CONFIG overrides with relative-path and home-directory expansion. Override resolution does not create the target file or the default Hoop directory.

common/clientconfig/clientconfig.go

context.goAdd profile parsing and persistence +682/-0

Add profile parsing and persistence

• Introduces named-profile parsing, active selection, profile CRUD, and flat-config handling. Rewrites profile files through a private temporary file while retaining unrecognized profile fields.

common/clientconfig/context.go

Tests (3) +532 / -0
context_test.goVerify context command registration +28/-0

Verify context command registration

• Checks that all five context commands are registered and that the use alias resolves correctly.

client/cmd/config/context_test.go

clientconfig_test.goTest config path overrides +79/-0

Test config path overrides

• Covers absolute, relative, and tilde-prefixed HOOP_CONFIG paths, missing-home behavior, and rejection of a bare tilde.

common/clientconfig/clientconfig_test.go

context_accounts_test.goExercise profile lifecycle and compatibility +425/-0

Exercise profile lifecycle and compatibility

• Tests spaced and dotted headers, selection, active-only updates, deletion safeguards, flat-file conversion, and preservation of extra or quoted keys. Also covers missing selections and profile-file parsing.

common/clientconfig/context_accounts_test.go

Documentation (1) +52 / -0
README.mdDocument config profiles and context commands +52/-0

Document config profiles and context commands

• Explains profile selection, command behavior, supported section headers, flat-file compatibility, and an example switch.

client/cmd/config/README.md

qodo-code-review[bot]4d ago

Code Review by Qodo


1. Profile extras discard write errors 📘 Rule violation ☼ Reliability
Description
ProfileFile.Marshal calls fmt.Fprintf for each unrecognized profile field without checking or
explicitly discarding its returned error. Profile switching and updates use this serialization path,
so a future writer failure would be silently ignored.
Code

common/clientconfig/context.go[235]

+			fmt.Fprintf(&buf, "%s = %s\n", formatKey(extra.Key), extra.Raw)
Relevance

●●● Strong

Accepted precedent requires checking writer results; this is a deterministic I/O error-handling fix.

PR-#1722

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed serializer writes an extra profile field with fmt.Fprintf and ignores both returned
values. Compliance rule 1807206 requires OS, execution, and I/O errors to be handled or explicitly
ignored with blank identifiers.

Rule 1807206: Do not ignore errors from OS/exec/IO calls; handle or propagate them common/clientconfig/context.go[235-235]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The profile serializer ignores the return values from `fmt.Fprintf` when writing preserved extra fields, contrary to the requirement to handle or explicitly document ignored I/O errors.

## Fix Focus Areas
- common/clientconfig/context.go[235-235]

## Recommended Fix
Check the return values from the formatting write, or use an explicitly documented blank assignment if the error is guaranteed to be impossible for the buffer implementation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


2. Quoted credentials are ignored ✓ Resolved
Description
ParseProfileFile passes the raw key from splitAssignment to duplicate tracking, current
selection, and profile-field dispatch, so quoted keys such as "token" and "current" are treated
as unknown names. A valid config using quoted recognized keys therefore loads without its token or
selected profile, and mixing quoted and bare spellings can emit duplicate settings when the file is
rewritten.
Code

common/clientconfig/context.go[R524-528]

+			key := strings.TrimSpace(text[:i])
+			if key == "" {
+				return "", "", false
+			}
+			return key, strings.TrimSpace(text[i+1:]), true
Relevance

●●● Strong

Deterministic parser bug: valid quoted TOML keys break promised profile loading and selection.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
splitAssignment returns the key text unchanged, including TOML quotes. ParseProfileFile compares
that raw key to bare current and passes it to setProfileField, whose recognized cases only
accept bare names; unmatched keys are stored as extras and later rewritten, so valid quoted
credential keys are ignored rather than loaded.

common/clientconfig/context.go[72-102] common/clientconfig/context.go[418-451] common/clientconfig/context.go[498-533] common/clientconfig/context_accounts_test.go[211-246]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Valid TOML quoted keys such as `"token"` and `"current"` are preserved as raw strings by `splitAssignment`, so profile parsing does not recognize them as credential fields or the active-profile selector. Quoted and bare spellings can also bypass duplicate-key detection.

## Fix Focus Areas
- common/clientconfig/context.go[72-102]
- common/clientconfig/context.go[498-533]

## Recommended Fix
Parse and normalize quoted keys before duplicate detection and recognized-field dispatch, while retaining the original key spelling for unknown `extraField` entries so quoted custom keys continue to round-trip unchanged. Add tests covering quoted `token`, quoted `current`, and duplicate detection when quoted and bare spellings refer to the same key.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Override paths silently hide home errors ✓ Resolved
Description
NewPath ignores the os.UserHomeDir() error while resolving the home directory for an override
path. When HOOP_CONFIG is set and home-directory lookup fails, callers receive no error and
continue with an empty hoopHomeDir, masking an operating-system failure that may affect downstream
configuration behavior.
Code

common/clientconfig/clientconfig.go[R95-97]

+			if home, homeErr := os.UserHomeDir(); homeErr == nil {
+				hoopHomeDir = filepath.Join(home, clientConfigFolder)
+			}
Relevance

●●● Strong

Unchecked OS errors are a deterministic reliability issue; propagating UserHomeDir failure matches
repository error-handling expectations.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed NewPath implementation conditionally assigns hoopHomeDir only when
os.UserHomeDir() succeeds and otherwise returns success, leaving the error unchecked. The
checklist requires OS and IO errors to be handled or propagated rather than silently ignored.

Rule 1807206: Do not ignore errors from OS/exec/IO calls; handle or propagate them common/clientconfig/clientconfig.go[87-98]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`NewPath` silently ignores failures from `os.UserHomeDir()` when `HOOP_CONFIG` is set, so callers cannot distinguish a valid override from an environment lookup failure.

## Fix Focus Areas
- common/clientconfig/clientconfig.go[95-97]

## Recommended Fix
Handle the `os.UserHomeDir()` error explicitly: either return a wrapped error when the lookup fails or document and safely avoid the lookup if the home directory is not required for the override path. Do not discard the error without an explicit rationale.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View high (2)
4. Account switches target the wrong file ✓ Resolved
Description
defaultConfig always selects $HOME/.hoop/config.toml instead of resolving HOOP_CONFIG, while
common/cmd/hoop/main.go duplicates the context commands, aliases, argument checks and help text
already defined by the client. When HOOP_CONFIG is set, wrapper profile commands can read or
change a different file from delegated client commands, and later command or path-rule changes must
be kept in sync across both implementations.
Code

common/cmd/hoop/main.go[R38-39]

+	// The installed release always reads this path. It does not read HOOP_CONFIG.
+	return filepath.Join(home, ".hoop", "config.toml"), nil
Relevance

●●● Strong

Ignoring HOOP_CONFIG causes wrapper and delegated commands to operate on different configuration
files.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The command switch and hard-coded help in common/cmd/hoop/main.go repeat definitions in the
client's context and config commands. The wrapper's defaultConfig hard-codes the default path,
whereas clientconfig.NewPath honors HOOP_CONFIG and the client's load path uses that resolver.

common/cmd/hoop/main.go[33-39] common/clientconfig/clientconfig.go[62-100] client/config/config.go[95-105] common/cmd/hoop/main.go[80-213] client/cmd/config/context.go[13-112]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The wrapper duplicates the client's command definitions and help while ignoring `HOOP_CONFIG`, so wrapper profile commands can operate on a different config file from delegated client commands.

## Fix Focus Areas
- common/cmd/hoop/main.go[33-213]
- common/clientconfig/clientconfig.go[62-83]

## Recommended Fix
Either remove the shim now that the client supports profiles, or derive its help and command behavior from shared definitions and resolve its config path with `clientconfig.NewPath`, including when `HOOP_CONFIG` is empty. Test profile switching and delegation with `HOOP_CONFIG` set.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Wrapper can overwrite all saved profiles with one ⊘ Outdated
Description
dispatch calls recoverBackup on every invocation, while delegate backs up and replaces the
shared config file without coordinating with other wrapper processes. If a second command starts
while a release hoop login is waiting for browser authentication, recovery removes the backup
before the login child saves its flat file; finish then has no backup from which to merge the
credentials, and the other profiles are lost.
Code

common/cmd/hoop/main.go[R42-45]

+func dispatch(args []string, real, configPath string) (int, error) {
+	if err := recoverBackup(configPath); err != nil {
+		return 1, err
+	}
Relevance

●● Moderate

Shared backups create a credible concurrent data-loss race, but fixing it requires broader
process-coordination design.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
delegate writes a backup and a flat config file before running its child, and concurrent processes
use the same backup pathname. A second process can call recoverBackup while the first child is
running; recovery writes the profile document back and removes the backup (lines 382–395). If the
first child subsequently saves, it writes flat TOML over that document. finish detects that the
file differs from what delegate wrote and calls recoverBackup, but the backup is gone, so
recovery returns without restoring the other profiles. No step coordinates these operations across
processes.

common/cmd/hoop/main.go[42-59] common/cmd/hoop/main.go[311-344] common/cmd/hoop/main.go[346-396] client/config/config.go[227-235] common/cmd/hoop/main.go[285-323] common/cmd/hoop/main.go[325-395] common/cmd/hoop/main.go[398-430]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Concurrent wrapper processes share a backup and temporarily replace the same config file. Recovery by one process can remove the backup while another process's child is still running, causing a later credential save to overwrite the profiles with no backup available to merge from.

## Fix Focus Areas
- common/cmd/hoop/main.go[42-59]
- common/cmd/hoop/main.go[285-395]

## Recommended Fix
Hold an exclusive interprocess advisory lock, such as `flock` on `config.toml.lock`, across recovery, backup, child execution, and restoration. Make startup recovery and profile commands wait for the lock or avoid recovery while it is held. Alternatively, use per-process backups that are not recovered while their owner process is alive. Add a test with overlapping delegated commands that both save credentials.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



6. Account changes erase top-level settings 🐞 Bug ≡ Correctness ⭐ New
Description
ParseProfileFile sends top-level assignments through setFlat, which discards unrecognized keys
while preserving unrecognized keys inside profiles. When a file contains such a top-level key
alongside profiles, switching or updating an account rewrites the file without that key; converting
a flat file with set-context drops it too.
Code

common/clientconfig/context.go[96]

+			if err := doc.setFlat(name, raw); err != nil {
Relevance

●●● Strong

Clear data-loss bug in configuration rewriting; preserving unknown top-level keys matches profile
preservation intent.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
setFlat returns without storing an unknown key, whereas the profile branch stores unknown keys in
Extra. Marshal emits only current and profile fields, and the account-switch and
active-profile update paths both write its output; SaveProfile also writes that output when
converting a flat file.

common/clientconfig/context.go[84-106] common/clientconfig/context.go[405-416] common/clientconfig/context.go[216-239] common/clientconfig/context.go[292-305] common/clientconfig/context.go[308-336] common/clientconfig/context.go[353-387]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Unrecognized top-level assignments are accepted but discarded by the profile parser, so profile rewrites and flat-to-profile conversion remove them.

## Fix Focus Areas
- common/clientconfig/context.go[95-105]
- common/clientconfig/context.go[405-416]
- common/clientconfig/context.go[216-239]

## Recommended Fix
Retain unrecognized top-level assignments in the parsed document. Emit them when rewriting a profiles file, and carry them into the new profile when converting a flat file. Add round-trip tests for both paths.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


7. Switching accounts corrupts dotted keys 🐞 Bug ≡ Correctness
Description
assignmentKey removes the quotes from a literal key such as "token.extra", and formatKey
writes it back unquoted because bareKey accepts dots. Switching contexts therefore changes that
single key into the dotted path token.extra; alongside an existing string-valued token, the
rewritten file is invalid TOML.
Code

common/clientconfig/context.go[R516-519]

+	if bareKey(name) {
+		return name
+	}
+	return strconv.Quote(name)
Relevance

●●● Strong

Quoted keys containing dots are rewritten as dotted paths, corrupting valid TOML during profile
switching.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Unknown keys are stored after assignmentKey has removed their quotes. Marshal passes the stored
name to formatKey, which emits dotted names without quotes, and UseProfile writes that marshaled
document on a switch. The focused regression test covers a quoted key containing =, but not one
containing a dot.

common/clientconfig/context.go[101-105] common/clientconfig/context.go[234-235] common/clientconfig/context.go[292-304] common/clientconfig/context.go[502-543] common/clientconfig/context_accounts_test.go[250-281]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Rewriting a quoted literal key containing a dot changes it into a dotted TOML path, which can make a previously valid profile file invalid.
## Fix Focus Areas
- common/clientconfig/context.go[502-544]
- common/clientconfig/context.go[101-105]
- common/clientconfig/context.go[234-235]
## Recommended Fix
Retain whether each parsed key segment was quoted, and use that structure when writing unknown profile keys and checking duplicates. Test switching profiles containing `token` and `"token.extra"` in the same section.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


8. Valid config keys block account commands 🐞 Bug ≡ Correctness
Description
assignmentKey sends every key starting with a quote to parseString, which rejects a valid
dotted-key suffix such as the .name in "vendor".name. When that key appears in a profile,
parsing fails before the unknown-key preservation path, so loading the account and running context
commands both fail.
Code

common/clientconfig/context.go[R508-509]

+	if raw[0] == '"' || raw[0] == '\'' {
+		return parseString(raw)
Relevance

●●● Strong

Valid quoted dotted keys currently fail parsing, directly breaking profile loading and context
commands.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Every assignment passes through assignmentKey, and its error aborts profile parsing. parseString
rejects text following the closing quote unless it is a comment; unknown fields are preserved only
after key parsing succeeds. Profile loading and context commands consume this parser.

common/clientconfig/context.go[72-105] common/clientconfig/context.go[502-511] common/clientconfig/context.go[601-610] client/config/config.go[108-114] client/cmd/config/context.go[72-76]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A valid TOML key such as `"vendor".name` is rejected because the key parser treats its first quoted segment as the entire key.
## Fix Focus Areas
- common/clientconfig/context.go[502-512]
- common/clientconfig/context.go[76-81]
## Recommended Fix
Parse complete TOML key paths, including mixed quoted and bare segments, before dispatching or preserving the key. Add a profile-file test that loads and switches a context containing such a key.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


View medium (11)
9. Account switches change quoted key names ✓ Resolved
Description
ParseProfileFile splits assignments at the first =, and Marshal inserts spaces around that
split when writing an unknown profile key. If a profile contains the valid TOML assignment `"team=x"
= "one", switching contexts rewrites its key as "team = x"`, changing the setting for any consumer
of that key.
Code

common/clientconfig/context.go[R232-234]

+		for _, extra := range p.Extra {
+			fmt.Fprintf(&buf, "%s = %s\n", extra.Key, extra.Raw)
+		}
Relevance

●●● Strong

Deterministic TOML round-trip bug directly breaks the PR's context-switching feature.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
strings.Cut splits "team=x" = "one" into key "team and raw value x" = "one". The new Extra
path retains those pieces, and Marshal joins them as "team = x" = "one"; UseProfile invokes
that rewrite when selecting a context.

common/clientconfig/context.go[72-77] common/clientconfig/context.go[99-103] common/clientconfig/context.go[232-234] common/clientconfig/context.go[290-303]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A profile switch can change a valid quoted TOML key containing `=` while rewriting unknown fields.
## Fix Focus Areas
- common/clientconfig/context.go[72-77]
- common/clientconfig/context.go[99-103]
- common/clientconfig/context.go[232-234]
- common/clientconfig/context_accounts_test.go[211-250]
## Recommended Fix
Parse the assignment separator outside quoted keys, or retain and emit the original unknown assignment line verbatim. Add a switch test using `"team=x" = "one"` and verify that the key remains unchanged.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Account switches erase extra profile keys ✓ Resolved
Description
ParseProfileFile ignores an unrecognized profile key without retaining its value, and
ProfileFile.Marshal writes only the recognized fields. Switching accounts rewrites the entire
file, so extra keys disappear from inactive profiles as well as the selected one.
Code

common/clientconfig/context.go[R91-93]

+		if err := setProfileField(current, key, raw); err != nil && !errors.Is(err, errUnknownKey) {
+			return ProfileFile{}, fmt.Errorf("line %d: profile %q: %w", lineNo, current.Name, err)
+		}
Relevance

●●● Strong

Rewriting profiles while silently dropping unknown keys causes direct configuration data loss and
violates profile preservation intent.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The parser suppresses errors for unknown profile keys, but Profile has nowhere to store them.
UseProfile then serializes every profile through Marshal, which emits only five credential
fields; the new test explicitly accepts an ignored extra key.

common/clientconfig/context.go[13-22] common/clientconfig/context.go[90-95] common/clientconfig/context.go[203-223] common/clientconfig/context.go[276-289] common/clientconfig/context_accounts_test.go[181-204]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Unknown keys in named profiles are accepted but silently lost when a context command rewrites the configuration file.
## Fix Focus Areas
- common/clientconfig/context.go[91-93]
- common/clientconfig/context.go[203-223]
- common/clientconfig/context.go[276-289]
## Recommended Fix
Preserve unrecognized profile entries in the parsed document and emit them during serialization, including for profiles other than the active one. Test that switching contexts retains an extra key in each profile.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. A missing release runs another version ⊘ Outdated
Description
runtimeBinary falls back to the last usable directory it encounters when the configured active
binary cannot be used. If that binary is missing but older versions remain installed, delegated
commands silently run a version other than the one selected in versions.toml.
Code

common/cmd/hoop/main.go[R496-499]

+		path, ok := usableBinary(candidate, self)
+		if ok {
+			found = path
+		}
Relevance

●●● Strong

Active version is authoritative; scanning can silently select an older release.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The version store identifies the active release as its source of truth, whereas the wrapper ignores
an unusable active selection and returns a different usable binary from its directory scan.

client/upgrade/store.go[20-25] common/cmd/hoop/main.go[479-505]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
When the selected release is missing, delegation silently chooses another installed binary.
## Fix Focus Areas
- common/cmd/hoop/main.go[479-505]
- client/upgrade/store.go[20-25]
## Recommended Fix
Treat a configured but unusable active version as an error instead of scanning other version directories; define a separate explicit policy for an absent active selection.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


12. Existing flat configs with extra TOML fail to load ✓ Resolved
Description
Load now runs the strict ParseProfileFile before falling back to toml.Decode; the custom
parser rejects unknown keys, non-profile tables, multi-line strings, and profile headers with
trailing comments because profileHeader requires ] to end the line. Files previously accepted by
BurntSushi/toml, including one with [profile.work] # production, now cause Load and Save to
fail, affecting hoop login, hoop connect, and other CLI commands.
Code

client/config/config.go[R105-108]

+	profiles, err := clientconfig.ParseProfileFile(data)
+	if err != nil {
+		return nil, fmt.Errorf("failed decoding configuration file=%v, err=%v", filepath, err)
+	}
Relevance

●●● Strong

Strict parsing breaks previously supported TOML files, violating backward compatibility for existing
configurations.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Load returns ParseProfileFile errors rather than falling back to toml.Decode. Within the
parser, setProfileField returns an error for unrecognized keys, profileHeader rejects
non-profile tables, and header lines reach profileHeader unmodified, so a trailing comment fails
its final-] check. Previously, toml.Decode handled these files and tolerated extra keys.

client/config/config.go[101-108] common/clientconfig/context.go[387-445] common/clientconfig/context.go[44-48] common/clientconfig/context.go[425-445]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The strict custom parser gates loading of every config file and rejects files previously accepted by BurntSushi/toml, including files with unknown keys and profile headers followed by inline comments.

## Fix Focus Areas
- client/config/config.go[101-108]
- common/clientconfig/context.go[44-48]
- common/clientconfig/context.go[387-423]
- common/clientconfig/context.go[425-445]

## Recommended Fix
Parse the file with BurntSushi/toml into a struct containing top-level fields and `profile map[string]Profile`, allowing unknown keys to be ignored. If retaining the custom parser, make its result authoritative only when it finds `[profile` headers, otherwise fall back to `toml.Decode`; do not fail on unknown keys, and parse a header’s closing bracket before an optional trailing comment while rejecting non-comment trailing content. Add tests for dotted profile headers with inline comments.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


13. An override still requires a home folder ✓ Resolved
Description
NewPath calls newHomeDir before checking whether HOOP_CONFIG supplies a complete path. With a
valid override but no usable home directory, loading and profile commands fail before they try the
specified file.
Code

common/clientconfig/clientconfig.go[R91-98]

Relevance

●●● Strong

A complete override path should not depend on resolving or creating the default home directory.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
newHomeDir can return an error while resolving or creating the home directory. NewPath returns
that error before its newly added override branch, and client loading propagates it.

common/clientconfig/clientconfig.go[47-59] common/clientconfig/clientconfig.go[86-101] client/config/config.go[95-105]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A valid HOOP_CONFIG file cannot be used unless default home-directory initialization succeeds first.
## Fix Focus Areas
- common/clientconfig/clientconfig.go[86-101]
- common/clientconfig/clientconfig.go[47-59]
## Recommended Fix
Resolve the client-file override before creating the default home directory; initialize the default directory only when no override is selected.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


14. Installed clients do not use the wrapper ✓ Resolved
Description
The new common/cmd/hoop executable is not built by the existing development or release targets,
which still compile the client entry point. Consequently, installed hoop invocations never enter
its flatten-and-delegate path, so that compatibility path is unavailable to installed releases.
Code

common/cmd/hoop/main.go[R1-3]

Relevance

●●● Strong

Without build-target integration, the wrapper feature is unavailable in installed releases.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new package declares a separate executable, but both cited build targets still compile client
rather than common/cmd/hoop.

common/cmd/hoop/main.go[1-3] Makefile[78-80] Makefile[375-379]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The newly added delegation executable is absent from the build and installation paths, so installed clients cannot use it.
## Fix Focus Areas
- common/cmd/hoop/main.go[1-3]
- Makefile[78-80]
- Makefile[375-379]
## Recommended Fix
Either package and install the wrapper as the intended entry point while retaining a separate release binary, or remove the unused compatibility path and rely on the native client commands.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


15. Windows clients cannot launch a release ⊘ Outdated
Description
runtimeBinary searches for a file named hoop in each installed version directory. On Windows,
the version manager installs hoop.exe, so a wrapper installed there reports that no released
binary exists instead of delegating.
Code

common/cmd/hoop/main.go[495]

Relevance

●●● Strong

Using a literal binary name prevents delegation on Windows installations that use the executable
suffix.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The version manager constructs installed binary paths using an OS-specific filename; the wrapper
constructs both candidate paths with the literal name hoop.

client/upgrade/paths.go[22-27] client/upgrade/paths.go[78-84] common/cmd/hoop/main.go[479-500]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The wrapper searches for Unix binary names on Windows, where installed releases have an .exe suffix.
## Fix Focus Areas
- common/cmd/hoop/main.go[479-500]
- client/upgrade/paths.go[22-27]
## Recommended Fix
Share or reproduce the version manager's platform-specific executable naming in both active-version lookup and fallback scanning.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


16. Login cannot create a new HOOP_CONFIG file ✓ Resolved
Description
With HOOP_CONFIG set, NewPath no longer creates the file, and Load now calls os.ReadFile and
returns a hard error when it fails. loadAndValidateConfig treats that as fatal, so
HOOP_CONFIG=./new.toml hoop login exits before it reaches the host prompt or Save. That leaves
the os.IsNotExist fallback added to Save unreachable for login, and the CLI no longer falls back
to localhost mode.
Code

client/config/config.go[R101-104]

Relevance

●●● Strong

Missing override files make login fail before Save, directly breaking the documented HOOP_CONFIG
login flow.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
NewPath returns the override path without creating the file. Load errors when the read fails.
Login only continues on ErrEmpty, so Save, which would create the file, never runs.

common/clientconfig/clientconfig.go[86-100] client/config/config.go[97-104] client/cmd/login.go[81-90] client/config/config.go[224-226]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A missing HOOP_CONFIG file aborts Load, so login can't bootstrap it.

## Fix Focus Areas
- client/config/config.go[101-104]

## Recommended Fix
If `os.ReadFile` fails with `os.ErrNotExist`, treat `data` as empty and keep going: localhost probing, then `return &Config{filepath: filepath}, ErrEmpty`. Login and create can then write the file through `Save`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


17. Typo in current locks out all profile commands ✓ Resolved
Description
ParseProfileFile returns an error when current names a profile that isn't in the file, so
readProfilePath fails for use-context, get-contexts, delete-context and Load alike. A user
who hand-edits or removes a section can't switch back with hoop config use-context, and the
fallback message in get-contexts (lines 42-44) never runs.
Code

common/clientconfig/context.go[R88-92]

Relevance

●●● Strong

Invalid current references prevent recovery commands from correcting the selected profile.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The validation at the end of ParseProfileFile returns an error from Active(). Every helper
parses the file first through readProfilePath, so UseProfile never reaches doc.Use(name),
which is the call that would fix current.

common/clientconfig/context.go[88-93] common/clientconfig/context.go[266-280] client/cmd/config/context.go[22-45]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
An invalid `current` value makes the file unparseable, so it can't be repaired with use-context.

## Fix Focus Areas
- common/clientconfig/context.go[88-93]

## Recommended Fix
Remove the `Active()` check from `ParseProfileFile`. Leave the error to `Active()`/`activeIndex()` callers such as Load and current-context, so `UseProfile`, `ListProfiles` and `DeleteProfile` still work and can reset `current`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


18. Kubernetes users get an unusable config path ✓ Resolved
Description
NewPath returns $HOME/.hoop as HoopHomeDir for an override without creating that directory.
When an existing HOOP_CONFIG is used on a machine without .hoop, Kubernetes connect ignores the
failed kubeconfig write and prints a path that does not exist.
Code

common/clientconfig/clientconfig.go[R95-98]

Relevance

●● Moderate

Potentially valid downstream failure, but creating $HOME/.hoop conflicts with the PR’s explicit
override-path behavior.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The override branch constructs and returns the home-directory path without calling newHomeDir.
Both file-loading paths propagate it to Config.HoopHomeDir, while the Kubernetes connection writes
beneath it without creating the parent or checking the write result.

common/clientconfig/clientconfig.go[86-105] common/clientconfig/clientconfig_test.go[56-70] client/config/config.go[112-143] client/cmd/connect.go[424-430]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
With HOOP_CONFIG set, NewPath can return a nonexistent `$HOME/.hoop` directory; Kubernetes connect then prints a kubeconfig path even though writing that file failed.
## Fix Focus Areas
- common/clientconfig/clientconfig.go[95-98]
- client/cmd/connect.go[424-430]
## Recommended Fix
Keep the override from creating its config file, but create the kubeconfig parent directory when needed before writing. Handle write failures instead of printing a path to a file that was not created.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


19. Repeated keys silently change the account ✓ Resolved
Description
ParseProfileFile applies each occurrence of a credential key or top-level current without
detecting duplicates. A file containing two token, api_url, or current assignments loads with
the last value, so an invalid configuration can silently select different credentials or an account
than its first assignment specifies.
Code

common/clientconfig/context.go[R84-85]

Relevance

●● Moderate

Duplicate credentials are a real validation gap, but strict duplicate-key semantics may be an
intentional parser choice.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The parser repeatedly assigns current and forwards profile keys to direct field assignments
without any seen-key check; client Load now runs this parser on configuration files.

common/clientconfig/context.go[61-85] common/clientconfig/context.go[387-422] client/config/config.go[101-108]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new profile parser silently accepts duplicate keys and uses their last values.
## Fix Focus Areas
- common/clientconfig/context.go[61-85]
- common/clientconfig/context.go[387-422]
## Recommended Fix
Track keys seen in the top-level section and each profile section and return a line-specific error on duplicates, including duplicate current assignments.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



20. Wrapper may run an older release than installed ⊘ Outdated
Description
When versions.toml has no usable active entry, the fallback loop in runtimeBinary keeps
overwriting found, so it ends with the last directory in os.ReadDir order, which is lexical.
Lexical order puts 1.9.0 after 1.10.0, so the wrapper can delegate every command to an older
installed release.
Code

common/cmd/hoop/main.go[R491-500]

Relevance

●●● Strong

Lexical fallback selection can deterministically choose an older installed release.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
os.ReadDir returns entries sorted by filename, and the loop sets found to every usable candidate
without comparing versions.

common/cmd/hoop/main.go[486-504]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The fallback binary selection uses lexical directory order.

## Fix Focus Areas
- common/cmd/hoop/main.go[486-504]

## Recommended Fix
Parse each directory name as semver (for example with golang.org/x/mod/semver) and choose the highest usable version.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


⚠️ Warning | Comment size exceeded, inner finding content trimmed View findings
⚠️ Warning | Comment size exceeded, non finding content trimmed Learn more

qodo-code-review[bot]reviewed4d ago
qodo-code-review[bot]reviewed4d ago
qodo-code-review[bot]4d ago
qodo-code-review[bot]reviewed4d ago
qodo-code-review[bot]4d ago
qodo-code-review[bot]reviewed4d ago
qodo-code-review[bot]4d ago
qodo-code-review[bot]reviewed4d ago
qodo-code-review[bot]4d ago
qodo-code-review[bot]reviewed4d ago
qodo-code-review[bot]4d ago