Skip to content
Development
Hook

Hooks

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

From plugin
stitch-kit
4736 skills1 agent2 hooks

Claude Code

> /plugin marketplace add gabelul/stitch-kit
> /plugin install stitch-kit@stitch-kit

What fires, and when

PreCompact

  • Matchesautonode "${CLAUDE_PLUGIN_ROOT}/hooks/pre-compact.mjs"
  • Matchesmanualnode "${CLAUDE_PLUGIN_ROOT}/hooks/pre-compact.mjs"

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.

  • Matchescompactnode "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.mjs"
  • Matchesresumenode "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.mjs"
Read hooks/hooks.json

Where it lives

  • hooks/pre-compact.mjsRunsGitHub
    Read the script
    #!/usr/bin/env node
    /**
     * pre-compact.mjs — PreCompact hook (matchers: auto, manual)
     *
     * Fires right before the host compacts the conversation. By this point the
     * skills have already been writing state to .stitch/session as they go, so the
     * important stuff is on disk. This hook is the backstop on top of that:
     *
     *   1. Copies the raw transcript into snapshots/ (owner-only perms) so nothing
     *      is ever truly lost. Best-effort and isolated — if the copy fails, the
     *      breadcrumb below still gets written.
     *   2. Refreshes RESUME.md with a human/agent-readable breadcrumb.
     *
     * Two hard rules:
     *   - No active Stitch session → do nothing, exit 0.
     *   - ALWAYS exit 0. PreCompact can block compaction with exit code 2, and the
     *     last thing we want is to wedge someone's session because a copy failed.
     */
    
    import {
      readFileSync,
      copyFileSync,
      chmodSync,
      existsSync,
      mkdirSync,
      writeFileSync,
      readdirSync,
      rmSync,
    } from "node:fs";
    import { join } from "node:path";
    import { loadState, formatStatus, sessionDir, snapshotsDir, resumeFile } from "../scripts/stitch-session.mjs";
    
    /** Keep at most this many transcript snapshots so the backstop can't bloat the project. */
    const MAX_SNAPSHOTS = 5;
    
    /** Delete the oldest snapshots beyond MAX_SNAPSHOTS (lexical sort works — names are timestamped). */
    function pruneSnapshots(dir) {
      try {
        const files = readdirSync(dir)
          .filter((f) => f.startsWith("transcript-") && f.endsWith(".jsonl"))
          .sort();
        for (const stale of files.slice(0, Math.max(0, files.length - MAX_SNAPSHOTS))) {
          rmSync(join(dir, stale), { force: true });
        }
      } catch {
        // pruning is housekeeping — failing it must not affect compaction
      }
    }
    
    try {
      // Parse the hook input (JSON on stdin) FIRST, so we can resolve the project
      // root from input.cwd before touching any state. session_id, transcript_path,
      // trigger, cwd are the fields we use.
      let sessionId = "session";
      let transcriptPath = "";
      let trigger = "auto";
      try {
        const raw = readFileSync(0, "utf8");
        if (raw) {
          const input = JSON.parse(raw);
          if (input.cwd && !process.env.CLAUDE_PROJECT_DIR) process.env.CLAUDE_PROJECT_DIR = input.cwd;
          sessionId = (input.session_id || "session").toString().replace(/[^\w.-]/g, "_");
          transcriptPath = (input.transcript_path || "").toString();
          trigger = (input.trigger || "auto").toString();
        }
      } catch {
        // missing/garbled input — proceed with defaults, still write the breadcrumb
      }
    
      // Only do anything if there's an active Stitch session to protect.
      const state = loadState();
      if (state) {
        mkdirSync(sessionDir(), { recursive: true });
    
        // 1. Raw transcript backstop. Isolated in its own try so a copy failure
        //    (transcript gone, perms, long filename) can't skip the breadcrumb.
        try {
          if (transcriptPath && existsSync(transcriptPath)) {
            mkdirSync(snapshotsDir(), { recursive: true });
            const stamp = new Date().toISOString().replace(/[:.]/g, "-");
            const dest = join(snapshotsDir(), `transcript-${sessionId}-${stamp}.jsonl`);
            copyFileSync(transcriptPath, dest);
            chmodSync(dest, 0o600); // snapshots can hold conversation content — owner-only
            pruneSnapshots(snapshotsDir());
          }
        } catch {
          // best-effort — fall through to the breadcrumb
        }
    
        // 2. Human/agent-readable breadcrumb — always attempted, even if (1) failed.
        writeFileSync(
          resumeFile(),
          `# Stitch session — resume breadcrumb\n\n` +
            `_Compaction (${trigger}) at ${new Date().toISOString()}._\n\n` +
            `${formatStatus(state)}\n`
        );
      }
    } catch {
      // Swallow everything — see the "ALWAYS exit 0" rule above.
    }
    
    process.exit(0);
    
  • hooks/session-start.mjsRunsGitHub
    Read the script
    #!/usr/bin/env node
    /**
     * session-start.mjs — SessionStart hook (matchers: compact, resume)
     *
     * Fires when a session begins or resumes. After a compaction the host re-runs
     * SessionStart with source "compact", which is our one chance to tell the
     * freshly-summarised model where its work actually lives. We emit the
     * re-orientation as `hookSpecificOutput.additionalContext` JSON — that's the
     * form both Claude Code and Codex inject as model-visible context (Codex treats
     * bare stdout as weaker "developer context", so JSON is the portable choice).
     *
     * Hard rule: this hook runs on EVERY session start for everyone who installs
     * stitch-kit. If there's no active Stitch session, it must print nothing and
     * get out of the way. And it must never throw — a crashing SessionStart hook is
     * a great way to ruin someone's day. So everything is wrapped and we always
     * exit 0.
     */
    
    import { readFileSync } from "node:fs";
    import { loadState, isRecent, formatStatus } from "../scripts/stitch-session.mjs";
    
    /** Sources where resurfacing state is useful. "startup"/"clear" are fresh starts — stay quiet there. */
    const RESURFACE_ON = new Set(["compact", "resume"]);
    
    try {
      // Hook input arrives as JSON on stdin. Parse source + cwd; if we can't read or
      // parse it, treat source as unknown and bail quietly rather than guessing.
      let source = "";
      try {
        const raw = readFileSync(0, "utf8");
        if (raw) {
          const input = JSON.parse(raw);
          if (input.cwd && !process.env.CLAUDE_PROJECT_DIR) process.env.CLAUDE_PROJECT_DIR = input.cwd;
          source = (input.source || "").toString();
        }
      } catch {
        source = "";
      }
    
      if (RESURFACE_ON.has(source)) {
        const state = loadState();
        if (state && isRecent(state)) {
          process.stdout.write(
            JSON.stringify({
              hookSpecificOutput: {
                hookEventName: "SessionStart",
                additionalContext: formatStatus(state),
              },
            }) + "\n"
          );
        }
      }
    } catch {
      // Never let a hook failure surface to the user or block the session.
    }
    
    process.exit(0);
    

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.

@gabelul
Ships withstitch-kit

Your coding agent writes decent code and designs terrible UI. stitch-kit fixes the second half — it wires agents into Google Stitch (text prompts → genuinely beautiful screens) and teaches them to drive it properly.

4736 skills1 agent
Get the whole plugin

Stars on GitHub

Momentum

stars in total
47
Star history of gabelul/stitch-kit 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 stitch-kit.

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