Skip to Content
Issues

Issues

Every issue is a markdown file with YAML frontmatter.

Frontmatter Fields

FieldTypeDefaultDescription
idstringAuto-generatedUnique identifier (e.g., ISS-1)
titlestringIssue title
typestring | nullnullIssue type (validated against config when provided)
statusstringFirst status in configWorkflow stage (validated against config)
prioritystring | nullnullUrgency level (validated against config when provided)
labelsstring[][]Tags for filtering (validated against config)
assigneestring | nullnullWho is working on this (freeform)
milestonestring | nullnullMilestone ID (validated exists)
estimatenumber | nullnullEffort points
spentnumber | nullnullActual effort spent
dueDatestring | nullnullISO 8601 date
blockedBystring[][]Issue IDs this depends on (cycles rejected)
parentstring | nullnullParent issue ID
relatedTostring[][]Related issue IDs
checklistobject[][]Acceptance criteria ({text, done})
logobject[][]Activity log ({timestamp, author, body})
createdAtstringAuto-generatedISO 8601 timestamp
updatedAtstringAuto-generatedISO 8601 timestamp

Example Issue File

--- id: ISS-2 title: Implement JWT tokens type: task status: In Progress priority: High labels: - backend - security assignee: agent-1 milestone: M-1 estimate: 5 spent: 4 dueDate: 2025-03-15 blockedBy: [] parent: ISS-1 relatedTo: - ISS-3 checklist: - text: Access token generation done: true - text: Refresh token rotation done: true - text: Token revocation endpoint done: false log: - timestamp: "2025-03-01T10:00:00.000Z" author: agent-1 body: Created issue. Planning JWT implementation with RS256 signing. - timestamp: "2025-03-02T14:30:00.000Z" author: agent-1 body: Access and refresh token logic complete. createdAt: 2025-03-01T10:00:00.000Z updatedAt: 2025-03-02T14:30:00.000Z --- ## Description Implement JWT-based authentication with access and refresh tokens. Uses RS256 signing algorithm with 15-minute access token expiry and 7-day refresh token rotation.

File Naming

ID Generation

IDs are sequential within their type and project:

  • Issues: {issuePrefix}-{N} — e.g., ISS-1, ISS-2
  • Milestones: {milestonePrefix}-{N} — e.g., M-1, M-2

The next ID is determined by scanning all existing items and incrementing the highest number found.

Slug Generation

Slugs are derived from the title:

  1. Convert to lowercase
  2. Replace non-alphanumeric characters with hyphens
  3. Collapse consecutive hyphens
  4. Trim leading/trailing hyphens
  5. Truncate to 50 characters (on a word boundary if possible)

Examples:

"Add user authentication" → add-user-authentication "Fix bug #42 — login" → fix-bug-42-login "API v2.0 (beta)" → api-v2-0-beta

Naming Convention

Each issue/milestone lives in its own folder:

{id}-{slug}/{id}-{slug}.md
  • ISS-1-implement-auth/ISS-1-implement-auth.md
  • M-3-q2-release/M-3-q2-release.md

The {id} portion uses the prefix from project config (issues.prefix or milestones.prefix). Since the prefix is embedded in every path, it cannot be changed after project creation without renaming all existing files.

Log Entries

Activity is tracked via the log frontmatter array:

log: - timestamp: "2025-03-01T10:00:00.000Z" author: agent-1 body: Started implementation. Using RS256 for JWT signing. - timestamp: "2025-03-02T09:15:00.000Z" author: agent-2 body: "Code review: looks good."

All activity lives in frontmatter — the markdown body is reserved for free-form prose only.

Create Issue

mdp issue create -t "Issue title"

Create a new issue.

FlagShortDefaultDescription
--title-trequiredIssue title
--typenullIssue type (must match configured type)
--status-sFirst status in configInitial status
--prioritynullPriority level
--labels-l[]Comma-separated labels
--assignee-anullAssignee identifier
--milestone-mnullMilestone ID
--estimate-enullEffort points (positive integer)
--spentnullActual effort spent
--due-datenullDue date (YYYY-MM-DD)
--blocked-by[]Comma-separated issue IDs
--parentnullParent issue ID
--related-to[]Comma-separated issue IDs
--checklist[]Comma-separated checklist items (all start unchecked)
--description-d""Short description (added as first paragraph)
--content-c""Full markdown body (or - for stdin)
--templatenullTemplate name from .mdp/templates/
--dry-runfalsePreview without creating

