mkforge 0.2.0__tar.gz → 0.3.0__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 (107) hide show
  1. {mkforge-0.2.0 → mkforge-0.3.0}/PKG-INFO +22 -1
  2. {mkforge-0.2.0 → mkforge-0.3.0}/README.md +21 -0
  3. {mkforge-0.2.0 → mkforge-0.3.0}/docs/API_REFERENCE.md +176 -0
  4. {mkforge-0.2.0 → mkforge-0.3.0}/docs/USER_GUIDE.md +83 -0
  5. {mkforge-0.2.0 → mkforge-0.3.0}/pyproject.toml +1 -1
  6. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/__init__.py +9 -1
  7. mkforge-0.3.0/src/mkforge/heading_numbering.py +320 -0
  8. mkforge-0.3.0/tests/test_heading_numbering.py +263 -0
  9. {mkforge-0.2.0 → mkforge-0.3.0}/uv.lock +1 -1
  10. {mkforge-0.2.0 → mkforge-0.3.0}/.github/workflows/ci.yml +0 -0
  11. {mkforge-0.2.0 → mkforge-0.3.0}/.github/workflows/publish.yml +0 -0
  12. {mkforge-0.2.0 → mkforge-0.3.0}/.gitignore +0 -0
  13. {mkforge-0.2.0 → mkforge-0.3.0}/.gitlab-ci.yml +0 -0
  14. {mkforge-0.2.0 → mkforge-0.3.0}/.mkforge +0 -0
  15. {mkforge-0.2.0 → mkforge-0.3.0}/.python-version +0 -0
  16. {mkforge-0.2.0 → mkforge-0.3.0}/AGENTS.md +0 -0
  17. {mkforge-0.2.0 → mkforge-0.3.0}/CLAUDE.md +0 -0
  18. {mkforge-0.2.0 → mkforge-0.3.0}/LICENSE +0 -0
  19. {mkforge-0.2.0 → mkforge-0.3.0}/Makefile +0 -0
  20. {mkforge-0.2.0 → mkforge-0.3.0}/demo_report.py +0 -0
  21. {mkforge-0.2.0 → mkforge-0.3.0}/demo_validation.py +0 -0
  22. {mkforge-0.2.0 → mkforge-0.3.0}/demo_verif.py +0 -0
  23. {mkforge-0.2.0 → mkforge-0.3.0}/doc/adr-001-markdown-conformance-verifier.md +0 -0
  24. {mkforge-0.2.0 → mkforge-0.3.0}/docs/MARKDOWN_RULES.md +0 -0
  25. {mkforge-0.2.0 → mkforge-0.3.0}/docs/SDD.md +0 -0
  26. {mkforge-0.2.0 → mkforge-0.3.0}/docs/SRS.md +0 -0
  27. {mkforge-0.2.0 → mkforge-0.3.0}/docs/adr/ADR-001-asset-management.md +0 -0
  28. {mkforge-0.2.0 → mkforge-0.3.0}/docs/adr/ADR-002-markdown-validation-contracts.md +0 -0
  29. {mkforge-0.2.0 → mkforge-0.3.0}/docs/pypi-publication.md +0 -0
  30. {mkforge-0.2.0 → mkforge-0.3.0}/main.py +0 -0
  31. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/_metadata.py +0 -0
  32. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/assets.py +0 -0
  33. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/content/__init__.py +0 -0
  34. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/content/_base.py +0 -0
  35. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/content/code.py +0 -0
  36. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/content/image.py +0 -0
  37. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/content/lists.py +0 -0
  38. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/content/misc.py +0 -0
  39. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/content/paragraph.py +0 -0
  40. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/content/table.py +0 -0
  41. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/content/text.py +0 -0
  42. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/document.py +0 -0
  43. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/errors.py +0 -0
  44. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/input_checks.py +0 -0
  45. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/py.typed +0 -0
  46. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/rendering.py +0 -0
  47. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/slugify.py +0 -0
  48. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/validation/__init__.py +0 -0
  49. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/validation/markdown_contracts.py +0 -0
  50. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/__init__.py +0 -0
  51. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/api.py +0 -0
  52. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/diagnostic_pattern.py +0 -0
  53. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/policy.py +0 -0
  54. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/registry.py +0 -0
  55. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/__init__.py +0 -0
  56. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/gfm/__init__.py +0 -0
  57. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/gfm/gfm001_table_delimiter.py +0 -0
  58. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/gfm/gfm002_table_column_count.py +0 -0
  59. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/gfm/gfm003_task_list_marker.py +0 -0
  60. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/__init__.py +0 -0
  61. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/_shared.py +0 -0
  62. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/markdownlint_remaining.py +0 -0
  63. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md001_heading_increment.py +0 -0
  64. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md002_first_heading_level.py +0 -0
  65. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md003_heading_style.py +0 -0
  66. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md004_unordered_list_style.py +0 -0
  67. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md005_md006_md007_list_indentation.py +0 -0
  68. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md009_trailing_spaces.py +0 -0
  69. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md010_hard_tabs.py +0 -0
  70. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md011_reversed_link_syntax.py +0 -0
  71. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md012_multiple_blank_lines.py +0 -0
  72. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md013_line_length.py +0 -0
  73. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md014_command_prompt.py +0 -0
  74. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md018_atx_heading_space.py +0 -0
  75. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md019_md021_atx_closed_spaces.py +0 -0
  76. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md020_closed_atx_heading_space.py +0 -0
  77. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md022_md023_heading_blanks.py +0 -0
  78. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md024_md025_duplicate_headings.py +0 -0
  79. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md026_heading_punctuation.py +0 -0
  80. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md027_md028_blockquote.py +0 -0
  81. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md029_md030_list_prefix.py +0 -0
  82. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md031_md032_fence_list_blanks.py +0 -0
  83. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md033_inline_html.py +0 -0
  84. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md034_bare_url.py +0 -0
  85. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md035_horizontal_rule.py +0 -0
  86. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md036_emphasis_heading.py +0 -0
  87. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md037_emphasis_marker_space.py +0 -0
  88. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md038_code_span_space.py +0 -0
  89. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md039_link_text_space.py +0 -0
  90. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/md040_md041_md046_md047_blocks.py +0 -0
  91. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/rules/markdown/mkf001_local_resource_exists.py +0 -0
  92. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/settings.py +0 -0
  93. {mkforge-0.2.0 → mkforge-0.3.0}/src/mkforge/verification/source_scan.py +0 -0
  94. {mkforge-0.2.0 → mkforge-0.3.0}/tests/__init__.py +0 -0
  95. {mkforge-0.2.0 → mkforge-0.3.0}/tests/conftest.py +0 -0
  96. {mkforge-0.2.0 → mkforge-0.3.0}/tests/expectations.py +0 -0
  97. {mkforge-0.2.0 → mkforge-0.3.0}/tests/test_assets.py +0 -0
  98. {mkforge-0.2.0 → mkforge-0.3.0}/tests/test_e2e.py +0 -0
  99. {mkforge-0.2.0 → mkforge-0.3.0}/tests/test_e2e_verification.py +0 -0
  100. {mkforge-0.2.0 → mkforge-0.3.0}/tests/test_helpers.py +0 -0
  101. {mkforge-0.2.0 → mkforge-0.3.0}/tests/test_markdown_validation.py +0 -0
  102. {mkforge-0.2.0 → mkforge-0.3.0}/tests/test_markdown_verification.py +0 -0
  103. {mkforge-0.2.0 → mkforge-0.3.0}/tests/test_metadata.py +0 -0
  104. {mkforge-0.2.0 → mkforge-0.3.0}/tests/test_report_generation.py +0 -0
  105. {mkforge-0.2.0 → mkforge-0.3.0}/tests/test_slugify.py +0 -0
  106. {mkforge-0.2.0 → mkforge-0.3.0}/tests/test_validation.py +0 -0
  107. {mkforge-0.2.0 → mkforge-0.3.0}/work/.gitkeep +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkforge
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Programmatic Markdown report generation for Python.
5
5
  Project-URL: Homepage, https://github.com/antoinebarre/mkforge
