codenaut 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 (63) hide show
  1. agent/__init__.py +1 -0
  2. agent/agent.py +236 -0
  3. agent/events.py +145 -0
  4. agent/persistence.py +123 -0
  5. agent/session.py +99 -0
  6. client/__init__.py +1 -0
  7. client/llm_client.py +266 -0
  8. client/response.py +88 -0
  9. codenaut-0.1.0.dist-info/METADATA +199 -0
  10. codenaut-0.1.0.dist-info/RECORD +63 -0
  11. codenaut-0.1.0.dist-info/WHEEL +5 -0
  12. codenaut-0.1.0.dist-info/entry_points.txt +3 -0
  13. codenaut-0.1.0.dist-info/licenses/LICENSE +21 -0
  14. codenaut-0.1.0.dist-info/top_level.txt +12 -0
  15. config/__init__.py +1 -0
  16. config/config.py +173 -0
  17. config/loader.py +173 -0
  18. context/__init__.py +1 -0
  19. context/compaction.py +96 -0
  20. context/loop_detector.py +49 -0
  21. context/manager.py +257 -0
  22. hooks/__init__.py +1 -0
  23. hooks/hook_system.py +140 -0
  24. main.py +422 -0
  25. prompts/__init__.py +1 -0
  26. prompts/system.py +380 -0
  27. safety/__init__.py +1 -0
  28. safety/approval.py +157 -0
  29. skills/__init__.py +9 -0
  30. skills/discovery.py +97 -0
  31. skills/models.py +14 -0
  32. skills/registry.py +56 -0
  33. tools/__init__.py +1 -0
  34. tools/base.py +200 -0
  35. tools/builtin/__init__.py +67 -0
  36. tools/builtin/apply_patch.py +388 -0
  37. tools/builtin/edit_file.py +211 -0
  38. tools/builtin/glob.py +80 -0
  39. tools/builtin/grep.py +117 -0
  40. tools/builtin/list_dir.py +60 -0
  41. tools/builtin/memory.py +115 -0
  42. tools/builtin/read_file.py +133 -0
  43. tools/builtin/shell.py +160 -0
  44. tools/builtin/tasks.py +336 -0
  45. tools/builtin/web_fetch.py +55 -0
  46. tools/builtin/web_search.py +60 -0
  47. tools/builtin/write_file.py +111 -0
  48. tools/discovery.py +70 -0
  49. tools/mcp/__init__.py +1 -0
  50. tools/mcp/client.py +127 -0
  51. tools/mcp/mcp_manager.py +83 -0
  52. tools/mcp/mcp_tool.py +51 -0
  53. tools/registry.py +156 -0
  54. tools/subagents.py +148 -0
  55. ui/__init__.py +12 -0
  56. ui/completer.py +160 -0
  57. ui/prompt.py +65 -0
  58. ui/tui.py +669 -0
  59. utils/__init__.py +1 -0
  60. utils/errors.py +49 -0
  61. utils/file_tags.py +83 -0
  62. utils/paths.py +40 -0
  63. utils/text.py +79 -0
