@@ -11,13 +11,17 @@ use serde::Deserialize;
1111/// Options for listing branches in a repository
1212#[ derive( Debug , clap:: Args , Deserialize , Clone ) ]
1313pub struct ListBranchesOptions {
14- /// Workspace slug (e.g., "myworkspace")
14+ /// Repository in workspace/repo_slug format (e.g., "myworkspace/myrepo")
15+ #[ arg( index = 1 ) ]
16+ pub repo_path : Option < String > ,
17+
18+ /// Workspace slug (e.g., "myworkspace") — overridden by positional arg
1519 #[ arg( long, short = 'w' ) ]
16- pub workspace : String ,
20+ pub workspace : Option < String > ,
1721
18- /// Repository slug (e.g., "myrepo")
22+ /// Repository slug (e.g., "myrepo") — overridden by positional arg
1923 #[ arg( long, short = 'r' ) ]
20- pub repo : String ,
24+ pub repo : Option < String > ,
2125
2226 /// Maximum number of results to return per page
2327 #[ arg( short, long, default_value = "10" ) ]
@@ -69,6 +73,29 @@ pub struct ListBranchesParams {
6973 pub app_password_override : Option < String > ,
7074}
7175
76+ /// Resolve workspace and repo from options.
77+ ///
78+ /// Accepts either a positional `workspace/repo_slug` argument or separate `--workspace` and `--repo` flags.
79+ fn resolve_workspace_repo ( options : & ListBranchesOptions ) -> Result < ( String , String ) > {
80+ if let Some ( ref path) = options. repo_path {
81+ let parts: Vec < & str > = path. splitn ( 2 , '/' ) . collect ( ) ;
82+ if parts. len ( ) != 2 || parts[ 0 ] . is_empty ( ) || parts[ 1 ] . is_empty ( ) {
83+ return Err ( eyre ! (
84+ "Invalid repo path '{}'. Expected format: workspace/repo_slug" ,
85+ path
86+ ) ) ;
87+ }
88+ Ok ( ( parts[ 0 ] . to_string ( ) , parts[ 1 ] . to_string ( ) ) )
89+ } else {
90+ match ( & options. workspace , & options. repo ) {
91+ ( Some ( ws) , Some ( repo) ) => Ok ( ( ws. clone ( ) , repo. clone ( ) ) ) ,
92+ _ => Err ( eyre ! (
93+ "Either provide a positional 'workspace/repo_slug' argument or both --workspace and --repo flags"
94+ ) ) ,
95+ }
96+ }
97+ }
98+
7299/// Fetch branch list from Bitbucket API
73100pub async fn list_branches_data (
74101 params : ListBranchesParams ,
@@ -138,6 +165,8 @@ pub async fn list_branches_data(
138165}
139166
140167pub async fn handler ( options : ListBranchesOptions , global : crate :: Global ) -> Result < ( ) > {
168+ let ( workspace, repo) = resolve_workspace_repo ( & options) ?;
169+
141170 let spinner = ProgressBar :: new_spinner ( ) ;
142171 spinner. set_style (
143172 ProgressStyle :: default_spinner ( )
@@ -161,8 +190,8 @@ pub async fn handler(options: ListBranchesOptions, global: crate::Global) -> Res
161190 }
162191
163192 let params = ListBranchesParams {
164- workspace : options . workspace . clone ( ) ,
165- repo : options . repo . clone ( ) ,
193+ workspace : workspace. clone ( ) ,
194+ repo : repo. clone ( ) ,
166195 limit : 100 ,
167196 next_page,
168197 query : options. query . clone ( ) ,
@@ -197,8 +226,8 @@ pub async fn handler(options: ListBranchesOptions, global: crate::Global) -> Res
197226 }
198227 } else {
199228 let params = ListBranchesParams {
200- workspace : options . workspace . clone ( ) ,
201- repo : options . repo . clone ( ) ,
229+ workspace : workspace. clone ( ) ,
230+ repo : repo. clone ( ) ,
202231 limit : options. limit ,
203232 next_page : options. next_page ,
204233 query : options. query ,
@@ -301,8 +330,8 @@ pub async fn handler(options: ListBranchesOptions, global: crate::Global) -> Res
301330 "More results available. To fetch the next page, run:" . cyan( )
302331 ) ;
303332 eprintln ! (
304- " mcptools atlassian bitbucket repo branches -w {} -r {} --limit {} --next-page '{}'" ,
305- options . workspace, options . repo, options. limit, next_url
333+ " mcptools atlassian bitbucket repo branches {}/ {} --limit {} --next-page '{}'" ,
334+ workspace, repo, options. limit, next_url
306335 ) ;
307336 }
308337 }
0 commit comments