// LORE ENTRY

DeepSeek Harness Stuck After npx: The pnpm dlx Wrapper That Worked

A grounded troubleshooting case study on the stalled npx prompt, the pnpm dlx route that worked on one machine, and the pinned wrapper that made dsh discoverable.

A dark mechanical workbench with one stalled package route and another passing through a brass relay

Enlarged view: A dark mechanical workbench with one stalled package route and another passing through a brass relay

Two package routes, one paused and one carried through a small relay, frame the repair before the terminal logs begin.

Two package routes, one paused and one carried through a small relay, frame the repair before the terminal logs begin.

The command documented in the DeepSeek Harness README reached its installation prompt on one Linux/zsh machine, accepted y, and then went quiet. A second run ended at the same blank cursor. pnpm dlx later crossed that bootstrap boundary, while a 67-byte wrapper turned the temporary runner into a normal dsh command.

That result is useful because it is narrow. The captured evidence does not reveal why npx appeared to stall, and it does not show that npm's runner is generally broken. It shows what worked on this machine, why the wrapper was needed, and how later plugin failures could be separated from the original package-runner problem.


The quiet prompt was not the whole diagnosis

The first attempt looked simple: npm asked permission to fetch @deepseek-ai/dsh, the terminal received y, and no further progress appeared. Ctrl+Z then made the situation less obvious. Bash's job-control manual explains that Ctrl+Z normally suspends a foreground process; it does not terminate it.

Code excerpt
jobs -l
kill %1 %2
jobs

jobs -l exposed the result. One npx process still ran while another sat suspended. Before another installer test, the shell needed a clean slate.

The captured verbose run added another clue. Registry requests returned HTTP 200 responses, which ruled out a total DNS failure or completely unreachable npm registry. It did not identify the internal wait. Dependency resolution, cache state, a lifecycle script, or another local condition could still have been involved; the available log never settled that question. Calling the event an “npm bug” would therefore claim more than the record supports.

npx and pnpm dlx are temporary runners, not permanent installs

npm describes npx as a way to run a binary from a local or remotely fetched package. When the package is absent from the project, npm places it in a cache-backed execution environment and may show the confirmation prompt seen in the terminal.

pnpm documents pnpm dlx under its pnx command. It also fetches and hotloads a package without adding that package as a project dependency, and it accepts an exact version. The two commands serve a similar job even though their stores and resolution paths differ.

On this machine, the alternate path moved farther:

Code excerpt
pnpm dlx @deepseek-ai/dsh@0.1.1-rc.2 web

That was an observed result, not a package-manager contest; DeepSeek still documents npx @deepseek-ai/dsh web as the ordinary run command, and another machine may complete that path without trouble.

The successful pnpm dlx run also created a predictable inconvenience. It launched the package binary for that invocation, but it did not place a permanent dsh executable in the shell's usual search path. External software that called command -v dsh, or simply tried to execute dsh, still had nothing durable to find.

A pinned wrapper made dsh discoverable

The smallest useful repair was a shell script at ~/.local/bin/dsh:

Code excerpt
#!/usr/bin/env bash
exec pnpm dlx @deepseek-ai/dsh@0.1.1-rc.2 "$@"

The surrounding setup stayed ordinary:

Code excerpt
mkdir -p "$HOME/.local/bin"
chmod +x "$HOME/.local/bin/dsh"
export PATH="$HOME/.local/bin:$PATH"
rehash

The same export PATH=... line belongs in .zshrc when that directory is not already present. After the shell reloads its configuration, three checks tell the story without starting the Web server:

Code excerpt
command -v dsh
dsh --version
dsh --help

On August 24, 2026, the live checks returned /home/tada/.local/bin/dsh, version 0.1.1-rc.2, and the CLI help successfully. The published DSH package manifest maps its package binary to the name dsh, so the wrapper forwards arguments into the command surface that the package already exposes.

The exact version matters. DeepSeek labels Harness a developer preview and warns that compatibility-breaking changes will occur. A wrapper containing @latest can change behavior even when the wrapper file itself has not changed. Pinning 0.1.1-rc.2, the tested and current npm release at the research cutoff, keeps the runner and profile relationship stable until an update is chosen deliberately.

The wrapper still depends on pnpm, its package store, and whatever network access a cold fetch needs. It is a command bridge, not an offline binary installation.

