diff --git a/ai/README.md b/ai/README.md index c4e67bd..34eb5a3 100644 --- a/ai/README.md +++ b/ai/README.md @@ -10,21 +10,31 @@ setup remains managed by the adapters below. `plugins/stephendolan` is a compatibility symlink for marketplace loaders that require plugin roots beneath `plugins/`; `ai/` remains the canonical source. -For local setup, `skillset.json` is the declarative cross-agent allowlist and -`scripts/install-skills.py` installs it through skills.sh, plus any declared -token-gated upstream installer. A missing optional token prints a concise skip -and does not interrupt the remaining setup. +For local setup, `skillset.json` declares non-plugin skills for installation +through skills.sh, plus any declared token-gated upstream installer. The +personal `stephendolan` bundle remains installed through its marketplace plugin; +it is deliberately excluded from skills.sh so Codex sees only its namespaced +plugin skills rather than duplicate copies in shared paths such as +`~/.agents/skills`. A missing optional token prints a concise skip and does not +interrupt the remaining setup. ## Install -```text -/plugin marketplace add stephendolan/dotfiles -/plugin install stephendolan@dotfiles -``` - -Plugin users can access skills under their runtime's plugin namespace. Local -setup installs the skills declared in `skillset.json` through skills.sh. Codex -uses generated native roles; Cursor links Comment Sicko's canonical role directly. +Plugin users access the personal skills under their runtime's plugin namespace. +Local setup installs only the non-plugin skills declared in `skillset.json` +through skills.sh. Codex uses generated native roles; Cursor links Comment +Sicko's canonical role directly. + +- Claude Code: `scripts/claude-code-setup.sh` adds the marketplace and installs + `stephendolan@dotfiles`. +- Codex: `scripts/codex-plugin-setup.sh` does the same and is run by `./install`. +- Cursor: install `stephendolan` at **user scope** from Customize → Plugins. + This is the supported account-synced route for Cursor and Cloud Agents; Cursor + does not provide a non-interactive plugin-install command. The repository's + `plugin.json` and `.cursor-plugin/plugin.json` supply the package metadata. + +Do not install `stephendolan/dotfiles` through skills.sh or link `ai/skills` +into an agent skills directory: either route creates an unnamespaced second copy. Amp loads the same skills from Stephen's personal skills repository, including in orbs. Pushing this repository's `main` branch publishes the committed `ai/skills` subtree to Amp. @@ -77,8 +87,11 @@ Personal skills available across Stephen's agents: | `mom-test` | Customer-discovery question and evidence review | | `drama-triangle` | Communication and agency analysis | -`skillset.json` may omit `skills` to install every skill from a personal source, -or name selected third-party skills to keep the shared set intentionally small. +`skillset.json` may omit `skills` to install every non-plugin source, or name +selected third-party skills to keep the shared set intentionally small. Do not +add the personal `stephendolan/dotfiles` bundle here: its marketplace plugin is +the canonical installation path, and the installer rejects it to prevent +unnamespaced duplicates. Model-invoked skills route natural-language requests into local tools or data: diff --git a/ai/scripts/install-skills.py b/ai/scripts/install-skills.py index d2b1133..d3f74fb 100644 --- a/ai/scripts/install-skills.py +++ b/ai/scripts/install-skills.py @@ -14,6 +14,7 @@ REPOSITORY_ROOT = Path(__file__).resolve().parents[2] SKILLSET_PATH = REPOSITORY_ROOT / "ai" / "skillset.json" +MARKETPLACE_SOURCES = {"stephendolan/dotfiles"} def load_skillset() -> tuple[list[str], list[dict[str, object]]]: @@ -33,6 +34,10 @@ def load_skillset() -> tuple[list[str], list[dict[str, object]]]: installer = entry.get("installer", "skills.sh") if installer == "skills.sh" and not isinstance(entry.get("source"), str): raise ValueError("skills.sh sources need a string source") + if entry.get("source") in MARKETPLACE_SOURCES: + raise ValueError( + f"{entry['source']} is installed through its marketplace plugin, not skills.sh" + ) if installer == "uidotsh" and not isinstance(entry.get("token_env"), str): raise ValueError("ui.sh sources need a token_env") if installer not in {"skills.sh", "uidotsh"}: diff --git a/ai/skillset.json b/ai/skillset.json index d878222..adb1c11 100644 --- a/ai/skillset.json +++ b/ai/skillset.json @@ -7,14 +7,18 @@ "skills": ["design", "canonicalize-tailwind", "componentize"] }, { - "source": "stephendolan/dotfiles" - }, - { - "source": "stephendolan/private-skills" + "source": "stephendolan/private-skills", + "skills": [ + "book-family-stay", + "cooking", + "notes-knowledge-base", + "order-daycare-lunch", + "troubleshoot-home-network" + ] }, { "source": "mattpocock/skills", - "skills": ["grill-me"] + "skills": ["grill-me", "writing-for-agents"] }, { "source": "emilkowalski/skills", @@ -34,6 +38,10 @@ { "source": "shadcn/improve", "skills": ["improve"] + }, + { + "source": "EveryInc/compound-engineering-plugin", + "skills": ["ce-code-review"] } ] } diff --git a/install.conf.yaml b/install.conf.yaml index 7bf224a..7eb78c8 100644 --- a/install.conf.yaml +++ b/install.conf.yaml @@ -102,6 +102,7 @@ - [./scripts/brew-setup.sh, Setting up Homebrew and packages] - command: python3 ./ai/scripts/install-skills.py description: Installing declared agent skills + - [./scripts/codex-plugin-setup.sh, Setting up Codex plugins] - [./ai/generate-mcp.sh, Generating MCP configs] - [./scripts/claude-code-setup.sh, Setting up Claude Code] - [./scripts/shell-setup.sh, Setting up default shell] diff --git a/scripts/codex-plugin-setup.sh b/scripts/codex-plugin-setup.sh new file mode 100755 index 0000000..2024523 --- /dev/null +++ b/scripts/codex-plugin-setup.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +MARKETPLACE="dotfiles" +MARKETPLACE_SOURCE="stephendolan/dotfiles" +PLUGIN="stephendolan@dotfiles" + +if ! command -v codex >/dev/null 2>&1; then + echo "Codex CLI is not installed; skipping plugin setup." + exit 0 +fi + +if ! codex plugin marketplace list --json | jq -e --arg name "$MARKETPLACE" \ + '.marketplaces[] | select(.name == $name)' >/dev/null; then + echo "Adding Codex marketplace: $MARKETPLACE_SOURCE" + codex plugin marketplace add "$MARKETPLACE_SOURCE" +fi + +if ! codex plugin list --marketplace "$MARKETPLACE" --json | jq -e --arg id "$PLUGIN" \ + '.installed[] | select(.pluginId == $id)' >/dev/null; then + echo "Installing Codex plugin: $PLUGIN" + codex plugin add "$PLUGIN" +fi + +echo "Codex plugin setup complete!"