pi-skill-playbook 0.2.0 → 1.0.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 CHANGED
@@ -22,7 +22,7 @@ Define ordered skill workflows as YAML playbooks in your project, then drive the
22
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
23
  - **Active run widget** — Displays current step, skill command, completion criteria, and outcome labels below the editor.
24
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.
25
+ - **Selection UI** — Playbook, run, and outcome selection use the Pi TUI selector instead of memorized ids.
26
26
  - **Local run state** — Run state is stored in `.pi/playbook-runs/` inside the target project, never in git.
27
27
 
28
28
  ## Install
@@ -96,12 +96,12 @@ The widget displays the current step, exact skill command, completion criteria,
96
96
  | `/playbook:list` | List available playbooks with validation status |
97
97
  | `/playbook:start` | Select a playbook and start a new run |
98
98
  | `/playbook:resume` | Select an active run to resume |
99
- | `/playbook:status [run-id]` | Show current step and completion criteria |
99
+ | `/playbook:status` | Show current step and completion criteria |
100
100
  | `/playbook:done` | Complete the current step (auto-advances if single outcome) |
101
101
  | `/playbook:choose` | Select an outcome for multi-branch steps |
102
102
  | `/playbook:cancel` | Select and confirm an active run cancellation |
103
103
 
104
- Legacy explicit-argument forms (`/playbook:start <id>`, `/playbook:resume <run-id>`, `/playbook:choose <outcome>`, `/playbook:cancel <run-id>`, and space-separated `/playbook start`) remain available for scripts and non-interactive use.
104
+ All commands are argument-free. Use the Pi TUI selection UI to pick playbooks, runs, and outcomes.
105
105
 
106
106
  ### Auto advance
107
107
 
@@ -5,7 +5,7 @@ Pi Skill Playbook defaults to marker-based **Auto Advance**: a run may advance a
5
5
  ## Considered Options
6
6
 
7
7
  - Fully automatic advancement after any matching skill invocation: rejected because failed or partial skill runs could silently advance the run.
8
- - Suggest-only advancement: rejected as too close to the existing `/playbook done` workflow.
8
+ - Suggest-only advancement: rejected as too close to the existing `/playbook:done` workflow.
9
9
  - Hidden structured metadata: rejected because visible markers make state changes auditable in conversation history.
10
10
 
11
11
  ## Consequences
package/docs/examples.md CHANGED
@@ -16,14 +16,3 @@ Copy or create YAML playbooks in the target project under `.pi/playbooks/`, then
16
16
  - `/playbook:resume` opens an active-run selector.
17
17
  - `/playbook:choose` opens a selector for the current step's valid outcomes.
18
18
  - `/playbook:cancel` selects an active run when needed and asks for confirmation before marking it cancelled.
19
-
20
- ## Script-compatible explicit args
21
-
22
- Non-interactive scripts can still pass ids explicitly:
23
-
24
- ```text
25
- /playbook:start feature-development --run scripted-feature
26
- /playbook:resume feature-development-20260608120000
27
- /playbook:choose ready-for-prd
28
- /playbook:cancel feature-development-20260608120000
29
- ```
@@ -19,14 +19,6 @@ const COMMANDS = [
19
19
  ["cancel", "cancel an active playbook run"],
20
20
  ] as const;
