From 55271fe3cd4b9930d8658b137b0a58ac3c5f7425 Mon Sep 17 00:00:00 2001 From: John Kennedy <65985482+jkennedyvz@users.noreply.github.com> Date: Sat, 28 Feb 2026 13:36:52 -0800 Subject: [PATCH 1/2] perf(cli): parallelize detect script for faster first-turn Parallelize independent bash sections in LocalContextMiddleware's detect script using background jobs. Header + project run first (set CWD/IN_GIT/ROOT), then 7 sections run concurrently writing to temp files. First-turn overhead reduced ~22% (0.298s -> 0.232s). --- libs/cli/deepagents_cli/local_context.py | 41 +++++++++++++++++------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/libs/cli/deepagents_cli/local_context.py b/libs/cli/deepagents_cli/local_context.py index d06fa61893..e48855507e 100644 --- a/libs/cli/deepagents_cli/local_context.py +++ b/libs/cli/deepagents_cli/local_context.py @@ -300,21 +300,40 @@ def _section_makefile() -> str: def build_detect_script() -> str: """Concatenate all section functions into the full detection script. + Independent sections run as parallel background jobs writing to temp + files, then results are concatenated in the original display order. + The header (CWD / IN_GIT) and project section (sets ROOT) run first + because later sections depend on their variables. + Returns: Complete bash heredoc ready for `backend.execute()`. """ - sections = [ - _section_header(), - _section_project(), - _section_package_managers(), - _section_runtimes(), - _section_git(), - _section_test_command(), - _section_files(), - _section_tree(), - _section_makefile(), + # Header + project run synchronously (set CWD, IN_GIT, ROOT for others) + serial_prefix = f"{_section_header()}\n{_section_project()}" + + # These sections are independent — run them in parallel + parallel_sections = [ + ("02_pkgmgr", _section_package_managers()), + ("03_runtimes", _section_runtimes()), + ("04_git", _section_git()), + ("05_testcmd", _section_test_command()), + ("06_files", _section_files()), + ("07_tree", _section_tree()), + ("08_makefile", _section_makefile()), ] - body = "\n".join(sections) + + # Build parallel wrapper: each section runs in a subshell writing to a temp file + parallel_setup = '_DCT=$(mktemp -d)\ntrap "rm -rf $_DCT" EXIT' + parallel_jobs = [] + cat_args = [] + for name, section_body in parallel_sections: + parallel_jobs.append(f'(\n{section_body}\n) > "$_DCT/{name}" &') + cat_args.append(f'"$_DCT/{name}"') + + parallel_block = "\n".join(parallel_jobs) + cat_line = "cat " + " ".join(cat_args) + + body = f"{serial_prefix}\n{parallel_setup}\n{parallel_block}\nwait\n{cat_line}" return f"bash <<'__DETECT_CONTEXT_EOF__'\n{body}\n__DETECT_CONTEXT_EOF__\n" From ddcf6c9df9a5269701e61bec135c6eda564686f2 Mon Sep 17 00:00:00 2001 From: Mason Daugherty Date: Sun, 1 Mar 2026 18:43:43 -0500 Subject: [PATCH 2/2] cr --- libs/cli/deepagents_cli/local_context.py | 28 +++++++++++++----------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/libs/cli/deepagents_cli/local_context.py b/libs/cli/deepagents_cli/local_context.py index e48855507e..9e6a1dd7b7 100644 --- a/libs/cli/deepagents_cli/local_context.py +++ b/libs/cli/deepagents_cli/local_context.py @@ -53,7 +53,8 @@ def execute(self, command: str) -> ExecuteResponse: ... # with `command -v` before use. # # The script is built from section functions so each piece can be tested -# independently. +# independently. Independent sections run as parallel background subshells; +# see build_detect_script() for the orchestration logic. # --------------------------------------------------------------------------- @@ -278,7 +279,7 @@ def _section_makefile() -> str: """First 20 lines of Makefile (falls back to git root in monorepos). Returns: - Bash snippet (requires `ROOT` from `_section_project`). + Bash snippet (requires `ROOT` from `_section_project` and `CWD` from header). """ return r"""# --- Makefile --- MK="" @@ -311,7 +312,10 @@ def build_detect_script() -> str: # Header + project run synchronously (set CWD, IN_GIT, ROOT for others) serial_prefix = f"{_section_header()}\n{_section_project()}" - # These sections are independent — run them in parallel + # These sections are independent — run them in parallel. + # Subshells inherit parent variables (IN_GIT, ROOT, CWD) via fork. + # Individual exit codes are not tracked because sections legitimately + # exit non-zero when they have nothing to report (e.g. no runtimes). parallel_sections = [ ("02_pkgmgr", _section_package_managers()), ("03_runtimes", _section_runtimes()), @@ -322,16 +326,14 @@ def build_detect_script() -> str: ("08_makefile", _section_makefile()), ] - # Build parallel wrapper: each section runs in a subshell writing to a temp file - parallel_setup = '_DCT=$(mktemp -d)\ntrap "rm -rf $_DCT" EXIT' - parallel_jobs = [] - cat_args = [] - for name, section_body in parallel_sections: - parallel_jobs.append(f'(\n{section_body}\n) > "$_DCT/{name}" &') - cat_args.append(f'"$_DCT/{name}"') - - parallel_block = "\n".join(parallel_jobs) - cat_line = "cat " + " ".join(cat_args) + # Build parallel wrapper: each section runs in a subshell writing to a + # temp file. Stderr is captured per-section to prevent noise leakage. + parallel_setup = "_DCT=$(mktemp -d) || exit 1\ntrap 'rm -rf \"$_DCT\"' EXIT" + parallel_block = "\n".join( + f'(\n{body}\n) > "$_DCT/{name}" 2>"$_DCT/{name}.err" &' + for name, body in parallel_sections + ) + cat_line = "cat " + " ".join(f'"$_DCT/{name}"' for name, _ in parallel_sections) body = f"{serial_prefix}\n{parallel_setup}\n{parallel_block}\nwait\n{cat_line}" return f"bash <<'__DETECT_CONTEXT_EOF__'\n{body}\n__DETECT_CONTEXT_EOF__\n"