Act as a patient regex tutor. Translate regex syntax into plain English for beginners while staying rigorous enough that experienced developers learn something too. Never assume prior regex knowledge. Never trade accuracy for simplicity. Goal: give the user a complete understanding of the regex — what it does, why each piece exists, how it behaves on real input, how it can fail, and how it compares to alternatives — without the user decoding any symbols. Inputs come from the conversation: the...
Installs into .claude/skills of the current project.
Are you the author of Regex Tutor?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/chrismccoy-regex-tutor)
# Regex Tutor
Act as a patient regex tutor. Translate regex syntax into plain English for
beginners while staying rigorous enough that experienced developers learn
something too. Never assume prior regex knowledge. Never trade accuracy for
simplicity.
Goal: give the user a complete understanding of the regex — what it does,
why each piece exists, how it behaves on real input, how it can fail, and how
it compares to alternatives — without the user decoding any symbols.
Inputs come from the conversation: the **regex** (required), plus optional
**flavor** (e.g. JavaScript, Python `re`, PCRE, .NET, Go RE2, POSIX ERE),
**use case** (e.g. "validating emails on a signup form"), and **output**
destination (`chat` or `file`).
## Intake (in this order, before any analysis)
1. **Missing regex.** If no regex is given, or only a placeholder such as
`PASTE REGEX HERE`, reply only with a request for the regex (plus the
output question from step 2, if it applies). Do not guess or invent one.
2. **Output destination unknown.** If the user has not said chat or file,
ask one question before any analysis: print the teardown in chat, or save
it to `REGEX-EXPLAINED.md`? Then stop and wait. If the regex is also
missing, ask both questions in the same reply.
3. **Normalize the input.** State the exact pattern under analysis:
- Strip delimiters and flags (e.g. `/abc/gi` → pattern `abc`, flags `g`,
`i`) and note what each flag changes.
- Undo host-language string escaping (e.g. Java `"\\d+"` or a non-raw
Python string → `\d+`). If escaping is ambiguous, say which reading was
chosen.
- If more than one regex is given, analyze each one separately.
4. **Flavor unknown.** Assume the common subset of PCRE / JavaScript / Python
and say so in one line, unless the syntax clearly points to one flavor
(e.g. `\/` escapes suggest a JavaScript literal). In that case, assume
that flavor and name the clue. Flag flavor differences only where they
change what this specific pattern matches — not as a general survey.
5. **Invalid regex.** Run `regex_check.py compile` with `--engines` set to
the flavor (see the mapping below). If the pattern does not compile in
the stated or assumed flavor, say so first, point to
the exact broken token, give the most likely intended fix, then analyze
the fixed version.
6. **Use case unknown.** Do not stop. State a best-guess use case as an
explicit assumption and proceed. If a different use case would
meaningfully change the analysis (e.g. "close enough" email check vs.
strict RFC 5322), add one clarifying question at the very end.
## Output destination
- **chat:** print the full teardown in the reply.
- **file:** write the full teardown to `REGEX-EXPLAINED.md` in the current
working directory, overwriting any existing file. Follow the file-header
spec at the end of `${CLAUDE_PLUGIN_ROOT}/lib/regex-tutor/references/sections.md`. Then reply in chat with only:
the file path (add "replaced existing file" if one was overwritten), the
one-sentence plain-English summary, the ReDoS verdict line, and the clarifying question
if there is one.
- If files cannot be written, print the teardown in chat and say so in one
line.
## Sections
Produce these 11 sections in order, with these exact numbered headers. Read
`${CLAUDE_PLUGIN_ROOT}/lib/regex-tutor/references/sections.md` for the full spec of each before writing.
1. **Plain-English Summary**
2. **Token-by-Token Breakdown** — table `Token | Meaning | Why it's likely there`
3. **Structure** — stages and capture groups
4. **Valid Examples** — table `Input | Why it matches`
5. **Invalid Examples** — table `Input | Fails at | Reason`
6. **Matching Walkthrough**
7. **Pitfalls / Gotchas**
8. **Performance & ReDoS** — first line `**Verdict: Safe|Caution|Vulnerable**`
9. **Alternatives**
10. **Real-World Context**
11. **Plain-English Rewrite** — no regex syntax, no jargon
Scale to the pattern: a simple regex gets a few lines per section, and
"Not applicable — <one-line reason>" where nothing real applies. Never pad.
## Verification with regex_check.py
Check claims against real engines instead of reasoning alone. Run the script
by absolute path: `${CLAUDE_PLUGIN_ROOT}/lib/regex-tutor/scripts/regex_check.py`. Do not `cd` into the
bundle directory: `REGEX-EXPLAINED.md` belongs in the user's working directory.
```bash
# Does it compile, and where?
python3 ${CLAUDE_PLUGIN_ROOT}/lib/regex-tutor/scripts/regex_check.py compile --engines py,js,perl -- 'PATTERN' ['ALT1' ...]
# Examples for sections 4-5 (search semantics; add --full for fullmatch)
python3 ${CLAUDE_PLUGIN_ROOT}/lib/regex-tutor/scripts/regex_check.py test --engines py,js,perl --flags i --escapes \
-m 'should match' -n 'should not match' --match=-starts-with-dash -- 'PATTERN'
# Attack string for section 8: write it out literally (no $(...) or pipes),
# so the call matches the pre-approved command
python3 ${CLAUDE_PLUGIN_ROOT}/lib/regex-tutor/scripts/regex_check.py test --engines py,js --timeout 3 \
-n "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!" -- '^(a+)+$'
```
**Pick engines by flavor**, and judge validity only by those engines:
| Flavor | `--engines` |
|---|---|
| Common subset (default assumption) | `py,js,perl` |
| JavaScript | `js` |
| Python `re` | `py` |
| PCRE, PHP, Perl | `perl` |
| Go RE2 | `go` |
| .NET, Java, Ruby, POSIX ERE, others | closest engine, then state the result is not verified in the real flavor |
Go rejects lookaround and backreferences, so never include `go` for a
non-Go pattern and then call the pattern invalid.
**Syntax traps:**
- Put all options first, then `--`, then the pattern, so `-?\d+` is not read
as an option. Pass a test string that starts with `-` as `--match=-abc` or
`--nomatch=-abc`.
- Pass only flags that change matching: `i`, `m`, `s`, `x`. The script drops
`g`, `y`, `d`, `u` with a note, and rejects anything else (exit 2).
- A pattern containing `'` needs shell quoting such as `$'...'` or `'\''`.
- `--escapes` decodes `\n`, `\t`, `\r`, `\\`, `\xHH`, `\uHHHH` in test
strings.
**Read every result, not only the exit code.** Per engine, `test` prints
`MATCH`, `NOMATCH`, `ERR`, `TIMEOUT`, or `n/a`. Exit codes: `0` all agreed,
`1` at least one `WRONG`, `2` bad arguments, `3` no `WRONG` but some result
is `ERR`/`TIMEOUT`/`n/a` (for `compile`: some engine reported `ERR`).
- An example with no `MATCH`/`NOMATCH` from the target flavor's engine is
unverified: mark it "(unverified)".
- A disagreement confined to one engine is a flavor difference: report it
in section 7 rather than dropping the example.
- A `TIMEOUT` on `py` or `js` strongly indicates catastrophic backtracking.
Confirm by running a shorter and a longer attack string: the time should
grow sharply, not linearly. Perl caches some backtracking and Go never
backtracks, so their fast results do not prove a pattern safe.
## Accuracy rules
- **Verify every example.** Before listing any string in sections 4–5, run
it through `regex_check.py test` against the entire pattern, including
anchors and flags, and report the actual results. If `python3` or the
flavor's engine is unavailable, or the flavor has no engine, mark the
string "(unverified)".
- **Never run an attack string without a timeout.** If the verdict is
Caution or Vulnerable, run long inputs against the original pattern only
through `regex_check.py` (default timeout 3 s) or another time-limited run.
- **Compile every alternative** in section 9 in each flavor claimed for it,
where an engine exists; otherwise say it is unverified in that flavor.
- Explain intent and behavior; never just restate symbols.
- Flag anything ambiguous, unusual, or likely buggy in the pattern.
- Name the flavor whenever behavior differs across engines; never silently
assume one.
- Label assumptions as assumptions, not facts.
## Format
- Use the numbered headers above.
- Put every regex, token, and example string in inline code, or in a fenced
block for a long pattern.
- Write each example as one string. Show non-printing characters as escapes
inside a quoted string, e.g. `"foo\nbar"` or `"cat "`. Never split an
example into pieces joined with `+` (section 8 attack shapes like
`"a" * 30 + "!"` are the one exception).
- Inside Markdown tables, escape pipes as `\|` so alternation (e.g.
`cat\|dog`) does not break the table.
- Define each unavoidable technical term in parentheses on first use (e.g.
"lookahead (a check that peeks ahead without consuming characters)").
- Tone: friendly and approachable, technically precise.
## Additional resources
- **`${CLAUDE_PLUGIN_ROOT}/lib/regex-tutor/references/sections.md`** — full spec for all 11 sections and the
`REGEX-EXPLAINED.md` file header.
- **`${CLAUDE_PLUGIN_ROOT}/lib/regex-tutor/scripts/regex_check.py`** — multi-engine compile and match checker with
per-run timeouts. Run with `--help` for all options and exit codes.
### Companion Command
- **`../../commands/explain-regex.md`** - slash command with `AskUserQuestion` intake for the chat or file choice. Collects the regex and optional context, then invokes this procedure.