python-agent-harness 1.5.6.3__tar.gz → 1.5.6.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. {python_agent_harness-1.5.6.3/python_agent_harness.egg-info → python_agent_harness-1.5.6.4}/PKG-INFO +2 -3
  2. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/README.md +1 -2
  3. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/pyproject.toml +1 -1
  4. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/__init__.py +1 -4
  5. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/core/agent.py +14 -0
  6. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/entry/cli.py +0 -13
  7. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/entry/headless.py +3 -4
  8. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/agent.md +1 -0
  9. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/agents/reviewer.md +0 -2
  10. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/session/config.py +0 -38
  11. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/session/session.py +2 -10
  12. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/__init__.py +1 -8
  13. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/base.py +1 -2
  14. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/bash.py +2 -0
  15. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/diffapply.py +1 -1
  16. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/edit.py +4 -1
  17. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/filesystem.py +6 -96
  18. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/grep.py +0 -1
  19. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tui/core.py +1 -1
  20. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tui/render.py +12 -0
  21. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4/python_agent_harness.egg-info}/PKG-INFO +2 -3
  22. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness.egg-info/SOURCES.txt +0 -7
  23. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/tests/test_invariants.py +16 -17
  24. python_agent_harness-1.5.6.3/python_agent_harness/lsp/__init__.py +0 -14
  25. python_agent_harness-1.5.6.3/python_agent_harness/lsp/client.py +0 -493
  26. python_agent_harness-1.5.6.3/python_agent_harness/lsp/config.py +0 -108
  27. python_agent_harness-1.5.6.3/python_agent_harness/lsp/manager.py +0 -117
  28. python_agent_harness-1.5.6.3/python_agent_harness/tools/insert.py +0 -87
  29. python_agent_harness-1.5.6.3/python_agent_harness/tools/lsp.py +0 -287
  30. python_agent_harness-1.5.6.3/python_agent_harness/tools/mkdir.py +0 -32
  31. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/LICENSE +0 -0
  32. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/__main__.py +0 -0
  33. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/core/__init__.py +0 -0
  34. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/core/context_manager.py +0 -0
  35. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/core/models.py +0 -0
  36. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/core/planmode.py +0 -0
  37. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/core/subagent.py +0 -0
  38. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/core/token_estimator.py +0 -0
  39. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/core/tool_runner.py +0 -0
  40. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/entry/__init__.py +0 -0
  41. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/entry/controller.py +0 -0
  42. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/entry/server.py +0 -0
  43. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/entry/view.py +0 -0
  44. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/io/__init__.py +0 -0
  45. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/io/attachments.py +0 -0
  46. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/io/diffrender.py +0 -0
  47. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/io/persistence.py +0 -0
  48. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/io/text_filter.py +0 -0
  49. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/llm/__init__.py +0 -0
  50. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/llm/client.py +0 -0
  51. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/mcp/__init__.py +0 -0
  52. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/mcp/client.py +0 -0
  53. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/mcp/config.py +0 -0
  54. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/mcp/manager.py +0 -0
  55. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/__init__.py +0 -0
  56. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/build-switch.md +0 -0
  57. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/commands/explain.md +0 -0
  58. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/compact.md +0 -0
  59. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/core.py +0 -0
  60. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/initialize.md +0 -0
  61. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/plan-mode.md +0 -0
  62. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/plan.md +0 -0
  63. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/review.md +0 -0
  64. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/subagent.md +0 -0
  65. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/summary.md +0 -0
  66. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/task-completion-rules.md +0 -0
  67. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/prompts/title.md +0 -0
  68. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/session/__init__.py +0 -0
  69. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/session/commands.py +0 -0
  70. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/agent_tool.py +0 -0
  71. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/edit_mac.py +0 -0
  72. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/glob.py +0 -0
  73. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/glob_mac.py +0 -0
  74. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/grep_mac.py +0 -0
  75. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/mcp.py +0 -0
  76. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/planexit.py +0 -0
  77. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/question.py +0 -0
  78. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/read.py +0 -0
  79. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/skill.py +0 -0
  80. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/todo.py +0 -0
  81. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tools/write.py +0 -0
  82. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tui/__init__.py +0 -0
  83. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tui/commands.py +0 -0
  84. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness/tui/input.py +0 -0
  85. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness.egg-info/dependency_links.txt +0 -0
  86. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness.egg-info/entry_points.txt +0 -0
  87. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness.egg-info/requires.txt +0 -0
  88. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/python_agent_harness.egg-info/top_level.txt +0 -0
  89. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/setup.cfg +0 -0
  90. {python_agent_harness-1.5.6.3 → python_agent_harness-1.5.6.4}/tests/test_scenarios.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-agent-harness
3
- Version: 1.5.6.3
3
+ Version: 1.5.6.4
4
4
  Summary: A lightweight, hackable mini-OpenCode written in Python.
5
5
  Author-email: Huming Chen <chenhuming@gmail.com>
6
6
  License-Expression: MIT
