Skip to content
Development
Hook

Hooks

What memesh runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.

From plugin
memesh
162 skills7 hooks

Claude Code

> /plugin marketplace add pcircle-ai/memesh
> /plugin install memesh@pcircle-memesh

What fires, and when

PreToolUse

  • MatchesEdit|Write${CLAUDE_PLUGIN_ROOT}/scripts/hooks/pre-edit-recall.js
  • MatchesBash${CLAUDE_PLUGIN_ROOT}/scripts/hooks/guard-check.js

SessionStart

Fires once when a session begins, and again after a context compaction. It is where a plugin sets up its environment, or restores state the compaction dropped.

  • Matches*${CLAUDE_PLUGIN_ROOT}/scripts/hooks/session-start.js
  • Matchesstartup|resume${CLAUDE_PLUGIN_ROOT}/dist/host-runtime/codex-session.js

SessionEnd

  • Matches*${CLAUDE_PLUGIN_ROOT}/dist/host-runtime/codex-session.js

PostToolUse

  • MatchesBash${CLAUDE_PLUGIN_ROOT}/scripts/hooks/post-commit.js
  • MatchesExitPlanMode|AskUserQuestion${CLAUDE_PLUGIN_ROOT}/scripts/hooks/decision-nudge.js

Stop

  • Matches*${CLAUDE_PLUGIN_ROOT}/scripts/hooks/session-summary.js${CLAUDE_PLUGIN_ROOT}/scripts/hooks/stop-message-gate.js

PreCompact

  • Matches*${CLAUDE_PLUGIN_ROOT}/scripts/hooks/pre-compact.js

UserPromptSubmit

Fires before Claude sees each prompt you send. A plugin can use it to inject context, so the same instruction reaches the model every turn instead of only at session start.

  • Matches*${CLAUDE_PLUGIN_ROOT}/scripts/hooks/user-prompt-intent.js
Read hooks/hooks.json

