14
14
# Output files (relative to PROJECT_ROOT, placed in tmp/)
15
15
TMP_DIR = PROJECT_ROOT / "tmp"
16
16
FULL_OUTPUT_FILE = TMP_DIR / "output_full.txt"
17
- DIFF_OUTPUT_FILE = TMP_DIR / "output_diff.txt"
18
17
LAYOUTS_OUTPUT_FILE = TMP_DIR / "output_layouts.txt"
19
18
20
19
# Files/Directories to EXCLUDE (relative to PROJECT_ROOT)
42
41
"hugo_stats.json" ,
43
42
".hugo_build.lock" ,
44
43
str (FULL_OUTPUT_FILE .name ),
45
- str (DIFF_OUTPUT_FILE .name ),
46
44
str (LAYOUTS_OUTPUT_FILE .name ),
47
45
"*.pyc" ,
48
46
"*~" , # Backup files
@@ -110,18 +108,15 @@ def should_include(filename: str) -> bool:
110
108
return True
111
109
return False
112
110
113
- def find_relevant_files (root : Path , checkpoint_mtime : float | None = None , mode : str = "full" , with_public_html : bool = False ) -> list [Path ]:
111
+ def find_relevant_files (root : Path , mode : str = "full" , with_public_html : bool = False ) -> list [Path ]:
114
112
"""
115
113
Walks the directory tree, applying include/exclude rules.
116
- If checkpoint_mtime is provided, only includes files newer than it.
117
114
If mode is "layouts", filters for files within "layouts" directories.
118
115
If with_public_html is True and mode is "full", also includes HTML files from ./public.
119
116
"""
120
117
initial_candidates = set () # Use a set to handle potential overlaps gracefully
121
118
122
119
log (f"Scanning directory: { root } for mode '{ mode } ' (main scan)" )
123
- if checkpoint_mtime :
124
- log (f"Filtering for files newer than: { datetime .fromtimestamp (checkpoint_mtime )} " )
125
120
126
121
for current_dir_str , dir_names , file_names in os .walk (root , topdown = True ):
127
122
current_path = Path (current_dir_str )
@@ -138,13 +133,6 @@ def find_relevant_files(root: Path, checkpoint_mtime: float | None = None, mode:
138
133
if not should_include (filename ):
139
134
continue
140
135
141
- if checkpoint_mtime :
142
- try :
143
- if file_path .stat ().st_mtime <= checkpoint_mtime :
144
- continue
145
- except OSError as e :
146
- log (f"Warning: Could not stat file { file_path } : { e } . Skipping." )
147
- continue
148
136
initial_candidates .add (file_path .resolve ()) # Store absolute paths
149
137
150
138
log (f"Found { len (initial_candidates )} candidate files from main scan." )
@@ -158,15 +146,6 @@ def find_relevant_files(root: Path, checkpoint_mtime: float | None = None, mode:
158
146
for item in public_dir .rglob ('*.html' ): # rglob for recursive search
159
147
if item .is_file ():
160
148
public_file_path = item .resolve () # Ensure absolute path
161
- # Apply mtime filter for public HTML files too, if diff mode were to use this
162
- if checkpoint_mtime :
163
- try :
164
- if public_file_path .stat ().st_mtime <= checkpoint_mtime :
165
- continue
166
- except OSError as e :
167
- log (f"Warning: Could not stat file { public_file_path } : { e } . Skipping." )
168
- continue
169
-
170
149
if public_file_path not in initial_candidates : # Add if not already picked up
171
150
initial_candidates .add (public_file_path )
172
151
public_html_added_count += 1
@@ -194,7 +173,7 @@ def find_relevant_files(root: Path, checkpoint_mtime: float | None = None, mode:
194
173
return final_relevant_files_list
195
174
196
175
197
- def generate_header (mode : str , checkpoint_file : Path | None = None , with_public_html : bool = False ) -> str :
176
+ def generate_header (mode : str , with_public_html : bool = False ) -> str :
198
177
"""Generates the header content for the output file."""
199
178
common_instructions = """
200
179
**Instructions for AI:**
@@ -209,23 +188,7 @@ def generate_header(mode: str, checkpoint_file: Path | None = None, with_public_
209
188
9. Format code changes in a way that is most simple for an LLM (gemini, copilot) to integrate - this could be one single code block. It is not necessary to provide human instructions that highlight the specific lines being updated.
210
189
10. indicate which file it is to be updated, outside of the file codeblock
211
190
"""
212
- if mode == "diff" :
213
- checkpoint_ts_str = "ERROR: Checkpoint file missing!"
214
- if checkpoint_file and checkpoint_file .exists ():
215
- checkpoint_mtime = checkpoint_file .stat ().st_mtime
216
- checkpoint_ts = datetime .fromtimestamp (checkpoint_mtime )
217
- checkpoint_ts_str = checkpoint_ts .strftime ('%Y-%m-%d %H:%M:%S %Z' )
218
-
219
- return f"""--- START OF PROJECT CONTEXT UPDATE ---
220
-
221
- This file contains ONLY the content of key files that have been MODIFIED since the last full context checkpoint was generated. Image files are listed by path only.
222
-
223
- **Checkpoint File:** { checkpoint_file .relative_to (PROJECT_ROOT ) if checkpoint_file else 'N/A' }
224
- **Checkpoint Timestamp:** { checkpoint_ts_str }
225
- { common_instructions .replace ("1. Analyze Structure..." , "1. **Apply Updates:** Use the content below to update your understanding of the project based on the changes since the checkpoint timestamp. Prioritize this information when it conflicts with previous context from the full checkpoint. Remember this is NOT the full project, only the changed files. Refer back to the full checkpoint if needed for unchanged files or broader structure." )}
226
- --- MODIFIED FILE CONTENTS START ---
227
- """
228
- elif mode == "layouts" :
191
+ if mode == "layouts" :
229
192
return f"""--- START OF PROJECT CONTEXT (Layouts Only) ---
230
193
231
194
This file contains the content of files found within all 'layouts' directories in the project. Image files are listed by path only.
@@ -252,37 +215,21 @@ def generate_header(mode: str, checkpoint_file: Path | None = None, with_public_
252
215
253
216
def generate_footer (mode : str ) -> str :
254
217
"""Generates the footer content."""
255
- if mode == "diff" :
256
- return "\n --- END OF PROJECT CONTEXT UPDATE ---"
257
- elif mode == "layouts" :
218
+ if mode == "layouts" :
258
219
return "\n --- END OF PROJECT LAYOUTS CONTEXT ---"
259
220
else : # mode == "full"
260
221
return "\n --- END OF PROJECT CONTEXT ---"
261
222
262
- def create_context_file (mode : str , with_public_html : bool ):
223
+ def generate_context (mode : str , with_public_html : bool ):
263
224
"""Main function to generate the context file based on the mode."""
264
225
log (f"Project Root: { PROJECT_ROOT } " )
265
- checkpoint_mtime = None
266
226
target_output_file = None
267
227
268
228
if mode == "full" :
269
229
target_output_file = FULL_OUTPUT_FILE
270
230
log (f"Mode: Generating FULL context checkpoint -> { target_output_file .relative_to (PROJECT_ROOT )} " )
271
231
if with_public_html :
272
232
log ("Including HTML files from 'public' directory." )
273
- elif mode == "diff" :
274
- target_output_file = DIFF_OUTPUT_FILE
275
- log (f"Mode: Generating DIFF context based on { FULL_OUTPUT_FILE .relative_to (PROJECT_ROOT )} -> { target_output_file .relative_to (PROJECT_ROOT )} " )
276
- if not FULL_OUTPUT_FILE .exists ():
277
- log (f"Error: Checkpoint file '{ FULL_OUTPUT_FILE } ' not found. Please run with --full first." )
278
- sys .exit (1 )
279
- try :
280
- checkpoint_mtime = FULL_OUTPUT_FILE .stat ().st_mtime
281
- except OSError as e :
282
- log (f"Error: Could not read checkpoint file timestamp { FULL_OUTPUT_FILE } : { e } " )
283
- sys .exit (1 )
284
- if with_public_html :
285
- log ("Note: --with-public-html is typically used with --full. For --diff, it will include public HTML files modified since the checkpoint if any." )
286
233
elif mode == "layouts" :
287
234
target_output_file = LAYOUTS_OUTPUT_FILE
288
235
log (f"Mode: Generating LAYOUTS context -> { target_output_file .relative_to (PROJECT_ROOT )} " )
@@ -298,10 +245,10 @@ def create_context_file(mode: str, with_public_html: bool):
298
245
log (f"Error: Could not create output directory { TMP_DIR } : { e } " )
299
246
sys .exit (1 )
300
247
301
- files_to_process = find_relevant_files (PROJECT_ROOT , checkpoint_mtime , mode = mode , with_public_html = with_public_html )
248
+ files_to_process = find_relevant_files (PROJECT_ROOT , mode = mode , with_public_html = with_public_html )
302
249
303
250
log (f"Generating context file: { target_output_file .relative_to (PROJECT_ROOT )} " )
304
- header = generate_header (mode , FULL_OUTPUT_FILE if mode == "diff" else None , with_public_html if mode == "full" else False )
251
+ header = generate_header (mode , with_public_html if mode == "full" else False )
305
252
footer = generate_footer (mode )
306
253
307
254
try :
@@ -344,38 +291,30 @@ def create_context_file(mode: str, with_public_html: bool):
344
291
def main ():
345
292
"""Parses command line arguments and runs the script."""
346
293
parser = argparse .ArgumentParser (
347
- description = "Generate a context file with project source code for AI analysis." ,
294
+ description = "Generate a context file with project source code for AI analysis. Defaults to '--full' if no mode is specified. " ,
348
295
formatter_class = argparse .RawDescriptionHelpFormatter ,
349
296
epilog = f"""
350
297
Examples:
351
- Generate a full context checkpoint:
298
+ Generate a full context checkpoint (default action):
299
+ { sys .argv [0 ]}
352
300
{ sys .argv [0 ]} --full
353
301
354
- Generate a full context checkpoint and include HTML files from the 'public' directory:
302
+ Generate a full context and include HTML files from the 'public' directory:
355
303
{ sys .argv [0 ]} --full --with-public-html
356
304
357
- Generate a context file with changes since the last full checkpoint:
358
- { sys .argv [0 ]} --diff
359
-
360
305
Generate a context file with only content from 'layouts' directories:
361
306
{ sys .argv [0 ]} --layouts
362
307
"""
363
308
)
364
- group = parser .add_mutually_exclusive_group (required = True )
309
+ # If no mode flag is given, args.mode will be None, and we'll default to 'full'.
310
+ group = parser .add_mutually_exclusive_group (required = False )
365
311
group .add_argument (
366
312
"-f" , "--full" ,
367
313
action = "store_const" ,
368
314
const = "full" ,
369
315
dest = "mode" ,
370
316
help = f"Generate the full project context checkpoint ({ FULL_OUTPUT_FILE .relative_to (PROJECT_ROOT )} )."
371
317
)
372
- group .add_argument (
373
- "-d" , "--diff" ,
374
- action = "store_const" ,
375
- const = "diff" ,
376
- dest = "mode" ,
377
- help = f"Generate context with files modified since the last full checkpoint ({ DIFF_OUTPUT_FILE .relative_to (PROJECT_ROOT )} )."
378
- )
379
318
group .add_argument (
380
319
"-l" , "--layouts" ,
381
320
action = "store_const" ,
@@ -390,7 +329,11 @@ def main():
390
329
)
391
330
392
331
args = parser .parse_args ()
393
- create_context_file (args .mode , args .with_public_html )
332
+
333
+ # Default to 'full' mode if no other mode is selected
334
+ mode = args .mode or "full"
335
+
336
+ generate_context (mode , args .with_public_html )
394
337
395
338
if __name__ == "__main__" :
396
339
main ()
0 commit comments