@akonwi/kit 0.1.0 → 0.2.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.
package/dist/kit CHANGED
Binary file
package/dist/plugin.d.ts CHANGED
@@ -1,45 +1,76 @@
1
1
  import type { AgentMessage } from "@mariozechner/pi-agent-core";
2
2
  import type { Api, Model, Static, TSchema } from "@mariozechner/pi-ai";
3
- import type { JSX } from "solid-js";
4
3
  import type { ToastInput } from "./toasts";
5
4
 
6
- export type PluginSubscription = () => void;
7
- export type PluginDispose = () => void;
5
+ export type Disposer = () => void;
8
6
 
9
- export type PluginOverlaySurfaceProps = {
10
- zIndex?: number;
11
- };
7
+ interface UI {
8
+ toast: (toast: ToastInput) => void;
9
+ select(input: {
10
+ title: string;
11
+ message?: string;
12
+ options: string[];
13
+ filterable?: boolean;
14
+ placeholder?: string;
15
+ }): Promise<string | undefined>;
16
+ select<T>(input: {
17
+ title: string;
18
+ message?: string;
19
+ options: Array<{ label: string; value: T; description?: string }>;
20
+ filterable?: boolean;
21
+ placeholder?: string;
22
+ }): Promise<T | undefined>;
23
+ input(input: {
24
+ title: string;
25
+ message?: string;
26
+ placeholder?: string;
27
+ initialValue?: string;
28
+ }): Promise<string | undefined>;
29
+ confirm(input: {
30
+ title: string;
31
+ message?: string;
32
+ confirmLabel?: string;
33
+ cancelLabel?: string;
34
+ defaultValue?: boolean;
35
+ }): Promise<boolean>;
36
+ }
12
37
 
13
- export type PluginOverlayComponentProps<T> = {
14
- done: (result: T) => void;
15
- surfaceProps: PluginOverlaySurfaceProps;
16
- };
38
+ export type ToolExecutionMode = "sequential" | "parallel";
17
39
 
