mdcompose 0.1.3__tar.gz → 0.2.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.
- mdcompose-0.2.0/.github/FUNDING.yml +1 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.github/workflows/ci.yml +25 -24
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.github/workflows/supply-chain.yml +1 -1
- {mdcompose-0.1.3 → mdcompose-0.2.0}/CHANGELOG.md +6 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/PKG-INFO +4 -7
- {mdcompose-0.1.3 → mdcompose-0.2.0}/README.md +3 -6
- {mdcompose-0.1.3 → mdcompose-0.2.0}/TODO.md +94 -197
- {mdcompose-0.1.3 → mdcompose-0.2.0}/docs/concepts.md +25 -6
- {mdcompose-0.1.3 → mdcompose-0.2.0}/docs/core-contract.md +63 -3
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/commands/init_cmd.py +67 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/config.py +33 -1
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/init_ops.py +218 -19
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/managed_block.py +3 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/manifest.py +76 -4
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/platform.py +2 -3
- mdcompose-0.2.0/mdcompose/core/skill_composition.py +81 -0
- mdcompose-0.2.0/mdcompose/core/skills.py +290 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/pyproject.toml +1 -1
- mdcompose-0.2.0/tests/fixtures/mdcompose.lock.schema1.json +28 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_config.py +33 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_config_cmd.py +25 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_init.py +129 -0
- mdcompose-0.2.0/tests/test_init_ops.py +673 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_managed_block.py +21 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_manifest.py +147 -0
- mdcompose-0.2.0/tests/test_skill_composition.py +85 -0
- mdcompose-0.2.0/tests/test_skills.py +242 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/uv.lock +258 -258
- mdcompose-0.1.3/tests/test_init_ops.py +0 -299
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.gitattributes +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.github/workflows/bump.yml +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.github/workflows/codeql.yml +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.github/workflows/dependency-review.yml +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.github/workflows/mutation.yml +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.github/workflows/python-eol.yml +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.github/workflows/release.yml +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.github/workflows/scorecard.yml +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.github/zizmor.yml +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.gitignore +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/.pre-commit-config.yaml +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/CODE_OF_CONDUCT.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/CONTRIBUTING.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/LICENSE +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/SECURITY.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/docs/benchmarks.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/docs/commit-and-release-tooling.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/docs/hardening-review.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/docs/optional-tooling.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/docs/publishing.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/docs/scorecard.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/docs/versioning-explained.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/__init__.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/cli.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/commands/__init__.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/commands/config_cmd.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/commands/convert_cmd.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/commands/eject_cmd.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/commands/import_cmd.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/commands/snippet_cmd.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/commands/target_cmd.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/__init__.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/composition.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/convert_ops.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/eject.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/exit_codes.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/files.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/import_ops.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/library_ops.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/output.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/report.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/sections.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/snippets.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/core/targets.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/prompts.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/mdcompose/version.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/renovate.json +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/scripts/check_ascii.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/scripts/check_licenses.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/benchmarks/conftest.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/benchmarks/test_core_ops.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/conftest.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/fixtures/plain.bom.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/fixtures/plain.crlf.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/fixtures/plain.lf.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/fixtures/prose.cp1252.md +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_cli.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_convert.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_doctor_drift.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_e2e_import_convert.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_eject.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_files.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_fuzz.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_import_cmd.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_import_ops.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_layering.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_onedrive.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_output.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_platform.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_prompts.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_report.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_sections.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_snippet_commands.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_snippets.py +0 -0
- {mdcompose-0.1.3 → mdcompose-0.2.0}/tests/test_targets.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ko_fi: rovetown
|
|
@@ -292,27 +292,28 @@ jobs:
|
|
|
292
292
|
esac
|
|
293
293
|
done
|
|
294
294
|
|
|
295
|
-
#
|
|
296
|
-
#
|
|
297
|
-
#
|
|
298
|
-
#
|
|
299
|
-
#
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
295
|
+
# CodSpeed runs the benchmarks instrumented rather than wall-clock, so the
|
|
296
|
+
# numbers are stable across runners, and it posts the delta as a pull request
|
|
297
|
+
# check rather than failing hard. Free for open source. Not in the all-green
|
|
298
|
+
# needs list: benchmarks never block a merge. Needs the repo connected at
|
|
299
|
+
# codspeed.io once before its first run.
|
|
300
|
+
benchmarks:
|
|
301
|
+
name: benchmarks
|
|
302
|
+
runs-on: ubuntu-latest
|
|
303
|
+
timeout-minutes: 20
|
|
304
|
+
permissions:
|
|
305
|
+
contents: read
|
|
306
|
+
id-token: write # OpenID Connect authentication with CodSpeed
|
|
307
|
+
steps:
|
|
308
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
309
|
+
with:
|
|
310
|
+
persist-credentials: false
|
|
311
|
+
- uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
312
|
+
with:
|
|
313
|
+
enable-cache: true
|
|
314
|
+
cache-dependency-glob: uv.lock
|
|
315
|
+
- run: uv sync --locked --group benchmark
|
|
316
|
+
- uses: CodSpeedHQ/action@373d6868929f444bc08d901fd0eb0ad52a8875ea # v5.2.1
|
|
317
|
+
with:
|
|
318
|
+
run: uv run --no-sync pytest tests/benchmarks --codspeed
|
|
319
|
+
mode: "simulation"
|
|
@@ -85,7 +85,7 @@ jobs:
|
|
|
85
85
|
with:
|
|
86
86
|
persist-credentials: false
|
|
87
87
|
|
|
88
|
-
- uses: google/osv-scanner-action/osv-scanner-action@
|
|
88
|
+
- uses: google/osv-scanner-action/osv-scanner-action@a345acffa64b0eaede81a3d9aae6141214d9c8fc # v2.6.0
|
|
89
89
|
with:
|
|
90
90
|
scan-args: >-
|
|
91
91
|
--lockfile=uv.lock
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
All notable changes to mdcompose are recorded here.
|
|
4
4
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
5
|
|
|
6
|
+
## v0.2.0 (2026-09-14)
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- compose agent skills alongside AGENTS.md and CLAUDE.md
|
|
11
|
+
|
|
6
12
|
## v0.1.3 (2026-09-11)
|
|
7
13
|
|
|
8
14
|
## v0.1.2 (2026-09-11)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: mdcompose
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Keep CLAUDE.md and AGENTS.md in sync and composed from a personal snippet library, without hand-editing every project
|
|
5
5
|
Project-URL: Homepage, https://github.com/Rovetown/mdcompose
|
|
6
6
|
Project-URL: Repository, https://github.com/Rovetown/mdcompose
|
|
@@ -40,15 +40,14 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
|
|
41
41
|
<div align="center">
|
|
42
42
|
|
|
43
|
-
[](https://pypi.org/project/mdcompose/) [](https://pypi.org/project/mdcompose/) [](https://github.com/Rovetown/mdcompose/actions/workflows/ci.yml) [](https://scorecard.dev/viewer/?uri=github.com/Rovetown/mdcompose) [](https://github.com/Rovetown/mdcompose/blob/main/LICENSE) [](https://pypi.org/project/mdcompose/)
|
|
43
|
+
[](https://pypi.org/project/mdcompose/) [](https://pypi.org/project/mdcompose/) [](https://github.com/Rovetown/mdcompose/actions/workflows/ci.yml) [](https://app.codspeed.io//Rovetown/mdcompose?utm_source=badge) [](https://scorecard.dev/viewer/?uri=github.com/Rovetown/mdcompose) [](https://www.bestpractices.dev/projects/14614) [](https://github.com/Rovetown/mdcompose/blob/main/LICENSE) [](https://pypi.org/project/mdcompose/) [](https://ko-fi.com/Y8Y31VUCGT)
|
|
44
44
|
|
|
45
45
|
</div>
|
|
46
46
|
|
|
47
47
|
Most repositories need the same handful of instructions for AI coding agents: how commits are written, which test command to run, which files not to touch.
|
|
48
48
|
mdcompose keeps those as small snippets in one place and builds each project's `AGENTS.md` and `CLAUDE.md` from the ones you pick, so you stop copying the same paragraphs between repositories.
|
|
49
49
|
|
|
50
|
-
> `mdcompose` composes Markdown.
|
|
51
|
-
> It is unrelated to Docker Compose; the shared `-compose` suffix is a coincidence.
|
|
50
|
+
> `mdcompose` composes Markdown. It is unrelated to Docker Compose.
|
|
52
51
|
|
|
53
52
|
## Highlights
|
|
54
53
|
|
|
@@ -89,11 +88,9 @@ uvx mdcompose doctor
|
|
|
89
88
|
|
|
90
89
|
The whole flow, start to finish:
|
|
91
90
|
|
|
92
|
-
<!-- demo GIF: docs/assets/mdcompose-demo.gif -- not yet recorded, see TODO.md. Restore the <img> below once it exists. -->
|
|
93
|
-
|
|
94
91
|
<div align="center">
|
|
95
92
|
|
|
96
|
-
|
|
93
|
+
<img src="https://raw.githubusercontent.com/Rovetown/mdcompose/main/docs/assets/mdcompose-demo.gif" alt="mdcompose demo: init's interactive picker composing AGENTS.md and CLAUDE.md from selected snippets, then doctor confirming both are clean" width="700">
|
|
97
94
|
|
|
98
95
|
</div>
|
|
99
96
|
|
|
@@ -10,15 +10,14 @@
|
|
|
10
10
|
|
|
11
11
|
<div align="center">
|
|
12
12
|
|
|
13
|
-
[](https://pypi.org/project/mdcompose/) [](https://pypi.org/project/mdcompose/) [](https://github.com/Rovetown/mdcompose/actions/workflows/ci.yml) [](https://scorecard.dev/viewer/?uri=github.com/Rovetown/mdcompose) [](https://github.com/Rovetown/mdcompose/blob/main/LICENSE) [](https://pypi.org/project/mdcompose/)
|
|
13
|
+
[](https://pypi.org/project/mdcompose/) [](https://pypi.org/project/mdcompose/) [](https://github.com/Rovetown/mdcompose/actions/workflows/ci.yml) [](https://app.codspeed.io//Rovetown/mdcompose?utm_source=badge) [](https://scorecard.dev/viewer/?uri=github.com/Rovetown/mdcompose) [](https://www.bestpractices.dev/projects/14614) [](https://github.com/Rovetown/mdcompose/blob/main/LICENSE) [](https://pypi.org/project/mdcompose/) [](https://ko-fi.com/Y8Y31VUCGT)
|
|
14
14
|
|
|
15
15
|
</div>
|
|
16
16
|
|
|
17
17
|
Most repositories need the same handful of instructions for AI coding agents: how commits are written, which test command to run, which files not to touch.
|
|
18
18
|
mdcompose keeps those as small snippets in one place and builds each project's `AGENTS.md` and `CLAUDE.md` from the ones you pick, so you stop copying the same paragraphs between repositories.
|
|
19
19
|
|
|
20
|
-
> `mdcompose` composes Markdown.
|
|
21
|
-
> It is unrelated to Docker Compose; the shared `-compose` suffix is a coincidence.
|
|
20
|
+
> `mdcompose` composes Markdown. It is unrelated to Docker Compose.
|
|
22
21
|
|
|
23
22
|
## Highlights
|
|
24
23
|
|
|
@@ -59,11 +58,9 @@ uvx mdcompose doctor
|
|
|
59
58
|
|
|
60
59
|
The whole flow, start to finish:
|
|
61
60
|
|
|
62
|
-
<!-- demo GIF: docs/assets/mdcompose-demo.gif -- not yet recorded, see TODO.md. Restore the <img> below once it exists. -->
|
|
63
|
-
|
|
64
61
|
<div align="center">
|
|
65
62
|
|
|
66
|
-
|
|
63
|
+
<img src="https://raw.githubusercontent.com/Rovetown/mdcompose/main/docs/assets/mdcompose-demo.gif" alt="mdcompose demo: init's interactive picker composing AGENTS.md and CLAUDE.md from selected snippets, then doctor confirming both are clean" width="700">
|
|
67
64
|
|
|
68
65
|
</div>
|
|
69
66
|
|
|
@@ -5,129 +5,42 @@ management CLI. Everything here is either open or a decision worth keeping.
|
|
|
5
5
|
|
|
6
6
|
## Next
|
|
7
7
|
|
|
8
|
-
The codebase
|
|
9
|
-
`github.com/Rovetown/mdcompose`
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
prerelease tag left commitizen unable to bound the next changelog (fixed by
|
|
45
|
-
reverting the botched bump in #13).
|
|
46
|
-
|
|
47
|
-
### README (active, before the stable release)
|
|
48
|
-
|
|
49
|
-
The README should be good before `0.1.0` reaches PyPI, since `readme =
|
|
50
|
-
"README.md"` becomes the PyPI long description. Structure is agreed: lean
|
|
51
|
-
front-door, ~130 to 200 lines, `for-the-badge` style shields badges, near-zero
|
|
52
|
-
emoji, ASCII diagrams and trees (no mermaid, because PyPI does not render it),
|
|
53
|
-
HTML only for layout. Prose is neutral-dev, accessible to a non-expert without
|
|
54
|
-
dropping the technical terms.
|
|
55
|
-
|
|
56
|
-
Done: [`README.md`](README.md) written (from the v3 draft, demo GIF placed in Quickstart
|
|
57
|
-
rather than Highlights, since the GIF shows the flow the Quickstart walks
|
|
58
|
-
through), both drafts deleted, `scripts/check_ascii.py` exempts [`README.md`](README.md)
|
|
59
|
-
via a `SKIP_FILES` set (Decisions log below records it), reference-level detail
|
|
60
|
-
moved out to [`docs/concepts.md`](docs/concepts.md), and a logo added: `docs/assets/mdcompose-logo.svg`
|
|
61
|
-
in the centered header (replacing the `# mdcompose` text heading), referenced
|
|
62
|
-
by the same absolute `raw.githubusercontent.com/.../main/...` URL as the demo
|
|
63
|
-
GIF so it also renders on PyPI, and `docs/assets/**` excluded from the sdist in
|
|
64
|
-
`pyproject.toml` (`[tool.hatch.build]`, verified with a real `uv build`; the
|
|
65
|
-
wheel was never affected, it already only packages `mdcompose`). Badges use
|
|
66
|
-
`style=for-the-badge`, not `flat-square`. Not yet committed.
|
|
67
|
-
|
|
68
|
-
Still to finish:
|
|
69
|
-
|
|
70
|
-
- **Record the demo GIF.** Deliberately deferred, not blocking `0.1.0`: no
|
|
71
|
-
time to set up the recording right now. The README's Quickstart spot now
|
|
72
|
-
holds an italic text placeholder instead of a broken `<img>` tag, with a
|
|
73
|
-
comment naming exactly what to restore once the GIF exists. Plan for when
|
|
74
|
-
time allows: script an asciinema recording of the real flow (create a
|
|
75
|
-
snippet file, `mdcompose init` with the picker, `mdcompose doctor` showing a
|
|
76
|
-
clean then drifted state), convert with
|
|
77
|
-
`agg demo.cast docs/assets/mdcompose-demo.gif`. Keep it short, roughly 15 to
|
|
78
|
-
25 seconds, one clear take. Shot list to write first: exact commands, where
|
|
79
|
-
to pause, terminal size and theme so it is re-recordable when output
|
|
80
|
-
changes. `docs/assets/` already exists (holds the logo).
|
|
81
|
-
- **Verify the badges resolve** once `0.1.0` is on PyPI (the PyPI, pyversions,
|
|
82
|
-
and scorecard badges 404 or show "unknown" until then).
|
|
83
|
-
|
|
84
|
-
### After the README, resume the release
|
|
85
|
-
|
|
86
|
-
4. Done. `bump.yml` ran with channel `stable`, `0.1.0a0` -> `0.1.0`, `v0.1.0`
|
|
87
|
-
tagged, the `pypi` environment approved. `v0.1.0` is live on PyPI and the
|
|
88
|
-
first GitHub Release exists with the SBOM and attestation assets attached.
|
|
89
|
-
5. Done. [`CHANGELOG.md`](CHANGELOG.md)'s `## v0.1.0` entry replaced cz's thin
|
|
90
|
-
auto body ("break the cli and command import cycle", "relock uv.lock",
|
|
91
|
-
etc., none of which belong in a *user-facing* changelog) with "Initial
|
|
92
|
-
public release." plus the full `### Added` command list restored verbatim
|
|
93
|
-
from the old `[Unreleased]` section (recovered from commit `2252051`, since
|
|
94
|
-
the alpha bump had already consumed it) and the `### Security`
|
|
95
|
-
SLSA-provenance note. cz's `### Changed` and `### Fixed` sections dropped:
|
|
96
|
-
there is no prior release to change from.
|
|
97
|
-
|
|
98
|
-
Also done in this pass, discovered from the live PyPI listing: the
|
|
99
|
-
`pypi/pyversions` badge showed a bare "Python 3" rather than the real
|
|
100
|
-
supported range, because shields.io reads PyPI's classifiers, not
|
|
101
|
-
`requires-python`, and only a bare `Programming Language :: Python :: 3` was
|
|
102
|
-
listed. `pyproject.toml` now lists `3.11` through `3.14` individually,
|
|
103
|
-
matching the CI matrix. This only takes effect on the *next* release; it does
|
|
104
|
-
not retroactively fix the metadata already published for `0.1.0`.
|
|
105
|
-
|
|
106
|
-
### After the first release
|
|
107
|
-
|
|
108
|
-
6. **Post-release hardening.** Partly done: `Development Status` moved to
|
|
109
|
-
`4 - Beta` (the v1 feature set is complete and tested -- all 8 OpenSpec
|
|
110
|
-
changes shipped, 93%+ coverage, mutation-tested, hardening review done --
|
|
111
|
-
but pre-1.0 semver still allows a breaking change, so Beta fits better than
|
|
112
|
-
a Stable claim; also takes effect on the next release only). Still open:
|
|
113
|
-
point `platform.ONEDRIVE_HELP_URL` at a real page.
|
|
114
|
-
7. **OpenSSF Best Practices passing badge.** Register the repo at
|
|
115
|
-
bestpractices.dev, complete the passing questionnaire (most criteria are
|
|
116
|
-
already met by the existing CI, tests, license, and static analysis), embed
|
|
117
|
-
the badge in the README. Scorecard's CII-Best-Practices check reads it
|
|
118
|
-
through the API: 0 to 5. Silver and gold are not attainable for a
|
|
119
|
-
solo-maintained project, so passing is the target. See [`docs/scorecard.md`](docs/scorecard.md).
|
|
120
|
-
8. **Dismiss the permanent Scorecard code-scanning alerts.** Code-Review,
|
|
121
|
-
Branch-Protection, and Fuzzing are solo-maintainer structural or a
|
|
122
|
-
deliberate non-goal (see [`docs/scorecard.md`](docs/scorecard.md), Accepted limitations). Dismiss
|
|
123
|
-
them as "won't fix" in the Security tab so the alert count stays meaningful;
|
|
124
|
-
leave Maintained and CII-Best-Practices to self-resolve.
|
|
125
|
-
|
|
126
|
-
### Not planned
|
|
127
|
-
|
|
128
|
-
9. **Fuzzing.** Left at 0. Scorecard does not detect Python Hypothesis, and an
|
|
129
|
-
Atheris plus ClusterFuzzLite setup is out of proportion to the risk for two
|
|
130
|
-
small parsers. Revisit only if the parser surface grows.
|
|
8
|
+
The codebase, the automation, and the account-side release setup are all done.
|
|
9
|
+
The repo is live at `github.com/Rovetown/mdcompose`, `v0.1.3` is on PyPI, and
|
|
10
|
+
the WSL demo recording did a clean-venv `pip install` against that real
|
|
11
|
+
published package. What is left is a short list of items that are either
|
|
12
|
+
genuinely still open or deliberately deferred to a future trigger (a date, a
|
|
13
|
+
second maintainer, a badge cache catching up). The Scorecard reasoning behind
|
|
14
|
+
the deferred items is in [`docs/scorecard.md`](docs/scorecard.md).
|
|
15
|
+
|
|
16
|
+
Done: CodSpeed badge added to the README badge row, `style=for-the-badge`,
|
|
17
|
+
placed next to the CI badge. Done: PyPI, Python-versions, and OpenSSF
|
|
18
|
+
Scorecard badges confirmed rendering real values, not "unknown" or a
|
|
19
|
+
broken-image placeholder.
|
|
20
|
+
|
|
21
|
+
### Deferred
|
|
22
|
+
|
|
23
|
+
- **OpenSSF Scorecard: Maintained check.** Scores 0 regardless of activity
|
|
24
|
+
until the repository passes 90 days old; it was created 2026-09-09, so this
|
|
25
|
+
clears around 2026-12-08. Nothing to do before then; revisit only to
|
|
26
|
+
confirm the score actually moved once that date passes.
|
|
27
|
+
- **OpenSSF Scorecard: code-scanning alerts left open, not dismissed.**
|
|
28
|
+
Code-Review, Branch-Protection, and Fuzzing are solo-maintainer structural
|
|
29
|
+
limits (see [`docs/scorecard.md`](docs/scorecard.md), Accepted
|
|
30
|
+
limitations), but "won't fix" is not a permanent label here -- the
|
|
31
|
+
constraint holds only while there is one maintainer. Revisit once a second
|
|
32
|
+
maintainer or a collaborator with review rights joins: Code-Review and
|
|
33
|
+
Branch-Protection stop being structural at that point, and Fuzzing can be
|
|
34
|
+
reconsidered on its own merits.
|
|
35
|
+
- **Fuzzing.** Left at 0 on purpose, not a gap to close now. Scorecard does
|
|
36
|
+
not detect Python Hypothesis (already used in the parser tests), and an
|
|
37
|
+
Atheris plus ClusterFuzzLite setup is out of proportion to the risk for two
|
|
38
|
+
small parsers. Revisit only if the parser surface grows materially.
|
|
39
|
+
- **Add each new Python to the CI matrix by hand** when its final release
|
|
40
|
+
ships (annual cadence, not worth automating). 3.15 is deferred until then
|
|
41
|
+
(was rc2 on 2026-09-13, final due October 2026): a prerelease-specific
|
|
42
|
+
version string in `uv sync --python` is not worth carrying for a few weeks
|
|
43
|
+
of coverage the final release gets for free.
|
|
131
44
|
|
|
132
45
|
## Where things live
|
|
133
46
|
|
|
@@ -200,6 +113,33 @@ not retroactively fix the metadata already published for `0.1.0`.
|
|
|
200
113
|
`version_provider = "scm"` handshake with commitizen) than a solo project
|
|
201
114
|
bumping one line per release needs. Decision closed.
|
|
202
115
|
|
|
116
|
+
- **Demo GIF recording needs a pty CPR trick, if it is ever re-recorded.**
|
|
117
|
+
`questionary`'s picker sits on `prompt_toolkit`, which probes the terminal
|
|
118
|
+
with a cursor-position request (`\x1b[6n`) before it will live-redraw. A
|
|
119
|
+
bare `pexpect.spawn` pty never answers that probe, so the picker silently
|
|
120
|
+
degrades to a static, non-redrawing render -- every keystroke lands, but
|
|
121
|
+
nothing visibly updates until the final "done (N selections)" line. Fix
|
|
122
|
+
used for `docs/assets/mdcompose-demo.gif`: a background thread reads the
|
|
123
|
+
child pty directly (`os.read` on `child.child_fd`, not `child.expect`,
|
|
124
|
+
since only one reader can drain the fd), mirrors every byte to stdout for
|
|
125
|
+
`asciinema` to capture, and answers any `\x1b[6n` it sees with a plausible
|
|
126
|
+
`\x1b[<row>;1R` immediately. That alone is what makes the recording a real
|
|
127
|
+
live checkbox animation instead of a slideshow. The driver script and seed
|
|
128
|
+
snippets were scratch-only (`temp/`), never committed, so this needs
|
|
129
|
+
redoing from scratch next time.
|
|
130
|
+
- **PyPI's `pyversions` badge reads classifiers, not `requires-python`.**
|
|
131
|
+
`pyproject.toml` had only a bare `Programming Language :: Python :: 3`
|
|
132
|
+
classifier, so shields.io rendered the badge as "Python 3" instead of the
|
|
133
|
+
real supported range. Fixed by listing `3.11` through `3.14` individually
|
|
134
|
+
as classifiers, matching the CI matrix. Takes effect only on the next
|
|
135
|
+
release; already-published metadata for a prior version is not
|
|
136
|
+
retroactively fixed.
|
|
137
|
+
- **OpenSSF Best Practices registration is project 14614.**
|
|
138
|
+
`https://www.bestpractices.dev/projects/14614`, homepage set to the GitHub
|
|
139
|
+
repo URL, Passing tier across all six categories. Needed again only if the
|
|
140
|
+
self-assessment has to be revisited (a criterion changes, or silver/gold
|
|
141
|
+
becomes reachable with a second maintainer).
|
|
142
|
+
|
|
203
143
|
## Reference docs
|
|
204
144
|
|
|
205
145
|
- [`docs/versioning-explained.md`](docs/versioning-explained.md) - how a number becomes `0.3.2` vs `0.4.0` vs
|
|
@@ -233,9 +173,10 @@ free for public or personal-account use:
|
|
|
233
173
|
- OpenSSF Scorecard - free
|
|
234
174
|
- CodeQL - free for public repos
|
|
235
175
|
|
|
236
|
-
Excluded because metered or paid: Codecov
|
|
237
|
-
|
|
238
|
-
`coverage --fail-under` and uploaded as an artifact, never
|
|
176
|
+
Excluded because metered or paid: Codecov. CodSpeed is wired (free OSS tier,
|
|
177
|
+
connected at codspeed.io, see the `benchmarks` job below); Coverage itself is
|
|
178
|
+
gated in-repo with `coverage --fail-under` and uploaded as an artifact, never
|
|
179
|
+
sent to a service.
|
|
239
180
|
|
|
240
181
|
One thing to confirm before wiring: `gitleaks/gitleaks-action` needs a free
|
|
241
182
|
`GITLEAKS_LICENSE` key **only when the repo owner is a GitHub organisation**. If
|
|
@@ -257,7 +198,8 @@ One thing to confirm before wiring: `gitleaks/gitleaks-action` needs a free
|
|
|
257
198
|
|
|
258
199
|
In the repo now: `ci.yml` (test matrix 3 OS x Python 3.11-3.14, `lint` (ruff +
|
|
259
200
|
`mypy` + `deptry`), `coverage`, `ascii`, `build`, plus `hooks`, `commits`,
|
|
260
|
-
`gitleaks`, `all-green`), `
|
|
201
|
+
`gitleaks`, `benchmarks` (CodSpeed, not in `all-green`'s needs), `all-green`),
|
|
202
|
+
`supply-chain.yml` (`licenses`, `audit`, `osv`,
|
|
261
203
|
`sbom`), `dependency-review.yml` (PR-only, `deny-licenses` = the copyleft
|
|
262
204
|
families), `release.yml` (`v*` tag), `bump.yml` (the dispatched release button),
|
|
263
205
|
`mutation.yml` (weekly `mutmut`), `codeql.yml`, `scorecard.yml`,
|
|
@@ -303,11 +245,15 @@ tag through.
|
|
|
303
245
|
|
|
304
246
|
### Dormant - shipped commented, with an `# ENABLE WHEN:` marker
|
|
305
247
|
|
|
306
|
-
- `codspeed` job in `ci.yml` - ENABLE WHEN the repo exists and a CodSpeed
|
|
307
|
-
project is connected. The benchmark suite is written; only the hosted runner
|
|
308
|
-
is missing.
|
|
309
248
|
- all-contributors config - ENABLE WHEN the first outside contributor lands.
|
|
310
249
|
|
|
250
|
+
The `benchmarks` job in `ci.yml` was in this list; it is now uncommented,
|
|
251
|
+
pinned, and connected at codspeed.io (run `34781861376`, commit `4b43aa5`,
|
|
252
|
+
2026-09-13, uploaded successfully after an earlier run failed with a
|
|
253
|
+
`401 Unauthorized` before the account-side connection existed). It runs on
|
|
254
|
+
every push and PR but is not in `all-green`'s needs list, so it cannot block a
|
|
255
|
+
merge.
|
|
256
|
+
|
|
311
257
|
(`mutation.yml` is not dormant - it ships active on a weekly cron, non-blocking.)
|
|
312
258
|
|
|
313
259
|
### Local iteration
|
|
@@ -349,19 +295,13 @@ alerts + security updates and private vulnerability reporting. Allow the
|
|
|
349
295
|
|
|
350
296
|
### Explicitly not in the pipeline
|
|
351
297
|
|
|
352
|
-
Codecov /
|
|
298
|
+
Codecov / any other metered service (cost); git-cliff / towncrier
|
|
353
299
|
(commitizen writes the changelog); release-please / python-semantic-release
|
|
354
300
|
(rejected, philosophy C is the model); a triage bot (issue volume); hatch-vcs /
|
|
355
301
|
`hatch version` / setuptools-scm (no version-derivation layer; `cz bump` writes
|
|
356
302
|
`version` directly); trufflehog as a hook or job (it is a one-time local
|
|
357
303
|
pre-publish run only).
|
|
358
304
|
|
|
359
|
-
## Open work
|
|
360
|
-
|
|
361
|
-
Things that are not behavior, so they carry no core-contract change.
|
|
362
|
-
|
|
363
|
-
- [ ] Add an optional funding link via `.github/FUNDING.yml` and a line in the README footer. Deferred at the maintainer's request.
|
|
364
|
-
|
|
365
305
|
## Standing decision: competitive landscape
|
|
366
306
|
|
|
367
307
|
Three tools cover the core idea of a personal library of reusable snippets
|
|
@@ -394,58 +334,19 @@ but because it is the active brand of an existing company (ContextSmith Inc.,
|
|
|
394
334
|
B2B customer-intelligence SaaS, founded 2015, Sunnyvale). Fine for purely
|
|
395
335
|
personal unpublished use, never for anything released.
|
|
396
336
|
|
|
397
|
-
## Repository and release setup
|
|
398
|
-
|
|
399
|
-
Plan and rationale: [`docs/publishing.md`](docs/publishing.md).
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
- [ ] Immediately before making the repo public: run `trufflehog git file://.
|
|
411
|
-
--only-verified` over the full local history, confirm clean. One time, local,
|
|
412
|
-
never a hook or a job.
|
|
413
|
-
- [ ] Create `github.com/Rovetown/mdcompose` and push. Enable private
|
|
414
|
-
vulnerability reporting (the CODE_OF_CONDUCT contact points at it), Dependabot
|
|
415
|
-
alerts, and Dependabot security updates.
|
|
416
|
-
- [ ] Confirm whether `Rovetown` is a personal account or an organisation: an
|
|
417
|
-
organisation needs a free `GITLEAKS_LICENSE` key for `gitleaks-action`, a
|
|
418
|
-
personal account does not.
|
|
419
|
-
- [ ] Branch protection on `main`: require `all-green`, `supply chain / *`, and
|
|
420
|
-
`CodeQL`; require one review; require the branch up to date; require linear
|
|
421
|
-
history; block force-push. Allow the `bump.yml` token identity to bypass the
|
|
422
|
-
push restriction.
|
|
423
|
-
- [ ] Create a fine-grained PAT (or GitHub App token) with `contents: write`,
|
|
424
|
-
store as the `RELEASE_TOKEN` secret; `bump.yml` uses it so the tag push
|
|
425
|
-
triggers `release.yml` and the bump commit clears branch protection.
|
|
426
|
-
- [ ] Register a PyPI pending publisher for `mdcompose` (owner `Rovetown`,
|
|
427
|
-
repo `mdcompose`, workflow `release.yml`, environment `pypi`), and the same
|
|
428
|
-
on TestPyPI.
|
|
429
|
-
- [ ] Add GitHub Actions environments `pypi` (required reviewer) and `testpypi`.
|
|
430
|
-
- [ ] Enable `pre-commit.ci` on the repo and delete the `hooks` job from
|
|
431
|
-
`ci.yml` (run one, not both).
|
|
432
|
-
|
|
433
|
-
Post-repo, at the release that earns it:
|
|
434
|
-
|
|
435
|
-
- [x] Move the `Development Status` classifier off `3 - Alpha`. Done: moved to
|
|
436
|
-
`4 - Beta` (see the `Next` section above for the reasoning).
|
|
437
|
-
- [ ] Add a new Python to the CI matrix by hand when its first release
|
|
438
|
-
candidate lands (annual, not worth a regex manager).
|
|
439
|
-
- [ ] Point `platform.ONEDRIVE_HELP_URL` (`https://mdcompose.dev/onedrive`) at a
|
|
440
|
-
real how-to-exclude video before the first release.
|
|
441
|
-
- [ ] Wire the CodSpeed `benchmarks` job once the repo is up (it is written,
|
|
442
|
-
just commented).
|
|
443
|
-
- [ ] Optional: write up the OneDrive warning in [`docs/core-contract.md`](docs/core-contract.md) if the
|
|
444
|
-
project keeps that discipline post-v1. Implemented directly for now.
|
|
445
|
-
|
|
446
|
-
## Roadmap (later)
|
|
447
|
-
|
|
448
|
-
Everything here is a later, larger effort. The codebase-wide quality pass is
|
|
337
|
+
## Repository and release setup
|
|
338
|
+
|
|
339
|
+
Done, 2026-09-10. Plan and rationale: [`docs/publishing.md`](docs/publishing.md).
|
|
340
|
+
Repo public, ruleset active, `RELEASE_TOKEN` and both Actions environments
|
|
341
|
+
exist, both trusted publishers registered, `pre-commit.ci` enabled. Nothing
|
|
342
|
+
open here; live remaining work is in `Next` at the top of this file.
|
|
343
|
+
|
|
344
|
+
## Roadmap (later, deferred)
|
|
345
|
+
|
|
346
|
+
This section is TODO the same as `Next`, just longer-horizon: each item below
|
|
347
|
+
is real backlog, not idle brainstorming, and stays here until it is either
|
|
348
|
+
scoped into an OpenSpec change or explicitly dropped. The codebase-wide
|
|
349
|
+
quality pass is
|
|
449
350
|
done: strict typing enforced in CI, a ratcheting coverage floor with its own
|
|
450
351
|
job, a benchmark suite with a recorded baseline, the file-I/O hardening review
|
|
451
352
|
and parser fuzzing. `platform.py` and the interactive questionary pickers in
|
|
@@ -475,6 +376,10 @@ having a real backing plan for, not yet an OpenSpec change.
|
|
|
475
376
|
reuses the existing core rather than forking it, the standing bar for
|
|
476
377
|
everything in this Roadmap section.
|
|
477
378
|
|
|
379
|
+
### Third-party integrations
|
|
380
|
+
|
|
381
|
+
- [ ] Research and, where practical, build integrations that let a user manage their content and snippet library without leaving their usual environment. Candidates are a VS Code extension or a JetBrains plugin; the TUI is tracked separately below. Before building anything new, evaluate whether extending an existing tool in this space is a better use of effort than duplicating config-sync mechanics that are already solved, so the focus stays on the snippet library and composition workflow that is not well covered elsewhere.
|
|
382
|
+
|
|
478
383
|
### TUI (terminal user interface)
|
|
479
384
|
|
|
480
385
|
Deferred branch. Not needed to make the CLI read well, which it already does;
|
|
@@ -527,10 +432,6 @@ own library one at a time from inside the TUI.
|
|
|
527
432
|
- [ ] Revisit once the TUI itself exists, since this is an additional TUI-only
|
|
528
433
|
caller of the library machinery, not a reason to build the TUI sooner.
|
|
529
434
|
|
|
530
|
-
### Third-party integrations
|
|
531
|
-
|
|
532
|
-
- [ ] Research and, where practical, build integrations that let a user manage their content and snippet library without leaving their usual environment. Candidates are a VS Code extension or a JetBrains plugin; the TUI is tracked separately above. Before building anything new, evaluate whether extending an existing tool in this space is a better use of effort than duplicating config-sync mechanics that are already solved, so the focus stays on the snippet library and composition workflow that is not well covered elsewhere.
|
|
533
|
-
|
|
534
435
|
### Additional language implementations
|
|
535
436
|
|
|
536
437
|
- [ ] Once v1 behavior is stable and [`docs/core-contract.md`](docs/core-contract.md) is complete, port the CLI to one or more additional languages. Node.js/TypeScript, Go and Rust are the likely candidates. Broader than the zero-install binary below: this is about giving people who prefer not to touch Python a native option, not only about removing a runtime dependency. The contract exists so a port reimplements a specification rather than translating Python.
|
|
@@ -539,10 +440,6 @@ own library one at a time from inside the TUI.
|
|
|
539
440
|
|
|
540
441
|
- [ ] Once behavior and file formats are stable, evaluate a zero-install path so end users need no Python. Candidates are PyInstaller or Nuitka, both trading binary size for no runtime dependency. If a port lands in Go or Rust, prefer shipping the binary from that instead: smaller static binaries, better cross-compilation, and a natural fit for Homebrew, Scoop, WinGet or an install script.
|
|
541
442
|
|
|
542
|
-
### Final naming verification before public release
|
|
543
|
-
|
|
544
|
-
- [ ] `mdcompose` was checked by search only. Before claiming it, run the exact checks: confirm `https://pypi.org/project/mdcompose/` returns 404, confirm `npm view mdcompose` errors, and register the GitHub repository before someone else does.
|
|
545
|
-
|
|
546
443
|
### Competitive landscape follow-up
|
|
547
444
|
|
|
548
445
|
- [ ] Periodically revisit how the tools listed above evolve, to confirm this project's differentiation stays genuine rather than duplicated.
|
|
@@ -39,6 +39,28 @@ No substitution syntax is reserved either, deliberately -- a snippet containing
|
|
|
39
39
|
The library itself is a flat directory of these files -- no index, no cache, no database.
|
|
40
40
|
mdcompose never writes anything else into it and never touches an entry it did not create (a `.git` directory, a stray subdirectory, a file with another extension), which is what lets you keep the library under version control without the tool fighting you.
|
|
41
41
|
|
|
42
|
+
## Skill frontmatter
|
|
43
|
+
|
|
44
|
+
A skill is a second library, entirely separate from the snippet library, in the same format: one markdown file per skill, optional YAML frontmatter then body, filename minus `.md` as the id.
|
|
45
|
+
Configured the same way too -- `skill_library_path` in the global config, defaulting to a `skills` sibling of the snippet library's own default directory.
|
|
46
|
+
|
|
47
|
+
| Field | Type | Meaning |
|
|
48
|
+
| ----- | ---- | ------- |
|
|
49
|
+
| `title` | string | shown to a person; the id is used when absent |
|
|
50
|
+
| `description` | string | one line of help, and what becomes the composed file's frontmatter description |
|
|
51
|
+
| `tags` | list of strings | for filtering |
|
|
52
|
+
| `stack_signals` | list of strings | filenames suggesting relevance |
|
|
53
|
+
| `category` | string | a grouping label in the picker |
|
|
54
|
+
|
|
55
|
+
There is no `applies_to` and no `order`: a skill is never split between AGENTS.md and CLAUDE.md the way a snippet can be, and its body is never concatenated with another skill's the way several snippet bodies share one block, so neither field has anything to do.
|
|
56
|
+
|
|
57
|
+
`init` offers the skill library as a second, independently optional checkbox picker alongside the snippet picker.
|
|
58
|
+
Each selected skill becomes its own file at `.claude/skills/<id>/SKILL.md`: generated `name`/`description` frontmatter first, then a managed block holding the skill's body, hashed and drift-checked the same way AGENTS.md's block is.
|
|
59
|
+
The frontmatter is regenerated from the library on every run, even when only the description changed and the block's content did not -- unlike a managed block's surroundings, it is not content mdcompose leaves alone, since Claude Code needs it to track the library, not a stale copy.
|
|
60
|
+
|
|
61
|
+
Deselecting a skill deletes its file outright, and its now-empty `.claude/skills/<id>/` directory with it, rather than leaving an empty block the way AGENTS.md would: an empty `SKILL.md` means nothing to a skill-loading agent.
|
|
62
|
+
An empty or unconfigured skill library never blocks `init`, and the reverse holds too -- an empty snippet library does not stop skills from composing.
|
|
63
|
+
|
|
42
64
|
## Drift detection: normalization and hashing
|
|
43
65
|
|
|
44
66
|
A managed block's hash has to mean the same thing on Windows, under WSL, and on Linux, or a committed lock file could never be trusted across a team.
|
|
@@ -129,17 +151,14 @@ None of them do these, which is the actual reason to reach for mdcompose instead
|
|
|
129
151
|
Composing from a repository already means trusting its authors the way you trust their build scripts; mdcompose adds no second channel on top of that.
|
|
130
152
|
- **Every interactive prompt has a flag.** Nothing here requires a human at a keyboard: `init --reapply`, `import --save-as-snippet`, and their siblings mean every command that can prompt can also run unattended, with the exit code contract above making the result scriptable in CI.
|
|
131
153
|
|
|
132
|
-
One more
|
|
133
|
-
A skill file (Claude Code Skills, or whatever equivalent another tool adds) is
|
|
134
|
-
|
|
154
|
+
One more: none of the three alternatives above appear to be aiming past agent *instructions*.
|
|
155
|
+
A skill file (Claude Code Skills, or whatever equivalent another tool adds) is snippet-shaped -- frontmatter, then a body -- so mdcompose composes those the same way it composes AGENTS.md and CLAUDE.md, as a second, independent library; see Skill frontmatter above.
|
|
156
|
+
A skill bundled with an accompanying script is not yet supported, since the library and manifest model here is single-file-per-item throughout.
|
|
135
157
|
|
|
136
158
|
## Planned
|
|
137
159
|
|
|
138
160
|
Larger efforts, tracked in [TODO.md](../TODO.md) under Roadmap, not needed for `0.1.0` and not yet built:
|
|
139
161
|
|
|
140
|
-
- **Composing agent skills, not just AGENTS.md and CLAUDE.md.** A skill file (Claude Code Skills, or an equivalent another tool adds) is already snippet-shaped, so extending composition to skills would let a personal library of them be curated per project instead of an agent loading every skill unconditionally.
|
|
141
|
-
Fewer unneeded skills loaded is context an agent never has to spend, which makes this a token-efficiency argument, not only an organizational one.
|
|
142
|
-
Not scoped: a second composed-file family has to prove it reuses the existing core rather than forking it, the same bar every item below has to clear.
|
|
143
162
|
- **A TUI**, likely on Textual, as a second adapter over the same core: browse and filter the library, tick snippets for a project, see per-file drift, review a diff before writing.
|
|
144
163
|
The CLI stays the interface that has to work unattended -- exit codes, the stdout/stderr split, `--json`, `--quiet` are CLI concepts with no TUI equivalent, so the TUI is additive, never the only way to do something.
|
|
145
164
|
- **A shared, browsable community snippet library, gated behind the TUI.** Concept borrowed from the skill marketplaces appearing around Claude Code Skills: a public GitHub repository as the source, crawled and indexed through GitHub's API into a locally cached, fuzzy-searchable list of community-contributed snippets, browsed and pulled into your own library one at a time from inside the TUI.
|