@danypops/pi-papyrus 0.36.2 → 0.37.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 +3 -0
- package/extension/src/domain-tools.ts +19 -8
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -116,8 +116,11 @@ papyrus tasks complete <id> --json
|
|
|
116
116
|
papyrus tasks reject <id> --json
|
|
117
117
|
papyrus tasks retry <id> --json
|
|
118
118
|
papyrus tasks cancel <id> --json
|
|
119
|
+
papyrus tasks cancel-subtree <id> --json
|
|
119
120
|
```
|
|
120
121
|
|
|
122
|
+
`cancel-subtree` cancels a Task and every Task in its containment (`contains`) subtree in one call -- for tearing down a whole materialized Playbook/Skill run at once instead of canceling each Task id by hand. A Task already `done`/`canceled` is skipped, not treated as an error.
|
|
123
|
+
|
|
121
124
|
Task edits mutate the existing Papyrus-owned Task identity and append an `updated` event; title, body, and labels can be revised without canceling the Task or creating a replacement. Lifecycle, relationships, gates, checklist metadata, scope, and focus remain intact. The same `update` action provides a narrowly guarded recovery for Tasks accidentally created terminal by a legacy default: `status=todo` requires an audit reason, cannot be combined with content edits, only applies when `created` is the sole lifecycle event, and appends `creation_recovered` rather than rewriting history.
|
|
122
125
|
|
|
123
126
|
### Focus-driven automatic continuation
|
|
@@ -244,7 +244,7 @@ export function registerTasksTool(pi: ExtensionAPI): void {
|
|
|
244
244
|
pi.registerTool({
|
|
245
245
|
name: "tasks",
|
|
246
246
|
label: "Tasks",
|
|
247
|
-
description: "Task domain tool. ACTIONS: create, update, list, show, history, context, scope, set_scope, assign_project, graph, plan, active, focused, focus, pause, unpause, clear_focus, start, submit, complete, reject, retry, cancel, run_gates, set_checklist, depend, undepend, contain, uncontain, remove, restore, claim, heartbeat_lease, release_lease, lease, event_feed.
|
|
247
|
+
description: "Task domain tool. ACTIONS: create, update, list, show, history, context, scope, set_scope, assign_project, graph, plan, active, focused, focus, pause, unpause, clear_focus, start, submit, complete, reject, retry, cancel, cancel_subtree, run_gates, set_checklist, depend, undepend, contain, uncontain, remove, restore, claim, heartbeat_lease, release_lease, lease, event_feed. Lifecycle: todo → in-progress → review → done; review failure → rejected → retry → in-progress; canceled is terminal. Focus and lease are independent of lifecycle and of each other -- multiple sessions can focus the same task while only one holds its lease (claim throws if a different owner already holds one; release/heartbeat need the exact token claim returned; owner defaults to this session's id). context returns the full plan (the system prompt itself only carries a one-line pointer) -- call it explicitly after a compaction or before reconciling. complete runs gates + checklist-proof review, then focuses one ready successor. cancel_subtree cancels a task and its whole containment subtree in one call, skipping tasks already done/canceled. remove/restore use a time-gated trash (refuses the live Focus); undepend/uncontain are idempotent no-ops when the edge is already absent. update recovers an accidentally-terminal task via status=todo + reason, without rewriting real history. Prefer `name` (exact title) over `id` -- id is a backend detail, resolved automatically, needed only to disambiguate a shared title; `dependency_name`/`parent_name`/`child_name`/`root_task_name`/`depends_on_names` are the same pattern for their `_id` counterparts.",
|
|
248
248
|
parameters: Type.Object({
|
|
249
249
|
action: Type.String(),
|
|
250
250
|
id: Type.Optional(Type.String()),
|
|
@@ -447,6 +447,11 @@ export function registerTasksTool(pi: ExtensionAPI): void {
|
|
|
447
447
|
const output = lease ? `Leased by "${lease.owner}" until ${lease.leaseExpiresAt} (token ${lease.token}).` : "No live lease.";
|
|
448
448
|
return text(output, createPreviewDetails(operation, "Task lease", output));
|
|
449
449
|
}
|
|
450
|
+
if (action === "cancel_subtree") {
|
|
451
|
+
const outcome = await callService<Record<string, unknown>, { canceled: string[]; skipped: string[] }>("tasks.cancel_subtree", request);
|
|
452
|
+
const output = `Canceled ${outcome.canceled.length} task(s)${outcome.skipped.length > 0 ? `, skipped ${outcome.skipped.length} already-terminal` : ""}.`;
|
|
453
|
+
return text(output, createPreviewDetails("tasks.cancel_subtree", "Cancel task subtree", JSON.stringify(outcome, null, 2)));
|
|
454
|
+
}
|
|
450
455
|
const trashResult = await handleArtifactRemoveRestore(action, params);
|
|
451
456
|
if (trashResult) return trashResult;
|
|
452
457
|
const operations = {
|
|
@@ -675,6 +680,18 @@ export function registerPlaybooksTool(pi: ExtensionAPI): void {
|
|
|
675
680
|
try {
|
|
676
681
|
const params: Record<string, unknown> = { ...rawParams };
|
|
677
682
|
const action = params.action;
|
|
683
|
+
// Name resolution must search regardless of project scope -- a Playbook itself is
|
|
684
|
+
// commonly unscoped (e.g. a cross-repo lab-deploy playbook), so resolutionRequest
|
|
685
|
+
// uses the caller's ORIGINAL project_root (undefined unless explicitly given), never
|
|
686
|
+
// the invoke-specific default applied below -- that default is only for where the
|
|
687
|
+
// resulting TASKS land, not for finding the playbook artifact itself.
|
|
688
|
+
const resolutionRequest = { project_root: params.project_root };
|
|
689
|
+
await resolveNameFields(params, [
|
|
690
|
+
{ nameKey: "name", idKey: "id", listOperation: "playbooks.list", baseRequest: resolutionRequest },
|
|
691
|
+
{ nameKey: "parent_name", idKey: "parent_id", listOperation: "playbooks.list", baseRequest: resolutionRequest },
|
|
692
|
+
{ nameKey: "child_name", idKey: "child_id", listOperation: "playbooks.list", baseRequest: resolutionRequest },
|
|
693
|
+
{ nameKey: "dependency_name", idKey: "dependency_id", listOperation: "playbooks.list", baseRequest: resolutionRequest },
|
|
694
|
+
]);
|
|
678
695
|
// invoke ends by calling tasks.focus server-side -- that focus write must land in the
|
|
679
696
|
// SAME session scope the tasks tool reads from (ctx.sessionManager.getSessionId()),
|
|
680
697
|
// the same resolution the tasks tool itself always applies, or the entry task's focus
|
|
@@ -684,6 +701,7 @@ export function registerPlaybooksTool(pi: ExtensionAPI): void {
|
|
|
684
701
|
// task is invisible to tasks(action=focused) even with the right session -- confirmed
|
|
685
702
|
// live (the focus_set event existed with the correct sessionId, but Tasks.focused's own
|
|
686
703
|
// project-scope filter silently excluded the unscoped task from a cwd-scoped read).
|
|
704
|
+
// Applied AFTER name resolution: it must never affect finding the playbook itself.
|
|
687
705
|
if (action === "invoke") {
|
|
688
706
|
const resolvedSessionId = params.session_id ?? ctx.sessionManager.getSessionId();
|
|
689
707
|
Object.assign(params, {
|
|
@@ -692,13 +710,6 @@ export function registerPlaybooksTool(pi: ExtensionAPI): void {
|
|
|
692
710
|
...sessionSecretField(resolvedSessionId as string),
|
|
693
711
|
});
|
|
694
712
|
}
|
|
695
|
-
const resolutionRequest = { project_root: params.project_root };
|
|
696
|
-
await resolveNameFields(params, [
|
|
697
|
-
{ nameKey: "name", idKey: "id", listOperation: "playbooks.list", baseRequest: resolutionRequest },
|
|
698
|
-
{ nameKey: "parent_name", idKey: "parent_id", listOperation: "playbooks.list", baseRequest: resolutionRequest },
|
|
699
|
-
{ nameKey: "child_name", idKey: "child_id", listOperation: "playbooks.list", baseRequest: resolutionRequest },
|
|
700
|
-
{ nameKey: "dependency_name", idKey: "dependency_id", listOperation: "playbooks.list", baseRequest: resolutionRequest },
|
|
701
|
-
]);
|
|
702
713
|
if (action === "create" || action === "invoke" || action === "preview") normalizeJsonEncodedField(params, "arguments");
|
|
703
714
|
if (action === "create") {
|
|
704
715
|
const artifact = await callService<Record<string, unknown>, Artifact>("playbooks.create", params);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/pi-papyrus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "Pi host extension for Papyrus: native tools, TUI panels, and context injection over the daemon-backed graph store",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": ["pi-package"],
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@danypops/daemon-kit": "^0.10.0",
|
|
21
|
-
"@danypops/papyrus": "^0.
|
|
21
|
+
"@danypops/papyrus": "^0.37.0",
|
|
22
22
|
"beautiful-mermaid": "1.1.3"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|