mdp — Markdown Project Management
A file-based project management CLI. Projects live in
directories containing markdown files with YAML frontmatter.
IMPORTANT: On first use
When you start working with an existing project,
immediately read to learn the project's valid statuses, types, labels, and priorities, and
read for project identity, health, and instructions. Do not guess or assume defaults — the config is the source of truth. Statuses are grouped by category (e.g.,
,
), and only the
field within each status is used in commands.
Installation
bash
bun install -g github:varunpandey0502/markdown-projects
See INSTALL.md for details.
Quick start
bash
mdp project create -p . --preset software
mdp issue create -p . -t "Fix login bug" --type bug --priority High
mdp issue list -p .
mdp issue update -p . --id ISS-1 -s "In Progress"
Project structure
.mdp/
├── settings.json # Schema config: statuses, priorities, labels, types (committed)
├── project.md # Project identity: title, description, instructions, health, log (committed)
├── issues/ # Flat directory of issue folders
│ └── ISS-1-add-auth/
│ └── ISS-1-add-auth.md
├── milestones/ # Flat directory of milestone folders
│ └── M-1-v1-release/
│ └── M-1-v1-release.md
├── docs/ # Documentation
└── templates/ # Issue/milestone templates
Configuration
Schema config is stored in
with entity-scoped objects:
json
{
"issues": {
"prefix": "ISS",
"statuses": {
"triage": [], "backlog": [...], "unstarted": [...],
"started": [...], "completed": [...], "canceled": []
},
"priorities": [...], "labels": [...], "types": [...]
},
"milestones": {
"prefix": "M",
"statuses": {
"backlog": [], "planned": [...], "in_progress": [...],
"completed": [...], "canceled": []
},
"priorities": [...], "labels": [...]
}
}
Project identity is stored in
with YAML frontmatter:
yaml
---
title: My Project
description: Optional one-line project description
instructions: Optional free-text guidance for LLMs and collaborators
health: on-track
log: []
createdAt: 2025-01-01T00:00:00.000Z
updatedAt: 2025-01-01T00:00:00.000Z
---
Statuses are grouped by
status category (lifecycle stage). Each category maps to an array of
status objects. The system uses categories (not status names) to determine completion, overdue detection, etc.
Issues and milestones have independent statuses, priorities, and labels.
At runtime,
is the sole source of truth for schema config — no merge layers.
At project creation: preset (built-in or custom from
) → CLI flag overrides → written to
.
Custom presets and default preferences (default preset, output format) are stored in
.
Global options
Every command accepts:
(project path),
(output format),
(quiet),
(verbose).
All output is JSON:
{ "ok": true, "data": {...} }
or
{ "ok": false, "error": {...} }
.
Commands
Project management
mdp project create -p <path> [--preset <name>] [-F|--force] [--with-templates] [--no-with-templates] [--issue-prefix <prefix>] [--milestone-prefix <prefix>] [--tags <tags>] [--title <title>] [--description <desc>] [--instructions <text>]
— Create a new project (presets: software, marketing, design, product, social-media, generic)
mdp project get -p <path> [--no-include-content]
— Get project identity, health, log, and body
mdp project settings -p <path>
— Show project schema settings
mdp project stats -p <path>
— Project statistics
mdp project fix -p <path> [--dry-run]
— Fix folder structure to match frontmatter
Project log
Manage log entries on the project (stored in
):
mdp project log add -p <path> -b "message" [--author <name>] [--health on-track|at-risk|off-track] [--dry-run]
mdp project log list -p <path>
mdp project log get -p <path> --index <n>
mdp project log update -p <path> --index <n> [-b <body>] [--author <name>] [--health <health>] [--dry-run]
mdp project log delete -p <path> --index <n> [--dry-run]
Project registry
mdp project list [--tag <tag>]
— List registered projects (includes in output)
mdp project add <path> [--tags <tags>]
mdp project remove <path>
mdp project tag <path> --add <tags> | --remove <tags>
— Tags added via are auto-created globally
Tags
Manage tags globally via
:
- — List all tags with descriptions and project counts
mdp tag add <tag> [-d "description"]
— Create a tag (description defaults to )
mdp tag update <tag> -d "description"
— Update a tag's description
mdp tag remove <tag> [--force]
— Remove a tag; to also strip from projects
Issues
Create
mdp issue create -p <path> -t "Title" [options]
| Option | Description |
|---|
| Issue title (required) |
| Issue type (config-driven) |
| Initial status (default: first status in config) |
| Priority level (default: null) |
| Comma-separated labels |
-a, --assignee <assignee>
| Assignee identifier |
-m, --milestone <milestone>
| Milestone ID |
-e, --estimate <estimate>
| Effort points (positive integer) |
| Actual effort spent |
| Due date (YYYY-MM-DD) |
| Comma-separated issue IDs |
| Parent issue ID |
| Comma-separated issue IDs |
| Comma-separated checklist items |
| Short description |
| Full markdown body (or for stdin) |
| Template name from .mdp/templates/ |
| Preview without creating |
List
mdp issue list -p <path> [options]
| Option | Description |
|---|
| Comma-separated status filter |
| Comma-separated type filter |
| Priority filter |
| Comma-separated labels filter |
-a, --assignee <assignee>
| Filter by assignee ( for unassigned) |
-m, --milestone <milestone>
| Filter by milestone ID ( for unassigned) |
| for blocked only, for unblocked |
| Filter by parent issue ID ( for top-level) |
| Created after date (YYYY-MM-DD) |
| Created before date (YYYY-MM-DD) |
| Due before date (YYYY-MM-DD) |
| Due after date (YYYY-MM-DD) |
| Sort: id, title, status, priority, type, created, updated, estimate, spent, dueDate (default: id) |
| Sort order: asc, desc (default: asc) |
Get
mdp issue get -p <path> --id <id> [--no-include-content]
Update
mdp issue update -p <path> --id <id> [options]
| Option | Description |
|---|
| New title |
| New type |
| New status |
| New priority |
-a, --assignee <assignee>
| Set assignee ( to clear) |
-m, --milestone <milestone>
| Set milestone ( to clear) |
-e, --estimate <estimate>
| Set estimate ( to clear) |
| Set spent ( to clear) |
| Set due date ( to clear) |
| Set parent issue ( to clear) |
| Set labels (replaces all) |
| Add labels |
| Remove labels |
| Set blockedBy (replaces all) |
| Add to blockedBy (with cycle detection) |
--remove-blocked-by <ids>
| Remove from blockedBy |
| Set relatedTo (replaces all) |
| Add to relatedTo |
--remove-related-to <ids>
| Remove from relatedTo |
| Add checklist items |
--remove-checklist <items>
| Remove checklist items by text |
| Check items by text |
| Uncheck items by text |
| Replace markdown body |
| Preview without writing |
Delete
mdp issue delete -p <path> --id <id> [--dry-run]
Cleans up blockedBy, relatedTo, and parent references in other issues.
Log
Manage log entries on an issue.
mdp issue log add -p <path> --id <id> -b "message" [--author <name>] [--dry-run]
mdp issue log list -p <path> --id <id>
mdp issue log get -p <path> --id <id> --index <n>
mdp issue log update -p <path> --id <id> --index <n> [--author <a>] [-b <body>] [--dry-run]
mdp issue log delete -p <path> --id <id> --index <n> [--dry-run]
Batch create
Create multiple issues at once by piping a JSON array via stdin. Processes items sequentially; continues on error and reports per-item success/failure.
echo '<json-array>' | mdp issue batch-create -p <path> [--dry-run]
Input: JSON array of issue objects. Required field:
. All other fields match
options (
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
). Array fields (
,
, etc.) accept
.
Output:
json
{
"ok": true,
"data": {
"total": 3, "succeeded": 2, "failed": 1,
"results": [
{ "ok": true, "data": { "id": "ISS-4", "title": "...", "filePath": "..." } },
{ "ok": false, "error": { "code": "INVALID_STATUS", "message": "...", "index": 2 } }
]
}
}
Exits with code 1 if any item failed.
Batch update
Update multiple issues at once by piping a JSON array via stdin. Processes items sequentially; continues on error and reports per-item success/failure. In-memory state is refreshed after each successful update for accurate cycle detection.
echo '<json-array>' | mdp issue batch-update -p <path> [--dry-run]
Input: JSON array of update objects. Required field:
. All other fields match
options (
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
). Array fields accept
.
Output: Same envelope format as batch-create, with
and
per item.
Exits with code 1 if any item failed.
Milestones
Create
mdp milestone create -p <path> -t "Title" [options]
| Option | Description |
|---|
| Milestone title (required) |
| Initial status (default: first status in config) |
| Priority level (default: null) |
| Comma-separated labels |
| Start date (YYYY-MM-DD) |
| Due date (YYYY-MM-DD) |
| Comma-separated checklist items |
| Short description |
| Full markdown body |
| Template name from .mdp/templates/ |
| Preview without creating |
List
mdp milestone list -p <path> [options]
| Option | Description |
|---|
| Comma-separated status filter |
| Priority filter |
| Comma-separated labels filter |
| for overdue only, for not overdue |
| Sort: id, title, status, priority, created, updated, dueDate, completion (default: id) |
| Sort order: asc, desc (default: asc) |
Get
mdp milestone get -p <path> --id <id> [--no-include-content]
Update
mdp milestone update -p <path> --id <id> [options]
| Option | Description |
|---|
| New title |
| New status |
| New priority |
| Set start date ( to clear) |
| Set due date ( to clear) |
| Set labels (replaces all) |
| Add labels |
| Remove labels |
| Add checklist items |
--remove-checklist <items>
| Remove checklist items by text |
| Check items by text |
| Uncheck items by text |
| Replace markdown body |
| Preview without writing |
Delete
mdp milestone delete -p <path> --id <id> [--dry-run]
Clears milestone reference from all assigned issues.
Progress
mdp milestone progress -p <path> --id <id>
Returns completion percentage, issue counts by status, and list of assigned issues.
Log
Manage log entries on a milestone.
mdp milestone log add -p <path> --id <id> -b "message" [--author <name>] [--dry-run]
mdp milestone log list -p <path> --id <id>
mdp milestone log get -p <path> --id <id> --index <n>
mdp milestone log update -p <path> --id <id> --index <n> [--author <a>] [-b <body>] [--dry-run]
mdp milestone log delete -p <path> --id <id> --index <n> [--dry-run]
Search
mdp search -p <path> -q "query text" [--limit <n>]
Searches project, issues, and milestones by text content using BM25 ranking. Returns matched fields with snippets. Use
to search only the project file.
Workflow recommendations
- Start with
mdp project create -p . --preset software
to set up a project
- Create issues with — use and for organization
- For bulk operations, use and with JSON arrays piped via stdin
- Move issues through statuses with
mdp issue update --id ISS-1 -s "In Progress"
- Group work into milestones, then track with
- Use with filters to find relevant issues
See WORKFLOWS.md for detailed workflow patterns.
References
- INSTALL.md — Installation guide
- WORKFLOWS.md — Detailed workflow patterns
- PROJECTS.md — Project commands reference
- ISSUES.md — Issue commands reference
- MILESTONES.md — Milestone commands reference
- PROJECT-CONFIG.md — Project configuration reference
- GLOBAL-CONFIG.md — Global configuration reference