@autonav/core 1.0.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 +115 -0
- package/dist/adapter/claude-adapter.d.ts +136 -0
- package/dist/adapter/claude-adapter.d.ts.map +1 -0
- package/dist/adapter/claude-adapter.js +340 -0
- package/dist/adapter/claude-adapter.js.map +1 -0
- package/dist/adapter/index.d.ts +7 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +7 -0
- package/dist/adapter/index.js.map +1 -0
- package/dist/cli/autonav.d.ts +11 -0
- package/dist/cli/autonav.d.ts.map +1 -0
- package/dist/cli/autonav.js +79 -0
- package/dist/cli/autonav.js.map +1 -0
- package/dist/cli/nav-chat.d.ts +3 -0
- package/dist/cli/nav-chat.d.ts.map +1 -0
- package/dist/cli/nav-chat.js +151 -0
- package/dist/cli/nav-chat.js.map +1 -0
- package/dist/cli/nav-init.d.ts +3 -0
- package/dist/cli/nav-init.d.ts.map +1 -0
- package/dist/cli/nav-init.js +366 -0
- package/dist/cli/nav-init.js.map +1 -0
- package/dist/cli/nav-query.d.ts +3 -0
- package/dist/cli/nav-query.d.ts.map +1 -0
- package/dist/cli/nav-query.js +217 -0
- package/dist/cli/nav-query.js.map +1 -0
- package/dist/conversation/App.d.ts +14 -0
- package/dist/conversation/App.d.ts.map +1 -0
- package/dist/conversation/App.js +229 -0
- package/dist/conversation/App.js.map +1 -0
- package/dist/conversation/index.d.ts +29 -0
- package/dist/conversation/index.d.ts.map +1 -0
- package/dist/conversation/index.js +44 -0
- package/dist/conversation/index.js.map +1 -0
- package/dist/conversation/prompts.d.ts +9 -0
- package/dist/conversation/prompts.d.ts.map +1 -0
- package/dist/conversation/prompts.js +47 -0
- package/dist/conversation/prompts.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/interview/App.d.ts +14 -0
- package/dist/interview/App.d.ts.map +1 -0
- package/dist/interview/App.js +159 -0
- package/dist/interview/App.js.map +1 -0
- package/dist/interview/index.d.ts +30 -0
- package/dist/interview/index.d.ts.map +1 -0
- package/dist/interview/index.js +52 -0
- package/dist/interview/index.js.map +1 -0
- package/dist/interview/prompts.d.ts +36 -0
- package/dist/interview/prompts.d.ts.map +1 -0
- package/dist/interview/prompts.js +116 -0
- package/dist/interview/prompts.js.map +1 -0
- package/dist/pack-installer/github.d.ts +41 -0
- package/dist/pack-installer/github.d.ts.map +1 -0
- package/dist/pack-installer/github.js +294 -0
- package/dist/pack-installer/github.js.map +1 -0
- package/dist/pack-installer/index.d.ts +57 -0
- package/dist/pack-installer/index.d.ts.map +1 -0
- package/dist/pack-installer/index.js +257 -0
- package/dist/pack-installer/index.js.map +1 -0
- package/dist/plugins/config-schema.d.ts +32 -0
- package/dist/plugins/config-schema.d.ts.map +1 -0
- package/dist/plugins/config-schema.js +26 -0
- package/dist/plugins/config-schema.js.map +1 -0
- package/dist/plugins/implementations/file-watcher/index.d.ts +110 -0
- package/dist/plugins/implementations/file-watcher/index.d.ts.map +1 -0
- package/dist/plugins/implementations/file-watcher/index.js +212 -0
- package/dist/plugins/implementations/file-watcher/index.js.map +1 -0
- package/dist/plugins/implementations/github/index.d.ts +201 -0
- package/dist/plugins/implementations/github/index.d.ts.map +1 -0
- package/dist/plugins/implementations/github/index.js +337 -0
- package/dist/plugins/implementations/github/index.js.map +1 -0
- package/dist/plugins/implementations/slack/index.d.ts +153 -0
- package/dist/plugins/implementations/slack/index.d.ts.map +1 -0
- package/dist/plugins/implementations/slack/index.js +221 -0
- package/dist/plugins/implementations/slack/index.js.map +1 -0
- package/dist/plugins/index.d.ts +23 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +35 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/plugin-manager.d.ts +66 -0
- package/dist/plugins/plugin-manager.d.ts.map +1 -0
- package/dist/plugins/plugin-manager.js +198 -0
- package/dist/plugins/plugin-manager.js.map +1 -0
- package/dist/plugins/types.d.ts +102 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +38 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/plugins/utils/security.d.ts +43 -0
- package/dist/plugins/utils/security.d.ts.map +1 -0
- package/dist/plugins/utils/security.js +115 -0
- package/dist/plugins/utils/security.js.map +1 -0
- package/dist/query-engine/index.d.ts +10 -0
- package/dist/query-engine/index.d.ts.map +1 -0
- package/dist/query-engine/index.js +10 -0
- package/dist/query-engine/index.js.map +1 -0
- package/dist/query-engine/navigator-loader.d.ts +37 -0
- package/dist/query-engine/navigator-loader.d.ts.map +1 -0
- package/dist/query-engine/navigator-loader.js +167 -0
- package/dist/query-engine/navigator-loader.js.map +1 -0
- package/dist/query-engine/output-formatter.d.ts +55 -0
- package/dist/query-engine/output-formatter.d.ts.map +1 -0
- package/dist/query-engine/output-formatter.js +165 -0
- package/dist/query-engine/output-formatter.js.map +1 -0
- package/dist/query-engine/response-validator.d.ts +55 -0
- package/dist/query-engine/response-validator.d.ts.map +1 -0
- package/dist/query-engine/response-validator.js +92 -0
- package/dist/query-engine/response-validator.js.map +1 -0
- package/dist/templates/.gitignore.template +23 -0
- package/dist/templates/CLAUDE-pack.md.template +32 -0
- package/dist/templates/CLAUDE.md.template +128 -0
- package/dist/templates/README.md.template +174 -0
- package/dist/templates/config-pack.json.template +16 -0
- package/dist/templates/config.json.template +11 -0
- package/dist/templates/index.d.ts +22 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +32 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/plugins.json.template +33 -0
- package/dist/templates/system-configuration.md.template +66 -0
- package/dist/tools/handler.d.ts +20 -0
- package/dist/tools/handler.d.ts.map +1 -0
- package/dist/tools/handler.js +202 -0
- package/dist/tools/handler.js.map +1 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +10 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/response.d.ts +30 -0
- package/dist/tools/response.d.ts.map +1 -0
- package/dist/tools/response.js +68 -0
- package/dist/tools/response.js.map +1 -0
- package/dist/tools/self-config.d.ts +54 -0
- package/dist/tools/self-config.d.ts.map +1 -0
- package/dist/tools/self-config.js +84 -0
- package/dist/tools/self-config.js.map +1 -0
- package/dist/validation/index.d.ts +19 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +91 -0
- package/dist/validation/index.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { WebClient, LogLevel } from '@slack/web-api';
|
|
3
|
+
import { assertNoCredentialsInText } from '../../utils/security.js';
|
|
4
|
+
// Configuration schema
|
|
5
|
+
export const SlackConfigSchema = z.object({
|
|
6
|
+
token: z.string().describe('Slack Bot Token (xoxb-...)'),
|
|
7
|
+
channels: z.array(z.string()).default([]).describe('Channel IDs to monitor'),
|
|
8
|
+
threadNotifications: z.boolean().default(true).describe('Reply in threads'),
|
|
9
|
+
summaryFrequency: z.enum(['realtime', 'hourly', 'daily']).default('daily'),
|
|
10
|
+
botUserId: z.string().optional().describe('Bot user ID (auto-populated)'),
|
|
11
|
+
});
|
|
12
|
+
// Event schema
|
|
13
|
+
export const SlackEventSchema = z.object({
|
|
14
|
+
type: z.enum(['message', 'mention', 'reaction']),
|
|
15
|
+
channel: z.string(),
|
|
16
|
+
user: z.string(),
|
|
17
|
+
text: z.string().optional(),
|
|
18
|
+
timestamp: z.string(),
|
|
19
|
+
threadTs: z.string().optional(),
|
|
20
|
+
});
|
|
21
|
+
// Action schema with validation
|
|
22
|
+
export const SlackActionSchema = z.object({
|
|
23
|
+
type: z.enum(['send-message', 'add-reaction', 'update-message']),
|
|
24
|
+
channel: z.string().min(1).max(255),
|
|
25
|
+
text: z.string().max(40000).optional(), // Slack's limit is 40,000 characters
|
|
26
|
+
threadTs: z.string().optional().describe('Thread timestamp for replies'),
|
|
27
|
+
reaction: z.string().max(100).optional().describe('Emoji for reactions'),
|
|
28
|
+
messageTs: z.string().optional().describe('Message timestamp for updates'),
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* Slack Integration Plugin
|
|
32
|
+
*
|
|
33
|
+
* Enables navigators to send and receive Slack messages.
|
|
34
|
+
* Supports channels, threads, mentions, and reactions.
|
|
35
|
+
*/
|
|
36
|
+
export class SlackPlugin {
|
|
37
|
+
name = 'slack';
|
|
38
|
+
version = '1.0.0';
|
|
39
|
+
description = 'Send and receive Slack messages';
|
|
40
|
+
configSchema = SlackConfigSchema;
|
|
41
|
+
eventSchema = SlackEventSchema;
|
|
42
|
+
actionSchema = SlackActionSchema;
|
|
43
|
+
config;
|
|
44
|
+
client;
|
|
45
|
+
lastCheckTimestamp = '0';
|
|
46
|
+
async initialize(config) {
|
|
47
|
+
this.config = config;
|
|
48
|
+
// Initialize Slack client
|
|
49
|
+
this.client = new WebClient(config.token, {
|
|
50
|
+
logLevel: LogLevel.WARN,
|
|
51
|
+
});
|
|
52
|
+
// Verify token and get bot user ID
|
|
53
|
+
try {
|
|
54
|
+
const authResult = await this.client.auth.test();
|
|
55
|
+
if (!authResult.ok) {
|
|
56
|
+
throw new Error('Slack authentication failed');
|
|
57
|
+
}
|
|
58
|
+
// Store bot user ID for filtering messages
|
|
59
|
+
this.config.botUserId = authResult.user_id;
|
|
60
|
+
// Verify channel access
|
|
61
|
+
for (const channel of config.channels) {
|
|
62
|
+
const info = await this.client.conversations.info({ channel });
|
|
63
|
+
if (!info.ok) {
|
|
64
|
+
throw new Error(`Cannot access channel: ${channel}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
throw new Error(`Slack initialization failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async listen() {
|
|
73
|
+
if (!this.client || !this.config) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
const events = [];
|
|
77
|
+
// Fetch messages from each channel since last check
|
|
78
|
+
for (const channel of this.config.channels) {
|
|
79
|
+
try {
|
|
80
|
+
const result = await this.client.conversations.history({
|
|
81
|
+
channel,
|
|
82
|
+
oldest: this.lastCheckTimestamp,
|
|
83
|
+
limit: 100,
|
|
84
|
+
});
|
|
85
|
+
if (!result.ok || !result.messages) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
// Filter out bot's own messages and convert to events
|
|
89
|
+
for (const message of result.messages) {
|
|
90
|
+
// Skip bot's own messages
|
|
91
|
+
if (message.user === this.config.botUserId) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
// Determine event type
|
|
95
|
+
const isMention = message.text?.includes(`<@${this.config.botUserId}>`);
|
|
96
|
+
const type = isMention ? 'mention' : 'message';
|
|
97
|
+
events.push({
|
|
98
|
+
type,
|
|
99
|
+
channel,
|
|
100
|
+
user: message.user || 'unknown',
|
|
101
|
+
text: message.text || '',
|
|
102
|
+
timestamp: message.ts || '',
|
|
103
|
+
threadTs: message.thread_ts,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
console.error(`Error fetching messages from ${channel}:`, error);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Update last check timestamp
|
|
112
|
+
if (events.length > 0) {
|
|
113
|
+
const latestTs = events.reduce((max, evt) => evt.timestamp > max ? evt.timestamp : max, this.lastCheckTimestamp);
|
|
114
|
+
this.lastCheckTimestamp = latestTs;
|
|
115
|
+
}
|
|
116
|
+
return events;
|
|
117
|
+
}
|
|
118
|
+
async execute(action) {
|
|
119
|
+
if (!this.client) {
|
|
120
|
+
throw new Error('Slack client not initialized');
|
|
121
|
+
}
|
|
122
|
+
switch (action.type) {
|
|
123
|
+
case 'send-message': {
|
|
124
|
+
if (!action.text) {
|
|
125
|
+
throw new Error('Message text required');
|
|
126
|
+
}
|
|
127
|
+
// Security: Ensure no credentials in message text
|
|
128
|
+
assertNoCredentialsInText(action.text, 'message text');
|
|
129
|
+
const result = await this.client.chat.postMessage({
|
|
130
|
+
channel: action.channel,
|
|
131
|
+
text: action.text,
|
|
132
|
+
thread_ts: action.threadTs,
|
|
133
|
+
});
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
case 'add-reaction': {
|
|
137
|
+
if (!action.reaction || !action.messageTs) {
|
|
138
|
+
throw new Error('Reaction and message timestamp required');
|
|
139
|
+
}
|
|
140
|
+
// Validate reaction emoji name (alphanumeric, hyphens, underscores only)
|
|
141
|
+
if (!/^[\w-]+$/.test(action.reaction)) {
|
|
142
|
+
throw new Error('Invalid reaction emoji name');
|
|
143
|
+
}
|
|
144
|
+
const result = await this.client.reactions.add({
|
|
145
|
+
channel: action.channel,
|
|
146
|
+
name: action.reaction,
|
|
147
|
+
timestamp: action.messageTs,
|
|
148
|
+
});
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
case 'update-message': {
|
|
152
|
+
if (!action.text || !action.messageTs) {
|
|
153
|
+
throw new Error('Message text and timestamp required for update');
|
|
154
|
+
}
|
|
155
|
+
// Security: Ensure no credentials in message text
|
|
156
|
+
assertNoCredentialsInText(action.text, 'message text');
|
|
157
|
+
const result = await this.client.chat.update({
|
|
158
|
+
channel: action.channel,
|
|
159
|
+
text: action.text,
|
|
160
|
+
ts: action.messageTs,
|
|
161
|
+
});
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
default:
|
|
165
|
+
throw new Error(`Unknown action type: ${action.type}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async updateConfig(updates) {
|
|
169
|
+
if (!this.config) {
|
|
170
|
+
throw new Error('Plugin not initialized');
|
|
171
|
+
}
|
|
172
|
+
const newConfig = { ...this.config, ...updates };
|
|
173
|
+
// Re-initialize if token changed
|
|
174
|
+
if (updates.token && updates.token !== this.config.token) {
|
|
175
|
+
await this.shutdown();
|
|
176
|
+
await this.initialize(newConfig);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
this.config = newConfig;
|
|
180
|
+
}
|
|
181
|
+
return newConfig;
|
|
182
|
+
}
|
|
183
|
+
getConfig() {
|
|
184
|
+
if (!this.config) {
|
|
185
|
+
throw new Error('Plugin not initialized');
|
|
186
|
+
}
|
|
187
|
+
return { ...this.config };
|
|
188
|
+
}
|
|
189
|
+
async healthCheck() {
|
|
190
|
+
if (!this.client) {
|
|
191
|
+
return {
|
|
192
|
+
healthy: false,
|
|
193
|
+
message: 'Client not initialized',
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const result = await this.client.auth.test();
|
|
198
|
+
return {
|
|
199
|
+
healthy: result.ok || false,
|
|
200
|
+
message: result.ok ? 'Connected to Slack' : 'Authentication failed',
|
|
201
|
+
metadata: {
|
|
202
|
+
team: result.team,
|
|
203
|
+
user: result.user,
|
|
204
|
+
botId: result.bot_id,
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
return {
|
|
210
|
+
healthy: false,
|
|
211
|
+
message: error instanceof Error ? error.message : String(error),
|
|
212
|
+
lastError: error instanceof Error ? error : undefined,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async shutdown() {
|
|
217
|
+
this.client = undefined;
|
|
218
|
+
this.lastCheckTimestamp = '0';
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/plugins/implementations/slack/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { PluginManager } from './plugin-manager.js';
|
|
2
|
+
import { SlackPlugin } from './implementations/slack/index.js';
|
|
3
|
+
import { GitHubPlugin } from './implementations/github/index.js';
|
|
4
|
+
import { FileWatcherPlugin } from './implementations/file-watcher/index.js';
|
|
5
|
+
export * from './types.js';
|
|
6
|
+
export * from './plugin-manager.js';
|
|
7
|
+
export * from './config-schema.js';
|
|
8
|
+
export { FileWatcherPlugin } from './implementations/file-watcher/index.js';
|
|
9
|
+
export { SlackPlugin } from './implementations/slack/index.js';
|
|
10
|
+
export { GitHubPlugin } from './implementations/github/index.js';
|
|
11
|
+
/**
|
|
12
|
+
* Registry of all available plugins.
|
|
13
|
+
* Extend this to add new built-in plugins.
|
|
14
|
+
*/
|
|
15
|
+
export declare const DEFAULT_PLUGINS: readonly [typeof FileWatcherPlugin, typeof SlackPlugin, typeof GitHubPlugin];
|
|
16
|
+
/**
|
|
17
|
+
* Create a plugin manager with all default plugins registered.
|
|
18
|
+
*
|
|
19
|
+
* @param configPath - Path to .claude/plugins.json
|
|
20
|
+
* @returns Configured PluginManager instance
|
|
21
|
+
*/
|
|
22
|
+
export declare function createPluginManager(configPath: string): PluginManager;
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAG5E,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AAGnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,eAAe,8EAIlB,CAAC;AAEX;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAQrE"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { PluginManager } from './plugin-manager.js';
|
|
2
|
+
import { SlackPlugin } from './implementations/slack/index.js';
|
|
3
|
+
import { GitHubPlugin } from './implementations/github/index.js';
|
|
4
|
+
import { FileWatcherPlugin } from './implementations/file-watcher/index.js';
|
|
5
|
+
// Export types
|
|
6
|
+
export * from './types.js';
|
|
7
|
+
export * from './plugin-manager.js';
|
|
8
|
+
export * from './config-schema.js';
|
|
9
|
+
// Export plugin implementations
|
|
10
|
+
export { FileWatcherPlugin } from './implementations/file-watcher/index.js';
|
|
11
|
+
export { SlackPlugin } from './implementations/slack/index.js';
|
|
12
|
+
export { GitHubPlugin } from './implementations/github/index.js';
|
|
13
|
+
/**
|
|
14
|
+
* Registry of all available plugins.
|
|
15
|
+
* Extend this to add new built-in plugins.
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_PLUGINS = [
|
|
18
|
+
FileWatcherPlugin,
|
|
19
|
+
SlackPlugin,
|
|
20
|
+
GitHubPlugin,
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* Create a plugin manager with all default plugins registered.
|
|
24
|
+
*
|
|
25
|
+
* @param configPath - Path to .claude/plugins.json
|
|
26
|
+
* @returns Configured PluginManager instance
|
|
27
|
+
*/
|
|
28
|
+
export function createPluginManager(configPath) {
|
|
29
|
+
const manager = new PluginManager(configPath);
|
|
30
|
+
for (const PluginClass of DEFAULT_PLUGINS) {
|
|
31
|
+
manager.register(new PluginClass());
|
|
32
|
+
}
|
|
33
|
+
return manager;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAE5E,eAAe;AACf,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AAEnC,gCAAgC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAEjE;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,iBAAiB;IACjB,WAAW;IACX,YAAY;CACJ,CAAC;AAEX;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAkB;IACpD,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,UAAU,CAAC,CAAC;IAE9C,KAAK,MAAM,WAAW,IAAI,eAAe,EAAE,CAAC;QAC1C,OAAO,CAAC,QAAQ,CAAC,IAAI,WAAW,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Plugin } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Schema for .claude/plugins.json
|
|
5
|
+
*/
|
|
6
|
+
export declare const PluginsConfigSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
7
|
+
enabled: z.ZodBoolean;
|
|
8
|
+
config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
config?: Record<string, unknown> | undefined;
|
|
12
|
+
}, {
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
config?: Record<string, unknown> | undefined;
|
|
15
|
+
}>>;
|
|
16
|
+
export type PluginsConfig = z.infer<typeof PluginsConfigSchema>;
|
|
17
|
+
/**
|
|
18
|
+
* Manages plugin lifecycle, loading, and coordination.
|
|
19
|
+
*/
|
|
20
|
+
export declare class PluginManager {
|
|
21
|
+
private plugins;
|
|
22
|
+
private configPath;
|
|
23
|
+
constructor(configPath: string);
|
|
24
|
+
/**
|
|
25
|
+
* Register a plugin with the manager.
|
|
26
|
+
* Does not initialize it yet.
|
|
27
|
+
*
|
|
28
|
+
* @param plugin - Plugin instance to register
|
|
29
|
+
*/
|
|
30
|
+
register(plugin: Plugin): void;
|
|
31
|
+
/**
|
|
32
|
+
* Load plugin configurations from .claude/plugins.json
|
|
33
|
+
* and initialize all enabled plugins.
|
|
34
|
+
*
|
|
35
|
+
* @param pluginsConfig - Parsed plugins configuration
|
|
36
|
+
*/
|
|
37
|
+
loadPlugins(pluginsConfig: PluginsConfig): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Get all enabled and initialized plugins.
|
|
40
|
+
*/
|
|
41
|
+
getEnabledPlugins(): Plugin[];
|
|
42
|
+
/**
|
|
43
|
+
* Get a specific plugin by name.
|
|
44
|
+
*/
|
|
45
|
+
getPlugin(name: string): Plugin | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Listen to all enabled plugins and gather events.
|
|
48
|
+
* Errors from individual plugins don't fail the entire listen operation.
|
|
49
|
+
* Plugins are queried in parallel for better performance.
|
|
50
|
+
*/
|
|
51
|
+
listenAll(): Promise<Map<string, unknown[]>>;
|
|
52
|
+
/**
|
|
53
|
+
* Update configuration for a specific plugin.
|
|
54
|
+
* Saves updated config back to .claude/plugins.json.
|
|
55
|
+
*/
|
|
56
|
+
updatePluginConfig(name: string, updates: Record<string, unknown>): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Shutdown all plugins gracefully.
|
|
59
|
+
*/
|
|
60
|
+
shutdownAll(): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Run health checks on all enabled plugins.
|
|
63
|
+
*/
|
|
64
|
+
healthCheckAll(): Promise<Map<string, any>>;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=plugin-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-manager.d.ts","sourceRoot":"","sources":["../../src/plugins/plugin-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAe,MAAM,YAAY,CAAC;AAGjD;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;GAM/B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAYhE;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA8C;IAC7D,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,EAAE,MAAM;IAI9B;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAY9B;;;;;OAKG;IACG,WAAW,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IA6C9D;;OAEG;IACH,iBAAiB,IAAI,MAAM,EAAE;IAM7B;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAO3C;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IAkDlD;;;OAGG;IACG,kBAAkB,CACtB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC;IA4ChB;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAelC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAmBlD"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { PluginError } from './types.js';
|
|
3
|
+
import { sanitizeError } from './utils/security.js';
|
|
4
|
+
/**
|
|
5
|
+
* Schema for .claude/plugins.json
|
|
6
|
+
*/
|
|
7
|
+
export const PluginsConfigSchema = z.record(z.string(), // Plugin name
|
|
8
|
+
z.object({
|
|
9
|
+
enabled: z.boolean(),
|
|
10
|
+
config: z.record(z.unknown()).optional(), // Plugin-specific config
|
|
11
|
+
}));
|
|
12
|
+
/**
|
|
13
|
+
* Manages plugin lifecycle, loading, and coordination.
|
|
14
|
+
*/
|
|
15
|
+
export class PluginManager {
|
|
16
|
+
plugins = new Map();
|
|
17
|
+
configPath;
|
|
18
|
+
constructor(configPath) {
|
|
19
|
+
this.configPath = configPath;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Register a plugin with the manager.
|
|
23
|
+
* Does not initialize it yet.
|
|
24
|
+
*
|
|
25
|
+
* @param plugin - Plugin instance to register
|
|
26
|
+
*/
|
|
27
|
+
register(plugin) {
|
|
28
|
+
if (this.plugins.has(plugin.name)) {
|
|
29
|
+
throw new Error(`Plugin "${plugin.name}" is already registered`);
|
|
30
|
+
}
|
|
31
|
+
this.plugins.set(plugin.name, {
|
|
32
|
+
plugin,
|
|
33
|
+
enabled: false,
|
|
34
|
+
initialized: false,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Load plugin configurations from .claude/plugins.json
|
|
39
|
+
* and initialize all enabled plugins.
|
|
40
|
+
*
|
|
41
|
+
* @param pluginsConfig - Parsed plugins configuration
|
|
42
|
+
*/
|
|
43
|
+
async loadPlugins(pluginsConfig) {
|
|
44
|
+
for (const [name, config] of Object.entries(pluginsConfig)) {
|
|
45
|
+
const registration = this.plugins.get(name);
|
|
46
|
+
if (!registration) {
|
|
47
|
+
console.warn(`Plugin "${name}" configured but not registered, skipping`);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (!config.enabled) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
// Validate config against plugin's schema
|
|
55
|
+
const validatedConfig = registration.plugin.configSchema.parse(config.config || {});
|
|
56
|
+
// Initialize plugin
|
|
57
|
+
await registration.plugin.initialize(validatedConfig);
|
|
58
|
+
registration.enabled = true;
|
|
59
|
+
registration.initialized = true;
|
|
60
|
+
registration.lastError = undefined;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
// Sanitize error message to prevent credential leakage
|
|
64
|
+
const safeMessage = sanitizeError(error instanceof Error ? error.message : String(error));
|
|
65
|
+
const pluginError = new PluginError(name, `Failed to load: ${safeMessage}`, error instanceof Error ? error : undefined);
|
|
66
|
+
registration.lastError = pluginError;
|
|
67
|
+
registration.enabled = false;
|
|
68
|
+
registration.initialized = false;
|
|
69
|
+
console.error(pluginError.message);
|
|
70
|
+
// Continue loading other plugins (fail-safe)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get all enabled and initialized plugins.
|
|
76
|
+
*/
|
|
77
|
+
getEnabledPlugins() {
|
|
78
|
+
return Array.from(this.plugins.values())
|
|
79
|
+
.filter(reg => reg.enabled && reg.initialized)
|
|
80
|
+
.map(reg => reg.plugin);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get a specific plugin by name.
|
|
84
|
+
*/
|
|
85
|
+
getPlugin(name) {
|
|
86
|
+
const registration = this.plugins.get(name);
|
|
87
|
+
return registration?.enabled && registration?.initialized
|
|
88
|
+
? registration.plugin
|
|
89
|
+
: undefined;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Listen to all enabled plugins and gather events.
|
|
93
|
+
* Errors from individual plugins don't fail the entire listen operation.
|
|
94
|
+
* Plugins are queried in parallel for better performance.
|
|
95
|
+
*/
|
|
96
|
+
async listenAll() {
|
|
97
|
+
const events = new Map();
|
|
98
|
+
// Get all enabled plugins
|
|
99
|
+
const enabledPlugins = Array.from(this.plugins.values()).filter((reg) => reg.enabled && reg.initialized);
|
|
100
|
+
// Listen to all plugins in parallel
|
|
101
|
+
const results = await Promise.allSettled(enabledPlugins.map(async (registration) => {
|
|
102
|
+
try {
|
|
103
|
+
const pluginEvents = await registration.plugin.listen();
|
|
104
|
+
return {
|
|
105
|
+
name: registration.plugin.name,
|
|
106
|
+
events: pluginEvents,
|
|
107
|
+
registration,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
// Sanitize error to prevent credential leakage
|
|
112
|
+
const safeMessage = sanitizeError(error instanceof Error ? error.message : String(error));
|
|
113
|
+
registration.lastError = new PluginError(registration.plugin.name, `Listen failed: ${safeMessage}`, error instanceof Error ? error : undefined);
|
|
114
|
+
console.error(registration.lastError.message);
|
|
115
|
+
return {
|
|
116
|
+
name: registration.plugin.name,
|
|
117
|
+
events: [],
|
|
118
|
+
registration,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}));
|
|
122
|
+
// Collect results
|
|
123
|
+
for (const result of results) {
|
|
124
|
+
if (result.status === 'fulfilled' && result.value.events.length > 0) {
|
|
125
|
+
events.set(result.value.name, result.value.events);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return events;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Update configuration for a specific plugin.
|
|
132
|
+
* Saves updated config back to .claude/plugins.json.
|
|
133
|
+
*/
|
|
134
|
+
async updatePluginConfig(name, updates) {
|
|
135
|
+
const registration = this.plugins.get(name);
|
|
136
|
+
if (!registration) {
|
|
137
|
+
throw new Error(`Plugin "${name}" not found`);
|
|
138
|
+
}
|
|
139
|
+
if (!registration.enabled || !registration.initialized) {
|
|
140
|
+
throw new Error(`Plugin "${name}" is not enabled or initialized`);
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const updatedConfig = await registration.plugin.updateConfig(updates);
|
|
144
|
+
// Read current plugins.json
|
|
145
|
+
const fs = await import('fs/promises');
|
|
146
|
+
const currentConfig = PluginsConfigSchema.parse(JSON.parse(await fs.readFile(this.configPath, 'utf-8')));
|
|
147
|
+
// Update specific plugin config
|
|
148
|
+
currentConfig[name] = {
|
|
149
|
+
enabled: true,
|
|
150
|
+
config: updatedConfig,
|
|
151
|
+
};
|
|
152
|
+
// Write back
|
|
153
|
+
await fs.writeFile(this.configPath, JSON.stringify(currentConfig, null, 2), 'utf-8');
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
// Sanitize error to prevent credential leakage
|
|
157
|
+
const safeMessage = sanitizeError(error instanceof Error ? error.message : String(error));
|
|
158
|
+
throw new PluginError(name, `Config update failed: ${safeMessage}`, error instanceof Error ? error : undefined);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Shutdown all plugins gracefully.
|
|
163
|
+
*/
|
|
164
|
+
async shutdownAll() {
|
|
165
|
+
for (const registration of this.plugins.values()) {
|
|
166
|
+
if (registration.initialized) {
|
|
167
|
+
try {
|
|
168
|
+
await registration.plugin.shutdown();
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.error(`Error shutting down plugin "${registration.plugin.name}":`, error);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Run health checks on all enabled plugins.
|
|
178
|
+
*/
|
|
179
|
+
async healthCheckAll() {
|
|
180
|
+
const statuses = new Map();
|
|
181
|
+
for (const registration of this.plugins.values()) {
|
|
182
|
+
if (registration.enabled && registration.initialized) {
|
|
183
|
+
try {
|
|
184
|
+
const status = await registration.plugin.healthCheck();
|
|
185
|
+
statuses.set(registration.plugin.name, status);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
statuses.set(registration.plugin.name, {
|
|
189
|
+
healthy: false,
|
|
190
|
+
message: error instanceof Error ? error.message : String(error),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return statuses;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=plugin-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-manager.js","sourceRoot":"","sources":["../../src/plugins/plugin-manager.ts"],"names":[],"mappings":"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"}
|