Package the plugin for Codex from the same bundle

The Claude Code plugin moves to plugins/haiku-rag/ and gains a Codex
manifest at .codex-plugin/plugin.json; both share one .mcp.json and one
haiku-rag Agent Skill. .agents/plugins/marketplace.json serves Codex, the
Claude marketplace points at the new path, and scripts/bump_version.py
bumps both manifests. The skill declares its compatibility.
This commit is contained in:
Yiorgis Gozadinos 2026-09-07 11:21:25 +03:00
parent 110adf23ee
commit d7c131bc28
No known key found for this signature in database
12 changed files with 131 additions and 35 deletions

View file

@ -0,0 +1,20 @@
{
"name": "haiku-rag",
"interface": {
"displayName": "haiku.rag"
},
"plugins": [
{
"name": "haiku-rag",
"source": {
"source": "local",
"path": "./plugins/haiku-rag"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Productivity"
}
]
}

View file

@ -7,8 +7,8 @@
"plugins": [
{
"name": "haiku-rag",
"source": "./claude-plugin",
"description": "Search, read and question your haiku.rag knowledge base from Claude Code."
"source": "./plugins/haiku-rag",
"description": "Search, read and analyze your haiku.rag knowledge base from Claude Code."
}
]
}

View file

@ -4,9 +4,8 @@
### Added
- Claude Code plugin under `claude-plugin/`: the server configuration and the
`haiku-rag` skill. `claude plugin marketplace add ggozad/haiku.rag`, then
`claude plugin install haiku-rag`.
- Claude Code and Codex plugin under `plugins/haiku-rag/`: two client manifests
sharing the server configuration and the `haiku-rag` Agent Skill.
- MCP tool `execute_code(code, filter, sources)`: runs a program in the
analysis sandbox over the selected documents and returns what it printed;
one sandbox per call.

View file

