mkdocs2confluence 0.13.6__tar.gz → 0.13.8__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 (101) hide show
  1. {mkdocs2confluence-0.13.6/src/mkdocs2confluence.egg-info → mkdocs2confluence-0.13.8}/PKG-INFO +1 -1
  2. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/pyproject.toml +1 -1
  3. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8/src/mkdocs2confluence.egg-info}/PKG-INFO +1 -1
  4. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/skills/mkdocs-changelog/SKILL.md +54 -1
  5. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/skills/mkdocs-changelog/scripts/changelog_data.py +68 -12
  6. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/LICENSE +0 -0
  7. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/README.md +0 -0
  8. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/setup.cfg +0 -0
  9. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs2confluence.egg-info/SOURCES.txt +0 -0
  10. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
  11. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
  12. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs2confluence.egg-info/requires.txt +0 -0
  13. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
  14. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/__init__.py +0 -0
  15. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/cli.py +0 -0
  16. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/compiler/__init__.py +0 -0
  17. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/compiler/models.py +0 -0
  18. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/compiler/page.py +0 -0
  19. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
  20. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/emitter/xhtml.py +0 -0
  21. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/ir/__init__.py +0 -0
  22. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/ir/document.py +0 -0
  23. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/ir/nodes.py +0 -0
  24. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
  25. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
  26. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/loader/config.py +0 -0
  27. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
  28. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/loader/nav.py +0 -0
  29. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/loader/page.py +0 -0
  30. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
  31. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/parser/markdown.py +0 -0
  32. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/pdf/__init__.py +0 -0
  33. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/pdf/generator.py +0 -0
  34. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/pdf/render.py +0 -0
  35. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
  36. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
  37. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
  38. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/preprocess/frontmatter.py +0 -0
  39. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/preprocess/icons.py +0 -0
  40. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
  41. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
  42. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
  43. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/preview/render.py +0 -0
  44. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/preview/server.py +0 -0
  45. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
  46. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/publisher/changelog.py +0 -0
  47. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/publisher/client.py +0 -0
  48. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/publisher/executor.py +0 -0
  49. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/publisher/http_retry.py +0 -0
  50. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/publisher/models.py +0 -0
  51. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/publisher/pipeline.py +0 -0
  52. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/publisher/planner.py +0 -0
  53. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/skill_installer.py +0 -0
  54. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/sync/__init__.py +0 -0
  55. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/sync/anchoring.py +0 -0
  56. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/sync/command.py +0 -0
  57. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/sync/comments.py +0 -0
  58. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/sync/github.py +0 -0
  59. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/sync/platform.py +0 -0
  60. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/sync/state.py +0 -0
  61. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
  62. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/transforms/abbrevs.py +0 -0
  63. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/transforms/assets.py +0 -0
  64. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/transforms/editlink.py +0 -0
  65. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/transforms/footer.py +0 -0
  66. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/transforms/images.py +0 -0
  67. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
  68. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/src/mkdocs_to_confluence/transforms/mermaid.py +0 -0
  69. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_abbrevs.py +0 -0
  70. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_changelog_config.py +0 -0
  71. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_changelog_publish.py +0 -0
  72. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_children_macro.py +0 -0
  73. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_cli.py +0 -0
  74. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_editlink.py +0 -0
  75. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_emitter.py +0 -0
  76. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_extra_css.py +0 -0
  77. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_footer.py +0 -0
  78. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_frontmatter.py +0 -0
  79. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_icons.py +0 -0
  80. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_images.py +0 -0
  81. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_internallinks.py +0 -0
  82. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_ir.py +0 -0
  83. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_linkdefs.py +0 -0
  84. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_loader.py +0 -0
  85. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_mermaid.py +0 -0
  86. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_page_loader.py +0 -0
  87. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_parser.py +0 -0
  88. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_pdf.py +0 -0
  89. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_preprocess.py +0 -0
  90. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_preview.py +0 -0
  91. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_publish_client.py +0 -0
  92. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_publish_config.py +0 -0
  93. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_publish_pipeline.py +0 -0
  94. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_server.py +0 -0
  95. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_skill_installer.py +0 -0
  96. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_sync_anchoring.py +0 -0
  97. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_sync_command.py +0 -0
  98. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_sync_comments.py +0 -0
  99. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_sync_github.py +0 -0
  100. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_sync_state.py +0 -0
  101. {mkdocs2confluence-0.13.6 → mkdocs2confluence-0.13.8}/tests/test_treeutil.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs2confluence
