codeshield-ai 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.
codeshield/__init__.py ADDED
@@ -0,0 +1,62 @@
1
+ """
2
+ CodeShield - The Complete AI Coding Safety Net
3
+
4
+ Verify, Enforce, Remember - AI-powered code verification for safe development.
5
+
6
+ Example usage:
7
+ >>> from codeshield import verify_code, check_style, full_verify
8
+ >>>
9
+ >>> # Quick verification
10
+ >>> result = verify_code("print('hello')")
11
+ >>> print(result.is_valid)
12
+ True
13
+ >>>
14
+ >>> # Full verification with sandbox execution
15
+ >>> result = full_verify("x = 1 + 2\\nprint(x)")
16
+ >>> print(result['overall_valid'])
17
+ True
18
+ """
19
+
20
+ __version__ = "0.1.0"
21
+ __author__ = "CodeShield Team"
22
+ __license__ = "MIT"
23
+
24
+ # Core verification functions
25
+ from codeshield.trustgate.checker import verify_code, VerificationResult
26
+ from codeshield.trustgate.sandbox import full_verification as full_verify, SandboxVerification
27
+
28
+ # Style checking
29
+ from codeshield.styleforge.corrector import check_style, StyleCheckResult
30
+
31
+ # Context management
32
+ from codeshield.contextvault.capture import save_context, list_contexts, get_context
33
+ from codeshield.contextvault.restore import restore_context
34
+
35
+ # Utilities
36
+ from codeshield.utils.daytona import DaytonaClient, get_daytona_client
37
+ from codeshield.utils.llm import LLMClient, get_llm_client
38
+
39
+ __all__ = [
40
+ # Version info
41
+ "__version__",
42
+ "__author__",
43
+ "__license__",
44
+ # Core verification
45
+ "verify_code",
46
+ "full_verify",
47
+ "VerificationResult",
48
+ "SandboxVerification",
49
+ # Style
50
+ "check_style",
51
+ "StyleCheckResult",
52
+ # Context
53
+ "save_context",
54
+ "restore_context",
55
+ "list_contexts",
56
+ "get_context",
57
+ # Utilities
58
+ "DaytonaClient",
59
+ "get_daytona_client",
60
+ "LLMClient",
61
+ "get_llm_client",
62
+ ]
@@ -0,0 +1,438 @@
1
+ """
2
+ CodeShield API Server
3
+ Exposes CodeShield functionality via HTTP for the React Frontend.
4
+ """
5
+
6
+ from fastapi import FastAPI, HTTPException, Body
7
+ from fastapi.middleware.cors import CORSMiddleware
8
+ from pydantic import BaseModel
9
+ from typing import List, Optional, Any
10
+ import uvicorn
11
+ import os
12
+ from fastapi.staticfiles import StaticFiles
13
+ from fastapi.responses import FileResponse
14
+
15
+ # Import CodeShield core modules
16
+ # We wrap imports in try/except to handle potential missing dependencies during dev
17
+ try:
18
+ from codeshield.trustgate.checker import verify_code
19
+ from codeshield.trustgate.sandbox import full_verification
20
+ from codeshield.styleforge.corrector import check_style
21
+ from codeshield.contextvault.capture import save_context, list_contexts
22
+ from codeshield.contextvault.restore import restore_context
23
+ except ImportError:
24
+ # Fallback for dev environment if paths aren't set up
25
+ print("WARNING: Could not import CodeShield modules. Ensure you are running as a module.")
26
+ verify_code = None
27
+ full_verification = None
28
+ check_style = None
29
+ save_context = None
30
+ list_contexts = None
31
+ restore_context = None
32
+
33
+ app = FastAPI(title="CodeShield API", version="0.1.0")
34
+
35
+ # Configure CORS
36
+ app.add_middleware(
37
+ CORSMiddleware,
38
+ allow_origins=["http://localhost:5173", "http://127.0.0.1:5173"], # Vite dev server
39
+ allow_credentials=True,
40
+ allow_methods=["*"],
41
+ allow_headers=["*"],
42
+ )
43
+
44
+ # --- Data Models ---
45
+
46
+ class VerifyRequest(BaseModel):
47
+ code: str
48
+ auto_fix: bool = True
49
+ use_sandbox: bool = False
50
+
51
+ class StyleCheckRequest(BaseModel):
52
+ code: str
53
+ codebase_path: str = "."
54
+
55
+ class ContextSaveRequest(BaseModel):
56
+ name: str
57
+ files: List[str] = []
58
+ cursor: Optional[dict] = None
59
+ notes: Optional[str] = None
60
+
61
+ class ContextRestoreRequest(BaseModel):
62
+ name: str
63
+
64
+ # --- Endpoints ---
65
+
66
+ @app.get("/api/health")
67
+ async def health_check():
68
+ return {"status": "online", "service": "CodeShield API"}
69
+
70
+ @app.post("/api/verify")
71
+ async def api_verify_code(req: VerifyRequest):
72
+ if not verify_code:
73
+ return {"error": "Backend modules not loaded"}
74
+
75
+ if req.use_sandbox:
76
+ # Full sandbox verification
77
+ result = full_verification(req.code)
78
+ return result
79
+ else:
80
+ # Fast static verification
81
+ result = verify_code(req.code, auto_fix=req.auto_fix)
82
+ return result.to_dict()
83
+
84
+ @app.post("/api/style")
85
+ async def api_check_style(req: StyleCheckRequest):
86
+ if not check_style:
87
+ return {"error": "Backend modules not loaded"}
88
+
89
+ result = check_style(req.code, req.codebase_path)
90
+ return result.to_dict()
91
+
92
+ @app.post("/api/context/save")
93
+ async def api_save_context(req: ContextSaveRequest):
94
+ if not save_context:
95
+ return {"error": "Backend modules not loaded"}
96
+
97
+ result = save_context(
98
+ name=req.name,
99
+ files=req.files,
100
+ cursor=req.cursor,
101
+ notes=req.notes
102
+ )
103
+ return result
104
+
105
+ @app.post("/api/context/restore")
106
+ async def api_restore_context(req: ContextRestoreRequest):
107
+ if not restore_context:
108
+ return {"error": "Backend modules not loaded"}
109
+
110
+ result = restore_context(name=req.name)
111
+ return result
112
+
113
+ @app.get("/api/contexts")
114
+ async def api_list_contexts():
115
+ if not list_contexts:
116
+ return {"error": "Backend modules not loaded"}
117
+
118
+ result = list_contexts()
119
+ return result
120
+
121
+
122
+ # --- Observability Endpoints ---
123
+
124
+ @app.get("/api/providers/status")
125
+ async def api_provider_status():
126
+ """
127
+ Get status of all LLM providers (CometAPI, Novita, AIML).
128
+ Useful for checking which providers are configured and their usage stats.
129
+ """
130
+ try:
131
+ from codeshield.utils.llm import get_llm_client, get_provider_stats
132
+
133
+ llm = get_llm_client()
134
+ status = llm.get_status()
135
+ stats = get_provider_stats()
136
+
137
+ return {
138
+ "providers": status,
139
+ "usage_stats": stats,
140
+ "active_provider": next(
141
+ (name for name, info in status.items() if info["configured"]),
142
+ None
143
+ )
144
+ }
145
+ except Exception as e:
146
+ return {"error": str(e)}
147
+
148
+
149
+ @app.get("/api/providers/test")
150
+ async def api_test_provider(provider: str = None):
151
+ """
152
+ Test LLM provider connectivity.
153
+ Query param: ?provider=cometapi|novita|aiml
154
+ """
155
+ try:
156
+ import time
157
+ from codeshield.utils.llm import get_llm_client
158
+
159
+ llm = get_llm_client()
160
+ if provider:
161
+ llm.preferred_provider = provider
162
+
163
+ start_time = time.time()
164
+ response = llm.chat(
165
+ prompt="Reply with exactly: 'CodeShield connected'",
166
+ max_tokens=20
167
+ )
168
+ elapsed = time.time() - start_time
169
+
170
+ if response:
171
+ return {
172
+ "success": True,
173
+ "provider": response.provider,
174
+ "model": response.model,
175
+ "response": response.content,
176
+ "response_time_ms": round(elapsed * 1000),
177
+ "tokens_used": response.tokens_used
178
+ }
179
+ else:
180
+ return {
181
+ "success": False,
182
+ "error": "No LLM provider available",
183
+ "hint": "Set COMETAPI_KEY, NOVITA_API_KEY, or AIML_API_KEY in .env"
184
+ }
185
+ except Exception as e:
186
+ return {"success": False, "error": str(e)}
187
+
188
+
189
+ @app.get("/api/mcp/status")
190
+ async def api_mcp_status():
191
+ """
192
+ Check if MCP server components are available.
193
+ """
194
+ try:
195
+ from mcp.server.fastmcp import FastMCP
196
+ mcp_available = True
197
+ except ImportError:
198
+ mcp_available = False
199
+
200
+ return {
201
+ "mcp_sdk_installed": mcp_available,
202
+ "mcp_config": "mcp_config.json",
203
+ "tools_available": [
204
+ "verify_code", "full_verify", "check_style",
205
+ "save_context", "restore_context", "list_contexts",
206
+ "mcp_health", "test_llm_connection"
207
+ ] if mcp_available else [],
208
+ "usage": "Add mcp_config.json to your Claude/Cursor MCP settings"
209
+ }
210
+
211
+
212
+ @app.get("/api/leanmcp/status")
213
+ async def api_leanmcp_status():
214
+ """
215
+ Get LeanMCP integration status and metrics.
216
+ """
217
+ try:
218
+ from codeshield.utils.leanmcp import get_leanmcp_client
219
+
220
+ client = get_leanmcp_client()
221
+ return {
222
+ "status": client.get_status(),
223
+ "metrics": client.get_metrics(),
224
+ "configured": client.is_configured()
225
+ }
226
+ except Exception as e:
227
+ return {"error": str(e), "configured": False}
228
+
229
+
230
+ @app.get("/api/leanmcp/health")
231
+ async def api_leanmcp_health():
232
+ """
233
+ Report and retrieve health status via LeanMCP.
234
+ """
235
+ try:
236
+ from codeshield.utils.leanmcp import get_leanmcp_client
237
+
238
+ client = get_leanmcp_client()
239
+ return client.report_health()
240
+ except Exception as e:
241
+ return {"error": str(e), "status": "error"}
242
+
243
+
244
+ @app.get("/api/integrations/status")
245
+ async def api_integrations_status():
246
+ """
247
+ Get status of ALL required integrations.
248
+ """
249
+ import os
250
+
251
+ integrations = {
252
+ "cometapi": {
253
+ "configured": bool(os.getenv("COMETAPI_KEY")),
254
+ "env_var": "COMETAPI_KEY",
255
+ "docs": "https://apidoc.cometapi.com/"
256
+ },
257
+ "novita": {
258
+ "configured": bool(os.getenv("NOVITA_API_KEY")),
259
+ "env_var": "NOVITA_API_KEY",
260
+ "docs": "https://novita.ai/docs/guides/llm-api"
261
+ },
262
+ "aiml": {
263
+ "configured": bool(os.getenv("AIML_API_KEY")),
264
+ "env_var": "AIML_API_KEY",
265
+ },
266
+ "daytona": {
267
+ "configured": bool(os.getenv("DAYTONA_API_KEY")),
268
+ "env_var": "DAYTONA_API_KEY",
269
+ "api_url": os.getenv("DAYTONA_API_URL"),
270
+ "docs": "https://www.daytona.io/docs"
271
+ },
272
+ "leanmcp": {
273
+ "configured": bool(os.getenv("LEANMCP_KEY")),
274
+ "env_var": "LEANMCP_KEY",
275
+ "api_url": os.getenv("LEANMCP_API_URL"),
276
+ "docs": "https://docs.leanmcp.com/"
277
+ }
278
+ }
279
+
280
+ all_configured = all(i["configured"] for i in integrations.values())
281
+
282
+ return {
283
+ "all_configured": all_configured,
284
+ "integrations": integrations,
285
+ "message": "All integrations configured!" if all_configured else "Some integrations missing. Check .env file."
286
+ }
287
+
288
+
289
+ # --- Metrics Endpoints ---
290
+
291
+ @app.get("/api/metrics")
292
+ async def api_get_metrics():
293
+ """
294
+ Get comprehensive CodeShield metrics.
295
+
296
+ Returns transparent, verifiable statistics for:
297
+ - TrustGate: Detection rates, fix accuracy, sandbox success
298
+ - StyleForge: Convention detection, corrections
299
+ - ContextVault: Save/restore stats
300
+ - Tokens: Usage efficiency, costs
301
+ """
302
+ try:
303
+ from codeshield.utils.metrics import get_metrics
304
+
305
+ metrics = get_metrics()
306
+ return metrics.get_summary()
307
+ except Exception as e:
308
+ return {"error": str(e)}
309
+
310
+
311
+ @app.get("/api/metrics/trustgate")
312
+ async def api_trustgate_metrics():
313
+ """Get TrustGate-specific metrics"""
314
+ try:
315
+ from codeshield.utils.metrics import get_metrics
316
+ return get_metrics().trustgate.to_dict()
317
+ except Exception as e:
318
+ return {"error": str(e)}
319
+
320
+
321
+ @app.get("/api/metrics/styleforge")
322
+ async def api_styleforge_metrics():
323
+ """Get StyleForge-specific metrics"""
324
+ try:
325
+ from codeshield.utils.metrics import get_metrics
326
+ return get_metrics().styleforge.to_dict()
327
+ except Exception as e:
328
+ return {"error": str(e)}
329
+
330
+
331
+ @app.get("/api/metrics/tokens")
332
+ async def api_token_metrics():
333
+ """
334
+ Get token utilization metrics.
335
+
336
+ Includes:
337
+ - Total tokens used (input/output)
338
+ - Token efficiency ratio
339
+ - Cost estimates per provider
340
+ - Average tokens per request
341
+ """
342
+ try:
343
+ from codeshield.utils.metrics import get_metrics
344
+ return get_metrics().tokens.to_dict()
345
+ except Exception as e:
346
+ return {"error": str(e)}
347
+
348
+
349
+ @app.post("/api/metrics/reset")
350
+ async def api_reset_metrics():
351
+ """Reset all metrics (for testing)"""
352
+ try:
353
+ from codeshield.utils.metrics import get_metrics
354
+ get_metrics().reset()
355
+ return {"success": True, "message": "Metrics reset"}
356
+ except Exception as e:
357
+ return {"error": str(e)}
358
+
359
+
360
+ @app.get("/api/tokens/efficiency")
361
+ async def api_token_efficiency():
362
+ """
363
+ Get token efficiency and optimization stats.
364
+
365
+ Shows:
366
+ - Cache hit rate (higher = more savings)
367
+ - Tokens saved by caching
368
+ - Budget usage
369
+ - Session statistics
370
+ """
371
+ try:
372
+ from codeshield.utils.token_optimizer import get_token_optimizer
373
+ from codeshield.utils.llm import get_provider_stats
374
+
375
+ optimizer = get_token_optimizer()
376
+ provider_stats = get_provider_stats()
377
+
378
+ return {
379
+ "optimization": optimizer.get_stats(),
380
+ "provider_efficiency": {
381
+ name: {
382
+ "token_efficiency": stats.get("token_efficiency", 0),
383
+ "avg_tokens_per_call": stats.get("avg_tokens_per_call", 0),
384
+ "avg_latency_ms": stats.get("avg_latency_ms", 0),
385
+ }
386
+ for name, stats in provider_stats.items()
387
+ },
388
+ "tips": [
389
+ "Cache hit rate > 20% indicates good prompt reuse",
390
+ "Token efficiency > 1.0 means more output than input (verbose responses)",
391
+ "Aim for avg_tokens_per_call < 500 for cost efficiency"
392
+ ]
393
+ }
394
+ except Exception as e:
395
+ return {"error": str(e)}
396
+
397
+
398
+ @app.post("/api/tokens/budget")
399
+ async def api_set_token_budget(budget: int = 100000):
400
+ """Set token budget for the session"""
401
+ try:
402
+ from codeshield.utils.token_optimizer import get_token_optimizer
403
+ optimizer = get_token_optimizer()
404
+ optimizer.set_budget(budget)
405
+ return {"success": True, "budget": budget}
406
+ except Exception as e:
407
+ return {"error": str(e)}
408
+
409
+
410
+ # --- Frontend Static Serving ---
411
+
412
+ # Mount assets (CSS/JS)
413
+ if os.path.exists("frontend/dist/assets"):
414
+ app.mount("/assets", StaticFiles(directory="frontend/dist/assets"), name="assets")
415
+
416
+ # Serve other static files (favicon, etc.) and SPA fallback
417
+ @app.get("/{full_path:path}")
418
+ async def serve_static(full_path: str):
419
+ # API requests are handled by routes above.
420
+ # If we get here, it's a static file or client-side route.
421
+
422
+ # Security: Prevent traversing up? FileResponse handles it.
423
+
424
+ dist_dir = "frontend/dist"
425
+ if not os.path.exists(dist_dir):
426
+ return {"status": "Frontend not built", "deployment": "backend-only"}
427
+
428
+ # Try to find specific file
429
+ file_path = os.path.join(dist_dir, full_path)
430
+ if os.path.exists(file_path) and os.path.isfile(file_path):
431
+ return FileResponse(file_path)
432
+
433
+ # Fallback to index.html for SPA routing
434
+ return FileResponse(os.path.join(dist_dir, "index.html"))
435
+
436
+
437
+ if __name__ == "__main__":
438
+ uvicorn.run("codeshield.api_server:app", host="0.0.0.0", port=8000, reload=True)
codeshield/cli.py ADDED
@@ -0,0 +1,48 @@
1
+ """
2
+ CLI entry point for CodeShield
3
+ """
4
+
5
+ import argparse
6
+ import sys
7
+
8
+
9
+ def main():
10
+ parser = argparse.ArgumentParser(
11
+ description="CodeShield - The Complete AI Coding Safety Net"
12
+ )
13
+ subparsers = parser.add_subparsers(dest="command", help="Commands")
14
+
15
+ # serve command
16
+ serve_parser = subparsers.add_parser("serve", help="Start MCP server")
17
+ serve_parser.add_argument("--port", type=int, default=8080, help="Server port")
18
+ serve_parser.add_argument("--host", default="localhost", help="Server host")
19
+
20
+ # verify command
21
+ verify_parser = subparsers.add_parser("verify", help="Verify code")
22
+ verify_parser.add_argument("file", help="File to verify")
23
+
24
+ # style command
25
+ style_parser = subparsers.add_parser("style", help="Check code style against codebase")
26
+ style_parser.add_argument("file", help="File to check")
27
+ style_parser.add_argument("--codebase", default=".", help="Codebase path")
28
+
29
+ args = parser.parse_args()
30
+
31
+ if args.command == "serve":
32
+ from codeshield.mcp.server import run_server
33
+ run_server(host=args.host, port=args.port)
34
+ elif args.command == "verify":
35
+ from codeshield.trustgate.checker import verify_file
36
+ result = verify_file(args.file)
37
+ print(result)
38
+ elif args.command == "style":
39
+ from codeshield.styleforge.corrector import check_style
40
+ result = check_style(args.file, args.codebase)
41
+ print(result)
42
+ else:
43
+ parser.print_help()
44
+ sys.exit(1)
45
+
46
+
47
+ if __name__ == "__main__":
48
+ main()
@@ -0,0 +1 @@
1
+ """ContextVault - Context memory module"""