pi-skill-playbook 0.1.3 → 0.1.5
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 +141 -51
- package/extensions/index.ts +114 -43
- package/package.json +1 -1
- package/src/auto-advance.ts +6 -6
package/README.md
CHANGED
|
@@ -1,33 +1,39 @@
|
|
|
1
1
|
# Pi Skill Playbook
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/eiei114/pi-skill-playbook/actions/workflows/auto-release.yml)
|
|
4
|
+
[](https://github.com/eiei114/pi-skill-playbook/actions/workflows/publish.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/pi-skill-playbook)
|
|
6
|
+
[](https://www.npmjs.com/package/pi-skill-playbook)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://github.com/eiei114/pi-skill-playbook)
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
Human-mediated Agent Skill playbooks for the [Pi coding agent](https://github.com/earendil-works/pi-coding-agent).
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
- `/playbook start <playbook-id> [--run <name>]`
|
|
9
|
-
- `/playbook resume <run-id>`
|
|
10
|
-
- `/playbook status [run-id]`
|
|
11
|
-
- `/playbook done`
|
|
12
|
-
- `/playbook choose <outcome>`
|
|
13
|
-
- strict YAML validation
|
|
14
|
-
- active run widget below the editor
|
|
15
|
-
- marker-based auto advance for single-outcome steps
|
|
16
|
-
- local run state in `.pi/playbook-runs/`
|
|
12
|
+
## What this is
|
|
17
13
|
|
|
18
|
-
|
|
14
|
+
Pi Skill Playbook is a Pi extension that guides Agent Skill usage flows with **visible, human-controlled playbooks**. It shows the current workflow step, next recommended skill command, and completion criteria — but never runs skills automatically.
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
- `/playbook record`
|
|
16
|
+
Define ordered skill workflows as YAML playbooks in your project, then drive them step by step from the Pi command palette.
|
|
22
17
|
|
|
23
|
-
##
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **Playbook-driven workflows** — Define multi-step skill sequences with named outcomes and transitions in YAML.
|
|
21
|
+
- **Human-mediated** — Every step requires an explicit user action. No hidden automation.
|
|
22
|
+
- **Marker-based auto advance** — Single-outcome steps advance automatically when the assistant emits a visible `PLAYBOOK_OUTCOME:` marker. Multi-outcome steps always require explicit confirmation.
|
|
23
|
+
- **Active run widget** — Displays current step, skill command, completion criteria, and outcome labels below the editor.
|
|
24
|
+
- **Strict YAML validation** — Playbooks are validated on load for structure, transitions, and skill references.
|
|
25
|
+
- **Tab completion** — Commands, playbook IDs, run IDs, outcomes, and flags all support tab completion.
|
|
26
|
+
- **Local run state** — Run state is stored in `.pi/playbook-runs/` inside the target project, never in git.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
### From npm
|
|
24
31
|
|
|
25
32
|
```bash
|
|
26
33
|
pi install npm:pi-skill-playbook
|
|
27
34
|
```
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
## Install from GitHub
|
|
36
|
+
### From GitHub
|
|
31
37
|
|
|
32
38
|
```bash
|
|
33
39
|
pi install git:github.com/eiei114/pi-skill-playbook
|
|
@@ -39,9 +45,7 @@ Or with a full Git URL:
|
|
|
39
45
|
pi install git+https://github.com/eiei114/pi-skill-playbook.git
|
|
40
46
|
```
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
Clone the repository first, then run Pi from the checkout:
|
|
48
|
+
### Locally (for development)
|
|
45
49
|
|
|
46
50
|
```bash
|
|
47
51
|
git clone https://github.com/eiei114/pi-skill-playbook.git
|
|
@@ -50,55 +54,141 @@ npm install
|
|
|
50
54
|
pi -e .
|
|
51
55
|
```
|
|
52
56
|
|
|
53
|
-
|
|
57
|
+
## Quick start
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
pi install /path/to/pi-skill-playbook
|
|
57
|
-
```
|
|
59
|
+
1. **Copy a sample playbook** into your project:
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
```bash
|
|
62
|
+
mkdir -p .pi/playbooks
|
|
63
|
+
cp node_modules/pi-skill-playbook/samples/feature-development.yml .pi/playbooks/
|
|
64
|
+
```
|
|
60
65
|
|
|
61
|
-
|
|
66
|
+
2. **Add run state to `.gitignore`** in the target project:
|
|
62
67
|
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
```
|
|
68
|
+
```gitignore
|
|
69
|
+
.pi/playbook-runs/
|
|
70
|
+
```
|
|
67
71
|
|
|
68
|
-
|
|
72
|
+
3. **Start a run**:
|
|
69
73
|
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
```
|
|
75
|
+
/playbook:list
|
|
76
|
+
/playbook:start feature-development --run my-feature
|
|
77
|
+
```
|
|
73
78
|
|
|
74
|
-
|
|
79
|
+
4. **Drive the workflow**:
|
|
75
80
|
|
|
76
|
-
```
|
|
77
|
-
/
|
|
78
|
-
/playbook
|
|
79
|
-
/playbook
|
|
80
|
-
/playbook
|
|
81
|
-
|
|
82
|
-
```
|
|
81
|
+
```
|
|
82
|
+
/skill:grill-with-docs <feature idea>
|
|
83
|
+
/playbook:done
|
|
84
|
+
/playbook:choose ready-for-prd
|
|
85
|
+
/playbook:status
|
|
86
|
+
```
|
|
83
87
|
|
|
84
88
|
The widget displays the current step, exact skill command, completion criteria, and outcome labels.
|
|
85
89
|
|
|
86
|
-
##
|
|
90
|
+
## Usage summary
|
|
91
|
+
|
|
92
|
+
| Command | Description |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `/playbook:list` | List available playbooks with validation status |
|
|
95
|
+
| `/playbook:start <id> [--run <name>]` | Start a new playbook run |
|
|
96
|
+
| `/playbook:resume <run-id>` | Resume a paused active run |
|
|
97
|
+
| `/playbook:status [run-id]` | Show current step and completion criteria |
|
|
98
|
+
| `/playbook:done` | Complete the current step (auto-advances if single outcome) |
|
|
99
|
+
| `/playbook:choose <outcome>` | Choose an outcome for multi-branch steps |
|
|
100
|
+
| `/playbook:cancel [run-id]` | Cancel an active run |
|
|
101
|
+
|
|
102
|
+
Legacy space-separated forms (`/playbook start`) remain available for compatibility.
|
|
87
103
|
|
|
88
|
-
|
|
104
|
+
### Auto advance
|
|
89
105
|
|
|
90
|
-
When a user explicitly runs the current step skill with `/skill:<name>`, Pi Skill Playbook injects a short prompt
|
|
106
|
+
Playbooks default to `autoAdvance: auto`. When a user explicitly runs the current step's skill with `/skill:<name>`, Pi Skill Playbook injects a short prompt asking the assistant to emit a visible completion marker:
|
|
91
107
|
|
|
92
108
|
```text
|
|
93
109
|
PLAYBOOK_OUTCOME: ready-for-prd
|
|
94
110
|
```
|
|
95
111
|
|
|
96
|
-
|
|
112
|
+
| Mode | Behavior |
|
|
113
|
+
|---|---|
|
|
114
|
+
| `auto` (default) | Marker can advance single-outcome steps automatically |
|
|
115
|
+
| `suggest` | Marker only suggests `/playbook:done` or `/playbook:choose` |
|
|
116
|
+
| `off` | No prompt injection or completion detection |
|
|
117
|
+
|
|
118
|
+
## Package contents
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
pi-skill-playbook/
|
|
122
|
+
├── extensions/ Pi extension entry point
|
|
123
|
+
├── src/ Domain logic: validation, state, rendering, auto-advance
|
|
124
|
+
├── samples/ Example playbooks (feature-development.yml)
|
|
125
|
+
├── tests/ Node.js test suite
|
|
126
|
+
├── docs/adr/ Architecture decision records
|
|
127
|
+
├── LICENSE MIT
|
|
128
|
+
└── README.md
|
|
129
|
+
```
|
|
97
130
|
|
|
98
|
-
|
|
131
|
+
### Playbook YAML structure
|
|
99
132
|
|
|
100
133
|
```yaml
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
134
|
+
version: 1
|
|
135
|
+
id: my-playbook
|
|
136
|
+
name: My Playbook
|
|
137
|
+
entry: first-step
|
|
138
|
+
autoAdvance: auto # auto | suggest | off
|
|
139
|
+
|
|
140
|
+
skills:
|
|
141
|
+
my-skill:
|
|
142
|
+
role: entry
|
|
143
|
+
|
|
144
|
+
steps:
|
|
145
|
+
first-step:
|
|
146
|
+
primarySkill: my-skill
|
|
147
|
+
commandHint: "/skill:my-skill <arg>"
|
|
148
|
+
doneWhen:
|
|
149
|
+
- Criterion one.
|
|
150
|
+
- Criterion two.
|
|
151
|
+
transitions:
|
|
152
|
+
- outcome: done
|
|
153
|
+
to: complete # "complete" ends the run
|
|
104
154
|
```
|
|
155
|
+
|
|
156
|
+
See [`samples/feature-development.yml`](samples/feature-development.yml) for a full example.
|
|
157
|
+
|
|
158
|
+
## Development
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npm install
|
|
162
|
+
npm run check # TypeScript type check
|
|
163
|
+
npm test # Run tests (Node.js built-in test runner + tsx)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Requires Node.js ≥ 24, TypeScript 5.8+, and tsx 4.20+.
|
|
167
|
+
|
|
168
|
+
## Release
|
|
169
|
+
|
|
170
|
+
Releases are automated via GitHub Actions:
|
|
171
|
+
|
|
172
|
+
1. Bump `version` in `package.json` and merge to `main`.
|
|
173
|
+
2. `auto-release.yml` detects the new version, creates a git tag and GitHub Release.
|
|
174
|
+
3. `publish.yml` publishes to npm with provenance.
|
|
175
|
+
|
|
176
|
+
Manual dispatch is also available from the Actions tab.
|
|
177
|
+
|
|
178
|
+
## Security
|
|
179
|
+
|
|
180
|
+
- Run state is local-only (`.pi/playbook-runs/`). No data leaves the machine.
|
|
181
|
+
- The extension does **not** inject system prompts for skill execution. It only adds a short playbook prompt describing the current step, valid outcomes, and expected marker format.
|
|
182
|
+
- No automatic file edits — the extension warns with a `.gitignore` snippet instead of modifying files.
|
|
183
|
+
- Report vulnerabilities via [GitHub Security Advisories](https://github.com/eiei114/pi-skill-playbook/security/advisories/new).
|
|
184
|
+
|
|
185
|
+
## Links
|
|
186
|
+
|
|
187
|
+
- [npm package](https://www.npmjs.com/package/pi-skill-playbook)
|
|
188
|
+
- [GitHub repository](https://github.com/eiei114/pi-skill-playbook)
|
|
189
|
+
- [Pi coding agent](https://github.com/earendil-works/pi-coding-agent)
|
|
190
|
+
- [Architecture decisions](docs/adr/)
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
[MIT](LICENSE)
|
package/extensions/index.ts
CHANGED
|
@@ -9,6 +9,30 @@ import type { LoadedPlaybook, PlaybookRunState } from "../src/types.js";
|
|
|
9
9
|
|
|
10
10
|
const WIDGET_ID = "pi-skill-playbook";
|
|
11
11
|
|
|
12
|
+
const COMMANDS = [
|
|
13
|
+
["list", "list available playbooks"],
|
|
14
|
+
["start", "start a playbook run"],
|
|
15
|
+
["resume", "resume an active playbook run"],
|
|
16
|
+
["status", "show playbook run status"],
|
|
17
|
+
["done", "complete the current step"],
|
|
18
|
+
["choose", "choose a step outcome"],
|
|
19
|
+
["cancel", "cancel an active playbook run"],
|
|
20
|
+
] as const;
|
|
21
|
+
|
|
22
|
+
const COLON_COMMAND_ALIASES = COMMANDS.map(([command, description]) => ({
|
|
23
|
+
name: `playbook:${command}`,
|
|
24
|
+
command,
|
|
25
|
+
description,
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
const COLON_COMPLETION_COMMANDS = new Set(["start", "resume", "status", "cancel", "choose"]);
|
|
29
|
+
|
|
30
|
+
type CommandContext = {
|
|
31
|
+
cwd: string;
|
|
32
|
+
hasUI: boolean;
|
|
33
|
+
ui?: UiLike;
|
|
34
|
+
};
|
|
35
|
+
|
|
12
36
|
export default function piSkillPlaybook(pi: ExtensionAPI) {
|
|
13
37
|
let completionCwd = process.cwd();
|
|
14
38
|
let pendingSkillInvocation: string | undefined;
|
|
@@ -65,44 +89,70 @@ export default function piSkillPlaybook(pi: ExtensionAPI) {
|
|
|
65
89
|
handler: async (args, ctx) => {
|
|
66
90
|
const parsed = parseArgs(args);
|
|
67
91
|
const command = parsed.shift() ?? "status";
|
|
68
|
-
|
|
69
92
|
try {
|
|
70
|
-
|
|
71
|
-
case "list":
|
|
72
|
-
await listPlaybooks(pi, ctx.cwd, ctx.hasUI ? ctx.ui : undefined);
|
|
73
|
-
return;
|
|
74
|
-
case "start":
|
|
75
|
-
await startPlaybook(pi, ctx.cwd, parsed, ctx.hasUI ? ctx.ui : undefined);
|
|
76
|
-
return;
|
|
77
|
-
case "resume":
|
|
78
|
-
await resumeRun(ctx.cwd, parsed, ctx.hasUI ? ctx.ui : undefined);
|
|
79
|
-
return;
|
|
80
|
-
case "status":
|
|
81
|
-
await showStatus(ctx.cwd, parsed[0], ctx.hasUI ? ctx.ui : undefined);
|
|
82
|
-
return;
|
|
83
|
-
case "done":
|
|
84
|
-
await completeCurrentStep(ctx.cwd, ctx.hasUI ? ctx.ui : undefined);
|
|
85
|
-
return;
|
|
86
|
-
case "choose":
|
|
87
|
-
await chooseOutcome(ctx.cwd, parsed[0], ctx.hasUI ? ctx.ui : undefined);
|
|
88
|
-
return;
|
|
89
|
-
case "cancel":
|
|
90
|
-
case "stop":
|
|
91
|
-
case "abort":
|
|
92
|
-
await cancelRun(ctx.cwd, parsed[0], ctx.hasUI ? ctx.ui : undefined);
|
|
93
|
-
return;
|
|
94
|
-
case "import-web":
|
|
95
|
-
case "record":
|
|
96
|
-
notify(ctx.hasUI ? ctx.ui : undefined, `/${command} is deferred after the Core 6 MVP scaffold.` , "warning");
|
|
97
|
-
return;
|
|
98
|
-
default:
|
|
99
|
-
notify(ctx.hasUI ? ctx.ui : undefined, usage(), "error");
|
|
100
|
-
}
|
|
93
|
+
await handlePlaybookCommand(pi, command, parsed, ctx);
|
|
101
94
|
} catch (error) {
|
|
102
95
|
notify(ctx.hasUI ? ctx.ui : undefined, error instanceof Error ? error.message : String(error), "error");
|
|
103
96
|
}
|
|
104
97
|
},
|
|
105
98
|
});
|
|
99
|
+
|
|
100
|
+
for (const alias of COLON_COMMAND_ALIASES) {
|
|
101
|
+
pi.registerCommand(alias.name, {
|
|
102
|
+
description: `Playbook: ${alias.description}. Alias for /playbook ${alias.command}.`,
|
|
103
|
+
...(COLON_COMPLETION_COMMANDS.has(alias.command)
|
|
104
|
+
? { getArgumentCompletions: (prefix) => getPlaybookColonArgumentCompletions(completionCwd, alias.command, prefix) }
|
|
105
|
+
: {}),
|
|
106
|
+
handler: async (args, ctx) => {
|
|
107
|
+
try {
|
|
108
|
+
await handlePlaybookCommand(pi, alias.command, parseArgs(args), ctx);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
notify(ctx.hasUI ? ctx.ui : undefined, error instanceof Error ? error.message : String(error), "error");
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function handlePlaybookCommand(
|
|
118
|
+
pi: ExtensionAPI,
|
|
119
|
+
command: string,
|
|
120
|
+
args: string[],
|
|
121
|
+
ctx: CommandContext,
|
|
122
|
+
): Promise<void> {
|
|
123
|
+
const ui = ctx.hasUI ? ctx.ui : undefined;
|
|
124
|
+
|
|
125
|
+
switch (command) {
|
|
126
|
+
case "list":
|
|
127
|
+
await listPlaybooks(pi, ctx.cwd, ui);
|
|
128
|
+
return;
|
|
129
|
+
case "start":
|
|
130
|
+
await startPlaybook(pi, ctx.cwd, args, ui);
|
|
131
|
+
return;
|
|
132
|
+
case "resume":
|
|
133
|
+
await resumeRun(ctx.cwd, args, ui);
|
|
134
|
+
return;
|
|
135
|
+
case "status":
|
|
136
|
+
await showStatus(ctx.cwd, args[0], ui);
|
|
137
|
+
return;
|
|
138
|
+
case "done":
|
|
139
|
+
await completeCurrentStep(ctx.cwd, ui);
|
|
140
|
+
return;
|
|
141
|
+
case "choose":
|
|
142
|
+
await chooseOutcome(ctx.cwd, args[0], ui);
|
|
143
|
+
return;
|
|
144
|
+
case "cancel":
|
|
145
|
+
case "stop":
|
|
146
|
+
case "abort":
|
|
147
|
+
await cancelRun(ctx.cwd, args[0], ui);
|
|
148
|
+
return;
|
|
149
|
+
case "import-web":
|
|
150
|
+
case "record":
|
|
151
|
+
notify(ui, `/playbook:${command} is deferred after the Core 6 MVP scaffold.`, "warning");
|
|
152
|
+
return;
|
|
153
|
+
default:
|
|
154
|
+
notify(ui, usage(), "error");
|
|
155
|
+
}
|
|
106
156
|
}
|
|
107
157
|
|
|
108
158
|
async function listPlaybooks(pi: ExtensionAPI, cwd: string, ui: UiLike | undefined): Promise<void> {
|
|
@@ -125,7 +175,7 @@ async function listPlaybooks(pi: ExtensionAPI, cwd: string, ui: UiLike | undefin
|
|
|
125
175
|
|
|
126
176
|
async function startPlaybook(pi: ExtensionAPI, cwd: string, args: string[], ui: UiLike | undefined): Promise<void> {
|
|
127
177
|
const playbookId = args[0];
|
|
128
|
-
if (!playbookId) throw new Error("Usage: /playbook
|
|
178
|
+
if (!playbookId) throw new Error("Usage: /playbook:start <playbook-id> [--run <name>]");
|
|
129
179
|
|
|
130
180
|
const playbook = await findPlaybook(cwd, playbookId);
|
|
131
181
|
if (!playbook) throw new Error(`Playbook '${playbookId}' not found in .pi/playbooks/.`);
|
|
@@ -157,7 +207,7 @@ async function startPlaybook(pi: ExtensionAPI, cwd: string, args: string[], ui:
|
|
|
157
207
|
|
|
158
208
|
async function resumeRun(cwd: string, args: string[], ui: UiLike | undefined): Promise<void> {
|
|
159
209
|
const runId = args[0];
|
|
160
|
-
if (!runId) throw new Error("Usage: /playbook
|
|
210
|
+
if (!runId) throw new Error("Usage: /playbook:resume <run-id>");
|
|
161
211
|
const run = await loadRun(cwd, runId);
|
|
162
212
|
if (!run) throw new Error(`Run '${runId}' not found.`);
|
|
163
213
|
if (run.status !== "active") throw new Error(`Run '${runId}' is ${run.status}.`);
|
|
@@ -170,7 +220,7 @@ async function resumeRun(cwd: string, args: string[], ui: UiLike | undefined): P
|
|
|
170
220
|
|
|
171
221
|
async function cancelRun(cwd: string, explicitRunId: string | undefined, ui: UiLike | undefined): Promise<void> {
|
|
172
222
|
const runId = explicitRunId ?? (await loadActiveRunId(cwd));
|
|
173
|
-
if (!runId) throw new Error("Usage: /playbook
|
|
223
|
+
if (!runId) throw new Error("Usage: /playbook:cancel [run-id]");
|
|
174
224
|
const run = await loadRun(cwd, runId);
|
|
175
225
|
if (!run) throw new Error(`Run '${runId}' not found.`);
|
|
176
226
|
|
|
@@ -231,7 +281,7 @@ async function completeCurrentStep(cwd: string, ui: UiLike | undefined): Promise
|
|
|
231
281
|
}
|
|
232
282
|
|
|
233
283
|
async function chooseOutcome(cwd: string, outcome: string | undefined, ui: UiLike | undefined): Promise<void> {
|
|
234
|
-
if (!outcome) throw new Error("Usage: /playbook
|
|
284
|
+
if (!outcome) throw new Error("Usage: /playbook:choose <outcome>");
|
|
235
285
|
const { run, playbook } = await loadActive(cwd);
|
|
236
286
|
await advanceRun(cwd, playbook, run, outcome, ui);
|
|
237
287
|
}
|
|
@@ -333,13 +383,14 @@ function notify(ui: UiLike | undefined, message: string, level: "info" | "warnin
|
|
|
333
383
|
function usage(): string {
|
|
334
384
|
return [
|
|
335
385
|
"Usage:",
|
|
336
|
-
"/playbook
|
|
337
|
-
"/playbook
|
|
338
|
-
"/playbook
|
|
339
|
-
"/playbook
|
|
340
|
-
"/playbook
|
|
341
|
-
"/playbook
|
|
342
|
-
"/playbook
|
|
386
|
+
"/playbook:list",
|
|
387
|
+
"/playbook:start <playbook-id> [--run <name>]",
|
|
388
|
+
"/playbook:resume <run-id>",
|
|
389
|
+
"/playbook:status [run-id]",
|
|
390
|
+
"/playbook:done",
|
|
391
|
+
"/playbook:choose <outcome>",
|
|
392
|
+
"/playbook:cancel [run-id]",
|
|
393
|
+
"Legacy space-separated /playbook <subcommand> forms remain available for compatibility.",
|
|
343
394
|
].join("\n");
|
|
344
395
|
}
|
|
345
396
|
|
|
@@ -353,6 +404,26 @@ export async function getPlaybookArgumentCompletions(cwd: string, prefix: string
|
|
|
353
404
|
}
|
|
354
405
|
}
|
|
355
406
|
|
|
407
|
+
export async function getPlaybookColonArgumentCompletions(
|
|
408
|
+
cwd: string,
|
|
409
|
+
command: string,
|
|
410
|
+
prefix: string,
|
|
411
|
+
): Promise<CompletionItem[] | null> {
|
|
412
|
+
try {
|
|
413
|
+
const normalizedPrefix = prefix.trimStart();
|
|
414
|
+
const legacyPrefix = normalizedPrefix ? `${command} ${normalizedPrefix}` : command;
|
|
415
|
+
const items = await getPlaybookArgumentCompletionsUnsafe(cwd, legacyPrefix);
|
|
416
|
+
if (!items) return null;
|
|
417
|
+
const legacyToken = `${command} `;
|
|
418
|
+
return items.map((item) => ({
|
|
419
|
+
...item,
|
|
420
|
+
value: item.value.startsWith(legacyToken) ? item.value.slice(legacyToken.length) : item.value,
|
|
421
|
+
}));
|
|
422
|
+
} catch {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
356
427
|
async function getPlaybookArgumentCompletionsUnsafe(cwd: string, prefix: string): Promise<CompletionItem[] | null> {
|
|
357
428
|
const parsed = parseCompletionPrefix(prefix);
|
|
358
429
|
const command = parsed.command;
|
package/package.json
CHANGED
package/src/auto-advance.ts
CHANGED
|
@@ -91,7 +91,7 @@ export function planCompletion(
|
|
|
91
91
|
kind: "suggest",
|
|
92
92
|
outcome: resolved.outcome,
|
|
93
93
|
to: resolved.to,
|
|
94
|
-
message: `Completion marked for step '${run.currentStep}'. Confirm outcome: /playbook
|
|
94
|
+
message: `Completion marked for step '${run.currentStep}'. Confirm outcome: /playbook:choose ${resolved.outcome}`,
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -101,8 +101,8 @@ export function planCompletion(
|
|
|
101
101
|
outcome: resolved.outcome,
|
|
102
102
|
to: resolved.to,
|
|
103
103
|
message: resolved.outcome === "complete"
|
|
104
|
-
? `Completion marked for final step '${run.currentStep}'. Run /playbook
|
|
105
|
-
: `Completion marked for step '${run.currentStep}'. Run /playbook
|
|
104
|
+
? `Completion marked for final step '${run.currentStep}'. Run /playbook:done to complete.`
|
|
105
|
+
: `Completion marked for step '${run.currentStep}'. Run /playbook:done to advance to '${resolved.to}'.`,
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
108
|
|
|
@@ -129,7 +129,7 @@ export function lastAssistantText(messages: unknown[]): string {
|
|
|
129
129
|
|
|
130
130
|
function suggestPlan(step: PlaybookStep, stepId: string): CompletionPlan {
|
|
131
131
|
if (step.transitions.length === 0) {
|
|
132
|
-
return { kind: "suggest", outcome: "complete", to: "complete", message: `Completion suspected for final step '${stepId}'. Run /playbook
|
|
132
|
+
return { kind: "suggest", outcome: "complete", to: "complete", message: `Completion suspected for final step '${stepId}'. Run /playbook:done to complete.` };
|
|
133
133
|
}
|
|
134
134
|
if (step.transitions.length === 1) {
|
|
135
135
|
const transition = step.transitions[0]!;
|
|
@@ -137,12 +137,12 @@ function suggestPlan(step: PlaybookStep, stepId: string): CompletionPlan {
|
|
|
137
137
|
kind: "suggest",
|
|
138
138
|
outcome: transition.outcome,
|
|
139
139
|
to: transition.to,
|
|
140
|
-
message: `Completion suspected for step '${stepId}'. Run /playbook
|
|
140
|
+
message: `Completion suspected for step '${stepId}'. Run /playbook:done to advance to '${transition.to}'.`,
|
|
141
141
|
};
|
|
142
142
|
}
|
|
143
143
|
return {
|
|
144
144
|
kind: "suggest",
|
|
145
|
-
message: `Completion suspected for step '${stepId}'. Choose outcome: ${step.transitions.map((transition) => `/playbook
|
|
145
|
+
message: `Completion suspected for step '${stepId}'. Choose outcome: ${step.transitions.map((transition) => `/playbook:choose ${transition.outcome}`).join(" | ")}`,
|
|
146
146
|
};
|
|
147
147
|
}
|
|
148
148
|
|