mir-linter 0.1.0a1__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.
Files changed (31) hide show
  1. mir_linter-0.1.0a1/PKG-INFO +279 -0
  2. mir_linter-0.1.0a1/README.md +265 -0
  3. mir_linter-0.1.0a1/mir/engine/config.py +207 -0
  4. mir_linter-0.1.0a1/mir/engine/disabler.py +272 -0
  5. mir_linter-0.1.0a1/mir/engine/rule_interface.py +94 -0
  6. mir_linter-0.1.0a1/mir/engine/rules_help.py +426 -0
  7. mir_linter-0.1.0a1/mir/engine/rules_loader.py +190 -0
  8. mir_linter-0.1.0a1/mir/engine/runner.py +355 -0
  9. mir_linter-0.1.0a1/mir/main.py +20 -0
  10. mir_linter-0.1.0a1/mir/rules/java/__init__.py +6 -0
  11. mir_linter-0.1.0a1/mir/rules/sql/__init__.py +6 -0
  12. mir_linter-0.1.0a1/mir/rules/sql/clause_alignment.py +261 -0
  13. mir_linter-0.1.0a1/mir/rules/sql/column_layout.py +439 -0
  14. mir_linter-0.1.0a1/mir/rules/sql/function_case.py +165 -0
  15. mir_linter-0.1.0a1/mir/rules/sql/indent.py +142 -0
  16. mir_linter-0.1.0a1/mir/rules/sql/keyword_case.py +240 -0
  17. mir_linter-0.1.0a1/mir/rules/sql/line_length.py +72 -0
  18. mir_linter-0.1.0a1/mir/rules/xml/__init__.py +6 -0
  19. mir_linter-0.1.0a1/mir_linter.egg-info/PKG-INFO +279 -0
  20. mir_linter-0.1.0a1/mir_linter.egg-info/SOURCES.txt +29 -0
  21. mir_linter-0.1.0a1/mir_linter.egg-info/dependency_links.txt +1 -0
  22. mir_linter-0.1.0a1/mir_linter.egg-info/entry_points.txt +3 -0
  23. mir_linter-0.1.0a1/mir_linter.egg-info/requires.txt +1 -0
  24. mir_linter-0.1.0a1/mir_linter.egg-info/top_level.txt +1 -0
  25. mir_linter-0.1.0a1/pyproject.toml +28 -0
  26. mir_linter-0.1.0a1/setup.cfg +4 -0
  27. mir_linter-0.1.0a1/tests/test_config.py +40 -0
  28. mir_linter-0.1.0a1/tests/test_disabler.py +52 -0
  29. mir_linter-0.1.0a1/tests/test_dynamic_loader.py +193 -0
  30. mir_linter-0.1.0a1/tests/test_meta_rules.py +88 -0
  31. mir_linter-0.1.0a1/tests/test_runner.py +336 -0