6
6
  Project-URL: Repository, https://github.com/antoinebarre/mkforge
@@ -161,6 +161,27 @@ collapsed to a single hyphen, leading/trailing hyphens trimmed. Unicode
161
161
  letters are preserved (case-folded, not transliterated), which keeps slugs
162
162
  consistent with the anchors GitHub generates for the same heading.
163
163
 
164
+ ## Heading Numbering
165
+
166
+ ```python
167
+ from mkforge import renumber_markdown_headings, strip_markdown_heading_numbering
168
+
169
+ markdown = "# 9. Document\n## 4. Titre 1\n### 8. Tritre niveau 2\n"
170
+
171
+ strip_markdown_heading_numbering(markdown)
172
+ # "# Document\n## Titre 1\n### Tritre niveau 2\n"
173
+
174
+ renumber_markdown_headings(markdown, start_level=2)
175
+ # "# Document\n## 1. Titre 1\n### 1.1. Tritre niveau 2\n"
176
+ ```
177
+
178
+ `strip_markdown_heading_numbering` and `renumber_markdown_headings` are pure
179
+ Markdown helpers for doc-as-code pipelines. They modify only ATX headings
180
+ outside fenced code blocks, preserve heading levels, and can start numbering
181
+ at a chosen level with `start_level`. Use `first_number` to start a fragment
182
+ at a later number, and `separator` to control the text between number and
183
+ title.
184
+
164
185
  ## Relationship With Scribpy
