Skip to main content
The bash::framehead wiki generator uses the framework itself to introspect and document all functions, creating a complete reference wiki from code comments.

Quick start

Generate or update the wiki documentation:
./gen_wiki.sh ./compiled.sh ./wiki
Output:
bash-framehead wiki generator
Source:  ./compiled.sh
Output:  ./wiki

Found 785 functions across 16 modules

  123 created, 662 skipped / 785 total

Generating module indexes...
  string.md
  array.md
  math.md
  ...

Generating root index...

Done — 123 created, 662 skipped, ./wiki/README.md updated

How it works

The wiki generator dogfoods the framework to document itself: From wiki-gen.sh:1-22:
#!/usr/bin/env bash
# gen_wiki.sh — generate wiki docs from bash-framehead compiled output
# Dogfoods the framework for string manipulation, fs operations, and path handling
#
# Usage: ./gen_wiki.sh <framework_file> [wiki_dir]
# Example: ./gen_wiki.sh ./bash-framehead.sh ./wiki
#
# Behaviour:
#   - Function pages: skipped if already exist (preserves manual edits)
#   - Module indexes: new entries appended, existing entries preserved
#   - Root index: new module rows appended, existing rows preserved

SRC="${1:?Usage: gen_wiki.sh <framework_file> [wiki_dir]}"
WIKI="${2:-./wiki}"

[[ -f "$SRC" ]] || { echo "Error: source file not found: $SRC" >&2; exit 1; }

# Load the framework — this is the whole point
source "$SRC"
The script:
  1. Sources the compiled framework
  2. Uses declare -F to introspect all functions
  3. Extracts comments using grep and string manipulation
  4. Generates markdown files using fs::write

Introspection helpers

The generator uses the framework’s own string functions: From wiki-gen.sh:27-41:
_wiki::all_functions() {
    declare -F | awk '{print $3}' | grep -v '^_' | grep '::' | sort
}

_wiki::module_of() {
    string::before "$1" "::"
}

_wiki::leaf_of() {
    string::after "$1" "::"
}

_wiki::to_path() {
    string::replace_all "$1" "::" "/"
}
These helpers:
  • List all public functions (excluding _internal ones)
  • Extract module namespace from module::function
  • Convert :: to / for file paths

Comment extraction

