@ampcode/plugin 0.0.0-20260225013638-g011c7ef
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 +60 -0
- package/package.json +36 -0
- package/src/ai.ts +25 -0
- package/src/configuration.ts +58 -0
- package/src/events.ts +240 -0
- package/src/index.ts +211 -0
- package/src/system.ts +43 -0
- package/src/thread.ts +97 -0
- package/src/ui.ts +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<!-- This file is rendered directly on https://ampcode.com/manual/plugin-api -->
|
|
2
|
+
|
|
3
|
+
# Amp Plugin API
|
|
4
|
+
|
|
5
|
+
Amp plugins are TypeScript files that extend and customize [Amp](https://ampcode.com). Plugins live in `~/.amp/plugins/` (global) or `.amp/plugins/` (project).
|
|
6
|
+
|
|
7
|
+
> The Amp plugin API is experimental. Expect many breaking changes if you choose to use it right now. Do not use it for anything critical.
|
|
8
|
+
|
|
9
|
+
<br/>
|
|
10
|
+
|
|
11
|
+
The plugin API supports:
|
|
12
|
+
|
|
13
|
+
- **Event handlers** — `amp.on(...)` for hooking into tool calls, messages, and other events
|
|
14
|
+
- **Tools** — `amp.registerTool(...)` for custom tools
|
|
15
|
+
- **Commands** — `amp.registerCommand(...)` to add to Amp's command palette
|
|
16
|
+
- **User input and UI** — `ctx.ui.notify(...)`, `ctx.ui.confirm(...)`
|
|
17
|
+
- **AI & system utilities** — `amp.ai.ask(...)` for yes-no LLM answers
|
|
18
|
+
|
|
19
|
+
<br/>
|
|
20
|
+
|
|
21
|
+
You can use plugins to:
|
|
22
|
+
|
|
23
|
+
- Format files and report file diagnostics after each edit
|
|
24
|
+
- Ensure Amp runs tests before finishing its work
|
|
25
|
+
- Block or require confirmation for commands you deem risky
|
|
26
|
+
- Correct common agent mistakes that AGENTS.md alone can't fix
|
|
27
|
+
|
|
28
|
+
See [Amp Plugin API documentation](https://ampcode.com/manual/plugin-api).
|
|
29
|
+
|
|
30
|
+
## Example
|
|
31
|
+
|
|
32
|
+
`.amp/plugins/permissions.ts`:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// @i-know-the-amp-plugin-api-is-wip-and-very-experimental-right-now
|
|
36
|
+
import type { PluginAPI } from '@ampcode/plugin'
|
|
37
|
+
|
|
38
|
+
export default function (amp: PluginAPI) {
|
|
39
|
+
// Ask the user before executing any tool.
|
|
40
|
+
amp.on('tool.call', async (event, ctx) => {
|
|
41
|
+
const confirmed = await ctx.ui.confirm({
|
|
42
|
+
title: `Allow ${event.tool}?`,
|
|
43
|
+
message: `The agent wants to execute the "${event.tool}" tool.`,
|
|
44
|
+
confirmButtonText: 'Allow',
|
|
45
|
+
})
|
|
46
|
+
if (!confirmed) {
|
|
47
|
+
ctx.logger.log(`User rejected tool execution: ${event.tool}`)
|
|
48
|
+
return {
|
|
49
|
+
action: 'reject-and-continue',
|
|
50
|
+
message: `User rejected execution of tool ${event.tool}.`,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { action: 'allow' }
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Acknowledgment
|
|
59
|
+
|
|
60
|
+
Amp's plugin API is inspired by [pi's extension API](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/extensions.md).
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ampcode/plugin",
|
|
3
|
+
"version": "0.0.0-20260225013638-g011c7ef",
|
|
4
|
+
"description": "Amp Plugin API",
|
|
5
|
+
"homepage": "https://ampcode.com/manual/plugin-api",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "The Amp Team",
|
|
8
|
+
"email": "amp-devs@ampcode.com"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/sourcegraph/amp",
|
|
13
|
+
"directory": "lib/plugin-api"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"types": "src/index.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./src/index.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"src",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"sideEffects": false,
|
|
30
|
+
"scripts": {
|
|
31
|
+
"test": "biome check --config-path=../.. src"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "catalog:"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/ai.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result from an AI ask operation.
|
|
3
|
+
*/
|
|
4
|
+
export interface PluginAIAskResult {
|
|
5
|
+
/** The classification result: 'yes', 'no', or 'uncertain' */
|
|
6
|
+
result: 'yes' | 'no' | 'uncertain'
|
|
7
|
+
|
|
8
|
+
/** Probability (0-1) that the answer is yes */
|
|
9
|
+
probability: number
|
|
10
|
+
|
|
11
|
+
/** Explanation of why the AI gave this answer */
|
|
12
|
+
reason: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* AI capabilities provided to plugins.
|
|
17
|
+
*/
|
|
18
|
+
export interface PluginAI {
|
|
19
|
+
/**
|
|
20
|
+
* Ask an AI model a yes/no question and get a confidence-based response with reasoning.
|
|
21
|
+
* @param question - The yes/no question to ask
|
|
22
|
+
* @returns Object with result, probability, and reason
|
|
23
|
+
*/
|
|
24
|
+
ask(question: string): Promise<PluginAIAskResult>
|
|
25
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observer interface for subscribing to configuration changes.
|
|
3
|
+
*/
|
|
4
|
+
export interface PluginConfigurationObserver<T> {
|
|
5
|
+
next?(value: T): void
|
|
6
|
+
|
|
7
|
+
error?(error: unknown): void
|
|
8
|
+
|
|
9
|
+
complete?(): void
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Subscription that can be unsubscribed to release resources.
|
|
14
|
+
*/
|
|
15
|
+
export interface Subscription {
|
|
16
|
+
unsubscribe(): void
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Target for configuration updates.
|
|
21
|
+
*/
|
|
22
|
+
export type PluginConfigurationTarget = 'workspace' | 'global'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Observable-like interface for Amp configuration.
|
|
26
|
+
* Provides a limited subset of Observable functionality for plugins.
|
|
27
|
+
*/
|
|
28
|
+
export interface PluginConfiguration<T> {
|
|
29
|
+
/**
|
|
30
|
+
* Subscribe to configuration changes.
|
|
31
|
+
*/
|
|
32
|
+
subscribe(observer: PluginConfigurationObserver<T>): Subscription
|
|
33
|
+
subscribe(onNext: (value: T) => void): Subscription
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Pipe operators for transforming the configuration observable.
|
|
37
|
+
*/
|
|
38
|
+
pipe<Out>(op: (input: PluginConfiguration<T>) => Out): Out
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get the current configuration value.
|
|
42
|
+
*/
|
|
43
|
+
get(): Promise<T>
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Update configuration with partial values.
|
|
47
|
+
* @param partial - The partial configuration to merge
|
|
48
|
+
* @param target - Where to store the setting: 'global' (user settings) or 'workspace' (default)
|
|
49
|
+
*/
|
|
50
|
+
update(partial: Partial<T>, target?: PluginConfigurationTarget): Promise<void>
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Delete a configuration key.
|
|
54
|
+
* @param key - The key to delete
|
|
55
|
+
* @param target - Where to delete from: 'global' (user settings) or 'workspace' (default)
|
|
56
|
+
*/
|
|
57
|
+
delete(key: keyof T, target?: PluginConfigurationTarget): Promise<void>
|
|
58
|
+
}
|
package/src/events.ts
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import type { PluginAI } from './ai'
|
|
2
|
+
import type { PluginLogger, PluginSystem, ShellFunction, SpanID } from './system'
|
|
3
|
+
import type { PluginThread, ThreadMessage } from './thread'
|
|
4
|
+
import type { PluginUI } from './ui'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* URI value returned by helper APIs.
|
|
8
|
+
*
|
|
9
|
+
* This stays intentionally minimal so external plugin authors don't need
|
|
10
|
+
* Amp's internal URI package in their dependency graph.
|
|
11
|
+
*/
|
|
12
|
+
export interface URI {
|
|
13
|
+
toString(): string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Event payload for session.start event.
|
|
18
|
+
*/
|
|
19
|
+
export type SessionStartEvent = Record<string, never>
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Event payload for tool.call event.
|
|
23
|
+
* This is a request that expects a response from the handler.
|
|
24
|
+
*/
|
|
25
|
+
export interface ToolCallEvent<T = string, I = Record<string, unknown>> {
|
|
26
|
+
/** Unique identifier for this tool use (e.g., "toolu_xxx") */
|
|
27
|
+
toolUseID: string
|
|
28
|
+
|
|
29
|
+
/** Name of the tool that will be executed */
|
|
30
|
+
tool: T
|
|
31
|
+
|
|
32
|
+
/** Input arguments that will be passed to the tool */
|
|
33
|
+
input: I
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Result returned from a tool.call handler.
|
|
38
|
+
* Determines how the tool execution should proceed.
|
|
39
|
+
*/
|
|
40
|
+
export type ToolCallResult =
|
|
41
|
+
/** Allow the tool to execute with its original input */
|
|
42
|
+
| { action: 'allow' }
|
|
43
|
+
|
|
44
|
+
/** Reject the tool call but allow the agent to continue with other tools */
|
|
45
|
+
| { action: 'reject-and-continue'; message: string }
|
|
46
|
+
|
|
47
|
+
/** Modify the tool's input arguments before execution */
|
|
48
|
+
| { action: 'modify'; input: Record<string, unknown> }
|
|
49
|
+
|
|
50
|
+
/** Provide a synthesized result without actually running the tool */
|
|
51
|
+
| { action: 'synthesize'; result: { output: string; exitCode?: number } }
|
|
52
|
+
|
|
53
|
+
/** Error occurred in the plugin - stops the thread worker and shows an ephemeral error */
|
|
54
|
+
| { action: 'error'; message: string }
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Event payload for tool.result event.
|
|
58
|
+
*/
|
|
59
|
+
export interface ToolResultEvent {
|
|
60
|
+
/** Unique identifier for this tool use (e.g., "toolu_xxx") */
|
|
61
|
+
toolUseID: string
|
|
62
|
+
|
|
63
|
+
/** Name of the tool that was executed */
|
|
64
|
+
tool: string
|
|
65
|
+
|
|
66
|
+
/** Input arguments passed to the tool */
|
|
67
|
+
input: Record<string, unknown>
|
|
68
|
+
|
|
69
|
+
/** Result status of the tool execution */
|
|
70
|
+
status: 'done' | 'error' | 'cancelled'
|
|
71
|
+
|
|
72
|
+
/** Error message if status is 'error' */
|
|
73
|
+
error?: string
|
|
74
|
+
|
|
75
|
+
/** Tool output/result if available */
|
|
76
|
+
output?: unknown
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Result returned from a tool.result handler.
|
|
81
|
+
* Allows modifying the tool result before it is sent back to the model.
|
|
82
|
+
*/
|
|
83
|
+
export type ToolResultResult =
|
|
84
|
+
| {
|
|
85
|
+
status: 'done'
|
|
86
|
+
output?: unknown
|
|
87
|
+
}
|
|
88
|
+
| {
|
|
89
|
+
status: 'error'
|
|
90
|
+
error?: string
|
|
91
|
+
output?: unknown
|
|
92
|
+
}
|
|
93
|
+
| {
|
|
94
|
+
status: 'cancelled'
|
|
95
|
+
error?: string
|
|
96
|
+
output?: unknown
|
|
97
|
+
}
|
|
98
|
+
| undefined
|
|
99
|
+
| void
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Event payload for agent.start event.
|
|
103
|
+
* Fired when a user submits a prompt (initial or reply).
|
|
104
|
+
*/
|
|
105
|
+
export interface AgentStartEvent {
|
|
106
|
+
/** The user's prompt message */
|
|
107
|
+
message: string
|
|
108
|
+
|
|
109
|
+
/** The message ID */
|
|
110
|
+
id: number
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Result returned from an agent.start handler.
|
|
115
|
+
* Allows adding context messages or modifying the system prompt.
|
|
116
|
+
*/
|
|
117
|
+
export interface AgentStartResult {
|
|
118
|
+
/**
|
|
119
|
+
* A message to append after the user's content in the user message.
|
|
120
|
+
* If display is true, the message is shown in the UI.
|
|
121
|
+
*/
|
|
122
|
+
message?: { content: string; display: true }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Event payload for agent.end event.
|
|
127
|
+
* Fired when the agent finishes handling a user prompt.
|
|
128
|
+
*/
|
|
129
|
+
export interface AgentEndEvent {
|
|
130
|
+
/** The user's prompt message that started this turn */
|
|
131
|
+
message: string
|
|
132
|
+
|
|
133
|
+
/** The message ID that started this turn */
|
|
134
|
+
id: number
|
|
135
|
+
|
|
136
|
+
/** The outcome of the agent's turn */
|
|
137
|
+
status: 'done' | 'error' | 'interrupted'
|
|
138
|
+
|
|
139
|
+
/** All messages since the agent.start event (including the user message that started this turn) */
|
|
140
|
+
messages: ThreadMessage[]
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Result returned from an agent.end handler.
|
|
145
|
+
* Allows starting a new agent turn by returning a user message.
|
|
146
|
+
*/
|
|
147
|
+
export type AgentEndResult =
|
|
148
|
+
/** Automatically send a follow-up user message to start a new agent turn */
|
|
149
|
+
{ action: 'continue'; userMessage: string } | void
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Map of event names to their payload types.
|
|
153
|
+
*/
|
|
154
|
+
export interface PluginEventMap {
|
|
155
|
+
'session.start': SessionStartEvent
|
|
156
|
+
'tool.call': ToolCallEvent
|
|
157
|
+
'tool.result': ToolResultEvent
|
|
158
|
+
'agent.start': AgentStartEvent
|
|
159
|
+
'agent.end': AgentEndEvent
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Map of request event names to their result types.
|
|
164
|
+
* These events expect a response from the handler.
|
|
165
|
+
*/
|
|
166
|
+
export interface PluginRequestResultMap {
|
|
167
|
+
'tool.call': ToolCallResult
|
|
168
|
+
'tool.result': ToolResultResult
|
|
169
|
+
'agent.start': AgentStartResult
|
|
170
|
+
'agent.end': AgentEndResult
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Context passed as the second argument to event handlers.
|
|
175
|
+
*/
|
|
176
|
+
export interface PluginEventContext {
|
|
177
|
+
/** Scoped logger for plugin output. Log messages are appended to the handler's trace span events. */
|
|
178
|
+
logger: PluginLogger
|
|
179
|
+
|
|
180
|
+
/** Bun's shell API for executing commands */
|
|
181
|
+
$: ShellFunction
|
|
182
|
+
|
|
183
|
+
/** Platform UI capabilities */
|
|
184
|
+
ui: PluginUI
|
|
185
|
+
|
|
186
|
+
/** AI capabilities */
|
|
187
|
+
ai: PluginAI
|
|
188
|
+
|
|
189
|
+
/** System capabilities */
|
|
190
|
+
system: PluginSystem
|
|
191
|
+
|
|
192
|
+
/** Thread manipulation API */
|
|
193
|
+
thread: PluginThread
|
|
194
|
+
|
|
195
|
+
/** The trace span ID for this handler invocation, if tracing is enabled */
|
|
196
|
+
span?: SpanID
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Handler return type based on whether the event expects a response.
|
|
201
|
+
* Request events (in PluginRequestResultMap) must return a result.
|
|
202
|
+
* Fire-and-forget events return void.
|
|
203
|
+
*/
|
|
204
|
+
export type PluginHandlerResult<E extends keyof PluginEventMap> =
|
|
205
|
+
E extends keyof PluginRequestResultMap
|
|
206
|
+
? PluginRequestResultMap[E] | Promise<PluginRequestResultMap[E]>
|
|
207
|
+
: void | Promise<void>
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Standardized shell command representation.
|
|
211
|
+
*/
|
|
212
|
+
export interface ShellCommand {
|
|
213
|
+
command: string
|
|
214
|
+
dir?: string
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* A tool call and its corresponding terminal tool result extracted from thread messages.
|
|
219
|
+
*/
|
|
220
|
+
export interface ToolCallWithResult {
|
|
221
|
+
call: ToolCallEvent
|
|
222
|
+
result: ToolResultEvent
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Extracts the shell command from a Bash or shell_command tool call.
|
|
227
|
+
* Returns null if the event is not a shell command tool call.
|
|
228
|
+
*/
|
|
229
|
+
export type ShellCommandFromToolCall = (event: ToolCallEvent) => ShellCommand | null
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Extracts paired tool calls and terminal tool results from a list of thread messages.
|
|
233
|
+
*/
|
|
234
|
+
export type ToolCallsInMessages = (messages: ThreadMessage[]) => ToolCallWithResult[]
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Returns an array of file URIs modified by a tool call, or null if the tool doesn't modify files.
|
|
238
|
+
* Supports edit/create/apply_patch tools and sed in-place shell commands.
|
|
239
|
+
*/
|
|
240
|
+
export type FilesModifiedByToolCall = (event: ToolCallEvent | ToolResultEvent) => URI[] | null
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amp Plugin API
|
|
3
|
+
*
|
|
4
|
+
* Plugins are JavaScript/TypeScript programs that extend Amp's functionality.
|
|
5
|
+
* They are long-lived processes that may run for multiple threads.
|
|
6
|
+
*
|
|
7
|
+
* Plugins live in `.amp/plugins/*.{js,ts}` and are executed using Bun.
|
|
8
|
+
*
|
|
9
|
+
* A plugin exports a default function that receives a {@link PluginAPI} instance:
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import type { PluginAPI } from '@ampcode/plugin'
|
|
13
|
+
*
|
|
14
|
+
* export default function (amp: PluginAPI) {
|
|
15
|
+
* amp.on('session.start', (event, ctx) => {
|
|
16
|
+
* ctx.ui.notify('Welcome')
|
|
17
|
+
* })
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export * from './ai'
|
|
23
|
+
export * from './configuration'
|
|
24
|
+
export * from './events'
|
|
25
|
+
export * from './system'
|
|
26
|
+
export * from './thread'
|
|
27
|
+
export * from './ui'
|
|
28
|
+
|
|
29
|
+
import type { PluginAI } from './ai'
|
|
30
|
+
import type { PluginConfiguration, Subscription } from './configuration'
|
|
31
|
+
import type {
|
|
32
|
+
FilesModifiedByToolCall,
|
|
33
|
+
PluginEventContext,
|
|
34
|
+
PluginEventMap,
|
|
35
|
+
PluginHandlerResult,
|
|
36
|
+
ShellCommandFromToolCall,
|
|
37
|
+
ToolCallsInMessages,
|
|
38
|
+
} from './events'
|
|
39
|
+
import type { PluginLogger, PluginSystem, ShellFunction } from './system'
|
|
40
|
+
import type { ThreadID } from './thread'
|
|
41
|
+
import type { PluginUI } from './ui'
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The client environment kind.
|
|
45
|
+
*/
|
|
46
|
+
export type PluginClientKind = 'terminal' | 'editor' | 'web'
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Information about the client running the plugin.
|
|
50
|
+
*/
|
|
51
|
+
export interface PluginClient {
|
|
52
|
+
/** The kind of client environment */
|
|
53
|
+
kind: PluginClientKind
|
|
54
|
+
|
|
55
|
+
/** The version of the client */
|
|
56
|
+
version: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Options for registering a command.
|
|
61
|
+
*/
|
|
62
|
+
export interface PluginCommandOptions {
|
|
63
|
+
/** The title shown after the colon in the command palette (e.g., "Greet" in "Hello: Greet") */
|
|
64
|
+
title: string
|
|
65
|
+
|
|
66
|
+
/** The category shown before the colon (e.g., "Hello" in "Hello: Greet"). Defaults to the plugin name. */
|
|
67
|
+
category?: string
|
|
68
|
+
|
|
69
|
+
/** Human-readable description of what this command does */
|
|
70
|
+
description?: string
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Context passed to command handlers.
|
|
75
|
+
* Provides access to UI capabilities for executing command actions.
|
|
76
|
+
*/
|
|
77
|
+
export interface PluginCommandContext {
|
|
78
|
+
/** Platform UI capabilities */
|
|
79
|
+
ui: PluginUI
|
|
80
|
+
|
|
81
|
+
/** AI capabilities */
|
|
82
|
+
ai: PluginAI
|
|
83
|
+
|
|
84
|
+
/** System capabilities */
|
|
85
|
+
system: PluginSystem
|
|
86
|
+
|
|
87
|
+
/** Bun's shell API for executing commands */
|
|
88
|
+
$: ShellFunction
|
|
89
|
+
|
|
90
|
+
/** Current thread context if a thread is active, undefined otherwise */
|
|
91
|
+
thread?: {
|
|
92
|
+
id: ThreadID
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Context passed to tool execute handlers.
|
|
98
|
+
*/
|
|
99
|
+
export interface PluginToolContext {
|
|
100
|
+
/** Scoped logger for plugin output */
|
|
101
|
+
logger: PluginLogger
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Options for registering a tool that the agent can call.
|
|
106
|
+
*/
|
|
107
|
+
export interface PluginToolDefinition {
|
|
108
|
+
/** Tool name (must match ^[a-zA-Z0-9_-]+$) */
|
|
109
|
+
name: string
|
|
110
|
+
|
|
111
|
+
/** Description shown to the LLM explaining what the tool does */
|
|
112
|
+
description: string
|
|
113
|
+
|
|
114
|
+
/** JSON Schema for the tool's input parameters */
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: 'object'
|
|
117
|
+
properties?: Record<string, object>
|
|
118
|
+
required?: string[]
|
|
119
|
+
[key: string]: unknown
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Execute the tool with the given input and return a result */
|
|
123
|
+
execute: (input: Record<string, unknown>, ctx: PluginToolContext) => Promise<unknown>
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* The plugin API object passed to the plugin's default export function.
|
|
128
|
+
*/
|
|
129
|
+
export interface PluginAPI {
|
|
130
|
+
/** Logger scoped to this plugin */
|
|
131
|
+
logger: PluginLogger
|
|
132
|
+
|
|
133
|
+
/** Information about the client environment */
|
|
134
|
+
client: PluginClient
|
|
135
|
+
|
|
136
|
+
/** Observable configuration that streams changes */
|
|
137
|
+
configuration: PluginConfiguration<Record<string, unknown>>
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Execute shell commands using Bun's shell.
|
|
141
|
+
* Unlike `ctx.$` in event handlers, this is not tied to a specific hook invocation.
|
|
142
|
+
*/
|
|
143
|
+
$: ShellFunction
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Helper utilities for interpreting tool events.
|
|
147
|
+
*/
|
|
148
|
+
helpers: {
|
|
149
|
+
shellCommandFromToolCall: ShellCommandFromToolCall
|
|
150
|
+
toolCallsInMessages: ToolCallsInMessages
|
|
151
|
+
filesModifiedByToolCall: FilesModifiedByToolCall
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Register a handler for plugin events.
|
|
156
|
+
* For request events (e.g., tool.call), the handler must return a result.
|
|
157
|
+
* For fire-and-forget events, the handler returns void.
|
|
158
|
+
*/
|
|
159
|
+
on<E extends keyof PluginEventMap>(
|
|
160
|
+
event: E,
|
|
161
|
+
handler: (event: PluginEventMap[E], ctx: PluginEventContext) => PluginHandlerResult<E>,
|
|
162
|
+
): Subscription
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Register a command that appears in Amp's command palette.
|
|
166
|
+
* When the user invokes the command, the handler is called.
|
|
167
|
+
*
|
|
168
|
+
* @param id - Stable identifier for the command (e.g., "hello-world").
|
|
169
|
+
* @param options - Configuration for the command including title, category, and description.
|
|
170
|
+
* @param handler - The function to execute when the command is invoked.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* amp.registerCommand('hello-world', { title: 'greet', category: 'hello', description: 'Say hello' }, async (ctx) => {
|
|
175
|
+
* await ctx.ui.notify('Hello, world!')
|
|
176
|
+
* })
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
registerCommand(
|
|
180
|
+
id: string,
|
|
181
|
+
options: PluginCommandOptions,
|
|
182
|
+
handler: (ctx: PluginCommandContext) => void | Promise<void>,
|
|
183
|
+
): Subscription
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Register a tool that the agent can call.
|
|
187
|
+
* Plugin tools appear alongside built-in tools and can be invoked by the LLM during conversations.
|
|
188
|
+
*
|
|
189
|
+
* @param definition - The tool definition including name, description, schema, and execute handler.
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```ts
|
|
193
|
+
* amp.registerTool({
|
|
194
|
+
* name: 'hello',
|
|
195
|
+
* description: 'Greet someone by name',
|
|
196
|
+
* inputSchema: {
|
|
197
|
+
* type: 'object',
|
|
198
|
+
* properties: { name: { type: 'string', description: 'Name to greet' } },
|
|
199
|
+
* required: ['name'],
|
|
200
|
+
* },
|
|
201
|
+
* async execute(input) {
|
|
202
|
+
* return `Hello, ${input.name}!`
|
|
203
|
+
* },
|
|
204
|
+
* })
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
registerTool(definition: PluginToolDefinition): Subscription
|
|
208
|
+
|
|
209
|
+
/** AI helpers */
|
|
210
|
+
ai: PluginAI
|
|
211
|
+
}
|
package/src/system.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger provided to plugins for scoped logging.
|
|
3
|
+
*/
|
|
4
|
+
export interface PluginLogger {
|
|
5
|
+
log: (...args: unknown[]) => void
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Bun shell function type (simplified version of Bun.$)
|
|
10
|
+
*/
|
|
11
|
+
export type ShellFunction = (
|
|
12
|
+
strings: TemplateStringsArray,
|
|
13
|
+
...values: unknown[]
|
|
14
|
+
) => Promise<ShellResult>
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Result from a shell command execution.
|
|
18
|
+
*/
|
|
19
|
+
export interface ShellResult {
|
|
20
|
+
exitCode: number
|
|
21
|
+
stdout: string
|
|
22
|
+
stderr: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* System capabilities provided to plugins.
|
|
27
|
+
*/
|
|
28
|
+
export interface PluginSystem {
|
|
29
|
+
/**
|
|
30
|
+
* Open a URL using the system's default protocol handler.
|
|
31
|
+
* On the CLI, it also shows a dialog with the URL text (for SSH users who can't open URLs remotely).
|
|
32
|
+
*/
|
|
33
|
+
open(url: string | URL): Promise<void>
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get the effective Amp base URL currently used by this Amp client.
|
|
37
|
+
* This reflects the active runtime configuration (for example, custom domains via `AMP_URL`).
|
|
38
|
+
*/
|
|
39
|
+
readonly ampURL: URL
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** @internal */
|
|
43
|
+
export type SpanID = string & { readonly __brand: 'SpanID' }
|
package/src/thread.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export type ThreadID = `T-${string}`
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A text content block in a message.
|
|
5
|
+
*/
|
|
6
|
+
export interface ThreadTextBlock {
|
|
7
|
+
type: 'text'
|
|
8
|
+
text: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A thinking content block in a message.
|
|
13
|
+
*/
|
|
14
|
+
export interface ThreadThinkingBlock {
|
|
15
|
+
type: 'thinking'
|
|
16
|
+
thinking: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A tool use content block in a message.
|
|
21
|
+
*/
|
|
22
|
+
export interface ThreadToolUseBlock {
|
|
23
|
+
type: 'tool_use'
|
|
24
|
+
id: string
|
|
25
|
+
name: string
|
|
26
|
+
input: Record<string, unknown>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A tool result content block in a message.
|
|
31
|
+
*/
|
|
32
|
+
export interface ThreadToolResultBlock {
|
|
33
|
+
type: 'tool_result'
|
|
34
|
+
toolUseID: string
|
|
35
|
+
output?: string
|
|
36
|
+
status: 'done' | 'error' | 'cancelled' | 'running' | 'pending'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A user message in the thread.
|
|
41
|
+
*/
|
|
42
|
+
export interface ThreadUserMessage {
|
|
43
|
+
role: 'user'
|
|
44
|
+
|
|
45
|
+
/** The message ID, which is unique in the thread. */
|
|
46
|
+
id: number
|
|
47
|
+
|
|
48
|
+
content: (ThreadTextBlock | ThreadToolResultBlock)[]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* An assistant message in the thread.
|
|
53
|
+
*/
|
|
54
|
+
export interface ThreadAssistantMessage {
|
|
55
|
+
role: 'assistant'
|
|
56
|
+
|
|
57
|
+
/** The message ID, which is unique in the thread. */
|
|
58
|
+
id: number
|
|
59
|
+
|
|
60
|
+
content: (ThreadTextBlock | ThreadThinkingBlock | ThreadToolUseBlock)[]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* An info message in the thread.
|
|
65
|
+
*/
|
|
66
|
+
export interface ThreadInfoMessage {
|
|
67
|
+
role: 'info'
|
|
68
|
+
|
|
69
|
+
/** The message ID, which is unique in the thread. */
|
|
70
|
+
id: number
|
|
71
|
+
|
|
72
|
+
content: ThreadTextBlock[]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* A message in the thread (simplified view for plugins).
|
|
77
|
+
*/
|
|
78
|
+
export type ThreadMessage = ThreadUserMessage | ThreadAssistantMessage | ThreadInfoMessage
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Thread API for manipulating the current thread.
|
|
82
|
+
*/
|
|
83
|
+
export interface PluginThread {
|
|
84
|
+
/**
|
|
85
|
+
* Append a user message to the thread.
|
|
86
|
+
*/
|
|
87
|
+
append(messages: UserMessage[]): Promise<void>
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* A user message that can be appended to the thread.
|
|
92
|
+
*/
|
|
93
|
+
export interface UserMessage {
|
|
94
|
+
type: 'user-message'
|
|
95
|
+
|
|
96
|
+
content: string
|
|
97
|
+
}
|
package/src/ui.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for the input dialog.
|
|
3
|
+
*/
|
|
4
|
+
export interface PluginInputOptions {
|
|
5
|
+
/** Dialog title */
|
|
6
|
+
title?: string
|
|
7
|
+
|
|
8
|
+
/** Help text/description shown below the title */
|
|
9
|
+
helpText?: string
|
|
10
|
+
|
|
11
|
+
/** Initial text value in the input field */
|
|
12
|
+
initialValue?: string
|
|
13
|
+
|
|
14
|
+
/** Text for the submit button (default: "Submit") */
|
|
15
|
+
submitButtonText?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Options for the confirm dialog.
|
|
20
|
+
*/
|
|
21
|
+
export interface PluginConfirmOptions {
|
|
22
|
+
/** Dialog title */
|
|
23
|
+
title: string
|
|
24
|
+
|
|
25
|
+
/** Message body shown below the title */
|
|
26
|
+
message?: string
|
|
27
|
+
|
|
28
|
+
/** Text for the confirm button (default: "Yes") */
|
|
29
|
+
confirmButtonText?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* UI capabilities provided to plugins.
|
|
34
|
+
*/
|
|
35
|
+
export interface PluginUI {
|
|
36
|
+
notify(message: string): Promise<void>
|
|
37
|
+
/**
|
|
38
|
+
* Show an input dialog prompting the user for text input.
|
|
39
|
+
* @returns The entered text, or undefined if the user cancelled.
|
|
40
|
+
*/
|
|
41
|
+
input(options: PluginInputOptions): Promise<string | undefined>
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Show a confirmation dialog with Yes/No options.
|
|
45
|
+
* @returns true if the user confirmed, false if they cancelled.
|
|
46
|
+
*/
|
|
47
|
+
confirm(options: PluginConfirmOptions): Promise<boolean>
|
|
48
|
+
}
|