@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.
- package/README.md +1 -1
- package/bin/runners/context/generators/cursor-enhanced.js +99 -13
- package/bin/runners/lib/unified-cli-output.js +16 -0
- package/bin/runners/runCI.js +353 -0
- package/bin/runners/runCheckpoint.js +2 -2
- package/mcp-server/.eslintrc.json +24 -0
- package/mcp-server/README.md +425 -135
- package/mcp-server/SPEC.md +583 -0
- package/mcp-server/configs/README.md +172 -0
- package/mcp-server/configs/claude-desktop-pro.json +31 -0
- package/mcp-server/configs/claude-desktop-with-workspace.json +25 -0
- package/mcp-server/configs/claude-desktop.json +19 -0
- package/mcp-server/configs/cursor-mcp.json +21 -0
- package/mcp-server/configs/windsurf-mcp.json +17 -0
- package/mcp-server/mcp-config.example.json +9 -0
- package/mcp-server/package.json +49 -34
- package/mcp-server/src/cli.ts +185 -0
- package/mcp-server/src/index.ts +85 -0
- package/mcp-server/src/server.ts +1933 -0
- package/mcp-server/src/services/cache-service.ts +466 -0
- package/mcp-server/src/services/cli-service.ts +345 -0
- package/mcp-server/src/services/context-manager.ts +717 -0
- package/mcp-server/src/services/firewall-service.ts +662 -0
- package/mcp-server/src/services/git-service.ts +671 -0
- package/mcp-server/src/services/index.ts +52 -0
- package/mcp-server/src/services/prompt-builder-service.ts +1031 -0
- package/mcp-server/src/services/session-service.ts +550 -0
- package/mcp-server/src/services/tier-service.ts +470 -0
- package/mcp-server/src/types.ts +351 -0
- package/mcp-server/tsconfig.json +16 -27
- package/package.json +6 -6
- package/mcp-server/.guardrail/audit/audit.log.jsonl +0 -2
- package/mcp-server/.specs/architecture.mdc +0 -90
- package/mcp-server/.specs/security.mdc +0 -30
- package/mcp-server/HARDENING_SUMMARY.md +0 -299
- package/mcp-server/agent-checkpoint.js +0 -364
- package/mcp-server/agent-firewall-interceptor.js +0 -500
- package/mcp-server/architect-tools.js +0 -707
- package/mcp-server/audit-mcp.js +0 -206
- package/mcp-server/authority-tools.js +0 -569
- package/mcp-server/codebase-architect-tools.js +0 -838
- package/mcp-server/conductor/conflict-resolver.js +0 -588
- package/mcp-server/conductor/execution-planner.js +0 -544
- package/mcp-server/conductor/index.js +0 -377
- package/mcp-server/conductor/lock-manager.js +0 -615
- package/mcp-server/conductor/request-queue.js +0 -550
- package/mcp-server/conductor/session-manager.js +0 -500
- package/mcp-server/conductor/tools.js +0 -510
- package/mcp-server/consolidated-tools.js +0 -1170
- package/mcp-server/deprecation-middleware.js +0 -282
- package/mcp-server/handlers/index.ts +0 -15
- package/mcp-server/handlers/tool-handler.ts +0 -593
- package/mcp-server/hygiene-tools.js +0 -428
- package/mcp-server/index-v1.js +0 -698
- package/mcp-server/index.js +0 -2940
- package/mcp-server/intelligence-tools.js +0 -664
- package/mcp-server/intent-drift-tools.js +0 -873
- package/mcp-server/intent-firewall-interceptor.js +0 -529
- package/mcp-server/lib/api-client.cjs +0 -13
- package/mcp-server/lib/cache-wrapper.cjs +0 -383
- package/mcp-server/lib/error-envelope.js +0 -138
- package/mcp-server/lib/executor.ts +0 -499
- package/mcp-server/lib/index.ts +0 -29
- package/mcp-server/lib/logger.cjs +0 -30
- package/mcp-server/lib/rate-limiter.js +0 -166
- package/mcp-server/lib/sandbox.test.ts +0 -519
- package/mcp-server/lib/sandbox.ts +0 -395
- package/mcp-server/lib/types.ts +0 -267
- package/mcp-server/logger.js +0 -173
- package/mcp-server/manifest.json +0 -473
- package/mcp-server/mdc-generator.js +0 -298
- package/mcp-server/premium-tools.js +0 -1275
- package/mcp-server/proof-tools.js +0 -571
- package/mcp-server/registry/tool-registry.js +0 -586
- package/mcp-server/registry/tools.json +0 -619
- package/mcp-server/registry.test.ts +0 -340
- package/mcp-server/test-mcp.js +0 -108
- package/mcp-server/test-tools.js +0 -36
- package/mcp-server/tests/tier-gating.test.js +0 -297
- package/mcp-server/tier-auth.js +0 -767
- package/mcp-server/tools/index.js +0 -72
- package/mcp-server/tools-reorganized.ts +0 -244
- package/mcp-server/tools-v3.js +0 -1004
- package/mcp-server/truth-context.js +0 -622
- package/mcp-server/truth-firewall-tools.js +0 -2183
- package/mcp-server/vibecheck-2.0-tools.js +0 -761
- package/mcp-server/vibecheck-mcp-server-3.2.0.tgz +0 -0
- 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/
|
|
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
|
|
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
|
-
|
|
2413
|
-
|
|
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 (
|
|
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
|
-
|
|
2423
|
-
|
|
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/
|
|
38
|
-
const {
|
|
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
|
+
}
|