@danypops/pi-papyrus 0.57.8 → 0.57.10
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
|
@@ -1,176 +1,30 @@
|
|
|
1
1
|
# @danypops/pi-papyrus
|
|
2
2
|
|
|
3
|
-
The Pi extension for Papyrus: native tools, TUI panels, and context injection over `@danypops/papyrus`'s authenticated loopback daemon.
|
|
3
|
+
The Pi extension for Papyrus: native tools, TUI panels, and context injection over `@danypops/papyrus`'s authenticated loopback daemon. See the [repo root README](../../README.md) for install and architecture, and [`@danypops/papyrus`](../papyrus/README.md) for the full CLI/schema reference.
|
|
4
4
|
|
|
5
5
|
## Tools
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`papyrus_query`/`papyrus_graph`/`papyrus_show` are the low-level graph-store API (filter/search, link/traverse, read+gates). Above that, `notes`, `docs`, `rules`, `playbooks`, `tasks`, and `discuss` are projected as one real tool per operation (`tasks_create`, `rules_enable`, `discuss_open`, ...) — no `action` dispatch, each with its own schema:
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- **tasks** (`tasks_create`, `tasks_update`, `tasks_list`, `tasks_show`, `tasks_plan`, `tasks_graph`, `tasks_focus`, `tasks_pause`, `tasks_unpause`, `tasks_clear_focus`, `tasks_start`, `tasks_submit`, `tasks_complete`, `tasks_reject`, `tasks_retry`, `tasks_cancel`, `tasks_cancel_subtree`, `tasks_run_gates`, `tasks_set_checklist`, `tasks_set_gates`, `tasks_depend`, `tasks_undepend`, `tasks_contain`, `tasks_uncontain`, `tasks_claim`, `tasks_heartbeat_lease`, `tasks_release_lease`, `tasks_lease`, `tasks_context`, `tasks_event_feed`, `tasks_projects`, `tasks_resolve_project`, `tasks_register_project`, `tasks_scope`, `tasks_set_scope`, `tasks_assign_project`, `tasks_active`, `tasks_focused`, `tasks_history`) — manages the singleton active focus, evidence-bearing checklists, hierarchy/dependencies, lifecycle transitions, non-blocking gates, and review completion that focuses one deterministic ready successor without claiming effort. `project_root` is required wherever a plain `tasks` call would otherwise need one (list/graph/plan/active/focused/scope/context/create) -- there is no ambient Pi cwd server-side. Resolve human scope names through `tasks_resolve_project`, then pass its canonical root. `tasks_create` supports a seven-day caller/project-scoped `idempotency_key` for exact-payload retries. `tasks_focus`/`tasks_pause`/`tasks_unpause`/`tasks_clear_focus` still authorize their write via this session's own cached secret, and still broadcast `papyrus.task-focus.v1` on Pi's own event bus for a sibling extension to observe, exactly as before
|
|
16
|
-
- **notes** (`notes_capture`, `notes_list`, `notes_show`, `notes_consume`, `notes_promote`, `notes_archive`) — capture/list/show deferred human intent, mark it consumed, promote it to an existing Task/Doc/Rule/Playbook, or archive it with an explicit disposition
|
|
17
|
-
- **docs** (`docs_create`, `docs_list`, `docs_show`, `docs_activate`, `docs_archive`, `docs_reopen`, `docs_link`, `docs_assign_project`, `docs_update`) — activate/archive/reopen and document-safe graph links; Note mutations remain behind the Notes facade
|
|
18
|
-
- **rules** (`rules_create`, `rules_list`, `rules_show`, `rules_preview`, `rules_enable`, `rules_disable`, `rules_gate`, `rules_assign_project`, `rules_update`) — enable/disable and attach governance gates to tasks
|
|
19
|
-
- **playbooks** (`playbooks_create`, `playbooks_list`, `playbooks_show`, `playbooks_invoke`, `playbooks_preview`, `playbooks_enable`, `playbooks_disable`, `playbooks_assign_project`, `playbooks_update`, `playbooks_contain`, `playbooks_uncontain`, `playbooks_depend`, `playbooks_undepend`) — a trigger and an ordered list of steps. Each step is a plain prose string (a task), or a structured object: `{kind:'doc',title,body?,subtype?,labels?}` creates a Doc, `{kind:'rule',title,body?,condition?,action?,severity?,labels?}` creates a Rule, `{kind:'call',title,playbookId,arguments?}` nests another Playbook's own run as a pipeline step gated in the same sequence, `{kind:'task',title?,body}` is an explicit task step. `playbooks_invoke` recycles the shared blueprint materialization engine: it compiles the steps and any `contain`/`depend` composition into real artifacts (a Task per plain/task step, a Doc/Rule per doc/rule step, a nested run per call step), wires task-like steps with `dependsOn` so completing one auto-focuses the next, and focuses the first real task. No text dump — one step surfaces at a time, as it becomes the focused task, same as any other Task. `playbooks_contain`/`playbooks_uncontain` nest a child Playbook inside a parent (its steps run after the parent's own, as part of it); `playbooks_depend`/`playbooks_undepend` chain a prerequisite Playbook before another (it must fully complete first) -- both are whole-Playbook composition, distinct from a `call` step's finer-grained, single-step nesting. `playbooks_preview` renders the whole tree as text with no side effects, for reading before invoking. A Playbook can declare named arguments (`{name, description?, required?, type?('string'|'number'|'boolean', default 'string'), enum?, default?}`, required defaults true; referenced in step text/call arguments as `{{name}}`); invoking with a required one unsupplied creates nothing and reports exactly which are still missing, directing the agent to ask via `discuss_open`/`discuss_reply` with `live:true` rather than guess
|
|
20
|
-
- **discuss** (`discuss_open`, `discuss_reply`, `discuss_defer`, `discuss_resume`, `discuss_settle`, `discuss_block`, `discuss_unblock`, `discuss_show`, `discuss_rounds`, `discuss_list`) — a Discussion persists across multiple rounds and can genuinely block a Task's completion until settled or deferred (`discuss_block`/`discuss_unblock`). `discuss_reply` is refused once deferred or settled -- `discuss_resume` first. `discuss_open`/`discuss_reply` can pose a structured choice via `options` (2-10 entries, each a bare string or `{title, description}`) + `options_mode` (`single`/`multi`); a later `discuss_reply` answers it via `selected`, validated against it. Adding `correct_options` + `explanation` turns that same choice into a graded quiz/knowledge assessment -- see "Quiz assessments" below. `live: true` on either gets the human's answer synchronously in the same tool call -- the operation itself always durably records the round first (exactly like every other Vehicle operation), then an optional local prompt (the pending choice's picker if one was posed, otherwise a freeform question) runs via a per-operation `interactiveFollowUps` resolver (see `@danypops/vehicle-client-pi`), degrading silently to the plain async round when there's no interactive UI
|
|
21
|
-
|
|
22
|
-
Every tool operation is registered in the daemon's `/api/v1/ops` registry; parity is verified in tests. The task consumer uses the `tasks.graph` operation, which returns task nodes with explicit parent, child, and dependency IDs rather than leaking SQLite rows or asking the UI to reconstruct relationships.
|
|
9
|
+
- **tasks** — lifecycle (`start`/`submit`/`complete`/`reject`/`retry`/`cancel`), a singleton active **focus** independent of lifecycle, evidence-bearing checklists, dependencies/containment, and non-blocking gates. `project_root` is required wherever a plain call would otherwise need one — resolve human scope names via `tasks_resolve_project` first.
|
|
10
|
+
- **notes** — capture/list/show a project-scoped deferred-intent inbox; consume, promote to a Task/Doc/Rule/Playbook, or archive with a disposition (`completed`/`duplicate`/`declined`/`superseded`).
|
|
11
|
+
- **docs** — knowledge artifacts: create, activate/archive/reopen, and graph-link.
|
|
12
|
+
- **rules** — governance injected into the Pi system prompt: enable/disable and attach gates to tasks.
|
|
13
|
+
- **playbooks** — a trigger plus an ordered list of steps (plain-prose task, or a typed `doc`/`rule`/`call` step); `playbooks_invoke` materializes the whole graph in one transaction and focuses the first real task.
|
|
14
|
+
- **discuss** — a persistent, multi-round deliberation that can genuinely block a Task's completion (`discuss_block`/`discuss_unblock`) until `settle`d or `defer`red. Supports posing a structured choice (`options`/`options_mode`), and grading it as a quiz via `correct_options` + `explanation` — see `packages/papyrus`'s own CLI reference for the full quiz semantics.
|
|
23
15
|
|
|
24
16
|
## Interactive frontends
|
|
25
17
|
|
|
26
|
-
- `/tasks` — project/focused-graph scope,
|
|
27
|
-
- `/note <request>` —
|
|
28
|
-
- `/notes` — searchable
|
|
29
|
-
- `/docs` — searchable
|
|
30
|
-
- `/rules` — severity
|
|
31
|
-
- `/playbooks
|
|
32
|
-
- `/
|
|
33
|
-
|
|
34
|
-
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, and Playbooks. 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; relationships render as a small Unicode graph via `beautiful-mermaid` when the neighbor set is real and within the routed-rendering bound, falling back to a plain, still name-resolved arrow list otherwise. `↑/↓` scrolls, `←/→` pans wide relationships, and Esc returns to the browser; non-interactive clients receive stable source text.
|
|
35
|
-
|
|
36
|
-
## Notes
|
|
37
|
-
|
|
38
|
-
Notes are project-scoped `doc/note` artifacts for human requests that should be considered later. Capturing a Note does not create work, inject the entire inbox into prompts, or imply acceptance. The agent can use the `notes` domain tool to list and consume open Notes, decide whether to create a Task, Doc, Rule, or Playbook through its owning domain tool, then promote the Note by linking that artifact. Archive requires one of `completed`, `duplicate`, `declined`, or `superseded`; promote archives with a `promoted` disposition and target ID. Capture, consumption, and disposition provenance remain in bounded Note history.
|
|
39
|
-
|
|
40
|
-
The default inbox contains draft and consumed/active Notes, is bounded to 50 rows, and has a hard limit of 200. Bodies are capped at 10,000 characters. Generic document and graph lifecycle operations reject Note mutations so they cannot bypass disposition provenance.
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
papyrus notes capture "Investigate the retry policy" --json
|
|
44
|
-
papyrus notes list --limit 25 --json
|
|
45
|
-
papyrus notes show <note-id> --json
|
|
46
|
-
papyrus notes consume <note-id> --json
|
|
47
|
-
# Create the resulting artifact with tasks/docs/rules/playbooks first, then:
|
|
48
|
-
papyrus notes promote <note-id> <target-id> --reason "Converted to tracked work" --json
|
|
49
|
-
papyrus notes archive <note-id> declined --reason "No longer relevant" --json
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
A persistent widget (matching the Task widget) shows a simple `Notes N` count, scoped by default to the current project (CWD), so a growing inbox is visible without opening `/notes`.
|
|
53
|
-
|
|
54
|
-
## Discuss
|
|
55
|
-
|
|
56
|
-
Discuss is a native, persistent deliberation, distinct from a one-shot ask: it survives across turns and sessions, takes multiple rounds, and can genuinely block a Task's completion until settled or deferred. A Discussion is a `task` artifact with `subtype: "discussion"` -- real graph citizenship (edges, show/list, `blocks`) without a dedicated enforced artifact kind of its own. Its fine-grained lifecycle (`active`/`deferred`/`settled`) lives in `extra.discussion`, since Papyrus enforces status vocabulary per kind, not per subtype.
|
|
57
|
-
|
|
58
|
-
Rounds are a dedicated append-only child table (mirroring Task history's own shape): `open` records round 1, `reply` appends further rounds, refused once the Discussion is `deferred` or `settled` -- resume first. `defer` is explicitly non-blocking (paused, reason optional, resumable); `settle` is terminal, records an outcome, and archives the Doc. `block`/`unblock` manage the blocking relationship to a Task independently of `open`.
|
|
59
|
-
|
|
60
|
-
Blocking is real: `tasks.complete` is refused while any `active` Discussion has a `blocks` edge to that Task. A `deferred` Discussion does not block -- "we will get back to this" is distinct from "resolved."
|
|
61
|
-
|
|
62
|
-
`open`/`reply` can also pose a structured choice instead of (or alongside) free text: `options` (2-10 entries) plus `options_mode` -- `single` is mutually exclusive (exactly one pick), `multi` allows several. The Discussion remembers the pending choice (`extra.discussion.pendingOptions`/`pendingOptionsMode`) until a `reply` answers it with `selected`, validated against exactly what was offered and the mode's cardinality; a reply can also pose the *next* round's choice in the same call.
|
|
63
|
-
|
|
64
|
-
Run `/discuss` for the interactive panel: browse every Discussion (the real `active`/`deferred`/`settled` state shown per row, alongside any choice awaiting an answer), open a scrollable transcript showing what was posed and picked in each round, and reply/defer/resume/settle or block/unblock a task without leaving the TUI. Replying to a pending choice shows a real picker -- the native single-select list for `single`, or a checkbox multi-select for `multi`, since no built-in multi-select exists in the Pi extension UI. Both modes append a numbered "type your own answer" row -- a genuinely open answer is exactly as valid as any posed option. The multi-select picker supports a number key as a direct quick-select (jump straight to that row instead of scrolling), and steadily highlights checked rows while dimming the rest so the eye reads "what's chosen" independent of cursor position; its cursor row blinks to mark focus. It also auto-cancels after 30s of zero input -- the very first keystroke of any kind stops that countdown permanently for that prompt. A quiz's options display lettered (A, B, C, ...) in this same picker. Opening a *new* Discussion is left to the agent (same as Docs/Rules/Playbooks) -- `/discuss` browses and drives existing ones.
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
papyrus discuss open --title "Naming" --actor alice --content "Should we rename this?" --blocks-json '["task-id"]' --json
|
|
68
|
-
papyrus discuss open --title "Which approach" --actor alice --content "Pick one" --options-json '["A","B"]' --options-mode single --json
|
|
69
|
-
papyrus discuss reply <discussion-id> --actor bob --content "I think so, here's why..." --json
|
|
70
|
-
papyrus discuss reply <discussion-id> --actor bob --content "Going with B" --selected-json '["B"]' --json
|
|
71
|
-
papyrus discuss defer <discussion-id> --reason "Waiting on design review" --json
|
|
72
|
-
papyrus discuss resume <discussion-id> --json
|
|
73
|
-
papyrus discuss settle <discussion-id> --settlement "Agreed: renaming to X" --json
|
|
74
|
-
papyrus discuss show <discussion-id> --json
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Quiz assessments
|
|
78
|
-
|
|
79
|
-
A posed choice becomes a graded knowledge assessment by adding `correct_options` (one or more entries drawn verbatim from `options` -- exact text, never an index or a display letter) + `explanation` (**required**, always shown after grading -- especially when the answer is wrong) to `discuss_open`/`discuss_reply`. Options are dynamic (any count from 2 up to the enforced 10-option ceiling, not hard-coded to four) and display lettered A, B, C, ... in every surface (the live picker, the `/discuss` transcript, the tool-call card) -- single letters always suffice since the 10-option ceiling sits well under the 26-letter limit.
|
|
80
|
-
|
|
81
|
-
- A `"single"` quiz (the participant can only pick one) must have exactly one correct option; a `"multi"` quiz may have several, graded correct iff the reply's `selected` set exactly matches the correct set -- no partial credit.
|
|
82
|
-
- The correct answer is never exposed before submission: `discuss_open`'s own response, and every `discuss_show`/`discuss_rounds` read of an unanswered quiz, carries only a `quiz: true` marker on that round -- never the answer. Server-side, it's held in dedicated hidden storage that Discuss's own general-purpose read queries structurally cannot select, not merely omitted at the API layer.
|
|
83
|
-
- Once answered, the round that carries `selected` also carries `quizResult: { correct, correctOptions, explanation }` -- the durable, permanent record of what was asked, what was picked, and whether it was right, so a later turn can adapt to demonstrated knowledge.
|
|
84
|
-
- Malformed quizzes are rejected: too few options (below the existing 2-option floor), a duplicate/unknown `correct_options` entry, more than one correct option under `"single"` mode, or `correct_options`/`explanation` given without the other.
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
# A four-option quiz with one correct answer.
|
|
88
|
-
papyrus discuss open --title "Geography check" --actor agent \
|
|
89
|
-
--content "What is the capital of France?" \
|
|
90
|
-
--options-json '["Paris","London","Berlin","Madrid"]' --options-mode single \
|
|
91
|
-
--correct-options-json '["Paris"]' \
|
|
92
|
-
--explanation "Paris has been the capital of France since 987 AD." --json
|
|
93
|
-
|
|
94
|
-
# A "select all that apply" quiz with more than one correct option.
|
|
95
|
-
papyrus discuss open --title "Primes check" --actor agent \
|
|
96
|
-
--content "Which of these are prime numbers?" \
|
|
97
|
-
--options-json '["2","3","4","9"]' --options-mode multi \
|
|
98
|
-
--correct-options-json '["2","3"]' \
|
|
99
|
-
--explanation "2 and 3 are prime; 4 and 9 are not." --json
|
|
100
|
-
|
|
101
|
-
# The participant answers like any other posed choice -- grading and the explanation come back in the same call.
|
|
102
|
-
papyrus discuss reply <discussion-id> --actor human --content "Paris" --selected-json '["Paris"]' --json
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
## Tasks
|
|
106
|
-
|
|
107
|
-
Run `/tasks` for the interactive task panel:
|
|
108
|
-
|
|
109
|
-
- `/` filters; arrow keys navigate; Enter opens task actions; `s` switches among the persisted current-project, focused-root graph, and explicit all-projects views
|
|
110
|
-
- `g` opens the programmatic Unicode graph; Tab switches dependency/composition views and arrow keys pan
|
|
111
|
-
- routed graph layouts are bounded to 48 nodes/96 edges; larger graphs use a deterministic, box-drawn line fallback, and renderer failures are contained inside the viewport rather than escaping Pi
|
|
112
|
-
- advance the `todo → in-progress → review → done` lifecycle; failed review becomes `rejected`, retry returns to `in-progress`, and `canceled` is terminal
|
|
113
|
-
- use **focus** as the independent singleton Task selection that automatic continuation follows; focusing, pausing, or resuming never changes lifecycle
|
|
114
|
-
- starting nested effort moves todo ancestors to in-progress; submitting enters review; completing review checks both typed checklist proofs and executable gates
|
|
115
|
-
- passing review marks only that task done and focuses one deterministic ready successor while leaving the successor todo until effort starts
|
|
116
|
-
- successors are never auto-completed; fan-in, fan-out, diamonds, and disconnected DAGs remain explicit
|
|
117
|
-
- inspect deterministic execution layers, readiness, a box-drawn nested hierarchy, composition, dependencies, evidence-bearing checklists, and verification gates
|
|
118
|
-
- lifecycle colors are semantic and redundant with text/glyphs: To-Do grey, in-progress yellow, review blue, rejected orange, done green, and canceled red; `▶` marks active focus
|
|
119
|
-
- Show details keeps Checklist and Validation gates separate from incidental Metadata, renders bounded post-migration lifecycle history with actor/source/reason and gate evidence, then renders relationships as a Unicode box-drawing graph footer; `↑/↓` scrolls and `←/→` pans wide graphs
|
|
120
|
-
- the compact persistent widget shows the current scope label plus bounded open work in containment order and always retains active focus when it belongs to that scope, refreshed both on tool activity and a bounded background poll so a mutation from another session or a plain CLI call is reflected without needing a Papyrus-tool call to trigger it
|
|
121
|
-
|
|
122
|
-
Authenticated CLI parity covers the changed lifecycle and focus operations:
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
papyrus tasks graph --json
|
|
126
|
-
papyrus tasks scope --json
|
|
127
|
-
papyrus tasks scope project --json
|
|
128
|
-
papyrus tasks scope graph <root-id> --json
|
|
129
|
-
papyrus tasks scope all --json
|
|
130
|
-
papyrus tasks assign-project <task-id> [project-root] --json
|
|
131
|
-
papyrus tasks active --json
|
|
132
|
-
papyrus tasks history <id> --json
|
|
133
|
-
papyrus tasks focus <id> --json
|
|
134
|
-
papyrus tasks focused --json
|
|
135
|
-
papyrus tasks pause --json
|
|
136
|
-
papyrus tasks unpause --json
|
|
137
|
-
papyrus tasks clear-focus --json
|
|
138
|
-
papyrus tasks update <id> --title "Revised title" --body "Revised body" --json
|
|
139
|
-
papyrus tasks update <id> --status todo --reason "created with legacy default" --json
|
|
140
|
-
papyrus tasks start <id> --json
|
|
141
|
-
papyrus tasks submit <id> --json
|
|
142
|
-
papyrus tasks complete <id> --json
|
|
143
|
-
papyrus tasks reject <id> --json
|
|
144
|
-
papyrus tasks retry <id> --json
|
|
145
|
-
papyrus tasks cancel <id> --json
|
|
146
|
-
papyrus tasks cancel-subtree <id> --json
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
`cancel-subtree` cancels a Task and every Task in its containment (`contains`) subtree in one call -- for tearing down a whole materialized Playbook run at once instead of canceling each Task id by hand. A Task already `done`/`canceled` is skipped, not treated as an error.
|
|
150
|
-
|
|
151
|
-
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.
|
|
152
|
-
|
|
153
|
-
### Focus-driven automatic continuation
|
|
154
|
-
|
|
155
|
-
Automatic continuation is a property of the singleton Task focus, not a per-Task automation flag. An active focus continues at Pi's public `agent_settled` boundary when Pi is idle and has no queued messages. `tasks pause` preserves the focused Task while stopping continuation; `tasks unpause` resumes it; `tasks clear-focus` removes it. Replacing focus selects an existing Task rather than creating or canceling one.
|
|
156
|
-
|
|
157
|
-
Continuation is single-flight and bounded to 20 automatic turns or 6 unchanged Task snapshots. Reaching either bound persists a paused focus and records the reason in append-only Task history. Human input resumes only these automatically paused focuses; an explicit user pause remains paused.
|
|
158
|
-
|
|
159
|
-
Checklist criteria are an item-to-proof map. Every new item requires one or more typed references to inspectable evidence; proof presence does not imply that the evidence passed an executable gate:
|
|
160
|
-
|
|
161
|
-
```ts
|
|
162
|
-
checklist: {
|
|
163
|
-
"Write failing playbook-row tests": {
|
|
164
|
-
proof: [
|
|
165
|
-
{ type: "file", target: "test/frontends.test.ts" },
|
|
166
|
-
{ type: "symbol", target: "test/frontends.test.ts#playbook row test" }
|
|
167
|
-
]
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
```
|
|
18
|
+
- `/tasks` — project/focused-graph scope, lifecycle, history, gates, dependencies, nested metadata
|
|
19
|
+
- `/note <request>` — capture one project-scoped deferred request directly
|
|
20
|
+
- `/notes` — searchable inbox with consume/promote/archive
|
|
21
|
+
- `/docs` — searchable documents with lifecycle, edit, and graph links
|
|
22
|
+
- `/rules` — condition/severity rows, injection preview, edit, enable/disable
|
|
23
|
+
- `/playbooks`, `/playbook <name>` — browse and invoke; a named playbook tab-completes straight into the editor
|
|
24
|
+
- `/discuss` — browse every Discussion, open its transcript, reply/defer/resume/settle, answer a pending choice via a real picker (single- or multi-select)
|
|
171
25
|
|
|
172
|
-
|
|
26
|
+
All frontends are daemon-backed (no direct SQLite access from the Pi process). **Show details** opens a bounded navigable view across every kind; user-authored bodies render as width-aware Markdown, generated sections (lifecycle, gates, history) keep semantic theme colors, and relationships render as a small Unicode graph.
|
|
173
27
|
|
|
174
|
-
|
|
28
|
+
## Focus-driven automatic continuation
|
|
175
29
|
|
|
176
|
-
|
|
30
|
+
An active Task focus continues automatically at Pi's `agent_settled` boundary when idle, bounded to 20 automatic turns or 6 unchanged snapshots — `tasks_pause`/`tasks_unpause`/`tasks_clear_focus` control it independently of lifecycle. Papyrus also injects a `Current`/`Desired`/`Verify`/`Next` reconciliation block at `before_agent_start` while work remains, and emits a content-free `papyrus.context-injection.v1` observation (sizes, Rule count, a payload fingerprint — never Rule/Task text or credentials) that Jittor can independently persist and assess.
|
|
@@ -42,6 +42,16 @@ export interface ArtifactBrowserConfig {
|
|
|
42
42
|
handleAction(choice: string, row: Artifact, ctx: ExtensionCommandContext): Promise<void>;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
export function artifactActivationEnabled(artifact: Pick<Artifact, "extra">): boolean {
|
|
46
|
+
const activation = artifact.extra.activation;
|
|
47
|
+
return !(
|
|
48
|
+
typeof activation === "object" &&
|
|
49
|
+
activation !== null &&
|
|
50
|
+
!Array.isArray(activation) &&
|
|
51
|
+
(activation as Record<string, unknown>).enabled === false
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
45
55
|
export function filterArtifactRows(rows: Artifact[], query: string): Artifact[] {
|
|
46
56
|
const needle = query.trim().toLowerCase();
|
|
47
57
|
if (!needle) return [...rows];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Artifact } from "@danypops/papyrus";
|
|
2
2
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import type { AutocompleteItem } from "@earendil-works/pi-tui";
|
|
4
|
-
import { showArtifactBrowser, showArtifactDetails } from "../artifact/artifact-browser.ts";
|
|
4
|
+
import { artifactActivationEnabled, showArtifactBrowser, showArtifactDetails } from "../artifact/artifact-browser.ts";
|
|
5
5
|
import { PLAYBOOK_STATUS_PRESENTATION } from "../artifact/artifact-status-presentation.ts";
|
|
6
6
|
import { parseLabelInput } from "../artifact/binder-navigation.ts";
|
|
7
7
|
import { matchArtifactByName } from "../domain-tools.ts";
|
|
@@ -85,7 +85,14 @@ export async function showPlaybooks(ctx: ExtensionCommandContext): Promise<void>
|
|
|
85
85
|
presentation: PLAYBOOK_STATUS_PRESENTATION,
|
|
86
86
|
hierarchical: true,
|
|
87
87
|
rowMeta: playbookRowMeta,
|
|
88
|
-
actions: (playbook) => [
|
|
88
|
+
actions: (playbook) => [
|
|
89
|
+
"Show details",
|
|
90
|
+
"Edit",
|
|
91
|
+
"Invoke",
|
|
92
|
+
"Toggle activation flag",
|
|
93
|
+
"Link artifact",
|
|
94
|
+
playbook.status === "active" ? "Disable" : "Enable",
|
|
95
|
+
],
|
|
89
96
|
handleAction: async (choice, playbook, commandCtx) => {
|
|
90
97
|
if (choice === "Show details") {
|
|
91
98
|
await showArtifactDetails(commandCtx, playbook.id, "playbooks.show");
|
|
@@ -112,6 +119,13 @@ export async function showPlaybooks(ctx: ExtensionCommandContext): Promise<void>
|
|
|
112
119
|
await invokeAndReport(playbook.id, playbook.title, commandCtx);
|
|
113
120
|
return;
|
|
114
121
|
}
|
|
122
|
+
if (choice === "Toggle activation flag") {
|
|
123
|
+
const current = await callService<Record<string, unknown>, Artifact>("playbooks.show", { id: playbook.id });
|
|
124
|
+
const enabled = artifactActivationEnabled(current);
|
|
125
|
+
await callService("playbooks.update", { id: playbook.id, activation_enabled: !enabled });
|
|
126
|
+
commandCtx.ui.notify(`${current.title} activation ${enabled ? "paused" : "resumed"}`, "info");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
115
129
|
if (choice === "Link artifact") {
|
|
116
130
|
const targetId = await commandCtx.ui.input("Target artifact id:", "");
|
|
117
131
|
if (!targetId) return;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Artifact } from "@danypops/papyrus";
|
|
2
2
|
import type { ExtensionCommandContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { showArtifactBrowser, showArtifactDetails } from "../artifact/artifact-browser.ts";
|
|
3
|
+
import { artifactActivationEnabled, showArtifactBrowser, showArtifactDetails } from "../artifact/artifact-browser.ts";
|
|
4
4
|
import { RULE_STATUS_PRESENTATION, severityColor } from "../artifact/artifact-status-presentation.ts";
|
|
5
5
|
import { parseLabelInput } from "../artifact/binder-navigation.ts";
|
|
6
6
|
import { callService } from "../service-client.ts";
|
|
@@ -27,7 +27,14 @@ export async function showRules(ctx: ExtensionCommandContext): Promise<void> {
|
|
|
27
27
|
presentation: RULE_STATUS_PRESENTATION,
|
|
28
28
|
hierarchical: true,
|
|
29
29
|
rowMeta: ruleRowMeta,
|
|
30
|
-
actions: (rule) => [
|
|
30
|
+
actions: (rule) => [
|
|
31
|
+
"Show details",
|
|
32
|
+
"Edit",
|
|
33
|
+
"Preview injection",
|
|
34
|
+
"Toggle activation flag",
|
|
35
|
+
"Link gated task",
|
|
36
|
+
rule.status === "active" ? "Disable" : "Enable",
|
|
37
|
+
],
|
|
31
38
|
handleAction: async (choice, rule, commandCtx) => {
|
|
32
39
|
if (choice === "Show details") await showArtifactDetails(commandCtx, rule.id, "rules.show");
|
|
33
40
|
else if (choice === "Edit") {
|
|
@@ -52,6 +59,11 @@ export async function showRules(ctx: ExtensionCommandContext): Promise<void> {
|
|
|
52
59
|
);
|
|
53
60
|
const text = result.warning === undefined ? result.preview : `${result.preview}\n\n⚠ ${result.warning}`;
|
|
54
61
|
commandCtx.ui.notify(text, result.warning === undefined ? "info" : "warning");
|
|
62
|
+
} else if (choice === "Toggle activation flag") {
|
|
63
|
+
const current = await callService<Record<string, unknown>, Artifact>("rules.show", { id: rule.id });
|
|
64
|
+
const enabled = artifactActivationEnabled(current);
|
|
65
|
+
await callService("rules.update", { id: rule.id, activation_enabled: !enabled });
|
|
66
|
+
commandCtx.ui.notify(`${current.title} activation ${enabled ? "paused" : "resumed"}`, "info");
|
|
55
67
|
} else if (choice === "Link gated task") {
|
|
56
68
|
const taskId = await commandCtx.ui.input("Task artifact id:", "");
|
|
57
69
|
if (taskId) await callService("rules.gate", { id: rule.id, task_id: taskId });
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/pi-papyrus",
|
|
3
|
-
"version": "0.57.
|
|
3
|
+
"version": "0.57.10",
|
|
4
4
|
"description": "Pi host extension for Papyrus: native tools, TUI panels, and context injection over the daemon-backed graph store",
|
|
5
|
+
"license": "MIT",
|
|
5
6
|
"type": "module",
|
|
6
|
-
"keywords": ["pi-package"],
|
|
7
|
+
"keywords": ["pi-package", "task-management", "knowledge-graph"],
|
|
7
8
|
"pi": {
|
|
8
9
|
"extensions": ["extension/src/index.ts"]
|
|
9
10
|
},
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
},
|
|
20
21
|
"dependencies": {
|
|
21
22
|
"@danypops/jittor": "^0.19.2",
|
|
22
|
-
"@danypops/papyrus": "^0.60.
|
|
23
|
+
"@danypops/papyrus": "^0.60.7",
|
|
23
24
|
"@danypops/vehicle-client": "^0.10.3",
|
|
24
25
|
"@danypops/vehicle-core": "^0.18.5",
|
|
25
26
|
"@danypops/vehicle-server": "^0.25.2",
|