Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,15 @@
],
"overrides": [
{
"files": ["**/tests/e2e/**/*.ts"],
"files": ["**/tests/**/*.ts"],
"rules": {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-unsafe-argument": "off"
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/unbound-method": "off"
}
}
]
Expand Down
39 changes: 20 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
[![codecov](https://codecov.io/gh/TeamNickHart/md2do/branch/main/graph/badge.svg)](https://codecov.io/gh/TeamNickHart/md2do)
[![CI](https://github.com/TeamNickHart/md2do/workflows/CI/badge.svg)](https://github.com/TeamNickHart/md2do/actions)

Manage TODO items in markdown files with powerful filtering, sorting, and [Todoist](https://www.todoist.com) sync.
Manage TODO items in markdown files with powerful filtering, sorting, native [Todoist](https://www.todoist.com) sync, and open multi-source ingestion.
Built with TypeScript, designed for developers who love markdown.

## ✨ Features
Expand All @@ -22,7 +22,8 @@ Built with TypeScript, designed for developers who love markdown.
- ⚡ **Fast** - Built with performance in mind using fast-glob
- 🔧 **Flexible** - Output in pretty, table, or JSON formats
- 📁 **Context-aware** - Automatically extracts project and person context from folder structure
- 🔄 **Todoist integration** - Import tasks and sync completion status with official [Todoist](https://www.todoist.com) API
- 🔄 **Todoist integration** - Native two-way sync with the official [Todoist](https://www.todoist.com) API (import, sync, list, add)
- 🌐 **Multi-source ingestion** - Bring in tasks from Teams, Outlook, Slack, or any source via JSONL — no API credentials needed in md2do
- ⚙️ **Configurable** - Hierarchical config support (global, project, environment)
- 🤖 **AI-powered** - MCP server integration for Claude and other AI assistants
- 🟣 **Obsidian plugin** - Native task list view, autocomplete, and completion tracking in Obsidian
Expand Down Expand Up @@ -93,7 +94,8 @@ md2do recognizes standard markdown task syntax with rich metadata:
- `#tag` - Tags
- `#due/YYYY-MM-DD` - Due date
- `{completed:YYYY-MM-DD}` - Completion date
- `{todoist:ID}` - Todoist sync ID
- `{todoist:ID}` - Todoist sync ID (native integration)
- `{slug:ID}` - Any external source link (Teams, Outlook, Slack, etc.)
- `- [x]` - Completed task
- `- [ ]` - Incomplete task

Expand Down Expand Up @@ -351,12 +353,13 @@ md2do/
├── packages/
│ ├── core/ # Core parsing, filtering, and file writing
│ │ ├── src/
│ │ │ ├── parser/ # Markdown task parser
│ │ │ ├── parser/ # Markdown task parser (extractSources, formatSources)
│ │ │ ├── scanner/ # File scanner
│ │ │ ├── filters/ # Task filtering
│ │ │ ├── sorting/ # Task sorting
│ │ │ ├── writer/ # File modification (atomic updates)
│ │ │ └── types/ # TypeScript types
│ │ │ ├── ingest/ # JSONL ingest engine (parseJsonl, ingestRecords)
│ │ │ └── types/ # TypeScript types (Task, SourceProvider, IngestRecord)
│ │ └── tests/
│ ├── cli/ # CLI interface
│ │ ├── src/
Expand All @@ -369,10 +372,11 @@ md2do/
│ │ │ ├── schema.ts # Zod schemas for validation
│ │ │ └── loader.ts # Hierarchical config loading
│ │ └── tests/
│ ├── todoist/ # Todoist API integration
│ ├── todoist/ # Todoist API integration (native SourceProvider)
│ │ ├── src/
│ │ │ ├── client.ts # API client wrapper
│ │ │ └── mapper.ts # Task format conversion
│ │ │ ├── mapper.ts # Task format conversion
│ │ │ └── provider.ts # TodoistProvider implements SourceProvider
│ │ └── tests/
│ ├── mcp/ # MCP server for AI integration
│ │ ├── src/
Expand Down Expand Up @@ -454,8 +458,8 @@ pnpm --filter @md2do/core test:ui

## 📖 Additional Documentation

- [Todoist Setup Guide](docs/todoist-setup.md) - Complete guide to configuring [Todoist](https://www.todoist.com) integration
- [Todoist Implementation Plan](docs/todoist-implementation-plan.md) - Technical roadmap and architecture
- [Todoist Integration](docs/integrations/todoist.md) - Native two-way [Todoist](https://www.todoist.com) sync
- [Multi-Source Ingestion](docs/integrations/ingest.md) - Import tasks from Teams, Outlook, Slack, and more
- [Config Package](packages/config/README.md) - Configuration management documentation
- [Todoist Package](packages/todoist/README.md) - [Todoist](https://www.todoist.com) API integration documentation
- [MCP Package](packages/mcp/README.md) - Model Context Protocol server documentation
Expand Down Expand Up @@ -502,14 +506,8 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file

- [x] **MCP (Model Context Protocol) integration** - ✅ Complete! See [MCP docs](packages/mcp/README.md)
- [x] **Configuration file support** - ✅ Complete! Hierarchical config with `.md2do.json`/`.yaml`
- [x] **Todoist integration foundation** - ✅ Complete! API client, task mapping, file writer
- [ ] CLI commands (`md2do todoist sync`, `md2do todoist push`, etc.)
- [ ] Bidirectional sync logic
- [ ] Interactive token setup
- [ ] Validation warnings for `{todoist:ID}` markers
- [ ] Detect malformed IDs
- [ ] Verify ID exists in Todoist
- [ ] Warn about orphaned/deleted tasks
- [x] **Todoist native integration** - ✅ Complete! Two-way sync, import, list, add — see [Todoist docs](docs/integrations/todoist.md)
- [x] **Pluggable multi-source ingestion** - ✅ Complete! Open `{slug:ID}` syntax, `md2do ingest` command, `SourceProvider` interface — see [ingest docs](docs/integrations/ingest.md)

### CLI Enhancements

Expand Down Expand Up @@ -594,12 +592,15 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
- [ ] Autocomplete for assignees and tags (learned from workspace)
- [ ] Inline suggestions with fuzzy matching

### Integrations
### Native Integrations (via `SourceProvider`)

Additional first-class integrations built on the same interface as Todoist:

- [ ] GitHub Issues integration
- [ ] Linear integration
- [ ] Jira integration
- [ ] Notion integration

> **Bring your own source today:** Use `md2do ingest` with JSONL to import from any system — no native integration required.

## 📞 Support

Expand Down
5 changes: 4 additions & 1 deletion docs/.vitepress/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export default defineConfig({
{ text: 'list', link: '/cli/list' },
{ text: 'stats', link: '/cli/stats' },
{ text: 'migrate', link: '/cli/migrate' },
{ text: 'ingest', link: '/cli/ingest' },
{
text: 'todoist',
collapsed: false,
Expand All @@ -94,7 +95,9 @@ export default defineConfig({
items: [
{ text: 'VSCode Extension', link: '/integrations/vscode' },
{ text: 'Obsidian Plugin', link: '/integrations/obsidian' },
{ text: 'Todoist Setup', link: '/integrations/todoist' },
{ text: 'Todoist', link: '/integrations/todoist' },
{ text: 'Multi-Source Ingestion', link: '/integrations/ingest' },
{ text: 'Integration Builder Prompt', link: '/integrations/prompts/build-integration' },
{ text: 'MCP (AI Integration)', link: '/integrations/mcp' },
],
},
Expand Down
140 changes: 140 additions & 0 deletions docs/cli/ingest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# ingest

Import tasks from any external source into your markdown vault via a JSONL file.

```bash
md2do ingest <file.jsonl> [options]
```

## Overview

`md2do ingest` reads a JSONL file (one task record per line), converts each record into a
markdown task line with full metadata, and writes the result to your vault as a regenerated
markdown file.

This is the recommended way to bring in tasks from sources that don't have a native md2do
integration — Teams mentions, Outlook flagged emails, Slack saved messages, calendar items,
or any other system an MCP agent can read.

::: tip Vault files are read-only
Files generated by `ingest` are fully regenerated on every run. Don't hand-edit them —
edit tasks in your regular markdown notes instead.
:::

## Options

| Option | Description |
| --------------------- | ---------------------------------------------------------------- |
| `<file.jsonl>` | Path to the JSONL input file |
| `-o, --output <path>` | Write output to a specific file (overrides `--vault` derivation) |
| `--vault <root>` | Vault root directory (default: current directory) |
| `--dry-run` | Print generated markdown to stdout without writing any file |

## Output Path

By default, output is written to `<vault>/<source>/<basename>.md`:

```
vault/
todoist/
inbox.md # md2do ingest todoist-inbox.jsonl --vault vault
teams/
mentions.md # md2do ingest teams-mentions.jsonl --vault vault
outlook/
flagged.md # md2do ingest outlook-flagged.jsonl --vault vault
```

Use `--output` to override:

```bash
md2do ingest my-tasks.jsonl --output notes/imported.md
```

## JSONL Format

Each line in the input file is a JSON object representing one task:

```jsonl
{"source":"teams","externalId":"msg-789","text":"Follow up on PR review","completed":false,"priority":"normal","tags":["eng"]}
{"source":"teams","externalId":"msg-790","text":"Update onboarding doc","completed":false,"priority":"high","dueDate":"2026-08-10","assignee":"nick"}
{"source":"teams","externalId":"msg-100","text":"Old action item","completed":true}
```

### Required Fields

| Field | Type | Description |
| ------------ | --------- | ---------------------------------------------------------------------------------------------- |
| `source` | `string` | Source slug (e.g. `teams`, `outlook`, `slack`). Used in `{source:ID}` markers and output path. |
| `externalId` | `string` | Unique ID in the source system. |
| `text` | `string` | Task description. |
| `completed` | `boolean` | Whether the task is done. |

### Optional Fields

| Field | Type | Description |
| ---------- | ---------- | ------------------------------------------------------------------- |
| `priority` | `string` | `urgent`, `high`, `normal`, or `low` |
| `dueDate` | `string` | Due date in `YYYY-MM-DD` format |
| `tags` | `string[]` | Array of tag names (without `#`) |
| `assignee` | `string` | Username (without `@`) |
| `metadata` | `object` | Arbitrary extra data — ignored by md2do but preserved for reference |

## Generated Markdown

Given this JSONL:

```jsonl
{"source":"outlook","externalId":"AAMk-abc","text":"Review Q3 budget","completed":false,"priority":"high","dueDate":"2026-08-10","tags":["finance"],"assignee":"nick"}
{"source":"outlook","externalId":"AAMk-def","text":"Reply to procurement","completed":true}
```

`md2do ingest outlook-flagged.jsonl --vault vault --dry-run` produces:

```markdown
# Outlook

- [ ] Review Q3 budget @nick !! #finance #due/2026-08-10 {outlook:AAMk-abc}

## Completed

- [x] Reply to procurement {outlook:AAMk-def} {completed:2026-08-02}
```

The generated lines use all standard md2do metadata syntax:

- `@assignee`, priority markers, `#tags`, `#due/DATE` — fully parsed by `md2do list`
- `{source:externalId}` — source link, deduplicated by scanner
- `{completed:DATE}` — set to today's date on ingest

## Examples

```bash
# Preview without writing
md2do ingest teams-mentions.jsonl --dry-run

# Write to vault
md2do ingest teams-mentions.jsonl --vault ~/notes

# Specify output file directly
md2do ingest slack-saved.jsonl --output ~/notes/slack/saved.md

# Ingest from a temporary file produced by an MCP agent
md2do ingest /tmp/outlook-flagged.jsonl --vault ~/obsidian-vault
```

## Mixed Sources

If a JSONL file contains records from more than one source, md2do warns you and uses the
first record's source slug for the output path derivation:

```
⚠️ Warning: Mixed sources detected: teams, outlook. Using first source "teams" for output path derivation.
```

Use separate JSONL files per source to avoid ambiguity.

## Next Steps

- [Multi-Source Ingestion Guide](/integrations/ingest) — conceptual overview and MCP agent workflow
- [Task Format](/guide/task-format) — how `{slug:ID}` source links work
- [Todoist Integration](/integrations/todoist) — native two-way Todoist sync
32 changes: 31 additions & 1 deletion docs/cli/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,35 @@ md2do migrate --path ./work-notes

See [migrate command](/cli/migrate) for details.

### `ingest`

Import tasks from any external source into your vault via a JSONL file.

```bash
md2do ingest <file.jsonl> [options]
```

**Options:**

- `-o, --output <path>` - Write to a specific output file
- `--vault <root>` - Vault root directory (default: current directory)
- `--dry-run` - Print generated markdown without writing

**Examples:**

```bash
# Preview output
md2do ingest teams-mentions.jsonl --dry-run

# Write to vault (output: vault/teams/teams-mentions.md)
md2do ingest teams-mentions.jsonl --vault ~/notes

# Specify output path directly
md2do ingest outlook-flagged.jsonl --output ~/notes/outlook/inbox.md
```

See [ingest command](/cli/ingest) for the full JSONL format spec and vault convention.

## Todoist Commands

Sync with [Todoist](https://www.todoist.com). Requires API token configuration.
Expand Down Expand Up @@ -566,5 +595,6 @@ md2do todoist sync --help
- [Task Format](/guide/task-format) - Learn task syntax
- [Filtering](/guide/filtering) - Advanced filtering
- [Configuration](/guide/configuration) - Set up config files
- [Todoist Integration](/integrations/todoist) - Sync with Todoist
- [Todoist Integration](/integrations/todoist) - Native two-way Todoist sync
- [Multi-Source Ingestion](/integrations/ingest) - Import from Teams, Outlook, Slack, and more
- [Examples](/guide/examples) - Real-world usage
Loading
Loading