Skip to content

Getting Started with Flude ​

Welcome to the Fast Layered Universal Document Engine (Flude)! This guide walks you through the real pipeline architecture, environment prerequisites, and a verified "Hello World" compilation using the engine's own mock example project.


📐 Core Pipeline Concept ​

Flude turns source code into a documentation site through a fixed sequence of stages. Nothing here is a generic black box — every stage is a concrete, named component:

A few concrete details worth knowing up front:

  • Flude never re-implements Doxygen's parsing. A *DoxygenCollector (e.g. PyDoxygenCollector, CsDoxygenCollector, JavaDoxygenCollector) shells out to the real doxygen binary as an external subprocess and reads back its XML output (index.xml plus one XML file per compound).
  • The XML is handed to a language-specific parser (CsharpDoxygenParser, JavaDoxygenParser, PythonDoxygenParser, or a legacy C++ shim), selected by engine/ude/parsers/doxygen_router.py.
  • Parsers normalize everything into a ProjectCatalog — a tree of Pydantic models: ProjectCatalog → NamespaceModel → ClassModel → MethodModel / VariableModel / EnumModel / ConstantModel / TypeAliasModel.
  • Rendering is done by one of 16 concrete renderer classes named <Lang><Output><ID>Renderer, where Lang ∈ {Cpp, Cs, Java, Py}, Output ∈ {Html, Hugo}, and ID ∈ {Default, ODA}. Static HTML renders to a flat directory of files; Hugo Markdown renders to nested directories with YAML front matter.
  • Every successful compile run — not just a separate "audit" step — ends by printing a documentation coverage audit table. This isn't optional or opt-in; it's unconditional pipeline behavior.

:::note There is no "RAG JSON" output format in the engine today. The only two supported renderer families are static HTML and Hugo Markdown, each available in a generic Default build and an ODA-conventions (ODA) build, per language. :::


💻 Environment Prerequisites ​

Before running Flude, make sure your host machine has the following available:

🐍 Python Runtime (3.11+) ​

Verify your local Python installation by running:

bash
python --version

The engine's pyproject.toml pins python = ">=3.11,<4.0", so anything older is not supported.

📑 Doxygen ​

Doxygen does the actual source-code extraction; Flude only ever calls it as an external subprocess. Verify that Doxygen is available on your PATH:

bash
doxygen --version

If not installed, download it from the official Doxygen website or install via your system package manager (e.g., sudo apt-get install doxygen or brew install doxygen).

If you'd rather not modify your system PATH, you can instead point the engine at a directory containing the Doxygen executable via the doxygen_path key in your global config (engine/ude/config.py:188-204). At startup, the engine prepends that directory to the process PATH so the doxygen subprocess can be found — this is the only place in the engine that reads or writes an environment variable. There are no UDE_* environment variables that control engine behavior.

📦 Package Managers ​

Ensure either pip or poetry is installed and ready to resolve package dependencies.


🚀 Quick Installation ​

Method 1: install from the project's pyproject.toml via Poetry ​

bash
git clone https://github.com/Flude-team/universal-document-engine.git
cd universal-document-engine/engine
poetry install

engine/pyproject.toml defines [tool.poetry.scripts] ude = "ude.cli:main", so after poetry install the ude command is available directly on your PATH inside that environment.

Method 2: run without installing, via PYTHONPATH ​

If you don't want to install the package, you can invoke the CLI as a module as long as the engine directory is importable:

bash
# from anywhere, with the engine checkout on PYTHONPATH
python -m ude.cli --help

On Windows, set PYTHONPATH to include the engine directory first, e.g. set PYTHONPATH=path\to\engine; on macOS/Linux, PYTHONPATH=path/to/engine.

:::note pip install universal-document-engine is not currently a verified install path — there is no confirmation this package is published to PyPI. Use the Poetry or PYTHONPATH methods above, which are directly verifiable from the repository's own pyproject.toml and CLI entry point. :::


⚡ Your First Compilation (Hello World) ​

The engine repository ships a real, working example project at ude_projects/mock/mock_api_py/, with three layered config files:

  • ude_projects/ude_global_config.json — global settings
  • ude_projects/mock/ude_sdk_config.json — SDK/product-level settings
  • ude_projects/mock/mock_api_py/ude_doc_config.json — the compilation target itself

That project's own generate_docs.bat runs, from inside the mock_api_py directory:

bash
python -m ude.cli --global-config ../../ude_global_config.json --sdk-config ../ude_sdk_config.json --doc-config ude_doc_config.json --format html

On Windows with no virtualenv active, you also need PYTHONPATH pointed at the engine directory first:

bash
set PYTHONPATH=..\..\..\engine

(on macOS/Linux: PYTHONPATH=../../../engine)

IMPORTANT

There is no bare "Hello World" invocation that skips config files. Every real compile run requires at least a doc-config (--doc-config / -d, or the legacy --config / -c alias); global and SDK configs are optional but almost always present in real projects. A command like python -m ude.cli --input ... --output ... --format html with no --doc-config will fail — --input/--output are only overrides layered on top of a resolved config, not a replacement for one.

Expected Output ​

This exact sequence was actually run against the mock project (exit code 0, success). Contrary to what you might expect, no [INFO]-style progress lines are printed at all. That's because GlobalConfig.log_level defaults to "WARNING" (engine/ude/config.py:117), and the real ude_global_config.json shipped in this repo doesn't actually set it — its "logging": {"level": "INFO", ...} block is not a real GlobalConfig field (see First Config for why), so it's silently ignored.

