You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(matcher): match sigil-prefixed positionals (fixes#361)
dig ns foo.bar @8.8.8.8 explained @8.8.8.8 with the wrong positional's
text: positionals were assigned purely by order, so the @-token landed
on whatever was next in line.
Option gains a prefix field — a literal sigil a token must start with
for that positional to claim it (dig's [@server]). Prefixed positionals
leave ordered consumption entirely: the matcher tries the prefix pool
first, then falls back to today's ordered/variadic logic, so ns and
foo.bar now correctly consume name and type.
The LLM prompt asks for the sigil from the synopsis; both sanitize
sites (response.py, postprocess.py) restrict it to a corpus-grounded
allowlist (@, +, :) so optional sub-components like ssh's
[user@]hostname can't knock a popular operand out of ordered matching.
Eval on the 12-page corpus: zero false-positive prefix emissions,
including ssh.
The e2e fixture db is rebuilt with llm:codex/gpt-5.4/medium (gpt-5.2
reproducibly dropped dig's positionals) in one parallel pass; snapshots
refreshed for the richer help-text markdown and new positional help
boxes, plus a new test pinning the issue-361 command end-to-end.
biome now skips .claude/ (agent worktrees with nested configs) and the
untracked parse-conformance corpus so make lint reflects repo code.
-**mapping** - command name → manpage id lookup (many-to-one, with score for preference)
195
195
196
196
Key classes (Pydantic models in models.py):
197
-
-`Option` - text, short/long flag lists, has_argument, positional, nested_cmd
198
-
-`ParsedManpage` - container with options/positionals properties and `find_option(flag)` lookup
197
+
-`Option` - text, short/long flag lists, has_argument, positional, prefix (literal sigil a token must start with for a positional to claim it, e.g. `@` in dig's `[@server]`; restricted to the `OPTION_PREFIX_SIGILS` allowlist `@`/`+`/`:`), nested_cmd
198
+
-`ParsedManpage` - container with options/positionals/prefixed_positionals properties and `find_option(flag)` lookup; `positionals` excludes prefix-bearing options, which are exposed via `prefixed_positionals` (name → (prefix, text))
199
199
200
200
### Command Matching (matcher.py)
201
201
202
202
Uses bashlex AST visitor pattern:
203
203
-`Matcher` inherits from `bashlex.ast.nodevisitor`
204
204
-`visitcommand()` - looks up man page, handles multi-command (e.g., `git commit`)
205
205
-`visitword()` - matches tokens to options (exact match, then fuzzy split for combined short flags like `-abc`)
206
+
- Positional operands use two pools: prefixed positionals are claimed only by tokens starting with their sigil (e.g. `@8.8.8.8` → dig's `server`); remaining tokens consume the non-prefixed positionals in order, reusing the last one (variadic). A token whose sigil no positional declares falls through to ordered consumption; if all positionals are prefixed and none match, the token is unknown.
206
207
- Produces `MatchResult(start, end, text, match)` where start/end are character positions in the original string
0 commit comments