@girardmedia/bootspring 1.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.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +255 -0
  3. package/agents/README.md +93 -0
  4. package/agents/api-expert/context.md +416 -0
  5. package/agents/architecture-expert/context.md +454 -0
  6. package/agents/backend-expert/context.md +483 -0
  7. package/agents/code-review-expert/context.md +365 -0
  8. package/agents/database-expert/context.md +250 -0
  9. package/agents/devops-expert/context.md +446 -0
  10. package/agents/frontend-expert/context.md +364 -0
  11. package/agents/index.js +140 -0
  12. package/agents/performance-expert/context.md +377 -0
  13. package/agents/security-expert/context.md +343 -0
  14. package/agents/testing-expert/context.md +414 -0
  15. package/agents/ui-ux-expert/context.md +448 -0
  16. package/agents/vercel-expert/context.md +426 -0
  17. package/bin/bootspring.js +310 -0
  18. package/cli/agent.js +337 -0
  19. package/cli/context.js +194 -0
  20. package/cli/dashboard.js +150 -0
  21. package/cli/generate.js +294 -0
  22. package/cli/init.js +410 -0
  23. package/cli/loop.js +421 -0
  24. package/cli/mcp.js +241 -0
  25. package/cli/memory.js +303 -0
  26. package/cli/orchestrator.js +400 -0
  27. package/cli/plugin.js +451 -0
  28. package/cli/quality.js +332 -0
  29. package/cli/skill.js +369 -0
  30. package/cli/task.js +628 -0
  31. package/cli/telemetry.js +114 -0
  32. package/cli/todo.js +614 -0
  33. package/cli/update.js +312 -0
  34. package/core/config.js +245 -0
  35. package/core/context.js +329 -0
  36. package/core/entitlements.js +209 -0
  37. package/core/index.js +43 -0
  38. package/core/policies.js +68 -0
  39. package/core/telemetry.js +247 -0
  40. package/core/utils.js +380 -0
  41. package/dashboard/server.js +818 -0
  42. package/docs/integrations/claude-code.md +42 -0
  43. package/docs/integrations/codex.md +42 -0
  44. package/docs/mcp-api-platform.md +102 -0
  45. package/generators/generate.js +598 -0
  46. package/generators/index.js +18 -0
  47. package/hooks/context-detector.js +177 -0
  48. package/hooks/index.js +35 -0
  49. package/hooks/prompt-enhancer.js +289 -0
  50. package/intelligence/git-memory.js +551 -0
  51. package/intelligence/index.js +59 -0
  52. package/intelligence/orchestrator.js +964 -0
  53. package/intelligence/prd.js +447 -0
  54. package/intelligence/recommendation-weights.json +18 -0
  55. package/intelligence/recommendations.js +234 -0
  56. package/mcp/capabilities.js +71 -0
  57. package/mcp/contracts/mcp-contract.v1.json +497 -0
  58. package/mcp/registry.js +213 -0
  59. package/mcp/response-formatter.js +462 -0
  60. package/mcp/server.js +99 -0
  61. package/mcp/tools/agent-tool.js +137 -0
  62. package/mcp/tools/capabilities-tool.js +54 -0
  63. package/mcp/tools/context-tool.js +49 -0
  64. package/mcp/tools/dashboard-tool.js +58 -0
  65. package/mcp/tools/generate-tool.js +46 -0
  66. package/mcp/tools/loop-tool.js +134 -0
  67. package/mcp/tools/memory-tool.js +180 -0
  68. package/mcp/tools/orchestrator-tool.js +232 -0
  69. package/mcp/tools/plugin-tool.js +76 -0
  70. package/mcp/tools/quality-tool.js +47 -0
  71. package/mcp/tools/skill-tool.js +233 -0
  72. package/mcp/tools/telemetry-tool.js +95 -0
  73. package/mcp/tools/todo-tool.js +133 -0
  74. package/package.json +98 -0
  75. package/plugins/index.js +141 -0
  76. package/quality/index.js +380 -0
  77. package/quality/lint-budgets.json +19 -0
  78. package/skills/index.js +787 -0
  79. package/skills/patterns/README.md +163 -0
  80. package/skills/patterns/api/route-handler.md +217 -0
  81. package/skills/patterns/api/server-action.md +249 -0
  82. package/skills/patterns/auth/clerk.md +132 -0
  83. package/skills/patterns/database/prisma.md +180 -0
  84. package/skills/patterns/payments/stripe.md +272 -0
  85. package/skills/patterns/security/validation.md +268 -0
  86. package/skills/patterns/testing/vitest.md +307 -0
  87. package/templates/bootspring.config.js +83 -0
  88. package/templates/mcp.json +9 -0
