feat(dashboard): extract inline script to standalone dashboard.ts/js, add UI build/check recipes, and add route unit tests

This commit is contained in:
Riz Ashraf committed 2026-10-05 12:48:36 +01:00
1 parent 4b307b55b9
commit 19f9c7ace7
24 files changed
+2343 -1819

No files matched your search

+47 -125
View File
@@ -495,94 +495,46 @@ impl MemoryHandler {
register!(tasks::TasksHandler);
register!(tasks::MilestonesHandler);
register!(tasks::AddTaskHandler);
register!(tasks::DeleteTaskHandler);
register!(tasks::UpdateTaskStatusHandler);
register!(tasks::ListActiveTasksHandler);
register!(tasks::SetAcceptanceCriteriaHandler);
register!(tasks::VerifyAcceptanceCriteriaHandler);
register!(tasks::AddMilestoneHandler);
register!(tasks::UpdateMilestoneHandler);
register!(tasks::ListMilestonesHandler);
register!(notes::StickyNotesHandler);
register!(notes::HandoffMemosHandler);
register!(notes::AddStickyNoteHandler);
register!(notes::ReadStickyNotesHandler);
register!(notes::DeleteStickyNoteHandler);
register!(notes::ClearStickyNotesHandler);
register!(notes::AddSessionSummaryHandler);
register!(notes::GenerateStandupReportHandler);
register!(notes::PromoteToEntityHandler);
register!(meta::DecisionsHandler);
register!(meta::TechDebtHandler);
register!(meta::LogDecisionHandler);
register!(meta::QueryDecisionsHandler);
register!(meta::DeleteDecisionHandler);
register!(meta::LogErrorFixHandler);
register!(meta::SearchErrorFixesHandler);
register!(meta::LogCodeChangeHandler);
register!(meta::QueryRecentChangesHandler);
register!(meta::LearnPreferenceHandler);
register!(meta::ReadPreferencesHandler);
register!(meta::LogTechDebtHandler);
register!(meta::ResolveTechDebtHandler);
register!(meta::ListTechDebtHandler);
register!(meta::OmniSearchHandler);
register!(meta::GetProjectHealthHandler);
register!(env::EnvironmentHandler);
register!(env::UpdateEnvFingerprintHandler);
register!(env::ReadEnvFingerprintHandler);
register!(env::LogEnvRequirementHandler);
register!(env::RegisterEnvironmentHandler);
register!(env::GetEnvironmentDetailsHandler);
register!(workspaces::PinnedFilesHandler);
register!(workspaces::ContextWorkspacesHandler);
register!(workspaces::PrChecklistHandler);
register!(workspaces::SnippetsHandler);
register!(workspaces::PinFileHandler);
register!(workspaces::UnpinFileHandler);
register!(workspaces::ListPinnedFilesHandler);
register!(workspaces::StoreSnippetHandler);
register!(workspaces::SearchSnippetsHandler);
register!(workspaces::DeleteSnippetHandler);
register!(workspaces::SaveContextWorkspaceHandler);
register!(workspaces::LoadContextWorkspaceHandler);
register!(workspaces::ListContextWorkspacesHandler);
register!(workspaces::DeleteContextWorkspaceHandler);
register!(workspaces::AddPrChecklistItemHandler);
register!(workspaces::GetPrChecklistHandler);
register!(workspaces::ClearPrChecklistHandler);
register!(vision::ClipboardHandler);
register!(vision::ReadClipboardHandler);
register!(vision::WriteClipboardHandler);
register!(git::GetActiveWorktreeContextHandler);
register!(git::QueryGitDiffsHandler);
register!(logs::WatchProcessLogsHandler);
register!(logs::GetRecentLogsHandler);
register!(ast::ReadFileSkeletonHandler);
register!(vision::ToggleClipboardWatchModeHandler);
register!(ast::ReplaceAstNodeHandler);
register!(ast::FindSymbolReferencesHandler);
register!(ast::GetCallersHandler);
register!(ast::AnalyzeImpactHandler);
register!(workspaces::ReadDirectoryArchitectureHandler);
register!(workspaces::SemanticCodeSearchHandler);
register!(workspaces::CreateSnapshotHandler);
register!(workspaces::RestoreSnapshotHandler);
register!(workspaces::CreateSubagentNamespaceHandler);
register!(workspaces::ManageSubagentNamespaceHandler);
register!(graph::GetSubgraphHandler);
register!(meta::SuggestErrorFixHandler);
register!(meta::CheckpointStateHandler);
register!(meta::ManageCheckpointHandler);
register!(meta::RestoreStateHandler);
register!(workspaces::TagSnippetHandler);
register!(workspaces::PurgeSubagentNamespaceHandler);
register!(workspaces::CondenseSubagentNamespaceHandler);
register!(graph::SweepGraphHealthHandler);
register!(meta::QueryLineageHandler);
register!(meta::GetNextActionableTasksHandler);
@@ -595,7 +547,6 @@ impl MemoryHandler {
register!(meta::BroadcastAgentSignalHandler);
register!(meta::QueryAgentSignalsHandler);
register!(meta::AutoSessionCheckpointHandler);
register!(meta::SearchSnippetsHybridHandler);
Self {
state,
@@ -763,19 +714,21 @@ impl MemoryHandler {
.unwrap_or(serde_json::Value::Object(Default::default()));
let category = match name {
"read_clipboard" | "write_clipboard" | "toggle_clipboard_watch_mode" => "CLIPBOARD",
"clipboard" => "CLIPBOARD",
"create_entities" | "create_relations" | "read_graph" | "get_subgraph" | "search_graph" | "get_schema" => "GRAPH",
"log_decision" => "DECISION",
"decisions" => "DECISION",
"log_code_change" => "CODE",
"log_error_fix" => "ERROR_FIX",
"log_tech_debt" => "TECH_DEBT",
"add_task" | "update_task_status" | "delete_task" | "add_milestone" => "TASK",
"manage_sticky_notes" | "read_notes" => "STICKY_NOTE",
"tech_debt" => "TECH_DEBT",
"tasks" | "milestones" => "TASK",
"sticky_notes" | "handoff_memos" => "STICKY_NOTE",
"manage_checkpoint" => "CHECKPOINT",
"manage_subagent_namespace" => "SUBAGENT",
"search_snippets" => "SNIPPET",
"snippets" => "SNIPPET",
"search_web" => "WEB_SEARCH",
"omni_search" => "OMNI_SEARCH",
"environment" => "ENVIRONMENT",
"pinned_files" | "context_workspaces" | "pr_checklist" => "WORKSPACE",
_ => "TOOL",
};
@@ -829,10 +782,38 @@ impl MemoryHandler {
pub fn format_tool_activity_description(name: &str, args: &serde_json::Value) -> String {
let (action, detail) = match name {
"tasks" => {
let act = args.get("action").and_then(|v| v.as_str()).unwrap_or("manage");
let title = args.get("title").or_else(|| args.get("id")).and_then(|v| v.as_str()).unwrap_or("");
("Tasks", format!("{}: {}", act, title).trim_end_matches(": ").to_string())
}
"decisions" => {
let act = args.get("action").and_then(|v| v.as_str()).unwrap_or("log");
let title = args.get("title").or_else(|| args.get("query")).and_then(|v| v.as_str()).unwrap_or("");
("Decisions", format!("{}: {}", act, title).trim_end_matches(": ").to_string())
}
"tech_debt" => {
let act = args.get("action").and_then(|v| v.as_str()).unwrap_or("log");
let desc = args.get("description").or_else(|| args.get("id")).and_then(|v| v.as_str()).unwrap_or("");
("Tech Debt", format!("{}: {}", act, desc).trim_end_matches(": ").to_string())
}
"sticky_notes" => {
let act = args.get("action").and_then(|v| v.as_str()).unwrap_or("add");
let preview = args.get("content").and_then(|v| v.as_str()).map(|c| c.chars().take(40).collect::<String>()).unwrap_or_default();
("Sticky Notes", format!("{}: {}", act, preview).trim_end_matches(": ").to_string())
}
"clipboard" => {
let act = args.get("action").and_then(|v| v.as_str()).unwrap_or("read");
("Clipboard", act.to_string())
}
"snippets" => {
let act = args.get("action").and_then(|v| v.as_str()).unwrap_or("search");
let q = args.get("query").and_then(|v| v.as_str()).unwrap_or("");
("Snippets", format!("{}: {}", act, q).trim_end_matches(": ").to_string())
}
"log_code_change" => {
let file = args.get("file_path")
.or_else(|| args.get("file"))
.or_else(|| args.get("path"))
.or_else(|| args.get("target_file"))
.and_then(|v| v.as_str());
let summary = args.get("summary")
@@ -847,15 +828,6 @@ pub fn format_tool_activity_description(name: &str, args: &serde_json::Value) ->
};
("Log Code Change", d)
}
"log_decision" => {
let d = args.get("title")
.or_else(|| args.get("decision"))
.or_else(|| args.get("summary"))
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string();
("Log Decision", d)
}
"log_error_fix" => {
let d = args.get("error")
.or_else(|| args.get("summary"))
@@ -865,16 +837,6 @@ pub fn format_tool_activity_description(name: &str, args: &serde_json::Value) ->
.to_string();
("Log Error Fix", d)
}
"log_tech_debt" => {
let d = if let Some(summary) = args.get("summary").or_else(|| args.get("description")).and_then(|v| v.as_str()) {
summary.to_string()
} else if let Some(file) = args.get("file_path").or_else(|| args.get("file")).and_then(|v| v.as_str()) {
file.to_string()
} else {
String::new()
};
("Log Tech Debt", d)
}
"create_entities" => {
let d = if let Some(entities) = args.get("entities").and_then(|v| v.as_array()) {
let names: Vec<&str> = entities
@@ -917,54 +879,17 @@ pub fn format_tool_activity_description(name: &str, args: &serde_json::Value) ->
};
("Create Relations", d)
}
"add_task" => {
let d = args.get("title")
.or_else(|| args.get("name"))
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string();
("Add Task", d)
}
"update_task_status" => {
let d = if let (Some(id), Some(status)) = (
args.get("task_id").or_else(|| args.get("id")).and_then(|v| v.as_str()),
args.get("status").and_then(|v| v.as_str()),
) {
format!("Task {} -> {}", id, status)
} else {
String::new()
};
("Update Task Status", d)
}
"omni_search" | "search_graph" | "search_snippets" | "search_web" => {
"omni_search" | "search_graph" | "search_web" => {
let d = args.get("query")
.and_then(|v| v.as_str())
.map(|q| format!("\"{}\"", q))
.unwrap_or_default();
("Search", d)
}
"manage_sticky_notes" | "add_sticky_note" => {
let action = args.get("action").and_then(|v| v.as_str()).unwrap_or("add");
let d = if let Some(content) = args.get("content").and_then(|v| v.as_str()) {
let preview: String = content.chars().take(40).collect();
format!("{} \"{}\"", action, preview)
} else {
action.to_string()
};
("Sticky Note", d)
}
"write_clipboard" => {
let d = if let Some(text) = args.get("text").or_else(|| args.get("content")).and_then(|v| v.as_str()) {
let preview: String = text.chars().take(40).collect();
format!("\"{}\"", preview)
} else {
String::new()
};
("Write Clipboard", d)
}
_ => {
let d = if let Some(title) = args
let d = args
.get("title")
.or_else(|| args.get("action"))
.or_else(|| args.get("summary"))
.or_else(|| args.get("description"))
.or_else(|| args.get("name"))
@@ -973,12 +898,8 @@ pub fn format_tool_activity_description(name: &str, args: &serde_json::Value) ->
.or_else(|| args.get("file"))
.or_else(|| args.get("path"))
.and_then(|v| v.as_str())
{
title.to_string()
} else {
String::new()
};
(name, d)
.unwrap_or("");
(name, d.to_string())
}
};
@@ -1021,7 +942,7 @@ mod tests {
// Assert some known tools are registered
assert!(handler.tools.contains_key("create_entities"));
assert!(handler.tools.contains_key("add_task"));
assert!(handler.tools.contains_key("tasks"));
// Ensure we can fetch list of tools
let list_tools_req = json!({
@@ -1145,10 +1066,11 @@ mod tests {
"id": 3,
"method": "tools/call",
"params": {
"name": "update_task_status",
"name": "tasks",
"arguments": {
"action": "update",
"id": "nonexistent_task_123",
"status": "in_progress"
"status": "completed"
}
}
});