Files
claudetools/imported-conversations/general-work/claude-general/651b9822-25fa-426b-9723-899e780a997f/tool-results/toolu_015h2R2CQ9g1uTo4ieuMDHf7.txt
Mike Swanson 75ce1c2fd5 feat: Add Sequential Thinking to Code Review + Frontend Validation
Enhanced code review and frontend validation with intelligent triggers:

Code Review Agent Enhancement:
- Added Sequential Thinking MCP integration for complex issues
- Triggers on 2+ rejections or 3+ critical issues
- New escalation format with root cause analysis
- Comprehensive solution strategies with trade-off evaluation
- Educational feedback to break rejection cycles
- Files: .claude/agents/code-review.md (+308 lines)
- Docs: CODE_REVIEW_ST_ENHANCEMENT.md, CODE_REVIEW_ST_TESTING.md

Frontend Design Skill Enhancement:
- Automatic invocation for ANY UI change
- Comprehensive validation checklist (200+ checkpoints)
- 8 validation categories (visual, interactive, responsive, a11y, etc.)
- 3 validation levels (quick, standard, comprehensive)
- Integration with code review workflow
- Files: .claude/skills/frontend-design/SKILL.md (+120 lines)
- Docs: UI_VALIDATION_CHECKLIST.md (462 lines), AUTOMATIC_VALIDATION_ENHANCEMENT.md (587 lines)

Settings Optimization:
- Repaired .claude/settings.local.json (fixed m365 pattern)
- Reduced permissions from 49 to 33 (33% reduction)
- Removed duplicates, sorted alphabetically
- Created SETTINGS_PERMISSIONS.md documentation

Checkpoint Command Enhancement:
- Dual checkpoint system (git + database)
- Saves session context to API for cross-machine recall
- Includes git metadata in database context
- Files: .claude/commands/checkpoint.md (+139 lines)

Decision Rationale:
- Sequential Thinking MCP breaks rejection cycles by identifying root causes
- Automatic frontend validation catches UI issues before code review
- Dual checkpoints enable complete project memory across machines
- Settings optimization improves maintainability

Total: 1,200+ lines of documentation and enhancements

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 16:23:52 -07:00

217 lines
8.0 KiB
Plaintext

