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
25 changes: 25 additions & 0 deletions bin/omarchy-install-ai-ghost
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

# omarchy:summary=Install Ghost and start it for this login
# omarchy:requires-sudo=true

set -euo pipefail

echo "Installing Ghost..."
omarchy-pkg-add ghost

# The package carries the daemon, the HUD plugin, the CLI, the browser relay,
# and the computer-use helper. Two things need this login's hand: the daemon is
# a user unit, and the HUD is an omarchy-shell plugin, so it is linked into this
# user's plugins directory and enabled in the shell already running.
echo "Starting Ghost for this login..."
systemctl --user daemon-reload
systemctl --user enable --now ghostd.service

mkdir -p ~/.config/omarchy/plugins
ln -sfn /usr/share/ghost/plugin ~/.config/omarchy/plugins/ferdousbhai.ghost
omarchy-shell shell rescanPlugins
omarchy plugin enable ferdousbhai.ghost

echo ""
echo "Ghost has been installed. Press Super + Ctrl + G to summon it."
19 changes: 19 additions & 0 deletions bin/omarchy-remove-ai-ghost
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash

# omarchy:summary=Remove Ghost, keeping every ghost home and credential in place.
# omarchy:requires-sudo=true

set -euo pipefail

# The HUD plugin and the daemon hold the package's files open, so both go first.
# `plugin remove` disables it and unlinks the symlink; the rm covers a shell
# that is not running to answer.
omarchy plugin remove ferdousbhai.ghost --yes 2>/dev/null || true
rm -f "$HOME/.config/omarchy/plugins/ferdousbhai.ghost"
systemctl --user disable --now ghostd.service 2>/dev/null || true
systemctl --user daemon-reload

omarchy-pkg-drop ghost

