$npx -y skills add michalparkola/tapestry-skills --skill learn-thisUnified content extraction and action planning. Use when user says "learn-this <URL>", "learn this <URL>", "weave <URL>", "help me plan <URL>", "extract and plan <URL>", "make this actionable <URL>", or similar phrases indicating they want to extract content and create an action
| 1 | # Tapestry: Unified Content Extraction + Action Planning |
| 2 | |
| 3 | This is the **master skill** that orchestrates the entire Tapestry workflow: |
| 4 | 1. Detect content type from URL |
| 5 | 2. Extract content using appropriate skill |
| 6 | 3. Automatically create a Ship-Learn-Next action plan |
| 7 | |
| 8 | ## When to Use This Skill |
| 9 | |
| 10 | Activate when the user: |
| 11 | - Says "learn-this [URL]" or "learn this [URL]" |
| 12 | - Says "weave [URL]" |
| 13 | - Says "help me plan [URL]" |
| 14 | - Says "extract and plan [URL]" |
| 15 | - Says "make this actionable [URL]" |
| 16 | - Says "turn [URL] into a plan" |
| 17 | - Provides a URL and asks to "learn and implement from this" |
| 18 | - Wants the full Tapestry workflow (extract → plan) |
| 19 | |
| 20 | **Keywords to watch for**: learn-this, learn this, weave, plan, actionable, extract and plan, make a plan, turn into action |
| 21 | |
| 22 | ## How It Works |
| 23 | |
| 24 | ### Complete Workflow: |
| 25 | 1. **Detect URL type** (YouTube, article, PDF) |
| 26 | 2. **Extract content** using appropriate skill: |
| 27 | - YouTube → youtube-transcript skill |
| 28 | - Article → article-extractor skill |
| 29 | - PDF → download and extract text |
| 30 | 3. **Create action plan** using ship-learn-next skill |
| 31 | 4. **Save both** content file and plan file |
| 32 | 5. **Present summary** to user |
| 33 | |
| 34 | ## URL Detection Logic |
| 35 | |
| 36 | ### YouTube Videos |
| 37 | |
| 38 | **Patterns to detect:** |
| 39 | - `youtube.com/watch?v=` |
| 40 | - `youtu.be/` |
| 41 | - `youtube.com/shorts/` |
| 42 | - `m.youtube.com/watch?v=` |
| 43 | |
| 44 | **Action:** Use youtube-transcript skill |
| 45 | |
| 46 | ### Web Articles/Blog Posts |
| 47 | |
| 48 | **Patterns to detect:** |
| 49 | - `http://` or `https://` |
| 50 | - NOT YouTube, NOT PDF |
| 51 | - Common domains: medium.com, substack.com, dev.to, etc. |
| 52 | - Any HTML page |
| 53 | |
| 54 | **Action:** Use article-extractor skill |
| 55 | |
| 56 | ### PDF Documents |
| 57 | |
| 58 | **Patterns to detect:** |
| 59 | - URL ends with `.pdf` |
| 60 | - URL returns `Content-Type: application/pdf` |
| 61 | |
| 62 | **Action:** Download and extract text |
| 63 | |
| 64 | ### Other Content |
| 65 | |
| 66 | **Fallback:** |
| 67 | - Try article-extractor (works for most HTML) |
| 68 | - If fails, inform user of unsupported type |
| 69 | |
| 70 | ## Step-by-Step Workflow |
| 71 | |
| 72 | ### Step 1: Detect Content Type |
| 73 | |
| 74 | ```bash |
| 75 | URL="$1" |
| 76 | |
| 77 | # Check for YouTube |
| 78 | if [[ "$URL" =~ youtube\.com/watch || "$URL" =~ youtu\.be/ || "$URL" =~ youtube\.com/shorts ]]; then |
| 79 | CONTENT_TYPE="youtube" |
| 80 | |
| 81 | # Check for PDF |
| 82 | elif [[ "$URL" =~ \.pdf$ ]]; then |
| 83 | CONTENT_TYPE="pdf" |
| 84 | |
| 85 | # Check if URL returns PDF |
| 86 | elif curl -sI "$URL" | grep -i "Content-Type: application/pdf" > /dev/null; then |
| 87 | CONTENT_TYPE="pdf" |
| 88 | |
| 89 | # Default to article |
| 90 | else |
| 91 | CONTENT_TYPE="article" |
| 92 | fi |
| 93 | |
| 94 | echo "📍 Detected: $CONTENT_TYPE" |
| 95 | ``` |
| 96 | |
| 97 | ### Step 2: Extract Content (by Type) |
| 98 | |
| 99 | #### YouTube Video |
| 100 | |
| 101 | ```bash |
| 102 | # Use youtube-transcript skill workflow |
| 103 | echo "📺 Extracting YouTube transcript..." |
| 104 | |
| 105 | # 1. Check for yt-dlp |
| 106 | if ! command -v yt-dlp &> /dev/null; then |
| 107 | echo "Installing yt-dlp..." |
| 108 | brew install yt-dlp |
| 109 | fi |
| 110 | |
| 111 | # 2. Get video title |
| 112 | VIDEO_TITLE=$(yt-dlp --print "%(title)s" "$URL" | tr '/' '_' | tr ':' '-' | tr '?' '' | tr '"' '') |
| 113 | |
| 114 | # 3. Download transcript |
| 115 | yt-dlp --write-auto-sub --skip-download --sub-langs en --output "temp_transcript" "$URL" |
| 116 | |
| 117 | # 4. Convert to clean text (deduplicate) |
| 118 | python3 -c " |
| 119 | import sys, re |
| 120 | seen = set() |
| 121 | vtt_file = 'temp_transcript.en.vtt' |
| 122 | try: |
| 123 | with open(vtt_file, 'r') as f: |
| 124 | for line in f: |
| 125 | line = line.strip() |
| 126 | if line and not line.startswith('WEBVTT') and not line.startswith('Kind:') and not line.startswith('Language:') and '-->' not in line: |
| 127 | clean = re.sub('<[^>]*>', '', line) |
| 128 | clean = clean.replace('&', '&').replace('>', '>').replace('<', '<') |
| 129 | if clean and clean not in seen: |
| 130 | print(clean) |
| 131 | seen.add(clean) |
| 132 | except FileNotFoundError: |
| 133 | print('Error: Could not find transcript file', file=sys.stderr) |
| 134 | sys.exit(1) |
| 135 | " > "${VIDEO_TITLE}.txt" |
| 136 | |
| 137 | # 5. Cleanup |
| 138 | rm -f temp_transcript.en.vtt |
| 139 | |
| 140 | CONTENT_FILE="${VIDEO_TITLE}.txt" |
| 141 | echo "✓ Saved transcript: $CONTENT_FILE" |
| 142 | ``` |
| 143 | |
| 144 | #### Article/Blog Post |
| 145 | |
| 146 | ```bash |
| 147 | # Use article-extractor skill workflow |
| 148 | echo "📄 Extracting article content..." |
| 149 | |
| 150 | # 1. Check for extraction tools |
| 151 | if command -v reader &> /dev/null; then |
| 152 | TOOL="reader" |
| 153 | elif command -v trafilatura &> /dev/null; then |
| 154 | TOOL="trafilatura" |
| 155 | else |
| 156 | TOOL="fallback" |
| 157 | fi |
| 158 | |
| 159 | echo "Using: $TOOL" |
| 160 | |
| 161 | # 2. Extract based on tool |
| 162 | case $TOOL in |
| 163 | reader) |
| 164 | reader "$URL" > temp_article.txt |
| 165 | ARTICLE_TITLE=$(head -n 1 temp_article.txt | sed 's/^# //') |
| 166 | ;; |
| 167 | |
| 168 | trafilatura) |
| 169 | METADATA=$(trafilatura --URL "$URL" --json) |
| 170 | ARTICLE_TITLE=$(echo "$METADATA" | python3 -c "import json, sys; print(json.load(sys.stdin).get('title', 'Article'))") |
| 171 | trafilatura --URL "$URL" --output-format txt --no-comments > temp_article.txt |
| 172 | ;; |
| 173 | |
| 174 | fallback) |