@aashari/mcp-server-atlassian-bitbucket
Version:
Node.js/TypeScript MCP server for Atlassian Bitbucket. Enables AI systems (LLMs) to interact with workspaces, repositories, and pull requests via tools (list, get, comment, search). Connects AI directly to version control workflows through the standard MC
128 lines (127 loc) • 6.61 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatDiffstat = formatDiffstat;
exports.formatFullDiff = formatFullDiff;
const formatter_util_js_1 = require("../utils/formatter.util.js");
/**
* Format diffstat results into Markdown
*
* @param diffstat - Diffstat response containing file changes
* @param baseBranchOrCommit - Name of the base branch or commit (source of the diff)
* @param targetBranchOrCommit - Name of the target branch or commit (destination of the diff)
* @returns Formatted Markdown string
*/
function formatDiffstat(diffstat, baseBranchOrCommit, targetBranchOrCommit) {
const lines = [];
const files = diffstat.values || [];
// Title section
lines.push((0, formatter_util_js_1.formatHeading)(`Diff: ${baseBranchOrCommit} → ${targetBranchOrCommit}`, 1));
lines.push('');
if (files.length === 0) {
lines.push('*No changes detected in the diffstat response. This might occur when:*');
lines.push('- The commits or branches are identical');
lines.push('- The changes are purely structural (e.g., merge commits without content changes)');
lines.push('- The parameters need to be specified in a different order');
lines.push('');
lines.push('**Try the following:**');
lines.push('1. For branch comparisons: Reverse the source and destination branch parameters');
lines.push('2. For commit comparisons: Ensure newer commit is `sinceCommit` and older commit is `untilCommit`');
lines.push('3. Check that both references exist and you have access to them');
lines.push('');
lines.push((0, formatter_util_js_1.formatSeparator)());
lines.push(`*Information retrieved at: ${(0, formatter_util_js_1.formatDate)(new Date())}*`);
return lines.join('\n');
}
// Summary statistics
let totalAdditions = 0;
let totalDeletions = 0;
let conflictedFiles = 0;
// Collect statistics
files.forEach((file) => {
if (file.lines_added)
totalAdditions += file.lines_added;
if (file.lines_removed)
totalDeletions += file.lines_removed;
if (file.status === 'merge conflict')
conflictedFiles++;
});
lines.push((0, formatter_util_js_1.formatHeading)('Summary', 2));
lines.push(`${files.length} file${files.length !== 1 ? 's' : ''} changed with ${totalAdditions} insertion${totalAdditions !== 1 ? 's' : ''} and ${totalDeletions} deletion${totalDeletions !== 1 ? 's' : ''}.`);
if (conflictedFiles > 0) {
lines.push('');
lines.push(`⚠️ **Merge conflicts detected in ${conflictedFiles} file${conflictedFiles !== 1 ? 's' : ''}.**`);
}
lines.push('');
// File changes section (limit to a reasonable number, like 20)
const maxFilesToShow = 20;
const hasMoreFiles = files.length > maxFilesToShow;
const filesToDisplay = files.slice(0, maxFilesToShow);
// File list with changes
lines.push((0, formatter_util_js_1.formatHeading)('Files Changed', 2));
lines.push('');
filesToDisplay.forEach((file) => {
const changes = [];
if (file.lines_added)
changes.push(`+${file.lines_added}`);
if (file.lines_removed)
changes.push(`-${file.lines_removed}`);
const changeStr = changes.length > 0 ? ` (${changes.join(', ')})` : '';
// Handle potentially null old/new paths
const filePath = file.new?.path || file.old?.path || '(unnamed file)';
// Show path, changes, and status if it's a conflict
let line = `- \`${filePath}\`${changeStr}`;
if (file.status === 'merge conflict') {
line += ' **CONFLICT**';
}
lines.push(line);
});
if (hasMoreFiles) {
lines.push('');
lines.push(`... and ${files.length - maxFilesToShow} more files`);
}
// Standard footer
lines.push('');
lines.push((0, formatter_util_js_1.formatSeparator)());
lines.push(`*Information retrieved at: ${(0, formatter_util_js_1.formatDate)(new Date())}*`);
return lines.join('\n');
}
/**
* Format complete diff results, including diffstat summary and raw diff
*
* @param diffstat - Diffstat response containing file changes
* @param rawDiff - Raw unified diff text
* @param baseBranchOrCommit - Name of the base branch or commit
* @param targetBranchOrCommit - Name of the target branch or commit
* @returns Formatted Markdown string
*/
function formatFullDiff(diffstat, rawDiff, baseBranchOrCommit, targetBranchOrCommit) {
const diffstatMd = formatDiffstat(diffstat, baseBranchOrCommit, targetBranchOrCommit);
// If there's a raw diff but empty diffstat, we should still show the raw diff
// This can happen with structural changes like merges
if (rawDiff &&
rawDiff.trim() !== '' &&
(diffstat.values || []).length === 0) {
const lines = diffstatMd.split('\n');
// Replace the "No changes detected" message with a more accurate one
const messageStartIndex = lines.findIndex((line) => line.includes('*No changes detected'));
if (messageStartIndex >= 0) {
lines.splice(messageStartIndex, 6, '*No file changes in diffstat but raw diff content was found. This often happens with:*', '- Merge commits or trivial merges', '- Rename-only changes without content modifications', '- Changes to file metadata or permissions without content changes', '', 'If the diff content below is not what you expected, try reversing the parameter order:', '- For branch comparisons: swap source and destination branch values', '- For commit comparisons: swap sinceCommit and untilCommit values');
}
// Insert section heading for the raw diff before the footer
const separatorIndex = lines.findIndex((line) => line.includes('---'));
if (separatorIndex >= 0) {
lines.splice(separatorIndex, 0, '', (0, formatter_util_js_1.formatHeading)('Raw Diff Content', 2), '');
lines.splice(separatorIndex + 3, 0, (0, formatter_util_js_1.formatDiff)(rawDiff));
}
return lines.join('\n');
}
if (!rawDiff || rawDiff.trim() === '') {
return diffstatMd;
}
const lines = diffstatMd.split('\n');
// Insert section heading for the raw diff
// Insert before the standard footer (which is the last 2 lines)
lines.splice(lines.length - 3, 0, '', (0, formatter_util_js_1.formatHeading)('Code Changes', 2), '');
lines.splice(lines.length - 3, 0, (0, formatter_util_js_1.formatDiff)(rawDiff));
return lines.join('\n');
}