@openacp/cli 2026.331.1 → 2026.331.3

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.
Files changed (182) hide show
  1. package/README.md +2 -1
  2. package/dist/cli.js +24987 -270
  3. package/dist/cli.js.map +1 -1
  4. package/dist/data/registry-snapshot.json +1 -1
  5. package/dist/index.d.ts +10 -0
  6. package/dist/index.js +17669 -406
  7. package/dist/index.js.map +1 -1
  8. package/package.json +2 -2
  9. package/dist/adapter-ELG3VRZ3.js +0 -14
  10. package/dist/adapter-ELG3VRZ3.js.map +0 -1
  11. package/dist/agent-catalog-UYD26QDK.js +0 -10
  12. package/dist/agent-catalog-UYD26QDK.js.map +0 -1
  13. package/dist/agent-dependencies-ED2ZTUHG.js +0 -23
  14. package/dist/agent-dependencies-ED2ZTUHG.js.map +0 -1
  15. package/dist/agent-registry-YOGP656W.js +0 -8
  16. package/dist/agent-registry-YOGP656W.js.map +0 -1
  17. package/dist/agent-store-5UHZH2XI.js +0 -8
  18. package/dist/agent-store-5UHZH2XI.js.map +0 -1
  19. package/dist/api-client-PEMHYL5U.js +0 -13
  20. package/dist/api-client-PEMHYL5U.js.map +0 -1
  21. package/dist/api-server-DATG2KBR.js +0 -10
  22. package/dist/api-server-DATG2KBR.js.map +0 -1
  23. package/dist/api-server-L5Z7XACW.js +0 -7
  24. package/dist/api-server-L5Z7XACW.js.map +0 -1
  25. package/dist/autostart-CUPZMKKC.js +0 -22
  26. package/dist/autostart-CUPZMKKC.js.map +0 -1
  27. package/dist/chunk-23SRIVG4.js +0 -50
  28. package/dist/chunk-23SRIVG4.js.map +0 -1
  29. package/dist/chunk-2KT6TROD.js +0 -129
  30. package/dist/chunk-2KT6TROD.js.map +0 -1
  31. package/dist/chunk-2R5XM3ES.js +0 -154
  32. package/dist/chunk-2R5XM3ES.js.map +0 -1
  33. package/dist/chunk-3EWTPOF7.js +0 -51
  34. package/dist/chunk-3EWTPOF7.js.map +0 -1
  35. package/dist/chunk-566W6INH.js +0 -83
  36. package/dist/chunk-566W6INH.js.map +0 -1
  37. package/dist/chunk-5WGVYX3C.js +0 -55
  38. package/dist/chunk-5WGVYX3C.js.map +0 -1
  39. package/dist/chunk-7GXEMMEV.js +0 -44
  40. package/dist/chunk-7GXEMMEV.js.map +0 -1
  41. package/dist/chunk-7U6IZIJP.js +0 -186
  42. package/dist/chunk-7U6IZIJP.js.map +0 -1
  43. package/dist/chunk-7YIKTRSM.js +0 -105
  44. package/dist/chunk-7YIKTRSM.js.map +0 -1
  45. package/dist/chunk-7ZCQF6QM.js +0 -27
  46. package/dist/chunk-7ZCQF6QM.js.map +0 -1
  47. package/dist/chunk-AFKX424Q.js +0 -92
  48. package/dist/chunk-AFKX424Q.js.map +0 -1
  49. package/dist/chunk-BYCJQPMN.js +0 -543
  50. package/dist/chunk-BYCJQPMN.js.map +0 -1
  51. package/dist/chunk-CDAUYTVP.js +0 -41
  52. package/dist/chunk-CDAUYTVP.js.map +0 -1
  53. package/dist/chunk-EWVXSTQK.js +0 -6544
  54. package/dist/chunk-EWVXSTQK.js.map +0 -1
  55. package/dist/chunk-FNRSWA2K.js +0 -1
  56. package/dist/chunk-FNRSWA2K.js.map +0 -1
  57. package/dist/chunk-FPKQYCQS.js +0 -776
  58. package/dist/chunk-FPKQYCQS.js.map +0 -1
  59. package/dist/chunk-IZ5UEZF7.js +0 -138
  60. package/dist/chunk-IZ5UEZF7.js.map +0 -1
  61. package/dist/chunk-K6UY5M75.js +0 -653
  62. package/dist/chunk-K6UY5M75.js.map +0 -1
  63. package/dist/chunk-KGAQW6F4.js +0 -106
  64. package/dist/chunk-KGAQW6F4.js.map +0 -1
  65. package/dist/chunk-LGFWH3AE.js +0 -26
  66. package/dist/chunk-LGFWH3AE.js.map +0 -1
  67. package/dist/chunk-LRV56K2M.js +0 -4106
  68. package/dist/chunk-LRV56K2M.js.map +0 -1
  69. package/dist/chunk-MDJHCCFS.js +0 -485
  70. package/dist/chunk-MDJHCCFS.js.map +0 -1
  71. package/dist/chunk-MLF4W5R6.js +0 -101
  72. package/dist/chunk-MLF4W5R6.js.map +0 -1
  73. package/dist/chunk-NHD5XDD2.js +0 -686
  74. package/dist/chunk-NHD5XDD2.js.map +0 -1
  75. package/dist/chunk-NJX75BLK.js +0 -259
  76. package/dist/chunk-NJX75BLK.js.map +0 -1
  77. package/dist/chunk-NOEAJNTK.js +0 -156
  78. package/dist/chunk-NOEAJNTK.js.map +0 -1
  79. package/dist/chunk-ON7HB5O7.js +0 -58
  80. package/dist/chunk-ON7HB5O7.js.map +0 -1
  81. package/dist/chunk-OSBZXY2W.js +0 -126
  82. package/dist/chunk-OSBZXY2W.js.map +0 -1
  83. package/dist/chunk-OYSAN7UX.js +0 -15
  84. package/dist/chunk-OYSAN7UX.js.map +0 -1
  85. package/dist/chunk-P3HHJANC.js +0 -209
  86. package/dist/chunk-P3HHJANC.js.map +0 -1
  87. package/dist/chunk-R2YLDQLI.js +0 -1115
  88. package/dist/chunk-R2YLDQLI.js.map +0 -1
  89. package/dist/chunk-R6KZYF7D.js +0 -231
  90. package/dist/chunk-R6KZYF7D.js.map +0 -1
  91. package/dist/chunk-S64CB6J3.js +0 -98
  92. package/dist/chunk-S64CB6J3.js.map +0 -1
  93. package/dist/chunk-SSLVNCEA.js +0 -236
  94. package/dist/chunk-SSLVNCEA.js.map +0 -1
  95. package/dist/chunk-TGP34LQN.js +0 -681
  96. package/dist/chunk-TGP34LQN.js.map +0 -1
  97. package/dist/chunk-VUSCVRJL.js +0 -229
  98. package/dist/chunk-VUSCVRJL.js.map +0 -1
  99. package/dist/chunk-W26AUH5B.js +0 -61
  100. package/dist/chunk-W26AUH5B.js.map +0 -1
  101. package/dist/chunk-WQCJTU2C.js +0 -84
  102. package/dist/chunk-WQCJTU2C.js.map +0 -1
  103. package/dist/chunk-XRJUS6FE.js +0 -53
  104. package/dist/chunk-XRJUS6FE.js.map +0 -1
  105. package/dist/chunk-YZCKSNRN.js +0 -453
  106. package/dist/chunk-YZCKSNRN.js.map +0 -1
  107. package/dist/chunk-ZIRH6QWW.js +0 -69
  108. package/dist/chunk-ZIRH6QWW.js.map +0 -1
  109. package/dist/chunk-ZSLHHQPQ.js +0 -282
  110. package/dist/chunk-ZSLHHQPQ.js.map +0 -1
  111. package/dist/config-X4UP7H6R.js +0 -13
  112. package/dist/config-X4UP7H6R.js.map +0 -1
  113. package/dist/config-editor-7BENRVG5.js +0 -11
  114. package/dist/config-editor-7BENRVG5.js.map +0 -1
  115. package/dist/config-registry-M3FFWEVM.js +0 -18
  116. package/dist/config-registry-M3FFWEVM.js.map +0 -1
  117. package/dist/context-FVGCU5TI.js +0 -9
  118. package/dist/context-FVGCU5TI.js.map +0 -1
  119. package/dist/core-plugins-JSY2I44L.js +0 -25
  120. package/dist/core-plugins-JSY2I44L.js.map +0 -1
  121. package/dist/daemon-UOSRDEXW.js +0 -34
  122. package/dist/daemon-UOSRDEXW.js.map +0 -1
  123. package/dist/dev-loader-7P3HZCIA.js +0 -37
  124. package/dist/dev-loader-7P3HZCIA.js.map +0 -1
  125. package/dist/doctor-6DLACBR4.js +0 -10
  126. package/dist/doctor-6DLACBR4.js.map +0 -1
  127. package/dist/file-service-FQQYME7M.js +0 -8
  128. package/dist/file-service-FQQYME7M.js.map +0 -1
  129. package/dist/install-cloudflared-LNS5L5FR.js +0 -33
  130. package/dist/install-cloudflared-LNS5L5FR.js.map +0 -1
  131. package/dist/install-context-KZO5FR4D.js +0 -78
  132. package/dist/install-context-KZO5FR4D.js.map +0 -1
  133. package/dist/install-jq-SN4IA5K4.js +0 -31
  134. package/dist/install-jq-SN4IA5K4.js.map +0 -1
  135. package/dist/instance-context-FLCE7VZ4.js +0 -13
  136. package/dist/instance-context-FLCE7VZ4.js.map +0 -1
  137. package/dist/instance-registry-SW5FWKHO.js +0 -7
  138. package/dist/instance-registry-SW5FWKHO.js.map +0 -1
  139. package/dist/integrate-JIEZYDOR.js +0 -371
  140. package/dist/integrate-JIEZYDOR.js.map +0 -1
  141. package/dist/log-YZ243M5G.js +0 -25
  142. package/dist/log-YZ243M5G.js.map +0 -1
  143. package/dist/main-D7M2AKRM.js +0 -697
  144. package/dist/main-D7M2AKRM.js.map +0 -1
  145. package/dist/menu-ALFN37IR.js +0 -15
  146. package/dist/menu-ALFN37IR.js.map +0 -1
  147. package/dist/notifications-MO23S7S3.js +0 -8
  148. package/dist/notifications-MO23S7S3.js.map +0 -1
  149. package/dist/plugin-create-HFKS23JY.js +0 -968
  150. package/dist/plugin-create-HFKS23JY.js.map +0 -1
  151. package/dist/plugin-installer-VSTYZSXC.js +0 -9
  152. package/dist/plugin-installer-VSTYZSXC.js.map +0 -1
  153. package/dist/plugin-registry-6J3YSFHF.js +0 -7
  154. package/dist/plugin-registry-6J3YSFHF.js.map +0 -1
  155. package/dist/plugin-search-MGKAL5JM.js +0 -39
  156. package/dist/plugin-search-MGKAL5JM.js.map +0 -1
  157. package/dist/post-upgrade-F4YPMTUT.js +0 -79
  158. package/dist/post-upgrade-F4YPMTUT.js.map +0 -1
  159. package/dist/read-text-file-DJBTITIB.js +0 -7
  160. package/dist/read-text-file-DJBTITIB.js.map +0 -1
  161. package/dist/registry-client-GTBWLXYU.js +0 -7
  162. package/dist/registry-client-GTBWLXYU.js.map +0 -1
  163. package/dist/security-O4XGN2CM.js +0 -8
  164. package/dist/security-O4XGN2CM.js.map +0 -1
  165. package/dist/settings-manager-B4UN2LAC.js +0 -7
  166. package/dist/settings-manager-B4UN2LAC.js.map +0 -1
  167. package/dist/setup-44WLBIOT.js +0 -989
  168. package/dist/setup-44WLBIOT.js.map +0 -1
  169. package/dist/speech-GHTSWDAN.js +0 -9
  170. package/dist/speech-GHTSWDAN.js.map +0 -1
  171. package/dist/suggest-RST5VOHB.js +0 -36
  172. package/dist/suggest-RST5VOHB.js.map +0 -1
  173. package/dist/telegram-D7ASLVEB.js +0 -7
  174. package/dist/telegram-D7ASLVEB.js.map +0 -1
  175. package/dist/tunnel-ALJDPFDQ.js +0 -10
  176. package/dist/tunnel-ALJDPFDQ.js.map +0 -1
  177. package/dist/tunnel-service-TBAHDXMF.js +0 -755
  178. package/dist/tunnel-service-TBAHDXMF.js.map +0 -1
  179. package/dist/validators-GITLOFXC.js +0 -11
  180. package/dist/validators-GITLOFXC.js.map +0 -1
  181. package/dist/version-AXXV6IV2.js +0 -15
  182. package/dist/version-AXXV6IV2.js.map +0 -1
