@girardmedia/bootspring 1.2.0 → 2.0.3
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/README.md +107 -14
- package/bin/bootspring.js +166 -27
- package/cli/agent.js +189 -17
- package/cli/analyze.js +499 -0
- package/cli/audit.js +557 -0
- package/cli/auth.js +495 -38
- package/cli/billing.js +302 -0
- package/cli/build.js +695 -0
- package/cli/business.js +109 -26
- package/cli/checkpoint-utils.js +168 -0
- package/cli/checkpoint.js +639 -0
- package/cli/cloud-sync.js +447 -0
- package/cli/content.js +198 -0
- package/cli/context.js +1 -1
- package/cli/deploy.js +543 -0
- package/cli/fundraise.js +112 -50
- package/cli/github-cmd.js +435 -0
- package/cli/health.js +477 -0
- package/cli/init.js +84 -13
- package/cli/legal.js +107 -95
- package/cli/log.js +2 -2
- package/cli/loop.js +976 -73
- package/cli/manager.js +711 -0
- package/cli/metrics.js +480 -0
- package/cli/monitor.js +812 -0
- package/cli/onboard.js +521 -0
- package/cli/orchestrator.js +12 -24
- package/cli/prd.js +594 -0
- package/cli/preseed-start.js +1483 -0
- package/cli/preseed.js +2302 -0
- package/cli/project.js +436 -0
- package/cli/quality.js +233 -0
- package/cli/security.js +913 -0
- package/cli/seed.js +1441 -5
- package/cli/skill.js +273 -211
- package/cli/suggest.js +989 -0
- package/cli/switch.js +453 -0
- package/cli/visualize.js +527 -0
- package/cli/watch.js +769 -0
- package/cli/workspace.js +607 -0
- package/core/analyze-workflow.js +1134 -0
- package/core/api-client.js +535 -22
- package/core/audit-workflow.js +1350 -0
- package/core/build-orchestrator.js +480 -0
- package/core/build-state.js +577 -0
- package/core/checkpoint-engine.js +408 -0
- package/core/config.js +1109 -26
- package/core/context-loader.js +21 -1
- package/core/deploy-workflow.js +836 -0
- package/core/entitlements.js +93 -22
- package/core/github-sync.js +610 -0
- package/core/index.js +8 -1
- package/core/ingest.js +1111 -0
- package/core/metrics-engine.js +768 -0
- package/core/onboard-workflow.js +1007 -0
- package/core/preseed-workflow.js +934 -0
- package/core/preseed.js +1617 -0
- package/core/project-context.js +325 -0
- package/core/project-state.js +694 -0
- package/core/r2-sync.js +583 -0
- package/core/scaffold.js +525 -7
- package/core/session.js +258 -0
- package/core/task-extractor.js +758 -0
- package/core/telemetry.js +28 -6
- package/core/tier-enforcement.js +737 -0
- package/core/utils.js +38 -14
- package/generators/questionnaire.js +15 -12
- package/generators/sections/ai.js +7 -7
- package/generators/sections/content.js +300 -0
- package/generators/sections/index.js +3 -0
- package/generators/sections/plugins.js +7 -6
- package/generators/templates/build-planning.template.js +596 -0
- package/generators/templates/content.template.js +819 -0
- package/generators/templates/index.js +2 -1
- package/hooks/git-autopilot.js +1250 -0
- package/hooks/index.js +9 -0
- package/intelligence/agent-collab.js +2057 -0
- package/intelligence/auto-suggest.js +634 -0
- package/intelligence/content-gen.js +1589 -0
- package/intelligence/cross-project.js +1647 -0
- package/intelligence/index.js +184 -0
- package/intelligence/learning/insights.json +517 -7
- package/intelligence/learning/pattern-learner.js +1008 -14
- package/intelligence/memory/decision-tracker.js +1431 -31
- package/intelligence/memory/decisions.jsonl +0 -0
- package/intelligence/orchestrator.js +2896 -1
- package/intelligence/prd.js +92 -1
- package/intelligence/recommendation-weights.json +14 -2
- package/intelligence/recommendations.js +463 -9
- package/intelligence/workflow-composer.js +1451 -0
- package/marketplace/index.d.ts +324 -0
- package/marketplace/index.js +1921 -0
- package/mcp/contracts/mcp-contract.v1.json +342 -4
- package/mcp/registry.js +680 -3
- package/mcp/response-formatter.js +23 -0
- package/mcp/tools/assist-tool.js +78 -4
- package/mcp/tools/autopilot-tool.js +408 -0
- package/mcp/tools/content-tool.js +571 -0
- package/mcp/tools/dashboard-tool.js +251 -5
- package/mcp/tools/mvp-tool.js +344 -0
- package/mcp/tools/plugin-tool.js +23 -1
- package/mcp/tools/prd-tool.js +579 -0
- package/mcp/tools/seed-tool.js +447 -0
- package/mcp/tools/skill-tool.js +43 -14
- package/mcp/tools/suggest-tool.js +147 -0
- package/package.json +15 -6
- package/agents/README.md +0 -93
- package/agents/ai-integration-expert/context.md +0 -386
- package/agents/api-expert/context.md +0 -416
- package/agents/architecture-expert/context.md +0 -454
- package/agents/auth-expert/context.md +0 -399
- package/agents/backend-expert/context.md +0 -483
- package/agents/business-strategy-expert/context.md +0 -180
- package/agents/code-review-expert/context.md +0 -365
- package/agents/competitive-analysis-expert/context.md +0 -239
- package/agents/data-modeling-expert/context.md +0 -352
- package/agents/database-expert/context.md +0 -250
- package/agents/devops-expert/context.md +0 -446
- package/agents/email-expert/context.md +0 -379
- package/agents/financial-expert/context.md +0 -213
- package/agents/frontend-expert/context.md +0 -364
- package/agents/fundraising-expert/context.md +0 -257
- package/agents/growth-expert/context.md +0 -249
- package/agents/index.js +0 -140
- package/agents/investor-relations-expert/context.md +0 -266
- package/agents/legal-expert/context.md +0 -284
- package/agents/marketing-expert/context.md +0 -236
- package/agents/monitoring-expert/context.md +0 -362
- package/agents/operations-expert/context.md +0 -279
- package/agents/partnerships-expert/context.md +0 -286
- package/agents/payment-expert/context.md +0 -340
- package/agents/performance-expert/context.md +0 -377
- package/agents/private-equity-expert/context.md +0 -246
- package/agents/railway-expert/context.md +0 -284
- package/agents/research-expert/context.md +0 -245
- package/agents/sales-expert/context.md +0 -241
- package/agents/security-expert/context.md +0 -343
- package/agents/testing-expert/context.md +0 -414
- package/agents/ui-ux-expert/context.md +0 -448
- package/agents/vercel-expert/context.md +0 -426
- package/skills/index.js +0 -787
- package/skills/patterns/README.md +0 -163
- package/skills/patterns/ai/agents.md +0 -281
- package/skills/patterns/ai/claude.md +0 -138
- package/skills/patterns/ai/embeddings.md +0 -150
- package/skills/patterns/ai/rag.md +0 -266
- package/skills/patterns/ai/streaming.md +0 -170
- package/skills/patterns/ai/structured-output.md +0 -162
- package/skills/patterns/ai/tools.md +0 -154
- package/skills/patterns/analytics/tracking.md +0 -220
- package/skills/patterns/api/errors.md +0 -296
- package/skills/patterns/api/graphql.md +0 -440
- package/skills/patterns/api/middleware.md +0 -279
- package/skills/patterns/api/openapi.md +0 -285
- package/skills/patterns/api/rate-limiting.md +0 -231
- package/skills/patterns/api/route-handler.md +0 -217
- package/skills/patterns/api/server-action.md +0 -249
- package/skills/patterns/api/versioning.md +0 -443
- package/skills/patterns/api/webhooks.md +0 -247
- package/skills/patterns/auth/clerk.md +0 -132
- package/skills/patterns/auth/mfa.md +0 -313
- package/skills/patterns/auth/nextauth.md +0 -140
- package/skills/patterns/auth/oauth.md +0 -237
- package/skills/patterns/auth/rbac.md +0 -152
- package/skills/patterns/auth/session-management.md +0 -367
- package/skills/patterns/auth/session.md +0 -120
- package/skills/patterns/database/audit.md +0 -177
- package/skills/patterns/database/migrations.md +0 -177
- package/skills/patterns/database/pagination.md +0 -230
- package/skills/patterns/database/pooling.md +0 -357
- package/skills/patterns/database/prisma.md +0 -180
- package/skills/patterns/database/relations.md +0 -187
- package/skills/patterns/database/seeding.md +0 -246
- package/skills/patterns/database/soft-delete.md +0 -153
- package/skills/patterns/database/transactions.md +0 -162
- package/skills/patterns/deployment/ci-cd.md +0 -231
- package/skills/patterns/deployment/docker.md +0 -188
- package/skills/patterns/deployment/monitoring.md +0 -387
- package/skills/patterns/deployment/vercel.md +0 -160
- package/skills/patterns/email/resend.md +0 -143
- package/skills/patterns/email/templates.md +0 -245
- package/skills/patterns/email/transactional.md +0 -503
- package/skills/patterns/email/verification.md +0 -176
- package/skills/patterns/files/download.md +0 -243
- package/skills/patterns/files/upload.md +0 -239
- package/skills/patterns/i18n/nextintl.md +0 -188
- package/skills/patterns/logging/structured.md +0 -292
- package/skills/patterns/notifications/email-queue.md +0 -248
- package/skills/patterns/notifications/push.md +0 -279
- package/skills/patterns/payments/checkout.md +0 -303
- package/skills/patterns/payments/invoices.md +0 -287
- package/skills/patterns/payments/portal.md +0 -245
- package/skills/patterns/payments/stripe.md +0 -272
- package/skills/patterns/payments/subscriptions.md +0 -300
- package/skills/patterns/payments/usage.md +0 -279
- package/skills/patterns/performance/caching.md +0 -276
- package/skills/patterns/performance/code-splitting.md +0 -233
- package/skills/patterns/performance/edge.md +0 -254
- package/skills/patterns/performance/isr.md +0 -266
- package/skills/patterns/performance/lazy-loading.md +0 -281
- package/skills/patterns/realtime/sse.md +0 -327
- package/skills/patterns/realtime/websockets.md +0 -336
- package/skills/patterns/search/filtering.md +0 -329
- package/skills/patterns/search/fulltext.md +0 -260
- package/skills/patterns/security/audit-logging.md +0 -444
- package/skills/patterns/security/csrf.md +0 -234
- package/skills/patterns/security/headers.md +0 -252
- package/skills/patterns/security/sanitization.md +0 -258
- package/skills/patterns/security/secrets.md +0 -261
- package/skills/patterns/security/validation.md +0 -268
- package/skills/patterns/security/xss.md +0 -229
- package/skills/patterns/seo/metadata.md +0 -252
- package/skills/patterns/state/context.md +0 -349
- package/skills/patterns/state/react-query.md +0 -313
- package/skills/patterns/state/url-state.md +0 -482
- package/skills/patterns/state/zustand.md +0 -262
- package/skills/patterns/testing/api.md +0 -259
- package/skills/patterns/testing/component.md +0 -233
- package/skills/patterns/testing/coverage.md +0 -207
- package/skills/patterns/testing/fixtures.md +0 -225
- package/skills/patterns/testing/integration.md +0 -436
- package/skills/patterns/testing/mocking.md +0 -177
- package/skills/patterns/testing/playwright.md +0 -162
- package/skills/patterns/testing/snapshot.md +0 -175
- package/skills/patterns/testing/vitest.md +0 -307
- package/skills/patterns/ui/accordions.md +0 -395
- package/skills/patterns/ui/cards.md +0 -299
- package/skills/patterns/ui/dropdowns.md +0 -476
- package/skills/patterns/ui/empty-states.md +0 -320
- package/skills/patterns/ui/forms.md +0 -405
- package/skills/patterns/ui/inputs.md +0 -319
- package/skills/patterns/ui/layouts.md +0 -282
- package/skills/patterns/ui/loading.md +0 -291
- package/skills/patterns/ui/modals.md +0 -338
- package/skills/patterns/ui/navigation.md +0 -374
- package/skills/patterns/ui/tables.md +0 -407
- package/skills/patterns/ui/toasts.md +0 -300
- package/skills/patterns/ui/tooltips.md +0 -396
- package/skills/patterns/utils/dates.md +0 -435
- package/skills/patterns/utils/errors.md +0 -451
- package/skills/patterns/utils/formatting.md +0 -345
- package/skills/patterns/utils/validation.md +0 -434
- package/templates/bootspring.config.js +0 -83
- package/templates/business/business-model-canvas.md +0 -246
- package/templates/business/business-plan.md +0 -266
- package/templates/business/competitive-analysis.md +0 -312
- package/templates/fundraising/data-room-checklist.md +0 -300
- package/templates/fundraising/investor-research.md +0 -243
- package/templates/fundraising/pitch-deck-outline.md +0 -253
- package/templates/legal/gdpr-checklist.md +0 -339
- package/templates/legal/privacy-policy.md +0 -285
- package/templates/legal/terms-of-service.md +0 -222
- package/templates/mcp.json +0 -9
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring Checkpoint Engine
|
|
3
|
+
* Detects and syncs project checkpoints based on file existence and criteria
|
|
4
|
+
*
|
|
5
|
+
* @package bootspring
|
|
6
|
+
* @module core/checkpoint-engine
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const utils = require('./utils');
|
|
12
|
+
const projectState = require('./project-state');
|
|
13
|
+
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Criteria Evaluators
|
|
16
|
+
// ============================================================================
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Check if a file exists
|
|
20
|
+
* @param {string} filePath - File path
|
|
21
|
+
* @returns {boolean} True if file exists
|
|
22
|
+
*/
|
|
23
|
+
function criteriaFileExists(filePath) {
|
|
24
|
+
return fs.existsSync(filePath);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if a file exists and has minimum content length
|
|
29
|
+
* @param {string} filePath - File path
|
|
30
|
+
* @param {number} minLength - Minimum content length
|
|
31
|
+
* @returns {boolean} True if file exists with minimum content
|
|
32
|
+
*/
|
|
33
|
+
function criteriaFileExistsMinLength(filePath, minLength = 500) {
|
|
34
|
+
if (!fs.existsSync(filePath)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
40
|
+
return content.length >= minLength;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check if a file contains a URL
|
|
48
|
+
* @param {string} filePath - File path
|
|
49
|
+
* @returns {boolean} True if file contains a URL
|
|
50
|
+
*/
|
|
51
|
+
function criteriaContainsUrl(filePath) {
|
|
52
|
+
if (!fs.existsSync(filePath)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
58
|
+
// Match common URL patterns
|
|
59
|
+
const urlPattern = /https?:\/\/[^\s)\]"'`]+/i;
|
|
60
|
+
return urlPattern.test(content);
|
|
61
|
+
} catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check if PROJECT_STATE.json exists (config exists)
|
|
68
|
+
* @param {string} projectRoot - Project root directory
|
|
69
|
+
* @returns {boolean} True if state file exists
|
|
70
|
+
*/
|
|
71
|
+
function criteriaConfigExists(projectRoot) {
|
|
72
|
+
const stateFile = projectState.getStateFilePath(projectRoot);
|
|
73
|
+
return fs.existsSync(stateFile);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Check if GitHub is connected in state
|
|
78
|
+
* @param {string} projectRoot - Project root directory
|
|
79
|
+
* @returns {boolean} True if GitHub is connected
|
|
80
|
+
*/
|
|
81
|
+
function criteriaGitHubConnected(projectRoot) {
|
|
82
|
+
const state = projectState.loadState(projectRoot);
|
|
83
|
+
return state?.github?.connected === true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check if content is published in state
|
|
88
|
+
* @param {string} projectRoot - Project root directory
|
|
89
|
+
* @returns {boolean} True if content is published
|
|
90
|
+
*/
|
|
91
|
+
function criteriaContentPublished(projectRoot) {
|
|
92
|
+
const state = projectState.loadState(projectRoot);
|
|
93
|
+
return state?.content?.published === true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// Checkpoint Evaluation
|
|
98
|
+
// ============================================================================
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Evaluate a single checkpoint
|
|
102
|
+
* @param {string} projectRoot - Project root directory
|
|
103
|
+
* @param {object} checkpoint - Checkpoint definition
|
|
104
|
+
* @returns {object} Evaluation result
|
|
105
|
+
*/
|
|
106
|
+
function evaluateCheckpoint(projectRoot, checkpoint) {
|
|
107
|
+
const planningDir = projectState.getPlanningDir(projectRoot);
|
|
108
|
+
const filePath = path.join(planningDir, checkpoint.sourceFile);
|
|
109
|
+
|
|
110
|
+
let completed = false;
|
|
111
|
+
let reason = null;
|
|
112
|
+
|
|
113
|
+
switch (checkpoint.criteria) {
|
|
114
|
+
case 'file_exists':
|
|
115
|
+
completed = criteriaFileExists(filePath);
|
|
116
|
+
reason = completed ? 'File exists' : 'File not found';
|
|
117
|
+
break;
|
|
118
|
+
|
|
119
|
+
case 'file_exists_min_500':
|
|
120
|
+
completed = criteriaFileExistsMinLength(filePath, 500);
|
|
121
|
+
reason = completed ? 'File exists with sufficient content' : 'File missing or too short (<500 chars)';
|
|
122
|
+
break;
|
|
123
|
+
|
|
124
|
+
case 'contains_url':
|
|
125
|
+
completed = criteriaContainsUrl(filePath);
|
|
126
|
+
reason = completed ? 'Deployment URL found' : 'No deployment URL found';
|
|
127
|
+
break;
|
|
128
|
+
|
|
129
|
+
case 'config_exists':
|
|
130
|
+
completed = criteriaConfigExists(projectRoot);
|
|
131
|
+
reason = completed ? 'Project initialized' : 'PROJECT_STATE.json not found';
|
|
132
|
+
break;
|
|
133
|
+
|
|
134
|
+
case 'github_connected':
|
|
135
|
+
completed = criteriaGitHubConnected(projectRoot);
|
|
136
|
+
reason = completed ? 'GitHub repository connected' : 'GitHub not connected';
|
|
137
|
+
break;
|
|
138
|
+
|
|
139
|
+
case 'content_published':
|
|
140
|
+
completed = criteriaContentPublished(projectRoot);
|
|
141
|
+
reason = completed ? 'Content published' : 'Content not yet published';
|
|
142
|
+
break;
|
|
143
|
+
|
|
144
|
+
default:
|
|
145
|
+
reason = `Unknown criteria: ${checkpoint.criteria}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
id: checkpoint.id,
|
|
150
|
+
label: checkpoint.label,
|
|
151
|
+
sourceFile: checkpoint.sourceFile,
|
|
152
|
+
criteria: checkpoint.criteria,
|
|
153
|
+
completed,
|
|
154
|
+
reason
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Evaluate all checkpoints for a project
|
|
160
|
+
* @param {string} projectRoot - Project root directory
|
|
161
|
+
* @param {string} projectType - Optional project type override
|
|
162
|
+
* @returns {Array} Array of evaluation results
|
|
163
|
+
*/
|
|
164
|
+
function evaluateAllCheckpoints(projectRoot, projectType = null) {
|
|
165
|
+
// Get project type from state if not provided
|
|
166
|
+
if (!projectType) {
|
|
167
|
+
const state = projectState.loadState(projectRoot);
|
|
168
|
+
projectType = state?.projectType || projectState.PROJECT_TYPES.DEVELOPMENT;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const checkpointDefs = projectState.getCheckpointDefinitions(projectType);
|
|
172
|
+
return checkpointDefs.map(checkpoint => evaluateCheckpoint(projectRoot, checkpoint));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Sync checkpoints - detect completed checkpoints from files
|
|
177
|
+
* @param {string} projectRoot - Project root directory
|
|
178
|
+
* @param {object} options - Options
|
|
179
|
+
* @param {boolean} options.dryRun - If true, don't save changes
|
|
180
|
+
* @param {boolean} options.verbose - If true, log changes
|
|
181
|
+
* @returns {object} Sync result
|
|
182
|
+
*/
|
|
183
|
+
function syncCheckpoints(projectRoot, options = {}) {
|
|
184
|
+
const state = projectState.getOrCreateState(projectRoot);
|
|
185
|
+
const evaluations = evaluateAllCheckpoints(projectRoot, state.projectType);
|
|
186
|
+
|
|
187
|
+
const changes = [];
|
|
188
|
+
const unchanged = [];
|
|
189
|
+
|
|
190
|
+
for (const evaluation of evaluations) {
|
|
191
|
+
const currentState = state.checkpoints[evaluation.id];
|
|
192
|
+
const wasCompleted = currentState?.completed || false;
|
|
193
|
+
|
|
194
|
+
if (evaluation.completed && !wasCompleted) {
|
|
195
|
+
// Newly completed
|
|
196
|
+
changes.push({
|
|
197
|
+
id: evaluation.id,
|
|
198
|
+
label: evaluation.label,
|
|
199
|
+
type: 'completed',
|
|
200
|
+
reason: evaluation.reason
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
if (!options.dryRun) {
|
|
204
|
+
state.checkpoints[evaluation.id] = {
|
|
205
|
+
completed: true,
|
|
206
|
+
completedAt: new Date().toISOString(),
|
|
207
|
+
completedBy: 'sync'
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
projectState.recordCheckpointHistory(projectRoot, {
|
|
211
|
+
checkpointId: evaluation.id,
|
|
212
|
+
completedAt: state.checkpoints[evaluation.id].completedAt,
|
|
213
|
+
completedBy: 'sync',
|
|
214
|
+
notes: evaluation.reason
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
} else if (!evaluation.completed && wasCompleted) {
|
|
218
|
+
// Was completed but no longer meets criteria (rare case)
|
|
219
|
+
changes.push({
|
|
220
|
+
id: evaluation.id,
|
|
221
|
+
label: evaluation.label,
|
|
222
|
+
type: 'uncompleted',
|
|
223
|
+
reason: evaluation.reason
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Note: We don't uncomplete checkpoints automatically - they stay completed once done
|
|
227
|
+
unchanged.push(evaluation);
|
|
228
|
+
} else {
|
|
229
|
+
unchanged.push(evaluation);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Save state if not dry run
|
|
234
|
+
if (!options.dryRun && changes.length > 0) {
|
|
235
|
+
projectState.saveState(projectRoot, state);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Log if verbose
|
|
239
|
+
if (options.verbose) {
|
|
240
|
+
for (const change of changes) {
|
|
241
|
+
if (change.type === 'completed') {
|
|
242
|
+
utils.print.success(`Checkpoint completed: ${change.label}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
changes,
|
|
249
|
+
unchanged,
|
|
250
|
+
total: evaluations.length,
|
|
251
|
+
completed: evaluations.filter(e => e.completed).length,
|
|
252
|
+
state
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ============================================================================
|
|
257
|
+
// Progress Reporting
|
|
258
|
+
// ============================================================================
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Get checkpoint status with visual indicators
|
|
262
|
+
* @param {string} projectRoot - Project root directory
|
|
263
|
+
* @returns {object} Status report
|
|
264
|
+
*/
|
|
265
|
+
function getCheckpointStatus(projectRoot) {
|
|
266
|
+
const state = projectState.loadState(projectRoot);
|
|
267
|
+
|
|
268
|
+
if (!state) {
|
|
269
|
+
return {
|
|
270
|
+
exists: false,
|
|
271
|
+
message: 'Project state not initialized. Run `bootspring checkpoint init` to start.'
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const evaluations = evaluateAllCheckpoints(projectRoot, state.projectType);
|
|
276
|
+
const completed = evaluations.filter(e => e.completed);
|
|
277
|
+
const pending = evaluations.filter(e => !e.completed);
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
exists: true,
|
|
281
|
+
projectType: state.projectType,
|
|
282
|
+
autoTagged: state.autoTagged,
|
|
283
|
+
taggedBy: state.taggedBy,
|
|
284
|
+
total: evaluations.length,
|
|
285
|
+
completed: completed.length,
|
|
286
|
+
pending: pending.length,
|
|
287
|
+
percentage: Math.round((completed.length / evaluations.length) * 100),
|
|
288
|
+
checkpoints: evaluations,
|
|
289
|
+
createdAt: state.createdAt,
|
|
290
|
+
updatedAt: state.updatedAt
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Get next steps - what checkpoints should be completed next
|
|
296
|
+
* @param {string} projectRoot - Project root directory
|
|
297
|
+
* @returns {Array} Array of suggested next checkpoints
|
|
298
|
+
*/
|
|
299
|
+
function getNextSteps(projectRoot) {
|
|
300
|
+
const state = projectState.loadState(projectRoot);
|
|
301
|
+
|
|
302
|
+
if (!state) {
|
|
303
|
+
return [];
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const evaluations = evaluateAllCheckpoints(projectRoot, state.projectType);
|
|
307
|
+
const pending = evaluations.filter(e => !e.completed);
|
|
308
|
+
|
|
309
|
+
// Return first 3 incomplete checkpoints as suggestions
|
|
310
|
+
return pending.slice(0, 3).map(checkpoint => ({
|
|
311
|
+
id: checkpoint.id,
|
|
312
|
+
label: checkpoint.label,
|
|
313
|
+
sourceFile: checkpoint.sourceFile,
|
|
314
|
+
action: getActionSuggestion(checkpoint)
|
|
315
|
+
}));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Get action suggestion for a checkpoint
|
|
320
|
+
* @param {object} checkpoint - Checkpoint evaluation
|
|
321
|
+
* @returns {string} Suggested action
|
|
322
|
+
*/
|
|
323
|
+
function getActionSuggestion(checkpoint) {
|
|
324
|
+
switch (checkpoint.criteria) {
|
|
325
|
+
case 'file_exists':
|
|
326
|
+
case 'file_exists_min_500':
|
|
327
|
+
return `Create planning/${checkpoint.sourceFile}`;
|
|
328
|
+
case 'contains_url':
|
|
329
|
+
return `Add deployment URL to planning/${checkpoint.sourceFile}`;
|
|
330
|
+
case 'github_connected':
|
|
331
|
+
return 'Run `bootspring github connect`';
|
|
332
|
+
case 'content_published':
|
|
333
|
+
return 'Mark content as published';
|
|
334
|
+
default:
|
|
335
|
+
return 'Complete this checkpoint';
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ============================================================================
|
|
340
|
+
// Progress Bar Generation
|
|
341
|
+
// ============================================================================
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Generate a progress bar string
|
|
345
|
+
* @param {number} percentage - Progress percentage (0-100)
|
|
346
|
+
* @param {number} width - Bar width in characters
|
|
347
|
+
* @returns {string} Progress bar string
|
|
348
|
+
*/
|
|
349
|
+
function generateProgressBar(percentage, width = 20) {
|
|
350
|
+
const filled = Math.round((percentage / 100) * width);
|
|
351
|
+
const empty = width - filled;
|
|
352
|
+
|
|
353
|
+
const filledChar = '█';
|
|
354
|
+
const emptyChar = '░';
|
|
355
|
+
|
|
356
|
+
return filledChar.repeat(filled) + emptyChar.repeat(empty);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Get colored progress bar
|
|
361
|
+
* @param {number} percentage - Progress percentage
|
|
362
|
+
* @param {number} width - Bar width
|
|
363
|
+
* @returns {string} Colored progress bar
|
|
364
|
+
*/
|
|
365
|
+
function getColoredProgressBar(percentage, width = 20) {
|
|
366
|
+
const bar = generateProgressBar(percentage, width);
|
|
367
|
+
let color;
|
|
368
|
+
|
|
369
|
+
if (percentage >= 80) {
|
|
370
|
+
color = utils.COLORS.green;
|
|
371
|
+
} else if (percentage >= 50) {
|
|
372
|
+
color = utils.COLORS.yellow;
|
|
373
|
+
} else if (percentage >= 25) {
|
|
374
|
+
color = utils.COLORS.cyan;
|
|
375
|
+
} else {
|
|
376
|
+
color = utils.COLORS.dim;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return `${color}${bar}${utils.COLORS.reset}`;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// ============================================================================
|
|
383
|
+
// Exports
|
|
384
|
+
// ============================================================================
|
|
385
|
+
|
|
386
|
+
module.exports = {
|
|
387
|
+
// Criteria evaluators
|
|
388
|
+
criteriaFileExists,
|
|
389
|
+
criteriaFileExistsMinLength,
|
|
390
|
+
criteriaContainsUrl,
|
|
391
|
+
criteriaConfigExists,
|
|
392
|
+
criteriaGitHubConnected,
|
|
393
|
+
criteriaContentPublished,
|
|
394
|
+
|
|
395
|
+
// Evaluation
|
|
396
|
+
evaluateCheckpoint,
|
|
397
|
+
evaluateAllCheckpoints,
|
|
398
|
+
syncCheckpoints,
|
|
399
|
+
|
|
400
|
+
// Status
|
|
401
|
+
getCheckpointStatus,
|
|
402
|
+
getNextSteps,
|
|
403
|
+
getActionSuggestion,
|
|
404
|
+
|
|
405
|
+
// Progress
|
|
406
|
+
generateProgressBar,
|
|
407
|
+
getColoredProgressBar
|
|
408
|
+
};
|