@bookedsolid/reagent 0.1.0 → 0.2.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 +322 -50
- package/dist/cli/commands/check.d.ts +2 -0
- package/dist/cli/commands/check.d.ts.map +1 -0
- package/dist/cli/commands/check.js +87 -0
- package/dist/cli/commands/check.js.map +1 -0
- package/dist/cli/commands/freeze.d.ts +2 -0
- package/dist/cli/commands/freeze.d.ts.map +1 -0
- package/dist/cli/commands/freeze.js +24 -0
- package/dist/cli/commands/freeze.js.map +1 -0
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +487 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +2 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +12 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/unfreeze.d.ts +2 -0
- package/dist/cli/commands/unfreeze.d.ts.map +1 -0
- package/dist/cli/commands/unfreeze.js +14 -0
- package/dist/cli/commands/unfreeze.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +71 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils.d.ts +5 -0
- package/dist/cli/utils.d.ts.map +1 -0
- package/dist/cli/utils.js +34 -0
- package/dist/cli/utils.js.map +1 -0
- package/dist/config/gateway-config.d.ts +3 -0
- package/dist/config/gateway-config.d.ts.map +1 -0
- package/dist/config/gateway-config.js +60 -0
- package/dist/config/gateway-config.js.map +1 -0
- package/dist/config/policy-loader.d.ts +3 -0
- package/dist/config/policy-loader.d.ts.map +1 -0
- package/dist/config/policy-loader.js +38 -0
- package/dist/config/policy-loader.js.map +1 -0
- package/dist/config/tier-map.d.ts +12 -0
- package/dist/config/tier-map.d.ts.map +1 -0
- package/dist/config/tier-map.js +76 -0
- package/dist/config/tier-map.js.map +1 -0
- package/dist/gateway/client-manager.d.ts +26 -0
- package/dist/gateway/client-manager.d.ts.map +1 -0
- package/dist/gateway/client-manager.js +75 -0
- package/dist/gateway/client-manager.js.map +1 -0
- package/dist/gateway/middleware/audit.d.ts +12 -0
- package/dist/gateway/middleware/audit.d.ts.map +1 -0
- package/dist/gateway/middleware/audit.js +88 -0
- package/dist/gateway/middleware/audit.js.map +1 -0
- package/dist/gateway/middleware/chain.d.ts +27 -0
- package/dist/gateway/middleware/chain.d.ts.map +1 -0
- package/dist/gateway/middleware/chain.js +37 -0
- package/dist/gateway/middleware/chain.js.map +1 -0
- package/dist/gateway/middleware/kill-switch.d.ts +10 -0
- package/dist/gateway/middleware/kill-switch.d.ts.map +1 -0
- package/dist/gateway/middleware/kill-switch.js +61 -0
- package/dist/gateway/middleware/kill-switch.js.map +1 -0
- package/dist/gateway/middleware/policy.d.ts +10 -0
- package/dist/gateway/middleware/policy.d.ts.map +1 -0
- package/dist/gateway/middleware/policy.js +52 -0
- package/dist/gateway/middleware/policy.js.map +1 -0
- package/dist/gateway/middleware/redact.d.ts +17 -0
- package/dist/gateway/middleware/redact.d.ts.map +1 -0
- package/dist/gateway/middleware/redact.js +109 -0
- package/dist/gateway/middleware/redact.js.map +1 -0
- package/dist/gateway/middleware/session.d.ts +11 -0
- package/dist/gateway/middleware/session.d.ts.map +1 -0
- package/dist/gateway/middleware/session.js +19 -0
- package/dist/gateway/middleware/session.js.map +1 -0
- package/dist/gateway/middleware/tier.d.ts +7 -0
- package/dist/gateway/middleware/tier.d.ts.map +1 -0
- package/dist/gateway/middleware/tier.js +11 -0
- package/dist/gateway/middleware/tier.js.map +1 -0
- package/dist/gateway/server.d.ts +14 -0
- package/dist/gateway/server.d.ts.map +1 -0
- package/dist/gateway/server.js +79 -0
- package/dist/gateway/server.js.map +1 -0
- package/dist/gateway/tool-proxy.d.ts +21 -0
- package/dist/gateway/tool-proxy.d.ts.map +1 -0
- package/dist/gateway/tool-proxy.js +134 -0
- package/dist/gateway/tool-proxy.js.map +1 -0
- package/dist/types/audit.d.ts +16 -0
- package/dist/types/audit.d.ts.map +1 -0
- package/dist/types/audit.js +2 -0
- package/dist/types/audit.js.map +1 -0
- package/dist/types/enums.d.ts +21 -0
- package/dist/types/enums.d.ts.map +1 -0
- package/dist/types/enums.js +25 -0
- package/dist/types/enums.js.map +1 -0
- package/dist/types/gateway.d.ts +16 -0
- package/dist/types/gateway.d.ts.map +1 -0
- package/dist/types/gateway.js +2 -0
- package/dist/types/gateway.js.map +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/policy.d.ts +14 -0
- package/dist/types/policy.d.ts.map +1 -0
- package/dist/types/policy.js +2 -0
- package/dist/types/policy.js.map +1 -0
- package/hooks/attribution-advisory.sh +78 -26
- package/husky/commit-msg.sh +102 -22
- package/package.json +24 -12
- package/profiles/bst-internal.json +2 -1
- package/profiles/client-engagement.json +2 -1
- package/bin/init.js +0 -818
package/README.md
CHANGED
|
@@ -1,97 +1,336 @@
|
|
|
1
1
|
# @bookedsolid/reagent
|
|
2
2
|
|
|
3
|
-
Zero-trust agentic infrastructure for AI-assisted development.
|
|
3
|
+
Zero-trust MCP gateway and agentic infrastructure for AI-assisted development.
|
|
4
4
|
|
|
5
|
-
Reagent
|
|
5
|
+
Reagent is two things:
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
1. **MCP Gateway** (`reagent serve`) — a proxy server that sits between your AI assistant (Claude Code, Cursor, etc.) and downstream MCP tool servers. Every tool call flows through a zero-trust middleware chain: policy enforcement, tier classification, secret redaction, and hash-chained audit logging.
|
|
8
8
|
|
|
9
|
-
`reagent init`
|
|
9
|
+
2. **Config Scaffolder** (`reagent init`) — installs safety hooks, behavioral policies, and developer tooling into any project.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- **Policy
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
11
|
+
## Why Reagent?
|
|
12
|
+
|
|
13
|
+
AI coding assistants are powerful but unconstrained. Reagent adds the missing governance layer:
|
|
14
|
+
|
|
15
|
+
- **Policy enforcement** — graduated autonomy levels (L0 read-only → L3 full access) control which tiers of tools an agent can invoke
|
|
16
|
+
- **Kill switch** — `reagent freeze` immediately blocks all tool calls across every connected MCP server
|
|
17
|
+
- **Secret redaction** — tool outputs are scanned for AWS keys, GitHub tokens, API keys, PEM private keys, Discord tokens, and more — redacted before they reach the AI
|
|
18
|
+
- **Audit trail** — every tool invocation is logged as hash-chained JSONL, providing tamper-evident compliance records
|
|
19
|
+
- **Tool blocking** — individual tools can be permanently blocked regardless of autonomy level
|
|
18
20
|
|
|
19
21
|
## Quick Start
|
|
20
22
|
|
|
23
|
+
### As an MCP Gateway
|
|
24
|
+
|
|
21
25
|
```bash
|
|
22
|
-
|
|
26
|
+
npm install -g @bookedsolid/reagent
|
|
27
|
+
|
|
28
|
+
# Initialize a project with policy and gateway config
|
|
29
|
+
reagent init --profile bst-internal
|
|
30
|
+
|
|
31
|
+
# Configure your downstream MCP servers in .reagent/gateway.yaml
|
|
32
|
+
# Then start the gateway
|
|
33
|
+
reagent serve
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Point your AI assistant's MCP configuration at the gateway:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"mcpServers": {
|
|
41
|
+
"reagent": {
|
|
42
|
+
"command": "reagent",
|
|
43
|
+
"args": ["serve"]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
23
47
|
```
|
|
24
48
|
|
|
25
|
-
|
|
49
|
+
All downstream tool calls now flow through Reagent's middleware chain.
|
|
50
|
+
|
|
51
|
+
### As a Config Scaffolder
|
|
26
52
|
|
|
27
53
|
```bash
|
|
28
|
-
|
|
29
|
-
npx @bookedsolid/reagent init --profile bst-internal
|
|
54
|
+
npx @bookedsolid/reagent init
|
|
30
55
|
|
|
31
|
-
#
|
|
56
|
+
# With a profile
|
|
57
|
+
npx @bookedsolid/reagent init --profile bst-internal
|
|
32
58
|
npx @bookedsolid/reagent init --profile client-engagement
|
|
59
|
+
|
|
60
|
+
# Preview without changes
|
|
61
|
+
npx @bookedsolid/reagent init --dry-run
|
|
33
62
|
```
|
|
34
63
|
|
|
35
|
-
|
|
64
|
+
## Commands
|
|
36
65
|
|
|
37
|
-
|
|
38
|
-
|
|
66
|
+
| Command | Description |
|
|
67
|
+
| ------------------------------- | ------------------------------------------------- |
|
|
68
|
+
| `reagent serve` | Start the MCP gateway server (stdio transport) |
|
|
69
|
+
| `reagent init` | Install reagent config into the current directory |
|
|
70
|
+
| `reagent check` | Verify what reagent components are installed |
|
|
71
|
+
| `reagent freeze --reason "..."` | Create `.reagent/HALT` — suspends all tool calls |
|
|
72
|
+
| `reagent unfreeze` | Remove `.reagent/HALT` — resumes tool calls |
|
|
73
|
+
| `reagent help` | Show usage help |
|
|
74
|
+
|
|
75
|
+
## MCP Gateway
|
|
76
|
+
|
|
77
|
+
### How It Works
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
AI Assistant (Claude Code, Cursor, etc.)
|
|
81
|
+
│
|
|
82
|
+
│ stdio (MCP protocol)
|
|
83
|
+
▼
|
|
84
|
+
┌─────────────────────────────┐
|
|
85
|
+
│ Reagent Gateway │
|
|
86
|
+
│ │
|
|
87
|
+
│ ┌───────────────────────┐ │
|
|
88
|
+
│ │ Middleware Chain │ │
|
|
89
|
+
│ │ │ │
|
|
90
|
+
│ │ 1. Audit (outermost) │ │
|
|
91
|
+
│ │ 2. Session context │ │
|
|
92
|
+
│ │ 3. Kill switch │ │
|
|
93
|
+
│ │ 4. Tier classify │ │
|
|
94
|
+
│ │ 5. Policy enforce │ │
|
|
95
|
+
│ │ 6. Secret redaction │ │
|
|
96
|
+
│ │ 7. [Execute] │ │
|
|
97
|
+
│ └───────────────────────┘ │
|
|
98
|
+
│ │
|
|
99
|
+
└──────────┬──────────────────┘
|
|
100
|
+
│ stdio (MCP protocol)
|
|
101
|
+
▼
|
|
102
|
+
Downstream MCP Servers
|
|
103
|
+
(discord-ops, filesystem, etc.)
|
|
39
104
|
```
|
|
40
105
|
|
|
41
|
-
|
|
106
|
+
The gateway:
|
|
107
|
+
|
|
108
|
+
1. Connects to all downstream MCP servers defined in `.reagent/gateway.yaml`
|
|
109
|
+
2. Discovers their tools via MCP `tools/list`
|
|
110
|
+
3. Re-registers each tool on the gateway with namespace prefixes (`servername__toolname`)
|
|
111
|
+
4. Wraps every tool call in the middleware chain
|
|
112
|
+
5. Listens on stdio for incoming MCP requests from the AI assistant
|
|
113
|
+
|
|
114
|
+
### Gateway Configuration
|
|
115
|
+
|
|
116
|
+
Create `.reagent/gateway.yaml`:
|
|
117
|
+
|
|
118
|
+
```yaml
|
|
119
|
+
version: '1'
|
|
120
|
+
servers:
|
|
121
|
+
discord-ops:
|
|
122
|
+
command: node
|
|
123
|
+
args:
|
|
124
|
+
- /path/to/discord-ops/dist/index.js
|
|
125
|
+
env:
|
|
126
|
+
DISCORD_BOT_TOKEN: '${DISCORD_BOT_TOKEN}'
|
|
127
|
+
tool_overrides:
|
|
128
|
+
get_messages:
|
|
129
|
+
tier: read
|
|
130
|
+
send_message:
|
|
131
|
+
tier: write
|
|
132
|
+
purge_messages:
|
|
133
|
+
tier: destructive
|
|
134
|
+
delete_channel:
|
|
135
|
+
tier: destructive
|
|
136
|
+
blocked: true
|
|
137
|
+
```
|
|
42
138
|
|
|
43
|
-
|
|
44
|
-
|
|
139
|
+
**Environment variable resolution:** Use `${VAR_NAME}` syntax in env values — Reagent resolves them from `process.env` at startup.
|
|
140
|
+
|
|
141
|
+
**Tool overrides:** Each downstream tool can be assigned a tier (`read`, `write`, `destructive`) and optionally blocked entirely.
|
|
142
|
+
|
|
143
|
+
### Tool Namespacing
|
|
144
|
+
|
|
145
|
+
Downstream tools are namespaced as `servername__toolname` to prevent collisions:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
discord-ops__send_message
|
|
149
|
+
discord-ops__get_messages
|
|
150
|
+
filesystem__read_file
|
|
151
|
+
filesystem__write_file
|
|
45
152
|
```
|
|
46
153
|
|
|
47
|
-
|
|
154
|
+
### Multiple Downstream Servers
|
|
155
|
+
|
|
156
|
+
```yaml
|
|
157
|
+
version: '1'
|
|
158
|
+
servers:
|
|
159
|
+
discord-ops:
|
|
160
|
+
command: node
|
|
161
|
+
args: [/path/to/discord-ops/dist/index.js]
|
|
162
|
+
filesystem:
|
|
163
|
+
command: npx
|
|
164
|
+
args: [-y, '@modelcontextprotocol/server-filesystem', '/allowed/path']
|
|
165
|
+
github:
|
|
166
|
+
command: npx
|
|
167
|
+
args: [-y, '@modelcontextprotocol/server-github']
|
|
168
|
+
env:
|
|
169
|
+
GITHUB_PERSONAL_ACCESS_TOKEN: '${GITHUB_TOKEN}'
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
All tools from all servers are aggregated into a single gateway.
|
|
48
173
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
174
|
+
## Middleware Chain
|
|
175
|
+
|
|
176
|
+
Every tool call passes through the middleware chain in onion (Koa-style) order. The chain is designed with security invariants:
|
|
177
|
+
|
|
178
|
+
### 1. Audit (outermost)
|
|
179
|
+
|
|
180
|
+
Records every invocation — including denials — as a hash-chained JSONL entry. Written to `.reagent/audit/YYYY-MM-DD.jsonl`. Each record contains:
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"timestamp": "2026-04-09T12:00:00.000Z",
|
|
185
|
+
"session_id": "a1b2c3d4-...",
|
|
186
|
+
"tool_name": "send_message",
|
|
187
|
+
"server_name": "discord-ops",
|
|
188
|
+
"tier": "write",
|
|
189
|
+
"status": "allowed",
|
|
190
|
+
"autonomy_level": "L1",
|
|
191
|
+
"duration_ms": 42,
|
|
192
|
+
"prev_hash": "0000...0000",
|
|
193
|
+
"hash": "abc123..."
|
|
194
|
+
}
|
|
195
|
+
```
|
|
56
196
|
|
|
57
|
-
|
|
197
|
+
The `prev_hash` field chains records together — tamper with one record and every subsequent hash becomes invalid.
|
|
58
198
|
|
|
59
|
-
|
|
199
|
+
### 2. Session Context
|
|
200
|
+
|
|
201
|
+
Attaches a unique session ID (UUID) to every invocation. Each gateway instance generates one session ID at startup.
|
|
202
|
+
|
|
203
|
+
### 3. Kill Switch
|
|
204
|
+
|
|
205
|
+
Checks for `.reagent/HALT` file. If present, the invocation is immediately denied. The HALT file contents become the denial reason.
|
|
60
206
|
|
|
61
207
|
```bash
|
|
62
|
-
# Emergency stop
|
|
63
|
-
|
|
208
|
+
# Emergency stop — all tool calls blocked immediately
|
|
209
|
+
reagent freeze --reason "security incident at 2026-04-09T12:00:00Z"
|
|
64
210
|
|
|
65
211
|
# Resume
|
|
66
|
-
|
|
212
|
+
reagent unfreeze
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 4. Tier Classification
|
|
216
|
+
|
|
217
|
+
Classifies the tool into one of three tiers:
|
|
218
|
+
|
|
219
|
+
| Tier | Description | Examples |
|
|
220
|
+
| ------------- | ------------------------------- | ------------------------------------------------ |
|
|
221
|
+
| `read` | Observes state, no side effects | `get_messages`, `list_channels`, `health_check` |
|
|
222
|
+
| `write` | Modifies state | `send_message`, `create_channel`, `edit_message` |
|
|
223
|
+
| `destructive` | Irreversible state changes | `delete_channel`, `purge_messages`, `ban_member` |
|
|
224
|
+
|
|
225
|
+
Tiers are assigned via `tool_overrides` in gateway config. Unknown tools default to `write`.
|
|
226
|
+
|
|
227
|
+
### 5. Policy Enforcement
|
|
228
|
+
|
|
229
|
+
Checks the tool's tier against the project's autonomy level:
|
|
230
|
+
|
|
231
|
+
| Autonomy Level | Allowed Tiers |
|
|
232
|
+
| ------------------ | -------------------------------- |
|
|
233
|
+
| `L0` (read-only) | `read` only |
|
|
234
|
+
| `L1` (standard) | `read` + `write` |
|
|
235
|
+
| `L2` (elevated) | `read` + `write` + `destructive` |
|
|
236
|
+
| `L3` (full access) | All tiers |
|
|
237
|
+
|
|
238
|
+
Also checks for explicitly blocked tools — a tool marked `blocked: true` in gateway config is denied regardless of autonomy level.
|
|
239
|
+
|
|
240
|
+
### 6. Secret Redaction
|
|
241
|
+
|
|
242
|
+
Post-execution: scans tool output for sensitive patterns and replaces them with `[REDACTED]`:
|
|
243
|
+
|
|
244
|
+
- AWS Access Keys (`AKIA...`)
|
|
245
|
+
- AWS Secret Keys
|
|
246
|
+
- GitHub Tokens (`ghp_...`, `gho_...`, `ghs_...`, `ghu_...`, `ghr_...`)
|
|
247
|
+
- Generic API Keys
|
|
248
|
+
- Bearer Tokens
|
|
249
|
+
- PEM Private Keys
|
|
250
|
+
- Discord Bot Tokens
|
|
251
|
+
- Base64-encoded AWS Keys
|
|
252
|
+
|
|
253
|
+
Redaction operates on individual string values within structured results — it never corrupts JSON structure.
|
|
254
|
+
|
|
255
|
+
### Security Invariants
|
|
256
|
+
|
|
257
|
+
- **Denial is permanent** — once any middleware denies an invocation, no subsequent middleware can revert it
|
|
258
|
+
- **Audit records everything** — audit is outermost, so even kill-switch denials are recorded
|
|
259
|
+
- **Policy re-derives tier** — never trusts mutable context; always re-classifies from tool name
|
|
260
|
+
- **Fail-closed** — errors in kill-switch or policy checks result in denial, not passthrough
|
|
261
|
+
- **All logging to stderr** — stdout is reserved for the MCP stdio transport
|
|
262
|
+
|
|
263
|
+
## Policy File
|
|
264
|
+
|
|
265
|
+
`.reagent/policy.yaml` controls agent behavior:
|
|
266
|
+
|
|
267
|
+
```yaml
|
|
268
|
+
version: '1'
|
|
269
|
+
profile: bst-internal
|
|
270
|
+
installed_by: 'reagent init'
|
|
271
|
+
installed_at: '2026-04-09T00:00:00.000Z'
|
|
272
|
+
autonomy_level: L1
|
|
273
|
+
max_autonomy_level: L3
|
|
274
|
+
promotion_requires_human_approval: true
|
|
275
|
+
blocked_paths:
|
|
276
|
+
- .github/workflows/
|
|
277
|
+
- .env
|
|
278
|
+
notification_channel: '#reagent-alerts'
|
|
67
279
|
```
|
|
68
280
|
|
|
69
|
-
|
|
281
|
+
| Field | Description |
|
|
282
|
+
| ----------------------------------- | ------------------------------------------------------------- |
|
|
283
|
+
| `autonomy_level` | Current level (L0-L3) — controls which tool tiers are allowed |
|
|
284
|
+
| `max_autonomy_level` | Ceiling — agents cannot request escalation beyond this |
|
|
285
|
+
| `promotion_requires_human_approval` | Whether level changes need human sign-off |
|
|
286
|
+
| `blocked_paths` | Directories the agent must never modify |
|
|
287
|
+
|
|
288
|
+
## Config Scaffolder
|
|
289
|
+
|
|
290
|
+
`reagent init` configures your repository with:
|
|
291
|
+
|
|
292
|
+
- **Git hooks** — commit-msg validation (Co-Authored-By attribution, secret detection) and pre-push quality gates
|
|
293
|
+
- **Cursor rules** — AI behavioral constraints for Cursor IDE
|
|
294
|
+
- **Claude hooks** — dangerous command interception, env file protection, secret scanning
|
|
295
|
+
- **Claude settings** — permission boundaries for Claude Code
|
|
296
|
+
- **Policy file** — `.reagent/policy.yaml` with graduated autonomy levels
|
|
297
|
+
- **CLAUDE.md** — project-level AI agent instructions
|
|
298
|
+
- **Commands** — `/restart` (session handoff) and `/rea` (AI team orchestration)
|
|
299
|
+
|
|
300
|
+
### What Gets Installed
|
|
301
|
+
|
|
302
|
+
| Path | Committed | Purpose |
|
|
303
|
+
| ----------------------- | --------------- | ------------------------------------ |
|
|
304
|
+
| `.reagent/policy.yaml` | Yes | Autonomy levels and agent policy |
|
|
305
|
+
| `.reagent/gateway.yaml` | Yes | MCP gateway downstream server config |
|
|
306
|
+
| `.reagent/audit/` | No (gitignored) | Hash-chained JSONL audit logs |
|
|
307
|
+
| `.cursor/rules/` | Yes | Cursor IDE behavioral rules |
|
|
308
|
+
| `.husky/commit-msg` | Yes | Git commit message validation |
|
|
309
|
+
| `.claude/hooks/` | No (gitignored) | Claude Code safety hooks |
|
|
310
|
+
| `.claude/settings.json` | No (gitignored) | Claude Code permissions |
|
|
311
|
+
| `.claude/commands/` | Yes | Slash commands (restart, rea) |
|
|
312
|
+
| `CLAUDE.md` | Yes | AI agent project instructions |
|
|
313
|
+
|
|
314
|
+
### Profiles
|
|
70
315
|
|
|
71
316
|
| Profile | Use Case | Hooks |
|
|
72
317
|
| ------------------- | -------------------------- | --------------------------------- |
|
|
73
318
|
| `bst-internal` | BST's own repositories | Full hook suite + Claude commands |
|
|
74
319
|
| `client-engagement` | Client consulting projects | Full hook suite + Claude commands |
|
|
75
320
|
|
|
76
|
-
|
|
321
|
+
### Idempotent
|
|
77
322
|
|
|
78
323
|
Run `reagent init` as many times as you want. It skips files that are already up-to-date and only updates what has changed.
|
|
79
324
|
|
|
80
|
-
|
|
325
|
+
### Verify Installation
|
|
81
326
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
| `.husky/commit-msg` | Yes | Git commit message validation |
|
|
86
|
-
| `.claude/hooks/` | No (gitignored) | Claude Code safety hooks |
|
|
87
|
-
| `.claude/settings.json` | No (gitignored) | Claude Code permissions |
|
|
88
|
-
| `.claude/commands/` | Yes | Slash commands (restart, rea) |
|
|
89
|
-
| `.reagent/policy.yaml` | Yes | Autonomy levels and agent policy |
|
|
90
|
-
| `CLAUDE.md` | Yes | AI agent project instructions |
|
|
327
|
+
```bash
|
|
328
|
+
reagent check
|
|
329
|
+
```
|
|
91
330
|
|
|
92
331
|
## Removing Reagent
|
|
93
332
|
|
|
94
|
-
To remove reagent from a project
|
|
333
|
+
To remove reagent from a project:
|
|
95
334
|
|
|
96
335
|
```bash
|
|
97
336
|
# Remove reagent-managed files
|
|
@@ -104,15 +343,48 @@ rm -rf .reagent/
|
|
|
104
343
|
rm -f .husky/commit-msg .husky/pre-commit .husky/pre-push
|
|
105
344
|
```
|
|
106
345
|
|
|
107
|
-
##
|
|
346
|
+
## Architecture
|
|
108
347
|
|
|
109
|
-
|
|
348
|
+
```
|
|
349
|
+
@bookedsolid/reagent
|
|
350
|
+
├── src/
|
|
351
|
+
│ ├── cli/ # CLI entry point and commands
|
|
352
|
+
│ │ ├── index.ts # ESM entry point, routes to commands
|
|
353
|
+
│ │ ├── commands/ # init, check, freeze, unfreeze, serve
|
|
354
|
+
│ │ └── utils.ts # Shared CLI utilities
|
|
355
|
+
│ ├── config/ # Configuration loaders
|
|
356
|
+
│ │ ├── policy-loader.ts # Zod-validated policy.yaml parser
|
|
357
|
+
│ │ ├── gateway-config.ts # Zod-validated gateway.yaml parser
|
|
358
|
+
│ │ └── tier-map.ts # Tool tier classification
|
|
359
|
+
│ ├── gateway/ # MCP gateway core
|
|
360
|
+
│ │ ├── server.ts # Gateway orchestrator (startup, shutdown)
|
|
361
|
+
│ │ ├── client-manager.ts # Downstream MCP server connections
|
|
362
|
+
│ │ ├── tool-proxy.ts # Tool discovery, namespacing, registration
|
|
363
|
+
│ │ └── middleware/ # Middleware chain
|
|
364
|
+
│ │ ├── chain.ts # Onion-style middleware executor
|
|
365
|
+
│ │ ├── session.ts # Session ID attachment
|
|
366
|
+
│ │ ├── kill-switch.ts # HALT file check
|
|
367
|
+
│ │ ├── tier.ts # Tier classification
|
|
368
|
+
│ │ ├── policy.ts # Autonomy level enforcement
|
|
369
|
+
│ │ ├── redact.ts # Secret pattern redaction
|
|
370
|
+
│ │ └── audit.ts # Hash-chained JSONL logging
|
|
371
|
+
│ └── types/ # TypeScript type definitions
|
|
372
|
+
├── profiles/ # Init profiles (bst-internal, client-engagement)
|
|
373
|
+
├── templates/ # Template files for scaffolding
|
|
374
|
+
├── hooks/ # Git hook scripts
|
|
375
|
+
├── cursor/ # Cursor IDE rules
|
|
376
|
+
└── agents/ # Agent definitions
|
|
377
|
+
```
|
|
110
378
|
|
|
111
379
|
## Requirements
|
|
112
380
|
|
|
113
381
|
- Node.js >= 22
|
|
114
382
|
- Git repository
|
|
115
383
|
|
|
384
|
+
## Scope
|
|
385
|
+
|
|
386
|
+
Reagent is a **local CLI tool** and **MCP gateway server**. It configures files in your repository and proxies MCP tool calls on your machine. It does not collect data, phone home, or operate as a hosted service.
|
|
387
|
+
|
|
116
388
|
## License
|
|
117
389
|
|
|
118
390
|
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/check.ts"],"names":[],"mappings":"AAIA,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAsF9C"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getPkgVersion, gitignoreHasEntry } from '../utils.js';
|
|
4
|
+
export function runCheck(_args) {
|
|
5
|
+
const targetDir = process.cwd();
|
|
6
|
+
const PKG_VERSION = getPkgVersion();
|
|
7
|
+
console.log(`\n@bookedsolid/reagent v${PKG_VERSION} check`);
|
|
8
|
+
console.log(` Target: ${targetDir}\n`);
|
|
9
|
+
const checks = [
|
|
10
|
+
{
|
|
11
|
+
label: '.cursor/rules/ installed',
|
|
12
|
+
pass: () => fs.existsSync(path.join(targetDir, '.cursor', 'rules', '001-no-hallucination.mdc')),
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
label: '.husky/commit-msg installed',
|
|
16
|
+
pass: () => fs.existsSync(path.join(targetDir, '.husky', 'commit-msg')),
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
label: '.husky/pre-commit installed',
|
|
20
|
+
pass: () => fs.existsSync(path.join(targetDir, '.husky', 'pre-commit')),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
label: '.husky/pre-push installed',
|
|
24
|
+
pass: () => fs.existsSync(path.join(targetDir, '.husky', 'pre-push')),
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: '.git/hooks/commit-msg installed (fallback)',
|
|
28
|
+
pass: () => fs.existsSync(path.join(targetDir, '.git', 'hooks', 'commit-msg')),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
label: '.claude/hooks/ installed',
|
|
32
|
+
pass: () => fs.existsSync(path.join(targetDir, '.claude', 'hooks', 'dangerous-bash-interceptor.sh')),
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
label: '.claude/settings.json installed',
|
|
36
|
+
pass: () => fs.existsSync(path.join(targetDir, '.claude', 'settings.json')),
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
label: 'CLAUDE.md has reagent block',
|
|
40
|
+
pass: () => {
|
|
41
|
+
const p = path.join(targetDir, 'CLAUDE.md');
|
|
42
|
+
if (!fs.existsSync(p))
|
|
43
|
+
return false;
|
|
44
|
+
return fs.readFileSync(p, 'utf8').includes('<!-- reagent-managed:start -->');
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
label: '.reagent/policy.yaml installed',
|
|
49
|
+
pass: () => fs.existsSync(path.join(targetDir, '.reagent', 'policy.yaml')),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
label: '.gitignore has .claude/agents/',
|
|
53
|
+
pass: () => gitignoreHasEntry(targetDir, '.claude/agents/'),
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
label: '.claude/commands/restart.md installed',
|
|
57
|
+
pass: () => fs.existsSync(path.join(targetDir, '.claude', 'commands', 'restart.md')),
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
label: '.claude/commands/rea.md installed',
|
|
61
|
+
pass: () => fs.existsSync(path.join(targetDir, '.claude', 'commands', 'rea.md')),
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
let allPass = true;
|
|
65
|
+
checks.forEach(({ label, pass }) => {
|
|
66
|
+
const ok = pass();
|
|
67
|
+
console.log(` ${ok ? '✓' : '✗'} ${label}`);
|
|
68
|
+
if (!ok)
|
|
69
|
+
allPass = false;
|
|
70
|
+
});
|
|
71
|
+
// Check HALT status
|
|
72
|
+
const haltFile = path.join(targetDir, '.reagent', 'HALT');
|
|
73
|
+
if (fs.existsSync(haltFile)) {
|
|
74
|
+
const reason = fs.readFileSync(haltFile, 'utf8').trim();
|
|
75
|
+
console.log(`\n ⚠ HALT ACTIVE: ${reason}`);
|
|
76
|
+
console.log(` Run 'reagent unfreeze' to resume agent operations.`);
|
|
77
|
+
}
|
|
78
|
+
console.log('');
|
|
79
|
+
if (allPass) {
|
|
80
|
+
console.log('All checks passed.');
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
console.log('Some checks failed. Run: npx @bookedsolid/reagent init');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/cli/commands/check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"freeze.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/freeze.ts"],"names":[],"mappings":"AAIA,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAwB9C"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { parseFlag } from '../utils.js';
|
|
4
|
+
export function runFreeze(args) {
|
|
5
|
+
const targetDir = process.cwd();
|
|
6
|
+
const rawReason = parseFlag(args, '--reason') || args.find((a) => !a.startsWith('--')) || 'Manual freeze';
|
|
7
|
+
// Strip control characters (terminal escape injection defense)
|
|
8
|
+
const reason = rawReason.replace(/[\x00-\x1f\x7f]/g, '');
|
|
9
|
+
const reagentDir = path.join(targetDir, '.reagent');
|
|
10
|
+
const haltFile = path.join(reagentDir, 'HALT');
|
|
11
|
+
if (!fs.existsSync(reagentDir)) {
|
|
12
|
+
fs.mkdirSync(reagentDir, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
const timestamp = new Date().toISOString();
|
|
15
|
+
const content = `${reason} (frozen at ${timestamp})`;
|
|
16
|
+
fs.writeFileSync(haltFile, content, 'utf8');
|
|
17
|
+
console.log(`\nREAGENT FROZEN`);
|
|
18
|
+
console.log(` Reason: ${reason}`);
|
|
19
|
+
console.log(` File: .reagent/HALT`);
|
|
20
|
+
console.log(` Effect: All PreToolUse hooks will exit 2 — agent operations blocked.`);
|
|
21
|
+
console.log(`\n To resume: reagent unfreeze`);
|
|
22
|
+
console.log('');
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=freeze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"freeze.js","sourceRoot":"","sources":["../../../src/cli/commands/freeze.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAChC,MAAM,SAAS,GACb,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,eAAe,CAAC;IAC1F,+DAA+D;IAC/D,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;IAEzD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAE/C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,OAAO,GAAG,GAAG,MAAM,eAAe,SAAS,GAAG,CAAC;IACrD,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAE5C,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,EAAE,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,yEAAyE,CAAC,CAAC;IACvF,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AASA,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAuH5C"}
|