Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
{
"name": "use-family",
"description": "The whole *-use family in one install: every *-use plugin below as a dependency, a routing skill that tells the agent which one to use and how they combine, and a session-start check for missing CLIs, and CLIs kept at their plugin's version.",
"version": "0.9.1",
"version": "0.10.0",
"category": "productivity",
"source": "./plugins/use-family",
"homepage": "https://github.com/leeguooooo/plugins/tree/main/plugins/use-family"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ jobs:
- uses: actions/checkout@v4
- run: sh tests/install-use-family.test.sh
- run: sh tests/doctor.test.sh
- run: sh tests/install-clis.test.sh
- run: sh tests/readme-codex-list.test.sh
- name: install-use-family.ps1 parses
shell: pwsh
Expand Down
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,15 @@ Agent-native CLIs that let coding agents drive real, logged-in surfaces — a br
/plugin install use-family@leeguooooo-plugins
```

`use-family` installs every `*-use` plugin below as a dependency, adds a `use-family` skill that tells the agent which one to reach for and how they combine, and checks at session start for any missing CLI (it only names the one-line installer; nothing runs without asking).
`use-family` installs every `*-use` plugin below as a dependency, adds a `use-family` skill that tells the agent which one to reach for and how they combine, and checks at session start for any missing CLI (it only names the installers; nothing runs without asking).

Then install every CLI this machine is missing in one go. It runs each use's own installer, skips the ones that don't run here (wechat-use on Intel Macs, for example) and the ones already installed, and lists the one-time setup some of them need:

```sh
curl -fsSL https://raw.githubusercontent.com/leeguooooo/plugins/main/install-clis.sh | sh
```

`… | sh -s -- mail-use ocs` installs just those, including iphone-use, which isn't in the default set because it needs an iPhone.

**Codex and the ChatGPT desktop app.** Codex doesn't install plugin dependencies, so `use-family` there brings in the routing skill alone and every `*-use` has to be added by name. To add the whole family:

Expand All @@ -57,7 +65,7 @@ for plugin in use-family chrome-use cookie-use mail-use message-use paste-use we
done
```

The list is `use-family`'s `dependencies` in [plugins/use-family/.claude-plugin/plugin.json](plugins/use-family/.claude-plugin/plugin.json); drop the ones you don't want. This installs the plugins (skills) only. The CLIs still install themselves on first use or with the one-line installers below, and a few uses need a one-time setup of their own (for example `memory-use init`, adding a mailbox to `mail-use`).
The list is `use-family`'s `dependencies` in [plugins/use-family/.claude-plugin/plugin.json](plugins/use-family/.claude-plugin/plugin.json); drop the ones you don't want. This installs the plugins (skills) only. The CLIs still install themselves on first use, or all at once with `install-clis.sh` (above), and a few uses need a one-time setup of their own (for example `memory-use init`, adding a mailbox to `mail-use`).

The marketplace also shows up in the ChatGPT desktop app's Plugins Directory. `codex plugin marketplace upgrade leeguooooo-plugins` refreshes it.

Expand All @@ -73,7 +81,7 @@ Windows (PowerShell; skips the macOS-only uses):
irm https://raw.githubusercontent.com/leeguooooo/plugins/main/install-use-family.ps1 | iex
```

A skill that already exists as a real folder is left alone and reported. Some uses (wechat-use, iphone-use) have installers that keep their skill folder in step with the CLI they install; leave those. A copy from `npx skills add` can be deleted so the installer links an updatable checkout instead. A link you made yourself (to your own checkout) is kept. The installer doesn't install CLIs; it lists the missing ones with their one-line installers. memory-use also needs `memory-use init --repo <owner>/<notes>` once (`--create` for a new private notes repo).
A skill that already exists as a real folder is left alone and reported. Some uses (wechat-use, iphone-use) have installers that keep their skill folder in step with the CLI they install; leave those. A copy from `npx skills add` can be deleted so the installer links an updatable checkout instead. A link you made yourself (to your own checkout) is kept. The installer doesn't install CLIs; it lists the missing ones, and `install-clis.sh` (above) installs them all. memory-use also needs `memory-use init --repo <owner>/<notes>` once (`--create` for a new private notes repo).

**Upgrading.** Every CLI in the family has `<name> upgrade` (updates the CLI and its skill, `--check` to only look) and prints one line on stderr when a newer release exists, at most once a day; see [docs/upgrade.md](docs/upgrade.md). To upgrade everything at once:

Expand Down
61 changes: 61 additions & 0 deletions install-clis.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/bin/sh
# Install every *-use CLI this machine is missing, each through its own install.sh, in one go.
# curl -fsSL https://raw.githubusercontent.com/leeguooooo/plugins/main/install-clis.sh | sh
# curl -fsSL https://raw.githubusercontent.com/leeguooooo/plugins/main/install-clis.sh | sh -s -- mail-use ocs
# With names, installs just those (iphone-use too, which needs an iPhone and isn't in the default set).
# Installed CLIs are left alone: `<name> upgrade`, or upgrade-use-family.sh, updates them.
# The plugins / skills come from `/plugin install use-family@leeguooooo-plugins` or
# install-use-family.sh; this only puts the CLIs on PATH. One failure doesn't stop the rest.
set -u
PATH="$PATH:$HOME/.local/bin"
RAW="${USE_FAMILY_RAW:-https://raw.githubusercontent.com/leeguooooo}"

# Keep in step with plugins/use-family/scripts/doctor.sh.
os=$(uname -s); arch=$(uname -m)
names="chrome-use cookie-use mail-use discord-use bitwarden-use chatgpt-use memory-use ocs image-use profile-use"
case "$os" in MINGW*|MSYS*|CYGWIN*) ;; *) names="$names motion-use" ;; esac # motion-use: macOS and Linux only
[ "$os" = Darwin ] && names="$names message-use paste-use"
# wechat-use's macOS installer stops with "Apple Silicon only" on Intel.
[ "$os-$arch" = Darwin-arm64 ] && names="$names wechat-use"
[ $# -gt 0 ] && names="$*"

repo_of() { case "$1" in ocs) echo open-cross-session ;; *) echo "$1" ;; esac; }
installed() { # installed <name>: already set up here
case "$1" in
# profile-use is a skill with a script, not a CLI; iphone-use is a daemon with an app.
profile-use) [ -f "${USE_FAMILY_DIR:-$HOME/.agents/use-family}/profile-use/SKILL.md" ] ;;
iphone-use) [ -d "$HOME/Applications/iPhoneUse.app" ] || [ -d /Applications/iPhoneUse.app ] ;;
*) command -v "$1" >/dev/null 2>&1 ;;
esac
}

tmp=$(mktemp -d) || exit 1
trap 'rm -rf "$tmp"' EXIT
done_list=""; skipped=""; failed=""
for name in $names; do
if installed "$name"; then skipped="$skipped $name"; continue; fi
echo "==> $name"
url="$RAW/$(repo_of "$name")/main/install.sh"
# Download first: in `curl | sh` a failed download is an empty script that "succeeds".
if ! curl -fsSL --retry 2 -o "$tmp/$name.sh" "$url"; then
echo " could not download $url"; failed="$failed $name"; continue
fi
if sh "$tmp/$name.sh" < /dev/null; then done_list="$done_list $name"; else failed="$failed $name"; fi
done

echo
[ -n "$done_list" ] && echo "installed:$done_list"
[ -n "$skipped" ] && echo "already installed:$skipped"
[ -n "$failed" ] && echo "failed:$failed (re-run to retry; each one's output is above)"
case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *)
echo "note: most CLIs live in ~/.local/bin; add it to PATH" ;;
esac
# One-time setup some of them need before they're useful.
case " $done_list $skipped " in *" memory-use "*)
[ -f "$HOME/.config/memory-use/config.json" ] ||
echo "next: memory-use init --repo <owner>/<notes> (--create for a new private notes repo)" ;;
esac
case " $done_list " in *" mail-use "*) echo "next: add a mailbox: \`mail-use apple-mail import\` (takes Apple Mail's accounts) or \`mail-use account --help\`" ;; esac
case " $done_list " in *" message-use "*) echo "next: give your terminal Full Disk Access so message-use can read Messages" ;; esac
case " $done_list " in *" discord-use "*) echo "next: set DISCORD_TOKEN (a bot token) for discord-use" ;; esac
[ -z "$failed" ]
15 changes: 4 additions & 11 deletions install-use-family.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,10 @@ echo
echo "Skills are in $SKILLS. Start a new Codex session to pick them up."
if [ -n "$missing" ]; then
echo "CLIs not installed yet:$missing"
echo "Install them all at once:"
echo " curl -fsSL https://raw.githubusercontent.com/leeguooooo/plugins/main/install-clis.sh | sh"
echo "Or one at a time:"
for name in $missing; do
case "$name" in
# image-use has no install.sh: the CLI is the single file at the root of the checkout above.
image-use) echo " sudo install \"$BASE/image-use/image-use\" /usr/local/bin/image-use" ;;
# No prebuilt binary for Intel Macs; the installer only says to build from source.
discord-use) if [ "$(uname -s)-$(uname -m)" = Darwin-x86_64 ]; then
echo " cargo install --git https://github.com/leeguooooo/discord-use --root ~/.local # Intel Mac: builds from source, needs Rust (rustup.rs)"
else
echo " curl -fsSL https://raw.githubusercontent.com/leeguooooo/discord-use/main/install.sh | sh"
fi ;;
*) echo " curl -fsSL https://raw.githubusercontent.com/leeguooooo/$(repo_of "$name")/main/install.sh | sh" ;;
esac
echo " curl -fsSL https://raw.githubusercontent.com/leeguooooo/$(repo_of "$name")/main/install.sh | sh"
done
fi
2 changes: 1 addition & 1 deletion plugins/use-family/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "use-family",
"version": "0.9.1",
"version": "0.10.0",
"description": "The whole *-use family in one install: browser, email, texts (iMessage/SMS), chat, phone, identity, passwords, sessions, notes, clipboard history, promo and explainer videos, and the other agents on your machines. Adds a routing skill that tells the agent which use to reach for and how they combine, plus a session-start check for missing CLIs and one that upgrades a CLI older than its plugin.",
"author": {
"name": "leeguooooo"
Expand Down
15 changes: 6 additions & 9 deletions plugins/use-family/scripts/doctor.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#!/bin/sh
# use-family session-start check: name any missing *-use CLI and its one-line installer.
# Silent when everything is installed, so the common case adds nothing to the context.
# Only names the installer; the agent asks the user before running it.
# Only names the installers; the agent asks the user before running them.
PATH="$PATH:$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin"

# iphone-use runs as a daemon and profile-use ships its script inside the skill,
# so neither has a CLI on PATH to look for.
# Keep the list in step with install-clis.sh at the repo root.
os=$(uname -s); arch=$(uname -m)
names="chrome-use cookie-use mail-use discord-use bitwarden-use chatgpt-use memory-use ocs image-use"
case "$os" in MINGW*|MSYS*|CYGWIN*) ;; *) names="$names motion-use" ;; esac # motion-use: macOS and Linux only
Expand All @@ -25,14 +26,10 @@ fi
[ -z "$missing" ] && exit 0

echo "use-family: these CLIs are not installed:$missing"
echo "Install one when a task needs it, after asking the user:"
echo "Install them when a task needs one, after asking the user. All at once:"
echo " curl -fsSL https://raw.githubusercontent.com/leeguooooo/plugins/main/install-clis.sh | sh"
echo "Or one at a time:"
for name in $missing; do
repo=$name; [ "$name" = ocs ] && repo=open-cross-session
case "$name-$os-$arch" in
# image-use has no install.sh: the CLI is the single file at the root of its repo.
image-use-*) echo " git clone -q --depth 1 https://github.com/leeguooooo/image-use ~/.agents/use-family/image-use && ln -sf ~/.agents/use-family/image-use/image-use ~/.local/bin/image-use" ;;
# No prebuilt binary for Intel Macs; the installer only says to build from source.
discord-use-Darwin-x86_64) echo " cargo install --git https://github.com/leeguooooo/discord-use --root ~/.local # Intel Mac: builds from source, needs Rust (rustup.rs)" ;;
*) echo " curl -fsSL https://raw.githubusercontent.com/leeguooooo/$repo/main/install.sh | sh" ;;
esac
echo " curl -fsSL https://raw.githubusercontent.com/leeguooooo/$repo/main/install.sh | sh"
done
2 changes: 1 addition & 1 deletion plugins/use-family/skills/use-family/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,4 @@ Each CLI prints `<name> X is available` on stderr when a newer release exists (a
2. Redacted by default. Raw values only at the moment of use, never in the final reply.
3. Never bypass a Touch ID or unlock prompt, and never handle the master password.
4. Before committing to any repo that could echo personal data, run `profile-use leak-scan --staged --all-profiles`.
5. A CLI is missing → this plugin's session-start check names the one-line installer; ask the user before running it.
5. A CLI is missing → this plugin's session-start check names its installer, plus `install-clis.sh` for all missing ones at once; ask the user before running either.
9 changes: 4 additions & 5 deletions tests/doctor.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,13 @@ fail() { echo "FAIL: $1"; echo "$out"; exit 1; }

out=$(run x86_64)
echo "$out" | grep -q 'not installed:.* wechat-use' && fail "Intel: suggests wechat-use, whose installer is Apple Silicon only"
echo "$out" | grep -qF 'cargo install --git https://github.com/leeguooooo/discord-use' || fail "Intel: no build-from-source hint for discord-use"
echo "$out" | grep -q 'discord-use/main/install.sh' && fail "Intel: points at discord-use's install.sh, which has no Intel binary"
echo "$out" | grep -qF 'leeguooooo/plugins/main/install-clis.sh | sh' || fail "no all-at-once install-clis.sh line"
echo "$out" | grep -qF 'leeguooooo/discord-use/main/install.sh | sh' || fail "Intel: no discord-use installer (it has an Intel binary since v0.2.1)"
echo "$out" | grep -q 'not installed:.* image-use' || fail "image-use not checked"
echo "$out" | grep -q 'image-use/main/install.sh' && fail "points at an install.sh image-use does not have"
echo "$out" | grep -qF 'ln -sf ~/.agents/use-family/image-use/image-use ~/.local/bin/image-use' || fail "no single-file install hint for image-use"
echo "$out" | grep -qF 'leeguooooo/image-use/main/install.sh | sh' || fail "no image-use installer line"
echo "$out" | grep -qF 'leeguooooo/open-cross-session/main/install.sh | sh' || fail "ocs not mapped to its repo"

out=$(run arm64)
echo "$out" | grep -q 'not installed:.* wechat-use' || fail "Apple Silicon: wechat-use not checked"
echo "$out" | grep -qF 'leeguooooo/discord-use/main/install.sh | sh' || fail "Apple Silicon: lost discord-use's install.sh hint"

echo "ok doctor.sh"
66 changes: 66 additions & 0 deletions tests/install-clis.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/bin/sh
# Runs install-clis.sh with a fake curl (each "installer" just drops a CLI into ~/.local/bin),
# a fake uname and an isolated HOME. Checks which CLIs it installs per platform, that it skips
# installed ones, keeps going past a failure and reports it. Nothing outside a temp dir is touched.
# sh tests/install-clis.test.sh
set -eu
ROOT=$(cd "$(dirname "$0")/.." && pwd)
TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
mkdir -p "$TMP/bin"
cat > "$TMP/bin/uname" <<'UNAME'
#!/bin/sh
case "$1" in -m) echo "$FAKE_ARCH" ;; *) echo Darwin ;; esac
UNAME
# Fake curl: -o <file> <url>; the "installer" for <repo> creates the CLI. $FAIL_REPO 404s.
cat > "$TMP/bin/curl" <<'CURL'
#!/bin/sh
out=""; url=""
while [ $# -gt 0 ]; do case "$1" in -o) out=$2; shift 2 ;; -*) shift ;; *) url=$1; shift ;; esac; done
repo=${url#*/leeguooooo/}; repo=${repo%%/*}
[ "$repo" = "${FAIL_REPO:-}" ] && exit 22
name=$repo; [ "$repo" = open-cross-session ] && name=ocs
if [ "$name" = profile-use ]; then
printf 'mkdir -p "$HOME/.agents/use-family/profile-use"; touch "$HOME/.agents/use-family/profile-use/SKILL.md"\n' > "$out"
else
printf 'mkdir -p "$HOME/.local/bin"; printf "#!/bin/sh\\n" > "$HOME/.local/bin/%s"; chmod +x "$HOME/.local/bin/%s"\n' "$name" "$name" > "$out"
fi
CURL
chmod +x "$TMP/bin/uname" "$TMP/bin/curl"
for tool in sh mkdir mktemp rm chmod touch printf cat; do
p=$(command -v "$tool") && case "$p" in /*) ln -sf "$p" "$TMP/bin/$tool" ;; esac
done

fresh() { rm -rf "$TMP/home"; mkdir -p "$TMP/home"; }
run() { # run <arch> [args]: install-clis.sh's output; status in $rc
arch=$1; shift
set +e; out=$(FAKE_ARCH=$arch HOME="$TMP/home" PATH="$TMP/bin" sh "$ROOT/install-clis.sh" "$@" 2>&1); rc=$?; set -e
}
fail() { echo "FAIL: $1"; echo "$out"; exit 1; }
has() { [ -x "$TMP/home/.local/bin/$1" ]; }

fresh; run x86_64
[ "$rc" = 0 ] || fail "Intel: exit $rc"
for n in chrome-use cookie-use mail-use discord-use bitwarden-use chatgpt-use memory-use ocs image-use motion-use message-use paste-use; do
has "$n" || fail "Intel: $n not installed"
done
[ -f "$TMP/home/.agents/use-family/profile-use/SKILL.md" ] || fail "Intel: profile-use not installed"
has wechat-use && fail "Intel: ran wechat-use's Apple Silicon-only installer"
echo "$out" | grep -q '^next: memory-use init' || fail "no memory-use init hint"

run x86_64
echo "$out" | grep -q '^installed:' && fail "re-run installed something again"
echo "$out" | grep -q '^already installed:.* ocs' || fail "re-run: ocs not reported as already installed"

fresh; run arm64
has wechat-use || fail "Apple Silicon: wechat-use not installed"

fresh; FAIL_REPO=mail-use run arm64
[ "$rc" != 0 ] || fail "a failed install still exited 0"
echo "$out" | grep -q '^failed: mail-use' || fail "failure not reported"
has ocs || fail "stopped after a failure instead of carrying on"

fresh; run arm64 ocs iphone-use
has ocs || fail "named: ocs not installed"
has chrome-use && fail "named: installed more than it was asked"

echo "ok install-clis.sh"
10 changes: 3 additions & 7 deletions tests/install-use-family.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,16 @@ fail() { echo "FAIL: $1"; echo "$out"; exit 1; }

out=$(run)
echo "$out" | grep -q '^CLIs not installed yet:.* image-use' || fail "image-use missing from the not-installed list"
echo "$out" | grep -qF "sudo install \"$TMP/home/.agents/use-family/image-use/image-use\" /usr/local/bin/image-use" \
|| fail "no single-file install hint for image-use"
echo "$out" | grep -q 'leeguooooo/image-use/main/install.sh' && fail "points at an install.sh image-use does not have"
echo "$out" | grep -qF 'leeguooooo/image-use/main/install.sh | sh' || fail "no image-use installer line"
echo "$out" | grep -qF 'leeguooooo/mail-use/main/install.sh | sh' || fail "generic install.sh hint lost"

echo "$out" | grep -qF 'leeguooooo/plugins/main/install-clis.sh | sh' || fail "no all-at-once install-clis.sh line"
echo "$out" | grep -q '^CLIs not installed yet:.* wechat-use' || fail "Apple Silicon: wechat-use missing from the not-installed list"
echo "$out" | grep -qF 'leeguooooo/discord-use/main/install.sh | sh' || fail "Apple Silicon: lost discord-use's install.sh hint"

out=$(FAKE_ARCH=x86_64 run)
echo "$out" | grep -q '^CLIs not installed yet:.* wechat-use' && fail "Intel: suggests wechat-use, whose installer is Apple Silicon only"
echo "$out" | grep -qF 'cargo install --git https://github.com/leeguooooo/discord-use' || fail "Intel: no build-from-source hint for discord-use"

printf '#!/bin/sh\n' > "$TMP/bin/image-use"; chmod +x "$TMP/bin/image-use"
out=$(run)
echo "$out" | grep -q 'image-use/image-use\|not installed yet:.* image-use' && fail "image-use reported although it is on PATH"
echo "$out" | grep -q 'not installed yet:.* image-use' && fail "image-use reported although it is on PATH"

echo "ok install-use-family.sh"
Loading