@ericsanchezok/synergy-plugin 2.1.3 → 2.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/README.md CHANGED
@@ -1,487 +1,169 @@
1
1
  # Synergy Plugin SDK
2
2
 
3
- `@ericsanchezok/synergy-plugin` is the server-side plugin SDK for Synergy.
3
+ `@ericsanchezok/synergy-plugin` is the authoring SDK for Synergy plugins.
4
4
 
5
- A plugin extends the runtime with one or more of these capabilities:
5
+ Plugins extend the Synergy server runtime and can also contribute Web UI surfaces through `plugin.json`. The current API is intentionally strict: a plugin module exports an object descriptor with a canonical `id` and an `init()` method. The descriptor id, `plugin.json.name`, registry id, lockfile key, and approval key must all be the same canonical plugin id.
6
6
 
7
- - custom tools
8
- - lifecycle hooks around sessions, agenda runs, notes, engram search, and tool execution
9
- - provider auth integration
10
- - config and event observers
7
+ Plugin authors should use the installed Synergy CLI and this SDK from a standalone plugin project. Cloning the Synergy source repository is only needed when changing or debugging the plugin platform itself.
11
8
 
12
- This package is for developers who want to add behavior to the Synergy runtime itself. Plugins do **not** run in the Web client. They run on the server/runtime side, inside the active Scope context, and can access:
9
+ ## Recommended Flow
13
10
 
14
- - `ctx.client` — a Synergy SDK client pointed at the current server
15
- - `ctx.scope` the resolved Scope
16
- - `ctx.directory` / `ctx.worktree` — the current workspace paths
17
- - `ctx.serverUrl` — the current server URL
18
- - `ctx.$` Bun shell access for local runtime-side commands
11
+ ```bash
12
+ synergy plugin create my-plugin --template tool-ui
13
+ cd my-plugin
14
+ bun install
15
+ synergy plugin validate --runtime-discovery
16
+ synergy plugin build
17
+ synergy plugin pack
18
+ synergy plugin sign my-plugin-0.1.0.synergy-plugin.tgz
19
+ synergy plugin publish my-plugin-0.1.0.synergy-plugin.tgz
20
+ ```
21
+
22
+ During local development you can also install directly:
19
23
 
20
- If you want the smallest possible example, see [`src/example.ts`](./src/example.ts). It is intentionally minimal. The example in this README shows a more realistic shape.
24
+ ```bash
25
+ synergy plugin add file:///absolute/path/to/my-plugin
26
+ ```
21
27
 
22
- ## What a plugin looks like
28
+ ## Runtime Descriptor
23
29
 
24
- A plugin module exports one or more async functions of type `Plugin`.
25
- Each function is initialized once and returns a set of hooks and capabilities.
26
- In practice, most plugins should export a single default plugin function.
30
+ Every runtime entry exports a `PluginDescriptor` object:
27
31
 
28
32
  ```ts
29
- import type { Plugin } from "@ericsanchezok/synergy-plugin"
33
+ import type { PluginDescriptor } from "@ericsanchezok/synergy-plugin"
34
+ import { tool } from "@ericsanchezok/synergy-plugin/tool"
30
35
 
31
- const MyPlugin: Plugin = async (ctx) => {
32
- return {
33
- // hooks, tools, auth, config observer, event observer
34
- }
36
+ export const plugin: PluginDescriptor = {
37
+ id: "my-plugin",
38
+ name: "My Plugin",
39
+ async init(input) {
40
+ return {
41
+ tool: {
42
+ greet: tool({
43
+ description: "Greet a user by name",
44
+ args: {
45
+ name: tool.schema.string(),
46
+ },
47
+ async execute(args, context) {
48
+ return {
49
+ output: `Hello, ${args.name}. Session: ${context.sessionID}`,
50
+ }
51
+ },
52
+ }),
53
+ },
54
+ async "session.turn.after"(event) {
55
+ console.log("turn completed", event.sessionID)
56
+ },
57
+ }
58
+ },
35
59
  }
36
60
 
37
- export default MyPlugin
61
+ export default plugin
38
62
  ```
39
63
 
