1use crate::config::Config;
12use crate::hook::Host;
13use crate::index::{self, Index};
14use crate::session::Session;
15use crate::{embed, paths, skill};
16use serde::Deserialize;
17use std::io::Read;
18
19#[derive(Debug, Default, Deserialize)]
22struct RawEvent {
23 #[serde(default)]
24 session_id: String,
25 #[serde(default)]
26 source: String,
27}
28
29pub fn run(host: Host) -> anyhow::Result<()> {
33 if let Err(e) = session_start(host) {
35 crate::trace::debug("session-start failed", &e);
36 }
37 Ok(())
38}
39
40fn session_start(host: Host) -> anyhow::Result<()> {
41 let mut buf = String::new();
42 std::io::stdin().read_to_string(&mut buf)?;
43 let ev: RawEvent = serde_json::from_str(&buf).unwrap_or_default();
44
45 reindex(host);
46
47 if should_rearm(&ev.source) && !ev.session_id.is_empty() {
48 let path = paths::session_path(&ev.session_id);
49 let mut session = Session::load(&path);
50 session.clear();
51 let _ = session.save(&path);
52 }
53 Ok(())
54}
55
56fn reindex(host: Host) {
61 let (cfg, _file) = Config::load(host);
62 let skills = match skill::discover(&cfg.roots) {
63 Ok(s) => s,
64 Err(e) => return crate::trace::debug("session-start: skill discovery failed", &e),
65 };
66 let embedder = match embed::build(&cfg.model) {
67 Ok(e) => e,
68 Err(e) => return crate::trace::debug("session-start: embedder build failed", &e),
69 };
70 let index_path = paths::index_path(host);
71 let prev = Index::load(&index_path).ok().flatten();
72 match index::build(&skills, embedder.as_ref(), prev.as_ref()) {
73 Ok(idx) => {
74 if let Err(e) = idx.save(&index_path) {
75 crate::trace::debug("session-start: saving reindexed index failed", &e);
76 }
77 }
78 Err(e) => crate::trace::debug("session-start: index build failed", &e),
79 }
80}
81
82fn should_rearm(source: &str) -> bool {
84 source.eq_ignore_ascii_case("compact")
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use crate::session::Source;
91
92 #[test]
93 fn only_compact_rearms() {
94 assert!(should_rearm("compact"));
95 assert!(!should_rearm("startup"));
96 assert!(!should_rearm("resume"));
97 assert!(!should_rearm(""));
98 }
99
100 #[test]
101 fn clear_on_compact_empties_the_ledger() {
102 let mut s = Session::default();
103 s.mark("pdf", Source::Ski);
104 s.mark("xlsx", Source::Model);
105 if should_rearm("compact") {
106 s.clear();
107 }
108 assert!(s.loaded.is_empty());
109 }
110}