@danypops/papyrus 0.54.5 → 0.55.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/papyrus",
3
- "version": "0.54.5",
3
+ "version": "0.55.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
  "main": "./src/index.ts",
@@ -319,14 +319,26 @@ const replaceProjectsCommand = buildCommand({
319
319
  });
320
320
 
321
321
  const updateCommand = buildCommand({
322
- func: async function (this: PlaybooksContext, flags: { title?: string; body?: string; labelsJson?: string[] }, id: string) {
323
- if (flags.title === undefined && flags.body === undefined && flags.labelsJson === undefined)
324
- throw new Error("playbooks update requires --title, --body, or --labels-json");
322
+ func: async function (
323
+ this: PlaybooksContext,
324
+ flags: { title?: string; body?: string; labelsJson?: string[]; trigger?: string; stepsJson?: unknown[] | Record<string, unknown> },
325
+ id: string,
326
+ ) {
327
+ if (
328
+ flags.title === undefined &&
329
+ flags.body === undefined &&
330
+ flags.labelsJson === undefined &&
331
+ flags.trigger === undefined &&
332
+ flags.stepsJson === undefined
333
+ )
334
+ throw new Error("playbooks update requires --title, --body, --labels-json, --trigger, or --steps-json");
325
335
  const artifact = await this.client.call<Record<string, unknown>, CliArtifact>("playbooks.update", {
326
336
  id,
327
337
  title: flags.title,
328
338
  body: flags.body,
329
339
  labels: flags.labelsJson,
340
+ trigger: flags.trigger,
341
+ steps: flags.stepsJson,
330
342
  });
331
343
  render.call(this, artifact, artifactLabel(artifact));
332
344
  },
@@ -335,10 +347,18 @@ const updateCommand = buildCommand({
335
347
  title: { brief: "New title", kind: "parsed", parse: String, placeholder: "text", optional: true },
336
348
  body: { brief: "New body", kind: "parsed", parse: String, placeholder: "text", optional: true },
337
349
  labelsJson: { brief: "JSON string array of labels", kind: "parsed", parse: parseStringArray, placeholder: "json", optional: true },
350
+ trigger: { brief: "New trigger -- replaces the existing one", kind: "parsed", parse: String, placeholder: "text", optional: true },
351
+ stepsJson: {
352
+ brief: "JSON array of steps -- REPLACES the entire existing step list, same shape as create's --steps-json",
353
+ kind: "parsed",
354
+ parse: parseAny,
355
+ placeholder: "json",
356
+ optional: true,
357
+ },
338
358
  },
339
359
  positional: { kind: "tuple", parameters: [{ brief: "Playbook id", parse: String, placeholder: "id" }] },
340
360
  },
341
- docs: { brief: "Change a Playbook's title/body/labels" },
361
+ docs: { brief: "Change a Playbook's title/body/labels/trigger/steps" },
342
362
  });
343
363
 
344
364
  function buildPairedIdCommand(
@@ -265,7 +265,7 @@ export function registerPlaybooksVehicleOperations(registry: VehicleRegistry, de
265
265
 
266
266
  define(
267
267
  "update",
268
- "Changes a Playbook's title/body/labels (at least one required). Refused for a read-only external projection.",
268
+ "Changes a Playbook's title/body/labels/trigger/steps (at least one required). `steps` accepts the exact same shape playbooks.create does and REPLACES the entire step list -- the way to fix a mistake or generalize a Playbook's steps after creation instead of the create-new+supersedes+disable workaround. Refused for a read-only external projection.",
269
269
  "local-write",
270
270
  {
271
271
  id: stringProp,
@@ -273,6 +273,8 @@ export function registerPlaybooksVehicleOperations(registry: VehicleRegistry, de
273
273
  title: stringProp,
274
274
  body: stringProp,
275
275
  labels: { type: "array" },
276
+ trigger: stringProp,
277
+ steps: { type: "array" },
276
278
  actor: stringProp,
277
279
  source: stringProp,
278
280
  session_id: stringProp,
@@ -202,6 +202,8 @@ export function playbooksOperations({
202
202
  title: optionalString(input, "title"),
203
203
  body: optionalString(input, "body"),
204
204
  labels: input.labels as string[] | undefined,
205
+ trigger: optionalString(input, "trigger"),
206
+ steps: input.steps,
205
207
  },
206
208
  eventContext(input),
207
209
  ),
@@ -53,7 +53,6 @@ import {
53
53
  assignArtifactProject,
54
54
  type ListFilter,
55
55
  listScoped,
56
- requireContentUpdateFields,
57
56
  requireKind,
58
57
  runTransition,
59
58
  type TransitionTable,
@@ -228,7 +227,20 @@ export interface CreatePlaybookInput {
228
227
  }
229
228
 
230
229
  export type PlaybookTransition = "enable" | "disable";
231
- export type UpdatePlaybookInput = UpdateContentInput;
230
+ /**
231
+ * Extends the shared {title,body,labels} content-update shape with the two fields that make a
232
+ * Playbook a Playbook -- steps and trigger -- previously settable only at creation. Before this,
233
+ * revising either after creation had no supported path: the only workaround was create-new +
234
+ * supersedes-link + disable-original, purely to fix a typo in one step or generalize a
235
+ * project-specific Playbook for reuse. `steps` accepts the exact same shape playbooks.create
236
+ * does (validated the same way, via validatePlaybookSteps) and REPLACES the Playbook's entire
237
+ * step list when given, matching replace_projects' own "replace the whole thing" semantics
238
+ * rather than a per-step edit -- Playbooks are typically short and hand-authored, so a full
239
+ * replace is simpler to reason about than a step-index-addressed patch. */
240
+ export interface UpdatePlaybookInput extends UpdateContentInput {
241
+ trigger?: string;
242
+ steps?: unknown;
243
+ }
232
244
 
233
245
  const PLAYBOOK_TRANSITIONS: TransitionTable<PlaybookTransition, string> = {
234
246
  enable: { from: ["deprecated"], to: "active" },
@@ -357,14 +369,37 @@ export function showPlaybook(artifacts: ArtifactStore, id: string): Artifact {
357
369
  }
358
370
 
359
371
  export function updatePlaybook(artifacts: ArtifactStore, id: string, input: UpdatePlaybookInput, context?: ArtifactEventContext): Artifact {
360
- requireContentUpdateFields(input);
372
+ if (
373
+ input.title === undefined &&
374
+ input.body === undefined &&
375
+ input.labels === undefined &&
376
+ input.trigger === undefined &&
377
+ input.steps === undefined
378
+ ) {
379
+ throw new Error("update requires title, body, labels, trigger, or steps");
380
+ }
361
381
  assertTitleBounds(input.title);
362
382
  assertBodyBounds(input.body);
363
383
  assertLabelsBounds(input.labels);
384
+ // Validated the same way create does, BEFORE any write -- an invalid steps array must never
385
+ // partially apply a title/body/labels change alongside a rejected steps change.
386
+ const declaredSteps = validatePlaybookSteps(input.steps);
364
387
  const playbook = requireLocallyOwnedContent(requireKind(artifacts, id, "playbook"));
365
- const updated = artifacts.updateContent(playbook.id, input, context);
388
+ const hasContentFields = input.title !== undefined || input.body !== undefined || input.labels !== undefined;
389
+ const updated = hasContentFields ? artifacts.updateContent(playbook.id, input, context) : playbook;
366
390
  if (!updated) throw new Error(`playbook "${id}" not found`);
367
- return updated;
391
+ if (declaredSteps === undefined && input.trigger === undefined) return updated;
392
+ const withExtra = artifacts.setExtra(
393
+ updated.id,
394
+ {
395
+ ...updated.extra,
396
+ ...(input.trigger !== undefined ? { trigger: input.trigger } : {}),
397
+ ...(declaredSteps !== undefined ? { steps: declaredSteps } : {}),
398
+ },
399
+ context,
400
+ );
401
+ if (!withExtra) throw new Error(`playbook "${id}" not found`);
402
+ return withExtra;
368
403
  }
369
404
 
370
405
  export function transitionPlaybook(