dotmd-parser 0.2.1__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. {dotmd_parser-0.2.1/src/dotmd_parser.egg-info → dotmd_parser-0.5.0}/PKG-INFO +130 -7
  2. {dotmd_parser-0.2.1 → dotmd_parser-0.5.0}/README.md +128 -5
  3. {dotmd_parser-0.2.1 → dotmd_parser-0.5.0}/pyproject.toml +6 -2
  4. dotmd_parser-0.5.0/src/dotmd_parser/__init__.py +115 -0
  5. dotmd_parser-0.5.0/src/dotmd_parser/analyze.py +676 -0
  6. dotmd_parser-0.5.0/src/dotmd_parser/cli.py +435 -0
  7. dotmd_parser-0.5.0/src/dotmd_parser/digest.py +193 -0
  8. dotmd_parser-0.5.0/src/dotmd_parser/index.py +300 -0
  9. dotmd_parser-0.5.0/src/dotmd_parser/inventory.py +299 -0
  10. {dotmd_parser-0.2.1 → dotmd_parser-0.5.0}/src/dotmd_parser/parser.py +108 -39
  11. dotmd_parser-0.5.0/src/dotmd_parser/templates/SKILL.md +212 -0
  12. dotmd_parser-0.5.0/src/dotmd_parser/templates/__init__.py +1 -0
  13. dotmd_parser-0.5.0/src/dotmd_parser/templates/prompts/__init__.py +1 -0
  14. dotmd_parser-0.5.0/src/dotmd_parser/templates/prompts/analyze-dependencies.md +42 -0
  15. {dotmd_parser-0.2.1 → dotmd_parser-0.5.0/src/dotmd_parser.egg-info}/PKG-INFO +130 -7
  16. dotmd_parser-0.5.0/src/dotmd_parser.egg-info/SOURCES.txt +27 -0
  17. dotmd_parser-0.5.0/tests/test_analyze.py +254 -0
  18. dotmd_parser-0.5.0/tests/test_cost_estimate.py +148 -0
  19. dotmd_parser-0.5.0/tests/test_empty_warnings.py +116 -0
  20. dotmd_parser-0.5.0/tests/test_host_agent_plan.py +174 -0
  21. dotmd_parser-0.5.0/tests/test_index_scope.py +195 -0
  22. dotmd_parser-0.5.0/tests/test_inventory.py +195 -0
  23. {dotmd_parser-0.2.1 → dotmd_parser-0.5.0}/tests/test_parser.py +98 -97
  24. dotmd_parser-0.5.0/tests/test_skill_integration.py +315 -0
  25. dotmd_parser-0.2.1/src/dotmd_parser/__init__.py +0 -33
  26. dotmd_parser-0.2.1/src/dotmd_parser.egg-info/SOURCES.txt +0 -11
  27. {dotmd_parser-0.2.1 → dotmd_parser-0.5.0}/LICENSE +0 -0
  28. {dotmd_parser-0.2.1 → dotmd_parser-0.5.0}/setup.cfg +0 -0
  29. {dotmd_parser-0.2.1 → dotmd_parser-0.5.0}/src/dotmd_parser.egg-info/dependency_links.txt +0 -0
  30. {dotmd_parser-0.2.1 → dotmd_parser-0.5.0}/src/dotmd_parser.egg-info/entry_points.txt +0 -0
  31. {dotmd_parser-0.2.1 → dotmd_parser-0.5.0}/src/dotmd_parser.egg-info/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dotmd-parser
