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
20 changes: 20 additions & 0 deletions .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "theopenapps",
"interface": {
"displayName": "OpenApps"
},
"plugins": [
{
"name": "plugin-stack",
"source": {
"source": "local",
"path": "./"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Developer Tools"
}
]
}
3 changes: 1 addition & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
"url": "https://theopenapps.com"
},
"metadata": {
"description": "Skills for AI plugin builders, on live data from OpenApps",
"version": "0.1.0"
"description": "Skills for AI plugin builders, on live data from OpenApps"
},
"plugins": [
{
Expand Down
1 change: 0 additions & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"name": "plugin-stack",
"description": "Skills for AI plugin builders: optimize listings, submit to stores, audit discoverability, design widgets and watch competitors, on live data from OpenApps",
"version": "0.1.0",
"author": {
"name": "The Open Apps, Inc.",
"url": "https://theopenapps.com"
Expand Down
1 change: 0 additions & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"name": "plugin-stack",
"description": "Skills for AI plugin builders: optimize listings, submit to stores, audit discoverability, design widgets and watch competitors, on live data from OpenApps",
"version": "0.1.0",
"author": {
"name": "The Open Apps, Inc.",
"url": "https://theopenapps.com"
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Check

on:
push:
branches: [main]
pull_request:

jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: node scripts/check-skills.mjs
- run: npm install -g @anthropic-ai/claude-code @openai/codex
- run: scripts/test-installs.sh
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Agents

Read [CONTRIBUTING.md](CONTRIBUTING.md) before changing a skill. Run `node scripts/check-skills.mjs` when you finish.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
42 changes: 42 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Contributing

Open an issue for a bug, a wrong number or a skill you'd like. For a change, open a pull request against `main`. Every commit on `main` reaches plugin users as an update, so merge only what's ready.

## Layout

```text
skills/<name>/
SKILL.md When to use the skill, and its steps
references/ What a step needs, one level deep
LICENSE.txt
shared/ data.md, cli.md, mcp.md and api.md, copied into every skill's references/
evals/ Routing queries and traps
scripts/ check-skills.mjs and test-installs.sh
.claude-plugin/ Claude Code plugin and marketplace
.codex-plugin/ Codex plugin
.agents/plugins/ Codex marketplace
```

## Rules

`scripts/check-skills.mjs` enforces the first three.

- A skill works alone. A single-skill install copies only that skill's folder, so a skill can't link to anything outside it.
- Skills name operations, such as `listings_versions`. Commands, tools and paths live in `shared/`. Edit `shared/`, then run `node scripts/check-skills.mjs --write` to copy it into every skill.
- Each store file starts with `Checked: YYYY-MM-DD against <url>`.
- `SKILL.md` holds the steps. Anything only some runs need goes in `references/`, with a step in `SKILL.md` saying when to read it.
- Every number has a date and a basis, and a report says what it couldn't read. Leave out a claim you can't back.

## Before you open a pull request

```bash
node scripts/check-skills.mjs
```

```bash
scripts/test-installs.sh
```

The second installs the skills into clean folders, as plugins for Claude Code and Codex and through `npx skills` for 11 agents, and checks that each one got all of them. CI runs both. Add `--run` to also ask each signed-in agent on your machine to use a skill, which spends model credits.

If you change a skill's description, run its queries from `evals/confusion-pool.json` with `evals/run_confusion.py`. If you change what a skill does, run its row in `evals/traps.md`.
135 changes: 115 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,143 @@
# plugin-stack
# Plugin Stack: Agent Skills for AI Plugin Builders

**You built the plugin. Now get it picked.**
A collection of agent skills for getting an AI plugin into the stores, found and picked. Built for developers who ship ChatGPT apps, Claude plugins and connectors, Cursor plugins and MCP servers, and want their coding agent to help with submission, listing copy, discoverability, widget design and competitor tracking. Works with Claude Code, Codex, Cursor and any agent that supports [Agent Skills](https://agentskills.io).

Agent skills for AI plugins: ChatGPT apps, Claude plugins and connectors, Cursor plugins and MCP servers. They run on live store data from [OpenApps](https://theopenapps.com), which tracks about 29,000 listings across six stores and the MCP servers they link to, in 48 ChatGPT countries, every day.
Built by [OpenApps](https://theopenapps.com?ref=plugin-stack), which tracks about 29,000 listings across six stores and the MCP servers they link to, in 48 ChatGPT countries, every day. The skills run on that data.

## Install
Found a problem, or want a skill changed? [Open an issue](https://github.com/theopenapps/plugin-stack/issues).

## What are skills?

Skills are folders of instructions that an agent loads when a task calls for them. Once these are installed, ask your agent about your plugin in your own words. It picks the skill, reads live store data, and gives you a report or copy you can paste.

## How the skills work together

Start with `plugin-stack`. It reads your repo and the stores, saves what it learns to `.agents/plugin-stack.md`, and suggests your next steps. The other skills read that file if it's there, and each one also works alone.

```text
┌─────────────────────────────────┐
│ plugin-stack │
│ setup, one finding, next steps │
└────────────────┬────────────────┘
│ .agents/plugin-stack.md
┌──────────────────────┼────────────────────────┐
▼ ▼ ▼
┌────────────────┐ ┌───────────────────────┐ ┌───────────────────┐
│ Ship │ │ Get found │ │ Keep up │
├────────────────┤ ├───────────────────────┤ ├───────────────────┤
│ submit-plugin │ │ optimize-listing │ │ watch-competitors │
│ design-widgets │ │ audit-discoverability │ │ │
└────────────────┘ └───────────────────────┘ └───────────────────┘
```

## Skills

| Skill | What you get |
|---|---|
| [`plugin-stack`](skills/plugin-stack/) | Setup, one finding about your plugin, and your next steps |
| [`submit-plugin`](skills/submit-plugin/) | A readiness check and submission kit for ChatGPT, Claude and Cursor, including the choices you can't change later |
| [`design-widgets`](skills/design-widgets/) | Rendered widgets from live ChatGPT apps, a critique of yours, and a redesign that says which moves have precedent |
| [`optimize-listing`](skills/optimize-listing/) | The words to target, field-by-field verdicts against your competitors, and paste-ready copy within the store's limits |
| [`audit-discoverability`](skills/audit-discoverability/) | Whether your listing and tools answer the prompts you care about, with a labeled simulated pick against competitors |
| [`watch-competitors`](skills/watch-competitors/) | Ranks, featuring, listing rewrites and tool changes side by side, and what changed since your last check |

## Installation

### Any agent: `npx skills` (recommended)

```bash
npx skills add theopenapps/plugin-stack
```

Add `--skill optimize-listing` to install one skill. Each one works alone.
Add `--skill optimize-listing` to install one skill. The CLI finds your agents and asks where to install. Inside an agent session it doesn't ask and writes only to `.agents/skills/`, which Claude Code doesn't read, so name your agent there:

In Claude Code you can also install it as a plugin:
```bash
npx skills add theopenapps/plugin-stack -a claude-code
```

### Claude Code plugin

```text
/plugin marketplace add theopenapps/plugin-stack
/plugin install plugin-stack@theopenapps
```

## Skills
From a shell, use `claude plugin marketplace add theopenapps/plugin-stack`, then `claude plugin install plugin-stack@theopenapps`. To update, run `claude plugin marketplace update theopenapps`, then `claude plugin update plugin-stack@theopenapps`.

Ask your agent what to do next for your plugin. `plugin-stack` reads your repo and the stores, saves what it learns to `.agents/plugin-stack.md`, and suggests two or three next steps. The other skills read that file if it's there.
### Codex plugin

| Skill | What you get |
|---|---|
| `plugin-stack` | Setup, one finding about your plugin, and your next steps |
| `optimize-listing` | The words to target, field-by-field verdicts against your competitors, and paste-ready copy within the store's limits |
| `submit-plugin` | A readiness check and submission kit for ChatGPT, Claude and Cursor, including the choices you can't change later |
| `audit-discoverability` | Whether your listing and tools answer the prompts you care about, with a labeled simulated pick against competitors |
| `design-widgets` | Rendered widgets from live ChatGPT apps, a critique of yours, and a redesign that says which moves have precedent |
| `watch-competitors` | Ranks, featuring, listing rewrites and tool changes side by side, and what changed since your last check |
```bash
codex plugin marketplace add theopenapps/plugin-stack
```

## What they can't tell you
```bash
codex plugin add plugin-stack@theopenapps
```

No study yet shows that listing rewrites move ranks. The discoverability pick is a simulation, not a store's real search. Each report says so, and dates every number.
To update, run `codex plugin marketplace upgrade theopenapps`.

### Copy a folder

Each folder in `skills/` is complete, so you can copy one into your agent's skills folder:

```bash
git clone https://github.com/theopenapps/plugin-stack
```

```bash
cp -r plugin-stack/skills/optimize-listing .claude/skills/
```

### What we've tested

Last run on 2026-11-05 from GitHub.

| Agent | `npx skills` | Plugin |
|---|---|---|
| Claude Code 2.1.303 | Session | Session |
| Codex 0.166.0 | Session | Session |
| Cursor 2026.10.14 | Session | |
| OpenCode, Amp, Gemini CLI, GitHub Copilot, Windsurf, Cline, Goose, Pi | Files in place | |

In a session, the agent found the skill, read its reference file and read OpenApps data. For the others we checked that the files landed and didn't run the agent. `scripts/test-installs.sh --run` repeats this on your machine.

## Usage

Ask in your own words:

```text
"We built the plugin. Now what?"
→ plugin-stack

"How do I get my MCP server listed on the Claude connectors directory?"
→ submit-plugin

"What do the widgets from Trip.com and Kiwi look like inside ChatGPT?"
→ design-widgets

"Is our live Claude plugin's store description good enough?"
→ optimize-listing

"My app has installs but nobody's agent calls it. How do I figure out what's wrong?"
→ audit-discoverability

"Kiwi rewrote their description last week. What did they change?"
→ watch-competitors
```

In Claude Code you can also call one by name: `/watch-competitors`, or `/plugin-stack:watch-competitors` from the plugin.

## Data

Each skill reads OpenApps through the CLI when it has a shell, the MCP server when it doesn't, and the REST API when all it can do is fetch a URL. You don't need an account. Signed out, the skills read the United States and 7 days of history. A free account adds every country, 30 days and more rows per list: run `npx -y @theopenapps/cli@latest auth login`. If a limit cuts something from a report, the report says what's missing.

## Changing the skills
## What they can't tell you

No study yet shows that listing rewrites move ranks. The discoverability pick is a simulation, not a store's real search. Each report says so, and dates every number.

## Contributing

Skills name operations, such as `listings_versions`. Commands, tools and paths live in `shared/`, which every skill carries a copy of, so a single-skill install still works. Edit `shared/`, then run `node scripts/check-skills.mjs --write`.
Fixes and new skills are welcome. [CONTRIBUTING.md](CONTRIBUTING.md) covers how a skill is laid out and how to check it.

## License

Expand Down
5 changes: 5 additions & 0 deletions scripts/check-skills.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ for (const skill of skills) {

if (licenses.size > 1) fail("LICENSE.txt", `differs across skills: ${[...licenses.values()].map((s) => s.join(", ")).join(" | ")}`);

// Claude Code holds back an update until the version changes. With no version, every commit on main is an update.
for (const manifest of [".claude-plugin/plugin.json", ".codex-plugin/plugin.json"]) {
if ("version" in JSON.parse(readFileSync(manifest, "utf8"))) fail(manifest, "has a version");
}

if (errors.length) {
console.error(errors.join("\n"));
process.exit(1);
Expand Down
77 changes: 77 additions & 0 deletions scripts/test-installs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
# Installs the skills the way a user would, into clean folders, and checks that each agent got all of them.
# scripts/test-installs.sh from this checkout
# scripts/test-installs.sh theopenapps/plugin-stack from GitHub
# scripts/test-installs.sh --run [source] then asks each signed-in agent here to use a skill (spends model credits)
set -uo pipefail
cd "$(dirname "$0")/.."
run=; [ "${1:-}" = --run ] && { run=1; shift; }
source=${1:-$PWD}
want=$(ls skills | wc -l | tr -d ' ')
tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT
failed=0

report() { printf '%-5s %s\n' "$1" "$2"; [ "$1" != FAIL ] || failed=1; }
expect() { # label, folder, how many skills
local got; got=$(find "$2" -name SKILL.md 2>/dev/null | wc -l | tr -d ' ')
if [ "$got" = "$3" ]; then report ok "$1: $got skills"; else report FAIL "$1: $got skills, wanted $3"; fi
}
has() { command -v "$1" >/dev/null; }

claude_cache="$tmp/claude/plugins/cache/theopenapps/plugin-stack"
codex_cache="$tmp/codex/plugins/cache/theopenapps/plugin-stack"

if has claude; then
[ "$source" != "$PWD" ] || claude plugin validate "$PWD" >/dev/null 2>&1 || report FAIL "claude plugin validate"
export CLAUDE_CONFIG_DIR="$tmp/claude"
{ claude plugin marketplace add "$source" && claude plugin install plugin-stack@theopenapps; } >"$tmp/claude.log" 2>&1
unset CLAUDE_CONFIG_DIR
expect "Claude Code plugin" "$claude_cache" "$want"
else report skip "Claude Code plugin: no claude here"; fi

if has codex; then
mkdir -p "$tmp/codex"
{ CODEX_HOME="$tmp/codex" codex plugin marketplace add "$source" && CODEX_HOME="$tmp/codex" codex plugin add plugin-stack@theopenapps; } >"$tmp/codex.log" 2>&1
expect "Codex plugin" "$codex_cache" "$want"
else report skip "Codex plugin: no codex here"; fi

for agent in claude-code codex cursor opencode amp gemini-cli github-copilot windsurf cline goose pi; do
mkdir -p "$tmp/skills/$agent"
(cd "$tmp/skills/$agent" && npx -y skills add "$source" -a "$agent" --copy -y) >"$tmp/skills/$agent.log" 2>&1
expect "npx skills -a $agent" "$tmp/skills/$agent" "$want"
done

mkdir -p "$tmp/one"
(cd "$tmp/one" && npx -y skills add "$source" --skill optimize-listing -a claude-code --copy -y) >"$tmp/one.log" 2>&1
expect "npx skills, one skill" "$tmp/one" 1
for file in data cli mcp api; do
[ -f "$tmp/one/.claude/skills/optimize-listing/references/$file.md" ] || report FAIL "one skill: references/$file.md is missing"
done

if [ -n "$run" ]; then
ask='Use the optimize-listing skill. Read its references/data.md, pick an OpenApps surface the way that file says, then read the top 3 apps on the ChatGPT store Featured chart. Reply with exactly three lines: SKILL=<absolute path of the optimize-listing SKILL.md you read>, SURFACE=<cli|mcp|api>, TOP3=<the three app names>.'
session() { # label, folder, command
local label=$1 dir=$2 out; shift 2; out="$tmp/$(echo "$label" | tr ' ' -).txt"
has "$1" || { report skip "$label: no $1 here"; return; }
(cd "$dir" && perl -e 'alarm shift; exec @ARGV' 300 "$@" </dev/null) >"$out" 2>&1
if grep -q "SKILL=.*$(basename "$tmp").*optimize-listing/SKILL.md" "$out" && grep -q 'TOP3=.' "$out"; then
report ok "$label: $(grep 'TOP3=' "$out" | tail -1)"
elif grep -qiE 'billing|log ?in|expired|unauthorized|not authenticated' "$out"; then
report skip "$label: not signed in"
else report FAIL "$label: $(tail -1 "$out" | cut -c1-120)"; fi
}
codex_args=(exec --skip-git-repo-check --dangerously-bypass-approvals-and-sandbox -c model_reasoning_effort=low)
[ -z "${CODEX_MODEL:-}" ] || codex_args+=(-m "$CODEX_MODEL")
mkdir -p "$tmp/w" "$tmp/codex-empty"
ln -sf "$HOME/.codex/auth.json" "$tmp/codex/auth.json"; ln -sf "$HOME/.codex/auth.json" "$tmp/codex-empty/auth.json"
session "Claude Code plugin session" "$tmp/w" claude -p "$ask" --plugin-dir "$(find "$claude_cache" -mindepth 1 -maxdepth 1 -type d | head -1)" --strict-mcp-config --model sonnet --dangerously-skip-permissions &
CODEX_HOME="$tmp/codex" session "Codex plugin session" "$tmp/w" codex "${codex_args[@]}" "$ask" &
session "Claude Code skills session" "$tmp/skills/claude-code" claude -p "$ask" --strict-mcp-config --model sonnet --dangerously-skip-permissions &
CODEX_HOME="$tmp/codex-empty" session "Codex skills session" "$tmp/skills/codex" codex "${codex_args[@]}" "$ask" &
session "Cursor skills session" "$tmp/skills/cursor" cursor-agent -p "$ask" --force --output-format text &
session "OpenCode skills session" "$tmp/skills/opencode" opencode run "$ask" &
session "Amp skills session" "$tmp/skills/amp" amp -x "$ask" --dangerously-allow-all &
wait
fi

[ "$failed" = 0 ] && echo "installs pass" || { echo "installs failed"; exit 1; }
Loading