@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,757 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring Checkpoint Command
|
|
3
|
+
* Manage project checkpoints and milestones
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* status Show all checkpoints with status
|
|
7
|
+
* complete <name> Mark a checkpoint as complete manually
|
|
8
|
+
* history View checkpoint completion timeline
|
|
9
|
+
* sync Re-scan files to detect completed items
|
|
10
|
+
* init Initialize checkpoint tracking
|
|
11
|
+
*
|
|
12
|
+
* @package bootspring
|
|
13
|
+
* @command checkpoint
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import * as path from 'path';
|
|
18
|
+
import { scanFilesForCheckpoints, pushCheckpointsToServer } from './checkpoint-utils';
|
|
19
|
+
|
|
20
|
+
// Type interfaces for JS modules
|
|
21
|
+
interface Config {
|
|
22
|
+
_projectRoot: string;
|
|
23
|
+
apiBase?: string | undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ConfigModule {
|
|
27
|
+
load(): Config;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface UtilsModule {
|
|
31
|
+
COLORS: Colors;
|
|
32
|
+
print: PrintModule;
|
|
33
|
+
createSpinner(text: string): Spinner;
|
|
34
|
+
parseArgs(args: string[]): ParsedArgs;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface Colors {
|
|
38
|
+
reset: string;
|
|
39
|
+
bold: string;
|
|
40
|
+
dim: string;
|
|
41
|
+
cyan: string;
|
|
42
|
+
green: string;
|
|
43
|
+
yellow: string;
|
|
44
|
+
red: string;
|
|
45
|
+
magenta: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface PrintModule {
|
|
49
|
+
success(msg: string): void;
|
|
50
|
+
error(msg: string): void;
|
|
51
|
+
warning(msg: string): void;
|
|
52
|
+
info(msg: string): void;
|
|
53
|
+
dim(msg: string): void;
|
|
54
|
+
debug(msg: string): void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface ParsedArgs {
|
|
58
|
+
_: string[];
|
|
59
|
+
json?: boolean | undefined;
|
|
60
|
+
force?: boolean | undefined;
|
|
61
|
+
'dry-run'?: boolean | undefined;
|
|
62
|
+
dryRun?: boolean | undefined;
|
|
63
|
+
type?: string | undefined;
|
|
64
|
+
notes?: string | undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface Spinner {
|
|
68
|
+
start(): Spinner;
|
|
69
|
+
stop(): void;
|
|
70
|
+
succeed(text: string): void;
|
|
71
|
+
fail(text: string): void;
|
|
72
|
+
info(text: string): void;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface CheckpointDef {
|
|
76
|
+
id: string;
|
|
77
|
+
label: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface CheckpointData {
|
|
81
|
+
completed?: boolean | undefined;
|
|
82
|
+
reason?: string | undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface ProjectState {
|
|
86
|
+
projectType: string;
|
|
87
|
+
checkpoints: Record<string, CheckpointData>;
|
|
88
|
+
autoTagged?: boolean | undefined;
|
|
89
|
+
taggedBy?: string | undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface CheckpointStatus {
|
|
93
|
+
exists: boolean;
|
|
94
|
+
projectType: string;
|
|
95
|
+
completed: number;
|
|
96
|
+
total: number;
|
|
97
|
+
percentage: number;
|
|
98
|
+
checkpoints: Array<{
|
|
99
|
+
id: string;
|
|
100
|
+
label: string;
|
|
101
|
+
completed: boolean;
|
|
102
|
+
reason?: string | undefined;
|
|
103
|
+
}>;
|
|
104
|
+
autoTagged?: boolean | undefined;
|
|
105
|
+
taggedBy?: string | undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
interface SyncResult {
|
|
109
|
+
changes: Array<{
|
|
110
|
+
type: string;
|
|
111
|
+
label: string;
|
|
112
|
+
reason: string;
|
|
113
|
+
}>;
|
|
114
|
+
completed: number;
|
|
115
|
+
total: number;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface HistoryRecord {
|
|
119
|
+
checkpointId: string;
|
|
120
|
+
timestamp: string;
|
|
121
|
+
notes?: string | undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
interface NextStep {
|
|
125
|
+
action: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
interface CheckpointProgress {
|
|
129
|
+
completed: number;
|
|
130
|
+
total: number;
|
|
131
|
+
percentage: number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
interface ProjectStateModule {
|
|
135
|
+
PROJECT_TYPES: Record<string, string>;
|
|
136
|
+
loadState(projectRoot: string): ProjectState | null;
|
|
137
|
+
initState(projectRoot: string, options: { projectType: string; autoTagged: boolean }): ProjectState;
|
|
138
|
+
getCheckpointDefinitions(projectType: string): CheckpointDef[];
|
|
139
|
+
completeCheckpoint(projectRoot: string, checkpointId: string, options: { completedBy: string; notes: string }): ProjectState | null;
|
|
140
|
+
getCheckpointProgress(projectRoot: string): CheckpointProgress;
|
|
141
|
+
getCheckpointHistory(projectRoot: string): HistoryRecord[];
|
|
142
|
+
setProjectType(projectRoot: string, type: string): ProjectState | null;
|
|
143
|
+
detectProjectType(projectRoot: string): string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface CheckpointEngineModule {
|
|
147
|
+
syncCheckpoints(projectRoot: string, options?: { verbose?: boolean | undefined; dryRun?: boolean | undefined }): SyncResult;
|
|
148
|
+
getCheckpointStatus(projectRoot: string): CheckpointStatus;
|
|
149
|
+
getColoredProgressBar(percentage: number, width: number): string;
|
|
150
|
+
getNextSteps(projectRoot: string): NextStep[];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
interface TelemetryModule {
|
|
154
|
+
emitEvent(event: string, data: Record<string, unknown>, options?: { projectRoot?: string }): void;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const config = require('../core/config') as ConfigModule;
|
|
158
|
+
const utils = require('../core/utils') as UtilsModule;
|
|
159
|
+
const projectState = require('../core/project-state') as ProjectStateModule;
|
|
160
|
+
const checkpointEngine = require('../core/checkpoint-engine') as CheckpointEngineModule;
|
|
161
|
+
const telemetry = require('../core/telemetry') as TelemetryModule;
|
|
162
|
+
|
|
163
|
+
// ============================================================================
|
|
164
|
+
// Command Handlers
|
|
165
|
+
// ============================================================================
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Show checkpoint status
|
|
169
|
+
*/
|
|
170
|
+
async function handleStatus(projectRoot: string, options: { json?: boolean } = {}): Promise<void> {
|
|
171
|
+
// Auto-sync checkpoints before showing status
|
|
172
|
+
checkpointEngine.syncCheckpoints(projectRoot);
|
|
173
|
+
|
|
174
|
+
// Auto-push to server if authenticated (non-blocking)
|
|
175
|
+
pushCheckpointsToServer(projectRoot, { quiet: true, autoSync: true, skipMetrics: true }).catch(() => {});
|
|
176
|
+
|
|
177
|
+
const status = checkpointEngine.getCheckpointStatus(projectRoot);
|
|
178
|
+
|
|
179
|
+
if (!status.exists) {
|
|
180
|
+
console.log(`
|
|
181
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Project Checkpoints${utils.COLORS.reset}
|
|
182
|
+
|
|
183
|
+
${utils.COLORS.yellow}No checkpoint tracking found.${utils.COLORS.reset}
|
|
184
|
+
|
|
185
|
+
${utils.COLORS.bold}Get started:${utils.COLORS.reset}
|
|
186
|
+
bootspring checkpoint init Initialize checkpoint tracking
|
|
187
|
+
bootspring preseed init Start with document generation
|
|
188
|
+
`);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Output as JSON if requested
|
|
193
|
+
if (options.json) {
|
|
194
|
+
console.log(JSON.stringify(status, null, 2));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Type badge
|
|
199
|
+
const typeBadge = getTypeBadge(status.projectType);
|
|
200
|
+
const autoTagNote = status.autoTagged ? ` ${utils.COLORS.dim}(auto-tagged by ${status.taggedBy})${utils.COLORS.reset}` : '';
|
|
201
|
+
|
|
202
|
+
console.log(`
|
|
203
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Project Checkpoints${utils.COLORS.reset}
|
|
204
|
+
|
|
205
|
+
${utils.COLORS.bold}Project Type:${utils.COLORS.reset} ${typeBadge}${autoTagNote}
|
|
206
|
+
|
|
207
|
+
${utils.COLORS.bold}Progress:${utils.COLORS.reset} ${status.completed}/${status.total} (${status.percentage}%)
|
|
208
|
+
${checkpointEngine.getColoredProgressBar(status.percentage, 30)}
|
|
209
|
+
`);
|
|
210
|
+
|
|
211
|
+
// Show all checkpoints
|
|
212
|
+
console.log(`${utils.COLORS.bold}Checkpoints:${utils.COLORS.reset}`);
|
|
213
|
+
|
|
214
|
+
for (const checkpoint of status.checkpoints) {
|
|
215
|
+
const icon = checkpoint.completed
|
|
216
|
+
? `${utils.COLORS.green}✓${utils.COLORS.reset}`
|
|
217
|
+
: `${utils.COLORS.dim}○${utils.COLORS.reset}`;
|
|
218
|
+
|
|
219
|
+
const label = checkpoint.completed
|
|
220
|
+
? checkpoint.label
|
|
221
|
+
: `${utils.COLORS.dim}${checkpoint.label}${utils.COLORS.reset}`;
|
|
222
|
+
|
|
223
|
+
const completedInfo = checkpoint.completed && checkpoint.reason
|
|
224
|
+
? ` ${utils.COLORS.dim}(${checkpoint.reason})${utils.COLORS.reset}`
|
|
225
|
+
: '';
|
|
226
|
+
|
|
227
|
+
console.log(` ${icon} ${label}${completedInfo}`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Show next steps
|
|
231
|
+
const nextSteps = checkpointEngine.getNextSteps(projectRoot);
|
|
232
|
+
if (nextSteps.length > 0) {
|
|
233
|
+
console.log(`\n${utils.COLORS.bold}Next Steps:${utils.COLORS.reset}`);
|
|
234
|
+
for (const step of nextSteps) {
|
|
235
|
+
console.log(` ${utils.COLORS.cyan}→${utils.COLORS.reset} ${step.action}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
console.log();
|
|
240
|
+
|
|
241
|
+
// Emit telemetry
|
|
242
|
+
telemetry.emitEvent('checkpoint:status', {
|
|
243
|
+
projectType: status.projectType,
|
|
244
|
+
completed: status.completed,
|
|
245
|
+
total: status.total,
|
|
246
|
+
percentage: status.percentage
|
|
247
|
+
}, { projectRoot });
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get type badge
|
|
252
|
+
*/
|
|
253
|
+
function getTypeBadge(projectType: string): string {
|
|
254
|
+
const badges: Record<string, string> = {
|
|
255
|
+
development: `${utils.COLORS.cyan}[DEV]${utils.COLORS.reset}`,
|
|
256
|
+
content: `${utils.COLORS.magenta}[CONTENT]${utils.COLORS.reset}`,
|
|
257
|
+
business: `${utils.COLORS.yellow}[BUSINESS]${utils.COLORS.reset}`
|
|
258
|
+
};
|
|
259
|
+
const devBadge = badges.development as string;
|
|
260
|
+
return badges[projectType] || devBadge;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Initialize checkpoint tracking
|
|
265
|
+
*/
|
|
266
|
+
function handleInit(projectRoot: string, options: { force?: boolean; type?: string | null } = {}): void {
|
|
267
|
+
const existingState = projectState.loadState(projectRoot);
|
|
268
|
+
|
|
269
|
+
if (existingState && !options.force) {
|
|
270
|
+
utils.print.warning('Checkpoint tracking already initialized.');
|
|
271
|
+
utils.print.dim('Use --force to reinitialize.');
|
|
272
|
+
console.log();
|
|
273
|
+
handleStatus(projectRoot);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Determine project type
|
|
278
|
+
let projectType = options.type;
|
|
279
|
+
if (!projectType) {
|
|
280
|
+
projectType = projectState.detectProjectType(projectRoot);
|
|
281
|
+
utils.print.info(`Auto-detected project type: ${projectType}`);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Initialize state
|
|
285
|
+
projectState.initState(projectRoot, {
|
|
286
|
+
projectType,
|
|
287
|
+
autoTagged: false
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Run initial sync
|
|
291
|
+
const syncResult = checkpointEngine.syncCheckpoints(projectRoot, { verbose: true });
|
|
292
|
+
|
|
293
|
+
utils.print.success('Checkpoint tracking initialized!');
|
|
294
|
+
console.log();
|
|
295
|
+
|
|
296
|
+
// Show status
|
|
297
|
+
handleStatus(projectRoot);
|
|
298
|
+
|
|
299
|
+
// Emit telemetry
|
|
300
|
+
telemetry.emitEvent('checkpoint:init', {
|
|
301
|
+
projectType,
|
|
302
|
+
initialCompleted: syncResult.completed
|
|
303
|
+
}, { projectRoot });
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Complete a checkpoint manually
|
|
308
|
+
*/
|
|
309
|
+
function handleComplete(projectRoot: string, checkpointId: string | undefined, options: { notes?: string | null } = {}): void {
|
|
310
|
+
if (!checkpointId) {
|
|
311
|
+
utils.print.error('Please specify a checkpoint to complete.');
|
|
312
|
+
console.log(`\n${utils.COLORS.bold}Usage:${utils.COLORS.reset} bootspring checkpoint complete <checkpoint-id>`);
|
|
313
|
+
console.log(`\n${utils.COLORS.bold}Available checkpoints:${utils.COLORS.reset}`);
|
|
314
|
+
|
|
315
|
+
const state = projectState.loadState(projectRoot);
|
|
316
|
+
if (state) {
|
|
317
|
+
const defs = projectState.getCheckpointDefinitions(state.projectType);
|
|
318
|
+
for (const def of defs) {
|
|
319
|
+
const completed = state.checkpoints[def.id]?.completed;
|
|
320
|
+
const icon = completed ? `${utils.COLORS.green}✓${utils.COLORS.reset}` : `${utils.COLORS.dim}○${utils.COLORS.reset}`;
|
|
321
|
+
console.log(` ${icon} ${def.id} - ${def.label}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
console.log();
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const state = projectState.loadState(projectRoot);
|
|
329
|
+
if (!state) {
|
|
330
|
+
utils.print.error('Checkpoint tracking not initialized.');
|
|
331
|
+
utils.print.dim('Run: bootspring checkpoint init');
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Check if checkpoint exists
|
|
336
|
+
const defs = projectState.getCheckpointDefinitions(state.projectType);
|
|
337
|
+
const checkpoint = defs.find(c => c.id === checkpointId);
|
|
338
|
+
|
|
339
|
+
if (!checkpoint) {
|
|
340
|
+
utils.print.error(`Unknown checkpoint: ${checkpointId}`);
|
|
341
|
+
console.log(`\n${utils.COLORS.bold}Available checkpoints:${utils.COLORS.reset}`);
|
|
342
|
+
for (const def of defs) {
|
|
343
|
+
console.log(` - ${def.id}`);
|
|
344
|
+
}
|
|
345
|
+
console.log();
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Check if already completed
|
|
350
|
+
if (state.checkpoints[checkpointId]?.completed) {
|
|
351
|
+
utils.print.warning(`Checkpoint "${checkpoint.label}" is already completed.`);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Complete the checkpoint
|
|
356
|
+
const updatedState = projectState.completeCheckpoint(projectRoot, checkpointId, {
|
|
357
|
+
completedBy: 'manual',
|
|
358
|
+
notes: options.notes || 'Manually marked as complete'
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
if (updatedState) {
|
|
362
|
+
utils.print.success(`Completed: ${checkpoint.label}`);
|
|
363
|
+
|
|
364
|
+
// Show progress
|
|
365
|
+
const progress = projectState.getCheckpointProgress(projectRoot);
|
|
366
|
+
console.log(`\nProgress: ${progress.completed}/${progress.total} (${progress.percentage}%)`);
|
|
367
|
+
console.log(checkpointEngine.getColoredProgressBar(progress.percentage, 30));
|
|
368
|
+
console.log();
|
|
369
|
+
|
|
370
|
+
// Emit telemetry
|
|
371
|
+
telemetry.emitEvent('checkpoint:complete', {
|
|
372
|
+
checkpointId,
|
|
373
|
+
label: checkpoint.label,
|
|
374
|
+
method: 'manual'
|
|
375
|
+
}, { projectRoot });
|
|
376
|
+
} else {
|
|
377
|
+
utils.print.error('Failed to complete checkpoint.');
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Show checkpoint history
|
|
383
|
+
*/
|
|
384
|
+
function handleHistory(projectRoot: string, options: { json?: boolean } = {}): void {
|
|
385
|
+
const history = projectState.getCheckpointHistory(projectRoot);
|
|
386
|
+
|
|
387
|
+
if (history.length === 0) {
|
|
388
|
+
console.log(`
|
|
389
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Checkpoint History${utils.COLORS.reset}
|
|
390
|
+
|
|
391
|
+
${utils.COLORS.dim}No checkpoint completions recorded yet.${utils.COLORS.reset}
|
|
392
|
+
`);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Output as JSON if requested
|
|
397
|
+
if (options.json) {
|
|
398
|
+
console.log(JSON.stringify(history, null, 2));
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const state = projectState.loadState(projectRoot);
|
|
403
|
+
const defs = state ? projectState.getCheckpointDefinitions(state.projectType) : [];
|
|
404
|
+
|
|
405
|
+
console.log(`
|
|
406
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Checkpoint History${utils.COLORS.reset}
|
|
407
|
+
`);
|
|
408
|
+
|
|
409
|
+
// Group by date
|
|
410
|
+
const byDate: Record<string, HistoryRecord[]> = {};
|
|
411
|
+
for (const record of history) {
|
|
412
|
+
const date = new Date(record.timestamp).toLocaleDateString();
|
|
413
|
+
if (!byDate[date]) {
|
|
414
|
+
byDate[date] = [];
|
|
415
|
+
}
|
|
416
|
+
byDate[date].push(record);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Display grouped
|
|
420
|
+
for (const [date, records] of Object.entries(byDate)) {
|
|
421
|
+
console.log(`${utils.COLORS.bold}${date}${utils.COLORS.reset}`);
|
|
422
|
+
|
|
423
|
+
for (const record of records) {
|
|
424
|
+
const checkpoint = defs.find(d => d.id === record.checkpointId);
|
|
425
|
+
const label = checkpoint?.label || record.checkpointId;
|
|
426
|
+
const time = new Date(record.timestamp).toLocaleTimeString();
|
|
427
|
+
|
|
428
|
+
console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} ${time} - ${label}`);
|
|
429
|
+
if (record.notes) {
|
|
430
|
+
console.log(` ${utils.COLORS.dim}${record.notes}${utils.COLORS.reset}`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
console.log();
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Sync checkpoints from files
|
|
439
|
+
*/
|
|
440
|
+
async function handleSync(projectRoot: string, options: { dryRun?: boolean } = {}): Promise<void> {
|
|
441
|
+
const spinner = utils.createSpinner('Scanning for completed checkpoints...');
|
|
442
|
+
spinner.start();
|
|
443
|
+
|
|
444
|
+
const result = checkpointEngine.syncCheckpoints(projectRoot, {
|
|
445
|
+
dryRun: options.dryRun,
|
|
446
|
+
verbose: false
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
if (result.changes.length === 0) {
|
|
450
|
+
spinner.info('No new checkpoints detected.');
|
|
451
|
+
} else {
|
|
452
|
+
spinner.succeed(`Found ${result.changes.length} new checkpoint(s)!`);
|
|
453
|
+
console.log();
|
|
454
|
+
|
|
455
|
+
for (const change of result.changes) {
|
|
456
|
+
if (change.type === 'completed') {
|
|
457
|
+
console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} ${change.label}`);
|
|
458
|
+
console.log(` ${utils.COLORS.dim}${change.reason}${utils.COLORS.reset}`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
console.log();
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Show progress
|
|
465
|
+
console.log(`${utils.COLORS.bold}Progress:${utils.COLORS.reset} ${result.completed}/${result.total} (${Math.round((result.completed / result.total) * 100)}%)`);
|
|
466
|
+
console.log(checkpointEngine.getColoredProgressBar(Math.round((result.completed / result.total) * 100), 30));
|
|
467
|
+
console.log();
|
|
468
|
+
|
|
469
|
+
if (options.dryRun) {
|
|
470
|
+
utils.print.dim('Dry run - no changes were saved.');
|
|
471
|
+
} else {
|
|
472
|
+
// Auto-push to server if authenticated
|
|
473
|
+
try {
|
|
474
|
+
const pushResult = await pushCheckpointsToServer(projectRoot, { quiet: true, autoSync: true });
|
|
475
|
+
if (pushResult.success) {
|
|
476
|
+
utils.print.success('Synced to dashboard automatically');
|
|
477
|
+
}
|
|
478
|
+
} catch {
|
|
479
|
+
// Silent fail - push is best-effort
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Emit telemetry
|
|
484
|
+
telemetry.emitEvent('checkpoint:sync', {
|
|
485
|
+
changes: result.changes.length,
|
|
486
|
+
completed: result.completed,
|
|
487
|
+
total: result.total
|
|
488
|
+
}, { projectRoot });
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Set project type
|
|
493
|
+
*/
|
|
494
|
+
function handleType(projectRoot: string, type: string | undefined): void {
|
|
495
|
+
const validTypes = Object.values(projectState.PROJECT_TYPES);
|
|
496
|
+
|
|
497
|
+
if (!type) {
|
|
498
|
+
// Show current type
|
|
499
|
+
const state = projectState.loadState(projectRoot);
|
|
500
|
+
if (!state) {
|
|
501
|
+
utils.print.error('Checkpoint tracking not initialized.');
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
console.log(`
|
|
506
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Project Type${utils.COLORS.reset}
|
|
507
|
+
|
|
508
|
+
Current type: ${getTypeBadge(state.projectType)}
|
|
509
|
+
`);
|
|
510
|
+
|
|
511
|
+
console.log(`${utils.COLORS.bold}Available types:${utils.COLORS.reset}`);
|
|
512
|
+
for (const t of validTypes) {
|
|
513
|
+
const badge = getTypeBadge(t);
|
|
514
|
+
const current = t === state.projectType ? ' (current)' : '';
|
|
515
|
+
console.log(` ${badge} ${t}${current}`);
|
|
516
|
+
}
|
|
517
|
+
console.log();
|
|
518
|
+
|
|
519
|
+
console.log(`${utils.COLORS.bold}Change type:${utils.COLORS.reset}`);
|
|
520
|
+
console.log(' bootspring checkpoint type <type>');
|
|
521
|
+
console.log();
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (!validTypes.includes(type)) {
|
|
526
|
+
utils.print.error(`Invalid project type: ${type}`);
|
|
527
|
+
console.log(`\n${utils.COLORS.bold}Valid types:${utils.COLORS.reset} ${validTypes.join(', ')}`);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const updatedState = projectState.setProjectType(projectRoot, type);
|
|
532
|
+
|
|
533
|
+
if (updatedState) {
|
|
534
|
+
utils.print.success(`Project type changed to: ${type}`);
|
|
535
|
+
|
|
536
|
+
// Sync checkpoints for new type
|
|
537
|
+
const syncResult = checkpointEngine.syncCheckpoints(projectRoot, { verbose: true });
|
|
538
|
+
console.log(`\nCheckpoints: ${syncResult.completed}/${syncResult.total} complete`);
|
|
539
|
+
|
|
540
|
+
telemetry.emitEvent('checkpoint:type-change', {
|
|
541
|
+
newType: type
|
|
542
|
+
}, { projectRoot });
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Push checkpoints to dashboard
|
|
548
|
+
* Sends raw file scan data - server evaluates checkpoints and calculates scores
|
|
549
|
+
*/
|
|
550
|
+
async function handlePush(projectRoot: string, _options: Record<string, unknown> = {}): Promise<void> {
|
|
551
|
+
const spinner = utils.createSpinner('Scanning and syncing to dashboard...');
|
|
552
|
+
spinner.start();
|
|
553
|
+
|
|
554
|
+
try {
|
|
555
|
+
const result = await pushCheckpointsToServer(projectRoot, { quiet: false });
|
|
556
|
+
|
|
557
|
+
if (!result.success) {
|
|
558
|
+
if (result.reason === 'not_authenticated') {
|
|
559
|
+
spinner.fail('Not authenticated. Run: bootspring auth login');
|
|
560
|
+
} else if (result.reason === 'no_project') {
|
|
561
|
+
spinner.fail('No project selected. Run: bootspring switch <project>');
|
|
562
|
+
} else {
|
|
563
|
+
spinner.fail('Failed to sync checkpoints');
|
|
564
|
+
}
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
spinner.succeed('Checkpoints synced to dashboard!');
|
|
569
|
+
|
|
570
|
+
// Display server-evaluated results
|
|
571
|
+
console.log(`
|
|
572
|
+
${utils.COLORS.bold}Sync Summary:${utils.COLORS.reset}
|
|
573
|
+
Project Type: ${result.projectType}
|
|
574
|
+
Checkpoints: ${result.progress?.completed}/${result.progress?.total} (${result.progress?.percentage}%)
|
|
575
|
+
Health Score: ${result.healthScore}%
|
|
576
|
+
Last Sync: ${result.lastSyncAt ? new Date(result.lastSyncAt).toLocaleString() : 'Now'}
|
|
577
|
+
`);
|
|
578
|
+
|
|
579
|
+
// Show health breakdown
|
|
580
|
+
if (result.healthBreakdown) {
|
|
581
|
+
console.log(`${utils.COLORS.bold}Health Breakdown:${utils.COLORS.reset}`);
|
|
582
|
+
for (const [key, value] of Object.entries(result.healthBreakdown)) {
|
|
583
|
+
const label = key.charAt(0).toUpperCase() + key.slice(1).replace(/([A-Z])/g, ' $1');
|
|
584
|
+
console.log(` ${label}: ${value}%`);
|
|
585
|
+
}
|
|
586
|
+
console.log();
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Emit telemetry
|
|
590
|
+
telemetry.emitEvent('checkpoint:push', {
|
|
591
|
+
healthScore: result.healthScore,
|
|
592
|
+
checkpointsCompleted: result.progress?.completed
|
|
593
|
+
}, { projectRoot });
|
|
594
|
+
|
|
595
|
+
} catch (error) {
|
|
596
|
+
spinner.fail(`Failed to sync: ${(error as Error).message}`);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Detect project type automatically
|
|
602
|
+
*/
|
|
603
|
+
function handleDetect(projectRoot: string): void {
|
|
604
|
+
const detectedType = projectState.detectProjectType(projectRoot);
|
|
605
|
+
const state = projectState.loadState(projectRoot);
|
|
606
|
+
const currentType = state?.projectType;
|
|
607
|
+
|
|
608
|
+
console.log(`
|
|
609
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Project Type Detection${utils.COLORS.reset}
|
|
610
|
+
|
|
611
|
+
Detected type: ${getTypeBadge(detectedType)}
|
|
612
|
+
`);
|
|
613
|
+
|
|
614
|
+
if (currentType && currentType !== detectedType) {
|
|
615
|
+
console.log(`Current type: ${getTypeBadge(currentType)}`);
|
|
616
|
+
console.log(`\n${utils.COLORS.bold}To change:${utils.COLORS.reset} bootspring checkpoint type ${detectedType}`);
|
|
617
|
+
} else if (!currentType) {
|
|
618
|
+
console.log(`${utils.COLORS.bold}To initialize:${utils.COLORS.reset} bootspring checkpoint init --type=${detectedType}`);
|
|
619
|
+
} else {
|
|
620
|
+
console.log(`${utils.COLORS.dim}Current type matches detected type.${utils.COLORS.reset}`);
|
|
621
|
+
}
|
|
622
|
+
console.log();
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Show help
|
|
627
|
+
*/
|
|
628
|
+
function showHelp(): void {
|
|
629
|
+
console.log(`
|
|
630
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Checkpoints${utils.COLORS.reset}
|
|
631
|
+
${utils.COLORS.dim}Track project milestones and progress${utils.COLORS.reset}
|
|
632
|
+
|
|
633
|
+
${utils.COLORS.bold}Usage:${utils.COLORS.reset}
|
|
634
|
+
bootspring checkpoint [command] [options]
|
|
635
|
+
|
|
636
|
+
${utils.COLORS.bold}Commands:${utils.COLORS.reset}
|
|
637
|
+
status Show all checkpoints with status (default)
|
|
638
|
+
init Initialize checkpoint tracking
|
|
639
|
+
complete <id> Mark a checkpoint as complete
|
|
640
|
+
history View checkpoint completion timeline
|
|
641
|
+
sync Re-scan files to detect completed items
|
|
642
|
+
type [type] Show or set project type
|
|
643
|
+
detect Auto-detect project type from files
|
|
644
|
+
push Sync checkpoints to dashboard
|
|
645
|
+
|
|
646
|
+
${utils.COLORS.bold}Options:${utils.COLORS.reset}
|
|
647
|
+
--json Output as JSON
|
|
648
|
+
--force Force reinitialize
|
|
649
|
+
--dry-run Don't save changes (for sync)
|
|
650
|
+
--type=<type> Set project type (dev, content, business)
|
|
651
|
+
--notes="..." Add notes when completing
|
|
652
|
+
|
|
653
|
+
${utils.COLORS.bold}Project Types:${utils.COLORS.reset}
|
|
654
|
+
development Software development projects
|
|
655
|
+
content Content/editorial projects
|
|
656
|
+
business Business planning projects
|
|
657
|
+
|
|
658
|
+
${utils.COLORS.bold}Examples:${utils.COLORS.reset}
|
|
659
|
+
bootspring checkpoint # Show status
|
|
660
|
+
bootspring checkpoint init # Initialize tracking
|
|
661
|
+
bootspring checkpoint complete prd # Complete the PRD checkpoint
|
|
662
|
+
bootspring checkpoint sync # Detect completed items
|
|
663
|
+
bootspring checkpoint type business # Change to business type
|
|
664
|
+
`);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// ============================================================================
|
|
668
|
+
// Main Runner
|
|
669
|
+
// ============================================================================
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Run checkpoint command
|
|
673
|
+
*/
|
|
674
|
+
export async function run(args: string[]): Promise<void> {
|
|
675
|
+
const cfg = config.load();
|
|
676
|
+
const projectRoot = cfg._projectRoot;
|
|
677
|
+
|
|
678
|
+
// Parse arguments
|
|
679
|
+
const parsed = utils.parseArgs(args);
|
|
680
|
+
const subcommand = parsed._[0] || 'status';
|
|
681
|
+
const options = {
|
|
682
|
+
json: parsed.json || false,
|
|
683
|
+
force: parsed.force || false,
|
|
684
|
+
dryRun: parsed['dry-run'] || parsed.dryRun || false,
|
|
685
|
+
type: parsed.type || null,
|
|
686
|
+
notes: parsed.notes || null
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
switch (subcommand) {
|
|
690
|
+
case 'status':
|
|
691
|
+
case 'show':
|
|
692
|
+
case 's':
|
|
693
|
+
await handleStatus(projectRoot, options);
|
|
694
|
+
break;
|
|
695
|
+
|
|
696
|
+
case 'init':
|
|
697
|
+
case 'initialize':
|
|
698
|
+
handleInit(projectRoot, options);
|
|
699
|
+
break;
|
|
700
|
+
|
|
701
|
+
case 'complete':
|
|
702
|
+
case 'done':
|
|
703
|
+
case 'c':
|
|
704
|
+
handleComplete(projectRoot, parsed._[1], options);
|
|
705
|
+
break;
|
|
706
|
+
|
|
707
|
+
case 'history':
|
|
708
|
+
case 'log':
|
|
709
|
+
case 'h':
|
|
710
|
+
handleHistory(projectRoot, options);
|
|
711
|
+
break;
|
|
712
|
+
|
|
713
|
+
case 'sync':
|
|
714
|
+
case 'scan':
|
|
715
|
+
await handleSync(projectRoot, options);
|
|
716
|
+
break;
|
|
717
|
+
|
|
718
|
+
case 'type':
|
|
719
|
+
case 't':
|
|
720
|
+
handleType(projectRoot, parsed._[1] || options.type || undefined);
|
|
721
|
+
break;
|
|
722
|
+
|
|
723
|
+
case 'detect':
|
|
724
|
+
case 'd':
|
|
725
|
+
handleDetect(projectRoot);
|
|
726
|
+
break;
|
|
727
|
+
|
|
728
|
+
case 'push':
|
|
729
|
+
case 'upload':
|
|
730
|
+
await handlePush(projectRoot, options);
|
|
731
|
+
break;
|
|
732
|
+
|
|
733
|
+
case 'help':
|
|
734
|
+
case '-h':
|
|
735
|
+
case '--help':
|
|
736
|
+
showHelp();
|
|
737
|
+
break;
|
|
738
|
+
|
|
739
|
+
default:
|
|
740
|
+
utils.print.error(`Unknown subcommand: ${subcommand}`);
|
|
741
|
+
showHelp();
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// ============================================================================
|
|
746
|
+
// Exports
|
|
747
|
+
// ============================================================================
|
|
748
|
+
|
|
749
|
+
export {
|
|
750
|
+
handleStatus,
|
|
751
|
+
handleInit,
|
|
752
|
+
handleComplete,
|
|
753
|
+
handleHistory,
|
|
754
|
+
handleSync,
|
|
755
|
+
handleType,
|
|
756
|
+
handleDetect
|
|
757
|
+
};
|