@girardmedia/bootspring 2.0.21 → 2.0.23

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 (159) hide show
  1. package/bin/bootspring.js +5 -0
  2. package/cli/org.js +474 -0
  3. package/cli/preseed/index.js +16 -0
  4. package/cli/preseed/interactive.js +143 -0
  5. package/cli/preseed/templates.js +227 -0
  6. package/cli/preseed.js +9 -301
  7. package/cli/seed/builders/ai-context-builder.js +85 -0
  8. package/cli/seed/builders/index.js +13 -0
  9. package/cli/seed/builders/seed-builder.js +272 -0
  10. package/cli/seed/extractors/content-extractors.js +383 -0
  11. package/cli/seed/extractors/index.js +47 -0
  12. package/cli/seed/extractors/metadata-extractors.js +167 -0
  13. package/cli/seed/extractors/section-extractor.js +54 -0
  14. package/cli/seed/extractors/stack-extractors.js +228 -0
  15. package/cli/seed/index.js +18 -0
  16. package/cli/seed/utils/folder-structure.js +84 -0
  17. package/cli/seed/utils/index.js +11 -0
  18. package/cli/seed.js +23 -1074
  19. package/core/api-client.js +77 -0
  20. package/core/entitlements.js +36 -0
  21. package/core/organizations.js +223 -0
  22. package/core/policies.js +51 -6
  23. package/core/policy-matrix.js +303 -0
  24. package/core/project-context.js +1 -0
  25. package/dist/cli/index.d.ts +3 -0
  26. package/dist/cli/index.js +3220 -0
  27. package/dist/cli/index.js.map +1 -0
  28. package/dist/context-McpJQa_2.d.ts +5710 -0
  29. package/dist/core/index.d.ts +635 -0
  30. package/dist/core/index.js +2593 -0
  31. package/dist/core/index.js.map +1 -0
  32. package/dist/index-QqbeEiDm.d.ts +857 -0
  33. package/dist/index-UiYCgwiH.d.ts +174 -0
  34. package/dist/index.d.ts +453 -0
  35. package/dist/index.js +44228 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/mcp/index.d.ts +1 -0
  38. package/dist/mcp/index.js +41173 -0
  39. package/dist/mcp/index.js.map +1 -0
  40. package/generators/index.ts +82 -0
  41. package/intelligence/orchestrator/config/failure-signatures.js +48 -0
  42. package/intelligence/orchestrator/config/index.js +23 -0
  43. package/intelligence/orchestrator/config/pack-lifecycle.js +262 -0
  44. package/intelligence/orchestrator/config/phases.js +111 -0
  45. package/intelligence/orchestrator/config/remediation.js +150 -0
  46. package/intelligence/orchestrator/config/workflows.js +168 -0
  47. package/intelligence/orchestrator/core/index.js +16 -0
  48. package/intelligence/orchestrator/core/state-manager.js +88 -0
  49. package/intelligence/orchestrator/core/telemetry.js +24 -0
  50. package/intelligence/orchestrator/index.js +17 -0
  51. package/intelligence/orchestrator.js +17 -512
  52. package/mcp/contracts/mcp-contract.v1.json +1 -1
  53. package/package.json +16 -3
  54. package/src/cli/agent.ts +703 -0
  55. package/src/cli/analyze.ts +640 -0
  56. package/src/cli/audit.ts +707 -0
  57. package/src/cli/auth.ts +930 -0
  58. package/src/cli/billing.ts +364 -0
  59. package/src/cli/build.ts +1089 -0
  60. package/src/cli/business.ts +508 -0
  61. package/src/cli/checkpoint-utils.ts +236 -0
  62. package/src/cli/checkpoint.ts +757 -0
  63. package/src/cli/cloud-sync.ts +534 -0
  64. package/src/cli/content.ts +273 -0
  65. package/src/cli/context.ts +667 -0
  66. package/src/cli/dashboard.ts +133 -0
  67. package/src/cli/deploy.ts +704 -0
  68. package/src/cli/doctor.ts +480 -0
  69. package/src/cli/fundraise.ts +494 -0
  70. package/src/cli/generate.ts +346 -0
  71. package/src/cli/github-cmd.ts +566 -0
  72. package/src/cli/health.ts +599 -0
  73. package/src/cli/index.ts +113 -0
  74. package/src/cli/init.ts +838 -0
  75. package/src/cli/legal.ts +495 -0
  76. package/src/cli/log.ts +316 -0
  77. package/src/cli/loop.ts +1660 -0
  78. package/src/cli/manager.ts +878 -0
  79. package/src/cli/mcp.ts +275 -0
  80. package/src/cli/memory.ts +346 -0
  81. package/src/cli/metrics.ts +590 -0
  82. package/src/cli/monitor.ts +960 -0
  83. package/src/cli/mvp.ts +662 -0
  84. package/src/cli/onboard.ts +663 -0
  85. package/src/cli/orchestrator.ts +622 -0
  86. package/src/cli/plugin.ts +483 -0
  87. package/src/cli/prd.ts +671 -0
  88. package/src/cli/preseed-start.ts +1633 -0
  89. package/src/cli/preseed.ts +2434 -0
  90. package/src/cli/project.ts +526 -0
  91. package/src/cli/quality.ts +885 -0
  92. package/src/cli/security.ts +1079 -0
  93. package/src/cli/seed.ts +1224 -0
  94. package/src/cli/skill.ts +537 -0
  95. package/src/cli/suggest.ts +1225 -0
  96. package/src/cli/switch.ts +518 -0
  97. package/src/cli/task.ts +780 -0
  98. package/src/cli/telemetry.ts +172 -0
  99. package/src/cli/todo.ts +627 -0
  100. package/src/cli/types.ts +15 -0
  101. package/src/cli/update.ts +334 -0
  102. package/src/cli/visualize.ts +609 -0
  103. package/src/cli/watch.ts +895 -0
  104. package/src/cli/workspace.ts +709 -0
  105. package/src/core/action-recorder.ts +673 -0
  106. package/src/core/analyze-workflow.ts +1453 -0
  107. package/src/core/api-client.ts +1120 -0
  108. package/src/core/audit-workflow.ts +1681 -0
  109. package/src/core/auth.ts +471 -0
  110. package/src/core/build-orchestrator.ts +509 -0
  111. package/src/core/build-state.ts +621 -0
  112. package/src/core/checkpoint-engine.ts +482 -0
  113. package/src/core/config.ts +1285 -0
  114. package/src/core/context-loader.ts +694 -0
  115. package/src/core/context.ts +410 -0
  116. package/src/core/deploy-workflow.ts +1085 -0
  117. package/src/core/entitlements.ts +322 -0
  118. package/src/core/github-sync.ts +720 -0
  119. package/src/core/index.ts +981 -0
  120. package/src/core/ingest.ts +1186 -0
  121. package/src/core/metrics-engine.ts +886 -0
  122. package/src/core/mvp.ts +847 -0
  123. package/src/core/onboard-workflow.ts +1293 -0
  124. package/src/core/policies.ts +81 -0
  125. package/src/core/preseed-workflow.ts +1163 -0
  126. package/src/core/preseed.ts +1826 -0
  127. package/src/core/project-context.ts +380 -0
  128. package/src/core/project-state.ts +699 -0
  129. package/src/core/r2-sync.ts +691 -0
  130. package/src/core/scaffold.ts +1715 -0
  131. package/src/core/session.ts +286 -0
  132. package/src/core/task-extractor.ts +799 -0
  133. package/src/core/telemetry.ts +371 -0
  134. package/src/core/tier-enforcement.ts +737 -0
  135. package/src/core/utils.ts +437 -0
  136. package/src/index.ts +29 -0
  137. package/src/intelligence/agent-collab.ts +2376 -0
  138. package/src/intelligence/auto-suggest.ts +713 -0
  139. package/src/intelligence/content-gen.ts +1351 -0
  140. package/src/intelligence/cross-project.ts +1692 -0
  141. package/src/intelligence/git-memory.ts +529 -0
  142. package/src/intelligence/index.ts +318 -0
  143. package/src/intelligence/orchestrator.ts +534 -0
  144. package/src/intelligence/prd.ts +466 -0
  145. package/src/intelligence/recommendations.ts +982 -0
  146. package/src/intelligence/workflow-composer.ts +1472 -0
  147. package/src/mcp/capabilities.ts +233 -0
  148. package/src/mcp/index.ts +37 -0
  149. package/src/mcp/registry.ts +1268 -0
  150. package/src/mcp/response-formatter.ts +797 -0
  151. package/src/mcp/server.ts +240 -0
  152. package/src/types/agent.ts +69 -0
  153. package/src/types/config.ts +86 -0
  154. package/src/types/context.ts +77 -0
  155. package/src/types/index.ts +53 -0
  156. package/src/types/mcp.ts +91 -0
  157. package/src/types/skills.ts +47 -0
  158. package/src/types/workflow.ts +155 -0
  159. package/generators/index.js +0 -18