The only thing printed to stdout is the coverage audit table, which every successful compile run ends with:

text
# Documentation Coverage Audit

**Total Entities:** 713
**Documented Entities:** 13
**Overall Coverage:** 1.82%

| Type | Entity Name | Documented |
| :--- | :--- | :--- |
| Class | `core_modeler::Box` | PASS |
| Method | `core_modeler::Box.getVolume` | FAIL |
| Method | `core_modeler::Box.renderToScreen` | FAIL |

This is a representative excerpt, not a placeholder — the real table has one row per parsed entity, and for this mock project that's 713 rows in total.

TIP

Why is coverage so low on the mock example? The mock project is a SWIG-wrapped Python binding, so Doxygen picks up a large number of unfiltered SWIG plumbing methods (swigCPtr, swigregister, and similar) as undocumented entities. The engine does have an exclude_swig_internals filter flag, but as of today it is only reachable if you call the parser directly as a Python library — it is not wired to any JSON config key, so a normal ude_doc_config.json-driven run like this one can't turn it on. Low coverage here is expected, not a bug.

Once it finishes, inspect ude_output/mock_api_py/ (resolved from output_base_dir + output_subdir, see First Config). For --format html you'll find a flat directory (no subdirectories) of files named {entity_type}_{fully_qualified_name}.html, with :: in fully-qualified names replaced by __, e.g. class_core_modeler__Box.html. Alongside those you'll also see a couple of static pages sourced from that project's sidebar.toml (api_ref_version.html, api_ref2dg.html), and a .build_cache.json.gz file — the on-disk L2 render cache used for incremental builds.


🚀 GitHub Action Quickstart (C/C++ Projects) ​

Flude can be run via GitHub Actions (flude-team/flude-action). To ensure high-quality parsing of C/C++ projects, provide a build configuration (compile_commands.json) so the parser understands your project's include paths and macros.

Generating compile_commands.json ​

Without this file, parsing complex codebases degrades because the compiler lacks the context needed to resolve #include paths or expand macros correctly. Provide it for the best result.

For CMake projects: Generate the file during your configure step by adding -DCMAKE_EXPORT_COMPILE_COMMANDS=ON:

yaml
- name: Generate build configuration
  run: cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

For Meson projects: Meson generates this file natively in the build directory by default:

yaml
- name: Setup Meson
  run: meson setup build

For Make-based projects: Use tools like bear or compiledb to intercept the build:

yaml
- name: Generate with bear
  run: bear -- make

Passing it to the Action ​

Once generated, point the Flude action to it using compile-commands-path:

yaml
- uses: flude-team/flude-action@v1
  with:
    api-token: ${{ secrets.FLUDE_API_KEY }}
    api-base-url: 'https://control-plane.flude.guide'
    language: cpp
    compile-commands-path: 'build/compile_commands.json'

For MSVC-only projects without CMake/Meson/Make ​

If your project is a pure MSVC/Visual Studio project (.vcxproj) and you cannot easily generate a compile_commands.json, you can point the Flude action directly to your project file. The parser will statically extract include paths and macros for you.

yaml
- uses: flude-team/flude-action@v1
  with:
    api-token: ${{ secrets.FLUDE_API_KEY }}
    api-base-url: 'https://control-plane.flude.guide'
    language: cpp
    vcxproj-path: 'MyProject/MyProject.vcxproj'
    vcxproj-configuration: 'Release' # Optional (defaults to Release)
    vcxproj-platform: 'x64'          # Optional (defaults to x64)

For Bazel projects ​

If your project is built with Bazel, you can point the Flude action directly to your BUILD or BUILD.bazel file and specify the target name. The parser will statically extract include paths and macros for you.

yaml
- uses: flude-team/flude-action@v1
  with:
    api-token: ${{ secrets.FLUDE_API_KEY }}
    api-base-url: 'https://control-plane.flude.guide'
    language: cpp
    bazel-build-path: 'src/my_lib/BUILD.bazel'
    bazel-target-name: 'my_lib' # Optional if the file has exactly one cc_* target

For CMake projects ​

If your project is built with CMake and you cannot or prefer not to generate a compile_commands.json dynamically, you can point the Flude action directly to a CMakeLists.txt file. The parser will statically extract directory-scoped include paths (include_directories()) and macros (add_definitions()).

yaml
- uses: flude-team/flude-action@v1
  with:
    api-token: ${{ secrets.FLUDE_API_KEY }}
    api-base-url: 'https://control-plane.flude.guide'
    language: cpp
    cmake-lists-path: 'src/CMakeLists.txt'

Macros and the C++ standard ​

Code under #if/#ifdef is documented the way your build compiles it. Two optional inputs tell Flude what the build defines:

  • defines — the macros your build passes with -D, one per line (NAME or NAME=VALUE) or as a JSON array of strings. Example: a project that enables GPU code with -DGPU_SUPPORT.
  • cpp-std — the C++ standard (c++11, c++14, c++17, c++20 or c++23). If you omit it, Flude uses the standard your top-level CMakeLists.txt names (set(CMAKE_CXX_STANDARD 17), cxx_std_17), and c++17 when there is none.
yaml
- uses: flude-team/flude-action@v1
  with:
    api-token: ${{ secrets.FLUDE_API_KEY }}
    api-base-url: 'https://control-plane.flude.guide'
    language: cpp
    defines: |
      GPU_SUPPORT
      LOG_LEVEL=2
    cpp-std: c++20

On GitLab and Bitbucket the same settings are the FLUDE_DEFINES and FLUDE_CPP_STD variables.