@@ -61,7 +61,11 @@ public static void Register(RootCommand root)
6161 ( services , directorContext ) => ConfigureHostedAgentServices ( services , directorContext , settings ) ) ;
6262 application = new DirectorAgentConsoleApplication (
6363 serviceProvider . GetRequiredService < IMcpHostedAgentFactory > ( ) . CreateHostedAgent ( ) ,
64- settings ) ;
64+ settings ,
65+ serviceProvider . GetRequiredService < ReplWorkflowToolAdapter > ( ) ,
66+ serviceProvider . GetRequiredService < McpServer . Repl . Core . ITodoWorkflow > ( ) ,
67+ serviceProvider . GetRequiredService < McpServer . Repl . Core . IRequirementsWorkflow > ( ) ,
68+ serviceProvider . GetRequiredService < McpServer . Repl . Core . IGenericClientPassthrough > ( ) ) ;
6569 using ( application )
6670 {
6771 var args = prompt . Length == 0
@@ -118,6 +122,22 @@ private static void ConfigureHostedAgentServices(
118122 options . Description = settings . AgentDescription ;
119123 options . SourceType = settings . SourceType ;
120124 } ) ;
125+
126+ // Register REPL workflow services for richer TODO, requirements, and client passthrough tools.
127+ services . AddSingleton < McpServer . Repl . Core . ISessionLogWorkflow > ( sp =>
128+ new McpServer . Repl . Core . SessionLogWorkflow (
129+ sp . GetRequiredService < McpServer . Client . McpServerClient > ( ) . SessionLog ,
130+ TimeProvider . System ) ) ;
131+ services . AddSingleton < McpServer . Repl . Core . ITodoWorkflow > ( sp =>
132+ new McpServer . Repl . Core . TodoWorkflow (
133+ sp . GetRequiredService < McpServer . Client . McpServerClient > ( ) . Todo ) ) ;
134+ services . AddSingleton < McpServer . Repl . Core . IRequirementsWorkflow > ( sp =>
135+ new McpServer . Repl . Core . RequirementsWorkflow (
136+ sp . GetRequiredService < McpServer . Client . McpServerClient > ( ) . Requirements ) ) ;
137+ services . AddSingleton < McpServer . Repl . Core . IGenericClientPassthrough > ( sp =>
138+ new McpServer . Repl . Core . GenericClientPassthrough (
139+ sp . GetRequiredService < McpServer . Client . McpServerClient > ( ) ) ) ;
140+ services . AddSingleton < ReplWorkflowToolAdapter > ( ) ;
121141 }
122142}
123143
@@ -129,6 +149,9 @@ internal sealed class DirectorAgentConsoleApplication : IDisposable
129149 private readonly ChatClientAgent _chatAgent ;
130150 private readonly object _powerShellCommandSync = new ( ) ;
131151 private readonly ChatClientAgentRunOptions _runOptions ;
152+ private readonly McpServer . Repl . Core . ITodoWorkflow _replTodo ;
153+ private readonly McpServer . Repl . Core . IRequirementsWorkflow _replRequirements ;
154+ private readonly McpServer . Repl . Core . IGenericClientPassthrough _replPassthrough ;
132155 private CancellationTokenSource ? _activePowerShellCommandCancellationSource ;
133156 private AgentSession ? _agentSession ;
134157 private string ? _powerShellSessionId ;
@@ -139,13 +162,29 @@ internal sealed class DirectorAgentConsoleApplication : IDisposable
139162 private int _historyBrowseIndex = - 1 ;
140163 private string _historyScratchLine = "" ;
141164
142- public DirectorAgentConsoleApplication ( IMcpHostedAgent hostedAgent , DirectorAgentSettings settings )
165+ public DirectorAgentConsoleApplication (
166+ IMcpHostedAgent hostedAgent ,
167+ DirectorAgentSettings settings ,
168+ ReplWorkflowToolAdapter replAdapter ,
169+ McpServer . Repl . Core . ITodoWorkflow replTodo ,
170+ McpServer . Repl . Core . IRequirementsWorkflow replRequirements ,
171+ McpServer . Repl . Core . IGenericClientPassthrough replPassthrough )
143172 {
144173 _hostedAgent = hostedAgent ?? throw new ArgumentNullException ( nameof ( hostedAgent ) ) ;
145174 _settings = settings ?? throw new ArgumentNullException ( nameof ( settings ) ) ;
175+ _replTodo = replTodo ?? throw new ArgumentNullException ( nameof ( replTodo ) ) ;
176+ _replRequirements = replRequirements ?? throw new ArgumentNullException ( nameof ( replRequirements ) ) ;
177+ _replPassthrough = replPassthrough ?? throw new ArgumentNullException ( nameof ( replPassthrough ) ) ;
146178 _chatClient = CreateChatClient ( settings ) ;
147179 _chatAgent = hostedAgent . CreateChatClientAgent ( _chatClient ) ;
148180 _runOptions = hostedAgent . CreateRunOptions ( ) ;
181+
182+ // Merge REPL workflow tools into the agent's tool set
183+ var chatOptions = _runOptions . ChatOptions ??= new Microsoft . Extensions . AI . ChatOptions ( ) ;
184+ chatOptions . Tools ??= new List < AITool > ( ) ;
185+ foreach ( var tool in replAdapter . CreateTools ( ) )
186+ chatOptions . Tools . Add ( tool ) ;
187+
149188 _powerShellCurrentLocation = settings . WorkspacePath ;
150189 _verbosity = settings . Verbosity ;
151190 LoadConsoleStateFromDisk ( ) ;
@@ -326,6 +365,16 @@ private bool TryHandleCommand(
326365 return Task . CompletedTask ;
327366 } ;
328367 return true ;
368+ case "/todo" :
369+ commandAction = ct => HandleTodoCommandAsync ( input , ct ) ;
370+ return true ;
371+ case "/requirements" :
372+ case "/reqs" :
373+ commandAction = ct => HandleRequirementsCommandAsync ( input , ct ) ;
374+ return true ;
375+ case "/client" :
376+ commandAction = ct => HandleClientCommandAsync ( input , ct ) ;
377+ return true ;
329378 default :
330379 commandAction = _ =>
331380 {
@@ -1028,12 +1077,21 @@ private void WriteBanner()
10281077 private void WriteHelp ( )
10291078 {
10301079 Console . WriteLine ( "Commands:" ) ;
1031- Console . WriteLine ( " /help Show this help text." ) ;
1032- Console . WriteLine ( " /tools List the MCP-backed tools attached to the hosted agent." ) ;
1033- Console . WriteLine ( " /session Show the current MCP session-log identifier." ) ;
1034- Console . WriteLine ( " /v N Set verbosity level (1=concise, 2=balanced, 3=detailed)." ) ;
1035- Console . WriteLine ( " /new Start a fresh conversation and session log." ) ;
1036- Console . WriteLine ( " /exit Exit the Director agent host." ) ;
1080+ Console . WriteLine ( " /help Show this help text." ) ;
1081+ Console . WriteLine ( " /tools List the MCP-backed tools attached to the hosted agent." ) ;
1082+ Console . WriteLine ( " /session Show the current MCP session-log identifier." ) ;
1083+ Console . WriteLine ( " /v N Set verbosity level (1=concise, 2=balanced, 3=detailed)." ) ;
1084+ Console . WriteLine ( " /new Start a fresh conversation and session log." ) ;
1085+ Console . WriteLine ( " /exit Exit the Director agent host." ) ;
1086+ Console . WriteLine ( ) ;
1087+ Console . WriteLine ( "REPL workflow commands:" ) ;
1088+ Console . WriteLine ( " /todo List all TODO items." ) ;
1089+ Console . WriteLine ( " /todo <keyword> Search TODOs by keyword." ) ;
1090+ Console . WriteLine ( " /todo select <id> Select a TODO as the active context." ) ;
1091+ Console . WriteLine ( " /todo get <id> Show TODO details." ) ;
1092+ Console . WriteLine ( " /requirements List functional requirements summary." ) ;
1093+ Console . WriteLine ( " /reqs Alias for /requirements." ) ;
1094+ Console . WriteLine ( " /client <c>.<m> Invoke McpServerClient sub-client method (e.g. /client context.SearchAsync)." ) ;
10371095 Console . WriteLine ( ) ;
10381096 Console . WriteLine ( "Prompt behavior:" ) ;
10391097 Console . WriteLine ( " - The prompt shows model [verbosity] <location>> (model id from MCP_AGENT_MODEL_NAME or default)." ) ;
@@ -1052,6 +1110,91 @@ private void WriteToolList()
10521110 Console . WriteLine ( "Attached MCP tools:" ) ;
10531111 foreach ( var tool in _hostedAgent . Registration . Tools )
10541112 Console . WriteLine ( $ " - { tool . Name } ") ;
1113+
1114+ var replTools = _runOptions . ChatOptions ? . Tools ?
1115+ . Where ( t => t . Name . StartsWith ( "repl_" , StringComparison . Ordinal ) )
1116+ . ToList ( ) ;
1117+ if ( replTools is { Count : > 0 } )
1118+ {
1119+ Console . WriteLine ( ) ;
1120+ Console . WriteLine ( "REPL workflow tools:" ) ;
1121+ foreach ( var tool in replTools )
1122+ Console . WriteLine ( $ " - { tool . Name } ") ;
1123+ }
1124+
1125+ Console . WriteLine ( ) ;
1126+ }
1127+
1128+ private async Task HandleTodoCommandAsync ( string input , CancellationToken cancellationToken )
1129+ {
1130+ var parts = input . Split ( ' ' , 3 , StringSplitOptions . RemoveEmptyEntries | StringSplitOptions . TrimEntries ) ;
1131+ var subCommand = parts . Length > 1 ? parts [ 1 ] : null ;
1132+
1133+ if ( string . Equals ( subCommand , "select" , StringComparison . OrdinalIgnoreCase ) && parts . Length > 2 )
1134+ {
1135+ await _replTodo . SelectAsync ( parts [ 2 ] , cancellationToken ) . ConfigureAwait ( false ) ;
1136+ var sel = _replTodo . CurrentSelection ( ) ;
1137+ Console . WriteLine ( $ "Selected: { sel ? . Id } — { sel ? . Title } [{ sel ? . Priority } ]") ;
1138+ Console . WriteLine ( ) ;
1139+ return ;
1140+ }
1141+
1142+ if ( string . Equals ( subCommand , "get" , StringComparison . OrdinalIgnoreCase ) && parts . Length > 2 )
1143+ {
1144+ var item = await _replTodo . GetAsync ( parts [ 2 ] , cancellationToken ) . ConfigureAwait ( false ) ;
1145+ Console . WriteLine ( $ "{ item . Id } { item . Title } ") ;
1146+ Console . WriteLine ( $ " Section: { item . Section } Priority: { item . Priority } Done: { item . Done } ") ;
1147+ if ( ! string . IsNullOrWhiteSpace ( item . Estimate ) ) Console . WriteLine ( $ " Estimate: { item . Estimate } ") ;
1148+ if ( item . Description . Count > 0 ) Console . WriteLine ( $ " Description: { string . Join ( " " , item . Description ) } ") ;
1149+ Console . WriteLine ( ) ;
1150+ return ;
1151+ }
1152+
1153+ // Default: query with optional keyword
1154+ var keyword = parts . Length > 1 ? string . Join ( ' ' , parts . Skip ( 1 ) ) : null ;
1155+ var result = await _replTodo . QueryAsync ( keyword : keyword , cancellationToken : cancellationToken ) . ConfigureAwait ( false ) ;
1156+ Console . WriteLine ( $ "TODOs ({ result . TotalCount } total):") ;
1157+ foreach ( var todo in result . Items )
1158+ {
1159+ var done = todo . Done ? "[x]" : "[ ]" ;
1160+ Console . WriteLine ( $ " { done } { todo . Id , - 25 } { todo . Priority , - 8 } { todo . Title } ") ;
1161+ }
1162+ Console . WriteLine ( ) ;
1163+ }
1164+
1165+ private async Task HandleRequirementsCommandAsync ( string input , CancellationToken cancellationToken )
1166+ {
1167+ var result = await _replRequirements . ListFrAsync ( cancellationToken : cancellationToken ) . ConfigureAwait ( false ) ;
1168+ Console . WriteLine ( $ "Functional Requirements ({ result . TotalCount } total):") ;
1169+ foreach ( var fr in result . Items )
1170+ Console . WriteLine ( $ " { fr . Id , - 20 } { fr . Status , - 12 } { fr . Title } ") ;
1171+ Console . WriteLine ( ) ;
1172+ }
1173+
1174+ private async Task HandleClientCommandAsync ( string input , CancellationToken cancellationToken )
1175+ {
1176+ // Format: /client <clientName>.<methodName> [json-args]
1177+ var parts = input . Split ( ' ' , 3 , StringSplitOptions . RemoveEmptyEntries | StringSplitOptions . TrimEntries ) ;
1178+ if ( parts . Length < 2 || ! parts [ 1 ] . Contains ( '.' ) )
1179+ {
1180+ Console . Error . WriteLine ( "Usage: /client <clientName>.<methodName> [json-args]" ) ;
1181+ Console . Error . WriteLine ( "Example: /client context.SearchAsync {\" query\" :\" auth\" }" ) ;
1182+ Console . Error . WriteLine ( ) ;
1183+ return ;
1184+ }
1185+
1186+ var dotIndex = parts [ 1 ] . IndexOf ( '.' ) ;
1187+ var clientName = parts [ 1 ] [ ..dotIndex ] ;
1188+ var methodName = parts [ 1 ] [ ( dotIndex + 1 ) ..] ;
1189+ var argsJson = parts . Length > 2 ? parts [ 2 ] : null ;
1190+
1191+ var arguments = string . IsNullOrWhiteSpace ( argsJson )
1192+ ? new Dictionary < string , object ? > ( )
1193+ : JsonSerializer . Deserialize < Dictionary < string , object ? > > ( argsJson ) ?? new Dictionary < string , object ? > ( ) ;
1194+
1195+ var result = await _replPassthrough . InvokeAsync ( clientName , methodName , arguments , cancellationToken ) . ConfigureAwait ( false ) ;
1196+ var json = JsonSerializer . Serialize ( result , new JsonSerializerOptions { WriteIndented = true } ) ;
1197+ Console . WriteLine ( json ) ;
10551198 Console . WriteLine ( ) ;
10561199 }
10571200
0 commit comments