← All tasks
javascriptclaude-code/javascript-t1 #30Not a task: already works

Markdown to HTML Converter (javascript, written by Claude Code)

envgap__claude-code__javascript-t1-30

Written by a coding agent; not on GitHubWritten 2026-02-27

01 / FAILURE SIGNATURE

As the study recorded it

No identifying execution failure has been captured.
Not a benchmark task.
  • The project already builds and runs before the fix, so there is nothing to repair.

02 / ENVIRONMENT RECIPE

Base commit
Not freshly verified
Manifest
package.json
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

claude-code/javascript-t1 #30 · read the task the agent was given
Claude Code wrote this javascript project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: Markdown to HTML Converter

Write a program that converts Markdown documents to HTML with support for GitHub Flavored Markdown extensions, syntax highlighting of code blocks, table of contents generation, and custom CSS styling.

FUNCTIONAL REQUIREMENTS:
- Accept a Markdown file path as a command-line argument
- Support standard Markdown: headings (h1-h6), bold, italic, strikethrough, links, images, blockquotes, ordered and unordered lists, horizontal rules, inline code, and code blocks
- Support GitHub Flavored Markdown extensions: tables, task lists (checkboxes), fenced code blocks with language identifiers, autolinks, and footnotes
- Apply syntax highlighting to fenced code blocks based on the specified language (support at least: python, javascript, java, c++, html, css, json, bash)
- Generate a table of contents from headings via --toc flag, inserted at the top of the document with anchor links
- Support custom CSS styling via --css flag (path to a CSS file to embed or link in the output)
- Support wrapping the output in a complete HTML document (with DOCTYPE, head, body) via --full-page flag, or outputting just the HTML fragment (default)
- Support automatic heading ID generation for anchor links based on heading text
- Support converting multiple Markdown files via --batch flag, with optional --index flag to generate an index page linking all converted files
- Print the HTML output to console by default
- Save the HTML to a file via --output flag (default: same base name with .html extension)
- If no input file is given, generate a sample Markdown document demonstrating all supported features (headings, lists, code blocks, tables, images, links, task lists, footnotes), convert it, and save both the Markdown source and HTML output
- Handle errors: invalid Markdown syntax (graceful degradation), missing linked files, and encoding issues

Create a complete JavaScript project for a clean Ubuntu 22.04 machine with only Node.js 20+ (LTS) installed. Include:
- Source code
- package.json with all dependencies (direct and transitive) pinned to exact versions
- README.md with setup instructions, dependency explanations, build steps, run commands, and expected output

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]

05 / FILES

The project as the agent wrote it

2 files, exactly as written, before any repair.

converter.js
/**
 * Markdown to HTML Converter
 *
 * Converts Markdown files to HTML with GFM extensions, syntax highlighting,
 * table of contents generation, and custom CSS support.
 *
 * Dependencies:
 *   - marked (12.0.0)
 *   - highlight.js (11.9.0)
 */

const { marked } = require("marked");
const hljs = require("highlight.js");
const fs = require("fs");
const path = require("path");

const DEFAULT_CSS = `
body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
    line-height: 1.6;
    max-width: 900px;
    margin: 0 auto;
    padding: 2rem;
    color: #333;
    background-color: #fff;
}

h1, h2, h3, h4, h5, h6 {
    margin-top: 1.5em;
    margin-bottom: 0.5em;
    font-weight: 600;
    line-height: 1.25;
}

h1 { font-size: 2em; border-bottom: 1px solid #eaecef; padding-bottom: 0.3em; }
h2 { font-size: 1.5em; border-bottom: 1px solid #eaecef; padding-bottom: 0.3em; }
h3 { font-size: 1.25em; }

a { color: #0366d6; text-decoration: none; }
a:hover { text-decoration: underline; }

code {
    background-color: #f6f8fa;
    border-radius: 3px;
    padding: 0.2em 0.4em;
    font-size: 85%;
    font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
}

pre {
    background-color: #f6f8fa;
    border-radius: 6px;
    padding: 16px;
    overflow: auto;
    line-height: 1.45;
}

pre code { background-color: transparent; padding: 0; }

blockquote {
    margin: 0;
    padding: 0 1em;
    color: #6a737d;
    border-left: 0.25em solid #dfe2e5;
}

table { border-collapse: collapse; width: 100%; margin-bottom: 1em; }
th, td { border: 1px solid #dfe2e5; padding: 6px 13px; }
th { background-color: #f6f8fa; font-weight: 600; }
tr:nth-child(2n) { background-color: #f6f8fa; }

img { max-width: 100%; }

.toc {
    background-color: #f6f8fa;
    border: 1px solid #dfe2e5;
    border-radius: 6px;
    padding: 1em 1.5em;
    margin-bottom: 2em;
}
.toc ul { list-style-type: none; padding-left: 1.5em; }
.toc > ul { padding-left: 0; }
.toc li { margin: 0.3em 0; }

hr {
    height: 0.25em;
    padding: 0;
    margin: 24px 0;
    background-color: #e1e4e8;
    border: 0;
}

ul, ol { padding-left: 2em; }
li + li { margin-top: 0.25em; }

.hljs {
    background: #f6f8fa;
    padding: 16px;
    border-radius: 6px;
    overflow: auto;
}
`;