1→//! REST API endpoints
2→
3→pub mod auth;
4→pub mod users;
5→
6→use axum::{
7→ extract::{Path, State, Query},
8→ Json,
9→};
10→use serde::{Deserialize, Serialize};
11→use uuid::Uuid;
12→
13→use crate::session::SessionManager;
14→use crate::db;
15→
16→/// Viewer info returned by API
17→#[derive(Debug, Serialize)]
18→pub struct ViewerInfoApi {
19→ pub id: String,
20→ pub name: String,
21→ pub connected_at: String,
22→}
23→
24→impl From<crate::session::ViewerInfo> for ViewerInfoApi {
25→ fn from(v: crate::session::ViewerInfo) -> Self {
26→ Self {
27→ id: v.id,
28→ name: v.name,
29→ connected_at: v.connected_at.to_rfc3339(),
30→ }
31→ }
32→}
33→
34→/// Session info returned by API
35→#[derive(Debug, Serialize)]
36→pub struct SessionInfo {
37→ pub id: String,
38→ pub agent_id: String,
39→ pub agent_name: String,
40→ pub started_at: String,
41→ pub viewer_count: usize,
42→ pub viewers: Vec<ViewerInfoApi>,
43→ pub is_streaming: bool,
44→ pub is_online: bool,
45→ pub is_persistent: bool,
46→ pub last_heartbeat: String,
47→ pub os_version: Option<String>,
48→ pub is_elevated: bool,
49→ pub uptime_secs: i64,
50→ pub display_count: i32,
51→}
52→
53→impl From<crate::session::Session> for SessionInfo {
54→ fn from(s: crate::session::Session) -> Self {
55→ Self {
56→ id: s.id.to_string(),
57→ agent_id: s.agent_id,
58→ agent_name: s.agent_name,
59→ started_at: s.started_at.to_rfc3339(),
60→ viewer_count: s.viewer_count,
61→ viewers: s.viewers.into_iter().map(ViewerInfoApi::from).collect(),
62→ is_streaming: s.is_streaming,
63→ is_online: s.is_online,
64→ is_persistent: s.is_persistent,
65→ last_heartbeat: s.last_heartbeat.to_rfc3339(),
66→ os_version: s.os_version,
67→ is_elevated: s.is_elevated,
68→ uptime_secs: s.uptime_secs,
69→ display_count: s.display_count,
70→ }
71→ }
72→}
73→
74→/// List all active sessions
75→pub async fn list_sessions(
76→ State(sessions): State<SessionManager>,
77→) -> Json<Vec<SessionInfo>> {
78→ let sessions = sessions.list_sessions().await;
79→ Json(sessions.into_iter().map(SessionInfo::from).collect())
80→}
81→
82→/// Get a specific session by ID
83→pub async fn get_session(
84→ State(sessions): State<SessionManager>,
85→ Path(id): Path<String>,
86→) -> Result<Json<SessionInfo>, (axum::http::StatusCode, &'static str)> {
87→ let session_id = Uuid::parse_str(&id)
88→ .map_err(|_| (axum::http::StatusCode::BAD_REQUEST, "Invalid session ID"))?;
89→
90→ let session = sessions.get_session(session_id).await
91→ .ok_or((axum::http::StatusCode::NOT_FOUND, "Session not found"))?;
92→
93→ Ok(Json(SessionInfo::from(session)))
94→}
95→
96→// ============================================================================
97→// Machine API Types
98→// ============================================================================
99→
100→/// Machine info returned by API
101→#[derive(Debug, Serialize)]
102→pub struct MachineInfo {
103→ pub id: String,
104→ pub agent_id: String,
105→ pub hostname: String,
106→ pub os_version: Option<String>,
107→ pub is_elevated: bool,
108→ pub is_persistent: bool,
109→ pub first_seen: String,
110→ pub last_seen: String,
111→ pub status: String,
112→}
113→
114→impl From<db::machines::Machine> for MachineInfo {
115→ fn from(m: db::machines::Machine) -> Self {
116→ Self {
117→ id: m.id.to_string(),
118→ agent_id: m.agent_id,
119→ hostname: m.hostname,
120→ os_version: m.os_version,
121→ is_elevated: m.is_elevated,
122→ is_persistent: m.is_persistent,
123→ first_seen: m.first_seen.to_rfc3339(),
124→ last_seen: m.last_seen.to_rfc3339(),
125→ status: m.status,
126→ }
127→ }
128→}
129→
130→/// Session record for history
131→#[derive(Debug, Serialize)]
132→pub struct SessionRecord {
133→ pub id: String,
134→ pub started_at: String,
135→ pub ended_at: Option<String>,
136→ pub duration_secs: Option<i32>,
137→ pub is_support_session: bool,
138→ pub support_code: Option<String>,
139→ pub status: String,
140→}
141→
142→impl From<db::sessions::DbSession> for SessionRecord {
143→ fn from(s: db::sessions::DbSession) -> Self {
144→ Self {
145→ id: s.id.to_string(),
146→ started_at: s.started_at.to_rfc3339(),
147→ ended_at: s.ended_at.map(|t| t.to_rfc3339()),
148→ duration_secs: s.duration_secs,
149→ is_support_session: s.is_support_session,
150→ support_code: s.support_code,
151→ status: s.status,
152→ }
153→ }
154→}
155→
156→/// Event record for history
157→#[derive(Debug, Serialize)]
158→pub struct EventRecord {
159→ pub id: i64,
160→ pub session_id: String,
161→ pub event_type: String,
162→ pub timestamp: String,
163→ pub viewer_id: Option<String>,
164→ pub viewer_name: Option<String>,
165→ pub details: Option<serde_json::Value>,
166→ pub ip_address: Option<String>,
167→}
168→
169→impl From<db::events::SessionEvent> for EventRecord {
170→ fn from(e: db::events::SessionEvent) -> Self {
171→ Self {
172→ id: e.id,
173→ session_id: e.session_id.to_string(),
174→ event_type: e.event_type,
175→ timestamp: e.timestamp.to_rfc3339(),
176→ viewer_id: e.viewer_id,
177→ viewer_name: e.viewer_name,
178→ details: e.details,
179→ ip_address: e.ip_address,
180→ }
181→ }
182→}
183→
184→/// Full machine history (for export)
185→#[derive(Debug, Serialize)]
186→pub struct MachineHistory {
187→ pub machine: MachineInfo,
188→ pub sessions: Vec<SessionRecord>,
189→ pub events: Vec<EventRecord>,
190→ pub exported_at: String,
191→}
192→
193→/// Query parameters for machine deletion
194→#[derive(Debug, Deserialize)]
195→pub struct DeleteMachineParams {
196→ /// If true, send uninstall command to agent (if online)
197→ #[serde(default)]
198→ pub uninstall: bool,
199→ /// If true, include history in response before deletion
200→ #[serde(default)]
201→ pub export: bool,
202→}
203→
204→/// Response for machine deletion
205→#[derive(Debug, Serialize)]
206→pub struct DeleteMachineResponse {
207→ pub success: bool,
208→ pub message: String,
209→ pub uninstall_sent: bool,
210→ pub history: Option<MachineHistory>,
211→}
212→
<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>