feat: version string formatting yy.mm.dd.hhmmss, app icon, and test fixes
This commit is contained in:
1 parent
1752753fcc
commit
81aac7245b
17 files changed
+170
-412
No files matched your search
@@ -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"
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
+8
-3
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in new issue
Block a user