@@ -703,90 +703,89 @@ impl AgentLoop {
703703 }
704704 }
705705
706- /// Inspect the workspace for well-known project marker files and return a short
707- /// `## Project Context` section that the agent can use without any manual configuration.
708- /// Returns an empty string when the workspace type cannot be determined.
709- fn detect_project_hint ( workspace : & std:: path:: Path ) -> String {
710- struct Marker {
711- file : & ' static str ,
712- lang : & ' static str ,
713- tip : & ' static str ,
714- }
715-
716- let markers = [
717- Marker {
718- file : "Cargo.toml" ,
719- lang : "Rust" ,
720- tip : "Use `cargo build`, `cargo test`, `cargo clippy`, and `cargo fmt`. \
706+ /// Inspect the workspace for well-known project marker files and return a short
707+ /// `## Project Context` section that the agent can use without any manual configuration.
708+ /// Returns an empty string when the workspace type cannot be determined.
709+ fn detect_project_hint ( workspace : & std:: path:: Path ) -> String {
710+ struct Marker {
711+ file : & ' static str ,
712+ lang : & ' static str ,
713+ tip : & ' static str ,
714+ }
715+
716+ let markers = [
717+ Marker {
718+ file : "Cargo.toml" ,
719+ lang : "Rust" ,
720+ tip : "Use `cargo build`, `cargo test`, `cargo clippy`, and `cargo fmt`. \
721721 Prefer `anyhow` / `thiserror` for error handling. \
722722 Follow the Microsoft Rust Guidelines (no panics in library code, \
723723 async-first with Tokio).",
724- } ,
725- Marker {
726- file : "package.json" ,
727- lang : "Node.js / TypeScript" ,
728- tip : "Check `package.json` for the package manager (npm/yarn/pnpm/bun) \
724+ } ,
725+ Marker {
726+ file : "package.json" ,
727+ lang : "Node.js / TypeScript" ,
728+ tip : "Check `package.json` for the package manager (npm/yarn/pnpm/bun) \
729729 and available scripts. Prefer TypeScript with strict mode. \
730730 Use ESM imports unless the project is CommonJS.",
731- } ,
732- Marker {
733- file : "pyproject.toml" ,
734- lang : "Python" ,
735- tip : "Use the package manager declared in `pyproject.toml` \
731+ } ,
732+ Marker {
733+ file : "pyproject.toml" ,
734+ lang : "Python" ,
735+ tip : "Use the package manager declared in `pyproject.toml` \
736736 (uv, poetry, hatch, etc.). Prefer type hints and async/await for I/O.",
737- } ,
738- Marker {
739- file : "setup.py" ,
740- lang : "Python" ,
741- tip : "Legacy Python project. Prefer type hints and async/await for I/O." ,
742- } ,
743- Marker {
744- file : "requirements.txt" ,
745- lang : "Python" ,
746- tip : "Python project with pip-style dependencies. \
737+ } ,
738+ Marker {
739+ file : "setup.py" ,
740+ lang : "Python" ,
741+ tip : "Legacy Python project. Prefer type hints and async/await for I/O." ,
742+ } ,
743+ Marker {
744+ file : "requirements.txt" ,
745+ lang : "Python" ,
746+ tip : "Python project with pip-style dependencies. \
747747 Prefer type hints and async/await for I/O.",
748- } ,
749- Marker {
750- file : "go.mod" ,
751- lang : "Go" ,
752- tip : "Use `go build ./...` and `go test ./...`. \
748+ } ,
749+ Marker {
750+ file : "go.mod" ,
751+ lang : "Go" ,
752+ tip : "Use `go build ./...` and `go test ./...`. \
753753 Follow standard Go project layout. Use `gofmt` for formatting.",
754- } ,
755- Marker {
756- file : "pom.xml" ,
757- lang : "Java / Maven" ,
758- tip : "Use `mvn compile`, `mvn test`, `mvn package`. \
754+ } ,
755+ Marker {
756+ file : "pom.xml" ,
757+ lang : "Java / Maven" ,
758+ tip : "Use `mvn compile`, `mvn test`, `mvn package`. \
759759 Follow standard Maven project structure.",
760- } ,
761- Marker {
762- file : "build.gradle" ,
763- lang : "Java / Gradle" ,
764- tip : "Use `./gradlew build` and `./gradlew test`. \
760+ } ,
761+ Marker {
762+ file : "build.gradle" ,
763+ lang : "Java / Gradle" ,
764+ tip : "Use `./gradlew build` and `./gradlew test`. \
765765 Follow standard Gradle project structure.",
766- } ,
767- Marker {
768- file : "build.gradle.kts" ,
769- lang : "Kotlin / Gradle" ,
770- tip : "Use `./gradlew build` and `./gradlew test`. \
766+ } ,
767+ Marker {
768+ file : "build.gradle.kts" ,
769+ lang : "Kotlin / Gradle" ,
770+ tip : "Use `./gradlew build` and `./gradlew test`. \
771771 Prefer Kotlin coroutines for async work.",
772- } ,
773- Marker {
774- file : "CMakeLists.txt" ,
775- lang : "C / C++" ,
776- tip : "Use `cmake -B build && cmake --build build`. \
772+ } ,
773+ Marker {
774+ file : "CMakeLists.txt" ,
775+ lang : "C / C++" ,
776+ tip : "Use `cmake -B build && cmake --build build`. \
777777 Check for `compile_commands.json` for IDE tooling.",
778- } ,
779- Marker {
780- file : "Makefile" ,
781- lang : "C / C++ (or generic)" ,
782- tip : "Use `make` or `make <target>`. \
778+ } ,
779+ Marker {
780+ file : "Makefile" ,
781+ lang : "C / C++ (or generic)" ,
782+ tip : "Use `make` or `make <target>`. \
783783 Check available targets with `make help` or by reading the Makefile.",
784- } ,
785- ] ;
784+ } ,
785+ ] ;
786786
787- // Check for C# / .NET — no single fixed filename, so glob for *.csproj / *.sln
788- let is_dotnet = workspace. join ( "*.csproj" ) . exists ( )
789- || {
787+ // Check for C# / .NET — no single fixed filename, so glob for *.csproj / *.sln
788+ let is_dotnet = workspace. join ( "*.csproj" ) . exists ( ) || {
790789 // Fast check: look for any .csproj or .sln in the workspace root
791790 std:: fs:: read_dir ( workspace)
792791 . map ( |entries| {
@@ -799,31 +798,30 @@ fn detect_project_hint(workspace: &std::path::Path) -> String {
799798 . unwrap_or ( false )
800799 } ;
801800
802- if is_dotnet {
803- return format ! (
804- "## Project Context\n \n This is a **C# / .NET** project. \
801+ if is_dotnet {
802+ return "## Project Context\n \n This is a **C# / .NET** project. \
805803 Use `dotnet build`, `dotnet test`, and `dotnet run`. \
806804 Follow C# coding conventions and async/await patterns."
807- ) ;
808- }
805+ . to_string ( ) ;
806+ }
809807
810- for marker in & markers {
811- if workspace. join ( marker. file ) . exists ( ) {
812- return format ! (
813- "## Project Context\n \n This is a **{}** project. {}" ,
814- marker. lang, marker. tip
815- ) ;
808+ for marker in & markers {
809+ if workspace. join ( marker. file ) . exists ( ) {
810+ return format ! (
811+ "## Project Context\n \n This is a **{}** project. {}" ,
812+ marker. lang, marker. tip
813+ ) ;
814+ }
816815 }
817- }
818816
819- String :: new ( )
820- }
817+ String :: new ( )
818+ }
821819
822- /// Returns `true` for errors that are likely transient (network, timeout, I/O contention).
823- /// Used to annotate tool error messages so the LLM knows whether retrying is safe.
824- fn is_transient_error ( msg : & str ) -> bool {
825- let lower = msg. to_lowercase ( ) ;
826- lower. contains ( "timeout" )
820+ /// Returns `true` for errors that are likely transient (network, timeout, I/O contention).
821+ /// Used to annotate tool error messages so the LLM knows whether retrying is safe.
822+ fn is_transient_error ( msg : & str ) -> bool {
823+ let lower = msg. to_lowercase ( ) ;
824+ lower. contains ( "timeout" )
827825 || lower. contains ( "timed out" )
828826 || lower. contains ( "connection refused" )
829827 || lower. contains ( "connection reset" )
@@ -836,25 +834,25 @@ fn is_transient_error(msg: &str) -> bool {
836834 || lower. contains ( "too many requests" )
837835 || lower. contains ( "service unavailable" )
838836 || lower. contains ( "network unreachable" )
839- }
837+ }
840838
841- /// Returns `true` when a tool writes a file and is safe to run concurrently with other
842- /// independent writes (no ordering dependencies, no side-channel output).
843- fn is_parallel_safe_write ( name : & str , _args : & serde_json:: Value ) -> bool {
844- matches ! (
845- name,
846- "write_file" | "edit_file" | "create_file" | "append_to_file" | "replace_in_file"
847- )
848- }
839+ /// Returns `true` when a tool writes a file and is safe to run concurrently with other
840+ /// independent writes (no ordering dependencies, no side-channel output).
841+ fn is_parallel_safe_write ( name : & str , _args : & serde_json:: Value ) -> bool {
842+ matches ! (
843+ name,
844+ "write_file" | "edit_file" | "create_file" | "append_to_file" | "replace_in_file"
845+ )
846+ }
849847
850- /// Extract the target file path from write-tool arguments so we can check for conflicts.
851- fn extract_write_path ( args : & serde_json:: Value ) -> Option < String > {
852- // write_file / create_file / append_to_file / replace_in_file use "path"
853- // edit_file uses "path" as well
854- args. get ( "path" )
855- . and_then ( |v| v. as_str ( ) )
856- . map ( |s| s. to_string ( ) )
857- }
848+ /// Extract the target file path from write-tool arguments so we can check for conflicts.
849+ fn extract_write_path ( args : & serde_json:: Value ) -> Option < String > {
850+ // write_file / create_file / append_to_file / replace_in_file use "path"
851+ // edit_file uses "path" as well
852+ args. get ( "path" )
853+ . and_then ( |v| v. as_str ( ) )
854+ . map ( |s| s. to_string ( ) )
855+ }
858856
859857 /// Execute a tool through the lane queue (if configured) or directly.
860858 async fn execute_tool_queued_or_direct (
@@ -1992,8 +1990,7 @@ fn extract_write_path(args: &serde_json::Value) -> Option<String> {
19921990 paths. len ( ) == tool_calls. len ( )
19931991 && paths. iter ( ) . collect :: < std:: collections:: HashSet < _ > > ( ) . len ( )
19941992 == paths. len ( )
1995- }
1996- {
1993+ } {
19971994 tracing:: info!(
19981995 count = tool_calls. len( ) ,
19991996 "Parallel write batch: executing {} independent file writes concurrently" ,
0 commit comments