Abstract package modules flowing through a small brass relay into a stable command port

Enlarged view: Abstract package modules flowing through a small brass relay into a stable command port

The wrapper's job is deliberately small: pass every argument to the tested package runner and give the shell a stable name to find.

The wrapper's job is deliberately small: pass every argument to the tested package runner and give the shell a stable name to find.

The profile survived because it lived somewhere else

Temporary package execution did not make the DSH profile temporary. The official CLI reference places named profiles under $DSH_HOME/profiles/<name> and runs plugin-management operations from that directory; in the captured setup, the web profile stayed under ~/.dsh/profiles/web while the CLI arrived through pnpm dlx.

That separation explains why the wrapper could be replaced or repinned without recreating the profile. It also explains why an old profile could boot far enough to reveal a different class of failure.

A successful bootstrap can still fail in the plugin tree

Once dsh web reached the loader, the installation question had changed. The CLI existed, the profile had been found, and bundles had begun to load.

How the captured failures separated by layer
LayerCaptured symptomWhat the evidence supportsNext check
Shell jobjobs showed running and suspended npx processesCtrl+Z left work behindClean the job table before another run
Package runnerPrompt appeared idle; verbose fetches returned HTTP 200The registry was reachable, but the wait's exact cause stayed unknownTry a pinned alternate runner and keep the claim machine-specific
DSH commanddsh --help and dsh --version succeeded through the wrapperPackage bootstrap and command discovery workedStop treating later loader errors as installer errors
Profile/plugin bootSchema validation, missing package, and missing build-file messages appearedDSH had progressed into its composed plugin treeBack up the profile and repair one entry at a time

The remaining errors belonged to plugins or their build artifacts: three historical plugins, dsh-plugin-academic-writing, dsh-plugin-education, and dsh-plugin-translation, reached schema validation before failing on additionalProperties. dsh-web-search-ext could not be found, while dsh-skill-hub lacked its expected lib/index.js build output. Those messages did not indict the wrapper; they showed that it had carried execution far enough for DSH to inspect the profile.

The current local profile no longer contains that failed plugin set, so those errors belong to the troubleshooting record rather than the machine's present state.

Open Design exposed one more useful boundary. pnpm first blocked a Git dependency's lifecycle script; after build approval, the script actually started and then failed at tsx: command not found. The inspected Open Design source manifest targets Node ~24 and declares its source tooling, while the captured machine ran Node v26.7.0. Build permission had been repaired, but source-build compatibility had not. The log showed both conditions without proving that either one alone caused every failure.

The safer repair order names the failing layer

A repeatable troubleshooting sequence came out of the mess:

  1. Record the last visible stage, command, and version.
  2. Check jobs -l after Ctrl+Z; end or resume every suspended process before another package-runner test begins.
  3. Use verbose registry output as a clue, never as proof of a specific resolver defect.
  4. Test an alternate runner with an exact package version.
  5. Verify command -v dsh, dsh --version, and dsh --help before touching the profile.
  6. Back up $DSH_HOME/profiles/<name>, then handle schema, package, and build failures one entry at a time.

This order keeps a working bootstrap from being thrown away because the next subsystem is broken. It also keeps a plugin error from being retold as “DeepSeek Harness will not install,” which was no longer true once the wrapper returned valid CLI help.

The working result was small, and deliberately limited

The final setup did not force a global npm installation. A pinned pnpm dlx invocation supplied the CLI, a tiny executable made its name discoverable, and the persistent profile stayed in ~/.dsh. That solved the runner and command-discovery problem on the captured system.

It did not explain the original npx wait, repair every third-party plugin, or promise that release-candidate behavior will remain fixed. Those limits make the result more useful, not less: each failure now has a layer and a next check.

Before changing the pinned version or adding another Git-hosted plugin, the next useful step is to inspect DeepSeek's current run instructions and dsh CLI behavior reference. The project is moving quickly, and its own record should decide what changes next.

Read next

Articles sharing this post's topics, followed by the latest entries.

View all articles
// READER SIGNAL

Reader notes

0 comments
// FIELD NOTES

Comments

Checking your session...

Loading reader comments...

Open sourceBack to Blog
DeepSeek Harness Stuck After npx: Fix It With pnpm dlx · Mukhtada