mkdocs2confluence 0.13.5__tar.gz → 0.13.7__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 (102) hide show
  1. {mkdocs2confluence-0.13.5/src/mkdocs2confluence.egg-info → mkdocs2confluence-0.13.7}/PKG-INFO +1 -1
  2. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/pyproject.toml +2 -2
  3. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7/src/mkdocs2confluence.egg-info}/PKG-INFO +1 -1
  4. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs2confluence.egg-info/SOURCES.txt +1 -0
  5. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/skill_installer.py +19 -0
  6. mkdocs2confluence-0.13.7/src/mkdocs_to_confluence/skills/mkdocs-changelog/SKILL.md +155 -0
  7. mkdocs2confluence-0.13.7/src/mkdocs_to_confluence/skills/mkdocs-changelog/scripts/changelog_data.py +168 -0
  8. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_skill_installer.py +11 -0
  9. mkdocs2confluence-0.13.5/src/mkdocs_to_confluence/skills/mkdocs-changelog/SKILL.md +0 -93
  10. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/LICENSE +0 -0
  11. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/README.md +0 -0
  12. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/setup.cfg +0 -0
  13. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
  14. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
  15. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs2confluence.egg-info/requires.txt +0 -0
  16. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
  17. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/__init__.py +0 -0
  18. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/cli.py +0 -0
  19. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/compiler/__init__.py +0 -0
  20. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/compiler/models.py +0 -0
  21. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/compiler/page.py +0 -0
  22. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
  23. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/emitter/xhtml.py +0 -0
  24. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/ir/__init__.py +0 -0
  25. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/ir/document.py +0 -0
  26. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/ir/nodes.py +0 -0
  27. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
  28. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
  29. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/loader/config.py +0 -0
  30. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
  31. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/loader/nav.py +0 -0
  32. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/loader/page.py +0 -0
  33. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
  34. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/parser/markdown.py +0 -0
  35. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/pdf/__init__.py +0 -0
  36. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/pdf/generator.py +0 -0
  37. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/pdf/render.py +0 -0
  38. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
  39. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
  40. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
  41. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/preprocess/frontmatter.py +0 -0
  42. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/preprocess/icons.py +0 -0
  43. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
  44. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
  45. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
  46. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/preview/render.py +0 -0
  47. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/preview/server.py +0 -0
  48. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
  49. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/publisher/changelog.py +0 -0
  50. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/publisher/client.py +0 -0
  51. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/publisher/executor.py +0 -0
  52. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/publisher/http_retry.py +0 -0
  53. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/publisher/models.py +0 -0
  54. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/publisher/pipeline.py +0 -0
  55. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/publisher/planner.py +0 -0
  56. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/sync/__init__.py +0 -0
  57. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/sync/anchoring.py +0 -0
  58. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/sync/command.py +0 -0
  59. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/sync/comments.py +0 -0
  60. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/sync/github.py +0 -0
  61. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/sync/platform.py +0 -0
  62. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/sync/state.py +0 -0
  63. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
  64. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/transforms/abbrevs.py +0 -0
  65. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/transforms/assets.py +0 -0
  66. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/transforms/editlink.py +0 -0
  67. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/transforms/footer.py +0 -0
  68. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/transforms/images.py +0 -0
  69. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
  70. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/src/mkdocs_to_confluence/transforms/mermaid.py +0 -0
  71. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_abbrevs.py +0 -0
  72. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_changelog_config.py +0 -0
  73. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_changelog_publish.py +0 -0
  74. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_children_macro.py +0 -0
  75. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_cli.py +0 -0
  76. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_editlink.py +0 -0
  77. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_emitter.py +0 -0
  78. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_extra_css.py +0 -0
  79. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_footer.py +0 -0
  80. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_frontmatter.py +0 -0
  81. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_icons.py +0 -0
  82. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_images.py +0 -0
  83. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_internallinks.py +0 -0
  84. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_ir.py +0 -0
  85. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_linkdefs.py +0 -0
  86. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_loader.py +0 -0
  87. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_mermaid.py +0 -0
  88. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_page_loader.py +0 -0
  89. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_parser.py +0 -0
  90. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_pdf.py +0 -0
  91. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_preprocess.py +0 -0
  92. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_preview.py +0 -0
  93. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_publish_client.py +0 -0
  94. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_publish_config.py +0 -0
  95. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_publish_pipeline.py +0 -0
  96. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_server.py +0 -0
  97. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_sync_anchoring.py +0 -0
  98. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_sync_command.py +0 -0
  99. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_sync_comments.py +0 -0
  100. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_sync_github.py +0 -0
  101. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_sync_state.py +0 -0
  102. {mkdocs2confluence-0.13.5 → mkdocs2confluence-0.13.7}/tests/test_treeutil.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs2confluence
