Skip to content

refactor(cli): colocate display metadata with commands#3098

Merged
cv merged 105 commits into
mainfrom
refactor/command-display-metadata
May 6, 2026
Merged

refactor(cli): colocate display metadata with commands#3098
cv merged 105 commits into
mainfrom
refactor/command-display-metadata

Conversation

@cv

@cv cv commented May 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Moves root-help/display metadata out of the central command registry array and onto the oclif command entrypoints discovered under src/commands/**. command-registry.ts now projects oclif command-class metadata into the legacy CommandDef shape so command discovery and display metadata continue converging on oclif as the source of truth.

Changes

  • Add withCommandDisplay(...) metadata helper and attach display entries to public/hidden oclif command entrypoints.
  • Derive COMMANDS, grouped help entries, and legacy command token helpers from oclif-discovered display metadata.
  • Extend oclif metadata loading to expose command display metadata.
  • Update tests to assert display entries come from command-class metadata instead of source-string checks.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Verification

  • npx prek run --all-files passes
  • npm test passes
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • make docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Carlos Villela cvillela@nvidia.com

Summary by CodeRabbit

  • New Features

    • CLI now exposes rich, structured command metadata (usage, descriptions, flags, grouping, scope, order), including deprecated aliases and hidden help entries for clearer help output and consistent display.
  • Refactor

    • Command modules now uniformly surface display metadata and command registration is dynamically built from that metadata for consistent help/registry generation.
  • Tests

    • Tests updated to validate command registry and CLI metadata integration, ensuring help/registry accuracy.

cv added 30 commits May 2, 2026 13:36
@cv cv self-assigned this May 6, 2026
@copy-pr-bot

copy-pr-bot Bot commented May 6, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: e74faef0-96ec-4626-b457-4c1068d45834

📥 Commits

Reviewing files that changed from the base of the PR and between ce754b2 and a2542aa.

📒 Files selected for processing (4)
  • src/commands/gc.ts
  • src/commands/sandbox/config/set.ts
  • src/commands/sandbox/recover.ts
  • src/commands/upgrade-sandboxes.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/commands/upgrade-sandboxes.ts

📝 Walkthrough

Walkthrough

Refactors many command entry modules to wrap command classes with a new withCommandDisplay helper, introduces typed command display metadata, extends oclif metadata to carry display entries, and replaces the static command registry with a dynamic registry derived from oclif command-class metadata. Tests updated to validate the registry and dispatch.

Changes

Command Display Infrastructure & Dynamic Registry

Layer / File(s) Summary
Data Shape / Types
src/lib/cli/command-display.ts
Adds CommandGroup, CommandDisplayEntry, CommandDisplayClass<T> and withCommandDisplay<T>(commandClass, display) to attach readonly display metadata to command classes.
Oclif Metadata Extension
src/lib/cli/oclif-metadata.ts
Extends OclifCommandMetadata with optional display?: readonly CommandDisplayEntry[]; adds getRegisteredOclifCommandsMetadata() and updates getRegisteredOclifCommandMetadata() to use it.
Core Registry
src/lib/command-registry.ts
Replaces the hard-coded COMMANDS list with displayEntriesFromOclifMetadata() that reads registered oclif metadata; CommandDef now extends Omit<CommandDisplayEntry, "order"> and includes commandId; COMMANDS exported from dynamic builder; token extraction logic updated/deduplicated.
Command Wiring
src/commands/**/*.ts
~40+ command entry modules changed: each now imports its implementation and withCommandDisplay, and exports export default withCommandDisplay(Command, [...metadata...]) (examples: src/commands/gc.ts, src/commands/onboard.ts, many src/commands/sandbox/**). Old single-line re-exports removed.
Tests / Verification
src/lib/command-display-metadata.test.ts, test/image-cleanup.test.ts
Adds a test asserting display entries derived from oclif metadata map to COMMANDS; image-cleanup test updated to assert global gc presence in COMMANDS, globalCommandTokens, and that resolveGlobalOclifDispatch dispatches as expected.

Sequence Diagram(s)

sequenceDiagram
  participant CmdFile as Command file (wrapped)
  participant Oclif as oclif loader
  participant Registry as Command registry
  participant Tests as Tests

  CmdFile->>CmdFile: call withCommandDisplay(Command, [display...])
  Oclif->>Registry: getRegisteredOclifCommandsMetadata()
  Registry->>Registry: displayEntriesFromOclifMetadata() (reads each command.display + commandId)
  Registry->>Tests: export COMMANDS, globalCommandTokens, sandboxActionTokens
  Tests->>Registry: verify COMMANDS entries and resolveGlobalOclifDispatch(...)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I wrapped each command with care,

pinned usages in tidy air,
metadata hops from file to tree,
registry wakes — CLI sings with me.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor(cli): colocate display metadata with commands' directly describes the main architectural change—moving display metadata from a central registry into command entrypoints, which is the core objective of this refactor.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/command-display-metadata

Comment @coderabbitai help to get the list of available commands and usage tips.

@cv cv added NemoClaw CLI refactor PR restructures code without intended behavior change labels May 6, 2026
@cv

cv commented May 6, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cv

cv commented May 6, 2026

Copy link
Copy Markdown
Collaborator Author

Automated PR review summary

Reviewed PR #3098: refactor(cli): colocate display metadata with commands

Recommendation

  • Recommendation: PASS
  • Highest observed severity: low
  • Block merge: no
  • Why: The highest-risk behavior introduced by this refactor was user-visible CLI/help regression from moving metadata off the central registry and onto discovered oclif entrypoints. Targeted black-box probes showed the installed CLI still renders grouped help correctly, keeps hidden aliases hidden while preserving their execution, projects scope-sensitive usage for migrated commands, and resolves a sandbox-scoped command end-to-end against the real sandbox.
  • Reviewer summary: Reviewed PR refactor(cli): colocate display metadata with commands #3098 with PR-specific runtime probes against the installed nemoclaw CLI and the existing OpenShell sandbox. The migrated display metadata continued to drive root help, hidden aliases, command-specific help, and sandbox-scoped command routing correctly in the tested environment.

Installation and setup findings

  • Install succeeded from /workspace/nemoclaw using install.sh with local-source overrides. Verification succeeded: nemoclaw CLI was installed, onboarding created sandbox nemoclaw-install-verify, SSH into that existing sandbox worked, and an in-sandbox OpenClaw query returned 4. The long installer run itself timed out while still applying late policy presets, but core install/onboarding/provider functionality was already proven working.

What was validated

  • The PR revision was checked out in an isolated review environment.
  • The local checkout was installed using the repository installer flow as closely as the environment allowed.
  • Adversarial, PR-specific probes were then run against the installed environment and relevant repository context.
  • Diff summary:
 .../skills/nemoclaw-contributor-create-pr/SKILL.md |   20 +-
 .../nemoclaw-maintainer-pr-comparator/SKILL.md     |  121 --
 .../checks/tier-0-gates.md                         |   61 -
 .../checks/tier-1-correctness.md                   |   86 --
 .../checks/tier-2-quality.md                       |   64 -
 .../repo-policy.md                                 |   86 --
 .../scripts/check-coderabbit-threads.sh            |  105 --
 .../scripts/collect-gates.sh                       |   84 --
 .../scripts/find-candidates.sh                     |   86 --
 .../scripts/parse-supersession.sh                  |   68 -
 .../scripts/render-verdict.py                      |  232 ----
 .../templates/verdict.md                           |   88 --
 .../tiebreakers.md                                 |   61 -
 .../validation/backtest.md                         |   69 --
 .agents/skills/nemoclaw-skills-guide/SKILL.md      |    2 +-
 .../references/agent-skills.md                     |    4 +-
 .../nemoclaw-user-configure-inference/SKILL.md     |  329 ++---
 .../references/inference-options.md                |    4 +-
 .../references/set-up-sub-agent.md                 |  120 --
 .../references/switc
...[truncated]

Failing tests and unresolved impact

  • No failing adversarial tests were captured.

Passing tests and why they mattered

Passing test 1: Root help still projects command display metadata correctly

  • What was tested: Moving display metadata onto oclif command entrypoints preserves grouped root help output while keeping hidden aliases out of the visible command table.
  • Why it mattered: If false, the CLI’s top-level discoverability regresses and users see missing, duplicated, or leaked hidden commands.
  • Observed result: Root help showed expected groups and migrated entries including onboard, list, connect, policy-add, channels list, debug, backup-all, and gc; hidden aliases help/-h/--help/--version/-v were not listed.
  • Command: nemoclaw --help
  • Recommended follow-up coverage: Add an integration regression test that snapshots root help from the built CLI and explicitly asserts hidden aliases stay omitted.

Passing test 2: Hidden help/version aliases still function after metadata relocation

  • What was tested: Alias commands marked hidden remain callable and resolve to the same help/version behavior even though they no longer come from the central registry array.
  • Why it mattered: If false, users lose shell-compatible aliases or the refactor leaves hidden alias metadata disconnected from actual execution.
  • Observed result: help and -h matched the root help output byte-for-byte; --version and -v both returned nemoclaw v0.0.33-128-g7e9d3c0.
  • Command: nemoclaw help; nemoclaw -h; nemoclaw --version; nemoclaw -v
  • Recommended follow-up coverage: Add an integration regression test covering hidden alias execution and equivalence to canonical root help/version output.

Passing test 3: Command-specific help still uses colocated metadata for global and sandbox commands

  • What was tested: After refactoring, discovered command metadata continues to project correct usage and description text for both global and sandbox-scoped commands.
  • Why it mattered: If false, command help becomes stale or misleading even if the underlying command still executes.
  • Observed result: Observed expected metadata-driven usage lines for global and sandbox commands, including nemoclaw list [--json], nemoclaw debug [--quick|-q] ... [--sandbox NAME], nemoclaw <name> connect [--probe-only], and nemoclaw <name> channels list.
  • Command: nemoclaw list --help; nemoclaw debug --help; nemoclaw nemoclaw-install-verify connect --help; nemoclaw nemoclaw-install-verify channels list --help
  • Recommended follow-up coverage: Add an integration/help-rendering regression test spanning one global and one sandbox command so scope-specific usage strings stay wired to discovered metadata.

Passing test 4: Metadata projection still preserves real sandbox command routing

  • What was tested: The legacy token/projection layer derived from discovered metadata still matches executable sandbox commands, not just displayed help text.
  • Why it mattered: If false, the CLI could advertise sandbox commands that do not actually resolve against a real sandbox target.
  • Observed result: The sandbox-scoped CLI command returned a 49-byte gateway token, and SSH into the real OpenShell sandbox confirmed live workspace access under /sandbox/.openclaw/workspace/, indicating sandbox command routing remained functional.
  • Command: nemoclaw nemoclaw-install-verify gateway-token --quiet; ssh -F /tmp/sshcfg openshell-nemoclaw-install-verify 'test -d /sandbox/.openclaw/workspace'
  • Recommended follow-up coverage: Add an integration regression test that invokes one metadata-migrated sandbox command end-to-end against a real or fixture-backed sandbox target.

Bottom line

  • Based on the install evidence and adversarial probes, this PR looks reasonable to approve.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/sandbox/channels/remove.ts`:
- Around line 9-11: The command metadata for the "channels remove" command has a
misleading description ("Clear credentials and rebuild") that doesn't match the
usage/flags; update the description field in the command object (replace the
value of the description key used alongside usage: "nemoclaw <name> channels
remove" and flags: "<channel> [--dry-run]") to accurately describe the command's
purpose (e.g., "Remove channel credentials" and mention the optional --dry-run
behavior), ensuring the description string aligns with the usage and flags in
the command definition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 80e63c82-c2ce-49fd-b8ab-b0fef87ab0dd

📥 Commits

Reviewing files that changed from the base of the PR and between 9f8c6a3 and 7e9d3c0.

📒 Files selected for processing (50)
  • src/commands/backup-all.ts
  • src/commands/credentials/list.ts
  • src/commands/credentials/reset.ts
  • src/commands/debug.ts
  • src/commands/deploy.ts
  • src/commands/gc.ts
  • src/commands/list.ts
  • src/commands/onboard.ts
  • src/commands/root/help.ts
  • src/commands/root/version.ts
  • src/commands/sandbox/channels/add.ts
  • src/commands/sandbox/channels/list.ts
  • src/commands/sandbox/channels/remove.ts
  • src/commands/sandbox/channels/start.ts
  • src/commands/sandbox/channels/stop.ts
  • src/commands/sandbox/config/get.ts
  • src/commands/sandbox/connect.ts
  • src/commands/sandbox/destroy.ts
  • src/commands/sandbox/doctor.ts
  • src/commands/sandbox/gateway/token.ts
  • src/commands/sandbox/logs.ts
  • src/commands/sandbox/policy/add.ts
  • src/commands/sandbox/policy/list.ts
  • src/commands/sandbox/policy/remove.ts
  • src/commands/sandbox/rebuild.ts
  • src/commands/sandbox/share/mount.ts
  • src/commands/sandbox/share/status.ts
  • src/commands/sandbox/share/unmount.ts
  • src/commands/sandbox/shields/down.ts
  • src/commands/sandbox/shields/status.ts
  • src/commands/sandbox/shields/up.ts
  • src/commands/sandbox/skill/install.ts
  • src/commands/sandbox/snapshot/create.ts
  • src/commands/sandbox/snapshot/list.ts
  • src/commands/sandbox/snapshot/restore.ts
  • src/commands/sandbox/status.ts
  • src/commands/setup-spark.ts
  • src/commands/setup.ts
  • src/commands/start.ts
  • src/commands/status.ts
  • src/commands/stop.ts
  • src/commands/tunnel/start.ts
  • src/commands/tunnel/stop.ts
  • src/commands/uninstall.ts
  • src/commands/upgrade-sandboxes.ts
  • src/lib/cli/command-display.ts
  • src/lib/cli/oclif-metadata.ts
  • src/lib/command-display-metadata.test.ts
  • src/lib/command-registry.ts
  • test/image-cleanup.test.ts

Comment thread src/commands/sandbox/channels/remove.ts
@cv cv marked this pull request as ready for review May 6, 2026 20:08
@cv cv changed the base branch from refactor/oclif-discovered-metadata to main May 6, 2026 20:08
@cv cv enabled auto-merge (squash) May 6, 2026 20:08

@cjagwani cjagwani left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

metadata moves are 1:1 — verified 3 random commands (debug, gc, sandbox:connect) preserve usage/description/flags/group/scope exactly. image-cleanup test now exercises real dispatch instead of source strings — that's a strengthening, not a regression.

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@cv cv requested a review from prekshivyas May 6, 2026 20:10
@cv cv disabled auto-merge May 6, 2026 20:13

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/lib/cli/oclif-metadata.ts (1)

107-114: ⚡ Quick win

Avoid exposing a mutable registry reference from the public API.

On Line 108, returning the loader result directly can leak a shared object (especially from the compatibility require path). External mutation of the returned map can affect later calls.

♻️ Suggested hardening
-export function getRegisteredOclifCommandsMetadata(): Record<string, OclifCommandMetadata> {
-  return loadOclifCommands() ?? {};
+export function getRegisteredOclifCommandsMetadata(): Readonly<
+  Record<string, OclifCommandMetadata>
+> {
+  const commands = loadOclifCommands() ?? {};
+  return { ...commands };
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/cli/oclif-metadata.ts` around lines 107 - 114,
getRegisteredOclifCommandsMetadata currently returns the raw object from
loadOclifCommands(), exposing a mutable shared registry; make the function
return a defensive copy (e.g., shallow clone or frozen copy) of the map so
callers cannot mutate the internal registry, and update
getRegisteredOclifCommandMetadata to read from that safe copy (call
getRegisteredOclifCommandsMetadata and then lookup the commandId) to avoid
leaking the original object; reference functions:
getRegisteredOclifCommandsMetadata, getRegisteredOclifCommandMetadata, and
loadOclifCommands.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/lib/cli/oclif-metadata.ts`:
- Around line 107-114: getRegisteredOclifCommandsMetadata currently returns the
raw object from loadOclifCommands(), exposing a mutable shared registry; make
the function return a defensive copy (e.g., shallow clone or frozen copy) of the
map so callers cannot mutate the internal registry, and update
getRegisteredOclifCommandMetadata to read from that safe copy (call
getRegisteredOclifCommandsMetadata and then lookup the commandId) to avoid
leaking the original object; reference functions:
getRegisteredOclifCommandsMetadata, getRegisteredOclifCommandMetadata, and
loadOclifCommands.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 69ebf76a-25e7-4680-8841-013a3f6dff3d

📥 Commits

Reviewing files that changed from the base of the PR and between 7e9d3c0 and ce754b2.

📒 Files selected for processing (2)
  • src/commands/sandbox/channels/remove.ts
  • src/lib/cli/oclif-metadata.ts

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@cv cv enabled auto-merge (squash) May 6, 2026 20:17
@cv cv merged commit 3477ab7 into main May 6, 2026
16 checks passed

@prekshivyas prekshivyas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid colocation refactor. Display metadata migrates from a 477-line central array in command-registry.ts onto each oclif command entrypoint via a withCommandDisplay(Command, [...]) decorator at src/lib/cli/command-display.ts:47. The legacy CommandDef/COMMANDS API is preserved by deriving it from oclif metadata: displayEntriesFromOclifMetadata() flattens every command's display[], sorts by order, and strips the order field to produce the historical shape. CommandGroup is re-exported for back-compat.

Multi-entry commands distribute correctly: onboard.ts has 2 entries (onboard, onboard --from), root/help.ts has 3 (help, --help, -h), root/version.ts has 2 (--version, -v), sandbox/config/set.ts has 2 (config set, config rotate-token).

Initial CI failure was a real catch — command-registry.test.ts count assertions (52→49, 29→26, 41→40, 11→9, 18→17) flagged that sandbox/config/set.ts and sandbox/recover.ts hadn't been decorated. Carlos pushed a fix-up adding those, and all previously-failing checks now pass: checks (3m30s), macos-e2e (2m34s), build-sandbox-images-arm64 (4m39s), test-e2e-ollama-proxy. wsl-e2e / build-sandbox-images / current self-hosted run still in progress at approval time.

test/image-cleanup.test.ts (+7/-8) is the only file outside src/commands/** and src/lib/** and improves test quality — replaces source-string-grep assertions (registrySrc.toContain('\"nemoclaw gc\"')) with API-level assertions (COMMANDS.toContainEqual(...), resolveGlobalOclifDispatch(\"gc\", []).toEqual({...})). Necessary for the architectural change since display entries no longer live in command-registry.ts source.

New round-trip test in command-display-metadata.test.ts (+15) asserts getRegisteredOclifCommandsMetadata() flattened display entries equal COMMANDS in length and content — proves per-command decorations and the central derived view stay in sync.

Observations (non-blocking):

  1. order: number is hand-managed (onboard 0/1, config/set 29/30, root/help 44/45/46). Inserting a new command mid-group needs renumbering. Modest maintenance cost; a priority-style scheme would scale better but matches the explicit GROUP_ORDER array elsewhere.
  2. The count assertion in command-registry.test.ts is doing real work — caught the missing-decoration bug before review. Valuable invariant going forward.

@cv cv deleted the refactor/command-display-metadata branch May 27, 2026 21:17
@wscurran wscurran added area: cli Command line interface, flags, terminal UX, or output and removed NemoClaw CLI labels Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command line interface, flags, terminal UX, or output refactor PR restructures code without intended behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants