@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,878 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring Manager Command
|
|
3
|
+
* Overview of all projects without entering each one
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* list [--sort] [--type] List all projects with health scores (default)
|
|
7
|
+
* show <name> Detailed project view
|
|
8
|
+
* export Export report as markdown
|
|
9
|
+
* scan Scan for local projects in workspace
|
|
10
|
+
*
|
|
11
|
+
* @package bootspring
|
|
12
|
+
* @command manager
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import * as os from 'os';
|
|
18
|
+
|
|
19
|
+
// Type interfaces for JS modules
|
|
20
|
+
interface Colors {
|
|
21
|
+
reset: string;
|
|
22
|
+
bold: string;
|
|
23
|
+
dim: string;
|
|
24
|
+
cyan: string;
|
|
25
|
+
green: string;
|
|
26
|
+
yellow: string;
|
|
27
|
+
red: string;
|
|
28
|
+
magenta: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface PrintModule {
|
|
32
|
+
error(msg: string): void;
|
|
33
|
+
dim(msg: string): void;
|
|
34
|
+
warning(msg: string): void;
|
|
35
|
+
success(msg: string): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface Spinner {
|
|
39
|
+
start(): Spinner;
|
|
40
|
+
stop(): void;
|
|
41
|
+
succeed(text: string): void;
|
|
42
|
+
fail(text: string): void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface ParsedArgs {
|
|
46
|
+
_: string[];
|
|
47
|
+
sort?: string | undefined;
|
|
48
|
+
type?: string | undefined;
|
|
49
|
+
json?: boolean | undefined;
|
|
50
|
+
output?: string | undefined;
|
|
51
|
+
dir?: string | undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface UtilsModule {
|
|
55
|
+
COLORS: Colors;
|
|
56
|
+
print: PrintModule;
|
|
57
|
+
parseArgs(args: string[]): ParsedArgs;
|
|
58
|
+
createSpinner(text: string): Spinner;
|
|
59
|
+
formatRelativeTime(date: Date): string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface SessionProject {
|
|
63
|
+
name?: string | undefined;
|
|
64
|
+
path?: string | undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface SessionModule {
|
|
68
|
+
getRecentProjects(): SessionProject[] | null;
|
|
69
|
+
getCurrentProject(): SessionProject | null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface GitHubStats {
|
|
73
|
+
totalCommits: number;
|
|
74
|
+
openPRs: number;
|
|
75
|
+
contributors: number;
|
|
76
|
+
lastCommitMessage?: string | undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface GitHubInfo {
|
|
80
|
+
connected: boolean;
|
|
81
|
+
owner?: string | undefined;
|
|
82
|
+
repo?: string | undefined;
|
|
83
|
+
repositoryUrl?: string | undefined;
|
|
84
|
+
stats?: GitHubStats | undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface UsageInfo {
|
|
88
|
+
currentPeriod: string;
|
|
89
|
+
contextGenerations: number;
|
|
90
|
+
agentInvocations: number;
|
|
91
|
+
skillSearches: number;
|
|
92
|
+
mcpCalls: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
interface HealthInfo {
|
|
96
|
+
score?: number | undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
interface ProjectStateData {
|
|
100
|
+
projectType?: string | undefined;
|
|
101
|
+
autoTagged?: boolean | undefined;
|
|
102
|
+
github?: GitHubInfo | undefined;
|
|
103
|
+
usage?: UsageInfo | undefined;
|
|
104
|
+
health?: HealthInfo | undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface ProjectStateModule {
|
|
108
|
+
loadState(projectRoot: string): ProjectStateData | null;
|
|
109
|
+
getCheckpointProgress(projectRoot: string): CheckpointProgress;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface CheckpointProgress {
|
|
113
|
+
percentage: number;
|
|
114
|
+
completed: number;
|
|
115
|
+
total: number;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface Checkpoint {
|
|
119
|
+
label: string;
|
|
120
|
+
completed: boolean;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
interface CheckpointStatus {
|
|
124
|
+
exists: boolean;
|
|
125
|
+
checkpoints?: Checkpoint[] | undefined;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
interface CheckpointEngineModule {
|
|
129
|
+
getColoredProgressBar(percentage: number, width: number): string;
|
|
130
|
+
getCheckpointStatus(projectRoot: string): CheckpointStatus;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
interface TelemetryModule {
|
|
134
|
+
emitEvent(event: string, data: Record<string, unknown>): void;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const config = require('../core/config');
|
|
138
|
+
const utils = require('../core/utils') as UtilsModule;
|
|
139
|
+
const session = require('../core/session') as SessionModule;
|
|
140
|
+
const projectState = require('../core/project-state') as ProjectStateModule;
|
|
141
|
+
const checkpointEngine = require('../core/checkpoint-engine') as CheckpointEngineModule;
|
|
142
|
+
const telemetry = require('../core/telemetry') as TelemetryModule;
|
|
143
|
+
|
|
144
|
+
// ============================================================================
|
|
145
|
+
// Interfaces
|
|
146
|
+
// ============================================================================
|
|
147
|
+
|
|
148
|
+
interface Project {
|
|
149
|
+
name?: string | undefined;
|
|
150
|
+
path?: string | undefined;
|
|
151
|
+
source?: string | undefined;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
interface HealthGrade {
|
|
155
|
+
grade: string;
|
|
156
|
+
color: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
interface EnrichedProject extends Project {
|
|
160
|
+
exists: boolean;
|
|
161
|
+
health: number;
|
|
162
|
+
healthGrade?: HealthGrade | undefined;
|
|
163
|
+
checkpointProgress: CheckpointProgress;
|
|
164
|
+
projectType: string | null;
|
|
165
|
+
autoTagged?: boolean | undefined;
|
|
166
|
+
github: GitHubInfo | null;
|
|
167
|
+
usage?: UsageInfo | null | undefined;
|
|
168
|
+
lastActivity: Date | null;
|
|
169
|
+
stateExists?: boolean | undefined;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
interface WorkspaceFile {
|
|
173
|
+
projects?: string[] | undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
interface ListOptions {
|
|
177
|
+
sort?: string | null | undefined;
|
|
178
|
+
type?: string | null | undefined;
|
|
179
|
+
json?: boolean | undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
interface ShowOptions {
|
|
183
|
+
json?: boolean | undefined;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
interface ExportOptions {
|
|
187
|
+
output?: string | null | undefined;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
interface ScanOptions {
|
|
191
|
+
dir?: string | null | undefined;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
interface ScanIndicators {
|
|
195
|
+
config: boolean;
|
|
196
|
+
planning: boolean;
|
|
197
|
+
bootspringDir: boolean;
|
|
198
|
+
packageJson: boolean;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
interface ScannedProject {
|
|
202
|
+
name: string;
|
|
203
|
+
path: string;
|
|
204
|
+
indicators: ScanIndicators;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ============================================================================
|
|
208
|
+
// Project Discovery
|
|
209
|
+
// ============================================================================
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get all known projects
|
|
213
|
+
* Combines recent projects from session with workspace projects
|
|
214
|
+
*/
|
|
215
|
+
function getKnownProjects(): Project[] {
|
|
216
|
+
const projects: Project[] = [];
|
|
217
|
+
const seenPaths = new Set<string>();
|
|
218
|
+
|
|
219
|
+
// Get recent projects from session
|
|
220
|
+
const recentProjects = session.getRecentProjects() || [];
|
|
221
|
+
for (const project of recentProjects) {
|
|
222
|
+
if (project.path && !seenPaths.has(project.path)) {
|
|
223
|
+
seenPaths.add(project.path);
|
|
224
|
+
projects.push({
|
|
225
|
+
...project,
|
|
226
|
+
source: 'recent'
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Get current project
|
|
232
|
+
const currentProject = session.getCurrentProject();
|
|
233
|
+
if (currentProject?.path && !seenPaths.has(currentProject.path)) {
|
|
234
|
+
seenPaths.add(currentProject.path);
|
|
235
|
+
projects.push({
|
|
236
|
+
...currentProject,
|
|
237
|
+
source: 'current'
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Scan workspace file if exists
|
|
242
|
+
const workspaceFile = path.join(os.homedir(), '.bootspring', 'workspace.json');
|
|
243
|
+
if (fs.existsSync(workspaceFile)) {
|
|
244
|
+
try {
|
|
245
|
+
const workspace = JSON.parse(fs.readFileSync(workspaceFile, 'utf-8')) as WorkspaceFile;
|
|
246
|
+
for (const projectPath of workspace.projects || []) {
|
|
247
|
+
if (!seenPaths.has(projectPath)) {
|
|
248
|
+
seenPaths.add(projectPath);
|
|
249
|
+
const projectName = path.basename(projectPath);
|
|
250
|
+
projects.push({
|
|
251
|
+
name: projectName,
|
|
252
|
+
path: projectPath,
|
|
253
|
+
source: 'workspace'
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
} catch {
|
|
258
|
+
// Ignore workspace read errors
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return projects;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Get detailed info for a project
|
|
267
|
+
*/
|
|
268
|
+
function getProjectDetails(project: Project): EnrichedProject {
|
|
269
|
+
const projectRoot = project.path;
|
|
270
|
+
|
|
271
|
+
if (!projectRoot || !fs.existsSync(projectRoot)) {
|
|
272
|
+
return {
|
|
273
|
+
...project,
|
|
274
|
+
exists: false,
|
|
275
|
+
health: 0,
|
|
276
|
+
checkpointProgress: { percentage: 0, completed: 0, total: 0 },
|
|
277
|
+
projectType: null,
|
|
278
|
+
github: null,
|
|
279
|
+
lastActivity: null
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Load state
|
|
284
|
+
const state = projectState.loadState(projectRoot);
|
|
285
|
+
|
|
286
|
+
// Get checkpoint progress
|
|
287
|
+
let checkpointProgress: CheckpointProgress = { percentage: 0, completed: 0, total: 0 };
|
|
288
|
+
if (state) {
|
|
289
|
+
checkpointProgress = projectState.getCheckpointProgress(projectRoot);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Get last activity from git or file timestamps
|
|
293
|
+
let lastActivity: Date | null = null;
|
|
294
|
+
try {
|
|
295
|
+
const gitHeadPath = path.join(projectRoot, '.git', 'HEAD');
|
|
296
|
+
if (fs.existsSync(gitHeadPath)) {
|
|
297
|
+
const stats = fs.statSync(gitHeadPath);
|
|
298
|
+
lastActivity = stats.mtime;
|
|
299
|
+
}
|
|
300
|
+
} catch {
|
|
301
|
+
// Ignore git errors
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Calculate health score
|
|
305
|
+
let health = 0;
|
|
306
|
+
if (state?.health?.score) {
|
|
307
|
+
health = state.health.score;
|
|
308
|
+
} else if (checkpointProgress.percentage > 0) {
|
|
309
|
+
// Calculate basic health from checkpoint progress
|
|
310
|
+
health = Math.round(checkpointProgress.percentage * 0.4); // 40% weight
|
|
311
|
+
if (state?.github?.connected) health += 20;
|
|
312
|
+
// Check for quality files
|
|
313
|
+
if (fs.existsSync(path.join(projectRoot, 'tsconfig.json'))) health += 10;
|
|
314
|
+
if (fs.existsSync(path.join(projectRoot, '.eslintrc')) ||
|
|
315
|
+
fs.existsSync(path.join(projectRoot, 'eslint.config.js'))) health += 10;
|
|
316
|
+
if (fs.existsSync(path.join(projectRoot, '__tests__')) ||
|
|
317
|
+
fs.existsSync(path.join(projectRoot, 'tests'))) health += 10;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
...project,
|
|
322
|
+
exists: true,
|
|
323
|
+
health,
|
|
324
|
+
healthGrade: getHealthGrade(health),
|
|
325
|
+
checkpointProgress,
|
|
326
|
+
projectType: state?.projectType || 'development',
|
|
327
|
+
autoTagged: state?.autoTagged || false,
|
|
328
|
+
github: state?.github || null,
|
|
329
|
+
usage: state?.usage || null,
|
|
330
|
+
lastActivity,
|
|
331
|
+
stateExists: !!state
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Get health grade
|
|
337
|
+
*/
|
|
338
|
+
function getHealthGrade(score: number): HealthGrade {
|
|
339
|
+
if (score >= 90) return { grade: 'A', color: utils.COLORS.green };
|
|
340
|
+
if (score >= 80) return { grade: 'B', color: utils.COLORS.green };
|
|
341
|
+
if (score >= 70) return { grade: 'C', color: utils.COLORS.yellow };
|
|
342
|
+
if (score >= 60) return { grade: 'D', color: utils.COLORS.yellow };
|
|
343
|
+
return { grade: 'F', color: utils.COLORS.red };
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ============================================================================
|
|
347
|
+
// Command Handlers
|
|
348
|
+
// ============================================================================
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Get type badge
|
|
352
|
+
*/
|
|
353
|
+
function getTypeBadge(projectType: string | null): string {
|
|
354
|
+
const badges: Record<string, string> = {
|
|
355
|
+
development: `${utils.COLORS.cyan}[DEV]${utils.COLORS.reset}`,
|
|
356
|
+
content: `${utils.COLORS.magenta}[CONTENT]${utils.COLORS.reset}`,
|
|
357
|
+
business: `${utils.COLORS.yellow}[BUSINESS]${utils.COLORS.reset}`
|
|
358
|
+
};
|
|
359
|
+
return badges[projectType || 'development'] || badges.development || '';
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* List all projects
|
|
364
|
+
*/
|
|
365
|
+
function handleList(options: ListOptions = {}): void {
|
|
366
|
+
const projects = getKnownProjects();
|
|
367
|
+
|
|
368
|
+
if (projects.length === 0) {
|
|
369
|
+
console.log(`
|
|
370
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Project Manager${utils.COLORS.reset}
|
|
371
|
+
|
|
372
|
+
${utils.COLORS.dim}No projects found.${utils.COLORS.reset}
|
|
373
|
+
|
|
374
|
+
${utils.COLORS.bold}To add projects:${utils.COLORS.reset}
|
|
375
|
+
1. Use ${utils.COLORS.cyan}bootspring init${utils.COLORS.reset} in a project directory
|
|
376
|
+
2. Or add to workspace: ${utils.COLORS.cyan}bootspring workspace add <path>${utils.COLORS.reset}
|
|
377
|
+
`);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Enhance with details
|
|
382
|
+
const enrichedProjects = projects.map(getProjectDetails);
|
|
383
|
+
|
|
384
|
+
// Filter by type if specified
|
|
385
|
+
let filteredProjects = enrichedProjects;
|
|
386
|
+
if (options.type) {
|
|
387
|
+
filteredProjects = enrichedProjects.filter(p => p.projectType === options.type);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Sort projects
|
|
391
|
+
if (options.sort === 'health') {
|
|
392
|
+
filteredProjects.sort((a, b) => b.health - a.health);
|
|
393
|
+
} else if (options.sort === 'activity') {
|
|
394
|
+
filteredProjects.sort((a, b) => {
|
|
395
|
+
if (!a.lastActivity) return 1;
|
|
396
|
+
if (!b.lastActivity) return -1;
|
|
397
|
+
return new Date(b.lastActivity).getTime() - new Date(a.lastActivity).getTime();
|
|
398
|
+
});
|
|
399
|
+
} else if (options.sort === 'name') {
|
|
400
|
+
filteredProjects.sort((a, b) => (a.name || '').localeCompare(b.name || ''));
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// JSON output
|
|
404
|
+
if (options.json) {
|
|
405
|
+
console.log(JSON.stringify(filteredProjects, null, 2));
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Display
|
|
410
|
+
console.log(`
|
|
411
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Project Manager${utils.COLORS.reset}
|
|
412
|
+
${utils.COLORS.dim}${filteredProjects.length} project(s)${utils.COLORS.reset}
|
|
413
|
+
`);
|
|
414
|
+
|
|
415
|
+
// Get current project for highlighting
|
|
416
|
+
const currentProject = session.getCurrentProject();
|
|
417
|
+
const currentPath = currentProject?.path;
|
|
418
|
+
|
|
419
|
+
for (const project of filteredProjects) {
|
|
420
|
+
const isCurrent = project.path === currentPath;
|
|
421
|
+
const marker = isCurrent ? `${utils.COLORS.green}›${utils.COLORS.reset} ` : ' ';
|
|
422
|
+
|
|
423
|
+
// Type badge
|
|
424
|
+
const typeBadge = getTypeBadge(project.projectType);
|
|
425
|
+
|
|
426
|
+
// Health indicator
|
|
427
|
+
const healthGrade = project.healthGrade || getHealthGrade(0);
|
|
428
|
+
const healthBadge = `${healthGrade.color}${healthGrade.grade}${utils.COLORS.reset}`;
|
|
429
|
+
|
|
430
|
+
// Checkpoint progress
|
|
431
|
+
const progress = project.checkpointProgress;
|
|
432
|
+
const progressBar = checkpointEngine.getColoredProgressBar(progress.percentage, 10);
|
|
433
|
+
|
|
434
|
+
// Name with current indicator
|
|
435
|
+
const nameStyle = isCurrent ? utils.COLORS.bold : '';
|
|
436
|
+
const name = project.name || path.basename(project.path || 'Unknown');
|
|
437
|
+
|
|
438
|
+
console.log(`${marker}${nameStyle}${name}${utils.COLORS.reset} ${typeBadge}`);
|
|
439
|
+
console.log(` Health: ${healthBadge} ${project.health}/100 Checkpoints: ${progressBar} ${progress.completed}/${progress.total}`);
|
|
440
|
+
|
|
441
|
+
// GitHub status
|
|
442
|
+
if (project.github?.connected) {
|
|
443
|
+
console.log(` ${utils.COLORS.dim}GitHub: ${project.github.owner}/${project.github.repo}${utils.COLORS.reset}`);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Last activity
|
|
447
|
+
if (project.lastActivity) {
|
|
448
|
+
console.log(` ${utils.COLORS.dim}Last activity: ${utils.formatRelativeTime(project.lastActivity)}${utils.COLORS.reset}`);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
console.log();
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Summary
|
|
455
|
+
const totalHealth = filteredProjects.reduce((sum, p) => sum + p.health, 0);
|
|
456
|
+
const avgHealth = filteredProjects.length > 0 ? Math.round(totalHealth / filteredProjects.length) : 0;
|
|
457
|
+
const connectedCount = filteredProjects.filter(p => p.github?.connected).length;
|
|
458
|
+
|
|
459
|
+
console.log(`${utils.COLORS.bold}Summary:${utils.COLORS.reset}`);
|
|
460
|
+
console.log(` Average health: ${avgHealth}/100`);
|
|
461
|
+
console.log(` GitHub connected: ${connectedCount}/${filteredProjects.length}`);
|
|
462
|
+
console.log();
|
|
463
|
+
|
|
464
|
+
// Emit telemetry
|
|
465
|
+
telemetry.emitEvent('manager:list', {
|
|
466
|
+
projectCount: filteredProjects.length,
|
|
467
|
+
avgHealth,
|
|
468
|
+
sortBy: options.sort || 'default'
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Show detailed project view
|
|
474
|
+
*/
|
|
475
|
+
function handleShow(nameOrPath: string | undefined, options: ShowOptions = {}): void {
|
|
476
|
+
if (!nameOrPath) {
|
|
477
|
+
utils.print.error('Please specify a project name or path.');
|
|
478
|
+
console.log(`\n${utils.COLORS.bold}Usage:${utils.COLORS.reset} bootspring manager show <name>`);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const projects = getKnownProjects();
|
|
483
|
+
|
|
484
|
+
// Find project by name or path
|
|
485
|
+
let project: Project | undefined = projects.find(p =>
|
|
486
|
+
p.name === nameOrPath ||
|
|
487
|
+
p.path === nameOrPath ||
|
|
488
|
+
(p.path && path.basename(p.path) === nameOrPath)
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
// Try as absolute path
|
|
492
|
+
if (!project && fs.existsSync(nameOrPath)) {
|
|
493
|
+
project = { path: nameOrPath, name: path.basename(nameOrPath) };
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (!project) {
|
|
497
|
+
utils.print.error(`Project not found: ${nameOrPath}`);
|
|
498
|
+
console.log(`\n${utils.COLORS.bold}Available projects:${utils.COLORS.reset}`);
|
|
499
|
+
for (const p of projects) {
|
|
500
|
+
console.log(` - ${p.name || path.basename(p.path || '')}`);
|
|
501
|
+
}
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const details = getProjectDetails(project);
|
|
506
|
+
|
|
507
|
+
// JSON output
|
|
508
|
+
if (options.json) {
|
|
509
|
+
console.log(JSON.stringify(details, null, 2));
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const name = details.name || path.basename(details.path || '');
|
|
514
|
+
const typeBadge = getTypeBadge(details.projectType);
|
|
515
|
+
const healthGrade = details.healthGrade || getHealthGrade(0);
|
|
516
|
+
|
|
517
|
+
console.log(`
|
|
518
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Project: ${name}${utils.COLORS.reset} ${typeBadge}
|
|
519
|
+
${utils.COLORS.dim}${details.path}${utils.COLORS.reset}
|
|
520
|
+
`);
|
|
521
|
+
|
|
522
|
+
// Health score
|
|
523
|
+
console.log(`${utils.COLORS.bold}Health Score${utils.COLORS.reset}`);
|
|
524
|
+
console.log(` ${healthGrade.color}${healthGrade.grade}${utils.COLORS.reset} ${details.health}/100`);
|
|
525
|
+
console.log();
|
|
526
|
+
|
|
527
|
+
// Checkpoint progress
|
|
528
|
+
const progress = details.checkpointProgress;
|
|
529
|
+
console.log(`${utils.COLORS.bold}Checkpoint Progress${utils.COLORS.reset}`);
|
|
530
|
+
console.log(` ${checkpointEngine.getColoredProgressBar(progress.percentage, 30)} ${progress.percentage}%`);
|
|
531
|
+
console.log(` ${progress.completed}/${progress.total} complete`);
|
|
532
|
+
console.log();
|
|
533
|
+
|
|
534
|
+
// If we have full state, show checkpoints
|
|
535
|
+
if (details.stateExists && details.path) {
|
|
536
|
+
const checkpointStatus = checkpointEngine.getCheckpointStatus(details.path);
|
|
537
|
+
if (checkpointStatus.exists && checkpointStatus.checkpoints) {
|
|
538
|
+
console.log(`${utils.COLORS.bold}Checkpoints:${utils.COLORS.reset}`);
|
|
539
|
+
for (const cp of checkpointStatus.checkpoints) {
|
|
540
|
+
const icon = cp.completed
|
|
541
|
+
? `${utils.COLORS.green}✓${utils.COLORS.reset}`
|
|
542
|
+
: `${utils.COLORS.dim}○${utils.COLORS.reset}`;
|
|
543
|
+
console.log(` ${icon} ${cp.label}`);
|
|
544
|
+
}
|
|
545
|
+
console.log();
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// GitHub
|
|
550
|
+
console.log(`${utils.COLORS.bold}GitHub${utils.COLORS.reset}`);
|
|
551
|
+
if (details.github?.connected) {
|
|
552
|
+
console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} Connected: ${details.github.owner}/${details.github.repo}`);
|
|
553
|
+
if (details.github.stats) {
|
|
554
|
+
console.log(` Commits: ${details.github.stats.totalCommits}`);
|
|
555
|
+
console.log(` Open PRs: ${details.github.stats.openPRs}`);
|
|
556
|
+
console.log(` Contributors: ${details.github.stats.contributors}`);
|
|
557
|
+
if (details.github.stats.lastCommitMessage) {
|
|
558
|
+
console.log(` Latest: ${details.github.stats.lastCommitMessage}`);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
} else {
|
|
562
|
+
console.log(` ${utils.COLORS.dim}Not connected${utils.COLORS.reset}`);
|
|
563
|
+
}
|
|
564
|
+
console.log();
|
|
565
|
+
|
|
566
|
+
// Usage
|
|
567
|
+
if (details.usage) {
|
|
568
|
+
console.log(`${utils.COLORS.bold}Usage (${details.usage.currentPeriod})${utils.COLORS.reset}`);
|
|
569
|
+
console.log(` Context generations: ${details.usage.contextGenerations}`);
|
|
570
|
+
console.log(` Agent invocations: ${details.usage.agentInvocations}`);
|
|
571
|
+
console.log(` Skill searches: ${details.usage.skillSearches}`);
|
|
572
|
+
console.log(` MCP calls: ${details.usage.mcpCalls}`);
|
|
573
|
+
console.log();
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Commands
|
|
577
|
+
console.log(`${utils.COLORS.bold}Commands:${utils.COLORS.reset}`);
|
|
578
|
+
console.log(` bootspring switch "${name}" Switch to this project`);
|
|
579
|
+
console.log(' bootspring checkpoint View checkpoints');
|
|
580
|
+
console.log(' bootspring github status View GitHub status');
|
|
581
|
+
console.log();
|
|
582
|
+
|
|
583
|
+
// Emit telemetry
|
|
584
|
+
telemetry.emitEvent('manager:show', {
|
|
585
|
+
projectName: name,
|
|
586
|
+
health: details.health
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Export project report as markdown
|
|
592
|
+
*/
|
|
593
|
+
function handleExport(options: ExportOptions = {}): void {
|
|
594
|
+
const projects = getKnownProjects().map(getProjectDetails);
|
|
595
|
+
|
|
596
|
+
if (projects.length === 0) {
|
|
597
|
+
utils.print.warning('No projects to export.');
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const date = new Date().toISOString().split('T')[0] || '';
|
|
602
|
+
const lines: string[] = [];
|
|
603
|
+
|
|
604
|
+
lines.push('# Project Manager Report');
|
|
605
|
+
lines.push('');
|
|
606
|
+
lines.push(`Generated: ${date}`);
|
|
607
|
+
lines.push(`Projects: ${projects.length}`);
|
|
608
|
+
lines.push('');
|
|
609
|
+
lines.push('---');
|
|
610
|
+
lines.push('');
|
|
611
|
+
|
|
612
|
+
// Summary table
|
|
613
|
+
lines.push('## Summary');
|
|
614
|
+
lines.push('');
|
|
615
|
+
lines.push('| Project | Type | Health | Checkpoints | GitHub |');
|
|
616
|
+
lines.push('|---------|------|--------|-------------|--------|');
|
|
617
|
+
|
|
618
|
+
for (const project of projects) {
|
|
619
|
+
const name = project.name || path.basename(project.path || 'Unknown');
|
|
620
|
+
const type = project.projectType || 'development';
|
|
621
|
+
const health = `${project.healthGrade?.grade || '-'} (${project.health})`;
|
|
622
|
+
const checkpoints = `${project.checkpointProgress.completed}/${project.checkpointProgress.total}`;
|
|
623
|
+
const github = project.github?.connected ? `${project.github.owner}/${project.github.repo}` : '-';
|
|
624
|
+
|
|
625
|
+
lines.push(`| ${name} | ${type} | ${health} | ${checkpoints} | ${github} |`);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
lines.push('');
|
|
629
|
+
lines.push('---');
|
|
630
|
+
lines.push('');
|
|
631
|
+
|
|
632
|
+
// Detailed sections
|
|
633
|
+
lines.push('## Project Details');
|
|
634
|
+
lines.push('');
|
|
635
|
+
|
|
636
|
+
for (const project of projects) {
|
|
637
|
+
const name = project.name || path.basename(project.path || 'Unknown');
|
|
638
|
+
|
|
639
|
+
lines.push(`### ${name}`);
|
|
640
|
+
lines.push('');
|
|
641
|
+
lines.push(`- **Type:** ${project.projectType}`);
|
|
642
|
+
lines.push(`- **Health:** ${project.health}/100 (${project.healthGrade?.grade || '-'})`);
|
|
643
|
+
lines.push(`- **Checkpoints:** ${project.checkpointProgress.completed}/${project.checkpointProgress.total} (${project.checkpointProgress.percentage}%)`);
|
|
644
|
+
|
|
645
|
+
if (project.github?.connected) {
|
|
646
|
+
lines.push(`- **GitHub:** [${project.github.owner}/${project.github.repo}](${project.github.repositoryUrl})`);
|
|
647
|
+
if (project.github.stats?.lastCommitMessage) {
|
|
648
|
+
lines.push(`- **Latest Commit:** ${project.github.stats.lastCommitMessage}`);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if (project.lastActivity) {
|
|
653
|
+
lines.push(`- **Last Activity:** ${utils.formatRelativeTime(project.lastActivity)}`);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
lines.push('');
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Statistics
|
|
660
|
+
lines.push('## Statistics');
|
|
661
|
+
lines.push('');
|
|
662
|
+
|
|
663
|
+
const totalHealth = projects.reduce((sum, p) => sum + p.health, 0);
|
|
664
|
+
const avgHealth = projects.length > 0 ? Math.round(totalHealth / projects.length) : 0;
|
|
665
|
+
const connectedCount = projects.filter(p => p.github?.connected).length;
|
|
666
|
+
const byType: Record<string, number> = {};
|
|
667
|
+
for (const p of projects) {
|
|
668
|
+
const type = p.projectType || 'development';
|
|
669
|
+
byType[type] = (byType[type] || 0) + 1;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
lines.push(`- **Total Projects:** ${projects.length}`);
|
|
673
|
+
lines.push(`- **Average Health:** ${avgHealth}/100`);
|
|
674
|
+
lines.push(`- **GitHub Connected:** ${connectedCount}/${projects.length}`);
|
|
675
|
+
lines.push('');
|
|
676
|
+
lines.push('**By Type:**');
|
|
677
|
+
for (const [type, count] of Object.entries(byType)) {
|
|
678
|
+
lines.push(`- ${type}: ${count}`);
|
|
679
|
+
}
|
|
680
|
+
lines.push('');
|
|
681
|
+
|
|
682
|
+
lines.push('---');
|
|
683
|
+
lines.push('');
|
|
684
|
+
lines.push('*Generated by Bootspring Project Manager*');
|
|
685
|
+
|
|
686
|
+
const content = lines.join('\n');
|
|
687
|
+
|
|
688
|
+
// Output to file or console
|
|
689
|
+
if (options.output) {
|
|
690
|
+
try {
|
|
691
|
+
fs.writeFileSync(options.output, content, 'utf-8');
|
|
692
|
+
utils.print.success(`Report exported to: ${options.output}`);
|
|
693
|
+
} catch (error) {
|
|
694
|
+
utils.print.error(`Failed to write file: ${(error as Error).message}`);
|
|
695
|
+
}
|
|
696
|
+
} else {
|
|
697
|
+
const defaultPath = path.join(process.cwd(), `project-report-${date}.md`);
|
|
698
|
+
fs.writeFileSync(defaultPath, content, 'utf-8');
|
|
699
|
+
utils.print.success(`Report exported to: ${defaultPath}`);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// Emit telemetry
|
|
703
|
+
telemetry.emitEvent('manager:export', {
|
|
704
|
+
projectCount: projects.length
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Scan for local projects
|
|
710
|
+
*/
|
|
711
|
+
function handleScan(options: ScanOptions = {}): void {
|
|
712
|
+
const searchDir = options.dir || process.cwd();
|
|
713
|
+
|
|
714
|
+
const spinner = utils.createSpinner(`Scanning ${searchDir} for projects...`);
|
|
715
|
+
spinner.start();
|
|
716
|
+
|
|
717
|
+
const found: ScannedProject[] = [];
|
|
718
|
+
|
|
719
|
+
try {
|
|
720
|
+
// Look for directories with bootspring indicators
|
|
721
|
+
const entries = fs.readdirSync(searchDir, { withFileTypes: true });
|
|
722
|
+
|
|
723
|
+
for (const entry of entries) {
|
|
724
|
+
if (!entry.isDirectory()) continue;
|
|
725
|
+
if (entry.name.startsWith('.')) continue;
|
|
726
|
+
if (entry.name === 'node_modules') continue;
|
|
727
|
+
|
|
728
|
+
const dirPath = path.join(searchDir, entry.name);
|
|
729
|
+
|
|
730
|
+
// Check for bootspring indicators
|
|
731
|
+
const hasConfig = fs.existsSync(path.join(dirPath, 'bootspring.config.js'));
|
|
732
|
+
const hasPlanning = fs.existsSync(path.join(dirPath, 'planning'));
|
|
733
|
+
const hasBootstrapDir = fs.existsSync(path.join(dirPath, '.bootspring'));
|
|
734
|
+
const hasPackageJson = fs.existsSync(path.join(dirPath, 'package.json'));
|
|
735
|
+
|
|
736
|
+
if (hasConfig || hasPlanning || hasBootstrapDir) {
|
|
737
|
+
found.push({
|
|
738
|
+
name: entry.name,
|
|
739
|
+
path: dirPath,
|
|
740
|
+
indicators: {
|
|
741
|
+
config: hasConfig,
|
|
742
|
+
planning: hasPlanning,
|
|
743
|
+
bootspringDir: hasBootstrapDir,
|
|
744
|
+
packageJson: hasPackageJson
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
} catch (error) {
|
|
750
|
+
spinner.fail(`Scan failed: ${(error as Error).message}`);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
spinner.succeed(`Found ${found.length} project(s)`);
|
|
755
|
+
|
|
756
|
+
if (found.length === 0) {
|
|
757
|
+
console.log(`\n${utils.COLORS.dim}No Bootspring projects found in ${searchDir}${utils.COLORS.reset}`);
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
console.log();
|
|
762
|
+
|
|
763
|
+
for (const project of found) {
|
|
764
|
+
console.log(` ${utils.COLORS.cyan}●${utils.COLORS.reset} ${project.name}`);
|
|
765
|
+
console.log(` ${utils.COLORS.dim}${project.path}${utils.COLORS.reset}`);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
console.log(`
|
|
769
|
+
${utils.COLORS.bold}To add to workspace:${utils.COLORS.reset}
|
|
770
|
+
bootspring workspace add <path>
|
|
771
|
+
`);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* Show help
|
|
776
|
+
*/
|
|
777
|
+
function showHelp(): void {
|
|
778
|
+
console.log(`
|
|
779
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Manager${utils.COLORS.reset}
|
|
780
|
+
${utils.COLORS.dim}Overview of all projects${utils.COLORS.reset}
|
|
781
|
+
|
|
782
|
+
${utils.COLORS.bold}Usage:${utils.COLORS.reset}
|
|
783
|
+
bootspring manager [command] [options]
|
|
784
|
+
|
|
785
|
+
${utils.COLORS.bold}Commands:${utils.COLORS.reset}
|
|
786
|
+
list List all projects with health scores (default)
|
|
787
|
+
show <name> Detailed project view
|
|
788
|
+
export Export report as markdown
|
|
789
|
+
scan Scan for local projects
|
|
790
|
+
|
|
791
|
+
${utils.COLORS.bold}Options:${utils.COLORS.reset}
|
|
792
|
+
--sort=<field> Sort by: health, activity, name
|
|
793
|
+
--type=<type> Filter by type: dev, content, business
|
|
794
|
+
--json Output as JSON
|
|
795
|
+
--output=<file> Export output file path
|
|
796
|
+
--dir=<path> Directory to scan
|
|
797
|
+
|
|
798
|
+
${utils.COLORS.bold}Examples:${utils.COLORS.reset}
|
|
799
|
+
bootspring manager # List all projects
|
|
800
|
+
bootspring manager --sort=health # Sort by health score
|
|
801
|
+
bootspring manager show my-project # View project details
|
|
802
|
+
bootspring manager export # Generate markdown report
|
|
803
|
+
bootspring manager scan --dir=~/projects # Find projects
|
|
804
|
+
`);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// ============================================================================
|
|
808
|
+
// Main Runner
|
|
809
|
+
// ============================================================================
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Run manager command
|
|
813
|
+
*/
|
|
814
|
+
export async function run(args: string[]): Promise<void> {
|
|
815
|
+
// Parse arguments
|
|
816
|
+
const parsed = utils.parseArgs(args);
|
|
817
|
+
const subcommand = parsed._[0] || 'list';
|
|
818
|
+
const options = {
|
|
819
|
+
sort: parsed.sort || null,
|
|
820
|
+
type: parsed.type || null,
|
|
821
|
+
json: parsed.json || false,
|
|
822
|
+
output: parsed.output || null,
|
|
823
|
+
dir: parsed.dir || null
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
switch (subcommand) {
|
|
827
|
+
case 'list':
|
|
828
|
+
case 'ls':
|
|
829
|
+
case 'l':
|
|
830
|
+
handleList(options);
|
|
831
|
+
break;
|
|
832
|
+
|
|
833
|
+
case 'show':
|
|
834
|
+
case 'view':
|
|
835
|
+
case 's':
|
|
836
|
+
handleShow(parsed._[1], options);
|
|
837
|
+
break;
|
|
838
|
+
|
|
839
|
+
case 'export':
|
|
840
|
+
case 'report':
|
|
841
|
+
case 'e':
|
|
842
|
+
handleExport(options);
|
|
843
|
+
break;
|
|
844
|
+
|
|
845
|
+
case 'scan':
|
|
846
|
+
case 'find':
|
|
847
|
+
handleScan(options);
|
|
848
|
+
break;
|
|
849
|
+
|
|
850
|
+
case 'help':
|
|
851
|
+
case '-h':
|
|
852
|
+
case '--help':
|
|
853
|
+
showHelp();
|
|
854
|
+
break;
|
|
855
|
+
|
|
856
|
+
default:
|
|
857
|
+
// If it looks like a project name, treat as show
|
|
858
|
+
if (!subcommand.startsWith('-')) {
|
|
859
|
+
handleShow(subcommand, options);
|
|
860
|
+
} else {
|
|
861
|
+
utils.print.error(`Unknown subcommand: ${subcommand}`);
|
|
862
|
+
showHelp();
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// ============================================================================
|
|
868
|
+
// Exports
|
|
869
|
+
// ============================================================================
|
|
870
|
+
|
|
871
|
+
export {
|
|
872
|
+
getKnownProjects,
|
|
873
|
+
getProjectDetails,
|
|
874
|
+
handleList,
|
|
875
|
+
handleShow,
|
|
876
|
+
handleExport,
|
|
877
|
+
handleScan
|
|
878
|
+
};
|