@vibecheckai/cli 3.1.8 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/registry.js +106 -116
- package/bin/runners/context/generators/mcp.js +18 -0
- package/bin/runners/context/index.js +72 -4
- package/bin/runners/context/proof-context.js +293 -1
- package/bin/runners/context/security-scanner.js +311 -73
- package/bin/runners/lib/analyzers.js +607 -20
- package/bin/runners/lib/detectors-v2.js +172 -15
- package/bin/runners/lib/entitlements-v2.js +48 -1
- package/bin/runners/lib/evidence-pack.js +678 -0
- package/bin/runners/lib/html-proof-report.js +913 -0
- package/bin/runners/lib/missions/plan.js +231 -41
- package/bin/runners/lib/missions/templates.js +125 -0
- package/bin/runners/lib/scan-output.js +492 -253
- package/bin/runners/lib/ship-output.js +901 -641
- package/bin/runners/runCheckpoint.js +44 -3
- package/bin/runners/runContext.d.ts +4 -0
- package/bin/runners/runDoctor.js +10 -2
- package/bin/runners/runFix.js +51 -341
- package/bin/runners/runInit.js +11 -0
- package/bin/runners/runPolish.d.ts +4 -0
- package/bin/runners/runPolish.js +608 -29
- package/bin/runners/runProve.js +210 -25
- package/bin/runners/runReality.js +846 -101
- package/bin/runners/runScan.js +238 -4
- package/bin/runners/runShip.js +19 -3
- package/bin/runners/runWatch.js +14 -1
- package/bin/vibecheck.js +32 -2
- package/mcp-server/consolidated-tools.js +408 -42
- package/mcp-server/index.js +152 -15
- package/mcp-server/proof-tools.js +571 -0
- package/mcp-server/tier-auth.js +22 -19
- package/mcp-server/tools-v3.js +744 -0
- package/mcp-server/truth-firewall-tools.js +190 -4
- package/package.json +3 -1
- package/bin/runners/runInstall.js +0 -281
- package/bin/runners/runLabs.js +0 -341
|
@@ -355,6 +355,7 @@ function detectAuthContractDrift(truthpack, authContract) {
|
|
|
355
355
|
/**
|
|
356
356
|
* D_ENV_USED_BUT_UNDECLARED (WARN→BLOCK if required)
|
|
357
357
|
* Trigger: truthpack envUsage includes FOO but contracts/env.json does not
|
|
358
|
+
* Enhanced with better required/optional detection
|
|
358
359
|
*/
|
|
359
360
|
function detectEnvUsedButUndeclared(truthpack, envContract) {
|
|
360
361
|
const findings = [];
|
|
@@ -363,7 +364,28 @@ function detectEnvUsedButUndeclared(truthpack, envContract) {
|
|
|
363
364
|
|
|
364
365
|
for (const usage of envUsage) {
|
|
365
366
|
if (!declaredVars.has(usage.name)) {
|
|
366
|
-
|
|
367
|
+
// Check if usage pattern suggests optional
|
|
368
|
+
const usageCode = usage.locations?.[0]?.snippet || "";
|
|
369
|
+
const hasOptionalPattern = hasOptionalUsagePattern(usageCode);
|
|
370
|
+
|
|
371
|
+
// Determine if required based on name patterns and usage
|
|
372
|
+
const isRequired = !hasOptionalPattern &&
|
|
373
|
+
(usage.inferredRequiredness === "required" || isLikelyRequired(usage.name));
|
|
374
|
+
|
|
375
|
+
// Skip reporting for common optional env vars that are typically not documented
|
|
376
|
+
const commonOptionalVars = [
|
|
377
|
+
/^DEBUG$/i,
|
|
378
|
+
/^LOG_LEVEL$/i,
|
|
379
|
+
/^NODE_ENV$/i,
|
|
380
|
+
/^CI$/i,
|
|
381
|
+
/^VERCEL/i,
|
|
382
|
+
/^GITHUB_/i,
|
|
383
|
+
/^NEXT_PUBLIC_VERCEL/i,
|
|
384
|
+
];
|
|
385
|
+
|
|
386
|
+
if (commonOptionalVars.some(p => p.test(usage.name))) {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
367
389
|
|
|
368
390
|
findings.push(createFindingV2({
|
|
369
391
|
detectorId: "ENV_USED_BUT_UNDECLARED",
|
|
@@ -620,44 +642,177 @@ function detectContractsOutOfDate(truthpack, contracts) {
|
|
|
620
642
|
// Helpers
|
|
621
643
|
// =============================================================================
|
|
622
644
|
|
|
645
|
+
/**
|
|
646
|
+
* Enhanced route matching with support for:
|
|
647
|
+
* - Dynamic segments (:id, [id], [slug], [...slug])
|
|
648
|
+
* - Optional catch-all routes [[...slug]]
|
|
649
|
+
* - Query string stripping
|
|
650
|
+
* - Trailing slash normalization
|
|
651
|
+
*/
|
|
623
652
|
function routeMatches(pattern, actual) {
|
|
624
|
-
|
|
625
|
-
const
|
|
653
|
+
// Normalize: strip query strings and trailing slashes
|
|
654
|
+
const normalizedPattern = pattern.split("?")[0].replace(/\/+$/, "") || "/";
|
|
655
|
+
const normalizedActual = actual.split("?")[0].replace(/\/+$/, "") || "/";
|
|
656
|
+
|
|
657
|
+
const patternParts = normalizedPattern.split("/").filter(Boolean);
|
|
658
|
+
const actualParts = normalizedActual.split("/").filter(Boolean);
|
|
659
|
+
|
|
660
|
+
// Handle catch-all routes: [...slug] or [[...slug]]
|
|
661
|
+
const hasCatchAll = patternParts.some(p =>
|
|
662
|
+
p.startsWith("[...") || p.startsWith("[[...")
|
|
663
|
+
);
|
|
664
|
+
|
|
665
|
+
if (hasCatchAll) {
|
|
666
|
+
const catchAllIndex = patternParts.findIndex(p =>
|
|
667
|
+
p.startsWith("[...") || p.startsWith("[[...")
|
|
668
|
+
);
|
|
669
|
+
|
|
670
|
+
// For catch-all, pattern up to catch-all must match
|
|
671
|
+
for (let i = 0; i < catchAllIndex; i++) {
|
|
672
|
+
const p = patternParts[i];
|
|
673
|
+
if (isDynamicSegment(p)) continue;
|
|
674
|
+
if (p !== actualParts[i]) return false;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// Catch-all matches any remaining segments (including none for [[...]])
|
|
678
|
+
const isOptional = patternParts[catchAllIndex].startsWith("[[...");
|
|
679
|
+
if (!isOptional && actualParts.length <= catchAllIndex) {
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
return true;
|
|
684
|
+
}
|
|
626
685
|
|
|
686
|
+
// Standard matching: lengths must match
|
|
627
687
|
if (patternParts.length !== actualParts.length) return false;
|
|
628
688
|
|
|
629
689
|
for (let i = 0; i < patternParts.length; i++) {
|
|
630
690
|
const p = patternParts[i];
|
|
631
|
-
if (
|
|
691
|
+
if (isDynamicSegment(p)) continue;
|
|
632
692
|
if (p !== actualParts[i]) return false;
|
|
633
693
|
}
|
|
634
694
|
return true;
|
|
635
695
|
}
|
|
636
696
|
|
|
697
|
+
/**
|
|
698
|
+
* Check if a route segment is dynamic
|
|
699
|
+
*/
|
|
700
|
+
function isDynamicSegment(segment) {
|
|
701
|
+
return segment.startsWith(":") || // Express-style :id
|
|
702
|
+
segment.startsWith("[") || // Next.js-style [id]
|
|
703
|
+
segment === "*" || // Wildcard
|
|
704
|
+
segment.startsWith("$"); // Remix-style $id
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Enhanced glob pattern matching with proper escaping
|
|
709
|
+
*/
|
|
637
710
|
function matchPattern(pattern, url) {
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
711
|
+
try {
|
|
712
|
+
// Normalize URL: extract pathname
|
|
713
|
+
let pathname;
|
|
714
|
+
try {
|
|
715
|
+
pathname = new URL(url, "http://localhost").pathname;
|
|
716
|
+
} catch {
|
|
717
|
+
pathname = url.split("?")[0];
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// Escape special regex chars except * and ?
|
|
721
|
+
const escaped = pattern
|
|
722
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
723
|
+
.replace(/\*\*/g, "{{GLOBSTAR}}")
|
|
724
|
+
.replace(/\*/g, "[^/]*")
|
|
725
|
+
.replace(/\?/g, ".")
|
|
726
|
+
.replace(/{{GLOBSTAR}}/g, ".*");
|
|
727
|
+
|
|
728
|
+
const regex = new RegExp("^" + escaped + "$");
|
|
729
|
+
return regex.test(url) || regex.test(pathname);
|
|
730
|
+
} catch {
|
|
731
|
+
// Fallback to simple comparison
|
|
732
|
+
return pattern === url;
|
|
733
|
+
}
|
|
646
734
|
}
|
|
647
735
|
|
|
736
|
+
/**
|
|
737
|
+
* Enhanced env var requirement detection
|
|
738
|
+
* Returns { required: boolean, confidence: 'high' | 'medium' | 'low', reason: string }
|
|
739
|
+
*/
|
|
648
740
|
function isLikelyRequired(name) {
|
|
649
|
-
|
|
741
|
+
// High-confidence required patterns
|
|
742
|
+
const highConfidenceRequired = [
|
|
650
743
|
/^DATABASE_URL$/i,
|
|
651
744
|
/^NEXTAUTH_SECRET$/i,
|
|
652
745
|
/^NEXTAUTH_URL$/i,
|
|
653
746
|
/^JWT_SECRET$/i,
|
|
747
|
+
/^AUTH_SECRET$/i,
|
|
748
|
+
/^SESSION_SECRET$/i,
|
|
749
|
+
/^ENCRYPTION_KEY$/i,
|
|
654
750
|
/^STRIPE_SECRET_KEY$/i,
|
|
655
751
|
/^STRIPE_WEBHOOK_SECRET$/i,
|
|
656
|
-
|
|
752
|
+
/^OPENAI_API_KEY$/i,
|
|
753
|
+
/^ANTHROPIC_API_KEY$/i,
|
|
754
|
+
];
|
|
755
|
+
|
|
756
|
+
// Medium-confidence required patterns
|
|
757
|
+
const mediumConfidenceRequired = [
|
|
758
|
+
/SECRET$/i,
|
|
657
759
|
/TOKEN$/i,
|
|
658
760
|
/API_KEY$/i,
|
|
761
|
+
/PRIVATE_KEY$/i,
|
|
762
|
+
/PASSWORD$/i,
|
|
763
|
+
/CREDENTIALS$/i,
|
|
764
|
+
];
|
|
765
|
+
|
|
766
|
+
// Patterns that indicate optional env vars
|
|
767
|
+
const optionalPatterns = [
|
|
768
|
+
/^DEBUG/i,
|
|
769
|
+
/^LOG_/i,
|
|
770
|
+
/^ENABLE_/i,
|
|
771
|
+
/^DISABLE_/i,
|
|
772
|
+
/^FEATURE_/i,
|
|
773
|
+
/^FLAG_/i,
|
|
774
|
+
/^ANALYTICS/i,
|
|
775
|
+
/^TELEMETRY/i,
|
|
776
|
+
/^SENTRY/i,
|
|
777
|
+
/^PORT$/i,
|
|
778
|
+
/^HOST$/i,
|
|
779
|
+
/^NODE_ENV$/i,
|
|
659
780
|
];
|
|
660
|
-
|
|
781
|
+
|
|
782
|
+
// Check optional first (overrides required)
|
|
783
|
+
if (optionalPatterns.some(p => p.test(name))) {
|
|
784
|
+
return false;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// Check high-confidence required
|
|
788
|
+
if (highConfidenceRequired.some(p => p.test(name))) {
|
|
789
|
+
return true;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// Check medium-confidence required
|
|
793
|
+
if (mediumConfidenceRequired.some(p => p.test(name))) {
|
|
794
|
+
return true;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Check if an env var usage suggests it's optional
|
|
802
|
+
*/
|
|
803
|
+
function hasOptionalUsagePattern(code) {
|
|
804
|
+
const optionalPatterns = [
|
|
805
|
+
/\|\|\s*['"]?undefined['"]?/, // || undefined
|
|
806
|
+
/\?\?\s*['"]?undefined['"]?/, // ?? undefined
|
|
807
|
+
/\|\|\s*null/, // || null
|
|
808
|
+
/\?\?\s*null/, // ?? null
|
|
809
|
+
/\|\|\s*false/, // || false
|
|
810
|
+
/\?\?\s*false/, // ?? false
|
|
811
|
+
/if\s*\(\s*process\.env\./, // Conditional usage
|
|
812
|
+
/process\.env\.\w+\s*\?\s*\./, // Optional chaining
|
|
813
|
+
];
|
|
814
|
+
|
|
815
|
+
return optionalPatterns.some(p => p.test(code));
|
|
661
816
|
}
|
|
662
817
|
|
|
663
818
|
// =============================================================================
|
|
@@ -700,4 +855,6 @@ module.exports = {
|
|
|
700
855
|
routeMatches,
|
|
701
856
|
matchPattern,
|
|
702
857
|
isLikelyRequired,
|
|
858
|
+
isDynamicSegment,
|
|
859
|
+
hasOptionalUsagePattern,
|
|
703
860
|
};
|
|
@@ -141,6 +141,50 @@ const ENTITLEMENTS = {
|
|
|
141
141
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
142
142
|
"security": { minTier: "pro" },
|
|
143
143
|
|
|
144
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
145
|
+
// AI FEATURES - Token/Cost Controls
|
|
146
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
147
|
+
"ai": { minTier: "starter", downgrade: "ai.none" },
|
|
148
|
+
"ai.none": { minTier: "free", caps: { free: "No AI features - upgrade for AI" } },
|
|
149
|
+
"ai.starter": {
|
|
150
|
+
minTier: "starter",
|
|
151
|
+
caps: {
|
|
152
|
+
starter: {
|
|
153
|
+
model: "gpt-3.5-turbo", // Cheaper model for Starter
|
|
154
|
+
maxCallsPerMonth: 10, // 10 AI calls/month
|
|
155
|
+
maxInputTokensPerRequest: 2000, // ~$0.002 max input cost
|
|
156
|
+
maxOutputTokensPerRequest: 1000, // ~$0.002 max output cost
|
|
157
|
+
maxCostPerMonth: 0.50, // $0.50 max AI spend/month
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"ai.pro": {
|
|
162
|
+
minTier: "pro",
|
|
163
|
+
caps: {
|
|
164
|
+
pro: {
|
|
165
|
+
model: "gpt-4-turbo-preview", // Premium model for Pro
|
|
166
|
+
alternateModel: "claude-3-5-sonnet-20241022", // Fallback
|
|
167
|
+
maxCallsPerMonth: 50, // 50 AI calls/month
|
|
168
|
+
maxInputTokensPerRequest: 4000, // ~$0.04 max input cost
|
|
169
|
+
maxOutputTokensPerRequest: 2000, // ~$0.06 max output cost
|
|
170
|
+
maxCostPerMonth: 5.00, // $5.00 max AI spend/month
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"ai.compliance": {
|
|
175
|
+
minTier: "compliance",
|
|
176
|
+
caps: {
|
|
177
|
+
compliance: {
|
|
178
|
+
model: "gpt-4-turbo-preview",
|
|
179
|
+
alternateModel: "claude-3-5-sonnet-20241022",
|
|
180
|
+
maxCallsPerMonth: 500, // 500 AI calls/month
|
|
181
|
+
maxInputTokensPerRequest: 8000,
|
|
182
|
+
maxOutputTokensPerRequest: 4000,
|
|
183
|
+
maxCostPerMonth: 50.00, // $50 max AI spend/month
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
|
|
144
188
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
145
189
|
// PRO ONLY
|
|
146
190
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -155,7 +199,10 @@ const ENTITLEMENTS = {
|
|
|
155
199
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
156
200
|
"gate": { minTier: "starter" },
|
|
157
201
|
"pr": { minTier: "starter" },
|
|
158
|
-
"badge": { minTier: "starter" },
|
|
202
|
+
"badge": { minTier: "starter", downgrade: "badge.basic" },
|
|
203
|
+
"badge.basic": { minTier: "starter" }, // Basic badge (STARTER)
|
|
204
|
+
"badge.verified": { minTier: "pro" }, // Verified badge with seal (PRO)
|
|
205
|
+
"fix_hints": { minTier: "starter" }, // Fix hints/missions in output
|
|
159
206
|
"launch": { minTier: "starter" },
|
|
160
207
|
"dashboard_sync": { minTier: "starter" },
|
|
161
208
|
|