Initialize session with the Claude client. Creates a new conversation if none exists, then sends an initial greeting. For resumed conversations, skips the greeting since history is loaded from DB. Yields message chunks as they stream in.
(self)
| 190 | self.client = None |
| 191 | |
| 192 | async def start(self) -> AsyncGenerator[dict, None]: |
| 193 | """ |
| 194 | Initialize session with the Claude client. |
| 195 | |
| 196 | Creates a new conversation if none exists, then sends an initial greeting. |
| 197 | For resumed conversations, skips the greeting since history is loaded from DB. |
| 198 | Yields message chunks as they stream in. |
| 199 | """ |
| 200 | # Track if this is a new conversation (for greeting decision) |
| 201 | is_new_conversation = self.conversation_id is None |
| 202 | |
| 203 | # Create a new conversation if we don't have one |
| 204 | if is_new_conversation: |
| 205 | conv = create_conversation(self.project_dir, self.project_name) |
| 206 | self.conversation_id = int(conv.id) # type coercion: Column[int] -> int |
| 207 | yield {"type": "conversation_created", "conversation_id": self.conversation_id} |
| 208 | |
| 209 | # Build permissions list for assistant access (read + feature management) |
| 210 | permissions_list = [ |
| 211 | "Read(./**)", |
| 212 | "Glob(./**)", |
| 213 | "Grep(./**)", |
| 214 | "WebFetch", |
| 215 | "WebSearch", |
| 216 | *ASSISTANT_FEATURE_TOOLS, |
| 217 | ] |
| 218 | |
| 219 | # Create security settings file |
| 220 | security_settings = { |
| 221 | "sandbox": {"enabled": False}, # No bash, so sandbox not needed |
| 222 | "permissions": { |
| 223 | "defaultMode": "bypassPermissions", # Read-only, no dangerous ops |
| 224 | "allow": permissions_list, |
| 225 | }, |
| 226 | } |
| 227 | from autoforge_paths import get_claude_assistant_settings_path |
| 228 | settings_file = get_claude_assistant_settings_path(self.project_dir) |
| 229 | settings_file.parent.mkdir(parents=True, exist_ok=True) |
| 230 | with open(settings_file, "w") as f: |
| 231 | json.dump(security_settings, f, indent=2) |
| 232 | |
| 233 | # Build MCP servers config - only features MCP for read-only access |
| 234 | mcp_servers = { |
| 235 | "features": { |
| 236 | "command": sys.executable, |
| 237 | "args": ["-m", "mcp_server.feature_mcp"], |
| 238 | "env": { |
| 239 | # Only specify variables the MCP server needs |
| 240 | # (subprocess inherits parent environment automatically) |
| 241 | "PROJECT_DIR": str(self.project_dir.resolve()), |
| 242 | "PYTHONPATH": str(ROOT_DIR.resolve()), |
| 243 | }, |
| 244 | }, |
| 245 | } |
| 246 | |
| 247 | # Get system prompt with project context |
| 248 | system_prompt = get_system_prompt(self.project_name, self.project_dir) |
| 249 |
nothing calls this directly
no test coverage detected