@@ -74,7 +74,7 @@ Edit `~/.config/python-agent-harness/config.json` and set your `base_url`, `api_
74
74
  - **FSM-driven execution** — explicit `WAIT` / `TOOL` / `TRET` / `SUPERVISE` / `DONE` / `ERRS` / `ABRT` states. Completion supervision nudges the model when it stops early; failed tool calls are sanitized. Transient API failures (`429` / `5xx`) retry with exponential backoff. Auth-expired codes (configurable via `AUTH_REFRESH_STATUS_CODES` in `config.py`; defaults to `[401, 502]`) trigger automatic API key re-read from config/env.
75
75
  - **Context management** — CJK-aware token estimation, per-model context windows, automatic compaction at 70% usage.
76
76
  - **Image & text attachments** — attach with `@path` (e.g. `@screenshot.png`, `@README.md`): images (PNG/JPEG/GIF/WebP, up to 20 MB) become multimodal `image_url` parts; text files are inlined. Web URLs supported for images.
77
- - **Coding tools** — `Agent`, `TodoWrite`, `Glob`, `Grep`, `Read`, `Insert`, `Edit` (incl. unified diffs), `Write`, `Mkdir`, `Bash`, `Skill`, `Question`, `LSP`, `PlanExit`. Read-only tool rounds (`Read`, `Glob`, `Grep`, `Skill`, `LSP`) dispatch concurrently; async tools (`Bash`, `Agent`) can also run concurrently. Results always delivered in emitted order.
77
+ - **Coding tools** — `Agent`, `TodoWrite`, `Glob`, `Grep`, `Read`, `Edit` (incl. unified diffs), `Write`, `Bash`, `Skill`, `Question`, `PlanExit`. Read-only tool rounds (`Read`, `Glob`, `Grep`, `Skill`) dispatch concurrently; async tools (`Bash`, `Agent`) can also run concurrently. Results always delivered in emitted order.
78
78
  - **Plan / Build modes** — plan mode is read-only except for the per-session plan file.
79
79
  - **Persistent sessions** — auto-saved after every response to `~/.local/share/python-agent-harness/sessions/`, with LLM-generated titles and `/restore --latest` / `/sessions`.
80
80
  - **Focused TUI** — Rich-based with pinned status bar, Todos panel, inline diff rendering, and a `prompt_toolkit` editor with history and completion. `Esc+Enter` submits, `Ctrl-D` quits, `Ctrl-C` cancels.
@@ -191,7 +191,6 @@ All LLM settings live in `~/.config/python-agent-harness/config.json`. Environme
191
191
  - **`subagent_llm`** — LLM config for `Agent` tool. Unset values inherit from `llm`. Set `profile` to reuse a `models` entry. Precedence: profile > `subagent_llm` > `llm` > env vars.
192
192
  - **`default_agent`** — agent name to use at session start (must exist in `prompts/agents/`). `null` = built-in default.
193
193
  - **`paths.context_path` / `paths.skill_path`** — locations for context files and skills. Default: project-local `contexts/` and `skills/`.
194
- - **`lsp.servers`** — per-extension LSP server overrides for the `LSP` tool. Keys are extensions (`.py`, `.cpp`); each has `command` (argv) and optional `language_id`. Layers on top of `DEFAULT_SERVERS` in `lsp/manager.py`.
195
194
  - **`mcp.servers`** — MCP server config (requires `[mcp]` extra). Each supports `transport`, `command`, `args`, `env`, `url`, `headers`, `parallel`, `timeout`, `enabled`.
196
195
  - **`headless` / `serve`** — optional budget limits: `max_rounds` (LLM rounds) and `timeout` (wall-clock seconds). `--max-rounds` / `--timeout` override per call; `<= 0` or `null` disables. Unset = unlimited.
197
196
  - **Precedence** — code defaults < config file < `OPENAI_*` env vars. Sub-agents also support `OPENAI_SUBAGENT_*` (`_BASE_URL`, `_API_KEY`, `_MODEL`).
@@ -40,7 +40,7 @@ Edit `~/.config/python-agent-harness/config.json` and set your `base_url`, `api_
40
40
  - **FSM-driven execution** — explicit `WAIT` / `TOOL` / `TRET` / `SUPERVISE` / `DONE` / `ERRS` / `ABRT` states. Completion supervision nudges the model when it stops early; failed tool calls are sanitized. Transient API failures (`429` / `5xx`) retry with exponential backoff. Auth-expired codes (configurable via `AUTH_REFRESH_STATUS_CODES` in `config.py`; defaults to `[401, 502]`) trigger automatic API key re-read from config/env.
41
41
  - **Context management** — CJK-aware token estimation, per-model context windows, automatic compaction at 70% usage.
42
42
  - **Image & text attachments** — attach with `@path` (e.g. `@screenshot.png`, `@README.md`): images (PNG/JPEG/GIF/WebP, up to 20 MB) become multimodal `image_url` parts; text files are inlined. Web URLs supported for images.
43
- - **Coding tools** — `Agent`, `TodoWrite`, `Glob`, `Grep`, `Read`, `Insert`, `Edit` (incl. unified diffs), `Write`, `Mkdir`, `Bash`, `Skill`, `Question`, `LSP`, `PlanExit`. Read-only tool rounds (`Read`, `Glob`, `Grep`, `Skill`, `LSP`) dispatch concurrently; async tools (`Bash`, `Agent`) can also run concurrently. Results always delivered in emitted order.
43
+ - **Coding tools** — `Agent`, `TodoWrite`, `Glob`, `Grep`, `Read`, `Edit` (incl. unified diffs), `Write`, `Bash`, `Skill`, `Question`, `PlanExit`. Read-only tool rounds (`Read`, `Glob`, `Grep`, `Skill`) dispatch concurrently; async tools (`Bash`, `Agent`) can also run concurrently. Results always delivered in emitted order.
44
44
  - **Plan / Build modes** — plan mode is read-only except for the per-session plan file.