40
- A plugin function receives this runtime context:
64
+ There is no compatibility layer for legacy descriptor shapes. `plugin.json.name` must match `plugin.id`; Synergy fails validation or loading if they differ.
65
+
66
+ ## Plugin Input
67
+
68
+ `init(input)` receives runtime services scoped to the active Synergy Scope:
41
69
 
42
70
  ```ts
43
71
  type PluginInput = {
44
72
  client: ReturnType<typeof createSynergyClient>
45
- scope: {
46
- type: "global" | "project"
47
- id: string
48
- directory: string
49
- worktree: string
50
- // ...other scope metadata
51
- }
73
+ scope: unknown
52
74
  directory: string
53
75
  worktree: string
54
76
  serverUrl: URL
55
77
  $: BunShell
78
+ pluginDir: string
79
+ config: { get(): Promise<Record<string, unknown>>; set(values: Record<string, unknown>): Promise<void> }
80
+ auth: { get(key: string): Promise<string | undefined>; set(key: string, value: string): Promise<void> }
81
+ cache: { get<T>(key: string): Promise<T | undefined>; set(key: string, value: unknown, ttl?: number): Promise<void> }
56
82
  }
57
83
  ```
58
84
 
59
- ## Setup
60
-
61
- For an external plugin package:
62
-
63
- ```bash
64
- bun add @ericsanchezok/synergy-plugin zod
65
- ```
66
-
67
- Use ESM and export your plugin from your package entrypoint.
68
- A typical package entry might look like this:
69
-
70
- ```ts
71
- import type { Plugin } from "@ericsanchezok/synergy-plugin"
72
-
73
- const MyPlugin: Plugin = async () => ({})
74
-
75
- export default MyPlugin
76
- ```
77
-
78
- If you are writing a local plugin directly in a Synergy config directory such as `.synergy/plugin/` or `~/.synergy/config/plugin/`, Synergy will install `@ericsanchezok/synergy-plugin` in that config directory automatically. Any additional dependencies declared in that directory's `package.json` are installed there as well.
85
+ For isolated worker/process plugins, these services are proxied through the host bridge and checked against the plugin approval record.
79
86
 
80
- ## How plugins are loaded
87
+ ## Manifest
81
88
 
82
- Synergy loads plugins from two places:
83
-
84
- ### 1. Explicit plugin entries in `synergy.jsonc`
85
-
86
- The config schema includes a top-level `plugin` field:
89
+ Each distributable plugin has a root `plugin.json`:
87
90
 
