Import markdown documents and mermaid diagrams into Joplin using the Joplin CLI.
Imports markdown documents and mermaid diagrams into Joplin notebooks using the Joplin CLI. Use this when you need to publish research, documentation, or diagrams to your Joplin knowledge base.
/plugin marketplace add mindmorass/reflex/plugin install reflex@mindmorass-reflexThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Import markdown documents and mermaid diagrams into Joplin using the Joplin CLI.
Joplin uses a database-backed system. Content is imported via the Joplin CLI or API. This skill focuses on CLI-based imports.
# Using npm
npm install -g joplin
# Verify installation
joplin version
# Set sync target (if needed)
joplin config sync.target 2 # Filesystem
joplin config sync.2.path /path/to/sync/folder
# Or connect to Joplin Server/Cloud
joplin config sync.target 9
joplin config sync.9.path https://your-joplin-server.com
joplin config sync.9.username your-username
joplin ls / # List top-level notebooks
joplin ls /Notebook # List notes in notebook
joplin mkbook "Notebook Name"
joplin mkbook "Parent/Child" # Nested notebook
# Import single file to notebook
joplin import /path/to/file.md --notebook "Notebook Name"
# Import directory
joplin import /path/to/folder --notebook "Notebook Name"
# Import with format specification
joplin import /path/to/file.md --format md --notebook "Notebook Name"
# Create note from stdin
echo "# Title
Content" | joplin mknote "Note Title" --notebook "Notebook Name"
# Create from file content
cat file.md | joplin mknote "Note Title" --notebook "Notebook Name"
import subprocess
def notebook_exists(notebook: str) -> bool:
"""Check if a Joplin notebook exists."""
result = subprocess.run(
["joplin", "ls", "/"],
capture_output=True,
text=True
)
notebooks = result.stdout.strip().split('
')
return notebook in notebooks
def create_notebook_if_missing(notebook: str):
"""Create notebook if it doesn't exist."""
if not notebook_exists(notebook):
subprocess.run(
["joplin", "mkbook", notebook],
check=True
)
def prepare_markdown(
title: str,
content: str,
tags: list = None
) -> str:
"""
Prepare markdown content for Joplin import.
Joplin supports YAML frontmatter for metadata.
"""
lines = [f"# {title}", ""]
if tags:
lines.extend([
"---",
f"tags: {', '.join(tags)}",
"---",
""
])
lines.append(content)
return '
'.join(lines)
import tempfile
from pathlib import Path
def write_temp_markdown(content: str, filename: str) -> Path:
"""Write content to a temporary markdown file."""
temp_dir = Path(tempfile.mkdtemp())
file_path = temp_dir / f"{filename}.md"
file_path.write_text(content, encoding='utf-8')
return file_path
def import_to_joplin(
file_path: Path,
notebook: str
) -> bool:
"""Import markdown file to Joplin notebook."""
result = subprocess.run(
[
"joplin", "import",
str(file_path),
"--notebook", notebook
],
capture_output=True,
text=True
)
if result.returncode != 0:
raise JoplinImportError(f"Import failed: {result.stderr}")
return True
def publish_to_joplin(
notebook: str,
title: str,
content: str,
tags: list = None
) -> bool:
"""
Publish markdown content to a Joplin notebook.
Args:
notebook: Target notebook name
title: Note title
content: Markdown content (can include mermaid)
tags: Optional list of tags
Returns:
True if successful
"""
# Ensure notebook exists
create_notebook_if_missing(notebook)
# Prepare content
full_content = prepare_markdown(title, content, tags)
# Write to temp file
temp_file = write_temp_markdown(full_content, title)
try:
# Import to Joplin
import_to_joplin(temp_file, notebook)
return True
finally:
# Cleanup
temp_file.unlink()
temp_file.parent.rmdir()
# Note Title
Content goes here.
## Section
More content.
# Note Title
Content here.
Joplin supports mermaid diagrams natively in markdown:
# System Architecture
```mermaid
flowchart TD
A[Client] --> B[Server]
B --> C[(Database)]
The system consists of...
## Notebook Organization
### Flat Structure
Notebooks/ ├── Research ├── Projects ├── Meetings └── Archive
### Nested Structure
Notebooks/ ├── Work/ │ ├── Project Alpha │ └── Project Beta ├── Personal/ │ ├── Notes │ └── Ideas └── Archive/
Create nested notebooks:
```bash
joplin mkbook "Work"
joplin mkbook "Work/Project Alpha"
publish_to_joplin(
notebook="Research",
title="API Design Patterns",
content="""
## Overview
Key findings from API design research.
## REST Best Practices
1. Use nouns for resources
2. Use HTTP methods correctly
3. Version your API
## GraphQL Considerations
- Schema-first design
- Query optimization
""",
tags=["api", "research", "design"]
)
publish_to_joplin(
notebook="Architecture",
title="System Overview Diagram",
content="""
## Architecture Diagram
```mermaid
flowchart TB
subgraph Frontend
A[Web App]
B[Mobile App]
end
subgraph Backend
C[API Server]
D[Worker]
end
subgraph Data
E[(PostgreSQL)]
F[(Redis)]
end
A --> C
B --> C
C --> E
C --> F
D --> E
| Component | Technology | Purpose |
|---|---|---|
| Web App | React | User interface |
| API Server | FastAPI | REST API |
| Worker | Celery | Background jobs |
| """, |
tags=["architecture", "diagram"]
)
### Publish Meeting Notes
```python
publish_to_joplin(
notebook="Meetings/2024",
title="Project Sync - Jan 15",
content="""
## Attendees
- Alice
- Bob
- Charlie
## Agenda
1. Sprint review
2. Blockers
3. Next steps
## Notes
### Sprint Review
- Feature X completed
- Bug Y in progress
### Blockers
- Waiting on API access
### Action Items
- [ ] Alice: Follow up on API access
- [ ] Bob: Complete bug fix
- [ ] Charlie: Update documentation
""",
tags=["meeting", "project-alpha"]
)
For importing multiple documents:
def batch_import(
notebook: str,
documents: list[dict]
) -> dict:
"""
Import multiple documents to Joplin.
Args:
notebook: Target notebook
documents: List of {title, content, tags} dicts
Returns:
{success: int, failed: int, errors: list}
"""
results = {"success": 0, "failed": 0, "errors": []}
for doc in documents:
try:
publish_to_joplin(
notebook=notebook,
title=doc["title"],
content=doc["content"],
tags=doc.get("tags", [])
)
results["success"] += 1
except Exception as e:
results["failed"] += 1
results["errors"].append({
"title": doc["title"],
"error": str(e)
})
return results
class JoplinError(Exception):
"""Base exception for Joplin operations."""
pass
class JoplinNotInstalledError(JoplinError):
"""Joplin CLI not found."""
pass
class JoplinImportError(JoplinError):
"""Failed to import content."""
pass
class NotebookNotFoundError(JoplinError):
"""Notebook does not exist."""
pass
def check_joplin_installed():
"""Verify Joplin CLI is available."""
result = subprocess.run(
["joplin", "version"],
capture_output=True
)
if result.returncode != 0:
raise JoplinNotInstalledError(
"Joplin CLI not found. Install with: npm install -g joplin"
)
For more control, use the Joplin Data API:
import requests
class JoplinAPI:
def __init__(self, token: str, port: int = 41184):
self.base_url = f"http://localhost:{port}"
self.token = token
def create_note(
self,
title: str,
body: str,
parent_id: str = None
) -> dict:
"""Create a note via Joplin API."""
response = requests.post(
f"{self.base_url}/notes",
params={"token": self.token},
json={
"title": title,
"body": body,
"parent_id": parent_id
}
)
response.raise_for_status()
return response.json()
Enable the API in Joplin Desktop: Options → Web Clipper → Enable
Before publishing:
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.