2026-08-10 02:15:34 -03:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
"""Apply persistent non-interactive settings for managed coding clients."""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
import os
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
from typing import Any
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def configure_claude_state(path: Path) -> None:
|
|
|
|
|
"""Disable Claude's long-session resume chooser without losing state."""
|
|
|
|
|
value: dict[str, Any] = {}
|
|
|
|
|
if path.is_file():
|
|
|
|
|
try:
|
|
|
|
|
loaded = json.loads(path.read_text(encoding="utf-8"))
|
|
|
|
|
except (OSError, json.JSONDecodeError):
|
|
|
|
|
loaded = {}
|
|
|
|
|
if isinstance(loaded, dict):
|
|
|
|
|
value = loaded
|
2026-08-10 02:24:32 -03:00
|
|
|
value["bypassPermissionsModeAccepted"] = True
|
2026-08-10 02:15:34 -03:00
|
|
|
value["resumeReturnDismissed"] = True
|
|
|
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
path.write_text(json.dumps(value, indent=2) + "\n", encoding="utf-8")
|
|
|
|
|
os.chmod(path, 0o600)
|
|
|
|
|
|
|
|
|
|
|
2026-08-10 16:31:06 -03:00
|
|
|
def configure_claude_settings(path: Path) -> None:
|
|
|
|
|
"""Layer unattended deny rules and a command hook onto Claude settings."""
|
|
|
|
|
value: dict[str, Any] = {}
|
|
|
|
|
if path.is_file():
|
|
|
|
|
try:
|
|
|
|
|
loaded = json.loads(path.read_text(encoding="utf-8"))
|
|
|
|
|
except (OSError, json.JSONDecodeError):
|
|
|
|
|
loaded = {}
|
|
|
|
|
if isinstance(loaded, dict):
|
|
|
|
|
value = loaded
|
|
|
|
|
|
|
|
|
|
permissions = value.get("permissions")
|
|
|
|
|
if not isinstance(permissions, dict):
|
|
|
|
|
permissions = {}
|
2026-08-10 17:05:14 -03:00
|
|
|
legacy_managed_denies = [
|
2026-08-10 16:31:06 -03:00
|
|
|
"Bash(kubectl apply *)",
|
|
|
|
|
"Bash(kubectl delete *)",
|
|
|
|
|
"Bash(kubectl patch *)",
|
|
|
|
|
"Bash(kubectl scale *)",
|
|
|
|
|
"Bash(kubectl exec *)",
|
|
|
|
|
"Bash(kubectl port-forward *)",
|
|
|
|
|
"Bash(flux reconcile *)",
|
|
|
|
|
"Bash(flux suspend *)",
|
|
|
|
|
"Bash(flux resume *)",
|
|
|
|
|
"Bash(vault kv *)",
|
2026-08-10 17:05:14 -03:00
|
|
|
]
|
|
|
|
|
managed_denies = [
|
2026-08-10 16:31:06 -03:00
|
|
|
"Bash(git push --force *)",
|
|
|
|
|
"Bash(git reset --hard *)",
|
|
|
|
|
"Bash(git clean -f *)",
|
|
|
|
|
]
|
|
|
|
|
existing_denies = permissions.get("deny")
|
|
|
|
|
if not isinstance(existing_denies, list):
|
|
|
|
|
existing_denies = []
|
2026-08-10 17:05:14 -03:00
|
|
|
previous_managed = {*legacy_managed_denies, *managed_denies}
|
|
|
|
|
existing_denies = [
|
|
|
|
|
str(item) for item in existing_denies if str(item) not in previous_managed
|
|
|
|
|
]
|
2026-08-10 16:31:06 -03:00
|
|
|
permissions["deny"] = [
|
2026-08-10 17:05:14 -03:00
|
|
|
*existing_denies,
|
|
|
|
|
*managed_denies,
|
2026-08-10 16:31:06 -03:00
|
|
|
]
|
|
|
|
|
value["permissions"] = permissions
|
|
|
|
|
|
|
|
|
|
hooks = value.get("hooks")
|
|
|
|
|
if not isinstance(hooks, dict):
|
|
|
|
|
hooks = {}
|
|
|
|
|
managed_hook = {
|
|
|
|
|
"matcher": "Bash",
|
|
|
|
|
"hooks": [
|
|
|
|
|
{
|
|
|
|
|
"type": "command",
|
|
|
|
|
"command": "/opt/hermes/.venv/bin/python /opt/coordinator/claude_command_policy.py",
|
|
|
|
|
"timeout": 10,
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
existing_hooks = hooks.get("PreToolUse")
|
|
|
|
|
if not isinstance(existing_hooks, list):
|
|
|
|
|
existing_hooks = []
|
|
|
|
|
existing_hooks = [
|
|
|
|
|
item
|
|
|
|
|
for item in existing_hooks
|
|
|
|
|
if "claude_command_policy.py" not in json.dumps(item, sort_keys=True)
|
|
|
|
|
]
|
|
|
|
|
hooks["PreToolUse"] = [*existing_hooks, managed_hook]
|
|
|
|
|
value["hooks"] = hooks
|
|
|
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
path.write_text(json.dumps(value, indent=2) + "\n", encoding="utf-8")
|
|
|
|
|
os.chmod(path, 0o600)
|
|
|
|
|
|
|
|
|
|
|
2026-08-10 02:15:34 -03:00
|
|
|
def main() -> None:
|
|
|
|
|
"""Configure provider clients under the persistent Hermes home."""
|
|
|
|
|
home = Path(os.environ.get("CLAUDE_CONFIG_DIR", "/opt/data/home/.claude"))
|
|
|
|
|
configure_claude_state(home / ".claude.json")
|
2026-08-10 16:31:06 -03:00
|
|
|
configure_claude_settings(home / "settings.json")
|
2026-08-10 02:15:34 -03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|