From sort
Sort and organize an accumulating folder of files — defaults to the folder Claude Code was launched in (`~/Downloads`, `~/Desktop`, a project inbox, etc.). Trigger on `/sort`, when the user asks to "sort my downloads", "organize this folder", "clean up the desktop", "tidy this directory", "categorize these files", or complains about piled-up DMGs, zips, PDFs, screenshots, receipts, or installers — even when they don't say "sort". Dispatcher that classifies each item — videos, images (vision + OCR for returns/receipts/memes/screenshots/photos), archives, disk images, app bundles, documents — and routes to `<target>/AI Library/<topic>/`. Videos delegate to `/sort-videos`; documents go through the `documents.md` sub-dispatcher; everything else is handled inline. Auto-deletes installers whose app is already in `/Applications/`; otherwise never deletes without confirmation. User overrides via `sort.md` / `sort.local.md`. Pass a path or glob to process specific items.
npx claudepluginhub tal/plugin-marketplace --plugin sortThis skill is limited to using the following tools:
Arguments passed: `$ARGUMENTS`
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
Arguments passed: $ARGUMENTS
Dispatcher skill. Scans a target folder, classifies each item by type, and routes it to the right pipeline. Videos delegate to the /sort-videos skill (same plugin) via the Skill tool. Other types are handled inline here.
The target folder is wherever the user wants sorting to happen. Resolve it in this order:
$ARGUMENTS is an absolute or relative path to a directory, that's the target folder.$ARGUMENTS is a file path or glob, the target folder is the parent directory of those files.pwd in Bash, or the dispatcher's session CWD). This is the common case: the user runs Claude in a folder they want tidied and types /sort.sort.md / sort.local.md rule file sets sources: (see §0.5), that overrides the default — useful when the user always wants to sort one specific folder regardless of where they launched Claude.The skill is location-agnostic by design. It works equally well on ~/Downloads, ~/Desktop, a project folder, a subfolder of Downloads, or anywhere else the user happens to be working.
All output lands under <target>/AI Library/ in topic subfolders. Create that directory if it doesn't already exist — mkdir -p "<target>/AI Library".
Discover existing topic folders at runtime with ls -1 "<target>/AI Library/" and reuse them whenever the content fits — never hardcode topic names. Create a new topic folder only when nothing existing fits. This keeps the skill content-agnostic: each folder builds up its own taxonomy over time (from prior runs in that folder plus anything the user manually creates there), and the dispatcher simply respects whatever's already there. A ~/Downloads/AI Library and a ~/Desktop/AI Library can have completely different folder structures, and that's fine — they reflect what each location is used for.
After resolving the target folder (see "Target folder resolution" above), build the list of items to process:
If $ARGUMENTS was a path or glob to specific files/dirs, use those directly (absolute, relative from CWD, or glob expansion).
If no arguments, scan the target folder at the root level. If <target>/Recents/ exists, scan it too — yt-dlp users sometimes have one. Skip anything already inside <target>/AI Library/ — those are already sorted, and re-processing them risks renaming files the user has organized by hand.
Capture path, size, extension, and (for directories) whether it's a .app bundle. The size and extension feed classification in §1; the .app check matters because .app bundles are directories, not files, and need different handling than the app-installer disk images and zips that contain them.
Users can override per-extension behavior, route specific filename patterns to fixed folders, suppress install prompts for tools they don't need, and tweak top-level settings (sources, sensitive_dir). The dispatcher reads every rule file that exists below and concatenates their rules: lists into one combined list, in this priority order:
$PWD/.claude/sort.local.md — project-local override (gitignored)$PWD/.claude/sort.md — project-shared (committable)~/.claude/sort.local.md — user-global override (gitignored)~/.claude/sort.md — user-shared (could live in a dotfiles repo)Resolution semantics:
sources, sensitive_dir), the highest-priority file that sets the key wins; the others are ignored for that key.Each file is markdown with a YAML frontmatter block. Parse with whatever YAML reader is available — yq if installed, else use the bundled ${CLAUDE_PLUGIN_ROOT}/scripts/match-rules.rb --rules-only, which reads all four rule files in priority order and prints the merged rules + top-level scalars (sources, sensitive_dir) as a structured listing the dispatcher can parse. For the schema, examples, and matcher/action lists, see ${CLAUDE_PLUGIN_ROOT}/skills/sort/OVERRIDES.md. For interactive rule creation, the user can run /sort:add-rule.
Apply rules at every dispatcher decision point:
sources: overrides the §0 default (current working directory). Use this when you always want to sort one specific folder regardless of where Claude was launched — e.g. a user who always sorts ~/Downloads would put sources: [~/Downloads, ~/Downloads/Recents] in ~/.claude/sort.md.sensitive_dir: overrides the prompt default in documents.md §4. Tilde expansion is supported. If unset, the dispatcher defaults to <target>/AI Library/Sensitive/.documents.md.Soft-fail behavior:
When a rule fires, record its source file and index. Include them in the §5 summary table's Rule column so the user can audit which override produced each routing decision.
Map each item to a type bucket:
| Extension / pattern | Type |
|---|---|
.mp4 .webm .mkv .avi .mov .flv .m4v .ts .wmv | video |
.jpg .jpeg .png .gif .heic .webp .tiff .bmp .svg | image |
.zip .tar .tar.gz .tgz .7z .rar | archive |
.dmg .iso | disk-image |
.pkg | installer |
.app (directory) | app-bundle |
.pdf .doc .docx .txt .md .rtf .epub | document |
| anything else | unknown |
Process each bucket independently. Buckets can run in parallel where they don't share state.
/sort-videosFor every video item, invoke the sort-videos skill via the Skill tool, passing the path as the argument. Do not re-implement transcription, OCR, talk detection, or markdown generation here — sort-videos owns that pipeline end-to-end.
sort-videos declares context: fork in its frontmatter, so each invocation runs in its own forked subagent. The dispatcher's context only sees the skill's reply, not the transcription / OCR / summary working details. No need to spawn an Agent manually — the fork happens at skill invocation.
If there are many videos, invoke sort-videos once per path (or pass a glob if that fits the user's request).
For each image (loose file, or an image extracted from a zip group — see Archives), the goal is to identify what the image actually is before routing — filename and EXIF heuristics alone routinely misfile things like return labels, receipts, and shipping artifacts as memes. Always look at the image content (vision + OCR) before falling back to heuristics.
(a) Cheap shortcut — filename-prefix screenshots only. If the filename starts with Screen Shot, Screenshot, CleanShot, or Shottr, route to <target>/AI Library/Screenshots/ and skip the vision pass. These are unambiguous and high-volume; analyzing them every run is wasteful.
(b) Vision + OCR analysis for every other image. For 1–3 loose images, Read each directly — the model is multimodal and can see both visual content and any embedded text in a single Read call. For larger batches, spawn one or more general-purpose Agents and pack multiple image paths into each prompt to amortize the per-call overhead (rule of thumb: ~10 images per Agent call, parallel Agents when total count is large). Whichever path you take, the analysis should treat the image as both visual scene + OCR target, and classify it into one of these content categories:
<target>/AI Library/Returns/. Any QR code present is sufficient on its own — QR-code-bearing images are essentially never memes or photos in this workflow.<target>/AI Library/Receipts/.<target>/AI Library/Screenshots/ unless the content clearly fits a more specific existing topic (e.g. a finance chart into a finance-related folder).<target>/AI Library/Memes/.<target>/AI Library/, or propose a new 2–3 word Title Case topic if nothing fits. Route to <target>/AI Library/<Topic>/.Suggested prompt / reply format for the Agent:
Look at this image at <path>. Use both the visual content and any text visible
in the image (OCR). Classify it into one of these categories: return, receipt,
screenshot, meme, photo, other. If photo or other, also pick a topic that
matches one of these existing folders if possible: <list of folders under
<target>/AI Library/>; if none fit, propose a new 2–3 word Title Case topic.
Reply with JSON: {
"category": "return|receipt|screenshot|meme|photo|other",
"topic": "<existing or new topic, only when category is photo or other>",
"reuse": true|false,
"ocr_summary": "<one-line description of any visible text — empty string if none>",
"description": "<one-line summary of what the image is>"
}
Always include ocr_summary so visible text feeds the routing decision (return-card numbers, "The UPS Store", "Amazon", merchant names on receipts, etc.). For zip-bundled images, see Archives for grouping rules.
(c) Fallback when no vision is available. If for some reason no vision-capable tool is reachable (no Agent, no multimodal Read), fall back to the legacy heuristics: meme = no EXIF + dimensions < 1200px on both axes + random/web filename; photo = EXIF camera Make + Model present (sips -g make -g model "<file>" or exiftool); everything else → <target>/AI Library/Review/. Note in the §5 summary which images used this fallback so the user can spot-check.
Peek at contents — do not extract blindly:
.zip: unzip -l "<file>".tar.gz / .tgz: tar -tzf "<file>".tar: tar -tf "<file>".7z: 7z l "<file>".rar: unrar l "<file>" (or 7z l if unrar isn't installed)Classify the archive from the listing:
.app at the top level (or within one subdirectory) → app installer. Extract into a temp dir (/tmp/sort-extract-<basename>/), run the app-bundle pipeline on the extracted .app, then:
/Applications/ → delete the original archive (.zip, .7z, .tar.gz, .rar, etc.) and remove the temp extraction. Same auto-delete rule as DMGs.<target>/AI Library/<Topic>/<zip-basename>/. Keep the original archive inside that same folder..pdf .doc .docx .epub) → unwrap and treat the inner file as a document. Extract into a temp dir, run that single file through the documents-pipeline subagent (same prompt as the ### Documents section), and route the extracted file to whatever destination it classifies into. Move the original archive into the same destination folder so nothing is silently discarded — the user can clean up duplicates later. Don't ask.AI Library/Review/<zip-basename>/), or skip.Mount read-only, inspect, then detach:
hdiutil attach -nobrowse -readonly "<file>" -mountpoint "/tmp/sort-mount-<basename>"
Find .app or .pkg inside the mount point. Run the app-bundle pipeline (for .app) or installer handling (for .pkg) on the mounted copy, then decide the fate of the original disk-image file:
/Applications/ → delete the .dmg/.iso after detaching. Same auto-delete rule as zip installers..dmg/.iso into the chosen topic folder (next to where the app would be installed).Detach before deleting/moving:
hdiutil detach "/tmp/sort-mount-<basename>"
Always detach on both success and failure (use a trap / cleanup step).
Read bundle ID and version:
defaults read "<path>/Contents/Info.plist" CFBundleIdentifier
defaults read "<path>/Contents/Info.plist" CFBundleShortVersionString
Check whether that bundle ID is already installed:
mdfind -onlyin /Applications "kMDItemCFBundleIdentifier == '<bundle-id>'"
Fall back to matching on .app name if mdfind returns nothing (Spotlight may be indexing).
/Applications/ — installer/source was a DMG, disk image, or zip:
.dmg, .iso, .zip, etc.). This is the only auto-delete case the user authorized..app bundle in the target folder (not inside an installer), do not delete — move it to <target>/AI Library/Apps/_duplicates/ for manual review instead..app) to a topic folder. Prefer an existing folder that fits the app's purpose (a DAW → a music-related folder if one exists, a dev tool → a tech-related folder, a game → a games folder, etc.) — discover the user's folders at runtime, don't guess names. If nothing fits, use <target>/AI Library/Apps/..pkg installersTreat like a DMG:
/Applications/ (check via pkgutil --pkgs | grep -i <basename> and cross-reference receipts), delete the .pkg..pkg installs, ask via AskUserQuestion.documents.mdFor any item in the document bucket (.pdf .doc .docx .txt .md .rtf .epub), follow the procedure in ${CLAUDE_PLUGIN_ROOT}/skills/sort/documents.md. Read it on demand — only when the run actually contains documents. That file is a sub-dispatcher: it owns text extraction, vision fallback for scanned PDFs, sensitivity routing, and confidence-based fallthrough to Review/.
The whole /sort skill runs forked (context: fork in frontmatter), so document classification work — pdftotext extracts, agent calls — already lives in an isolated context relative to the user's parent conversation. No additional forking needed inside the doc path.
Don't re-implement that logic here. The dispatcher's only job for documents is to hand the path off and incorporate the result into the summary table (§5).
Anything that didn't match a type bucket (.json, .torrent, .spk, .ipsw, etc.) is normally moved to <target>/AI Library/Review/ for manual triage — these don't have a portable inspection method.
Sensitive-name default: before routing to Review/, check the basename (case-insensitive) against this regex:
(recovery|backup-codes?|\.env|credentials|secret|private-key|api-key|api_key|recovery-kit)
If it matches, route to the resolved sensitive_dir instead of Review/ — recovery keys, dotenv files, and credential bundles should not sit unguarded in a manual-triage pile. This default fires only if no §0.5 user rule already fired for the file (rules always take priority). Users who don't want this default can add a rule with match: { phase: unknown-sensitive-default } action: skip.
When type or topic classification is weak, conflicting, or ambiguous, use AskUserQuestion. Examples:
.pkg whose target app can't be inferredBatch ambiguous items into a single AskUserQuestion call per run — interrupting one-by-one across many files trains the user to mash through the prompts and degrades signal. Group the uncertain ones and present them together (e.g., one question with 2-4 files per option, or a multiSelect question for categorizing a list).
Never delete without explicit confirmation, except the installer-already-installed case spelled out above. Deletion is the only irreversible action; everything else can be undone by moving files back.
/sort-videos which has its own parallel transcription.unzip -l etc.) run in parallel.documents.md, which extracts text in parallel and fires Agent classification in parallel.Print a summary table:
| File | Type | Destination | Action | Rule |
|---|
Type — video, image, archive, disk-image, installer, app-bundle, document, unknownAction — moved, extracted, delegated (to /sort-videos), deleted (installer-dedup or action: delete rule), mounted+moved, classified / classified-sensitive / review / error (from documents.md), or skipped. For images, append (vision) when the §Images §b vision/OCR pass produced the routing, or (fallback) when §c heuristics were used because no vision tool was reachable — the user wants to spot-check fallback rows.Rule — when a §0.5 user rule fired, show <file>:<index> (e.g. ~/.claude/sort.local.md:1); blank when defaults appliedAlso list:
AI Library/Review/ for manual triage/Applications/ app name)