Behavior:

  • Generates the next sequential ID
  • Creates {id}-{slug}/ directory inside issues/
  • Writes {id}-{slug}.md with frontmatter and body content
  • Validates status, priority, labels, and type against config
  • Validates blockedBy, parent, and relatedTo IDs exist
  • Rejects changes that would create a cycle (CIRCULAR_DEPENDENCY error)
{ "ok": true, "data": { "id": "ISS-1", "title": "Authentication", "type": "feature", "status": "Backlog", "priority": "High", "labels": ["backend"], "assignee": "agent-1", "milestone": "M-1", "estimate": 13, "spent": null, "dueDate": null, "blockedBy": [], "parent": null, "relatedTo": [], "checklist": [], "log": [], "createdAt": "2025-03-01T10:00:00.000Z", "updatedAt": "2025-03-01T10:00:00.000Z", "filePath": ".mdp/issues/ISS-1-authentication/ISS-1-authentication.md" } }

The actual .md file written to disk:

--- id: ISS-1 title: Authentication type: feature status: Backlog priority: High labels: - backend assignee: agent-1 milestone: M-1 estimate: 13 spent: null dueDate: null blockedBy: [] parent: null relatedTo: [] checklist: [] log: [] createdAt: 2025-03-01T10:00:00.000Z updatedAt: 2025-03-01T10:00:00.000Z ---

List Issues

mdp issue list

List issues with filtering and sorting.

FlagShortDefaultDescription
--status-sallComma-separated status filter
--typeallComma-separated type filter
--priorityallPriority filter
--labels-lallComma-separated labels (issues must have at least one)
--assignee-aallFilter by assignee (none for unassigned)
--milestone-mallFilter by milestone ID (none for unassigned)
--blockedalltrue for only blocked, false for only unblocked
--parentallFilter by parent ID (none for top-level only)
--sort"id"Sort field: id, title, status, priority, type, created, updated, estimate, spent, dueDate
--order"asc"Sort order: asc, desc
--created-afterFilter: created after date (YYYY-MM-DD)
--created-beforeFilter: created before date
--due-beforeFilter: due before date
--due-afterFilter: due after date

Get Issue

mdp issue get --id ISS-1

Get a single issue by ID with full details including markdown body.

FlagShortDefaultDescription
--idrequiredIssue ID
--include-contenttrueInclude markdown body in output

Returns all frontmatter fields plus content and filePath.

Additionally, the following computed fields are calculated at read time (not stored in the file):

FieldTypeDescription
blocksstring[]Inverse of blockedBy — which issues are blocked by this one
childrenstring[]Inverse of parent — which issues have this as parent
checklistTotalnumberCount of checklist items
checklistCheckednumberCount of checked items
checklistProgressnumber | nullPercentage (0-100), null if empty

Update Issue

mdp issue update --id ISS-1 --status "In Progress"

Update one or more fields on an existing issue.

FlagShortDefaultDescription
--idrequiredIssue ID to update
--title-tNew title (renames folder/file)
--typeNew issue type
--status-sNew status
--priorityNew priority
--labels-lReplace all labels (comma-separated)
--add-labelsAdd labels without removing existing
--remove-labelsRemove specific labels
--assignee-aSet assignee (none to unassign)
--milestone-mSet milestone (none to unassign)
--estimate-eSet estimate (none to clear)
--spentSet spent (none to clear)
--due-dateSet due date (none to clear)
--blocked-byReplace blockedBy list
--add-blocked-byAdd to blockedBy list
--remove-blocked-byRemove from blockedBy list
--parentSet parent issue (none to clear)
--related-toReplace relatedTo list
--add-related-toAdd to relatedTo list
--remove-related-toRemove from relatedTo list
--add-checklistAdd checklist items (comma-separated)
--remove-checklistRemove checklist items by text (comma-separated)
--checkMark items done by text (comma-separated)
--uncheckMark items not done by text (comma-separated)
--content-cReplace markdown body (or - for stdin)
--dry-runfalsePreview changes without applying

