22
33from __future__ import annotations
44
5+ from collections .abc import Sequence
6+ import contextlib
57import dataclasses
6- from pathlib import Path
8+ from pathlib import Path , PurePosixPath
9+ from typing import Protocol
710
811from ..common import ensure_state_home , qualified_class_name
9- from ..toolbox import Toolbox
1012
1113
1214@dataclasses .dataclass (frozen = True )
@@ -17,8 +19,51 @@ class Goal:
1719 # TODO: Add timeout.
1820
1921
22+ class Worktree (Protocol ):
23+ """File operations
24+
25+ Implementations may not be thread-safe. Concurrent operations should be
26+ serialized by the caller.
27+ """
28+
29+ def list_files (self ) -> Sequence [PurePosixPath ]:
30+ """List all files"""
31+
32+ def read_file (self , path : PurePosixPath ) -> str | None :
33+ """Get a file's contents"""
34+
35+ def write_file (self , path : PurePosixPath , contents : str ) -> None :
36+ """Update a file's contents"""
37+
38+ def delete_file (self , path : PurePosixPath ) -> None :
39+ """Remove a file"""
40+
41+ def rename_file (
42+ self , src_path : PurePosixPath , dst_path : PurePosixPath
43+ ) -> None :
44+ """Move a file"""
45+
46+ def edit_files (self ) -> contextlib .AbstractContextManager [Path ]:
47+ """Return path to a temporary folder with editable copies of all files
48+
49+ Any updates are synced back to the work tree when the context exits.
50+ Other operations should not be performed concurrently as they may be
51+ stale or lost.
52+ """
53+
54+
55+ class UserFeedback (Protocol ):
56+ """User interactions"""
57+
58+ def notify (self , update : str ) -> None :
59+ """Report progress to the user"""
60+
61+ def ask (self , question : str ) -> str :
62+ """Request additional information from the user"""
63+
64+
2065@dataclasses .dataclass
21- class Action :
66+ class ActionSummary :
2267 """End-of-action statistics
2368
2469 This dataclass is not frozen to allow bot implementors to populate its
@@ -28,7 +73,6 @@ class Action:
2873 title : str | None = None
2974 request_count : int | None = None
3075 token_count : int | None = None
31- question : str | None = None
3276
3377 def increment_request_count (self , n : int = 1 , init : bool = False ) -> None :
3478 self ._increment ("request_count" , n , init )
@@ -66,6 +110,8 @@ def state_folder_path(cls, ensure_exists: bool = False) -> Path:
66110 path .mkdir (parents = True , exist_ok = True )
67111 return path
68112
69- async def act (self , goal : Goal , toolbox : Toolbox ) -> Action :
70- """Runs the bot, striving to achieve the goal with the given toolbox"""
113+ async def act (
114+ self , goal : Goal , tree : Worktree , feedback : UserFeedback
115+ ) -> ActionSummary :
116+ """Runs the bot, striving to achieve the goal"""
71117 raise NotImplementedError ()
0 commit comments