     1→"""
     2→Spec Creation Chat Session
     3→==========================
     4→
     5→Manages interactive spec creation conversation with Claude.
     6→Uses the create-spec.md skill to guide users through app spec creation.
     7→"""
     8→
     9→import json
    10→import logging
    11→import shutil
    12→import threading
    13→from datetime import datetime
    14→from pathlib import Path
    15→from typing import AsyncGenerator, Optional
    16→
    17→from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient
    18→
    19→from ..schemas import ImageAttachment
    20→
    21→logger = logging.getLogger(__name__)
    22→
    23→
    24→async def _make_multimodal_message(content_blocks: list[dict]) -> AsyncGenerator[dict, None]:
    25→    """
    26→    Create an async generator that yields a properly formatted multimodal message.
    27→
    28→    The Claude Agent SDK's query() method accepts either:
    29→    - A string (simple text)
    30→    - An AsyncIterable[dict] (for custom message formats)
    31→
    32→    This function wraps content blocks in the expected message format.
    33→    """
    34→    yield {
    35→        "type": "user",
    36→        "message": {"role": "user", "content": content_blocks},
    37→        "parent_tool_use_id": None,
    38→        "session_id": "default",
    39→    }
    40→
    41→# Root directory of the project
    42→ROOT_DIR = Path(__file__).parent.parent.parent
    43→
    44→
    45→class SpecChatSession:
    46→    """
    47→    Manages a spec creation conversation for one project.
    48→
    49→    For NEW projects, uses create-spec skill to guide users through:
    50→    - Phase 1: Project Overview (name, description, audience)
    51→    - Phase 2: Involvement Level (Quick vs Detailed mode)
    52→    - Phase 3: Technology Preferences
    53→    - Phase 4: Features (main exploration phase)
    54→    - Phase 5: Technical Details (derived or discussed)
    55→    - Phase 6-7: Success Criteria & Approval
    56→
    57→    For IMPORTED projects, uses import-spec skill to:
    58→    - Present codebase analysis
    59→    - Document existing features
    60→    - Identify gaps and plan new features
    61→    """
    62→
    63→    def __init__(
    64→        self,
    65→        project_name: str,
    66→        project_dir: Path,
    67→        is_import: bool = False,
    68→        codebase_analysis: Optional[str] = None,
    69→    ):
    70→        """
    71→        Initialize the session.
    72→
    73→        Args:
    74→            project_name: Name of the project being created
    75→            project_dir: Absolute path to the project directory
    76→            is_import: If True, use import-spec skill instead of create-spec
    77→            codebase_analysis: Formatted codebase analysis for import mode
    78→        """
    79→        self.project_name = project_name
    80→        self.project_dir = project_dir
    81→        self.is_import = is_import
    82→        self.codebase_analysis = codebase_analysis
    83→        self.client: Optional[ClaudeSDKClient] = None
    84→        self.messages: list[dict] = []
    85→        self.complete: bool = False
    86→        self.created_at = datetime.now()
    87→        self._conversation_id: Optional[str] = None
    88→        self._client_entered: bool = False  # Track if context manager is active
    89→
    90→    async def close(self) -> None:
    91→        """Clean up resources and close the Claude client."""
    92→        if self.client and self._client_entered:
    93→            try:
    94→                await self.client.__aexit__(None, None, None)
    95→            except Exception as e:
    96→                logger.warning(f"Error closing Claude client: {e}")
    97→            finally:
    98→                self._client_entered = False
    99→                self.client = None
   100→
   101→    async def start(self) -> AsyncGenerator[dict, None]:
   102→        """
   103→        Initialize session and get initial greeting from Claude.
   104→
   105→        Yields message chunks as they stream in.
   106→        """
   107→        # Load the appropriate skill based on import mode
   108→        if self.is_import:
   109→            skill_path = ROOT_DIR / ".claude" / "commands" / "import-spec.md"
   110→            skill_type = "import"
   111→        else:
   112→            skill_path = ROOT_DIR / ".claude" / "commands" / "create-spec.md"
   113→            skill_type = "create"
   114→
   115→        if not skill_path.exists():
   116→            yield {
   117→                "type": "error",
   118→                "content": f"Spec {skill_type} skill not found at {skill_path}"
   119→            }
   120→            return
   121→
   122→        try:
   123→            skill_content = skill_path.read_text(encoding="utf-8")
   124→        except UnicodeDecodeError:
   125→            skill_content = skill_path.read_text(encoding="utf-8", errors="replace")
   126→
   127→        # Ensure project directory exists (like CLI does in start.py)
   128→        self.project_dir.mkdir(parents=True, exist_ok=True)
   129→
   130→        # Delete app_spec.txt so Claude can create it fresh
   131→        # The SDK requires reading existing files before writing, but app_spec.txt is created new
   132→        # Note: We keep initializer_prompt.md so Claude can read and update the template
   133→        prompts_dir = self.project_dir / "prompts"
   134→        app_spec_path = prompts_dir / "app_spec.txt"
   135→        if app_spec_path.exists():
   136→            app_spec_path.unlink()
   137→            logger.info("Deleted scaffolded app_spec.txt for fresh spec creation")
   138→
   139→        # Create security settings file (like client.py does)
   140→        # This grants permissions for file operations in the project directory
   141→        security_settings = {
   142→            "sandbox": {"enabled": False},  # Disable sandbox for spec creation
   143→            "permissions": {
   144→                "defaultMode": "acceptEdits",
   145→                "allow": [
   146→                    "Read(./**)",
   147→                    "Write(./**)",
   148→                    "Edit(./**)",
   149→                    "Glob(./**)",
   150→                ],
   151→            },
   152→        }
   153→        settings_file = self.project_dir / ".claude_settings.json"
   154→        with open(settings_file, "w") as f:
   155→            json.dump(security_settings, f, indent=2)
   156→
   157→        # Replace $ARGUMENTS with absolute project path (like CLI does in start.py:184)
   158→        # Using absolute path avoids confusion when project folder name differs from app name
   159→        project_path = str(self.project_dir.resolve())
   160→        system_prompt = skill_content.replace("$ARGUMENTS", project_path)
   161→
   162→        # For import mode, replace $CODEBASE_ANALYSIS with the analysis content
   163→        if self.is_import and self.codebase_analysis:
   164→            system_prompt = system_prompt.replace("$CODEBASE_ANALYSIS", self.codebase_analysis)
   165→        elif self.is_import:
   166→            # No analysis provided - use placeholder
   167→            system_prompt = system_prompt.replace(
   168→                "$CODEBASE_ANALYSIS",
   169→                "*No automated analysis available. Please describe your project.*"
   170→            )
   171→
   172→        # Create Claude SDK client with limited tools for spec creation
   173→        # Use Opus for best quality spec generation
   174→        # Use system CLI to avoid bundled Bun runtime crash (exit code 3) on Windows
   175→        system_cli = shutil.which("claude")
   176→        try:
   177→            self.client = ClaudeSDKClient(
   178→                options=ClaudeAgentOptions(
   179→                    model="claude-opus-4-5-20251101",
   180→                    cli_path=system_cli,
   181→                    system_prompt=system_prompt,
   182→                    allowed_tools=[
   183→                        "Read",
   184→                        "Write",
   185→                        "Edit",
   186→                        "Glob",
   187→                    ],
   188→                    permission_mode="acceptEdits",  # Auto-approve file writes for spec creation
   189→                    max_turns=100,
   190→                    cwd=str(self.project_dir.resolve()),
   191→                    settings=str(settings_file.resolve()),
   192→                )
   193→            )
   194→            # Enter the async context and track it
   195→            await self.client.__aenter__()
   196→            self._client_entered = True
   197→        except Exception as e:
   198→            logger.exception("Failed to create Claude client")
   199→            yield {
   200→                "type": "error",
   201→                "content": f"Failed to initialize Claude: {str(e)}"
   202→            }
   203→            return
   204→
   205→        # Start the conversation - Claude will send the Phase 1 greeting
   206→        try:
   207→            async for chunk in self._query_claude("Begin the spec creation process."):
   208→                yield chunk
   209→            # Signal that the response is complete (for UI to hide loading indicator)
   210→            yield {"type": "response_done"}
   211→        except Exception as e:
   212→            logger.exception("Failed to start spec chat")
   213→            yield {
   214→                "type": "error",
   215→                "content": f"Failed to start conversation: {str(e)}"
   216→            }
   217→
   218→    async def send_message(
   219→        self,
   220→        user_message: str,
   221→        attachments: list[ImageAttachment] | None = None
   222→    ) -> AsyncGenerator[dict, None]:
   223→        """
   224→        Send user message and stream Claude's response.
   225→
   226→        Args:
   227→            user_message: The user's response
   228→            attachments: Optional list of image attachments
   229→
   230→        Yields:
   231→            Message chunks of various types:
   232→            - {"type": "text", "content": str}
   233→            - {"type": "question", "questions": list}
   234→            - {"type": "spec_complete", "path": str}
   235→            - {"type": "error", "content": str}
   236→        """
   237→        if not self.client:
   238→            yield {
   239→                "type": "error",
   240→                "content": "Session not initialized. Call start() first."
   241→            }
   242→            return
   243→
   244→        # Store the user message
   245→        self.messages.append({
   246→            "role": "user",
   247→            "content": user_message,
   248→            "has_attachments": bool(attachments),
   249→            "timestamp": datetime.now().isoformat()
   250→        })
   251→
   252→        try:
   253→            async for chunk in self._query_claude(user_message, attachments):
   254→                yield chunk
   255→            # Signal that the response is complete (for UI to hide loading indicator)
   256→            yield {"type": "response_done"}
   257→        except Exception as e:
   258→            logger.exception("Error during Claude query")
   259→            yield {
   260→                "type": "error",
   261→                "content": f"Error: {str(e)}"
   262→            }
   263→
   264→    async def _query_claude(
   265→        self,
   266→        message: str,
   267→        attachments: list[ImageAttachment] | None = None
   268→    ) -> AsyncGenerator[dict, None]:
   269→        """
   270→        Internal method to query Claude and stream responses.
   271→
   272→        Handles tool calls (Write) and text responses.
   273→        Supports multimodal content with image attachments.
   274→
   275→        IMPORTANT: Spec creation requires BOTH files to be written:
   276→        1. app_spec.txt - the main specification
   277→        2. initializer_prompt.md - tells the agent how many features to create
   278→
   279→        We only signal spec_complete when BOTH files are verified on disk.
   280→        """
   281→        if not self.client:
   282→            return
   283→
   284→        # Build the message content
   285→        if attachments and len(attachments) > 0:
   286→            # Multimodal message: build content blocks array
   287→            content_blocks = []
   288→
   289→            # Add text block if there's text
   290→            if message:
   291→                content_blocks.append({"type": "text", "text": message})
   292→
   293→            # Add image blocks
   294→            for att in attachments:
   295→                content_blocks.append({
   296→                    "type": "image",
   297→                    "source": {
   298→                        "type": "base64",
   299→                        "media_type": att.mimeType,
   300→                        "data": att.base64Data,
   301→                    }
   302→                })
   303→
   304→            # Send multimodal content to Claude using async generator format
   305→            # The SDK's query() accepts AsyncIterable[dict] for custom message formats
   306→            await self.client.query(_make_multimodal_message(content_blocks))
   307→            logger.info(f"Sent multimodal message with {len(attachments)} image(s)")
   308→        else:
   309→            # Text-only message: use string format
   310→            await self.client.query(message)
   311→
   312→        current_text = ""
   313→
   314→        # Track pending writes for BOTH required files
   315→        pending_writes = {
   316→            "app_spec": None,      # {"tool_id": ..., "path": ...}
   317→            "initializer": None,   # {"tool_id": ..., "path": ...}
   318→        }
   319→
   320→        # Track which files have been successfully written
   321→        files_written = {
   322→            "app_spec": False,
   323→            "initializer": False,
   324→        }
   325→
   326→        # Store paths for the completion message
   327→        spec_path = None
   328→
   329→        # Stream the response using receive_response
   330→        async for msg in self.client.receive_response():
   331→            msg_type = type(msg).__name__
   332→
   333→            if msg_type == "AssistantMessage" and hasattr(msg, "content"):
   334→                # Process content blocks in the assistant message
   335→                for block in msg.content:
   336→                    block_type = type(block).__name__
   337→
   338→                    if block_type == "TextBlock" and hasattr(block, "text"):
   339→                        # Accumulate text and yield it
   340→                        text = block.text
   341→                        if text:
   342→                            current_text += text
   343→                            yield {"type": "text", "content": text}
   344→
   345→                            # Store in message history
   346→                            self.messages.append({
   347→                                "role": "assistant",
   348→                                "content": text,
   349→                                "timestamp": datetime.now().isoformat()
   350→                            })
   351→
   352→                    elif block_type == "ToolUseBlock" and hasattr(block, "name"):
   353→                        tool_name = block.name
   354→                        tool_input = getattr(block, "input", {})
   355→                        tool_id = getattr(block, "id", "")
   356→
   357→                        if tool_name in ("Write", "Edit"):
   358→                            # File being written or edited - track for verification
   359→                            file_path = tool_input.get("file_path", "")
   360→
   361→                            # Track app_spec.txt
   362→                            if "app_spec.txt" in str(file_path):
   363→                                pending_writes["app_spec"] = {
   364→                                    "tool_id": tool_id,
   365→                                    "path": file_path
   366→                                }
   367→                                logger.info(f"{tool_name} tool called for app_spec.txt: {file_path}")
   368→
   369→                            # Track initializer_prompt.md
   370→                            elif "initializer_prompt.md" in str(file_path):
   371→                                pending_writes["initializer"] = {
   372→                                    "tool_id": tool_id,
   373→                                    "path": file_path
   374→                                }
   375→                                logger.info(f"{tool_name} tool called for initializer_prompt.md: {file_path}")
   376→
   377→            elif msg_type == "UserMessage" and hasattr(msg, "content"):
   378→                # Tool results - check for write confirmations and errors
   379→                for block in msg.content:
   380→                    block_type = type(block).__name__
   381→                    if block_type == "ToolResultBlock":
   382→                        is_error = getattr(block, "is_error", False)
   383→                        tool_use_id = getattr(block, "tool_use_id", "")
   384→
   385→                        if is_error:
   386→                            content = getattr(block, "content", "Unknown error")
   387→                            logger.warning(f"Tool error: {content}")
   388→                            # Clear any pending writes that failed
   389→                            for key in pending_writes:
   390→                                if pending_writes[key] and tool_use_id == pending_writes[key].get("tool_id"):
   391→                                    logger.error(f"{key} write failed: {content}")
   392→                                    pending_writes[key] = None
   393→                        else:
   394→                            # Tool succeeded - check which file was written
   395→
   396→                            # Check app_spec.txt
   397→                            if pending_writes["app_spec"] and tool_use_id == pending_writes["app_spec"].get("tool_id"):
   398→                                file_path = pending_writes["app_spec"]["path"]
   399→                                full_path = Path(file_path) if Path(file_path).is_absolute() else self.project_dir / file_path
   400→                                if full_path.exists():
   401→                                    logger.info(f"app_spec.txt verified at: {full_path}")
   402→                                    files_written["app_spec"] = True
   403→                                    spec_path = file_path
   404→
   405→                                    # Notify about file write (but NOT completion yet)
   406→                                    yield {
   407→                                        "type": "file_written",
   408→                                        "path": str(file_path)
   409→                                    }
   410→                                else:
   411→                                    logger.error(f"app_spec.txt not found after write: {full_path}")
   412→                                pending_writes["app_spec"] = None
   413→
   414→                            # Check initializer_prompt.md
   415→                            if pending_writes["initializer"] and tool_use_id == pending_writes["initializer"].get("tool_id"):
   416→                                file_path = pending_writes["initializer"]["path"]
   417→                                full_path = Path(file_path) if Path(file_path).is_absolute() else self.project_dir / file_path
   418→                                if full_path.exists():
   419→                                    logger.info(f"initializer_prompt.md verified at: {full_path}")
   420→                                    files_written["initializer"] = True
   421→
   422→                                    # Notify about file write
   423→                                    yield {
   424→                                        "type": "file_written",
   425→                                        "path": str(file_path)
   426→                                    }
   427→                                else:
   428→                                    logger.error(f"initializer_prompt.md not found after write: {full_path}")
   429→                                pending_writes["initializer"] = None
   430→
   431→                            # Check if BOTH files are now written - only then signal completion
   432→                            if files_written["app_spec"] and files_written["initializer"]:
   433→                                logger.info("Both app_spec.txt and initializer_prompt.md verified - signaling completion")
   434→                                self.complete = True
   435→                                yield {
   436→                                    "type": "spec_complete",
   437→                                    "path": str(spec_path)
   438→                                }
   439→
   440→    def is_complete(self) -> bool:
   441→        """Check if spec creation is complete."""
   442→        return self.complete
   443→
   444→    def get_messages(self) -> list[dict]:
   445→        """Get all messages in the conversation."""
   446→        return self.messages.copy()
   447→
   448→
   449→# Session registry with thread safety
   450→_sessions: dict[str, SpecChatSession] = {}
   451→_sessions_lock = threading.Lock()
   452→
   453→
   454→def get_session(project_name: str) -> Optional[SpecChatSession]:
   455→    """Get an existing session for a project."""
   456→    with _sessions_lock:
   457→        return _sessions.get(project_name)
   458→
   459→
   460→async def create_session(
   461→    project_name: str,
   462→    project_dir: Path,
   463→    is_import: bool = False,
   464→    codebase_analysis: Optional[str] = None,
   465→) -> SpecChatSession:
   466→    """Create a new session for a project, closing any existing one.
   467→
   468→    Args:
   469→        project_name: Name of the project
   470→        project_dir: Absolute path to the project directory
   471→        is_import: If True, use import-spec skill for existing codebases
   472→        codebase_analysis: Formatted analysis content for import mode
   473→    """
   474→    old_session: Optional[SpecChatSession] = None
   475→
   476→    with _sessions_lock:
   477→        # Get existing session to close later (outside the lock)
   478→        old_session = _sessions.pop(project_name, None)
   479→        session = SpecChatSession(
   480→            project_name,
   481→            project_dir,
   482→            is_import=is_import,
   483→            codebase_analysis=codebase_analysis,
   484→        )
   485→        _sessions[project_name] = session
   486→
   487→    # Close old session outside the lock to avoid blocking
   488→    if old_session:
   489→        try:
   490→            await old_session.close()
   491→        except Exception as e:
   492→            logger.warning(f"Error closing old session for {project_name}: {e}")
   493→
   494→    return session
   495→
   496→
   497→async def remove_session(project_name: str) -> None:
   498→    """Remove and close a session."""
   499→    session: Optional[SpecChatSession] = None
   500→
   501→    with _sessions_lock:
   502→        session = _sessions.pop(project_name, None)
   503→
   504→    # Close session outside the lock
   505→    if session:
   506→        try:
   507→            await session.close()
   508→        except Exception as e:
   509→            logger.warning(f"Error closing session for {project_name}: {e}")
   510→
   511→
   512→def list_sessions() -> list[str]:
   513→    """List all active session project names."""
   514→    with _sessions_lock:
   515→        return list(_sessions.keys())
   516→
   517→
   518→async def cleanup_all_sessions() -> None:
   519→    """Close all active sessions. Called on server shutdown."""
   520→    sessions_to_close: list[SpecChatSession] = []
   521→
   522→    with _sessions_lock:
   523→        sessions_to_close = list(_sessions.values())
   524→        _sessions.clear()
   525→
   526→    for session in sessions_to_close:
   527→        try:
   528→            await session.close()
   529→        except Exception as e:
   530→            logger.warning(f"Error closing session {session.project_name}: {e}")
   531→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

