@openforge-app/plugin-sdk 0.1.0 → 0.2.4
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/LICENSE +21 -0
- package/README.md +26 -0
- package/dist/backend.d.ts +2 -2
- package/dist/browserSurfaces.d.ts +78 -0
- package/dist/browserSurfaces.js +17 -0
- package/dist/browserSurfacesTesting.d.ts +60 -0
- package/dist/browserSurfacesTesting.js +240 -0
- package/dist/domain.d.ts +74 -15
- package/dist/domain.js +5 -1
- package/dist/fileIcons.d.ts +6 -0
- package/dist/fileIcons.js +84 -0
- package/dist/frontend.d.ts +5 -2
- package/dist/frontend.js +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +2 -1
- package/dist/manifest.d.ts +80 -2
- package/dist/manifest.js +55 -1
- package/dist/markdown.d.ts +5 -1
- package/dist/markdown.js +67 -18
- package/dist/openforgePackageMetadataSchema.json +80 -4
- package/dist/pluginIconContract.d.ts +3 -0
- package/dist/pluginIconContract.js +12 -0
- package/dist/pluginIcons.d.ts +7 -0
- package/dist/pluginIcons.js +99 -0
- package/dist/publicUiExports.d.mts +16 -0
- package/dist/publicUiExports.mjs +60 -0
- package/dist/svelteHostRuntimeContract.d.mts +1 -0
- package/dist/svelteHostRuntimeContract.mjs +16 -0
- package/dist/testing/backendServicesFake.d.ts +19 -0
- package/dist/testing/backendServicesFake.js +87 -0
- package/dist/testing/commonApiFake.d.ts +23 -0
- package/dist/testing/commonApiFake.js +306 -0
- package/dist/testing/contracts.d.ts +236 -0
- package/dist/testing/contracts.js +1 -0
- package/dist/testing/frontendContributionFake.d.ts +36 -0
- package/dist/testing/frontendContributionFake.js +204 -0
- package/dist/testing/registryFake.d.ts +40 -0
- package/dist/testing/registryFake.js +124 -0
- package/dist/testing/support.d.ts +40 -0
- package/dist/testing/support.js +247 -0
- package/dist/testing.d.ts +3 -223
- package/dist/testing.js +2 -625
- package/dist/types.d.ts +251 -9
- package/dist/types.js +47 -0
- package/dist/ui/Button.svelte +49 -0
- package/dist/ui/Checkbox.svelte +137 -0
- package/dist/ui/FileTypeIcon.svelte +37 -0
- package/dist/ui/MarkdownContent.svelte +71 -11
- package/dist/ui/Modal.svelte +157 -0
- package/dist/ui/PluginPageHeader.svelte +26 -0
- package/dist/ui/PluginSidebarLink.svelte +54 -0
- package/dist/ui/PluginViewState.svelte +76 -0
- package/dist/ui/ResizablePanel.svelte +10 -0
- package/dist/ui/icons/README.md +9 -0
- package/dist/ui/icons/c.svg +1 -0
- package/dist/ui/icons/console.svg +1 -0
- package/dist/ui/icons/cpp.svg +1 -0
- package/dist/ui/icons/csharp.svg +1 -0
- package/dist/ui/icons/css.svg +3 -0
- package/dist/ui/icons/database.svg +1 -0
- package/dist/ui/icons/docker.svg +1 -0
- package/dist/ui/icons/document.svg +4 -0
- package/dist/ui/icons/file.svg +1 -0
- package/dist/ui/icons/folder-open.svg +1 -0
- package/dist/ui/icons/folder.svg +1 -0
- package/dist/ui/icons/git.svg +3 -0
- package/dist/ui/icons/go.svg +1 -0
- package/dist/ui/icons/graphql.svg +20 -0
- package/dist/ui/icons/h.svg +3 -0
- package/dist/ui/icons/html.svg +3 -0
- package/dist/ui/icons/image.svg +3 -0
- package/dist/ui/icons/java.svg +6 -0
- package/dist/ui/icons/javascript.svg +3 -0
- package/dist/ui/icons/json.svg +3 -0
- package/dist/ui/icons/kotlin.svg +1 -0
- package/dist/ui/icons/lock.svg +3 -0
- package/dist/ui/icons/markdown.svg +6 -0
- package/dist/ui/icons/nodejs.svg +6 -0
- package/dist/ui/icons/npm.svg +3 -0
- package/dist/ui/icons/pdf.svg +1 -0
- package/dist/ui/icons/php.svg +1 -0
- package/dist/ui/icons/python.svg +1 -0
- package/dist/ui/icons/react.svg +8 -0
- package/dist/ui/icons/react_ts.svg +8 -0
- package/dist/ui/icons/readme.svg +4 -0
- package/dist/ui/icons/ruby.svg +1 -0
- package/dist/ui/icons/rust.svg +1 -0
- package/dist/ui/icons/sass.svg +3 -0
- package/dist/ui/icons/settings.svg +4 -0
- package/dist/ui/icons/svelte.svg +1 -0
- package/dist/ui/icons/svg.svg +3 -0
- package/dist/ui/icons/swift.svg +1 -0
- package/dist/ui/icons/tune.svg +11 -0
- package/dist/ui/icons/typescript-def.svg +6 -0
- package/dist/ui/icons/typescript.svg +3 -0
- package/dist/ui/icons/vue.svg +1 -0
- package/dist/ui/icons/xml.svg +1 -0
- package/dist/ui/icons/yaml.svg +3 -0
- package/dist/ui/icons/zip.svg +3 -0
- package/dist/vite.js +5 -2
- package/package.json +35 -7
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Component } from 'svelte';
|
|
2
|
-
import type {
|
|
2
|
+
import type { BrowserSurfacesAPI } from './browserSurfaces';
|
|
3
|
+
import type { AgentSession, CommandInfo, FileContent, FileEntry, Project, ProjectAttention, Task, TaskWorkspaceInfo, WritableBoardStatus } from './domain';
|
|
3
4
|
export type SupportedOpenForgeApiVersion = 1;
|
|
4
5
|
export declare const SUPPORTED_OPENFORGE_API_VERSIONS: readonly [1, ...1[]];
|
|
5
6
|
export declare const OPENFORGE_PLUGIN_API_VERSION: SupportedOpenForgeApiVersion;
|
|
@@ -16,14 +17,18 @@ export interface ValidationError {
|
|
|
16
17
|
path: string;
|
|
17
18
|
message: string;
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
+
declare const OPENFORGE_PLUGIN_CAPABILITY_TYPE_MEMBERS: readonly ["commands", "events", "views", "injectionPoints", "taskPane", "taskStart", "settings", "background", "backend", "storage", "context", "navigation", "tasks", "projects", "fs", "shell", "notifications", "attention", "system.openUrl", "system.writeClipboardText", "config", "projectConfig", "browserSurfaces", "taskLinks", "appEnablement", "customSidebarNavigation"];
|
|
21
|
+
export type OpenForgePluginCapability = (typeof OPENFORGE_PLUGIN_CAPABILITY_TYPE_MEMBERS)[number];
|
|
20
22
|
export interface OpenForgePackageMetadata {
|
|
21
23
|
id: string;
|
|
22
24
|
apiVersion: SupportedOpenForgeApiVersion;
|
|
23
25
|
displayName: string;
|
|
24
26
|
description: string;
|
|
25
|
-
|
|
27
|
+
/** Defaults to project-owned lifecycle when omitted. */
|
|
28
|
+
enablement?: 'app' | 'project';
|
|
29
|
+
icon?: PluginIcon;
|
|
26
30
|
frontend?: string;
|
|
31
|
+
frontendStyles?: string[];
|
|
27
32
|
backend?: string;
|
|
28
33
|
requires?: OpenForgePluginCapability[];
|
|
29
34
|
}
|
|
@@ -61,11 +66,23 @@ export interface OpenForgeNavigationRequest {
|
|
|
61
66
|
viewId?: string;
|
|
62
67
|
projectId?: string | null;
|
|
63
68
|
taskId?: string | null;
|
|
69
|
+
/** Plugin-local Task UI tab id. Requires a non-null taskId. */
|
|
70
|
+
taskViewId?: string;
|
|
64
71
|
}
|
|
65
72
|
export interface NavigationAPI {
|
|
66
73
|
get(): OpenForgeNavigationSnapshot;
|
|
67
74
|
navigate(request: OpenForgeNavigationRequest): Promise<OpenForgeNavigationSnapshot>;
|
|
68
75
|
}
|
|
76
|
+
export interface TaskLinkOpenRequest {
|
|
77
|
+
taskId: string;
|
|
78
|
+
url: string;
|
|
79
|
+
}
|
|
80
|
+
export type TaskLinkHandlerResult = 'handled' | 'declined';
|
|
81
|
+
export type TaskLinkHandler = (request: TaskLinkOpenRequest) => Promise<TaskLinkHandlerResult>;
|
|
82
|
+
export interface TaskLinksAPI {
|
|
83
|
+
open(request: TaskLinkOpenRequest): Promise<void>;
|
|
84
|
+
registerHandler(handler: TaskLinkHandler): Disposable;
|
|
85
|
+
}
|
|
69
86
|
export interface OpenForgePluginContext {
|
|
70
87
|
pluginId: string;
|
|
71
88
|
apiVersion: SupportedOpenForgeApiVersion;
|
|
@@ -89,6 +106,33 @@ export type CommandShortcutMetadata = string | {
|
|
|
89
106
|
scope?: 'global' | 'project' | 'task';
|
|
90
107
|
when?: string;
|
|
91
108
|
};
|
|
109
|
+
export interface AgentCommandMetadata {
|
|
110
|
+
/** Concise guidance explaining when and why an agent should use this command. */
|
|
111
|
+
description: string;
|
|
112
|
+
/** Example plugin-owned JSON inputs. Task and Project context are supplied separately. */
|
|
113
|
+
examples?: JsonValue[];
|
|
114
|
+
/** Whether the command appears in the routine agent catalog. Defaults to true. */
|
|
115
|
+
discoverable?: boolean;
|
|
116
|
+
}
|
|
117
|
+
export type AgentCommandRuntime = 'backend' | 'frontend';
|
|
118
|
+
/** Serializable agent-facing projection of a Plugin Command. Never contains its handler. */
|
|
119
|
+
export interface AgentCommandDescriptor {
|
|
120
|
+
qualifiedId: string;
|
|
121
|
+
pluginId: string;
|
|
122
|
+
runtime: AgentCommandRuntime;
|
|
123
|
+
description: string;
|
|
124
|
+
examples: JsonValue[];
|
|
125
|
+
discoverable: boolean;
|
|
126
|
+
input?: JsonSchema;
|
|
127
|
+
output?: JsonSchema;
|
|
128
|
+
}
|
|
129
|
+
export type PluginCommandInvocationSource = 'agent-cli' | 'plugin';
|
|
130
|
+
/** Host-resolved targeting information supplied separately from plugin-owned command input. */
|
|
131
|
+
export interface PluginCommandInvocationContext {
|
|
132
|
+
taskId: string | null;
|
|
133
|
+
projectId: string | null;
|
|
134
|
+
source: PluginCommandInvocationSource;
|
|
135
|
+
}
|
|
92
136
|
export interface CommandRegistration<TInput = unknown, TOutput = unknown> {
|
|
93
137
|
id: string;
|
|
94
138
|
title: string;
|
|
@@ -96,9 +140,11 @@ export interface CommandRegistration<TInput = unknown, TOutput = unknown> {
|
|
|
96
140
|
shortcut?: CommandShortcutMetadata;
|
|
97
141
|
/** Whether this command should appear in user-facing discovery surfaces such as the Command Palette. Defaults to true. */
|
|
98
142
|
discoverable?: boolean;
|
|
143
|
+
/** Explicitly opts this command into agent access. Omitted commands are unavailable to agents. */
|
|
144
|
+
agent?: AgentCommandMetadata;
|
|
99
145
|
input?: JsonSchema;
|
|
100
146
|
output?: JsonSchema;
|
|
101
|
-
handler(input: TInput): MaybePromise<TOutput>;
|
|
147
|
+
handler(input: TInput, context: PluginCommandInvocationContext): MaybePromise<TOutput>;
|
|
102
148
|
}
|
|
103
149
|
export interface CommandDescriptor {
|
|
104
150
|
id: string;
|
|
@@ -116,7 +162,18 @@ export interface CommandRegistry {
|
|
|
116
162
|
register<TInput = unknown, TOutput = unknown>(registration: CommandRegistration<TInput, TOutput>): Disposable;
|
|
117
163
|
invoke<TOutput = unknown>(id: string, payload?: unknown): Promise<TOutput>;
|
|
118
164
|
invokeGlobal<TOutput = unknown>(qualifiedId: string, payload?: unknown): Promise<TOutput>;
|
|
165
|
+
/** Plugin-registered commands (this and other enabled plugins). */
|
|
119
166
|
list(): Promise<CommandDescriptor[]>;
|
|
167
|
+
/**
|
|
168
|
+
* The host's Claude skills/commands catalog for the given project (skills from
|
|
169
|
+
* `~/.claude`/`.agents`, builtin commands, and plugin-provided commands), as the
|
|
170
|
+
* app's own injectable/autocomplete surfaces see it. Omitting `projectId` (or
|
|
171
|
+
* passing null) yields only project-independent entries. Distinct from `list()`,
|
|
172
|
+
* which returns plugin-registered commands.
|
|
173
|
+
*/
|
|
174
|
+
listCatalog(request?: {
|
|
175
|
+
projectId?: string | null;
|
|
176
|
+
}): Promise<CommandInfo[]>;
|
|
120
177
|
}
|
|
121
178
|
export type EventHandler<TPayload = unknown> = (payload: TPayload) => void;
|
|
122
179
|
export interface EventRegistry {
|
|
@@ -134,20 +191,54 @@ export interface PluginViewProps extends Record<string, unknown> {
|
|
|
134
191
|
api: FrontendOpenForgeAPI;
|
|
135
192
|
context: OpenForgeContextSnapshot;
|
|
136
193
|
}
|
|
194
|
+
export interface PluginSidebarViewIdentity {
|
|
195
|
+
pluginId: string;
|
|
196
|
+
id: string;
|
|
197
|
+
qualifiedId: string;
|
|
198
|
+
title: string;
|
|
199
|
+
icon: PluginIcon;
|
|
200
|
+
}
|
|
201
|
+
export interface PluginSidebarNavigationProps extends Record<string, unknown> {
|
|
202
|
+
api: FrontendOpenForgeAPI;
|
|
203
|
+
context: OpenForgeContextSnapshot;
|
|
204
|
+
active: boolean;
|
|
205
|
+
collapsed: boolean;
|
|
206
|
+
view: PluginSidebarViewIdentity;
|
|
207
|
+
onActivate: () => void;
|
|
208
|
+
}
|
|
137
209
|
export interface PluginTaskPaneProps extends Record<string, unknown> {
|
|
138
210
|
api: FrontendOpenForgeAPI;
|
|
139
211
|
context: OpenForgeContextSnapshot;
|
|
140
212
|
taskId: string;
|
|
141
213
|
projectId: string | null;
|
|
142
214
|
}
|
|
215
|
+
export interface PluginTaskUISectionProps extends PluginTaskPaneProps {
|
|
216
|
+
/** True while the host is running a task-scoped action represented by the section. */
|
|
217
|
+
taskActionPending: boolean;
|
|
218
|
+
}
|
|
143
219
|
export interface PluginSettingsSectionProps extends Record<string, unknown> {
|
|
144
220
|
api: FrontendOpenForgeAPI;
|
|
145
221
|
context: OpenForgeContextSnapshot;
|
|
146
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Inline SVG geometry for a plugin view icon.
|
|
225
|
+
*
|
|
226
|
+
* The host sanitizes the markup, strips root sizing and accessibility metadata,
|
|
227
|
+
* renders it decoratively at the navigation surface's size, and uses the view
|
|
228
|
+
* title as the accessible navigation label. Plugins own the `viewBox`, paths,
|
|
229
|
+
* and paint. Use `currentColor` when the icon should follow host navigation
|
|
230
|
+
* states; literal safe colors remain plugin-owned and do not change with state.
|
|
231
|
+
*/
|
|
232
|
+
export interface PluginSvgIcon {
|
|
233
|
+
type: 'svg';
|
|
234
|
+
svg: string;
|
|
235
|
+
}
|
|
236
|
+
/** A host-registered icon name or inline SVG geometry. */
|
|
237
|
+
export type PluginIcon = string | PluginSvgIcon;
|
|
147
238
|
export interface PluginViewRegistration {
|
|
148
239
|
id: string;
|
|
149
240
|
title: string;
|
|
150
|
-
icon:
|
|
241
|
+
icon: PluginIcon;
|
|
151
242
|
/**
|
|
152
243
|
* Where the host surfaces the view's nav entry. `'rail'` (default) places it on
|
|
153
244
|
* the icon rail; `'sidebar'` places it in the left projects sidebar. Either way
|
|
@@ -157,6 +248,8 @@ export interface PluginViewRegistration {
|
|
|
157
248
|
order?: number;
|
|
158
249
|
shortcut?: string;
|
|
159
250
|
component: PluginComponentLoader<PluginViewProps> | PluginComponent<PluginViewProps>;
|
|
251
|
+
/** Custom content for a sidebar-placed View's host-owned navigation slot. */
|
|
252
|
+
navigationComponent?: PluginComponentLoader<PluginSidebarNavigationProps> | PluginComponent<PluginSidebarNavigationProps>;
|
|
160
253
|
}
|
|
161
254
|
export interface PluginTaskPaneTabRegistration {
|
|
162
255
|
id: string;
|
|
@@ -165,15 +258,72 @@ export interface PluginTaskPaneTabRegistration {
|
|
|
165
258
|
order?: number;
|
|
166
259
|
component: PluginComponentLoader<PluginTaskPaneProps> | PluginComponent<PluginTaskPaneProps>;
|
|
167
260
|
}
|
|
261
|
+
export interface PluginTaskUISectionRegistration {
|
|
262
|
+
id: string;
|
|
263
|
+
order?: number;
|
|
264
|
+
component: PluginComponentLoader<PluginTaskUISectionProps> | PluginComponent<PluginTaskUISectionProps>;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Where a settings section is surfaced. `'project'` (the default) renders it on the
|
|
268
|
+
* per-project settings page, scoped to the active project. `'global'` renders it
|
|
269
|
+
* inside the plugin's own card on the global settings page — for configuration that
|
|
270
|
+
* is one value for the whole app (an API key, a credential) rather than per-project.
|
|
271
|
+
*/
|
|
272
|
+
export type PluginSettingsSectionScope = 'project' | 'global';
|
|
168
273
|
export interface PluginSettingsSectionRegistration {
|
|
169
274
|
id: string;
|
|
170
275
|
title: string;
|
|
171
276
|
order?: number;
|
|
277
|
+
/** Defaults to `'project'` when omitted. */
|
|
278
|
+
scope?: PluginSettingsSectionScope;
|
|
172
279
|
component: PluginComponentLoader<PluginSettingsSectionProps> | PluginComponent<PluginSettingsSectionProps>;
|
|
173
280
|
}
|
|
174
281
|
export interface FrontendViewRegistry {
|
|
175
282
|
register(registration: PluginViewRegistration): Disposable;
|
|
176
283
|
}
|
|
284
|
+
export type InjectionPointLocation = 'createTaskPrompt' | 'agentSession' | 'backlogPrompt';
|
|
285
|
+
export interface PluginInjectionPointProps extends Record<string, unknown> {
|
|
286
|
+
api: FrontendOpenForgeAPI;
|
|
287
|
+
context: OpenForgeContextSnapshot;
|
|
288
|
+
location: InjectionPointLocation;
|
|
289
|
+
projectId: string | null;
|
|
290
|
+
taskId: string | null;
|
|
291
|
+
onInsert: (text: string) => void;
|
|
292
|
+
}
|
|
293
|
+
export interface PluginInjectionPointRegistration {
|
|
294
|
+
id: string;
|
|
295
|
+
location: InjectionPointLocation;
|
|
296
|
+
component: PluginComponentLoader<PluginInjectionPointProps> | PluginComponent<PluginInjectionPointProps>;
|
|
297
|
+
}
|
|
298
|
+
export interface TaskStartPrefixContext {
|
|
299
|
+
/** The task being started, or null when the caller is authoring a new one. */
|
|
300
|
+
taskId: string | null;
|
|
301
|
+
projectId: string | null;
|
|
302
|
+
}
|
|
303
|
+
export interface TaskStartPrefixProviderRegistration {
|
|
304
|
+
id: string;
|
|
305
|
+
/** Menu label shown on the surface offering this provider. */
|
|
306
|
+
title: string;
|
|
307
|
+
/** Lower sorts first. Defaults to 0. */
|
|
308
|
+
order?: number;
|
|
309
|
+
/**
|
|
310
|
+
* Asks the user for a prefix. Returning null means they cancelled, and the
|
|
311
|
+
* task is not started. The host — not the provider — starts the task, so the
|
|
312
|
+
* diverged-branch gate, starting spinner and terminal handling all still run.
|
|
313
|
+
*/
|
|
314
|
+
provide(context: TaskStartPrefixContext): MaybePromise<string | null>;
|
|
315
|
+
}
|
|
316
|
+
export interface FrontendTaskStartRegistry {
|
|
317
|
+
registerPrefixProvider(registration: TaskStartPrefixProviderRegistration): Disposable;
|
|
318
|
+
}
|
|
319
|
+
export interface FrontendInjectionPointRegistry {
|
|
320
|
+
register(registration: PluginInjectionPointRegistration): Disposable;
|
|
321
|
+
}
|
|
322
|
+
export interface FrontendTaskUIRegistry {
|
|
323
|
+
registerTab(registration: PluginTaskPaneTabRegistration): Disposable;
|
|
324
|
+
registerSection(registration: PluginTaskUISectionRegistration): Disposable;
|
|
325
|
+
}
|
|
326
|
+
/** @deprecated Use `FrontendTaskUIRegistry.registerTab` through `openforge.taskUI`. */
|
|
177
327
|
export interface FrontendTaskPaneRegistry {
|
|
178
328
|
registerTab(registration: PluginTaskPaneTabRegistration): Disposable;
|
|
179
329
|
}
|
|
@@ -223,6 +373,37 @@ export interface FileSystemAPI {
|
|
|
223
373
|
limit?: number;
|
|
224
374
|
}): Promise<string[]>;
|
|
225
375
|
}
|
|
376
|
+
export interface UserDataDirectoryRequest {
|
|
377
|
+
path?: string | null;
|
|
378
|
+
}
|
|
379
|
+
export interface UserDataFileRequest {
|
|
380
|
+
path: string;
|
|
381
|
+
}
|
|
382
|
+
export interface UserDataFileWriteRequest extends UserDataFileRequest {
|
|
383
|
+
content: string;
|
|
384
|
+
}
|
|
385
|
+
export interface ExternalReadDirectoryRequest {
|
|
386
|
+
root: string;
|
|
387
|
+
path?: string | null;
|
|
388
|
+
}
|
|
389
|
+
export interface ExternalReadFileRequest {
|
|
390
|
+
root: string;
|
|
391
|
+
path: string;
|
|
392
|
+
}
|
|
393
|
+
export interface UserDataFileSystemAPI {
|
|
394
|
+
readDir(request?: UserDataDirectoryRequest): Promise<FileEntry[]>;
|
|
395
|
+
readTextFile(request: UserDataFileRequest): Promise<string>;
|
|
396
|
+
writeTextFile(request: UserDataFileWriteRequest): Promise<void>;
|
|
397
|
+
}
|
|
398
|
+
export interface ExternalReadFileSystemAPI {
|
|
399
|
+
readDir(request: ExternalReadDirectoryRequest): Promise<FileEntry[]>;
|
|
400
|
+
readTextFile(request: ExternalReadFileRequest): Promise<string>;
|
|
401
|
+
}
|
|
402
|
+
export interface BackendFileSystemAPI extends FileSystemAPI {
|
|
403
|
+
userData: UserDataFileSystemAPI;
|
|
404
|
+
external: ExternalReadFileSystemAPI;
|
|
405
|
+
}
|
|
406
|
+
export type TerminalImageProtocol = 'iterm2';
|
|
226
407
|
export interface ShellSessionRequest {
|
|
227
408
|
taskId: string;
|
|
228
409
|
terminalIndex: number;
|
|
@@ -231,6 +412,7 @@ export interface ShellSpawnRequest extends ShellSessionRequest {
|
|
|
231
412
|
cwd: string;
|
|
232
413
|
cols: number;
|
|
233
414
|
rows: number;
|
|
415
|
+
terminalImageProtocol?: TerminalImageProtocol | null;
|
|
234
416
|
}
|
|
235
417
|
export interface ShellWriteRequest extends ShellSessionRequest {
|
|
236
418
|
data: string;
|
|
@@ -239,12 +421,16 @@ export interface ShellResizeRequest extends ShellSessionRequest {
|
|
|
239
421
|
cols: number;
|
|
240
422
|
rows: number;
|
|
241
423
|
}
|
|
424
|
+
export interface PtyBufferState {
|
|
425
|
+
buffer: string | null;
|
|
426
|
+
isLive: boolean;
|
|
427
|
+
}
|
|
242
428
|
export interface ShellAPI {
|
|
243
429
|
spawn(request: ShellSpawnRequest): Promise<number>;
|
|
244
430
|
write(request: ShellWriteRequest): Promise<void>;
|
|
245
431
|
resize(request: ShellResizeRequest): Promise<void>;
|
|
246
432
|
kill(request: ShellSessionRequest): Promise<void>;
|
|
247
|
-
getBuffer(request: ShellSessionRequest): Promise<
|
|
433
|
+
getBuffer(request: ShellSessionRequest): Promise<PtyBufferState>;
|
|
248
434
|
}
|
|
249
435
|
export interface CreateTaskRequest {
|
|
250
436
|
initialPrompt: string;
|
|
@@ -252,10 +438,27 @@ export interface CreateTaskRequest {
|
|
|
252
438
|
dependsOn?: string[];
|
|
253
439
|
labelNames?: string[];
|
|
254
440
|
}
|
|
441
|
+
export interface ComposeTaskRequest {
|
|
442
|
+
projectId: string;
|
|
443
|
+
/** Seeds the dialog's prompt field; the user edits it before saving. */
|
|
444
|
+
initialPrompt: string;
|
|
445
|
+
sourceTicketUrl?: string | null;
|
|
446
|
+
title?: string | null;
|
|
447
|
+
}
|
|
448
|
+
export interface ComposeTaskResult {
|
|
449
|
+
task: Task;
|
|
450
|
+
/** True when the user chose Start Task rather than plain Create. */
|
|
451
|
+
started: boolean;
|
|
452
|
+
}
|
|
255
453
|
export interface StartPromptContribution {
|
|
454
|
+
/** Host-assigned identity of the plugin that owns this persisted contribution. */
|
|
455
|
+
readonly ownerPluginId?: string;
|
|
256
456
|
id: string;
|
|
257
457
|
enabled: boolean;
|
|
258
|
-
/**
|
|
458
|
+
/**
|
|
459
|
+
* Prompt text injected before OpenForge's task prompt.
|
|
460
|
+
* The host substitutes {{taskId}} and {{task_id}}.
|
|
461
|
+
*/
|
|
259
462
|
content: string;
|
|
260
463
|
/** Lower values are injected first. Defaults to 0. */
|
|
261
464
|
order?: number;
|
|
@@ -271,17 +474,47 @@ export interface ImplementationRun {
|
|
|
271
474
|
sessionId: string;
|
|
272
475
|
workspacePath: string;
|
|
273
476
|
}
|
|
477
|
+
export type TaskFollowUpDisposition = 'delivered' | 'queued';
|
|
478
|
+
export interface SendTaskFollowUpRequest {
|
|
479
|
+
taskId: string;
|
|
480
|
+
message: string;
|
|
481
|
+
}
|
|
482
|
+
export interface TaskFollowUpReceipt {
|
|
483
|
+
taskId: string;
|
|
484
|
+
sessionId: string;
|
|
485
|
+
disposition: TaskFollowUpDisposition;
|
|
486
|
+
}
|
|
487
|
+
export type TaskFollowUpErrorCode = 'NO_SESSION' | 'DELIVERY_FAILED';
|
|
488
|
+
export declare class TaskFollowUpError extends Error {
|
|
489
|
+
readonly code: TaskFollowUpErrorCode;
|
|
490
|
+
constructor(code: TaskFollowUpErrorCode, message: string);
|
|
491
|
+
}
|
|
274
492
|
export interface TasksAPI {
|
|
493
|
+
/**
|
|
494
|
+
* Lists tasks, optionally scoped to a project. By default done tasks are
|
|
495
|
+
* excluded (matching the app board's active-only view); pass
|
|
496
|
+
* `includeDone: true` to include tasks in the terminal `done` state. The
|
|
497
|
+
* unscoped listing (no `projectId`) always returns all states, so
|
|
498
|
+
* `includeDone` only affects the project-scoped path.
|
|
499
|
+
*/
|
|
275
500
|
list(request?: {
|
|
276
501
|
projectId?: string | null;
|
|
502
|
+
includeDone?: boolean;
|
|
277
503
|
}): Promise<Task[]>;
|
|
278
|
-
get(taskId: string): Promise<Task>;
|
|
504
|
+
get(taskId: string): Promise<Task | null>;
|
|
279
505
|
create(request: CreateTaskRequest): Promise<Task>;
|
|
280
|
-
|
|
506
|
+
/**
|
|
507
|
+
* Opens the host's create-task dialog pre-filled, letting the user edit the
|
|
508
|
+
* prompt — including anything contributed at that injection point —
|
|
509
|
+
* before the task exists.
|
|
510
|
+
* Resolves null if they dismiss it.
|
|
511
|
+
*/
|
|
512
|
+
compose(request: ComposeTaskRequest): Promise<ComposeTaskResult | null>;
|
|
281
513
|
updateStatus(taskId: string, status: WritableBoardStatus): Promise<void>;
|
|
282
514
|
listStartPromptContributions(projectId: string): Promise<StartPromptContribution[]>;
|
|
283
515
|
configureStartPromptContribution(request: ConfigureStartPromptContributionRequest): Promise<StartPromptContribution[]>;
|
|
284
516
|
startImplementation(request: StartTaskImplementationRequest): Promise<ImplementationRun>;
|
|
517
|
+
sendFollowUp(request: SendTaskFollowUpRequest): Promise<TaskFollowUpReceipt>;
|
|
285
518
|
getWorkspace(taskId: string): Promise<TaskWorkspaceInfo | null>;
|
|
286
519
|
getLatestSession(taskId: string): Promise<AgentSession | null>;
|
|
287
520
|
}
|
|
@@ -302,6 +535,7 @@ export interface AttentionAPI {
|
|
|
302
535
|
}
|
|
303
536
|
export interface SystemAPI {
|
|
304
537
|
openUrl(url: string): Promise<void>;
|
|
538
|
+
writeClipboardText(text: string): Promise<void>;
|
|
305
539
|
}
|
|
306
540
|
export interface KeyValueConfigAPI {
|
|
307
541
|
get<T extends JsonValue = JsonValue>(key: string, projectId?: string): Promise<T | null>;
|
|
@@ -325,13 +559,20 @@ export interface OpenForgeCommonAPI {
|
|
|
325
559
|
projectConfig: KeyValueConfigAPI;
|
|
326
560
|
}
|
|
327
561
|
export interface FrontendOpenForgeAPI extends OpenForgeCommonAPI {
|
|
562
|
+
browserSurfaces: BrowserSurfacesAPI;
|
|
563
|
+
taskLinks: TaskLinksAPI;
|
|
328
564
|
navigation: NavigationAPI;
|
|
329
565
|
views: FrontendViewRegistry;
|
|
566
|
+
taskUI: FrontendTaskUIRegistry;
|
|
567
|
+
/** @deprecated Use `taskUI.registerTab(...)`. */
|
|
330
568
|
taskPane: FrontendTaskPaneRegistry;
|
|
331
569
|
settings: FrontendSettingsRegistry;
|
|
332
570
|
backend: FrontendBackendBridge;
|
|
571
|
+
injectionPoints: FrontendInjectionPointRegistry;
|
|
572
|
+
taskStart: FrontendTaskStartRegistry;
|
|
333
573
|
}
|
|
334
574
|
export interface BackendOpenForgeAPI extends OpenForgeCommonAPI {
|
|
575
|
+
fs: BackendFileSystemAPI;
|
|
335
576
|
backend: BackendMethodRegistry;
|
|
336
577
|
background: BackgroundServiceRegistry;
|
|
337
578
|
}
|
|
@@ -348,3 +589,4 @@ export declare function parsePluginViewKey(key: PluginViewKey): {
|
|
|
348
589
|
pluginId: string;
|
|
349
590
|
viewId: string;
|
|
350
591
|
};
|
|
592
|
+
export {};
|
package/dist/types.js
CHANGED
|
@@ -10,6 +10,53 @@ export const SUPPORTED_OPENFORGE_API_VERSIONS = Object.freeze(readSupportedOpenF
|
|
|
10
10
|
export const OPENFORGE_PLUGIN_API_VERSION = SUPPORTED_OPENFORGE_API_VERSIONS[0];
|
|
11
11
|
export const MIN_SUPPORTED_API_VERSION = Math.min(...SUPPORTED_OPENFORGE_API_VERSIONS);
|
|
12
12
|
export const MAX_SUPPORTED_API_VERSION = Math.max(...SUPPORTED_OPENFORGE_API_VERSIONS);
|
|
13
|
+
const OPENFORGE_PLUGIN_CAPABILITY_TYPE_MEMBERS = [
|
|
14
|
+
'commands',
|
|
15
|
+
'events',
|
|
16
|
+
'views',
|
|
17
|
+
'injectionPoints',
|
|
18
|
+
'taskPane',
|
|
19
|
+
'taskStart',
|
|
20
|
+
'settings',
|
|
21
|
+
'background',
|
|
22
|
+
'backend',
|
|
23
|
+
'storage',
|
|
24
|
+
'context',
|
|
25
|
+
'navigation',
|
|
26
|
+
'tasks',
|
|
27
|
+
'projects',
|
|
28
|
+
'fs',
|
|
29
|
+
'shell',
|
|
30
|
+
'notifications',
|
|
31
|
+
'attention',
|
|
32
|
+
'system.openUrl',
|
|
33
|
+
'system.writeClipboardText',
|
|
34
|
+
'config',
|
|
35
|
+
'projectConfig',
|
|
36
|
+
'browserSurfaces',
|
|
37
|
+
'taskLinks',
|
|
38
|
+
'appEnablement',
|
|
39
|
+
'customSidebarNavigation',
|
|
40
|
+
];
|
|
41
|
+
function assertOpenForgePluginCapabilitiesMatchSchema() {
|
|
42
|
+
const schemaCapabilities = packageMetadataSchemaData.properties.requires.items.enum;
|
|
43
|
+
if (!Array.isArray(schemaCapabilities) || !schemaCapabilities.every((capability) => typeof capability === 'string')) {
|
|
44
|
+
throw new Error('openforgePackageMetadataSchema.json properties.requires.items.enum must contain only strings');
|
|
45
|
+
}
|
|
46
|
+
if (schemaCapabilities.length !== OPENFORGE_PLUGIN_CAPABILITY_TYPE_MEMBERS.length
|
|
47
|
+
|| schemaCapabilities.some((capability, index) => capability !== OPENFORGE_PLUGIN_CAPABILITY_TYPE_MEMBERS[index])) {
|
|
48
|
+
throw new Error('OpenForgePluginCapability must match openforgePackageMetadataSchema.json properties.requires.items.enum');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
assertOpenForgePluginCapabilitiesMatchSchema();
|
|
52
|
+
export class TaskFollowUpError extends Error {
|
|
53
|
+
code;
|
|
54
|
+
constructor(code, message) {
|
|
55
|
+
super(message);
|
|
56
|
+
this.name = 'TaskFollowUpError';
|
|
57
|
+
this.code = code;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
13
60
|
export function makePluginViewKey(pluginId, viewId) {
|
|
14
61
|
return `plugin:${pluginId}:${viewId}`;
|
|
15
62
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
3
|
+
import type { HTMLButtonAttributes } from 'svelte/elements'
|
|
4
|
+
|
|
5
|
+
type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'error'
|
|
6
|
+
type ButtonSize = 'xs' | 'sm' | 'md' | 'lg'
|
|
7
|
+
|
|
8
|
+
interface Props extends HTMLButtonAttributes {
|
|
9
|
+
children: Snippet
|
|
10
|
+
variant?: ButtonVariant
|
|
11
|
+
size?: ButtonSize
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const variantClasses: Record<ButtonVariant, string> = {
|
|
15
|
+
primary: 'btn-primary',
|
|
16
|
+
secondary: 'btn-secondary',
|
|
17
|
+
outline: 'btn-outline',
|
|
18
|
+
ghost: 'btn-ghost',
|
|
19
|
+
error: 'btn-error',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const sizeClasses: Record<ButtonSize, string> = {
|
|
23
|
+
xs: 'btn-xs',
|
|
24
|
+
sm: 'btn-sm',
|
|
25
|
+
md: 'btn-md',
|
|
26
|
+
lg: 'btn-lg',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let {
|
|
30
|
+
children,
|
|
31
|
+
variant = 'primary',
|
|
32
|
+
size = 'md',
|
|
33
|
+
class: className,
|
|
34
|
+
disabled = false,
|
|
35
|
+
onclick,
|
|
36
|
+
...attributes
|
|
37
|
+
}: Props = $props()
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<button
|
|
41
|
+
{...attributes}
|
|
42
|
+
class={['btn', variantClasses[variant], sizeClasses[size], className]}
|
|
43
|
+
{disabled}
|
|
44
|
+
onclick={(event) => {
|
|
45
|
+
if (!disabled) onclick?.(event)
|
|
46
|
+
}}
|
|
47
|
+
>
|
|
48
|
+
{@render children()}
|
|
49
|
+
</button>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLInputAttributes } from 'svelte/elements'
|
|
3
|
+
|
|
4
|
+
type CheckboxSize = 'xs' | 'sm' | 'md'
|
|
5
|
+
|
|
6
|
+
interface Props extends Omit<HTMLInputAttributes, 'checked' | 'size' | 'type'> {
|
|
7
|
+
checked?: boolean
|
|
8
|
+
indeterminate?: boolean
|
|
9
|
+
size?: CheckboxSize
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
checked = $bindable(false),
|
|
14
|
+
indeterminate = false,
|
|
15
|
+
size = 'sm',
|
|
16
|
+
class: className,
|
|
17
|
+
onchange,
|
|
18
|
+
...attributes
|
|
19
|
+
}: Props = $props()
|
|
20
|
+
|
|
21
|
+
let input = $state<HTMLInputElement | null>(null)
|
|
22
|
+
|
|
23
|
+
$effect(() => {
|
|
24
|
+
if (input) input.indeterminate = indeterminate
|
|
25
|
+
})
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<input
|
|
29
|
+
{...attributes}
|
|
30
|
+
bind:this={input}
|
|
31
|
+
bind:checked
|
|
32
|
+
type="checkbox"
|
|
33
|
+
class={["of-checkbox", className]}
|
|
34
|
+
data-size={size}
|
|
35
|
+
aria-checked={indeterminate ? 'mixed' : undefined}
|
|
36
|
+
{onchange}
|
|
37
|
+
/>
|
|
38
|
+
|
|
39
|
+
<style>
|
|
40
|
+
.of-checkbox {
|
|
41
|
+
width: 1.25rem;
|
|
42
|
+
height: 1.25rem;
|
|
43
|
+
flex: none;
|
|
44
|
+
appearance: none;
|
|
45
|
+
display: inline-grid;
|
|
46
|
+
place-content: center;
|
|
47
|
+
margin: 0;
|
|
48
|
+
border: 2px solid color-mix(in oklab, var(--color-base-content) 55%, transparent);
|
|
49
|
+
border-radius: 0.25rem;
|
|
50
|
+
background: var(--color-base-100);
|
|
51
|
+
color: var(--color-primary-content);
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
transition:
|
|
54
|
+
background-color 150ms ease,
|
|
55
|
+
border-color 150ms ease,
|
|
56
|
+
box-shadow 150ms ease;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.of-checkbox::before {
|
|
60
|
+
width: 0.75rem;
|
|
61
|
+
height: 0.75rem;
|
|
62
|
+
background: currentColor;
|
|
63
|
+
clip-path: polygon(14% 44%, 0 59%, 40% 100%, 100% 19%, 84% 4%, 39% 73%);
|
|
64
|
+
content: '';
|
|
65
|
+
transform: scale(0);
|
|
66
|
+
transform-origin: center;
|
|
67
|
+
transition: transform 120ms ease-out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.of-checkbox:hover:not(:disabled) {
|
|
71
|
+
border-color: var(--color-primary);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.of-checkbox:checked,
|
|
75
|
+
.of-checkbox:indeterminate {
|
|
76
|
+
border-color: var(--color-primary);
|
|
77
|
+
background: var(--color-primary);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.of-checkbox:checked::before {
|
|
81
|
+
transform: scale(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.of-checkbox:indeterminate::before {
|
|
85
|
+
width: 0.625rem;
|
|
86
|
+
height: 0.125rem;
|
|
87
|
+
clip-path: none;
|
|
88
|
+
transform: scale(1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.of-checkbox:focus-visible {
|
|
92
|
+
outline: 2px solid var(--color-primary);
|
|
93
|
+
outline-offset: 2px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.of-checkbox:disabled {
|
|
97
|
+
cursor: not-allowed;
|
|
98
|
+
opacity: 0.45;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.of-checkbox[data-size='xs'] {
|
|
102
|
+
width: 1rem;
|
|
103
|
+
height: 1rem;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.of-checkbox[data-size='xs']::before {
|
|
107
|
+
width: 0.625rem;
|
|
108
|
+
height: 0.625rem;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.of-checkbox[data-size='md'] {
|
|
112
|
+
width: 1.5rem;
|
|
113
|
+
height: 1.5rem;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.of-checkbox[data-size='md']::before {
|
|
117
|
+
width: 0.875rem;
|
|
118
|
+
height: 0.875rem;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@media (prefers-reduced-motion: reduce) {
|
|
122
|
+
.of-checkbox,
|
|
123
|
+
.of-checkbox::before {
|
|
124
|
+
transition: none;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@media (forced-colors: active) {
|
|
129
|
+
.of-checkbox {
|
|
130
|
+
appearance: auto;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.of-checkbox::before {
|
|
134
|
+
display: none;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
</style>
|