3
- Version: 0.2.1
4
- Summary: Dependency graph parser for .md skill files — parse @include/@delegate/@ref directives, build graphs, and resolve templates for AI agent prompt engineering
3
+ Version: 0.5.0
4
+ Summary: Dependency graph parser and AI analyzer for .md skill files — parse @include/@delegate/@ref directives, build graphs, resolve templates, and detect implicit dependencies via Claude for AI agent prompt engineering
5
5
  Author: dotmd-projects
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/dotmd-projects/dotmd-parser
@@ -57,6 +57,8 @@ As AI agent projects grow, `.md` files start referencing each other via `@includ
57
57
  | Expand `@include` to final text | Copy-paste by hand | `resolve("SKILL.md", variables={...})` — recursive expansion |
58
58
  | Find unresolved `{{variables}}` | `grep "{{" *.md` — noisy, no dedup | Deduplicated list per node and after expansion |
59
59
  | Missing file detection | Runtime failure | Warnings at parse time with exact paths |
60
+ | Detect **implicit** deps (no directives yet) | Read every file, draw by hand | `analyze` subcommand asks Claude, emits `@include` / `deps.yml` |
61
+ | Feed Claude Code with compact context | Read 20 files × 5 KB each | `digest` outputs one ~2 KB summary; `.claude/dotmd-index.json` cache |
60
62
 
61
63
  ## Installation
62
64
 
@@ -170,15 +172,136 @@ from dotmd_parser import parse_directives, parse_read_refs, parse_placeholders,
170
172
 
171
173
  ## CLI
172
174
 
175
+ `dotmd-parser` ships with sub-commands tuned for Claude Code and CI use. Running `dotmd-parser <path>` with no sub-command still works and falls through to `show` for backward compatibility.
176
+
177
+ | Command | Purpose |
178
+ |---|---|
179
+ | `dotmd-parser inventory <path>` | **API-free**: extension counts, markdown/binary ratio, largest files |
180
+ | `dotmd-parser index <path>` | Build and save `.claude/dotmd-index.json` |
181
+ | `dotmd-parser index <path> --scope <subdir>` | Incrementally re-index one subfolder, merge into the existing index |
182
+ | `dotmd-parser check <path>` | Exit non-zero on cycles / missing refs (CI-friendly) |
183
+ | `dotmd-parser affects <path> <file>` | Reverse dependencies of `<file>` |
184
+ | `dotmd-parser deps <path> <file>` | Direct dependencies of `<file>` |
185
+ | `dotmd-parser digest <path>` | Token-efficient text summary for LLM context |
186
+ | `dotmd-parser tree <path>` | ASCII dependency tree |
187
+ | `dotmd-parser resolve <file> [--var k=v]` | Recursively expand `@include` |
188
+ | `dotmd-parser analyze <path>` | AI dependency detection (requires `ANTHROPIC_API_KEY`) |
189
+ | `dotmd-parser analyze <path> --dry-run` | **API-free**: estimate tokens and USD cost |
190
+ | `dotmd-parser analyze <path> --plan` | **API-free**: emit a host-agent prompt pack for Claude Code to execute |
191
+ | `dotmd-parser analyze <path> --apply-from <json>` | Apply a pre-computed analysis JSON |
192
+ | `dotmd-parser show <path>` | Summary + full JSON graph (legacy default) |
193
+
194
+ ```bash
195
+ # Typical Claude Code workflow
196
+ dotmd-parser inventory ./my-skill/ # start here if you've never seen the folder
197
+ dotmd-parser index ./my-skill/ # one-off; cached until files change
198
+ dotmd-parser digest ./my-skill/ # compact summary for the LLM
199
+ dotmd-parser affects ./my-skill/ shared/role.md
200
+ ```
201
+
202
+ ## `analyze` — AI-assisted dependency detection
203
+
204
+ Use when a folder of markdown has **no explicit directives yet**. `analyze`
205
+ asks Claude to infer dependencies and can apply the result in one step:
206
+
173
207
  ```bash
