-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbb.edn
More file actions
224 lines (190 loc) · 12.8 KB
/
bb.edn
File metadata and controls
224 lines (190 loc) · 12.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
{:min-bb-version "1.3.0"
;; ╔═══════════════════════════════════════════════════════════════════════╗
;; ║ Places Database - Babashka Tasks ║
;; ║ Google Maps → SQLite → MCP Server → Claude ║
;; ║ ║
;; ║ Quick start: ║
;; ║ 1. bb setup ║
;; ║ 2. cp your_places*.csv data/ ║
;; ║ 3. bb places:import ║
;; ║ 4. bb index:fts ║
;; ║ 5. bb geo:enrich (optional, adds missing coords) ║
;; ║ 6. bb mcp:config (copy to Claude Desktop config) ║
;; ║ ║
;; ║ See CLAUDE.md for full docs ║
;; ╚═══════════════════════════════════════════════════════════════════════╝
:tasks
{;; ═══════════════════════════════════════════════════════════════════════
;; Import & Update
;; ═══════════════════════════════════════════════════════════════════════
places:import
{:doc "Initial import of CSV files to SQLite
Usage: bb places:import [file.csv]
Default: imports all CSVs from data/*.csv
Creates SQLite database with deduplication and proper indexing"
:task (shell "python3 scripts/import_places.py" (or (System/getenv "CSV_PATH") "data/*.csv"))}
places:update
{:doc "Merge new CSV exports (preserves manual enrichments)
Usage: bb places:update [file.csv]
Smart update: only adds new places or fills missing fields
Never overwrites manual notes or corrections"
:task (shell "python3 scripts/import_places.py --update" (or (System/getenv "CSV_PATH") "data/*.csv"))}
places:stats
{:doc "Show database statistics
Displays: total places, geocoding coverage, top lists/countries, ratings"
:task (shell "python3 scripts/db_stats.py")}
;; ═══════════════════════════════════════════════════════════════════════
;; Geocoding (Free via Nominatim/OSM)
;; ═══════════════════════════════════════════════════════════════════════
geo:enrich
{:doc "Geocode missing coordinates (free via Nominatim)
Rate limit: 1 req/sec → ~1 hour for 3600 places
Uses formatted_address + city + country for accuracy
Usage: bb geo:enrich [--dry-run]"
:task (shell "python3 scripts/geocode_places.py")}
geo:status
{:doc "Show geocoding coverage statistics
Displays: total/geocoded/missing, sources breakdown"
:task (shell "python3 scripts/geocode_places.py --status")}
geo:validate
{:doc "Validate coordinate accuracy
Checks for invalid lat/lon ranges and obvious errors"
:task (shell "python3 scripts/geocode_places.py --validate")}
;; ═══════════════════════════════════════════════════════════════════════
;; Indexing (FTS5, Spatial, Optional Embeddings)
;; ═══════════════════════════════════════════════════════════════════════
index:fts
{:doc "Build full-text search index (FTS5) - REQUIRED for fast search
Indexes: name, categories, address, city, region, country, notes
Run after import or when adding many new places"
:task (shell "python3 scripts/build_indexes.py --fts")}
index:embeddings
{:doc "Generate embeddings for semantic search (optional, ~$0.50)
Enables queries like: 'find places similar to X'
Uses voyage-3.1 API (requires ANTHROPIC_API_KEY)
Estimate cost first: bb cost:embeddings"
:task (shell "python3 scripts/build_indexes.py --embeddings")}
index:spatial
{:doc "Build spatial index (SpatiaLite if available, else Haversine)
Auto-detects SpatiaLite extension for faster geo queries
Fallback: Uses manual lat/lon index with Haversine distance"
:task (shell "python3 scripts/build_indexes.py --spatial")}
index:all
{:doc "Build all indexes (FTS5 + spatial)
Run after initial import for full search capability"
:depends [index:fts index:spatial]}
;; ═══════════════════════════════════════════════════════════════════════
;; MCP Server (Claude Integration)
;; ═══════════════════════════════════════════════════════════════════════
mcp:serve
{:doc "Start MCP server for Claude Desktop/Code
Usually runs automatically via Claude config
Manual start for debugging: bb mcp:serve"
:task (shell "python3 scripts/places_mcp_server.py")}
mcp:config
{:doc "Print Claude Desktop config snippet
Copy output to: ~/Library/Application Support/Claude/claude_desktop_config.json
Then restart Claude Desktop"
:task (println
"\n╔════════════════════════════════════════════════════════════╗"
"\n║ Add to Claude Desktop config.json: ║"
"\n╚════════════════════════════════════════════════════════════╝\n"
"{\n"
" \"mcpServers\": {\n"
" \"places\": {\n"
" \"command\": \"python3\",\n"
" \"args\": [\"" (str (System/getProperty "user.dir") "/scripts/places_mcp_server.py") "\"]\n"
" }\n"
" }\n"
"}\n\n"
"Config location:\n"
" ~/Library/Application Support/Claude/claude_desktop_config.json\n\n"
"After adding, restart Claude Desktop.\n")}
mcp:test
{:doc "Test MCP server locally (smoke tests)
Verifies: database connectivity, FTS index, geocoding coverage"
:task (shell "python3 scripts/test_mcp.py")}
;; ═══════════════════════════════════════════════════════════════════════
;; Database Maintenance
;; ═══════════════════════════════════════════════════════════════════════
db:backup
{:doc "Create timestamped database backup
Saves to: backups/places-YYYYMMDD-HHMMSS.db
Run before major updates or experiments"
:task (let [timestamp (.format (java.time.LocalDateTime/now)
(java.time.format.DateTimeFormatter/ofPattern "yyyyMMdd-HHmmss"))
backup-path (str "backups/places-" timestamp ".db")]
(shell "mkdir -p backups")
(shell "cp data/places.db" backup-path)
(println "✓ Backed up to" backup-path))}
db:vacuum
{:doc "Optimize database size (reclaim deleted space)
Run after large deletions or updates
Can reduce file size by 30-50%"
:task (shell "sqlite3 data/places.db 'VACUUM;'")}
db:shell
{:doc "Open interactive SQLite shell
Direct SQL access to places.db
Exit with: .quit or Ctrl+D"
:task (shell "sqlite3 data/places.db")}
;; ═══════════════════════════════════════════════════════════════════════
;; Quick Queries (Command Line)
;; ═══════════════════════════════════════════════════════════════════════
query:lists
{:doc "Show all custom lists with place counts
Sorted by count (descending)"
:task (shell "sqlite3 data/places.db"
"\"SELECT list, COUNT(*) as count FROM places GROUP BY list ORDER BY count DESC;\"")}
query:recent
{:doc "Show 20 most recently added places
Requires 'added_at' field from Google export"
:task (shell "sqlite3 data/places.db"
"\"SELECT name, list, formatted_address FROM places WHERE added_at IS NOT NULL ORDER BY added_at DESC LIMIT 20;\"")}
query:ungeocoded
{:doc "Count places still missing coordinates
Run bb geo:enrich to fill these in"
:task (shell "sqlite3 data/places.db"
"\"SELECT COUNT(*) FROM places WHERE lat IS NULL OR lon IS NULL;\"")}
query:near
{:doc "Find places near coordinates (requires args: lat lon radius_km)
Example: bb query:near 59.3293 18.0686 5
Uses Haversine distance calculation"
:task (let [[lat lon radius] *command-line-args*]
(if (and lat lon radius)
(shell "python3 scripts/spatial_query.py --near" lat lon radius)
(println "Usage: bb query:near <lat> <lon> <radius_km>")))}
;; ═══════════════════════════════════════════════════════════════════════
;; Setup & Installation
;; ═══════════════════════════════════════════════════════════════════════
setup
{:doc "Initial setup: create directories, install dependencies
Run once before first import
Installs: anthropic, requests, tqdm, python-dotenv"
:task (do
(println "🗺️ Setting up Places database...")
(shell "mkdir -p data backups scripts")
(shell "pip3 install --break-system-packages -q anthropic requests tqdm python-dotenv 2>/dev/null || true")
(println "\n✓ Setup complete!")
(println "\nNext steps:")
(println " 1. Place CSV files in data/ directory")
(println " 2. Run: bb places:import")
(println " 3. Run: bb index:fts")
(println " 4. Optional: bb geo:enrich")
(println " 5. Connect to Claude: bb mcp:config\n"))}
;; ═══════════════════════════════════════════════════════════════════════
;; Cost Estimation
;; ═══════════════════════════════════════════════════════════════════════
cost:embeddings
{:doc "Estimate embedding generation cost
Uses voyage-3.1 pricing: $0.10 per 1M tokens
~500 tokens per place → ~$0.50 for 10k places
Shows estimate without generating"
:task (shell "python3 scripts/build_indexes.py --embeddings --estimate")}
;; ═══════════════════════════════════════════════════════════════════════
;; Help
;; ═══════════════════════════════════════════════════════════════════════
help
{:doc "Show this help (alias for 'tasks')"
:task (shell "bb tasks")}
}
}