dotmd-cli 0.28.0 → 0.28.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 +73 -21
- package/dotmd.config.example.mjs +54 -1
- package/package.json +1 -1
- package/src/init.mjs +5 -3
- package/src/new.mjs +17 -3
package/README.md
CHANGED
|
@@ -89,19 +89,19 @@ Every document can have a `type` field in its frontmatter. Types determine which
|
|
|
89
89
|
| Type | Purpose | Valid Statuses |
|
|
90
90
|
|------|---------|----------------|
|
|
91
91
|
| `plan` | Execution plans | `in-session`, `active`, `planned`, `blocked`, `partial`, `paused`, `awaiting`, `queued-after`, `archived` |
|
|
92
|
-
| `doc` | Design docs, specs, ADRs, RFCs | `draft`, `active`, `review`, `reference`, `deprecated`, `archived` |
|
|
93
|
-
| `
|
|
92
|
+
| `doc` | Design docs, specs, ADRs, RFCs, reference material | `draft`, `active`, `review`, `reference`, `deprecated`, `archived` |
|
|
93
|
+
| `prompt` | Saved prompts that seed future Claude sessions | `pending`, `claimed`, `archived` |
|
|
94
94
|
|
|
95
95
|
Documents without a `type` field use the global `statuses.order` from config.
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
`dotmd new <type> <name>` sets the `type:` field automatically (`plan`, `doc`, or `prompt`).
|
|
98
98
|
|
|
99
99
|
Filter by type with `--type`:
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
102
|
dotmd query --type plan --status active # active plans
|
|
103
103
|
dotmd list --type doc # all docs
|
|
104
|
-
dotmd export --type
|
|
104
|
+
dotmd export --type prompt # export only saved prompts
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
Customize types and their statuses in config with the `types` key. See [`dotmd.config.example.mjs`](dotmd.config.example.mjs).
|
|
@@ -150,7 +150,6 @@ dotmd hud Three-line actionable triage (silent when clean —
|
|
|
150
150
|
dotmd pickup <file> Pick up a plan (in-session + print body or queued handoff)
|
|
151
151
|
dotmd release [<file>] Release in-session lease (alias: unpickup)
|
|
152
152
|
dotmd handoff <file> [...] Queue a resume-prompt sidecar + release
|
|
153
|
-
dotmd finish <file> Finish a plan (done or active)
|
|
154
153
|
dotmd status <file> <status> Transition document status
|
|
155
154
|
dotmd archive <file> Archive (status + move + update refs)
|
|
156
155
|
dotmd bulk archive <files> Archive multiple files at once
|
|
@@ -167,7 +166,8 @@ dotmd summary <file> AI summary of a document
|
|
|
167
166
|
dotmd glossary <term> Look up domain terms + related docs
|
|
168
167
|
dotmd watch [command] Re-run a command on file changes
|
|
169
168
|
dotmd diff [file] Show changes since last updated date
|
|
170
|
-
dotmd new <name>
|
|
169
|
+
dotmd new <type> <name> Create a new doc (type: doc, plan, or prompt)
|
|
170
|
+
dotmd prompts [sub] List, claim, or archive saved prompts
|
|
171
171
|
dotmd init Create starter config + docs directory
|
|
172
172
|
dotmd completions <shell> Output shell completion script (bash, zsh)
|
|
173
173
|
```
|
|
@@ -197,32 +197,80 @@ dotmd query --status active --summarize --summarize-limit 3
|
|
|
197
197
|
|
|
198
198
|
Flags: `--type`, `--status`, `--keyword`, `--module`, `--surface`, `--domain`, `--owner`, `--updated-since`, `--stale`, `--has-next-step`, `--has-blockers`, `--checklist-open`, `--sort`, `--limit`, `--all`, `--git`, `--json`, `--summarize`, `--summarize-limit`, `--model`.
|
|
199
199
|
|
|
200
|
-
###
|
|
200
|
+
### Create Documents
|
|
201
|
+
|
|
202
|
+
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`.
|
|
201
203
|
|
|
202
204
|
```bash
|
|
203
|
-
dotmd new
|
|
204
|
-
dotmd new
|
|
205
|
-
dotmd new my-
|
|
206
|
-
dotmd new
|
|
207
|
-
dotmd new my-
|
|
208
|
-
dotmd new my-
|
|
209
|
-
dotmd new
|
|
210
|
-
dotmd new my-doc --root modules # create in a specific root
|
|
211
|
-
dotmd new --list-templates # show all available templates
|
|
205
|
+
dotmd new plan auth-revamp # type: plan → docs/plans/auth-revamp.md
|
|
206
|
+
dotmd new doc token-refresh-design # type: doc → docs/token-refresh-design.md
|
|
207
|
+
dotmd new my-feature # implicit type: doc
|
|
208
|
+
dotmd new plan auth --status planned # initial status override
|
|
209
|
+
dotmd new doc my-doc --title "Custom Title" # title override
|
|
210
|
+
dotmd new doc my-doc --root modules # create in a specific root
|
|
211
|
+
dotmd new --list-types # show registered types
|
|
212
212
|
```
|
|
213
213
|
|
|
214
|
-
|
|
214
|
+
Each built-in type has a template baked in:
|
|
215
|
+
|
|
216
|
+
| Type | Default destination | Shape |
|
|
217
|
+
|------|---------------------|-------|
|
|
218
|
+
| `plan` | `docs/plans/<slug>.md` | Problem → Phases → Closeout, with phase status markers and Version History |
|
|
219
|
+
| `doc` | `docs/<slug>.md` | Overview → Version History → Related (build-up shape lite) |
|
|
220
|
+
| `prompt` | `docs/prompts/<slug>.md` | Body is required (see [Saved Prompts](#saved-prompts)) |
|
|
221
|
+
|
|
222
|
+
Add custom types via `templates` in your config:
|
|
215
223
|
|
|
216
224
|
```js
|
|
217
225
|
export const templates = {
|
|
218
226
|
spike: {
|
|
219
227
|
description: 'Timeboxed investigation',
|
|
220
|
-
|
|
228
|
+
defaultStatus: 'active',
|
|
229
|
+
targetRoot: 'spikes', // in flat-array root configs, lands in the matching root
|
|
230
|
+
dir: 'spikes', // in single-root configs, creates docs/spikes/<slug>.md
|
|
231
|
+
frontmatter: (status, today) => `type: spike\nstatus: ${status}\nupdated: ${today}\ntimebox: 2d`,
|
|
221
232
|
body: (title) => `\n# ${title}\n\n## Hypothesis\n\n\n\n## Findings\n\n\n`,
|
|
222
233
|
},
|
|
223
234
|
};
|
|
224
235
|
```
|
|
225
236
|
|
|
237
|
+
Then `dotmd new spike my-spike` creates a doc from your template.
|
|
238
|
+
|
|
239
|
+
**Routing your custom type to a directory.** Two knobs:
|
|
240
|
+
|
|
241
|
+
- `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.
|
|
242
|
+
- `dir: '<subdir>'` — subdirectory under `config.docsRoot`. Used as the fallback when `targetRoot` doesn't match anything (typical single-root layout).
|
|
243
|
+
|
|
244
|
+
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.
|
|
245
|
+
|
|
246
|
+
### Saved Prompts
|
|
247
|
+
|
|
248
|
+
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.
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
dotmd new prompt cleanup-tomorrow "look at remaining lint warnings"
|
|
252
|
+
dotmd new prompt resume-foo - <<'EOF'
|
|
253
|
+
multi-line
|
|
254
|
+
prompt body
|
|
255
|
+
EOF
|
|
256
|
+
dotmd new prompt from-file @/tmp/draft.md
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Manage them with the `prompts` command family:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
dotmd prompts # list pending prompts (default)
|
|
263
|
+
dotmd prompts list --all # all statuses
|
|
264
|
+
dotmd prompts next # show + claim the oldest pending prompt (prints body)
|
|
265
|
+
dotmd prompts use <file> # claim a specific prompt (prints body, flips to claimed)
|
|
266
|
+
dotmd prompts archive <file> # archive a prompt
|
|
267
|
+
dotmd prompts new <name> [body] # alias for `dotmd new prompt`
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
`dotmd hud` surfaces pending prompts on session start (alongside held leases and queued handoffs), so a saved prompt acts as a self-addressed reminder: write it now, the next session sees it.
|
|
271
|
+
|
|
272
|
+
Statuses: `pending` (drafted, awaiting a session), `claimed` (consumed by a session), `archived`.
|
|
273
|
+
|
|
226
274
|
### Check & Fix
|
|
227
275
|
|
|
228
276
|
```bash
|
|
@@ -397,14 +445,18 @@ dotmd bulk archive docs/old-a.md docs/old-b.md # archive multiple
|
|
|
397
445
|
dotmd bulk archive docs/old-*.md -n # preview
|
|
398
446
|
```
|
|
399
447
|
|
|
400
|
-
### Pickup &
|
|
448
|
+
### Pickup & Closeout
|
|
401
449
|
|
|
402
450
|
```bash
|
|
403
451
|
dotmd pickup docs/plans/my-plan.md # set in-session + print body (or queued handoff)
|
|
404
|
-
dotmd
|
|
405
|
-
dotmd
|
|
452
|
+
dotmd archive docs/plans/my-plan.md # fully shipped: archive + auto-release lease
|
|
453
|
+
dotmd release docs/plans/my-plan.md # need more work: release lease, flip to prior status
|
|
454
|
+
dotmd status docs/plans/my-plan.md partial # shipped + tail deferred (reference successors in body)
|
|
455
|
+
dotmd status docs/plans/my-plan.md awaiting # stuck on a human decision
|
|
406
456
|
```
|
|
407
457
|
|
|
458
|
+
`finish` is a legacy command that defaults to `status: done`, which is no longer in the default plan vocabulary as of 0.16. Use `archive` (fully shipped) or `release` + `status` (anything else). If you need it back, add `done` to `types.plan.statuses` in your config.
|
|
459
|
+
|
|
408
460
|
### Handoff (resume-prompts attached to plans)
|
|
409
461
|
|
|
410
462
|
When you're stopping mid-work and the next session will need to pick up where
|
package/dotmd.config.example.mjs
CHANGED
|
@@ -14,7 +14,7 @@ export const archiveDir = 'archived';
|
|
|
14
14
|
export const excludeDirs = ['evidence'];
|
|
15
15
|
|
|
16
16
|
// Document types — each type has its own status vocabulary and context layout.
|
|
17
|
-
// Defaults: plan, doc,
|
|
17
|
+
// Defaults: plan, doc, prompt. Override to customize statuses per type, or add new types.
|
|
18
18
|
//
|
|
19
19
|
// Statuses can be defined as an array (names only) or as an object (rich form).
|
|
20
20
|
// The object form co-locates all behavioral properties with each status,
|
|
@@ -66,6 +66,15 @@ export const excludeDirs = ['evidence'];
|
|
|
66
66
|
// 'archived': { context: 'counted', archive: true, terminal: true, quiet: true },
|
|
67
67
|
// },
|
|
68
68
|
// },
|
|
69
|
+
// prompt: {
|
|
70
|
+
// // Saved prompts that seed future Claude sessions. `dotmd hud` surfaces
|
|
71
|
+
// // pending prompts on session start; `dotmd prompts next` claims the oldest.
|
|
72
|
+
// statuses: {
|
|
73
|
+
// 'pending': { context: 'expanded', staleDays: 30 },
|
|
74
|
+
// 'claimed': { context: 'counted', quiet: true },
|
|
75
|
+
// 'archived': { context: 'counted', archive: true, terminal: true, quiet: true },
|
|
76
|
+
// },
|
|
77
|
+
// },
|
|
69
78
|
// };
|
|
70
79
|
|
|
71
80
|
// ─── Array form (also supported) ────────────────────────────────────────────
|
|
@@ -166,6 +175,50 @@ export const presets = {
|
|
|
166
175
|
mine: ['--owner', 'robert', '--status', 'active', '--all'],
|
|
167
176
|
};
|
|
168
177
|
|
|
178
|
+
// ─── Templates ───────────────────────────────────────────────────────────────
|
|
179
|
+
// Define new types or override builtins. `dotmd new <type> <name>` looks here first.
|
|
180
|
+
//
|
|
181
|
+
// Properties:
|
|
182
|
+
// description: string — shown in `dotmd new --list-types`
|
|
183
|
+
// defaultStatus: string — initial status if `--status` not passed
|
|
184
|
+
// requiresBody: boolean — error if no body input (see `prompt` builtin)
|
|
185
|
+
// targetRoot: string — name (basename or suffix) of the root this type lives in.
|
|
186
|
+
// In flat-array `root` configs (e.g. ['docs/plans', 'docs/prompts']),
|
|
187
|
+
// the new doc lands in the matching root. Falls back to `config.docsRoot`
|
|
188
|
+
// if no root matches. The `--root` CLI flag still overrides.
|
|
189
|
+
// dir: string — subdirectory under `docsRoot` to use when `targetRoot` doesn't
|
|
190
|
+
// match anything. Builtin `plan` and `prompt` set both for portability:
|
|
191
|
+
// under `docsRoot='docs'`, `dir` puts files in `docs/plans/` and `docs/prompts/`;
|
|
192
|
+
// under flat-array roots, `targetRoot` routes directly to the type-specific root.
|
|
193
|
+
// frontmatter: (status, isoTime, ctx) => string
|
|
194
|
+
// body: (title, ctx) => string
|
|
195
|
+
//
|
|
196
|
+
// Custom type example — adds a `spike` type that lives in the `spikes` root (or
|
|
197
|
+
// under `docs/spikes/` in single-root layouts):
|
|
198
|
+
// export const templates = {
|
|
199
|
+
// spike: {
|
|
200
|
+
// description: 'Timeboxed investigation',
|
|
201
|
+
// defaultStatus: 'active',
|
|
202
|
+
// targetRoot: 'spikes', // for flat-array root configs
|
|
203
|
+
// dir: 'spikes', // for single-root configs
|
|
204
|
+
// frontmatter: (s, d) => `type: spike\nstatus: ${s}\ncreated: ${d}\ntimebox: 2d`,
|
|
205
|
+
// body: (t) => `\n# ${t}\n\n## Hypothesis\n\n\n\n## Findings\n\n`,
|
|
206
|
+
// },
|
|
207
|
+
//
|
|
208
|
+
// // Override a builtin (e.g. project-specific prompt frontmatter shape).
|
|
209
|
+
// // IMPORTANT: builtin properties are NOT inherited — re-declare `targetRoot`, `dir`,
|
|
210
|
+
// // `defaultStatus`, `requiresBody`, etc. that you want preserved.
|
|
211
|
+
// // prompt: {
|
|
212
|
+
// // description: 'Project resume prompt',
|
|
213
|
+
// // defaultStatus: 'pending',
|
|
214
|
+
// // requiresBody: true,
|
|
215
|
+
// // targetRoot: 'prompts',
|
|
216
|
+
// // dir: 'prompts',
|
|
217
|
+
// // frontmatter: (s, d, ctx) => `type: prompt\nstatus: ${s}\nproject_field: yes`,
|
|
218
|
+
// // body: (t, ctx) => `\n${ctx?.bodyInput ?? ''}\n`,
|
|
219
|
+
// // },
|
|
220
|
+
// };
|
|
221
|
+
|
|
169
222
|
// ─── Notion ──────────────────────────────────────────────────────────────────
|
|
170
223
|
// IMPORTANT: Use environment variables for tokens — never hardcode secrets in config files.
|
|
171
224
|
// export const notion = {
|
package/package.json
CHANGED
package/src/init.mjs
CHANGED
|
@@ -169,7 +169,9 @@ export function runInit(cwd, config) {
|
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
process.stdout.write(`\nReady.
|
|
173
|
-
process.stdout.write(` dotmd new my-doc\n`);
|
|
174
|
-
process.stdout.write(` dotmd
|
|
172
|
+
process.stdout.write(`\nReady. A few starting points:\n`);
|
|
173
|
+
process.stdout.write(` dotmd new doc my-doc # scaffold a reference doc\n`);
|
|
174
|
+
process.stdout.write(` dotmd new plan my-plan # scaffold an execution plan\n`);
|
|
175
|
+
process.stdout.write(` dotmd list # see what you've got\n`);
|
|
176
|
+
process.stdout.write(` dotmd hud # session-start triage (ideal SessionStart hook)\n\n`);
|
|
175
177
|
}
|
package/src/new.mjs
CHANGED
|
@@ -44,6 +44,7 @@ const BUILTIN_TEMPLATES = {
|
|
|
44
44
|
plan: {
|
|
45
45
|
description: 'Execution plan — build-up shape (Problem → Phases → Closeout) with phase status markers and Version History',
|
|
46
46
|
dir: 'plans',
|
|
47
|
+
targetRoot: 'plans',
|
|
47
48
|
defaultStatus: 'active',
|
|
48
49
|
frontmatter: (s, d) => [
|
|
49
50
|
'type: plan',
|
|
@@ -124,6 +125,7 @@ Status markers (put in heading text):
|
|
|
124
125
|
prompt: {
|
|
125
126
|
description: 'Saved prompt to seed a future Claude session — body is required',
|
|
126
127
|
dir: 'prompts',
|
|
128
|
+
targetRoot: 'prompts',
|
|
127
129
|
defaultStatus: 'pending',
|
|
128
130
|
requiresBody: true,
|
|
129
131
|
frontmatter: (s, d, ctx) => [
|
|
@@ -244,8 +246,11 @@ export async function runNew(argv, config, opts = {}) {
|
|
|
244
246
|
// Title
|
|
245
247
|
const docTitle = title ?? namePart.replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
246
248
|
|
|
247
|
-
// Resolve target root
|
|
249
|
+
// Resolve target root. Precedence: CLI --root > template.targetRoot > config.docsRoot.
|
|
250
|
+
// When the chosen root is a first-class type-container (matched by --root or targetRoot),
|
|
251
|
+
// we skip the `template.dir` join — the root already points at the right directory.
|
|
248
252
|
let targetRoot = config.docsRoot;
|
|
253
|
+
let routedToTypeRoot = false;
|
|
249
254
|
if (rootName) {
|
|
250
255
|
const roots = config.docsRoots || [config.docsRoot];
|
|
251
256
|
const match = roots.find(r => r.endsWith(rootName) || path.basename(r) === rootName);
|
|
@@ -254,10 +259,19 @@ export async function runNew(argv, config, opts = {}) {
|
|
|
254
259
|
die(`Unknown root: ${rootName}\nAvailable: ${available}`);
|
|
255
260
|
}
|
|
256
261
|
targetRoot = match;
|
|
262
|
+
routedToTypeRoot = true;
|
|
263
|
+
} else if (typeof template === 'object' && template.targetRoot) {
|
|
264
|
+
const roots = config.docsRoots || [config.docsRoot];
|
|
265
|
+
const match = roots.find(r => r.endsWith(template.targetRoot) || path.basename(r) === template.targetRoot);
|
|
266
|
+
if (match) {
|
|
267
|
+
targetRoot = match;
|
|
268
|
+
routedToTypeRoot = true;
|
|
269
|
+
}
|
|
257
270
|
}
|
|
258
271
|
|
|
259
|
-
// Template-declared subdirectory (e.g., prompt → 'prompts')
|
|
260
|
-
|
|
272
|
+
// Template-declared subdirectory (e.g., prompt → 'prompts') — only relevant when we
|
|
273
|
+
// didn't already land in a type-specific root via --root or targetRoot.
|
|
274
|
+
if (typeof template === 'object' && template.dir && !nameDir && !routedToTypeRoot) {
|
|
261
275
|
nameDir = path.join(path.relative(config.repoRoot, targetRoot), template.dir);
|
|
262
276
|
}
|
|
263
277
|
|