@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,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring Cloud Sync CLI
|
|
3
|
+
* Sync project context to Cloudflare R2
|
|
4
|
+
*
|
|
5
|
+
* @package bootspring
|
|
6
|
+
* @module cli/cloud-sync
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const r2Sync = require('../core/r2-sync');
|
|
10
|
+
const auth = require('../core/auth');
|
|
11
|
+
const session = require('../core/session');
|
|
12
|
+
const utils = require('../core/utils');
|
|
13
|
+
|
|
14
|
+
const { COLORS: C } = utils;
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Help
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
function showHelp() {
|
|
21
|
+
console.log(`
|
|
22
|
+
${C.bold}${C.cyan}bootspring cloud-sync${C.reset} - Sync project context to cloud storage
|
|
23
|
+
|
|
24
|
+
${C.bold}USAGE${C.reset}
|
|
25
|
+
bootspring cloud-sync <command> [options]
|
|
26
|
+
|
|
27
|
+
${C.bold}COMMANDS${C.reset}
|
|
28
|
+
${C.cyan}status${C.reset} Show sync status
|
|
29
|
+
${C.cyan}push${C.reset} Push local context to cloud
|
|
30
|
+
${C.cyan}pull${C.reset} Pull context from cloud
|
|
31
|
+
${C.cyan}history${C.reset} List version history
|
|
32
|
+
${C.cyan}restore${C.reset} <version> Restore a specific version
|
|
33
|
+
${C.cyan}configure${C.reset} Configure R2 credentials
|
|
34
|
+
${C.cyan}help${C.reset} Show this help
|
|
35
|
+
|
|
36
|
+
${C.bold}OPTIONS${C.reset}
|
|
37
|
+
--force Force overwrite without confirmation
|
|
38
|
+
--no-version Don't create version backup on push
|
|
39
|
+
--verify Verify checksum before pull
|
|
40
|
+
|
|
41
|
+
${C.bold}EXAMPLES${C.reset}
|
|
42
|
+
${C.dim}# Check sync status${C.reset}
|
|
43
|
+
bootspring cloud-sync status
|
|
44
|
+
|
|
45
|
+
${C.dim}# Push context to cloud${C.reset}
|
|
46
|
+
bootspring cloud-sync push
|
|
47
|
+
|
|
48
|
+
${C.dim}# Pull latest context${C.reset}
|
|
49
|
+
bootspring cloud-sync pull
|
|
50
|
+
|
|
51
|
+
${C.dim}# Restore previous version${C.reset}
|
|
52
|
+
bootspring cloud-sync restore v1708534800000
|
|
53
|
+
|
|
54
|
+
${C.bold}ENVIRONMENT VARIABLES${C.reset}
|
|
55
|
+
BOOTSPRING_R2_ACCOUNT_ID Cloudflare account ID
|
|
56
|
+
BOOTSPRING_R2_ACCESS_KEY_ID R2 access key ID
|
|
57
|
+
BOOTSPRING_R2_SECRET_ACCESS_KEY R2 secret access key
|
|
58
|
+
BOOTSPRING_R2_BUCKET_NAME Bucket name (default: bootspring-context)
|
|
59
|
+
|
|
60
|
+
${C.dim}Configure R2 at: https://dash.cloudflare.com > R2 > Manage R2 API Tokens${C.reset}
|
|
61
|
+
`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// Commands
|
|
66
|
+
// ============================================================================
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Show sync status
|
|
70
|
+
*/
|
|
71
|
+
async function statusCommand(projectRoot, projectId) {
|
|
72
|
+
console.log(`\n${C.bold}Cloud Sync Status${C.reset}\n`);
|
|
73
|
+
|
|
74
|
+
// Check configuration
|
|
75
|
+
if (!r2Sync.isConfigured()) {
|
|
76
|
+
console.log(`${C.yellow}⚠${C.reset} R2 not configured`);
|
|
77
|
+
console.log(`${C.dim} Set environment variables or run: bootspring cloud-sync configure${C.reset}\n`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
console.log(`${C.green}✓${C.reset} R2 configured`);
|
|
82
|
+
|
|
83
|
+
// Validate credentials
|
|
84
|
+
const spinner = utils.createSpinner('Checking connection...');
|
|
85
|
+
spinner.start();
|
|
86
|
+
|
|
87
|
+
const validation = await r2Sync.validateCredentials();
|
|
88
|
+
if (!validation.valid) {
|
|
89
|
+
spinner.fail(`Connection failed: ${validation.error}`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
spinner.succeed('Connection verified');
|
|
93
|
+
|
|
94
|
+
// Get status
|
|
95
|
+
const status = await r2Sync.getStatus(projectRoot, projectId);
|
|
96
|
+
|
|
97
|
+
console.log(`\n${C.bold}Local State${C.reset}`);
|
|
98
|
+
if (status.local.lastPush) {
|
|
99
|
+
console.log(` Last push: ${C.cyan}${status.local.lastPush}${C.reset}`);
|
|
100
|
+
}
|
|
101
|
+
if (status.local.lastPull) {
|
|
102
|
+
console.log(` Last pull: ${C.cyan}${status.local.lastPull}${C.reset}`);
|
|
103
|
+
}
|
|
104
|
+
if (status.local.currentChecksum) {
|
|
105
|
+
console.log(` Checksum: ${C.dim}${status.local.currentChecksum.substring(0, 16)}...${C.reset}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (status.remote) {
|
|
109
|
+
console.log(`\n${C.bold}Remote State${C.reset}`);
|
|
110
|
+
console.log(` Last sync: ${C.cyan}${status.remote.lastPush}${C.reset}`);
|
|
111
|
+
console.log(` Version: ${C.cyan}${status.remote.lastVersion}${C.reset}`);
|
|
112
|
+
console.log(` Files: ${status.remote.fileCount}`);
|
|
113
|
+
console.log(` Size: ${formatBytes(status.remote.totalSize)}`);
|
|
114
|
+
|
|
115
|
+
if (status.needsSync) {
|
|
116
|
+
console.log(`\n${C.yellow}⚠${C.reset} Local changes detected - run ${C.cyan}bootspring cloud-sync push${C.reset} to sync`);
|
|
117
|
+
} else {
|
|
118
|
+
console.log(`\n${C.green}✓${C.reset} Up to date`);
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
console.log(`\n${C.dim}No remote context found. Run 'bootspring cloud-sync push' to create.${C.reset}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
console.log();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Push context to cloud
|
|
129
|
+
*/
|
|
130
|
+
async function pushCommand(projectRoot, projectId, options) {
|
|
131
|
+
console.log(`\n${C.bold}Push Context to Cloud${C.reset}\n`);
|
|
132
|
+
|
|
133
|
+
if (!r2Sync.isConfigured()) {
|
|
134
|
+
console.log(`${C.red}Error:${C.reset} R2 not configured`);
|
|
135
|
+
console.log(`${C.dim}Set environment variables or run: bootspring cloud-sync configure${C.reset}\n`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Generate package preview
|
|
140
|
+
const pkg = r2Sync.generateContextPackage(projectRoot);
|
|
141
|
+
console.log(`Files to sync: ${C.cyan}${pkg.metadata.totalFiles}${C.reset}`);
|
|
142
|
+
console.log(`Total size: ${C.cyan}${formatBytes(pkg.metadata.totalSize)}${C.reset}`);
|
|
143
|
+
console.log();
|
|
144
|
+
|
|
145
|
+
// Confirm unless forced
|
|
146
|
+
if (!options.force) {
|
|
147
|
+
const readline = require('readline');
|
|
148
|
+
const rl = readline.createInterface({
|
|
149
|
+
input: process.stdin,
|
|
150
|
+
output: process.stdout
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const answer = await new Promise(resolve => {
|
|
154
|
+
rl.question('Push to cloud? (y/N) ', resolve);
|
|
155
|
+
});
|
|
156
|
+
rl.close();
|
|
157
|
+
|
|
158
|
+
if (answer.toLowerCase() !== 'y') {
|
|
159
|
+
console.log(`${C.dim}Cancelled${C.reset}\n`);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const spinner = utils.createSpinner('Pushing context to R2...');
|
|
165
|
+
spinner.start();
|
|
166
|
+
|
|
167
|
+
const result = await r2Sync.pushContext(projectRoot, projectId, {
|
|
168
|
+
createVersion: !options.noVersion
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if (result.success) {
|
|
172
|
+
spinner.succeed(`Pushed ${result.fileCount} files (${formatBytes(result.totalSize)})`);
|
|
173
|
+
console.log(`${C.dim}Version: ${result.version}${C.reset}\n`);
|
|
174
|
+
} else {
|
|
175
|
+
spinner.fail(`Push failed: ${result.error}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Pull context from cloud
|
|
181
|
+
*/
|
|
182
|
+
async function pullCommand(projectRoot, projectId, options) {
|
|
183
|
+
console.log(`\n${C.bold}Pull Context from Cloud${C.reset}\n`);
|
|
184
|
+
|
|
185
|
+
if (!r2Sync.isConfigured()) {
|
|
186
|
+
console.log(`${C.red}Error:${C.reset} R2 not configured`);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Check for local changes
|
|
191
|
+
if (!options.force) {
|
|
192
|
+
const status = await r2Sync.getStatus(projectRoot, projectId);
|
|
193
|
+
if (status.needsSync) {
|
|
194
|
+
console.log(`${C.yellow}Warning:${C.reset} Local changes will be overwritten`);
|
|
195
|
+
|
|
196
|
+
const readline = require('readline');
|
|
197
|
+
const rl = readline.createInterface({
|
|
198
|
+
input: process.stdin,
|
|
199
|
+
output: process.stdout
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const answer = await new Promise(resolve => {
|
|
203
|
+
rl.question('Continue? (y/N) ', resolve);
|
|
204
|
+
});
|
|
205
|
+
rl.close();
|
|
206
|
+
|
|
207
|
+
if (answer.toLowerCase() !== 'y') {
|
|
208
|
+
console.log(`${C.dim}Cancelled${C.reset}\n`);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const spinner = utils.createSpinner('Pulling context from R2...');
|
|
215
|
+
spinner.start();
|
|
216
|
+
|
|
217
|
+
const result = await r2Sync.pullContext(projectRoot, projectId, {
|
|
218
|
+
version: options.version,
|
|
219
|
+
verifyChecksum: options.verify
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (result.success) {
|
|
223
|
+
if (result.upToDate) {
|
|
224
|
+
spinner.succeed('Already up to date');
|
|
225
|
+
} else {
|
|
226
|
+
spinner.succeed(`Restored ${result.restoredCount} files (${formatBytes(result.totalSize)})`);
|
|
227
|
+
console.log(`${C.dim}Version: ${result.version}${C.reset}`);
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
spinner.fail(`Pull failed: ${result.error}`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
console.log();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* List version history
|
|
238
|
+
*/
|
|
239
|
+
async function historyCommand(projectId) {
|
|
240
|
+
console.log(`\n${C.bold}Version History${C.reset}\n`);
|
|
241
|
+
|
|
242
|
+
if (!r2Sync.isConfigured()) {
|
|
243
|
+
console.log(`${C.red}Error:${C.reset} R2 not configured`);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const spinner = utils.createSpinner('Fetching versions...');
|
|
248
|
+
spinner.start();
|
|
249
|
+
|
|
250
|
+
const versions = await r2Sync.listVersions(projectId);
|
|
251
|
+
spinner.stop();
|
|
252
|
+
|
|
253
|
+
if (versions.length === 0) {
|
|
254
|
+
console.log(`${C.dim}No versions found${C.reset}\n`);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
console.log(`Found ${C.cyan}${versions.length}${C.reset} versions:\n`);
|
|
259
|
+
|
|
260
|
+
for (const version of versions.slice(0, 10)) {
|
|
261
|
+
// Parse timestamp from version
|
|
262
|
+
const timestamp = parseInt(version.replace('v', ''));
|
|
263
|
+
const date = new Date(timestamp);
|
|
264
|
+
console.log(` ${C.cyan}${version}${C.reset} ${C.dim}${date.toLocaleString()}${C.reset}`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (versions.length > 10) {
|
|
268
|
+
console.log(` ${C.dim}... and ${versions.length - 10} more${C.reset}`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
console.log(`\n${C.dim}Restore with: bootspring cloud-sync restore <version>${C.reset}\n`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Restore a specific version
|
|
276
|
+
*/
|
|
277
|
+
async function restoreCommand(projectRoot, projectId, version, options) {
|
|
278
|
+
console.log(`\n${C.bold}Restore Version${C.reset}\n`);
|
|
279
|
+
|
|
280
|
+
if (!r2Sync.isConfigured()) {
|
|
281
|
+
console.log(`${C.red}Error:${C.reset} R2 not configured`);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
console.log(`Version: ${C.cyan}${version}${C.reset}`);
|
|
286
|
+
|
|
287
|
+
// Confirm
|
|
288
|
+
if (!options.force) {
|
|
289
|
+
console.log(`${C.yellow}Warning:${C.reset} This will overwrite local files`);
|
|
290
|
+
|
|
291
|
+
const readline = require('readline');
|
|
292
|
+
const rl = readline.createInterface({
|
|
293
|
+
input: process.stdin,
|
|
294
|
+
output: process.stdout
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const answer = await new Promise(resolve => {
|
|
298
|
+
rl.question('Continue? (y/N) ', resolve);
|
|
299
|
+
});
|
|
300
|
+
rl.close();
|
|
301
|
+
|
|
302
|
+
if (answer.toLowerCase() !== 'y') {
|
|
303
|
+
console.log(`${C.dim}Cancelled${C.reset}\n`);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const spinner = utils.createSpinner(`Restoring ${version}...`);
|
|
309
|
+
spinner.start();
|
|
310
|
+
|
|
311
|
+
const result = await r2Sync.pullContext(projectRoot, projectId, { version });
|
|
312
|
+
|
|
313
|
+
if (result.success) {
|
|
314
|
+
spinner.succeed(`Restored ${result.restoredCount} files`);
|
|
315
|
+
} else {
|
|
316
|
+
spinner.fail(`Restore failed: ${result.error}`);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
console.log();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Configure R2 credentials
|
|
324
|
+
*/
|
|
325
|
+
async function configureCommand() {
|
|
326
|
+
console.log(`\n${C.bold}Configure Cloud Sync (Cloudflare R2)${C.reset}\n`);
|
|
327
|
+
|
|
328
|
+
console.log('To configure R2, set the following environment variables:\n');
|
|
329
|
+
console.log(` ${C.cyan}BOOTSPRING_R2_ACCOUNT_ID${C.reset} Your Cloudflare account ID`);
|
|
330
|
+
console.log(` ${C.cyan}BOOTSPRING_R2_ACCESS_KEY_ID${C.reset} R2 API token access key`);
|
|
331
|
+
console.log(` ${C.cyan}BOOTSPRING_R2_SECRET_ACCESS_KEY${C.reset} R2 API token secret`);
|
|
332
|
+
console.log(` ${C.cyan}BOOTSPRING_R2_BUCKET_NAME${C.reset} Bucket name (optional)`);
|
|
333
|
+
|
|
334
|
+
console.log(`\n${C.bold}Steps:${C.reset}`);
|
|
335
|
+
console.log(` 1. Go to ${C.cyan}https://dash.cloudflare.com${C.reset}`);
|
|
336
|
+
console.log(' 2. Navigate to R2 > Manage R2 API Tokens');
|
|
337
|
+
console.log(' 3. Create a new API token with Object Read & Write permissions');
|
|
338
|
+
console.log(' 4. Copy the Access Key ID and Secret Access Key');
|
|
339
|
+
console.log(' 5. Create a bucket (or use default: bootspring-context)');
|
|
340
|
+
console.log(' 6. Add the environment variables to your shell profile');
|
|
341
|
+
|
|
342
|
+
console.log(`\n${C.bold}Example (.bashrc / .zshrc):${C.reset}`);
|
|
343
|
+
console.log(`${C.dim} export BOOTSPRING_R2_ACCOUNT_ID="your-account-id"${C.reset}`);
|
|
344
|
+
console.log(`${C.dim} export BOOTSPRING_R2_ACCESS_KEY_ID="your-access-key"${C.reset}`);
|
|
345
|
+
console.log(`${C.dim} export BOOTSPRING_R2_SECRET_ACCESS_KEY="your-secret"${C.reset}`);
|
|
346
|
+
|
|
347
|
+
console.log();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ============================================================================
|
|
351
|
+
// Utilities
|
|
352
|
+
// ============================================================================
|
|
353
|
+
|
|
354
|
+
function formatBytes(bytes) {
|
|
355
|
+
if (bytes === 0) return '0 B';
|
|
356
|
+
const k = 1024;
|
|
357
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
358
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
359
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function parseArgs(args) {
|
|
363
|
+
const options = {
|
|
364
|
+
force: false,
|
|
365
|
+
noVersion: false,
|
|
366
|
+
verify: false,
|
|
367
|
+
version: null
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const positional = [];
|
|
371
|
+
|
|
372
|
+
for (let i = 0; i < args.length; i++) {
|
|
373
|
+
const arg = args[i];
|
|
374
|
+
if (arg === '--force' || arg === '-f') {
|
|
375
|
+
options.force = true;
|
|
376
|
+
} else if (arg === '--no-version') {
|
|
377
|
+
options.noVersion = true;
|
|
378
|
+
} else if (arg === '--verify') {
|
|
379
|
+
options.verify = true;
|
|
380
|
+
} else if (!arg.startsWith('-')) {
|
|
381
|
+
positional.push(arg);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return { options, positional };
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ============================================================================
|
|
389
|
+
// Main
|
|
390
|
+
// ============================================================================
|
|
391
|
+
|
|
392
|
+
async function main(args) {
|
|
393
|
+
const { options, positional } = parseArgs(args);
|
|
394
|
+
const command = positional[0] || 'help';
|
|
395
|
+
|
|
396
|
+
// Help doesn't require auth
|
|
397
|
+
if (command === 'help' || command === '--help' || command === '-h') {
|
|
398
|
+
showHelp();
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Configure doesn't require auth
|
|
403
|
+
if (command === 'configure') {
|
|
404
|
+
await configureCommand();
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Get project context
|
|
409
|
+
const projectRoot = process.cwd();
|
|
410
|
+
const project = session.getEffectiveProject();
|
|
411
|
+
const projectId = project?.id || project?.slug || 'local';
|
|
412
|
+
|
|
413
|
+
switch (command) {
|
|
414
|
+
case 'status':
|
|
415
|
+
await statusCommand(projectRoot, projectId);
|
|
416
|
+
break;
|
|
417
|
+
|
|
418
|
+
case 'push':
|
|
419
|
+
await pushCommand(projectRoot, projectId, options);
|
|
420
|
+
break;
|
|
421
|
+
|
|
422
|
+
case 'pull':
|
|
423
|
+
await pullCommand(projectRoot, projectId, options);
|
|
424
|
+
break;
|
|
425
|
+
|
|
426
|
+
case 'history':
|
|
427
|
+
await historyCommand(projectId);
|
|
428
|
+
break;
|
|
429
|
+
|
|
430
|
+
case 'restore': {
|
|
431
|
+
const version = positional[1];
|
|
432
|
+
if (!version) {
|
|
433
|
+
console.log(`${C.red}Error:${C.reset} Version required`);
|
|
434
|
+
console.log(`${C.dim}Usage: bootspring cloud-sync restore <version>${C.reset}\n`);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
await restoreCommand(projectRoot, projectId, version, options);
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
default:
|
|
442
|
+
console.log(`${C.red}Unknown command:${C.reset} ${command}`);
|
|
443
|
+
showHelp();
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
module.exports = { run: main, showHelp };
|
package/cli/content.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring Content Command
|
|
3
|
+
* Generate and manage content (blogs, docs, release notes, etc.)
|
|
4
|
+
*
|
|
5
|
+
* @package bootspring
|
|
6
|
+
* @command content
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const utils = require('../core/utils');
|
|
11
|
+
const contentTemplate = require('../generators/templates/content.template');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Generate content file
|
|
15
|
+
*/
|
|
16
|
+
function generateContent(type, args) {
|
|
17
|
+
const contentType = contentTemplate.getContentType(type);
|
|
18
|
+
|
|
19
|
+
if (!contentType) {
|
|
20
|
+
utils.print.error(`Unknown content type: ${type}`);
|
|
21
|
+
utils.print.dim('Available types: ' + Object.keys(contentTemplate.CONTENT_TYPES).join(', '));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const parsedArgs = utils.parseArgs(args);
|
|
26
|
+
|
|
27
|
+
// Build options from args
|
|
28
|
+
const options = {
|
|
29
|
+
title: parsedArgs.title || parsedArgs.t || '',
|
|
30
|
+
description: parsedArgs.description || parsedArgs.d || '',
|
|
31
|
+
author: parsedArgs.author || parsedArgs.a || 'Team',
|
|
32
|
+
version: parsedArgs.version || parsedArgs.v || '1.0.0',
|
|
33
|
+
tags: parsedArgs.tags ? parsedArgs.tags.split(',').map(t => t.trim()) : [],
|
|
34
|
+
name: parsedArgs.name || parsedArgs.n || '',
|
|
35
|
+
date: parsedArgs.date || new Date().toISOString().split('T')[0]
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
console.log(`
|
|
39
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Content${utils.COLORS.reset}
|
|
40
|
+
${utils.COLORS.dim}Generating ${contentType.name}...${utils.COLORS.reset}
|
|
41
|
+
`);
|
|
42
|
+
|
|
43
|
+
const spinner = utils.createSpinner(`Creating ${contentType.name}`).start();
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const content = contentTemplate.generate(type, options);
|
|
47
|
+
|
|
48
|
+
// Determine output path
|
|
49
|
+
const filename = options.title
|
|
50
|
+
? options.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '')
|
|
51
|
+
: type;
|
|
52
|
+
|
|
53
|
+
const outputDir = parsedArgs.output || parsedArgs.o || contentType.directory;
|
|
54
|
+
const outputPath = path.join(process.cwd(), outputDir, `${filename}${contentType.extension}`);
|
|
55
|
+
|
|
56
|
+
// Create directory if needed
|
|
57
|
+
const dir = path.dirname(outputPath);
|
|
58
|
+
if (!utils.fileExists(dir)) {
|
|
59
|
+
require('fs').mkdirSync(dir, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
utils.writeFile(outputPath, content);
|
|
63
|
+
spinner.succeed(`Created ${contentType.name}`);
|
|
64
|
+
|
|
65
|
+
console.log(`
|
|
66
|
+
${utils.COLORS.bold}File created:${utils.COLORS.reset} ${outputPath}
|
|
67
|
+
|
|
68
|
+
${utils.COLORS.dim}Next steps:${utils.COLORS.reset}
|
|
69
|
+
1. Edit the generated file with your content
|
|
70
|
+
2. Run ${utils.COLORS.cyan}bootspring agent invoke content-expert${utils.COLORS.reset} for writing help
|
|
71
|
+
3. Publish when ready
|
|
72
|
+
`);
|
|
73
|
+
|
|
74
|
+
} catch (error) {
|
|
75
|
+
spinner.fail(`Failed to create ${contentType.name}: ${error.message}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* List available content types
|
|
81
|
+
*/
|
|
82
|
+
function listTypes() {
|
|
83
|
+
console.log(`
|
|
84
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Content Types${utils.COLORS.reset}
|
|
85
|
+
${utils.COLORS.dim}Generate any content type with a single command${utils.COLORS.reset}
|
|
86
|
+
`);
|
|
87
|
+
|
|
88
|
+
const types = contentTemplate.listContentTypes();
|
|
89
|
+
|
|
90
|
+
for (const [id, type] of Object.entries(types)) {
|
|
91
|
+
console.log(` ${utils.COLORS.cyan}${id}${utils.COLORS.reset}`);
|
|
92
|
+
console.log(` ${utils.COLORS.dim}${type.name} (${type.extension})${utils.COLORS.reset}`);
|
|
93
|
+
console.log(` ${utils.COLORS.dim}Directory: ${type.directory}${utils.COLORS.reset}`);
|
|
94
|
+
console.log();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
utils.print.dim(`${Object.keys(types).length} content types available`);
|
|
98
|
+
utils.print.dim('Use "bootspring content new <type> --title \'Title\'" to generate');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Show content help
|
|
103
|
+
*/
|
|
104
|
+
function showHelp() {
|
|
105
|
+
console.log(`
|
|
106
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Content${utils.COLORS.reset}
|
|
107
|
+
${utils.COLORS.dim}Generate and manage content for your project${utils.COLORS.reset}
|
|
108
|
+
|
|
109
|
+
${utils.COLORS.bold}Usage:${utils.COLORS.reset}
|
|
110
|
+
bootspring content <command> [options]
|
|
111
|
+
|
|
112
|
+
${utils.COLORS.bold}Commands:${utils.COLORS.reset}
|
|
113
|
+
${utils.COLORS.cyan}new${utils.COLORS.reset} <type> Generate new content from template
|
|
114
|
+
${utils.COLORS.cyan}types${utils.COLORS.reset} List available content types
|
|
115
|
+
${utils.COLORS.cyan}help${utils.COLORS.reset} Show this help
|
|
116
|
+
|
|
117
|
+
${utils.COLORS.bold}Content Types:${utils.COLORS.reset}
|
|
118
|
+
${utils.COLORS.cyan}blog-post${utils.COLORS.reset} Blog article template
|
|
119
|
+
${utils.COLORS.cyan}release-notes${utils.COLORS.reset} Version release notes
|
|
120
|
+
${utils.COLORS.cyan}documentation${utils.COLORS.reset} Documentation page (MDX)
|
|
121
|
+
${utils.COLORS.cyan}changelog${utils.COLORS.reset} Changelog entry
|
|
122
|
+
${utils.COLORS.cyan}readme${utils.COLORS.reset} README file
|
|
123
|
+
${utils.COLORS.cyan}api-doc${utils.COLORS.reset} API endpoint documentation
|
|
124
|
+
${utils.COLORS.cyan}tutorial${utils.COLORS.reset} Step-by-step tutorial
|
|
125
|
+
|
|
126
|
+
${utils.COLORS.bold}Options:${utils.COLORS.reset}
|
|
127
|
+
${utils.COLORS.cyan}--title, -t${utils.COLORS.reset} Content title
|
|
128
|
+
${utils.COLORS.cyan}--description, -d${utils.COLORS.reset} Brief description
|
|
129
|
+
${utils.COLORS.cyan}--author, -a${utils.COLORS.reset} Author name
|
|
130
|
+
${utils.COLORS.cyan}--tags${utils.COLORS.reset} Comma-separated tags
|
|
131
|
+
${utils.COLORS.cyan}--version, -v${utils.COLORS.reset} Version number (for releases)
|
|
132
|
+
${utils.COLORS.cyan}--output, -o${utils.COLORS.reset} Output directory
|
|
133
|
+
|
|
134
|
+
${utils.COLORS.bold}Examples:${utils.COLORS.reset}
|
|
135
|
+
${utils.COLORS.dim}# Create a blog post${utils.COLORS.reset}
|
|
136
|
+
bootspring content new blog-post --title "Getting Started with Bootspring"
|
|
137
|
+
|
|
138
|
+
${utils.COLORS.dim}# Generate release notes${utils.COLORS.reset}
|
|
139
|
+
bootspring content new release-notes --version 1.2.0
|
|
140
|
+
|
|
141
|
+
${utils.COLORS.dim}# Create API documentation${utils.COLORS.reset}
|
|
142
|
+
bootspring content new api-doc --title "Create User"
|
|
143
|
+
|
|
144
|
+
${utils.COLORS.dim}# Generate a tutorial${utils.COLORS.reset}
|
|
145
|
+
bootspring content new tutorial --title "Building Your First App"
|
|
146
|
+
|
|
147
|
+
${utils.COLORS.bold}Workflow:${utils.COLORS.reset}
|
|
148
|
+
1. Generate content: ${utils.COLORS.cyan}bootspring content new blog-post --title "My Post"${utils.COLORS.reset}
|
|
149
|
+
2. Get writing help: ${utils.COLORS.cyan}bootspring agent invoke content-expert${utils.COLORS.reset}
|
|
150
|
+
3. Publish when ready
|
|
151
|
+
`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Run content command
|
|
156
|
+
*/
|
|
157
|
+
async function run(args) {
|
|
158
|
+
const subcommand = args[0] || 'help';
|
|
159
|
+
const subargs = args.slice(1);
|
|
160
|
+
|
|
161
|
+
switch (subcommand) {
|
|
162
|
+
case 'new':
|
|
163
|
+
case 'create':
|
|
164
|
+
case 'generate':
|
|
165
|
+
case 'gen':
|
|
166
|
+
if (!subargs[0]) {
|
|
167
|
+
utils.print.error('Please specify a content type');
|
|
168
|
+
utils.print.dim('Usage: bootspring content new <type> [options]');
|
|
169
|
+
utils.print.dim('Run "bootspring content types" to see available types');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
generateContent(subargs[0], subargs.slice(1));
|
|
173
|
+
break;
|
|
174
|
+
|
|
175
|
+
case 'types':
|
|
176
|
+
case 'list':
|
|
177
|
+
case 'ls':
|
|
178
|
+
listTypes();
|
|
179
|
+
break;
|
|
180
|
+
|
|
181
|
+
case 'help':
|
|
182
|
+
case '-h':
|
|
183
|
+
case '--help':
|
|
184
|
+
showHelp();
|
|
185
|
+
break;
|
|
186
|
+
|
|
187
|
+
default:
|
|
188
|
+
// Check if it's a content type shortcut
|
|
189
|
+
if (contentTemplate.getContentType(subcommand)) {
|
|
190
|
+
generateContent(subcommand, subargs);
|
|
191
|
+
} else {
|
|
192
|
+
utils.print.error(`Unknown subcommand: ${subcommand}`);
|
|
193
|
+
showHelp();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
module.exports = { run, generateContent, listTypes };
|
package/cli/context.js
CHANGED