@@ -1,968 +0,0 @@
1
- import {
2
- getCurrentVersion
3
- } from "./chunk-S64CB6J3.js";
4
-
5
- // src/cli/commands/plugin-create.ts
6
- import * as p from "@clack/prompts";
7
- import fs from "fs";
8
- import path from "path";
9
-
10
- // src/cli/plugin-template/package-json.ts
11
- function generatePackageJson(params) {
12
- const packageJson = {
13
- name: params.pluginName,
14
- version: "0.1.0",
15
- description: params.description || "",
16
- type: "module",
17
- main: "dist/index.js",
18
- types: "dist/index.d.ts",
19
- scripts: {
20
- build: "tsc",
21
- dev: "tsc --watch",
22
- test: "vitest",
23
- prepublishOnly: "npm run build"
24
- },
25
- author: params.author || "",
26
- license: params.license,
27
- keywords: ["openacp", "openacp-plugin"],
28
- engines: {
29
- openacp: `>=${params.cliVersion}`
30
- },
31
- peerDependencies: {
32
- "@openacp/cli": `>=${params.cliVersion}`
33
- },
34
- devDependencies: {
35
- "@openacp/plugin-sdk": params.cliVersion,
36
- typescript: "^5.4.0",
37
- vitest: "^3.0.0"
38
- }
39
- };
40
- return JSON.stringify(packageJson, null, 2) + "\n";
41
- }
42
-
43
- // src/cli/plugin-template/tsconfig.ts
44
- function generateTsconfig() {
45
- const tsconfig = {
46
- compilerOptions: {
47
- target: "ES2022",
48
- module: "NodeNext",
49
- moduleResolution: "NodeNext",
50
- declaration: true,
51
- outDir: "dist",
52
- rootDir: "src",
53
- strict: true,
54
- esModuleInterop: true,
55
- skipLibCheck: true,
56
- forceConsistentCasingInFileNames: true
57
- },
58
- include: ["src"],
59
- exclude: ["node_modules", "dist", "src/**/__tests__"]
60
- };
61
- return JSON.stringify(tsconfig, null, 2) + "\n";
62
- }
63
-
64
- // src/cli/plugin-template/dotfiles.ts
65
- function generateGitignore() {
66
- return ["node_modules/", "dist/", "*.tsbuildinfo", ".DS_Store", ""].join("\n");
67
- }
68
- function generateNpmignore() {
69
- return ["src/", "tsconfig.json", ".editorconfig", ".gitignore", "*.test.ts", "__tests__/", ""].join("\n");
70
- }
71
- function generateEditorconfig() {
72
- return [
73
- "root = true",
74
- "",
75
- "[*]",
76
- "indent_style = space",
77
- "indent_size = 2",
78
- "end_of_line = lf",
79
- "charset = utf-8",
80
- "trim_trailing_whitespace = true",
81
- "insert_final_newline = true",
82
- ""
83
- ].join("\n");
84
- }
85
-
86
- // src/cli/plugin-template/readme.ts
87
- function generateReadme(params) {
88
- return [
89
- `# ${params.pluginName}`,
90
- "",
91
- params.description || "An OpenACP plugin.",
92
- "",
93
- "## Installation",
94
- "",
95
- "```bash",
96
- `openacp plugin add ${params.pluginName}`,
97
- "```",
98
- "",
99
- "## Development",
100
- "",
101
- "```bash",
102
- "npm install",
103
- "npm run build",
104
- "npm test",
105
- "",
106
- "# Live development with hot-reload:",
107
- `openacp dev .`,
108
- "```",
109
- "",
110
- "## License",
111
- "",
112
- params.license,
113
- ""
114
- ].join("\n");
115
- }
116
-
117
- // src/cli/plugin-template/plugin-source.ts
118
- function generatePluginSource(params) {
119
- const dirName = params.pluginName.replace(/^@[^/]+\//, "");
120
- const escapedDescription = (params.description || "").replace(/'/g, "\\'");
121
- return `import type { OpenACPPlugin, PluginContext, InstallContext, MigrateContext } from '@openacp/plugin-sdk'
122
-
123
- const plugin: OpenACPPlugin = {
124
- name: '${params.pluginName}',
125
- version: '0.1.0',
126
- description: '${escapedDescription}',
127
-
128
- // Declare which permissions your plugin needs.
129
- // Available: events:read, events:emit, services:register, services:use,
130
- // middleware:register, commands:register, storage:read, storage:write, kernel:access
131
- permissions: ['events:read', 'services:register'],
132
-
133
- // Dependencies on other plugins (loaded before this one).
134
- // pluginDependencies: { '@openacp/security': '>=1.0.0' },
135
-
136
- // Optional dependencies (used if available, gracefully degrade if not).
137
- // optionalPluginDependencies: { '@openacp/usage': '>=1.0.0' },
138
-
139
- /**
140
- * Called during server startup in dependency order.
141
- * Register services, middleware, commands, and event listeners here.
142
- */
143
- async setup(ctx: PluginContext): Promise<void> {
144
- ctx.log.info('Plugin setup started')
145
-
146
- // Example: register a service
147
- // ctx.registerService('my-service', myServiceImpl)
148
-
149
- // Example: listen to events
150
- // ctx.on('session:created', (event) => { ... })
151
-
152
- // Example: register a slash command
153
- // ctx.registerCommand({
154
- // name: 'mycommand',
155
- // description: 'Does something useful',
156
- // category: 'plugin',
157
- // async handler(args) {
158
- // return { type: 'text', text: 'Hello from ${params.pluginName}!' }
159
- // },
160
- // })
161
-
162
- ctx.log.info('Plugin setup complete')
163
- },
164
-
165
- /**
166
- * Called during server shutdown in reverse dependency order.
167
- * Clean up resources, close connections, stop timers here.
168
- * Has a 10-second timeout.
169
- */
170
- async teardown(): Promise<void> {
171
- // Clean up resources here
172
- },
173
-
174
- /**
175
- * Called when user runs \`openacp plugin add ${params.pluginName}\`.
176
- * Use ctx.terminal for interactive prompts to gather configuration.
177
- */
178
- async install(ctx: InstallContext): Promise<void> {
179
- ctx.terminal.log.info('Installing ${params.pluginName}...')
180
-
181
- // Example: prompt for configuration
182
- // const apiKey = await ctx.terminal.text({
183
- // message: 'Enter your API key',
184
- // validate: (v) => v.length === 0 ? 'Required' : undefined,
185
- // })
186
- // await ctx.settings.set('apiKey', apiKey)
187
-
188
- ctx.terminal.log.success('Installation complete!')
189
- },
190
-
191
- /**
192
- * Called when user runs \`openacp plugin configure ${params.pluginName}\`.
193
- * Re-run configuration prompts to update settings.
194
- */
195
- async configure(ctx: InstallContext): Promise<void> {
196
- ctx.terminal.log.info('Configuring ${params.pluginName}...')
197
-
198
- // Re-run configuration prompts, pre-filling with current values
199
- // const current = await ctx.settings.getAll()
200
- // ...
201
-
202
- ctx.terminal.log.success('Configuration updated!')
203
- },
204
-
205
- /**
206
- * Called during boot when the plugin version has changed.
207
- * Migrate settings from the old format to the new format.
208
- */
209
- async migrate(ctx: MigrateContext, oldSettings: unknown, oldVersion: string): Promise<unknown> {
210
- ctx.log.info(\`Migrating from v\${oldVersion}\`)
211
- // Return the migrated settings object
212
- return oldSettings
213
- },
214
-
215
- /**
216
- * Called when user runs \`openacp plugin remove ${params.pluginName}\`.
217
- * Clean up any external resources. If opts.purge is true, delete all data.
218
- */
219
- async uninstall(ctx: InstallContext, opts: { purge: boolean }): Promise<void> {
220
- ctx.terminal.log.info('Uninstalling ${params.pluginName}...')
221
- if (opts.purge) {
222
- await ctx.settings.clear()
223
- }
224
- ctx.terminal.log.success('Uninstalled!')
225
- },
226
- }
227
-
228
- export default plugin
229
- `;
230
- }
231
-
232
- // src/cli/plugin-template/plugin-test.ts
233
- function generatePluginTest(params) {
234
- return `import { describe, it, expect } from 'vitest'
235
- import { createTestContext, createTestInstallContext } from '@openacp/plugin-sdk/testing'
236
- import plugin from '../index.js'
237
-
238
- describe('${params.pluginName}', () => {
239
- it('has correct metadata', () => {
240
- expect(plugin.name).toBe('${params.pluginName}')
241
- expect(plugin.version).toBeDefined()
242
- expect(plugin.setup).toBeInstanceOf(Function)
243
- })
244
-
245
- it('sets up without errors', async () => {
246
- const ctx = createTestContext({
247
- pluginName: '${params.pluginName}',
248
- pluginConfig: { enabled: true },
249
- permissions: plugin.permissions,
250
- })
251
- await expect(plugin.setup(ctx)).resolves.not.toThrow()
252
- })
253
-
254
- it('tears down without errors', async () => {
255
- if (plugin.teardown) {
256
- await expect(plugin.teardown()).resolves.not.toThrow()
257
- }
258
- })
259
-
260
- it('installs without errors', async () => {
261
- if (plugin.install) {
262
- const ctx = createTestInstallContext({
263
- pluginName: '${params.pluginName}',
264
- terminalResponses: { password: [''], confirm: [true], select: ['apiKey'] },
265
- })
266
- await expect(plugin.install(ctx)).resolves.not.toThrow()
267
- }
268
- })
269
- })
270
- `;
271
- }
272
-
273
- // src/cli/plugin-template/claude-md.ts
274
- function generateClaudeMd(params) {
275
- return `# CLAUDE.md
276
-
277
- This file provides context for AI coding agents (Claude, Cursor, etc.) working on this plugin.
278
-
279
- ## What is OpenACP?
280
-
281
- OpenACP is an open-source platform that bridges AI coding agents (Claude Code, Codex, etc.) to messaging platforms (Telegram, Discord, Slack) and custom UIs via the Agent Client Protocol (ACP). It features a microkernel plugin architecture where all features \u2014 adapters, services, commands \u2014 are plugins.
282
-
283
- - **Website & Docs**: https://openacp.gitbook.io/docs
284
- - **GitHub**: https://github.com/Open-ACP/OpenACP
285
- - **Plugin Registry**: https://github.com/Open-ACP/plugin-registry
286
-
287
- Key documentation pages:
288
- - [Getting Started](https://openacp.gitbook.io/docs/getting-started) \u2014 What is OpenACP, quickstart
289
- - [Plugin Development](https://openacp.gitbook.io/docs/extending/building-plugins) \u2014 How to build plugins
290
- - [Architecture](https://openacp.gitbook.io/docs/extending/architecture) \u2014 System design, plugin lifecycle
291
- - [Dev Mode](https://openacp.gitbook.io/docs/extending/dev-mode) \u2014 Hot-reload development workflow
292
- - [CLI Commands](https://openacp.gitbook.io/docs/api-reference/cli-commands) \u2014 Full CLI reference
293
- - [Platform Setup](https://openacp.gitbook.io/docs/platform-setup) \u2014 Telegram, Discord, Slack guides
294
- - [Configuration](https://openacp.gitbook.io/docs/self-hosting/configuration) \u2014 Config and settings reference
295
-
296
- ## Project Overview
297
-
298
- This is an OpenACP plugin. Plugins extend OpenACP with new adapters, services, commands, and middleware.
299
-
300
- - **Package**: ${params.pluginName}
301
- - **SDK**: \`@openacp/plugin-sdk\` (types, base classes, testing utilities)
302
- - **Entry point**: \`src/index.ts\` (default export of \`OpenACPPlugin\` object)
303
-
304
- ## Build & Run
305
-
306
- \`\`\`bash
307
- npm install # Install dependencies
308
- npm run build # Compile TypeScript (tsc)
309
- npm run dev # Watch mode (tsc --watch)
310
- npm test # Run tests (vitest)
311
- \`\`\`
312
-
313
- ### Development with hot-reload
314
-
315
- \`\`\`bash
316
- openacp dev . # Compiles, watches, and reloads plugin on changes
317
- \`\`\`
318
-
319
- ## File Structure
320
-
321
- \`\`\`
322
- src/
323
- index.ts \u2014 Plugin entry point (exports OpenACPPlugin)
324
- __tests__/
325
- index.test.ts \u2014 Tests using @openacp/plugin-sdk/testing
326
- package.json \u2014 engines.openacp declares minimum CLI version
327
- tsconfig.json \u2014 ES2022, NodeNext, strict mode
328
- CLAUDE.md \u2014 This file (AI agent context)
329
- PLUGIN_GUIDE.md \u2014 Human-readable developer guide
330
- \`\`\`
331
-
332
- ## Architecture: How OpenACP Plugins Work
333
-
334
- ### Plugin Lifecycle
335
-
336
- \`\`\`
337
- install \u2500\u2500> [reboot] \u2500\u2500> migrate? \u2500\u2500> setup \u2500\u2500> [running] \u2500\u2500> teardown \u2500\u2500> uninstall
338
- \`\`\`
339
-
340
- | Hook | Trigger | Interactive? | Has Services? |
341
- |------|---------|-------------|---------------|
342
- | \`install(ctx)\` | \`openacp plugin add <name>\` | Yes | No |
343
- | \`migrate(ctx, oldSettings, oldVersion)\` | Boot \u2014 stored version differs from plugin version | No | No |
344
- | \`configure(ctx)\` | \`openacp plugin configure <name>\` | Yes | No |
345
- | \`setup(ctx)\` | Every boot, after migrate | No | Yes |
346
- | \`teardown()\` | Shutdown (10s timeout) | No | Yes |
347
- | \`uninstall(ctx, opts)\` | \`openacp plugin remove <name>\` | Yes | No |
348
-
349
- ### OpenACPPlugin Interface
350
-
351
- \`\`\`typescript
352
- interface OpenACPPlugin {
353
- name: string // unique identifier, e.g. '@myorg/my-plugin'
354
- version: string // semver
355
- description?: string
356
- permissions?: PluginPermission[]
357
- pluginDependencies?: Record<string, string> // name -> semver range
358
- optionalPluginDependencies?: Record<string, string> // used if available
359
- overrides?: string // replace a built-in plugin entirely
360
- settingsSchema?: ZodSchema // Zod validation for settings
361
- essential?: boolean // true = needs setup before system can run
362
-
363
- setup(ctx: PluginContext): Promise<void>
364
- teardown?(): Promise<void>
365
- install?(ctx: InstallContext): Promise<void>
366
- configure?(ctx: InstallContext): Promise<void>
367
- migrate?(ctx: MigrateContext, oldSettings: unknown, oldVersion: string): Promise<unknown>
368
- uninstall?(ctx: InstallContext, opts: { purge: boolean }): Promise<void>
369
- }
370
- \`\`\`
371
-
372
- ### PluginContext API (available in setup)
373
-
374
- \`\`\`typescript
375
- interface PluginContext {
376
- pluginName: string
377
- pluginConfig: Record<string, unknown> // from settings.json
378
-
379
- // Events (requires 'events:read' / 'events:emit')
380
- on(event: string, handler: (...args: unknown[]) => void): void
381
- off(event: string, handler: (...args: unknown[]) => void): void
382
- emit(event: string, payload: unknown): void
383
-
384
- // Services (requires 'services:register' / 'services:use')
385
- registerService<T>(name: string, implementation: T): void
386
- getService<T>(name: string): T | undefined
387
-
388
- // Middleware (requires 'middleware:register')
389
- registerMiddleware<H extends MiddlewareHook>(hook: H, opts: MiddlewareOptions<MiddlewarePayloadMap[H]>): void
390
-
391
- // Commands (requires 'commands:register')
392
- registerCommand(def: CommandDef): void
393
-
394
- // Storage (requires 'storage:read' / 'storage:write')
395
- storage: PluginStorage // get, set, delete, list, getDataDir
396
-
397
- // Messaging (requires 'services:use')
398
- sendMessage(sessionId: string, content: OutgoingMessage): Promise<void>
399
-
400
- // Kernel access (requires 'kernel:access')
401
- sessions: SessionManager
402
- config: ConfigManager
403
- eventBus: EventBus
404
-
405
- // Always available
406
- log: Logger // trace, debug, info, warn, error, fatal, child
407
- }
408
- \`\`\`
409
-
410
- ### CommandDef and CommandResponse
411
-
412
- \`\`\`typescript
413
- interface CommandDef {
414
- name: string // command name without slash
415
- description: string // shown in /help
416
- usage?: string // e.g. '<city>' or 'on|off'
417
- category: 'system' | 'plugin'
418
- handler(args: CommandArgs): Promise<CommandResponse | void>
419
- }
420
-
421
- interface CommandArgs {
422
- raw: string // text after command name
423
- sessionId: string | null
424
- channelId: string // 'telegram', 'discord', 'slack'
425
- userId: string
426
- reply(content: string | CommandResponse): Promise<void>
427
- }
428
-
429
- type CommandResponse =
430
- | { type: 'text'; text: string }
431
- | { type: 'menu'; title: string; options: MenuOption[] }
432
- | { type: 'list'; title: string; items: ListItem[] }
433
- | { type: 'confirm'; question: string; onYes: string; onNo: string }
434
- | { type: 'error'; message: string }
435
- | { type: 'silent' }
436
- \`\`\`
437
-
438
- ### Settings System
439
-
440
- - \`settingsSchema\`: Zod schema for validation
441
- - \`SettingsAPI\` (in InstallContext): get, set, getAll, setAll, delete, clear, has
442
- - Settings stored at \`~/.openacp/plugins/@scope/name/settings.json\`
443
- - \`PluginStorage\` (in PluginContext): key-value store at \`~/.openacp/plugins/data/@scope/name/kv.json\`
444
- - \`storage.getDataDir()\`: returns path for large files, databases, caches
445
-
446
- ### InstallContext (for install/configure/uninstall)
447
-
448
- \`\`\`typescript
449
- interface InstallContext {
450
- pluginName: string
451
- terminal: TerminalIO // text, select, confirm, password, multiselect, log, spinner, note
452
- settings: SettingsAPI
453
- legacyConfig?: Record<string, unknown>
454
- dataDir: string
455
- log: Logger
456
- }
457
- \`\`\`
458
-
459
- ### Service Interfaces (available via ctx.getService)
460
-
461
- | Service name | Interface | Description |
462
- |---|---|---|
463
- | \`security\` | \`SecurityService\` | Access control, session limits, user roles |
464
- | \`file-service\` | \`FileServiceInterface\` | File saving, resolving, format conversion |
465
- | \`notifications\` | \`NotificationService\` | Send notifications to users |
466
- | \`usage\` | \`UsageService\` | Token/cost tracking and budget checking |
467
- | \`speech\` | \`SpeechServiceInterface\` | Text-to-speech and speech-to-text |
468
- | \`tunnel\` | \`TunnelServiceInterface\` | Port tunneling and public URL management |
469
- | \`context\` | \`ContextService\` | Context building for agent sessions |
470
-
471
- ## Plugin Permissions
472
-
473
- Declare in \`permissions\` array. Only request what you need.
474
-
475
- | Permission | Allows |
476
- |---|---|
477
- | \`events:read\` | \`ctx.on()\` \u2014 subscribe to events |
478
- | \`events:emit\` | \`ctx.emit()\` \u2014 emit custom events (must prefix with plugin name) |
479
- | \`services:register\` | \`ctx.registerService()\` \u2014 provide services to other plugins |
480
- | \`services:use\` | \`ctx.getService()\`, \`ctx.sendMessage()\` \u2014 consume services |
481
- | \`middleware:register\` | \`ctx.registerMiddleware()\` \u2014 intercept and modify flows |
482
- | \`commands:register\` | \`ctx.registerCommand()\` \u2014 add chat commands |
483
- | \`storage:read\` | \`ctx.storage.get()\`, \`ctx.storage.list()\` |
484
- | \`storage:write\` | \`ctx.storage.set()\`, \`ctx.storage.delete()\` |
485
- | \`kernel:access\` | \`ctx.sessions\`, \`ctx.config\`, \`ctx.eventBus\`, \`ctx.core\` |
486
-
487
- Calling a method without the required permission throws \`PluginPermissionError\`.
488
-
489
- ## Middleware Hooks (20 total)
490
-
491
- Register with \`ctx.registerMiddleware(hook, { priority?, handler })\`. Return \`null\` to block the flow, call \`next()\` to continue.
492
-
493
- ### Message flow
494
- - \`message:incoming\` \u2014 incoming user message (channelId, threadId, userId, text, attachments)
495
- - \`message:outgoing\` \u2014 outgoing message to user (sessionId, message)
496
-
497
- ### Agent flow
498
- - \`agent:beforePrompt\` \u2014 before prompt is sent to agent (sessionId, text, attachments)
499
- - \`agent:beforeEvent\` \u2014 before agent event is processed (sessionId, event)
500
- - \`agent:afterEvent\` \u2014 after agent event, before delivery (sessionId, event, outgoingMessage)
501
-
502
- ### Turn lifecycle
503
- - \`turn:start\` \u2014 agent turn begins (sessionId, promptText, promptNumber)
504
- - \`turn:end\` \u2014 agent turn ends (sessionId, stopReason, durationMs)
505
-
506
- ### File system
507
- - \`fs:beforeRead\` \u2014 before file read (sessionId, path, line, limit)
508
- - \`fs:beforeWrite\` \u2014 before file write (sessionId, path, content)
509
-
510
- ### Terminal
511
- - \`terminal:beforeCreate\` \u2014 before terminal process spawned (sessionId, command, args, env, cwd)
512
- - \`terminal:afterExit\` \u2014 after terminal process exits (sessionId, terminalId, command, exitCode, durationMs)
513
-
514
- ### Permission
515
- - \`permission:beforeRequest\` \u2014 before permission prompt (sessionId, request, autoResolve)
516
- - \`permission:afterResolve\` \u2014 after permission resolved (sessionId, requestId, decision, userId, durationMs)
517
-
518
- ### Session
519
- - \`session:beforeCreate\` \u2014 before session creation (agentName, workingDir, userId, channelId, threadId)
520
- - \`session:afterDestroy\` \u2014 after session destroyed (sessionId, reason, durationMs, promptCount)
521
-
522
- ### Control
523
- - \`mode:beforeChange\` \u2014 before mode change (sessionId, fromMode, toMode)
524
- - \`config:beforeChange\` \u2014 before config change (sessionId, configId, oldValue, newValue)
525
- - \`model:beforeChange\` \u2014 before model change (sessionId, fromModel, toModel)
526
- - \`agent:beforeCancel\` \u2014 before agent cancellation (sessionId, reason)
527
- - \`agent:beforeSwitch\` \u2014 **blocking** before agent switch (sessionId, fromAgent, toAgent). Return null/false to block.
528
- - \`agent:afterSwitch\` \u2014 **fire-and-forget** after agent switch (sessionId, fromAgent, toAgent, resumed). Observational only.
529
-
530
- ## Plugin Events (subscribe with ctx.on)
531
-
532
- ### System
533
- - \`kernel:booted\`, \`system:ready\`, \`system:shutdown\`, \`system:commands-ready\`
534
-
535
- ### Plugin lifecycle
536
- - \`plugin:loaded\`, \`plugin:failed\`, \`plugin:disabled\`, \`plugin:unloaded\`
537
-
538
- ### Session
539
- - \`session:created\`, \`session:ended\`, \`session:named\`, \`session:updated\`
540
-
541
- ### Agent
542
- - \`agent:event\`, \`agent:prompt\`
543
-
544
- ### Permission
545
- - \`permission:request\`, \`permission:resolved\`
546
-
547
- ## Testing
548
-
549
- Use \`@openacp/plugin-sdk/testing\`:
550
-
551
- \`\`\`typescript
552
- import { createTestContext, createTestInstallContext, mockServices } from '@openacp/plugin-sdk/testing'
553
- \`\`\`
554
-
555
- ### createTestContext(opts)
556
-
557
- Creates a test \`PluginContext\`. All state is in-memory.
558
-
559
- \`\`\`typescript
560
- const ctx = createTestContext({
561
- pluginName: '${params.pluginName}',
562
- pluginConfig: { enabled: true },
563
- permissions: plugin.permissions,
564
- services: { security: mockServices.security() },
565
- })
566
- await plugin.setup(ctx)
567
- expect(ctx.registeredCommands.has('mycommand')).toBe(true)
568
- const response = await ctx.executeCommand('mycommand', { raw: 'test' })
569
- \`\`\`
570
-
571
- Inspection properties: \`registeredServices\`, \`registeredCommands\`, \`registeredMiddleware\`, \`emittedEvents\`, \`sentMessages\`, \`executeCommand()\`.
572
-
573
- ### createTestInstallContext(opts)
574
-
575
- Creates a test \`InstallContext\`. Terminal prompts auto-answered from \`terminalResponses\`.
576
-
577
- \`\`\`typescript
578
- const ctx = createTestInstallContext({
579
- pluginName: '${params.pluginName}',
580
- terminalResponses: { password: ['sk-test-key'], select: ['en'] },
581
- })
582
- await plugin.install!(ctx)
583
- expect(ctx.settingsData.get('apiKey')).toBe('sk-test-key')
584
- \`\`\`
585
-
586
- ### mockServices
587
-
588
- Factory functions for mock service implementations:
589
-
590
- \`\`\`typescript
591
- mockServices.security(overrides?) // checkAccess, checkSessionLimit, getUserRole
592
- mockServices.fileService(overrides?) // saveFile, resolveFile, readTextFileWithRange
593
- mockServices.notifications(overrides?) // notify, notifyAll
594
- mockServices.usage(overrides?) // trackUsage, checkBudget, getSummary
595
- mockServices.speech(overrides?) // textToSpeech, speechToText, register*
596
- mockServices.tunnel(overrides?) // getPublicUrl, start, stop, getStore, fileUrl, diffUrl
597
- mockServices.context(overrides?) // buildContext, registerProvider
598
- \`\`\`
599
-
600
- ## Conventions
601
-
602
- - **ESM-only**: \`"type": "module"\` in package.json
603
- - **Import extensions**: All imports must use \`.js\` extension (e.g., \`import x from './util.js'\`)
604
- - **TypeScript strict mode**: \`strict: true\` in tsconfig.json
605
- - **Target**: ES2022, module NodeNext
606
- - **Test framework**: Vitest
607
- - **Test files**: \`src/**/__tests__/*.test.ts\`
608
-
609
- ## How to Add a Command
610
-
611
- \`\`\`typescript
612
- // In setup():
613
- ctx.registerCommand({
614
- name: 'mycommand',
615
- description: 'Does something useful',
616
- usage: '<arg>',
617
- category: 'plugin',
618
- async handler(args) {
619
- const input = args.raw.trim()
620
- if (!input) return { type: 'error', message: 'Usage: /mycommand <arg>' }
621
- return { type: 'text', text: \\\`Result: \\\${input}\\\` }
622
- },
623
- })
624
- \`\`\`
625
-
626
- Requires \`commands:register\` permission. Available as \`/mycommand\` (if no conflict) and \`/pluginscope:mycommand\` (always).
627
-
628
- ## How to Add a Service
629
-
630
- \`\`\`typescript
631
- // In setup():
632
- const myService = new MyService()
633
- ctx.registerService('my-service', myService)
634
- \`\`\`
635
-
636
- Requires \`services:register\` permission. Other plugins consume with \`ctx.getService<MyService>('my-service')\`.
637
-
638
- ## How to Add Middleware
639
-
640
- \`\`\`typescript
641
- // In setup():
642
- ctx.registerMiddleware('message:outgoing', {
643
- priority: 50, // lower = earlier execution
644
- handler: async (payload, next) => {
645
- payload.message.text = modifyText(payload.message.text)
646
- return next() // continue chain; return null to block
647
- },
648
- })
649
- \`\`\`
650
-
651
- Requires \`middleware:register\` permission.
652
-
653
- ## How Settings Work
654
-
655
- 1. Define \`settingsSchema\` (Zod) on the plugin object
656
- 2. In \`install()\`: use \`ctx.terminal\` for interactive prompts, save with \`ctx.settings.set()\`
657
- 3. In \`configure()\`: re-run prompts with current values pre-filled
658
- 4. In \`setup()\`: read settings from \`ctx.pluginConfig\`
659
- 5. In \`migrate()\`: transform old settings to new format on version change
660
-
661
- ## Version Compatibility
662
-
663
- The \`engines.openacp\` field in package.json declares the minimum CLI version. OpenACP checks this on install and warns if incompatible.
664
- `;
665
- }
666
-
667
- // src/cli/plugin-template/plugin-guide.ts
668
- function generatePluginGuide(params) {
669
- return `# Plugin Developer Guide
670
-
671
- ## Overview
672
-
673
- **${params.pluginName}** is an OpenACP plugin.
674
-
675
- > TODO: Describe what this plugin does.
676
-
677
- ## Project Structure
678
-
679
- \`\`\`
680
- src/
681
- index.ts \u2014 Plugin entry point (exports OpenACPPlugin object)
682
- __tests__/
683
- index.test.ts \u2014 Tests using Vitest + @openacp/plugin-sdk/testing
684
- package.json \u2014 npm package config with engines.openacp constraint
685
- tsconfig.json \u2014 TypeScript strict mode, ES2022, NodeNext
686
- CLAUDE.md \u2014 Full technical reference for AI coding agents
687
- PLUGIN_GUIDE.md \u2014 This file
688
- \`\`\`
689
-
690
- ## Development Workflow
691
-
692
- 1. **Edit** \`src/index.ts\` \u2014 implement your plugin logic
693
- 2. **Dev mode**: \`openacp dev .\` \u2014 compiles, watches, and hot-reloads your plugin
694
- 3. **Test**: \`npm test\` \u2014 runs Vitest with SDK testing utilities
695
- 4. **Build**: \`npm run build\` \u2014 compiles TypeScript to \`dist/\`
696
-
697
- \`\`\`bash
698
- npm install
699
- openacp dev . # start developing with hot-reload
700
- npm test # run tests
701
- npm run build # compile for publishing
702
- \`\`\`
703
-
704
- ## Adding a Command
705
-
706
- Register commands in your \`setup()\` function. Requires \`commands:register\` permission.
707
-
708
- \`\`\`typescript
709
- async setup(ctx: PluginContext) {
710
- ctx.registerCommand({
711
- name: 'greet',
712
- description: 'Send a greeting',
713
- usage: '[name]',
714
- category: 'plugin',
715
- async handler(args) {
716
- const name = args.raw.trim() || 'World'
717
- return { type: 'text', text: \\\`Hello, \\\${name}!\\\` }
718
- },
719
- })
720
- }
721
- \`\`\`
722
-
723
- The command will be available as \`/greet\` in all messaging platforms.
724
-
725
- ## Adding a Service
726
-
727
- Provide a service that other plugins can consume. Requires \`services:register\` permission.
728
-
729
- \`\`\`typescript
730
- async setup(ctx: PluginContext) {
731
- const myService = {
732
- doSomething(input: string): string {
733
- return input.toUpperCase()
734
- },
735
- }
736
- ctx.registerService('my-service', myService)
737
- }
738
- \`\`\`
739
-
740
- Other plugins access it with \`ctx.getService<MyServiceType>('my-service')\`.
741
-
742
- ## Adding Middleware
743
-
744
- Intercept and modify message flows. Requires \`middleware:register\` permission.
745
-
746
- \`\`\`typescript
747
- async setup(ctx: PluginContext) {
748
- ctx.registerMiddleware('message:outgoing', {
749
- priority: 50,
750
- handler: async (payload, next) => {
751
- // Modify the message before delivery
752
- payload.message.text += '\\n-- sent via ${params.pluginName}'
753
- return next() // continue the chain
754
- // return null to block the message entirely
755
- },
756
- })
757
- }
758
- \`\`\`
759
-
760
- ## Handling Settings
761
-
762
- ### Install flow (first-time setup)
763
-
764
- \`\`\`typescript
765
- async install(ctx: InstallContext) {
766
- const apiKey = await ctx.terminal.password({
767
- message: 'Enter your API key:',
768
- validate: (v) => v.length > 0 ? undefined : 'Required',
769
- })
770
- await ctx.settings.set('apiKey', apiKey)
771
- ctx.terminal.log.success('Configured!')
772
- }
773
- \`\`\`
774
-
775
- ### Configure flow (reconfiguration)
776
-
777
- \`\`\`typescript
778
- async configure(ctx: InstallContext) {
779
- const current = await ctx.settings.getAll()
780
- const apiKey = await ctx.terminal.password({
781
- message: \\\`API key (current: \\\${current.apiKey ? '***' : 'not set'}):\\\`,
782
- })
783
- if (apiKey) await ctx.settings.set('apiKey', apiKey)
784
- ctx.terminal.log.success('Updated!')
785
- }
786
- \`\`\`
787
-
788
- ### Reading settings at runtime
789
-
790
- \`\`\`typescript
791
- async setup(ctx: PluginContext) {
792
- const apiKey = ctx.pluginConfig.apiKey as string
793
- if (!apiKey) {
794
- ctx.log.warn('Not configured \u2014 run: openacp plugin configure ${params.pluginName}')
795
- return
796
- }
797
- // Use apiKey...
798
- }
799
- \`\`\`
800
-
801
- ## Testing
802
-
803
- Tests use Vitest and \`@openacp/plugin-sdk/testing\`.
804
-
805
- \`\`\`typescript
806
- import { describe, it, expect } from 'vitest'
807
- import { createTestContext, createTestInstallContext, mockServices } from '@openacp/plugin-sdk/testing'
808
- import plugin from '../index.js'
809
-
810
- describe('${params.pluginName}', () => {
811
- it('registers commands on setup', async () => {
812
- const ctx = createTestContext({ pluginName: '${params.pluginName}' })
813
- await plugin.setup(ctx)
814
- expect(ctx.registeredCommands.has('greet')).toBe(true)
815
- })
816
-
817
- it('command returns expected response', async () => {
818
- const ctx = createTestContext({ pluginName: '${params.pluginName}' })
819
- await plugin.setup(ctx)
820
- const res = await ctx.executeCommand('greet', { raw: 'Alice' })
821
- expect(res).toEqual({ type: 'text', text: 'Hello, Alice!' })
822
- })
823
-
824
- it('install saves settings', async () => {
825
- const ctx = createTestInstallContext({
826
- pluginName: '${params.pluginName}',
827
- terminalResponses: { password: ['sk-test-key'] },
828
- })
829
- await plugin.install!(ctx)
830
- expect(ctx.settingsData.get('apiKey')).toBe('sk-test-key')
831
- })
832
- })
833
- \`\`\`
834
-
835
- ### Available mock services
836
-
837
- \`\`\`typescript
838
- const ctx = createTestContext({
839
- pluginName: '${params.pluginName}',
840
- services: {
841
- security: mockServices.security(),
842
- usage: mockServices.usage({ async checkBudget() { return { ok: false, percent: 100 } } }),
843
- },
844
- })
845
- \`\`\`
846
-
847
- ## Publishing
848
-
849
- 1. Update \`version\` in both \`package.json\` and \`src/index.ts\`
850
- 2. Build and test:
851
- \`\`\`bash
852
- npm run build
853
- npm test
854
- \`\`\`
855
- 3. Publish:
856
- \`\`\`bash
857
- npm publish --access public
858
- \`\`\`
859
- 4. Users install with:
860
- \`\`\`bash
861
- openacp plugin install ${params.pluginName}
862
- \`\`\`
863
- 5. Submit to the [OpenACP Plugin Registry](https://github.com/Open-ACP/plugin-registry) for discoverability.
864
-
865
- ## Useful Links
866
-
867
- - [Architecture: Plugin System](https://docs.openacp.dev/architecture/plugin-system)
868
- - [Architecture: Writing Plugins](https://docs.openacp.dev/architecture/writing-plugins)
869
- - [Architecture: Command System](https://docs.openacp.dev/architecture/command-system)
870
- - [Plugin SDK Reference](https://docs.openacp.dev/extending/plugin-sdk-reference)
871
- - [Getting Started: Your First Plugin](https://docs.openacp.dev/extending/getting-started-plugin)
872
- - [Dev Mode](https://docs.openacp.dev/extending/dev-mode)
873
- - [Contributing](https://github.com/Open-ACP/OpenACP/blob/main/CONTRIBUTING.md)
874
- `;
875
- }
876
-
877
- // src/cli/commands/plugin-create.ts
878
- async function cmdPluginCreate() {
879
- p.intro("Create a new OpenACP plugin");
880
- const result = await p.group(
881
- {
882
- name: () => p.text({
883
- message: "Plugin name (e.g., @myorg/adapter-matrix)",
884
- placeholder: "@myorg/my-plugin",
885
- validate: (value) => {
886
- if (!value || !value.trim()) return "Plugin name is required";
887
- if (!/^(@[a-z0-9-]+\/)?[a-z0-9-]+$/.test(value.trim())) {
888
- return "Must be a valid npm package name (lowercase, hyphens, optional @scope/)";
889
- }
890
- return void 0;
891
- }
892
- }),
893
- description: () => p.text({
894
- message: "Description",
895
- placeholder: "A short description of your plugin"
896
- }),
897
- author: () => p.text({
898
- message: "Author",
899
- placeholder: "Your Name <email@example.com>"
900
- }),
901
- license: () => p.select({
902
- message: "License",
903
- options: [
904
- { value: "MIT", label: "MIT" },
905
- { value: "Apache-2.0", label: "Apache 2.0" },
906
- { value: "ISC", label: "ISC" },
907
- { value: "UNLICENSED", label: "Unlicensed (private)" }
908
- ]
909
- })
910
- },
911
- {
912
- onCancel: () => {
913
- p.cancel("Plugin creation cancelled.");
914
- process.exit(0);
915
- }
916
- }
917
- );
918
- const pluginName = result.name.trim();
919
- const dirName = pluginName.replace(/^@[^/]+\//, "");
920
- const targetDir = path.resolve(process.cwd(), dirName);
921
- if (fs.existsSync(targetDir)) {
922
- p.cancel(`Directory "${dirName}" already exists.`);
923
- process.exit(1);
924
- }
925
- const spinner2 = p.spinner();
926
- spinner2.start("Scaffolding plugin...");
927
- fs.mkdirSync(path.join(targetDir, "src", "__tests__"), { recursive: true });
928
- const params = {
929
- pluginName,
930
- description: result.description || "",
931
- author: result.author || "",
932
- license: result.license,
933
- cliVersion: getCurrentVersion()
934
- };
935
- const files = [
936
- { relativePath: "package.json", content: generatePackageJson(params) },
937
- { relativePath: "tsconfig.json", content: generateTsconfig() },
938
- { relativePath: ".gitignore", content: generateGitignore() },
939
- { relativePath: ".npmignore", content: generateNpmignore() },
940
- { relativePath: ".editorconfig", content: generateEditorconfig() },
941
- { relativePath: "README.md", content: generateReadme(params) },
942
- { relativePath: "CLAUDE.md", content: generateClaudeMd(params) },
943
- { relativePath: "PLUGIN_GUIDE.md", content: generatePluginGuide(params) },
944
- { relativePath: path.join("src", "index.ts"), content: generatePluginSource(params) },
945
- { relativePath: path.join("src", "__tests__", "index.test.ts"), content: generatePluginTest(params) }
946
- ];
947
- for (const file of files) {
948
- fs.writeFileSync(path.join(targetDir, file.relativePath), file.content);
949
- }
950
- spinner2.stop("Plugin scaffolded!");
951
- p.note(
952
- [
953
- `cd ${dirName}`,
954
- "npm install",
955
- "npm run build",
956
- "npm test",
957
- "",
958
- "# Start development with hot-reload:",
959
- `openacp dev .`
960
- ].join("\n"),
961
- "Next steps"
962
- );
963
- p.outro(`Plugin ${pluginName} created in ./${dirName}`);
964
- }
965
- export {
966
- cmdPluginCreate
967
- };
968
- //# sourceMappingURL=plugin-create-HFKS23JY.js.map