feat: version string formatting yy.mm.dd.hhmmss, app icon, and test fixes

This commit is contained in:
Riz Ashraf committed 2026-09-18 02:05:08 +01:00
1 parent 1752753fcc
commit 81aac7245b
17 files changed
+170 -412

No files matched your search

+1
View File
@@ -31,6 +31,7 @@ rmcp = { version = "3.4.0", features = ["server"] }
[build-dependencies]
chrono = "0.4.45"
winres = "0.1.12"
[dev-dependencies]
tempfile = "3.27.0"
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+8 -3
View File
@@ -9,17 +9,22 @@ fn main() {
.unwrap_or_else(|| "unknown".to_string());
let git_date = Command::new("git")
.args(["log", "-1", "--format=%cd", "--date=format:%Y.%m.%d"])
.args(["log", "-1", "--format=%cd", "--date=format:%y.%m.%d.%H%M%S"])
.output()
.ok()
.and_then(|out| String::from_utf8(out.stdout).ok())
.unwrap_or_else(|| "unknown".to_string());
let version = format!(
"{} ({} {})",
env!("CARGO_PKG_VERSION"),
"{} ({})",
git_date.trim(),
git_hash.trim()
);
println!("cargo:rustc-env=APP_VERSION={}", version);
if std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default() == "windows" {
let mut res = winres::WindowsResource::new();
res.set_icon("app.ico");
res.compile().unwrap();
}
}
+28 -5
View File
@@ -462,7 +462,10 @@
</head>
<body>
<div class="header-bar">
<h1>🧠 Antigravity Memory Hub</h1>
<div>
<h1 style="display:inline-block; margin:0;">🧠 Antigravity Memory Hub</h1>
<span id="app-version" style="font-size:0.8em; color:var(--text-secondary); margin-left:12px; font-family:monospace;"></span>
</div>
<div class="header-actions">
<button class="theme-toggle" onclick="toggleTheme()" title="Toggle Dark Mode">🌓</button>
<div style="font-weight: 500;"><span class="status-dot"></span>System Online</div>
@@ -874,10 +877,16 @@
}
container.innerHTML = data.results.map(r => `
<div class="search-result-item" onclick="if(rawEntities['${r.type_name}']){ showInspector('${r.type_name}'); network.selectNodes(['${r.type_name}']); network.focus('${r.type_name}', {scale:1.2, animation:true}); }" style="cursor:pointer;">
<strong>${highlightText(r.type_name, q)}</strong>
<div>${highlightText(r.content.substring(0, 150), q)}...</div>
<div style="font-size:0.85em;color:var(--text-secondary);margin-top:4px;">Score: ${r.score.toFixed(2)}</div>
<div class="search-result-item" onclick="if(rawEntities['${r.id}']){ showInspector('${r.id}'); network.selectNodes(['${r.id}']); network.focus('${r.id}', {scale:1.2, animation:true}); }" style="cursor:pointer;">
<div style="display:flex; justify-content:space-between; align-items:flex-start;">
<div>
<strong>${highlightText(r.title, q)}</strong>
<span style="font-size: 0.7em; color: var(--text-secondary); background: var(--canvas-bg); padding: 2px 6px; border-radius: 4px; margin-left: 8px; text-transform: uppercase; border: 1px solid var(--border-color);">${r.type_name}</span>
</div>
<div style="font-size:0.75em; color:var(--text-secondary);">${r.score.toFixed(2)}</div>
</div>
<div style="margin-top:6px; font-size:0.9em; line-height:1.4;">${highlightText(r.content.substring(0, 150), q)}${r.content.length > 150 ? '...' : ''}</div>
<div style="font-size:0.75em; color:var(--text-secondary); margin-top:8px; font-family:monospace; background: var(--canvas-bg); padding: 2px 4px; border-radius: 3px; display: inline-block;">ID: ${r.id}</div>
</div>
`).join('');
}
@@ -940,7 +949,21 @@
}
}
async function loadVersion() {
try {
const res = await fetch('/api/version');
const data = await res.json();
const verEl = document.getElementById('app-version');
if (verEl) {
verEl.textContent = `v${data.version}`;
}
} catch (err) {
console.error("Failed to load version", err);
}
}
// --- Start ---
loadVersion();
loadGraph();
loadTasks();
loadStickyNotes();
+34 -5
View File
@@ -141,6 +141,14 @@ impl MemoryHandler {
"read_sticky_notes",
"Read all active sticky notes.",
),
crate::mcp::tool_def::<DeleteStickyNoteTool>(
"delete_sticky_note",
"Delete a specific sticky note by its 1-indexed position.",
),
crate::mcp::tool_def::<ClearStickyNotesTool>(
"clear_sticky_notes",
"Clear all active sticky notes.",
),
crate::mcp::tool_def::<CondenseEntityTool>(
"condense_entity",
"Condense or summarize an entity's observations to reduce size.",
@@ -527,7 +535,7 @@ impl MemoryHandler {
let mut result = KnowledgeGraph::default();
let full = self.state.get_full_graph();
for (id, doc_type) in matches {
for (id, doc_type, _, _, _) in matches {
if doc_type == "entity"
&& let Some(e) = full.entities.get(&id)
{
@@ -669,6 +677,27 @@ impl MemoryHandler {
.unwrap_or_else(|_| "[]".to_string());
Ok(vec![data.to_string()][0].clone())
}
"delete_sticky_note" => {
let req = parse_tool!(args.clone(), id, DeleteStickyNoteTool);
let mut success = false;
self.state.sticky.modify(|notes| {
if req.index > 0 && req.index <= notes.len() {
notes.remove(req.index - 1);
success = true;
}
});
if success {
Ok(vec!["Sticky note deleted.".to_string()][0].clone())
} else {
Err("Invalid sticky note index.".to_string())
}
}
"clear_sticky_notes" => {
self.state.sticky.modify(|notes| {
notes.clear();
});
Ok(vec!["All sticky notes cleared.".to_string()][0].clone())
}
"condense_entity" => {
let req = parse_tool!(args.clone(), id, CondenseEntityTool);
self.state.apply_sync_write(|master| {
@@ -1471,7 +1500,7 @@ impl MemoryHandler {
let mut adrs = Vec::new();
let full = self.state.get_full_graph();
for (id, doc_type) in &matches {
for (id, doc_type, _, _, _) in &matches {
if doc_type == "entity"
&& let Some(e) = full.entities.get(id)
{
@@ -1479,20 +1508,20 @@ impl MemoryHandler {
}
}
for t in self.state.tasks.read() {
if matches.iter().any(|(id, typ)| id == &t.id && typ == "task") {
if matches.iter().any(|(id, typ, _, _, _)| id == &t.id && typ == "task") {
tasks.push(t);
}
}
for s in self.state.snippets.read() {
if matches
.iter()
.any(|(id, typ)| id == &s.name && typ == "snippet")
.any(|(id, typ, _, _, _)| id == &s.name && typ == "snippet")
{
snippets.push(s);
}
}
for a in self.state.adrs.read() {
if matches.iter().any(|(id, typ)| id == &a.id && typ == "adr") {
if matches.iter().any(|(id, typ, _, _, _)| id == &a.id && typ == "adr") {
adrs.push(a);
}
}
+6 -4
View File
@@ -331,11 +331,13 @@ fn run_server(state: Arc<MemoryState>) -> Result<(), Box<dyn std::error::Error>>
&& let Ok(idx) = state_clone.search_index.read()
&& let Ok(results) = idx.search(q, None) {
let mut formatted_results = Vec::new();
for (type_name, content) in results {
for (id, doc_type, title, body, score) in results {
formatted_results.push(serde_json::json!({
"type_name": type_name,
"content": content,
"score": 1.0
"id": id,
"type_name": doc_type,
"title": title,
"content": body,
"score": score
}));
}
return axum::Json(
+13 -3
View File
@@ -83,7 +83,7 @@ impl MemoryIndex {
&self,
query: &str,
namespace: Option<&str>,
) -> tantivy::Result<Vec<(String, String)>> {
) -> tantivy::Result<Vec<(String, String, String, String, f32)>> {
let searcher = self.reader.searcher();
let query_parser = tantivy::query::QueryParser::for_index(
&self.index,
@@ -96,7 +96,7 @@ impl MemoryIndex {
&tantivy::collector::TopDocs::with_limit(50).order_by_score(),
)?;
let mut results = Vec::new();
for (_score, doc_address) in top_docs {
for (score, doc_address) in top_docs {
let retrieved_doc = searcher.doc::<tantivy::TantivyDocument>(doc_address)?;
let id = retrieved_doc
.get_first(self.id_field)
@@ -108,6 +108,16 @@ impl MemoryIndex {
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string();
let title = retrieved_doc
.get_first(self.title_field)
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string();
let body = retrieved_doc
.get_first(self.body_field)
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string();
let doc_ns = retrieved_doc
.get_first(self.namespace_field)
.and_then(|v| v.as_str())
@@ -118,7 +128,7 @@ impl MemoryIndex {
{
continue;
}
results.push((id, doc_type));
results.push((id, doc_type, title, body, score));
}
Ok(results)
}
+11
View File
@@ -119,6 +119,17 @@ pub struct AddStickyNoteTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ReadStickyNotesTool {}
/// Delete a specific sticky note by its 1-indexed position.
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct DeleteStickyNoteTool {
/// The 1-indexed position of the sticky note to delete.
pub index: usize,
}
/// Clear all sticky notes.
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ClearStickyNotesTool {}
/// Condense or summarize an entity's observations to reduce size.
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct CondenseEntityTool {
+7 -7
View File
@@ -6,14 +6,14 @@ fn test_eager_tools_parity() {
let memory_source =
std::fs::read_to_string("src/handlers.rs").expect("Failed to read handlers.rs");
let mut memory_tools = HashSet::new();
for line in memory_source.lines() {
if line.contains("crate::mcp::tool_def")
&& let Some(start) = line.find("(\"") {
let rest = &line[start + 2..];
if let Some(end) = rest.find("\"") {
memory_tools.insert(rest[..end].to_string());
}
let parts: Vec<&str> = memory_source.split("crate::mcp::tool_def").collect();
for part in parts.iter().skip(1) {
if let Some(start) = part.find("\"") {
let rest = &part[start + 1..];
if let Some(end) = rest.find("\"") {
memory_tools.insert(rest[..end].to_string());
}
}
}
assert!(
!memory_tools.is_empty(),