-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add audit logging and web dashboard #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,194 @@ | ||
| # Claude Memory - New Features | ||
|
|
||
| ## Recently Added (Latest Update) | ||
|
|
||
| ### 1. Audit Logging System | ||
|
|
||
| Track all team actions with detailed audit logs. | ||
|
|
||
| **Features:** | ||
| - Automatic logging of all API actions | ||
| - User and action filtering | ||
| - Team statistics and activity summaries | ||
| - Persistent audit trail (stored in `.memory/audit/audit.jsonl`) | ||
|
|
||
| **Available Actions:** | ||
| - `remember` - Store memory | ||
| - `recall` - Search memory | ||
| - `forget` - Delete memory | ||
| - `add_rule` - Create rules | ||
| - `handoff_share` - Share work | ||
| - `handoff_pickup` - Pick up work | ||
| - `session_save` - Save session | ||
| - `user_created` - User management | ||
| - `api_key_generated` - Security events | ||
|
|
||
| **API Endpoints:** | ||
| ``` | ||
| GET /audit/logs - Get audit logs with filtering | ||
| GET /audit/stats - Get team statistics | ||
| GET /audit/user/<username> - Get user's activity | ||
| ``` | ||
|
|
||
| ### 2. Web Dashboard | ||
|
|
||
| Modern React-based dashboard for team collaboration. | ||
|
|
||
| **Pages:** | ||
| - **Dashboard** - Overview with stats and recent activity | ||
| - **Audit Logs** - Full audit trail with search and filtering | ||
| - **Team Members** - View all team members and roles | ||
| - **Memories** - Search and manage team decisions | ||
| - **Rules** - View team coding standards | ||
|
|
||
| **Features:** | ||
| - Real-time statistics | ||
| - Search and filtering | ||
| - Role-based color coding | ||
| - Responsive design | ||
| - Dark theme (modern UI/UX) | ||
|
|
||
| **Tech Stack:** | ||
| - React 18 + Vite | ||
| - Tailwind CSS for styling | ||
| - Lucide React icons | ||
| - Axios for HTTP | ||
|
|
||
| **Setup:** | ||
| ```bash | ||
| cd web | ||
| npm install | ||
| npm run dev | ||
| ``` | ||
|
|
||
| Then open http://localhost:3000 | ||
|
|
||
| ## All Current Features | ||
|
|
||
| ### Core Features | ||
| - Session sharing and handoffs between team members | ||
| - Team rules and personal coding standards | ||
| - Vector search for memories (ChromaDB) | ||
| - Team member profiles and roles | ||
| - Organization-wide policies | ||
| - Session summaries and history | ||
| - Role-based suggestions | ||
| - Rules synchronization | ||
|
|
||
| ### Admin Features | ||
| - API key management (generate, revoke, regenerate) | ||
| - User management (create, delete) | ||
| - Team administration | ||
| - Organization setup | ||
|
|
||
| ### Team Collaboration | ||
| - Handoff sharing with context | ||
| - Pickup and complete workflows | ||
| - Cross-team knowledge sharing | ||
| - Member expertise tracking | ||
|
|
||
| ### Memory System | ||
| - Store decisions with categories | ||
| - Semantic search with vector embeddings | ||
| - Importance levels (critical, high, normal, low) | ||
| - Tag-based organization | ||
| - Session summaries | ||
|
|
||
| ### Audit & Compliance | ||
| - Complete audit trail of all actions | ||
| - User activity tracking | ||
| - Team statistics | ||
| - Action filtering and search | ||
|
|
||
| ### Dashboard & UI | ||
| - Web-based management interface | ||
| - Real-time activity feeds | ||
| - Team insights and statistics | ||
| - Search and filtering capabilities | ||
| - Responsive design | ||
|
|
||
| ## Upcoming Features (Roadmap) | ||
|
|
||
| **Tier 1 - High Priority:** | ||
| - [ ] Notifications and webhooks (Slack integration) | ||
| - [ ] Export/Backup functionality (Markdown, PDF, JSON) | ||
| - [ ] Decision versioning and history tracking | ||
| - [ ] Conflict detection for contradicting rules | ||
|
|
||
| **Tier 2 - Medium Priority:** | ||
| - [ ] Code integration (parse comments, commits) | ||
| - [ ] Team insights reports (weekly/monthly) | ||
| - [ ] Fine-tuned models on team knowledge | ||
| - [ ] Decision templates and workflows | ||
|
|
||
| **Tier 3 - Nice to Have:** | ||
| - [ ] Multi-server sync for HA | ||
| - [ ] Access control (private vs public memories) | ||
| - [ ] Team voting on decisions | ||
| - [ ] Mobile app | ||
| - [ ] Git repository integration | ||
|
|
||
| ## Architecture | ||
|
|
||
| ``` | ||
| Claude Memory | ||
| ├── memory_server/ | ||
| │ ├── server.py (MCP server) | ||
| │ ├── server_http.py (HTTP server with audit) | ||
| │ ├── server_mcp_client.py (MCP client) | ||
| │ └── audit/ (new) | ||
| │ └── audit_logger.py (audit system) | ||
| ├── scripts/ | ||
| │ ├── memory_utils.py (core storage) | ||
| │ ├── manage_team.py (team management) | ||
| │ └── summarize_session.py | ||
| ├── web/ (new) | ||
| │ ├── src/ | ||
| │ │ ├── pages/ (Dashboard, Audit, Team, Rules, Memories) | ||
| │ │ ├── components/ (Navbar, Sidebar, Card) | ||
| │ │ ├── api/ (HTTP client) | ||
| │ │ └── App.jsx | ||
| │ ├── package.json | ||
| │ └── vite.config.js | ||
| ├── client/ | ||
| │ ├── server_mcp_client.py | ||
| │ ├── setup_mcp.bat | ||
| │ └── requirements.txt | ||
| └── .memory/ | ||
| ├── audit/ (new - audit logs) | ||
| ├── chroma_db/ (vector database) | ||
| ├── team.json (team data) | ||
| └── sessions/ (session history) | ||
| ``` | ||
|
|
||
| ## Quick Start | ||
|
|
||
| ### Server Setup | ||
| ```bash | ||
| pip install -r memory_server/requirements.txt | ||
| python memory_server/server_http.py --host 0.0.0.0 --port 8765 | ||
| curl -X POST http://localhost:8765/admin/setup | ||
| python scripts/manage_team.py create "Alice" backend | ||
| ``` | ||
|
|
||
| ### Web Dashboard | ||
| ```bash | ||
| cd web | ||
| npm install | ||
| npm run dev | ||
| # Visit http://localhost:3000 | ||
| ``` | ||
|
|
||
| ### Client Setup | ||
| ```bash | ||
| cd client | ||
| pip install -r requirements.txt | ||
| setup_mcp.bat | ||
| ``` | ||
|
|
||
| ## Documentation | ||
|
|
||
| - [README.md](README.md) - Main project documentation | ||
| - [client/README.md](client/README.md) - Client setup guide | ||
| - [web/README.md](web/README.md) - Dashboard documentation | ||
| - [CONTRIBUTING.md](CONTRIBUTING.md) - Contribution guidelines |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,150 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Audit logging system for Claude Memory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Tracks all user actions for compliance and debugging. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import json | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from datetime import datetime | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from pathlib import Path | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import Optional, Any | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from enum import Enum | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class AuditAction(Enum): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Types of actions to audit.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| REMEMBER = "remember" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RECALL = "recall" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FORGET = "forget" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ADD_RULE = "add_rule" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DELETE_RULE = "delete_rule" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| HANDOFF_SHARE = "handoff_share" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| HANDOFF_PICKUP = "handoff_pickup" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SESSION_SAVE = "session_save" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SESSION_LOAD = "session_load" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| USER_LOGIN = "user_login" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| USER_CREATED = "user_created" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| USER_DELETED = "user_deleted" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| API_KEY_GENERATED = "api_key_generated" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| API_KEY_REVOKED = "api_key_revoked" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class AuditLogger: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Log all actions for audit trail.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def __init__(self, log_dir: str = ".memory/audit"): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.log_dir = Path(log_dir) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.log_dir.mkdir(parents=True, exist_ok=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.log_file = self.log_dir / "audit.jsonl" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def log( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| action: AuditAction, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user: str, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| details: Optional[dict] = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: str = "success", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error: Optional[str] = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> dict: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Log an action. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| action: Type of action (from AuditAction enum) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user: Username who performed action | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| details: Additional context about the action | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: "success" or "failure" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error: Error message if failed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| entry = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "timestamp": datetime.utcnow().isoformat(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "action": action.value, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "user": user, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "status": status, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "details": details or {}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "error": error, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+56
to
+63
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Append to log file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with open(self.log_file, "a") as f: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f.write(json.dumps(entry) + "\n") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+66
to
+69
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return entry | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def get_audit_trail( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user: Optional[str] = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| action: Optional[str] = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| limit: int = 100, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> list[dict]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Retrieve audit logs filtered by user and/or action. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user: Filter by username (optional) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| action: Filter by action type (optional) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| limit: Max results to return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not self.log_file.exists(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logs = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with open(self.log_file, "r") as f: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for line in f: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not line.strip(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| entry = json.loads(line) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if user and entry["user"] != user: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if action and entry["action"] != action: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logs.append(entry) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+89
to
+101
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Return most recent first | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return sorted( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logs, key=lambda x: x["timestamp"], reverse=True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )[:limit] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def get_user_activity(self, user: str, limit: int = 50) -> dict: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Get summary of user's recent activity.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logs = self.get_audit_trail(user=user, limit=limit) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| action_counts = {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for log in logs: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| action = log["action"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| action_counts[action] = action_counts.get(action, 0) + 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "user": user, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "total_actions": len(logs), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "action_summary": action_counts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "recent_actions": logs[:10], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def get_team_stats(self) -> dict: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Get overall team statistics.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not self.log_file.exists(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "total_actions": 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "active_users": [], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "action_breakdown": {}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logs = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with open(self.log_file, "r") as f: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logs = [json.loads(line) for line in f if line.strip()] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logs = [json.loads(line) for line in f if line.strip()] | |
| for line in f: | |
| if not line.strip(): | |
| continue | |
| try: | |
| logs.append(json.loads(line)) | |
| except json.JSONDecodeError: | |
| continue |
Copilot
AI
Apr 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get_team_stats loads the entire audit log file into memory, which won’t scale as the audit trail grows. Consider streaming the file and aggregating counts incrementally (and/or limiting to a time window) to keep memory usage predictable.
| logs = [] | |
| with open(self.log_file, "r") as f: | |
| logs = [json.loads(line) for line in f if line.strip()] | |
| users = set() | |
| actions = {} | |
| for log in logs: | |
| users.add(log["user"]) | |
| action = log["action"] | |
| actions[action] = actions.get(action, 0) + 1 | |
| return { | |
| "total_actions": len(logs), | |
| "active_users": sorted(list(users)), | |
| "action_breakdown": actions, | |
| "last_action": logs[-1]["timestamp"] if logs else None, | |
| total_actions = 0 | |
| users = set() | |
| actions = {} | |
| last_action = None | |
| with open(self.log_file, "r") as f: | |
| for line in f: | |
| if not line.strip(): | |
| continue | |
| log = json.loads(line) | |
| total_actions += 1 | |
| users.add(log["user"]) | |
| action = log["action"] | |
| actions[action] = actions.get(action, 0) + 1 | |
| last_action = log["timestamp"] | |
| return { | |
| "total_actions": total_actions, | |
| "active_users": sorted(list(users)), | |
| "action_breakdown": actions, | |
| "last_action": last_action, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This command hardcodes a machine-specific absolute path and appears to have a typo (
Claude_memeory). This makes the settings non-portable and likely broken for other developers/CI. Consider removing the absolute path, fixing the spelling, or avoiding committing local-only commands into the repo.