18
- export type PluginUI = {
19
- toast: (toast: ToastInput) => void;
20
- custom: <T>(
21
- component: (props: PluginOverlayComponentProps<T>) => JSX.Element,
22
- ) => Promise<T>;
23
- getTranscriptViewport: () => { width: number; height: number } | null;
40
+ export type ToolCall = {
41
+ id: string;
42
+ name: string;
43
+ input: Record<string, unknown>;
24
44
  };
25
45
 
26
- export type PluginToolExecutionMode = "sequential" | "parallel";
46
+ export type ToolCallDecision =
47
+ | { action: "allow" }
48
+ | { action: "reject-and-continue"; message?: string }
49
+ | undefined
50
+ // biome-ignore lint/suspicious/noConfusingVoidType: tool call handlers may omit a return value to allow the call.
51
+ | void;
27
52
 
28
- export type PluginToolResultContentBlock =
53
+ export type ToolCallHandler = (
54
+ toolCall: ToolCall,
55
+ ctx: EventContext,
56
+ signal?: AbortSignal,
57
+ ) => ToolCallDecision | Promise<ToolCallDecision>;
58
+
59
+ export type ToolResultContent =
29
60
  | { type: "text"; text: string }
30
61
  | { type: "image"; data: string; mimeType: string };
31
62
 
32
- export type PluginToolResult<TDetails = unknown> = {
33
- content: PluginToolResultContentBlock[];
63
+ export type ToolResult<TDetails = unknown> = {
64
+ content: ToolResultContent[];
34
65
  details: TDetails;
35
66
  terminate?: boolean;
36
67
  };
37
68
 
38
- export type PluginToolUpdateCallback<TDetails = unknown> = (
39
- partialResult: PluginToolResult<TDetails>,
69
+ export type ToolUpdateCallback<TDetails = unknown> = (
70
+ partialResult: ToolResult<TDetails>,
40
71
  ) => void;
41
72
 
42
- export type PluginToolDefinition<
73
+ export type ToolDefinition<
43
74
  TParameters extends TSchema = TSchema,
44
75
  TDetails = unknown,
45
76
  > = {
@@ -54,21 +85,21 @@ export type PluginToolDefinition<
54
85
  toolCallId: string,
55
86
  params: Static<TParameters>,
56
87
  signal?: AbortSignal,
57
- onUpdate?: PluginToolUpdateCallback<TDetails>,
58
- ) => Promise<PluginToolResult<TDetails>>;
59
- executionMode?: PluginToolExecutionMode;
88
+ onUpdate?: ToolUpdateCallback<TDetails>,
89
+ ) => Promise<ToolResult<TDetails>>;
90
+ executionMode?: ToolExecutionMode;
60
91
  };
61
92
 
62
- export type PluginLogger = {
93
+ type Logger = {
63
94
  log: (...args: unknown[]) => void;
64
95
  };
65
96
 
66
- export type PluginMessagePart = {
97
+ export type MessagePart = {
67
98
  type: string;
68
99
  [key: string]: unknown;
69
100
  };
70
101
 
71
- export type PluginSession = {
102
+ export type Session = {
72
103
  id: string;
73
104
  cwd: string;
74
105
  name?: string;
@@ -82,11 +113,11 @@ export type PluginSession = {
82
113
  [key: string]: unknown;
83
114
  };
84
115
 
85
- export type PluginSessionAPI = {
86
- get: () => PluginSession;
116
+ type SessionAPI = {
117
+ get: () => Session;
87
118
  getMessages: () => AgentMessage[];
88
119
  setName: (name: string) => Promise<void>;
89
- submitMessage: (input: string | PluginMessagePart[]) => Promise<void>;
120
+ submitMessage: (input: string | MessagePart[]) => Promise<void>;
90
121
  submitPromptCommandMessage: (
91
122
  command: string,
92
123
  args: string,
@@ -94,9 +125,7 @@ export type PluginSessionAPI = {
94
125
  ) => Promise<void>;
95
126
  };
96
127
 
97
- export type PluginReviewDiffView = "unified" | "split";
98
-
99
- export type PluginSettings = {
128
+ export type Settings = {
100
129
  theme?: string;
101
130
  bells?: boolean;
102
131
  speech?:
@@ -110,7 +139,7 @@ export type PluginSettings = {
110
139
  guidedQuestions?: boolean;
111
140
  sessionNaming?: boolean;
112
141
  diffs?: {
113
- view?: PluginReviewDiffView;
142
+ view?: "unified" | "split";
114
143
  };
115
144
  retry?: {
116
145
  enabled?: boolean;
@@ -121,76 +150,76 @@ export type PluginSettings = {
121
150
  [key: string]: unknown;
122
151
  };
123
152
 
124
- export type PluginSettingsAPI = {
125
- get: () => PluginSettings;
126
- update: (patch: Partial<PluginSettings>) => Promise<void>;
153
+ type SettingsAPI = {
154
+ get: () => Settings;
155
+ update: (patch: Partial<Settings>) => Promise<void>;
127
156
  };
128
157
 
129
- export type PluginModelAPI = {
158
+ type ModelAPI = {
130
159
  getCurrent: () => Model<Api> | undefined;
131
160
  };
132
161
 
133
- export type PluginSystemAPI = {
162
+ type SystemAPI = {
134
163
  readonly cwd: string;
135
164
  open: (url: string | URL) => Promise<void>;
136
165
  };
137
166
 
138
- export type PluginEventContext = {
139
- logger: PluginLogger;
140
- ui: PluginUI;
141
- session: PluginSessionAPI;
142
- settings: PluginSettingsAPI;
143
- model: PluginModelAPI;
144
- system: PluginSystemAPI;
167
+ export type EventContext = {
168
+ logger: Logger;
169
+ ui: UI;
170
+ session: SessionAPI;
171
+ settings: SettingsAPI;
172
+ model: ModelAPI;
173
+ system: SystemAPI;
145
174
  };
146
175
 
147
- export type PluginCommandContext = PluginEventContext & {
176
+ export type CommandContext = EventContext & {
148
177
  args: string;
149
178
  };
150
179
 
151
- export type PluginCommandOptions = {
180
+ export type CommandOptions = {
152
181
  title?: string;
153
182
  description?: string;
154
183
  argName?: string;
155
184
  category?: string;
156
185
  };
157
186
 
158
- export type PluginRuntimeEvent<Type extends string = string> = {
187
+ export type RuntimeEvent<Type extends string = string> = {
159
188
  type: Type;
160
189
  } & Record<string, unknown>;
161
190
 
162
- export type PluginEventHandler<Type extends string = string> = (
163
- event: PluginRuntimeEvent<Type>,
164
- ctx: PluginEventContext,
191
+ export type EventHandler<Type extends string = string> = (
192
+ event: RuntimeEvent<Type>,
193
+ ctx: EventContext,
165
194
  ) => void | Promise<void>;
166
195
 
167
196
  export interface PluginAPI {
168
- logger: PluginLogger;
169
- ui: PluginUI;
170
- session: PluginSessionAPI;
171
- settings: PluginSettingsAPI;
172
- model: PluginModelAPI;
173
- system: PluginSystemAPI;
174
- on(handler: PluginEventHandler): PluginSubscription;
175
- on<Type extends string>(
176
- type: Type,
177
- handler: PluginEventHandler<Type>,
178
- ): PluginSubscription;
197
+ logger: Logger;
198
+ ui: UI;
199
+ session: SessionAPI;
200
+ settings: SettingsAPI;
201
+ model: ModelAPI;
202
+ system: SystemAPI;
203
+ on(handler: EventHandler): Disposer;
204
+ on<Type extends string>(type: Type, handler: EventHandler<Type>): Disposer;
179
205
  on<Prefix extends string>(
180
206
  options: { prefix: Prefix },
181
- handler: PluginEventHandler<`${Prefix}${string}`>,
182
- ): PluginSubscription;
207
+ handler: EventHandler<`${Prefix}${string}`>,
208
+ ): Disposer;
183
209
  registerCommand: (
184
210
  id: string,
185
- options: PluginCommandOptions,
186
- handler: (ctx: PluginCommandContext) => void | Promise<void>,
187
- ) => PluginSubscription;
211
+ options: CommandOptions,
212
+ handler: (ctx: CommandContext) => void | Promise<void>,
213
+ ) => Disposer;
188
214
  registerTool: <TParameters extends TSchema, TDetails>(
189
- tool: PluginToolDefinition<TParameters, TDetails>,
190
- ) => PluginSubscription;
191
- addSystemPrompt: (text: string) => PluginSubscription;
192
- addDebugSection: (key: string, lines: string[]) => PluginSubscription;
215
+ tool: ToolDefinition<TParameters, TDetails>,
216
+ ) => Disposer;
217
+ onToolCall: (handler: ToolCallHandler) => Disposer;
218
+ addSystemPrompt: (text: string) => Disposer;
219
+ addDebugSection: (key: string, lines: string[]) => Disposer;
193
220
  }
194
221
 
195
- // biome-ignore lint/suspicious/noConfusingVoidType: plugin definitions may omit a return value or return a disposer.
196
- export type PluginDefinition = (kit: PluginAPI) => void | PluginDispose;
222
+ export type Plugin = (
223
+ kit: PluginAPI,
224
+ // biome-ignore lint/suspicious/noConfusingVoidType: plugin definitions may omit a return value or return a disposer.
225
+ ) => void | Disposer;
package/dist/toasts.d.ts CHANGED
@@ -3,7 +3,7 @@ export type ToastVariant = "error" | "warning" | "info";
3
3
  export type ToastInput = {
4
4
  variant: ToastVariant;
5
5
  title: string;
6
- lines: string[];
6
+ subtitle?: string;
7
7
  persistent?: boolean;
8
8
  };
9
9
 
@@ -37,7 +37,7 @@ export default function MyPlugin(kit: PluginAPI) {
37
37
  async (ctx) => {
38
38
  ctx.ui.toast({
39
39
  title: "Hello plugin",
40
- lines: ["Loaded from a Kit plugin."],
40
+ subtitle: "Loaded from a Kit plugin.",
41
41
  variant: "info",
42
42
  });
43
43
  },
@@ -45,7 +45,7 @@ export default function MyPlugin(kit: PluginAPI) {
45
45
  }
46
46
  ```
47
47
 
48
- Plugin functions may return a disposer for resources not registered through Kit:
48
+ Plugin functions may return a `Disposer` for resources not registered through Kit:
49
49
 
50
50
  ```ts
51
51
  import type { PluginAPI } from "@akonwi/kit/plugin";
@@ -61,6 +61,64 @@ export default function WatchPlugin(kit: PluginAPI) {
61
61
 
62
62
  Registrations made through `kit` are cleaned up automatically on `/reload`.
63
63
 
64
+ Common exported SDK types include `PluginAPI`, `Plugin`, `Disposer`, `CommandContext`, `CommandOptions`, `RuntimeEvent`, `EventContext`, `ToolDefinition`, `ToolResult`, `ToolCall`, and `ToolCallDecision`.
65
+
66
+ ## UI helpers
67
+
68
+ `kit.ui.toast({ title, subtitle, variant })` remains the lightweight notification API. For small interactive flows, Kit also provides app-owned UI primitives:
69
+
70
+ ```ts
71
+ const picked = await kit.ui.select({
72
+ title: "Choose target",
73
+ options: [
74
+ { label: "Current file", value: "file", description: "Use the active context" },
75
+ { label: "Whole project", value: "project" },
76
+ ],
77
+ filterable: true,
78
+ });
79
+
80
+ const name = await kit.ui.input({
81
+ title: "Name this run",
82
+ placeholder: "experiment name",
83
+ });
84
+
85
+ const ok = await kit.ui.confirm({
86
+ title: "Continue?",
87
+ message: "This will submit a follow-up message.",
88
+ confirmLabel: "Continue",
89
+ });
90
+ ```
91
+
92
+ These helpers use Kit-owned dialogs and return `undefined` when selection/input is cancelled. `confirm` returns `false` for cancel/escape. The public plugin UI API is intentionally limited to `toast`, `select`, `input`, and `confirm` so Kit can keep ownership of rendering, focus, theme, and compatibility.
93
+
94
+ ## Tool approval hooks
95
+
96
+ Plugins can register a callback that runs before a tool executes. Return `{ action: "allow" }` or no value to run the tool; return `{ action: "reject-and-continue", message }` to block it and let the agent continue.
97
+
98
+ If multiple plugins register tool-call handlers, Kit evaluates them in registration order. `allow` does not short-circuit; the first rejection blocks the call.
99
+
100
+ ```ts
101
+ kit.onToolCall(async (toolCall, ctx) => {
102
+ if (toolCall.name !== "bash") return { action: "allow" };
103
+ const command = toolCall.input.command;
104
+ if (typeof command !== "string" || !command.includes("rm")) {
105
+ return { action: "allow" };
106
+ }
107
+
108
+ const approved = await ctx.ui.confirm({
109
+ title: "Approve bash?",
110
+ message: command,
111
+ confirmLabel: "Allow",
112
+ cancelLabel: "Block",
113
+ defaultValue: false,
114
+ });
115
+
116
+ return approved
117
+ ? { action: "allow" }
118
+ : { action: "reject-and-continue", message: "User denied bash." };
119
+ });
120
+ ```
121
+
64
122
  ## Reloading
65
123
 
66
124
  Use `/reload` after editing plugin files. Kit re-discovers plugin files and reloads them with cache busting so changed `.ts` contents are picked up.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akonwi/kit",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "author": "Akonwi Ngoh <akonwi@gmail.com>",
5
5
  "description": "Standalone kit app with Pi-core compatibility and a custom terminal shell.",
6
6
  "license": "MIT",