Configuration
taskmd supports .taskmd.yaml configuration files for setting default options.
Config File
Create a .taskmd.yaml file in your project root or home directory:
# .taskmd.yaml
# Default directory to search for task files
dir: ./tasks
# Web server configuration
web:
# Default port for the web dashboard
port: 8080
# Automatically open browser when starting the web server
auto_open_browser: falseConfig File Locations
Config files are loaded in this order (highest precedence first):
- Project-level:
./.taskmd.yaml- project-specific settings - Global:
~/.taskmd.yaml- user-wide defaults - Custom:
--config path/to/config.yaml- explicit path - Built-in defaults - fallback values
Command-line flags always override config file values.
Supported Options
| Option | Type | Default | Description |
|---|---|---|---|
dir | string | . | Default task directory |
ignore | string[] | [] | Additional directories to skip when scanning (beyond the built-in skip list) |
worklogs | boolean | false | Allow writing worklog entries; set to true to opt in |
workflow | string | "solo" | Workflow mode: "solo" or "pr-review" |
worktree_scope | string | "unified" | unified merges task state across git worktrees; isolated keeps each worktree to its own files (details) |
todos.exclude | string[] | [] | Glob patterns to exclude from TODO/FIXME scanning |
web.port | integer | 8080 | Web server port |
web.auto_open_browser | boolean | false | Auto-open browser on web start |
web.readonly | boolean | false | Start web server in read-only mode (disables editing) |
id.strategy | string | "sequential" | ID generation strategy (details) |
id.prefix | string | "" | Prefix for prefixed strategy |
id.length | integer | 6 | Length of generated IDs (used by random and ulid strategies) |
id.padding | integer | 3 | Zero-padding width for sequential strategy |
effort | string[] | [small, medium, large] | Ordered effort vocabulary, lowest to highest (details) |
scopes | map | — | Scope-to-path mappings for the touches field (details) |
phases | array | [] | Phase definitions with metadata (details) |
projects | array | [] | Registered projects for multi-project workflows (details) (global config only) |
default_project | string | "" | Default project ID to use when no --project flag is specified (global config only) |
ignore — The scanner already skips common directories (node_modules, vendor, dist, build, .next, .nuxt, out, target, __pycache__, and hidden directories). Use ignore to add project-specific directories:
ignore:
- "tmp"
- "cache"
- "legacy"worklogs — Worklogs are opt-in; an absent key means disabled. Until you set worklogs: true, taskmd worklog <id> --add refuses to write and explains why, and agents skip worklog steps. Only writing is gated — existing worklogs can always be read regardless of this setting, and taskmd rm still deletes a removed task's worklog.
workflow — Controls the development workflow mode:
solo(default) — optimized for single-developer workflowspr-review— optimized for PR-based collaborative workflows
todos.exclude — Glob patterns to exclude files from taskmd todos scanning. These are additive with CLI --exclude flags:
todos:
exclude:
- "parser_test.go"
- "**/*_test.go"TIP
Only project-level settings are supported in config files. Per-invocation preferences like format, verbose, and quiet are intentionally CLI-only.
ID Strategy Configuration
The id section controls how task IDs are generated by the add and next-id commands. Four strategies are available:
| Strategy | Format | Example ID | Description |
|---|---|---|---|
sequential | Zero-padded number | 001, 042 | Default. Finds the highest existing ID and increments. |
prefixed | Prefix + number | dr-001, jk-042 | Like sequential, but with a user-defined prefix. Useful for multi-contributor projects. |
random | Random alphanumeric | a3f9x2 | Short random string. Avoids ID collisions across branches. |
ulid | ULID | 01HGW3... | Universally Unique Lexicographically Sortable Identifier. Guarantees uniqueness with natural sort order. |
# .taskmd.yaml
# Default: sequential IDs
id:
strategy: sequential
padding: 3 # 001, 002, ... 999
# Prefixed IDs (useful for teams)
id:
strategy: prefixed
prefix: "dr" # dr-001, dr-002, ...
# Random IDs (branch-safe)
id:
strategy: random
# ULID IDs (sortable + unique)
id:
strategy: ulidAvoiding ID Collisions
When multiple contributors create tasks on separate branches, sequential IDs can collide after merge. Use prefixed (with unique per-contributor prefixes), random, or ulid to avoid this. If collisions do occur, use taskmd deduplicate to resolve them.
Scopes Configuration
The scopes key defines concrete mappings for the abstract scope identifiers used by the touches frontmatter field. The tracks command uses touches to detect spatial overlap between tasks and assign them to parallel work tracks — tasks sharing a scope are placed in separate tracks to avoid merge conflicts.
# .taskmd.yaml
scopes:
cli/graph:
description: "Graph visualization and dependency rendering"
paths:
- "apps/cli/internal/graph/"
- "apps/cli/internal/cli/graph.go"
cli/output:
paths:
- "apps/cli/internal/cli/format.go"Each scope entry has the following fields:
| Field | Required | Description |
|---|---|---|
description | No | Human-readable explanation of what the scope covers. Included in validation error messages. |
paths | No | List of file or directory paths that the scope maps to. |
Behavior:
- When scopes are configured, any
touchesvalue in a task that does not match a configured scope produces a warning. - When no scopes config exists, all
touchesvalues are accepted silently.
Worktree Scope Configuration
The worktree_scope key controls what taskmd reads in a git repository with multiple worktrees:
unified(default) — taskmd merges task state across all worktrees so each checkout sees one coherent view. The merge activates by itself only when sibling worktrees actually exist, so single-worktree repos and non-git directories are entirely unaffected.isolated— each worktree sees only its own task files. For worktrees that keep intentionally independent task states.
# .taskmd.yaml
worktree_scope: isolatedWhen the overlay is active:
- Read commands (
list,next,board,stats,get,graph, …) show each task's effective status — the most advanced status across all worktree copies — with provenance for the worktree that owns it. taskmd nextnever recommends a task that isin-progress(or further along) in a sibling worktree, so marking a taskin-progressin one worktree claims it for the whole repository.- Mutations (
set,add,rm,archive) always write to the current worktree's files;taskmd setfails with a guard error when the target task exists only in a sibling worktree. - All worktrees of one repository resolve to a single registered project — no duplicate entries or double-counting in
--all-projects.
Across projects. --all-projects reads merge each repository's worktrees too, so a task claimed in a sibling worktree shows the same effective status whether you ask from inside that repo or from a cross-project view. Each project's scope comes from its own .taskmd.yaml, so one repository can set worktree_scope: isolated while the others keep merging.
Override per invocation with the global --worktree-scope flag or the TASKMD_WORKTREE_SCOPE environment variable — unlike the config key, an explicit override applies to every project in the run. See the CLI guide for the full behavior reference.
Usage Examples
Project Setup
# Create project config
cat > .taskmd.yaml <<EOF
dir: ./tasks
web:
port: 3000
auto_open_browser: true
EOF
# Now these commands use config defaults
taskmd list # Uses ./tasks directory
taskmd web start # Uses port 3000 and opens browser
# CLI flags still override config
taskmd list --dir ./other-tasks
taskmd web start --port 8080Global Defaults
Create ~/.taskmd.yaml for defaults that apply to all projects:
web:
port: 3000
auto_open_browser: trueEnvironment Variables
taskmd supports environment variables with the TASKMD_ prefix:
export TASKMD_DIR=./tasks
export TASKMD_VERBOSE=truePrecedence (highest to lowest):
- Command-line flags
- Project-level
.taskmd.yaml - Global
~/.taskmd.yaml - Environment variables
- Built-in defaults
Sync Configuration
The sync command reads its configuration from the sync section of .taskmd.yaml. Each source defines where to fetch tasks from, how to map fields, and where to write files.
GitHub source:
# .taskmd.yaml
dir: ./tasks
sync:
sources:
- name: github
project: "owner/repo"
token_env: GITHUB_TOKEN # Environment variable holding the API token
output_dir: ./tasks/synced # Where to write synced task files
field_map:
status:
open: pending
closed: completed
priority:
urgent: critical
high: high
medium: medium
low: low
labels_to_tags: true # Convert issue labels to task tags
assignee_to_owner: true # Map assignee to owner field
filters:
state: open # Only sync open issuesJira source:
# .taskmd.yaml
dir: ./tasks
sync:
sources:
- name: jira
project: "PROJ" # Jira project key
base_url: https://myteam.atlassian.net # Jira Cloud instance URL (required)
token_env: JIRA_API_TOKEN # Jira API token
user_env: JIRA_USER_EMAIL # Jira account email (for Basic auth)
output_dir: ./tasks/jira
field_map:
status:
To Do: pending
In Progress: in-progress
Done: completed
priority:
Highest: critical
High: high
Medium: medium
Low: low
Lowest: low
labels_to_tags: true
assignee_to_owner: true
filters:
jql: 'status != "Done"' # Additional JQL (ANDed with project)Jira Authentication
Jira Cloud uses Basic authentication with your account email and an API token. Both token_env and user_env are required. Generate an API token at id.atlassian.net/manage-profile/security/api-tokens.
Jira Descriptions
Jira Cloud API v3 returns descriptions in Atlassian Document Format (ADF). taskmd automatically converts ADF to Markdown, supporting paragraphs, headings, lists, code blocks, blockquotes, and inline formatting.
Source fields:
| Field | Required | Description |
|---|---|---|
name | Yes | Unique name for this source |
project | No | Project identifier (e.g., owner/repo for GitHub) |
base_url | No | Custom API base URL |
token_env | No | Environment variable name for API token |
user_env | No | Environment variable name for username |
output_dir | Yes | Directory where synced task files are written |
field_map | No | How to map external fields to taskmd frontmatter |
filters | No | Source-specific filters (e.g., state: open) |
Field mapping (field_map):
| Sub-field | Type | Description |
|---|---|---|
status | map[string]string | Map external status values to taskmd statuses |
priority | map[string]string | Map external priority values to taskmd priorities |
labels_to_tags | bool | Convert external labels/categories to task tags |
assignee_to_owner | bool | Map external assignee to the owner field |
Phases Configuration
The phases key defines the phases available for your project. Tasks reference phases via the phase frontmatter field.
# .taskmd.yaml
phases:
- id: v0.2
name: "v0.2 – Core CLI"
description: "Core CLI features"
due: 2026-04-01
- id: v0.3
name: "v0.3 – Web Dashboard"
description: "Web dashboard"
due: 2026-06-01Each phase entry has the following fields:
| Field | Required | Description |
|---|---|---|
id | Yes | Unique phase identifier (must match the task's phase field) |
name | Yes | Human-readable display name for the phase |
description | No | Description of the phase's scope |
due | No | Target date in YYYY-MM-DD format |
Behavior:
- When phases are configured, any
phasevalue in a task that does not match a configured phase name produces a warning. - When no phases config exists, all
phasevalues are accepted silently.
Effort Configuration
The effort key defines the values the effort frontmatter field may take. It replaces the built-in small, medium, large entirely — it is not additive.
# .taskmd.yaml
effort: [xs, s, m, l, xl]Values are listed lowest to highest, and the order is significant:
| Behavior | How the order is used |
|---|---|
| Comparison filters | --filter "effort>=m" ranks values by position |
board --group-by effort | Column order follows the configured order |
list --sort effort | Sorts lowest first; unset values sort last |
next scoring | Points scale with position — the lowest value earns the most, the highest earns none |
next --quick-wins | Selects tasks at the lowest configured value |
Behavior:
- When no
effortconfig exists, the defaultsmall,medium,largeapplies and nothing changes. A bareeffort:with no value is treated the same way. - When configured,
taskmd validatereports any task using a value outside the list, andadd/setreject one. - The config itself is rejected when it is not a list, is empty, contains a blank entry, contains duplicates, or contains a non-string item.
- Items are effort values as strings. Object items are reserved for future per-value metadata.
Changing an existing project
The configured vocabulary replaces the default, so existing task files using small/medium/large become invalid. Update them in the same change, or keep the old values in the list.
Projects Configuration
The projects key in the global config (~/.taskmd.yaml) registers projects for multi-project workflows. Use taskmd projects register to add entries, or edit the config directly.
# ~/.taskmd.yaml
projects:
- id: frontend
name: "Frontend App"
path: /Users/me/projects/frontend
- id: backend
name: "Backend API"
path: /Users/me/projects/backend
# Optional: set a default project so --project can be omitted
default_project: frontendEach project entry has the following fields:
| Field | Required | Description |
|---|---|---|
id | Yes | Unique project identifier (auto-generated from directory basename if omitted during registration) |
name | No | Human-readable display name (defaults to the same value as id) |
path | Yes | Absolute path to the project directory (must contain a .taskmd.yaml file) |
Usage:
# List all registered projects
taskmd projects
# Run commands against a specific project
taskmd list --project backend
taskmd next --project frontend
# Aggregate tasks from all projects
taskmd stats --all-projects
taskmd list --all-projectsWhen default_project is set, commands automatically scope to that project unless --project or --all-projects is explicitly provided.
Shell Aliases
For quick access, add aliases to your shell config:
# ~/.bashrc or ~/.zshrc
alias tm='taskmd --dir ./tasks'
alias tmw='taskmd web start --port 8080 --open'
alias tnext='taskmd next --limit 3'
alias thigh='taskmd list --filter priority=high --filter status=pending'