Skip to content

CogitoAgency/GhostUI

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GhostUI — Ghost UI toolkit for LLM-assisted iOS development

CI Swift 6.0 iOS 17+ macOS 14+ SPM Compatible MIT License

GhostUI adds an invisible observation layer — ghost UI — to your SwiftUI app. AI assistants like Claude can see your view hierarchy, track state changes, dispatch actions, and run test plans, all without touching the visible interface. The user sees nothing. The LLM sees everything.

Installation

Add to your Package.swift:

dependencies: [
    .package(url: "https://github.com/CogitoAgency/GhostUI.git", from: "1.0.0")
],
targets: [
    .target(name: "YourApp", dependencies: ["GhostUI"])
]

Or in Xcode: File > Add Package Dependencies and paste the repo URL.

Requirements: Swift 6.0+ / iOS 17+ / macOS 14+ / watchOS 10+ / tvOS 17+


Ghost UI

The ghost layer instruments your views without any visible side effects.

.ghostID() — Register views for LLM visibility

import GhostUI

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello").ghostID("greeting")
            Button("Save") { save() }
                .ghostID("save-button")
        }
        .trackLifecycle("ContentView")
    }
}

An LLM can now check Aware.dispatcher.isViewVisible("greeting") and dispatch .tap(id: "save-button").

@TrackedState — Observable state property wrapper

struct SettingsView: View {
    @TrackedState("selectedTab") var selectedTab: Tab = .general

    var body: some View {
        TabView(selection: $selectedTab) {
            GeneralTab().tag(Tab.general)
            AdvancedTab().tag(Tab.advanced)
        }
    }
}

Every change is logged and queryable: Aware.dispatcher.getState("selectedTab").

.ghostTrackState() — Track any computed value

Text("Score: \(score)")
    .ghostTrackState("score", value: "\(score)")

TrackedButton — Tap logging

TrackedButton("Save", in: "SettingsView") {
    saveSettings()
} label: {
    Label("Save", systemImage: "checkmark")
}

.trackLifecycle() — View appear/disappear monitoring

MyView()
    .trackLifecycle("MyView", properties: ["screen": "settings"])

Logs appear/disappear events with duration in milliseconds.

.trackTap() — Add tap tracking to any view

Image(systemName: "gear")
    .trackTap("open-settings") { showSettings() }

Action Dispatch

UIActionDispatcher lets LLMs programmatically interact with the ghost layer.

let dispatcher = Aware.dispatcher

// Dispatch UI actions
await dispatcher.dispatch(.tap(id: "save-button"))
await dispatcher.dispatch(.type(id: "search-field", text: "query"))
await dispatcher.dispatch(.swipe(id: "card", direction: .left))
await dispatcher.dispatch(.selectTab(name: "Settings"))
await dispatcher.dispatch(.wait(seconds: 1.0))

// Assertions
await dispatcher.dispatch(.expectVisible(id: "results-list"))
await dispatcher.dispatch(.expectHidden(id: "loading-spinner"))
await dispatcher.dispatch(.expectState(key: "selectedTab", value: "settings"))

// Wait for views
await dispatcher.waitForView("results-list", timeout: 5.0)
await dispatcher.waitForState("status", equals: "loaded", timeout: 3.0)

// Query state
dispatcher.isViewVisible("greeting")       // Bool
dispatcher.getState("selectedTab")          // String?
dispatcher.visibleViews                     // Set<String>
dispatcher.state                            // [String: String]

Plan vs Actual Reports

dispatcher.clearLog()

// ... run a sequence of actions ...

print(dispatcher.getPlanVsActualReport())

Output:

═══════════ PLAN VS ACTUAL ═══════════
▶ PLAN: Tap 'save-button'
  ACTUAL: ✅ 0.12s
  📱 View appeared: confirmation-dialog
▶ PLAN: Expect 'confirmation-dialog' visible
  ACTUAL: ✅ 0.00s
═══════════════════════════════════════

Register Custom Handlers

dispatcher.registerHandler(for: "save-button") { action in
    await performSave()
    return true
}

dispatcher.registerCustomAction("clearCache") {
    CacheManager.shared.clear()
}

Structured Logging

Actor-based logger with levels, categories, metadata, and emoji support.

Aware.logger.trace("Cache lookup", metadata: ["key": cacheKey])
Aware.logger.debug("Parsed response", metadata: ["items": "\(items.count)"])
Aware.logger.info("User logged in", metadata: ["userId": "123"])
Aware.logger.warn("Retrying request", metadata: ["attempt": "2"])
Aware.logger.error("Network failed", metadata: ["error": error.localizedDescription])

Levels: trace > debug > info > warn > error — configurable minimum via .ghostui.json.

Logs are forwarded to both os.log and the GhostUI log collector for aggregation and export.


Auto-Log Macros

Compile-time macros that inject entry, exit, and error logging into functions.

@AutoLog — Single function

@AutoLog(verbosity: .verbose, category: "Network")
func fetchUser(id: Int) async throws -> User {
    let response = try await api.get("/users/\(id)")
    return try decode(response)
}
// Logs: [ENTER] fetchUser(42)
// Logs: [EXIT] fetchUser -> User (0.234s)
// Or:   [ERROR] fetchUser: NetworkError.timeout (0.234s)

@AutoLogAll — All methods in a type

@AutoLogAll(category: "UserService")
class UserService {
    func fetchUser(id: Int) async throws -> User { ... }
    func updateUser(_ user: User) async throws { ... }

    @NoLog
    func internalHelper() { ... }  // excluded
}

