@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.
- package/bin/bootspring.js +5 -0
- package/cli/org.js +474 -0
- package/cli/preseed/index.js +16 -0
- package/cli/preseed/interactive.js +143 -0
- package/cli/preseed/templates.js +227 -0
- package/cli/preseed.js +9 -301
- package/cli/seed/builders/ai-context-builder.js +85 -0
- package/cli/seed/builders/index.js +13 -0
- package/cli/seed/builders/seed-builder.js +272 -0
- package/cli/seed/extractors/content-extractors.js +383 -0
- package/cli/seed/extractors/index.js +47 -0
- package/cli/seed/extractors/metadata-extractors.js +167 -0
- package/cli/seed/extractors/section-extractor.js +54 -0
- package/cli/seed/extractors/stack-extractors.js +228 -0
- package/cli/seed/index.js +18 -0
- package/cli/seed/utils/folder-structure.js +84 -0
- package/cli/seed/utils/index.js +11 -0
- package/cli/seed.js +23 -1074
- package/core/api-client.js +77 -0
- package/core/entitlements.js +36 -0
- package/core/organizations.js +223 -0
- package/core/policies.js +51 -6
- package/core/policy-matrix.js +303 -0
- package/core/project-context.js +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +3220 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/context-McpJQa_2.d.ts +5710 -0
- package/dist/core/index.d.ts +635 -0
- package/dist/core/index.js +2593 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index-QqbeEiDm.d.ts +857 -0
- package/dist/index-UiYCgwiH.d.ts +174 -0
- package/dist/index.d.ts +453 -0
- package/dist/index.js +44228 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.js +41173 -0
- package/dist/mcp/index.js.map +1 -0
- package/generators/index.ts +82 -0
- package/intelligence/orchestrator/config/failure-signatures.js +48 -0
- package/intelligence/orchestrator/config/index.js +23 -0
- package/intelligence/orchestrator/config/pack-lifecycle.js +262 -0
- package/intelligence/orchestrator/config/phases.js +111 -0
- package/intelligence/orchestrator/config/remediation.js +150 -0
- package/intelligence/orchestrator/config/workflows.js +168 -0
- package/intelligence/orchestrator/core/index.js +16 -0
- package/intelligence/orchestrator/core/state-manager.js +88 -0
- package/intelligence/orchestrator/core/telemetry.js +24 -0
- package/intelligence/orchestrator/index.js +17 -0
- package/intelligence/orchestrator.js +17 -512
- package/mcp/contracts/mcp-contract.v1.json +1 -1
- package/package.json +16 -3
- package/src/cli/agent.ts +703 -0
- package/src/cli/analyze.ts +640 -0
- package/src/cli/audit.ts +707 -0
- package/src/cli/auth.ts +930 -0
- package/src/cli/billing.ts +364 -0
- package/src/cli/build.ts +1089 -0
- package/src/cli/business.ts +508 -0
- package/src/cli/checkpoint-utils.ts +236 -0
- package/src/cli/checkpoint.ts +757 -0
- package/src/cli/cloud-sync.ts +534 -0
- package/src/cli/content.ts +273 -0
- package/src/cli/context.ts +667 -0
- package/src/cli/dashboard.ts +133 -0
- package/src/cli/deploy.ts +704 -0
- package/src/cli/doctor.ts +480 -0
- package/src/cli/fundraise.ts +494 -0
- package/src/cli/generate.ts +346 -0
- package/src/cli/github-cmd.ts +566 -0
- package/src/cli/health.ts +599 -0
- package/src/cli/index.ts +113 -0
- package/src/cli/init.ts +838 -0
- package/src/cli/legal.ts +495 -0
- package/src/cli/log.ts +316 -0
- package/src/cli/loop.ts +1660 -0
- package/src/cli/manager.ts +878 -0
- package/src/cli/mcp.ts +275 -0
- package/src/cli/memory.ts +346 -0
- package/src/cli/metrics.ts +590 -0
- package/src/cli/monitor.ts +960 -0
- package/src/cli/mvp.ts +662 -0
- package/src/cli/onboard.ts +663 -0
- package/src/cli/orchestrator.ts +622 -0
- package/src/cli/plugin.ts +483 -0
- package/src/cli/prd.ts +671 -0
- package/src/cli/preseed-start.ts +1633 -0
- package/src/cli/preseed.ts +2434 -0
- package/src/cli/project.ts +526 -0
- package/src/cli/quality.ts +885 -0
- package/src/cli/security.ts +1079 -0
- package/src/cli/seed.ts +1224 -0
- package/src/cli/skill.ts +537 -0
- package/src/cli/suggest.ts +1225 -0
- package/src/cli/switch.ts +518 -0
- package/src/cli/task.ts +780 -0
- package/src/cli/telemetry.ts +172 -0
- package/src/cli/todo.ts +627 -0
- package/src/cli/types.ts +15 -0
- package/src/cli/update.ts +334 -0
- package/src/cli/visualize.ts +609 -0
- package/src/cli/watch.ts +895 -0
- package/src/cli/workspace.ts +709 -0
- package/src/core/action-recorder.ts +673 -0
- package/src/core/analyze-workflow.ts +1453 -0
- package/src/core/api-client.ts +1120 -0
- package/src/core/audit-workflow.ts +1681 -0
- package/src/core/auth.ts +471 -0
- package/src/core/build-orchestrator.ts +509 -0
- package/src/core/build-state.ts +621 -0
- package/src/core/checkpoint-engine.ts +482 -0
- package/src/core/config.ts +1285 -0
- package/src/core/context-loader.ts +694 -0
- package/src/core/context.ts +410 -0
- package/src/core/deploy-workflow.ts +1085 -0
- package/src/core/entitlements.ts +322 -0
- package/src/core/github-sync.ts +720 -0
- package/src/core/index.ts +981 -0
- package/src/core/ingest.ts +1186 -0
- package/src/core/metrics-engine.ts +886 -0
- package/src/core/mvp.ts +847 -0
- package/src/core/onboard-workflow.ts +1293 -0
- package/src/core/policies.ts +81 -0
- package/src/core/preseed-workflow.ts +1163 -0
- package/src/core/preseed.ts +1826 -0
- package/src/core/project-context.ts +380 -0
- package/src/core/project-state.ts +699 -0
- package/src/core/r2-sync.ts +691 -0
- package/src/core/scaffold.ts +1715 -0
- package/src/core/session.ts +286 -0
- package/src/core/task-extractor.ts +799 -0
- package/src/core/telemetry.ts +371 -0
- package/src/core/tier-enforcement.ts +737 -0
- package/src/core/utils.ts +437 -0
- package/src/index.ts +29 -0
- package/src/intelligence/agent-collab.ts +2376 -0
- package/src/intelligence/auto-suggest.ts +713 -0
- package/src/intelligence/content-gen.ts +1351 -0
- package/src/intelligence/cross-project.ts +1692 -0
- package/src/intelligence/git-memory.ts +529 -0
- package/src/intelligence/index.ts +318 -0
- package/src/intelligence/orchestrator.ts +534 -0
- package/src/intelligence/prd.ts +466 -0
- package/src/intelligence/recommendations.ts +982 -0
- package/src/intelligence/workflow-composer.ts +1472 -0
- package/src/mcp/capabilities.ts +233 -0
- package/src/mcp/index.ts +37 -0
- package/src/mcp/registry.ts +1268 -0
- package/src/mcp/response-formatter.ts +797 -0
- package/src/mcp/server.ts +240 -0
- package/src/types/agent.ts +69 -0
- package/src/types/config.ts +86 -0
- package/src/types/context.ts +77 -0
- package/src/types/index.ts +53 -0
- package/src/types/mcp.ts +91 -0
- package/src/types/skills.ts +47 -0
- package/src/types/workflow.ts +155 -0
- 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
|
+
};
|