3
- Version: 0.13.6
3
+ Version: 0.13.8
4
4
  Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
5
5
  Author: Anders Hybertz
6
6
  License: GPL-3.0-or-later
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mkdocs2confluence"
3
- version = "0.13.6"
3
+ version = "0.13.8"
4
4
  description = "Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more"
5
5
  readme = "README.md"
6
6
  license = { text = "GPL-3.0-or-later" }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs2confluence
3
- Version: 0.13.6
3
+ Version: 0.13.8
4
4
  Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
5
5
  Author: Anders Hybertz
6
6
  License: GPL-3.0-or-later
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: mkdocs-changelog
3
3
  description: Analyse doc changes since the last CHANGELOG.md update and draft a major-change entry if the changes qualify.
4
- version: "1.2.0"
4
+ version: "1.4.0"
5
5
  tags: [documentation, git, changelog, mkdocs, confluence]
6
6
  specificity: context-specific
7
7
  tool_agnostic: true
@@ -22,15 +22,24 @@ Analyse git changes to the docs directory since the last `CHANGELOG.md` commit.
22
22
 
23
23
  1. **Extract git data** — run the bundled data script to get structured, deterministic input:
24
24
 
25
+ Normal update (since last CHANGELOG.md commit):
25
26
  ```
26
27
  python .mk2conf/scripts/changelog_data.py --docs-dir <docs_dir>
27
28
  ```
28
29
 
30
+ Initial changelog (from a specific date):
31
+ ```
32
+ python .mk2conf/scripts/changelog_data.py --docs-dir <docs_dir> --since YYYY-MM-DD
33
+ ```
34
+
29
35
  The script prints a JSON object to stdout. Use this as your sole source of truth for
30
36
  commits, changed files, and contributors. Do not run git commands yourself.
31
37
 
32
38
  If the script is missing, tell the user to run `mk2conf install-skill` first.
33
39
 
40
+ When `--since` is used, the JSON will contain `"mode": "since_date"`. Use this to
41
+ trigger the initial changelog flow described below.
42
+
34
43
  2. **Read the existing changelog** — read `<docs_dir>/CHANGELOG.md` for context on what
35
44
  was previously recorded.
36
45
 
@@ -55,6 +64,30 @@ Analyse git changes to the docs directory since the last `CHANGELOG.md` commit.
55
64
  `CHANGELOG.md`. The previous latest entry (if any) must be converted from `???+` to `???`
56
65
  so only the new entry is expanded by default.
57
66
 
67
+ ## Linking to changed pages
68
+
69
+ The `changes` object in the script JSON now includes `{"path": "...", "title": "..."}` for
70
+ each file. Use this to link readers directly to the most relevant pages — but only where
71
+ it adds value and reads naturally.
72
+
73
+ Rules:
74
+
75
+ - **Select, don't list.** Pick at most two or three files per entry — the ones a reader
76
+ would actually want to visit. Omit files that are supporting material (images, assets,
77
+ index stubs, nav-only pages).
78
+ - **Weave into prose**, not as a separate list. Example:
79
+
80
+ Good: `Updated the [Configuration reference](configuration/index.md) to cover the new auth options.`
81
+ Bad: `Updated configuration/index.md. See also: getting-started.md, reference.md.`
82
+
83
+ - **Use `title` as link text** when it reads naturally. Fall back to a short descriptive
84
+ phrase if the title is too long or too generic (e.g. "Overview", "Index").
85
+ - **Paths are relative to `docs_dir`**, and `CHANGELOG.md` lives at the root of `docs_dir`.
86
+ A file at `docs/configuration/index.md` links as `configuration/index.md`.
87
+ - **Deleted pages** — do not link to them. Name them in prose without a link.
88
+ - **Skip linking entirely** if the change is a typo fix, formatting pass, or other
89
+ non-substantive update — linking draws attention and signals importance.
90
+
58
91
  ## Entry format
