local-coding-agent 1.1.0__py3-none-any.whl

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 (101) hide show
  1. local_coding_agent/__init__.py +314 -0
  2. local_coding_agent/__main__.py +5 -0
  3. local_coding_agent/acp_server/__init__.py +38 -0
  4. local_coding_agent/acp_server/_codec.py +95 -0
  5. local_coding_agent/acp_server/_protocol.py +13 -0
  6. local_coding_agent/acp_server/_server.py +663 -0
  7. local_coding_agent/acp_server/_session.py +24 -0
  8. local_coding_agent/acp_server/_tools.py +126 -0
  9. local_coding_agent/ast_compactor.py +130 -0
  10. local_coding_agent/atomizer.py +77 -0
  11. local_coding_agent/benchmark/__init__.py +57 -0
  12. local_coding_agent/benchmark/_cases.py +307 -0
  13. local_coding_agent/benchmark/_judge.py +175 -0
  14. local_coding_agent/benchmark/_oracles.py +157 -0
  15. local_coding_agent/benchmark/_runner.py +259 -0
  16. local_coding_agent/benchmark/_stats.py +22 -0
  17. local_coding_agent/benchmark/_summary.py +143 -0
  18. local_coding_agent/benchmark_oracle_worker.py +169 -0
  19. local_coding_agent/calibration.py +154 -0
  20. local_coding_agent/capability.py +450 -0
  21. local_coding_agent/chat_compaction.py +276 -0
  22. local_coding_agent/cli/__init__.py +12 -0
  23. local_coding_agent/cli/_dispatch.py +377 -0
  24. local_coding_agent/cli/_handlers.py +320 -0
  25. local_coding_agent/cli/_handlers2.py +725 -0
  26. local_coding_agent/cli/_input.py +61 -0
  27. local_coding_agent/cli/_main.py +159 -0
  28. local_coding_agent/cli/_parser.py +327 -0
  29. local_coding_agent/context_manager.py +155 -0
  30. local_coding_agent/controller/__init__.py +18 -0
  31. local_coding_agent/controller/_constants.py +108 -0
  32. local_coding_agent/controller/_controller.py +1008 -0
  33. local_coding_agent/controller/_post_apply.py +216 -0
  34. local_coding_agent/delegator.py +180 -0
  35. local_coding_agent/desktop/__init__.py +8 -0
  36. local_coding_agent/desktop/app.py +100 -0
  37. local_coding_agent/desktop/assets/lucide.min.js +12 -0
  38. local_coding_agent/desktop/assets/tailwind.css +1 -0
  39. local_coding_agent/desktop/client_js.py +1271 -0
  40. local_coding_agent/desktop/components.py +555 -0
  41. local_coding_agent/desktop/server/__init__.py +37 -0
  42. local_coding_agent/desktop/server/_handlers.py +2178 -0
  43. local_coding_agent/desktop/server/_models.py +204 -0
  44. local_coding_agent/desktop/server/_server.py +347 -0
  45. local_coding_agent/desktop/server/_telemetry.py +87 -0
  46. local_coding_agent/desktop/styles.py +76 -0
  47. local_coding_agent/desktop/ui.py +53 -0
  48. local_coding_agent/doctor.py +413 -0
  49. local_coding_agent/hooks/__init__.py +19 -0
  50. local_coding_agent/hooks/_adapters.py +458 -0
  51. local_coding_agent/hooks/_bridge.py +346 -0
  52. local_coding_agent/hooks/_decision.py +26 -0
  53. local_coding_agent/lsp/__init__.py +60 -0
  54. local_coding_agent/lsp/_client.py +322 -0
  55. local_coding_agent/lsp/_fallback.py +297 -0
  56. local_coding_agent/lsp/_manager.py +173 -0
  57. local_coding_agent/lsp/_mock.py +200 -0
  58. local_coding_agent/lsp/_types.py +398 -0
  59. local_coding_agent/mcp_config.py +398 -0
  60. local_coding_agent/mcp_server.py +377 -0
  61. local_coding_agent/memory.py +140 -0
  62. local_coding_agent/mode_router.py +200 -0
  63. local_coding_agent/model_scanner.py +525 -0
  64. local_coding_agent/monitor.py +635 -0
  65. local_coding_agent/observation_policy.py +184 -0
  66. local_coding_agent/ollama_adapter.py +736 -0
  67. local_coding_agent/plan_mode.py +653 -0
  68. local_coding_agent/prescriptions.py +278 -0
  69. local_coding_agent/profiles.py +239 -0
  70. local_coding_agent/repository_tools.py +782 -0
  71. local_coding_agent/ripgrep.py +326 -0
  72. local_coding_agent/semantic_linter.py +122 -0
  73. local_coding_agent/service.py +405 -0
  74. local_coding_agent/session_events.py +684 -0
  75. local_coding_agent/session_query.py +578 -0
  76. local_coding_agent/skill_config.py +426 -0
  77. local_coding_agent/smoke.py +268 -0
  78. local_coding_agent/speculative_racing.py +85 -0
  79. local_coding_agent/spill.py +258 -0
  80. local_coding_agent/stats.py +255 -0
  81. local_coding_agent/stdio.py +132 -0
  82. local_coding_agent/subagent.py +516 -0
  83. local_coding_agent/task.py +90 -0
  84. local_coding_agent/task_store.py +142 -0
  85. local_coding_agent/tasks.py +240 -0
  86. local_coding_agent/terminal/__init__.py +35 -0
  87. local_coding_agent/terminal/_manager.py +156 -0
  88. local_coding_agent/terminal/_process.py +148 -0
  89. local_coding_agent/terminal/_session.py +452 -0
  90. local_coding_agent/terminal/_tools.py +233 -0
  91. local_coding_agent/terminal/_types.py +60 -0
  92. local_coding_agent/tool_call_parser.py +178 -0
  93. local_coding_agent/validators.py +563 -0
  94. local_coding_agent/vram_fit.py +240 -0
  95. local_coding_agent/worker_pool.py +516 -0
  96. local_coding_agent-1.1.0.dist-info/METADATA +202 -0
  97. local_coding_agent-1.1.0.dist-info/RECORD +101 -0
  98. local_coding_agent-1.1.0.dist-info/WHEEL +5 -0
  99. local_coding_agent-1.1.0.dist-info/entry_points.txt +3 -0
  100. local_coding_agent-1.1.0.dist-info/licenses/LICENSE +21 -0
  101. local_coding_agent-1.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,314 @@
