feat: add Codex plugin support with hooks, commands, and documentation
This commit is contained in:
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"mempalace": {
|
||||
"command": "python3",
|
||||
"args": [
|
||||
"-m",
|
||||
"mempalace.mcp_server"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"commands": [],
|
||||
"mcp": {
|
||||
"mcpServers": {
|
||||
"mempalace": {
|
||||
"command": "python3",
|
||||
"args": [
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user