tweek 0.1.0__py3-none-any.whl
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.
- tweek/__init__.py +16 -0
- tweek/cli.py +3390 -0
- tweek/cli_helpers.py +193 -0
- tweek/config/__init__.py +13 -0
- tweek/config/allowed_dirs.yaml +23 -0
- tweek/config/manager.py +1064 -0
- tweek/config/patterns.yaml +751 -0
- tweek/config/tiers.yaml +129 -0
- tweek/diagnostics.py +589 -0
- tweek/hooks/__init__.py +1 -0
- tweek/hooks/pre_tool_use.py +861 -0
- tweek/integrations/__init__.py +3 -0
- tweek/integrations/moltbot.py +243 -0
- tweek/licensing.py +398 -0
- tweek/logging/__init__.py +9 -0
- tweek/logging/bundle.py +350 -0
- tweek/logging/json_logger.py +150 -0
- tweek/logging/security_log.py +745 -0
- tweek/mcp/__init__.py +24 -0
- tweek/mcp/approval.py +456 -0
- tweek/mcp/approval_cli.py +356 -0
- tweek/mcp/clients/__init__.py +37 -0
- tweek/mcp/clients/chatgpt.py +112 -0
- tweek/mcp/clients/claude_desktop.py +203 -0
- tweek/mcp/clients/gemini.py +178 -0
- tweek/mcp/proxy.py +667 -0
- tweek/mcp/screening.py +175 -0
- tweek/mcp/server.py +317 -0
- tweek/platform/__init__.py +131 -0
- tweek/plugins/__init__.py +835 -0
- tweek/plugins/base.py +1080 -0
- tweek/plugins/compliance/__init__.py +30 -0
- tweek/plugins/compliance/gdpr.py +333 -0
- tweek/plugins/compliance/gov.py +324 -0
- tweek/plugins/compliance/hipaa.py +285 -0
- tweek/plugins/compliance/legal.py +322 -0
- tweek/plugins/compliance/pci.py +361 -0
- tweek/plugins/compliance/soc2.py +275 -0
- tweek/plugins/detectors/__init__.py +30 -0
- tweek/plugins/detectors/continue_dev.py +206 -0
- tweek/plugins/detectors/copilot.py +254 -0
- tweek/plugins/detectors/cursor.py +192 -0
- tweek/plugins/detectors/moltbot.py +205 -0
- tweek/plugins/detectors/windsurf.py +214 -0
- tweek/plugins/git_discovery.py +395 -0
- tweek/plugins/git_installer.py +491 -0
- tweek/plugins/git_lockfile.py +338 -0
- tweek/plugins/git_registry.py +503 -0
- tweek/plugins/git_security.py +482 -0
- tweek/plugins/providers/__init__.py +30 -0
- tweek/plugins/providers/anthropic.py +181 -0
- tweek/plugins/providers/azure_openai.py +289 -0
- tweek/plugins/providers/bedrock.py +248 -0
- tweek/plugins/providers/google.py +197 -0
- tweek/plugins/providers/openai.py +230 -0
- tweek/plugins/scope.py +130 -0
- tweek/plugins/screening/__init__.py +26 -0
- tweek/plugins/screening/llm_reviewer.py +149 -0
- tweek/plugins/screening/pattern_matcher.py +273 -0
- tweek/plugins/screening/rate_limiter.py +174 -0
- tweek/plugins/screening/session_analyzer.py +159 -0
- tweek/proxy/__init__.py +302 -0
- tweek/proxy/addon.py +223 -0
- tweek/proxy/interceptor.py +313 -0
- tweek/proxy/server.py +315 -0
- tweek/sandbox/__init__.py +71 -0
- tweek/sandbox/executor.py +382 -0
- tweek/sandbox/linux.py +278 -0
- tweek/sandbox/profile_generator.py +323 -0
- tweek/screening/__init__.py +13 -0
- tweek/screening/context.py +81 -0
- tweek/security/__init__.py +22 -0
- tweek/security/llm_reviewer.py +348 -0
- tweek/security/rate_limiter.py +682 -0
- tweek/security/secret_scanner.py +506 -0
- tweek/security/session_analyzer.py +600 -0
- tweek/vault/__init__.py +40 -0
- tweek/vault/cross_platform.py +251 -0
- tweek/vault/keychain.py +288 -0
- tweek-0.1.0.dist-info/METADATA +335 -0
- tweek-0.1.0.dist-info/RECORD +85 -0
- tweek-0.1.0.dist-info/WHEEL +5 -0
- tweek-0.1.0.dist-info/entry_points.txt +25 -0
- tweek-0.1.0.dist-info/licenses/LICENSE +190 -0
- tweek-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Claude Desktop MCP Client Configuration
|
|
4
|
+
|
|
5
|
+
Auto-configures Claude Desktop to use Tweek as an MCP security server.
|
|
6
|
+
Config file: ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import shutil
|
|
11
|
+
import sys
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Optional, Dict, Any
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ClaudeDesktopClient:
|
|
18
|
+
"""Manage Tweek MCP integration with Claude Desktop."""
|
|
19
|
+
|
|
20
|
+
CLIENT_NAME = "claude-desktop"
|
|
21
|
+
DISPLAY_NAME = "Claude Desktop"
|
|
22
|
+
SERVER_KEY = "tweek-security"
|
|
23
|
+
|
|
24
|
+
def _get_config_path(self) -> Path:
|
|
25
|
+
"""Get Claude Desktop config path based on platform."""
|
|
26
|
+
if sys.platform == "darwin":
|
|
27
|
+
return (
|
|
28
|
+
Path.home()
|
|
29
|
+
/ "Library"
|
|
30
|
+
/ "Application Support"
|
|
31
|
+
/ "Claude"
|
|
32
|
+
/ "claude_desktop_config.json"
|
|
33
|
+
)
|
|
34
|
+
elif sys.platform == "win32":
|
|
35
|
+
appdata = Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming"))
|
|
36
|
+
return appdata / "Claude" / "claude_desktop_config.json"
|
|
37
|
+
else:
|
|
38
|
+
# Linux - XDG config
|
|
39
|
+
xdg_config = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config"))
|
|
40
|
+
return xdg_config / "claude" / "claude_desktop_config.json"
|
|
41
|
+
|
|
42
|
+
def _get_tweek_command(self) -> str:
|
|
43
|
+
"""Get the path to the tweek executable."""
|
|
44
|
+
# Use the installed tweek command
|
|
45
|
+
tweek_path = shutil.which("tweek")
|
|
46
|
+
if tweek_path:
|
|
47
|
+
return tweek_path
|
|
48
|
+
# Fallback to python -m
|
|
49
|
+
return sys.executable
|
|
50
|
+
|
|
51
|
+
def _get_tweek_args(self) -> list:
|
|
52
|
+
"""Get the arguments for the tweek MCP server."""
|
|
53
|
+
tweek_path = shutil.which("tweek")
|
|
54
|
+
if tweek_path:
|
|
55
|
+
return ["mcp", "serve"]
|
|
56
|
+
return ["-m", "tweek.mcp", "serve"]
|
|
57
|
+
|
|
58
|
+
def _build_server_config(self) -> Dict[str, Any]:
|
|
59
|
+
"""Build the MCP server configuration entry."""
|
|
60
|
+
return {
|
|
61
|
+
"command": self._get_tweek_command(),
|
|
62
|
+
"args": self._get_tweek_args(),
|
|
63
|
+
"env": {},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
def _backup_config(self, config_path: Path) -> Optional[Path]:
|
|
67
|
+
"""Create a timestamped backup of the config file."""
|
|
68
|
+
if not config_path.exists():
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
72
|
+
backup_path = config_path.with_suffix(f".backup_{timestamp}.json")
|
|
73
|
+
shutil.copy2(config_path, backup_path)
|
|
74
|
+
return backup_path
|
|
75
|
+
|
|
76
|
+
def install(self) -> Dict[str, Any]:
|
|
77
|
+
"""
|
|
78
|
+
Install Tweek MCP server into Claude Desktop config.
|
|
79
|
+
|
|
80
|
+
Merges with existing config, preserving other MCP servers.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Dict with status, message, and any backup info.
|
|
84
|
+
"""
|
|
85
|
+
config_path = self._get_config_path()
|
|
86
|
+
|
|
87
|
+
# Load existing config
|
|
88
|
+
existing = {}
|
|
89
|
+
if config_path.exists():
|
|
90
|
+
try:
|
|
91
|
+
existing = json.loads(config_path.read_text())
|
|
92
|
+
except json.JSONDecodeError:
|
|
93
|
+
return {
|
|
94
|
+
"success": False,
|
|
95
|
+
"error": f"Invalid JSON in {config_path}. Fix manually or delete to reset.",
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# Backup
|
|
99
|
+
backup = self._backup_config(config_path)
|
|
100
|
+
|
|
101
|
+
# Merge Tweek into mcpServers
|
|
102
|
+
existing.setdefault("mcpServers", {})
|
|
103
|
+
existing["mcpServers"][self.SERVER_KEY] = self._build_server_config()
|
|
104
|
+
|
|
105
|
+
# Write config
|
|
106
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
107
|
+
config_path.write_text(json.dumps(existing, indent=2))
|
|
108
|
+
|
|
109
|
+
result = {
|
|
110
|
+
"success": True,
|
|
111
|
+
"client": self.DISPLAY_NAME,
|
|
112
|
+
"config_path": str(config_path),
|
|
113
|
+
"server_key": self.SERVER_KEY,
|
|
114
|
+
"message": (
|
|
115
|
+
f"Tweek MCP server installed for {self.DISPLAY_NAME}. "
|
|
116
|
+
f"Restart Claude Desktop to activate."
|
|
117
|
+
),
|
|
118
|
+
}
|
|
119
|
+
if backup:
|
|
120
|
+
result["backup"] = str(backup)
|
|
121
|
+
|
|
122
|
+
return result
|
|
123
|
+
|
|
124
|
+
def uninstall(self) -> Dict[str, Any]:
|
|
125
|
+
"""
|
|
126
|
+
Remove Tweek MCP server from Claude Desktop config.
|
|
127
|
+
|
|
128
|
+
Preserves other MCP servers.
|
|
129
|
+
"""
|
|
130
|
+
config_path = self._get_config_path()
|
|
131
|
+
|
|
132
|
+
if not config_path.exists():
|
|
133
|
+
return {
|
|
134
|
+
"success": True,
|
|
135
|
+
"message": f"No config file found at {config_path}. Nothing to remove.",
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
config = json.loads(config_path.read_text())
|
|
140
|
+
except json.JSONDecodeError:
|
|
141
|
+
return {
|
|
142
|
+
"success": False,
|
|
143
|
+
"error": f"Invalid JSON in {config_path}",
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
servers = config.get("mcpServers", {})
|
|
147
|
+
if self.SERVER_KEY not in servers:
|
|
148
|
+
return {
|
|
149
|
+
"success": True,
|
|
150
|
+
"message": f"Tweek not found in {self.DISPLAY_NAME} config. Nothing to remove.",
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# Backup and remove
|
|
154
|
+
backup = self._backup_config(config_path)
|
|
155
|
+
del servers[self.SERVER_KEY]
|
|
156
|
+
|
|
157
|
+
config_path.write_text(json.dumps(config, indent=2))
|
|
158
|
+
|
|
159
|
+
result = {
|
|
160
|
+
"success": True,
|
|
161
|
+
"client": self.DISPLAY_NAME,
|
|
162
|
+
"config_path": str(config_path),
|
|
163
|
+
"message": (
|
|
164
|
+
f"Tweek MCP server removed from {self.DISPLAY_NAME}. "
|
|
165
|
+
f"Restart Claude Desktop to apply."
|
|
166
|
+
),
|
|
167
|
+
}
|
|
168
|
+
if backup:
|
|
169
|
+
result["backup"] = str(backup)
|
|
170
|
+
|
|
171
|
+
return result
|
|
172
|
+
|
|
173
|
+
def status(self) -> Dict[str, Any]:
|
|
174
|
+
"""Check if Tweek is installed in Claude Desktop."""
|
|
175
|
+
config_path = self._get_config_path()
|
|
176
|
+
|
|
177
|
+
if not config_path.exists():
|
|
178
|
+
return {
|
|
179
|
+
"installed": False,
|
|
180
|
+
"client": self.DISPLAY_NAME,
|
|
181
|
+
"config_path": str(config_path),
|
|
182
|
+
"message": "Config file not found",
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
config = json.loads(config_path.read_text())
|
|
187
|
+
servers = config.get("mcpServers", {})
|
|
188
|
+
installed = self.SERVER_KEY in servers
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
"installed": installed,
|
|
192
|
+
"client": self.DISPLAY_NAME,
|
|
193
|
+
"config_path": str(config_path),
|
|
194
|
+
"server_config": servers.get(self.SERVER_KEY) if installed else None,
|
|
195
|
+
"other_servers": [k for k in servers if k != self.SERVER_KEY],
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
except json.JSONDecodeError:
|
|
199
|
+
return {
|
|
200
|
+
"installed": False,
|
|
201
|
+
"error": "Invalid JSON in config file",
|
|
202
|
+
"config_path": str(config_path),
|
|
203
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Gemini CLI MCP Client Configuration
|
|
4
|
+
|
|
5
|
+
Gemini CLI supports MCP servers via ~/.gemini/settings.json.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import shutil
|
|
10
|
+
import sys
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Dict, Any, Optional
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GeminiClient:
|
|
17
|
+
"""Manage Tweek MCP integration with Gemini CLI."""
|
|
18
|
+
|
|
19
|
+
CLIENT_NAME = "gemini"
|
|
20
|
+
DISPLAY_NAME = "Gemini CLI"
|
|
21
|
+
SERVER_KEY = "tweek-security"
|
|
22
|
+
|
|
23
|
+
def _get_config_path(self) -> Path:
|
|
24
|
+
"""Get Gemini CLI settings path."""
|
|
25
|
+
return Path.home() / ".gemini" / "settings.json"
|
|
26
|
+
|
|
27
|
+
def _get_tweek_command(self) -> str:
|
|
28
|
+
"""Get the path to the tweek executable."""
|
|
29
|
+
tweek_path = shutil.which("tweek")
|
|
30
|
+
if tweek_path:
|
|
31
|
+
return tweek_path
|
|
32
|
+
return sys.executable
|
|
33
|
+
|
|
34
|
+
def _get_tweek_args(self) -> list:
|
|
35
|
+
"""Get the arguments for the tweek MCP server."""
|
|
36
|
+
tweek_path = shutil.which("tweek")
|
|
37
|
+
if tweek_path:
|
|
38
|
+
return ["mcp", "serve"]
|
|
39
|
+
return ["-m", "tweek.mcp", "serve"]
|
|
40
|
+
|
|
41
|
+
def _build_server_config(self) -> Dict[str, Any]:
|
|
42
|
+
"""Build the MCP server configuration entry."""
|
|
43
|
+
return {
|
|
44
|
+
"command": self._get_tweek_command(),
|
|
45
|
+
"args": self._get_tweek_args(),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def _backup_config(self, config_path: Path) -> Optional[Path]:
|
|
49
|
+
"""Create a timestamped backup of the config file."""
|
|
50
|
+
if not config_path.exists():
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
54
|
+
backup_path = config_path.with_suffix(f".backup_{timestamp}.json")
|
|
55
|
+
shutil.copy2(config_path, backup_path)
|
|
56
|
+
return backup_path
|
|
57
|
+
|
|
58
|
+
def install(self) -> Dict[str, Any]:
|
|
59
|
+
"""
|
|
60
|
+
Install Tweek MCP server into Gemini CLI settings.
|
|
61
|
+
|
|
62
|
+
Merges with existing config, preserving other MCP servers.
|
|
63
|
+
"""
|
|
64
|
+
config_path = self._get_config_path()
|
|
65
|
+
|
|
66
|
+
# Load existing config
|
|
67
|
+
existing = {}
|
|
68
|
+
if config_path.exists():
|
|
69
|
+
try:
|
|
70
|
+
existing = json.loads(config_path.read_text())
|
|
71
|
+
except json.JSONDecodeError:
|
|
72
|
+
return {
|
|
73
|
+
"success": False,
|
|
74
|
+
"error": f"Invalid JSON in {config_path}. Fix manually or delete to reset.",
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# Backup
|
|
78
|
+
backup = self._backup_config(config_path)
|
|
79
|
+
|
|
80
|
+
# Merge Tweek into mcpServers
|
|
81
|
+
existing.setdefault("mcpServers", {})
|
|
82
|
+
existing["mcpServers"][self.SERVER_KEY] = self._build_server_config()
|
|
83
|
+
|
|
84
|
+
# Write config
|
|
85
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
86
|
+
config_path.write_text(json.dumps(existing, indent=2))
|
|
87
|
+
|
|
88
|
+
result = {
|
|
89
|
+
"success": True,
|
|
90
|
+
"client": self.DISPLAY_NAME,
|
|
91
|
+
"config_path": str(config_path),
|
|
92
|
+
"server_key": self.SERVER_KEY,
|
|
93
|
+
"message": (
|
|
94
|
+
f"Tweek MCP server installed for {self.DISPLAY_NAME}. "
|
|
95
|
+
f"Restart Gemini CLI to activate."
|
|
96
|
+
),
|
|
97
|
+
}
|
|
98
|
+
if backup:
|
|
99
|
+
result["backup"] = str(backup)
|
|
100
|
+
|
|
101
|
+
return result
|
|
102
|
+
|
|
103
|
+
def uninstall(self) -> Dict[str, Any]:
|
|
104
|
+
"""Remove Tweek MCP server from Gemini CLI settings."""
|
|
105
|
+
config_path = self._get_config_path()
|
|
106
|
+
|
|
107
|
+
if not config_path.exists():
|
|
108
|
+
return {
|
|
109
|
+
"success": True,
|
|
110
|
+
"message": f"No config file found at {config_path}. Nothing to remove.",
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
config = json.loads(config_path.read_text())
|
|
115
|
+
except json.JSONDecodeError:
|
|
116
|
+
return {
|
|
117
|
+
"success": False,
|
|
118
|
+
"error": f"Invalid JSON in {config_path}",
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
servers = config.get("mcpServers", {})
|
|
122
|
+
if self.SERVER_KEY not in servers:
|
|
123
|
+
return {
|
|
124
|
+
"success": True,
|
|
125
|
+
"message": f"Tweek not found in {self.DISPLAY_NAME} config. Nothing to remove.",
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# Backup and remove
|
|
129
|
+
backup = self._backup_config(config_path)
|
|
130
|
+
del servers[self.SERVER_KEY]
|
|
131
|
+
|
|
132
|
+
config_path.write_text(json.dumps(config, indent=2))
|
|
133
|
+
|
|
134
|
+
result = {
|
|
135
|
+
"success": True,
|
|
136
|
+
"client": self.DISPLAY_NAME,
|
|
137
|
+
"config_path": str(config_path),
|
|
138
|
+
"message": (
|
|
139
|
+
f"Tweek MCP server removed from {self.DISPLAY_NAME}. "
|
|
140
|
+
f"Restart Gemini CLI to apply."
|
|
141
|
+
),
|
|
142
|
+
}
|
|
143
|
+
if backup:
|
|
144
|
+
result["backup"] = str(backup)
|
|
145
|
+
|
|
146
|
+
return result
|
|
147
|
+
|
|
148
|
+
def status(self) -> Dict[str, Any]:
|
|
149
|
+
"""Check if Tweek is installed in Gemini CLI."""
|
|
150
|
+
config_path = self._get_config_path()
|
|
151
|
+
|
|
152
|
+
if not config_path.exists():
|
|
153
|
+
return {
|
|
154
|
+
"installed": False,
|
|
155
|
+
"client": self.DISPLAY_NAME,
|
|
156
|
+
"config_path": str(config_path),
|
|
157
|
+
"message": "Gemini CLI settings not found",
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
config = json.loads(config_path.read_text())
|
|
162
|
+
servers = config.get("mcpServers", {})
|
|
163
|
+
installed = self.SERVER_KEY in servers
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
"installed": installed,
|
|
167
|
+
"client": self.DISPLAY_NAME,
|
|
168
|
+
"config_path": str(config_path),
|
|
169
|
+
"server_config": servers.get(self.SERVER_KEY) if installed else None,
|
|
170
|
+
"other_servers": [k for k in servers if k != self.SERVER_KEY],
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
except json.JSONDecodeError:
|
|
174
|
+
return {
|
|
175
|
+
"installed": False,
|
|
176
|
+
"error": "Invalid JSON in settings file",
|
|
177
|
+
"config_path": str(config_path),
|
|
178
|
+
}
|