@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,964 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Bootspring Intelligence Orchestrator
5
+ *
6
+ * Coordinates agent execution based on:
7
+ * - Project lifecycle phase
8
+ * - Detected events and triggers
9
+ * - Task requirements
10
+ * - Agent collaboration patterns
11
+ *
12
+ * @package bootspring
13
+ * @module intelligence/orchestrator
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const telemetry = require('../core/telemetry');
19
+
20
+ // Paths (resolved relative to bootspring package)
21
+ function getPaths() {
22
+ // Find the bootspring directory
23
+ let bootspringDir = __dirname;
24
+ while (!fs.existsSync(path.join(bootspringDir, 'package.json')) && bootspringDir !== '/') {
25
+ bootspringDir = path.dirname(bootspringDir);
26
+ }
27
+
28
+ const projectRoot = process.cwd();
29
+
30
+ return {
31
+ bootspringDir,
32
+ projectRoot,
33
+ agentsDir: path.join(bootspringDir, 'agents', 'profiles'),
34
+ statePath: path.join(projectRoot, '.bootspring', 'orchestrator-state.json'),
35
+ roadmapPath: path.join(projectRoot, 'ROADMAP.md')
36
+ };
37
+ }
38
+
39
+ // Colors
40
+ const c = {
41
+ reset: '\x1b[0m',
42
+ bold: '\x1b[1m',
43
+ dim: '\x1b[2m',
44
+ green: '\x1b[32m',
45
+ blue: '\x1b[34m',
46
+ yellow: '\x1b[33m',
47
+ cyan: '\x1b[36m',
48
+ red: '\x1b[31m',
49
+ magenta: '\x1b[35m'
50
+ };
51
+
52
+ /**
53
+ * Agent lifecycle phase mapping
54
+ * Maps project phases to recommended agents
55
+ */
56
+ const PHASE_AGENTS = {
57
+ 1: {
58
+ name: 'Foundation',
59
+ description: 'Initial setup, planning, and architecture decisions',
60
+ primary: ['architecture-expert', 'database-expert'],
61
+ supporting: ['security-expert', 'devops-expert']
62
+ },
63
+ 2: {
64
+ name: 'Requirements',
65
+ description: 'Data modeling, research, and specification',
66
+ primary: ['database-expert', 'api-expert'],
67
+ supporting: ['testing-expert']
68
+ },
69
+ 3: {
70
+ name: 'Design',
71
+ description: 'UI/UX, database schema, API contracts',
72
+ primary: ['frontend-expert', 'ui-ux-expert', 'database-expert', 'api-expert'],
73
+ supporting: ['security-expert']
74
+ },
75
+ 4: {
76
+ name: 'Implementation',
77
+ description: 'Core feature development',
78
+ primary: ['backend-expert', 'frontend-expert', 'database-expert'],
79
+ supporting: ['performance-expert', 'code-review-expert']
80
+ },
81
+ 5: {
82
+ name: 'Testing',
83
+ description: 'Quality assurance and security review',
84
+ primary: ['testing-expert', 'security-expert', 'code-review-expert'],
85
+ supporting: ['performance-expert']
86
+ },
87
+ 6: {
88
+ name: 'Deployment',
89
+ description: 'CI/CD, infrastructure, and monitoring',
90
+ primary: ['devops-expert', 'vercel-expert'],
91
+ supporting: ['security-expert', 'performance-expert']
92
+ },
93
+ 7: {
94
+ name: 'Launch',
95
+ description: 'Go-live and post-launch optimization',
96
+ primary: ['devops-expert', 'performance-expert'],
97
+ supporting: ['security-expert']
98
+ }
99
+ };
100
+
101
+ /**
102
+ * Technical context triggers
103
+ * Maps keywords to recommended agents
104
+ */
105
+ const TECHNICAL_TRIGGERS = {
106
+ 'api': {
107
+ agents: ['api-expert', 'backend-expert', 'security-expert'],
108
+ indicators: ['api', 'endpoint', 'REST', 'GraphQL', 'route', 'handler'],
109
+ skills: ['api/route-handler', 'api/server-action']
110
+ },
111
+ 'database': {
112
+ agents: ['database-expert', 'backend-expert'],
113
+ indicators: ['database', 'schema', 'prisma', 'migration', 'query', 'model', 'sql'],
114
+ skills: ['database/prisma']
115
+ },
116
+ 'frontend': {
117
+ agents: ['frontend-expert', 'ui-ux-expert'],
118
+ indicators: ['component', 'react', 'tailwind', 'ui', 'ux', 'page', 'layout', 'css'],
119
+ skills: []
120
+ },
121
+ 'authentication': {
122
+ agents: ['security-expert', 'backend-expert'],
123
+ indicators: ['auth', 'login', 'signup', 'session', 'clerk', 'jwt', 'oauth', 'password'],
124
+ skills: ['auth/clerk', 'auth/nextauth']
125
+ },
126
+ 'security': {
127
+ agents: ['security-expert'],
128
+ indicators: ['security', 'vulnerability', 'OWASP', 'xss', 'csrf', 'injection', 'sanitize'],
129
+ skills: []
130
+ },
131
+ 'performance': {
132
+ agents: ['performance-expert', 'database-expert'],
133
+ indicators: ['performance', 'speed', 'optimization', 'cache', 'slow', 'memory', 'bundle'],
134
+ skills: []
135
+ },
136
+ 'deployment': {
137
+ agents: ['devops-expert', 'vercel-expert'],
138
+ indicators: ['deploy', 'CI/CD', 'docker', 'kubernetes', 'production', 'vercel', 'hosting'],
139
+ skills: []
140
+ },
141
+ 'testing': {
142
+ agents: ['testing-expert', 'code-review-expert'],
143
+ indicators: ['test', 'jest', 'vitest', 'playwright', 'coverage', 'spec', 'mock'],
144
+ skills: ['testing/vitest']
145
+ },
146
+ 'payments': {
147
+ agents: ['backend-expert', 'security-expert'],
148
+ indicators: ['stripe', 'payment', 'subscription', 'checkout', 'billing', 'invoice'],
149
+ skills: ['payments/stripe']
150
+ }
151
+ };
152
+
153
+ /**
154
+ * Predefined workflows for common scenarios
155
+ */
156
+ const WORKFLOWS = {
157
+ 'feature-development': {
158
+ name: 'Feature Development',
159
+ description: 'End-to-end feature implementation workflow',
160
+ tier: 'free',
161
+ pack: null,
162
+ outcomes: ['Feature shipped with tests and review complete'],
163
+ completionSignals: ['PR merged', 'Tests passing in CI', 'Security/perf review completed'],
164
+ phases: [
165
+ { name: 'Design', agents: ['ui-ux-expert', 'frontend-expert', 'api-expert'], duration: '1-2 days' },
166
+ { name: 'Implementation', agents: ['backend-expert', 'frontend-expert', 'database-expert'], duration: '2-5 days' },
167
+ { name: 'Testing', agents: ['testing-expert', 'code-review-expert'], duration: '1-2 days' },
168
+ { name: 'Review', agents: ['security-expert', 'performance-expert'], duration: '1 day' }
169
+ ]
170
+ },
171
+ 'security-audit': {
172
+ name: 'Security Audit',
173
+ description: 'Comprehensive security review',
174
+ tier: 'free',
175
+ pack: null,
176
+ outcomes: ['Critical/high vulnerabilities remediated'],
177
+ completionSignals: ['Security findings closed', 'Post-fix verification complete'],
178
+ phases: [
179
+ { name: 'Code Review', agents: ['code-review-expert', 'security-expert'], duration: '2-3 days' },
180
+ { name: 'Vulnerability Scan', agents: ['security-expert'], duration: '1 day' },
181
+ { name: 'Remediation', agents: ['security-expert', 'backend-expert'], duration: '2-5 days' }
182
+ ]
183
+ },
184
+ 'performance-optimization': {
185
+ name: 'Performance Optimization',
186
+ description: 'Full performance review and optimization',
187
+ tier: 'free',
188
+ pack: null,
189
+ outcomes: ['Performance regressions reduced and hotspots addressed'],
190
+ completionSignals: ['Baseline captured', 'Improvement report documented'],
191
+ phases: [
192
+ { name: 'Analysis', agents: ['performance-expert'], duration: '1-2 days' },
193
+ { name: 'Database', agents: ['database-expert', 'performance-expert'], duration: '2-3 days' },
194
+ { name: 'Frontend', agents: ['frontend-expert', 'performance-expert'], duration: '2-3 days' },
195
+ { name: 'Infrastructure', agents: ['devops-expert'], duration: '1-2 days' }
196
+ ]
197
+ },
198
+ 'api-development': {
199
+ name: 'API Development',
200
+ description: 'Design and implement new API endpoints',
201
+ tier: 'free',
202
+ pack: null,
203
+ outcomes: ['API endpoints delivered with security and test coverage'],
204
+ completionSignals: ['Contract approved', 'API tests passing', 'Auth/rate limits verified'],
205
+ phases: [
206
+ { name: 'Design', agents: ['api-expert', 'database-expert'], duration: '1 day' },
207
+ { name: 'Implementation', agents: ['backend-expert', 'api-expert'], duration: '2-3 days' },
208
+ { name: 'Security', agents: ['security-expert'], duration: '1 day' },
209
+ { name: 'Testing', agents: ['testing-expert'], duration: '1 day' }
210
+ ]
211
+ },
212
+ 'database-migration': {
213
+ name: 'Database Migration',
214
+ description: 'Schema changes and data migration',
215
+ tier: 'free',
216
+ pack: null,
217
+ outcomes: ['Migration completed safely with rollback readiness'],
218
+ completionSignals: ['Migration rehearsed', 'Rollback tested', 'Data integrity checks passing'],
219
+ phases: [
220
+ { name: 'Schema Design', agents: ['database-expert'], duration: '1 day' },
221
+ { name: 'Migration Plan', agents: ['database-expert', 'devops-expert'], duration: '1 day' },
222
+ { name: 'Implementation', agents: ['database-expert', 'backend-expert'], duration: '2-3 days' },
223
+ { name: 'Verification', agents: ['testing-expert'], duration: '1 day' }
224
+ ]
225
+ },
226
+ 'launch-preparation': {
227
+ name: 'Launch Preparation',
228
+ description: 'Pre-launch checklist and deployment',
229
+ tier: 'free',
230
+ pack: null,
231
+ outcomes: ['Launch readiness validated across quality, security, and ops'],
232
+ completionSignals: ['Release checklist complete', 'Monitoring configured', 'Rollback plan confirmed'],
233
+ phases: [
234
+ { name: 'Testing', agents: ['testing-expert', 'performance-expert'], duration: '2-3 days' },
235
+ { name: 'Security', agents: ['security-expert'], duration: '1-2 days' },
236
+ { name: 'Deployment', agents: ['devops-expert', 'vercel-expert'], duration: '1 day' },
237
+ { name: 'Monitoring', agents: ['devops-expert', 'performance-expert'], duration: '1 day' }
238
+ ]
239
+ },
240
+ 'launch-pack': {
241
+ name: 'Launch Pack',
242
+ description: 'Premium guided launch workflow from freeze to production validation',
243
+ tier: 'pro',
244
+ pack: 'launch',
245
+ outcomes: ['Production launch completed with no P0 regressions in first 48h'],
246
+ completionSignals: ['Change freeze checklist complete', 'Launch playbook executed', 'Post-launch metrics healthy'],
247
+ phases: [
248
+ { name: 'Readiness Gate', agents: ['testing-expert', 'security-expert', 'performance-expert'], duration: '1-2 days' },
249
+ { name: 'Cutover Plan', agents: ['devops-expert', 'architecture-expert'], duration: '1 day' },
250
+ { name: 'Go-Live', agents: ['devops-expert', 'vercel-expert'], duration: 'same day' },
251
+ { name: 'Stabilization', agents: ['performance-expert', 'backend-expert'], duration: '2 days' }
252
+ ]
253
+ },
254
+ 'reliability-pack': {
255
+ name: 'Reliability Pack',
256
+ description: 'Premium reliability hardening workflow for incidents and regressions',
257
+ tier: 'pro',
258
+ pack: 'reliability',
259
+ outcomes: ['Error budget burn reduced and top incident classes mitigated'],
260
+ completionSignals: ['SLOs defined', 'Top 3 failure modes mitigated', 'Runbooks verified in drill'],
261
+ phases: [
262
+ { name: 'Reliability Baseline', agents: ['performance-expert', 'devops-expert'], duration: '1 day' },
263
+ { name: 'Failure Mode Audit', agents: ['security-expert', 'backend-expert'], duration: '1-2 days' },
264
+ { name: 'Resilience Implementation', agents: ['backend-expert', 'database-expert'], duration: '2-4 days' },
265
+ { name: 'Game Day Validation', agents: ['testing-expert', 'devops-expert'], duration: '1 day' }
266
+ ]
267
+ },
268
+ 'growth-pack': {
269
+ name: 'Growth Pack',
270
+ description: 'Premium experimentation workflow for activation and conversion improvements',
271
+ tier: 'pro',
272
+ pack: 'growth',
273
+ outcomes: ['Primary growth KPI improved with statistically valid experiment result'],
274
+ completionSignals: ['North-star metric selected', 'Experiment shipped', 'Readout documented with decision'],
275
+ phases: [
276
+ { name: 'Metric Design', agents: ['architecture-expert', 'frontend-expert'], duration: '1 day' },
277
+ { name: 'Instrumentation', agents: ['backend-expert', 'api-expert'], duration: '1-2 days' },
278
+ { name: 'Experiment Build', agents: ['frontend-expert', 'backend-expert'], duration: '2-3 days' },
279
+ { name: 'Analysis & Decision', agents: ['code-review-expert', 'architecture-expert'], duration: '1 day' }
280
+ ]
281
+ }
282
+ };
283
+
284
+ /**
285
+ * Load orchestrator state from project
286
+ */
287
+ function loadState() {
288
+ const paths = getPaths();
289
+
290
+ if (fs.existsSync(paths.statePath)) {
291
+ try {
292
+ return JSON.parse(fs.readFileSync(paths.statePath, 'utf8'));
293
+ } catch (e) {
294
+ return createDefaultState();
295
+ }
296
+ }
297
+ return createDefaultState();
298
+ }
299
+
300
+ /**
301
+ * Create default orchestrator state
302
+ */
303
+ function createDefaultState() {
304
+ return {
305
+ lastAnalysis: null,
306
+ currentPhase: 1,
307
+ activeWorkflow: null,
308
+ workflowStep: 0,
309
+ workflowSignals: {},
310
+ recentAgents: [],
311
+ suggestions: [],
312
+ history: []
313
+ };
314
+ }
315
+
316
+ function emitWorkflowTelemetry(event, payload = {}) {
317
+ const paths = getPaths();
318
+ try {
319
+ telemetry.emitEvent(event, payload, { projectRoot: paths.projectRoot });
320
+ } catch {
321
+ // Telemetry should not block workflow execution.
322
+ }
323
+ }
324
+
325
+ function getWorkflowSignalProgress(workflowName, state = null) {
326
+ const workflow = WORKFLOWS[workflowName];
327
+ if (!workflow) return null;
328
+
329
+ const activeState = state || loadState();
330
+ const completed = new Set(activeState.workflowSignals?.[workflowName] || []);
331
+ const signals = workflow.completionSignals || [];
332
+ const pending = signals.filter(signal => !completed.has(signal));
333
+
334
+ return {
335
+ workflow: workflowName,
336
+ totalSignals: signals.length,
337
+ completedSignals: Array.from(completed),
338
+ pendingSignals: pending
339
+ };
340
+ }
341
+
342
+ function resolveSignal(workflow, signalRef, stateSignals = []) {
343
+ const signals = workflow.completionSignals || [];
344
+ if (signals.length === 0) return null;
345
+
346
+ const completedSet = new Set(stateSignals);
347
+ const trimmed = String(signalRef || '').trim();
348
+ if (!trimmed) {
349
+ return signals.find(signal => !completedSet.has(signal)) || null;
350
+ }
351
+
352
+ const byIndex = Number(trimmed);
353
+ if (Number.isInteger(byIndex) && byIndex >= 1 && byIndex <= signals.length) {
354
+ return signals[byIndex - 1];
355
+ }
356
+
357
+ const lower = trimmed.toLowerCase();
358
+ return signals.find(signal => signal.toLowerCase().includes(lower)) || null;
359
+ }
360
+
361
+ /**
362
+ * Save orchestrator state
363
+ */
364
+ function saveState(state) {
365
+ const paths = getPaths();
366
+
367
+ // Ensure .bootspring directory exists
368
+ const stateDir = path.dirname(paths.statePath);
369
+ if (!fs.existsSync(stateDir)) {
370
+ fs.mkdirSync(stateDir, { recursive: true });
371
+ }
372
+
373
+ state.lastUpdated = new Date().toISOString();
374
+ fs.writeFileSync(paths.statePath, JSON.stringify(state, null, 2));
375
+ }
376
+
377
+ /**
378
+ * Get current phase from ROADMAP.md
379
+ */
380
+ function getCurrentPhase() {
381
+ const paths = getPaths();
382
+
383
+ try {
384
+ if (!fs.existsSync(paths.roadmapPath)) return 1;
385
+
386
+ const content = fs.readFileSync(paths.roadmapPath, 'utf8');
387
+
388
+ for (let i = 1; i <= 7; i++) {
389
+ const phaseRegex = new RegExp(`## Phase ${i}[\\s\\S]*?(?=## Phase|$)`, 'i');
390
+ const match = content.match(phaseRegex);
391
+ if (match) {
392
+ const incomplete = match[0].match(/- \[ \]/g);
393
+ if (incomplete && incomplete.length > 0) {
394
+ return i;
395
+ }
396
+ }
397
+ }
398
+ return 7;
399
+ } catch (e) {
400
+ return 1;
401
+ }
402
+ }
403
+
404
+ /**
405
+ * Get the AGENTS object from cli/agent.js
406
+ */
407
+ function getAgentsRegistry() {
408
+ try {
409
+ const agentModule = require('../cli/agent');
410
+ return agentModule.AGENTS || {};
411
+ } catch (e) {
412
+ // Fallback to checking files
413
+ return null;
414
+ }
415
+ }
416
+
417
+ /**
418
+ * Check if an agent exists
419
+ */
420
+ function agentExists(agentName) {
421
+ // First try the AGENTS registry
422
+ const registry = getAgentsRegistry();
423
+ if (registry) {
424
+ return agentName in registry;
425
+ }
426
+
427
+ // Fallback to checking files
428
+ const paths = getPaths();
429
+ const agentFile = path.join(paths.agentsDir, `${agentName}.md`);
430
+ return fs.existsSync(agentFile);
431
+ }
432
+
433
+ /**
434
+ * Get list of available agents
435
+ */
436
+ function getAvailableAgents() {
437
+ // First try the AGENTS registry
438
+ const registry = getAgentsRegistry();
439
+ if (registry) {
440
+ return Object.keys(registry);
441
+ }
442
+
443
+ // Fallback to checking files
444
+ const paths = getPaths();
445
+
446
+ if (!fs.existsSync(paths.agentsDir)) {
447
+ return [];
448
+ }
449
+
450
+ return fs.readdirSync(paths.agentsDir)
451
+ .filter(f => f.endsWith('.md'))
452
+ .map(f => f.replace('.md', ''));
453
+ }
454
+
455
+ /**
456
+ * Analyze context and suggest agents
457
+ */
458
+ function analyzeContext(input = '') {
459
+ const state = loadState();
460
+ const currentPhase = getCurrentPhase();
461
+ const suggestions = [];
462
+ const matchedSkills = [];
463
+
464
+ // Add phase-based agents
465
+ const phaseConfig = PHASE_AGENTS[currentPhase];
466
+ if (phaseConfig) {
467
+ phaseConfig.primary.forEach(agent => {
468
+ if (agentExists(agent)) {
469
+ suggestions.push({
470
+ agent,
471
+ reason: `Phase ${currentPhase} (${phaseConfig.name}) - primary agent`,
472
+ priority: 'high',
473
+ source: 'phase'
474
+ });
475
+ }
476
+ });
477
+ }
478
+
479
+ // Analyze input for technical triggers
480
+ const inputLower = input.toLowerCase();
481
+
482
+ Object.entries(TECHNICAL_TRIGGERS).forEach(([context, config]) => {
483
+ const matches = config.indicators.filter(ind => inputLower.includes(ind.toLowerCase()));
484
+ if (matches.length > 0) {
485
+ config.agents.forEach(agent => {
486
+ if (agentExists(agent) && !suggestions.find(s => s.agent === agent)) {
487
+ suggestions.push({
488
+ agent,
489
+ reason: `Detected ${context} context (matched: ${matches.join(', ')})`,
490
+ priority: matches.length >= 2 ? 'high' : 'medium',
491
+ source: 'trigger'
492
+ });
493
+ }
494
+ });
495
+
496
+ // Collect matching skills
497
+ if (config.skills) {
498
+ matchedSkills.push(...config.skills);
499
+ }
500
+ }
501
+ });
502
+
503
+ // Sort by priority
504
+ const priorityOrder = { high: 0, medium: 1, low: 2 };
505
+ suggestions.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
506
+
507
+ // Update state
508
+ state.currentPhase = currentPhase;
509
+ state.lastAnalysis = new Date().toISOString();
510
+ state.suggestions = suggestions;
511
+ saveState(state);
512
+
513
+ return {
514
+ phase: currentPhase,
515
+ phaseConfig,
516
+ suggestions,
517
+ skills: [...new Set(matchedSkills)],
518
+ input: input.substring(0, 100)
519
+ };
520
+ }
521
+
522
+ /**
523
+ * Get workflow by name
524
+ */
525
+ function getWorkflow(name) {
526
+ return WORKFLOWS[name] || null;
527
+ }
528
+
529
+ /**
530
+ * List all workflows
531
+ */
532
+ function listWorkflows() {
533
+ return Object.entries(WORKFLOWS).map(([key, workflow]) => ({
534
+ key,
535
+ name: workflow.name,
536
+ description: workflow.description,
537
+ phaseCount: workflow.phases.length,
538
+ tier: workflow.tier || 'free',
539
+ pack: workflow.pack || null,
540
+ outcomes: workflow.outcomes || [],
541
+ completionSignals: workflow.completionSignals || []
542
+ }));
543
+ }
544
+
545
+ /**
546
+ * Start a workflow
547
+ */
548
+ function startWorkflow(workflowName) {
549
+ const workflow = WORKFLOWS[workflowName];
550
+
551
+ if (!workflow) {
552
+ return { success: false, error: `Unknown workflow: ${workflowName}` };
553
+ }
554
+
555
+ const state = loadState();
556
+ state.activeWorkflow = workflowName;
557
+ state.workflowStep = 0;
558
+ state.workflowSignals = state.workflowSignals || {};
559
+ state.workflowSignals[workflowName] = state.workflowSignals[workflowName] || [];
560
+ state.history.push({
561
+ action: 'workflow_start',
562
+ workflow: workflowName,
563
+ timestamp: new Date().toISOString()
564
+ });
565
+ saveState(state);
566
+ emitWorkflowTelemetry('workflow_started', {
567
+ workflow: workflowName,
568
+ tier: workflow.tier || 'free',
569
+ pack: workflow.pack || null,
570
+ phaseCount: workflow.phases.length
571
+ });
572
+ if ((workflow.tier || 'free') !== 'free') {
573
+ emitWorkflowTelemetry('premium_unlocked', {
574
+ capability: 'workflow_pack',
575
+ workflow: workflowName,
576
+ tier: workflow.tier || 'free',
577
+ pack: workflow.pack || null,
578
+ source: 'workflow_start'
579
+ });
580
+ }
581
+
582
+ return {
583
+ success: true,
584
+ workflowKey: workflowName,
585
+ workflow: workflow.name,
586
+ currentPhase: workflow.phases[0],
587
+ totalPhases: workflow.phases.length,
588
+ completionSignals: workflow.completionSignals || []
589
+ };
590
+ }
591
+
592
+ /**
593
+ * Advance workflow to next step
594
+ */
595
+ function advanceWorkflow() {
596
+ const state = loadState();
597
+
598
+ if (!state.activeWorkflow) {
599
+ return { success: false, error: 'No active workflow' };
600
+ }
601
+
602
+ const workflow = WORKFLOWS[state.activeWorkflow];
603
+ const activeWorkflowName = state.activeWorkflow;
604
+ state.workflowStep += 1;
605
+
606
+ if (state.workflowStep >= workflow.phases.length) {
607
+ // Workflow complete
608
+ state.history.push({
609
+ action: 'workflow_complete',
610
+ workflow: state.activeWorkflow,
611
+ timestamp: new Date().toISOString()
612
+ });
613
+ state.activeWorkflow = null;
614
+ state.workflowStep = 0;
615
+ saveState(state);
616
+ const signalProgress = getWorkflowSignalProgress(activeWorkflowName, state);
617
+ emitWorkflowTelemetry('workflow_completed', {
618
+ workflow: activeWorkflowName,
619
+ tier: workflow.tier || 'free',
620
+ pack: workflow.pack || null,
621
+ phaseCount: workflow.phases.length,
622
+ completedSignalCount: signalProgress?.completedSignals?.length || 0,
623
+ totalSignalCount: signalProgress?.totalSignals || 0
624
+ });
625
+
626
+ return { success: true, complete: true };
627
+ }
628
+
629
+ saveState(state);
630
+ emitWorkflowTelemetry('workflow_step_advanced', {
631
+ workflow: activeWorkflowName,
632
+ tier: workflow.tier || 'free',
633
+ pack: workflow.pack || null,
634
+ step: state.workflowStep + 1,
635
+ totalSteps: workflow.phases.length,
636
+ phaseName: workflow.phases[state.workflowStep]?.name
637
+ });
638
+
639
+ return {
640
+ success: true,
641
+ complete: false,
642
+ currentPhase: workflow.phases[state.workflowStep],
643
+ step: state.workflowStep + 1,
644
+ totalSteps: workflow.phases.length
645
+ };
646
+ }
647
+
648
+ function markWorkflowCheckpoint(workflowName, signalRef = '') {
649
+ const state = loadState();
650
+ const targetWorkflow = workflowName || state.activeWorkflow;
651
+ if (!targetWorkflow) {
652
+ return { success: false, error: 'No workflow provided and no active workflow' };
653
+ }
654
+
655
+ const workflow = WORKFLOWS[targetWorkflow];
656
+ if (!workflow) {
657
+ return { success: false, error: `Unknown workflow: ${targetWorkflow}` };
658
+ }
659
+
660
+ state.workflowSignals = state.workflowSignals || {};
661
+ const existing = state.workflowSignals[targetWorkflow] || [];
662
+ const signal = resolveSignal(workflow, signalRef, existing);
663
+ if (!signal) {
664
+ return {
665
+ success: false,
666
+ error: `Signal not found for workflow: ${targetWorkflow}`,
667
+ availableSignals: workflow.completionSignals || []
668
+ };
669
+ }
670
+
671
+ const alreadyCompleted = existing.includes(signal);
672
+ if (!alreadyCompleted) {
673
+ existing.push(signal);
674
+ state.workflowSignals[targetWorkflow] = existing;
675
+ state.history.push({
676
+ action: 'workflow_checkpoint',
677
+ workflow: targetWorkflow,
678
+ signal,
679
+ timestamp: new Date().toISOString()
680
+ });
681
+ saveState(state);
682
+ emitWorkflowTelemetry('workflow_checkpoint_completed', {
683
+ workflow: targetWorkflow,
684
+ tier: workflow.tier || 'free',
685
+ pack: workflow.pack || null,
686
+ signal
687
+ });
688
+ if (workflow.pack) {
689
+ emitWorkflowTelemetry('pack_signal_checkpoint', {
690
+ workflow: targetWorkflow,
691
+ pack: workflow.pack,
692
+ signal
693
+ });
694
+ }
695
+ }
696
+
697
+ return {
698
+ success: true,
699
+ workflow: targetWorkflow,
700
+ signal,
701
+ alreadyCompleted,
702
+ progress: getWorkflowSignalProgress(targetWorkflow, state)
703
+ };
704
+ }
705
+
706
+ /**
707
+ * Display analysis results
708
+ */
709
+ function displayAnalysis(analysis) {
710
+ console.log(`\n${c.cyan}+--------------------------------------------------------+${c.reset}`);
711
+ console.log(`${c.cyan}|${c.reset}${c.bold} Bootspring Intelligence Analysis ${c.reset}${c.cyan}|${c.reset}`);
712
+ console.log(`${c.cyan}+--------------------------------------------------------+${c.reset}\n`);
713
+
714
+ console.log(`${c.bold}Current Phase:${c.reset} ${analysis.phase} - ${analysis.phaseConfig?.name || 'Unknown'}`);
715
+ if (analysis.phaseConfig?.description) {
716
+ console.log(`${c.dim}${analysis.phaseConfig.description}${c.reset}`);
717
+ }
718
+ console.log('');
719
+
720
+ if (analysis.suggestions.length === 0) {
721
+ console.log(`${c.yellow}No specific agent suggestions for this context${c.reset}`);
722
+ console.log(`${c.dim}Try adding more context about what you're working on${c.reset}`);
723
+ return;
724
+ }
725
+
726
+ console.log(`${c.bold}Suggested Agents:${c.reset}\n`);
727
+
728
+ const high = analysis.suggestions.filter(s => s.priority === 'high');
729
+ const medium = analysis.suggestions.filter(s => s.priority === 'medium');
730
+
731
+ if (high.length > 0) {
732
+ console.log(` ${c.green}High Priority:${c.reset}`);
733
+ high.forEach(s => {
734
+ console.log(` ${c.green}*${c.reset} ${s.agent}`);
735
+ console.log(` ${c.dim}${s.reason}${c.reset}`);
736
+ });
737
+ console.log('');
738
+ }
739
+
740
+ if (medium.length > 0) {
741
+ console.log(` ${c.yellow}Medium Priority:${c.reset}`);
742
+ medium.forEach(s => {
743
+ console.log(` ${c.yellow}*${c.reset} ${s.agent}`);
744
+ console.log(` ${c.dim}${s.reason}${c.reset}`);
745
+ });
746
+ console.log('');
747
+ }
748
+
749
+ if (analysis.skills.length > 0) {
750
+ console.log(`${c.bold}Relevant Skills:${c.reset}`);
751
+ analysis.skills.forEach(skill => {
752
+ console.log(` ${c.cyan}*${c.reset} ${skill}`);
753
+ });
754
+ console.log('');
755
+ }
756
+
757
+ console.log(`${c.dim}Invoke an agent: bootspring agent invoke <name>${c.reset}`);
758
+ }
759
+
760
+ /**
761
+ * Display workflow details
762
+ */
763
+ function displayWorkflow(workflowName) {
764
+ const workflow = WORKFLOWS[workflowName];
765
+
766
+ if (!workflow) {
767
+ console.log(`${c.red}Unknown workflow: ${workflowName}${c.reset}`);
768
+ console.log('Available: ' + Object.keys(WORKFLOWS).join(', '));
769
+ return;
770
+ }
771
+
772
+ console.log(`\n${c.cyan}+--------------------------------------------------------+${c.reset}`);
773
+ console.log(`${c.cyan}|${c.reset}${c.bold} Workflow: ${workflow.name.padEnd(41)}${c.reset}${c.cyan}|${c.reset}`);
774
+ console.log(`${c.cyan}+--------------------------------------------------------+${c.reset}\n`);
775
+
776
+ console.log(`${c.bold}Description:${c.reset} ${workflow.description}\n`);
777
+ console.log(`${c.bold}Phases:${c.reset}\n`);
778
+
779
+ workflow.phases.forEach((phase, i) => {
780
+ const isLast = i === workflow.phases.length - 1;
781
+ const prefix = isLast ? '\\--' : '|--';
782
+ const line = isLast ? ' ' : '| ';
783
+
784
+ console.log(` ${prefix} ${c.cyan}${phase.name}${c.reset} ${c.dim}(${phase.duration})${c.reset}`);
785
+ phase.agents.forEach((agent, j) => {
786
+ const agentPrefix = j === phase.agents.length - 1 ? '\\-' : '|-';
787
+ const exists = agentExists(agent);
788
+ const status = exists ? `${c.green}*${c.reset}` : `${c.red}o${c.reset}`;
789
+ console.log(` ${line} ${agentPrefix} ${status} ${agent}`);
790
+ });
791
+ });
792
+
793
+ console.log('');
794
+ }
795
+
796
+ /**
797
+ * Display orchestrator status
798
+ */
799
+ function displayStatus() {
800
+ const state = loadState();
801
+ const currentPhase = getCurrentPhase();
802
+ const availableAgents = getAvailableAgents();
803
+
804
+ console.log(`\n${c.cyan}+--------------------------------------------------------+${c.reset}`);
805
+ console.log(`${c.cyan}|${c.reset}${c.bold} Orchestrator Status ${c.reset}${c.cyan}|${c.reset}`);
806
+ console.log(`${c.cyan}+--------------------------------------------------------+${c.reset}\n`);
807
+
808
+ console.log(`${c.bold}Current Phase:${c.reset} ${currentPhase} - ${PHASE_AGENTS[currentPhase]?.name || 'Unknown'}`);
809
+ console.log(`${c.bold}Last Analysis:${c.reset} ${state.lastAnalysis || 'Never'}`);
810
+ console.log(`${c.bold}Active Workflow:${c.reset} ${state.activeWorkflow || 'None'}`);
811
+
812
+ if (state.activeWorkflow) {
813
+ const workflow = WORKFLOWS[state.activeWorkflow];
814
+ console.log(`${c.bold}Workflow Step:${c.reset} ${state.workflowStep + 1}/${workflow.phases.length}`);
815
+ }
816
+
817
+ if (state.suggestions.length > 0) {
818
+ console.log(`\n${c.bold}Last Suggestions:${c.reset}`);
819
+ state.suggestions.slice(0, 5).forEach(s => {
820
+ console.log(` * ${s.agent} (${s.priority})`);
821
+ });
822
+ }
823
+
824
+ console.log(`\n${c.bold}Agent Inventory:${c.reset} ${availableAgents.length} agents available`);
825
+ console.log('');
826
+ }
827
+
828
+ /**
829
+ * Show help
830
+ */
831
+ function showHelp() {
832
+ console.log(`
833
+ ${c.bold}Bootspring Intelligence Orchestrator${c.reset}
834
+
835
+ ${c.cyan}Usage:${c.reset}
836
+ node orchestrator.js <command> [options]
837
+
838
+ ${c.cyan}Commands:${c.reset}
839
+ analyze [context] Analyze context and suggest agents
840
+ suggest [context] Alias for analyze
841
+ workflow <name> Show workflow details
842
+ workflows List available workflows
843
+ start <workflow> Start a workflow
844
+ next Advance to next workflow step
845
+ status Show orchestrator status
846
+ agents List available agents
847
+ help Show this help
848
+
849
+ ${c.cyan}Examples:${c.reset}
850
+ node orchestrator.js analyze "building authentication api"
851
+ node orchestrator.js workflow feature-development
852
+ node orchestrator.js start feature-development
853
+ node orchestrator.js status
854
+ `);
855
+ }
856
+
857
+ // CLI execution
858
+ if (require.main === module) {
859
+ const args = process.argv.slice(2);
860
+ const command = args[0] || 'help';
861
+
862
+ switch (command) {
863
+ case 'analyze':
864
+ case 'suggest': {
865
+ const context = args.slice(1).join(' ');
866
+ const analysis = analyzeContext(context);
867
+ displayAnalysis(analysis);
868
+ break;
869
+ }
870
+
871
+ case 'workflow': {
872
+ const workflowName = args[1];
873
+ if (!workflowName) {
874
+ console.log(`\n${c.bold}Available Workflows:${c.reset}\n`);
875
+ listWorkflows().forEach(w => {
876
+ console.log(` ${c.cyan}${w.key}${c.reset} - ${w.description}`);
877
+ });
878
+ console.log('');
879
+ } else {
880
+ displayWorkflow(workflowName);
881
+ }
882
+ break;
883
+ }
884
+
885
+ case 'workflows':
886
+ console.log(`\n${c.bold}Available Workflows:${c.reset}\n`);
887
+ listWorkflows().forEach(w => {
888
+ console.log(` ${c.cyan}${w.key}${c.reset}`);
889
+ console.log(` ${w.description}`);
890
+ console.log(` ${c.dim}${w.phaseCount} phases${c.reset}\n`);
891
+ });
892
+ break;
893
+
894
+ case 'start': {
895
+ const startName = args[1];
896
+ if (!startName) {
897
+ console.log(`${c.red}Usage: orchestrator.js start <workflow-name>${c.reset}`);
898
+ } else {
899
+ const result = startWorkflow(startName);
900
+ if (result.success) {
901
+ console.log(`${c.green}Started workflow:${c.reset} ${result.workflow}`);
902
+ console.log(`${c.bold}Current Phase:${c.reset} ${result.currentPhase.name}`);
903
+ console.log(`${c.bold}Agents:${c.reset} ${result.currentPhase.agents.join(', ')}`);
904
+ } else {
905
+ console.log(`${c.red}Error: ${result.error}${c.reset}`);
906
+ }
907
+ }
908
+ break;
909
+ }
910
+
911
+ case 'next': {
912
+ const advanceResult = advanceWorkflow();
913
+ if (advanceResult.success) {
914
+ if (advanceResult.complete) {
915
+ console.log(`${c.green}Workflow complete!${c.reset}`);
916
+ } else {
917
+ console.log(`${c.green}Advanced to step ${advanceResult.step}/${advanceResult.totalSteps}${c.reset}`);
918
+ console.log(`${c.bold}Phase:${c.reset} ${advanceResult.currentPhase.name}`);
919
+ console.log(`${c.bold}Agents:${c.reset} ${advanceResult.currentPhase.agents.join(', ')}`);
920
+ }
921
+ } else {
922
+ console.log(`${c.red}Error: ${advanceResult.error}${c.reset}`);
923
+ }
924
+ break;
925
+ }
926
+
927
+ case 'status':
928
+ displayStatus();
929
+ break;
930
+
931
+ case 'agents': {
932
+ const agents = getAvailableAgents();
933
+ console.log(`\n${c.bold}Available Agents (${agents.length}):${c.reset}\n`);
934
+ agents.forEach(agent => {
935
+ console.log(` * ${agent}`);
936
+ });
937
+ console.log('');
938
+ break;
939
+ }
940
+
941
+ case 'help':
942
+ default:
943
+ showHelp();
944
+ }
945
+ }
946
+
947
+ // Export for use as module
948
+ module.exports = {
949
+ analyzeContext,
950
+ getWorkflow,
951
+ listWorkflows,
952
+ startWorkflow,
953
+ advanceWorkflow,
954
+ markWorkflowCheckpoint,
955
+ getWorkflowSignalProgress,
956
+ loadState,
957
+ saveState,
958
+ getCurrentPhase,
959
+ agentExists,
960
+ getAvailableAgents,
961
+ PHASE_AGENTS,
962
+ TECHNICAL_TRIGGERS,
963
+ WORKFLOWS
964
+ };