// Name: Unified Search // Description: Search files, browser history, and Atuin command history in one place. // Author: ScottGuthart // GitHub: ScottGuthart import '@johnlindquist/kit' import Database from 'better-sqlite3' type Result = | { type: 'file'; path: string } | { type: 'url'; url: string; title?: string; source?: string } | { type: 'command'; command: string; when?: string } const MAX_PER_GROUP = 20 const searchFiles = async (q: string): Promise<Result[]> => { try { const results = await fileSearch(q, { onlyin: home() }) return results.slice(0, MAX_PER_GROUP).map(p => ({ type: 'file', path: p })) } catch { return [] } } const copyDbToTmp = async (src: string, name: string) => { try { const tmpDb = tmpPath(`${name}-${path.basename(src)}-${Date.now()}.db`) await copyFile(src, tmpDb) return tmpDb } catch { return '' } } const queryChromeLike = (dbPath: string, q: string) => { try { const db = new Database(dbPath, { readonly: true, fileMustExist: true }) const stmt = db.prepare( `SELECT url, title, last_visit_time FROM urls WHERE (url LIKE ? OR title LIKE ?) ORDER BY last_visit_time DESC LIMIT ?` ) const rows = stmt.all(`%${q}%`, `%${q}%`, MAX_PER_GROUP) as { url: string; title: string }[] db.close() return rows.map(r => ({ type: 'url', url: r.url, title: r.title, source: 'Chromium' }) as Result) } catch { return [] as Result[] } } const queryFirefox = (dbPath: string, q: string) => { try { const db = new Database(dbPath, { readonly: true, fileMustExist: true }) const stmt = db.prepare( `SELECT url, title, last_visit_date FROM moz_places WHERE (url LIKE ? OR title LIKE ?) AND last_visit_date IS NOT NULL ORDER BY last_visit_date DESC LIMIT ?` ) const rows = stmt.all(`%${q}%`, `%${q}%`, MAX_PER_GROUP) as { url: string; title: string }[] db.close() return rows.map(r => ({ type: 'url', url: r.url, title: r.title, source: 'Firefox' }) as Result) } catch { return [] as Result[] } } const searchBrowserHistory = async (q: string): Promise<Result[]> => { const paths: string[] = [] const addIfExists = async (p: string) => (await pathExists(p)) && paths.push(p) if (isMac) { await addIfExists(home('Library', 'Application Support', 'Google', 'Chrome', 'Default', 'History')) await addIfExists(home('Library', 'Application Support', 'BraveSoftware', 'Brave-Browser', 'Default', 'History')) await addIfExists(home('Library', 'Application Support', 'Microsoft Edge', 'Default', 'History')) await addIfExists(home('Library', 'Application Support', 'Vivaldi', 'Default', 'History')) // Firefox profiles const ffProfiles = await globby(home('Library', 'Application Support', 'Firefox', 'Profiles', '*', 'places.sqlite')) paths.push(...ffProfiles) } else if (isLinux) { await addIfExists(home('.config', 'google-chrome', 'Default', 'History')) await addIfExists(home('.config', 'chromium', 'Default', 'History')) await addIfExists(home('.config', 'brave', 'Default', 'History')) await addIfExists(home('.config', 'microsoft-edge', 'Default', 'History')) const ffProfiles = await globby(home('.mozilla', 'firefox', '*.default*', 'places.sqlite')) paths.push(...ffProfiles) } const results: Result[] = [] for (const p of paths) { if (p.endsWith('places.sqlite')) { const tmp = await copyDbToTmp(p, 'ff') if (tmp) results.push(...queryFirefox(tmp, q)) } else { const tmp = await copyDbToTmp(p, 'chromium') if (tmp) results.push(...queryChromeLike(tmp, q)) } } // Deduplicate by URL const seen = new Set<string>() return results.filter(r => { if (r.type !== 'url') return true if (seen.has(r.url)) return false seen.add(r.url) return true }).slice(0, MAX_PER_GROUP) } const searchAtuin = async (q: string): Promise<Result[]> => { // Common Atuin DB locations const candidates = [ home('.local', 'share', 'atuin', 'history.db'), home('Library', 'Application Support', 'atuin', 'history.db'), ] let dbPath = '' for (const c of candidates) { if (await pathExists(c)) { dbPath = c break } } if (!dbPath) return [] try { const tmp = await copyDbToTmp(dbPath, 'atuin') if (!tmp) return [] const db = new Database(tmp, { readonly: true, fileMustExist: true }) // Atuin schema typically: history(command TEXT, timestamp INTEGER/REAL, ...). Fallback to the fields that usually exist. let rows: { command: string; timestamp?: number }[] = [] try { rows = db .prepare( `SELECT command, timestamp FROM history WHERE command LIKE ? ORDER BY timestamp DESC LIMIT ?` ) .all(`%${q}%`, MAX_PER_GROUP) } catch { rows = db .prepare( `SELECT command FROM history WHERE command LIKE ? ORDER BY ROWID DESC LIMIT ?` ) .all(`%${q}%`, MAX_PER_GROUP) } db.close() return rows.map(r => ({ type: 'command', command: r.command, })) } catch { return [] } } const toChoices = (files: Result[], urls: Result[], cmds: Result[]) => { const fileChoices = files.map(f => ({ name: path.basename(f.path) || f.path, description: f.path, value: f, group: 'Files', })) const urlChoices = urls.map(u => ({ name: u.title?.trim() || u.url, description: u.url, value: u, group: 'Browser History', })) const cmdChoices = cmds.map(c => ({ name: c.command, value: c, group: 'Commands (Atuin)', })) return [...fileChoices, ...urlChoices, ...cmdChoices] } const choicePreview = (r?: Result) => { if (!r) return '' if (r.type === 'file') { return md(`### File - Path: ${r.path} `) } if (r.type === 'url') { return md(`### URL - Title: ${r.title || '(no title)'} - Link: ${r.url} `) } return md(`### Command \`\`\`sh ${r.command} \`\`\` `) } const selected = await arg<Result>( { placeholder: 'Search files, browser history, and Atuin...', strict: false, debounceInput: 200, onChoiceFocus: async (input, { focused }) => { setPreview(choicePreview(focused?.value)) }, }, async (input: string) => { if (!input?.trim()) { return [ { name: 'Type to search across Files, Browser History, and Atuin…', value: { type: 'info' } as any, info: true, miss: true, }, ] } const [files, urls, cmds] = await Promise.all([ searchFiles(input), searchBrowserHistory(input), searchAtuin(input), ]) const choices = toChoices(files, urls, cmds) if (choices.length === 0) { return [{ name: 'No results', value: { type: 'none' } as any, info: true, miss: true }] } return choices } ) if (!selected) exit() if (selected.type === 'file') { await open((selected as Extract<Result, { type: 'file' }>).path) } else if (selected.type === 'url') { await open((selected as Extract<Result, { type: 'url' }>).url) } else if (selected.type === 'command') { await copy((selected as Extract<Result, { type: 'command' }>).command) await toast('Command copied to clipboard') } else { // do nothing }