Preface¶
Regular expressions are almost the default tool when agents need to process logs, validate user-provided patterns, or extract fields from text. The problem is that models have a high error rate when “mentally calculating” regex behavior, and they cannot share intermediate results for human review. A common alternative is to have the model write a node -e or Python script on the fly, then run it via bash — which adds both process overhead and additional risks of script correctness issues.
The built-in grep in DeepSeek Harness (dsh) only supports file scope searches, and cannot test, extract, or replace arbitrary strings, let alone explain what a given pattern actually matches. The community plugin dsh-tool-regex was built for this exact purpose: it registers a regex tool for the current dsh process, replacing mental regex calculations with deterministic pure-function results.
This article is organized after cross-checking the plugin directory page, GitHub repository README, and source code: what it is, how to use its four actions, how to install it, and the boundaries related to ReDoS.
What It Is¶
dsh-tool-regex is a tool and capability plugin for DeepSeek Harness, maintained by the GitHub organization omdsh-dev, with the repository address omdsh-dev/dsh-tool-regex. The directory page was indexed on 2026-08-14, licensed under MIT, and primarily written in TypeScript. As of the time of this article’s review, the repository has 3 stars.
It solves the following types of tasks:
- Determine if a given text matches a specified pattern
- Extract numbered capture groups and named capture groups from logs or any arbitrary string
- Perform safe substitution with $1 / $2 / $$ semantics
- Break down a pattern into human-readable explanation nodes without executing any matching operations
The plugin integrates as a Profile Bundle. Its package name declared in package.json is @deepseek-ai/dsh-tool-regex, and after installation, it will insert a row ID tool-regex into the profile’s layer stack. This is an open-source community plugin, not an official application from DeepSeek / Horizon Robotics. The core design principle of DeepSeek Harness is “everything is a plugin”, and the community directory deepseek-harness-plugin.com is an independent site with no affiliation to the official repository.
There are no third-party dependencies at runtime: the package.json has no dependencies field, and peer dependencies are provided by the profile (@deepseek-ai/cordis, @deepseek-ai/dsh-tools, @deepseek-ai/dsh-invariants). The engine side uses pure functions; test / find / replace operations are executed in worker threads.
Core Features¶
The plugin only registers one tool: regex. Calls use the action parameter to distinguish four types of operations, and all return JSON text strings.
Four Actions¶
| action | Function | Output Format |
|---|---|---|
test |
Check if the text matches the pattern | {"matched":true} or {"matched":false} |
find |
Collect all matches: indexes, full match text, numbered group captures, and named group groups |
Array; returns [] if there are zero matches |
replace |
Perform global safe substitution, return the result text and substitution count | {"result":"...","replaced":1} |
explain |
Static analysis of the pattern, output a human-readable node sequence | Node array, for example {"kind":"escape","text":"\\d","meaning":"A digit [0-9]"} |
Some key behaviors need to be remembered separately:
1. test will not automatically add start/end anchors. To match the entire string, the model must explicitly write ^...$ in the pattern.
2. find and replace will automatically add the g flag if it is not present, otherwise only the first match will be retrieved.
3. explain is a unique capability: it only runs a linear tokenizer, does not construct a RegExp instance or execute any matching operations, so it is naturally immune to ReDoS. The node limit is 4,096; if exceeded, it returns regex: explain: pattern too complex.
Three reproducible examples from the README are as follows.
Extract capture groups:
regex { action: "find", pattern: "(\\w+)@(\\w+)", input: "a@b x c@d" }
This returns two matches: the first is roughly {"index":0,"match":"a@b","captures":["a","b"],"groups":null}, and the second starts at index 6, corresponding to c@d.
Swap two words:
regex { action: "replace", pattern: "(\\w+) (\\w+)", input: "hello world", replacement: "$2 $1" }
This yields {"result":"world hello","replaced":1}.
Explain a date fragment:
regex { action: "explain", pattern: "\\d{4}-\\d{2}" }
This will split the pattern into nodes such as escaped \d, quantifier {4}, literal -, etc., with an English meaning field attached.
Tool Parameters¶
| Parameter | Required | Description |
|---|---|---|
action |
Yes | test / find / replace / explain |
pattern |
Yes | JavaScript regex syntax, do not include surrounding /; maximum size 16KB |
input |
Required for test/find/replace |
Text to match; maximum size 64,000 bytes (UTF-8) |
flags |
No | e.g. "gi"; allows g i m s u y d v, must be unique and valid |
replacement |
Required for replace |
Substitution text, follows JavaScript’s native string replacement rules; maximum size 16KB |
limit |
No | Maximum number of matches to report for find, default 50, capped at 1,000 |
The flags parameter is validated character by character: invalid characters will return regex: invalid flag "q", and duplicate flags will return regex: duplicate flag "g". Invalid patterns will catch SyntaxError, usually with position information in the error message formatted as regex: invalid pattern: ..., and will not crash the host process.
The replace operation explicitly follows the string replacement path of String.prototype.replace, with no new Function or eval calls. $1 / $2 refer to numbered groups, $$ represents a literal $, named groups follow JavaScript’s native $<name> semantics; unknown references are preserved literally per V8 rules (e.g. $0).
Multi-layer ReDoS Defenses¶
Catastrophic backtracking in JavaScript regular expressions is a real threat, with a typical example being (a+)+$ paired with a long input string. The repository README and tool description both document the same set of defenses, which correspond to the code in src/index.ts and src/engine.ts:
1. Worker hard timeout: test / find / replace operations run synchronously in a terminable worker thread, with a budget of 1,000ms. If the time expires, worker.terminate() is called, returning regex: execution timed out (1000ms). The tool’s own timeoutMs is only cooperative for synchronous blocking bodies, so a separate worker is necessary.
2. Entry rejection without truncation: If the input exceeds 64KB, the pattern exceeds 16KB, or the replacement text exceeds 16KB, an error is returned directly without triggering backtracking.
3. Output and match count limits: If the output exceeds 1MB (e.g. substitution amplification caused by $`` /$’), it is rejected instead of truncated; the defaultlimitforfindis 50, with an upper cap of 1,000.
4. **explain` executes zero matching operations**: Only static scanning is performed, and any pattern returns results immediately.
Both the tool description and README warn users: Do not use unanchored nested quantifiers with untrusted large inputs, such as (a+)+ or (.*)*. While the timeout can prevent the host process from hanging, it does not turn pathological patterns into “safe to use”.
Installation and Activation¶
The installation command given on the directory page is (per the original page text):
dsh plugin add github:omdsh-dev/dsh-tool-regex
For reproducible installations, pin the commit hash:
dsh plugin add github:omdsh-dev/dsh-tool-regex#commit
Replace #commit with the actual commit hash. The latest commit on the repository’s main branch as of 2026-08-14 is 457c84fed7849003dd006145fe7838519c8fc132. Pinning the commit hash ensures that upstream pushes will not silently change the code running on your machine.
The README recommends installing per profile. The web (interactive web UI) and headless (default for dsh run) profiles are separate, and installing to one will not automatically affect the other:
# Interactive (web) profile
dsh plugin --profile web add github:omdsh-dev/dsh-tool-regex
# One-off task (headless) profile
dsh plugin --profile headless add github:omdsh-dev/dsh-tool-regex
You can also run npm pack in the repository first, then install using the generated tarball:
npm pack
dsh plugin --profile web add ./dsh-tool-regex-<version>.tgz
The cordis.patch.yml file in the package will insert the plugin into the layer stack with row ID tool-regex after installation. Missing peer dependencies will be installed via fallback from the profile’s profiles/node_modules. Use forward slashes for Windows paths, e.g. C:/....
The Node engine declared in package.json is ^22.19.0 || >=24.0.0. The README states that this plugin has been verified for isolated consumption with @deepseek-ai/dsh@0.1.0-rc.6 (npm private package), with an example startup command of npx -p @deepseek-ai/dsh@0.1.0-rc.6 dsh web, and explicitly warns against global installation with install -g. This is the compatibility line documented in the repository, not a guarantee for all dsh snapshots.
Verify that the plugin is installed:
dsh --profile web --dump-config | grep tool-regex
Run a real test call:
dsh run "Use the regex tool to test if d+ matches abc123"
Typical Usage¶
Following the contract in the README, map the four actions to common tasks below. All patterns are written in JavaScript syntax, without surrounding /.../ slashes.
1. Explain first, then execute. When a user provides a regex that you do not understand, run explain first. It does not perform matching, only returning a sequence of nodes, which is suitable for displaying “what this pattern does” to humans. Unclosed [ / ( will return a position-specific error, e.g. regex: explain: unmatched "[" at position N.
2. Extract fields from logs. Use find. Use (?<name>...) syntax when you need named capture groups, and the groups field in the results will include the names; use the captures field when only numbered groups are needed. The default limit is 50, so explicitly set a smaller value for very long logs to avoid bloated output.
3. Perform auditable substitutions. Use replace, relying on references like $1 instead of having the model write manual string concatenation. If there are zero matches, the original text is returned with replaced set to 0, making it easy to confirm whether any changes were made.
4. Check for a match. Use test. To check if the input is an exact full match instead of a substring match, add ^ and $ to the pattern yourself. test will not automatically add the g flag, avoiding lastIndex state from skewing subsequent checks.
An empty pattern is valid (it matches empty strings). When using the u / v flags, empty matches advance by code points to avoid matching UTF-16 surrogate pairs twice. These boundaries are implemented in src/engine.ts per the ECMAScript AdvanceStringIndex specification, and the test file engine.spec.ts covers flags, the 64KB input limit, and worker cancellation for pathological patterns.
Applicable Scenarios and Notes¶
This plugin is suitable for the following situations:
- An agent needs to validate a regex provided by a user and provide a displayable explanation, instead of verbally guaranteeing “this works”
- Extract fields from in-memory text (log snippets, form values, strings just generated by the model), instead of searching workspace files — searching files should still use the built-in grep
- Need substitution with capture groups, and do not want the agent to write node -e scripts on the fly
- Worried that pathological regular expressions will hang the host process, and need hard timeouts and input limits
Situations where it is not suitable, or requires extra caution:
- Pairing untrusted long inputs with unanchored nested quantifiers. The timeout will terminate the worker, but the call will still fail.
- Treating explain as a full regex semantic engine. It is only a tokenizer: it can recognize anchors, character classes, groups, quantifiers, escapes, and alternations, with English meaning descriptions; it will directly reject patterns with more than 4,096 nodes.
- Only installing the web profile but using dsh run for one-off tasks. dsh run defaults to the headless profile, so plugins need to be installed separately for each profile.
- Misinterpreting the directory page or scoped package name as an official product. The maintainer is omdsh-dev, the license file copyright belongs to whiteicey 2026, and the @deepseek-ai/ prefix in the package name is a common convention for DSH plugins, not an indication of official release by DeepSeek.
The security prompt on the directory page must be followed: The plugin runs with the permissions of the current dsh process, and may execute code during installation. Review the source code repository and license before installing; pin the commit hash for reproducible installations. This is not just a formality — installing from the GitHub source pulls code directly instead of prebuilt artifacts, so the trust boundary is the dsh process running on your local machine.
Summary¶
dsh-tool-regex wraps “performing regex operations on arbitrary text” into a deterministic tool: test for matching checks, find for extraction, replace for safe substitution, and explain for static interpretation. Compared to having the model write scripts and pass them to bash, it eliminates one layer of correctness risk; compared to the built-in grep, it does not depend on files. The key boundaries to watch for are the 1-second worker hard timeout, input/pattern/output size limits, and the fact that explain does not execute any matching code.
Directory page: https://deepseek-harness-plugin.com/zh-CN/plugins/dsh-tool-regex/
GitHub: https://github.com/omdsh-dev/dsh-tool-regex