Travel assistant for NanoClaw: byAir flight notifications (delay, gate, connection risk, inbound aircraft delay, time-to-leave, arrival logistics), traffic-aware drive planning for in-person meetings (auto drive blocks + leave-by traffic rechecks), travel-booking gap checks, and nightly TripIt sync. Per-chat overlay plugin.
—
—
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
High
Do not use without reviewing
Per coding-policy: stateful-artifacts. This skill owns two cross-invocation JSON artifacts: travel-schedule.json (cross-plugin) and travel-trips-seen.json (the new-trip-detection snapshot, this skill only). (The day-indexed travel-db.json it rebuilds in Step 4 is owned by the sibling check-travel-bookings skill — see that skill's state-schema.md.)
/workspace/group/travel-schedule.jsonFlat list of upcoming TripIt events, projected from the live ICS feed.
nightly-travel-sync (this skill)scripts/refresh-travel-schedule.py (Step 2) — the sole writertessl__check-travel-bookings/scripts/build-travel-db.py (same plugin, Step 4 rebuilds travel-db.json from this file)scripts/check-travel-freshness.py (same plugin, Step 3 reads mtime only, never the body)nanoclaw-admin's morning-brief and check-cfps (cross-plugin via the shared /workspace/group/ mount, reading Trip-type records for travel-conflict checks)A JSON array. Each element is one event record:
[
{
"schema_version": 2,
"summary": "Madrid trip",
"start": "2026-06-01",
"end": "2026-06-05",
"location": "Madrid, ES",
"type": "Trip",
"uid": "trip-100@tripit.com",
"description": "..."
}
]| Field | Type | Required | Description |
|---|---|---|---|
schema_version | integer | yes | Currently 2. Present on every record (the artifact is a bare array, with no top-level object to hold a single version). |
summary | string | yes | Event title from the ICS SUMMARY. |
start / end | string | yes | YYYY-MM-DD for date-only VEVENTs (trip wrappers). YYYY-MM-DDTHH:MM:SSZ for timed VEVENTs (flights, lodging check-ins, rentals). |
location | string | no | ICS LOCATION. |
type | string | yes | Trip (trip-level wrapper) or the item [Type] from the ICS DESCRIPTION (Flight, Lodging, Rail, Car Rental, …). Unknown when absent. |
uid | string | yes | ICS UID. Trip wrappers lack item-. Items contain it. |
description | string | no | The ICS DESCRIPTION verbatim (added in v2). Carries the [Type] <DEP> to <ARR> line drive-engine's TripIt-union parser reads for a Flight's route (#156 R2). Additive — readers that don't use it are unaffected. |
Additive: the record gains the optional description field. No stored state to migrate — the schedule is regenerated in full each run, so the next Step 2 emits v2 records. Cross-plugin readers that gate on the version must accept v2: travel-core/trip_origin.py bumped its SCHEDULE_SCHEMA_VERSION to 2 (it does not read description, so it reads v1 and v2 identically).
SCHEMA_VERSION..tmp sibling, validates it is a list with the required keys, then os.replaces it into place. A failed fetch/parse or a partial write exits non-zero and leaves the prior file (and its mtime) untouched, which Step 3's two-tier probe detects.Bump SCHEMA_VERSION in refresh-travel-schedule.py for any record-shape change. The file is fully regenerated each run. The next successful Step 2 rewrites every record at the new version. No stored old-version state exists to upgrade. Readers are non-owners per coding-policy: stateful-artifacts. They extract the fields they need and tolerate the per-record schema_version without migrating it. A reader that gates on the version treats an unfamiliar value as "no usable schedule" and falls back to its own degraded path.
/workspace/group/travel-trips-seen.jsonLast-seen snapshot of the upcoming trip set, used by Step 5 to detect trips that appeared since the previous nightly run (#204). New-trip detection only — never a source of truth for trip data (that is travel-db.json).
nightly-travel-sync (this skill)scripts/detect-new-trips.py --commit (Step 5) — the sole writerscripts/detect-new-trips.py (Step 5 detect mode) — the sole reader; no cross-plugin reader{
"schema_version": 1,
"generated_at": "2026-07-20T06:00:00Z",
"trips": {
"madrid-trip-2026-06": {
"summary": "Madrid trip",
"start": "2026-06-01",
"end": "2026-06-05"
}
}
}| Field | Type | Required | Description |
|---|---|---|---|
schema_version | integer | yes | Currently 1. |
generated_at | string | yes | UTC write time, YYYY-MM-DDTHH:MM:SSZ. Diagnostic only — never read back. |
trips | object | yes | Map of trip slug → {summary, start, end}. Keys are the make_slug slugs from build-travel-db.py; a new key on the next run is a new trip. |
--commit rewrites trips to exactly the current upcoming set (end on/after today), so a trip aging into the past drops out. It is bounded by the number of upcoming trips, not ever-seen trips.schema_version), detect mode reports seeded: true with an empty new_trips; the whole itinerary is never logged as new. The following --commit writes the first snapshot. Only trips appearing after the seed count as new.--commit, so a logging failure leaves the snapshot untouched and the next run retries. The trusted-memory daily-log helper's line-dedup guards against a double entry across that retry..tmp sibling + os.replace, matching the other travel writers.Bump SCHEMA_VERSION in detect-new-trips.py (and this section) for any shape change. The owner is the sole reader and writer, and the snapshot is disposable — an unrecognised version reads as "no usable prior state" and triggers a seed rewrite at the current version, so no in-place migration path is needed. A lost snapshot costs at most one seed run (nothing logged that cycle).
.tessl-plugin
skills
check-travel-bookings
drive-engine
flight-assist
references
nightly-travel-sync
sync-tripit
travel-core