Skip to content

fix(cli): respect defaultValue in promptOrDefault interactive mode (Fixes #360)#631

Open
craigamcw wants to merge 1 commit intoNVIDIA:mainfrom
craigamcw:fix/prompt-or-default-fallback
Open

fix(cli): respect defaultValue in promptOrDefault interactive mode (Fixes #360)#631
craigamcw wants to merge 1 commit intoNVIDIA:mainfrom
craigamcw:fix/prompt-or-default-fallback

Conversation

@craigamcw
Copy link

@craigamcw craigamcw commented Mar 22, 2026

Implemented feature with help from Claude Code

promptOrDefault discarded defaultValue in interactive mode — the raw prompt() return was used directly, so pressing Enter or entering whitespace yielded an empty string instead of the default. The redundant || "my-assistant" fallback in createSandbox masked the problem only for that one call site.

Summary

Fix promptOrDefault to respect defaultValue in interactive mode. Previously, pressing Enter or entering whitespace returned an empty string instead of the default, because the raw prompt() return was used directly. The redundant || "my-assistant" fallback in createSandbox masked the bug for that one call site only.

Related Issue

Fixes #360

Changes

  • Capture and trim the interactive answer in promptOrDefault, falling back to defaultValue when the result is empty.
  • Remove the redundant || "my-assistant" fallback in createSandbox (now handled upstream in promptOrDefault).
  • Export promptOrDefault and _setNonInteractiveForTest for testability.
  • Add 7 tests covering custom names, empty/whitespace fallback, trim preservation, and RFC 1123 validation.

Type of Change

  • Code change for a new feature, bug fix, or refactor.
  • Code change with doc updates.
  • Doc only. Prose changes without code sample modifications.
  • Doc only. Includes code sample changes.

Testing

  • npm test passes (174/174, 7 new tests added).
  • make check passes — 4 pre-existing TypeScript lint errors in unrelated files (fetch.ts, logs.ts,
    status.test.ts); identical on main branch, not introduced by this PR.
  • make docs builds without warnings. (for doc-only changes)

Checklist

General

Code Changes

  • make format applied (TypeScript and Python).
  • Tests added or updated for new or changed behavior.
  • No secrets, API keys, or credentials committed.
  • Doc pages updated for any user-facing behavior changes (new commands, changed defaults, new features, bug fixes that contradict existing docs).

Doc Changes

  • Follows the style guide. Try running the update-docs agent skill to draft changes while complying with the style guide. For example, prompt your agent with "/update-docs catch up the docs for the new changes I made in this PR."
  • New pages include SPDX license header and frontmatter, if creating a new page.
  • Cross-references and links verified.

Summary by CodeRabbit

  • Bug Fixes

    • Improved onboarding input handling: environment-sourced values and typed answers are trimmed; empty/whitespace responses now fall back to defaults and sandbox names are derived more consistently.
  • Tests

    • Expanded coverage for prompt behavior, non-interactive env-var handling, and name-format validation (RFC-style rules).
  • Chores

    • Added test-only controls to simulate prompting and toggle interactive mode for more reliable automated testing.

@coderabbitai
Copy link

coderabbitai bot commented Mar 22, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Prompt handling in onboarding was changed: non-interactive env values are read raw and trimmed (empty/whitespace treated as unset), interactive prompts route through an overrideable prompt, sandbox name is computed from the trimmed answer (no hardcoded "my-assistant" fallback), and test hooks plus promptOrDefault were exported.

Changes

Cohort / File(s) Summary
Onboarding Module
bin/lib/onboard.js
Added and exported promptOrDefault(question, envVar, defaultValue) which reads env var raw, trims values, treats whitespace-only as unset, uses an overrideable prompt for interactive input, trims/normalizes answers, computes sandboxName from the trimmed answer (removed internal "my-assistant" fallback), and added test hooks _setNonInteractiveForTest(value) and _setPromptForTest(fn) with runtime validation.
Tests
test/onboard.test.js
Imported promptOrDefault, _setNonInteractiveForTest, _setPromptForTest; added non-interactive and interactive suites to validate env-var vs default behavior, trimming, handling of empty/null inputs, hyphen preservation, and test setup/teardown for env and prompt overrides.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I trim the lines and nudge defaults on cue,
No phantom "my-assistant" — just lowercase true.
Tests hop in, mocking prompts both old and new,
Hooks for test-time mischief, tidy and few.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR directly addresses item #6 and #7 from issue #360 by fixing the sandbox name prompt to correctly accept defaults and custom names through the promptOrDefault refactoring.
Out of Scope Changes check ✅ Passed All changes are scoped to promptOrDefault functionality and its tests; no out-of-scope modifications to unrelated features or systems are present.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title accurately summarizes the main fix: it addresses the bug where promptOrDefault failed to respect defaultValue in interactive mode, which is the primary change evident in the code modifications.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
bin/lib/onboard.js (1)

968-969: Coerce test toggle input to boolean.

Line 969 currently accepts any value; passing "false" would still enable non-interactive mode. Coercing avoids accidental test flakiness.

💡 Proposed fix
-function _setNonInteractiveForTest(value) { NON_INTERACTIVE = value; }
+function _setNonInteractiveForTest(value) { NON_INTERACTIVE = Boolean(value); }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/lib/onboard.js` around lines 968 - 969, The _setNonInteractiveForTest
helper currently assigns the input directly to NON_INTERACTIVE which treats
truthy strings like "false" as true; change _setNonInteractiveForTest to coerce
the incoming value to a boolean (e.g., use Boolean(value) or !!value) before
assigning to NON_INTERACTIVE so only true-like values enable non-interactive
mode; update the function named _setNonInteractiveForTest and ensure references
to NON_INTERACTIVE remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/onboard.test.js`:
- Around line 35-85: The tests never exercise the interactive branch of
promptOrDefault; add a test seam so promptOrDefault can be invoked with an
injectable prompt function (or expose a setter like setPromptForTest) and then
write tests that disable non-interactive mode via
_setNonInteractiveForTest(false) and call promptOrDefault using the injected
prompt to simulate responses "", "   ", and "  custom  " asserting that
empty/blank inputs fall back to default and trimmed inputs return the trimmed
custom value; update the existing test suite to restore non-interactive mode and
remove the injected prompt after each test to avoid cross-test leakage.
- Around line 40-43: In the after hook replace the unconditional delete of
process.env.TEST_PROMPT_CUSTOM with logic that restores whatever value existed
before the test: save the original value (e.g., in a local variable at top of
the test file or in the before hook), call _setNonInteractiveForTest(false) as
you do now, and then if the saved original is undefined delete
process.env.TEST_PROMPT_CUSTOM else set process.env.TEST_PROMPT_CUSTOM back to
the saved value so pre-existing environment state is preserved.

---

Nitpick comments:
In `@bin/lib/onboard.js`:
- Around line 968-969: The _setNonInteractiveForTest helper currently assigns
the input directly to NON_INTERACTIVE which treats truthy strings like "false"
as true; change _setNonInteractiveForTest to coerce the incoming value to a
boolean (e.g., use Boolean(value) or !!value) before assigning to
NON_INTERACTIVE so only true-like values enable non-interactive mode; update the
function named _setNonInteractiveForTest and ensure references to
NON_INTERACTIVE remain unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 46e3e00d-1f57-48a8-bb17-81bdda1f307c

📥 Commits

Reviewing files that changed from the base of the PR and between 04012f7 and 4044f73.

📒 Files selected for processing (2)
  • bin/lib/onboard.js
  • test/onboard.test.js

@craigamcw craigamcw force-pushed the fix/prompt-or-default-fallback branch 3 times, most recently from ab85d17 to 7b826ab Compare March 22, 2026 11:03
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
bin/lib/onboard.js (2)

55-64: Normalize non-interactive prompt values consistently with interactive mode.

promptOrDefault() now trims/falls back in interactive mode, but non-interactive mode still uses raw env values. A whitespace-only env var can bypass defaulting and later fail sandbox-name validation. Consider applying the same trim() + empty => default rule in both branches.

Proposed diff
 async function promptOrDefault(question, envVar, defaultValue) {
   if (isNonInteractive()) {
-    const val = envVar ? process.env[envVar] : null;
-    const result = val || defaultValue;
+    const val = envVar ? process.env[envVar] : "";
+    const normalized = String(val || "").trim();
+    const result = normalized || defaultValue;
     note(`  [non-interactive] ${question.trim()} → ${result}`);
     return result;
   }
   const promptFn = _promptOverride || prompt;
   const answer = (await promptFn(question) || "").trim();
   return answer || defaultValue;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/lib/onboard.js` around lines 55 - 64, The non-interactive branch of
promptOrDefault uses raw env values and can return whitespace-only strings;
update it to mirror the interactive branch by reading process.env[envVar],
trimming the value, and treating empty/whitespace as absent so it falls back to
defaultValue. In practice, inside promptOrDefault's isNonInteractive() branch
(the code that reads process.env[envVar] into val/result), call .trim() on the
env value, then use the trimmed value || defaultValue and log the trimmed
result, ensuring the same behavior as the interactive path.

1060-1061: Optionally harden _setPromptForTest input.

A non-function truthy value will cause a runtime error when promptFn(question) is called. A small type guard would make test failures clearer.

Proposed diff
 let _promptOverride = null;
-function _setPromptForTest(fn) { _promptOverride = fn; }
+function _setPromptForTest(fn) {
+  if (fn !== null && typeof fn !== "function") {
+    throw new TypeError("_setPromptForTest expects a function or null");
+  }
+  _promptOverride = fn;
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/lib/onboard.js` around lines 1060 - 1061, The test helper
_setPromptForTest currently assigns any truthy value to _promptOverride which
will later cause a runtime error when promptFn(question) is invoked; update the
function _setPromptForTest to validate its input (accept only a function or
null/undefined) and either coerce non-function falsy values to null or throw a
clear TypeError for invalid types, e.g. check typeof fn === 'function' before
assigning to _promptOverride and reject other truthy non-function values so
failures are explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@bin/lib/onboard.js`:
- Around line 55-64: The non-interactive branch of promptOrDefault uses raw env
values and can return whitespace-only strings; update it to mirror the
interactive branch by reading process.env[envVar], trimming the value, and
treating empty/whitespace as absent so it falls back to defaultValue. In
practice, inside promptOrDefault's isNonInteractive() branch (the code that
reads process.env[envVar] into val/result), call .trim() on the env value, then
use the trimmed value || defaultValue and log the trimmed result, ensuring the
same behavior as the interactive path.
- Around line 1060-1061: The test helper _setPromptForTest currently assigns any
truthy value to _promptOverride which will later cause a runtime error when
promptFn(question) is invoked; update the function _setPromptForTest to validate
its input (accept only a function or null/undefined) and either coerce
non-function falsy values to null or throw a clear TypeError for invalid types,
e.g. check typeof fn === 'function' before assigning to _promptOverride and
reject other truthy non-function values so failures are explicit.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8ba54f1e-2ddf-4db5-820d-89edaa615ac3

📥 Commits

Reviewing files that changed from the base of the PR and between 8664860 and ab85d17.

📒 Files selected for processing (2)
  • bin/lib/onboard.js
  • test/onboard.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/onboard.test.js

@craigamcw craigamcw force-pushed the fix/prompt-or-default-fallback branch from 7b826ab to c05689e Compare March 22, 2026 11:05
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
bin/lib/onboard.js (1)

483-483: Minor: Redundant .trim() call.

Since promptOrDefault (line 63) already trims the answer before returning, the .trim() here is redundant. The code works correctly as-is, but you could simplify to:

-  const sandboxName = nameAnswer.trim().toLowerCase();
+  const sandboxName = nameAnswer.toLowerCase();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/lib/onboard.js` at line 483, The assignment to sandboxName redundantly
calls .trim() because promptOrDefault already returns a trimmed string; remove
the extra .trim() from the sandboxName initialization (where sandboxName is set
from nameAnswer) so it simply lowercases the returned value, and leave
promptOrDefault (the function that trims input) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@bin/lib/onboard.js`:
- Line 483: The assignment to sandboxName redundantly calls .trim() because
promptOrDefault already returns a trimmed string; remove the extra .trim() from
the sandboxName initialization (where sandboxName is set from nameAnswer) so it
simply lowercases the returned value, and leave promptOrDefault (the function
that trims input) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3c1094ff-5382-490a-8d28-839af303832c

📥 Commits

Reviewing files that changed from the base of the PR and between ab85d17 and 7b826ab.

📒 Files selected for processing (2)
  • bin/lib/onboard.js
  • test/onboard.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/onboard.test.js

Implemented feature with help from Claude Code

promptOrDefault discarded defaultValue in interactive mode — the raw
prompt() return was used directly, so pressing Enter or entering
whitespace yielded an empty string instead of the default. The
redundant `|| "my-assistant"` fallback in createSandbox masked the
problem only for that one call site.

- Capture and trim the interactive answer, falling back to defaultValue
  when empty.
- Remove the redundant fallback in createSandbox (now handled upstream).
- Export promptOrDefault and _setNonInteractiveForTest for testability.
- Add _setPromptForTest to inject a mock prompt for interactive tests.
- Add 7 non-interactive tests covering env var, empty/whitespace
  fallback, trim preservation, and RFC 1123 validation.
- Add 5 interactive tests covering empty input, whitespace-only,
  padded input, clean input, and null prompt return.

Fixes NVIDIA#360

Signed-off-by: Craig <craig@epic28.com>
@craigamcw craigamcw force-pushed the fix/prompt-or-default-fallback branch from c05689e to cfaadff Compare March 22, 2026 11:07
@craigamcw craigamcw changed the title fix(cli): respect defaultValue in promptOrDefault interactive mode fix(cli): respect defaultValue in promptOrDefault interactive mode (Fixes #360) Mar 22, 2026
@craigamcw craigamcw mentioned this pull request Mar 22, 2026
craigamcw pushed a commit to craigamcw/NemoClaw that referenced this pull request Mar 22, 2026
Upstream main migrated from node:test to vitest (NVIDIA#653). Update the
promptOrDefault tests to use vitest globals (expect/toMatch/toBe) and
beforeAll/afterAll instead of node:test assert + before/after. Add root
vitest.config.ts with globals enabled.

https://claude.ai/code/session_01JwUa2BdccTciPkfsSRtBsM
craigamcw pushed a commit to craigamcw/NemoClaw that referenced this pull request Mar 22, 2026
Resolve merge conflict with upstream vitest migration (NVIDIA#653).
Rebase PR NVIDIA#631 onto current upstream main so the test file uses
vitest globals (expect/toMatch/toBe, beforeAll/afterAll) instead
of the removed node:test + node:assert/strict APIs.

Fixes NVIDIA#360

https://claude.ai/code/session_01JwUa2BdccTciPkfsSRtBsM
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.

Install issues

1 participant