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("&", "&").replace("<", "<").replace(">", ">")
.replace("\"", """).replace("'", "'");
}
}