lean_ctx/core/context_package/
content.rs1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use crate::core::knowledge::{ConsolidatedInsight, KnowledgeFact, ProjectPattern};
5
6use super::graph_model::ContextGraph;
7
8#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9pub struct PackageContent {
10 #[serde(default, skip_serializing_if = "Option::is_none")]
11 pub knowledge: Option<KnowledgeLayer>,
12 #[serde(default, skip_serializing_if = "Option::is_none")]
13 pub graph: Option<GraphLayer>,
14 #[serde(default, skip_serializing_if = "Option::is_none")]
15 pub session: Option<SessionLayer>,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub patterns: Option<PatternsLayer>,
18 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub gotchas: Option<GotchasLayer>,
20 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub context_graph: Option<ContextGraph>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct KnowledgeLayer {
26 pub facts: Vec<KnowledgeFact>,
27 pub patterns: Vec<ProjectPattern>,
28 pub insights: Vec<ConsolidatedInsight>,
29 pub exported_at: DateTime<Utc>,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct GraphLayer {
34 pub nodes: Vec<GraphNodeExport>,
35 pub edges: Vec<GraphEdgeExport>,
36 pub exported_at: DateTime<Utc>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct GraphNodeExport {
41 pub kind: String,
42 pub name: String,
43 pub file_path: String,
44 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub line_start: Option<usize>,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub line_end: Option<usize>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
49 pub metadata: Option<String>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct GraphEdgeExport {
54 pub source_path: String,
55 pub source_name: String,
56 pub target_path: String,
57 pub target_name: String,
58 pub kind: String,
59 #[serde(default, skip_serializing_if = "Option::is_none")]
60 pub metadata: Option<String>,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct SessionLayer {
65 pub task_description: Option<String>,
66 pub findings: Vec<SessionFinding>,
67 pub decisions: Vec<SessionDecision>,
68 pub next_steps: Vec<String>,
69 pub files_touched: Vec<String>,
70 pub exported_at: DateTime<Utc>,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct SessionFinding {
75 pub summary: String,
76 #[serde(default, skip_serializing_if = "Option::is_none")]
77 pub file: Option<String>,
78 #[serde(default, skip_serializing_if = "Option::is_none")]
79 pub line: Option<u32>,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct SessionDecision {
84 pub summary: String,
85 #[serde(default, skip_serializing_if = "Option::is_none")]
86 pub rationale: Option<String>,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct PatternsLayer {
91 pub patterns: Vec<ProjectPattern>,
92 pub exported_at: DateTime<Utc>,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct GotchasLayer {
97 pub gotchas: Vec<GotchaExport>,
98 pub exported_at: DateTime<Utc>,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct GotchaExport {
103 pub id: String,
104 pub category: String,
105 pub severity: String,
106 pub trigger: String,
107 pub resolution: String,
108 #[serde(default)]
109 pub file_patterns: Vec<String>,
110 pub confidence: f32,
111}
112
113impl PackageContent {
114 pub fn active_layer_count(&self) -> usize {
115 let mut n = 0;
116 if self.knowledge.is_some() {
117 n += 1;
118 }
119 if self.graph.is_some() {
120 n += 1;
121 }
122 if self.session.is_some() {
123 n += 1;
124 }
125 if self.patterns.is_some() {
126 n += 1;
127 }
128 if self.gotchas.is_some() {
129 n += 1;
130 }
131 if self.context_graph.is_some() {
132 n += 1;
133 }
134 n
135 }
136
137 pub fn is_empty(&self) -> bool {
138 self.active_layer_count() == 0
139 }
140
141 pub fn estimated_token_count(&self) -> usize {
142 let json = serde_json::to_string(self).unwrap_or_default();
143 json.len() / 4
144 }
145}