@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
package/cli/memory.js ADDED
@@ -0,0 +1,303 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Bootspring Memory Command
5
+ * View and manage git-based project memory
6
+ *
7
+ * @package bootspring
8
+ * @command memory
9
+ */
10
+
11
+ const path = require('path');
12
+ const gitMemory = require('../intelligence/git-memory');
13
+ const utils = require('../core/utils');
14
+
15
+ // Colors
16
+ const c = {
17
+ reset: '\x1b[0m',
18
+ bold: '\x1b[1m',
19
+ dim: '\x1b[2m',
20
+ green: '\x1b[32m',
21
+ blue: '\x1b[34m',
22
+ yellow: '\x1b[33m',
23
+ cyan: '\x1b[36m',
24
+ red: '\x1b[31m',
25
+ magenta: '\x1b[35m'
26
+ };
27
+
28
+ /**
29
+ * Display learnings summary
30
+ */
31
+ function showSummary(options = {}) {
32
+ const { limit = 30, since = '3 months ago' } = options;
33
+
34
+ console.log(`\n${c.cyan}${c.bold}⚡ Project Memory${c.reset}\n`);
35
+
36
+ if (!gitMemory.isGitRepo()) {
37
+ console.log(`${c.red}Not a git repository${c.reset}`);
38
+ return;
39
+ }
40
+
41
+ const result = gitMemory.extractLearnings({ limit, since });
42
+
43
+ if (result.error) {
44
+ console.log(`${c.red}Error: ${result.error}${c.reset}`);
45
+ return;
46
+ }
47
+
48
+ if (result.learnings.length === 0) {
49
+ console.log(`${c.yellow}No significant learnings found in recent commits${c.reset}`);
50
+ console.log(`${c.dim}Try using more descriptive commit messages${c.reset}`);
51
+ return;
52
+ }
53
+
54
+ // Group by category
55
+ const grouped = gitMemory.groupByCategory(result.learnings);
56
+
57
+ console.log(`${c.dim}Extracted ${result.learnings.length} learnings from ${result.total} commits${c.reset}\n`);
58
+
59
+ for (const [category, items] of Object.entries(grouped)) {
60
+ const config = gitMemory.MEMORY_CATEGORIES[category] || { icon: '📝', label: category };
61
+
62
+ console.log(`${c.bold}${config.icon} ${config.label}${c.reset} (${items.length})`);
63
+
64
+ for (const item of items.slice(0, 3)) {
65
+ console.log(` ${c.dim}[${item.hash}]${c.reset} ${item.summary}`);
66
+ }
67
+
68
+ if (items.length > 3) {
69
+ console.log(` ${c.dim}... and ${items.length - 3} more${c.reset}`);
70
+ }
71
+
72
+ console.log('');
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Search learnings
78
+ */
79
+ function searchMemory(query) {
80
+ console.log(`\n${c.cyan}${c.bold}⚡ Search Memory${c.reset}\n`);
81
+
82
+ if (!query) {
83
+ console.log(`${c.red}Usage: bootspring memory search <query>${c.reset}`);
84
+ return;
85
+ }
86
+
87
+ const result = gitMemory.extractLearnings({ limit: 100 });
88
+ const matches = gitMemory.searchLearnings(result.learnings, query);
89
+
90
+ console.log(`${c.dim}Found ${matches.length} matches for "${query}"${c.reset}\n`);
91
+
92
+ for (const match of matches.slice(0, 15)) {
93
+ const icons = match.categories
94
+ .map(cat => gitMemory.MEMORY_CATEGORIES[cat]?.icon || '📝')
95
+ .join('');
96
+
97
+ console.log(`${icons} ${c.bold}${match.summary}${c.reset}`);
98
+ console.log(` ${c.dim}[${match.hash}] ${match.date}${c.reset}`);
99
+
100
+ if (match.details) {
101
+ const preview = match.details.split('\n')[0].slice(0, 80);
102
+ console.log(` ${c.dim}${preview}${c.reset}`);
103
+ }
104
+
105
+ console.log('');
106
+ }
107
+
108
+ if (matches.length > 15) {
109
+ console.log(`${c.dim}... and ${matches.length - 15} more matches${c.reset}`);
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Show learnings for specific files
115
+ */
116
+ function showForFiles(files) {
117
+ console.log(`\n${c.cyan}${c.bold}⚡ File Memory${c.reset}\n`);
118
+
119
+ if (!files || files.length === 0) {
120
+ console.log(`${c.red}Usage: bootspring memory files <file1> [file2...]${c.reset}`);
121
+ return;
122
+ }
123
+
124
+ const learnings = gitMemory.getLearningsForFiles(files);
125
+
126
+ if (learnings.length === 0) {
127
+ console.log(`${c.yellow}No learnings found for these files${c.reset}`);
128
+ return;
129
+ }
130
+
131
+ console.log(`${c.dim}Learnings related to: ${files.join(', ')}${c.reset}\n`);
132
+
133
+ for (const learning of learnings) {
134
+ const icons = learning.categories
135
+ .map(cat => gitMemory.MEMORY_CATEGORIES[cat]?.icon || '📝')
136
+ .join('');
137
+
138
+ console.log(`${icons} ${c.bold}${learning.summary}${c.reset}`);
139
+ console.log(` ${c.dim}[${learning.hash}] File: ${learning.file}${c.reset}`);
140
+ console.log('');
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Export learnings to markdown
146
+ */
147
+ function exportMarkdown(outputPath) {
148
+ const result = gitMemory.extractLearnings({ limit: 50 });
149
+
150
+ if (result.error) {
151
+ console.log(`${c.red}Error: ${result.error}${c.reset}`);
152
+ return;
153
+ }
154
+
155
+ const markdown = gitMemory.toMarkdown(result.learnings, {
156
+ title: 'Project Learnings',
157
+ maxPerCategory: 10
158
+ });
159
+
160
+ if (outputPath) {
161
+ require('fs').writeFileSync(outputPath, markdown);
162
+ console.log(`${c.green}Exported to ${outputPath}${c.reset}`);
163
+ } else {
164
+ console.log(markdown);
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Show repo stats
170
+ */
171
+ function showStats() {
172
+ console.log(`\n${c.cyan}${c.bold}⚡ Repository Stats${c.reset}\n`);
173
+
174
+ const stats = gitMemory.getRepoStats();
175
+
176
+ if (!stats) {
177
+ console.log(`${c.red}Not a git repository${c.reset}`);
178
+ return;
179
+ }
180
+
181
+ console.log(` ${c.bold}Total Commits:${c.reset} ${stats.totalCommits}`);
182
+ console.log(` ${c.bold}Contributors:${c.reset} ${stats.contributors}`);
183
+ console.log(` ${c.bold}Weekly Activity:${c.reset} ${stats.weeklyPace} commits`);
184
+ console.log(` ${c.bold}First Commit:${c.reset} ${stats.firstCommit || 'unknown'}`);
185
+ console.log('');
186
+
187
+ // Show category breakdown
188
+ const result = gitMemory.extractLearnings({ limit: 100 });
189
+ const grouped = gitMemory.groupByCategory(result.learnings);
190
+
191
+ console.log(`${c.bold}Learning Categories:${c.reset}\n`);
192
+
193
+ for (const [category, items] of Object.entries(grouped).sort((a, b) => b[1].length - a[1].length)) {
194
+ const config = gitMemory.MEMORY_CATEGORIES[category] || { icon: '📝', label: category };
195
+ const bar = '█'.repeat(Math.min(items.length, 20));
196
+ console.log(` ${config.icon} ${config.label.padEnd(20)} ${c.cyan}${bar}${c.reset} ${items.length}`);
197
+ }
198
+
199
+ console.log('');
200
+ }
201
+
202
+ /**
203
+ * Show help
204
+ */
205
+ function showHelp() {
206
+ console.log(`
207
+ ${c.bold}Bootspring Memory${c.reset}
208
+
209
+ Extract and search project learnings from git history.
210
+
211
+ ${c.cyan}Usage:${c.reset}
212
+ bootspring memory <command> [options]
213
+
214
+ ${c.cyan}Commands:${c.reset}
215
+ summary Show categorized learning summary (default)
216
+ search <query> Search learnings by keyword
217
+ files <file...> Show learnings for specific files
218
+ export [path] Export learnings to markdown
219
+ stats Show repository statistics
220
+ categories List memory categories
221
+ help Show this help
222
+
223
+ ${c.cyan}Options:${c.reset}
224
+ --limit <n> Number of commits to analyze (default: 30)
225
+ --since <date> How far back to look (default: "3 months ago")
226
+
227
+ ${c.cyan}Examples:${c.reset}
228
+ bootspring memory summary
229
+ bootspring memory search "authentication"
230
+ bootspring memory files src/api/auth.ts
231
+ bootspring memory export LEARNINGS.md
232
+ bootspring memory stats
233
+
234
+ ${c.cyan}Categories:${c.reset}
235
+ ${Object.entries(gitMemory.MEMORY_CATEGORIES)
236
+ .map(([key, cfg]) => ` ${cfg.icon} ${key.padEnd(15)} ${cfg.label}`)
237
+ .join('\n')}
238
+ `);
239
+ }
240
+
241
+ /**
242
+ * Main CLI handler
243
+ */
244
+ async function run(args) {
245
+ const command = args[0] || 'summary';
246
+
247
+ // Parse options
248
+ const options = {};
249
+ for (let i = 0; i < args.length; i++) {
250
+ if (args[i] === '--limit' && args[i + 1]) {
251
+ options.limit = parseInt(args[i + 1]);
252
+ }
253
+ if (args[i] === '--since' && args[i + 1]) {
254
+ options.since = args[i + 1];
255
+ }
256
+ }
257
+
258
+ switch (command) {
259
+ case 'summary':
260
+ case 'show':
261
+ showSummary(options);
262
+ break;
263
+
264
+ case 'search':
265
+ searchMemory(args.slice(1).filter(a => !a.startsWith('--')).join(' '));
266
+ break;
267
+
268
+ case 'files':
269
+ case 'file':
270
+ showForFiles(args.slice(1).filter(a => !a.startsWith('--')));
271
+ break;
272
+
273
+ case 'export':
274
+ exportMarkdown(args[1]);
275
+ break;
276
+
277
+ case 'stats':
278
+ showStats();
279
+ break;
280
+
281
+ case 'categories':
282
+ console.log(`\n${c.bold}Memory Categories:${c.reset}\n`);
283
+ for (const [key, cfg] of Object.entries(gitMemory.MEMORY_CATEGORIES)) {
284
+ console.log(` ${cfg.icon} ${key.padEnd(15)} ${c.dim}${cfg.label}${c.reset}`);
285
+ console.log(` ${c.dim}Patterns: ${cfg.patterns.slice(0, 3).map(p => p.source).join(', ')}...${c.reset}`);
286
+ }
287
+ console.log('');
288
+ break;
289
+
290
+ case 'help':
291
+ case '--help':
292
+ case '-h':
293
+ default:
294
+ showHelp();
295
+ }
296
+ }
297
+
298
+ // CLI execution
299
+ if (require.main === module) {
300
+ run(process.argv.slice(2));
301
+ }
302
+
303
+ module.exports = { run };
@@ -0,0 +1,400 @@
1
+ /**
2
+ * Bootspring Orchestrator Command
3
+ * Intelligent agent coordination and workflow management
4
+ *
5
+ * @package bootspring
6
+ * @command orchestrator
7
+ */
8
+
9
+ const utils = require('../core/utils');
10
+ const entitlements = require('../core/entitlements');
11
+ const telemetry = require('../core/telemetry');
12
+ const intelligence = require('../intelligence');
13
+
14
+ function trackTelemetry(event, payload) {
15
+ try {
16
+ telemetry.emitEvent(event, payload);
17
+ } catch {
18
+ // Do not block CLI flow on telemetry issues.
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Run orchestrator command
24
+ */
25
+ async function run(args) {
26
+ const parsedArgs = utils.parseArgs(args);
27
+ const subcommand = args[0] || 'help';
28
+ const context = args.slice(1).join(' ');
29
+ const accessOptions = {
30
+ mode: parsedArgs['access-mode'],
31
+ entitled: parsedArgs.entitled,
32
+ tier: parsedArgs.tier
33
+ };
34
+
35
+ switch (subcommand) {
36
+ case 'analyze':
37
+ case 'suggest':
38
+ runAnalyze(context);
39
+ break;
40
+
41
+ case 'workflow':
42
+ runWorkflow(args[1], accessOptions);
43
+ break;
44
+
45
+ case 'workflows':
46
+ runListWorkflows(accessOptions);
47
+ break;
48
+
49
+ case 'start':
50
+ runStartWorkflow(args[1], accessOptions);
51
+ break;
52
+
53
+ case 'next':
54
+ runNextStep();
55
+ break;
56
+
57
+ case 'checkpoint':
58
+ if (!args[1]) {
59
+ runCheckpoint(undefined, '', accessOptions);
60
+ } else if (intelligence.getWorkflow(args[1])) {
61
+ runCheckpoint(args[1], args.slice(2).join(' '), accessOptions);
62
+ } else {
63
+ // Convenience: allow `checkpoint <signal>` for active workflow.
64
+ runCheckpoint(undefined, args.slice(1).join(' '), accessOptions);
65
+ }
66
+ break;
67
+
68
+ case 'status':
69
+ runStatus();
70
+ break;
71
+
72
+ case 'agents':
73
+ runListAgents();
74
+ break;
75
+
76
+ case 'help':
77
+ default:
78
+ showHelp();
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Analyze context and suggest agents
84
+ */
85
+ function runAnalyze(context) {
86
+ console.log(`
87
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Intelligence Analysis${utils.COLORS.reset}
88
+ `);
89
+
90
+ const analysis = intelligence.analyzeContext(context);
91
+
92
+ console.log(`${utils.COLORS.bold}Current Phase:${utils.COLORS.reset} ${analysis.phase} - ${analysis.phaseConfig?.name || 'Unknown'}`);
93
+ if (analysis.phaseConfig?.description) {
94
+ console.log(`${utils.COLORS.dim}${analysis.phaseConfig.description}${utils.COLORS.reset}`);
95
+ }
96
+ console.log('');
97
+
98
+ if (analysis.suggestions.length === 0) {
99
+ utils.print.warning('No specific agent suggestions for this context');
100
+ utils.print.dim('Try providing more context about what you\'re working on');
101
+ return;
102
+ }
103
+
104
+ console.log(`${utils.COLORS.bold}Suggested Agents:${utils.COLORS.reset}\n`);
105
+
106
+ const high = analysis.suggestions.filter(s => s.priority === 'high');
107
+ const medium = analysis.suggestions.filter(s => s.priority === 'medium');
108
+
109
+ if (high.length > 0) {
110
+ console.log(` ${utils.COLORS.green}High Priority:${utils.COLORS.reset}`);
111
+ high.forEach(s => {
112
+ console.log(` ${utils.COLORS.green}*${utils.COLORS.reset} ${s.agent}`);
113
+ console.log(` ${utils.COLORS.dim}${s.reason}${utils.COLORS.reset}`);
114
+ });
115
+ console.log('');
116
+ }
117
+
118
+ if (medium.length > 0) {
119
+ console.log(` ${utils.COLORS.yellow}Medium Priority:${utils.COLORS.reset}`);
120
+ medium.forEach(s => {
121
+ console.log(` ${utils.COLORS.yellow}*${utils.COLORS.reset} ${s.agent}`);
122
+ console.log(` ${utils.COLORS.dim}${s.reason}${utils.COLORS.reset}`);
123
+ });
124
+ console.log('');
125
+ }
126
+
127
+ if (analysis.skills.length > 0) {
128
+ console.log(`${utils.COLORS.bold}Relevant Skills:${utils.COLORS.reset}`);
129
+ analysis.skills.forEach(skill => {
130
+ console.log(` ${utils.COLORS.cyan}*${utils.COLORS.reset} ${skill}`);
131
+ });
132
+ console.log('');
133
+ }
134
+
135
+ utils.print.dim('Invoke an agent: bootspring agent invoke <name>');
136
+ }
137
+
138
+ /**
139
+ * Show workflow details
140
+ */
141
+ function runWorkflow(name, accessOptions = {}) {
142
+ if (!name) {
143
+ runListWorkflows(accessOptions);
144
+ return;
145
+ }
146
+
147
+ const workflow = intelligence.getWorkflow(name);
148
+
149
+ if (!workflow) {
150
+ utils.print.error(`Unknown workflow: ${name}`);
151
+ const filtered = entitlements.filterAccessibleWorkflows(intelligence.listWorkflows(), accessOptions);
152
+ console.log('Available: ' + filtered.allowed.map(w => w.key).join(', '));
153
+ return;
154
+ }
155
+
156
+ const decision = entitlements.checkWorkflowAccess(workflow, accessOptions);
157
+ if (!decision.allowed) {
158
+ trackTelemetry('premium_prompted', {
159
+ capability: 'workflow_pack',
160
+ workflow: name,
161
+ tier: workflow.tier || 'free',
162
+ mode: decision.context?.mode,
163
+ userTier: decision.context?.tier,
164
+ reason: decision.code
165
+ });
166
+ utils.print.error(decision.reason);
167
+ return;
168
+ }
169
+
170
+ console.log(`
171
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Workflow: ${workflow.name}${utils.COLORS.reset}
172
+
173
+ ${utils.COLORS.bold}Description:${utils.COLORS.reset} ${workflow.description}
174
+ ${utils.COLORS.bold}Tier:${utils.COLORS.reset} ${workflow.tier || 'free'}
175
+
176
+ ${utils.COLORS.bold}Phases:${utils.COLORS.reset}
177
+ `);
178
+
179
+ workflow.phases.forEach((phase, i) => {
180
+ const isLast = i === workflow.phases.length - 1;
181
+ const prefix = isLast ? '\\--' : '|--';
182
+ const line = isLast ? ' ' : '| ';
183
+
184
+ console.log(` ${prefix} ${utils.COLORS.cyan}${phase.name}${utils.COLORS.reset} ${utils.COLORS.dim}(${phase.duration})${utils.COLORS.reset}`);
185
+ phase.agents.forEach((agent, j) => {
186
+ const agentPrefix = j === phase.agents.length - 1 ? '\\-' : '|-';
187
+ const exists = intelligence.agentExists(agent);
188
+ const status = exists ? `${utils.COLORS.green}*${utils.COLORS.reset}` : `${utils.COLORS.red}o${utils.COLORS.reset}`;
189
+ console.log(` ${line} ${agentPrefix} ${status} ${agent}`);
190
+ });
191
+ });
192
+ if (Array.isArray(workflow.completionSignals) && workflow.completionSignals.length > 0) {
193
+ console.log(`\n${utils.COLORS.bold}Completion Signals:${utils.COLORS.reset}`);
194
+ workflow.completionSignals.forEach(signal => {
195
+ console.log(` - ${signal}`);
196
+ });
197
+ }
198
+ console.log('');
199
+ }
200
+
201
+ /**
202
+ * List all workflows
203
+ */
204
+ function runListWorkflows(accessOptions = {}) {
205
+ console.log(`
206
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Available Workflows${utils.COLORS.reset}
207
+ `);
208
+
209
+ const all = intelligence.listWorkflows();
210
+ const { allowed, denied } = entitlements.filterAccessibleWorkflows(all, accessOptions);
211
+
212
+ allowed.forEach(w => {
213
+ console.log(` ${utils.COLORS.cyan}${w.key}${utils.COLORS.reset}`);
214
+ console.log(` ${w.description}`);
215
+ console.log(` ${utils.COLORS.dim}${w.phaseCount} phases | tier: ${w.tier || 'free'}${utils.COLORS.reset}\n`);
216
+ });
217
+ if (denied.length > 0) {
218
+ console.log(`${utils.COLORS.dim}${denied.length} premium workflow(s) hidden by entitlement policy${utils.COLORS.reset}`);
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Start a workflow
224
+ */
225
+ function runStartWorkflow(name, accessOptions = {}) {
226
+ if (!name) {
227
+ utils.print.error('Usage: bootspring orchestrator start <workflow-name>');
228
+ return;
229
+ }
230
+
231
+ const workflow = intelligence.getWorkflow(name);
232
+ if (!workflow) {
233
+ utils.print.error(`Unknown workflow: ${name}`);
234
+ return;
235
+ }
236
+
237
+ const decision = entitlements.checkWorkflowAccess(workflow, accessOptions);
238
+ if (!decision.allowed) {
239
+ utils.print.error(decision.reason);
240
+ return;
241
+ }
242
+
243
+ const result = intelligence.startWorkflow(name);
244
+
245
+ if (result.success) {
246
+ utils.print.success(`Started workflow: ${result.workflow}`);
247
+ console.log(`${utils.COLORS.bold}Current Phase:${utils.COLORS.reset} ${result.currentPhase.name}`);
248
+ console.log(`${utils.COLORS.bold}Agents:${utils.COLORS.reset} ${result.currentPhase.agents.join(', ')}`);
249
+ } else {
250
+ utils.print.error(result.error);
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Advance to next workflow step
256
+ */
257
+ function runNextStep() {
258
+ const result = intelligence.advanceWorkflow();
259
+
260
+ if (result.success) {
261
+ if (result.complete) {
262
+ utils.print.success('Workflow complete!');
263
+ } else {
264
+ utils.print.success(`Advanced to step ${result.step}/${result.totalSteps}`);
265
+ console.log(`${utils.COLORS.bold}Phase:${utils.COLORS.reset} ${result.currentPhase.name}`);
266
+ console.log(`${utils.COLORS.bold}Agents:${utils.COLORS.reset} ${result.currentPhase.agents.join(', ')}`);
267
+ }
268
+ } else {
269
+ utils.print.error(result.error);
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Show orchestrator status
275
+ */
276
+ function runStatus() {
277
+ const state = intelligence.loadState();
278
+ const currentPhase = intelligence.getCurrentPhase();
279
+ const phaseConfig = intelligence.PHASE_AGENTS[currentPhase];
280
+ const agents = intelligence.getAvailableAgents();
281
+
282
+ console.log(`
283
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Orchestrator Status${utils.COLORS.reset}
284
+
285
+ ${utils.COLORS.bold}Current Phase:${utils.COLORS.reset} ${currentPhase} - ${phaseConfig?.name || 'Unknown'}
286
+ ${utils.COLORS.bold}Last Analysis:${utils.COLORS.reset} ${state.lastAnalysis || 'Never'}
287
+ ${utils.COLORS.bold}Active Workflow:${utils.COLORS.reset} ${state.activeWorkflow || 'None'}
288
+ `);
289
+
290
+ if (state.activeWorkflow) {
291
+ const workflow = intelligence.getWorkflow(state.activeWorkflow);
292
+ console.log(`${utils.COLORS.bold}Workflow Step:${utils.COLORS.reset} ${state.workflowStep + 1}/${workflow.phases.length}`);
293
+ const progress = intelligence.getWorkflowSignalProgress(state.activeWorkflow, state);
294
+ if (progress) {
295
+ console.log(`${utils.COLORS.bold}Checkpoint Signals:${utils.COLORS.reset} ${progress.completedSignals.length}/${progress.totalSignals}`);
296
+ }
297
+ }
298
+
299
+ if (state.suggestions.length > 0) {
300
+ console.log(`${utils.COLORS.bold}Last Suggestions:${utils.COLORS.reset}`);
301
+ state.suggestions.slice(0, 5).forEach(s => {
302
+ console.log(` * ${s.agent} (${s.priority})`);
303
+ });
304
+ }
305
+
306
+ console.log(`\n${utils.COLORS.bold}Available Agents:${utils.COLORS.reset} ${agents.length}`);
307
+ }
308
+
309
+ function runCheckpoint(workflowName, signalRef, accessOptions = {}) {
310
+ const targetWorkflowName = workflowName || intelligence.loadState().activeWorkflow;
311
+ if (targetWorkflowName) {
312
+ const workflow = intelligence.getWorkflow(targetWorkflowName);
313
+ if (workflow) {
314
+ const decision = entitlements.checkWorkflowAccess(workflow, accessOptions);
315
+ if (!decision.allowed) {
316
+ trackTelemetry('premium_prompted', {
317
+ capability: 'workflow_pack',
318
+ workflow: targetWorkflowName,
319
+ tier: workflow.tier || 'free',
320
+ mode: decision.context?.mode,
321
+ userTier: decision.context?.tier,
322
+ reason: decision.code
323
+ });
324
+ utils.print.error(decision.reason);
325
+ return;
326
+ }
327
+ }
328
+ }
329
+
330
+ const result = intelligence.markWorkflowCheckpoint(workflowName, signalRef);
331
+ if (!result.success) {
332
+ utils.print.error(result.error);
333
+ if (Array.isArray(result.availableSignals) && result.availableSignals.length > 0) {
334
+ utils.print.dim(`Available signals: ${result.availableSignals.join(' | ')}`);
335
+ }
336
+ return;
337
+ }
338
+
339
+ if (result.alreadyCompleted) {
340
+ utils.print.warning(`Signal already completed: ${result.signal}`);
341
+ } else {
342
+ utils.print.success(`Checkpoint completed: ${result.signal}`);
343
+ }
344
+
345
+ if (result.progress) {
346
+ console.log(`${utils.COLORS.bold}Progress:${utils.COLORS.reset} ${result.progress.completedSignals.length}/${result.progress.totalSignals}`);
347
+ }
348
+ }
349
+
350
+ /**
351
+ * List available agents
352
+ */
353
+ function runListAgents() {
354
+ const agents = intelligence.getAvailableAgents();
355
+
356
+ console.log(`
357
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Available Agents (${agents.length})${utils.COLORS.reset}
358
+ `);
359
+
360
+ agents.forEach(agent => {
361
+ console.log(` * ${agent}`);
362
+ });
363
+ console.log('');
364
+ }
365
+
366
+ /**
367
+ * Show help
368
+ */
369
+ function showHelp() {
370
+ console.log(`
371
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Bootspring Intelligence Orchestrator${utils.COLORS.reset}
372
+
373
+ ${utils.COLORS.bold}Usage:${utils.COLORS.reset}
374
+ bootspring orchestrator <command> [options]
375
+
376
+ ${utils.COLORS.bold}Commands:${utils.COLORS.reset}
377
+ analyze [context] Analyze context and suggest agents
378
+ suggest [context] Alias for analyze
379
+ workflow <name> Show workflow details
380
+ workflows List available workflows
381
+ start <workflow> Start a workflow
382
+ next Advance to next workflow step
383
+ checkpoint [w] [s] Mark workflow completion signal (workflow optional if active)
384
+ status Show orchestrator status
385
+ agents List available agents
386
+ help Show this help
387
+ --access-mode <m> Entitlement mode: local (default) or server
388
+ --entitled <bool> Entitlement override for premium workflows
389
+ --tier <tier> Tier override: free, pro, team, enterprise
390
+
391
+ ${utils.COLORS.bold}Examples:${utils.COLORS.reset}
392
+ bootspring orchestrator analyze "building authentication api"
393
+ bootspring orchestrator workflow feature-development
394
+ bootspring orchestrator start feature-development
395
+ bootspring orchestrator checkpoint launch-pack 1
396
+ bootspring orchestrator status
397
+ `);
398
+ }
399
+
400
+ module.exports = { run };