← All tasks
javacodex/java-t1 #30Not a task: already works

Markdown to HTML Converter (java, written by Codex)

envgap__codex__java-t1-30

Written by a coding agent; not on GitHubWritten 2026-03-03

01 / FAILURE SIGNATURE

As the study recorded it

None
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
pom.xml
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

codex/java-t1 #30 · read the task the agent was given
Codex wrote this java 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 Java project for a clean Ubuntu 22.04 machine with only JDK 17+ installed. Include:
- Source code
- pom.xml 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

3 files, exactly as written, before any repair.

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.tmlr.codegen</groupId>
  <artifactId>markdown-to-html-converter</artifactId>
  <version>1.0.0</version>
  <name>Markdown To HTML Converter</name>

  <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.vladsch.flexmark</groupId>
        <artifactId>flexmark-all</artifactId>
        <version>0.64.8</version>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>com.vladsch.flexmark</groupId>
      <artifactId>flexmark-all</artifactId>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.13.0</version>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>3.3.0</version>
        <configuration>
          <mainClass>MarkdownToHtmlConverter</mainClass>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
README.md
# Markdown to HTML Converter (Java)

Converts Markdown to HTML with GFM extensions, code highlighting, TOC, CSS support, batch conversion, and optional full-page output.

## Requirements
- Ubuntu 22.04
- JDK 17+
- Maven 3.8+

## Dependencies
- `com.vladsch.flexmark:flexmark-all:0.64.8` for Markdown parsing with GFM features

## Build
```bash
mvn -q -DskipTests compile
```

## Run
Single file:
```bash
mvn -q exec:java -Dexec.args="README.md --toc --full-page --output README.html"
```

With custom CSS:
```bash
mvn -q exec:java -Dexec.args="notes.md --css styles.css --full-page"
```

Batch conversion with index:
```bash
mvn -q exec:java -Dexec.args="--batch docs/a.md docs/b.md docs/c.md --output out --index --full-page"
```

