@girardmedia/bootspring 2.1.3 → 2.2.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/cli/agent.js DELETED
@@ -1,799 +0,0 @@
1
- /**
2
- * Bootspring Agent Command
3
- * Work with specialized AI agents
4
- *
5
- * @package bootspring
6
- * @command agent
7
- */
8
-
9
- const crypto = require('crypto');
10
- const utils = require('../core/utils');
11
- const telemetry = require('../core/telemetry');
12
- const tierEnforcement = require('../core/tier-enforcement');
13
- const api = require('../core/api-client');
14
- const auth = require('../core/auth');
15
-
16
- // Re-export AGENT_TIERS from tier-enforcement for compatibility
17
- const { AGENT_TIERS, checkAgentAccess: canAccessAgent } = tierEnforcement;
18
-
19
- /**
20
- * Track agent invocation via telemetry
21
- */
22
- function trackAgentInvocation(agentId, topic = null) {
23
- try {
24
- telemetry.emitEvent('agent.invoked', {
25
- agent: agentId,
26
- topic: topic ? topic.substring(0, 100) : null, // Truncate for privacy
27
- timestamp: new Date().toISOString()
28
- });
29
- } catch {
30
- // Telemetry should never break the command
31
- }
32
- }
33
-
34
- function trackTelemetry(event, payload = {}) {
35
- try {
36
- telemetry.emitEvent(event, payload);
37
- } catch {
38
- // Never block command flow on telemetry issues.
39
- }
40
- }
41
-
42
- function getUpgradePromptContext(agentId, requiredTier = 'pro') {
43
- if (typeof tierEnforcement.getUpgradePromptContext === 'function') {
44
- return tierEnforcement.getUpgradePromptContext(`agent: ${agentId}`, requiredTier, {
45
- capability: 'agent_access',
46
- action: 'agent_invoke'
47
- });
48
- }
49
-
50
- return {
51
- feature: `agent: ${agentId}`,
52
- featureType: 'agent',
53
- featureLabel: `Agent ${agentId}`,
54
- capability: 'agent_access',
55
- action: 'agent_invoke',
56
- requiredTier,
57
- userTier: typeof tierEnforcement.getTier === 'function' ? tierEnforcement.getTier() : 'free',
58
- placement: 'inline',
59
- variant: 'control'
60
- };
61
- }
62
-
63
- function trackUpgradePrompted(agentId, requiredTier, reason = 'agent_subscription_required') {
64
- const promptContext = getUpgradePromptContext(agentId, requiredTier || 'pro');
65
- trackTelemetry('premium_prompted', {
66
- ...promptContext,
67
- agent: agentId,
68
- reason
69
- });
70
- return promptContext;
71
- }
72
-
73
- function trackUpgradeUnlocked(agentId, requiredTier) {
74
- const promptContext = getUpgradePromptContext(agentId, requiredTier || 'pro');
75
- trackTelemetry('premium_unlocked', {
76
- ...promptContext,
77
- agent: agentId
78
- });
79
- }
80
-
81
- function normalizeChecksum(checksum) {
82
- const raw = String(checksum || '').trim().toLowerCase();
83
- if (!raw) return '';
84
- return raw.startsWith('sha256:') ? raw.slice(7) : raw;
85
- }
86
-
87
- function verifyPayloadIntegrity(agentId, content, checksum, options = {}) {
88
- const { requireChecksum = false } = options;
89
- const expected = normalizeChecksum(checksum);
90
- if (requireChecksum && !expected) {
91
- throw new Error(`Integrity metadata missing for agent payload: ${agentId}`);
92
- }
93
-
94
- const digest = crypto.createHash('sha256').update(String(content || ''), 'utf8').digest('hex');
95
- if (expected && expected !== digest) {
96
- throw new Error(`Integrity check failed for agent payload: ${agentId}`);
97
- }
98
-
99
- return {
100
- algorithm: 'sha256',
101
- checksum: `sha256:${digest}`,
102
- expected: expected ? `sha256:${expected}` : null,
103
- verified: !!expected
104
- };
105
- }
106
-
107
- /**
108
- * Fetch agent context from API (thin client model)
109
- * Agent context is not bundled locally - must be fetched from API
110
- * @param {string} agentId - Agent ID
111
- * @returns {Promise<{context: string, name: string}|null>}
112
- */
113
- async function fetchAgentContext(agentId) {
114
- if (!auth.isAuthenticated()) {
115
- return null;
116
- }
117
-
118
- try {
119
- const response = await api.getAgentContext(agentId);
120
- const responseTier = String(response?.tier || AGENT_TIERS[agentId] || 'free').toLowerCase();
121
- const requiredTier = AGENT_TIERS[agentId] || (responseTier === 'pro' || responseTier === 'premium' ? 'pro' : 'free');
122
- if (requiredTier !== 'free' && !tierEnforcement.meetsTierRequirement(requiredTier, auth.getTier())) {
123
- return { error: 'upgrade_required', requiredTier };
124
- }
125
-
126
- const integrity = verifyPayloadIntegrity(agentId, response?.context || '', response?.checksum, {
127
- requireChecksum: Boolean(response?.checksum)
128
- });
129
-
130
- return { ...response, integrity };
131
- } catch (error) {
132
- if (error.message && error.message.toLowerCase().includes('integrity')) {
133
- return { error: 'integrity_error', message: error.message };
134
- }
135
- if (error.status === 403) {
136
- // Tier not sufficient - will be handled by canAccessAgent
137
- return { error: 'upgrade_required', requiredTier: error.requiredTier };
138
- }
139
- if (error.status === 401) {
140
- return { error: 'auth_required' };
141
- }
142
- // Network error or other issue
143
- return null;
144
- }
145
- }
146
-
147
- async function fetchHostedAgentCatalog() {
148
- if (!auth.isAuthenticated()) {
149
- return null;
150
- }
151
-
152
- try {
153
- const response = await api.listAgents();
154
- return response.agents || null;
155
- } catch {
156
- return null;
157
- }
158
- }
159
-
160
- async function resolveAgentDefinition(agentId) {
161
- if (auth.isAuthenticated()) {
162
- try {
163
- const response = await api.getAgent(agentId);
164
- return response.agent || response;
165
- } catch {
166
- // Fall back to bundled metadata for discovery.
167
- }
168
- }
169
-
170
- return AGENTS[agentId] || null;
171
- }
172
-
173
- // Agent definitions - these will be loaded from agents/profiles/ in production
174
- const AGENTS = {
175
- 'database-expert': {
176
- name: 'Database Expert',
177
- category: 'Backend',
178
- description: 'Specializes in database design, queries, migrations, and optimization',
179
- expertise: ['SQL', 'NoSQL', 'Prisma', 'Drizzle', 'PostgreSQL', 'MongoDB'],
180
- triggers: ['database', 'schema', 'migration', 'query', 'prisma', 'sql']
181
- },
182
- 'security-expert': {
183
- name: 'Security Expert',
184
- category: 'Security',
185
- description: 'Focuses on application security, OWASP, authentication, and authorization',
186
- expertise: ['OWASP', 'Authentication', 'Authorization', 'Input Validation', 'XSS', 'CSRF'],
187
- triggers: ['security', 'auth', 'owasp', 'vulnerability', 'xss', 'csrf', 'injection']
188
- },
189
- 'frontend-expert': {
190
- name: 'Frontend Expert',
191
- category: 'Frontend',
192
- description: 'Expert in React, Next.js, UI components, and modern frontend patterns',
193
- expertise: ['React', 'Next.js', 'TypeScript', 'Tailwind', 'Components', 'State Management'],
194
- triggers: ['component', 'react', 'frontend', 'ui', 'tailwind', 'css', 'styling']
195
- },
196
- 'backend-expert': {
197
- name: 'Backend Expert',
198
- category: 'Backend',
199
- description: 'Specializes in server-side development, APIs, and business logic',
200
- expertise: ['Node.js', 'APIs', 'Server Actions', 'Middleware', 'Business Logic'],
201
- triggers: ['api', 'backend', 'server', 'endpoint', 'route', 'middleware']
202
- },
203
- 'api-expert': {
204
- name: 'API Expert',
205
- category: 'Backend',
206
- description: 'Focuses on API design, REST, GraphQL, and integration patterns',
207
- expertise: ['REST', 'GraphQL', 'tRPC', 'OpenAPI', 'Webhooks', 'Integration'],
208
- triggers: ['api', 'rest', 'graphql', 'trpc', 'webhook', 'endpoint']
209
- },
210
- 'testing-expert': {
211
- name: 'Testing Expert',
212
- category: 'Quality',
213
- description: 'Expert in testing strategies, test frameworks, and quality assurance',
214
- expertise: ['Unit Testing', 'Integration Testing', 'E2E', 'Vitest', 'Jest', 'Playwright'],
215
- triggers: ['test', 'testing', 'jest', 'vitest', 'playwright', 'coverage']
216
- },
217
- 'performance-expert': {
218
- name: 'Performance Expert',
219
- category: 'DevOps',
220
- description: 'Specializes in performance optimization, caching, and monitoring',
221
- expertise: ['Optimization', 'Caching', 'Profiling', 'Core Web Vitals', 'Load Testing'],
222
- triggers: ['performance', 'optimize', 'cache', 'slow', 'speed', 'profiling']
223
- },
224
- 'devops-expert': {
225
- name: 'DevOps Expert',
226
- category: 'DevOps',
227
- description: 'Focuses on deployment, CI/CD, infrastructure, and monitoring',
228
- expertise: ['CI/CD', 'Docker', 'Kubernetes', 'Monitoring', 'Infrastructure'],
229
- triggers: ['deploy', 'ci', 'cd', 'docker', 'kubernetes', 'infrastructure']
230
- },
231
- 'ui-ux-expert': {
232
- name: 'UI/UX Expert',
233
- category: 'Frontend',
234
- description: 'Expert in user interface design, user experience, and accessibility',
235
- expertise: ['Design Systems', 'Accessibility', 'Responsive Design', 'Animation'],
236
- triggers: ['ui', 'ux', 'design', 'accessibility', 'a11y', 'responsive']
237
- },
238
- 'architecture-expert': {
239
- name: 'Architecture Expert',
240
- category: 'Backend',
241
- description: 'Specializes in system architecture, design patterns, and scalability',
242
- expertise: ['System Design', 'Design Patterns', 'Microservices', 'Scalability'],
243
- triggers: ['architecture', 'design', 'pattern', 'scalability', 'structure']
244
- },
245
- 'code-review-expert': {
246
- name: 'Code Review Expert',
247
- category: 'Quality',
248
- description: 'Focuses on code quality, best practices, and constructive feedback',
249
- expertise: ['Code Quality', 'Best Practices', 'Refactoring', 'Clean Code'],
250
- triggers: ['review', 'refactor', 'quality', 'clean', 'improve']
251
- },
252
- 'vercel-expert': {
253
- name: 'Vercel Expert',
254
- category: 'DevOps',
255
- description: 'Expert in Vercel deployment, serverless, and edge functions',
256
- expertise: ['Vercel', 'Serverless', 'Edge Functions', 'ISR', 'Deployment'],
257
- triggers: ['vercel', 'serverless', 'edge', 'deployment', 'hosting']
258
- },
259
- // Business Agents
260
- 'business-strategy-expert': {
261
- name: 'Business Strategy Expert',
262
- category: 'Business',
263
- description: 'Specializes in business model design, market analysis, and strategic planning',
264
- expertise: ['Business Models', 'Market Analysis', 'Competitive Strategy', 'Go-to-Market', 'Unit Economics'],
265
- triggers: ['business', 'strategy', 'market', 'model', 'gtm', 'positioning']
266
- },
267
- 'financial-expert': {
268
- name: 'Financial Expert',
269
- category: 'Business',
270
- description: 'Expert in financial modeling, projections, and startup finance',
271
- expertise: ['Financial Modeling', 'Revenue Projections', 'Unit Economics', 'Cash Flow', 'Budgeting'],
272
- triggers: ['financial', 'finance', 'revenue', 'projection', 'budget', 'cash', 'model']
273
- },
274
- 'fundraising-expert': {
275
- name: 'Fundraising Expert',
276
- category: 'Business',
277
- description: 'Specializes in investor relations, pitch decks, and fundraising strategy',
278
- expertise: ['Pitch Decks', 'Investor Relations', 'Term Sheets', 'Due Diligence', 'Fundraising Strategy'],
279
- triggers: ['fundraise', 'investor', 'pitch', 'deck', 'seed', 'series', 'vc', 'angel']
280
- },
281
- 'private-equity-expert': {
282
- name: 'Private Equity Expert',
283
- category: 'Business',
284
- description: 'Expert in PE/VC dynamics, valuation, and deal structuring',
285
- expertise: ['Valuation', 'Deal Structure', 'Cap Tables', 'Term Sheets', 'Exit Strategy'],
286
- triggers: ['pe', 'equity', 'valuation', 'cap table', 'term sheet', 'dilution']
287
- },
288
- 'investor-relations-expert': {
289
- name: 'Investor Relations Expert',
290
- category: 'Business',
291
- description: 'Specializes in investor communications, reporting, and stakeholder management',
292
- expertise: ['Investor Updates', 'Board Management', 'KPI Reporting', 'Stakeholder Communication'],
293
- triggers: ['investor', 'board', 'update', 'reporting', 'stakeholder']
294
- },
295
- 'legal-expert': {
296
- name: 'Legal Expert',
297
- category: 'Business',
298
- description: 'Expert in startup legal matters, contracts, and compliance',
299
- expertise: ['Terms of Service', 'Privacy Policy', 'Contracts', 'IP', 'Compliance', 'GDPR'],
300
- triggers: ['legal', 'terms', 'privacy', 'contract', 'compliance', 'gdpr', 'ip']
301
- },
302
- 'marketing-expert': {
303
- name: 'Marketing Expert',
304
- category: 'Business',
305
- description: 'Specializes in digital marketing, content strategy, and brand building',
306
- expertise: ['Content Marketing', 'SEO', 'Social Media', 'Email Marketing', 'Brand Strategy'],
307
- triggers: ['marketing', 'content', 'seo', 'social', 'brand', 'campaign']
308
- },
309
- 'sales-expert': {
310
- name: 'Sales Expert',
311
- category: 'Business',
312
- description: 'Expert in sales strategy, pipeline management, and customer acquisition',
313
- expertise: ['Sales Strategy', 'Pipeline Management', 'CRM', 'Lead Generation', 'Closing'],
314
- triggers: ['sales', 'pipeline', 'lead', 'crm', 'close', 'deal', 'prospect']
315
- },
316
- 'growth-expert': {
317
- name: 'Growth Expert',
318
- category: 'Business',
319
- description: 'Specializes in growth hacking, metrics, and user acquisition',
320
- expertise: ['Growth Hacking', 'User Acquisition', 'Retention', 'Viral Loops', 'A/B Testing'],
321
- triggers: ['growth', 'acquisition', 'retention', 'viral', 'metrics', 'experiment']
322
- },
323
- 'operations-expert': {
324
- name: 'Operations Expert',
325
- category: 'Business',
326
- description: 'Expert in operational efficiency, processes, and team management',
327
- expertise: ['Process Design', 'Team Management', 'Hiring', 'OKRs', 'Operational Efficiency'],
328
- triggers: ['operations', 'ops', 'process', 'team', 'hiring', 'okr', 'efficiency']
329
- },
330
- 'competitive-analysis-expert': {
331
- name: 'Competitive Analysis Expert',
332
- category: 'Business',
333
- description: 'Specializes in market research, competitor analysis, and positioning',
334
- expertise: ['Competitor Analysis', 'Market Research', 'SWOT', 'Positioning', 'Differentiation'],
335
- triggers: ['competitive', 'competitor', 'analysis', 'market', 'swot', 'positioning']
336
- },
337
- 'partnerships-expert': {
338
- name: 'Partnerships Expert',
339
- category: 'Business',
340
- description: 'Expert in partnership development, BD, and strategic alliances',
341
- expertise: ['Partnership Development', 'Business Development', 'Strategic Alliances', 'Channel Partners'],
342
- triggers: ['partnership', 'partner', 'bd', 'alliance', 'channel', 'integration']
343
- },
344
- // DevOps Agents
345
- 'railway-expert': {
346
- name: 'Railway Expert',
347
- category: 'DevOps',
348
- description: 'Expert in Railway deployment, databases, and infrastructure',
349
- expertise: ['Railway', 'PostgreSQL', 'Redis', 'Deployment', 'Scaling'],
350
- triggers: ['railway', 'deploy', 'postgres', 'redis', 'infrastructure']
351
- },
352
- 'monitoring-expert': {
353
- name: 'Monitoring Expert',
354
- category: 'DevOps',
355
- description: 'Specializes in observability, logging, metrics, and alerting',
356
- expertise: ['Observability', 'Logging', 'Metrics', 'Alerting', 'APM', 'Sentry', 'Datadog'],
357
- triggers: ['monitoring', 'logging', 'metrics', 'alerts', 'observability', 'sentry']
358
- },
359
- // Research Agents
360
- 'research-expert': {
361
- name: 'Research Expert',
362
- category: 'Research',
363
- description: 'Expert in market research, user research, and data analysis',
364
- expertise: ['Market Research', 'User Research', 'Surveys', 'Interviews', 'Data Analysis'],
365
- triggers: ['research', 'survey', 'interview', 'user research', 'market research']
366
- },
367
- 'data-modeling-expert': {
368
- name: 'Data Modeling Expert',
369
- category: 'Research',
370
- description: 'Specializes in data architecture, ERD design, and schema optimization',
371
- expertise: ['ERD Design', 'Schema Design', 'Normalization', 'Indexing', 'Data Architecture'],
372
- triggers: ['data model', 'erd', 'schema', 'entity', 'relationship', 'normalization']
373
- },
374
- // Integration Agents
375
- 'payment-expert': {
376
- name: 'Payment Expert',
377
- category: 'Integration',
378
- description: 'Expert in payment processing, Stripe integration, and subscription billing',
379
- expertise: ['Stripe', 'Subscriptions', 'Billing', 'Webhooks', 'Checkout', 'Payment Processing'],
380
- triggers: ['payment', 'stripe', 'subscription', 'billing', 'checkout', 'invoice']
381
- },
382
- 'ai-integration-expert': {
383
- name: 'AI Integration Expert',
384
- category: 'Integration',
385
- description: 'Specializes in AI/LLM integration, Anthropic Claude, and AI SDK',
386
- expertise: ['Claude API', 'AI SDK', 'Streaming', 'Tool Use', 'RAG', 'Embeddings'],
387
- triggers: ['ai', 'llm', 'claude', 'anthropic', 'openai', 'streaming', 'embedding']
388
- },
389
- 'email-expert': {
390
- name: 'Email Expert',
391
- category: 'Integration',
392
- description: 'Expert in email integration, Resend, and transactional emails',
393
- expertise: ['Resend', 'React Email', 'Transactional Email', 'Templates', 'Email Delivery'],
394
- triggers: ['email', 'resend', 'sendgrid', 'template', 'notification', 'transactional']
395
- },
396
- 'auth-expert': {
397
- name: 'Auth Expert',
398
- category: 'Integration',
399
- description: 'Specializes in authentication, Clerk, NextAuth, and session management',
400
- expertise: ['Clerk', 'NextAuth', 'OAuth', 'JWT', 'Session Management', 'RBAC'],
401
- triggers: ['auth', 'authentication', 'login', 'session', 'oauth', 'clerk', 'nextauth']
402
- },
403
- // Content Agents
404
- 'content-expert': {
405
- name: 'Content Expert',
406
- category: 'Content',
407
- description: 'Expert in technical writing, documentation, blogs, marketing copy, and content strategy',
408
- expertise: ['Technical Writing', 'Documentation', 'Blog Posts', 'Release Notes', 'README', 'API Docs', 'SEO', 'Content Strategy'],
409
- triggers: ['content', 'writing', 'documentation', 'docs', 'blog', 'readme', 'changelog', 'copy', 'article', 'marketing']
410
- },
411
- // Product Agents
412
- 'product-expert': {
413
- name: 'Product Expert',
414
- category: 'Product',
415
- description: 'Expert in product management, roadmapping, feature prioritization, and user research',
416
- expertise: ['Product Strategy', 'Roadmapping', 'Feature Prioritization', 'User Research', 'PRDs', 'OKRs', 'Metrics', 'Product-Market Fit'],
417
- triggers: ['product', 'roadmap', 'feature', 'prioritization', 'prd', 'requirements', 'user research', 'okr', 'metrics', 'backlog']
418
- },
419
- // Mobile Agents
420
- 'mobile-expert': {
421
- name: 'Mobile Expert',
422
- category: 'Mobile',
423
- description: 'Expert in mobile development with React Native, Flutter, iOS, and Android',
424
- expertise: ['React Native', 'Flutter', 'iOS', 'Android', 'Mobile Architecture', 'App Store', 'Push Notifications', 'Offline-First'],
425
- triggers: ['mobile', 'react native', 'flutter', 'ios', 'android', 'app', 'native', 'swift', 'kotlin', 'expo']
426
- },
427
- // Infrastructure Agents
428
- 'infrastructure-expert': {
429
- name: 'Infrastructure Expert',
430
- category: 'DevOps',
431
- description: 'Expert in cloud infrastructure, Terraform, Kubernetes, and DevOps practices',
432
- expertise: ['AWS', 'GCP', 'Azure', 'Terraform', 'Kubernetes', 'Docker', 'CI/CD', 'Infrastructure-as-Code', 'Cloud Architecture'],
433
- triggers: ['infrastructure', 'cloud', 'aws', 'gcp', 'terraform', 'kubernetes', 'k8s', 'docker', 'devops', 'iac']
434
- }
435
- };
436
-
437
- /**
438
- * List all available agents
439
- */
440
- async function listAgents() {
441
- const userTier = tierEnforcement.getTier();
442
- const hostedAgents = await fetchHostedAgentCatalog();
443
-
444
- console.log(`
445
- ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Agents${utils.COLORS.reset}
446
- ${utils.COLORS.dim}Specialized AI expertise on demand${utils.COLORS.reset}
447
- ${utils.COLORS.dim}Your tier: ${userTier}${utils.COLORS.reset}
448
- `);
449
-
450
- const catalog = Array.isArray(hostedAgents) && hostedAgents.length > 0
451
- ? hostedAgents.map((agent) => ({
452
- id: agent.id,
453
- name: agent.name,
454
- category: agent.category || 'Hosted',
455
- description: agent.description,
456
- expertise: agent.expertise || [],
457
- accessible: agent.accessible,
458
- requiredTier: agent.requiredTier || AGENT_TIERS[agent.id] || 'free'
459
- }))
460
- : Object.entries(AGENTS).map(([id, agent]) => ({
461
- id,
462
- ...agent,
463
- accessible: canAccessAgent(id).allowed,
464
- requiredTier: AGENT_TIERS[id] || 'free'
465
- }));
466
-
467
- const categories = {};
468
- for (const agent of catalog) {
469
- if (!categories[agent.category]) {
470
- categories[agent.category] = [];
471
- }
472
- categories[agent.category].push(agent);
473
- }
474
-
475
- let accessibleCount = 0;
476
- let lockedCount = 0;
477
-
478
- for (const [category, agents] of Object.entries(categories)) {
479
- console.log(`${utils.COLORS.bold}${category}${utils.COLORS.reset}`);
480
- for (const agent of agents) {
481
- const accessible = agent.accessible !== false;
482
- const requiredTier = agent.requiredTier || AGENT_TIERS[agent.id];
483
- const tierBadge = requiredTier && requiredTier !== 'free'
484
- ? ` ${utils.COLORS.yellow}[${requiredTier}]${utils.COLORS.reset}`
485
- : '';
486
- const lockIcon = accessible ? '' : ` ${utils.COLORS.red}🔒${utils.COLORS.reset}`;
487
-
488
- if (accessible) {
489
- accessibleCount++;
490
- } else {
491
- lockedCount++;
492
- }
493
-
494
- console.log(` ${utils.COLORS.cyan}${agent.id}${utils.COLORS.reset}${tierBadge}${lockIcon}`);
495
- console.log(` ${utils.COLORS.dim}${agent.description}${utils.COLORS.reset}`);
496
- }
497
- console.log();
498
- }
499
-
500
- utils.print.dim(`${accessibleCount} agents available, ${lockedCount} locked`);
501
- utils.print.dim('Use "bootspring agent show <name>" for details');
502
- }
503
-
504
- /**
505
- * Show agent details
506
- */
507
- async function showAgent(agentId) {
508
- const agent = await resolveAgentDefinition(agentId);
509
-
510
- if (!agent) {
511
- utils.print.error(`Agent not found: ${agentId}`);
512
- utils.print.dim('Use "bootspring agent list" to see available agents');
513
- return;
514
- }
515
-
516
- const expertise = Array.isArray(agent.expertise) ? agent.expertise : [];
517
- const triggers = Array.isArray(agent.triggers) ? agent.triggers : [];
518
-
519
- console.log(`
520
- ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ ${agent.name}${utils.COLORS.reset}
521
- ${utils.COLORS.dim}Category: ${agent.category || 'Hosted'}${utils.COLORS.reset}
522
-
523
- ${utils.COLORS.bold}Description${utils.COLORS.reset}
524
- ${agent.description}
525
-
526
- ${utils.COLORS.bold}Expertise${utils.COLORS.reset}
527
- ${expertise.length > 0 ? expertise.map(e => ` ${utils.COLORS.green}●${utils.COLORS.reset} ${e}`).join('\n') : ` ${utils.COLORS.dim}Hosted agent expertise${utils.COLORS.reset}`}
528
-
529
- ${utils.COLORS.bold}Trigger Keywords${utils.COLORS.reset}
530
- ${triggers.length > 0 ? triggers.map(t => ` ${utils.COLORS.dim}${t}${utils.COLORS.reset}`).join(', ') : ` ${utils.COLORS.dim}Server-managed${utils.COLORS.reset}`}
531
-
532
- ${utils.COLORS.bold}Usage${utils.COLORS.reset}
533
- bootspring agent invoke ${agentId} --topic "your question"
534
- `);
535
- }
536
-
537
- /**
538
- * Invoke an agent
539
- */
540
- async function invokeAgent(agentId, args) {
541
- const agent = await resolveAgentDefinition(agentId);
542
-
543
- if (!agent) {
544
- utils.print.error(`Agent not found: ${agentId}`);
545
- utils.print.dim('Use "bootspring agent list" to see available agents');
546
- return;
547
- }
548
-
549
- // Check authentication first
550
- if (!auth.isAuthenticated()) {
551
- console.log(`
552
- ${utils.COLORS.yellow}${utils.COLORS.bold}⚡ ${agent.name}${utils.COLORS.reset}
553
- ${utils.COLORS.red}Authentication required${utils.COLORS.reset}
554
-
555
- ${utils.COLORS.dim}Agent context is served from the API. Please log in:${utils.COLORS.reset}
556
- ${utils.COLORS.cyan}bootspring auth login${utils.COLORS.reset}
557
- `);
558
- return;
559
- }
560
-
561
- // Check tier access (client-side check for UX, server enforces)
562
- const access = canAccessAgent(agentId);
563
- if (!access.allowed) {
564
- const promptContext = trackUpgradePrompted(agentId, access.requiredTier || 'pro');
565
- console.log(`
566
- ${utils.COLORS.yellow}${utils.COLORS.bold}⚡ ${agent.name}${utils.COLORS.reset}
567
- ${utils.COLORS.red}This agent requires ${access.requiredTier} tier or higher.${utils.COLORS.reset}
568
- `);
569
- console.log(tierEnforcement.getUpgradePrompt(`agent: ${agentId}`, access.requiredTier || 'pro', {
570
- capability: promptContext.capability,
571
- action: promptContext.action,
572
- placement: promptContext.placement,
573
- variant: promptContext.variant
574
- }));
575
- return;
576
- }
577
-
578
- const isMCP = utils.isMCPContext();
579
- const parsedArgs = utils.parseArgs(args);
580
- const topic = parsedArgs.topic || args.filter(a => !a.startsWith('--')).join(' ');
581
-
582
- // Track agent invocation
583
- trackAgentInvocation(agentId, topic);
584
-
585
- console.log(`
586
- ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ ${agent.name}${utils.COLORS.reset}
587
- ${utils.COLORS.dim}${agent.description}${utils.COLORS.reset}
588
- `);
589
-
590
- // Fetch agent context from API (thin client - no local content)
591
- const spinner = utils.createSpinner('Loading agent context...');
592
- spinner.start();
593
-
594
- const contextResult = await fetchAgentContext(agentId);
595
-
596
- if (!contextResult) {
597
- spinner.fail('Failed to load agent context');
598
- console.log(`${utils.COLORS.dim}Check your internet connection and try again.${utils.COLORS.reset}`);
599
- return;
600
- }
601
-
602
- if (contextResult.error === 'upgrade_required') {
603
- const promptContext = trackUpgradePrompted(agentId, contextResult.requiredTier || 'pro');
604
- spinner.fail('Upgrade required');
605
- console.log(tierEnforcement.getUpgradePrompt(`agent: ${agentId}`, contextResult.requiredTier || 'pro', {
606
- capability: promptContext.capability,
607
- action: promptContext.action,
608
- placement: promptContext.placement,
609
- variant: promptContext.variant
610
- }));
611
- return;
612
- }
613
-
614
- if (contextResult.error === 'auth_required') {
615
- spinner.fail('Authentication required');
616
- console.log(`${utils.COLORS.dim}Run: bootspring auth login${utils.COLORS.reset}`);
617
- return;
618
- }
619
-
620
- if (contextResult.error === 'integrity_error') {
621
- spinner.fail('Integrity verification failed');
622
- console.log(`${utils.COLORS.dim}${contextResult.message || 'Agent payload checksum mismatch.'}${utils.COLORS.reset}`);
623
- return;
624
- }
625
-
626
- spinner.succeed('Agent context loaded');
627
- const responseTier = String(contextResult.tier || AGENT_TIERS[agentId] || 'free').toLowerCase();
628
- const requiredTier = AGENT_TIERS[agentId] || (responseTier === 'pro' || responseTier === 'premium' ? 'pro' : 'free');
629
- if (requiredTier !== 'free') {
630
- trackUpgradeUnlocked(agentId, requiredTier);
631
- }
632
-
633
- // In MCP mode, output the context for the AI to use
634
- if (isMCP) {
635
- console.log(`\n${utils.COLORS.bold}Agent Context:${utils.COLORS.reset}\n`);
636
- console.log(contextResult.context);
637
- return;
638
- }
639
-
640
- // CLI mode - show context and guidance
641
- console.log(`\n${utils.COLORS.bold}Agent Context:${utils.COLORS.reset}`);
642
- console.log(`${utils.COLORS.dim}─────────────────────────────────────${utils.COLORS.reset}`);
643
-
644
- // Show truncated context in CLI mode
645
- const contextLines = contextResult.context.split('\n');
646
- const previewLines = contextLines.slice(0, 20);
647
- console.log(previewLines.join('\n'));
648
-
649
- if (contextLines.length > 20) {
650
- console.log(`\n${utils.COLORS.dim}... (${contextLines.length - 20} more lines)${utils.COLORS.reset}`);
651
- }
652
-
653
- console.log(`${utils.COLORS.dim}─────────────────────────────────────${utils.COLORS.reset}`);
654
-
655
- if (topic) {
656
- console.log(`\n${utils.COLORS.bold}Your topic:${utils.COLORS.reset} "${topic}"
657
- ${utils.COLORS.dim}Use this context with your AI assistant for the topic above.${utils.COLORS.reset}
658
- `);
659
- }
660
-
661
- console.log(`
662
- ${utils.COLORS.bold}Usage:${utils.COLORS.reset}
663
- Copy the context above and use it with your AI assistant.
664
-
665
- ${utils.COLORS.yellow}${utils.COLORS.bold}Enhanced Mode${utils.COLORS.reset}
666
- ${utils.COLORS.dim}With MCP integration, agents activate automatically.${utils.COLORS.reset}
667
- ${utils.COLORS.cyan}bootspring mcp start${utils.COLORS.reset}
668
- `);
669
- }
670
-
671
- /**
672
- * Search for agents by keyword
673
- */
674
- async function searchAgents(query) {
675
- const hostedAgents = await fetchHostedAgentCatalog();
676
- const queryLower = query.toLowerCase();
677
-
678
- const catalog = Array.isArray(hostedAgents) && hostedAgents.length > 0
679
- ? hostedAgents.map((agent) => [agent.id, {
680
- name: agent.name,
681
- description: agent.description,
682
- expertise: agent.expertise || [],
683
- triggers: agent.triggers || []
684
- }])
685
- : Object.entries(AGENTS);
686
-
687
- const matches = catalog.filter(([id, agent]) => (
688
- id.includes(queryLower) ||
689
- agent.name.toLowerCase().includes(queryLower) ||
690
- agent.description.toLowerCase().includes(queryLower) ||
691
- agent.expertise.some(e => e.toLowerCase().includes(queryLower)) ||
692
- agent.triggers.some(t => t.includes(queryLower))
693
- ));
694
-
695
- if (matches.length === 0) {
696
- utils.print.warning(`No agents found matching "${query}"`);
697
- utils.print.dim('Use "bootspring agent list" to see all agents');
698
- return;
699
- }
700
-
701
- console.log(`
702
- ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Agent Search: "${query}"${utils.COLORS.reset}
703
- `);
704
-
705
- for (const [id, agent] of matches) {
706
- console.log(` ${utils.COLORS.cyan}${id}${utils.COLORS.reset} - ${agent.description}`);
707
- }
708
-
709
- console.log();
710
- utils.print.dim(`${matches.length} agents found`);
711
- }
712
-
713
- /**
714
- * Show agent help
715
- */
716
- function showHelp() {
717
- console.log(`
718
- ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Agent${utils.COLORS.reset}
719
- ${utils.COLORS.dim}Work with specialized AI agents${utils.COLORS.reset}
720
-
721
- ${utils.COLORS.bold}Usage:${utils.COLORS.reset}
722
- bootspring agent <command> [args]
723
-
724
- ${utils.COLORS.bold}Commands:${utils.COLORS.reset}
725
- ${utils.COLORS.cyan}list${utils.COLORS.reset} List all available agents
726
- ${utils.COLORS.cyan}show${utils.COLORS.reset} <name> Show agent details
727
- ${utils.COLORS.cyan}invoke${utils.COLORS.reset} <name> Invoke an agent
728
- ${utils.COLORS.cyan}search${utils.COLORS.reset} <query> Search for agents
729
-
730
- ${utils.COLORS.bold}Examples:${utils.COLORS.reset}
731
- bootspring agent list
732
- bootspring agent show security-expert
733
- bootspring agent invoke database-expert --topic "optimize queries"
734
- bootspring agent search performance
735
- `);
736
- }
737
-
738
- /**
739
- * Run agent command
740
- */
741
- async function run(args) {
742
- const subcommand = args[0] || 'list';
743
- const subargs = args.slice(1);
744
-
745
- switch (subcommand) {
746
- case 'list':
747
- case 'ls':
748
- await listAgents();
749
- break;
750
-
751
- case 'show':
752
- case 'info':
753
- if (!subargs[0]) {
754
- utils.print.error('Please specify an agent name');
755
- utils.print.dim('Usage: bootspring agent show <name>');
756
- return;
757
- }
758
- await showAgent(subargs[0]);
759
- break;
760
-
761
- case 'invoke':
762
- case 'use':
763
- case 'call':
764
- if (!subargs[0]) {
765
- utils.print.error('Please specify an agent name');
766
- utils.print.dim('Usage: bootspring agent invoke <name> --topic "your question"');
767
- return;
768
- }
769
- await invokeAgent(subargs[0], subargs.slice(1));
770
- break;
771
-
772
- case 'search':
773
- case 'find':
774
- if (!subargs[0]) {
775
- utils.print.error('Please specify a search query');
776
- utils.print.dim('Usage: bootspring agent search <query>');
777
- return;
778
- }
779
- await searchAgents(subargs.join(' '));
780
- break;
781
-
782
- case 'help':
783
- case '-h':
784
- case '--help':
785
- showHelp();
786
- break;
787
-
788
- default:
789
- // Check if it's an agent name (shortcut for show)
790
- if (AGENTS[subcommand]) {
791
- await showAgent(subcommand);
792
- } else {
793
- utils.print.error(`Unknown subcommand: ${subcommand}`);
794
- showHelp();
795
- }
796
- }
797
- }
798
-
799
- module.exports = { run, AGENTS, listAgents, showAgent, invokeAgent, searchAgents };