88
91
  ```jsonc
89
92
  {
90
- "plugin": ["your-plugin-package"],
91
- }
92
- ```
93
-
94
- Those entries are resolved as module specifiers and loaded by the runtime.
95
- Published plugin packages are installed automatically when needed.
96
-
97
- ### 2. Auto-discovered local plugin files
98
-
99
- Synergy also scans these directories for `*.ts` and `*.js` files:
100
-
101
- - project scope: `<project>/.synergy/plugin/` and `<project>/.synergy/plugins/`
102
- - global config: `~/.synergy/config/plugin/` and `~/.synergy/config/plugins/`
103
-
104
- This makes local development straightforward:
105
-
106
- ```text
107
- my-project/
108
- .synergy/
109
- plugin/
110
- my-plugin.ts
111
- ```
112
-
113
- A few practical details:
114
-
115
- - plugins are initialized in the current runtime Scope
116
- - every exported plugin function in a module is loaded once
117
- - `default` export is the safest convention unless you intentionally want multiple plugin instances from one file
118
- - reloading plugin state also reloads plugin-provided tools
119
-
120
- ## Minimal plugin
121
-
122
- This is the smallest useful plugin: one observation hook and no custom tools.
123
-
124
- ```ts
125
- import type { Plugin } from "@ericsanchezok/synergy-plugin"
126
-
127
- const SessionLogger: Plugin = async () => {
128
- return {
129
- async "session.turn.after"(input) {
130
- if (input.error) {
131
- console.error("session turn failed", input.sessionID, input.error)
132
- return
133
- }
134
-
135
- console.log("session turn completed", input.sessionID, input.assistantMessageID)
93
+ "name": "my-plugin",
94
+ "version": "0.1.0",
95
+ "description": "Example Synergy plugin",
96
+ "main": "./src/index.ts",
97
+ "permissions": {
98
+ "tools": {
99
+ "invoke": true,
100
+ "filesystem": "none",
101
+ "network": false,
102
+ "shell": false,
103
+ "mcp": "none",
136
104
  },
137
- }
138
- }
139
-
140
- export default SessionLogger
141
- ```
142
-
143
- ## Custom tools
144
-
145
- Plugins can register tools by returning a `tool` map.
146
- Use the `tool()` helper to define the tool schema and execution function.
147
-
148
- ```ts
149
- import type { Plugin } from "@ericsanchezok/synergy-plugin"
150
- import { tool } from "@ericsanchezok/synergy-plugin/tool"
151
-
152
- const GitPlugin: Plugin = async (ctx) => {
153
- return {
154
- tool: {
155
- current_branch: tool({
156
- description: "Get the current git branch",
157
- args: {},
158
- async execute(_args, toolCtx) {
159
- const out = await ctx.$`git rev-parse --abbrev-ref HEAD`.cwd(ctx.worktree).quiet().text()
160
-
161
- return [`session: ${toolCtx.sessionID}`, `agent: ${toolCtx.agent}`, `branch: ${out.trim()}`].join("\n")
105
+ },
106
+ "contributes": {
107
+ "tools": [
108
+ {
109
+ "name": "greet",
110
+ "title": "Greet",
111
+ "description": "Greet a user by name",
112
+ "capabilities": {
113
+ "filesystem": "none",
114
+ "network": false,
115
+ "shell": false,
162
116
  },
163
- }),
117
+ },
118
+ ],
119
+ "ui": {
120
+ "entry": "./dist/ui/index.js",
121
+ "toolRenderers": [{ "tool": "greet" }],
164
122
  },
165
- }
123
+ },
166
124
  }
167
-
168
- export default GitPlugin
169
- ```
170
-
171
- Tool execution receives a narrower context:
172
-
173
- ```ts
174
- type ToolContext = {
175
- sessionID: string
176
- messageID: string
177
- agent: string
178
- abort: AbortSignal
179
- }
180
- ```
181
-
182
- A few things to know about plugin tools:
183
-
184
- - tool args are defined with Zod shapes
185
- - the helper returns plain text output; Synergy wraps it into the runtime tool result format
186
- - long output may be truncated by the runtime, just like built-in tools
187
- - if you need runtime context like Scope paths or shell access, close over the outer plugin `ctx`
188
-
189
- ## Hooks overview
190
-
191
- If you want a current CLI list instead of reading source, run `synergy plugin hooks` or `synergy plugin hooks --json`.
192
-
193
- Hooks fall into two broad categories.
194
-
195
- ### Observation hooks
196
-
197
- These let you react to runtime events without changing the main result.
198
- They either have no mutable output object, or their output is not used to drive the main flow.
199
-
200
- Common examples:
201
-
202
- - `event`
203
- - `config`
204
- - `session.turn.after`
205
- - `cortex.task.after`
206
- - `agenda.run.after`
207
- - `agenda.run.error`
208
- - `engram.experience.encode.after`
209
-
210
- Use these for logging, metrics, side effects, external notifications, and indexing.
211
-
212
- ### Mutation hooks
213
-
214
- These can shape what Synergy does by mutating the `output` object passed as the second argument.
215
- The runtime passes an object, your hook edits it in place, and the updated object continues through the pipeline.
216
-
217
- Common examples:
218
-
219
- - `chat.message`
220
- - `chat.params`
221
- - `permission.ask`
222
- - `tool.execute.before`
223
- - `tool.execute.after`
224
- - `agenda.run.before`
225
- - `note.create.before`
226
- - `note.update.before`
227
- - `note.search.before`
228
- - `note.search.after`
229
- - `engram.memory.search.before`
230
- - `engram.memory.search.after`
231
- - `experimental.*` transform hooks
232
-
233
- The rule of thumb is simple: treat `input` as context, and treat `output` as the thing you may change.
234
-
235
- ## Hook reference
236
-
237
- ### Core capabilities
238
-
239
- | Hook / field | Purpose | Typical use |
240
- | ------------ | ------------------------------------------ | -------------------------------------------- |
241
- | `tool` | Register custom tools | Runtime-side integrations, project utilities |
242
- | `auth` | Add provider auth methods and auth loaders | Custom providers, OAuth, API key flows |
243
- | `config` | Observe loaded config | Initialize plugin state from current config |
244
- | `event` | Observe 72 runtime bus events (see below) | Logging, metrics, passive integrations |
245
-
246
- #### `event` — observable bus events
247
-
248
- The `event` hook lets you subscribe to any of the following runtime bus events by matching `event.type` in your handler. Run `synergy plugin hooks --json` for the full machine-readable list.
249
-
250
- **Installation & scope**
251
-
252
- ```
253
- installation.updated installation.update_available
254
- scope.updated scope.removed
255
- ```
256
-
257
- **Config & server**
258
-
259
- ```
260
- config.updated config.set_activated
261
- server.connected server.instance.disposed global.disposed
262
- ```
263
-
264
- **File & LSP**
265
-
266
- ```
267
- file.edited file.watcher.updated
268
- lsp.updated lsp.client_diagnostics
269
- ```
270
-
271
- **MCP & command**
272
-
273
- ```
274
- mcp.ready mcp.tools_changed mcp.prompts_changed mcp.resources_changed
275
- command.executed
276
- vcs.branch.updated
277
- ```
278
-
279
- **Permission & note**
280
-
281
- ```
282
- permission.asked permission.replied permission.allow_all_changed
283
- note.created note.updated note.deleted
284
- ```
285
-
286
- **Session & message**
287
-
288
- ```
289
- session.created session.updated session.deleted session.diff
290
- session.error session.status session.idle session.compacted
291
- message.updated message.removed message.part.updated message.part.removed
292
- question.asked question.replied question.rejected
293
- runtime.reloaded
294
- todo.updated dag.updated
295
- ```
296
-
297
- **Cortex & agenda**
298
-
299
- ```
300
- cortex.task.created cortex.task.completed cortex.tasks.updated
301
- agenda.item.created agenda.item.updated agenda.item.deleted
302
125
  ```
