import from local vendored copy (2026-05-06)
Some checks failed
CI / validate (push) Failing after 0s
Some checks failed
CI / validate (push) Failing after 0s
This commit is contained in:
commit
7f46f585d6
5
.github/workflows/ci.yml
vendored
Normal file
5
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
name: CI
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
validate:
|
||||
uses: Molecule-AI/molecule-ci/.github/workflows/validate-plugin.yml@main
|
||||
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Credentials — never commit. Use .env.example as the template.
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.sample
|
||||
|
||||
# Private keys + certs
|
||||
*.pem
|
||||
*.key
|
||||
*.crt
|
||||
*.p12
|
||||
*.pfx
|
||||
|
||||
# Secret directories
|
||||
.secrets/
|
||||
|
||||
# Workspace auth tokens
|
||||
.auth-token
|
||||
.auth_token
|
||||
19
README.md
Normal file
19
README.md
Normal file
@ -0,0 +1,19 @@
|
||||
# molecule-prompt-watchdog
|
||||
|
||||
Molecule AI plugin. Install via the Molecule AI platform plugin system.
|
||||
|
||||
## Usage
|
||||
|
||||
### In org template (org.yaml)
|
||||
```yaml
|
||||
plugins:
|
||||
- molecule-prompt-watchdog
|
||||
```
|
||||
|
||||
### From URL (community install)
|
||||
```
|
||||
github://Molecule-AI/molecule-ai-plugin-molecule-prompt-watchdog
|
||||
```
|
||||
|
||||
## License
|
||||
Business Source License 1.1 — © Molecule AI.
|
||||
0
adapters/__init__.py
Normal file
0
adapters/__init__.py
Normal file
2
adapters/claude_code.py
Normal file
2
adapters/claude_code.py
Normal file
@ -0,0 +1,2 @@
|
||||
"""Claude Code adaptor — uses the generic rule+skill+hooks installer."""
|
||||
from plugins_registry.builtins import AgentskillsAdaptor as Adaptor # noqa: F401
|
||||
46
hooks/_lib.py
Executable file
46
hooks/_lib.py
Executable file
@ -0,0 +1,46 @@
|
||||
"""Common helpers for Claude Code hooks. Imported by the .py hook scripts.
|
||||
|
||||
Hooks receive JSON on stdin per the Claude Code hook spec, and may emit
|
||||
JSON on stdout or exit with code 2 to block. This module wraps both.
|
||||
"""
|
||||
import json
|
||||
import sys
|
||||
|
||||
|
||||
def read_input() -> dict:
|
||||
"""Parse stdin JSON. Empty input → empty dict."""
|
||||
raw = sys.stdin.read().strip()
|
||||
if not raw:
|
||||
return {}
|
||||
try:
|
||||
return json.loads(raw)
|
||||
except json.JSONDecodeError:
|
||||
return {}
|
||||
|
||||
|
||||
def emit(payload: dict) -> None:
|
||||
"""Print JSON payload to stdout for the harness to interpret."""
|
||||
print(json.dumps(payload))
|
||||
|
||||
|
||||
def deny_pretooluse(reason: str) -> None:
|
||||
"""Emit a PreToolUse denial with reason and exit 0."""
|
||||
emit({
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "PreToolUse",
|
||||
"permissionDecision": "deny",
|
||||
"permissionDecisionReason": reason,
|
||||
}
|
||||
})
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def add_context(text: str) -> None:
|
||||
"""Emit additionalContext for SessionStart / UserPromptSubmit hooks."""
|
||||
if text and text.strip():
|
||||
emit({"additionalContext": text})
|
||||
|
||||
|
||||
def warn_to_stderr(msg: str) -> None:
|
||||
"""Non-blocking warning visible to the next agent turn via stderr."""
|
||||
print(msg, file=sys.stderr)
|
||||
58
hooks/user-prompt-tag.py
Executable file
58
hooks/user-prompt-tag.py
Executable file
@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
"""UserPromptSubmit — inject context warnings for destructive-keyword prompts."""
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from _lib import read_input, add_context, warn_to_stderr # noqa
|
||||
|
||||
PATTERNS = [
|
||||
(
|
||||
["force push", "force-push", "git push -f", "--force"],
|
||||
"Mention of force-push detected. Confirm scope (which branch? to main? careful-mode REFUSES force to main).",
|
||||
),
|
||||
(
|
||||
["delete all", "drop all", "wipe all", "remove all", "clear all"],
|
||||
"'all'-scoped destructive operation detected. Re-confirm exact target set (which workspaces / which rows / which files) before tooling.",
|
||||
),
|
||||
(
|
||||
["drop table", "truncate", "delete from", "drop database"],
|
||||
"Direct SQL DDL/DML detected. Use a migration via goose or a parameterized query through platform handlers — not raw psql against prod.",
|
||||
),
|
||||
(
|
||||
["merge directly", "push to main", "commit to main", "directly to main"],
|
||||
"Mention of working on main detected. Standing rule: never push to main. Use a branch + PR.",
|
||||
),
|
||||
]
|
||||
|
||||
CLOSE_BULK = ["close all", "close every"]
|
||||
CLOSE_OBJ = ["pr", "issue", "workspace"]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
data = read_input()
|
||||
prompt = data.get("prompt", "").lower()
|
||||
if not prompt:
|
||||
return
|
||||
|
||||
warnings = []
|
||||
for needles, msg in PATTERNS:
|
||||
if any(n in prompt for n in needles):
|
||||
warnings.append(f"• {msg}")
|
||||
|
||||
if any(b in prompt for b in CLOSE_BULK) and any(o in prompt for o in CLOSE_OBJ):
|
||||
warnings.append("• Bulk close requested. List the targets first; do NOT loop a close command.")
|
||||
|
||||
if warnings:
|
||||
add_context(
|
||||
"## ⚠ Prompt-watchdog warnings\n\n"
|
||||
+ "\n".join(warnings)
|
||||
+ "\n\ncareful-mode applies — re-confirm scope before any destructive tool call."
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as e:
|
||||
warn_to_stderr(f"[prompt-tag hook error] {e}")
|
||||
sys.exit(0)
|
||||
2
hooks/user-prompt-tag.sh
Executable file
2
hooks/user-prompt-tag.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
exec python3 "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/user-prompt-tag.py"
|
||||
11
plugin.yaml
Normal file
11
plugin.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
name: molecule-prompt-watchdog
|
||||
version: 1.0.0
|
||||
description: Inject context warnings when the user prompt mentions destructive keywords (force push, drop table, delete all). UserPromptSubmit hook.
|
||||
author: Molecule AI
|
||||
tags: [molecule, guardrails]
|
||||
|
||||
runtimes:
|
||||
- claude_code
|
||||
|
||||
hooks:
|
||||
- user-prompt-tag
|
||||
1
settings-fragment.json
Normal file
1
settings-fragment.json
Normal file
@ -0,0 +1 @@
|
||||
{"hooks":{"UserPromptSubmit":[{"hooks":[{"type":"command","command":"bash ${CLAUDE_DIR}/hooks/user-prompt-tag.sh"}]}]}}
|
||||
Loading…
Reference in New Issue
Block a user