Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,26 @@ agsec audit --stats # see what would be blocked
agsec enforce # start blocking when ready
```

## Integrations
## Supported Platforms

### Claude Code / Codex (hooks)
### System Agents (hook-based enforcement)

```bash
agsec install claude-code
agsec install codex
agsec install claude-code # Claude Code + Claude Cowork
agsec install codex # OpenAI Codex
agsec install cursor # Cursor
agsec install windsurf # Windsurf (Codeium)
agsec install cline # Cline
agsec install copilot # GitHub Copilot
```

### LangChain (one line)
Claude Code and Claude Cowork are fully tested. Other integrations are functional but community testing is welcome — please report issues.

### Python SDKs (client wrapper)

### Frameworks (tool wrapper)

**LangChain:**

```python
from agsec.integrations.langchain import guard, allow, deny, review, param
Expand All @@ -56,7 +66,7 @@ agent = create_react_agent(llm, guard(
))
```

### OpenAI / Anthropic / OpenRouter (one line)
**OpenAI / Anthropic / OpenRouter:**

```python
from agsec.integrations.openai import protect, deny, param
Expand All @@ -68,7 +78,7 @@ client = protect(OpenAI(),
# Works with OpenRouter, Groq, Together — anything OpenAI-compatible
```

### Any Python function
**Any Python function:**

```python
from agsec import guard
Expand Down Expand Up @@ -109,8 +119,8 @@ Deny always wins. Same evaluation order as AWS IAM.

```bash
agsec init [--observe] # scaffold policies
agsec install claude-code # activate for Claude Code
agsec install codex # activate for Codex
agsec install <platform> # activate (claude-code, codex, cursor, windsurf, cline, copilot)
agsec uninstall <platform> # deactivate
agsec policy list # see all rules
agsec policy add # add a rule (interactive)
agsec policy remove <sid> # remove a rule
Expand All @@ -124,7 +134,7 @@ agsec enforce # switch to enforce mode

- [Policy Format](docs/policies.md) — schema, operators, conditions, examples
- [CLI Reference](docs/cli.md) — all commands in detail
- [Integrations](docs/integrations.md) — LangChain, OpenAI, Anthropic, Claude Code, Codex
- [Integrations](docs/integrations.md) — Claude Code/Cowork, Codex, Cursor, Windsurf, Cline, Copilot, LangChain, OpenAI, Anthropic
- [SDK Usage](docs/sdk.md) — programmatic Python API
- [Observe Mode](docs/observe-mode.md) — audit first, enforce later

Expand Down
28 changes: 19 additions & 9 deletions agsec/cli/commands/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

def register(subparsers):
p = subparsers.add_parser("check", help="Check an action against policies (used by hooks)")
p.add_argument("--format", choices=["generic", "claude-code", "codex"], default="generic",
help="Output format (default: generic)")
p.add_argument("--format", choices=["generic", "claude-code", "codex", "cursor", "windsurf", "cline", "copilot"],
default="generic", help="Output format (default: generic)")
p.add_argument("--policy-dir", help="Override policy directory")
p.add_argument("--action", help="Action name (if not reading from stdin)")
p.add_argument("--params", help="JSON params (if not reading from stdin)")
Expand Down Expand Up @@ -106,23 +106,33 @@ def run(args):
reason = result.reason or "Blocked by policy"
sid = result.metadata.get("sid", "")

if args.format == "claude-code":
reason_full = f"[agsec] {reason}" + (f" (sid: {sid})" if sid else "")

if args.format in ("claude-code", "windsurf"):
output = {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": f"[agsec] {reason}" + (f" (sid: {sid})" if sid else ""),
"permissionDecisionReason": reason_full,
}
}
print(json.dumps(output))
sys.exit(2)

elif args.format == "codex":
output = {
"decision": "block",
"reason": f"[agsec] {reason}" + (f" (sid: {sid})" if sid else ""),
}
print(json.dumps(output))
print(json.dumps({"decision": "block", "reason": reason_full}))
sys.exit(2)

elif args.format == "cursor":
print(json.dumps({"deny": True, "reason": reason_full}))
sys.exit(2)

elif args.format == "cline":
print(json.dumps({"cancel": True, "errorMessage": reason_full}))
sys.exit(0) # Cline uses exit 0 with cancel:true in JSON

elif args.format == "copilot":
print(json.dumps({"permissionDecision": "deny", "permissionDecisionReason": reason_full}))
sys.exit(2)

else: # generic
Expand Down
Loading
Loading