@aliou/pi-dev-kit 0.5.0 → 0.6.1

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.
@@ -42,6 +42,135 @@ Test event hooks by triggering the relevant actions:
42
42
  - `input`: Type a message that matches your transform pattern.
43
43
  - `before_agent_start`: Start any agent turn and verify system prompt modifications.
44
44
 
45
+ ## Unit Testing Core Logic
46
+
47
+ The core/lib pattern makes domain logic testable without the Pi framework. Extract business logic into modules that don't import from `@mariozechner/pi-coding-agent` and test them directly.
48
+
49
+ ### Testable core modules
50
+
51
+ ```typescript
52
+ // src/manager.ts — no Pi imports
53
+ export class ProcessManager {
54
+ start(name: string, command: string, cwd: string): ProcessInfo { ... }
55
+ get(id: string): ProcessInfo | undefined { ... }
56
+ kill(id: string): Promise<KillResult> { ... }
57
+ }
58
+ ```
59
+
60
+ ```typescript
61
+ // src/manager.test.ts
62
+ import { describe, it, expect, afterEach } from "vitest";
63
+ import { ProcessManager } from "./manager";
64
+
65
+ describe("ProcessManager", () => {
66
+ let manager: ProcessManager;
67
+ afterEach(() => manager.cleanup());
68
+
69
+ it("starts a process and returns info", () => {
70
+ manager = new ProcessManager();
71
+ const info = manager.start("test", "echo hello", "/tmp");
72
+ expect(info.id).toMatch(/^proc_/);
73
+ expect(info.name).toBe("test");
74
+ });
75
+ });
76
+ ```
77
+
78
+ ### Testable execute functions
79
+
80
+ Export the execute logic as a pure function with injected dependencies:
81
+
82
+ ```typescript
83
+ // src/tools/read-url.ts
84
+ export async function executeReadUrlRequest(
85
+ input: string,
86
+ signal: AbortSignal | undefined,
87
+ handlers: ReadUrlHandler[],
88
+ fetchImpl: FetchLike = fetch,
89
+ ): Promise<ExecuteResult> {
90
+ // all logic here, no Pi imports
91
+ }
92
+
93
+ // In the tool registration:
94
+ async execute(_toolCallId, params, signal, _onUpdate, _ctx) {
95
+ return executeReadUrlRequest(params.url, signal, handlers, fetch);
96
+ }
97
+ ```
98
+
99
+ ```typescript
100
+ // src/tools/read-url.test.ts
101
+ import { describe, it, expect } from "vitest";
102
+ import { executeReadUrlRequest } from "./read-url";
103
+
104
+ const mockHandler = {
105
+ name: "mock",
106
+ matches: (url: URL) => url.hostname === "example.com",
107
+ fetchData: async () => ({ markdown: "# Hello", sourceUrl: "..." }),
108
+ };
109
+
110
+ describe("executeReadUrlRequest", () => {
111
+ it("routes to matching handler", async () => {
112
+ const result = await executeReadUrlRequest(
113
+ "https://example.com/page",
114
+ undefined,
115
+ [mockHandler],
116
+ );
117
+ expect(result.details.handler).toBe("mock");
118
+ });
119
+ });
120
+ ```
121
+
122
+ ### Handler pattern
123
+
124
+ For tools that route to different backends based on input, use an interface:
125
+
126
+ ```typescript
127
+ export interface ReadUrlHandler {
128
+ name: string;
129
+ matches(url: URL): boolean;
130
+ fetchData(url: URL, signal?: AbortSignal): Promise<HandlerResult>;
131
+ }
132
+ ```
133
+
134
+ Multiple handlers are tried in order. Each handler is independently testable.
135
+
136
+ ### Pi stub for hook testing
137
+
138
+ When testing hooks or tool registration, create a minimal Pi stub:
139
+
140
+ ```typescript
141
+ function createPiStub() {
142
+ const toolCallHandlers: Array<Parameters<ExtensionAPI["on"]>[1]> = [];
143
+ const registeredTools: unknown[] = [];
144
+
145
+ const pi = {
146
+ on(eventName: string, handler: Parameters<ExtensionAPI["on"]>[1]) {
147
+ if (eventName === "tool_call") toolCallHandlers.push(handler);
148
+ },
149
+ registerTool(tool: unknown) {
150
+ registeredTools.push(tool);
151
+ },
152
+ } as unknown as ExtensionAPI;
153
+
154
+ return { pi, toolCallHandlers, registeredTools };
155
+ }
156
+ ```
157
+
158
+ ### Test setup
159
+
160
+ Extensions use vitest. Add to `package.json`:
161
+
162
+ ```json
163
+ {
164
+ "devDependencies": {
165
+ "vitest": "^3.2.0"
166
+ },
167
+ "scripts": {
168
+ "test": "vitest run",
169
+ "test:watch": "vitest"
170
+ }
171
+ }
172
+ ```
173
+
45
174
  ## Debugging
46
175
 
47
176
  Extension errors are logged to the pi log file. Check the output for stack traces: