From memesis
Open the review-canvas web UI, optionally loading a specific file. Supports JSONL session transcripts and plain .md/.txt files. Hot-reloads the running server via POST /load — no MCP reconnect needed.
How this skill is triggered — by the user, by Claude, or both
Slash command
/memesis:canvasThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Open the review-canvas web UI pre-loaded with a specific file (transcript or markdown document) for inline commenting.
Open the review-canvas web UI pre-loaded with a specific file (transcript or markdown document) for inline commenting.
/memesis:canvas [file] # Load <file> into the canvas
/memesis:canvas # Show currently loaded file
[file] — path to a JSONL transcript or .md/.txt file (absolute or relative to cwd). Supports tilde expansion.POST http://localhost:{PORT}/load — if success, report and done.POST /load once.GET http://localhost:{PORT}/status — report daemon state (turn count, bridge connection) if up, otherwise report not running.
import json, os, sys, time, subprocess, urllib.request, urllib.error
PORT = int(os.environ.get("REVIEW_CANVAS_PORT", "8788"))
PLUGIN_DIR = os.path.expanduser("~/projects/memesis/tools/channels/review-canvas")
args = "<args>"
def post_load(file_path):
payload = json.dumps({"path": file_path}).encode()
req = urllib.request.Request(
f"http://localhost:{PORT}/load", data=payload,
headers={"Content-Type": "application/json"}, method="POST")
with urllib.request.urlopen(req, timeout=3) as resp:
return json.loads(resp.read())
def start_daemon():
subprocess.Popen(
["bun", "run", "daemon.ts"],
cwd=PLUGIN_DIR,
stdout=open("/tmp/review-canvas-daemon.log", "a"),
stderr=subprocess.STDOUT,
start_new_session=True,
)
# wait for port to open (up to 3s)
for _ in range(15):
try:
urllib.request.urlopen(f"http://localhost:{PORT}/status", timeout=0.5)
return True
except Exception:
time.sleep(0.2)
return False
if args.strip():
file_path = os.path.abspath(os.path.expanduser(args.strip()))
if not os.path.exists(file_path):
print(f"File not found: {file_path}")
sys.exit(1)
try:
result = post_load(file_path)
print(f"Loaded: {file_path}")
print(f"{result['turns']} turns — http://localhost:{PORT}")
except (urllib.error.URLError, OSError):
print("Daemon not running — starting...")
if start_daemon():
try:
result = post_load(file_path)
print(f"Loaded: {file_path}")
print(f"{result['turns']} turns — http://localhost:{PORT}")
except Exception as e:
print(f"Error: daemon started but /load failed: {e}")
sys.exit(1)
else:
print(f"Error: daemon failed to start. Check /tmp/review-canvas-daemon.log")
sys.exit(1)
else:
try:
req = urllib.request.Request(f"http://localhost:{PORT}/status", method="GET")
with urllib.request.urlopen(req, timeout=2) as resp:
status = json.loads(resp.read())
print(f"Daemon running — {status['turns']} turns, bridge {'connected' if status['bridge_connected'] else 'disconnected'}. http://localhost:{PORT}")
except (urllib.error.URLError, OSError):
print(f"Daemon not running on port {PORT}.")
{type: 'reload', turns: [...]} over WebSocket — browser re-renders without a page refresh, no MCP reconnect needed.Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub emmahyde/memesis --plugin memesis