client/llm_client.py ADDED
@@ -0,0 +1,266 @@
1
+ import asyncio
2
+ from typing import Any, AsyncGenerator
3
+ import httpx
4
+ from openai import APIConnectionError, APIError, AsyncOpenAI, RateLimitError
5
+
6
+ from client.response import (
7
+ StreamEventType,
8
+ StreamEvent,
9
+ TextDelta,
10
+ TokenUsage,
11
+ ToolCall,
12
+ ToolCallDelta,
13
+ parse_tool_call_arguments,
14
+ )
15
+ from config.config import Config
16
+ from langsmith import trace
17
+
18
+ class LLMClient:
19
+ def __init__(self, config: Config) -> None:
20
+ self._client: AsyncOpenAI | None = None
21
+ self._max_retries: int = 3
22
+ self.config = config
23
+
24
+ def get_client(self) -> AsyncOpenAI:
25
+ if self._client is None:
26
+ self._client = AsyncOpenAI(
27
+ api_key=self.config.api_key,
28
+ base_url=self.config.base_url,
29
+ # Fail fast on a stalled endpoint instead of hanging on the SDK
30
+ # default (~10 min). `read` bounds silence between streamed chunks
31
+ # (and time-to-first-token); `connect` bounds the initial connect.
32
+ timeout=httpx.Timeout(90.0, connect=15.0),
33
+ # We run our own retry loop below; leaving the SDK's default (2)
34
+ # would multiply every attempt and compound stalls.
35
+ max_retries=0,
36
+ )
37
+ return self._client
38
+
39
+ async def close(self) -> None:
40
+ if self._client:
41
+ await self._client.close()
42
+ self._client = None
43
+
44
+ def _build_tools(self, tools: list[dict[str, Any]]):
45
+ return [
46
+ {
47
+ "type": "function",
48
+ "function": {
49
+ "name": tool["name"],
50
+ "description": tool.get("description", ""),
51
+ "parameters": tool.get(
52
+ "parameters",
53
+ {
54
+ "type": "object",
55
+ "properties": {},
56
+ },
57
+ ),
58
+ },
59
+ }
60
+ for tool in tools
61
+ ]
62
+
63
+ def _get_cached_tokens(self, usage: Any) -> int:
64
+ prompt_details = getattr(usage, "prompt_tokens_details", None)
65
+ if prompt_details is None:
66
+ return 0
67
+
68
+ cached_tokens = getattr(prompt_details, "cached_tokens", None)
69
+ return cached_tokens or 0
70
+
71
+ async def chat_completion(
72
+ self,
73
+ messages: list[dict[str, Any]],
74
+ tools: list[dict[str, Any]] | None = None,
75
+ stream: bool = True,
76
+ ) -> AsyncGenerator[StreamEvent, None]:
77
+
78
+ with trace(
79
+ name="llm.chat_completion",
80
+ run_type="llm",
81
+ inputs={
82
+ "messages": messages,
83
+ "model": self.config.model_name,
84
+ "tools_count": len(tools) if tools else 0,
85
+ },
86
+ metadata={"model": self.config.model_name},
87
+ ) as run:
88
+ client = self.get_client()
89
+
90
+ kwargs = {
91
+ "model": self.config.model_name,
92
+ "messages": messages,
93
+ "stream": stream,
94
+ }
95
+
96
+ if tools:
97
+ kwargs["tools"] = self._build_tools(tools)
98
+ kwargs["tool_choice"] = "auto"
99
+
100
+ for attempt in range(self._max_retries + 1):
101
+ try:
102
+ if stream:
103
+ async for event in self._stream_response(client, kwargs):
104
+ yield event
105
+ else:
106
+ event = await self._non_stream_response(client, kwargs)
107
+ yield event
108
+ return
109
+ except RateLimitError as e:
110
+ if attempt < self._max_retries:
111
+ wait_time = 2**attempt
112
+ await asyncio.sleep(wait_time)
113
+ else:
114
+ yield StreamEvent(
115
+ type=StreamEventType.ERROR,
116
+ error=f"Rate limit exceeded: {e}",
117
+ )
118
+ return
119
+ except APIConnectionError as e:
120
+ if attempt < self._max_retries:
121
+ wait_time = 2**attempt
122
+ await asyncio.sleep(wait_time)
123
+ else:
124
+ yield StreamEvent(
125
+ type=StreamEventType.ERROR,
126
+ error=f"Connection error: {e}",
127
+ )
128
+ return
129
+ except APIError as e:
130
+ yield StreamEvent(
131
+ type=StreamEventType.ERROR,
132
+ error=f"API error: {e}",
133
+ )
134
+ return
135
+
136
+ async def _stream_response(
137
+ self,
138
+ client: AsyncOpenAI,
139
+ kwargs: dict[str, Any],
140
+ ) -> AsyncGenerator[StreamEvent, None]:
141
+ response = await client.chat.completions.create(**kwargs)
142
+
143
+ finish_reason: str | None = None
144
+ usage: TokenUsage | None = None
145
+ tool_calls: dict[int, dict[str, Any]] = {}
146
+
147
+ async for chunk in response:
148
+ if hasattr(chunk, "usage") and chunk.usage:
149
+ usage = TokenUsage(
150
+ prompt_tokens=chunk.usage.prompt_tokens,
151
+ completion_tokens=chunk.usage.completion_tokens,
152
+ total_tokens=chunk.usage.total_tokens,
153
+ cached_tokens=self._get_cached_tokens(chunk.usage),
154
+ )
155
+
156
+ if not chunk.choices:
157
+ continue
158
+
159
+ choice = chunk.choices[0]
160
+ delta = choice.delta
161
+
162
+ if choice.finish_reason:
163
+ finish_reason = choice.finish_reason
164
+
165
+ if delta.content:
166
+ yield StreamEvent(
167
+ type=StreamEventType.TEXT_DELTA,
168
+ text_delta=TextDelta(delta.content),
169
+ )
170
+
171
+ if delta.tool_calls:
172
+ for tool_call_delta in delta.tool_calls:
173
+ idx = tool_call_delta.index
174
+
175
+ if idx not in tool_calls:
176
+ tool_calls[idx] = {
177
+ "id": "",
178
+ "name": "",
179
+ "arguments": "",
180
+ "started": False,
181
+ }
182
+
183
+ tool_call = tool_calls[idx]
184
+ if tool_call_delta.id:
185
+ tool_call["id"] = tool_call_delta.id
186
+
187
+ function = tool_call_delta.function
188
+ if function and function.name:
189
+ tool_call["name"] = function.name
190
+
191
+ if tool_call["name"] and not tool_call["started"]:
192
+ tool_call["started"] = True
193
+ yield StreamEvent(
194
+ type=StreamEventType.TOOL_CALL_START,
195
+ tool_call_delta=ToolCallDelta(
196
+ call_id=tool_call["id"],
197
+ name=tool_call["name"],
198
+ ),
199
+ )
200
+
201
+ if function and function.arguments:
202
+ tool_call["arguments"] += function.arguments
203
+ yield StreamEvent(
204
+ type=StreamEventType.TOOL_CALL_DELTA,
205
+ tool_call_delta=ToolCallDelta(
206
+ call_id=tool_call["id"],
207
+ name=tool_call["name"],
208
+ arguments_delta=function.arguments,
209
+ ),
210
+ )
211
+
212
+ for idx, tc in tool_calls.items():
213
+ yield StreamEvent(
214
+ type=StreamEventType.TOOL_CALL_COMPLETE,
215
+ tool_call=ToolCall(
216
+ call_id=tc["id"],
217
+ name=tc["name"],
218
+ arguments=parse_tool_call_arguments(tc["arguments"]),
219
+ ),
220
+ )
221
+
222
+ yield StreamEvent(
223
+ type=StreamEventType.MESSAGE_COMPLETE,
224
+ finish_reason=finish_reason,
225
+ usage=usage,
226
+ )
227
+
228
+ async def _non_stream_response(
229
+ self,
230
+ client: AsyncOpenAI,
231
+ kwargs: dict[str, Any],
232
+ ) -> StreamEvent:
233
+ response = await client.chat.completions.create(**kwargs)
234
+ choice = response.choices[0]
235
+ message = choice.message
236
+
237
+ text_delta = None
238
+ if message.content:
239
+ text_delta = TextDelta(content=message.content)
240
+
241
+ tool_calls: list[ToolCall] = []
242
+ if message.tool_calls:
243
+ for tc in message.tool_calls:
244
+ tool_calls.append(
245
+ ToolCall(
246
+ call_id=tc.id,
247
+ name=tc.function.name,
248
+ arguments=parse_tool_call_arguments(tc.function.arguments),
249
+ )
250
+ )
251
+
252
+ usage = None
253
+ if response.usage:
254
+ usage = TokenUsage(
255
+ prompt_tokens=response.usage.prompt_tokens,
256
+ completion_tokens=response.usage.completion_tokens,
257
+ total_tokens=response.usage.total_tokens,
258
+ cached_tokens=self._get_cached_tokens(response.usage),
259
+ )
260
+
261
+ return StreamEvent(
262
+ type=StreamEventType.MESSAGE_COMPLETE,
263
+ text_delta=text_delta,
264
+ finish_reason=choice.finish_reason,
265
+ usage=usage,
266
+ )
client/response.py ADDED
@@ -0,0 +1,88 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass
3
+ from enum import Enum
4
+ from typing import Any
5
+ import json
6
+
7
+
8
+ @dataclass
9
+ class TextDelta:
10
+ content: str
11
+
12
+ def __str__(self):
13
+ return self.content
14
+
15
+
16
+ class StreamEventType(str, Enum):
17
+ TEXT_DELTA = "text_delta"
18
+ MESSAGE_COMPLETE = "message_complete"
19
+ ERROR = "error"
20
+
21
+ TOOL_CALL_START = "tool_call_start"
22
+ TOOL_CALL_DELTA = "tool_call_delta"
23
+ TOOL_CALL_COMPLETE = "tool_call_complete"
24
+
25
+
26
+ @dataclass
27
+ class TokenUsage:
28
+ prompt_tokens: int = 0
29
+ completion_tokens: int = 0
30
+ total_tokens: int = 0
31
+ cached_tokens: int = 0
32
+
33
+ def __add__(self, other: TokenUsage):
34
+ return TokenUsage(
35
+ prompt_tokens=self.prompt_tokens + other.prompt_tokens,
36
+ completion_tokens=self.completion_tokens + other.completion_tokens,
37
+ total_tokens=self.total_tokens + other.total_tokens,
38
+ cached_tokens=self.cached_tokens + other.cached_tokens,
39
+ )
40
+
41
+
42
+ @dataclass
43
+ class ToolCallDelta:
44
+ call_id: str
45
+ name: str | None = None
46
+ arguments_delta: str = ""
47
+
48
+
49
+ @dataclass
50
+ class ToolCall:
51
+ call_id: str
52
+ name: str | None = None
53
+ arguments: str = ""
54
+
55
+
56
+ @dataclass
57
+ class StreamEvent:
58
+ type: StreamEventType
59
+ text_delta: TextDelta | None = None
60
+ error: str | None = None
61
+ finish_reason: str | None = None
62
+ tool_call_delta: ToolCallDelta | None = None
63
+ tool_call: ToolCall | None = None
64
+ usage: TokenUsage | None = None
65
+
66
+
67
+ @dataclass
68
+ class ToolResultMessage:
69
+ tool_call_id: str
70
+ content: str
71
+ is_error: bool = False
72
+
73
+ def to_openai_message(self) -> dict[str, Any]:
74
+ return {
75
+ "role": "tool",
76
+ "tool_call_id": self.tool_call_id,
77
+ "content": self.content,
78
+ }
79
+
80
+
81
+ def parse_tool_call_arguments(arguments_str: str) -> dict[str, Any]:
82
+ if not arguments_str:
83
+ return {}
84
+
85
+ try:
86
+ return json.loads(arguments_str)
87
+ except json.JSONDecodeError:
88
+ return {"raw_arguments": arguments_str}
@@ -0,0 +1,199 @@
1
+ Metadata-Version: 2.4
2
+ Name: codenaut
3
+ Version: 0.1.0
4
+ Summary: A terminal-based OpenAI-compatible coding agent with tools, approvals, MCP, and session persistence.
5
+ Author: nick211908
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/nick211908/coding-agent
8
+ Project-URL: Issues, https://github.com/nick211908/coding-agent/issues
9
+ Project-URL: Repository, https://github.com/nick211908/coding-agent
10
+ Keywords: agent,cli,coding,openai,mcp
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Software Development
17
+ Classifier: Topic :: Utilities
18
+ Requires-Python: >=3.13
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: click>=8.4.2
22
+ Requires-Dist: ddgs>=9.14.4
23
+ Requires-Dist: fastmcp>=3.4.2
24
+ Requires-Dist: httpx>=0.28.1
25
+ Requires-Dist: langsmith>=0.13.0
26
+ Requires-Dist: openai>=2.44.0
27
+ Requires-Dist: platformdirs>=4.10.0
28
+ Requires-Dist: prompt-toolkit>=3.0.0
29
+ Requires-Dist: pydantic>=2.13.4
30
+ Requires-Dist: python-dotenv>=1.1.1
31
+ Requires-Dist: rich>=14.2.0
32
+ Requires-Dist: tiktoken>=0.13.0
33
+ Requires-Dist: tomli>=2.4.1; python_version < "3.11"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
36
+ Requires-Dist: pylint>=4.0.6; extra == "dev"
37
+ Dynamic: license-file
38
+
39
+ # code-it
40
+
41
+ A terminal-based, OpenAI-compatible coding agent that reads, edits, and runs code in your project through a ReAct-style tool-calling loop with approvals, hooks, MCP, and session persistence.
42
+
43
+ ## Install
44
+
45
+ Prerequisites: Python 3.13+ and `uv`.
46
+
47
+ Install dependencies into the project-managed environment:
48
+
49
+ ```bash
50
+ uv sync
51
+ ```
52
+
53
+ Build distributable artifacts:
54
+
55
+ ```bash
56
+ uv build
57
+ ```
58
+
59
+ This creates:
60
+
61
+ - `dist/code_it-<version>-py3-none-any.whl`
62
+ - `dist/code_it-<version>.tar.gz`
63
+
64
+ ## First Run
65
+
66
+ After syncing the project environment, configure credentials:
67
+
68
+ ```bash
69
+ uv run code-it --configure
70
+ ```
71
+
72
+ This prompts for:
73
+
74
+ - your API key
75
+ - an optional base URL for any OpenAI-compatible provider
76
+
77
+ The values are saved in your user config file at:
78
+
79
+ - Windows: `%APPDATA%\code-it\config.toml`
80
+ - macOS: `~/Library/Application Support/code-it/config.toml`
81
+ - Linux: `~/.config/code-it/config.toml`
82
+
83
+ You can still use a project-local `.env` file or `API_KEY` and `BASE_URL` environment variables, but they are no longer required for packaged installs.
84
+
85
+ ## Usage
86
+
87
+ Run interactively:
88
+
89
+ ```bash
90
+ uv run code-it
91
+ ```
92
+
93
+ Run a single prompt:
94
+
95
+ ```bash
96
+ uv run code-it "Summarize what this repo does"
97
+ ```
98
+
99
+ Target a specific working directory:
100
+
101
+ ```bash
102
+ uv run code-it --cwd /path/to/project "Find and fix the failing test"
103
+ ```
104
+
105
+ Update saved credentials later:
106
+
107
+ ```bash
108
+ uv run code-it --configure
109
+ ```
110
+
111
+ Inside an interactive session:
112
+
113
+ ```text
114
+ [user]> /setup # update saved API credentials
115
+ [user]> /tools # list available tools
116
+ [user]> /mcp # list connected MCP servers
117
+ [user]> /approval auto-edit # change the approval policy
118
+ [user]> /save # save the current session
119
+ [user]> /sessions # list saved sessions
120
+ [user]> /resume <session_id> # resume a saved session
121
+ [user]> /checkpoint # snapshot the current session
122
+ [user]> /stats # show token/turn usage stats
123
+ [user]> /exit # quit
124
+ ```
125
+
126
+ ## Project Config
127
+
128
+ Project-level overrides still live in `.ai-agent/config.toml`.
129
+
130
+ You can also add:
131
+
132
+ - `AGENT.MD` for project-specific developer instructions
133
+ - `.ai-agent/skills/<skill-name>/SKILL.md` for reusable skills
134
+ - `.ai-agent/tools/*.py` for custom tools
135
+
136
+ ## Publish Checklist
137
+
138
+ 1. Bump `version` in `pyproject.toml`.
139
+ 2. Run tests.
140
+ 3. Build the wheel and sdist with `uv build --no-sources`.
141
+ 4. Test the CLI with `uv run code-it --configure`.
142
+ 5. Upload to TestPyPI.
143
+ 6. Verify both `uv run code-it --configure` and `uv run code-it` work.
144
+ 7. Publish to PyPI.
145
+
146
+ ## Publish With uv
147
+
148
+ Recommended local release flow:
149
+
150
+ ```bash
151
+ uv sync --frozen
152
+ uv run python -m unittest tests.test_config_loader tests.test_skills
153
+ uv build --no-sources
154
+ uv run --isolated --no-project --with dist/*.whl tests/smoke_test.py
155
+ uv run --isolated --no-project --with dist/*.tar.gz tests/smoke_test.py
156
+ ```
157
+
158
+ TestPyPI publish:
159
+
160
+ ```bash
161
+ $env:UV_PUBLISH_TOKEN="pypi-***"
162
+ uv publish --publish-url https://test.pypi.org/legacy/
163
+ ```
164
+
165
+ PyPI publish:
166
+
167
+ ```bash
168
+ $env:UV_PUBLISH_TOKEN="pypi-***"
169
+ uv publish
170
+ ```
171
+
172
+ For the first release, verify the package name on PyPI before publishing. If `code-it` is already taken, rename `project.name` in `pyproject.toml` before uploading.
173
+
174
+ ## Trusted Publishing
175
+
176
+ A GitHub Actions release workflow is included at `.github/workflows/publish.yml`.
177
+
178
+ To use it:
179
+
180
+ 1. Create the project on PyPI.
181
+ 2. In the PyPI project settings, add a Trusted Publisher for this GitHub repository.
182
+ 3. In GitHub, create an environment named `pypi`.
183
+ 4. Push a version tag like `v0.1.0`.
184
+
185
+ That workflow will:
186
+
187
+ - sync with `uv`
188
+ - run the unit tests
189
+ - build the wheel and source distribution
190
+ - smoke-test both artifacts
191
+ - publish with `uv publish`
192
+
193
+ ## Why `uv` Instead of `pip install -e .`
194
+
195
+ This repository already carries a `uv.lock`, so `uv sync` is the safer default for development. It avoids polluting a shared Python installation and reduces resolver conflicts with unrelated packages already installed on your machine.
196
+
197
+ ## License
198
+
199
+ Distributed under the [MIT License](LICENSE).
@@ -0,0 +1,63 @@
1
+ main.py,sha256=LFHn2Ov49-zNQqu3AX24UrH2PGXbNPwcnUs1qtgd7PM,17759
2
+ agent/__init__.py,sha256=nZIx5ApwFdMjKOQPtS-4qR5I8UA6LRlu4rqFAErAgcA,34
3
+ agent/agent.py,sha256=3BpqyIVlZwn5fUjk4eUpKGYAnh8VHWmp6YtvJj9oJfU,8782
4
+ agent/events.py,sha256=WYJ5vcaZ3ItVOjyXla1TWrKb45KN8zpIK965VZUsxks,3974
5
+ agent/persistence.py,sha256=8JK1fCzEHVLJC10DPkb1Amlg_Hn9ASGwjdeMoN96zhU,4165
6
+ agent/session.py,sha256=3Pq0muVZY85pr6a6PLohyc42BhfD6yZt2PFfCNJXRZE,3411
7
+ client/__init__.py,sha256=M4ahv6R9Kj3qD4JBpyUPgpjHnGy1riJM_iTn79Jw6Ig,31
8
+ client/llm_client.py,sha256=eT2zlCWfQd7ea56IM61nhxSVH8oIOyn-IpypVmZvySY,9693
9
+ client/response.py,sha256=K6tSQalNHxnr3KrpawYd93Clsr9yd8LOdLkE4W3lHDQ,2107
10
+ codenaut-0.1.0.dist-info/licenses/LICENSE,sha256=1LZfjHOqkRejkZyYAe7R1xGMZprN0ExOIaM-df7gYY4,1067
11
+ config/__init__.py,sha256=FXE68WpVrO9ePz1Hnd9ASfk4wXYD2dF6vzbeKvmm0bI,40
12
+ config/config.py,sha256=Yi1vpfOfEzv4dQ9qcQ807cZr3poBvJ7UhShRd1FIxUA,4928
13
+ config/loader.py,sha256=cS2uFa5XL90w59nKkfYgNLrAH4JP0yMKQfMpfAL4fug,5123
14
+ context/__init__.py,sha256=g7OlaNtUW3JOROZ8rYPjVZum6oVlr5icfNO07N6xJUg,39
15
+ context/compaction.py,sha256=vBS2qcMqKFcpEmCKj4cvwbmF1XxQw3EeF-44RoEytTs,3437
16
+ context/loop_detector.py,sha256=dTyxIIZEijjrs1chScexUu7Dlb2TAmEkOwc_D2Ac_lk,1613
17
+ context/manager.py,sha256=L-6Vs_5vV09PkzWrpnTwvaYNgzmK5fWRqm0xO1EeWPA,7934
18
+ hooks/__init__.py,sha256=2qnpTS65dGZvePaSWNu-J0azm4YNSYdbw3WCEKpmikY,30
19
+ hooks/hook_system.py,sha256=gb1QVUFiCEpSVdQIH7zsOEKbZt1w-TovY4P_2O200Xw,4394
20
+ prompts/__init__.py,sha256=oHvySSUnV3dY00xMgRyLocCXhQrTcm0lXMGONiKONh4,37
21
+ prompts/system.py,sha256=u5dy-e1iFwZLwyKdk7Ped9oOcHXsVI67sqjVKXNc4UU,20413
22
+ safety/__init__.py,sha256=OY1s9BJe2zBTV0P5Rr5TsIoFPLza_tvzl7ERl6taZ64,36
23
+ safety/approval.py,sha256=t00EzL5AoGz1C8wow5ELvPMRYQDANd6eY52x4yM_0Gs,4613
24
+ skills/__init__.py,sha256=t_e173VDOEU9SLxy20dYG3-mVgPuyGZdPvs3o_90wGs,223
25
+ skills/discovery.py,sha256=AgxBMNRgjGV1riHFCOs0WyVD6JK6vACjUs3uXdnPFWM,3313
26
+ skills/models.py,sha256=IfsqiZh8cEkii6sF_tS7C6f4juNOZVG-4snYP3hPlBc,318
27
+ skills/registry.py,sha256=SEbphwtXp8ER20x66gPu0W2iKgYDjmz9NPN5ijSrpWA,1819
28
+ tools/__init__.py,sha256=-l-DUD3QxdEYoGwI37vjwsKulVxt8m9NsLr3UJKCrzs,44
29
+ tools/base.py,sha256=76YetOiyB_2m4HG4rVhVrtGlcoAS4WiaHBf1EwKTcWE,5506
30
+ tools/discovery.py,sha256=xBI3StV_JuWy6FJQ0RhfVxj2fANCnJ6cGWXxga0j5dc,2174
31
+ tools/registry.py,sha256=-GzxUcWZaYJ5JIlRKBAWKDowSzN9vyhwJoOxbj2Ue6U,5302
32
+ tools/subagents.py,sha256=sG3K6zSmC09bHLHivJwJxObkVn_RHjCp034b0CMZsdg,5051
33
+ tools/builtin/__init__.py,sha256=JsemuTRhOaHnOSTebFKBQ_xjg6hBEpc5YImnqxCRrpo,1576
34
+ tools/builtin/apply_patch.py,sha256=VxhX8hYeJAF3xYxMzIKaEgrK68dGyG4M0UHunoVhQ0w,12272
35
+ tools/builtin/edit_file.py,sha256=tBQDO_q3Hbr9PZ7Q2361D9eQLSrqXfwaxoNB14_WgSA,7229
36
+ tools/builtin/glob.py,sha256=ed_fVL2bB6A8DmbqbXkmoCqhfYOQNz-3DY9dNeH74yI,2425
37
+ tools/builtin/grep.py,sha256=jClE2-uICi7onANmAyxvgfUbEhppHNnc0ioHYi1ltDw,3690
38
+ tools/builtin/list_dir.py,sha256=xxJ6smvuVE8qeOR5CRPgiu2aM-oN-w5ifUn8NEumKkI,1818
39
+ tools/builtin/memory.py,sha256=PvSayh_zikx47Dny05ZVGWswpgTpt-vYrmwprcnmX-o,4247
40
+ tools/builtin/read_file.py,sha256=X3OBTNNDFRCX-3n6LX79WykENy67rht7VDTVUs372nE,4341
41
+ tools/builtin/shell.py,sha256=BGKWHkzEaf6GSRTu8K1o9yw8v2_MFp7OD5ZMIqcnNVk,4822
42
+ tools/builtin/tasks.py,sha256=IChW-WpDYy-JyyvvgE4LzQFwOwTp4h7Ci4hfaV6RqeY,12526
43
+ tools/builtin/web_fetch.py,sha256=ooPzI8fBruEWeT6n7No4A5HGC6WYPXx0eSUn_PaAXpo,1824
44
+ tools/builtin/web_search.py,sha256=PD5upTRAN2xZJ_dnRX8ZO28sOwSwd-yrCIey0Fda1VY,1821
45
+ tools/builtin/write_file.py,sha256=07QtMR9LqVXN-G5Rtfus2WPPkMlQbnKhyY4QTJS26YE,3495
46
+ tools/mcp/__init__.py,sha256=hruMBtSTRDIsC5MA83-BLdaQzjy_LJKKq1OlGzxrmU8,39
47
+ tools/mcp/client.py,sha256=TwjIZr_55qnmy1r_aWNVgM-4Ng-B-9WSoE0w9jmWVkQ,3855
48
+ tools/mcp/mcp_manager.py,sha256=5FbntJ-C0BXNHmRA3q_C53Us7GT_yuXWUOjmrM0D-co,2399
49
+ tools/mcp/mcp_tool.py,sha256=yuWNeJ2L6fWy0afi7Kszzv9v4GNQWNVLqp5eOCvMfzM,1498
50
+ ui/__init__.py,sha256=iHQwkfGYAYTxR3tVv6TK-k6V-KZ-LufnUx0TspOlgcw,252
51
+ ui/completer.py,sha256=K9ElFxbmDgYZTTTGCXIn8VYjjZz82z3dnnE73Dh_85U,5246
52
+ ui/prompt.py,sha256=KrUQaQUFX-1cUfBy1SFKQrLcJWEihBRpf0D0hpkA83Y,2172
53
+ ui/tui.py,sha256=Lwn9ZpXXC0i2-NNBq5QfFM1-8oGdC0kUmL_X5ETlUzw,21836
54
+ utils/__init__.py,sha256=ZzhtEkM5EYuz8Oi6eOevMF4Lhyaz30f4Bh7exMBuByo,30
55
+ utils/errors.py,sha256=TGafz7_2XvFqJJKhU9d8gUVHLGkbFCZ9sNKDZpwLPOA,1429
56
+ utils/file_tags.py,sha256=vp5HRagPNNRxt1p3ZgDLcck3nI70gtJXoc8iXTY23QM,2876
57
+ utils/paths.py,sha256=UWhZu9pgrl--ganIBM3UVCZ1xRsKoxKbmWfSKaMCnrw,834
58
+ utils/text.py,sha256=Ja4ROXd9wPeyjxssSkVddHHGnwXyFJxCVrDPFtftvTQ,2077
59
+ codenaut-0.1.0.dist-info/METADATA,sha256=iiaWDohrNywMHQPBrqq-XbExp3X2qGZn338ONlvDx_w,5644
60
+ codenaut-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
61
+ codenaut-0.1.0.dist-info/entry_points.txt,sha256=wrn5K1tYT1aVSP-KckqtwtobA9oISGRWfK5axMwOkKI,59
62
+ codenaut-0.1.0.dist-info/top_level.txt,sha256=rCQsbocwJaqy1bpr0B0YuJNWQd31KXCmv1OjIquEWus,76
63
+ codenaut-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ code-it = main:main
3
+ codenaut = main:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nick211908
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,12 @@
1
+ agent
2
+ client
3
+ config
4
+ context
5
+ hooks
6
+ main
7
+ prompts
8
+ safety
9
+ skills
10
+ tools
11
+ ui
12
+ utils
config/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Configuration models and loaders."""