hooks-authoring

Rules for authoring, registering, and testing hooks in Rosetta. Use when creating a new hook, adding a SemanticKind, troubleshooting a hook that doesn't fire, or reviewing a hooks PR.

8 stars

Best use case

hooks-authoring is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Rules for authoring, registering, and testing hooks in Rosetta. Use when creating a new hook, adding a SemanticKind, troubleshooting a hook that doesn't fire, or reviewing a hooks PR.

Teams using hooks-authoring should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/hooks-authoring/SKILL.md --create-dirs "https://raw.githubusercontent.com/griddynamics/rosetta/main/instructions/r2/core/skills/hooks-authoring/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/hooks-authoring/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How hooks-authoring Compares

Feature / Agenthooks-authoringStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Rules for authoring, registering, and testing hooks in Rosetta. Use when creating a new hook, adding a SemanticKind, troubleshooting a hook that doesn't fire, or reviewing a hooks PR.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

<hooks_authoring>

### Hook entry rule

Only files that export `defineHook(…)` AND call `runAsCli(hook, module)` belong directly in `hooks/src/hooks/`.
Every `.ts` at the top level of that directory becomes a standalone CJS bundle distributed to all 5 IDEs (claude, codex, copilot, cursor, windsurf).
Helper/data files without `runAsCli` belong in a named subdirectory: `hooks/src/hooks/<feature>/`.

### Helper placement

- Feature-local helpers → `hooks/src/hooks/<feature>/` (e.g. `hooks/src/hooks/dangerous-actions/patterns.ts`).
- Cross-hook runtime helpers → `hooks/src/runtime/`.

### Build is non-recursive

`hooks/scripts/build-bundles.mjs:24` uses `readdirSync(hooksDir).filter(f => f.endsWith('.ts'))`.
There is **no `{ recursive: true }`**. Subdirectories are invisible to the bundler.
Adding a top-level `.ts` without `runAsCli` produces a dead bundle for all 5 IDEs.

### Adding a SemanticKind

When a hook needs a new tool category (e.g. `mcp-call`):

