Skip to content

Commit d9c456d

Browse files
committed
Add mods: sec-default, diff and telemetry, the hooks-module plugins built into Claude Code
1 parent 9cdc2a4 commit d9c456d

555 files changed

Lines changed: 7866 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎mods/README.md‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Mods
2+
3+
A mod is a Claude Code plugin whose behaviour lives in a hooks module: one
4+
`register(on, options)` entry that hooks the engine's events as functions
5+
`($, e, next)`. These three ship inside Claude Code; this folder is their
6+
source, published as it is built into the binary.
7+
8+
| Mod | What it does | Seated |
9+
| --- | --- | --- |
10+
| [`sec-default`](sec-default) | Keeps an organization's classic hooks, prompt content, managed settings and tool policy out of reach of the plugins a person installs; adds no policy of its own. | Outermost, on a machine with managed settings or for a Team or Enterprise organization, unless managed `prependPlugins` says otherwise |
11+
| [`diff`](diff) | `/diff`: the session's uncommitted changes in a pane beside the transcript, file by file with their hunks, refreshed as Claude edits files and runs commands. | Built in |
12+
| [`telemetry`](telemetry) | Adds `$.telemetry` (`log`, `mark`) in the `engine.create` fold so a plugin can record an event as a first-party analytics row; sends nothing wherever Claude Code's analytics are off. | Built in |
13+
14+
Each folder is a complete plugin: `.claude-plugin/plugin.json`, a
15+
`hooks/hooks.json` naming the module, and TypeScript under `hooks/` typed
16+
against the declarations `/plugin-types` writes (`import type … from
17+
'claude-code'`). To read one running from source:
18+
19+
claude --plugin-dir mods/diff
20+
21+
Early access: hooks modules load only where function hooks are enabled, and
22+
the API these mods are written against may change between releases without
23+
notice. They are not listed in this repository's marketplace; the copies that
24+
matter are the ones already in your Claude Code.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "diff",
3+
"version": "0.1.0",
4+
"description": "The diff pane as a plugin: /diff opens the session's uncommitted changes beside the transcript, file by file with their hunks, refreshed as Claude edits and runs commands; the first edit opens it where the terminal is wide enough, and a file's ask button rides its hunks on the next prompt.",
5+
"author": {
6+
"name": "Anthropic"
7+
}
8+
}

‎mods/diff/README.md‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# diff
2+
3+
The diff pane as a plugin: `/diff` opens the session's uncommitted changes
4+
beside the transcript, one row per changed file and the selected file's
5+
hunks beneath, and closes it again. The pane refreshes as Claude edits,
6+
runs shell commands and finishes turns, and while it is open it polls the
7+
repository's HEAD so a commit or checkout made elsewhere shows too. The
8+
first successful edit of a session opens the pane by itself where the
9+
terminal is wide enough (144 columns when the person never chose, 110 when
10+
they kept it open before; a person who closed it is left alone). A file's
11+
ask button arms that file: its hunks ride the next prompt as context, once.
12+
13+
The pane compares the working tree against HEAD, split at the session's
14+
start (the default), against HEAD plainly, or against the merge-base with
15+
the default branch; the choice is kept per repository in the plugin's
16+
store. A second picker shows one earlier turn's edits instead of the
17+
working tree, read from the session's messages. Files that changed before
18+
the session started, and noise (lockfiles, generated and test files), are
19+
listed apart and folded until asked for. Outside a git repository `/diff`
20+
says so and does nothing else.
21+
22+
`hooks/register.ts` is the module; everything under `hooks/` is its parts.
23+
24+
## What it hooks
25+
26+
| event | what the hook does |
27+
| --- | --- |
28+
| `session.start` | Binds the engine once, registers `/diff` (a session where another `/diff` is listed leaves the plugin idle), and pins the repository. |
29+
| `ui.render` of `PromptHint` | Reads the terminal's width, which decides whether the first edit opens the pane. |
30+
| `ui.render` of `Pane` | Draws the pane: header, the file list, the toggles, the base and source pickers, and the selected file's hunks. |
31+
| `command.run` of `diff` | Opens or closes the pane and remembers the choice. |
32+
| `command.run` of `clear`, `resume` | Closes the pane and forgets the session's state. |
33+
| `tool.call` of `Edit`, `Write`, `NotebookEdit` | After the edit, refreshes an open pane; the session's first successful edit opens it. |
34+
| `tool.call` of `Bash`, `PowerShell` | After the command, refreshes an open pane. |
35+
| `turn.complete` | Refreshes an open pane. |
36+
| `prompt.submit` | Adds the armed file's hunks to the prompt's context and disarms. |
37+
38+
## What it calls on `$`
39+
40+
`clock.after`, `clock.every`, `clock.now`, `clock.sleep`, `command.register`,
41+
`fs.list`, `fs.read`, `fs.stat`, `process.run` (git, read-only), `session.messages`,
42+
`store.get`, `store.set`, `telemetry.log`, `telemetry.mark`, `ui.close`,
43+
`ui.invalidate`, `ui.log`, `ui.open`, `ui.resolve`, `ui.status`.
44+
45+
`$.telemetry` is the telemetry plugin's noun; where it is absent the rows
46+
are dropped and nothing else changes.
47+
48+
## Try it
49+
50+
```sh
51+
claude --plugin-dir /path/to/diff
52+
```
53+
54+
then `/diff` inside a git repository with a modified file.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* A value a tool stored as the array it is, or an empty one when it is
3+
* anything else.
4+
*
5+
* @param value a field of a tool's input or result
6+
* @returns the array's items, unknown each
7+
*/
8+
export const arrayOf = (value: unknown): readonly unknown[] =>
9+
Array.isArray(value) ? value : []

‎mods/diff/hooks/array-of/index.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './array-of.js'
2+
3+
export * as default from '.'
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type Git from '../git'
2+
import Limits from '../limits'
3+
import type { ArmedAsk } from './armed-ask'
4+
5+
/**
6+
* A file's diff as the context block the next prompt carries when the
7+
* person presses ask.
8+
*
9+
* A line saying where it came from, then the hunks in unified form, cut at
10+
* MAX_LINES_PER_FILE lines.
11+
*
12+
* @param path the file's repository-relative path
13+
* @param hunks its hunks as the pane shows them
14+
* @returns the armed ask
15+
*/
16+
export function armedAskOf(path: string, hunks: readonly Git.Hunk[]): ArmedAsk {
17+
const body = hunks
18+
.flatMap(hunk => [
19+
`@@ -${hunk.oldStart} +${hunk.newStart} @@`,
20+
...hunk.lines,
21+
])
22+
.slice(0, Limits.MAX_LINES_PER_FILE)
23+
24+
return {
25+
path,
26+
text:
27+
`The user attached the diff of ${path} from the diff pane to this ` +
28+
`prompt:\n${body.join('\n')}`,
29+
lines: body.length,
30+
}
31+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* The file the person asked about from the pane: its path and its diff as
3+
* text, held until the next prompt carries it as context.
4+
*/
5+
export type ArmedAsk = {
6+
path: string
7+
text: string
8+
lines: number
9+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export type * from './armed-ask.js'
2+
3+
export * as default from '.'

‎mods/diff/hooks/ask/index.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './armed-ask'
2+
export * from './armed-ask-of.js'
3+
4+
export * as default from '.'
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* A `/`-separated path's last segment: the file's own name.
3+
*
4+
* @param path the repository-relative path
5+
* @returns the base name
6+
*/
7+
export const baseNameOf = (path: string) => path.split('/').at(-1) ?? path

0 commit comments

Comments
 (0)