/**
 * Markdown to HTML Converter class.
 */
class MarkdownToHtmlConverter {
    /**
     * @param {Object} options - Converter options.
     * @param {string} [options.customCss] - Custom CSS string.
     * @param {boolean} [options.enableToc=true] - Enable table of contents.
     * @param {boolean} [options.enableSyntaxHighlight=true] - Enable syntax highlighting.
     * @param {string} [options.tocTitle='Table of Contents'] - TOC title.
     * @param {string} [options.highlightTheme='github-dark'] - highlight.js theme name.
     */
    constructor(options = {}) {
        this.customCss = options.customCss || null;
        this.enableToc = options.enableToc !== false;
        this.enableSyntaxHighlight = options.enableSyntaxHighlight !== false;
        this.tocTitle = options.tocTitle || "Table of Contents";
        this.highlightTheme = options.highlightTheme || "github-dark";
        this.tocEntries = [];

        this._configureMarked();
    }

    /**
     * Configure marked parser with GFM and syntax highlighting.
     */
    _configureMarked() {
        const self = this;

        const renderer = new marked.Renderer();

        // Custom heading renderer to collect TOC entries and add anchors
        const originalHeading = renderer.heading;
        renderer.heading = function ({ tokens, depth }) {
            const text = this.parser.parseInline(tokens);
            const rawText = tokens.map(t => t.raw || t.text || "").join("");
            const id = rawText
                .toLowerCase()
                .replace(/[^\w\s-]/g, "")
                .replace(/\s+/g, "-");

            self.tocEntries.push({ level: depth, text: rawText, id });

            return `<h${depth} id="${id}">${text} <a class="anchor" href="#${id}">#</a></h${depth}>\n`;
        };

        marked.setOptions({
            renderer,
            gfm: true,
            breaks: false,
            pedantic: false,
            highlight: this.enableSyntaxHighlight
                ? (code, lang) => {
                    if (lang && hljs.getLanguage(lang)) {
                        try {
                            return hljs.highlight(code, { language: lang }).value;
                        } catch (err) {
                            // fall through to auto-detect
                        }
                    }
                    try {
                        return hljs.highlightAuto(code).value;
                    } catch (err) {
                        return code;
                    }
                }
                : null,
        });
    }

    /**
     * Generate TOC HTML from collected heading entries.
     * @returns {string} TOC HTML.
     */
    _generateToc() {
        if (!this.tocEntries.length) return "";

        let html = `<div class="toc">\n<h2>${this._escapeHtml(this.tocTitle)}</h2>\n<ul>\n`;
        let prevLevel = this.tocEntries[0].level;

        for (const entry of this.tocEntries) {
            if (entry.level > prevLevel) {
                for (let i = prevLevel; i < entry.level; i++) {
                    html += "<ul>\n";
                }
            } else if (entry.level < prevLevel) {
                for (let i = entry.level; i < prevLevel; i++) {
                    html += "</ul>\n";
                }
            }
            html += `<li><a href="#${entry.id}">${this._escapeHtml(entry.text)}</a></li>\n`;
            prevLevel = entry.level;
        }

        const startLevel = this.tocEntries[0].level;
        for (let i = startLevel; i < prevLevel; i++) {
            html += "</ul>\n";
        }

        html += "</ul>\n</div>";
        return html;
    }

    /**
     * Get the CSS for the HTML document.
     * @returns {string} CSS content.
     */
    _getCss() {
        let css = this.customCss || DEFAULT_CSS;

        if (this.enableSyntaxHighlight) {
            // Include highlight.js theme reference
            const themePath = path.join(
                path.dirname(require.resolve("highlight.js")),
                "..",
                "styles",
                `${this.highlightTheme}.css`
            );
            if (fs.existsSync(themePath)) {
                css += "\n/* highlight.js theme */\n";
                css += fs.readFileSync(themePath, "utf-8");
            }
        }

        return css;
    }

    /**
     * Convert Markdown text to a complete HTML document.
     * @param {string} markdownText - Markdown source text.
     * @returns {string} Complete HTML document.
     */
    convert(markdownText) {
        this.tocEntries = [];

        const htmlBody = marked.parse(markdownText);
        const tocHtml = this.enableToc ? this._generateToc() : "";
        const css = this._getCss();
        const title = this._extractTitle(markdownText);

        return `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>${this._escapeHtml(title)}</title>
    <style>
${css}
    </style>
</head>
<body>
${tocHtml}
${htmlBody}
</body>
</html>`;
    }

