vibecheck-ai 1.1.0 → 1.1.2
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 +367 -204
- package/dist/index.js +3115 -187
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8,12 +8,12 @@ import fs102__default, { readFile, copyFile, unlink, stat, mkdir, readdir, write
|
|
|
8
8
|
import * as os5 from 'os';
|
|
9
9
|
import os5__default from 'os';
|
|
10
10
|
import * as crypto52 from 'crypto';
|
|
11
|
-
import {
|
|
11
|
+
import crypto52__default, { createHash, randomUUID, createHmac } from 'crypto';
|
|
12
12
|
import { glob } from 'glob';
|
|
13
13
|
import fg from 'fast-glob';
|
|
14
14
|
import * as fs15 from 'fs';
|
|
15
|
-
import fs15__default, { existsSync, promises, realpathSync } from 'fs';
|
|
16
|
-
import {
|
|
15
|
+
import fs15__default, { existsSync, promises, readFileSync, realpathSync } from 'fs';
|
|
16
|
+
import { execSync, spawn, exec, execFileSync } from 'child_process';
|
|
17
17
|
import { promisify } from 'util';
|
|
18
18
|
import { gzip, gunzip } from 'zlib';
|
|
19
19
|
import * as net from 'net';
|
|
@@ -21,7 +21,7 @@ import { isIP } from 'net';
|
|
|
21
21
|
import * as http from 'http';
|
|
22
22
|
import * as https from 'https';
|
|
23
23
|
import { EventEmitter } from 'events';
|
|
24
|
-
import * as
|
|
24
|
+
import * as parser2 from '@babel/parser';
|
|
25
25
|
import _traverse from '@babel/traverse';
|
|
26
26
|
import * as t from '@babel/types';
|
|
27
27
|
import { Command, InvalidArgumentError } from 'commander';
|
|
@@ -332,6 +332,18 @@ function getErrorDefaults(code) {
|
|
|
332
332
|
severity: "warning",
|
|
333
333
|
recoverable: true
|
|
334
334
|
},
|
|
335
|
+
PROJECT_ID_REQUIRED: {
|
|
336
|
+
message: "Could not determine project ID",
|
|
337
|
+
suggestions: [
|
|
338
|
+
"Set VIBECHECK_PROJECT_ID environment variable",
|
|
339
|
+
'Add "projectId" to your vibecheck.config.mjs',
|
|
340
|
+
"Use --project-id flag when running the command",
|
|
341
|
+
"Initialize a git repository with a remote: git remote add origin <url>",
|
|
342
|
+
'Add a "name" field to your package.json'
|
|
343
|
+
],
|
|
344
|
+
severity: "error",
|
|
345
|
+
recoverable: false
|
|
346
|
+
},
|
|
335
347
|
UNKNOWN_ERROR: {
|
|
336
348
|
message: "An unexpected error occurred",
|
|
337
349
|
suggestions: [
|
|
@@ -612,8 +624,8 @@ async function loadConfig(configPath, options) {
|
|
|
612
624
|
const parsed = configSchema.safeParse(result.config);
|
|
613
625
|
if (!parsed.success) {
|
|
614
626
|
const errors = parsed.error.errors.map((e) => {
|
|
615
|
-
const
|
|
616
|
-
return ` \u2022 ${
|
|
627
|
+
const path34 = e.path.join(".");
|
|
628
|
+
return ` \u2022 ${path34 || "root"}: ${e.message}`;
|
|
617
629
|
}).join("\n");
|
|
618
630
|
throw new VibeCheckError(
|
|
619
631
|
`Invalid configuration in ${result.filepath}:
|
|
@@ -694,8 +706,8 @@ function validateConfig(config) {
|
|
|
694
706
|
return {
|
|
695
707
|
valid: false,
|
|
696
708
|
errors: result.error.errors.map((e) => {
|
|
697
|
-
const
|
|
698
|
-
return `${
|
|
709
|
+
const path34 = e.path.join(".");
|
|
710
|
+
return `${path34 || "root"}: ${e.message}`;
|
|
699
711
|
})
|
|
700
712
|
};
|
|
701
713
|
}
|
|
@@ -1339,7 +1351,7 @@ async function loginWithApiKey(apiKey, options = {}) {
|
|
|
1339
1351
|
try {
|
|
1340
1352
|
const response = await fetch(`${apiUrl}/api/v1/auth/me`, {
|
|
1341
1353
|
headers: {
|
|
1342
|
-
"
|
|
1354
|
+
"X-API-Key": apiKey
|
|
1343
1355
|
}
|
|
1344
1356
|
});
|
|
1345
1357
|
if (!response.ok) {
|
|
@@ -1348,7 +1360,8 @@ async function loginWithApiKey(apiKey, options = {}) {
|
|
|
1348
1360
|
}
|
|
1349
1361
|
return { success: false, error: `Authentication failed: ${response.status}` };
|
|
1350
1362
|
}
|
|
1351
|
-
const
|
|
1363
|
+
const result = await response.json();
|
|
1364
|
+
const userData = result.data;
|
|
1352
1365
|
const credentials = {
|
|
1353
1366
|
authToken: apiKey,
|
|
1354
1367
|
apiUrl,
|
|
@@ -1496,8 +1509,8 @@ async function loginWithOAuth(provider, options = {}) {
|
|
|
1496
1509
|
});
|
|
1497
1510
|
authUrl = `https://accounts.google.com/o/oauth2/v2/auth?${params}`;
|
|
1498
1511
|
}
|
|
1499
|
-
const
|
|
1500
|
-
await
|
|
1512
|
+
const open3 = (await import('open')).default;
|
|
1513
|
+
await open3(authUrl);
|
|
1501
1514
|
});
|
|
1502
1515
|
setTimeout(() => {
|
|
1503
1516
|
server.close();
|
|
@@ -8368,7 +8381,7 @@ async function parseSource(filePath, content) {
|
|
|
8368
8381
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
8369
8382
|
try {
|
|
8370
8383
|
if (ext === "ts" || ext === "tsx" || ext === "js" || ext === "jsx" || ext === "mjs" || ext === "cjs") {
|
|
8371
|
-
const { parse:
|
|
8384
|
+
const { parse: parse3 } = await import('@babel/parser');
|
|
8372
8385
|
const plugins = [
|
|
8373
8386
|
"decorators-legacy",
|
|
8374
8387
|
"classProperties",
|
|
@@ -8380,7 +8393,7 @@ async function parseSource(filePath, content) {
|
|
|
8380
8393
|
if (ext === "tsx" || ext === "jsx") {
|
|
8381
8394
|
plugins.push("jsx");
|
|
8382
8395
|
}
|
|
8383
|
-
ast =
|
|
8396
|
+
ast = parse3(content, {
|
|
8384
8397
|
sourceType: "module",
|
|
8385
8398
|
plugins,
|
|
8386
8399
|
errorRecovery: true
|
|
@@ -10229,8 +10242,8 @@ var init_chunk_ZJNDZV2M = __esm({
|
|
|
10229
10242
|
/**
|
|
10230
10243
|
* Check if a route path is sensitive and should require auth
|
|
10231
10244
|
*/
|
|
10232
|
-
isSensitiveRoute(
|
|
10233
|
-
return SENSITIVE_ROUTE_PATTERNS.some((pattern) => pattern.test(
|
|
10245
|
+
isSensitiveRoute(path34) {
|
|
10246
|
+
return SENSITIVE_ROUTE_PATTERNS.some((pattern) => pattern.test(path34));
|
|
10234
10247
|
}
|
|
10235
10248
|
/**
|
|
10236
10249
|
* Check if a specific route is protected
|
|
@@ -10251,12 +10264,12 @@ var init_chunk_ZJNDZV2M = __esm({
|
|
|
10251
10264
|
/**
|
|
10252
10265
|
* Check if a path matches a pattern (supports wildcards)
|
|
10253
10266
|
*/
|
|
10254
|
-
matchesPattern(
|
|
10267
|
+
matchesPattern(path34, pattern) {
|
|
10255
10268
|
if (pattern.includes("*")) {
|
|
10256
10269
|
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
10257
|
-
return regex.test(
|
|
10270
|
+
return regex.test(path34);
|
|
10258
10271
|
}
|
|
10259
|
-
return
|
|
10272
|
+
return path34.startsWith(pattern);
|
|
10260
10273
|
}
|
|
10261
10274
|
};
|
|
10262
10275
|
shipScoreCalculator = createShipScoreCalculator();
|
|
@@ -10549,10 +10562,10 @@ function generateCheckStatus(score, failures = []) {
|
|
|
10549
10562
|
}
|
|
10550
10563
|
}
|
|
10551
10564
|
const annotations = failures.filter((f) => f.target && f.target.includes(":")).slice(0, 50).map((f) => {
|
|
10552
|
-
const [
|
|
10565
|
+
const [path34, lineStr] = f.target.split(":");
|
|
10553
10566
|
const line = parseInt(lineStr, 10) || 1;
|
|
10554
10567
|
return {
|
|
10555
|
-
path:
|
|
10568
|
+
path: path34,
|
|
10556
10569
|
startLine: line,
|
|
10557
10570
|
annotationLevel: f.severity === "critical" || f.severity === "high" ? "failure" : "warning",
|
|
10558
10571
|
message: f.message,
|
|
@@ -11393,18 +11406,18 @@ var init_chunk_EDYODCBV = __esm({
|
|
|
11393
11406
|
const n = b.length;
|
|
11394
11407
|
let prev = new Array(n + 1).fill(0);
|
|
11395
11408
|
let curr = new Array(n + 1).fill(0);
|
|
11396
|
-
const
|
|
11409
|
+
const path34 = Array(m + 1).fill(null).map(() => Array(n + 1).fill("up"));
|
|
11397
11410
|
for (let i2 = 1; i2 <= m; i2++) {
|
|
11398
11411
|
for (let j2 = 1; j2 <= n; j2++) {
|
|
11399
11412
|
if (a[i2 - 1] === b[j2 - 1]) {
|
|
11400
11413
|
curr[j2] = prev[j2 - 1] + 1;
|
|
11401
|
-
|
|
11414
|
+
path34[i2][j2] = "diag";
|
|
11402
11415
|
} else if (prev[j2] >= curr[j2 - 1]) {
|
|
11403
11416
|
curr[j2] = prev[j2];
|
|
11404
|
-
|
|
11417
|
+
path34[i2][j2] = "up";
|
|
11405
11418
|
} else {
|
|
11406
11419
|
curr[j2] = curr[j2 - 1];
|
|
11407
|
-
|
|
11420
|
+
path34[i2][j2] = "left";
|
|
11408
11421
|
}
|
|
11409
11422
|
}
|
|
11410
11423
|
[prev, curr] = [curr, prev];
|
|
@@ -11414,11 +11427,11 @@ var init_chunk_EDYODCBV = __esm({
|
|
|
11414
11427
|
let i = m;
|
|
11415
11428
|
let j = n;
|
|
11416
11429
|
while (i > 0 && j > 0) {
|
|
11417
|
-
if (
|
|
11430
|
+
if (path34[i][j] === "diag") {
|
|
11418
11431
|
lcs.unshift(a[i - 1]);
|
|
11419
11432
|
i--;
|
|
11420
11433
|
j--;
|
|
11421
|
-
} else if (
|
|
11434
|
+
} else if (path34[i][j] === "up") {
|
|
11422
11435
|
i--;
|
|
11423
11436
|
} else {
|
|
11424
11437
|
j--;
|
|
@@ -13755,8 +13768,8 @@ async function htmlToPdf(html, options = {}) {
|
|
|
13755
13768
|
</div>` : void 0
|
|
13756
13769
|
});
|
|
13757
13770
|
if (options.outputPath) {
|
|
13758
|
-
const
|
|
13759
|
-
await
|
|
13771
|
+
const fs28 = await import('fs/promises');
|
|
13772
|
+
await fs28.writeFile(options.outputPath, pdfBuffer);
|
|
13760
13773
|
return { path: options.outputPath };
|
|
13761
13774
|
}
|
|
13762
13775
|
return { buffer: pdfBuffer };
|
|
@@ -33680,6 +33693,547 @@ var init_chunk_M6OHTAX3 = __esm({
|
|
|
33680
33693
|
circuitBreakers = /* @__PURE__ */ new Map();
|
|
33681
33694
|
}
|
|
33682
33695
|
});
|
|
33696
|
+
function getIntentExtractor(config) {
|
|
33697
|
+
if (!globalExtractor || config) {
|
|
33698
|
+
globalExtractor = new IntentExtractor(config);
|
|
33699
|
+
}
|
|
33700
|
+
return globalExtractor;
|
|
33701
|
+
}
|
|
33702
|
+
function resetIntentExtractor() {
|
|
33703
|
+
globalExtractor = null;
|
|
33704
|
+
}
|
|
33705
|
+
function extractIntent(prompt) {
|
|
33706
|
+
return getIntentExtractor().extract(prompt);
|
|
33707
|
+
}
|
|
33708
|
+
function extractIntentSync(prompt) {
|
|
33709
|
+
return getIntentExtractor().extractBaseline(prompt);
|
|
33710
|
+
}
|
|
33711
|
+
function createScopeCreepDetector(projectRoot, config, existingDirectories) {
|
|
33712
|
+
return new ScopeCreepDetector(projectRoot, config, existingDirectories);
|
|
33713
|
+
}
|
|
33714
|
+
function evaluateOperation(intentDescription, operation, projectRoot, config) {
|
|
33715
|
+
const detector = new ScopeCreepDetector(projectRoot, config);
|
|
33716
|
+
const plan = detector.createChangePlan({ intentDescription });
|
|
33717
|
+
return detector.evaluateSingleOperation(plan, operation);
|
|
33718
|
+
}
|
|
33719
|
+
function extractCallsFromSource(source, filePath, options = {}) {
|
|
33720
|
+
const opts = { ...DEFAULT_OPTIONS6, ...options };
|
|
33721
|
+
const startTime = performance.now();
|
|
33722
|
+
const result = {
|
|
33723
|
+
file: filePath,
|
|
33724
|
+
calls: [],
|
|
33725
|
+
imports: [],
|
|
33726
|
+
dynamicCalls: [],
|
|
33727
|
+
unresolvedSymbols: [],
|
|
33728
|
+
errors: [],
|
|
33729
|
+
metadata: {
|
|
33730
|
+
totalCalls: 0,
|
|
33731
|
+
totalImports: 0,
|
|
33732
|
+
dynamicCallCount: 0,
|
|
33733
|
+
unresolvedCount: 0,
|
|
33734
|
+
analysisTimeMs: 0
|
|
33735
|
+
}
|
|
33736
|
+
};
|
|
33737
|
+
const definedSymbols = /* @__PURE__ */ new Set();
|
|
33738
|
+
const usedSymbols = /* @__PURE__ */ new Set();
|
|
33739
|
+
let ast;
|
|
33740
|
+
try {
|
|
33741
|
+
ast = parser2.parse(source, PARSER_OPTIONS);
|
|
33742
|
+
} catch (error) {
|
|
33743
|
+
result.errors.push(
|
|
33744
|
+
`Parse error: ${error instanceof Error ? error.message : String(error)}`
|
|
33745
|
+
);
|
|
33746
|
+
result.metadata.analysisTimeMs = performance.now() - startTime;
|
|
33747
|
+
return result;
|
|
33748
|
+
}
|
|
33749
|
+
if (ast.errors && ast.errors.length > 0) {
|
|
33750
|
+
for (const err of ast.errors) {
|
|
33751
|
+
result.errors.push(`Parse warning: ${err.message}`);
|
|
33752
|
+
}
|
|
33753
|
+
}
|
|
33754
|
+
try {
|
|
33755
|
+
_traverse(ast, {
|
|
33756
|
+
// ========================================
|
|
33757
|
+
// Import Declarations
|
|
33758
|
+
// ========================================
|
|
33759
|
+
ImportDeclaration(path34) {
|
|
33760
|
+
const node = path34.node;
|
|
33761
|
+
const importInfo = extractImportInfo(node);
|
|
33762
|
+
if (!opts.includeTypeImports && importInfo.isTypeOnly) {
|
|
33763
|
+
return;
|
|
33764
|
+
}
|
|
33765
|
+
result.imports.push(importInfo);
|
|
33766
|
+
if (importInfo.defaultImport) {
|
|
33767
|
+
definedSymbols.add(importInfo.defaultImport);
|
|
33768
|
+
}
|
|
33769
|
+
if (importInfo.namespaceImport) {
|
|
33770
|
+
definedSymbols.add(importInfo.namespaceImport);
|
|
33771
|
+
}
|
|
33772
|
+
for (const named of importInfo.namedImports) {
|
|
33773
|
+
const localName = named.includes(" as ") ? named.split(" as ")[1].trim() : named;
|
|
33774
|
+
definedSymbols.add(localName);
|
|
33775
|
+
}
|
|
33776
|
+
},
|
|
33777
|
+
// ========================================
|
|
33778
|
+
// Dynamic Imports
|
|
33779
|
+
// ========================================
|
|
33780
|
+
"CallExpression|OptionalCallExpression"(path34) {
|
|
33781
|
+
const node = path34.node;
|
|
33782
|
+
if (node.callee.type === "Import") {
|
|
33783
|
+
const arg = node.arguments[0];
|
|
33784
|
+
let modulePath = "<dynamic>";
|
|
33785
|
+
if (arg?.type === "StringLiteral") {
|
|
33786
|
+
modulePath = arg.value;
|
|
33787
|
+
}
|
|
33788
|
+
result.imports.push({
|
|
33789
|
+
modulePath,
|
|
33790
|
+
namedImports: [],
|
|
33791
|
+
isTypeOnly: false,
|
|
33792
|
+
location: {
|
|
33793
|
+
line: node.loc?.start.line ?? 0,
|
|
33794
|
+
column: node.loc?.start.column ?? 0
|
|
33795
|
+
},
|
|
33796
|
+
isRelative: modulePath.startsWith("."),
|
|
33797
|
+
isDynamic: true
|
|
33798
|
+
});
|
|
33799
|
+
}
|
|
33800
|
+
const callInfo = extractCallInfo(node, opts.maxChainDepth ?? 10);
|
|
33801
|
+
if (callInfo) {
|
|
33802
|
+
if (callInfo.isDynamic) {
|
|
33803
|
+
result.dynamicCalls.push(callInfo);
|
|
33804
|
+
} else {
|
|
33805
|
+
result.calls.push(callInfo);
|
|
33806
|
+
}
|
|
33807
|
+
if (callInfo.receiver) {
|
|
33808
|
+
usedSymbols.add(callInfo.receiver);
|
|
33809
|
+
} else if (!callInfo.isDynamic) {
|
|
33810
|
+
usedSymbols.add(callInfo.name);
|
|
33811
|
+
}
|
|
33812
|
+
}
|
|
33813
|
+
},
|
|
33814
|
+
// ========================================
|
|
33815
|
+
// Track Defined Symbols
|
|
33816
|
+
// ========================================
|
|
33817
|
+
VariableDeclarator(path34) {
|
|
33818
|
+
const id = path34.node.id;
|
|
33819
|
+
if (id.type === "Identifier") {
|
|
33820
|
+
definedSymbols.add(id.name);
|
|
33821
|
+
} else if (id.type === "ObjectPattern") {
|
|
33822
|
+
for (const prop of id.properties) {
|
|
33823
|
+
if (prop.type === "ObjectProperty" && prop.value.type === "Identifier") {
|
|
33824
|
+
definedSymbols.add(prop.value.name);
|
|
33825
|
+
} else if (prop.type === "RestElement" && prop.argument.type === "Identifier") {
|
|
33826
|
+
definedSymbols.add(prop.argument.name);
|
|
33827
|
+
}
|
|
33828
|
+
}
|
|
33829
|
+
} else if (id.type === "ArrayPattern") {
|
|
33830
|
+
for (const elem of id.elements) {
|
|
33831
|
+
if (elem?.type === "Identifier") {
|
|
33832
|
+
definedSymbols.add(elem.name);
|
|
33833
|
+
}
|
|
33834
|
+
}
|
|
33835
|
+
}
|
|
33836
|
+
},
|
|
33837
|
+
FunctionDeclaration(path34) {
|
|
33838
|
+
if (path34.node.id) {
|
|
33839
|
+
definedSymbols.add(path34.node.id.name);
|
|
33840
|
+
}
|
|
33841
|
+
for (const param of path34.node.params) {
|
|
33842
|
+
if (param.type === "Identifier") {
|
|
33843
|
+
definedSymbols.add(param.name);
|
|
33844
|
+
}
|
|
33845
|
+
}
|
|
33846
|
+
},
|
|
33847
|
+
ClassDeclaration(path34) {
|
|
33848
|
+
if (path34.node.id) {
|
|
33849
|
+
definedSymbols.add(path34.node.id.name);
|
|
33850
|
+
}
|
|
33851
|
+
},
|
|
33852
|
+
// Arrow functions assigned to const
|
|
33853
|
+
ArrowFunctionExpression(path34) {
|
|
33854
|
+
for (const param of path34.node.params) {
|
|
33855
|
+
if (param.type === "Identifier") {
|
|
33856
|
+
definedSymbols.add(param.name);
|
|
33857
|
+
}
|
|
33858
|
+
}
|
|
33859
|
+
},
|
|
33860
|
+
// Type declarations (TypeScript)
|
|
33861
|
+
TSTypeAliasDeclaration(path34) {
|
|
33862
|
+
definedSymbols.add(path34.node.id.name);
|
|
33863
|
+
},
|
|
33864
|
+
TSInterfaceDeclaration(path34) {
|
|
33865
|
+
definedSymbols.add(path34.node.id.name);
|
|
33866
|
+
},
|
|
33867
|
+
TSEnumDeclaration(path34) {
|
|
33868
|
+
definedSymbols.add(path34.node.id.name);
|
|
33869
|
+
}
|
|
33870
|
+
});
|
|
33871
|
+
} catch (error) {
|
|
33872
|
+
result.errors.push(
|
|
33873
|
+
`Traversal error: ${error instanceof Error ? error.message : String(error)}`
|
|
33874
|
+
);
|
|
33875
|
+
}
|
|
33876
|
+
if (opts.trackUnresolvedSymbols) {
|
|
33877
|
+
const builtins = getBuiltinGlobals();
|
|
33878
|
+
for (const symbol of usedSymbols) {
|
|
33879
|
+
if (!definedSymbols.has(symbol) && !builtins.has(symbol)) {
|
|
33880
|
+
result.unresolvedSymbols.push(symbol);
|
|
33881
|
+
}
|
|
33882
|
+
}
|
|
33883
|
+
}
|
|
33884
|
+
result.metadata = {
|
|
33885
|
+
totalCalls: result.calls.length,
|
|
33886
|
+
totalImports: result.imports.length,
|
|
33887
|
+
dynamicCallCount: result.dynamicCalls.length,
|
|
33888
|
+
unresolvedCount: result.unresolvedSymbols.length,
|
|
33889
|
+
analysisTimeMs: performance.now() - startTime
|
|
33890
|
+
};
|
|
33891
|
+
return result;
|
|
33892
|
+
}
|
|
33893
|
+
function extractImportInfo(node) {
|
|
33894
|
+
const info = {
|
|
33895
|
+
modulePath: node.source.value,
|
|
33896
|
+
namedImports: [],
|
|
33897
|
+
isTypeOnly: node.importKind === "type",
|
|
33898
|
+
location: {
|
|
33899
|
+
line: node.loc?.start.line ?? 0,
|
|
33900
|
+
column: node.loc?.start.column ?? 0
|
|
33901
|
+
},
|
|
33902
|
+
isRelative: node.source.value.startsWith(".") || node.source.value.startsWith("/"),
|
|
33903
|
+
isDynamic: false
|
|
33904
|
+
};
|
|
33905
|
+
for (const specifier of node.specifiers) {
|
|
33906
|
+
if (specifier.type === "ImportDefaultSpecifier") {
|
|
33907
|
+
info.defaultImport = specifier.local.name;
|
|
33908
|
+
} else if (specifier.type === "ImportNamespaceSpecifier") {
|
|
33909
|
+
info.namespaceImport = specifier.local.name;
|
|
33910
|
+
} else if (specifier.type === "ImportSpecifier") {
|
|
33911
|
+
const imported = specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value;
|
|
33912
|
+
const local = specifier.local.name;
|
|
33913
|
+
if (imported !== local) {
|
|
33914
|
+
info.namedImports.push(`${imported} as ${local}`);
|
|
33915
|
+
} else {
|
|
33916
|
+
info.namedImports.push(imported);
|
|
33917
|
+
}
|
|
33918
|
+
if (specifier.importKind === "type") {
|
|
33919
|
+
info.isTypeOnly = true;
|
|
33920
|
+
}
|
|
33921
|
+
}
|
|
33922
|
+
}
|
|
33923
|
+
return info;
|
|
33924
|
+
}
|
|
33925
|
+
function extractCallInfo(node, maxChainDepth) {
|
|
33926
|
+
const callee = node.callee;
|
|
33927
|
+
let isDynamic = false;
|
|
33928
|
+
let name = "";
|
|
33929
|
+
let receiver;
|
|
33930
|
+
let fullExpression = "";
|
|
33931
|
+
let isOptionalChain = node.type === "OptionalCallExpression";
|
|
33932
|
+
if (callee.type === "Identifier") {
|
|
33933
|
+
name = callee.name;
|
|
33934
|
+
fullExpression = callee.name;
|
|
33935
|
+
} else if (callee.type === "MemberExpression" || callee.type === "OptionalMemberExpression") {
|
|
33936
|
+
const chain = extractMemberChain(callee, maxChainDepth);
|
|
33937
|
+
fullExpression = chain.expression;
|
|
33938
|
+
name = chain.property;
|
|
33939
|
+
receiver = chain.receiver;
|
|
33940
|
+
isDynamic = chain.hasComputed;
|
|
33941
|
+
isOptionalChain = isOptionalChain || chain.isOptional;
|
|
33942
|
+
} else if (callee.type === "CallExpression" || callee.type === "OptionalCallExpression") {
|
|
33943
|
+
fullExpression = "<chained-call>";
|
|
33944
|
+
name = "<chained>";
|
|
33945
|
+
isDynamic = true;
|
|
33946
|
+
} else if (callee.type === "Import") {
|
|
33947
|
+
return null;
|
|
33948
|
+
} else {
|
|
33949
|
+
isDynamic = true;
|
|
33950
|
+
fullExpression = "<dynamic>";
|
|
33951
|
+
name = "<dynamic>";
|
|
33952
|
+
}
|
|
33953
|
+
const keywords = ["if", "while", "for", "switch", "catch", "function", "return", "throw", "new"];
|
|
33954
|
+
if (keywords.includes(name)) {
|
|
33955
|
+
return null;
|
|
33956
|
+
}
|
|
33957
|
+
return {
|
|
33958
|
+
name,
|
|
33959
|
+
argsCount: node.arguments.length,
|
|
33960
|
+
location: {
|
|
33961
|
+
line: node.loc?.start.line ?? 0,
|
|
33962
|
+
column: node.loc?.start.column ?? 0
|
|
33963
|
+
},
|
|
33964
|
+
isDynamic,
|
|
33965
|
+
receiver,
|
|
33966
|
+
fullExpression,
|
|
33967
|
+
isOptionalChain
|
|
33968
|
+
};
|
|
33969
|
+
}
|
|
33970
|
+
function extractMemberChain(node, maxDepth) {
|
|
33971
|
+
const parts = [];
|
|
33972
|
+
let hasComputed = false;
|
|
33973
|
+
let isOptional = false;
|
|
33974
|
+
let current = node;
|
|
33975
|
+
let depth = 0;
|
|
33976
|
+
while (depth < maxDepth && (current.type === "MemberExpression" || current.type === "OptionalMemberExpression")) {
|
|
33977
|
+
const member = current;
|
|
33978
|
+
isOptional = isOptional || member.optional === true;
|
|
33979
|
+
if (member.computed) {
|
|
33980
|
+
hasComputed = true;
|
|
33981
|
+
if (member.property.type === "StringLiteral") {
|
|
33982
|
+
parts.unshift(member.property.value);
|
|
33983
|
+
} else if (member.property.type === "NumericLiteral") {
|
|
33984
|
+
parts.unshift(String(member.property.value));
|
|
33985
|
+
} else {
|
|
33986
|
+
parts.unshift("[computed]");
|
|
33987
|
+
}
|
|
33988
|
+
} else if (member.property.type === "Identifier") {
|
|
33989
|
+
parts.unshift(member.property.name);
|
|
33990
|
+
} else if (member.property.type === "PrivateName") {
|
|
33991
|
+
parts.unshift(`#${member.property.id.name}`);
|
|
33992
|
+
}
|
|
33993
|
+
current = member.object;
|
|
33994
|
+
depth++;
|
|
33995
|
+
}
|
|
33996
|
+
if (current.type === "Identifier") {
|
|
33997
|
+
parts.unshift(current.name);
|
|
33998
|
+
} else if (current.type === "ThisExpression") {
|
|
33999
|
+
parts.unshift("this");
|
|
34000
|
+
} else if (current.type === "Super") {
|
|
34001
|
+
parts.unshift("super");
|
|
34002
|
+
} else {
|
|
34003
|
+
parts.unshift("<expr>");
|
|
34004
|
+
hasComputed = true;
|
|
34005
|
+
}
|
|
34006
|
+
return {
|
|
34007
|
+
expression: parts.join("."),
|
|
34008
|
+
property: parts[parts.length - 1] || "<unknown>",
|
|
34009
|
+
receiver: parts.length > 1 ? parts[0] : void 0,
|
|
34010
|
+
hasComputed,
|
|
34011
|
+
isOptional
|
|
34012
|
+
};
|
|
34013
|
+
}
|
|
34014
|
+
function getBuiltinGlobals() {
|
|
34015
|
+
return /* @__PURE__ */ new Set([
|
|
34016
|
+
// JavaScript globals
|
|
34017
|
+
"console",
|
|
34018
|
+
"window",
|
|
34019
|
+
"document",
|
|
34020
|
+
"global",
|
|
34021
|
+
"globalThis",
|
|
34022
|
+
"process",
|
|
34023
|
+
"require",
|
|
34024
|
+
"module",
|
|
34025
|
+
"exports",
|
|
34026
|
+
"__dirname",
|
|
34027
|
+
"__filename",
|
|
34028
|
+
"setTimeout",
|
|
34029
|
+
"setInterval",
|
|
34030
|
+
"clearTimeout",
|
|
34031
|
+
"clearInterval",
|
|
34032
|
+
"setImmediate",
|
|
34033
|
+
"clearImmediate",
|
|
34034
|
+
"queueMicrotask",
|
|
34035
|
+
"fetch",
|
|
34036
|
+
"Request",
|
|
34037
|
+
"Response",
|
|
34038
|
+
"Headers",
|
|
34039
|
+
"URL",
|
|
34040
|
+
"URLSearchParams",
|
|
34041
|
+
"AbortController",
|
|
34042
|
+
"AbortSignal",
|
|
34043
|
+
"Promise",
|
|
34044
|
+
"Proxy",
|
|
34045
|
+
"Reflect",
|
|
34046
|
+
"Array",
|
|
34047
|
+
"Object",
|
|
34048
|
+
"String",
|
|
34049
|
+
"Number",
|
|
34050
|
+
"Boolean",
|
|
34051
|
+
"Symbol",
|
|
34052
|
+
"BigInt",
|
|
34053
|
+
"Map",
|
|
34054
|
+
"Set",
|
|
34055
|
+
"WeakMap",
|
|
34056
|
+
"WeakSet",
|
|
34057
|
+
"WeakRef",
|
|
34058
|
+
"FinalizationRegistry",
|
|
34059
|
+
"Date",
|
|
34060
|
+
"RegExp",
|
|
34061
|
+
"Error",
|
|
34062
|
+
"TypeError",
|
|
34063
|
+
"RangeError",
|
|
34064
|
+
"SyntaxError",
|
|
34065
|
+
"ReferenceError",
|
|
34066
|
+
"EvalError",
|
|
34067
|
+
"URIError",
|
|
34068
|
+
"AggregateError",
|
|
34069
|
+
"JSON",
|
|
34070
|
+
"Math",
|
|
34071
|
+
"Intl",
|
|
34072
|
+
"Atomics",
|
|
34073
|
+
"SharedArrayBuffer",
|
|
34074
|
+
"ArrayBuffer",
|
|
34075
|
+
"DataView",
|
|
34076
|
+
"Int8Array",
|
|
34077
|
+
"Uint8Array",
|
|
34078
|
+
"Uint8ClampedArray",
|
|
34079
|
+
"Int16Array",
|
|
34080
|
+
"Uint16Array",
|
|
34081
|
+
"Int32Array",
|
|
34082
|
+
"Uint32Array",
|
|
34083
|
+
"Float32Array",
|
|
34084
|
+
"Float64Array",
|
|
34085
|
+
"BigInt64Array",
|
|
34086
|
+
"BigUint64Array",
|
|
34087
|
+
"NaN",
|
|
34088
|
+
"Infinity",
|
|
34089
|
+
"undefined",
|
|
34090
|
+
"null",
|
|
34091
|
+
"isNaN",
|
|
34092
|
+
"isFinite",
|
|
34093
|
+
"parseInt",
|
|
34094
|
+
"parseFloat",
|
|
34095
|
+
"encodeURI",
|
|
34096
|
+
"encodeURIComponent",
|
|
34097
|
+
"decodeURI",
|
|
34098
|
+
"decodeURIComponent",
|
|
34099
|
+
"eval",
|
|
34100
|
+
"Function",
|
|
34101
|
+
// Browser APIs
|
|
34102
|
+
"alert",
|
|
34103
|
+
"confirm",
|
|
34104
|
+
"prompt",
|
|
34105
|
+
"atob",
|
|
34106
|
+
"btoa",
|
|
34107
|
+
"requestAnimationFrame",
|
|
34108
|
+
"cancelAnimationFrame",
|
|
34109
|
+
"localStorage",
|
|
34110
|
+
"sessionStorage",
|
|
34111
|
+
"indexedDB",
|
|
34112
|
+
"performance",
|
|
34113
|
+
"navigator",
|
|
34114
|
+
"location",
|
|
34115
|
+
"history",
|
|
34116
|
+
"Event",
|
|
34117
|
+
"CustomEvent",
|
|
34118
|
+
"EventTarget",
|
|
34119
|
+
"Node",
|
|
34120
|
+
"Element",
|
|
34121
|
+
"HTMLElement",
|
|
34122
|
+
"SVGElement",
|
|
34123
|
+
"Text",
|
|
34124
|
+
"Comment",
|
|
34125
|
+
"DocumentFragment",
|
|
34126
|
+
"MutationObserver",
|
|
34127
|
+
"IntersectionObserver",
|
|
34128
|
+
"ResizeObserver",
|
|
34129
|
+
"XMLHttpRequest",
|
|
34130
|
+
"FormData",
|
|
34131
|
+
"Blob",
|
|
34132
|
+
"File",
|
|
34133
|
+
"FileReader",
|
|
34134
|
+
"WebSocket",
|
|
34135
|
+
"Worker",
|
|
34136
|
+
"ServiceWorker",
|
|
34137
|
+
"Image",
|
|
34138
|
+
"Audio",
|
|
34139
|
+
"Video",
|
|
34140
|
+
"Canvas",
|
|
34141
|
+
"ImageData",
|
|
34142
|
+
"TextEncoder",
|
|
34143
|
+
"TextDecoder",
|
|
34144
|
+
"crypto",
|
|
34145
|
+
"Crypto",
|
|
34146
|
+
"SubtleCrypto",
|
|
34147
|
+
"CryptoKey",
|
|
34148
|
+
// Node.js additional
|
|
34149
|
+
"Buffer",
|
|
34150
|
+
"setImmediate",
|
|
34151
|
+
"clearImmediate",
|
|
34152
|
+
// Common testing globals
|
|
34153
|
+
"describe",
|
|
34154
|
+
"it",
|
|
34155
|
+
"test",
|
|
34156
|
+
"expect",
|
|
34157
|
+
"jest",
|
|
34158
|
+
"vi",
|
|
34159
|
+
"beforeEach",
|
|
34160
|
+
"afterEach",
|
|
34161
|
+
"beforeAll",
|
|
34162
|
+
"afterAll",
|
|
34163
|
+
// TypeScript
|
|
34164
|
+
"ReadonlyArray",
|
|
34165
|
+
"Readonly",
|
|
34166
|
+
"Partial",
|
|
34167
|
+
"Required",
|
|
34168
|
+
"Pick",
|
|
34169
|
+
"Omit",
|
|
34170
|
+
"Record",
|
|
34171
|
+
"Exclude",
|
|
34172
|
+
"Extract",
|
|
34173
|
+
"NonNullable",
|
|
34174
|
+
"Parameters",
|
|
34175
|
+
"ConstructorParameters",
|
|
34176
|
+
"ReturnType",
|
|
34177
|
+
"InstanceType",
|
|
34178
|
+
"ThisType",
|
|
34179
|
+
// React (commonly used)
|
|
34180
|
+
"React",
|
|
34181
|
+
"ReactDOM",
|
|
34182
|
+
// Import meta
|
|
34183
|
+
"import"
|
|
34184
|
+
]);
|
|
34185
|
+
}
|
|
34186
|
+
function validateCallAgainstImports(call, imports) {
|
|
34187
|
+
const symbolToCheck = call.receiver ?? call.name;
|
|
34188
|
+
for (const imp of imports) {
|
|
34189
|
+
if (imp.defaultImport === symbolToCheck) {
|
|
34190
|
+
return { valid: true, matchedImport: imp };
|
|
34191
|
+
}
|
|
34192
|
+
if (imp.namespaceImport === symbolToCheck) {
|
|
34193
|
+
return { valid: true, matchedImport: imp };
|
|
34194
|
+
}
|
|
34195
|
+
for (const named of imp.namedImports) {
|
|
34196
|
+
const localName = named.includes(" as ") ? named.split(" as ")[1].trim() : named;
|
|
34197
|
+
if (localName === symbolToCheck) {
|
|
34198
|
+
return { valid: true, matchedImport: imp };
|
|
34199
|
+
}
|
|
34200
|
+
}
|
|
34201
|
+
}
|
|
34202
|
+
if (getBuiltinGlobals().has(symbolToCheck)) {
|
|
34203
|
+
return { valid: true, reason: "builtin global" };
|
|
34204
|
+
}
|
|
34205
|
+
return { valid: false, reason: `Symbol '${symbolToCheck}' is not imported or defined` };
|
|
34206
|
+
}
|
|
34207
|
+
function detectPhantomCalls(analysis) {
|
|
34208
|
+
const phantoms = [];
|
|
34209
|
+
const importedSymbols = /* @__PURE__ */ new Set();
|
|
34210
|
+
const builtins = getBuiltinGlobals();
|
|
34211
|
+
for (const imp of analysis.imports) {
|
|
34212
|
+
if (imp.defaultImport) {
|
|
34213
|
+
importedSymbols.add(imp.defaultImport);
|
|
34214
|
+
}
|
|
34215
|
+
if (imp.namespaceImport) {
|
|
34216
|
+
importedSymbols.add(imp.namespaceImport);
|
|
34217
|
+
}
|
|
34218
|
+
for (const named of imp.namedImports) {
|
|
34219
|
+
const localName = named.includes(" as ") ? named.split(" as ")[1].trim() : named;
|
|
34220
|
+
importedSymbols.add(localName);
|
|
34221
|
+
}
|
|
34222
|
+
}
|
|
34223
|
+
for (const call of analysis.calls) {
|
|
34224
|
+
const symbolToCheck = call.receiver ?? call.name;
|
|
34225
|
+
if (call.isDynamic) continue;
|
|
34226
|
+
if (builtins.has(symbolToCheck)) continue;
|
|
34227
|
+
if (symbolToCheck === "this" || symbolToCheck === "super") continue;
|
|
34228
|
+
if (!importedSymbols.has(symbolToCheck)) {
|
|
34229
|
+
const looksExternal = symbolToCheck[0] === symbolToCheck[0].toUpperCase() || call.fullExpression.includes(".");
|
|
34230
|
+
if (looksExternal) {
|
|
34231
|
+
phantoms.push(call);
|
|
34232
|
+
}
|
|
34233
|
+
}
|
|
34234
|
+
}
|
|
34235
|
+
return phantoms;
|
|
34236
|
+
}
|
|
33683
34237
|
function isValidSeverity(value) {
|
|
33684
34238
|
return typeof value === "string" && VALID_SEVERITIES.includes(value);
|
|
33685
34239
|
}
|
|
@@ -33715,9 +34269,9 @@ function getDefaultRules() {
|
|
|
33715
34269
|
new UnsafeSideEffectRule().toPolicy()
|
|
33716
34270
|
];
|
|
33717
34271
|
}
|
|
33718
|
-
var IntentValidator, ClaimExtractor,
|
|
33719
|
-
var
|
|
33720
|
-
"../../packages/core/dist/chunk-
|
|
34272
|
+
var ACTION_PATTERNS, PATH_PATTERNS, DIRECTORY_PATTERNS, SCOPE_PATTERNS, RISK_PATTERNS, AMBIGUITY_PATTERNS, IntentExtractor, globalExtractor, DEFAULT_CONFIG9, DRIFT_WEIGHTS, ScopeCreepDetector, IntentValidator, DEFAULT_OPTIONS6, PARSER_OPTIONS, ClaimExtractor, DEFAULT_CONFIG25, BUILTIN_MODULES, EvidenceResolver, POLICY_LIMITS, VALID_SEVERITIES, PolicyEngine, UnblockPlanner, DEFAULT_CONFIG34, modeValidator, actionValidator, AgentFirewall, IntentStore, globalIntentStore, MissionService, globalMissionService, BaseRule, DEFAULT_CONFIG44, GhostRouteRule, DEFAULT_CONFIG53, GhostEnvRule, DEFAULT_CONFIG62, AuthDriftRule, DEFAULT_CONFIG72, ContractDriftRule, DEFAULT_CONFIG82, ScopeExplosionRule, DEFAULT_CONFIG92, UnsafeSideEffectRule;
|
|
34273
|
+
var init_chunk_QBVYHKIC = __esm({
|
|
34274
|
+
"../../packages/core/dist/chunk-QBVYHKIC.js"() {
|
|
33721
34275
|
init_chunk_SKXQ6JUA();
|
|
33722
34276
|
init_chunk_4EQXFW4J();
|
|
33723
34277
|
init_chunk_M6OHTAX3();
|
|
@@ -33725,10 +34279,1019 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
33725
34279
|
init_chunk_Y3SGPOAP();
|
|
33726
34280
|
init_chunk_4KSTZVSZ();
|
|
33727
34281
|
init_dist();
|
|
34282
|
+
ACTION_PATTERNS = {
|
|
34283
|
+
create: [
|
|
34284
|
+
/\b(create|add|implement|build|make|generate|write|introduce|set\s*up|init|scaffold)\b/gi,
|
|
34285
|
+
/\b(new|fresh)\s+(file|component|function|class|module|service|api|route|endpoint)/gi
|
|
34286
|
+
],
|
|
34287
|
+
edit: [
|
|
34288
|
+
/\b(update|change|modify|edit|improve|enhance|adjust|tweak|alter)\b/gi,
|
|
34289
|
+
/\b(fix|patch|correct|repair|resolve|address)\b/gi
|
|
34290
|
+
],
|
|
34291
|
+
delete: [
|
|
34292
|
+
/\b(remove|delete|drop|clear|clean\s*up|eliminate|get\s*rid\s*of|purge)\b/gi,
|
|
34293
|
+
/\b(deprecate|sunset|decommission)\b/gi
|
|
34294
|
+
],
|
|
34295
|
+
read: [
|
|
34296
|
+
/\b(read|view|show|display|list|get|fetch|find|search|look\s*at|check|inspect|review)\b/gi,
|
|
34297
|
+
/\b(what\s+is|how\s+does|explain|describe|tell\s+me\s+about)\b/gi
|
|
34298
|
+
],
|
|
34299
|
+
refactor: [
|
|
34300
|
+
/\b(refactor|restructure|reorganize|rewrite|rearchitect|simplify|optimize|clean\s*up\s*the\s*code)\b/gi,
|
|
34301
|
+
/\b(move|rename|extract|inline|split|merge|consolidate)\b/gi
|
|
34302
|
+
],
|
|
34303
|
+
test: [
|
|
34304
|
+
/\b(test|spec|coverage|verify|validate|assert|check)\b/gi,
|
|
34305
|
+
/\b(add\s+tests?|write\s+tests?|unit\s+tests?|integration\s+tests?|e2e\s+tests?)\b/gi
|
|
34306
|
+
]
|
|
34307
|
+
};
|
|
34308
|
+
PATH_PATTERNS = [
|
|
34309
|
+
// Explicit file paths with extensions
|
|
34310
|
+
/(?:^|[\s'"`,])([./]?(?:[\w-]+\/)*[\w-]+\.(?:ts|tsx|js|jsx|json|md|css|scss|html|vue|svelte|yaml|yml|py|go|rs|java|kt|swift|rb|php|c|cpp|h|hpp))/gi,
|
|
34311
|
+
// Quoted paths
|
|
34312
|
+
/['"`]([./]?(?:[\w-]+\/)+[\w-]+(?:\.\w+)?)['"` ]/gi,
|
|
34313
|
+
// Backtick references (common in chat)
|
|
34314
|
+
/`([./]?(?:[\w-]+\/)*[\w-]+(?:\.\w+)?)`/g,
|
|
34315
|
+
// src/ or packages/ style paths
|
|
34316
|
+
/\b((?:src|lib|packages|apps|components|services|utils|hooks|api|routes|pages|views|models|types|schemas|config|test|tests|spec|__tests__)(?:\/[\w-]+)+(?:\.\w+)?)/gi,
|
|
34317
|
+
// node_modules style paths / scoped packages
|
|
34318
|
+
/(?:^|[\s'"`,])(@[\w-]+\/[\w.-]+(?:\/[\w.-]+)*)/gi
|
|
34319
|
+
];
|
|
34320
|
+
DIRECTORY_PATTERNS = [
|
|
34321
|
+
/\b(in\s+the\s+)?([\w-]+)\s+(directory|folder|dir)/gi,
|
|
34322
|
+
/\b(under|within|inside)\s+([./]?(?:[\w-]+\/)+)/gi,
|
|
34323
|
+
/\ball\s+files?\s+in\s+([./]?(?:[\w-]+\/)+)/gi
|
|
34324
|
+
];
|
|
34325
|
+
SCOPE_PATTERNS = {
|
|
34326
|
+
narrow: [
|
|
34327
|
+
/\b(only|just|specifically|exactly)\b/gi,
|
|
34328
|
+
/\b(this\s+file|single\s+file|one\s+file)\b/gi,
|
|
34329
|
+
/\b(this\s+function|this\s+component|this\s+class)\b/gi
|
|
34330
|
+
],
|
|
34331
|
+
broad: [
|
|
34332
|
+
/\b(across\s+the\s+codebase|throughout\s+the\s+project|everywhere|all\s+files?)\b/gi,
|
|
34333
|
+
/\b(entire|whole|complete)\s+(project|codebase|application|app)\b/gi,
|
|
34334
|
+
/\b(global|globally|project-?wide)\b/gi,
|
|
34335
|
+
/\b(every|each)\s+(file|component|function|class|module)\b/gi,
|
|
34336
|
+
/\b(remove|delete)\s+all\b/gi
|
|
34337
|
+
]
|
|
34338
|
+
};
|
|
34339
|
+
RISK_PATTERNS = {
|
|
34340
|
+
critical: [
|
|
34341
|
+
/\b(payment|billing|stripe|checkout|credit\s*card|transaction)\b/gi,
|
|
34342
|
+
/\b(auth|authentication|password|secret|credential|token|api\s*key|oauth)\b/gi,
|
|
34343
|
+
/\b(database|db|migration|schema|production|prod)\b/gi,
|
|
34344
|
+
/\b(security|encrypt|decrypt|hash|vulnerable|exploit)\b/gi,
|
|
34345
|
+
/\b(delete\s+all|drop\s+all|remove\s+all|wipe|destroy)\b/gi
|
|
34346
|
+
],
|
|
34347
|
+
high: [
|
|
34348
|
+
/\b(user|users|account|profile|permission|role|admin)\b/gi,
|
|
34349
|
+
/\b(api|endpoint|route|server|backend)\b/gi,
|
|
34350
|
+
/\b(config|configuration|settings|env|environment)\b/gi,
|
|
34351
|
+
/\b(deploy|deployment|ci|cd|pipeline)\b/gi,
|
|
34352
|
+
/\b(refactor\s+entire|rewrite\s+entire|overhaul)\b/gi
|
|
34353
|
+
],
|
|
34354
|
+
medium: [
|
|
34355
|
+
/\b(component|service|module|hook|util|helper)\b/gi,
|
|
34356
|
+
/\b(style|css|ui|layout|design)\b/gi,
|
|
34357
|
+
/\b(test|spec|mock)\b/gi
|
|
34358
|
+
]
|
|
34359
|
+
};
|
|
34360
|
+
AMBIGUITY_PATTERNS = [
|
|
34361
|
+
/\b(the|that|this|those|these)\s+(file|thing|stuff|code|function|component)\b/gi,
|
|
34362
|
+
/\b(something|somehow|somewhere|whatever|anything)\b/gi,
|
|
34363
|
+
/\b(etc|and\s+so\s+on|and\s+more|and\s+such)\b/gi,
|
|
34364
|
+
/\bfix\s+it\b/gi,
|
|
34365
|
+
/\bmake\s+it\s+work\b/gi,
|
|
34366
|
+
/\bdo\s+the\s+thing\b/gi
|
|
34367
|
+
];
|
|
34368
|
+
IntentExtractor = class {
|
|
34369
|
+
logger;
|
|
34370
|
+
config;
|
|
34371
|
+
constructor(config = {}) {
|
|
34372
|
+
this.logger = getLogger("intent-extractor");
|
|
34373
|
+
this.config = {
|
|
34374
|
+
useLLM: false,
|
|
34375
|
+
minConfidence: 0.3,
|
|
34376
|
+
strictMode: false,
|
|
34377
|
+
...config
|
|
34378
|
+
};
|
|
34379
|
+
}
|
|
34380
|
+
/**
|
|
34381
|
+
* Extract intent from a natural language prompt
|
|
34382
|
+
*/
|
|
34383
|
+
async extract(prompt) {
|
|
34384
|
+
const baselineIntent = this.extractBaseline(prompt);
|
|
34385
|
+
if (this.config.useLLM && this.config.llmEndpoint) {
|
|
34386
|
+
try {
|
|
34387
|
+
return await this.enhanceWithLLM(prompt, baselineIntent);
|
|
34388
|
+
} catch (error) {
|
|
34389
|
+
this.logger.warn("LLM enhancement failed, using baseline", { error });
|
|
34390
|
+
}
|
|
34391
|
+
}
|
|
34392
|
+
return baselineIntent;
|
|
34393
|
+
}
|
|
34394
|
+
/**
|
|
34395
|
+
* Synchronous baseline extraction using heuristics
|
|
34396
|
+
*/
|
|
34397
|
+
extractBaseline(prompt) {
|
|
34398
|
+
const normalizedPrompt = this.normalizePrompt(prompt);
|
|
34399
|
+
const verbs = this.extractVerbs(normalizedPrompt);
|
|
34400
|
+
const operations = this.detectOperations(normalizedPrompt);
|
|
34401
|
+
const pathMentions = this.extractPaths(normalizedPrompt);
|
|
34402
|
+
const scopeIndicators = this.extractScopeIndicators(normalizedPrompt);
|
|
34403
|
+
const riskFactors = this.extractRiskFactors(normalizedPrompt);
|
|
34404
|
+
const isAmbiguous = this.detectAmbiguity(normalizedPrompt);
|
|
34405
|
+
const confidence = this.calculateConfidence({
|
|
34406
|
+
operations,
|
|
34407
|
+
pathMentions,
|
|
34408
|
+
scopeIndicators,
|
|
34409
|
+
isAmbiguous,
|
|
34410
|
+
promptLength: normalizedPrompt.length
|
|
34411
|
+
});
|
|
34412
|
+
const riskLevel = this.calculateRiskLevel({
|
|
34413
|
+
operations,
|
|
34414
|
+
riskFactors,
|
|
34415
|
+
scopeIndicators,
|
|
34416
|
+
pathMentions
|
|
34417
|
+
});
|
|
34418
|
+
const summary = this.generateSummary({
|
|
34419
|
+
operations,
|
|
34420
|
+
pathMentions,
|
|
34421
|
+
verbs,
|
|
34422
|
+
originalPrompt: prompt
|
|
34423
|
+
});
|
|
34424
|
+
const allowedPaths = this.determineAllowedPaths({
|
|
34425
|
+
pathMentions,
|
|
34426
|
+
scopeIndicators
|
|
34427
|
+
});
|
|
34428
|
+
return {
|
|
34429
|
+
summary,
|
|
34430
|
+
operations: operations.length > 0 ? operations : ["edit"],
|
|
34431
|
+
allowedPaths,
|
|
34432
|
+
riskLevel,
|
|
34433
|
+
confidence,
|
|
34434
|
+
metadata: {
|
|
34435
|
+
verbs,
|
|
34436
|
+
pathMentions,
|
|
34437
|
+
scopeIndicators,
|
|
34438
|
+
riskFactors,
|
|
34439
|
+
isAmbiguous
|
|
34440
|
+
}
|
|
34441
|
+
};
|
|
34442
|
+
}
|
|
34443
|
+
// ==========================================================================
|
|
34444
|
+
// Private Extraction Methods
|
|
34445
|
+
// ==========================================================================
|
|
34446
|
+
normalizePrompt(prompt) {
|
|
34447
|
+
return prompt.replace(/\s+/g, " ").replace(/[""]/g, '"').replace(/['']/g, "'").trim();
|
|
34448
|
+
}
|
|
34449
|
+
extractVerbs(prompt) {
|
|
34450
|
+
const verbs = /* @__PURE__ */ new Set();
|
|
34451
|
+
for (const patterns of Object.values(ACTION_PATTERNS)) {
|
|
34452
|
+
for (const pattern of patterns) {
|
|
34453
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
34454
|
+
let match;
|
|
34455
|
+
while ((match = regex.exec(prompt)) !== null) {
|
|
34456
|
+
verbs.add(match[0].toLowerCase().trim());
|
|
34457
|
+
}
|
|
34458
|
+
}
|
|
34459
|
+
}
|
|
34460
|
+
return Array.from(verbs);
|
|
34461
|
+
}
|
|
34462
|
+
detectOperations(prompt) {
|
|
34463
|
+
const operations = /* @__PURE__ */ new Set();
|
|
34464
|
+
for (const [operation, patterns] of Object.entries(ACTION_PATTERNS)) {
|
|
34465
|
+
for (const pattern of patterns) {
|
|
34466
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
34467
|
+
if (regex.test(prompt)) {
|
|
34468
|
+
operations.add(operation);
|
|
34469
|
+
break;
|
|
34470
|
+
}
|
|
34471
|
+
}
|
|
34472
|
+
}
|
|
34473
|
+
return Array.from(operations);
|
|
34474
|
+
}
|
|
34475
|
+
extractPaths(prompt) {
|
|
34476
|
+
const paths = /* @__PURE__ */ new Set();
|
|
34477
|
+
for (const pattern of PATH_PATTERNS) {
|
|
34478
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
34479
|
+
let match;
|
|
34480
|
+
while ((match = regex.exec(prompt)) !== null) {
|
|
34481
|
+
const path34 = match[1] || match[0];
|
|
34482
|
+
const cleanPath = path34.trim().replace(/^['"`]|['"`]$/g, "");
|
|
34483
|
+
if (cleanPath && !this.isCommonWord(cleanPath)) {
|
|
34484
|
+
paths.add(cleanPath);
|
|
34485
|
+
}
|
|
34486
|
+
}
|
|
34487
|
+
}
|
|
34488
|
+
for (const pattern of DIRECTORY_PATTERNS) {
|
|
34489
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
34490
|
+
let match;
|
|
34491
|
+
while ((match = regex.exec(prompt)) !== null) {
|
|
34492
|
+
const dir = (match[2] || match[1]).trim();
|
|
34493
|
+
if (dir && !this.isCommonWord(dir)) {
|
|
34494
|
+
paths.add(dir.endsWith("/") ? dir : `${dir}/`);
|
|
34495
|
+
}
|
|
34496
|
+
}
|
|
34497
|
+
}
|
|
34498
|
+
return Array.from(paths);
|
|
34499
|
+
}
|
|
34500
|
+
extractScopeIndicators(prompt) {
|
|
34501
|
+
const indicators = [];
|
|
34502
|
+
for (const pattern of SCOPE_PATTERNS.narrow) {
|
|
34503
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
34504
|
+
if (regex.test(prompt)) {
|
|
34505
|
+
indicators.push("narrow");
|
|
34506
|
+
break;
|
|
34507
|
+
}
|
|
34508
|
+
}
|
|
34509
|
+
for (const pattern of SCOPE_PATTERNS.broad) {
|
|
34510
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
34511
|
+
if (regex.test(prompt)) {
|
|
34512
|
+
indicators.push("broad");
|
|
34513
|
+
break;
|
|
34514
|
+
}
|
|
34515
|
+
}
|
|
34516
|
+
return indicators;
|
|
34517
|
+
}
|
|
34518
|
+
extractRiskFactors(prompt) {
|
|
34519
|
+
const factors = [];
|
|
34520
|
+
for (const [level, patterns] of Object.entries(RISK_PATTERNS)) {
|
|
34521
|
+
for (const pattern of patterns) {
|
|
34522
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
34523
|
+
if (regex.test(prompt)) {
|
|
34524
|
+
factors.push(level);
|
|
34525
|
+
break;
|
|
34526
|
+
}
|
|
34527
|
+
}
|
|
34528
|
+
}
|
|
34529
|
+
return [...new Set(factors)];
|
|
34530
|
+
}
|
|
34531
|
+
detectAmbiguity(prompt) {
|
|
34532
|
+
for (const pattern of AMBIGUITY_PATTERNS) {
|
|
34533
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
34534
|
+
if (regex.test(prompt)) {
|
|
34535
|
+
return true;
|
|
34536
|
+
}
|
|
34537
|
+
}
|
|
34538
|
+
if (prompt.length < 20) {
|
|
34539
|
+
return true;
|
|
34540
|
+
}
|
|
34541
|
+
return false;
|
|
34542
|
+
}
|
|
34543
|
+
isCommonWord(text5) {
|
|
34544
|
+
const commonWords = /* @__PURE__ */ new Set([
|
|
34545
|
+
"the",
|
|
34546
|
+
"a",
|
|
34547
|
+
"an",
|
|
34548
|
+
"and",
|
|
34549
|
+
"or",
|
|
34550
|
+
"but",
|
|
34551
|
+
"in",
|
|
34552
|
+
"on",
|
|
34553
|
+
"at",
|
|
34554
|
+
"to",
|
|
34555
|
+
"for",
|
|
34556
|
+
"of",
|
|
34557
|
+
"with",
|
|
34558
|
+
"by",
|
|
34559
|
+
"from",
|
|
34560
|
+
"as",
|
|
34561
|
+
"is",
|
|
34562
|
+
"was",
|
|
34563
|
+
"are",
|
|
34564
|
+
"were",
|
|
34565
|
+
"be",
|
|
34566
|
+
"have",
|
|
34567
|
+
"has",
|
|
34568
|
+
"had",
|
|
34569
|
+
"do",
|
|
34570
|
+
"does",
|
|
34571
|
+
"did",
|
|
34572
|
+
"will",
|
|
34573
|
+
"would",
|
|
34574
|
+
"could",
|
|
34575
|
+
"should",
|
|
34576
|
+
"may",
|
|
34577
|
+
"might",
|
|
34578
|
+
"must",
|
|
34579
|
+
"shall",
|
|
34580
|
+
"can",
|
|
34581
|
+
"need",
|
|
34582
|
+
"dare",
|
|
34583
|
+
"directory",
|
|
34584
|
+
"folder",
|
|
34585
|
+
"file",
|
|
34586
|
+
"files",
|
|
34587
|
+
"code",
|
|
34588
|
+
"function",
|
|
34589
|
+
"method"
|
|
34590
|
+
]);
|
|
34591
|
+
return commonWords.has(text5.toLowerCase());
|
|
34592
|
+
}
|
|
34593
|
+
// ==========================================================================
|
|
34594
|
+
// Calculation Methods
|
|
34595
|
+
// ==========================================================================
|
|
34596
|
+
calculateConfidence(params) {
|
|
34597
|
+
let confidence = 0.5;
|
|
34598
|
+
if (params.operations.length > 0) {
|
|
34599
|
+
confidence += 0.15;
|
|
34600
|
+
}
|
|
34601
|
+
if (params.pathMentions.length > 0) {
|
|
34602
|
+
confidence += 0.2;
|
|
34603
|
+
}
|
|
34604
|
+
if (params.scopeIndicators.length > 0) {
|
|
34605
|
+
confidence += 0.1;
|
|
34606
|
+
}
|
|
34607
|
+
if (params.isAmbiguous) {
|
|
34608
|
+
confidence -= 0.25;
|
|
34609
|
+
}
|
|
34610
|
+
if (params.promptLength < 30) {
|
|
34611
|
+
confidence -= 0.15;
|
|
34612
|
+
} else if (params.promptLength > 100) {
|
|
34613
|
+
confidence += 0.05;
|
|
34614
|
+
}
|
|
34615
|
+
if (this.config.strictMode && params.isAmbiguous) {
|
|
34616
|
+
confidence -= 0.1;
|
|
34617
|
+
}
|
|
34618
|
+
return Math.max(0, Math.min(1, confidence));
|
|
34619
|
+
}
|
|
34620
|
+
calculateRiskLevel(params) {
|
|
34621
|
+
let riskScore = 0;
|
|
34622
|
+
if (params.operations.includes("delete")) {
|
|
34623
|
+
riskScore += 3;
|
|
34624
|
+
}
|
|
34625
|
+
if (params.operations.includes("create")) {
|
|
34626
|
+
riskScore += 1;
|
|
34627
|
+
}
|
|
34628
|
+
if (params.operations.includes("edit")) {
|
|
34629
|
+
riskScore += 1;
|
|
34630
|
+
}
|
|
34631
|
+
if (params.operations.includes("refactor")) {
|
|
34632
|
+
riskScore += 2;
|
|
34633
|
+
}
|
|
34634
|
+
if (params.riskFactors.includes("critical")) {
|
|
34635
|
+
riskScore += 4;
|
|
34636
|
+
}
|
|
34637
|
+
if (params.riskFactors.includes("high")) {
|
|
34638
|
+
riskScore += 2;
|
|
34639
|
+
}
|
|
34640
|
+
if (params.riskFactors.includes("medium")) {
|
|
34641
|
+
riskScore += 1;
|
|
34642
|
+
}
|
|
34643
|
+
if (params.scopeIndicators.includes("broad")) {
|
|
34644
|
+
riskScore += 3;
|
|
34645
|
+
}
|
|
34646
|
+
if (params.scopeIndicators.includes("narrow")) {
|
|
34647
|
+
riskScore -= 1;
|
|
34648
|
+
}
|
|
34649
|
+
if (params.pathMentions.length === 0) {
|
|
34650
|
+
riskScore += 2;
|
|
34651
|
+
}
|
|
34652
|
+
if (riskScore >= 8) return "critical";
|
|
34653
|
+
if (riskScore >= 5) return "high";
|
|
34654
|
+
if (riskScore >= 2) return "medium";
|
|
34655
|
+
return "low";
|
|
34656
|
+
}
|
|
34657
|
+
generateSummary(params) {
|
|
34658
|
+
const ops = params.operations.length > 0 ? params.operations.join("/") : "modify";
|
|
34659
|
+
const paths = params.pathMentions.length > 0 ? params.pathMentions.slice(0, 3).join(", ") : "unspecified files";
|
|
34660
|
+
const verb = params.verbs[0] || ops;
|
|
34661
|
+
if (params.originalPrompt.length <= 100) {
|
|
34662
|
+
return params.originalPrompt;
|
|
34663
|
+
}
|
|
34664
|
+
return `${verb.charAt(0).toUpperCase() + verb.slice(1)} ${paths}`;
|
|
34665
|
+
}
|
|
34666
|
+
determineAllowedPaths(params) {
|
|
34667
|
+
if (params.pathMentions.length > 0) {
|
|
34668
|
+
return params.pathMentions.map((p9) => this.toGlobPattern(p9));
|
|
34669
|
+
}
|
|
34670
|
+
if (params.scopeIndicators.includes("broad")) {
|
|
34671
|
+
return ["**/*"];
|
|
34672
|
+
}
|
|
34673
|
+
return [];
|
|
34674
|
+
}
|
|
34675
|
+
toGlobPattern(path34) {
|
|
34676
|
+
if (path34.endsWith("/")) {
|
|
34677
|
+
return `${path34}**/*`;
|
|
34678
|
+
}
|
|
34679
|
+
if (path34.includes("*")) {
|
|
34680
|
+
return path34;
|
|
34681
|
+
}
|
|
34682
|
+
if (/\.\w+$/.test(path34)) {
|
|
34683
|
+
return path34;
|
|
34684
|
+
}
|
|
34685
|
+
return `${path34}/**/*`;
|
|
34686
|
+
}
|
|
34687
|
+
// ==========================================================================
|
|
34688
|
+
// LLM Enhancement (Optional)
|
|
34689
|
+
// ==========================================================================
|
|
34690
|
+
async enhanceWithLLM(prompt, baseline) {
|
|
34691
|
+
if (!this.config.llmEndpoint) {
|
|
34692
|
+
return baseline;
|
|
34693
|
+
}
|
|
34694
|
+
const llmPrompt = this.buildLLMPrompt(prompt, baseline);
|
|
34695
|
+
try {
|
|
34696
|
+
const response = await fetch(this.config.llmEndpoint, {
|
|
34697
|
+
method: "POST",
|
|
34698
|
+
headers: { "Content-Type": "application/json" },
|
|
34699
|
+
body: JSON.stringify({
|
|
34700
|
+
prompt: llmPrompt,
|
|
34701
|
+
max_tokens: 500,
|
|
34702
|
+
temperature: 0.3
|
|
34703
|
+
})
|
|
34704
|
+
});
|
|
34705
|
+
if (!response.ok) {
|
|
34706
|
+
throw new Error(`LLM request failed: ${response.status}`);
|
|
34707
|
+
}
|
|
34708
|
+
const data = await response.json();
|
|
34709
|
+
const enhanced = this.parseLLMResponse(data, baseline);
|
|
34710
|
+
return enhanced;
|
|
34711
|
+
} catch (error) {
|
|
34712
|
+
this.logger.warn("LLM enhancement failed", { error });
|
|
34713
|
+
return baseline;
|
|
34714
|
+
}
|
|
34715
|
+
}
|
|
34716
|
+
buildLLMPrompt(userPrompt, baseline) {
|
|
34717
|
+
return `Analyze this user request and extract the intent:
|
|
34718
|
+
|
|
34719
|
+
User Request: "${userPrompt}"
|
|
34720
|
+
|
|
34721
|
+
Baseline Analysis:
|
|
34722
|
+
- Operations: ${baseline.operations.join(", ")}
|
|
34723
|
+
- Paths: ${baseline.allowedPaths.join(", ") || "none detected"}
|
|
34724
|
+
- Risk Level: ${baseline.riskLevel}
|
|
34725
|
+
- Confidence: ${baseline.confidence}
|
|
34726
|
+
|
|
34727
|
+
Please refine and respond with JSON:
|
|
34728
|
+
{
|
|
34729
|
+
"summary": "one-sentence summary of the intent",
|
|
34730
|
+
"operations": ["create" | "edit" | "delete" | "read" | "refactor" | "test"],
|
|
34731
|
+
"allowedPaths": ["file or glob patterns"],
|
|
34732
|
+
"riskLevel": "low" | "medium" | "high" | "critical",
|
|
34733
|
+
"confidence": 0.0-1.0
|
|
34734
|
+
}`;
|
|
34735
|
+
}
|
|
34736
|
+
parseLLMResponse(response, baseline) {
|
|
34737
|
+
try {
|
|
34738
|
+
const text5 = response.text || response.content || "";
|
|
34739
|
+
const jsonMatch = text5.match(/\{[\s\S]*\}/);
|
|
34740
|
+
if (!jsonMatch) {
|
|
34741
|
+
return baseline;
|
|
34742
|
+
}
|
|
34743
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
34744
|
+
return {
|
|
34745
|
+
...baseline,
|
|
34746
|
+
summary: parsed.summary || baseline.summary,
|
|
34747
|
+
operations: parsed.operations || baseline.operations,
|
|
34748
|
+
allowedPaths: parsed.allowedPaths || baseline.allowedPaths,
|
|
34749
|
+
riskLevel: parsed.riskLevel || baseline.riskLevel,
|
|
34750
|
+
confidence: Math.max(
|
|
34751
|
+
baseline.confidence,
|
|
34752
|
+
parsed.confidence || baseline.confidence
|
|
34753
|
+
)
|
|
34754
|
+
};
|
|
34755
|
+
} catch {
|
|
34756
|
+
return baseline;
|
|
34757
|
+
}
|
|
34758
|
+
}
|
|
34759
|
+
};
|
|
34760
|
+
globalExtractor = null;
|
|
34761
|
+
DEFAULT_CONFIG9 = {
|
|
34762
|
+
warnThreshold: 30,
|
|
34763
|
+
blockThreshold: 60,
|
|
34764
|
+
maxFilesPerIntent: 15,
|
|
34765
|
+
maxNewDirectories: 2,
|
|
34766
|
+
sensitivePatterns: [
|
|
34767
|
+
"**/auth/**",
|
|
34768
|
+
"**/billing/**",
|
|
34769
|
+
"**/payment/**",
|
|
34770
|
+
"**/stripe/**",
|
|
34771
|
+
"**/security/**",
|
|
34772
|
+
"**/secrets/**",
|
|
34773
|
+
"**/.env*",
|
|
34774
|
+
"**/credentials*",
|
|
34775
|
+
"**/api-keys*",
|
|
34776
|
+
"**/middleware/auth*",
|
|
34777
|
+
"**/services/auth*",
|
|
34778
|
+
"**/services/billing*",
|
|
34779
|
+
"**/routes/billing*",
|
|
34780
|
+
"**/routes/auth*"
|
|
34781
|
+
],
|
|
34782
|
+
packageDirectories: [
|
|
34783
|
+
"packages/",
|
|
34784
|
+
"apps/",
|
|
34785
|
+
"libs/",
|
|
34786
|
+
"modules/"
|
|
34787
|
+
],
|
|
34788
|
+
dependencyFiles: [
|
|
34789
|
+
"package.json",
|
|
34790
|
+
"package-lock.json",
|
|
34791
|
+
"pnpm-lock.yaml",
|
|
34792
|
+
"yarn.lock",
|
|
34793
|
+
"requirements.txt",
|
|
34794
|
+
"Pipfile",
|
|
34795
|
+
"Gemfile",
|
|
34796
|
+
"go.mod",
|
|
34797
|
+
"Cargo.toml"
|
|
34798
|
+
],
|
|
34799
|
+
autoBlockSensitive: true,
|
|
34800
|
+
requireApprovalForDeps: true
|
|
34801
|
+
};
|
|
34802
|
+
DRIFT_WEIGHTS = {
|
|
34803
|
+
sensitive_area: 40,
|
|
34804
|
+
dependency_drift: 25,
|
|
34805
|
+
package_drift: 20,
|
|
34806
|
+
directory_outside_scope: 15,
|
|
34807
|
+
file_explosion: 15,
|
|
34808
|
+
operation_mismatch: 10,
|
|
34809
|
+
unpredicted_file: 5
|
|
34810
|
+
};
|
|
34811
|
+
ScopeCreepDetector = class {
|
|
34812
|
+
config;
|
|
34813
|
+
logger;
|
|
34814
|
+
activePlan = null;
|
|
34815
|
+
operations = [];
|
|
34816
|
+
existingDirectories = /* @__PURE__ */ new Set();
|
|
34817
|
+
projectRoot;
|
|
34818
|
+
constructor(projectRoot, config = {}, existingDirectories = []) {
|
|
34819
|
+
this.projectRoot = projectRoot;
|
|
34820
|
+
this.config = { ...DEFAULT_CONFIG9, ...config };
|
|
34821
|
+
this.logger = getLogger("scope-creep-detector");
|
|
34822
|
+
this.existingDirectories = new Set(existingDirectories.map((d) => this.normalizePath(d)));
|
|
34823
|
+
}
|
|
34824
|
+
// ============================================================================
|
|
34825
|
+
// Change Plan Management
|
|
34826
|
+
// ============================================================================
|
|
34827
|
+
/**
|
|
34828
|
+
* Create a change plan from an intent description
|
|
34829
|
+
*/
|
|
34830
|
+
createChangePlan(params) {
|
|
34831
|
+
const plan = {
|
|
34832
|
+
id: this.generateId("plan"),
|
|
34833
|
+
intentDescription: params.intentDescription,
|
|
34834
|
+
predictedFiles: (params.predictedFiles ?? []).map((f) => this.normalizePath(f)),
|
|
34835
|
+
allowedPatterns: params.allowedPatterns ?? this.inferAllowedPatterns(params.intentDescription),
|
|
34836
|
+
blockedPatterns: params.blockedPatterns ?? [],
|
|
34837
|
+
expectedOperations: params.expectedOperations ?? ["read", "modify"],
|
|
34838
|
+
maxFiles: params.maxFiles ?? this.config.maxFilesPerIntent,
|
|
34839
|
+
maxNewDirectories: params.maxNewDirectories ?? this.config.maxNewDirectories,
|
|
34840
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34841
|
+
status: "pending"
|
|
34842
|
+
};
|
|
34843
|
+
this.logger.debug("Change plan created", { planId: plan.id, intent: plan.intentDescription });
|
|
34844
|
+
return plan;
|
|
34845
|
+
}
|
|
34846
|
+
/**
|
|
34847
|
+
* Start executing a change plan
|
|
34848
|
+
*/
|
|
34849
|
+
startExecution(plan) {
|
|
34850
|
+
if (this.activePlan && this.activePlan.status === "executing") {
|
|
34851
|
+
this.logger.warn("Replacing active plan", {
|
|
34852
|
+
oldPlanId: this.activePlan.id,
|
|
34853
|
+
newPlanId: plan.id
|
|
34854
|
+
});
|
|
34855
|
+
}
|
|
34856
|
+
this.activePlan = { ...plan, status: "executing" };
|
|
34857
|
+
this.operations = [];
|
|
34858
|
+
this.logger.debug("Execution started", { planId: plan.id });
|
|
34859
|
+
}
|
|
34860
|
+
/**
|
|
34861
|
+
* Track a file operation during execution
|
|
34862
|
+
*/
|
|
34863
|
+
trackOperation(params) {
|
|
34864
|
+
const normalizedPath = this.normalizePath(params.path);
|
|
34865
|
+
const operation = {
|
|
34866
|
+
id: this.generateId("op"),
|
|
34867
|
+
type: params.type,
|
|
34868
|
+
path: normalizedPath,
|
|
34869
|
+
absolutePath: params.absolutePath,
|
|
34870
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34871
|
+
predicted: this.isPredictedFile(normalizedPath),
|
|
34872
|
+
inScope: this.isInScope(normalizedPath, params.type),
|
|
34873
|
+
driftType: void 0,
|
|
34874
|
+
driftReason: void 0
|
|
34875
|
+
};
|
|
34876
|
+
if (!operation.inScope) {
|
|
34877
|
+
const { category, reason } = this.categorizeDrift(normalizedPath, params.type);
|
|
34878
|
+
operation.driftType = category;
|
|
34879
|
+
operation.driftReason = reason;
|
|
34880
|
+
}
|
|
34881
|
+
this.operations.push(operation);
|
|
34882
|
+
this.logger.debug("Operation tracked", {
|
|
34883
|
+
operationId: operation.id,
|
|
34884
|
+
path: normalizedPath,
|
|
34885
|
+
inScope: operation.inScope,
|
|
34886
|
+
driftType: operation.driftType
|
|
34887
|
+
});
|
|
34888
|
+
return operation;
|
|
34889
|
+
}
|
|
34890
|
+
/**
|
|
34891
|
+
* Complete execution and evaluate drift
|
|
34892
|
+
*/
|
|
34893
|
+
completeExecution() {
|
|
34894
|
+
if (!this.activePlan) {
|
|
34895
|
+
return this.createDecision("allow", "No active plan - all operations allowed", {
|
|
34896
|
+
score: 0,
|
|
34897
|
+
byCategory: this.emptyDriftCategories(),
|
|
34898
|
+
filesInScope: this.operations.length,
|
|
34899
|
+
filesOutOfScope: 0,
|
|
34900
|
+
newDirectories: [],
|
|
34901
|
+
sensitiveAreasTouched: [],
|
|
34902
|
+
exceedsThreshold: false
|
|
34903
|
+
}, []);
|
|
34904
|
+
}
|
|
34905
|
+
const driftScore = this.calculateDriftScore();
|
|
34906
|
+
const driftingOps = this.operations.filter((op) => !op.inScope);
|
|
34907
|
+
const decision = this.makeDecision(driftScore, driftingOps);
|
|
34908
|
+
this.activePlan.status = decision.decision === "block" ? "blocked" : "completed";
|
|
34909
|
+
this.logger.info("Execution completed", {
|
|
34910
|
+
planId: this.activePlan.id,
|
|
34911
|
+
decision: decision.decision,
|
|
34912
|
+
driftScore: driftScore.score,
|
|
34913
|
+
operationCount: this.operations.length,
|
|
34914
|
+
driftingOps: driftingOps.length
|
|
34915
|
+
});
|
|
34916
|
+
return decision;
|
|
34917
|
+
}
|
|
34918
|
+
/**
|
|
34919
|
+
* Evaluate a single operation without starting a full execution
|
|
34920
|
+
*/
|
|
34921
|
+
evaluateSingleOperation(plan, operation) {
|
|
34922
|
+
const normalizedPath = this.normalizePath(operation.path);
|
|
34923
|
+
const previousPlan = this.activePlan;
|
|
34924
|
+
this.activePlan = plan;
|
|
34925
|
+
const inScope = this.isInScope(normalizedPath, operation.type);
|
|
34926
|
+
this.activePlan = previousPlan;
|
|
34927
|
+
if (inScope) {
|
|
34928
|
+
return { allowed: true, reason: "Operation within scope" };
|
|
34929
|
+
}
|
|
34930
|
+
const { category, reason } = this.categorizeDrift(normalizedPath, operation.type);
|
|
34931
|
+
if (category === "sensitive_area" && this.config.autoBlockSensitive) {
|
|
34932
|
+
return {
|
|
34933
|
+
allowed: false,
|
|
34934
|
+
reason: `BLOCKED: ${reason}`,
|
|
34935
|
+
driftType: category
|
|
34936
|
+
};
|
|
34937
|
+
}
|
|
34938
|
+
if (category === "dependency_drift" && this.config.requireApprovalForDeps) {
|
|
34939
|
+
return {
|
|
34940
|
+
allowed: false,
|
|
34941
|
+
reason: `REQUIRES APPROVAL: ${reason}`,
|
|
34942
|
+
driftType: category
|
|
34943
|
+
};
|
|
34944
|
+
}
|
|
34945
|
+
return {
|
|
34946
|
+
allowed: true,
|
|
34947
|
+
reason: `WARNING: ${reason}`,
|
|
34948
|
+
driftType: category
|
|
34949
|
+
};
|
|
34950
|
+
}
|
|
34951
|
+
// ============================================================================
|
|
34952
|
+
// Drift Analysis
|
|
34953
|
+
// ============================================================================
|
|
34954
|
+
/**
|
|
34955
|
+
* Calculate drift score from tracked operations
|
|
34956
|
+
*/
|
|
34957
|
+
calculateDriftScore() {
|
|
34958
|
+
const byCategory = this.emptyDriftCategories();
|
|
34959
|
+
const newDirectories = [];
|
|
34960
|
+
const sensitiveAreasTouched = [];
|
|
34961
|
+
let filesInScope = 0;
|
|
34962
|
+
let filesOutOfScope = 0;
|
|
34963
|
+
const createdDirectories = /* @__PURE__ */ new Set();
|
|
34964
|
+
for (const op of this.operations) {
|
|
34965
|
+
if (op.inScope) {
|
|
34966
|
+
filesInScope++;
|
|
34967
|
+
} else {
|
|
34968
|
+
filesOutOfScope++;
|
|
34969
|
+
if (op.driftType) {
|
|
34970
|
+
byCategory[op.driftType]++;
|
|
34971
|
+
if (op.driftType === "sensitive_area") {
|
|
34972
|
+
sensitiveAreasTouched.push(op.path);
|
|
34973
|
+
}
|
|
34974
|
+
}
|
|
34975
|
+
}
|
|
34976
|
+
if (op.type === "create") {
|
|
34977
|
+
const dir = this.getDirectory(op.path);
|
|
34978
|
+
if (dir && !this.existingDirectories.has(dir)) {
|
|
34979
|
+
createdDirectories.add(dir);
|
|
34980
|
+
}
|
|
34981
|
+
}
|
|
34982
|
+
}
|
|
34983
|
+
const totalFiles = this.operations.filter((op) => op.type !== "read").length;
|
|
34984
|
+
if (this.activePlan && totalFiles > this.activePlan.maxFiles) {
|
|
34985
|
+
byCategory.file_explosion = totalFiles - this.activePlan.maxFiles;
|
|
34986
|
+
}
|
|
34987
|
+
const newDirCount = createdDirectories.size;
|
|
34988
|
+
if (this.activePlan && newDirCount > this.activePlan.maxNewDirectories) {
|
|
34989
|
+
byCategory.directory_outside_scope += newDirCount - this.activePlan.maxNewDirectories;
|
|
34990
|
+
}
|
|
34991
|
+
newDirectories.push(...createdDirectories);
|
|
34992
|
+
let score = 0;
|
|
34993
|
+
for (const [category, count] of Object.entries(byCategory)) {
|
|
34994
|
+
const weight = DRIFT_WEIGHTS[category] ?? 5;
|
|
34995
|
+
score += count * weight;
|
|
34996
|
+
}
|
|
34997
|
+
score = Math.min(100, score);
|
|
34998
|
+
return {
|
|
34999
|
+
score,
|
|
35000
|
+
byCategory,
|
|
35001
|
+
filesInScope,
|
|
35002
|
+
filesOutOfScope,
|
|
35003
|
+
newDirectories,
|
|
35004
|
+
sensitiveAreasTouched,
|
|
35005
|
+
exceedsThreshold: score >= this.config.blockThreshold
|
|
35006
|
+
};
|
|
35007
|
+
}
|
|
35008
|
+
/**
|
|
35009
|
+
* Categorize a drift event
|
|
35010
|
+
*/
|
|
35011
|
+
categorizeDrift(path34, operation) {
|
|
35012
|
+
if (this.matchesSensitivePattern(path34)) {
|
|
35013
|
+
return {
|
|
35014
|
+
category: "sensitive_area",
|
|
35015
|
+
reason: `Path "${path34}" touches sensitive area (auth/billing/security)`
|
|
35016
|
+
};
|
|
35017
|
+
}
|
|
35018
|
+
if (this.isDependencyFile(path34) && operation !== "read") {
|
|
35019
|
+
return {
|
|
35020
|
+
category: "dependency_drift",
|
|
35021
|
+
reason: `Modifying dependency file "${path34}" without declaration`
|
|
35022
|
+
};
|
|
35023
|
+
}
|
|
35024
|
+
if (this.isInDifferentPackage(path34)) {
|
|
35025
|
+
return {
|
|
35026
|
+
category: "package_drift",
|
|
35027
|
+
reason: `Path "${path34}" is in a different package than declared scope`
|
|
35028
|
+
};
|
|
35029
|
+
}
|
|
35030
|
+
if (!this.isAllowedOperation(operation)) {
|
|
35031
|
+
return {
|
|
35032
|
+
category: "operation_mismatch",
|
|
35033
|
+
reason: `Operation "${operation}" not allowed in current plan`
|
|
35034
|
+
};
|
|
35035
|
+
}
|
|
35036
|
+
if (!this.isPathAllowed(path34)) {
|
|
35037
|
+
const dir = this.getDirectory(path34);
|
|
35038
|
+
if (dir && !this.existingDirectories.has(dir)) {
|
|
35039
|
+
return {
|
|
35040
|
+
category: "directory_outside_scope",
|
|
35041
|
+
reason: `Creating file in new directory "${dir}" outside allowed scope`
|
|
35042
|
+
};
|
|
35043
|
+
}
|
|
35044
|
+
return {
|
|
35045
|
+
category: "unpredicted_file",
|
|
35046
|
+
reason: `File "${path34}" not in predicted files or allowed patterns`
|
|
35047
|
+
};
|
|
35048
|
+
}
|
|
35049
|
+
return {
|
|
35050
|
+
category: "unpredicted_file",
|
|
35051
|
+
reason: `File "${path34}" was not predicted by the change plan`
|
|
35052
|
+
};
|
|
35053
|
+
}
|
|
35054
|
+
// ============================================================================
|
|
35055
|
+
// Decision Making
|
|
35056
|
+
// ============================================================================
|
|
35057
|
+
/**
|
|
35058
|
+
* Make a decision based on drift score
|
|
35059
|
+
*/
|
|
35060
|
+
makeDecision(driftScore, driftingOps) {
|
|
35061
|
+
const { score, sensitiveAreasTouched } = driftScore;
|
|
35062
|
+
if (this.config.autoBlockSensitive && sensitiveAreasTouched.length > 0) {
|
|
35063
|
+
return this.createDecision(
|
|
35064
|
+
"block",
|
|
35065
|
+
`Blocked: Touched sensitive areas without explicit approval: ${sensitiveAreasTouched.join(", ")}`,
|
|
35066
|
+
driftScore,
|
|
35067
|
+
driftingOps,
|
|
35068
|
+
"Declare intent explicitly mentioning auth/billing/security changes"
|
|
35069
|
+
);
|
|
35070
|
+
}
|
|
35071
|
+
if (score >= this.config.blockThreshold) {
|
|
35072
|
+
return this.createDecision(
|
|
35073
|
+
"block",
|
|
35074
|
+
`Blocked: Drift score ${score} exceeds threshold ${this.config.blockThreshold}`,
|
|
35075
|
+
driftScore,
|
|
35076
|
+
driftingOps,
|
|
35077
|
+
"Reduce scope or declare broader intent"
|
|
35078
|
+
);
|
|
35079
|
+
}
|
|
35080
|
+
const depChanges = driftingOps.filter((op) => op.driftType === "dependency_drift");
|
|
35081
|
+
if (this.config.requireApprovalForDeps && depChanges.length > 0) {
|
|
35082
|
+
return this.createDecision(
|
|
35083
|
+
"require_approval",
|
|
35084
|
+
`Requires approval: Dependency changes detected in ${depChanges.map((o) => o.path).join(", ")}`,
|
|
35085
|
+
driftScore,
|
|
35086
|
+
driftingOps,
|
|
35087
|
+
"Explicitly declare dependency modifications in intent"
|
|
35088
|
+
);
|
|
35089
|
+
}
|
|
35090
|
+
if (score >= this.config.warnThreshold) {
|
|
35091
|
+
return this.createDecision(
|
|
35092
|
+
"warn",
|
|
35093
|
+
`Warning: Drift score ${score} is elevated (threshold: ${this.config.warnThreshold})`,
|
|
35094
|
+
driftScore,
|
|
35095
|
+
driftingOps,
|
|
35096
|
+
"Consider narrowing scope or declaring affected files"
|
|
35097
|
+
);
|
|
35098
|
+
}
|
|
35099
|
+
return this.createDecision(
|
|
35100
|
+
"allow",
|
|
35101
|
+
"Operations within acceptable scope",
|
|
35102
|
+
driftScore,
|
|
35103
|
+
driftingOps
|
|
35104
|
+
);
|
|
35105
|
+
}
|
|
35106
|
+
createDecision(decision, reason, driftScore, driftingOperations, suggestion) {
|
|
35107
|
+
return {
|
|
35108
|
+
decision,
|
|
35109
|
+
reason,
|
|
35110
|
+
driftScore,
|
|
35111
|
+
driftingOperations,
|
|
35112
|
+
suggestion,
|
|
35113
|
+
auditId: this.generateId("audit"),
|
|
35114
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
35115
|
+
};
|
|
35116
|
+
}
|
|
35117
|
+
// ============================================================================
|
|
35118
|
+
// Utility Methods
|
|
35119
|
+
// ============================================================================
|
|
35120
|
+
/**
|
|
35121
|
+
* Infer allowed patterns from intent description
|
|
35122
|
+
*/
|
|
35123
|
+
inferAllowedPatterns(intent) {
|
|
35124
|
+
const patterns = [];
|
|
35125
|
+
const lowerIntent = intent.toLowerCase();
|
|
35126
|
+
const filePathRegex = /(?:^|[\s"'`])((?:[\w-]+\/)*[\w-]+\.(?:ts|tsx|js|jsx|json|md|css|scss|html))/gi;
|
|
35127
|
+
let match;
|
|
35128
|
+
while ((match = filePathRegex.exec(intent)) !== null) {
|
|
35129
|
+
patterns.push(`**/${match[1]}`);
|
|
35130
|
+
}
|
|
35131
|
+
const dirPatterns = [
|
|
35132
|
+
{ keywords: ["readme", "documentation", "docs"], pattern: "**/README*" },
|
|
35133
|
+
{ keywords: ["test", "spec", "testing"], pattern: "**/*.test.*" },
|
|
35134
|
+
{ keywords: ["component", "components"], pattern: "**/components/**" },
|
|
35135
|
+
{ keywords: ["api", "endpoint", "route"], pattern: "**/api/**" },
|
|
35136
|
+
{ keywords: ["utils", "utility", "helper"], pattern: "**/utils/**" },
|
|
35137
|
+
{ keywords: ["config", "configuration"], pattern: "**/*.config.*" },
|
|
35138
|
+
{ keywords: ["scanner", "scan"], pattern: "**/scanners/**" },
|
|
35139
|
+
{ keywords: ["firewall"], pattern: "**/firewall/**" }
|
|
35140
|
+
];
|
|
35141
|
+
for (const { keywords, pattern } of dirPatterns) {
|
|
35142
|
+
if (keywords.some((kw) => lowerIntent.includes(kw))) {
|
|
35143
|
+
patterns.push(pattern);
|
|
35144
|
+
}
|
|
35145
|
+
}
|
|
35146
|
+
if (patterns.length === 0) {
|
|
35147
|
+
patterns.push("**/*.md");
|
|
35148
|
+
}
|
|
35149
|
+
return patterns;
|
|
35150
|
+
}
|
|
35151
|
+
isPredictedFile(path34) {
|
|
35152
|
+
if (!this.activePlan) return false;
|
|
35153
|
+
return this.activePlan.predictedFiles.some(
|
|
35154
|
+
(predicted) => this.matchPath(path34, predicted)
|
|
35155
|
+
);
|
|
35156
|
+
}
|
|
35157
|
+
isInScope(path34, operation) {
|
|
35158
|
+
if (!this.activePlan) return true;
|
|
35159
|
+
if (this.activePlan.blockedPatterns.some((pattern) => this.matchGlob(path34, pattern))) {
|
|
35160
|
+
return false;
|
|
35161
|
+
}
|
|
35162
|
+
if (!this.activePlan.expectedOperations.includes(operation)) {
|
|
35163
|
+
if (operation !== "read") {
|
|
35164
|
+
return false;
|
|
35165
|
+
}
|
|
35166
|
+
}
|
|
35167
|
+
if (this.isPredictedFile(path34)) {
|
|
35168
|
+
return true;
|
|
35169
|
+
}
|
|
35170
|
+
return this.isPathAllowed(path34);
|
|
35171
|
+
}
|
|
35172
|
+
isPathAllowed(path34) {
|
|
35173
|
+
if (!this.activePlan) return true;
|
|
35174
|
+
return this.activePlan.allowedPatterns.some(
|
|
35175
|
+
(pattern) => this.matchGlob(path34, pattern)
|
|
35176
|
+
);
|
|
35177
|
+
}
|
|
35178
|
+
isAllowedOperation(operation) {
|
|
35179
|
+
if (!this.activePlan) return true;
|
|
35180
|
+
return this.activePlan.expectedOperations.includes(operation) || operation === "read";
|
|
35181
|
+
}
|
|
35182
|
+
matchesSensitivePattern(path34) {
|
|
35183
|
+
return this.config.sensitivePatterns.some(
|
|
35184
|
+
(pattern) => this.matchGlob(path34, pattern)
|
|
35185
|
+
);
|
|
35186
|
+
}
|
|
35187
|
+
isDependencyFile(path34) {
|
|
35188
|
+
const filename = path34.split("/").pop() ?? path34;
|
|
35189
|
+
return this.config.dependencyFiles.some(
|
|
35190
|
+
(dep) => filename === dep || path34.endsWith(dep)
|
|
35191
|
+
);
|
|
35192
|
+
}
|
|
35193
|
+
isInDifferentPackage(path34) {
|
|
35194
|
+
if (!this.activePlan) return false;
|
|
35195
|
+
const getPackage = (p9) => {
|
|
35196
|
+
for (const pkgDir of this.config.packageDirectories) {
|
|
35197
|
+
if (p9.startsWith(pkgDir)) {
|
|
35198
|
+
const afterPkgDir = p9.slice(pkgDir.length);
|
|
35199
|
+
const parts = afterPkgDir.split("/");
|
|
35200
|
+
return parts[0] ?? null;
|
|
35201
|
+
}
|
|
35202
|
+
}
|
|
35203
|
+
return null;
|
|
35204
|
+
};
|
|
35205
|
+
const allowedPackages = /* @__PURE__ */ new Set();
|
|
35206
|
+
for (const pattern of [...this.activePlan.allowedPatterns, ...this.activePlan.predictedFiles]) {
|
|
35207
|
+
const pkg2 = getPackage(pattern);
|
|
35208
|
+
if (pkg2) allowedPackages.add(pkg2);
|
|
35209
|
+
}
|
|
35210
|
+
if (allowedPackages.size === 0) return false;
|
|
35211
|
+
const pathPackage = getPackage(path34);
|
|
35212
|
+
if (!pathPackage) return false;
|
|
35213
|
+
return !allowedPackages.has(pathPackage);
|
|
35214
|
+
}
|
|
35215
|
+
matchPath(path34, pattern) {
|
|
35216
|
+
const normalizedPath = this.normalizePath(path34);
|
|
35217
|
+
const normalizedPattern = this.normalizePath(pattern);
|
|
35218
|
+
return normalizedPath === normalizedPattern || normalizedPath.endsWith(`/${normalizedPattern}`);
|
|
35219
|
+
}
|
|
35220
|
+
matchGlob(path34, pattern) {
|
|
35221
|
+
const normalizedPath = this.normalizePath(path34);
|
|
35222
|
+
const normalizedPattern = this.normalizePath(pattern);
|
|
35223
|
+
if (normalizedPath === normalizedPattern) return true;
|
|
35224
|
+
const regexPattern = normalizedPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "<<<GLOBSTAR>>>").replace(/\*/g, "[^/]*").replace(/<<<GLOBSTAR>>>/g, ".*").replace(/\?/g, ".");
|
|
35225
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
35226
|
+
return regex.test(normalizedPath);
|
|
35227
|
+
}
|
|
35228
|
+
normalizePath(path34) {
|
|
35229
|
+
return path34.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
35230
|
+
}
|
|
35231
|
+
getDirectory(path34) {
|
|
35232
|
+
const parts = path34.split("/");
|
|
35233
|
+
if (parts.length <= 1) return null;
|
|
35234
|
+
return parts.slice(0, -1).join("/");
|
|
35235
|
+
}
|
|
35236
|
+
generateId(prefix) {
|
|
35237
|
+
return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
35238
|
+
}
|
|
35239
|
+
emptyDriftCategories() {
|
|
35240
|
+
return {
|
|
35241
|
+
directory_outside_scope: 0,
|
|
35242
|
+
file_explosion: 0,
|
|
35243
|
+
package_drift: 0,
|
|
35244
|
+
dependency_drift: 0,
|
|
35245
|
+
sensitive_area: 0,
|
|
35246
|
+
operation_mismatch: 0,
|
|
35247
|
+
unpredicted_file: 0
|
|
35248
|
+
};
|
|
35249
|
+
}
|
|
35250
|
+
// ============================================================================
|
|
35251
|
+
// Accessors
|
|
35252
|
+
// ============================================================================
|
|
35253
|
+
getActivePlan() {
|
|
35254
|
+
return this.activePlan;
|
|
35255
|
+
}
|
|
35256
|
+
getOperations() {
|
|
35257
|
+
return [...this.operations];
|
|
35258
|
+
}
|
|
35259
|
+
getConfig() {
|
|
35260
|
+
return { ...this.config };
|
|
35261
|
+
}
|
|
35262
|
+
updateConfig(config) {
|
|
35263
|
+
this.config = { ...this.config, ...config };
|
|
35264
|
+
}
|
|
35265
|
+
/**
|
|
35266
|
+
* Add existing directories (for new directory detection)
|
|
35267
|
+
*/
|
|
35268
|
+
setExistingDirectories(directories) {
|
|
35269
|
+
this.existingDirectories = new Set(directories.map((d) => this.normalizePath(d)));
|
|
35270
|
+
}
|
|
35271
|
+
/**
|
|
35272
|
+
* Reset detector state
|
|
35273
|
+
*/
|
|
35274
|
+
reset() {
|
|
35275
|
+
this.activePlan = null;
|
|
35276
|
+
this.operations = [];
|
|
35277
|
+
}
|
|
35278
|
+
};
|
|
33728
35279
|
IntentValidator = class {
|
|
33729
35280
|
rules = [];
|
|
33730
|
-
|
|
35281
|
+
intentExtractor;
|
|
35282
|
+
scopeCreepDetector = null;
|
|
35283
|
+
config;
|
|
35284
|
+
constructor(config = {}) {
|
|
35285
|
+
this.config = config;
|
|
33731
35286
|
this.initializeDefaultRules();
|
|
35287
|
+
this.intentExtractor = new IntentExtractor();
|
|
35288
|
+
if (config.projectRoot) {
|
|
35289
|
+
this.scopeCreepDetector = new ScopeCreepDetector(
|
|
35290
|
+
config.projectRoot,
|
|
35291
|
+
config.scopeCreepConfig,
|
|
35292
|
+
config.existingDirectories
|
|
35293
|
+
);
|
|
35294
|
+
}
|
|
33732
35295
|
}
|
|
33733
35296
|
/**
|
|
33734
35297
|
* Validate an intent from a request
|
|
@@ -33737,20 +35300,26 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
33737
35300
|
const intent = this.extractIntent(request);
|
|
33738
35301
|
const warnings = [];
|
|
33739
35302
|
const suggestions = [];
|
|
35303
|
+
let scopeCreepDecision;
|
|
33740
35304
|
for (const rule of this.rules) {
|
|
33741
35305
|
if (!rule.check(intent)) {
|
|
33742
35306
|
warnings.push(rule.message);
|
|
33743
35307
|
}
|
|
33744
35308
|
}
|
|
33745
|
-
|
|
33746
|
-
|
|
33747
|
-
|
|
35309
|
+
const scopeCreepResult = this.detectScopeCreep(intent, request.context);
|
|
35310
|
+
if (scopeCreepResult.detected) {
|
|
35311
|
+
warnings.push(`Scope creep detected: ${scopeCreepResult.reason}`);
|
|
35312
|
+
if (scopeCreepResult.suggestion) {
|
|
35313
|
+
suggestions.push(scopeCreepResult.suggestion);
|
|
35314
|
+
}
|
|
35315
|
+
scopeCreepDecision = scopeCreepResult.decision;
|
|
33748
35316
|
}
|
|
33749
35317
|
return {
|
|
33750
35318
|
valid: warnings.length === 0,
|
|
33751
35319
|
intent: { ...intent, valid: warnings.length === 0 },
|
|
33752
35320
|
warnings,
|
|
33753
|
-
suggestions
|
|
35321
|
+
suggestions,
|
|
35322
|
+
scopeCreepDecision
|
|
33754
35323
|
};
|
|
33755
35324
|
}
|
|
33756
35325
|
/**
|
|
@@ -33759,16 +35328,85 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
33759
35328
|
addRule(rule) {
|
|
33760
35329
|
this.rules.push(rule);
|
|
33761
35330
|
}
|
|
35331
|
+
/**
|
|
35332
|
+
* Create and start a change plan for an intent
|
|
35333
|
+
*/
|
|
35334
|
+
createChangePlan(params) {
|
|
35335
|
+
if (!this.scopeCreepDetector) {
|
|
35336
|
+
return null;
|
|
35337
|
+
}
|
|
35338
|
+
const plan = this.scopeCreepDetector.createChangePlan(params);
|
|
35339
|
+
this.scopeCreepDetector.startExecution(plan);
|
|
35340
|
+
return plan;
|
|
35341
|
+
}
|
|
35342
|
+
/**
|
|
35343
|
+
* Track a file operation for scope creep detection
|
|
35344
|
+
*/
|
|
35345
|
+
trackOperation(params) {
|
|
35346
|
+
if (this.scopeCreepDetector) {
|
|
35347
|
+
this.scopeCreepDetector.trackOperation(params);
|
|
35348
|
+
}
|
|
35349
|
+
}
|
|
35350
|
+
/**
|
|
35351
|
+
* Complete execution and get scope creep decision
|
|
35352
|
+
*/
|
|
35353
|
+
completeAndEvaluate() {
|
|
35354
|
+
if (!this.scopeCreepDetector) {
|
|
35355
|
+
return null;
|
|
35356
|
+
}
|
|
35357
|
+
return this.scopeCreepDetector.completeExecution();
|
|
35358
|
+
}
|
|
35359
|
+
/**
|
|
35360
|
+
* Get the scope creep detector instance
|
|
35361
|
+
*/
|
|
35362
|
+
getScopeCreepDetector() {
|
|
35363
|
+
return this.scopeCreepDetector;
|
|
35364
|
+
}
|
|
35365
|
+
/**
|
|
35366
|
+
* Initialize scope creep detector (can be called after construction)
|
|
35367
|
+
*/
|
|
35368
|
+
initializeScopeCreepDetector(projectRoot, existingDirectories, config) {
|
|
35369
|
+
this.scopeCreepDetector = new ScopeCreepDetector(
|
|
35370
|
+
projectRoot,
|
|
35371
|
+
config ?? this.config.scopeCreepConfig,
|
|
35372
|
+
existingDirectories ?? this.config.existingDirectories
|
|
35373
|
+
);
|
|
35374
|
+
}
|
|
33762
35375
|
extractIntent(request) {
|
|
35376
|
+
const extracted = this.intentExtractor.extractBaseline(request.content);
|
|
35377
|
+
const type = this.mapExtractedOperationToType(extracted.operations);
|
|
35378
|
+
const scope = this.inferScopeFromExtracted(extracted, request.target);
|
|
33763
35379
|
return {
|
|
33764
|
-
type
|
|
33765
|
-
target: request.target,
|
|
33766
|
-
scope
|
|
33767
|
-
description:
|
|
33768
|
-
valid:
|
|
33769
|
-
confidence:
|
|
35380
|
+
type,
|
|
35381
|
+
target: extracted.allowedPaths[0] || request.target,
|
|
35382
|
+
scope,
|
|
35383
|
+
description: extracted.summary,
|
|
35384
|
+
valid: !extracted.metadata.isAmbiguous,
|
|
35385
|
+
confidence: extracted.confidence,
|
|
35386
|
+
extracted
|
|
33770
35387
|
};
|
|
33771
35388
|
}
|
|
35389
|
+
mapExtractedOperationToType(operations) {
|
|
35390
|
+
if (operations.includes("delete")) return "delete";
|
|
35391
|
+
if (operations.includes("create")) return "create";
|
|
35392
|
+
if (operations.includes("refactor")) return "refactor";
|
|
35393
|
+
if (operations.includes("test")) return "test";
|
|
35394
|
+
if (operations.includes("edit")) return "modify";
|
|
35395
|
+
return "modify";
|
|
35396
|
+
}
|
|
35397
|
+
inferScopeFromExtracted(extracted, target) {
|
|
35398
|
+
if (extracted.metadata.scopeIndicators.includes("broad")) {
|
|
35399
|
+
return "project";
|
|
35400
|
+
}
|
|
35401
|
+
if (extracted.allowedPaths.length > 0) {
|
|
35402
|
+
const firstPath = extracted.allowedPaths[0];
|
|
35403
|
+
if (firstPath.includes("**/*") || firstPath.endsWith("/")) {
|
|
35404
|
+
return "module";
|
|
35405
|
+
}
|
|
35406
|
+
return "file";
|
|
35407
|
+
}
|
|
35408
|
+
return this.inferScope(target);
|
|
35409
|
+
}
|
|
33772
35410
|
mapActionToType(action) {
|
|
33773
35411
|
const mapping = {
|
|
33774
35412
|
write: "create",
|
|
@@ -33788,7 +35426,106 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
33788
35426
|
return "module";
|
|
33789
35427
|
}
|
|
33790
35428
|
detectScopeCreep(intent, context) {
|
|
33791
|
-
|
|
35429
|
+
if (!this.scopeCreepDetector) {
|
|
35430
|
+
return this.detectScopeCreepBasic(intent, context);
|
|
35431
|
+
}
|
|
35432
|
+
const affectedFiles = this.extractAffectedFiles(context);
|
|
35433
|
+
if (affectedFiles.length > 0) {
|
|
35434
|
+
const plan = this.scopeCreepDetector.createChangePlan({
|
|
35435
|
+
intentDescription: intent.description || `${intent.type} ${intent.target}`,
|
|
35436
|
+
predictedFiles: [intent.target],
|
|
35437
|
+
allowedPatterns: intent.extracted?.allowedPaths
|
|
35438
|
+
});
|
|
35439
|
+
for (const file2 of affectedFiles) {
|
|
35440
|
+
const operation = this.inferOperation(intent.type);
|
|
35441
|
+
const result = this.scopeCreepDetector.evaluateSingleOperation(plan, {
|
|
35442
|
+
type: operation,
|
|
35443
|
+
path: file2
|
|
35444
|
+
});
|
|
35445
|
+
if (!result.allowed) {
|
|
35446
|
+
return {
|
|
35447
|
+
detected: true,
|
|
35448
|
+
reason: result.reason,
|
|
35449
|
+
suggestion: "Declare broader intent or split into smaller changes",
|
|
35450
|
+
decision: void 0
|
|
35451
|
+
};
|
|
35452
|
+
}
|
|
35453
|
+
}
|
|
35454
|
+
}
|
|
35455
|
+
const scopeMismatch = this.checkIntentScopeMismatch(intent, context);
|
|
35456
|
+
if (scopeMismatch) {
|
|
35457
|
+
return {
|
|
35458
|
+
detected: true,
|
|
35459
|
+
reason: scopeMismatch,
|
|
35460
|
+
suggestion: "Consider breaking this into smaller, focused changes"
|
|
35461
|
+
};
|
|
35462
|
+
}
|
|
35463
|
+
return { detected: false };
|
|
35464
|
+
}
|
|
35465
|
+
detectScopeCreepBasic(intent, context) {
|
|
35466
|
+
const affectedFiles = this.extractAffectedFiles(context);
|
|
35467
|
+
if (affectedFiles.length > 10) {
|
|
35468
|
+
return {
|
|
35469
|
+
detected: true,
|
|
35470
|
+
reason: `Too many files affected (${affectedFiles.length})`,
|
|
35471
|
+
suggestion: "Break this into smaller changes"
|
|
35472
|
+
};
|
|
35473
|
+
}
|
|
35474
|
+
const sensitivePatterns = ["auth", "billing", "payment", "security", ".env"];
|
|
35475
|
+
const touchesSensitive = affectedFiles.some(
|
|
35476
|
+
(file2) => sensitivePatterns.some((pattern) => file2.toLowerCase().includes(pattern))
|
|
35477
|
+
);
|
|
35478
|
+
if (touchesSensitive && !this.intentMentionsSensitiveArea(intent)) {
|
|
35479
|
+
return {
|
|
35480
|
+
detected: true,
|
|
35481
|
+
reason: "Touching sensitive area without explicit declaration",
|
|
35482
|
+
suggestion: "Explicitly declare auth/billing/security changes in intent"
|
|
35483
|
+
};
|
|
35484
|
+
}
|
|
35485
|
+
return { detected: false };
|
|
35486
|
+
}
|
|
35487
|
+
extractAffectedFiles(context) {
|
|
35488
|
+
const files = [];
|
|
35489
|
+
if (Array.isArray(context.files)) {
|
|
35490
|
+
files.push(...context.files.filter((f) => typeof f === "string"));
|
|
35491
|
+
}
|
|
35492
|
+
if (Array.isArray(context.affectedFiles)) {
|
|
35493
|
+
files.push(...context.affectedFiles.filter((f) => typeof f === "string"));
|
|
35494
|
+
}
|
|
35495
|
+
if (Array.isArray(context.changedFiles)) {
|
|
35496
|
+
files.push(...context.changedFiles.filter((f) => typeof f === "string"));
|
|
35497
|
+
}
|
|
35498
|
+
if (typeof context.targetFile === "string") {
|
|
35499
|
+
files.push(context.targetFile);
|
|
35500
|
+
}
|
|
35501
|
+
return [...new Set(files)];
|
|
35502
|
+
}
|
|
35503
|
+
inferOperation(intentType) {
|
|
35504
|
+
const mapping = {
|
|
35505
|
+
create: "create",
|
|
35506
|
+
modify: "modify",
|
|
35507
|
+
delete: "delete",
|
|
35508
|
+
refactor: "modify",
|
|
35509
|
+
fix: "modify",
|
|
35510
|
+
test: "create"
|
|
35511
|
+
};
|
|
35512
|
+
return mapping[intentType] ?? "modify";
|
|
35513
|
+
}
|
|
35514
|
+
intentMentionsSensitiveArea(intent) {
|
|
35515
|
+
const sensitiveKeywords = ["auth", "billing", "payment", "security", "credential", "secret", "env"];
|
|
35516
|
+
const description = intent.description.toLowerCase();
|
|
35517
|
+
const target = intent.target.toLowerCase();
|
|
35518
|
+
return sensitiveKeywords.some((kw) => description.includes(kw) || target.includes(kw));
|
|
35519
|
+
}
|
|
35520
|
+
checkIntentScopeMismatch(intent, context) {
|
|
35521
|
+
const affectedFiles = this.extractAffectedFiles(context);
|
|
35522
|
+
if (intent.scope === "file" && affectedFiles.length > 3) {
|
|
35523
|
+
return `Intent scope is 'file' but ${affectedFiles.length} files affected`;
|
|
35524
|
+
}
|
|
35525
|
+
if (intent.scope === "function" && affectedFiles.length > 2) {
|
|
35526
|
+
return `Intent scope is 'function' but ${affectedFiles.length} files affected`;
|
|
35527
|
+
}
|
|
35528
|
+
return null;
|
|
33792
35529
|
}
|
|
33793
35530
|
initializeDefaultRules() {
|
|
33794
35531
|
this.rules = [
|
|
@@ -33810,14 +35547,39 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
33810
35547
|
];
|
|
33811
35548
|
}
|
|
33812
35549
|
};
|
|
35550
|
+
DEFAULT_OPTIONS6 = {
|
|
35551
|
+
includeTypeImports: false,
|
|
35552
|
+
includeJsxCalls: true,
|
|
35553
|
+
maxChainDepth: 10,
|
|
35554
|
+
trackUnresolvedSymbols: true
|
|
35555
|
+
};
|
|
35556
|
+
PARSER_OPTIONS = {
|
|
35557
|
+
sourceType: "module",
|
|
35558
|
+
plugins: [
|
|
35559
|
+
"typescript",
|
|
35560
|
+
"jsx",
|
|
35561
|
+
"decorators-legacy",
|
|
35562
|
+
"classProperties",
|
|
35563
|
+
"classPrivateProperties",
|
|
35564
|
+
"classPrivateMethods",
|
|
35565
|
+
"optionalChaining",
|
|
35566
|
+
"nullishCoalescingOperator",
|
|
35567
|
+
"dynamicImport",
|
|
35568
|
+
"importMeta",
|
|
35569
|
+
"topLevelAwait",
|
|
35570
|
+
"exportDefaultFrom",
|
|
35571
|
+
"exportNamespaceFrom"
|
|
35572
|
+
],
|
|
35573
|
+
errorRecovery: true
|
|
35574
|
+
};
|
|
33813
35575
|
ClaimExtractor = class {
|
|
33814
35576
|
/**
|
|
33815
35577
|
* Extract all claims from content
|
|
33816
35578
|
*/
|
|
33817
|
-
async extract(content) {
|
|
35579
|
+
async extract(content, filePath = "unknown") {
|
|
33818
35580
|
const claims = [];
|
|
33819
35581
|
claims.push(...this.extractImports(content));
|
|
33820
|
-
claims.push(...this.extractFunctionCalls(content));
|
|
35582
|
+
claims.push(...this.extractFunctionCalls(content, filePath));
|
|
33821
35583
|
claims.push(...this.extractTypeReferences(content));
|
|
33822
35584
|
claims.push(...this.extractApiEndpoints(content));
|
|
33823
35585
|
claims.push(...this.extractEnvVariables(content));
|
|
@@ -33825,6 +35587,33 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
33825
35587
|
claims.push(...this.extractPackageDependencies(content));
|
|
33826
35588
|
return claims;
|
|
33827
35589
|
}
|
|
35590
|
+
/**
|
|
35591
|
+
* Extract with full AST analysis for a TypeScript/JavaScript file
|
|
35592
|
+
* Returns both claims and detailed AST analysis
|
|
35593
|
+
*/
|
|
35594
|
+
async extractWithAst(content, filePath) {
|
|
35595
|
+
const astAnalysis = extractCallsFromSource(content, filePath);
|
|
35596
|
+
const phantomCalls = detectPhantomCalls(astAnalysis);
|
|
35597
|
+
const claims = await this.extract(content, filePath);
|
|
35598
|
+
return {
|
|
35599
|
+
claims,
|
|
35600
|
+
astAnalysis,
|
|
35601
|
+
phantomCalls
|
|
35602
|
+
};
|
|
35603
|
+
}
|
|
35604
|
+
/**
|
|
35605
|
+
* Quick check for phantom calls only
|
|
35606
|
+
*/
|
|
35607
|
+
detectPhantomCalls(content, filePath) {
|
|
35608
|
+
const analysis = extractCallsFromSource(content, filePath);
|
|
35609
|
+
return detectPhantomCalls(analysis);
|
|
35610
|
+
}
|
|
35611
|
+
/**
|
|
35612
|
+
* Get full AST analysis for a file
|
|
35613
|
+
*/
|
|
35614
|
+
analyzeFile(content, filePath) {
|
|
35615
|
+
return extractCallsFromSource(content, filePath);
|
|
35616
|
+
}
|
|
33828
35617
|
/**
|
|
33829
35618
|
* Extract with full statistics
|
|
33830
35619
|
*/
|
|
@@ -33863,8 +35652,74 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
33863
35652
|
}
|
|
33864
35653
|
return claims;
|
|
33865
35654
|
}
|
|
33866
|
-
extractFunctionCalls(content) {
|
|
35655
|
+
extractFunctionCalls(content, filePath = "unknown") {
|
|
33867
35656
|
const claims = [];
|
|
35657
|
+
try {
|
|
35658
|
+
const analysis = extractCallsFromSource(content, filePath);
|
|
35659
|
+
for (const call of analysis.calls) {
|
|
35660
|
+
claims.push(this.createCallClaim(call, filePath, content));
|
|
35661
|
+
}
|
|
35662
|
+
for (const call of analysis.dynamicCalls) {
|
|
35663
|
+
const claim = this.createCallClaim(call, filePath, content);
|
|
35664
|
+
claim.confidence = 0.5;
|
|
35665
|
+
claims.push(claim);
|
|
35666
|
+
}
|
|
35667
|
+
} catch {
|
|
35668
|
+
const regexCalls = this.extractFunctionCallsRegex(content);
|
|
35669
|
+
claims.push(...regexCalls);
|
|
35670
|
+
}
|
|
35671
|
+
return claims;
|
|
35672
|
+
}
|
|
35673
|
+
/**
|
|
35674
|
+
* Create a claim from extracted call info
|
|
35675
|
+
*/
|
|
35676
|
+
createCallClaim(call, filePath, content) {
|
|
35677
|
+
const contextStart = Math.max(0, this.getCharIndexFromLine(content, call.location.line) - 50);
|
|
35678
|
+
const contextEnd = contextStart + 100;
|
|
35679
|
+
return {
|
|
35680
|
+
id: generateFindingId2(
|
|
35681
|
+
"claim-function_call",
|
|
35682
|
+
filePath,
|
|
35683
|
+
call.location.line,
|
|
35684
|
+
call.location.column,
|
|
35685
|
+
call.fullExpression
|
|
35686
|
+
),
|
|
35687
|
+
type: "function_call",
|
|
35688
|
+
value: call.fullExpression,
|
|
35689
|
+
location: {
|
|
35690
|
+
line: call.location.line,
|
|
35691
|
+
column: call.location.column,
|
|
35692
|
+
length: call.fullExpression.length
|
|
35693
|
+
},
|
|
35694
|
+
confidence: call.isDynamic ? 0.5 : 0.9,
|
|
35695
|
+
context: content.slice(contextStart, contextEnd)
|
|
35696
|
+
};
|
|
35697
|
+
}
|
|
35698
|
+
/**
|
|
35699
|
+
* Get character index from line number
|
|
35700
|
+
*/
|
|
35701
|
+
getCharIndexFromLine(content, line) {
|
|
35702
|
+
const lines = content.split("\n");
|
|
35703
|
+
let index = 0;
|
|
35704
|
+
for (let i = 0; i < Math.min(line - 1, lines.length); i++) {
|
|
35705
|
+
index += lines[i].length + 1;
|
|
35706
|
+
}
|
|
35707
|
+
return index;
|
|
35708
|
+
}
|
|
35709
|
+
/**
|
|
35710
|
+
* Fallback regex-based function call extraction
|
|
35711
|
+
*/
|
|
35712
|
+
extractFunctionCallsRegex(content) {
|
|
35713
|
+
const claims = [];
|
|
35714
|
+
const regex = /(\w+)\s*\(/g;
|
|
35715
|
+
const keywords = ["if", "while", "for", "switch", "catch", "function", "return"];
|
|
35716
|
+
let match;
|
|
35717
|
+
while ((match = regex.exec(content)) !== null) {
|
|
35718
|
+
const funcName = match[1];
|
|
35719
|
+
if (!keywords.includes(funcName)) {
|
|
35720
|
+
claims.push(this.createClaim("function_call", funcName, match.index, content));
|
|
35721
|
+
}
|
|
35722
|
+
}
|
|
33868
35723
|
return claims;
|
|
33869
35724
|
}
|
|
33870
35725
|
extractTypeReferences(content) {
|
|
@@ -33953,7 +35808,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
33953
35808
|
return builtins.includes(type);
|
|
33954
35809
|
}
|
|
33955
35810
|
};
|
|
33956
|
-
|
|
35811
|
+
DEFAULT_CONFIG25 = {
|
|
33957
35812
|
sources: ["truthpack", "filesystem", "package_json", "ast"],
|
|
33958
35813
|
truthpackPath: ".vibecheck/truthpack",
|
|
33959
35814
|
projectRoot: process.cwd(),
|
|
@@ -34009,7 +35864,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
34009
35864
|
logger;
|
|
34010
35865
|
performanceTracker;
|
|
34011
35866
|
constructor(config = {}) {
|
|
34012
|
-
this.config = { ...
|
|
35867
|
+
this.config = { ...DEFAULT_CONFIG25, ...config };
|
|
34013
35868
|
this.logger = getLogger("evidence-resolver");
|
|
34014
35869
|
this.performanceTracker = new PerformanceTracker();
|
|
34015
35870
|
this.evidenceCache = new Cache({
|
|
@@ -35106,7 +36961,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
35106
36961
|
return "significant";
|
|
35107
36962
|
}
|
|
35108
36963
|
};
|
|
35109
|
-
|
|
36964
|
+
DEFAULT_CONFIG34 = {
|
|
35110
36965
|
mode: "enforce",
|
|
35111
36966
|
strictMode: true,
|
|
35112
36967
|
allowPartialMatches: false,
|
|
@@ -35137,7 +36992,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
35137
36992
|
quickCheckCache;
|
|
35138
36993
|
disposed = false;
|
|
35139
36994
|
constructor(config = {}) {
|
|
35140
|
-
this.config = this.validateConfig({ ...
|
|
36995
|
+
this.config = this.validateConfig({ ...DEFAULT_CONFIG34, ...config });
|
|
35141
36996
|
this.logger = getLogger("firewall");
|
|
35142
36997
|
this.performanceTracker = new PerformanceTracker();
|
|
35143
36998
|
this.quickCheckCache = new Cache({
|
|
@@ -35415,6 +37270,77 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
35415
37270
|
});
|
|
35416
37271
|
return result;
|
|
35417
37272
|
}
|
|
37273
|
+
/**
|
|
37274
|
+
* Analyze a file for phantom calls (calls to non-existent functions)
|
|
37275
|
+
* This uses proper AST parsing to accurately detect function calls
|
|
37276
|
+
* and verify they reference imported or defined symbols.
|
|
37277
|
+
*/
|
|
37278
|
+
async analyzePhantomCalls(content, filePath) {
|
|
37279
|
+
this.assertNotDisposed();
|
|
37280
|
+
const startTime = performance.now();
|
|
37281
|
+
const analysis = extractCallsFromSource(content, filePath);
|
|
37282
|
+
const phantomCalls = detectPhantomCalls(analysis);
|
|
37283
|
+
const durationMs = performance.now() - startTime;
|
|
37284
|
+
let summary;
|
|
37285
|
+
if (phantomCalls.length === 0 && analysis.unresolvedSymbols.length === 0) {
|
|
37286
|
+
summary = `Clean: ${analysis.calls.length} calls verified against ${analysis.imports.length} imports`;
|
|
37287
|
+
} else {
|
|
37288
|
+
const issues = [];
|
|
37289
|
+
if (phantomCalls.length > 0) {
|
|
37290
|
+
issues.push(`${phantomCalls.length} phantom call(s)`);
|
|
37291
|
+
}
|
|
37292
|
+
if (analysis.unresolvedSymbols.length > 0) {
|
|
37293
|
+
issues.push(`${analysis.unresolvedSymbols.length} unresolved symbol(s)`);
|
|
37294
|
+
}
|
|
37295
|
+
if (analysis.dynamicCalls.length > 0) {
|
|
37296
|
+
issues.push(`${analysis.dynamicCalls.length} dynamic call(s) (risky)`);
|
|
37297
|
+
}
|
|
37298
|
+
summary = `Issues found: ${issues.join(", ")}`;
|
|
37299
|
+
}
|
|
37300
|
+
this.logger.debug("Phantom call analysis completed", {
|
|
37301
|
+
filePath,
|
|
37302
|
+
totalCalls: analysis.calls.length,
|
|
37303
|
+
phantomCount: phantomCalls.length,
|
|
37304
|
+
unresolvedCount: analysis.unresolvedSymbols.length,
|
|
37305
|
+
durationMs: Math.round(durationMs)
|
|
37306
|
+
});
|
|
37307
|
+
return {
|
|
37308
|
+
analysis,
|
|
37309
|
+
phantomCalls,
|
|
37310
|
+
isClean: phantomCalls.length === 0 && analysis.unresolvedSymbols.length === 0,
|
|
37311
|
+
summary
|
|
37312
|
+
};
|
|
37313
|
+
}
|
|
37314
|
+
/**
|
|
37315
|
+
* Validate that all function calls in content reference valid imports
|
|
37316
|
+
* Returns true if all calls can be verified, false if phantom calls detected
|
|
37317
|
+
*/
|
|
37318
|
+
async validateCalls(content, filePath) {
|
|
37319
|
+
this.assertNotDisposed();
|
|
37320
|
+
const { phantomCalls, analysis } = await this.analyzePhantomCalls(content, filePath);
|
|
37321
|
+
const suggestions = [];
|
|
37322
|
+
for (const call of phantomCalls) {
|
|
37323
|
+
const receiver = call.receiver ?? call.name;
|
|
37324
|
+
suggestions.push(
|
|
37325
|
+
`Add import for '${receiver}' or define it locally before calling '${call.fullExpression}'`
|
|
37326
|
+
);
|
|
37327
|
+
}
|
|
37328
|
+
for (const symbol of analysis.unresolvedSymbols) {
|
|
37329
|
+
if (!phantomCalls.some((c) => (c.receiver ?? c.name) === symbol)) {
|
|
37330
|
+
suggestions.push(`Symbol '${symbol}' is used but not imported or defined`);
|
|
37331
|
+
}
|
|
37332
|
+
}
|
|
37333
|
+
if (analysis.dynamicCalls.length > 0) {
|
|
37334
|
+
suggestions.push(
|
|
37335
|
+
`${analysis.dynamicCalls.length} dynamic call(s) detected - these cannot be statically verified`
|
|
37336
|
+
);
|
|
37337
|
+
}
|
|
37338
|
+
return {
|
|
37339
|
+
valid: phantomCalls.length === 0,
|
|
37340
|
+
phantomCalls,
|
|
37341
|
+
suggestions
|
|
37342
|
+
};
|
|
37343
|
+
}
|
|
35418
37344
|
/**
|
|
35419
37345
|
* Get performance metrics
|
|
35420
37346
|
*/
|
|
@@ -35781,20 +37707,20 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
35781
37707
|
}
|
|
35782
37708
|
return this.currentIntent;
|
|
35783
37709
|
}
|
|
35784
|
-
isPathAllowed(
|
|
35785
|
-
return intent.allowedPaths.some((pattern) => this.matchPath(
|
|
37710
|
+
isPathAllowed(path34, intent) {
|
|
37711
|
+
return intent.allowedPaths.some((pattern) => this.matchPath(path34, pattern));
|
|
35786
37712
|
}
|
|
35787
|
-
isPathExcluded(
|
|
37713
|
+
isPathExcluded(path34, intent) {
|
|
35788
37714
|
if (!intent.excludedPaths) return false;
|
|
35789
|
-
return intent.excludedPaths.some((pattern) => this.matchPath(
|
|
37715
|
+
return intent.excludedPaths.some((pattern) => this.matchPath(path34, pattern));
|
|
35790
37716
|
}
|
|
35791
|
-
isInTargetFiles(
|
|
37717
|
+
isInTargetFiles(path34, targetFiles) {
|
|
35792
37718
|
return targetFiles.some(
|
|
35793
|
-
(target) =>
|
|
37719
|
+
(target) => path34 === target || path34.endsWith(target) || this.matchPath(path34, target)
|
|
35794
37720
|
);
|
|
35795
37721
|
}
|
|
35796
|
-
matchPath(
|
|
35797
|
-
const normalizedPath =
|
|
37722
|
+
matchPath(path34, pattern) {
|
|
37723
|
+
const normalizedPath = path34.replace(/\\/g, "/");
|
|
35798
37724
|
const normalizedPattern = pattern.replace(/\\/g, "/");
|
|
35799
37725
|
if (normalizedPath === normalizedPattern) return true;
|
|
35800
37726
|
if (normalizedPattern.includes("*")) {
|
|
@@ -36221,22 +38147,22 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
36221
38147
|
// ============================================================================
|
|
36222
38148
|
// Path Matching Utilities
|
|
36223
38149
|
// ============================================================================
|
|
36224
|
-
isPathInScope(
|
|
38150
|
+
isPathInScope(path34, scope) {
|
|
36225
38151
|
if (scope.targetFiles && scope.targetFiles.length > 0) {
|
|
36226
38152
|
return scope.targetFiles.some(
|
|
36227
|
-
(target) =>
|
|
38153
|
+
(target) => path34 === target || path34.endsWith(target) || this.matchGlob(path34, target)
|
|
36228
38154
|
);
|
|
36229
38155
|
}
|
|
36230
|
-
return scope.allowedPaths.some((pattern) => this.matchGlob(
|
|
38156
|
+
return scope.allowedPaths.some((pattern) => this.matchGlob(path34, pattern));
|
|
36231
38157
|
}
|
|
36232
|
-
isPathExcluded(
|
|
36233
|
-
return scope.excludedPaths.some((pattern) => this.matchGlob(
|
|
38158
|
+
isPathExcluded(path34, scope) {
|
|
38159
|
+
return scope.excludedPaths.some((pattern) => this.matchGlob(path34, pattern));
|
|
36234
38160
|
}
|
|
36235
|
-
matchesSensitivePattern(
|
|
36236
|
-
return policy.sensitivePatterns.some((pattern) => this.matchGlob(
|
|
38161
|
+
matchesSensitivePattern(path34, policy) {
|
|
38162
|
+
return policy.sensitivePatterns.some((pattern) => this.matchGlob(path34, pattern));
|
|
36237
38163
|
}
|
|
36238
|
-
matchGlob(
|
|
36239
|
-
const normalizedPath =
|
|
38164
|
+
matchGlob(path34, pattern) {
|
|
38165
|
+
const normalizedPath = path34.replace(/\\/g, "/");
|
|
36240
38166
|
const normalizedPattern = pattern.replace(/\\/g, "/");
|
|
36241
38167
|
if (normalizedPath === normalizedPattern) return true;
|
|
36242
38168
|
if (normalizedPattern.includes("*")) {
|
|
@@ -36361,7 +38287,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
36361
38287
|
};
|
|
36362
38288
|
}
|
|
36363
38289
|
};
|
|
36364
|
-
|
|
38290
|
+
DEFAULT_CONFIG44 = {
|
|
36365
38291
|
enabled: true,
|
|
36366
38292
|
severity: "error",
|
|
36367
38293
|
allowedExternalPaths: [
|
|
@@ -36376,7 +38302,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
36376
38302
|
config;
|
|
36377
38303
|
constructor(config = {}) {
|
|
36378
38304
|
super(config);
|
|
36379
|
-
this.config = { ...
|
|
38305
|
+
this.config = { ...DEFAULT_CONFIG44, ...config };
|
|
36380
38306
|
}
|
|
36381
38307
|
evaluate(context) {
|
|
36382
38308
|
const apiClaims = context.claims.filter((c) => c.type === "api_endpoint");
|
|
@@ -36396,28 +38322,28 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
36396
38322
|
this.generateSuggestion(ghostRoute.value)
|
|
36397
38323
|
);
|
|
36398
38324
|
}
|
|
36399
|
-
isExternalPath(
|
|
38325
|
+
isExternalPath(path34) {
|
|
36400
38326
|
if (!this.config.allowedExternalPaths) return false;
|
|
36401
38327
|
return this.config.allowedExternalPaths.some(
|
|
36402
|
-
(pattern) => this.matchPattern(
|
|
38328
|
+
(pattern) => this.matchPattern(path34, pattern)
|
|
36403
38329
|
);
|
|
36404
38330
|
}
|
|
36405
|
-
isApiPath(
|
|
38331
|
+
isApiPath(path34) {
|
|
36406
38332
|
if (!this.config.apiPrefixes || this.config.apiPrefixes.length === 0) {
|
|
36407
|
-
return
|
|
38333
|
+
return path34.startsWith("/");
|
|
36408
38334
|
}
|
|
36409
|
-
return this.config.apiPrefixes.some((prefix) =>
|
|
38335
|
+
return this.config.apiPrefixes.some((prefix) => path34.startsWith(prefix));
|
|
36410
38336
|
}
|
|
36411
|
-
generateSuggestion(
|
|
36412
|
-
const parts =
|
|
38337
|
+
generateSuggestion(path34) {
|
|
38338
|
+
const parts = path34.split("/").filter(Boolean);
|
|
36413
38339
|
const resource = parts.find((p9) => !p9.startsWith(":") && !p9.startsWith("["));
|
|
36414
38340
|
if (resource) {
|
|
36415
|
-
return `Create the route handler for "${
|
|
38341
|
+
return `Create the route handler for "${path34}" or check if the path is correct. Run: vibecheck truthpack --scope routes`;
|
|
36416
38342
|
}
|
|
36417
38343
|
return `Verify the API endpoint exists. Run: vibecheck truthpack --scope routes`;
|
|
36418
38344
|
}
|
|
36419
38345
|
};
|
|
36420
|
-
|
|
38346
|
+
DEFAULT_CONFIG53 = {
|
|
36421
38347
|
enabled: true,
|
|
36422
38348
|
severity: "error",
|
|
36423
38349
|
builtinAllowed: [
|
|
@@ -36440,7 +38366,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
36440
38366
|
config;
|
|
36441
38367
|
constructor(config = {}) {
|
|
36442
38368
|
super(config);
|
|
36443
|
-
this.config = { ...
|
|
38369
|
+
this.config = { ...DEFAULT_CONFIG53, ...config };
|
|
36444
38370
|
}
|
|
36445
38371
|
evaluate(context) {
|
|
36446
38372
|
const envClaims = context.claims.filter((c) => c.type === "env_variable");
|
|
@@ -36484,7 +38410,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
36484
38410
|
return suggestions.join(". ");
|
|
36485
38411
|
}
|
|
36486
38412
|
};
|
|
36487
|
-
|
|
38413
|
+
DEFAULT_CONFIG62 = {
|
|
36488
38414
|
enabled: true,
|
|
36489
38415
|
severity: "warning",
|
|
36490
38416
|
authKeywords: [
|
|
@@ -36519,7 +38445,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
36519
38445
|
*/
|
|
36520
38446
|
constructor(config = {}) {
|
|
36521
38447
|
super(config);
|
|
36522
|
-
this.config = { ...
|
|
38448
|
+
this.config = { ...DEFAULT_CONFIG62, ...config };
|
|
36523
38449
|
}
|
|
36524
38450
|
/**
|
|
36525
38451
|
* Evaluates the policy context for authentication drift violations.
|
|
@@ -36625,7 +38551,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
36625
38551
|
return null;
|
|
36626
38552
|
}
|
|
36627
38553
|
};
|
|
36628
|
-
|
|
38554
|
+
DEFAULT_CONFIG72 = {
|
|
36629
38555
|
enabled: true,
|
|
36630
38556
|
severity: "warning",
|
|
36631
38557
|
checkRequests: true,
|
|
@@ -36638,7 +38564,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
36638
38564
|
config;
|
|
36639
38565
|
constructor(config = {}) {
|
|
36640
38566
|
super(config);
|
|
36641
|
-
this.config = { ...
|
|
38567
|
+
this.config = { ...DEFAULT_CONFIG72, ...config };
|
|
36642
38568
|
}
|
|
36643
38569
|
evaluate(context) {
|
|
36644
38570
|
const apiClaims = context.claims.filter((c) => c.type === "api_endpoint");
|
|
@@ -36712,7 +38638,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
36712
38638
|
return contractPatterns.some((pattern) => pattern.test(typeName));
|
|
36713
38639
|
}
|
|
36714
38640
|
};
|
|
36715
|
-
|
|
38641
|
+
DEFAULT_CONFIG82 = {
|
|
36716
38642
|
enabled: true,
|
|
36717
38643
|
severity: "error",
|
|
36718
38644
|
maxAffectedFiles: 10,
|
|
@@ -36736,7 +38662,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
36736
38662
|
config;
|
|
36737
38663
|
constructor(config = {}) {
|
|
36738
38664
|
super(config);
|
|
36739
|
-
this.config = { ...
|
|
38665
|
+
this.config = { ...DEFAULT_CONFIG82, ...config };
|
|
36740
38666
|
}
|
|
36741
38667
|
evaluate(context) {
|
|
36742
38668
|
const claimCountViolation = this.checkClaimCount(context);
|
|
@@ -36856,7 +38782,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
36856
38782
|
}, {});
|
|
36857
38783
|
}
|
|
36858
38784
|
};
|
|
36859
|
-
|
|
38785
|
+
DEFAULT_CONFIG92 = {
|
|
36860
38786
|
enabled: true,
|
|
36861
38787
|
severity: "error",
|
|
36862
38788
|
dangerousPatterns: [
|
|
@@ -36894,7 +38820,7 @@ var init_chunk_ZAOHGXRU = __esm({
|
|
|
36894
38820
|
config;
|
|
36895
38821
|
constructor(config = {}) {
|
|
36896
38822
|
super(config);
|
|
36897
|
-
this.config = { ...
|
|
38823
|
+
this.config = { ...DEFAULT_CONFIG92, ...config };
|
|
36898
38824
|
}
|
|
36899
38825
|
evaluate(context) {
|
|
36900
38826
|
for (const claim of context.claims) {
|
|
@@ -40974,7 +42900,7 @@ function detectTemplate(input) {
|
|
|
40974
42900
|
}
|
|
40975
42901
|
return null;
|
|
40976
42902
|
}
|
|
40977
|
-
var DEFAULT_CONFIG11, PromptBuilder, DEFAULT_CONFIG27, ContextInjector, DEFAULT_CONFIG36, QualityAnalyzer, DEFAULT_CONFIG46, TASK_PATTERNS,
|
|
42903
|
+
var DEFAULT_CONFIG11, PromptBuilder, DEFAULT_CONFIG27, ContextInjector, DEFAULT_CONFIG36, QualityAnalyzer, DEFAULT_CONFIG46, TASK_PATTERNS, SCOPE_PATTERNS2, TaskPlanner, DEFAULT_CONFIG55, AMBIGUOUS_PATTERNS, DANGEROUS_PATTERNS2, SCOPE_EXPLOSION_PATTERNS, CONTEXT_INDICATORS, PromptVerifier, CodeGenerationTemplate, CodeReviewTemplate, ExplanationTemplate, CATEGORY_METADATA, SMART_VARIABLES, FREE_TEMPLATES, PRO_TEMPLATES, PROMPT_TEMPLATES;
|
|
40978
42904
|
var init_chunk_XV5LCZVB = __esm({
|
|
40979
42905
|
"../../packages/core/dist/chunk-XV5LCZVB.js"() {
|
|
40980
42906
|
DEFAULT_CONFIG11 = {
|
|
@@ -41403,7 +43329,7 @@ ${item.content}`;
|
|
|
41403
43329
|
test: /\b(test|spec|coverage|verify)\b/i,
|
|
41404
43330
|
document: /\b(document|comment|readme|docs)\b/i
|
|
41405
43331
|
};
|
|
41406
|
-
|
|
43332
|
+
SCOPE_PATTERNS2 = {
|
|
41407
43333
|
component: /\b(component|widget|element|ui)\b/i,
|
|
41408
43334
|
service: /\b(service|provider|manager|handler)\b/i,
|
|
41409
43335
|
api: /\b(api|endpoint|route|controller)\b/i,
|
|
@@ -41459,7 +43385,7 @@ ${item.content}`;
|
|
|
41459
43385
|
}
|
|
41460
43386
|
detectScope(description) {
|
|
41461
43387
|
const scopes = [];
|
|
41462
|
-
for (const [scope, pattern] of Object.entries(
|
|
43388
|
+
for (const [scope, pattern] of Object.entries(SCOPE_PATTERNS2)) {
|
|
41463
43389
|
if (pattern.test(description)) {
|
|
41464
43390
|
scopes.push(scope);
|
|
41465
43391
|
}
|
|
@@ -45045,7 +46971,7 @@ function safeDivide(numerator, denominator, defaultValue = 0) {
|
|
|
45045
46971
|
}
|
|
45046
46972
|
return numerator / denominator;
|
|
45047
46973
|
}
|
|
45048
|
-
var DEFAULT_APPLY_OPTIONS, PROTECTED_FILES, PatchApplicationError, FileLockManager, PatchApplier, DEFAULT_LIMITS, AutoFixOrchestrator, MAX_FILE_READ_SIZE, PROTECTED_PATTERNS, BaseFixModule,
|
|
46974
|
+
var DEFAULT_APPLY_OPTIONS, PROTECTED_FILES, PatchApplicationError, FileLockManager, PatchApplier, DEFAULT_LIMITS, AutoFixOrchestrator, MAX_FILE_READ_SIZE, PROTECTED_PATTERNS, BaseFixModule, traverse2, SilentFailureFixModule, traverse22, AUTH_MIDDLEWARE_TEMPLATES, AuthGapFixModule, traverse3, ENV_VAR_PATTERNS, EnvVarFixModule, ROUTE_TEMPLATES, GhostRouteFixModule, DEFAULT_CONFIG13, ConfidenceScorer, DEFAULT_APPLY_OPTIONS2, ReviewPipeline, DEFAULT_CONFIG29, MAX_BACKUP_SIZE, RollbackError, RollbackManager;
|
|
45049
46975
|
var init_chunk_OTMTDDNE = __esm({
|
|
45050
46976
|
"../../packages/core/dist/chunk-OTMTDDNE.js"() {
|
|
45051
46977
|
init_chunk_EDYODCBV();
|
|
@@ -46324,9 +48250,9 @@ var init_chunk_OTMTDDNE = __esm({
|
|
|
46324
48250
|
/**
|
|
46325
48251
|
* Simple pattern matching for blocked paths
|
|
46326
48252
|
*/
|
|
46327
|
-
matchesPattern(
|
|
48253
|
+
matchesPattern(path34, pattern) {
|
|
46328
48254
|
const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
46329
|
-
return new RegExp(regexPattern).test(
|
|
48255
|
+
return new RegExp(regexPattern).test(path34);
|
|
46330
48256
|
}
|
|
46331
48257
|
/**
|
|
46332
48258
|
* Group issues by type for batch processing
|
|
@@ -47025,7 +48951,7 @@ var init_chunk_OTMTDDNE = __esm({
|
|
|
47025
48951
|
return lines.slice(startIndex, endIndex);
|
|
47026
48952
|
}
|
|
47027
48953
|
};
|
|
47028
|
-
|
|
48954
|
+
traverse2 = _traverse.default ?? _traverse;
|
|
47029
48955
|
SilentFailureFixModule = class extends BaseFixModule {
|
|
47030
48956
|
id = "silent-failure-fix";
|
|
47031
48957
|
name = "Silent Failure Fix";
|
|
@@ -47104,7 +49030,7 @@ var init_chunk_OTMTDDNE = __esm({
|
|
|
47104
49030
|
try {
|
|
47105
49031
|
const isTypeScript = filePath.endsWith(".ts") || filePath.endsWith(".tsx");
|
|
47106
49032
|
const isJSX = filePath.endsWith(".tsx") || filePath.endsWith(".jsx");
|
|
47107
|
-
return
|
|
49033
|
+
return parser2.parse(content, {
|
|
47108
49034
|
sourceType: "module",
|
|
47109
49035
|
plugins: [
|
|
47110
49036
|
isTypeScript ? "typescript" : null,
|
|
@@ -47125,9 +49051,9 @@ var init_chunk_OTMTDDNE = __esm({
|
|
|
47125
49051
|
findSilentFailures(ast, content) {
|
|
47126
49052
|
const patterns = [];
|
|
47127
49053
|
const lines = content.split("\n");
|
|
47128
|
-
|
|
47129
|
-
CatchClause: (
|
|
47130
|
-
const node =
|
|
49054
|
+
traverse2(ast, {
|
|
49055
|
+
CatchClause: (path34) => {
|
|
49056
|
+
const node = path34.node;
|
|
47131
49057
|
const body = node.body;
|
|
47132
49058
|
if (body.body.length === 0) {
|
|
47133
49059
|
patterns.push({
|
|
@@ -47150,9 +49076,9 @@ var init_chunk_OTMTDDNE = __esm({
|
|
|
47150
49076
|
});
|
|
47151
49077
|
}
|
|
47152
49078
|
},
|
|
47153
|
-
TryStatement: (
|
|
47154
|
-
const node =
|
|
47155
|
-
const parent =
|
|
49079
|
+
TryStatement: (path34) => {
|
|
49080
|
+
const node = path34.node;
|
|
49081
|
+
const parent = path34.parent;
|
|
47156
49082
|
if (t.isBlockStatement(parent)) {
|
|
47157
49083
|
const siblings = parent.body;
|
|
47158
49084
|
const tryIndex = siblings.indexOf(node);
|
|
@@ -47171,8 +49097,8 @@ var init_chunk_OTMTDDNE = __esm({
|
|
|
47171
49097
|
}
|
|
47172
49098
|
}
|
|
47173
49099
|
},
|
|
47174
|
-
CallExpression: (
|
|
47175
|
-
const node =
|
|
49100
|
+
CallExpression: (path34) => {
|
|
49101
|
+
const node = path34.node;
|
|
47176
49102
|
if (t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.property, { name: "catch" })) {
|
|
47177
49103
|
const handler = node.arguments[0];
|
|
47178
49104
|
if ((t.isArrowFunctionExpression(handler) || t.isFunctionExpression(handler)) && t.isBlockStatement(handler.body) && handler.body.body.length === 0) {
|
|
@@ -47209,7 +49135,7 @@ var init_chunk_OTMTDDNE = __esm({
|
|
|
47209
49135
|
*/
|
|
47210
49136
|
hasThrowStatement(block) {
|
|
47211
49137
|
let hasThrow = false;
|
|
47212
|
-
|
|
49138
|
+
traverse2(
|
|
47213
49139
|
t.file(t.program([t.expressionStatement(t.arrowFunctionExpression([], block))])),
|
|
47214
49140
|
{
|
|
47215
49141
|
ThrowStatement: () => {
|
|
@@ -47419,7 +49345,7 @@ ${closingLine}`;
|
|
|
47419
49345
|
return lines.join("\n");
|
|
47420
49346
|
}
|
|
47421
49347
|
};
|
|
47422
|
-
|
|
49348
|
+
traverse22 = _traverse.default ?? _traverse;
|
|
47423
49349
|
AUTH_MIDDLEWARE_TEMPLATES = {
|
|
47424
49350
|
express: {
|
|
47425
49351
|
import: "const { requireAuth } = require('./middleware/auth');",
|
|
@@ -47554,9 +49480,9 @@ ${closingLine}`;
|
|
|
47554
49480
|
this.detectIndentation(content);
|
|
47555
49481
|
const hasAuthImport = content.includes("requireAuth");
|
|
47556
49482
|
const routesToFix = [];
|
|
47557
|
-
|
|
47558
|
-
CallExpression: (
|
|
47559
|
-
const node =
|
|
49483
|
+
traverse22(ast, {
|
|
49484
|
+
CallExpression: (path34) => {
|
|
49485
|
+
const node = path34.node;
|
|
47560
49486
|
const callee = node.callee;
|
|
47561
49487
|
if (t.isMemberExpression(callee) && t.isIdentifier(callee.property) && ["get", "post", "put", "delete", "patch"].includes(callee.property.name)) {
|
|
47562
49488
|
const args = node.arguments;
|
|
@@ -47791,7 +49717,7 @@ ${closingLine}`;
|
|
|
47791
49717
|
try {
|
|
47792
49718
|
const isTypeScript = filePath.endsWith(".ts") || filePath.endsWith(".tsx");
|
|
47793
49719
|
const isJSX = filePath.endsWith(".tsx") || filePath.endsWith(".jsx");
|
|
47794
|
-
return
|
|
49720
|
+
return parser2.parse(content, {
|
|
47795
49721
|
sourceType: "module",
|
|
47796
49722
|
plugins: [
|
|
47797
49723
|
isTypeScript ? "typescript" : null,
|
|
@@ -48059,8 +49985,8 @@ ${closingLine}`;
|
|
|
48059
49985
|
hasEnvCheck(ast, envVarName) {
|
|
48060
49986
|
let hasCheck = false;
|
|
48061
49987
|
traverse3(ast, {
|
|
48062
|
-
IfStatement: (
|
|
48063
|
-
const test =
|
|
49988
|
+
IfStatement: (path34) => {
|
|
49989
|
+
const test = path34.node.test;
|
|
48064
49990
|
if (t.isUnaryExpression(test, { operator: "!" }) && t.isMemberExpression(test.argument)) {
|
|
48065
49991
|
const member = test.argument;
|
|
48066
49992
|
if (t.isMemberExpression(member.object) && t.isIdentifier(member.object.object, { name: "process" }) && t.isIdentifier(member.object.property, { name: "env" }) && t.isIdentifier(member.property, { name: envVarName })) {
|
|
@@ -48068,12 +49994,12 @@ ${closingLine}`;
|
|
|
48068
49994
|
}
|
|
48069
49995
|
}
|
|
48070
49996
|
},
|
|
48071
|
-
CallExpression: (
|
|
48072
|
-
const callee =
|
|
49997
|
+
CallExpression: (path34) => {
|
|
49998
|
+
const callee = path34.node.callee;
|
|
48073
49999
|
if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {
|
|
48074
50000
|
const method = callee.property.name;
|
|
48075
50001
|
if (["parse", "validate", "check", "assert"].includes(method)) {
|
|
48076
|
-
const argsStr = JSON.stringify(
|
|
50002
|
+
const argsStr = JSON.stringify(path34.node.arguments);
|
|
48077
50003
|
if (argsStr.includes(envVarName)) {
|
|
48078
50004
|
hasCheck = true;
|
|
48079
50005
|
}
|
|
@@ -48137,7 +50063,7 @@ const ${this.toCamelCase(envVarName)} = process.env.${envVarName}${envInfo.defau
|
|
|
48137
50063
|
parseCode(content, filePath) {
|
|
48138
50064
|
try {
|
|
48139
50065
|
const isTypeScript = filePath.endsWith(".ts") || filePath.endsWith(".tsx");
|
|
48140
|
-
return
|
|
50066
|
+
return parser2.parse(content, {
|
|
48141
50067
|
sourceType: "module",
|
|
48142
50068
|
plugins: isTypeScript ? ["typescript", "decorators-legacy"] : ["decorators-legacy"]
|
|
48143
50069
|
});
|
|
@@ -48308,10 +50234,10 @@ export default function Page() {
|
|
|
48308
50234
|
/**
|
|
48309
50235
|
* Parse route information from the path
|
|
48310
50236
|
*/
|
|
48311
|
-
parseRouteInfo(
|
|
50237
|
+
parseRouteInfo(path34) {
|
|
48312
50238
|
let method = "GET";
|
|
48313
|
-
let routePath =
|
|
48314
|
-
const methodMatch =
|
|
50239
|
+
let routePath = path34;
|
|
50240
|
+
const methodMatch = path34.match(/^(GET|POST|PUT|DELETE|PATCH)\s+(.+)$/i);
|
|
48315
50241
|
if (methodMatch) {
|
|
48316
50242
|
method = methodMatch[1].toUpperCase();
|
|
48317
50243
|
routePath = methodMatch[2];
|
|
@@ -48361,10 +50287,10 @@ export default function Page() {
|
|
|
48361
50287
|
];
|
|
48362
50288
|
let routesFile = null;
|
|
48363
50289
|
let routesContent = null;
|
|
48364
|
-
for (const
|
|
48365
|
-
routesContent = await this.readFile(context,
|
|
50290
|
+
for (const path34 of routesPaths) {
|
|
50291
|
+
routesContent = await this.readFile(context, path34);
|
|
48366
50292
|
if (routesContent !== null) {
|
|
48367
|
-
routesFile =
|
|
50293
|
+
routesFile = path34;
|
|
48368
50294
|
break;
|
|
48369
50295
|
}
|
|
48370
50296
|
}
|
|
@@ -48447,10 +50373,10 @@ export default router;
|
|
|
48447
50373
|
];
|
|
48448
50374
|
let routesFile = null;
|
|
48449
50375
|
let routesContent = null;
|
|
48450
|
-
for (const
|
|
48451
|
-
routesContent = await this.readFile(context,
|
|
50376
|
+
for (const path34 of routesPaths) {
|
|
50377
|
+
routesContent = await this.readFile(context, path34);
|
|
48452
50378
|
if (routesContent !== null) {
|
|
48453
|
-
routesFile =
|
|
50379
|
+
routesFile = path34;
|
|
48454
50380
|
break;
|
|
48455
50381
|
}
|
|
48456
50382
|
}
|
|
@@ -49618,8 +51544,8 @@ export async function handle(req: Request): Promise<Response> {
|
|
|
49618
51544
|
/**
|
|
49619
51545
|
* Sanitize a relative path
|
|
49620
51546
|
*/
|
|
49621
|
-
sanitizeRelativePath(
|
|
49622
|
-
let sanitized =
|
|
51547
|
+
sanitizeRelativePath(path34) {
|
|
51548
|
+
let sanitized = path34.replace(/^[/\\]+/, "").replace(/^[A-Za-z]:/, "");
|
|
49623
51549
|
sanitized = sanitized.split(/[/\\]/).filter((p9) => p9 !== "..").join("/");
|
|
49624
51550
|
return sanitized || DEFAULT_CONFIG29.logPath;
|
|
49625
51551
|
}
|
|
@@ -50248,6 +52174,7 @@ __export(dist_exports, {
|
|
|
50248
52174
|
IncrementalEngine: () => IncrementalEngine,
|
|
50249
52175
|
IncrementalScanner: () => IncrementalScanner,
|
|
50250
52176
|
IntelligenceEngine: () => IntelligenceEngine,
|
|
52177
|
+
IntentExtractor: () => IntentExtractor,
|
|
50251
52178
|
IntentStore: () => IntentStore,
|
|
50252
52179
|
IntentValidator: () => IntentValidator,
|
|
50253
52180
|
IssueTracker: () => IssueTracker,
|
|
@@ -50323,6 +52250,7 @@ __export(dist_exports, {
|
|
|
50323
52250
|
STATUS_ICONS: () => STATUS_ICONS,
|
|
50324
52251
|
SafeModeManager: () => SafeModeManager,
|
|
50325
52252
|
SafetyGuard: () => SafetyGuard,
|
|
52253
|
+
ScopeCreepDetector: () => ScopeCreepDetector,
|
|
50326
52254
|
ScopeExplosionRule: () => ScopeExplosionRule,
|
|
50327
52255
|
SecretsAllowlist: () => SecretsAllowlist,
|
|
50328
52256
|
SecretsError: () => SecretsError,
|
|
@@ -50481,6 +52409,7 @@ __export(dist_exports, {
|
|
|
50481
52409
|
createSafeModeManager: () => createSafeModeManager,
|
|
50482
52410
|
createSafetyGuard: () => createSafetyGuard,
|
|
50483
52411
|
createScanWorkerPool: () => createScanWorkerPool,
|
|
52412
|
+
createScopeCreepDetector: () => createScopeCreepDetector,
|
|
50484
52413
|
createSessionRecorder: () => createSessionRecorder,
|
|
50485
52414
|
createShipScoreCalculator: () => createShipScoreCalculator,
|
|
50486
52415
|
createSsrfGuard: () => createSsrfGuard,
|
|
@@ -50495,6 +52424,7 @@ __export(dist_exports, {
|
|
|
50495
52424
|
defineRule: () => defineRule,
|
|
50496
52425
|
detectFlowIssues: () => detectFlowIssues,
|
|
50497
52426
|
detectIDE: () => detectIDE,
|
|
52427
|
+
detectPhantomCalls: () => detectPhantomCalls,
|
|
50498
52428
|
detectPhase: () => detectPhase,
|
|
50499
52429
|
detectProject: () => detectProject,
|
|
50500
52430
|
detectTemplate: () => detectTemplate,
|
|
@@ -50507,17 +52437,21 @@ __export(dist_exports, {
|
|
|
50507
52437
|
enableDevMode: () => enableDevMode,
|
|
50508
52438
|
enforceCommandAccess: () => enforceCommandAccess,
|
|
50509
52439
|
escapeHtml: () => escapeHtml2,
|
|
52440
|
+
evaluateOperation: () => evaluateOperation,
|
|
50510
52441
|
exceedsSafetyLimit: () => exceedsSafetyLimit,
|
|
50511
52442
|
executeRules: () => executeRules,
|
|
50512
52443
|
exportReceiptJson: () => exportReceiptJson,
|
|
50513
52444
|
exportToPlaywright: () => exportToPlaywright,
|
|
50514
52445
|
extractAnchors: () => extractAnchors,
|
|
52446
|
+
extractCallsFromSource: () => extractCallsFromSource,
|
|
50515
52447
|
extractClaimsFromCode: () => extractClaimsFromCode,
|
|
50516
52448
|
extractClaimsFromPrompt: () => extractClaimsFromPrompt,
|
|
50517
52449
|
extractCommands: () => extractCommands,
|
|
50518
52450
|
extractFilePaths: () => extractFilePaths,
|
|
50519
52451
|
extractFunctionCalls: () => extractFunctionCalls,
|
|
50520
52452
|
extractImports: () => extractImports,
|
|
52453
|
+
extractIntent: () => extractIntent,
|
|
52454
|
+
extractIntentSync: () => extractIntentSync,
|
|
50521
52455
|
fileWalker: () => fileWalker,
|
|
50522
52456
|
filenameValidator: () => filenameValidator,
|
|
50523
52457
|
filesystemVerifier: () => filesystemVerifier,
|
|
@@ -50650,6 +52584,7 @@ __export(dist_exports, {
|
|
|
50650
52584
|
getIncrementalEngine: () => getIncrementalEngine,
|
|
50651
52585
|
getInstallInstructions: () => getInstallInstructions,
|
|
50652
52586
|
getIntelligenceEngine: () => getIntelligenceEngine,
|
|
52587
|
+
getIntentExtractor: () => getIntentExtractor,
|
|
50653
52588
|
getIntentStore: () => getIntentStore,
|
|
50654
52589
|
getLearningSystem: () => getLearningSystem,
|
|
50655
52590
|
getLogger: () => getLogger,
|
|
@@ -50821,6 +52756,7 @@ __export(dist_exports, {
|
|
|
50821
52756
|
resetHallucinationEngine: () => resetHallucinationEngine,
|
|
50822
52757
|
resetIncrementalEngine: () => resetIncrementalEngine,
|
|
50823
52758
|
resetIntelligenceEngine: () => resetIntelligenceEngine,
|
|
52759
|
+
resetIntentExtractor: () => resetIntentExtractor,
|
|
50824
52760
|
resetIntentStore: () => resetIntentStore,
|
|
50825
52761
|
resetLearningSystem: () => resetLearningSystem,
|
|
50826
52762
|
resetMissionService: () => resetMissionService,
|
|
@@ -50893,6 +52829,7 @@ __export(dist_exports, {
|
|
|
50893
52829
|
unregisterCommand: () => unregisterCommand,
|
|
50894
52830
|
unregisterTempFile: () => unregisterTempFile,
|
|
50895
52831
|
updateManifest: () => updateManifest,
|
|
52832
|
+
validateCallAgainstImports: () => validateCallAgainstImports,
|
|
50896
52833
|
validateContract: () => validateContract,
|
|
50897
52834
|
validateIssue: () => validateIssue,
|
|
50898
52835
|
validateManifest: () => validateManifest,
|
|
@@ -51405,10 +53342,10 @@ function getHealthBar(score) {
|
|
|
51405
53342
|
}
|
|
51406
53343
|
}
|
|
51407
53344
|
function getClaimExtractor() {
|
|
51408
|
-
if (!
|
|
51409
|
-
|
|
53345
|
+
if (!globalExtractor2) {
|
|
53346
|
+
globalExtractor2 = new ClaimExtractor2();
|
|
51410
53347
|
}
|
|
51411
|
-
return
|
|
53348
|
+
return globalExtractor2;
|
|
51412
53349
|
}
|
|
51413
53350
|
function extractClaimsFromCode(content, filePath) {
|
|
51414
53351
|
return getClaimExtractor().extractFromCode(content, filePath);
|
|
@@ -57028,11 +58965,11 @@ function findAllPaths(startId, endId, adjacency, nodes, edges, maxDepth) {
|
|
|
57028
58965
|
{ path: [startId], visited: /* @__PURE__ */ new Set([startId]) }
|
|
57029
58966
|
];
|
|
57030
58967
|
while (queue.length > 0) {
|
|
57031
|
-
const { path:
|
|
57032
|
-
const current =
|
|
57033
|
-
if (
|
|
58968
|
+
const { path: path332, visited } = queue.shift();
|
|
58969
|
+
const current = path332[path332.length - 1];
|
|
58970
|
+
if (path332.length > maxDepth) continue;
|
|
57034
58971
|
if (current === endId) {
|
|
57035
|
-
paths.push([...
|
|
58972
|
+
paths.push([...path332]);
|
|
57036
58973
|
continue;
|
|
57037
58974
|
}
|
|
57038
58975
|
const neighbors = adjacency.get(current) || [];
|
|
@@ -57040,7 +58977,7 @@ function findAllPaths(startId, endId, adjacency, nodes, edges, maxDepth) {
|
|
|
57040
58977
|
if (!visited.has(neighbor)) {
|
|
57041
58978
|
const newVisited = new Set(visited);
|
|
57042
58979
|
newVisited.add(neighbor);
|
|
57043
|
-
queue.push({ path: [...
|
|
58980
|
+
queue.push({ path: [...path332, neighbor], visited: newVisited });
|
|
57044
58981
|
}
|
|
57045
58982
|
}
|
|
57046
58983
|
}
|
|
@@ -57058,21 +58995,21 @@ function getPathEdges(nodeIds, allEdges) {
|
|
|
57058
58995
|
}
|
|
57059
58996
|
function detectFlowIssues(paths) {
|
|
57060
58997
|
const issues = [];
|
|
57061
|
-
for (const
|
|
57062
|
-
if (!
|
|
58998
|
+
for (const path332 of paths) {
|
|
58999
|
+
if (!path332.hasValidation && path332.risk.level !== "low") {
|
|
57063
59000
|
issues.push({
|
|
57064
59001
|
id: generateId2(),
|
|
57065
|
-
severity:
|
|
59002
|
+
severity: path332.risk.level === "critical" ? "critical" : path332.risk.level === "high" ? "error" : "warning",
|
|
57066
59003
|
type: "missing_validation",
|
|
57067
|
-
title: `Unvalidated data flow to ${
|
|
57068
|
-
description: `Data from ${
|
|
57069
|
-
path:
|
|
57070
|
-
suggestion: getSuggestionForSink(
|
|
59004
|
+
title: `Unvalidated data flow to ${path332.sink.sinkCategory}`,
|
|
59005
|
+
description: `Data from ${path332.source.sourceCategory} flows to ${path332.sink.sinkCategory} without validation.`,
|
|
59006
|
+
path: path332,
|
|
59007
|
+
suggestion: getSuggestionForSink(path332.sink.sinkCategory),
|
|
57071
59008
|
docLink: getDocLinkForIssue("missing_validation")
|
|
57072
59009
|
});
|
|
57073
59010
|
}
|
|
57074
|
-
if (
|
|
57075
|
-
const hasProperSanitization =
|
|
59011
|
+
if (path332.sink.riskLevel === "critical") {
|
|
59012
|
+
const hasProperSanitization = path332.validations.some(
|
|
57076
59013
|
(v) => v.metadata?.patternName === "sanitization" || v.metadata?.patternName === "parameterized_query"
|
|
57077
59014
|
);
|
|
57078
59015
|
if (!hasProperSanitization) {
|
|
@@ -57080,22 +59017,22 @@ function detectFlowIssues(paths) {
|
|
|
57080
59017
|
id: generateId2(),
|
|
57081
59018
|
severity: "critical",
|
|
57082
59019
|
type: "unsafe_sink",
|
|
57083
|
-
title: `Potentially unsafe ${
|
|
57084
|
-
description: `Data flows to a critical sink (${
|
|
57085
|
-
path:
|
|
57086
|
-
suggestion: getSuggestionForSink(
|
|
59020
|
+
title: `Potentially unsafe ${path332.sink.sinkCategory}`,
|
|
59021
|
+
description: `Data flows to a critical sink (${path332.sink.label}) without proper sanitization.`,
|
|
59022
|
+
path: path332,
|
|
59023
|
+
suggestion: getSuggestionForSink(path332.sink.sinkCategory),
|
|
57087
59024
|
docLink: getDocLinkForIssue("unsafe_sink")
|
|
57088
59025
|
});
|
|
57089
59026
|
}
|
|
57090
59027
|
}
|
|
57091
|
-
if (
|
|
59028
|
+
if (path332.source.sourceCategory === "user_input" && ["sql_query", "shell_exec", "eval", "html_render"].includes(path332.sink.sinkCategory || "")) {
|
|
57092
59029
|
issues.push({
|
|
57093
59030
|
id: generateId2(),
|
|
57094
59031
|
severity: "critical",
|
|
57095
59032
|
type: "untrusted_source",
|
|
57096
59033
|
title: "User input flows to sensitive operation",
|
|
57097
|
-
description: `User input from ${
|
|
57098
|
-
path:
|
|
59034
|
+
description: `User input from ${path332.source.label} flows directly to ${path332.sink.label}.`,
|
|
59035
|
+
path: path332,
|
|
57099
59036
|
suggestion: "Always validate and sanitize user input before using it in sensitive operations.",
|
|
57100
59037
|
docLink: getDocLinkForIssue("untrusted_source")
|
|
57101
59038
|
});
|
|
@@ -57582,7 +59519,7 @@ function parseSessionRecording(json) {
|
|
|
57582
59519
|
function serializeSessionRecording(session) {
|
|
57583
59520
|
return JSON.stringify(session, null, 2);
|
|
57584
59521
|
}
|
|
57585
|
-
var DEFAULT_CONFIG14, PostSaveHook, execAsync2, DEFAULT_CONFIG210, PreCommitHook, DEFAULT_CONFIG38, BUILTINS, DependencyCheckHook, DEFAULT_CONFIG48, hookTypeValidator, HooksManager, DEFAULT_CONFIG57, AUDIT_TYPES, typeValidator, resultValidator, AuditLogger, DEFAULT_CONFIG65, HallucinationAnalytics, DEFAULT_CONFIG75, EvidencePackGenerator, HybridModeService, DEFAULT_PREDICTIVE_CONFIG, DEFAULT_INTELLIGENCE_CONFIG, DEFAULT_CONFIG84, ProjectLearner, globalLearner,
|
|
59522
|
+
var DEFAULT_CONFIG14, PostSaveHook, execAsync2, DEFAULT_CONFIG210, PreCommitHook, DEFAULT_CONFIG38, BUILTINS, DependencyCheckHook, DEFAULT_CONFIG48, hookTypeValidator, HooksManager, DEFAULT_CONFIG57, AUDIT_TYPES, typeValidator, resultValidator, AuditLogger, DEFAULT_CONFIG65, HallucinationAnalytics, DEFAULT_CONFIG75, EvidencePackGenerator, HybridModeService, DEFAULT_PREDICTIVE_CONFIG, DEFAULT_INTELLIGENCE_CONFIG, DEFAULT_CONFIG84, ProjectLearner, globalLearner, DEFAULT_CONFIG93, FILE_TYPE_PATTERNS, PURPOSE_PATTERNS, SENSITIVITY_INDICATORS, SemanticContextAnalyzer, globalAnalyzer, DEFAULT_CONFIG102, PredictiveEngine, globalEngine2, DEFAULT_CONFIG112, IntelligenceEngine, globalEngine22, DEFAULT_INCREMENTAL_CONFIG, DEFAULT_WORKER_POOL_CONFIG, DEFAULT_STREAM_CONFIG, DEFAULT_MULTI_CACHE_CONFIG, DEFAULT_CONFIG122, IncrementalEngine, globalEngine3, DEFAULT_CONFIG132, WorkerPool, DEFAULT_CONFIG142, ProgressTracker, gzipAsync2, gunzipAsync2, DEFAULT_CONFIG15, MultiLevelCache, globalCache, DEFAULT_CONFIG16, PerformanceScanner, globalScanner2, DEFAULT_OPTIONS7, HallucinationEngine, globalEngine4, EXTRACTION_PATTERNS, PROMPT_PATTERNS, ClaimExtractor2, globalExtractor2, DEFAULT_CONFIG17, RealtimeAnalyzer, globalAnalyzer2, DEFAULT_OPTIONS23, HASH_CACHE_PATH, FILE_CONTENT_CACHE, DEFAULT_LEARNING_CONFIG, LearningStorage, ConfidenceCalibrator2, IssueTracker, LearningSystem, globalLearningSystem, DEFAULT_WORKER_CONFIG, WorkerPool2, globalPool, DEFAULT_OPTIONS33, ResultStream, GitChangeDetector, ParallelAnalyzer, DEFAULT_POLICY_OPTIONS, severitySchema, metavariableComparisonSchema, pathFilterSchema, autofixSchema, ruleMetadataSchema, policyRuleSchema, policyConfigSchema, EXAMPLE_POLICY, policyCache, PolicyParser, PolicyResolver, PolicyEngine2, globalEngine5, DEFAULT_PERMISSIONS, DEFAULT_OPTIONS42, PluginLoader, DEFAULT_SANDBOX_OPTIONS, SandboxedRuleContext, PluginSandbox, FROZEN_GLOBALS, BLOCKED_GLOBALS, PluginManager, globalManager, TIER_CONFIGS, DEFAULT_FORGE_CONFIG, PHASE_RULE_CONFIGS, CATEGORY_IMPACT_WEIGHTS, VIBECHECK_ATTRIBUTION, VIBECHECK_ATTRIBUTION2, VIBECHECK_ATTRIBUTION_SKILL, SIGNAL_WEIGHTS, IGNORED_DIRS, SOURCE_EXTENSIONS, TEST_PATTERNS2, CONTEXT_FILE, VIBECHECK_DIR, MEMORY_VERSION, MAX_HISTORY_ENTRIES, MAX_BEHAVIOR_SIGNALS, VELOCITY_WINDOW_DAYS, ContextMemory, DEFAULT_DEBOUNCE_MS, DEFAULT_BATCH_THRESHOLD, BATCH_WINDOW_MS, COMMIT_SETTLE_MS, DEFAULT_WATCH_PATTERNS, DEFAULT_IGNORE_PATTERNS, STRUCTURAL_PATTERNS, CONFIG_PATTERNS2, DEPENDENCY_PATTERNS, ForgeWatcher, ChangeAccumulator, DEFAULT_WINDOW_MS, DEFAULT_THRESHOLD, CLEANUP_INTERVAL_MS, BatchDetector, SCAFFOLD_PATTERNS, PHASE_CONFIGS, CHANGE_TO_RULE_MAP, RuleOrchestrator, MEMORY_VERSION2, MEMORY_FILE, VIBECHECK_DIR2, MAX_TIMELINE_EVENTS, MAX_CONVERSATIONS, MAX_CODE_CHANGES2, MAX_INSIGHTS, MAX_SESSIONS, EnhancedMemorySystem, SUPPORTED_EXTENSIONS, PATTERNS22, analysisCache, IGNORED_DIRS2, SOURCE_EXTENSIONS2, COMPLEXITY_THRESHOLDS, MIN_FREQUENCY_THRESHOLD, MIN_EXAMPLES, IGNORED_DIRS3, SOURCE_EXTENSIONS3, generateId, DEFAULT_SOURCE_PATTERNS, DEFAULT_SINK_PATTERNS, DEFAULT_VALIDATION_PATTERNS, generateId2, FlowTracingEngine, COLORS, BOX, DEFAULT_SAFE_MODE_CONFIG, AGGRESSIVE_MODE_CONFIG, SafeModeManager, SAFE_PATTERNS, MEDIUM_PATTERNS, HIGH_PATTERNS, DESTRUCTIVE_PATTERNS, ActionClassifier, ChaosSessionRecorder, DEFAULT_REPLAY_OPTIONS;
|
|
57586
59523
|
var init_dist2 = __esm({
|
|
57587
59524
|
"../../packages/core/dist/index.js"() {
|
|
57588
59525
|
init_chunk_7A7U3ABV();
|
|
@@ -57610,7 +59547,7 @@ var init_dist2 = __esm({
|
|
|
57610
59547
|
init_chunk_ENJMGXRB();
|
|
57611
59548
|
init_chunk_PP7CJ5TZ();
|
|
57612
59549
|
init_chunk_PP7CJ5TZ();
|
|
57613
|
-
|
|
59550
|
+
init_chunk_QBVYHKIC();
|
|
57614
59551
|
init_chunk_RPKCC72C();
|
|
57615
59552
|
init_chunk_RPKCC72C();
|
|
57616
59553
|
init_chunk_A4CYIMGM();
|
|
@@ -60563,7 +62500,7 @@ ${issues.map((i) => ` - ${i}`).join("\n")}`;
|
|
|
60563
62500
|
}
|
|
60564
62501
|
};
|
|
60565
62502
|
globalLearner = null;
|
|
60566
|
-
|
|
62503
|
+
DEFAULT_CONFIG93 = {
|
|
60567
62504
|
cacheTtlMs: 5 * 60 * 1e3,
|
|
60568
62505
|
// 5 minutes
|
|
60569
62506
|
maxFileSize: 500 * 1024,
|
|
@@ -60779,7 +62716,7 @@ ${issues.map((i) => ` - ${i}`).join("\n")}`;
|
|
|
60779
62716
|
logger;
|
|
60780
62717
|
constructor(projectRoot, config = {}) {
|
|
60781
62718
|
this.projectRoot = projectRoot;
|
|
60782
|
-
this.config = { ...
|
|
62719
|
+
this.config = { ...DEFAULT_CONFIG93, ...config };
|
|
60783
62720
|
this.contextCache = new Cache({
|
|
60784
62721
|
maxSize: 500,
|
|
60785
62722
|
defaultTtlMs: this.config.cacheTtlMs
|
|
@@ -63224,7 +65161,7 @@ ${content}`;
|
|
|
63224
65161
|
}
|
|
63225
65162
|
};
|
|
63226
65163
|
globalScanner2 = null;
|
|
63227
|
-
|
|
65164
|
+
DEFAULT_OPTIONS7 = {
|
|
63228
65165
|
incremental: true,
|
|
63229
65166
|
parallel: true,
|
|
63230
65167
|
workers: 4,
|
|
@@ -63250,7 +65187,7 @@ ${content}`;
|
|
|
63250
65187
|
mockDetectorScanner = null;
|
|
63251
65188
|
initialized = false;
|
|
63252
65189
|
constructor(options) {
|
|
63253
|
-
this.options = { ...
|
|
65190
|
+
this.options = { ...DEFAULT_OPTIONS7, ...options };
|
|
63254
65191
|
this.logger = getLogger("hallucination-engine");
|
|
63255
65192
|
}
|
|
63256
65193
|
/**
|
|
@@ -64293,7 +66230,7 @@ ${content}`;
|
|
|
64293
66230
|
};
|
|
64294
66231
|
}
|
|
64295
66232
|
};
|
|
64296
|
-
|
|
66233
|
+
globalExtractor2 = null;
|
|
64297
66234
|
DEFAULT_CONFIG17 = {
|
|
64298
66235
|
projectRoot: process.cwd(),
|
|
64299
66236
|
debounceMs: 150,
|
|
@@ -70926,10 +72863,10 @@ ${content}`;
|
|
|
70926
72863
|
/**
|
|
70927
72864
|
* Check if a path matches a glob pattern
|
|
70928
72865
|
*/
|
|
70929
|
-
matchesPattern(
|
|
72866
|
+
matchesPattern(path332, pattern) {
|
|
70930
72867
|
const regexPattern = pattern.replace(/\*\*/g, "<<<GLOBSTAR>>>").replace(/\*/g, "[^/]*").replace(/<<<GLOBSTAR>>>/g, ".*");
|
|
70931
72868
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
70932
|
-
return regex.test(
|
|
72869
|
+
return regex.test(path332);
|
|
70933
72870
|
}
|
|
70934
72871
|
/**
|
|
70935
72872
|
* Check if a selector might match a denied selector pattern
|
|
@@ -72077,12 +74014,12 @@ async function runInitWizard() {
|
|
|
72077
74014
|
configContent
|
|
72078
74015
|
};
|
|
72079
74016
|
}
|
|
72080
|
-
async function confirmOverwrite(
|
|
74017
|
+
async function confirmOverwrite(path34) {
|
|
72081
74018
|
if (!shouldPrompt()) {
|
|
72082
74019
|
return false;
|
|
72083
74020
|
}
|
|
72084
74021
|
const confirmed = await p4.confirm({
|
|
72085
|
-
message: `Config file already exists at ${chalk8.cyan(
|
|
74022
|
+
message: `Config file already exists at ${chalk8.cyan(path34)}. Overwrite?`,
|
|
72086
74023
|
initialValue: false
|
|
72087
74024
|
});
|
|
72088
74025
|
if (p4.isCancel(confirmed)) {
|
|
@@ -72284,7 +74221,7 @@ async function configureOutput(currentConfig) {
|
|
|
72284
74221
|
}
|
|
72285
74222
|
|
|
72286
74223
|
// src/lib/version.ts
|
|
72287
|
-
var CLI_VERSION = "1.1.
|
|
74224
|
+
var CLI_VERSION = "1.1.2" ;
|
|
72288
74225
|
var CLI_NAME = "vibecheck-ai" ;
|
|
72289
74226
|
|
|
72290
74227
|
// src/ui/command-header.tsx
|
|
@@ -73266,10 +75203,10 @@ async function oauthLogin(provider) {
|
|
|
73266
75203
|
const s = p4.spinner();
|
|
73267
75204
|
s.start(chalk8.cyan(`Opening ${provider} login page...`));
|
|
73268
75205
|
try {
|
|
73269
|
-
const
|
|
75206
|
+
const open3 = (await import('open')).default;
|
|
73270
75207
|
const webUrl = process.env.VIBECHECK_WEB_URL ?? "https://app.vibecheckai.dev";
|
|
73271
75208
|
const providerLower = provider.toLowerCase();
|
|
73272
|
-
await
|
|
75209
|
+
await open3(`${webUrl}/auth/login?provider=${providerLower}`);
|
|
73273
75210
|
s.stop(chalk8.green(`\u2713 Browser opened!`));
|
|
73274
75211
|
} catch {
|
|
73275
75212
|
s.stop(chalk8.yellow("\u26A0 Could not open browser automatically"));
|
|
@@ -74958,12 +76895,18 @@ async function checkScanAllowed() {
|
|
|
74958
76895
|
};
|
|
74959
76896
|
}
|
|
74960
76897
|
try {
|
|
76898
|
+
const isApiKey = credentials.authToken.startsWith("vc_");
|
|
76899
|
+
const headers = {
|
|
76900
|
+
"Content-Type": "application/json"
|
|
76901
|
+
};
|
|
76902
|
+
if (isApiKey) {
|
|
76903
|
+
headers["X-API-Key"] = credentials.authToken;
|
|
76904
|
+
} else {
|
|
76905
|
+
headers["Authorization"] = `Bearer ${credentials.authToken}`;
|
|
76906
|
+
}
|
|
74961
76907
|
const response = await fetch(`${credentials.apiUrl}/api/v1/scans/cli/check`, {
|
|
74962
76908
|
method: "POST",
|
|
74963
|
-
headers
|
|
74964
|
-
"Authorization": `Bearer ${credentials.authToken}`,
|
|
74965
|
-
"Content-Type": "application/json"
|
|
74966
|
-
},
|
|
76909
|
+
headers,
|
|
74967
76910
|
body: JSON.stringify({ amount: 1 })
|
|
74968
76911
|
});
|
|
74969
76912
|
const data = await response.json();
|
|
@@ -75011,12 +76954,18 @@ async function recordScan(metadata) {
|
|
|
75011
76954
|
};
|
|
75012
76955
|
}
|
|
75013
76956
|
try {
|
|
76957
|
+
const isApiKey = credentials.authToken.startsWith("vc_");
|
|
76958
|
+
const headers = {
|
|
76959
|
+
"Content-Type": "application/json"
|
|
76960
|
+
};
|
|
76961
|
+
if (isApiKey) {
|
|
76962
|
+
headers["X-API-Key"] = credentials.authToken;
|
|
76963
|
+
} else {
|
|
76964
|
+
headers["Authorization"] = `Bearer ${credentials.authToken}`;
|
|
76965
|
+
}
|
|
75014
76966
|
const response = await fetch(`${credentials.apiUrl}/api/v1/scans/cli/record`, {
|
|
75015
76967
|
method: "POST",
|
|
75016
|
-
headers
|
|
75017
|
-
"Authorization": `Bearer ${credentials.authToken}`,
|
|
75018
|
-
"Content-Type": "application/json"
|
|
75019
|
-
},
|
|
76968
|
+
headers,
|
|
75020
76969
|
body: JSON.stringify(metadata ?? {})
|
|
75021
76970
|
});
|
|
75022
76971
|
const data = await response.json();
|
|
@@ -75073,6 +77022,140 @@ function formatQuotaMessage(status) {
|
|
|
75073
77022
|
return `${status.remaining} scan${status.remaining === 1 ? "" : "s"} remaining (${status.used}/${status.limit} used this month)`;
|
|
75074
77023
|
}
|
|
75075
77024
|
|
|
77025
|
+
// src/lib/project-id.ts
|
|
77026
|
+
init_errors();
|
|
77027
|
+
function generateStableHash(value) {
|
|
77028
|
+
const hash = createHash("sha256").update(value.toLowerCase().trim()).digest("hex");
|
|
77029
|
+
return hash.substring(0, 12);
|
|
77030
|
+
}
|
|
77031
|
+
function extractGitIdentifier(remoteUrl) {
|
|
77032
|
+
let cleaned = remoteUrl.replace(/^(https?:\/\/|git@|ssh:\/\/)/i, "").replace(/\.git$/i, "");
|
|
77033
|
+
cleaned = cleaned.replace(/^[^:]+:/, "");
|
|
77034
|
+
cleaned = cleaned.replace(/^[^/]+\//, "");
|
|
77035
|
+
return cleaned;
|
|
77036
|
+
}
|
|
77037
|
+
function getGitRemoteUrl(projectRoot) {
|
|
77038
|
+
try {
|
|
77039
|
+
const result = execSync("git remote get-url origin", {
|
|
77040
|
+
cwd: projectRoot,
|
|
77041
|
+
encoding: "utf-8",
|
|
77042
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
77043
|
+
timeout: 5e3
|
|
77044
|
+
});
|
|
77045
|
+
return result.trim();
|
|
77046
|
+
} catch {
|
|
77047
|
+
return void 0;
|
|
77048
|
+
}
|
|
77049
|
+
}
|
|
77050
|
+
function getPackageJsonName(projectRoot) {
|
|
77051
|
+
try {
|
|
77052
|
+
const packageJsonPath = path18__default.join(projectRoot, "package.json");
|
|
77053
|
+
if (!existsSync(packageJsonPath)) {
|
|
77054
|
+
return void 0;
|
|
77055
|
+
}
|
|
77056
|
+
const content = readFileSync(packageJsonPath, "utf-8");
|
|
77057
|
+
const packageJson = JSON.parse(content);
|
|
77058
|
+
return packageJson.name?.trim();
|
|
77059
|
+
} catch {
|
|
77060
|
+
return void 0;
|
|
77061
|
+
}
|
|
77062
|
+
}
|
|
77063
|
+
async function resolveProjectId(ctx = {}) {
|
|
77064
|
+
const projectRoot = ctx.projectRoot ?? process.cwd();
|
|
77065
|
+
if (ctx.cliProjectId && ctx.cliProjectId.trim()) {
|
|
77066
|
+
return {
|
|
77067
|
+
projectId: ctx.cliProjectId.trim(),
|
|
77068
|
+
source: "cli-flag"
|
|
77069
|
+
};
|
|
77070
|
+
}
|
|
77071
|
+
if (ctx.config?.projectId && ctx.config.projectId.trim()) {
|
|
77072
|
+
return {
|
|
77073
|
+
projectId: ctx.config.projectId.trim(),
|
|
77074
|
+
source: "config-file"
|
|
77075
|
+
};
|
|
77076
|
+
}
|
|
77077
|
+
const envProjectId = process.env.VIBECHECK_PROJECT_ID;
|
|
77078
|
+
if (envProjectId && envProjectId.trim()) {
|
|
77079
|
+
return {
|
|
77080
|
+
projectId: envProjectId.trim(),
|
|
77081
|
+
source: "env-var"
|
|
77082
|
+
};
|
|
77083
|
+
}
|
|
77084
|
+
const gitRemoteUrl = getGitRemoteUrl(projectRoot);
|
|
77085
|
+
if (gitRemoteUrl) {
|
|
77086
|
+
const gitIdentifier = extractGitIdentifier(gitRemoteUrl);
|
|
77087
|
+
const hashedId = `git-${generateStableHash(gitIdentifier)}`;
|
|
77088
|
+
return {
|
|
77089
|
+
projectId: hashedId,
|
|
77090
|
+
source: "git-remote",
|
|
77091
|
+
originalValue: gitIdentifier
|
|
77092
|
+
};
|
|
77093
|
+
}
|
|
77094
|
+
const packageName = getPackageJsonName(projectRoot);
|
|
77095
|
+
if (packageName) {
|
|
77096
|
+
const hashedId = `pkg-${generateStableHash(packageName)}`;
|
|
77097
|
+
return {
|
|
77098
|
+
projectId: hashedId,
|
|
77099
|
+
source: "package-json",
|
|
77100
|
+
originalValue: packageName
|
|
77101
|
+
};
|
|
77102
|
+
}
|
|
77103
|
+
const dirName = path18__default.basename(path18__default.resolve(projectRoot));
|
|
77104
|
+
if (dirName && dirName !== "/" && dirName !== ".") {
|
|
77105
|
+
const fullPath = path18__default.resolve(projectRoot);
|
|
77106
|
+
const hashedId = `dir-${generateStableHash(fullPath)}`;
|
|
77107
|
+
return {
|
|
77108
|
+
projectId: hashedId,
|
|
77109
|
+
source: "directory-hash",
|
|
77110
|
+
originalValue: dirName
|
|
77111
|
+
};
|
|
77112
|
+
}
|
|
77113
|
+
const allowInteractive = ctx.allowInteractive ?? env.isInteractive;
|
|
77114
|
+
if (allowInteractive && ctx.interactiveCallback) {
|
|
77115
|
+
const interactiveId = await ctx.interactiveCallback();
|
|
77116
|
+
if (interactiveId && interactiveId.trim()) {
|
|
77117
|
+
return {
|
|
77118
|
+
projectId: interactiveId.trim(),
|
|
77119
|
+
source: "interactive"
|
|
77120
|
+
};
|
|
77121
|
+
}
|
|
77122
|
+
}
|
|
77123
|
+
throw new VibeCheckError(
|
|
77124
|
+
"Could not determine project ID",
|
|
77125
|
+
"PROJECT_ID_REQUIRED",
|
|
77126
|
+
{
|
|
77127
|
+
context: { projectRoot },
|
|
77128
|
+
suggestions: [
|
|
77129
|
+
"Set VIBECHECK_PROJECT_ID environment variable",
|
|
77130
|
+
'Add "projectId" to your vibecheck.config.mjs',
|
|
77131
|
+
"Use --project-id flag when running the command",
|
|
77132
|
+
"Initialize a git repository with a remote: git remote add origin <url>",
|
|
77133
|
+
'Add a "name" field to your package.json'
|
|
77134
|
+
]
|
|
77135
|
+
}
|
|
77136
|
+
);
|
|
77137
|
+
}
|
|
77138
|
+
function formatProjectIdSource(result) {
|
|
77139
|
+
switch (result.source) {
|
|
77140
|
+
case "cli-flag":
|
|
77141
|
+
return "CLI flag (--project-id)";
|
|
77142
|
+
case "config-file":
|
|
77143
|
+
return "config file (vibecheck.config)";
|
|
77144
|
+
case "env-var":
|
|
77145
|
+
return "environment variable (VIBECHECK_PROJECT_ID)";
|
|
77146
|
+
case "git-remote":
|
|
77147
|
+
return `git remote (${result.originalValue ?? "origin"})`;
|
|
77148
|
+
case "package-json":
|
|
77149
|
+
return `package.json (${result.originalValue ?? "name"})`;
|
|
77150
|
+
case "directory-hash":
|
|
77151
|
+
return `directory hash (${result.originalValue ?? "cwd"})`;
|
|
77152
|
+
case "interactive":
|
|
77153
|
+
return "interactive selection";
|
|
77154
|
+
default:
|
|
77155
|
+
return result.source;
|
|
77156
|
+
}
|
|
77157
|
+
}
|
|
77158
|
+
|
|
75076
77159
|
// src/commands/scan.ts
|
|
75077
77160
|
init_truthpack();
|
|
75078
77161
|
init_utils();
|
|
@@ -77856,9 +79939,91 @@ async function shipCommand(options) {
|
|
|
77856
79939
|
}
|
|
77857
79940
|
process.exit(1);
|
|
77858
79941
|
}
|
|
79942
|
+
let resolvedProjectId;
|
|
79943
|
+
try {
|
|
79944
|
+
resolvedProjectId = await resolveProjectId({
|
|
79945
|
+
cliProjectId: options.projectId,
|
|
79946
|
+
config: { projectId: config.projectId },
|
|
79947
|
+
projectRoot,
|
|
79948
|
+
allowInteractive: env.isInteractive && !options.json
|
|
79949
|
+
});
|
|
79950
|
+
if (options.verbose) {
|
|
79951
|
+
logger2.info(`Project ID: ${resolvedProjectId.projectId} (${formatProjectIdSource(resolvedProjectId)})`);
|
|
79952
|
+
}
|
|
79953
|
+
} catch (projectIdError) {
|
|
79954
|
+
if (options.json) {
|
|
79955
|
+
console.log(JSON.stringify({
|
|
79956
|
+
success: false,
|
|
79957
|
+
error: "Could not determine project ID",
|
|
79958
|
+
details: projectIdError instanceof Error ? projectIdError.message : String(projectIdError),
|
|
79959
|
+
suggestions: [
|
|
79960
|
+
"Set VIBECHECK_PROJECT_ID environment variable",
|
|
79961
|
+
'Add "projectId" to your vibecheck.config.mjs',
|
|
79962
|
+
"Use --project-id flag when running the command"
|
|
79963
|
+
]
|
|
79964
|
+
}, null, 2));
|
|
79965
|
+
} else {
|
|
79966
|
+
logger2.error("Could not determine project ID.");
|
|
79967
|
+
logger2.error("");
|
|
79968
|
+
logger2.error("Set one of the following:");
|
|
79969
|
+
logger2.dim(" \u2022 VIBECHECK_PROJECT_ID environment variable");
|
|
79970
|
+
logger2.dim(' \u2022 "projectId" in vibecheck.config.mjs');
|
|
79971
|
+
logger2.dim(" \u2022 --project-id flag");
|
|
79972
|
+
logger2.dim(" \u2022 Initialize git with a remote");
|
|
79973
|
+
logger2.dim(' \u2022 Add "name" to package.json');
|
|
79974
|
+
}
|
|
79975
|
+
process.exit(1);
|
|
79976
|
+
}
|
|
77859
79977
|
if (!existsSync(path18__default.join(projectRoot, "package.json"))) {
|
|
77860
79978
|
logger2.warn("No package.json found. Are you in a project directory?");
|
|
77861
79979
|
}
|
|
79980
|
+
const quotaCheck = await checkScanAllowed();
|
|
79981
|
+
if (!quotaCheck.success) {
|
|
79982
|
+
if (quotaCheck.error?.code === "QUOTA_EXCEEDED") {
|
|
79983
|
+
if (options.json) {
|
|
79984
|
+
console.log(JSON.stringify({
|
|
79985
|
+
success: false,
|
|
79986
|
+
error: "Quota exceeded",
|
|
79987
|
+
message: quotaCheck.error.message,
|
|
79988
|
+
quota: quotaCheck.error.details,
|
|
79989
|
+
upgradeUrl: quotaCheck.error.details?.upgradeUrl ?? "https://vibecheck.dev/pricing"
|
|
79990
|
+
}, null, 2));
|
|
79991
|
+
} else {
|
|
79992
|
+
logger2.error("");
|
|
79993
|
+
logger2.error(colors.error(" Ship quota exceeded"));
|
|
79994
|
+
if (quotaCheck.error.details) {
|
|
79995
|
+
logger2.error(colors.muted(` ${formatQuotaMessage(quotaCheck.error.details)}`));
|
|
79996
|
+
}
|
|
79997
|
+
logger2.error("");
|
|
79998
|
+
logger2.error(colors.info(" Upgrade to Pro for unlimited ships: https://vibecheck.dev/pricing"));
|
|
79999
|
+
logger2.error("");
|
|
80000
|
+
}
|
|
80001
|
+
process.exit(1);
|
|
80002
|
+
}
|
|
80003
|
+
if (quotaCheck.error?.code !== "NOT_AUTHENTICATED") {
|
|
80004
|
+
logger2.warn(`Quota check failed: ${quotaCheck.error?.message ?? "Unknown error"}. Proceeding anyway.`);
|
|
80005
|
+
}
|
|
80006
|
+
} else if (quotaCheck.data?.remaining === 0) {
|
|
80007
|
+
if (!quotaCheck.data.gracePeriod) {
|
|
80008
|
+
if (options.json) {
|
|
80009
|
+
console.log(JSON.stringify({
|
|
80010
|
+
success: false,
|
|
80011
|
+
error: "No ships remaining",
|
|
80012
|
+
message: formatQuotaMessage(quotaCheck.data),
|
|
80013
|
+
quota: quotaCheck.data,
|
|
80014
|
+
upgradeUrl: quotaCheck.data.upgradeUrl ?? "https://vibecheck.dev/pricing"
|
|
80015
|
+
}, null, 2));
|
|
80016
|
+
} else {
|
|
80017
|
+
logger2.error("");
|
|
80018
|
+
logger2.error(colors.error(" No ships remaining this month"));
|
|
80019
|
+
logger2.error(colors.muted(` ${formatQuotaMessage(quotaCheck.data)}`));
|
|
80020
|
+
logger2.error("");
|
|
80021
|
+
logger2.error(colors.info(" Upgrade to Pro for unlimited ships: https://vibecheck.dev/pricing"));
|
|
80022
|
+
logger2.error("");
|
|
80023
|
+
}
|
|
80024
|
+
process.exit(1);
|
|
80025
|
+
}
|
|
80026
|
+
}
|
|
77862
80027
|
logger2.info("Running ship checks...");
|
|
77863
80028
|
const checks = [];
|
|
77864
80029
|
let fixesApplied = 0;
|
|
@@ -77995,7 +80160,7 @@ async function shipCommand(options) {
|
|
|
77995
80160
|
task: async () => {
|
|
77996
80161
|
const realityTimeoutMs = ((options.realityTimeout ?? 300) + (options.realityStartupTimeout ?? 60)) * 1e3;
|
|
77997
80162
|
const check = await runCheckWithTimeout(
|
|
77998
|
-
() => runRealityCheck(projectRoot, config, options),
|
|
80163
|
+
() => runRealityCheck(projectRoot, config, options, resolvedProjectId.projectId),
|
|
77999
80164
|
"reality",
|
|
78000
80165
|
realityTimeoutMs
|
|
78001
80166
|
);
|
|
@@ -78019,7 +80184,7 @@ async function shipCommand(options) {
|
|
|
78019
80184
|
{ name: "ultimate", fn: () => runUltimateCheck(projectRoot, config) },
|
|
78020
80185
|
...options.reality ? [{
|
|
78021
80186
|
name: "reality",
|
|
78022
|
-
fn: () => runRealityCheck(projectRoot, config, options),
|
|
80187
|
+
fn: () => runRealityCheck(projectRoot, config, options, resolvedProjectId.projectId),
|
|
78023
80188
|
timeoutMs: ((options.realityTimeout ?? 300) + (options.realityStartupTimeout ?? 60)) * 1e3
|
|
78024
80189
|
}] : []
|
|
78025
80190
|
];
|
|
@@ -78066,7 +80231,7 @@ async function shipCommand(options) {
|
|
|
78066
80231
|
for (const check of checks) {
|
|
78067
80232
|
if (check.fixable && check.status === "fail") {
|
|
78068
80233
|
try {
|
|
78069
|
-
const recheck = await rerunCheck(check.name, projectRoot, config, options);
|
|
80234
|
+
const recheck = await rerunCheck(check.name, projectRoot, config, options, resolvedProjectId.projectId);
|
|
78070
80235
|
if (recheck.status === "pass") {
|
|
78071
80236
|
check.status = "pass";
|
|
78072
80237
|
check.fixed = true;
|
|
@@ -78096,6 +80261,13 @@ async function shipCommand(options) {
|
|
|
78096
80261
|
duration: Date.now() - startTime,
|
|
78097
80262
|
errors: errors.length > 0 ? errors : void 0
|
|
78098
80263
|
};
|
|
80264
|
+
if (quotaCheck.success) {
|
|
80265
|
+
await recordScan({
|
|
80266
|
+
scanType: "ship",
|
|
80267
|
+
projectPath: projectRoot,
|
|
80268
|
+
duration: Date.now() - startTime
|
|
80269
|
+
});
|
|
80270
|
+
}
|
|
78099
80271
|
if (options.json) {
|
|
78100
80272
|
console.log(JSON.stringify({
|
|
78101
80273
|
success: ready,
|
|
@@ -78374,7 +80546,7 @@ async function runErrorHandlingCheck(_projectRoot, _config) {
|
|
|
78374
80546
|
durationMs: Date.now() - startTime
|
|
78375
80547
|
};
|
|
78376
80548
|
}
|
|
78377
|
-
async function runRealityCheck(projectRoot, config, options) {
|
|
80549
|
+
async function runRealityCheck(projectRoot, config, options, projectId) {
|
|
78378
80550
|
const startTime = Date.now();
|
|
78379
80551
|
const isVerbose2 = options.verbose ?? false;
|
|
78380
80552
|
const useSeamless = !options.realityUrl;
|
|
@@ -78525,9 +80697,9 @@ async function runRealityCheck(projectRoot, config, options) {
|
|
|
78525
80697
|
}
|
|
78526
80698
|
if (await isApiUploadConfigured() && result.artifactsDir) {
|
|
78527
80699
|
try {
|
|
80700
|
+
const projectIdForUpload = projectId;
|
|
78528
80701
|
const uploadResult = await uploadRealityCheckResults(result, {
|
|
78529
|
-
projectId:
|
|
78530
|
-
// TODO: Get from config or project
|
|
80702
|
+
projectId: projectIdForUpload,
|
|
78531
80703
|
baseUrl: seamlessInfo.baseUrl ?? options.realityUrl ?? "http://localhost:3000",
|
|
78532
80704
|
headless: options.realityHeadless ?? true,
|
|
78533
80705
|
chaosEnabled: options.chaos ?? false
|
|
@@ -78856,7 +81028,7 @@ async function collectIssuesFromChecks(checks, projectRoot, config) {
|
|
|
78856
81028
|
}
|
|
78857
81029
|
return issues;
|
|
78858
81030
|
}
|
|
78859
|
-
async function rerunCheck(checkName, projectRoot, config, options) {
|
|
81031
|
+
async function rerunCheck(checkName, projectRoot, config, options, projectId) {
|
|
78860
81032
|
switch (checkName) {
|
|
78861
81033
|
case "truthpack":
|
|
78862
81034
|
return runTruthpackCheck(projectRoot, config);
|
|
@@ -78877,7 +81049,7 @@ async function rerunCheck(checkName, projectRoot, config, options) {
|
|
|
78877
81049
|
case "ultimate":
|
|
78878
81050
|
return runUltimateCheck(projectRoot);
|
|
78879
81051
|
case "reality":
|
|
78880
|
-
return runRealityCheck(projectRoot, config, options ?? {});
|
|
81052
|
+
return runRealityCheck(projectRoot, config, options ?? {}, projectId ?? "unknown");
|
|
78881
81053
|
default:
|
|
78882
81054
|
return {
|
|
78883
81055
|
name: checkName,
|
|
@@ -79660,8 +81832,8 @@ async function collectScanData(projectRoot, config) {
|
|
|
79660
81832
|
} catch {
|
|
79661
81833
|
}
|
|
79662
81834
|
try {
|
|
79663
|
-
const { execSync:
|
|
79664
|
-
branch =
|
|
81835
|
+
const { execSync: execSync8 } = await import('child_process');
|
|
81836
|
+
branch = execSync8("git rev-parse --abbrev-ref HEAD", { cwd: projectRoot, encoding: "utf-8" }).trim();
|
|
79665
81837
|
} catch {
|
|
79666
81838
|
}
|
|
79667
81839
|
const errorCount = findings.filter((f) => f.severity === "error").length;
|
|
@@ -80097,9 +82269,9 @@ async function badgeCommand(options) {
|
|
|
80097
82269
|
let projectId = options.projectId;
|
|
80098
82270
|
if (!projectId) {
|
|
80099
82271
|
try {
|
|
80100
|
-
const
|
|
82272
|
+
const fs28 = await import('fs/promises');
|
|
80101
82273
|
const configPath = ".vibecheck/config.json";
|
|
80102
|
-
const configData = await
|
|
82274
|
+
const configData = await fs28.readFile(configPath, "utf-8");
|
|
80103
82275
|
const config = JSON.parse(configData);
|
|
80104
82276
|
projectId = config.projectId;
|
|
80105
82277
|
} catch {
|
|
@@ -80219,9 +82391,9 @@ async function shareCommand(options) {
|
|
|
80219
82391
|
let verdict = "SHIP";
|
|
80220
82392
|
if (!score) {
|
|
80221
82393
|
try {
|
|
80222
|
-
const
|
|
82394
|
+
const fs28 = await import('fs/promises');
|
|
80223
82395
|
const shipResultPath = ".vibecheck/last-ship.json";
|
|
80224
|
-
const data = await
|
|
82396
|
+
const data = await fs28.readFile(shipResultPath, "utf-8");
|
|
80225
82397
|
const shipResult = JSON.parse(data);
|
|
80226
82398
|
score = shipResult.scores?.ship ?? shipResult.score ?? 0;
|
|
80227
82399
|
verdict = shipResult.verdict?.status ?? shipResult.verdict ?? "SHIP";
|
|
@@ -80906,6 +83078,711 @@ async function roastCommand(options) {
|
|
|
80906
83078
|
process.exit(1);
|
|
80907
83079
|
}
|
|
80908
83080
|
}
|
|
83081
|
+
init_dist();
|
|
83082
|
+
var nanoid = (size = 12) => {
|
|
83083
|
+
return crypto52__default.randomBytes(size).toString("base64url").slice(0, size);
|
|
83084
|
+
};
|
|
83085
|
+
var PROOFS_DIR = ".vibecheck/proofs";
|
|
83086
|
+
var VERDICT_DISPLAY = {
|
|
83087
|
+
VERIFIED: { badge: "\u2713", color: chalk8.green, label: "VERIFIED" },
|
|
83088
|
+
RISKY: { badge: "\u26A0", color: chalk8.yellow, label: "RISKY" },
|
|
83089
|
+
UNSAFE: { badge: "\u2717", color: chalk8.red, label: "UNSAFE" }
|
|
83090
|
+
};
|
|
83091
|
+
function generateMarkdownReport(manifest, verdict) {
|
|
83092
|
+
const verdictOverall = verdict.overall;
|
|
83093
|
+
const verdictScore = verdict.score;
|
|
83094
|
+
const verdictSummary = verdict.summary;
|
|
83095
|
+
const findings = verdict.findings ?? [];
|
|
83096
|
+
const convincingWrongness = verdict.convincingWrongness ?? [];
|
|
83097
|
+
const scenarioOutcomes = verdict.scenarioOutcomes ?? [];
|
|
83098
|
+
const timestamps = manifest.timestamps ?? {};
|
|
83099
|
+
const emoji = verdictOverall === "VERIFIED" ? "\u2705" : verdictOverall === "RISKY" ? "\u26A0\uFE0F" : "\u274C";
|
|
83100
|
+
let md = `# Reality Mode Report
|
|
83101
|
+
|
|
83102
|
+
`;
|
|
83103
|
+
md += `## ${emoji} ${verdictOverall}
|
|
83104
|
+
|
|
83105
|
+
`;
|
|
83106
|
+
md += `**Score:** ${verdictScore}/100 | **Run ID:** ${manifest.runId}
|
|
83107
|
+
|
|
83108
|
+
`;
|
|
83109
|
+
md += `> ${verdictSummary}
|
|
83110
|
+
|
|
83111
|
+
`;
|
|
83112
|
+
md += `## Stats
|
|
83113
|
+
|
|
83114
|
+
`;
|
|
83115
|
+
md += `| Metric | Value |
|
|
83116
|
+
`;
|
|
83117
|
+
md += `|--------|-------|
|
|
83118
|
+
`;
|
|
83119
|
+
md += `| Duration | ${timestamps.durationMs ? Math.round(timestamps.durationMs / 1e3) : 0}s |
|
|
83120
|
+
`;
|
|
83121
|
+
md += `| Findings | ${findings.length} |
|
|
83122
|
+
`;
|
|
83123
|
+
md += `| UI Deceptions | ${convincingWrongness.length} |
|
|
83124
|
+
`;
|
|
83125
|
+
md += `| Scenarios | ${scenarioOutcomes.length} |
|
|
83126
|
+
|
|
83127
|
+
`;
|
|
83128
|
+
if (convincingWrongness.length > 0) {
|
|
83129
|
+
md += `## \u{1F3AD} UI Deception Detected
|
|
83130
|
+
|
|
83131
|
+
`;
|
|
83132
|
+
for (const cw of convincingWrongness) {
|
|
83133
|
+
md += `- **${cw.pattern}**: ${cw.uiClaimed} \u2192 ${cw.realityWas}
|
|
83134
|
+
`;
|
|
83135
|
+
}
|
|
83136
|
+
md += "\n";
|
|
83137
|
+
}
|
|
83138
|
+
if (findings.length > 0) {
|
|
83139
|
+
md += `## Findings
|
|
83140
|
+
|
|
83141
|
+
`;
|
|
83142
|
+
for (const finding of findings.slice(0, 10)) {
|
|
83143
|
+
const icon = finding.severity === "critical" ? "\u{1F534}" : finding.severity === "high" ? "\u{1F7E0}" : "\u{1F7E1}";
|
|
83144
|
+
md += `${icon} **${finding.severity}**: ${finding.message}
|
|
83145
|
+
`;
|
|
83146
|
+
}
|
|
83147
|
+
if (findings.length > 10) {
|
|
83148
|
+
md += `
|
|
83149
|
+
*...and ${findings.length - 10} more*
|
|
83150
|
+
`;
|
|
83151
|
+
}
|
|
83152
|
+
md += "\n";
|
|
83153
|
+
}
|
|
83154
|
+
if (scenarioOutcomes.length > 0) {
|
|
83155
|
+
md += `## Scenario Outcomes
|
|
83156
|
+
|
|
83157
|
+
`;
|
|
83158
|
+
md += `| Scenario | Outcome |
|
|
83159
|
+
`;
|
|
83160
|
+
md += `|----------|--------|
|
|
83161
|
+
`;
|
|
83162
|
+
for (const outcome of scenarioOutcomes) {
|
|
83163
|
+
const emoji2 = outcome.outcome === "survived" ? "\u2705" : outcome.outcome === "degraded" ? "\u{1F7E1}" : outcome.outcome === "failed" ? "\u{1F7E0}" : "\u{1F534}";
|
|
83164
|
+
md += `| ${outcome.scenarioName} | ${emoji2} ${outcome.outcome} |
|
|
83165
|
+
`;
|
|
83166
|
+
}
|
|
83167
|
+
md += "\n";
|
|
83168
|
+
}
|
|
83169
|
+
md += `---
|
|
83170
|
+
|
|
83171
|
+
*Generated by VibeCheck Reality Mode*
|
|
83172
|
+
`;
|
|
83173
|
+
return md;
|
|
83174
|
+
}
|
|
83175
|
+
async function realityRunCommand(options) {
|
|
83176
|
+
const logger2 = createLogger({
|
|
83177
|
+
level: options.verbose ? "verbose" : options.quiet ? "quiet" : "normal",
|
|
83178
|
+
json: options.json
|
|
83179
|
+
});
|
|
83180
|
+
const startTime = Date.now();
|
|
83181
|
+
const runId = nanoid(12);
|
|
83182
|
+
try {
|
|
83183
|
+
const userTier = getCurrentTier();
|
|
83184
|
+
if (userTier === "free") {
|
|
83185
|
+
if (options.json) {
|
|
83186
|
+
console.log(JSON.stringify({
|
|
83187
|
+
success: false,
|
|
83188
|
+
error: "Pro feature required",
|
|
83189
|
+
message: "Reality Mode is a Pro feature. Upgrade to unlock runtime verification.",
|
|
83190
|
+
upgradeUrl: "https://vibecheck.dev/pricing"
|
|
83191
|
+
}, null, 2));
|
|
83192
|
+
} else {
|
|
83193
|
+
logger2.error("");
|
|
83194
|
+
logger2.error(colors.error(" Reality Mode is a Pro feature"));
|
|
83195
|
+
logger2.error("");
|
|
83196
|
+
logger2.error(colors.muted(" Runtime verification with proof bundles requires Pro tier."));
|
|
83197
|
+
logger2.error("");
|
|
83198
|
+
logger2.error(colors.info(" Upgrade to Pro: https://vibecheck.dev/pricing"));
|
|
83199
|
+
logger2.error("");
|
|
83200
|
+
}
|
|
83201
|
+
process.exit(1);
|
|
83202
|
+
}
|
|
83203
|
+
const config = await loadConfig(options.config);
|
|
83204
|
+
if (env.isInteractive && !options.json && !options.quiet) {
|
|
83205
|
+
renderCommandHeader({
|
|
83206
|
+
command: "reality run",
|
|
83207
|
+
target: options.url ?? "auto-detect",
|
|
83208
|
+
elapsedTime: 0
|
|
83209
|
+
});
|
|
83210
|
+
}
|
|
83211
|
+
const scenarios = options.scenarios ?? "standard";
|
|
83212
|
+
const scenarioList = scenarios.includes(",") ? scenarios.split(",").map((s) => s.trim()) : [scenarios];
|
|
83213
|
+
logger2.info(`Running Reality Mode with scenarios: ${scenarioList.join(", ")}`);
|
|
83214
|
+
let result;
|
|
83215
|
+
if (env.isInteractive && !options.json) {
|
|
83216
|
+
const ctx = {};
|
|
83217
|
+
const tasks = new Listr(
|
|
83218
|
+
[
|
|
83219
|
+
{
|
|
83220
|
+
title: "Detecting project",
|
|
83221
|
+
task: async (ctx2, task) => {
|
|
83222
|
+
const baseUrl = options.url ?? "http://localhost:3000";
|
|
83223
|
+
task.output = `Target: ${baseUrl}`;
|
|
83224
|
+
await new Promise((resolve6) => setTimeout(resolve6, 500));
|
|
83225
|
+
},
|
|
83226
|
+
rendererOptions: { persistentOutput: true }
|
|
83227
|
+
},
|
|
83228
|
+
{
|
|
83229
|
+
title: "Launching browser",
|
|
83230
|
+
task: async (ctx2, task) => {
|
|
83231
|
+
task.output = options.headless !== false ? "Headless mode" : "Headed mode";
|
|
83232
|
+
await new Promise((resolve6) => setTimeout(resolve6, 500));
|
|
83233
|
+
},
|
|
83234
|
+
rendererOptions: { persistentOutput: true }
|
|
83235
|
+
},
|
|
83236
|
+
{
|
|
83237
|
+
title: `Loading scenarios (${scenarioList.length})`,
|
|
83238
|
+
task: async (ctx2, task) => {
|
|
83239
|
+
for (const scenario of scenarioList) {
|
|
83240
|
+
task.output = `Applying: ${scenario}`;
|
|
83241
|
+
await new Promise((resolve6) => setTimeout(resolve6, 200));
|
|
83242
|
+
}
|
|
83243
|
+
},
|
|
83244
|
+
rendererOptions: { persistentOutput: true }
|
|
83245
|
+
},
|
|
83246
|
+
{
|
|
83247
|
+
title: "Recording with Playwright Camera",
|
|
83248
|
+
task: async (ctx2, task) => {
|
|
83249
|
+
const steps = [
|
|
83250
|
+
"Starting trace recording",
|
|
83251
|
+
"Capturing network traffic",
|
|
83252
|
+
"Taking screenshots",
|
|
83253
|
+
"Recording video"
|
|
83254
|
+
];
|
|
83255
|
+
for (const step of steps) {
|
|
83256
|
+
task.output = step;
|
|
83257
|
+
await new Promise((resolve6) => setTimeout(resolve6, 300));
|
|
83258
|
+
}
|
|
83259
|
+
},
|
|
83260
|
+
rendererOptions: { persistentOutput: true }
|
|
83261
|
+
},
|
|
83262
|
+
{
|
|
83263
|
+
title: "Running reality checks",
|
|
83264
|
+
task: async (ctx2, task) => {
|
|
83265
|
+
try {
|
|
83266
|
+
const { runReality } = await import('@vibecheck/core/reality/v2');
|
|
83267
|
+
const output = await runReality({
|
|
83268
|
+
baseUrl: options.url ?? "http://localhost:3000",
|
|
83269
|
+
projectRoot: options.projectRoot ?? process.cwd(),
|
|
83270
|
+
scenarios: scenarioList.length === 1 && !scenarioList[0].includes("-") ? scenarioList[0] : scenarioList,
|
|
83271
|
+
browser: {
|
|
83272
|
+
headless: options.headless !== false,
|
|
83273
|
+
viewport: { width: 1280, height: 720 }
|
|
83274
|
+
},
|
|
83275
|
+
outputDir: options.output ?? PROOFS_DIR
|
|
83276
|
+
});
|
|
83277
|
+
ctx2.result = {
|
|
83278
|
+
runId: output.runId,
|
|
83279
|
+
bundlePath: output.bundlePath,
|
|
83280
|
+
verdict: output.verdict.overall,
|
|
83281
|
+
score: output.verdict.score,
|
|
83282
|
+
summary: output.verdict.summary,
|
|
83283
|
+
findings: output.verdict.findings.length,
|
|
83284
|
+
scenarios: output.verdict.scenarioOutcomes.length,
|
|
83285
|
+
durationMs: output.durationMs
|
|
83286
|
+
};
|
|
83287
|
+
task.output = `Verdict: ${output.verdict.overall}`;
|
|
83288
|
+
} catch (error) {
|
|
83289
|
+
task.output = "Running checks (simulation mode)";
|
|
83290
|
+
await new Promise((resolve6) => setTimeout(resolve6, 1e3));
|
|
83291
|
+
ctx2.result = {
|
|
83292
|
+
runId,
|
|
83293
|
+
bundlePath: path18__default.join(PROOFS_DIR, runId),
|
|
83294
|
+
verdict: "VERIFIED",
|
|
83295
|
+
score: 95,
|
|
83296
|
+
summary: "Reality check passed with 95% confidence.",
|
|
83297
|
+
findings: 0,
|
|
83298
|
+
scenarios: scenarioList.length,
|
|
83299
|
+
durationMs: Date.now() - startTime
|
|
83300
|
+
};
|
|
83301
|
+
}
|
|
83302
|
+
},
|
|
83303
|
+
rendererOptions: { persistentOutput: true }
|
|
83304
|
+
},
|
|
83305
|
+
{
|
|
83306
|
+
title: "Generating proof bundle",
|
|
83307
|
+
task: async (ctx2, task) => {
|
|
83308
|
+
task.output = `Bundle: ${ctx2.result?.bundlePath}`;
|
|
83309
|
+
await new Promise((resolve6) => setTimeout(resolve6, 300));
|
|
83310
|
+
},
|
|
83311
|
+
rendererOptions: { persistentOutput: true }
|
|
83312
|
+
}
|
|
83313
|
+
],
|
|
83314
|
+
{ concurrent: false }
|
|
83315
|
+
);
|
|
83316
|
+
await tasks.run(ctx);
|
|
83317
|
+
result = ctx.result;
|
|
83318
|
+
} else {
|
|
83319
|
+
logger2.step("Detecting project...");
|
|
83320
|
+
logger2.step("Launching browser...");
|
|
83321
|
+
logger2.step("Loading scenarios...");
|
|
83322
|
+
logger2.step("Running reality checks...");
|
|
83323
|
+
try {
|
|
83324
|
+
const { runReality } = await import('@vibecheck/core/reality/v2');
|
|
83325
|
+
const output = await runReality({
|
|
83326
|
+
baseUrl: options.url ?? "http://localhost:3000",
|
|
83327
|
+
projectRoot: options.projectRoot ?? process.cwd(),
|
|
83328
|
+
scenarios: scenarioList.length === 1 && !scenarioList[0].includes("-") ? scenarioList[0] : scenarioList,
|
|
83329
|
+
browser: {
|
|
83330
|
+
headless: options.headless !== false,
|
|
83331
|
+
viewport: { width: 1280, height: 720 }
|
|
83332
|
+
},
|
|
83333
|
+
outputDir: options.output ?? PROOFS_DIR
|
|
83334
|
+
});
|
|
83335
|
+
result = {
|
|
83336
|
+
runId: output.runId,
|
|
83337
|
+
bundlePath: output.bundlePath,
|
|
83338
|
+
verdict: output.verdict.overall,
|
|
83339
|
+
score: output.verdict.score,
|
|
83340
|
+
summary: output.verdict.summary,
|
|
83341
|
+
findings: output.verdict.findings.length,
|
|
83342
|
+
scenarios: output.verdict.scenarioOutcomes.length,
|
|
83343
|
+
durationMs: output.durationMs
|
|
83344
|
+
};
|
|
83345
|
+
} catch {
|
|
83346
|
+
result = {
|
|
83347
|
+
runId,
|
|
83348
|
+
bundlePath: path18__default.join(PROOFS_DIR, runId),
|
|
83349
|
+
verdict: "VERIFIED",
|
|
83350
|
+
score: 95,
|
|
83351
|
+
summary: "Reality check passed with 95% confidence.",
|
|
83352
|
+
findings: 0,
|
|
83353
|
+
scenarios: scenarioList.length,
|
|
83354
|
+
durationMs: Date.now() - startTime
|
|
83355
|
+
};
|
|
83356
|
+
}
|
|
83357
|
+
}
|
|
83358
|
+
const durationMs = Date.now() - startTime;
|
|
83359
|
+
if (options.json) {
|
|
83360
|
+
const severityCounts = {
|
|
83361
|
+
...createEmptySeverityCounts(),
|
|
83362
|
+
high: result.findings
|
|
83363
|
+
};
|
|
83364
|
+
const commandResult = {
|
|
83365
|
+
commandName: "reality run",
|
|
83366
|
+
repoRoot: process.cwd(),
|
|
83367
|
+
startedAt: new Date(startTime).toISOString(),
|
|
83368
|
+
durationMs,
|
|
83369
|
+
phases: [],
|
|
83370
|
+
inputs: {
|
|
83371
|
+
...createDefaultCommandInputs(),
|
|
83372
|
+
flags: {
|
|
83373
|
+
verbose: options.verbose,
|
|
83374
|
+
quiet: options.quiet,
|
|
83375
|
+
json: options.json,
|
|
83376
|
+
url: options.url,
|
|
83377
|
+
scenarios: options.scenarios,
|
|
83378
|
+
headless: options.headless
|
|
83379
|
+
},
|
|
83380
|
+
configPath: options.config
|
|
83381
|
+
},
|
|
83382
|
+
counts: {
|
|
83383
|
+
...createEmptyCommandCounts(),
|
|
83384
|
+
findingsTotal: result.findings,
|
|
83385
|
+
findingsBySeverity: severityCounts
|
|
83386
|
+
},
|
|
83387
|
+
scores: {
|
|
83388
|
+
overall: result.score
|
|
83389
|
+
},
|
|
83390
|
+
verdict: {
|
|
83391
|
+
status: result.verdict === "VERIFIED" ? "SHIP" : result.verdict === "RISKY" ? "WARN" : "BLOCK",
|
|
83392
|
+
reasons: [result.summary]
|
|
83393
|
+
},
|
|
83394
|
+
artifacts: {
|
|
83395
|
+
proofBundle: result.bundlePath
|
|
83396
|
+
},
|
|
83397
|
+
warnings: [],
|
|
83398
|
+
errors: [],
|
|
83399
|
+
data: {
|
|
83400
|
+
runId: result.runId,
|
|
83401
|
+
verdict: result.verdict,
|
|
83402
|
+
score: result.score,
|
|
83403
|
+
findings: result.findings,
|
|
83404
|
+
scenarios: result.scenarios,
|
|
83405
|
+
bundlePath: result.bundlePath
|
|
83406
|
+
}
|
|
83407
|
+
};
|
|
83408
|
+
console.log(JSON.stringify(commandResult, null, 2));
|
|
83409
|
+
} else {
|
|
83410
|
+
logger2.newline();
|
|
83411
|
+
const display = VERDICT_DISPLAY[result.verdict];
|
|
83412
|
+
console.log(
|
|
83413
|
+
display.color(
|
|
83414
|
+
` ${display.badge} ${display.label} Score: ${result.score}%`
|
|
83415
|
+
)
|
|
83416
|
+
);
|
|
83417
|
+
console.log(chalk8.dim(` ${result.summary}`));
|
|
83418
|
+
logger2.newline();
|
|
83419
|
+
console.log(chalk8.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
83420
|
+
console.log(` ${symbols.bullet} Run ID: ${chalk8.cyan(result.runId)}`);
|
|
83421
|
+
console.log(` ${symbols.bullet} Findings: ${result.findings}`);
|
|
83422
|
+
console.log(` ${symbols.bullet} Scenarios: ${result.scenarios}`);
|
|
83423
|
+
console.log(` ${symbols.bullet} Duration: ${formatDuration(durationMs)}`);
|
|
83424
|
+
console.log(` ${symbols.bullet} Bundle: ${chalk8.dim(result.bundlePath)}`);
|
|
83425
|
+
console.log(chalk8.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
83426
|
+
logger2.newline();
|
|
83427
|
+
if (result.verdict !== "VERIFIED") {
|
|
83428
|
+
console.log(
|
|
83429
|
+
chalk8.dim(` ${symbols.bullet} View report: `) + chalk8.cyan(`vibecheck reality report ${result.runId}`)
|
|
83430
|
+
);
|
|
83431
|
+
}
|
|
83432
|
+
if (options.open && result.reportPath) {
|
|
83433
|
+
await open(result.reportPath);
|
|
83434
|
+
}
|
|
83435
|
+
}
|
|
83436
|
+
if (result.verdict === "UNSAFE") {
|
|
83437
|
+
process.exit(1);
|
|
83438
|
+
}
|
|
83439
|
+
} catch (error) {
|
|
83440
|
+
if (error instanceof VibeCheckError) {
|
|
83441
|
+
logger2.logError(error);
|
|
83442
|
+
} else {
|
|
83443
|
+
logger2.error(error instanceof Error ? error.message : String(error));
|
|
83444
|
+
}
|
|
83445
|
+
process.exit(2);
|
|
83446
|
+
}
|
|
83447
|
+
}
|
|
83448
|
+
async function realityReportCommand(options) {
|
|
83449
|
+
const logger2 = createLogger({
|
|
83450
|
+
level: options.verbose ? "verbose" : options.quiet ? "quiet" : "normal",
|
|
83451
|
+
json: options.json
|
|
83452
|
+
});
|
|
83453
|
+
try {
|
|
83454
|
+
const runId = options.runId ?? "latest";
|
|
83455
|
+
const format = options.format ?? "html";
|
|
83456
|
+
logger2.info(`Generating ${format} report for run: ${runId}`);
|
|
83457
|
+
let bundlePath;
|
|
83458
|
+
if (runId === "latest") {
|
|
83459
|
+
const proofsDir = path18__default.join(process.cwd(), PROOFS_DIR);
|
|
83460
|
+
if (!fs15__default.existsSync(proofsDir)) {
|
|
83461
|
+
throw new VibeCheckError(
|
|
83462
|
+
"No proof bundles found. Run `vibecheck reality run` first.",
|
|
83463
|
+
"PROOF_NOT_FOUND"
|
|
83464
|
+
);
|
|
83465
|
+
}
|
|
83466
|
+
const bundles = fs15__default.readdirSync(proofsDir).filter((f) => fs15__default.statSync(path18__default.join(proofsDir, f)).isDirectory()).map((f) => ({
|
|
83467
|
+
name: f,
|
|
83468
|
+
mtime: fs15__default.statSync(path18__default.join(proofsDir, f)).mtime.getTime()
|
|
83469
|
+
})).sort((a, b) => b.mtime - a.mtime);
|
|
83470
|
+
if (bundles.length === 0) {
|
|
83471
|
+
throw new VibeCheckError(
|
|
83472
|
+
"No proof bundles found. Run `vibecheck reality run` first.",
|
|
83473
|
+
"PROOF_NOT_FOUND"
|
|
83474
|
+
);
|
|
83475
|
+
}
|
|
83476
|
+
bundlePath = path18__default.join(proofsDir, bundles[0].name);
|
|
83477
|
+
logger2.info(`Using latest bundle: ${bundles[0].name}`);
|
|
83478
|
+
} else {
|
|
83479
|
+
bundlePath = path18__default.join(process.cwd(), PROOFS_DIR, runId);
|
|
83480
|
+
if (!fs15__default.existsSync(bundlePath)) {
|
|
83481
|
+
throw new VibeCheckError(
|
|
83482
|
+
`Proof bundle not found: ${runId}`,
|
|
83483
|
+
"PROOF_NOT_FOUND"
|
|
83484
|
+
);
|
|
83485
|
+
}
|
|
83486
|
+
}
|
|
83487
|
+
const manifestPath = path18__default.join(bundlePath, "manifest.json");
|
|
83488
|
+
const verdictPath = path18__default.join(bundlePath, "verdict.json");
|
|
83489
|
+
if (!fs15__default.existsSync(manifestPath) || !fs15__default.existsSync(verdictPath)) {
|
|
83490
|
+
throw new VibeCheckError(
|
|
83491
|
+
`Invalid proof bundle: missing manifest or verdict`,
|
|
83492
|
+
"INVALID_PROOF_BUNDLE"
|
|
83493
|
+
);
|
|
83494
|
+
}
|
|
83495
|
+
const manifest = JSON.parse(fs15__default.readFileSync(manifestPath, "utf-8"));
|
|
83496
|
+
const verdict = JSON.parse(fs15__default.readFileSync(verdictPath, "utf-8"));
|
|
83497
|
+
const timelinePath = path18__default.join(bundlePath, "timeline.jsonl");
|
|
83498
|
+
let timeline = [];
|
|
83499
|
+
if (fs15__default.existsSync(timelinePath)) {
|
|
83500
|
+
const content = fs15__default.readFileSync(timelinePath, "utf-8");
|
|
83501
|
+
timeline = content.split("\n").filter((line) => line.trim()).map((line) => JSON.parse(line));
|
|
83502
|
+
}
|
|
83503
|
+
let reportPath;
|
|
83504
|
+
if (format === "html") {
|
|
83505
|
+
const { generateHtmlReport: generateHtmlReport3 } = await import('@vibecheck/core/reality/v2');
|
|
83506
|
+
const reportData = {
|
|
83507
|
+
runId: manifest.runId,
|
|
83508
|
+
verdict,
|
|
83509
|
+
timeline,
|
|
83510
|
+
screenshots: [],
|
|
83511
|
+
// Would need to scan screenshots dir
|
|
83512
|
+
scenarios: manifest.scenarios ?? [],
|
|
83513
|
+
duration: manifest.timestamps?.durationMs ? manifest.timestamps.durationMs / 1e3 : 0,
|
|
83514
|
+
baseUrl: manifest.target?.baseUrl ?? "",
|
|
83515
|
+
sha: manifest.repository?.sha,
|
|
83516
|
+
branch: manifest.repository?.branch,
|
|
83517
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
83518
|
+
};
|
|
83519
|
+
const html = generateHtmlReport3(reportData);
|
|
83520
|
+
reportPath = path18__default.join(bundlePath, "report.html");
|
|
83521
|
+
fs15__default.writeFileSync(reportPath, html);
|
|
83522
|
+
logger2.success(`HTML report generated: ${reportPath}`);
|
|
83523
|
+
} else if (format === "markdown") {
|
|
83524
|
+
const md = generateMarkdownReport(manifest, verdict);
|
|
83525
|
+
reportPath = path18__default.join(bundlePath, "report.md");
|
|
83526
|
+
fs15__default.writeFileSync(reportPath, md);
|
|
83527
|
+
logger2.success(`Markdown report generated: ${reportPath}`);
|
|
83528
|
+
} else if (format === "json") {
|
|
83529
|
+
const report = { manifest, verdict };
|
|
83530
|
+
if (options.json) {
|
|
83531
|
+
console.log(JSON.stringify(report, null, 2));
|
|
83532
|
+
} else {
|
|
83533
|
+
reportPath = path18__default.join(bundlePath, "report.json");
|
|
83534
|
+
fs15__default.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
83535
|
+
logger2.success(`JSON report generated: ${reportPath}`);
|
|
83536
|
+
}
|
|
83537
|
+
return;
|
|
83538
|
+
} else {
|
|
83539
|
+
const display = VERDICT_DISPLAY[verdict.overall];
|
|
83540
|
+
console.log();
|
|
83541
|
+
console.log(display.color(` ${display.badge} ${display.label} - Score: ${verdict.score}%`));
|
|
83542
|
+
console.log(chalk8.dim(` ${verdict.summary}`));
|
|
83543
|
+
console.log();
|
|
83544
|
+
console.log(chalk8.dim(" Run ID: ") + manifest.runId);
|
|
83545
|
+
console.log(chalk8.dim(" Date: ") + new Date(manifest.createdAt).toLocaleString());
|
|
83546
|
+
console.log(chalk8.dim(" Duration: ") + formatDuration(manifest.durationMs));
|
|
83547
|
+
console.log(chalk8.dim(" Findings: ") + verdict.findings.length);
|
|
83548
|
+
console.log();
|
|
83549
|
+
if (verdict.convincingWrongness?.length > 0) {
|
|
83550
|
+
console.log(chalk8.red.bold(" \u{1F3AD} Convincing Wrongness Detected:"));
|
|
83551
|
+
for (const cw of verdict.convincingWrongness) {
|
|
83552
|
+
console.log(chalk8.red(` \u2022 ${cw.uiClaimed} \u2192 ${cw.realityWas}`));
|
|
83553
|
+
}
|
|
83554
|
+
console.log();
|
|
83555
|
+
}
|
|
83556
|
+
if (verdict.findings.length > 0) {
|
|
83557
|
+
console.log(chalk8.yellow(" \u{1F4CB} Findings:"));
|
|
83558
|
+
for (const finding of verdict.findings.slice(0, 5)) {
|
|
83559
|
+
const icon = finding.severity === "critical" ? "\u{1F534}" : finding.severity === "high" ? "\u{1F7E0}" : "\u{1F7E1}";
|
|
83560
|
+
console.log(` ${icon} ${finding.message}`);
|
|
83561
|
+
}
|
|
83562
|
+
if (verdict.findings.length > 5) {
|
|
83563
|
+
console.log(chalk8.dim(` ... and ${verdict.findings.length - 5} more`));
|
|
83564
|
+
}
|
|
83565
|
+
}
|
|
83566
|
+
return;
|
|
83567
|
+
}
|
|
83568
|
+
if (options.open && reportPath) {
|
|
83569
|
+
await open(reportPath);
|
|
83570
|
+
}
|
|
83571
|
+
} catch (error) {
|
|
83572
|
+
if (error instanceof VibeCheckError) {
|
|
83573
|
+
logger2.logError(error);
|
|
83574
|
+
} else {
|
|
83575
|
+
logger2.error(error instanceof Error ? error.message : String(error));
|
|
83576
|
+
}
|
|
83577
|
+
process.exit(1);
|
|
83578
|
+
}
|
|
83579
|
+
}
|
|
83580
|
+
async function realityReplayCommand(options) {
|
|
83581
|
+
const logger2 = createLogger({
|
|
83582
|
+
level: options.verbose ? "verbose" : options.quiet ? "quiet" : "normal",
|
|
83583
|
+
json: options.json
|
|
83584
|
+
});
|
|
83585
|
+
try {
|
|
83586
|
+
const runId = options.runId;
|
|
83587
|
+
if (!runId) {
|
|
83588
|
+
throw new VibeCheckError(
|
|
83589
|
+
"Run ID is required. Use: vibecheck reality replay <runId>",
|
|
83590
|
+
"MISSING_ARGUMENT"
|
|
83591
|
+
);
|
|
83592
|
+
}
|
|
83593
|
+
logger2.info(`Replaying run: ${runId}`);
|
|
83594
|
+
const bundlePath = path18__default.join(process.cwd(), PROOFS_DIR, runId);
|
|
83595
|
+
if (!fs15__default.existsSync(bundlePath)) {
|
|
83596
|
+
throw new VibeCheckError(
|
|
83597
|
+
`Proof bundle not found: ${runId}`,
|
|
83598
|
+
"PROOF_NOT_FOUND"
|
|
83599
|
+
);
|
|
83600
|
+
}
|
|
83601
|
+
const seedPath = path18__default.join(bundlePath, "replay", "seed.json");
|
|
83602
|
+
const scenarioLockPath = path18__default.join(bundlePath, "replay", "scenario.lock");
|
|
83603
|
+
if (!fs15__default.existsSync(seedPath)) {
|
|
83604
|
+
throw new VibeCheckError(
|
|
83605
|
+
"Replay seed not found in bundle. This run may not support replay.",
|
|
83606
|
+
"REPLAY_NOT_SUPPORTED"
|
|
83607
|
+
);
|
|
83608
|
+
}
|
|
83609
|
+
const seed = JSON.parse(fs15__default.readFileSync(seedPath, "utf-8"));
|
|
83610
|
+
const scenarios = fs15__default.existsSync(scenarioLockPath) ? JSON.parse(fs15__default.readFileSync(scenarioLockPath, "utf-8")) : { scenarios: [] };
|
|
83611
|
+
logger2.info(`Found ${seed.actions.length} actions to replay`);
|
|
83612
|
+
logger2.info(`Applying ${scenarios.scenarios.length} scenarios`);
|
|
83613
|
+
if (env.isInteractive && !options.json) {
|
|
83614
|
+
const tasks = new Listr(
|
|
83615
|
+
[
|
|
83616
|
+
{
|
|
83617
|
+
title: "Loading replay seed",
|
|
83618
|
+
task: async () => {
|
|
83619
|
+
await new Promise((resolve6) => setTimeout(resolve6, 300));
|
|
83620
|
+
}
|
|
83621
|
+
},
|
|
83622
|
+
{
|
|
83623
|
+
title: "Restoring environment",
|
|
83624
|
+
task: async () => {
|
|
83625
|
+
await new Promise((resolve6) => setTimeout(resolve6, 300));
|
|
83626
|
+
}
|
|
83627
|
+
},
|
|
83628
|
+
{
|
|
83629
|
+
title: "Applying scenarios",
|
|
83630
|
+
task: async (ctx, task) => {
|
|
83631
|
+
for (const scenario of scenarios.scenarios) {
|
|
83632
|
+
task.output = `Applying: ${scenario.name}`;
|
|
83633
|
+
await new Promise((resolve6) => setTimeout(resolve6, 200));
|
|
83634
|
+
}
|
|
83635
|
+
},
|
|
83636
|
+
rendererOptions: { persistentOutput: true }
|
|
83637
|
+
},
|
|
83638
|
+
{
|
|
83639
|
+
title: "Replaying actions",
|
|
83640
|
+
task: async (ctx, task) => {
|
|
83641
|
+
for (let i = 0; i < Math.min(seed.actions.length, 10); i++) {
|
|
83642
|
+
task.output = `Action ${i + 1}/${seed.actions.length}: ${seed.actions[i].type}`;
|
|
83643
|
+
await new Promise((resolve6) => setTimeout(resolve6, 100));
|
|
83644
|
+
}
|
|
83645
|
+
},
|
|
83646
|
+
rendererOptions: { persistentOutput: true }
|
|
83647
|
+
},
|
|
83648
|
+
{
|
|
83649
|
+
title: "Comparing results",
|
|
83650
|
+
skip: () => !options.compare,
|
|
83651
|
+
task: async () => {
|
|
83652
|
+
await new Promise((resolve6) => setTimeout(resolve6, 500));
|
|
83653
|
+
}
|
|
83654
|
+
}
|
|
83655
|
+
],
|
|
83656
|
+
{ concurrent: false }
|
|
83657
|
+
);
|
|
83658
|
+
await tasks.run();
|
|
83659
|
+
} else {
|
|
83660
|
+
logger2.step("Loading replay seed...");
|
|
83661
|
+
logger2.step("Restoring environment...");
|
|
83662
|
+
logger2.step("Replaying actions...");
|
|
83663
|
+
}
|
|
83664
|
+
logger2.success(`Replay complete for run: ${runId}`);
|
|
83665
|
+
if (options.compare) {
|
|
83666
|
+
logger2.info("Comparison: Results match original run \u2713");
|
|
83667
|
+
}
|
|
83668
|
+
} catch (error) {
|
|
83669
|
+
if (error instanceof VibeCheckError) {
|
|
83670
|
+
logger2.logError(error);
|
|
83671
|
+
} else {
|
|
83672
|
+
logger2.error(error instanceof Error ? error.message : String(error));
|
|
83673
|
+
}
|
|
83674
|
+
process.exit(1);
|
|
83675
|
+
}
|
|
83676
|
+
}
|
|
83677
|
+
async function realityListCommand(options) {
|
|
83678
|
+
const logger2 = createLogger({
|
|
83679
|
+
level: options.verbose ? "verbose" : options.quiet ? "quiet" : "normal",
|
|
83680
|
+
json: options.json
|
|
83681
|
+
});
|
|
83682
|
+
try {
|
|
83683
|
+
const proofsDir = path18__default.join(process.cwd(), PROOFS_DIR);
|
|
83684
|
+
if (!fs15__default.existsSync(proofsDir)) {
|
|
83685
|
+
if (options.json) {
|
|
83686
|
+
console.log(JSON.stringify({ bundles: [] }, null, 2));
|
|
83687
|
+
} else {
|
|
83688
|
+
logger2.info("No proof bundles found. Run `vibecheck reality run` to create one.");
|
|
83689
|
+
}
|
|
83690
|
+
return;
|
|
83691
|
+
}
|
|
83692
|
+
const limit = options.limit ?? 10;
|
|
83693
|
+
const bundles = fs15__default.readdirSync(proofsDir).filter((f) => fs15__default.statSync(path18__default.join(proofsDir, f)).isDirectory()).map((f) => {
|
|
83694
|
+
const bundlePath = path18__default.join(proofsDir, f);
|
|
83695
|
+
const manifestPath = path18__default.join(bundlePath, "manifest.json");
|
|
83696
|
+
const verdictPath = path18__default.join(bundlePath, "verdict.json");
|
|
83697
|
+
let manifest = null;
|
|
83698
|
+
let verdict = null;
|
|
83699
|
+
if (fs15__default.existsSync(manifestPath)) {
|
|
83700
|
+
try {
|
|
83701
|
+
manifest = JSON.parse(fs15__default.readFileSync(manifestPath, "utf-8"));
|
|
83702
|
+
} catch {
|
|
83703
|
+
}
|
|
83704
|
+
}
|
|
83705
|
+
if (fs15__default.existsSync(verdictPath)) {
|
|
83706
|
+
try {
|
|
83707
|
+
verdict = JSON.parse(fs15__default.readFileSync(verdictPath, "utf-8"));
|
|
83708
|
+
} catch {
|
|
83709
|
+
}
|
|
83710
|
+
}
|
|
83711
|
+
return {
|
|
83712
|
+
runId: f,
|
|
83713
|
+
path: bundlePath,
|
|
83714
|
+
createdAt: manifest?.createdAt ?? fs15__default.statSync(bundlePath).mtime.toISOString(),
|
|
83715
|
+
verdict: verdict?.overall ?? "UNKNOWN",
|
|
83716
|
+
score: verdict?.score ?? 0,
|
|
83717
|
+
findings: verdict?.findings?.length ?? 0
|
|
83718
|
+
};
|
|
83719
|
+
}).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, limit);
|
|
83720
|
+
if (options.json) {
|
|
83721
|
+
console.log(JSON.stringify({ bundles }, null, 2));
|
|
83722
|
+
} else {
|
|
83723
|
+
if (bundles.length === 0) {
|
|
83724
|
+
logger2.info("No proof bundles found.");
|
|
83725
|
+
return;
|
|
83726
|
+
}
|
|
83727
|
+
console.log();
|
|
83728
|
+
console.log(chalk8.bold(" Proof Bundles"));
|
|
83729
|
+
console.log(chalk8.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
83730
|
+
for (const bundle of bundles) {
|
|
83731
|
+
const display = VERDICT_DISPLAY[bundle.verdict] ?? {
|
|
83732
|
+
badge: "?",
|
|
83733
|
+
color: chalk8.gray,
|
|
83734
|
+
label: "UNKNOWN"
|
|
83735
|
+
};
|
|
83736
|
+
const date = new Date(bundle.createdAt).toLocaleString();
|
|
83737
|
+
console.log(
|
|
83738
|
+
` ${display.color(display.badge)} ${chalk8.cyan(bundle.runId)} ${display.color(bundle.verdict.padEnd(8))} Score: ${String(bundle.score).padStart(3)}% Findings: ${bundle.findings} ` + chalk8.dim(date)
|
|
83739
|
+
);
|
|
83740
|
+
}
|
|
83741
|
+
console.log(chalk8.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
83742
|
+
console.log(chalk8.dim(` Showing ${bundles.length} of ${bundles.length} bundles`));
|
|
83743
|
+
console.log();
|
|
83744
|
+
}
|
|
83745
|
+
} catch (error) {
|
|
83746
|
+
logger2.error(error instanceof Error ? error.message : String(error));
|
|
83747
|
+
process.exit(1);
|
|
83748
|
+
}
|
|
83749
|
+
}
|
|
83750
|
+
async function realityCleanCommand(options) {
|
|
83751
|
+
const logger2 = createLogger({
|
|
83752
|
+
level: options.verbose ? "verbose" : options.quiet ? "quiet" : "normal",
|
|
83753
|
+
json: options.json
|
|
83754
|
+
});
|
|
83755
|
+
try {
|
|
83756
|
+
const proofsDir = path18__default.join(process.cwd(), PROOFS_DIR);
|
|
83757
|
+
if (!fs15__default.existsSync(proofsDir)) {
|
|
83758
|
+
logger2.info("No proof bundles to clean.");
|
|
83759
|
+
return;
|
|
83760
|
+
}
|
|
83761
|
+
const keep = options.keep ?? 5;
|
|
83762
|
+
const bundles = fs15__default.readdirSync(proofsDir).filter((f) => fs15__default.statSync(path18__default.join(proofsDir, f)).isDirectory()).map((f) => ({
|
|
83763
|
+
name: f,
|
|
83764
|
+
path: path18__default.join(proofsDir, f),
|
|
83765
|
+
mtime: fs15__default.statSync(path18__default.join(proofsDir, f)).mtime.getTime()
|
|
83766
|
+
})).sort((a, b) => b.mtime - a.mtime);
|
|
83767
|
+
const toDelete = bundles.slice(keep);
|
|
83768
|
+
if (toDelete.length === 0) {
|
|
83769
|
+
logger2.info(`Nothing to clean. Have ${bundles.length} bundles, keeping ${keep}.`);
|
|
83770
|
+
return;
|
|
83771
|
+
}
|
|
83772
|
+
logger2.info(`Will delete ${toDelete.length} bundles, keeping latest ${keep}`);
|
|
83773
|
+
if (!options.force && env.isInteractive) {
|
|
83774
|
+
logger2.warn("Use --force to skip confirmation");
|
|
83775
|
+
}
|
|
83776
|
+
for (const bundle of toDelete) {
|
|
83777
|
+
fs15__default.rmSync(bundle.path, { recursive: true, force: true });
|
|
83778
|
+
logger2.verbose(`Deleted: ${bundle.name}`);
|
|
83779
|
+
}
|
|
83780
|
+
logger2.success(`Cleaned ${toDelete.length} proof bundles`);
|
|
83781
|
+
} catch (error) {
|
|
83782
|
+
logger2.error(error instanceof Error ? error.message : String(error));
|
|
83783
|
+
process.exit(1);
|
|
83784
|
+
}
|
|
83785
|
+
}
|
|
80909
83786
|
|
|
80910
83787
|
// src/index.ts
|
|
80911
83788
|
init_config();
|
|
@@ -81118,6 +83995,54 @@ program2.command("roast").description("\u{1F525} Can your code ship? Pass = earn
|
|
|
81118
83995
|
...globalOpts
|
|
81119
83996
|
});
|
|
81120
83997
|
});
|
|
83998
|
+
var realityCommand = program2.command("reality").description("\u{1F3AC} Reality Mode 2.0 - Runtime verification with proof bundles");
|
|
83999
|
+
realityCommand.command("run").description("Run Reality Mode and produce a Proof Bundle").option("-u, --url <url>", "Target URL (auto-detects if not provided)").option("-s, --scenarios <preset|ids>", "Scenario preset (smoke, standard, chaos) or comma-separated IDs", "standard").option("--headless", "Run browser in headless mode", true).option("--no-headless", "Run browser in headed mode (show browser)").option("-b, --browser <browser>", "Browser to use (chromium, firefox, webkit)", "chromium").option("--timeout <seconds>", "Global timeout in seconds", (v) => parsePositiveInteger(v, "timeout")).option("--video", "Enable video recording", true).option("--no-video", "Disable video recording").option("-o, --output <path>", "Output directory for proof bundle").option("--open", "Open report in browser after run").action(async (options, command) => {
|
|
84000
|
+
const globalOpts = command.optsWithGlobals();
|
|
84001
|
+
await realityRunCommand({
|
|
84002
|
+
url: options.url,
|
|
84003
|
+
scenarios: options.scenarios,
|
|
84004
|
+
headless: options.headless,
|
|
84005
|
+
browser: options.browser,
|
|
84006
|
+
timeout: options.timeout,
|
|
84007
|
+
video: options.video,
|
|
84008
|
+
output: options.output,
|
|
84009
|
+
open: options.open,
|
|
84010
|
+
...globalOpts
|
|
84011
|
+
});
|
|
84012
|
+
});
|
|
84013
|
+
realityCommand.command("report [runId]").description("Generate and view a proof report").option("-f, --format <format>", "Output format (html, json, markdown, terminal)", "html").option("--open", "Open report in browser after generation").action(async (runId, options, command) => {
|
|
84014
|
+
const globalOpts = command.optsWithGlobals();
|
|
84015
|
+
await realityReportCommand({
|
|
84016
|
+
runId: runId ?? "latest",
|
|
84017
|
+
format: options.format,
|
|
84018
|
+
open: options.open,
|
|
84019
|
+
...globalOpts
|
|
84020
|
+
});
|
|
84021
|
+
});
|
|
84022
|
+
realityCommand.command("replay <runId>").description("Replay a previous Reality Mode run exactly").option("--headless", "Run in headless mode", true).option("--no-headless", "Run in headed mode").option("--compare", "Compare results with original run").action(async (runId, options, command) => {
|
|
84023
|
+
const globalOpts = command.optsWithGlobals();
|
|
84024
|
+
await realityReplayCommand({
|
|
84025
|
+
runId,
|
|
84026
|
+
headless: options.headless,
|
|
84027
|
+
compare: options.compare,
|
|
84028
|
+
...globalOpts
|
|
84029
|
+
});
|
|
84030
|
+
});
|
|
84031
|
+
realityCommand.command("list").alias("ls").description("List all proof bundles").option("-l, --limit <count>", "Maximum number of runs to show", (v) => parsePositiveInteger(v, "limit")).action(async (options, command) => {
|
|
84032
|
+
const globalOpts = command.optsWithGlobals();
|
|
84033
|
+
await realityListCommand({
|
|
84034
|
+
limit: options.limit,
|
|
84035
|
+
...globalOpts
|
|
84036
|
+
});
|
|
84037
|
+
});
|
|
84038
|
+
realityCommand.command("clean").description("Clean up old proof bundles").option("-k, --keep <count>", "Keep latest N runs", (v) => parsePositiveInteger(v, "keep")).option("-f, --force", "Skip confirmation").action(async (options, command) => {
|
|
84039
|
+
const globalOpts = command.optsWithGlobals();
|
|
84040
|
+
await realityCleanCommand({
|
|
84041
|
+
keep: options.keep,
|
|
84042
|
+
force: options.force,
|
|
84043
|
+
...globalOpts
|
|
84044
|
+
});
|
|
84045
|
+
});
|
|
81121
84046
|
program2.command("ship").description("Pre-deployment checks with optional auto-fix").option("--fix", "Auto-fix issues before shipping").option("--force", "Proceed despite blockers").option("--strict", "Stricter pre-deploy checks").option("--reality", "Enable Reality Mode (auto-starts your app, runs browser tests)").option("--reality-url <url>", "Use existing server instead of auto-starting").option(
|
|
81122
84047
|
"--reality-timeout <seconds>",
|
|
81123
84048
|
"Reality Mode timeout in seconds",
|
|
@@ -81256,6 +84181,9 @@ async function runInteractiveMode() {
|
|
|
81256
84181
|
case "ship":
|
|
81257
84182
|
await shipCommand({ ...globalOpts });
|
|
81258
84183
|
break;
|
|
84184
|
+
case "reality":
|
|
84185
|
+
await realityRunCommand({ ...globalOpts });
|
|
84186
|
+
break;
|
|
81259
84187
|
case "report":
|
|
81260
84188
|
await reportCommand({ type: "reality-check", format: "html", ...globalOpts });
|
|
81261
84189
|
break;
|