Skip to content

Hooks

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

From plugin
brooks-lint
1.6k9 skills5 agents6 commands1 hook

Claude Code

> /plugin marketplace add hyhmrright/brooks-lint
> /plugin install brooks-lint@brooks-lint-marketplace

What fires, and when

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.

  • Matchesstartup|clear|compactnode "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.mjs"
Read hooks/hooks.json

Where it lives

  • hooks/session-start.mjsRunsGitHub
    Read the script
    #!/usr/bin/env node
    
    import {
      existsSync,
      mkdirSync,
      readFileSync,
      readdirSync,
      rmSync,
      writeFileSync,
    } from "node:fs";
    import os from "node:os";
    import path from "node:path";
    import { fileURLToPath } from "node:url";
    
    const __dirname = path.dirname(fileURLToPath(import.meta.url));
    const pluginDir = path.resolve(__dirname, "..");
    const homeDir = process.env.HOME || os.homedir();
    
    function readVersion() {
      const packageJson = JSON.parse(
        readFileSync(path.join(pluginDir, "package.json"), "utf8"),
      );
      if (!packageJson.version) {
        throw new Error("Failed to read version from package.json");
      }
      return packageJson.version;
    }
    
    function installCommands(version) {
      const commandDir = path.join(homeDir, ".claude", "commands");
      const sentinel = path.join(commandDir, `.brooks-lint-v${version}`);
    
      // Already installed for this version — nothing to do.
      if (existsSync(sentinel)) return;
    
      mkdirSync(commandDir, { recursive: true });
    
      const commandsDir = path.join(pluginDir, "commands");
      for (const entry of readdirSync(commandsDir)) {
        if (/^brooks-.*\.md$/.test(entry)) {
          // Short forms live in ~/.claude/commands/ (user commands), where
          // ${CLAUDE_PLUGIN_ROOT} does not expand — bake in the absolute path so
          // the wrapper can still locate its SKILL.md.
          const body = readFileSync(path.join(commandsDir, entry), "utf8").replaceAll(
            "${CLAUDE_PLUGIN_ROOT}",
            pluginDir,
          );
          writeFileSync(path.join(commandDir, entry), body);
        }
      }
    
      for (const entry of readdirSync(commandDir)) {
        if (entry === ".brooks-lint-installed" || entry.startsWith(".brooks-lint-v")) {
          rmSync(path.join(commandDir, entry), { force: true });
        }
      }
    
      writeFileSync(sentinel, "");
    }
    
    function buildContext() {
      return [
        "You have the brooks-lint plugin installed. It provides six independent skills - load the relevant one via the Skill tool:",
        "  brooks-lint:brooks-review  -> PR code review",
        "  brooks-lint:brooks-audit   -> Architecture audit",
        "  brooks-lint:brooks-debt    -> Tech debt assessment",
        "  brooks-lint:brooks-test    -> Test quality review",
        "  brooks-lint:brooks-health  -> Codebase health dashboard",
        "  brooks-lint:brooks-sweep   -> Full sweep: analyse all dimensions and auto-fix findings",
        "",
        "Triggers when the user asks to review code, discuss architecture, assess tech debt, or discuss test quality. Each skill's own description carries its full trigger phrases and exclusions.",
      ].join("\n");
    }
    
    function buildOutput(context) {
      if (process.env.CLAUDE_PLUGIN_ROOT) {
        return {
          hookSpecificOutput: {
            hookEventName: "SessionStart",
            additionalContext: context,
          },
        };
      }
    
      return {
        additional_context: context,
      };
    }
    
    try {
      installCommands(readVersion());
      process.stdout.write(`${JSON.stringify(buildOutput(buildContext()), null, 2)}\n`);
    } catch (error) {
      process.stderr.write(`${error.message}\n`);
      process.exit(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.

@hyhmrright
Ships withbrooks-lint

Portable AI code quality reviews for Codex, Cursor, Copilot and other coding agents: six Agent Skills for architecture, technical debt, tests and remediation.

Stars on GitHub

Momentum

stars in total
1.6k
Star history of hyhmrright/brooks-lint on GitHub, a chart by star-history.com

More plugins that ship hooks.

code-review-graph

code-review-graph

Builds a local, persistent map of your codebase with tree-sitter so Claude Code reads only the code that matters. Offers skills for code review, debugging, exploring structure, and safe refactoring.

7 skills+233k
@tirth8205@tirth8205
Install Plugin

Questions about the hooks in brooks-lint.

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