dotmd-cli 0.69.0 → 0.70.1
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.
- package/README.md +144 -964
- package/bin/dotmd.mjs +251 -202
- package/dotmd.config.example.mjs +5 -8
- package/package.json +6 -10
- package/src/agent-context.mjs +132 -0
- package/src/atomic-mutation.mjs +1505 -0
- package/src/baton.mjs +109 -114
- package/src/bulk-tag.mjs +7 -7
- package/src/check-collapse.mjs +2 -2
- package/src/commands.mjs +326 -12
- package/src/completions.mjs +38 -98
- package/src/config.mjs +18 -3
- package/src/diff.mjs +7 -3
- package/src/doctor.mjs +25 -15
- package/src/export.mjs +154 -25
- package/src/fix-refs.mjs +2 -0
- package/src/frontmatter-fix.mjs +9 -7
- package/src/frontmatter.mjs +3 -2
- package/src/git.mjs +722 -14
- package/src/graph.mjs +53 -25
- package/src/guard.mjs +163 -60
- package/src/hud.mjs +65 -76
- package/src/index-file.mjs +28 -16
- package/src/index.mjs +21 -13
- package/src/init.mjs +1 -1
- package/src/journal.mjs +145 -12
- package/src/lifecycle.mjs +596 -294
- package/src/lint.mjs +117 -56
- package/src/managed-path.mjs +192 -0
- package/src/migrate-prompts.mjs +2 -0
- package/src/migrate-template.mjs +2 -0
- package/src/migrate.mjs +7 -1
- package/src/new.mjs +135 -54
- package/src/output-identity.mjs +106 -0
- package/src/pickup-card.mjs +24 -10
- package/src/pickup.mjs +457 -0
- package/src/prompts.mjs +134 -75
- package/src/query.mjs +22 -10
- package/src/reference-planner.mjs +292 -0
- package/src/rename.mjs +65 -73
- package/src/render.mjs +24 -11
- package/src/runlist.mjs +109 -71
- package/src/section.mjs +2 -1
- package/src/ship.mjs +39 -20
- package/src/stats.mjs +1 -1
- package/src/status-metadata.mjs +87 -0
- package/src/statuses.mjs +11 -26
- package/src/summary.mjs +14 -3
- package/src/update.mjs +38 -10
- package/src/use.mjs +4 -1
- package/src/util.mjs +1 -0
- package/src/validate.mjs +53 -17
- package/src/watch.mjs +6 -1
- package/src/notion.mjs +0 -528
package/README.md
CHANGED
|
@@ -1,1070 +1,250 @@
|
|
|
1
1
|
# dotmd
|
|
2
2
|
|
|
3
|
-
CLI for managing
|
|
3
|
+
CLI for managing Markdown documents with YAML frontmatter.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
dotmd indexes, queries, validates, graphs, exports, and lifecycle-manages plans,
|
|
6
|
+
ADRs, RFCs, design docs, and other structured Markdown. It is built for
|
|
7
|
+
AI-assisted development workflows where documents need to remain current and
|
|
8
|
+
safe to mutate.
|
|
9
|
+
|
|
10
|
+
- Zero runtime dependencies
|
|
11
|
+
- Node.js 20 or newer
|
|
12
|
+
- Runtime support for Linux, macOS, and Windows
|
|
13
|
+
- Type-aware lifecycle rules for plans, docs, and saved prompts
|
|
6
14
|
|
|
7
15
|
## Install
|
|
8
16
|
|
|
9
17
|
```bash
|
|
10
|
-
npm install -g dotmd-cli # global
|
|
11
|
-
npm install -D dotmd-cli # project
|
|
12
|
-
npx dotmd-cli init # try it without installing
|
|
13
|
-
# requires Node.js >= 20
|
|
18
|
+
npm install -g dotmd-cli # global CLI and Claude Code plugin hooks
|
|
19
|
+
npm install -D dotmd-cli # project scripts via node_modules/.bin
|
|
20
|
+
npx dotmd-cli init # try it without installing
|
|
14
21
|
```
|
|
15
22
|
|
|
16
|
-
|
|
23
|
+
Maintainer release automation is POSIX-only because it uses Bash and POSIX
|
|
24
|
+
command-line tools. The published Node.js CLI remains cross-platform.
|
|
17
25
|
|
|
18
|
-
|
|
26
|
+
### Claude Code Plugin
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
The recommended Claude Code setup is the dotmd plugin:
|
|
29
|
+
|
|
30
|
+
```text
|
|
21
31
|
/plugin marketplace add reowens/dotmd
|
|
22
32
|
/plugin install dotmd@dotmd
|
|
23
33
|
```
|
|
24
34
|
|
|
25
|
-
The plugin
|
|
35
|
+
The plugin provides SessionStart and SubagentStart orientation, a PreToolUse
|
|
36
|
+
guard, the canonical workflow skill, and `/plans`, `/docs`, `/prompts`, and
|
|
37
|
+
`/baton` commands.
|
|
26
38
|
|
|
27
|
-
|
|
39
|
+
The plugin requires a global CLI install because its hooks resolve `dotmd` from
|
|
40
|
+
`PATH`. A project devDependency is useful for npm scripts but does not put the
|
|
41
|
+
CLI on the hook's `PATH`.
|
|
28
42
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
### Updating
|
|
32
|
-
|
|
33
|
-
The CLI and the Claude Code plugin are versioned in lockstep but ship as separate artifacts, so upgrading one can leave the other behind. `dotmd update` keeps them aligned:
|
|
43
|
+
Keep the CLI and plugin aligned with:
|
|
34
44
|
|
|
35
45
|
```bash
|
|
36
|
-
dotmd update
|
|
37
|
-
dotmd update --check
|
|
38
|
-
dotmd update --cli-only
|
|
39
|
-
dotmd update --plugin-only
|
|
46
|
+
dotmd update
|
|
47
|
+
dotmd update --check
|
|
48
|
+
dotmd update --cli-only
|
|
49
|
+
dotmd update --plugin-only
|
|
40
50
|
```
|
|
41
51
|
|
|
42
|
-
|
|
52
|
+
Restart Claude Code, or run `/reload-plugins`, after a plugin update.
|
|
43
53
|
|
|
44
54
|
## Quick Start
|
|
45
55
|
|
|
46
56
|
```bash
|
|
47
|
-
dotmd init
|
|
48
|
-
dotmd new
|
|
49
|
-
dotmd
|
|
50
|
-
dotmd
|
|
51
|
-
dotmd
|
|
52
|
-
dotmd doctor
|
|
57
|
+
dotmd init # create config, docs/, and the generated index
|
|
58
|
+
dotmd new plan auth-refresh # scaffold a typed document
|
|
59
|
+
dotmd briefing # compact active-work orientation
|
|
60
|
+
dotmd plans # live plan dashboard
|
|
61
|
+
dotmd check # validate schema, references, and lifecycle shape
|
|
62
|
+
dotmd doctor # preview repairs; add --apply to write
|
|
53
63
|
```
|
|
54
64
|
|
|
55
|
-
|
|
65
|
+
`dotmd briefing` is the compact orientation view. `dotmd context` is the fuller
|
|
66
|
+
human/LLM briefing, while `dotmd agent-context` emits bounded structured JSON for
|
|
67
|
+
agent integrations.
|
|
56
68
|
|
|
57
|
-
|
|
58
|
-
# bash
|
|
59
|
-
eval "$(dotmd completions bash)" # add to ~/.bashrc
|
|
69
|
+
## Core Workflow
|
|
60
70
|
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
```bash
|
|
72
|
+
dotmd briefing
|
|
73
|
+
dotmd use docs/plans/auth-refresh.md
|
|
74
|
+
dotmd set awaiting docs/plans/auth-refresh.md --note "Need API owner decision"
|
|
75
|
+
dotmd set active docs/plans/auth-refresh.md --note "Decision received"
|
|
76
|
+
dotmd archive docs/plans/auth-refresh.md --note "Shipped and verified"
|
|
63
77
|
```
|
|
64
78
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
Use `dotmd set <status> [<file>]` for lifecycle changes rather than editing a
|
|
80
|
+
`status:` line. It validates the status for the document type, updates history,
|
|
81
|
+
runs lifecycle hooks, repairs references after moves, and synchronizes the
|
|
82
|
+
index.
|
|
68
83
|
|
|
69
|
-
|
|
84
|
+
For unfinished session work, save the handoff and release the owned plan in one
|
|
85
|
+
operation:
|
|
70
86
|
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
- [x] Index regen on every mutation
|
|
75
|
-
- [x] Auto-checklist progress bars
|
|
76
|
-
- [x] Untagged docs surfaced in `list`
|
|
77
|
-
- [ ] SessionStart hook auto-wired by init
|
|
78
|
-
- [ ] Bulk-tag prompt for brownfield repos
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
`dotmd list` picks them up — zero config, no extra field:
|
|
82
|
-
|
|
83
|
-
```
|
|
84
|
-
Polish-Pass 2d ██████░░░░ 3/5
|
|
87
|
+
```bash
|
|
88
|
+
dotmd baton @/tmp/resume.md
|
|
85
89
|
```
|
|
86
90
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
| Field | Falls back to | Example body |
|
|
90
|
-
|-------|---------------|--------------|
|
|
91
|
-
| `title` | first `# H1` heading | `# Auth Token Refresh` |
|
|
92
|
-
| `summary` | first `> blockquote` line (skipping `Status note` lines) | `> One-line summary of what this doc covers.` |
|
|
93
|
-
| `current_state` | `**Status:** ...`, `- Status: ...`, or `> Status note (...): ...` lines (skipped on terminal docs to avoid stale claims) | `**Status:** Phase 2 underway` |
|
|
94
|
-
| `next_step` | first bullet under a `## Next Step` (or `## Suggested Next Step`) H2 section | `## Next Step`<br>`- wire token refresh into middleware` |
|
|
95
|
-
| Body links | inline `[text](path.md)` references | validated as ref edges by `check` |
|
|
96
|
-
|
|
97
|
-
Explicit frontmatter always wins. Body extraction is a cushion for partially-tagged docs, not a replacement for it.
|
|
98
|
-
|
|
99
|
-
## What It Does
|
|
100
|
-
|
|
101
|
-
- **Index** — group docs by status, with auto-detected progress bars (from `- [ ]` checklists) and next steps
|
|
102
|
-
- **Query** — filter by status, keyword, module, surface, owner, staleness; `dotmd grep` searches document bodies too
|
|
103
|
-
- **Resume handoff** — `dotmd baton` saves a resume prompt for the next session and releases the in-session plan in one verb
|
|
104
|
-
- **Runlists** — group plans into an ordered sequence on a hub plan; `dotmd runlist next` picks up the next one
|
|
105
|
-
- **Validate** — check for missing fields, broken references, broken body links, stale dates
|
|
106
|
-
- **Stats** — health dashboard with staleness, completeness, audit coverage
|
|
107
|
-
- **Graph** — visualize document relationships as text, Graphviz DOT, or JSON
|
|
108
|
-
- **Deps** — dependency tree or overview of what blocks what
|
|
109
|
-
- **Unblocks** — impact analysis: what depends on a doc
|
|
110
|
-
- **Health** — plan velocity, aging, pipeline status
|
|
111
|
-
- **Glossary** — domain term lookup with related docs
|
|
112
|
-
- **Lifecycle** — transition statuses, auto-archive with `git mv` and reference updates
|
|
113
|
-
- **Doctor** — auto-fix broken refs, lint issues, date drift, and stale indexes in one pass
|
|
114
|
-
- **Scaffold** — create new docs from templates (plan, ADR, RFC, audit, design)
|
|
115
|
-
- **AI summaries** — summarize docs via local MLX model or custom hook
|
|
116
|
-
- **Export** — generate concatenated markdown, static HTML site, or JSON bundle
|
|
117
|
-
- **Notion** — import from, export to, and bidirectionally sync with Notion databases
|
|
118
|
-
- **Multi-root** — manage docs across multiple directories with a single config
|
|
119
|
-
- **Context briefing** — compact summary designed for AI/LLM consumption
|
|
120
|
-
- **Dry-run** — preview any mutation with `--dry-run` before committing
|
|
91
|
+
Saved prompts are local session state. Consume them with `dotmd use`; inspect
|
|
92
|
+
without consuming via `dotmd prompts show`.
|
|
121
93
|
|
|
122
94
|
## Document Format
|
|
123
95
|
|
|
124
|
-
Any `.md` file with YAML frontmatter:
|
|
125
|
-
|
|
126
96
|
```markdown
|
|
127
97
|
---
|
|
128
|
-
type:
|
|
98
|
+
type: plan
|
|
129
99
|
status: active
|
|
130
|
-
updated: 2026-
|
|
100
|
+
updated: 2026-07-13
|
|
131
101
|
modules:
|
|
132
102
|
- auth
|
|
133
103
|
surfaces:
|
|
134
104
|
- backend
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
related_plans:
|
|
138
|
-
- ./design-doc.md
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
# Auth Token Refresh
|
|
142
|
-
|
|
143
|
-
Design doc content here...
|
|
144
|
-
|
|
145
|
-
- [x] Research existing patterns
|
|
146
|
-
- [ ] Implement refresh logic
|
|
147
|
-
- [ ] Add tests
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
The only required field is `status`. Everything else is optional but unlocks more features. The `type` field (`plan`, `doc`, or `prompt`) enables type-specific statuses and smarter context briefings.
|
|
151
|
-
|
|
152
|
-
> **Note:** `module:` and `surface:` (singular) are deprecated as of 0.36.3 — use the plural array forms (`modules:`, `surfaces:`). Run `dotmd lint --fix` to migrate existing docs.
|
|
153
|
-
|
|
154
|
-
## Document Types
|
|
155
|
-
|
|
156
|
-
Every document can have a `type` field in its frontmatter. Types determine which statuses are valid and how the document appears in context briefings.
|
|
157
|
-
|
|
158
|
-
| Type | Purpose | Valid Statuses |
|
|
159
|
-
|------|---------|----------------|
|
|
160
|
-
| `plan` | Execution plans | `in-session`, `active`, `planned`, `blocked`, `partial`, `paused`, `awaiting`, `queued-after`, `archived` |
|
|
161
|
-
| `doc` | Design docs, specs, ADRs, RFCs, reference material | `draft`, `active`, `review`, `reference`, `deprecated`, `archived` |
|
|
162
|
-
| `prompt` | Saved prompts that seed future Claude sessions | `pending`, `held`, `shelved`, `claimed`, `archived` |
|
|
163
|
-
|
|
164
|
-
Documents without a `type` field use the global `statuses.order` from config.
|
|
165
|
-
|
|
166
|
-
`dotmd new <type> <name>` sets the `type:` field automatically (`plan`, `doc`, or `prompt`).
|
|
167
|
-
|
|
168
|
-
Filter by type with `--type`:
|
|
169
|
-
|
|
170
|
-
```bash
|
|
171
|
-
dotmd query --type plan --status active # active plans
|
|
172
|
-
dotmd list --type doc # all docs
|
|
173
|
-
dotmd export --type prompt # export only saved prompts
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
Customize types and their statuses in config with the `types` key. See [`dotmd.config.example.mjs`](dotmd.config.example.mjs).
|
|
177
|
-
|
|
178
|
-
### What each plan status means
|
|
179
|
-
|
|
180
|
-
The default plan vocabulary is shaped around the **unstuck-action test**: every stop-status should map to a distinct next move. If two statuses have the same unstuck-action, one is dead weight; if a single status covers several different actions, it's overloaded.
|
|
181
|
-
|
|
182
|
-
| Status | Unstuck-action | When to use |
|
|
183
|
-
|--------|----------------|-------------|
|
|
184
|
-
| `in-session` | — | A Claude session is working on it right now. Don't pick up. |
|
|
185
|
-
| `active` | Pick up | Ready to be worked on. |
|
|
186
|
-
| `planned` | Wait for trigger | Queued; not yet ready to execute. |
|
|
187
|
-
| `blocked` | **Monitor** | External arrival on its own schedule (hardware, vendor, third-party rollout). You can't speed it up. |
|
|
188
|
-
| `partial` | **Spawn successors** | Shipped most of the plan; tail deferred. Body should reference successor plans tracking the tail. Visible but quiet (no nagging). |
|
|
189
|
-
| `paused` | **Re-evaluate** | Started but stopped mid-work; needs near-term review. NOT quiet — short (3-day) stale threshold so resume-decisions don't decay. |
|
|
190
|
-
| `awaiting` | **Ask** | Needs a human decision or input. NOT quiet — pings get forgotten, so this status generates stale pressure to chase the answer. |
|
|
191
|
-
| `queued-after` | **Check predecessor** | Sequenced behind another plan; can start once that one ships. Quiet. |
|
|
192
|
-
| `archived` | — | No longer relevant; auto-moved to the archive directory on transition. |
|
|
193
|
-
|
|
194
|
-
Each *quiet* status (`partial`, `queued-after`, `archived`) is exempt from stale-warning pressure but still appears in active scope and metrics — quietness is a presentation flag, not a closure flag. `awaiting` and `paused` deliberately stay loud so unanswered questions and stalled mid-flight work don't decay into invisible backlog.
|
|
195
|
-
|
|
196
|
-
> **Heads-up:** versions before 0.15 included a `done` plan status in the defaults. It saw effectively zero real-world use (plans went `in-session`/`active` → `archived` directly), so it was dropped from the built-in vocabulary. To finish a plan, run `dotmd archive <plan-file>` — or, if you preferred the previous behavior, add `done` back via the `types.plan.statuses` key in your config.
|
|
197
|
-
|
|
198
|
-
### Runlists: ordered groups of plans
|
|
199
|
-
|
|
200
|
-
When several plans must ship in a known order (an "auth revamp" sprint with extract → rewrite → cleanup phases), declare a `runlist:` array on a hub plan instead of chaining `queued-after` per pair or keeping the order in prose:
|
|
201
|
-
|
|
202
|
-
```yaml
|
|
203
|
-
---
|
|
204
|
-
type: plan
|
|
205
|
-
status: active
|
|
206
|
-
title: Auth Revamp
|
|
207
|
-
runlist:
|
|
208
|
-
- auth-revamp-01-extract.md
|
|
209
|
-
- auth-revamp-02-rewrite.md
|
|
210
|
-
- auth-revamp-03-cleanup.md
|
|
211
|
-
---
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
Scaffold the whole sprint in one command instead of hand-writing the hub + children:
|
|
215
|
-
|
|
216
|
-
```bash
|
|
217
|
-
$ dotmd new plan auth-revamp --runlist extract,rewrite,cleanup
|
|
218
|
-
Created: plans/auth-revamp.md (plan (runlist hub))
|
|
219
|
-
Created: plans/auth-revamp-01-extract.md (plan · runlist child, planned)
|
|
220
|
-
Created: plans/auth-revamp-02-rewrite.md (plan · runlist child, planned)
|
|
221
|
-
Created: plans/auth-revamp-03-cleanup.md (plan · runlist child, planned)
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
That writes the hub above (the `runlist:` array + an `## Order of operations` link list) plus one `planned` child stub per slug, each carrying a `parent_plan: auth-revamp.md` back-ref. Then walk the sequence:
|
|
225
|
-
|
|
226
|
-
```bash
|
|
227
|
-
$ dotmd runlist auth-revamp # the sequence + statuses; → marks the next pickup
|
|
228
|
-
runlist: plans/auth-revamp.md
|
|
229
|
-
→ 1. [planned] plans/auth-revamp-01-extract.md
|
|
230
|
-
2. [planned] plans/auth-revamp-02-rewrite.md
|
|
231
|
-
3. [planned] plans/auth-revamp-03-cleanup.md
|
|
232
|
-
|
|
233
|
-
$ dotmd runlist next auth-revamp # pick up the → child (planned → in-session) + print its card
|
|
234
|
-
▶ Started: plans/auth-revamp-01-extract.md (planned → in-session)
|
|
235
|
-
|
|
236
|
-
$ dotmd runlists # dashboard of coordination-hub runlists (--json, --limit N)
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
`runlist next` stops with a runlist-aware error if the next child isn't in a workable status (`active` / `planned` / `in-session`), so you resolve the blocker before continuing. Each child should set `parent_plan:` pointing back at the hub — `dotmd check` warns when it doesn't. There's no separate doc type: a runlist hub is just a plan with the array.
|
|
240
|
-
|
|
241
|
-
In `dotmd plans`, the hub folds its children under one tagged row — `auth-revamp runlist · 0/3 · next → 01-extract [RUNLIST]` — so a multi-plan sprint reads as one runlist instead of cluttering the triage list. A child whose hub is filtered out of the view (e.g. `--status active` when the hub is `planned`) still renders on its own.
|
|
242
|
-
|
|
243
|
-
**Coordination runlists.** A `runlist:` array fits a small ordered *sprint*. For a large, prose-first *coordination map* (a domain hub pointing at many plans, with gating/sequence rationale, sometimes unordered), set `execution_mode: coordination` instead — or just name it `*-runlist`. These aren't folded: `dotmd plans` lifts them into a pinned `Runlists` section and out of the active count, and `dotmd runlists` shows that dashboard standalone. `dotmd briefing` and `dotmd health` do the same — coordination hubs are pulled out of the live/active counts into a `runlists` bucket (briefing) and a held-out `Runlists:` tally (health), so they don't inflate the actionable-plan or aging numbers. The per-hub "N related" count comes from `related_plans:`. `dotmd check` nudges a `*-runlist` hub missing `execution_mode: coordination`.
|
|
244
|
-
|
|
245
|
-
## Commands
|
|
246
|
-
|
|
247
|
-
```
|
|
248
|
-
dotmd list [--verbose] List docs grouped by status (default)
|
|
249
|
-
dotmd json Full index as JSON
|
|
250
|
-
dotmd check [flags] Validate frontmatter and references
|
|
251
|
-
dotmd coverage [--json] Metadata coverage report
|
|
252
|
-
dotmd stats [--json] Doc health dashboard
|
|
253
|
-
dotmd graph [--dot|--json] Visualize document relationships
|
|
254
|
-
dotmd deps [file] Dependency tree or overview
|
|
255
|
-
dotmd unblocks <file> Show what depends on this doc
|
|
256
|
-
dotmd health [--json] Plan velocity, aging, and pipeline
|
|
257
|
-
dotmd briefing Compact summary for session start
|
|
258
|
-
dotmd context [--summarize] Full briefing (LLM-oriented)
|
|
259
|
-
dotmd agent-context Compact bounded JSON context for agents
|
|
260
|
-
dotmd focus [status] Detailed view for one status group
|
|
261
|
-
dotmd query [filters] Filtered search (--body scans document bodies)
|
|
262
|
-
dotmd grep <term> Keyword search incl. bodies — "which doc discussed X?"
|
|
263
|
-
dotmd plans List live plans (excludes archived)
|
|
264
|
-
dotmd modules Module dashboard (plans grouped by module)
|
|
265
|
-
dotmd module <name> Plans for one module, grouped by status
|
|
266
|
-
dotmd surfaces List configured surface taxonomy
|
|
267
|
-
dotmd stale List stale docs
|
|
268
|
-
dotmd actionable List docs with next steps
|
|
269
|
-
dotmd index [--print] Generate/update docs.md index block
|
|
270
|
-
dotmd hud Actionable triage (silent when clean — ideal SessionStart hook)
|
|
271
|
-
dotmd use [<file-or-slug>] Open by type: prompt → consume, plan → start, doc → read
|
|
272
|
-
(no arg: consume the oldest pending prompt)
|
|
273
|
-
dotmd set <status> <file> Change a document's status (--note appends why to Version History)
|
|
274
|
-
dotmd baton [<plan>|<slug>] <@draft|-> Save a resume prompt; releases the in-session plan
|
|
275
|
-
dotmd runlist <hub> [next] Show or walk an ordered group of plans
|
|
276
|
-
dotmd status <file> <status> Transition document status (deprecated; prefer set)
|
|
277
|
-
dotmd archive <file> Archive (status + move + update refs)
|
|
278
|
-
dotmd bulk archive <files> Archive multiple files at once
|
|
279
|
-
dotmd bulk-tag [files] Tag pre-existing untagged .md files
|
|
280
|
-
dotmd touch <file> Bump updated date
|
|
281
|
-
dotmd touch --git Bulk-sync dates from git history
|
|
282
|
-
dotmd doctor [--apply] Fix refs, lint, dates, index (previews by default)
|
|
283
|
-
dotmd self-check Project/version skew diagnostic
|
|
284
|
-
dotmd fix-refs Auto-fix broken reference paths
|
|
285
|
-
dotmd lint [--fix] Check and auto-fix frontmatter issues
|
|
286
|
-
dotmd rename <old> <new> Rename doc and update references
|
|
287
|
-
dotmd migrate <f> <old> <new> Batch update a frontmatter field
|
|
288
|
-
dotmd ship [patch|minor|major] Regen + commit + bump in one step
|
|
289
|
-
dotmd notion <sub> [db-id] Notion import/export/sync
|
|
290
|
-
dotmd export [file] Export docs as md, html, or json
|
|
291
|
-
dotmd summary <file> AI summary of a document
|
|
292
|
-
dotmd glossary <term> Look up domain terms + related docs
|
|
293
|
-
dotmd watch [command] Re-run a command on file changes
|
|
294
|
-
dotmd diff [file] Show changes since last updated date
|
|
295
|
-
dotmd new <type> <name> Create a new doc (type: doc, plan, or prompt)
|
|
296
|
-
dotmd prompts [sub] Manage saved prompts (list, show, hold, archive, new)
|
|
297
|
-
dotmd statuses [sub] Manage per-project status taxonomy
|
|
298
|
-
dotmd journal [flags] View opt-in command-usage journal (DOTMD_JOURNAL=1)
|
|
299
|
-
dotmd init Create starter config + docs directory
|
|
300
|
-
dotmd completions <shell> Output shell completion script (bash, zsh)
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
Run `dotmd help all` for the always-current version of this list, `dotmd help statuses` for the status vocabulary, and `dotmd <cmd> --help` for per-command details.
|
|
304
|
-
|
|
305
|
-
### Global Flags
|
|
306
|
-
|
|
307
|
-
```
|
|
308
|
-
--config <path> Explicit config file path
|
|
309
|
-
--dry-run, -n Preview changes without writing anything
|
|
310
|
-
--root <name> Filter to a specific docs root
|
|
311
|
-
--type <t1,t2> Filter by document type (plan, doc, prompt, or custom)
|
|
312
|
-
--verbose Show resolved config details
|
|
313
|
-
--help, -h Show help (per-command with: dotmd <cmd> --help)
|
|
314
|
-
--version, -v Show version
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
### Query Filters
|
|
318
|
-
|
|
319
|
-
```bash
|
|
320
|
-
dotmd query --status active,ready --module auth
|
|
321
|
-
dotmd query --keyword "token" --has-next-step
|
|
322
|
-
dotmd query --stale --sort updated --all
|
|
323
|
-
dotmd query --surface backend --checklist-open
|
|
324
|
-
dotmd query --status active --summarize # AI summaries
|
|
325
|
-
dotmd query --status active --summarize --summarize-limit 3
|
|
326
|
-
dotmd query --keyword "retries" --body # scan document bodies too
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
Flags: `--type`, `--status`, `--keyword`, `--body`, `--module`, `--surface`, `--domain`, `--owner`, `--updated-since`, `--stale`, `--has-next-step`, `--has-blockers`, `--checklist-open`, `--sort`, `--limit`, `--all`, `--git`, `--json`, `--summarize`, `--summarize-limit`, `--model`.
|
|
330
|
-
|
|
331
|
-
`dotmd grep <term>` is the "which doc discussed X?" shorthand — an alias for `dotmd query --keyword <term> --body --all` that prints doc cards plus line-numbered excerpts per body hit. Bodies are read lazily (frontmatter filters run first), and it composes with the usual query flags.
|
|
332
|
-
|
|
333
|
-
### Create Documents
|
|
334
|
-
|
|
335
|
-
The signature is `dotmd new <type> <name> [body]`. `<type>` is one of the built-in types (`doc`, `plan`, `prompt`) or a custom type from your config. If you omit `<type>`, it defaults to `doc`.
|
|
336
|
-
|
|
337
|
-
```bash
|
|
338
|
-
dotmd new plan auth-revamp # type: plan → docs/plans/auth-revamp.md
|
|
339
|
-
dotmd new doc token-refresh-design # type: doc → docs/token-refresh-design.md
|
|
340
|
-
dotmd new my-feature # implicit type: doc
|
|
341
|
-
dotmd new plan auth --status planned # initial status override
|
|
342
|
-
dotmd new doc my-doc --title "Custom Title" # title override
|
|
343
|
-
dotmd new doc my-doc --root modules # create in a specific root
|
|
344
|
-
dotmd new --list-types # show registered types
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
Each built-in type has a template baked in:
|
|
348
|
-
|
|
349
|
-
| Type | Default destination | Shape |
|
|
350
|
-
|------|---------------------|-------|
|
|
351
|
-
| `plan` | `docs/plans/<slug>.md` | Problem → Phases → Closeout, with phase status markers and Version History |
|
|
352
|
-
| `doc` | `docs/<slug>.md` | Overview → Version History → Related (build-up shape lite) |
|
|
353
|
-
| `prompt` | `docs/prompts/<slug>.md` | Body is required (see [Saved Prompts](#saved-prompts)) |
|
|
354
|
-
|
|
355
|
-
**Plan body variants (plans only).** The default `plan` template is the full build-up shape. For a smaller plan, or the recurring audit shape, pass one body-variant flag:
|
|
356
|
-
|
|
357
|
-
- `--lite` / `--minimal` — Problem → Phases → Version History (drops Goals / Non-Goals / What Exists Today / Constraints / Decisions / Deferred / Closeout).
|
|
358
|
-
- `--audit` / `--findings` — Problem → Findings (ranked) → Suggested order → Open Questions, for "I investigated X, here's what I found" plans.
|
|
359
|
-
|
|
360
|
-
```bash
|
|
361
|
-
dotmd new plan quick-fix --lite
|
|
362
|
-
dotmd new plan perf-audit --audit
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
To scaffold an ordered sprint or a coordination map instead, see [Runlists](#runlists-ordered-groups-of-plans) (`--runlist a,b,c` / `--coordination`). The body variants and the hub flags are all mutually exclusive — a plan has exactly one body shape.
|
|
366
|
-
|
|
367
|
-
Add custom types via `templates` in your config:
|
|
368
|
-
|
|
369
|
-
```js
|
|
370
|
-
export const templates = {
|
|
371
|
-
spike: {
|
|
372
|
-
description: 'Timeboxed investigation',
|
|
373
|
-
defaultStatus: 'active',
|
|
374
|
-
targetRoot: 'spikes', // in flat-array root configs, lands in the matching root
|
|
375
|
-
dir: 'spikes', // in single-root configs, creates docs/spikes/<slug>.md
|
|
376
|
-
frontmatter: (status, today) => `type: spike\nstatus: ${status}\nupdated: ${today}\ntimebox: 2d`,
|
|
377
|
-
body: (title) => `\n# ${title}\n\n## Hypothesis\n\n\n\n## Findings\n\n\n`,
|
|
378
|
-
},
|
|
379
|
-
};
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
Then `dotmd new spike my-spike` creates a doc from your template.
|
|
383
|
-
|
|
384
|
-
**Routing your custom type to a directory.** Two knobs:
|
|
385
|
-
|
|
386
|
-
- `targetRoot: '<name>'` — name (basename or suffix) of a root entry. In configs with `root: ['docs/plans', 'docs/spikes', ...]` (flat-array layout), the new doc lands in the matching root.
|
|
387
|
-
- `dir: '<subdir>'` — subdirectory under `config.docsRoot`. Used as the fallback when `targetRoot` doesn't match anything (typical single-root layout).
|
|
388
|
-
|
|
389
|
-
Set both for portability. The `--root` CLI flag overrides both. **Overrides do not inherit builtin properties** — if you override `templates.prompt`, re-declare `targetRoot`, `dir`, `defaultStatus`, `requiresBody`, etc. that you want preserved.
|
|
390
|
-
|
|
391
|
-
### Saved Prompts
|
|
392
|
-
|
|
393
|
-
Saved prompts are `.md` files with `type: prompt` that capture a request meant to seed a future Claude session — "look at the remaining lint warnings tomorrow," "resume the payments refactor," "draft the on-call runbook." The body is the prompt; the frontmatter tracks status.
|
|
394
|
-
|
|
395
|
-
```bash
|
|
396
|
-
dotmd new prompt cleanup-tomorrow "look at remaining lint warnings"
|
|
397
|
-
dotmd new prompt resume-foo - <<'EOF'
|
|
398
|
-
multi-line
|
|
399
|
-
prompt body
|
|
400
|
-
EOF
|
|
401
|
-
dotmd new prompt from-file @/tmp/draft.md
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
Manage them with the `prompts` command family:
|
|
405
|
-
|
|
406
|
-
Consume one with `dotmd use` — it atomically prints the body and archives the prompt so it can't be double-consumed:
|
|
407
|
-
|
|
408
|
-
```bash
|
|
409
|
-
dotmd use # consume the oldest pending prompt
|
|
410
|
-
dotmd use <file-or-slug> # consume a specific prompt
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
Admin verbs live under the `prompts` namespace:
|
|
414
|
-
|
|
415
|
-
```bash
|
|
416
|
-
dotmd prompts # list pending prompts (default)
|
|
417
|
-
dotmd prompts list --all # all statuses
|
|
418
|
-
dotmd prompts show <file> # read-only peek: print the body WITHOUT consuming
|
|
419
|
-
dotmd prompts next # print body of oldest pending + auto-archive (one-shot)
|
|
420
|
-
dotmd prompts use <file> # print body of a specific prompt + auto-archive
|
|
421
|
-
dotmd prompts hold <file> # park a prompt (status → held) under prompts/held/:
|
|
422
|
-
# kept in list, hidden from hud/briefing, skipped by `next`
|
|
423
|
-
dotmd prompts unhold <file> # move a held prompt back to pending
|
|
424
|
-
dotmd prompts shelve <file> # legacy alias for `hold`
|
|
425
|
-
dotmd prompts archive <file> # archive without printing the body
|
|
426
|
-
dotmd prompts new <name> [body] # alias for `dotmd new prompt`
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
`dotmd hud` surfaces pending prompts on session start, so a saved prompt acts as a self-addressed reminder: write it now, the next session sees it. Held prompts are kept out of the SessionStart surface — use them for "saved but not next."
|
|
430
|
-
|
|
431
|
-
Statuses: `pending` (drafted, awaiting a session), `held` (saved but parked under `prompts/held/` — visible in `prompts list`, hidden from `hud`/`briefing`, skipped by `prompts next`), `archived` (consumed or filed away). `shelved` is a legacy spelling accepted for older files; `claimed` is reserved for a future "in-flight" state but is currently a synonym for archived in practice.
|
|
432
|
-
|
|
433
|
-
### Baton: resume prompts & session handoff
|
|
434
|
-
|
|
435
|
-
`dotmd baton` is the "save a resume prompt" verb — the way one session hands work to the next without pasting resume text into chat. Write a short draft (the next concrete decision plus any gotchas, not a recap), then:
|
|
436
|
-
|
|
437
|
-
```bash
|
|
438
|
-
dotmd baton @/tmp/draft.md # plan mode: a plan is in-session
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
In plan mode, baton does the whole closeout in one call:
|
|
442
|
-
|
|
443
|
-
1. Saves a resume prompt named `resume-<plan-slug>` (collision-safe: `-2`, `-3`, …) under `docs/prompts/` with `status: pending`.
|
|
444
|
-
2. Releases the plan: one status flip, `in-session` → `active` by default (`--status paused|awaiting|partial|blocked` to override, `--note "why"` to record the reason in `## Version History`).
|
|
445
|
-
3. Prints the exact `git commit` command for the plan's frontmatter change — the prompt stays out of the pathspec, because saved prompts are session-local.
|
|
446
|
-
|
|
447
|
-
Baton resolves *your* plan via the command journal (or takes it explicitly: `dotmd baton <plan-file> @draft`), falling back to the only in-session plan.
|
|
448
|
-
|
|
449
|
-
No plan involved? Slug mode saves the prompt and touches nothing else:
|
|
450
|
-
|
|
451
|
-
```bash
|
|
452
|
-
dotmd baton checkout-fixes @/tmp/draft.md # saves resume-checkout-fixes; no status changes
|
|
453
|
-
cat /tmp/draft.md | dotmd baton # body from stdin
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
Either way, the next session's `dotmd hud` surfaces the pending prompt, and `dotmd use` consumes it.
|
|
457
|
-
|
|
458
|
-
### Command Journal (opt-in)
|
|
459
|
-
|
|
460
|
-
dotmd's primary user is an agent. Every CLI invocation can be journaled
|
|
461
|
-
to `.dotmd/journal.jsonl` so agents (and humans) can see what got run,
|
|
462
|
-
what failed, and how long things took — observability that turns every
|
|
463
|
-
session into data the next design call can use.
|
|
464
|
-
|
|
465
|
-
Default off. Enable with either:
|
|
466
|
-
|
|
467
|
-
```bash
|
|
468
|
-
export DOTMD_JOURNAL=1 # env var
|
|
469
|
-
# or, in dotmd.config.mjs:
|
|
470
|
-
export const journal = true; # config flag
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
(`DOTMD_JOURNAL=0` forces off even when the config opts in.)
|
|
474
|
-
|
|
475
|
-
Each invocation appends one JSON line:
|
|
476
|
-
`{ts, sid, pid, argv, exit, ms, v, err?}`. Writes are atomic via
|
|
477
|
-
`O_APPEND` (entries are well under `PIPE_BUF`), so concurrent sessions
|
|
478
|
-
interleave cleanly without locking. Lazy rotation to
|
|
479
|
-
`.dotmd/journal.jsonl.1` on version change, at >5MB, or when the oldest
|
|
480
|
-
entry is >30 days; one backup retained and pruned after 30 days.
|
|
481
|
-
Version-change rotation keeps agent-facing journal summaries focused on the
|
|
482
|
-
currently installed dotmd.
|
|
483
|
-
|
|
484
|
-
Read it back with `dotmd journal`:
|
|
485
|
-
|
|
486
|
-
```bash
|
|
487
|
-
dotmd journal --tail 20 # last N entries (default)
|
|
488
|
-
dotmd journal --errors # only non-zero exits
|
|
489
|
-
dotmd journal --session <id> # filter by session id
|
|
490
|
-
dotmd journal --since 2026-05-01 # filter by ts
|
|
491
|
-
dotmd journal --by-command # group by argv[0]: count, median ms, errors
|
|
492
|
-
dotmd journal --json # raw entries as a JSON array
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
The journal is local-only and gitignored (or should be — `.dotmd/` is
|
|
496
|
-
typically already ignored). Default-off keeps the surface clean for
|
|
497
|
-
users who don't want the storage / PII tradeoff.
|
|
498
|
-
|
|
499
|
-
### Check & Fix
|
|
500
|
-
|
|
501
|
-
```bash
|
|
502
|
-
dotmd check # validate everything
|
|
503
|
-
dotmd check --errors-only # suppress warnings, show only errors
|
|
504
|
-
dotmd check --fix # auto-fix broken refs + lint + regen index
|
|
505
|
-
```
|
|
506
|
-
|
|
507
|
-
Validates: required fields, status values, broken reference paths, broken body links (`[text](path.md)`), bidirectional reference symmetry, git date drift, taxonomy mismatches.
|
|
508
|
-
|
|
509
|
-
#### Per-ref one-way opt-out (`>` prefix)
|
|
510
|
-
|
|
511
|
-
`referenceFields.bidirectional` is per-field — once a field is bidirectional, every ref in it expects a back-ref. For leaf-to-upstream cases (a plan referencing the audit doc that spawned it; many docs pointing at a single hub) that's noise: the parent shouldn't list every child. Prefix the value with `>` to mark a single ref one-way without changing the field:
|
|
512
|
-
|
|
513
|
-
```yaml
|
|
105
|
+
current_state: Token validation is complete.
|
|
106
|
+
next_step: Wire refresh rotation into middleware.
|
|
514
107
|
related_docs:
|
|
515
|
-
-
|
|
516
|
-
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
The prefix is stripped before path resolution — refs still resolve normally. Works on any ref field. Quote the value (it starts with `>`, which is YAML's block-scalar indicator). Shipped 0.35.0 — closed 7 false-positive warnings in this repo's own corpus.
|
|
520
|
-
|
|
521
|
-
### Stats
|
|
522
|
-
|
|
523
|
-
```bash
|
|
524
|
-
dotmd stats # health dashboard
|
|
525
|
-
dotmd stats --json # machine-readable
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
Shows: status counts, staleness, errors/warnings, freshness (today/week/month), completeness (owner/surface/module/next_step), checklist progress, audit coverage.
|
|
529
|
-
|
|
530
|
-
### Doctor
|
|
531
|
-
|
|
532
|
-
```bash
|
|
533
|
-
dotmd doctor # preview: fix refs → lint → sync git dates → regen index
|
|
534
|
-
dotmd doctor --apply # actually write the fixes (previews by default since 0.37.0)
|
|
535
|
-
dotmd doctor --statuses # detect overloaded status buckets (read-only)
|
|
536
|
-
dotmd doctor --statuses --json # machine-readable suggestions
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
`--statuses` is a read-only diagnostic. It scans each status with at least
|
|
540
|
-
10 plans and groups their `current_state` / `next_step` text against cue
|
|
541
|
-
keywords for `partial`, `paused`, `awaiting`, `queued-after`, and `blocked`.
|
|
542
|
-
When a single bucket lands plans in two or more cue groups (each above 15%
|
|
543
|
-
of the bucket), it prints a split suggestion:
|
|
544
|
-
|
|
545
|
-
```
|
|
546
|
-
47 plan/backlog plans cluster across 4 patterns — consider splitting:
|
|
547
|
-
~22 → partial (cues: "shipped", "landed", "tail", "deferred")
|
|
548
|
-
~15 → paused (cues: "paused", "on hold", "set aside")
|
|
549
|
-
~ 6 → queued-after (cues: "after", "once", "depends on", "waiting on <plan>")
|
|
550
|
-
~ 4 → (kept in backlog — no clear pattern match)
|
|
551
|
-
|
|
552
|
-
Heuristic — verify before migrating.
|
|
553
|
-
```
|
|
554
|
-
|
|
555
|
-
The heuristic is intentionally conservative: small buckets are skipped, plans
|
|
556
|
-
that match no cues stay in the original bucket, and the output is always a
|
|
557
|
-
suggestion — never a verdict.
|
|
558
|
-
|
|
559
|
-
### Graph
|
|
560
|
-
|
|
561
|
-
```bash
|
|
562
|
-
dotmd graph # text adjacency list
|
|
563
|
-
dotmd graph --dot | dot -Tpng -o g.png # Graphviz PNG
|
|
564
|
-
dotmd graph --json # machine-readable
|
|
565
|
-
dotmd graph --status active,ready # filter by status
|
|
566
|
-
dotmd graph --module auth # filter by module
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
### Deps
|
|
570
|
-
|
|
571
|
-
```bash
|
|
572
|
-
dotmd deps # overview: most blocking, most blocked
|
|
573
|
-
dotmd deps docs/plan-a.md # tree: depends-on + depended-on-by
|
|
574
|
-
dotmd deps docs/plan-a.md --depth 2 # limit tree depth
|
|
575
|
-
dotmd deps --json # machine-readable
|
|
576
|
-
```
|
|
577
|
-
|
|
578
|
-
### Unblocks
|
|
579
|
-
|
|
580
|
-
```bash
|
|
581
|
-
dotmd unblocks docs/plan-a.md # what depends on this plan
|
|
582
|
-
dotmd unblocks docs/plan-a.md --json # machine-readable
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
### Health
|
|
586
|
-
|
|
587
|
-
```bash
|
|
588
|
-
dotmd health # plan pipeline and aging
|
|
589
|
-
dotmd health --json # machine-readable
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
### Briefing
|
|
593
|
-
|
|
594
|
-
```bash
|
|
595
|
-
dotmd briefing # compact 5-10 line summary
|
|
596
|
-
dotmd briefing --json # machine-readable
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
### Modules Dashboard
|
|
600
|
-
|
|
601
|
-
A triage view for codebases with enough plans that a flat list stops being useful (rule-of-thumb: ~50+ plans across many modules). Composes existing primitives (`modules: []`, `isStale`, `daysSinceUpdate`, `hasNextStep`, `statusOrder`) — no new config.
|
|
602
|
-
|
|
603
|
-
```bash
|
|
604
|
-
dotmd modules # one row per module, dynamic status columns
|
|
605
|
-
dotmd modules --sort cleanup # rank by (stale × avgAge) / total — "rotting hardest"
|
|
606
|
-
dotmd modules --sort stale|age|nextstep|total
|
|
607
|
-
dotmd modules --type doc # docs instead of plans
|
|
608
|
-
dotmd modules --limit 20 # default 20; --all to disable
|
|
609
|
-
dotmd modules --json # includes _totalUnique for double-count detection
|
|
610
|
-
|
|
611
|
-
dotmd module <name> # deep view of one module, plans grouped by status
|
|
612
|
-
dotmd module <name> --sort updated|age # default sort is status
|
|
613
|
-
dotmd module <name> --json
|
|
614
|
-
```
|
|
615
|
-
|
|
616
|
-
Workflow for systematic cleanup:
|
|
617
|
-
|
|
618
|
-
```
|
|
619
|
-
dotmd modules --sort cleanup → walk the top row → dotmd module <name> → triage/archive → next
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
Notes:
|
|
623
|
-
|
|
624
|
-
- Status columns are dynamic — only statuses with ≥1 plan render, so default and custom vocabularies both look right.
|
|
625
|
-
- A plan with `modules: [a, b]` counts in both rows. This is intentional. `--json` exposes `_totalUnique` so tooling can detect this if needed.
|
|
626
|
-
- `(none)` is a literal row for unmoduled plans — surfaces unowned work that would otherwise hide in a flat list.
|
|
627
|
-
- Unknown module names exit with `Module 'foo' not found. Did you mean: …?` (substring-first, Levenshtein ≤3 fallback).
|
|
628
|
-
- The dashboard auto-falls-back to a stacked render when the table doesn't fit your terminal width.
|
|
629
|
-
|
|
630
|
-
`dotmd stale --group module` is the canonical "what's rotting per module" companion view (uses the existing `query --group` mechanism, called out here so it's findable).
|
|
631
|
-
|
|
632
|
-
### AI Summaries
|
|
633
|
-
|
|
634
|
-
```bash
|
|
635
|
-
dotmd summary docs/plan-a.md # AI summary of a single doc
|
|
636
|
-
dotmd summary docs/plan-a.md --json # JSON output
|
|
637
|
-
dotmd query --status active --summarize # AI summaries in query results
|
|
638
|
-
dotmd context --summarize # AI-enhanced briefing
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
Uses a local model by default. Override with `--model <name>` or the `summarizeDoc` hook.
|
|
642
|
-
|
|
643
|
-
### Glossary
|
|
644
|
-
|
|
645
|
-
```bash
|
|
646
|
-
dotmd glossary "auth token" # look up a term
|
|
647
|
-
dotmd glossary --list # list all terms
|
|
648
|
-
dotmd glossary --json # machine-readable
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
### Export
|
|
652
|
-
|
|
653
|
-
```bash
|
|
654
|
-
dotmd export # all docs as concatenated markdown
|
|
655
|
-
dotmd export --format html --output site # static HTML site
|
|
656
|
-
dotmd export --format json > bundle.json # JSON bundle with bodies
|
|
657
|
-
dotmd export docs/plan-a.md # single doc + dependencies
|
|
658
|
-
dotmd export --status active # filtered export
|
|
659
|
-
dotmd export --type plan # export only plans
|
|
660
|
-
```
|
|
661
|
-
|
|
662
|
-
### Notion Integration
|
|
663
|
-
|
|
664
|
-
```bash
|
|
665
|
-
dotmd notion import <database-id> # pull Notion database → local .md files
|
|
666
|
-
dotmd notion export <database-id> # push local docs → Notion database
|
|
667
|
-
dotmd notion sync <database-id> # bidirectional sync (newer wins)
|
|
668
|
-
dotmd notion import <db-id> --force # overwrite existing files
|
|
669
|
-
dotmd notion sync <db-id> --dry-run # preview sync actions
|
|
670
|
-
```
|
|
671
|
-
|
|
672
|
-
Requires `NOTION_TOKEN` env var or `notion.token` in config. Maps Notion properties (select, multi_select, date, status, people, etc.) to YAML frontmatter fields. Configure property mapping in config:
|
|
673
|
-
|
|
674
|
-
```js
|
|
675
|
-
export const notion = {
|
|
676
|
-
token: process.env.NOTION_TOKEN,
|
|
677
|
-
database: 'your-database-id',
|
|
678
|
-
propertyMap: {
|
|
679
|
-
'Status': 'status',
|
|
680
|
-
'Last Updated': 'updated',
|
|
681
|
-
'Tags': 'surfaces',
|
|
682
|
-
},
|
|
683
|
-
};
|
|
684
|
-
```
|
|
685
|
-
|
|
686
|
-
### Multi-Root
|
|
687
|
-
|
|
688
|
-
Manage docs across multiple directories:
|
|
689
|
-
|
|
690
|
-
```js
|
|
691
|
-
export const root = ['docs/plans', 'docs/modules', 'docs/app'];
|
|
692
|
-
```
|
|
693
|
-
|
|
694
|
-
All commands work across all roots. Filter with `--root`:
|
|
695
|
-
|
|
696
|
-
```bash
|
|
697
|
-
dotmd list --root plans # only docs from docs/plans
|
|
698
|
-
dotmd stats --root modules # stats for modules only
|
|
699
|
-
dotmd new my-doc --root modules # create in docs/modules
|
|
700
|
-
```
|
|
701
|
-
|
|
702
|
-
Archive stays within the source file's root. Cross-root references validate correctly.
|
|
703
|
-
|
|
704
|
-
### Archive
|
|
705
|
-
|
|
706
|
-
```bash
|
|
707
|
-
dotmd archive docs/old-plan.md # move + update refs + regen index
|
|
708
|
-
dotmd archive docs/old-plan.md -n # preview
|
|
709
|
-
```
|
|
710
|
-
|
|
711
|
-
### Bulk Archive
|
|
712
|
-
|
|
713
|
-
```bash
|
|
714
|
-
dotmd bulk archive docs/old-a.md docs/old-b.md # archive multiple
|
|
715
|
-
dotmd bulk archive docs/old-*.md -n # preview
|
|
716
|
-
```
|
|
717
|
-
|
|
718
|
-
### Open & Closeout
|
|
719
|
-
|
|
720
|
-
Status is just frontmatter. There's no checkout, lock, or lease — opening a
|
|
721
|
-
plan, transitioning it, and closing it are all plain status writes (archive
|
|
722
|
-
also moves the file).
|
|
723
|
-
|
|
724
|
-
```bash
|
|
725
|
-
dotmd use docs/plans/my-plan.md # mark in-session + print the plan card
|
|
726
|
-
dotmd set in-session docs/plans/my-plan.md # set the status without printing
|
|
727
|
-
dotmd set active docs/plans/my-plan.md # need more work: flip back to active
|
|
728
|
-
dotmd set partial docs/plans/my-plan.md # shipped + tail deferred (reference successors in body)
|
|
729
|
-
dotmd set awaiting docs/plans/my-plan.md # stuck on a human decision
|
|
730
|
-
dotmd archive docs/plans/my-plan.md # fully shipped: archive + move + update refs
|
|
731
|
-
dotmd archive docs/plans/my-plan.md --closeout-template # also inject ## Closeout skeleton
|
|
732
|
-
```
|
|
733
|
-
|
|
734
|
-
`in-session` is a status like any other — `dotmd set <status> <file>` writes it
|
|
735
|
-
to the file's frontmatter and does nothing else.
|
|
736
|
-
|
|
737
|
-
Add `--note "why"` to any `set` or `archive` to append the reason to the doc's
|
|
738
|
-
`## Version History` section in the same call (creates the section if missing) —
|
|
739
|
-
it saves the status-change + worklog-edit round-trip. `set partial` without a
|
|
740
|
-
note or successor link prints a reminder.
|
|
741
|
-
|
|
742
|
-
To stop mid-work and hand off to a future session, use `dotmd baton` (see
|
|
743
|
-
[Baton](#baton-resume-prompts--session-handoff)) — it saves the resume prompt
|
|
744
|
-
and releases the plan in one verb.
|
|
108
|
+
- ./auth-design.md
|
|
109
|
+
---
|
|
745
110
|
|
|
746
|
-
|
|
747
|
-
(or your project's `.claude/settings.json`):
|
|
111
|
+
# Auth Refresh
|
|
748
112
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
"hooks": {
|
|
752
|
-
"SessionStart": [
|
|
753
|
-
{
|
|
754
|
-
"hooks": [
|
|
755
|
-
{ "type": "command", "command": "dotmd hud", "timeout": 5 }
|
|
756
|
-
]
|
|
757
|
-
}
|
|
758
|
-
]
|
|
759
|
-
}
|
|
760
|
-
}
|
|
113
|
+
- [x] Validate tokens
|
|
114
|
+
- [ ] Rotate refresh tokens
|
|
761
115
|
```
|
|
762
116
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
117
|
+
`status` is the only universally required field. A `type` enables type-specific
|
|
118
|
+
statuses, validation, templates, and briefing behavior. Explicit frontmatter
|
|
119
|
+
wins, but dotmd can also derive titles, summaries, state, next steps, checklist
|
|
120
|
+
progress, and Markdown links from the body.
|
|
767
121
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
> `permissions.allow` list as well, otherwise the hooks will be blocked.
|
|
122
|
+
Use plural `modules:` and `surfaces:` arrays. The old singular keys remain
|
|
123
|
+
readable for compatibility and can be migrated with `dotmd lint --fix`.
|
|
771
124
|
|
|
772
|
-
###
|
|
125
|
+
### Built-In Types
|
|
773
126
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
127
|
+
| Type | Purpose | Default statuses |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| `plan` | Executable work | `in-session`, `active`, `planned`, `blocked`, `partial`, `paused`, `awaiting`, `queued-after`, `archived` |
|
|
130
|
+
| `doc` | Specs, ADRs, audits, and reference material | `draft`, `active`, `review`, `reference`, `deprecated`, `archived` |
|
|
131
|
+
| `prompt` | Saved future-session instructions | `pending`, `held`, `shelved`, `claimed`, `archived` |
|
|
778
132
|
|
|
779
|
-
|
|
133
|
+
Status definitions can be customized per type. Rich status objects co-locate
|
|
134
|
+
display, staleness, validation, terminal, and archive behavior in one place.
|
|
780
135
|
|
|
781
|
-
|
|
782
|
-
dotmd fix-refs # fix broken frontmatter refs + body links
|
|
783
|
-
dotmd fix-refs --dry-run # preview fixes
|
|
784
|
-
```
|
|
136
|
+
## Runlists And Roadmaps
|
|
785
137
|
|
|
786
|
-
|
|
138
|
+
A sprint runlist is an ordered `runlist:` array on a hub plan. Scaffold a hub
|
|
139
|
+
and children together:
|
|
787
140
|
|
|
788
141
|
```bash
|
|
789
|
-
dotmd
|
|
790
|
-
dotmd
|
|
142
|
+
dotmd new plan auth-revamp --runlist extract,rewrite,cleanup
|
|
143
|
+
dotmd runlist auth-revamp
|
|
144
|
+
dotmd runlist next auth-revamp
|
|
791
145
|
```
|
|
792
146
|
|
|
793
|
-
|
|
147
|
+
Mutate the structure through the CLI so the array, child `parent_plan` refs, and
|
|
148
|
+
body order list remain synchronized:
|
|
794
149
|
|
|
795
150
|
```bash
|
|
796
|
-
dotmd
|
|
797
|
-
dotmd
|
|
798
|
-
dotmd
|
|
799
|
-
|
|
800
|
-
dotmd statuses add paused --type plan --like blocked --quiet # clone blocked, then quiet
|
|
801
|
-
dotmd statuses set archived --type plan --no-quiet # tweak a flag
|
|
802
|
-
dotmd statuses remove obsolete --type plan # refuses if any docs use it
|
|
803
|
-
|
|
804
|
-
dotmd statuses migrate plan # array-form → rich-form
|
|
151
|
+
dotmd runlist add auth-revamp docs/plans/existing-plan.md
|
|
152
|
+
dotmd runlist add auth-revamp follow-up
|
|
153
|
+
dotmd runlist reorder auth-revamp follow-up --before cleanup
|
|
154
|
+
dotmd runlist remove auth-revamp extract --clear-parent
|
|
805
155
|
```
|
|
806
156
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
`--dry-run` to preview without writing. Edits are atomic: the rewrite lands in
|
|
811
|
-
a sibling temp file, is validated by re-importing it and running
|
|
812
|
-
`resolveConfig`, then renamed into place — a syntax error or new warning
|
|
813
|
-
leaves the original untouched.
|
|
157
|
+
Archived children count as complete. Parked children (`blocked`, `partial`,
|
|
158
|
+
`paused`, `awaiting`, and `queued-after`) are skipped when choosing the next
|
|
159
|
+
pickup but do not count as done.
|
|
814
160
|
|
|
815
|
-
|
|
816
|
-
explicit `export const lifecycle = {...}`, the explicit lifecycle silently
|
|
817
|
-
overrides per-status flags at runtime. `dotmd statuses` write commands refuse
|
|
818
|
-
to write into that state and recommend deleting the explicit `lifecycle` block;
|
|
819
|
-
pass `--ignore-lifecycle-override` to write anyway.
|
|
820
|
-
|
|
821
|
-
### Rename
|
|
161
|
+
For a larger prose-first domain map, create a coordination runlist:
|
|
822
162
|
|
|
823
163
|
```bash
|
|
824
|
-
dotmd
|
|
164
|
+
dotmd new plan platform-work --coordination
|
|
165
|
+
dotmd runlists
|
|
825
166
|
```
|
|
826
167
|
|
|
827
|
-
|
|
168
|
+
For progress across several runlists, create a roadmap:
|
|
828
169
|
|
|
829
170
|
```bash
|
|
830
|
-
dotmd
|
|
831
|
-
dotmd
|
|
832
|
-
|
|
833
|
-
# Per-file form: split one overloaded status into several distinct ones.
|
|
834
|
-
# Only the listed files are rewritten; every other doc with the old value is left alone.
|
|
835
|
-
dotmd migrate status backlog paused docs/plans/foo.md docs/plans/bar.md
|
|
836
|
-
dotmd migrate status backlog partial docs/plans/payments-future.md # one at a time also works
|
|
171
|
+
dotmd new plan platform-roadmap --roadmap
|
|
172
|
+
dotmd roadmap platform-roadmap
|
|
173
|
+
dotmd roadmap platform-roadmap next
|
|
837
174
|
```
|
|
838
175
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
want to split it across the new vocabulary. File args match the same
|
|
843
|
-
way as `bulk archive`: exact path first, then substring fallback
|
|
844
|
-
against full path or basename.
|
|
845
|
-
|
|
846
|
-
### Preset Aliases
|
|
176
|
+
Roadmaps roll up progress recursively and choose the first startable plan across
|
|
177
|
+
their child runlists. Runlists and roadmaps are held out of actionable plan
|
|
178
|
+
counts so dashboards do not double-count their children.
|
|
847
179
|
|
|
848
|
-
|
|
180
|
+
## Safety Model
|
|
849
181
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
182
|
+
- Mutation commands support `--dry-run` / `-n`.
|
|
183
|
+
- Managed writes are confined to configured document roots.
|
|
184
|
+
- Lifecycle and multi-file moves use atomic, conflict-aware mutation paths.
|
|
185
|
+
- Session ownership is durable local state, not inferred from telemetry.
|
|
186
|
+
- Passive orientation commands do not mutate repository state.
|
|
187
|
+
- Repository paths in machine and human output use stable slash-normalized
|
|
188
|
+
identities across supported operating systems.
|
|
856
189
|
|
|
857
|
-
|
|
190
|
+
## Command Reference
|
|
858
191
|
|
|
859
|
-
|
|
192
|
+
The CLI is the source of truth for command syntax and options:
|
|
860
193
|
|
|
861
194
|
```bash
|
|
862
|
-
dotmd
|
|
863
|
-
dotmd
|
|
864
|
-
dotmd
|
|
195
|
+
dotmd --help
|
|
196
|
+
dotmd help all
|
|
197
|
+
dotmd help statuses
|
|
198
|
+
dotmd <command> --help
|
|
865
199
|
```
|
|
866
200
|
|
|
867
|
-
|
|
201
|
+
Shell completion is generated from the same command registry:
|
|
868
202
|
|
|
869
203
|
```bash
|
|
870
|
-
dotmd
|
|
871
|
-
dotmd
|
|
872
|
-
dotmd diff --stat # summary stats only
|
|
873
|
-
dotmd diff --summarize # AI summary via local MLX model
|
|
204
|
+
eval "$(dotmd completions bash)"
|
|
205
|
+
eval "$(dotmd completions zsh)"
|
|
874
206
|
```
|
|
875
207
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
When `dotmd init` runs in a directory with existing `.md` files, it scans them and pre-populates the config with discovered statuses, surfaces, modules, and reference fields.
|
|
208
|
+
This README intentionally documents onboarding and concepts instead of
|
|
209
|
+
duplicating the complete command catalog.
|
|
879
210
|
|
|
880
211
|
## Configuration
|
|
881
212
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
### Rich status definitions (recommended)
|
|
885
|
-
|
|
886
|
-
Define each status as an object that co-locates all behavioral properties. Adding a new status is one line in one place — no need to update separate `lifecycle`, `staleDays`, `context`, or `taxonomy` sections.
|
|
213
|
+
Run `dotmd init` to create `dotmd.config.mjs`. A minimal typed configuration:
|
|
887
214
|
|
|
888
215
|
```js
|
|
889
|
-
export const root = 'docs
|
|
216
|
+
export const root = 'docs';
|
|
890
217
|
export const archiveDir = 'archived';
|
|
891
218
|
|
|
892
219
|
export const types = {
|
|
893
220
|
plan: {
|
|
894
221
|
statuses: {
|
|
895
|
-
'
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
222
|
+
'in-session': { context: 'expanded', staleDays: 1 },
|
|
223
|
+
active: { context: 'expanded', staleDays: 14 },
|
|
224
|
+
planned: { context: 'listed', staleDays: 30 },
|
|
225
|
+
archived: {
|
|
226
|
+
context: 'counted',
|
|
227
|
+
archive: true,
|
|
228
|
+
terminal: true,
|
|
229
|
+
skipStale: true,
|
|
230
|
+
skipWarnings: true,
|
|
231
|
+
},
|
|
899
232
|
},
|
|
900
233
|
},
|
|
901
234
|
};
|
|
902
235
|
```
|
|
903
236
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
| `context` | `'expanded'` \| `'listed'` \| `'counted'` | `'counted'` | Display mode in `dotmd context` |
|
|
909
|
-
| `staleDays` | `number` \| `null` | `null` | Days before doc is stale (`null` = never) |
|
|
910
|
-
| `requiresModule` | `boolean` | `false` | Require `module` in frontmatter |
|
|
911
|
-
| `terminal` | `boolean` | `false` | Skip `current_state`/`next_step` warnings |
|
|
912
|
-
| `archive` | `boolean` | `false` | Auto-move to `archiveDir` on transition |
|
|
913
|
-
| `skipStale` | `boolean` | `false` | Exempt from stale checks |
|
|
914
|
-
| `skipWarnings` | `boolean` | `false` | Exempt from validation warnings |
|
|
915
|
-
|
|
916
|
-
Object key order determines display order. The config resolver derives `statuses.order`, `lifecycle.*`, `taxonomy.moduleRequiredFor`, and `context.*` from these definitions. Explicit global sections still win when provided.
|
|
917
|
-
|
|
918
|
-
**Contradiction check.** Combining `skipStale: true` with a `staleDays` value, or `skipWarnings: true` with `requiresModule: true`, makes one of the fields dead config — the boolean wins silently. From 0.36.2, dotmd `warn()`s at config load when it spots either pair, naming the type, status, and conflicting fields. Drop one to silence the warning. The same check runs against the `quiet: true` sugar (which implies both `skipStale` and `skipWarnings` unless explicitly overridden).
|
|
919
|
-
|
|
920
|
-
### Array form (also supported)
|
|
921
|
-
|
|
922
|
-
The traditional array form remains fully backwards compatible:
|
|
923
|
-
|
|
924
|
-
```js
|
|
925
|
-
export const types = {
|
|
926
|
-
plan: {
|
|
927
|
-
statuses: ['active', 'planned', 'blocked', 'archived'],
|
|
928
|
-
context: { expanded: ['active'], listed: ['planned', 'blocked'], counted: ['archived'] },
|
|
929
|
-
staleDays: { active: 14, planned: 30, blocked: 30 },
|
|
930
|
-
},
|
|
931
|
-
};
|
|
932
|
-
|
|
933
|
-
// When using array form, define behavior in separate sections:
|
|
934
|
-
export const statuses = {
|
|
935
|
-
order: ['active', 'planned', 'blocked', 'archived'],
|
|
936
|
-
staleDays: { active: 14, planned: 30, blocked: 30 },
|
|
937
|
-
};
|
|
938
|
-
|
|
939
|
-
export const lifecycle = {
|
|
940
|
-
archiveStatuses: ['archived'],
|
|
941
|
-
skipStaleFor: ['archived'],
|
|
942
|
-
skipWarningsFor: ['archived'],
|
|
943
|
-
terminalStatuses: ['archived'],
|
|
944
|
-
// Types that archive into their own <typeDir>/<archiveDir> (e.g.
|
|
945
|
-
// docs/prompts/archived/) instead of the shared <root>/<archiveDir>.
|
|
946
|
-
// Defaults to ['prompt'] so session-local prompt churn doesn't bury
|
|
947
|
-
// plans and docs in the shared archive. Set to [] to disable.
|
|
948
|
-
archiveNestedTypes: ['prompt'],
|
|
949
|
-
};
|
|
950
|
-
|
|
951
|
-
export const taxonomy = {
|
|
952
|
-
moduleRequiredFor: ['active', 'planned', 'blocked'],
|
|
953
|
-
};
|
|
954
|
-
```
|
|
955
|
-
|
|
956
|
-
### Other config
|
|
957
|
-
|
|
958
|
-
```js
|
|
959
|
-
export const taxonomy = {
|
|
960
|
-
surfaces: ['web', 'ios', 'backend', 'api', 'platform'],
|
|
961
|
-
};
|
|
962
|
-
|
|
963
|
-
export const referenceFields = {
|
|
964
|
-
bidirectional: ['related_plans'], // warn if A→B but B↛A
|
|
965
|
-
unidirectional: ['supports_plans'], // one-way, no symmetry check
|
|
966
|
-
};
|
|
967
|
-
// Per-ref opt-out: prefix any value with `>` to mark that specific ref one-way
|
|
968
|
-
// without changing the field's default. Useful for leaf→upstream-parent refs
|
|
969
|
-
// (audits, hub docs) where a back-ref would force editing a stable parent.
|
|
970
|
-
// related_docs:
|
|
971
|
-
// - docs/sibling-design.md # bidirectional (default for the field)
|
|
972
|
-
// - "> docs/audit-beyond-platform.md" # one-way upstream — no back-ref expected
|
|
973
|
-
|
|
974
|
-
export const index = {
|
|
975
|
-
path: 'docs/docs.md',
|
|
976
|
-
startMarker: '<!-- GENERATED:dotmd:start -->',
|
|
977
|
-
endMarker: '<!-- GENERATED:dotmd:end -->',
|
|
978
|
-
snapshot: 'status', // default; use 'state' to include live current_state text
|
|
979
|
-
};
|
|
980
|
-
```
|
|
981
|
-
|
|
982
|
-
Generated indexes default to status-only rows for live sections so README files
|
|
983
|
-
do not become stale mirrors of volatile `current_state` text. Set
|
|
984
|
-
`snapshot: 'state'` if you want the older `Status Snapshot` table for live
|
|
985
|
-
sections too. Archived highlights still include their historical snapshots.
|
|
986
|
-
|
|
987
|
-
All exports are optional. Additional options: `context`, `display`, `presets`, `templates`, `excludeDirs`, `notion`. See [`dotmd.config.example.mjs`](dotmd.config.example.mjs) for the full reference.
|
|
988
|
-
|
|
989
|
-
Config discovery walks up from cwd looking for `dotmd.config.mjs` or `.dotmd.config.mjs`.
|
|
237
|
+
Configuration supports multiple roots, custom types and templates, taxonomy,
|
|
238
|
+
reference fields, presets, rendering, lifecycle hooks, validation hooks, and
|
|
239
|
+
AI summarization hooks. See [`dotmd.config.example.mjs`](dotmd.config.example.mjs)
|
|
240
|
+
for the complete annotated reference.
|
|
990
241
|
|
|
991
242
|
## Hooks
|
|
992
243
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
```js
|
|
998
|
-
export function validate(doc, ctx) {
|
|
999
|
-
const warnings = [];
|
|
1000
|
-
if (doc.status === 'active' && !doc.owner) {
|
|
1001
|
-
warnings.push({
|
|
1002
|
-
path: doc.path, level: 'warning',
|
|
1003
|
-
message: 'Active docs should have an owner.',
|
|
1004
|
-
});
|
|
1005
|
-
}
|
|
1006
|
-
return { errors: [], warnings };
|
|
1007
|
-
}
|
|
1008
|
-
```
|
|
1009
|
-
|
|
1010
|
-
### Render Hooks
|
|
1011
|
-
|
|
1012
|
-
Override any renderer by exporting a function that receives the default:
|
|
1013
|
-
|
|
1014
|
-
```js
|
|
1015
|
-
export function renderContext(index, defaultRenderer) {
|
|
1016
|
-
let output = defaultRenderer(index);
|
|
1017
|
-
return `# My Project\n\n${output}`;
|
|
1018
|
-
}
|
|
1019
|
-
```
|
|
1020
|
-
|
|
1021
|
-
Available: `renderContext`, `renderCompactList`, `renderCheck`, `renderGraph`, `renderStats`, `formatSnapshot`.
|
|
1022
|
-
|
|
1023
|
-
### Lifecycle Hooks
|
|
1024
|
-
|
|
1025
|
-
```js
|
|
1026
|
-
export function onArchive(doc, { oldPath, newPath }) {
|
|
1027
|
-
console.log(`Archived: ${oldPath} → ${newPath}`);
|
|
1028
|
-
}
|
|
1029
|
-
```
|
|
1030
|
-
|
|
1031
|
-
Available: `onArchive`, `onStatusChange`, `onTouch`, `onNew`, `onRename`, `onLint`.
|
|
1032
|
-
|
|
1033
|
-
### Transform Hooks
|
|
1034
|
-
|
|
1035
|
-
```js
|
|
1036
|
-
// Add computed fields to every doc after parsing
|
|
1037
|
-
export function transformDoc(doc) {
|
|
1038
|
-
doc.priority = doc.blockers?.length ? 'high' : 'normal';
|
|
1039
|
-
return doc;
|
|
1040
|
-
}
|
|
1041
|
-
```
|
|
1042
|
-
|
|
1043
|
-
### AI Hooks
|
|
1044
|
-
|
|
1045
|
-
```js
|
|
1046
|
-
// Override doc summarization (replaces local MLX model)
|
|
1047
|
-
export function summarizeDoc(body, meta) {
|
|
1048
|
-
return 'Custom summary for ' + meta.title;
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
// Override diff summarization
|
|
1052
|
-
export function summarizeDiff(diffOutput, filePath) {
|
|
1053
|
-
return `Changes in ${filePath}: ...`;
|
|
1054
|
-
}
|
|
1055
|
-
```
|
|
1056
|
-
|
|
1057
|
-
## Features
|
|
1058
|
-
|
|
1059
|
-
- **Git-aware** — detects frontmatter date drift vs git history, uses `git mv` for archives
|
|
1060
|
-
- **Dry-run everything** — preview any mutation with `--dry-run` / `-n`
|
|
1061
|
-
- **Multi-root** — manage docs across multiple directories with `--root` filtering
|
|
1062
|
-
- **Configurable** — statuses, taxonomy, lifecycle, validation rules, display, templates
|
|
1063
|
-
- **Hook system** — extend with JS functions, no plugin framework to learn
|
|
1064
|
-
- **AI-powered** — local MLX summaries for docs, queries, diffs, and context briefings
|
|
1065
|
-
- **Notion sync** — import, export, and bidirectional sync with Notion databases
|
|
1066
|
-
- **LLM-friendly** — `dotmd context` generates compact briefings for AI assistants
|
|
1067
|
-
- **Shell completion** — bash and zsh via `dotmd completions`
|
|
244
|
+
Functions exported from `dotmd.config.mjs` are detected as hooks. They can add
|
|
245
|
+
validation, customize rendering and summaries, or react to lifecycle events.
|
|
246
|
+
Hooks receive the resolved config and command context; mutation hooks participate
|
|
247
|
+
in the command's dry-run and failure contracts.
|
|
1068
248
|
|
|
1069
249
|
## License
|
|
1070
250
|
|