Behavior:

  • Only specified fields are updated; omitted fields are unchanged
  • Changing title renames the folder and file to match the new slug
  • Updates updatedAt timestamp automatically
  • Rejects changes that would create dependency or parent-child cycles

Before/After Example

Running mdp issue update --id ISS-1 --status "In Progress" --priority High --assignee agent-1:

Before:

status: Backlog priority: null assignee: null

After:

status: In Progress priority: High assignee: agent-1

Delete Issue

mdp issue delete --id ISS-1

Permanently delete an issue.

FlagShortDefaultDescription
--idrequiredIssue ID to delete
--dry-runfalsePreview without deleting

Behavior:

  • Removes the entire issue folder (including attachments)
  • Cleans up references in other issues: removes this ID from blockedBy and relatedTo arrays, sets parent: null on child issues
  • Does not reclaim the ID

For example, deleting ISS-2 when ISS-3 has blockedBy: [ISS-2] will update ISS-3’s frontmatter to blockedBy: [].

Issue Log

Manage log entries on an issue. Five subcommands: add, list, get, update, delete.

Add Log Entry

mdp issue log add --id ISS-1 -b "Started implementation"

Append a log entry to an issue’s log frontmatter array.

FlagShortDefaultDescription
--idrequiredIssue ID
--body-brequiredEntry body text
--author"cli"Author identifier
--dry-runfalsePreview without writing

Behavior:

  • Appends a {timestamp, author, body} entry to the log array
  • Timestamp is the current UTC time in ISO 8601 format
  • Does not modify the markdown body — all activity lives in frontmatter
  • Updates updatedAt timestamp
{ "ok": true, "data": { "id": "ISS-1", "entry": { "author": "agent-1", "timestamp": "2025-03-02T14:30:00.000Z", "body": "Implementation complete. All tests passing." }, "totalEntries": 2, "filePath": ".mdp/issues/ISS-1-add-authentication/ISS-1-add-authentication.md" } }

List Log Entries

mdp issue log list --id ISS-1
FlagShortDefaultDescription
--idrequiredIssue ID

Get Log Entry

mdp issue log get --id ISS-1 --index 0
FlagShortDefaultDescription
--idrequiredIssue ID
--indexrequiredLog entry index (0-based)

Update Log Entry

mdp issue log update --id ISS-1 --index 0 -b "Updated message"
FlagShortDefaultDescription
--idrequiredIssue ID
--indexrequiredLog entry index (0-based)
--authorNew author
--body-bNew body text
--dry-runfalsePreview without writing

Delete Log Entry

mdp issue log delete --id ISS-1 --index 0
FlagShortDefaultDescription
--idrequiredIssue ID
--indexrequiredLog entry index (0-based)
--dry-runfalsePreview without writing

Batch Create Issues

echo '[...]' | mdp issue batch-create

Create multiple issues in one command by piping a JSON array to stdin.

FlagDefaultDescription
--dry-runfalsePreview without creating (IDs shown as ISS-X)

Input Fields

Each object in the JSON array accepts:

FieldRequiredDefaultDescription
titleyesIssue title
typenonullIssue type
statusnoFirst status in configInitial status
prioritynonullPriority level
labelsno[]Array of label strings
assigneenonullAssignee identifier
milestonenonullMilestone ID
estimatenonullEffort points
spentnonullActual effort spent
dueDatenonullDue date ("YYYY-MM-DD")
blockedByno[]Array of issue IDs
parentnonullParent issue ID
relatedTono[]Array of issue IDs
checklistno[]Array of checklist item strings
descriptionno""Short description
contentno""Full markdown body
templatenonullTemplate name

Example

echo '[ { "title": "User authentication", "type": "feature", "priority": "High", "labels": ["backend", "security"], "milestone": "M-1" }, { "title": "Write auth tests", "type": "task", "labels": ["backend"], "blockedBy": ["ISS-1"] }, { "title": "Login page UI", "type": "feature", "labels": ["frontend"] } ]' | mdp issue batch-create

Output

