@vibecheckai/cli 3.9.0 → 4.0.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.
Files changed (88) hide show
  1. package/README.md +1 -1
  2. package/bin/runners/context/generators/cursor-enhanced.js +99 -13
  3. package/bin/runners/lib/unified-cli-output.js +16 -0
  4. package/bin/runners/runCI.js +353 -0
  5. package/bin/runners/runCheckpoint.js +2 -2
  6. package/mcp-server/.eslintrc.json +24 -0
  7. package/mcp-server/README.md +425 -135
  8. package/mcp-server/SPEC.md +583 -0
  9. package/mcp-server/configs/README.md +172 -0
  10. package/mcp-server/configs/claude-desktop-pro.json +31 -0
  11. package/mcp-server/configs/claude-desktop-with-workspace.json +25 -0
  12. package/mcp-server/configs/claude-desktop.json +19 -0
  13. package/mcp-server/configs/cursor-mcp.json +21 -0
  14. package/mcp-server/configs/windsurf-mcp.json +17 -0
  15. package/mcp-server/mcp-config.example.json +9 -0
  16. package/mcp-server/package.json +49 -34
  17. package/mcp-server/src/cli.ts +185 -0
  18. package/mcp-server/src/index.ts +85 -0
  19. package/mcp-server/src/server.ts +1933 -0
  20. package/mcp-server/src/services/cache-service.ts +466 -0
  21. package/mcp-server/src/services/cli-service.ts +345 -0
  22. package/mcp-server/src/services/context-manager.ts +717 -0
  23. package/mcp-server/src/services/firewall-service.ts +662 -0
  24. package/mcp-server/src/services/git-service.ts +671 -0
  25. package/mcp-server/src/services/index.ts +52 -0
  26. package/mcp-server/src/services/prompt-builder-service.ts +1031 -0
  27. package/mcp-server/src/services/session-service.ts +550 -0
  28. package/mcp-server/src/services/tier-service.ts +470 -0
  29. package/mcp-server/src/types.ts +351 -0
  30. package/mcp-server/tsconfig.json +16 -27
  31. package/package.json +6 -6
  32. package/mcp-server/.guardrail/audit/audit.log.jsonl +0 -2
  33. package/mcp-server/.specs/architecture.mdc +0 -90
  34. package/mcp-server/.specs/security.mdc +0 -30
  35. package/mcp-server/HARDENING_SUMMARY.md +0 -299
  36. package/mcp-server/agent-checkpoint.js +0 -364
  37. package/mcp-server/agent-firewall-interceptor.js +0 -500
  38. package/mcp-server/architect-tools.js +0 -707
  39. package/mcp-server/audit-mcp.js +0 -206
  40. package/mcp-server/authority-tools.js +0 -569
  41. package/mcp-server/codebase-architect-tools.js +0 -838
  42. package/mcp-server/conductor/conflict-resolver.js +0 -588
  43. package/mcp-server/conductor/execution-planner.js +0 -544
  44. package/mcp-server/conductor/index.js +0 -377
  45. package/mcp-server/conductor/lock-manager.js +0 -615
  46. package/mcp-server/conductor/request-queue.js +0 -550
  47. package/mcp-server/conductor/session-manager.js +0 -500
  48. package/mcp-server/conductor/tools.js +0 -510
  49. package/mcp-server/consolidated-tools.js +0 -1170
  50. package/mcp-server/deprecation-middleware.js +0 -282
  51. package/mcp-server/handlers/index.ts +0 -15
  52. package/mcp-server/handlers/tool-handler.ts +0 -593
  53. package/mcp-server/hygiene-tools.js +0 -428
  54. package/mcp-server/index-v1.js +0 -698
  55. package/mcp-server/index.js +0 -2940
  56. package/mcp-server/intelligence-tools.js +0 -664
  57. package/mcp-server/intent-drift-tools.js +0 -873
  58. package/mcp-server/intent-firewall-interceptor.js +0 -529
  59. package/mcp-server/lib/api-client.cjs +0 -13
  60. package/mcp-server/lib/cache-wrapper.cjs +0 -383
  61. package/mcp-server/lib/error-envelope.js +0 -138
  62. package/mcp-server/lib/executor.ts +0 -499
  63. package/mcp-server/lib/index.ts +0 -29
  64. package/mcp-server/lib/logger.cjs +0 -30
  65. package/mcp-server/lib/rate-limiter.js +0 -166
  66. package/mcp-server/lib/sandbox.test.ts +0 -519
  67. package/mcp-server/lib/sandbox.ts +0 -395
  68. package/mcp-server/lib/types.ts +0 -267
  69. package/mcp-server/logger.js +0 -173
  70. package/mcp-server/manifest.json +0 -473
  71. package/mcp-server/mdc-generator.js +0 -298
  72. package/mcp-server/premium-tools.js +0 -1275
  73. package/mcp-server/proof-tools.js +0 -571
  74. package/mcp-server/registry/tool-registry.js +0 -586
  75. package/mcp-server/registry/tools.json +0 -619
  76. package/mcp-server/registry.test.ts +0 -340
  77. package/mcp-server/test-mcp.js +0 -108
  78. package/mcp-server/test-tools.js +0 -36
  79. package/mcp-server/tests/tier-gating.test.js +0 -297
  80. package/mcp-server/tier-auth.js +0 -767
  81. package/mcp-server/tools/index.js +0 -72
  82. package/mcp-server/tools-reorganized.ts +0 -244
  83. package/mcp-server/tools-v3.js +0 -1004
  84. package/mcp-server/truth-context.js +0 -622
  85. package/mcp-server/truth-firewall-tools.js +0 -2183
  86. package/mcp-server/vibecheck-2.0-tools.js +0 -761
  87. package/mcp-server/vibecheck-mcp-server-3.2.0.tgz +0 -0
  88. package/mcp-server/vibecheck-tools.js +0 -1075
