Markdown to HTML Converter (cpp, written by Claude Code)
envgap__claude-code__cpp-t2-30
Written by a coding agent; not on GitHubWritten 2026-02-28
01 / FAILURE SIGNATURE
As the study recorded it
GCC 11 aggregate init with default member initializer in default param
Not a benchmark task.
- In a clean container the reported failure did not reproduce, or the known fix did not make the project run.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
CMakeLists.txt- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
claude-code/cpp-t2 #30 · read the task the agent was given
Claude Code wrote this cpp project from the task below. It does not run 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 C++ project for a clean Ubuntu 22.04 machine with only G++ 12+ and CMake 3.22+ installed. Include: - Source code - CMakeLists.txt 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.
CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(md_converter VERSION 1.0.0 LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# ---------------------------------------------------------------------------
# Dependencies
# ---------------------------------------------------------------------------
# md4c v0.5.2 — Fast Markdown parser in C
# nlohmann/json v3.11.3 — header-only JSON library
# ---------------------------------------------------------------------------
include(FetchContent)
FetchContent_Declare(
md4c
GIT_REPOSITORY https://github.com/mity/md4c.git
GIT_TAG release-0.5.2
GIT_SHALLOW TRUE
)
FetchContent_Declare(
nlohmann_json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.3
GIT_SHALLOW TRUE
)
set(JSON_BuildTests OFF CACHE BOOL "" FORCE)
set(JSON_Install OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(md4c nlohmann_json)
# ---------------------------------------------------------------------------
# Executable
# ---------------------------------------------------------------------------
add_executable(md_converter converter.cpp)
target_include_directories(md_converter PRIVATE
${md4c_SOURCE_DIR}/src
)
target_link_libraries(md_converter PRIVATE
md4c
md4c-html
nlohmann_json::nlohmann_json
)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(md_converter PRIVATE -Wall -Wextra -Wpedantic)
endif()
converter.cpp
/**
* Markdown to HTML Converter (C++ - Trial 2)
*
* Converts Markdown files to HTML with table of contents generation,
* custom CSS support, and JSON-based configuration using md4c.
*
* Dependencies:
* - md4c (0.5.2) - Fast Markdown parser in C
* - nlohmann/json (3.11.3) - JSON configuration and metadata
*/
#include <md4c-html.h>
#include <nlohmann/json.hpp>
#include <algorithm>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>
#include <vector>
namespace fs = std::filesystem;
using json = nlohmann::json;
static const std::string DEFAULT_CSS = R"(
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, 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; }
)";
struct TocEntry {
int level;
std::string text;
std::string id;
};
static std::string escape_html(const std::string& s) {
std::string out;
out.reserve(s.size());
for (char c : s) {
switch (c) {
case '&': out += "&"; break;
case '<': out += "<"; break;
case '>': out += ">"; break;
case '"': out += """; break;
default: out += c; break;
}
}
return out;
}
static std::string to_slug(const std::string& text) {
std::string slug;
for (char c : text) {
if (std::isalnum(static_cast<unsigned char>(c)))
slug += static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
else if (c == ' ' || c == '\t')
slug += '-';
}
return slug;
}
static std::string read_file(const std::string& path) {
std::ifstream ifs(path, std::ios::binary);
if (!ifs) throw std::runtime_error("Cannot open file: " + path);
std::ostringstream ss;
ss << ifs.rdbuf();
return ss.str();
}
static void write_file(const std::string& path, const std::string& content) {
auto parent = fs::path(path).parent_path();
if (!parent.empty()) fs::create_directories(parent);
std::ofstream ofs(path, std::ios::binary);
if (!ofs) throw std::runtime_error("Cannot write file: " + path);
ofs << content;
}
// md4c callback: appends HTML to the output string
static void md4c_output_callback(const MD_CHAR* data, MD_SIZE size, void* userdata) {
auto* output = static_cast<std::string*>(userdata);
output->append(data, size);
}
static std::vector<TocEntry> extract_headings(const std::string& markdown) {
std::vector<TocEntry> entries;
std::regex heading_re(R"(^(#{1,6})\s+(.+)$)");
std::istringstream stream(markdown);
std::string line;
while (std::getline(stream, line)) {
std::smatch match;
if (std::regex_match(line, match, heading_re)) {
int level = static_cast<int>(match[1].str().size());
std::string text = match[2].str();
while (!text.empty() && std::isspace(static_cast<unsigned char>(text.back())))
text.pop_back();
entries.push_back({level, text, to_slug(text)});
}
}
return entries;
}
static std::string generate_toc(const std::vector<TocEntry>& entries,
const std::string& toc_title) {
if (entries.empty()) return "";
std::ostringstream toc;
toc << "<div class=\"toc\">\n<h2>" << escape_html(toc_title) << "</h2>\n<ul>\n";
int prev = entries.front().level;
for (const auto& e : entries) {
if (e.level > prev) for (int i = prev; i < e.level; ++i) toc << "<ul>\n";
else if (e.level < prev) for (int i = e.level; i < prev; ++i) toc << "</ul>\n";
toc << "<li><a href=\"#" << e.id << "\">" << escape_html(e.text) << "</a></li>\n";
prev = e.level;
}
int start = entries.front().level;
for (int i = start; i < prev; ++i) toc << "</ul>\n";
toc << "</ul>\n</div>\n";
return toc.str();
}
static std::string extract_title(const std::string& markdown) {
std::regex title_re(R"(^#{1,6}\s+(.+)$)");
std::istringstream stream(markdown);
std::string line;
while (std::getline(stream, line)) {
std::smatch match;
if (std::regex_match(line, match, title_re)) {
std::string t = match[1].str();
while (!t.empty() && std::isspace(static_cast<unsigned char>(t.back()))) t.pop_back();
return t;
}
}
return "Converted Document";
}
class MarkdownToHtmlConverter {
public:
struct Config {
bool enable_toc = true;
std::string toc_title = "Table of Contents";
std::string custom_css_path;
};
explicit MarkdownToHtmlConverter(Config cfg = {}) : config_(std::move(cfg)) {
if (!config_.custom_css_path.empty()) {
try { custom_css_ = read_file(config_.custom_css_path); }
catch (const std::exception& e) {
std::cerr << "Warning: Could not load custom CSS: " << e.what() << "\n";
}
}
}
std::string convert(const std::string& markdown) const {
// Use md4c to render HTML
std::string html_body;
unsigned parser_flags = MD_FLAG_TABLES | MD_FLAG_STRIKETHROUGH |
MD_FLAG_PERMISSIVEAUTOLINKS | MD_FLAG_TASKLISTS |
MD_FLAG_PERMISSIVEURLAUTOLINKS;
unsigned renderer_flags = MD_HTML_FLAG_SKIP_UTF8_BOM;
int ret = md_html(markdown.c_str(), static_cast<MD_SIZE>(markdown.size()),
md4c_output_callback, &html_body,
parser_flags, renderer_flags);
if (ret != 0) {
throw std::runtime_error("md4c parsing failed");
}
std::string toc_html;
if (config_.enable_toc) {
auto headings = extract_headings(markdown);
toc_html = generate_toc(headings, config_.toc_title);
}
const std::string& css = custom_css_.empty() ? DEFAULT_CSS : custom_css_;
std::string title = extract_title(markdown);
std::ostringstream out;
out << "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n"
<< " <meta charset=\"UTF-8\">\n"
<< " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
<< " <title>" << escape_html(title) << "</title>\n"
<< " <style>\n" << css << "\n </style>\n"
<< "</head>\n<body>\n"
<< toc_html << html_body
<< "\n</body>\n</html>";
return out.str();
}
std::string convert_file(const std::string& input_path,
const std::string& output_path_arg = "") const {
std::string md = read_file(input_path);
std::string html = convert(md);
std::string output_path = output_path_arg;
if (output_path.empty()) {
fs::path p(input_path);
p.replace_extension(".html");
output_path = p.string();
}
write_file(output_path, html);
return output_path;
}
std::string get_metadata() const {
json meta;
meta["name"] = "MdConverter";
meta["version"] = "1.0.0";
meta["tocEnabled"] = config_.enable_toc;
meta["tocTitle"] = config_.toc_title;
meta["backend"] = "md4c";
return meta.dump(2);
}
static Config load_config(const std::string& path) {
std::string content = read_file(path);
json j = json::parse(content);
Config cfg;
if (j.contains("enableToc")) cfg.enable_toc = j["enableToc"].get<bool>();
if (j.contains("tocTitle")) cfg.toc_title = j["tocTitle"].get<std::string>();
if (j.contains("customCssPath")) cfg.custom_css_path = j["customCssPath"].get<std::string>();
return cfg;
}
static void generate_default_config(const std::string& path) {
json cfg;
cfg["enableToc"] = true;
cfg["tocTitle"] = "Table of Contents";
cfg["customCssPath"] = "";
write_file(path, cfg.dump(2));
std::cout << "Default configuration written to: " << path << "\n";
}
private:
Config config_;
std::string custom_css_;
};
static void print_usage() {
std::cout << "Usage: md_converter [options] <input.md>\n\n"
<< "Options:\n"
<< " -o, --output <file> Output HTML file path\n"
<< " -c, --config <file> Path to JSON configuration file\n"
<< " --generate-config Generate a default config file\n"
<< " --no-toc Disable table of contents\n"
<< " --stdout Print HTML to stdout\n"
<< " -h, --help Show this help message\n";
}
int main(int argc, char* argv[]) {
std::string input_file, output_file, config_file;
bool gen_config = false, to_stdout = false, no_toc = false;
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg == "-o" || arg == "--output") { if (++i < argc) output_file = argv[i]; }
else if (arg == "-c" || arg == "--config") { if (++i < argc) config_file = argv[i]; }
else if (arg == "--generate-config") gen_config = true;
else if (arg == "--no-toc") no_toc = true;
else if (arg == "--stdout") to_stdout = true;
else if (arg == "-h" || arg == "--help") { print_usage(); return 0; }
else if (arg[0] != '-') input_file = arg;
}
try {
if (gen_config) {
MarkdownToHtmlConverter::generate_default_config(
config_file.empty() ? "md-converter-config.json" : config_file);
return 0;
}
if (input_file.empty()) {
std::cerr << "Error: No input file specified.\n";
print_usage();
return 1;
}
MarkdownToHtmlConverter::Config cfg;
if (!config_file.empty()) cfg = MarkdownToHtmlConverter::load_config(config_file);
if (no_toc) cfg.enable_toc = false;
MarkdownToHtmlConverter converter(cfg);
if (to_stdout) {
std::cout << converter.convert(read_file(input_file)) << "\n";
} else {
std::string out = converter.convert_file(input_file, output_file);
std::cout << "HTML written to: " << out << "\n";
}
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n";
return 1;
}
return 0;
}
README.md
# Markdown to HTML Converter (C++ - Trial 2) A C++ Markdown to HTML converter using md4c for fast Markdown parsing and nlohmann/json for configuration and metadata. ## Dependencies - **md4c** (0.5.2) - Fast C Markdown parser with GFM extensions (tables, strikethrough, autolinks, task lists) - **nlohmann/json** (3.11.3) - Header-only JSON library for configuration loading and metadata output ## Build ```bash cmake -B build -DCMAKE_BUILD_TYPE=Release cmake --build build --parallel ``` ## Usage ```bash # Basic conversion ./build/md_converter input.md # Specify output file ./build/md_converter input.md -o output.html # Use configuration file ./build/md_converter input.md -c config.json # Disable table of contents ./build/md_converter input.md --no-toc # Print to stdout ./build/md_converter input.md --stdout # Generate default configuration ./build/md_converter --generate-config ``` ## Features - Fast Markdown parsing via md4c with GFM extensions - Automatic table of contents generation - JSON-based configuration via nlohmann/json - Custom CSS support - Complete HTML document output