|
| 1 | +--- |
| 2 | +name: browser-history |
| 3 | +description: Search the user's browsing history (Firefox or any Chromium-based browser — Chrome, Vivaldi, Brave, Edge, Arc, Chromium) by keyword, URL, or time. Auto-detects default browser. Use when the user wants to find something they looked at, recall a link, or see what they were browsing around a specific time. |
| 4 | +argument-hint: [search term or question] [--browser=firefox|chrome|vivaldi|brave|edge|arc|chromium] |
| 5 | +allowed-tools: Bash, Read |
| 6 | +--- |
| 7 | + |
| 8 | +# Browser History Search |
| 9 | + |
| 10 | +Help the user find pages from their browsing history. Auto-detect the default browser and query the right database. If the user explicitly names a browser (`--browser=firefox`, "search Firefox for…"), honour that override — useful when they're not sure which browser they were in. |
| 11 | + |
| 12 | +## Step 1: Detect the default browser |
| 13 | + |
| 14 | +Run this once at the start to get the engine and DB path. macOS reads LaunchServices; Linux uses xdg-settings. |
| 15 | + |
| 16 | +```bash |
| 17 | +detect_browser() { |
| 18 | + case "$OSTYPE" in |
| 19 | + darwin*) |
| 20 | + # Avoid $N references — the skill loader strips them when rendering into context. |
| 21 | + BUNDLE=$(defaults read ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure 2>/dev/null \ |
| 22 | + | grep -B1 'LSHandlerURLScheme = https;' \ |
| 23 | + | head -n 1 \ |
| 24 | + | sed -E 's/.*"([^"]+)".*/\1/') |
| 25 | + APPSUP="$HOME/Library/Application Support" |
| 26 | + case "$BUNDLE" in |
| 27 | + org.mozilla.firefox) ENGINE=firefox; DB="$APPSUP/Firefox/Profiles/*.default-release/places.sqlite" ;; |
| 28 | + org.mozilla.firefoxdeveloperedition) ENGINE=firefox; DB="$APPSUP/Firefox/Profiles/*.dev-edition-default/places.sqlite" ;; |
| 29 | + com.google.chrome) ENGINE=chromium; DB="$APPSUP/Google/Chrome/Default/History" ;; |
| 30 | + com.vivaldi.vivaldi) ENGINE=chromium; DB="$APPSUP/Vivaldi/Default/History" ;; |
| 31 | + com.brave.browser) ENGINE=chromium; DB="$APPSUP/BraveSoftware/Brave-Browser/Default/History" ;; |
| 32 | + com.microsoft.edgemac) ENGINE=chromium; DB="$APPSUP/Microsoft Edge/Default/History" ;; |
| 33 | + company.thebrowser.browser) ENGINE=chromium; DB="$APPSUP/Arc/User Data/Default/History" ;; |
| 34 | + org.chromium.Chromium) ENGINE=chromium; DB="$APPSUP/Chromium/Default/History" ;; |
| 35 | + *) echo "Unknown bundle ID: $BUNDLE" >&2; return 1 ;; |
| 36 | + esac |
| 37 | + ;; |
| 38 | + *) |
| 39 | + DESKTOP=$(xdg-settings get default-web-browser 2>/dev/null) |
| 40 | + case "$DESKTOP" in |
| 41 | + firefox.desktop) ENGINE=firefox; DB="$HOME/.mozilla/firefox/*.default-release/places.sqlite" ;; |
| 42 | + firefox-developer-edition.desktop) ENGINE=firefox; DB="$HOME/.mozilla/firefox/*.dev-edition-default/places.sqlite" ;; |
| 43 | + google-chrome.desktop) ENGINE=chromium; DB="$HOME/.config/google-chrome/Default/History" ;; |
| 44 | + vivaldi-stable.desktop|vivaldi.desktop) ENGINE=chromium; DB="$HOME/.config/vivaldi/Default/History" ;; |
| 45 | + brave-browser.desktop) ENGINE=chromium; DB="$HOME/.config/BraveSoftware/Brave-Browser/Default/History" ;; |
| 46 | + microsoft-edge.desktop) ENGINE=chromium; DB="$HOME/.config/microsoft-edge/Default/History" ;; |
| 47 | + chromium.desktop|chromium-browser.desktop) ENGINE=chromium; DB="$HOME/.config/chromium/Default/History" ;; |
| 48 | + *) echo "Unknown default browser: $DESKTOP" >&2; return 1 ;; |
| 49 | + esac |
| 50 | + ;; |
| 51 | + esac |
| 52 | + # Resolve any glob in DB (Firefox profile dirs) |
| 53 | + DB=$(ls $DB 2>/dev/null | head -1) |
| 54 | + echo "engine=$ENGINE db=$DB" |
| 55 | +} |
| 56 | +detect_browser |
| 57 | +``` |
| 58 | + |
| 59 | +If the user passed `--browser=NAME`, skip detection and set `ENGINE`/`DB` from the same table. |
| 60 | + |
| 61 | +## Step 2: Copy the DB |
| 62 | + |
| 63 | +The history DB is locked while the browser runs. Always copy first: |
| 64 | + |
| 65 | +```bash |
| 66 | +cp "$DB" /tmp/history_copy.sqlite |
| 67 | +``` |
| 68 | + |
| 69 | +## Step 3: Query — branch on engine |
| 70 | + |
| 71 | +### Firefox (`moz_places` + `moz_historyvisits`, unix-epoch µs) |
| 72 | + |
| 73 | +```sql |
| 74 | +-- Search by keyword (title or URL) |
| 75 | +sqlite3 -header -column /tmp/history_copy.sqlite " |
| 76 | + SELECT datetime(v.visit_date/1000000,'unixepoch','localtime') as time, |
| 77 | + p.title, p.url |
| 78 | + FROM moz_historyvisits v JOIN moz_places p ON v.place_id=p.id |
| 79 | + WHERE p.title LIKE '%KEYWORD%' OR p.url LIKE '%KEYWORD%' |
| 80 | + ORDER BY v.visit_date DESC LIMIT 20;" |
| 81 | + |
| 82 | +-- Activity around a timestamp (±30 min context window) |
| 83 | +sqlite3 -header -column /tmp/history_copy.sqlite " |
| 84 | + SELECT datetime(v.visit_date/1000000,'unixepoch','localtime') as time, |
| 85 | + p.title, p.url |
| 86 | + FROM moz_historyvisits v JOIN moz_places p ON v.place_id=p.id |
| 87 | + WHERE datetime(v.visit_date/1000000,'unixepoch','localtime') |
| 88 | + BETWEEN 'YYYY-MM-DD HH:MM' AND 'YYYY-MM-DD HH:MM' |
| 89 | + ORDER BY v.visit_date;" |
| 90 | + |
| 91 | +-- Browse a specific date |
| 92 | +sqlite3 -header -column /tmp/history_copy.sqlite " |
| 93 | + SELECT datetime(v.visit_date/1000000,'unixepoch','localtime') as time, |
| 94 | + p.title, p.url |
| 95 | + FROM moz_historyvisits v JOIN moz_places p ON v.place_id=p.id |
| 96 | + WHERE date(v.visit_date/1000000,'unixepoch','localtime') = 'YYYY-MM-DD' |
| 97 | + ORDER BY v.visit_date;" |
| 98 | +``` |
| 99 | + |
| 100 | +### Chromium (`urls` + `visits`, WebKit-epoch µs — offset 11644473600000000) |
| 101 | + |
| 102 | +```sql |
| 103 | +-- Search by keyword (title or URL) |
| 104 | +sqlite3 -header -column /tmp/history_copy.sqlite " |
| 105 | + SELECT datetime((v.visit_time - 11644473600000000)/1000000,'unixepoch','localtime') as time, |
| 106 | + u.title, u.url |
| 107 | + FROM visits v JOIN urls u ON v.url = u.id |
| 108 | + WHERE u.title LIKE '%KEYWORD%' OR u.url LIKE '%KEYWORD%' |
| 109 | + ORDER BY v.visit_time DESC LIMIT 20;" |
| 110 | + |
| 111 | +-- Activity around a timestamp |
| 112 | +sqlite3 -header -column /tmp/history_copy.sqlite " |
| 113 | + SELECT datetime((v.visit_time - 11644473600000000)/1000000,'unixepoch','localtime') as time, |
| 114 | + u.title, u.url |
| 115 | + FROM visits v JOIN urls u ON v.url = u.id |
| 116 | + WHERE datetime((v.visit_time - 11644473600000000)/1000000,'unixepoch','localtime') |
| 117 | + BETWEEN 'YYYY-MM-DD HH:MM' AND 'YYYY-MM-DD HH:MM' |
| 118 | + ORDER BY v.visit_time;" |
| 119 | + |
| 120 | +-- Browse a specific date |
| 121 | +sqlite3 -header -column /tmp/history_copy.sqlite " |
| 122 | + SELECT datetime((v.visit_time - 11644473600000000)/1000000,'unixepoch','localtime') as time, |
| 123 | + u.title, u.url |
| 124 | + FROM visits v JOIN urls u ON v.url = u.id |
| 125 | + WHERE date((v.visit_time - 11644473600000000)/1000000,'unixepoch','localtime') = 'YYYY-MM-DD' |
| 126 | + ORDER BY v.visit_time;" |
| 127 | +``` |
| 128 | + |
| 129 | +## Workflow |
| 130 | + |
| 131 | +1. Detect default browser (or honour `--browser=` override). Tell the user which engine + browser you resolved to. |
| 132 | +2. Copy the DB to `/tmp/history_copy.sqlite` |
| 133 | +3. Pick the query block matching `$ENGINE`. Substitute keyword / date / time window. |
| 134 | +4. If the user is looking for something they found *near* another page, find the anchor first, note its timestamp, then query a window around it. |
| 135 | +5. Present results concisely — time, title, URL. |
| 136 | +6. If too many results, narrow with extra filters or a tighter window. |
| 137 | +7. Clean up: `rm /tmp/history_copy.sqlite` |
| 138 | + |
| 139 | +## Tips |
| 140 | + |
| 141 | +- Users often remember vaguely. Try multiple LIKE patterns if the first doesn't hit. |
| 142 | +- Twitter/X links often appear as both `twitter.com` and `x.com` — search both. `t.co` short URLs indicate a click-through from Twitter. |
| 143 | +- For navigation chains: Firefox has `moz_historyvisits.from_visit` → another visit row. Chromium has `visits.from_visit` → another visit row. Both join back to themselves. |
| 144 | +- If the user can't remember which browser they were in, run the query against the other engine too — pass `--browser=` to force. |
| 145 | +- Chromium profiles other than `Default` (e.g. `Profile 1`, `Profile 2`) live alongside `Default`. Firefox dev-edition uses `*.dev-edition-default` instead of `*.default-release`. |
| 146 | + |
| 147 | +## User's Request |
| 148 | + |
| 149 | +$ARGUMENTS |
0 commit comments