303
126
 
304
- **PTY**
127
+ `contributes.ui.entry` is a runtime-loadable JavaScript asset. Source files such as `src/ui.tsx` are only build inputs. `synergy plugin build` uses the conventional UI source path and writes the compiled bundle to the declared entry.
305
128
 
306
- ```
307
- pty.created pty.updated pty.exited pty.deleted
308
- ```
129
+ ## UI Types
309
130
 
310
- **Channel**
131
+ UI contribution types are exported separately:
311
132
 
133
+ ```ts
134
+ import type { PluginToolRendererProps, PluginPanelProps } from "@ericsanchezok/synergy-plugin/ui"
312
135
  ```
313
- channel.command.executed channel.connected channel.disconnected
314
- channel.message.received
315
- ```
316
-
317
- **App & Holos**
318
-
319
- ```
320
- app.push
321
- holos.profile.updated
322
- holos.contact.added holos.contact.removed holos.contact.updated holos.contact.config_updated
323
- holos.friend_request.created holos.friend_request.updated holos.friend_request.removed
324
- holos.queue.enqueued holos.queue.delivered holos.queue.expired
325
- holos.connected holos.connection_status.changed holos.presence
326
- ```
327
-
328
- ### Chat and session hooks
329
-
330
- | Hook | Mutates output? | Notes |
331
- | -------------------------------------- | --------------- | ---------------------------------------------------------------- |
332
- | `chat.message` | Yes | Inspect or rewrite incoming user message parts before processing |
333
- | `chat.params` | Yes | Adjust model parameters and provider options before LLM calls |
334
- | `session.turn.after` | No | Observe the completed turn, including error state if present |
335
- | `experimental.chat.messages.transform` | Yes | Rewrite the message history sent to the model |
336
- | `experimental.chat.system.transform` | Yes | Rewrite the assembled system prompt |
337
- | `experimental.session.compacting` | Yes | Add compaction context or replace the compaction prompt |
338
- | `experimental.text.complete` | Yes | Rewrite a text completion result |
339
-
340
- ### Permission and tool hooks
341
-
342
- | Hook | Mutates output? | Notes |
343
- | --------------------- | --------------- | ------------------------------------------------------- |
344
- | `permission.ask` | Yes | Override `ask` / `deny` / `allow` decisions |
345
- | `tool.execute.before` | Yes | Rewrite tool args before execution |
346
- | `tool.execute.after` | Yes | Rewrite tool output, title, or metadata after execution |
347
136
 
348
- ### Cortex and agenda hooks
137
+ Supported UI surfaces are tool renderers, part renderers, workspace panels, global panels, settings sections, chat components, themes, icons, routes, and commands. The Web client loads aggregated UI metadata with the generated SDK method `plugin.listUiContributions()`, which maps to `/plugin/ui/contributions`; plugin JS and assets are still loaded through browser-native asset URLs.
349
138
 
350
- | Hook | Mutates output? | Notes |
351
- | ------------------- | --------------- | --------------------------------------------------------- |
352
- | `cortex.task.after` | No | Observe completed Cortex task execution |
353
- | `agenda.run.before` | Yes | Skip a run or replace the `AgendaItem` used for execution |
354
- | `agenda.run.after` | No | Observe a successful agenda run |
355
- | `agenda.run.error` | No | Observe a failed agenda run |
139
+ ## Runtime Modes
356
140
 
357
- ### Note hooks
141
+ Synergy resolves each plugin to one runtime mode:
358
142
 
359
- | Hook | Mutates output? | Notes |
360
- | -------------------- | --------------- | ------------------------------------------------------------------- |
361
- | `note.create.before` | Yes | Rewrite note creation input before storage |
362
- | `note.create.after` | Usually no | Observe the created note after persistence |
363
- | `note.update.before` | Yes | Rewrite the patch before update logic runs |
364
- | `note.update.after` | Usually no | Observe the updated note after persistence |
365
- | `note.search.before` | Yes | Rewrite search pattern, scope, date filters, tags, or pinned filter |
366
- | `note.search.after` | Yes | Filter or reorder returned notes |
143
+ - `in-process` for trusted local or built-in plugins.
144
+ - `worker` for isolated plugins that do not need a separate OS process.
145
+ - `process` for third-party, high-risk, or policy-forced isolation.
367
146
 
368
- ### Engram hooks
147
+ Worker and process plugins are started through Synergy's plugin runner. The runner imports the descriptor, calls `init()`, reports tools and hooks to the host, and proxies tool and hook calls over the runtime protocol.
369
148
 
370
- | Hook | Mutates output? | Notes |
371
- | -------------------------------- | --------------- | -------------------------------------------------------------------------- |
372
- | `engram.memory.search.before` | Yes | Rewrite query, vector, top-k, categories, recall modes, or rerank behavior |
373
- | `engram.memory.search.after` | Yes | Filter or rerank returned engram memory results |
374
- | `engram.experience.encode.after` | No | Observe whether an experience was encoded, skipped, or deduplicated |
149
+ ## Packaging
375
150
 
376
- ### Experimental hooks
151
+ `synergy plugin build` writes a distributable `dist/` directory:
377
152
 
378
- Hooks under `experimental.*` are available, but they are less stable than the core hook surface. Use them when you need them, but expect them to evolve more quickly than the main plugin API.
153
+ - `dist/plugin.json`
154
+ - `dist/runtime/index.js`
155
+ - `dist/ui/index.js` when UI entry is declared
156
+ - copied theme/icon/assets files
157
+ - `dist/permissions.summary.json`
158
+ - `dist/integrity.json`
379
159
 
380
- ## Short example
160
+ `synergy plugin pack` archives `dist/` into `<name>-<version>.synergy-plugin.tgz`. `synergy plugin publish` accepts that tarball, stores the real artifact, records its `downloadUrl` and `sha256-...` integrity, and publishes registry metadata.
381
161
 
382
- This example combines a custom tool with two hooks:
383
-
384
- - `session.turn.after` for observation
385
- - `note.search.after` for result shaping
162
+ ## Exports
386
163
 
387
164
  ```ts
