Imported from ywwg/packingdb (
AGENTS.md). Install upstream withnpx skills add ywwg/packingdb. Copyright stays with the author.
Agent Instructions
This document provides comprehensive guidance for AI agents working on the packingdb project. For user-facing documentation, see README.md and WEB_IMPLEMENTATION.md.
⚠️ IMPORTANT: When making changes to the project, always update this document to reflect:
- New features or functionality added
- Architecture or design pattern changes
- New dependencies or tools introduced
- Updated workflows or best practices
- Common issues discovered and their solutions
Project Overview
PackingDB is a trip packing list management system with both a TUI (Terminal UI) and web interface. It helps users:
- Create and manage trips with specific contexts (nights, temperature, properties)
- Configure trip properties (Camping, Swimming, Hiking, etc.)
- Track what items are packed for each trip
- Generate packing lists based on trip context
Architecture
Core Library (pkg/packinglib/):
registry.go- Property and item registrytrip.go- Trip data structures and operationscontext.go- Trip context (nights, temp, properties)item.go- Item definitions and packing state
Weather (pkg/weather/):
weather.go- Location geocoding and temperature lookup using the free Open-Meteo API- Geocodes location names to coordinates
- Rejects past dates (start date must be today or in the future)
- Uses forecast API for dates within 16 days (inclusive)
- Uses previous year's data for dates too far in the future ("typical" temperatures)
- Returns min low and max high across the date range in Fahrenheit
- Server and weather package compare dates using the server's local calendar day, avoiding UTC rollover issues for local "today"
weather_test.go- Unit tests with mock httptest server covering Lookup (forecast, typical, past-date rejection, geocode failure, min-nights clamp, API errors), GeocodeSuggestions (basic, comma filter, US/Canadian abbreviations, empty query, max results, no admin1, server error), and matchesAdmin1Filter (prefix match, abbreviation expansion)
Web Backend (cmd/packingweb/):
main.go- HTTP server entry point with command-line flags- Uses
pkg/server/for REST API handlers and background persistence - Uses chi router for clean route definitions
Web Frontend (static/):
index.html- Alpine.js-based single-page application with Tailwind CSS (285 lines)app.js- Alpine.js reactive state management (307 lines)styles.css- Minimal custom CSS (3 lines, only x-cloak directive)
TUI (cmd/packingdb/):
- Terminal-based interactive interface using promptui
Key Design Decisions
- File-based storage - Trips stored as YAML/CSV files in
public/trips/ - No authentication - Single-user system, no login required
- Mobile-first - Web UI designed primarily for phones
- Alpine.js framework - Lightweight reactive framework, no build step
- Tailwind CSS - Utility-first CSS framework via CDN, minimal custom CSS
- Stateless server - Each API request is independent, trips cached in memory
- Chi router - Clean, standard Go routing library
- CDN-based frontend - No build tools, npm, or node dependencies required
- Name-based trip lookup - Trip names (stored inside files) are the API identifiers, not filenames. A name→filename mapping is built on startup by scanning all trip files.
Trip Creation Flow
The "New Trip" form asks for a location and date range (start/end dates). The frontend calls GET /api/weather with these values to geocode the location and fetch temperature data from Open-Meteo. Past dates are rejected (start date must be today or later). The weather source varies:
- Forecast: dates within 16 days from now (inclusive)
- Typical: dates further in the future (uses same dates from previous year)
Nights are calculated automatically from the date range. The user can adjust the temperatures and nights before creating the trip.
Server Management
After Manual Testing
Always kill the test server after completing manual testing to avoid:
- Port conflicts on subsequent runs
- Resource leaks
- Confusion about which server is running
Command:
pkill packingweb
Starting the Server for Testing
From repo root:
go run ./cmd/packingweb
With custom flags:
go run ./cmd/packingweb -port 9000 -trips ./custom/public/trips -static ./custom/static
Verifying Server is Running
pgrep -la packingweb
Or check the port:
lsof -ti:8080
Testing Guidelines
E2E Tests
- E2E tests automatically start/stop their own server with random ports
- No manual cleanup needed after running
go test - Tests use temporary directories that are automatically cleaned up
- Located in
cmd/packingweb/e2e_test.go
Run E2E tests:
cd cmd/packingweb
go test -v -run TestE2E
Manual Testing
- Start server with
go run ./cmd/packingweb - Test functionality in browser or with curl
- ALWAYS kill the server when done with
pkill packingweb
API Testing with curl
# List trips
curl -s http://localhost:8080/api/trips | python3 -m json.tool
# Get trip details
curl -s http://localhost:8080/api/trips/test-trip | python3 -m json.tool
# Get properties
curl -s http://localhost:8080/api/properties | python3 -m json.tool
# Toggle item
curl -s -X POST http://localhost:8080/api/trips/test-trip/items/z/toggle
Development Workflow
- Make changes to code
- Start server for testing:
go run ./cmd/packingweb - Test changes (browser or curl)
- Kill server:
pkill packingweb - Run automated tests if needed:
go test ./... - Commit changes
REST API Reference
See WEB_IMPLEMENTATION.md for complete API documentation.
Key endpoints:
GET /api/weather?location=X&startDate=Y&endDate=Z- Look up weather for location/datesGET /api/trips- List all tripsPOST /api/trips- Create new tripGET /api/trips/{name}- Get trip detailsPUT /api/trips/{name}/update- Update trip settings (name is stored in file, filename stays the same)GET /api/properties- List all available propertiesGET /api/trips/{name}/properties- Get trip propertiesPOST /api/trips/{name}/properties/{property}/toggle- Toggle propertyGET /api/trips/{name}/items- Get packing itemsPOST /api/trips/{name}/items/{code}/toggle- Toggle item packed status
Important behaviors:
- Toggling a property may automatically enable other properties (e.g., enabling "Camping" might auto-enable "Outdoors")
- After toggling properties, always fetch the full property list to see cascading changes
- Items are organized by category in the response
- Toggling an item's packed status marks the trip as dirty; dirty trips are persisted to their trip files asynchronously by a background task that runs every
persistInterval(default 30 seconds) and on graceful shutdown - Trip name can be changed independently of the filename (stored inside the file)
- The
{name}URL parameter is the trip name (from inside the file), not the filename - On startup, the server scans all trip files to build a name→filename mapping (
ScanTrips()) - Creating a new trip adds to the mapping; renaming a trip re-keys the mapping
Frontend Development
Alpine.js Patterns
The frontend uses Alpine.js for reactive state management. Key patterns:
State management:
function packingApp() {
return {
currentPage: 'main-menu',
trips: [],
currentTrip: null,
collapsedCategories: new Set(), // Track collapsed state
// ... other state
init() {
this.loadTrips();
}
}
}
Template directives:
x-data="packingApp()"- Initialize Alpine componentx-show="condition"- Conditionally show elementsx-for="item in items"- Loop over arraysx-model="property"- Two-way data binding@click="method()"- Event handlersx-text="expression"- Dynamic text content:class="{ 'active': isActive }"- Dynamic classesx-collapse- Smooth collapse/expand animations
Computed properties: Use getters for derived state:
get filteredProperties() {
return this.properties.filter(p => p.name.includes(this.search));
}
Collapsible sections:
toggleCategoryCollapse(categoryName) {
if (this.collapsedCategories.has(categoryName)) {
this.collapsedCategories.delete(categoryName);
} else {
this.collapsedCategories.add(categoryName);
}
}
isCategoryCollapsed(categoryName) {
return this.collapsedCategories.has(categoryName);
}
Auto-refresh for multi-device usage:
startAutoRefresh() {
this.stopAutoRefresh();
// Refresh every 10 seconds
this.refreshInterval = setInterval(() => {
if (this.currentPage === 'packing') {
this.refreshItems();
}
}, 10000);
}
stopAutoRefresh() {
if (this.refreshInterval) {
clearInterval(this.refreshInterval);
this.refreshInterval = null;
}
}
The packing page automatically refreshes every 10 seconds to pick up changes made in other browsers or devices. The refresh is silent (no loading spinner) and preserves UI state like hidePacked and collapsed categories.
Styling Guidelines
- Mobile-first approach (design for phones, scale up)
- Minimum tap target: 44px (Tailwind:
h-11or larger) - Use gradient theme:
#667eeato#764ba2 - Touch-friendly animations (scale on tap)
- Support safe areas for notched devices
- Tailwind utility classes for all styling
- Minimal custom CSS (only framework-required styles like x-cloak)
- Responsive breakpoints: sm (640px), md (768px), lg (1024px)
State Synchronization
After API mutations, always refresh state to catch server-side changes:
- After toggling property: fetch all properties
- After toggling item: optimistic update is OK (no cascading changes)
- After updating trip: reload trip details
- After creating trip: reload trip list
Multi-device support:
- Packing page auto-refreshes every 10 seconds to detect changes from other browsers/devices
- Refresh is silent (no loading indicator) and preserves UI state (hidePacked, collapsed categories)
- Auto-refresh stops when navigating away from packing page
Common Issues
Port Already in Use
If you get "bind: address already in use":
lsof -ti:8080 | xargs kill -9
Finding Running Servers
pgrep -la packingweb
# or
ps aux | grep packingweb
Static Files Not Loading
- Check that
-staticflag points to correct directory - Default is
./staticfrom repo root - From
cmd/packingwebdirectory, server auto-detects wrong path
Properties Not Updating After Toggle
- Always reload full property list after toggle
- Properties can have cascading dependencies
- Don't rely on optimistic updates for properties
Code Organization
packingdb/
├── cmd/
│ ├── packingdb/ # TUI application
│ └── packingweb/ # Web server
│ ├── main.go # Entry point, CLI flags
│ └── e2e_test.go # End-to-end tests
├── pkg/
│ ├── server/
│ │ ├── routes.go # REST API handlers, chi router
│ │ └── persist.go # Background persistence
│ ├── weather/
│ │ └── weather.go # Open-Meteo geocoding + weather lookup
│ ├── contexts/
│ │ └── registry.go # Property definitions
│ └── packinglib/
│ ├── registry.go # Core registry interface
│ ├── trip.go # Trip data structures
│ ├── context.go # Trip context
│ └── item.go # Item definitions
├── static/
│ ├── index.html # Alpine.js SPA
│ ├── app.js # Alpine.js app logic
│ └── styles.css # Mobile-first CSS
└── public/
└── trips/ # Trip YAML/CSV files
Dependencies
Backend
- Go 1.22+ (uses Go modules)
github.com/go-chi/chi/v5- HTTP router- Standard library for everything else
Frontend
- Alpine.js 3.x (CDN) - Reactive framework
- Tailwind CSS 3.x (CDN) - Utility-first CSS framework
- canvas-confetti 1.9.2 (CDN) - Celebration animations
- No build tools required
- No npm/node dependencies
File Formats
Trip Files (YAML)
Trips are stored as YAML with this structure:
name: weekend-camping
nights: 2
temperatureMin: 50
temperatureMax: 75
properties:
- Camping
- Swimming
items:
- code: z
packed: true
- code: aa
packed: false
CSV Support
Legacy CSV format also supported for backwards compatibility.
Performance Considerations
- Trips are cached in memory after first load
- Background persistence: Changes are saved to disk every 30 seconds instead of on every API call
- Dirty tracking: Only modified trips are persisted
- Graceful shutdown ensures all pending changes are saved before exit
- No database - file I/O is batched and asynchronous
- Suitable for single-user, low-volume usage
- For high-volume, consider adding proper caching layer
Future Development Notes
Potential improvements documented in WEB_IMPLEMENTATION.md:
- User authentication/multi-user support
- Offline mode with service workers
- Push notifications for packing reminders
- Import/export functionality
- Trip sharing
- Weather API integration
Related Documentation
- README.md - User-facing quick start guide
- WEB_IMPLEMENTATION.md - Detailed web implementation docs
- static/README.md - Web interface user guide