@probelabs/visor 0.1.33 → 0.1.35

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/dist/index.js CHANGED
@@ -80893,45 +80893,168 @@ class AIReviewService {
80893
80893
  */
80894
80894
  async buildCustomPrompt(prInfo, customInstructions, _schema) {
80895
80895
  const prContext = this.formatPRContext(prInfo);
80896
+ const isIssue = prInfo.isIssue === true;
80897
+ if (isIssue) {
80898
+ // Issue context - no code analysis needed
80899
+ return `<review_request>
80900
+ <instructions>
80901
+ ${customInstructions}
80902
+ </instructions>
80903
+
80904
+ <context>
80905
+ ${prContext}
80906
+ </context>
80907
+
80908
+ <rules>
80909
+ <rule>Understand the issue context and requirements from the XML data structure</rule>
80910
+ <rule>Provide helpful, actionable guidance based on the issue details</rule>
80911
+ <rule>Be constructive and supportive in your analysis</rule>
80912
+ <rule>Consider project conventions and patterns when making recommendations</rule>
80913
+ <rule>Suggest practical solutions or next steps that address the specific concern</rule>
80914
+ <rule>Focus on addressing the specific concern raised in the issue</rule>
80915
+ <rule>Reference relevant XML elements like metadata, description, labels, assignees when providing context</rule>
80916
+ </rules>
80917
+ </review_request>`;
80918
+ }
80919
+ // PR context - structured XML format
80896
80920
  const analysisType = prInfo.isIncremental ? 'INCREMENTAL' : 'FULL';
80897
- return `You are a senior code reviewer.
80921
+ return `<review_request>
80922
+ <analysis_type>${analysisType}</analysis_type>
80898
80923
 
80899
- ANALYSIS TYPE: ${analysisType}
80900
- ${analysisType === 'INCREMENTAL'
80901
- ? '- You are analyzing a NEW COMMIT added to an existing PR. Focus on the <commit_diff> section for changes made in this specific commit.'
80902
- : '- You are analyzing the COMPLETE PR. Review all changes in the <full_diff> section.'}
80924
+ <analysis_focus>
80925
+ ${analysisType === 'INCREMENTAL'
80926
+ ? 'You are analyzing a NEW COMMIT added to an existing PR. Focus on the changes in the commit_diff section for this specific commit.'
80927
+ : 'You are analyzing the COMPLETE PR. Review all changes in the full_diff section.'}
80928
+ </analysis_focus>
80903
80929
 
80904
- REVIEW INSTRUCTIONS:
80930
+ <instructions>
80905
80931
  ${customInstructions}
80932
+ </instructions>
80906
80933
 
80907
- Analyze the following structured pull request data:
80908
-
80934
+ <context>
80909
80935
  ${prContext}
80910
-
80911
- XML Data Structure Guide:
80912
- - <pull_request>: Root element containing all PR information
80913
- - <metadata>: PR metadata (number, title, author, branches, statistics)
80914
- - <description>: PR description text if provided
80915
- - <full_diff>: Complete unified diff of all changes (for FULL analysis)
80916
- - <commit_diff>: Diff of only the latest commit (for INCREMENTAL analysis)
80917
- - <files_summary>: List of all files changed with statistics
80918
-
80919
- IMPORTANT RULES:
80920
- 1. Only analyze code that appears with + (additions) or - (deletions) in the diff
80921
- 2. Ignore unchanged code unless it's directly relevant to understanding a change
80922
- 3. Line numbers in your response should match the actual file line numbers
80923
- 4. Focus on real issues, not nitpicks
80924
- 5. Provide actionable, specific feedback
80925
- 6. For INCREMENTAL analysis, ONLY review changes in <commit_diff>
80926
- 7. For FULL analysis, review all changes in <full_diff>`;
80936
+ </context>
80937
+
80938
+ <rules>
80939
+ <rule>Only analyze code that appears with + (additions) or - (deletions) in the diff sections</rule>
80940
+ <rule>Ignore unchanged code unless directly relevant to understanding a change</rule>
80941
+ <rule>Line numbers in your response should match actual file line numbers from the diff</rule>
80942
+ <rule>Focus on real issues, not nitpicks or cosmetic concerns</rule>
80943
+ <rule>Provide actionable, specific feedback with clear remediation steps</rule>
80944
+ <rule>For INCREMENTAL analysis, ONLY review changes in commit_diff section</rule>
80945
+ <rule>For FULL analysis, review all changes in full_diff section</rule>
80946
+ <rule>Reference specific XML elements like files_summary, metadata when providing context</rule>
80947
+ </rules>
80948
+ </review_request>`;
80927
80949
  }
80928
80950
  // REMOVED: Built-in prompts - only use custom prompts from .visor.yaml
80929
80951
  // REMOVED: getFocusInstructions - only use custom prompts from .visor.yaml
80930
80952
  /**
80931
- * Format PR context for the AI using XML structure
80953
+ * Format PR or Issue context for the AI using XML structure
80932
80954
  */
80933
80955
  formatPRContext(prInfo) {
80956
+ // Check if this is an issue (not a PR)
80957
+ const isIssue = prInfo.isIssue === true;
80958
+ if (isIssue) {
80959
+ // Format as issue context
80960
+ let context = `<issue>
80961
+ <!-- Core issue metadata including identification, status, and timeline information -->
80962
+ <metadata>
80963
+ <number>${prInfo.number}</number>
80964
+ <title>${this.escapeXml(prInfo.title)}</title>
80965
+ <author>${prInfo.author}</author>
80966
+ <state>${prInfo.eventContext?.issue?.state || 'open'}</state>
80967
+ <created_at>${prInfo.eventContext?.issue?.created_at || ''}</created_at>
80968
+ <updated_at>${prInfo.eventContext?.issue?.updated_at || ''}</updated_at>
80969
+ <comments_count>${prInfo.eventContext?.issue?.comments || 0}</comments_count>
80970
+ </metadata>`;
80971
+ // Add issue body/description if available
80972
+ if (prInfo.body) {
80973
+ context += `
80974
+ <!-- Full issue description and body text provided by the issue author -->
80975
+ <description>
80976
+ ${this.escapeXml(prInfo.body)}
80977
+ </description>`;
80978
+ }
80979
+ // Add labels if available
80980
+ const labels = prInfo.eventContext?.issue?.labels;
80981
+ if (labels && labels.length > 0) {
80982
+ context += `
80983
+ <!-- Applied labels for issue categorization and organization -->
80984
+ <labels>`;
80985
+ labels.forEach((label) => {
80986
+ context += `
80987
+ <label>${this.escapeXml(label.name || label)}</label>`;
80988
+ });
80989
+ context += `
80990
+ </labels>`;
80991
+ }
80992
+ // Add assignees if available
80993
+ const assignees = prInfo.eventContext?.issue?.assignees;
80994
+ if (assignees && assignees.length > 0) {
80995
+ context += `
80996
+ <!-- Users assigned to work on this issue -->
80997
+ <assignees>`;
80998
+ assignees.forEach((assignee) => {
80999
+ context += `
81000
+ <assignee>${this.escapeXml(assignee.login || assignee)}</assignee>`;
81001
+ });
81002
+ context += `
81003
+ </assignees>`;
81004
+ }
81005
+ // Add milestone if available
81006
+ const milestone = prInfo.eventContext?.issue?.milestone;
81007
+ if (milestone) {
81008
+ context += `
81009
+ <!-- Associated project milestone information -->
81010
+ <milestone>
81011
+ <title>${this.escapeXml(milestone.title || '')}</title>
81012
+ <state>${milestone.state || 'open'}</state>
81013
+ <due_on>${milestone.due_on || ''}</due_on>
81014
+ </milestone>`;
81015
+ }
81016
+ // Add current/triggering comment if this is a comment event
81017
+ const triggeringComment = prInfo.eventContext?.comment;
81018
+ if (triggeringComment) {
81019
+ context += `
81020
+ <!-- The comment that triggered this analysis -->
81021
+ <triggering_comment>
81022
+ <author>${this.escapeXml(triggeringComment.user?.login || 'unknown')}</author>
81023
+ <created_at>${triggeringComment.created_at || ''}</created_at>
81024
+ <body>${this.escapeXml(triggeringComment.body || '')}</body>
81025
+ </triggering_comment>`;
81026
+ }
81027
+ // Add comment history (excluding the current comment if it exists)
81028
+ const issueComments = prInfo.comments;
81029
+ if (issueComments && issueComments.length > 0) {
81030
+ // Filter out the triggering comment from history if present
81031
+ const historicalComments = triggeringComment
81032
+ ? issueComments.filter((c) => c.id !== triggeringComment.id)
81033
+ : issueComments;
81034
+ if (historicalComments.length > 0) {
81035
+ context += `
81036
+ <!-- Previous comments in chronological order (excluding triggering comment) -->
81037
+ <comment_history>`;
81038
+ historicalComments.forEach((comment) => {
81039
+ context += `
81040
+ <comment>
81041
+ <author>${this.escapeXml(comment.author || 'unknown')}</author>
81042
+ <created_at>${comment.createdAt || ''}</created_at>
81043
+ <body>${this.escapeXml(comment.body || '')}</body>
81044
+ </comment>`;
81045
+ });
81046
+ context += `
81047
+ </comment_history>`;
81048
+ }
81049
+ }
81050
+ // Close the issue tag
81051
+ context += `
81052
+ </issue>`;
81053
+ return context;
81054
+ }
81055
+ // Original PR context formatting
80934
81056
  let context = `<pull_request>
81057
+ <!-- Core pull request metadata including identification, branches, and change statistics -->
80935
81058
  <metadata>
80936
81059
  <number>${prInfo.number}</number>
80937
81060
  <title>${this.escapeXml(prInfo.title)}</title>
@@ -80945,6 +81068,7 @@ IMPORTANT RULES:
80945
81068
  // Add PR description if available
80946
81069
  if (prInfo.body) {
80947
81070
  context += `
81071
+ <!-- Full pull request description provided by the author -->
80948
81072
  <description>
80949
81073
  ${this.escapeXml(prInfo.body)}
80950
81074
  </description>`;
@@ -80952,6 +81076,7 @@ ${this.escapeXml(prInfo.body)}
80952
81076
  // Add full diff if available (for complete PR review)
80953
81077
  if (prInfo.fullDiff) {
80954
81078
  context += `
81079
+ <!-- Complete unified diff showing all changes in the pull request -->
80955
81080
  <full_diff>
80956
81081
  ${this.escapeXml(prInfo.fullDiff)}
80957
81082
  </full_diff>`;
@@ -80960,14 +81085,15 @@ ${this.escapeXml(prInfo.fullDiff)}
80960
81085
  if (prInfo.isIncremental) {
80961
81086
  if (prInfo.commitDiff && prInfo.commitDiff.length > 0) {
80962
81087
  context += `
81088
+ <!-- Diff of only the latest commit for incremental analysis -->
80963
81089
  <commit_diff>
80964
81090
  ${this.escapeXml(prInfo.commitDiff)}
80965
81091
  </commit_diff>`;
80966
81092
  }
80967
81093
  else {
80968
81094
  context += `
81095
+ <!-- Commit diff could not be retrieved - falling back to full diff analysis -->
80969
81096
  <commit_diff>
80970
- <!-- Commit diff could not be retrieved - falling back to full diff analysis -->
80971
81097
  ${prInfo.fullDiff ? this.escapeXml(prInfo.fullDiff) : ''}
80972
81098
  </commit_diff>`;
80973
81099
  }
@@ -80975,10 +81101,11 @@ ${prInfo.fullDiff ? this.escapeXml(prInfo.fullDiff) : ''}
80975
81101
  // Add file summary for context
80976
81102
  if (prInfo.files.length > 0) {
80977
81103
  context += `
81104
+ <!-- Summary of all files changed with statistics -->
80978
81105
  <files_summary>`;
80979
- prInfo.files.forEach((file, index) => {
81106
+ prInfo.files.forEach(file => {
80980
81107
  context += `
80981
- <file index="${index + 1}">
81108
+ <file>
80982
81109
  <filename>${this.escapeXml(file.filename)}</filename>
80983
81110
  <status>${file.status}</status>
80984
81111
  <additions>${file.additions}</additions>
@@ -80988,6 +81115,40 @@ ${prInfo.fullDiff ? this.escapeXml(prInfo.fullDiff) : ''}
80988
81115
  context += `
80989
81116
  </files_summary>`;
80990
81117
  }
81118
+ // Add current/triggering comment if this is a comment event
81119
+ const triggeringComment = prInfo.eventContext?.comment;
81120
+ if (triggeringComment) {
81121
+ context += `
81122
+ <!-- The comment that triggered this analysis -->
81123
+ <triggering_comment>
81124
+ <author>${this.escapeXml(triggeringComment.user?.login || 'unknown')}</author>
81125
+ <created_at>${triggeringComment.created_at || ''}</created_at>
81126
+ <body>${this.escapeXml(triggeringComment.body || '')}</body>
81127
+ </triggering_comment>`;
81128
+ }
81129
+ // Add comment history (excluding the current comment if it exists)
81130
+ const prComments = prInfo.comments;
81131
+ if (prComments && prComments.length > 0) {
81132
+ // Filter out the triggering comment from history if present
81133
+ const historicalComments = triggeringComment
81134
+ ? prComments.filter((c) => c.id !== triggeringComment.id)
81135
+ : prComments;
81136
+ if (historicalComments.length > 0) {
81137
+ context += `
81138
+ <!-- Previous PR comments in chronological order (excluding triggering comment) -->
81139
+ <comment_history>`;
81140
+ historicalComments.forEach((comment) => {
81141
+ context += `
81142
+ <comment>
81143
+ <author>${this.escapeXml(comment.author || 'unknown')}</author>
81144
+ <created_at>${comment.createdAt || ''}</created_at>
81145
+ <body>${this.escapeXml(comment.body || '')}</body>
81146
+ </comment>`;
81147
+ });
81148
+ context += `
81149
+ </comment_history>`;
81150
+ }
81151
+ }
80991
81152
  context += `
80992
81153
  </pull_request>`;
80993
81154
  return context;
@@ -81563,6 +81724,7 @@ const check_provider_registry_1 = __nccwpck_require__(57140);
81563
81724
  const dependency_resolver_1 = __nccwpck_require__(98645);
81564
81725
  const failure_condition_evaluator_1 = __nccwpck_require__(54429);
81565
81726
  const github_check_service_1 = __nccwpck_require__(21367);
81727
+ const issue_filter_1 = __nccwpck_require__(36879);
81566
81728
  /**
81567
81729
  * Filter environment variables to only include safe ones for sandbox evaluation
81568
81730
  */
@@ -81598,8 +81760,11 @@ class CheckExecutionEngine {
81598
81760
  githubCheckService;
81599
81761
  checkRunMap;
81600
81762
  githubContext;
81763
+ workingDirectory;
81764
+ config;
81601
81765
  constructor(workingDirectory) {
81602
- this.gitAnalyzer = new git_repository_analyzer_1.GitRepositoryAnalyzer(workingDirectory);
81766
+ this.workingDirectory = workingDirectory || process.cwd();
81767
+ this.gitAnalyzer = new git_repository_analyzer_1.GitRepositoryAnalyzer(this.workingDirectory);
81603
81768
  this.providerRegistry = check_provider_registry_1.CheckProviderRegistry.getInstance();
81604
81769
  this.failureEvaluator = new failure_condition_evaluator_1.FailureConditionEvaluator();
81605
81770
  // Create a mock Octokit instance for local analysis
@@ -81743,6 +81908,8 @@ class CheckExecutionEngine {
81743
81908
  * Execute review checks using parallel execution for multiple AI checks
81744
81909
  */
81745
81910
  async executeReviewChecks(prInfo, checks, timeout, config, outputFormat, debug, maxParallelism, failFast) {
81911
+ // Store config for use in filtering
81912
+ this.config = config;
81746
81913
  // Determine where to send log messages based on output format
81747
81914
  const logFn = outputFormat === 'json' || outputFormat === 'sarif' ? console.error : console.log;
81748
81915
  logFn(`🔧 Debug: executeReviewChecks called with checks: ${JSON.stringify(checks)}`);
@@ -82583,6 +82750,10 @@ class CheckExecutionEngine {
82583
82750
  // Add summary information
82584
82751
  aggregatedSuggestions.unshift(...debugInfo);
82585
82752
  console.error(`🔧 Debug: Aggregated ${aggregatedIssues.length} issues from ${results.size} dependency-aware checks`);
82753
+ // Apply issue suppression filtering
82754
+ const suppressionEnabled = this.config?.output?.suppressionEnabled !== false;
82755
+ const issueFilter = new issue_filter_1.IssueFilter(suppressionEnabled);
82756
+ const filteredIssues = issueFilter.filterIssues(aggregatedIssues, this.workingDirectory);
82586
82757
  // Collect debug information when debug mode is enabled
82587
82758
  let aggregatedDebug;
82588
82759
  if (debug) {
@@ -82621,7 +82792,7 @@ class CheckExecutionEngine {
82621
82792
  }
82622
82793
  }
82623
82794
  return {
82624
- issues: aggregatedIssues,
82795
+ issues: filteredIssues,
82625
82796
  suggestions: aggregatedSuggestions,
82626
82797
  debug: aggregatedDebug,
82627
82798
  };
@@ -82709,6 +82880,10 @@ class CheckExecutionEngine {
82709
82880
  : `🔍 Parallel execution completed: ${successfulChecks} successful, ${failedChecks} failed`);
82710
82881
  aggregatedSuggestions.unshift(...debugInfo);
82711
82882
  console.error(`🔧 Debug: Aggregated ${aggregatedIssues.length} issues from ${results.length} checks`);
82883
+ // Apply issue suppression filtering
82884
+ const suppressionEnabled = this.config?.output?.suppressionEnabled !== false;
82885
+ const issueFilter = new issue_filter_1.IssueFilter(suppressionEnabled);
82886
+ const filteredIssues = issueFilter.filterIssues(aggregatedIssues, this.workingDirectory);
82712
82887
  // Collect debug information when debug mode is enabled
82713
82888
  let aggregatedDebug;
82714
82889
  if (debug) {
@@ -82804,7 +82979,7 @@ class CheckExecutionEngine {
82804
82979
  }
82805
82980
  }
82806
82981
  return {
82807
- issues: aggregatedIssues,
82982
+ issues: filteredIssues,
82808
82983
  suggestions: aggregatedSuggestions,
82809
82984
  debug: aggregatedDebug,
82810
82985
  };
@@ -84023,13 +84198,31 @@ class ConfigManager {
84023
84198
  */
84024
84199
  loadBundledDefaultConfig() {
84025
84200
  try {
84026
- // Find the package root by looking for package.json
84027
- const packageRoot = this.findPackageRoot();
84028
- if (!packageRoot) {
84029
- return null;
84201
+ // Try different paths to find the bundled default config
84202
+ const possiblePaths = [
84203
+ // When running as GitHub Action (bundled in dist/)
84204
+ path.join(__dirname, 'defaults', '.visor.yaml'),
84205
+ // When running from source
84206
+ path.join(__dirname, '..', 'defaults', '.visor.yaml'),
84207
+ // Try via package root
84208
+ this.findPackageRoot() ? path.join(this.findPackageRoot(), 'defaults', '.visor.yaml') : '',
84209
+ // GitHub Action environment variable
84210
+ process.env.GITHUB_ACTION_PATH
84211
+ ? path.join(process.env.GITHUB_ACTION_PATH, 'defaults', '.visor.yaml')
84212
+ : '',
84213
+ process.env.GITHUB_ACTION_PATH
84214
+ ? path.join(process.env.GITHUB_ACTION_PATH, 'dist', 'defaults', '.visor.yaml')
84215
+ : '',
84216
+ ].filter(p => p); // Remove empty paths
84217
+ let bundledConfigPath;
84218
+ for (const possiblePath of possiblePaths) {
84219
+ if (fs.existsSync(possiblePath)) {
84220
+ bundledConfigPath = possiblePath;
84221
+ break;
84222
+ }
84030
84223
  }
84031
- const bundledConfigPath = path.join(packageRoot, 'defaults', '.visor.yaml');
84032
- if (fs.existsSync(bundledConfigPath)) {
84224
+ if (bundledConfigPath && fs.existsSync(bundledConfigPath)) {
84225
+ console.log(`📦 Loading bundled default configuration from ${bundledConfigPath}`);
84033
84226
  const configContent = fs.readFileSync(bundledConfigPath, 'utf8');
84034
84227
  const parsedConfig = yaml.load(configContent);
84035
84228
  if (!parsedConfig || typeof parsedConfig !== 'object') {
@@ -85983,7 +86176,6 @@ exports.run = run;
85983
86176
  const rest_1 = __nccwpck_require__(47432);
85984
86177
  const auth_app_1 = __nccwpck_require__(76479);
85985
86178
  const core_1 = __nccwpck_require__(37484);
85986
- const path = __importStar(__nccwpck_require__(16928));
85987
86179
  const commands_1 = __nccwpck_require__(99153);
85988
86180
  const pr_analyzer_1 = __nccwpck_require__(80100);
85989
86181
  const reviewer_1 = __nccwpck_require__(532);
@@ -86147,12 +86339,12 @@ async function run() {
86147
86339
  }
86148
86340
  catch {
86149
86341
  // Fall back to bundled default config
86150
- try {
86151
- const defaultConfigPath = path.join(process.env.GITHUB_ACTION_PATH || __dirname, 'defaults', '.visor.yaml');
86152
- config = await configManager.loadConfig(defaultConfigPath);
86153
- console.log('📋 Using bundled default configuration from defaults/.visor.yaml');
86342
+ const bundledConfig = configManager.loadBundledDefaultConfig();
86343
+ if (bundledConfig) {
86344
+ config = bundledConfig;
86345
+ console.log('📋 Using bundled default configuration');
86154
86346
  }
86155
- catch {
86347
+ else {
86156
86348
  // Ultimate fallback if even defaults/.visor.yaml can't be loaded
86157
86349
  config = {
86158
86350
  version: '1.0',
@@ -86349,6 +86541,18 @@ async function handleIssueEvent(octokit, owner, repo, context, inputs, config, c
86349
86541
  isIssue: true, // Flag to indicate this is an issue, not a PR
86350
86542
  eventContext: context.event, // Pass the full event context for templates
86351
86543
  };
86544
+ // Fetch comment history for issues
86545
+ try {
86546
+ console.log(`💬 Fetching comment history for issue #${issue.number}`);
86547
+ const analyzer = new pr_analyzer_1.PRAnalyzer(octokit);
86548
+ const comments = await analyzer.fetchPRComments(owner, repo, issue.number);
86549
+ prInfo.comments = comments;
86550
+ console.log(`✅ Retrieved ${comments.length} comments for issue`);
86551
+ }
86552
+ catch (error) {
86553
+ console.warn(`⚠️ Could not fetch issue comments: ${error instanceof Error ? error.message : 'Unknown error'}`);
86554
+ prInfo.comments = [];
86555
+ }
86352
86556
  // Run the checks using CheckExecutionEngine
86353
86557
  const { CheckExecutionEngine } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(80299)));
86354
86558
  const engine = new CheckExecutionEngine();
@@ -86520,7 +86724,7 @@ async function handleIssueComment(octokit, owner, repo, context, inputs, actionC
86520
86724
  if (isPullRequest) {
86521
86725
  // It's a PR comment - fetch the PR diff
86522
86726
  prInfo = await analyzer.fetchPRDiff(owner, repo, prNumber, undefined, 'issue_comment');
86523
- // Add event context for templates
86727
+ // Add event context for templates and XML generation
86524
86728
  prInfo.eventContext = context.event;
86525
86729
  }
86526
86730
  else {
@@ -86538,8 +86742,19 @@ async function handleIssueComment(octokit, owner, repo, context, inputs, actionC
86538
86742
  fullDiff: '',
86539
86743
  eventType: 'issue_comment',
86540
86744
  isIssue: true, // Flag to indicate this is an issue, not a PR
86541
- eventContext: context.event, // Pass the full event context for templates
86745
+ eventContext: context.event, // Pass the full event context
86542
86746
  };
86747
+ // Fetch comment history for the issue
86748
+ try {
86749
+ console.log(`💬 Fetching comment history for issue #${issue.number}`);
86750
+ const comments = await analyzer.fetchPRComments(owner, repo, issue.number);
86751
+ prInfo.comments = comments;
86752
+ console.log(`✅ Retrieved ${comments.length} comments for issue`);
86753
+ }
86754
+ catch (error) {
86755
+ console.warn(`⚠️ Could not fetch issue comments: ${error instanceof Error ? error.message : 'Unknown error'}`);
86756
+ prInfo.comments = [];
86757
+ }
86543
86758
  }
86544
86759
  // Extract common arguments
86545
86760
  const focus = command.args?.find(arg => arg.startsWith('--focus='))?.split('=')[1];
@@ -86619,7 +86834,7 @@ async function handlePullRequestWithConfig(octokit, owner, repo, inputs, config,
86619
86834
  let prInfo;
86620
86835
  try {
86621
86836
  prInfo = await analyzer.fetchPRDiff(owner, repo, prNumber, undefined, eventType);
86622
- // Add event context for templates
86837
+ // Add event context for templates and XML generation
86623
86838
  prInfo.eventContext = context.event;
86624
86839
  }
86625
86840
  catch (error) {
@@ -87094,6 +87309,159 @@ if (process.env.NODE_ENV !== 'test' && process.env.JEST_WORKER_ID === undefined)
87094
87309
  }
87095
87310
 
87096
87311
 
87312
+ /***/ }),
87313
+
87314
+ /***/ 36879:
87315
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
87316
+
87317
+ "use strict";
87318
+
87319
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
87320
+ if (k2 === undefined) k2 = k;
87321
+ var desc = Object.getOwnPropertyDescriptor(m, k);
87322
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
87323
+ desc = { enumerable: true, get: function() { return m[k]; } };
87324
+ }
87325
+ Object.defineProperty(o, k2, desc);
87326
+ }) : (function(o, m, k, k2) {
87327
+ if (k2 === undefined) k2 = k;
87328
+ o[k2] = m[k];
87329
+ }));
87330
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
87331
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
87332
+ }) : function(o, v) {
87333
+ o["default"] = v;
87334
+ });
87335
+ var __importStar = (this && this.__importStar) || (function () {
87336
+ var ownKeys = function(o) {
87337
+ ownKeys = Object.getOwnPropertyNames || function (o) {
87338
+ var ar = [];
87339
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
87340
+ return ar;
87341
+ };
87342
+ return ownKeys(o);
87343
+ };
87344
+ return function (mod) {
87345
+ if (mod && mod.__esModule) return mod;
87346
+ var result = {};
87347
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
87348
+ __setModuleDefault(result, mod);
87349
+ return result;
87350
+ };
87351
+ })();
87352
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
87353
+ exports.IssueFilter = void 0;
87354
+ const fs = __importStar(__nccwpck_require__(79896));
87355
+ const path = __importStar(__nccwpck_require__(16928));
87356
+ /**
87357
+ * Filter for suppressing Visor issues based on special comments in code
87358
+ */
87359
+ class IssueFilter {
87360
+ fileCache = new Map();
87361
+ suppressionEnabled;
87362
+ constructor(suppressionEnabled = true) {
87363
+ this.suppressionEnabled = suppressionEnabled;
87364
+ }
87365
+ /**
87366
+ * Filter out issues that have suppression comments
87367
+ * @param issues Array of issues to filter
87368
+ * @param workingDir Working directory for resolving file paths
87369
+ * @returns Filtered array of issues with suppressed ones removed
87370
+ */
87371
+ filterIssues(issues, workingDir = process.cwd()) {
87372
+ if (!this.suppressionEnabled || !issues || issues.length === 0) {
87373
+ return issues;
87374
+ }
87375
+ const filteredIssues = [];
87376
+ const suppressedCount = {};
87377
+ for (const issue of issues) {
87378
+ if (this.shouldSuppressIssue(issue, workingDir)) {
87379
+ // Track suppressed issues for logging
87380
+ suppressedCount[issue.file] = (suppressedCount[issue.file] || 0) + 1;
87381
+ }
87382
+ else {
87383
+ filteredIssues.push(issue);
87384
+ }
87385
+ }
87386
+ // Log suppression summary if any issues were suppressed
87387
+ const totalSuppressed = Object.values(suppressedCount).reduce((sum, count) => sum + count, 0);
87388
+ if (totalSuppressed > 0) {
87389
+ console.log(`🔇 Suppressed ${totalSuppressed} issue(s) via visor-disable comments:`);
87390
+ for (const [file, count] of Object.entries(suppressedCount)) {
87391
+ console.log(` - ${file}: ${count} issue(s)`);
87392
+ }
87393
+ }
87394
+ return filteredIssues;
87395
+ }
87396
+ /**
87397
+ * Check if an issue should be suppressed based on comments in the file
87398
+ */
87399
+ shouldSuppressIssue(issue, workingDir) {
87400
+ // Skip system-level issues or issues without file/line info
87401
+ if (!issue.file || issue.file === 'system' || issue.file === 'webhook' || issue.line === 0) {
87402
+ return false;
87403
+ }
87404
+ const lines = this.getFileLines(issue.file, workingDir);
87405
+ if (!lines || lines.length === 0) {
87406
+ return false;
87407
+ }
87408
+ // Check for file-level suppression (visor-disable-file in first 5 lines)
87409
+ const firstFiveLines = lines.slice(0, 5).join('\n').toLowerCase();
87410
+ if (firstFiveLines.includes('visor-disable-file')) {
87411
+ return true;
87412
+ }
87413
+ // Check for line-level suppression (visor-disable within ±2 lines)
87414
+ const lineIndex = issue.line - 1; // Convert to 0-based index
87415
+ const startLine = Math.max(0, lineIndex - 2);
87416
+ const endLine = Math.min(lines.length - 1, lineIndex + 2);
87417
+ for (let i = startLine; i <= endLine; i++) {
87418
+ if (lines[i].toLowerCase().includes('visor-disable')) {
87419
+ return true;
87420
+ }
87421
+ }
87422
+ return false;
87423
+ }
87424
+ /**
87425
+ * Get file lines from cache or read from disk
87426
+ */
87427
+ getFileLines(filePath, workingDir) {
87428
+ // Check cache first
87429
+ if (this.fileCache.has(filePath)) {
87430
+ return this.fileCache.get(filePath);
87431
+ }
87432
+ try {
87433
+ // Resolve the file path
87434
+ const resolvedPath = path.isAbsolute(filePath) ? filePath : path.join(workingDir, filePath);
87435
+ if (!fs.existsSync(resolvedPath)) {
87436
+ // Try without working directory if the file doesn't exist
87437
+ if (fs.existsSync(filePath)) {
87438
+ const content = fs.readFileSync(filePath, 'utf8');
87439
+ const lines = content.split('\n');
87440
+ this.fileCache.set(filePath, lines);
87441
+ return lines;
87442
+ }
87443
+ return null;
87444
+ }
87445
+ const content = fs.readFileSync(resolvedPath, 'utf8');
87446
+ const lines = content.split('\n');
87447
+ this.fileCache.set(filePath, lines);
87448
+ return lines;
87449
+ }
87450
+ catch {
87451
+ // Silently skip files that can't be read
87452
+ return null;
87453
+ }
87454
+ }
87455
+ /**
87456
+ * Clear the file cache (useful for testing or long-running processes)
87457
+ */
87458
+ clearCache() {
87459
+ this.fileCache.clear();
87460
+ }
87461
+ }
87462
+ exports.IssueFilter = IssueFilter;
87463
+
87464
+
87097
87465
  /***/ }),
87098
87466
 
87099
87467
  /***/ 25508:
@@ -87840,6 +88208,17 @@ class PRAnalyzer {
87840
88208
  fullDiff: this.generateFullDiff(validFiles),
87841
88209
  eventType,
87842
88210
  };
88211
+ // Fetch comment history for better context
88212
+ try {
88213
+ console.log(`💬 Fetching comment history for PR #${prInfo.number}`);
88214
+ const comments = await this.fetchPRComments(owner, repo, prInfo.number);
88215
+ prInfo.comments = comments;
88216
+ console.log(`✅ Retrieved ${comments.length} comments`);
88217
+ }
88218
+ catch (error) {
88219
+ console.warn(`⚠️ Could not fetch comments: ${error instanceof Error ? error.message : 'Unknown error'}`);
88220
+ prInfo.comments = [];
88221
+ }
87843
88222
  // Add commit diff for incremental analysis
87844
88223
  if (commitSha) {
87845
88224
  console.log(`🔧 Fetching incremental diff for commit: ${commitSha}`);
@@ -87942,6 +88321,7 @@ exports.AICheckProvider = void 0;
87942
88321
  const check_provider_interface_1 = __nccwpck_require__(14131);
87943
88322
  const ai_review_service_1 = __nccwpck_require__(51796);
87944
88323
  const env_resolver_1 = __nccwpck_require__(58749);
88324
+ const issue_filter_1 = __nccwpck_require__(36879);
87945
88325
  const liquidjs_1 = __nccwpck_require__(48694);
87946
88326
  const promises_1 = __importDefault(__nccwpck_require__(91943));
87947
88327
  const path_1 = __importDefault(__nccwpck_require__(16928));
@@ -88311,15 +88691,24 @@ class AICheckProvider extends check_provider_interface_1.CheckProvider {
88311
88691
  console.error(`🔧 Debug: AICheckProvider full config: ${JSON.stringify(config, null, 2)}`);
88312
88692
  try {
88313
88693
  console.error(`🔧 Debug: AICheckProvider passing checkName: ${config.checkName} to service`);
88694
+ let result;
88314
88695
  // Check if we should use session reuse
88315
88696
  if (sessionInfo?.reuseSession && sessionInfo.parentSessionId) {
88316
88697
  console.error(`🔄 Debug: Using session reuse with parent session: ${sessionInfo.parentSessionId}`);
88317
- return await service.executeReviewWithSessionReuse(prInfo, processedPrompt, sessionInfo.parentSessionId, schema, config.checkName);
88698
+ result = await service.executeReviewWithSessionReuse(prInfo, processedPrompt, sessionInfo.parentSessionId, schema, config.checkName);
88318
88699
  }
88319
88700
  else {
88320
88701
  console.error(`🆕 Debug: Creating new AI session for check: ${config.checkName}`);
88321
- return await service.executeReview(prInfo, processedPrompt, schema, config.checkName, config.sessionId);
88702
+ result = await service.executeReview(prInfo, processedPrompt, schema, config.checkName, config.sessionId);
88322
88703
  }
88704
+ // Apply issue suppression filtering
88705
+ const suppressionEnabled = config.suppressionEnabled !== false;
88706
+ const issueFilter = new issue_filter_1.IssueFilter(suppressionEnabled);
88707
+ const filteredIssues = issueFilter.filterIssues(result.issues || [], process.cwd());
88708
+ return {
88709
+ ...result,
88710
+ issues: filteredIssues,
88711
+ };
88323
88712
  }
88324
88713
  catch (error) {
88325
88714
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -88607,6 +88996,7 @@ exports.NoopCheckProvider = NoopCheckProvider;
88607
88996
  Object.defineProperty(exports, "__esModule", ({ value: true }));
88608
88997
  exports.ToolCheckProvider = void 0;
88609
88998
  const check_provider_interface_1 = __nccwpck_require__(14131);
88999
+ const issue_filter_1 = __nccwpck_require__(36879);
88610
89000
  const child_process_1 = __nccwpck_require__(35317);
88611
89001
  const liquidjs_1 = __nccwpck_require__(48694);
88612
89002
  /**
@@ -88692,8 +89082,12 @@ class ToolCheckProvider extends check_provider_interface_1.CheckProvider {
88692
89082
  suggestion: undefined,
88693
89083
  replacement: undefined,
88694
89084
  }));
89085
+ // Apply issue suppression filtering
89086
+ const suppressionEnabled = config.suppressionEnabled !== false;
89087
+ const issueFilter = new issue_filter_1.IssueFilter(suppressionEnabled);
89088
+ const filteredIssues = issueFilter.filterIssues(issues, process.cwd());
88695
89089
  return {
88696
- issues,
89090
+ issues: filteredIssues,
88697
89091
  suggestions: this.generateSuggestions(comments, renderedCommand),
88698
89092
  };
88699
89093
  }
@@ -88788,6 +89182,7 @@ exports.ToolCheckProvider = ToolCheckProvider;
88788
89182
  Object.defineProperty(exports, "__esModule", ({ value: true }));
88789
89183
  exports.WebhookCheckProvider = void 0;
88790
89184
  const check_provider_interface_1 = __nccwpck_require__(14131);
89185
+ const issue_filter_1 = __nccwpck_require__(36879);
88791
89186
  /**
88792
89187
  * Check provider that sends PR info to a webhook for external analysis
88793
89188
  */
@@ -88848,7 +89243,15 @@ class WebhookCheckProvider extends check_provider_interface_1.CheckProvider {
88848
89243
  // Send webhook request
88849
89244
  const response = await this.sendWebhookRequest(url, method, headers, payload, timeout);
88850
89245
  // Parse webhook response
88851
- return this.parseWebhookResponse(response, url);
89246
+ const result = this.parseWebhookResponse(response, url);
89247
+ // Apply issue suppression filtering
89248
+ const suppressionEnabled = config.suppressionEnabled !== false;
89249
+ const issueFilter = new issue_filter_1.IssueFilter(suppressionEnabled);
89250
+ const filteredIssues = issueFilter.filterIssues(result.issues || [], process.cwd());
89251
+ return {
89252
+ ...result,
89253
+ issues: filteredIssues,
89254
+ };
88852
89255
  }
88853
89256
  catch (error) {
88854
89257
  return this.createErrorResult(url, error);
@@ -89088,9 +89491,19 @@ class PRReviewer {
89088
89491
  prNumber,
89089
89492
  commitSha: options.commitSha,
89090
89493
  });
89091
- const commentId = options.commentId
89092
- ? `${options.commentId}-${groupName}`
89093
- : `visor-review-${groupName}`;
89494
+ // Generate comment ID - use unique ID for "dynamic" group
89495
+ let commentId;
89496
+ if (groupName === 'dynamic') {
89497
+ // Dynamic group creates a new comment each time with timestamp-based ID
89498
+ const timestamp = Date.now();
89499
+ commentId = `visor-dynamic-${timestamp}`;
89500
+ }
89501
+ else {
89502
+ // Regular groups use static IDs that get updated
89503
+ commentId = options.commentId
89504
+ ? `${options.commentId}-${groupName}`
89505
+ : `visor-review-${groupName}`;
89506
+ }
89094
89507
  await this.commentManager.updateOrCreateComment(owner, repo, prNumber, comment, {
89095
89508
  commentId,
89096
89509
  triggeredBy: options.triggeredBy || 'unknown',
@@ -89536,15 +89949,31 @@ class ConfigLoader {
89536
89949
  * Load bundled default configuration
89537
89950
  */
89538
89951
  async fetchDefaultConfig() {
89539
- // First try to find the bundled default config
89540
- let packageRoot = this.findPackageRoot();
89541
- if (!packageRoot) {
89542
- // Fallback: try relative to current file
89543
- packageRoot = path.resolve(__dirname, '..', '..');
89544
- }
89545
- const defaultConfigPath = path.join(packageRoot, 'defaults', '.visor.yaml');
89546
- if (fs.existsSync(defaultConfigPath)) {
89547
- console.log(`📦 Loading bundled default configuration`);
89952
+ // Try different paths to find the bundled default config
89953
+ const possiblePaths = [
89954
+ // When running as GitHub Action (bundled in dist/)
89955
+ path.join(__dirname, 'defaults', '.visor.yaml'),
89956
+ // When running from source
89957
+ path.join(__dirname, '..', '..', 'defaults', '.visor.yaml'),
89958
+ // Try via package root
89959
+ this.findPackageRoot() ? path.join(this.findPackageRoot(), 'defaults', '.visor.yaml') : '',
89960
+ // GitHub Action environment variable
89961
+ process.env.GITHUB_ACTION_PATH
89962
+ ? path.join(process.env.GITHUB_ACTION_PATH, 'defaults', '.visor.yaml')
89963
+ : '',
89964
+ process.env.GITHUB_ACTION_PATH
89965
+ ? path.join(process.env.GITHUB_ACTION_PATH, 'dist', 'defaults', '.visor.yaml')
89966
+ : '',
89967
+ ].filter(p => p); // Remove empty paths
89968
+ let defaultConfigPath;
89969
+ for (const possiblePath of possiblePaths) {
89970
+ if (fs.existsSync(possiblePath)) {
89971
+ defaultConfigPath = possiblePath;
89972
+ break;
89973
+ }
89974
+ }
89975
+ if (defaultConfigPath && fs.existsSync(defaultConfigPath)) {
89976
+ console.log(`📦 Loading bundled default configuration from ${defaultConfigPath}`);
89548
89977
  const content = fs.readFileSync(defaultConfigPath, 'utf8');
89549
89978
  const config = yaml.load(content);
89550
89979
  if (!config || typeof config !== 'object') {
@@ -110291,6 +110720,103 @@ ${decodedContent}
110291
110720
  };
110292
110721
  }
110293
110722
  }
110723
+ let proactiveFixesApplied = false;
110724
+ const { diagrams: currentDiagrams } = extractMermaidFromMarkdown(fixedResponse);
110725
+ for (let diagramIndex = currentDiagrams.length - 1; diagramIndex >= 0; diagramIndex--) {
110726
+ const diagram = currentDiagrams[diagramIndex];
110727
+ const originalContent = diagram.content;
110728
+ const lines = originalContent.split("\n");
110729
+ let wasFixed = false;
110730
+ const fixedLines = lines.map((line) => {
110731
+ const trimmedLine = line.trim();
110732
+ let modifiedLine = line;
110733
+ if (trimmedLine.match(/\[[^\]]*\]/)) {
110734
+ modifiedLine = modifiedLine.replace(/\[([^\]]*)\]/g, (match, content) => {
110735
+ if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
110736
+ return match;
110737
+ }
110738
+ const needsQuoting = /[()'"<>&]/.test(content) || // Core problematic characters
110739
+ content.includes("e.g.") || content.includes("i.e.") || content.includes("src/") || content.includes("defaults/") || content.includes(".ts") || content.includes(".js") || content.includes(".yaml") || content.includes(".json") || content.includes(".md") || content.includes(".html") || content.includes(".css");
110740
+ if (needsQuoting) {
110741
+ wasFixed = true;
110742
+ const safeContent = content.replace(/"/g, "'");
110743
+ return `["${safeContent}"]`;
110744
+ }
110745
+ return match;
110746
+ });
110747
+ }
110748
+ if (trimmedLine.match(/\{[^{}]*\}/)) {
110749
+ modifiedLine = modifiedLine.replace(/\{([^{}]*)\}/g, (match, content) => {
110750
+ if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
110751
+ return match;
110752
+ }
110753
+ const needsQuoting = /[()'"<>&]/.test(content) || // Core problematic characters
110754
+ content.includes("e.g.") || content.includes("i.e.") || content.includes("src/") || content.includes("defaults/") || content.includes(".ts") || content.includes(".js") || content.includes(".yaml") || content.includes(".json") || content.includes(".md") || content.includes(".html") || content.includes(".css");
110755
+ if (needsQuoting) {
110756
+ wasFixed = true;
110757
+ const safeContent = content.replace(/"/g, "'");
110758
+ return `{"${safeContent}"}`;
110759
+ }
110760
+ return match;
110761
+ });
110762
+ }
110763
+ return modifiedLine;
110764
+ });
110765
+ if (wasFixed) {
110766
+ const fixedContent = fixedLines.join("\n");
110767
+ const attributesStr = diagram.attributes ? ` ${diagram.attributes}` : "";
110768
+ const newCodeBlock = `\`\`\`mermaid${attributesStr}
110769
+ ${fixedContent}
110770
+ \`\`\``;
110771
+ fixedResponse = fixedResponse.slice(0, diagram.startIndex) + newCodeBlock + fixedResponse.slice(diagram.endIndex);
110772
+ fixingResults.push({
110773
+ diagramIndex,
110774
+ wasFixed: true,
110775
+ originalContent,
110776
+ fixedContent,
110777
+ originalError: "Proactive node label quoting",
110778
+ fixMethod: "node_label_quote_wrapping",
110779
+ fixedWithProactiveQuoting: true
110780
+ });
110781
+ proactiveFixesApplied = true;
110782
+ if (debug) {
110783
+ console.log(`[DEBUG] Mermaid validation: Proactively fixed diagram ${diagramIndex + 1} with node label quoting`);
110784
+ console.log(`[DEBUG] Mermaid validation: Applied automatic quoting to special characters`);
110785
+ }
110786
+ }
110787
+ }
110788
+ if (proactiveFixesApplied) {
110789
+ const revalidation = await validateMermaidResponse(fixedResponse);
110790
+ if (revalidation.isValid) {
110791
+ const totalTime2 = Date.now() - startTime;
110792
+ if (debug) {
110793
+ console.log(`[DEBUG] Mermaid validation: All diagrams fixed with proactive quoting in ${totalTime2}ms, no AI needed`);
110794
+ console.log(`[DEBUG] Mermaid validation: Applied ${fixingResults.length} proactive fixes`);
110795
+ }
110796
+ if (tracer) {
110797
+ tracer.recordMermaidValidationEvent("proactive_fix_completed", {
110798
+ "mermaid_validation.success": true,
110799
+ "mermaid_validation.fix_method": "node_label_quote_wrapping",
110800
+ "mermaid_validation.diagrams_fixed": fixingResults.length,
110801
+ "mermaid_validation.duration_ms": totalTime2
110802
+ });
110803
+ }
110804
+ return {
110805
+ ...revalidation,
110806
+ wasFixed: true,
110807
+ originalResponse: response,
110808
+ fixedResponse,
110809
+ fixingResults,
110810
+ performanceMetrics: {
110811
+ totalTimeMs: totalTime2,
110812
+ aiFixingTimeMs: 0,
110813
+ finalValidationTimeMs: 0,
110814
+ diagramsProcessed: fixingResults.length,
110815
+ diagramsFixed: fixingResults.length
110816
+ }
110817
+ };
110818
+ }
110819
+ }
110294
110820
  let subgraphFixesApplied = false;
110295
110821
  const { diagrams: postHtmlDiagrams } = extractMermaidFromMarkdown(fixedResponse);
110296
110822
  const postHtmlValidation = await validateMermaidResponse(fixedResponse);
@@ -110384,16 +110910,21 @@ ${fixedContent}
110384
110910
  const postSubgraphValidation = await validateMermaidResponse(fixedResponse);
110385
110911
  const stillInvalidAfterSubgraph = postSubgraphValidation.diagrams.map((result, index) => ({ ...result, originalIndex: index })).filter((result) => !result.isValid).reverse();
110386
110912
  for (const invalidDiagram of stillInvalidAfterSubgraph) {
110387
- if (invalidDiagram.error && (invalidDiagram.error.includes("Parentheses in node label") || invalidDiagram.error.includes("Complex expression in diamond node"))) {
110913
+ if (invalidDiagram.error && (invalidDiagram.error.includes("Parentheses in node label") || invalidDiagram.error.includes("Complex expression in diamond node") || invalidDiagram.error.includes("Single quotes in node label"))) {
110388
110914
  const originalContent = invalidDiagram.content;
110389
110915
  const lines = originalContent.split("\n");
110390
110916
  let wasFixed = false;
110391
110917
  const fixedLines = lines.map((line) => {
110392
110918
  const trimmedLine = line.trim();
110393
110919
  let modifiedLine = line;
110394
- if (trimmedLine.match(/\[[^\]"]*\([^\]"]*\]/)) {
110395
- modifiedLine = modifiedLine.replace(/\[([^\]"]*\([^\]"]*)\]/g, (match, content) => {
110396
- if (!content.trim().startsWith('"') || !content.trim().endsWith('"')) {
110920
+ if (trimmedLine.match(/\[[^\]]*\]/)) {
110921
+ modifiedLine = modifiedLine.replace(/\[([^\]]*)\]/g, (match, content) => {
110922
+ if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
110923
+ return match;
110924
+ }
110925
+ const needsQuoting = /[()'"<>&]/.test(content) || // Core problematic characters
110926
+ content.includes("e.g.") || content.includes("i.e.") || content.includes("src/") || content.includes("defaults/") || content.includes(".ts") || content.includes(".js") || content.includes(".yaml") || content.includes(".json");
110927
+ if (needsQuoting) {
110397
110928
  wasFixed = true;
110398
110929
  const safeContent = content.replace(/"/g, "'");
110399
110930
  return `["${safeContent}"]`;
@@ -110401,9 +110932,14 @@ ${fixedContent}
110401
110932
  return match;
110402
110933
  });
110403
110934
  }
110404
- if (trimmedLine.match(/\{[^{}"]*\([^{}"]*\}/)) {
110405
- modifiedLine = modifiedLine.replace(/\{([^{}"]*\([^{}"]*)\}/g, (match, content) => {
110406
- if (!content.trim().startsWith('"') || !content.trim().endsWith('"')) {
110935
+ if (trimmedLine.match(/\{[^{}]*\}/)) {
110936
+ modifiedLine = modifiedLine.replace(/\{([^{}]*)\}/g, (match, content) => {
110937
+ if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
110938
+ return match;
110939
+ }
110940
+ const needsQuoting = /[()'"<>&]/.test(content) || // Core problematic characters
110941
+ content.includes("e.g.") || content.includes("i.e.") || content.includes("src/") || content.includes("defaults/") || content.includes(".ts") || content.includes(".js") || content.includes(".yaml") || content.includes(".json");
110942
+ if (needsQuoting) {
110407
110943
  wasFixed = true;
110408
110944
  const safeContent = content.replace(/"/g, "'");
110409
110945
  return `{"${safeContent}"}`;
@@ -110837,6 +111373,7 @@ var init_ProbeAgent = __esm({
110837
111373
  * @param {boolean} [options.debug] - Enable debug mode
110838
111374
  * @param {boolean} [options.outline] - Enable outline-xml format for search results
110839
111375
  * @param {number} [options.maxResponseTokens] - Maximum tokens for AI responses
111376
+ * @param {boolean} [options.disableMermaidValidation=false] - Disable automatic mermaid diagram validation and fixing
110840
111377
  */
110841
111378
  constructor(options = {}) {
110842
111379
  this.sessionId = options.sessionId || (0, import_crypto5.randomUUID)();
@@ -110848,6 +111385,7 @@ var init_ProbeAgent = __esm({
110848
111385
  this.tracer = options.tracer || null;
110849
111386
  this.outline = !!options.outline;
110850
111387
  this.maxResponseTokens = options.maxResponseTokens || parseInt(process.env.MAX_RESPONSE_TOKENS || "0", 10) || null;
111388
+ this.disableMermaidValidation = !!options.disableMermaidValidation;
110851
111389
  this.allowedFolders = options.path ? [options.path] : [process.cwd()];
110852
111390
  this.clientApiProvider = options.provider || null;
110853
111391
  this.clientApiKey = null;
@@ -111541,56 +112079,60 @@ Convert your previous response content into actual JSON data that follows this s
111541
112079
  _schemaFormatted: true
111542
112080
  });
111543
112081
  finalResult = cleanSchemaResponse(finalResult);
111544
- try {
111545
- if (this.debug) {
111546
- console.log(`[DEBUG] Mermaid validation: Starting enhanced mermaid validation...`);
111547
- }
111548
- if (this.tracer) {
111549
- this.tracer.recordMermaidValidationEvent("schema_processing_started", {
111550
- "mermaid_validation.context": "schema_processing",
111551
- "mermaid_validation.response_length": finalResult.length
111552
- });
111553
- }
111554
- const mermaidValidation = await validateAndFixMermaidResponse(finalResult, {
111555
- debug: this.debug,
111556
- path: this.allowedFolders[0],
111557
- provider: this.clientApiProvider,
111558
- model: this.model,
111559
- tracer: this.tracer
111560
- });
111561
- if (mermaidValidation.wasFixed) {
111562
- finalResult = mermaidValidation.fixedResponse;
112082
+ if (!this.disableMermaidValidation) {
112083
+ try {
111563
112084
  if (this.debug) {
111564
- console.log(`[DEBUG] Mermaid validation: Diagrams successfully fixed`);
111565
- if (mermaidValidation.performanceMetrics) {
111566
- const metrics = mermaidValidation.performanceMetrics;
111567
- console.log(`[DEBUG] Mermaid validation: Performance - total: ${metrics.totalTimeMs}ms, AI fixing: ${metrics.aiFixingTimeMs}ms`);
111568
- console.log(`[DEBUG] Mermaid validation: Results - ${metrics.diagramsFixed}/${metrics.diagramsProcessed} diagrams fixed`);
112085
+ console.log(`[DEBUG] Mermaid validation: Starting enhanced mermaid validation...`);
112086
+ }
112087
+ if (this.tracer) {
112088
+ this.tracer.recordMermaidValidationEvent("schema_processing_started", {
112089
+ "mermaid_validation.context": "schema_processing",
112090
+ "mermaid_validation.response_length": finalResult.length
112091
+ });
112092
+ }
112093
+ const mermaidValidation = await validateAndFixMermaidResponse(finalResult, {
112094
+ debug: this.debug,
112095
+ path: this.allowedFolders[0],
112096
+ provider: this.clientApiProvider,
112097
+ model: this.model,
112098
+ tracer: this.tracer
112099
+ });
112100
+ if (mermaidValidation.wasFixed) {
112101
+ finalResult = mermaidValidation.fixedResponse;
112102
+ if (this.debug) {
112103
+ console.log(`[DEBUG] Mermaid validation: Diagrams successfully fixed`);
112104
+ if (mermaidValidation.performanceMetrics) {
112105
+ const metrics = mermaidValidation.performanceMetrics;
112106
+ console.log(`[DEBUG] Mermaid validation: Performance - total: ${metrics.totalTimeMs}ms, AI fixing: ${metrics.aiFixingTimeMs}ms`);
112107
+ console.log(`[DEBUG] Mermaid validation: Results - ${metrics.diagramsFixed}/${metrics.diagramsProcessed} diagrams fixed`);
112108
+ }
112109
+ if (mermaidValidation.fixingResults) {
112110
+ mermaidValidation.fixingResults.forEach((fixResult, index) => {
112111
+ if (fixResult.wasFixed) {
112112
+ const method = fixResult.fixedWithHtmlDecoding ? "HTML entity decoding" : "AI correction";
112113
+ const time = fixResult.aiFixingTimeMs ? ` in ${fixResult.aiFixingTimeMs}ms` : "";
112114
+ console.log(`[DEBUG] Mermaid validation: Fixed diagram ${fixResult.diagramIndex + 1} with ${method}${time}`);
112115
+ console.log(`[DEBUG] Mermaid validation: Original error: ${fixResult.originalError}`);
112116
+ } else {
112117
+ console.log(`[DEBUG] Mermaid validation: Failed to fix diagram ${fixResult.diagramIndex + 1}: ${fixResult.fixingError}`);
112118
+ }
112119
+ });
112120
+ }
111569
112121
  }
111570
- if (mermaidValidation.fixingResults) {
111571
- mermaidValidation.fixingResults.forEach((fixResult, index) => {
111572
- if (fixResult.wasFixed) {
111573
- const method = fixResult.fixedWithHtmlDecoding ? "HTML entity decoding" : "AI correction";
111574
- const time = fixResult.aiFixingTimeMs ? ` in ${fixResult.aiFixingTimeMs}ms` : "";
111575
- console.log(`[DEBUG] Mermaid validation: Fixed diagram ${fixResult.diagramIndex + 1} with ${method}${time}`);
111576
- console.log(`[DEBUG] Mermaid validation: Original error: ${fixResult.originalError}`);
111577
- } else {
111578
- console.log(`[DEBUG] Mermaid validation: Failed to fix diagram ${fixResult.diagramIndex + 1}: ${fixResult.fixingError}`);
111579
- }
111580
- });
112122
+ } else if (this.debug) {
112123
+ console.log(`[DEBUG] Mermaid validation: No fixes needed or fixes unsuccessful`);
112124
+ if (mermaidValidation.diagrams?.length > 0) {
112125
+ console.log(`[DEBUG] Mermaid validation: Found ${mermaidValidation.diagrams.length} diagrams, all valid: ${mermaidValidation.isValid}`);
111581
112126
  }
111582
112127
  }
111583
- } else if (this.debug) {
111584
- console.log(`[DEBUG] Mermaid validation: No fixes needed or fixes unsuccessful`);
111585
- if (mermaidValidation.diagrams?.length > 0) {
111586
- console.log(`[DEBUG] Mermaid validation: Found ${mermaidValidation.diagrams.length} diagrams, all valid: ${mermaidValidation.isValid}`);
112128
+ } catch (error) {
112129
+ if (this.debug) {
112130
+ console.log(`[DEBUG] Mermaid validation: Process failed with error: ${error.message}`);
112131
+ console.log(`[DEBUG] Mermaid validation: Stack trace: ${error.stack}`);
111587
112132
  }
111588
112133
  }
111589
- } catch (error) {
111590
- if (this.debug) {
111591
- console.log(`[DEBUG] Mermaid validation: Process failed with error: ${error.message}`);
111592
- console.log(`[DEBUG] Mermaid validation: Stack trace: ${error.stack}`);
111593
- }
112134
+ } else if (this.debug) {
112135
+ console.log(`[DEBUG] Mermaid validation: Skipped due to disableMermaidValidation option`);
111594
112136
  }
111595
112137
  if (isJsonSchema(options.schema)) {
111596
112138
  if (this.debug) {
@@ -111695,26 +112237,30 @@ Convert your previous response content into actual JSON data that follows this s
111695
112237
  } else if (completionAttempted && options.schema) {
111696
112238
  try {
111697
112239
  finalResult = cleanSchemaResponse(finalResult);
111698
- if (this.debug) {
111699
- console.log(`[DEBUG] Mermaid validation: Validating attempt_completion result...`);
111700
- }
111701
- const mermaidValidation = await validateAndFixMermaidResponse(finalResult, {
111702
- debug: this.debug,
111703
- path: this.allowedFolders[0],
111704
- provider: this.clientApiProvider,
111705
- model: this.model,
111706
- tracer: this.tracer
111707
- });
111708
- if (mermaidValidation.wasFixed) {
111709
- finalResult = mermaidValidation.fixedResponse;
112240
+ if (!this.disableMermaidValidation) {
111710
112241
  if (this.debug) {
111711
- console.log(`[DEBUG] Mermaid validation: attempt_completion diagrams fixed`);
111712
- if (mermaidValidation.performanceMetrics) {
111713
- console.log(`[DEBUG] Mermaid validation: Fixed in ${mermaidValidation.performanceMetrics.totalTimeMs}ms`);
112242
+ console.log(`[DEBUG] Mermaid validation: Validating attempt_completion result...`);
112243
+ }
112244
+ const mermaidValidation = await validateAndFixMermaidResponse(finalResult, {
112245
+ debug: this.debug,
112246
+ path: this.allowedFolders[0],
112247
+ provider: this.clientApiProvider,
112248
+ model: this.model,
112249
+ tracer: this.tracer
112250
+ });
112251
+ if (mermaidValidation.wasFixed) {
112252
+ finalResult = mermaidValidation.fixedResponse;
112253
+ if (this.debug) {
112254
+ console.log(`[DEBUG] Mermaid validation: attempt_completion diagrams fixed`);
112255
+ if (mermaidValidation.performanceMetrics) {
112256
+ console.log(`[DEBUG] Mermaid validation: Fixed in ${mermaidValidation.performanceMetrics.totalTimeMs}ms`);
112257
+ }
111714
112258
  }
112259
+ } else if (this.debug) {
112260
+ console.log(`[DEBUG] Mermaid validation: attempt_completion result validation completed (no fixes needed)`);
111715
112261
  }
111716
112262
  } else if (this.debug) {
111717
- console.log(`[DEBUG] Mermaid validation: attempt_completion result validation completed (no fixes needed)`);
112263
+ console.log(`[DEBUG] Mermaid validation: Skipped for attempt_completion result due to disableMermaidValidation option`);
111718
112264
  }
111719
112265
  if (isJsonSchema(options.schema)) {
111720
112266
  if (this.debug) {