@danypops/papyrus 0.32.0 → 0.32.2

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
@@ -98,7 +98,7 @@ papyrus skills run <skill-id> \
98
98
  --json
99
99
  ```
100
100
 
101
- The existing `artifact-template` skill subtype remains a compatibility mechanism for one-artifact templates with metadata `{targetKind, defaults, required}`. Instantiate it through `papyrus_create` with `template_id`; defaults merge recursively, explicit arrays replace defaults, required paths such as `extra.owner` are validated, and target-kind mismatches are rejected.
101
+ The existing `artifact-template` skill subtype remains a compatibility mechanism for one-artifact templates with metadata `{targetKind, defaults, required}`. Instantiate it through the `skills` tool's `instantiate` action with `template_id`; defaults merge recursively, explicit arrays replace defaults, required paths such as `extra.owner` are validated, and target-kind mismatches are rejected.
102
102
 
103
103
  ### Removing an artifact
104
104
 
@@ -110,9 +110,8 @@ Once the deadline passes, the daemon's periodic sweep performs a real, cascading
110
110
 
111
111
  The `papyrus_*` tools are the low-level graph-store API:
112
112
 
113
- - **`papyrus_create`** — create directly or instantiate via `template_id`
114
113
  - **`papyrus_query`** — filter by kind/status or search title and body
115
- - **`papyrus_graph`** — link artifacts, perform bounded traversal, or update status
114
+ - **`papyrus_graph`** — link artifacts, perform bounded traversal, or read the mutation event log
116
115
  - **`papyrus_show`** — read nested metadata and bounded edges, optionally running gates
117
116
 
118
117
  Agent-facing domain tools own lifecycle invariants and sit above this store API:
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * pi-papyrus — native Pi extension for the Papyrus graph store.
3
3
  *
4
- * Tools: papyrus_create/query/graph/show.
4
+ * Tools: papyrus_query/graph/show (low-level), plus one native tool per domain (docs/rules/skills/playbooks/tasks/discuss/notes).
5
5
  * Command: /tasks (interactive task panel).
6
6
  * Widget: persistent task status above editor (rpiv-todo pattern).
7
7
  * Injection: active rules + open tasks appended to system prompt every turn.
@@ -279,43 +279,6 @@ export default async function (pi: ExtensionAPI) {
279
279
 
280
280
  // ── Low-level graph-store tools ────────────────────────────────────
281
281
 
282
- pi.registerTool({
283
- name: "papyrus_create",
284
- label: "Papyrus Create",
285
- description:
286
- "Create a graph artifact. KINDS: doc (knowledge — specs, decisions, research), " +
287
- "task (work — with gates/checklists in extra), rule (governance — when doing X, follow Y; " +
288
- "active rules inject into the system prompt), skill (parameterized workflow bundle — validated inputs render connected Tasks, Rules, and Docs). " +
289
- "RULE extra: {condition, action, severity: 'block'|'warn'|'info'}. " +
290
- "TASK extra: {gates: [{type:'file-exists'|'contains'|'command'|'test', target, expect}], checklist: {'criterion': {proof: [{type:'file'|'symbol'|'code'|'test'|'command'|'artifact'|'url', target, expect}]}}}. " +
291
- "Legacy SKILL extra: {trigger, steps: [...], tools: [...]}. Workflow Skill schemas are versioned separately. " +
292
- "Templates are skills with subtype='artifact-template' and extra {targetKind, defaults, required}; pass template_id to instantiate.",
293
- parameters: Type.Object({
294
- kind: Type.Optional(Type.String({ description: "doc | task | rule | skill; optional when template_id supplies targetKind" })),
295
- title: Type.Optional(Type.String({ description: "required unless supplied by template defaults" })),
296
- status: Type.Optional(Type.String({ description: "default: first registered for kind" })),
297
- subtype: Type.Optional(Type.String()),
298
- body: Type.Optional(Type.String()),
299
- labels: Type.Optional(Type.Array(Type.String())),
300
- extra: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
301
- template_id: Type.Optional(Type.String({ description: "skill/artifact-template id whose defaults and requirements apply" })),
302
- project_root: Type.Optional(Type.String({ description: "required for Tasks; defaults to Pi cwd" })),
303
- }),
304
- renderCall(args, theme) { return renderPapyrusToolCall("Create artifact", args, theme); },
305
- renderResult(result, options, theme, context) { return renderPapyrusToolResult(result, options, theme, context); },
306
- async execute(_id, params, _signal, _onUpdate, ctx) {
307
- try {
308
- const a = await callService<Record<string, unknown>, Artifact>("artifact.create", {
309
- ...params,
310
- ...(params.kind === "task" ? { project_root: params.project_root ?? ctx.cwd } : {}),
311
- });
312
- return text(`Created ${artifactTextLabel(a)}`, createArtifactDetails("artifact.create", a));
313
- } catch (e) {
314
- throw new Error(`papyrus_create failed: ${e instanceof Error ? e.message : e}`);
315
- }
316
- },
317
- });
318
-
319
282
  pi.registerTool({
320
283
  name: "papyrus_query",
321
284
  label: "Papyrus Query",
@@ -344,11 +307,12 @@ export default async function (pi: ExtensionAPI) {
344
307
  name: "papyrus_graph",
345
308
  label: "Papyrus Graph",
346
309
  description:
347
- "Link artifacts with typed edges (any kind → any kind), view subgraph, update status, or read the mutation event log. " +
310
+ "Link artifacts with typed edges (any kind → any kind), view subgraph, or read the mutation event log. " +
348
311
  "RELATIONS: references, implements, follows, depends_on, documents, blocks, supersedes, relates_to, gates, triggers, contains, part_of. " +
349
312
  "ACTIONS: link (from+relation+to), unlink (from+relation+to — idempotent, no error if already absent; for Task depends_on/contains prefer the tasks tool's undepend/uncontain), " +
350
- "tree (id → bounded BFS subgraph), status (id+status → lifecycle), " +
351
- "history (who did what, when — requires id, actor, or session_id).",
313
+ "tree (id → bounded BFS subgraph), " +
314
+ "history (who did what, when — requires id, actor, or session_id). " +
315
+ "status (id+status) exists at the protocol level but is refused for every kind with its own lifecycle (Doc/Rule/Skill/Playbook/Task/Note all reject it) -- use that kind's own domain tool for status changes (docs.activate, rules.enable, tasks.start, etc), never this.",
352
316
  parameters: Type.Object({
353
317
  action: Type.String({ description: "link | unlink | tree | status | history" }),
354
318
  from: Type.Optional(Type.String()),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/papyrus",
3
- "version": "0.32.0",
3
+ "version": "0.32.2",
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"],
package/src/service.ts CHANGED
@@ -164,9 +164,31 @@ const tasksAuthorityClaim: AuthorityClaim = {
164
164
  denyMessage: () => "task lifecycle changes require a tasks.* operation so history and review invariants are preserved",
165
165
  };
166
166
 
167
+ /**
168
+ * The same status-bypass protection Tasks and Notes already have, extended to every other kind
169
+ * with its own validated transition set (Doc's draft/active/archived, Rule/Skill/Playbook's
170
+ * active/deprecated) -- graph.status previously let a caller jump straight to any status string,
171
+ * skipping e.g. Doc's draft-must-go-through-active-before-archived rule entirely.
172
+ */
173
+ function lifecycleAuthorityClaim(owner: "docs" | "rules" | "skills" | "playbooks", kind: string): AuthorityClaim {
174
+ return {
175
+ owner,
176
+ matchesArtifact: (candidateKind, subtype) => candidateKind === kind && !(kind === "doc" && subtype === NOTE_SUBTYPE),
177
+ appliesToAction: (action) => action === "status",
178
+ denyMessage: () => `${kind} lifecycle changes require a ${owner}.* operation so transition validation is preserved`,
179
+ };
180
+ }
181
+
167
182
  function createAuthorityRegistry(): AuthorityRegistry {
168
183
  const authority = new AuthorityRegistry();
169
- authority.claimAll([notesAuthorityClaim, tasksAuthorityClaim]);
184
+ authority.claimAll([
185
+ notesAuthorityClaim,
186
+ tasksAuthorityClaim,
187
+ lifecycleAuthorityClaim("docs", "doc"),
188
+ lifecycleAuthorityClaim("rules", "rule"),
189
+ lifecycleAuthorityClaim("skills", "skill"),
190
+ lifecycleAuthorityClaim("playbooks", "playbook"),
191
+ ]);
170
192
  return authority;
171
193
  }
172
194