The generator extracts documentation comments above function definitions: From wiki-gen.sh:43-81:
# Extract comment block above a function definition
# Stops at blank lines, non-comment lines, and divider lines (# ===...)
_wiki::extract_comments() {
    local funcname="$1"
    local lineno
    lineno=$(grep -n "^${funcname}()" "$SRC" | head -1 | cut -d: -f1)
    [[ -z "$lineno" ]] && return

    local i=$(( lineno - 1 ))
    local -a lines=()

    while (( i >= 1 )); do
        local line
        line=$(sed -n "${i}p" "$SRC")

        # Stop at divider lines: # ===... or # ---...
        if [[ "$line" =~ ^#[[:space:]]*[=\-]{4,}[[:space:]]*$ ]]; then
            break
        fi

        if [[ "$line" =~ ^#[[:space:]]?(.*) ]]; then
            local content="${BASH_REMATCH[1]}"
            lines=("$content" "${lines[@]}")
            (( i-- ))
        elif [[ -z "$line" ]]; then
            # Allow blank lines if surrounded by comments
            local prev
            prev=$(sed -n "$(( i - 1 ))p" "$SRC")
            [[ "$prev" =~ ^# ]] && { (( i-- )); continue; }
            break
        else
            break
        fi
    done

    printf '%s\n' "${lines[@]}"
}
The comment extractor stops at divider lines (# === or # ---) to avoid including unrelated comments from module headers.

Function page generation

Each function gets its own markdown page: From wiki-gen.sh:110-160:
_wiki::write_function_page() {
    local funcname="$1"
    local module leaf leaf_path out_file out_dir
    module=$(_wiki::module_of "$funcname")
    leaf=$(_wiki::leaf_of "$funcname")
    leaf_path=$(_wiki::to_path "$leaf")
    out_file="${WIKI}/${module}/${leaf_path}.md"
    out_dir=$(fs::path::dirname "$out_file")

    fs::mkdir "$out_dir"

    # Skip if already exists — preserve manual edits
    if [[ -f "$out_file" ]]; then
        return
    fi

    local comments usage description example
    comments=$(_wiki::extract_comments "$funcname")
    usage=$(grep -i    '^Usage:'   <<< "$comments" | string::after_last "Usage: " || true)
    example=$(grep -i  '^Example:' <<< "$comments" | string::after_last "Example: " || true)
    description=$(grep -iv '^Usage:\|^Example:' <<< "$comments" | grep -v '^$' | head -1 || true)

    local body
    body=$(_wiki::extract_body "$funcname")

    fs::write "$out_file" "# \`${funcname}\`

${description:-_No description available._}

## Usage

\`\`\`bash
${usage:-${funcname} ...}
\`\`\`

## Source

\`\`\`bash
${body}
\`\`\`

## Module

[\`${module}\`](../${module}.md)
"
}
Preserves manual edits: Function pages are only created if they don’t exist. Existing files are never overwritten, allowing you to manually enhance documentation.

Module index generation

Module index pages list all functions in a module: From wiki-gen.sh:162-192:
_wiki::write_module_index() {
    local module="$1"
    shift
    local -a funcs=("$@")
    local index_file="${WIKI}/${module}.md"

    # Create with header if it doesn't exist yet
    if [[ ! -f "$index_file" ]]; then
        {
            echo "# \`${module}\`"
            echo ""
            echo "| Function | Description |"
            echo "|----------|-------------|"
        } > "$index_file"
    fi

    local existing
    existing=$(cat "$index_file")

    local fn comments desc leaf leaf_path
    for fn in "${funcs[@]}"; do
        # Skip if this function already has an entry
        string::contains "$existing" "\`${fn}\`" && continue

        leaf=$(_wiki::leaf_of "$fn")
        leaf_path=$(_wiki::to_path "$leaf")
        comments=$(_wiki::extract_comments "$fn")
        desc=$(grep -iv '^Usage:\|^Example:' <<< "$comments" | grep -v '^$' | head -1 || true)
        echo "| [\`${fn}\`](./${module}/${leaf_path}.md) | ${desc:-—} |" >> "$index_file"
    done
}
Index pages are append-only — new functions are added without modifying existing entries.

Root index generation

The root README.md provides an overview of all modules: From wiki-gen.sh:194-221:
_wiki::write_root_index() {
    local -a modules=("$@")
    local index_file="${WIKI}/README.md"

    # Create with header if it doesn't exist yet
    if [[ ! -f "$index_file" ]]; then
        {
            echo "# bash-framehead"
            echo ""
            echo "## Modules"
            echo ""
            echo "| Module | Functions |"
            echo "|--------|-----------|" 
        } > "$index_file"
    fi

    local existing
    existing=$(cat "$index_file")

    local m count
    for m in $(printf '%s\n' "${modules[@]}" | sort -u); do
        # Skip if module already has an entry
        string::contains "$existing" "\`${m}\`" && continue

        count=$(grep -c '^| `' "${WIKI}/${m}.md" 2>/dev/null || echo 0)
        echo "| [\`${m}\`](./${m}.md) | ${count} |" >> "$index_file"
    done
}

Comment format specification

For optimal documentation generation, format comments like this:
# Brief one-line description of what the function does
# Usage: module::function arg1 arg2 [optional]
# Example: module::function hello world
module::function() {
    # implementation
}
The generator extracts:
  • Description: First non-Usage/Example comment line
  • Usage: Text after Usage: prefix
  • Example: Text after Example: prefix
1

First line: description

# Convert a string to uppercase
Used in module index tables and function page headers.
2

Usage line

# Usage: string::upper str
Shows function signature with parameter names.
3

Example line (optional)

# Example: string::upper "hello world"  # HELLO WORLD
Demonstrates practical usage.
4

Additional context (optional)

# This function uses pure Bash parameter expansion.
# Requires Bash 4.0+ for ${var^^} syntax.
Any extra comments become part of the description.

Generated wiki structure

The generator creates this structure:
wiki/
├── README.md              # Root index with module overview
├── string.md              # String module index
├── string/
│   ├── upper.md          # string::upper function page
│   ├── lower.md          # string::lower function page
│   ├── trim.md           # string::trim function page
│   └── split.md          # string::split function page
├── array.md              # Array module index  
├── array/
│   ├── length.md
│   ├── first.md
│   └── last.md
└── math.md               # Math module index
└── math/
    ├── abs.md
    └── factorial.md

Incremental updates

The generator is designed for incremental updates:
  1. Function pages: Created once, never overwritten
  2. Module indexes: New entries appended
  3. Root index: New modules appended
This means you can:
Run the generator repeatedly without losing manual edits
Add examples and detailed explanations to function pages
Regenerate after adding new functions
Customize module index introductions
Workflow:
# Initial generation
./gen_wiki.sh ./compiled.sh ./wiki
# → Creates all pages

# Manually enhance string::upper.md with detailed examples
vim wiki/string/upper.md

# Add new functions to the framework
vim src/string.sh
./main.sh compile

# Regenerate wiki
./gen_wiki.sh ./compiled.sh ./wiki
# → Adds new function pages, preserves your edits to existing ones

Body extraction

The generator also extracts complete function bodies for reference: From wiki-gen.sh:84-104:
# Extract function body from source
_wiki::extract_body() {
    local funcname="$1"
    local lineno
    lineno=$(grep -n "^${funcname}()" "$SRC" | head -1 | cut -d: -f1)
    [[ -z "$lineno" ]] && return

    local depth=0 started=false
    local -a body=()

    while IFS= read -r line; do
        started=true
        body+=("$line")
        local opens closes
        opens=$(grep -o '{' <<< "$line" | wc -l)
        closes=$(grep -o '}' <<< "$line" | wc -l)
        depth=$(( depth + opens - closes ))
        $started && (( depth == 0 )) && break
    done < <(tail -n "+${lineno}" "$SRC")

    printf '%s\n' "${body[@]}"
}
This provides:
  • Complete function implementation
  • Syntax highlighting in markdown
  • Reference for understanding internals

Main execution flow

From wiki-gen.sh:227-288:
fs::mkdir "$WIKI"

echo "bash-framehead wiki generator"
echo "Source:  $SRC"
echo "Output:  $WIKI"
echo ""

# Collect all functions grouped by module
declare -A module_funcs=()
declare -a all_funcs=()

while IFS= read -r fn; do
    module=$(_wiki::module_of "$fn")
    if [[ -z "${module_funcs[$module]+x}" ]]; then
        module_funcs["$module"]="$fn"
    else
        module_funcs["$module"]+" $fn"
    fi
    all_funcs+=("$fn")
done < <(_wiki::all_functions)

total=${#all_funcs[@]}
module_count=${#module_funcs[@]}

echo "Found $total functions across $module_count modules"
echo ""

# Generate function pages
created=0
skipped=0
for fn in "${all_funcs[@]}"; do
    # ... page generation ...
    printf '\r  %d created, %d skipped / %d total' "$created" "$skipped" "$total"
done

# Generate module indexes
for module in "${!module_funcs[@]}"; do
    read -ra funcs <<< "${module_funcs[$module]}"
    _wiki::write_module_index "$module" "${funcs[@]}"
done

# Generate root index
_wiki::write_root_index "${!module_funcs[@]}"

Best practices

1

Always compile first

Ensure the framework is compiled before generating docs:
./main.sh compile
./gen_wiki.sh ./compiled.sh ./wiki
2

Use consistent comment format

Follow the Description/Usage/Example pattern:
# What the function does
# Usage: module::function args
# Example: module::function value
3

Separate function sections with dividers

Use dividers to prevent comment bleed:
# ==============================
# String validation functions
# ==============================

# Check if string is numeric
string::is_numeric() { ... }
4

Enhance generated pages manually

After initial generation, add:
  • Detailed examples
  • Performance notes
  • Related functions
  • Edge case documentation

Example function documentation

Well-documented function in source:
# Convert string to uppercase using pure Bash
# Usage: string::upper str
# Example: string::upper "hello"  # Returns: HELLO
string::upper() {
    echo "${1^^}"
}
Generated wiki page:
# `string::upper`

Convert string to uppercase using pure Bash

## Usage

```bash
string::upper str

Example

string::upper "hello"  # Returns: HELLO

Source

string::upper() {
    echo "${1^^}"
}

Module

string

## Next steps

<CardGroup cols={2}>
  <Card title="Adding modules" icon="puzzle-piece" href="/development/adding-modules">
    Create modules with proper documentation comments
  </Card>
  <Card title="Contributing" icon="code-pull-request" href="/development/contributing">
    Submit your documented modules to the project
  </Card>
</CardGroup>

Build docs developers (and LLMs) love