diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json new file mode 100644 index 0000000..58223a0 --- /dev/null +++ b/.agents/plugins/marketplace.json @@ -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" + } + ] +} diff --git a/.claude-plugin/.mcp.json b/.claude-plugin/.mcp.json new file mode 100644 index 0000000..b1e81ed --- /dev/null +++ b/.claude-plugin/.mcp.json @@ -0,0 +1,9 @@ +{ + "mempalace": { + "command": "python3", + "args": [ + "-m", + "mempalace.mcp_server" + ] + } +} diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 482e77a..488f6f3 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -7,7 +7,7 @@ }, "license": "MIT", "commands": [], - "mcp": { + "mcpServers": { "mempalace": { "command": "python3", "args": [ diff --git a/.codex-plugin/README.md b/.codex-plugin/README.md new file mode 100644 index 0000000..a7ec1de --- /dev/null +++ b/.codex-plugin/README.md @@ -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 diff --git a/.codex-plugin/hooks.json b/.codex-plugin/hooks.json new file mode 100644 index 0000000..1c235f2 --- /dev/null +++ b/.codex-plugin/hooks.json @@ -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" + } + ] + } + ] + } +} diff --git a/.codex-plugin/hooks/mempal-precompact-hook.sh b/.codex-plugin/hooks/mempal-precompact-hook.sh new file mode 100644 index 0000000..46af49d --- /dev/null +++ b/.codex-plugin/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 diff --git a/.codex-plugin/hooks/mempal-session-start-hook.sh b/.codex-plugin/hooks/mempal-session-start-hook.sh new file mode 100644 index 0000000..7c7b5c2 --- /dev/null +++ b/.codex-plugin/hooks/mempal-session-start-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-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 diff --git a/.codex-plugin/hooks/mempal-stop-hook.sh b/.codex-plugin/hooks/mempal-stop-hook.sh new file mode 100644 index 0000000..2d38932 --- /dev/null +++ b/.codex-plugin/hooks/mempal-stop-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-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 diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json new file mode 100644 index 0000000..f040014 --- /dev/null +++ b/.codex-plugin/plugin.json @@ -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" + } +} diff --git a/.codex-plugin/skills/help/SKILL.md b/.codex-plugin/skills/help/SKILL.md new file mode 100644 index 0000000..d0e8f43 --- /dev/null +++ b/.codex-plugin/skills/help/SKILL.md @@ -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 +``` diff --git a/.codex-plugin/skills/init/SKILL.md b/.codex-plugin/skills/init/SKILL.md new file mode 100644 index 0000000..cc0e2f9 --- /dev/null +++ b/.codex-plugin/skills/init/SKILL.md @@ -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 +``` diff --git a/.codex-plugin/skills/mine/SKILL.md b/.codex-plugin/skills/mine/SKILL.md new file mode 100644 index 0000000..1a94e29 --- /dev/null +++ b/.codex-plugin/skills/mine/SKILL.md @@ -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 +``` diff --git a/.codex-plugin/skills/search/SKILL.md b/.codex-plugin/skills/search/SKILL.md new file mode 100644 index 0000000..4d5bf4b --- /dev/null +++ b/.codex-plugin/skills/search/SKILL.md @@ -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 +``` diff --git a/.codex-plugin/skills/status/SKILL.md b/.codex-plugin/skills/status/SKILL.md new file mode 100644 index 0000000..617d3be --- /dev/null +++ b/.codex-plugin/skills/status/SKILL.md @@ -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 +``` diff --git a/.github/workflows/bump-plugin-version.yml b/.github/workflows/bump-plugin-version.yml index d91c026..43adc8b 100644 --- a/.github/workflows/bump-plugin-version.yml +++ b/.github/workflows/bump-plugin-version.yml @@ -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 diff --git a/mempalace/cli.py b/mempalace/cli.py index 9d2465b..0a24abf 100644 --- a/mempalace/cli.py +++ b/mempalace/cli.py @@ -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)", ) diff --git a/mempalace/hooks_cli.py b/mempalace/hooks_cli.py index 6e0ec0a..6b4bb0f 100644 --- a/mempalace/hooks_cli.py +++ b/mempalace/hooks_cli.py @@ -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, }