388
- import type { Plugin } from "@ericsanchezok/synergy-plugin"
165
+ import type { PluginDescriptor, PluginInput } from "@ericsanchezok/synergy-plugin"
389
166
  import { tool } from "@ericsanchezok/synergy-plugin/tool"
390
-
391
- const ExamplePlugin: Plugin = async (ctx) => {
392
- return {
393
- tool: {
394
- scope_info: tool({
395
- description: "Show the current Scope and worktree",
396
- args: {},
397
- async execute() {
398
- return [
399
- `scope: ${ctx.scope.id}`,
400
- `scopeType: ${ctx.scope.type}`,
401
- `directory: ${ctx.directory}`,
402
- `worktree: ${ctx.worktree}`,
403
- ].join("\n")
404
- },
405
- }),
406
- },
407
-
408
- async "session.turn.after"(input) {
409
- if (!input.error) {
410
- console.log("assistant replied in session", input.sessionID)
411
- }
412
- },
413
-
414
- async "note.search.after"(_input, output) {
415
- output.notes = output.notes.filter((note) => !note.tags.includes("private"))
416
- },
417
- }
418
- }
419
-
420
- export default ExamplePlugin
167
+ import type { BunShell } from "@ericsanchezok/synergy-plugin/shell"
168
+ import type { PluginToolRendererProps } from "@ericsanchezok/synergy-plugin/ui"
421
169
  ```
422
-
423
- ## Best practices
424
-
425
- ### Keep hooks fast
426
-
427
- Most hooks run inline with real user work. If a hook blocks, the user feels it.
428
- Do the minimum in the hook path. Push slow work to another system when possible.
429
-
430
- ### Be conservative with mutation
431
-
432
- If you mutate an output object, make the change narrow and obvious.
433
- A good plugin should be easy to reason about six months later.
434
-
435
- ### Treat Scope as real context
436
-
437
- Plugins run inside a resolved Scope. Prefer `ctx.scope`, `ctx.directory`, and `ctx.worktree` over guessing from process state.
438
-
439
- ### Use `ctx.$` carefully
440
-
441
- `ctx.$` is useful for local runtime-side commands, but it also makes it easy to couple a plugin to one machine or one repository layout. Shell out when that is the simplest correct answer, not by default.
442
-
443
- ### Prefer observation hooks for telemetry and indexing
444
-
445
- If you only need to watch what happened, use an after-hook or `event` hook instead of intercepting earlier stages.
446
-
447
- ### Be explicit about note and engram behavior
448
-
449
- Hooks like `note.search.after` and `engram.memory.search.after` can quietly change what users see. Document those policies in the plugin itself and keep them predictable.
450
-
451
- ### Expect experimental hooks to move
452
-
453
- If your plugin depends on an `experimental.*` hook, isolate that logic so future API changes are easy to update.
454
-
455
- ### Export one plugin by default
456
-
457
- A single default export keeps module behavior obvious. Multiple exported plugin functions are supported, but they are loaded independently.
458
-
459
- ## Auth plugins
460
-
461
- A plugin can also provide `auth` for a custom provider. That is how plugins participate in provider-specific API key or OAuth flows and, when needed, compute provider options through an auth loader.
462
-
463
- If you only need custom tools or hooks, you can ignore `auth` entirely.
464
-
465
- ## When to use a plugin vs other extension points
466
-
467
- Use a plugin when you need runtime behavior:
468
-
469
- - add a tool
470
- - intercept a session, agenda, note, or engram lifecycle step
471
- - integrate provider auth
472
- - react to runtime events
473
-
474
- Use other extension points when the problem is simpler:
475
-
476
- - use `.synergy/command/` for reusable prompt commands
477
- - use `.synergy/skill/` for domain instructions and workflows
478
- - use config in `synergy.jsonc` for static runtime configuration
479
-
480
- ## Development notes
481
-
482
- - local plugin files can live under `.synergy/plugin/` or `.synergy/plugins/`
483
- - global plugin files can live under `~/.synergy/config/plugin/` or `~/.synergy/config/plugins/`
484
- - explicit package plugins can be listed in `synergy.jsonc` under `plugin`
485
- - plugin reload also refreshes plugin-provided tools
486
-
487
- That is the core model: a plugin is an async server-side module that receives runtime context, returns hooks and tools, and participates directly in Scope-aware execution.
package/dist/example.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import type { Plugin } from "./index";
2
- export declare const ExamplePlugin: Plugin;
1
+ import type { PluginDescriptor } from "./index";
2
+ export declare const ExamplePlugin: PluginDescriptor;
3
3
  export default ExamplePlugin;
package/dist/example.js CHANGED
@@ -78,9 +78,6 @@ export const ExamplePlugin = {
78
78
  async "note.create.before"(_, output) {
79
79
  output.note.title = output.note.title.trim() || "Untitled note";
80
80
  output.note.tags = mergeTags(output.note.tags);
81
- if (!output.note.contentText?.trim()) {
82
- output.note.contentText = preview(output.note.title);
83
- }
84
81
  },
85
82
  async "engram.memory.search.after"(input, output) {
86
83
  output.results = output.results
package/dist/hooks.js CHANGED
@@ -177,7 +177,7 @@ export const BUS_EVENT_NAMES = [
177
177
  "config.updated",
178
178
  "config.set_activated",
179
179
  // server
180
- "server.instance.disposed",
180
+ "scope.runtime.disposed",
181
181
  "server.connected",
182
182
  "global.disposed",
183
183
  // file
package/dist/index.d.ts CHANGED
@@ -3,16 +3,25 @@ import type { BunShell } from "./shell";
3
3
  import type { ToolDefinition, ToolResult } from "./tool";
4
4
  export * from "./tool";
5
5
  export type { ToolResult };
6
+ export * from "./manifest";
7
+ export type { BunShell, BunShellOutput, BunShellPromise, ShellExpression, ShellFunction } from "./shell";
6
8
  export interface PluginConfigAccessor {
7
9
  /** Get the plugin's full config object */
8
10
  get(): Promise<Record<string, any>>;
9
11
  /** Set one or more config values (deep-merged into the plugin's namespace) */
10
12
  set(values: Record<string, any>): Promise<void>;
11
13
  }
14
+ /**
15
+ * Plugin credential store (plaintext JSON on disk).
16
+ *
17
+ * WARNING: Credentials are stored as unencrypted JSON in the plugin data directory
18
+ * at ~/.synergy/data/plugin/{id}/auth.json. Protect your filesystem.
19
+ * Future versions will use system keychain encryption.
20
+ */
12
21
  export interface PluginAuthStore {
13
22
  /** Read a credential by key */
14
23
  get(key: string): Promise<string | undefined>;
15
- /** Persist a credential (encrypted at rest) */
24
+ /** Persist a credential. WARNING: stored as plaintext JSON on disk. Protect your filesystem. */
16
25
  set(key: string, value: string): Promise<void>;
17
26
  /** Remove a credential */
18
27
  delete(key: string): Promise<void>;
@@ -178,7 +187,7 @@ export type AuthOuathResult = {
178
187
  export type PluginInput = {
179
188
  client: ReturnType<typeof createSynergyClient>;
180
189
  scope: {
181
- type: "global" | "project";
190
+ type: "home" | "project";
182
191
  id: string;
183
192
  directory: string;
184
193
  worktree: string;
@@ -205,7 +214,7 @@ export type PluginInput = {
205
214
  /** Absolute path to this plugin's package root (where package.json lives) */
206
215
  pluginDir: string;
207
216
  };
208
- export interface Plugin {
217
+ export interface PluginDescriptor {
209
218
  /** Unique identifier for this plugin (used as config/auth/cache namespace) */
210
219
  id: string;
211
220
  /** Human-readable display name */
package/dist/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export * from "./tool";
2
+ export * from "./manifest";