Skip to content

Commit 22b5485

Browse files
authored
fix: various multi-host fixes (#10)
## Summary Brings the plugin in line with where each host actually is today (Claude Code, Codex CLI 0.125+, Cursor, Gemini CLI, OpenCode), and starts pushing the install path through native marketplace commands instead of shell scripts. ## What's in here **Hooks** The session-start hook used to be a single bash script that printed a static banner. It's now split into a small detection library (`hooks/lib/`) and a thin emitter, with PowerShell and Node ports so the same logic runs on every host. The detector scans the project for signals (`pyproject.toml` deps, Python imports, file globs) and injects targeted reminders to use the relevant skill — `litestar`, `sqlspec`, `advanced-alchemy`, `litestar-saq`, etc. The matcher map is data-only (`hooks/lib/skill-map.json`) so new skills just add an entry. `LITESTAR_SKILLS_HOOK_DISABLE=1` opts out. **Codex plugin layout** Codex 0.125 stopped accepting `source.path: \"./\"` for a marketplace, so the plugin moved to a nested layout under `.agents/plugins/plugins/litestar-skills/`. Marketplace config lives at `.agents/plugins/marketplace.json`. `.gitignore` got a narrow carve-out so just those two paths are tracked — the rest of `.agents/` stays authoring-only. **Subagents** The four host-specific dialects (Claude / Codex / Gemini / OpenCode) of `litestar-reviewer` were drifting whenever one was edited. They're now generated from a single YAML source via `tools/generate-agents.py`, with a CI check that fails on hand-edits. To change the agent: edit the YAML, run `make agents`, commit both. **OpenCode plugin** The plugin file used to be an empty stub. It's now a real plugin that injects skill reminders into the system prompt via `experimental.chat.system.transform` and respects `managedConfig.disabledPlugins` / `allowedPlugins` so org policy actually takes effect. **Policy & install docs** Added `docs/policy.md` covering per-host allow/ask/deny grammar and managed-settings paths, plus a drop-in `templates/managed-settings/claude-code.json`. Gemini's `excludeTools` now ships opinionated defaults blocking `sudo`, `rm -rf /*`, and `* | sh`/`* | bash` patterns. Each host got its own `INSTALL.md` and the README has a real support-tier matrix. **Validation** The Claude `userConfig` schema is now enforced (camelCase keys, valid types, required `title`); deprecated `decision: approve|block` and undocumented `${CLAUDE_PLUGIN_DATA}` are rejected. `make check` validates the four hook manifests, mirrors flow's `validate-codex-manifest.py`, and the agent generator drift gate. **Distribution** `docs/submission-tracker.md` lays out a 3-phase rollout (zero-gatekeeper → form/PR → async/blocked) so submissions to skills.sh, the Anthropic community directory, openai/skills, and the various awesome-* lists are tracked rather than ad-hoc. README points users at `npx skills add litestar-org/litestar-skills`. ## Verification - `make check` is clean (lint, type-check, 100 tests, manifest sync at 0.1.2 across 13 files, agent drift gate, Codex manifest validator). - `claude plugin tag .claude-plugin --dry-run` accepts the manifest. - `codex plugin marketplace add .` accepts the marketplace. - Reference: same-author multi-host plugin at `~/code/c/flow` learned a lot of these things the hard way; this PR ports the patterns over.
1 parent d36a4df commit 22b5485

54 files changed

Lines changed: 3785 additions & 257 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/plugins/marketplace.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "litestar-marketplace",
3+
"interface": {
4+
"displayName": "Litestar Skills",
5+
"shortDescription": "First-party Litestar framework skills: handlers, DTOs, DI, plugins, MCP, SAQ, Granian, SQLSpec, Advanced Alchemy"
6+
},
7+
"metadata": {
8+
"description": "The Litestar Marketplace — opinionated first-party skills, plugins, subagents, commands, and MCP servers for Litestar and its ecosystem"
9+
},
10+
"owner": {
11+
"name": "litestar-org",
12+
"url": "https://github.com/litestar-org"
13+
},
14+
"plugins": [
15+
{
16+
"name": "litestar-skills",
17+
"version": "0.1.2",
18+
"description": "Opinionated first-party agent skills, plugins, subagents, slash commands, and MCP servers for the Litestar framework ecosystem",
19+
"source": { "source": "local", "path": "./plugins/litestar-skills" },
20+
"policy": { "installation": "AVAILABLE" },
21+
"category": "Development",
22+
"author": {
23+
"name": "Cody Fincher",
24+
"email": "cofin@litestar.dev"
25+
},
26+
"homepage": "https://github.com/litestar-org/litestar-skills",
27+
"license": "MIT",
28+
"keywords": ["litestar", "skills", "sqlspec", "advanced-alchemy", "msgspec", "dishka", "granian", "saq", "vite", "mcp"]
29+
}
30+
]
31+
}