165
186
 
166
187
  MkForge is intended to be independent from Scribpy.
@@ -146,6 +146,27 @@ collapsed to a single hyphen, leading/trailing hyphens trimmed. Unicode
146
146
  letters are preserved (case-folded, not transliterated), which keeps slugs
147
147
  consistent with the anchors GitHub generates for the same heading.
148
148
 
149
+ ## Heading Numbering
150
+
151
+ ```python
152
+ from mkforge import renumber_markdown_headings, strip_markdown_heading_numbering
153
+
154
+ markdown = "# 9. Document\n## 4. Titre 1\n### 8. Tritre niveau 2\n"
155
+
156
+ strip_markdown_heading_numbering(markdown)
157
+ # "# Document\n## Titre 1\n### Tritre niveau 2\n"
158
+
159
+ renumber_markdown_headings(markdown, start_level=2)
160
+ # "# Document\n## 1. Titre 1\n### 1.1. Tritre niveau 2\n"
161
+ ```
162
+
163
+ `strip_markdown_heading_numbering` and `renumber_markdown_headings` are pure
164
+ Markdown helpers for doc-as-code pipelines. They modify only ATX headings
165
+ outside fenced code blocks, preserve heading levels, and can start numbering
166
+ at a chosen level with `start_level`. Use `first_number` to start a fragment
167
+ at a later number, and `separator` to control the text between number and
168
+ title.
169
+
149
170
  ## Relationship With Scribpy
150
171
 
151
172
  MkForge is intended to be independent from Scribpy.
@@ -21,7 +21,10 @@ from mkforge import (
21
21
  Report,
22
22
  Section,
23
23
  Table,
24
+ renumber_markdown_headings,
24
25
  slugify_heading,
26
+ strip_heading_numbering_text,
27
+ strip_markdown_heading_numbering,
25
28
  validate_markdown_chapters,
26
29
  validate_markdown_headings,
27
30
  validate_markdown_images,
@@ -97,6 +100,9 @@ Module-level helpers are documented for advanced integrations and tests.
97
100
  | `validate_markdown_headings` | `mkforge` | validation | Check level/title heading contract |
98
101
  | `validate_markdown_images` | `mkforge` | validation/assets | Check Markdown image targets |
99
102
  | `slugify_heading` | `mkforge` | text | Convert a heading title to a GitHub-style anchor slug |
103
+ | `strip_heading_numbering_text` | `mkforge` | text | Remove a numeric prefix from heading text |
104
+ | `strip_markdown_heading_numbering` | `mkforge` | text | Remove numeric prefixes from ATX headings |
105
+ | `renumber_markdown_headings` | `mkforge` | text | Rebuild ATX heading numbering by hierarchy |
100
106
  | `InvalidChildError` | `mkforge` | errors | Unsupported report-tree child |
101
107
  | `InvalidTableError` | `mkforge` | errors | Invalid table shape |
102
108
  | `ReportDepthError` | `mkforge` | errors | Section nesting below H6 |
@@ -1108,6 +1114,176 @@ separator). Use `slugify_heading` for anchors shared with external tools;
1108
1114
 
