feat: add automatic greeting upon connection #42

Merged
naomi merged 3 commits from feat/greeting into main 2026-01-16 15:10:28 -08:00
2 changed files with 37 additions and 12 deletions
Showing only changes of commit f8e989c5ab - Show all commits
+17 -2
View File
@@ -21,7 +21,7 @@ pub struct ClaudeStartOptions {
pub allowed_tools: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HikariConfig {
#[serde(default)]
pub model: Option<String>,
@@ -48,6 +48,21 @@ pub struct HikariConfig {
pub greeting_custom_prompt: Option<String>,
}
impl Default for HikariConfig {
fn default() -> Self {
Self {
model: None,
api_key: None,
custom_instructions: None,
mcp_servers_json: None,
auto_granted_tools: Vec::new(),
theme: Theme::default(),
greeting_enabled: true,
greeting_custom_prompt: None,
}
}
}
fn default_greeting_enabled() -> bool {
true
}
@@ -73,7 +88,7 @@ mod tests {
assert!(config.mcp_servers_json.is_none());
assert!(config.auto_granted_tools.is_empty());
assert_eq!(config.theme, Theme::Dark);
assert!(!config.greeting_enabled);
assert!(config.greeting_enabled);
assert!(config.greeting_custom_prompt.is_none());
}
+20 -10
View File
@@ -11,20 +11,25 @@ interface StateChangePayload {
tool_name: string | null;
}
function generateTimeBasedGreeting(): string {
function getTimeOfDay(): string {
const hour = new Date().getHours();
if (hour >= 5 && hour < 12) {
return "Good morning! How can I help you today?";
return "morning";
} else if (hour >= 12 && hour < 17) {
return "Good afternoon! What are we working on?";
return "afternoon";
} else if (hour >= 17 && hour < 21) {
return "Good evening! Ready to get some work done?";
return "evening";
} else {
return "Working late? I'm here to help!";
return "late night";
}
}
function generateGreetingPrompt(): string {
const timeOfDay = getTimeOfDay();
return `[System: A new session has started. It's currently ${timeOfDay}. Please greet the user warmly and briefly. Keep it short - just 1-2 sentences.]`;
}
async function sendGreeting() {
const config = configStore.getConfig();
@@ -32,12 +37,17 @@ async function sendGreeting() {
return;
}
const greetingPrompt = config.greeting_custom_prompt?.trim() || generateTimeBasedGreeting();
const greetingPrompt = config.greeting_custom_prompt?.trim() || generateGreetingPrompt();
// Don't show the system prompt in the UI - just trigger Claude to respond
characterState.setState("thinking");
try {
await invoke("send_prompt", { message: greetingPrompt });
} catch (error) {
console.error("Failed to send greeting:", error);
claudeStore.addLine("error", `Failed to send greeting: ${error}`);
characterState.setTemporaryState("error", 3000);
}
}
@@ -48,13 +58,15 @@ interface OutputPayload {
}
export async function initializeTauriListeners() {
await listen<string>("claude:connection", (event) => {
await listen<string>("claude:connection", async (event) => {
const status = event.payload as ConnectionStatus;
claudeStore.setConnectionStatus(status);
if (status === "connected") {
claudeStore.addLine("system", "Connected to Claude Code");
characterState.setState("idle");
// Send greeting when connection is established
await sendGreeting();
} else if (status === "disconnected") {
claudeStore.addLine("system", "Disconnected from Claude Code");
characterState.setState("idle");
@@ -101,11 +113,9 @@ export async function initializeTauriListeners() {
// no-op
});
await listen<string>("claude:session", async (event) => {
await listen<string>("claude:session", (event) => {
claudeStore.setSessionId(event.payload);
claudeStore.addLine("system", `Session: ${event.payload.substring(0, 8)}...`);
await sendGreeting();
});
await listen<string>("claude:cwd", (event) => {