PLUGIN

Report

4 Downloads

Converts Markdown table to Excel file.

src / toolsProvider.ts

import { tool, Tool, ToolsProviderController } from "@lmstudio/sdk";
import { z } from "zod";
import fs from "fs";
import fsp from "fs/promises";
import { join } from "path";
import * as ExcelJS from "exceljs";


export async function toolsProvider(ctl: ToolsProviderController): Promise<Tool[]> {
  const tools: Tool[] = [];

  const excelTool = tool({
    name: "GenerateExcel",
    description:
      "Convert Markdown table to Excel file.",
    parameters: {
      text: z.string().describe("Markdown table content to include in the Excel file"),
      filename: z.string().optional().describe("Optional filename"),
    },
    implementation: async ({ text, filename }, { status, warn }) => {
      try {
        status("Generating Excel...");
        const workingDir = ctl.getWorkingDirectory();
        await fsp.mkdir(workingDir, { recursive: true });       

        const fileExists = (p: string) => {
          try {
            return fs.existsSync(p);
          } catch {
            return false;
          }
        };

        const timestamp = Date.now();
        let fileName = filename || `ai-output-${timestamp}.xlsx`;
        
        // Ensure filename has .xlsx extension
        if (filename && !filename.toLowerCase().endsWith('.xlsx')) {
          fileName = `${filename}.xlsx`;
        }
        
        const filePath = join(workingDir, fileName);

        // Parse markdown table and convert to Excel
        const lines = text.split('\n').filter(line => line.trim() !== '');
        const tableRows = lines.filter(line => line.includes('|'));
        
        if (tableRows.length === 0) {
          throw new Error("No table data found in the provided text");
        }

        // Parse table data and filter out separator rows (lines with dashes)
        const data = tableRows
          .filter(row => {
            // Check if row is a separator (contains only dashes, spaces, and pipes)
            const content = row.replace(/[\|\s]/g, ''); // Remove pipes and spaces
            return !/^-+$/.test(content); // Filter out rows that are only dashes
          })
          .map(row => 
            row.split('|').map(cell => cell.trim()).filter(cell => cell !== '')
          );

        if (data.length === 0) {
          throw new Error("No valid table data found after filtering separators");
        }

        // Create workbook and worksheet
        const workbook = new ExcelJS.Workbook();
        const worksheet = workbook.addWorksheet("Sheet1");

        // Add data to worksheet
        data.forEach((row, index) => {
          worksheet.addRow(row);
          // Style header row if it's the first row
          if (index === 0) {
            const headerRow = worksheet.getRow(1);
            headerRow.eachCell((cell) => {
              cell.font = { bold: true };
              cell.fill = {
                type: 'pattern',
                pattern: 'solid',
                fgColor: { argb: 'FFE0E0E0' }
              };
            });
          }
        });

        // Auto-fit columns
        worksheet.columns.forEach(column => {
          let maxLength = 0;
          if (column.eachCell) {
            column.eachCell({ includeEmpty: true }, (cell) => {
              const cellValue = cell.value ? cell.value.toString() : '';
              maxLength = Math.max(maxLength, cellValue.length);
            });
          }
          column.width = Math.min(Math.max(maxLength + 2, 10), 50);
        });

        // Write file
        await workbook.xlsx.writeFile(filePath);

        status(`Excel saved as ${fileName}`);
        return { filename: fileName, path: filePath };
      } catch (err: any) {
        console.error("Excel generation failed:", err);
        warn(`Failed to generate Excel: ${err?.message ?? String(err)}`);
        return { error: err?.message ?? String(err) };
      }
    },
  });

  tools.push(excelTool);
  return tools;
}