{ "ok": true, "data": { "total": 3, "succeeded": 3, "failed": 0, "results": [ { "ok": true, "data": { "id": "ISS-1", "title": "User authentication", "filePath": ".mdp/issues/ISS-1-user-authentication/ISS-1-user-authentication.md" } }, { "ok": true, "data": { "id": "ISS-2", "title": "Write auth tests", "filePath": ".mdp/issues/ISS-2-write-auth-tests/ISS-2-write-auth-tests.md" } }, { "ok": true, "data": { "id": "ISS-3", "title": "Login page UI", "filePath": ".mdp/issues/ISS-3-login-page-ui/ISS-3-login-page-ui.md" } } ] } }

When some items fail:

{ "ok": true, "data": { "total": 2, "succeeded": 1, "failed": 1, "results": [ { "ok": true, "data": { "id": "ISS-4", "title": "Valid issue", "filePath": ".mdp/issues/ISS-4-valid-issue/ISS-4-valid-issue.md" } }, { "ok": false, "error": { "code": "INVALID_TYPE", "message": "Invalid type \"unknown\"", "index": 1, "details": {} } } ] } }

Behavior:

  • Items are processed sequentially in array order
  • IDs are assigned sequentially starting from the next available ID
  • Processing continues on individual item failures (continue-on-error)
  • Exit code is 1 if any item fails, 0 if all succeed
  • Invalid JSON or a non-array input produces an INVALID_INPUT error

Batch Update Issues

echo '[...]' | mdp issue batch-update

Update multiple issues in one command by piping a JSON array to stdin.

FlagDefaultDescription
--dry-runfalsePreview changes without writing

Input Fields

Each object in the JSON array accepts:

FieldRequiredDescription
idyesIssue ID to update (case-insensitive)
titlenoNew title
typenoNew issue type
statusnoNew status
prioritynoNew priority
labelsnoReplace all labels (array)
addLabelsnoAdd labels without removing existing (array)
removeLabelsnoRemove specific labels (array)
assigneenoSet assignee
milestonenoSet milestone
estimatenoSet estimate
spentnoSet spent
dueDatenoSet due date ("YYYY-MM-DD")
blockedBynoReplace blockedBy list (array)
addBlockedBynoAdd to blockedBy list (array)
removeBlockedBynoRemove from blockedBy list (array)
parentnoSet parent issue
relatedTonoReplace relatedTo list (array)
addRelatedTonoAdd to relatedTo list (array)
removeRelatedTonoRemove from relatedTo list (array)
addChecklistnoAdd checklist items (array of strings)
removeChecklistnoRemove checklist items by text (array of strings)
checknoMark items done by text (array of strings)
unchecknoMark items not done by text (array of strings)
contentnoReplace markdown body

Example

echo '[ { "id": "ISS-1", "status": "In Progress", "assignee": "agent-1" }, { "id": "ISS-2", "priority": "High", "addLabels": ["security"] }, { "id": "ISS-3", "addChecklist": ["Write unit tests", "Update docs"], "check": ["Write unit tests"] } ]' | mdp issue batch-update

Output

{ "ok": true, "data": { "total": 3, "succeeded": 3, "failed": 0, "results": [ { "ok": true, "data": { "id": "ISS-1", "changes": { "status": "In Progress", "assignee": "agent-1" }, "filePath": ".mdp/issues/ISS-1-user-authentication/ISS-1-user-authentication.md" } }, { "ok": true, "data": { "id": "ISS-2", "changes": { "priority": "High", "addLabels": ["security"] }, "filePath": ".mdp/issues/ISS-2-write-auth-tests/ISS-2-write-auth-tests.md" } }, { "ok": true, "data": { "id": "ISS-3", "changes": { "addChecklist": ["Write unit tests", "Update docs"], "check": ["Write unit tests"] }, "filePath": ".mdp/issues/ISS-3-login-page-ui/ISS-3-login-page-ui.md" } } ] } }

Behavior:

  • Items are processed sequentially in array order
  • In-memory state is refreshed after each write — cycle detection and ID lookups reflect earlier items in the same batch
  • Processing continues on individual item failures (continue-on-error)
  • Exit code is 1 if any item fails, 0 if all succeed
  • Invalid JSON or a non-array input produces an INVALID_INPUT error
Last updated on