dsh-open-path
English · 中文
/open — opens files and http(s) links with the system default program and folders in the file manager, covering direct paths, direct URLs and fuzzy workspace search.
dsh-TUI · a plugin built for the dsh-TUI ecosystem
This plugin is a dsh-TUI ecosystem plugin; ecosystem site: https://dshtui.com/. Community v0.15 manifest, MIT licensed, supporting Windows / macOS / Linux (including WSL).
Capabilities
/open (no argument) → opens the current session's working directory in the system file manager
/open https://example.com → opens the http(s) link with the default browser/handler (case-insensitive; only http/https are let through, and file:/ftp:/javascript: and other schemes are rejected with an explicit error)
/open github.com → bare domains get their scheme filled in: domains get https://, localhost:5173/127.0.0.1:8080 get http:// (local development); a real file of the same name in the workspace wins; common file extensions (.md/.ts/.json…) are not misread as domains
/open src/index.ts → opens an existing relative / absolute path
/open ~/docs → ~ / ~/… expands to the home directory (~user is not expanded)
/open readme → fuzzy-searches the current workspace (file/folder names, CJK- and case-insensitive, Unicode NFD/NFC normalised)
- exactly one hit → opened directly
- several hits → the host's managed dialog (TUI seam 十) lists the candidates, with no cap on the count: the panel is windowed to the terminal height and scrolls up/down with ↑/↓ (with scroll arrows), Enter opens, Esc cancels
- more hits than the host dialog's ceiling (100) → only the top 100 are shown, and the title spells it out:
共 N 个匹配,仅显示前 100 个 — never a silent swallow
- zero hits → an explicit error:
工作区中找不到与 “…” 相关的文件或文件夹
- a folder hit → the folder opens in the file manager; a file hit → it opens in the associated default program
Install
# install from npm (package name: dsh-open-path)
dsh plugin --profile dsh-tui add dsh-open-path
# install from a local checkout of this repository (development)
pnpm install --frozen-lockfile && pnpm build
cd ~/.dsh/profiles/dsh-tui && pnpm add <path to this repository>
# and add dsh-open-path to dsh.profile.bundles in package.json
After installing, run /restart inside the TUI (or reopen the window) for it to take effect.
Platform support
The open action runs an ordered launcher chain: the first candidate on the chain that can start successfully (and does not exit non-zero within the grace window) wins; otherwise it automatically falls back to the next one; when the whole chain fails it reports an explicit error and lists the launchers it tried. A chain can also come back empty — a Windows file/URL containing % has no cmd-free channel — and that is reported as a refusal naming the reason, never as an attempted hand-off.
| Platform | Folders | Files / http(s) links | Notes |
|---|
| Windows | Shell.Application COM, falling back to cmd /c start | cmd /d /s /c start "" "<target>" | a single-instance Explorer swallows start <directory> requests, so the COM channel is the only reliable way to open a folder; that COM child process cannot be detached (a console-less PowerShell drops the call), so only windowsHide is used; a target containing % is refused on the start channel (cmd expands %VAR% even inside quotes), while folders still open through the COM channel, whose PowerShell single-quoted literal leaves % alone |
| macOS | open | open | the NFD (on disk) / NFC (as typed) file-name difference is already normalised in the matching layer |
| Linux | xdg-open → gio open → kde-open5 → kde-open → exo-open | the same as on the left | any one of them must be installed (most distributions ship xdg-utils); all of them handle files, folders and URLs |
| WSL | wslview → the Linux chain above | wslview → the chain above → cmd.exe /c start | without WSLg (no DISPLAY/WAYLAND_DISPLAY) the session still counts as graphical, and the request switches to the Windows-side handler; the cmd.exe last resort is dropped for a %-carrying URL for the same cmd reason |
Failure is not judged by "the process started, so it worked": a launcher that cannot be spawned (xdg-open not installed → ENOENT) or that exits non-zero quickly (no associated program) is recognised and triggers the fallback; when the target path itself does not exist it reports an explicit error instead of falsely claiming 已打开.
The receipt also says which evidence the success rests on: 已打开 only when the launcher exited 0 inside the grace window, and 已启动 …(未确认退出码) when the window expired with the launcher still alive — a timeout is never upgraded into a confirmed open.
Compatibility
- Minimum dependency:
@deepseek-harness-tui/dsh-tui >= 0.10.0-beta.5 (command-registration surface C-041 managed surface + fallback; the managed dialog is a "stable candidate" seam)
- Manifest: Community v0.15 (
commands.dsh/v1alpha1#Command required; commands.invoke is declared, allowed by default and revocable through the authorization file's denies)
- Command registration: prefers
ctx.tuiPluginHost.registerCommand (C-041 attribution + invoke checkpoint); when the host does not provide it, it falls back to the plain commands service (C-070 boundary — functionally equivalent, but without the attribution mark)
- Dialog: a soft probe through
ctx.get('tuiDialogs', false) (#183 discipline); when the service is missing it degrades to a clear error listing the candidates, never crashing and never affecting TUI startup
- Diagnostic log:
~/.dsh-tui/dsh-open-path.log — one line each for module import / apply start / seam probes (tuiPluginHost·commands·tuiDialogs, each 0/1) / the registration result / unload; trimmed past 128 KiB to keep the newest half; not written during test runs and under node --test
- No dependency pollution: it does not import upstream package types (structural minimal interfaces), so upstream version drift cannot break this plugin; it also does not reuse the TUI's internal private modules
- No graphical session: always available on Windows/macOS (on Windows this is an assumption rather than a probe — see Known Limitations); Linux needs
DISPLAY/WAYLAND_DISPLAY; WSL is the exception (it goes through the Windows side). When unavailable it reports an explicit error rather than failing silently
- CI:
ubuntu-latest / windows-latest / macos-latest (Node 24, plus Ubuntu × Node 22 to cover the engines lower bound)
Language
Command receipts, error messages, picker candidates and command hints all follow
the host language, resolved along the same chain dsh-TUI itself uses:
DSH_TUI_LANG → the live dsh-tui settings namespace (where /lang writes) →
~/.dsh-tui/lang.json → the OS locale → Chinese.
- An unsupported but present locale falls back to English; a missing one
keeps Chinese, which is what keeps a Chinese host that never wrote a
preference byte-identical to the pre-i18n releases.
- The language is re-resolved on every invocation, so switching with
/lang
needs no restart; the hint text inside command completion is a snapshot
taken at registration time and follows the new language after a restart.
DSH_OPEN_PATH_LANG_FILE overrides the preferences file path (for tests and
diagnostics).
Configuration
| Key | Default | Description |
|---|
maxCandidates | 0 | the maximum number of candidates the picker shows; 0 = no limit (every fuzzy match, still bounded by the host dialog's ceiling of 100), or set a positive integer to narrow it yourself |
includeHidden | false | whether hidden (dot-prefixed) files/folders are indexed |
Configuration is overridden through /settings or the profile's cordis configuration; every key has a default, and a missing one degrades to the default behaviour.
Working-directory semantics
The command is relative to the working directory of the receiving session (agent.session.header.cwd, the host-side cwd recorded in the DSH session header) rather than the process cwd — switching with /workspace in the TUI creates a new session, whose header carries the new directory, so /open always follows along; in extreme cases it falls back through session.meta.cwd (the older host alias) → the process start directory in turn.
Relative paths in the fuzzy index are always separated with / (equally true on Windows), so the same index has identical semantics on all three platforms.
Model Experience
The command executes on the UI command plane and the result text is rendered directly by the adapter: it produces no model messages, counts toward no model tokens and enters no model KV cache. command/run / command/done are recorded in the session log as log-only events.
Known Limitations
- Only
http:// / https:// links are accepted (including bare-domain completion); file:, ftp:, javascript:, mailto: and other schemes are rejected with an explicit error and are never handed to the system. The bare-domain guess exempts common file extensions (.md/.ts/.json etc. are treated as files), and single-character TLDs (such as a.b) and version numbers (v2.0.1) are not taken for URLs either.
- The fuzzy index is bounded by depth (≤6 levels) and entry count (≤20000); on very large repositories the scan stops at the cancellation signal or the ceiling, and whatever lies beyond is not searched.
- The number of rows in the managed picker is ultimately bounded by the host: dsh-tui 0.10.x's
tuiDialogs.select keeps only the first 100 options of the request (the rest are dropped silently, and the plugin cannot read that constant). This plugin therefore aligns itself with 100 and says so in the title when there are more hits, but candidates beyond 100 cannot be shown on the current host; a genuinely unlimited picker would have to go through a self-drawn tuiScenes selector.
- Symbolic links pointing at directories are indexed as directories but are not traversed recursively (loop and workspace-escape protection).
~ expansion covers ~ / ~/… / ~\… only; ~user is not resolved (no passwd lookup).
- When the target is a file and the system has no associated program, the behaviour is up to the system (Windows may show a "How do you want to open this file?" dialog) — the platform default is kept, and no program is chosen on your behalf.
- The open action is still fire-and-forget: failure is observed and rolled back only within a 180 ms grace window, after which it no longer waits for the target program to finish loading; a launcher that crashes after the window goes unnoticed. The window can only ever prove "still running", so the receipt keeps the two readings apart —
已打开 requires an exit 0 inside the window, and a launcher still alive when the window expires is reported as 已启动 …(未确认退出码).
- A Windows target containing
% cannot be handed to cmd at all: %VAR% is expanded even inside double quotes, so a percent-encoded URL (https://x.com/a%20b%20c) or a path like 50%off would be silently rewritten to a different target. Such a file or URL is refused with an explicit error naming ; a with still opens, because its channel is the PowerShell COM call, whose single-quoted literal leaves alone. macOS/Linux/WSL channels pass through unchanged.
Publishing
- Repository: https://github.com/VviLliAm-qwq/dsh-open-path (public)
- Versioning: semantic versioning; releases are driven by
v* tags (.github/workflows/release.yml: verify the tag matches the package.json version → build/test/checks → npm publish --provenance → GitHub Release)
- Prerequisite: the npm package has a Trusted Publisher configured (GitHub Actions · this repository ·
release.yml) — publishing uses OIDC, so no token has to be stored in the repository
- Ecosystem listing: per the listing requirements of https://dshtui.com/plugins/, this README carries the dsh-TUI link at the top (the fixed wording given by that site:
[dsh-TUI](https://github.com/ccch1mneyyy/dsh-TUI) · 一个为 dsh-TUI 生态打造的插件)
Development and verification
pnpm install
pnpm build # tsc -> lib/
pnpm test # vitest (platform branches are covered cross-platform through injected platform/env)
pnpm validate:manifest # dsh-plugin.json admission-shape check
pnpm pack:verify # published-artifact layout check
pnpm prepublishOnly # all four, in one go
License
MIT