Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# `default-members` in the workspace manifest limits a bare `cargo build` to the
# crates that build on every OS. These aliases are the explicit "give me
# everything" verbs for Windows development.
[alias]
build-all = "build --workspace"
check-all = "check --workspace"
test-all = "test --workspace"
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,40 @@ jobs:
- name: Clippy
run: cargo clippy --workspace -- -D warnings

# The portable subset only. These jobs must never be given `--workspace`: the
# Windows-only crates are *expected* to fail off Windows, and a red X on an
# expected failure trains people to ignore CI.
# See docs/plans/2026-07-cross-platform-plan.md.
check-macos:
name: Check (macOS)
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable

- name: Check portable crates
run: cargo check -p ghost-platform -p ghost-ground

- name: Test platform contract
run: cargo test -p ghost-platform

check-linux:
name: Check (Linux)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable

- name: Check portable crates
run: cargo check -p ghost-platform -p ghost-ground

- name: Test platform contract
run: cargo test -p ghost-platform

release-build:
name: Release build
runs-on: windows-latest
Expand Down
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ members = [
"crates/ghost-ground",
"crates/ghost-platform",
]
# The crates that legitimately build on every OS. A bare `cargo build` / `cargo
# check` therefore produces a green, honest build on macOS and Linux, while the
# Windows-only crates fail with a single explanatory line if you ask for them
# explicitly. Windows developers get everything with `cargo build --workspace`
# (or the `cargo build-all` alias in .cargo/config.toml).
# See docs/plans/2026-07-cross-platform-plan.md.
default-members = [
"crates/ghost-platform",
"crates/ghost-ground",
]
resolver = "2"

[workspace.dependencies]
Expand Down
32 changes: 18 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Ship it three ways:

- **`ghost` CLI** — one-shot commands, great for scripts and CI (`ghost click --name "Submit"`)
- **`ghost-http` server** — local REST API, call it from Python, Node, curl, anything (`curl http://127.0.0.1:7878/list-windows`)
- **`ghost-mcp` server** — Model Context Protocol server for Claude, Cursor, and any MCP client (37 tools)
- **`ghost-mcp` server** — Model Context Protocol server for Claude, Cursor, and any MCP client (20 verbs; legacy tool names stay dispatchable)

No Claude required. No browser required. No CDP. It drives apps through the OS's
own automation and input APIs, so it works with native apps that have no API and
Expand All @@ -47,23 +47,25 @@ built to be automated.

### Platforms

Ghost targets three OSes through one shared contract (`crates/ghost-platform`):
Today Ghost runs on **Windows 10/11 only.** macOS and Linux are on the roadmap
and have a shared cross-platform contract in place (`crates/ghost-platform`),
but the native backends are not written yet — those binaries will not build or
run until they are. See [`docs/cross-platform.md`](docs/cross-platform.md) for
the capability matrix and
[`docs/plans/2026-07-cross-platform-plan.md`](docs/plans/2026-07-cross-platform-plan.md)
for the plan.

- **Windows** — full and verified. The flagship; every feature above works here.
- **macOS / Linux** — architecture in place, native backends in progress (not yet
functional). The cross-platform crate compiles for all three; the macOS
(Accessibility/CGEvent) and Linux (AT-SPI/XTest) engines are scaffolded with a
precise implementation map and must be built and verified on those machines.

See [`docs/cross-platform.md`](docs/cross-platform.md) for the capability matrix
and the plan. Note: Ghost's background-without-focus-steal wedge relies on Windows
window messages, which have no exact macOS/Linux equivalent — that capability is
"measure before claiming" off Windows.
Ghost's background-without-focus-steal wedge relies on Windows posted window
messages; that specific capability has no exact macOS/Linux equivalent and will
be re-measured once the native backends land.

Ghost is a general-purpose automation tool. Use it on systems you own or are
authorized to automate, and in line with the terms of the software you drive.

## Install
## Install (Windows 10/11)

Ghost binaries only build and run on Windows today. macOS/Linux support is
tracked in [`docs/cross-platform.md`](docs/cross-platform.md).

