Skip to main content

toolpath_pi/
lib.rs

1#![doc = include_str!("../README.md")]
2
3pub mod derive;
4pub mod error;
5pub mod io;
6pub mod paths;
7pub mod provider;
8pub mod reader;
9pub mod types;
10
11pub use derive::{derive_graph, derive_path, derive_project};
12pub use error::{PiError, Result};
13pub use paths::PathResolver;
14pub use provider::session_to_view;
15pub use reader::{PiSession, SessionMeta};
16pub use toolpath_convo::DeriveConfig;
17pub use types::{
18    AgentMessage, ContentBlock, CostBreakdown, Entry, EntryBase, SessionHeader, Usage,
19};
20
21use toolpath_convo::ConversationView;
22
23/// High-level interface for reading Pi sessions.
24#[derive(Debug, Clone, Default)]
25pub struct PiConvo {
26    resolver: PathResolver,
27}
28
29impl PiConvo {
30    /// Build a manager with the default resolver (`~/.pi/agent/sessions/`).
31    pub fn new() -> Self {
32        Self {
33            resolver: PathResolver::new(),
34        }
35    }
36
37    /// Build a manager with a custom resolver (useful for tests).
38    pub fn with_resolver(resolver: PathResolver) -> Self {
39        Self { resolver }
40    }
41
42    /// Access the underlying resolver.
43    pub fn resolver(&self) -> &PathResolver {
44        &self.resolver
45    }
46
47    /// Whether the Pi sessions directory exists on disk.
48    pub fn exists(&self) -> bool {
49        self.resolver.sessions_dir().exists()
50    }
51
52    /// List known project paths (decoded cwd values).
53    pub fn list_projects(&self) -> Result<Vec<String>> {
54        io::list_projects(&self.resolver)
55    }
56
57    /// List session metadata for a project, newest first.
58    pub fn list_sessions(&self, project: &str) -> Result<Vec<SessionMeta>> {
59        io::list_sessions(&self.resolver, project)
60    }
61
62    /// Read a specific session by ID.
63    pub fn read_session(&self, project: &str, session_id: &str) -> Result<PiSession> {
64        reader::read_session(&self.resolver, project, session_id)
65    }
66
67    /// Read the most recently active session for a project, if any.
68    pub fn most_recent_session(&self, project: &str) -> Result<Option<PiSession>> {
69        let mut metas = self.list_sessions(project)?;
70        if let Some(meta) = metas.drain(..).next() {
71            let session = self.read_session(project, &meta.id)?;
72            Ok(Some(session))
73        } else {
74            Ok(None)
75        }
76    }
77
78    /// Convert a Pi session into a provider-agnostic `ConversationView`.
79    pub fn to_view(&self, session: &PiSession) -> ConversationView {
80        provider::session_to_view(session)
81    }
82
83    /// Read all sessions for a project.
84    pub fn read_all_sessions(&self, project: &str) -> Result<Vec<PiSession>> {
85        let metas = self.list_sessions(project)?;
86        let mut sessions = Vec::new();
87        for meta in metas {
88            match self.read_session(project, &meta.id) {
89                Ok(s) => sessions.push(s),
90                Err(e) => {
91                    eprintln!("Warning: failed to read Pi session {}: {}", meta.id, e);
92                }
93            }
94        }
95        Ok(sessions)
96    }
97}