dddlint 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dddlint-0.1.2/.coverage +0 -0
- dddlint-0.1.2/.github/actions/smallsteps/action.yaml +20 -0
- dddlint-0.1.2/.github/workflows/workflow.yaml +123 -0
- dddlint-0.1.2/.gitignore +10 -0
- dddlint-0.1.2/.pre-commit-config.yaml +94 -0
- dddlint-0.1.2/.python-version +1 -0
- dddlint-0.1.2/.skills/dddlint/SKILL.md +46 -0
- dddlint-0.1.2/PKG-INFO +184 -0
- dddlint-0.1.2/README.md +167 -0
- dddlint-0.1.2/coverage.json +1 -0
- dddlint-0.1.2/dddlint.yaml +21 -0
- dddlint-0.1.2/examples/billing/invoice_service.py +14 -0
- dddlint-0.1.2/examples/core/customer_repo.py +13 -0
- dddlint-0.1.2/examples/core/order_service.py +17 -0
- dddlint-0.1.2/examples/dddlint.yaml +23 -0
- dddlint-0.1.2/llms.txt +20 -0
- dddlint-0.1.2/pyproject.toml +137 -0
- dddlint-0.1.2/smallsteps.toml +9 -0
- dddlint-0.1.2/src/dddlint/__init__.py +1 -0
- dddlint-0.1.2/src/dddlint/check.py +135 -0
- dddlint-0.1.2/src/dddlint/cli.py +146 -0
- dddlint-0.1.2/src/dddlint/config.py +31 -0
- dddlint-0.1.2/src/dddlint/config_check.py +89 -0
- dddlint-0.1.2/src/dddlint/extract.py +82 -0
- dddlint-0.1.2/src/dddlint/mcp_server.py +76 -0
- dddlint-0.1.2/src/dddlint/server.py +159 -0
- dddlint-0.1.2/src/dddlint/view.py +364 -0
- dddlint-0.1.2/tests/test_check.py +59 -0
- dddlint-0.1.2/tests/test_cli.py +54 -0
- dddlint-0.1.2/tests/test_config.py +23 -0
- dddlint-0.1.2/tests/test_config_check.py +88 -0
- dddlint-0.1.2/tests/test_extract.py +44 -0
- dddlint-0.1.2/tests/test_server.py +126 -0
- dddlint-0.1.2/tests/test_view.py +63 -0
- dddlint-0.1.2/uv.lock +2832 -0
- dddlint-0.1.2/zensical.toml +12 -0
dddlint-0.1.2/.coverage
ADDED
|
Binary file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: 'Smallsteps Ratchet Evaluation'
|
|
2
|
+
description: 'Evaluates your quality goals'
|
|
3
|
+
inputs:
|
|
4
|
+
SMALLSTEPS_PYTEST_COVERAGE:
|
|
5
|
+
description: "Current live performance value for 'Pytest Coverage'"
|
|
6
|
+
required: true
|
|
7
|
+
|
|
8
|
+
runs:
|
|
9
|
+
using: 'composite'
|
|
10
|
+
steps:
|
|
11
|
+
- name: Install uv
|
|
12
|
+
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
|
|
13
|
+
with:
|
|
14
|
+
enable-cache: true
|
|
15
|
+
|
|
16
|
+
- name: Run Smallsteps Guardrail Validation
|
|
17
|
+
shell: bash
|
|
18
|
+
run: uvx smallsteps check
|
|
19
|
+
env:
|
|
20
|
+
SMALLSTEPS_PYTEST_COVERAGE: ${{ inputs.SMALLSTEPS_PYTEST_COVERAGE }}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
name: CI and Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [main]
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Install uv
|
|
16
|
+
uses: astral-sh/setup-uv@v5
|
|
17
|
+
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
run: uv python install 3.12
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: uv sync --all-extras --frozen
|
|
23
|
+
|
|
24
|
+
- name: Lint with ruff
|
|
25
|
+
run: uv run ruff check .
|
|
26
|
+
|
|
27
|
+
- name: Format check with ruff
|
|
28
|
+
run: uv run ruff format --check .
|
|
29
|
+
|
|
30
|
+
- name: Type check with basedpyright
|
|
31
|
+
run: uv run basedpyright
|
|
32
|
+
|
|
33
|
+
- name: Run unit tests (parallel)
|
|
34
|
+
run: uv run pytest -m unit -n auto --cov=dddlint --cov-report=term-missing --cov-report=json
|
|
35
|
+
|
|
36
|
+
- name: Compute coverage
|
|
37
|
+
id: cov
|
|
38
|
+
run: echo "value=$(uv run python -c 'import json; print(json.load(open("coverage.json"))["totals"]["percent_covered"])')" >> "$GITHUB_OUTPUT"
|
|
39
|
+
|
|
40
|
+
- name: Enforce coverage ratchet
|
|
41
|
+
uses: ./.github/actions/smallsteps
|
|
42
|
+
with:
|
|
43
|
+
SMALLSTEPS_PYTEST_COVERAGE: ${{ steps.cov.outputs.value }}
|
|
44
|
+
|
|
45
|
+
publish:
|
|
46
|
+
needs: test
|
|
47
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
environment:
|
|
50
|
+
name: pypi
|
|
51
|
+
url: https://pypi.org/p/dddlint
|
|
52
|
+
permissions:
|
|
53
|
+
id-token: write
|
|
54
|
+
contents: write
|
|
55
|
+
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v4
|
|
58
|
+
with:
|
|
59
|
+
fetch-depth: 0
|
|
60
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
61
|
+
- name: Install uv
|
|
62
|
+
uses: astral-sh/setup-uv@v5
|
|
63
|
+
|
|
64
|
+
- name: Set up Python
|
|
65
|
+
run: uv python install 3.12
|
|
66
|
+
|
|
67
|
+
- name: Install dependencies
|
|
68
|
+
run: uv sync --all-extras --frozen
|
|
69
|
+
|
|
70
|
+
- name: Configure git
|
|
71
|
+
run: |
|
|
72
|
+
git config user.name "github-actions[bot]"
|
|
73
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
74
|
+
|
|
75
|
+
- name: Bump version
|
|
76
|
+
run: uv run bump-my-version bump patch
|
|
77
|
+
|
|
78
|
+
- name: Push version bump
|
|
79
|
+
run: git push --follow-tags
|
|
80
|
+
|
|
81
|
+
- name: Build package
|
|
82
|
+
run: uv build
|
|
83
|
+
|
|
84
|
+
- name: Publish to PyPI
|
|
85
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
86
|
+
|
|
87
|
+
docs:
|
|
88
|
+
needs: test
|
|
89
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
90
|
+
runs-on: ubuntu-latest
|
|
91
|
+
permissions:
|
|
92
|
+
contents: read
|
|
93
|
+
pages: write
|
|
94
|
+
id-token: write
|
|
95
|
+
environment:
|
|
96
|
+
name: github-pages
|
|
97
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
98
|
+
steps:
|
|
99
|
+
- uses: actions/checkout@v4
|
|
100
|
+
|
|
101
|
+
- name: Install uv
|
|
102
|
+
uses: astral-sh/setup-uv@v5
|
|
103
|
+
|
|
104
|
+
- name: Set up Python
|
|
105
|
+
run: uv python install 3.12
|
|
106
|
+
|
|
107
|
+
- name: Install dependencies
|
|
108
|
+
run: uv sync --all-extras --frozen
|
|
109
|
+
|
|
110
|
+
- name: Build docs
|
|
111
|
+
run: uv run zensical build
|
|
112
|
+
|
|
113
|
+
- name: Setup Pages
|
|
114
|
+
uses: actions/configure-pages@v5
|
|
115
|
+
|
|
116
|
+
- name: Upload artifact
|
|
117
|
+
uses: actions/upload-pages-artifact@v3
|
|
118
|
+
with:
|
|
119
|
+
path: site
|
|
120
|
+
|
|
121
|
+
- name: Deploy to GitHub Pages
|
|
122
|
+
id: deployment
|
|
123
|
+
uses: actions/deploy-pages@v4
|
dddlint-0.1.2/.gitignore
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
default_language_version:
|
|
2
|
+
python: python3
|
|
3
|
+
default_stages: [pre-commit]
|
|
4
|
+
repos:
|
|
5
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
6
|
+
rev: v6.0.0
|
|
7
|
+
hooks:
|
|
8
|
+
- id: trailing-whitespace
|
|
9
|
+
args: [--markdown-linebreak-ext=md]
|
|
10
|
+
- id: end-of-file-fixer
|
|
11
|
+
- id: mixed-line-ending
|
|
12
|
+
args: [--fix=lf]
|
|
13
|
+
- id: check-yaml
|
|
14
|
+
args: [--unsafe]
|
|
15
|
+
- id: check-json
|
|
16
|
+
- id: check-toml
|
|
17
|
+
- id: check-merge-conflict
|
|
18
|
+
- id: check-case-conflict
|
|
19
|
+
- id: check-ast
|
|
20
|
+
- id: check-docstring-first
|
|
21
|
+
- id: debug-statements
|
|
22
|
+
- id: name-tests-test
|
|
23
|
+
args: [--pytest-test-first]
|
|
24
|
+
- id: detect-private-key
|
|
25
|
+
- id: check-added-large-files
|
|
26
|
+
args: [--maxkb=1000]
|
|
27
|
+
|
|
28
|
+
- repo: https://github.com/igorshubovych/markdownlint-cli
|
|
29
|
+
rev: v0.47.0
|
|
30
|
+
hooks:
|
|
31
|
+
- id: markdownlint
|
|
32
|
+
args: [--fix, --disable, MD013, MD024, MD033, MD036, MD040, MD041, MD060, --]
|
|
33
|
+
|
|
34
|
+
- repo: https://github.com/commitizen-tools/commitizen
|
|
35
|
+
rev: v4.12.1
|
|
36
|
+
hooks:
|
|
37
|
+
- id: commitizen
|
|
38
|
+
stages: [commit-msg]
|
|
39
|
+
|
|
40
|
+
- repo: https://github.com/benomahony/nasa-lsp
|
|
41
|
+
rev: v0.1.7
|
|
42
|
+
hooks:
|
|
43
|
+
- id: nasa-lsp
|
|
44
|
+
exclude: ^(tests|examples)/
|
|
45
|
+
|
|
46
|
+
- repo: local
|
|
47
|
+
hooks:
|
|
48
|
+
- id: ruff-format
|
|
49
|
+
name: ruff-format
|
|
50
|
+
entry: uv run ruff format
|
|
51
|
+
language: system
|
|
52
|
+
types_or: [python, pyi]
|
|
53
|
+
require_serial: true
|
|
54
|
+
- id: ruff
|
|
55
|
+
name: ruff
|
|
56
|
+
entry: uv run ruff check --fix --exit-non-zero-on-fix
|
|
57
|
+
language: system
|
|
58
|
+
types_or: [python, pyi]
|
|
59
|
+
require_serial: true
|
|
60
|
+
- id: basedpyright
|
|
61
|
+
name: basedpyright
|
|
62
|
+
entry: uv run basedpyright
|
|
63
|
+
language: system
|
|
64
|
+
types: [python]
|
|
65
|
+
pass_filenames: true
|
|
66
|
+
require_serial: true
|
|
67
|
+
- id: vulture
|
|
68
|
+
name: vulture
|
|
69
|
+
entry: uv run vulture src/dddlint/ --min-confidence 80
|
|
70
|
+
language: system
|
|
71
|
+
types: [python]
|
|
72
|
+
files: ^src/dddlint/
|
|
73
|
+
pass_filenames: false
|
|
74
|
+
require_serial: true
|
|
75
|
+
- id: bandit
|
|
76
|
+
name: bandit
|
|
77
|
+
entry: uv run bandit -c pyproject.toml -r src/dddlint/
|
|
78
|
+
language: system
|
|
79
|
+
files: ^src/dddlint/
|
|
80
|
+
exclude: ^tests/
|
|
81
|
+
pass_filenames: false
|
|
82
|
+
- id: cliqa
|
|
83
|
+
name: cliqa
|
|
84
|
+
entry: uv run cliqa analyze dddlint
|
|
85
|
+
language: system
|
|
86
|
+
files: ^(pyproject\.toml|src/.*\.py)$
|
|
87
|
+
pass_filenames: false
|
|
88
|
+
require_serial: true
|
|
89
|
+
- id: deptry
|
|
90
|
+
name: deptry
|
|
91
|
+
entry: uv run deptry . --extend-exclude "\.venv|tests"
|
|
92
|
+
language: system
|
|
93
|
+
pass_filenames: false
|
|
94
|
+
require_serial: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dddlint
|
|
3
|
+
description: Help users work with dddlint. Use when the user asks about dddlint features, usage, or wants to implement functionality using this project.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# dddlint Skill
|
|
7
|
+
|
|
8
|
+
This skill helps you work with dddlint.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
Use this skill when:
|
|
13
|
+
|
|
14
|
+
- User asks about dddlint features or capabilities
|
|
15
|
+
- User wants to use dddlint in their code
|
|
16
|
+
- User needs help with dddlint API or CLI
|
|
17
|
+
- User wants examples of dddlint usage
|
|
18
|
+
|
|
19
|
+
## Project Information
|
|
20
|
+
|
|
21
|
+
- **Description**: Polyglot ubiquitous language linter for codebases and coding agents
|
|
22
|
+
- **Author**: benomahony
|
|
23
|
+
- **Documentation**: See docs/index.md for full documentation
|
|
24
|
+
- **Source**: src/dddlint/
|
|
25
|
+
|
|
26
|
+
## Quick Reference
|
|
27
|
+
|
|
28
|
+
### CLI Usage
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
dddlint --help
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Library Usage
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from dddlint.core import example_function
|
|
38
|
+
|
|
39
|
+
result = example_function("World")
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Resources
|
|
43
|
+
|
|
44
|
+
- Check docs/index.md for comprehensive documentation
|
|
45
|
+
- Check llms.txt for LLM-friendly documentation summary
|
|
46
|
+
- Check src/dddlint/ for implementation details
|
dddlint-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dddlint
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Polyglot ubiquitous language linter for codebases and coding agents
|
|
5
|
+
Project-URL: Repository, https://github.com/benomahony/dddlint
|
|
6
|
+
Author: benomahony
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Requires-Dist: lsprotocol>=2025.0.0
|
|
9
|
+
Requires-Dist: mcp>=1.28.1
|
|
10
|
+
Requires-Dist: pydantic>=2.13.4
|
|
11
|
+
Requires-Dist: pygls>=2.1.1
|
|
12
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
13
|
+
Requires-Dist: rich>=15.0.0
|
|
14
|
+
Requires-Dist: tree-sitter-language-pack>=1.8.1
|
|
15
|
+
Requires-Dist: typer>=0.26.2
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# dddlint
|
|
19
|
+
|
|
20
|
+
Polyglot ubiquitous language linter. Reads class, function, method, and type names across **306 languages** and enforces them against a domain vocabulary — banned terms, non-canonical synonyms, and one concept spelled multiple ways.
|
|
21
|
+
|
|
22
|
+
Works with any language tree-sitter recognises, without per-language queries. Slots into pre-commit hooks, CI pipelines, and coding agent loops via a non-zero exit code on findings. Ships with an LSP server for inline editor diagnostics and rename code actions.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
uv add dddlint
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
# lint current directory (looks for dddlint.yaml)
|
|
34
|
+
dddlint lint
|
|
35
|
+
|
|
36
|
+
# lint a specific path
|
|
37
|
+
dddlint lint src/
|
|
38
|
+
|
|
39
|
+
# open an interactive language graph in the browser
|
|
40
|
+
dddlint html
|
|
41
|
+
|
|
42
|
+
# start the LSP server (stdio)
|
|
43
|
+
dddlint lsp
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Config
|
|
47
|
+
|
|
48
|
+
Place `dddlint.yaml` at the project root. When a path is passed to `lint` or `html`, the config is looked up in that directory first, then falls back to the current working directory.
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
similarity_threshold: 0.85 # Jaccard threshold for drift detection
|
|
52
|
+
enforce_canonical: true # flag alias terms in addition to forbidden ones
|
|
53
|
+
|
|
54
|
+
# terms that must never appear in a definition name
|
|
55
|
+
forbidden:
|
|
56
|
+
- util
|
|
57
|
+
- helper
|
|
58
|
+
- manager
|
|
59
|
+
|
|
60
|
+
# canonical terms and their aliases
|
|
61
|
+
synonyms:
|
|
62
|
+
- canonical: customer
|
|
63
|
+
aliases: [client, user, accountholder]
|
|
64
|
+
- canonical: order
|
|
65
|
+
aliases: [purchase, transaction]
|
|
66
|
+
|
|
67
|
+
# high-level business domains
|
|
68
|
+
domains:
|
|
69
|
+
- name: commerce
|
|
70
|
+
include: ["**/commerce/**"]
|
|
71
|
+
synonyms:
|
|
72
|
+
- canonical: order
|
|
73
|
+
aliases: [purchase]
|
|
74
|
+
|
|
75
|
+
# bounded contexts — same structure as domains, applied after (context wins on conflict)
|
|
76
|
+
contexts:
|
|
77
|
+
- name: billing
|
|
78
|
+
include: ["**/billing/**"]
|
|
79
|
+
forbidden: [discount]
|
|
80
|
+
synonyms:
|
|
81
|
+
- canonical: invoice
|
|
82
|
+
aliases: [bill, statement]
|
|
83
|
+
|
|
84
|
+
# register file extensions for languages not auto-detected
|
|
85
|
+
languages:
|
|
86
|
+
svelte:
|
|
87
|
+
extensions: [".svelte"]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Global rules apply everywhere. Domain rules apply to matching paths. Context rules apply after domains, so a context can override a domain synonym.
|
|
91
|
+
|
|
92
|
+
## Rules
|
|
93
|
+
|
|
94
|
+
| Rule | Severity | Description |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| `forbidden` | error | A definition name contains a banned term |
|
|
97
|
+
| `alias` | warning | A definition uses a non-canonical synonym — includes a rename suggestion |
|
|
98
|
+
| `drift` | info | The same concept is spelled multiple ways across the codebase |
|
|
99
|
+
| `config:forbidden-canonical-clash` | error | A term is both forbidden and a canonical synonym |
|
|
100
|
+
| `config:alias-conflict` | warning | The same alias maps to different canonicals in different scopes |
|
|
101
|
+
| `config:duplicate-name` | info | Two domains or contexts have suspiciously similar names |
|
|
102
|
+
|
|
103
|
+
Config rules are checked against `dddlint.yaml` itself on every run.
|
|
104
|
+
|
|
105
|
+
## LSP
|
|
106
|
+
|
|
107
|
+
The LSP server publishes diagnostics on file open and save, scanning the entire workspace each time so cross-file drift is always caught. Alias findings include a code action to rename the identifier to the canonical term with case preserved (`ClientRepo` → `CustomerRepo`, `get_client` → `get_customer`).
|
|
108
|
+
|
|
109
|
+
**Neovim** — add to `init.lua`:
|
|
110
|
+
|
|
111
|
+
```lua
|
|
112
|
+
vim.api.nvim_create_autocmd("BufReadPost", {
|
|
113
|
+
callback = function(args)
|
|
114
|
+
local root = vim.fs.root(args.buf, { "dddlint.yaml" })
|
|
115
|
+
if root then
|
|
116
|
+
vim.lsp.start({
|
|
117
|
+
name = "dddlint",
|
|
118
|
+
cmd = { "dddlint", "lsp" },
|
|
119
|
+
root_dir = root,
|
|
120
|
+
}, { bufnr = args.buf })
|
|
121
|
+
end
|
|
122
|
+
end,
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The autocmd fires on every buffer, attaches only when `dddlint.yaml` is found, and is language-agnostic — no filetype list required.
|
|
127
|
+
|
|
128
|
+
**VS Code** — via a generic LSP client extension:
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"lsp.servers": {
|
|
133
|
+
"dddlint": {
|
|
134
|
+
"command": ["uvx", "dddlint", "lsp"],
|
|
135
|
+
"filetypes": ["*"]
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Helix** — `.helix/languages.toml`:
|
|
142
|
+
|
|
143
|
+
```toml
|
|
144
|
+
[language-server.dddlint]
|
|
145
|
+
command = "dddlint"
|
|
146
|
+
args = ["lsp"]
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Language support
|
|
150
|
+
|
|
151
|
+
Extraction is powered by [`tree-sitter-language-pack`](https://github.com/Goldziher/tree-sitter-language-pack), which covers 306 languages including:
|
|
152
|
+
|
|
153
|
+
Ada · Agda · Arduino · Bash · C · C++ · C# · Clojure · COBOL · Crystal · CSS · D · Dart · Dockerfile · Elixir · Elm · Erlang · F# · Fortran · GDScript · GLSL · Go · GraphQL · Groovy · Hack · Haskell · HCL · HTML · Java · JavaScript · Julia · Kotlin · Lean · Lua · MATLAB · Mojo · Nix · OCaml · Odin · Pascal · Perl · PHP · PowerShell · Prolog · Python · R · Racket · Ruby · Rust · Scala · Scheme · Solidity · SQL · Svelte · Swift · Terraform · TLA+ · TOML · TypeScript · V · VHDL · Vim · Vue · WebAssembly · XML · YAML · Zig — and [243 more](https://github.com/Goldziher/tree-sitter-language-pack).
|
|
154
|
+
|
|
155
|
+
## CI
|
|
156
|
+
|
|
157
|
+
```yaml
|
|
158
|
+
# .github/workflows/dddlint.yml
|
|
159
|
+
- run: uvx dddlint lint
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Exit code is `0` when clean, `1` when findings exist.
|
|
163
|
+
|
|
164
|
+
## Pre-commit
|
|
165
|
+
|
|
166
|
+
```yaml
|
|
167
|
+
# .pre-commit-config.yaml
|
|
168
|
+
repos:
|
|
169
|
+
- repo: local
|
|
170
|
+
hooks:
|
|
171
|
+
- id: dddlint
|
|
172
|
+
name: dddlint
|
|
173
|
+
entry: dddlint lint
|
|
174
|
+
language: python
|
|
175
|
+
pass_filenames: false
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Offline use
|
|
179
|
+
|
|
180
|
+
The language pack downloads parsers on first use. For CI or air-gapped runs, warm the cache in the image:
|
|
181
|
+
|
|
182
|
+
```sh
|
|
183
|
+
python -c "import tree_sitter_language_pack as t; t.download_all()"
|
|
184
|
+
```
|
dddlint-0.1.2/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# dddlint
|
|
2
|
+
|
|
3
|
+
Polyglot ubiquitous language linter. Reads class, function, method, and type names across **306 languages** and enforces them against a domain vocabulary — banned terms, non-canonical synonyms, and one concept spelled multiple ways.
|
|
4
|
+
|
|
5
|
+
Works with any language tree-sitter recognises, without per-language queries. Slots into pre-commit hooks, CI pipelines, and coding agent loops via a non-zero exit code on findings. Ships with an LSP server for inline editor diagnostics and rename code actions.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
uv add dddlint
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
# lint current directory (looks for dddlint.yaml)
|
|
17
|
+
dddlint lint
|
|
18
|
+
|
|
19
|
+
# lint a specific path
|
|
20
|
+
dddlint lint src/
|
|
21
|
+
|
|
22
|
+
# open an interactive language graph in the browser
|
|
23
|
+
dddlint html
|
|
24
|
+
|
|
25
|
+
# start the LSP server (stdio)
|
|
26
|
+
dddlint lsp
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Config
|
|
30
|
+
|
|
31
|
+
Place `dddlint.yaml` at the project root. When a path is passed to `lint` or `html`, the config is looked up in that directory first, then falls back to the current working directory.
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
similarity_threshold: 0.85 # Jaccard threshold for drift detection
|
|
35
|
+
enforce_canonical: true # flag alias terms in addition to forbidden ones
|
|
36
|
+
|
|
37
|
+
# terms that must never appear in a definition name
|
|
38
|
+
forbidden:
|
|
39
|
+
- util
|
|
40
|
+
- helper
|
|
41
|
+
- manager
|
|
42
|
+
|
|
43
|
+
# canonical terms and their aliases
|
|
44
|
+
synonyms:
|
|
45
|
+
- canonical: customer
|
|
46
|
+
aliases: [client, user, accountholder]
|
|
47
|
+
- canonical: order
|
|
48
|
+
aliases: [purchase, transaction]
|
|
49
|
+
|
|
50
|
+
# high-level business domains
|
|
51
|
+
domains:
|
|
52
|
+
- name: commerce
|
|
53
|
+
include: ["**/commerce/**"]
|
|
54
|
+
synonyms:
|
|
55
|
+
- canonical: order
|
|
56
|
+
aliases: [purchase]
|
|
57
|
+
|
|
58
|
+
# bounded contexts — same structure as domains, applied after (context wins on conflict)
|
|
59
|
+
contexts:
|
|
60
|
+
- name: billing
|
|
61
|
+
include: ["**/billing/**"]
|
|
62
|
+
forbidden: [discount]
|
|
63
|
+
synonyms:
|
|
64
|
+
- canonical: invoice
|
|
65
|
+
aliases: [bill, statement]
|
|
66
|
+
|
|
67
|
+
# register file extensions for languages not auto-detected
|
|
68
|
+
languages:
|
|
69
|
+
svelte:
|
|
70
|
+
extensions: [".svelte"]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Global rules apply everywhere. Domain rules apply to matching paths. Context rules apply after domains, so a context can override a domain synonym.
|
|
74
|
+
|
|
75
|
+
## Rules
|
|
76
|
+
|
|
77
|
+
| Rule | Severity | Description |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| `forbidden` | error | A definition name contains a banned term |
|
|
80
|
+
| `alias` | warning | A definition uses a non-canonical synonym — includes a rename suggestion |
|
|
81
|
+
| `drift` | info | The same concept is spelled multiple ways across the codebase |
|
|
82
|
+
| `config:forbidden-canonical-clash` | error | A term is both forbidden and a canonical synonym |
|
|
83
|
+
| `config:alias-conflict` | warning | The same alias maps to different canonicals in different scopes |
|
|
84
|
+
| `config:duplicate-name` | info | Two domains or contexts have suspiciously similar names |
|
|
85
|
+
|
|
86
|
+
Config rules are checked against `dddlint.yaml` itself on every run.
|
|
87
|
+
|
|
88
|
+
## LSP
|
|
89
|
+
|
|
90
|
+
The LSP server publishes diagnostics on file open and save, scanning the entire workspace each time so cross-file drift is always caught. Alias findings include a code action to rename the identifier to the canonical term with case preserved (`ClientRepo` → `CustomerRepo`, `get_client` → `get_customer`).
|
|
91
|
+
|
|
92
|
+
**Neovim** — add to `init.lua`:
|
|
93
|
+
|
|
94
|
+
```lua
|
|
95
|
+
vim.api.nvim_create_autocmd("BufReadPost", {
|
|
96
|
+
callback = function(args)
|
|
97
|
+
local root = vim.fs.root(args.buf, { "dddlint.yaml" })
|
|
98
|
+
if root then
|
|
99
|
+
vim.lsp.start({
|
|
100
|
+
name = "dddlint",
|
|
101
|
+
cmd = { "dddlint", "lsp" },
|
|
102
|
+
root_dir = root,
|
|
103
|
+
}, { bufnr = args.buf })
|
|
104
|
+
end
|
|
105
|
+
end,
|
|
106
|
+
})
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The autocmd fires on every buffer, attaches only when `dddlint.yaml` is found, and is language-agnostic — no filetype list required.
|
|
110
|
+
|
|
111
|
+
**VS Code** — via a generic LSP client extension:
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"lsp.servers": {
|
|
116
|
+
"dddlint": {
|
|
117
|
+
"command": ["uvx", "dddlint", "lsp"],
|
|
118
|
+
"filetypes": ["*"]
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Helix** — `.helix/languages.toml`:
|
|
125
|
+
|
|
126
|
+
```toml
|
|
127
|
+
[language-server.dddlint]
|
|
128
|
+
command = "dddlint"
|
|
129
|
+
args = ["lsp"]
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Language support
|
|
133
|
+
|
|
134
|
+
Extraction is powered by [`tree-sitter-language-pack`](https://github.com/Goldziher/tree-sitter-language-pack), which covers 306 languages including:
|
|
135
|
+
|
|
136
|
+
Ada · Agda · Arduino · Bash · C · C++ · C# · Clojure · COBOL · Crystal · CSS · D · Dart · Dockerfile · Elixir · Elm · Erlang · F# · Fortran · GDScript · GLSL · Go · GraphQL · Groovy · Hack · Haskell · HCL · HTML · Java · JavaScript · Julia · Kotlin · Lean · Lua · MATLAB · Mojo · Nix · OCaml · Odin · Pascal · Perl · PHP · PowerShell · Prolog · Python · R · Racket · Ruby · Rust · Scala · Scheme · Solidity · SQL · Svelte · Swift · Terraform · TLA+ · TOML · TypeScript · V · VHDL · Vim · Vue · WebAssembly · XML · YAML · Zig — and [243 more](https://github.com/Goldziher/tree-sitter-language-pack).
|
|
137
|
+
|
|
138
|
+
## CI
|
|
139
|
+
|
|
140
|
+
```yaml
|
|
141
|
+
# .github/workflows/dddlint.yml
|
|
142
|
+
- run: uvx dddlint lint
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Exit code is `0` when clean, `1` when findings exist.
|
|
146
|
+
|
|
147
|
+
## Pre-commit
|
|
148
|
+
|
|
149
|
+
```yaml
|
|
150
|
+
# .pre-commit-config.yaml
|
|
151
|
+
repos:
|
|
152
|
+
- repo: local
|
|
153
|
+
hooks:
|
|
154
|
+
- id: dddlint
|
|
155
|
+
name: dddlint
|
|
156
|
+
entry: dddlint lint
|
|
157
|
+
language: python
|
|
158
|
+
pass_filenames: false
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Offline use
|
|
162
|
+
|
|
163
|
+
The language pack downloads parsers on first use. For CI or air-gapped runs, warm the cache in the image:
|
|
164
|
+
|
|
165
|
+
```sh
|
|
166
|
+
python -c "import tree_sitter_language_pack as t; t.download_all()"
|
|
167
|
+
```
|