1
+ """Bounded controller components for delegating small coding tasks."""
2
+
3
+ __version__ = "1.1.0"
4
+
5
+
6
+ from .acp_server import (
7
+ ACP_TOOLS,
8
+ AcpCodec,
9
+ AcpServer,
10
+ AcpSession,
11
+ )
12
+ from .ast_compactor import skeletonize_file, skeletonize_python
13
+ from .atomizer import Decomposition, PreflightReport, TaskBudget, decompose, preflight
14
+
15
+ from .calibration import calibrate_for_model, calibrate_workers, model_vram_bytes
16
+ from .context_manager import (
17
+ ContextAssembler,
18
+ HarnessState,
19
+ compact_tool_exchanges,
20
+ purge_diff_residues,
21
+ )
22
+ from .controller import Controller
23
+ from .delegator import DelegatingAgent, DecompositionTemplate, is_decomposable_failure
24
+ from .doctor import (
25
+ CheckResult,
26
+ DoctorFixReport,
27
+ DoctorReport,
28
+ diagnose_environment,
29
+ remediate_environment,
30
+ )
31
+ from .mcp_config import generate_mcp_config_dict, get_client_config_path, integrate_mcp_config
32
+ from .mcp_server import build_server
33
+ from .memory import LoadedModel, MemoryBudgetError, MemoryOperationError, MemorySnapshot, ModelMemoryManager
34
+ from .mode_router import (
35
+ DEFAULT_MODE_ROUTER_PROFILE,
36
+ MODES,
37
+ VALID_ROUTED,
38
+ ModeName,
39
+ build_mode_router,
40
+ classify_fast,
41
+ classify_mode,
42
+ )
43
+ from .monitor import MonitorServer
44
+ from .lsp import (
45
+ FallbackLspEngine,
46
+ LspClient,
47
+ LspConnectionError,
48
+ LspError,
49
+ LspHoverResult,
50
+ LspLocation,
51
+ LspManager,
52
+ LspPosition,
53
+ LspRange,
54
+ LspResponseError,
55
+ LspSymbol,
56
+ LspTimeoutError,
57
+ MockLspServer,
58
+ )
59
+ from .observation_policy import (
60
+ FsObservationError,
61
+ FsObservationGate,
62
+ is_observed,
63
+ observe_file,
64
+ reset_session,
65
+ verify_edit_intent,
66
+ )
67
+ from .plan_mode import (
68
+ AskUserQuestionTool,
69
+ PlanArtifact,
70
+ PlanModeController,
71
+ PlanModeError,
72
+ PlanModePolicyError,
73
+ PlanModeState,
74
+ QuestionAnswer,
75
+ QuestionItem,
76
+ TodoChecklist,
77
+ TodoItem,
78
+ TodoStatus,
79
+ )
80
+ from .ollama_adapter import (
81
+ ModelProfile,
82
+ OllamaClient,
83
+ OllamaError,
84
+ OpenAICompatibleClient,
85
+ build_client,
86
+ )
87
+ from .ripgrep import RipgrepMatch, ripgrep_files, ripgrep_search
88
+ from .semantic_linter import (
89
+ LinterDiagnostic,
90
+ LinterReport,
91
+ lint_patch_in_memory,
92
+ lint_source_code,
93
+ )
94
+ from .service import DelegationRequest, DelegationService
95
+ from .smoke import run_smoke_test
96
+ from .speculative_racing import SpeculativeRacer
97
+ from .spill import (
98
+ DEFAULT_MAX_BYTES,
99
+ DEFAULT_MAX_LINES,
100
+ SpillRef,
101
+ SpillStore,
102
+ maybe_spill,
103
+ read_spill,
104
+ save_text,
105
+ )
106
+ from .stats import DelegationStats, JsonlStatsSink, TimedDelegationStats
107
+ from .stdio import StdioDelegationAdapter
108
+ from .hooks import (
109
+ ClaudeCodeHookAdapter,
110
+ CodexHookAdapter,
111
+ HookBridge,
112
+ HookDecision,
113
+ )
114
+ from .subagent import (
115
+ MailboxMessage,
116
+ SubagentContext,
117
+ SubagentCoordinator,
118
+ SubagentReport,
119
+ )
120
+ from .task import TaskEnvelope
121
+ from .task_store import JsonFileTaskStore, TaskRecord, TaskStore
122
+ from .terminal import (
123
+ TerminalError,
124
+ TerminalManager,
125
+ TerminalProcessExitedError,
126
+ TerminalSession,
127
+ TerminalSessionExistsError,
128
+ TerminalSessionInfo,
129
+ TerminalSessionNotFoundError,
130
+ TerminalTimeoutError,
131
+ execute_terminal_tool,
132
+ get_terminal_tool_schemas,
133
+ kill_process_tree,
134
+ )
135
+ from .validators import ValidationReport, validate_candidate
136
+ from .worker_pool import BoundedWorkerPool
137
+ from .session_events import (
138
+ ModelTurnEvent,
139
+ PrescriptionEvent,
140
+ SessionCompletedEvent,
141
+ SessionCreatedEvent,
142
+ SessionEvent,
143
+ SessionLog,
144
+ ToolCallEvent,
145
+ ToolResultEvent,
146
+ UserPromptEvent,
147
+ derive_messages,
148
+ event_from_dict,
149
+ event_to_dict,
150
+ fork_session,
151
+ )
152
+ from .session_query import (
153
+ SessionQueryEngine,
154
+ get_session_trace,
155
+ sanitize_fts5_query,
156
+ search_events,
157
+ search_sessions,
158
+ )
159
+
160
+ try:
161
+ from .tasks import TASKS_IDENTIFIER, TasksExtension
162
+ except ImportError: # pragma: no cover - mcp is an optional dependency
163
+ TasksExtension = None # type: ignore[assignment]
164
+ TASKS_IDENTIFIER = "io.modelcontextprotocol/tasks"
165
+
166
+ __all__ = [
167
+ "Controller",
168
+ "DelegationRequest",
169
+ "DelegationService",
170
+ "DelegationStats",
171
+ "JsonlStatsSink",
172
+ "TimedDelegationStats",
173
+ "BoundedWorkerPool",
174
+ "StdioDelegationAdapter",
175
+ "DelegatingAgent",
176
+ "DecompositionTemplate",
177
+ "is_decomposable_failure",
178
+ "Decomposition",
179
+ "PreflightReport",
180
+ "TaskBudget",
181
+ "decompose",
182
+ "preflight",
183
+ "calibrate_for_model",
184
+ "calibrate_workers",
185
+ "model_vram_bytes",
186
+ "LoadedModel",
187
+ "MemoryBudgetError",
188
+ "MemoryOperationError",
189
+ "MemorySnapshot",
190
+ "ModelMemoryManager",
191
+ "ModeName",
192
+ "MODES",
193
+ "VALID_ROUTED",
194
+ "build_mode_router",
195
+ "DEFAULT_MODE_ROUTER_PROFILE",
196
+ "classify_fast",
197
+ "classify_mode",
198
+ "ModelProfile",
199
+ "OllamaClient",
200
+ "OllamaError",
201
+ "OpenAICompatibleClient",
202
+ "build_client",
203
+ "build_server",
204
+ "TaskEnvelope",
205
+ "ValidationReport",
206
+ "validate_candidate",
207
+ "TasksExtension",
208
+ "TASKS_IDENTIFIER",
209
+ "diagnose_environment",
210
+ "DoctorReport",
211
+ "DoctorFixReport",
212
+ "remediate_environment",
213
+ "CheckResult",
214
+ "generate_mcp_config_dict",
215
+ "get_client_config_path",
216
+ "integrate_mcp_config",
217
+ "run_smoke_test",
218
+ "MonitorServer",
219
+ "TaskStore",
220
+ "TaskRecord",
221
+ "JsonFileTaskStore",
222
+ "HarnessState",
223
+ "ContextAssembler",
224
+ "compact_tool_exchanges",
225
+ "purge_diff_residues",
226
+ "skeletonize_python",
227
+ "skeletonize_file",
228
+ "LinterDiagnostic",
229
+ "LinterReport",
230
+ "lint_source_code",
231
+ "lint_patch_in_memory",
232
+ "SpeculativeRacer",
233
+ "FsObservationError",
234
+ "FsObservationGate",
235
+ "observe_file",
236
+ "is_observed",
237
+ "verify_edit_intent",
238
+ "reset_session",
239
+ "RipgrepMatch",
240
+ "ripgrep_search",
241
+ "ripgrep_files",
242
+ "DEFAULT_MAX_BYTES",
243
+ "DEFAULT_MAX_LINES",
244
+ "SpillRef",
245
+ "SpillStore",
246
+ "save_text",
247
+ "read_spill",
248
+ "maybe_spill",
249
+ "LspPosition",
250
+ "LspRange",
251
+ "LspLocation",
252
+ "LspHoverResult",
253
+ "LspSymbol",
254
+ "LspClient",
255
+ "MockLspServer",
256
+ "FallbackLspEngine",
257
+ "LspManager",
258
+ "LspError",
259
+ "LspTimeoutError",
260
+ "LspConnectionError",
261
+ "LspResponseError",
262
+ "TerminalSession",
263
+ "TerminalSessionInfo",
264
+ "TerminalManager",
265
+ "TerminalError",
266
+ "TerminalSessionNotFoundError",
267
+ "TerminalSessionExistsError",
268
+ "TerminalProcessExitedError",
269
+ "TerminalTimeoutError",
270
+ "get_terminal_tool_schemas",
271
+ "execute_terminal_tool",
272
+ "kill_process_tree",
273
+ "PlanModeState",
274
+ "PlanArtifact",
275
+ "PlanModeController",
276
+ "PlanModeError",
277
+ "PlanModePolicyError",
278
+ "QuestionItem",
279
+ "QuestionAnswer",
280
+ "AskUserQuestionTool",
281
+ "TodoStatus",
282
+ "TodoItem",
283
+ "TodoChecklist",
284
+ "HookBridge",
285
+ "HookDecision",
286
+ "CodexHookAdapter",
287
+ "ClaudeCodeHookAdapter",
288
+ "SubagentCoordinator",
289
+ "SubagentContext",
290
+ "MailboxMessage",
291
+ "SubagentReport",
292
+ "AcpServer",
293
+ "AcpSession",
294
+ "AcpCodec",
295
+ "ACP_TOOLS",
296
+ "SessionCreatedEvent",
297
+ "UserPromptEvent",
298
+ "ModelTurnEvent",
299
+ "ToolCallEvent",
300
+ "ToolResultEvent",
301
+ "PrescriptionEvent",
302
+ "SessionCompletedEvent",
303
+ "SessionEvent",
304
+ "SessionLog",
305
+ "derive_messages",
306
+ "fork_session",
307
+ "event_to_dict",
308
+ "event_from_dict",
309
+ "SessionQueryEngine",
310
+ "search_sessions",
311
+ "search_events",
312
+ "get_session_trace",
313
+ "sanitize_fts5_query",
314
+ ]
@@ -0,0 +1,5 @@
1
+ from .cli import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ raise SystemExit(main())
@@ -0,0 +1,38 @@
1
+ """Universal Agent Client Protocol (ACP) Server & Interop Gateway (R29).
2
+
3
+ Adapted from DeepSeek Harness @deepseek-ai/dsh-acp.
4
+ Implements the standard JSON-RPC 2.0 Agent Client Protocol over stdio with
5
+ both Content-Length header and newline-delimited JSONL framing support.
6
+ Exposes session lifecycle, prompting, cancellation, and repo tools to
7
+ external AI-native editors (Zed, Cursor, VS Code, JetBrains, OpenCode).
8
+ """
9
+
10
+ from ._codec import AcpCodec
11
+ from ._protocol import (
12
+ JSONRPC_INTERNAL_ERROR,
13
+ JSONRPC_INVALID_PARAMS,
14
+ JSONRPC_INVALID_REQUEST,
15
+ JSONRPC_METHOD_NOT_FOUND,
16
+ JSONRPC_PARSE_ERROR,
17
+ PROTOCOL_VERSION,
18
+ SERVER_NAME,
19
+ SERVER_VERSION,
20
+ )
21
+ from ._server import AcpServer
22
+ from ._session import AcpSession
23
+ from ._tools import ACP_TOOLS
24
+
25
+ __all__ = [
26
+ "PROTOCOL_VERSION",
27
+ "SERVER_NAME",
28
+ "SERVER_VERSION",
29
+ "JSONRPC_PARSE_ERROR",
30
+ "JSONRPC_INVALID_REQUEST",
31
+ "JSONRPC_METHOD_NOT_FOUND",
32
+ "JSONRPC_INVALID_PARAMS",
33
+ "JSONRPC_INTERNAL_ERROR",
34
+ "ACP_TOOLS",
35
+ "AcpCodec",
36
+ "AcpSession",
37
+ "AcpServer",
38
+ ]
@@ -0,0 +1,95 @@
1
+ """Stdio framing codec (Content-Length / JSONL)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from typing import Any
7
+
8
+
9
+ class AcpCodec:
10
+ """Header & line framing codec for JSON-RPC 2.0 stdio."""
11
+
12
+ @staticmethod
13
+ def read_message(stream: Any, max_bytes: int = 10 * 1024 * 1024) -> tuple[dict[str, Any] | None, str]:
14
+ """Read a single JSON-RPC message from a binary or text stream.
15
+
16
+ Returns:
17
+ (message_dict, framing_type) where framing_type is "content-length" or "jsonl".
18
+ Returns (None, "") on EOF.
19
+ """
20
+ while True:
21
+ line = stream.readline()
22
+ if not line:
23
+ return None, ""
24
+ if isinstance(line, bytes):
25
+ line_str = line.decode("utf-8", errors="replace").strip()
26
+ else:
27
+ line_str = str(line).strip()
28
+ if line_str:
29
+ break
30
+
31
+ # Check for Content-Length header
32
+ if line_str.lower().startswith("content-length:"):
33
+ try:
34
+ parts = line_str.split(":", 1)
35
+ if len(parts) < 2:
36
+ raise ValueError(f"Invalid Content-Length header: {line_str}")
37
+ content_length = int(parts[1].strip())
38
+ except ValueError:
39
+ raise ValueError(f"Invalid Content-Length header: {line_str}")
40
+
41
+ if content_length < 0:
42
+ raise ValueError(f"Content-Length must be non-negative: {content_length}")
43
+ if content_length > max_bytes:
44
+ raise ValueError(f"Content-Length {content_length} exceeds max allowed {max_bytes} bytes")
45
+
46
+ # Read remaining headers until empty line (bounded against header floods)
47
+ header_count = 0
48
+ while True:
49
+ header_line = stream.readline()
50
+ if not header_line:
51
+ break
52
+ header_count += 1
53
+ if header_count > 50:
54
+ raise ValueError("Too many header lines in request (max 50)")
55
+ if isinstance(header_line, bytes):
56
+ h_str = header_line.decode("utf-8", errors="replace").strip()
57
+ else:
58
+ h_str = str(header_line).strip()
59
+ if not h_str:
60
+ break
61
+
62
+ # Read exact content_length bytes
63
+ body = stream.read(content_length)
64
+ if isinstance(body, bytes):
65
+ if len(body) < content_length:
66
+ raise ValueError(f"Unexpected EOF reading message body: expected {content_length} bytes, got {len(body)}")
67
+ body_str = body.decode("utf-8", errors="replace")
68
+ else:
69
+ body_str = str(body)
70
+ if len(body_str.encode("utf-8")) < content_length and len(body_str) < content_length:
71
+ raise ValueError(f"Unexpected EOF reading message body: expected {content_length} bytes")
72
+
73
+ parsed = json.loads(body_str)
74
+ if not isinstance(parsed, dict):
75
+ raise ValueError("JSON-RPC message must be an object")
76
+ return parsed, "content-length"
77
+ else:
78
+ # JSONL framing
79
+ if len(line_str.encode("utf-8")) > max_bytes:
80
+ raise ValueError(f"JSONL line exceeds max allowed {max_bytes} bytes")
81
+ parsed = json.loads(line_str)
82
+ if not isinstance(parsed, dict):
83
+ raise ValueError("JSON-RPC message must be an object")
84
+ return parsed, "jsonl"
85
+
86
+ @staticmethod
87
+ def format_message(message: dict[str, Any], framing: str = "jsonl") -> bytes:
88
+ """Format a JSON-RPC message dictionary into framed bytes."""
89
+ raw_json = json.dumps(message, ensure_ascii=False)
90
+ encoded = raw_json.encode("utf-8")
91
+ if framing == "content-length":
92
+ header = f"Content-Length: {len(encoded)}\r\n\r\n".encode("ascii")
93
+ return header + encoded
94
+ else:
95
+ return encoded + b"\n"
@@ -0,0 +1,13 @@
1
+ """ACP protocol constants and error codes."""
2
+
3
+ from .. import __version__
4
+
5
+ PROTOCOL_VERSION: str = "2026-08-20"
6
+ SERVER_NAME: str = "local-coding-agent-acp"
7
+ SERVER_VERSION: str = __version__
8
+
9
+ JSONRPC_PARSE_ERROR: int = -32700
10
+ JSONRPC_INVALID_REQUEST: int = -32600
11
+ JSONRPC_METHOD_NOT_FOUND: int = -32601
12
+ JSONRPC_INVALID_PARAMS: int = -32602
13
+ JSONRPC_INTERNAL_ERROR: int = -32603