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.
Files changed (121) hide show
  1. mycode/__init__.py +0 -0
  2. mycode/adapters/__init__.py +21 -0
  3. mycode/adapters/jsonl.py +692 -0
  4. mycode/agent/__init__.py +25 -0
  5. mycode/agent/events.py +111 -0
  6. mycode/agent/outcome.py +103 -0
  7. mycode/agent/progress.py +373 -0
  8. mycode/agent/runner.py +1481 -0
  9. mycode/application/__init__.py +38 -0
  10. mycode/application/agent_session.py +367 -0
  11. mycode/application/events.py +59 -0
  12. mycode/application/runtime.py +211 -0
  13. mycode/application/sessions.py +180 -0
  14. mycode/cli.py +840 -0
  15. mycode/config.py +355 -0
  16. mycode/context/__init__.py +1 -0
  17. mycode/context/artifacts.py +672 -0
  18. mycode/context/budget.py +752 -0
  19. mycode/context/builder.py +112 -0
  20. mycode/context/compact.py +795 -0
  21. mycode/context/tool_result_format.py +199 -0
  22. mycode/context/tool_result_retention.py +261 -0
  23. mycode/conversation.py +78 -0
  24. mycode/error_handling.py +481 -0
  25. mycode/event_format.py +147 -0
  26. mycode/instructions.py +285 -0
  27. mycode/llm.py +771 -0
  28. mycode/mcp/__init__.py +41 -0
  29. mycode/mcp/client.py +44 -0
  30. mycode/mcp/config.py +207 -0
  31. mycode/mcp/errors.py +302 -0
  32. mycode/mcp/manager.py +339 -0
  33. mycode/mcp/models.py +20 -0
  34. mycode/mcp/result_adapter.py +58 -0
  35. mycode/mcp/tool_adapter.py +145 -0
  36. mycode/mcp/trust.py +313 -0
  37. mycode/memory.py +570 -0
  38. mycode/memory_context.py +245 -0
  39. mycode/messages.py +63 -0
  40. mycode/observability.py +28 -0
  41. mycode/permissions.py +262 -0
  42. mycode/persistence/__init__.py +1 -0
  43. mycode/persistence/filesystem.py +291 -0
  44. mycode/persistence/project_storage.py +208 -0
  45. mycode/persistence/session_lock.py +138 -0
  46. mycode/persistence/session_store.py +503 -0
  47. mycode/presentation/__init__.py +1 -0
  48. mycode/presentation/cli/__init__.py +14 -0
  49. mycode/presentation/cli/confirmer.py +116 -0
  50. mycode/presentation/cli/mcp_trust.py +61 -0
  51. mycode/presentation/cli/presenter.py +320 -0
  52. mycode/presentation/cli/session_menu.py +146 -0
  53. mycode/presentation/cli/subagent_observer.py +124 -0
  54. mycode/presentation/command_format.py +90 -0
  55. mycode/presentation/commands.py +95 -0
  56. mycode/presentation/tui/__init__.py +6 -0
  57. mycode/presentation/tui/app.py +1351 -0
  58. mycode/presentation/tui/interactions.py +253 -0
  59. mycode/presentation/tui/presenter.py +266 -0
  60. mycode/presentation/tui/screens.py +305 -0
  61. mycode/presentation/tui/widgets.py +214 -0
  62. mycode/project.py +22 -0
  63. mycode/prompts.py +181 -0
  64. mycode/reasoning.py +40 -0
  65. mycode/session.py +86 -0
  66. mycode/skills/__init__.py +27 -0
  67. mycode/skills/builtin/database-recovery/SKILL.md +138 -0
  68. mycode/skills/builtin/database-recovery/references/sqlite.md +235 -0
  69. mycode/skills/registry.py +295 -0
  70. mycode/skills/state.py +68 -0
  71. mycode/subagents/__init__.py +1 -0
  72. mycode/subagents/audit.py +212 -0
  73. mycode/subagents/concurrency.py +124 -0
  74. mycode/subagents/contracts.py +421 -0
  75. mycode/subagents/delegate.py +80 -0
  76. mycode/subagents/delegation.py +128 -0
  77. mycode/subagents/lifecycle.py +86 -0
  78. mycode/subagents/limits.py +7 -0
  79. mycode/subagents/observability.py +150 -0
  80. mycode/subagents/persistence.py +152 -0
  81. mycode/subagents/profiles.py +184 -0
  82. mycode/subagents/prompts.py +67 -0
  83. mycode/subagents/results.py +178 -0
  84. mycode/subagents/runtime.py +528 -0
  85. mycode/subagents/snapshots.py +211 -0
  86. mycode/subagents/tool_batch.py +260 -0
  87. mycode/tools/__init__.py +81 -0
  88. mycode/tools/base.py +222 -0
  89. mycode/tools/bounds.py +14 -0
  90. mycode/tools/command_executor.py +167 -0
  91. mycode/tools/command_output.py +166 -0
  92. mycode/tools/command_risk.py +596 -0
  93. mycode/tools/defaults.py +59 -0
  94. mycode/tools/edit_file.py +524 -0
  95. mycode/tools/file_mutation.py +30 -0
  96. mycode/tools/glob.py +247 -0
  97. mycode/tools/grep.py +324 -0
  98. mycode/tools/ignore.py +122 -0
  99. mycode/tools/inspect_changes.py +269 -0
  100. mycode/tools/load_skill.py +92 -0
  101. mycode/tools/memory.py +264 -0
  102. mycode/tools/path_permissions.py +78 -0
  103. mycode/tools/patterns.py +48 -0
  104. mycode/tools/permission_metadata.py +27 -0
  105. mycode/tools/process_tree.py +166 -0
  106. mycode/tools/read_file.py +242 -0
  107. mycode/tools/read_skill_resource.py +93 -0
  108. mycode/tools/registry.py +279 -0
  109. mycode/tools/run_command.py +237 -0
  110. mycode/tools/run_skill_script.py +206 -0
  111. mycode/tools/run_validation.py +107 -0
  112. mycode/tools/submit_result.py +93 -0
  113. mycode/tools/text.py +15 -0
  114. mycode/tools/validation_command.py +377 -0
  115. mycode/tools/workspace.py +33 -0
  116. mycode/tools/write_file.py +169 -0
  117. mycode_coding_agent-0.1.0.dist-info/METADATA +244 -0
  118. mycode_coding_agent-0.1.0.dist-info/RECORD +121 -0
  119. mycode_coding_agent-0.1.0.dist-info/WHEEL +4 -0
  120. mycode_coding_agent-0.1.0.dist-info/entry_points.txt +2 -0
  121. 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() + "..."