@@ -0,0 +1,279 @@
1
+ Metadata-Version: 2.4
2
+ Name: mir-linter
3
+ Version: 0.1.0a1
4
+ Summary: Mitchell's Ineffable Rules (IR) Linter - A modular linter with customizable rules for SQL, Java, and XML.
5
+ License: MIT
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Topic :: Software Development :: Quality Assurance
10
+ Classifier: Development Status :: 4 - Beta
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: pyyaml>=6.0
14
+
15
+ # Mitchell's Ineffable Rules (IR) Linter
16
+
17
+ A highly modular, extensible linting and auto-fixing utility for **SQL, Java, and XML** codebase assets. The linter can be run locally via `uv` / `python`, inside a containerized **Docker** execution loop, or integrated into your lifecycle as a **Git Pre-commit Hook**.
18
+
19
+ ---
20
+
21
+ ## Features
22
+
23
+ - **Extensible Rules Architecture**: Add language rules simply by adding Python rule modules under `mir/rules/<language>/`.
24
+ - **User Styling Guides**: Detailed style guides explaining each rule, its configuration parameters, auto-fixability, and clean vs. violating examples under `docs/`.
25
+ - **Precedence-Aware Configuration**: Merges CLI flags, environment variables, and YAML config file settings:
26
+ $$\text{CLI arguments} > \text{Environment variables} > \text{Config file} > \text{Defaults}$$
27
+ - **Granular Disable Comments**:
28
+ - **Single-line disable** (disables a rule for the next non-comment code line):
29
+ - SQL: `-- IR-rule-name`
30
+ - Java: `// IR-rule-name`
31
+ - XML: `<!-- IR-rule-name -->`
32
+ - **Block disable** (disables a rule for a range of lines):
33
+ - SQL: `-- IR-start-rule-name` ... `-- IR-end-rule-name`
34
+ - Java: `// IR-start-rule-name` ... `// IR-end-rule-name`
35
+ - XML: `<!-- IR-start-rule-name -->` ... `<!-- IR-end-rule-name -->`
36
+ - **Execution Modes**:
37
+ - `check`: Validates files and reports all errors (default).
38
+ - `fix`: Applies automatic corrections to fixable rules directly to files.
39
+ - `dry-run`: Runs checks and generates unified diffs showing proposed fixes without modifying the source.
40
+ - `verbose`: Prints rule descriptions and the offending lines along with errors.
41
+
42
+ ---
43
+
44
+ ## Directory Structure
45
+
46
+ ```text
47
+ .
48
+ ├── docs/ # Language-specific Style Guides
49
+ │ ├── sql.md # SQL style guide & rules
50
+ │ ├── java.md # Java style guide (placeholder)
51
+ │ └── xml.md # XML style guide (placeholder)
52
+ ├── Dockerfile # Containerization
53
+ ├── README.md # This instruction manual
54
+ ├── requirements.txt # Python dependencies (PyYAML)
55
+ ├── pre-commit-hook.sh # Shell hook executing linter via Docker
56
+ ├── mir/
57
+ │ ├── main.py # Core CLI Entrypoint
58
+ │ ├── engine/ # Core engine mechanics
59
+ │ │ ├── config.py # Precedence-aware configuration loader
60
+ │ │ ├── disabler.py # Parsing and handling disable comments
61
+ │ │ ├── rule_interface.py # BaseRule & Violation class structures
62
+ │ │ ├── rules_loader.py # Dynamic imports of language rules
63
+ │ │ └── runner.py # Processing workflow (Check, Fix, Dry-Run)
64
+ │ └── rules/ # Language specific rules
65
+ │ └── sql/
66
+ │ ├── line_length.py # Checks lines exceeding maximum characters
67
+ │ └── keyword_case.py # Enforces and fixes uppercase SQL keywords
68
+ └── tests/ # Suite of unit & integration tests
69
+ ├── test_config.py # Tests config parser
70
+ ├── test_disabler.py # Tests comment disabler logic
71
+ ├── test_runner.py # Tests CLI runner integration
72
+ └── rules/ # Rule specific tests
73
+ └── sql/
74
+ ├── test_line_length.py
75
+ └── test_keyword_case.py
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Setup & Local Usage
81
+
82
+ Ensure you have [uv](https://github.com/astral-sh/uv) or `python` installed.
83
+
84
+ ### 1. Running Tests
85
+ You can run the full test suite using `uv`:
86
+ ```bash
87
+ uv run --with pyyaml python -m unittest discover -s tests -p "test_*.py"
88
+ ```
89
+
90
+ ### 2. Style Guides & Documentation
91
+
92
+ - **Custom Rules Guide**: Refer to the [Custom Rules Guide](docs/custom_rules.md) for details on writing your own custom linting rules, parameter interfaces, and execution validation.
93
+ - **Language Style Guides**: The other language-specific files in the `docs/` folder (such as `docs/sql.md`, `docs/java.md`, and `docs/xml.md`) are **automatically generated by the package** from code metadata. To compile and update these guides in sync with rule source definitions, run:
94
+
95
+ ```bash
96
+ # Generate style guides directly inside the 'docs/' directory
97
+ uv run mir/main.py --help docs
98
+
99
+ # Generate style guides inside a custom directory name
100
+ uv run mir/main.py --help docs:my_rules_docs
101
+ ```
102
+ This scans all rule modules under `mir/rules/` and compiles the style guide markdown files directly.
103
+
104
+ ### 2. Linting execution
105
+ ```bash
106
+ # Check files (default path: current directory)
107
+ uv run mir/main.py
108
+
109
+ # Verbose check on a specific file path
110
+ uv run mir/main.py -v path/to/file.sql
111
+
112
+ # Safe auto-fixing rules on a directory (rewrites matching files)
113
+ uv run mir/main.py --fix path/to/file.sql
114
+
115
+ # Show dry-run diffs
116
+ uv run mir/main.py --dry-run path/to/file.sql
117
+ ```
118
+
119
+ ### 3. Rules CLI Documentation & Help
120
+ You can query and inspect the list of rules and details directly from the command line:
121
+ ```bash
122
+ # List all rules across all languages
123
+ uv run mir/main.py --help rules
124
+
125
+ # List only SQL rules
126
+ uv run mir/main.py --help rules:sql
127
+
128
+ # Show detailed documentation & examples for a specific rule across all languages
129
+ uv run mir/main.py --help IR-line-length
130
+
131
+ # Show detailed documentation for a specific rule inside a specific language
132
+ uv run mir/main.py --help sql:IR-keyword-case
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Docker Usage
138
+
139
+ Build and execute the linter entirely inside a containerized sandbox.
140
+
141
+ ### 1. Build the Docker Image
142
+ ```bash
143
+ docker build -t mitchells-ineffable-rules:latest .
144
+ ```
145
+
146
+ ### 2. Lint Files via Docker
147
+ Mount your workspace volume into `/workspace` in the container:
148
+ ```bash
149
+ # Basic check on the current directory
150
+ docker run --rm -v "$(pwd):/workspace" mitchells-ineffable-rules:latest .
151
+
152
+ # Run auto-fixes
153
+ docker run --rm -v "$(pwd):/workspace" mitchells-ineffable-rules:latest --fix .
154
+
155
+ # Verbose dry-run on a file
156
+ docker run --rm -v "$(pwd):/workspace" mitchells-ineffable-rules:latest --dry-run -v query.sql
157
+ ```
158
+
159
+ ---
160
+
161
+ ## Git Pre-commit Hook Integration
162
+
163
+ Integrate the linter to block bad code from being committed:
164
+
165
+ 1. Copy the hook script into your repository's hook directory:
166
+ ```bash
167
+ cp pre-commit-hook.sh .git/hooks/pre-commit
168
+ ```
169
+ 2. Make sure it is executable:
170
+ ```bash
171
+ chmod +x .git/hooks/pre-commit
172
+ ```
173
+
174
+ When committing files, the hook automatically detects staged SQL, Java, and XML changes, running them through the Docker container before allowing the commit to succeed.
175
+
176
+ ---
177
+
178
+ ## Configuration
179
+
180
+ You can create an `.ir-config.yaml` file in the root of your project:
181
+
182
+ ```yaml
183
+ # Rules to globally disable
184
+ disable:
185
+ - IR-some-disabled-rule
186
+
187
+ # Global flags
188
+ verbose: true
189
+ fix: false
190
+
191
+ # External rule directories (to extend or replace built-in rulesets)
192
+ include_dirs:
193
+ - /path/to/my/custom_rules
194
+
195
+ # Rule mode for common languages: "extend" (default, built-in + custom) or "replace" (custom only)
196
+ rule_mode: extend
197
+
198
+ # Individual rule configuration
199
+ rules:
200
+ IR-line-length:
201
+ max_length: 120
202
+ ```
203
+
204
+ ### Rule Configuration Fallbacks
205
+
206
+ To keep rules independent and decoupled, certain rules can dynamically inherit configuration parameters from other rules if they are not explicitly set.
207
+
208
+ For example, the SQL Column Layout formatter (`IR-column-layout`) dynamically falls back to query its parameters from the canonical rule configurations:
209
+ - `max_length` is inherited from the Line Length rule (`IR-line-length`)
210
+ - `indent_size` is inherited from the Indentation rule (`IR-indent`)
211
+
212
+ You can override these parameters specifically for the dependent rule or set them once on the canonical rule:
213
+ ```yaml
214
+ rules:
215
+ # Sets line length limit to 80 (affects both IR-line-length and IR-column-layout fallback)
216
+ IR-line-length:
217
+ max_length: 80
218
+
219
+ # Specifically overrides column layout to use 100 instead
220
+ IR-column-layout:
221
+ max_length: 100
222
+ ```
223
+
224
+ ### Order of Precedence
225
+
226
+ Configuration parameters are merged with the following priority hierarchy (highest overrides lowest):
227
+ 1. **Command Line Arguments** (e.g. `--fix`, `--disable IR-line-length`, `--include-dir /path`, `--rule-mode replace`)
228
+ 2. **Environment Variables** (e.g. `IR_FIX=true`, `IR_DISABLE=IR-line-length`, `IR_INCLUDE_DIRS=/path`, `IR_RULE_MODE=replace`)
229
+ 3. **YAML Config File** (e.g. `disable: [IR-line-length]`, `include_dirs: [/path]`, `rule_mode: replace` inside `.ir-config.yaml`)
230
+
231
+ ---
232
+
233
+ ## Writing Custom Rules
234
+
235
+ You can write custom rules to run against your codebase, either placing them inside the built-in rules directory or in an external custom rules directory. For full details on class specifications, default parameters, and strict execution validation, refer to the [Custom Rules Guide](docs/custom_rules.md).
236
+
237
+ ### 1. Built-in Directory
238
+ Create your rule Python module under `mir/rules/<language>/my_rule_name.py`.
239
+
240
+ ### 2. External Directory
241
+ 1. Create a directory structure, e.g. `/my-custom-rules/<language>/my_rule_name.py`.
242
+ 2. Provide the path to the linter via CLI (`--include-dir /my-custom-rules`), Env (`IR_INCLUDE_DIRS`), or config (`include_dirs: ["/my-custom-rules"]`).
243
+ 3. Set the `--rule-mode` to specify whether custom rules should **extend** (default) or **replace** the built-in rule sets.
244
+
245
+ ### Custom Rule Implementation Example
246
+ Each custom rule must subclass `BaseRule` (from `mir.engine.rule_interface`):
247
+
248
+ ```python
249
+ from typing import List, Dict, Any
250
+ from mir.engine.rule_interface import BaseRule, Violation
251
+
252
+ class MyXmlRule(BaseRule):
253
+ rule_id = "IR-xml-rule"
254
+ description = "A custom description of my rule."
255
+ category = "general"
256
+ is_fixable = "yes" # "yes", "no", or "sometimes"
257
+
258
+ def check(self, content: str, file_path: str, rule_config: Dict[str, Any]) -> List[Violation]:
259
+ violations = []
260
+ # Implement check logic...
261
+ # If a violation is found, append Violation(...) to list.
262
+ return violations
263
+
264
+ def fix(self, content: str, file_path: str, rule_config: Dict[str, Any]) -> str:
265
+ # If is_fixable is "yes" or "sometimes", return the modified content string
266
+ return content
267
+ ```
268
+
269
+ ---
270
+
271
+ ## Publishing to PyPI
272
+
273
+ For instructions on building distribution archives, TestPyPI validation, and publishing to the live Python Package Index, refer to the [PyPI Publishing Guide](docs/publishing.md).
274
+
275
+ ---
276
+
277
+ *This entire package was built using **Antigravity**, a powerful agentic AI coding assistant designed by the Google DeepMind team.*
278
+
279
+ *Total Cumulative Development Time: ~4.1 hours*
@@ -0,0 +1,265 @@
1
+ # Mitchell's Ineffable Rules (IR) Linter
2
+
3
+ A highly modular, extensible linting and auto-fixing utility for **SQL, Java, and XML** codebase assets. The linter can be run locally via `uv` / `python`, inside a containerized **Docker** execution loop, or integrated into your lifecycle as a **Git Pre-commit Hook**.
4
+
5
+ ---
6
+
7
+ ## Features
8
+
9
+ - **Extensible Rules Architecture**: Add language rules simply by adding Python rule modules under `mir/rules/<language>/`.
10
+ - **User Styling Guides**: Detailed style guides explaining each rule, its configuration parameters, auto-fixability, and clean vs. violating examples under `docs/`.
11
+ - **Precedence-Aware Configuration**: Merges CLI flags, environment variables, and YAML config file settings:
12
+ $$\text{CLI arguments} > \text{Environment variables} > \text{Config file} > \text{Defaults}$$
13
+ - **Granular Disable Comments**:
14
+ - **Single-line disable** (disables a rule for the next non-comment code line):
15
+ - SQL: `-- IR-rule-name`
16
+ - Java: `// IR-rule-name`
17
+ - XML: `<!-- IR-rule-name -->`
18
+ - **Block disable** (disables a rule for a range of lines):
19
+ - SQL: `-- IR-start-rule-name` ... `-- IR-end-rule-name`
20
+ - Java: `// IR-start-rule-name` ... `// IR-end-rule-name`
21
+ - XML: `<!-- IR-start-rule-name -->` ... `<!-- IR-end-rule-name -->`
22
+ - **Execution Modes**:
23
+ - `check`: Validates files and reports all errors (default).
24
+ - `fix`: Applies automatic corrections to fixable rules directly to files.
25
+ - `dry-run`: Runs checks and generates unified diffs showing proposed fixes without modifying the source.
26
+ - `verbose`: Prints rule descriptions and the offending lines along with errors.
27
+
28
+ ---
29
+
30
+ ## Directory Structure
31
+
32
+ ```text
33
+ .
34
+ ├── docs/ # Language-specific Style Guides
35
+ │ ├── sql.md # SQL style guide & rules
36
+ │ ├── java.md # Java style guide (placeholder)
37
+ │ └── xml.md # XML style guide (placeholder)
38
+ ├── Dockerfile # Containerization
39
+ ├── README.md # This instruction manual
40
+ ├── requirements.txt # Python dependencies (PyYAML)
41
+ ├── pre-commit-hook.sh # Shell hook executing linter via Docker
42
+ ├── mir/
43
+ │ ├── main.py # Core CLI Entrypoint
44
+ │ ├── engine/ # Core engine mechanics
45
+ │ │ ├── config.py # Precedence-aware configuration loader
46
+ │ │ ├── disabler.py # Parsing and handling disable comments
47
+ │ │ ├── rule_interface.py # BaseRule & Violation class structures
48
+ │ │ ├── rules_loader.py # Dynamic imports of language rules
49
+ │ │ └── runner.py # Processing workflow (Check, Fix, Dry-Run)
50
+ │ └── rules/ # Language specific rules
51
+ │ └── sql/
52
+ │ ├── line_length.py # Checks lines exceeding maximum characters
53
+ │ └── keyword_case.py # Enforces and fixes uppercase SQL keywords
54
+ └── tests/ # Suite of unit & integration tests
55
+ ├── test_config.py # Tests config parser
56
+ ├── test_disabler.py # Tests comment disabler logic
57
+ ├── test_runner.py # Tests CLI runner integration
58
+ └── rules/ # Rule specific tests
59
+ └── sql/
60
+ ├── test_line_length.py
61
+ └── test_keyword_case.py
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Setup & Local Usage
67
+
68
+ Ensure you have [uv](https://github.com/astral-sh/uv) or `python` installed.
69
+
70
+ ### 1. Running Tests
71
+ You can run the full test suite using `uv`:
72
+ ```bash
73
+ uv run --with pyyaml python -m unittest discover -s tests -p "test_*.py"
74
+ ```
75
+
76
+ ### 2. Style Guides & Documentation
77
+
78
+ - **Custom Rules Guide**: Refer to the [Custom Rules Guide](docs/custom_rules.md) for details on writing your own custom linting rules, parameter interfaces, and execution validation.
79
+ - **Language Style Guides**: The other language-specific files in the `docs/` folder (such as `docs/sql.md`, `docs/java.md`, and `docs/xml.md`) are **automatically generated by the package** from code metadata. To compile and update these guides in sync with rule source definitions, run:
80
+
81
+ ```bash
82
+ # Generate style guides directly inside the 'docs/' directory
83
+ uv run mir/main.py --help docs
84
+
85
+ # Generate style guides inside a custom directory name
86
+ uv run mir/main.py --help docs:my_rules_docs
87
+ ```
88
+ This scans all rule modules under `mir/rules/` and compiles the style guide markdown files directly.
89
+
90
+ ### 2. Linting execution
91
+ ```bash
92
+ # Check files (default path: current directory)
93
+ uv run mir/main.py
94
+
95
+ # Verbose check on a specific file path
96
+ uv run mir/main.py -v path/to/file.sql
97
+
98
+ # Safe auto-fixing rules on a directory (rewrites matching files)
99
+ uv run mir/main.py --fix path/to/file.sql
100
+
101
+ # Show dry-run diffs
102
+ uv run mir/main.py --dry-run path/to/file.sql
103
+ ```
104
+
105
+ ### 3. Rules CLI Documentation & Help
106
+ You can query and inspect the list of rules and details directly from the command line:
107
+ ```bash
108
+ # List all rules across all languages
109
+ uv run mir/main.py --help rules
110
+
111
+ # List only SQL rules
112
+ uv run mir/main.py --help rules:sql
113
+
114
+ # Show detailed documentation & examples for a specific rule across all languages
115
+ uv run mir/main.py --help IR-line-length
116
+
117
+ # Show detailed documentation for a specific rule inside a specific language
118
+ uv run mir/main.py --help sql:IR-keyword-case
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Docker Usage
124
+
125
+ Build and execute the linter entirely inside a containerized sandbox.
126
+
127
+ ### 1. Build the Docker Image
128
+ ```bash
129
+ docker build -t mitchells-ineffable-rules:latest .
130
+ ```
131
+
132
+ ### 2. Lint Files via Docker
133
+ Mount your workspace volume into `/workspace` in the container:
134
+ ```bash
135
+ # Basic check on the current directory
136
+ docker run --rm -v "$(pwd):/workspace" mitchells-ineffable-rules:latest .
137
+
138
+ # Run auto-fixes
139
+ docker run --rm -v "$(pwd):/workspace" mitchells-ineffable-rules:latest --fix .
140
+
141
+ # Verbose dry-run on a file
142
+ docker run --rm -v "$(pwd):/workspace" mitchells-ineffable-rules:latest --dry-run -v query.sql
143
+ ```
144
+
145
+ ---
146
+
147
+ ## Git Pre-commit Hook Integration
148
+
149
+ Integrate the linter to block bad code from being committed:
150
+
151
+ 1. Copy the hook script into your repository's hook directory:
152
+ ```bash
153
+ cp pre-commit-hook.sh .git/hooks/pre-commit
154
+ ```
155
+ 2. Make sure it is executable:
156
+ ```bash
157
+ chmod +x .git/hooks/pre-commit
158
+ ```
159
+
160
+ When committing files, the hook automatically detects staged SQL, Java, and XML changes, running them through the Docker container before allowing the commit to succeed.
161
+
162
+ ---
163
+
164
+ ## Configuration
165
+
166
+ You can create an `.ir-config.yaml` file in the root of your project:
167
+
168
+ ```yaml
169
+ # Rules to globally disable
170
+ disable:
171
+ - IR-some-disabled-rule
172
+
173
+ # Global flags
174
+ verbose: true
175
+ fix: false
176
+
177
+ # External rule directories (to extend or replace built-in rulesets)
178
+ include_dirs:
179
+ - /path/to/my/custom_rules
180
+
181
+ # Rule mode for common languages: "extend" (default, built-in + custom) or "replace" (custom only)
182
+ rule_mode: extend
183
+
184
+ # Individual rule configuration
185
+ rules:
186
+ IR-line-length:
187
+ max_length: 120
188
+ ```
189
+
190
+ ### Rule Configuration Fallbacks
191
+
192
+ To keep rules independent and decoupled, certain rules can dynamically inherit configuration parameters from other rules if they are not explicitly set.
193
+
194
+ For example, the SQL Column Layout formatter (`IR-column-layout`) dynamically falls back to query its parameters from the canonical rule configurations:
195
+ - `max_length` is inherited from the Line Length rule (`IR-line-length`)
196
+ - `indent_size` is inherited from the Indentation rule (`IR-indent`)
197
+
198
+ You can override these parameters specifically for the dependent rule or set them once on the canonical rule:
199
+ ```yaml
200
+ rules:
201
+ # Sets line length limit to 80 (affects both IR-line-length and IR-column-layout fallback)
202
+ IR-line-length:
203
+ max_length: 80
204
+
205
+ # Specifically overrides column layout to use 100 instead
206
+ IR-column-layout:
207
+ max_length: 100
208
+ ```
209
+
210
+ ### Order of Precedence
211
+
212
+ Configuration parameters are merged with the following priority hierarchy (highest overrides lowest):
213
+ 1. **Command Line Arguments** (e.g. `--fix`, `--disable IR-line-length`, `--include-dir /path`, `--rule-mode replace`)
214
+ 2. **Environment Variables** (e.g. `IR_FIX=true`, `IR_DISABLE=IR-line-length`, `IR_INCLUDE_DIRS=/path`, `IR_RULE_MODE=replace`)
215
+ 3. **YAML Config File** (e.g. `disable: [IR-line-length]`, `include_dirs: [/path]`, `rule_mode: replace` inside `.ir-config.yaml`)
216
+
217
+ ---
218
+
219
+ ## Writing Custom Rules
220
+
221
+ You can write custom rules to run against your codebase, either placing them inside the built-in rules directory or in an external custom rules directory. For full details on class specifications, default parameters, and strict execution validation, refer to the [Custom Rules Guide](docs/custom_rules.md).
222
+
223
+ ### 1. Built-in Directory
224
+ Create your rule Python module under `mir/rules/<language>/my_rule_name.py`.
225
+
226
+ ### 2. External Directory
227
+ 1. Create a directory structure, e.g. `/my-custom-rules/<language>/my_rule_name.py`.
228
+ 2. Provide the path to the linter via CLI (`--include-dir /my-custom-rules`), Env (`IR_INCLUDE_DIRS`), or config (`include_dirs: ["/my-custom-rules"]`).
229
+ 3. Set the `--rule-mode` to specify whether custom rules should **extend** (default) or **replace** the built-in rule sets.
230
+
231
+ ### Custom Rule Implementation Example
232
+ Each custom rule must subclass `BaseRule` (from `mir.engine.rule_interface`):
233
+
234
+ ```python
235
+ from typing import List, Dict, Any
236
+ from mir.engine.rule_interface import BaseRule, Violation
237
+
238
+ class MyXmlRule(BaseRule):
239
+ rule_id = "IR-xml-rule"
240
+ description = "A custom description of my rule."
241
+ category = "general"
242
+ is_fixable = "yes" # "yes", "no", or "sometimes"
243
+
244
+ def check(self, content: str, file_path: str, rule_config: Dict[str, Any]) -> List[Violation]:
245
+ violations = []
246
+ # Implement check logic...
247
+ # If a violation is found, append Violation(...) to list.
248
+ return violations
249
+
250
+ def fix(self, content: str, file_path: str, rule_config: Dict[str, Any]) -> str:
251
+ # If is_fixable is "yes" or "sometimes", return the modified content string
252
+ return content
253
+ ```
254
+
255
+ ---
256
+
257
+ ## Publishing to PyPI
258
+
259
+ For instructions on building distribution archives, TestPyPI validation, and publishing to the live Python Package Index, refer to the [PyPI Publishing Guide](docs/publishing.md).
260
+
261
+ ---
262
+
263
+ *This entire package was built using **Antigravity**, a powerful agentic AI coding assistant designed by the Google DeepMind team.*
264
+
265
+ *Total Cumulative Development Time: ~4.1 hours*