Skip to content

feat: implicit default catalog and shell completion - #4

Merged
trankimtung merged 18 commits into
mainfrom
feat/default-catalog-and-completion
May 27, 2026
Merged

feat: implicit default catalog and shell completion#4
trankimtung merged 18 commits into
mainfrom
feat/default-catalog-and-completion

Conversation

@trankimtung

@trankimtung trankimtung commented May 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Implicit default catalog (vi-dev/nem-catalog@main) synthesized in-memory when config.yaml has none; opt out with NEM_NO_DEFAULT_CATALOG=1.
  • nem catalog list shows SOURCE column; info prints implicit details with opt-out hint; remove/reorder return actionable error via config.ImplicitDefaultError.
  • Shell completion: ValidArgs + cobra.OnlyValidArgs on activate/deactivate; canonical list moved to shell.Supported() with anti-drift test against embedded scripts/*.sh.
  • ADR-017 documents the implicit-default contract.

Test plan

  • go test ./... -race
  • golangci-lint run
  • go vet ./...
  • Smoke: NEM_HOME=/tmp/x nem catalog list shows one row, SOURCE=implicit
  • Smoke: NEM_HOME=/tmp/x nem catalog info default prints remote/ref + implicit hint
  • Smoke: NEM_HOME=/tmp/x nem catalog remove default errors with NEM_NO_DEFAULT_CATALOG hint
  • Smoke: NEM_HOME=/tmp/x NEM_NO_DEFAULT_CATALOG=1 nem catalog list empty (header only)
  • Smoke: nem __complete activate "" offers bash, zsh, fish
  • Smoke: nem activate junk rejected at args validation
  • No ~/.nem/config.yaml written by any of the above

OnlyValidArgs (added in 0791f21) restricts args[0] to shell.Supported()
exact-case match, so the prior strings.ToLower was both unreachable for
mixed-case input and dead code. Removed along with the strings import.
The EffectiveCatalog wrapper conflated a catalog (a domain entity) with
its resolution provenance (a query-time fact). Replace it with a plain
[]CatalogConfig return from EffectiveCatalogs and a new IsImplicit(cfg,
name) query so callers ask the question they actually have rather than
plumbing a flag through the catalog value.

- internal/config: EffectiveCatalogs now returns []CatalogConfig; add
  IsImplicit helper; remove EffectiveCatalog wrapper type.
- internal/app: drop the .CatalogConfig unwrap when building sources.
- cmd/catalog/list: derive the SOURCE column via config.IsImplicit.
- cmd/catalog/remove: replace the scan-for-Implicit loop with a single
  IsImplicit call.
Only 'remove' previously returned an actionable error when invoked
against the implicit default catalog. 'reorder' and 'info' both fell
through to the generic 'catalog %q not found' path, which gives the user
no hint that the catalog exists at runtime but isn't stored in
config.yaml.

- Add an unexported implicitDefaultError(verb, name) helper in cmd.go
  and route remove/reorder/info through it (DRY).
- Extract reorderCatalogs() so the implicit-default check can run before
  config.ReorderCatalogs sees the request.
- info.go now checks IsImplicit before returning 'not found'.
- Tests: reorder_test.go and info_test.go assert the error mentions
  NEM_NO_DEFAULT_CATALOG and the relevant verb.
Move EnvNoDefaultCatalog reads out of EffectiveCatalogs/IsImplicit and
into a one-shot snapshot at Config.Load() time. Both functions are now
pure functions of Config, eliminating os.Getenv calls from their bodies
and removing the empty-vs-unset ambiguity.

- Add unexported Config.noDefaultCatalog field captured at Load().
- Document the contract on EnvNoDefaultCatalog: any non-empty value
  disables; empty string is treated as unset.
- Add isNoDefaultCatalogSet helper alongside the constant.
- Add internal test helper ConfigWithNoDefault for tests that need to
  construct a Config with the snapshot pre-populated without staging
  a config.yaml.
- Drive internal/app/app_test.go opt-out test through config.Load() so
  the snapshot is captured from the env-set test setup.
…derCatalogs

Move the implicit-default guard from the cmd layer into the config-layer
mutators so both checks happen atomically under the file lock, eliminate
the double Load() in cmd/catalog/{remove,reorder}, and unify the
"reject implicit default" contract behind a typed error.

- Add config.ImplicitDefaultError (Name-carrying) plus ErrImplicitDefault
  sentinel for errors.Is callers that don't need the name.
- RemoveCatalog and ReorderCatalogs check IsImplicit inside the Mutate
  callback and return ImplicitDefaultError{Name: n}.
- cmd/catalog/{remove,reorder} drop their explicit config.Load() + check
  and translate via errors.As(err, &config.ImplicitDefaultError{}).
- info.go keeps its in-place check (read-only path, no mutator).
Catalog-name tab-completion previously read cfg.Catalogs directly, so
the implicit default catalog never appeared in completions even when
it was usable everywhere else. Switch to config.EffectiveCatalogs so
completion stays in sync with what the rest of the CLI sees, and fold
the two near-duplicate iteration helpers behind a shared body.

- Add unexported completeFromCatalogs body.
- CompleteCatalogNames and CompleteCatalogNamesFromConfig become thin
  wrappers around it.
- Add tests covering implicit-default inclusion for both wrappers.
Previously 'nem catalog info default' against the implicit default returned
the same actionable error as 'remove'/'reorder'. Since 'info' is purely
read-only and the implicit catalog's remote and ref are well-defined,
refusing was unhelpful. Now shows the same fields as for explicit catalogs
plus a 'Source: implicit' line pointing at the opt-out env var.
@trankimtung trankimtung changed the title Phase 1: implicit default catalog + shell completion feat: implicit default catalog and shell completion May 27, 2026
@trankimtung
trankimtung merged commit 7bc6a1c into main May 27, 2026
13 checks passed
@trankimtung
trankimtung deleted the feat/default-catalog-and-completion branch May 27, 2026 06:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant