@danypops/papyrus 0.23.0 → 0.24.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
@@ -145,6 +145,7 @@ Tasks, Docs, Rules, and Skills all support first-class `update` (title/body/labe
145
145
  - `/rules` — severity/condition rows, exact injection preview, edit, enable/disable, and task gating
146
146
  - `/skills` — trigger/tools rows, edit, invocation into the editor, and artifact templates
147
147
  - `/playbooks` — trigger/tools rows, edit, invocation into the editor, and graph links
148
+ - `/playbook <name>` — tab-completes active playbook titles and places that one's invocation directly in the editor, one step instead of browse-then-select; no argument falls back to the full `/playbooks` browser
148
149
 
149
150
  All frontends use daemon-backed domain operations; none opens SQLite from the Pi process. **Show details** opens a bounded navigable view across Tasks, Notes, Docs, Rules, legacy Skills, templates, and workflow Skills. User-authored bodies render as width-aware Markdown with headings, emphasis, links, quotes, lists, tables, inline/fenced code, syntax highlighting, and every color/decorative style derived dynamically from the active Pi theme. Generated lifecycle, metadata, checklist, gate, history, and relationship sections keep explicit semantic theme colors. `↑/↓` scrolls, `←/→` pans wide relationships, and Esc returns to the browser; non-interactive clients receive stable source text.
150
151
 
@@ -462,6 +462,11 @@ export default async function (pi: ExtensionAPI) {
462
462
  description: "Browse, edit, and invoke Papyrus playbooks -- trigger/steps guidance an agent reads and follows (interactive)",
463
463
  handler: async (_args, ctx) => { await playbooksModule.showPlaybooks(ctx); },
464
464
  });
465
+ pi.registerCommand("playbook", {
466
+ description: "Open one Papyrus playbook directly by name (tab-completes active playbook titles) and place its invocation in the editor; no argument opens the full /playbooks browser instead",
467
+ getArgumentCompletions: (argumentPrefix) => playbooksModule.playbookArgumentCompletions(argumentPrefix),
468
+ handler: async (args, ctx) => { await playbooksModule.openPlaybookByName(args, ctx); },
469
+ });
465
470
  pi.registerCommand("discuss", {
466
471
  description: "Browse Papyrus Discussions and reply, defer, resume, settle, or block/unblock a task (interactive)",
467
472
  handler: async (_args, ctx) => { await discussModule.showDiscussions(ctx); },
@@ -1,9 +1,44 @@
1
+ import type { AutocompleteItem } from "@earendil-works/pi-tui";
1
2
  import type { ExtensionCommandContext, Theme } from "@earendil-works/pi-coding-agent";
2
3
  import type { Artifact } from "../../src/domain/artifact.ts";
3
4
  import { showArtifactBrowser, showArtifactDetails } from "./artifact-browser.ts";
4
5
  import { PLAYBOOK_STATUS_PRESENTATION } from "./artifact-status-presentation.ts";
6
+ import { matchArtifactByName } from "./domain-tools.ts";
5
7
  import { callService } from "./service-client.ts";
6
8
 
9
+ const PLAYBOOK_COMPLETION_MAX_CANDIDATES = 100;
10
+
11
+ async function activePlaybooks(): Promise<Artifact[]> {
12
+ return callService<Record<string, unknown>, Artifact[]>("playbooks.list", { status: "active", limit: PLAYBOOK_COMPLETION_MAX_CANDIDATES });
13
+ }
14
+
15
+ /** `/playbook <tab>` completions -- title-prefix match, since that's what a human actually types, not a full-text search of body content. */
16
+ export async function playbookArgumentCompletions(argumentPrefix: string): Promise<AutocompleteItem[] | null> {
17
+ try {
18
+ const needle = argumentPrefix.trim().toLowerCase();
19
+ const rows = await activePlaybooks();
20
+ return rows
21
+ .filter((row) => row.title.toLowerCase().startsWith(needle))
22
+ .sort((a, b) => a.title.localeCompare(b.title))
23
+ .map((row) => ({ value: row.title, label: row.title, description: typeof row.extra["trigger"] === "string" ? row.extra["trigger"] : undefined }));
24
+ } catch {
25
+ return null; // a Papyrus daemon hiccup degrades to "no suggestions", never breaks the command line
26
+ }
27
+ }
28
+
29
+ /** `/playbook <name>` (no args opens the full browser instead): resolves by exact title, then places its invocation directly in the editor -- one step, not browse-then-select-then-invoke. */
30
+ export async function openPlaybookByName(name: string, ctx: ExtensionCommandContext): Promise<void> {
31
+ if (!name.trim()) { await showPlaybooks(ctx); return; }
32
+ try {
33
+ const id = matchArtifactByName(await activePlaybooks(), name);
34
+ const invocation = await callService<Record<string, unknown>, string>("playbooks.invoke", { id });
35
+ ctx.ui.setEditorText(invocation);
36
+ ctx.ui.notify(`"${name.trim()}" invocation placed in the editor`, "info");
37
+ } catch (error) {
38
+ ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
39
+ }
40
+ }
41
+
7
42
  const PLAYBOOK_RELATIONS = ["references", "documents", "relates_to", "contains", "part_of"];
8
43
 
9
44
  function strings(value: unknown): string[] {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/papyrus",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "description": "Daemon-backed graph artifacts, evidence-bearing tasks, rules, skills, and native TUI workflows for Pi",
5
5
  "type": "module",
6
6
  "keywords": ["pi-package"],