@@ -2872,30 +2872,83 @@ def extension_update(
28722872 # Remove any NEW command files created by failed install
28732873 # (files that weren't in the original backup)
28742874 try :
2875+ # Use the backup directory's mtime as an approximate
2876+ # "start of install" timestamp. Files modified after this
2877+ # and not present in the backup are candidates for cleanup
2878+ backup_timestamp = None
2879+ try :
2880+ backup_timestamp = Path (backup_base ).stat ().st_mtime
2881+ except Exception :
2882+ # If we can't stat the backup directory, we still fall
2883+ # back to registry-based cleanup where possible.
2884+ pass
2885+
28752886 new_registry_entry = manager .registry .get (extension_id )
2876- if new_registry_entry is None :
2877- new_registered_commands = {}
2878- else :
2887+ if new_registry_entry is not None :
28792888 new_registered_commands = new_registry_entry .get ("registered_commands" , {})
2880- for agent_name , cmd_names in new_registered_commands .items ():
2881- if agent_name not in registrar .AGENT_CONFIGS :
2882- continue
2883- agent_config = registrar .AGENT_CONFIGS [agent_name ]
2884- commands_dir = project_root / agent_config ["dir" ]
2885-
2886- for cmd_name in cmd_names :
2887- cmd_file = commands_dir / f"{ cmd_name } { agent_config ['extension' ]} "
2888- # Delete if it exists and wasn't in our backup
2889- if cmd_file .exists () and str (cmd_file ) not in backed_up_command_files :
2890- cmd_file .unlink ()
2891-
2892- # Also handle copilot prompt files
2889+
2890+ for agent_name , cmd_names in new_registered_commands .items ():
2891+ if agent_name not in registrar .AGENT_CONFIGS :
2892+ continue
2893+ agent_config = registrar .AGENT_CONFIGS [agent_name ]
2894+ commands_dir = project_root / agent_config ["dir" ]
2895+
2896+ for cmd_name in cmd_names :
2897+ cmd_file = commands_dir / f"{ cmd_name } { agent_config ['extension' ]} "
2898+ # Delete if it exists and wasn't in our backup
2899+ if cmd_file .exists () and str (cmd_file ) not in backed_up_command_files :
2900+ # Optionally use timestamp heuristic if available
2901+ if backup_timestamp is None or cmd_file .stat ().st_mtime >= backup_timestamp :
2902+ cmd_file .unlink ()
2903+
2904+ # Also handle copilot prompt files
2905+ if agent_name == "copilot" :
2906+ prompt_file = project_root / ".github" / "prompts" / f"{ cmd_name } .prompt.md"
2907+ if prompt_file .exists () and str (prompt_file ) not in backed_up_command_files :
2908+ if backup_timestamp is None or prompt_file .stat ().st_mtime >= backup_timestamp :
2909+ prompt_file .unlink ()
2910+ else :
2911+ # Fallback: we have no new registry entry (e.g., failure
2912+ # occurred before it was written). Best-effort cleanup by
2913+ # scanning agent command directories for files that weren't
2914+ # in the backup and appear to have been created/modified
2915+ # after the backup was taken.
2916+ for agent_name , agent_config in registrar .AGENT_CONFIGS .items ():
2917+ commands_dir = project_root / agent_config ["dir" ]
2918+ if commands_dir .is_dir ():
2919+ for cmd_file in commands_dir .glob (f"*{ agent_config ['extension' ]} " ):
2920+ cmd_path_str = str (cmd_file )
2921+ if cmd_path_str in backed_up_command_files :
2922+ continue
2923+ if backup_timestamp is not None :
2924+ try :
2925+ if cmd_file .stat ().st_mtime < backup_timestamp :
2926+ continue
2927+ except OSError :
2928+ # If we can't stat the file, skip it
2929+ continue
2930+ cmd_file .unlink ()
2931+
2932+ # Also handle copilot prompt files in fallback mode
28932933 if agent_name == "copilot" :
2894- prompt_file = project_root / ".github" / "prompts" / f"{ cmd_name } .prompt.md"
2895- if prompt_file .exists () and str (prompt_file ) not in backed_up_command_files :
2896- prompt_file .unlink ()
2934+ prompts_dir = project_root / ".github" / "prompts"
2935+ if prompts_dir .is_dir ():
2936+ for prompt_file in prompts_dir .glob ("*.prompt.md" ):
2937+ prompt_path_str = str (prompt_file )
2938+ if prompt_path_str in backed_up_command_files :
2939+ continue
2940+ if backup_timestamp is not None :
2941+ try :
2942+ if prompt_file .stat ().st_mtime < backup_timestamp :
2943+ continue
2944+ except OSError :
2945+ continue
2946+ prompt_file .unlink ()
28972947 except KeyError :
2898- pass # No new registry entry exists, nothing to clean up
2948+ # If the registry lookup itself fails, we conservatively do
2949+ # nothing extra here; the backup-based restoration below will
2950+ # still put back any original files we saved.
2951+ pass
28992952
29002953 # Restore backed up command files
29012954 for original_path , backup_path in backed_up_command_files .items ():
0 commit comments