11import typer
2- import toml
3- from pathlib import Path
4- from typing import List , Set
5- from collections import defaultdict
6-
72from rich .console import Console
83
94from faff_core import Workspace
@@ -38,69 +33,21 @@ def list(
3833 ws : Workspace = ctx .obj
3934 console = Console ()
4035
41- # Get all plan files
42- plan_dir = Path (ws .storage ().plan_dir ())
43- plan_files = sorted (plan_dir .glob ("*.toml" ))
36+ plural_field = PLURAL_MAP [field ]
37+
38+ # Get intent counts from plans via Rust
39+ intent_count = ws .plans .get_field_usage_stats (field )
4440
45- # Collect all unique values with detailed counts
46- values : Set [str ] = set ()
47- intent_count : dict [str , int ] = defaultdict (int )
48- session_count : dict [str , int ] = defaultdict (int )
49- log_count : dict [str , set [str ]] = defaultdict (set ) # Set of log dates per value
41+ # Get session counts and log dates from logs via Rust
42+ session_count , log_dates_dict = ws .logs .get_field_usage_stats (field )
5043
51- plural_field = PLURAL_MAP [field ]
44+ # Combine all unique values from both plans and logs
45+ values = set (intent_count .keys ()) | set (session_count .keys ())
5246
53- # Count intents in plans
54- for plan_file in plan_files :
55- try :
56- plan_data = toml .load (plan_file )
57-
58- # Get values from intents
59- for intent_dict in plan_data .get ("intents" , []):
60- if field == "tracker" :
61- intent_values = intent_dict .get ("trackers" , [])
62- else :
63- intent_value = intent_dict .get (field )
64- intent_values = [intent_value ] if intent_value else []
65-
66- for value in intent_values :
67- if value :
68- values .add (value )
69- intent_count [value ] += 1
70-
71- except Exception :
72- continue
73-
74- # Count sessions in logs
75- log_dir = Path (ws .storage ().log_dir ())
76- log_files = sorted (log_dir .glob ("*.toml" ))
77-
78- for log_file in log_files :
79- try :
80- log_data = toml .load (log_file )
81- log_date = log_file .stem # Use filename as identifier
82-
83- # Sessions are stored as timeline entries with flattened intent fields
84- for session in log_data .get ("timeline" , []):
85- if field == "tracker" :
86- # Trackers can be a string or list
87- trackers = session .get ("trackers" , [])
88- if isinstance (trackers , str ):
89- session_values = [trackers ]
90- else :
91- session_values = trackers
92- else :
93- session_value = session .get (field )
94- session_values = [session_value ] if session_value else []
95-
96- for value in session_values :
97- if value :
98- values .add (value )
99- session_count [value ] += 1
100- log_count [value ].add (log_date )
101-
102- except Exception :
103- continue
47+ # Convert log_dates_dict values (lists of PyDate) to count of unique logs
48+ log_count = {}
49+ for value , dates in log_dates_dict .items ():
50+ log_count [value ] = len (dates )
10451
10552 # Display results
10653 if not values :
@@ -111,7 +58,7 @@ def list(
11158 for value in sorted (values ):
11259 intents = intent_count .get (value , 0 )
11360 sessions = session_count .get (value , 0 )
114- logs = len ( log_count .get (value , set ()) )
61+ logs = log_count .get (value , 0 )
11562
11663 console .print (
11764 f" { value } [dim]({ intents } intent{ 's' if intents != 1 else '' } , "
@@ -155,80 +102,18 @@ def replace(
155102 ws : Workspace = ctx .obj
156103 console = Console ()
157104
158- plural_field = PLURAL_MAP [field ]
159-
160- # Get all plan files
161- plan_dir = Path (ws .storage ().plan_dir ())
162- plan_files = sorted (plan_dir .glob ("*.toml" ))
163-
164- plans_updated = 0
165- intents_updated = 0
166-
167- # Update plans
168- for plan_file in plan_files :
169- try :
170- plan_data = toml .load (plan_file )
171- plan_modified = False
172-
173- # Update plan-level ROAST collection
174- if plural_field in plan_data :
175- field_list = plan_data [plural_field ]
176- if old_value in field_list :
177- # Replace in list
178- field_list = [new_value if v == old_value else v for v in field_list ]
179- plan_data [plural_field ] = field_list
180- plan_modified = True
181-
182- # Update intents
183- if "intents" in plan_data :
184- for intent_dict in plan_data ["intents" ]:
185- if intent_dict .get (field ) == old_value :
186- intent_dict [field ] = new_value
187- intents_updated += 1
188- plan_modified = True
189-
190- # Write back if modified
191- if plan_modified :
192- with open (plan_file , 'w' ) as f :
193- toml .dump (plan_data , f )
194- plans_updated += 1
195-
196- except Exception as e :
197- console .print (f"[yellow]Warning: Failed to update { plan_file .name } : { e } [/yellow]" )
198- continue
199-
105+ # Update plans via Rust layer
106+ plans_updated , intents_updated = ws .plans .replace_field_in_all_plans (
107+ field , old_value , new_value
108+ )
200109 console .print (f"[green]Updated { intents_updated } intent(s) across { plans_updated } plan(s)[/green]" )
201110
202- # Now update logs (do this regardless of whether intents were updated)
203-
204- log_dir = Path (ws .storage ().log_dir ())
205- log_files = sorted (log_dir .glob ("*.toml" ))
206-
207- logs_updated = 0
208- sessions_updated = 0
209-
210- for log_file in log_files :
211- try :
212- log_data = toml .load (log_file )
213- log_modified = False
214-
215- # Sessions are stored as timeline entries with flattened intent fields
216- if "timeline" in log_data :
217- for session in log_data ["timeline" ]:
218- if session .get (field ) == old_value :
219- session [field ] = new_value
220- sessions_updated += 1
221- log_modified = True
222-
223- if log_modified :
224- with open (log_file , 'w' ) as f :
225- toml .dump (log_data , f )
226- logs_updated += 1
227-
228- except Exception as e :
229- console .print (f"[yellow]Warning: Failed to update { log_file .name } : { e } [/yellow]" )
230- continue
231-
111+ # Update logs via Rust layer
112+ import datetime
113+ trackers = ws .plans .get_trackers (datetime .date .today ())
114+ logs_updated , sessions_updated = ws .logs .replace_field_in_all_logs (
115+ field , old_value , new_value , trackers
116+ )
232117 console .print (f"[green]Updated { sessions_updated } session(s) across { logs_updated } log(s)[/green]" )
233118
234119 console .print (f"\n [bold green]✓ Replacement complete[/bold green]" )
0 commit comments