macOS menu bar utility that opens a terminal in the active IDE's project directory. Supports JetBrains IDEs, VS Code, Cursor, Antigravity, and Xcode. Pure SwiftPM app — no Xcode project.
Sources/Shellporter: Swift 6.2 menu bar app (resolver pipeline, terminal launcher, hotkey system, config, UI). Keep changes small; reuse existing helpers.Tests/ShellporterTests: Swift Testing (@Test,#expect) — not XCTest. Mirror new logic with focused tests.Scripts: build/package helpers (compile_and_run.sh,package_app.sh,sign-and-notarize.sh,make_appcast.sh).docs: release notes and process docs.
- Dev loop:
./Scripts/compile_and_run.sh --dev- kills old dev instance, builds, packagesShellporter Dev.app, relaunches. - With tests:
./Scripts/compile_and_run.sh --dev --test - Quick build/test:
swift build/swift test - Universal release:
./Scripts/compile_and_run.sh --release-universal - Signing setup:
./Scripts/setup_dev_signing.sh
- Swift 6.2 strict concurrency.
@MainActorwhere needed; background work viawithCheckedContinuation+DispatchQueue.@unchecked Sendableonly where a dispatch queue provides thread safety. - Dependency injection via constructor for testability.
- All UI strings go through
AppStrings.swift, backed byLocalizable.stringsviaBundle.module.
- Use Swift Testing framework (
import Testing,@Test,#expect). Tests touching@MainActortypes:@Test @MainActor. - Always run
swift testbefore handoff. - After code changes affecting the app, rebuild and relaunch the dev app with
./Scripts/compile_and_run.sh --devto validate.
- Bump
version.env(MARKETING_VERSION + BUILD_NUMBER) - Update
CHANGELOG.md swift test./Scripts/sign-and-notarize.sh(requires.envwith Apple credentials, Sparkle key)- GitHub release + upload zip
./Scripts/make_appcast.sh <zip>to updateappcast.xml- Commit and push
appcast.xml
- Use SwiftPM and provided scripts; avoid adding dependencies without confirmation.
- After any code edit, rebuild with
./Scripts/compile_and_run.sh --devbefore validating behavior. - Prefer modern SwiftUI/Observation macros (
@Observable,@State,@Bindable); avoidObservableObject/@ObservedObject/@StateObject.