@@ -34,6 +34,7 @@ Examples:
3434 ` + constants .CLIExtensionPrefix + ` add githubnext/agentics/ci-doctor --pr --force
3535 ` + constants .CLIExtensionPrefix + ` add githubnext/agentics/*
3636 ` + constants .CLIExtensionPrefix + ` add githubnext/agentics/*@v1.0.0
37+ ` + constants .CLIExtensionPrefix + ` add githubnext/agentics/ci-doctor --dir shared # Add to .github/workflows/shared/
3738
3839Workflow specifications:
3940 - Two parts: "owner/repo[@version]" (lists available workflows in the repository)
@@ -44,6 +45,7 @@ Workflow specifications:
4445 - Version can be tag, branch, or SHA
4546
4647The -n flag allows you to specify a custom name for the workflow file (only applies to the first workflow when adding multiple).
48+ The --dir flag allows you to specify a subdirectory under .github/workflows/ where the workflow will be added.
4749The --pr flag automatically creates a pull request with the workflow changes.
4850The --force flag overwrites existing workflow files.` ,
4951 Run : func (cmd * cobra.Command , args []string ) {
@@ -56,6 +58,7 @@ The --force flag overwrites existing workflow files.`,
5658 appendText , _ := cmd .Flags ().GetString ("append" )
5759 verbose , _ := cmd .Flags ().GetBool ("verbose" )
5860 noGitattributes , _ := cmd .Flags ().GetBool ("no-gitattributes" )
61+ workflowDir , _ := cmd .Flags ().GetString ("dir" )
5962
6063 // If no arguments provided and not in CI, automatically use interactive mode
6164 if len (args ) == 0 && ! IsRunningInCI () {
@@ -76,12 +79,12 @@ The --force flag overwrites existing workflow files.`,
7679
7780 // Handle normal mode
7881 if prFlag {
79- if err := AddWorkflows (workflows , numberFlag , verbose , engineOverride , nameFlag , forceFlag , appendText , true , noGitattributes ); err != nil {
82+ if err := AddWorkflows (workflows , numberFlag , verbose , engineOverride , nameFlag , forceFlag , appendText , true , noGitattributes , workflowDir ); err != nil {
8083 fmt .Fprintln (os .Stderr , console .FormatErrorMessage (err .Error ()))
8184 os .Exit (1 )
8285 }
8386 } else {
84- if err := AddWorkflows (workflows , numberFlag , verbose , engineOverride , nameFlag , forceFlag , appendText , false , noGitattributes ); err != nil {
87+ if err := AddWorkflows (workflows , numberFlag , verbose , engineOverride , nameFlag , forceFlag , appendText , false , noGitattributes , workflowDir ); err != nil {
8588 fmt .Fprintln (os .Stderr , console .FormatErrorMessage (err .Error ()))
8689 os .Exit (1 )
8790 }
@@ -113,13 +116,16 @@ The --force flag overwrites existing workflow files.`,
113116 // Add no-gitattributes flag to add command
114117 cmd .Flags ().Bool ("no-gitattributes" , false , "Skip updating .gitattributes file" )
115118
119+ // Add workflow directory flag to add command
120+ cmd .Flags ().StringP ("dir" , "d" , "" , "Specify subdirectory under .github/workflows/ (e.g., 'shared' for .github/workflows/shared/)" )
121+
116122 return cmd
117123}
118124
119125// AddWorkflows adds one or more workflows from components to .github/workflows
120126// with optional repository installation and PR creation
121- func AddWorkflows (workflows []string , number int , verbose bool , engineOverride string , name string , force bool , appendText string , createPR bool , noGitattributes bool ) error {
122- addLog .Printf ("Adding workflows: count=%d, engineOverride=%s, createPR=%v, noGitattributes=%v" , len (workflows ), engineOverride , createPR , noGitattributes )
127+ func AddWorkflows (workflows []string , number int , verbose bool , engineOverride string , name string , force bool , appendText string , createPR bool , noGitattributes bool , workflowDir string ) error {
128+ addLog .Printf ("Adding workflows: count=%d, engineOverride=%s, createPR=%v, noGitattributes=%v, workflowDir=%s " , len (workflows ), engineOverride , createPR , noGitattributes , workflowDir )
123129
124130 if len (workflows ) == 0 {
125131 return fmt .Errorf ("at least one workflow name is required" )
@@ -232,12 +238,12 @@ func AddWorkflows(workflows []string, number int, verbose bool, engineOverride s
232238 // Handle PR creation workflow
233239 if createPR {
234240 addLog .Print ("Creating workflow with PR" )
235- return addWorkflowsWithPR (processedWorkflows , number , verbose , engineOverride , name , force , appendText , noGitattributes , hasWildcard )
241+ return addWorkflowsWithPR (processedWorkflows , number , verbose , engineOverride , name , force , appendText , noGitattributes , hasWildcard , workflowDir )
236242 }
237243
238244 // Handle normal workflow addition
239245 addLog .Print ("Adding workflows normally without PR" )
240- return addWorkflowsNormal (processedWorkflows , number , verbose , engineOverride , name , force , appendText , noGitattributes , hasWildcard )
246+ return addWorkflowsNormal (processedWorkflows , number , verbose , engineOverride , name , force , appendText , noGitattributes , hasWildcard , workflowDir )
241247}
242248
243249// handleRepoOnlySpec handles the case when user provides only owner/repo without workflow name
@@ -338,7 +344,7 @@ func displayAvailableWorkflows(repoSlug, version string, verbose bool) error {
338344}
339345
340346// addWorkflowsNormal handles normal workflow addition without PR creation
341- func addWorkflowsNormal (workflows []* WorkflowSpec , number int , verbose bool , engineOverride string , name string , force bool , appendText string , noGitattributes bool , fromWildcard bool ) error {
347+ func addWorkflowsNormal (workflows []* WorkflowSpec , number int , verbose bool , engineOverride string , name string , force bool , appendText string , noGitattributes bool , fromWildcard bool , workflowDir string ) error {
342348 // Create file tracker for all operations
343349 tracker , err := NewFileTracker ()
344350 if err != nil {
@@ -379,7 +385,7 @@ func addWorkflowsNormal(workflows []*WorkflowSpec, number int, verbose bool, eng
379385 currentName = name
380386 }
381387
382- if err := addWorkflowWithTracking (workflow , number , verbose , engineOverride , currentName , force , appendText , tracker , fromWildcard ); err != nil {
388+ if err := addWorkflowWithTracking (workflow , number , verbose , engineOverride , currentName , force , appendText , tracker , fromWildcard , workflowDir ); err != nil {
383389 return fmt .Errorf ("failed to add workflow '%s': %w" , workflow .String (), err )
384390 }
385391 }
@@ -392,7 +398,7 @@ func addWorkflowsNormal(workflows []*WorkflowSpec, number int, verbose bool, eng
392398}
393399
394400// addWorkflowsWithPR handles workflow addition with PR creation
395- func addWorkflowsWithPR (workflows []* WorkflowSpec , number int , verbose bool , engineOverride string , name string , force bool , appendText string , noGitattributes bool , fromWildcard bool ) error {
401+ func addWorkflowsWithPR (workflows []* WorkflowSpec , number int , verbose bool , engineOverride string , name string , force bool , appendText string , noGitattributes bool , fromWildcard bool , workflowDir string ) error {
396402 // Get current branch for restoration later
397403 currentBranch , err := getCurrentBranch ()
398404 if err != nil {
@@ -421,7 +427,7 @@ func addWorkflowsWithPR(workflows []*WorkflowSpec, number int, verbose bool, eng
421427 }()
422428
423429 // Add workflows using the normal function logic
424- if err := addWorkflowsNormal (workflows , number , verbose , engineOverride , name , force , appendText , noGitattributes , fromWildcard ); err != nil {
430+ if err := addWorkflowsNormal (workflows , number , verbose , engineOverride , name , force , appendText , noGitattributes , fromWildcard , workflowDir ); err != nil {
425431 // Rollback on error
426432 if rollbackErr := tracker .RollbackAllFiles (verbose ); rollbackErr != nil && verbose {
427433 fmt .Fprintln (os .Stderr , console .FormatWarningMessage (fmt .Sprintf ("Failed to rollback files: %v" , rollbackErr )))
@@ -500,7 +506,7 @@ func addWorkflowsWithPR(workflows []*WorkflowSpec, number int, verbose bool, eng
500506}
501507
502508// addWorkflowWithTracking adds a workflow from components to .github/workflows with file tracking
503- func addWorkflowWithTracking (workflow * WorkflowSpec , number int , verbose bool , engineOverride string , name string , force bool , appendText string , tracker * FileTracker , fromWildcard bool ) error {
509+ func addWorkflowWithTracking (workflow * WorkflowSpec , number int , verbose bool , engineOverride string , name string , force bool , appendText string , tracker * FileTracker , fromWildcard bool , workflowDir string ) error {
504510 if verbose {
505511 fmt .Fprintln (os .Stderr , console .FormatInfoMessage (fmt .Sprintf ("Adding workflow: %s" , workflow .String ())))
506512 fmt .Fprintln (os .Stderr , console .FormatInfoMessage (fmt .Sprintf ("Number of copies: %d" , number )))
@@ -555,10 +561,30 @@ func addWorkflowWithTracking(workflow *WorkflowSpec, number int, verbose bool, e
555561 return fmt .Errorf ("add workflow requires being in a git repository: %w" , err )
556562 }
557563
558- // Ensure .github/workflows directory exists relative to git root
559- githubWorkflowsDir := filepath .Join (gitRoot , ".github/workflows" )
564+ // Determine the target workflow directory
565+ var githubWorkflowsDir string
566+ if workflowDir != "" {
567+ // Validate that the path is relative
568+ if filepath .IsAbs (workflowDir ) {
569+ return fmt .Errorf ("workflow directory must be a relative path, got: %s" , workflowDir )
570+ }
571+ // Clean the path to avoid issues with ".." or other problematic elements
572+ workflowDir = filepath .Clean (workflowDir )
573+ // Ensure the path is under .github/workflows
574+ if ! strings .HasPrefix (workflowDir , ".github/workflows" ) {
575+ // If user provided a subdirectory name, prepend .github/workflows/
576+ githubWorkflowsDir = filepath .Join (gitRoot , ".github/workflows" , workflowDir )
577+ } else {
578+ githubWorkflowsDir = filepath .Join (gitRoot , workflowDir )
579+ }
580+ } else {
581+ // Use default .github/workflows directory
582+ githubWorkflowsDir = filepath .Join (gitRoot , ".github/workflows" )
583+ }
584+
585+ // Ensure the target directory exists
560586 if err := os .MkdirAll (githubWorkflowsDir , 0755 ); err != nil {
561- return fmt .Errorf ("failed to create .github/workflows directory: %w" , err )
587+ return fmt .Errorf ("failed to create workflow directory %s : %w" , githubWorkflowsDir , err )
562588 }
563589
564590 // Determine the workflowName to use
0 commit comments