@@ -28,6 +28,12 @@ pub enum GitHubSubcommand {
2828
2929 /// Check GitHub Actions installation status.
3030 Status ( StatusArgs ) ,
31+
32+ /// Uninstall/remove the Cortex GitHub workflow.
33+ Uninstall ( UninstallArgs ) ,
34+
35+ /// Update the Cortex GitHub workflow to the latest version.
36+ Update ( UpdateArgs ) ,
3137}
3238
3339/// Arguments for install command.
@@ -100,13 +106,51 @@ pub struct StatusArgs {
100106 pub json : bool ,
101107}
102108
109+ /// Arguments for uninstall command.
110+ #[ derive( Debug , Parser ) ]
111+ pub struct UninstallArgs {
112+ /// Path to the repository root (defaults to current directory).
113+ #[ arg( short, long) ]
114+ pub path : Option < PathBuf > ,
115+
116+ /// Workflow name to remove (defaults to "cortex").
117+ #[ arg( long, default_value = "cortex" ) ]
118+ pub workflow_name : String ,
119+
120+ /// Force removal without confirmation.
121+ #[ arg( short, long) ]
122+ pub force : bool ,
123+ }
124+
125+ /// Arguments for update command.
126+ #[ derive( Debug , Parser ) ]
127+ pub struct UpdateArgs {
128+ /// Path to the repository root (defaults to current directory).
129+ #[ arg( short, long) ]
130+ pub path : Option < PathBuf > ,
131+
132+ /// Workflow name to update (defaults to "cortex").
133+ #[ arg( long, default_value = "cortex" ) ]
134+ pub workflow_name : String ,
135+
136+ /// Include PR review automation.
137+ #[ arg( long, default_value_t = true ) ]
138+ pub pr_review : bool ,
139+
140+ /// Include issue automation.
141+ #[ arg( long, default_value_t = true ) ]
142+ pub issue_automation : bool ,
143+ }
144+
103145impl GitHubCli {
104146 /// Run the GitHub command.
105147 pub async fn run ( self ) -> Result < ( ) > {
106148 match self . subcommand {
107149 GitHubSubcommand :: Install ( args) => run_install ( args) . await ,
108150 GitHubSubcommand :: Run ( args) => run_github_agent ( args) . await ,
109151 GitHubSubcommand :: Status ( args) => run_status ( args) . await ,
152+ GitHubSubcommand :: Uninstall ( args) => run_uninstall ( args) . await ,
153+ GitHubSubcommand :: Update ( args) => run_update ( args) . await ,
110154 }
111155 }
112156}
@@ -614,6 +658,154 @@ async fn run_status(args: StatusArgs) -> Result<()> {
614658 Ok ( ( ) )
615659}
616660
661+ /// Uninstall/remove GitHub Actions workflow.
662+ async fn run_uninstall ( args : UninstallArgs ) -> Result < ( ) > {
663+ use std:: io:: { self , Write } ;
664+
665+ let repo_path = args. path . unwrap_or_else ( || PathBuf :: from ( "." ) ) ;
666+
667+ // Validate path exists
668+ if !repo_path. exists ( ) {
669+ bail ! ( "Path does not exist: {}" , repo_path. display( ) ) ;
670+ }
671+
672+ // Check for workflow files
673+ let workflows_dir = repo_path. join ( ".github" ) . join ( "workflows" ) ;
674+
675+ // Try multiple possible workflow file names
676+ let possible_names = vec ! [
677+ format!( "{}.yml" , args. workflow_name) ,
678+ format!( "{}.yaml" , args. workflow_name) ,
679+ ] ;
680+
681+ let mut found_workflow: Option < PathBuf > = None ;
682+ for name in & possible_names {
683+ let path = workflows_dir. join ( name) ;
684+ if path. exists ( ) {
685+ // Verify it's a Cortex workflow
686+ if let Ok ( content) = std:: fs:: read_to_string ( & path) {
687+ if content. contains ( "Cortex" ) || content. contains ( "cortex" ) {
688+ found_workflow = Some ( path) ;
689+ break ;
690+ }
691+ }
692+ }
693+ }
694+
695+ let workflow_path = match found_workflow {
696+ Some ( p) => p,
697+ None => {
698+ bail ! (
699+ "Cortex workflow '{}' not found in {}.\n \
700+ Use `cortex github status` to check installation status.",
701+ args. workflow_name,
702+ workflows_dir. display( )
703+ ) ;
704+ }
705+ } ;
706+
707+ // Confirm removal unless --force
708+ if !args. force {
709+ print ! (
710+ "Remove Cortex workflow '{}'? [y/N]: " ,
711+ workflow_path. display( )
712+ ) ;
713+ io:: stdout ( ) . flush ( ) ?;
714+
715+ let mut input = String :: new ( ) ;
716+ io:: stdin ( ) . read_line ( & mut input) ?;
717+
718+ if !input. trim ( ) . eq_ignore_ascii_case ( "y" ) {
719+ println ! ( "Cancelled." ) ;
720+ return Ok ( ( ) ) ;
721+ }
722+ }
723+
724+ // Remove the workflow file
725+ std:: fs:: remove_file ( & workflow_path)
726+ . with_context ( || format ! ( "Failed to remove workflow: {}" , workflow_path. display( ) ) ) ?;
727+
728+ println ! ( "Cortex workflow removed successfully!" ) ;
729+ println ! ( " Removed: {}" , workflow_path. display( ) ) ;
730+ println ! ( ) ;
731+ println ! (
732+ "Note: You may also want to remove the CORTEX_API_KEY secret from your repository settings."
733+ ) ;
734+
735+ Ok ( ( ) )
736+ }
737+
738+ /// Update GitHub Actions workflow to latest version.
739+ async fn run_update ( args : UpdateArgs ) -> Result < ( ) > {
740+ use cortex_engine:: github:: { WorkflowConfig , generate_workflow} ;
741+
742+ let repo_path = args. path . unwrap_or_else ( || PathBuf :: from ( "." ) ) ;
743+
744+ // Validate path exists
745+ if !repo_path. exists ( ) {
746+ bail ! ( "Path does not exist: {}" , repo_path. display( ) ) ;
747+ }
748+
749+ let workflows_dir = repo_path. join ( ".github" ) . join ( "workflows" ) ;
750+
751+ // Try to find existing workflow
752+ let possible_names = vec ! [
753+ format!( "{}.yml" , args. workflow_name) ,
754+ format!( "{}.yaml" , args. workflow_name) ,
755+ ] ;
756+
757+ let mut existing_path: Option < PathBuf > = None ;
758+ for name in & possible_names {
759+ let path = workflows_dir. join ( name) ;
760+ if path. exists ( ) {
761+ existing_path = Some ( path) ;
762+ break ;
763+ }
764+ }
765+
766+ let workflow_file = match existing_path {
767+ Some ( p) => p,
768+ None => {
769+ bail ! (
770+ "Cortex workflow '{}' not found. Use `cortex github install` first." ,
771+ args. workflow_name
772+ ) ;
773+ }
774+ } ;
775+
776+ // Generate updated workflow
777+ let config = WorkflowConfig {
778+ name : args. workflow_name . clone ( ) ,
779+ pr_review : args. pr_review ,
780+ issue_automation : args. issue_automation ,
781+ } ;
782+
783+ let workflow_content = generate_workflow ( & config) ;
784+
785+ // Write updated workflow
786+ std:: fs:: write ( & workflow_file, & workflow_content)
787+ . with_context ( || format ! ( "Failed to write workflow file: {}" , workflow_file. display( ) ) ) ?;
788+
789+ println ! ( "Cortex workflow updated successfully!" ) ;
790+ println ! ( " Path: {}" , workflow_file. display( ) ) ;
791+ println ! ( ) ;
792+ println ! ( "Features enabled:" ) ;
793+ if args. pr_review {
794+ println ! ( " • PR review automation" ) ;
795+ }
796+ if args. issue_automation {
797+ println ! ( " • Issue automation" ) ;
798+ }
799+ println ! ( ) ;
800+ println ! ( "Next steps:" ) ;
801+ println ! ( " 1. Commit and push the updated workflow:" ) ;
802+ println ! ( " git add {}" , workflow_file. display( ) ) ;
803+ println ! ( " git commit -m \" Update Cortex workflow\" " ) ;
804+ println ! ( " git push" ) ;
805+
806+ Ok ( ( ) )
807+ }
808+
617809/// Installation status information.
618810#[ derive( Debug , Default , serde:: Serialize ) ]
619811struct InstallationStatus {
0 commit comments