IMPORTANT
Every PR MUST have exactly one of these labels, the merge is blocked otherwise:
major/minor/patchpublishes a new release on merge with the corresponding semver bump.skip-releasemerges 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 oneconfig.toml, includingcurrent. - Add
hoop config get-contexts,current-context,use-context,set-context, anddelete-context.get-contextsaliases arecontextsandls.use-contextalias isuse. - Update only the active profile on save. Refuse to delete the active profile.
- Honor
HOOP_CONFIGwithout creating that file when it is missing. - Document the commands in
client/cmd/config/README.mdand inhoop confighelp.
🧪 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
20PR 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.go
Register 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.context.go
Implement 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.config.go
Load 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.clientconfig.go
Resolve 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.context.go
Add 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.
Tests (3)
+532 / -0-
context_test.go
Verify context command registration+28/-0
Verify context command registration
• Checks that all five context commands are registered and that the use alias resolves correctly.clientconfig_test.go
Test 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.context_accounts_test.go
Exercise 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.
Documentation (1)
+52 / -0-
README.md
Document 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.
-
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
●●● StrongAccepted precedent requires checking writer results; this is a deterministic I/O error-handling fix.
ⓘ 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 themcommon/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
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
●●● StrongDeterministic 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
●●● StrongUnchecked 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 themcommon/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
●●● StrongIgnoring 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
●● ModerateShared 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.
Relevance
●●● StrongClear 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
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
●●● StrongQuoted 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
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
●●● StrongValid 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
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
●●● StrongDeterministic 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
●●● StrongRewriting 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
●●● StrongActive 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
●●● StrongStrict 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.
Relevance
●●● StrongA 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.
Relevance
●●● StrongWithout 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.
Relevance
●●● StrongUsing 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.
Relevance
●●● StrongMissing 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.
Relevance
●●● StrongInvalid 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.
Relevance
●● ModeratePotentially 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.
Relevance
●● ModerateDuplicate 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.
Relevance
●●● StrongLexical 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.
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 moreCode review by qodo was updated up to the latest commit https://github.com/hoophq/hoop/commit/685869ec8ad17fae1220de8837481e314e4d7339
Code review by qodo was updated up to the latest commit https://github.com/hoophq/hoop/commit/631d86ddd72c1cb0ab439747fce4d95233c957fc
Code review by qodo was updated up to the latest commit https://github.com/hoophq/hoop/commit/e1183655d2ebaeacea871f2a8a6452a284eaec91
Code review by qodo was updated up to the latest commit https://github.com/hoophq/hoop/commit/62497e0616afd49407cb7d6bc482c4e1c8691157
Code review by qodo was updated up to the latest commit https://github.com/hoophq/hoop/commit/62497e0616afd49407cb7d6bc482c4e1c8691157
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.