174
- # Installed as a command
175
- dotmd-parser ./my-skill/
208
+ export ANTHROPIC_API_KEY=... # or put it in ./.env
209
+
210
+ dotmd-parser analyze ./docs/ # runs the proposal (uses API)
211
+ dotmd-parser analyze ./docs/ --apply # inject @include, write deps.yml
212
+ dotmd-parser analyze ./docs/ --json # machine-readable
213
+ dotmd-parser analyze ./docs/ --ext md --ext pdf --ext docx
214
+ ```
215
+
216
+ ### No-API-key workflows
217
+
218
+ ```bash
219
+ # Estimate cost before spending any API credit
220
+ dotmd-parser analyze ./docs/ --dry-run
221
+
222
+ # Delegate the analysis to Claude Code itself — no API key needed
223
+ dotmd-parser analyze ./docs/ --plan > plan.md
224
+ # 1. Claude Code reads plan.md and executes the task locally
225
+ # 2. It writes the result to analysis.json
226
+ # 3. Apply it:
227
+ dotmd-parser analyze ./docs/ --apply-from analysis.json
228
+ ```
229
+
230
+ - Text files (`.md`, `.txt`, etc.) get `@include` lines prepended.
231
+ - Binary sources (`.pdf`, `.docx`) are recorded in `deps.yml` — they can't
232
+ be edited in-place, so the parser reads them from there.
233
+ - PDF / DOCX support is optional: `pip install pdfplumber python-docx`.
234
+ - Re-run with `--apply` over time as the repo grows — existing directives
235
+ are preserved.
236
+
237
+ The bundled prompt lives at
238
+ `src/dotmd_parser/templates/prompts/analyze-dependencies.md` and is shipped
239
+ inside the wheel; no network access is needed except for the Claude API
240
+ call itself.
241
+
242
+ ### Programmatic use
243
+
244
+ ```python
245
+ from dotmd_parser import analyze_dependencies, apply_analysis
176
246
 
177
- # Or via Python module
178
- python -m dotmd_parser.parser ./my-skill/
247
+ proposal = analyze_dependencies("./docs/")
248
+ print(proposal["edges"])
249
+ apply_analysis("./docs/", proposal)
250
+ ```
251
+
252
+ For offline tests, pass a `caller=...` kwarg that returns a stub JSON string.
253
+
254
+ ## Claude Code Skill integration
255
+
256
+ A ready-to-use Claude Code Skill is bundled with the package at
257
+ `src/dotmd_parser/templates/SKILL.md`. Three install paths:
258
+
259
+ ```bash
260
+ # via pip — installs the CLI and drops SKILL.md into the current project
261
+ pip install dotmd-parser
262
+ dotmd-parser init .
263
+
264
+ # via Release archive — no pip required
265
+ mkdir -p .claude/skills
266
+ curl -L https://github.com/dotmd-projects/dotmd-parser/releases/latest/download/skill.tar.gz \
267
+ | tar -xz -C .claude/skills/
268
+
269
+ # manual
270
+ cp src/dotmd_parser/templates/SKILL.md \
271
+ /path/to/project/.claude/skills/dotmd-parser/
272
+ ```
273
+
274
+ Once installed, Claude will consult the skill whenever it encounters
275
+ `SKILL.md`, `deps.yml`, a `.claude/skills/` tree, or is asked about
276
+ dependencies of a markdown file.
277
+
278
+ **Why this saves tokens.** Without the skill, Claude typically `grep -r`s
279
+ for `@include`/`@ref` and `cat`s every referenced file to trace a graph.
280
+ With the skill it reads `.claude/dotmd-index.json` (compact, relative paths,
281
+ first-paragraph descriptions) or the `digest` output once, then queries
282
+ `affects` / `deps` by name — never touching the raw markdown until an edit
283
+ is actually needed.
284
+
285
+ ### Auto-refresh via Hook (optional)
286
+
287
+ Add to `~/.claude/settings.json` to keep `.claude/dotmd-index.json` fresh
288
+ after every markdown edit:
289
+
290
+ ```json
291
+ {
292
+ "hooks": {
293
+ "PostToolUse": [
294
+ {
295
+ "matcher": "Edit|Write",
296
+ "command": "dotmd-parser index \"$CLAUDE_PROJECT_DIR\" >/dev/null 2>&1 || true"
297
+ }
298
+ ]
299
+ }
300
+ }
179
301
  ```
180
302
 
181
- Outputs a human-readable summary followed by the full JSON graph.
303
+ The command is idempotent (SHA-256 cache) and exits fast when nothing
304
+ changed.
182
305
 
183
306
  ## Development
184
307
 
@@ -30,6 +30,8 @@ As AI agent projects grow, `.md` files start referencing each other via `@includ
30
30
  | Expand `@include` to final text | Copy-paste by hand | `resolve("SKILL.md", variables={...})` — recursive expansion |
31
31
  | Find unresolved `{{variables}}` | `grep "{{" *.md` — noisy, no dedup | Deduplicated list per node and after expansion |