3
- Version: 0.13.5
3
+ Version: 0.13.7
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.5"
3
+ version = "0.13.7"
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" }
@@ -68,7 +68,7 @@ build-backend = "setuptools.build_meta"
68
68
  where = ["src"]
69
69
 
70
70
  [tool.setuptools.package-data]
71
- "mkdocs_to_confluence" = ["skills/**/*.md"]
71
+ "mkdocs_to_confluence" = ["skills/**/*.md", "skills/**/*.py"]
72
72
 
73
73
  [tool.ruff]
74
74
  line-length = 120
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs2confluence
3
- Version: 0.13.5
3
+ Version: 0.13.7
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
@@ -48,6 +48,7 @@ src/mkdocs_to_confluence/publisher/models.py
48
48
  src/mkdocs_to_confluence/publisher/pipeline.py
49
49
  src/mkdocs_to_confluence/publisher/planner.py
50
50
  src/mkdocs_to_confluence/skills/mkdocs-changelog/SKILL.md
51
+ src/mkdocs_to_confluence/skills/mkdocs-changelog/scripts/changelog_data.py
51
52
  src/mkdocs_to_confluence/sync/__init__.py
52
53
  src/mkdocs_to_confluence/sync/anchoring.py
53
54
  src/mkdocs_to_confluence/sync/command.py
@@ -14,6 +14,21 @@ def _read_skill() -> str:
14
14
  return files("mkdocs_to_confluence").joinpath(f"skills/{_SKILL_NAME}/SKILL.md").read_text(encoding="utf-8")
15
15
 
16
16
 
17
+ def _read_script() -> str:
18
+ from importlib.resources import files
19
+ return files("mkdocs_to_confluence").joinpath(
20
+ f"skills/{_SKILL_NAME}/scripts/changelog_data.py"
21
+ ).read_text(encoding="utf-8")
22
+
23
+
24
+ def _install_script(project_dir: Path) -> Path:
25
+ """Write changelog_data.py to .mk2conf/scripts/ and return its path."""
26
+ dest = project_dir / ".mk2conf" / "scripts" / "changelog_data.py"
27
+ dest.parent.mkdir(parents=True, exist_ok=True)
28
+ dest.write_text(_read_script(), encoding="utf-8")
29
+ return dest
30
+
31
+
17
32
  def _strip_front_matter(content: str) -> str:
18
33
  return _FRONT_MATTER_RE.sub("", content).lstrip("\n")
19
34
 
