agent-base-kit 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -152
- package/dist/index.d.mts +197 -38
- package/dist/index.mjs +436 -123
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -4,253 +4,201 @@
|
|
|
4
4
|
|
|
5
5
|
# agent-base-kit
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**The base kit for Claude Code and Codex hooks.**
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/agent-base-kit)
|
|
10
10
|
[](https://www.npmjs.com/package/agent-base-kit)
|
|
11
|
-
[](https://github.com/devmaxxx/agent-kit/actions/workflows/ci.yml)
|
|
11
|
+
[](https://github.com/devmaxxx/agent-base-kit/actions/workflows/ci.yml)
|
|
12
12
|
[](https://www.npmjs.com/package/agent-base-kit)
|
|
13
13
|
[](./package.json)
|
|
14
14
|
[](./package.json)
|
|
15
15
|
[](./LICENSE)
|
|
16
16
|
|
|
17
|
-
[
|
|
17
|
+
[Why](#why) · [What you get](#what-you-get) · [Usage](#usage) · [What install writes](#what-install-writes) · [Fail-closed](#the-fail-closed-contract) · [Kinds](#kinds-and-matchers) · [Other events](#stop-and-userpromptsubmit) · [API](#api)
|
|
18
18
|
|
|
19
19
|
</div>
|
|
20
20
|
|
|
21
|
-
<details>
|
|
22
|
-
<summary><b>Table of contents</b></summary>
|
|
23
|
-
|
|
24
|
-
- [Why](#why)
|
|
25
|
-
- [Features](#features)
|
|
26
|
-
- [Install](#install)
|
|
27
|
-
- [Quick start](#quick-start)
|
|
28
|
-
- [Why a devDependency](#why-a-devdependency)
|
|
29
|
-
- [Kinds and matchers](#kinds-and-matchers)
|
|
30
|
-
- [The fail-closed contract](#the-fail-closed-contract)
|
|
31
|
-
- [What install writes](#what-install-writes)
|
|
32
|
-
- [Shipping as a Claude Code plugin](#shipping-as-a-claude-code-plugin)
|
|
33
|
-
- [API](#api)
|
|
34
|
-
- [Design notes](#design-notes)
|
|
35
|
-
- [Contributing](#contributing)
|
|
36
|
-
- [License](#license)
|
|
37
|
-
|
|
38
|
-
</details>
|
|
39
|
-
|
|
40
21
|
## Why
|
|
41
22
|
|
|
42
|
-
A [PreToolUse hook](https://code.claude.com/docs/en/hooks) is the last point where you can stop an agent's tool call
|
|
23
|
+
A [PreToolUse hook](https://code.claude.com/docs/en/hooks) is the last point where you can stop an agent's tool call. Every hook needs the same plumbing before its own check: read the agent's payload, write the agent's config without breaking anyone else's entries, keep running across Node.js upgrades, and block when the check cannot run, since the agents treat every exit except 2 as "go on".
|
|
43
24
|
|
|
44
|
-
agent-base-kit is that plumbing,
|
|
25
|
+
agent-base-kit is that plumbing, shared by every hook you build. You write the check; reuse the rest for a secret scanner, a policy guard, or any other hook.
|
|
45
26
|
|
|
46
|
-
##
|
|
27
|
+
## What you get
|
|
47
28
|
|
|
48
|
-
- **One function.** `check(call)` gets a typed call (`shell`, `write`, `edit` or
|
|
49
|
-
- **
|
|
50
|
-
- **Both agents.** Claude Code and Codex,
|
|
51
|
-
- **
|
|
52
|
-
- **
|
|
29
|
+
- **One function to write.** `check(call)` gets a typed call (`shell`, `write`, `edit`, `patch`, or a named tool such as an MCP server's) and returns `{ block, reason }`, sync or async.
|
|
30
|
+
- **Stop and UserPromptSubmit too.** Check the reply before the turn ends or the prompt before the model sees it, failing open where a broken hook would trap the session.
|
|
31
|
+
- **Both agents.** Claude Code and Codex, with matchers derived from the kinds you list.
|
|
32
|
+
- **Fail-closed by default.** Unreadable payloads, throws, bad verdicts, hangs and a hook that cannot start all block.
|
|
33
|
+
- **One command to install.** Your bin gets `install` and `uninstall` for `claude` or `codex`, at user or project scope.
|
|
34
|
+
- **Other hooks left alone.** Install and uninstall touch only their own entry, and refuse a config they cannot extend safely.
|
|
35
|
+
- **Survives upgrades.** A user install runs its own copy of your build on the Node.js that installed it, so an nvm or Homebrew upgrade or a rebuild does not switch it off.
|
|
53
36
|
- **Claude Code plugin files.** One call writes the manifest, `hooks.json` and a marketplace.
|
|
54
37
|
- **Zero dependencies**, ESM only, typed.
|
|
55
38
|
|
|
56
|
-
##
|
|
39
|
+
## Usage
|
|
57
40
|
|
|
58
41
|
```sh
|
|
59
42
|
pnpm add -D agent-base-kit
|
|
60
|
-
# or: npm i -D agent-base-kit · yarn add -D agent-base-kit · bun add -d agent-base-kit
|
|
61
43
|
```
|
|
62
44
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
## Quick start
|
|
66
|
-
|
|
67
|
-
A toy check that stops force pushes, as a plugin in three files:
|
|
45
|
+
Write the check and hand it to `runCli` in your bin:
|
|
68
46
|
|
|
69
47
|
```ts
|
|
70
|
-
|
|
71
|
-
import { defineHook } from 'agent-base-kit'
|
|
48
|
+
#!/usr/bin/env node
|
|
49
|
+
import { defineHook, runCli } from 'agent-base-kit'
|
|
72
50
|
|
|
73
|
-
|
|
51
|
+
const plugin = defineHook({
|
|
74
52
|
name: 'no-force-push',
|
|
75
53
|
kinds: ['shell'],
|
|
76
|
-
statusMessage: 'Checking for force pushes',
|
|
77
54
|
check: call => /\bgit\s+push\b.*\s(?:-f|--force)(?:\s|$)/.test(call.command)
|
|
78
55
|
? { block: true, reason: 'Blocked by no-force-push: push without --force, or use --force-with-lease.' }
|
|
79
56
|
: { block: false },
|
|
80
57
|
})
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
```ts
|
|
84
|
-
#!/usr/bin/env node
|
|
85
|
-
// src/cli.ts
|
|
86
|
-
import { runCli } from 'agent-base-kit'
|
|
87
|
-
import { plugin } from './plugin'
|
|
88
58
|
|
|
89
59
|
void runCli(plugin, import.meta.url)
|
|
90
60
|
```
|
|
91
61
|
|
|
92
|
-
|
|
93
|
-
// package.json (the parts that matter)
|
|
94
|
-
{
|
|
95
|
-
"name": "no-force-push",
|
|
96
|
-
"type": "module",
|
|
97
|
-
"bin": { "no-force-push": "./dist/cli.mjs" },
|
|
98
|
-
"files": ["dist"],
|
|
99
|
-
"engines": { "node": ">=22" },
|
|
100
|
-
"devDependencies": {
|
|
101
|
-
"agent-base-kit": "^0.1.0",
|
|
102
|
-
"tsdown": "^0.23.1"
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Build `src/cli.ts` with tsdown. That gives you this CLI:
|
|
62
|
+
Bundle the bin into `.mjs` files with agent-base-kit inlined, for example with tsdown, since the stable copy has no `node_modules`. Your users then run:
|
|
108
63
|
|
|
109
64
|
```sh
|
|
110
65
|
npx no-force-push install claude # ~/.claude/settings.json
|
|
111
66
|
npx no-force-push install codex --project # ./.codex/hooks.json
|
|
112
67
|
npx no-force-push uninstall claude
|
|
113
|
-
npx no-force-push hook # what the agent runs, with the tool call on stdin
|
|
114
68
|
```
|
|
115
69
|
|
|
116
|
-
|
|
70
|
+
Requires Node.js 22 or later and a POSIX shell.
|
|
71
|
+
|
|
72
|
+
## What install writes
|
|
117
73
|
|
|
118
|
-
A user
|
|
74
|
+
A user install (the default scope) does four things:
|
|
119
75
|
|
|
120
|
-
|
|
76
|
+
1. **Copies the build.** The `.mjs` files beside your bin go to `~/.<name>/<build-id>/`, where the id is a hash of their content. The same build reuses its directory, a new build gets a new one, and the copy is staged and renamed in, so a hook never loads a half-written one.
|
|
77
|
+
2. **Runs the copy once.** Before any config points at it, the copy runs on a tool no kind covers. If it cannot start, `install` refuses and changes nothing.
|
|
78
|
+
3. **Swaps `~/.<name>/current`.** One rename moves the link to the new build, so the hook command stays the same across versions and Codex keeps trusting it.
|
|
79
|
+
4. **Writes the hook:**
|
|
121
80
|
|
|
122
|
-
|
|
81
|
+
```sh
|
|
82
|
+
n='/path/to/the/installing/node'; [ -x "$n" ] || n=node; "$n" '/home/you/.<name>/current/cli.mjs' hook --event PreToolUse --agent claude || exit 2
|
|
83
|
+
```
|
|
123
84
|
|
|
124
|
-
|
|
125
|
-
| --- | --- | --- | --- |
|
|
126
|
-
| `shell` | `{ kind: 'shell', command }` | `Bash` | `Bash` |
|
|
127
|
-
| `write` | `{ kind: 'write', path, content }` | `Write`, `NotebookEdit` | `apply_patch` |
|
|
128
|
-
| `edit` | `{ kind: 'edit', path, before, after }` (a MultiEdit's edits joined by newlines) | `Edit`, `MultiEdit` | `apply_patch` |
|
|
129
|
-
| `patch` | `{ kind: 'patch', patch }` (the whole patch text) | none | `apply_patch` |
|
|
85
|
+
`--project` writes `npx --no-install <packageName> hook --event PreToolUse --agent claude || exit 2` instead, which is safe to commit. Each of the plugin's [other events](#stop-and-userpromptsubmit) gets an entry of its own with the same command, naming its own event, minus `|| exit 2`. Uninstall removes every entry and leaves the copy, since another agent may still run it.
|
|
130
86
|
|
|
131
|
-
`
|
|
87
|
+
`--event` tells the hook which event it runs for without reading the payload, so a Stop or UserPromptSubmit payload that arrives cut short or empty still passes instead of being blocked as unreadable. `--agent` tells it which agent to answer, so a prompt block sent to Codex carries only the fields Codex accepts. A command written before the flags existed still works: the hook then takes the event from the payload's `hook_event_name`, and treats a UserPromptSubmit payload carrying `turn_id` (which Codex's input schema requires and Claude Code does not send) as Codex's. Install recognizes those older commands as its own and replaces them; Codex asks to trust the hook once more, since its command changed.
|
|
132
88
|
|
|
133
|
-
|
|
89
|
+
Install replaces any entry it wrote before, at any event, and keeps every other one; an entry an earlier version wrote for an event this one no longer has is removed. It refuses, and changes nothing, when the settings file is not JSON, not an object with a `hooks` object, or has a `PreToolUse`, `Stop` or `UserPromptSubmit` that is not a list, or when `~/.<name>/current` exists and is not a link.
|
|
90
|
+
|
|
91
|
+
`onInstall` runs once the config is written, for what else the hook needs, such as a skill copied into the agent's directory; `onUninstall` runs after uninstall. Each gets the agent, scope, home, working directory, bin and config file, and may return `{ notes, warnings }`, which the command line prints after the config file, to stdout and stderr. A step that throws puts the config and `~/.<name>/current` back as they were and fails the command, so the hooks already installed keep their build.
|
|
92
|
+
|
|
93
|
+
A hook that wrote other commands before it moved to agent-base-kit lists them in `ownCommands`, so install replaces and uninstall removes those entries too.
|
|
134
94
|
|
|
135
95
|
## The fail-closed contract
|
|
136
96
|
|
|
137
|
-
The
|
|
97
|
+
The hook exits 0 only when your check passes the call:
|
|
138
98
|
|
|
139
99
|
| What happens | Exit |
|
|
140
100
|
| --- | --- |
|
|
141
101
|
| `check` returns `{ block: false }` | 0 |
|
|
142
102
|
| `check` returns `{ block: true, reason }` (the reason goes to stderr as is) | 2 |
|
|
143
|
-
| `check` throws or
|
|
144
|
-
|
|
|
145
|
-
| `
|
|
146
|
-
| The
|
|
147
|
-
| The tool is not one of your kinds | 0, and `check` is not called |
|
|
103
|
+
| `check` throws, rejects, never settles, or returns anything else | 2 |
|
|
104
|
+
| The payload is not JSON, names no tool, has a field of the wrong shape, or names an event other than PreToolUse, Stop or UserPromptSubmit | 2 |
|
|
105
|
+
| The command says `--event PreToolUse` and the payload names another event | 2 |
|
|
106
|
+
| The command has an argument `hook` does not take | 2, or 0 when its first valid `--event` names Stop or UserPromptSubmit or, with none, the payload does |
|
|
107
|
+
| The tool is not one of your kinds or `tools` | 0, and `check` is not called |
|
|
148
108
|
| The hook cannot start: Node.js is missing, the build was deleted, a dependency was not bundled | 2, from `\|\| exit 2` |
|
|
149
109
|
| The hook runs on a Node.js older than 22 | 2, with a message naming the fix |
|
|
150
|
-
| The hook times out | Claude Code blocks it (`onFailure: "block"
|
|
151
|
-
|
|
152
|
-
`check` may be async. Keep it well inside `timeout`, which defaults to 10 seconds. Past the timeout Codex runs the call unchecked.
|
|
110
|
+
| The hook times out | Claude Code blocks it (`onFailure: "block"`, Claude Code 2.1.295 or later). Codex lets the call run. |
|
|
153
111
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
The user scope is the default. Install copies the build to `~/.<name>/<build-id>/` and swaps the `~/.<name>/current` link onto it with a rename. Then it writes:
|
|
157
|
-
|
|
158
|
-
```sh
|
|
159
|
-
n='/path/to/the/installing/node'; [ -x "$n" ] || n=node; "$n" '/home/you/.<name>/current/cli.mjs' hook || exit 2
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
- The hook keeps running on the Node.js that installed it, so a project that pins an older Node does not change it.
|
|
163
|
-
- If that Node.js is removed, the hook falls back to the `node` on PATH.
|
|
164
|
-
- The copy outlives the package. An nvm upgrade or an `npx` cache clean does not remove it.
|
|
165
|
-
- The command stays the same across versions installed by the same Node.js, so Codex keeps trusting it.
|
|
112
|
+
Keep `check` well inside `timeout`, which defaults to 10 seconds.
|
|
166
113
|
|
|
167
|
-
|
|
114
|
+
## Kinds and matchers
|
|
168
115
|
|
|
169
|
-
|
|
116
|
+
`kinds` decides which calls reach your check, and each agent's matcher is derived from it.
|
|
170
117
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
118
|
+
| Kind | The call your check gets | Claude Code tools | Codex tools |
|
|
119
|
+
| --- | --- | --- | --- |
|
|
120
|
+
| `shell` | `{ kind: 'shell', command }` | `Bash` | `Bash` |
|
|
121
|
+
| `write` | `{ kind: 'write', path, content }` | `Write`, `NotebookEdit` | `apply_patch` |
|
|
122
|
+
| `edit` | `{ kind: 'edit', path, before, after }` (a MultiEdit's edits one call each) | `Edit`, `MultiEdit` | `apply_patch` |
|
|
123
|
+
| `patch` | `{ kind: 'patch', patch }` (the whole patch text) | none | `apply_patch` |
|
|
124
|
+
| `tool` | `{ kind: 'tool', name, input }` (the tool's input object) | the names in `tools` | the names in `tools` |
|
|
175
125
|
|
|
176
|
-
|
|
126
|
+
`tools` names tools beyond the agents' own, such as `mcp__github__create_pull_request`, and `kinds` lists `tool` exactly when it does. A name holds only letters, digits, `_` and `-`, and may not be a tool a kind already covers (`Bash`, `Write`, `Edit`, `MultiEdit`, `NotebookEdit`, `apply_patch`). A listed tool's input that is not an object blocks the call; an absent one reaches the check as `{}`.
|
|
177
127
|
|
|
178
|
-
|
|
128
|
+
Codex sends every file change as one `apply_patch`, so `install codex` refuses a plugin with `write` or `edit` but no `patch`. A plugin with no tool in an agent, such as `['patch']` for Claude Code, is refused too.
|
|
179
129
|
|
|
180
|
-
##
|
|
130
|
+
## Stop and UserPromptSubmit
|
|
181
131
|
|
|
182
|
-
|
|
132
|
+
`events` adds checks on two more events, installed beside PreToolUse:
|
|
183
133
|
|
|
184
134
|
```ts
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
description: 'Stops force pushes',
|
|
199
|
-
author: { name: 'you' },
|
|
200
|
-
license: 'MIT',
|
|
201
|
-
marketplace: { name: 'no-force-push', owner: { name: 'you' } },
|
|
202
|
-
})
|
|
135
|
+
function verdict(text: string): HookVerdict {
|
|
136
|
+
return /\bsk-[\w-]{20,}/.test(text)
|
|
137
|
+
? { block: true, reason: 'no-pasted-keys: reference the key through an environment variable instead.' }
|
|
138
|
+
: { block: false }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
defineHook({
|
|
142
|
+
name: 'no-pasted-keys',
|
|
143
|
+
kinds: ['shell'],
|
|
144
|
+
check: call => verdict(call.command),
|
|
145
|
+
events: {
|
|
146
|
+
Stop: { check: ({ reply }) => verdict(reply) },
|
|
147
|
+
UserPromptSubmit: { check: ({ prompt }) => verdict(prompt), hidePrompt: true },
|
|
203
148
|
},
|
|
204
149
|
})
|
|
205
150
|
```
|
|
206
151
|
|
|
207
|
-
|
|
152
|
+
A block goes to stdout as `{"decision":"block","reason":…}`. At Stop the agent keeps going with the reason as its next instruction; at UserPromptSubmit it drops the prompt and shows the reason, and `hidePrompt` keeps Claude Code from printing the prompt back under it (Codex has no such field, and a reply carrying it would let the prompt through, so it is not sent there). Both fail open, since an exit 2 at Stop would keep the agent going forever and one at UserPromptSubmit would drop every prompt:
|
|
208
153
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
/plugin install no-force-push@no-force-push
|
|
218
|
-
```
|
|
154
|
+
| What happens | Exit |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| `check` returns `{ block: true, reason }` | 0, with the decision on stdout |
|
|
157
|
+
| `check` returns `{ block: false }` | 0, silently |
|
|
158
|
+
| `check` throws, rejects, never settles, returns anything else, or the field has the wrong shape | 0, with the cause on stderr |
|
|
159
|
+
| With `--event`: the payload is not JSON, is empty or cut short, or names another event | 0, with the cause on stderr |
|
|
160
|
+
| A Stop payload with `stop_hook_active`, the reply to the hook's own block | 0, and `check` is not called |
|
|
161
|
+
| The hook cannot start, or runs on a Node.js older than 22 | anything but 2: the event passes |
|
|
219
162
|
|
|
220
|
-
|
|
163
|
+
Each event takes its own `statusMessage` and `timeout`, defaulting to the hook's. Without `--event`, a payload that is not JSON is blocked with exit 2 whatever event it was meant for, which is why install writes the flag. A payload with no `hook_event_name` is read as PreToolUse; one naming any other event (`PostToolUse`, `pre_tool_use`, an empty string) is blocked with exit 2, since the tool call it may carry would otherwise run unchecked.
|
|
221
164
|
|
|
222
165
|
## API
|
|
223
166
|
|
|
224
167
|
```ts
|
|
225
168
|
function defineHook<const K extends ToolKind>(definition: HookDefinition<K>): HookPlugin
|
|
226
|
-
function runHook(plugin: HookPlugin, stdin: string, options?: { cwd?: string, stderr?: (text: string) => void }): Promise<number>
|
|
227
|
-
function createCli(plugin: HookPlugin, options?: { bin?: string }): (argv: readonly string[], io: CliIO) => Promise<number>
|
|
228
169
|
function runCli(plugin: HookPlugin, entryUrl: string, options?: { bin?: string }): Promise<void>
|
|
170
|
+
function createCli(plugin: HookPlugin, options?: { bin?: string }): (argv: readonly string[], io: CliIO) => Promise<number>
|
|
171
|
+
function runHook(plugin: HookPlugin, stdin: string, options?: { cwd?: string, stderr?: (text: string) => void, stdout?: (text: string) => void, event?: HookEvent, agent?: Agent }): Promise<number>
|
|
229
172
|
function readStdin(stream?: AsyncIterable<unknown>): Promise<string>
|
|
230
|
-
function install(options: InstallOptions): { file
|
|
231
|
-
function uninstall(options: UninstallOptions):
|
|
173
|
+
function install(options: InstallOptions): InstallResult // { file, command, commands, notes, warnings }
|
|
174
|
+
function uninstall(options: UninstallOptions): InstallResult
|
|
232
175
|
function configFile(agent: Agent, scope: Scope, place: { home: string, cwd: string }): string
|
|
233
176
|
function matcherFor(agent: Agent, kinds: readonly ToolKind[]): string
|
|
234
177
|
function claudePluginFiles(options: ClaudePluginOptions): Record<string, string>
|
|
235
178
|
function writeClaudePlugin(dir: string, options: ClaudePluginOptions): string[]
|
|
236
179
|
```
|
|
237
180
|
|
|
238
|
-
`
|
|
181
|
+
`defineHook` takes `name`, `kinds` and `check`, and optionally:
|
|
239
182
|
|
|
240
|
-
|
|
183
|
+
| Field | Default | What it does |
|
|
184
|
+
| --- | --- | --- |
|
|
185
|
+
| `packageName` | `name` | The npm package a project install runs through `npx --no-install`. |
|
|
186
|
+
| `statusMessage` | `Running <name>` | What the agent shows while the hook runs. |
|
|
187
|
+
| `timeout` | `10` | Seconds the agent waits for the hook. |
|
|
188
|
+
| `ownCommands` | `[]` | Patterns for commands an earlier version of your hook wrote, which install replaces and uninstall removes, at any event. A pattern must match the whole command, not a part of it; the `g`, `m` and `y` flags are refused. |
|
|
189
|
+
| `tools` | `[]` | Tools beyond the agents' own, checked as `tool` calls. |
|
|
190
|
+
| `events` | `{}` | `Stop` and `UserPromptSubmit` checks, each with an optional `statusMessage` and `timeout`, and `hidePrompt` for UserPromptSubmit. |
|
|
191
|
+
| `onInstall`, `onUninstall` | none | Steps run after the config is written; see [What install writes](#what-install-writes). They do not run for a Claude Code plugin. |
|
|
241
192
|
|
|
242
|
-
|
|
243
|
-
- **The reason is printed as is.** Your plugin owns its message, so start it with the plugin's name. Errors get a `Blocked by <name>, it could not check the call: …` prefix.
|
|
244
|
-
- **Which entries count as its own.** The `npx` form must match exactly. The pinned form matches any file under `~/.<name>/current/`, so a bin renamed between releases still replaces its old entry. The bare `node '<path>' hook` form from before the copy existed needs a directory named exactly after the plugin. So a hook that only mentions the plugin, or another plugin whose path merely contains this one's name, is never removed.
|
|
245
|
-
- **POSIX shells only.** Hook commands rely on `|| exit 2` and single-quoted paths, so Windows is not supported yet.
|
|
193
|
+
`runCli` is the only function that reads the home directory, the working directory and the Node.js path from the process; the others take them as arguments, which keeps tests away from your real `~`. `writeClaudePlugin` writes the files to ship the same hook as a Claude Code plugin instead; use either the plugin or `install claude`, not both.
|
|
246
194
|
|
|
247
|
-
|
|
195
|
+
The commands install and `writeClaudePlugin` write pass `--event` and `--agent`, which `createCli` hands to `runHook`. A bin that wires its own hook command passes them as `runHook`'s `event` and `agent` options, so a cut-short payload at Stop or UserPromptSubmit passes there too.
|
|
248
196
|
|
|
249
|
-
|
|
197
|
+
## Contributing
|
|
250
198
|
|
|
251
199
|
```sh
|
|
252
|
-
git clone https://github.com/devmaxxx/agent-kit.git
|
|
253
|
-
cd agent-kit
|
|
200
|
+
git clone https://github.com/devmaxxx/agent-base-kit.git
|
|
201
|
+
cd agent-base-kit
|
|
254
202
|
pnpm install
|
|
255
203
|
pnpm lint && pnpm typecheck && pnpm test
|
|
256
204
|
```
|