wait until both app_spec and initializer prompt files updated

This commit is contained in:
Auto
2025-12-30 17:49:21 +02:00
parent cb65cfe151
commit d6c736261b

View File

@@ -100,8 +100,10 @@ class SpecChatSession:
allowed_tools=[ allowed_tools=[
"Read", "Read",
"Write", "Write",
"Edit",
"Glob", "Glob",
], ],
permission_mode="acceptEdits", # Auto-approve file writes for spec creation
max_turns=100, max_turns=100,
cwd=str(ROOT_DIR.resolve()), cwd=str(ROOT_DIR.resolve()),
) )
@@ -175,6 +177,12 @@ class SpecChatSession:
Internal method to query Claude and stream responses. Internal method to query Claude and stream responses.
Handles tool calls (Write) and text responses. Handles tool calls (Write) and text responses.
IMPORTANT: Spec creation requires BOTH files to be written:
1. app_spec.txt - the main specification
2. initializer_prompt.md - tells the agent how many features to create
We only signal spec_complete when BOTH files are verified on disk.
""" """
if not self.client: if not self.client:
return return
@@ -183,7 +191,21 @@ class SpecChatSession:
await self.client.query(message) await self.client.query(message)
current_text = "" current_text = ""
pending_spec_write = None # Track if we're waiting for app_spec.txt write result
# Track pending writes for BOTH required files
pending_writes = {
"app_spec": None, # {"tool_id": ..., "path": ...}
"initializer": None, # {"tool_id": ..., "path": ...}
}
# Track which files have been successfully written
files_written = {
"app_spec": False,
"initializer": False,
}
# Store paths for the completion message
spec_path = None
# Stream the response using receive_response # Stream the response using receive_response
async for msg in self.client.receive_response(): async for msg in self.client.receive_response():
@@ -213,23 +235,25 @@ class SpecChatSession:
tool_input = getattr(block, "input", {}) tool_input = getattr(block, "input", {})
tool_id = getattr(block, "id", "") tool_id = getattr(block, "id", "")
if tool_name == "Write": if tool_name in ("Write", "Edit"):
# File being written - track for verification # File being written or edited - track for verification
file_path = tool_input.get("file_path", "") file_path = tool_input.get("file_path", "")
# Track if this is the app_spec.txt file # Track app_spec.txt
if "app_spec.txt" in str(file_path): if "app_spec.txt" in str(file_path):
pending_spec_write = { pending_writes["app_spec"] = {
"tool_id": tool_id, "tool_id": tool_id,
"path": file_path "path": file_path
} }
logger.info(f"Write tool called for app_spec.txt: {file_path}") logger.info(f"{tool_name} tool called for app_spec.txt: {file_path}")
# Track initializer_prompt.md
elif "initializer_prompt.md" in str(file_path): elif "initializer_prompt.md" in str(file_path):
yield { pending_writes["initializer"] = {
"type": "file_written", "tool_id": tool_id,
"path": str(file_path) "path": file_path
} }
logger.info(f"{tool_name} tool called for initializer_prompt.md: {file_path}")
elif msg_type == "UserMessage" and hasattr(msg, "content"): elif msg_type == "UserMessage" and hasattr(msg, "content"):
# Tool results - check for write confirmations and errors # Tool results - check for write confirmations and errors
@@ -242,26 +266,57 @@ class SpecChatSession:
if is_error: if is_error:
content = getattr(block, "content", "Unknown error") content = getattr(block, "content", "Unknown error")
logger.warning(f"Tool error: {content}") logger.warning(f"Tool error: {content}")
# If the spec write failed, clear the pending write # Clear any pending writes that failed
if pending_spec_write and tool_use_id == pending_spec_write.get("tool_id"): for key in pending_writes:
logger.error(f"app_spec.txt write failed: {content}") if pending_writes[key] and tool_use_id == pending_writes[key].get("tool_id"):
pending_spec_write = None logger.error(f"{key} write failed: {content}")
pending_writes[key] = None
else: else:
# Tool succeeded - check if it was the spec write # Tool succeeded - check which file was written
if pending_spec_write and tool_use_id == pending_spec_write.get("tool_id"):
spec_path = pending_spec_write["path"] # Check app_spec.txt
# Verify the file actually exists if pending_writes["app_spec"] and tool_use_id == pending_writes["app_spec"].get("tool_id"):
full_path = ROOT_DIR / spec_path file_path = pending_writes["app_spec"]["path"]
full_path = ROOT_DIR / file_path
if full_path.exists(): if full_path.exists():
logger.info(f"app_spec.txt verified at: {full_path}") logger.info(f"app_spec.txt verified at: {full_path}")
self.complete = True files_written["app_spec"] = True
spec_path = file_path
# Notify about file write (but NOT completion yet)
yield { yield {
"type": "spec_complete", "type": "file_written",
"path": str(spec_path) "path": str(file_path)
} }
else: else:
logger.error(f"app_spec.txt not found after write: {full_path}") logger.error(f"app_spec.txt not found after write: {full_path}")
pending_spec_write = None pending_writes["app_spec"] = None
# Check initializer_prompt.md
if pending_writes["initializer"] and tool_use_id == pending_writes["initializer"].get("tool_id"):
file_path = pending_writes["initializer"]["path"]
full_path = ROOT_DIR / file_path
if full_path.exists():
logger.info(f"initializer_prompt.md verified at: {full_path}")
files_written["initializer"] = True
# Notify about file write
yield {
"type": "file_written",
"path": str(file_path)
}
else:
logger.error(f"initializer_prompt.md not found after write: {full_path}")
pending_writes["initializer"] = None
# Check if BOTH files are now written - only then signal completion
if files_written["app_spec"] and files_written["initializer"]:
logger.info("Both app_spec.txt and initializer_prompt.md verified - signaling completion")
self.complete = True
yield {
"type": "spec_complete",
"path": str(spec_path)
}
def is_complete(self) -> bool: def is_complete(self) -> bool:
"""Check if spec creation is complete.""" """Check if spec creation is complete."""