@girardmedia/bootspring 2.1.3 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/bootspring.js +102 -82
- package/generators/api-docs.js +3 -3
- package/generators/decisions.js +14 -14
- package/generators/health.js +6 -6
- package/generators/sprint.js +4 -4
- package/generators/templates/build-planning.template.js +2 -2
- package/generators/visual-doc-generator.js +1 -1
- package/package.json +2 -15
- package/cli/agent.js +0 -799
- package/cli/auth.js +0 -896
- package/cli/billing.js +0 -320
- package/cli/build.js +0 -1442
- package/cli/dashboard.js +0 -123
- package/cli/init.js +0 -669
- package/cli/mcp.js +0 -240
- package/cli/orchestrator.js +0 -240
- package/cli/project.js +0 -825
- package/cli/quality.js +0 -281
- package/cli/skill.js +0 -503
- package/cli/switch.js +0 -453
- package/cli/todo.js +0 -629
- package/cli/update.js +0 -132
package/cli/quality.js
DELETED
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bootspring Quality Command
|
|
3
|
-
* Thin client for hosted quality gates.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
|
|
9
|
-
const api = require('../core/api-client');
|
|
10
|
-
const auth = require('../core/auth');
|
|
11
|
-
const config = require('../core/config');
|
|
12
|
-
const utils = require('../core/utils');
|
|
13
|
-
|
|
14
|
-
const C = utils.COLORS;
|
|
15
|
-
|
|
16
|
-
const GATES = {
|
|
17
|
-
'pre-commit': {
|
|
18
|
-
name: 'Pre-Commit',
|
|
19
|
-
description: 'Quick checks before committing code',
|
|
20
|
-
checks: [
|
|
21
|
-
{ id: 'typecheck', name: 'TypeScript', command: 'npx tsc --noEmit', description: 'Type checking', requires: 'hasTypeScript' },
|
|
22
|
-
{ id: 'lint', name: 'ESLint', command: 'npx eslint .', description: 'Linting', requires: 'hasEslint' },
|
|
23
|
-
{ id: 'format', name: 'Prettier', command: 'npx prettier --check .', description: 'Format checking', requires: 'hasPrettier' }
|
|
24
|
-
]
|
|
25
|
-
},
|
|
26
|
-
'pre-push': {
|
|
27
|
-
name: 'Pre-Push',
|
|
28
|
-
description: 'Thorough checks before pushing to remote',
|
|
29
|
-
checks: [
|
|
30
|
-
{ id: 'typecheck', name: 'TypeScript', command: 'npx tsc --noEmit', description: 'Type checking', requires: 'hasTypeScript' },
|
|
31
|
-
{ id: 'lint', name: 'ESLint', command: 'npx eslint .', description: 'Linting', requires: 'hasEslint' },
|
|
32
|
-
{ id: 'test', name: 'Unit Tests', command: 'npm test', description: 'Running tests' },
|
|
33
|
-
{ id: 'e2e', name: 'E2E Tests', command: 'npx playwright test', description: 'Running Playwright tests', requires: 'hasPlaywright' },
|
|
34
|
-
{ id: 'build', name: 'Build', command: 'npm run build', description: 'Production build', requires: 'hasBuildScript' }
|
|
35
|
-
]
|
|
36
|
-
},
|
|
37
|
-
'pre-deploy': {
|
|
38
|
-
name: 'Pre-Deploy',
|
|
39
|
-
description: 'Full audit before deployment',
|
|
40
|
-
checks: [
|
|
41
|
-
{ id: 'typecheck', name: 'TypeScript', command: 'npx tsc --noEmit', description: 'Type checking', requires: 'hasTypeScript' },
|
|
42
|
-
{ id: 'lint', name: 'ESLint', command: 'npx eslint .', description: 'Linting', requires: 'hasEslint' },
|
|
43
|
-
{ id: 'test', name: 'Unit Tests', command: 'npm test', description: 'Running tests' },
|
|
44
|
-
{ id: 'e2e', name: 'E2E Tests', command: 'npx playwright test', description: 'Running Playwright tests', requires: 'hasPlaywright' },
|
|
45
|
-
{ id: 'build', name: 'Build', command: 'npm run build', description: 'Production build', requires: 'hasBuildScript' },
|
|
46
|
-
{ id: 'security', name: 'Security Audit', command: 'npm audit --audit-level=high', description: 'Checking for vulnerabilities' }
|
|
47
|
-
]
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const GATE_CONFIG_KEYS = {
|
|
52
|
-
'pre-commit': 'preCommit',
|
|
53
|
-
'pre-push': 'prePush',
|
|
54
|
-
'pre-deploy': 'preDeploy'
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
function detectProjectCapabilities(projectRoot) {
|
|
58
|
-
const capabilities = {
|
|
59
|
-
hasTypeScript: false,
|
|
60
|
-
hasBuildScript: false,
|
|
61
|
-
hasPrettier: false,
|
|
62
|
-
hasEslint: false,
|
|
63
|
-
hasPlaywright: false,
|
|
64
|
-
projectType: 'javascript'
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
if (fs.existsSync(path.join(projectRoot, 'tsconfig.json'))) {
|
|
68
|
-
capabilities.hasTypeScript = true;
|
|
69
|
-
capabilities.projectType = 'typescript';
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
73
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
74
|
-
try {
|
|
75
|
-
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
76
|
-
const allDeps = {
|
|
77
|
-
...(pkg.dependencies || {}),
|
|
78
|
-
...(pkg.devDependencies || {})
|
|
79
|
-
};
|
|
80
|
-
const scripts = pkg.scripts || {};
|
|
81
|
-
|
|
82
|
-
if (allDeps.typescript) {
|
|
83
|
-
capabilities.hasTypeScript = true;
|
|
84
|
-
capabilities.projectType = 'typescript';
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
capabilities.hasBuildScript = Boolean(scripts.build);
|
|
88
|
-
capabilities.hasPrettier = Boolean(allDeps.prettier);
|
|
89
|
-
capabilities.hasEslint = Boolean(allDeps.eslint);
|
|
90
|
-
capabilities.hasPlaywright = Boolean(allDeps['@playwright/test'] || allDeps.playwright);
|
|
91
|
-
} catch {
|
|
92
|
-
// Best-effort capability detection only.
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
for (const file of ['playwright.config.js', 'playwright.config.ts']) {
|
|
97
|
-
if (fs.existsSync(path.join(projectRoot, file))) {
|
|
98
|
-
capabilities.hasPlaywright = true;
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
for (const file of ['.prettierrc', '.prettierrc.json', '.prettierrc.js', 'prettier.config.js']) {
|
|
104
|
-
if (fs.existsSync(path.join(projectRoot, file))) {
|
|
105
|
-
capabilities.hasPrettier = true;
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
for (const file of ['.eslintrc', '.eslintrc.json', '.eslintrc.js', 'eslint.config.js']) {
|
|
111
|
-
if (fs.existsSync(path.join(projectRoot, file))) {
|
|
112
|
-
capabilities.hasEslint = true;
|
|
113
|
-
break;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return capabilities;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function readPackageScripts(projectRoot) {
|
|
121
|
-
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
122
|
-
if (!fs.existsSync(packageJsonPath)) {
|
|
123
|
-
return {};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
128
|
-
return pkg.scripts || {};
|
|
129
|
-
} catch {
|
|
130
|
-
return {};
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function getConfiguredChecksForGate(gateName, qualityConfig = {}) {
|
|
135
|
-
const configKey = GATE_CONFIG_KEYS[gateName];
|
|
136
|
-
const gateConfig = configKey ? qualityConfig[configKey] : undefined;
|
|
137
|
-
|
|
138
|
-
if (!gateConfig || typeof gateConfig !== 'object' || !Array.isArray(gateConfig.checks)) {
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return gateConfig.checks.map((item) => String(item).toLowerCase());
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function filterChecksByConfig(gateName, checks, qualityConfig = {}) {
|
|
146
|
-
const configuredChecks = getConfiguredChecksForGate(gateName, qualityConfig);
|
|
147
|
-
if (!configuredChecks || configuredChecks.length === 0) {
|
|
148
|
-
return checks;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return checks.filter((check) => configuredChecks.includes(check.id));
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function resolveCheckCommand(check, scripts = {}) {
|
|
155
|
-
if (check.id === 'typecheck' && scripts['typecheck:active']) return 'npm run typecheck:active';
|
|
156
|
-
if (check.id === 'typecheck' && scripts.typecheck) return 'npm run typecheck';
|
|
157
|
-
if (check.id === 'lint' && scripts.lint) return 'npm run lint';
|
|
158
|
-
if (check.id === 'format' && scripts['format:check']) return 'npm run format:check';
|
|
159
|
-
if (check.id === 'test' && scripts.test) return 'npm test';
|
|
160
|
-
if (check.id === 'build' && scripts.build) return 'npm run build';
|
|
161
|
-
return check.command;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function shouldSkipCheck(check, skipList = []) {
|
|
165
|
-
const normalizedSkip = skipList.map((item) => String(item).toLowerCase());
|
|
166
|
-
return normalizedSkip.includes(check.id) || normalizedSkip.includes(check.name.toLowerCase());
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function buildProjectContext() {
|
|
170
|
-
const cfg = config.load();
|
|
171
|
-
return {
|
|
172
|
-
name: cfg.project?.name,
|
|
173
|
-
framework: cfg.stack?.framework,
|
|
174
|
-
language: cfg.stack?.language,
|
|
175
|
-
testRunner: cfg.plugins?.testing?.provider,
|
|
176
|
-
cwd: process.cwd()
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function showAuthMessage() {
|
|
181
|
-
console.log(`${C.yellow}Not logged in${C.reset}`);
|
|
182
|
-
console.log(`${C.dim}Run 'bootspring auth login' first${C.reset}\n`);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async function listGates() {
|
|
186
|
-
console.log(`\n${C.cyan}${C.bold}Hosted Quality Gates${C.reset}\n`);
|
|
187
|
-
const response = await api.listQualityGates();
|
|
188
|
-
const gates = response.gates || [];
|
|
189
|
-
|
|
190
|
-
for (const gate of gates) {
|
|
191
|
-
const status = gate.accessible ? '' : ` ${C.yellow}(Upgrade Required)${C.reset}`;
|
|
192
|
-
console.log(` ${C.cyan}${gate.id}${C.reset}${status}`);
|
|
193
|
-
console.log(` ${C.dim}${gate.description}${C.reset}\n`);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
async function runGate(gateId) {
|
|
198
|
-
if (!gateId) {
|
|
199
|
-
await listGates();
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
console.log(`\n${C.cyan}${C.bold}Quality Gate: ${gateId}${C.reset}\n`);
|
|
204
|
-
const response = await api.runQualityGate(gateId, buildProjectContext(), {});
|
|
205
|
-
const checks = response.checks || [];
|
|
206
|
-
|
|
207
|
-
console.log(`${response.message || 'Quality gate prepared.'}`);
|
|
208
|
-
console.log(`${C.dim}${response.instructions || 'Run the returned checks locally.'}${C.reset}\n`);
|
|
209
|
-
|
|
210
|
-
if (checks.length === 0) {
|
|
211
|
-
console.log(`${C.dim}No checks returned for this gate.${C.reset}\n`);
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
console.log(`${C.bold}Checks${C.reset}`);
|
|
216
|
-
checks.forEach((check) => {
|
|
217
|
-
console.log(` ${C.cyan}${check.name}${C.reset}`);
|
|
218
|
-
if (check.config?.description) {
|
|
219
|
-
console.log(` ${C.dim}${check.config.description}${C.reset}`);
|
|
220
|
-
}
|
|
221
|
-
if (check.config?.command) {
|
|
222
|
-
console.log(` ${C.dim}${check.config.command}${C.reset}`);
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
console.log();
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function showHelp() {
|
|
230
|
-
console.log(`
|
|
231
|
-
${C.cyan}${C.bold}Bootspring Quality${C.reset}
|
|
232
|
-
|
|
233
|
-
${C.bold}Usage:${C.reset}
|
|
234
|
-
bootspring quality <gate>
|
|
235
|
-
|
|
236
|
-
${C.bold}Examples:${C.reset}
|
|
237
|
-
bootspring quality pre-commit
|
|
238
|
-
bootspring quality pre-push
|
|
239
|
-
bootspring quality pre-deploy
|
|
240
|
-
`);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
async function run(args) {
|
|
244
|
-
const subcommand = args[0];
|
|
245
|
-
|
|
246
|
-
if (subcommand === 'help' || subcommand === '-h' || subcommand === '--help') {
|
|
247
|
-
showHelp();
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (!auth.isAuthenticated()) {
|
|
252
|
-
showAuthMessage();
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
if (!subcommand || subcommand === 'list') {
|
|
258
|
-
await listGates();
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
await runGate(subcommand);
|
|
263
|
-
} catch (error) {
|
|
264
|
-
if (error.status === 403) {
|
|
265
|
-
console.log(`${C.yellow}This quality gate requires a paid Bootspring plan.${C.reset}`);
|
|
266
|
-
console.log(`${C.dim}Upgrade: https://bootspring.com/pricing${C.reset}\n`);
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
throw error;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
module.exports = {
|
|
275
|
-
GATES,
|
|
276
|
-
detectProjectCapabilities,
|
|
277
|
-
filterChecksByConfig,
|
|
278
|
-
resolveCheckCommand,
|
|
279
|
-
shouldSkipCheck,
|
|
280
|
-
run
|
|
281
|
-
};
|