@danypops/pi-papyrus 0.38.5 → 0.39.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.
|
@@ -540,121 +540,9 @@ export function registerTasksTool(pi: ExtensionAPI): void {
|
|
|
540
540
|
});
|
|
541
541
|
}
|
|
542
542
|
|
|
543
|
-
// notes
|
|
544
|
-
//
|
|
545
|
-
//
|
|
546
|
-
|
|
547
|
-
export function registerDocsTool(pi: ExtensionAPI): void {
|
|
548
|
-
pi.registerTool({
|
|
549
|
-
name: "docs",
|
|
550
|
-
label: "Documents",
|
|
551
|
-
description: "Document domain tool. ACTIONS: create, list, show, activate, archive, reopen, link, assign_project, update, remove, remove_subtree, restore. project_root is optional at creation (omitted = unscoped); assign_project reassigns it later, or unscopes when project_root is omitted. update changes title/body/labels (at least one required) and is refused for a read-only external projection (e.g. web-spider-ingested Docs) -- capture a correction as a new linked Doc instead. remove moves a Doc to a time-gated trash, excluded from list/query but still directly showable, restorable via restore until the purge deadline; remove_subtree extends this to a whole `contains` subtree in one call. PREFER `name` (the doc's exact title) over `id`, and `target_name` over `target_id` for link -- both are backend implementation details, resolved from name automatically (target_name searches across every kind, since a link target can be a doc, task, rule, or skill). Prefer this over low-level papyrus_* tools for document work.",
|
|
552
|
-
parameters: Type.Object({
|
|
553
|
-
action: Type.String(),
|
|
554
|
-
id: Type.Optional(Type.String()),
|
|
555
|
-
name: Type.Optional(Type.String()),
|
|
556
|
-
title: Type.Optional(Type.String()),
|
|
557
|
-
body: Type.Optional(Type.String()),
|
|
558
|
-
subtype: Type.Optional(Type.String()),
|
|
559
|
-
status: Type.Optional(Type.String()),
|
|
560
|
-
text: Type.Optional(Type.String()),
|
|
561
|
-
limit: Type.Optional(Type.Number()),
|
|
562
|
-
labels: Type.Optional(Type.Array(Type.String())),
|
|
563
|
-
extra: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
|
|
564
|
-
template_id: Type.Optional(Type.String()),
|
|
565
|
-
relation: Type.Optional(Type.String()),
|
|
566
|
-
target_id: Type.Optional(Type.String()),
|
|
567
|
-
target_name: Type.Optional(Type.String()),
|
|
568
|
-
project_root: Type.Optional(Type.String()),
|
|
569
|
-
reason: Type.Optional(Type.String()),
|
|
570
|
-
}),
|
|
571
|
-
renderCall(args, theme) { return renderPapyrusToolCall("Documents", args, theme); },
|
|
572
|
-
renderResult(result, options, theme, context) { return renderPapyrusToolResult(result, options, theme, context); },
|
|
573
|
-
async execute(_id, rawParams) {
|
|
574
|
-
try {
|
|
575
|
-
const params: Record<string, unknown> = { ...rawParams };
|
|
576
|
-
const action = params.action;
|
|
577
|
-
const scopeRequest = { project_root: params.project_root };
|
|
578
|
-
await resolveNameFields(params, [
|
|
579
|
-
{ nameKey: "name", idKey: "id", listOperation: "docs.list", baseRequest: scopeRequest },
|
|
580
|
-
// Kind-agnostic: a link target can be a doc, task, rule, or skill, so this searches every kind rather than only docs.
|
|
581
|
-
{ nameKey: "target_name", idKey: "target_id", listOperation: "artifact.query", baseRequest: scopeRequest },
|
|
582
|
-
]);
|
|
583
|
-
if (action === "create") {
|
|
584
|
-
const artifact = await callService<Record<string, unknown>, Artifact>("docs.create", params);
|
|
585
|
-
return text(`Created document ${artifactLine(artifact)}`, createArtifactDetails("docs.create", artifact));
|
|
586
|
-
}
|
|
587
|
-
if (action === "list") {
|
|
588
|
-
const rows = await callService<Record<string, unknown>, Artifact[]>("docs.list", params);
|
|
589
|
-
return text(rows.length ? artifactLines(rows).join("\n") : "No documents found.", createArtifactListDetails("docs.list", rows));
|
|
590
|
-
}
|
|
591
|
-
if (action === "show") {
|
|
592
|
-
const artifact = await callService<Record<string, unknown>, Artifact>("docs.show", params);
|
|
593
|
-
return text(`${artifactLine(artifact)}\n\n${artifact.body}`, createArtifactDetails("docs.show", artifact));
|
|
594
|
-
}
|
|
595
|
-
const trashResult = await handleArtifactRemoveRestore(action, params);
|
|
596
|
-
if (trashResult) return trashResult;
|
|
597
|
-
const operations = { activate: "docs.activate", archive: "docs.archive", reopen: "docs.reopen", link: "docs.link", assign_project: "docs.assign_project", update: "docs.update" } as const;
|
|
598
|
-
const operation = operations[action as keyof typeof operations];
|
|
599
|
-
if (!operation) throw new Error(`unknown docs action: ${action}`);
|
|
600
|
-
const artifact = await callService<Record<string, unknown>, Artifact>(operation, params);
|
|
601
|
-
return text(artifactLine(artifact), createArtifactDetails(operation, artifact));
|
|
602
|
-
} catch (error) {
|
|
603
|
-
throw new Error(`docs failed: ${error instanceof Error ? error.message : error}`);
|
|
604
|
-
}
|
|
605
|
-
},
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
export function registerRulesTool(pi: ExtensionAPI): void {
|
|
610
|
-
pi.registerTool({
|
|
611
|
-
name: "rules",
|
|
612
|
-
label: "Rules",
|
|
613
|
-
description: "Rule domain tool. ACTIONS: create, list, show, preview, enable, disable, gate, assign_project, update, remove, remove_subtree, restore. project_root is optional at creation (omitted = unscoped); assign_project reassigns it later, or unscopes when project_root is omitted. Active rules inject into the agent system prompt. update changes title/body/labels (at least one required); body updates still enforce the same combined condition+action+body context-tax bound as creation, and are refused for a read-only external projection. remove moves a Rule to a time-gated trash, excluded from list/query but still directly showable, restorable via restore until the purge deadline; remove_subtree extends this to a whole `contains` subtree in one call. PREFER `name` (the rule's exact title) over `id`, and `task_name` over `task_id` for gate -- both are backend implementation details, resolved from name automatically.",
|
|
614
|
-
parameters: Type.Object({
|
|
615
|
-
action: Type.String(), id: Type.Optional(Type.String()), name: Type.Optional(Type.String()), title: Type.Optional(Type.String()),
|
|
616
|
-
body: Type.Optional(Type.String()), condition: Type.Optional(Type.String()), rule_action: Type.Optional(Type.String()),
|
|
617
|
-
severity: Type.Optional(Type.String()), labels: Type.Optional(Type.Array(Type.String())),
|
|
618
|
-
extra: Type.Optional(Type.Record(Type.String(), Type.Unknown())), status: Type.Optional(Type.String()),
|
|
619
|
-
text: Type.Optional(Type.String()), limit: Type.Optional(Type.Number()), task_id: Type.Optional(Type.String()),
|
|
620
|
-
task_name: Type.Optional(Type.String()),
|
|
621
|
-
project_root: Type.Optional(Type.String()), reason: Type.Optional(Type.String()),
|
|
622
|
-
}),
|
|
623
|
-
renderCall(args, theme) { return renderPapyrusToolCall("Rules", args, theme); },
|
|
624
|
-
renderResult(result, options, theme, context) { return renderPapyrusToolResult(result, options, theme, context); },
|
|
625
|
-
async execute(_id, rawParams, _signal, _onUpdate, ctx) {
|
|
626
|
-
try {
|
|
627
|
-
const params: Record<string, unknown> = { ...rawParams };
|
|
628
|
-
const action = params.action;
|
|
629
|
-
await resolveNameFields(params, [
|
|
630
|
-
{ nameKey: "name", idKey: "id", listOperation: "rules.list", baseRequest: { project_root: params.project_root } },
|
|
631
|
-
{ nameKey: "task_name", idKey: "task_id", listOperation: "tasks.list", baseRequest: { project_root: params.project_root ?? ctx.cwd } },
|
|
632
|
-
]);
|
|
633
|
-
if (action === "create") {
|
|
634
|
-
const artifact = await callService<Record<string, unknown>, Artifact>("rules.create", params);
|
|
635
|
-
return text(`Created rule ${artifactLine(artifact)}`, createArtifactDetails("rules.create", artifact));
|
|
636
|
-
}
|
|
637
|
-
if (action === "list") {
|
|
638
|
-
const rows = await callService<Record<string, unknown>, Artifact[]>("rules.list", params);
|
|
639
|
-
return text(rows.length ? artifactLines(rows).join("\n") : "No rules found.", createArtifactListDetails("rules.list", rows));
|
|
640
|
-
}
|
|
641
|
-
if (action === "preview") {
|
|
642
|
-
const preview = await callService<Record<string, unknown>, string>("rules.preview", params);
|
|
643
|
-
return text(preview, createPreviewDetails("rules.preview", "Rule preview", preview));
|
|
644
|
-
}
|
|
645
|
-
const trashResult = await handleArtifactRemoveRestore(action, params);
|
|
646
|
-
if (trashResult) return trashResult;
|
|
647
|
-
const operations = { show: "rules.show", enable: "rules.enable", disable: "rules.disable", gate: "rules.gate", assign_project: "rules.assign_project", update: "rules.update" } as const;
|
|
648
|
-
const operation = operations[action as keyof typeof operations];
|
|
649
|
-
if (!operation) throw new Error(`unknown rules action: ${action}`);
|
|
650
|
-
const artifact = await callService<Record<string, unknown>, Artifact>(operation, params);
|
|
651
|
-
return text(`${artifactLine(artifact)}${action === "show" ? `\n\n${artifact.body}` : ""}`, createArtifactDetails(operation, artifact));
|
|
652
|
-
} catch (error) {
|
|
653
|
-
throw new Error(`rules failed: ${error instanceof Error ? error.message : error}`);
|
|
654
|
-
}
|
|
655
|
-
},
|
|
656
|
-
});
|
|
657
|
-
}
|
|
543
|
+
// notes.*, rules.*, docs.*, and the shared artifact.* are registered as Vehicles
|
|
544
|
+
// (see ../vehicle-notes-client.ts and @danypops/papyrus's src/vehicle/papyrus-vehicle.ts),
|
|
545
|
+
// not pi.registerTool()s in this file.
|
|
658
546
|
|
|
659
547
|
export function registerPlaybooksTool(pi: ExtensionAPI): void {
|
|
660
548
|
pi.registerTool({
|
|
@@ -950,10 +838,12 @@ export function registerDiscussTool(pi: ExtensionAPI): void {
|
|
|
950
838
|
}
|
|
951
839
|
|
|
952
840
|
/** Thin orchestrator: each domain's tool is independently navigable/testable via its own registerXTool function. */
|
|
841
|
+
// docs and rules are no longer registered here -- both migrated onto Vehicle
|
|
842
|
+
// (registerNotesVehicle in vehicle-notes-client.ts, wired at session_start in
|
|
843
|
+
// index.ts), replacing their own pi.registerTool() mega-tools. See
|
|
844
|
+
// @danypops/papyrus's src/vehicle/papyrus-vehicle.ts for the server side.
|
|
953
845
|
export function registerDomainTools(pi: ExtensionAPI): void {
|
|
954
846
|
registerTasksTool(pi);
|
|
955
|
-
registerDocsTool(pi);
|
|
956
|
-
registerRulesTool(pi);
|
|
957
847
|
registerPlaybooksTool(pi);
|
|
958
848
|
registerSkillsTool(pi);
|
|
959
849
|
registerDiscussTool(pi);
|
|
@@ -1,39 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Registers
|
|
3
|
-
*
|
|
4
|
-
* VehicleRegistry side. Same daemon, same handle file, same Bearer token every
|
|
5
|
-
* other Papyrus RPC call already uses (resolveVehicleClientTarget mirrors
|
|
6
|
-
* resolvePushChannelTarget's own resolution).
|
|
2
|
+
* Registers every Vehicle-projected domain (notes.*, rules.*, docs.*, artifact.*)
|
|
3
|
+
* as real Pi tools -- see @danypops/papyrus's src/vehicle/papyrus-vehicle.ts.
|
|
7
4
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* stale handle) is tolerated the same silent-degrade way
|
|
12
|
-
* subscribeTaskPushChannel already tolerates it, rather than letting a
|
|
13
|
-
* daemon-not-running condition abort the rest of extension setup. There is
|
|
14
|
-
* no retry-on-later-connect for a tool that was never registered at all --
|
|
15
|
-
* Pi has no way to add one after the fact outside the initial registration
|
|
16
|
-
* flow.
|
|
5
|
+
* Fails silently on a stale/unreachable daemon handle instead of aborting extension
|
|
6
|
+
* setup: Papyrus's daemon doesn't auto-spawn, and a tool that failed to register
|
|
7
|
+
* here has no later retry path.
|
|
17
8
|
*
|
|
18
|
-
*
|
|
19
|
-
* (
|
|
20
|
-
*
|
|
21
|
-
* the bare extension entrypoint on every registerPapyrus(api) call, so an
|
|
22
|
-
* un-injected default would resolve the real daemonStateDir() in any test
|
|
23
|
-
* exercising the full entrypoint, not just ones about notes.
|
|
9
|
+
* Uses service-client.ts's currentVehicleClientTarget() (test-injectable) rather
|
|
10
|
+
* than resolveVehicleClientTarget() directly, so a test exercising the full
|
|
11
|
+
* extension entrypoint doesn't resolve a real daemonStateDir().
|
|
24
12
|
*/
|
|
25
13
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
26
14
|
import { RemoteVehicleClient } from "@danypops/vehicle-client/http";
|
|
27
15
|
import { registerVehicleTools } from "@danypops/vehicle-client-pi";
|
|
28
16
|
import { currentVehicleClientTarget } from "./service-client.ts";
|
|
29
17
|
|
|
18
|
+
const REGISTERED_PERMISSIONS = ["notes:read", "notes:write", "rules:read", "rules:write", "docs:read", "docs:write", "artifact:read", "artifact:write"];
|
|
19
|
+
|
|
30
20
|
export async function registerNotesVehicle(pi: ExtensionAPI): Promise<void> {
|
|
31
21
|
const target = currentVehicleClientTarget();
|
|
32
22
|
if (!target) return;
|
|
33
23
|
try {
|
|
34
24
|
const client = new RemoteVehicleClient({ baseUrl: target.baseUrl, token: target.token });
|
|
35
25
|
await registerVehicleTools(pi, client, {
|
|
36
|
-
permissions:
|
|
26
|
+
permissions: REGISTERED_PERMISSIONS,
|
|
37
27
|
principal: { id: "pi-papyrus" },
|
|
38
28
|
});
|
|
39
29
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/pi-papyrus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.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"],
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"typebox": "*"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@danypops/papyrus": "^0.
|
|
20
|
+
"@danypops/papyrus": "^0.39.0",
|
|
21
21
|
"@danypops/vehicle-core": "^0.1.1",
|
|
22
22
|
"@danypops/vehicle-server": "^0.1.1",
|
|
23
23
|
"@danypops/vehicle-client": "^0.1.1",
|