You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Migrate the NemoClaw CLI from the current hand-rolled switch/case dispatcher (bin/nemoclaw.js, 515 lines) to oclif (Open CLI Framework) with TypeScript. This is a foundational cleanup that addresses systemic CLI issues — inconsistent help, missing validations, no completions, docs drift, and poor error handling — in one structural change rather than piecemeal fixes.
This wouldn't take precedence over security work, but it should be high priority. — @cv
Motivation
The current CLI has accumulated enough problems that fixing them individually is less efficient than fixing the architecture:
No shell completions — users type everything manually
No per-command --help — most commands have no usage info
Docs drift from implementation — docs say X but the CLI actually uses --Y X Z (per @rubenhagege)
Monolithic dispatcher — 515-line switch/case with inline implementations
No type safety — plain JS with no compile-time checks
oclif solves most of these structurally: auto-generated help, built-in flag validation, shell completions via plugin, one-file-per-command architecture, and TypeScript support.
Note: oclif's @oclif/plugin-autocomplete supports bash, zsh, and PowerShell — not fish. Fish completion will need a lightweight custom generator or a community plugin on top of oclif.
The nemoclaw <sandbox-name> <action> syntax is preserved via a command_not_found hook — no breaking changes to existing workflows or scripts. The new nemoclaw sandbox <action> <name> syntax is also available.
Risks
Onboard wizard complexity — At ~900 lines with deeply nested conditional flows, interactive prompts, process spawning, and polling loops, this is where bugs are most likely during migration. Plan: keep the wizard logic in its own module and have the oclif command class be a thin wrapper.
Interactive prompt fidelity — The custom promptSecret in credentials.js does raw-mode terminal input with careful escape sequence handling. oclif's prompts or inquirer may behave differently. Needs careful testing on macOS and Linux.
Atomic cutover — package.jsonbin field can only point to one dispatcher. There's no way to run both the old and new entry points simultaneously for the same binary name. The switch happens all at once.
stdio: 'inherit' with oclif — Several commands (debug, uninstall, start, stop, setup-spark) delegate to bash scripts via spawnSync(..., { stdio: 'inherit' }). oclif's output handling could interfere. Test early in Phase 1.
TypeScript compilation — Adding a tsc build step. The project already uses tsc for type-checking via jsconfig.json, but now it becomes a required build step for the CLI to run.
package.jsonbin field — Must change from ./bin/nemoclaw.js to oclif's entry point. This is the atomic cutover point.
Test runner — The project uses Vitest with ESM imports. oclif scaffolds mocha/jest by default, but we will keep Vitest to avoid rewriting all existing lib module tests. Only CLI integration tests (cli.test.js) need rewriting for oclif's test patterns.
ESM vs CommonJS — The project is currently CommonJS, but tests use ESM via Vitest. Newer oclif versions support ESM. Decision on module system should be made in Phase 1 scaffolding.
Other listed PRs can be closed individually as each phase lands, or kept open if the non-completion parts of the PR (e.g., backup feature in feat: add sandbox export/import backup commands #472) are still needed.
Definition of done
This issue is complete when:
All commands are migrated to oclif command classes (Phases 1-3)
All lib modules are converted to TypeScript
Old dispatcher (bin/nemoclaw.js) is deleted
nemoclaw <sandbox-name> <action> backward compat works via hook
Auto-generated --help works for all commands
Shell completions work for bash and zsh via @oclif/plugin-autocomplete
All existing tests pass (Vitest)
CI pipeline updated for TypeScript build step
Fish completion and further hardening (per-command examples, stricter validations, etc.) are follow-up PRs.
Out of scope
Security fixes — those continue on their own track with higher priority
New features — this is purely structural migration + TypeScript conversion
The onboard wizard's business logic — it moves to an oclif command class but the flow stays the same
Impact
24 issues + 17 PRs = 41 items fully or partially addressed by this migration.
Summary
Migrate the NemoClaw CLI from the current hand-rolled
switch/casedispatcher (bin/nemoclaw.js, 515 lines) to oclif (Open CLI Framework) with TypeScript. This is a foundational cleanup that addresses systemic CLI issues — inconsistent help, missing validations, no completions, docs drift, and poor error handling — in one structural change rather than piecemeal fixes.Motivation
The current CLI has accumulated enough problems that fixing them individually is less efficient than fixing the architecture:
--help— most commands have no usage infoXbut the CLI actually uses--Y X Z(per @rubenhagege)deploy()Shell Commands #575)oclif solves most of these structurally: auto-generated help, built-in flag validation, shell completions via plugin, one-file-per-command architecture, and TypeScript support.
Note: oclif's
@oclif/plugin-autocompletesupports bash, zsh, and PowerShell — not fish. Fish completion will need a lightweight custom generator or a community plugin on top of oclif.Issues addressed (24)
Completions
@oclif/plugin-autocomplete(bash/zsh/PowerShell; fish requires additional work)Help / docs consistency
--helpfor all commands → oclif auto-generates help from command metadataexamplesproperty on each command classoclif readmeauto-generates docs from codeFlag / parameter validation
deploy()Shell Commands #575 — Unvalidated instance name in deploy (shell injection risk) → oclif'sargswithparsefunctions--timeoutflags is trivial in oclif--portflags with validationinithook for preflight checksCLI structure / architecture
@oclif/plugin-update--jsonoutput for list/status → oclif'senableJsonFlagandthis.logJson()--verbose/--debugglobal flag → oclif base flags inherited by all commandsprerun/postrunhookssessions resetsubcommand → topic/command structureError handling
catch()on Command base classPRs that would be superseded or simplified (17)
oclif readme--jsonto list commandenableJsonFlag+this.logJson()argswithparse--typeflag for brev createdefaultValuein promptOrDefault interactive mode (Fixes #360)@oclif/plugin-updatecatch()for global redactioncatch()/finally()lifecycleMigration plan
Phase 1 — Scaffold + simple commands (1-2 days)
list,status,start,stop,setup,setup-spark,debug,uninstallPhase 2 — Sandbox commands + deploy (1-2 days)
sandbox/topiccommand_not_foundhook sonemoclaw <sandbox-name> <action>still works (zero UX breakage)deployprocess.exit()calls in lib modules with thrown errors (~15-20 call sites)Phase 3 — Onboard wizard (2-3 days)
readline/promptSecretwith oclif-compatible prompts--non-interactivemode still worksPhase 4 — Cleanup, tests, hardening (1-2 days)
bin/nemoclaw.js)--jsonoutputEstimated total: 5-9 days of focused work, ~30-35 files touched.
Proposed structure
UX compatibility
The
nemoclaw <sandbox-name> <action>syntax is preserved via acommand_not_foundhook — no breaking changes to existing workflows or scripts. The newnemoclaw sandbox <action> <name>syntax is also available.Risks
promptSecretincredentials.jsdoes raw-mode terminal input with careful escape sequence handling. oclif's prompts orinquirermay behave differently. Needs careful testing on macOS and Linux.package.jsonbinfield can only point to one dispatcher. There's no way to run both the old and new entry points simultaneously for the same binary name. The switch happens all at once.stdio: 'inherit'with oclif — Several commands (debug,uninstall,start,stop,setup-spark) delegate to bash scripts viaspawnSync(..., { stdio: 'inherit' }). oclif's output handling could interfere. Test early in Phase 1.Build & tooling changes
tscbuild step. The project already usestscfor type-checking viajsconfig.json, but now it becomes a required build step for the CLI to run.package.jsonbinfield — Must change from./bin/nemoclaw.jsto oclif's entry point. This is the atomic cutover point.cli.test.js) need rewriting for oclif's test patterns.PR disposition
Definition of done
This issue is complete when:
bin/nemoclaw.js) is deletednemoclaw <sandbox-name> <action>backward compat works via hook--helpworks for all commands@oclif/plugin-autocompleteFish completion and further hardening (per-command examples, stricter validations, etc.) are follow-up PRs.
Out of scope
onboardwizard's business logic — it moves to an oclif command class but the flow stays the sameImpact
24 issues + 17 PRs = 41 items fully or partially addressed by this migration.
/cc @cv @rubenhagege