原始内容
Scripts
These are personal productivity utilities that simplify my workflow on Windows (Cygwin) and Linux. These are in 2 places:
| Type | Location | Windows Path | Linux Path |
|---|---|---|---|
| Public | GitHub | C:\code\scripts\ |
~/code/scripts/ |
| Private | Dropbox | C:\Dropbox\scripts\ |
/c/Dropbox/scripts/ |
Note: I use /c/Dropbox as a symlink to C:\Dropbox in Cygwin to standardize scripts.
Common Setup
echo 'source ~/code/scripts/setup.fish' >> ~/.config/fish/config.fish
echo 'source ~/code/scripts/setup.bash' >> ~/.bashrc
See agents/ for setting up AI code editors.
Windows Setup
On an Admin command prompt, run:
CD /D C:\cygwin\home\Anand\
MKLINK /H .gitconfig C:\code\scripts\.gitconfig
MKLINK /D .ssh C:\Dropbox\scripts\.ssh
REM TODO: Set up `llm` keys.json on Windows
Disable inherited permissions, remove all permissions, and only add yourself with full permissions.
System setup
Here are the setup details for my laptops.
- Linux
- Media Keys: Gnome keybindings
- Windows
- Android
- Online tools replacing installed software
Config files
- setup.fish: fish config file. Symlinked to
~/.config/fish/config.fishon Linux/Cygwin. - setup.bash: bash config file. Symlinked to
~/.bashrcon Linux/Cygwin. - .gitconfig: git config file. Symlinked to
~/.gitconfigon Linux/Cygwin. - git-ignore: global git ignore file. Symlinked to
~/.config/git/ignoreon Linux/Cygwin. - .tmux.conf: tmux config file. Symlinked to
~/.tmux.confon Linux/Cygwin. - espanso-match-base.yml: espanso match file. Symlinked to
~/.config/espanso/match/base.ymlon Linux/Cygwin. - touchegg.conf: touchpad gesture config file. Symlinked to
~/.config/touchegg/touchegg.confon Linux/Cygwin.
Scripts
- aboutmerge.py. Updates people-specific notes from my transcripts. I run About Updates on ChatGPT, save output in
~/Dropbox/notes/about/week-YYYY-MM-DD.md, and runaboutmerge.pyto merge the new notes into~/Dropbox/notes/about/*.md. - activities.py generates daily activity reports in
~/Documents/activities/YYYY-MM-DD.tsvfrom calendar events, sent mail, commits, browser history, and coding-agent prompts. By default it fills pending days through yesterday. Examples:activities.py --date 2026-05-14,activities.py --days 3 --limit-per-source 50,activities.py --sources calendar,email,commit --dry-run. - ask records a short voice note, sends it to
llmfor custom action (transcribe, bash code, fish code, ...), copies to clipboard - askwin calls ask and pastes on window we called it from. Triggered by Ctrl + Alt + 0
- asu is a one-off ASU GSV calendar query helper for April 2026 events; the script itself says to delete it after 20 Apr 2026.
- audiosync.py syncs audio and video files using cross-correlation. Usage:
uv run audiosync.py video.mkv audio.opus output.mkv. I use this to sync screen recordings viavideorecordwith phone audio recordings of better quality. - backupgoogle.py archives Google Mail, Calendar, and Chat from currently logged in
gwsuser into/home/sanand/Documents/data/$EMAIL/.backupgoogle.py --since YYYY-MM-DD --until YYYY-MM-DD. - backuplinkedin.py backs up LinkedIn posts and comments that the standard export misses via a logged-in CDP browser. Examples:
backuplinkedin.py posts --username sanand0 --limit 5,backuplinkedin.py posts --username sanand0 --no-comments --dry-run,backuplinkedin.py --describe | jaq .. - backupmeet.py archives owned Google Meet recordings/transcripts from
root.node@gmail.comDrive into/home/sanand/Documents/Meet Recordings/, renames files to start with the meeting date, converts.mp4files to.opusin/home/sanand/Documents/calls/, and deletes Drive originals older than--older-than 7dafter a verified copy. It refuses to run against any othergwsaccount. Set up the isolated login withGOOGLE_WORKSPACE_CLI_CONFIG_DIR="$HOME/.config/gws-root.node@gmail.com" gws auth login, then run e.g.backupmeet.py --dry-run,backupmeet.py --older-than 365d --type video --limit 3, orbackupmeet.py --before "6 months ago" --type transcript. - backupwhatsapp.py backs up WhatsApp Web conversations through Chrome DevTools Protocol into
~/Documents/data/whatsapp/. Examples:backupwhatsapp.py --limit 5,backupwhatsapp.py --conversation "Family" --format jsonl,backupwhatsapp.py --since 2026-05-01 --until 2026-05-17 --limit 20 | moor. - browsing_history.py syncs Microsoft Edge URL activity from
Historyand recoverableShortcutsrecords into~/Documents/data/browsing-history.db, then queries it as TSV, CSV, or JSON. Examples:browsing_history.py --root ~/.config/microsoft-edge --sync-only,browsing_history.py --no-sync --since 6m --fields timestamp,activity_source,url,title --limit 100. - ccusage shows Claude Code usage and reset times - if you're already logged into Claude Code CLI.
- chars lists non-ASCII characters in files.
- chatgpt opens ChatGPT through a CDP browser, prepares prompts with optional files, model URL, project, connector mentions, reasoning selection, submit/no-submit modes, and
--save [TARGET]Markdown capture. Usechatgpt --inspect --format json | jaq .before relying on current UI labels; examples:chatgpt --save "Draft this",chatgpt --file report.pdf --project "Transcripts" "Analyze this",cat prompt.md | chatgpt --connector "Google Drive" --format json. - clean_markdown.py normalizes Markdown list spacing (removes extra blank lines inside lists while preserving paragraph breaks). Supports file, clipboard (
--xclip), and self-tests (--test). - copy-to-markdown.sh converts clipboard rich text (HTML) to Markdown in clipboard. Usage: Ctrl + C, then Ctrl + Alt + C
- daily-activities runs daily personal activity and backup jobs under a lock, including
activities.py, open tab snapshots in~/Documents/data/open-tabs/, Google backups, summarizers, and unmetered-only rsync/rclone jobs. It is normally invoked by thedaily-activities.*systemd service. - dev.sh runs a development container for safer experimentation.
- dev.dockerfile contains the image spec.
- dev.test.sh smoke-tests the dev tool environment. By default it re-runs itself through
dev.sh; use--local-onlyto test the current shell instead.
- consolidate_transcripts.py aggregates lessons from my call transcript into a unified transcripts.md.
- daydream fuses recalled concepts into radical ideas. Example:
daydream -c llm -c oblique-strategies "web app" - discourse.py extracts recent posts from a Discourse category or topic.
- dock.sh restarts GNOME user extensions and the Ubuntu AppIndicators extension after screen blanking breaks the dock/tray state.
- freeslots.py suggests preferred and fallback meeting slots from Google Calendar free/busy data via
gws, explicitly showing my time zone and the requested time zone with DST-aware abbreviations. It skips weekends for broad searches, moves recognized-holiday slots to the fallback section with the holiday name, honors explicit one-day dates, and keeps the longest 3 slots per day unless overridden. Examples:freeslots.py --timezone UK --days 7,freeslots.py --timezone "San Francisco" --since tomorrow --until "next friday" --slots-per-day 2,freeslots.py --timezone ET --duration 45 --include-weekends --format json | jaq .. - gitget clones a git repository and copies specific paths to local directories.
- git-size shows the size of changes that
git add <repo-root>would stage in net lines and total bytes. - git-stage-repo stages committed files from a repo under the current repo. Useful when experimenting in sub-folders with git, then squashing the changes in the parent. Same files are tracked by parent and child repos.
- git-uncommitted lists directories not git-synced with remote.
- githubscore.py evaluates GitHub developer quality.
- googleconnections.py lists apps connected to the Google Account in a CDP browser on port 9222, including URL, app name, access time, permissions, and connection ID. Defaults to TSV sorted by URL; use
googleconnections.py --format csvfor CSV orgoogleconnections.py --format jsonl --limit 5for a small structured sample. - gmail: Gmail search CLI. Uses OAuth tokens from google_oauth.py.
- gwslog.py shows recent Google Drive changes through
gws, with cached folder paths and shared-drive names.- Recent Docs:
gwslog.py --since 7d --type doc - Path/user feed:
gwslog.py --path Innovation --user s.anand@gramener.com --since 30d --format jsonl - Copy a compact list:
gwslog.py --since 1d --columns "iso user title path link" | xclip -selection clipboard - Incremental feed from another account:
GOOGLE_WORKSPACE_CLI_CONFIG_DIR=~/.config/gws-root.node@gmail.com gwslog.py since
- Recent Docs:
- histfreq ranks the most common commands from a NUL-delimited shell history stream.
history --null | uv run histfreq.py -n 40. - htmlemail.py renders Markdown to HTML and sends as email via Gmail API. Usage:
uv run htmlemail.py --from EMAIL --email EMAIL body.md.- Initialized via
htmlemail.py --init --client-secrets google-root.node-desktop_872568319651-7pde9a28vem61qfvon8pu0d9bgijv8lf.apps.googleusercontent.com.jsonwith Google OAuth desktop client secrets JSON.
- Initialized via
- linkedin.py is a CLI for LinkedIn that uses CDP to fetch profiles, etc.
linkedin.py profile sanand0fetches my profile. - livetranscribe watches a growing
.opusrecording and streams timestamped transcription via the Gemini Live API. Examples:livetranscribe ~/Documents/calls/meeting.opus,livetranscribe meeting.opus --output notes.txt --prompt "Technical meeting",livetranscribe meeting.opus --dry-run. - mailindex.py builds a compact, resumable SQLite FTS index for
~/Documents/Mail/*.mboxat~/Documents/Mail/mail-index.sqlite. - mcpserver.py exposes an MCP server on localhost:8000 that lets LLMs run bash commands. Useful for ChatGPT to control your machine. Run in sandbox to reduce risk.
- musictag.py manages MP3 ID3 tags from
~/Music/musicdump.csv, with dry-run cleanup diffs, CSV dump/apply parity, managed tag columns, and preserved personalPOPM/clean longUSLTframes. Examples:musictag.py dump,musictag.py fix "Album.Title.mp3",musictag.py fix --write --genre Tamil --year 2014 *.mp3,musictag.py check | moor. - q is a terminal AI chat interface.
q 'What is 2 + 2?' --llm chatgptopens Google AI mode, asks the question, and prints the answer.q --m chatgpt 'What is 2 + 2?'does the same with ChatGPT. - recall shows a random note bullet. Example:
recallorrecall talks - rename_receipts.py renames PDF receipts to
YYYY-MM-DD Service $0.00 Card-1234.pdfby extracting date, vendor, amount, and last-4 card details from invoice text. - rgb converts RGB to hex and vice versa.
- rofi-files.sh and rofi-chrome-tabs.sh are used by rofi to get recent files. Triggered by Ctrl + Alt + F.
- rofi-clip.sh opens a rofi clipboard transform menu (text/Markdown/Rich text/URL/date utilities), applies the selected transform, and writes back to clipboard. Triggered by Ctrl + Alt + M (since it's mostly Markdown related).
- rofi-prompts.sh shows prompts from Markdown files in
~/code/blog/pages/promptsand~/code/scripts/agents/**/SKILL.mdlets you pick one via rofi, then copies/pastes the selected fenced code block. Triggered by Ctrl + Alt + P. - rss-archive-page is called by
uvx feed2exec fetchvia~/.config/feed2exec.inito download RSS feeds into~/Documents/rss/$FEED/*.htmlas single-page HTMLs. - skilluse.py scans Codex, Claude, and Copilot logs for agent skill usage. Examples:
skilluse.py --agent codex --skill "github:*",skilluse.py --format json | jaq .,skilluse.py --describe | jaq .. - slide.py creates slides from Markdown files. Usage:
uvx slide presentation.md - summarize.py adds AI-generated metadata (summary, keywords, people, actions for transcripts; description and keywords for blog posts) to Markdown files. Already-processed files are skipped.
- New transcripts:
summarize.py transcript "/home/sanand/Dropbox/notes/transcripts/2026-04-*.md"(edit2026-04to the target month) - New blog posts:
ug -rl '^date: "?2026-04' /home/sanand/code/blog/posts/ | xargs summarize.py blog(edit2026-04to the target month)
- New transcripts:
- touchegg.conf is my touchpad gesture config for Touchegg on Ubuntu.
- Global Gestures (Application: All)
- Swipe Up 4 fingers: Increase volume 5%
- Swipe Down 4 fingers: Decrease volume 5%
- Swipe Left/Right 4 fingers: Pause / Play on VLC
- Tap 2 fingers: Right mouse click (Button 3) on gesture begin.
- Tap 3 fingers: Middle mouse click (Button 2) on gesture begin.
- Application: microsoft-edge
- Pinch Out 2 fingers: Zoom In
- Pinch In 2 fingers: Zoom Out
- Swipe Left 3 fingers: Back
- Swipe Right 3 fingers: Forward
- Global Gestures (Application: All)
- timers picks a systemd user timer with
fzf, sorted by its most recent run, then follows the activated service's journal. - unbrace.js unwraps single-statement JavaScript blocks.
- transcribe_calls.py transcribes missing audio call recordings from
~/Documents/callsinto Markdown notes under~/Dropbox/notes/transcripts, with chunking and patching for invalid transcript sections. Examples:transcribe_calls.py --dry-run,transcribe_calls.py --glob "*.opus",transcribe_calls.py --patch-invalid-sections. - update-files caches directory listings so
rofi-files.shcan stay fast even on large mounts. - viz.py embeds CSV files a HTML templates. This is the script that started Gramener in 2011.
AI coding agent scripts
- agentlog.py reads logs from all AI coding agents: Codex, Claude, and Copilot
- opencodelog.jq converts OpenCode session logs to Markdown (from
opencode export sessionID) but I rarely use it since I don't use OpenCode much. - codextools.py lists tools used by Codex
- codexerrors.py lists tool errors by Codex
- podcast.py renders speaker-labeled Markdown into a compact MP3 or Opus podcast with Gemini TTS, cached per segment for cheap resumes. Examples:
podcast.py notes.md --dry-run,podcast.py notes.md --output episode.mp3,podcast.py notes.md --parallel 8.
jq scripts
- jsonpaths.jq lists all unique JSON paths in a JSON/NDJSON file
- tsv.jq converts a JSON array of objects into TSV
- whatsappthread.jq converts https://tools.s-anand.net/whatsappscraper/ JSON into LLM-friendly JSONL + thread_id + urls[]
Others
- generate/ has scripts to generate data.
- pdbhook/ runs Python debugger on error. Usage:
PYTHONPATH=~/code/scripts/pdbhook uv run script.py
Services
services/ has systemd services that are installed by services/setup.sh. Current services are:
consolidate-transcripts-daily.*: Consolidate transcript learningsdaily-activities.*: Run daily activity reports, open tab snapshots, metadata summarizers, and unmetered-only personal backup/sync jobstrending-repo-weekly.*: Update trending GitHub reposupdate-files-daily.*: Update local filesupdate-files-weekly.*: Update mounted files