fix(commands): Phase 0 gives the arguments to the parser on stdin, never as shell words

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-26 15:55:21 +01:00
commit 2c0a98c1ba
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
8 changed files with 157 additions and 29 deletions

View file

@ -32,9 +32,16 @@ See **Handover 7** in `docs/HANDOVER-CONTRACTS.md` for the full schema.
Parse `$ARGUMENTS` with the shared arg-parser. Run first, exactly like this:
```bash
node ${CLAUDE_PLUGIN_ROOT}/lib/parsers/arg-parser.mjs --command trekcontinue -- $ARGUMENTS
node ${CLAUDE_PLUGIN_ROOT}/lib/parsers/arg-parser.mjs --command trekcontinue --stdin <<'VOYAGE_ARGS_EOF_7F3C9E'
$ARGUMENTS
VOYAGE_ARGS_EOF_7F3C9E
```
The arguments reach the parser as the one line inside the quoted heredoc, on
stdin, never as shell words. It prints the parse result as JSON. On exit 1,
print its `errors` and stop. When it prints no JSON, say that the parser did
not run and stop.
Read `flags` and `positional` from the JSON it prints. Dispatch the usage
block ONLY when one of these two conditions equals exactly true (no substring
search, no "contains" check):

View file

@ -31,14 +31,21 @@ See **Handover 7** in `docs/HANDOVER-CONTRACTS.md` for the schema.
## Phase 0 — `--help` handling
Parse `$ARGUMENTS` with the arg parser. Run this via the Bash tool at runtime
(NOT eager-exec):
(NOT eager-exec), exactly as written: the arguments reach the parser as the one
line inside the quoted heredoc, on stdin, never as shell words:
```bash
setopt nonomatch 2>/dev/null || true
node ${CLAUDE_PLUGIN_ROOT}/lib/parsers/arg-parser.mjs --command trekendsession -- $ARGUMENTS
node ${CLAUDE_PLUGIN_ROOT}/lib/parsers/arg-parser.mjs --command trekendsession --stdin <<'VOYAGE_ARGS_EOF_7F3C9E'
$ARGUMENTS
VOYAGE_ARGS_EOF_7F3C9E
```
The `setopt nonomatch` line is no longer needed for the arguments (the quoted
heredoc expands no glob); it stays harmless.
It prints the parse result as JSON. On exit 1, print its `errors` and stop.
When it prints no JSON, say that the parser did not run and stop.
Print the usage block below and exit cleanly ONLY when one of these two
conditions equals exactly true (no substring search, no "contains" check):
@ -95,8 +102,9 @@ Decision tree:
## Phase 2 — Required args check (headless-safe)
Read the parse result from Phase 0: `<next-brief-path>` is `positional[0]`
and `<next-label>` is `positional[1]`. Both are required. If either is
missing or empty:
and `<next-label>` is the parser's `tail` (the rest of the text as typed after
the brief path, one outer quote pair removed, so a label with spaces stays
whole). Both are required. If either is missing or empty:
```
Error: missing required args.

View file

@ -46,10 +46,13 @@ command executes inline.
## Phase 1 — Parse mode and validate input
Parse the arguments with the shared arg-parser. Run it first, exactly like
this, before anything else:
this, before anything else (the arguments reach the parser as the one line
inside the quoted heredoc, on stdin, never as shell words):
```bash
node ${CLAUDE_PLUGIN_ROOT}/lib/parsers/arg-parser.mjs --command trekreview -- $ARGUMENTS
node ${CLAUDE_PLUGIN_ROOT}/lib/parsers/arg-parser.mjs --command trekreview --stdin <<'VOYAGE_ARGS_EOF_7F3C9E'
$ARGUMENTS
VOYAGE_ARGS_EOF_7F3C9E
```
It prints the parse as JSON on stdout. It also checks the required flag