Repository navigation
chore(github-tag): pin tag devsecninja/ai-toolkit to 2491ced [automerge] #339
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| TripIt Export skill — attribution & license | ||
| ============================================ | ||
|
|
||
| This skill is adapted from Casey Grippi's "TripIt Export" skill. | ||
|
|
||
| - Source: https://lobehub.com/skills/caseyg-caseys-claude-tripit-export | ||
| - Author: Casey Grippi (@caseyg) | ||
|
|
||
| License (as stated by the upstream author): | ||
|
|
||
| Personal use. Skills contain hardcoded assumptions (locations, accounts, | ||
| preferences) that you'd need to adapt for your own use. | ||
|
|
||
| This material is redistributed here for personal use, with attribution to the | ||
| original author. Adapt it for your own accounts and preferences before use. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,274 @@ | ||
| --- | ||
| name: tripit-exporter | ||
| description: Export all TripIt travel data (trips, flights, lodging, activities, cars, rail, cruises and more) to a single JSON file using TripIt's undocumented API v2. Use when the user asks to export, download, or back up their TripIt trips — e.g. "export tripit data", "export my trips from tripit", "download tripit trips", or /tripit-export. Requires a browser-automation plugin and an interactive TripIt login. | ||
| targets: | ||
| - copilot | ||
| - claude | ||
| - agent-skills | ||
| --- | ||
|
|
||
| # TripIt Export | ||
|
|
||
| Export all TripIt travel data (trips, flights, lodging, activities) to JSON | ||
| format using TripIt's undocumented API v2. | ||
|
|
||
| ## Trigger Phrases | ||
|
|
||
| - "export tripit data" | ||
| - "export my trips from tripit" | ||
| - "download tripit trips" | ||
| - `/tripit-export` | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - **dev-browser plugin**: Browser automation with persistent state. | ||
| - **TripIt account**: The user must be able to log in to tripit.com. | ||
|
|
||
| ## API Reference | ||
|
|
||
| ### Discovered Endpoints | ||
|
|
||
| ```text | ||
| # List upcoming trips (paginated) | ||
| GET /api/v2/list/trip?exclude_types=weather&page_size=50&past=false&traveler=true&page_num=1 | ||
|
|
||
| # List past trips (paginated) | ||
| GET /api/v2/list/trip?exclude_types=weather&page_size=50&past=true&traveler=all&page_num=1 | ||
|
|
||
| # Get full trip details (includes flights, lodging, activities) | ||
| GET /api/v2/get/trip/uuid/{trip-uuid}/include_objects/true?exclude_types=weather | ||
|
|
||
| # User profile | ||
| GET /api/v2/get/profile | ||
| ``` | ||
|
|
||
| ### Required request headers | ||
|
|
||
| Send both headers on **every** API request: | ||
|
|
||
| ```text | ||
| Accept: application/json | ||
| X-Requested-With: XMLHttpRequest | ||
| ``` | ||
|
|
||
| > The `X-Requested-With: XMLHttpRequest` header is required. Without it, the v2 | ||
| > endpoints return `401 Unauthorized` even when a valid session cookie is | ||
| > present. | ||
|
|
||
| ### Response Structure | ||
|
|
||
| **Trip List Response:** | ||
|
|
||
| ```json | ||
| { | ||
| "timestamp": "...", | ||
| "page_num": "1", | ||
| "page_size": "50", | ||
| "max_page": "1", | ||
| "Trip": [] | ||
| } | ||
| ``` | ||
|
|
||
| **Trip Details Response:** | ||
|
|
||
| ```json | ||
| { | ||
| "Trip": {}, | ||
| "AirObject": [], | ||
| "LodgingObject": [], | ||
| "ActivityObject": [], | ||
| "CarObject": [], | ||
| "RestaurantObject": [], | ||
| "NoteObject": [], | ||
| "TransportObject": [], | ||
| "RailObject": [], | ||
| "CruiseObject": [] | ||
| } | ||
| ``` | ||
|
|
||
| ## Workflow | ||
|
|
||
| ### Step 1: Launch Browser and Authenticate | ||
|
|
||
| Navigate to TripIt and have the user log in: | ||
|
|
||
| ```typescript | ||
| import { connect, waitForPageLoad } from "@/client.js"; | ||
|
|
||
| const client = await connect("http://127.0.0.1:9222"); | ||
| const page = await client.page("tripit"); | ||
| await page.goto("https://www.tripit.com/app/trips"); | ||
| await waitForPageLoad(page); | ||
|
|
||
| console.log("Please log in to TripIt..."); | ||
| ``` | ||
|
|
||
| **Tell the user to log in manually, then confirm when done.** | ||
|
|
||
| ### Step 2: Fetch All Trips | ||
|
|
||
| After authentication, fetch upcoming and past trips with pagination: | ||
|
|
||
| ```typescript | ||
| async function fetchAllTrips(page, past: boolean): Promise<any[]> { | ||
| const allTrips: any[] = []; | ||
| let pageNum = 1; | ||
| let maxPage = 1; | ||
|
|
||
| do { | ||
| const params = new URLSearchParams({ | ||
| exclude_types: "weather", | ||
| page_size: "50", | ||
| past: String(past), | ||
| traveler: past ? "all" : "true", | ||
| page_num: String(pageNum) | ||
| }); | ||
|
|
||
| const response = await page.evaluate(async (url) => { | ||
| const res = await fetch(url, { | ||
| headers: { | ||
| "Accept": "application/json", | ||
| "X-Requested-With": "XMLHttpRequest" | ||
| } | ||
| }); | ||
| return res.json(); | ||
| }, `/api/v2/list/trip?${params}`); | ||
|
Comment on lines
+127
to
+135
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Check the HTTP status before accepting API responses. The code calls 🤖 Prompt for AI Agents |
||
|
|
||
| if (response.Trip) { | ||
| const trips = Array.isArray(response.Trip) ? response.Trip : [response.Trip]; | ||
| allTrips.push(...trips); | ||
| } | ||
|
|
||
| maxPage = parseInt(response.max_page || "1"); | ||
| pageNum++; | ||
| } while (pageNum <= maxPage); | ||
|
|
||
| return allTrips; | ||
| } | ||
|
|
||
| const upcomingTrips = await fetchAllTrips(page, false); | ||
| const pastTrips = await fetchAllTrips(page, true); | ||
| ``` | ||
|
|
||
| ### Step 3: Fetch Full Trip Details | ||
|
|
||
| For each trip, fetch complete details: | ||
|
|
||
| ```typescript | ||
| const tripDetails = {}; | ||
|
|
||
| for (const trip of [...upcomingTrips, ...pastTrips]) { | ||
| const details = await page.evaluate(async (uuid) => { | ||
| const res = await fetch( | ||
| `/api/v2/get/trip/uuid/${uuid}/include_objects/true?exclude_types=weather`, | ||
| { | ||
| headers: { | ||
| "Accept": "application/json", | ||
| "X-Requested-With": "XMLHttpRequest" | ||
| } | ||
| } | ||
| ); | ||
| return res.json(); | ||
| }, trip.uuid); | ||
|
|
||
| tripDetails[trip.uuid] = { | ||
|
Comment on lines
+158
to
+174
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- skill outline/context ---'
wc -l .agents/skills/tripit-exporter/SKILL.md
sed -n '1,230p' .agents/skills/tripit-exporter/SKILL.md
printf '%s\n' '--- repository language/config references ---'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
'tripit-exporter|SKILL\.md|noImplicitAny|strict|tsconfig|typescript|\.ts\b|\.js\b' \
.agents package.json package-lock.json pnpm-lock.yaml yarn.lock tsconfig.json jsconfig.json 2>/dev/null || trueRepository: DevSecNinja/.github Length of output: 6697 Declare If this 🤖 Prompt for AI Agents |
||
| trip: details.Trip, | ||
| flights: details.AirObject || [], | ||
| lodging: details.LodgingObject || [], | ||
| activities: details.ActivityObject || [], | ||
| cars: details.CarObject || [], | ||
| restaurants: details.RestaurantObject || [], | ||
| notes: details.NoteObject || [], | ||
| transport: details.TransportObject || [], | ||
| rail: details.RailObject || [], | ||
| cruise: details.CruiseObject || [] | ||
| }; | ||
|
|
||
| // Rate limiting | ||
| await page.waitForTimeout(500); | ||
| } | ||
| ``` | ||
|
|
||
| ### Step 4: Generate Final Export | ||
|
|
||
| ```typescript | ||
| const exportData = { | ||
| exportDate: new Date().toISOString(), | ||
| summary: { | ||
| upcomingTrips: upcomingTrips.length, | ||
| pastTrips: pastTrips.length, | ||
| totalTrips: upcomingTrips.length + pastTrips.length | ||
| }, | ||
| trips: Object.entries(tripDetails).map(([uuid, details]) => ({ | ||
| uuid, | ||
| name: details.trip?.display_name, | ||
| startDate: details.trip?.start_date, | ||
| endDate: details.trip?.end_date, | ||
| primaryLocation: details.trip?.primary_location, | ||
| ...details | ||
| })) | ||
| }; | ||
|
|
||
| const outputPath = `tripit-export-${new Date().toISOString().split("T")[0]}.json`; | ||
| writeFileSync(outputPath, JSON.stringify(exportData, null, 2)); | ||
|
Comment on lines
+212
to
+213
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Import The skill never imports 🤖 Prompt for AI Agents |
||
| ``` | ||
|
|
||
| ## Output Format | ||
|
|
||
| ```json | ||
| { | ||
| "exportDate": "2026-01-13T...", | ||
| "summary": { | ||
| "upcomingTrips": 2, | ||
| "pastTrips": 45, | ||
| "totalTrips": 47 | ||
| }, | ||
| "trips": [ | ||
| { | ||
| "uuid": "...", | ||
| "name": "Trip Name", | ||
| "startDate": "2026-01-15", | ||
| "endDate": "2026-01-20", | ||
| "primaryLocation": "Tokyo, Japan", | ||
| "trip": {}, | ||
| "flights": [], | ||
| "lodging": [], | ||
| "activities": [], | ||
| "cars": [], | ||
| "restaurants": [], | ||
| "notes": [], | ||
| "transport": [], | ||
| "rail": [], | ||
| "cruise": [] | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| ## Error Handling | ||
|
|
||
| - **401 Unauthorized**: Either the session expired, or the | ||
| `X-Requested-With: XMLHttpRequest` header is missing. Confirm the header is | ||
| set on every request; if it is, re-authenticate by navigating to the TripIt | ||
| login page. | ||
| - **Rate limiting**: Add delays between API calls if receiving `429` errors. | ||
| - **Empty responses**: Some trips may have no flights/lodging — this is normal. | ||
|
|
||
| ## Notes | ||
|
|
||
| - TripIt API v2 is undocumented and may change without notice. | ||
| - Authentication is session-based (cookies) — no API key required. | ||
| - Large trip histories may take several minutes to export. | ||
| - The `exclude_types=weather` param reduces response size. | ||
| - The `X-Requested-With: XMLHttpRequest` header is required on every request; | ||
| without it the v2 endpoints return `401` even with a valid session cookie. | ||
|
|
||
| ## Attribution | ||
|
|
||
| Adapted from Casey Grippi's `tripit-export` skill. Redistributed for personal | ||
| use. Skills contain hardcoded assumptions (locations, accounts, preferences) | ||
| that you may need to adapt for your own use. See | ||
| [`LICENSE`](LICENSE) and the repository | ||
| [`THIRD_PARTY_NOTICES.md`](../../../apm_modules/DevSecNinja/ai-toolkit/THIRD_PARTY_NOTICES.md). | ||
|
|
||
| - **Source:** <https://lobehub.com/skills/caseyg-caseys-claude-tripit-export> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: DevSecNinja/.github
Length of output: 1612
🌐 Web query:
https://lobehub.com/skills/caseyg-caseys-claude-tripit-export license redistribution permission💡 Result:
<search_synthesis>
The plugin identifier caseyg-caseys-claude-tripit-export refers to a specific skill hosted in the LobeHub Skills Marketplace [1]. Because this is a user-contributed plugin hosted on a third-party repository (caseyg/caseys-claude) [2][1], its license is determined by the author of that repository, not by the LobeHub platform&
#39;s license itself [3][4]. To determine the redistribution and usage permissions for this specific plugin, you must consult the repository where the source code is hosted: 1. Visit the repository: https://github.com/caseyg/caseys-claude [1] 2. Look for a LICENSE file in the root of the repository or the plugin's directory. 3. If no explicit license is provided, copyright law defaults to "all rights reserved," meaning you do not have permission to redistribute or modify the code without express authorization from the author, regardless of its availability on the LobeHub marketplace. Note that while LobeHub itself uses a custom license (the LobeHub Community License) for its core software [3][5], this does not automatically apply to third-party plugins submitted to their marketplace. The LobeHub platform acts as a directory; it does not grant you rights to the intellectual property of individual plugin developers [1]. Always check the repository's license file before redistributing or using the code commercially.</search_synthesis>
<source_evidence>
Citations:
🌐 Web query:
site:github.com/caseyg/caseys-claude LICENSE tripit-export redistribution permission Casey Grippi💡 Result:
<search_synthesis>
There is no publicly available information regarding specific redistribution permissions or an explicit open-source license for the tripit-export tool developed by Casey Gollan (identified in search results as caseyg) [1][2]. The tool is hosted within a repository titled caseys-claude, which is described as containing personal skills and configuration files intended for personal use, with the author noting that these skills often include hardcoded assumptions that require adaptation for other users [1]. Because no license is provided in the repository, you should assume the code is under standard copyright, meaning it cannot be redistributed or modified without explicit permission from the author [1][2].
</search_synthesis>
<source_evidence>
Citations:
Resolve the upstream license before merging. The upstream repository states “Personal use” but does not grant redistribution or modification rights. Confirm that Casey Grippi permits this repository to redistribute the adapted skill, or add the applicable license text. If permission is unavailable, do not publish the adapted material.
🤖 Prompt for AI Agents