@vibecheckai/cli 3.9.0 → 3.9.1

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.
@@ -97,6 +97,22 @@ const sym = {
97
97
  arrow: "→",
98
98
  arrowRight: "▸",
99
99
  star: "★",
100
+ // Aliases for compatibility
101
+ success: "✓",
102
+ error: "✗",
103
+ // Additional symbols
104
+ key: "🔑",
105
+ list: "📋",
106
+ badge: "🏅",
107
+ timer: "⏱️",
108
+ add: "+",
109
+ remove: "-",
110
+ person: "👤",
111
+ expired: "⌛",
112
+ review: "👁️",
113
+ pending: "○",
114
+ active: "●",
115
+ skip: "↷",
100
116
  // Progress
101
117
  blockFull: "█",
102
118
  blockMed: "▓",
@@ -0,0 +1,353 @@
1
+ /**
2
+ * vibecheck ci - Enterprise CI Wiring
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * One-command enterprise CI wiring for GitHub Actions
6
+ * ═══════════════════════════════════════════════════════════════════════════════
7
+ *
8
+ * Auto-detects your stack (Next.js, Fastify, etc.), package manager, Node version,
9
+ * and creates optimized workflows with SARIF output, PR comments, and status checks.
10
+ */
11
+
12
+ "use strict";
13
+
14
+ const fs = require("fs");
15
+ const path = require("path");
16
+ const { parseGlobalFlags, shouldSuppressOutput, isJsonMode } = require("./lib/global-flags");
17
+ const { EXIT } = require("./lib/exit-codes");
18
+
19
+ // ═══════════════════════════════════════════════════════════════════════════════
20
+ // CLI OUTPUT
21
+ // ═══════════════════════════════════════════════════════════════════════════════
22
+
23
+ let _cliOutput = null;
24
+ function getCliOutput() {
25
+ if (!_cliOutput) _cliOutput = require("./lib/unified-cli-output");
26
+ return _cliOutput;
27
+ }
28
+
29
+ // ═══════════════════════════════════════════════════════════════════════════════
30
+ // DETECTION UTILITIES
31
+ // ═══════════════════════════════════════════════════════════════════════════════
32
+
33
+ function detectPackageManager(projectPath) {
34
+ if (fs.existsSync(path.join(projectPath, "pnpm-lock.yaml"))) return "pnpm";
35
+ if (fs.existsSync(path.join(projectPath, "yarn.lock"))) return "yarn";
36
+ if (fs.existsSync(path.join(projectPath, "bun.lockb"))) return "bun";
37
+ return "npm";
38
+ }
39
+
40
+ function detectNodeVersion(projectPath) {
41
+ // Try .nvmrc
42
+ const nvmrcPath = path.join(projectPath, ".nvmrc");
43
+ if (fs.existsSync(nvmrcPath)) {
44
+ const version = fs.readFileSync(nvmrcPath, "utf-8").trim();
45
+ return version.replace(/^v/, "");
46
+ }
47
+
48
+ // Try package.json engines
49
+ const pkgPath = path.join(projectPath, "package.json");
50
+ if (fs.existsSync(pkgPath)) {
51
+ try {
52
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
53
+ if (pkg.engines?.node) {
54
+ const match = pkg.engines.node.match(/(\d+)/);
55
+ if (match) return match[1];
56
+ }
57
+ } catch {}
58
+ }
59
+
60
+ return "20"; // Default to LTS
61
+ }
62
+
63
+ function detectFramework(projectPath) {
64
+ const pkgPath = path.join(projectPath, "package.json");
65
+ if (!fs.existsSync(pkgPath)) return "unknown";
66
+
67
+ try {
68
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
69
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
70
+
71
+ if (deps["next"]) return "nextjs";
72
+ if (deps["@fastify/core"] || deps["fastify"]) return "fastify";
73
+ if (deps["express"]) return "express";
74
+ if (deps["@nestjs/core"]) return "nestjs";
75
+ if (deps["vite"]) return "vite";
76
+ if (deps["nuxt"]) return "nuxt";
77
+ if (deps["remix"]) return "remix";
78
+ if (deps["@sveltejs/kit"]) return "sveltekit";
79
+
80
+ return "generic";
81
+ } catch {
82
+ return "unknown";
83
+ }
84
+ }
85
+
86
+ // ═══════════════════════════════════════════════════════════════════════════════
87
+ // WORKFLOW GENERATION
88
+ // ═══════════════════════════════════════════════════════════════════════════════
89
+
90
+ function generateGitHubActionsWorkflow(options) {
91
+ const { packageManager, nodeVersion, framework, full } = options;
92
+
93
+ const installCmd = {
94
+ npm: "npm ci",
95
+ yarn: "yarn install --frozen-lockfile",
96
+ pnpm: "pnpm install --frozen-lockfile",
97
+ bun: "bun install --frozen-lockfile",
98
+ }[packageManager];
99
+
100
+ const workflow = `# Generated by vibecheck ci
101
+ # https://docs.vibecheckai.dev/ci
102
+
103
+ name: VibeCheck
104
+
105
+ on:
106
+ push:
107
+ branches: [main, master, develop]
108
+ pull_request:
109
+ branches: [main, master, develop]
110
+ workflow_dispatch:
111
+
112
+ permissions:
113
+ contents: read
114
+ security-events: write
115
+ pull-requests: write
116
+
117
+ jobs:
118
+ vibecheck:
119
+ name: Security & Quality Scan
120
+ runs-on: ubuntu-latest
121
+ steps:
122
+ - name: Checkout
123
+ uses: actions/checkout@v4
124
+ with:
125
+ fetch-depth: 0
126
+
127
+ - name: Setup Node.js
128
+ uses: actions/setup-node@v4
129
+ with:
130
+ node-version: '${nodeVersion}'
131
+ cache: '${packageManager === "yarn" ? "yarn" : packageManager === "pnpm" ? "pnpm" : "npm"}'
132
+
133
+ - name: Install dependencies
134
+ run: ${installCmd}
135
+
136
+ - name: Install VibeCheck CLI
137
+ run: npm install -g @vibecheckai/cli
138
+
139
+ - name: Run VibeCheck Audit
140
+ id: audit
141
+ run: vibecheck audit --ci --sarif --output vibecheck-results.sarif
142
+ env:
143
+ VIBECHECK_API_KEY: \${{ secrets.VIBECHECK_API_KEY }}
144
+ continue-on-error: true
145
+
146
+ - name: Upload SARIF to GitHub
147
+ if: always()
148
+ uses: github/codeql-action/upload-sarif@v3
149
+ with:
150
+ sarif_file: vibecheck-results.sarif
151
+ category: vibecheck
152
+
153
+ - name: Run VibeCheck Ship
154
+ if: github.event_name == 'pull_request'
155
+ run: vibecheck ship --ci --json > ship-verdict.json
156
+ env:
157
+ VIBECHECK_API_KEY: \${{ secrets.VIBECHECK_API_KEY }}
158
+ continue-on-error: true
159
+
160
+ - name: Comment PR with Verdict
161
+ if: github.event_name == 'pull_request'
162
+ uses: actions/github-script@v7
163
+ with:
164
+ script: |
165
+ const fs = require('fs');
166
+ let verdict = { verdict: 'UNKNOWN', score: 0 };
167
+ try {
168
+ verdict = JSON.parse(fs.readFileSync('ship-verdict.json', 'utf8'));
169
+ } catch (e) {}
170
+
171
+ const emoji = verdict.verdict === 'SHIP' ? '🚀' : verdict.verdict === 'WARN' ? '⚠️' : '🚫';
172
+ const body = \`## \${emoji} VibeCheck Verdict: **\${verdict.verdict}**
173
+
174
+ Score: \${verdict.score}/100
175
+
176
+ [View full report](https://vibecheckai.dev/reports/\${context.repo.owner}/\${context.repo.repo}/\${context.sha})
177
+ \`;
178
+
179
+ github.rest.issues.createComment({
180
+ issue_number: context.issue.number,
181
+ owner: context.repo.owner,
182
+ repo: context.repo.repo,
183
+ body: body
184
+ });
185
+ `;
186
+
187
+ return workflow;
188
+ }
189
+
190
+ // ═══════════════════════════════════════════════════════════════════════════════
191
+ // HELP
192
+ // ═══════════════════════════════════════════════════════════════════════════════
193
+
194
+ function printHelp() {
195
+ const { ansi, sym, renderMinimalHeader } = getCliOutput();
196
+
197
+ renderMinimalHeader("ci", "free");
198
+
199
+ console.log(`${ansi.bold}vibecheck ci${ansi.reset} - One-command enterprise CI wiring
200
+
201
+ ${ansi.bold}USAGE${ansi.reset}
202
+ vibecheck ci [options]
203
+
204
+ ${ansi.bold}OPTIONS${ansi.reset}
205
+ ${ansi.cyan}--dry-run${ansi.reset} Preview without creating files
206
+ ${ansi.cyan}--full${ansi.reset} Create all workflows (audit, ship, e2e, security)
207
+ ${ansi.cyan}--validate${ansi.reset} Validate existing workflows
208
+ ${ansi.cyan}--provider${ansi.reset} CI provider: github, gitlab, azure (default: github)
209
+ ${ansi.cyan}--json${ansi.reset} Output as JSON
210
+ ${ansi.cyan}--path <dir>${ansi.reset} Project path
211
+
212
+ ${ansi.bold}EXAMPLES${ansi.reset}
213
+ ${ansi.dim}# Auto-detect and create CI workflows${ansi.reset}
214
+ vibecheck ci
215
+
216
+ ${ansi.dim}# Preview without creating files${ansi.reset}
217
+ vibecheck ci --dry-run
218
+
219
+ ${ansi.dim}# Create all workflows${ansi.reset}
220
+ vibecheck ci --full
221
+
222
+ ${ansi.dim}# Validate existing workflows${ansi.reset}
223
+ vibecheck ci --validate
224
+ `);
225
+ }
226
+
227
+ // ═══════════════════════════════════════════════════════════════════════════════
228
+ // MAIN RUNNER
229
+ // ═══════════════════════════════════════════════════════════════════════════════
230
+
231
+ async function runCI(args = [], context = {}) {
232
+ const { flags, cleanArgs } = parseGlobalFlags(args);
233
+ const quiet = shouldSuppressOutput(flags);
234
+ const json = isJsonMode(flags);
235
+ const projectPath = flags.path || context.repoRoot || process.cwd();
236
+
237
+ // Handle help
238
+ if (flags.help) {
239
+ printHelp();
240
+ return EXIT.SUCCESS;
241
+ }
242
+
243
+ const dryRun = args.includes("--dry-run");
244
+ const full = args.includes("--full");
245
+ const validate = args.includes("--validate");
246
+ const provider = args.find(a => a.startsWith("--provider="))?.split("=")[1] || "github";
247
+
248
+ const { ansi, sym, renderMinimalHeader, renderSuccess, renderWarning, renderError } = getCliOutput();
249
+
250
+ if (!quiet && !json) {
251
+ renderMinimalHeader("ci", "free");
252
+ }
253
+
254
+ try {
255
+ // Detect project settings
256
+ const packageManager = detectPackageManager(projectPath);
257
+ const nodeVersion = detectNodeVersion(projectPath);
258
+ const framework = detectFramework(projectPath);
259
+
260
+ if (!quiet && !json) {
261
+ console.log(` ${ansi.dim}Detected:${ansi.reset}`);
262
+ console.log(` Package Manager: ${ansi.cyan}${packageManager}${ansi.reset}`);
263
+ console.log(` Node Version: ${ansi.cyan}${nodeVersion}${ansi.reset}`);
264
+ console.log(` Framework: ${ansi.cyan}${framework}${ansi.reset}`);
265
+ console.log();
266
+ }
267
+
268
+ // Handle validate
269
+ if (validate) {
270
+ const workflowDir = path.join(projectPath, ".github", "workflows");
271
+ const vibecheckWorkflow = path.join(workflowDir, "vibecheck.yml");
272
+
273
+ if (fs.existsSync(vibecheckWorkflow)) {
274
+ renderSuccess("vibecheck.yml workflow exists");
275
+ return EXIT.SUCCESS;
276
+ } else {
277
+ renderWarning("vibecheck.yml workflow not found");
278
+ console.log(` ${ansi.dim}Run 'vibecheck ci' to create it${ansi.reset}\n`);
279
+ return EXIT.NOT_FOUND || 4;
280
+ }
281
+ }
282
+
283
+ // Generate workflow
284
+ const workflow = generateGitHubActionsWorkflow({
285
+ packageManager,
286
+ nodeVersion,
287
+ framework,
288
+ full,
289
+ });
290
+
291
+ if (dryRun) {
292
+ if (json) {
293
+ console.log(JSON.stringify({
294
+ success: true,
295
+ dryRun: true,
296
+ provider,
297
+ detection: { packageManager, nodeVersion, framework },
298
+ workflow: workflow,
299
+ }, null, 2));
300
+ } else {
301
+ console.log(` ${ansi.yellow}${sym.warning}${ansi.reset} ${ansi.bold}DRY RUN${ansi.reset} - No files created\n`);
302
+ console.log(` ${ansi.dim}Would create:${ansi.reset} .github/workflows/vibecheck.yml\n`);
303
+ console.log(` ${ansi.dim}Preview:${ansi.reset}`);
304
+ console.log(` ${ansi.dim}${"─".repeat(60)}${ansi.reset}`);
305
+ workflow.split("\n").slice(0, 20).forEach(line => {
306
+ console.log(` ${ansi.dim}${line}${ansi.reset}`);
307
+ });
308
+ console.log(` ${ansi.dim}... (${workflow.split("\n").length - 20} more lines)${ansi.reset}`);
309
+ console.log(` ${ansi.dim}${"─".repeat(60)}${ansi.reset}\n`);
310
+ }
311
+ return EXIT.SUCCESS;
312
+ }
313
+
314
+ // Create workflow file
315
+ const workflowDir = path.join(projectPath, ".github", "workflows");
316
+ const workflowPath = path.join(workflowDir, "vibecheck.yml");
317
+
318
+ // Create directories
319
+ fs.mkdirSync(workflowDir, { recursive: true });
320
+
321
+ // Write workflow
322
+ fs.writeFileSync(workflowPath, workflow, "utf-8");
323
+
324
+ if (json) {
325
+ console.log(JSON.stringify({
326
+ success: true,
327
+ created: [workflowPath],
328
+ provider,
329
+ detection: { packageManager, nodeVersion, framework },
330
+ }, null, 2));
331
+ } else {
332
+ renderSuccess(`Created .github/workflows/vibecheck.yml`);
333
+ console.log();
334
+ console.log(` ${ansi.bold}Next steps:${ansi.reset}`);
335
+ console.log(` 1. Add ${ansi.cyan}VIBECHECK_API_KEY${ansi.reset} to your repository secrets`);
336
+ console.log(` 2. Push to trigger the workflow`);
337
+ console.log();
338
+ console.log(` ${ansi.dim}Get your API key: https://vibecheckai.dev/settings/keys${ansi.reset}\n`);
339
+ }
340
+
341
+ return EXIT.SUCCESS;
342
+
343
+ } catch (error) {
344
+ if (json) {
345
+ console.log(JSON.stringify({ success: false, error: error.message }));
346
+ } else {
347
+ renderError(`CI setup failed: ${error.message}`);
348
+ }
349
+ return EXIT.INTERNAL_ERROR || 10;
350
+ }
351
+ }
352
+
353
+ module.exports = { runCI };
@@ -34,8 +34,8 @@ const {
34
34
  AUTO_REASONS,
35
35
  } = require("./lib/checkpoint");
36
36
 
37
- const { parseGlobalFlags, shouldSuppressOutput, isJsonMode } = require("./lib/parse-args");
38
- const { renderTable, renderBox, colors, symbols } = require("./lib/renderers");
37
+ const { parseGlobalFlags, shouldSuppressOutput, isJsonMode } = require("./lib/global-flags");
38
+ const { c: colors, sym: symbols, box: renderBox, table: renderTable } = require("./lib/ui");
39
39
 
40
40
  // ═══════════════════════════════════════════════════════════════════════════════
41
41
  // EXIT CODES
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibecheckai/cli",
3
- "version": "3.9.0",
3
+ "version": "3.9.1",
4
4
  "description": "Vibecheck CLI - Ship with confidence. One verdict: SHIP | WARN | BLOCK.",
5
5
  "main": "bin/vibecheck.js",
6
6
  "bin": {