feat: add Codex plugin support with hooks, commands, and documentation

This commit is contained in:
Tal Muskal
2026-04-08 19:10:44 +03:00
parent 61924ea018
commit 50c3db383a
17 changed files with 320 additions and 7 deletions
+20
View File
@@ -0,0 +1,20 @@
{
"name": "mempalace",
"interface": {
"displayName": "MemPalace"
},
"plugins": [
{
"name": "mempalace",
"source": {
"source": "local",
"path": "./.codex-plugin"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "NONE"
},
"category": "Coding"
}
]
}
+9
View File
@@ -0,0 +1,9 @@
{
"mempalace": {
"command": "python3",
"args": [
"-m",
"mempalace.mcp_server"
]
}
}
+1 -1
View File
@@ -7,7 +7,7 @@
},
"license": "MIT",
"commands": [],
"mcp": {
"mcpServers": {
"mempalace": {
"command": "python3",
"args": [
+73
View File
@@ -0,0 +1,73 @@
# MemPalace - Codex CLI Plugin
Give your AI a persistent memory -- mine projects and conversations into a searchable palace backed by ChromaDB, with 19 MCP tools, auto-save hooks, and guided skills.
## Prerequisites
- Python 3.10+
- Codex CLI installed and configured
- `pip install mempalace`
## Installation
### Local Install
1. Copy or symlink the `.codex-plugin` directory into your project root:
```bash
cp -r .codex-plugin /path/to/your/project/.codex-plugin
```
2. Verify the plugin is detected:
```bash
codex --plugins
```
3. Initialize your palace:
```bash
codex /init
```
### Git Install
1. Clone the MemPalace repository:
```bash
git clone https://github.com/milla-jovovich/mempalace.git
cd mempalace
```
2. Install the Python package:
```bash
pip install -e .
```
3. The `.codex-plugin` directory is already in the repo root. Codex CLI will detect it automatically when you run Codex from inside the repository.
4. Initialize your palace:
```bash
codex /init
```
## Available Skills
| Skill | Description |
|-------|-------------|
| `/help` | Show available commands and usage tips |
| `/init` | Initialize a new memory palace |
| `/search` | Semantic search across all mined memories |
| `/mine` | Mine a project or conversation into your palace |
| `/status` | Show palace status, room counts, and health |
## Hooks
The plugin includes an auto-save hook that runs on session stop, automatically preserving conversation context into your palace.
## Support
- Repository: https://github.com/milla-jovovich/mempalace
- Issues: https://github.com/milla-jovovich/mempalace/issues
+37
View File
@@ -0,0 +1,37 @@
{
"hooks": {
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "./hooks/mempal-session-start-hook.sh"
}
]
}
],
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "./hooks/mempal-stop-hook.sh"
}
]
}
],
"PreCompact": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "./hooks/mempal-precompact-hook.sh"
}
]
}
]
}
}
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail
PLUGIN_ROOT="${CODEX_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
# Capture stdin (hook input from Codex)
INPUT_FILE=$(mktemp 2>/dev/null || echo "/tmp/mempal-precompact-hook-$$.json")
cat > "$INPUT_FILE"
# Pipe to Python CLI with codex harness
cat "$INPUT_FILE" | python3 -m mempalace hook run --hook precompact --harness codex
EXIT_CODE=$?
# Cleanup
rm -f "$INPUT_FILE" 2>/dev/null
exit $EXIT_CODE
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail
PLUGIN_ROOT="${CODEX_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
# Capture stdin (hook input from Codex)
INPUT_FILE=$(mktemp 2>/dev/null || echo "/tmp/mempal-session-start-hook-$$.json")
cat > "$INPUT_FILE"
# Pipe to Python CLI with codex harness
cat "$INPUT_FILE" | python3 -m mempalace hook run --hook session-start --harness codex
EXIT_CODE=$?
# Cleanup
rm -f "$INPUT_FILE" 2>/dev/null
exit $EXIT_CODE
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail
PLUGIN_ROOT="${CODEX_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
# Capture stdin (hook input from Codex)
INPUT_FILE=$(mktemp 2>/dev/null || echo "/tmp/mempal-stop-hook-$$.json")
cat > "$INPUT_FILE"
# Pipe to Python CLI with codex harness
cat "$INPUT_FILE" | python3 -m mempalace hook run --hook stop --harness codex
EXIT_CODE=$?
# Cleanup
rm -f "$INPUT_FILE" 2>/dev/null
exit $EXIT_CODE
+35
View File
@@ -0,0 +1,35 @@
{
"name": "mempalace",
"version": "3.0.0",
"description": "Give your AI a memory — mine projects and conversations into a searchable palace. 19 MCP tools, auto-save hooks, and guided setup.",
"author": { "name": "milla-jovovich" },
"homepage": "https://github.com/milla-jovovich/mempalace",
"repository": "https://github.com/milla-jovovich/mempalace",
"license": "MIT",
"keywords": ["memory", "ai", "rag", "mcp", "chromadb", "palace", "search"],
"skills": "./skills/",
"hooks": "./hooks.json",
"mcpServers": {
"mempalace": {
"command": "python3",
"args": ["-m", "mempalace.mcp_server"]
}
},
"interface": {
"displayName": "MemPalace",
"shortDescription": "AI memory system for Codex",
"longDescription": "Give your AI a persistent memory — mine projects and conversations into a searchable palace backed by ChromaDB, with 19 MCP tools, auto-save hooks, and guided skills.",
"developerName": "milla-jovovich",
"category": "Coding",
"capabilities": ["Interactive", "Read", "Write"],
"websiteURL": "https://github.com/milla-jovovich/mempalace",
"privacyPolicyURL": "https://github.com/milla-jovovich/mempalace",
"termsOfServiceURL": "https://github.com/milla-jovovich/mempalace",
"defaultPrompt": [
"Search my memories for recent decisions",
"Mine this project into my memory palace",
"Show my palace status and room counts"
],
"brandColor": "#7C3AED"
}
}
+13
View File
@@ -0,0 +1,13 @@
---
name: help
description: Show MemPalace help — available commands, usage tips, and getting started guidance.
allowed-tools: Bash, Read
---
# MemPalace Help
Run the following command and follow the returned instructions step by step:
```bash
mempalace instructions help
```
+13
View File
@@ -0,0 +1,13 @@
---
name: init
description: Initialize a new MemPalace — guided setup for your AI memory palace with ChromaDB backend.
allowed-tools: Bash, Read, Write, Edit
---
# MemPalace Init
Run the following command and follow the returned instructions step by step:
```bash
mempalace instructions init
```
+13
View File
@@ -0,0 +1,13 @@
---
name: mine
description: Mine a project or conversation into your MemPalace — extract and store memories for later retrieval.
allowed-tools: Bash, Read, Glob, Grep
---
# MemPalace Mine
Run the following command and follow the returned instructions step by step:
```bash
mempalace instructions mine
```
+13
View File
@@ -0,0 +1,13 @@
---
name: search
description: Search your MemPalace — semantic search across all mined memories, projects, and conversations.
allowed-tools: Bash, Read
---
# MemPalace Search
Run the following command and follow the returned instructions step by step:
```bash
mempalace instructions search
```
+13
View File
@@ -0,0 +1,13 @@
---
name: status
description: Show MemPalace status — room counts, storage usage, and palace health.
allowed-tools: Bash, Read
---
# MemPalace Status
Run the following command and follow the returned instructions step by step:
```bash
mempalace instructions status
```
+5 -1
View File
@@ -32,11 +32,15 @@ jobs:
run: |
jq --arg v "${{ steps.version.outputs.version }}" '.plugins[0].version = $v' .claude-plugin/marketplace.json > tmp.json && mv tmp.json .claude-plugin/marketplace.json
- name: Sync codex plugin.json
run: |
jq --arg v "${{ steps.version.outputs.version }}" '.version = $v' .codex-plugin/plugin.json > tmp.json && mv tmp.json .codex-plugin/plugin.json
- name: Commit and push
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add mempalace/version.py .claude-plugin/plugin.json .claude-plugin/marketplace.json
git add mempalace/version.py .claude-plugin/plugin.json .claude-plugin/marketplace.json .codex-plugin/plugin.json
if ! git diff --staged --quiet; then
git commit -m "chore: bump version to ${{ steps.version.outputs.version }}"
git push
+2 -2
View File
@@ -475,13 +475,13 @@ def main():
p_hook_run.add_argument(
"--hook",
required=True,
choices=["stop", "precompact"],
choices=["session-start", "stop", "precompact"],
help="Hook name to run",
)
p_hook_run.add_argument(
"--harness",
required=True,
choices=["claude-code"],
choices=["claude-code", "codex"],
help="Harness type (determines stdin JSON format)",
)
+28 -3
View File
@@ -1,9 +1,9 @@
"""
Hook logic for MemPalace — Python implementation of stop and precompact hooks.
Hook logic for MemPalace — Python implementation of session-start, stop, and precompact hooks.
Reads JSON from stdin, outputs JSON to stdout.
Supported hooks: stop, precompact
Supported harnesses: claude-code (extensible to cursor, gemini, etc.)
Supported hooks: session-start, stop, precompact
Supported harnesses: claude-code, codex (extensible to cursor, gemini, etc.)
"""
import json
@@ -105,10 +105,20 @@ def _parse_claude_code_input(data: dict) -> dict:
}
def _parse_codex_input(data: dict) -> dict:
"""Parse stdin JSON for the codex harness."""
return {
"session_id": _sanitize_session_id(str(data.get("session_id", "unknown"))),
"stop_hook_active": data.get("stop_hook_active", False),
"transcript_path": str(data.get("transcript_path", "")),
}
def _parse_harness_input(data: dict, harness: str) -> dict:
"""Parse stdin JSON according to the harness type."""
parsers = {
"claude-code": _parse_claude_code_input,
"codex": _parse_codex_input,
}
parser = parsers.get(harness)
if parser is None:
@@ -163,6 +173,20 @@ def hook_stop(data: dict, harness: str):
_output({})
def hook_session_start(data: dict, harness: str):
"""Session start hook: initialize session tracking state."""
parsed = _parse_harness_input(data, harness)
session_id = parsed["session_id"]
_log(f"SESSION START for session {session_id}")
# Initialize session state directory
STATE_DIR.mkdir(parents=True, exist_ok=True)
# Pass through — no blocking on session start
_output({})
def hook_precompact(data: dict, harness: str):
"""Precompact hook: always block with comprehensive save instruction."""
parsed = _parse_harness_input(data, harness)
@@ -196,6 +220,7 @@ def run_hook(hook_name: str, harness: str):
data = {}
hooks = {
"session-start": hook_session_start,
"stop": hook_stop,
"precompact": hook_precompact,
}