← back to Opened Tagger
opened-tagger: blue Finder tag for everything opened/launched, with launchd agent
a25520e1ea4851d2dda4c98f642a8f11675fcf73 · 2026-06-24 13:48:46 -0700 · Steve
Files touched
A .gitignoreA README.mdA tag-opened.py
Diff
commit a25520e1ea4851d2dda4c98f642a8f11675fcf73
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Jun 24 13:48:46 2026 -0700
opened-tagger: blue Finder tag for everything opened/launched, with launchd agent
---
.gitignore | 2 +
README.md | 51 ++++++++++++++++++++++
tag-opened.py | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 188 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..05de3c6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+tagger.log
+.DS_Store
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8749f40
--- /dev/null
+++ b/README.md
@@ -0,0 +1,51 @@
+# opened-tagger
+
+Marks **everything you've ever opened or launched** on this Mac with the blue
+Finder tag. In any Finder window, a **blue dot = you've opened this before**;
+**no dot = never touched**. Your "what haven't I read yet?" system.
+
+## How it works
+
+macOS Spotlight records `kMDItemUseCount` (times opened) on every indexed file
+and app. `tag-opened.py` asks Spotlight for items with use-count > 0 and adds the
+system **Blue** tag to each, preserving any tags already present. Pure built-ins
+(`mdfind`, `xattr`, Python stdlib) — nothing to install.
+
+## Usage
+
+```sh
+python3 tag-opened.py # tag opened items in the default surfaces
+python3 tag-opened.py --dry-run # preview only, change nothing
+python3 tag-opened.py --untag # remove the Blue tag (full undo)
+python3 tag-opened.py --dirs ~/Desktop ~/Projects # custom surfaces
+```
+
+Default surfaces: `~/Desktop`, `~/Documents`, `~/Downloads`, `~/Projects`
+(skipping `node_modules`, `.git`, caches, Trash) + all launched apps Mac-wide.
+
+## Make it automatic (re-tag newly-opened items every 30 min)
+
+The LaunchAgent is at `~/Library/LaunchAgents/com.steve.opened-tagger.plist`.
+Enable it yourself (installing a background agent is intentionally gated):
+
+```sh
+launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.steve.opened-tagger.plist
+launchctl list | grep opened-tagger # confirm a PID appears
+```
+
+Disable later:
+
+```sh
+launchctl bootout gui/$(id -u)/com.steve.opened-tagger
+```
+
+## Known limitations (honest)
+
+- **Blue dot, not blue text.** Finder renders tags as a colored dot/pill next to
+ the filename, never as recolored text — macOS has no setting for text color.
+ For literal light-blue text you'd need a custom viewer window (not real Finder).
+- **SIP-protected system apps can't be tagged.** Safari, Mail, and other Apple
+ apps in `/System/Applications` block attribute writes; the tag silently no-ops.
+ User-installed apps (in `~/Applications`, `/Applications`) tag fine.
+- **"Opened" = opened by an app Spotlight noticed.** Quick Look previews and some
+ background reads may not bump the use-count.
diff --git a/tag-opened.py b/tag-opened.py
new file mode 100644
index 0000000..ecc1091
--- /dev/null
+++ b/tag-opened.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python3
+"""
+tag-opened.py — Mark everything you've ever opened/launched with the blue Finder tag.
+
+How it works
+------------
+macOS Spotlight records `kMDItemUseCount` (times opened) on every indexed file and
+app bundle. We ask Spotlight (via `mdfind`) for items whose use-count > 0, then add
+the system "Blue" tag to each. Untouched items stay untagged, so in any Finder
+window the blue dot = "I've opened this before", no dot = "never touched".
+
+No third-party installs: uses the built-in `mdfind` and `xattr` commands plus the
+Python stdlib `plistlib`. Tags are written into com.apple.metadata:_kMDItemUserTags,
+exactly where Finder stores them.
+
+Existing tags are PRESERVED — Blue is appended only if not already present.
+
+Usage
+-----
+ python3 tag-opened.py # tag opened items in the default surfaces
+ python3 tag-opened.py --dry-run # show what WOULD be tagged, change nothing
+ python3 tag-opened.py --untag # remove the Blue tag everywhere we'd add it
+ python3 tag-opened.py --dirs ~/Desktop ~/Projects # custom surfaces
+"""
+
+import argparse
+import os
+import plistlib
+import subprocess
+import sys
+
+TAG = "Blue\n4" # Finder tag name + color index (4 == blue)
+TAG_NAME = "Blue"
+XATTR = "com.apple.metadata:_kMDItemUserTags"
+
+# Default surfaces. Apps are handled separately (whole-Mac query) so launched
+# apps in /Applications, /System/Applications, etc. all get caught.
+DEFAULT_DIRS = [
+ os.path.expanduser("~/Desktop"),
+ os.path.expanduser("~/Documents"),
+ os.path.expanduser("~/Downloads"),
+ os.path.expanduser("~/Projects"),
+]
+
+# Folders we never want to tag inside — pure noise, never "things you read".
+EXCLUDE_SUBSTR = ("/node_modules/", "/.git/", "/Library/Caches/", "/.Trash/")
+
+
+def mdfind(query, only_in=None):
+ cmd = ["mdfind"]
+ if only_in:
+ cmd += ["-onlyin", only_in]
+ cmd.append(query)
+ try:
+ out = subprocess.run(cmd, capture_output=True, text=True, timeout=120).stdout
+ except subprocess.TimeoutExpired:
+ return []
+ return [l for l in out.splitlines() if l]
+
+
+def read_tags(path):
+ """Return the current list of Finder tags on a file (empty if none)."""
+ r = subprocess.run(["xattr", "-px", XATTR, path], capture_output=True, text=True)
+ if r.returncode != 0 or not r.stdout.strip():
+ return []
+ try:
+ raw = bytes.fromhex(r.stdout.replace("\n", "").replace(" ", ""))
+ val = plistlib.loads(raw)
+ return val if isinstance(val, list) else []
+ except Exception:
+ return []
+
+
+def write_tags(path, tags):
+ blob = plistlib.dumps(tags, fmt=plistlib.FMT_BINARY).hex()
+ subprocess.run(["xattr", "-wx", XATTR, blob, path], capture_output=True)
+
+
+def has_blue(tags):
+ # A tag may be stored as "Blue\n4" or bare "Blue"; match either.
+ return any(t.split("\n")[0] == TAG_NAME for t in tags)
+
+
+def gather_targets(dirs):
+ seen = set()
+ # Files/folders opened at least once in each surface.
+ for d in dirs:
+ if not os.path.isdir(d):
+ continue
+ for p in mdfind("kMDItemUseCount > 0", only_in=d):
+ if any(s in p for s in EXCLUDE_SUBSTR):
+ continue
+ seen.add(p)
+ # Launched apps, anywhere on the Mac.
+ for p in mdfind('kMDItemContentType == "com.apple.application-bundle" '
+ '&& kMDItemUseCount > 0'):
+ seen.add(p)
+ return sorted(seen)
+
+
+def main():
+ ap = argparse.ArgumentParser(description="Blue-tag everything you've opened.")
+ ap.add_argument("--dry-run", action="store_true",
+ help="list targets, change nothing")
+ ap.add_argument("--untag", action="store_true",
+ help="remove the Blue tag from the same targets")
+ ap.add_argument("--dirs", nargs="*", default=None,
+ help="override the default surfaces")
+ args = ap.parse_args()
+
+ dirs = [os.path.expanduser(d) for d in args.dirs] if args.dirs else DEFAULT_DIRS
+ targets = gather_targets(dirs)
+
+ changed = 0
+ for p in targets:
+ tags = read_tags(p)
+ if args.untag:
+ if has_blue(tags):
+ if not args.dry_run:
+ write_tags(p, [t for t in tags if t.split("\n")[0] != TAG_NAME])
+ changed += 1
+ print(("WOULD UNTAG " if args.dry_run else "untagged ") + p)
+ else:
+ if not has_blue(tags):
+ if not args.dry_run:
+ write_tags(p, tags + [TAG])
+ changed += 1
+ print(("WOULD TAG " if args.dry_run else "tagged ") + p)
+
+ verb = "would change" if args.dry_run else ("untagged" if args.untag else "tagged")
+ print(f"\n{len(targets)} opened items found · {changed} {verb}.", file=sys.stderr)
+
+
+if __name__ == "__main__":
+ main()
(oldest)
·
back to Opened Tagger
·
add light-blue-text web viewer (opened=blue, unopened=bold w bc8dd09 →