{"id":29636544,"url":"https://github.com/jackchuka/mdschema","last_synced_at":"2026-02-22T18:34:21.605Z","repository":{"id":305644746,"uuid":"1023006685","full_name":"jackchuka/mdschema","owner":"jackchuka","description":"📝 A declarative schema-based Markdown validator that helps maintain consistent documentation structure across projects.","archived":false,"fork":false,"pushed_at":"2026-01-31T06:03:36.000Z","size":164,"stargazers_count":32,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-31T06:23:15.152Z","etag":null,"topics":["cli-tool","devtools","markdown","markdown-lint","schema"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jackchuka.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-20T10:26:43.000Z","updated_at":"2026-01-31T06:00:44.000Z","dependencies_parsed_at":null,"dependency_job_id":"087fe2bc-1356-4586-bfa3-3b05b4746803","html_url":"https://github.com/jackchuka/mdschema","commit_stats":null,"previous_names":["jackchuka/mdschema"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/jackchuka/mdschema","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackchuka%2Fmdschema","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackchuka%2Fmdschema/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackchuka%2Fmdschema/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackchuka%2Fmdschema/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jackchuka","download_url":"https://codeload.github.com/jackchuka/mdschema/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackchuka%2Fmdschema/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29116451,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-05T05:31:32.482Z","status":"ssl_error","status_checked_at":"2026-02-05T05:31:29.075Z","response_time":65,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli-tool","devtools","markdown","markdown-lint","schema"],"created_at":"2025-07-21T17:05:10.581Z","updated_at":"2026-02-05T08:06:53.089Z","avatar_url":"https://github.com/jackchuka.png","language":"Go","readme":"# mdschema\n\n[![Test](https://github.com/jackchuka/mdschema/workflows/Test/badge.svg)](https://github.com/jackchuka/mdschema/actions)\n[![Go Report Card](https://goreportcard.com/badge/github.com/jackchuka/mdschema)](https://goreportcard.com/report/github.com/jackchuka/mdschema)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nA declarative schema-based Markdown documentation validator that helps maintain consistent documentation structure across projects.\n\nThis README file itself is an example of how to use mdschema to validate and generate documentation.\n\n```bash\nmdschema check README.md --schema ./examples/README-schema.yml\n✓ No violations found\n```\n\n## Features\n\n- **Schema-driven validation** - Define your documentation structure in simple YAML\n- **Hierarchical structure** - Support for nested sections and complex document layouts\n- **Template generation** - Generate markdown templates from your schemas\n- **Comprehensive rules** - Validate headings, code blocks, images, tables, lists, links, and more\n- **Frontmatter validation** - Validate YAML frontmatter with type and format checking\n- **Link validation** - Check internal anchors, relative files, and external URLs\n- **Context-aware** - Uses AST parsing for accurate validation without string matching\n- **Fast and lightweight** - Single binary with no dependencies\n- **Cross-platform** - Works on Linux, macOS, and Windows\n- **Editor support** - JSON Schema for auto-completion and validation in VS Code, Neovim, and more\n\n## Installation\n\n### Homebrew\n\n```bash\nbrew install jackchuka/tap/mdschema\n```\n\n### Go Install\n\n```bash\ngo install github.com/jackchuka/mdschema/cmd/mdschema@latest\n```\n\n### From Source\n\n```bash\ngit clone https://github.com/jackchuka/mdschema.git\ncd mdschema\ngo build -o mdschema ./cmd/mdschema\n```\n\n## Quick Start\n\n1. **Initialize a schema** in your project:\n\n```bash\nmdschema init\n```\n\n2. **Validate your markdown files**:\n\n```bash\nmdschema check README.md docs/*.md\n```\n\n3. **Generate a template** from your schema:\n\n```bash\nmdschema generate -o new-doc.md\n```\n\n## Schema Format\n\nCreate a `.mdschema.yml` file to define your documentation structure:\n\n```yaml\nstructure:\n  - heading:\n      pattern: \"# [a-zA-Z0-9_\\\\- ]+\" # Regex pattern for project title\n    children:\n      - heading: \"## Features\"\n        optional: true\n      - heading: \"## Installation\"\n        code_blocks:\n          - { lang: bash, min: 1 } # Require at least 1 bash code block\n        children:\n          - heading: \"### Windows\"\n            optional: true\n          - heading: \"### macOS\"\n            optional: true\n      - heading: \"## Usage\"\n        code_blocks:\n          - { lang: go, min: 2 } # Require at least 2 Go code blocks\n        required_text:\n          - \"example\" # Must contain the word \"example\"\n  - heading: \"# LICENSE\"\n    optional: true\n```\n\n### Schema Elements\n\n#### Structure Elements\n\n- **`heading`** - Heading pattern:\n  - String: `\"# Title\"` (literal match)\n  - Regex: `{pattern: \"# .*\"}` (regex match after headings are extracted)\n  - Expression: `{expr: \"slug(filename) == slug(heading)\"}` (dynamic match)\n- **`description`** - Guidance text shown as HTML comment in generated templates\n- **`optional`** - Whether the section is optional (default: false)\n- **`count`** - Match multiple sections: `{min: 1, max: 5}` (0 = unlimited)\n- **`allow_additional`** - Allow extra subsections not defined in schema (default: false)\n- **`children`** - Nested subsections that must appear within this section\n\n##### Heading Expressions\n\nUse `expr` for dynamic heading matching (e.g., match filename to heading):\n\n```yaml\nstructure:\n  - heading:\n      expr: \"slug(filename) == slug(heading)\" # my-file.md matches \"# My File\"\n    children:\n      - heading: \"## Features\" # Static pattern for children\n```\n\n**Available functions:**\n\n| Function                 | Description         | Example                                         |\n| ------------------------ | ------------------- | ----------------------------------------------- |\n| `slug(s)`                | URL-friendly slug   | `slug(\"My File\")` → `\"my-file\"`                 |\n| `kebab(s)`               | PascalCase to kebab | `kebab(\"MyFile\")` → `\"my-file\"`                 |\n| `lower(s)` / `upper(s)`  | Case conversion     | `lower(\"README\")` → `\"readme\"`                  |\n| `trimPrefix(s, pattern)` | Remove regex prefix | `trimPrefix(\"01-file\", \"^\\\\d+-\")` → `\"file\"`    |\n| `trimSuffix(s, pattern)` | Remove regex suffix | `trimSuffix(\"file_draft\", \"_draft\")` → `\"file\"` |\n| `hasPrefix(s, prefix)`   | Check prefix        | `hasPrefix(\"api-ref\", \"api\")` → `true`          |\n| `hasSuffix(s, suffix)`   | Check suffix        | `hasSuffix(\"file_v2\", \"_v2\")` → `true`          |\n| `strContains(s, substr)` | Check contains      | `strContains(\"api-ref\", \"api\")` → `true`        |\n| `match(s, pattern)`      | Regex match         | `match(\"test-123\", \"test-\\\\d+\")` → `true`       |\n| `replace(s, old, new)`   | Replace all         | `replace(\"a-b-c\", \"-\", \"_\")` → `\"a_b_c\"`        |\n\n**Variables:**\n\n- `filename` (without extension)\n- `heading` (heading text)\n- `level` (heading level 1-6)\n\n#### Section Rules (apply to each section)\n\n- **`required_text`** - Text that must appear (`\"text\"` for substring or `{pattern: \"...\"}` for regex)\n- **`forbidden_text`** - Text that must NOT appear (`\"text\"` for substring or `{pattern: \"...\"}` for regex)\n- **`code_blocks`** - Code block requirements: `{lang: \"bash\", min: 1, max: 3}`\n- **`images`** - Image requirements: `{min: 1, require_alt: true, formats: [\"png\", \"svg\"]}`\n- **`tables`** - Table requirements: `{min: 1, min_columns: 2, required_headers: [\"Name\"]}`\n- **`lists`** - List requirements: `{min: 1, type: \"ordered\", min_items: 3}`\n- **`word_count`** - Word count constraints: `{min: 50, max: 500}`\n\n#### Global Rules (apply to entire document)\n\n- **`links`** - Link validation (internal anchors, relative files, external URLs)\n- **`heading_rules`** - Heading constraints (no skipped levels, unique headings, max depth)\n- **`frontmatter`** - YAML frontmatter validation (required fields, types, formats)\n\n## Commands\n\n### `check` - Validate Documents\n\n```bash\nmdschema check README.md docs/**/*.md\nmdschema check --schema custom.yml *.md\n```\n\n### `generate` - Create Templates\n\n```bash\n# Generate from .mdschema.yml\nmdschema generate\n# Generate from specific schema file\nmdschema generate --schema custom.yml\n# Generate and save to file\nmdschema generate -o template.md\n```\n\n### `init` - Initialize Schema\n\n```bash\n# Create .mdschema.yml with defaults\nmdschema init\n```\n\n### `derive` - Infer Schema from Document\n\n```bash\n# Infer schema from existing markdown, output to stdout\nmdschema derive README.md\n\n# Save inferred schema to a file\nmdschema derive README.md -o inferred-schema.yml\n```\n\n## Examples\n\n### Basic README Schema\n\n```yaml\nstructure:\n  - heading:\n      pattern: \"# .*\"\n    children:\n      - heading: \"## Installation\"\n        code_blocks:\n          - { lang: bash, min: 1 }\n      - heading: \"## Usage\"\n        code_blocks:\n          - { lang: go, min: 1 }\n```\n\n### API Documentation Schema\n\n```yaml\nstructure:\n  - heading: \"# API Reference\"\n    children:\n      - heading: \"## Authentication\"\n        required_text: [\"API key\", \"Bearer token\"]\n      - heading: \"## Endpoints\"\n        children:\n          - heading: \"### GET /users\"\n            code_blocks:\n              - { lang: json, min: 1 }\n              - { lang: curl, min: 1 }\n```\n\n### Tutorial Schema\n\n```yaml\nstructure:\n  - heading:\n      pattern: \"# .*\"\n    children:\n      - heading: \"## Prerequisites\"\n      - heading:\n          pattern: \"## Step [0-9]+: .*\"\n        count:\n          min: 1 # At least 1 step required\n          max: 0 # Unlimited steps allowed\n        code_blocks:\n          - { min: 1 } # Each step must have a code block\n      - heading: \"## Next Steps\"\n        optional: true\n```\n\n### Flexible Documentation Schema (allow additional sections)\n\n```yaml\nstructure:\n  - heading: \"# Project Name\"\n    allow_additional: true # Allow extra subsections not defined in schema\n    children:\n      - heading: \"## Overview\"\n      - heading: \"## Installation\"\n        code_blocks:\n          - { lang: bash, min: 1 }\n      # Users can add any other sections like \"## FAQ\", \"## Troubleshooting\", etc.\n```\n\n### Blog Post Schema (comprehensive example)\n\n```yaml\n# Global rules\nfrontmatter:\n  # optional: false is default, meaning frontmatter is required\n  fields:\n    - { name: \"title\" } # required by default\n    - { name: \"date\", format: date } # required by default\n    - { name: \"author\", optional: true, format: email }\n    - { name: \"tags\", optional: true, type: array }\n\nheading_rules:\n  no_skip_levels: true\n  max_depth: 3\n\nlinks:\n  validate_internal: true\n  validate_files: true\n\n# Document structure\nstructure:\n  - heading:\n      pattern: \"# .*\"\n    children:\n      - heading: \"## Introduction\"\n        word_count: { min: 100, max: 300 }\n        forbidden_text: [\"TODO\", \"FIXME\"]\n      - heading: \"## Content\"\n        images:\n          - { min: 1, require_alt: true }\n        code_blocks:\n          - { min: 1 }\n      - heading: \"## Conclusion\"\n        word_count: { min: 50 }\n        lists:\n          - { min: 1, type: unordered }\n```\n\n## Validation Rules\n\nmdschema includes comprehensive validation rules organized into three categories:\n\n### Section Rules (per-section validation)\n\n| Rule               | Description                                        | Options                                                        |\n| ------------------ | -------------------------------------------------- | -------------------------------------------------------------- |\n| **Structure**      | Ensures sections appear in correct order/hierarchy | `heading`, `optional`, `count`, `allow_additional`, `children` |\n| **Required Text**  | Text/patterns that must appear                     | `\"text\"` (literal) or `{pattern: \"...\"}` (regex)               |\n| **Forbidden Text** | Text/patterns that must NOT appear                 | `\"text\"` (literal) or `{pattern: \"...\"}` (regex)               |\n| **Code Blocks**    | Code block requirements                            | `lang`, `min`, `max`                                           |\n| **Images**         | Image presence and format                          | `min`, `max`, `require_alt`, `formats`                         |\n| **Tables**         | Table structure validation                         | `min`, `max`, `min_columns`, `required_headers`                |\n| **Lists**          | List presence and type                             | `min`, `max`, `type`, `min_items`                              |\n| **Word Count**     | Content length constraints                         | `min`, `max`                                                   |\n\n### Global Rules (document-wide validation)\n\n#### Link Validation\n\n```yaml\nlinks:\n  validate_internal: true # Check anchor links (#section)\n  validate_files: true # Check relative file links (./file.md)\n  validate_external: false # Check external URLs (slower)\n  external_timeout: 10 # Timeout in seconds\n  allowed_domains: # Restrict to these domains\n    - github.com\n    - golang.org\n  blocked_domains: # Block these domains\n    - example.com\n```\n\n#### Heading Rules\n\n```yaml\nheading_rules:\n  no_skip_levels: true # Disallow h1 -\u003e h3 without h2\n  unique: true # All headings must be unique\n  unique_per_level: false # Unique within same level only\n  max_depth: 4 # Maximum heading depth (h4)\n```\n\n#### Frontmatter Validation\n\n```yaml\nfrontmatter:\n  optional: true # Set to make frontmatter optional (default: required)\n  fields:\n    - { name: \"title\", type: string } # required by default\n    - { name: \"date\", type: date, format: date } # required by default\n    - { name: \"author\", optional: true, format: email } # explicitly optional\n    - { name: \"tags\", optional: true, type: array }\n    - { name: \"draft\", optional: true, type: boolean }\n    - { name: \"version\", optional: true, type: number }\n    - { name: \"repo\", optional: true, format: url }\n```\n\n**Field types:** `string`, `number`, `boolean`, `array`, `date`\n**Field formats:** `date` (YYYY-MM-DD), `email`, `url`\n\n## Use Cases\n\n- **Documentation Standards** - Enforce consistent README structure across repositories\n- **API Documentation** - Ensure all endpoints have required sections and examples\n- **Tutorial Validation** - Verify step-by-step guides follow the expected format\n- **CI/CD Integration** - Validate documentation in pull requests\n- **Template Generation** - Create starter templates for new projects\n\n## GitHub Action\n\nValidate your Markdown files in CI/CD pipelines using the mdschema GitHub Action.\n\n### Basic Usage\n\n```yaml\nname: Validate Documentation\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  validate:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Validate markdown\n        uses: jackchuka/mdschema@v0.9.1\n        with:\n          files: \"README.md docs/**/*.md\"\n          schema: \".mdschema.yml\"\n```\n\n### Inputs\n\n| Input               | Description                                    | Default         |\n| ------------------- | ---------------------------------------------- | --------------- |\n| `version`           | mdschema CLI version (use `latest` for newest) | Action ref      |\n| `files`             | Files or glob patterns                         | `**/*.md`       |\n| `schema`            | Path to schema file                            | `.mdschema.yml` |\n| `args`              | Additional CLI arguments                       | (empty)         |\n| `working-directory` | Working directory for validation               | `.`             |\n\n### Monorepo Example\n\n```yaml\n- uses: jackchuka/mdschema@v0.9.1\n  with:\n    working-directory: \"./packages/docs\"\n    files: \"**/*.md\"\n```\n\n## Editor Support\n\nmdschema provides a [JSON Schema](https://json-schema.org/) for `.mdschema.yml` files, enabling auto-completion, validation, and hover documentation in editors that support YAML Language Server.\n\n### VS Code\n\nAdd this to your `.vscode/settings.json`:\n\n```json\n{\n  \"yaml.schemas\": {\n    \"https://raw.githubusercontent.com/jackchuka/mdschema/main/schema.json\": \".mdschema.yml\"\n  }\n}\n```\n\nOr add a schema comment at the top of your `.mdschema.yml` file:\n\n```yaml\n# yaml-language-server: $schema=https://raw.githubusercontent.com/jackchuka/mdschema/main/schema.json\nstructure:\n  - heading: \"# My Project\"\n```\n\n### Other Editors\n\nAny editor with YAML Language Server support (Neovim, JetBrains IDEs, etc.) can use the schema URL:\n\n```\nhttps://raw.githubusercontent.com/jackchuka/mdschema/main/schema.json\n```\n\n## Development\n\n### Running Tests\n\n```bash\ngo test ./...\n```\n\n### Building\n\n```bash\ngo build -o mdschema ./cmd/mdschema\n```\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change. See [CONTRIBUTING.md](CONTRIBUTING.md) for more details.\n\n## License\n\nMIT License - see [LICENSE](LICENSE) for details.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjackchuka%2Fmdschema","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjackchuka%2Fmdschema","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjackchuka%2Fmdschema/lists"}