45
45
  - **Persistent sessions** — auto-saved after every response to `~/.local/share/python-agent-harness/sessions/`, with LLM-generated titles and `/restore --latest` / `/sessions`.
46
46
  - **Focused TUI** — Rich-based with pinned status bar, Todos panel, inline diff rendering, and a `prompt_toolkit` editor with history and completion. `Esc+Enter` submits, `Ctrl-D` quits, `Ctrl-C` cancels.
@@ -157,7 +157,6 @@ All LLM settings live in `~/.config/python-agent-harness/config.json`. Environme
157
157
  - **`subagent_llm`** — LLM config for `Agent` tool. Unset values inherit from `llm`. Set `profile` to reuse a `models` entry. Precedence: profile > `subagent_llm` > `llm` > env vars.
158
158
  - **`default_agent`** — agent name to use at session start (must exist in `prompts/agents/`). `null` = built-in default.
159
159
  - **`paths.context_path` / `paths.skill_path`** — locations for context files and skills. Default: project-local `contexts/` and `skills/`.
160
- - **`lsp.servers`** — per-extension LSP server overrides for the `LSP` tool. Keys are extensions (`.py`, `.cpp`); each has `command` (argv) and optional `language_id`. Layers on top of `DEFAULT_SERVERS` in `lsp/manager.py`.
161
160
  - **`mcp.servers`** — MCP server config (requires `[mcp]` extra). Each supports `transport`, `command`, `args`, `env`, `url`, `headers`, `parallel`, `timeout`, `enabled`.
162
161
  - **`headless` / `serve`** — optional budget limits: `max_rounds` (LLM rounds) and `timeout` (wall-clock seconds). `--max-rounds` / `--timeout` override per call; `<= 0` or `null` disables. Unset = unlimited.
163
162
  - **Precedence** — code defaults < config file < `OPENAI_*` env vars. Sub-agents also support `OPENAI_SUBAGENT_*` (`_BASE_URL`, `_API_KEY`, `_MODEL`).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-agent-harness"
7
- version = "1.5.6.3"
7
+ version = "1.5.6.4"
8
8
  description = "A lightweight, hackable mini-OpenCode written in Python."
