@caplets/pi 0.1.3 → 0.1.5

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.
Files changed (2) hide show
  1. package/dist/index.js +49 -23
  2. package/package.json +7 -3
package/dist/index.js CHANGED
@@ -1,26 +1,16 @@
1
+ import { keyText } from "@earendil-works/pi-coding-agent";
2
+ import { generatedToolInputJsonSchema } from "@caplets/core/generated-tool-input-schema";
1
3
  import { createNativeCapletsService, registerNativeCapletsProcessCleanup } from "@caplets/core/native";
2
- import { Type } from "@sinclair/typebox";
3
- import { operations } from "@caplets/core/generated-tool-input-schema";
4
- //#region src/schema.ts
5
- function capletsPiParameters() {
6
- return Type.Object({
7
- operation: Type.Union(operations.map((operation) => Type.Literal(operation))),
8
- query: Type.Optional(Type.String()),
9
- limit: Type.Optional(Type.Integer({ minimum: 1 })),
10
- tool: Type.Optional(Type.String()),
11
- arguments: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
12
- fields: Type.Optional(Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }))
13
- }, { additionalProperties: false });
14
- }
15
- //#endregion
16
4
  //#region src/index.ts
17
5
  function capletsPiExtension(pi, options = {}) {
18
6
  const ownsService = !options.service;
19
7
  const service = options.service ?? createNativeCapletsService();
20
8
  if (ownsService) registerNativeCapletsProcessCleanup(service);
21
9
  const registeredCapletToolSignatures = /* @__PURE__ */ new Map();
22
- let knownCapletTools = new Set(pi.getActiveTools?.().filter((name) => name.startsWith("caplets_")) ?? []);
23
- const syncTools = (caplets = service.listTools()) => {
10
+ let currentCapletTools = /* @__PURE__ */ new Set();
11
+ let knownCapletTools = /* @__PURE__ */ new Set();
12
+ let canSyncActiveTools = false;
13
+ const syncToolRegistrations = (caplets = service.listTools()) => {
24
14
  const nextCapletTools = new Set(caplets.map((caplet) => caplet.toolName));
25
15
  for (const [toolName] of registeredCapletToolSignatures) if (!nextCapletTools.has(toolName)) registeredCapletToolSignatures.delete(toolName);
26
16
  for (const caplet of caplets) {
@@ -29,14 +19,24 @@ function capletsPiExtension(pi, options = {}) {
29
19
  registeredCapletToolSignatures.set(caplet.toolName, signature);
30
20
  pi.registerTool(createPiTool(service, caplet));
31
21
  }
32
- if (pi.getActiveTools && pi.setActiveTools) {
33
- const activeNonCaplets = pi.getActiveTools().filter((name) => !knownCapletTools.has(name) && !nextCapletTools.has(name));
34
- pi.setActiveTools([...activeNonCaplets, ...nextCapletTools]);
35
- }
22
+ currentCapletTools = nextCapletTools;
23
+ return nextCapletTools;
24
+ };
25
+ const syncActiveTools = (nextCapletTools = currentCapletTools) => {
26
+ if (!canSyncActiveTools || !pi.getActiveTools || !pi.setActiveTools) return;
27
+ const activeNonCaplets = pi.getActiveTools().filter((name) => !knownCapletTools.has(name) && !nextCapletTools.has(name));
28
+ pi.setActiveTools([...activeNonCaplets, ...nextCapletTools]);
36
29
  knownCapletTools = nextCapletTools;
37
30
  };
38
- syncTools();
39
- const unsubscribe = service.onToolsChanged(syncTools);
31
+ currentCapletTools = syncToolRegistrations();
32
+ const unsubscribe = service.onToolsChanged((caplets) => {
33
+ syncActiveTools(syncToolRegistrations(caplets));
34
+ });
35
+ pi.on?.("session_start", () => {
36
+ canSyncActiveTools = true;
37
+ knownCapletTools = new Set(pi.getActiveTools?.().filter((name) => name.startsWith("caplets_")) ?? []);
38
+ syncActiveTools();
39
+ });
40
40
  pi.on?.("session_shutdown", () => {
41
41
  unsubscribe();
42
42
  if (ownsService) service.close();
@@ -57,7 +57,7 @@ function createPiTool(service, caplet) {
57
57
  description: caplet.description,
58
58
  promptSnippet: `Use ${caplet.toolName} for the ${caplet.title} Caplet capability domain.`,
59
59
  promptGuidelines: caplet.promptGuidance,
60
- parameters: capletsPiParameters(),
60
+ parameters: generatedToolInputJsonSchema(),
61
61
  async execute(_toolCallId, params) {
62
62
  const result = await service.execute(caplet.caplet, params);
63
63
  const serialized = serializeResult(result);
@@ -71,9 +71,35 @@ function createPiTool(service, caplet) {
71
71
  serializationError: serialized.serializationError
72
72
  } : { result }
73
73
  };
74
+ },
75
+ renderCall(args, theme) {
76
+ const suffix = [stringProperty(args, "operation"), stringProperty(args, "tool")].filter(Boolean).join(" ");
77
+ return textComponent(theme.fg("toolTitle", theme.bold(caplet.title)) + (suffix ? ` ${theme.fg("muted", suffix)}` : ""));
78
+ },
79
+ renderResult(result, { expanded, isPartial }, theme) {
80
+ if (isPartial) return textComponent(theme.fg("warning", `${caplet.title} running...`));
81
+ const output = result.content.filter((item) => item.type === "text").map((item) => item.text).join("\n");
82
+ if (expanded) return textComponent(theme.fg("success", `✓ ${caplet.title} complete`) + theme.fg("dim", ` (${toolExpandKeyText()} to collapse)`) + (output ? `\n${theme.fg("toolOutput", output)}` : ""));
83
+ return textComponent(theme.fg("success", `✓ ${caplet.title} complete`) + theme.fg("dim", ` (${toolExpandKeyText()} to expand)`));
74
84
  }
75
85
  };
76
86
  }
87
+ function toolExpandKeyText() {
88
+ return keyText("app.tools.expand") || "ctrl+o";
89
+ }
90
+ function textComponent(text) {
91
+ return {
92
+ render(_width) {
93
+ return text.split("\n");
94
+ },
95
+ invalidate() {}
96
+ };
97
+ }
98
+ function stringProperty(value, key) {
99
+ if (!value || typeof value !== "object" || !(key in value)) return;
100
+ const property = value[key];
101
+ return typeof property === "string" && property.length > 0 ? property : void 0;
102
+ }
77
103
  function serializeResult(result) {
78
104
  try {
79
105
  return { text: JSON.stringify(result, null, 2) ?? "null" };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caplets/pi",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Native Pi extension for Caplets.",
5
5
  "homepage": "https://github.com/spiritledsoftware/caplets#readme",
6
6
  "bugs": {
@@ -26,8 +26,7 @@
26
26
  "access": "public"
27
27
  },
28
28
  "dependencies": {
29
- "@sinclair/typebox": "^0.34.49",
30
- "@caplets/core": "0.12.1"
29
+ "@caplets/core": "0.13.0"
31
30
  },
32
31
  "devDependencies": {
33
32
  "@types/node": "^25.7.0",
@@ -41,6 +40,11 @@
41
40
  "engines": {
42
41
  "node": ">=22"
43
42
  },
43
+ "pi": {
44
+ "extensions": [
45
+ "dist/index.js"
46
+ ]
47
+ },
44
48
  "scripts": {
45
49
  "build": "rm -rf dist && rolldown -c",
46
50
  "typecheck": "tsc --noEmit",