@@ -0,0 +1,233 @@
1
+ /**
2
+ * MCP skill tool module
3
+ */
4
+
5
+ function getToolDefinition() {
6
+ return {
7
+ name: 'bootspring_skill',
8
+ description: 'Search and retrieve code patterns/skills. Find battle-tested implementations.',
9
+ inputSchema: {
10
+ type: 'object',
11
+ properties: {
12
+ action: {
13
+ type: 'string',
14
+ enum: ['list', 'show', 'search', 'sync'],
15
+ description: 'Action: list all skills, show a skill, search, or sync remote external catalog'
16
+ },
17
+ name: {
18
+ type: 'string',
19
+ description: 'Skill name (e.g., auth/clerk, database/prisma)'
20
+ },
21
+ query: {
22
+ type: 'string',
23
+ description: 'Search query'
24
+ },
25
+ includeExternal: {
26
+ type: 'boolean',
27
+ description: 'Include external skills from skills/external'
28
+ },
29
+ limit: {
30
+ type: 'number',
31
+ description: 'Limit list/search result count (default: 50)'
32
+ },
33
+ summary: {
34
+ type: 'boolean',
35
+ description: 'For action=show, return concise summary instead of full content'
36
+ },
37
+ sections: {
38
+ type: 'string',
39
+ description: 'For action=show, comma-separated section keywords to include'
40
+ },
41
+ maxChars: {
42
+ type: 'number',
43
+ description: 'For action=show, cap returned content length'
44
+ },
45
+ mode: {
46
+ type: 'string',
47
+ enum: ['local', 'server'],
48
+ description: 'Optional entitlement mode override (default: local)'
49
+ },
50
+ entitled: {
51
+ type: 'boolean',
52
+ description: 'Optional entitlement override for external skills'
53
+ },
54
+ tier: {
55
+ type: 'string',
56
+ description: 'Optional tier override (free, pro, team, enterprise)'
57
+ },
58
+ manifestUrl: {
59
+ type: 'string',
60
+ description: 'For action=sync, remote manifest URL'
61
+ },
62
+ contentBaseUrl: {
63
+ type: 'string',
64
+ description: 'For action=sync, fallback base URL for SKILL.md files'
65
+ },
66
+ token: {
67
+ type: 'string',
68
+ description: 'For action=sync, optional bearer token'
69
+ },
70
+ cacheDir: {
71
+ type: 'string',
72
+ description: 'For action=sync, local cache directory override'
73
+ },
74
+ manifestPublicKey: {
75
+ type: 'string',
76
+ description: 'For action=sync, PEM public key for manifest signature verification'
77
+ },
78
+ requireManifestSignature: {
79
+ type: 'boolean',
80
+ description: 'For action=sync, require signature validation'
81
+ }
82
+ },
83
+ required: ['action']
84
+ }
85
+ };
86
+ }
87
+
88
+ function createHandler({ skills, entitlements, trackTelemetry }) {
89
+ return async (args) => {
90
+ const {
91
+ action,
92
+ name,
93
+ query,
94
+ includeExternal = false,
95
+ summary = false,
96
+ sections,
97
+ maxChars,
98
+ mode,
99
+ entitled,
100
+ tier,
101
+ manifestUrl,
102
+ contentBaseUrl,
103
+ token,
104
+ cacheDir,
105
+ manifestPublicKey,
106
+ requireManifestSignature
107
+ } = args;
108
+
109
+ const parsedLimit = Number(args.limit);
110
+ const limit = Number.isFinite(parsedLimit) && parsedLimit > 0 ? parsedLimit : 50;
111
+ const accessOptions = { mode, entitled, tier };
112
+
113
+ switch (action) {
114
+ case 'list': {
115
+ let skillIds = skills.listSkills({ includeExternal });
116
+ if (includeExternal) {
117
+ skillIds = entitlements.filterAccessibleSkills(skillIds, accessOptions).allowed;
118
+ }
119
+ const items = skillIds
120
+ .slice(0, limit)
121
+ .map(id => {
122
+ const metadata = skills.getSkillMetadata(id) || {};
123
+ return {
124
+ id,
125
+ name: metadata.name || id,
126
+ description: metadata.description || ''
127
+ };
128
+ });
129
+
130
+ return {
131
+ content: [{
132
+ type: 'text',
133
+ text: JSON.stringify(items, null, 2)
134
+ }]
135
+ };
136
+ }
137
+ case 'show': {
138
+ if (!name) throw new Error('Skill name required');
139
+ const metadata = skills.getSkillMetadata(name);
140
+ const targetSkillId = metadata?.id || name;
141
+ const access = entitlements.checkSkillAccess(targetSkillId, accessOptions);
142
+ if (!access.allowed) {
143
+ trackTelemetry('premium_prompted', {
144
+ capability: 'external_skill',
145
+ skillId: targetSkillId,
146
+ mode: access.context?.mode,
147
+ tier: access.context?.tier,
148
+ reason: access.code
149
+ });
150
+ throw new Error(access.reason);
151
+ }
152
+ const rawContent = skills.loadSkill(name, {
153
+ includeExternal: includeExternal || entitlements.isExternalSkill(targetSkillId)
154
+ });
155
+ if (!rawContent) throw new Error(`Skill not found: ${name}`);
156
+ const content = skills.formatSkillContent(rawContent, {
157
+ summary,
158
+ sections,
159
+ maxChars
160
+ });
161
+ if (entitlements.isExternalSkill(targetSkillId)) {
162
+ trackTelemetry('premium_unlocked', {
163
+ capability: 'external_skill',
164
+ skillId: targetSkillId,
165
+ mode: access.context?.mode,
166
+ tier: access.context?.tier,
167
+ reason: access.code
168
+ });
169
+ }
170
+
171
+ return {
172
+ content: [{
173
+ type: 'text',
174
+ text: JSON.stringify({
175
+ id: metadata?.id || name,
176
+ name: metadata?.name || name,
177
+ description: metadata?.description || '',
178
+ source: metadata?.source || 'built-in',
179
+ content
180
+ }, null, 2)
181
+ }]
182
+ };
183
+ }
184
+ case 'search': {
185
+ if (!query) throw new Error('Search query required');
186
+ let matches = skills.searchSkills(query, { includeExternal, limit });
187
+ if (includeExternal) {
188
+ matches = entitlements.filterAccessibleSkills(matches, accessOptions).allowed;
189
+ }
190
+ const items = matches
191
+ .map(id => {
192
+ const metadata = skills.getSkillMetadata(id) || {};
193
+ return {
194
+ id,
195
+ name: metadata.name || id,
196
+ description: metadata.description || '',
197
+ source: metadata.source || 'built-in'
198
+ };
199
+ });
200
+
201
+ return {
202
+ content: [{
203
+ type: 'text',
204
+ text: JSON.stringify(items, null, 2)
205
+ }]
206
+ };
207
+ }
208
+ case 'sync': {
209
+ const result = await skills.syncExternalCatalog({
210
+ manifestUrl,
211
+ contentBaseUrl,
212
+ token,
213
+ cacheDir,
214
+ manifestPublicKey,
215
+ requireManifestSignature
216
+ });
217
+ return {
218
+ content: [{
219
+ type: 'text',
220
+ text: JSON.stringify(result, null, 2)
221
+ }]
222
+ };
223
+ }
224
+ default:
225
+ throw new Error(`Unknown action: ${action}`);
226
+ }
227
+ };
228
+ }
229
+
230
+ module.exports = {
231
+ getToolDefinition,
232
+ createHandler
233
+ };
@@ -0,0 +1,95 @@
1
+ /**
2
+ * MCP telemetry tool module
3
+ */
4
+
5
+ function getToolDefinition() {
6
+ return {
7
+ name: 'bootspring_telemetry',
8
+ description: 'Inspect and upload Bootspring telemetry events.',
9
+ inputSchema: {
10
+ type: 'object',
11
+ properties: {
12
+ action: {
13
+ type: 'string',
14
+ enum: ['status', 'list', 'upload', 'clear'],
15
+ description: 'Action: status, list, upload, or clear'
16
+ },
17
+ event: {
18
+ type: 'string',
19
+ description: 'Optional event filter'
20
+ },
21
+ limit: {
22
+ type: 'number',
23
+ description: 'Optional event limit for list/upload'
24
+ },
25
+ endpoint: {
26
+ type: 'string',
27
+ description: 'Optional upload endpoint override'
28
+ },
29
+ token: {
30
+ type: 'string',
31
+ description: 'Optional upload bearer token override'
32
+ },
33
+ clearOnSuccess: {
34
+ type: 'boolean',
35
+ description: 'For action=upload, clear local events on success'
36
+ }
37
+ },
38
+ required: ['action']
39
+ }
40
+ };
41
+ }
42
+
43
+ function createHandler({ telemetry }) {
44
+ return async (args) => {
45
+ const { action, event, endpoint, token, clearOnSuccess } = args;
46
+ const limit = Number(args.limit);
47
+ const safeLimit = Number.isFinite(limit) && limit > 0 ? limit : undefined;
48
+
49
+ switch (action) {
50
+ case 'status':
51
+ return {
52
+ content: [{
53
+ type: 'text',
54
+ text: JSON.stringify(telemetry.getStatus(), null, 2)
55
+ }]
56
+ };
57
+ case 'list':
58
+ return {
59
+ content: [{
60
+ type: 'text',
61
+ text: JSON.stringify(telemetry.listEvents({ event, limit: safeLimit }), null, 2)
62
+ }]
63
+ };
64
+ case 'upload': {
65
+ const result = await telemetry.uploadEvents({
66
+ event,
67
+ limit: safeLimit,
68
+ endpoint,
69
+ token,
70
+ clearOnSuccess: Boolean(clearOnSuccess)
71
+ });
72
+ return {
73
+ content: [{
74
+ type: 'text',
75
+ text: JSON.stringify(result, null, 2)
76
+ }]
77
+ };
78
+ }
79
+ case 'clear':
80
+ return {
81
+ content: [{
82
+ type: 'text',
83
+ text: JSON.stringify(telemetry.clearEvents(), null, 2)
84
+ }]
85
+ };
86
+ default:
87
+ throw new Error(`Unknown action: ${action}`);
88
+ }
89
+ };
90
+ }
91
+
92
+ module.exports = {
93
+ getToolDefinition,
94
+ createHandler
95
+ };
@@ -0,0 +1,133 @@
1
+ /**
2
+ * MCP todo tool module
3
+ */
4
+
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+
8
+ function getToolDefinition() {
9
+ return {
10
+ name: 'bootspring_todo',
11
+ description: 'Manage project todo items. Add, list, complete, or remove tasks.',
12
+ inputSchema: {
13
+ type: 'object',
14
+ properties: {
15
+ action: {
16
+ type: 'string',
17
+ enum: ['list', 'add', 'done', 'remove', 'clear'],
18
+ description: 'Action: list, add, done (complete), remove, or clear'
19
+ },
20
+ text: {
21
+ type: 'string',
22
+ description: 'Todo text (when adding)'
23
+ },
24
+ index: {
25
+ type: 'number',
26
+ description: 'Todo index number (when completing or removing)'
27
+ }
28
+ },
29
+ required: ['action']
30
+ }
31
+ };
32
+ }
33
+
34
+ function createHandler({ config, todoModule, format }) {
35
+ return async (args) => {
36
+ const { action, text, index } = args;
37
+ const cfg = config.load();
38
+ const todoPath = path.join(cfg._projectRoot, cfg.paths?.todo || 'todo.md');
39
+
40
+ let content = '';
41
+ if (fs.existsSync(todoPath)) {
42
+ content = fs.readFileSync(todoPath, 'utf-8');
43
+ } else {
44
+ content = '# Todo List\n\n## Pending\n\n## Completed\n';
45
+ fs.writeFileSync(todoPath, content);
46
+ }
47
+
48
+ const todos = todoModule.parseTodos(content);
49
+
50
+ switch (action) {
51
+ case 'list': {
52
+ const pending = todos.filter(t => !t.completed);
53
+ const completed = todos.filter(t => t.completed);
54
+ return format.todoList(pending, completed);
55
+ }
56
+ case 'add': {
57
+ if (!text) return format.error('Todo text required', ['Provide text="your task"']);
58
+ const lines = content.split('\n');
59
+
60
+ let insertIndex = lines.length;
61
+ for (let i = 0; i < lines.length; i++) {
62
+ if (lines[i].match(/^##?\s*(Pending|In Progress|Todo)/i)) {
63
+ insertIndex = i + 1;
64
+ while (insertIndex < lines.length && lines[insertIndex].trim() === '') {
65
+ insertIndex++;
66
+ }
67
+ break;
68
+ }
69
+ }
70
+
71
+ lines.splice(insertIndex, 0, `- [ ] ${text}`);
72
+ fs.writeFileSync(todoPath, lines.join('\n'));
73
+
74
+ return format.success({
75
+ summary: `Added todo: "${text}"`,
76
+ hints: ['Use action=list to see all todos']
77
+ });
78
+ }
79
+ case 'done': {
80
+ if (index === undefined) return format.error('Todo index required', ['Provide index=0']);
81
+ const todo = todos.find(t => t.index === index);
82
+ if (!todo) return format.error(`Todo #${index} not found`, ['Use action=list to see valid indices']);
83
+
84
+ const lines = content.split('\n');
85
+ lines[todo.line] = lines[todo.line].replace('[ ]', '[x]');
86
+ fs.writeFileSync(todoPath, lines.join('\n'));
87
+
88
+ return format.success({
89
+ summary: `Completed: "${todo.text}" 🎉`,
90
+ hints: ['Use action=list to see remaining todos']
91
+ });
92
+ }
93
+ case 'remove': {
94
+ if (index === undefined) return format.error('Todo index required', ['Provide index=0']);
95
+ const todo = todos.find(t => t.index === index);
96
+ if (!todo) return format.error(`Todo #${index} not found`, ['Use action=list to see valid indices']);
97
+
98
+ const lines = content.split('\n');
99
+ lines.splice(todo.line, 1);
100
+ fs.writeFileSync(todoPath, lines.join('\n'));
101
+
102
+ return format.success({
103
+ summary: `Removed: "${todo.text}"`,
104
+ hints: ['Use action=list to see remaining todos']
105
+ });
106
+ }
107
+ case 'clear': {
108
+ const completed = todos.filter(t => t.completed);
109
+ const lines = content.split('\n');
110
+ const linesToRemove = completed.map(t => t.line).sort((a, b) => b - a);
111
+
112
+ for (const lineIndex of linesToRemove) {
113
+ lines.splice(lineIndex, 1);
114
+ }
115
+ fs.writeFileSync(todoPath, lines.join('\n'));
116
+
117
+ return format.success({
118
+ summary: `Cleared ${completed.length} completed todos`,
119
+ hints: completed.length > 0 ? ['Todo list is now clean!'] : ['No completed todos to clear']
120
+ });
121
+ }
122
+ default:
123
+ return format.error(`Unknown action: ${action}`, [
124
+ 'Valid actions: list, add, done, remove, clear'
125
+ ]);
126
+ }
127
+ };
128
+ }
129
+
130
+ module.exports = {
131
+ getToolDefinition,
132
+ createHandler
133
+ };
package/package.json ADDED
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "@girardmedia/bootspring",
3
+ "version": "1.1.0",
4
+ "description": "Development scaffolding with intelligence - AI-powered context, agents, and workflows for any MCP-compatible assistant",
5
+ "keywords": [
6
+ "ai",
7
+ "development",
8
+ "scaffolding",
9
+ "mcp",
10
+ "claude",
11
+ "agents",
12
+ "context",
13
+ "workflow",
14
+ "devtools"
15
+ ],
16
+ "author": "Bootspring",
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/bootspring/bootspring.git"
21
+ },
22
+ "homepage": "https://bootspring.com",
23
+ "bugs": {
24
+ "url": "https://github.com/bootspring/bootspring/issues"
25
+ },
26
+ "bin": {
27
+ "bootspring": "./bin/bootspring.js"
28
+ },
29
+ "main": "./core/index.js",
30
+ "exports": {
31
+ ".": "./core/index.js",
32
+ "./mcp": "./mcp/server.js",
33
+ "./hooks": "./hooks/index.js",
34
+ "./agents": "./agents/index.js",
35
+ "./skills": "./skills/index.js",
36
+ "./plugins": "./plugins/index.js",
37
+ "./intelligence": "./intelligence/index.js",
38
+ "./generators": "./generators/index.js",
39
+ "./quality": "./quality/index.js"
40
+ },
41
+ "files": [
42
+ "bin/",
43
+ "core/",
44
+ "cli/",
45
+ "mcp/",
46
+ "hooks/",
47
+ "agents/",
48
+ "skills/index.js",
49
+ "skills/patterns/",
50
+ "plugins/",
51
+ "dashboard/",
52
+ "quality/",
53
+ "templates/",
54
+ "docs/",
55
+ "intelligence/",
56
+ "generators/"
57
+ ],
58
+ "scripts": {
59
+ "start": "node bin/bootspring.js",
60
+ "dashboard": "node bin/bootspring.js dashboard",
61
+ "mcp": "node mcp/server.js",
62
+ "test": "jest",
63
+ "test:watch": "jest --watch",
64
+ "test:coverage": "jest --coverage",
65
+ "lint": "eslint .",
66
+ "lint:fix": "eslint . --fix",
67
+ "verify:lint-budget": "node scripts/check-lint-budgets.js",
68
+ "build:mcp-contract": "node scripts/export-mcp-contract.js",
69
+ "verify:mcp-contract": "node scripts/export-mcp-contract.js --check",
70
+ "verify:package": "node scripts/check-package-boundaries.js",
71
+ "prepublishOnly": "npm test && npm run lint --if-present && npm run verify:package && npm run verify:mcp-contract"
72
+ },
73
+ "devDependencies": {
74
+ "@eslint/js": "^9.39.2",
75
+ "eslint": "^9.39.2",
76
+ "globals": "^17.3.0",
77
+ "jest": "^29.7.0"
78
+ },
79
+ "dependencies": {
80
+ "ws": "^8.18.0",
81
+ "yaml": "^2.8.0"
82
+ },
83
+ "peerDependencies": {
84
+ "@modelcontextprotocol/sdk": "^1.0.0",
85
+ "zod": "^3.25.0"
86
+ },
87
+ "peerDependenciesMeta": {
88
+ "@modelcontextprotocol/sdk": {
89
+ "optional": true
90
+ },
91
+ "zod": {
92
+ "optional": true
93
+ }
94
+ },
95
+ "engines": {
96
+ "node": ">=18.0.0"
97
+ }
98
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Bootspring Plugins Module
3
+ * Plugin management and configuration
4
+ */
5
+
6
+ // Available plugins and their providers
7
+ const PLUGINS = {
8
+ auth: {
9
+ name: 'Authentication',
10
+ description: 'User authentication and session management',
11
+ providers: ['clerk', 'nextauth', 'auth0', 'supabase'],
12
+ defaultProvider: 'clerk',
13
+ features: ['social_login', 'email_password', 'organizations', 'mfa']
14
+ },
15
+ payments: {
16
+ name: 'Payments',
17
+ description: 'Payment processing and subscriptions',
18
+ providers: ['stripe', 'paddle', 'lemonsqueezy'],
19
+ defaultProvider: 'stripe',
20
+ features: ['checkout', 'subscriptions', 'webhooks', 'customer_portal']
21
+ },
22
+ database: {
23
+ name: 'Database',
24
+ description: 'Database ORM and management',
25
+ providers: ['prisma', 'drizzle', 'typeorm', 'mongoose'],
26
+ defaultProvider: 'prisma',
27
+ features: ['migrations', 'transactions', 'soft_deletes', 'seeding']
28
+ },
29
+ testing: {
30
+ name: 'Testing',
31
+ description: 'Testing framework and utilities',
32
+ providers: ['vitest', 'jest', 'playwright'],
33
+ defaultProvider: 'vitest',
34
+ features: ['unit', 'integration', 'e2e', 'coverage']
35
+ },
36
+ security: {
37
+ name: 'Security',
38
+ description: 'Security features and validation',
39
+ providers: ['default'],
40
+ defaultProvider: 'default',
41
+ features: ['input_validation', 'rate_limiting', 'csrf', 'headers']
42
+ },
43
+ ai: {
44
+ name: 'AI',
45
+ description: 'AI/LLM integration',
46
+ providers: ['anthropic', 'openai', 'google'],
47
+ defaultProvider: 'anthropic',
48
+ features: ['streaming', 'tool_use', 'structured_output', 'embeddings']
49
+ }
50
+ };
51
+
52
+ /**
53
+ * Get all available plugins
54
+ */
55
+ function getPlugins() {
56
+ return PLUGINS;
57
+ }
58
+
59
+ /**
60
+ * Get a specific plugin
61
+ */
62
+ function getPlugin(name) {
63
+ return PLUGINS[name] || null;
64
+ }
65
+
66
+ /**
67
+ * List plugin names
68
+ */
69
+ function listPlugins() {
70
+ return Object.keys(PLUGINS);
71
+ }
72
+
73
+ /**
74
+ * Get providers for a plugin
75
+ */
76
+ function getProviders(pluginName) {
77
+ const plugin = PLUGINS[pluginName];
78
+ return plugin ? plugin.providers : [];
79
+ }
80
+
81
+ /**
82
+ * Check if a provider is valid for a plugin
83
+ */
84
+ function isValidProvider(pluginName, providerName) {
85
+ const providers = getProviders(pluginName);
86
+ return providers.includes(providerName);
87
+ }
88
+
89
+ /**
90
+ * Get plugin configuration template
91
+ */
92
+ function getPluginConfig(pluginName, provider = null) {
93
+ const plugin = PLUGINS[pluginName];
94
+ if (!plugin) return null;
95
+
96
+ return {
97
+ enabled: true,
98
+ provider: provider || plugin.defaultProvider,
99
+ features: plugin.features
100
+ };
101
+ }
102
+
103
+ /**
104
+ * Validate plugin configuration
105
+ */
106
+ function validateConfig(config) {
107
+ const errors = [];
108
+
109
+ if (!config || typeof config !== 'object') {
110
+ return { valid: false, errors: ['Configuration must be an object'] };
111
+ }
112
+
113
+ for (const [pluginName, pluginConfig] of Object.entries(config)) {
114
+ if (!PLUGINS[pluginName]) {
115
+ errors.push(`Unknown plugin: ${pluginName}`);
116
+ continue;
117
+ }
118
+
119
+ if (pluginConfig.enabled && pluginConfig.provider) {
120
+ if (!isValidProvider(pluginName, pluginConfig.provider)) {
121
+ errors.push(`Invalid provider '${pluginConfig.provider}' for plugin '${pluginName}'`);
122
+ }
123
+ }
124
+ }
125
+
126
+ return {
127
+ valid: errors.length === 0,
128
+ errors
129
+ };
130
+ }
131
+
132
+ module.exports = {
133
+ PLUGINS,
134
+ getPlugins,
135
+ getPlugin,
136
+ listPlugins,
137
+ getProviders,
138
+ isValidProvider,
139
+ getPluginConfig,
140
+ validateConfig
141
+ };