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
24 changes: 0 additions & 24 deletions .codex-plugin/marketplace.json

This file was deleted.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ Want to create or modify HOTL skills? See [Authoring Skills vs Agents](docs/auth
curl -fsSL https://raw.githubusercontent.com/yimwoo/hotl-plugin/main/update.sh | bash
```

Covers Claude Code, native-skills Codex installs, and Cline, and skips tools that are not installed. Codex plugin installs are updated through Codex's plugin lifecycle instead. In Claude Code, you can also run `/hotl:check-update`. For backup behavior, manual checks, and `--force-codex`, see [Updating HOTL](docs/updating.md).
Covers Claude Code, Codex (both native-skills and plugin source checkout), and Cline. Skips tools that are not installed. In Claude Code, you can also run `/hotl:check-update`. For backup behavior, manual checks, and `--force-codex`, see [Updating HOTL](docs/updating.md).

## Supported Tools

Expand Down
66 changes: 52 additions & 14 deletions docs/README.codex.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ HOTL for Codex can be installed either as a native Codex plugin or as native ski

### Plugin Install (Recommended)

Requires a Codex version with plugin support. Register HOTL as a Codex plugin for
stable, versioned team distribution. Codex manages the plugin cache and lifecycle —
no manual symlinks needed.
Requires a Codex version with plugin support. The installer clones the HOTL repo
to a source checkout at `~/.codex/plugins/hotl-source/` and registers it in
`~/.agents/plugins/marketplace.json` as a local Codex plugin.

1. Clone the repo (or use an existing checkout):

Expand All @@ -27,23 +27,42 @@ git clone https://github.com/yimwoo/hotl-plugin /tmp/hotl-plugin
bash /tmp/hotl-plugin/install.sh --codex-plugin
```

This copies the HOTL plugin bundle into `~/.codex/plugins/hotl` and registers it
in `~/.agents/plugins/marketplace.json` as a local Codex plugin.
This clones HOTL to `~/.codex/plugins/hotl-source/` and writes a marketplace
entry with `"source": "local"` pointing at that checkout.

3. Restart Codex.

4. Open the Codex plugin directory, switch the source to **Local Plugins**, and
click **Add to Codex** for HOTL.

**For contributors** testing the plugin packaging from a repo checkout, use `--local`
to install the plugin under the repo and write to the repo-local marketplace
instead of your user-global config:
**For contributors** testing the plugin from a working copy, use `--local`
to point the marketplace at your current checkout without cloning:

```bash
bash install.sh --codex-plugin --local
```

Plugin updates are handled through Codex's plugin refresh flow, not `update.sh`.
This writes a repo-local marketplace entry at `.agents/plugins/marketplace.json`
pointing at your checkout directory.

**Generated marketplace entry shape:**

```json
{
"name": "hotl",
"source": {
"source": "local",
"path": "~/.codex/plugins/hotl-source"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Productivity"
}
```

Plugin updates are handled via `update.sh --codex-plugin` (see Updating below).

### Native Skills Install (Fallback / Development)

Expand Down Expand Up @@ -284,9 +303,23 @@ Restart Codex after updating so it re-discovers the latest skill files.

### Plugin Install

Plugin updates are managed through Codex's plugin lifecycle. When a new HOTL
version is available, refresh or reinstall the plugin through Codex's plugin UI.
`update.sh` does not apply to plugin installs.
The standard curl one-liner also updates the plugin source checkout if it exists:

```bash
curl -fsSL https://raw.githubusercontent.com/yimwoo/hotl-plugin/main/update.sh | bash
```

Or update only the plugin checkout:

```bash
bash ~/.codex/plugins/hotl-source/update.sh --codex-plugin
```

If the source checkout has local changes, they are backed up to
`~/.codex/backups/hotl-plugin/<timestamp>/` before resetting.
Use `--force-codex-plugin` to skip the backup.

Restart Codex after updating so it picks up the new plugin files.

## Codex Manual Canary

Expand Down Expand Up @@ -343,8 +376,13 @@ Remove-Item -Recurse -Force "$env:USERPROFILE\.codex\hotl"

### Plugin Install

Uninstall HOTL through Codex's plugin UI. To also remove the marketplace entry:
Remove the source checkout and marketplace entry:

**macOS / Linux:** Edit `~/.agents/plugins/marketplace.json` and remove the `hotl` entry.
**macOS / Linux:**

```bash
rm -rf ~/.codex/plugins/hotl-source
# Edit ~/.agents/plugins/marketplace.json and remove the hotl entry
```

**Repo-local:** Delete `.agents/plugins/marketplace.json` or remove the `hotl` entry from it.
71 changes: 36 additions & 35 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,37 +40,49 @@ if [ "$CODEX_PLUGIN" = true ]; then
exit 1
fi

GIT_REPO_URL="https://github.com/yimwoo/hotl-plugin.git"

if [ "$LOCAL" = true ]; then
INSTALL_ROOT="${SCRIPT_DIR}"
MARKETPLACE_DIR="${INSTALL_ROOT}/.agents/plugins"
# Local/contributor mode: point marketplace at the current checkout
MARKETPLACE_DIR="${SCRIPT_DIR}/.agents/plugins"
MARKETPLACE_FILE="${MARKETPLACE_DIR}/marketplace.json"
PLUGIN_ROOT="${INSTALL_ROOT}/.codex/plugins/${PLUGIN_NAME}"
PLUGIN_SOURCE_PATH="./.codex/plugins/${PLUGIN_NAME}"
echo "Installing HOTL in repo-local Codex plugin directory: ${PLUGIN_ROOT}"
PLUGIN_SOURCE_PATH="${SCRIPT_DIR}"
echo "Registering HOTL in repo-local Codex marketplace: ${MARKETPLACE_FILE}"
echo "Plugin source: current checkout at ${SCRIPT_DIR}"
else
INSTALL_ROOT="${HOME}"
# User-global mode: clone source checkout
MARKETPLACE_DIR="${HOME}/.agents/plugins"
MARKETPLACE_FILE="${MARKETPLACE_DIR}/marketplace.json"
PLUGIN_ROOT="${HOME}/.codex/plugins/${PLUGIN_NAME}"
PLUGIN_SOURCE_PATH="./.codex/plugins/${PLUGIN_NAME}"
echo "Installing HOTL in user-global Codex plugin directory: ${PLUGIN_ROOT}"
echo "Registering HOTL in user-global Codex marketplace: ${MARKETPLACE_FILE}"
PLUGIN_SOURCE_PATH="${HOME}/.codex/plugins/hotl-source"

if [ -d "${PLUGIN_SOURCE_PATH}/.git" ]; then
echo "Updating existing source checkout at ${PLUGIN_SOURCE_PATH}..."
git -C "${PLUGIN_SOURCE_PATH}" fetch origin main
git -C "${PLUGIN_SOURCE_PATH}" reset --hard origin/main
else
echo "Cloning HOTL to ${PLUGIN_SOURCE_PATH}..."
mkdir -p "$(dirname "${PLUGIN_SOURCE_PATH}")"
git clone "${GIT_REPO_URL}" "${PLUGIN_SOURCE_PATH}"
fi

# Migration: remove old copied-bundle install if present
OLD_BUNDLE="${HOME}/.codex/plugins/hotl"
if [ -d "${OLD_BUNDLE}" ] && [ ! -d "${OLD_BUNDLE}/.git" ]; then
echo ""
echo "Migrating from copied-bundle plugin install at ${OLD_BUNDLE}"
echo "to source checkout at ${PLUGIN_SOURCE_PATH}..."
rm -rf "${OLD_BUNDLE}"
echo "Old bundle removed."
fi
fi

PLUGIN_MANIFEST="${SCRIPT_DIR}/.codex-plugin/plugin.json"
PLUGIN_MANIFEST="${PLUGIN_SOURCE_PATH}/.codex-plugin/plugin.json"
if [ ! -f "$PLUGIN_MANIFEST" ]; then
echo "Error: ${PLUGIN_MANIFEST} not found." >&2
echo "Are you running install.sh from the hotl-plugin repository?" >&2
exit 1
fi

mkdir -p "$MARKETPLACE_DIR" "$(dirname "${PLUGIN_ROOT}")"
rsync -a --delete \
--exclude '.git' \
--exclude '.codex/plugins' \
--exclude '.agents/plugins' \
"${SCRIPT_DIR}/" "${PLUGIN_ROOT}/"
mkdir -p "$MARKETPLACE_DIR"

python3 -c "
import json, sys, os
Expand All @@ -83,20 +95,6 @@ owner_name = os.environ.get('USER', 'unknown')
with open(manifest_path) as f:
manifest = json.load(f)

manifest.setdefault('interface', {
'displayName': 'HOTL',
'shortDescription': 'Human-on-the-Loop workflows for structured AI development',
'longDescription': 'Use HOTL to brainstorm, plan, execute, review, and verify software changes with explicit human-on-the-loop contracts.',
'developerName': owner_name,
'category': 'Productivity',
'capabilities': ['Interactive', 'Read', 'Write'],
'defaultPrompt': 'Plan, execute, review, and verify coding tasks with HOTL workflows'
})

with open(manifest_path, 'w') as f:
json.dump(manifest, f, indent=2)
f.write('\n')

hotl_entry = {
'name': manifest['name'],
'description': manifest['description'],
Expand All @@ -113,6 +111,9 @@ hotl_entry = {
'category': 'Productivity',
}

if manifest.get('interface'):
hotl_entry['interface'] = manifest['interface']

# Read or create the destination marketplace file
if os.path.exists(dest_path):
with open(dest_path) as f:
Expand Down Expand Up @@ -148,7 +149,7 @@ with open(dest_path, 'w') as f:

action = 'Updated' if updated else 'Added'
print(f'{action} HOTL plugin entry (version {hotl_entry[\"version\"]})')
" "$PLUGIN_ROOT/.codex-plugin/plugin.json" "$MARKETPLACE_FILE" "$PLUGIN_SOURCE_PATH"
" "$PLUGIN_MANIFEST" "$MARKETPLACE_FILE" "$PLUGIN_SOURCE_PATH"

# Warn if an existing native-skills install is detected
if [ -L "${HOME}/.agents/skills/hotl" ] || [ -d "${HOME}/.codex/hotl" ]; then
Expand All @@ -169,12 +170,12 @@ print(f'{action} HOTL plugin entry (version {hotl_entry[\"version\"]})')
fi

echo ""
echo "HOTL Codex plugin prepared. Next steps:"
echo "HOTL Codex plugin registered. Next steps:"
echo " 1. Restart Codex to discover the plugin"
echo " 2. Open the Codex plugin directory, switch to Local Plugins,"
echo " and click Add to Codex for HOTL"
echo ""
echo "To update later, use Codex's plugin refresh flow."
echo "To update later, run: update.sh (updates all installs) or update.sh --codex-plugin (plugin only)"
echo "For native skills install (dev/iteration), see docs/README.codex.md"
exit 0
fi
Expand Down
8 changes: 5 additions & 3 deletions test/smoke.bats
Original file line number Diff line number Diff line change
Expand Up @@ -296,18 +296,20 @@ assert 'sessionStartNotice' not in data.get('hookSpecificOutput', {})
ver=$(cat "$REPO_ROOT/VERSION" | tr -d '[:space:]')
[ -n "$ver" ] || { echo "VERSION file is empty"; return 1; }

local claude_ver cursor_ver market_ver codex_ver codex_market_ver
local claude_ver cursor_ver market_ver codex_ver
claude_ver=$(python3 -c "import json; print(json.load(open('$REPO_ROOT/.claude-plugin/plugin.json'))['version'])")
cursor_ver=$(python3 -c "import json; print(json.load(open('$REPO_ROOT/.cursor-plugin/plugin.json'))['version'])")
market_ver=$(python3 -c "import json; print(json.load(open('$REPO_ROOT/.claude-plugin/marketplace.json'))['plugins'][0]['version'])")
codex_ver=$(python3 -c "import json; print(json.load(open('$REPO_ROOT/.codex-plugin/plugin.json'))['version'])")
codex_market_ver=$(python3 -c "import json; print(json.load(open('$REPO_ROOT/.codex-plugin/marketplace.json'))['plugins'][0]['version'])")

[ "$ver" = "$claude_ver" ] || { echo "VERSION ($ver) != .claude-plugin/plugin.json ($claude_ver)"; return 1; }
[ "$ver" = "$cursor_ver" ] || { echo "VERSION ($ver) != .cursor-plugin/plugin.json ($cursor_ver)"; return 1; }
[ "$ver" = "$market_ver" ] || { echo "VERSION ($ver) != .claude-plugin/marketplace.json ($market_ver)"; return 1; }
[ "$ver" = "$codex_ver" ] || { echo "VERSION ($ver) != .codex-plugin/plugin.json ($codex_ver)"; return 1; }
[ "$ver" = "$codex_market_ver" ] || { echo "VERSION ($ver) != .codex-plugin/marketplace.json ($codex_market_ver)"; return 1; }
}

@test ".codex-plugin/marketplace.json does not exist (installer generates entries)" {
[ ! -f "$REPO_ROOT/.codex-plugin/marketplace.json" ]
}

# ── command/skill name collision guard ────────────────────────────────────────
Expand Down
Loading
Loading