echo ""
echo "Ghost has been removed. Your ghosts (~/ghosts), settings, and state are still there."
2 changes: 2 additions & 0 deletions default/omarchy/omarchy-menu.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@
"install.terminal.kitty": {"icon":"","label":"Kitty","disabled":"omarchy-pkg-present kitty","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-terminal kitty'"},
"install.ai.chatgpt": {"icon":"","iconFont":"omarchy","label":"ChatGPT Desktop","disabled":"omarchy-pkg-present openai-codex-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-chatgpt"},
"install.ai.claude": {"icon":"","iconFont":"omarchy","label":"Claude Desktop","disabled":"omarchy-pkg-present claude-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-claude"},
"install.ai.ghost": {"icon":"󰊠","label":"Ghost","disabled":"omarchy-pkg-present ghost","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-ghost"},
"install.ai.dictation": {"icon":"","label":"Dictation","disabled":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-install"},
"install.ai.grok-bot": {"icon":"","iconFont":"omarchy","label":"Grok Bot","disabled":"omarchy-pkg-present grok-bot","action":"omarchy-install-and-launch 'Grok Bot' grok-bot grok-bot"},
"install.ai.hermes": {"icon":"","iconFont":"omarchy","label":"Hermes Desktop","disabled":"omarchy-pkg-present hermes-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-hermes"},
Expand Down Expand Up @@ -311,6 +312,7 @@
"remove.service.tailscale": {"icon":"","label":"Tailscale","when":"omarchy-pkg-present tailscale","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-service-tailscale"},
"remove.ai.chatgpt": {"icon":"","iconFont":"omarchy","label":"ChatGPT Desktop","when":"omarchy-pkg-present openai-codex-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-chatgpt"},
"remove.ai.claude": {"icon":"","iconFont":"omarchy","label":"Claude Desktop","when":"omarchy-pkg-present claude-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-claude"},
"remove.ai.ghost": {"icon":"󰊠","label":"Ghost","when":"omarchy-pkg-present ghost","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-ghost"},
"remove.ai.dictation": {"icon":"","label":"Dictation","when":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-remove"},
"remove.ai.grok-bot": {"icon":"","iconFont":"omarchy","label":"Grok Bot","when":"omarchy-pkg-present grok-bot","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-grok-bot"},
"remove.ai.lm-studio": {"icon":"","iconFont":"omarchy","label":"LM Studio","when":"omarchy-pkg-present lmstudio-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-lm-studio"},
Expand Down
2 changes: 1 addition & 1 deletion manual/17-ai.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Crashes can also be silenced one program at a time, which is what the diagnosis

### Desktop apps

The _Install > AI_ menu also carries a few graphical AI apps: the ChatGPT desktop app, the Claude desktop app (Anthropic's Linux beta, with Chat, Cowork, and Claude Code tabs), Grok Bot for chatting with xAI's models, Hermes Desktop, OpenClaw, and the Perplexity desktop app.
The _Install > AI_ menu also carries a few graphical AI apps: the ChatGPT desktop app, the Claude desktop app (Anthropic's Linux beta, with Chat, Cowork, and Claude Code tabs), Ghost (a local AI persona with its own HUD, summoned with `Super + Ctrl + G`), Grok Bot for chatting with xAI's models, Hermes Desktop, OpenClaw, and the Perplexity desktop app.

Hermes Desktop is the one to know about, because there is only ever one Hermes on a machine. The app only runs against a runtime built from its own commit, so it installs one of its own under `~/.hermes` on first launch, which takes a few minutes and shows its own progress. From then on that is the Hermes the terminal `hermes` command and the default agent use too, whichever order you installed them in. Installing it also hands Hermes the Omarchy theme as a skin named `omarchy`, which every Hermes surface follows as you switch themes; pick another under Hermes' Appearance settings or with `/skin` if you'd rather it didn't, and Omarchy leaves that choice alone. Removing the app under _Remove > AI_ takes that runtime with it, and keeps your chats, memories, and the skills Hermes wrote for itself unless you tell it otherwise: it asks, defaulting to no, whether that data and your connection settings should go too.

Expand Down
73 changes: 73 additions & 0 deletions test/shell.d/ghost-install-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/bin/bash

set -euo pipefail

source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"

tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT

mkdir -p "$tmp_dir/bin" "$tmp_dir/home"
export TEST_LOG="$tmp_dir/log"
export HOME="$tmp_dir/home"

# Package installation and the user manager are stubbed; the script's job is
# the order between them.
cat >"$tmp_dir/bin/omarchy-pkg-add" <<'SCRIPT'
#!/bin/bash
printf 'add:%s\n' "$*" >>"$TEST_LOG"
SCRIPT
cat >"$tmp_dir/bin/systemctl" <<'SCRIPT'
#!/bin/bash
printf 'systemctl:%s\n' "$*" >>"$TEST_LOG"
SCRIPT
# The HUD is an omarchy-shell plugin, so the install also talks to the running
# shell. Both are stubbed; the script's job is the order between them.
cat >"$tmp_dir/bin/omarchy-shell" <<'SCRIPT'
#!/bin/bash
printf 'omarchy-shell:%s\n' "$*" >>"$TEST_LOG"
SCRIPT
cat >"$tmp_dir/bin/omarchy" <<'SCRIPT'
#!/bin/bash
printf 'omarchy:%s\n' "$*" >>"$TEST_LOG"
SCRIPT
chmod +x "$tmp_dir/bin"/*
export PATH="$tmp_dir/bin:$PATH"

: >"$TEST_LOG"
"$ROOT/bin/omarchy-install-ai-ghost" >"$tmp_dir/output" 2>&1 || fail "Ghost install succeeds" "$(cat "$tmp_dir/output")"

grep -q '^add:ghost$' "$TEST_LOG" || fail "Ghost install adds the ghost package"
pass "Ghost install adds the ghost package"

grep -q '^systemctl:--user enable --now ghostd.service$' "$TEST_LOG" ||
fail "Ghost install enables and starts the daemon unit"
pass "Ghost install enables and starts the daemon unit"

grep -q '^omarchy:plugin enable ferdousbhai.ghost$' "$TEST_LOG" ||
fail "Ghost install enables the HUD plugin in the running shell"
pass "Ghost install enables the HUD plugin in the running shell"

[[ -L $HOME/.config/omarchy/plugins/ferdousbhai.ghost ]] ||
fail "Ghost install links the packaged plugin into the user's plugins directory"
pass "Ghost install links the packaged plugin into the user's plugins directory"

[[ $(grep -n '^add:ghost$' "$TEST_LOG" | cut -d: -f1) -lt $(grep -n 'enable --now' "$TEST_LOG" | cut -d: -f1) ]] ||
fail "Ghost install starts the daemon only after the package is present"
pass "Ghost install starts the daemon only after the package is present"

grep -q 'Super + Ctrl + G' "$tmp_dir/output" || fail "Ghost install names the summon key"
pass "Ghost install names the summon key"

# A failed package install must not try to start units that do not exist.
cat >"$tmp_dir/bin/omarchy-pkg-add" <<'SCRIPT'
#!/bin/bash
printf 'add:%s\n' "$*" >>"$TEST_LOG"
exit 1
SCRIPT
: >"$TEST_LOG"
rc=0
"$ROOT/bin/omarchy-install-ai-ghost" >/dev/null 2>&1 || rc=$?
[[ $rc != 0 ]] || fail "Ghost install aborts when the package cannot be added"
! grep -q 'enable --now' "$TEST_LOG" || fail "Ghost install aborts when the package cannot be added" "units enabled anyway"
pass "Ghost install aborts when the package cannot be added"
44 changes: 44 additions & 0 deletions test/shell.d/remove-ai-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -378,3 +378,47 @@ rc=0
! grep -q '^drop:openclaw$' "$TEST_LOG" ||
fail "OpenClaw removal aborts when systemd cannot be reached" "package dropped anyway"
pass "OpenClaw removal aborts when systemd cannot be reached"

# Ghost's removal takes the package, its daemon unit, and its HUD plugin, and
# nothing of the owner's: ghost homes, credentials, and daemon state stay where
# they are.
cat >"$tmp_dir/bin/systemctl" <<'SCRIPT'
#!/bin/bash
printf 'systemctl:%s\n' "$*" >>"$TEST_LOG"
SCRIPT
cat >"$tmp_dir/bin/omarchy" <<'SCRIPT'
#!/bin/bash
printf 'omarchy:%s\n' "$*" >>"$TEST_LOG"
SCRIPT
chmod +x "$tmp_dir/bin/systemctl" "$tmp_dir/bin/omarchy"
cat >"$tmp_dir/bin/omarchy-pkg-present" <<'SCRIPT'
#!/bin/bash
[[ $1 == ghost ]]
SCRIPT
chmod +x "$tmp_dir/bin/omarchy-pkg-present"

: >"$TEST_LOG"
fresh_home
mkdir -p "$HOME/ghosts/casper" "$HOME/.config/ghost" "$HOME/.local/state/ghost" \
"$HOME/.config/omarchy/plugins"
ln -sfn /usr/share/ghost/plugin "$HOME/.config/omarchy/plugins/ferdousbhai.ghost"
"$ROOT/bin/omarchy-remove-ai-ghost" >/dev/null

grep -q '^drop:ghost$' "$TEST_LOG" || fail "Ghost removal drops the package"
pass "Ghost removal drops the package"

grep -q '^systemctl:--user disable --now ghostd.service$' "$TEST_LOG" ||
fail "Ghost removal stops the daemon before the package goes"
pass "Ghost removal stops the daemon before the package goes"

grep -q '^omarchy:plugin remove ferdousbhai.ghost --yes$' "$TEST_LOG" ||
fail "Ghost removal takes the HUD plugin out of the running shell"
pass "Ghost removal takes the HUD plugin out of the running shell"

[[ ! -e $HOME/.config/omarchy/plugins/ferdousbhai.ghost ]] ||
fail "Ghost removal unlinks the plugin from the user's plugins directory"
pass "Ghost removal unlinks the plugin from the user's plugins directory"

[[ -d $HOME/ghosts/casper && -d $HOME/.config/ghost && -d $HOME/.local/state/ghost ]] ||
fail "Ghost removal keeps ghost homes, settings, and state"
pass "Ghost removal keeps ghost homes, settings, and state"