1. **`hooks/src/runtime/ide-registry.ts`** — add a row to `TOOL_KINDS` with all 5 IDE columns (`null` where the event doesn't exist). `SemanticKind = keyof typeof TOOL_KINDS` so TypeScript enforces coverage.
2. **`hooks/src/runtime/ide-rows/<ide>.ts`** — if the kind requires special logic (e.g. prefix-match for `mcp__.*`), add a conditional branch at the top of `lookupToolKind` in the IDE-row file before the table loop. Table-driven lookup alone cannot handle open-ended tool name patterns.
3. **Hook entry** — add the new kind to `def.on.toolKinds`.
4. **Matcher in `hooks.json.tmpl`** — widen to include new tool names/patterns.

Order matters: `run-hook.ts:98` gates on `toolKinds` before calling `run(ctx)`. Matcher passes the event in; `toolKinds` must include the mapped kind or the call is dropped silently.

### Registration

Every new hook must appear in every plugin's `hooks.json`. The canonical source is `plugins/core-claude/hooks/hooks.json.tmpl` (and equivalent templates in other plugins). Direct edits to generated `hooks.json` files are overwritten on next `pre_commit.py` run.

Paths by plugin:
- `plugins/core-claude/hooks/hooks.json.tmpl` → `hooks.json`
- `plugins/core-copilot/hooks/hooks.json.tmpl` → `hooks/hooks.json`
- `plugins/core-cursor/.cursor/hooks/hooks.json.tmpl` → `hooks.json`
- `plugins/core-codex/.codex/hooks/hooks.json.tmpl` → `hooks.json`
- `plugins/core-windsurf/` — bundles are distributed but hooks.json registration is not covered by the regression test (`hooks-registered.test.ts`); register manually if needed.

### Platform-scoped events

`PreToolUse` is absent on Copilot (`'copilot': null` in `ide-registry.ts`). If a hook uses a platform-exclusive event, add its name to `CLAUDE_CODE_ONLY_HOOKS` Set in `hooks/tests/regression/hooks-registered.test.ts`. Before adding a second scoped hook, refactor the Set to `Map<string, Set<IdeName>>`.

### Tests

Co-locate tests in `hooks/tests/<hook-name>.test.ts`. The regression test (`hooks/tests/regression/hooks-registered.test.ts`) automatically discovers all `.ts` entries at `hooks/src/hooks/` top level and asserts each is referenced in every plugin's `hooks.json`. A new hook without registration immediately fails the regression guard.

### Sync command

After any source change under `hooks/src/` or `instructions/r{2,3}/core/`:

```bash
venv/bin/python scripts/pre_commit.py
```

This builds CJS bundles, runs full test suite, and syncs `instructions/r{2,3}/core/` → all four plugin directories.

### Pitfalls

- **Helper files in top-level** — produces dead bundles for all 5 IDEs + false regression test failures without `isLibraryModule` workaround. Fix: move to subdirectory.
- **Missing registration** — hook fires silently nowhere; regression test catches this at CI time.
- **Secrets in Evidence** — `buildDenyMessage` echoes `evidence` to transcript by default. Pass `redact=true` for DANGEROUS_CONTENT matches (AWS keys, PEM certs, SQL with row data).
- **Regex `[rf]{2,}` false positives** — matches `rm -rr` and `rm -ff`. Require both flags with lookaheads: `/\brm\s+-(?=[a-zA-Z]*[rR])(?=[a-zA-Z]*[fF])[a-zA-Z]+\b/`.
- **`$`-anchor vs trailing slash** — path patterns like `/\.kube\/config$` fail when tested against `filePath` with trailing slash. Always test against `normalizedPath = filePath.replace(/\/+$/, '')`.
- **Matcher without toolKinds mapping** — adding a name to the JSON matcher but not to `lookupToolKind` and `def.on.toolKinds` is inert.

### Reference files

```
hooks/scripts/build-bundles.mjs
hooks/src/runtime/ide-registry.ts
hooks/src/runtime/ide-rows/claude-code.ts
hooks/src/runtime/run-hook.ts:98
plugins/core-claude/hooks/hooks.json.tmpl
hooks/tests/regression/hooks-registered.test.ts
```

</hooks_authoring>

Related Skills

requirements-authoring

8
from griddynamics/rosetta

Author, update, and validate functional and non-functional requirements as a source of truth using atomic requirement units with explicit user approval.

coding-agents-prompt-authoring

8
from griddynamics/rosetta

Author, update, and validate prompts (skills, agents, subagents, workflows, commands, rules, templates, or just any generic prompt). Produces a final prompt with analytics artifacts (brief, contracts, and a validation pack). Use when creating, editing, refactoring, reviewing, validating, or migrating prompts for AI coding agents.

operation-manager

8
from griddynamics/rosetta

Rosetta skill for reliable execution: plan creation, tracking, and execution coordination via local JSON files.

load-workflow

8
from griddynamics/rosetta

Rosetta MUST skill to select, load, and activate the best-matching workflow for the current request, inject its phases into the execution plan, and restore state when resuming.

load-context-instructions

8
from griddynamics/rosetta

Detect active execution mode and load Rosetta bootstrap instructions accordingly.

gitnexus-setup

8
from griddynamics/rosetta

Use when directly requested to install GitNexus.

gitnexus-cli

8
from griddynamics/rosetta

GitNexus CLI reference for npx commands — analyze, status, clean, wiki, list — with flags, effects, and when to run each.

testing

8
from griddynamics/rosetta

Rosetta testing skill for thorough, isolated, idempotent tests with 80% minimum coverage, external-only mocking, and scenario-driven testing. Use when writing or updating tests.

tech-specs

8
from griddynamics/rosetta

Rosetta skill for defining clear, testable tech specifications from requirements. Use when creating implementation-ready documentation that defines the target state architecture, contracts, and interfaces.

subagent-contract

8
from griddynamics/rosetta

Rosetta MUST skill. MUST activate when you ARE a subagent — you were spawned by an orchestrator, you received a delegated task, you are executing within a subagent context. Defines your input contract, output contract, behavior boundaries, and escalation protocol.

specflow-use

8
from griddynamics/rosetta

Connect Rosetta locally with Grid Dynamics SpecFlow MCP. Trigger only when the user mentions SpecFlow or SpecFlow workspaces and if SpecFlow MCP is already installed.

sensitive-data

8
from griddynamics/rosetta

Rosetta CRITICAL MUST skill. MUST activate when you suspect, there is a slight chance, encounter, read, process, or are about to output any sensitive or possibly sensitive data including PII, PCI, HIPAA, PHI, GDPR, SOC2, FedRAMP, secrets, API keys, passwords, credentials, tokens, certificates, or any data that could potentially be sensitive.