@agentplugins/adapter-claude 0.1.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 ADDED
@@ -0,0 +1,55 @@
1
+ # @agentplugins/adapter-claude
2
+
3
+ > AgentPlugins platform adapter for [Claude Code](https://code.claude.com/docs/en/plugins-reference).
4
+
5
+ Compiles a universal `PluginManifest` into Claude Code's native plugin layout: `plugin.json` manifest, `hooks.json` hook wiring, inline handler scripts, `skills/`, and MCP server configuration.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @agentplugins/adapter-claude
11
+ ```
12
+
13
+ This package is typically installed transitively via [`@agentplugins/cli`](https://www.npmjs.com/package/@agentplugins/cli). Install it directly only if you're building a custom build pipeline.
14
+
15
+ ## Usage
16
+
17
+ ```typescript
18
+ import { createClaudeAdapter } from '@agentplugins/adapter-claude';
19
+
20
+ const adapter = createClaudeAdapter();
21
+ const output = await adapter.compile(manifest);
22
+ // → output.files: FileOutput[] of plugin.json, hooks/handlers, skills/, MCP config
23
+ ```
24
+
25
+ You usually don't call the adapter directly — let `@agentplugins/cli` route to it via the registry:
26
+
27
+ ```bash
28
+ npx agentplugins build --target claude
29
+ ```
30
+
31
+ ## Output shape
32
+
33
+ A successful build writes to `dist/claude/`:
34
+
35
+ ```
36
+ dist/claude/
37
+ ├── .claude-plugin/
38
+ │ └── plugin.json
39
+ ├── hooks/
40
+ │ └── hooks.json
41
+ ├── skills/
42
+ │ └── <skill-name>/
43
+ │ └── SKILL.md
44
+ └── .mcp.json
45
+ ```
46
+
47
+ Install with: `cp -r dist/claude ~/.claude/skills/<plugin-name>`
48
+
49
+ ## Supported hooks
50
+
51
+ `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`, `SubagentStop`, `SessionStart`, `SessionEnd`. See [HookContext in `@agentplugins/core`](https://www.npmjs.com/package/@agentplugins/core) for the universal hook names.
52
+
53
+ ## License
54
+
55
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,509 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ createAdapter: () => createAdapter,
24
+ createClaudeAdapter: () => createClaudeAdapter,
25
+ default: () => createClaudeAdapter
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+ var import_core = require("@agentplugins/core");
29
+ var CLAUDE_SUPPORTED_HOOKS = [
30
+ "sessionStart",
31
+ "sessionEnd",
32
+ "userPromptSubmit",
33
+ "userPromptExpansion",
34
+ "preToolUse",
35
+ "postToolUse",
36
+ "postToolUseFailure",
37
+ "permissionRequest",
38
+ "permissionDenied",
39
+ "subagentStart",
40
+ "subagentStop",
41
+ "preCompact",
42
+ "postCompact",
43
+ "stop",
44
+ "stopFailure",
45
+ "notification",
46
+ "fileChanged",
47
+ "cwdChanged",
48
+ "setup"
49
+ ];
50
+ var CLAUDE_SUPPORTED_HANDLERS = [
51
+ "command",
52
+ "http",
53
+ "inline"
54
+ // auto-wrapped as command scripts
55
+ ];
56
+ var HOOK_NAME_MAP = {
57
+ sessionStart: "SessionStart",
58
+ sessionEnd: "SessionEnd",
59
+ userPromptSubmit: "UserPromptSubmit",
60
+ userPromptExpansion: "UserPromptExpansion",
61
+ preToolUse: "PreToolUse",
62
+ postToolUse: "PostToolUse",
63
+ postToolUseFailure: "PostToolUseFailure",
64
+ permissionRequest: "PermissionRequest",
65
+ permissionDenied: "PermissionDenied",
66
+ subagentStart: "SubagentStart",
67
+ subagentStop: "SubagentStop",
68
+ preCompact: "PreCompact",
69
+ postCompact: "PostCompact",
70
+ stop: "Stop",
71
+ stopFailure: "StopFailure",
72
+ notification: "Notification",
73
+ fileChanged: "FileChanged",
74
+ cwdChanged: "CwdChanged",
75
+ setup: "Setup"
76
+ };
77
+ function createClaudeAdapter() {
78
+ return new ClaudePlatformAdapter();
79
+ }
80
+ function createAdapter() {
81
+ return createClaudeAdapter();
82
+ }
83
+ var ClaudePlatformAdapter = class {
84
+ name = "claude";
85
+ displayName = "Claude Code";
86
+ supportedHooks = CLAUDE_SUPPORTED_HOOKS;
87
+ supportedHandlers = CLAUDE_SUPPORTED_HANDLERS;
88
+ manifestPath = ".claude-plugin/plugin.json";
89
+ manifestFormat = "json";
90
+ // Inline handlers are wrapped as command scripts; tracks wrapper ID -> hook name
91
+ inlineWrappers = /* @__PURE__ */ new Map();
92
+ // ─── Validation ────────────────────────────────────────────────────────────
93
+ validate(plugin) {
94
+ const issues = [...(0, import_core.validateForPlatform)(plugin, "claude")];
95
+ if (plugin.hooks) {
96
+ for (const [name, def] of Object.entries(plugin.hooks)) {
97
+ const hookName = name;
98
+ if (!def) continue;
99
+ if (def.handler.type === "inline") {
100
+ const handler = def.handler;
101
+ if (typeof handler.handler !== "function") {
102
+ issues.push({
103
+ severity: import_core.Severity.ERROR,
104
+ field: `hooks.${hookName}.handler`,
105
+ message: "Inline handler must be a function"
106
+ });
107
+ } else {
108
+ issues.push({
109
+ severity: import_core.Severity.INFO,
110
+ field: `hooks.${hookName}.handler`,
111
+ message: "Inline handler will be auto-wrapped as a Claude command script",
112
+ suggestion: 'For better performance, use a "command" or "http" handler'
113
+ });
114
+ }
115
+ }
116
+ if (def.matcher && ["preToolUse", "postToolUse", "postToolUseFailure"].includes(hookName)) {
117
+ if (def.matcher.includes("*") && !def.matcher.match(/^\*?\w+\*?$/)) {
118
+ issues.push({
119
+ severity: import_core.Severity.WARNING,
120
+ field: `hooks.${hookName}.matcher`,
121
+ message: `Matcher pattern "${def.matcher}" may not be supported by Claude for tool hooks`,
122
+ suggestion: 'Use simple patterns like "toolName", "prefix*", or "*suffix"'
123
+ });
124
+ }
125
+ }
126
+ }
127
+ }
128
+ if (plugin.mcpServers) {
129
+ for (const [serverName, config] of Object.entries(plugin.mcpServers)) {
130
+ if (!config.command) {
131
+ issues.push({
132
+ severity: import_core.Severity.ERROR,
133
+ field: `mcpServers.${serverName}.command`,
134
+ message: `MCP server "${serverName}" requires a command`
135
+ });
136
+ }
137
+ if (config.transport && config.transport !== "stdio" && config.transport !== "http") {
138
+ issues.push({
139
+ severity: import_core.Severity.WARNING,
140
+ field: `mcpServers.${serverName}.transport`,
141
+ message: `Transport "${config.transport}" may not be supported by Claude MCP`,
142
+ suggestion: 'Use "stdio" (default) or "http"'
143
+ });
144
+ }
145
+ }
146
+ }
147
+ if (plugin.skills) {
148
+ for (let i = 0; i < plugin.skills.length; i++) {
149
+ const skill = plugin.skills[i];
150
+ if (skill.content && skill.filePath) {
151
+ issues.push({
152
+ severity: import_core.Severity.WARNING,
153
+ field: `skills[${i}]`,
154
+ message: `Skill "${skill.name}" has both content and filePath \u2014 content takes precedence`
155
+ });
156
+ }
157
+ }
158
+ }
159
+ if (plugin.userConfig) {
160
+ const supportedTypes = ["string", "number", "boolean", "directory", "file"];
161
+ for (const [key, opt] of Object.entries(plugin.userConfig)) {
162
+ if (!supportedTypes.includes(opt.type)) {
163
+ issues.push({
164
+ severity: import_core.Severity.WARNING,
165
+ field: `userConfig.${key}.type`,
166
+ message: `User config type "${opt.type}" may not be fully supported by Claude`,
167
+ suggestion: `Supported types: ${supportedTypes.join(", ")}`
168
+ });
169
+ }
170
+ }
171
+ }
172
+ return issues;
173
+ }
174
+ // ─── Compilation ───────────────────────────────────────────────────────────
175
+ compile(plugin) {
176
+ this.inlineWrappers.clear();
177
+ const files = [];
178
+ const warnings = [];
179
+ const postInstall = [];
180
+ const claudeManifest = this.buildManifest(plugin);
181
+ const hooksConfig = this.buildHooksConfig(plugin);
182
+ if (hooksConfig.length > 0) {
183
+ files.push({
184
+ path: "hooks/hooks.json",
185
+ content: JSON.stringify(hooksConfig, null, 2)
186
+ });
187
+ }
188
+ const inlineHandlers = this.extractInlineHandlers(plugin);
189
+ if (inlineHandlers.size > 0) {
190
+ const handlersModule = (0, import_core.generateHandlersModule)(inlineHandlers);
191
+ files.push({
192
+ path: "hooks/__agentplugins_handlers__.js",
193
+ content: handlersModule
194
+ });
195
+ for (const [wrapperId, { hookName }] of this.inlineWrappers.entries()) {
196
+ const wrapperScript = (0, import_core.generateHookWrapper)("./__agentplugins_handlers__.js", {
197
+ platform: "claude",
198
+ hookName,
199
+ wrapperId
200
+ });
201
+ files.push({
202
+ path: `hooks/${wrapperId}.js`,
203
+ content: wrapperScript
204
+ });
205
+ }
206
+ warnings.push(
207
+ `${inlineHandlers.size} inline handler(s) wrapped as command scripts. These require Node.js at runtime.`
208
+ );
209
+ }
210
+ if (plugin.skills && plugin.skills.length > 0) {
211
+ for (const skill of plugin.skills) {
212
+ const skillContent = this.buildSkillFile(skill);
213
+ const skillPath = `skills/${skill.name}/SKILL.md`;
214
+ files.push({
215
+ path: skillPath,
216
+ content: skillContent
217
+ });
218
+ }
219
+ }
220
+ if (plugin.mcpServers && Object.keys(plugin.mcpServers).length > 0) {
221
+ const mcpConfig = this.buildMCPConfig(plugin);
222
+ files.push({
223
+ path: ".mcp.json",
224
+ content: JSON.stringify(mcpConfig, null, 2)
225
+ });
226
+ }
227
+ const envScript = this.buildEnvScript(plugin);
228
+ if (envScript) {
229
+ files.push({
230
+ path: "hooks/__env__.sh",
231
+ content: envScript
232
+ });
233
+ }
234
+ postInstall.push(
235
+ `Copy the generated files to your Claude Code project root:`,
236
+ ` cp -r .claude-plugin hooks/ skills/ .mcp.json <your-project>/`,
237
+ ``,
238
+ `Environment variables available at runtime:`,
239
+ ` \${CLAUDE_PLUGIN_ROOT} \u2014 path to the plugin directory`,
240
+ ` \${CLAUDE_PLUGIN_DATA} \u2014 path to plugin data storage`,
241
+ ``,
242
+ `Install location: ~/.claude/plugins/ or project-local .claude-plugin/`
243
+ );
244
+ if (inlineHandlers.size > 0) {
245
+ postInstall.push(
246
+ ``,
247
+ `NOTE: ${inlineHandlers.size} inline handler(s) require Node.js to execute.`,
248
+ ` Wrapper scripts are in hooks/*.js`
249
+ );
250
+ }
251
+ return {
252
+ files,
253
+ manifest: claudeManifest,
254
+ warnings,
255
+ issues: [],
256
+ postInstall: postInstall.length > 0 ? postInstall : void 0
257
+ };
258
+ }
259
+ // ─── Internal Helpers ──────────────────────────────────────────────────────
260
+ /**
261
+ * Build the Claude plugin.json manifest from a universal plugin manifest.
262
+ */
263
+ buildManifest(plugin) {
264
+ const manifest = {
265
+ name: plugin.name,
266
+ version: plugin.version,
267
+ description: plugin.description,
268
+ displayName: plugin.displayName,
269
+ author: plugin.author,
270
+ homepage: plugin.homepage,
271
+ repository: plugin.repository,
272
+ license: plugin.license,
273
+ keywords: plugin.keywords,
274
+ defaultEnabled: plugin.defaultEnabled ?? true,
275
+ root: "${CLAUDE_PLUGIN_ROOT}",
276
+ data: "${CLAUDE_PLUGIN_DATA}"
277
+ };
278
+ if (plugin.userConfig) {
279
+ manifest.userConfig = Object.fromEntries(
280
+ Object.entries(plugin.userConfig).map(([key, opt]) => [
281
+ key,
282
+ {
283
+ type: opt.type,
284
+ title: opt.title,
285
+ description: opt.description,
286
+ sensitive: opt.sensitive,
287
+ required: opt.required,
288
+ default: opt.default,
289
+ multiple: opt.multiple
290
+ }
291
+ ])
292
+ );
293
+ }
294
+ if (plugin.dependencies) {
295
+ manifest.dependencies = plugin.dependencies;
296
+ }
297
+ if (plugin.themes) {
298
+ manifest.themes = plugin.themes;
299
+ }
300
+ if (plugin.monitors) {
301
+ manifest.monitors = plugin.monitors;
302
+ }
303
+ if (plugin.lspServers) {
304
+ manifest.lspServers = plugin.lspServers;
305
+ }
306
+ return manifest;
307
+ }
308
+ /**
309
+ * Build the hooks.json configuration from universal hooks.
310
+ */
311
+ buildHooksConfig(plugin) {
312
+ if (!plugin.hooks) return [];
313
+ const entries = [];
314
+ for (const [name, def] of Object.entries(plugin.hooks)) {
315
+ const hookName = name;
316
+ if (!def) continue;
317
+ if (!this.supportedHooks.includes(hookName)) continue;
318
+ const claudeEvent = HOOK_NAME_MAP[hookName];
319
+ const handlerConfig = this.buildHandlerConfig(def, hookName);
320
+ if (handlerConfig) {
321
+ const entry = {
322
+ event: claudeEvent,
323
+ handler: handlerConfig
324
+ };
325
+ if (def.matcher) {
326
+ entry.matcher = def.matcher;
327
+ } else if (hookName === "preToolUse" || hookName === "postToolUse" || hookName === "postToolUseFailure") {
328
+ entry.matcher = "*";
329
+ }
330
+ entries.push(entry);
331
+ }
332
+ }
333
+ return entries;
334
+ }
335
+ /**
336
+ * Build a single handler configuration for hooks.json.
337
+ */
338
+ buildHandlerConfig(def, hookName) {
339
+ const handler = def.handler;
340
+ switch (handler.type) {
341
+ case "command": {
342
+ return {
343
+ type: "command",
344
+ command: handler.command,
345
+ shell: handler.shell,
346
+ statusMessage: handler.statusMessage
347
+ };
348
+ }
349
+ case "http": {
350
+ return {
351
+ type: "http",
352
+ url: handler.url,
353
+ headers: handler.headers
354
+ };
355
+ }
356
+ case "inline": {
357
+ const wrapperId = `__inline_${hookName}_${this.hashId(hookName)}`;
358
+ this.inlineWrappers.set(wrapperId, { hookName });
359
+ return {
360
+ type: "command",
361
+ command: `node "\${CLAUDE_PLUGIN_ROOT}/hooks/${wrapperId}.js"`,
362
+ shell: "bash",
363
+ statusMessage: `Running ${hookName} hook...`
364
+ };
365
+ }
366
+ default: {
367
+ const unknownHandler = handler;
368
+ if (unknownHandler.type === "mcp_tool") {
369
+ return {
370
+ type: "mcp_tool",
371
+ mcpTool: unknownHandler.mcpTool || unknownHandler.tool,
372
+ mcpServer: unknownHandler.mcpServer || unknownHandler.server
373
+ };
374
+ }
375
+ if (unknownHandler.type === "prompt") {
376
+ return {
377
+ type: "prompt",
378
+ prompt: unknownHandler.prompt
379
+ };
380
+ }
381
+ if (unknownHandler.type === "agent") {
382
+ return {
383
+ type: "agent",
384
+ agent: unknownHandler.agent
385
+ };
386
+ }
387
+ return null;
388
+ }
389
+ }
390
+ }
391
+ /**
392
+ * Build a SKILL.md file with YAML frontmatter from a universal skill definition.
393
+ */
394
+ buildSkillFile(skill) {
395
+ const frontmatter = {
396
+ name: skill.name,
397
+ description: skill.description
398
+ };
399
+ if (skill.filePath && !skill.content) {
400
+ frontmatter.source = skill.filePath;
401
+ }
402
+ const yamlLines = Object.entries(frontmatter).map(([key, value]) => {
403
+ if (typeof value === "string") {
404
+ if (value.includes(":") || value.includes("#") || value.includes('"')) {
405
+ return `${key}: "${value.replace(/"/g, '\\"')}"`;
406
+ }
407
+ return `${key}: ${value}`;
408
+ }
409
+ return `${key}: ${value}`;
410
+ }).join("\n");
411
+ const frontmatterBlock = `---
412
+ ${yamlLines}
413
+ ---
414
+ `;
415
+ if (skill.content) {
416
+ if (skill.content.trimStart().startsWith("---")) {
417
+ return skill.content;
418
+ }
419
+ return frontmatterBlock + "\n" + skill.content;
420
+ }
421
+ if (skill.filePath) {
422
+ return frontmatterBlock + `
423
+ # ${skill.name}
424
+
425
+ See [${skill.filePath}](${skill.filePath}) for the full skill documentation.
426
+ `;
427
+ }
428
+ return frontmatterBlock + `
429
+ # ${skill.name}
430
+
431
+ ${skill.description}
432
+ `;
433
+ }
434
+ /**
435
+ * Build the .mcp.json configuration from universal MCP server definitions.
436
+ */
437
+ buildMCPConfig(plugin) {
438
+ if (!plugin.mcpServers) return {};
439
+ const servers = {};
440
+ for (const [serverName, config] of Object.entries(plugin.mcpServers)) {
441
+ servers[serverName] = {
442
+ command: config.command,
443
+ args: config.args ?? [],
444
+ env: config.env ?? {},
445
+ // Claude uses 'stdio' by default
446
+ transport: config.transport ?? "stdio"
447
+ };
448
+ }
449
+ return { servers };
450
+ }
451
+ /**
452
+ * Extract inline handlers that need wrapping, returning a map suitable
453
+ * for generateHandlersModule().
454
+ */
455
+ extractInlineHandlers(plugin) {
456
+ const handlers = /* @__PURE__ */ new Map();
457
+ if (!plugin.hooks) return handlers;
458
+ for (const [name, def] of Object.entries(plugin.hooks)) {
459
+ const hookName = name;
460
+ if (!def || def.handler.type !== "inline") continue;
461
+ const inlineHandler = def.handler;
462
+ if (typeof inlineHandler.handler === "function") {
463
+ const wrapperId = `__inline_${hookName}_${this.hashId(hookName)}`;
464
+ handlers.set(wrapperId, inlineHandler.handler);
465
+ }
466
+ }
467
+ return handlers;
468
+ }
469
+ /**
470
+ * Build an environment setup script that exports CLAUDE_PLUGIN_ROOT and
471
+ * CLAUDE_PLUGIN_DATA for use by hook scripts.
472
+ */
473
+ buildEnvScript(plugin) {
474
+ if (!plugin.hooks) return null;
475
+ const hasCommandHandlers = Object.values(plugin.hooks).some(
476
+ (def) => def?.handler.type === "command" || def?.handler.type === "inline"
477
+ );
478
+ if (!hasCommandHandlers) return null;
479
+ return `#!/usr/bin/env bash
480
+ # AgentPlugins Auto-Generated Environment Setup for Claude Code
481
+ # Plugin: ${plugin.name}
482
+ # DO NOT EDIT \u2014 This file is regenerated on each build.
483
+
484
+ # Resolve plugin root relative to this script
485
+ export CLAUDE_PLUGIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
486
+ export CLAUDE_PLUGIN_DATA="\${CLAUDE_PLUGIN_ROOT}/.data"
487
+
488
+ # Ensure data directory exists
489
+ mkdir -p "\${CLAUDE_PLUGIN_DATA}"
490
+ `;
491
+ }
492
+ /**
493
+ * Generate a short hash for a hook name to use in wrapper script filenames.
494
+ */
495
+ hashId(input) {
496
+ let hash = 0;
497
+ for (let i = 0; i < input.length; i++) {
498
+ const char = input.charCodeAt(i);
499
+ hash = (hash << 5) - hash + char;
500
+ hash |= 0;
501
+ }
502
+ return Math.abs(hash).toString(36).slice(0, 6);
503
+ }
504
+ };
505
+ // Annotate the CommonJS export names for ESM import in node:
506
+ 0 && (module.exports = {
507
+ createAdapter,
508
+ createClaudeAdapter
509
+ });
@@ -0,0 +1,120 @@
1
+ import { PlatformAdapter } from '@agentplugins/core';
2
+
3
+ /**
4
+ * AgentPlugins Claude Code Adapter
5
+ *
6
+ * Compiles universal AgentPlugins plugins into Claude Code's native plugin format.
7
+ *
8
+ * Claude Code uses:
9
+ * - `.claude-plugin/plugin.json` — manifest
10
+ * - `hooks/hooks.json` — hook configuration with matchers
11
+ * - `skills/<name>/SKILL.md` — skill documentation (YAML frontmatter + markdown)
12
+ * - `.mcp.json` — MCP server configurations
13
+ * - `${CLAUDE_PLUGIN_ROOT}` and `${CLAUDE_PLUGIN_DATA}` — env var placeholders
14
+ *
15
+ * Handler mapping:
16
+ * - command → native command handler
17
+ * - http → native HTTP handler
18
+ * - mcp_tool → Claude's MCP tool handler (emitted in hooks.json)
19
+ * - prompt → Claude's prompt handler (emitted in hooks.json)
20
+ * - agent → Claude's agent handler (emitted in hooks.json)
21
+ * - inline → auto-wrapped as Node.js command script
22
+ *
23
+ * All 19 universal hooks are supported by Claude Code.
24
+ */
25
+
26
+ /** Claude-specific handler types beyond the universal ones. */
27
+ type ClaudeHandlerType = 'command' | 'http' | 'mcp_tool' | 'prompt' | 'agent';
28
+ /** Claude plugin manifest shape. */
29
+ interface ClaudePluginManifest {
30
+ name: string;
31
+ version: string;
32
+ description: string;
33
+ displayName?: string;
34
+ author?: string | {
35
+ name: string;
36
+ email?: string;
37
+ url?: string;
38
+ };
39
+ homepage?: string;
40
+ repository?: string;
41
+ license?: string;
42
+ keywords?: string[];
43
+ defaultEnabled?: boolean;
44
+ /** Claude-specific: plugin root directory env var */
45
+ root?: string;
46
+ /** Claude-specific: plugin data directory env var */
47
+ data?: string;
48
+ /** User configuration schema */
49
+ userConfig?: Record<string, ClaudeUserConfigField>;
50
+ /** Dependencies on other plugins */
51
+ dependencies?: string[];
52
+ /** Theme configuration */
53
+ themes?: ClaudeThemeConfig[];
54
+ /** File monitors */
55
+ monitors?: ClaudeMonitorConfig[];
56
+ /** LSP server configuration */
57
+ lspServers?: ClaudeLSPServerConfig[];
58
+ /** Extra metadata allowed by Claude */
59
+ [key: string]: unknown;
60
+ }
61
+ /** Claude user config field definition. */
62
+ interface ClaudeUserConfigField {
63
+ type: 'string' | 'number' | 'boolean' | 'directory' | 'file';
64
+ title: string;
65
+ description: string;
66
+ sensitive?: boolean;
67
+ required?: boolean;
68
+ default?: unknown;
69
+ multiple?: boolean;
70
+ }
71
+ /** Claude theme configuration. */
72
+ interface ClaudeThemeConfig {
73
+ name: string;
74
+ path: string;
75
+ }
76
+ /** Claude file monitor configuration. */
77
+ interface ClaudeMonitorConfig {
78
+ pattern: string;
79
+ event: string;
80
+ }
81
+ /** Claude LSP server configuration. */
82
+ interface ClaudeLSPServerConfig {
83
+ name: string;
84
+ command: string;
85
+ args?: string[];
86
+ }
87
+ /** Hook entry in Claude's hooks.json. */
88
+ interface ClaudeHookEntry {
89
+ event: string;
90
+ matcher?: string;
91
+ handler: ClaudeHandlerConfig;
92
+ }
93
+ /** Handler configuration in hooks.json. */
94
+ interface ClaudeHandlerConfig {
95
+ type: ClaudeHandlerType;
96
+ command?: string;
97
+ shell?: string;
98
+ statusMessage?: string;
99
+ url?: string;
100
+ headers?: Record<string, string>;
101
+ mcpTool?: string;
102
+ mcpServer?: string;
103
+ prompt?: string;
104
+ agent?: string;
105
+ }
106
+ /**
107
+ * Create a Claude Code platform adapter.
108
+ *
109
+ * Usage:
110
+ * ```ts
111
+ * import { createAdapter } from '@agentplugins/adapter-claude';
112
+ * const adapter = createAdapter();
113
+ * const output = adapter.compile(manifest);
114
+ * ```
115
+ */
116
+ declare function createClaudeAdapter(): PlatformAdapter;
117
+ /** @deprecated Use createClaudeAdapter instead */
118
+ declare function createAdapter(): PlatformAdapter;
119
+
120
+ export { type ClaudeHandlerConfig, type ClaudeHookEntry, type ClaudeLSPServerConfig, type ClaudeMonitorConfig, type ClaudePluginManifest, type ClaudeThemeConfig, type ClaudeUserConfigField, createAdapter, createClaudeAdapter, createClaudeAdapter as default };
@@ -0,0 +1,120 @@
1
+ import { PlatformAdapter } from '@agentplugins/core';
2
+
3
+ /**
4
+ * AgentPlugins Claude Code Adapter
5
+ *
6
+ * Compiles universal AgentPlugins plugins into Claude Code's native plugin format.
7
+ *
8
+ * Claude Code uses:
9
+ * - `.claude-plugin/plugin.json` — manifest
10
+ * - `hooks/hooks.json` — hook configuration with matchers
11
+ * - `skills/<name>/SKILL.md` — skill documentation (YAML frontmatter + markdown)
12
+ * - `.mcp.json` — MCP server configurations
13
+ * - `${CLAUDE_PLUGIN_ROOT}` and `${CLAUDE_PLUGIN_DATA}` — env var placeholders
14
+ *
15
+ * Handler mapping:
16
+ * - command → native command handler
17
+ * - http → native HTTP handler
18
+ * - mcp_tool → Claude's MCP tool handler (emitted in hooks.json)
19
+ * - prompt → Claude's prompt handler (emitted in hooks.json)
20
+ * - agent → Claude's agent handler (emitted in hooks.json)
21
+ * - inline → auto-wrapped as Node.js command script
22
+ *
23
+ * All 19 universal hooks are supported by Claude Code.
24
+ */
25
+
26
+ /** Claude-specific handler types beyond the universal ones. */
27
+ type ClaudeHandlerType = 'command' | 'http' | 'mcp_tool' | 'prompt' | 'agent';
28
+ /** Claude plugin manifest shape. */
29
+ interface ClaudePluginManifest {
30
+ name: string;
31
+ version: string;
32
+ description: string;
33
+ displayName?: string;
34
+ author?: string | {
35
+ name: string;
36
+ email?: string;
37
+ url?: string;
38
+ };
39
+ homepage?: string;
40
+ repository?: string;
41
+ license?: string;
42
+ keywords?: string[];
43
+ defaultEnabled?: boolean;
44
+ /** Claude-specific: plugin root directory env var */
45
+ root?: string;
46
+ /** Claude-specific: plugin data directory env var */
47
+ data?: string;
48
+ /** User configuration schema */
49
+ userConfig?: Record<string, ClaudeUserConfigField>;
50
+ /** Dependencies on other plugins */
51
+ dependencies?: string[];
52
+ /** Theme configuration */
53
+ themes?: ClaudeThemeConfig[];
54
+ /** File monitors */
55
+ monitors?: ClaudeMonitorConfig[];
56
+ /** LSP server configuration */
57
+ lspServers?: ClaudeLSPServerConfig[];
58
+ /** Extra metadata allowed by Claude */
59
+ [key: string]: unknown;
60
+ }
61
+ /** Claude user config field definition. */
62
+ interface ClaudeUserConfigField {
63
+ type: 'string' | 'number' | 'boolean' | 'directory' | 'file';
64
+ title: string;
65
+ description: string;
66
+ sensitive?: boolean;
67
+ required?: boolean;
68
+ default?: unknown;
69
+ multiple?: boolean;
70
+ }
71
+ /** Claude theme configuration. */
72
+ interface ClaudeThemeConfig {
73
+ name: string;
74
+ path: string;
75
+ }
76
+ /** Claude file monitor configuration. */
77
+ interface ClaudeMonitorConfig {
78
+ pattern: string;
79
+ event: string;
80
+ }
81
+ /** Claude LSP server configuration. */
82
+ interface ClaudeLSPServerConfig {
83
+ name: string;
84
+ command: string;
85
+ args?: string[];
86
+ }
87
+ /** Hook entry in Claude's hooks.json. */
88
+ interface ClaudeHookEntry {
89
+ event: string;
90
+ matcher?: string;
91
+ handler: ClaudeHandlerConfig;
92
+ }
93
+ /** Handler configuration in hooks.json. */
94
+ interface ClaudeHandlerConfig {
95
+ type: ClaudeHandlerType;
96
+ command?: string;
97
+ shell?: string;
98
+ statusMessage?: string;
99
+ url?: string;
100
+ headers?: Record<string, string>;
101
+ mcpTool?: string;
102
+ mcpServer?: string;
103
+ prompt?: string;
104
+ agent?: string;
105
+ }
106
+ /**
107
+ * Create a Claude Code platform adapter.
108
+ *
109
+ * Usage:
110
+ * ```ts
111
+ * import { createAdapter } from '@agentplugins/adapter-claude';
112
+ * const adapter = createAdapter();
113
+ * const output = adapter.compile(manifest);
114
+ * ```
115
+ */
116
+ declare function createClaudeAdapter(): PlatformAdapter;
117
+ /** @deprecated Use createClaudeAdapter instead */
118
+ declare function createAdapter(): PlatformAdapter;
119
+
120
+ export { type ClaudeHandlerConfig, type ClaudeHookEntry, type ClaudeLSPServerConfig, type ClaudeMonitorConfig, type ClaudePluginManifest, type ClaudeThemeConfig, type ClaudeUserConfigField, createAdapter, createClaudeAdapter, createClaudeAdapter as default };
package/dist/index.js ADDED
@@ -0,0 +1,488 @@
1
+ // src/index.ts
2
+ import {
3
+ generateHookWrapper,
4
+ generateHandlersModule,
5
+ validateForPlatform,
6
+ Severity
7
+ } from "@agentplugins/core";
8
+ var CLAUDE_SUPPORTED_HOOKS = [
9
+ "sessionStart",
10
+ "sessionEnd",
11
+ "userPromptSubmit",
12
+ "userPromptExpansion",
13
+ "preToolUse",
14
+ "postToolUse",
15
+ "postToolUseFailure",
16
+ "permissionRequest",
17
+ "permissionDenied",
18
+ "subagentStart",
19
+ "subagentStop",
20
+ "preCompact",
21
+ "postCompact",
22
+ "stop",
23
+ "stopFailure",
24
+ "notification",
25
+ "fileChanged",
26
+ "cwdChanged",
27
+ "setup"
28
+ ];
29
+ var CLAUDE_SUPPORTED_HANDLERS = [
30
+ "command",
31
+ "http",
32
+ "inline"
33
+ // auto-wrapped as command scripts
34
+ ];
35
+ var HOOK_NAME_MAP = {
36
+ sessionStart: "SessionStart",
37
+ sessionEnd: "SessionEnd",
38
+ userPromptSubmit: "UserPromptSubmit",
39
+ userPromptExpansion: "UserPromptExpansion",
40
+ preToolUse: "PreToolUse",
41
+ postToolUse: "PostToolUse",
42
+ postToolUseFailure: "PostToolUseFailure",
43
+ permissionRequest: "PermissionRequest",
44
+ permissionDenied: "PermissionDenied",
45
+ subagentStart: "SubagentStart",
46
+ subagentStop: "SubagentStop",
47
+ preCompact: "PreCompact",
48
+ postCompact: "PostCompact",
49
+ stop: "Stop",
50
+ stopFailure: "StopFailure",
51
+ notification: "Notification",
52
+ fileChanged: "FileChanged",
53
+ cwdChanged: "CwdChanged",
54
+ setup: "Setup"
55
+ };
56
+ function createClaudeAdapter() {
57
+ return new ClaudePlatformAdapter();
58
+ }
59
+ function createAdapter() {
60
+ return createClaudeAdapter();
61
+ }
62
+ var ClaudePlatformAdapter = class {
63
+ name = "claude";
64
+ displayName = "Claude Code";
65
+ supportedHooks = CLAUDE_SUPPORTED_HOOKS;
66
+ supportedHandlers = CLAUDE_SUPPORTED_HANDLERS;
67
+ manifestPath = ".claude-plugin/plugin.json";
68
+ manifestFormat = "json";
69
+ // Inline handlers are wrapped as command scripts; tracks wrapper ID -> hook name
70
+ inlineWrappers = /* @__PURE__ */ new Map();
71
+ // ─── Validation ────────────────────────────────────────────────────────────
72
+ validate(plugin) {
73
+ const issues = [...validateForPlatform(plugin, "claude")];
74
+ if (plugin.hooks) {
75
+ for (const [name, def] of Object.entries(plugin.hooks)) {
76
+ const hookName = name;
77
+ if (!def) continue;
78
+ if (def.handler.type === "inline") {
79
+ const handler = def.handler;
80
+ if (typeof handler.handler !== "function") {
81
+ issues.push({
82
+ severity: Severity.ERROR,
83
+ field: `hooks.${hookName}.handler`,
84
+ message: "Inline handler must be a function"
85
+ });
86
+ } else {
87
+ issues.push({
88
+ severity: Severity.INFO,
89
+ field: `hooks.${hookName}.handler`,
90
+ message: "Inline handler will be auto-wrapped as a Claude command script",
91
+ suggestion: 'For better performance, use a "command" or "http" handler'
92
+ });
93
+ }
94
+ }
95
+ if (def.matcher && ["preToolUse", "postToolUse", "postToolUseFailure"].includes(hookName)) {
96
+ if (def.matcher.includes("*") && !def.matcher.match(/^\*?\w+\*?$/)) {
97
+ issues.push({
98
+ severity: Severity.WARNING,
99
+ field: `hooks.${hookName}.matcher`,
100
+ message: `Matcher pattern "${def.matcher}" may not be supported by Claude for tool hooks`,
101
+ suggestion: 'Use simple patterns like "toolName", "prefix*", or "*suffix"'
102
+ });
103
+ }
104
+ }
105
+ }
106
+ }
107
+ if (plugin.mcpServers) {
108
+ for (const [serverName, config] of Object.entries(plugin.mcpServers)) {
109
+ if (!config.command) {
110
+ issues.push({
111
+ severity: Severity.ERROR,
112
+ field: `mcpServers.${serverName}.command`,
113
+ message: `MCP server "${serverName}" requires a command`
114
+ });
115
+ }
116
+ if (config.transport && config.transport !== "stdio" && config.transport !== "http") {
117
+ issues.push({
118
+ severity: Severity.WARNING,
119
+ field: `mcpServers.${serverName}.transport`,
120
+ message: `Transport "${config.transport}" may not be supported by Claude MCP`,
121
+ suggestion: 'Use "stdio" (default) or "http"'
122
+ });
123
+ }
124
+ }
125
+ }
126
+ if (plugin.skills) {
127
+ for (let i = 0; i < plugin.skills.length; i++) {
128
+ const skill = plugin.skills[i];
129
+ if (skill.content && skill.filePath) {
130
+ issues.push({
131
+ severity: Severity.WARNING,
132
+ field: `skills[${i}]`,
133
+ message: `Skill "${skill.name}" has both content and filePath \u2014 content takes precedence`
134
+ });
135
+ }
136
+ }
137
+ }
138
+ if (plugin.userConfig) {
139
+ const supportedTypes = ["string", "number", "boolean", "directory", "file"];
140
+ for (const [key, opt] of Object.entries(plugin.userConfig)) {
141
+ if (!supportedTypes.includes(opt.type)) {
142
+ issues.push({
143
+ severity: Severity.WARNING,
144
+ field: `userConfig.${key}.type`,
145
+ message: `User config type "${opt.type}" may not be fully supported by Claude`,
146
+ suggestion: `Supported types: ${supportedTypes.join(", ")}`
147
+ });
148
+ }
149
+ }
150
+ }
151
+ return issues;
152
+ }
153
+ // ─── Compilation ───────────────────────────────────────────────────────────
154
+ compile(plugin) {
155
+ this.inlineWrappers.clear();
156
+ const files = [];
157
+ const warnings = [];
158
+ const postInstall = [];
159
+ const claudeManifest = this.buildManifest(plugin);
160
+ const hooksConfig = this.buildHooksConfig(plugin);
161
+ if (hooksConfig.length > 0) {
162
+ files.push({
163
+ path: "hooks/hooks.json",
164
+ content: JSON.stringify(hooksConfig, null, 2)
165
+ });
166
+ }
167
+ const inlineHandlers = this.extractInlineHandlers(plugin);
168
+ if (inlineHandlers.size > 0) {
169
+ const handlersModule = generateHandlersModule(inlineHandlers);
170
+ files.push({
171
+ path: "hooks/__agentplugins_handlers__.js",
172
+ content: handlersModule
173
+ });
174
+ for (const [wrapperId, { hookName }] of this.inlineWrappers.entries()) {
175
+ const wrapperScript = generateHookWrapper("./__agentplugins_handlers__.js", {
176
+ platform: "claude",
177
+ hookName,
178
+ wrapperId
179
+ });
180
+ files.push({
181
+ path: `hooks/${wrapperId}.js`,
182
+ content: wrapperScript
183
+ });
184
+ }
185
+ warnings.push(
186
+ `${inlineHandlers.size} inline handler(s) wrapped as command scripts. These require Node.js at runtime.`
187
+ );
188
+ }
189
+ if (plugin.skills && plugin.skills.length > 0) {
190
+ for (const skill of plugin.skills) {
191
+ const skillContent = this.buildSkillFile(skill);
192
+ const skillPath = `skills/${skill.name}/SKILL.md`;
193
+ files.push({
194
+ path: skillPath,
195
+ content: skillContent
196
+ });
197
+ }
198
+ }
199
+ if (plugin.mcpServers && Object.keys(plugin.mcpServers).length > 0) {
200
+ const mcpConfig = this.buildMCPConfig(plugin);
201
+ files.push({
202
+ path: ".mcp.json",
203
+ content: JSON.stringify(mcpConfig, null, 2)
204
+ });
205
+ }
206
+ const envScript = this.buildEnvScript(plugin);
207
+ if (envScript) {
208
+ files.push({
209
+ path: "hooks/__env__.sh",
210
+ content: envScript
211
+ });
212
+ }
213
+ postInstall.push(
214
+ `Copy the generated files to your Claude Code project root:`,
215
+ ` cp -r .claude-plugin hooks/ skills/ .mcp.json <your-project>/`,
216
+ ``,
217
+ `Environment variables available at runtime:`,
218
+ ` \${CLAUDE_PLUGIN_ROOT} \u2014 path to the plugin directory`,
219
+ ` \${CLAUDE_PLUGIN_DATA} \u2014 path to plugin data storage`,
220
+ ``,
221
+ `Install location: ~/.claude/plugins/ or project-local .claude-plugin/`
222
+ );
223
+ if (inlineHandlers.size > 0) {
224
+ postInstall.push(
225
+ ``,
226
+ `NOTE: ${inlineHandlers.size} inline handler(s) require Node.js to execute.`,
227
+ ` Wrapper scripts are in hooks/*.js`
228
+ );
229
+ }
230
+ return {
231
+ files,
232
+ manifest: claudeManifest,
233
+ warnings,
234
+ issues: [],
235
+ postInstall: postInstall.length > 0 ? postInstall : void 0
236
+ };
237
+ }
238
+ // ─── Internal Helpers ──────────────────────────────────────────────────────
239
+ /**
240
+ * Build the Claude plugin.json manifest from a universal plugin manifest.
241
+ */
242
+ buildManifest(plugin) {
243
+ const manifest = {
244
+ name: plugin.name,
245
+ version: plugin.version,
246
+ description: plugin.description,
247
+ displayName: plugin.displayName,
248
+ author: plugin.author,
249
+ homepage: plugin.homepage,
250
+ repository: plugin.repository,
251
+ license: plugin.license,
252
+ keywords: plugin.keywords,
253
+ defaultEnabled: plugin.defaultEnabled ?? true,
254
+ root: "${CLAUDE_PLUGIN_ROOT}",
255
+ data: "${CLAUDE_PLUGIN_DATA}"
256
+ };
257
+ if (plugin.userConfig) {
258
+ manifest.userConfig = Object.fromEntries(
259
+ Object.entries(plugin.userConfig).map(([key, opt]) => [
260
+ key,
261
+ {
262
+ type: opt.type,
263
+ title: opt.title,
264
+ description: opt.description,
265
+ sensitive: opt.sensitive,
266
+ required: opt.required,
267
+ default: opt.default,
268
+ multiple: opt.multiple
269
+ }
270
+ ])
271
+ );
272
+ }
273
+ if (plugin.dependencies) {
274
+ manifest.dependencies = plugin.dependencies;
275
+ }
276
+ if (plugin.themes) {
277
+ manifest.themes = plugin.themes;
278
+ }
279
+ if (plugin.monitors) {
280
+ manifest.monitors = plugin.monitors;
281
+ }
282
+ if (plugin.lspServers) {
283
+ manifest.lspServers = plugin.lspServers;
284
+ }
285
+ return manifest;
286
+ }
287
+ /**
288
+ * Build the hooks.json configuration from universal hooks.
289
+ */
290
+ buildHooksConfig(plugin) {
291
+ if (!plugin.hooks) return [];
292
+ const entries = [];
293
+ for (const [name, def] of Object.entries(plugin.hooks)) {
294
+ const hookName = name;
295
+ if (!def) continue;
296
+ if (!this.supportedHooks.includes(hookName)) continue;
297
+ const claudeEvent = HOOK_NAME_MAP[hookName];
298
+ const handlerConfig = this.buildHandlerConfig(def, hookName);
299
+ if (handlerConfig) {
300
+ const entry = {
301
+ event: claudeEvent,
302
+ handler: handlerConfig
303
+ };
304
+ if (def.matcher) {
305
+ entry.matcher = def.matcher;
306
+ } else if (hookName === "preToolUse" || hookName === "postToolUse" || hookName === "postToolUseFailure") {
307
+ entry.matcher = "*";
308
+ }
309
+ entries.push(entry);
310
+ }
311
+ }
312
+ return entries;
313
+ }
314
+ /**
315
+ * Build a single handler configuration for hooks.json.
316
+ */
317
+ buildHandlerConfig(def, hookName) {
318
+ const handler = def.handler;
319
+ switch (handler.type) {
320
+ case "command": {
321
+ return {
322
+ type: "command",
323
+ command: handler.command,
324
+ shell: handler.shell,
325
+ statusMessage: handler.statusMessage
326
+ };
327
+ }
328
+ case "http": {
329
+ return {
330
+ type: "http",
331
+ url: handler.url,
332
+ headers: handler.headers
333
+ };
334
+ }
335
+ case "inline": {
336
+ const wrapperId = `__inline_${hookName}_${this.hashId(hookName)}`;
337
+ this.inlineWrappers.set(wrapperId, { hookName });
338
+ return {
339
+ type: "command",
340
+ command: `node "\${CLAUDE_PLUGIN_ROOT}/hooks/${wrapperId}.js"`,
341
+ shell: "bash",
342
+ statusMessage: `Running ${hookName} hook...`
343
+ };
344
+ }
345
+ default: {
346
+ const unknownHandler = handler;
347
+ if (unknownHandler.type === "mcp_tool") {
348
+ return {
349
+ type: "mcp_tool",
350
+ mcpTool: unknownHandler.mcpTool || unknownHandler.tool,
351
+ mcpServer: unknownHandler.mcpServer || unknownHandler.server
352
+ };
353
+ }
354
+ if (unknownHandler.type === "prompt") {
355
+ return {
356
+ type: "prompt",
357
+ prompt: unknownHandler.prompt
358
+ };
359
+ }
360
+ if (unknownHandler.type === "agent") {
361
+ return {
362
+ type: "agent",
363
+ agent: unknownHandler.agent
364
+ };
365
+ }
366
+ return null;
367
+ }
368
+ }
369
+ }
370
+ /**
371
+ * Build a SKILL.md file with YAML frontmatter from a universal skill definition.
372
+ */
373
+ buildSkillFile(skill) {
374
+ const frontmatter = {
375
+ name: skill.name,
376
+ description: skill.description
377
+ };
378
+ if (skill.filePath && !skill.content) {
379
+ frontmatter.source = skill.filePath;
380
+ }
381
+ const yamlLines = Object.entries(frontmatter).map(([key, value]) => {
382
+ if (typeof value === "string") {
383
+ if (value.includes(":") || value.includes("#") || value.includes('"')) {
384
+ return `${key}: "${value.replace(/"/g, '\\"')}"`;
385
+ }
386
+ return `${key}: ${value}`;
387
+ }
388
+ return `${key}: ${value}`;
389
+ }).join("\n");
390
+ const frontmatterBlock = `---
391
+ ${yamlLines}
392
+ ---
393
+ `;
394
+ if (skill.content) {
395
+ if (skill.content.trimStart().startsWith("---")) {
396
+ return skill.content;
397
+ }
398
+ return frontmatterBlock + "\n" + skill.content;
399
+ }
400
+ if (skill.filePath) {
401
+ return frontmatterBlock + `
402
+ # ${skill.name}
403
+
404
+ See [${skill.filePath}](${skill.filePath}) for the full skill documentation.
405
+ `;
406
+ }
407
+ return frontmatterBlock + `
408
+ # ${skill.name}
409
+
410
+ ${skill.description}
411
+ `;
412
+ }
413
+ /**
414
+ * Build the .mcp.json configuration from universal MCP server definitions.
415
+ */
416
+ buildMCPConfig(plugin) {
417
+ if (!plugin.mcpServers) return {};
418
+ const servers = {};
419
+ for (const [serverName, config] of Object.entries(plugin.mcpServers)) {
420
+ servers[serverName] = {
421
+ command: config.command,
422
+ args: config.args ?? [],
423
+ env: config.env ?? {},
424
+ // Claude uses 'stdio' by default
425
+ transport: config.transport ?? "stdio"
426
+ };
427
+ }
428
+ return { servers };
429
+ }
430
+ /**
431
+ * Extract inline handlers that need wrapping, returning a map suitable
432
+ * for generateHandlersModule().
433
+ */
434
+ extractInlineHandlers(plugin) {
435
+ const handlers = /* @__PURE__ */ new Map();
436
+ if (!plugin.hooks) return handlers;
437
+ for (const [name, def] of Object.entries(plugin.hooks)) {
438
+ const hookName = name;
439
+ if (!def || def.handler.type !== "inline") continue;
440
+ const inlineHandler = def.handler;
441
+ if (typeof inlineHandler.handler === "function") {
442
+ const wrapperId = `__inline_${hookName}_${this.hashId(hookName)}`;
443
+ handlers.set(wrapperId, inlineHandler.handler);
444
+ }
445
+ }
446
+ return handlers;
447
+ }
448
+ /**
449
+ * Build an environment setup script that exports CLAUDE_PLUGIN_ROOT and
450
+ * CLAUDE_PLUGIN_DATA for use by hook scripts.
451
+ */
452
+ buildEnvScript(plugin) {
453
+ if (!plugin.hooks) return null;
454
+ const hasCommandHandlers = Object.values(plugin.hooks).some(
455
+ (def) => def?.handler.type === "command" || def?.handler.type === "inline"
456
+ );
457
+ if (!hasCommandHandlers) return null;
458
+ return `#!/usr/bin/env bash
459
+ # AgentPlugins Auto-Generated Environment Setup for Claude Code
460
+ # Plugin: ${plugin.name}
461
+ # DO NOT EDIT \u2014 This file is regenerated on each build.
462
+
463
+ # Resolve plugin root relative to this script
464
+ export CLAUDE_PLUGIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
465
+ export CLAUDE_PLUGIN_DATA="\${CLAUDE_PLUGIN_ROOT}/.data"
466
+
467
+ # Ensure data directory exists
468
+ mkdir -p "\${CLAUDE_PLUGIN_DATA}"
469
+ `;
470
+ }
471
+ /**
472
+ * Generate a short hash for a hook name to use in wrapper script filenames.
473
+ */
474
+ hashId(input) {
475
+ let hash = 0;
476
+ for (let i = 0; i < input.length; i++) {
477
+ const char = input.charCodeAt(i);
478
+ hash = (hash << 5) - hash + char;
479
+ hash |= 0;
480
+ }
481
+ return Math.abs(hash).toString(36).slice(0, 6);
482
+ }
483
+ };
484
+ export {
485
+ createAdapter,
486
+ createClaudeAdapter,
487
+ createClaudeAdapter as default
488
+ };
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@agentplugins/adapter-claude",
3
+ "version": "0.1.0",
4
+ "description": "AgentPlugins platform adapter for Claude Code",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.cjs",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "tsup src/index.ts --format cjs,esm --dts",
17
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
18
+ "typecheck": "tsc --noEmit"
19
+ },
20
+ "dependencies": {
21
+ "@agentplugins/core": "workspace:*"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^20.0.0",
25
+ "tsup": "^8.0.0",
26
+ "typescript": "^5.5.0"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "README.md"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "license": "MIT"
36
+ }