59
92
 
60
93
  The latest entry uses `???+` (expanded by default on Material, always visible on Confluence).
@@ -109,6 +142,26 @@ All notable changes to this documentation are recorded here.
109
142
  The format is inspired by [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
110
143
  ```
111
144
 
145
+ ## Initial changelog (--since DATE)
146
+
147
+ When the script is run with `--since` and the JSON contains `"mode": "since_date"`,
148
+ the user wants to bootstrap a CHANGELOG.md from scratch for a given time window.
149
+
150
+ Different rules apply:
151
+
152
+ - **Always draft** — the MAJOR threshold does not apply. This is a retrospective
153
+ summary, not an incremental gate.
154
+ - **Group by theme**, not by individual commit. Synthesise the changed files and
155
+ commit subjects into meaningful categories.
156
+ - **Date range in the title** — use the `since` value from the JSON as the start
157
+ and `date` as the end, e.g. `2026-05-01 → 2026-05-27`.
158
+ - **If CHANGELOG.md already exists**, stop and ask the user whether they want to
159
+ prepend, append, or replace. Do not overwrite silently.
160
+ - **If CHANGELOG.md does not exist**, create it with the standard header and the
161
+ entry as `???+`.
162
+
163
+ Example title: `2026-05-01 → 2026-05-27 — Initial documentation release`
164
+
112
165
  ## Pitfalls
113
166
 
114
167
  - **Do not draft an entry for every change.** The changelog is for readers who want to know
@@ -12,9 +12,11 @@ from __future__ import annotations
12
12
 
13
13
  import argparse
14
14
  import json
15
+ import re
15
16
  import subprocess
16
17
  import sys
17
18
  from datetime import date
19
+ from pathlib import Path
18
20
 
19
21
 
20
22
  def _run(args: list[str]) -> str:
@@ -37,6 +39,16 @@ def _baseline_commit(changelog_path: str) -> str | None:
37
39
  return sha or None
38
40
 
39
41
 
42
+ def _commit_before_date(since_date: str) -> str | None:
43
+ """Return the SHA of the last commit strictly before since_date (YYYY-MM-DD), or None."""
44
+ sha = _run([
45
+ "git", "log", "--format=%H",
46
+ f"--before={since_date}",
47
+ "-1",
48
+ ])
49
+ return sha or None
50
+
51
+
40
52
  def _root_commit() -> str:
41
53
  """Return the SHA of the very first commit in the repo."""
42
54
  return _run(["git", "rev-list", "--max-parents=0", "HEAD"])
@@ -59,8 +71,30 @@ def _commits_since(baseline: str) -> list[dict[str, str]]:
59
71
  return commits
60
72
 
61
73
 
62
- def _changed_files(baseline: str, docs_dir: str) -> dict[str, list[str]]:
63
- """Return files changed in docs_dir since baseline, grouped by status."""
74
+ def _extract_title(file_path: str) -> str | None:
75
+ """Return the first H1 heading from a markdown file, or None if unreadable."""
76
+ try:
77
+ text = Path(file_path).read_text(encoding="utf-8", errors="replace")
78
+ for line in text.splitlines():
79
+ m = re.match(r"^#\s+(.+)", line)
80
+ if m:
81
+ return m.group(1).strip()
82
+ except OSError:
83
+ pass
84
+ return None
85
+
86
+
87
+ def _enrich(paths: list[str]) -> list[dict[str, str | None]]:
88
+ """Attach page titles to file paths. Non-.md files get title=None."""
89
+ result = []
90
+ for p in paths:
91
+ title = _extract_title(p) if p.endswith(".md") else None
92
+ result.append({"path": p, "title": title})
93
+ return result
94
+
95
+
96
+ def _changed_files(baseline: str, docs_dir: str) -> dict[str, list[dict[str, str | None]]]:
97
+ """Return files changed in docs_dir since baseline, grouped by status, with titles."""
64
98
  raw = _run([
65
99
  "git", "diff", "--name-status", f"{baseline}..HEAD", "--", docs_dir,
66
100
  ])
@@ -68,7 +102,7 @@ def _changed_files(baseline: str, docs_dir: str) -> dict[str, list[str]]:
68
102
  modified: list[str] = []
69
103
  deleted: list[str] = []
70
104
  if not raw:
71
- return {"added": added, "modified": modified, "deleted": deleted}
105
+ return {"added": _enrich(added), "modified": _enrich(modified), "deleted": _enrich(deleted)}
72
106
  for line in raw.splitlines():
73
107
  parts = line.split("\t", maxsplit=1)
74
108
  if len(parts) != 2: # noqa: PLR2004
@@ -80,7 +114,7 @@ def _changed_files(baseline: str, docs_dir: str) -> dict[str, list[str]]:
80
114
  deleted.append(path)
81
115
  elif status.startswith("M") or status.startswith("R") or status.startswith("C"):
82
116
  modified.append(path)
83
- return {"added": added, "modified": modified, "deleted": deleted}
117
+ return {"added": _enrich(added), "modified": _enrich(modified), "deleted": _enrich(deleted)}
84
118
 
85
119
 
86
120
  def _contributors(commits: list[dict[str, str]]) -> list[str]:
@@ -102,19 +136,39 @@ def main() -> None:
102
136
  default="docs",
103
137
  help="Path to the MkDocs docs directory, relative to project root (default: docs)",
104
138
  )
139
+ parser.add_argument(
140
+ "--since",
141
+ metavar="DATE",
142
+ default=None,
143
+ help=(
144
+ "Use the last commit before DATE (YYYY-MM-DD) as the baseline instead of the "
145
+ "last commit that touched CHANGELOG.md. Useful for creating an initial changelog."
146
+ ),
147
+ )
105
148
  args = parser.parse_args()
106
149
 
107
150
  docs_dir: str = args.docs_dir
108
151
  changelog_rel = f"{docs_dir}/CHANGELOG.md"
109
152
 
110
- # Baseline: last commit touching CHANGELOG.md, or root commit if none
111
- baseline = _baseline_commit(changelog_rel) or _root_commit()
112
- if not baseline:
113
- print(
114
- "error: could not determine a baseline commit — is this a git repository?",
115
- file=sys.stderr,
116
- )
117
- sys.exit(1)
153
+ # Baseline resolution --since takes priority
154
+ if args.since:
155
+ baseline = _commit_before_date(args.since)
156
+ if not baseline:
157
+ print(
158
+ f"error: no commit found before {args.since}",
159
+ file=sys.stderr,
160
+ )
161
+ sys.exit(1)
162
+ mode = "since_date"
163
+ else:
164
+ baseline = _baseline_commit(changelog_rel) or _root_commit()
165
+ if not baseline:
166
+ print(
167
+ "error: could not determine a baseline commit — is this a git repository?",
168
+ file=sys.stderr,
169
+ )
170
+ sys.exit(1)
171
+ mode = "changelog_commit"
118
172
 
119
173
  commits = _commits_since(baseline)
120
174
  changes = _changed_files(baseline, docs_dir)
@@ -122,6 +176,8 @@ def main() -> None:
122
176
 
123
177
  output = {
124
178
  "date": date.today().isoformat(),
179
+ "mode": mode,
180
+ "since": args.since,
125
181
  "baseline_commit": baseline,
126
182
  "commits": commits,
127
183
  "contributors": contributors,