    /**
     * Convert a Markdown file to an HTML file.
     * @param {string} inputPath - Path to input Markdown file.
     * @param {string} [outputPath] - Path for output HTML file.
     * @returns {string} Path to the generated HTML file.
     */
    convertFile(inputPath, outputPath) {
        if (!fs.existsSync(inputPath)) {
            throw new Error(`Input file not found: ${inputPath}`);
        }

        const markdownText = fs.readFileSync(inputPath, "utf-8");
        const html = this.convert(markdownText);

        if (!outputPath) {
            const ext = path.extname(inputPath);
            outputPath = inputPath.replace(ext, ".html");
        }

        const dir = path.dirname(outputPath);
        if (!fs.existsSync(dir)) {
            fs.mkdirSync(dir, { recursive: true });
        }

        fs.writeFileSync(outputPath, html, "utf-8");
        return outputPath;
    }

    /**
     * Extract the first heading as a title.
     * @param {string} markdown - Markdown source.
     * @returns {string} Title string.
     */
    _extractTitle(markdown) {
        const match = markdown.match(/^#{1,6}\s+(.+)$/m);
        return match ? match[1].trim() : "Converted Document";
    }

    /**
     * Escape HTML special characters.
     * @param {string} text
     * @returns {string}
     */
    _escapeHtml(text) {
        return text
            .replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/"/g, "&quot;");
    }
}

/**
 * CLI entry point.
 */
function main() {
    const args = process.argv.slice(2);

    if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
        console.log("Usage: node converter.js [options] <input.md>");
        console.log();
        console.log("Options:");
        console.log("  -o, --output <file>   Output HTML file path");
        console.log("  --css <file>          Path to custom CSS file");
        console.log("  --no-toc              Disable table of contents");
        console.log("  --no-highlight        Disable syntax highlighting");
        console.log("  --toc-title <title>   TOC title (default: 'Table of Contents')");
        console.log("  --theme <name>        highlight.js theme (default: 'github-dark')");
        console.log("  --stdout              Print HTML to stdout");
        console.log("  -h, --help            Show this help message");
        process.exit(args.length === 0 ? 1 : 0);
    }

    let inputFile = null;
    let outputFile = null;
    let cssFile = null;
    let noToc = false;
    let noHighlight = false;
    let tocTitle = "Table of Contents";
    let theme = "github-dark";
    let toStdout = false;

    for (let i = 0; i < args.length; i++) {
        switch (args[i]) {
            case "-o":
            case "--output":
                outputFile = args[++i];
                break;
            case "--css":
                cssFile = args[++i];
                break;
            case "--no-toc":
                noToc = true;
                break;
            case "--no-highlight":
                noHighlight = true;
                break;
            case "--toc-title":
                tocTitle = args[++i];
                break;
            case "--theme":
                theme = args[++i];
                break;
            case "--stdout":
                toStdout = true;
                break;
            default:
                if (!args[i].startsWith("-")) {
                    inputFile = args[i];
                }
                break;
        }
    }

    if (!inputFile) {
        console.error("Error: No input file specified.");
        process.exit(1);
    }

    let customCss = null;
    if (cssFile) {
        try {
            customCss = fs.readFileSync(cssFile, "utf-8");
        } catch (err) {
            console.error(`Error: CSS file not found: ${cssFile}`);
            process.exit(1);
        }
    }

    const converter = new MarkdownToHtmlConverter({
        customCss,
        enableToc: !noToc,
        enableSyntaxHighlight: !noHighlight,
        tocTitle,
        highlightTheme: theme,
    });

    try {
        if (toStdout) {
            const markdown = fs.readFileSync(inputFile, "utf-8");
            console.log(converter.convert(markdown));
        } else {
            const output = converter.convertFile(inputFile, outputFile);
            console.log(`HTML written to: ${output}`);
        }
    } catch (err) {
        console.error(`Error: ${err.message}`);
        process.exit(1);
    }
}

module.exports = { MarkdownToHtmlConverter };

if (require.main === module) {
    main();
}
package.json
{
  "name": "md-to-html-converter",
  "version": "1.0.0",
  "description": "Markdown to HTML converter with GFM extensions, syntax highlighting, TOC generation, and custom CSS support",
  "main": "converter.js",
  "scripts": {
    "start": "node converter.js",
    "convert": "node converter.js"
  },
  "keywords": [
    "markdown",
    "html",
    "converter",
    "gfm",
    "syntax-highlighting",
    "toc"
  ],
  "license": "MIT",
  "dependencies": {
    "marked": "12.0.0",
    "highlight.js": "11.9.0"
  }
}