@ -110,7 +110,7 @@ For direct agent composition, see the [capabilities documentation](https://ggoza
## MCP Server
Use with AI assistants like Claude Code and Claude Desktop:
Use with AI assistants like Claude Code, Codex, and Claude Desktop:
```bash
haiku-rag mcp --stdio
@ -123,6 +123,13 @@ claude plugin marketplace add ggozad/haiku.rag
claude plugin install haiku-rag
```
In Codex, install the same plugin from its marketplace:
```bash
codex plugin marketplace add ggozad/haiku.rag
codex plugin add haiku-rag@haiku-rag
```
Add to your Claude Desktop configuration:
```json
@ -136,7 +143,7 @@ Add to your Claude Desktop configuration:
}
```
Provides search, document, QA, and analysis tools directly in your AI assistant.
Provides search, document reading, and analysis tools directly in your AI assistant.
## Examples

View file

@ -57,10 +57,34 @@ plugin:
claude mcp add haiku-rag -- haiku-rag mcp --stdio
```
The skill works with that registration too: copy `claude-plugin/skills/haiku-rag`
The skill works with that registration too: copy `plugins/haiku-rag/skills/haiku-rag`
into `~/.claude/skills/` and change the tool prefix in its `allowed-tools` from
`mcp__plugin_haiku-rag_haiku-rag__` to `mcp__haiku-rag__`.
## Codex
The repository's Codex plugin registers the server and installs the same Agent
Skill:
```bash
codex plugin marketplace add ggozad/haiku.rag
codex plugin add haiku-rag@haiku-rag
```
The plugin runs `haiku-rag mcp --stdio`, so `haiku-rag` must be on the PATH.
Invoke the skill as `$haiku-rag`. Codex can also select it automatically from
its description. To register the server without the plugin:
```bash
codex mcp add haiku-rag -- haiku-rag mcp --stdio
```
The skill works with that registration too: copy
`plugins/haiku-rag/skills/haiku-rag` into `~/.agents/skills/`.
The `allowed-tools` field supplies Claude Code's tool pre-approval and may be
ignored by other Agent Skills clients. Codex configures MCP tool approvals
separately in `config.toml`.
## Claude Desktop Integration
Add to your Claude Desktop configuration (`claude_desktop_config.json`):
@ -168,8 +192,8 @@ traceback goes to the server log.
The server publishes `instructions` describing the knowledge base: what it
holds, when to reach for it, the collection names when it covers several, and
`prompts.domain_preamble` when set. Claude Code shows them to the model. Claude
Desktop does not, so every tool description stands on its own.
`prompts.domain_preamble` when set. Claude Code and Codex show them to the
model. Claude Desktop does not, so every tool description stands on its own.
## Continuous ingestion

View file

@ -1,7 +1,7 @@
{
"name": "haiku-rag",
"version": "0.82.1",
"description": "Search, read and question your haiku.rag knowledge base from Claude Code.",
"description": "Search, read and analyze your haiku.rag knowledge base from Claude Code.",
"author": {
"name": "Yiorgis Gozadinos",
"email": "ggozadinos@gmail.com"

View file

@ -0,0 +1,26 @@
{
"name": "haiku-rag",
"version": "0.82.1",
"description": "Search, read and analyze your haiku.rag knowledge base from Codex.",
"author": {
"name": "Yiorgis Gozadinos",
"email": "ggozadinos@gmail.com",
"url": "https://github.com/ggozad"
},
"homepage": "https://ggozad.github.io/haiku.rag/mcp/",
"repository": "https://github.com/ggozad/haiku.rag",
"license": "MIT",
"keywords": ["rag", "knowledge-base", "search", "documents", "mcp"],
"skills": "./skills/",
"mcpServers": "./.mcp.json",
"interface": {
"displayName": "haiku.rag",
"shortDescription": "Search and analyze your haiku.rag knowledge base",
"longDescription": "Search, read, and compute over documents in your local haiku.rag knowledge base through MCP tools.",
"developerName": "Yiorgis Gozadinos",
"category": "Productivity",
"capabilities": ["Interactive", "Read"],
"websiteURL": "https://ggozad.github.io/haiku.rag/",
"defaultPrompt": "Search my haiku.rag knowledge base and cite the relevant documents."
}
}

View file

@ -5,6 +5,7 @@ description: Search, read and compute over the user's haiku.rag knowledge base
from the user's ingested documents, when asked to find, look up, check or
cite something in their documents or knowledge base, or when the question is
about the user's own material rather than general knowledge.
compatibility: Requires the haiku-rag MCP server to be registered in the client.
allowed-tools:
- mcp__plugin_haiku-rag_haiku-rag__search_documents
- mcp__plugin_haiku-rag_haiku-rag__search_documents_by_image

View file

@ -2,8 +2,8 @@
"""
Version bumping script for haiku.rag workspace.
Updates version in all pyproject.toml files, the Claude Code plugin manifest
and CHANGELOG.md.
Updates version in all pyproject.toml files, both plugin manifests, and
CHANGELOG.md.
"""
import re
@ -56,7 +56,7 @@ def update_example_dependencies(file_path: Path, new_version: str) -> None:
def update_plugin_version(file_path: Path, new_version: str) -> None:
"""Update the version in the Claude Code plugin manifest."""
"""Update the version in a plugin manifest."""
content = file_path.read_text()
updated = re.sub(
r'^(\s*"version": )"[^"]+"',
@ -136,12 +136,15 @@ def main():
root / "app" / "backend" / "pyproject.toml",
]
plugin_file = root / "claude-plugin" / ".claude-plugin" / "plugin.json"
plugin_files = [
root / "plugins" / "haiku-rag" / ".claude-plugin" / "plugin.json",
root / "plugins" / "haiku-rag" / ".codex-plugin" / "plugin.json",
]
changelog_file = root / "CHANGELOG.md"
# Check all files exist
for file in (
pyproject_files + example_pyproject_files + [plugin_file, changelog_file]
pyproject_files + example_pyproject_files + plugin_files + [changelog_file]
):
if not file.exists():
print(f"Error: {file} not found")
@ -172,7 +175,8 @@ def main():
for file in example_pyproject_files:
update_example_dependencies(file, new_version)
update_plugin_version(plugin_file, new_version)
for file in plugin_files:
update_plugin_version(file, new_version)
# Update CHANGELOG.md
update_changelog(changelog_file, new_version)

View file

@ -27,12 +27,15 @@ def test_update_plugin_version_rewrites_only_the_version_field(tmp_path, monkeyp
assert manifest.read_text().endswith("}\n")
def test_the_shipped_plugin_manifest_carries_the_package_version():
def test_the_shipped_plugin_manifests_carry_the_package_version():
root = Path(__file__).resolve().parents[1]
plugin = json.loads(
(root / "claude-plugin" / ".claude-plugin" / "plugin.json").read_text()
)
assert plugin["version"] == bump_version.get_current_version(
package_version = bump_version.get_current_version(
root / "haiku_rag_slim" / "pyproject.toml"
)
for client in ("claude", "codex"):
manifest = json.loads(
(
root / "plugins" / "haiku-rag" / f".{client}-plugin" / "plugin.json"
).read_text()
)
assert manifest["version"] == package_version

View file

@ -1158,27 +1158,39 @@ class TestMCPErrorContract:
)
class TestClaudeCodePlugin:
"""The plugin under claude-plugin/ points at the server this module builds."""
class TestAgentPlugins:
"""The shared Claude Code and Codex plugin points at this MCP server."""
root = Path(__file__).resolve().parents[1]
def test_the_manifests_name_the_plugin_and_its_server(self):
import json
plugin = json.loads(
(self.root / "claude-plugin/.claude-plugin/plugin.json").read_text()
claude_plugin = json.loads(
(self.root / "plugins/haiku-rag/.claude-plugin/plugin.json").read_text()
)
marketplace = json.loads(
codex_plugin = json.loads(
(self.root / "plugins/haiku-rag/.codex-plugin/plugin.json").read_text()
)
claude_marketplace = json.loads(
(self.root / ".claude-plugin/marketplace.json").read_text()
)
servers = json.loads((self.root / "claude-plugin/.mcp.json").read_text())
codex_marketplace = json.loads(
(self.root / ".agents/plugins/marketplace.json").read_text()
)
servers = json.loads((self.root / "plugins/haiku-rag/.mcp.json").read_text())
assert plugin["name"] == "haiku-rag"
assert plugin["description"]
[entry] = marketplace["plugins"]
assert entry["name"] == plugin["name"]
assert entry["source"] == "./claude-plugin"
assert claude_plugin["name"] == codex_plugin["name"] == "haiku-rag"
assert claude_plugin["description"]
assert codex_plugin["description"]
assert codex_plugin["skills"] == "./skills/"
assert codex_plugin["mcpServers"] == "./.mcp.json"
[claude_entry] = claude_marketplace["plugins"]
assert claude_entry["name"] == claude_plugin["name"]
assert claude_entry["source"] == "./plugins/haiku-rag"
[codex_entry] = codex_marketplace["plugins"]
assert codex_entry["name"] == codex_plugin["name"]
assert codex_entry["source"]["path"] == "./plugins/haiku-rag"
assert servers["mcpServers"]["haiku-rag"]["args"] == ["mcp", "--stdio"]
@pytest.mark.asyncio
@ -1187,7 +1199,7 @@ class TestClaudeCodePlugin:
):
import yaml
text = (self.root / "claude-plugin/skills/haiku-rag/SKILL.md").read_text()
text = (self.root / "plugins/haiku-rag/skills/haiku-rag/SKILL.md").read_text()
_, frontmatter, _ = text.split("---", 2)
skill = yaml.safe_load(frontmatter)
prefix = "mcp__plugin_haiku-rag_haiku-rag__"