1109
1115
  ---
1110
1116
 
1117
+ ## 6.8 `mkforge.heading_numbering` Helpers
1118
+
1119
+ These helpers normalize numeric prefixes on Markdown headings. They are pure,
1120
+ deterministic, perform no I/O, and are useful for doc-as-code assemblers that
1121
+ merge Markdown fragments before publishing.
1122
+
1123
+ All document-level helpers:
1124
+
1125
+ - modify only ATX headings (`#` through `######`);
1126
+ - ignore fenced code blocks;
1127
+ - preserve non-heading lines;
1128
+ - preserve heading levels;
1129
+ - strip old numeric prefixes before adding new ones.
1130
+
1131
+ ### 6.8.1 `strip_heading_numbering_text`
1132
+
1133
+ Module: `mkforge.heading_numbering`
1134
+
1135
+ Exported by: `mkforge`
1136
+
1137
+ Signature:
1138
+
1139
+ ```python
1140
+ strip_heading_numbering_text(text: str) -> str
1141
+ ```
1142
+
1143
+ Removes one leading numeric heading prefix from a heading title.
1144
+
1145
+ Examples:
1146
+
1147
+ ```python
1148
+ from mkforge import strip_heading_numbering_text
1149
+
1150
+ strip_heading_numbering_text("1. Introduction") # "Introduction"
1151
+ strip_heading_numbering_text("1.2 Architecture") # "Architecture"
1152
+ strip_heading_numbering_text("1.2.3 - Design") # "Design"
1153
+ strip_heading_numbering_text("2026 Roadmap") # "2026 Roadmap"
1154
+ strip_heading_numbering_text("Version 2.0") # "Version 2.0"
1155
+ ```
1156
+
1157
+ Raises:
1158
+
1159
+ | Condition | Exception |
1160
+ |---|---|
1161
+ | `text` is not a string | `TypeError` |
1162
+
1163
+ ### 6.8.2 `strip_markdown_heading_numbering`
1164
+
1165
+ Module: `mkforge.heading_numbering`
1166
+
1167
+ Exported by: `mkforge`
1168
+
1169
+ Signature:
1170
+
1171
+ ```python
1172
+ strip_markdown_heading_numbering(markdown: str) -> str
1173
+ ```
1174
+
1175
+ Removes numeric prefixes from ATX headings in a complete Markdown document.
1176
+
1177
+ Example:
1178
+
1179
+ ````python
1180
+ from mkforge import strip_markdown_heading_numbering
1181
+
1182
+ markdown = """# 1. Introduction
1183
+
1184
+ ## 1.3 Architecture
1185
+
1186
+ ```python
1187
+ # 1. This must not change
1188
+ ```
1189
+ """
1190
+
1191
+ strip_markdown_heading_numbering(markdown)
1192
+ ````
1193
+
1194
+ Result:
1195
+
1196
+ ````markdown
1197
+ # Introduction
1198
+
1199
+ ## Architecture
1200
+
1201
+ ```python
1202
+ # 1. This must not change
1203
+ ```
1204
+ ````
1205
+
1206
+ Raises:
1207
+
1208
+ | Condition | Exception |
1209
+ |---|---|
1210
+ | `markdown` is not a string | `TypeError` |
1211
+
1212
+ ### 6.8.3 `renumber_markdown_headings`
1213
+
1214
+ Module: `mkforge.heading_numbering`
1215
+
1216
+ Exported by: `mkforge`
1217
+
1218
+ Signature:
1219
+
1220
+ ```python
1221
+ renumber_markdown_headings(
1222
+ markdown: str,
1223
+ *,
1224
+ separator: str = ". ",
1225
+ first_number: int = 1,
1226
+ start_level: int = 1,
1227
+ ) -> str
1228
+ ```
1229
+
1230
+ Removes old numeric prefixes from ATX headings, then generates coherent
1231
+ hierarchical numbering.
1232
+
1233
+ Parameters:
1234
+
1235
+ | Parameter | Type | Default | Description |
1236
+ |---|---|---|---|
1237
+ | `markdown` | `str` | required | Markdown source text |
1238
+ | `separator` | `str` | `". "` | Text inserted between the number and title |
1239
+ | `first_number` | `int` | `1` | First number generated at the configured root level |
1240
+ | `start_level` | `int` | `1` | First heading level that receives generated numbering |
1241
+
1242
+ Examples:
1243
+
1244
+ ```python
1245
+ from mkforge import renumber_markdown_headings
1246
+
1247
+ renumber_markdown_headings("# 9. Intro\n## 4. Detail\n")
1248
+ # "# 1. Intro\n## 1.1. Detail\n"
1249
+
1250
+ renumber_markdown_headings("# Intro\n## Detail\n", first_number=4)
1251
+ # "# 4. Intro\n## 4.1. Detail\n"
1252
+
1253
+ renumber_markdown_headings("# Document\n## Titre\n### Détail\n", start_level=2)
1254
+ # "# Document\n## 1. Titre\n### 1.1. Détail\n"
1255
+
1256
+ renumber_markdown_headings("## Titre\n### Détail\n", first_number=4, start_level=2)
1257
+ # "## 4. Titre\n### 4.1. Détail\n"
1258
+ ```
1259
+
1260
+ The `separator` controls only the text between the generated number and the
1261
+ title:
1262
+
1263
+ | `separator` | Heading output |
1264
+ |---|---|
1265
+ | `". "` | `# 1. Title` |
1266
+ | `" "` | `# 1 Title` |
1267
+ | `" - "` | `# 1 - Title` |
1268
+ | `""` | `# 1Title` |
1269
+
1270
+ When `start_level=2`, H1 headings are cleaned but not numbered. H2 headings
1271
+ become numeric roots (`1`, `2`, ...), and H3 headings become children
1272
+ (`1.1`, `1.2`, ...).
1273
+
1274
+ Raises:
1275
+
1276
+ | Condition | Exception |
1277
+ |---|---|
1278
+ | `markdown` is not a string | `TypeError` |
1279
+ | `separator` is not a string | `TypeError` |
1280
+ | `first_number` is not an integer | `TypeError` |
1281
+ | `start_level` is not an integer | `TypeError` |
1282
+ | `first_number` is less than 1 | `ValueError` |
1283
+ | `start_level` is outside 1-6 | `ValueError` |
1284
+
1285
+ ---
1286
+
1111
1287
  ## 7. Metadata Reference
