File size: 2,101 Bytes
93df7ed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
from dataclasses import dataclass, asdict
from llm import get_llm_client, build_prompt
from rag.rag_chain import build_context
from rag.retriever import retrieve_relevant_chunks


@dataclass
class FileProposal:
    path: str
    content: str
    reason: str
    approved: bool = False


def propose_new_file(description: str, context_query: str | None = None) -> FileProposal:
    context = ""
    if context_query:
        related_chunks = retrieve_relevant_chunks(context_query, k=5)
        context = build_context(related_chunks)

    prompt = build_prompt(description, context, task_type="file_creation")
    content = get_llm_client().generate(prompt)

    proposed_path = infer_file_path(description)

    return FileProposal(path=proposed_path, content=content, reason=description)


def infer_file_path(description: str) -> str:
    prompt = f"Given this file creation request: '{description}', respond with ONLY a suitable relative file path, nothing else."
    path = get_llm_client().generate(prompt).strip()
    return path


async def apply_approved_file(proposal: FileProposal, user_confirmed: bool, base_dir: str = ".") -> dict:
    """Write the proposed file to local disk under *base_dir*.



    The LLM-suggested path (e.g. /src/geometry/rectangle.js) is treated as

    relative to *base_dir*, so leading slashes/backslashes are stripped before

    joining to avoid accidental absolute-path writes.

    """
    if not user_confirmed:
        return {"status": "rejected", "proposal": asdict(proposal)}

    import os

    # Strip any leading separators so the path is always relative to base_dir
    relative_path = proposal.path.lstrip("/\\")
    abs_path = os.path.join(base_dir, relative_path)

    # Create parent directories if they don't exist
    os.makedirs(os.path.dirname(abs_path), exist_ok=True)

    with open(abs_path, "w", encoding="utf-8") as f:
        f.write(proposal.content)

    proposal.path = abs_path  # update so the caller can display the real path
    return {"status": "written", "path": abs_path}