dotmd-parser 0.2.0__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 (34) hide show
  1. dotmd_parser-0.5.0/PKG-INFO +318 -0
  2. dotmd_parser-0.5.0/README.md +291 -0
  3. {dotmd_parser-0.2.0 → 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.0 → 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.5.0/src/dotmd_parser.egg-info/PKG-INFO +318 -0
  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.0 → 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.0/PKG-INFO +0 -153
  26. dotmd_parser-0.2.0/README.md +0 -126
  27. dotmd_parser-0.2.0/src/dotmd_parser/__init__.py +0 -33
  28. dotmd_parser-0.2.0/src/dotmd_parser.egg-info/PKG-INFO +0 -153
  29. dotmd_parser-0.2.0/src/dotmd_parser.egg-info/SOURCES.txt +0 -11
  30. {dotmd_parser-0.2.0 → dotmd_parser-0.5.0}/LICENSE +0 -0
  31. {dotmd_parser-0.2.0 → dotmd_parser-0.5.0}/setup.cfg +0 -0
  32. {dotmd_parser-0.2.0 → dotmd_parser-0.5.0}/src/dotmd_parser.egg-info/dependency_links.txt +0 -0
  33. {dotmd_parser-0.2.0 → dotmd_parser-0.5.0}/src/dotmd_parser.egg-info/entry_points.txt +0 -0
  34. {dotmd_parser-0.2.0 → dotmd_parser-0.5.0}/src/dotmd_parser.egg-info/top_level.txt +0 -0
@@ -0,0 +1,318 @@
1
+ Metadata-Version: 2.4
2
+ Name: dotmd-parser
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
+ Author: dotmd-projects
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/dotmd-projects/dotmd-parser
8
+ Project-URL: Repository, https://github.com/dotmd-projects/dotmd-parser
9
+ Project-URL: Issues, https://github.com/dotmd-projects/dotmd-parser/issues
10
+ Keywords: claude-code,ai-agent,skill-management,prompt-engineering,dependency-graph,SKILL.md,markdown,parser,dotmd,llm
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Topic :: Software Development :: Code Generators
21
+ Classifier: Topic :: Text Processing :: Markup :: Markdown
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Dynamic: license-file
27
+
28
+ # dotmd-parser
29
+
30
+ [![PyPI version](https://img.shields.io/pypi/v/dotmd-parser)](https://pypi.org/project/dotmd-parser/)
31
+ [![Python](https://img.shields.io/pypi/pyversions/dotmd-parser)](https://pypi.org/project/dotmd-parser/)
32
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
33
+
34
+ ![dotmd-parser demo](assets/demo.gif)
35
+
36
+ > [日本語版 README はこちら](README.ja.md)
37
+
38
+ Dependency graph parser for `.md` skill files — built for AI agent prompt engineering with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and similar tools.
39
+
40
+ ## Why dotmd-parser?
41
+
42
+ As AI agent projects grow, `.md` files start referencing each other via `@include`, `@delegate`, and `@ref` directives. Without tooling, you're left manually tracing dependencies to answer basic questions:
43
+
44
+ - *"Which files break if I edit `shared/role.md`?"*
45
+ - *"Is there a circular reference hiding in my skill tree?"*
46
+ - *"What `{{variables}}` are still unresolved after expansion?"*
47
+
48
+ **dotmd-parser** solves this by parsing your `.md` files into a dependency graph — automatically detecting directives, runtime references, and template placeholders. One function call gives you the full picture.
49
+
50
+ ## Comparison
51
+
52
+ | Capability | Manual / grep | dotmd-parser |
53
+ |---|---|---|
54
+ | Find `@include` / `@delegate` / `@ref` references | `grep -r "@include"` — flat list, no context | Structured graph with node types and edge metadata |
55
+ | Detect circular references | Hope you notice before the agent loops | Automatic detection with full cycle path in warnings |
56
+ | Reverse dependency ("what breaks?") | Manually trace every file | `dependents_of(graph, "shared/role.md")` — one call |
57
+ | Expand `@include` to final text | Copy-paste by hand | `resolve("SKILL.md", variables={...})` — recursive expansion |
58
+ | Find unresolved `{{variables}}` | `grep "{{" *.md` — noisy, no dedup | Deduplicated list per node and after expansion |
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 |
62
+
63
+ ## Installation
64
+
65
+ ```bash
66
+ pip install dotmd-parser
67
+ ```
68
+
69
+ ## Quick Start
70
+
71
+ ```python
72
+ from dotmd_parser import build_graph, resolve, dependents_of, summary
73
+ ```
74
+
75
+ ### build_graph — Build a dependency graph
76
+
77
+ ```python
78
+ graph = build_graph("./my-skill/")
79
+ # or from a specific file
80
+ graph = build_graph("./my-skill/SKILL.md")
81
+ ```
82
+
83
+ Returns:
84
+
85
+ ```json
86
+ {
87
+ "nodes": [
88
+ {"id": "/abs/path/to/SKILL.md", "type": "skill", "missing": false, "placeholders": []}
89
+ ],
90
+ "edges": [
91
+ {"from": "...", "to": "...", "type": "include", "parallel": false}
92
+ ],
93
+ "warnings": []
94
+ }
95
+ ```
96
+
97
+ **Custom node type mapping:**
98
+
99
+ By default, node types are inferred from path keywords (`agent`, `shared`, `prompt`, `reference`, `asset`, `template`). You can override this with the `type_map` parameter:
100
+
101
+ ```python
102
+ graph = build_graph("./my-skill/", type_map=[
103
+ ("helper", "utility"),
104
+ ("core", "foundation"),
105
+ ])
106
+ ```
107
+
108
+ **deps.yml support:**
109
+
110
+ If a `deps.yml` file exists in the root directory, its dependencies are automatically merged into the graph:
111
+
112
+ ```yaml
113
+ - path: agents/planner.md
114
+ includes:
115
+ - shared/role.md
116
+ - shared/tools.md
117
+ ```
118
+
119
+ ### resolve — Expand @include directives
120
+
121
+ Recursively expands `@include` directives into final text. `@delegate` and `@ref` lines are left as-is.
122
+
123
+ ```python
124
+ result = resolve("./prompts/main.md", variables={"name": "Alice"})
125
+
126
+ print(result["content"]) # Fully expanded text
127
+ print(result["placeholders"]) # Unresolved {{variable}} names
128
+ print(result["warnings"]) # Circular refs, missing files, etc.
129
+ ```
130
+
131
+ ### dependents_of — Reverse dependency query
132
+
133
+ ```python
134
+ # "If I change shared/role.md, what else breaks?"
135
+ affected = dependents_of(graph, "/abs/path/to/shared/role.md")
136
+ ```
137
+
138
+ ### summary — Human-readable overview
139
+
140
+ ```python
141
+ print(summary(graph))
142
+ # Nodes: 5 (agent:1, prompt:1, shared:2, skill:1)
143
+ # Edges: 4 (include:2, ref:1, read-ref:1)
144
+ # Warnings: 0
145
+ # Placeholders: name, role
146
+ ```
147
+
148
+ ## Supported Directives
149
+
150
+ | Directive | Edge type | Expanded by `resolve()`? | Description |
151
+ |---|---|---|---|
152
+ | `@include path/to/file.md` | `include` | Yes | Inline expansion — file content is inserted at this position |
153
+ | `@delegate path/to/agent.md` | `delegate` | No | Agent delegation — recorded in graph, left as-is in output |
154
+ | `@delegate path/to/agent.md --parallel` | `delegate` | No | Parallel delegation with `--parallel` flag |
155
+ | `@ref path/to/file.md` | `ref` | No | Runtime reference — recorded in graph, left as-is in output |
156
+ | `` Read `path/to/file.md` `` | `read-ref` | No | Legacy runtime reference (same as `@ref`, kept for backward compatibility) |
157
+
158
+ ## Utility Functions
159
+
160
+ Lower-level parsing functions are also exported for custom use:
161
+
162
+ ```python
163
+ from dotmd_parser import parse_directives, parse_read_refs, parse_placeholders, parse_deps_yml
164
+ ```
165
+
166
+ | Function | Description |
167
+ |---|---|
168
+ | `parse_directives(content)` | Extract `@include` / `@delegate` / `@ref` directives from text |
169
+ | `parse_read_refs(content)` | Extract legacy `Read`/`See`/list-style `.md` references (deduplicated) |
170
+ | `parse_placeholders(content)` | Extract `{{variable}}` placeholder names (deduplicated) |
171
+ | `parse_deps_yml(content)` | Parse `deps.yml` text into `{path: [includes]}` dict (no PyYAML required) |
172
+
173
+ ## CLI
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
+
207
+ ```bash
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
246
+
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
+ }
301
+ ```
302
+
303
+ The command is idempotent (SHA-256 cache) and exits fast when nothing
304
+ changed.
305
+
306
+ ## Development
307
+
308
+ ```bash
309
+ git clone https://github.com/dotmd-projects/dotmd-parser.git
310
+ cd dotmd-parser
311
+ pip install -e .
312
+ pip install pytest
313
+ pytest tests/ -v
314
+ ```
315
+
316
+ ## License
317
+
318
+ MIT
@@ -0,0 +1,291 @@
1
+ # dotmd-parser
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/dotmd-parser)](https://pypi.org/project/dotmd-parser/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/dotmd-parser)](https://pypi.org/project/dotmd-parser/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+
7
+ ![dotmd-parser demo](assets/demo.gif)
8
+
9
+ > [日本語版 README はこちら](README.ja.md)
10
+
11
+ Dependency graph parser for `.md` skill files — built for AI agent prompt engineering with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and similar tools.
12
+
13
+ ## Why dotmd-parser?
14
+
15
+ As AI agent projects grow, `.md` files start referencing each other via `@include`, `@delegate`, and `@ref` directives. Without tooling, you're left manually tracing dependencies to answer basic questions:
16
+
17
+ - *"Which files break if I edit `shared/role.md`?"*
18
+ - *"Is there a circular reference hiding in my skill tree?"*
19
+ - *"What `{{variables}}` are still unresolved after expansion?"*
20
+
21
+ **dotmd-parser** solves this by parsing your `.md` files into a dependency graph — automatically detecting directives, runtime references, and template placeholders. One function call gives you the full picture.
22
+
23
+ ## Comparison
24
+
25
+ | Capability | Manual / grep | dotmd-parser |
26
+ |---|---|---|
27
+ | Find `@include` / `@delegate` / `@ref` references | `grep -r "@include"` — flat list, no context | Structured graph with node types and edge metadata |
28
+ | Detect circular references | Hope you notice before the agent loops | Automatic detection with full cycle path in warnings |
29
+ | Reverse dependency ("what breaks?") | Manually trace every file | `dependents_of(graph, "shared/role.md")` — one call |
30
+ | Expand `@include` to final text | Copy-paste by hand | `resolve("SKILL.md", variables={...})` — recursive expansion |
31
+ | Find unresolved `{{variables}}` | `grep "{{" *.md` — noisy, no dedup | Deduplicated list per node and after expansion |
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 |
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install dotmd-parser
40
+ ```
41
+
42
+ ## Quick Start
43
+
44
+ ```python
45
+ from dotmd_parser import build_graph, resolve, dependents_of, summary
46
+ ```
47
+
48
+ ### build_graph — Build a dependency graph
49
+
50
+ ```python
51
+ graph = build_graph("./my-skill/")
52
+ # or from a specific file
53
+ graph = build_graph("./my-skill/SKILL.md")
54
+ ```
55
+
56
+ Returns:
57
+
58
+ ```json
59
+ {
60
+ "nodes": [
61
+ {"id": "/abs/path/to/SKILL.md", "type": "skill", "missing": false, "placeholders": []}
62
+ ],
63
+ "edges": [
64
+ {"from": "...", "to": "...", "type": "include", "parallel": false}
65
+ ],
66
+ "warnings": []
67
+ }
68
+ ```
69
+
70
+ **Custom node type mapping:**
71
+
72
+ By default, node types are inferred from path keywords (`agent`, `shared`, `prompt`, `reference`, `asset`, `template`). You can override this with the `type_map` parameter:
73
+
74
+ ```python
75
+ graph = build_graph("./my-skill/", type_map=[
76
+ ("helper", "utility"),
77
+ ("core", "foundation"),
78
+ ])
79
+ ```
80
+
81
+ **deps.yml support:**
82
+
83
+ If a `deps.yml` file exists in the root directory, its dependencies are automatically merged into the graph:
84
+
85
+ ```yaml
86
+ - path: agents/planner.md
87
+ includes:
88
+ - shared/role.md
89
+ - shared/tools.md
90
+ ```
91
+
92
+ ### resolve — Expand @include directives
93
+
94
+ Recursively expands `@include` directives into final text. `@delegate` and `@ref` lines are left as-is.
95
+
96
+ ```python
97
+ result = resolve("./prompts/main.md", variables={"name": "Alice"})
98
+
99
+ print(result["content"]) # Fully expanded text
100
+ print(result["placeholders"]) # Unresolved {{variable}} names
101
+ print(result["warnings"]) # Circular refs, missing files, etc.
102
+ ```
103
+
104
+ ### dependents_of — Reverse dependency query
105
+
106
+ ```python
107
+ # "If I change shared/role.md, what else breaks?"
108
+ affected = dependents_of(graph, "/abs/path/to/shared/role.md")
109
+ ```
110
+
111
+ ### summary — Human-readable overview
112
+
113
+ ```python
114
+ print(summary(graph))
115
+ # Nodes: 5 (agent:1, prompt:1, shared:2, skill:1)
116
+ # Edges: 4 (include:2, ref:1, read-ref:1)
117
+ # Warnings: 0
118
+ # Placeholders: name, role
119
+ ```
120
+
121
+ ## Supported Directives
122
+
123
+ | Directive | Edge type | Expanded by `resolve()`? | Description |
124
+ |---|---|---|---|
125
+ | `@include path/to/file.md` | `include` | Yes | Inline expansion — file content is inserted at this position |
126
+ | `@delegate path/to/agent.md` | `delegate` | No | Agent delegation — recorded in graph, left as-is in output |
127
+ | `@delegate path/to/agent.md --parallel` | `delegate` | No | Parallel delegation with `--parallel` flag |
128
+ | `@ref path/to/file.md` | `ref` | No | Runtime reference — recorded in graph, left as-is in output |
129
+ | `` Read `path/to/file.md` `` | `read-ref` | No | Legacy runtime reference (same as `@ref`, kept for backward compatibility) |
130
+
131
+ ## Utility Functions
132
+
133
+ Lower-level parsing functions are also exported for custom use:
134
+
135
+ ```python
136
+ from dotmd_parser import parse_directives, parse_read_refs, parse_placeholders, parse_deps_yml
137
+ ```
138
+
139
+ | Function | Description |
140
+ |---|---|
141
+ | `parse_directives(content)` | Extract `@include` / `@delegate` / `@ref` directives from text |
142
+ | `parse_read_refs(content)` | Extract legacy `Read`/`See`/list-style `.md` references (deduplicated) |
143
+ | `parse_placeholders(content)` | Extract `{{variable}}` placeholder names (deduplicated) |
144
+ | `parse_deps_yml(content)` | Parse `deps.yml` text into `{path: [includes]}` dict (no PyYAML required) |
145
+
146
+ ## CLI
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
+
180
+ ```bash
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
219
+
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
+ }
274
+ ```
275
+
276
+ The command is idempotent (SHA-256 cache) and exits fast when nothing
277
+ changed.
278
+
279
+ ## Development
280
+
281
+ ```bash
282
+ git clone https://github.com/dotmd-projects/dotmd-parser.git
283
+ cd dotmd-parser
284
+ pip install -e .
285
+ pip install pytest
286
+ pytest tests/ -v
287
+ ```
288
+
289
+ ## License
290
+
291
+ MIT
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "dotmd-parser"
3
- version = "0.2.0"
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"]