1112
1288
 
1113
1289
  Metadata is a free-form dictionary. MkForge does not define allowed keys.
@@ -29,6 +29,7 @@ For exhaustive class, function, helper, rule, and feature contracts, see
29
29
  - [Verification](#verification)
30
30
  - [Validation](#validation)
31
31
  - [Heading Slugification](#heading-slugification)
32
+ - [Heading Numbering](#heading-numbering)
32
33
  - [Assets](#assets)
33
34
  - [Errors](#errors)
34
35
  - [Patterns And Recommendations](#patterns-and-recommendations)
@@ -91,6 +92,16 @@ For heading anchor slugs:
91
92
  from mkforge import slugify_heading
92
93
  ```
93
94
 
95
+ For cleaning and rebuilding Markdown heading numbering:
96
+
97
+ ```python
98
+ from mkforge import (
99
+ renumber_markdown_headings,
100
+ strip_heading_numbering_text,
101
+ strip_markdown_heading_numbering,
102
+ )
103
+ ```
104
+
94
105
  ## How To Think About MkForge
95
106
 
96
107
  MkForge has three jobs:
@@ -1009,6 +1020,78 @@ Behavior:
1009
1020
  This matches the anchors GitHub generates for the same heading text, so a
1010
1021
  link such as `[Analyse des Risques](#analyse-des-risques)` stays valid.
1011
1022
 
1023
+ ## Heading Numbering
1024
+
1025
+ Use the heading numbering helpers when a documentation pipeline assembles
1026
+ Markdown fragments and must clean or rebuild heading numbers before publishing.
1027
+
1028
+ ````python
1029
+ from mkforge import (
1030
+ renumber_markdown_headings,
1031
+ strip_markdown_heading_numbering,
1032
+ )
1033
+
1034
+ markdown = """# 9. Document
1035
+ ## 4. Titre 1
1036
+ ### 8. Tritre niveau 2
1037
+
1038
+ ```markdown
1039
+ ## 99. This must not change
1040
+ ```
1041
+ """
1042
+
1043
+ strip_markdown_heading_numbering(markdown)
1044
+ renumber_markdown_headings(markdown, start_level=2)
1045
+ ````
1046
+
1047
+ With `start_level=2`, H1 headings are cleaned but not numbered, H2 headings
1048
+ become numeric roots, and H3 headings become their children:
1049
+
1050
+ ````markdown
1051
+ # Document
1052
+ ## 1. Titre 1
1053
+ ### 1.1. Tritre niveau 2
1054
+
1055
+ ```markdown
1056
+ ## 99. This must not change
1057
+ ```
1058
+ ````
1059
+
1060
+ The document-level helpers:
1061
+
1062
+ - modify only ATX headings (`#` through `######`);
1063
+ - ignore fenced code blocks;
1064
+ - preserve non-heading lines;
1065
+ - preserve heading levels;
1066
+ - strip old numbering before adding new numbering.
1067
+
1068
+ Use `first_number` when the fragment belongs later in a larger document:
1069
+
1070
+ ```python
1071
+ renumber_markdown_headings(markdown, first_number=4, start_level=2)
1072
+ ```
1073
+
1074
+ That starts numbering at the configured root level:
1075
+
1076
+ ```markdown
1077
+ ## 4. Titre 1
1078
+ ### 4.1. Tritre niveau 2
1079
+ ```
1080
+
1081
+ Use `separator` to control the text between the generated number and the
1082
+ title:
1083
+
1084
+ ```python
1085
+ renumber_markdown_headings(markdown, separator=" - ")
1086
+ ```
1087
+
1088
+ Examples:
1089
+
1090
+ - `separator=". "` renders `# 1. Title`;
1091
+ - `separator=" "` renders `# 1 Title`;
1092
+ - `separator=" - "` renders `# 1 - Title`;
1093
+ - `separator=""` renders `# 1Title`.
1094
+
1012
1095
  ## Assets
1013
1096
 
1014
1097
  MkForge treats image references carefully:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "mkforge"
7
- version = "0.2.0"
7
+ version = "0.3.0"
8
8
  description = "Programmatic Markdown report generation for Python."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -22,6 +22,11 @@ from mkforge.errors import (
22
22
  MissingAssetError,
23
23
  ReportDepthError,
24
24
  )
25
+ from mkforge.heading_numbering import (
26
+ renumber_markdown_headings,
27
+ strip_heading_numbering_text,
28
+ strip_markdown_heading_numbering,
29
+ )
25
30
  from mkforge.slugify import slugify_heading
26
31
  from mkforge.validation import (
27
32
  validate_markdown_chapters,
@@ -68,7 +73,10 @@ __all__ = [
68
73
  "Text",
69
74
  "VerificationReport",
70
75
  "VerificationSettings",
76
+ "renumber_markdown_headings",
71
77
  "slugify_heading",
78
+ "strip_heading_numbering_text",
79
+ "strip_markdown_heading_numbering",
72
80
  "validate_markdown_chapters",
73
81
  "validate_markdown_headings",
74
82
  "validate_markdown_images",
@@ -77,4 +85,4 @@ __all__ = [
77
85
  "verify_markdown_file",
78
86
  ]
79
87
 
80
- __version__ = "0.2.0"
88
+ __version__ = "0.3.0"