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.inject_user_message(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

Class Method Details

.expand(command, arguments: '') ⇒ String

Returns the user turn to send.

Parameters:

Returns:

  • (String)

    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 expand(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