@@ -0,0 +1,1225 @@
1
+ /**
2
+ * Bootspring Suggest Command
3
+ * AI-powered recommendations based on telemetry and learning
4
+ *
5
+ * @package bootspring
6
+ * @command suggest
7
+ */
8
+
9
+ import * as fs from 'fs';
10
+ import * as path from 'path';
11
+
12
+ // Interfaces
13
+ interface ConfigModule {
14
+ load: () => { _projectRoot: string };
15
+ }
16
+
17
+ interface UtilsModule {
18
+ COLORS: {
19
+ cyan: string;
20
+ bold: string;
21
+ reset: string;
22
+ dim: string;
23
+ green: string;
24
+ yellow: string;
25
+ red: string;
26
+ };
27
+ createSpinner: (text: string) => {
28
+ start: () => SpinnerInstance;
29
+ stop: () => void;
30
+ };
31
+ parseArgs: (args: string[]) => ParsedArgs;
32
+ print: {
33
+ error: (msg: string) => void;
34
+ };
35
+ }
36
+
37
+ interface SpinnerInstance {
38
+ stop: () => void;
39
+ fail: (text: string) => void;
40
+ }
41
+
42
+ interface IntelligenceModule {
43
+ [key: string]: unknown;
44
+ }
45
+
46
+ interface TelemetryModule {
47
+ [key: string]: unknown;
48
+ }
49
+
50
+ interface EntitlementsModule {
51
+ [key: string]: unknown;
52
+ }
53
+
54
+ interface SkillsModule {
55
+ listSkills: () => unknown[];
56
+ getSkillMetadata: () => unknown | null;
57
+ loadSkill: () => unknown | null;
58
+ searchSkills: () => unknown[];
59
+ getCategories: () => unknown[];
60
+ hasExternalSkillLibrary: () => boolean;
61
+ }
62
+
63
+ interface RecommendationsEngineFactory {
64
+ createRecommendationsEngine: (options: RecommendationsEngineOptions) => RecommendationsEngine;
65
+ }
66
+
67
+ interface RecommendationsEngineOptions {
68
+ intelligence: IntelligenceModule;
69
+ telemetry: TelemetryModule;
70
+ skills: SkillsModule;
71
+ entitlements: EntitlementsModule;
72
+ }
73
+
74
+ interface RecommendationsEngine {
75
+ recommend: (options: RecommendOptions) => RecommendationResult;
76
+ }
77
+
78
+ interface RecommendOptions {
79
+ contextText: string;
80
+ limit: number;
81
+ accessOptions: Record<string, unknown>;
82
+ }
83
+
84
+ interface RecommendationResult {
85
+ context: {
86
+ phase?: string | undefined;
87
+ phaseName?: string | undefined;
88
+ };
89
+ workflows: WorkflowRecommendation[];
90
+ skills: SkillRecommendation[];
91
+ learning?: LearningData | undefined;
92
+ telemetryWindow: {
93
+ eventsAnalyzed: number;
94
+ };
95
+ }
96
+
97
+ interface WorkflowRecommendation {
98
+ name: string;
99
+ key?: string | undefined;
100
+ tier: string;
101
+ score: number;
102
+ reasons: string[];
103
+ learningImpact?: {
104
+ adjustment: number;
105
+ breakdown: {
106
+ impactBoost: number;
107
+ };
108
+ } | undefined;
109
+ }
110
+
111
+ interface SkillRecommendation {
112
+ id: string;
113
+ name?: string | undefined;
114
+ description?: string | undefined;
115
+ score: number;
116
+ }
117
+
118
+ interface LearningData {
119
+ usingDefaults?: boolean | undefined;
120
+ antiPatterns: AntiPattern[];
121
+ insights: Insight[];
122
+ phaseRecommendation?: PhaseRecommendation | undefined;
123
+ impact?: {
124
+ boostedPatterns?: BoostedPattern[] | undefined;
125
+ } | undefined;
126
+ generalRecommendations?: string[] | undefined;
127
+ }
128
+
129
+ interface AntiPattern {
130
+ decision: string;
131
+ failure_rate: number;
132
+ is_default?: boolean | undefined;
133
+ }
134
+
135
+ interface Insight {
136
+ message: string;
137
+ level?: string | undefined;
138
+ }
139
+
140
+ interface PhaseRecommendation {
141
+ success_rate: number;
142
+ is_default?: boolean | undefined;
143
+ }
144
+
145
+ interface BoostedPattern {
146
+ type: string;
147
+ averageImpactScore: number;
148
+ }
149
+
150
+ interface ParsedArgs {
151
+ _: string[];
152
+ help?: boolean;
153
+ h?: boolean;
154
+ limit?: number | string;
155
+ json?: boolean;
156
+ 'enable-auto'?: boolean;
157
+ 'disable-auto'?: boolean;
158
+ category?: string;
159
+ suppress?: string;
160
+ unsuppress?: string;
161
+ [key: string]: string | boolean | number | string[] | undefined;
162
+ }
163
+
164
+ interface PatternLearnerModule {
165
+ analyze: () => PatternAnalysis;
166
+ }
167
+
168
+ interface PatternAnalysis {
169
+ using_defaults?: boolean | undefined;
170
+ analysis_summary?: {
171
+ total_decisions?: number | undefined;
172
+ success_rate?: number | undefined;
173
+ unique_patterns?: number | undefined;
174
+ } | undefined;
175
+ insights?: Insight[] | undefined;
176
+ anti_patterns?: AntiPattern[] | undefined;
177
+ correlations?: Correlation[] | undefined;
178
+ }
179
+
180
+ interface Correlation {
181
+ decision_a: string;
182
+ decision_b: string;
183
+ occurrences: number;
184
+ }
185
+
186
+ interface AutoSuggestModule {
187
+ new (projectRoot: string): AutoSuggestInstance;
188
+ }
189
+
190
+ interface AutoSuggestInstance {
191
+ getStatus: () => AutoSuggestStatus;
192
+ setEnabled: (enabled: boolean) => void;
193
+ setCategory: (category: string, enabled: boolean) => void;
194
+ suppressCommand: (command: string) => void;
195
+ unsuppressCommand: (command: string) => void;
196
+ analyze: (text: string) => Suggestion[];
197
+ }
198
+
199
+ interface AutoSuggestStatus {
200
+ enabled: boolean;
201
+ categories: Record<string, boolean>;
202
+ suppressedCommands: string[];
203
+ }
204
+
205
+ interface Suggestion {
206
+ command: string;
207
+ description: string;
208
+ confidence: number;
209
+ }
210
+
211
+ interface Prompt {
212
+ category: string;
213
+ priority: string;
214
+ title: string;
215
+ prompt: string;
216
+ command: string | null;
217
+ }
218
+
219
+ interface ProjectState {
220
+ checkpoints?: Record<string, { completed?: boolean | undefined }> | undefined;
221
+ projectType?: string | undefined;
222
+ }
223
+
224
+ interface PackageJson {
225
+ dependencies?: Record<string, string> | undefined;
226
+ }
227
+
228
+ // Lazy load modules
229
+ const config = require('../core/config') as ConfigModule;
230
+ const utils = require('../core/utils') as UtilsModule;
231
+ const intelligence = require('../intelligence') as IntelligenceModule;
232
+ const telemetry = require('../core/telemetry') as TelemetryModule;
233
+ const entitlements = require('../core/entitlements') as EntitlementsModule;
234
+
235
+ // Thin client: skills module may not exist locally
236
+ // Skills are now served from the API
237
+ let skills: SkillsModule;
238
+ try {
239
+ skills = require('../skills') as SkillsModule;
240
+ } catch {
241
+ // Stub for thin client - skills fetched from API
242
+ skills = {
243
+ listSkills: () => [],
244
+ getSkillMetadata: () => null,
245
+ loadSkill: () => null,
246
+ searchSkills: () => [],
247
+ getCategories: () => [],
248
+ hasExternalSkillLibrary: () => false
249
+ };
250
+ }
251
+
252
+ // Lazy load recommendations engine
253
+ let recommendationsEngine: RecommendationsEngine | null = null;
254
+ function getRecommendationsEngine(): RecommendationsEngine {
255
+ if (!recommendationsEngine) {
256
+ const { createRecommendationsEngine } = require('../intelligence/recommendations') as RecommendationsEngineFactory;
257
+ recommendationsEngine = createRecommendationsEngine({
258
+ intelligence,
259
+ telemetry,
260
+ skills,
261
+ entitlements
262
+ });
263
+ }
264
+ return recommendationsEngine;
265
+ }
266
+
267
+ /**
268
+ * Get context from project files
269
+ */
270
+ function getProjectContext(projectRoot: string): string {
271
+ const contextParts: string[] = [];
272
+
273
+ // Read CLAUDE.md
274
+ const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
275
+ if (fs.existsSync(claudeMdPath)) {
276
+ const content = fs.readFileSync(claudeMdPath, 'utf-8');
277
+ contextParts.push(content.slice(0, 2000));
278
+ }
279
+
280
+ // Read recent todos
281
+ const todoPath = path.join(projectRoot, 'todo.md');
282
+ if (fs.existsSync(todoPath)) {
283
+ const content = fs.readFileSync(todoPath, 'utf-8');
284
+ const pendingTodos = content.match(/- \[ \].*/g) || [];
285
+ if (pendingTodos.length > 0) {
286
+ contextParts.push('Pending tasks: ' + pendingTodos.slice(0, 5).join(', '));
287
+ }
288
+ }
289
+
290
+ // Read recent workflow state
291
+ const workflows = ['analyze', 'audit', 'deploy'];
292
+ for (const workflow of workflows) {
293
+ const stateFile = path.join(projectRoot, '.bootspring', workflow, 'workflow-state.json');
294
+ if (fs.existsSync(stateFile)) {
295
+ try {
296
+ const state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
297
+ const incomplete = Object.entries(state.phases || {})
298
+ .filter(([, p]) => (p as { status: string }).status !== 'completed')
299
+ .map(([name]) => name);
300
+ if (incomplete.length > 0) {
301
+ contextParts.push(`${workflow} workflow incomplete: ${incomplete.join(', ')}`);
302
+ }
303
+ } catch {
304
+ // Skip invalid state
305
+ }
306
+ }
307
+ }
308
+
309
+ return contextParts.join('\n\n');
310
+ }
311
+
312
+ /**
313
+ * Format workflow recommendation
314
+ */
315
+ function formatWorkflow(workflow: WorkflowRecommendation, index: number): string {
316
+ const tierIcon = workflow.tier === 'free' ?
317
+ '' : ` ${utils.COLORS.yellow}[${workflow.tier}]${utils.COLORS.reset}`;
318
+
319
+ let output = ` ${index + 1}. ${utils.COLORS.cyan}${workflow.name}${utils.COLORS.reset}${tierIcon}\n`;
320
+ output += ` ${utils.COLORS.dim}Score: ${workflow.score}${utils.COLORS.reset}\n`;
321
+
322
+ if (workflow.reasons.length > 0) {
323
+ const topReasons = workflow.reasons.slice(0, 3);
324
+ output += ` ${utils.COLORS.dim}${topReasons.join(' • ')}${utils.COLORS.reset}\n`;
325
+ }
326
+
327
+ if (workflow.learningImpact) {
328
+ const { adjustment, breakdown } = workflow.learningImpact;
329
+ const sign = adjustment >= 0 ? '+' : '';
330
+ output += ` ${utils.COLORS.green}Learning: ${sign}${adjustment}${utils.COLORS.reset}`;
331
+ if (breakdown.impactBoost > 0) {
332
+ output += ` ${utils.COLORS.dim}(impact: +${breakdown.impactBoost})${utils.COLORS.reset}`;
333
+ }
334
+ output += '\n';
335
+ }
336
+
337
+ return output;
338
+ }
339
+
340
+ /**
341
+ * Format skill recommendation
342
+ */
343
+ function formatSkill(skill: SkillRecommendation, index: number): string {
344
+ let output = ` ${index + 1}. ${utils.COLORS.cyan}${skill.name || skill.id}${utils.COLORS.reset}\n`;
345
+
346
+ if (skill.description) {
347
+ output += ` ${utils.COLORS.dim}${skill.description.slice(0, 60)}${utils.COLORS.reset}\n`;
348
+ }
349
+
350
+ output += ` ${utils.COLORS.dim}Score: ${skill.score}${utils.COLORS.reset}\n`;
351
+
352
+ return output;
353
+ }
354
+
355
+ /**
356
+ * Show recommendations
357
+ */
358
+ async function showRecommendations(projectRoot: string, options: { limit?: number | undefined; json?: boolean | undefined } = {}): Promise<RecommendationResult | undefined> {
359
+ const engine = getRecommendationsEngine();
360
+
361
+ const spinner = utils.createSpinner('Analyzing context...').start();
362
+
363
+ try {
364
+ // Get project context
365
+ const contextText = getProjectContext(projectRoot);
366
+
367
+ // Get recommendations
368
+ const result = engine.recommend({
369
+ contextText,
370
+ limit: options.limit || 5,
371
+ accessOptions: {}
372
+ });
373
+
374
+ spinner.stop();
375
+
376
+ console.log(`
377
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Smart Suggestions${utils.COLORS.reset}
378
+ ${utils.COLORS.dim}Based on your project context and history${utils.COLORS.reset}
379
+ `);
380
+
381
+ // Current context
382
+ if (result.context.phase) {
383
+ console.log(`${utils.COLORS.bold}Current Phase${utils.COLORS.reset}`);
384
+ console.log(` ${result.context.phaseName || result.context.phase}`);
385
+ console.log();
386
+ }
387
+
388
+ // Workflow recommendations
389
+ if (result.workflows.length > 0) {
390
+ console.log(`${utils.COLORS.bold}Recommended Workflows${utils.COLORS.reset}`);
391
+ for (let i = 0; i < result.workflows.length; i++) {
392
+ const workflow = result.workflows[i];
393
+ if (workflow) {
394
+ console.log(formatWorkflow(workflow, i));
395
+ }
396
+ }
397
+ }
398
+
399
+ // Skill recommendations
400
+ if (result.skills.length > 0) {
401
+ console.log(`${utils.COLORS.bold}Recommended Skills${utils.COLORS.reset}`);
402
+ for (let i = 0; i < Math.min(result.skills.length, 3); i++) {
403
+ const skill = result.skills[i];
404
+ if (skill) {
405
+ console.log(formatSkill(skill, i));
406
+ }
407
+ }
408
+ }
409
+
410
+ // Learning insights
411
+ if (result.learning && !result.learning.usingDefaults) {
412
+ console.log(`${utils.COLORS.bold}Insights from History${utils.COLORS.reset}`);
413
+
414
+ // Anti-patterns to avoid
415
+ if (result.learning.antiPatterns.length > 0) {
416
+ const ap = result.learning.antiPatterns[0];
417
+ if (ap && !ap.is_default) {
418
+ console.log(` ${utils.COLORS.yellow}⚠${utils.COLORS.reset} Avoid: ${ap.decision} (${(ap.failure_rate * 100).toFixed(0)}% failure rate)`);
419
+ }
420
+ }
421
+
422
+ // Top insight
423
+ if (result.learning.insights.length > 0) {
424
+ const insight = result.learning.insights[0];
425
+ if (insight) {
426
+ console.log(` ${utils.COLORS.green}→${utils.COLORS.reset} ${insight.message}`);
427
+ }
428
+ }
429
+
430
+ // Phase recommendation
431
+ if (result.learning.phaseRecommendation && !result.learning.phaseRecommendation.is_default) {
432
+ const pr = result.learning.phaseRecommendation;
433
+ console.log(` ${utils.COLORS.dim}This phase has ${(pr.success_rate * 100).toFixed(0)}% success rate${utils.COLORS.reset}`);
434
+ }
435
+
436
+ // Impact insights
437
+ if (result.learning.impact?.boostedPatterns && result.learning.impact.boostedPatterns.length > 0) {
438
+ const topPattern = result.learning.impact.boostedPatterns[0];
439
+ if (topPattern) {
440
+ console.log(` ${utils.COLORS.green}★${utils.COLORS.reset} High-impact pattern: ${topPattern.type} (${topPattern.averageImpactScore.toFixed(1)} impact score)`);
441
+ }
442
+ }
443
+
444
+ console.log();
445
+ }
446
+
447
+ // General recommendations
448
+ if (result.learning?.generalRecommendations && result.learning.generalRecommendations.length > 0) {
449
+ console.log(`${utils.COLORS.bold}Recommendations${utils.COLORS.reset}`);
450
+ for (const rec of result.learning.generalRecommendations.slice(0, 3)) {
451
+ console.log(` ${utils.COLORS.cyan}→${utils.COLORS.reset} ${rec}`);
452
+ }
453
+ console.log();
454
+ }
455
+
456
+ // Telemetry summary
457
+ console.log(`${utils.COLORS.dim}Based on ${result.telemetryWindow.eventsAnalyzed} events${utils.COLORS.reset}`);
458
+
459
+ if (options.json) {
460
+ return result;
461
+ }
462
+
463
+ } catch (error) {
464
+ spinner.fail('Failed to generate recommendations');
465
+ utils.print.error((error as Error).message);
466
+ }
467
+
468
+ return undefined;
469
+ }
470
+
471
+ /**
472
+ * Show next action suggestion
473
+ */
474
+ async function showNextAction(projectRoot: string): Promise<void> {
475
+ const engine = getRecommendationsEngine();
476
+ const contextText = getProjectContext(projectRoot);
477
+
478
+ const result = engine.recommend({
479
+ contextText,
480
+ limit: 1,
481
+ accessOptions: {}
482
+ });
483
+
484
+ console.log(`
485
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Next Action${utils.COLORS.reset}
486
+ `);
487
+
488
+ if (result.workflows.length > 0) {
489
+ const workflow = result.workflows[0];
490
+ if (workflow) {
491
+ console.log(`${utils.COLORS.bold}${workflow.name}${utils.COLORS.reset}`);
492
+
493
+ if (workflow.reasons.length > 0) {
494
+ console.log(`${utils.COLORS.dim}${workflow.reasons[0]}${utils.COLORS.reset}`);
495
+ }
496
+
497
+ // Suggest command
498
+ const commands: Record<string, string> = {
499
+ 'analyze': 'bootspring analyze',
500
+ 'audit': 'bootspring audit',
501
+ 'deploy': 'bootspring deploy',
502
+ 'preseed': 'bootspring preseed init',
503
+ 'seed': 'bootspring seed scaffold',
504
+ 'onboard': 'bootspring onboard'
505
+ };
506
+
507
+ for (const [key, cmd] of Object.entries(commands)) {
508
+ if (workflow.key?.includes(key) || workflow.name?.toLowerCase().includes(key)) {
509
+ console.log();
510
+ console.log(`${utils.COLORS.cyan}Run:${utils.COLORS.reset} ${cmd}`);
511
+ break;
512
+ }
513
+ }
514
+ }
515
+ } else {
516
+ console.log('No specific suggestions at this time.');
517
+ console.log(`${utils.COLORS.dim}Run 'bootspring health' to check project status${utils.COLORS.reset}`);
518
+ }
519
+
520
+ console.log();
521
+ }
522
+
523
+ /**
524
+ * Show learning stats
525
+ */
526
+ function showLearningStats(): void {
527
+ console.log(`
528
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Learning Stats${utils.COLORS.reset}
529
+ `);
530
+
531
+ try {
532
+ const patternLearner = require('../intelligence/learning/pattern-learner') as PatternLearnerModule;
533
+ const insights = patternLearner.analyze();
534
+
535
+ if (insights.using_defaults) {
536
+ console.log(`${utils.COLORS.yellow}No learning data yet${utils.COLORS.reset}`);
537
+ console.log(`${utils.COLORS.dim}Use Bootspring more to build up patterns${utils.COLORS.reset}`);
538
+ return;
539
+ }
540
+
541
+ // Summary
542
+ if (insights.analysis_summary) {
543
+ const summary = insights.analysis_summary;
544
+ console.log(`${utils.COLORS.bold}Summary${utils.COLORS.reset}`);
545
+ console.log(` Total decisions: ${summary.total_decisions || 0}`);
546
+ console.log(` Success rate: ${((summary.success_rate || 0) * 100).toFixed(0)}%`);
547
+ console.log(` Unique patterns: ${summary.unique_patterns || 0}`);
548
+ console.log();
549
+ }
550
+
551
+ // Top insights
552
+ if (insights.insights && insights.insights.length > 0) {
553
+ console.log(`${utils.COLORS.bold}Top Insights${utils.COLORS.reset}`);
554
+ for (const insight of insights.insights.slice(0, 5)) {
555
+ const icon = insight.level === 'positive' ?
556
+ `${utils.COLORS.green}↑${utils.COLORS.reset}` :
557
+ insight.level === 'negative' ?
558
+ `${utils.COLORS.red}↓${utils.COLORS.reset}` :
559
+ `${utils.COLORS.dim}→${utils.COLORS.reset}`;
560
+ console.log(` ${icon} ${insight.message}`);
561
+ }
562
+ console.log();
563
+ }
564
+
565
+ // Anti-patterns
566
+ const realAntiPatterns = (insights.anti_patterns || []).filter(ap => !ap.is_default);
567
+ if (realAntiPatterns.length > 0) {
568
+ console.log(`${utils.COLORS.bold}Anti-Patterns Detected${utils.COLORS.reset}`);
569
+ for (const ap of realAntiPatterns.slice(0, 3)) {
570
+ console.log(` ${utils.COLORS.yellow}⚠${utils.COLORS.reset} ${ap.decision}: ${(ap.failure_rate * 100).toFixed(0)}% failure rate`);
571
+ }
572
+ console.log();
573
+ }
574
+
575
+ // Correlations
576
+ if (insights.correlations && insights.correlations.length > 0) {
577
+ console.log(`${utils.COLORS.bold}Pattern Correlations${utils.COLORS.reset}`);
578
+ for (const corr of insights.correlations.slice(0, 3)) {
579
+ console.log(` ${utils.COLORS.dim}${corr.decision_a} → ${corr.decision_b} (${corr.occurrences} times)${utils.COLORS.reset}`);
580
+ }
581
+ console.log();
582
+ }
583
+
584
+ } catch (error) {
585
+ console.log(`${utils.COLORS.dim}Learning data not available: ${(error as Error).message}${utils.COLORS.reset}`);
586
+ }
587
+ }
588
+
589
+ /**
590
+ * Auto-suggest configuration and display
591
+ */
592
+ function showAutoSuggestConfig(projectRoot: string): void {
593
+ const { AutoSuggest } = require('../intelligence/auto-suggest') as { AutoSuggest: AutoSuggestModule };
594
+ const autoSuggest = new AutoSuggest(projectRoot);
595
+ const status = autoSuggest.getStatus();
596
+
597
+ console.log(`
598
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Auto-Suggest Configuration${utils.COLORS.reset}
599
+
600
+ ${utils.COLORS.bold}Status${utils.COLORS.reset}
601
+ Enabled: ${status.enabled ? `${utils.COLORS.green}Yes${utils.COLORS.reset}` : `${utils.COLORS.red}No${utils.COLORS.reset}`}
602
+
603
+ ${utils.COLORS.bold}Categories${utils.COLORS.reset}`);
604
+
605
+ for (const [category, enabled] of Object.entries(status.categories)) {
606
+ const icon = enabled ? `${utils.COLORS.green}✓${utils.COLORS.reset}` : `${utils.COLORS.dim}○${utils.COLORS.reset}`;
607
+ console.log(` ${icon} ${category}`);
608
+ }
609
+
610
+ if (status.suppressedCommands.length > 0) {
611
+ console.log(`
612
+ ${utils.COLORS.bold}Suppressed Commands${utils.COLORS.reset}`);
613
+ for (const cmd of status.suppressedCommands) {
614
+ console.log(` ${utils.COLORS.dim}${cmd}${utils.COLORS.reset}`);
615
+ }
616
+ }
617
+
618
+ console.log(`
619
+ ${utils.COLORS.bold}Commands${utils.COLORS.reset}
620
+ bootspring suggest --enable-auto Enable auto-suggestions
621
+ bootspring suggest --disable-auto Disable auto-suggestions
622
+ bootspring suggest --category <name> Toggle category
623
+ bootspring suggest --suppress <cmd> Suppress a command
624
+ `);
625
+ }
626
+
627
+ /**
628
+ * Toggle auto-suggest
629
+ */
630
+ function toggleAutoSuggest(projectRoot: string, enable: boolean): void {
631
+ const { AutoSuggest } = require('../intelligence/auto-suggest') as { AutoSuggest: AutoSuggestModule };
632
+ const autoSuggest = new AutoSuggest(projectRoot);
633
+ autoSuggest.setEnabled(enable);
634
+
635
+ if (enable) {
636
+ console.log(`${utils.COLORS.green}✓${utils.COLORS.reset} Auto-suggest enabled`);
637
+ console.log(`${utils.COLORS.dim}Bootspring will suggest relevant commands during Claude Code sessions${utils.COLORS.reset}`);
638
+ } else {
639
+ console.log(`${utils.COLORS.yellow}○${utils.COLORS.reset} Auto-suggest disabled`);
640
+ console.log(`${utils.COLORS.dim}Re-enable with: bootspring suggest --enable-auto${utils.COLORS.reset}`);
641
+ }
642
+ }
643
+
644
+ /**
645
+ * Toggle category
646
+ */
647
+ function toggleCategory(projectRoot: string, category: string): void {
648
+ const { AutoSuggest } = require('../intelligence/auto-suggest') as { AutoSuggest: AutoSuggestModule };
649
+ const autoSuggest = new AutoSuggest(projectRoot);
650
+ const status = autoSuggest.getStatus();
651
+
652
+ if (!Object.prototype.hasOwnProperty.call(status.categories, category)) {
653
+ console.log(`${utils.COLORS.red}Unknown category: ${category}${utils.COLORS.reset}`);
654
+ console.log(`Available: ${Object.keys(status.categories).join(', ')}`);
655
+ return;
656
+ }
657
+
658
+ const newState = !status.categories[category];
659
+ autoSuggest.setCategory(category, newState);
660
+
661
+ const icon = newState ? `${utils.COLORS.green}✓${utils.COLORS.reset}` : `${utils.COLORS.dim}○${utils.COLORS.reset}`;
662
+ console.log(`${icon} Category '${category}' ${newState ? 'enabled' : 'disabled'}`);
663
+ }
664
+
665
+ /**
666
+ * Suppress or unsuppress a command
667
+ */
668
+ function suppressCommand(projectRoot: string, command: string, suppress = true): void {
669
+ const { AutoSuggest } = require('../intelligence/auto-suggest') as { AutoSuggest: AutoSuggestModule };
670
+ const autoSuggest = new AutoSuggest(projectRoot);
671
+
672
+ if (suppress) {
673
+ autoSuggest.suppressCommand(command);
674
+ console.log(`${utils.COLORS.dim}○${utils.COLORS.reset} Suppressed suggestions for: ${command}`);
675
+ } else {
676
+ autoSuggest.unsuppressCommand(command);
677
+ console.log(`${utils.COLORS.green}✓${utils.COLORS.reset} Enabled suggestions for: ${command}`);
678
+ }
679
+ }
680
+
681
+ /**
682
+ * Analyze text for suggestions (for testing/demo)
683
+ */
684
+ function analyzeText(projectRoot: string, text: string): void {
685
+ const { AutoSuggest } = require('../intelligence/auto-suggest') as { AutoSuggest: AutoSuggestModule };
686
+ const autoSuggest = new AutoSuggest(projectRoot);
687
+ const suggestions = autoSuggest.analyze(text);
688
+
689
+ if (suggestions.length === 0) {
690
+ console.log(`${utils.COLORS.dim}No suggestions for this input${utils.COLORS.reset}`);
691
+ return;
692
+ }
693
+
694
+ console.log(`
695
+ ${utils.COLORS.cyan}${utils.COLORS.bold}💡 Suggestions${utils.COLORS.reset}
696
+ `);
697
+
698
+ for (const suggestion of suggestions) {
699
+ console.log(` ${utils.COLORS.green}→${utils.COLORS.reset} ${utils.COLORS.bold}${suggestion.command}${utils.COLORS.reset}`);
700
+ console.log(` ${utils.COLORS.dim}${suggestion.description}${utils.COLORS.reset}`);
701
+ console.log(` ${utils.COLORS.dim}Confidence: ${(suggestion.confidence * 100).toFixed(0)}%${utils.COLORS.reset}`);
702
+ console.log();
703
+ }
704
+ }
705
+
706
+ /**
707
+ * Generate AI-ready prompts based on project analysis
708
+ */
709
+ async function generatePrompts(projectRoot: string, options: { limit?: number | undefined; json?: boolean | undefined } = {}): Promise<Prompt[] | undefined> {
710
+ const prompts: Prompt[] = [];
711
+ const projectState = loadProjectState(projectRoot);
712
+ const checkpoints = projectState?.checkpoints || {};
713
+
714
+ // Analyze what's missing and generate prompts
715
+
716
+ // 1. Check for missing planning docs
717
+ const planningDocs: Array<{ file: string; checkpoint: string; name: string; prompt: string }> = [
718
+ { file: 'planning/PRD.md', checkpoint: 'prd', name: 'Product Requirements Document',
719
+ prompt: `Create a comprehensive PRD.md file in the planning/ folder. Include:
720
+ - Product vision and goals
721
+ - Target users and personas
722
+ - Core features and requirements (prioritized)
723
+ - Success metrics and KPIs
724
+ - Timeline and milestones
725
+ - Out of scope items
726
+
727
+ Base it on the existing project structure and any available context.` },
728
+ { file: 'planning/TECHNICAL_SPEC.md', checkpoint: 'technical_spec', name: 'Technical Specification',
729
+ prompt: `Create a TECHNICAL_SPEC.md file in planning/ folder. Include:
730
+ - System architecture overview
731
+ - Technology stack decisions with rationale
732
+ - Data models and schemas
733
+ - API design patterns
734
+ - Security considerations
735
+ - Performance requirements
736
+ - Integration points` },
737
+ { file: 'planning/ARCHITECTURE.md', checkpoint: 'architecture', name: 'Architecture Document',
738
+ prompt: `Create an ARCHITECTURE.md file in planning/ folder. Include:
739
+ - High-level system diagram (ASCII or Mermaid)
740
+ - Component breakdown and responsibilities
741
+ - Data flow between components
742
+ - External service integrations
743
+ - Deployment architecture
744
+ - Scalability considerations` },
745
+ { file: 'planning/DATABASE_SCHEMA.md', checkpoint: 'database_schema', name: 'Database Schema',
746
+ prompt: `Create a DATABASE_SCHEMA.md file in planning/ folder. Include:
747
+ - Entity relationship diagram (Mermaid or ASCII)
748
+ - Table definitions with columns and types
749
+ - Relationships and foreign keys
750
+ - Indexes for performance
751
+ - Migration strategy
752
+ - Sample queries for common operations` },
753
+ { file: 'planning/API_CONTRACTS.md', checkpoint: 'api_contracts', name: 'API Contracts',
754
+ prompt: `Create an API_CONTRACTS.md file in planning/ folder. Include:
755
+ - RESTful endpoint definitions
756
+ - Request/response schemas (with examples)
757
+ - Authentication requirements
758
+ - Rate limiting policies
759
+ - Error response formats
760
+ - Versioning strategy` },
761
+ { file: 'planning/TEST_PLAN.md', checkpoint: 'tests_planned', name: 'Test Plan',
762
+ prompt: `Create a TEST_PLAN.md file in planning/ folder. Include:
763
+ - Testing strategy (unit, integration, e2e)
764
+ - Test coverage targets
765
+ - Critical paths to test
766
+ - Test data requirements
767
+ - CI/CD integration plan
768
+ - Performance testing approach` }
769
+ ];
770
+
771
+ for (const doc of planningDocs) {
772
+ const filePath = path.join(projectRoot, doc.file);
773
+ if (!fs.existsSync(filePath) && !checkpoints[doc.checkpoint]?.completed) {
774
+ prompts.push({
775
+ category: 'Documentation',
776
+ priority: doc.checkpoint === 'prd' ? 'high' : 'medium',
777
+ title: `Create ${doc.name}`,
778
+ prompt: doc.prompt,
779
+ command: `bootspring checkpoint complete ${doc.checkpoint}`
780
+ });
781
+ }
782
+ }
783
+
784
+ // 2. Check for missing test setup
785
+ const hasTests = fs.existsSync(path.join(projectRoot, '__tests__')) ||
786
+ fs.existsSync(path.join(projectRoot, 'tests')) ||
787
+ fs.existsSync(path.join(projectRoot, 'test'));
788
+ const hasTestConfig = fs.existsSync(path.join(projectRoot, 'vitest.config.ts')) ||
789
+ fs.existsSync(path.join(projectRoot, 'jest.config.js')) ||
790
+ fs.existsSync(path.join(projectRoot, 'vitest.config.js'));
791
+
792
+ if (!hasTests || !hasTestConfig) {
793
+ prompts.push({
794
+ category: 'Quality',
795
+ priority: 'high',
796
+ title: 'Set up test infrastructure',
797
+ prompt: `Set up a comprehensive testing infrastructure for this project:
798
+
799
+ 1. Install vitest and testing utilities
800
+ 2. Create vitest.config.ts with proper configuration
801
+ 3. Set up test directory structure (__tests__/ or tests/)
802
+ 4. Create example unit tests for existing modules
803
+ 5. Add test scripts to package.json
804
+ 6. Configure code coverage reporting
805
+
806
+ Focus on making tests easy to write and maintain.`,
807
+ command: 'npm install -D vitest @vitest/coverage-v8'
808
+ });
809
+ }
810
+
811
+ // 3. Check for CI/CD
812
+ const hasGithubActions = fs.existsSync(path.join(projectRoot, '.github/workflows'));
813
+ if (!hasGithubActions) {
814
+ prompts.push({
815
+ category: 'DevOps',
816
+ priority: 'medium',
817
+ title: 'Add GitHub Actions CI/CD',
818
+ prompt: `Create a GitHub Actions CI/CD pipeline in .github/workflows/ci.yml:
819
+
820
+ 1. Run on push to main and pull requests
821
+ 2. Set up Node.js environment
822
+ 3. Install dependencies (npm ci)
823
+ 4. Run linting (if configured)
824
+ 5. Run tests with coverage
825
+ 6. Build the project
826
+ 7. Add caching for node_modules
827
+
828
+ Make it fast and reliable for developer productivity.`,
829
+ command: 'mkdir -p .github/workflows'
830
+ });
831
+ }
832
+
833
+ // 4. Check for security setup
834
+ const hasEnvExample = fs.existsSync(path.join(projectRoot, '.env.example'));
835
+ if (!hasEnvExample && fs.existsSync(path.join(projectRoot, '.env'))) {
836
+ prompts.push({
837
+ category: 'Security',
838
+ priority: 'high',
839
+ title: 'Create .env.example',
840
+ prompt: `Create a .env.example file documenting all required environment variables:
841
+
842
+ 1. List all env vars from .env (without actual values)
843
+ 2. Add comments explaining each variable
844
+ 3. Group by category (database, auth, external services)
845
+ 4. Include example/placeholder values where helpful
846
+ 5. Document which are required vs optional
847
+
848
+ This helps new developers set up the project securely.`,
849
+ command: null
850
+ });
851
+ }
852
+
853
+ // 5. Check for README quality
854
+ const readmePath = path.join(projectRoot, 'README.md');
855
+ let readmeScore = 0;
856
+ if (fs.existsSync(readmePath)) {
857
+ const readme = fs.readFileSync(readmePath, 'utf-8');
858
+ if (readme.includes('## Installation')) readmeScore++;
859
+ if (readme.includes('## Usage')) readmeScore++;
860
+ if (readme.includes('## Contributing')) readmeScore++;
861
+ if (readme.includes('## License')) readmeScore++;
862
+ if (readme.length > 1000) readmeScore++;
863
+ }
864
+
865
+ if (readmeScore < 3) {
866
+ prompts.push({
867
+ category: 'Documentation',
868
+ priority: 'medium',
869
+ title: 'Improve README.md',
870
+ prompt: `Enhance the README.md to be comprehensive and welcoming:
871
+
872
+ 1. Clear project description and value proposition
873
+ 2. Installation instructions (step by step)
874
+ 3. Quick start / Usage examples
875
+ 4. Configuration options
876
+ 5. Contributing guidelines
877
+ 6. License information
878
+ 7. Links to documentation
879
+ 8. Badges (build status, version, license)
880
+
881
+ Make it easy for new users to understand and get started.`,
882
+ command: null
883
+ });
884
+ }
885
+
886
+ // 6. Check for TypeScript setup
887
+ const hasTypeScript = fs.existsSync(path.join(projectRoot, 'tsconfig.json'));
888
+ const packageJson = loadPackageJson(projectRoot);
889
+ const hasJsFiles = fs.readdirSync(projectRoot).some(f => f.endsWith('.js') && !f.includes('config'));
890
+
891
+ if (!hasTypeScript && hasJsFiles) {
892
+ prompts.push({
893
+ category: 'Quality',
894
+ priority: 'low',
895
+ title: 'Migrate to TypeScript',
896
+ prompt: `Convert this JavaScript project to TypeScript:
897
+
898
+ 1. Install typescript and @types packages
899
+ 2. Create tsconfig.json with strict settings
900
+ 3. Rename .js files to .ts (start with entry points)
901
+ 4. Add type annotations to functions and variables
902
+ 5. Fix type errors iteratively
903
+ 6. Update build scripts
904
+
905
+ Start with the most critical files and expand gradually.`,
906
+ command: 'npm install -D typescript @types/node'
907
+ });
908
+ }
909
+
910
+ // 7. Check for linting
911
+ const hasEslint = fs.existsSync(path.join(projectRoot, '.eslintrc.js')) ||
912
+ fs.existsSync(path.join(projectRoot, '.eslintrc.json')) ||
913
+ fs.existsSync(path.join(projectRoot, 'eslint.config.js'));
914
+
915
+ if (!hasEslint) {
916
+ prompts.push({
917
+ category: 'Quality',
918
+ priority: 'medium',
919
+ title: 'Set up ESLint',
920
+ prompt: `Set up ESLint for code quality and consistency:
921
+
922
+ 1. Install eslint and relevant plugins
923
+ 2. Create eslint.config.js (flat config format)
924
+ 3. Configure rules appropriate for the project
925
+ 4. Add lint script to package.json
926
+ 5. Fix any existing linting issues
927
+ 6. Consider adding Prettier for formatting
928
+
929
+ Use sensible defaults that catch real bugs without being annoying.`,
930
+ command: 'npm install -D eslint'
931
+ });
932
+ }
933
+
934
+ // 8. Project-specific suggestions based on tech stack
935
+ if (packageJson?.dependencies?.next) {
936
+ const hasMiddleware = fs.existsSync(path.join(projectRoot, 'middleware.ts'));
937
+ if (!hasMiddleware) {
938
+ prompts.push({
939
+ category: 'Security',
940
+ priority: 'medium',
941
+ title: 'Add Next.js middleware',
942
+ prompt: `Create a middleware.ts file for Next.js request handling:
943
+
944
+ 1. Set up authentication checks for protected routes
945
+ 2. Add rate limiting headers
946
+ 3. Configure security headers (CSP, HSTS, etc.)
947
+ 4. Handle redirects for legacy URLs
948
+ 5. Add request logging for debugging
949
+
950
+ Keep it performant as middleware runs on every request.`,
951
+ command: null
952
+ });
953
+ }
954
+ }
955
+
956
+ // Group by priority
957
+ const highPriority = prompts.filter(p => p.priority === 'high');
958
+ const mediumPriority = prompts.filter(p => p.priority === 'medium');
959
+ const lowPriority = prompts.filter(p => p.priority === 'low');
960
+
961
+ const limit = options.limit || 5;
962
+ const allPrompts = [...highPriority, ...mediumPriority, ...lowPriority].slice(0, limit);
963
+
964
+ // JSON mode - return data only, no display
965
+ if (options.json) {
966
+ return allPrompts;
967
+ }
968
+
969
+ // Display prompts
970
+ console.log(`
971
+ ${utils.COLORS.cyan}${utils.COLORS.bold}📋 AI-Ready Prompts${utils.COLORS.reset}
972
+ ${utils.COLORS.dim}Copy and paste into Cursor, Claude Code, Codex, etc.${utils.COLORS.reset}
973
+ `);
974
+
975
+ if (prompts.length === 0) {
976
+ console.log(`${utils.COLORS.green}✓${utils.COLORS.reset} Project looks well set up! No urgent suggestions.`);
977
+ console.log(`${utils.COLORS.dim}Run 'bootspring health' for detailed analysis.${utils.COLORS.reset}`);
978
+ return undefined;
979
+ }
980
+
981
+ for (let i = 0; i < allPrompts.length; i++) {
982
+ const p = allPrompts[i];
983
+ if (!p) continue;
984
+ const priorityColor = p.priority === 'high' ? utils.COLORS.red :
985
+ p.priority === 'medium' ? utils.COLORS.yellow : utils.COLORS.dim;
986
+ const priorityLabel = p.priority.toUpperCase();
987
+
988
+ console.log(`${utils.COLORS.bold}${i + 1}. ${p.title}${utils.COLORS.reset} ${priorityColor}[${priorityLabel}]${utils.COLORS.reset} ${utils.COLORS.dim}(${p.category})${utils.COLORS.reset}`);
989
+ console.log();
990
+ console.log(`${utils.COLORS.cyan}┌${'─'.repeat(70)}${utils.COLORS.reset}`);
991
+ console.log(`${utils.COLORS.cyan}│${utils.COLORS.reset}`);
992
+
993
+ // Format prompt for display
994
+ const lines = p.prompt.split('\n');
995
+ for (const line of lines) {
996
+ console.log(`${utils.COLORS.cyan}│${utils.COLORS.reset} ${line}`);
997
+ }
998
+
999
+ console.log(`${utils.COLORS.cyan}│${utils.COLORS.reset}`);
1000
+ console.log(`${utils.COLORS.cyan}└${'─'.repeat(70)}${utils.COLORS.reset}`);
1001
+
1002
+ if (p.command) {
1003
+ console.log(`${utils.COLORS.dim} After: ${p.command}${utils.COLORS.reset}`);
1004
+ }
1005
+ console.log();
1006
+ }
1007
+
1008
+ const remaining = prompts.length - limit;
1009
+ if (remaining > 0) {
1010
+ console.log(`${utils.COLORS.dim}+${remaining} more suggestions. Use --limit to see more.${utils.COLORS.reset}`);
1011
+ }
1012
+
1013
+ console.log(`
1014
+ ${utils.COLORS.bold}Quick Copy:${utils.COLORS.reset} Click a prompt box to copy, then paste into your AI assistant.
1015
+ ${utils.COLORS.dim}Tip: Use 'bootspring suggest prompts --json' for machine-readable output.${utils.COLORS.reset}
1016
+ `);
1017
+
1018
+ return undefined;
1019
+ }
1020
+
1021
+ /**
1022
+ * Load PROJECT_STATE.json
1023
+ */
1024
+ function loadProjectState(projectRoot: string): ProjectState | null {
1025
+ try {
1026
+ const statePath = path.join(projectRoot, 'planning', 'PROJECT_STATE.json');
1027
+ if (fs.existsSync(statePath)) {
1028
+ return JSON.parse(fs.readFileSync(statePath, 'utf-8'));
1029
+ }
1030
+ } catch {
1031
+ // Ignore errors
1032
+ }
1033
+ return null;
1034
+ }
1035
+
1036
+ /**
1037
+ * Load package.json
1038
+ */
1039
+ function loadPackageJson(projectRoot: string): PackageJson | null {
1040
+ try {
1041
+ const pkgPath = path.join(projectRoot, 'package.json');
1042
+ if (fs.existsSync(pkgPath)) {
1043
+ return JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
1044
+ }
1045
+ } catch {
1046
+ // Ignore errors
1047
+ }
1048
+ return null;
1049
+ }
1050
+
1051
+ /**
1052
+ * Show help
1053
+ */
1054
+ function showHelp(): void {
1055
+ console.log(`
1056
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Suggest${utils.COLORS.reset}
1057
+ ${utils.COLORS.dim}AI-powered recommendations and auto-suggestions${utils.COLORS.reset}
1058
+
1059
+ ${utils.COLORS.bold}Usage:${utils.COLORS.reset}
1060
+ bootspring suggest [command] [options]
1061
+
1062
+ ${utils.COLORS.bold}Commands:${utils.COLORS.reset}
1063
+ ${utils.COLORS.cyan}(default)${utils.COLORS.reset} Show all recommendations
1064
+ ${utils.COLORS.cyan}prompts${utils.COLORS.reset} Generate copy-paste prompts for AI assistants
1065
+ ${utils.COLORS.cyan}next${utils.COLORS.reset} Show single next action
1066
+ ${utils.COLORS.cyan}learning${utils.COLORS.reset} Show learning statistics
1067
+ ${utils.COLORS.cyan}workflows${utils.COLORS.reset} Show workflow recommendations only
1068
+ ${utils.COLORS.cyan}skills${utils.COLORS.reset} Show skill recommendations only
1069
+ ${utils.COLORS.cyan}config${utils.COLORS.reset} Show auto-suggest configuration
1070
+ ${utils.COLORS.cyan}analyze <text>${utils.COLORS.reset} Test auto-suggest on text
1071
+
1072
+ ${utils.COLORS.bold}Auto-Suggest Options:${utils.COLORS.reset}
1073
+ --enable-auto Enable proactive suggestions in Claude Code
1074
+ --disable-auto Disable auto-suggestions
1075
+ --category <name> Toggle a suggestion category
1076
+ --suppress <cmd> Stop suggesting a specific command
1077
+ --unsuppress <cmd> Resume suggesting a command
1078
+
1079
+ ${utils.COLORS.bold}Other Options:${utils.COLORS.reset}
1080
+ --limit <n> Number of recommendations (default: 5)
1081
+ --json Output as JSON
1082
+
1083
+ ${utils.COLORS.bold}How Auto-Suggest Works:${utils.COLORS.reset}
1084
+ 1. Monitors your messages in Claude Code
1085
+ 2. Detects when Bootspring features could help
1086
+ 3. Proactively suggests relevant commands
1087
+ 4. Learn from your preferences over time
1088
+
1089
+ ${utils.COLORS.bold}Examples:${utils.COLORS.reset}
1090
+ bootspring suggest # Show recommendations
1091
+ bootspring suggest prompts # AI-ready prompts for Cursor/Claude Code
1092
+ bootspring suggest prompts --limit 10 # Show more prompts
1093
+ bootspring suggest next # Single next action
1094
+ bootspring suggest --enable-auto # Enable auto-suggestions
1095
+ bootspring suggest config # View auto-suggest settings
1096
+ bootspring suggest analyze "deploy to vercel"
1097
+ `);
1098
+ }
1099
+
1100
+ /**
1101
+ * Run suggest command
1102
+ */
1103
+ export async function run(args: string[]): Promise<void> {
1104
+ const parsedArgs = utils.parseArgs(args);
1105
+ const subcommand = parsedArgs._[0];
1106
+
1107
+ if (subcommand === 'help' || subcommand === '-h' || subcommand === '--help') {
1108
+ showHelp();
1109
+ return;
1110
+ }
1111
+
1112
+ const cfg = config.load();
1113
+ const projectRoot = cfg._projectRoot;
1114
+
1115
+ // Handle auto-suggest flags first
1116
+ if (parsedArgs['enable-auto']) {
1117
+ toggleAutoSuggest(projectRoot, true);
1118
+ return;
1119
+ }
1120
+
1121
+ if (parsedArgs['disable-auto']) {
1122
+ toggleAutoSuggest(projectRoot, false);
1123
+ return;
1124
+ }
1125
+
1126
+ if (parsedArgs.category) {
1127
+ toggleCategory(projectRoot, parsedArgs.category);
1128
+ return;
1129
+ }
1130
+
1131
+ if (parsedArgs.suppress) {
1132
+ suppressCommand(projectRoot, parsedArgs.suppress, true);
1133
+ return;
1134
+ }
1135
+
1136
+ if (parsedArgs.unsuppress) {
1137
+ suppressCommand(projectRoot, parsedArgs.unsuppress, false);
1138
+ return;
1139
+ }
1140
+
1141
+ const limit = typeof parsedArgs.limit === 'number' ? parsedArgs.limit :
1142
+ typeof parsedArgs.limit === 'string' ? parseInt(parsedArgs.limit, 10) : 5;
1143
+
1144
+ switch (subcommand) {
1145
+ case 'prompts':
1146
+ case 'prompt':
1147
+ case 'ai': {
1148
+ const promptResult = await generatePrompts(projectRoot, {
1149
+ limit,
1150
+ json: parsedArgs.json
1151
+ });
1152
+ if (parsedArgs.json && promptResult) {
1153
+ console.log(JSON.stringify(promptResult, null, 2));
1154
+ }
1155
+ break;
1156
+ }
1157
+
1158
+ case 'next':
1159
+ await showNextAction(projectRoot);
1160
+ break;
1161
+
1162
+ case 'learning':
1163
+ case 'stats':
1164
+ showLearningStats();
1165
+ break;
1166
+
1167
+ case 'config':
1168
+ case 'auto':
1169
+ case 'settings':
1170
+ showAutoSuggestConfig(projectRoot);
1171
+ break;
1172
+
1173
+ case 'analyze':
1174
+ case 'test': {
1175
+ const text = parsedArgs._.slice(1).join(' ');
1176
+ if (!text) {
1177
+ console.log(`${utils.COLORS.yellow}Usage: bootspring suggest analyze "<text>"${utils.COLORS.reset}`);
1178
+ return;
1179
+ }
1180
+ analyzeText(projectRoot, text);
1181
+ break;
1182
+ }
1183
+
1184
+ case 'workflows': {
1185
+ const wfResult = await showRecommendations(projectRoot, {
1186
+ limit,
1187
+ json: parsedArgs.json
1188
+ });
1189
+ if (parsedArgs.json) {
1190
+ console.log(JSON.stringify({ workflows: wfResult?.workflows || [] }, null, 2));
1191
+ }
1192
+ break;
1193
+ }
1194
+
1195
+ case 'skills': {
1196
+ const skResult = await showRecommendations(projectRoot, {
1197
+ limit,
1198
+ json: parsedArgs.json
1199
+ });
1200
+ if (parsedArgs.json) {
1201
+ console.log(JSON.stringify({ skills: skResult?.skills || [] }, null, 2));
1202
+ }
1203
+ break;
1204
+ }
1205
+
1206
+ default: {
1207
+ const result = await showRecommendations(projectRoot, {
1208
+ limit,
1209
+ json: parsedArgs.json
1210
+ });
1211
+ if (parsedArgs.json) {
1212
+ console.log(JSON.stringify(result, null, 2));
1213
+ }
1214
+ }
1215
+ }
1216
+ }
1217
+
1218
+ export {
1219
+ getProjectContext,
1220
+ showRecommendations,
1221
+ generatePrompts,
1222
+ toggleAutoSuggest,
1223
+ showAutoSuggestConfig,
1224
+ analyzeText
1225
+ };