Commit 93bb63c
feat: --prompt-file flag to read long prompts from disk (v1.2.4) (#13)
Add a new `--prompt-file <path>` argument to `/watchdog:start` so users
can pass multi-paragraph prompts (Markdown task specs with headings,
code fences, quoted strings, etc.) without mangling them through shell-
argument escaping.
## Problem
Slash command `!` shell blocks invoke setup-watchdog.js via `$ARGUMENTS`
literal substitution. Any unescaped `"`, backtick, `$`, or newline in
the user's inline prompt breaks bash parsing with `unexpected EOF`. In
practice this hits every realistic multi-paragraph Markdown prompt the
moment a user pastes it into `/watchdog:start "..."`.
ralph-loop (from which watchdog is derived) has the same failure mode
and does not provide a file or stdin fallback.
## Solution
New `--prompt-file <path>` flag in scripts/setup-watchdog.js. The file
is read directly with Node's `fs.readFileSync`, bypassing shell
argument parsing entirely. Mutually exclusive with an inline `<PROMPT>`
positional — pick one or the other.
Path handling is delegated to `path.resolve()`, which is platform-
aware:
- Linux/macOS/WSL POSIX paths (absolute + relative, `./name`, `../name`)
- Windows `C:\...`, `C:/...`, and UNC `\\server\share\...` paths
- Relative paths resolve against `process.cwd()` on every platform
Leading UTF-8 BOM is stripped automatically — Windows Notepad and
PowerShell's default `Set-Content` silently prepend U+FEFF to UTF-8
files, and `.trim()` does not remove it (BOM is not whitespace), so
without this Claude would see an invisible zero-width marker as the
first character of the prompt.
CRLF line endings inside the content are preserved byte-for-byte. `~`
is NOT expanded by watchdog — that is the shell's job, and bash/zsh
already expand it before the args reach the script. `cmd.exe` users
should pass an absolute path or `%USERPROFILE%\...`.
Error paths return clean messages:
- ENOENT => "prompt file not found: <resolved>"
- EISDIR => "--prompt-file expects a file, got a directory: <resolved>"
- EACCES/EPERM => "permission denied reading prompt file: <resolved>"
- empty file => "prompt file is empty: <resolved>"
- missing arg => "--prompt-file requires a path argument"
- combined with inline => "--prompt-file cannot be combined with a
positional prompt"
## What changed
- scripts/setup-watchdog.js — parseArgs() recognizes --prompt-file,
new readPromptFile() helper centralizes file read + BOM strip +
error mapping, main() enforces mutual exclusion
- commands/start.md — argument-hint updated to show the alternate
form
- commands/help.md — new --prompt-file option with the full path-
handling reference (POSIX / Windows / UNC / BOM / CRLF / spaces /
encoding / ~ expansion)
- test/setup.test.js — +17 tests: happy path, shell metacharacters
in content, whitespace trimming, UTF-8 BOM strip, relative path,
./name relative path, CRLF content, non-ASCII filename, symlink
following (non-Windows), permission denied (non-Windows non-root),
missing file, directory target, empty file, missing path arg,
mutual exclusion with inline, help text listing new form
- README.{md,zh,es,ja,ko,vi,pt}.md — new "Long prompts from a file"
subsection under Commands, plus a new "Prompt input" row in the
Watchdog vs ralph-loop comparison table
- .claude-plugin/plugin.json — version => 1.2.4
- .claude-plugin/marketplace.json — version => 1.2.4 (both the
plugin entry and the marketplace top-level)
## Test plan
- [x] 96-test suite passes locally (94 active + 2 skipped-inside-
Claude-Code, 0 failures)
- [x] setup.test.js: 27 tests — 26 active + 1 pre-existing skip
- [x] Root cause debugged by reproducing the real bash `unexpected
EOF` on a pasted multi-paragraph Markdown prompt
- [ ] CI will confirm on the standard matrix (ubuntu/macos/windows
× Node 18/20/22)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent cc2d6d0 commit 93bb63c
13 files changed
Lines changed: 438 additions & 7 deletions
File tree
- .claude-plugin
- commands
- scripts
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
| 13 | + | |
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | | - | |
| 33 | + | |
34 | 34 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
117 | 117 | | |
118 | 118 | | |
119 | 119 | | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
120 | 132 | | |
121 | 133 | | |
122 | 134 | | |
| |||
414 | 426 | | |
415 | 427 | | |
416 | 428 | | |
| 429 | + | |
417 | 430 | | |
418 | 431 | | |
419 | 432 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
118 | 118 | | |
119 | 119 | | |
120 | 120 | | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
121 | 133 | | |
122 | 134 | | |
123 | 135 | | |
| |||
415 | 427 | | |
416 | 428 | | |
417 | 429 | | |
| 430 | + | |
418 | 431 | | |
419 | 432 | | |
420 | 433 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
118 | 118 | | |
119 | 119 | | |
120 | 120 | | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
121 | 133 | | |
122 | 134 | | |
123 | 135 | | |
| |||
415 | 427 | | |
416 | 428 | | |
417 | 429 | | |
| 430 | + | |
418 | 431 | | |
419 | 432 | | |
420 | 433 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
117 | 117 | | |
118 | 118 | | |
119 | 119 | | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
120 | 132 | | |
121 | 133 | | |
122 | 134 | | |
| |||
414 | 426 | | |
415 | 427 | | |
416 | 428 | | |
| 429 | + | |
417 | 430 | | |
418 | 431 | | |
419 | 432 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
117 | 117 | | |
118 | 118 | | |
119 | 119 | | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
120 | 132 | | |
121 | 133 | | |
122 | 134 | | |
| |||
414 | 426 | | |
415 | 427 | | |
416 | 428 | | |
| 429 | + | |
417 | 430 | | |
418 | 431 | | |
419 | 432 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
117 | 117 | | |
118 | 118 | | |
119 | 119 | | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
120 | 132 | | |
121 | 133 | | |
122 | 134 | | |
| |||
414 | 426 | | |
415 | 427 | | |
416 | 428 | | |
| 429 | + | |
417 | 430 | | |
418 | 431 | | |
419 | 432 | | |
| |||
0 commit comments