@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,694 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring Project State
|
|
3
|
+
* Manages PROJECT_STATE.json in the planning folder
|
|
4
|
+
* Single source of truth for project checkpoints and state
|
|
5
|
+
*
|
|
6
|
+
* @package bootspring
|
|
7
|
+
* @module core/project-state
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const utils = require('./utils');
|
|
13
|
+
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Constants
|
|
16
|
+
// ============================================================================
|
|
17
|
+
|
|
18
|
+
const VERSION = '1.0.0';
|
|
19
|
+
const STATE_FILE = 'PROJECT_STATE.json';
|
|
20
|
+
const HISTORY_FILE = 'CHECKPOINT_HISTORY.json';
|
|
21
|
+
const GITHUB_SYNC_FILE = 'GITHUB_SYNC.json';
|
|
22
|
+
const PLANNING_DIR = 'planning';
|
|
23
|
+
|
|
24
|
+
// Project types
|
|
25
|
+
const PROJECT_TYPES = {
|
|
26
|
+
DEVELOPMENT: 'development',
|
|
27
|
+
CONTENT: 'content',
|
|
28
|
+
BUSINESS: 'business'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Checkpoints by project type
|
|
32
|
+
const CHECKPOINTS = {
|
|
33
|
+
development: [
|
|
34
|
+
{ id: 'initialized', label: 'Project Initialized', sourceFile: 'PROJECT_STATE.json', criteria: 'config_exists' },
|
|
35
|
+
{ id: 'prd', label: 'PRD Created', sourceFile: 'PRD.md', criteria: 'file_exists_min_500' },
|
|
36
|
+
{ id: 'technical_spec', label: 'Technical Spec', sourceFile: 'TECHNICAL_SPEC.md', criteria: 'file_exists' },
|
|
37
|
+
{ id: 'architecture', label: 'Architecture Defined', sourceFile: 'ARCHITECTURE.md', criteria: 'file_exists' },
|
|
38
|
+
{ id: 'database_schema', label: 'Database Schema', sourceFile: 'DATABASE_SCHEMA.md', criteria: 'file_exists' },
|
|
39
|
+
{ id: 'api_contracts', label: 'API Contracts', sourceFile: 'API_CONTRACTS.md', criteria: 'file_exists' },
|
|
40
|
+
{ id: 'tests_planned', label: 'Tests Planned', sourceFile: 'TEST_PLAN.md', criteria: 'file_exists' },
|
|
41
|
+
{ id: 'deployed', label: 'Deployed', sourceFile: 'DEPLOYMENT.md', criteria: 'contains_url' },
|
|
42
|
+
{ id: 'github_connected', label: 'GitHub Connected', sourceFile: 'PROJECT_STATE.json', criteria: 'github_connected' }
|
|
43
|
+
],
|
|
44
|
+
content: [
|
|
45
|
+
{ id: 'initialized', label: 'Project Initialized', sourceFile: 'PROJECT_STATE.json', criteria: 'config_exists' },
|
|
46
|
+
{ id: 'content_strategy', label: 'Content Strategy', sourceFile: 'CONTENT_STRATEGY.md', criteria: 'file_exists' },
|
|
47
|
+
{ id: 'editorial_calendar', label: 'Editorial Calendar', sourceFile: 'EDITORIAL_CALENDAR.md', criteria: 'file_exists' },
|
|
48
|
+
{ id: 'style_guide', label: 'Style Guide', sourceFile: 'STYLE_GUIDE.md', criteria: 'file_exists' },
|
|
49
|
+
{ id: 'seo_plan', label: 'SEO Plan', sourceFile: 'SEO_PLAN.md', criteria: 'file_exists' },
|
|
50
|
+
{ id: 'published', label: 'Published', sourceFile: 'PROJECT_STATE.json', criteria: 'content_published' },
|
|
51
|
+
{ id: 'github_connected', label: 'GitHub Connected', sourceFile: 'PROJECT_STATE.json', criteria: 'github_connected' }
|
|
52
|
+
],
|
|
53
|
+
business: [
|
|
54
|
+
{ id: 'initialized', label: 'Project Initialized', sourceFile: 'PROJECT_STATE.json', criteria: 'config_exists' },
|
|
55
|
+
{ id: 'business_plan', label: 'Business Plan', sourceFile: 'BUSINESS_PLAN.md', criteria: 'file_exists' },
|
|
56
|
+
{ id: 'market_analysis', label: 'Market Analysis', sourceFile: 'MARKET_ANALYSIS.md', criteria: 'file_exists' },
|
|
57
|
+
{ id: 'competitor_analysis', label: 'Competitor Analysis', sourceFile: 'COMPETITOR_ANALYSIS.md', criteria: 'file_exists' },
|
|
58
|
+
{ id: 'financial_model', label: 'Financial Model', sourceFile: 'FINANCIAL_MODEL.md', criteria: 'file_exists' },
|
|
59
|
+
{ id: 'pitch_deck', label: 'Pitch Deck', sourceFile: 'PITCH_DECK.md', criteria: 'file_exists' },
|
|
60
|
+
{ id: 'investor_list', label: 'Investor List', sourceFile: 'INVESTOR_LIST.md', criteria: 'file_exists' },
|
|
61
|
+
{ id: 'github_connected', label: 'GitHub Connected', sourceFile: 'PROJECT_STATE.json', criteria: 'github_connected' }
|
|
62
|
+
]
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// ============================================================================
|
|
66
|
+
// State Management
|
|
67
|
+
// ============================================================================
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get the planning directory path
|
|
71
|
+
* @param {string} projectRoot - Project root directory
|
|
72
|
+
* @returns {string} Planning directory path
|
|
73
|
+
*/
|
|
74
|
+
function getPlanningDir(projectRoot) {
|
|
75
|
+
return path.join(projectRoot, PLANNING_DIR);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get the state file path
|
|
80
|
+
* @param {string} projectRoot - Project root directory
|
|
81
|
+
* @returns {string} State file path
|
|
82
|
+
*/
|
|
83
|
+
function getStateFilePath(projectRoot) {
|
|
84
|
+
return path.join(getPlanningDir(projectRoot), STATE_FILE);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get the history file path
|
|
89
|
+
* @param {string} projectRoot - Project root directory
|
|
90
|
+
* @returns {string} History file path
|
|
91
|
+
*/
|
|
92
|
+
function getHistoryFilePath(projectRoot) {
|
|
93
|
+
return path.join(getPlanningDir(projectRoot), HISTORY_FILE);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get the GitHub sync file path
|
|
98
|
+
* @param {string} projectRoot - Project root directory
|
|
99
|
+
* @returns {string} GitHub sync file path
|
|
100
|
+
*/
|
|
101
|
+
function getGitHubSyncFilePath(projectRoot) {
|
|
102
|
+
return path.join(getPlanningDir(projectRoot), GITHUB_SYNC_FILE);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Create default state
|
|
107
|
+
* @param {string} projectType - Project type
|
|
108
|
+
* @returns {object} Default state object
|
|
109
|
+
*/
|
|
110
|
+
function createDefaultState(projectType = PROJECT_TYPES.DEVELOPMENT) {
|
|
111
|
+
const now = new Date().toISOString();
|
|
112
|
+
const checkpoints = {};
|
|
113
|
+
|
|
114
|
+
// Initialize all checkpoints as not completed
|
|
115
|
+
const checkpointDefs = CHECKPOINTS[projectType] || CHECKPOINTS.development;
|
|
116
|
+
for (const checkpoint of checkpointDefs) {
|
|
117
|
+
checkpoints[checkpoint.id] = {
|
|
118
|
+
completed: false,
|
|
119
|
+
completedAt: null,
|
|
120
|
+
completedBy: null
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Mark initialized as completed by default
|
|
125
|
+
checkpoints.initialized = {
|
|
126
|
+
completed: true,
|
|
127
|
+
completedAt: now,
|
|
128
|
+
completedBy: 'system'
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
version: VERSION,
|
|
133
|
+
projectType,
|
|
134
|
+
autoTagged: false,
|
|
135
|
+
taggedBy: null,
|
|
136
|
+
createdAt: now,
|
|
137
|
+
updatedAt: now,
|
|
138
|
+
checkpoints,
|
|
139
|
+
github: {
|
|
140
|
+
connected: false,
|
|
141
|
+
repositoryUrl: null,
|
|
142
|
+
owner: null,
|
|
143
|
+
repo: null,
|
|
144
|
+
defaultBranch: null,
|
|
145
|
+
lastSync: null,
|
|
146
|
+
stats: {
|
|
147
|
+
totalCommits: 0,
|
|
148
|
+
openPRs: 0,
|
|
149
|
+
closedPRs: 0,
|
|
150
|
+
contributors: 0,
|
|
151
|
+
lastCommit: null,
|
|
152
|
+
lastCommitMessage: null
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
health: {
|
|
156
|
+
score: 0,
|
|
157
|
+
lastCalculated: null,
|
|
158
|
+
breakdown: {
|
|
159
|
+
checkpoints: 0,
|
|
160
|
+
github: 0,
|
|
161
|
+
docs: 0,
|
|
162
|
+
quality: 0
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
usage: {
|
|
166
|
+
currentPeriod: new Date().toISOString().slice(0, 7),
|
|
167
|
+
contextGenerations: 0,
|
|
168
|
+
agentInvocations: 0,
|
|
169
|
+
skillSearches: 0,
|
|
170
|
+
mcpCalls: 0
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Load project state
|
|
177
|
+
* @param {string} projectRoot - Project root directory
|
|
178
|
+
* @returns {object|null} State object or null if not found
|
|
179
|
+
*/
|
|
180
|
+
function loadState(projectRoot) {
|
|
181
|
+
const stateFile = getStateFilePath(projectRoot);
|
|
182
|
+
|
|
183
|
+
if (!fs.existsSync(stateFile)) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
const content = fs.readFileSync(stateFile, 'utf-8');
|
|
189
|
+
return JSON.parse(content);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
utils.print.debug(`Error loading state: ${error.message}`);
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Save project state
|
|
198
|
+
* @param {string} projectRoot - Project root directory
|
|
199
|
+
* @param {object} state - State object to save
|
|
200
|
+
* @returns {boolean} Success status
|
|
201
|
+
*/
|
|
202
|
+
function saveState(projectRoot, state) {
|
|
203
|
+
const planningDir = getPlanningDir(projectRoot);
|
|
204
|
+
const stateFile = getStateFilePath(projectRoot);
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
// Ensure planning directory exists
|
|
208
|
+
if (!fs.existsSync(planningDir)) {
|
|
209
|
+
fs.mkdirSync(planningDir, { recursive: true });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Update timestamp
|
|
213
|
+
state.updatedAt = new Date().toISOString();
|
|
214
|
+
|
|
215
|
+
// Write state
|
|
216
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2), 'utf-8');
|
|
217
|
+
return true;
|
|
218
|
+
} catch (error) {
|
|
219
|
+
utils.print.debug(`Error saving state: ${error.message}`);
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Initialize project state
|
|
226
|
+
* @param {string} projectRoot - Project root directory
|
|
227
|
+
* @param {object} options - Options
|
|
228
|
+
* @param {string} options.projectType - Project type
|
|
229
|
+
* @param {boolean} options.autoTagged - Whether auto-tagged
|
|
230
|
+
* @param {string} options.taggedBy - What triggered the tag
|
|
231
|
+
* @returns {object} Created state
|
|
232
|
+
*/
|
|
233
|
+
function initState(projectRoot, options = {}) {
|
|
234
|
+
const projectType = options.projectType || PROJECT_TYPES.DEVELOPMENT;
|
|
235
|
+
const state = createDefaultState(projectType);
|
|
236
|
+
|
|
237
|
+
if (options.autoTagged) {
|
|
238
|
+
state.autoTagged = true;
|
|
239
|
+
state.taggedBy = options.taggedBy || null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
saveState(projectRoot, state);
|
|
243
|
+
return state;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Get or create project state
|
|
248
|
+
* @param {string} projectRoot - Project root directory
|
|
249
|
+
* @param {object} options - Options for creation if state doesn't exist
|
|
250
|
+
* @returns {object} State object
|
|
251
|
+
*/
|
|
252
|
+
function getOrCreateState(projectRoot, options = {}) {
|
|
253
|
+
let state = loadState(projectRoot);
|
|
254
|
+
|
|
255
|
+
if (!state) {
|
|
256
|
+
state = initState(projectRoot, options);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return state;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ============================================================================
|
|
263
|
+
// Checkpoint Management
|
|
264
|
+
// ============================================================================
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Get checkpoint definitions for a project type
|
|
268
|
+
* @param {string} projectType - Project type
|
|
269
|
+
* @returns {Array} Checkpoint definitions
|
|
270
|
+
*/
|
|
271
|
+
function getCheckpointDefinitions(projectType) {
|
|
272
|
+
return CHECKPOINTS[projectType] || CHECKPOINTS.development;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Mark a checkpoint as complete
|
|
277
|
+
* @param {string} projectRoot - Project root directory
|
|
278
|
+
* @param {string} checkpointId - Checkpoint ID
|
|
279
|
+
* @param {object} options - Options
|
|
280
|
+
* @param {string} options.completedBy - Who completed it
|
|
281
|
+
* @param {string} options.notes - Optional notes
|
|
282
|
+
* @returns {object|null} Updated state or null on error
|
|
283
|
+
*/
|
|
284
|
+
function completeCheckpoint(projectRoot, checkpointId, options = {}) {
|
|
285
|
+
const state = loadState(projectRoot);
|
|
286
|
+
|
|
287
|
+
if (!state) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const now = new Date().toISOString();
|
|
292
|
+
|
|
293
|
+
// Update checkpoint
|
|
294
|
+
if (!state.checkpoints[checkpointId]) {
|
|
295
|
+
state.checkpoints[checkpointId] = {};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
state.checkpoints[checkpointId] = {
|
|
299
|
+
completed: true,
|
|
300
|
+
completedAt: now,
|
|
301
|
+
completedBy: options.completedBy || 'manual'
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// Save state
|
|
305
|
+
saveState(projectRoot, state);
|
|
306
|
+
|
|
307
|
+
// Record to history
|
|
308
|
+
recordCheckpointHistory(projectRoot, {
|
|
309
|
+
checkpointId,
|
|
310
|
+
completedAt: now,
|
|
311
|
+
completedBy: options.completedBy || 'manual',
|
|
312
|
+
notes: options.notes || null
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
return state;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Record checkpoint completion to history
|
|
320
|
+
* @param {string} projectRoot - Project root directory
|
|
321
|
+
* @param {object} record - History record
|
|
322
|
+
*/
|
|
323
|
+
function recordCheckpointHistory(projectRoot, record) {
|
|
324
|
+
const historyFile = getHistoryFilePath(projectRoot);
|
|
325
|
+
let history = [];
|
|
326
|
+
|
|
327
|
+
if (fs.existsSync(historyFile)) {
|
|
328
|
+
try {
|
|
329
|
+
history = JSON.parse(fs.readFileSync(historyFile, 'utf-8'));
|
|
330
|
+
} catch {
|
|
331
|
+
history = [];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
history.push({
|
|
336
|
+
...record,
|
|
337
|
+
timestamp: new Date().toISOString()
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
fs.writeFileSync(historyFile, JSON.stringify(history, null, 2), 'utf-8');
|
|
342
|
+
} catch (error) {
|
|
343
|
+
utils.print.debug(`Error saving history: ${error.message}`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Get checkpoint history
|
|
349
|
+
* @param {string} projectRoot - Project root directory
|
|
350
|
+
* @returns {Array} History records
|
|
351
|
+
*/
|
|
352
|
+
function getCheckpointHistory(projectRoot) {
|
|
353
|
+
const historyFile = getHistoryFilePath(projectRoot);
|
|
354
|
+
|
|
355
|
+
if (!fs.existsSync(historyFile)) {
|
|
356
|
+
return [];
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
try {
|
|
360
|
+
return JSON.parse(fs.readFileSync(historyFile, 'utf-8'));
|
|
361
|
+
} catch {
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Get checkpoint progress summary
|
|
368
|
+
* @param {string} projectRoot - Project root directory
|
|
369
|
+
* @returns {object} Progress summary
|
|
370
|
+
*/
|
|
371
|
+
function getCheckpointProgress(projectRoot) {
|
|
372
|
+
const state = loadState(projectRoot);
|
|
373
|
+
|
|
374
|
+
if (!state) {
|
|
375
|
+
return {
|
|
376
|
+
total: 0,
|
|
377
|
+
completed: 0,
|
|
378
|
+
percentage: 0,
|
|
379
|
+
checkpoints: []
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const definitions = getCheckpointDefinitions(state.projectType);
|
|
384
|
+
const checkpoints = definitions.map(def => ({
|
|
385
|
+
...def,
|
|
386
|
+
completed: state.checkpoints[def.id]?.completed || false,
|
|
387
|
+
completedAt: state.checkpoints[def.id]?.completedAt || null
|
|
388
|
+
}));
|
|
389
|
+
|
|
390
|
+
const completed = checkpoints.filter(c => c.completed).length;
|
|
391
|
+
const total = checkpoints.length;
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
total,
|
|
395
|
+
completed,
|
|
396
|
+
percentage: total > 0 ? Math.round((completed / total) * 100) : 0,
|
|
397
|
+
checkpoints
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// ============================================================================
|
|
402
|
+
// Project Type Management
|
|
403
|
+
// ============================================================================
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Set project type
|
|
407
|
+
* @param {string} projectRoot - Project root directory
|
|
408
|
+
* @param {string} projectType - Project type
|
|
409
|
+
* @param {object} options - Options
|
|
410
|
+
* @returns {object|null} Updated state
|
|
411
|
+
*/
|
|
412
|
+
function setProjectType(projectRoot, projectType, options = {}) {
|
|
413
|
+
if (!Object.values(PROJECT_TYPES).includes(projectType)) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
let state = loadState(projectRoot);
|
|
418
|
+
|
|
419
|
+
if (!state) {
|
|
420
|
+
state = initState(projectRoot, { projectType, ...options });
|
|
421
|
+
return state;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// If changing type, reset checkpoints
|
|
425
|
+
if (state.projectType !== projectType) {
|
|
426
|
+
const definitions = getCheckpointDefinitions(projectType);
|
|
427
|
+
const oldCheckpoints = state.checkpoints;
|
|
428
|
+
state.checkpoints = {};
|
|
429
|
+
|
|
430
|
+
// Initialize new checkpoints
|
|
431
|
+
for (const checkpoint of definitions) {
|
|
432
|
+
// Preserve initialized and github_connected if they exist
|
|
433
|
+
if ((checkpoint.id === 'initialized' || checkpoint.id === 'github_connected') && oldCheckpoints[checkpoint.id]) {
|
|
434
|
+
state.checkpoints[checkpoint.id] = oldCheckpoints[checkpoint.id];
|
|
435
|
+
} else {
|
|
436
|
+
state.checkpoints[checkpoint.id] = {
|
|
437
|
+
completed: false,
|
|
438
|
+
completedAt: null,
|
|
439
|
+
completedBy: null
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
state.projectType = projectType;
|
|
446
|
+
|
|
447
|
+
if (options.autoTagged) {
|
|
448
|
+
state.autoTagged = true;
|
|
449
|
+
state.taggedBy = options.taggedBy || null;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
saveState(projectRoot, state);
|
|
453
|
+
return state;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Detect project type from files
|
|
458
|
+
* @param {string} projectRoot - Project root directory
|
|
459
|
+
* @returns {string} Detected project type
|
|
460
|
+
*/
|
|
461
|
+
function detectProjectType(projectRoot) {
|
|
462
|
+
const planningDir = getPlanningDir(projectRoot);
|
|
463
|
+
|
|
464
|
+
// Check for development indicators
|
|
465
|
+
const devIndicators = [
|
|
466
|
+
'TECHNICAL_SPEC.md', 'ARCHITECTURE.md', 'DATABASE_SCHEMA.md',
|
|
467
|
+
'API_CONTRACTS.md', 'TEST_PLAN.md'
|
|
468
|
+
];
|
|
469
|
+
const devCount = devIndicators.filter(f => fs.existsSync(path.join(planningDir, f))).length;
|
|
470
|
+
|
|
471
|
+
// Check for content indicators
|
|
472
|
+
const contentIndicators = [
|
|
473
|
+
'CONTENT_STRATEGY.md', 'EDITORIAL_CALENDAR.md', 'STYLE_GUIDE.md', 'SEO_PLAN.md'
|
|
474
|
+
];
|
|
475
|
+
const contentCount = contentIndicators.filter(f => fs.existsSync(path.join(planningDir, f))).length;
|
|
476
|
+
|
|
477
|
+
// Check for business indicators
|
|
478
|
+
const businessIndicators = [
|
|
479
|
+
'BUSINESS_PLAN.md', 'MARKET_ANALYSIS.md', 'COMPETITOR_ANALYSIS.md',
|
|
480
|
+
'FINANCIAL_MODEL.md', 'PITCH_DECK.md', 'INVESTOR_LIST.md'
|
|
481
|
+
];
|
|
482
|
+
const businessCount = businessIndicators.filter(f => fs.existsSync(path.join(planningDir, f))).length;
|
|
483
|
+
|
|
484
|
+
// Return type with most indicators
|
|
485
|
+
if (contentCount > devCount && contentCount > businessCount) {
|
|
486
|
+
return PROJECT_TYPES.CONTENT;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (businessCount > devCount && businessCount > contentCount) {
|
|
490
|
+
return PROJECT_TYPES.BUSINESS;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return PROJECT_TYPES.DEVELOPMENT;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// ============================================================================
|
|
497
|
+
// GitHub State Management
|
|
498
|
+
// ============================================================================
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Update GitHub state
|
|
502
|
+
* @param {string} projectRoot - Project root directory
|
|
503
|
+
* @param {object} githubData - GitHub data
|
|
504
|
+
* @returns {object|null} Updated state
|
|
505
|
+
*/
|
|
506
|
+
function updateGitHubState(projectRoot, githubData) {
|
|
507
|
+
const state = getOrCreateState(projectRoot);
|
|
508
|
+
|
|
509
|
+
state.github = {
|
|
510
|
+
...state.github,
|
|
511
|
+
...githubData,
|
|
512
|
+
lastSync: new Date().toISOString()
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// Mark github_connected checkpoint if connected
|
|
516
|
+
if (githubData.connected) {
|
|
517
|
+
if (!state.checkpoints.github_connected?.completed) {
|
|
518
|
+
state.checkpoints.github_connected = {
|
|
519
|
+
completed: true,
|
|
520
|
+
completedAt: new Date().toISOString(),
|
|
521
|
+
completedBy: 'github-sync'
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
recordCheckpointHistory(projectRoot, {
|
|
525
|
+
checkpointId: 'github_connected',
|
|
526
|
+
completedAt: state.checkpoints.github_connected.completedAt,
|
|
527
|
+
completedBy: 'github-sync',
|
|
528
|
+
notes: `Connected to ${githubData.repositoryUrl}`
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
saveState(projectRoot, state);
|
|
534
|
+
return state;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Clear GitHub connection
|
|
539
|
+
* @param {string} projectRoot - Project root directory
|
|
540
|
+
* @returns {object|null} Updated state
|
|
541
|
+
*/
|
|
542
|
+
function clearGitHubState(projectRoot) {
|
|
543
|
+
const state = loadState(projectRoot);
|
|
544
|
+
|
|
545
|
+
if (!state) {
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
state.github = {
|
|
550
|
+
connected: false,
|
|
551
|
+
repositoryUrl: null,
|
|
552
|
+
owner: null,
|
|
553
|
+
repo: null,
|
|
554
|
+
defaultBranch: null,
|
|
555
|
+
lastSync: null,
|
|
556
|
+
stats: {
|
|
557
|
+
totalCommits: 0,
|
|
558
|
+
openPRs: 0,
|
|
559
|
+
closedPRs: 0,
|
|
560
|
+
contributors: 0,
|
|
561
|
+
lastCommit: null,
|
|
562
|
+
lastCommitMessage: null
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
// Reset github_connected checkpoint
|
|
567
|
+
if (state.checkpoints.github_connected) {
|
|
568
|
+
state.checkpoints.github_connected = {
|
|
569
|
+
completed: false,
|
|
570
|
+
completedAt: null,
|
|
571
|
+
completedBy: null
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
saveState(projectRoot, state);
|
|
576
|
+
return state;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// ============================================================================
|
|
580
|
+
// Health Score Management
|
|
581
|
+
// ============================================================================
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Update health score
|
|
585
|
+
* @param {string} projectRoot - Project root directory
|
|
586
|
+
* @param {object} healthData - Health data
|
|
587
|
+
* @returns {object|null} Updated state
|
|
588
|
+
*/
|
|
589
|
+
function updateHealthScore(projectRoot, healthData) {
|
|
590
|
+
const state = getOrCreateState(projectRoot);
|
|
591
|
+
|
|
592
|
+
state.health = {
|
|
593
|
+
...healthData,
|
|
594
|
+
lastCalculated: new Date().toISOString()
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
saveState(projectRoot, state);
|
|
598
|
+
return state;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Calculate checkpoint health score
|
|
603
|
+
* @param {string} projectRoot - Project root directory
|
|
604
|
+
* @returns {number} Checkpoint score (0-100)
|
|
605
|
+
*/
|
|
606
|
+
function calculateCheckpointScore(projectRoot) {
|
|
607
|
+
const progress = getCheckpointProgress(projectRoot);
|
|
608
|
+
return progress.percentage;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// ============================================================================
|
|
612
|
+
// Usage Tracking
|
|
613
|
+
// ============================================================================
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Increment usage counter
|
|
617
|
+
* @param {string} projectRoot - Project root directory
|
|
618
|
+
* @param {string} metric - Metric to increment
|
|
619
|
+
* @param {number} count - Amount to increment (default 1)
|
|
620
|
+
* @returns {object|null} Updated state
|
|
621
|
+
*/
|
|
622
|
+
function incrementUsage(projectRoot, metric, count = 1) {
|
|
623
|
+
const state = getOrCreateState(projectRoot);
|
|
624
|
+
|
|
625
|
+
const currentPeriod = new Date().toISOString().slice(0, 7);
|
|
626
|
+
|
|
627
|
+
// Reset counters if new period
|
|
628
|
+
if (state.usage.currentPeriod !== currentPeriod) {
|
|
629
|
+
state.usage = {
|
|
630
|
+
currentPeriod,
|
|
631
|
+
contextGenerations: 0,
|
|
632
|
+
agentInvocations: 0,
|
|
633
|
+
skillSearches: 0,
|
|
634
|
+
mcpCalls: 0
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// Increment counter
|
|
639
|
+
if (state.usage[metric] !== undefined) {
|
|
640
|
+
state.usage[metric] += count;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
saveState(projectRoot, state);
|
|
644
|
+
return state;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// ============================================================================
|
|
648
|
+
// Exports
|
|
649
|
+
// ============================================================================
|
|
650
|
+
|
|
651
|
+
module.exports = {
|
|
652
|
+
// Constants
|
|
653
|
+
VERSION,
|
|
654
|
+
PROJECT_TYPES,
|
|
655
|
+
CHECKPOINTS,
|
|
656
|
+
PLANNING_DIR,
|
|
657
|
+
STATE_FILE,
|
|
658
|
+
HISTORY_FILE,
|
|
659
|
+
|
|
660
|
+
// Paths
|
|
661
|
+
getPlanningDir,
|
|
662
|
+
getStateFilePath,
|
|
663
|
+
getHistoryFilePath,
|
|
664
|
+
getGitHubSyncFilePath,
|
|
665
|
+
|
|
666
|
+
// State management
|
|
667
|
+
createDefaultState,
|
|
668
|
+
loadState,
|
|
669
|
+
saveState,
|
|
670
|
+
initState,
|
|
671
|
+
getOrCreateState,
|
|
672
|
+
|
|
673
|
+
// Checkpoint management
|
|
674
|
+
getCheckpointDefinitions,
|
|
675
|
+
completeCheckpoint,
|
|
676
|
+
recordCheckpointHistory,
|
|
677
|
+
getCheckpointHistory,
|
|
678
|
+
getCheckpointProgress,
|
|
679
|
+
|
|
680
|
+
// Project type
|
|
681
|
+
setProjectType,
|
|
682
|
+
detectProjectType,
|
|
683
|
+
|
|
684
|
+
// GitHub
|
|
685
|
+
updateGitHubState,
|
|
686
|
+
clearGitHubState,
|
|
687
|
+
|
|
688
|
+
// Health
|
|
689
|
+
updateHealthScore,
|
|
690
|
+
calculateCheckpointScore,
|
|
691
|
+
|
|
692
|
+
// Usage
|
|
693
|
+
incrementUsage
|
|
694
|
+
};
|