@vibecheckai/cli 3.0.10 → 3.1.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/.generated +25 -0
- package/bin/registry.js +105 -0
- package/bin/runners/lib/cli-output.js +368 -0
- package/bin/runners/lib/entitlements-v2.js +26 -30
- package/bin/runners/lib/receipts.js +179 -0
- package/bin/runners/lib/upsell.js +510 -0
- package/bin/runners/lib/usage.js +153 -0
- package/bin/runners/runBadge.js +31 -4
- package/bin/runners/runDoctor.js +72 -3
- package/bin/runners/runFix.js +13 -0
- package/bin/runners/runGraph.js +14 -0
- package/bin/runners/runMcp.js +865 -42
- package/bin/runners/runPermissions.js +14 -0
- package/bin/runners/runPreflight.js +553 -0
- package/bin/runners/runProve.js +100 -41
- package/bin/runners/runShip.js +98 -19
- package/bin/runners/runVerify.js +272 -0
- package/bin/vibecheck.js +108 -94
- package/mcp-server/package.json +1 -1
- package/package.json +1 -1
package/bin/runners/runProve.js
CHANGED
|
@@ -22,6 +22,14 @@ const path = require("path");
|
|
|
22
22
|
const { buildTruthpack, writeTruthpack, detectFastifyEntry } = require("./lib/truth");
|
|
23
23
|
const { shipCore } = require("./runShip");
|
|
24
24
|
const { findContractDrift, loadContracts, hasContracts, getDriftSummary } = require("./lib/drift");
|
|
25
|
+
const {
|
|
26
|
+
generateRunId,
|
|
27
|
+
createJsonOutput,
|
|
28
|
+
writeJsonOutput,
|
|
29
|
+
exitCodeToVerdict,
|
|
30
|
+
verdictToExitCode,
|
|
31
|
+
saveArtifact
|
|
32
|
+
} = require("./lib/cli-output");
|
|
25
33
|
|
|
26
34
|
let runReality;
|
|
27
35
|
try {
|
|
@@ -723,7 +731,11 @@ function stamp() {
|
|
|
723
731
|
// MAIN PROVE FUNCTION
|
|
724
732
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
725
733
|
|
|
726
|
-
async function runProve(argsOrOpts = {}) {
|
|
734
|
+
async function runProve(argsOrOpts = {}, context = {}) {
|
|
735
|
+
// Extract runId from context or generate new one
|
|
736
|
+
const runId = context.runId || generateRunId();
|
|
737
|
+
const startTime = context.startTime || new Date().toISOString();
|
|
738
|
+
|
|
727
739
|
// Handle array args from CLI
|
|
728
740
|
if (Array.isArray(argsOrOpts)) {
|
|
729
741
|
if (argsOrOpts.includes("--help") || argsOrOpts.includes("-h")) {
|
|
@@ -747,6 +759,9 @@ async function runProve(argsOrOpts = {}) {
|
|
|
747
759
|
skipFix: argsOrOpts.includes("--skip-fix"),
|
|
748
760
|
headed: argsOrOpts.includes("--headed"),
|
|
749
761
|
danger: argsOrOpts.includes("--danger"),
|
|
762
|
+
json: argsOrOpts.includes("--json"),
|
|
763
|
+
output: getArg(["--output", "-o"]),
|
|
764
|
+
ci: argsOrOpts.includes("--ci"),
|
|
750
765
|
};
|
|
751
766
|
}
|
|
752
767
|
|
|
@@ -765,33 +780,38 @@ async function runProve(argsOrOpts = {}) {
|
|
|
765
780
|
danger = false,
|
|
766
781
|
maxPages = 18,
|
|
767
782
|
maxDepth = 2,
|
|
768
|
-
timeoutMs = 15000
|
|
783
|
+
timeoutMs = 15000,
|
|
784
|
+
json = false,
|
|
785
|
+
output = null,
|
|
786
|
+
ci = false
|
|
769
787
|
} = argsOrOpts;
|
|
770
788
|
|
|
771
789
|
const root = repoRoot || process.cwd();
|
|
772
790
|
const projectName = path.basename(root);
|
|
773
|
-
const startTime = Date.now();
|
|
774
791
|
|
|
775
|
-
//
|
|
776
|
-
printBanner();
|
|
777
|
-
|
|
778
|
-
console.log(` ${c.dim}Project:${c.reset} ${c.bold}${projectName}${c.reset}`);
|
|
779
|
-
console.log(` ${c.dim}Path:${c.reset} ${root}`);
|
|
780
|
-
if (url) {
|
|
781
|
-
console.log(` ${c.dim}URL:${c.reset} ${colors.accent}${url}${c.reset}`);
|
|
782
|
-
}
|
|
783
|
-
console.log(` ${c.dim}Fix Rounds:${c.reset} ${maxFixRounds} max`);
|
|
784
|
-
|
|
785
|
-
// Show pipeline overview
|
|
792
|
+
// Initialize pipeline status
|
|
786
793
|
const pipelineStatus = ['pending', 'pending', 'pending', 'pending', 'pending'];
|
|
787
|
-
printPipelineOverview(pipelineStatus);
|
|
788
|
-
|
|
789
794
|
const outDir = path.join(root, ".vibecheck", "prove", stamp());
|
|
790
795
|
ensureDir(outDir);
|
|
791
|
-
|
|
796
|
+
|
|
792
797
|
const timeline = [];
|
|
793
798
|
let finalVerdict = "UNKNOWN";
|
|
794
799
|
|
|
800
|
+
// Print banner (unless JSON or CI mode)
|
|
801
|
+
if (!json && !ci) {
|
|
802
|
+
printBanner();
|
|
803
|
+
|
|
804
|
+
console.log(` ${c.dim}Project:${c.reset} ${c.bold}${projectName}${c.reset}`);
|
|
805
|
+
console.log(` ${c.dim}Path:${c.reset} ${root}`);
|
|
806
|
+
if (url) {
|
|
807
|
+
console.log(` ${c.dim}URL:${c.reset} ${colors.accent}${url}${c.reset}`);
|
|
808
|
+
}
|
|
809
|
+
console.log(` ${c.dim}Fix Rounds:${c.reset} ${maxFixRounds} max`);
|
|
810
|
+
|
|
811
|
+
// Show pipeline overview
|
|
812
|
+
printPipelineOverview(pipelineStatus);
|
|
813
|
+
}
|
|
814
|
+
|
|
795
815
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
796
816
|
// STEP 1: CTX - Refresh truthpack
|
|
797
817
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -1138,12 +1158,12 @@ async function runProve(argsOrOpts = {}) {
|
|
|
1138
1158
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1139
1159
|
// SUMMARY
|
|
1140
1160
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1141
|
-
const duration = Date.now() -
|
|
1161
|
+
const duration = Date.now() - executionStart;
|
|
1142
1162
|
const durationStr = formatDuration(duration);
|
|
1143
1163
|
|
|
1144
1164
|
const report = {
|
|
1145
1165
|
meta: {
|
|
1146
|
-
startedAt: new Date(
|
|
1166
|
+
startedAt: new Date(executionStart).toISOString(),
|
|
1147
1167
|
finishedAt: new Date().toISOString(),
|
|
1148
1168
|
durationMs: duration,
|
|
1149
1169
|
url: url || null,
|
|
@@ -1161,34 +1181,73 @@ async function runProve(argsOrOpts = {}) {
|
|
|
1161
1181
|
ensureDir(path.dirname(latestPath));
|
|
1162
1182
|
fs.writeFileSync(latestPath, JSON.stringify(report, null, 2), "utf8");
|
|
1163
1183
|
|
|
1164
|
-
//
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
// Timeline summary
|
|
1168
|
-
printTimelineSummary(timeline);
|
|
1184
|
+
// Save artifacts
|
|
1185
|
+
saveArtifact(runId, "prove-report", report);
|
|
1186
|
+
saveArtifact(runId, "timeline", timeline);
|
|
1169
1187
|
|
|
1170
|
-
//
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1188
|
+
// JSON output mode
|
|
1189
|
+
if (json) {
|
|
1190
|
+
const output = createJsonOutput({
|
|
1191
|
+
runId,
|
|
1192
|
+
command: "prove",
|
|
1193
|
+
startTime: executionStart,
|
|
1194
|
+
exitCode: verdictToExitCode(finalVerdict),
|
|
1195
|
+
verdict: finalVerdict,
|
|
1196
|
+
result: {
|
|
1197
|
+
meta: report.meta,
|
|
1198
|
+
timeline,
|
|
1199
|
+
finalVerdict,
|
|
1200
|
+
fixRounds: fixRound,
|
|
1201
|
+
duration: duration
|
|
1202
|
+
},
|
|
1203
|
+
tier: "pro",
|
|
1204
|
+
version: require("../../package.json").version,
|
|
1205
|
+
artifacts: [
|
|
1206
|
+
{
|
|
1207
|
+
type: "report",
|
|
1208
|
+
path: path.join(outDir, "prove_report.json"),
|
|
1209
|
+
description: "Prove report with timeline"
|
|
1210
|
+
}
|
|
1211
|
+
]
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1214
|
+
writeJsonOutput(output, output);
|
|
1215
|
+
}
|
|
1176
1216
|
|
|
1177
|
-
//
|
|
1178
|
-
if (
|
|
1179
|
-
|
|
1217
|
+
// Final verdict display (unless JSON or CI mode)
|
|
1218
|
+
if (!json && !ci) {
|
|
1219
|
+
printFinalVerdict(finalVerdict, durationStr, fixRound, shipResult.report?.findings?.length || 0);
|
|
1220
|
+
|
|
1221
|
+
// Timeline summary
|
|
1222
|
+
printTimelineSummary(timeline);
|
|
1223
|
+
|
|
1224
|
+
// Report links
|
|
1225
|
+
printSection('REPORTS', ICONS.doc);
|
|
1180
1226
|
console.log();
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
} else if (fixRound >= maxFixRounds) {
|
|
1184
|
-
console.log(` ${colors.accent}vibecheck prove --max-fix-rounds ${maxFixRounds + 2}${c.reset} ${c.dim}Try more fix rounds${c.reset}`);
|
|
1185
|
-
}
|
|
1186
|
-
console.log(` ${colors.accent}vibecheck ship --fix${c.reset} ${c.dim}Manual fix mode${c.reset}`);
|
|
1227
|
+
console.log(` ${colors.accent}${outDir}/prove_report.json${c.reset}`);
|
|
1228
|
+
console.log(` ${c.dim}${path.join(root, '.vibecheck', 'prove', 'last_prove.json')}${c.reset}`);
|
|
1187
1229
|
console.log();
|
|
1230
|
+
|
|
1231
|
+
// Next steps if not proved
|
|
1232
|
+
if (finalVerdict !== 'SHIP') {
|
|
1233
|
+
printSection('NEXT STEPS', ICONS.lightning);
|
|
1234
|
+
console.log();
|
|
1235
|
+
if (skipFix) {
|
|
1236
|
+
console.log(` ${colors.accent}vibecheck prove --url ${url || '<url>'}${c.reset} ${c.dim}Enable auto-fix${c.reset}`);
|
|
1237
|
+
} else if (fixRound >= maxFixRounds) {
|
|
1238
|
+
console.log(` ${colors.accent}vibecheck prove --max-fix-rounds ${maxFixRounds + 2}${c.reset} ${c.dim}Try more fix rounds${c.reset}`);
|
|
1239
|
+
}
|
|
1240
|
+
console.log(` ${colors.accent}vibecheck ship --fix${c.reset} ${c.dim}Manual fix mode${c.reset}`);
|
|
1241
|
+
console.log();
|
|
1242
|
+
}
|
|
1243
|
+
} else if (ci) {
|
|
1244
|
+
// CI mode - minimal output
|
|
1245
|
+
console.log(`VERDICT=${finalVerdict}`);
|
|
1246
|
+
console.log(`DURATION=${duration}ms`);
|
|
1247
|
+
console.log(`FIX_ROUNDS=${fixRound}`);
|
|
1188
1248
|
}
|
|
1189
1249
|
|
|
1190
|
-
|
|
1191
|
-
return process.exitCode;
|
|
1250
|
+
return verdictToExitCode(finalVerdict);
|
|
1192
1251
|
}
|
|
1193
1252
|
|
|
1194
1253
|
module.exports = { runProve };
|
package/bin/runners/runShip.js
CHANGED
|
@@ -13,6 +13,14 @@ const { withErrorHandling } = require("./lib/error-handler");
|
|
|
13
13
|
const { ensureOutputDir, detectProjectFeatures } = require("./utils");
|
|
14
14
|
const { enforceLimit, enforceFeature, trackUsage, getCurrentTier } = require("./lib/entitlements");
|
|
15
15
|
const { emitShipCheck } = require("./lib/audit-bridge");
|
|
16
|
+
const {
|
|
17
|
+
generateRunId,
|
|
18
|
+
createJsonOutput,
|
|
19
|
+
writeJsonOutput,
|
|
20
|
+
exitCodeToVerdict,
|
|
21
|
+
verdictToExitCode,
|
|
22
|
+
saveArtifact
|
|
23
|
+
} = require("./lib/cli-output");
|
|
16
24
|
|
|
17
25
|
// Route Truth v1 - Fake endpoint detection
|
|
18
26
|
const { buildTruthpack, writeTruthpack, detectFastifyEntry } = require("./lib/truth");
|
|
@@ -27,6 +35,8 @@ const {
|
|
|
27
35
|
} = require("./lib/analyzers");
|
|
28
36
|
const { findingsFromReality } = require("./lib/reality-findings");
|
|
29
37
|
const { findContractDrift, loadContracts, hasContracts, getDriftSummary } = require("./lib/drift");
|
|
38
|
+
const upsell = require("./lib/upsell");
|
|
39
|
+
const entitlements = require("./lib/entitlements-v2");
|
|
30
40
|
|
|
31
41
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
32
42
|
// ADVANCED TERMINAL - ANSI CODES & UTILITIES
|
|
@@ -846,9 +856,13 @@ function parseArgs(args) {
|
|
|
846
856
|
// MAIN SHIP FUNCTION
|
|
847
857
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
848
858
|
|
|
849
|
-
async function runShip(args) {
|
|
859
|
+
async function runShip(args, context = {}) {
|
|
860
|
+
// Extract runId from context or generate new one
|
|
861
|
+
const runId = context.runId || generateRunId();
|
|
862
|
+
const startTime = context.startTime || new Date().toISOString();
|
|
863
|
+
|
|
850
864
|
const opts = parseArgs(args);
|
|
851
|
-
const
|
|
865
|
+
const executionStart = Date.now();
|
|
852
866
|
|
|
853
867
|
// Show help if requested
|
|
854
868
|
if (opts.help) {
|
|
@@ -990,23 +1004,53 @@ async function runShip(args) {
|
|
|
990
1004
|
|
|
991
1005
|
// JSON output mode
|
|
992
1006
|
if (opts.json) {
|
|
993
|
-
const output = {
|
|
994
|
-
|
|
995
|
-
|
|
1007
|
+
const output = createJsonOutput({
|
|
1008
|
+
runId,
|
|
1009
|
+
command: "ship",
|
|
1010
|
+
startTime,
|
|
1011
|
+
exitCode: verdictToExitCode(verdict),
|
|
996
1012
|
verdict,
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1013
|
+
result: {
|
|
1014
|
+
verdict,
|
|
1015
|
+
score: results.score,
|
|
1016
|
+
grade: results.grade,
|
|
1017
|
+
canShip: results.canShip,
|
|
1018
|
+
summary: {
|
|
1019
|
+
blockers: blockers.length,
|
|
1020
|
+
warnings: warnings.length,
|
|
1021
|
+
total: allFindings.length,
|
|
1022
|
+
},
|
|
1023
|
+
findings: allFindings,
|
|
1024
|
+
proofGraph: proofGraph.summary,
|
|
1025
|
+
duration: Date.now() - executionStart,
|
|
1003
1026
|
},
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1027
|
+
tier: getCurrentTier(),
|
|
1028
|
+
version: require("../../package.json").version,
|
|
1029
|
+
artifacts: [
|
|
1030
|
+
{
|
|
1031
|
+
type: "report",
|
|
1032
|
+
path: path.join(outputDir, "report.json"),
|
|
1033
|
+
description: "Ship report with findings"
|
|
1034
|
+
},
|
|
1035
|
+
{
|
|
1036
|
+
type: "proof",
|
|
1037
|
+
path: path.join(outputDir, "proof-graph.json"),
|
|
1038
|
+
description: "Proof graph analysis"
|
|
1039
|
+
}
|
|
1040
|
+
]
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
writeJsonOutput(output, opts.output);
|
|
1044
|
+
|
|
1045
|
+
// Save artifacts
|
|
1046
|
+
const reportPath = saveArtifact(runId, "report", {
|
|
1047
|
+
...output.result,
|
|
1048
|
+
truthpack,
|
|
1049
|
+
integrity: results.integrity
|
|
1050
|
+
});
|
|
1051
|
+
const proofPath = saveArtifact(runId, "proof-graph", proofGraph);
|
|
1052
|
+
|
|
1053
|
+
return verdictToExitCode(verdict);
|
|
1010
1054
|
}
|
|
1011
1055
|
|
|
1012
1056
|
// CI output mode (minimal)
|
|
@@ -1015,7 +1059,17 @@ async function runShip(args) {
|
|
|
1015
1059
|
console.log(`SCORE=${results.score}`);
|
|
1016
1060
|
console.log(`BLOCKERS=${blockers.length}`);
|
|
1017
1061
|
console.log(`WARNINGS=${warnings.length}`);
|
|
1018
|
-
|
|
1062
|
+
|
|
1063
|
+
// Save CI artifacts
|
|
1064
|
+
saveArtifact(runId, "ci-summary", {
|
|
1065
|
+
verdict,
|
|
1066
|
+
score: results.score,
|
|
1067
|
+
blockers: blockers.length,
|
|
1068
|
+
warnings: warnings.length,
|
|
1069
|
+
timestamp: new Date().toISOString()
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
return verdictToExitCode(verdict);
|
|
1019
1073
|
}
|
|
1020
1074
|
|
|
1021
1075
|
// Fix mode
|
|
@@ -1076,6 +1130,20 @@ async function runShip(args) {
|
|
|
1076
1130
|
}
|
|
1077
1131
|
console.log(` ${colors.accent}vibecheck ship --assist${c.reset} ${c.dim}Get AI help for complex issues${c.reset}`);
|
|
1078
1132
|
console.log();
|
|
1133
|
+
|
|
1134
|
+
// Earned upsell: Badge withheld when verdict != SHIP
|
|
1135
|
+
const currentTier = context?.authInfo?.access?.tier || "free";
|
|
1136
|
+
if (entitlements.tierMeetsMinimum(currentTier, "starter")) {
|
|
1137
|
+
// User has badge access but verdict prevents it
|
|
1138
|
+
console.log(upsell.formatEarnedUpsell({
|
|
1139
|
+
cmd: "ship",
|
|
1140
|
+
verdict,
|
|
1141
|
+
topIssues: blockers.slice(0, 3),
|
|
1142
|
+
withheldArtifact: "badge",
|
|
1143
|
+
currentTier,
|
|
1144
|
+
suggestedCmd: currentTier === "free" ? "vibecheck fix --plan-only" : "vibecheck fix",
|
|
1145
|
+
}));
|
|
1146
|
+
}
|
|
1079
1147
|
}
|
|
1080
1148
|
|
|
1081
1149
|
// Emit audit event
|
|
@@ -1093,7 +1161,18 @@ async function runShip(args) {
|
|
|
1093
1161
|
} catch {}
|
|
1094
1162
|
|
|
1095
1163
|
// Exit code: 0=SHIP, 1=WARN, 2=BLOCK
|
|
1096
|
-
|
|
1164
|
+
const exitCode = verdictToExitCode(verdict);
|
|
1165
|
+
|
|
1166
|
+
// Save final results
|
|
1167
|
+
saveArtifact(runId, "summary", {
|
|
1168
|
+
verdict,
|
|
1169
|
+
score: results.score,
|
|
1170
|
+
canShip: results.canShip,
|
|
1171
|
+
exitCode,
|
|
1172
|
+
timestamp: new Date().toISOString()
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
return exitCode;
|
|
1097
1176
|
|
|
1098
1177
|
} catch (error) {
|
|
1099
1178
|
if (!opts.json) stopSpinner(`Ship check failed: ${error.message}`, false);
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* runVerify.js - CLI Verify Command
|
|
4
|
+
* Verifies AI-generated agent output against vibecheck-v1 protocol
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* vibecheck verify [options]
|
|
8
|
+
* vibecheck verify --input response.json
|
|
9
|
+
* vibecheck verify --stdin
|
|
10
|
+
* vibecheck verify --mode ship --strict
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
"use strict";
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const { parseArgs } = require('util');
|
|
18
|
+
|
|
19
|
+
const HELP = `
|
|
20
|
+
vibecheck verify - Verify AI-generated agent output
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
vibecheck verify [options]
|
|
24
|
+
|
|
25
|
+
Options:
|
|
26
|
+
--input, -i <file> Input file containing agent response JSON
|
|
27
|
+
--stdin Read agent response from stdin
|
|
28
|
+
--root, -r <dir> Project root directory (default: cwd)
|
|
29
|
+
--mode, -m <mode> Verification mode: explore|build|ship (default: build)
|
|
30
|
+
--strict Enable strict mode (fail on warnings)
|
|
31
|
+
--json Output results as JSON
|
|
32
|
+
--help, -h Show this help message
|
|
33
|
+
|
|
34
|
+
Examples:
|
|
35
|
+
vibecheck verify --input agent-response.json
|
|
36
|
+
echo '{"format":"vibecheck-v1",...}' | vibecheck verify --stdin
|
|
37
|
+
vibecheck verify --input response.json --mode ship --strict
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
async function main() {
|
|
41
|
+
const { values, positionals } = parseArgs({
|
|
42
|
+
options: {
|
|
43
|
+
input: { type: 'string', short: 'i' },
|
|
44
|
+
stdin: { type: 'boolean', default: false },
|
|
45
|
+
root: { type: 'string', short: 'r' },
|
|
46
|
+
mode: { type: 'string', short: 'm', default: 'build' },
|
|
47
|
+
strict: { type: 'boolean', default: false },
|
|
48
|
+
json: { type: 'boolean', default: false },
|
|
49
|
+
help: { type: 'boolean', short: 'h' },
|
|
50
|
+
},
|
|
51
|
+
allowPositionals: true,
|
|
52
|
+
strict: false,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (values.help) {
|
|
56
|
+
console.log(HELP);
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const projectRoot = values.root ? path.resolve(values.root) : process.cwd();
|
|
61
|
+
const mode = values.mode || 'build';
|
|
62
|
+
const strict = values.strict || false;
|
|
63
|
+
const outputJson = values.json || false;
|
|
64
|
+
|
|
65
|
+
// Validate mode
|
|
66
|
+
if (!['explore', 'build', 'ship'].includes(mode)) {
|
|
67
|
+
console.error(`Error: Invalid mode "${mode}". Must be one of: explore, build, ship`);
|
|
68
|
+
process.exit(2);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Get input
|
|
72
|
+
let rawInput = '';
|
|
73
|
+
|
|
74
|
+
if (values.stdin) {
|
|
75
|
+
rawInput = await readStdin();
|
|
76
|
+
} else if (values.input) {
|
|
77
|
+
const inputPath = path.resolve(values.input);
|
|
78
|
+
if (!fs.existsSync(inputPath)) {
|
|
79
|
+
console.error(`Error: Input file not found: ${inputPath}`);
|
|
80
|
+
process.exit(2);
|
|
81
|
+
}
|
|
82
|
+
rawInput = fs.readFileSync(inputPath, 'utf-8');
|
|
83
|
+
} else if (positionals.length > 0) {
|
|
84
|
+
// Allow positional argument as input file
|
|
85
|
+
const inputPath = path.resolve(positionals[0]);
|
|
86
|
+
if (!fs.existsSync(inputPath)) {
|
|
87
|
+
console.error(`Error: Input file not found: ${inputPath}`);
|
|
88
|
+
process.exit(2);
|
|
89
|
+
}
|
|
90
|
+
rawInput = fs.readFileSync(inputPath, 'utf-8');
|
|
91
|
+
} else {
|
|
92
|
+
console.error('Error: No input provided. Use --input <file> or --stdin');
|
|
93
|
+
console.log(HELP);
|
|
94
|
+
process.exit(2);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!rawInput.trim()) {
|
|
98
|
+
console.error('Error: Empty input');
|
|
99
|
+
process.exit(2);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Load the verification module
|
|
103
|
+
let validateFormat, verifyAgentOutput, formatCheckResults, buildJsonReport;
|
|
104
|
+
try {
|
|
105
|
+
// Try loading from the built TypeScript output
|
|
106
|
+
const verification = require('../../dist/lib/verification/index.js');
|
|
107
|
+
validateFormat = verification.validateFormat;
|
|
108
|
+
verifyAgentOutput = verification.verifyAgentOutput;
|
|
109
|
+
formatCheckResults = verification.formatCheckResults;
|
|
110
|
+
buildJsonReport = verification.buildJsonReport;
|
|
111
|
+
} catch (err) {
|
|
112
|
+
// Fallback: try loading directly (for development)
|
|
113
|
+
try {
|
|
114
|
+
const verification = require('../../src/lib/verification/index.ts');
|
|
115
|
+
validateFormat = verification.validateFormat;
|
|
116
|
+
verifyAgentOutput = verification.verifyAgentOutput;
|
|
117
|
+
formatCheckResults = verification.formatCheckResults;
|
|
118
|
+
buildJsonReport = verification.buildJsonReport;
|
|
119
|
+
} catch (err2) {
|
|
120
|
+
console.error('Error loading verification module:', err.message);
|
|
121
|
+
console.error('Make sure the project is built: pnpm build');
|
|
122
|
+
process.exit(2);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Step 1: Validate format
|
|
127
|
+
if (!outputJson) {
|
|
128
|
+
console.log('🔍 Validating agent output format...');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const formatResult = validateFormat(rawInput);
|
|
132
|
+
|
|
133
|
+
if (!formatResult.valid) {
|
|
134
|
+
if (outputJson) {
|
|
135
|
+
console.log(JSON.stringify({
|
|
136
|
+
status: 'fail',
|
|
137
|
+
phase: 'format-validation',
|
|
138
|
+
error: formatResult.error,
|
|
139
|
+
retryPrompt: formatResult.retryPrompt,
|
|
140
|
+
}, null, 2));
|
|
141
|
+
} else {
|
|
142
|
+
console.error('❌ Format validation failed:', formatResult.error);
|
|
143
|
+
console.error('\n📝 Retry prompt:');
|
|
144
|
+
console.error(formatResult.retryPrompt);
|
|
145
|
+
}
|
|
146
|
+
process.exit(2);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check if it's an error response
|
|
150
|
+
if ('error' in formatResult && formatResult.error) {
|
|
151
|
+
if (outputJson) {
|
|
152
|
+
console.log(JSON.stringify({
|
|
153
|
+
status: 'error',
|
|
154
|
+
phase: 'agent-error',
|
|
155
|
+
error: formatResult.error.error,
|
|
156
|
+
}, null, 2));
|
|
157
|
+
} else {
|
|
158
|
+
console.log('⚠️ Agent returned an error response:');
|
|
159
|
+
console.log(formatResult.error.error);
|
|
160
|
+
}
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const agentOutput = formatResult.output;
|
|
165
|
+
|
|
166
|
+
if (!outputJson) {
|
|
167
|
+
console.log('✅ Format valid');
|
|
168
|
+
console.log(` Files in diff: ${countFilesInDiff(agentOutput.diff)}`);
|
|
169
|
+
console.log(` Commands: ${agentOutput.commands?.length || 0}`);
|
|
170
|
+
console.log(` Tests: ${agentOutput.tests?.length || 0}`);
|
|
171
|
+
console.log('');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Step 2: Run verification pipeline
|
|
175
|
+
if (!outputJson) {
|
|
176
|
+
console.log('🔬 Running verification pipeline...');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const context = {
|
|
180
|
+
projectRoot,
|
|
181
|
+
mode,
|
|
182
|
+
runTests: mode === 'ship',
|
|
183
|
+
strictMode: strict,
|
|
184
|
+
maxFilesChanged: mode === 'ship' ? 10 : mode === 'build' ? 20 : 50,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
let result;
|
|
188
|
+
try {
|
|
189
|
+
result = await verifyAgentOutput(agentOutput, context);
|
|
190
|
+
} catch (err) {
|
|
191
|
+
if (outputJson) {
|
|
192
|
+
console.log(JSON.stringify({
|
|
193
|
+
status: 'fail',
|
|
194
|
+
phase: 'verification',
|
|
195
|
+
error: err.message,
|
|
196
|
+
}, null, 2));
|
|
197
|
+
} else {
|
|
198
|
+
console.error('❌ Verification error:', err.message);
|
|
199
|
+
}
|
|
200
|
+
process.exit(2);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Output results
|
|
204
|
+
if (outputJson) {
|
|
205
|
+
console.log(JSON.stringify(buildJsonReport(result), null, 2));
|
|
206
|
+
} else {
|
|
207
|
+
console.log('');
|
|
208
|
+
console.log(formatCheckResults(result.checks));
|
|
209
|
+
console.log('');
|
|
210
|
+
|
|
211
|
+
if (result.status === 'pass') {
|
|
212
|
+
console.log('✅ VERIFICATION PASSED');
|
|
213
|
+
} else if (result.status === 'warn') {
|
|
214
|
+
console.log('⚠️ VERIFICATION PASSED WITH WARNINGS');
|
|
215
|
+
} else {
|
|
216
|
+
console.log('❌ VERIFICATION FAILED');
|
|
217
|
+
if (result.failureContext) {
|
|
218
|
+
console.log('\n📝 Failure context for retry:');
|
|
219
|
+
console.log(result.failureContext);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Exit codes: 0 = pass, 1 = warn (if strict), 2 = fail
|
|
225
|
+
if (result.status === 'fail') {
|
|
226
|
+
process.exit(2);
|
|
227
|
+
} else if (result.status === 'warn' && strict) {
|
|
228
|
+
process.exit(1);
|
|
229
|
+
} else {
|
|
230
|
+
process.exit(0);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Read all input from stdin
|
|
236
|
+
*/
|
|
237
|
+
function readStdin() {
|
|
238
|
+
return new Promise((resolve, reject) => {
|
|
239
|
+
let data = '';
|
|
240
|
+
process.stdin.setEncoding('utf-8');
|
|
241
|
+
process.stdin.on('data', chunk => { data += chunk; });
|
|
242
|
+
process.stdin.on('end', () => resolve(data));
|
|
243
|
+
process.stdin.on('error', reject);
|
|
244
|
+
|
|
245
|
+
// Timeout after 5 seconds if no input
|
|
246
|
+
setTimeout(() => {
|
|
247
|
+
if (data === '') {
|
|
248
|
+
reject(new Error('No input received from stdin'));
|
|
249
|
+
}
|
|
250
|
+
}, 5000);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Count files in a diff string
|
|
256
|
+
*/
|
|
257
|
+
function countFilesInDiff(diff) {
|
|
258
|
+
if (!diff) return 0;
|
|
259
|
+
const matches = diff.match(/^diff --git/gm);
|
|
260
|
+
return matches ? matches.length : 0;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Export for CLI registry
|
|
264
|
+
module.exports = { main };
|
|
265
|
+
|
|
266
|
+
// Run if executed directly
|
|
267
|
+
if (require.main === module) {
|
|
268
|
+
main().catch(err => {
|
|
269
|
+
console.error('Fatal error:', err.message);
|
|
270
|
+
process.exit(2);
|
|
271
|
+
});
|
|
272
|
+
}
|