| name | location |
| triggers | ["location","gps","where","place","places","visit","visits","track","position","coordinates","attendance","reverse geocode","day summary","neighborhood","summary"] |
| description | Location tracking, place recognition, visit history, and calendar attendance |
| cli | true |
| env | [{"var":"LOCATION_DB_PATH","from":"setup_env"},{"var":"CALDAV_URL","from":"config","config_path":"caldav_url","when":"caldav_url","gate_has_discovered_calendars":true},{"var":"CALDAV_USERNAME","from":"config","config_path":"caldav_username","when":"caldav_url","gate_has_discovered_calendars":true},{"var":"CALDAV_PASSWORD","from":"config","config_path":"caldav_password","when":"caldav_url","gate_has_discovered_calendars":true,"sensitive":true}] |
Location Skill
GPS-based location tracking via the Overland iOS app. Tracks location pings, resolves named places, and records visits.
Places
Places (named geofences) are stored in the database. Full CRUD via CLI:
places — list all saved places (includes id for each)
learn — save current GPS position as a named place
update — modify an existing place (category, name, radius, coordinates, notes)
delete — remove a place (also clears place assignment from historical pings)
place-stats — visit count, first/last/longest visit, total time spent (derived from pings)
Changes take effect on the next incoming GPS ping (no restart needed).
Discover and dismiss
The web UI surfaces "discovered clusters" — recurring locations that aren't yet saved as named places. The same flow is available via CLI:
discover — find clusters of stationary pings not assigned to any place. Filters out clusters near existing places or inside dismissed zones.
dismiss-cluster — record a lat/lon/radius zone so future discover calls skip it (use when the user doesn't want a place suggested again).
list-dismissed — list dismissed cluster zones with their ids.
restore-dismissed — un-dismiss a zone by id (so it can surface again).
CLI
Run istota-skill location --help (or istota-skill location <subcommand> --help) to see the live argument list.
All commands output JSON. LOCATION_DB_PATH (the user's per-user location.db) is set automatically by the skill's setup_env hook. The reverse-geocode and day-summary subcommands also read ISTOTA_DB_PATH (the framework DB) for the cross-user reverse-geocode cache.
istota-skill location current
istota-skill location history
istota-skill location history --limit 50
istota-skill location history --date 2026-02-15
istota-skill location history --date 2026-02-15 --tz America/New_York
istota-skill location places
istota-skill location learn "coffee shop"
istota-skill location learn "gym" --category gym --radius 75
istota-skill location learn "office" --notes "side entrance, 4th floor"
istota-skill location update --name "coffee shop" --category food
istota-skill location update --name "old name" --rename "new name"
istota-skill location update --id 42 --radius 200 --notes "back entrance"
istota-skill location update --id 42 --notes ""
istota-skill location update --name "office" --lat 40.71 --lon -74.01
istota-skill location delete --name "coffee shop"
istota-skill location delete --id 42
istota-skill location attendance
istota-skill location attendance --date 2026-02-15
istota-skill location attendance --event "dentist"
istota-skill location reverse-geocode --lat 40.71 --lon -74.01
istota-skill location day-summary --date 2026-03-08
istota-skill location day-summary --date 2026-03-08 --tz America/New_York
istota-skill location place-stats --name "home"
istota-skill location place-stats --id 42
istota-skill location discover
istota-skill location discover --min-pings 20
istota-skill location dismiss-cluster --lat 40.71 --lon -73.98 --radius 200
istota-skill location list-dismissed
istota-skill location restore-dismissed 7
Output Examples
current
Aliased as last (istota-skill location last).
{
"last_ping": {
"timestamp": "2026-02-20T10:30:00Z",
"lat": 40.71,
"lon": -73.98,
"accuracy": 5,
"activity_type": "stationary",
"place": "home"
},
"current_visit": {
"place_name": "home",
"entered_at": "2026-02-20T08:00:00Z",
"duration_minutes": 150,
"ping_count": 30
}
}
history
[
{
"timestamp": "2026-02-20T10:30:00Z",
"lat": 40.71,
"lon": -73.98,
"accuracy": 5,
"place": "home",
"activity_type": "stationary"
}
]
places
[
{
"id": 1,
"name": "home",
"lat": 40.71,
"lon": -73.98,
"radius_meters": 150,
"category": "home",
"notes": null
}
]
learn
{
"status": "ok",
"place": "coffee shop",
"lat": 40.75,
"lon": -73.99,
"radius_meters": 100,
"message": "Saved 'coffee shop' at 40.7500, -73.9900"
}
update
{
"status": "ok",
"place": {
"id": 42,
"name": "coffee shop",
"lat": 40.75,
"lon": -73.99,
"radius_meters": 100,
"category": "food",
"notes": null
}
}
delete
{
"status": "ok",
"deleted": "coffee shop"
}
place-stats
{
"place_id": 42,
"total_visits": 12,
"first_visit": "2026-01-08T09:00:00Z",
"last_visit": "2026-04-22T18:30:00Z",
"avg_duration_min": 75,
"total_duration_min": 902,
"longest_visit_min": 210
}
discover
{
"clusters": [
{
"lat": 40.7580,
"lon": -73.9855,
"total_pings": 38,
"first_seen": "2026-03-01T08:00:00Z",
"last_seen": "2026-04-25T17:30:00Z",
"radius_meters": 75
}
]
}
list-dismissed
{
"dismissed": [
{
"id": 7,
"lat": 40.71,
"lon": -73.98,
"radius_meters": 100,
"dismissed_at": "2026-04-20T12:00:00Z"
}
]
}
reverse-geocode
{
"display_name": "123 Main St, New York, NY 10001, USA",
"neighborhood": "Downtown",
"suburb": "Central LA",
"road": "Main St",
"city": "New York",
"source": "nominatim"
}
day-summary
Clusters the day's pings into stops. Resolves location names by: (1) direct place match from ping data, (2) proximity match against saved places (100m minimum radius), (3) reverse geocoding via Nominatim. Filters out transit clusters (1-2 pings without a place match). Merges consecutive stops at the same location.
{
"date": "2026-03-08",
"timezone": "America/Los_Angeles",
"ping_count": 120,
"transit_pings": 8,
"stops": [
{
"location": "home",
"location_source": "saved_place",
"arrived": "08:00",
"departed": "09:30",
"ping_count": 20,
"lat": 40.71,
"lon": -74.01
},
{
"location": "Magnolia Park",
"location_source": "nominatim",
"road": "Elm St",
"neighborhood": null,
"suburb": "Magnolia Park",
"arrived": "10:15",
"departed": "12:30",
"ping_count": 25,
"lat": 40.78,
"lon": -73.96
}
]
}
attendance
Cross-references calendar events with GPS pings to confirm attendance. Skips all-day events, events without a location, and virtual meetings. Resolves event locations by matching against known places first, then geocoding via Nominatim (results cached in DB). Uses a 30-minute buffer around event times and a default 200m radius (or the place's radius if matched).
{
"date": "2026-02-20",
"events": [
{
"summary": "Dentist",
"uid": "abc123",
"start": "2026-02-20T10:00:00-08:00",
"end": "2026-02-20T11:00:00-08:00",
"location": "123 Main St",
"location_resolved": true,
"resolution_source": "geocode",
"event_lat": 40.71,
"event_lon": -73.98,
"radius_meters": 200,
"attended": true,
"first_nearby_ping": "2026-02-20T09:45:00Z",
"last_nearby_ping": "2026-02-20T10:55:00Z",
"nearby_ping_count": 12
}
]
}