一键导入
add-feature-skill
Guide for adding new features to TabPilot. Covers architecture, extension points, patterns, and build process.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Guide for adding new features to TabPilot. Covers architecture, extension points, patterns, and build process.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | add-feature-skill |
| description | Guide for adding new features to TabPilot. Covers architecture, extension points, patterns, and build process. |
TabPilot is a macOS menu bar app that serves as a Safari tab command center -- it polls Safari tabs via AppleScript, tracks tab age with JSON persistence, groups tabs by domain, and supports AI-powered semantic clustering via the Codex WebSocket protocol.
TabPilot is a single-file SwiftUI menu bar app (TabPilot.swift, ~1645 lines). It uses the @main struct TabPilotApp with a MenuBarExtra(.window) scene. Two @Observable classes own all mutable state:
SafariScanner -- polls Safari every 5 seconds via AppleScript, maintains the live tab list, computes domain groups and duplicate detection, and persists tab history to ~/.tabpilot/tab_history.json.TabClusterer -- connects to the Codex app-server over WebSocket (JSON-RPC 2.0), sends tab data for AI-powered semantic clustering, and supports natural-language tab queries.Both are held as @State properties on the App struct and passed into ContentView.
| Type | Kind | Description |
|---|---|---|
TPTheme | enum | Static color constants for the dark UI theme |
RawWebSocket | class | NWConnection-based WebSocket client (shared pattern across apps) |
SafariTab | struct | Identifiable tab with url, title, windowIndex, tabIndex, domain helpers |
AgeCategory | enum | Four tiers: .fresh, .warm, .cooling, .stale with color and label |
TabCluster | struct | AI-generated cluster with name, description, and tab list |
FilterMode | enum | CaseIterable filter options (all, duplicates, fresh, warm, cooling, stale) |
ViewMode | enum | CaseIterable view modes (domains, clusters) |
TabHistory / TabHistoryStore | struct/enum | Codable persistence of first-seen timestamps per URL |
SafariScanner | @Observable class | Safari polling, tab list, domain grouping, history tracking |
ClusterState | enum | FSM for clustering: idle, connecting, clustering, done, error |
TabClusterer | @Observable class | Codex AI integration for semantic clustering and NL queries |
TabCountBadge | View | Displays tab count in a pill badge |
FilterChips | View | Horizontal row of filter mode toggle buttons |
AgeBadge | View | Colored dot + label for tab age category |
DomainIcon | View | Favicon-style first-letter icon for a domain |
TabRowView | View | Single tab row with title, domain, age, and action buttons |
DomainGroupView | View | Collapsible section grouping tabs by domain |
ClusterGroupView | View | Collapsible section grouping tabs by AI cluster |
ContentView | View | Root view with filter chips, view mode toggle, tab list, and cluster UI |
Define any new model types at the top of TabPilot.swift in the // MARK: - Models section. Follow the existing pattern: Identifiable structs, CaseIterable enums.
If the feature needs new state, add properties to the appropriate @Observable class:
SafariScannerTabClusterer@Observable class and adding it as @State in TabPilotApp.If adding a new filter, add a case to FilterMode and update the filtering logic in ContentView (the filteredTabs computed property).
If adding a new view mode, add a case to ViewMode and add a corresponding branch in ContentView's body that renders the new grouping.
If adding a new view, create a new struct MyView: View in the // MARK: - Views section. Use TPTheme colors for all styling.
If adding a new Codex AI interaction, follow the TabClusterer pattern:
RawWebSocket to 127.0.0.1:4663initialize JSON-RPC request, then thread/start, then turn/start with your promptitem/agentMessage/delta notificationsBuild and test with bash build.sh then open TabPilot.app.
FilterMode enum, implement filtering logic in ContentViewViewMode enum, implement grouping/rendering in ContentViewSafariScanner.scanAllTabs() AppleScript to extract additional tab properties (e.g., favicons, reading progress)TabClusterer pattern: create a new @Observable class with its own ClusterState-style FSM, connect to Codex, send prompts, parse streaming responsesTabRowView that call AppleScript commands (close tab, move tab, open URL)TabHistory/TabHistoryStore to track additional metrics (visit count, time spent)TPTheme static properties. Use TPTheme.bg for backgrounds, TPTheme.surface for cards, TPTheme.accent for interactive elements.RawWebSocket class handles raw TCP + WebSocket framing via NWConnection. JSON-RPC 2.0 messages use sequential integer IDs tracked in pendingRequests: [Int: String]. Always send initialize before thread/start before turn/start.safari, arrow.clockwise, doc.on.doc, sparkles, xmark.circle). Keep the icon style consistent.NSAppleScript. Scripts query every window and every tab for URL and title. Keep scripts minimal for performance.ClusterState) to drive UI transitions. The view switches on state to show appropriate content.~/.tabpilot/tab_history.json via TabHistoryStore.save()/load().bash build.sh # Compiles TabPilot.swift and creates TabPilot.app bundle
open TabPilot.app # Run the app (appears in menu bar)
Requires macOS 14.0+ and Xcode command-line tools. The app runs as LSUIElement (no Dock icon). Safari automation requires user approval for AppleScript access.