32
32
  | Missing file detection | Runtime failure | Warnings at parse time with exact paths |
33
+ | Detect **implicit** deps (no directives yet) | Read every file, draw by hand | `analyze` subcommand asks Claude, emits `@include` / `deps.yml` |
34
+ | Feed Claude Code with compact context | Read 20 files × 5 KB each | `digest` outputs one ~2 KB summary; `.claude/dotmd-index.json` cache |
33
35
 
34
36
  ## Installation
35
37
 
@@ -143,15 +145,136 @@ from dotmd_parser import parse_directives, parse_read_refs, parse_placeholders,
143
145
 
144
146
  ## CLI
145
147
 
148
+ `dotmd-parser` ships with sub-commands tuned for Claude Code and CI use. Running `dotmd-parser <path>` with no sub-command still works and falls through to `show` for backward compatibility.
149
+
150
+ | Command | Purpose |
151
+ |---|---|
152
+ | `dotmd-parser inventory <path>` | **API-free**: extension counts, markdown/binary ratio, largest files |
153
+ | `dotmd-parser index <path>` | Build and save `.claude/dotmd-index.json` |
154
+ | `dotmd-parser index <path> --scope <subdir>` | Incrementally re-index one subfolder, merge into the existing index |
155
+ | `dotmd-parser check <path>` | Exit non-zero on cycles / missing refs (CI-friendly) |
156
+ | `dotmd-parser affects <path> <file>` | Reverse dependencies of `<file>` |
157
+ | `dotmd-parser deps <path> <file>` | Direct dependencies of `<file>` |
158
+ | `dotmd-parser digest <path>` | Token-efficient text summary for LLM context |
159
+ | `dotmd-parser tree <path>` | ASCII dependency tree |
160
+ | `dotmd-parser resolve <file> [--var k=v]` | Recursively expand `@include` |
161
+ | `dotmd-parser analyze <path>` | AI dependency detection (requires `ANTHROPIC_API_KEY`) |
162
+ | `dotmd-parser analyze <path> --dry-run` | **API-free**: estimate tokens and USD cost |
163
+ | `dotmd-parser analyze <path> --plan` | **API-free**: emit a host-agent prompt pack for Claude Code to execute |
164
+ | `dotmd-parser analyze <path> --apply-from <json>` | Apply a pre-computed analysis JSON |
165
+ | `dotmd-parser show <path>` | Summary + full JSON graph (legacy default) |
166
+
167
+ ```bash
168
+ # Typical Claude Code workflow
169
+ dotmd-parser inventory ./my-skill/ # start here if you've never seen the folder
170
+ dotmd-parser index ./my-skill/ # one-off; cached until files change
171
+ dotmd-parser digest ./my-skill/ # compact summary for the LLM
172
+ dotmd-parser affects ./my-skill/ shared/role.md
173
+ ```
174
+
175
+ ## `analyze` — AI-assisted dependency detection
176
+
177
+ Use when a folder of markdown has **no explicit directives yet**. `analyze`
178
+ asks Claude to infer dependencies and can apply the result in one step:
179
+
146
180
  ```bash
147
- # Installed as a command
148
- dotmd-parser ./my-skill/
181
+ export ANTHROPIC_API_KEY=... # or put it in ./.env
182
+
183
+ dotmd-parser analyze ./docs/ # runs the proposal (uses API)
184
+ dotmd-parser analyze ./docs/ --apply # inject @include, write deps.yml
185
+ dotmd-parser analyze ./docs/ --json # machine-readable
186
+ dotmd-parser analyze ./docs/ --ext md --ext pdf --ext docx
187
+ ```
188
+
189
+ ### No-API-key workflows
190
+
191
+ ```bash
192
+ # Estimate cost before spending any API credit
193
+ dotmd-parser analyze ./docs/ --dry-run
194
+
195
+ # Delegate the analysis to Claude Code itself — no API key needed
196
+ dotmd-parser analyze ./docs/ --plan > plan.md
197
+ # 1. Claude Code reads plan.md and executes the task locally
198
+ # 2. It writes the result to analysis.json
199
+ # 3. Apply it:
200
+ dotmd-parser analyze ./docs/ --apply-from analysis.json
201
+ ```
202
+
203
+ - Text files (`.md`, `.txt`, etc.) get `@include` lines prepended.
204
+ - Binary sources (`.pdf`, `.docx`) are recorded in `deps.yml` — they can't
205
+ be edited in-place, so the parser reads them from there.
206
+ - PDF / DOCX support is optional: `pip install pdfplumber python-docx`.
207
+ - Re-run with `--apply` over time as the repo grows — existing directives
208
+ are preserved.
209
+
210
+ The bundled prompt lives at
211
+ `src/dotmd_parser/templates/prompts/analyze-dependencies.md` and is shipped
212
+ inside the wheel; no network access is needed except for the Claude API
213
+ call itself.
214
+
215
+ ### Programmatic use
216
+
217
+ ```python
218
+ from dotmd_parser import analyze_dependencies, apply_analysis
149
219
 
150
- # Or via Python module
151
- python -m dotmd_parser.parser ./my-skill/
220
+ proposal = analyze_dependencies("./docs/")
221
+ print(proposal["edges"])
222
+ apply_analysis("./docs/", proposal)
223
+ ```
224
+
225
+ For offline tests, pass a `caller=...` kwarg that returns a stub JSON string.
226
+
227
+ ## Claude Code Skill integration
228
+
229
+ A ready-to-use Claude Code Skill is bundled with the package at
230
+ `src/dotmd_parser/templates/SKILL.md`. Three install paths:
231
+
232
+ ```bash
233
+ # via pip — installs the CLI and drops SKILL.md into the current project
234
+ pip install dotmd-parser
235
+ dotmd-parser init .
236
+
237
+ # via Release archive — no pip required
238
+ mkdir -p .claude/skills
239
+ curl -L https://github.com/dotmd-projects/dotmd-parser/releases/latest/download/skill.tar.gz \
240
+ | tar -xz -C .claude/skills/
241
+
242
+ # manual
243
+ cp src/dotmd_parser/templates/SKILL.md \
244
+ /path/to/project/.claude/skills/dotmd-parser/
245
+ ```
246
+
247
+ Once installed, Claude will consult the skill whenever it encounters
248
+ `SKILL.md`, `deps.yml`, a `.claude/skills/` tree, or is asked about
249
+ dependencies of a markdown file.
250
+
251
+ **Why this saves tokens.** Without the skill, Claude typically `grep -r`s
252
+ for `@include`/`@ref` and `cat`s every referenced file to trace a graph.
253
+ With the skill it reads `.claude/dotmd-index.json` (compact, relative paths,
254
+ first-paragraph descriptions) or the `digest` output once, then queries
255
+ `affects` / `deps` by name — never touching the raw markdown until an edit
256
+ is actually needed.
257
+
258
+ ### Auto-refresh via Hook (optional)
259
+
260
+ Add to `~/.claude/settings.json` to keep `.claude/dotmd-index.json` fresh
261
+ after every markdown edit:
262
+
263
+ ```json
264
+ {
265
+ "hooks": {
266
+ "PostToolUse": [
267
+ {
268
+ "matcher": "Edit|Write",
269
+ "command": "dotmd-parser index \"$CLAUDE_PROJECT_DIR\" >/dev/null 2>&1 || true"
270
+ }
271
+ ]
272
+ }
273
+ }
152
274
  ```