.codex-plugin/plugin.json renamed to .agents/plugins/plugins/litestar-skills/.codex-plugin/plugin.json

File renamed without changes.

.claude-plugin/INSTALL.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Claude Code — Install
2+
3+
## One-line install (recommended)
4+
5+
Inside a Claude Code session:
6+
7+
```text
8+
/plugin marketplace add litestar-org/litestar-skills
9+
/plugin install litestar-skills@litestar-marketplace
10+
```
11+
12+
The `/plugin` commands run **inside** Claude Code — they cannot be automated from a shell. Claude prompts for any `userConfig` values on first install (none today), then enables the plugin.
13+
14+
## What gets loaded
15+
16+
| Path | Loaded by Claude as |
17+
| --- | --- |
18+
| `skills/<name>/SKILL.md` | Skills (description-based auto-activation) |
19+
| `commands/<prefix>/<command>.toml` | Slash commands (e.g. `/litestar:new-app`) |
20+
| `.claude-plugin/agents/<name>.md` | Subagents (PascalCase tool list dialect) |
21+
| `hooks/hooks-claude.json``hooks/session-start.sh` | SessionStart hook injecting Litestar skill reminders |
22+
23+
## Project-local install (less common)
24+
25+
If you want the plugin scoped to one project rather than your user directory:
26+
27+
```bash
28+
git clone https://github.com/litestar-org/litestar-skills .claude-plugin-local
29+
```
30+
31+
Then in `.claude/settings.json`:
32+
33+
```json
34+
{
35+
"plugins": [".claude-plugin-local"]
36+
}
37+
```
38+
39+
Most users should prefer the marketplace install — Claude handles updates, version pinning, and `userConfig` migration for you.
40+
41+
## Updating
42+
43+
```text
44+
/plugin marketplace update litestar-marketplace
45+
/plugin update litestar-skills@litestar-marketplace
46+
```
47+
48+
## Verifying the install
49+
50+
In a Claude Code session:
51+
52+
```text
53+
/status
54+
```
55+
56+
Should show `litestar-skills` under enabled plugins. Open a Litestar project (one with `litestar` in `pyproject.toml`) and a fresh session — the SessionStart hook injects a context paragraph naming `litestar-skills:litestar` (plus any other detected skills).
57+
58+
## Restricting capabilities
59+
60+
Claude Code is the only host with a mature allow/ask/deny grammar. See [`docs/policy.md`](../docs/policy.md) for per-rule syntax and managed-settings paths. A drop-in template for org-managed installs lives at [`templates/managed-settings/claude-code.json`](../templates/managed-settings/claude-code.json).
61+
62+
To deny a specific litestar-skills skill:
63+
64+
```json
65+
{
66+
"permissions": {
67+
"deny": ["Skill(litestar-skills:litestar-deployment)"]
68+
}
69+
}
70+
```
71+
72+
## Troubleshooting
73+
74+
- **`Unrecognized key: "displayName"`** — your local clone is on a stale revision. Pull main; root-level `displayName` was removed (it's only valid inside marketplace.json plugin entries, not in plugin.json).
75+
- **Hook doesn't inject context** — ensure the session was started in a directory containing `pyproject.toml`. The hook short-circuits silently when no Litestar-ecosystem signals are detected. Override with `LITESTAR_SKILLS_HOOK_DISABLE=1` to confirm the hook is firing.
76+
- **`/status` doesn't show the plugin**`/plugin marketplace add` and `/plugin install` are separate steps. Run both.

.claude-plugin/marketplace.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"homepage": "https://github.com/litestar-org/litestar-skills",
2121
"license": "MIT",
2222
"keywords": ["litestar", "skills", "sqlspec", "advanced-alchemy", "msgspec", "dishka", "granian", "saq", "vite", "mcp"],
23-
"category": "Development"
23+
"category": "Development",
24+
"policy": { "installation": "AVAILABLE" }
2425
}
2526
]
2627
}

