@weave_protocol/adapter-claudecode 0.1.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 +254 -0
- package/SKILL.md +83 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +237 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +102 -0
- package/dist/config.js.map +1 -0
- package/dist/hook.d.ts +31 -0
- package/dist/hook.d.ts.map +1 -0
- package/dist/hook.js +259 -0
- package/dist/hook.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +72 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# 🛡️ @weave_protocol/adapter-claudecode
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@weave_protocol/adapter-claudecode)
|
|
4
|
+
[](https://www.npmjs.com/package/@weave_protocol/adapter-claudecode)
|
|
5
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
6
|
+
|
|
7
|
+
**WARD.md enforcement for Claude Code, via Claude Code's native hook system.**
|
|
8
|
+
|
|
9
|
+
> *AGENTS.md tells your agent what to do. SKILL.md tells your agent how to do it. **WARD.md tells your agent what it can't.** This package makes Claude Code listen.*
|
|
10
|
+
|
|
11
|
+
Part of the [Weave Protocol](https://github.com/Tyox-all/Weave_Protocol) security suite. First in a planned series of cross-platform harness adapters (Claude Code → Antigravity → MDASH).
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## What it does
|
|
16
|
+
|
|
17
|
+
When Claude Code is about to use a tool (Bash, Edit, Write, Read, WebFetch, etc.), this adapter intercepts via Claude Code's `PreToolUse` hook, reads your project's `WARD.md`, and blocks the call if it violates the declared policy.
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
You: "Delete all the SSH keys"
|
|
21
|
+
Claude Code: [about to run Bash with `rm -rf ~/.ssh`]
|
|
22
|
+
↓
|
|
23
|
+
PreToolUse hook fires
|
|
24
|
+
↓
|
|
25
|
+
weave-claude-code reads ./WARD.md
|
|
26
|
+
↓
|
|
27
|
+
checkFilesystem('delete', '~/.ssh/**') → DENY
|
|
28
|
+
↓
|
|
29
|
+
Returns { "decision": "block", "reason": "🛡️ WARD: ..." }
|
|
30
|
+
↓
|
|
31
|
+
Claude Code refuses, shows reason
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
No code changes needed in your project. Just a `WARD.md` file and a one-time hook install.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Install once
|
|
42
|
+
npm install -g @weave_protocol/adapter-claudecode
|
|
43
|
+
|
|
44
|
+
# Wire it into Claude Code's hook config
|
|
45
|
+
weave-claude-code init
|
|
46
|
+
|
|
47
|
+
# Drop a WARD.md in your project root
|
|
48
|
+
npx @weave_protocol/ward init
|
|
49
|
+
|
|
50
|
+
# Done. Open Claude Code in that project — every tool call is now gated by WARD.
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Verify with:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
weave-claude-code status
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## How WARD.md is resolved
|
|
62
|
+
|
|
63
|
+
When the hook fires, it looks for a WARD.md in this order:
|
|
64
|
+
|
|
65
|
+
1. `$WEAVE_WARD_PATH` — explicit env var override
|
|
66
|
+
2. `<project>/WARD.md` — project-level (CWD from the hook payload)
|
|
67
|
+
3. `<project>/.weave/WARD.md` — alternate project location
|
|
68
|
+
4. `~/.claude/WARD.md` — **user-global fallback** (applies to every Claude Code session)
|
|
69
|
+
|
|
70
|
+
The first one that exists wins. If none exists, the hook silently passes through — zero impact on workflows that haven't adopted WARD yet.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Tool mapping
|
|
75
|
+
|
|
76
|
+
Claude Code's tools are mapped to generic capability names so a single WARD.md works across platforms:
|
|
77
|
+
|
|
78
|
+
| Claude Code tool | WARD capability | Path arg | Implicit fs op |
|
|
79
|
+
|---|---|---|---|
|
|
80
|
+
| `Bash` | `shell_exec` | (heuristic scan of command) | varies |
|
|
81
|
+
| `Edit` / `MultiEdit` / `Write` | `file_write` | `file_path` | `write` |
|
|
82
|
+
| `Read` | `file_read` | `file_path` | `read` |
|
|
83
|
+
| `Grep` / `LS` | `file_read` / `file_list` | `path` | `read` / `list` |
|
|
84
|
+
| `Glob` | `file_list` | `path` | `list` |
|
|
85
|
+
| `WebFetch` | `http_request` | (uses `url`) | — |
|
|
86
|
+
| `WebSearch` | `web_search` | — | — |
|
|
87
|
+
| `Task` | `subagent` | — | — |
|
|
88
|
+
| `TodoWrite` | `todo_write` | — | — |
|
|
89
|
+
| `NotebookEdit` | `notebook_edit` | `notebook_path` | `write` |
|
|
90
|
+
|
|
91
|
+
Your WARD.md can use either name — `Bash` or `shell_exec`, `Edit` or `file_write`. The stricter rule wins.
|
|
92
|
+
|
|
93
|
+
For `Bash`, the adapter also runs a light heuristic scan of the command string to catch obvious violations (URLs to denied domains, references to sensitive paths like `~/.ssh`, `~/.aws`, `/etc/`).
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Commands
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
weave-claude-code init [--matcher=X] [--fail-closed]
|
|
101
|
+
Install the WARD pre-tool-use hook.
|
|
102
|
+
--matcher=Bash|Edit|... Restrict to specific tools (default: all)
|
|
103
|
+
--fail-closed Block on policy errors (default: fail-open)
|
|
104
|
+
|
|
105
|
+
weave-claude-code disable
|
|
106
|
+
Remove the WARD hook from ~/.claude/settings.json
|
|
107
|
+
(your other hooks are not touched)
|
|
108
|
+
|
|
109
|
+
weave-claude-code status
|
|
110
|
+
Show hook installation state + active WARD policy for current dir
|
|
111
|
+
|
|
112
|
+
weave-claude-code test <tool> [--input=JSON]
|
|
113
|
+
Dry-run a tool call against your WARD.md without invoking Claude Code
|
|
114
|
+
|
|
115
|
+
weave-claude-code help
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Examples
|
|
119
|
+
|
|
120
|
+
Test whether `rm -rf ~/.ssh` would be allowed:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
weave-claude-code test Bash --input='{"command":"rm -rf ~/.ssh"}'
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Test a file write to /etc/:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
weave-claude-code test Write --input='{"file_path":"/etc/passwd","content":"..."}'
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Test a fetch to a disallowed domain:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
weave-claude-code test WebFetch --input='{"url":"https://evil.example.com/exfil"}'
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Fail-open vs fail-closed
|
|
141
|
+
|
|
142
|
+
By default, the hook is **fail-open**: if WARD.md is missing, malformed, or evaluation throws, the tool call is **allowed** and a warning is written to stderr. This is the right default for adoption — broken policy files shouldn't break your workflow.
|
|
143
|
+
|
|
144
|
+
For higher-stakes contexts, install with `--fail-closed`:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
weave-claude-code init --fail-closed
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Then a missing or broken policy blocks the call.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## What gets installed
|
|
155
|
+
|
|
156
|
+
`weave-claude-code init` adds an entry to `~/.claude/settings.json`:
|
|
157
|
+
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"hooks": {
|
|
161
|
+
"PreToolUse": [
|
|
162
|
+
{
|
|
163
|
+
"matcher": "*",
|
|
164
|
+
"hooks": [
|
|
165
|
+
{
|
|
166
|
+
"type": "command",
|
|
167
|
+
"command": "weave-claude-code hook pre-tool-use # WEAVE_WARD_HOOK",
|
|
168
|
+
"timeout": 5
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The `# WEAVE_WARD_HOOK` marker is how `disable` finds and removes only our entry without affecting other hooks you've added manually.
|
|
178
|
+
|
|
179
|
+
A backup of your existing `settings.json` is written to `settings.json.weave-backup` before any modification.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## A sample WARD.md for Claude Code
|
|
184
|
+
|
|
185
|
+
```markdown
|
|
186
|
+
---
|
|
187
|
+
ward: "1.0"
|
|
188
|
+
agent: my-coding-agent
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
# WARD.md
|
|
192
|
+
|
|
193
|
+
## Filesystem
|
|
194
|
+
|
|
195
|
+
allow:
|
|
196
|
+
- read: /Users/me/projects/**
|
|
197
|
+
- write: /Users/me/projects/**
|
|
198
|
+
deny:
|
|
199
|
+
- read: ~/.ssh/**
|
|
200
|
+
- read: ~/.aws/**
|
|
201
|
+
- write: /etc/**
|
|
202
|
+
default: deny
|
|
203
|
+
|
|
204
|
+
## Network
|
|
205
|
+
|
|
206
|
+
allow:
|
|
207
|
+
- url: "https://api.github.com/**"
|
|
208
|
+
- url: "https://registry.npmjs.org/**"
|
|
209
|
+
default: deny
|
|
210
|
+
|
|
211
|
+
## Capabilities
|
|
212
|
+
|
|
213
|
+
allow:
|
|
214
|
+
- file_read
|
|
215
|
+
- file_write
|
|
216
|
+
- file_list
|
|
217
|
+
requireApproval:
|
|
218
|
+
- shell_exec
|
|
219
|
+
- http_request
|
|
220
|
+
deny:
|
|
221
|
+
- subagent
|
|
222
|
+
default: deny
|
|
223
|
+
|
|
224
|
+
## Behavioral Limits
|
|
225
|
+
|
|
226
|
+
maxIterations: 50
|
|
227
|
+
maxCostUSD: 10.00
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
With this loaded, Claude Code is allowed to read/write inside `~/projects` but cannot touch SSH/AWS keys, will require approval before running shell commands, and is hard-blocked from spawning subagents.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Programmatic use
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import {
|
|
238
|
+
resolveWardForCwd,
|
|
239
|
+
evaluateCall,
|
|
240
|
+
installHook,
|
|
241
|
+
} from '@weave_protocol/adapter-claudecode';
|
|
242
|
+
|
|
243
|
+
const resolved = resolveWardForCwd(process.cwd());
|
|
244
|
+
if (resolved) {
|
|
245
|
+
const decision = evaluateCall(resolved.policy, 'Bash', { command: 'rm -rf /' });
|
|
246
|
+
console.log(decision.decision); // "deny"
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
Apache 2.0 — see [LICENSE](../LICENSE).
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: adapter-claudecode
|
|
3
|
+
description: Use this skill when the user wants to enforce a WARD.md security policy inside Claude Code via its native hook system. Triggers on requests to "secure my Claude Code session", "block Claude Code from doing X", "install WARD hooks in Claude Code", "make Claude Code respect my policy file", or any work involving the `weave-claude-code` command. Also useful when reviewing a user's `~/.claude/settings.json` for hook configuration, or when a user reports that Claude Code is doing things they want stopped.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude Code adapter for Weave Protocol
|
|
7
|
+
|
|
8
|
+
The `@weave_protocol/adapter-claudecode` package enforces [WARD.md](https://www.npmjs.com/package/@weave_protocol/ward) policies inside Claude Code via the `PreToolUse` hook system. When Claude Code is about to use a tool, the adapter reads the active WARD.md and blocks the call if it violates the policy.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
|
|
12
|
+
- User runs Claude Code and wants visibility/control over what it does
|
|
13
|
+
- User has a WARD.md and wants it enforced inside Claude Code (not just on MCP servers)
|
|
14
|
+
- User wants to lock down a sensitive project so Claude Code can't escape its bounds
|
|
15
|
+
- User has a global `~/.claude/WARD.md` they want applied to every session
|
|
16
|
+
- User wants to test what their WARD policy would do before deploying it
|
|
17
|
+
|
|
18
|
+
## Commands
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
weave-claude-code init [--matcher=X] [--fail-closed] # Install hook
|
|
22
|
+
weave-claude-code disable # Remove hook
|
|
23
|
+
weave-claude-code status # Show config + active policy
|
|
24
|
+
weave-claude-code test <tool> [--input=JSON] # Dry-run a tool call
|
|
25
|
+
weave-claude-code help
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## WARD resolution order
|
|
29
|
+
|
|
30
|
+
When the hook fires:
|
|
31
|
+
|
|
32
|
+
1. `$WEAVE_WARD_PATH` — explicit override
|
|
33
|
+
2. `<cwd>/WARD.md` — project root
|
|
34
|
+
3. `<cwd>/.weave/WARD.md` — alternate location
|
|
35
|
+
4. `~/.claude/WARD.md` — user-global fallback
|
|
36
|
+
|
|
37
|
+
First match wins. If none exists, the hook passes through silently.
|
|
38
|
+
|
|
39
|
+
## Tool mapping
|
|
40
|
+
|
|
41
|
+
Claude Code → WARD capability:
|
|
42
|
+
|
|
43
|
+
- `Bash` → `shell_exec` (also scans command string heuristically)
|
|
44
|
+
- `Edit`, `MultiEdit`, `Write` → `file_write`
|
|
45
|
+
- `Read` → `file_read`
|
|
46
|
+
- `Grep`, `LS`, `Glob` → `file_read` / `file_list`
|
|
47
|
+
- `WebFetch` → `http_request` (checks `url`)
|
|
48
|
+
- `WebSearch` → `web_search`
|
|
49
|
+
- `Task` → `subagent`
|
|
50
|
+
- `NotebookEdit` → `notebook_edit`
|
|
51
|
+
|
|
52
|
+
WARD.md can use either the literal Claude tool name (e.g., `Bash`) or the generic capability (`shell_exec`). The stricter rule wins.
|
|
53
|
+
|
|
54
|
+
## Decision rules
|
|
55
|
+
|
|
56
|
+
| Situation | Action |
|
|
57
|
+
|---|---|
|
|
58
|
+
| User wants to enforce WARD in Claude Code | `weave-claude-code init` |
|
|
59
|
+
| User wants to test their policy | `weave-claude-code test <tool>` |
|
|
60
|
+
| User wants global policy across all projects | Write `~/.claude/WARD.md` |
|
|
61
|
+
| User wants project-specific policy | Write `<project>/WARD.md` |
|
|
62
|
+
| User wants stricter failure mode | `init --fail-closed` |
|
|
63
|
+
| User wants to uninstall | `weave-claude-code disable` |
|
|
64
|
+
| User reports Claude Code blocked unexpectedly | `weave-claude-code status` (shows active policy and source) |
|
|
65
|
+
|
|
66
|
+
## Fail modes
|
|
67
|
+
|
|
68
|
+
- **fail-open** (default): broken or missing WARD.md → allow the call, warn to stderr
|
|
69
|
+
- **fail-closed** (`--fail-closed`): broken or missing WARD.md → block the call
|
|
70
|
+
|
|
71
|
+
Default is fail-open because broken policy files shouldn't break the user's workflow. Recommend fail-closed only for high-stakes environments (production deployment agents, etc.).
|
|
72
|
+
|
|
73
|
+
## Pairs with
|
|
74
|
+
|
|
75
|
+
- `@weave_protocol/ward` — the policy format being enforced
|
|
76
|
+
- `@weave_protocol/hundredmen` — enforces the same WARD.md on MCP servers (complementary)
|
|
77
|
+
- `@weave_protocol/cli` — `weave init` scaffolds projects with a WARD.md
|
|
78
|
+
|
|
79
|
+
## Anti-patterns
|
|
80
|
+
|
|
81
|
+
- **Don't hand-edit `~/.claude/settings.json` to add the hook.** Use `weave-claude-code init` so it backs up your config and stays idempotent.
|
|
82
|
+
- **Don't ship a project with `--fail-closed` and no WARD.md.** That blocks every tool call. Either ship a WARD.md or stay fail-open.
|
|
83
|
+
- **Don't expect WARD to catch semantic intent drift.** WARD is policy-as-code. For drift between declared intent and actual behavior, pair this with `hundredmen_declare_intent`.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* weave-claude-code CLI
|
|
4
|
+
*
|
|
5
|
+
* weave-claude-code init [--matcher=X] [--fail-closed] Install the WARD hook
|
|
6
|
+
* weave-claude-code disable Remove the WARD hook
|
|
7
|
+
* weave-claude-code status Show current config + WARD
|
|
8
|
+
* weave-claude-code test <tool> [--input=JSON] Dry-run a tool call
|
|
9
|
+
* weave-claude-code hook <event> [--fail-closed] Hook handler (called by CC)
|
|
10
|
+
* weave-claude-code help
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* weave-claude-code CLI
|
|
4
|
+
*
|
|
5
|
+
* weave-claude-code init [--matcher=X] [--fail-closed] Install the WARD hook
|
|
6
|
+
* weave-claude-code disable Remove the WARD hook
|
|
7
|
+
* weave-claude-code status Show current config + WARD
|
|
8
|
+
* weave-claude-code test <tool> [--input=JSON] Dry-run a tool call
|
|
9
|
+
* weave-claude-code hook <event> [--fail-closed] Hook handler (called by CC)
|
|
10
|
+
* weave-claude-code help
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync } from 'node:fs';
|
|
13
|
+
import { resolve } from 'node:path';
|
|
14
|
+
import { installHook, removeHook, readSettings, isHookInstalled, claudeSettingsPath, claudeConfigDir, userWardPath, } from './config.js';
|
|
15
|
+
import { resolveWardForCwd, evaluateCall, runPreToolUseHook } from './hook.js';
|
|
16
|
+
// ─── ANSI colors ───────────────────────────────────────────────
|
|
17
|
+
const tty = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
18
|
+
const c = {
|
|
19
|
+
reset: tty ? '\x1b[0m' : '',
|
|
20
|
+
bold: tty ? '\x1b[1m' : '',
|
|
21
|
+
dim: tty ? '\x1b[2m' : '',
|
|
22
|
+
red: tty ? '\x1b[31m' : '',
|
|
23
|
+
green: tty ? '\x1b[32m' : '',
|
|
24
|
+
yellow: tty ? '\x1b[33m' : '',
|
|
25
|
+
blue: tty ? '\x1b[34m' : '',
|
|
26
|
+
cyan: tty ? '\x1b[36m' : '',
|
|
27
|
+
gray: tty ? '\x1b[90m' : '',
|
|
28
|
+
};
|
|
29
|
+
function banner() {
|
|
30
|
+
console.error(`
|
|
31
|
+
${c.cyan}${c.bold}🛡️ weave-claude-code${c.reset}
|
|
32
|
+
${c.gray}WARD.md enforcement for Claude Code via hooks${c.reset}
|
|
33
|
+
`);
|
|
34
|
+
}
|
|
35
|
+
// ─── help ──────────────────────────────────────────────────────
|
|
36
|
+
function help() {
|
|
37
|
+
banner();
|
|
38
|
+
console.error(`${c.bold}Usage:${c.reset} weave-claude-code <command> [options]\n`);
|
|
39
|
+
console.error(`${c.bold}Commands:${c.reset}`);
|
|
40
|
+
console.error(` ${c.cyan}init${c.reset} ${c.gray}[--matcher=X] [--fail-closed]${c.reset} Install the WARD pre-tool-use hook`);
|
|
41
|
+
console.error(` ${c.cyan}disable${c.reset} Remove the WARD hook`);
|
|
42
|
+
console.error(` ${c.cyan}status${c.reset} Show hook installation + active WARD policy`);
|
|
43
|
+
console.error(` ${c.cyan}test${c.reset} ${c.gray}<tool> [--input=JSON]${c.reset} Dry-run a tool call against your WARD.md`);
|
|
44
|
+
console.error(` ${c.cyan}hook${c.reset} ${c.gray}<event>${c.reset} Internal: called by Claude Code as a hook`);
|
|
45
|
+
console.error(` ${c.cyan}help${c.reset} Show this message\n`);
|
|
46
|
+
console.error(`${c.bold}Examples:${c.reset}`);
|
|
47
|
+
console.error(` ${c.gray}$${c.reset} weave-claude-code init`);
|
|
48
|
+
console.error(` ${c.gray}$${c.reset} weave-claude-code status`);
|
|
49
|
+
console.error(` ${c.gray}$${c.reset} weave-claude-code test Bash --input='{"command":"rm -rf ~/.ssh"}'`);
|
|
50
|
+
console.error(` ${c.gray}$${c.reset} weave-claude-code disable\n`);
|
|
51
|
+
console.error(`${c.gray}Docs: https://github.com/Tyox-all/Weave_Protocol/tree/main/adapter-claudecode${c.reset}\n`);
|
|
52
|
+
}
|
|
53
|
+
// ─── init ──────────────────────────────────────────────────────
|
|
54
|
+
function parseFlag(args, name) {
|
|
55
|
+
for (const a of args) {
|
|
56
|
+
if (a === `--${name}`)
|
|
57
|
+
return '';
|
|
58
|
+
if (a.startsWith(`--${name}=`))
|
|
59
|
+
return a.slice(`--${name}=`.length);
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
function hasFlag(args, name) {
|
|
64
|
+
return args.some((a) => a === `--${name}` || a.startsWith(`--${name}=`));
|
|
65
|
+
}
|
|
66
|
+
function runInit(args) {
|
|
67
|
+
banner();
|
|
68
|
+
const matcher = parseFlag(args, 'matcher') || undefined;
|
|
69
|
+
const failClosed = hasFlag(args, 'fail-closed');
|
|
70
|
+
const settings = readSettings();
|
|
71
|
+
const wasInstalled = isHookInstalled(settings);
|
|
72
|
+
installHook({
|
|
73
|
+
toolMatcher: matcher,
|
|
74
|
+
failClosed,
|
|
75
|
+
timeoutSeconds: 5,
|
|
76
|
+
});
|
|
77
|
+
const verb = wasInstalled ? 'Updated' : 'Installed';
|
|
78
|
+
console.error(`${c.green}✓${c.reset} ${verb} WARD hook in ${c.cyan}${claudeSettingsPath()}${c.reset}`);
|
|
79
|
+
console.error(` ${c.gray}matcher:${c.reset} ${matcher || '*'}`);
|
|
80
|
+
console.error(` ${c.gray}fail mode:${c.reset} ${failClosed ? c.red + 'closed' + c.reset : c.green + 'open' + c.reset}`);
|
|
81
|
+
console.error('');
|
|
82
|
+
console.error(`Next:`);
|
|
83
|
+
console.error(` ${c.gray}1.${c.reset} Drop a ${c.cyan}WARD.md${c.reset} in your project root (or at ${c.cyan}~/.claude/WARD.md${c.reset} for a user-global policy)`);
|
|
84
|
+
console.error(` ${c.gray}2.${c.reset} Open Claude Code in that project — the hook will gate tool calls automatically`);
|
|
85
|
+
console.error(` ${c.gray}3.${c.reset} Run ${c.cyan}weave-claude-code status${c.reset} to verify`);
|
|
86
|
+
console.error('');
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
// ─── disable ───────────────────────────────────────────────────
|
|
90
|
+
function runDisable() {
|
|
91
|
+
banner();
|
|
92
|
+
const result = removeHook();
|
|
93
|
+
if (result.removed === 0) {
|
|
94
|
+
console.error(`${c.yellow}⚠${c.reset} No WARD hooks found in ${claudeSettingsPath()}.`);
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
console.error(`${c.green}✓${c.reset} Removed ${result.removed} WARD hook entry from ${c.cyan}${claudeSettingsPath()}${c.reset}`);
|
|
98
|
+
console.error(`${c.gray} (your other hooks were not touched)${c.reset}`);
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
// ─── status ────────────────────────────────────────────────────
|
|
102
|
+
function runStatus() {
|
|
103
|
+
banner();
|
|
104
|
+
const settingsPath = claudeSettingsPath();
|
|
105
|
+
const exists = existsSync(settingsPath);
|
|
106
|
+
console.error(`${c.bold}Claude Code config${c.reset}`);
|
|
107
|
+
console.error(`${c.gray}─${c.reset}`.repeat(60));
|
|
108
|
+
console.error(` ${c.gray}Config dir:${c.reset} ${c.cyan}${claudeConfigDir()}${c.reset}`);
|
|
109
|
+
console.error(` ${c.gray}settings.json:${c.reset} ${exists ? c.green + 'present' + c.reset : c.red + 'missing' + c.reset}`);
|
|
110
|
+
if (exists) {
|
|
111
|
+
const settings = readSettings();
|
|
112
|
+
const installed = isHookInstalled(settings);
|
|
113
|
+
console.error(` ${c.gray}WARD hook:${c.reset} ${installed ? c.green + 'installed' + c.reset : c.yellow + 'not installed' + c.reset}`);
|
|
114
|
+
}
|
|
115
|
+
console.error('');
|
|
116
|
+
console.error(`${c.bold}Active WARD policy${c.reset}`);
|
|
117
|
+
console.error(`${c.gray}─${c.reset}`.repeat(60));
|
|
118
|
+
try {
|
|
119
|
+
const resolved = resolveWardForCwd(process.cwd());
|
|
120
|
+
if (!resolved) {
|
|
121
|
+
console.error(` ${c.yellow}No WARD.md found${c.reset}`);
|
|
122
|
+
console.error(` ${c.gray}Looked at:${c.reset}`);
|
|
123
|
+
if (process.env.WEAVE_WARD_PATH)
|
|
124
|
+
console.error(` ${c.gray}•${c.reset} ${process.env.WEAVE_WARD_PATH} ${c.gray}(WEAVE_WARD_PATH)${c.reset}`);
|
|
125
|
+
console.error(` ${c.gray}•${c.reset} ${resolve(process.cwd(), 'WARD.md')}`);
|
|
126
|
+
console.error(` ${c.gray}•${c.reset} ${resolve(process.cwd(), '.weave', 'WARD.md')}`);
|
|
127
|
+
console.error(` ${c.gray}•${c.reset} ${userWardPath()} ${c.gray}(user-global)${c.reset}`);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
console.error(` ${c.gray}Source:${c.reset} ${c.cyan}${resolved.source}${c.reset}`);
|
|
131
|
+
console.error(` ${c.gray}Name:${c.reset} ${resolved.policy.name || c.gray + '(unnamed)' + c.reset}`);
|
|
132
|
+
console.error(` ${c.gray}Agent:${c.reset} ${resolved.policy.agent || c.gray + '(none)' + c.reset}`);
|
|
133
|
+
console.error(` ${c.gray}WARD version:${c.reset} ${resolved.policy.version}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
console.error(` ${c.red}Error loading WARD.md:${c.reset} ${err instanceof Error ? err.message : String(err)}`);
|
|
138
|
+
return 1;
|
|
139
|
+
}
|
|
140
|
+
console.error('');
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
// ─── test ──────────────────────────────────────────────────────
|
|
144
|
+
function runTest(args) {
|
|
145
|
+
const tool = args[0];
|
|
146
|
+
if (!tool) {
|
|
147
|
+
console.error(`${c.red}✗${c.reset} Usage: weave-claude-code test <tool> [--input='{"file_path":"..."}']`);
|
|
148
|
+
return 2;
|
|
149
|
+
}
|
|
150
|
+
banner();
|
|
151
|
+
const inputArg = parseFlag(args, 'input');
|
|
152
|
+
let toolInput = {};
|
|
153
|
+
if (inputArg) {
|
|
154
|
+
try {
|
|
155
|
+
toolInput = JSON.parse(inputArg);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
console.error(`${c.red}✗${c.reset} --input must be valid JSON`);
|
|
159
|
+
return 2;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const resolved = resolveWardForCwd(process.cwd());
|
|
163
|
+
if (!resolved) {
|
|
164
|
+
console.error(`${c.yellow}⚠${c.reset} No WARD.md found — call would be allowed by default.`);
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
console.error(`Testing ${c.bold}${tool}${c.reset}${inputArg ? ' with ' + c.cyan + JSON.stringify(toolInput) + c.reset : ''}`);
|
|
168
|
+
console.error(`Policy: ${c.cyan}${resolved.source}${c.reset}\n`);
|
|
169
|
+
const result = evaluateCall(resolved.policy, tool, toolInput);
|
|
170
|
+
const colorMap = {
|
|
171
|
+
allow: c.green,
|
|
172
|
+
deny: c.red,
|
|
173
|
+
require_approval: c.yellow,
|
|
174
|
+
};
|
|
175
|
+
const iconMap = { allow: '✓', deny: '✗', require_approval: '⚠' };
|
|
176
|
+
console.error(`Decision: ${colorMap[result.decision]}${iconMap[result.decision]} ${result.decision.toUpperCase()}${c.reset}`);
|
|
177
|
+
if (result.reasons.length > 0) {
|
|
178
|
+
console.error(`\nReasons:`);
|
|
179
|
+
for (const r of result.reasons)
|
|
180
|
+
console.error(` ${c.gray}•${c.reset} ${r}`);
|
|
181
|
+
}
|
|
182
|
+
console.error('');
|
|
183
|
+
return result.decision === 'deny' ? 1 : 0;
|
|
184
|
+
}
|
|
185
|
+
// ─── router ────────────────────────────────────────────────────
|
|
186
|
+
async function main() {
|
|
187
|
+
const [, , cmd, ...rest] = process.argv;
|
|
188
|
+
let code = 0;
|
|
189
|
+
try {
|
|
190
|
+
switch (cmd) {
|
|
191
|
+
case 'init':
|
|
192
|
+
code = runInit(rest);
|
|
193
|
+
break;
|
|
194
|
+
case 'disable':
|
|
195
|
+
case 'uninstall':
|
|
196
|
+
code = runDisable();
|
|
197
|
+
break;
|
|
198
|
+
case 'status':
|
|
199
|
+
code = runStatus();
|
|
200
|
+
break;
|
|
201
|
+
case 'test':
|
|
202
|
+
code = runTest(rest);
|
|
203
|
+
break;
|
|
204
|
+
case 'hook': {
|
|
205
|
+
const event = rest[0];
|
|
206
|
+
const failClosed = hasFlag(rest, 'fail-closed');
|
|
207
|
+
if (event === 'pre-tool-use' || !event) {
|
|
208
|
+
await runPreToolUseHook(failClosed);
|
|
209
|
+
// runPreToolUseHook calls process.exit, but TS doesn't know
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
// Other hook events: silently allow for now (forward compat)
|
|
213
|
+
process.stdout.write('{}');
|
|
214
|
+
process.exit(0);
|
|
215
|
+
}
|
|
216
|
+
case 'help':
|
|
217
|
+
case '--help':
|
|
218
|
+
case '-h':
|
|
219
|
+
case undefined:
|
|
220
|
+
help();
|
|
221
|
+
break;
|
|
222
|
+
default:
|
|
223
|
+
console.error(`${c.red}Unknown command: ${cmd}${c.reset}\n`);
|
|
224
|
+
help();
|
|
225
|
+
code = 2;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
console.error(`${c.red}Error:${c.reset} ${err instanceof Error ? err.message : String(err)}`);
|
|
230
|
+
if (process.env.DEBUG)
|
|
231
|
+
console.error(err);
|
|
232
|
+
code = 2;
|
|
233
|
+
}
|
|
234
|
+
process.exit(code);
|
|
235
|
+
}
|
|
236
|
+
main();
|
|
237
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manage ~/.claude/settings.json — install, remove, inspect the WARD hook.
|
|
3
|
+
*
|
|
4
|
+
* Safety rules:
|
|
5
|
+
* - Always back up existing settings.json before modifying
|
|
6
|
+
* - Use atomic writes (write to .tmp then rename)
|
|
7
|
+
* - Idempotent: running `init` twice doesn't duplicate hook entries
|
|
8
|
+
* - Preserve unknown keys (other hooks, other config) untouched
|
|
9
|
+
*/
|
|
10
|
+
import type { ClaudeSettingsHooksEntry } from './types.js';
|
|
11
|
+
export declare function claudeConfigDir(): string;
|
|
12
|
+
export declare function claudeSettingsPath(): string;
|
|
13
|
+
export declare function userWardPath(): string;
|
|
14
|
+
export interface ClaudeSettings {
|
|
15
|
+
hooks?: {
|
|
16
|
+
PreToolUse?: ClaudeSettingsHooksEntry[];
|
|
17
|
+
PostToolUse?: ClaudeSettingsHooksEntry[];
|
|
18
|
+
Stop?: ClaudeSettingsHooksEntry[];
|
|
19
|
+
SessionStart?: ClaudeSettingsHooksEntry[];
|
|
20
|
+
[other: string]: ClaudeSettingsHooksEntry[] | undefined;
|
|
21
|
+
};
|
|
22
|
+
[other: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
export declare function readSettings(): ClaudeSettings;
|
|
25
|
+
export declare function writeSettings(settings: ClaudeSettings): void;
|
|
26
|
+
/**
|
|
27
|
+
* The unique tag we put inside the hook command so we can find and remove
|
|
28
|
+
* our own entries without affecting hooks the user added manually.
|
|
29
|
+
*/
|
|
30
|
+
export declare const WARD_HOOK_MARKER = "# WEAVE_WARD_HOOK";
|
|
31
|
+
export interface InstallOptions {
|
|
32
|
+
/** Path to the weave-claude-code binary. Defaults to "weave-claude-code". */
|
|
33
|
+
binary?: string;
|
|
34
|
+
/** Restrict the hook to specific tool names. Default: all tools. */
|
|
35
|
+
toolMatcher?: string;
|
|
36
|
+
/** Hook timeout in seconds. */
|
|
37
|
+
timeoutSeconds?: number;
|
|
38
|
+
/** Block on error or fail-open? Default: fail-open (false). */
|
|
39
|
+
failClosed?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export declare function isHookInstalled(settings: ClaudeSettings): boolean;
|
|
42
|
+
export declare function installHook(opts?: InstallOptions): ClaudeSettings;
|
|
43
|
+
export declare function removeHook(): {
|
|
44
|
+
removed: number;
|
|
45
|
+
settings: ClaudeSettings;
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAM3D,wBAAgB,eAAe,IAAI,MAAM,CAIxC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAMD,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE;QACN,UAAU,CAAC,EAAE,wBAAwB,EAAE,CAAC;QACxC,WAAW,CAAC,EAAE,wBAAwB,EAAE,CAAC;QACzC,IAAI,CAAC,EAAE,wBAAwB,EAAE,CAAC;QAClC,YAAY,CAAC,EAAE,wBAAwB,EAAE,CAAC;QAC1C,CAAC,KAAK,EAAE,MAAM,GAAG,wBAAwB,EAAE,GAAG,SAAS,CAAC;KACzD,CAAC;IACF,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,wBAAgB,YAAY,IAAI,cAAc,CAY7C;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAc5D;AAMD;;;GAGG;AACH,eAAO,MAAM,gBAAgB,sBAAsB,CAAC;AAEpD,MAAM,WAAW,cAAc;IAC7B,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAKjE;AAED,wBAAgB,WAAW,CAAC,IAAI,GAAE,cAAmB,GAAG,cAAc,CAiCrE;AAED,wBAAgB,UAAU,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,cAAc,CAAA;CAAE,CAY1E"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manage ~/.claude/settings.json — install, remove, inspect the WARD hook.
|
|
3
|
+
*
|
|
4
|
+
* Safety rules:
|
|
5
|
+
* - Always back up existing settings.json before modifying
|
|
6
|
+
* - Use atomic writes (write to .tmp then rename)
|
|
7
|
+
* - Idempotent: running `init` twice doesn't duplicate hook entries
|
|
8
|
+
* - Preserve unknown keys (other hooks, other config) untouched
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync, copyFileSync } from 'node:fs';
|
|
11
|
+
import { dirname, join, resolve } from 'node:path';
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Paths
|
|
15
|
+
// ============================================================================
|
|
16
|
+
export function claudeConfigDir() {
|
|
17
|
+
return process.env.CLAUDE_CONFIG_DIR
|
|
18
|
+
? resolve(process.env.CLAUDE_CONFIG_DIR)
|
|
19
|
+
: join(homedir(), '.claude');
|
|
20
|
+
}
|
|
21
|
+
export function claudeSettingsPath() {
|
|
22
|
+
return join(claudeConfigDir(), 'settings.json');
|
|
23
|
+
}
|
|
24
|
+
export function userWardPath() {
|
|
25
|
+
return join(claudeConfigDir(), 'WARD.md');
|
|
26
|
+
}
|
|
27
|
+
export function readSettings() {
|
|
28
|
+
const path = claudeSettingsPath();
|
|
29
|
+
if (!existsSync(path))
|
|
30
|
+
return {};
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
throw new Error(`Failed to parse ${path} — please fix the JSON syntax or back it up and re-run. Original: ${err instanceof Error ? err.message : String(err)}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function writeSettings(settings) {
|
|
39
|
+
const path = claudeSettingsPath();
|
|
40
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
41
|
+
// Backup existing
|
|
42
|
+
if (existsSync(path)) {
|
|
43
|
+
const backup = `${path}.weave-backup`;
|
|
44
|
+
copyFileSync(path, backup);
|
|
45
|
+
}
|
|
46
|
+
// Atomic write
|
|
47
|
+
const tmp = `${path}.tmp`;
|
|
48
|
+
writeFileSync(tmp, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
49
|
+
renameSync(tmp, path);
|
|
50
|
+
}
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Hook install / remove
|
|
53
|
+
// ============================================================================
|
|
54
|
+
/**
|
|
55
|
+
* The unique tag we put inside the hook command so we can find and remove
|
|
56
|
+
* our own entries without affecting hooks the user added manually.
|
|
57
|
+
*/
|
|
58
|
+
export const WARD_HOOK_MARKER = '# WEAVE_WARD_HOOK';
|
|
59
|
+
export function isHookInstalled(settings) {
|
|
60
|
+
const entries = settings.hooks?.PreToolUse || [];
|
|
61
|
+
return entries.some((e) => e.hooks?.some((h) => h.command.includes(WARD_HOOK_MARKER)));
|
|
62
|
+
}
|
|
63
|
+
export function installHook(opts = {}) {
|
|
64
|
+
const settings = readSettings();
|
|
65
|
+
const binary = opts.binary || 'weave-claude-code';
|
|
66
|
+
const matcher = opts.toolMatcher || '*';
|
|
67
|
+
const failFlag = opts.failClosed ? ' --fail-closed' : '';
|
|
68
|
+
// Construct the command with our marker
|
|
69
|
+
const command = `${binary} hook pre-tool-use${failFlag} ${WARD_HOOK_MARKER}`;
|
|
70
|
+
const entry = {
|
|
71
|
+
matcher,
|
|
72
|
+
hooks: [
|
|
73
|
+
{
|
|
74
|
+
type: 'command',
|
|
75
|
+
command,
|
|
76
|
+
timeout: opts.timeoutSeconds ?? 5,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
if (!settings.hooks)
|
|
81
|
+
settings.hooks = {};
|
|
82
|
+
if (!settings.hooks.PreToolUse)
|
|
83
|
+
settings.hooks.PreToolUse = [];
|
|
84
|
+
// Remove any existing WARD entries first (idempotent)
|
|
85
|
+
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter((e) => !e.hooks?.some((h) => h.command.includes(WARD_HOOK_MARKER)));
|
|
86
|
+
// Add our new entry
|
|
87
|
+
settings.hooks.PreToolUse.push(entry);
|
|
88
|
+
writeSettings(settings);
|
|
89
|
+
return settings;
|
|
90
|
+
}
|
|
91
|
+
export function removeHook() {
|
|
92
|
+
const settings = readSettings();
|
|
93
|
+
if (!settings.hooks?.PreToolUse)
|
|
94
|
+
return { removed: 0, settings };
|
|
95
|
+
const before = settings.hooks.PreToolUse.length;
|
|
96
|
+
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter((e) => !e.hooks?.some((h) => h.command.includes(WARD_HOOK_MARKER)));
|
|
97
|
+
const removed = before - settings.hooks.PreToolUse.length;
|
|
98
|
+
if (removed > 0)
|
|
99
|
+
writeSettings(settings);
|
|
100
|
+
return { removed, settings };
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/hook.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook handler.
|
|
3
|
+
*
|
|
4
|
+
* This is what Claude Code actually invokes when a tool is about to be used.
|
|
5
|
+
* Reads the hook payload from stdin, looks up the active WARD policy,
|
|
6
|
+
* evaluates the proposed tool call, and writes the decision to stdout.
|
|
7
|
+
*
|
|
8
|
+
* Resolution order for WARD.md:
|
|
9
|
+
* 1. $WEAVE_WARD_PATH if set
|
|
10
|
+
* 2. <cwd>/WARD.md (from the hook payload)
|
|
11
|
+
* 3. <cwd>/.weave/WARD.md
|
|
12
|
+
* 4. ~/.claude/WARD.md (user-global fallback)
|
|
13
|
+
*
|
|
14
|
+
* Failure mode (when WARD parse fails):
|
|
15
|
+
* - default: fail OPEN (write a warning to stderr, allow the call)
|
|
16
|
+
* - --fail-closed: fail CLOSED (block the call with the error reason)
|
|
17
|
+
*/
|
|
18
|
+
import { type WardPolicy } from '@weave_protocol/ward';
|
|
19
|
+
export interface ResolvedWard {
|
|
20
|
+
policy: WardPolicy;
|
|
21
|
+
source: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function resolveWardForCwd(cwd: string): ResolvedWard | null;
|
|
24
|
+
export interface HookDecision {
|
|
25
|
+
decision: 'allow' | 'deny' | 'require_approval';
|
|
26
|
+
reasons: string[];
|
|
27
|
+
policySource?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function evaluateCall(policy: WardPolicy, toolName: string, toolInput: Record<string, unknown> | undefined): HookDecision;
|
|
30
|
+
export declare function runPreToolUseHook(failClosed: boolean): Promise<never>;
|
|
31
|
+
//# sourceMappingURL=hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,EAKL,KAAK,UAAU,EAEhB,MAAM,sBAAsB,CAAC;AA2B9B,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAmBlE;AAMD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,kBAAkB,CAAC;IAChD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC7C,YAAY,CA8Dd;AAmGD,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAkE3E"}
|
package/dist/hook.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook handler.
|
|
3
|
+
*
|
|
4
|
+
* This is what Claude Code actually invokes when a tool is about to be used.
|
|
5
|
+
* Reads the hook payload from stdin, looks up the active WARD policy,
|
|
6
|
+
* evaluates the proposed tool call, and writes the decision to stdout.
|
|
7
|
+
*
|
|
8
|
+
* Resolution order for WARD.md:
|
|
9
|
+
* 1. $WEAVE_WARD_PATH if set
|
|
10
|
+
* 2. <cwd>/WARD.md (from the hook payload)
|
|
11
|
+
* 3. <cwd>/.weave/WARD.md
|
|
12
|
+
* 4. ~/.claude/WARD.md (user-global fallback)
|
|
13
|
+
*
|
|
14
|
+
* Failure mode (when WARD parse fails):
|
|
15
|
+
* - default: fail OPEN (write a warning to stderr, allow the call)
|
|
16
|
+
* - --fail-closed: fail CLOSED (block the call with the error reason)
|
|
17
|
+
*/
|
|
18
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
19
|
+
import { join, resolve } from 'node:path';
|
|
20
|
+
import { parseWard, checkCapability, checkFilesystem, checkNetwork, } from '@weave_protocol/ward';
|
|
21
|
+
import { TOOL_MAPPINGS, } from './types.js';
|
|
22
|
+
import { userWardPath } from './config.js';
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// stdin reader
|
|
25
|
+
// ============================================================================
|
|
26
|
+
async function readStdin() {
|
|
27
|
+
if (process.stdin.isTTY)
|
|
28
|
+
return '';
|
|
29
|
+
return new Promise((resolveStdin) => {
|
|
30
|
+
let data = '';
|
|
31
|
+
process.stdin.setEncoding('utf8');
|
|
32
|
+
process.stdin.on('data', (chunk) => (data += chunk));
|
|
33
|
+
process.stdin.on('end', () => resolveStdin(data));
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export function resolveWardForCwd(cwd) {
|
|
37
|
+
const candidates = [];
|
|
38
|
+
if (process.env.WEAVE_WARD_PATH)
|
|
39
|
+
candidates.push(process.env.WEAVE_WARD_PATH);
|
|
40
|
+
if (cwd) {
|
|
41
|
+
candidates.push(join(cwd, 'WARD.md'));
|
|
42
|
+
candidates.push(join(cwd, '.weave', 'WARD.md'));
|
|
43
|
+
}
|
|
44
|
+
candidates.push(userWardPath());
|
|
45
|
+
for (const path of candidates) {
|
|
46
|
+
const resolved = resolve(path);
|
|
47
|
+
if (existsSync(resolved)) {
|
|
48
|
+
const source = readFileSync(resolved, 'utf8');
|
|
49
|
+
const policy = parseWard(source);
|
|
50
|
+
return { policy, source: resolved };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
export function evaluateCall(policy, toolName, toolInput) {
|
|
56
|
+
const reasons = [];
|
|
57
|
+
let decision = 'allow';
|
|
58
|
+
const mapping = TOOL_MAPPINGS[toolName] || { capability: toolName };
|
|
59
|
+
// 1. Capability check (try mapped name first, then raw tool name)
|
|
60
|
+
const capByMapping = checkCapability(policy, mapping.capability);
|
|
61
|
+
const capByRaw = checkCapability(policy, toolName);
|
|
62
|
+
// Smart merge: explicit rules (allow/deny/approval list) beat default decisions.
|
|
63
|
+
// The user can use either the Claude tool name (Bash) or the generic
|
|
64
|
+
// capability (shell_exec) in their WARD.md — either explicit match wins.
|
|
65
|
+
const cap = mergeCapability(capByMapping, capByRaw);
|
|
66
|
+
if (cap.decision === 'deny') {
|
|
67
|
+
return { decision: 'deny', reasons: [cap.reason] };
|
|
68
|
+
}
|
|
69
|
+
if (cap.decision === 'require_approval') {
|
|
70
|
+
decision = 'require_approval';
|
|
71
|
+
reasons.push(cap.reason);
|
|
72
|
+
}
|
|
73
|
+
// 2. Filesystem check
|
|
74
|
+
if (mapping.pathField && toolInput && typeof toolInput[mapping.pathField] === 'string') {
|
|
75
|
+
const path = toolInput[mapping.pathField];
|
|
76
|
+
const fs = checkFilesystem(policy, mapping.fsOp || 'read', path);
|
|
77
|
+
if (fs.decision === 'deny') {
|
|
78
|
+
return { decision: 'deny', reasons: [fs.reason] };
|
|
79
|
+
}
|
|
80
|
+
if (fs.decision === 'require_approval' && decision !== 'require_approval') {
|
|
81
|
+
decision = 'require_approval';
|
|
82
|
+
reasons.push(fs.reason);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// 3. Network check
|
|
86
|
+
if (mapping.urlField && toolInput && typeof toolInput[mapping.urlField] === 'string') {
|
|
87
|
+
const url = toolInput[mapping.urlField];
|
|
88
|
+
const method = toolInput.method || 'GET';
|
|
89
|
+
const net = checkNetwork(policy, url, method);
|
|
90
|
+
if (net.decision === 'deny') {
|
|
91
|
+
return { decision: 'deny', reasons: [net.reason] };
|
|
92
|
+
}
|
|
93
|
+
if (net.decision === 'require_approval' && decision !== 'require_approval') {
|
|
94
|
+
decision = 'require_approval';
|
|
95
|
+
reasons.push(net.reason);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// 4. Bash command heuristics — scan the command string for path patterns
|
|
99
|
+
if (mapping.commandField && toolInput && typeof toolInput[mapping.commandField] === 'string') {
|
|
100
|
+
const command = toolInput[mapping.commandField];
|
|
101
|
+
const bashCheck = scanBashCommand(policy, command);
|
|
102
|
+
if (bashCheck.decision === 'deny') {
|
|
103
|
+
return { decision: 'deny', reasons: [bashCheck.reason] };
|
|
104
|
+
}
|
|
105
|
+
if (bashCheck.decision === 'require_approval' && decision !== 'require_approval') {
|
|
106
|
+
decision = 'require_approval';
|
|
107
|
+
reasons.push(bashCheck.reason);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { decision, reasons };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Distinguish "explicit rule match" (user listed this tool in allow/deny/
|
|
114
|
+
* requireApproval) from "default decision" (policy default kicked in
|
|
115
|
+
* because nothing matched). Explicit rules win across both name variants.
|
|
116
|
+
*/
|
|
117
|
+
function isExplicit(reason) {
|
|
118
|
+
return (reason.includes('is in the allow list') ||
|
|
119
|
+
reason.includes('is in the deny list') ||
|
|
120
|
+
reason.includes('requires human approval'));
|
|
121
|
+
}
|
|
122
|
+
function mergeCapability(a, b) {
|
|
123
|
+
const aExp = isExplicit(a.reason);
|
|
124
|
+
const bExp = isExplicit(b.reason);
|
|
125
|
+
// If exactly one is explicit, that one wins regardless of decision.
|
|
126
|
+
if (aExp && !bExp)
|
|
127
|
+
return a;
|
|
128
|
+
if (bExp && !aExp)
|
|
129
|
+
return b;
|
|
130
|
+
// Both explicit OR both default — take the strictest:
|
|
131
|
+
// deny > require_approval > allow
|
|
132
|
+
if (a.decision === 'deny' || b.decision === 'deny') {
|
|
133
|
+
return a.decision === 'deny' ? a : b;
|
|
134
|
+
}
|
|
135
|
+
if (a.decision === 'require_approval' || b.decision === 'require_approval') {
|
|
136
|
+
return a.decision === 'require_approval' ? a : b;
|
|
137
|
+
}
|
|
138
|
+
return a;
|
|
139
|
+
}
|
|
140
|
+
function mergeStricter(a, b) {
|
|
141
|
+
if (a.decision === 'deny' || b.decision === 'deny') {
|
|
142
|
+
return a.decision === 'deny' ? a : b;
|
|
143
|
+
}
|
|
144
|
+
if (a.decision === 'require_approval' || b.decision === 'require_approval') {
|
|
145
|
+
return a.decision === 'require_approval' ? a : b;
|
|
146
|
+
}
|
|
147
|
+
return a;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Lightweight scan of a bash command for paths/URLs that might violate the
|
|
151
|
+
* filesystem/network policy. This is best-effort — full bash parsing isn't
|
|
152
|
+
* practical at this layer. We catch the common cases: explicit paths to
|
|
153
|
+
* sensitive files, curl/wget to disallowed URLs.
|
|
154
|
+
*/
|
|
155
|
+
function scanBashCommand(policy, command) {
|
|
156
|
+
// Look for URLs
|
|
157
|
+
const urlMatch = command.match(/https?:\/\/[^\s'"`)]+/);
|
|
158
|
+
if (urlMatch) {
|
|
159
|
+
const net = checkNetwork(policy, urlMatch[0], 'GET');
|
|
160
|
+
if (net.decision === 'deny')
|
|
161
|
+
return { decision: 'deny', reason: `bash command targets ${urlMatch[0]}: ${net.reason}` };
|
|
162
|
+
if (net.decision === 'require_approval')
|
|
163
|
+
return { decision: 'require_approval', reason: `bash command targets ${urlMatch[0]}: ${net.reason}` };
|
|
164
|
+
}
|
|
165
|
+
// Look for obvious destructive operations to common sensitive paths
|
|
166
|
+
const dangerousPaths = ['~/.ssh', '~/.aws', '/etc/', '/usr/', '/.env'];
|
|
167
|
+
for (const danger of dangerousPaths) {
|
|
168
|
+
if (command.includes(danger)) {
|
|
169
|
+
// Infer op from command verb
|
|
170
|
+
let op = 'read';
|
|
171
|
+
if (/^|[\s;|]rm\s|>\s*['"]?\Q+danger/.test(command))
|
|
172
|
+
op = 'delete';
|
|
173
|
+
else if (/>|>>|tee|sed -i/.test(command))
|
|
174
|
+
op = 'write';
|
|
175
|
+
const fs = checkFilesystem(policy, op, danger + (danger.endsWith('/') ? '**' : ''));
|
|
176
|
+
if (fs.decision === 'deny')
|
|
177
|
+
return { decision: 'deny', reason: `bash touches ${danger}: ${fs.reason}` };
|
|
178
|
+
if (fs.decision === 'require_approval')
|
|
179
|
+
return { decision: 'require_approval', reason: `bash touches ${danger}: ${fs.reason}` };
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return { decision: 'allow', reason: '' };
|
|
183
|
+
}
|
|
184
|
+
// ============================================================================
|
|
185
|
+
// Hook output helpers
|
|
186
|
+
// ============================================================================
|
|
187
|
+
function emit(output) {
|
|
188
|
+
process.stdout.write(JSON.stringify(output));
|
|
189
|
+
process.exit(0);
|
|
190
|
+
}
|
|
191
|
+
// ============================================================================
|
|
192
|
+
// Main entry
|
|
193
|
+
// ============================================================================
|
|
194
|
+
export async function runPreToolUseHook(failClosed) {
|
|
195
|
+
const raw = await readStdin();
|
|
196
|
+
// No payload → can't do anything useful, pass through silently
|
|
197
|
+
if (!raw.trim())
|
|
198
|
+
emit({});
|
|
199
|
+
let payload;
|
|
200
|
+
try {
|
|
201
|
+
payload = JSON.parse(raw);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// Malformed payload — fail open (default) or closed
|
|
205
|
+
if (failClosed)
|
|
206
|
+
emit({ decision: 'block', reason: 'WARD hook: malformed payload from Claude Code' });
|
|
207
|
+
process.stderr.write('weave-claude-code: malformed hook payload, allowing\n');
|
|
208
|
+
emit({});
|
|
209
|
+
}
|
|
210
|
+
if (!payload.tool_name)
|
|
211
|
+
emit({});
|
|
212
|
+
const cwd = payload.cwd || process.cwd();
|
|
213
|
+
// Resolve WARD policy
|
|
214
|
+
let resolved = null;
|
|
215
|
+
try {
|
|
216
|
+
resolved = resolveWardForCwd(cwd);
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
220
|
+
if (failClosed)
|
|
221
|
+
emit({ decision: 'block', reason: `WARD policy could not be loaded: ${msg}` });
|
|
222
|
+
process.stderr.write(`weave-claude-code: WARD load failed (${msg}), allowing\n`);
|
|
223
|
+
emit({});
|
|
224
|
+
}
|
|
225
|
+
if (!resolved) {
|
|
226
|
+
// No WARD.md found — nothing to enforce
|
|
227
|
+
emit({});
|
|
228
|
+
}
|
|
229
|
+
// Evaluate
|
|
230
|
+
let result;
|
|
231
|
+
try {
|
|
232
|
+
result = evaluateCall(resolved.policy, payload.tool_name, payload.tool_input);
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
236
|
+
if (failClosed)
|
|
237
|
+
emit({ decision: 'block', reason: `WARD evaluation failed: ${msg}` });
|
|
238
|
+
process.stderr.write(`weave-claude-code: WARD eval failed (${msg}), allowing\n`);
|
|
239
|
+
emit({});
|
|
240
|
+
}
|
|
241
|
+
if (result.decision === 'deny') {
|
|
242
|
+
emit({
|
|
243
|
+
decision: 'block',
|
|
244
|
+
reason: `🛡️ WARD: ${result.reasons.join(' | ')}\n Policy: ${resolved.source}`,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
if (result.decision === 'require_approval') {
|
|
248
|
+
// Claude Code's hook protocol doesn't have a "needs approval" decision
|
|
249
|
+
// distinct from "block", so we block with a clear reason. The user can
|
|
250
|
+
// explicitly retry / override after reviewing.
|
|
251
|
+
emit({
|
|
252
|
+
decision: 'block',
|
|
253
|
+
reason: `🛡️ WARD requires approval before this call:\n ${result.reasons.join('\n ')}\n Policy: ${resolved.source}\n To proceed, explicitly confirm or update WARD.md.`,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
// Allow — silent
|
|
257
|
+
emit({});
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=hook.js.map
|
package/dist/hook.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @weave_protocol/adapter-claudecode - public API
|
|
3
|
+
*/
|
|
4
|
+
export * from './types.js';
|
|
5
|
+
export { installHook, removeHook, readSettings, writeSettings, isHookInstalled, claudeSettingsPath, claudeConfigDir, userWardPath, WARD_HOOK_MARKER, } from './config.js';
|
|
6
|
+
export type { ClaudeSettings, InstallOptions } from './config.js';
|
|
7
|
+
export { resolveWardForCwd, evaluateCall, runPreToolUseHook, } from './hook.js';
|
|
8
|
+
export type { ResolvedWard, HookDecision } from './hook.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EACL,WAAW,EACX,UAAU,EACV,YAAY,EACZ,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @weave_protocol/adapter-claudecode - public API
|
|
3
|
+
*/
|
|
4
|
+
export * from './types.js';
|
|
5
|
+
export { installHook, removeHook, readSettings, writeSettings, isHookInstalled, claudeSettingsPath, claudeConfigDir, userWardPath, WARD_HOOK_MARKER, } from './config.js';
|
|
6
|
+
export { resolveWardForCwd, evaluateCall, runPreToolUseHook, } from './hook.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EACL,WAAW,EACX,UAAU,EACV,YAAY,EACZ,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,GAClB,MAAM,WAAW,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the Claude Code adapter.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Shape of the JSON sent to a Claude Code hook on stdin.
|
|
6
|
+
* Based on Claude Code's documented hook input format.
|
|
7
|
+
*/
|
|
8
|
+
export interface ClaudeCodeHookInput {
|
|
9
|
+
session_id?: string;
|
|
10
|
+
transcript_path?: string;
|
|
11
|
+
cwd?: string;
|
|
12
|
+
hook_event_name?: string;
|
|
13
|
+
tool_name?: string;
|
|
14
|
+
tool_input?: Record<string, unknown>;
|
|
15
|
+
tool_response?: unknown;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Shape of the JSON returned on stdout to control Claude Code's behavior.
|
|
19
|
+
*
|
|
20
|
+
* For PreToolUse:
|
|
21
|
+
* - { "decision": "approve" } → auto-approve (skip user confirm)
|
|
22
|
+
* - { "decision": "block", "reason": …} → block the call with reason
|
|
23
|
+
* - {} (or no output) → let Claude Code decide normally
|
|
24
|
+
*
|
|
25
|
+
* Optional "systemMessage" appears in the conversation; "continue" controls
|
|
26
|
+
* whether the overall turn proceeds.
|
|
27
|
+
*/
|
|
28
|
+
export interface ClaudeCodeHookOutput {
|
|
29
|
+
decision?: 'approve' | 'block';
|
|
30
|
+
reason?: string;
|
|
31
|
+
continue?: boolean;
|
|
32
|
+
stopReason?: string;
|
|
33
|
+
systemMessage?: string;
|
|
34
|
+
suppressOutput?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The hook type. v0.1 only ships PreToolUse; PostToolUse and Stop come later.
|
|
38
|
+
*/
|
|
39
|
+
export type HookType = 'pre-tool-use' | 'post-tool-use' | 'stop' | 'session-start';
|
|
40
|
+
/**
|
|
41
|
+
* Mapping from Claude Code tool names to their nature.
|
|
42
|
+
* Used to extract the right args and apply the right WARD checks.
|
|
43
|
+
*/
|
|
44
|
+
export interface ToolMapping {
|
|
45
|
+
/** Generic capability name for WARD `## Capabilities`. */
|
|
46
|
+
capability: string;
|
|
47
|
+
/** Args field that contains a filesystem path, if any. */
|
|
48
|
+
pathField?: string;
|
|
49
|
+
/** Implied filesystem operation for that path. */
|
|
50
|
+
fsOp?: 'read' | 'write' | 'execute' | 'delete' | 'list';
|
|
51
|
+
/** Args field that contains a URL, if any. */
|
|
52
|
+
urlField?: string;
|
|
53
|
+
/** Args field that contains a shell command, if any. */
|
|
54
|
+
commandField?: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Built-in mappings for Claude Code's standard tools.
|
|
58
|
+
* Unknown tools fall through with the tool name as the capability.
|
|
59
|
+
*/
|
|
60
|
+
export declare const TOOL_MAPPINGS: Record<string, ToolMapping>;
|
|
61
|
+
/**
|
|
62
|
+
* Settings written into ~/.claude/settings.json for the WARD hook.
|
|
63
|
+
*/
|
|
64
|
+
export interface ClaudeSettingsHooksEntry {
|
|
65
|
+
matcher?: string;
|
|
66
|
+
hooks: Array<{
|
|
67
|
+
type: 'command';
|
|
68
|
+
command: string;
|
|
69
|
+
timeout?: number;
|
|
70
|
+
}>;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,eAAe,GAAG,MAAM,GAAG,eAAe,CAAC;AAEnF;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;IACxD,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAcrD,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,SAAS,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CACJ"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the Claude Code adapter.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Built-in mappings for Claude Code's standard tools.
|
|
6
|
+
* Unknown tools fall through with the tool name as the capability.
|
|
7
|
+
*/
|
|
8
|
+
export const TOOL_MAPPINGS = {
|
|
9
|
+
Bash: { capability: 'shell_exec', commandField: 'command' },
|
|
10
|
+
Edit: { capability: 'file_write', pathField: 'file_path', fsOp: 'write' },
|
|
11
|
+
MultiEdit: { capability: 'file_write', pathField: 'file_path', fsOp: 'write' },
|
|
12
|
+
Write: { capability: 'file_write', pathField: 'file_path', fsOp: 'write' },
|
|
13
|
+
Read: { capability: 'file_read', pathField: 'file_path', fsOp: 'read' },
|
|
14
|
+
Glob: { capability: 'file_list', pathField: 'path', fsOp: 'list' },
|
|
15
|
+
Grep: { capability: 'file_read', pathField: 'path', fsOp: 'read' },
|
|
16
|
+
LS: { capability: 'file_list', pathField: 'path', fsOp: 'list' },
|
|
17
|
+
WebFetch: { capability: 'http_request', urlField: 'url' },
|
|
18
|
+
WebSearch: { capability: 'web_search' },
|
|
19
|
+
Task: { capability: 'subagent' },
|
|
20
|
+
TodoWrite: { capability: 'todo_write' },
|
|
21
|
+
NotebookEdit: { capability: 'notebook_edit', pathField: 'notebook_path', fsOp: 'write' },
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AA0DH;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAgC;IACxD,IAAI,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE;IAC3D,IAAI,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE;IACzE,SAAS,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE;IAC9E,KAAK,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE;IAC1E,IAAI,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE;IACvE,IAAI,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;IAClE,IAAI,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;IAClE,EAAE,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;IAChE,QAAQ,EAAE,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,KAAK,EAAE;IACzD,SAAS,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE;IACvC,IAAI,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE;IAChC,SAAS,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE;IACvC,YAAY,EAAE,EAAE,UAAU,EAAE,eAAe,EAAE,SAAS,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,EAAE;CACzF,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@weave_protocol/adapter-claudecode",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Weave Protocol adapter for Claude Code - enforces WARD.md policies via Claude Code's hook system",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"weave-claude-code": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsc --watch",
|
|
14
|
+
"test": "node --test tests/",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"weave-protocol",
|
|
19
|
+
"ward",
|
|
20
|
+
"claude-code",
|
|
21
|
+
"anthropic",
|
|
22
|
+
"agent-security",
|
|
23
|
+
"ai-security",
|
|
24
|
+
"hooks",
|
|
25
|
+
"harness-adapter",
|
|
26
|
+
"policy-enforcement",
|
|
27
|
+
"agents-md",
|
|
28
|
+
"skill-md",
|
|
29
|
+
"ward-md"
|
|
30
|
+
],
|
|
31
|
+
"author": "Trent Yoxall",
|
|
32
|
+
"license": "Apache-2.0",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/Tyox-all/Weave_Protocol.git",
|
|
36
|
+
"directory": "adapter-claudecode"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/Tyox-all/Weave_Protocol/tree/main/adapter-claudecode#readme",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/Tyox-all/Weave_Protocol/issues"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@weave_protocol/ward": "^0.1.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^20.0.0",
|
|
47
|
+
"typescript": "^5.3.0"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18.0.0"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
"dist/",
|
|
54
|
+
"README.md",
|
|
55
|
+
"SKILL.md",
|
|
56
|
+
"LICENSE"
|
|
57
|
+
]
|
|
58
|
+
}
|