Skip to content

feat: add file browser component for PRD file selection#263

Merged
subsy merged 37 commits intomainfrom
feat/file-browser-pr236
Feb 3, 2026
Merged

feat: add file browser component for PRD file selection#263
subsy merged 37 commits intomainfrom
feat/file-browser-pr236

Conversation

@subsy
Copy link
Copy Markdown
Owner

@subsy subsy commented Feb 3, 2026

Summary

This PR incorporates the work from PR #236 by @esumerfd with fixes and updates merged from main.

Replace manual text input with a keyboard-navigable file browser when selecting prd.json files.

Features (from #236)

  • Directory navigation with j/k, Enter, Backspace, ~, and arrow keys
  • File filtering to show only prd*.json files
  • Editable path field (press / to type a path directly)
  • Hidden files toggle (press .)
  • Visual hint showing typical prd.json location
  • Auto-scroll when selection moves out of visible area

Files Changed

  • src/tui/components/FileBrowser.tsx - New file browser component
  • src/tui/components/EpicLoaderOverlay.tsx - Integrate file browser
  • src/tui/components/index.ts - Export new component
  • src/utils/files.ts - File utilities

Additional Fixes

  • Fixed tests/templates/engine.test.ts:
    • Properly handle undefined HOME env var in afterEach (delete instead of assign)
    • Updated comment to use evergreen wording
  • Fixed tests/commands/info.test.ts:
    • Changed from broken ?test-reload query string approach to direct imports with singleton reset
  • Merged latest main and resolved conflict in src/tui/components/index.ts

Test Status

  • 2674 passing tests
  • 25 failing tests (test isolation issues - fewer than main's 53 failures)

Closes #236

Summary by CodeRabbit

  • New Features

    • Keyboard‑navigable File Browser added to the TUI.
    • Directory listing utility for filtered, sorted listings.
  • Refactor

    • Epic loader refactored to use mode‑specific props and delegate file prompts to the File Browser.
    • Run flow adjusted to unify list vs file‑prompt handling.
  • Bug Fixes

    • Improved epic loading error handling and consistent loader closing.
  • Tests

    • Large-scale test rework: lifecycle-scoped mocks, dynamic module reloads and many new/updated suites.
  • Documentation

    • Example JSON updated: root field renamed from "project" to "name".

esumerfield and others added 30 commits January 28, 2026 13:24
Replace manual text input with a keyboard-navigable file browser when
selecting prd.json files. Features include:

- Directory navigation with j/k, Enter, Backspace, ~, and arrow keys
- File filtering to show only prd*.json files
- Editable path field (press / to type a path directly)
- Hidden files toggle (press .)
- Visual hint showing typical prd.json location
- Auto-scroll to keep selection visible in viewport

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The example prd.json output was using "project" instead of "name",
causing Claude to sometimes generate invalid JSON that failed
ralph-tui validation with "Missing required field: name".

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix formatPath to check path separator boundary, preventing false ~
  substitution on sibling directories (e.g., /home/alice2)
- Fix setEditedPath to only accept printable characters, preventing
  control/escape sequences from being inserted
- Add filePatternHint prop to FileBrowser for configurable hint text
- Refactor EpicLoaderOverlayProps to use discriminated union type for
  type-safe mode handling
- Update RunApp.tsx to use conditional rendering for proper type narrowing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Resolve conflict in src/tui/components/index.ts (keep both FileBrowser and parallel components)
- Fix info.test.ts to use direct imports instead of query string hack
- Fix engine.test.ts to handle undefined HOME correctly and use evergreen comments
@vercel
Copy link
Copy Markdown

vercel bot commented Feb 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
ralph-tui Ignored Ignored Preview Feb 3, 2026 5:26pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 3, 2026

Caution

Review failed

The pull request is closed.

Walkthrough

Adds a keyboard-driven FileBrowser and listDirectory utility; integrates FileBrowser into EpicLoaderOverlay (file-prompt mode) and updates RunApp error handling. Refactors EpicLoaderOverlay props to a discriminated union. Large-scale tests moved mocks into lifecycle hooks and use dynamic imports. Updates SKILL.md example field projectname.

Changes

Cohort / File(s) Summary
Documentation
skills/ralph-tui-create-json/SKILL.md
Updated example prd.json: renamed root field projectname.
File utilities
src/utils/files.ts, tests/utils/files.test.ts
Added DirectoryEntry type and listDirectory() with hidden/extension/prefix filters and directory-first sorting; unit tests added.
File browser component
src/tui/components/FileBrowser.tsx, src/tui/components/index.ts, tests/tui/file-browser.test.ts
New keyboard-driven FileBrowser (navigation, path editing, filters, hidden toggle, auto-scroll) with helpers formatPath/truncateText; exported from components barrel and covered by tests.
Epic loader / TUI integration
src/tui/components/EpicLoaderOverlay.tsx, src/tui/components/RunApp.tsx
EpicLoaderOverlay refactored to discriminated-union props (list vs file-prompt); file-prompt delegated to FileBrowser; RunApp adds explicit try/catch and error reporting for both flows.
TUI barrel exports
src/tui/components/index.ts
Exported FileBrowser and FileBrowserProps type.
Tracker readiness guard
src/plugins/trackers/builtin/beads/index.ts
Added await this.isReady() early-return in getNextTask to avoid invoking CLI when not ready.
Test harness & mocking
src/commands/*.test.ts, src/setup/*.test.ts, src/setup/wizard.test.ts, src/setup/skill-installer*.test.ts
Moved mock setup into beforeAll/afterAll, introduced dynamic/fresh imports (?test-reload) and pass-through mock patterns to isolate mocks and avoid cross-test leakage.
Plugin & tracker tests (refactored)
src/plugins/agents/base.test.ts, src/plugins/trackers/builtin/*/index.test.ts, src/plugins/trackers/builtin/beads*.test.ts
Replaced integration-style tests with unit-style/TestAgentPlugin or Bun.spawn patterns, deferred imports until after mocks, added typed lazy declarations.
Sandbox & process tests (self-contained)
tests/sandbox/detect.test.ts, tests/utils/process.test.ts, tests/engine/auto-commit.test.ts
Introduced Bun.spawn-based local helpers (runProcess, git helpers, commandExists) so tests are self-contained and avoid node:child_process mock pollution.
New/updated tests
tests/commands/info.test.ts, tests/plugins/agents/base-execute.test.ts, tests/templates/engine.test.ts, various tests/*
Added new suites and adapted many tests to dynamic import/mocking patterns; some integration tests converted to unit-style.
Misc test import timing adjustments
tests/utils/clipboard.test.ts, other tests/*, src/plugins/trackers/...
Deferred module imports until after mock setup, added afterAll restore hooks, and adopted dynamic import patterns across many tests.
Website typings
website/next-env.d.ts
Adjusted ambient import path from ./.next/dev/types/routes.d.ts./.next/types/routes.d.ts.

Sequence Diagram(s)

sequenceDiagram
  participant User as User (keyboard)
  participant RunApp as RunApp
  participant EpicOverlay as EpicLoaderOverlay
  participant FileBrowser as FileBrowser
  participant FS as Filesystem
  User->>RunApp: open epic loader (file-prompt)
  RunApp->>EpicOverlay: render file-prompt mode
  EpicOverlay->>FileBrowser: mount(initialPath)
  FileBrowser->>FS: listDirectory(currentPath, filters)
  FS-->>FileBrowser: DirectoryEntry[]
  User->>FileBrowser: navigate / edit / select
  FileBrowser->>FS: listDirectory(newPath) / validate path
  FS-->>FileBrowser: DirectoryEntry[] or error
  FileBrowser-->>EpicOverlay: onSelect(selectedFilePath)
  EpicOverlay-->>RunApp: onFilePath(selectedFilePath)
  RunApp->>RunApp: load epic (try/catch -> set error or close)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.78% 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 PR title accurately describes the main feature addition: a new file browser component for selecting PRD files, which aligns with the primary objective and the majority of changes.
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from issue #236: keyboard-navigable FileBrowser component, EpicLoaderOverlay integration, directory utilities, prd.json schema fix, and comprehensive test isolation improvements.
Out of Scope Changes check ✅ Passed All changes are within scope of issue #236. While the PR includes extensive test refactoring, this is explicitly listed as part of the objectives (improving test isolation and reducing failures from 87 to 0).

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/file-browser-pr236

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Improves test coverage for the new listDirectory utility function
used by the FileBrowser component.
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 3, 2026

Codecov Report

❌ Patch coverage is 78.26087% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 43.35%. Comparing base (173cc67) to head (f14652a).
⚠️ Report is 38 commits behind head on main.

Files with missing lines Patch % Lines
src/utils/files.ts 80.48% 8 Missing ⚠️
src/plugins/trackers/builtin/beads/index.ts 60.00% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #263      +/-   ##
==========================================
- Coverage   44.95%   43.35%   -1.61%     
==========================================
  Files          91       91              
  Lines       28124    28281     +157     
==========================================
- Hits        12643    12260     -383     
- Misses      15481    16021     +540     
Files with missing lines Coverage Δ
src/plugins/trackers/builtin/beads/index.ts 59.96% <60.00%> (+0.92%) ⬆️
src/utils/files.ts 78.10% <80.48%> (+0.76%) ⬆️

... and 9 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Export formatPath and truncateText from FileBrowser.tsx
- Add tests for path formatting and text truncation
- Add tests for navigation and path resolution logic
Copy link
Copy Markdown
Contributor

@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: 12

🤖 Fix all issues with AI agents
In `@src/plugins/agents/base.test.ts`:
- Around line 312-319: Update the block comment explaining why tests for
BaseAgentPlugin lifecycle and envExclude live in
tests/plugins/agents/base-execute.test.ts so it is evergreen: remove time‑bound
phrases like “have been moved” and “fail in the full test suite” and instead
state the structural rationale — e.g., that tests requiring real process
execution are placed in base-execute.test.ts to isolate them from other tests
that mock node:child_process and avoid mock pollution — and keep the reference
to the Bun issue link if desired.

In `@src/plugins/trackers/builtin/beads-bv/index.test.ts`:
- Around line 6-8: Update the comment that explains why the mock is applied in
beforeAll to remove temporal phrasing: reword it as a stable invariant stating
that the mock must be configured in beforeAll (rather than at module scope) so
it does not affect other tests, and that the module under test is imported only
after this mock is in place to ensure isolation; reference the existing setup
symbols "beforeAll" and "dynamic import" (or "module under test") so readers
know where the invariant applies.

In `@src/plugins/trackers/builtin/beads.test.ts`:
- Around line 5-7: Reword the setup note to state the invariant: the mock is
configured inside beforeAll and the module under test is imported afterwards to
avoid polluting other tests; update the comment near beforeAll and the dynamic
import of the module under test (the lines describing "mock is set up in
beforeAll" and "module under test is dynamically imported") to remove temporal
phrasing like "after the mock is applied" and replace it with a stable statement
that the test imports the module only once the mock has been established.

In `@src/setup/skill-installer-spawn.test.ts`:
- Around line 5-7: Reword the two temporal test comments so they read as
evergreen invariants: state that mocks are applied in beforeAll to avoid
polluting other tests and that the module under test is dynamically imported
only after the mock has been applied; reference the test setup using the
beforeAll hook and the dynamic import of the module under test (no timing
language like "later" or "afterwards").

In `@src/tui/components/EpicLoaderOverlay.tsx`:
- Around line 180-191: EpicLoaderOverlay drops the passed-in error when mode ===
'file-prompt' because FileBrowser is returned without forwarding the error prop;
update the file-prompt branch to pass the error (and optionally an error display
handler) into <FileBrowser> so the user sees PRD load failures — locate the mode
check in EpicLoaderOverlay (the 'file-prompt' branch that returns FileBrowser)
and add the error prop (and wire it to whatever FileBrowser prop shows errors,
or add a new prop like errorMessage/onError) so failed loads surface in the UI.

In `@src/tui/components/FileBrowser.tsx`:
- Around line 116-123: When the FileBrowser becomes visible the existing
useEffect resets navigation state but not the edit state, so add resetting of
the editing flags/inputs inside that same block: when visible is true call your
edit-state setters (e.g. setIsEditing(false) and clear the edit buffer via
setEditInput('') or setEditValue('')) alongside setCurrentPath,
setSelectedIndex, and setShowHidden so the overlay never reopens with stale edit
input.

In `@tests/engine/auto-commit.test.ts`:
- Around line 110-120: The initGitRepo helper currently calls runProcess
multiple times without verifying success; update initGitRepo to fail fast on any
git command error by checking runProcess results or wrapping each await in
try/catch and rethrowing a descriptive Error that includes which command failed
(e.g., "git init", "git config user.email", "git add", "git commit") and the
underlying error/message; ensure failures from writeFile are also propagated so
tests stop immediately rather than proceeding with a broken repo state.
- Around line 21-108: The test duplicates production logic by defining
runProcess, hasUncommittedChanges, and performAutoCommit locally instead of
importing src/engine/auto-commit.ts; replace the local implementations with an
import from the production module (e.g., import { hasUncommittedChanges,
performAutoCommit } from 'src/engine/auto-commit') and adapt the test to either
run without the node child_process mocks or provide a Bun-specific shim only for
the process spawn (keep a small wrapper named runProcess only if necessary), or
alternatively add a clear TODO and CI note in the test header referencing the
production functions so future changes in
hasUncommittedChanges/performAutoCommit are kept in sync.

In `@tests/plugins/agents/base-execute.test.ts`:
- Around line 1-12: Rewrite the header comment to remove temporal phrasing and
describe the rationale in evergreen language: explain that the test file creates
test plugins that override BaseAgentPlugin.execute() to call Bun.spawn directly
to avoid interference from tests that mock node:child_process, because Bun's
mock.restore() does not reliably restore built-in modules, and note that this
preserves isolation for these tests; replace phrases like "ISOLATION FIX" and
"original tests … fail" with this persistent explanation referencing Bun.spawn
and BaseAgentPlugin.execute.
- Around line 72-163: The async runExecution inside the overridden execute
method can throw (Bun.spawn, stream reads, awaits) and currently never resolves
resolvePromise on error; wrap the entire runExecution body in try/catch/finally,
and in the catch build and call resolvePromise with a failed
AgentExecutionResult (use the existing executionId, status: 'failed', a non-zero
exitCode like 1, include any captured stdout/stderr if available or empty
strings, compute duration using startedAt and new Date(), set interrupted:
false, and set startedAt/endedAt ISO strings), and still invoke
options?.onEnd(result) inside a try/catch so onEnd runs for error cases too;
ensure resolvePromise is always called exactly once even when errors occur.

In `@tests/templates/engine.test.ts`:
- Around line 704-721: The tests currently only override process.env.HOME but
installBuiltinTemplates -> getUserConfigDir uses os.homedir() which on Windows
reads USERPROFILE, so extend the sandbox: in the beforeEach that sets testDir
and originalHome, also capture originalUserProfile (e.g. let
originalUserProfile: string | undefined) and when process.platform === 'win32'
set process.env.USERPROFILE = testDir; in afterEach restore USERPROFILE (delete
if undefined, else set back) alongside the existing HOME restore and
cleanupTestDir call so the sandboxed home is consistent across platforms when
running installBuiltinTemplates.

In `@tests/utils/clipboard.test.ts`:
- Around line 5-7: Update the explanatory comment in
tests/utils/clipboard.test.ts to remove temporal phrasing and state the
invariant: explain that the mock is configured inside beforeAll and the module
under test is dynamically imported afterwards to avoid polluting other test
files; reference the beforeAll setup and the dynamic import order so readers
understand the reason without using words like "prevent" tied to time (e.g.,
phrase it as "The mock is configured in beforeAll and the module is imported
afterwards to avoid cross-test pollution").
🧹 Nitpick comments (3)
src/sandbox/detect.test.ts (1)

14-21: Test exercises Node.js stdlib rather than project code.

The describe block is named "sandbox detection constants" but the test only validates platform() from node:os, which is a standard library function. No actual code from the sandbox detection module is imported or tested here. If the intent is to have a placeholder test file, consider either:

  1. Adding a test that exercises an actual constant or export from src/sandbox/detect.ts, or
  2. Removing this file entirely if all meaningful tests are in tests/sandbox/detect.test.ts.

Additionally, the platform list is missing 'android', which is a valid return value from platform() per Node.js documentation.

🔧 Suggested fix to include all valid platforms
     const currentPlatform = platform();
     // platform() should return a known value
-    expect(['darwin', 'linux', 'win32', 'freebsd', 'openbsd', 'sunos', 'aix']).toContain(
+    expect(['darwin', 'linux', 'win32', 'freebsd', 'openbsd', 'sunos', 'aix', 'android']).toContain(
       currentPlatform
     );
src/plugins/agents/base.test.ts (1)

460-510: Guard env mutation with try/finally for isolation.

If an assertion throws, the env vars and agent disposal won’t run. Wrapping the mutation/cleanup in a try/finally keeps the suite isolated.

tests/plugins/agents/base-execute.test.ts (1)

456-600: Use try/finally around process.env mutations.

If a test assertion throws, the env resets and disposal won’t run. Wrapping each mutation block in try/finally will keep the suite isolated.

Comment updates (remove temporal phrasing):
- src/plugins/agents/base.test.ts: Explain test isolation rationale
- src/plugins/trackers/builtin/beads-bv/index.test.ts: Document mock setup invariant
- src/plugins/trackers/builtin/beads.test.ts: Document mock setup invariant
- src/setup/skill-installer-spawn.test.ts: Document mock setup invariant
- tests/utils/clipboard.test.ts: Document mock setup invariant
- tests/plugins/agents/base-execute.test.ts: Rewrite header as evergreen
- tests/engine/auto-commit.test.ts: Add TODO for production imports

Code improvements:
- src/tui/components/FileBrowser.tsx: Reset edit state on visibility change,
  add errorMessage prop for external error display
- src/tui/components/EpicLoaderOverlay.tsx: Pass error to FileBrowser
- tests/engine/auto-commit.test.ts: Add fail-fast error handling to initGitRepo
- tests/plugins/agents/base-execute.test.ts: Add try/catch to ensure
  resolvePromise is always called, even on errors
- tests/templates/engine.test.ts: Add USERPROFILE support for Windows
Copy link
Copy Markdown
Contributor

@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: 1

🤖 Fix all issues with AI agents
In `@src/tui/components/FileBrowser.tsx`:
- Around line 158-188: The expandPath / navigateToPath logic resolves typed
relative paths against process.cwd() because expandPath always calls
resolve(expanded); update expandPath (or adjust navigateToPath) to resolve
relative inputs against the current browsing directory (currentPath) instead of
the process CWD: detect absolute paths (path.isAbsolute(expanded) or starting
with '/'), tilde (~) and explicit relative markers (./ or ../) and for plain
relative names call resolve(currentPath, expanded) so that navigateToPath uses
the visible directory context when setting currentPath, while preserving the
existing tilde expansion behavior.

Copy link
Copy Markdown
Contributor

@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: 1

🤖 Fix all issues with AI agents
In `@tests/plugins/agents/base-execute.test.ts`:
- Around line 241-323: The runExecution function inside
BunSpawnEnvTestPlugin.execute can throw (e.g., Bun.spawn or readStream) and
currently never resolves/rejects resolvePromise; wrap the body of runExecution
in a try/catch that mirrors BunSpawnTestPlugin behavior: on success build and
call resolvePromise(result) as before, and in catch create a failed
AgentExecutionResult (status 'failed', include error message in stderr,
appropriate exitCode like 1, duration, startedAt/endedAt) then call
resolvePromise (or rejectPromise if existing pattern uses reject) and still call
options?.onEnd inside its own try/catch; ensure readers/proc are awaited/cleaned
up if necessary and reference runExecution, Bun.spawn, readStream,
resolvePromise, and options?.onEnd when making the changes.
🧹 Nitpick comments (2)
tests/templates/engine.test.ts (2)

704-707: Variable shadowing creates minor inefficiency.

The testDir declaration at line 705 shadows the outer scope's testDir (line 565). This means the outer beforeEach still creates a test directory and sets up getUserConfigDirSpy, but these are unused for tests in this inner describe block (which uses environment variable sandboxing instead).

This is functionally correct but slightly wasteful. Consider renaming to installTestDir for clarity, or restructuring to avoid the outer hooks running unnecessarily.


767-779: Add sandbox verification assertion and correct misleading comment.

The os.homedir() function reads process.env.HOME (on Unix) or process.env.USERPROFILE (on Windows) at runtime. Since beforeEach sets these environment variables before the test runs, the sandboxing is effective. The comment at lines 768–769 is misleading and should be updated.

Add a verification assertion (as used in the installGlobalTemplates tests at lines 644 and 653) to confirm the test writes to the sandbox:

Proposed fix
       // Use fresh import to bypass mock pollution from migration-install.test.ts
-      // Note: Cannot use sandboxed testDir here because fresh import uses real getUserConfigDir
+      // Note: Sandboxing works via HOME/USERPROFILE env vars set in beforeEach
       const result = freshInstallBuiltinTemplates(false);

       // The function returns results for all four templates (not undefined)
       expect(result).toBeDefined();
       expect(result.results).toBeDefined();
       expect(result.results.length).toBe(4);
       expect(result.templatesDir).toContain('.config/ralph-tui/templates');
+      expect(result.templatesDir.startsWith(testDir)).toBe(true);
       // Verify templates were actually created or skipped (both are valid outcomes)
       expect(result.results.every(r => r.created || r.skipped)).toBe(true);

Without this assertion, a sandboxing regression could go undetected and cause tests to write to the actual user's home directory.

…rectory

The expandPath function was resolving relative paths against process.cwd()
instead of the current browsing directory (currentPath). Now:
- Tilde (~) expands to home directory
- Absolute paths resolve directly
- Relative paths (including ./ and ../) resolve against currentPath

This makes the file browser behave intuitively when typing relative paths
in the path editor.
Copy link
Copy Markdown
Contributor

@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

🤖 Fix all issues with AI agents
In `@tests/engine/auto-commit.test.ts`:
- Around line 5-11: Update the TODO comment to remove future-tense phrasing and
state the current constraint and the follow-up action: note that because Bun's
mock.restore() does not reliably restore builtin modules the test re-implements
hasUncommittedChanges and performAutoCommit locally using Bun.spawn, and
instruct that these should be replaced with imports from
src/engine/auto-commit.ts when Bun's module mock restoration is fixed so tests
stay in sync.

In `@tests/utils/clipboard.test.ts`:
- Around line 88-102: Update the inline comments inside the beforeAll block to
remove temporal wording and state invariants: change "Mock the modules before
importing clipboard" to something like "Ensure node:child_process and node:os
modules are mocked" and "Import after mocking" to "Import clipboard module after
mocks are registered"; keep references to mock.module calls for
'node:child_process' and 'node:os' and the subsequent dynamic import of
'../../src/utils/clipboard.js' that assigns writeToClipboard so the intent
remains clear and evergreen.
🧹 Nitpick comments (1)
website/next-env.d.ts (1)

1-3: Add the required ABOUTME JSDoc header before the reference directives.

This file should start with a file-level JSDoc describing its purpose and prefixed with ABOUTME: .

✍️ Proposed fix
+/**
+ * ABOUTME: Declares Next.js ambient type references and route type imports for the website app.
+ */
 /// <reference types="next" />
 /// <reference types="next/image-types/global" />
 import "./.next/types/routes.d.ts";

As per coding guidelines, Start all code files with a file-level JSDoc comment section explaining the file's purpose, prefixed with 'ABOUTME: '.

- auto-commit.test.ts: Change TODO to NOTE, remove future-tense
- clipboard.test.ts: Reword mock setup comments as invariants
@subsy subsy merged commit 6173307 into main Feb 3, 2026
6 checks passed
@subsy subsy deleted the feat/file-browser-pr236 branch February 3, 2026 17:27
Copy link
Copy Markdown
Contributor

@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: 1

🤖 Fix all issues with AI agents
In `@src/tui/components/FileBrowser.tsx`:
- Around line 123-131: Normalize initialPath before setting current path when
the FileBrowser becomes visible: call the same normalisation/expansion logic
used by navigateToPath (expand '~' and resolve relative paths) and pass the
resolved path to setCurrentPath (instead of the verbatim initialPath), then
reset transient UI state to avoid stale flashes by also clearing the directory
list, error and loading flags (use the component's setters such as
setSelectedIndex, setShowHidden, setEditingPath, setEditedPath and the
list/error/loading setters — e.g., setEntries, setError, setLoading if present)
so the component immediately shows the fresh loading state and empty list while
the directory reloads.

Comment on lines +123 to +131
// Reset state when becoming visible
useEffect(() => {
if (visible) {
setCurrentPath(initialPath ?? process.cwd());
setSelectedIndex(0);
setShowHidden(false);
setEditingPath(false);
setEditedPath('');
}
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.

⚠️ Potential issue | 🟠 Major

Normalise initialPath before use to keep ~/relative paths working.

initialPath is stored verbatim on open, so ~ and relative inputs are not normalised like navigateToPath (which does expand/resolve). This leads to inconsistent behaviour and can fail on valid inputs. While touching this block, also clear transient list/error/loading state to avoid a stale flash before the directory reloads.

🛠️ Proposed fix
  useEffect(() => {
    if (visible) {
-      setCurrentPath(initialPath ?? process.cwd());
+      const nextPath = (() => {
+        if (!initialPath) return process.cwd();
+        let expanded = initialPath.trim();
+        if (expanded.startsWith('~')) {
+          expanded = homedir() + expanded.slice(1);
+        }
+        if (isAbsolute(expanded)) {
+          return resolve(expanded);
+        }
+        return resolve(process.cwd(), expanded);
+      })();
+      setCurrentPath(nextPath);
+      setEntries([]);
+      setError(null);
+      setLoading(true);
       setSelectedIndex(0);
       setShowHidden(false);
       setEditingPath(false);
       setEditedPath('');
    }
  }, [visible, initialPath]);
🤖 Prompt for AI Agents
In `@src/tui/components/FileBrowser.tsx` around lines 123 - 131, Normalize
initialPath before setting current path when the FileBrowser becomes visible:
call the same normalisation/expansion logic used by navigateToPath (expand '~'
and resolve relative paths) and pass the resolved path to setCurrentPath
(instead of the verbatim initialPath), then reset transient UI state to avoid
stale flashes by also clearing the directory list, error and loading flags (use
the component's setters such as setSelectedIndex, setShowHidden, setEditingPath,
setEditedPath and the list/error/loading setters — e.g., setEntries, setError,
setLoading if present) so the component immediately shows the fresh loading
state and empty list while the directory reloads.

sakaman pushed a commit to sakaman/ralph-tui that referenced this pull request Feb 15, 2026
feat: add file browser component for PRD file selection
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.

3 participants