diff --git a/.github/workflows/docs-quality.yml b/.github/workflows/docs-quality.yml
new file mode 100644
index 0000000..0672f46
--- /dev/null
+++ b/.github/workflows/docs-quality.yml
@@ -0,0 +1,87 @@
+name: Documentation Quality
+
+on:
+ pull_request:
+ paths:
+ - "docs/**"
+ - "*.md"
+ - "tests/README.md"
+ - "scripts/*.md"
+ - "scripts/check-*.py"
+ - ".github/workflows/docs-quality.yml"
+ - ".markdownlint-cli2.yaml"
+ - "cspell.json"
+ - "lychee.toml"
+ - "requirements-test.txt"
+ - "pytest.ini"
+ push:
+ branches: [main]
+ paths:
+ - "docs/**"
+ - "*.md"
+ - "tests/README.md"
+ - "scripts/*.md"
+ - "scripts/check-*.py"
+ - ".github/workflows/docs-quality.yml"
+
+permissions:
+ contents: read
+
+jobs:
+ markdown-lint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: DavidAnson/markdownlint-cli2-action@v19
+ with:
+ globs: |
+ **/*.md
+ !venv/**
+ !.venv/**
+ !node_modules/**
+ !site/**
+ !docs/tmp/**
+
+ link-check:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Check external links
+ uses: lycheeverse/lychee-action@v2
+ with:
+ args: --config lychee.toml --no-progress .
+ fail: true
+ jobSummary: true
+
+ spell-check:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Check spelling
+ uses: streetsidesoftware/cspell-action@v6
+ with:
+ config: cspell.json
+ files: "**/*.md"
+ inline: error
+ incremental_files_only: false
+ strict: true
+
+ validate-codeblocks:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.11"
+ - name: Validate Python code blocks in markdown
+ run: python3 scripts/check-codeblocks.py
+
+ check-doc-coverage:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.11"
+ - name: Check documentation covers all routes and modules
+ run: python3 scripts/check-doc-coverage.py
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..1e01069
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,45 @@
+name: Deploy Documentation
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - "docs/**"
+ - "mkdocs.yml"
+ - ".github/workflows/docs.yml"
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: pages
+ cancel-in-progress: false
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.11"
+ - run: pip install mkdocs-material mkdocstrings mkdocstrings-python pymdown-extensions
+ - run: python3 scripts/sync-docs.py
+ - run: mkdocs build --site-dir site
+ - uses: actions/upload-pages-artifact@v3
+ with:
+ path: site
+
+ deploy:
+ needs: build
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.gitignore b/.gitignore
index 6dc2c74..13dd4b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,9 @@ tmp/
docs/tmp/
CTFd-Docker-Challenges/
+# MkDocs build output
+site/
+
# Database
instance/
uploads/
@@ -29,4 +32,7 @@ catering_sql_lab.db
# OS files
.DS_Store
+# Command Code (AI coding assistant state)
+.commandcode/
+
# Core maintainers only: optional commit-msg in .git/hooks/ (local-only, never pushed).
diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml
new file mode 100644
index 0000000..4cafe99
--- /dev/null
+++ b/.markdownlint-cli2.yaml
@@ -0,0 +1,41 @@
+# Markdownlint configuration for PwnzzAI docs
+# See: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md
+
+default: true
+
+# Allow inline HTML (needed for
, ,