Skip to content

opencode: allow nested workdir picker paths#791

Open
Datawav wants to merge 1 commit into
marmot-protocol:masterfrom
Datawav:opencode/subdirectory-workdir-picker
Open

opencode: allow nested workdir picker paths#791
Datawav wants to merge 1 commit into
marmot-protocol:masterfrom
Datawav:opencode/subdirectory-workdir-picker

Conversation

@Datawav

@Datawav Datawav commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

What

Extend the wn-opencode workdir picker to accept nested paths under $HOME, so users can send /projects/mdk fix the failing test in addition to the existing /mdk shape.

Why

The picker previously required the selected directory to be a direct child of $HOME. Repos commonly live one or more levels deeper (e.g. ~/projects/mdk, ~/work/whitenoise), and the only workaround was a top-level symlink. Nested selection is the natural user-facing extension.

How

  • parse_repo_picker splits the picker on / and applies the existing charset rule per segment. Empty segments, ., and .. are rejected at parse time.
  • resolve_repo and validate_session_cwd now use a canonical starts_with($HOME) ancestor check instead of a parent() equality check. Direct $HOME selection is still rejected in resolve_repo.
  • Symlinks are still resolved via tokio::fs::canonicalize before the ancestor check, so a symlink whose target escapes $HOME is rejected.

Tests

cargo test -p wn-opencode — 36 unit tests pass (was 31). New coverage:

  • parse_repo_picker_matches_nested_path
  • parse_repo_picker_rejects_bare_slash_or_empty_segments (expanded)
  • parse_repo_picker_rejects_dot_segments (expanded with nested .. / .)
  • validate_session_cwd_allows_home_or_nested_child (expanded)
  • resolve_repo_accepts_nested_path
  • resolve_repo_rejects_direct_home_target
  • resolve_repo_rejects_symlink_escaping_home (unix)
  • validate_session_cwd_rejects_symlink_escaping_home (unix)

cargo fmt -p wn-opencode -- --check clean.
cargo clippy -p wn-opencode --all-targets -- -D warnings clean.

Docs

README workdir picker section updated with the nested example and the symlink-canonicalization note.

Scope

No changes to bridge.rs, control protocol, chunking, session store, or the installer. Behavior for single-segment pickers is unchanged.


Open in Stage

Summary by CodeRabbit

  • New Features

    • Expanded workdir/repo picker support for nested paths, allowing selections like /<path> instead of only direct child directories.
    • Added clearer examples and updated guidance for how path selection works.
  • Bug Fixes

    • Strengthened validation to reject empty, . and .. path segments, and invalid characters.
    • Improved directory handling so selections inside ~ are accepted, while paths that resolve outside ~ are still blocked, including through symlinks.

Extend the workdir picker to accept multi-segment paths like
`/projects/mdk` in addition to `/mdk`. Each `/`-separated segment
still uses the existing ASCII-alphanumeric + `._-` charset and
rejects empty, `.`, and `..` segments.

Replace the parent-equals check in `resolve_repo` and
`validate_session_cwd` with a canonical `starts_with` ancestor
check so nested targets resolve inside $HOME while symlinks
that escape are still rejected after canonicalization.

Add tests for nested acceptance, expanded empty/dot segment
rejection, direct $HOME rejection, and symlink escape rejection
for both entry points.
@stage-review

stage-review Bot commented Jul 8, 2026

Copy link
Copy Markdown

Ready to review this PR? Stage has broken it down into 3 individual chapters for you:

Title
1 Update workdir picker to parse nested paths
2 Relax path validation to allow nested subdirectories
3 Document nested path support and security rules
Open in Stage

Chapters generated by Stage for commit b1dfcbd on Jul 8, 2026 10:51am UTC.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR extends the OpenCode marmot repo picker to accept nested /<path> segments instead of only direct $HOME children. Path segment validation rejects empty, ., and .. segments and disallowed characters. Containment checks in resolve_repo and validate_session_cwd now use starts_with against canonicalized $HOME. README and tests updated accordingly.

Changes

Nested Repo Picker Path Support

Layer / File(s) Summary
Parser validation for nested picker paths
integrations/opencode/marmot/src/repo_picker.rs
parse_repo_picker validates /-separated segments, rejecting empty/./.. segments and invalid characters, returning the full nested path; tests cover nested paths and invalid segment forms.
Containment checks in resolve_repo and validate_session_cwd
integrations/opencode/marmot/src/repo_picker.rs
Both functions replace direct-parent checks with starts_with-based containment under $HOME, allowing $HOME itself and nested directories while still rejecting symlink escapes; tests updated with nested-directory and symlink-escape scenarios.
README picker documentation update
integrations/opencode/marmot/README.md
Workdir Picker section documents the /<path> syntax, segment validation rules, additional nested-path example, and symlink rejection after canonicalization.

Estimated code review effort: 3 (Moderate) | ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: allowing nested workdir picker paths in opencode.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
integrations/opencode/marmot/src/repo_picker.rs (1)

44-48: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Error message is misleading when the target is $HOME itself.

When canonical == home_canonical (e.g., name "."), the error says "resolves outside $HOME" but the path actually resolves to $HOME. Consider a distinct message for the direct-$HOME case.

💡 Proposed fix
     if canonical == home_canonical || !canonical.starts_with(&home_canonical) {
-        return Err(repo_error(format!(
-            "Repo ~/{name} resolves outside $HOME; refusing."
-        )));
+        if canonical == home_canonical {
+            return Err(repo_error(format!(
+                "Repo ~/{name} resolves to $HOME; refusing."
+            )));
+        }
+        return Err(repo_error(format!(
+            "Repo ~/{name} resolves outside $HOME; refusing."
+        )));
     }
🤖 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 `@integrations/opencode/marmot/src/repo_picker.rs` around lines 44 - 48, The
validation in repo_picker::pick_repo uses one error message for both cases, but
canonical == home_canonical is not “outside $HOME” and needs its own branch.
Update the conditional around the canonical/home_canonical check so direct
resolution to $HOME (for example from "." or equivalent) reports a distinct,
accurate message, while the existing message remains for paths that resolve
outside $HOME.
🤖 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.

Outside diff comments:
In `@integrations/opencode/marmot/src/repo_picker.rs`:
- Around line 44-48: The validation in repo_picker::pick_repo uses one error
message for both cases, but canonical == home_canonical is not “outside $HOME”
and needs its own branch. Update the conditional around the
canonical/home_canonical check so direct resolution to $HOME (for example from
"." or equivalent) reports a distinct, accurate message, while the existing
message remains for paths that resolve outside $HOME.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 166833f3-61ea-4738-aab2-b8f3e7027f4c

📥 Commits

Reviewing files that changed from the base of the PR and between 4b44b5d and b1dfcbd.

📒 Files selected for processing (2)
  • integrations/opencode/marmot/README.md
  • integrations/opencode/marmot/src/repo_picker.rs

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