"""
Spec Creation Chat Session
==========================

Manages interactive spec creation conversation with Claude.
Uses the create-spec.md skill to guide users through app spec creation.
"""

import json
import logging
import shutil
import threading
from datetime import datetime
from pathlib import Path
from typing import AsyncGenerator, Optional

from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient

from ..schemas import ImageAttachment

logger = logging.getLogger(__name__)

async def _make_multimodal_message(content_blocks: list[dict]) -> AsyncGenerator[dict, None]:
    """
    Create an async generator that yields a properly formatted multimodal message.

    The Claude Agent SDK's query() method accepts either:
    - A string (simple text)
    - An AsyncIterable[dict] (for custom message formats)

    This function wraps content blocks in the expected message format.
    """
    yield {
        "type": "user",
        "message": {"role": "user", "content": content_blocks},
        "parent_tool_use_id": None,
        "session_id": "default",
    }

# Root directory of the project
ROOT_DIR = Path(__file__).parent.parent.parent

class SpecChatSession:
    """
    Manages a spec creation conversation for one project.

    For NEW projects, uses create-spec skill to guide users through:
    - Phase 1: Project Overview (name, description, audience)
    - Phase 2: Involvement Level (Quick vs Detailed mode)
    - Phase 3: Technology Preferences
    - Phase 4: Features (main exploration phase)
    - Phase 5: Technical Details (derived or discussed)
    - Phase 6-7: Success Criteria & Approval

    For IMPORTED projects, uses import-spec skill to:
    - Present codebase analysis
    - Document existing features
    - Identify gaps and plan new features
    """

    def __init__(
        self,
        project_name: str,
        project_dir: Path,
        is_import: bool = False,
        codebase_analysis: Optional[str] = None,
    ):
        """
        Initialize the session.

        Args:
            project_name: Name of the project being created
            project_dir: Absolute path to the project directory
            is_import: If True, use import-spec skill instead of create-spec
            codebase_analysis: Formatted codebase analysis for import mode
        """
        self.project_name = project_name
        self.project_dir = project_dir
        self.is_import = is_import
        self.codebase_analysis = codebase_analysis
        self.client: Optional[ClaudeSDKClient] = None
        self.messages: list[dict] = []
        self.complete: bool = False
        self.created_at = datetime.now()
        self._conversation_id: Optional[str] = None
        self._client_entered: bool = False  # Track if context manager is active

    async def close(self) -> None:
        """Clean up resources and close the Claude client."""
        if self.client and self._client_entered:
            try:
                await self.client.__aexit__(None, None, None)
            except Exception as e:
                logger.warning(f"Error closing Claude client: {e}")
            finally:
                self._client_entered = False
                self.client = None

    async def start(self) -> AsyncGenerator[dict, None]:
        """
        Initialize session and get initial greeting from Claude.

        Yields message chunks as they stream in.
        """
        # Load the appropriate skill based on import mode
        if self.is_import:
            skill_path = ROOT_DIR / ".claude" / "commands" / "import-spec.md"
            skill_type = "import"
        else:
            skill_path = ROOT_DIR / ".claude" / "commands" / "create-spec.md"
            skill_type = "create"

        if not skill_path.exists():
            yield {
                "type": "error",
                "content": f"Spec {skill_type} skill not found at {skill_path}"
            }
            return

        try:
            skill_content = skill_path.read_text(encoding="utf-8")
        except UnicodeDecodeError:
            skill_content = skill_path.read_text(encoding="utf-8", errors="replace")

        # Ensure project directory exists (like CLI does in start.py)
        self.project_dir.mkdir(parents=True, exist_ok=True)

        # Delete app_spec.txt so Claude can create it fresh
        # The SDK requires reading existing files before writing, but app_spec.txt is created new
        # Note: We keep initializer_prompt.md so Claude can read and update the template
        prompts_dir = self.project_dir / "prompts"
        app_spec_path = prompts_dir / "app_spec.txt"
        if app_spec_path.exists():
            app_spec_path.unlink()
            logger.info("Deleted scaffolded app_spec.txt for fresh spec creation")

        # Create security settings file (like client.py does)
        # This grants permissions for file operations in the project directory
        security_settings = {
            "sandbox": {"enabled": False},  # Disable sandbox for spec creation
            "permissions": {
                "defaultMode": "acceptEdits",
                "allow": [
                    "Read(./**)",
                    "Write(./**)",
                    "Edit(./**)",
                    "Glob(./**)",
                ],
            },
        }
        settings_file = self.project_dir / ".claude_settings.json"
        with open(settings_file, "w") as f:
            json.dump(security_settings, f, indent=2)

        # Replace $ARGUMENTS with absolute project path (like CLI does in start.py:184)
        # Using absolute path avoids confusion when project folder name differs from app name
        project_path = str(self.project_dir.resolve())
        system_prompt = skill_content.replace("$ARGUMENTS", project_path)

        # For import mode, replace $CODEBASE_ANALYSIS with the analysis content
        if self.is_import and self.codebase_analysis:
            system_prompt = system_prompt.replace("$CODEBASE_ANALYSIS", self.codebase_analysis)
        elif self.is_import:
            # No analysis provided - use placeholder
            system_prompt = system_prompt.replace(
                "$CODEBASE_ANALYSIS",
                "*No automated analysis available. Please describe your project.*"
            )

        # Create Claude SDK client with limited tools for spec creation
        # Use Opus for best quality spec generation
        # Use system CLI to avoid bundled Bun runtime crash (exit code 3) on Windows
        system_cli = shutil.which("claude")
        try:
            self.client = ClaudeSDKClient(
                options=ClaudeAgentOptions(
                    model="claude-opus-4-5-20251101",
                    cli_path=system_cli,
                    system_prompt=system_prompt,
                    allowed_tools=[
                        "Read",
                        "Write",
                        "Edit",
                        "Glob",
                    ],
                    permission_mode="acceptEdits",  # Auto-approve file writes for spec creation
                    max_turns=100,
                    cwd=str(self.project_dir.resolve()),
                    settings=str(settings_file.resolve()),
                )
            )
            # Enter the async context and track it
            await self.client.__aenter__()
            self._client_entered = True
        except Exception as e:
            logger.exception("Failed to create Claude client")
            yield {
                "type": "error",
                "content": f"Failed to initialize Claude: {str(e)}"
            }
            return

        # Start the conversation - Claude will send the Phase 1 greeting
        try:
            async for chunk in self._query_claude("Begin the spec creation process."):
                yield chunk
            # Signal that the response is complete (for UI to hide loading indicator)
            yield {"type": "response_done"}
        except Exception as e:
            logger.exception("Failed to start spec chat")
            yield {
                "type": "error",
                "content": f"Failed to start conversation: {str(e)}"
            }

    async def send_message(
        self,
        user_message: str,
        attachments: list[ImageAttachment] | None = None
    ) -> AsyncGenerator[dict, None]:
        """
        Send user message and stream Claude's response.

        Args:
            user_message: The user's response
            attachments: Optional list of image attachments

        Yields:
            Message chunks of various types:
            - {"type": "text", "content": str}
            - {"type": "question", "questions": list}
            - {"type": "spec_complete", "path": str}
            - {"type": "error", "content": str}
        """
        if not self.client:
            yield {
                "type": "error",
                "content": "Session not initialized. Call start() first."
            }
            return

        # Store the user message
        self.messages.append({
            "role": "user",
            "content": user_message,
            "has_attachments": bool(attachments),
            "timestamp": datetime.now().isoformat()
        })

        try:
            async for chunk in self._query_claude(user_message, attachments):
                yield chunk
            # Signal that the response is complete (for UI to hide loading indicator)
            yield {"type": "response_done"}
        except Exception as e:
            logger.exception("Error during Claude query")
            yield {
                "type": "error",
                "content": f"Error: {str(e)}"
            }

    async def _query_claude(
        self,
        message: str,
        attachments: list[ImageAttachment] | None = None
    ) -> AsyncGenerator[dict, None]:
        """
        Internal method to query Claude and stream responses.

        Handles tool calls (Write) and text responses.
        Supports multimodal content with image attachments.

        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:
            return

        # Build the message content
        if attachments and len(attachments) > 0:
            # Multimodal message: build content blocks array
            content_blocks = []

            # Add text block if there's text
            if message:
                content_blocks.append({"type": "text", "text": message})

            # Add image blocks
            for att in attachments:
                content_blocks.append({
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": att.mimeType,
                        "data": att.base64Data,
                    }
                })

            # Send multimodal content to Claude using async generator format
            # The SDK's query() accepts AsyncIterable[dict] for custom message formats
            await self.client.query(_make_multimodal_message(content_blocks))
            logger.info(f"Sent multimodal message with {len(attachments)} image(s)")
        else:
            # Text-only message: use string format
            await self.client.query(message)

        current_text = ""

        # 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
        async for msg in self.client.receive_response():
            msg_type = type(msg).__name__

            if msg_type == "AssistantMessage" and hasattr(msg, "content"):
                # Process content blocks in the assistant message
                for block in msg.content:
                    block_type = type(block).__name__

                    if block_type == "TextBlock" and hasattr(block, "text"):
                        # Accumulate text and yield it
                        text = block.text
                        if text:
                            current_text += text
                            yield {"type": "text", "content": text}

                            # Store in message history
                            self.messages.append({
                                "role": "assistant",
                                "content": text,
                                "timestamp": datetime.now().isoformat()
                            })

                    elif block_type == "ToolUseBlock" and hasattr(block, "name"):
                        tool_name = block.name
                        tool_input = getattr(block, "input", {})
                        tool_id = getattr(block, "id", "")

                        if tool_name in ("Write", "Edit"):
                            # File being written or edited - track for verification
                            file_path = tool_input.get("file_path", "")

                            # Track app_spec.txt
                            if "app_spec.txt" in str(file_path):
                                pending_writes["app_spec"] = {
                                    "tool_id": tool_id,
                                    "path": 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):
                                pending_writes["initializer"] = {
                                    "tool_id": tool_id,
                                    "path": file_path
                                }
                                logger.info(f"{tool_name} tool called for initializer_prompt.md: {file_path}")

            elif msg_type == "UserMessage" and hasattr(msg, "content"):
                # Tool results - check for write confirmations and errors
                for block in msg.content:
                    block_type = type(block).__name__
                    if block_type == "ToolResultBlock":
                        is_error = getattr(block, "is_error", False)
                        tool_use_id = getattr(block, "tool_use_id", "")

                        if is_error:
                            content = getattr(block, "content", "Unknown error")
                            logger.warning(f"Tool error: {content}")
                            # Clear any pending writes that failed
                            for key in pending_writes:
                                if pending_writes[key] and tool_use_id == pending_writes[key].get("tool_id"):
                                    logger.error(f"{key} write failed: {content}")
                                    pending_writes[key] = None
                        else:
                            # Tool succeeded - check which file was written

                            # Check app_spec.txt
                            if pending_writes["app_spec"] and tool_use_id == pending_writes["app_spec"].get("tool_id"):
                                file_path = pending_writes["app_spec"]["path"]
                                full_path = Path(file_path) if Path(file_path).is_absolute() else self.project_dir / file_path
                                if full_path.exists():
                                    logger.info(f"app_spec.txt verified at: {full_path}")
                                    files_written["app_spec"] = True
                                    spec_path = file_path

                                    # Notify about file write (but NOT completion yet)
                                    yield {
                                        "type": "file_written",
                                        "path": str(file_path)
                                    }
                                else:
                                    logger.error(f"app_spec.txt not found after write: {full_path}")
                                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 = Path(file_path) if Path(file_path).is_absolute() else self.project_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:
        """Check if spec creation is complete."""
        return self.complete

    def get_messages(self) -> list[dict]:
        """Get all messages in the conversation."""
        return self.messages.copy()

# Session registry with thread safety
_sessions: dict[str, SpecChatSession] = {}
_sessions_lock = threading.Lock()

def get_session(project_name: str) -> Optional[SpecChatSession]:
    """Get an existing session for a project."""
    with _sessions_lock:
        return _sessions.get(project_name)

async def create_session(
    project_name: str,
    project_dir: Path,
    is_import: bool = False,
    codebase_analysis: Optional[str] = None,
) -> SpecChatSession:
    """Create a new session for a project, closing any existing one.

    Args:
        project_name: Name of the project
        project_dir: Absolute path to the project directory
        is_import: If True, use import-spec skill for existing codebases
        codebase_analysis: Formatted analysis content for import mode
    """
    old_session: Optional[SpecChatSession] = None

    with _sessions_lock:
        # Get existing session to close later (outside the lock)
        old_session = _sessions.pop(project_name, None)
        session = SpecChatSession(
            project_name,
            project_dir,
            is_import=is_import,
            codebase_analysis=codebase_analysis,
        )
        _sessions[project_name] = session

    # Close old session outside the lock to avoid blocking
    if old_session:
        try:
            await old_session.close()
        except Exception as e:
            logger.warning(f"Error closing old session for {project_name}: {e}")

    return session

async def remove_session(project_name: str) -> None:
    """Remove and close a session."""
    session: Optional[SpecChatSession] = None

    with _sessions_lock:
        session = _sessions.pop(project_name, None)

    # Close session outside the lock
    if session:
        try:
            await session.close()
        except Exception as e:
            logger.warning(f"Error closing session for {project_name}: {e}")

def list_sessions() -> list[str]:
    """List all active session project names."""
    with _sessions_lock:
        return list(_sessions.keys())

async def cleanup_all_sessions() -> None:
    """Close all active sessions. Called on server shutdown."""
    sessions_to_close: list[SpecChatSession] = []

    with _sessions_lock:
        sessions_to_close = list(_sessions.values())
        _sessions.clear()

    for session in sessions_to_close:
        try:
            await session.close()
        except Exception as e:
            logger.warning(f"Error closing session {session.project_name}: {e}")


Now I understand the architecture. I need to:

1. Add a "refine" mode to the spec creation flow that:
   - Loads the existing app_spec.txt content
   - Provides it to Claude in the system prompt so it can see what already exists
   - Allows the user to discuss changes
   - Updates the spec when done

2. Update the UI to:
   - On re-init click for projects with existing spec/features, offer options:
     - "Re-generate features" (current reinit - just reset features and run initializer)
     - "Refine app spec" (open the spec interview with existing spec loaded)

Let me:
1. First, create a "refine-spec.md" skill or modify the existing one to handle refinement mode
2. Add `is_refine` mode to the SpecChatSession
3. Update the UI to show options

Let me check the create-spec.md skill to understand its structure.


     1→---
     2→description: Create an app spec for autonomous coding (project)
     3→---
     4→
     5→# PROJECT DIRECTORY
     6→
     7→This command **requires** the project directory as an argument via `$ARGUMENTS`.
     8→
     9→**Example:** `/create-spec generations/my-app`
    10→
    11→**Output location:** `$ARGUMENTS/prompts/app_spec.txt` and `$ARGUMENTS/prompts/initializer_prompt.md`
    12→
    13→If `$ARGUMENTS` is empty, inform the user they must provide a project path and exit.
    14→
    15→---
    16→
    17→# GOAL
    18→
    19→Help the user create a comprehensive project specification for a long-running autonomous coding process. This specification will be used by AI coding agents to build their application across multiple sessions.
    20→
    21→This tool works for projects of any size - from simple utilities to large-scale applications.
    22→
    23→---
    24→
    25→# YOUR ROLE
    26→
    27→You are the **Spec Creation Assistant** - an expert at translating project ideas into detailed technical specifications. Your job is to:
    28→
    29→1. Understand what the user wants to build (in their own words)
    30→2. Ask about features and functionality (things anyone can describe)
    31→3. **Derive** the technical details (database, API, architecture) from their requirements
    32→4. Generate the specification files that autonomous coding agents will use
    33→
    34→**IMPORTANT: Cater to all skill levels.** Many users are product owners or have functional knowledge but aren't technical. They know WHAT they want to build, not HOW to build it. You should:
    35→
    36→- Ask questions anyone can answer (features, user flows, what screens exist)
    37→- **Derive** technical details (database schema, API endpoints, architecture) yourself
    38→- Only ask technical questions if the user wants to be involved in those decisions
    39→
    40→**Use conversational questions** to gather information. For questions with clear options, present them as numbered choices that the user can select from. For open-ended exploration, use natural conversation.
    41→
    42→---
    43→
    44→# CONVERSATION FLOW
    45→
    46→There are two paths through this process:
    47→
    48→**Quick Path** (recommended for most users): You describe what you want, agent derives the technical details
    49→**Detailed Path**: You want input on technology choices, database design, API structure, etc.
    50→
    51→**CRITICAL: This is a CONVERSATION, not a form.**
    52→
    53→- Ask questions for ONE phase at a time
    54→- WAIT for the user to respond before moving to the next phase
    55→- Acknowledge their answers before continuing
    56→- Do NOT bundle multiple phases into one message
    57→
    58→---
    59→
    60→## Phase 1: Project Overview
    61→
    62→Start with simple questions anyone can answer:
    63→
    64→1. **Project Name**: What should this project be called?
    65→2. **Description**: In your own words, what are you building and what problem does it solve?
    66→3. **Target Audience**: Who will use this?
    67→
    68→**IMPORTANT: Ask these questions and WAIT for the user to respond before continuing.**
    69→Do NOT immediately jump to Phase 2. Let the user answer, acknowledge their responses, then proceed.
    70→
    71→---
    72→
    73→## Phase 2: Involvement Level
    74→
    75→Ask the user about their involvement preference:
    76→
    77→> "How involved do you want to be in technical decisions?
    78→>
    79→> 1. **Quick Mode (Recommended)** - You describe what you want, I'll handle database, API, and architecture
    80→> 2. **Detailed Mode** - You want input on technology choices and architecture decisions
    81→>
    82→> Which would you prefer?"
    83→
    84→**If Quick Mode**: Skip to Phase 3, then go to Phase 4 (Features). You will derive technical details yourself.
    85→**If Detailed Mode**: Go through all phases, asking technical questions.
    86→
    87→## Phase 3: Technology Preferences
    88→
    89→**For Quick Mode users**, also ask about tech preferences:
    90→
    91→> "Any technology preferences, or should I choose sensible defaults?
    92→>
    93→> 1. **Use defaults (Recommended)** - React, Node.js, SQLite - solid choices for most apps
    94→> 2. **I have preferences** - I'll specify my preferred languages/frameworks"
    95→
    96→**For Detailed Mode users**, ask specific tech questions about frontend, backend, database, etc.
    97→
    98→## Phase 4: Features (THE MAIN PHASE)
    99→
   100→This is where you spend most of your time. Ask questions in plain language that anyone can answer.
   101→
   102→**Start broad with open conversation:**
   103→
   104→> "Walk me through your app. What does a user see when they first open it? What can they do?"
   105→
   106→**Then ask about key feature areas:**
   107→
   108→> "Let me ask about a few common feature areas:
   109→>
   110→> 1. **User Accounts** - Do users need to log in / have accounts? (Yes with profiles, No anonymous use, or Maybe optional)
   111→> 2. **Mobile Support** - Should this work well on mobile phones? (Yes fully responsive, Desktop only, or Basic mobile)
   112→> 3. **Search** - Do users need to search or filter content? (Yes, No, or Basic only)
   113→> 4. **Sharing** - Any sharing or collaboration features? (Yes, No, or Maybe later)"
   114→
   115→**Then drill into the "Yes" answers with open conversation:**
   116→
   117→**4a. The Main Experience**
   118→
   119→- What's the main thing users do in your app?
   120→- Walk me through a typical user session
   121→
   122→**4b. User Accounts** (if they said Yes)
   123→
   124→- What can they do with their account?
   125→- Any roles or permissions?
   126→
   127→**4c. What Users Create/Manage**
   128→
   129→- What "things" do users create, save, or manage?
   130→- Can they edit or delete these things?
   131→- Can they organize them (folders, tags, categories)?
   132→
   133→**4d. Settings & Customization**
   134→
   135→- What should users be able to customize?
   136→- Light/dark mode? Other display preferences?
   137→
   138→**4e. Search & Finding Things** (if they said Yes)
   139→
   140→- What do they search for?
   141→- What filters would be helpful?
   142→
   143→**4f. Sharing & Collaboration** (if they said Yes)
   144→
   145→- What can be shared?
   146→- View-only or collaborative editing?
   147→
   148→**4g. Any Dashboards or Analytics?**
   149→
   150→- Does the user see any stats, reports, or metrics?
   151→
   152→**4h. Domain-Specific Features**
   153→
   154→- What else is unique to your app?
   155→- Any features we haven't covered?
   156→
   157→**4i. Security & Access Control (if app has authentication)**
   158→
   159→Ask about user roles:
   160→
   161→> "Who are the different types of users?
   162→>
   163→> 1. **Just regular users** - Everyone has the same permissions
   164→> 2. **Users + Admins** - Regular users and administrators with extra powers
   165→> 3. **Multiple roles** - Several distinct user types (e.g., viewer, editor, manager, admin)"
   166→
   167→**If multiple roles, explore in conversation:**
   168→
   169→- What can each role see?
   170→- What can each role do?
   171→- Are there pages only certain roles can access?
   172→- What happens if someone tries to access something they shouldn't?
   173→
   174→**Also ask about authentication:**
   175→
   176→- How do users log in? (email/password, social login, SSO)
   177→- Password requirements? (for security testing)
   178→- Session timeout? Auto-logout after inactivity?
   179→- Any sensitive operations requiring extra confirmation?
   180→
   181→**4j. Data Flow & Integration**
   182→
   183→- What data do users create vs what's system-generated?
   184→- Are there workflows that span multiple steps or pages?
   185→- What happens to related data when something is deleted?
   186→- Are there any external systems or APIs to integrate with?
   187→- Any import/export functionality?
   188→
   189→**4k. Error & Edge Cases**
   190→
   191→- What should happen if the network fails mid-action?
   192→- What about duplicate entries (e.g., same email twice)?
   193→- Very long text inputs?
   194→- Empty states (what shows when there's no data)?
   195→
   196→**Keep asking follow-up questions until you have a complete picture.** For each feature area, understand:
   197→
   198→- What the user sees
   199→- What actions they can take
   200→- What happens as a result
   201→- Who is allowed to do it (permissions)
   202→- What errors could occur
   203→
   204→## Phase 4L: Derive Feature Count (DO NOT ASK THE USER)
   205→
   206→After gathering all features, **you** (the agent) should tally up the testable features. Do NOT ask the user how many features they want - derive it from what was discussed.
   207→
   208→**Typical ranges for reference:**
   209→
   210→- **Simple apps** (todo list, calculator, notes): ~20-50 features
   211→- **Medium apps** (blog, task manager with auth): ~100 features
   212→- **Advanced apps** (e-commerce, CRM, full SaaS): ~150-200 features
   213→
   214→These are just reference points - your actual count should come from the requirements discussed.
   215→
   216→**How to count features:**
   217→For each feature area discussed, estimate the number of discrete, testable behaviors:
   218→
   219→- Each CRUD operation = 1 feature (create, read, update, delete)
   220→- Each UI interaction = 1 feature (click, drag, hover effect)
   221→- Each validation/error case = 1 feature
   222→- Each visual requirement = 1 feature (styling, animation, responsive behavior)
   223→
   224→**Present your estimate to the user:**
   225→
   226→> "Based on what we discussed, here's my feature breakdown:
   227→>
   228→> - [Category 1]: ~X features
   229→> - [Category 2]: ~Y features
   230→> - [Category 3]: ~Z features
   231→> - ...
   232→>
   233→> **Total: ~N features**
   234→>
   235→> Does this seem right, or should I adjust?"
   236→
   237→Let the user confirm or adjust. This becomes your `feature_count` for the spec.
   238→
   239→## Phase 5: Technical Details (DERIVED OR DISCUSSED)
   240→
   241→**For Quick Mode users:**
   242→Tell them: "Based on what you've described, I'll design the database, API, and architecture. Here's a quick summary of what I'm planning..."
   243→
   244→Then briefly outline:
   245→
   246→- Main data entities you'll create (in plain language: "I'll create tables for users, projects, documents, etc.")
   247→- Overall app structure ("sidebar navigation with main content area")
   248→- Any key technical decisions
   249→
   250→Ask: "Does this sound right? Any concerns?"
   251→
   252→**For Detailed Mode users:**
   253→Walk through each technical area:
   254→
   255→**5a. Database Design**
   256→
   257→- What entities/tables are needed?
   258→- Key fields for each?
   259→- Relationships?
   260→
   261→**5b. API Design**
   262→
   263→- What endpoints are needed?
   264→- How should they be organized?
   265→
   266→**5c. UI Layout**
   267→
   268→- Overall structure (columns, navigation)
   269→- Key screens/pages
   270→- Design preferences (colors, themes)
   271→
   272→**5d. Implementation Phases**
   273→
   274→- What order to build things?
   275→- Dependencies?
   276→
   277→## Phase 6: Success Criteria
   278→
   279→Ask in simple terms:
   280→
   281→> "What does 'done' look like for you? When would you consider this app complete and successful?"
   282→
   283→Prompt for:
   284→
   285→- Must-have functionality
   286→- Quality expectations (polished vs functional)
   287→- Any specific requirements
   288→
   289→## Phase 7: Review & Approval
   290→
   291→Present everything gathered:
   292→
   293→1. **Summary of the app** (in plain language)
   294→2. **Feature count**
   295→3. **Technology choices** (whether specified or derived)
   296→4. **Brief technical plan** (for their awareness)
   297→
   298→First ask in conversation if they want to make changes.
   299→
   300→**Then ask for final confirmation:**
   301→
   302→> "Ready to generate the specification files?
   303→>
   304→> 1. **Yes, generate files** - Create app_spec.txt and update prompt files
   305→> 2. **I have changes** - Let me add or modify something first"
   306→
   307→---
   308→
   309→# FILE GENERATION
   310→
   311→**Note: This section is for YOU (the agent) to execute. Do not burden the user with these technical details.**
   312→
   313→## Output Directory
   314→
   315→The output directory is: `$ARGUMENTS/prompts/`
   316→
   317→Once the user approves, generate these files:
   318→
   319→## 1. Generate `app_spec.txt`
   320→
   321→**Output path:** `$ARGUMENTS/prompts/app_spec.txt`
   322→
   323→Create a new file using this XML structure:
   324→
   325→```xml
   326→<project_specification>
   327→  <project_name>[Project Name]</project_name>
   328→
   329→  <overview>
   330→    [2-3 sentence description from Phase 1]
   331→  </overview>
   332→
   333→  <technology_stack>
   334→    <frontend>
   335→      <framework>[Framework]</framework>
   336→      <styling>[Styling solution]</styling>
   337→      [Additional frontend config]
   338→    </frontend>
   339→    <backend>
   340→      <runtime>[Runtime]</runtime>
   341→      <database>[Database]</database>
   342→      [Additional backend config]
   343→    </backend>
   344→    <communication>
   345→      <api>[API style]</api>
   346→      [Additional communication config]
   347→    </communication>
   348→  </technology_stack>
   349→
   350→  <prerequisites>
   351→    <environment_setup>
   352→      [Setup requirements]
   353→    </environment_setup>
   354→  </prerequisites>
   355→
   356→  <feature_count>[derived count from Phase 4L]</feature_count>
   357→
   358→  <security_and_access_control>
   359→    <user_roles>
   360→      <role name="[role_name]">
   361→        <permissions>
   362→          - [Can do X]
   363→          - [Can see Y]
   364→          - [Cannot access Z]
   365→        </permissions>
   366→        <protected_routes>
   367→          - /admin/* (admin only)
   368→          - /settings (authenticated users)
   369→        </protected_routes>
   370→      </role>
   371→      [Repeat for each role]
   372→    </user_roles>
   373→    <authentication>
   374→      <method>[email/password | social | SSO]</method>
   375→      <session_timeout>[duration or "none"]</session_timeout>
   376→      <password_requirements>[if applicable]</password_requirements>
   377→    </authentication>
   378→    <sensitive_operations>
   379→      - [Delete account requires password confirmation]
   380→      - [Financial actions require 2FA]
   381→    </sensitive_operations>
   382→  </security_and_access_control>
   383→
   384→  <core_features>
   385→    <[category_name]>
   386→      - [Feature 1]
   387→      - [Feature 2]
   388→      - [Feature 3]
   389→    </[category_name]>
   390→    [Repeat for all feature categories]
   391→  </core_features>
   392→
   393→  <database_schema>
   394→    <tables>
   395→      <[table_name]>
   396→        - [field1], [field2], [field3]
   397→        - [additional fields]
   398→      </[table_name]>
   399→      [Repeat for all tables]
   400→    </tables>
   401→  </database_schema>
   402→
   403→  <api_endpoints_summary>
   404→    <[category]>
   405→      - [VERB] /api/[path]
   406→      - [VERB] /api/[path]
   407→    </[category]>
   408→    [Repeat for all categories]
   409→  </api_endpoints_summary>
   410→
   411→  <ui_layout>
   412→    <main_structure>
   413→      [Layout description]
   414→    </main_structure>
   415→    [Additional UI sections as needed]
   416→  </ui_layout>
   417→
   418→  <design_system>
   419→    <color_palette>
   420→      [Colors]
   421→    </color_palette>
   422→    <typography>
   423→      [Font preferences]
   424→    </typography>
   425→  </design_system>
   426→
   427→  <implementation_steps>
   428→    <step number="1">
   429→      <title>[Phase Title]</title>
   430→      <tasks>
   431→        - [Task 1]
   432→        - [Task 2]
   433→      </tasks>
   434→    </step>
   435→    [Repeat for all phases]
   436→  </implementation_steps>
   437→
   438→  <success_criteria>
   439→    <functionality>
   440→      [Functionality criteria]
   441→    </functionality>
   442→    <user_experience>
   443→      [UX criteria]
   444→    </user_experience>
   445→    <technical_quality>
   446→      [Technical criteria]
   447→    </technical_quality>
   448→    <design_polish>
   449→      [Design criteria]
   450→    </design_polish>
   451→  </success_criteria>
   452→</project_specification>
   453→```
   454→
   455→## 2. Update `initializer_prompt.md`
   456→
   457→**Output path:** `$ARGUMENTS/prompts/initializer_prompt.md`
   458→
   459→If the output directory has an existing `initializer_prompt.md`, read it and update the feature count.
   460→If not, copy from `.claude/templates/initializer_prompt.template.md` first, then update.
   461→
   462→**CRITICAL: You MUST update the feature count placeholder:**
   463→
   464→1. Find the line containing `**[FEATURE_COUNT]**` in the "REQUIRED FEATURE COUNT" section
   465→2. Replace `[FEATURE_COUNT]` with the exact number agreed upon in Phase 4L (e.g., `25`)
   466→3. The result should read like: `You must create exactly **25** features using the...`
   467→
   468→**Example edit:**
   469→```
   470→Before: **CRITICAL:** You must create exactly **[FEATURE_COUNT]** features using the `feature_create_bulk` tool.
   471→After:  **CRITICAL:** You must create exactly **25** features using the `feature_create_bulk` tool.
   472→```
   473→
   474→**Verify the update:** After editing, read the file again to confirm the feature count appears correctly. If `[FEATURE_COUNT]` still appears in the file, the update failed and you must try again.
   475→
   476→**Note:** You may also update `coding_prompt.md` if the user requests changes to how the coding agent should work. Include it in the status file if modified.
   477→
   478→## 3. Write Status File (REQUIRED - Do This Last)
   479→
   480→**Output path:** `$ARGUMENTS/prompts/.spec_status.json`
   481→
   482→**CRITICAL:** After you have completed ALL requested file changes, write this status file to signal completion to the UI. This is required for the "Continue to Project" button to appear.
   483→
   484→Write this JSON file:
   485→
   486→```json
   487→{
   488→  "status": "complete",
   489→  "version": 1,
   490→  "timestamp": "[current ISO 8601 timestamp, e.g., 2025-01-15T14:30:00.000Z]",
   491→  "files_written": [
   492→    "prompts/app_spec.txt",
   493→    "prompts/initializer_prompt.md"
   494→  ],
   495→  "feature_count": [the feature count from Phase 4L]
   496→}
   497→```
   498→
   499→**Include ALL files you modified** in the `files_written` array. If the user asked you to also modify `coding_prompt.md`, include it:
   500→
   501→```json
   502→{
   503→  "status": "complete",
   504→  "version": 1,
   505→  "timestamp": "2025-01-15T14:30:00.000Z",
   506→  "files_written": [
   507→    "prompts/app_spec.txt",
   508→    "prompts/initializer_prompt.md",
   509→    "prompts/coding_prompt.md"
   510→  ],
   511→  "feature_count": 35
   512→}
   513→```
   514→
   515→**IMPORTANT:**
   516→- Write this file LAST, after all other files are successfully written
   517→- Only write it when you consider ALL requested work complete
   518→- The UI polls this file to detect completion and show the Continue button
   519→- If the user asks for additional changes after you've written this, you may update it again when the new changes are complete
   520→
   521→---
   522→
   523→# AFTER FILE GENERATION: NEXT STEPS
   524→
   525→Once files are generated, tell the user what to do next:
   526→
   527→> "Your specification files have been created in `$ARGUMENTS/prompts/`!
   528→>
   529→> **Files created:**
   530→> - `$ARGUMENTS/prompts/app_spec.txt`
   531→> - `$ARGUMENTS/prompts/initializer_prompt.md`
   532→>
   533→> The **Continue to Project** button should now appear. Click it to start the autonomous coding agent!
   534→>
   535→> **If you don't see the button:** Type `/exit` or click **Exit to Project** in the header.
   536→>
   537→> **Important timing expectations:**
   538→>
   539→> - **First session:** The agent generates features in the database. This takes several minutes.
   540→> - **Subsequent sessions:** Each coding iteration takes 5-15 minutes depending on complexity.
   541→> - **Full app:** Building all [X] features will take many hours across multiple sessions.
   542→>
   543→> **Controls:**
   544→>
   545→> - Press `Ctrl+C` to pause the agent at any time
   546→> - Run `start.bat` (Windows) or `./start.sh` (Mac/Linux) to resume where you left off"
   547→
   548→Replace `[X]` with their feature count.
   549→
   550→---
   551→
   552→# IMPORTANT REMINDERS
   553→
   554→- **Meet users where they are**: Not everyone is technical. Ask about what they want, not how to build it.
   555→- **Quick Mode is the default**: Most users should be able to describe their app and let you handle the technical details.
   556→- **Derive, don't interrogate**: For non-technical users, derive database schema, API endpoints, and architecture from their feature descriptions. Don't ask them to specify these.
   557→- **Use plain language**: Instead of "What entities need CRUD operations?", ask "What things can users create, edit, or delete?"
   558→- **Be thorough on features**: This is where to spend time. Keep asking follow-up questions until you have a complete picture.
   559→- **Derive feature count, don't guess**: After gathering requirements, tally up testable features yourself and present the estimate. Don't use fixed tiers or ask users to guess.
   560→- **Validate before generating**: Present a summary including your derived feature count and get explicit approval before creating files.
   561→
   562→---
   563→
   564→# BEGIN
   565→
   566→Start by greeting the user warmly. Ask ONLY the Phase 1 questions:
   567→
   568→> "Hi! I'm here to help you create a detailed specification for your app.
   569→>
   570→> Let's start with the basics:
   571→>
   572→> 1. What do you want to call this project?
   573→> 2. In your own words, what are you building?
   574→> 3. Who will use it - just you, or others too?"
   575→
   576→**STOP HERE and wait for their response.** Do not ask any other questions yet. Do not use AskUserQuestion yet. Just have a conversation about their project basics first.
   577→
   578→After they respond, acknowledge what they said, then move to Phase 2.
   579→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

---
description: Create an app spec for autonomous coding (project)
---

# PROJECT DIRECTORY

This command **requires** the project directory as an argument via `$ARGUMENTS`.

**Example:** `/create-spec generations/my-app`

**Output location:** `$ARGUMENTS/prompts/app_spec.txt` and `$ARGUMENTS/prompts/initializer_prompt.md`

If `$ARGUMENTS` is empty, inform the user they must provide a project path and exit.

---

# GOAL

Help the user create a comprehensive project specification for a long-running autonomous coding process. This specification will be used by AI coding agents to build their application across multiple sessions.

This tool works for projects of any size - from simple utilities to large-scale applications.

---

# YOUR ROLE

You are the **Spec Creation Assistant** - an expert at translating project ideas into detailed technical specifications. Your job is to:

1. Understand what the user wants to build (in their own words)
2. Ask about features and functionality (things anyone can describe)
3. **Derive** the technical details (database, API, architecture) from their requirements
4. Generate the specification files that autonomous coding agents will use

**IMPORTANT: Cater to all skill levels.** Many users are product owners or have functional knowledge but aren't technical. They know WHAT they want to build, not HOW to build it. You should:

- Ask questions anyone can answer (features, user flows, what screens exist)
- **Derive** technical details (database schema, API endpoints, architecture) yourself
- Only ask technical questions if the user wants to be involved in those decisions

**Use conversational questions** to gather information. For questions with clear options, present them as numbered choices that the user can select from. For open-ended exploration, use natural conversation.

---

# CONVERSATION FLOW

There are two paths through this process:

**Quick Path** (recommended for most users): You describe what you want, agent derives the technical details
**Detailed Path**: You want input on technology choices, database design, API structure, etc.

**CRITICAL: This is a CONVERSATION, not a form.**

- Ask questions for ONE phase at a time
- WAIT for the user to respond before moving to the next phase
- Acknowledge their answers before continuing
- Do NOT bundle multiple phases into one message

---

## Phase 1: Project Overview

Start with simple questions anyone can answer:

1. **Project Name**: What should this project be called?
2. **Description**: In your own words, what are you building and what problem does it solve?
3. **Target Audience**: Who will use this?

**IMPORTANT: Ask these questions and WAIT for the user to respond before continuing.**
Do NOT immediately jump to Phase 2. Let the user answer, acknowledge their responses, then proceed.

---

## Phase 2: Involvement Level

Ask the user about their involvement preference:

> "How involved do you want to be in technical decisions?
>
> 1. **Quick Mode (Recommended)** - You describe what you want, I'll handle database, API, and architecture
> 2. **Detailed Mode** - You want input on technology choices and architecture decisions
>
> Which would you prefer?"

**If Quick Mode**: Skip to Phase 3, then go to Phase 4 (Features). You will derive technical details yourself.
**If Detailed Mode**: Go through all phases, asking technical questions.

## Phase 3: Technology Preferences

**For Quick Mode users**, also ask about tech preferences:

> "Any technology preferences, or should I choose sensible defaults?
>
> 1. **Use defaults (Recommended)** - React, Node.js, SQLite - solid choices for most apps
> 2. **I have preferences** - I'll specify my preferred languages/frameworks"

**For Detailed Mode users**, ask specific tech questions about frontend, backend, database, etc.

## Phase 4: Features (THE MAIN PHASE)

This is where you spend most of your time. Ask questions in plain language that anyone can answer.

**Start broad with open conversation:**

> "Walk me through your app. What does a user see when they first open it? What can they do?"

**Then ask about key feature areas:**

> "Let me ask about a few common feature areas:
>
> 1. **User Accounts** - Do users need to log in / have accounts? (Yes with profiles, No anonymous use, or Maybe optional)
> 2. **Mobile Support** - Should this work well on mobile phones? (Yes fully responsive, Desktop only, or Basic mobile)
> 3. **Search** - Do users need to search or filter content? (Yes, No, or Basic only)
> 4. **Sharing** - Any sharing or collaboration features? (Yes, No, or Maybe later)"

**Then drill into the "Yes" answers with open conversation:**

**4a. The Main Experience**

- What's the main thing users do in your app?
- Walk me through a typical user session

**4b. User Accounts** (if they said Yes)

- What can they do with their account?
- Any roles or permissions?

**4c. What Users Create/Manage**

- What "things" do users create, save, or manage?
- Can they edit or delete these things?
- Can they organize them (folders, tags, categories)?

**4d. Settings & Customization**

- What should users be able to customize?
- Light/dark mode? Other display preferences?

**4e. Search & Finding Things** (if they said Yes)

- What do they search for?
- What filters would be helpful?

**4f. Sharing & Collaboration** (if they said Yes)

- What can be shared?
- View-only or collaborative editing?

**4g. Any Dashboards or Analytics?**

- Does the user see any stats, reports, or metrics?

**4h. Domain-Specific Features**

- What else is unique to your app?
- Any features we haven't covered?

**4i. Security & Access Control (if app has authentication)**

Ask about user roles:

> "Who are the different types of users?
>
> 1. **Just regular users** - Everyone has the same permissions
> 2. **Users + Admins** - Regular users and administrators with extra powers
> 3. **Multiple roles** - Several distinct user types (e.g., viewer, editor, manager, admin)"

**If multiple roles, explore in conversation:**

- What can each role see?
- What can each role do?
- Are there pages only certain roles can access?
- What happens if someone tries to access something they shouldn't?

**Also ask about authentication:**

- How do users log in? (email/password, social login, SSO)
- Password requirements? (for security testing)
- Session timeout? Auto-logout after inactivity?
- Any sensitive operations requiring extra confirmation?

**4j. Data Flow & Integration**

- What data do users create vs what's system-generated?
- Are there workflows that span multiple steps or pages?
- What happens to related data when something is deleted?
- Are there any external systems or APIs to integrate with?
- Any import/export functionality?

**4k. Error & Edge Cases**

- What should happen if the network fails mid-action?
- What about duplicate entries (e.g., same email twice)?
- Very long text inputs?
- Empty states (what shows when there's no data)?

**Keep asking follow-up questions until you have a complete picture.** For each feature area, understand:

- What the user sees
- What actions they can take
- What happens as a result
- Who is allowed to do it (permissions)
- What errors could occur

## Phase 4L: Derive Feature Count (DO NOT ASK THE USER)

After gathering all features, **you** (the agent) should tally up the testable features. Do NOT ask the user how many features they want - derive it from what was discussed.

**Typical ranges for reference:**

- **Simple apps** (todo list, calculator, notes): ~20-50 features
- **Medium apps** (blog, task manager with auth): ~100 features
- **Advanced apps** (e-commerce, CRM, full SaaS): ~150-200 features

These are just reference points - your actual count should come from the requirements discussed.

**How to count features:**
For each feature area discussed, estimate the number of discrete, testable behaviors:

- Each CRUD operation = 1 feature (create, read, update, delete)
- Each UI interaction = 1 feature (click, drag, hover effect)
- Each validation/error case = 1 feature
- Each visual requirement = 1 feature (styling, animation, responsive behavior)

**Present your estimate to the user:**

> "Based on what we discussed, here's my feature breakdown:
>
> - [Category 1]: ~X features
> - [Category 2]: ~Y features
> - [Category 3]: ~Z features
> - ...
>
> **Total: ~N features**
>
> Does this seem right, or should I adjust?"

Let the user confirm or adjust. This becomes your `feature_count` for the spec.

## Phase 5: Technical Details (DERIVED OR DISCUSSED)

**For Quick Mode users:**
Tell them: "Based on what you've described, I'll design the database, API, and architecture. Here's a quick summary of what I'm planning..."

Then briefly outline:

- Main data entities you'll create (in plain language: "I'll create tables for users, projects, documents, etc.")
- Overall app structure ("sidebar navigation with main content area")
- Any key technical decisions

Ask: "Does this sound right? Any concerns?"

**For Detailed Mode users:**
Walk through each technical area:

**5a. Database Design**

- What entities/tables are needed?
- Key fields for each?
- Relationships?

**5b. API Design**

- What endpoints are needed?
- How should they be organized?

**5c. UI Layout**

- Overall structure (columns, navigation)
- Key screens/pages
- Design preferences (colors, themes)

**5d. Implementation Phases**

- What order to build things?
- Dependencies?

## Phase 6: Success Criteria

Ask in simple terms:

> "What does 'done' look like for you? When would you consider this app complete and successful?"

Prompt for:

- Must-have functionality
- Quality expectations (polished vs functional)
- Any specific requirements

## Phase 7: Review & Approval

Present everything gathered:

1. **Summary of the app** (in plain language)
2. **Feature count**
3. **Technology choices** (whether specified or derived)
4. **Brief technical plan** (for their awareness)

First ask in conversation if they want to make changes.

**Then ask for final confirmation:**

> "Ready to generate the specification files?
>
> 1. **Yes, generate files** - Create app_spec.txt and update prompt files
> 2. **I have changes** - Let me add or modify something first"

---

# FILE GENERATION

**Note: This section is for YOU (the agent) to execute. Do not burden the user with these technical details.**

## Output Directory

The output directory is: `$ARGUMENTS/prompts/`

Once the user approves, generate these files:

## 1. Generate `app_spec.txt`

**Output path:** `$ARGUMENTS/prompts/app_spec.txt`

Create a new file using this XML structure:

```xml
<project_specification>
  <project_name>[Project Name]</project_name>

  <overview>
    [2-3 sentence description from Phase 1]
  </overview>

  <technology_stack>
    <frontend>
      <framework>[Framework]</framework>
      <styling>[Styling solution]</styling>
      [Additional frontend config]
    </frontend>
    <backend>
      <runtime>[Runtime]</runtime>
      <database>[Database]</database>
      [Additional backend config]
    </backend>
    <communication>
      <api>[API style]</api>
      [Additional communication config]
    </communication>
  </technology_stack>

  <prerequisites>
    <environment_setup>
      [Setup requirements]
    </environment_setup>
  </prerequisites>

  <feature_count>[derived count from Phase 4L]</feature_count>

  <security_and_access_control>
    <user_roles>
      <role name="[role_name]">
        <permissions>
          - [Can do X]
          - [Can see Y]
          - [Cannot access Z]
        </permissions>
        <protected_routes>
          - /admin/* (admin only)
          - /settings (authenticated users)
        </protected_routes>
      </role>
      [Repeat for each role]
    </user_roles>
    <authentication>
      <method>[email/password | social | SSO]</method>
      <session_timeout>[duration or "none"]</session_timeout>
      <password_requirements>[if applicable]</password_requirements>
    </authentication>
    <sensitive_operations>
      - [Delete account requires password confirmation]
      - [Financial actions require 2FA]
    </sensitive_operations>
  </security_and_access_control>

  <core_features>
    <[category_name]>
      - [Feature 1]
      - [Feature 2]
      - [Feature 3]
    </[category_name]>
    [Repeat for all feature categories]
  </core_features>

  <database_schema>
    <tables>
      <[table_name]>
        - [field1], [field2], [field3]
        - [additional fields]
      </[table_name]>
      [Repeat for all tables]
    </tables>
  </database_schema>

  <api_endpoints_summary>
    <[category]>
      - [VERB] /api/[path]
      - [VERB] /api/[path]
    </[category]>
    [Repeat for all categories]
  </api_endpoints_summary>

  <ui_layout>
    <main_structure>
      [Layout description]
    </main_structure>
    [Additional UI sections as needed]
  </ui_layout>

  <design_system>
    <color_palette>
      [Colors]
    </color_palette>
    <typography>
      [Font preferences]
    </typography>
  </design_system>

  <implementation_steps>
    <step number="1">
      <title>[Phase Title]</title>
      <tasks>
        - [Task 1]
        - [Task 2]
      </tasks>
    </step>
    [Repeat for all phases]
  </implementation_steps>

  <success_criteria>
    <functionality>
      [Functionality criteria]
    </functionality>
    <user_experience>
      [UX criteria]
    </user_experience>
    <technical_quality>
      [Technical criteria]
    </technical_quality>
    <design_polish>
      [Design criteria]
    </design_polish>
  </success_criteria>
</project_specification>
```

## 2. Update `initializer_prompt.md`

**Output path:** `$ARGUMENTS/prompts/initializer_prompt.md`

If the output directory has an existing `initializer_prompt.md`, read it and update the feature count.
If not, copy from `.claude/templates/initializer_prompt.template.md` first, then update.

**CRITICAL: You MUST update the feature count placeholder:**

1. Find the line containing `**[FEATURE_COUNT]**` in the "REQUIRED FEATURE COUNT" section
2. Replace `[FEATURE_COUNT]` with the exact number agreed upon in Phase 4L (e.g., `25`)
3. The result should read like: `You must create exactly **25** features using the...`

**Example edit:**
```
Before: **CRITICAL:** You must create exactly **[FEATURE_COUNT]** features using the `feature_create_bulk` tool.
After:  **CRITICAL:** You must create exactly **25** features using the `feature_create_bulk` tool.
```

**Verify the update:** After editing, read the file again to confirm the feature count appears correctly. If `[FEATURE_COUNT]` still appears in the file, the update failed and you must try again.

**Note:** You may also update `coding_prompt.md` if the user requests changes to how the coding agent should work. Include it in the status file if modified.

## 3. Write Status File (REQUIRED - Do This Last)

**Output path:** `$ARGUMENTS/prompts/.spec_status.json`

**CRITICAL:** After you have completed ALL requested file changes, write this status file to signal completion to the UI. This is required for the "Continue to Project" button to appear.

Write this JSON file:

```json
{
  "status": "complete",
  "version": 1,
  "timestamp": "[current ISO 8601 timestamp, e.g., 2025-01-15T14:30:00.000Z]",
  "files_written": [
    "prompts/app_spec.txt",
    "prompts/initializer_prompt.md"
  ],
  "feature_count": [the feature count from Phase 4L]
}
```

**Include ALL files you modified** in the `files_written` array. If the user asked you to also modify `coding_prompt.md`, include it:

```json
{
  "status": "complete",
  "version": 1,
  "timestamp": "2025-01-15T14:30:00.000Z",
  "files_written": [
    "prompts/app_spec.txt",
    "prompts/initializer_prompt.md",
    "prompts/coding_prompt.md"
  ],
  "feature_count": 35
}
```

**IMPORTANT:**
- Write this file LAST, after all other files are successfully written
- Only write it when you consider ALL requested work complete
- The UI polls this file to detect completion and show the Continue button
- If the user asks for additional changes after you've written this, you may update it again when the new changes are complete

---

# AFTER FILE GENERATION: NEXT STEPS

Once files are generated, tell the user what to do next:

> "Your specification files have been created in `$ARGUMENTS/prompts/`!
>
> **Files created:**
> - `$ARGUMENTS/prompts/app_spec.txt`
> - `$ARGUMENTS/prompts/initializer_prompt.md`
>
> The **Continue to Project** button should now appear. Click it to start the autonomous coding agent!
>
> **If you don't see the button:** Type `/exit` or click **Exit to Project** in the header.
>
> **Important timing expectations:**
>
> - **First session:** The agent generates features in the database. This takes several minutes.
> - **Subsequent sessions:** Each coding iteration takes 5-15 minutes depending on complexity.
> - **Full app:** Building all [X] features will take many hours across multiple sessions.
>
> **Controls:**
>
> - Press `Ctrl+C` to pause the agent at any time
> - Run `start.bat` (Windows) or `./start.sh` (Mac/Linux) to resume where you left off"

Replace `[X]` with their feature count.

---

# IMPORTANT REMINDERS

- **Meet users where they are**: Not everyone is technical. Ask about what they want, not how to build it.
- **Quick Mode is the default**: Most users should be able to describe their app and let you handle the technical details.
- **Derive, don't interrogate**: For non-technical users, derive database schema, API endpoints, and architecture from their feature descriptions. Don't ask them to specify these.
- **Use plain language**: Instead of "What entities need CRUD operations?", ask "What things can users create, edit, or delete?"
- **Be thorough on features**: This is where to spend time. Keep asking follow-up questions until you have a complete picture.
- **Derive feature count, don't guess**: After gathering requirements, tally up testable features yourself and present the estimate. Don't use fixed tiers or ask users to guess.
- **Validate before generating**: Present a summary including your derived feature count and get explicit approval before creating files.

---

# BEGIN

Start by greeting the user warmly. Ask ONLY the Phase 1 questions:

> "Hi! I'm here to help you create a detailed specification for your app.
>
> Let's start with the basics:
>
> 1. What do you want to call this project?
> 2. In your own words, what are you building?
> 3. Who will use it - just you, or others too?"

**STOP HERE and wait for their response.** Do not ask any other questions yet. Do not use AskUserQuestion yet. Just have a conversation about their project basics first.

After they respond, acknowledge what they said, then move to Phase 2.


Now I have a good understanding of the spec creation flow. I need to create a new skill for "refine-spec" that:

1. Loads the existing app_spec.txt content
2. Presents a modified conversation flow where Claude:
   - Starts by acknowledging the existing spec
   - Asks what the user wants to change/improve
   - Has the full context of the existing spec
   - Can suggest improvements based on what it sees
   - Updates the spec when done

Let me create a new `refine-spec.md` skill and update the session handling to support a "refine" mode.

The plan:
1. Create `refine-spec.md` skill that:
   - Loads and displays existing spec to user
   - Allows discussion and refinement
   - Updates both app_spec.txt and initializer_prompt.md

2. Update `spec_chat_session.py` to support:
   - `is_refine` mode that uses refine-spec skill
   - Loads existing spec content for the system prompt

3. Update `spec_creation.py` router to accept `is_refine` parameter

4. Update UI to show "Refine App Spec" option when re-init is clicked for projects with existing specs

Let me start by creating the refine-spec.md skill.


I'll create a comprehensive solution for refining existing app specs. Let me create the refine-spec skill and update the backend to support it.


Write

---
description: Refine an existing app spec for a project
---

# PROJECT DIRECTORY

This command **requires** the project directory as an argument via `$ARGUMENTS`.

**Output location:** `$ARGUMENTS/prompts/app_spec.txt` and `$ARGUMENTS/prompts/initializer_prompt.md`

If `$ARGUMENTS` is empty, inform the user they must provide a project path and exit.

---

# EXISTING SPEC

The project already has an app specification. Here is the current content:

<existing_spec>
$EXISTING_SPEC
</existing_spec>

---

# GOAL

Help the user **refine and improve** their existing app specification. They may want to:

- Add new features
- Remove or modify existing features
- Change technology choices
- Adjust the scope (feature count)
- Fix issues discovered during development
- Pivot the project direction

---

# YOUR ROLE

You are the **Spec Refinement Assistant**. You have full knowledge of the existing specification above. Your job is to:

1. Understand what the user wants to change
2. Discuss the implications of changes
3. Suggest improvements based on best practices
4. Update the specification files when ready

**IMPORTANT:** You already know everything about the current spec. Don't ask users to repeat information you can see above. Reference specific parts of the existing spec when discussing changes.

---

# CONVERSATION FLOW

## Phase 1: Understand the Changes

Start by acknowledging the existing spec and asking what they want to change:

> "I can see your current spec for **[project_name]** with approximately **[feature_count]** features.
>
> **Current overview:** [Brief summary from existing spec]
>
> What would you like to change or improve?
>
> Common reasons for refinement:
> 1. **Add features** - New functionality to include
> 2. **Modify features** - Change how something works
> 3. **Remove features** - Simplify or reduce scope
> 4. **Change tech stack** - Different frameworks or tools
> 5. **Adjust scope** - More or fewer features
> 6. **Fix issues** - Problems found during development
> 7. **Other** - Something else entirely"

**WAIT for their response before continuing.**

## Phase 2: Discuss Details

Based on what they want to change:

**For adding features:**
- What should the new feature do?
- How does it relate to existing features?
- Does it need new database tables or API endpoints?
- Should it integrate with existing functionality?

**For modifying features:**
- What's not working well currently?
- What should it do instead?
- Will this change affect other features?

**For removing features:**
- Why is this feature no longer needed?
- Are other features dependent on it?
- Should related features also be adjusted?

**For tech stack changes:**
- What's driving the change?
- Are there migration concerns?
- How will this affect existing code?

**For scope changes:**
- What's the new target feature count?
- Which features should be prioritized/deprioritized?

**Keep discussing until you have a clear picture of all changes.**

## Phase 3: Impact Summary

Before making changes, summarize what will change:

> "Here's what I'll update in your spec:
>
> **Changes:**
> - [Change 1]
> - [Change 2]
> - [Change 3]
>
> **New feature count:** [X] (was [Y])
>
> **Affected sections:**
> - [Section 1]
> - [Section 2]
>
> Does this look right? Should I proceed with these updates?"

## Phase 4: Generate Updated Files

Once approved:

1. **Update `app_spec.txt`** with all changes
2. **Update `initializer_prompt.md`** with new feature count (if changed)
3. **Write status file** to signal completion

---

# FILE GENERATION

## 1. Update `app_spec.txt`

**Output path:** `$ARGUMENTS/prompts/app_spec.txt`

Read the existing file first, then update it with the agreed changes. Maintain the XML structure:

```xml
<project_specification>
  <!-- Update relevant sections while preserving unchanged content -->
</project_specification>
```

**IMPORTANT:** Don't recreate sections that didn't change. Only modify what was discussed.

## 2. Update `initializer_prompt.md`

**Output path:** `$ARGUMENTS/prompts/initializer_prompt.md`

If the feature count changed:
1. Read the existing file
2. Find the line containing the feature count
3. Update it to the new count

**Example:**
```
Before: **CRITICAL:** You must create exactly **25** features using the `feature_create_bulk` tool.
After:  **CRITICAL:** You must create exactly **35** features using the `feature_create_bulk` tool.
```

## 3. Write Status File

**Output path:** `$ARGUMENTS/prompts/.spec_status.json`

Write this JSON file after all changes are complete:

```json
{
  "status": "complete",
  "version": 1,
  "timestamp": "[current ISO 8601 timestamp]",
  "files_written": [
    "prompts/app_spec.txt",
    "prompts/initializer_prompt.md"
  ],
  "feature_count": [new feature count],
  "refinement": true
}
```

---

# AFTER REFINEMENT

Tell the user what happens next:

> "Your spec has been updated!
>
> **Changes made:**
> - [Summary of changes]
>
> **Next steps:**
> You can now re-initialize the project to regenerate features from this updated spec. This will:
> 1. Reset the feature database (optionally keeping completed features)
> 2. Run the initializer to create features from the new spec
> 3. The coding agent will then implement the new/modified features
>
> Click **Re-initialize** or use the re-init button to start fresh with the updated spec."

---

# IMPORTANT REMINDERS

- **Reference the existing spec**: You have full context. Use it.
- **Preserve what works**: Don't change things unnecessarily.
- **Discuss implications**: Help users understand how changes affect the project.
- **Be conversational**: This is a discussion, not a form.
- **Validate before writing**: Always get approval before modifying files.

---

# BEGIN

Start by greeting the user and showing you understand their current spec:

> "Hi! I see you want to refine your **[project_name]** specification.
>
> **Current state:**
> - [Feature count] features planned
> - Stack: [tech stack summary]
> - Focus: [brief overview]
>
> What would you like to change or improve?"

**Extract the project name, feature count, and overview from the existing spec above. Then WAIT for their response.**