9
9
  authors = [
10
10
  {name = "Huming Chen", email = "chenhuming@gmail.com"}
@@ -3,20 +3,17 @@
3
3
  from .core.models import AgentMode, ImagePart, Message, TextPart, ToolCall, ToolSpec
4
4
  from .entry.cli import main # noqa: F401 (public lib surface: main entry point)
5
5
  from .llm.client import Client, LLMClient
6
- from .lsp.config import LSPConfig, LSPServerConfig
7
6
  from .mcp.config import MCPConfig, MCPServerConfig
8
7
  from .mcp.manager import MCPManager
9
8
  from .session.session import Session
10
9
 
11
- __version__ = "1.5.6.3"
10
+ __version__ = "1.5.6.4"
12
11
 
13
12
  __all__ = [
14
13
  "Client",
15
14
  "LLMClient",
16
15
  "Session",
17
16
  "AgentMode",
18
- "LSPConfig",
19
- "LSPServerConfig",
20
17
  "MCPConfig",
21
18
  "MCPManager",
22
19
  "MCPServerConfig",
@@ -552,6 +552,20 @@ class AgentLoop:
552
552
  self._just_compacted = True
553
553
  self.info["compacted"] = True
554
554
  return
555
+ # Compaction failed (empty summary, no reducible
556
+ # prompts, or an exception -- see ContextManager.
557
+ # compact): the history is unchanged, so without a
558
+ # guard the next WAIT round sees the same
559
+ # above-trigger ratio and compacts again, re-billing
560
+ # the summary request every round for nothing.
561
+ # Remember the current ratio as the floor -- reusing
562
+ # the same mechanism as the "still above trigger
563
+ # after a successful compaction" case above -- so
564
+ # the guard below suppresses re-compaction until the
565
+ # context grows meaningfully past it, at which point
566
+ # a retry is warranted (the conversation really did
567
+ # grow) rather than a no-op loop.
568
+ self._compaction_floor = ratio
555
569
  else:
556
570
  self._compaction_floor = None
557
571
  self._just_compacted = False
@@ -45,11 +45,6 @@ def make_session(
45
45
  settings = config.load_llm_config(config_path)
46
46
  paths = config.load_paths_config(config_path)
47
47
  mcp_config = config.load_mcp_config(config_path)
48
- # Validate lsp.servers eagerly so a malformed override fails fast at
49
- # session start (with a clear message) instead of poisoning the first
50
- # LSP tool call mid-session. The LSP manager re-reads it lazily via
51
- # config_path; this call only surfaces config errors early.
52
- config.load_lsp_config(config_path)
53
48
  model = model or settings["model"]
54
49
  # resolve sub-agent overrides against the EFFECTIVE main settings
55
50
  # (so a CLI/caller model override is inherited too when the
@@ -345,14 +340,6 @@ def cmd_config(args: argparse.Namespace) -> int:
345
340
  print(f" {pattern}: {size}")
346
341
  else:
347
342
  print("context_windows: (none configured — built-in table in config.py applies)")
348
- # Show LSP server overrides (per-extension, layered over the built-ins)
349
- lsp_config = config.load_lsp_config(args.path)
350
- if lsp_config.servers:
351
- print("lsp servers:")
352
- for ext, server in sorted(lsp_config.servers.items()):
353
- print(f" {ext}: language_id={server.language_id}, command={' '.join(server.command)}")
354
- else:
355
- print("lsp servers: (none configured — built-in DEFAULT_SERVERS in lsp/manager.py applies)")
356
343
  # Show default agent
357
344
  default_agent = config.load_default_agent(args.path)
358
345
  print(f"default_agent: {default_agent or '(default: agent.md)'}")
@@ -59,8 +59,8 @@ def _as_error(value: Any) -> dict[str, Any]:
59
59
  ``HeadlessView.errors`` accumulates whatever the notifier passed:
60
60
  the agent loop sends a structured ``{"code", "message"}`` for
61
61
  classified failures (``budget``/``timeout``), other sites send a
62
- plain string whose code is then derived from the text, and the LSP
63
- client sends a ``{"message": ...}`` dict. A nested
62
+ plain string whose code is then derived from the text, and some
63
+ callers send a ``{"message": ...}`` dict. A nested
64
64
  ``{"error": {...}}`` shape is unwrapped; a dict carrying only a
65
65
  ``code`` falls back to that code for its message so the two never
66
66
  disagree on emptiness.
@@ -106,8 +106,7 @@ def error_display_text(value: Any) -> str:
106
106
 
107
107
  ``notify("error", ...)`` carries either a plain message string or a
108
108
  structured ``{"code", "message"}`` payload (the agent loop sends
109
- the latter where the outcome is known; the LSP client sends a
110
- ``{"message": ...}`` dict too). Views render this rather than the
109
+ the latter where the outcome is known). Views render this rather than the
111
110
  raw value, so a structured error does not reach a human as a dict
112
111
  repr.
113
112
 
@@ -56,6 +56,7 @@ The user will primarily request you perform software engineering tasks. This inc
56
56
  - Use the available search tools to understand the codebase and the user's query. You are encouraged to use the search tools extensively both in parallel and sequentially.
57
57
  - Implement the solution using all tools available to you
58
58
  - Verify the solution if possible with tests. NEVER assume specific test framework or test script. Check the README or search codebase to determine the testing approach.
59
+ - Test edge cases to ensure your solution is robust.
59
60
  - VERY IMPORTANT:
60
61
  - When you have completed a task, you MUST run the lint and typecheck commands (e.g. npm run lint, npm run typecheck, ruff, etc.) with Bash if they were provided to you to ensure your code is correct. If you are unable to find the correct command, ask the user for the command to run and if they supply it, proactively suggest writing it to AGENTS.md so that you will know to run it next time.
61
62
  - NEVER commit changes unless the user explicitly asks you to.
@@ -3,9 +3,7 @@ name: reviewer
3
3
  exclude_tools:
4
4
  - Bash
5
5
  - Edit
6
- - Insert
7
6
  - Write
8
- - Mkdir
9
7
  ---
10
8
 
11
9
  # Role and Behavior
@@ -6,7 +6,6 @@ import json
6
6
  import os
7
7
  from pathlib import Path
8
8
 
9
- from ..lsp.config import LSPConfig
10
9
  from ..mcp.config import MCPConfig
11
10
 
12
11
  # ---- context management -------------------------------------------------
@@ -88,14 +87,11 @@ DEFAULT_TOOLS: list[str] = [
88
87
  "Glob",
89
88
  "Grep",
90
89
  "Read",
91
- "Insert",
92
90
  "Edit",
93
91
  "Write",
94
- "Mkdir",
95
92
  "Bash",
96
93
  "Skill",
97
94
  "Question",
98
- "LSP",
99
95
  ]
100
96
 
101
97
  # ---- tool output limits --------------------------------------------------------
@@ -261,15 +257,6 @@ CONFIG_TEMPLATE = """\
261
257
  "context_path": null,
262
258
  "skill_path": null
263
259
  }},
264
- "lsp": {{
265
- "_comment": "Optional per-extension LSP server overrides. Keys are file extensions (e.g. '.py', '.cpp'); 'command' is the server argv; 'language_id' defaults to the extension without the dot. These override the built-in DEFAULT_SERVERS table. Remove this section to use only the built-ins. The server binary must be on PATH. The '.example' entry below is inert (no such extension) — copy it to a real extension like '.cpp' to activate.",
266
- "servers": {{
267
- ".example": {{
268
- "command": ["clangd", "--background-index", "--clang-tidy"],
269
- "language_id": "cpp"
270
- }}
271
- }}
272
- }},
273
260
  "mcp": {{
274
261
  "_comment": "Optional MCP servers (requires: pip install -e '.[mcp]'). Each server's tools become agent tools named mcp__<server>__<tool>. Transports: stdio (spawn command+args, pass through env var names), streamable-http / sse (connect to url, optional headers). 'parallel: true' marks read-only servers whose tools may run concurrently; default is serial. 'timeout' bounds connects, discovery and calls (seconds).",
275
262
  "servers": {{
@@ -526,31 +513,6 @@ def load_mcp_config(path: str | os.PathLike | None = None) -> MCPConfig:
526
513
  return MCPConfig.from_dict(section.get("servers"))
527
514
 
528
515
 
529
- def load_lsp_config(path: str | os.PathLike | None = None) -> LSPConfig:
530
- """Load per-extension LSP server overrides from the config file.
531
-
532
- Reads the optional ``lsp.servers`` object (keyed by file extension,
533
- e.g. ``".py"``) and returns an ``LSPConfig`` (empty when the file
534
- has no ``lsp`` section or no servers). These entries layer on top of
535
- the built-in ``DEFAULT_SERVERS`` table in ``lsp/manager.py``.
536
-
537
- Malformed entries raise ValueError so config errors surface at
538
- session start rather than the first LSP call.
539
- """
540
- data = _read_config(path)
541
- section = data.get("lsp") or {}
542
- if not isinstance(section, dict):
543
- raise ValueError(f"config file {_config_path(path)}: lsp must be an object")
544
- servers = section.get("servers") or {}
545
- if not isinstance(servers, dict):
546
- raise ValueError(f"config file {_config_path(path)}: lsp.servers must be an object")
547
- try:
548
- return LSPConfig.from_dict(servers)
549
- except ValueError as e:
550
- # Re-raise with the config-file path for a clearer message.
551
- raise ValueError(f"config file {_config_path(path)}: {e}") from e
552
-
553
-
554
516
  def load_models_config(path: str | os.PathLike | None = None) -> dict[str, dict]:
555
517
  """Load named LLM profiles from the config file's ``models`` object.
556
518
 
@@ -370,7 +370,7 @@ class Session:
370
370
  # write_file/create_directory), so plan mode stays read-only
371
371
  # by refusing every mcp__ tool.
372
372
  if self.plan_mode.is_plan and (
373
- name in ("Write", "Edit", "Insert", "Mkdir", "Bash") or name.startswith("mcp__")
373
+ name in ("Write", "Edit", "Bash") or name.startswith("mcp__")
374
374
  ):
375
375
  blocked = self._plan_blocked(name, args)
376
376
  if blocked:
@@ -467,16 +467,8 @@ class Session:
467
467
  ),
468
468
  self.project_dir,
469
469
  )
