Browser History Search
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.
Step 1: Detect the default browser
Run this once at the start to get the engine and DB path. macOS reads LaunchServices; Linux uses xdg-settings.
bash
detect_browser() {
case "$OSTYPE" in
darwin*)
# Avoid $N references — the skill loader strips them when rendering into context.
BUNDLE=$(defaults read ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure 2>/dev/null \
| grep -B1 'LSHandlerURLScheme = https;' \
| head -n 1 \
| sed -E 's/.*"([^"]+)".*/\1/')
APPSUP="$HOME/Library/Application Support"
case "$BUNDLE" in
org.mozilla.firefox) ENGINE=firefox; DB="$APPSUP/Firefox/Profiles/*.default-release/places.sqlite" ;;
org.mozilla.firefoxdeveloperedition) ENGINE=firefox; DB="$APPSUP/Firefox/Profiles/*.dev-edition-default/places.sqlite" ;;
com.google.chrome) ENGINE=chromium; DB="$APPSUP/Google/Chrome/Default/History" ;;
com.vivaldi.vivaldi) ENGINE=chromium; DB="$APPSUP/Vivaldi/Default/History" ;;
com.brave.browser) ENGINE=chromium; DB="$APPSUP/BraveSoftware/Brave-Browser/Default/History" ;;
com.microsoft.edgemac) ENGINE=chromium; DB="$APPSUP/Microsoft Edge/Default/History" ;;
company.thebrowser.browser) ENGINE=chromium; DB="$APPSUP/Arc/User Data/Default/History" ;;
org.chromium.Chromium) ENGINE=chromium; DB="$APPSUP/Chromium/Default/History" ;;
*) echo "Unknown bundle ID: $BUNDLE" >&2; return 1 ;;
esac
;;
*)
DESKTOP=$(xdg-settings get default-web-browser 2>/dev/null)
case "$DESKTOP" in
firefox.desktop) ENGINE=firefox; DB="$HOME/.mozilla/firefox/*.default-release/places.sqlite" ;;
firefox-developer-edition.desktop) ENGINE=firefox; DB="$HOME/.mozilla/firefox/*.dev-edition-default/places.sqlite" ;;
google-chrome.desktop) ENGINE=chromium; DB="$HOME/.config/google-chrome/Default/History" ;;
vivaldi-stable.desktop|vivaldi.desktop) ENGINE=chromium; DB="$HOME/.config/vivaldi/Default/History" ;;
brave-browser.desktop) ENGINE=chromium; DB="$HOME/.config/BraveSoftware/Brave-Browser/Default/History" ;;
microsoft-edge.desktop) ENGINE=chromium; DB="$HOME/.config/microsoft-edge/Default/History" ;;
chromium.desktop|chromium-browser.desktop) ENGINE=chromium; DB="$HOME/.config/chromium/Default/History" ;;
*) echo "Unknown default browser: $DESKTOP" >&2; return 1 ;;
esac
;;
esac
# Resolve any glob in DB (Firefox profile dirs)
DB=$(ls $DB 2>/dev/null | head -1)
echo "engine=$ENGINE db=$DB"
}
detect_browserIf the user passed --browser=NAME, skip detection and set ENGINE/DB from the same table.
Step 2: Copy the DB
The history DB is locked while the browser runs. Always copy first:
bash
cp "$DB" /tmp/history_copy.sqliteStep 3: Query — branch on engine
Firefox (moz_places + moz_historyvisits, unix-epoch µs)
sql
-- Search by keyword (title or URL)
sqlite3 -header -column /tmp/history_copy.sqlite "
SELECT datetime(v.visit_date/1000000,'unixepoch','localtime') as time,
p.title, p.url
FROM moz_historyvisits v JOIN moz_places p ON v.place_id=p.id
WHERE p.title LIKE '%KEYWORD%' OR p.url LIKE '%KEYWORD%'
ORDER BY v.visit_date DESC LIMIT 20;"
-- Activity around a timestamp (±30 min context window)
sqlite3 -header -column /tmp/history_copy.sqlite "
SELECT datetime(v.visit_date/1000000,'unixepoch','localtime') as time,
p.title, p.url
FROM moz_historyvisits v JOIN moz_places p ON v.place_id=p.id
WHERE datetime(v.visit_date/1000000,'unixepoch','localtime')
BETWEEN 'YYYY-MM-DD HH:MM' AND 'YYYY-MM-DD HH:MM'
ORDER BY v.visit_date;"
-- Browse a specific date
sqlite3 -header -column /tmp/history_copy.sqlite "
SELECT datetime(v.visit_date/1000000,'unixepoch','localtime') as time,
p.title, p.url
FROM moz_historyvisits v JOIN moz_places p ON v.place_id=p.id
WHERE date(v.visit_date/1000000,'unixepoch','localtime') = 'YYYY-MM-DD'
ORDER BY v.visit_date;"Chromium (urls + visits, WebKit-epoch µs — offset 11644473600000000)
sql
-- Search by keyword (title or URL)
sqlite3 -header -column /tmp/history_copy.sqlite "
SELECT datetime((v.visit_time - 11644473600000000)/1000000,'unixepoch','localtime') as time,
u.title, u.url
FROM visits v JOIN urls u ON v.url = u.id
WHERE u.title LIKE '%KEYWORD%' OR u.url LIKE '%KEYWORD%'
ORDER BY v.visit_time DESC LIMIT 20;"
-- Activity around a timestamp
sqlite3 -header -column /tmp/history_copy.sqlite "
SELECT datetime((v.visit_time - 11644473600000000)/1000000,'unixepoch','localtime') as time,
u.title, u.url
FROM visits v JOIN urls u ON v.url = u.id
WHERE datetime((v.visit_time - 11644473600000000)/1000000,'unixepoch','localtime')
BETWEEN 'YYYY-MM-DD HH:MM' AND 'YYYY-MM-DD HH:MM'
ORDER BY v.visit_time;"
-- Browse a specific date
sqlite3 -header -column /tmp/history_copy.sqlite "
SELECT datetime((v.visit_time - 11644473600000000)/1000000,'unixepoch','localtime') as time,
u.title, u.url
FROM visits v JOIN urls u ON v.url = u.id
WHERE date((v.visit_time - 11644473600000000)/1000000,'unixepoch','localtime') = 'YYYY-MM-DD'
ORDER BY v.visit_time;"Workflow
- Detect default browser (or honour
--browser=override). Tell the user which engine + browser you resolved to. - Copy the DB to
/tmp/history_copy.sqlite - Pick the query block matching
$ENGINE. Substitute keyword / date / time window. - 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.
- Present results concisely — time, title, URL.
- If too many results, narrow with extra filters or a tighter window.
- Clean up:
rm /tmp/history_copy.sqlite
Tips
- Users often remember vaguely. Try multiple LIKE patterns if the first doesn't hit.
- Twitter/X links often appear as both
twitter.comandx.com— search both.t.coshort URLs indicate a click-through from Twitter. - For navigation chains: Firefox has
moz_historyvisits.from_visit→ another visit row. Chromium hasvisits.from_visit→ another visit row. Both join back to themselves. - If the user can't remember which browser they were in, run the query against the other engine too — pass
--browser=to force. - Chromium profiles other than
Default(e.g.Profile 1,Profile 2) live alongsideDefault. Firefox dev-edition uses*.dev-edition-defaultinstead of*.default-release.
User's Request
$ARGUMENTS
