@probelabs/visor 0.1.39 → 0.1.41
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 +3 -3
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/check-execution-engine.d.ts.map +1 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/git-repository-analyzer.d.ts +2 -2
- package/dist/git-repository-analyzer.d.ts.map +1 -1
- package/dist/index.js +739 -258
- package/dist/providers/check-provider.interface.d.ts +0 -1
- package/dist/providers/check-provider.interface.d.ts.map +1 -1
- package/dist/providers/command-check-provider.d.ts +21 -0
- package/dist/providers/command-check-provider.d.ts.map +1 -0
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/config.d.ts +4 -2
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/providers/tool-check-provider.d.ts +0 -24
- package/dist/providers/tool-check-provider.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -94115,6 +94115,21 @@ ${prContext}
|
|
|
94115
94115
|
formatPRContext(prInfo) {
|
|
94116
94116
|
// Check if this is an issue (not a PR)
|
|
94117
94117
|
const isIssue = prInfo.isIssue === true;
|
|
94118
|
+
// Check if we should include code context (diffs)
|
|
94119
|
+
const isPRContext = prInfo.isPRContext === true;
|
|
94120
|
+
// In PR context, always include diffs. Otherwise check the flag.
|
|
94121
|
+
const includeCodeContext = isPRContext || prInfo.includeCodeContext !== false;
|
|
94122
|
+
// Log the decision for transparency
|
|
94123
|
+
const log = this.config.debug ? console.error : () => { };
|
|
94124
|
+
if (isPRContext) {
|
|
94125
|
+
log('🔍 Including full code diffs in AI context (PR mode)');
|
|
94126
|
+
}
|
|
94127
|
+
else if (!includeCodeContext) {
|
|
94128
|
+
log('📊 Including only file summary in AI context (no diffs)');
|
|
94129
|
+
}
|
|
94130
|
+
else {
|
|
94131
|
+
log('🔍 Including code diffs in AI context');
|
|
94132
|
+
}
|
|
94118
94133
|
if (isIssue) {
|
|
94119
94134
|
// Format as issue context
|
|
94120
94135
|
let context = `<issue>
|
|
@@ -94236,31 +94251,39 @@ ${this.escapeXml(prInfo.body)}
|
|
|
94236
94251
|
${this.escapeXml(prInfo.body)}
|
|
94237
94252
|
</description>`;
|
|
94238
94253
|
}
|
|
94239
|
-
// Add
|
|
94240
|
-
if (
|
|
94241
|
-
|
|
94254
|
+
// Add diffs only if includeCodeContext is true (or in PR mode)
|
|
94255
|
+
if (includeCodeContext) {
|
|
94256
|
+
// Add full diff if available (for complete PR review)
|
|
94257
|
+
if (prInfo.fullDiff) {
|
|
94258
|
+
context += `
|
|
94242
94259
|
<!-- Complete unified diff showing all changes in the pull request -->
|
|
94243
94260
|
<full_diff>
|
|
94244
94261
|
${this.escapeXml(prInfo.fullDiff)}
|
|
94245
94262
|
</full_diff>`;
|
|
94246
|
-
|
|
94247
|
-
|
|
94248
|
-
|
|
94249
|
-
|
|
94250
|
-
|
|
94263
|
+
}
|
|
94264
|
+
// Add incremental commit diff if available (for new commit analysis)
|
|
94265
|
+
if (prInfo.isIncremental) {
|
|
94266
|
+
if (prInfo.commitDiff && prInfo.commitDiff.length > 0) {
|
|
94267
|
+
context += `
|
|
94251
94268
|
<!-- Diff of only the latest commit for incremental analysis -->
|
|
94252
94269
|
<commit_diff>
|
|
94253
94270
|
${this.escapeXml(prInfo.commitDiff)}
|
|
94254
94271
|
</commit_diff>`;
|
|
94255
|
-
|
|
94256
|
-
|
|
94257
|
-
|
|
94272
|
+
}
|
|
94273
|
+
else {
|
|
94274
|
+
context += `
|
|
94258
94275
|
<!-- Commit diff could not be retrieved - falling back to full diff analysis -->
|
|
94259
94276
|
<commit_diff>
|
|
94260
94277
|
${prInfo.fullDiff ? this.escapeXml(prInfo.fullDiff) : ''}
|
|
94261
94278
|
</commit_diff>`;
|
|
94279
|
+
}
|
|
94262
94280
|
}
|
|
94263
94281
|
}
|
|
94282
|
+
else {
|
|
94283
|
+
// When not including diffs, add a note about it
|
|
94284
|
+
context += `
|
|
94285
|
+
<!-- Code diffs excluded to reduce token usage (no code-review schema detected or disabled by flag) -->`;
|
|
94286
|
+
}
|
|
94264
94287
|
// Add file summary for context
|
|
94265
94288
|
if (prInfo.files.length > 0) {
|
|
94266
94289
|
context += `
|
|
@@ -95612,8 +95635,7 @@ class CheckExecutionEngine {
|
|
|
95612
95635
|
// Execute checks level by level
|
|
95613
95636
|
const results = new Map();
|
|
95614
95637
|
const sessionRegistry = (__nccwpck_require__(46059).SessionRegistry).getInstance();
|
|
95615
|
-
|
|
95616
|
-
this.setProviderWebhookContext(provider);
|
|
95638
|
+
// Note: We'll get the provider dynamically per check, not a single one for all
|
|
95617
95639
|
const sessionIds = new Map(); // checkName -> sessionId
|
|
95618
95640
|
let shouldStopExecution = false;
|
|
95619
95641
|
for (let levelIndex = 0; levelIndex < dependencyGraph.executionOrder.length && !shouldStopExecution; levelIndex++) {
|
|
@@ -95661,15 +95683,24 @@ class CheckExecutionEngine {
|
|
|
95661
95683
|
};
|
|
95662
95684
|
}
|
|
95663
95685
|
}
|
|
95686
|
+
// Get the appropriate provider for this check type
|
|
95687
|
+
const providerType = checkConfig.type || 'ai';
|
|
95688
|
+
const provider = this.providerRegistry.getProviderOrThrow(providerType);
|
|
95689
|
+
this.setProviderWebhookContext(provider);
|
|
95664
95690
|
// Create provider config for this specific check
|
|
95665
95691
|
const providerConfig = {
|
|
95666
|
-
type:
|
|
95692
|
+
type: providerType,
|
|
95667
95693
|
prompt: checkConfig.prompt,
|
|
95694
|
+
exec: checkConfig.exec,
|
|
95668
95695
|
focus: checkConfig.focus || this.mapCheckNameToFocus(checkName),
|
|
95669
95696
|
schema: checkConfig.schema,
|
|
95670
95697
|
group: checkConfig.group,
|
|
95671
95698
|
checkName: checkName, // Add checkName for sessionID
|
|
95672
95699
|
eventContext: prInfo.eventContext, // Pass event context for templates
|
|
95700
|
+
transform: checkConfig.transform,
|
|
95701
|
+
level: checkConfig.level,
|
|
95702
|
+
message: checkConfig.message,
|
|
95703
|
+
env: checkConfig.env,
|
|
95673
95704
|
ai: {
|
|
95674
95705
|
timeout: timeout || 600000,
|
|
95675
95706
|
debug: debug,
|
|
@@ -95678,9 +95709,19 @@ class CheckExecutionEngine {
|
|
|
95678
95709
|
};
|
|
95679
95710
|
// Pass results from dependencies if needed
|
|
95680
95711
|
const dependencyResults = new Map();
|
|
95712
|
+
let isForEachDependent = false;
|
|
95713
|
+
let forEachItems = [];
|
|
95714
|
+
let forEachParentName;
|
|
95681
95715
|
for (const depId of checkConfig.depends_on || []) {
|
|
95682
95716
|
if (results.has(depId)) {
|
|
95683
|
-
|
|
95717
|
+
const depResult = results.get(depId);
|
|
95718
|
+
dependencyResults.set(depId, depResult);
|
|
95719
|
+
// Check if this dependency has forEach enabled
|
|
95720
|
+
if (depResult.isForEach && depResult.forEachItems) {
|
|
95721
|
+
isForEachDependent = true;
|
|
95722
|
+
forEachItems = depResult.forEachItems;
|
|
95723
|
+
forEachParentName = depId;
|
|
95724
|
+
}
|
|
95684
95725
|
}
|
|
95685
95726
|
}
|
|
95686
95727
|
// Determine if we should use session reuse
|
|
@@ -95709,10 +95750,54 @@ class CheckExecutionEngine {
|
|
|
95709
95750
|
// Add session ID to provider config
|
|
95710
95751
|
providerConfig.sessionId = currentSessionId;
|
|
95711
95752
|
}
|
|
95712
|
-
|
|
95713
|
-
|
|
95753
|
+
// Handle forEach dependent execution
|
|
95754
|
+
let finalResult;
|
|
95755
|
+
if (isForEachDependent && forEachParentName) {
|
|
95756
|
+
log(`🔄 Debug: Check "${checkName}" depends on forEach check "${forEachParentName}", executing ${forEachItems.length} times`);
|
|
95757
|
+
const allIssues = [];
|
|
95758
|
+
const allOutputs = [];
|
|
95759
|
+
// Execute check for each item in the forEach array
|
|
95760
|
+
for (let itemIndex = 0; itemIndex < forEachItems.length; itemIndex++) {
|
|
95761
|
+
const item = forEachItems[itemIndex];
|
|
95762
|
+
// Create modified dependency results with current item
|
|
95763
|
+
const forEachDependencyResults = new Map();
|
|
95764
|
+
for (const [depName, depResult] of dependencyResults) {
|
|
95765
|
+
if (depName === forEachParentName) {
|
|
95766
|
+
// Replace the forEach parent's output with the current item
|
|
95767
|
+
const modifiedResult = {
|
|
95768
|
+
...depResult,
|
|
95769
|
+
output: item,
|
|
95770
|
+
};
|
|
95771
|
+
forEachDependencyResults.set(depName, modifiedResult);
|
|
95772
|
+
}
|
|
95773
|
+
else {
|
|
95774
|
+
forEachDependencyResults.set(depName, depResult);
|
|
95775
|
+
}
|
|
95776
|
+
}
|
|
95777
|
+
log(`🔄 Debug: Executing check "${checkName}" for item ${itemIndex + 1}/${forEachItems.length}`);
|
|
95778
|
+
const itemResult = await provider.execute(prInfo, providerConfig, forEachDependencyResults, sessionInfo);
|
|
95779
|
+
// Collect issues from each iteration
|
|
95780
|
+
if (itemResult.issues) {
|
|
95781
|
+
allIssues.push(...itemResult.issues);
|
|
95782
|
+
}
|
|
95783
|
+
// Collect outputs from each iteration
|
|
95784
|
+
if (itemResult.output) {
|
|
95785
|
+
allOutputs.push(itemResult.output);
|
|
95786
|
+
}
|
|
95787
|
+
}
|
|
95788
|
+
finalResult = {
|
|
95789
|
+
issues: allIssues,
|
|
95790
|
+
output: allOutputs.length > 0 ? allOutputs : undefined,
|
|
95791
|
+
};
|
|
95792
|
+
log(`🔄 Debug: Completed forEach execution for check "${checkName}", total issues: ${allIssues.length}`);
|
|
95793
|
+
}
|
|
95794
|
+
else {
|
|
95795
|
+
// Normal single execution
|
|
95796
|
+
finalResult = await provider.execute(prInfo, providerConfig, dependencyResults, sessionInfo);
|
|
95797
|
+
log(`🔧 Debug: Completed check: ${checkName}, issues found: ${(finalResult.issues || []).length}`);
|
|
95798
|
+
}
|
|
95714
95799
|
// Add group, schema, template info and timestamp to issues from config
|
|
95715
|
-
const enrichedIssues = (
|
|
95800
|
+
const enrichedIssues = (finalResult.issues || []).map(issue => ({
|
|
95716
95801
|
...issue,
|
|
95717
95802
|
ruleId: `${checkName}/${issue.ruleId}`,
|
|
95718
95803
|
group: checkConfig.group,
|
|
@@ -95721,7 +95806,7 @@ class CheckExecutionEngine {
|
|
|
95721
95806
|
timestamp: Date.now(),
|
|
95722
95807
|
}));
|
|
95723
95808
|
const enrichedResult = {
|
|
95724
|
-
...
|
|
95809
|
+
...finalResult,
|
|
95725
95810
|
issues: enrichedIssues,
|
|
95726
95811
|
};
|
|
95727
95812
|
return {
|
|
@@ -95746,8 +95831,34 @@ class CheckExecutionEngine {
|
|
|
95746
95831
|
for (let i = 0; i < levelResults.length; i++) {
|
|
95747
95832
|
const checkName = executionGroup.parallel[i];
|
|
95748
95833
|
const result = levelResults[i];
|
|
95834
|
+
const checkConfig = config.checks[checkName];
|
|
95749
95835
|
if (result.status === 'fulfilled' && result.value.result && !result.value.error) {
|
|
95750
|
-
|
|
95836
|
+
const reviewResult = result.value.result;
|
|
95837
|
+
// Handle forEach logic - process array outputs
|
|
95838
|
+
if (checkConfig?.forEach && reviewResult.output) {
|
|
95839
|
+
let outputArray = reviewResult.output;
|
|
95840
|
+
// Ensure output is an array
|
|
95841
|
+
if (!Array.isArray(outputArray)) {
|
|
95842
|
+
// Try to parse as JSON if it's a string
|
|
95843
|
+
if (typeof outputArray === 'string') {
|
|
95844
|
+
try {
|
|
95845
|
+
const parsed = JSON.parse(outputArray);
|
|
95846
|
+
outputArray = Array.isArray(parsed) ? parsed : [parsed];
|
|
95847
|
+
}
|
|
95848
|
+
catch {
|
|
95849
|
+
outputArray = [outputArray];
|
|
95850
|
+
}
|
|
95851
|
+
}
|
|
95852
|
+
else {
|
|
95853
|
+
outputArray = [outputArray];
|
|
95854
|
+
}
|
|
95855
|
+
}
|
|
95856
|
+
log(`🔄 Debug: Check "${checkName}" has forEach enabled, processing ${outputArray.length} items`);
|
|
95857
|
+
// Store the array for iteration by dependent checks
|
|
95858
|
+
reviewResult.forEachItems = outputArray;
|
|
95859
|
+
reviewResult.isForEach = true;
|
|
95860
|
+
}
|
|
95861
|
+
results.set(checkName, reviewResult);
|
|
95751
95862
|
}
|
|
95752
95863
|
else {
|
|
95753
95864
|
// Store error result for dependency tracking
|
|
@@ -96781,10 +96892,6 @@ async function main() {
|
|
|
96781
96892
|
.findAndLoadConfig()
|
|
96782
96893
|
.catch(() => configManager.getDefaultConfig());
|
|
96783
96894
|
}
|
|
96784
|
-
// Get repository info using GitRepositoryAnalyzer
|
|
96785
|
-
const { GitRepositoryAnalyzer } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(71259)));
|
|
96786
|
-
const analyzer = new GitRepositoryAnalyzer(process.cwd());
|
|
96787
|
-
const repositoryInfo = await analyzer.analyzeRepository();
|
|
96788
96895
|
// Determine checks to run and validate check types early
|
|
96789
96896
|
const checksToRun = options.checks.length > 0 ? options.checks : Object.keys(config.checks || {});
|
|
96790
96897
|
// Validate that all requested checks exist in the configuration
|
|
@@ -96794,13 +96901,58 @@ async function main() {
|
|
|
96794
96901
|
console.error(`❌ Error: No configuration found for check: ${invalidChecks[0]}`);
|
|
96795
96902
|
process.exit(1);
|
|
96796
96903
|
}
|
|
96797
|
-
//
|
|
96904
|
+
// Use stderr for status messages when outputting formatted results to stdout
|
|
96905
|
+
const logFn = console.error;
|
|
96906
|
+
// Determine if we should include code context (diffs)
|
|
96907
|
+
// In CLI mode (local), we do smart detection. PR mode always includes context.
|
|
96908
|
+
const isPRContext = false; // This is CLI mode, not GitHub Action
|
|
96909
|
+
let includeCodeContext = false;
|
|
96910
|
+
if (isPRContext) {
|
|
96911
|
+
// ALWAYS include full context in PR/GitHub Action mode
|
|
96912
|
+
includeCodeContext = true;
|
|
96913
|
+
logFn('📝 Code context: ENABLED (PR context - always included)');
|
|
96914
|
+
}
|
|
96915
|
+
else if (options.codeContext === 'enabled') {
|
|
96916
|
+
includeCodeContext = true;
|
|
96917
|
+
logFn('📝 Code context: ENABLED (forced by --enable-code-context)');
|
|
96918
|
+
}
|
|
96919
|
+
else if (options.codeContext === 'disabled') {
|
|
96920
|
+
includeCodeContext = false;
|
|
96921
|
+
logFn('📝 Code context: DISABLED (forced by --disable-code-context)');
|
|
96922
|
+
}
|
|
96923
|
+
else {
|
|
96924
|
+
// Auto-detect based on schemas (CLI mode only)
|
|
96925
|
+
const hasCodeReviewSchema = checksToRun.some(check => config.checks?.[check]?.schema === 'code-review');
|
|
96926
|
+
includeCodeContext = hasCodeReviewSchema;
|
|
96927
|
+
if (hasCodeReviewSchema) {
|
|
96928
|
+
logFn('📝 Code context: ENABLED (code-review schema detected in local mode)');
|
|
96929
|
+
}
|
|
96930
|
+
else {
|
|
96931
|
+
logFn('📝 Code context: DISABLED (no code-review schema found in local mode)');
|
|
96932
|
+
}
|
|
96933
|
+
}
|
|
96934
|
+
// Get repository info using GitRepositoryAnalyzer
|
|
96935
|
+
const { GitRepositoryAnalyzer } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(71259)));
|
|
96936
|
+
const analyzer = new GitRepositoryAnalyzer(process.cwd());
|
|
96937
|
+
let repositoryInfo;
|
|
96938
|
+
try {
|
|
96939
|
+
repositoryInfo = await analyzer.analyzeRepository(includeCodeContext);
|
|
96940
|
+
}
|
|
96941
|
+
catch (error) {
|
|
96942
|
+
console.error('❌ Error analyzing git repository:', error);
|
|
96943
|
+
console.error('💡 Make sure you are in a git repository or initialize one with "git init"');
|
|
96944
|
+
process.exit(1);
|
|
96945
|
+
}
|
|
96946
|
+
// Check if we're in a git repository
|
|
96798
96947
|
if (!repositoryInfo.isGitRepository) {
|
|
96799
|
-
console.error('❌ Error: Not a git repository
|
|
96948
|
+
console.error('❌ Error: Not a git repository. Run "git init" to initialize a repository.');
|
|
96949
|
+
process.exit(1);
|
|
96950
|
+
}
|
|
96951
|
+
// Check if there are any changes to analyze
|
|
96952
|
+
if (repositoryInfo.files.length === 0) {
|
|
96953
|
+
console.error('❌ Error: No changes to analyze. Make some file changes first.');
|
|
96800
96954
|
process.exit(1);
|
|
96801
96955
|
}
|
|
96802
|
-
// Use stderr for status messages when outputting formatted results to stdout
|
|
96803
|
-
const logFn = console.error;
|
|
96804
96956
|
logFn('🔍 Visor - AI-powered code review tool');
|
|
96805
96957
|
logFn(`Configuration version: ${config.version}`);
|
|
96806
96958
|
logFn(`Configuration source: ${options.configPath || 'default search locations'}`);
|
|
@@ -96823,8 +96975,12 @@ async function main() {
|
|
|
96823
96975
|
exclude: options.excludeTags,
|
|
96824
96976
|
}
|
|
96825
96977
|
: undefined;
|
|
96826
|
-
//
|
|
96827
|
-
const
|
|
96978
|
+
// Convert repository info to PRInfo format
|
|
96979
|
+
const prInfo = analyzer.toPRInfo(repositoryInfo, includeCodeContext);
|
|
96980
|
+
// Store the includeCodeContext flag in prInfo for downstream use
|
|
96981
|
+
prInfo.includeCodeContext = includeCodeContext;
|
|
96982
|
+
// Execute checks with proper parameters
|
|
96983
|
+
const groupedResults = await engine.executeGroupedChecks(prInfo, checksToRun, options.timeout, config, options.output, options.debug || false, options.maxParallelism, options.failFast, tagFilter);
|
|
96828
96984
|
// Format output based on format type
|
|
96829
96985
|
let output;
|
|
96830
96986
|
if (options.output === 'json') {
|
|
@@ -96993,6 +97149,8 @@ class CLI {
|
|
|
96993
97149
|
.option('--tags <tags>', 'Include checks with these tags (comma-separated)')
|
|
96994
97150
|
.option('--exclude-tags <tags>', 'Exclude checks with these tags (comma-separated)')
|
|
96995
97151
|
.option('--no-remote-extends', 'Disable loading configurations from remote URLs')
|
|
97152
|
+
.option('--enable-code-context', 'Force include code diffs in analysis (CLI mode)')
|
|
97153
|
+
.option('--disable-code-context', 'Force exclude code diffs from analysis (CLI mode)')
|
|
96996
97154
|
.addHelpText('after', this.getExamplesText())
|
|
96997
97155
|
.exitOverride(); // Prevent automatic process.exit for better error handling
|
|
96998
97156
|
// Add validation for options
|
|
@@ -97030,6 +97188,8 @@ class CLI {
|
|
|
97030
97188
|
.option('--tags <tags>', 'Include checks with these tags (comma-separated)')
|
|
97031
97189
|
.option('--exclude-tags <tags>', 'Exclude checks with these tags (comma-separated)')
|
|
97032
97190
|
.option('--allowed-remote-patterns <patterns>', 'Comma-separated list of allowed URL prefixes for remote config extends (e.g., "https://github.com/,https://raw.githubusercontent.com/")')
|
|
97191
|
+
.option('--enable-code-context', 'Force include code diffs in analysis (CLI mode)')
|
|
97192
|
+
.option('--disable-code-context', 'Force exclude code diffs from analysis (CLI mode)')
|
|
97033
97193
|
.allowUnknownOption(false)
|
|
97034
97194
|
.allowExcessArguments(false) // Don't allow positional arguments
|
|
97035
97195
|
.addHelpText('after', this.getExamplesText())
|
|
@@ -97060,6 +97220,14 @@ class CLI {
|
|
|
97060
97220
|
if (options.excludeTags) {
|
|
97061
97221
|
excludeTags = options.excludeTags.split(',').map((t) => t.trim());
|
|
97062
97222
|
}
|
|
97223
|
+
// Determine code context mode
|
|
97224
|
+
let codeContext = 'auto';
|
|
97225
|
+
if (options.enableCodeContext) {
|
|
97226
|
+
codeContext = 'enabled';
|
|
97227
|
+
}
|
|
97228
|
+
else if (options.disableCodeContext) {
|
|
97229
|
+
codeContext = 'disabled';
|
|
97230
|
+
}
|
|
97063
97231
|
return {
|
|
97064
97232
|
checks: uniqueChecks,
|
|
97065
97233
|
output: options.output,
|
|
@@ -97073,6 +97241,7 @@ class CLI {
|
|
|
97073
97241
|
allowedRemotePatterns,
|
|
97074
97242
|
help: options.help,
|
|
97075
97243
|
version: options.version,
|
|
97244
|
+
codeContext,
|
|
97076
97245
|
};
|
|
97077
97246
|
}
|
|
97078
97247
|
catch (error) {
|
|
@@ -97141,6 +97310,10 @@ class CLI {
|
|
|
97141
97310
|
.option('--max-parallelism <count>', 'Maximum number of checks to run in parallel (default: 3)', value => parseInt(value, 10))
|
|
97142
97311
|
.option('--debug', 'Enable debug mode for detailed output')
|
|
97143
97312
|
.option('--fail-fast', 'Stop execution on first failure condition')
|
|
97313
|
+
.option('--tags <tags>', 'Include checks with these tags (comma-separated)')
|
|
97314
|
+
.option('--exclude-tags <tags>', 'Exclude checks with these tags (comma-separated)')
|
|
97315
|
+
.option('--enable-code-context', 'Force include code diffs in analysis (CLI mode)')
|
|
97316
|
+
.option('--disable-code-context', 'Force exclude code diffs from analysis (CLI mode)')
|
|
97144
97317
|
.addHelpText('after', this.getExamplesText());
|
|
97145
97318
|
// Get the basic help and append examples manually if addHelpText doesn't work
|
|
97146
97319
|
const basicHelp = tempProgram.helpInformation();
|
|
@@ -97315,7 +97488,7 @@ class ConfigManager {
|
|
|
97315
97488
|
validCheckTypes = [
|
|
97316
97489
|
'ai',
|
|
97317
97490
|
'claude-code',
|
|
97318
|
-
'
|
|
97491
|
+
'command',
|
|
97319
97492
|
'http',
|
|
97320
97493
|
'http_input',
|
|
97321
97494
|
'http_client',
|
|
@@ -97661,11 +97834,11 @@ class ConfigManager {
|
|
|
97661
97834
|
message: `Invalid check configuration for "${checkName}": missing prompt (required for AI checks)`,
|
|
97662
97835
|
});
|
|
97663
97836
|
}
|
|
97664
|
-
//
|
|
97665
|
-
if (checkConfig.type === '
|
|
97837
|
+
// Command checks require exec field
|
|
97838
|
+
if (checkConfig.type === 'command' && !checkConfig.exec) {
|
|
97666
97839
|
errors.push({
|
|
97667
97840
|
field: `checks.${checkName}.exec`,
|
|
97668
|
-
message: `Invalid check configuration for "${checkName}": missing exec field (required for
|
|
97841
|
+
message: `Invalid check configuration for "${checkName}": missing exec field (required for command checks)`,
|
|
97669
97842
|
});
|
|
97670
97843
|
}
|
|
97671
97844
|
// HTTP output checks require url and body fields
|
|
@@ -98692,7 +98865,7 @@ class GitRepositoryAnalyzer {
|
|
|
98692
98865
|
/**
|
|
98693
98866
|
* Analyze the current git repository state and return data compatible with PRInfo interface
|
|
98694
98867
|
*/
|
|
98695
|
-
async analyzeRepository() {
|
|
98868
|
+
async analyzeRepository(includeContext = true) {
|
|
98696
98869
|
// Check if we're in a git repository
|
|
98697
98870
|
const isRepo = await this.isGitRepository();
|
|
98698
98871
|
if (!isRepo) {
|
|
@@ -98705,15 +98878,36 @@ class GitRepositoryAnalyzer {
|
|
|
98705
98878
|
this.getCurrentBranch(),
|
|
98706
98879
|
]);
|
|
98707
98880
|
// Get uncommitted changes
|
|
98708
|
-
const uncommittedFiles = await this.getUncommittedChanges();
|
|
98709
|
-
// Get recent commit info
|
|
98710
|
-
|
|
98711
|
-
|
|
98881
|
+
const uncommittedFiles = await this.getUncommittedChanges(includeContext);
|
|
98882
|
+
// Get recent commit info (handle repos with no commits)
|
|
98883
|
+
let lastCommit = null;
|
|
98884
|
+
try {
|
|
98885
|
+
const recentCommits = await this.git.log({ maxCount: 1 });
|
|
98886
|
+
lastCommit = recentCommits.latest;
|
|
98887
|
+
}
|
|
98888
|
+
catch {
|
|
98889
|
+
// Repository has no commits yet - this is OK
|
|
98890
|
+
console.log('📝 Repository has no commits yet, analyzing uncommitted changes');
|
|
98891
|
+
}
|
|
98892
|
+
// Get author from git config if no commits exist
|
|
98893
|
+
let author = lastCommit?.author_name;
|
|
98894
|
+
if (!author) {
|
|
98895
|
+
try {
|
|
98896
|
+
const [userName, userEmail] = await Promise.all([
|
|
98897
|
+
this.git.raw(['config', 'user.name']).catch(() => null),
|
|
98898
|
+
this.git.raw(['config', 'user.email']).catch(() => null),
|
|
98899
|
+
]);
|
|
98900
|
+
author = userName?.trim() || userEmail?.trim() || 'unknown';
|
|
98901
|
+
}
|
|
98902
|
+
catch {
|
|
98903
|
+
author = 'unknown';
|
|
98904
|
+
}
|
|
98905
|
+
}
|
|
98712
98906
|
// Create repository info
|
|
98713
98907
|
const repositoryInfo = {
|
|
98714
98908
|
title: this.generateTitle(status, currentBranch),
|
|
98715
98909
|
body: this.generateDescription(status, lastCommit),
|
|
98716
|
-
author
|
|
98910
|
+
author,
|
|
98717
98911
|
base: await this.getBaseBranch(),
|
|
98718
98912
|
head: currentBranch,
|
|
98719
98913
|
files: uncommittedFiles,
|
|
@@ -98725,14 +98919,32 @@ class GitRepositoryAnalyzer {
|
|
|
98725
98919
|
return repositoryInfo;
|
|
98726
98920
|
}
|
|
98727
98921
|
catch (error) {
|
|
98728
|
-
|
|
98922
|
+
// Don't log the full error object to avoid confusing stack traces
|
|
98923
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
98924
|
+
console.error('Error analyzing git repository:', errorMessage);
|
|
98729
98925
|
return this.createEmptyRepositoryInfo('Error analyzing git repository');
|
|
98730
98926
|
}
|
|
98731
98927
|
}
|
|
98732
98928
|
/**
|
|
98733
98929
|
* Convert GitRepositoryInfo to PRInfo format for compatibility with existing PRReviewer
|
|
98734
98930
|
*/
|
|
98735
|
-
toPRInfo(repositoryInfo) {
|
|
98931
|
+
toPRInfo(repositoryInfo, includeContext = true) {
|
|
98932
|
+
const files = repositoryInfo.files.map((file) => ({
|
|
98933
|
+
filename: file.filename,
|
|
98934
|
+
additions: file.additions,
|
|
98935
|
+
deletions: file.deletions,
|
|
98936
|
+
changes: file.changes,
|
|
98937
|
+
patch: includeContext ? file.patch : undefined,
|
|
98938
|
+
status: file.status,
|
|
98939
|
+
}));
|
|
98940
|
+
// Generate fullDiff from patches if includeContext is true
|
|
98941
|
+
let fullDiff;
|
|
98942
|
+
if (includeContext) {
|
|
98943
|
+
fullDiff = files
|
|
98944
|
+
.filter(file => file.patch)
|
|
98945
|
+
.map(file => `--- ${file.filename}\n${file.patch}`)
|
|
98946
|
+
.join('\n\n');
|
|
98947
|
+
}
|
|
98736
98948
|
return {
|
|
98737
98949
|
number: 0, // Local analysis doesn't have PR number
|
|
98738
98950
|
title: repositoryInfo.title,
|
|
@@ -98740,16 +98952,10 @@ class GitRepositoryAnalyzer {
|
|
|
98740
98952
|
author: repositoryInfo.author,
|
|
98741
98953
|
base: repositoryInfo.base,
|
|
98742
98954
|
head: repositoryInfo.head,
|
|
98743
|
-
files
|
|
98744
|
-
filename: file.filename,
|
|
98745
|
-
additions: file.additions,
|
|
98746
|
-
deletions: file.deletions,
|
|
98747
|
-
changes: file.changes,
|
|
98748
|
-
patch: file.patch,
|
|
98749
|
-
status: file.status,
|
|
98750
|
-
})),
|
|
98955
|
+
files,
|
|
98751
98956
|
totalAdditions: repositoryInfo.totalAdditions,
|
|
98752
98957
|
totalDeletions: repositoryInfo.totalDeletions,
|
|
98958
|
+
fullDiff,
|
|
98753
98959
|
};
|
|
98754
98960
|
}
|
|
98755
98961
|
async isGitRepository() {
|
|
@@ -98799,7 +99005,7 @@ class GitRepositoryAnalyzer {
|
|
|
98799
99005
|
return null;
|
|
98800
99006
|
}
|
|
98801
99007
|
}
|
|
98802
|
-
async getUncommittedChanges() {
|
|
99008
|
+
async getUncommittedChanges(includeContext = true) {
|
|
98803
99009
|
try {
|
|
98804
99010
|
const status = await this.git.status();
|
|
98805
99011
|
const changes = [];
|
|
@@ -98815,7 +99021,7 @@ class GitRepositoryAnalyzer {
|
|
|
98815
99021
|
];
|
|
98816
99022
|
for (const { file, status } of fileChanges) {
|
|
98817
99023
|
const filePath = path.join(this.cwd, file);
|
|
98818
|
-
const fileChange = await this.analyzeFileChange(file, status, filePath);
|
|
99024
|
+
const fileChange = await this.analyzeFileChange(file, status, filePath, includeContext);
|
|
98819
99025
|
changes.push(fileChange);
|
|
98820
99026
|
}
|
|
98821
99027
|
return changes;
|
|
@@ -98825,14 +99031,14 @@ class GitRepositoryAnalyzer {
|
|
|
98825
99031
|
return [];
|
|
98826
99032
|
}
|
|
98827
99033
|
}
|
|
98828
|
-
async analyzeFileChange(filename, status, filePath) {
|
|
99034
|
+
async analyzeFileChange(filename, status, filePath, includeContext = true) {
|
|
98829
99035
|
let additions = 0;
|
|
98830
99036
|
let deletions = 0;
|
|
98831
99037
|
let patch;
|
|
98832
99038
|
let content;
|
|
98833
99039
|
try {
|
|
98834
99040
|
// Get diff for the file if it exists and is not binary
|
|
98835
|
-
if (status !== 'added' && fs.existsSync(filePath)) {
|
|
99041
|
+
if (includeContext && status !== 'added' && fs.existsSync(filePath)) {
|
|
98836
99042
|
const diff = await this.git.diff(['--', filename]).catch(() => '');
|
|
98837
99043
|
if (diff) {
|
|
98838
99044
|
patch = diff;
|
|
@@ -98842,15 +99048,28 @@ class GitRepositoryAnalyzer {
|
|
|
98842
99048
|
deletions = lines.filter(line => line.startsWith('-')).length;
|
|
98843
99049
|
}
|
|
98844
99050
|
}
|
|
98845
|
-
|
|
99051
|
+
else if (status !== 'added' && fs.existsSync(filePath)) {
|
|
99052
|
+
// If not including context, still count changes for statistics
|
|
99053
|
+
const diff = await this.git.diff(['--', filename]).catch(() => '');
|
|
99054
|
+
if (diff) {
|
|
99055
|
+
const lines = diff.split('\n');
|
|
99056
|
+
additions = lines.filter(line => line.startsWith('+')).length;
|
|
99057
|
+
deletions = lines.filter(line => line.startsWith('-')).length;
|
|
99058
|
+
}
|
|
99059
|
+
}
|
|
99060
|
+
// For added files
|
|
98846
99061
|
if (status === 'added' && fs.existsSync(filePath)) {
|
|
98847
99062
|
try {
|
|
98848
99063
|
const stats = fs.statSync(filePath);
|
|
98849
99064
|
if (stats.isFile() && stats.size < 1024 * 1024) {
|
|
98850
99065
|
// Skip files larger than 1MB
|
|
98851
|
-
|
|
98852
|
-
|
|
98853
|
-
|
|
99066
|
+
if (includeContext) {
|
|
99067
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
99068
|
+
patch = content; // For new files, the entire content is the "patch"
|
|
99069
|
+
}
|
|
99070
|
+
// Always count additions for statistics
|
|
99071
|
+
const fileContent = includeContext ? content : fs.readFileSync(filePath, 'utf8');
|
|
99072
|
+
additions = fileContent.split('\n').length;
|
|
98854
99073
|
}
|
|
98855
99074
|
}
|
|
98856
99075
|
catch {
|
|
@@ -100232,6 +100451,9 @@ async function handleIssueComment(octokit, owner, repo, context, inputs, actionC
|
|
|
100232
100451
|
prInfo = await analyzer.fetchPRDiff(owner, repo, prNumber, undefined, 'issue_comment');
|
|
100233
100452
|
// Add event context for templates and XML generation
|
|
100234
100453
|
prInfo.eventContext = context.event;
|
|
100454
|
+
// PR context always includes code diffs
|
|
100455
|
+
prInfo.includeCodeContext = true;
|
|
100456
|
+
prInfo.isPRContext = true;
|
|
100235
100457
|
}
|
|
100236
100458
|
else {
|
|
100237
100459
|
// It's an issue comment - create a minimal PRInfo structure for issue assistant
|
|
@@ -100337,11 +100559,16 @@ async function handlePullRequestWithConfig(octokit, owner, repo, inputs, config,
|
|
|
100337
100559
|
// Map the action to event type
|
|
100338
100560
|
const eventType = mapGitHubEventToTrigger('pull_request', action);
|
|
100339
100561
|
// Fetch PR diff (handle test scenarios gracefully)
|
|
100562
|
+
// In PR context, ALWAYS include full diffs for proper code review
|
|
100563
|
+
console.log('📝 Code context: ENABLED (PR context - always included)');
|
|
100340
100564
|
let prInfo;
|
|
100341
100565
|
try {
|
|
100342
100566
|
prInfo = await analyzer.fetchPRDiff(owner, repo, prNumber, undefined, eventType);
|
|
100343
100567
|
// Add event context for templates and XML generation
|
|
100344
100568
|
prInfo.eventContext = context.event;
|
|
100569
|
+
// Mark that we're in PR context and should always include diffs
|
|
100570
|
+
prInfo.includeCodeContext = true;
|
|
100571
|
+
prInfo.isPRContext = true;
|
|
100345
100572
|
}
|
|
100346
100573
|
catch (error) {
|
|
100347
100574
|
// Handle test scenarios with mock repos
|
|
@@ -102367,13 +102594,13 @@ exports.AICheckProvider = AICheckProvider;
|
|
|
102367
102594
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
102368
102595
|
exports.CheckProviderRegistry = void 0;
|
|
102369
102596
|
const ai_check_provider_1 = __nccwpck_require__(98901);
|
|
102370
|
-
const tool_check_provider_1 = __nccwpck_require__(70477);
|
|
102371
102597
|
const http_check_provider_1 = __nccwpck_require__(31115);
|
|
102372
102598
|
const http_input_provider_1 = __nccwpck_require__(74423);
|
|
102373
102599
|
const http_client_provider_1 = __nccwpck_require__(36270);
|
|
102374
102600
|
const noop_check_provider_1 = __nccwpck_require__(53003);
|
|
102375
102601
|
const log_check_provider_1 = __nccwpck_require__(24903);
|
|
102376
102602
|
const claude_code_check_provider_1 = __nccwpck_require__(17985);
|
|
102603
|
+
const command_check_provider_1 = __nccwpck_require__(11878);
|
|
102377
102604
|
/**
|
|
102378
102605
|
* Registry for managing check providers
|
|
102379
102606
|
*/
|
|
@@ -102399,7 +102626,7 @@ class CheckProviderRegistry {
|
|
|
102399
102626
|
registerDefaultProviders() {
|
|
102400
102627
|
// Register all built-in providers
|
|
102401
102628
|
this.register(new ai_check_provider_1.AICheckProvider());
|
|
102402
|
-
this.register(new
|
|
102629
|
+
this.register(new command_check_provider_1.CommandCheckProvider());
|
|
102403
102630
|
this.register(new http_check_provider_1.HttpCheckProvider());
|
|
102404
102631
|
this.register(new http_input_provider_1.HttpInputProvider());
|
|
102405
102632
|
this.register(new http_client_provider_1.HttpClientProvider());
|
|
@@ -103183,6 +103410,244 @@ async function safeImport(moduleName) {
|
|
|
103183
103410
|
}
|
|
103184
103411
|
|
|
103185
103412
|
|
|
103413
|
+
/***/ }),
|
|
103414
|
+
|
|
103415
|
+
/***/ 11878:
|
|
103416
|
+
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
|
103417
|
+
|
|
103418
|
+
"use strict";
|
|
103419
|
+
|
|
103420
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
103421
|
+
if (k2 === undefined) k2 = k;
|
|
103422
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
103423
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
103424
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
103425
|
+
}
|
|
103426
|
+
Object.defineProperty(o, k2, desc);
|
|
103427
|
+
}) : (function(o, m, k, k2) {
|
|
103428
|
+
if (k2 === undefined) k2 = k;
|
|
103429
|
+
o[k2] = m[k];
|
|
103430
|
+
}));
|
|
103431
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
103432
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
103433
|
+
}) : function(o, v) {
|
|
103434
|
+
o["default"] = v;
|
|
103435
|
+
});
|
|
103436
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
103437
|
+
var ownKeys = function(o) {
|
|
103438
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
103439
|
+
var ar = [];
|
|
103440
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
103441
|
+
return ar;
|
|
103442
|
+
};
|
|
103443
|
+
return ownKeys(o);
|
|
103444
|
+
};
|
|
103445
|
+
return function (mod) {
|
|
103446
|
+
if (mod && mod.__esModule) return mod;
|
|
103447
|
+
var result = {};
|
|
103448
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
103449
|
+
__setModuleDefault(result, mod);
|
|
103450
|
+
return result;
|
|
103451
|
+
};
|
|
103452
|
+
})();
|
|
103453
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
103454
|
+
exports.CommandCheckProvider = void 0;
|
|
103455
|
+
const check_provider_interface_1 = __nccwpck_require__(14131);
|
|
103456
|
+
const liquidjs_1 = __nccwpck_require__(48694);
|
|
103457
|
+
/**
|
|
103458
|
+
* Check provider that executes shell commands and captures their output
|
|
103459
|
+
* Supports JSON parsing and integration with forEach functionality
|
|
103460
|
+
*/
|
|
103461
|
+
class CommandCheckProvider extends check_provider_interface_1.CheckProvider {
|
|
103462
|
+
liquid;
|
|
103463
|
+
constructor() {
|
|
103464
|
+
super();
|
|
103465
|
+
this.liquid = new liquidjs_1.Liquid({
|
|
103466
|
+
cache: false,
|
|
103467
|
+
strictFilters: false,
|
|
103468
|
+
strictVariables: false,
|
|
103469
|
+
});
|
|
103470
|
+
}
|
|
103471
|
+
getName() {
|
|
103472
|
+
return 'command';
|
|
103473
|
+
}
|
|
103474
|
+
getDescription() {
|
|
103475
|
+
return 'Execute shell commands and capture output for processing';
|
|
103476
|
+
}
|
|
103477
|
+
async validateConfig(config) {
|
|
103478
|
+
if (!config || typeof config !== 'object') {
|
|
103479
|
+
return false;
|
|
103480
|
+
}
|
|
103481
|
+
const cfg = config;
|
|
103482
|
+
// Must have exec specified
|
|
103483
|
+
if (!cfg.exec || typeof cfg.exec !== 'string') {
|
|
103484
|
+
return false;
|
|
103485
|
+
}
|
|
103486
|
+
return true;
|
|
103487
|
+
}
|
|
103488
|
+
async execute(prInfo, config, dependencyResults) {
|
|
103489
|
+
const command = config.exec;
|
|
103490
|
+
const transform = config.transform;
|
|
103491
|
+
// Prepare template context for Liquid rendering
|
|
103492
|
+
const templateContext = {
|
|
103493
|
+
pr: {
|
|
103494
|
+
number: prInfo.number,
|
|
103495
|
+
title: prInfo.title,
|
|
103496
|
+
author: prInfo.author,
|
|
103497
|
+
branch: prInfo.head,
|
|
103498
|
+
base: prInfo.base,
|
|
103499
|
+
},
|
|
103500
|
+
files: prInfo.files,
|
|
103501
|
+
fileCount: prInfo.files.length,
|
|
103502
|
+
outputs: this.buildOutputContext(dependencyResults),
|
|
103503
|
+
env: this.getSafeEnvironmentVariables(),
|
|
103504
|
+
};
|
|
103505
|
+
try {
|
|
103506
|
+
// Render the command with Liquid templates if needed
|
|
103507
|
+
let renderedCommand = command;
|
|
103508
|
+
if (command.includes('{{') || command.includes('{%')) {
|
|
103509
|
+
renderedCommand = await this.liquid.parseAndRender(command, templateContext);
|
|
103510
|
+
}
|
|
103511
|
+
// Prepare environment variables - convert all to strings
|
|
103512
|
+
const scriptEnv = {};
|
|
103513
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
103514
|
+
if (value !== undefined) {
|
|
103515
|
+
scriptEnv[key] = value;
|
|
103516
|
+
}
|
|
103517
|
+
}
|
|
103518
|
+
if (config.env) {
|
|
103519
|
+
for (const [key, value] of Object.entries(config.env)) {
|
|
103520
|
+
if (value !== undefined && value !== null) {
|
|
103521
|
+
scriptEnv[key] = String(value);
|
|
103522
|
+
}
|
|
103523
|
+
}
|
|
103524
|
+
}
|
|
103525
|
+
// Execute the script using dynamic import to avoid Jest issues
|
|
103526
|
+
const { exec } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(35317)));
|
|
103527
|
+
const { promisify } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(39023)));
|
|
103528
|
+
const execAsync = promisify(exec);
|
|
103529
|
+
const { stdout, stderr } = await execAsync(renderedCommand, {
|
|
103530
|
+
env: scriptEnv,
|
|
103531
|
+
timeout: 60000, // 60 second timeout
|
|
103532
|
+
maxBuffer: 10 * 1024 * 1024, // 10MB buffer
|
|
103533
|
+
});
|
|
103534
|
+
if (stderr && process.env.DEBUG) {
|
|
103535
|
+
console.error(`Command stderr: ${stderr}`);
|
|
103536
|
+
}
|
|
103537
|
+
// Try to parse output as JSON
|
|
103538
|
+
let output = stdout.trim();
|
|
103539
|
+
try {
|
|
103540
|
+
// Attempt to parse as JSON
|
|
103541
|
+
const parsed = JSON.parse(stdout.trim());
|
|
103542
|
+
output = parsed;
|
|
103543
|
+
}
|
|
103544
|
+
catch {
|
|
103545
|
+
// If not JSON, keep as string
|
|
103546
|
+
output = stdout.trim();
|
|
103547
|
+
}
|
|
103548
|
+
// Apply transform if specified
|
|
103549
|
+
let finalOutput = output;
|
|
103550
|
+
if (transform) {
|
|
103551
|
+
try {
|
|
103552
|
+
const transformContext = {
|
|
103553
|
+
...templateContext,
|
|
103554
|
+
output,
|
|
103555
|
+
};
|
|
103556
|
+
const rendered = await this.liquid.parseAndRender(transform, transformContext);
|
|
103557
|
+
// Try to parse the transformed result as JSON
|
|
103558
|
+
try {
|
|
103559
|
+
finalOutput = JSON.parse(rendered.trim());
|
|
103560
|
+
}
|
|
103561
|
+
catch {
|
|
103562
|
+
finalOutput = rendered.trim();
|
|
103563
|
+
}
|
|
103564
|
+
}
|
|
103565
|
+
catch (error) {
|
|
103566
|
+
return {
|
|
103567
|
+
issues: [
|
|
103568
|
+
{
|
|
103569
|
+
file: 'command',
|
|
103570
|
+
line: 0,
|
|
103571
|
+
ruleId: 'command/transform_error',
|
|
103572
|
+
message: `Failed to apply transform: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
103573
|
+
severity: 'error',
|
|
103574
|
+
category: 'logic',
|
|
103575
|
+
},
|
|
103576
|
+
],
|
|
103577
|
+
};
|
|
103578
|
+
}
|
|
103579
|
+
}
|
|
103580
|
+
// Return the output as part of the review summary
|
|
103581
|
+
// The output will be available to dependent checks
|
|
103582
|
+
return {
|
|
103583
|
+
issues: [],
|
|
103584
|
+
output: finalOutput,
|
|
103585
|
+
};
|
|
103586
|
+
}
|
|
103587
|
+
catch (error) {
|
|
103588
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
103589
|
+
return {
|
|
103590
|
+
issues: [
|
|
103591
|
+
{
|
|
103592
|
+
file: 'command',
|
|
103593
|
+
line: 0,
|
|
103594
|
+
ruleId: 'command/execution_error',
|
|
103595
|
+
message: `Command execution failed: ${errorMessage}`,
|
|
103596
|
+
severity: 'error',
|
|
103597
|
+
category: 'logic',
|
|
103598
|
+
},
|
|
103599
|
+
],
|
|
103600
|
+
};
|
|
103601
|
+
}
|
|
103602
|
+
}
|
|
103603
|
+
buildOutputContext(dependencyResults) {
|
|
103604
|
+
if (!dependencyResults) {
|
|
103605
|
+
return {};
|
|
103606
|
+
}
|
|
103607
|
+
const outputs = {};
|
|
103608
|
+
for (const [checkName, result] of dependencyResults) {
|
|
103609
|
+
if (result.output !== undefined) {
|
|
103610
|
+
outputs[checkName] = result.output;
|
|
103611
|
+
}
|
|
103612
|
+
else {
|
|
103613
|
+
outputs[checkName] = {
|
|
103614
|
+
issueCount: result.issues?.length || 0,
|
|
103615
|
+
issues: result.issues || [],
|
|
103616
|
+
};
|
|
103617
|
+
}
|
|
103618
|
+
}
|
|
103619
|
+
return outputs;
|
|
103620
|
+
}
|
|
103621
|
+
getSafeEnvironmentVariables() {
|
|
103622
|
+
const safeVars = {};
|
|
103623
|
+
const allowedPrefixes = ['CI_', 'GITHUB_', 'RUNNER_', 'NODE_', 'npm_', 'PATH', 'HOME', 'USER'];
|
|
103624
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
103625
|
+
if (value !== undefined && allowedPrefixes.some(prefix => key.startsWith(prefix))) {
|
|
103626
|
+
safeVars[key] = value;
|
|
103627
|
+
}
|
|
103628
|
+
}
|
|
103629
|
+
// Add current working directory
|
|
103630
|
+
safeVars['PWD'] = process.cwd();
|
|
103631
|
+
return safeVars;
|
|
103632
|
+
}
|
|
103633
|
+
getSupportedConfigKeys() {
|
|
103634
|
+
return ['type', 'exec', 'transform', 'env', 'depends_on', 'on', 'if', 'group', 'forEach'];
|
|
103635
|
+
}
|
|
103636
|
+
async isAvailable() {
|
|
103637
|
+
// Command provider is always available as long as we can execute commands
|
|
103638
|
+
return true;
|
|
103639
|
+
}
|
|
103640
|
+
getRequirements() {
|
|
103641
|
+
return [
|
|
103642
|
+
'Valid shell command to execute',
|
|
103643
|
+
'Shell environment available',
|
|
103644
|
+
'Optional: Transform template for processing output',
|
|
103645
|
+
];
|
|
103646
|
+
}
|
|
103647
|
+
}
|
|
103648
|
+
exports.CommandCheckProvider = CommandCheckProvider;
|
|
103649
|
+
|
|
103650
|
+
|
|
103186
103651
|
/***/ }),
|
|
103187
103652
|
|
|
103188
103653
|
/***/ 31115:
|
|
@@ -104041,181 +104506,6 @@ class NoopCheckProvider extends check_provider_interface_1.CheckProvider {
|
|
|
104041
104506
|
exports.NoopCheckProvider = NoopCheckProvider;
|
|
104042
104507
|
|
|
104043
104508
|
|
|
104044
|
-
/***/ }),
|
|
104045
|
-
|
|
104046
|
-
/***/ 70477:
|
|
104047
|
-
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
104048
|
-
|
|
104049
|
-
"use strict";
|
|
104050
|
-
|
|
104051
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
104052
|
-
exports.ToolCheckProvider = void 0;
|
|
104053
|
-
const check_provider_interface_1 = __nccwpck_require__(14131);
|
|
104054
|
-
const issue_filter_1 = __nccwpck_require__(36879);
|
|
104055
|
-
const child_process_1 = __nccwpck_require__(35317);
|
|
104056
|
-
const liquidjs_1 = __nccwpck_require__(48694);
|
|
104057
|
-
/**
|
|
104058
|
-
* Check provider that executes external tools and commands with Liquid template support
|
|
104059
|
-
* Supports both simple commands and complex templated execution with stdin
|
|
104060
|
-
*/
|
|
104061
|
-
class ToolCheckProvider extends check_provider_interface_1.CheckProvider {
|
|
104062
|
-
liquid;
|
|
104063
|
-
constructor() {
|
|
104064
|
-
super();
|
|
104065
|
-
this.liquid = new liquidjs_1.Liquid({
|
|
104066
|
-
strictVariables: false,
|
|
104067
|
-
strictFilters: false,
|
|
104068
|
-
});
|
|
104069
|
-
}
|
|
104070
|
-
getName() {
|
|
104071
|
-
return 'tool';
|
|
104072
|
-
}
|
|
104073
|
-
getDescription() {
|
|
104074
|
-
return 'Execute external code analysis tools (ESLint, Prettier, etc.)';
|
|
104075
|
-
}
|
|
104076
|
-
async validateConfig(config) {
|
|
104077
|
-
if (!config || typeof config !== 'object') {
|
|
104078
|
-
return false;
|
|
104079
|
-
}
|
|
104080
|
-
const cfg = config;
|
|
104081
|
-
// Type must be 'tool'
|
|
104082
|
-
if (cfg.type !== 'tool') {
|
|
104083
|
-
return false;
|
|
104084
|
-
}
|
|
104085
|
-
// Must have exec specified for tool execution
|
|
104086
|
-
if (typeof cfg.exec !== 'string' || !cfg.exec) {
|
|
104087
|
-
return false;
|
|
104088
|
-
}
|
|
104089
|
-
return true;
|
|
104090
|
-
}
|
|
104091
|
-
async execute(prInfo, config, _dependencyResults, _sessionInfo) {
|
|
104092
|
-
const execTemplate = config.exec;
|
|
104093
|
-
const stdinTemplate = config.stdin;
|
|
104094
|
-
// Prepare template context
|
|
104095
|
-
const templateContext = {
|
|
104096
|
-
pr: {
|
|
104097
|
-
number: prInfo.number,
|
|
104098
|
-
title: prInfo.title,
|
|
104099
|
-
body: prInfo.body,
|
|
104100
|
-
author: prInfo.author,
|
|
104101
|
-
base: prInfo.base,
|
|
104102
|
-
head: prInfo.head,
|
|
104103
|
-
totalAdditions: prInfo.totalAdditions,
|
|
104104
|
-
totalDeletions: prInfo.totalDeletions,
|
|
104105
|
-
},
|
|
104106
|
-
files: prInfo.files.map(f => ({
|
|
104107
|
-
filename: f.filename,
|
|
104108
|
-
status: f.status,
|
|
104109
|
-
additions: f.additions,
|
|
104110
|
-
deletions: f.deletions,
|
|
104111
|
-
changes: f.changes,
|
|
104112
|
-
patch: f.patch,
|
|
104113
|
-
})),
|
|
104114
|
-
// Add convenience arrays for common use cases
|
|
104115
|
-
filenames: prInfo.files.map(f => f.filename),
|
|
104116
|
-
config: config, // Allow access to config values in templates
|
|
104117
|
-
};
|
|
104118
|
-
// Render the command template
|
|
104119
|
-
const renderedCommand = await this.liquid.parseAndRender(execTemplate, templateContext);
|
|
104120
|
-
// Render stdin if provided
|
|
104121
|
-
let renderedStdin;
|
|
104122
|
-
if (stdinTemplate) {
|
|
104123
|
-
renderedStdin = await this.liquid.parseAndRender(stdinTemplate, templateContext);
|
|
104124
|
-
}
|
|
104125
|
-
// Execute the tool
|
|
104126
|
-
const output = await this.executeCommand(renderedCommand.trim(), renderedStdin);
|
|
104127
|
-
// Parse tool output (this would be customized per tool)
|
|
104128
|
-
const comments = this.parseToolOutput(output, renderedCommand);
|
|
104129
|
-
const issues = comments.map(comment => ({
|
|
104130
|
-
file: comment.file,
|
|
104131
|
-
line: comment.line,
|
|
104132
|
-
endLine: undefined,
|
|
104133
|
-
ruleId: `tool/${comment.category}`,
|
|
104134
|
-
message: comment.message,
|
|
104135
|
-
severity: comment.severity,
|
|
104136
|
-
category: comment.category,
|
|
104137
|
-
suggestion: undefined,
|
|
104138
|
-
replacement: undefined,
|
|
104139
|
-
}));
|
|
104140
|
-
// Apply issue suppression filtering
|
|
104141
|
-
const suppressionEnabled = config.suppressionEnabled !== false;
|
|
104142
|
-
const issueFilter = new issue_filter_1.IssueFilter(suppressionEnabled);
|
|
104143
|
-
const filteredIssues = issueFilter.filterIssues(issues, process.cwd());
|
|
104144
|
-
return {
|
|
104145
|
-
issues: filteredIssues,
|
|
104146
|
-
};
|
|
104147
|
-
}
|
|
104148
|
-
async executeCommand(command, stdin) {
|
|
104149
|
-
return new Promise((resolve, reject) => {
|
|
104150
|
-
// Parse command and arguments (handle quoted arguments)
|
|
104151
|
-
const parts = command.match(/(?:[^\s"]+|"[^"]*")+/g) || [command];
|
|
104152
|
-
const cmd = parts[0];
|
|
104153
|
-
const args = parts.slice(1).map(arg => arg.replace(/^"(.*)"$/, '$1'));
|
|
104154
|
-
const child = (0, child_process_1.spawn)(cmd, args, {
|
|
104155
|
-
shell: false,
|
|
104156
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
104157
|
-
});
|
|
104158
|
-
let output = '';
|
|
104159
|
-
let error = '';
|
|
104160
|
-
child.stdout.on('data', data => {
|
|
104161
|
-
output += data.toString();
|
|
104162
|
-
});
|
|
104163
|
-
child.stderr.on('data', data => {
|
|
104164
|
-
error += data.toString();
|
|
104165
|
-
});
|
|
104166
|
-
// Send stdin data if provided
|
|
104167
|
-
if (stdin) {
|
|
104168
|
-
child.stdin.write(stdin);
|
|
104169
|
-
child.stdin.end();
|
|
104170
|
-
}
|
|
104171
|
-
child.on('close', _code => {
|
|
104172
|
-
// Many tools return non-zero on issues found
|
|
104173
|
-
resolve(output || error);
|
|
104174
|
-
});
|
|
104175
|
-
child.on('error', err => {
|
|
104176
|
-
reject(new Error(`Failed to execute ${cmd}: ${err.message}`));
|
|
104177
|
-
});
|
|
104178
|
-
});
|
|
104179
|
-
}
|
|
104180
|
-
parseToolOutput(output, _command) {
|
|
104181
|
-
const comments = [];
|
|
104182
|
-
// This is a simplified parser - real implementation would handle specific tool formats
|
|
104183
|
-
const lines = output.split('\n');
|
|
104184
|
-
for (const line of lines) {
|
|
104185
|
-
// Example: file.js:10:5: error: Missing semicolon
|
|
104186
|
-
const match = line.match(/^(.+?):(\d+):(\d+):\s*(critical|error|warning|info):\s*(.+)$/);
|
|
104187
|
-
if (match) {
|
|
104188
|
-
const severity = match[4];
|
|
104189
|
-
comments.push({
|
|
104190
|
-
file: match[1],
|
|
104191
|
-
line: parseInt(match[2]),
|
|
104192
|
-
message: match[5],
|
|
104193
|
-
severity: severity,
|
|
104194
|
-
category: 'style',
|
|
104195
|
-
});
|
|
104196
|
-
}
|
|
104197
|
-
}
|
|
104198
|
-
return comments;
|
|
104199
|
-
}
|
|
104200
|
-
getSupportedConfigKeys() {
|
|
104201
|
-
return ['type', 'exec', 'command', 'stdin', 'timeout', 'workingDirectory'];
|
|
104202
|
-
}
|
|
104203
|
-
async isAvailable() {
|
|
104204
|
-
// Check if common tools are available
|
|
104205
|
-
// In a real implementation, this would check for specific tools based on config
|
|
104206
|
-
return true;
|
|
104207
|
-
}
|
|
104208
|
-
getRequirements() {
|
|
104209
|
-
return [
|
|
104210
|
-
'External tool must be installed (e.g., eslint, prettier)',
|
|
104211
|
-
'Tool must be accessible in PATH',
|
|
104212
|
-
'Appropriate configuration files for the tool',
|
|
104213
|
-
];
|
|
104214
|
-
}
|
|
104215
|
-
}
|
|
104216
|
-
exports.ToolCheckProvider = ToolCheckProvider;
|
|
104217
|
-
|
|
104218
|
-
|
|
104219
104509
|
/***/ }),
|
|
104220
104510
|
|
|
104221
104511
|
/***/ 532:
|
|
@@ -125696,7 +125986,7 @@ async function delegate({ task, timeout = 300, debug = false, currentIteration =
|
|
|
125696
125986
|
console.error(`[DELEGATE] Using binary at: ${binaryPath}`);
|
|
125697
125987
|
console.error(`[DELEGATE] Command args: ${args.join(" ")}`);
|
|
125698
125988
|
}
|
|
125699
|
-
return new Promise((
|
|
125989
|
+
return new Promise((resolve2, reject) => {
|
|
125700
125990
|
const delegationSpan = tracer ? tracer.createDelegationSpan(sessionId, task) : null;
|
|
125701
125991
|
const process2 = (0, import_child_process5.spawn)(binaryPath, args, {
|
|
125702
125992
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -125761,7 +126051,7 @@ async function delegate({ task, timeout = 300, debug = false, currentIteration =
|
|
|
125761
126051
|
delegationSpan.end();
|
|
125762
126052
|
}
|
|
125763
126053
|
}
|
|
125764
|
-
|
|
126054
|
+
resolve2(response);
|
|
125765
126055
|
} else {
|
|
125766
126056
|
const errorMessage = stderr.trim() || `Delegate process failed with exit code ${code}`;
|
|
125767
126057
|
if (debug) {
|
|
@@ -126193,13 +126483,19 @@ For GitHub-compatible mermaid diagrams, avoid single quotes and parentheses in n
|
|
|
126193
126483
|
|
|
126194
126484
|
**Rules:**
|
|
126195
126485
|
- NO single quotes in any node labels: 'text' \u2192 "text" or text
|
|
126196
|
-
- NO parentheses in square brackets: [Text (detail)] \u2192 [Text - detail]
|
|
126486
|
+
- NO parentheses in square brackets: [Text (detail)] \u2192 [Text - detail]
|
|
126197
126487
|
- NO complex expressions in diamonds: {a && b} \u2192 {condition}
|
|
126488
|
+
- NO HTML tags in node labels: [<pre>code</pre>] \u2192 ["code block"] or [Code Block]
|
|
126198
126489
|
- USE single quotes for styles and classes: classDef highlight fill:'#ff9999'
|
|
126490
|
+
- CRITICAL: When using quotes in node labels, place them INSIDE the brackets: ["quoted text"], NOT [quoted text"]
|
|
126199
126491
|
|
|
126200
126492
|
**Examples:**
|
|
126201
126493
|
- \u2705 [Load Config] ["Run command"] {Valid?}
|
|
126494
|
+
- \u2705 ["depends_on: [generate-items]"] (correct quote placement)
|
|
126495
|
+
- \u2705 ["Code Block"] (clean text instead of HTML)
|
|
126202
126496
|
- \u274C [Load (config)] [Run 'command'] {isValid('x')}
|
|
126497
|
+
- \u274C [depends_on: [generate-items"] (incorrect quote placement - quote ends inside bracket)
|
|
126498
|
+
- \u274C [<pre>depends_on: [generate-items]</pre>] (HTML tags in node labels)
|
|
126203
126499
|
|
|
126204
126500
|
**Diagram Type Selection:**
|
|
126205
126501
|
|
|
@@ -127015,7 +127311,7 @@ function createMockProvider() {
|
|
|
127015
127311
|
provider: "mock",
|
|
127016
127312
|
// Mock the doGenerate method used by Vercel AI SDK
|
|
127017
127313
|
doGenerate: async ({ messages, tools: tools2 }) => {
|
|
127018
|
-
await new Promise((
|
|
127314
|
+
await new Promise((resolve2) => setTimeout(resolve2, 10));
|
|
127019
127315
|
return {
|
|
127020
127316
|
text: "This is a mock response for testing",
|
|
127021
127317
|
toolCalls: [],
|
|
@@ -128840,7 +129136,7 @@ var ProbeAgent_exports = {};
|
|
|
128840
129136
|
__export(ProbeAgent_exports, {
|
|
128841
129137
|
ProbeAgent: () => ProbeAgent
|
|
128842
129138
|
});
|
|
128843
|
-
var import_anthropic, import_openai, import_google, import_ai2, import_crypto5, import_events2, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, ProbeAgent;
|
|
129139
|
+
var import_anthropic, import_openai, import_google, import_ai2, import_crypto5, import_events2, import_fs5, import_promises, import_path7, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, SUPPORTED_IMAGE_EXTENSIONS, MAX_IMAGE_FILE_SIZE, ProbeAgent;
|
|
128844
129140
|
var init_ProbeAgent = __esm({
|
|
128845
129141
|
"src/agent/ProbeAgent.js"() {
|
|
128846
129142
|
"use strict";
|
|
@@ -128850,6 +129146,9 @@ var init_ProbeAgent = __esm({
|
|
|
128850
129146
|
import_ai2 = __nccwpck_require__(86619);
|
|
128851
129147
|
import_crypto5 = __nccwpck_require__(76982);
|
|
128852
129148
|
import_events2 = __nccwpck_require__(24434);
|
|
129149
|
+
import_fs5 = __nccwpck_require__(79896);
|
|
129150
|
+
import_promises = __nccwpck_require__(91943);
|
|
129151
|
+
import_path7 = __nccwpck_require__(16928);
|
|
128853
129152
|
init_tokenCounter();
|
|
128854
129153
|
init_tools2();
|
|
128855
129154
|
init_common();
|
|
@@ -128860,6 +129159,8 @@ var init_ProbeAgent = __esm({
|
|
|
128860
129159
|
init_mcp();
|
|
128861
129160
|
MAX_TOOL_ITERATIONS = parseInt(process.env.MAX_TOOL_ITERATIONS || "30", 10);
|
|
128862
129161
|
MAX_HISTORY_MESSAGES = 100;
|
|
129162
|
+
SUPPORTED_IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "webp", "gif", "bmp", "svg"];
|
|
129163
|
+
MAX_IMAGE_FILE_SIZE = 20 * 1024 * 1024;
|
|
128863
129164
|
ProbeAgent = class {
|
|
128864
129165
|
/**
|
|
128865
129166
|
* Create a new ProbeAgent instance
|
|
@@ -128903,6 +129204,8 @@ var init_ProbeAgent = __esm({
|
|
|
128903
129204
|
}
|
|
128904
129205
|
this.initializeTools();
|
|
128905
129206
|
this.history = [];
|
|
129207
|
+
this.pendingImages = /* @__PURE__ */ new Map();
|
|
129208
|
+
this.currentImages = [];
|
|
128906
129209
|
this.events = new import_events2.EventEmitter();
|
|
128907
129210
|
this.enableMcp = !!options.enableMcp || process.env.ENABLE_MCP === "1";
|
|
128908
129211
|
this.mcpConfigPath = options.mcpConfigPath || null;
|
|
@@ -129028,6 +129331,175 @@ var init_ProbeAgent = __esm({
|
|
|
129028
129331
|
console.log(`Using Google API with model: ${this.model}${apiUrl ? ` (URL: ${apiUrl})` : ""}`);
|
|
129029
129332
|
}
|
|
129030
129333
|
}
|
|
129334
|
+
/**
|
|
129335
|
+
* Process assistant response content and detect/load image references
|
|
129336
|
+
* @param {string} content - The assistant's response content
|
|
129337
|
+
* @returns {Promise<void>}
|
|
129338
|
+
*/
|
|
129339
|
+
async processImageReferences(content) {
|
|
129340
|
+
if (!content) return;
|
|
129341
|
+
const extensionsPattern = `(?:${SUPPORTED_IMAGE_EXTENSIONS.join("|")})`;
|
|
129342
|
+
const imagePatterns = [
|
|
129343
|
+
// Direct file path mentions: "./screenshot.png", "/path/to/image.jpg", etc.
|
|
129344
|
+
new RegExp(`(?:\\.?\\.\\/)?[^\\s"'<>\\[\\]]+\\.${extensionsPattern}(?!\\w)`, "gi"),
|
|
129345
|
+
// Contextual mentions: "look at image.png", "the file screenshot.jpg shows"
|
|
129346
|
+
new RegExp(`(?:image|file|screenshot|diagram|photo|picture|graphic)\\s*:?\\s*([^\\s"'<>\\[\\]]+\\.${extensionsPattern})(?!\\w)`, "gi"),
|
|
129347
|
+
// Tool result mentions: often contain file paths
|
|
129348
|
+
new RegExp(`(?:found|saved|created|generated).*?([^\\s"'<>\\[\\]]+\\.${extensionsPattern})(?!\\w)`, "gi")
|
|
129349
|
+
];
|
|
129350
|
+
const foundPaths = /* @__PURE__ */ new Set();
|
|
129351
|
+
for (const pattern of imagePatterns) {
|
|
129352
|
+
let match;
|
|
129353
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
129354
|
+
const imagePath = match[1] || match[0];
|
|
129355
|
+
if (imagePath && imagePath.length > 0) {
|
|
129356
|
+
foundPaths.add(imagePath.trim());
|
|
129357
|
+
}
|
|
129358
|
+
}
|
|
129359
|
+
}
|
|
129360
|
+
if (foundPaths.size === 0) return;
|
|
129361
|
+
if (this.debug) {
|
|
129362
|
+
console.log(`[DEBUG] Found ${foundPaths.size} potential image references:`, Array.from(foundPaths));
|
|
129363
|
+
}
|
|
129364
|
+
for (const imagePath of foundPaths) {
|
|
129365
|
+
await this.loadImageIfValid(imagePath);
|
|
129366
|
+
}
|
|
129367
|
+
}
|
|
129368
|
+
/**
|
|
129369
|
+
* Load and cache an image if it's valid and accessible
|
|
129370
|
+
* @param {string} imagePath - Path to the image file
|
|
129371
|
+
* @returns {Promise<boolean>} - True if image was loaded successfully
|
|
129372
|
+
*/
|
|
129373
|
+
async loadImageIfValid(imagePath) {
|
|
129374
|
+
try {
|
|
129375
|
+
if (this.pendingImages.has(imagePath)) {
|
|
129376
|
+
if (this.debug) {
|
|
129377
|
+
console.log(`[DEBUG] Image already loaded: ${imagePath}`);
|
|
129378
|
+
}
|
|
129379
|
+
return true;
|
|
129380
|
+
}
|
|
129381
|
+
const allowedDirs = this.allowedFolders && this.allowedFolders.length > 0 ? this.allowedFolders : [process.cwd()];
|
|
129382
|
+
let absolutePath;
|
|
129383
|
+
let isPathAllowed = false;
|
|
129384
|
+
if ((0, import_path7.isAbsolute)(imagePath)) {
|
|
129385
|
+
absolutePath = imagePath;
|
|
129386
|
+
isPathAllowed = allowedDirs.some((dir) => absolutePath.startsWith((0, import_path7.resolve)(dir)));
|
|
129387
|
+
} else {
|
|
129388
|
+
for (const dir of allowedDirs) {
|
|
129389
|
+
const resolvedPath = (0, import_path7.resolve)(dir, imagePath);
|
|
129390
|
+
if (resolvedPath.startsWith((0, import_path7.resolve)(dir))) {
|
|
129391
|
+
absolutePath = resolvedPath;
|
|
129392
|
+
isPathAllowed = true;
|
|
129393
|
+
break;
|
|
129394
|
+
}
|
|
129395
|
+
}
|
|
129396
|
+
}
|
|
129397
|
+
if (!isPathAllowed) {
|
|
129398
|
+
if (this.debug) {
|
|
129399
|
+
console.log(`[DEBUG] Image path outside allowed directories: ${imagePath}`);
|
|
129400
|
+
}
|
|
129401
|
+
return false;
|
|
129402
|
+
}
|
|
129403
|
+
let fileStats;
|
|
129404
|
+
try {
|
|
129405
|
+
fileStats = await (0, import_promises.stat)(absolutePath);
|
|
129406
|
+
} catch (error) {
|
|
129407
|
+
if (this.debug) {
|
|
129408
|
+
console.log(`[DEBUG] Image file not found: ${absolutePath}`);
|
|
129409
|
+
}
|
|
129410
|
+
return false;
|
|
129411
|
+
}
|
|
129412
|
+
if (fileStats.size > MAX_IMAGE_FILE_SIZE) {
|
|
129413
|
+
if (this.debug) {
|
|
129414
|
+
console.log(`[DEBUG] Image file too large: ${absolutePath} (${fileStats.size} bytes, max: ${MAX_IMAGE_FILE_SIZE})`);
|
|
129415
|
+
}
|
|
129416
|
+
return false;
|
|
129417
|
+
}
|
|
129418
|
+
const extension = absolutePath.toLowerCase().split(".").pop();
|
|
129419
|
+
if (!SUPPORTED_IMAGE_EXTENSIONS.includes(extension)) {
|
|
129420
|
+
if (this.debug) {
|
|
129421
|
+
console.log(`[DEBUG] Unsupported image format: ${extension}`);
|
|
129422
|
+
}
|
|
129423
|
+
return false;
|
|
129424
|
+
}
|
|
129425
|
+
const mimeTypes = {
|
|
129426
|
+
"png": "image/png",
|
|
129427
|
+
"jpg": "image/jpeg",
|
|
129428
|
+
"jpeg": "image/jpeg",
|
|
129429
|
+
"webp": "image/webp",
|
|
129430
|
+
"gif": "image/gif",
|
|
129431
|
+
"bmp": "image/bmp",
|
|
129432
|
+
"svg": "image/svg+xml"
|
|
129433
|
+
};
|
|
129434
|
+
const mimeType = mimeTypes[extension];
|
|
129435
|
+
const fileBuffer = await (0, import_promises.readFile)(absolutePath);
|
|
129436
|
+
const base64Data = fileBuffer.toString("base64");
|
|
129437
|
+
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
129438
|
+
this.pendingImages.set(imagePath, dataUrl);
|
|
129439
|
+
if (this.debug) {
|
|
129440
|
+
console.log(`[DEBUG] Successfully loaded image: ${imagePath} (${fileBuffer.length} bytes)`);
|
|
129441
|
+
}
|
|
129442
|
+
return true;
|
|
129443
|
+
} catch (error) {
|
|
129444
|
+
if (this.debug) {
|
|
129445
|
+
console.log(`[DEBUG] Failed to load image ${imagePath}: ${error.message}`);
|
|
129446
|
+
}
|
|
129447
|
+
return false;
|
|
129448
|
+
}
|
|
129449
|
+
}
|
|
129450
|
+
/**
|
|
129451
|
+
* Get all currently loaded images as an array for AI model consumption
|
|
129452
|
+
* @returns {Array<string>} - Array of base64 data URLs
|
|
129453
|
+
*/
|
|
129454
|
+
getCurrentImages() {
|
|
129455
|
+
return Array.from(this.pendingImages.values());
|
|
129456
|
+
}
|
|
129457
|
+
/**
|
|
129458
|
+
* Clear loaded images (useful for new conversations)
|
|
129459
|
+
*/
|
|
129460
|
+
clearLoadedImages() {
|
|
129461
|
+
this.pendingImages.clear();
|
|
129462
|
+
this.currentImages = [];
|
|
129463
|
+
if (this.debug) {
|
|
129464
|
+
console.log("[DEBUG] Cleared all loaded images");
|
|
129465
|
+
}
|
|
129466
|
+
}
|
|
129467
|
+
/**
|
|
129468
|
+
* Prepare messages for AI consumption, adding images to the latest user message if available
|
|
129469
|
+
* @param {Array} messages - Current conversation messages
|
|
129470
|
+
* @returns {Array} - Messages formatted for AI SDK with potential image content
|
|
129471
|
+
*/
|
|
129472
|
+
prepareMessagesWithImages(messages) {
|
|
129473
|
+
const loadedImages = this.getCurrentImages();
|
|
129474
|
+
if (loadedImages.length === 0) {
|
|
129475
|
+
return messages;
|
|
129476
|
+
}
|
|
129477
|
+
const messagesWithImages = [...messages];
|
|
129478
|
+
const lastUserMessageIndex = messagesWithImages.map((m) => m.role).lastIndexOf("user");
|
|
129479
|
+
if (lastUserMessageIndex === -1) {
|
|
129480
|
+
if (this.debug) {
|
|
129481
|
+
console.log("[DEBUG] No user messages found to attach images to");
|
|
129482
|
+
}
|
|
129483
|
+
return messages;
|
|
129484
|
+
}
|
|
129485
|
+
const lastUserMessage = messagesWithImages[lastUserMessageIndex];
|
|
129486
|
+
if (typeof lastUserMessage.content === "string") {
|
|
129487
|
+
messagesWithImages[lastUserMessageIndex] = {
|
|
129488
|
+
...lastUserMessage,
|
|
129489
|
+
content: [
|
|
129490
|
+
{ type: "text", text: lastUserMessage.content },
|
|
129491
|
+
...loadedImages.map((imageData) => ({
|
|
129492
|
+
type: "image",
|
|
129493
|
+
image: imageData
|
|
129494
|
+
}))
|
|
129495
|
+
]
|
|
129496
|
+
};
|
|
129497
|
+
if (this.debug) {
|
|
129498
|
+
console.log(`[DEBUG] Added ${loadedImages.length} images to the latest user message`);
|
|
129499
|
+
}
|
|
129500
|
+
}
|
|
129501
|
+
return messagesWithImages;
|
|
129502
|
+
}
|
|
129031
129503
|
/**
|
|
129032
129504
|
* Initialize mock model for testing
|
|
129033
129505
|
*/
|
|
@@ -129127,7 +129599,7 @@ Examples:
|
|
|
129127
129599
|
</extract>
|
|
129128
129600
|
|
|
129129
129601
|
<attempt_completion>
|
|
129130
|
-
|
|
129602
|
+
The configuration is loaded from src/config.js lines 15-25 which contains the database settings.
|
|
129131
129603
|
</attempt_completion>
|
|
129132
129604
|
|
|
129133
129605
|
# Special Case: Quick Completion
|
|
@@ -129154,7 +129626,7 @@ I need to find code related to error handling in the search module. The most app
|
|
|
129154
129626
|
6. Wait for the tool execution result, which will be provided in the next message (within a <tool_result> block).
|
|
129155
129627
|
7. Analyze the tool result and decide the next step. If more tool calls are needed, repeat steps 2-6.
|
|
129156
129628
|
8. If the task is fully complete and all previous steps were successful, use the \`<attempt_completion>\` tool to provide the final answer. This is the ONLY way to finish the task.
|
|
129157
|
-
9. If you cannot proceed (e.g., missing information, invalid request), explain the issue clearly
|
|
129629
|
+
9. If you cannot proceed (e.g., missing information, invalid request), use \`<attempt_completion>\` to explain the issue clearly with an appropriate message directly inside the tags.
|
|
129158
129630
|
10. If your previous response was already correct and complete, you may use \`<attempt_complete>\` as a shorthand.
|
|
129159
129631
|
|
|
129160
129632
|
Available Tools:
|
|
@@ -129409,9 +129881,10 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
129409
129881
|
let assistantResponseContent = "";
|
|
129410
129882
|
try {
|
|
129411
129883
|
const executeAIRequest = async () => {
|
|
129884
|
+
const messagesForAI = this.prepareMessagesWithImages(currentMessages);
|
|
129412
129885
|
const result = await (0, import_ai2.streamText)({
|
|
129413
129886
|
model: this.provider(this.model),
|
|
129414
|
-
messages:
|
|
129887
|
+
messages: messagesForAI,
|
|
129415
129888
|
maxTokens: maxResponseTokens,
|
|
129416
129889
|
temperature: 0.3
|
|
129417
129890
|
});
|
|
@@ -129445,6 +129918,9 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
129445
129918
|
const assistantPreview = createMessagePreview(assistantResponseContent);
|
|
129446
129919
|
console.log(`[DEBUG] Assistant response (${assistantResponseContent.length} chars): ${assistantPreview}`);
|
|
129447
129920
|
}
|
|
129921
|
+
if (assistantResponseContent) {
|
|
129922
|
+
await this.processImageReferences(assistantResponseContent);
|
|
129923
|
+
}
|
|
129448
129924
|
const validTools = [
|
|
129449
129925
|
"search",
|
|
129450
129926
|
"query",
|
|
@@ -129569,12 +130045,17 @@ ${toolResultContent}
|
|
|
129569
130045
|
throw toolError;
|
|
129570
130046
|
}
|
|
129571
130047
|
currentMessages.push({ role: "assistant", content: assistantResponseContent });
|
|
130048
|
+
const toolResultContent = typeof toolResult === "string" ? toolResult : JSON.stringify(toolResult, null, 2);
|
|
130049
|
+
const toolResultMessage = `<tool_result>
|
|
130050
|
+
${toolResultContent}
|
|
130051
|
+
</tool_result>`;
|
|
129572
130052
|
currentMessages.push({
|
|
129573
130053
|
role: "user",
|
|
129574
|
-
content:
|
|
129575
|
-
${typeof toolResult === "string" ? toolResult : JSON.stringify(toolResult, null, 2)}
|
|
129576
|
-
</tool_result>`
|
|
130054
|
+
content: toolResultMessage
|
|
129577
130055
|
});
|
|
130056
|
+
if (toolResultContent) {
|
|
130057
|
+
await this.processImageReferences(toolResultContent);
|
|
130058
|
+
}
|
|
129578
130059
|
if (this.debug) {
|
|
129579
130060
|
console.log(`[DEBUG] Tool ${toolName} executed successfully. Result length: ${typeof toolResult === "string" ? toolResult.length : JSON.stringify(toolResult).length}`);
|
|
129580
130061
|
}
|
|
@@ -130072,12 +130553,12 @@ function initializeSimpleTelemetryFromOptions(options) {
|
|
|
130072
130553
|
});
|
|
130073
130554
|
return telemetry;
|
|
130074
130555
|
}
|
|
130075
|
-
var
|
|
130556
|
+
var import_fs6, import_path8, SimpleTelemetry, SimpleAppTracer;
|
|
130076
130557
|
var init_simpleTelemetry = __esm({
|
|
130077
130558
|
"src/agent/simpleTelemetry.js"() {
|
|
130078
130559
|
"use strict";
|
|
130079
|
-
|
|
130080
|
-
|
|
130560
|
+
import_fs6 = __nccwpck_require__(79896);
|
|
130561
|
+
import_path8 = __nccwpck_require__(16928);
|
|
130081
130562
|
SimpleTelemetry = class {
|
|
130082
130563
|
constructor(options = {}) {
|
|
130083
130564
|
this.serviceName = options.serviceName || "probe-agent";
|
|
@@ -130091,11 +130572,11 @@ var init_simpleTelemetry = __esm({
|
|
|
130091
130572
|
}
|
|
130092
130573
|
initializeFileExporter() {
|
|
130093
130574
|
try {
|
|
130094
|
-
const dir = (0,
|
|
130095
|
-
if (!(0,
|
|
130096
|
-
(0,
|
|
130575
|
+
const dir = (0, import_path8.dirname)(this.filePath);
|
|
130576
|
+
if (!(0, import_fs6.existsSync)(dir)) {
|
|
130577
|
+
(0, import_fs6.mkdirSync)(dir, { recursive: true });
|
|
130097
130578
|
}
|
|
130098
|
-
this.stream = (0,
|
|
130579
|
+
this.stream = (0, import_fs6.createWriteStream)(this.filePath, { flags: "a" });
|
|
130099
130580
|
this.stream.on("error", (error) => {
|
|
130100
130581
|
console.error(`[SimpleTelemetry] Stream error: ${error.message}`);
|
|
130101
130582
|
});
|
|
@@ -130156,20 +130637,20 @@ var init_simpleTelemetry = __esm({
|
|
|
130156
130637
|
}
|
|
130157
130638
|
async flush() {
|
|
130158
130639
|
if (this.stream) {
|
|
130159
|
-
return new Promise((
|
|
130160
|
-
this.stream.once("drain",
|
|
130640
|
+
return new Promise((resolve2) => {
|
|
130641
|
+
this.stream.once("drain", resolve2);
|
|
130161
130642
|
if (!this.stream.writableNeedDrain) {
|
|
130162
|
-
|
|
130643
|
+
resolve2();
|
|
130163
130644
|
}
|
|
130164
130645
|
});
|
|
130165
130646
|
}
|
|
130166
130647
|
}
|
|
130167
130648
|
async shutdown() {
|
|
130168
130649
|
if (this.stream) {
|
|
130169
|
-
return new Promise((
|
|
130650
|
+
return new Promise((resolve2) => {
|
|
130170
130651
|
this.stream.end(() => {
|
|
130171
130652
|
console.log(`[SimpleTelemetry] File stream closed: ${this.filePath}`);
|
|
130172
|
-
|
|
130653
|
+
resolve2();
|
|
130173
130654
|
});
|
|
130174
130655
|
});
|
|
130175
130656
|
}
|