Verbosity levels: .minimal (timing only), .standard (entry/exit), .verbose (includes parameter values).


Test Runner

Tiered test execution with presets and build-change detection.

Presets

Preset Tiers Parallel Stop on Failure Retries
.minimal smoke No Yes 0
.ci smoke, structure Yes Yes 2
.verbose smoke, structure, integration No No 3

Run on Launch

@main
struct MyApp: App {
    init() {
        #if DEBUG
        Task { await Aware.runOnLaunchIfNeeded() }
        #endif
    }

    var body: some Scene {
        WindowGroup { ContentView() }
    }
}

Only re-runs when the build number changes.

Custom Test Cases

class SmokeTests: AwareTestCase {
    var tier: TestTier { .smoke }

    func runTest() async throws {
        try assert(AppState.shared.isReady, "App not ready")
        try assertEqual(AppState.shared.screens.count, 5)
    }
}

// Register and run
let runner = await Aware.getTestRunner()
await runner.register(SmokeTests())
let result = await Aware.run(preset: .minimal)

Test Plans (Plan vs Actual)

let plan = TestPlan(name: "Login Flow") {
    TestStep("Show login screen") {
        await dispatcher.dispatch(.expectVisible(id: "login-view"))
    }
    TestStep("Enter credentials") {
        await dispatcher.dispatch(.type(id: "email", text: "user@example.com"))
        await dispatcher.dispatch(.type(id: "password", text: "secret"))
    }
    TestStep("Submit") {
        await dispatcher.dispatch(.tap(id: "login-button"))
        await dispatcher.dispatch(.waitForView(id: "home-view", timeout: 5))
    }
}

let result = await Aware.runPlan(plan)

MCP Server

Built-in Model Context Protocol server for direct AI assistant integration. Register tools that expose your app's ghost UI to LLMs over stdio, HTTP, or WebSocket.

let server = MCPServer(name: "MyApp", version: "1.0")

await server.registerTool(ViewQueryTool())
await server.registerTool(ActionDispatchTool())

try await server.start(transport: .stdio)

Custom MCP Tools

struct ViewQueryTool: MCPTool {
    let name = "queryViews"
    let description = "List all visible ghost UI views"
    let inputSchema: MCPToolSchema = .empty

    func execute(arguments: [String: MCPValue]?) async throws -> MCPToolCallResult {
        let views = await Aware.dispatcher.visibleViews
        return .text(views.sorted().joined(separator: "\n"))
    }
}

Design Tokens

AwareTheme provides centralized design constants for consistent iOS UI.

// Colors (SwiftUI)
AwareTheme.Colors.primary       // Color.blue
AwareTheme.Colors.success       // Color.green
AwareTheme.Colors.background    // system background (iOS/macOS adaptive)

// Spacing (4pt base)
AwareTheme.Spacing.xs   // 4pt
AwareTheme.Spacing.sm   // 8pt
AwareTheme.Spacing.md   // 16pt
AwareTheme.Spacing.lg   // 24pt

// Typography
AwareTheme.Typography.body      // 13pt
AwareTheme.Typography.title     // 17pt
AwareTheme.Typography.headline  // 24pt

// Corner radius, animation durations, shadows also available

Feature Flags

Typed feature flag registry with environment variable fallback.

let flags = FeatureFlagRegistry.shared

// Query
if await flags.isEnabled("cook_enabled") { ... }
let timeout = flags.getDouble("agent_timeout_seconds", default: 300.0)

// Register
await flags.register("my_feature", enabled: true)
await flags.register("max_items", value: 50)

// Toggle at runtime
await flags.toggle("debug_mode")

// Environment variable override: AWARE_FLAG_MY_FEATURE=true

Built-in flags: AwareFeatureFlag.debugMode, .cookEnabled, .swarmEnabled, .proUser, .cloudSync, and more.


Configuration

Create .ghostui.json in your project root or app bundle:

{
  "version": "1.0",
  "project": {
    "name": "MyApp",
    "platform": "swift",
    "buildNumber": "42"
  },
  "logging": {
    "level": "debug",
    "format": "pretty",
    "includeEmoji": true,
    "includeTimestamps": true
  },
  "testing": {
    "runOnLaunch": true,
    "runOnBuildChange": true,
    "tiers": ["smoke", "structure"],
    "parallel": false,
    "retries": 2,
    "timeout": {
      "smoke": 3,
      "structure": 30,
      "integration": 120
    }
  }
}

Or initialize programmatically:

Aware.initialize(configPath: "/path/to/.ghostui.json")

Architecture

Sources/
  GhostUI/
    SwiftUI/              Ghost UI — ghostID, ghostTrackState, TrackedState,
                          TrackedButton, UIActionDispatcher, ViewLifecycleTracker
    Logging/              Structured logging — Logger, LogCollector, AwareLogLevel
    Testing/              Test runner — AwareTestCase, TestPlan, TestRunner, presets
    Standards/            Design tokens — AwareTheme, AwareFeatureFlags, AwareStrings
    MCP/                  Model Context Protocol — MCPServer, MCPTool, transports
    Cook/                 Multi-agent orchestration — CookAgent, CookTask, blocking
    Config/               Configuration — AwareConfig, ConfigLoader, Preset
    Storage/              Persistence — MemoryStorage, SQLiteStorage
    Integration/          External bridges — BreatheIntegration
  GhostUIMacros/       Macro declarations — @AutoLog, @AutoLogAll, @NoLog
  GhostUIMacrosPlugin/ Compiler plugin — AutoLogMacro, CodeGenerator

License

MIT

About

Ghost UI toolkit for LLM-assisted Swift and iOS development

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages