feat(story-93): expose server logs to agents via get_server_logs MCP tool
- Add log_buffer module: bounded 1000-line ring buffer with push/get_recent API - Add slog! macro: drop-in for eprintln! that also captures to ring buffer - Replace all eprintln! calls across agents, watcher, search, chat, worktree, claude_code with slog! - Add get_server_logs MCP tool: accepts count (1-500) and optional filter params - 5 unit tests for log_buffer covering push/retrieve, eviction, filtering, count limits, empty buffer - 262 tests passing, clippy clean Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use crate::slog;
|
||||
use crate::state::SessionState;
|
||||
use ignore::WalkBuilder;
|
||||
use serde::Serialize;
|
||||
@@ -52,7 +53,7 @@ pub async fn search_files_impl(query: String, root: PathBuf) -> Result<Vec<Searc
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(err) => eprintln!("Error walking dir: {}", err),
|
||||
Err(err) => slog!("Error walking dir: {}", err),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
//! via exit-code inspection and silently skips the commit while still broadcasting
|
||||
//! the event so connected clients stay in sync.
|
||||
|
||||
use crate::slog;
|
||||
use notify::{EventKind, RecommendedWatcher, RecursiveMode, Watcher, recommended_watcher};
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
@@ -151,13 +152,13 @@ fn flush_pending(
|
||||
("remove", item.to_string(), format!("story-kit: remove {item}"))
|
||||
};
|
||||
|
||||
eprintln!("[watcher] flush: {commit_msg}");
|
||||
slog!("[watcher] flush: {commit_msg}");
|
||||
match git_add_work_and_commit(git_root, &commit_msg) {
|
||||
Ok(committed) => {
|
||||
if committed {
|
||||
eprintln!("[watcher] committed: {commit_msg}");
|
||||
slog!("[watcher] committed: {commit_msg}");
|
||||
} else {
|
||||
eprintln!("[watcher] skipped (already committed): {commit_msg}");
|
||||
slog!("[watcher] skipped (already committed): {commit_msg}");
|
||||
}
|
||||
let stage = additions.first().map_or("unknown", |(_, s)| s);
|
||||
let evt = WatcherEvent {
|
||||
@@ -169,7 +170,7 @@ fn flush_pending(
|
||||
let _ = event_tx.send(evt);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[watcher] git error: {e}");
|
||||
slog!("[watcher] git error: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -192,17 +193,17 @@ pub fn start_watcher(
|
||||
}) {
|
||||
Ok(w) => w,
|
||||
Err(e) => {
|
||||
eprintln!("[watcher] failed to create watcher: {e}");
|
||||
slog!("[watcher] failed to create watcher: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = watcher.watch(&work_dir, RecursiveMode::Recursive) {
|
||||
eprintln!("[watcher] failed to watch {}: {e}", work_dir.display());
|
||||
slog!("[watcher] failed to watch {}: {e}", work_dir.display());
|
||||
return;
|
||||
}
|
||||
|
||||
eprintln!("[watcher] watching {}", work_dir.display());
|
||||
slog!("[watcher] watching {}", work_dir.display());
|
||||
|
||||
const DEBOUNCE: Duration = Duration::from_millis(300);
|
||||
|
||||
@@ -237,13 +238,13 @@ pub fn start_watcher(
|
||||
false
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
eprintln!("[watcher] notify error: {e}");
|
||||
slog!("[watcher] notify error: {e}");
|
||||
false
|
||||
}
|
||||
// Debounce window expired — time to flush.
|
||||
Err(mpsc::RecvTimeoutError::Timeout) => true,
|
||||
Err(mpsc::RecvTimeoutError::Disconnected) => {
|
||||
eprintln!("[watcher] channel disconnected, shutting down");
|
||||
slog!("[watcher] channel disconnected, shutting down");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user