@juicesharp/rpiv-args 1.1.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/args.ts +51 -14
- package/index.ts +3 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
[](https://www.npmjs.com/package/@juicesharp/rpiv-args)
|
|
12
12
|
[](https://opensource.org/licenses/MIT)
|
|
13
13
|
|
|
14
|
-
Pass arguments to your skills like a shell command. `rpiv-args` adds `$1`, `$ARGUMENTS`, `$@`, `${@:N}`, and `${@:N:L}` placeholders to [Pi Agent](https://github.com/badlogic/pi-mono) skills
|
|
14
|
+
Pass arguments to your skills like a shell command. `rpiv-args` adds `$1`, `$ARGUMENTS`, `$@`, `${@:N}`, and `${@:N:L}` placeholders to [Pi Agent](https://github.com/badlogic/pi-mono) skills - write `/skill:deploy api production` and your skill body sees `$1` = `api`, `$2` = `production`. Skills without placeholders are untouched, so installing `rpiv-args` is safe for any existing skill collection.
|
|
15
15
|
|
|
16
16
|
## Install
|
|
17
17
|
|
|
@@ -31,7 +31,7 @@ Or run `/rpiv-setup` if you have `@juicesharp/rpiv-pi` installed.
|
|
|
31
31
|
| `${@:N}` | Arguments from position N onward | `/skill:foo a b c` → `${@:2}` = `b c` |
|
|
32
32
|
| `${@:N:L}` | L arguments starting at position N | `/skill:foo a b c d` → `${@:2:2}` = `b c` |
|
|
33
33
|
|
|
34
|
-
**Indexing is 1-based**
|
|
34
|
+
**Indexing is 1-based** - `$1` is the first argument, `$2` is the second.
|
|
35
35
|
Out-of-range positions resolve to an empty string. For `${@:N[:L]}`, `N` is
|
|
36
36
|
clamped to `≥ 1` and out-of-range slices yield an empty string.
|
|
37
37
|
|
|
@@ -51,14 +51,14 @@ expansion). When a skill body contains at least one placeholder, the extension:
|
|
|
51
51
|
1. Parses arguments using shell-style quoting
|
|
52
52
|
2. Substitutes all placeholders in the body
|
|
53
53
|
3. Wraps the result in a `<skill>` block byte-identical to Pi's native format
|
|
54
|
-
4. Appends the raw arguments after the block
|
|
54
|
+
4. Appends the raw arguments after the block - matches Pi's standard output so any tool that parses `<skill>` blocks continues to work unchanged
|
|
55
55
|
|
|
56
56
|
When no placeholders are found in the skill body, the output is byte-identical
|
|
57
|
-
to Pi's built-in expansion
|
|
57
|
+
to Pi's built-in expansion - zero behavioral change.
|
|
58
58
|
|
|
59
59
|
## Writing skills with arguments
|
|
60
60
|
|
|
61
|
-
### `$ARGUMENTS` vs `$1`
|
|
61
|
+
### `$ARGUMENTS` vs `$1` - which to use
|
|
62
62
|
|
|
63
63
|
Use **`$ARGUMENTS`** (or `$@`) when the input is freeform text the LLM should
|
|
64
64
|
interpret naturally:
|
|
@@ -104,7 +104,7 @@ If a positional skill receives natural language input:
|
|
|
104
104
|
/skill:migrate-component can you migrate the search bar please
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
→ `Migrate the can component from you to migrate.`
|
|
107
|
+
→ `Migrate the can component from you to migrate.` - **broken**.
|
|
108
108
|
|
|
109
109
|
The LLM is good at interpreting `$ARGUMENTS` as a whole, but positional
|
|
110
110
|
placeholders blindly split on spaces. Use `$ARGUMENTS` unless your skill has
|
|
@@ -130,17 +130,17 @@ argument-hint: [component] [from] [to]
|
|
|
130
130
|
---
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
rpiv-args ignores this field
|
|
133
|
+
rpiv-args ignores this field - substitution is triggered by placeholders in the body, not the hint.
|
|
134
134
|
|
|
135
135
|
**Note**: Pi currently surfaces `argument-hint` in autocomplete for prompt
|
|
136
136
|
templates (`commands/*.md`) but **not** for skills (`/skill:<name>`). The
|
|
137
137
|
field is read by Pi but not displayed in the `/skill:` autocomplete UI at
|
|
138
|
-
present
|
|
138
|
+
present - treat it as documentation metadata until upstream Pi exposes it.
|
|
139
139
|
|
|
140
140
|
### Full example
|
|
141
141
|
|
|
142
142
|
<details>
|
|
143
|
-
<summary>Deploy skill
|
|
143
|
+
<summary>Deploy skill - SKILL.md, invocation, and the exact text the LLM sees</summary>
|
|
144
144
|
|
|
145
145
|
```yaml
|
|
146
146
|
---
|
|
@@ -179,7 +179,7 @@ api production
|
|
|
179
179
|
```
|
|
180
180
|
|
|
181
181
|
Note: the raw arguments (`api production`) are also appended after the
|
|
182
|
-
`</skill>` block
|
|
182
|
+
`</skill>` block - this is Pi's standard behavior and is preserved for
|
|
183
183
|
backward compatibility.
|
|
184
184
|
|
|
185
185
|
</details>
|
package/args.ts
CHANGED
|
@@ -1,19 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* rpiv-args — core logic.
|
|
3
3
|
*
|
|
4
|
-
* Intercepts `/skill:<name> <args>` at the input hook and emits a
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Intercepts `/skill:<name> <args>` at the input hook and emits a Pi skill
|
|
5
|
+
* wrapper with opt-in $N/$ARGUMENTS/$@/${@:N[:L]} substitution on the body.
|
|
6
|
+
* Two emit paths:
|
|
7
|
+
* - No-token path: byte-identical to Pi's built-in `_expandSkillCommand`
|
|
8
|
+
* output (wrapper + `\n\n${args}` suffix), preserving full backward
|
|
9
|
+
* compatibility for skills without placeholders.
|
|
10
|
+
* - Token path: substitutes inside the body and INTENTIONALLY drops the
|
|
11
|
+
* trailing `\n\n${args}` suffix. The bare imperative outside the block
|
|
12
|
+
* hijacks LLM attention from the skill workflow; inside-only emission
|
|
13
|
+
* leaves the skill body as the sole user-message payload competing for
|
|
14
|
+
* attention. See architecture.md "System-Prompt Protocol & Token-Path
|
|
15
|
+
* Divergence".
|
|
16
|
+
*
|
|
17
|
+
* Also prepends a skill-invocation protocol to the system prompt every turn
|
|
18
|
+
* (via before_agent_start) so the LLM treats trailing text after `</skill>`
|
|
19
|
+
* as the skill's argument input rather than a separate imperative.
|
|
9
20
|
*
|
|
10
21
|
* Byte-exact wrapper requirement: parseSkillBlock regex at
|
|
11
22
|
* node_modules/@mariozechner/pi-coding-agent/dist/core/agent-session.js:40
|
|
12
|
-
* is the load-bearing contract. Do not reformat the
|
|
23
|
+
* is the load-bearing contract for the wrapper itself. Do not reformat the
|
|
24
|
+
* template literal below.
|
|
13
25
|
*/
|
|
14
26
|
|
|
15
27
|
import { readFileSync } from "node:fs";
|
|
16
28
|
import {
|
|
29
|
+
type BeforeAgentStartEvent,
|
|
30
|
+
type BeforeAgentStartEventResult,
|
|
17
31
|
type ExtensionAPI,
|
|
18
32
|
getAgentDir,
|
|
19
33
|
type InputEvent,
|
|
@@ -29,13 +43,13 @@ import {
|
|
|
29
43
|
// ---------------------------------------------------------------------------
|
|
30
44
|
|
|
31
45
|
/** Matches any placeholder Pi's substituteArgs would replace. Used as the
|
|
32
|
-
* opt-in gate: absent → pass through verbatim
|
|
46
|
+
* opt-in gate: absent → pass through verbatim. */
|
|
33
47
|
const TOKEN_REGEX = /\$(?:\d+|ARGUMENTS|@|\{@:\d+(?::\d+)?\})/;
|
|
34
48
|
|
|
35
|
-
/** Prefix Pi uses (`agent-session.js:829`). Single-space tokenisation
|
|
49
|
+
/** Prefix Pi uses (`agent-session.js:829`). Single-space tokenisation. */
|
|
36
50
|
const SKILL_PREFIX = "/skill:";
|
|
37
51
|
|
|
38
|
-
/** Re-entrancy guard
|
|
52
|
+
/** Re-entrancy guard. */
|
|
39
53
|
const WRAPPED_PREFIX = "<skill ";
|
|
40
54
|
|
|
41
55
|
// ---------------------------------------------------------------------------
|
|
@@ -151,13 +165,13 @@ function appendArgs(skillBlock: string, args: string): string {
|
|
|
151
165
|
export function handleInput(event: InputEvent): InputEventResult {
|
|
152
166
|
const text = event.text;
|
|
153
167
|
|
|
154
|
-
//
|
|
168
|
+
// Re-entrancy: already-wrapped text (from our own or any other
|
|
155
169
|
// extension's {action:"transform"}) passes through untouched.
|
|
156
170
|
if (text.startsWith(WRAPPED_PREFIX)) return { action: "continue" };
|
|
157
171
|
|
|
158
172
|
if (!text.startsWith(SKILL_PREFIX)) return { action: "continue" };
|
|
159
173
|
|
|
160
|
-
//
|
|
174
|
+
// Single-space tokenisation — byte-match Pi's indexOf(" ") at :831.
|
|
161
175
|
const spaceIndex = text.indexOf(" ");
|
|
162
176
|
const skillName = spaceIndex === -1 ? text.slice(SKILL_PREFIX.length) : text.slice(SKILL_PREFIX.length, spaceIndex);
|
|
163
177
|
const argsString = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1).trim();
|
|
@@ -173,17 +187,39 @@ export function handleInput(event: InputEvent): InputEventResult {
|
|
|
173
187
|
}
|
|
174
188
|
|
|
175
189
|
const { frontmatter } = parseFrontmatter<{ "argument-hint"?: string }>(content);
|
|
176
|
-
void frontmatter; // informational only in v1
|
|
190
|
+
void frontmatter; // informational only in v1
|
|
177
191
|
const body = stripFrontmatter(content).trim();
|
|
178
192
|
|
|
179
|
-
//
|
|
193
|
+
// Opt-in gate: if body has no token, emit byte-identical to Pi's :841.
|
|
180
194
|
if (!TOKEN_REGEX.test(body)) {
|
|
181
195
|
return { action: "transform", text: appendArgs(buildSkillBlock(entry, body), argsString) };
|
|
182
196
|
}
|
|
183
197
|
|
|
184
198
|
const parsed = parseCommandArgs(argsString);
|
|
185
199
|
const substituted = substituteArgs(body, parsed);
|
|
186
|
-
|
|
200
|
+
// Substitution consumes the args — do not also append them after </skill>.
|
|
201
|
+
// Bare trailing imperatives hijack LLM attention from the skill body. See architecture.md.
|
|
202
|
+
return { action: "transform", text: buildSkillBlock(entry, substituted) };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
// Skill-invocation protocol — prepended to the system prompt every turn via
|
|
207
|
+
// before_agent_start. See architecture.md for rationale and re-application
|
|
208
|
+
// semantics (agent-session.js:112-113 — Pi's canonical per-turn pattern).
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
export const SKILL_INVOCATION_PROTOCOL = `## Skill invocation protocol (CRITICAL)
|
|
212
|
+
|
|
213
|
+
A \`<skill name="..." location="...">...</skill>\` block in a user message is a structured invocation. Handle it as follows:
|
|
214
|
+
|
|
215
|
+
1. The block body defines the workflow you must execute. Follow it.
|
|
216
|
+
2. Any text after \`</skill>\` is the user's argument input to that skill — never a separate command, even when it reads as an imperative ("create X", "update Y", "delete Z").
|
|
217
|
+
3. Do not bypass the skill's workflow to act on trailing text directly. The user invoked the skill because they want the skill's workflow applied to that input.
|
|
218
|
+
|
|
219
|
+
`;
|
|
220
|
+
|
|
221
|
+
export function handleBeforeAgentStart(event: BeforeAgentStartEvent): BeforeAgentStartEventResult {
|
|
222
|
+
return { systemPrompt: SKILL_INVOCATION_PROTOCOL + event.systemPrompt };
|
|
187
223
|
}
|
|
188
224
|
|
|
189
225
|
// ---------------------------------------------------------------------------
|
|
@@ -192,6 +228,7 @@ export function handleInput(event: InputEvent): InputEventResult {
|
|
|
192
228
|
|
|
193
229
|
export function registerArgsHandler(pi: ExtensionAPI): void {
|
|
194
230
|
pi.on("input", (event) => handleInput(event));
|
|
231
|
+
pi.on("before_agent_start", (event) => handleBeforeAgentStart(event));
|
|
195
232
|
pi.on("session_start", (event) => {
|
|
196
233
|
if (event.reason === "reload" || event.reason === "startup") {
|
|
197
234
|
invalidateSkillIndex();
|
package/index.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* rpiv-args — Pi extension entry point.
|
|
3
3
|
*
|
|
4
|
-
* Registers the `input` event handler
|
|
5
|
-
* All logic lives in
|
|
4
|
+
* Registers the `input` event handler, a `before_agent_start` system-prompt
|
|
5
|
+
* augmenter, and a `session_start` cache invalidator. All logic lives in
|
|
6
|
+
* args.ts.
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juicesharp/rpiv-args",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Pi extension
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Pi extension. Shell-style $1 and $ARGUMENTS placeholders, expanded into your Pi skills at invocation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
7
7
|
"pi-extension",
|