Where it lives

  • hooks/native-memory.jsGitHub
    Read the script
    // Claude Code Mod. Rendering and MCP calls stay inside the existing host; no model turn or web server.
    const SERVER = 'plugin:memesh:memesh';
    const PANE = 'memesh-memory';
    // Left out, a pane gets a third of the terminal, which cuts a memory's text off below the actions.
    const OPEN = { id: PANE, title: 'MeMesh memories', focus: true, closeOnEscape: true, rows: 24 };
    
    // Words a person reads in place of the stored identifiers; anything unlisted is shown as stored.
    const TYPE_NAMES = { decision: 'Decision', note: 'Note', lesson_learned: 'Lesson', pattern: 'Pattern', bug: 'Bug', 'task-state': 'Task state' };
    const HOST_NAMES = { 'claude-code': 'Claude Code', codex: 'Codex', 'codex-mcp-client': 'Codex', cli: 'MeMesh CLI' };
    
    let query = '';
    let project;
    let folder;
    let memories = [];
    let selected;
    let draft;
    let busy = false;
    // tone is undefined (plain), 'success' or 'error'; the words carry the meaning, colour only confirms it.
    let notice = { text: '' };
    
    const call = async ($, tool, args) => {
      const result = await $.mcp.call(SERVER, tool, args);
      const text = result.content?.find(item => item.type === 'text')?.text;
      if (result.isError) throw new Error(text || 'MeMesh could not complete this operation.');
      return JSON.parse(text);
    };
    const run = async ($, action) => {
      if (busy) return;
      busy = true;
      notice = { text: '' };
      $.ui.invalidate('ui.render');
      try { await action(); }
      catch (error) {
        // A dropped reply may come after a commit, so promise nothing about the data: point to where to check, then the reason.
        const reason = (error instanceof Error ? error.message : '').replace(/\s+/g, ' ').trim();
        notice = { text: 'This did not finish. Reopen the memory to check what it holds now.', tone: 'error', detail: reason && `Reason: ${reason.length > 90 ? `${reason.slice(0, 89)}…` : reason}` };
      }
      finally { busy = false; $.ui.invalidate('ui.render'); }
    };
    const search = ($, value) => run($, async () => {
      query = value;
      selected = undefined;
      draft = undefined;
      const result = await call($, 'recall', { query, limit: 5 });
      project = result.scope?.project;
      // The first slice edits only the current project, never an implicitly selected global memory.
      memories = project ? result.entities.filter(memory => memory.projects?.includes(project)) : [];
      if (!project) notice = { text: 'This folder has no MeMesh project, so there are no memories to show. Open Claude Code in your project folder.', tone: 'error' };
      else if (!memories.length) notice = { text: query ? 'No memories match. Try other words.' : 'This project has no memories yet.' };
    });
    const edit = $ => run($, async () => {
      try {
        const result = await call($, 'remember', { name: selected.name, project, interactive: true });
        if (result.status === 'saved') {
          selected = { ...selected, title: result.memory.title, observations: [result.memory.body], truncated: undefined };
          memories = memories.map(memory => memory.name === selected.name ? selected : memory);
          notice = { text: '✓ Saved. Below is the text read back from the database.', tone: 'success' };
        } else if (result.status === 'cancelled') notice = { text: 'Cancelled. Nothing was changed.' };
        else notice = { text: 'Nothing changed, so nothing was saved.' };
      } finally {
        // Closing the form hands the keys to the prompt, whatever it ended in; ask for them back for the pane.
        await $.ui.open(OPEN);
      }
    });
    const editInPane = $ => run($, async () => {
      const result = await call($, 'remember', { name: selected.name, project, editor: { action: 'prepare' } });
      selected = { ...selected, ...result.memory, truncated: undefined };
      draft = { revision: result.revision, memory: result.memory, title: '', observations: result.memory.observations.map(() => '') };
    });
    const saveInPane = $ => run($, async () => {
      const { memory, revision, title, observations } = draft;
      const result = await call($, 'remember', { name: memory.name, project, editor: {
        action: 'save', revision,
        title: title.trim() ? title : memory.title,
        observations: observations.map((text, index) => text.trim() ? text : memory.observations[index]),
      } });
      selected = { ...selected, ...result.memory, truncated: undefined };
      memories = memories.map(memory => memory.name === selected.name ? selected : memory);
      draft = undefined;
      notice = result.status === 'saved'
        ? { text: '✓ Saved. Below is the text read back from the database.', tone: 'success' }
        : { text: 'Nothing changed, so nothing was saved.' };
    });
    
    export function register(on) {
      on('session.start', async ($, event, next) => {
        // The plugin's skill already owns /memesh (as /memesh:memesh), so the pane takes its own name.
        await $.command.register({ name: 'memesh-memories', description: 'View and edit this project’s MeMesh memories' });
        return next(event);
      });
      on('command.run', { command: 'memesh-memories' }, async ($, event) => {
        try {
          folder = await $.session.cwd();
          await $.ui.open(OPEN);
        } catch (error) {
          return { text: `Could not open the MeMesh memories pane: ${error instanceof Error ? error.message : 'unknown reason'}` };
        }
        await search($, event.args.trim());
        return {};
      });
      on('ui.render', { component: 'Pane' }, async ($, event, next) => {
        if (event.requestId !== PANE) return next(event);
        const { Box, Text, Button, Input } = $.ui.resolve(event);
        const redraw = () => $.ui.invalidate('ui.render');
        const typeName = memory => TYPE_NAMES[memory.type] || memory.type;
        const day = memory => memory.created_at?.slice(0, 10) || 'date not recorded';
        const oneLine = text => {
          const flat = (text || '').replace(/\s+/g, ' ').trim();
          return flat.length > 60 ? `${flat.slice(0, 59)}…` : flat;
        };
        const hint = text => Text({ children: [text], dimColor: true });
        const where = folder && [
          Text({ children: ['Project folder: ', Text({ children: [folder.split('/').filter(Boolean).at(-1) ||

Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.

@pcircle-ai
Ships withmemesh

Local collaboration layer for AI coding agents. MeMesh helps AI coding agents share context, exchange tasks and review results, and leave clear handoffs across sessions.

Stars on GitHub

Momentum

stars in total
16
Star history of PCIRCLE-AI/memesh-llm-memory on GitHub, a chart by star-history.com

More plugins that ship hooks.

superpowers

superpowers

A development methodology for coding agents that makes the agent brainstorm before building, follow plans, run parallel work and finish branches in a disciplined way. You get fewer skipped steps and more consistent results from your coding assistant.

15 skills1 hook311k
@obra@obra
Install Plugin
ponytail

ponytail

A Claude Code plugin with skills for auditing a whole repo, reviewing a change, listing deferred shortcut comments, and showing measured savings and a quick command reference.

6 skills10 hooks188k
@dietrichgebert@dietrichgebert
Install Plugin
agent-skills

agent-skills

Walks a software change from a written spec through design, testing, code review, CI/CD, and release, applying the matching engineering skill at each stage. Useful for developers using Claude Code on full development workflows.

25 skills+3112k
@addyosmani@addyosmani
Install Plugin
planning-with-files

planning-with-files

Persistent file-based planning for AI coding agents on long-running, multi-step tasks. It keeps task_plan.md, findings.md and progress.md on disk, so work can resume after /clear or context compaction.

6 skills+228k
@othmanadi@othmanadi
Install Plugin
gsd-core

gsd-core

A spec-driven development workflow for Claude Code that guides a project through planning, phases, milestone audits and fixes. Includes skills to generate tests from UAT criteria and to audit completed work.

72 skills+311k
@open-gsd@open-gsd
Install Plugin
superpowers-zh

superpowers-zh

Chinese-language skill pack for Claude Code that adds workflow guidance for brainstorming, TDD, debugging, and code review, plus Chinese conventions for commits, documentation, and domestic Git platforms like Gitee and Coding.net.

21 skills1 hook8.5k
@jnmetacode@jnmetacode
Install Plugin
socraticode

socraticode

Gives Claude Code semantic search, dependency graph and architecture context over a codebase, so an AI assistant can explore and understand a project's code. Includes skills for indexing a project and managing its file watching.

2 skills+33.4k
@giancarloerra@giancarloerra
Install Plugin
cocoindex-code

cocoindex-code

A lightweight code search engine that uses AST-based semantic search with local embeddings and incremental indexing. It helps Claude Code users find code faster and more efficiently.

1 skill1 MCP+13k
@cocoindex-io@cocoindex-io
Install Plugin

Questions about the hooks in memesh.

Which coding agents can use the hooks in memesh?
The hooks in memesh install in Claude Code and Cursor: each runs Claude Code hooks from .claude/settings.json, so copy the hooks there.