@plannotator/pi-extension 0.15.0 → 0.15.2
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 +1 -1
- package/generated/ai/base-session.ts +95 -0
- package/generated/ai/context.ts +212 -0
- package/generated/ai/endpoints.ts +309 -0
- package/generated/ai/index.ts +106 -0
- package/generated/ai/provider.ts +104 -0
- package/generated/ai/providers/claude-agent-sdk.ts +441 -0
- package/generated/ai/providers/codex-sdk.ts +430 -0
- package/generated/ai/providers/opencode-sdk.ts +491 -0
- package/generated/ai/providers/pi-events.ts +111 -0
- package/generated/ai/providers/pi-sdk-node.ts +377 -0
- package/generated/ai/providers/pi-sdk.ts +442 -0
- package/generated/ai/session-manager.ts +196 -0
- package/generated/ai/types.ts +370 -0
- package/generated/resolve-file.ts +28 -0
- package/index.ts +74 -45
- package/package.json +2 -2
- package/plannotator.html +70 -70
- package/review-editor.html +2 -2
- package/server/serverAnnotate.ts +2 -1
- package/server/serverReview.ts +5 -5
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
// @generated — DO NOT EDIT. Source: packages/ai/types.ts
|
|
2
|
+
/**
|
|
3
|
+
* Core types for the Plannotator AI provider layer.
|
|
4
|
+
*
|
|
5
|
+
* This module defines the abstract interfaces that any agent runtime
|
|
6
|
+
* (Claude Agent SDK, OpenCode, future providers) must implement to
|
|
7
|
+
* power AI features inside Plannotator's plan review and code review UIs.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Context — what the AI session knows about
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
/** The surface the user is interacting with when they invoke AI. */
|
|
15
|
+
export type AIContextMode = "plan-review" | "code-review" | "annotate";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Describes the parent agent session that originally produced the plan or diff.
|
|
19
|
+
* Used to fork conversations with full history.
|
|
20
|
+
*/
|
|
21
|
+
export interface ParentSession {
|
|
22
|
+
/** Session ID from the host agent (e.g. Claude Code session UUID). */
|
|
23
|
+
sessionId: string;
|
|
24
|
+
/** Working directory the parent session was running in. */
|
|
25
|
+
cwd: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Snapshot of plan-review-specific context.
|
|
30
|
+
* Passed when AIContextMode is "plan-review".
|
|
31
|
+
*/
|
|
32
|
+
export interface PlanContext {
|
|
33
|
+
/** The full plan markdown as submitted by the agent. */
|
|
34
|
+
plan: string;
|
|
35
|
+
/** Previous plan version (if this is a resubmission). */
|
|
36
|
+
previousPlan?: string;
|
|
37
|
+
/** The version number in the plan's history. */
|
|
38
|
+
version?: number;
|
|
39
|
+
/** Annotations the user has made so far (serialised for the prompt). */
|
|
40
|
+
annotations?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Snapshot of code-review-specific context.
|
|
45
|
+
* Passed when AIContextMode is "code-review".
|
|
46
|
+
*/
|
|
47
|
+
export interface CodeReviewContext {
|
|
48
|
+
/** The unified diff patch. */
|
|
49
|
+
patch: string;
|
|
50
|
+
/** The specific file being discussed (if scoped). */
|
|
51
|
+
filePath?: string;
|
|
52
|
+
/** The line range being discussed (if scoped). */
|
|
53
|
+
lineRange?: { start: number; end: number; side: "old" | "new" };
|
|
54
|
+
/** The code snippet being discussed (if scoped). */
|
|
55
|
+
selectedCode?: string;
|
|
56
|
+
/** Summary of annotations the user has made. */
|
|
57
|
+
annotations?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Snapshot of annotate-mode context.
|
|
62
|
+
* Passed when AIContextMode is "annotate".
|
|
63
|
+
*/
|
|
64
|
+
export interface AnnotateContext {
|
|
65
|
+
/** The markdown file content being annotated. */
|
|
66
|
+
content: string;
|
|
67
|
+
/** Path to the file on disk. */
|
|
68
|
+
filePath: string;
|
|
69
|
+
/** Summary of annotations the user has made. */
|
|
70
|
+
annotations?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Union of mode-specific contexts, discriminated by `mode`.
|
|
75
|
+
*/
|
|
76
|
+
export type AIContext =
|
|
77
|
+
| { mode: "plan-review"; plan: PlanContext; parent?: ParentSession }
|
|
78
|
+
| { mode: "code-review"; review: CodeReviewContext; parent?: ParentSession }
|
|
79
|
+
| { mode: "annotate"; annotate: AnnotateContext; parent?: ParentSession };
|
|
80
|
+
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Messages — what streams back from the AI
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
export interface AITextMessage {
|
|
86
|
+
type: "text";
|
|
87
|
+
text: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface AITextDeltaMessage {
|
|
91
|
+
type: "text_delta";
|
|
92
|
+
delta: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface AIToolUseMessage {
|
|
96
|
+
type: "tool_use";
|
|
97
|
+
toolName: string;
|
|
98
|
+
toolInput: Record<string, unknown>;
|
|
99
|
+
toolUseId: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface AIToolResultMessage {
|
|
103
|
+
type: "tool_result";
|
|
104
|
+
toolUseId?: string;
|
|
105
|
+
result: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface AIErrorMessage {
|
|
109
|
+
type: "error";
|
|
110
|
+
error: string;
|
|
111
|
+
code?: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface AIResultMessage {
|
|
115
|
+
type: "result";
|
|
116
|
+
sessionId: string;
|
|
117
|
+
success: boolean;
|
|
118
|
+
/** The final text result (if success). */
|
|
119
|
+
result?: string;
|
|
120
|
+
/** Total cost in USD (if available). */
|
|
121
|
+
costUsd?: number;
|
|
122
|
+
/** Number of agentic turns used. */
|
|
123
|
+
turns?: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface AIPermissionRequestMessage {
|
|
127
|
+
type: "permission_request";
|
|
128
|
+
requestId: string;
|
|
129
|
+
toolName: string;
|
|
130
|
+
toolInput: Record<string, unknown>;
|
|
131
|
+
title?: string;
|
|
132
|
+
displayName?: string;
|
|
133
|
+
description?: string;
|
|
134
|
+
toolUseId: string;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface AIUnknownMessage {
|
|
138
|
+
type: "unknown";
|
|
139
|
+
/** The raw message from the provider, for debugging/transparency. */
|
|
140
|
+
raw: Record<string, unknown>;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export type AIMessage =
|
|
144
|
+
| AITextMessage
|
|
145
|
+
| AITextDeltaMessage
|
|
146
|
+
| AIToolUseMessage
|
|
147
|
+
| AIToolResultMessage
|
|
148
|
+
| AIErrorMessage
|
|
149
|
+
| AIResultMessage
|
|
150
|
+
| AIPermissionRequestMessage
|
|
151
|
+
| AIUnknownMessage;
|
|
152
|
+
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// Session — a live conversation with the AI
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
export interface AISession {
|
|
158
|
+
/** Unique identifier for this session. */
|
|
159
|
+
readonly id: string;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The parent session this was forked from, if any.
|
|
163
|
+
* Null for fresh sessions.
|
|
164
|
+
*/
|
|
165
|
+
readonly parentSessionId: string | null;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Send a prompt and stream back messages.
|
|
169
|
+
* The returned async iterable yields messages as they arrive.
|
|
170
|
+
*/
|
|
171
|
+
query(prompt: string): AsyncIterable<AIMessage>;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Abort the current in-flight query.
|
|
175
|
+
* Safe to call if no query is running (no-op).
|
|
176
|
+
*/
|
|
177
|
+
abort(): void;
|
|
178
|
+
|
|
179
|
+
/** Whether a query is currently in progress. */
|
|
180
|
+
readonly isActive: boolean;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Respond to a permission request from the provider.
|
|
184
|
+
* Called when the user approves or denies a tool use in the UI.
|
|
185
|
+
*/
|
|
186
|
+
respondToPermission?(requestId: string, allow: boolean, message?: string): void;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Callback invoked when the real session ID is resolved from the provider.
|
|
190
|
+
* Set by the SessionManager to remap its internal tracking key.
|
|
191
|
+
*/
|
|
192
|
+
onIdResolved?: (oldId: string, newId: string) => void;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
// Provider — the pluggable backend
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
|
|
199
|
+
export interface AIProviderCapabilities {
|
|
200
|
+
/** Whether the provider supports forking from a parent session. */
|
|
201
|
+
fork: boolean;
|
|
202
|
+
/** Whether the provider supports resuming a prior session by ID. */
|
|
203
|
+
resume: boolean;
|
|
204
|
+
/** Whether the provider streams partial text deltas. */
|
|
205
|
+
streaming: boolean;
|
|
206
|
+
/** Whether the provider can execute tools (read files, search, etc.). */
|
|
207
|
+
tools: boolean;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface CreateSessionOptions {
|
|
211
|
+
/** The context (plan, diff, file) to seed the session with. */
|
|
212
|
+
context: AIContext;
|
|
213
|
+
/**
|
|
214
|
+
* Working directory override for the agent session.
|
|
215
|
+
* Falls back to the provider's configured cwd if omitted.
|
|
216
|
+
*/
|
|
217
|
+
cwd?: string;
|
|
218
|
+
/**
|
|
219
|
+
* Model override. Provider-specific string.
|
|
220
|
+
* Falls back to provider default if omitted.
|
|
221
|
+
*/
|
|
222
|
+
model?: string;
|
|
223
|
+
/**
|
|
224
|
+
* Maximum agentic turns for the session.
|
|
225
|
+
* Keeps inline chat cost-bounded.
|
|
226
|
+
*/
|
|
227
|
+
maxTurns?: number;
|
|
228
|
+
/**
|
|
229
|
+
* Maximum budget in USD for this session.
|
|
230
|
+
*/
|
|
231
|
+
maxBudgetUsd?: number;
|
|
232
|
+
/**
|
|
233
|
+
* Reasoning effort level (Codex only).
|
|
234
|
+
* Controls how much thinking the model does before responding.
|
|
235
|
+
*/
|
|
236
|
+
reasoningEffort?: "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* An AI provider implements the bridge between Plannotator and a specific
|
|
241
|
+
* agent runtime. The provider is responsible for:
|
|
242
|
+
*
|
|
243
|
+
* 1. Creating new AI sessions seeded with review context
|
|
244
|
+
* 2. Forking from parent agent sessions to maintain conversation history
|
|
245
|
+
* 3. Streaming responses back as AIMessage events
|
|
246
|
+
*
|
|
247
|
+
* Providers are registered by name and selected at runtime based on the
|
|
248
|
+
* host environment (Claude Code → "claude-agent-sdk", OpenCode → "opencode-sdk").
|
|
249
|
+
*/
|
|
250
|
+
export interface AIProvider {
|
|
251
|
+
/** Unique name for this provider (e.g. "claude-agent-sdk"). */
|
|
252
|
+
readonly name: string;
|
|
253
|
+
|
|
254
|
+
/** What this provider can do. */
|
|
255
|
+
readonly capabilities: AIProviderCapabilities;
|
|
256
|
+
|
|
257
|
+
/** Available models for this provider. */
|
|
258
|
+
readonly models?: ReadonlyArray<{ id: string; label: string; default?: boolean }>;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Create a fresh session (no parent history).
|
|
262
|
+
* Context is injected via the system prompt.
|
|
263
|
+
*/
|
|
264
|
+
createSession(options: CreateSessionOptions): Promise<AISession>;
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Fork from a parent agent session.
|
|
268
|
+
*
|
|
269
|
+
* The new session inherits the parent's full conversation history
|
|
270
|
+
* (files read, analysis performed, decisions made) and additionally
|
|
271
|
+
* receives the Plannotator review context. This enables the user to
|
|
272
|
+
* ask contextual questions like "why did you change this function?"
|
|
273
|
+
* without the AI losing insight.
|
|
274
|
+
*
|
|
275
|
+
* Providers that don't support real forking MUST throw. The endpoint
|
|
276
|
+
* layer checks `capabilities.fork` before calling this, so it should
|
|
277
|
+
* only be reached by providers that genuinely support history inheritance.
|
|
278
|
+
*/
|
|
279
|
+
forkSession(options: CreateSessionOptions): Promise<AISession>;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Resume a previously created Plannotator AI session by its ID.
|
|
283
|
+
* Used when the user returns to a conversation they started earlier.
|
|
284
|
+
*
|
|
285
|
+
* If the provider doesn't support resuming, this should throw.
|
|
286
|
+
*/
|
|
287
|
+
resumeSession(sessionId: string): Promise<AISession>;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Clean up any resources held by the provider.
|
|
291
|
+
* Called when the server shuts down.
|
|
292
|
+
*/
|
|
293
|
+
dispose(): void;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ---------------------------------------------------------------------------
|
|
297
|
+
// Provider configuration
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Configuration passed to a provider factory.
|
|
302
|
+
* Each provider type may extend this with its own fields.
|
|
303
|
+
*/
|
|
304
|
+
export interface AIProviderConfig {
|
|
305
|
+
/** Provider type identifier (matches AIProvider.name). */
|
|
306
|
+
type: string;
|
|
307
|
+
/** Working directory for the agent. */
|
|
308
|
+
cwd?: string;
|
|
309
|
+
/** Default model to use. */
|
|
310
|
+
model?: string;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export interface ClaudeAgentSDKConfig extends AIProviderConfig {
|
|
314
|
+
type: "claude-agent-sdk";
|
|
315
|
+
/**
|
|
316
|
+
* Tools the AI session is allowed to use.
|
|
317
|
+
* Defaults to read-only tools for safety in inline chat.
|
|
318
|
+
*/
|
|
319
|
+
allowedTools?: string[];
|
|
320
|
+
/**
|
|
321
|
+
* Permission mode for the session.
|
|
322
|
+
* Defaults to "default" (inherits user's existing permission rules).
|
|
323
|
+
*/
|
|
324
|
+
permissionMode?: "default" | "plan" | "bypassPermissions";
|
|
325
|
+
/**
|
|
326
|
+
* Explicit path to the claude CLI binary.
|
|
327
|
+
* Required when running inside a compiled binary where PATH resolution
|
|
328
|
+
* doesn't work the same way (e.g., bun build --compile).
|
|
329
|
+
*/
|
|
330
|
+
claudeExecutablePath?: string;
|
|
331
|
+
/**
|
|
332
|
+
* Setting sources to load permission rules from.
|
|
333
|
+
* Loads user's existing Claude Code permission rules so inline chat
|
|
334
|
+
* inherits what they've already approved.
|
|
335
|
+
*/
|
|
336
|
+
settingSources?: string[];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export interface CodexSDKConfig extends AIProviderConfig {
|
|
340
|
+
type: "codex-sdk";
|
|
341
|
+
/**
|
|
342
|
+
* Sandbox mode controls what the Codex agent can do.
|
|
343
|
+
* Defaults to "read-only" for safety in inline chat.
|
|
344
|
+
*/
|
|
345
|
+
sandboxMode?: "read-only" | "workspace-write" | "danger-full-access";
|
|
346
|
+
/**
|
|
347
|
+
* Explicit path to the codex CLI binary.
|
|
348
|
+
* Required when running inside a compiled binary where PATH resolution
|
|
349
|
+
* doesn't work the same way (e.g., bun build --compile).
|
|
350
|
+
*/
|
|
351
|
+
codexExecutablePath?: string;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export interface PiSDKConfig extends AIProviderConfig {
|
|
355
|
+
type: "pi-sdk";
|
|
356
|
+
/**
|
|
357
|
+
* Explicit path to the pi CLI binary.
|
|
358
|
+
* Required when running inside a compiled binary where PATH resolution
|
|
359
|
+
* doesn't work the same way (e.g., bun build --compile).
|
|
360
|
+
*/
|
|
361
|
+
piExecutablePath?: string;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export interface OpenCodeConfig extends AIProviderConfig {
|
|
365
|
+
type: "opencode-sdk";
|
|
366
|
+
/** Hostname for the OpenCode server. Default: "127.0.0.1". */
|
|
367
|
+
hostname?: string;
|
|
368
|
+
/** Port for the OpenCode server. Default: random. */
|
|
369
|
+
port?: number;
|
|
370
|
+
}
|
|
@@ -218,3 +218,31 @@ export function resolveMarkdownFile(
|
|
|
218
218
|
|
|
219
219
|
return { kind: "not_found", input };
|
|
220
220
|
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Check if a directory contains at least one markdown file.
|
|
224
|
+
* Used to validate folder annotation targets.
|
|
225
|
+
*
|
|
226
|
+
* @param dirPath - Directory to search
|
|
227
|
+
* @param excludedDirs - Directory names to skip (with trailing slash, e.g. "node_modules/")
|
|
228
|
+
*/
|
|
229
|
+
export function hasMarkdownFiles(dirPath: string, excludedDirs: string[] = IGNORED_DIRS): boolean {
|
|
230
|
+
function walk(dir: string): boolean {
|
|
231
|
+
let entries;
|
|
232
|
+
try {
|
|
233
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
234
|
+
} catch {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
for (const entry of entries) {
|
|
238
|
+
if (entry.isDirectory()) {
|
|
239
|
+
if (excludedDirs.some((d) => d === entry.name + "/")) continue;
|
|
240
|
+
if (walk(join(dir, entry.name))) return true;
|
|
241
|
+
} else if (entry.isFile() && /\.mdx?$/i.test(entry.name)) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
return walk(dirPath);
|
|
248
|
+
}
|