@hiro-c/agent-gate 1.0.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/AGENTS.md +76 -0
- package/LICENSE +21 -0
- package/README.md +205 -0
- package/dist/adapters/Adapter.d.ts +32 -0
- package/dist/adapters/Adapter.d.ts.map +1 -0
- package/dist/adapters/Adapter.js +2 -0
- package/dist/adapters/claude-code/adapter.d.ts +3 -0
- package/dist/adapters/claude-code/adapter.d.ts.map +1 -0
- package/dist/adapters/claude-code/adapter.js +45 -0
- package/dist/adapters/claude-code/transcript.d.ts +16 -0
- package/dist/adapters/claude-code/transcript.d.ts.map +1 -0
- package/dist/adapters/claude-code/transcript.js +104 -0
- package/dist/adapters/cursor/adapter.d.ts +3 -0
- package/dist/adapters/cursor/adapter.d.ts.map +1 -0
- package/dist/adapters/cursor/adapter.js +89 -0
- package/dist/adapters/index.d.ts +8 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +20 -0
- package/dist/cli/agent-gate.d.ts +13 -0
- package/dist/cli/agent-gate.d.ts.map +1 -0
- package/dist/cli/agent-gate.js +226 -0
- package/dist/cli/installer.d.ts +21 -0
- package/dist/cli/installer.d.ts.map +1 -0
- package/dist/cli/installer.js +71 -0
- package/dist/collector/collectClaudeMd.d.ts +3 -0
- package/dist/collector/collectClaudeMd.d.ts.map +1 -0
- package/dist/collector/collectClaudeMd.js +87 -0
- package/dist/collector/collectRuleSources.d.ts +3 -0
- package/dist/collector/collectRuleSources.d.ts.map +1 -0
- package/dist/collector/collectRuleSources.js +151 -0
- package/dist/config/AgentGateConfig.d.ts +18 -0
- package/dist/config/AgentGateConfig.d.ts.map +1 -0
- package/dist/config/AgentGateConfig.js +34 -0
- package/dist/config/Config.d.ts +26 -0
- package/dist/config/Config.d.ts.map +1 -0
- package/dist/config/Config.js +25 -0
- package/dist/config/PluginConfigLoader.d.ts +3 -0
- package/dist/config/PluginConfigLoader.d.ts.map +1 -0
- package/dist/config/PluginConfigLoader.js +85 -0
- package/dist/config/defineConfig.d.ts +23 -0
- package/dist/config/defineConfig.d.ts.map +1 -0
- package/dist/config/defineConfig.js +10 -0
- package/dist/contracts/schemas/hookDataSchema.d.ts +7 -0
- package/dist/contracts/schemas/hookDataSchema.d.ts.map +1 -0
- package/dist/contracts/schemas/hookDataSchema.js +9 -0
- package/dist/contracts/types/Action.d.ts +23 -0
- package/dist/contracts/types/Action.d.ts.map +1 -0
- package/dist/contracts/types/Action.js +2 -0
- package/dist/contracts/types/ClaudeMdFile.d.ts +5 -0
- package/dist/contracts/types/ClaudeMdFile.d.ts.map +1 -0
- package/dist/contracts/types/ClaudeMdFile.js +2 -0
- package/dist/contracts/types/HookData.d.ts +6 -0
- package/dist/contracts/types/HookData.d.ts.map +1 -0
- package/dist/contracts/types/HookData.js +2 -0
- package/dist/contracts/types/ModelClient.d.ts +4 -0
- package/dist/contracts/types/ModelClient.d.ts.map +1 -0
- package/dist/contracts/types/ModelClient.js +2 -0
- package/dist/contracts/types/RuleSource.d.ts +7 -0
- package/dist/contracts/types/RuleSource.d.ts.map +1 -0
- package/dist/contracts/types/RuleSource.js +2 -0
- package/dist/contracts/types/SessionContext.d.ts +25 -0
- package/dist/contracts/types/SessionContext.d.ts.map +1 -0
- package/dist/contracts/types/SessionContext.js +2 -0
- package/dist/contracts/types/ValidationResult.d.ts +5 -0
- package/dist/contracts/types/ValidationResult.d.ts.map +1 -0
- package/dist/contracts/types/ValidationResult.js +2 -0
- package/dist/daemon/client.d.ts +17 -0
- package/dist/daemon/client.d.ts.map +1 -0
- package/dist/daemon/client.js +59 -0
- package/dist/daemon/protocol.d.ts +17 -0
- package/dist/daemon/protocol.d.ts.map +1 -0
- package/dist/daemon/protocol.js +8 -0
- package/dist/daemon/server.d.ts +27 -0
- package/dist/daemon/server.d.ts.map +1 -0
- package/dist/daemon/server.js +100 -0
- package/dist/deterministic/defaultRules.d.ts +11 -0
- package/dist/deterministic/defaultRules.d.ts.map +1 -0
- package/dist/deterministic/defaultRules.js +33 -0
- package/dist/deterministic/engine.d.ts +11 -0
- package/dist/deterministic/engine.d.ts.map +1 -0
- package/dist/deterministic/engine.js +12 -0
- package/dist/deterministic/factories.d.ts +20 -0
- package/dist/deterministic/factories.d.ts.map +1 -0
- package/dist/deterministic/factories.js +56 -0
- package/dist/deterministic/rules/preventBashSecretWrite.d.ts +3 -0
- package/dist/deterministic/rules/preventBashSecretWrite.d.ts.map +1 -0
- package/dist/deterministic/rules/preventBashSecretWrite.js +75 -0
- package/dist/deterministic/rules/preventForcePushMain.d.ts +7 -0
- package/dist/deterministic/rules/preventForcePushMain.d.ts.map +1 -0
- package/dist/deterministic/rules/preventForcePushMain.js +85 -0
- package/dist/deterministic/rules/preventRmRfRoot.d.ts +3 -0
- package/dist/deterministic/rules/preventRmRfRoot.d.ts.map +1 -0
- package/dist/deterministic/rules/preventRmRfRoot.js +68 -0
- package/dist/deterministic/rules/preventSecretFileWrite.d.ts +7 -0
- package/dist/deterministic/rules/preventSecretFileWrite.d.ts.map +1 -0
- package/dist/deterministic/rules/preventSecretFileWrite.js +55 -0
- package/dist/deterministic/rules/preventSystemPathWrite.d.ts +3 -0
- package/dist/deterministic/rules/preventSystemPathWrite.d.ts.map +1 -0
- package/dist/deterministic/rules/preventSystemPathWrite.js +38 -0
- package/dist/deterministic/types.d.ts +20 -0
- package/dist/deterministic/types.d.ts.map +1 -0
- package/dist/deterministic/types.js +2 -0
- package/dist/doctor/findings.d.ts +15 -0
- package/dist/doctor/findings.d.ts.map +1 -0
- package/dist/doctor/findings.js +2 -0
- package/dist/doctor/formatFindings.d.ts +3 -0
- package/dist/doctor/formatFindings.d.ts.map +1 -0
- package/dist/doctor/formatFindings.js +37 -0
- package/dist/doctor/lintRuleSources.d.ts +4 -0
- package/dist/doctor/lintRuleSources.d.ts.map +1 -0
- package/dist/doctor/lintRuleSources.js +87 -0
- package/dist/hooks/processHookData.d.ts +37 -0
- package/dist/hooks/processHookData.d.ts.map +1 -0
- package/dist/hooks/processHookData.js +181 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/observability/decisionLogger.d.ts +15 -0
- package/dist/observability/decisionLogger.d.ts.map +1 -0
- package/dist/observability/decisionLogger.js +20 -0
- package/dist/observability/eventBus.d.ts +15 -0
- package/dist/observability/eventBus.d.ts.map +1 -0
- package/dist/observability/eventBus.js +33 -0
- package/dist/observability/sinks/JsonlFileSink.d.ts +13 -0
- package/dist/observability/sinks/JsonlFileSink.d.ts.map +1 -0
- package/dist/observability/sinks/JsonlFileSink.js +36 -0
- package/dist/observability/sinks/Sink.d.ts +46 -0
- package/dist/observability/sinks/Sink.d.ts.map +1 -0
- package/dist/observability/sinks/Sink.js +2 -0
- package/dist/observability/stats.d.ts +14 -0
- package/dist/observability/stats.d.ts.map +1 -0
- package/dist/observability/stats.js +78 -0
- package/dist/validation/models/AnthropicApi.d.ts +9 -0
- package/dist/validation/models/AnthropicApi.d.ts.map +1 -0
- package/dist/validation/models/AnthropicApi.js +44 -0
- package/dist/validation/models/ClaudeCli.d.ts +10 -0
- package/dist/validation/models/ClaudeCli.d.ts.map +1 -0
- package/dist/validation/models/ClaudeCli.js +64 -0
- package/dist/validation/models/CompositeModelClient.d.ts +20 -0
- package/dist/validation/models/CompositeModelClient.d.ts.map +1 -0
- package/dist/validation/models/CompositeModelClient.js +53 -0
- package/dist/validation/prompts/context.d.ts +3 -0
- package/dist/validation/prompts/context.d.ts.map +1 -0
- package/dist/validation/prompts/context.js +29 -0
- package/dist/validation/prompts/response.d.ts +2 -0
- package/dist/validation/prompts/response.d.ts.map +1 -0
- package/dist/validation/prompts/response.js +20 -0
- package/dist/validation/prompts/system-prompt.d.ts +7 -0
- package/dist/validation/prompts/system-prompt.d.ts.map +1 -0
- package/dist/validation/prompts/system-prompt.js +69 -0
- package/dist/validation/validator.d.ts +5 -0
- package/dist/validation/validator.d.ts.map +1 -0
- package/dist/validation/validator.js +98 -0
- package/package.json +67 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# AGENTS.md for agent-gate
|
|
2
|
+
|
|
3
|
+
This file is read by any AI coding agent working on this repo, including
|
|
4
|
+
Claude Code, Cursor, Cline, and Aider. It is the project's vendor-neutral
|
|
5
|
+
instruction file, complementing the existing `CLAUDE.md`.
|
|
6
|
+
|
|
7
|
+
## What this project is
|
|
8
|
+
|
|
9
|
+
agent-gate is a runtime enforcer for AI coding agent rules. It reads
|
|
10
|
+
natural-language instruction files (CLAUDE.md, AGENTS.md, .cursorrules,
|
|
11
|
+
.cursor/rules/*.mdc, .clinerules, .windsurf/rules, .github/copilot-instructions.md,
|
|
12
|
+
CONVENTIONS.md) as one combined rule set and enforces them at hook time
|
|
13
|
+
in Claude Code and Cursor. A deterministic safety baseline catches
|
|
14
|
+
catastrophic operations before any AI call.
|
|
15
|
+
|
|
16
|
+
## Hard rules
|
|
17
|
+
|
|
18
|
+
These are non-negotiable while modifying this codebase.
|
|
19
|
+
|
|
20
|
+
- Never weaken the deterministic safety rules to make a test pass. If a
|
|
21
|
+
rule blocks something it should not, narrow the rule by adding an
|
|
22
|
+
explicit allow-case, never by deleting the protection.
|
|
23
|
+
- Never commit any file matching `.env`, `.env.*` (other than
|
|
24
|
+
`.env.example`), `*.pem`, `*.key`, or files inside `.ssh/`.
|
|
25
|
+
- Never run `npm publish` from a local checkout. Publishing is performed
|
|
26
|
+
exclusively by the GitHub Actions release workflow on a signed `v*`
|
|
27
|
+
tag push (using OIDC + npm provenance). This keeps releases
|
|
28
|
+
reproducible and supply-chain auditable.
|
|
29
|
+
|
|
30
|
+
## Soft rules (style and approach)
|
|
31
|
+
|
|
32
|
+
- Strict TDD for every new rule: write a failing test, make it pass with
|
|
33
|
+
the smallest possible change, refactor, commit.
|
|
34
|
+
- One logical change per commit. Use Conventional Commits prefixes
|
|
35
|
+
(`feat`, `fix`, `refactor`, `chore`, `docs`, `test`).
|
|
36
|
+
- Prefer adding a new deterministic rule over expanding an existing one
|
|
37
|
+
when the concept is distinct; small, single-purpose rules are easier
|
|
38
|
+
to disable per project.
|
|
39
|
+
- Keep block reasons guidance-shaped: name the violated rule and the
|
|
40
|
+
next correct step the agent should take. No bare denials.
|
|
41
|
+
- Tests live alongside the file they cover: `test/<mirror of src path>`.
|
|
42
|
+
|
|
43
|
+
## Development workflow
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install
|
|
47
|
+
npm run checks # typecheck + vitest
|
|
48
|
+
npm run build # tsc to dist/
|
|
49
|
+
npm test # vitest run
|
|
50
|
+
npm run typecheck # tsc --noEmit
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Every change should keep `npm run checks` green before being committed.
|
|
54
|
+
|
|
55
|
+
## Project structure
|
|
56
|
+
|
|
57
|
+
- `src/adapters/` — vendor-specific hook payload parsing and response
|
|
58
|
+
formatting (Claude Code, Cursor).
|
|
59
|
+
- `src/collector/` — multi-source instruction-file aggregator.
|
|
60
|
+
- `src/config/` — `Config` (env vars) and `AgentGateConfig`
|
|
61
|
+
(`.agent-gate.json`).
|
|
62
|
+
- `src/contracts/` — shared types (`Action`, `RuleSource`,
|
|
63
|
+
`ValidationResult`) and Zod schemas.
|
|
64
|
+
- `src/deterministic/` — the safety baseline. `engine.ts` runs rules
|
|
65
|
+
in order, `rules/*` are individual rules, `defaultRules.ts` exports
|
|
66
|
+
the curated default list.
|
|
67
|
+
- `src/hooks/processHookData.ts` — pipeline orchestrator.
|
|
68
|
+
- `src/observability/` — decision logger and stats aggregator.
|
|
69
|
+
- `src/validation/` — AI client interface, model implementations
|
|
70
|
+
(`ClaudeCli`, `AnthropicApi`), prompt templates.
|
|
71
|
+
|
|
72
|
+
## When in doubt
|
|
73
|
+
|
|
74
|
+
Read `docs/architecture.md` (kept locally, gitignored) for the v1
|
|
75
|
+
roadmap and design rationale. Otherwise, prefer the smallest change
|
|
76
|
+
that keeps `npm run checks` green and ship it.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hiro-Chiba
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# agent-gate
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Hiro-Chiba/agent-gate/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
One natural-language rule source, enforced at runtime across multiple AI coding tools.
|
|
6
|
+
|
|
7
|
+
agent-gate reads the instruction files you already have (`CLAUDE.md`, `AGENTS.md`, `.cursorrules`, `.cursor/rules/*.mdc`, `.clinerules`, `.windsurf/rules`, `.github/copilot-instructions.md`, `CONVENTIONS.md`) as a single combined rule set, then enforces them at hook time in Claude Code and Cursor. A deterministic safety baseline catches catastrophic operations before any AI call.
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
The AI coding tool landscape in 2026 has fragmented into many tools, each with its own instruction file format. Existing tooling either syncs rule files across tools (rulesync, symlinks) or enforces a single rule format at runtime (probity, tdd-guard), but not both. agent-gate sits in the gap: it accepts whatever natural-language instruction files you already maintain and enforces them across multiple AI tools through a single hook.
|
|
12
|
+
|
|
13
|
+
Two pain points the project is built to address:
|
|
14
|
+
|
|
15
|
+
- **Rule forgetting** in long agent sessions, where context compression drops the rules from the prompt and the agent quietly drifts off-spec.
|
|
16
|
+
- **Destructive operations** like `rm -rf $HOME` or force-pushing main, which AI judgment is too unreliable to catch consistently.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- Multi-source rule collection across 8 instruction file formats, surfaced to AI with per-source attribution.
|
|
21
|
+
- Adapter pattern: same binary works in Claude Code (`--agent claude-code`, default) and Cursor 1.7 (`--agent cursor`).
|
|
22
|
+
- Deterministic safety baseline with five built-in rules that fire before any AI call.
|
|
23
|
+
- AI validation against the combined rule set when the safety baseline passes.
|
|
24
|
+
- Per-rule disable and per-project customization via `.agent-gate.json` and `AGENT_GATE_DISABLED_RULES`.
|
|
25
|
+
- Optional decision log (`AGENT_GATE_LOG=1`) and `agent-gate stats` for auditing.
|
|
26
|
+
- Block reasons are guidance, not denials: the AI is instructed to describe the next correct step alongside the violated rule.
|
|
27
|
+
|
|
28
|
+
## Requirements
|
|
29
|
+
|
|
30
|
+
- Node.js >= 22.0.0
|
|
31
|
+
- Claude Code or Cursor 1.7
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
git clone https://github.com/Hiro-Chiba/agent-gate.git
|
|
37
|
+
cd agent-gate
|
|
38
|
+
./install.sh
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The install script handles dependency install, build, and Claude Code hook registration. Restart Claude Code to activate.
|
|
42
|
+
|
|
43
|
+
To register against Cursor instead of Claude Code, run the binary with `--agent cursor` from your Cursor hook config. To remove the Claude Code hook, run `./uninstall.sh`.
|
|
44
|
+
|
|
45
|
+
## Configuration
|
|
46
|
+
|
|
47
|
+
### Environment variables
|
|
48
|
+
|
|
49
|
+
| Variable | Default | Description |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `AGENT_GATE_MODEL` | `claude-sonnet-4-6` | Model used for AI validation |
|
|
52
|
+
| `AGENT_GATE_API_KEY` | (none) | Anthropic API key. Uses the API directly when set; otherwise spawns the Claude CLI |
|
|
53
|
+
| `AGENT_GATE_COOLDOWN` | `0` | Cooldown in seconds between AI validations (deterministic rules always fire) |
|
|
54
|
+
| `AGENT_GATE_DISABLED` | `false` | Set to `true` to disable the whole tool |
|
|
55
|
+
| `AGENT_GATE_DISABLED_RULES` | (none) | Comma-separated rule ids to disable, merged with the config file |
|
|
56
|
+
| `AGENT_GATE_LOG` | (none) | Set to `1` to append decisions to `~/.agent-gate/log.jsonl` |
|
|
57
|
+
| `AGENT_GATE_REASON_LANG` | `auto` | Language for AI-generated `reason` text. `auto` matches the instruction files (English fallback when mixed). Pass `en`, `ja`, `zh`, `ko`, etc. to force a specific language |
|
|
58
|
+
| `USE_SYSTEM_CLAUDE` | `false` | `true` forces PATH `claude` (default: `~/.claude/local/claude` with PATH fallback) |
|
|
59
|
+
|
|
60
|
+
### Project config file
|
|
61
|
+
|
|
62
|
+
agent-gate looks for either a TypeScript / JavaScript config or a legacy JSON config, walking upward from the cwd until it finds one. Precedence (highest wins): `.agent-gate.config.ts` > `.mts` > `.mjs` > `.cjs` > `.js` > `.agent-gate.json`.
|
|
63
|
+
|
|
64
|
+
#### TypeScript / JavaScript config
|
|
65
|
+
|
|
66
|
+
The full API including custom rules:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
// .agent-gate.config.ts
|
|
70
|
+
import { defineConfig, forbidCommandPattern, forbidFilePathPattern } from 'agent-gate'
|
|
71
|
+
|
|
72
|
+
export default defineConfig({
|
|
73
|
+
disabledRules: ['prevent-force-push-main'],
|
|
74
|
+
protectedBranches: ['main', 'release'],
|
|
75
|
+
extraSecretPathPrefixes: ['vault/', 'secrets/'],
|
|
76
|
+
customRules: [
|
|
77
|
+
forbidCommandPattern({
|
|
78
|
+
id: 'no-drop-table',
|
|
79
|
+
match: /drop\s+table/i,
|
|
80
|
+
reason: 'DROP TABLE is forbidden. Use a migration instead.',
|
|
81
|
+
}),
|
|
82
|
+
forbidFilePathPattern({
|
|
83
|
+
id: 'no-prod-config',
|
|
84
|
+
match: /production\.ya?ml$/,
|
|
85
|
+
reason: 'Production config edits go through ops review.',
|
|
86
|
+
}),
|
|
87
|
+
],
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
| Field | Effect |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `disabledRules` | List of rule ids that will not run. Merged with `AGENT_GATE_DISABLED_RULES`. |
|
|
94
|
+
| `protectedBranches` | Overrides the default list used by `prevent-force-push-main`. |
|
|
95
|
+
| `extraSecretPathPrefixes` | Additional path substrings treated as secret targets by `prevent-secret-file-write`. |
|
|
96
|
+
| `customRules` | User-defined `DeterministicRule[]` appended after the built-ins. Use the `forbidCommandPattern` / `forbidContentPattern` / `forbidFilePathPattern` factories or hand-write your own. |
|
|
97
|
+
|
|
98
|
+
#### Legacy JSON config
|
|
99
|
+
|
|
100
|
+
Still works for simple cases:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"disabled_rules": ["prevent-force-push-main"],
|
|
105
|
+
"protected_branches": ["main", "release"],
|
|
106
|
+
"extra_secret_paths": ["vault/", "secrets/"]
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
JSON cannot express custom rules. Migrate to the TS/JS form when you need them.
|
|
111
|
+
|
|
112
|
+
## How It Works
|
|
113
|
+
|
|
114
|
+
1. The AI coding tool fires a pre-tool-use hook with its vendor-specific JSON payload.
|
|
115
|
+
2. The selected adapter parses that payload into a normalized `Action`.
|
|
116
|
+
3. Deterministic safety rules run first. Catastrophic patterns are blocked here without calling AI.
|
|
117
|
+
4. If the safety baseline passes, agent-gate collects all 8 instruction file formats present in the project tree.
|
|
118
|
+
5. The AI validates the operation against the combined rule set, with each source attributed by kind.
|
|
119
|
+
6. The verdict (block + guidance, or allow) is returned through the adapter's response formatter.
|
|
120
|
+
|
|
121
|
+
## Built-in Safety Rules
|
|
122
|
+
|
|
123
|
+
| Rule | Blocks |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `prevent-rm-rf-root` | Recursive `rm` on `/`, `$HOME`, `~`, `/etc`, `/usr`, `/var`, and other catastrophic paths. Handles `sudo` prefix and flag variants (`-rf`, `-fr`, `-Rf`). |
|
|
126
|
+
| `prevent-secret-file-write` | `Edit`/`Write` on `.env*` (non-template), `.ssh/*`, `.aws/credentials`, `*.pem`, `*.key`, `id_rsa`, `.netrc`. |
|
|
127
|
+
| `prevent-bash-secret-write` | Shell redirects to the same secret paths via `>`, `>>`, or `tee`. |
|
|
128
|
+
| `prevent-force-push-main` | `git push --force` or `-f` to `main`, `master`, `develop`, `production`, `release`, `stable`. Allows `--force-with-lease`. |
|
|
129
|
+
| `prevent-system-path-write` | `Edit`/`Write` to `/etc`, `/usr`, `/var`, `/System`, `/Library`, `/opt`, and other system-owned paths. |
|
|
130
|
+
|
|
131
|
+
Each rule can be disabled individually through `.agent-gate.json` or the env var.
|
|
132
|
+
|
|
133
|
+
## Supported Instruction File Formats
|
|
134
|
+
|
|
135
|
+
agent-gate aggregates rules from any combination of:
|
|
136
|
+
|
|
137
|
+
- `CLAUDE.md` (Claude Code)
|
|
138
|
+
- `AGENTS.md` (cross-tool spec backed by the Linux Foundation Agentic AI Foundation)
|
|
139
|
+
- `.cursorrules` (Cursor legacy)
|
|
140
|
+
- `.cursor/rules/*.mdc` (Cursor current)
|
|
141
|
+
- `.clinerules/*.md` (Cline)
|
|
142
|
+
- `.windsurf/rules/*.md` (Windsurf)
|
|
143
|
+
- `.github/copilot-instructions.md` (GitHub Copilot)
|
|
144
|
+
- `CONVENTIONS.md` (Aider)
|
|
145
|
+
|
|
146
|
+
You do not need to choose. Maintain whichever file your team already uses; agent-gate reads them all.
|
|
147
|
+
|
|
148
|
+
## Supported AI Coding Tools
|
|
149
|
+
|
|
150
|
+
agent-gate enforces in any tool that exposes a pre-tool-use hook. As of v1:
|
|
151
|
+
|
|
152
|
+
- Claude Code (mature). `agent-gate --agent claude-code`, the default.
|
|
153
|
+
- Cursor 1.7 (beta). `agent-gate --agent cursor`. Payload mapping is best-effort against public docs.
|
|
154
|
+
|
|
155
|
+
Tools without a hook surface (Copilot, Cline, Aider, Codex web, Replit, Devin) can still benefit from agent-gate as a rule source linter or via downstream sync (rulesync, symlinks), but cannot be enforced at runtime.
|
|
156
|
+
|
|
157
|
+
## CLAUDE.md Doctor
|
|
158
|
+
|
|
159
|
+
Run `agent-gate lint` from a project root to audit your instruction files for AI-friendliness. The doctor walks the same 8 file formats the runtime reads, then surfaces:
|
|
160
|
+
|
|
161
|
+
- **Empty files** that would make the AI think no rules exist.
|
|
162
|
+
- **Files with no concrete rules** (no imperatives, no bullets, no numbered items).
|
|
163
|
+
- **Ambiguous modifiers** like "where possible", "as needed", "適切に", "なるべく", "可能な限り", "必要に応じて". AI judgment cannot enforce these reliably; the doctor suggests replacing them with a concrete condition or threshold.
|
|
164
|
+
|
|
165
|
+
```text
|
|
166
|
+
$ agent-gate lint
|
|
167
|
+
/p/CLAUDE.md
|
|
168
|
+
[warning] no-concrete-rules: No imperatives ...
|
|
169
|
+
[info] ambiguous-modifier (line 5): Ambiguous modifier "適切に" ...
|
|
170
|
+
> - エラーは適切に扱う
|
|
171
|
+
|
|
172
|
+
1 finding.
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Exit code is 1 if any finding has severity `error`, otherwise 0, so the command can run in CI.
|
|
176
|
+
|
|
177
|
+
## Daemon mode
|
|
178
|
+
|
|
179
|
+
Each hook invocation normally spawns a fresh Node process (cold start ~300ms). For users that fire hooks at high frequency, agent-gate can run as a long-lived daemon on a Unix socket and let hook invocations reuse the warm process.
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# Terminal 1: start the daemon (foreground; manage with systemd / launchctl / tmux in production).
|
|
183
|
+
agent-gate daemon
|
|
184
|
+
|
|
185
|
+
# Terminal 2 (or in your hook config):
|
|
186
|
+
AGENT_GATE_DAEMON=1 agent-gate
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
When `AGENT_GATE_DAEMON=1`, the hook tries the socket first and transparently falls back to direct mode if the daemon is unreachable. Set `AGENT_GATE_SOCKET_PATH` to override the default `$TMPDIR/agent-gate.sock`.
|
|
190
|
+
|
|
191
|
+
The daemon is opt-in. Users not setting `AGENT_GATE_DAEMON=1` keep the existing one-shot behavior.
|
|
192
|
+
|
|
193
|
+
## Observability
|
|
194
|
+
|
|
195
|
+
Set `AGENT_GATE_LOG=1` to append every decision to `~/.agent-gate/log.jsonl`. Each line is a JSON object with timestamp, adapter, tool, decision, reason, source (`deterministic` / `ai`), and `ruleId` when a deterministic rule fired.
|
|
196
|
+
|
|
197
|
+
Run `agent-gate stats` for a summary: total decisions, block percentage, breakdown by source, adapter, tool, and rule id.
|
|
198
|
+
|
|
199
|
+
## Network Access
|
|
200
|
+
|
|
201
|
+
agent-gate only communicates with Anthropic endpoints, either directly via the Anthropic API (when `AGENT_GATE_API_KEY` is set) or indirectly through the Claude CLI subprocess. It does not contact any other external services and does not send telemetry. Deterministic rules run entirely locally.
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ParsedHook } from '../contracts/types/Action';
|
|
2
|
+
import { ValidationResult } from '../contracts/types/ValidationResult';
|
|
3
|
+
import { SessionEvent } from '../contracts/types/SessionContext';
|
|
4
|
+
export interface ReadHistoryOptions {
|
|
5
|
+
cwd: string;
|
|
6
|
+
/** Max events to return. Adapters should honor this; default is impl-defined. */
|
|
7
|
+
limit?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* An Adapter bridges a specific AI coding tool's hook API to agent-gate's
|
|
11
|
+
* normalized pipeline. Each adapter:
|
|
12
|
+
* 1. parses the vendor's stdin JSON into a ParsedHook,
|
|
13
|
+
* 2. formats agent-gate's ValidationResult into the vendor's expected
|
|
14
|
+
* stdout JSON,
|
|
15
|
+
* 3. optionally reads the vendor's transcript file to provide
|
|
16
|
+
* SessionContext.history to rules and the AI prompt.
|
|
17
|
+
*/
|
|
18
|
+
export interface Adapter {
|
|
19
|
+
/** Identifier used for CLI dispatch and logging. */
|
|
20
|
+
readonly id: string;
|
|
21
|
+
/** Parse stdin JSON into a normalized ParsedHook. */
|
|
22
|
+
parseHook(stdinJson: string): ParsedHook;
|
|
23
|
+
/** Format a ValidationResult into the vendor-specific stdout JSON. */
|
|
24
|
+
formatResponse(result: ValidationResult): string;
|
|
25
|
+
/**
|
|
26
|
+
* Read recent session events from the vendor's transcript. Optional;
|
|
27
|
+
* an adapter that cannot read history should omit this method or
|
|
28
|
+
* return an empty array.
|
|
29
|
+
*/
|
|
30
|
+
readHistory?(opts: ReadHistoryOptions): Promise<SessionEvent[]>;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=Adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/Adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAA;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAA;AAEhE,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,iFAAiF;IACjF,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,OAAO;IACtB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IAEnB,qDAAqD;IACrD,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAAA;IAExC,sEAAsE;IACtE,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAAA;IAEhD;;;;OAIG;IACH,WAAW,CAAC,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;CAChE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/claude-code/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAsB,MAAM,YAAY,CAAA;AASxD,eAAO,MAAM,iBAAiB,EAAE,OA4C/B,CAAA"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.claudeCodeAdapter = void 0;
|
|
4
|
+
const hookDataSchema_1 = require("../../contracts/schemas/hookDataSchema");
|
|
5
|
+
const transcript_1 = require("./transcript");
|
|
6
|
+
const HOOK_EVENT_PRE_TOOL_USE = 'PreToolUse';
|
|
7
|
+
exports.claudeCodeAdapter = {
|
|
8
|
+
id: 'claude-code',
|
|
9
|
+
parseHook(stdinJson) {
|
|
10
|
+
let raw;
|
|
11
|
+
try {
|
|
12
|
+
raw = JSON.parse(stdinJson);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return { kind: 'skip', reason: 'invalid JSON' };
|
|
16
|
+
}
|
|
17
|
+
const parsed = hookDataSchema_1.HookDataSchema.safeParse(raw);
|
|
18
|
+
if (!parsed.success) {
|
|
19
|
+
return { kind: 'skip', reason: 'unrecognized hook payload' };
|
|
20
|
+
}
|
|
21
|
+
const data = parsed.data;
|
|
22
|
+
if (data.hook_event_name !== HOOK_EVENT_PRE_TOOL_USE) {
|
|
23
|
+
return { kind: 'skip', reason: `not a PreToolUse event: ${data.hook_event_name}` };
|
|
24
|
+
}
|
|
25
|
+
const toolName = data.tool_name;
|
|
26
|
+
const toolInput = data.tool_input;
|
|
27
|
+
if (!toolName || !toolInput) {
|
|
28
|
+
return { kind: 'skip', reason: 'missing tool_name or tool_input' };
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
kind: 'action',
|
|
32
|
+
action: { toolName, toolInput },
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
formatResponse(result) {
|
|
36
|
+
if (result.decision === 'block') {
|
|
37
|
+
return JSON.stringify({ decision: 'block', reason: result.reason });
|
|
38
|
+
}
|
|
39
|
+
// Allow case: Claude Code expects no `decision` key (or null) for allow.
|
|
40
|
+
return JSON.stringify({ reason: result.reason });
|
|
41
|
+
},
|
|
42
|
+
async readHistory(opts) {
|
|
43
|
+
return (0, transcript_1.readClaudeCodeTranscript)({ cwd: opts.cwd, limit: opts.limit });
|
|
44
|
+
},
|
|
45
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SessionEvent } from '../../contracts/types/SessionContext';
|
|
2
|
+
/**
|
|
3
|
+
* Encode a project path into the directory name Claude Code uses under
|
|
4
|
+
* `~/.claude/projects/`. The convention: replace `/` with `-`, drop
|
|
5
|
+
* trailing slash. e.g. `/Users/me/proj` -> `-Users-me-proj`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function encodeProjectsPath(cwd: string): string;
|
|
8
|
+
export interface ReadClaudeCodeTranscriptOptions {
|
|
9
|
+
cwd: string;
|
|
10
|
+
/** Override $HOME for testing. Defaults to os.homedir(). */
|
|
11
|
+
home?: string;
|
|
12
|
+
/** Max events to return. Returns the last `limit` events. */
|
|
13
|
+
limit?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function readClaudeCodeTranscript(opts: ReadClaudeCodeTranscriptOptions): Promise<SessionEvent[]>;
|
|
16
|
+
//# sourceMappingURL=transcript.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../../../src/adapters/claude-code/transcript.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAA;AAEnE;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMtD;AAED,MAAM,WAAW,+BAA+B;IAC9C,GAAG,EAAE,MAAM,CAAA;IACX,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AA6DD,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,+BAA+B,GACpC,OAAO,CAAC,YAAY,EAAE,CAAC,CA+BzB"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.encodeProjectsPath = encodeProjectsPath;
|
|
4
|
+
exports.readClaudeCodeTranscript = readClaudeCodeTranscript;
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const os_1 = require("os");
|
|
8
|
+
/**
|
|
9
|
+
* Encode a project path into the directory name Claude Code uses under
|
|
10
|
+
* `~/.claude/projects/`. The convention: replace `/` with `-`, drop
|
|
11
|
+
* trailing slash. e.g. `/Users/me/proj` -> `-Users-me-proj`.
|
|
12
|
+
*/
|
|
13
|
+
function encodeProjectsPath(cwd) {
|
|
14
|
+
let trimmed = cwd;
|
|
15
|
+
if (trimmed.length > 1 && trimmed.endsWith('/')) {
|
|
16
|
+
trimmed = trimmed.slice(0, -1);
|
|
17
|
+
}
|
|
18
|
+
return trimmed.replace(/\//g, '-');
|
|
19
|
+
}
|
|
20
|
+
function classify(raw) {
|
|
21
|
+
const t = raw.type;
|
|
22
|
+
if (t === 'tool_use') {
|
|
23
|
+
return {
|
|
24
|
+
kind: 'tool-call',
|
|
25
|
+
toolName: typeof raw.name === 'string' ? raw.name : undefined,
|
|
26
|
+
toolInput: raw.input && typeof raw.input === 'object'
|
|
27
|
+
? raw.input
|
|
28
|
+
: undefined,
|
|
29
|
+
timestamp: raw.timestamp,
|
|
30
|
+
raw,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (t === 'tool_result') {
|
|
34
|
+
return {
|
|
35
|
+
kind: 'tool-result',
|
|
36
|
+
timestamp: raw.timestamp,
|
|
37
|
+
raw,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (t === 'user') {
|
|
41
|
+
return { kind: 'user-message', timestamp: raw.timestamp, raw };
|
|
42
|
+
}
|
|
43
|
+
if (t === 'assistant') {
|
|
44
|
+
return { kind: 'assistant-message', timestamp: raw.timestamp, raw };
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function pickMostRecentJsonl(dir) {
|
|
49
|
+
let entries;
|
|
50
|
+
try {
|
|
51
|
+
entries = (0, fs_1.readdirSync)(dir).filter((name) => name.endsWith('.jsonl'));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (entries.length === 0)
|
|
57
|
+
return null;
|
|
58
|
+
let best = null;
|
|
59
|
+
for (const name of entries) {
|
|
60
|
+
try {
|
|
61
|
+
const m = (0, fs_1.statSync)((0, path_1.join)(dir, name)).mtimeMs;
|
|
62
|
+
if (!best || m > best.mtime)
|
|
63
|
+
best = { name, mtime: m };
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// skip
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return best ? (0, path_1.join)(dir, best.name) : null;
|
|
70
|
+
}
|
|
71
|
+
async function readClaudeCodeTranscript(opts) {
|
|
72
|
+
const home = opts.home ?? (0, os_1.homedir)();
|
|
73
|
+
const dir = (0, path_1.join)(home, '.claude', 'projects', encodeProjectsPath(opts.cwd));
|
|
74
|
+
if (!(0, fs_1.existsSync)(dir))
|
|
75
|
+
return [];
|
|
76
|
+
const file = pickMostRecentJsonl(dir);
|
|
77
|
+
if (!file)
|
|
78
|
+
return [];
|
|
79
|
+
let content;
|
|
80
|
+
try {
|
|
81
|
+
content = (0, fs_1.readFileSync)(file, 'utf-8');
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
const events = [];
|
|
87
|
+
for (const line of content.split('\n')) {
|
|
88
|
+
const trimmed = line.trim();
|
|
89
|
+
if (!trimmed)
|
|
90
|
+
continue;
|
|
91
|
+
let raw;
|
|
92
|
+
try {
|
|
93
|
+
raw = JSON.parse(trimmed);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const evt = classify(raw);
|
|
99
|
+
if (evt)
|
|
100
|
+
events.push(evt);
|
|
101
|
+
}
|
|
102
|
+
const limit = opts.limit ?? events.length;
|
|
103
|
+
return events.slice(-limit);
|
|
104
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/cursor/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AA2BpC,eAAO,MAAM,aAAa,EAAE,OAoE3B,CAAA"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cursorAdapter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Cursor 1.7 hook adapter.
|
|
6
|
+
*
|
|
7
|
+
* Cursor exposes pre-execution hooks (beforeShellExecution, beforeReadFile,
|
|
8
|
+
* beforeFileEdit) and post-event hooks (afterFileEdit). agent-gate is a
|
|
9
|
+
* prevention tool, so it only handles the "before" hooks; "after" payloads
|
|
10
|
+
* are skipped because the operation already happened.
|
|
11
|
+
*
|
|
12
|
+
* The payload schema below is the best-effort shape based on Cursor's
|
|
13
|
+
* public docs as of early 2026. Field names may need adjustment as
|
|
14
|
+
* Cursor's hook API matures.
|
|
15
|
+
*/
|
|
16
|
+
const PRE_HOOK_EVENTS = new Set([
|
|
17
|
+
'beforeShellExecution',
|
|
18
|
+
'beforeReadFile',
|
|
19
|
+
'beforeFileEdit',
|
|
20
|
+
]);
|
|
21
|
+
function isObject(v) {
|
|
22
|
+
return typeof v === 'object' && v !== null;
|
|
23
|
+
}
|
|
24
|
+
exports.cursorAdapter = {
|
|
25
|
+
id: 'cursor',
|
|
26
|
+
parseHook(stdinJson) {
|
|
27
|
+
let raw;
|
|
28
|
+
try {
|
|
29
|
+
raw = JSON.parse(stdinJson);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return { kind: 'skip', reason: 'invalid JSON' };
|
|
33
|
+
}
|
|
34
|
+
if (!isObject(raw)) {
|
|
35
|
+
return { kind: 'skip', reason: 'payload is not an object' };
|
|
36
|
+
}
|
|
37
|
+
const event = raw['hook_event_name'];
|
|
38
|
+
if (typeof event !== 'string' || !PRE_HOOK_EVENTS.has(event)) {
|
|
39
|
+
return { kind: 'skip', reason: `not a pre-hook event: ${String(event)}` };
|
|
40
|
+
}
|
|
41
|
+
switch (event) {
|
|
42
|
+
case 'beforeShellExecution': {
|
|
43
|
+
const command = raw['command'];
|
|
44
|
+
if (typeof command !== 'string') {
|
|
45
|
+
return { kind: 'skip', reason: 'missing command' };
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
kind: 'action',
|
|
49
|
+
action: { toolName: 'Bash', toolInput: { command } },
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
case 'beforeReadFile': {
|
|
53
|
+
const filePath = raw['file_path'];
|
|
54
|
+
if (typeof filePath !== 'string') {
|
|
55
|
+
return { kind: 'skip', reason: 'missing file_path' };
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
kind: 'action',
|
|
59
|
+
action: { toolName: 'Read', toolInput: { file_path: filePath } },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
case 'beforeFileEdit': {
|
|
63
|
+
const filePath = raw['file_path'];
|
|
64
|
+
if (typeof filePath !== 'string') {
|
|
65
|
+
return { kind: 'skip', reason: 'missing file_path' };
|
|
66
|
+
}
|
|
67
|
+
const toolInput = { file_path: filePath };
|
|
68
|
+
if (typeof raw['new_content'] === 'string') {
|
|
69
|
+
toolInput.new_content = raw['new_content'];
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
kind: 'action',
|
|
73
|
+
action: { toolName: 'Edit', toolInput },
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
default:
|
|
77
|
+
return { kind: 'skip', reason: 'unrecognized event' };
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
formatResponse(result) {
|
|
81
|
+
if (result.decision === 'block') {
|
|
82
|
+
return JSON.stringify({
|
|
83
|
+
permission: 'deny',
|
|
84
|
+
userMessage: result.reason,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return JSON.stringify({ permission: 'allow' });
|
|
88
|
+
},
|
|
89
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Adapter } from './Adapter';
|
|
2
|
+
import { claudeCodeAdapter } from './claude-code/adapter';
|
|
3
|
+
import { cursorAdapter } from './cursor/adapter';
|
|
4
|
+
export declare const DEFAULT_ADAPTER_ID = "claude-code";
|
|
5
|
+
export declare function getAdapter(id: string): Adapter | undefined;
|
|
6
|
+
export declare function availableAdapterIds(): string[];
|
|
7
|
+
export { Adapter, claudeCodeAdapter, cursorAdapter };
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAEhD,eAAO,MAAM,kBAAkB,gBAAgB,CAAA;AAO/C,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAE1D;AAED,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C;AAED,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cursorAdapter = exports.claudeCodeAdapter = exports.DEFAULT_ADAPTER_ID = void 0;
|
|
4
|
+
exports.getAdapter = getAdapter;
|
|
5
|
+
exports.availableAdapterIds = availableAdapterIds;
|
|
6
|
+
const adapter_1 = require("./claude-code/adapter");
|
|
7
|
+
Object.defineProperty(exports, "claudeCodeAdapter", { enumerable: true, get: function () { return adapter_1.claudeCodeAdapter; } });
|
|
8
|
+
const adapter_2 = require("./cursor/adapter");
|
|
9
|
+
Object.defineProperty(exports, "cursorAdapter", { enumerable: true, get: function () { return adapter_2.cursorAdapter; } });
|
|
10
|
+
exports.DEFAULT_ADAPTER_ID = 'claude-code';
|
|
11
|
+
const ADAPTER_REGISTRY = {
|
|
12
|
+
'claude-code': adapter_1.claudeCodeAdapter,
|
|
13
|
+
cursor: adapter_2.cursorAdapter,
|
|
14
|
+
};
|
|
15
|
+
function getAdapter(id) {
|
|
16
|
+
return ADAPTER_REGISTRY[id];
|
|
17
|
+
}
|
|
18
|
+
function availableAdapterIds() {
|
|
19
|
+
return Object.keys(ADAPTER_REGISTRY).sort();
|
|
20
|
+
}
|