470
- if name in ("Edit", "Insert"):
470
+ if name == "Edit":
471
471
  return resolve_path(str(args.get("path", "")), self.project_dir)
472
- if name == "Mkdir":
473
- # Mirrors Mkdir.run: only the parent is ~-expanded.
474
- return os.path.realpath(
475
- os.path.join(
476
- resolve_path(str(args.get("parent", "")), self.project_dir),
477
- str(args.get("name", "")),
478
- )
479
- )
480
472
  return None
481
473
 
482
474
  # ------------------------------------------------------------------
@@ -9,10 +9,9 @@ from .base import PendingToolResult, Registry, Tool, ToolContext
9
9
  from .bash import Bash
10
10
  from .edit import Edit
11
11
  from .edit_mac import EditMac
12
- from .filesystem import GlobTool, Grep, Insert, Mkdir, Read, Write
12
+ from .filesystem import GlobTool, Grep, Read, Write
13
13
  from .glob_mac import GlobMac
14
14
  from .grep_mac import GrepMac
15
- from .lsp import LSP
16
15
  from .mcp import MCPTool, mcp_tools_from_manager, normalize_mcp_result
17
16
  from .planexit import PlanExit
18
17
  from .question import Question
@@ -32,10 +31,7 @@ __all__ = [
32
31
  "GlobTool",
33
32
  "Grep",
34
33
  "GrepMac",
35
- "Insert",
36
- "LSP",
37
34
  "MCPTool",
38
- "Mkdir",
39
35
  "PlanExit",
40
36
  "Question",
41
37
  "Read",
@@ -60,14 +56,11 @@ def default_registry() -> Registry:
60
56
  for tool in (
61
57
  AgentTool(),
62
58
  TodoWrite(),
63
- LSP(),
64
59
  glob_tool,
65
60
  grep_tool,
66
61
  Read(),
67
- Insert(),
68
62
  edit_tool,
69
63
  Write(),
70
- Mkdir(),
71
64
  bash_tool,
72
65
  Skill(),
73
66
  Question(),
@@ -165,8 +165,7 @@ class ToolContext:
165
165
  def config_path(self) -> str | None:
166
166
  """Path to the active config file (None = default resolution).
167
167
 
168
- Tools that read the config file (e.g. the LSP tool, which loads
169
- per-extension server overrides) use this so a session started
168
+ Tools that read the config file use this so a session started
170
169
  with ``--config PATH`` reads the same file.
171
170
  """
172
171
  if self.session:
@@ -217,6 +217,8 @@ class Bash(Tool):
217
217
  - Commands that truly require shell execution
218
218
  - Running builds, tests, or development servers
219
219
  - System administration tasks
220
+ - Creating empty directories: use `mkdir -p <path>` (idempotent; `Write`
221
+ already creates parent directories for any file it writes)
220
222
 
221
223
  **When NOT to use `Bash`:**
222
224
  - File operations → use `Read`, `Write`, `Edit`, `Glob`, `Grep` instead
@@ -227,7 +227,7 @@ def _apply_section(section: _Section, cwd: str, fallback_path: str | None) -> tu
227
227
  # realpath BEFORE writing (the same resolution diff_targets applies):
228
228
  # the write goes through atomic_write_text, whose os.replace would
229
229
  # otherwise overwrite a symlink with a regular file and leave the real
230
- # target untouched. Edit/Insert/Write resolve their own paths already.
230
+ # target untouched. Edit/Write resolve their own paths already.
231
231
  target = os.path.realpath(_resolve_target(section.new_path, cwd, fallback_path))
232
232
  if not os.path.isfile(target):
233
233
  return False, f"target file does not exist: {target}"
@@ -98,7 +98,10 @@ class Edit(Tool):
98
98
  "fenced code block).\n"
99
99
  "- The file paths within the diff (e.g. '--- a/filename' "
100
100
  "'+++ b/filename') must be appropriate for the `path`.\n\n"
101
- 'To simply insert text at some line, use the "Insert" tool instead.'
101
+ "To insert text at a specific point, use string-replacement mode: "
102
+ "match a unique nearby line as `old_str` and include it plus the new "
103
+ "text in `new_str`. To add content to a brand-new or empty file, use "
104
+ 'the "Write" tool instead.'
102
105
  )
103
106
  parameters = {
104
107
  "type": "object",
@@ -5,7 +5,7 @@ tools — atomic file replacement (``atomic_write_text``), spooling
5
5
  oversized tool results to temp files (`gptel-agent--truncate-buffer`
6
6
  parity), git-root detection, and the ``natnump`` predicate — and
7
7
  re-exports the tool classes that live in per-tool modules (`read.py`,
8
- `glob.py`, `grep.py`, `edit.py`, `write.py`, `insert.py`, `mkdir.py`),
8
+ `glob.py`, `grep.py`, `edit.py`, `write.py`),
9
9
  so existing imports such as ``from .tools.filesystem import Read``
10
10
  keep working.
11
11
 
@@ -19,7 +19,7 @@ NOTE: these filesystem tools are intentionally SYNCHRONOUS. Only Bash
19
19
  and Agent are `:async t` in gptel-agent; sync tools run one at a time in
20
20
  the model-emitted order. Do NOT be tempted to port every tool to async
21
21
  for parallelism: tools can depend on one another's side effects within a
22
- single round (e.g. Write/Mkdir then Read/Edit the same path, or Edit then
22
+ single round (e.g. Write then Read/Edit the same path, or Edit then
23
23
  Grep the just-changed file). Running them concurrently would introduce
24
24
  read-after-write races and non-deterministic results. Keep filesystem
25
25
  tools synchronous so ordering — and therefore correctness — is preserved.
@@ -34,7 +34,6 @@ from __future__ import annotations
34
34
 
35
35
  import contextlib
36
36
  import os
37
- import re
38
37
  import shutil # noqa: F401 (mock target for tests)
39
38
  import stat
40
39
  import subprocess # noqa: F401 (mock target for tests)
@@ -42,7 +41,6 @@ import tempfile
42
41
  import threading
43
42
  import time
44
43
  import uuid
45
- from collections.abc import Callable, Iterator
46
44
  from pathlib import Path
47
45
  from typing import TypeGuard
48
46
 
@@ -138,90 +136,6 @@ def _natnump(n: object) -> TypeGuard[int]:
138
136
  return isinstance(n, int) and not isinstance(n, bool) and n >= 0
139
137
 
140
138
 
141
- def _glob_to_regex(pattern: str) -> str:
142
- """Translate a pathlib-style glob pattern into a :mod:`re` pattern.
143
-
144
- Follows :mod:`fnmatch` conventions with two deliberate deviations:
145
- ``*`` does not cross path separators (unlike :func:`fnmatch.fnmatch`)
146
- and ``**`` matches any number of directories (including none). A
147
- pattern without any glob metacharacters is escaped, so plain names
148
- match exactly.
149
- """
150
- i, n = 0, len(pattern)
151
- out: list[str] = []
152
- while i < n:
153
- c = pattern[i]
154
- if c == "*":
155
- if pattern[i : i + 2] == "**":
156
- i += 2
157
- if pattern[i : i + 1] == "/":
158
- i += 1
159
- out.append("(?:.*/)?")
160
- continue
161
- out.append("[^/]*")
162
- i += 1
163
- elif c == "?":
164
- out.append("[^/]")
165
- i += 1
166
- elif c == "[":
167
- end = _find_glob_class_end(pattern, i)
168
- if end is None:
169
- out.append(re.escape(c))
170
- i += 1
171
- else:
172
- # The class body is regex-compatible as-is (\], \\, ranges,
173
- # and member brackets mean the same thing in both dialects);
174
- # only the negation sigil differs (! -> ^).
175
- body = pattern[i + 1 : end]
176
- if body.startswith("!"):
177
- body = "^" + body[1:]
178
- out.append("[" + body + "]")
179
- i = end + 1
180
- else:
181
- out.append(re.escape(c))
182
- i += 1
183
- return "".join(out)
184
-
185
-
186
- def _find_glob_class_end(pattern: str, start: int) -> int | None:
187
- """Index of the ``]`` closing the char class opened at ``start``.
188
-
189
- Mirrors glob semantics: a ``]`` immediately after ``[`` (or ``[!``)
190
- is a literal bracket, and backslash escapes are honoured.
191
- """
192
- i = start + 1
193
- if i < len(pattern) and pattern[i] == "!":
194
- i += 1
195
- if i < len(pattern) and pattern[i] == "]":
196
- i += 1
197
- while i < len(pattern):
198
- if pattern[i] == "\\" and i + 1 < len(pattern):
199
- i += 2
200
- elif pattern[i] == "]":
201
- return i
202
- else:
203
- i += 1
204
- return None
205
-
206
-
207
- def _walk_files(root: Path, onerror: Callable[[OSError], object] | None = None) -> Iterator[Path]:
208
- """Depth-first file iterator that never raises on traversal errors.
209
-
210
- Wraps :func:`os.walk` with ``onerror`` so unreadable directories are
211
- reported instead of silently swallowed, and wraps the whole iteration
212
- in a try/except so a scan that dies mid-flight still yields the
213
- entries collected so far (mirroring the 3.13+ ``pathlib`` scan
214
- behaviour on every supported version).
215
- """
216
- try:
217
- for dirpath, _dirnames, filenames in os.walk(root, onerror=onerror, followlinks=False):
218
- for name in filenames:
219
- yield Path(dirpath) / name
220
- except OSError as e:
221
- if onerror is not None:
222
- onerror(e)
223
-
224
-
225
139
  def _git_root(path: str) -> str | None:
226
140
  d = Path(path).resolve()
227
141
  for parent in [d, *d.parents]:
@@ -236,11 +150,11 @@ def atomic_write_text(path: str, content: str) -> None:
236
150
  """Replace PATH's contents with CONTENT atomically.
237
151
 
238
152
  THE single write path for every tool that rewrites a file (``Edit``,
239
- ``Insert``, ``Write``, and the pure-Python diff applier), so the four
153
+ ``Write``, and the pure-Python diff applier), so the three
240
154
  callers cannot drift apart. It lives in this module with the other
241
- shared helpers, and the four callers import it LAZILY (inside the
155
+ shared helpers, and the three callers import it LAZILY (inside the
242
156
  method that writes) rather than at module level, because this module
243
- re-imports ``edit``/``write``/``insert``/``diffapply`` at its bottom
157
+ re-imports ``edit``/``write``/``diffapply`` at its bottom
244
158
  for the compatibility re-exports: a module-level import here would
245
159
  close the cycle on a half-initialised module (``tools/__init__``
246
160
  loads ``edit`` first, whose import of this module would then ask the
@@ -286,7 +200,7 @@ def atomic_write_text(path: str, content: str) -> None:
286
200
  - PATH must already be symlink-resolved. ``os.replace`` onto a
287
201
  symlink overwrites the LINK with a regular file, where an in-place
288
202
  write would have followed it. Every caller resolves the real path
289
- first (``Edit``/``Insert``/``Write`` via ``os.path.realpath`` on
203
+ first (``Edit``/``Write`` via ``os.path.realpath`` on
290
204
  entry, ``_apply_section`` on its resolved target).
291
205
  - Hard links are broken: the new content lands on a new inode, so
292
206
  other links to the old inode keep the old content.
@@ -332,8 +246,6 @@ from .glob import GlobTool, _git_glob_results # noqa: E402
332
246
  from .glob_mac import GlobMac # noqa: E402
333
247
  from .grep import Grep, _grep_out # noqa: E402
334
248
  from .grep_mac import GrepMac # noqa: E402
335
- from .insert import Insert # noqa: E402
336
- from .mkdir import Mkdir # noqa: E402
337
249
  from .read import Read # noqa: E402
338
250
  from .write import Write # noqa: E402
339
251
  # isort: on
@@ -344,8 +256,6 @@ __all__ = [
344
256
  "GlobTool",
345
257
  "Grep",
346
258
  "GrepMac",
347
- "Insert",
348
- "Mkdir",
349
259
  "Read",
350
260
  "Write",
351
261
  "MAX_OUTPUT",
@@ -31,7 +31,6 @@ class Grep(Tool):
31
31
  **When NOT to use `Grep`:**
32
32
  - Searching for files by name → use `Glob`
33
33
  - Reading known file contents → use `Read`
34
- - Navigating a known code symbol (its definition, references, implementations, type info, or call hierarchy) → use `LSP` (semantically correct; `Grep` matches text, not symbols)
35
34
  - open-ended search that may require multiple rounds of globbing and grepping → use `Agent`
36
35
 
37
36
  **How to use `Grep`:**
@@ -202,7 +202,7 @@ class Tui(RenderMixin, InputMixin, CommandMixin):
202
202
  # old bare "error" so the user sees WHAT failed; the
203
203
  # red styling comes from "error" appearing in the text.
204
204
  # A structured {"code", "message"} payload (budget /
205
- # timeout outcomes, LSP failures) is unwrapped by the
205
+ # timeout outcomes) is unwrapped by the
206
206
  # shared helper — rendering the dict would both look
207
207
  # wrong and lose the detail.
208
208
  detail = error_display_text(data) if data else ""
@@ -563,7 +563,19 @@ class RenderMixin:
563
563
  cells); line 2 gets the full terminal width. Newlines in the
564
564
  message are flattened, words break at spaces where possible,
565
565
  and anything beyond two lines is truncated with an ellipsis.
566
+
567
+ The status is the one display path that reaches the terminal
568
+ unsanitized: it is set from endpoint/MCP error text
569
+ (``error_display_text``) and ``_on_log`` messages (``agent
570
+ error: {e}``, ``compaction failed: {e}``, ``MCP: [{server}]
571
+ {err}``), any of which can carry a hostile server's escape
572
+ sequences. Strip them here -- the single sink for every status
573
+ source -- mirroring the sibling paths (tool name, history rows)
574
+ that already call ``sanitize_terminal``. Sanitizing BEFORE the
575
+ newline flatten below keeps a stray OSC introducer from hiding
576
+ the rest of the line.
566
577
  """
578
+ msg = sanitize_terminal(msg)
567
579
  msg = " ".join(msg.splitlines())
568
580
  width = self.console.width or 80
569
581
  avail1 = max(1, width - used - 1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-agent-harness
3
- Version: 1.5.6.3
3
+ Version: 1.5.6.4
4
4
  Summary: A lightweight, hackable mini-OpenCode written in Python.
5
5
  Author-email: Huming Chen <chenhuming@gmail.com>
6
6
  License-Expression: MIT
@@ -74,7 +74,7 @@ Edit `~/.config/python-agent-harness/config.json` and set your `base_url`, `api_
74
74
  - **FSM-driven execution** — explicit `WAIT` / `TOOL` / `TRET` / `SUPERVISE` / `DONE` / `ERRS` / `ABRT` states. Completion supervision nudges the model when it stops early; failed tool calls are sanitized. Transient API failures (`429` / `5xx`) retry with exponential backoff. Auth-expired codes (configurable via `AUTH_REFRESH_STATUS_CODES` in `config.py`; defaults to `[401, 502]`) trigger automatic API key re-read from config/env.
75
75
  - **Context management** — CJK-aware token estimation, per-model context windows, automatic compaction at 70% usage.
76
76
  - **Image & text attachments** — attach with `@path` (e.g. `@screenshot.png`, `@README.md`): images (PNG/JPEG/GIF/WebP, up to 20 MB) become multimodal `image_url` parts; text files are inlined. Web URLs supported for images.
77
- - **Coding tools** — `Agent`, `TodoWrite`, `Glob`, `Grep`, `Read`, `Insert`, `Edit` (incl. unified diffs), `Write`, `Mkdir`, `Bash`, `Skill`, `Question`, `LSP`, `PlanExit`. Read-only tool rounds (`Read`, `Glob`, `Grep`, `Skill`, `LSP`) dispatch concurrently; async tools (`Bash`, `Agent`) can also run concurrently. Results always delivered in emitted order.
77
+ - **Coding tools** — `Agent`, `TodoWrite`, `Glob`, `Grep`, `Read`, `Edit` (incl. unified diffs), `Write`, `Bash`, `Skill`, `Question`, `PlanExit`. Read-only tool rounds (`Read`, `Glob`, `Grep`, `Skill`) dispatch concurrently; async tools (`Bash`, `Agent`) can also run concurrently. Results always delivered in emitted order.
78
78
  - **Plan / Build modes** — plan mode is read-only except for the per-session plan file.
79
79
  - **Persistent sessions** — auto-saved after every response to `~/.local/share/python-agent-harness/sessions/`, with LLM-generated titles and `/restore --latest` / `/sessions`.
80
80
  - **Focused TUI** — Rich-based with pinned status bar, Todos panel, inline diff rendering, and a `prompt_toolkit` editor with history and completion. `Esc+Enter` submits, `Ctrl-D` quits, `Ctrl-C` cancels.
@@ -191,7 +191,6 @@ All LLM settings live in `~/.config/python-agent-harness/config.json`. Environme
191
191
  - **`subagent_llm`** — LLM config for `Agent` tool. Unset values inherit from `llm`. Set `profile` to reuse a `models` entry. Precedence: profile > `subagent_llm` > `llm` > env vars.
192
192
  - **`default_agent`** — agent name to use at session start (must exist in `prompts/agents/`). `null` = built-in default.
193
193
  - **`paths.context_path` / `paths.skill_path`** — locations for context files and skills. Default: project-local `contexts/` and `skills/`.
194
- - **`lsp.servers`** — per-extension LSP server overrides for the `LSP` tool. Keys are extensions (`.py`, `.cpp`); each has `command` (argv) and optional `language_id`. Layers on top of `DEFAULT_SERVERS` in `lsp/manager.py`.
195
194
  - **`mcp.servers`** — MCP server config (requires `[mcp]` extra). Each supports `transport`, `command`, `args`, `env`, `url`, `headers`, `parallel`, `timeout`, `enabled`.
196
195
  - **`headless` / `serve`** — optional budget limits: `max_rounds` (LLM rounds) and `timeout` (wall-clock seconds). `--max-rounds` / `--timeout` override per call; `<= 0` or `null` disables. Unset = unlimited.
197
196
  - **Precedence** — code defaults < config file < `OPENAI_*` env vars. Sub-agents also support `OPENAI_SUBAGENT_*` (`_BASE_URL`, `_API_KEY`, `_MODEL`).
@@ -30,10 +30,6 @@ python_agent_harness/io/persistence.py
30
30
  python_agent_harness/io/text_filter.py
31
31
  python_agent_harness/llm/__init__.py
32
32
  python_agent_harness/llm/client.py
33
- python_agent_harness/lsp/__init__.py
34
- python_agent_harness/lsp/client.py
35
- python_agent_harness/lsp/config.py
36
- python_agent_harness/lsp/manager.py
37
33
  python_agent_harness/mcp/__init__.py
38
34
  python_agent_harness/mcp/client.py
39
35
  python_agent_harness/mcp/config.py
@@ -69,10 +65,7 @@ python_agent_harness/tools/glob.py
69
65
  python_agent_harness/tools/glob_mac.py
70
66
  python_agent_harness/tools/grep.py
71
67
  python_agent_harness/tools/grep_mac.py
72
- python_agent_harness/tools/insert.py
73
- python_agent_harness/tools/lsp.py
74
68
  python_agent_harness/tools/mcp.py
75
- python_agent_harness/tools/mkdir.py
76
69
  python_agent_harness/tools/planexit.py
77
70
  python_agent_harness/tools/question.py
78
71
  python_agent_harness/tools/read.py