Module: Pikuri::Command::Renderer
- Defined in:
- lib/pikuri/command/renderer.rb
Overview
Expands a Pikuri::Command::Registry::Command into the text a host sends as the user's turn. Stateless.
body: "Weekly log for $ARGUMENTS, Monday through today."
expand(cmd, arguments: 'swingai')
# => "Weekly log for swingai, Monday through today."
A body that only mentions the token keeps it — the span is code — and
the arguments arrive in the tail instead, which is exactly what
~/.claude/commands/wh.md does:
body: "Read the label off `$ARGUMENTS`."
expand(cmd, arguments: 'swingai')
# => "Read the label off `$ARGUMENTS`.\n\n\nARGUMENTS: swingai"
The result is a plain user message — the interloper's :user kind, not
its :system one: /wh swingai is the human's request typed short, so
on_user_message must fire (a memory layer that never sees it is a silent
hole) and it must not arrive wrapped as reference text.
agent.run_loop(user_message: text) # synchronous host
interloper.(text) # async host, mid-loop
What gets substituted
$ARGUMENTS is everything typed after the command name, verbatim.
+$1+…+$9+ index that string split on whitespace, 1-based — the one
place this diverges from Claude Code 2.1.233, which is 0-based in the
binary while being 1-based in its own docs and in every body anyone
writes. An out-of-range $n stays literal, as it does there: a
placeholder that silently vanishes is worse than one a human can see in
the transcript.
There is no quote handling and no flag parsing: /foo "two words" is
three tokens. A body that wants either reads $ARGUMENTS, which is what
it meant.
Fenced blocks and code spans are skipped
Substitution ignores ``` blocks and <code>inline spans</code>, because a false
positive here is silent corruption of an instruction: a body writing
about its own arguments ("read it off <code>$ARGUMENTS</code> rather than +$1+")
would otherwise have that sentence rewritten into nonsense, and a body
emitting a shell script would have its "$1" hardcoded to one
invocation.
Note that Registry's !cmd</code> detection is deliberately
fence-blind while this is fence-aware. Not an inconsistency: they
answer different questions about the same characters, and the failure
directions are opposite — a false positive there is a loud exception on
an editable file, here it is silent damage. Don't "fix" one to match the
other.
The two tails
When the body references neither placeholder anywhere outside code, the
arguments are appended as ARGUMENTS: <raw> — Claude Code's exact
spelling, so a body authored against it reads identically here. That
rule and the skipping above are one pass over one tokenization on
purpose: read the body blind and a file whose every $ARGUMENTS is
backticked counts as "referenced", gets no append, gets no substitution,
and silently loses what the human typed.
A (this command lives in …) footer follows when the command has
sidecars, giving the model a base directory to resolve a path against —
since @path inlining is deliberately not implemented, a path in the
body stays literal text and reaches the file only through the gated
read tool.
Class Method Summary collapse
-
.expand(command, arguments: '') ⇒ String
The user turn to send.
Class Method Details
.expand(command, arguments: '') ⇒ String
Returns the user turn to send.
79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/pikuri/command/renderer.rb', line 79 def (command, arguments: '') raw = arguments.to_s tokens = raw.split segments = segment(command.body) text = segments.map { |chunk, code| code ? chunk : substitute(chunk, raw, tokens) }.join unless raw.strip.empty? || segments.any? { |chunk, code| !code && references?(chunk) } text = "#{text.rstrip}\n\n\nARGUMENTS: #{raw}" end text = "#{text.rstrip}\n\n(this command lives in #{File.dirname(command.location)})" if command.sidecars? text end |