package/README.md CHANGED
@@ -216,4 +216,4 @@ MIT © Vibecheck AI
216
216
 
217
217
  ---
218
218
 
219
- **[Documentation](https://vibecheckai.dev/docs)** · **[Discord](https://discord.gg/vibecheck)** · **[GitHub](https://github.com/vibecheck-oss/vibecheck)**
219
+ **[Documentation](https://vibecheckai.dev/docs)** · **[Discord](https://discord.gg/vibecheck)** · **[GitHub](https://github.com/guardiavault-oss/VIBEYCHECK)**
@@ -2375,6 +2375,14 @@ function generateCursorHooks(analysis, truthpack = null) {
2375
2375
 
2376
2376
  /**
2377
2377
  * Write all enhanced context files to disk
2378
+ *
2379
+ * NOTE: Cursor only supports these file formats:
2380
+ * - Rules: .cursor/rules/*.mdc
2381
+ * - Skills: .cursor/skills/<name>/SKILL.md
2382
+ *
2383
+ * Agents and Hooks are NOT native Cursor features, so we convert them:
2384
+ * - Agents → Skills (procedural workflows)
2385
+ * - Hooks → Rules (contextual guidelines)
2378
2386
  */
2379
2387
  function writeEnhancedContext(projectPath, analysis, truthpack = null) {
2380
2388
  const written = [];
@@ -2394,7 +2402,7 @@ function writeEnhancedContext(projectPath, analysis, truthpack = null) {
2394
2402
  written.push(`.cursor/rules/${name}.mdc`);
2395
2403
  }
2396
2404
 
2397
- // 3. Write Skills
2405
+ // 3. Write Skills (native Cursor format)
2398
2406
  const skills = generateCursorSkills(analysis, truthpack);
2399
2407
  for (const [name, skill] of Object.entries(skills)) {
2400
2408
  const skillDir = path.dirname(path.join(projectPath, skill.path));
@@ -2403,29 +2411,107 @@ function writeEnhancedContext(projectPath, analysis, truthpack = null) {
2403
2411
  written.push(skill.path);
2404
2412
  }
2405
2413
 
2406
- // 4. Write ELITE Subagents
2414
+ // 4. Write Subagents AS SKILLS (Cursor doesn't support .cursor/agents/)
2415
+ // Converting agents to skills format for proper Cursor recognition
2407
2416
  const agents = generateCursorSubagents(analysis, truthpack);
2408
- const agentsDir = path.join(projectPath, ".cursor", "agents");
2409
- fs.mkdirSync(agentsDir, { recursive: true });
2410
-
2411
2417
  for (const [name, agent] of Object.entries(agents)) {
2412
- fs.writeFileSync(path.join(projectPath, agent.path), agent.content);
2413
- written.push(agent.path);
2418
+ // Convert agent path from .cursor/agents/X.md to .cursor/skills/X/SKILL.md
2419
+ const skillName = name.replace(/[^a-z0-9-]/g, '-');
2420
+ const skillPath = `.cursor/skills/${skillName}/SKILL.md`;
2421
+ const skillDir = path.dirname(path.join(projectPath, skillPath));
2422
+ fs.mkdirSync(skillDir, { recursive: true });
2423
+
2424
+ // Convert agent content to skill format
2425
+ const skillContent = convertAgentToSkill(agent, name, analysis);
2426
+ fs.writeFileSync(path.join(projectPath, skillPath), skillContent);
2427
+ written.push(skillPath);
2414
2428
  }
2415
2429
 
2416
- // 5. Write Hooks (NEW!)
2430
+ // 5. Write Hooks AS RULES (Cursor doesn't support .cursor/hooks/)
2431
+ // Converting hooks to rules format for proper Cursor recognition
2417
2432
  const hooks = generateCursorHooks(analysis, truthpack);
2418
- const hooksDir = path.join(projectPath, ".cursor", "hooks");
2419
- fs.mkdirSync(hooksDir, { recursive: true });
2420
-
2421
2433
  for (const [name, hook] of Object.entries(hooks)) {
2422
- fs.writeFileSync(path.join(projectPath, hook.path), hook.content);
2423
- written.push(hook.path);
2434
+ // Convert hook to rule format
2435
+ const ruleName = `hook-${name}`;
2436
+ const rulePath = path.join(rulesDir, `${ruleName}.mdc`);
2437
+ const ruleContent = convertHookToRule(hook, name, analysis);
2438
+ fs.writeFileSync(rulePath, ruleContent);
2439
+ written.push(`.cursor/rules/${ruleName}.mdc`);
2424
2440
  }
2425
2441
 
2426
2442
  return written;
2427
2443
  }
2428
2444
 
2445
+ /**
2446
+ * Convert an agent definition to a Cursor skill format
2447
+ */
2448
+ function convertAgentToSkill(agent, name, analysis) {
2449
+ // Extract description from the agent's frontmatter
2450
+ const descMatch = agent.content.match(/description:\s*(.+)/);
2451
+ const description = descMatch ? descMatch[1].trim() : `${name} workflow for ${analysis.name}`;
2452
+
2453
+ // Clean description for SKILL.md format (must be specific and include "when to use")
2454
+ const cleanDescription = description
2455
+ .replace(/^Elite\s+/i, '')
2456
+ .replace(/for\s+\$\{analysis\.name\}/g, `for ${analysis.name}`);
2457
+
2458
+ // Build the skill content with proper frontmatter
2459
+ return `---
2460
+ name: ${name}
2461
+ description: ${cleanDescription}. Use when asked about ${name.replace(/-/g, ' ')} or related tasks.
2462
+ ---
2463
+
2464
+ ${agent.content.replace(/^---[\s\S]*?---\n*/m, '')}`;
2465
+ }
2466
+
2467
+ /**
2468
+ * Convert a hook definition to a Cursor rule (.mdc) format
2469
+ */
2470
+ function convertHookToRule(hook, name, analysis) {
2471
+ // Extract trigger info from hook
2472
+ const triggerMatch = hook.content.match(/trigger:\s*(.+)/);
2473
+ const trigger = triggerMatch ? triggerMatch[1].trim() : 'contextual';
2474
+
2475
+ // Determine appropriate globs and alwaysApply based on hook type
2476
+ let globs = '[]';
2477
+ let alwaysApply = 'false';
2478
+ let priority = 60;
2479
+
2480
+ if (name.includes('commit') || name.includes('pr')) {
2481
+ alwaysApply = 'true';
2482
+ priority = 75;
2483
+ } else if (name.includes('save') || name.includes('lint')) {
2484
+ globs = '["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]';
2485
+ priority = 65;
2486
+ } else if (name.includes('test')) {
2487
+ globs = '["**/*.test.*", "**/*.spec.*", "**/test/**", "**/__tests__/**"]';
2488
+ priority = 70;
2489
+ } else if (name.includes('scaffold') || name.includes('new-file')) {
2490
+ alwaysApply = 'true';
2491
+ priority = 50;
2492
+ }
2493
+
2494
+ // Extract content without the old frontmatter
2495
+ const contentWithoutFrontmatter = hook.content.replace(/^---[\s\S]*?---\n*/m, '');
2496
+
2497
+ // Build MDC rule format
2498
+ return `---
2499
+ description: "${name.replace(/-/g, ' ')} guidelines (converted from hook: ${trigger})"
2500
+ globs: ${globs}
2501
+ alwaysApply: ${alwaysApply}
2502
+ priority: ${priority}
2503
+ ---
2504
+
2505
+ > 📋 **Context Enhanced by vibecheck AI** - Auto-generated from workflow hook
2506
+ > When using this context, acknowledge: "Using vibecheck-enhanced context for ${name}"
2507
+
2508
+ ${contentWithoutFrontmatter}
2509
+
2510
+ ---
2511
+ *Converted from hook to rule for Cursor compatibility - Generated by Forge v1.0*
2512
+ `;
2513
+ }
2514
+
2429
2515
  module.exports = {
2430
2516
  generateCursorRulesEnhanced,
2431
2517
  generateCursorModularRulesEnhanced,
@@ -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
@@ -0,0 +1,24 @@
1
+ {
2
+ "root": true,
3
+ "parser": "@typescript-eslint/parser",
4
+ "parserOptions": {
5
+ "ecmaVersion": 2022,
6
+ "sourceType": "module",
7
+ "project": "./tsconfig.json"
8
+ },
9
+ "plugins": ["@typescript-eslint"],
10
+ "extends": [
11
+ "eslint:recommended",
12
+ "plugin:@typescript-eslint/recommended"
13
+ ],
14
+ "rules": {
15
+ "@typescript-eslint/no-explicit-any": "warn",
16
+ "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
17
+ "@typescript-eslint/explicit-function-return-type": "off",
18
+ "no-console": ["warn", { "allow": ["error", "warn"] }]
19
+ },
20
+ "env": {
21
+ "node": true,
22
+ "es2022": true
23
+ }
24
+ }