153
275
 
154
- Outputs a human-readable summary followed by the full JSON graph.
276
+ The command is idempotent (SHA-256 cache) and exits fast when nothing
277
+ changed.
155
278
 
156
279
  ## Development
157
280
 
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "dotmd-parser"
3
- version = "0.2.1"
4
- description = "Dependency graph parser for .md skill files — parse @include/@delegate/@ref directives, build graphs, and resolve templates for AI agent prompt engineering"
3
+ version = "0.5.0"
4
+ description = "Dependency graph parser and AI analyzer for .md skill files — parse @include/@delegate/@ref directives, build graphs, resolve templates, and detect implicit dependencies via Claude for AI agent prompt engineering"
5
5
  requires-python = ">=3.10"
6
6
  license = "MIT"
7
7
  readme = "README.md"
@@ -50,5 +50,9 @@ build-backend = "setuptools.build_meta"
50
50
  [tool.setuptools.packages.find]
51
51
  where = ["src"]
52
52
 
53
+ [tool.setuptools.package-data]
54
+ "dotmd_parser.templates" = ["*.md"]
55
+ "dotmd_parser.templates.prompts" = ["*.md"]
56
+
53
57
  [tool.pytest.ini_options]
54
58
  testpaths = ["tests"]
@@ -0,0 +1,115 @@
1
+ """
2
+ dotmd-parser — Dependency graph parser for .md skill files.
3
+
4
+ Parse @include/@delegate/@ref directives, build dependency graphs, resolve
5
+ templates, and produce a token-efficient on-disk index for AI agents.
6
+
7
+ API:
8
+ from dotmd_parser import build_graph, resolve, dependents_of, summary
9
+ from dotmd_parser import build_index, save_index, load_index
10
+ from dotmd_parser import digest, tree, affects
11
+ """
12
+
13
+ __version__ = "0.5.0"
14
+
15
+ from dotmd_parser.parser import (
16
+ build_graph,
17
+ resolve,
18
+ dependents_of,
19
+ parse_directives,
20
+ parse_read_refs,
21
+ parse_placeholders,
22
+ parse_description,
23
+ parse_deps_yml,
24
+ hash_content,
25
+ summary,
26
+ )
27
+ from dotmd_parser.index import (
28
+ build_index,
29
+ build_scoped_index,
30
+ compact_graph,
31
+ merge_index,
32
+ save_index,
33
+ load_index,
34
+ needs_rebuild,
35
+ changed_files,
36
+ default_index_path,
37
+ )
38
+ from dotmd_parser.digest import (
39
+ digest,
40
+ tree,
41
+ affects,
42
+ deps_of,
43
+ )
44
+ from dotmd_parser.analyze import (
45
+ analyze_dependencies,
46
+ apply_analysis,
47
+ apply_analysis_from_file,
48
+ estimate_cost,
49
+ format_cost_estimate,
50
+ format_host_agent_plan,
51
+ generate_directives,
52
+ format_proposal,
53
+ scan_documents,
54
+ save_deps_yml,
55
+ load_deps_yml,
56
+ MODEL_PRICING,
57
+ )
58
+ from dotmd_parser.inventory import (
59
+ inventory,
60
+ format_inventory,
61
+ suggest_next_command,
62
+ TEXT_EXTENSIONS,
63
+ BINARY_EXTENSIONS,
64
+ MARKDOWN_EXTENSIONS,
65
+ )
66
+
67
+ __all__ = [
68
+ "__version__",
69
+ # parser
70
+ "build_graph",
71
+ "resolve",
72
+ "dependents_of",
73
+ "parse_directives",
74
+ "parse_read_refs",
75
+ "parse_placeholders",
76
+ "parse_description",
77
+ "parse_deps_yml",
78
+ "hash_content",
79
+ "summary",
80
+ # index
81
+ "build_index",
82
+ "build_scoped_index",
83
+ "compact_graph",
84
+ "merge_index",
85
+ "save_index",
86
+ "load_index",
87
+ "needs_rebuild",
88
+ "changed_files",
89
+ "default_index_path",
90
+ # digest
91
+ "digest",
92
+ "tree",
93
+ "affects",
94
+ "deps_of",
95
+ # analyze
96
+ "analyze_dependencies",
97
+ "apply_analysis",
98
+ "apply_analysis_from_file",
99
+ "estimate_cost",
100
+ "format_cost_estimate",
101
+ "format_host_agent_plan",
102
+ "generate_directives",
103
+ "format_proposal",
104
+ "scan_documents",
105
+ "save_deps_yml",
106
+ "load_deps_yml",
107
+ "MODEL_PRICING",
108
+ # inventory
109
+ "inventory",
110
+ "format_inventory",
111
+ "suggest_next_command",
112
+ "TEXT_EXTENSIONS",
113
+ "BINARY_EXTENSIONS",
114
+ "MARKDOWN_EXTENSIONS",
115
+ ]