.claude-plugin/plugin.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{
22
"name": "litestar-skills",
3-
"displayName": "Litestar Skills",
43
"description": "Opinionated first-party agent skills, plugins, subagents, slash commands, and MCP servers for the Litestar framework ecosystem",
54
"version": "0.1.2",
65
"author": {
@@ -13,5 +12,6 @@
1312
"keywords": ["litestar", "skills", "agent", "ai", "sqlspec", "advanced-alchemy", "granian", "msgspec", "dishka", "mcp"],
1413
"skills": ["./skills/"],
1514
"commands": ["./commands/"],
16-
"agents": ["./.claude-plugin/agents/litestar-reviewer.md"]
15+
"agents": ["./.claude-plugin/agents/litestar-reviewer.md"],
16+
"hooks": "./hooks/hooks-claude.json"
1717
}

.codex/INSTALL.md

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,69 @@
11
# Codex CLI — Install
22

3-
Three install paths.
3+
> **Codex CLI 0.125+** is required. Earlier versions accept the `source: "./"` marketplace shape but newer versions reject it.
44
5-
## Option 1: User-level plugin (recommended)
5+
## Option 1: Marketplace install (recommended)
66

77
```bash
8-
git clone https://github.com/litestar-org/litestar-skills ~/.codex/plugins/litestar-skills
8+
codex plugin marketplace add litestar-org/litestar-skills
9+
```
10+
11+
Then in a Codex session:
12+
13+
```text
14+
/plugins
915
```
1016

11-
Codex auto-discovers plugins in `~/.codex/plugins/`. The clone includes `.codex/agents/litestar-reviewer.toml` (pure-TOML custom agent; tools inherited from your session `config.toml`).
17+
Enable `litestar-skills` from the list. The plugin manifest lives at `.agents/plugins/plugins/litestar-skills/.codex-plugin/plugin.json`; the marketplace catalog is `.agents/plugins/marketplace.json` at the repo root.
1218

13-
## Option 2: Repo-scoped marketplace (team)
19+
## Option 2: Local marketplace (for development)
1420

15-
For a single project, clone into the repo's `plugins/` directory and register a local marketplace:
21+
If you have the repo cloned and want Codex to pick up your local changes:
1622

1723
```bash
18-
mkdir -p plugins
19-
git clone https://github.com/litestar-org/litestar-skills plugins/litestar-skills
20-
mkdir -p .codex
21-
cat > .codex/marketplace.json <<'EOF'
22-
{
23-
"name": "litestar-marketplace",
24-
"description": "The Litestar Marketplace — opinionated first-party skills, plugins, subagents, commands, and MCP servers for Litestar and its ecosystem",
25-
"plugins": [
26-
{ "name": "litestar-skills", "source": "./plugins/litestar-skills" }
27-
]
28-
}
29-
EOF
24+
codex plugin marketplace add /path/to/litestar-skills
3025
```
3126

32-
## Option 3: Skills-only via `.agents/skills/`
27+
Codex auto-discovers the marketplace at `.agents/plugins/marketplace.json` and the nested plugin under `.agents/plugins/plugins/litestar-skills/`.
3328

34-
If you only want the skills (no plugin metadata or custom agent), clone and copy:
29+
## Option 3: User-level clone (legacy fallback)
3530

3631
```bash
37-
git clone --depth 1 https://github.com/litestar-org/litestar-skills /tmp/litestar-skills
38-
mkdir -p .agents/skills
39-
cp -r /tmp/litestar-skills/skills/* .agents/skills/
32+
git clone https://github.com/litestar-org/litestar-skills ~/.codex/plugins/litestar-skills
4033
```
4134

42-
Codex reads `.agents/skills/` natively at session start.
35+
Codex auto-discovers plugins under `~/.codex/plugins/`. Provided as a fallback for environments where `codex plugin marketplace add` is unavailable.
4336

4437
## Custom agents
4538

46-
Codex custom agents live in `.codex/agents/*.toml` and are discovered automatically when the plugin is installed. The repo ships `litestar-reviewer` — invoke with `$agent litestar-reviewer` inside Codex.
39+
Codex custom agents live in `.codex/agents/*.toml` (pure TOML; tools inherited from session `config.toml`). The repo ships `litestar-reviewer` — invoke with `$agent litestar-reviewer` inside Codex.
40+
41+
The four host-dialect agent files are generated from canonical YAML sources at `tools/agent-sources/<name>.yaml`. Run `make agents` after editing the source.
4742

4843
## Updating
4944

45+
```bash
46+
codex plugin marketplace upgrade litestar-marketplace
47+
```
48+
49+
For Option 3:
50+
5051
```bash
5152
cd ~/.codex/plugins/litestar-skills && git pull
5253
```
5354

5455
## Verification
5556

56-
Inside Codex:
57+
In a Codex session:
5758

5859
```text
5960
$skill list | grep litestar
6061
$agent list | grep litestar-reviewer
6162
```
6263

63-
## Disabling Specific Skills
64+
The SessionStart hook (`hooks/session-start.sh`, dispatched via `hooks/hooks-codex.json`) should inject project-aware Litestar skill reminders into the session context.
65+
66+
## Disabling specific skills
6467

6568
Edit `~/.codex/config.toml`:
6669

@@ -69,3 +72,13 @@ Edit `~/.codex/config.toml`:
6972
path = "/path/to/skill/SKILL.md"
7073
enabled = false
7174
```
75+
76+
## Why the nested layout
77+
78+
Codex CLI 0.125.0+ requires local marketplace `source.path` to:
79+
80+
1. Start with `./`
81+
2. Be a non-empty subdirectory (rejects `./` alone)
82+
3. Contain no `..` traversal
83+
84+
That's why the plugin lives under `./plugins/litestar-skills` (relative to `.agents/plugins/marketplace.json`) instead of being collocated with the marketplace file.

.cursor-plugin/INSTALL.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Cursor — Install
2+
3+
## In-editor install
4+
5+
Inside Cursor:
6+
7+
```text
8+
/add-plugin
9+
```
10+
11+
Then search for `litestar-skills` (once the plugin is approved on the Cursor marketplace — submission is pending; see [docs/roadmap.md](../docs/roadmap.md)).
12+
13+
## Local-development install
14+
15+
Until the plugin lands on the Cursor marketplace, install it locally:
16+
17+
```bash
18+
mkdir -p ~/.cursor/plugins/local
19+
ln -s "$(pwd)" ~/.cursor/plugins/local/litestar-skills
20+
```
21+
22+
Then restart Cursor. The plugin manifest at `.cursor-plugin/plugin.json` is auto-discovered.
23+
24+
## What gets loaded
25+
26+
| Path | Loaded by Cursor as |
27+
| --- | --- |
28+
| `skills/<name>/SKILL.md` | Skills (agentskills.io standard, adopted in Cursor 2.4+) |
29+
| `commands/<prefix>/<command>.toml` | Slash commands |
30+
| `hooks/hooks-cursor.json``hooks/session-start.sh` | SessionStart hook |
31+
32+
## Updating
33+
34+
If installed via the local symlink, just `git pull` in the cloned directory:
35+
36+
```bash
37+
cd ~/.cursor/plugins/local/litestar-skills && git pull
38+
```
39+
40+
## Restricting capabilities
41+
42+
Cursor has no documented public deny grammar today. Restriction options:
43+
44+
- Uninstall via `/remove-plugin`
45+
- Team / Enterprise plans support private team marketplaces with central governance — talk to your Cursor admin
46+
47+
See [`docs/policy.md`](../docs/policy.md) for the full per-host reference.
48+
49+
## Troubleshooting
50+
51+
- **Plugin not discovered** — ensure the symlink target points at the repo root (the directory containing `.cursor-plugin/plugin.json`), not at `.cursor-plugin/` itself.
52+
- **Hook doesn't fire** — Cursor's SessionStart hook reads `./hooks/hooks-cursor.json`; ensure the `hooks` field in `.cursor-plugin/plugin.json` is present (it is, as of v0.2).

.cursor-plugin/plugin.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,10 @@
1313
"keywords": ["litestar", "skills", "agent", "ai", "sqlspec", "advanced-alchemy"],
1414
"skills": "./skills/",
1515
"commands": "./commands/",
16-
"hooks": "./hooks/hooks-cursor.json"
16+
"hooks": "./hooks/hooks-cursor.json",
17+
"interface": {
18+
"displayName": "Litestar Skills",
19+
"shortDescription": "First-party Litestar framework skills: handlers, DTOs, DI, plugins, MCP, SAQ, Granian, SQLSpec, Advanced Alchemy",
20+
"category": "Development"
21+
}
1722
}

.gemini/INSTALL.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Gemini CLI — Install
2+
3+
## One-line install (recommended)
4+
5+
```bash
6+
gemini extensions install https://github.com/litestar-org/litestar-skills --auto-update
7+
```
8+
9+
Gemini auto-indexes the repo into its [extension gallery](https://geminicli.com/extensions/) via the `gemini-cli-extension` GitHub topic.
10+
11+
## What gets loaded
12+
13+
| Path | Loaded by Gemini as |
14+
| --- | --- |
15+
| `skills/<name>/SKILL.md` | Skills (agentskills.io standard) |
16+
| `commands/<prefix>/<command>.toml` | Slash commands |
17+
| `agents/<name>.md` | Subagents (YAML-list tool dialect) |
18+
| `hooks/hooks.json` (auto-discovered) → `hooks/session-start.sh` | SessionStart hook |
19+
| `gemini-extension.json` `excludeTools` | Tool denylist (sudo, rm -rf, curl\|sh, etc.) |
20+
| `GEMINI.md` | Per-host context file (auto-loaded) |
21+
22+
The hook manifest at `hooks/hooks.json` uses Gemini's `${extensionPath}${/}` substitution + a `bun || node || bash` multi-runtime fallback so the SessionStart hook works on any machine.
23+
24+
## Updating
25+
26+
```bash
27+
gemini extensions update litestar-skills
28+
```
29+
30+
## Verifying the install
31+
32+
In a Gemini session, ask: *"What Litestar skills are available?"* — Gemini should list the skills loaded from this extension. Or check the gallery:
33+
34+
```bash
35+
gemini extensions list | grep litestar
36+
```
37+
38+
## Restricting capabilities
39+
40+
Gemini ships a denylist via `excludeTools` in `gemini-extension.json`. The repo ships defenses against:
41+
42+
- All `sudo` invocations (bare, prefixed, semicolon-chained, embedded, piped)
43+
- Destructive `rm -rf` against root, `$HOME`, and `~`
44+
- `curl | sh`, `wget | sh`, and any `*|sh`/`*|bash` install-script piping
45+
46+
See [`docs/policy.md`](../docs/policy.md) for the full pattern list.
47+
48+
**Caveat:** Gemini's `excludeTools` does NOT apply to MCP servers bundled with the extension itself ([gemini-cli #8481](https://github.com/google-gemini/gemini-cli/issues/8481)). Treat it as belt-and-suspenders, not a hard guarantee.
49+
50+
## Troubleshooting
51+
52+
- **`gemini extensions install` fails on a fresh clone** — the manifest is `gemini-extension.json` at the repo root (sibling of `README.md`). If the file isn't present, your checkout may be on a pre-Gemini-support revision.
53+
- **Hook doesn't inject context** — the hook short-circuits silently when no Litestar-ecosystem signals are detected in the cwd. Override with `LITESTAR_SKILLS_HOOK_DISABLE=1` to confirm it's firing. The hook scans `pyproject.toml`, Python imports (capped), and a curated set of file globs (`Dockerfile`, `Chart.yaml`, `*.tf`, etc.).
54+
- **`bun`/`node` not found at hook time** — the multi-runtime fallback ends with `bash` so a system without Bun or Node still gets the hook.

.gitignore

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,16 @@ build/
3232
# Host-local settings (may contain per-machine paths or tokens)
3333
.claude/settings.local.json
3434

35-
# Flow framework state — local-only per project convention
36-
.agents/
35+
# Flow framework state — local-only per project convention.
36+
# `.agents/*` (NOT `.agents/`) so git descends into the directory and honors
37+
# the `!`-includes below. Codex CLI 0.125+ requires the marketplace file at
38+
# `.agents/plugins/marketplace.json` AND a nested plugin subtree at
39+
# `.agents/plugins/plugins/<name>/` (because Codex rejects `source.path: "./"`).
40+
.agents/*
41+
!.agents/plugins/
42+
.agents/plugins/*
43+
!.agents/plugins/marketplace.json
44+
!.agents/plugins/plugins/
3745
.beads/
3846

3947
# Beads / Dolt internals

0 commit comments

Comments
 (0)