@@ -38,6 +53,10 @@ def install_skill(
38
53
  content_full = _read_skill()
39
54
  content_body = _strip_front_matter(content_full)
40
55
 
56
+ # Always install the data script to a fixed project-local path so every
57
+ # AI tool's skill file can reference it unconditionally.
58
+ _install_script(project_dir)
59
+
41
60
  installed: list[tuple[str, Path]] = []
42
61
  explicit = tool is not None
43
62
 
@@ -0,0 +1,155 @@
1
+ ---
2
+ name: mkdocs-changelog
3
+ description: Analyse doc changes since the last CHANGELOG.md update and draft a major-change entry if the changes qualify.
4
+ version: "1.3.0"
5
+ tags: [documentation, git, changelog, mkdocs, confluence]
6
+ specificity: context-specific
7
+ tool_agnostic: true
8
+ authors: [Anders Hybertz]
9
+ tested_on: []
10
+ ---
11
+
12
+ # MkDocs Changelog Entry
13
+
14
+ Analyse git changes to the docs directory since the last `CHANGELOG.md` commit. If any changes qualify as **MAJOR**, draft a dated changelog entry in the collapsible format and prepend it to `CHANGELOG.md`. If not, explain why and exit without modifying any file.
15
+
16
+ ## When to Use
17
+
18
+ - After making one or more documentation changes and before running `mk2conf publish`
19
+ - Any point in the writing flow when you want to assess whether a "What's New" entry is warranted
20
+
21
+ ## Steps
22
+
23
+ 1. **Extract git data** — run the bundled data script to get structured, deterministic input:
24
+
25
+ Normal update (since last CHANGELOG.md commit):
26
+ ```
27
+ python .mk2conf/scripts/changelog_data.py --docs-dir <docs_dir>
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
+
35
+ The script prints a JSON object to stdout. Use this as your sole source of truth for
36
+ commits, changed files, and contributors. Do not run git commands yourself.
37
+
38
+ If the script is missing, tell the user to run `mk2conf install-skill` first.
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
+
43
+ 2. **Read the existing changelog** — read `<docs_dir>/CHANGELOG.md` for context on what
44
+ was previously recorded.
45
+
46
+ 3. **Decide: is this MAJOR?**
47
+
48
+ **MAJOR criteria — any one of these qualifies:**
49
+ - A new top-level documentation area or section added (a new folder or nav section that didn't exist before)
50
+ - A significant area deleted or substantially restructured (not just moved or renamed)
51
+ - A fundamental definition, concept, or policy changed in a way that affects how readers understand the subject
52
+
53
+ **NOT major — do not draft an entry for:**
54
+ - Typo fixes, grammar corrections, spelling
55
+ - Formatting, diagram adjustments, image swaps
56
+ - Small additions (a paragraph, a note, a clarification) that do not change the substance
57
+ - Rewordings that preserve the original meaning
58
+ - Internal restructuring with no reader-facing impact
59
+
60
+ 4. **If NOT MAJOR** — report what was found, explain in one sentence why it did not qualify,
61
+ and stop. Do not modify any file.
62
+
63
+ 5. **If MAJOR** — draft an entry using the collapsible format below and prepend it to
64
+ `CHANGELOG.md`. The previous latest entry (if any) must be converted from `???+` to `???`
65
+ so only the new entry is expanded by default.
66
+
67
+ ## Entry format
68
+
69
+ The latest entry uses `???+` (expanded by default on Material, always visible on Confluence).
70
+ All older entries use `???` (collapsed, showing only the date and title as the trigger).
71
+
72
+ ```markdown
73
+ ???+ note "YYYY-MM-DD — Brief title describing the major change"
74
+
75
+ ### Added
76
+ - …
77
+
78
+ ### Changed
79
+ - …
80
+
81
+ ### Deprecated
82
+ - …
83
+
84
+ ### Removed
85
+ - …
86
+
87
+ ### Fixed
88
+ - …
89
+
90
+ ### Security
91
+ - …
92
+
93
+ Contributors: Name One, Name Two
94
+ ```
95
+
96
+ Rules for the entry:
97
+
98
+ - Date is the `date` field from the script output (`YYYY-MM-DD`)
99
+ - Title is a brief, reader-facing description — not a git commit message
100
+ - No version numbers — dates only
101
+ - **Include only sections that have actual content** — omit any empty section entirely
102
+ - Section meanings: `Added` (new content), `Changed` (updated content), `Deprecated`
103
+ (content being phased out), `Removed` (deleted content), `Fixed` (corrected errors or
104
+ misleading information), `Security` (security-related documentation updates)
105
+ - `Contributors:` line — include when the `contributors` array from the script output has
106
+ **more than one name**. Omit when there is only one contributor.
107
+ - Content inside the admonition block must be indented with **4 spaces**
108
+ - When prepending, convert the previous `???+` opener to `???` first
109
+
110
+ ## CHANGELOG.md structure
111
+
112
+ If `CHANGELOG.md` does not exist yet, create it with this header before the first entry:
113
+
114
+ ```markdown
115
+ # Changelog
116
+
117
+ All notable changes to this documentation are recorded here.
118
+ The format is inspired by [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
119
+ ```
120
+
121
+ ## Initial changelog (--since DATE)
122
+
123
+ When the script is run with `--since` and the JSON contains `"mode": "since_date"`,
124
+ the user wants to bootstrap a CHANGELOG.md from scratch for a given time window.
125
+
126
+ Different rules apply:
127
+
128
+ - **Always draft** — the MAJOR threshold does not apply. This is a retrospective
129
+ summary, not an incremental gate.
130
+ - **Group by theme**, not by individual commit. Synthesise the changed files and
131
+ commit subjects into meaningful categories.
132
+ - **Date range in the title** — use the `since` value from the JSON as the start
133
+ and `date` as the end, e.g. `2026-05-01 → 2026-05-27`.
134
+ - **If CHANGELOG.md already exists**, stop and ask the user whether they want to
135
+ prepend, append, or replace. Do not overwrite silently.
136
+ - **If CHANGELOG.md does not exist**, create it with the standard header and the
137
+ entry as `???+`.
138
+
139
+ Example title: `2026-05-01 → 2026-05-27 — Initial documentation release`
140
+
141
+ ## Pitfalls
142
+
143
+ - **Do not draft an entry for every change.** The changelog is for readers who want to know
144
+ what fundamentally changed — not a git log. When in doubt, do not draft.
145
+ - **Do not commit.** Always leave the file for the user to review. The user runs `git add`
146
+ and `git commit` themselves before publishing.
147
+ - **The script is the only source of git data.** Do not interpret commit messages or diffs
148
+ yourself — the script output is deterministic and already scoped to the docs directory.
149
+ - **4-space indent is required** inside the admonition block. 2-space or tab indent will
150
+ break both Material rendering and the Confluence compile step.
151
+
152
+ ## Verification
153
+
154
+ After drafting, show the user the proposed entry in the terminal and remind them to review
155
+ `CHANGELOG.md` before committing and running `mk2conf publish`.
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env python3
2
+ """Extract changelog-relevant git data and print it as JSON.
3
+
4
+ Usage (run from the project root):
5
+ python .mk2conf/scripts/changelog_data.py [--docs-dir docs]
6
+
7
+ Output is a JSON object on stdout. Nothing is written to disk.
8
+ No external dependencies — stdlib only.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import argparse
14
+ import json
15
+ import subprocess
16
+ import sys
17
+ from datetime import date
18
+
19
+
20
+ def _run(args: list[str]) -> str:
21
+ """Run a git command and return stripped stdout. Returns '' on error."""
22
+ try:
23
+ result = subprocess.run(
24
+ args,
25
+ capture_output=True,
26
+ text=True,
27
+ check=False,
28
+ )
29
+ return result.stdout.strip()
30
+ except FileNotFoundError:
31
+ return ""
32
+
33
+
34
+ def _baseline_commit(changelog_path: str) -> str | None:
35
+ """Return the SHA of the last commit that touched changelog_path, or None."""
36
+ sha = _run(["git", "log", "--follow", "-1", "--format=%H", "--", changelog_path])
37
+ return sha or None
38
+
39
+
40
+ def _commit_before_date(since_date: str) -> str | None:
41
+ """Return the SHA of the last commit strictly before since_date (YYYY-MM-DD), or None."""
42
+ sha = _run([
43
+ "git", "log", "--format=%H",
44
+ f"--before={since_date}",
45
+ "-1",
46
+ ])
47
+ return sha or None
48
+
49
+
50
+ def _root_commit() -> str:
51
+ """Return the SHA of the very first commit in the repo."""
52
+ return _run(["git", "rev-list", "--max-parents=0", "HEAD"])
53
+
54
+
55
+ def _commits_since(baseline: str) -> list[dict[str, str]]:
56
+ """Return commits reachable from HEAD but not from baseline (exclusive)."""
57
+ sep = "\x1f" # unit separator — safe delimiter
58
+ fmt = f"%H{sep}%s{sep}%aN{sep}%as" # sha, subject, author name, date
59
+ raw = _run(["git", "log", f"{baseline}..HEAD", f"--format={fmt}"])
60
+ if not raw:
61
+ return []
62
+ commits = []
63
+ for line in raw.splitlines():
64
+ parts = line.split(sep)
65
+ if len(parts) != 4: # noqa: PLR2004
66
+ continue
67
+ sha, subject, author, commit_date = parts
68
+ commits.append({"sha": sha, "subject": subject, "author": author, "date": commit_date})
69
+ return commits
70
+
71
+
72
+ def _changed_files(baseline: str, docs_dir: str) -> dict[str, list[str]]:
73
+ """Return files changed in docs_dir since baseline, grouped by status."""
74
+ raw = _run([
75
+ "git", "diff", "--name-status", f"{baseline}..HEAD", "--", docs_dir,
76
+ ])
77
+ added: list[str] = []
78
+ modified: list[str] = []
79
+ deleted: list[str] = []
80
+ if not raw:
81
+ return {"added": added, "modified": modified, "deleted": deleted}
82
+ for line in raw.splitlines():
83
+ parts = line.split("\t", maxsplit=1)
84
+ if len(parts) != 2: # noqa: PLR2004
85
+ continue
86
+ status, path = parts[0].strip(), parts[1].strip()
87
+ if status.startswith("A"):
88
+ added.append(path)
89
+ elif status.startswith("D"):
90
+ deleted.append(path)
91
+ elif status.startswith("M") or status.startswith("R") or status.startswith("C"):
92
+ modified.append(path)
93
+ return {"added": added, "modified": modified, "deleted": deleted}
94
+
95
+
96
+ def _contributors(commits: list[dict[str, str]]) -> list[str]:
97
+ """Return unique contributor names from commits, preserving first-seen order."""
98
+ seen: set[str] = set()
99
+ result: list[str] = []
100
+ for c in commits:
101
+ name = c["author"]
102
+ if name and name not in seen:
103
+ seen.add(name)
104
+ result.append(name)
105
+ return result
106
+
107
+
108
+ def main() -> None:
109
+ parser = argparse.ArgumentParser(description="Extract git changelog data as JSON.")
110
+ parser.add_argument(
111
+ "--docs-dir",
112
+ default="docs",
113
+ help="Path to the MkDocs docs directory, relative to project root (default: docs)",
114
+ )
115
+ parser.add_argument(
116
+ "--since",
117
+ metavar="DATE",
118
+ default=None,
119
+ help=(
120
+ "Use the last commit before DATE (YYYY-MM-DD) as the baseline instead of the "
121
+ "last commit that touched CHANGELOG.md. Useful for creating an initial changelog."
122
+ ),
123
+ )
124
+ args = parser.parse_args()
125
+
126
+ docs_dir: str = args.docs_dir
127
+ changelog_rel = f"{docs_dir}/CHANGELOG.md"
128
+
129
+ # Baseline resolution — --since takes priority
130
+ if args.since:
131
+ baseline = _commit_before_date(args.since)
132
+ if not baseline:
133
+ print(
134
+ f"error: no commit found before {args.since}",
135
+ file=sys.stderr,
136
+ )
137
+ sys.exit(1)
138
+ mode = "since_date"
139
+ else:
140
+ baseline = _baseline_commit(changelog_rel) or _root_commit()
141
+ if not baseline:
142
+ print(
143
+ "error: could not determine a baseline commit — is this a git repository?",
144
+ file=sys.stderr,
145
+ )
146
+ sys.exit(1)
147
+ mode = "changelog_commit"
148
+
149
+ commits = _commits_since(baseline)
150
+ changes = _changed_files(baseline, docs_dir)
151
+ contributors = _contributors(commits)
152
+
153
+ output = {
154
+ "date": date.today().isoformat(),
155
+ "mode": mode,
156
+ "since": args.since,
157
+ "baseline_commit": baseline,
158
+ "commits": commits,
159
+ "contributors": contributors,
160
+ "changes": changes,
161
+ "docs_dir": docs_dir,
162
+ }
163
+
164
+ print(json.dumps(output, indent=2, ensure_ascii=False))
165
+
166
+
167
+ if __name__ == "__main__":
168
+ main()
@@ -115,6 +115,17 @@ def test_install_skill_explicit_tool_ignores_detection(tmp_path: Path) -> None:
115
115
  assert dest.exists()
116
116
 
117
117
 
118
+ def test_install_skill_copies_script(tmp_path: Path) -> None:
119
+ """The data script is always installed to .mk2conf/scripts/ regardless of tool."""
120
+ install_skill(project_dir=tmp_path, tool="claude")
121
+
122
+ script = tmp_path / ".mk2conf" / "scripts" / "changelog_data.py"
123
+ assert script.exists(), ".mk2conf/scripts/changelog_data.py should be created"
124
+ content = script.read_text(encoding="utf-8")
125
+ assert "changelog_data" in content
126
+ assert "import json" in content
127
+
128
+
118
129
  def test_install_skill_overwrites_existing(tmp_path: Path) -> None:
119
130
  (tmp_path / ".claude").mkdir()
120
131
  dest = tmp_path / ".claude" / "commands" / "mk2conf-changelog.md"
@@ -1,93 +0,0 @@
1
- ---
2
- name: mkdocs-changelog
3
- description: Analyse doc changes since the last CHANGELOG.md update and draft a major-change entry if the changes qualify.
4
- version: "1.1.0"
5
- tags: [documentation, git, changelog, mkdocs, confluence]
6
- specificity: context-specific
7
- tool_agnostic: true
8
- authors: [Anders Hybertz]
9
- tested_on: []
10
- ---
11
-
12
- # MkDocs Changelog Entry
13
-
14
- Analyse git changes to the docs directory since the last `CHANGELOG.md` commit. If any changes qualify as **MAJOR**, draft a dated changelog entry and prepend it to `CHANGELOG.md`. If not, explain why and exit without modifying any file.
15
-
16
- ## When to Use
17
-
18
- - After making one or more documentation changes and before running `mk2conf publish`
19
- - Any point in the writing flow when you want to assess whether a "What's New" entry is warranted
20
-
21
- ## Steps
22
-
23
- 1. **Find the baseline** — run `git log --follow -1 --format="%H" -- <docs_dir>/CHANGELOG.md` to get the last commit that touched `CHANGELOG.md`. If no commit is found, use the root commit as the baseline.
24
-
25
- 2. **Collect doc changes** — run `git diff <baseline>..HEAD -- <docs_dir>/` to see everything that changed in the docs directory since that baseline.
26
-
27
- 3. **Read the existing changelog** — read `<docs_dir>/CHANGELOG.md` for context on what was previously recorded.
28
-
29
- 4. **Decide: is this MAJOR?**
30
-
31
- **MAJOR criteria — any one of these qualifies:**
32
- - A new top-level documentation area or section added (a new folder or nav section that didn't exist before)
33
- - A significant area deleted or substantially restructured (not just moved or renamed)
34
- - A fundamental definition, concept, or policy changed in a way that affects how readers understand the subject
35
-
36
- **NOT major — do not draft an entry for:**
37
- - Typo fixes, grammar corrections, spelling
38
- - Formatting, diagram adjustments, image swaps
39
- - Small additions (a paragraph, a note, a clarification) that do not change the substance
40
- - Rewordings that preserve the original meaning
41
- - Internal restructuring with no reader-facing impact
42
-
43
- 5. **If NOT MAJOR** — report what was found, explain in one sentence why it did not qualify, and stop. Do not modify any file.
44
-
45
- 6. **If MAJOR** — draft an entry using this format and prepend it to `CHANGELOG.md`:
46
-
47
- ```markdown
48
- ## YYYY-MM-DD — Brief title describing the major change
49
-
50
- ### Added
51
- - …
52
-
53
- ### Changed
54
- - …
55
-
56
- ### Deprecated
57
- - …
58
-
59
- ### Removed
60
- - …
61
-
62
- ### Fixed
63
- - …
64
-
65
- ### Security
66
- - …
67
- ```
68
-
69
- Rules for the entry:
70
- - Date is today's date in `YYYY-MM-DD` format
71
- - Title is a brief, reader-facing description (not a git commit message)
72
- - No version numbers — dates only
73
- - **Include only sections that have actual content** — omit any empty section entirely
74
- - Section meanings: `Added` (new content), `Changed` (updated content), `Deprecated` (content being phased out), `Removed` (deleted content), `Fixed` (corrected errors or misleading information), `Security` (security-related documentation updates)
75
-
76
- Prepend the entry above any existing entries in `CHANGELOG.md`. Do not commit — the user reviews, edits if needed, and commits manually.
77
-
78
- ## Pitfalls
79
-
80
- - **Do not draft an entry for every change.** The changelog is for readers who want to know what fundamentally changed, not a git log. When in doubt, do not draft.
81
- - **Do not commit.** Always leave the file for the user to review. The user runs `git add` and `git commit` themselves before publishing.
82
- - **If `CHANGELOG.md` does not exist yet**, create it with this header before the first entry:
83
-
84
- ```markdown
85
- # Changelog
86
-
87
- All notable changes to this documentation are recorded here.
88
- The format is inspired by [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
89
- ```
90
-
91
- ## Verification
92
-
93
- After drafting, show the user the proposed entry in the terminal and remind them to review `CHANGELOG.md` before committing and running `mk2conf publish`.