99from collections .abc import Mapping
1010import dataclasses
1111import logging
12+ import re
1213from typing import Any
1314
1415import claude_code_sdk as sdk
@@ -25,9 +26,14 @@ def new_bot() -> Bot:
2526
2627
2728_PROMPT_SUFFIX = reindent ("""
28- ALWAYS use the feedback's MCP server ask_user tool if you need to request
29- any information from the user. NEVER repeat yourself by also asking your
30- question to the user in other ways.
29+ ALWAYS use the `ask_user` tool if you need to request any information from
30+ the user. NEVER repeat yourself by also asking your question to the user in
31+ other ways.
32+
33+ When you are done performing all the changes the user asked for, ALWAYS use
34+ the `summarize_change` tool to describe what you have done. This should be
35+ the last action you perform. NEVER stop responding to the user without
36+ calling this tool first.
3137""" )
3238
3339
@@ -47,7 +53,9 @@ async def act(
4753 options = dataclasses .replace (
4854 self ._options ,
4955 cwd = tree_path ,
50- mcp_servers = {"feedback" : _feedback_mcp_server (feedback )},
56+ mcp_servers = {
57+ "feedback" : _feedback_mcp_server (feedback , summary ),
58+ },
5159 )
5260 async with sdk .ClaudeSDKClient (options ) as client :
5361 await client .query (goal .prompt )
@@ -81,17 +89,21 @@ def _token_count(usage: Mapping[str, Any]) -> int:
8189 )
8290
8391
92+ _text_suffix = re .compile (":$" )
93+
94+
8495def _notify (
8596 feedback : UserFeedback , content : str | list [sdk .ContentBlock ]
8697) -> None :
8798 if isinstance (content , str ):
8899 feedback .notify (content )
89100 return
90-
91101 for block in content :
92102 match block :
93103 case sdk .TextBlock (text ):
94- feedback .notify (text )
104+ # Text blocks end with a colon by default (likely to look
105+ # better in the default CLI), which looks off here.
106+ feedback .notify (_text_suffix .sub ("." , text ))
95107 case sdk .ThinkingBlock (thinking , signature ):
96108 feedback .notify (thinking )
97109 feedback .notify (signature )
@@ -101,10 +113,32 @@ def _notify(
101113 raise UnreachableError ()
102114
103115
104- def _feedback_mcp_server (feedback : UserFeedback ) -> sdk .McpServerConfig :
116+ def _feedback_mcp_server (
117+ feedback : UserFeedback ,
118+ summary : ActionSummary ,
119+ ) -> sdk .McpServerConfig :
105120 @sdk .tool ("ask_user" , "Request feedback from the user" , {"question" : str })
106121 async def ask_user (args : Any ) -> Any :
107122 question = args ["question" ]
108123 return {"content" : [{"type" : "text" , "text" : feedback .ask (question )}]}
109124
110- return sdk .create_sdk_mcp_server (name = "feedback" , tools = [ask_user ])
125+ @sdk .tool (
126+ "summarize_change" ,
127+ reindent ("""
128+ Describe work performed in a format suitable for a git commit
129+
130+ The message should include a short title that fits on a single line
131+ of 55 characters. This title should be followed by one or more
132+ paragraphs which describe the change done. Avoid repeating
133+ implementation details here, focus instead on non-obvious choices
134+ that were made.
135+ """ ),
136+ {"commit_message" : str },
137+ )
138+ async def summarize_change (args : Any ) -> Any :
139+ summary .message = args ["commit_message" ]
140+ return {"content" : [{"type" : "text" , "text" : "Great, thank you." }]}
141+
142+ return sdk .create_sdk_mcp_server (
143+ name = "feedback" , tools = [ask_user , summarize_change ]
144+ )
0 commit comments