21
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
22
  type CommandContext = {
31
23
  cwd: string;
32
24
  hasUI: boolean;
@@ -39,11 +31,9 @@ type SelectOption<T> = {
39
31
  };
40
32
 
41
33
  export default function piSkillPlaybook(pi: ExtensionAPI) {
42
- let completionCwd = process.cwd();
43
34
  let pendingSkillInvocation: string | undefined;
44
35
 
45
36
  pi.on("session_start", async (_event, ctx) => {
46
- completionCwd = ctx.cwd;
47
37
  if (!ctx.hasUI) return;
48
38
  const activeRunId = await loadActiveRunId(ctx.cwd);
49
39
  if (!activeRunId) {
@@ -88,29 +78,12 @@ export default function piSkillPlaybook(pi: ExtensionAPI) {
88
78
  }
89
79
  });
90
80
 
91
- pi.registerCommand("playbook", {
92
- description: "Guide Agent Skill workflows with project-local playbooks",
93
- getArgumentCompletions: (prefix) => getPlaybookArgumentCompletions(completionCwd, prefix),
94
- handler: async (args, ctx) => {
95
- const parsed = parseArgs(args);
96
- const command = parsed.shift() ?? "status";
97
- try {
98
- await handlePlaybookCommand(pi, command, parsed, ctx);
99
- } catch (error) {
100
- notify(ctx.hasUI ? ctx.ui : undefined, error instanceof Error ? error.message : String(error), "error");
101
- }
102
- },
103
- });
104
-
105
- for (const alias of COLON_COMMAND_ALIASES) {
106
- pi.registerCommand(alias.name, {
107
- description: `Playbook: ${alias.description}. Alias for /playbook ${alias.command}.`,
108
- ...(COLON_COMPLETION_COMMANDS.has(alias.command)
109
- ? { getArgumentCompletions: (prefix) => getPlaybookColonArgumentCompletions(completionCwd, alias.command, prefix) }
110
- : {}),
111
- handler: async (args, ctx) => {
81
+ for (const [command, description] of COMMANDS) {
82
+ pi.registerCommand(`playbook:${command}`, {
83
+ description: `Playbook: ${description}.`,
84
+ handler: async (_args, ctx) => {
112
85
  try {
113
- await handlePlaybookCommand(pi, alias.command, parseArgs(args), ctx);
86
+ await handlePlaybookCommand(pi, command, ctx);
114
87
  } catch (error) {
115
88
  notify(ctx.hasUI ? ctx.ui : undefined, error instanceof Error ? error.message : String(error), "error");
116
89
  }
@@ -122,7 +95,6 @@ export default function piSkillPlaybook(pi: ExtensionAPI) {
122
95
  export async function handlePlaybookCommand(
123
96
  pi: ExtensionAPI,
124
97
  command: string,
125
- args: string[],
126
98
  ctx: CommandContext,
127
99
  ): Promise<void> {
128
100
  const ui = ctx.hasUI ? ctx.ui : undefined;
@@ -132,24 +104,24 @@ export async function handlePlaybookCommand(
132
104
  await listPlaybooks(pi, ctx.cwd, ui);
133
105
  return;
134
106
  case "start":
135
- await startPlaybook(pi, ctx.cwd, args, ui);
107
+ await startPlaybook(pi, ctx.cwd, ui);
136
108
  return;
137
109
  case "resume":
138
- await resumeRun(ctx.cwd, args, ui);
110
+ await resumeRun(ctx.cwd, ui);
139
111
  return;
140
112
  case "status":
141
- await showStatus(ctx.cwd, args[0], ui);
113
+ await showStatus(ctx.cwd, ui);
142
114
  return;
143
115
  case "done":
144
116
  await completeCurrentStep(ctx.cwd, ui);
145
117
  return;
146
118
  case "choose":
147
- await chooseOutcome(ctx.cwd, args[0], ui);
119
+ await chooseOutcome(ctx.cwd, ui);
148
120
  return;
149
121
  case "cancel":
150
122
  case "stop":
151
123
  case "abort":
152
- await cancelRun(ctx.cwd, args[0], ui);
124
+ await cancelRun(ctx.cwd, ui);
153
125
  return;
154
126
  case "import-web":
155
127
  case "record":
@@ -178,25 +150,10 @@ async function listPlaybooks(pi: ExtensionAPI, cwd: string, ui: UiLike | undefin
178
150
  notify(ui, lines.join("\n"), duplicateResult.valid ? "info" : "error");
179
151
  }
180
152
 
181
- async function startPlaybook(pi: ExtensionAPI, cwd: string, args: string[], ui: UiLike | undefined): Promise<void> {
182
- const playbookId = args[0];
183
- if (!playbookId) {
184
- const playbook = await pickPlaybook(pi, cwd, ui);
185
- if (!playbook) return;
186
- await createAndActivateRun(cwd, playbook, undefined, ui);
187
- return;
188
- }
189
-
190
- const playbook = await findPlaybook(cwd, playbookId);
191
- if (!playbook) throw new Error(`Playbook '${playbookId}' not found in .pi/playbooks/.`);
192
-
193
- const validation = validatePlaybook(playbook, getAvailableSkills(pi), { requireSkills: true });
194
- if (!validation.valid) {
195
- throw new Error(`Playbook validation failed:\n${renderValidationErrors(validation.errors)}`);
196
- }
197
-
198
- const runName = readFlagValue(args.slice(1), "--run");
199
- await createAndActivateRun(cwd, playbook, runName, ui);
153
+ async function startPlaybook(pi: ExtensionAPI, cwd: string, ui: UiLike | undefined): Promise<void> {
154
+ const playbook = await pickPlaybook(pi, cwd, ui);
155
+ if (!playbook) return;
156
+ await createAndActivateRun(cwd, playbook, undefined, ui);
200
157
  }
201
158
 
202
159
  async function createAndActivateRun(cwd: string, playbook: LoadedPlaybook, runName: string | undefined, ui: UiLike | undefined): Promise<void> {
@@ -219,33 +176,20 @@ async function createAndActivateRun(cwd: string, playbook: LoadedPlaybook, runNa
219
176
  notify(ui, [`Started ${run.runId}.`, ...(advisory ? ["", advisory] : [])].join("\n"), advisory ? "warning" : "info");
220
177
  }
221
178
 
222
- async function resumeRun(cwd: string, args: string[], ui: UiLike | undefined): Promise<void> {
223
- const runId = args[0];
224
- if (!runId) {
225
- const run = await pickActiveRun(cwd, ui, "Resume which playbook run?");
226
- if (!run) return;
227
- await resumeRun(cwd, [run.runId], ui);
228
- return;
229
- }
230
- const run = await loadRun(cwd, runId);
231
- if (!run) throw new Error(`Run '${runId}' not found.`);
232
- if (run.status !== "active") throw new Error(`Run '${runId}' is ${run.status}.`);
179
+ async function resumeRun(cwd: string, ui: UiLike | undefined): Promise<void> {
180
+ const run = await pickActiveRun(cwd, ui, "Resume which playbook run?");
181
+ if (!run) return;
182
+
233
183
  const playbook = await findPlaybook(cwd, run.playbookId);
234
- if (!playbook) throw new Error(`Run '${runId}' references missing playbook '${run.playbookId}'.`);
184
+ if (!playbook) throw new Error(`Run '${run.runId}' references missing playbook '${run.playbookId}'.`);
235
185
  await setActiveRun(cwd, run.runId);
236
186
  renderWidget(ui, playbook, run);
237
187
  notify(ui, `Resumed ${run.runId}.`, "info");
238
188
  }
239
189
 
240
- async function cancelRun(cwd: string, explicitRunId: string | undefined, ui: UiLike | undefined): Promise<void> {
241
- let runId = explicitRunId;
242
- if (!runId) {
243
- const run = await pickRunToCancel(cwd, ui);
244
- if (!run) return;
245
- runId = run.runId;
246
- }
247
- const run = await loadRun(cwd, runId);
248
- if (!run) throw new Error(`Run '${runId}' not found.`);
190
+ async function cancelRun(cwd: string, ui: UiLike | undefined): Promise<void> {
191
+ const run = await pickRunToCancel(cwd, ui);
192
+ if (!run) return;
249
193
 
250
194
  if (run.status !== "active") {
251
195
  if ((await loadActiveRunId(cwd)) === run.runId) await clearActiveRun(cwd);
@@ -264,8 +208,8 @@ async function cancelRun(cwd: string, explicitRunId: string | undefined, ui: UiL
264
208
  notify(ui, `Cancelled playbook run ${run.runId}.`, "info");
265
209
  }
266
210
 
267
- async function showStatus(cwd: string, explicitRunId: string | undefined, ui: UiLike | undefined): Promise<void> {
268
- const runId = explicitRunId ?? (await loadActiveRunId(cwd));
211
+ async function showStatus(cwd: string, ui: UiLike | undefined): Promise<void> {
212
+ const runId = await loadActiveRunId(cwd);
269
213
  if (!runId) {
270
214
  notify(ui, "No active playbook run.", "info");
271
215
  clearWidget(ui);
@@ -275,7 +219,7 @@ async function showStatus(cwd: string, explicitRunId: string | undefined, ui: Ui
275
219
  if (!run) throw new Error(`Run '${runId}' not found.`);
276
220
  if (run.status !== "active") {
277
221
  notify(ui, `Run '${run.runId}' is ${run.status}.`, "info");
278
- if (!explicitRunId) clearWidget(ui);
222
+ clearWidget(ui);
279
223
  return;
280
224
  }
281
225
  const playbook = await findPlaybook(cwd, run.playbookId);
@@ -303,19 +247,16 @@ async function completeCurrentStep(cwd: string, ui: UiLike | undefined): Promise
303
247
  await advanceRun(cwd, playbook, run, step.transitions[0].outcome, ui);
304
248
  }
305
249
 
306
- async function chooseOutcome(cwd: string, outcome: string | undefined, ui: UiLike | undefined): Promise<void> {
250
+ async function chooseOutcome(cwd: string, ui: UiLike | undefined): Promise<void> {
307
251
  const { run, playbook } = await loadActive(cwd);
308
- if (!outcome) {
309
- const selected = await pickOutcome(playbook, run, ui);
310
- if (!selected) return;
311
- outcome = selected.outcome;
312
- }
313
- await advanceRun(cwd, playbook, run, outcome, ui);
252
+ const selected = await pickOutcome(playbook, run, ui);
253
+ if (!selected) return;
254
+ await advanceRun(cwd, playbook, run, selected.outcome, ui);
314
255
  }
315
256
 
316
257
  async function pickPlaybook(pi: ExtensionAPI, cwd: string, ui: UiLike | undefined): Promise<LoadedPlaybook | undefined> {
317
258
  if (!hasSelectionUI(ui)) {
318
- notify(ui, "Interactive playbook selection requires the Pi TUI. For scripts, use /playbook:start <playbook-id> [--run <name>].", "error");
259
+ notify(ui, "Interactive playbook selection requires the Pi TUI. Run /playbook:start from the command palette.", "error");
319
260
  return undefined;
320
261
  }
321
262
 
@@ -380,11 +321,11 @@ function duplicateIdErrors(playbooks: LoadedPlaybook[]): Map<LoadedPlaybook, str
380
321
 
381
322
  async function pickActiveRun(cwd: string, ui: UiLike | undefined, title: string): Promise<PlaybookRunState | undefined> {
382
323
  if (!hasSelectionUI(ui)) {
383
- notify(ui, "Interactive run selection requires the Pi TUI. For scripts, pass the run id explicitly.", "error");
324
+ notify(ui, "Interactive run selection requires the Pi TUI. Run /playbook:resume from the command palette.", "error");
384
325
  return undefined;
385
326
  }
386
327
 
387
- const runs = await getRunCompletionCandidates(cwd, true);
328
+ const runs = await getActiveRuns(cwd);
388
329
  if (runs.length === 0) {
389
330
  clearWidget(ui);
390
331
  notify(ui, "No active playbook runs. Start one with /playbook:start.", "info");
@@ -396,11 +337,11 @@ async function pickActiveRun(cwd: string, ui: UiLike | undefined, title: string)
396
337
 
397
338
  async function pickRunToCancel(cwd: string, ui: UiLike | undefined): Promise<PlaybookRunState | undefined> {
398
339
  if (!hasConfirmUI(ui)) {
399
- notify(ui, "Interactive cancellation requires the Pi TUI. For scripts, use /playbook:cancel <run-id>.", "error");
340
+ notify(ui, "Interactive cancellation requires the Pi TUI. Run /playbook:cancel from the command palette.", "error");
400
341
  return undefined;
401
342
  }
402
343
 
403
- const runs = await getRunCompletionCandidates(cwd, true);
344
+ const runs = await getActiveRuns(cwd);
404
345
  if (runs.length === 0) {
405
346
  clearWidget(ui);
406
347
  notify(ui, "No active playbook runs to cancel.", "info");
@@ -426,7 +367,7 @@ function activeRunLabel(run: PlaybookRunState): string {
426
367
 
427
368
  async function pickOutcome(playbook: LoadedPlaybook, run: PlaybookRunState, ui: UiLike | undefined) {
428
369
  if (!hasSelectionUI(ui)) {
429
- notify(ui, "Interactive outcome selection requires the Pi TUI. For scripts, use /playbook:choose <outcome>.", "error");
370
+ notify(ui, "Interactive outcome selection requires the Pi TUI. Run /playbook:choose from the command palette.", "error");
430
371
  return undefined;
431
372
  }
432
373
 
@@ -563,183 +504,17 @@ function usage(): string {
563
504
  "/playbook:list",
564
505
  "/playbook:start",
565
506
  "/playbook:resume",
566
- "/playbook:status [run-id]",
507
+ "/playbook:status",
567
508
  "/playbook:done",
568
509
  "/playbook:choose",
569
510
  "/playbook:cancel",
570
- "Legacy explicit args remain available for scripts: start <playbook-id> [--run <name>], resume <run-id>, choose <outcome>, cancel <run-id>.",
571
511
  ].join("\n");
572
512
  }
573
513
 
574
- type CompletionItem = { value: string; label: string; description?: string };
575
-
576
- export async function getPlaybookArgumentCompletions(cwd: string, prefix: string): Promise<CompletionItem[] | null> {
577
- try {
578
- return await getPlaybookArgumentCompletionsUnsafe(cwd, prefix);
579
- } catch {
580
- return null;
581
- }
582
- }
583
-
584
- export async function getPlaybookColonArgumentCompletions(
585
- cwd: string,
586
- command: string,
587
- prefix: string,
588
- ): Promise<CompletionItem[] | null> {
589
- try {
590
- const normalizedPrefix = prefix.trimStart();
591
- const legacyPrefix = normalizedPrefix ? `${command} ${normalizedPrefix}` : command;
592
- const items = await getPlaybookArgumentCompletionsUnsafe(cwd, legacyPrefix);
593
- if (!items) return null;
594
- const legacyToken = `${command} `;
595
- return items.map((item) => ({
596
- ...item,
597
- value: item.value.startsWith(legacyToken) ? item.value.slice(legacyToken.length) : item.value,
598
- }));
599
- } catch {
600
- return null;
601
- }
602
- }
603
-
604
- async function getPlaybookArgumentCompletionsUnsafe(cwd: string, prefix: string): Promise<CompletionItem[] | null> {
605
- const parsed = parseCompletionPrefix(prefix);
606
- const command = parsed.command;
607
- if (!command) {
608
- return completeCommands(parsed.currentToken);
609
- }
610
-
611
- if (parsed.completedTokens.length === 0) {
612
- return completeCommands(parsed.currentToken);
613
- }
614
-
615
- switch (command) {
616
- case "start":
617
- return completeStartArguments(cwd, parsed);
618
- case "resume":
619
- return completeRunArgument(cwd, parsed, command, { activeOnly: true });
620
- case "status":
621
- return completeRunArgument(cwd, parsed, command, { activeOnly: false, optional: true });
622
- case "cancel":
623
- case "stop":
624
- case "abort":
625
- return completeRunArgument(cwd, parsed, command, { activeOnly: true, optional: true });
626
- case "choose":
627
- return completeOutcomeArgument(cwd, parsed);
628
- default:
629
- return null;
630
- }
631
- }
632
-
633
- function completeCommands(token: string): CompletionItem[] | null {
634
- const commands = ["list", "start", "resume", "status", "done", "choose", "cancel"];
635
- return toCompletionItems(commands, token, (command) => command, (command) => command);
636
- }
637
-
638
- async function completeStartArguments(cwd: string, parsed: CompletionPrefix): Promise<CompletionItem[] | null> {
639
- const args = parsed.completedTokens.slice(1);
640
- if (args.length === 0) {
641
- const playbooks = await loadPlaybooks(cwd);
642
- return toCompletionItems(
643
- playbooks,
644
- parsed.currentToken,
645
- (playbook) => `start ${playbook.definition.id}`,
646
- (playbook) => playbook.definition.id,
647
- (playbook) => playbook.definition.name,
648
- );
649
- }
650
-
651
- if (args.length >= 1 && !args.includes("--run")) {
652
- return toCompletionItems(["--run"], parsed.currentToken, (flag) => `start ${args.join(" ")} ${flag} `, (flag) => flag);
653
- }
654
- return null;
655
- }
656
-
657
- async function completeRunArgument(
658
- cwd: string,
659
- parsed: CompletionPrefix,
660
- command: string,
661
- options: { activeOnly: boolean; optional?: boolean },
662
- ): Promise<CompletionItem[] | null> {
663
- const args = parsed.completedTokens.slice(1);
664
- if (args.length > 0 || (options.optional && parsed.currentToken === "" && !parsed.trailingWhitespace)) return null;
665
- const runs = await getRunCompletionCandidates(cwd, options.activeOnly);
666
- return toCompletionItems(
667
- runs,
668
- parsed.currentToken,
669
- (run) => `${command} ${run.runId}`,
670
- (run) => run.runId,
671
- (run) => `${run.playbookId} (${run.status})`,
672
- );
673
- }
674
-
675
- async function completeOutcomeArgument(cwd: string, parsed: CompletionPrefix): Promise<CompletionItem[] | null> {
676
- const args = parsed.completedTokens.slice(1);
677
- if (args.length > 0) return null;
678
- const { run, playbook } = await loadActive(cwd);
679
- const step = playbook.definition.steps[run.currentStep];
680
- if (!step) return null;
681
- return toCompletionItems(
682
- step.transitions,
683
- parsed.currentToken,
684
- (transition) => `choose ${transition.outcome}`,
685
- (transition) => transition.outcome,
686
- (transition) => `to ${transition.to}`,
687
- );
688
- }
689
-
690
- async function getRunCompletionCandidates(cwd: string, activeOnly: boolean): Promise<PlaybookRunState[]> {
514
+ async function getActiveRuns(cwd: string): Promise<PlaybookRunState[]> {
691
515
  const ids = await listRunIds(cwd);
692
516
  const runs = (await Promise.all(ids.map((id) => loadRun(cwd, id)))).filter((run): run is PlaybookRunState => Boolean(run));
693
- const filtered = activeOnly ? runs.filter((run) => run.status === "active") : runs;
694
- return filtered.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
695
- }
696
-
697
- function toCompletionItems<T>(
698
- values: T[],
699
- token: string,
700
- valueFor: (item: T) => string,
701
- labelFor: (item: T) => string,
702
- descriptionFor?: (item: T) => string | undefined,
703
- ): CompletionItem[] | null {
704
- const normalizedToken = token.trim().toLowerCase();
705
- const items = values
706
- .filter((item) => matchesCompletion(labelFor(item), normalizedToken))
707
- .map((item) => {
708
- const description = descriptionFor?.(item);
709
- return { value: valueFor(item), label: labelFor(item), ...(description ? { description } : {}) };
710
- });
711
- return items.length > 0 ? items : null;
712
- }
713
-
714
- function matchesCompletion(value: string, token: string): boolean {
715
- if (token === "") return true;
716
- return value.toLowerCase().includes(token);
717
- }
718
-
719
- type CompletionPrefix = {
720
- completedTokens: string[];
721
- currentToken: string;
722
- command: string | undefined;
723
- trailingWhitespace: boolean;
724
- };
725
-
726
- function parseCompletionPrefix(prefix: string): CompletionPrefix {
727
- const trailingWhitespace = /\s$/.test(prefix);
728
- const tokens = parseArgs(prefix);
729
- const completedTokens = trailingWhitespace ? tokens : tokens.slice(0, -1);
730
- const currentToken = trailingWhitespace ? "" : tokens.at(-1) ?? "";
731
- return { completedTokens, currentToken, command: completedTokens[0] ?? (!trailingWhitespace && tokens.length > 1 ? tokens[0] : undefined), trailingWhitespace };
732
- }
733
-
734
- function parseArgs(args: string): string[] {
735
- const matches = args.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? [];
736
- return matches.map((arg) => arg.replace(/^(["'])(.*)\1$/, "$2"));
737
- }
738
-
739
- function readFlagValue(args: string[], flag: string): string | undefined {
740
- const index = args.indexOf(flag);
741
- if (index === -1) return undefined;
742
- return args[index + 1];
517
+ return runs.filter((run) => run.status === "active").sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
743
518
  }
744
519
 
745
520
  interface UiLike {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-skill-playbook",
3
- "version": "0.2.0",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "description": "Pi extension for passive, human-mediated Agent Skill playbooks.",
6
6
  "keywords": ["pi-package", "pi-extension", "agent-skills", "playbook"],
@@ -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:choose ${resolved.outcome}`,
94
+ message: `Completion marked for step '${run.currentStep}'. Confirm outcome with /playbook:choose.`,
95
95
  };
96
96
  }
97
97
 
@@ -142,7 +142,7 @@ function suggestPlan(step: PlaybookStep, stepId: string): CompletionPlan {
142
142
  }
143
143
  return {
144
144
  kind: "suggest",
145
- message: `Completion suspected for step '${stepId}'. Choose outcome: ${step.transitions.map((transition) => `/playbook:choose ${transition.outcome}`).join(" | ")}`,
145
+ message: `Completion suspected for step '${stepId}'. Run /playbook:choose to select an outcome.`,
146
146
  };
147
147
  }
148
148