Initialize session and get initial greeting from Claude. Yields message chunks as they stream in.
(self)
| 69 | self.client = None |
| 70 | |
| 71 | async def start(self) -> AsyncGenerator[dict, None]: |
| 72 | """ |
| 73 | Initialize session and get initial greeting from Claude. |
| 74 | |
| 75 | Yields message chunks as they stream in. |
| 76 | """ |
| 77 | # Load the create-spec skill |
| 78 | skill_path = ROOT_DIR / ".claude" / "commands" / "create-spec.md" |
| 79 | |
| 80 | if not skill_path.exists(): |
| 81 | yield { |
| 82 | "type": "error", |
| 83 | "content": f"Spec creation skill not found at {skill_path}" |
| 84 | } |
| 85 | return |
| 86 | |
| 87 | try: |
| 88 | skill_content = skill_path.read_text(encoding="utf-8") |
| 89 | except UnicodeDecodeError: |
| 90 | skill_content = skill_path.read_text(encoding="utf-8", errors="replace") |
| 91 | |
| 92 | # Ensure project directory exists (like CLI does in start.py) |
| 93 | self.project_dir.mkdir(parents=True, exist_ok=True) |
| 94 | |
| 95 | # Delete app_spec.txt so Claude can create it fresh |
| 96 | # The SDK requires reading existing files before writing, but app_spec.txt is created new |
| 97 | # Note: We keep initializer_prompt.md so Claude can read and update the template |
| 98 | from autoforge_paths import get_prompts_dir |
| 99 | prompts_dir = get_prompts_dir(self.project_dir) |
| 100 | app_spec_path = prompts_dir / "app_spec.txt" |
| 101 | if app_spec_path.exists(): |
| 102 | app_spec_path.unlink() |
| 103 | logger.info("Deleted scaffolded app_spec.txt for fresh spec creation") |
| 104 | |
| 105 | # Create security settings file (like client.py does) |
| 106 | # This grants permissions for file operations in the project directory |
| 107 | security_settings = { |
| 108 | "sandbox": {"enabled": False}, # Disable sandbox for spec creation |
| 109 | "permissions": { |
| 110 | "defaultMode": "acceptEdits", |
| 111 | "allow": [ |
| 112 | "Read(./**)", |
| 113 | "Write(./**)", |
| 114 | "Edit(./**)", |
| 115 | "Glob(./**)", |
| 116 | ], |
| 117 | }, |
| 118 | } |
| 119 | from autoforge_paths import get_claude_settings_path |
| 120 | settings_file = get_claude_settings_path(self.project_dir) |
| 121 | settings_file.parent.mkdir(parents=True, exist_ok=True) |
| 122 | with open(settings_file, "w") as f: |
| 123 | json.dump(security_settings, f, indent=2) |
| 124 | |
| 125 | # Replace $ARGUMENTS with absolute project path (like CLI does in start.py:184) |
| 126 | # Using absolute path avoids confusion when project folder name differs from app name |
| 127 | project_path = str(self.project_dir.resolve()) |
| 128 | system_prompt = skill_content.replace("$ARGUMENTS", project_path) |
nothing calls this directly
no test coverage detected