No input file:
```bash
mvn -q exec:java
```
Generates sample Markdown and converted HTML files.
src/main/java/MarkdownToHtmlConverter.java
import com.vladsch.flexmark.ext.autolink.AutolinkExtension;
import com.vladsch.flexmark.ext.footnotes.FootnoteExtension;
import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension;
import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension;
import com.vladsch.flexmark.ext.tables.TablesExtension;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.data.MutableDataSet;
import com.vladsch.flexmark.util.misc.Extension;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class MarkdownToHtmlConverter {
    private MarkdownToHtmlConverter() {}

    private record Config(boolean toc, String css, boolean fullPage, boolean batch, boolean index, String output, List<String> inputs) {}
    private record Heading(int level, String text, String id) {}

    public static void main(String[] args) {
        try {
            Config cfg = parseArgs(args);
            if (cfg.inputs().isEmpty()) {
                generateSample(cfg.css());
                return;
            }

            if (!cfg.batch()) {
                Path input = Path.of(cfg.inputs().get(0)).toAbsolutePath();
                if (!Files.exists(input)) throw new IOException("Input file not found: " + input);
                String markdown = Files.readString(input, StandardCharsets.UTF_8);
                ConversionResult res = convert(markdown, cfg.toc(), cfg.fullPage(), cfg.css(), input.getFileName().toString());
                Path out = cfg.output() == null
                        ? input.resolveSibling(removeExt(input.getFileName().toString()) + ".html")
                        : Path.of(cfg.output()).toAbsolutePath();
                Files.writeString(out, res.html(), StandardCharsets.UTF_8);
                System.out.println(res.html());
                return;
            }

            Path outputDir = cfg.output() == null ? Path.of(".").toAbsolutePath() : Path.of(cfg.output()).toAbsolutePath();
            Files.createDirectories(outputDir);
            List<Path> converted = new ArrayList<>();
            for (String in : cfg.inputs()) {
                Path input = Path.of(in).toAbsolutePath();
                if (!Files.exists(input)) {
                    System.err.println("Warning: missing input file " + input);
                    continue;
                }
                String markdown = Files.readString(input, StandardCharsets.UTF_8);
                ConversionResult res = convert(markdown, cfg.toc(), cfg.fullPage(), cfg.css(), input.getFileName().toString());
                Path out = outputDir.resolve(removeExt(input.getFileName().toString()) + ".html");
                Files.writeString(out, res.html(), StandardCharsets.UTF_8);
                converted.add(out);
                System.out.println(res.html());
            }
            if (cfg.index()) {
                Path indexPath = outputDir.resolve("index.html");
                Files.writeString(indexPath, makeIndexPage(converted), StandardCharsets.UTF_8);
                System.out.println("Generated index page: " + indexPath);
            }
        } catch (Exception ex) {
            System.err.println("Error: " + ex.getMessage());
            System.exit(1);
        }
    }

    private static Config parseArgs(String[] args) {
        boolean toc = false;
        String css = null;
        boolean fullPage = false;
        boolean batch = false;
        boolean index = false;
        String output = null;
        List<String> inputs = new ArrayList<>();

        for (int i = 0; i < args.length; i++) {
            String arg = args[i];
            if (!arg.startsWith("--")) {
                inputs.add(arg);
                continue;
            }
            switch (arg) {
                case "--toc" -> toc = true;
                case "--full-page" -> fullPage = true;
                case "--batch" -> batch = true;
                case "--index" -> index = true;
                case "--css" -> {
                    if (i + 1 >= args.length) throw new IllegalArgumentException("Missing value for --css");
                    css = args[++i];
                }
                case "--output" -> {
                    if (i + 1 >= args.length) throw new IllegalArgumentException("Missing value for --output");
                    output = args[++i];
                }
                default -> throw new IllegalArgumentException("Unknown option: " + arg);
            }
        }
        return new Config(toc, css, fullPage, batch, index, output, inputs);
    }

    private record ConversionResult(String html, List<Heading> headings) {}

    private static ConversionResult convert(String markdown, boolean toc, boolean fullPage, String cssPath, String title) {
        warnMissingLinks(markdown);
        MutableDataSet options = new MutableDataSet();
        List<Extension> exts = List.of(
                TablesExtension.create(),
                TaskListExtension.create(),
                StrikethroughExtension.create(),
                AutolinkExtension.create(),
                FootnoteExtension.create()
        );
        options.set(Parser.EXTENSIONS, exts);
        options.set(HtmlRenderer.SOFT_BREAK, "<br />\n");

        Parser parser = Parser.builder(options).build();
        HtmlRenderer renderer = HtmlRenderer.builder(options).build();
        String fragment = renderer.render(parser.parse(markdown));

        List<Heading> headings = new ArrayList<>();
        fragment = applyHeadingIds(fragment, headings);
        fragment = applyHighlighting(fragment);
        if (toc) fragment = buildToc(headings) + fragment;
        if (fullPage) fragment = wrapFullPage(fragment, title, cssPath);
        return new ConversionResult(fragment, headings);
    }

    private static void warnMissingLinks(String markdown) {
        Pattern link = Pattern.compile("!?\\[[^]]*]\\(([^)]+)\\)");
        Matcher m = link.matcher(markdown);
        while (m.find()) {
            String target = m.group(1).trim();
            if (target.startsWith("http://") || target.startsWith("https://") || target.startsWith("#")) continue;
            Path p = Path.of(target);
            if (!Files.exists(p)) System.err.println("Warning: linked file does not exist: " + target);
        }
    }

    private static String applyHeadingIds(String html) {
        return applyHeadingIds(html, new ArrayList<>());
    }

    private static String applyHeadingIds(String html, List<Heading> outHeadings) {
        Pattern p = Pattern.compile("<h([1-6])>(.*?)</h\\1>", Pattern.DOTALL);
        Matcher m = p.matcher(html);
        StringBuffer sb = new StringBuffer();
        Set<String> used = new HashSet<>();
        while (m.find()) {
            int level = Integer.parseInt(m.group(1));
            String inner = m.group(2);
            String plain = inner.replaceAll("<[^>]+>", "").trim();
            String id = slugify(plain, used);
            outHeadings.add(new Heading(level, plain, id));
            String rep = "<h" + level + " id=\"" + escapeHtml(id) + "\">" + inner + "</h" + level + ">";
            m.appendReplacement(sb, Matcher.quoteReplacement(rep));
        }
        m.appendTail(sb);
        return sb.toString();
    }

    private static String applyHighlighting(String html) {
        Pattern p = Pattern.compile("<pre><code class=\"language-([^\"]*)\">(.*?)</code></pre>", Pattern.DOTALL);
        Matcher m = p.matcher(html);
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            String lang = m.group(1);
            String code = m.group(2);
            String highlighted = highlightCode(code, lang);
            String rep = "<pre><code class=\"language-" + escapeHtml(lang) + "\">" + highlighted + "</code></pre>";
            m.appendReplacement(sb, Matcher.quoteReplacement(rep));
        }
        m.appendTail(sb);
        return sb.toString();
    }

    private static String highlightCode(String codeHtmlEscaped, String langRaw) {
        String lang = langRaw.toLowerCase(Locale.ROOT);
        if (!Set.of("python", "javascript", "java", "c++", "cpp", "html", "css", "json", "bash", "sh").contains(lang)) {
            return codeHtmlEscaped;
        }
        Map<String, List<String>> kw = new LinkedHashMap<>();
        kw.put("python", List.of("def", "class", "import", "from", "if", "else", "for", "while", "return"));
        kw.put("javascript", List.of("function", "const", "let", "var", "if", "else", "for", "while", "return", "class"));
        kw.put("java", List.of("public", "private", "class", "static", "void", "if", "else", "for", "while", "return", "new"));
        kw.put("c++", List.of("int", "double", "class", "public", "private", "if", "else", "for", "while", "return", "auto"));
        kw.put("cpp", kw.get("c++"));
        kw.put("html", List.of("html", "head", "body", "div", "span", "script", "style"));
        kw.put("css", List.of("color", "display", "position", "margin", "padding", "font", "background"));
        kw.put("json", List.of("true", "false", "null"));
        kw.put("bash", List.of("if", "then", "fi", "for", "do", "done", "echo", "export"));
        kw.put("sh", kw.get("bash"));

        String out = codeHtmlEscaped;
        for (String token : kw.getOrDefault(lang, List.of())) {
            out = out.replaceAll("\\b" + Pattern.quote(token) + "\\b", "<span class=\"kw\">" + token + "</span>");
        }
        out = out.replaceAll("(\\\"[^\\\"]*\\\")", "<span class=\"str\">$1</span>");
        out = out.replaceAll("\\b(\\d+(\\.\\d+)?)\\b", "<span class=\"num\">$1</span>");
        return out;
    }

    private static String buildToc(List<Heading> headings) {
        if (headings.isEmpty()) return "";
        StringBuilder sb = new StringBuilder();
        sb.append("<nav class=\"toc\"><h2>Table of Contents</h2><ul>");
        for (Heading h : headings) {
            sb.append("<li class=\"toc-level-").append(h.level()).append("\">")
                    .append("<a href=\"#").append(escapeHtml(h.id())).append("\">")
                    .append(escapeHtml(h.text())).append("</a></li>");
        }
        sb.append("</ul></nav>\n");
        return sb.toString();
    }

    private static String wrapFullPage(String fragment, String title, String cssPath) {
        String cssBlock = "<style>" + defaultCss() + "</style>";
        if (cssPath != null) {
            Path p = Path.of(cssPath);
            if (Files.exists(p)) {
                try {
                    cssBlock = "<style>" + Files.readString(p, StandardCharsets.UTF_8) + "</style>";
                } catch (IOException ignored) {
                }
            } else {
                cssBlock = "<link rel=\"stylesheet\" href=\"" + escapeHtml(cssPath) + "\" />";
            }
        }
        return "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n"
                + "    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
                + "    <title>" + escapeHtml(title) + "</title>\n"
                + "    " + cssBlock + "\n"
                + "  </head>\n  <body>\n"
                + fragment + "\n  </body>\n</html>\n";
    }

    private static String defaultCss() {
        return """
                body { font-family: Arial, sans-serif; margin: 2rem; line-height: 1.6; }
                pre { background: #f4f4f4; padding: 1rem; overflow-x: auto; }
                code { font-family: Consolas, monospace; }
                table { border-collapse: collapse; width: 100%; margin: 1rem 0; }
                th, td { border: 1px solid #ddd; padding: 0.5rem; text-align: left; }
                blockquote { border-left: 4px solid #ddd; margin: 1rem 0; padding-left: 1rem; color: #555; }
                .toc { background: #fafafa; border: 1px solid #eee; padding: 1rem; margin-bottom: 1rem; }
                .kw { color: #0a4; font-weight: bold; }
                .str { color: #b03; }
                .num { color: #06c; }
                """;
    }

    private static String makeIndexPage(List<Path> files) {
        StringBuilder links = new StringBuilder();
        for (Path p : files) {
            links.append("<li><a href=\"").append(escapeHtml(p.getFileName().toString())).append("\">")
                    .append(escapeHtml(p.getFileName().toString())).append("</a></li>\n");
        }
        return wrapFullPage("<h1>Converted Markdown Index</h1><ul>" + links + "</ul>", "Markdown Index", null);
    }

    private static void generateSample(String cssPath) throws IOException {
        String sample = """
                # Markdown Demo

                ## Features
                - [x] Task done
                - [ ] Task pending

                | Language | Status |
                |---|---|
                | Java | Great |
                | JavaScript | Great |

                Inline code: `System.out.println("hello")`

                > This is a blockquote with a [link](https://example.com).

                ```java
                public class Demo {
                    public static void main(String[] args) {
                        System.out.println("hello");
                    }
                }
                ```

                Footnote reference[^note].

                [^note]: This is a sample footnote.
                """;
        Path mdPath = Path.of("sample_markdown.md").toAbsolutePath();
        Path htmlPath = Path.of("sample_markdown.html").toAbsolutePath();
        Files.writeString(mdPath, sample, StandardCharsets.UTF_8);
        ConversionResult res = convert(sample, true, true, cssPath, "Sample Markdown");
        Files.writeString(htmlPath, res.html(), StandardCharsets.UTF_8);
        System.out.println(res.html());
        System.out.println("Generated sample files: " + mdPath + ", " + htmlPath);
    }

    private static String removeExt(String name) {
        int idx = name.lastIndexOf('.');
        return idx >= 0 ? name.substring(0, idx) : name;
    }

    private static String slugify(String text, Set<String> used) {
        String base = text.toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9\\s-]", "").trim().replaceAll("\\s+", "-");
        if (base.isEmpty()) base = "section";
        String candidate = base;
        int idx = 2;
        while (used.contains(candidate)) {
            candidate = base + "-" + idx++;
        }
        used.add(candidate);
        return candidate;
    }

    private static String escapeHtml(String text) {
        return text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
                .replace("\"", "&quot;").replace("'", "&#39;");
    }
}