mycode-coding-agent 0.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.
- mycode/__init__.py +0 -0
- mycode/adapters/__init__.py +21 -0
- mycode/adapters/jsonl.py +692 -0
- mycode/agent/__init__.py +25 -0
- mycode/agent/events.py +111 -0
- mycode/agent/outcome.py +103 -0
- mycode/agent/progress.py +373 -0
- mycode/agent/runner.py +1481 -0
- mycode/application/__init__.py +38 -0
- mycode/application/agent_session.py +367 -0
- mycode/application/events.py +59 -0
- mycode/application/runtime.py +211 -0
- mycode/application/sessions.py +180 -0
- mycode/cli.py +840 -0
- mycode/config.py +355 -0
- mycode/context/__init__.py +1 -0
- mycode/context/artifacts.py +672 -0
- mycode/context/budget.py +752 -0
- mycode/context/builder.py +112 -0
- mycode/context/compact.py +795 -0
- mycode/context/tool_result_format.py +199 -0
- mycode/context/tool_result_retention.py +261 -0
- mycode/conversation.py +78 -0
- mycode/error_handling.py +481 -0
- mycode/event_format.py +147 -0
- mycode/instructions.py +285 -0
- mycode/llm.py +771 -0
- mycode/mcp/__init__.py +41 -0
- mycode/mcp/client.py +44 -0
- mycode/mcp/config.py +207 -0
- mycode/mcp/errors.py +302 -0
- mycode/mcp/manager.py +339 -0
- mycode/mcp/models.py +20 -0
- mycode/mcp/result_adapter.py +58 -0
- mycode/mcp/tool_adapter.py +145 -0
- mycode/mcp/trust.py +313 -0
- mycode/memory.py +570 -0
- mycode/memory_context.py +245 -0
- mycode/messages.py +63 -0
- mycode/observability.py +28 -0
- mycode/permissions.py +262 -0
- mycode/persistence/__init__.py +1 -0
- mycode/persistence/filesystem.py +291 -0
- mycode/persistence/project_storage.py +208 -0
- mycode/persistence/session_lock.py +138 -0
- mycode/persistence/session_store.py +503 -0
- mycode/presentation/__init__.py +1 -0
- mycode/presentation/cli/__init__.py +14 -0
- mycode/presentation/cli/confirmer.py +116 -0
- mycode/presentation/cli/mcp_trust.py +61 -0
- mycode/presentation/cli/presenter.py +320 -0
- mycode/presentation/cli/session_menu.py +146 -0
- mycode/presentation/cli/subagent_observer.py +124 -0
- mycode/presentation/command_format.py +90 -0
- mycode/presentation/commands.py +95 -0
- mycode/presentation/tui/__init__.py +6 -0
- mycode/presentation/tui/app.py +1351 -0
- mycode/presentation/tui/interactions.py +253 -0
- mycode/presentation/tui/presenter.py +266 -0
- mycode/presentation/tui/screens.py +305 -0
- mycode/presentation/tui/widgets.py +214 -0
- mycode/project.py +22 -0
- mycode/prompts.py +181 -0
- mycode/reasoning.py +40 -0
- mycode/session.py +86 -0
- mycode/skills/__init__.py +27 -0
- mycode/skills/builtin/database-recovery/SKILL.md +138 -0
- mycode/skills/builtin/database-recovery/references/sqlite.md +235 -0
- mycode/skills/registry.py +295 -0
- mycode/skills/state.py +68 -0
- mycode/subagents/__init__.py +1 -0
- mycode/subagents/audit.py +212 -0
- mycode/subagents/concurrency.py +124 -0
- mycode/subagents/contracts.py +421 -0
- mycode/subagents/delegate.py +80 -0
- mycode/subagents/delegation.py +128 -0
- mycode/subagents/lifecycle.py +86 -0
- mycode/subagents/limits.py +7 -0
- mycode/subagents/observability.py +150 -0
- mycode/subagents/persistence.py +152 -0
- mycode/subagents/profiles.py +184 -0
- mycode/subagents/prompts.py +67 -0
- mycode/subagents/results.py +178 -0
- mycode/subagents/runtime.py +528 -0
- mycode/subagents/snapshots.py +211 -0
- mycode/subagents/tool_batch.py +260 -0
- mycode/tools/__init__.py +81 -0
- mycode/tools/base.py +222 -0
- mycode/tools/bounds.py +14 -0
- mycode/tools/command_executor.py +167 -0
- mycode/tools/command_output.py +166 -0
- mycode/tools/command_risk.py +596 -0
- mycode/tools/defaults.py +59 -0
- mycode/tools/edit_file.py +524 -0
- mycode/tools/file_mutation.py +30 -0
- mycode/tools/glob.py +247 -0
- mycode/tools/grep.py +324 -0
- mycode/tools/ignore.py +122 -0
- mycode/tools/inspect_changes.py +269 -0
- mycode/tools/load_skill.py +92 -0
- mycode/tools/memory.py +264 -0
- mycode/tools/path_permissions.py +78 -0
- mycode/tools/patterns.py +48 -0
- mycode/tools/permission_metadata.py +27 -0
- mycode/tools/process_tree.py +166 -0
- mycode/tools/read_file.py +242 -0
- mycode/tools/read_skill_resource.py +93 -0
- mycode/tools/registry.py +279 -0
- mycode/tools/run_command.py +237 -0
- mycode/tools/run_skill_script.py +206 -0
- mycode/tools/run_validation.py +107 -0
- mycode/tools/submit_result.py +93 -0
- mycode/tools/text.py +15 -0
- mycode/tools/validation_command.py +377 -0
- mycode/tools/workspace.py +33 -0
- mycode/tools/write_file.py +169 -0
- mycode_coding_agent-0.1.0.dist-info/METADATA +244 -0
- mycode_coding_agent-0.1.0.dist-info/RECORD +121 -0
- mycode_coding_agent-0.1.0.dist-info/WHEEL +4 -0
- mycode_coding_agent-0.1.0.dist-info/entry_points.txt +2 -0
- mycode_coding_agent-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from contextlib import AbstractContextManager
|
|
4
|
+
from dataclasses import dataclass, field, replace
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Literal
|
|
7
|
+
|
|
8
|
+
from mycode.context.compact import CompactState
|
|
9
|
+
from mycode.conversation import Conversation
|
|
10
|
+
from mycode.messages import Message
|
|
11
|
+
from mycode.persistence.session_store import (
|
|
12
|
+
DEFAULT_SESSION_TITLE,
|
|
13
|
+
SessionNotFoundError,
|
|
14
|
+
SessionRecord,
|
|
15
|
+
SessionStore,
|
|
16
|
+
WritableSession,
|
|
17
|
+
)
|
|
18
|
+
from mycode.project import ProjectIdentity
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
SessionStartMode = Literal["new", "continue", "resume"]
|
|
22
|
+
DEFAULT_SESSION_LIST_LIMIT = 10
|
|
23
|
+
AUTO_SESSION_TITLE_CHARS = 80
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class SessionStartRequest:
|
|
28
|
+
mode: SessionStartMode
|
|
29
|
+
session_id: str | None = None
|
|
30
|
+
|
|
31
|
+
def __post_init__(self) -> None:
|
|
32
|
+
if self.mode not in {"new", "continue", "resume"}:
|
|
33
|
+
raise ValueError(f"Unsupported session start mode: {self.mode}")
|
|
34
|
+
if self.mode == "resume":
|
|
35
|
+
if self.session_id is None or self.session_id.strip() == "":
|
|
36
|
+
raise ValueError("resume mode requires session_id.")
|
|
37
|
+
return
|
|
38
|
+
if self.session_id is not None:
|
|
39
|
+
raise ValueError(f"{self.mode} mode must not include session_id.")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class ActiveProjectSession:
|
|
44
|
+
store: SessionStore
|
|
45
|
+
project: ProjectIdentity
|
|
46
|
+
writer: WritableSession
|
|
47
|
+
_ownership: AbstractContextManager[WritableSession] = field(repr=False)
|
|
48
|
+
_created: bool = field(default=False, repr=False)
|
|
49
|
+
_finished: bool = field(default=False, init=False)
|
|
50
|
+
_compact_state_recovered: bool = field(default=False, init=False)
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def record(self) -> SessionRecord:
|
|
54
|
+
return replace(
|
|
55
|
+
self.writer.record,
|
|
56
|
+
status=self.writer.record.status if self._finished else "active",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def created(self) -> bool:
|
|
61
|
+
return self._created
|
|
62
|
+
|
|
63
|
+
def load_history(self) -> Conversation:
|
|
64
|
+
return self.writer.load_history()
|
|
65
|
+
|
|
66
|
+
def load_compact_state(self) -> CompactState:
|
|
67
|
+
loaded = self.writer.load_or_reset_compact_state()
|
|
68
|
+
self._compact_state_recovered = loaded.recovered_invalid_state
|
|
69
|
+
return loaded.state
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def compact_state_recovered(self) -> bool:
|
|
73
|
+
return self._compact_state_recovered
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def artifact_directory(self) -> Path:
|
|
77
|
+
return self.writer.layout.artifacts_directory
|
|
78
|
+
|
|
79
|
+
def persist_message(self, message: Message) -> None:
|
|
80
|
+
self.writer.append_messages([message])
|
|
81
|
+
if message.role == "user" and self.record.title == DEFAULT_SESSION_TITLE:
|
|
82
|
+
title = _session_title_from_message(message.content)
|
|
83
|
+
if title is not None:
|
|
84
|
+
self.writer.rename(title)
|
|
85
|
+
|
|
86
|
+
def persist_compact_state(self, state: CompactState) -> None:
|
|
87
|
+
self.writer.save_compact_state(state)
|
|
88
|
+
|
|
89
|
+
def close(self) -> None:
|
|
90
|
+
self._finish("closed")
|
|
91
|
+
|
|
92
|
+
def interrupt(self) -> None:
|
|
93
|
+
self._finish("interrupted")
|
|
94
|
+
|
|
95
|
+
def _finish(self, status: Literal["closed", "interrupted"]) -> None:
|
|
96
|
+
if self._finished:
|
|
97
|
+
return
|
|
98
|
+
try:
|
|
99
|
+
self.writer.finish(status)
|
|
100
|
+
finally:
|
|
101
|
+
self._finished = True
|
|
102
|
+
self._ownership.__exit__(None, None, None)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def list_project_sessions(
|
|
106
|
+
store: SessionStore,
|
|
107
|
+
project: ProjectIdentity,
|
|
108
|
+
*,
|
|
109
|
+
limit: int = DEFAULT_SESSION_LIST_LIMIT,
|
|
110
|
+
) -> list[SessionRecord]:
|
|
111
|
+
return store.list_sessions(project, limit=limit)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def delete_project_session(
|
|
115
|
+
store: SessionStore,
|
|
116
|
+
project: ProjectIdentity,
|
|
117
|
+
session_id: str,
|
|
118
|
+
) -> bool:
|
|
119
|
+
return store.delete_session(project, session_id)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def start_project_session(
|
|
123
|
+
store: SessionStore,
|
|
124
|
+
project: ProjectIdentity,
|
|
125
|
+
*,
|
|
126
|
+
request: SessionStartRequest,
|
|
127
|
+
) -> ActiveProjectSession:
|
|
128
|
+
if request.mode == "new":
|
|
129
|
+
return _open_active(store, project, created=True)
|
|
130
|
+
|
|
131
|
+
if request.mode == "continue":
|
|
132
|
+
sessions = list_project_sessions(store, project, limit=1)
|
|
133
|
+
if not sessions:
|
|
134
|
+
return _open_active(store, project, created=True)
|
|
135
|
+
return _open_active(store, project, record=sessions[0])
|
|
136
|
+
|
|
137
|
+
identifier = request.session_id or ""
|
|
138
|
+
session = store.get_session(project, identifier)
|
|
139
|
+
if session is None:
|
|
140
|
+
raise SessionNotFoundError(
|
|
141
|
+
f"Session not found in current project: {identifier}"
|
|
142
|
+
)
|
|
143
|
+
return _open_active(store, project, record=session)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _open_active(
|
|
147
|
+
store: SessionStore,
|
|
148
|
+
project: ProjectIdentity,
|
|
149
|
+
*,
|
|
150
|
+
record: SessionRecord | None = None,
|
|
151
|
+
created: bool = False,
|
|
152
|
+
) -> ActiveProjectSession:
|
|
153
|
+
ownership = store.open_session(
|
|
154
|
+
project,
|
|
155
|
+
None if record is None else record.id,
|
|
156
|
+
create=record is None,
|
|
157
|
+
)
|
|
158
|
+
writer = ownership.__enter__()
|
|
159
|
+
active = ActiveProjectSession(
|
|
160
|
+
store,
|
|
161
|
+
project,
|
|
162
|
+
writer,
|
|
163
|
+
ownership,
|
|
164
|
+
_created=created,
|
|
165
|
+
)
|
|
166
|
+
try:
|
|
167
|
+
writer.finish("interrupted")
|
|
168
|
+
return active
|
|
169
|
+
except BaseException:
|
|
170
|
+
active.interrupt()
|
|
171
|
+
raise
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _session_title_from_message(content: str) -> str | None:
|
|
175
|
+
normalized = " ".join(content.split())
|
|
176
|
+
if normalized == "":
|
|
177
|
+
return None
|
|
178
|
+
if len(normalized) <= AUTO_SESSION_TITLE_CHARS:
|
|
179
|
+
return normalized
|
|
180
|
+
return normalized[: AUTO_SESSION_TITLE_CHARS - 3].rstrip() + "..."
|