ascfix 0.7.0

Automatic ASCII diagram repair tool for Markdown files
Documentation
.--------------.  .------------.  .--------------.  .--------------.  .-----------.  .--------------. 
|      __      |  |   _______  |  |     ______   |  |  _________   |  |   _____   |  |  ____  ____  | 
|     /  \     |  |  /  ___  | |  |   .' ___  |  |  | |_   ___  |  |  |  |_   _|  |  | |_  _||_  _| | 
|    / /\ \    |  | |  (__ \_| |  |  / .'   \_|  |  |   | |_  \_|  |  |    | |    |  |   \ \  / /   | 
|   / ____ \   |  |  '.___`-.  |  |  | |         |  |   |  _|      |  |    | |    |  |    > `' <    | 
| _/ /    \ \_ |  | |`\____) | |  |  \ `.___.'\  |  |  _| |_       |  |   _| |_   |  |  _/ /'`\ \_  | 
||____|  |____||  | |_______.' |  |   `._____.'  |  | |_____|      |  |  |_____|  |  | |____||____| | 
|              |  |            |  |              |  |              |  |           |  |              | 
'--------------'  '------------'  '--------------'  '--------------'  '-----------'  '--------------' 
        ║                ║                ║               ║                 ║              ║
        ╚════════════════╩════════════════╩═══════════════╩═════════════════╩══════════════╝

Automatic ASCII diagram repair tool for Markdown files

Tests & Lints Security Audit

Crates.io docs.rs Downloads

License: MIT Rust Version

Overview

ascfix fixes misaligned ASCII diagrams in Markdown, especially those generated by LLMs. It normalizes box widths, aligns arrows, enforces uniform padding, and ensures diagrams remain maintainable.

Ideal for:

  • AI-generated documentation with ASCII diagrams
  • Markdown files with workflow diagrams
  • CI/CD pipeline documentation
  • Architecture diagrams in code repositories

Quality Guarantees

ascfix prioritizes safety and reliability with a conservative approach:

Zero Data Loss

  • Content preservation - designed to never delete or modify unintended text
  • Non-destructive processing - safe to run on Markdown files
  • Idempotent operations - running multiple times produces consistent results for simple diagrams (see Known Limitations)
  • Conservative default - when uncertain, preserves original formatting

Intelligent Quality Assurance

  • Automated quality validation - comprehensive test suite with 10+ passing unit fixtures
  • Semantic transformation analysis - distinguishes beneficial improvements from potential corruption
  • Context-aware processing - understands diagram elements vs. regular text
  • Active development - complex nested structures handled conservatively

Professional Output

  • Consistent formatting - uniform spacing, proper alignment for simple and moderate diagrams
  • Basic nested box support - single-level nesting works reliably
  • Arrow alignment - intelligent positioning for standard workflows
  • Conservative mode - deeply nested (3+ levels) or complex overlapping structures preserved unchanged

Features

Processing Modes

  • Diagram mode (default): Repair ASCII boxes, arrows, and diagram structures
  • Safe mode: Normalize Markdown tables only - safest for any file
  • Check mode: Validate without modifying (exit code 1 if changes needed - perfect for CI/CD)
  • All mode: Shorthand for --fences --mode=diagram to repair everything

Diagram Repair

  • Box style preservation: Single-line (┌┐└┘│─), double-line (╔╗╚╝║═), and rounded (╭╮╰╯│─) boxes
  • Box width normalization: Expands boxes to fit content with uniform padding
  • Side-by-side balancing: Equalizes widths of adjacent boxes for visual consistency
  • Nested box support: Parent boxes expand to properly contain children with margins
  • Arrow alignment: Aligns vertical arrows to box centers, horizontal arrows to edges
  • Connection lines: L-shaped paths with corner connectors
  • Label preservation: Maintains text labels attached to primitives

Markdown Repair

  • Table normalization: Aligns columns and fixes hard-wrapped cells
  • List normalization: Standardizes bullet styles (- * +-), fixes indentation, adds spacing
  • Link preservation: Handles URLs with parentheses and reference-style links
  • Fence repair: Fixes mismatched fence lengths, unclosed blocks, duplicate closing fences

File Operations

  • Directory processing: Recursively process directories with automatic file discovery
  • Multiple extensions: Process .md, .mdx, .txt files (customizable with --ext)
  • Smart filtering: Automatically skips hidden directories, node_modules, target, build output
  • Size limits: Configurable max file size (--max-size 5MB)
  • In-place editing: Modify files directly with --in-place (default: stdout output)
  • Error resilience: Continue processing remaining files on individual errors

Safety & Quality

  • Conservative: Only fixes well-understood structures; unknown patterns preserved unchanged
  • Idempotent: Running twice produces identical output (safe for repeated application)
  • No panics: Handles malformed input gracefully without crashes
  • Zero unsafe code: Compile-time enforced with #![forbid(unsafe_code)]
  • No data loss: Never deletes or rewords content - only improves formatting
  • Fast: Linear O(n) processing time suitable for large files and CI/CD

Integration

  • Exit codes: 0 = success/no changes needed, 1 = check failed or error (CI/CD friendly)
  • GitHub Actions ready: Verified workflows for testing, linting, security auditing
  • Configuration file support: .ascfix.toml for project-wide settings

Installation

Recommended: From crates.io

The simplest way to install ascfix is via crates.io:

cargo install ascfix

From source

To build from the latest development version:

git clone https://github.com/evoludigit/ascfix.git
cd ascfix
cargo install --path .

Usage

Single Files

# Fix a file (default: diagram mode, output to stdout)
ascfix README.md

# Fix a file in place (default: diagram mode)
ascfix README.md --in-place

# Only normalize tables and lists (safe mode)
ascfix README.md --in-place --mode safe

# Repair code fence boundaries
ascfix README.md --in-place --fences

# Repair everything (diagram mode + fence repair)
ascfix README.md --in-place --all

# Validate without modifying (exit code 1 if changes needed)
ascfix README.md --check

Directories

# Process all .md and .mdx files in a directory
ascfix docs/ --in-place --mode=diagram

# Process a directory recursively (skips hidden dirs, node_modules, target, etc.)
ascfix . --in-place --all

# Process only .md files (default includes .md, .mdx, .txt)
ascfix docs/ -e .md --in-place

# Process .mdx files only
ascfix docs/ -e .mdx --in-place

# Complex filtering with find (exclude specific paths)
ascfix $(find . -name "*.md" -not -path "*/node_modules/*" -not -path "*/.git/*") --in-place

# Or use fd (faster)
ascfix $(fd -e md) --in-place

# Check multiple files without modifying
ascfix docs/ --check

Modes

Mode Description Use Case
safe Fix only Markdown tables Conservative, safe for any file
diagram Fix boxes and arrows For files with ASCII diagrams
check Validate without writing CI/CD validation

Flags

Flag Short Description Default
--in-place -i Modify files instead of printing to stdout Off
--check -c Validate files without modifying (returns exit code 1 if changes needed) Off
--fences Repair code fence boundaries Off
--all Shorthand for --fences --mode=diagram Off
--mode Processing mode (safe, diagram, check) safe
--ext -e File extensions to process (comma-separated, e.g., .md,.mdx) .md,.mdx
--max-size Maximum file size to process (e.g., "100MB", "1GB") Unlimited

Output & Formatting Flags

Flag Short Description Use Case
--summary Print processing statistics (files processed, modified, errors) Quick overview
--list-files Output only filenames that need fixing (one per line) CI/CD, scripting
--verbose -v Show detailed processing information with colored output Debugging, understanding
--json Output results as JSON (machine-readable) AI/programmatic use
--diff Show unified diff of changes without modifying files Preview changes

Ignore Markers

ascfix supports HTML comment markers to exclude specific content from processing. This is useful for:

  • Preserving intentional ASCII art that shouldn't be "fixed"
  • Protecting decorative elements like logos or banners
  • Excluding complex diagrams that should remain unchanged

Supported marker syntax:

<!-- ascfix:ignore -->
[content to ignore]
<!-- /ascfix:ignore -->

Or alternatively:

<!-- ascfix-ignore-start -->
[content to ignore]
<!-- ascfix-ignore-end -->

Example:

<!-- ascfix:ignore -->
```
 .----------------.  .----------------.
| .--------------. || .--------------. |
| |    LOGO      | || |    HERE      | |
| '--------------' || '--------------' |
 '----------------'  '----------------'
```
<!-- /ascfix:ignore -->

This box will be processed:
┌──────┐
│ Test │
└──────┘

Content inside ignore markers is completely skipped by ascfix, regardless of mode. Ignore markers work with:

  • Code fences (content inside fences within ignore blocks is preserved)
  • Inline diagrams
  • Tables
  • Any other Markdown content

Important: Ignore markers are removed from the output (they're HTML comments, so they won't appear in rendered Markdown).

Examples

Safe Mode: Markdown Table Alignment

Before (inconsistent column widths):

| Name    | Age | City     |
|---------|-----|----------|
| Alice   | 30  | New York |
| Bob     | 25  | Boston   |
| Charlie | 35  | Chicago  |

After (normalized columns):

| Name    | Age | City     |
|---------|-----|----------|
| Alice   | 30  | New York |
| Bob     | 25  | Boston   |
| Charlie | 35  | Chicago  |

Command:

ascfix table.md --in-place

Code Fence Repair

Before (mismatched fence lengths):

```python
def hello():
    print("Hello, World!")
`````

After (balanced fences):

`````python
def hello():
    print("Hello, World!")
`````

Also handles:

  • Unclosed fences (adds closing fence)
  • Inconsistent lengths (uses longest)
  • Preserves language specifiers
  • Skips type mismatches (conservative approach)

Command:

ascfix docs/*.md --in-place --fences

Diagram Mode: Box and Arrow Alignment

Before (misaligned, inconsistent):

┌──────┐
│ Step │
└──────┘
┌─────────┐
│ Process  │
└─────────┘

After (normalized):

┌────────┐
│ Step   │
└────────┘
┌────────┐
│ Process│
└────────┘

Command:

ascfix workflow.md --in-place --mode=diagram

Diagram Mode: Box Width Normalization

Before (text too close to border):

┌────────┐
│Process │
└────────┘

After (proper padding):

┌─────────┐
│ Process │
└─────────┘

Diagram Mode: Complex Workflow

Before (AI-generated, misaligned):

Source Code
┌──────────────────┐
│ Build & Test     │
└──────────────────┘
  ↓
┌────────┐
│ Deploy │
└────────┘

After (normalized, consistent):

Source Code
┌─────────────────┐
│ Build & Test    │
└─────────────────┘
┌──────────────────┐
│ Deploy           │
└──────────────────┘

Diagram Mode: Box Padding and Arrow Alignment

Before (LLM-generated, text cramped against borders, arrows misaligned):

┌───────┐    ┌────────┐
│Process│    │Database│
└───────┘    └────────┘
   ↓          ↓
┌──────────────────┐
│ResultProcessor   │
└──────────────────┘

After (proper padding added, arrows aligned to box centers):

┌─────────┐    ┌──────────┐
│ Process │    │ Database │
└─────────┘    └──────────┘
     │             │
┌────────────────────────┐
│ Result Processor       │
└────────────────────────┘

Changes made:

  • Expanded boxes to fit content with 1-space padding
  • Aligned arrows to box center columns
  • Normalized interior spacing

Command:

ascfix workflow.md --in-place --mode=diagram

Diagram Mode: Multi-Step Pipeline with Inconsistent Arrows

Before (arrows at inconsistent positions, no padding):

Source Code
  ↓
┌──────────┐
│Build     │
└──────────┘
┌─────────────┐
│Test Suite   │
└─────────────┘
  ↓
┌──────┐
│Deploy│
└──────┘

After (arrows normalized to consistent columns, boxes expanded with padding):

Source Code
┌──────────────┐
│ Build        │
└──────────────┘
┌───────────────────┐
│ Test Suite        │
└───────────────────┘
┌────────────┐
│ Deploy     │
└────────────┘

Changes made:

  • Aligned all vertical arrows to consistent column
  • Added interior padding to all boxes (text no longer touching borders)
  • Expanded boxes to fit content with proper spacing

Command:

ascfix pipeline.md --in-place --mode=diagram

Diagram Mode: Horizontal Workflow with Inconsistent Alignment

Before (arrows offset from box edges, inconsistent padding):

┌──────┐
│Input │
└──────┘
  ──→
     ┌─────────┐
     │Processing│
     └─────────┘
        ──→
          ┌──────┐
          │Output│
          └──────┘

After (arrows snapped to box edges, uniform padding):

┌────────┐
│ Input  │
└────────┘
     ┌───────────────┐
     │ Processing    │
     └───────────────┘
          ┌────────┐
          │ Output │
          └────────┘

Changes made:

  • Expanded boxes to fit content with padding
  • Snapped arrow endpoints to box edge columns
  • Made padding uniform (1 space) inside boxes

Command:

ascfix workflow.md --in-place --mode=diagram

Check Mode: CI/CD Validation

Validate without modifying (useful for pull requests):

# Exit 0 if file is already normalized
# Exit 1 if file needs fixing
ascfix docs/*.md --check --mode=diagram

Perfect for GitHub Actions:

- name: Check diagram alignment
  run: ascfix docs/*.md --check --mode=diagram
  # Fails the build if any diagrams need fixing

Output Modes: Enhanced DX & AX

Summary mode - quick statistics:

ascfix docs/ --summary --in-place --mode=diagram
Summary:
  Total files processed: 15
  Modified: 8
  Unchanged: 7
  Errors: 0

List files mode - for CI/CD scripts:

# Get list of files that need fixing
files=$(ascfix docs/ --check --list-files --mode=diagram)
if [ -n "$files" ]; then
  echo "Files need fixing:"
  echo "$files"
  exit 1
fi

JSON mode - for AI/programmatic use:

ascfix docs/ --json --check --mode=diagram
{
  "files": [
    {
      "status": "modified",
      "file": "docs/api.md"
    },
    {
      "status": "unchanged",
      "file": "docs/guide.md"
    }
  ],
  "stats": {
    "total": 2,
    "modified": 1,
    "unchanged": 1,
    "errors": 0,
    "skipped": 0
  }
}

Diff mode - preview changes:

ascfix workflow.md --diff --mode=diagram
diff workflow.md
  # Workflow

  ┌──────┐
-│Process│
+│ Process │
  └──────┘

Verbose mode - debugging:

ascfix docs/ --verbose --in-place --mode=diagram
[verbose] Found 3 files to process
[success] Modified: docs/api.md
[verbose] Unchanged: docs/guide.md
[success] Modified: docs/workflow.md

Combined modes - maximum visibility:

ascfix docs/ --verbose --summary --in-place --mode=diagram

Visual Examples Gallery

All examples below are verified test fixtures from our test suite (see tests/data/). Each example demonstrates specific features with real before/after transformations.

Box Style Variants

ascfix detects and preserves different box drawing styles:

Single-line boxes (┌ ┐ └ ┘ │ ─):

┌────────┐
│ Single │
└────────┘

Double-line boxes (╔ ╗ ╚ ╝ ║ ═):

╔════════════════╗
║ Double-Line    ║
║ Box Style      ║
╚════════════════╝

Rounded-corner boxes (╭ ╮ ╰ ╯):

╭────────────────╮
│ Rounded        │
│ Corner Box     │
╰────────────────╯

Side-by-Side Box Balancing

ascfix automatically normalizes widths of horizontally adjacent boxes:

Before:

┌───────┐  ┌─────┐  ┌────────────┐
│ Box 1 │  │ B2  │  │ Box Three  │
└───────┘  └─────┘  └────────────┘

After:

┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Box 1       │ │ B2          │ │ Box Three   │
└─────────────┘ └─────────────┘ └─────────────┘

Nested Box Hierarchies

Parent boxes expand to properly contain children with appropriate margins:

Before:

┌──────────────┐
│ Parent       │
│ ┌────────┐   │
│ │ Child  │   │
│ └────────┘   │
└──────────────┘

After:

┌────────────────────┐
│ Parent             │
│                    │
│  ┌──────────────┐  │
│  │ Child        │  │
│  └──────────────┘  │
│                    │
└────────────────────┘

CI/CD Pipeline Diagram

Real-world pipeline with consistent arrow alignment:

┌──────────────┐
│ Source Code  │
└──────────────┘
       ↓
┌──────────────┐
│  Build Job   │
└──────────────┘
       ↓
┌──────────────┐
│  Test Suite  │
└──────────────┘
       ↓
┌──────────────┐
│   Deploy     │
└──────────────┘

Complex Real-World Example

API documentation with mixed diagrams and tables:

┌─────────────────┐    ┌─────────────────┐
│   Client App    │    │   API Gateway   │
│                 │    │                 │
│ - Mobile App    │    │ - Authentication  │
│ - Web App       │    │ - Rate Limiting   │
└─────────┬───────┘    └─────────┬───────┘
          │                      │
          ▼                      ▼

Test Suite & Fixtures

The project includes 55+ test fixtures organized in tests/data/:

Verified Working Examples:

  • Safe mode fixtures: Table normalization, list formatting, link preservation
  • Basic diagrams: Simple boxes, arrows, side-by-side alignment
  • Standard workflows: CI/CD pipelines, simple nested structures

Edge Cases & Error Handling:

  • Malformed inputs: Broken tables, wrapped cells
  • Boundary conditions: Empty files, minimal diagrams
  • Real-world patterns: API docs, ML pipelines (in conservative mode)

Conservative Mode Active: Some complex diagram fixtures (deep nesting, overlapping elements, mixed styles with labels) exercise the tool's conservative behavior. When structures are ambiguous or highly complex, ascfix preserves the original formatting unchanged rather than risk corruption. This prioritizes safety and data preservation over aggressive transformation.

Verify examples yourself:

# Run all tests to verify fixtures
cargo test

# Process a specific file
ascfix README.md --mode=diagram
  • tests/data/unit/ - Simple, focused examples
  • tests/data/integration/ - Complex real-world scenarios
  • tests/data/integration/dirty/ - Malformed inputs and error cases

Every example in this README can be verified:

# Run tests to verify all fixtures
cargo test

# Process a specific fixture
ascfix tests/data/unit/input/ci_pipeline.md --mode=diagram

Advanced Features in Diagram Mode

Box Style Variants

ascfix supports multiple box drawing styles and automatically preserves them:

Single-line boxes (default):

┌─────┐
│ Box │
└─────┘

Double-line boxes:

╔═════╗
║ Box ║
╚═════╝

Rounded-corner boxes:

╭─────╮
│ Box │
╰─────╯

All styles are detected, preserved, and normalized consistently. Mixed styles in the same diagram are handled correctly.


Nested Box Hierarchies

ascfix detects and supports basic parent-child box relationships. Single-level nesting works reliably:

Before (parent too small):

┌──────────────┐
│ Parent       │
│ ┌────────┐   │
│ │ Child  │   │
│ └────────┘   │
└──────────────┘

After (parent expanded with margins):

┌────────────────────┐
│ Parent             │
│                    │
│  ┌──────────────┐  │
│  │ Child        │  │
│  └──────────────┘  │
│                    │
└────────────────────┘

Important: Complex nested structures (>2 levels deep) or diagrams with overlapping elements are currently handled in conservative mode - they are preserved unchanged rather than risk corruption. This ensures safety over aggressive transformation.


Side-by-Side Box Alignment

ascfix automatically balances the width of horizontally adjacent boxes for visual consistency:

Before (inconsistent widths):

┌───────┐  ┌─────┐  ┌────────────┐
│ Box 1 │  │ B2  │  │ Box Three  │
└───────┘  └─────┘  └────────────┘

After (uniform widths):

┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Box 1       │ │ B2          │ │ Box Three   │
└─────────────┘ └─────────────┘ └─────────────┘

Enhanced Arrow Support

ascfix recognizes and normalizes various arrow styles:

  • Standard arrows: → ↓ ↑ ←
  • Double arrows: ⇒ ⇓ ⇑ ⇐
  • Extended arrows: ⟶ ⟹

All arrows are aligned to box centers and maintained at consistent columns throughout the diagram.


Connection Lines (Conservative)

ascfix supports L-shaped connection paths with limited scope (4 segments maximum per connection):

┌────────┐
│ Start  │
└───┬────┘
    │
    └─────┬─────────┐
          │         │
      ┌───▼───┐ ┌──▼──┐
      │ Path1 │ │Path2│
      └───────┘ └─────┘

Connection detection is conservative to avoid false positives. Very complex paths are skipped.


Label Preservation (Framework)

ascfix includes framework support for preserving text labels attached to primitives during normalization. Label detection and rendering are currently conservative (skeleton implementation):

  • Labels attached to boxes are tracked
  • Labels attached to arrows are preserved
  • Relative offsets are maintained during normalization
  • Collision detection prevents label-diagram overlap

Known Limitations

Idempotence for Complex Diagrams

ascfix is designed to be idempotent (running multiple times produces the same result), and this works reliably for most use cases:

✅ Fully Idempotent:

  • Simple ASCII boxes and arrows
  • Markdown tables in Safe mode
  • Single-level nested boxes
  • Standard list formatting

⚠️ Known Limitations: Complex diagrams with deeply nested boxes (3+ levels) or overlapping elements may not be fully idempotent. After the first normalization pass, the expanded parent boxes create new spatial relationships that can trigger different detection on subsequent runs.

Why This Happens:

  1. First pass: Parent box expands to fit children
  2. Second pass: Detection sees new box dimensions and may recalculate relationships
  3. For deeply nested hierarchies (3+ levels), this can cascade

Recommended Approach:

  • Run ascfix once on complex diagrams
  • Review the output
  • Commit the normalized version
  • For table-only processing, use --mode=safe which is always idempotent

For technical details about the detection phase and spatial relationship algorithms, see ARCHITECTURE.md. For workarounds and testing strategies, see LIBRARY_USAGE.md.


Building & Testing

# Run all tests
cargo test

# Check code quality
cargo clippy --all-targets --all-features -- -D warnings

# Build release binary
cargo build --release

All code is tested with TDD discipline - unit tests, integration tests, and golden file tests. Test data is organized in tests/data/ with clear separation between unit and integration test cases.

Programmatic Usage

ascfix is designed for easy integration into scripts, AI agents, and CI/CD pipelines.

For AI Agents

Use JSON mode for structured output:

# Get machine-readable results
ascfix docs/ --json --check --mode=diagram > results.json

Parse the JSON to understand what changed:

{
  "files": [
    {
      "status": "modified",
      "file": "docs/workflow.md"
    },
    {
      "status": "error",
      "file": "docs/broken.md",
      "error": "Failed to read: Permission denied"
    }
  ],
  "stats": {
    "total": 2,
    "modified": 1,
    "errors": 1
  }
}

For Shell Scripts

Use list-files mode for simple pipelines:

#!/bin/bash
# Fix all files that need it
for file in $(ascfix docs/ --list-files --check --mode=diagram); do
  echo "Fixing: $file"
  ascfix "$file" --in-place --mode=diagram
done

Use exit codes for CI/CD:

# Exit 0 if all files are clean, 1 if fixes needed
ascfix docs/ --check --mode=diagram
if [ $? -ne 0 ]; then
  echo "⚠️  Diagrams need normalization"
  exit 1
fi

For Python Scripts

import subprocess
import json

result = subprocess.run(
    ["ascfix", "docs/", "--json", "--check", "--mode=diagram"],
    capture_output=True,
    text=True
)

data = json.loads(result.stdout)
modified_files = [f["file"] for f in data["files"] if f["status"] == "modified"]
print(f"Files needing fixes: {modified_files}")

GitHub Actions Integration

name: Validate Diagrams

on: [pull_request]

jobs:
  check-diagrams:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Install ascfix
        run: cargo install ascfix

      - name: Check diagrams
        run: |
          ascfix docs/ --check --summary --mode=diagram
          if [ $? -ne 0 ]; then
            echo "::error::Diagrams need normalization. Run: ascfix docs/ --in-place --mode=diagram"
            exit 1
          fi

Documentation

User Guides

  • CONFIG.md - Configuration options, file formats, and examples
  • LIBRARY_USAGE.md - Using ascfix as a Rust library, API documentation, and integration examples

Reference

License

Licensed under the MIT License (LICENSE or https://opensource.org/licenses/MIT)

Contribution

Contributions are welcome! Please feel free to submit pull requests.