**Option A — Ready-to-run kit ($20, one-time).** Prebuilt Windows binaries (`ghost.exe`,
`ghost-http.exe`, `ghost-mcp.exe`) plus a quick-start, MCP config, and examples — no Rust toolchain, runs in
Expand Down Expand Up @@ -326,10 +328,12 @@ Run with `ghost run flow.json`, `POST /run`, or `ghost_execute_intent` over MCP.
ghost-cli ghost-http ghost-mcp Rust SDK
\ | / |
\ | / |
+-----> ghost-session <----------------+ ← safe Rust API
+-----> ghost-session <----------------+ ← safe Rust API (today: Windows-only)
|
ghost-core ← Win32 FFI: UIA, SendInput, DXGI
|
ghost-platform ← cross-OS contract; Win backend today, mac/linux scaffolded
|
Windows OS
```

Expand Down
3 changes: 3 additions & 0 deletions crates/ghost-cache/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ serde.workspace = true
serde_json.workspace = true
blake3.workspace = true
crossbeam-channel.workspace = true

# Windows-only today; see docs/plans/2026-07-cross-platform-plan.md.
[target.'cfg(windows)'.dependencies]
windows.workspace = true

[features]
Expand Down
10 changes: 10 additions & 0 deletions crates/ghost-cache/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Build guard: Ghost's engine is Windows-only today. Failing here gives a single
// readable sentence on macOS/Linux instead of hundreds of Win32 FFI errors.
// See docs/cross-platform.md and docs/plans/2026-07-cross-platform-plan.md.
fn main() {
println!("cargo:rerun-if-changed=build.rs");
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") {
eprintln!("Ghost's ghost-cache is Windows-only today; see docs/cross-platform.md");
std::process::exit(1);
}
}
5 changes: 5 additions & 0 deletions crates/ghost-cache/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
//! ghost-cache: event-driven UIA mirror + in-memory locator cache.
// Ghost's engine is Windows-only today. Off Windows this crate compiles to
// nothing and its build script fails with a one-line explanation; see
// docs/cross-platform.md and docs/plans/2026-07-cross-platform-plan.md.
#![cfg(windows)]

pub mod uia_mirror;
pub mod locator_cache;
pub mod error;
Expand Down
3 changes: 3 additions & 0 deletions crates/ghost-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@ serde_json = { workspace = true }
clap = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }

# Windows-only today; see docs/plans/2026-07-cross-platform-plan.md.
[target.'cfg(windows)'.dependencies]
windows = { workspace = true }
10 changes: 10 additions & 0 deletions crates/ghost-cli/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Build guard: Ghost's engine is Windows-only today. Failing here gives a single
// readable sentence on macOS/Linux instead of hundreds of Win32 FFI errors.
// See docs/cross-platform.md and docs/plans/2026-07-cross-platform-plan.md.
fn main() {
println!("cargo:rerun-if-changed=build.rs");
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") {
eprintln!("Ghost's ghost-cli is Windows-only today; see docs/cross-platform.md");
std::process::exit(1);
}
}
7 changes: 6 additions & 1 deletion crates/ghost-cli/src/doctor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,12 @@ fn capture_probe() -> Result<usize, String> {

#[cfg(not(windows))]
pub fn run_checks() -> Vec<Check> {
vec![Check::new("platform", Status::Fail, "Ghost's engine is Windows-only")]
vec![Check::new(
"platform",
Status::Fail,
"Ghost is Windows-only at v0.16.x; macOS/Linux backends land in v0.17+. \
See docs/cross-platform.md.",
)]
}

#[cfg(test)]
Expand Down
5 changes: 5 additions & 0 deletions crates/ghost-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
//! ghost query --fields "title,status"
//! ghost serve

// Ghost's engine is Windows-only today. Off Windows this crate compiles to
// nothing and its build script fails with a one-line explanation; see
// docs/cross-platform.md and docs/plans/2026-07-cross-platform-plan.md.
#![cfg(windows)]

mod doctor;

use clap::{Parser, Subcommand};
Expand Down
9 changes: 7 additions & 2 deletions crates/ghost-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ repository = "https://github.com/NORTHTEKDevs/ghost"
readme = "../../README.md"

[dependencies]
windows = { workspace = true }
windows-core = "0.58"
thiserror = { workspace = true }
tracing = { workspace = true }
tokio = { workspace = true }
Expand All @@ -21,6 +19,13 @@ serde = { workspace = true }
serde_json = { workspace = true }
blake3 = { workspace = true }

# Ghost's engine is Windows-only today. Keeping the Win32 bindings under a target
# section lets the workspace be resolved and checked from macOS/Linux without
# pulling Win32 FFI; see docs/plans/2026-07-cross-platform-plan.md.
[target.'cfg(windows)'.dependencies]
windows = { workspace = true }
windows-core = "0.58"

[dev-dependencies]
criterion = { workspace = true }

Expand Down
10 changes: 10 additions & 0 deletions crates/ghost-core/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Build guard: Ghost's engine is Windows-only today. Failing here gives a single
// readable sentence on macOS/Linux instead of hundreds of Win32 FFI errors.
// See docs/cross-platform.md and docs/plans/2026-07-cross-platform-plan.md.
fn main() {
println!("cargo:rerun-if-changed=build.rs");
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") {
eprintln!("Ghost's ghost-core is Windows-only today; see docs/cross-platform.md");
std::process::exit(1);
}
}
5 changes: 5 additions & 0 deletions crates/ghost-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// Ghost's engine is Windows-only today. Off Windows this crate compiles to
// nothing and its build script fails with a one-line explanation; see
// docs/cross-platform.md and docs/plans/2026-07-cross-platform-plan.md.
#![cfg(windows)]

pub mod error;
pub mod input;
pub mod uia;
Expand Down
10 changes: 10 additions & 0 deletions crates/ghost-http/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Build guard: Ghost's engine is Windows-only today. Failing here gives a single
// readable sentence on macOS/Linux instead of hundreds of Win32 FFI errors.
// See docs/cross-platform.md and docs/plans/2026-07-cross-platform-plan.md.
fn main() {
println!("cargo:rerun-if-changed=build.rs");
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") {
eprintln!("Ghost's ghost-http is Windows-only today; see docs/cross-platform.md");
std::process::exit(1);
}
}
5 changes: 5 additions & 0 deletions crates/ghost-http/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
//! GhostSession holds !Send COM handles, so it runs on a dedicated OS thread
//! and we dispatch requests to it via a channel actor.

// Ghost's engine is Windows-only today. Off Windows this crate compiles to
// nothing and its build script fails with a one-line explanation; see
// docs/cross-platform.md and docs/plans/2026-07-cross-platform-plan.md.
#![cfg(windows)]

use axum::{extract::State, http::StatusCode, response::Json, routing::{get, post}, Router};
use clap::Parser;
use ghost_session::{By, GhostSession, Region};
Expand Down
10 changes: 10 additions & 0 deletions crates/ghost-intent/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Build guard: Ghost's engine is Windows-only today. Failing here gives a single
// readable sentence on macOS/Linux instead of hundreds of Win32 FFI errors.
// See docs/cross-platform.md and docs/plans/2026-07-cross-platform-plan.md.
fn main() {
println!("cargo:rerun-if-changed=build.rs");
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") {
eprintln!("Ghost's ghost-intent is Windows-only today; see docs/cross-platform.md");
std::process::exit(1);
}
}
5 changes: 5 additions & 0 deletions crates/ghost-intent/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
//! ghost-intent: JSON intent compiler + JSONLogic + FSM executor.
// Ghost's engine is Windows-only today. Off Windows this crate compiles to
// nothing and its build script fails with a one-line explanation; see
// docs/cross-platform.md and docs/plans/2026-07-cross-platform-plan.md.
#![cfg(windows)]

pub mod compiler;
pub mod executor;
pub mod jsonlogic;
Expand Down
3 changes: 3 additions & 0 deletions crates/ghost-mcp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ sonic-rs = { workspace = true }
reqwest = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }

# Windows-only today; see docs/plans/2026-07-cross-platform-plan.md.
[target.'cfg(windows)'.dependencies]
windows = { workspace = true }
10 changes: 10 additions & 0 deletions crates/ghost-mcp/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Build guard: Ghost's engine is Windows-only today. Failing here gives a single
// readable sentence on macOS/Linux instead of hundreds of Win32 FFI errors.
// See docs/cross-platform.md and docs/plans/2026-07-cross-platform-plan.md.
fn main() {
println!("cargo:rerun-if-changed=build.rs");
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") {
eprintln!("Ghost's ghost-mcp is Windows-only today; see docs/cross-platform.md");
std::process::exit(1);
}
}
5 changes: 5 additions & 0 deletions crates/ghost-mcp/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
#![recursion_limit = "512"]

// Ghost's engine is Windows-only today. Off Windows this crate compiles to
// nothing and its build script fails with a one-line explanation; see
// docs/cross-platform.md and docs/plans/2026-07-cross-platform-plan.md.
#![cfg(windows)]

use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use ghost_session::{GhostSession, Target, LocateMode};
Expand Down
48 changes: 48 additions & 0 deletions crates/ghost-platform/tests/host_capability_tripwire.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//! Tripwire: the host's backend must report exactly the functionality Ghost has
//! actually shipped for that OS — `true` on Windows, `false` on macOS/Linux.
//!
//! This test exists to fail loudly if someone flips a scaffold to
//! `functional: true` before its native backend is built and verified on-device
//! (see the checklist in `docs/plans/2026-07-cross-platform-plan.md` §7). Turning
//! a platform on is a deliberate act that must edit this file in the same commit.

use ghost_platform::{capabilities_for, current, Feature, Platform};

#[test]
fn host_backend_functionality_matches_shipped_truth() {
let backend = current();
let caps = backend.capabilities();
assert_eq!(caps.functional, backend.is_functional());

if cfg!(windows) {
assert!(
backend.is_functional(),
"the Windows backend is the shipped engine and must report functional"
);
} else {
assert!(
!backend.is_functional(),
"{:?} is a scaffold: no native backend has been built and verified \
on-device, so it must not report functional",
backend.platform()
);
assert!(
caps.supported.is_empty(),
"a scaffold must not advertise any Feature"
);
}
}

#[test]
fn scaffold_platforms_never_claim_capabilities() {
for platform in [Platform::MacOS, Platform::Linux] {
let caps = capabilities_for(platform);
assert!(!caps.functional, "{platform:?} must not claim functional yet");
assert!(caps.supported.is_empty(), "{platform:?} must advertise no Feature");
assert!(
!caps.supports(Feature::BackgroundDispatch),
"background dispatch is a Windows posted-message primitive; it stays \
unclaimed off Windows until it is measured on-device"
);
}
}
5 changes: 4 additions & 1 deletion crates/ghost-session/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ tokio = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
async-trait = "0.1"
windows = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
image = { workspace = true }
reqwest = { workspace = true }

# Windows-only today; see docs/plans/2026-07-cross-platform-plan.md.
[target.'cfg(windows)'.dependencies]
windows = { workspace = true }

[dev-dependencies]
ghost-intent = { path = "../ghost-intent" }
async-trait = "0.1"
Expand Down
10 changes: 10 additions & 0 deletions crates/ghost-session/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Build guard: Ghost's engine is Windows-only today. Failing here gives a single
// readable sentence on macOS/Linux instead of hundreds of Win32 FFI errors.
// See docs/cross-platform.md and docs/plans/2026-07-cross-platform-plan.md.
fn main() {
println!("cargo:rerun-if-changed=build.rs");
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") {
eprintln!("Ghost's ghost-session is Windows-only today; see docs/cross-platform.md");
std::process::exit(1);
}
}
Loading
Loading