@girardmedia/bootspring 1.2.0 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -14
- package/bin/bootspring.js +166 -27
- package/cli/agent.js +189 -17
- package/cli/analyze.js +499 -0
- package/cli/audit.js +557 -0
- package/cli/auth.js +495 -38
- package/cli/billing.js +302 -0
- package/cli/build.js +695 -0
- package/cli/business.js +109 -26
- package/cli/checkpoint-utils.js +168 -0
- package/cli/checkpoint.js +639 -0
- package/cli/cloud-sync.js +447 -0
- package/cli/content.js +198 -0
- package/cli/context.js +1 -1
- package/cli/deploy.js +543 -0
- package/cli/fundraise.js +112 -50
- package/cli/github-cmd.js +435 -0
- package/cli/health.js +477 -0
- package/cli/init.js +84 -13
- package/cli/legal.js +107 -95
- package/cli/log.js +2 -2
- package/cli/loop.js +976 -73
- package/cli/manager.js +711 -0
- package/cli/metrics.js +480 -0
- package/cli/monitor.js +812 -0
- package/cli/onboard.js +521 -0
- package/cli/orchestrator.js +12 -24
- package/cli/prd.js +594 -0
- package/cli/preseed-start.js +1483 -0
- package/cli/preseed.js +2302 -0
- package/cli/project.js +436 -0
- package/cli/quality.js +233 -0
- package/cli/security.js +913 -0
- package/cli/seed.js +1441 -5
- package/cli/skill.js +273 -211
- package/cli/suggest.js +989 -0
- package/cli/switch.js +453 -0
- package/cli/visualize.js +527 -0
- package/cli/watch.js +769 -0
- package/cli/workspace.js +607 -0
- package/core/analyze-workflow.js +1134 -0
- package/core/api-client.js +535 -22
- package/core/audit-workflow.js +1350 -0
- package/core/build-orchestrator.js +480 -0
- package/core/build-state.js +577 -0
- package/core/checkpoint-engine.js +408 -0
- package/core/config.js +1109 -26
- package/core/context-loader.js +21 -1
- package/core/deploy-workflow.js +836 -0
- package/core/entitlements.js +93 -22
- package/core/github-sync.js +610 -0
- package/core/index.js +8 -1
- package/core/ingest.js +1111 -0
- package/core/metrics-engine.js +768 -0
- package/core/onboard-workflow.js +1007 -0
- package/core/preseed-workflow.js +934 -0
- package/core/preseed.js +1617 -0
- package/core/project-context.js +325 -0
- package/core/project-state.js +694 -0
- package/core/r2-sync.js +583 -0
- package/core/scaffold.js +525 -7
- package/core/session.js +258 -0
- package/core/task-extractor.js +758 -0
- package/core/telemetry.js +28 -6
- package/core/tier-enforcement.js +737 -0
- package/core/utils.js +38 -14
- package/generators/questionnaire.js +15 -12
- package/generators/sections/ai.js +7 -7
- package/generators/sections/content.js +300 -0
- package/generators/sections/index.js +3 -0
- package/generators/sections/plugins.js +7 -6
- package/generators/templates/build-planning.template.js +596 -0
- package/generators/templates/content.template.js +819 -0
- package/generators/templates/index.js +2 -1
- package/hooks/git-autopilot.js +1250 -0
- package/hooks/index.js +9 -0
- package/intelligence/agent-collab.js +2057 -0
- package/intelligence/auto-suggest.js +634 -0
- package/intelligence/content-gen.js +1589 -0
- package/intelligence/cross-project.js +1647 -0
- package/intelligence/index.js +184 -0
- package/intelligence/learning/insights.json +517 -7
- package/intelligence/learning/pattern-learner.js +1008 -14
- package/intelligence/memory/decision-tracker.js +1431 -31
- package/intelligence/memory/decisions.jsonl +0 -0
- package/intelligence/orchestrator.js +2896 -1
- package/intelligence/prd.js +92 -1
- package/intelligence/recommendation-weights.json +14 -2
- package/intelligence/recommendations.js +463 -9
- package/intelligence/workflow-composer.js +1451 -0
- package/marketplace/index.d.ts +324 -0
- package/marketplace/index.js +1921 -0
- package/mcp/contracts/mcp-contract.v1.json +342 -4
- package/mcp/registry.js +680 -3
- package/mcp/response-formatter.js +23 -0
- package/mcp/tools/assist-tool.js +78 -4
- package/mcp/tools/autopilot-tool.js +408 -0
- package/mcp/tools/content-tool.js +571 -0
- package/mcp/tools/dashboard-tool.js +251 -5
- package/mcp/tools/mvp-tool.js +344 -0
- package/mcp/tools/plugin-tool.js +23 -1
- package/mcp/tools/prd-tool.js +579 -0
- package/mcp/tools/seed-tool.js +447 -0
- package/mcp/tools/skill-tool.js +43 -14
- package/mcp/tools/suggest-tool.js +147 -0
- package/package.json +15 -6
- package/agents/README.md +0 -93
- package/agents/ai-integration-expert/context.md +0 -386
- package/agents/api-expert/context.md +0 -416
- package/agents/architecture-expert/context.md +0 -454
- package/agents/auth-expert/context.md +0 -399
- package/agents/backend-expert/context.md +0 -483
- package/agents/business-strategy-expert/context.md +0 -180
- package/agents/code-review-expert/context.md +0 -365
- package/agents/competitive-analysis-expert/context.md +0 -239
- package/agents/data-modeling-expert/context.md +0 -352
- package/agents/database-expert/context.md +0 -250
- package/agents/devops-expert/context.md +0 -446
- package/agents/email-expert/context.md +0 -379
- package/agents/financial-expert/context.md +0 -213
- package/agents/frontend-expert/context.md +0 -364
- package/agents/fundraising-expert/context.md +0 -257
- package/agents/growth-expert/context.md +0 -249
- package/agents/index.js +0 -140
- package/agents/investor-relations-expert/context.md +0 -266
- package/agents/legal-expert/context.md +0 -284
- package/agents/marketing-expert/context.md +0 -236
- package/agents/monitoring-expert/context.md +0 -362
- package/agents/operations-expert/context.md +0 -279
- package/agents/partnerships-expert/context.md +0 -286
- package/agents/payment-expert/context.md +0 -340
- package/agents/performance-expert/context.md +0 -377
- package/agents/private-equity-expert/context.md +0 -246
- package/agents/railway-expert/context.md +0 -284
- package/agents/research-expert/context.md +0 -245
- package/agents/sales-expert/context.md +0 -241
- package/agents/security-expert/context.md +0 -343
- package/agents/testing-expert/context.md +0 -414
- package/agents/ui-ux-expert/context.md +0 -448
- package/agents/vercel-expert/context.md +0 -426
- package/skills/index.js +0 -787
- package/skills/patterns/README.md +0 -163
- package/skills/patterns/ai/agents.md +0 -281
- package/skills/patterns/ai/claude.md +0 -138
- package/skills/patterns/ai/embeddings.md +0 -150
- package/skills/patterns/ai/rag.md +0 -266
- package/skills/patterns/ai/streaming.md +0 -170
- package/skills/patterns/ai/structured-output.md +0 -162
- package/skills/patterns/ai/tools.md +0 -154
- package/skills/patterns/analytics/tracking.md +0 -220
- package/skills/patterns/api/errors.md +0 -296
- package/skills/patterns/api/graphql.md +0 -440
- package/skills/patterns/api/middleware.md +0 -279
- package/skills/patterns/api/openapi.md +0 -285
- package/skills/patterns/api/rate-limiting.md +0 -231
- package/skills/patterns/api/route-handler.md +0 -217
- package/skills/patterns/api/server-action.md +0 -249
- package/skills/patterns/api/versioning.md +0 -443
- package/skills/patterns/api/webhooks.md +0 -247
- package/skills/patterns/auth/clerk.md +0 -132
- package/skills/patterns/auth/mfa.md +0 -313
- package/skills/patterns/auth/nextauth.md +0 -140
- package/skills/patterns/auth/oauth.md +0 -237
- package/skills/patterns/auth/rbac.md +0 -152
- package/skills/patterns/auth/session-management.md +0 -367
- package/skills/patterns/auth/session.md +0 -120
- package/skills/patterns/database/audit.md +0 -177
- package/skills/patterns/database/migrations.md +0 -177
- package/skills/patterns/database/pagination.md +0 -230
- package/skills/patterns/database/pooling.md +0 -357
- package/skills/patterns/database/prisma.md +0 -180
- package/skills/patterns/database/relations.md +0 -187
- package/skills/patterns/database/seeding.md +0 -246
- package/skills/patterns/database/soft-delete.md +0 -153
- package/skills/patterns/database/transactions.md +0 -162
- package/skills/patterns/deployment/ci-cd.md +0 -231
- package/skills/patterns/deployment/docker.md +0 -188
- package/skills/patterns/deployment/monitoring.md +0 -387
- package/skills/patterns/deployment/vercel.md +0 -160
- package/skills/patterns/email/resend.md +0 -143
- package/skills/patterns/email/templates.md +0 -245
- package/skills/patterns/email/transactional.md +0 -503
- package/skills/patterns/email/verification.md +0 -176
- package/skills/patterns/files/download.md +0 -243
- package/skills/patterns/files/upload.md +0 -239
- package/skills/patterns/i18n/nextintl.md +0 -188
- package/skills/patterns/logging/structured.md +0 -292
- package/skills/patterns/notifications/email-queue.md +0 -248
- package/skills/patterns/notifications/push.md +0 -279
- package/skills/patterns/payments/checkout.md +0 -303
- package/skills/patterns/payments/invoices.md +0 -287
- package/skills/patterns/payments/portal.md +0 -245
- package/skills/patterns/payments/stripe.md +0 -272
- package/skills/patterns/payments/subscriptions.md +0 -300
- package/skills/patterns/payments/usage.md +0 -279
- package/skills/patterns/performance/caching.md +0 -276
- package/skills/patterns/performance/code-splitting.md +0 -233
- package/skills/patterns/performance/edge.md +0 -254
- package/skills/patterns/performance/isr.md +0 -266
- package/skills/patterns/performance/lazy-loading.md +0 -281
- package/skills/patterns/realtime/sse.md +0 -327
- package/skills/patterns/realtime/websockets.md +0 -336
- package/skills/patterns/search/filtering.md +0 -329
- package/skills/patterns/search/fulltext.md +0 -260
- package/skills/patterns/security/audit-logging.md +0 -444
- package/skills/patterns/security/csrf.md +0 -234
- package/skills/patterns/security/headers.md +0 -252
- package/skills/patterns/security/sanitization.md +0 -258
- package/skills/patterns/security/secrets.md +0 -261
- package/skills/patterns/security/validation.md +0 -268
- package/skills/patterns/security/xss.md +0 -229
- package/skills/patterns/seo/metadata.md +0 -252
- package/skills/patterns/state/context.md +0 -349
- package/skills/patterns/state/react-query.md +0 -313
- package/skills/patterns/state/url-state.md +0 -482
- package/skills/patterns/state/zustand.md +0 -262
- package/skills/patterns/testing/api.md +0 -259
- package/skills/patterns/testing/component.md +0 -233
- package/skills/patterns/testing/coverage.md +0 -207
- package/skills/patterns/testing/fixtures.md +0 -225
- package/skills/patterns/testing/integration.md +0 -436
- package/skills/patterns/testing/mocking.md +0 -177
- package/skills/patterns/testing/playwright.md +0 -162
- package/skills/patterns/testing/snapshot.md +0 -175
- package/skills/patterns/testing/vitest.md +0 -307
- package/skills/patterns/ui/accordions.md +0 -395
- package/skills/patterns/ui/cards.md +0 -299
- package/skills/patterns/ui/dropdowns.md +0 -476
- package/skills/patterns/ui/empty-states.md +0 -320
- package/skills/patterns/ui/forms.md +0 -405
- package/skills/patterns/ui/inputs.md +0 -319
- package/skills/patterns/ui/layouts.md +0 -282
- package/skills/patterns/ui/loading.md +0 -291
- package/skills/patterns/ui/modals.md +0 -338
- package/skills/patterns/ui/navigation.md +0 -374
- package/skills/patterns/ui/tables.md +0 -407
- package/skills/patterns/ui/toasts.md +0 -300
- package/skills/patterns/ui/tooltips.md +0 -396
- package/skills/patterns/utils/dates.md +0 -435
- package/skills/patterns/utils/errors.md +0 -451
- package/skills/patterns/utils/formatting.md +0 -345
- package/skills/patterns/utils/validation.md +0 -434
- package/templates/bootspring.config.js +0 -83
- package/templates/business/business-model-canvas.md +0 -246
- package/templates/business/business-plan.md +0 -266
- package/templates/business/competitive-analysis.md +0 -312
- package/templates/fundraising/data-room-checklist.md +0 -300
- package/templates/fundraising/investor-research.md +0 -243
- package/templates/fundraising/pitch-deck-outline.md +0 -253
- package/templates/legal/gdpr-checklist.md +0 -339
- package/templates/legal/privacy-policy.md +0 -285
- package/templates/legal/terms-of-service.md +0 -222
- package/templates/mcp.json +0 -9
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP seed tool module
|
|
3
|
+
* Provides seed operations via MCP: setup, init, generate, scaffold, status, export
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
function getToolDefinition() {
|
|
7
|
+
return {
|
|
8
|
+
name: 'bootspring_seed',
|
|
9
|
+
description: 'Manage seed configuration and project scaffolding. Setup input folders, run questionnaire, ingest files, generate documents, and scaffold projects.',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
action: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
enum: ['setup', 'init', 'generate', 'scaffold', 'status', 'export'],
|
|
16
|
+
description: 'Action to perform: setup (create input folders), init (run questionnaire), generate (ingest files and generate docs), scaffold (create project structure), status (show config), export (export as JSON/YAML)'
|
|
17
|
+
},
|
|
18
|
+
preset: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Preset for init (minimal, standard, full, startup, api) or scaffold (nextjs, react, node, fullstack)'
|
|
21
|
+
},
|
|
22
|
+
format: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
enum: ['json', 'yaml'],
|
|
25
|
+
description: 'Export format (for export action)'
|
|
26
|
+
},
|
|
27
|
+
dryRun: {
|
|
28
|
+
type: 'boolean',
|
|
29
|
+
description: 'For scaffold action: show plan without creating files'
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
required: ['action']
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function createHandler({ ingestModule, scaffoldModule, config, format }) {
|
|
38
|
+
return async (args) => {
|
|
39
|
+
const { action, preset, dryRun } = args;
|
|
40
|
+
const cfg = config.load();
|
|
41
|
+
const projectRoot = cfg._projectRoot;
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
switch (action) {
|
|
45
|
+
case 'setup':
|
|
46
|
+
return await handleSetup(projectRoot, format);
|
|
47
|
+
|
|
48
|
+
case 'init':
|
|
49
|
+
return format.warning(
|
|
50
|
+
'Interactive questionnaire not available via MCP',
|
|
51
|
+
['Run "bootspring seed init" in terminal for interactive mode']
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
case 'generate':
|
|
55
|
+
return await handleGenerate(projectRoot, ingestModule, format);
|
|
56
|
+
|
|
57
|
+
case 'scaffold':
|
|
58
|
+
return await handleScaffold(projectRoot, preset, dryRun, scaffoldModule, config, format);
|
|
59
|
+
|
|
60
|
+
case 'status':
|
|
61
|
+
return await handleStatus(projectRoot, config, scaffoldModule, format);
|
|
62
|
+
|
|
63
|
+
case 'export':
|
|
64
|
+
return await handleExport(projectRoot, args.format || 'json', format);
|
|
65
|
+
|
|
66
|
+
default:
|
|
67
|
+
return format.error(`Unknown action: ${action}`, [
|
|
68
|
+
'Valid actions: setup, init, generate, scaffold, status, export'
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return format.error(`Seed operation failed: ${error.message}`);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function handleSetup(projectRoot, _format) {
|
|
78
|
+
const fs = require('fs');
|
|
79
|
+
const path = require('path');
|
|
80
|
+
|
|
81
|
+
const folders = [
|
|
82
|
+
'.bootspring',
|
|
83
|
+
'.bootspring/inputs',
|
|
84
|
+
'.bootspring/inputs/mvp',
|
|
85
|
+
'.bootspring/inputs/mvp/source',
|
|
86
|
+
'.bootspring/inputs/business',
|
|
87
|
+
'.bootspring/inputs/prd',
|
|
88
|
+
'.bootspring/inputs/prd/features',
|
|
89
|
+
'.bootspring/inputs/designs',
|
|
90
|
+
'.bootspring/inputs/designs/figma-exports',
|
|
91
|
+
'.bootspring/inputs/designs/wireframes',
|
|
92
|
+
'.bootspring/inputs/legal',
|
|
93
|
+
'.bootspring/inputs/api',
|
|
94
|
+
'.bootspring/inputs/data',
|
|
95
|
+
'.bootspring/generated',
|
|
96
|
+
'.bootspring/generated/prd',
|
|
97
|
+
'.bootspring/generated/master-plan',
|
|
98
|
+
'.bootspring/generated/business',
|
|
99
|
+
'.bootspring/generated/architecture',
|
|
100
|
+
'.bootspring/generated/legal',
|
|
101
|
+
'.bootspring/context',
|
|
102
|
+
'.bootspring/logs',
|
|
103
|
+
'.bootspring/config'
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
let created = 0;
|
|
107
|
+
for (const folder of folders) {
|
|
108
|
+
const folderPath = path.join(projectRoot, folder);
|
|
109
|
+
if (!fs.existsSync(folderPath)) {
|
|
110
|
+
fs.mkdirSync(folderPath, { recursive: true });
|
|
111
|
+
created++;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const inputReadme = `# Input Files
|
|
116
|
+
|
|
117
|
+
Drop your existing files here before running \`bootspring seed generate\`.
|
|
118
|
+
|
|
119
|
+
| Folder | What to put here |
|
|
120
|
+
|--------|------------------|
|
|
121
|
+
| \`mvp/source/\` | AI-generated MVP code (Lovable, Bolt, V0, etc.) |
|
|
122
|
+
| \`business/\` | Business plans, pitch decks, market research |
|
|
123
|
+
| \`prd/\` | Product requirements documents |
|
|
124
|
+
| \`designs/\` | Figma exports, wireframes, mockups |
|
|
125
|
+
| \`legal/\` | Existing terms, privacy policies |
|
|
126
|
+
| \`api/\` | OpenAPI specs, API documentation |
|
|
127
|
+
| \`data/\` | Sample data, database schemas |
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
const readmePath = path.join(projectRoot, '.bootspring/inputs/README.md');
|
|
131
|
+
if (!fs.existsSync(readmePath)) {
|
|
132
|
+
fs.writeFileSync(readmePath, inputReadme);
|
|
133
|
+
created++;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
content: [{
|
|
138
|
+
type: 'text',
|
|
139
|
+
text: `## Seed Setup Complete
|
|
140
|
+
|
|
141
|
+
Created ${created} folders/files.
|
|
142
|
+
|
|
143
|
+
**Folder Structure:**
|
|
144
|
+
\`\`\`
|
|
145
|
+
.bootspring/
|
|
146
|
+
├── inputs/
|
|
147
|
+
│ ├── mvp/source/ # AI-generated MVP code
|
|
148
|
+
│ ├── business/ # Business plans, pitch decks
|
|
149
|
+
│ ├── prd/ # Product requirements
|
|
150
|
+
│ ├── designs/ # Figma exports, wireframes
|
|
151
|
+
│ ├── legal/ # Terms, privacy policies
|
|
152
|
+
│ ├── api/ # OpenAPI specs
|
|
153
|
+
│ └── data/ # Sample data, schemas
|
|
154
|
+
├── generated/ # Bootspring output
|
|
155
|
+
├── context/ # AI context index
|
|
156
|
+
└── logs/ # Action history
|
|
157
|
+
\`\`\`
|
|
158
|
+
|
|
159
|
+
**Next Steps:**
|
|
160
|
+
1. Drop your files in \`.bootspring/inputs/\` subfolders
|
|
161
|
+
2. Run \`bootspring_seed\` with action: "generate" to process them`
|
|
162
|
+
}]
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function handleGenerate(projectRoot, ingestModule, format) {
|
|
167
|
+
const fs = require('fs');
|
|
168
|
+
const path = require('path');
|
|
169
|
+
|
|
170
|
+
const inputsDir = path.join(projectRoot, '.bootspring', 'inputs');
|
|
171
|
+
if (!fs.existsSync(inputsDir)) {
|
|
172
|
+
return format.warning(
|
|
173
|
+
'No .bootspring/inputs folder found',
|
|
174
|
+
['Run bootspring_seed with action: "setup" first']
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Ingest all files
|
|
179
|
+
const ingested = await ingestModule.ingestAll(projectRoot);
|
|
180
|
+
|
|
181
|
+
// Count files found
|
|
182
|
+
const counts = {
|
|
183
|
+
mvp: ingested.mvp?.files?.length || 0,
|
|
184
|
+
business: ingested.business?.length || 0,
|
|
185
|
+
prd: ingested.prd?.length || 0,
|
|
186
|
+
designs: ingested.designs?.files?.length || 0,
|
|
187
|
+
api: ingested.api?.length || 0,
|
|
188
|
+
data: ingested.data?.length || 0
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const totalFiles = Object.values(counts).reduce((a, b) => a + b, 0);
|
|
192
|
+
|
|
193
|
+
// Parse SEED.md if exists
|
|
194
|
+
const seedPath = path.join(projectRoot, 'SEED.md');
|
|
195
|
+
const seedConfig = {};
|
|
196
|
+
if (fs.existsSync(seedPath)) {
|
|
197
|
+
try {
|
|
198
|
+
const content = fs.readFileSync(seedPath, 'utf-8');
|
|
199
|
+
const yamlBlocks = content.matchAll(/```yaml\n([\s\S]*?)```/g);
|
|
200
|
+
for (const match of yamlBlocks) {
|
|
201
|
+
try {
|
|
202
|
+
const yaml = require('yaml');
|
|
203
|
+
const parsed = yaml.parse(match[1]);
|
|
204
|
+
Object.assign(seedConfig, parsed);
|
|
205
|
+
} catch {
|
|
206
|
+
// Skip invalid blocks
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} catch {
|
|
210
|
+
// Continue without seed config
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Generate documents
|
|
215
|
+
const outputs = await ingestModule.generateDocuments(ingested, seedConfig, projectRoot);
|
|
216
|
+
|
|
217
|
+
// Update context index
|
|
218
|
+
await ingestModule.updateContextIndex(projectRoot, ingested);
|
|
219
|
+
|
|
220
|
+
const generatedDocs = Object.keys(outputs).filter(k => outputs[k]).length;
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
content: [{
|
|
224
|
+
type: 'text',
|
|
225
|
+
text: `## Files Ingested
|
|
226
|
+
|
|
227
|
+
| Category | Files Found |
|
|
228
|
+
|----------|-------------|
|
|
229
|
+
| MVP Code | ${counts.mvp} |
|
|
230
|
+
| Business Docs | ${counts.business} |
|
|
231
|
+
| PRD Docs | ${counts.prd} |
|
|
232
|
+
| Design Files | ${counts.designs} |
|
|
233
|
+
| API Specs | ${counts.api} |
|
|
234
|
+
| Data Files | ${counts.data} |
|
|
235
|
+
| **Total** | **${totalFiles}** |
|
|
236
|
+
|
|
237
|
+
## Documents Generated
|
|
238
|
+
|
|
239
|
+
Generated ${generatedDocs} documents in \`.bootspring/generated/\`
|
|
240
|
+
|
|
241
|
+
**MVP Patterns Detected:**
|
|
242
|
+
${ingested.mvp?.patterns?.join(', ') || 'None'}
|
|
243
|
+
|
|
244
|
+
**Next Steps:**
|
|
245
|
+
1. Review generated documents in \`.bootspring/generated/\`
|
|
246
|
+
2. Run \`bootspring_seed\` with action: "scaffold" to create project structure`
|
|
247
|
+
}]
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function handleScaffold(projectRoot, preset, dryRun, scaffoldModule, config, format) {
|
|
252
|
+
const fs = require('fs');
|
|
253
|
+
const path = require('path');
|
|
254
|
+
|
|
255
|
+
let scaffoldConfig;
|
|
256
|
+
|
|
257
|
+
if (preset) {
|
|
258
|
+
const validPresets = scaffoldModule.getPresets();
|
|
259
|
+
if (!validPresets.includes(preset)) {
|
|
260
|
+
return format.error(`Invalid preset: ${preset}`, [
|
|
261
|
+
`Valid presets: ${validPresets.join(', ')}`
|
|
262
|
+
]);
|
|
263
|
+
}
|
|
264
|
+
scaffoldConfig = scaffoldModule.getPresetConfig(preset);
|
|
265
|
+
} else {
|
|
266
|
+
// Try SEED.md
|
|
267
|
+
const seedPath = path.join(projectRoot, 'SEED.md');
|
|
268
|
+
if (!fs.existsSync(seedPath)) {
|
|
269
|
+
const presets = scaffoldModule.getPresets();
|
|
270
|
+
return format.warning(
|
|
271
|
+
'No SEED.md found',
|
|
272
|
+
[`Use preset parameter: ${presets.join(', ')}`, 'Or run "bootspring seed init" first']
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Parse SEED.md
|
|
277
|
+
const content = fs.readFileSync(seedPath, 'utf-8');
|
|
278
|
+
scaffoldConfig = {};
|
|
279
|
+
const yamlBlocks = content.matchAll(/```yaml\n([\s\S]*?)```/g);
|
|
280
|
+
for (const match of yamlBlocks) {
|
|
281
|
+
try {
|
|
282
|
+
const yaml = require('yaml');
|
|
283
|
+
Object.assign(scaffoldConfig, yaml.parse(match[1]));
|
|
284
|
+
} catch {
|
|
285
|
+
// Skip
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const plan = scaffoldModule.planScaffold(scaffoldConfig, projectRoot);
|
|
291
|
+
|
|
292
|
+
if (dryRun) {
|
|
293
|
+
return {
|
|
294
|
+
content: [{
|
|
295
|
+
type: 'text',
|
|
296
|
+
text: `## Scaffold Plan (Dry Run)
|
|
297
|
+
|
|
298
|
+
**Configuration:**
|
|
299
|
+
- Framework: ${scaffoldConfig.stack?.framework || 'nextjs'}
|
|
300
|
+
- Language: ${scaffoldConfig.stack?.language || 'typescript'}
|
|
301
|
+
- UI: ${scaffoldConfig.frontend?.uiLibrary || 'shadcn'}
|
|
302
|
+
|
|
303
|
+
**Directories:** ${plan.directories.length}
|
|
304
|
+
**Files:** ${plan.files.length}
|
|
305
|
+
|
|
306
|
+
**Sample directories:**
|
|
307
|
+
${plan.directories.slice(0, 10).map(d => `- ${d}`).join('\n')}
|
|
308
|
+
${plan.directories.length > 10 ? `\n... and ${plan.directories.length - 10} more` : ''}
|
|
309
|
+
|
|
310
|
+
**Sample files:**
|
|
311
|
+
${plan.files.slice(0, 10).map(f => `- ${f.path}`).join('\n')}
|
|
312
|
+
${plan.files.length > 10 ? `\n... and ${plan.files.length - 10} more` : ''}`
|
|
313
|
+
}]
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const result = await scaffoldModule.execute(plan, projectRoot);
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
content: [{
|
|
321
|
+
type: 'text',
|
|
322
|
+
text: `## Scaffold Complete
|
|
323
|
+
|
|
324
|
+
Created ${result.filesCreated} files and ${result.dirsCreated} directories.
|
|
325
|
+
|
|
326
|
+
**Configuration:**
|
|
327
|
+
- Framework: ${scaffoldConfig.stack?.framework || 'nextjs'}
|
|
328
|
+
- Language: ${scaffoldConfig.stack?.language || 'typescript'}
|
|
329
|
+
|
|
330
|
+
${result.warnings.length > 0 ? `**Warnings:**\n${result.warnings.map(w => `- ${w}`).join('\n')}` : ''}
|
|
331
|
+
|
|
332
|
+
**Next Steps:**
|
|
333
|
+
1. Run \`npm install\` to install dependencies
|
|
334
|
+
2. Copy \`.env.example\` to \`.env.local\`
|
|
335
|
+
3. Run \`npm run dev\` to start development`
|
|
336
|
+
}]
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async function handleStatus(projectRoot, _config, scaffoldModule, _format) {
|
|
341
|
+
const fs = require('fs');
|
|
342
|
+
const path = require('path');
|
|
343
|
+
|
|
344
|
+
const seedPath = path.join(projectRoot, 'SEED.md');
|
|
345
|
+
const configPath = path.join(projectRoot, 'bootspring.config.js');
|
|
346
|
+
const inputsDir = path.join(projectRoot, '.bootspring', 'inputs');
|
|
347
|
+
|
|
348
|
+
const status = {
|
|
349
|
+
seedMd: fs.existsSync(seedPath),
|
|
350
|
+
config: fs.existsSync(configPath),
|
|
351
|
+
inputsFolder: fs.existsSync(inputsDir)
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
let seedInfo = '';
|
|
355
|
+
if (status.seedMd) {
|
|
356
|
+
try {
|
|
357
|
+
const content = fs.readFileSync(seedPath, 'utf-8');
|
|
358
|
+
const seedConfig = {};
|
|
359
|
+
const yamlBlocks = content.matchAll(/```yaml\n([\s\S]*?)```/g);
|
|
360
|
+
for (const match of yamlBlocks) {
|
|
361
|
+
try {
|
|
362
|
+
const yaml = require('yaml');
|
|
363
|
+
Object.assign(seedConfig, yaml.parse(match[1]));
|
|
364
|
+
} catch {
|
|
365
|
+
// Skip
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
seedInfo = `
|
|
369
|
+
**Configuration:**
|
|
370
|
+
- Project: ${seedConfig.project?.name || 'unknown'}
|
|
371
|
+
- Framework: ${seedConfig.stack?.framework || 'unknown'}
|
|
372
|
+
- Language: ${seedConfig.stack?.language || 'unknown'}
|
|
373
|
+
- Database: ${seedConfig.stack?.database || 'none'}`;
|
|
374
|
+
} catch {
|
|
375
|
+
// Continue without seed info
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const presets = scaffoldModule.getPresets();
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
content: [{
|
|
383
|
+
type: 'text',
|
|
384
|
+
text: `## Seed Status
|
|
385
|
+
|
|
386
|
+
| Item | Status |
|
|
387
|
+
|------|--------|
|
|
388
|
+
| SEED.md | ${status.seedMd ? '✓ exists' : '○ not found'} |
|
|
389
|
+
| bootspring.config.js | ${status.config ? '✓ exists' : '○ not found'} |
|
|
390
|
+
| .bootspring/inputs/ | ${status.inputsFolder ? '✓ exists' : '○ not found'} |
|
|
391
|
+
${seedInfo}
|
|
392
|
+
|
|
393
|
+
**Available Presets:** ${presets.join(', ')}
|
|
394
|
+
|
|
395
|
+
**Commands:**
|
|
396
|
+
- \`bootspring_seed\` action: "setup" - Create input folders
|
|
397
|
+
- \`bootspring_seed\` action: "generate" - Process input files
|
|
398
|
+
- \`bootspring_seed\` action: "scaffold" preset: "nextjs" - Generate project`
|
|
399
|
+
}]
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async function handleExport(projectRoot, exportFormat, format) {
|
|
404
|
+
const fs = require('fs');
|
|
405
|
+
const path = require('path');
|
|
406
|
+
|
|
407
|
+
const seedPath = path.join(projectRoot, 'SEED.md');
|
|
408
|
+
if (!fs.existsSync(seedPath)) {
|
|
409
|
+
return format.error('No SEED.md found', ['Run "bootspring seed init" first']);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const content = fs.readFileSync(seedPath, 'utf-8');
|
|
413
|
+
const seedConfig = {};
|
|
414
|
+
const yamlBlocks = content.matchAll(/```yaml\n([\s\S]*?)```/g);
|
|
415
|
+
for (const match of yamlBlocks) {
|
|
416
|
+
try {
|
|
417
|
+
const yaml = require('yaml');
|
|
418
|
+
Object.assign(seedConfig, yaml.parse(match[1]));
|
|
419
|
+
} catch {
|
|
420
|
+
// Skip
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
let exported;
|
|
425
|
+
if (exportFormat === 'yaml') {
|
|
426
|
+
const yaml = require('yaml');
|
|
427
|
+
exported = yaml.stringify(seedConfig);
|
|
428
|
+
} else {
|
|
429
|
+
exported = JSON.stringify(seedConfig, null, 2);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return {
|
|
433
|
+
content: [{
|
|
434
|
+
type: 'text',
|
|
435
|
+
text: `## Seed Configuration (${exportFormat.toUpperCase()})
|
|
436
|
+
|
|
437
|
+
\`\`\`${exportFormat}
|
|
438
|
+
${exported}
|
|
439
|
+
\`\`\``
|
|
440
|
+
}]
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
module.exports = {
|
|
445
|
+
getToolDefinition,
|
|
446
|
+
createHandler
|
|
447
|
+
};
|
package/mcp/tools/skill-tool.js
CHANGED
|
@@ -112,18 +112,28 @@ function createHandler({ skills, entitlements, trackTelemetry }) {
|
|
|
112
112
|
|
|
113
113
|
switch (action) {
|
|
114
114
|
case 'list': {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
const skillIds = skills.listSkills({ includeExternal });
|
|
116
|
+
// Filter by access - includes both external skills and premium patterns
|
|
117
|
+
const filtered = [];
|
|
118
|
+
for (const id of skillIds) {
|
|
119
|
+
const metadata = skills.getSkillMetadata(id) || {};
|
|
120
|
+
const access = entitlements.checkSkillAccess(id, {
|
|
121
|
+
...accessOptions,
|
|
122
|
+
skillTier: metadata.tier
|
|
123
|
+
});
|
|
124
|
+
if (access.allowed) {
|
|
125
|
+
filtered.push(id);
|
|
126
|
+
}
|
|
118
127
|
}
|
|
119
|
-
const items =
|
|
128
|
+
const items = filtered
|
|
120
129
|
.slice(0, limit)
|
|
121
130
|
.map(id => {
|
|
122
131
|
const metadata = skills.getSkillMetadata(id) || {};
|
|
123
132
|
return {
|
|
124
133
|
id,
|
|
125
134
|
name: metadata.name || id,
|
|
126
|
-
description: metadata.description || ''
|
|
135
|
+
description: metadata.description || '',
|
|
136
|
+
tier: metadata.tier || 'free'
|
|
127
137
|
};
|
|
128
138
|
});
|
|
129
139
|
|
|
@@ -138,11 +148,17 @@ function createHandler({ skills, entitlements, trackTelemetry }) {
|
|
|
138
148
|
if (!name) throw new Error('Skill name required');
|
|
139
149
|
const metadata = skills.getSkillMetadata(name);
|
|
140
150
|
const targetSkillId = metadata?.id || name;
|
|
141
|
-
const
|
|
151
|
+
const skillTier = metadata?.tier || 'free';
|
|
152
|
+
const access = entitlements.checkSkillAccess(targetSkillId, {
|
|
153
|
+
...accessOptions,
|
|
154
|
+
skillTier
|
|
155
|
+
});
|
|
142
156
|
if (!access.allowed) {
|
|
157
|
+
const isPremium = entitlements.isExternalSkill(targetSkillId) || skillTier === 'pro';
|
|
143
158
|
trackTelemetry('premium_prompted', {
|
|
144
|
-
capability: 'external_skill',
|
|
159
|
+
capability: isPremium ? 'premium_pattern' : 'external_skill',
|
|
145
160
|
skillId: targetSkillId,
|
|
161
|
+
skillTier,
|
|
146
162
|
mode: access.context?.mode,
|
|
147
163
|
tier: access.context?.tier,
|
|
148
164
|
reason: access.code
|
|
@@ -158,10 +174,12 @@ function createHandler({ skills, entitlements, trackTelemetry }) {
|
|
|
158
174
|
sections,
|
|
159
175
|
maxChars
|
|
160
176
|
});
|
|
161
|
-
|
|
177
|
+
const isPremiumUnlock = entitlements.isExternalSkill(targetSkillId) || skillTier === 'pro';
|
|
178
|
+
if (isPremiumUnlock) {
|
|
162
179
|
trackTelemetry('premium_unlocked', {
|
|
163
|
-
capability: 'external_skill',
|
|
180
|
+
capability: skillTier === 'pro' ? 'premium_pattern' : 'external_skill',
|
|
164
181
|
skillId: targetSkillId,
|
|
182
|
+
skillTier,
|
|
165
183
|
mode: access.context?.mode,
|
|
166
184
|
tier: access.context?.tier,
|
|
167
185
|
reason: access.code
|
|
@@ -176,6 +194,7 @@ function createHandler({ skills, entitlements, trackTelemetry }) {
|
|
|
176
194
|
name: metadata?.name || name,
|
|
177
195
|
description: metadata?.description || '',
|
|
178
196
|
source: metadata?.source || 'built-in',
|
|
197
|
+
tier: skillTier,
|
|
179
198
|
content
|
|
180
199
|
}, null, 2)
|
|
181
200
|
}]
|
|
@@ -183,18 +202,28 @@ function createHandler({ skills, entitlements, trackTelemetry }) {
|
|
|
183
202
|
}
|
|
184
203
|
case 'search': {
|
|
185
204
|
if (!query) throw new Error('Search query required');
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
205
|
+
const matches = skills.searchSkills(query, { includeExternal, limit });
|
|
206
|
+
// Filter by access - includes both external skills and premium patterns
|
|
207
|
+
const filtered = [];
|
|
208
|
+
for (const id of matches) {
|
|
209
|
+
const metadata = skills.getSkillMetadata(id) || {};
|
|
210
|
+
const access = entitlements.checkSkillAccess(id, {
|
|
211
|
+
...accessOptions,
|
|
212
|
+
skillTier: metadata.tier
|
|
213
|
+
});
|
|
214
|
+
if (access.allowed) {
|
|
215
|
+
filtered.push(id);
|
|
216
|
+
}
|
|
189
217
|
}
|
|
190
|
-
const items =
|
|
218
|
+
const items = filtered
|
|
191
219
|
.map(id => {
|
|
192
220
|
const metadata = skills.getSkillMetadata(id) || {};
|
|
193
221
|
return {
|
|
194
222
|
id,
|
|
195
223
|
name: metadata.name || id,
|
|
196
224
|
description: metadata.description || '',
|
|
197
|
-
source: metadata.source || 'built-in'
|
|
225
|
+
source: metadata.source || 'built-in',
|
|
226
|
+
tier: metadata.tier || 'free'
|
|
198
227
|
};
|
|
199
228
|
});
|
|
200
229
|
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Auto-Suggest Tool
|
|
3
|
+
*
|
|
4
|
+
* Analyzes user context and suggests relevant Bootspring commands.
|
|
5
|
+
*
|
|
6
|
+
* @package bootspring
|
|
7
|
+
* @module mcp/tools/suggest-tool
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const toolName = 'bootspring_suggest';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get tool definition for MCP
|
|
14
|
+
*/
|
|
15
|
+
function getToolDefinition() {
|
|
16
|
+
return {
|
|
17
|
+
name: toolName,
|
|
18
|
+
description: 'Analyze user context and suggest relevant Bootspring commands. Use this to proactively help users discover Bootspring features that can assist with their current task.',
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
userMessage: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'The user message or context to analyze for suggestions'
|
|
25
|
+
},
|
|
26
|
+
action: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
enum: ['analyze', 'enable', 'disable', 'config', 'toggle-category'],
|
|
29
|
+
description: 'Action to perform. Default is "analyze"'
|
|
30
|
+
},
|
|
31
|
+
category: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
description: 'Category name for toggle-category action'
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
required: []
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Create tool handler
|
|
43
|
+
*/
|
|
44
|
+
function createHandler({ config, format }) {
|
|
45
|
+
// Lazy load auto-suggest
|
|
46
|
+
let autoSuggestModule = null;
|
|
47
|
+
|
|
48
|
+
function getAutoSuggest() {
|
|
49
|
+
if (!autoSuggestModule) {
|
|
50
|
+
autoSuggestModule = require('../../intelligence/auto-suggest');
|
|
51
|
+
}
|
|
52
|
+
return autoSuggestModule;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return async (params) => {
|
|
56
|
+
const cfg = config.load();
|
|
57
|
+
const projectRoot = cfg._projectRoot;
|
|
58
|
+
const action = params.action || 'analyze';
|
|
59
|
+
|
|
60
|
+
const { AutoSuggest } = getAutoSuggest();
|
|
61
|
+
const autoSuggest = new AutoSuggest(projectRoot);
|
|
62
|
+
|
|
63
|
+
switch (action) {
|
|
64
|
+
case 'analyze': {
|
|
65
|
+
if (!params.userMessage) {
|
|
66
|
+
return format.error('userMessage is required for analyze action');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const suggestions = autoSuggest.analyze(params.userMessage);
|
|
70
|
+
|
|
71
|
+
if (suggestions.length === 0) {
|
|
72
|
+
return format.success('No relevant suggestions for this context');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const formattedSuggestions = suggestions.map(s => ({
|
|
76
|
+
command: s.command,
|
|
77
|
+
description: s.description,
|
|
78
|
+
confidence: Math.round(s.confidence * 100),
|
|
79
|
+
category: s.category,
|
|
80
|
+
priority: s.priority
|
|
81
|
+
}));
|
|
82
|
+
|
|
83
|
+
return format.success(
|
|
84
|
+
`Found ${suggestions.length} relevant suggestion(s)`,
|
|
85
|
+
formattedSuggestions.map(s => `${s.command} (${s.confidence}%) - ${s.description}`)
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
case 'enable': {
|
|
90
|
+
autoSuggest.setEnabled(true);
|
|
91
|
+
return format.success('Auto-suggest enabled. Bootspring will proactively suggest relevant commands.');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
case 'disable': {
|
|
95
|
+
autoSuggest.setEnabled(false);
|
|
96
|
+
return format.success('Auto-suggest disabled. Run with action="enable" to re-enable.');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
case 'config': {
|
|
100
|
+
const status = autoSuggest.getStatus();
|
|
101
|
+
const enabledCategories = Object.entries(status.categories)
|
|
102
|
+
.filter(([, enabled]) => enabled)
|
|
103
|
+
.map(([name]) => name);
|
|
104
|
+
const disabledCategories = Object.entries(status.categories)
|
|
105
|
+
.filter(([, enabled]) => !enabled)
|
|
106
|
+
.map(([name]) => name);
|
|
107
|
+
|
|
108
|
+
return format.success(
|
|
109
|
+
status.enabled
|
|
110
|
+
? 'Auto-suggest is enabled and will proactively suggest commands'
|
|
111
|
+
: 'Auto-suggest is disabled',
|
|
112
|
+
[
|
|
113
|
+
`Status: ${status.enabled ? 'enabled' : 'disabled'}`,
|
|
114
|
+
`Enabled categories: ${enabledCategories.join(', ') || 'none'}`,
|
|
115
|
+
`Disabled categories: ${disabledCategories.join(', ') || 'none'}`,
|
|
116
|
+
`Suppressed commands: ${status.suppressedCommands.length || 0}`
|
|
117
|
+
]
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
case 'toggle-category': {
|
|
122
|
+
if (!params.category) {
|
|
123
|
+
return format.error('category is required for toggle-category action');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const status = autoSuggest.getStatus();
|
|
127
|
+
if (!status.categories.hasOwnProperty(params.category)) {
|
|
128
|
+
return format.error(`Unknown category: ${params.category}. Available: ${Object.keys(status.categories).join(', ')}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const newState = !status.categories[params.category];
|
|
132
|
+
autoSuggest.setCategory(params.category, newState);
|
|
133
|
+
|
|
134
|
+
return format.success(`Category '${params.category}' ${newState ? 'enabled' : 'disabled'}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
default:
|
|
138
|
+
return format.error(`Unknown action: ${action}`);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = {
|
|
144
|
+
getToolDefinition,
|
|
145
|
+
createHandler,
|
|
146
|
+
toolName
|
|
147
|
+
};
|