// Helper function to format text with proper line breaks
export function formatText(text: string): string {
if (!text) return "";
// Replace multiple spaces with single spaces, but preserve intentional line breaks
let formatted = text.replace(/\s+/g, " ").trim();
// Convert markdown-style line breaks
formatted = formatted
// Convert ## headers to have line breaks
.replace(/##\s+/g, "\n\n## ")
// Convert - list items to have line breaks
.replace(/\s+-\s+/g, "\n- ")
// Convert ✅ checkmarks to have line breaks
.replace(/\s+✅/g, "\n\n✅")
// Convert numbered lists
.replace(/\s+(\d+\.)\s+/g, "\n$1 ")
// Convert [ ] checkboxes to have line breaks
.replace(/\s+-\s+\[\s*\]/g, "\n- [ ]")
// Convert [x] checkboxes to have line breaks
.replace(/\s+-\s+\[x\]/g, "\n- [x]")
// Add line breaks before "## " sections
.replace(/([^#])(##\s+)/g, "$1\n\n$2")
// Add line breaks before status indicators
.replace(
/([^\n])(##\s+Current Status|##\s+Testing|##\s+Requirements)/g,
"$1\n\n$2"
);
return formatted;
}
// Helper function to format API responses for MCP
export function formatResponse(data: any): any {
// Format the data first (for text fields)
const formattedData = formatDataFields(data);
// Return in MCP response format
return {
content: [
{
type: "text" as const,
text: JSON.stringify(formattedData, null, 2),
},
],
};
}
// Helper function to format data fields
function formatDataFields(data: any): any {
if (!data) return data;
// If it's an array, format each item
if (Array.isArray(data)) {
return data.map(formatDataFields);
}
// If it's an object, format specific fields
if (typeof data === "object" && data !== null) {
const formatted = { ...data };
// Format description fields
if (formatted.description && typeof formatted.description === 'string') {
formatted.description = formatText(formatted.description);
}
// Format comment fields
if (formatted.comment && typeof formatted.comment === 'string') {
formatted.comment = formatText(formatted.comment);
}
return formatted;
}
return data;
}
// Delete safety validation functions
import { config } from "../config/index.js";
export type DeleteType = "board" | "task" | "label";
export interface DeleteSafetyError {
allowed: false;
reason: string;
code: "DELETES_DISABLED" | "DELETE_TYPE_NOT_ALLOWED" | "CONFIRMATION_REQUIRED";
}
export interface DeleteSafetySuccess {
allowed: true;
}
export type DeleteSafetyResult = DeleteSafetyError | DeleteSafetySuccess;
/**
* Check if a delete operation is allowed based on configuration
*/
export function validateDeleteOperation(
deleteType: DeleteType,
confirmDelete?: boolean | string
): DeleteSafetyResult {
const { deleteSafety } = config;
// Check if deletes are enabled at all
if (!deleteSafety.enableDeletes) {
return {
allowed: false,
reason: `Delete operations are disabled. Set ENABLE_DELETES=true in environment to enable.`,
code: "DELETES_DISABLED"
};
}
// Check if this specific delete type is allowed
if (deleteSafety.allowedDeleteTypes.length > 0 &&
!deleteSafety.allowedDeleteTypes.includes(deleteType)) {
return {
allowed: false,
reason: `Delete operations for '${deleteType}' are not allowed. Allowed types: ${deleteSafety.allowedDeleteTypes.join(', ')}`,
code: "DELETE_TYPE_NOT_ALLOWED"
};
}
// Check confirmation requirement
if (deleteSafety.requireConfirmation) {
const confirmValue = typeof confirmDelete === "string"
? confirmDelete.toLowerCase()
: confirmDelete;
if (confirmValue !== true && confirmValue !== "yes" && confirmValue !== "confirm") {
return {
allowed: false,
reason: `Confirmation required for delete operations. Add 'confirm_delete: true' or 'confirm_delete: "yes"' parameter.`,
code: "CONFIRMATION_REQUIRED"
};
}
}
return { allowed: true };
}
/**
* Create a standardized error for delete safety violations
*/
export function createDeleteSafetyError(result: DeleteSafetyError) {
return {
error: "Delete operation not allowed",
reason: result.reason,
code: result.code,
message: "Delete operation blocked by safety configuration"
};
}
// Board focus validation functions
export interface BoardFocusError {
allowed: false;
reason: string;
code: "OUT_OF_FOCUS" | "FOCUS_BOARD_ONLY";
}
export interface BoardFocusSuccess {
allowed: true;
}
export type BoardFocusResult = BoardFocusError | BoardFocusSuccess;
/**
* Check if access to a specific board is allowed based on board focus configuration
*/
export function validateBoardAccess(boardId: number): BoardFocusResult {
const { boardFocus } = config;
// If board focus is not enabled, allow all boards
if (!boardFocus.enabled || !boardFocus.boardId) {
return { allowed: true };
}
// Check if the requested board matches the focused board
if (boardId !== boardFocus.boardId) {
return {
allowed: false,
reason: `Operations are focused on board ${boardFocus.boardId}. Access to board ${boardId} is outside the current focus scope.`,
code: "OUT_OF_FOCUS"
};
}
return { allowed: true };
}
/**
* Check if board-wide operations are allowed (like creating/listing boards)
*/
export function validateBoardWideOperation(): BoardFocusResult {
const { boardFocus } = config;
// If board focus is enabled, streamline to focus board operations only
if (boardFocus.enabled && boardFocus.boardId) {
return {
allowed: false,
reason: `Operations are streamlined for board ${boardFocus.boardId}. Use get_board with board_id ${boardFocus.boardId} to access board details.`,
code: "FOCUS_BOARD_ONLY"
};
}
return { allowed: true };
}
/**
* Create a standardized error for board focus scope violations
*/
export function createBoardFocusError(result: BoardFocusError) {
return {
error: "Operation outside board focus scope",
reason: result.reason,
code: result.code,
message: "Operation not available in board focus mode"
};
}
/**
* Get the focused board ID if board focus is enabled
*/
export function getFocusedBoardId(): number | null {
const { boardFocus } = config;
return boardFocus.enabled && boardFocus.boardId ? boardFocus.boardId : null;
}
/**
* Check if board focus mode is enabled
*/
export function isBoardFocusEnabled(): boolean {
const { boardFocus } = config;
return boardFocus.enabled && !!boardFocus.boardId;
}