ClaudeSuperPower
S

SessionStart

Hook

Fires on

SessionStart

Once, as a session begins — before you type anything.

Where this sits in a session

  1. Session start
  2. Prompt submitted
  3. Prompt expansion
  4. Before tool use
  5. After tool use
  6. Notification
  7. Before compaction
  8. Subagent finished
  9. Turn finished
  10. Session end

Observes onlyEmits messages or context. It never returns a permission decision.

What it runs

Executed automatically when the event fires — no prompt, no confirmation.

command
node "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.mjs"
command· timeout 10s
node "${CLAUDE_PLUGIN_ROOT}/hooks/convex-freshness.mjs"

Install it yourself

Paste into .claude/settings.json to run this hook without installing the whole plugin. Adjust the paths to point at your own copy of the scripts.

settings.json
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start.mjs\""
          },
          {
            "type": "command",
            "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/convex-freshness.mjs\"",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

Source (2 files)

Read this before installing — it runs on your machine with your permissions.

#!/usr/bin/env node
// SessionStart hook: emits a single anonymous `plugin_session_start`
// telemetry event (see analytics.mjs for the privacy and opt-out gates).
// Beyond OS platform + Node version, the event carries two locally-derived,
// non-identifying fields read from the hook payload:
//   - convex_project: whether the session's cwd looks like a Convex app
//     (convex/ dir, convex.json, or a convex package dep — see
//     isConvexProject in analytics.mjs). The boolean is sent, the path never
//     is. This is what separates "plugin installed" from "doing Convex work"
//     in the session numbers.
//   - session_source: how the session began (startup | resume | clear |
//     compact, per Claude Code's SessionStart payload). Any other value is
//     clamped to "other" so no free-form harness string is ever recorded.
// Prints nothing and exits 0 in every case; the capture itself is
// fire-and-forget via a detached child, so this hook never delays the
// session.

import { readFileSync } from "node:fs";
import { capture, isConvexProject } from "./analytics.mjs";

const SESSION_SOURCES = new Set(["startup", "resume", "clear", "compact"]);

try {
  // Read the hook payload from stdin, tolerating garbage — malformed input
  // degrades to an event without the derived fields, never an error.
  let payload = {};
  try {
    payload = JSON.parse(readFileSync(0, "utf8") || "{}") ?? {};
  } catch {
    // ignore malformed input
  }
  const props = {
    os: process.platform,
    node_version: process.version,
  };
  if (typeof payload.cwd === "string" && payload.cwd) {
    props.convex_project = isConvexProject(payload.cwd);
  }
  if (typeof payload.source === "string" && payload.source) {
    props.session_source = SESSION_SOURCES.has(payload.source)
      ? payload.source
      : "other";
  }
  capture("plugin_session_start", props);
} catch {
  // Telemetry must never surface an error to the session.
}
process.exit(0);

Shipped by 1 plugin

Installing any of these installs this hook.

Reviews

Log in to leave a review.

No reviews yet — be the first.

Explore related

Other things in this space — across every part of the ecosystem, not just hooks.