feat: Add OurDigital custom skills package (10 skills)

Complete implementation of OurDigital skills with dual-platform support
(Claude Desktop + Claude Code) following standardized structure.

Skills created:
- 01-ourdigital-brand-guide: Brand reference & style guidelines
- 02-ourdigital-blog: Korean blog drafts (blog.ourdigital.org)
- 03-ourdigital-journal: English essays (journal.ourdigital.org)
- 04-ourdigital-research: Research prompts & workflows
- 05-ourdigital-document: Notion-to-presentation pipeline
- 06-ourdigital-designer: Visual/image prompt generation
- 07-ourdigital-ad-manager: Ad copywriting & keyword research
- 08-ourdigital-trainer: Training materials & workshop planning
- 09-ourdigital-backoffice: Quotes, proposals, cost analysis
- 10-ourdigital-skill-creator: Meta skill for creating new skills

Features:
- YAML frontmatter with "ourdigital" or "our" prefix triggers
- Standardized directory structure (code/, desktop/, shared/, docs/)
- Shared environment setup (_ourdigital-shared/)
- Comprehensive reference documentation
- Cross-skill integration support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 16:50:17 +07:00
parent 7d20abe811
commit 0bc24d00b9
169 changed files with 9970 additions and 741 deletions

View File

@@ -0,0 +1,113 @@
#!/usr/bin/env python3
"""
Ulysses Export Helper
Exports markdown files to iCloud folder for Ulysses sync.
Designed for OurDigital research-to-publisher workflow.
Usage:
python export_to_ulysses.py --content "# Title\n\nContent..." --filename "my-post.md"
python export_to_ulysses.py --file /path/to/draft.md --channel blog
"""
import os
import sys
import argparse
from pathlib import Path
from datetime import datetime
# Default iCloud paths (user should customize)
ICLOUD_BASE = Path.home() / "Library/Mobile Documents/com~apple~CloudDocs"
# Channel-specific folders (customize based on Ulysses library structure)
CHANNEL_FOLDERS = {
"blog": "Ulysses/OurDigital/Blog Drafts",
"journal": "Ulysses/OurDigital/Journal Drafts",
"ourstory": "Ulysses/OurDigital/OurStory Drafts",
"medium": "Ulysses/OurDigital/Medium Drafts",
"default": "Ulysses/Blog Drafts"
}
def get_export_path(channel: str = "default") -> Path:
"""Get the export path for a specific channel."""
folder = CHANNEL_FOLDERS.get(channel, CHANNEL_FOLDERS["default"])
export_path = ICLOUD_BASE / folder
# Create directory if it doesn't exist
export_path.mkdir(parents=True, exist_ok=True)
return export_path
def generate_filename(title: str = None) -> str:
"""Generate a filename with timestamp."""
timestamp = datetime.now().strftime("%Y%m%d_%H%M")
if title:
# Clean title for filename
clean_title = "".join(c if c.isalnum() or c in "- " else "" for c in title)
clean_title = clean_title.replace(" ", "-").lower()[:50]
return f"{timestamp}_{clean_title}.md"
return f"{timestamp}_draft.md"
def export_content(content: str, filename: str, channel: str = "default") -> Path:
"""Export content to the appropriate iCloud folder."""
export_path = get_export_path(channel)
file_path = export_path / filename
# Add Ulysses-friendly frontmatter if not present
if not content.startswith("---"):
frontmatter = f"""---
created: {datetime.now().isoformat()}
channel: {channel}
status: draft
---
"""
content = frontmatter + content
file_path.write_text(content, encoding="utf-8")
return file_path
def export_file(source_path: str, channel: str = "default") -> Path:
"""Copy an existing file to the iCloud folder."""
source = Path(source_path)
if not source.exists():
raise FileNotFoundError(f"Source file not found: {source_path}")
content = source.read_text(encoding="utf-8")
filename = source.name
return export_content(content, filename, channel)
def main():
parser = argparse.ArgumentParser(description="Export markdown to Ulysses via iCloud")
parser.add_argument("--content", help="Markdown content to export")
parser.add_argument("--file", help="Path to existing markdown file")
parser.add_argument("--filename", help="Output filename (auto-generated if not provided)")
parser.add_argument("--channel", choices=list(CHANNEL_FOLDERS.keys()),
default="default", help="Target channel/folder")
parser.add_argument("--list-paths", action="store_true",
help="List configured export paths")
args = parser.parse_args()
if args.list_paths:
print("Configured export paths:")
for channel, folder in CHANNEL_FOLDERS.items():
full_path = ICLOUD_BASE / folder
exists = "" if full_path.exists() else ""
print(f" [{exists}] {channel}: {full_path}")
return
if args.file:
result = export_file(args.file, args.channel)
print(f"✓ Exported to: {result}")
elif args.content:
filename = args.filename or generate_filename()
result = export_content(args.content, filename, args.channel)
print(f"✓ Exported to: {result}")
else:
parser.print_help()
sys.exit(1)
if __name__ == "__main__":
main()