luckyd-code 1.2.2__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 (127) hide show
  1. luckyd_code/__init__.py +54 -0
  2. luckyd_code/__main__.py +5 -0
  3. luckyd_code/_agent_loop.py +551 -0
  4. luckyd_code/_data_dir.py +73 -0
  5. luckyd_code/agent.py +38 -0
  6. luckyd_code/analytics/__init__.py +18 -0
  7. luckyd_code/analytics/reporter.py +195 -0
  8. luckyd_code/analytics/scanner.py +443 -0
  9. luckyd_code/analytics/smells.py +316 -0
  10. luckyd_code/analytics/trends.py +303 -0
  11. luckyd_code/api.py +473 -0
  12. luckyd_code/audit_daemon.py +845 -0
  13. luckyd_code/autonomous_fixer.py +473 -0
  14. luckyd_code/background.py +159 -0
  15. luckyd_code/backup.py +237 -0
  16. luckyd_code/brain/__init__.py +84 -0
  17. luckyd_code/brain/assembler.py +100 -0
  18. luckyd_code/brain/chunker.py +345 -0
  19. luckyd_code/brain/constants.py +73 -0
  20. luckyd_code/brain/embedder.py +163 -0
  21. luckyd_code/brain/graph.py +311 -0
  22. luckyd_code/brain/indexer.py +316 -0
  23. luckyd_code/brain/parser.py +140 -0
  24. luckyd_code/brain/retriever.py +234 -0
  25. luckyd_code/cli.py +894 -0
  26. luckyd_code/cli_commands/__init__.py +1 -0
  27. luckyd_code/cli_commands/audit.py +120 -0
  28. luckyd_code/cli_commands/background.py +83 -0
  29. luckyd_code/cli_commands/brain.py +87 -0
  30. luckyd_code/cli_commands/config.py +75 -0
  31. luckyd_code/cli_commands/dispatcher.py +695 -0
  32. luckyd_code/cli_commands/sessions.py +41 -0
  33. luckyd_code/cli_entry.py +147 -0
  34. luckyd_code/cli_utils.py +112 -0
  35. luckyd_code/config.py +205 -0
  36. luckyd_code/context.py +214 -0
  37. luckyd_code/cost_tracker.py +209 -0
  38. luckyd_code/error_reporter.py +508 -0
  39. luckyd_code/exceptions.py +39 -0
  40. luckyd_code/export.py +126 -0
  41. luckyd_code/feedback_analyzer.py +290 -0
  42. luckyd_code/file_watcher.py +258 -0
  43. luckyd_code/git/__init__.py +11 -0
  44. luckyd_code/git/auto_commit.py +157 -0
  45. luckyd_code/git/tools.py +85 -0
  46. luckyd_code/hooks.py +236 -0
  47. luckyd_code/indexer.py +280 -0
  48. luckyd_code/init.py +39 -0
  49. luckyd_code/keybindings.py +77 -0
  50. luckyd_code/log.py +55 -0
  51. luckyd_code/mcp/__init__.py +6 -0
  52. luckyd_code/mcp/client.py +184 -0
  53. luckyd_code/memory/__init__.py +19 -0
  54. luckyd_code/memory/manager.py +339 -0
  55. luckyd_code/metrics/__init__.py +5 -0
  56. luckyd_code/model_registry.py +131 -0
  57. luckyd_code/orchestrator.py +204 -0
  58. luckyd_code/permissions/__init__.py +1 -0
  59. luckyd_code/permissions/manager.py +103 -0
  60. luckyd_code/planner.py +361 -0
  61. luckyd_code/plugins.py +91 -0
  62. luckyd_code/py.typed +0 -0
  63. luckyd_code/retry.py +57 -0
  64. luckyd_code/router.py +417 -0
  65. luckyd_code/sandbox.py +156 -0
  66. luckyd_code/self_critique.py +2 -0
  67. luckyd_code/self_improve.py +274 -0
  68. luckyd_code/sessions.py +114 -0
  69. luckyd_code/settings.py +72 -0
  70. luckyd_code/skills/__init__.py +8 -0
  71. luckyd_code/skills/review.py +22 -0
  72. luckyd_code/skills/security.py +17 -0
  73. luckyd_code/tasks/__init__.py +1 -0
  74. luckyd_code/tasks/manager.py +102 -0
  75. luckyd_code/templates/icon-192.png +0 -0
  76. luckyd_code/templates/icon-512.png +0 -0
  77. luckyd_code/templates/index.html +1965 -0
  78. luckyd_code/templates/manifest.json +14 -0
  79. luckyd_code/templates/src/app.js +694 -0
  80. luckyd_code/templates/src/body.html +767 -0
  81. luckyd_code/templates/src/cdn.txt +2 -0
  82. luckyd_code/templates/src/style.css +474 -0
  83. luckyd_code/templates/sw.js +31 -0
  84. luckyd_code/templates/test.html +6 -0
  85. luckyd_code/themes.py +48 -0
  86. luckyd_code/tools/__init__.py +97 -0
  87. luckyd_code/tools/agent_tools.py +65 -0
  88. luckyd_code/tools/bash.py +360 -0
  89. luckyd_code/tools/brain_tools.py +137 -0
  90. luckyd_code/tools/browser.py +369 -0
  91. luckyd_code/tools/datetime_tool.py +34 -0
  92. luckyd_code/tools/dockerfile_gen.py +212 -0
  93. luckyd_code/tools/file_ops.py +381 -0
  94. luckyd_code/tools/game_gen.py +360 -0
  95. luckyd_code/tools/git_tools.py +130 -0
  96. luckyd_code/tools/git_worktree.py +63 -0
  97. luckyd_code/tools/path_validate.py +64 -0
  98. luckyd_code/tools/project_gen.py +187 -0
  99. luckyd_code/tools/readme_gen.py +227 -0
  100. luckyd_code/tools/registry.py +157 -0
  101. luckyd_code/tools/shell_detect.py +109 -0
  102. luckyd_code/tools/web.py +89 -0
  103. luckyd_code/tools/youtube.py +187 -0
  104. luckyd_code/tools_bridge.py +144 -0
  105. luckyd_code/undo.py +126 -0
  106. luckyd_code/update.py +60 -0
  107. luckyd_code/verify.py +360 -0
  108. luckyd_code/web_app.py +176 -0
  109. luckyd_code/web_routes/__init__.py +23 -0
  110. luckyd_code/web_routes/background.py +73 -0
  111. luckyd_code/web_routes/brain.py +109 -0
  112. luckyd_code/web_routes/cost.py +12 -0
  113. luckyd_code/web_routes/files.py +133 -0
  114. luckyd_code/web_routes/memories.py +94 -0
  115. luckyd_code/web_routes/misc.py +67 -0
  116. luckyd_code/web_routes/project.py +48 -0
  117. luckyd_code/web_routes/review.py +20 -0
  118. luckyd_code/web_routes/sessions.py +44 -0
  119. luckyd_code/web_routes/settings.py +43 -0
  120. luckyd_code/web_routes/static.py +70 -0
  121. luckyd_code/web_routes/update.py +19 -0
  122. luckyd_code/web_routes/ws.py +237 -0
  123. luckyd_code-1.2.2.dist-info/METADATA +297 -0
  124. luckyd_code-1.2.2.dist-info/RECORD +127 -0
  125. luckyd_code-1.2.2.dist-info/WHEEL +4 -0
  126. luckyd_code-1.2.2.dist-info/entry_points.txt +3 -0
  127. luckyd_code-1.2.2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,237 @@
1
+ """WebSocket chat handler with tool execution, model fallback, and streaming."""
2
+
3
+ import asyncio
4
+ import json
5
+
6
+ from fastapi import APIRouter, WebSocket, WebSocketDisconnect
7
+
8
+ from ..api import _repair_json
9
+ from ..log import get_logger
10
+
11
+ logger = get_logger()
12
+ router = APIRouter()
13
+
14
+ MAX_MESSAGE_LENGTH = 10000
15
+ MAX_TOOL_LOOPS = 100
16
+
17
+
18
+ @router.websocket("/ws")
19
+ async def websocket_endpoint(ws: WebSocket):
20
+ await ws.accept()
21
+ from ..web_app import stream_chat
22
+
23
+ state = ws.app.state.web_state
24
+ config = state.config
25
+ context = state.context
26
+ registry = state.registry
27
+ mcp = state.mcp
28
+ memory_module = state.memory_module
29
+
30
+ try:
31
+ while True:
32
+ data = await asyncio.wait_for(ws.receive_text(), timeout=300.0)
33
+ msg = json.loads(data)
34
+ msg_type = msg.get("type", "message")
35
+
36
+ if msg_type == "message":
37
+ user_text = msg.get("content", "").strip()
38
+ if not user_text:
39
+ continue
40
+
41
+ if len(user_text) > MAX_MESSAGE_LENGTH:
42
+ await ws.send_json({
43
+ "type": "error",
44
+ "content": f"Message too long (max {MAX_MESSAGE_LENGTH} characters)",
45
+ })
46
+ continue
47
+
48
+ context.add_user_message(user_text)
49
+
50
+ text_buffer = ""
51
+ pending_tool_calls = None
52
+ tool_loop_count = 0
53
+ tool_call_count = 0
54
+ active_model = config.model
55
+ current_tier = 2
56
+
57
+ from .. import settings as cfg
58
+ web_settings = cfg.load_settings()
59
+ auto_route = web_settings.get("auto_route", True)
60
+ from ..router import resolve_initial_route, escalate_tier
61
+ routing = resolve_initial_route(
62
+ user_text, 0, config.provider, config.model, auto_route,
63
+ )
64
+ if routing.tier_changed:
65
+ active_model = routing.model
66
+ await ws.send_json({
67
+ "type": "text",
68
+ "content": f"[Routing: {routing.tier_description} → {active_model}]\n\n",
69
+ })
70
+
71
+ all_tools = registry.list_tools()
72
+ all_tools.extend(mcp.get_all_tools())
73
+
74
+ while tool_loop_count < MAX_TOOL_LOOPS:
75
+ messages = context.get_messages()
76
+ text_buffer = ""
77
+ pending_tool_calls = None
78
+ tool_reasoning = ""
79
+
80
+ if auto_route:
81
+ escalation = escalate_tier(
82
+ user_text, tool_call_count, config.provider,
83
+ config.model, active_model, current_tier, auto_route,
84
+ )
85
+ if escalation.tier_changed:
86
+ active_model = escalation.model
87
+ current_tier = escalation.tier
88
+ await ws.send_json({
89
+ "type": "text",
90
+ "content": f"[Escalating: {escalation.tier_description} → {active_model}]\n\n",
91
+ })
92
+
93
+ for event_type, evt_data in stream_chat(
94
+ messages=messages,
95
+ tools=all_tools,
96
+ model=active_model,
97
+ api_key=config.api_key,
98
+ base_url=config.base_url,
99
+ max_tokens=config.max_tokens,
100
+ temperature=config.temperature,
101
+ ):
102
+ if event_type == "text":
103
+ text_buffer += evt_data
104
+ await ws.send_json({
105
+ "type": "text",
106
+ "content": evt_data,
107
+ })
108
+ elif event_type == "tool_calls":
109
+ pending_tool_calls, tool_reasoning = evt_data
110
+ elif event_type == "model_not_found":
111
+ await ws.send_json({
112
+ "type": "error",
113
+ "content": f"Model not found: {evt_data[:300]}",
114
+ })
115
+ break
116
+ elif event_type == "error":
117
+ await ws.send_json({
118
+ "type": "error",
119
+ "content": f"API error: {evt_data[:500]}",
120
+ })
121
+ break
122
+ elif event_type == "done":
123
+ content, reasoning = evt_data
124
+ if reasoning:
125
+ context.add_assistant_message(content=content or text_buffer, reasoning_content=reasoning)
126
+ elif content or text_buffer:
127
+ context.add_assistant_message(content=content or text_buffer)
128
+ text_buffer = ""
129
+
130
+ if pending_tool_calls:
131
+ context.add_assistant_message(
132
+ text_buffer or None,
133
+ tool_calls=pending_tool_calls,
134
+ reasoning_content=tool_reasoning or None,
135
+ )
136
+
137
+ for tc in pending_tool_calls:
138
+ name = tc["function"]["name"]
139
+ raw_args = tc["function"]["arguments"]
140
+ try:
141
+ args = json.loads(_repair_json(raw_args)) if raw_args else {}
142
+ except json.JSONDecodeError:
143
+ await ws.send_json({
144
+ "type": "tool",
145
+ "name": name,
146
+ "content": f"Invalid JSON arguments: {raw_args[:200]}",
147
+ })
148
+ context.add_tool_result(
149
+ tool_call_id=tc["id"], tool_name=name,
150
+ result=f"Error: invalid JSON in tool arguments: {raw_args[:200]}",
151
+ )
152
+ continue
153
+
154
+ await ws.send_json({
155
+ "type": "tool",
156
+ "name": name,
157
+ "content": f"Running {name}...",
158
+ })
159
+
160
+ try:
161
+ if name.startswith("mcp_"):
162
+ result = mcp.execute(name, args)
163
+ else:
164
+ result = registry.execute(name, args)
165
+ except Exception as e:
166
+ result = f"Error executing {name}: {e}"
167
+
168
+ result_preview = result[:300].replace("\n", " ")
169
+ if len(result) > 300:
170
+ result_preview += "..."
171
+
172
+ await ws.send_json({
173
+ "type": "tool_result",
174
+ "name": name,
175
+ "content": result_preview,
176
+ })
177
+
178
+ context.add_tool_result(
179
+ tool_call_id=tc["id"],
180
+ tool_name=name,
181
+ result=result,
182
+ )
183
+ tool_call_count += 1
184
+
185
+ tool_loop_count += 1
186
+ continue
187
+
188
+ if text_buffer:
189
+ context.add_assistant_message(content=text_buffer)
190
+ await ws.send_json({"type": "done"})
191
+ break
192
+
193
+ if tool_loop_count >= MAX_TOOL_LOOPS:
194
+ await ws.send_json({
195
+ "type": "error",
196
+ "content": "Max tool loop iterations reached",
197
+ })
198
+
199
+ elif msg_type == "clear":
200
+ context.reset()
201
+ md = memory_module.load_claude_md()
202
+ session_memories = state.web_memory_mgr.get_all_memories_formatted()
203
+ if md and session_memories:
204
+ merged = md + "\n\n" + session_memories
205
+ elif session_memories:
206
+ merged = session_memories
207
+ else:
208
+ merged = md or ""
209
+ if merged:
210
+ context.messages.insert(1, {
211
+ "role": "user",
212
+ "content": f"<claude-md>{merged}</claude-md>",
213
+ })
214
+ await ws.send_json({"type": "cleared"})
215
+
216
+ except asyncio.TimeoutError:
217
+ try:
218
+ await ws.send_json({"type": "error", "content": "Request timed out (300s)"})
219
+ except Exception:
220
+ logger.warning("WebSocket send_json (timeout) failed", exc_info=True)
221
+ try:
222
+ await ws.close(code=1001)
223
+ except Exception:
224
+ logger.warning("WebSocket close failed", exc_info=True)
225
+ except WebSocketDisconnect:
226
+ pass
227
+ except json.JSONDecodeError:
228
+ try:
229
+ await ws.send_json({"type": "error", "content": "Invalid JSON"})
230
+ except Exception:
231
+ logger.warning("WebSocket send_json (invalid JSON) failed", exc_info=True)
232
+ except Exception as e:
233
+ logger.warning(f"WebSocket error: {e}")
234
+ try:
235
+ await ws.send_json({"type": "error", "content": f"Server error: {str(e)[:200]}"})
236
+ except Exception:
237
+ logger.warning("WebSocket send_json (error notification) failed", exc_info=True)
@@ -0,0 +1,297 @@
1
+ Metadata-Version: 2.4
2
+ Name: luckyd-code
3
+ Version: 1.2.2
4
+ Summary: LuckyD Code — AI coding assistant powered by DeepSeek
5
+ Project-URL: Homepage, https://github.com/Dylanchess0320/LuckyD-Code
6
+ Project-URL: BugTracker, https://github.com/Dylanchess0320/LuckyD-Code/issues
7
+ Author-email: Dylan Kaye <dylanchess0320@users.noreply.github.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: ai,assistant,cli,coding,luckyd
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development
20
+ Classifier: Topic :: Software Development :: Code Generators
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: aiofiles>=23.0
23
+ Requires-Dist: beautifulsoup4>=4.12.0
24
+ Requires-Dist: fastapi>=0.100.0
25
+ Requires-Dist: httpx>=0.25.0
26
+ Requires-Dist: nest-asyncio>=1.5.0
27
+ Requires-Dist: openai>=1.0.0
28
+ Requires-Dist: prompt-toolkit>=3.0.0
29
+ Requires-Dist: pyyaml>=6.0
30
+ Requires-Dist: rich>=13.0.0
31
+ Requires-Dist: tiktoken>=0.5.0
32
+ Requires-Dist: uvicorn>=0.23.0
33
+ Requires-Dist: websockets>=11.0
34
+ Provides-Extra: browser
35
+ Requires-Dist: playwright>=1.40.0; extra == 'browser'
36
+ Provides-Extra: dev
37
+ Requires-Dist: mypy>=1.0; extra == 'dev'
38
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
39
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
40
+ Requires-Dist: pytest>=7.0; extra == 'dev'
41
+ Requires-Dist: types-aiofiles; extra == 'dev'
42
+ Requires-Dist: types-beautifulsoup4; extra == 'dev'
43
+ Requires-Dist: types-pyyaml; extra == 'dev'
44
+ Provides-Extra: rag
45
+ Requires-Dist: numpy>=1.24.0; extra == 'rag'
46
+ Requires-Dist: sentence-transformers>=2.2.0; extra == 'rag'
47
+ Provides-Extra: rag-full
48
+ Requires-Dist: faiss-cpu>=1.7.0; extra == 'rag-full'
49
+ Requires-Dist: numpy>=1.24.0; extra == 'rag-full'
50
+ Requires-Dist: sentence-transformers>=2.2.0; extra == 'rag-full'
51
+ Requires-Dist: watchdog>=3.0.0; extra == 'rag-full'
52
+ Description-Content-Type: text/markdown
53
+
54
+ # LuckyD Code
55
+
56
+ [![CI](https://github.com/Dylanchess0320/LuckyD-Code/actions/workflows/ci.yml/badge.svg)](https://github.com/Dylanchess0320/LuckyD-Code/actions/workflows/ci.yml)
57
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
58
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
59
+ [![PyPI](https://img.shields.io/pypi/v/luckyd-code)](https://pypi.org/project/luckyd-code/)
60
+
61
+ A full-featured AI coding assistant for your terminal and browser, powered by the DeepSeek API. Features 35+ built-in tools, a semantic knowledge graph, Playwright browser automation, multi-agent orchestration, Docker sandboxing, and a live Web UI.
62
+
63
+ ---
64
+
65
+ ## Features
66
+
67
+ - **AI Chat** — Conversational coding assistant with streaming responses and thinking/reasoning mode
68
+ - **Smart Model Routing** — Auto-classifies prompt complexity into 4 tiers, escalating from cheap/fast to powerful/reasoning models
69
+ - **Knowledge Graph** — Automatically indexes your codebase into a searchable vector graph; falls back to a keyword graph if vector dependencies aren't installed
70
+ - **Memory System** — Persistent memory across sessions with auto-save and relevance search
71
+ - **Cost Tracking** — Per-session and cumulative cost tracking across models
72
+ - **Web UI** — Browser-based interface with cost panel, memory management, and model routing info
73
+ - **35+ Built-in Tools** — File operations, search, code analysis, shell commands, and more
74
+ - **MCP Support** — Model Context Protocol for extending with custom tools
75
+ - **Plugin System** — Drop Python files into `~/.claude/plugins/` to register additional tools at runtime
76
+ - **Context Management** — Auto-compaction with summarization to stay within context windows
77
+ - **Background Agents** — Run tasks asynchronously while continuing to chat
78
+ - **Orchestrator** — Researcher → Coder → Reviewer pipeline for complex tasks
79
+ - **Hooks System** — Pre/post tool use, pre/post chat, and session lifecycle hooks
80
+ - **Sandboxing** — Docker-based secure command execution
81
+ - **Session Management** — Save and load conversations
82
+ - **Undo** — Revert file writes and edits
83
+ - **Export** — Export conversations to Markdown or HTML
84
+ - **Shell Detection** — Auto-detects Git Bash → WSL → cmd.exe on Windows; rewrites interactive commands so the correct shell is always used
85
+ - **Analytics** — Code health scoring, code smell detection, and trend tracking
86
+
87
+ ---
88
+
89
+ ## Prerequisites
90
+
91
+ - **Python 3.10+**
92
+ - **DeepSeek API key** — Get one free at [platform.deepseek.com/api_keys](https://platform.deepseek.com/api_keys)
93
+
94
+ ---
95
+
96
+ ## Quick Start — No setup required
97
+
98
+ **Windows** — double-click one of these:
99
+ - `Install and Run - Windows.bat` — CLI mode
100
+ - `Install and Run Web UI - Windows.bat` — browser UI
101
+
102
+ **Mac** — double-click `Install and Run - Mac.command`
103
+ *(first time: right-click → Open to bypass Gatekeeper)*
104
+
105
+ **Linux** — run in terminal:
106
+ ```bash
107
+ chmod +x "Install and Run - Linux.sh"
108
+ "./Install and Run - Linux.sh"
109
+ ```
110
+
111
+ The launcher automatically creates a virtual environment, installs dependencies, and prompts for your API key on first run.
112
+
113
+ ---
114
+
115
+ ## Installation
116
+
117
+ ### From PyPI
118
+
119
+ ```bash
120
+ pip install luckyd-code
121
+ ```
122
+
123
+ ### From source
124
+
125
+ ```bash
126
+ git clone https://github.com/Dylanchess0320/LuckyD-Code
127
+ cd luckyd-code
128
+ python -m venv .venv
129
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
130
+ pip install -e .
131
+ ```
132
+
133
+ ### Optional dependencies
134
+
135
+ ```bash
136
+ # RAG support (semantic memory search)
137
+ pip install -e ".[rag]"
138
+
139
+ # Full RAG with FAISS vector search and file watching
140
+ pip install -e ".[rag-full]"
141
+
142
+ # Browser automation (Playwright)
143
+ pip install -e ".[browser]"
144
+
145
+ # Development dependencies
146
+ pip install -e ".[dev]"
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Usage
152
+
153
+ ### CLI mode
154
+
155
+ ```bash
156
+ luckyd-code
157
+ # or
158
+ ldc
159
+ # or
160
+ python main.py
161
+ ```
162
+
163
+ ### Web UI mode
164
+
165
+ ```bash
166
+ luckyd-code --web
167
+ # or
168
+ python main.py --web
169
+ ```
170
+
171
+ Then open `http://localhost:8000` in your browser.
172
+
173
+ ### Commands
174
+
175
+ Once in the CLI, use slash commands:
176
+
177
+ | Command | Description |
178
+ |---------|-------------|
179
+ | `/help` | Show available commands |
180
+ | `/model` | Switch model or view current model |
181
+ | `/cost` | Show session cost |
182
+ | `/memory` | View or search memory |
183
+ | `/session` | Save, load, or list sessions |
184
+ | `/export` | Export conversation |
185
+ | `/undo` | Undo last file write/edit |
186
+ | `/orchestrate` | Run multi-agent pipeline |
187
+ | `/analytics` | Run code health analysis |
188
+ | `/clear` | Clear conversation |
189
+ | `/quit` | Exit |
190
+
191
+ ### Environment variables
192
+
193
+ Copy `.env.example` to `.env` and set your API key:
194
+
195
+ ```bash
196
+ cp .env.example .env
197
+ ```
198
+
199
+ Then edit `.env`:
200
+
201
+ ```
202
+ DEEPSEEK_API_KEY=sk-your-deepseek-key-here
203
+ ```
204
+
205
+ ---
206
+
207
+ ## Built-in Tools
208
+
209
+ LuckyD Code includes 35+ tools for interacting with your codebase:
210
+
211
+ - **File operations**: Read, Write, Edit, Glob, Grep, Delete, Move, Copy
212
+ - **Code analysis**: Lint, TypeCheck, Complexity, Coverage
213
+ - **Search**: Semantic search, keyword search, file search
214
+ - **Shell**: Bash execution, PowerShell, Git operations
215
+ - **Web**: Playwright browser automation, HTTP requests
216
+ - **Knowledge**: Knowledge graph query, memory search
217
+ - **Project**: Project scan, dependency analysis, code smell detection
218
+ - **Utility**: Undo, Export, Session management
219
+
220
+ ---
221
+
222
+ ## Project Structure
223
+
224
+ ```
225
+ luckyd_code/
226
+ ├── cli.py # Terminal UI and REPL
227
+ ├── cli_commands/ # Slash-command handlers
228
+ ├── web_app.py # Web UI server (FastAPI)
229
+ ├── web_routes/ # Web UI route handlers
230
+ ├── api.py # API streaming client
231
+ ├── router.py # Model routing
232
+ ├── config.py # Configuration
233
+ ├── context.py # Conversation context
234
+ ├── cost_tracker.py # Cost tracking
235
+ ├── hooks.py # Lifecycle hooks
236
+ ├── model_registry.py # Model definitions
237
+ ├── memory/ # Persistent memory
238
+ ├── brain/ # Knowledge graph & RAG
239
+ ├── tools/ # Tool registry (35+ tools)
240
+ ├── mcp/ # MCP client
241
+ ├── permissions/ # Permission system
242
+ ├── skills/ # Review & security
243
+ ├── analytics/ # Usage analytics
244
+ ├── templates/ # Web UI assets
245
+ └── background/ # Background agents
246
+ ```
247
+
248
+ ---
249
+
250
+ ## Development
251
+
252
+ ```bash
253
+ # Install in editable mode with dev dependencies
254
+ pip install -e ".[dev]"
255
+
256
+ # Run tests
257
+ pytest
258
+
259
+ # With coverage
260
+ pytest --cov=luckyd_code --cov-report=term
261
+
262
+ # Type checking
263
+ mypy luckyd_code
264
+
265
+ # Build distribution
266
+ make build
267
+ ```
268
+
269
+ ---
270
+
271
+ ## Contributing
272
+
273
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines.
274
+
275
+ 1. Fork the repository
276
+ 2. Create a feature branch from `main`
277
+ 3. Write tests for new functionality
278
+ 4. Ensure all existing tests pass
279
+ 5. Submit a pull request
280
+
281
+ ---
282
+
283
+ ## License
284
+
285
+ MIT License — see [LICENSE](LICENSE) for details.
286
+
287
+ ---
288
+
289
+ ## Security
290
+
291
+ See [SECURITY.md](SECURITY.md) for supported versions and how to report vulnerabilities.
292
+
293
+ ---
294
+
295
+ ## Changelog
296
+
297
+ See [CHANGELOG.md](CHANGELOG.md) for a history of changes.
@@ -0,0 +1,127 @@
1
+ luckyd_code/__init__.py,sha256=B-nEQd8Kz69iTMyE8PeBCQQb8zmGrNdvUY6M71PBbd0,1871
2
+ luckyd_code/__main__.py,sha256=29wc0D-c-lzxpWvwbIeyoPH8EfW2LwOB1Be9i4OjfKg,102
3
+ luckyd_code/_agent_loop.py,sha256=ntcDQE4-lgCmhEG9DQT52wL8JmaAxSH6_JztzG4DCUo,21720
4
+ luckyd_code/_data_dir.py,sha256=BmbTO0W_t-KX0Ug7E0MOuW4lB8NZ9wZ7ZAwHLrllxYM,2232
5
+ luckyd_code/agent.py,sha256=5RVqtwBeZG-nmSl2YwuIujS_ZXfsBOiH-CjIZ2i-48c,1162
6
+ luckyd_code/api.py,sha256=FlfqyimaptY-CBK0lkcnmhpUJR18u6mX8wuMBuj7C6M,16536
7
+ luckyd_code/audit_daemon.py,sha256=JbWgNqN8cKjEsYTHITG1Xgojpw9I6Wnb9TpR8p6F7CM,33619
8
+ luckyd_code/autonomous_fixer.py,sha256=wDp3cVHdtVJZelKwaHr0521oXCJdut1QnMhtUQY1X3U,15235
9
+ luckyd_code/background.py,sha256=2ZPGGmqREuPY8eRYo1ynOywvDNEaI4xPfhRgMMqArm0,5468
10
+ luckyd_code/backup.py,sha256=xmLGOoZzpRI3cD5xMPuUYSRZGtfwXFRM6dHN_PkaXLQ,8168
11
+ luckyd_code/cli.py,sha256=7jFfaA1SmQZvma4VgW9WajVZxqc8IEwqKwqWMpyb8n4,37562
12
+ luckyd_code/cli_entry.py,sha256=HF6J09sOoWvv-653uaz3br4VTlWJZ66fXsdxuYwC0Po,5433
13
+ luckyd_code/cli_utils.py,sha256=EyZosgbjt1hFRNWkRPOZ6FbpVQwM82kJYqSItJ38Ql0,3421
14
+ luckyd_code/config.py,sha256=UWEykbBpN9Ikp1CzQlgNv0PSgdhedebZG8N_oawPGLs,8852
15
+ luckyd_code/context.py,sha256=-wLQmDOBAHoLkHDo8qtGVS1av9IUQhyXiz811K-a9ps,8870
16
+ luckyd_code/cost_tracker.py,sha256=SwM2LKc6bq11OsEfPPS6rpH9rICE0388jWT5NioQYoY,8461
17
+ luckyd_code/error_reporter.py,sha256=u4Gw5yEckybOpPhiRVDJeGrGWqOuXVGxlXzA0-j8yOU,17102
18
+ luckyd_code/exceptions.py,sha256=PT8rQZF8HWiOQtUDb0cFYRG1XYnOxW8zt9Fclg_668Q,1117
19
+ luckyd_code/export.py,sha256=Jm4y13mFL7QzymLTuWdC7V4fA3daTIeq2Wt0emXNmSg,4830
20
+ luckyd_code/feedback_analyzer.py,sha256=1fLa41guebw5BcVLfn5FtItG726p9ctFWOIu2qtjQfQ,9770
21
+ luckyd_code/file_watcher.py,sha256=4jV8X3uejgyE00fWxUG-aPqWRmI9RRwlJFteT8eBJVs,8973
22
+ luckyd_code/hooks.py,sha256=zeQKOv6X_AGFLRoMBi0JhAdNRk59eIKHNXV_YLjIvDE,8293
23
+ luckyd_code/indexer.py,sha256=WoTXLJbp9PmH5Na-_fXzjZb9cqrzwoLjyCHtTNPYWm0,9484
24
+ luckyd_code/init.py,sha256=MEAlISSCtbiS5AiThxg8iWHZuJZ8oeYknYXgsKZhmkE,939
25
+ luckyd_code/keybindings.py,sha256=Z51Pw8i_NNf0tODlCo_gL4ndVEg563WhaikAtYrYZwo,2123
26
+ luckyd_code/log.py,sha256=Twb5cKdlWLzKFS_JWDzI53L-Q-JxGeReYB9WkgF9AbA,1666
27
+ luckyd_code/model_registry.py,sha256=zFm0LeKpbtYMO5Qca3NTZlBxpnowPLlgI9UTTz1Jcrw,4461
28
+ luckyd_code/orchestrator.py,sha256=HVGSwwxh5U39RC-l96UfCYA0Vzb3Zw6jcdvNVWC3KFE,8703
29
+ luckyd_code/planner.py,sha256=MYSuWcOgWLBQmkXEhQJ72pJMbLkTmfYwrHuUjRHLQ5s,12324
30
+ luckyd_code/plugins.py,sha256=4VEOZBLxNxiwCfLBonMP3z-fwQIGzEOV7xiGasup57E,2730
31
+ luckyd_code/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
+ luckyd_code/retry.py,sha256=EHMi9dayc3NhrQ-i1qfDmxg8BCv1ugLgwKqrmzrc6JE,2161
33
+ luckyd_code/router.py,sha256=El91u1rn1NcfKs5H3DLm3HVjkRNgxGnMDvr76FdKjKA,15140
34
+ luckyd_code/sandbox.py,sha256=AYxbssqNunKQMVPLYUjCYqZtlaSVdmDCs93hBXkV0Xg,5228
35
+ luckyd_code/self_critique.py,sha256=YxzbhoOVDeazGtRbjrj5GiUVVBBnF8WpSHr1GSbaNrQ,162
36
+ luckyd_code/self_improve.py,sha256=GvdaW8mV6556V8TLq99sBw7Ii9XMgwy4n2Pn7oSoXF8,12073
37
+ luckyd_code/sessions.py,sha256=81IPE0EzIky23lOEzvs5Af6osNbeRceeeESvQ40Pn7k,3582
38
+ luckyd_code/settings.py,sha256=chGcTr_5sxgcSe1SoZUJJ0iLiNqEuiacAQmBxeT8gKI,2165
39
+ luckyd_code/themes.py,sha256=fEJme4SlIk43ccjojmu445fkR3C7BoVXoZRNJuQ49YU,1035
40
+ luckyd_code/tools_bridge.py,sha256=p7PVAIGlb89I9gnPyYZqrJdnk4mcEn5fPoWQmjtPz14,5209
41
+ luckyd_code/undo.py,sha256=HlbuRwbyBESXXT1PI-6YjQVHiGWtBCc1P-GG454SHpE,3390
42
+ luckyd_code/update.py,sha256=ANXKdUYi6n4-26as2ftHSqQ5eCUUE-iqK1PwAopN3aE,1992
43
+ luckyd_code/verify.py,sha256=8YBhmDFs59LZiu2m1AswjxxIj3u4pCSu5tGDoVxOYbQ,14047
44
+ luckyd_code/web_app.py,sha256=mDy_Ypv3BSqDbAksPwkxZVBB0tJEbjqsICgdGkQOe18,5999
45
+ luckyd_code/analytics/__init__.py,sha256=F88oArnOex1os8l3HwuLBqL5RwN6Nz-rM7Y7gfkWQHM,510
46
+ luckyd_code/analytics/reporter.py,sha256=MBQ_4D-zfeaJtPHiAhvRuylEWvaquBD8xom9QA_pAGs,6797
47
+ luckyd_code/analytics/scanner.py,sha256=CRQOYqfpYfRtSgupgJobwTJwmDHpUaDrCzA3z5xmvJk,15075
48
+ luckyd_code/analytics/smells.py,sha256=P5svsjbo4NFRHNDlHU9vovvZN9cbLqldSj7uhcZKxjs,12873
49
+ luckyd_code/analytics/trends.py,sha256=7O-gyO3Dk8nxOoE2GU3pgPEDKuw7NFqtZG8Hj6EHonY,10495
50
+ luckyd_code/brain/__init__.py,sha256=iBxmwXJ_ZBA7sDDRPKWza9ZkK9YP5YldDaj5MpBfOF0,2709
51
+ luckyd_code/brain/assembler.py,sha256=U9aurkm26ExQltt8l7O7tbhA2F9QggfxHqNfiqGWmlg,3168
52
+ luckyd_code/brain/chunker.py,sha256=--pii3pfTpMSud_P5oa6jLCwd-rleo22FwJ90qiTNg8,11884
53
+ luckyd_code/brain/constants.py,sha256=slT0goqohwfeOLf_LWmXF8CLE_I7pmFiMWi6thLNEE8,1766
54
+ luckyd_code/brain/embedder.py,sha256=85_ij8shpSKZqxw8HanrvUevaobl_8c6bzeWfch_dms,5222
55
+ luckyd_code/brain/graph.py,sha256=L4iQWszdf1KHAunKAnvFneeh7UoxE2e5GUz34h3EAjY,11743
56
+ luckyd_code/brain/indexer.py,sha256=-oLJBF_hugbyU38uzsDLcBvv4du3BHDfTyS3oZfOeKQ,10086
57
+ luckyd_code/brain/parser.py,sha256=xLTJ2tJkQD4NQ0JkArfZ60OS5ZM8xw_SczfVySMx-SU,4910
58
+ luckyd_code/brain/retriever.py,sha256=oKuY2xBSssWcBG5e-Tf6JRPmRsjozxUI_8ROKGg7c-w,8107
59
+ luckyd_code/cli_commands/__init__.py,sha256=hc6STRHVU3MaSFVFmWgfROsA8-U9Lm-HqFjchBQgkX4,34
60
+ luckyd_code/cli_commands/audit.py,sha256=on9Fd3vqq3sxv2AFsLaFkqOfQKG_n4tmAm3RmGWHMaU,4094
61
+ luckyd_code/cli_commands/background.py,sha256=ltm_5elpJw-WP74n-pB03JckFLkld7hLPTilBi25LyA,3044
62
+ luckyd_code/cli_commands/brain.py,sha256=geJeXpK3D7RqoJkeLDfGVDqg16m9vWhxV6GcFNFcIrc,3284
63
+ luckyd_code/cli_commands/config.py,sha256=zP1WFtjxfgNDlOl_HxpALybEU78aN1zirBZeBVZ8LUY,2838
64
+ luckyd_code/cli_commands/dispatcher.py,sha256=pKl-z4OkF-GwYYwCTBSt3Htf1aFALQCIm0wiVzgzFG8,31289
65
+ luckyd_code/cli_commands/sessions.py,sha256=raeW6CRhd3FvtV10yNXA2Jri_Qbq-5EJbwooqpxOElY,1318
66
+ luckyd_code/git/__init__.py,sha256=OkkXSmDeNttwQYsDfgpjIc6wtiAWIL33n_u6lXMd0qM,192
67
+ luckyd_code/git/auto_commit.py,sha256=6WCLUMiebJFO-T-7oLG0qHYY0kgDrcM0dEIEwZVl8t0,5042
68
+ luckyd_code/git/tools.py,sha256=9_8AZOtC7JztJwVvkinej_6U1RftHHCzEzdonk80Eh0,2556
69
+ luckyd_code/mcp/__init__.py,sha256=P0S1s_UMBRg3sZELyF_U5KKc2vr6hGDFZ5JFDIKfA-U,151
70
+ luckyd_code/mcp/client.py,sha256=2Bt6heIL7tw3JCmqQ8nFOr2vHwiZ4KGDC1qtKwLmdSE,6928
71
+ luckyd_code/memory/__init__.py,sha256=JUWxyeOJQDvqqcZhUSWR7xrO8LTlj0Zj86iFmuasdvU,346
72
+ luckyd_code/memory/manager.py,sha256=LO7QahygLGoV-i_GVYsud70uy4rl1WPtRNNjVUKxOXk,13313
73
+ luckyd_code/metrics/__init__.py,sha256=PZzlmGn6QHAyn5CsflfO1rsS-8Fr4FA1-CCVpgYKbpo,180
74
+ luckyd_code/permissions/__init__.py,sha256=3LCcI5CFhuWIRTF7g1Ke2AOpnA_2Ovew5j7D4TAsV4s,50
75
+ luckyd_code/permissions/manager.py,sha256=ZwvDBRB9fWB9Qk8DuAqHAy92UGLin45dMYFp5--AYrA,3011
76
+ luckyd_code/skills/__init__.py,sha256=BjMn7aqZQJ6RlT_ZGUiPeSbgWI6EdJQmucklB85VCtc,137
77
+ luckyd_code/skills/review.py,sha256=emgq4ZRi-3vi9Ul7r51lDhD97tos6Bpnq_C0Xgn3DjA,646
78
+ luckyd_code/skills/security.py,sha256=EHHu9XFmygcU4BM4rrRo0LKor2cPbTPcdPPldad5K9Q,482
79
+ luckyd_code/tasks/__init__.py,sha256=3mQrNW09kzV0kIxWKIr6TjZKYPnQQu0wxSdUXGCXmUU,68
80
+ luckyd_code/tasks/manager.py,sha256=Zq5lAx-cd2UdT63vCRNiH_bfVKBEgVO1BmA2QCvAZBw,3022
81
+ luckyd_code/templates/icon-192.png,sha256=w5ly8dInHA0ZOUqeIQxggLTRAeYUwfj1U_D5_cVR3Aw,545
82
+ luckyd_code/templates/icon-512.png,sha256=hPWk5h7uL_lN5um6MxUqQ3zczv0YJQc4Z7mgGL42SLU,1879
83
+ luckyd_code/templates/index.html,sha256=IiS1N_tm6m6ZckSNNpbRFhnbAuDPSxLn9B1wyDSby4c,56633
84
+ luckyd_code/templates/manifest.json,sha256=5f7DpJDMfro6S9Ara4WnmGHmMoOJ1NPPMoMHNahODqg,404
85
+ luckyd_code/templates/sw.js,sha256=mVouoTNHStf2y_BqRy1bXZjnMB1w8blSW337n8HkHxE,743
86
+ luckyd_code/templates/test.html,sha256=L0FV3KYZVHcxlAP7a_0ZDo2MMD1Wg_nJsSfYc51ybvo,59
87
+ luckyd_code/templates/src/app.js,sha256=gu9MaGPkiI-GSMLI6PdKxCfWVkXttny2iT39APMUBxo,20595
88
+ luckyd_code/templates/src/body.html,sha256=5VvS1ifVqTPSnuYld8nsWkGvlxsQYz6q6Evc8g8Tz-g,23528
89
+ luckyd_code/templates/src/cdn.txt,sha256=EpxH2jnBR-CjogOYUoAykqMpFTDQgcP4UThrfhBtVYA,190
90
+ luckyd_code/templates/src/style.css,sha256=iMMEvg9lZDbBZoln3V-lB0cJx9st8BK8_M9lTsgkC0c,11279
91
+ luckyd_code/tools/__init__.py,sha256=OHsnqSuFdYXMVjqC67H5_ElEYccL5QHgec67tWmfCFc,3553
92
+ luckyd_code/tools/agent_tools.py,sha256=mOrqfNZ95G8ImdJ1VZHCv5A_b84v4Hs60aXtZ0st99E,2124
93
+ luckyd_code/tools/bash.py,sha256=j3FI9KVjPZLJ7YUse5o54gTqcyOXPP0lty28k48cHxU,12534
94
+ luckyd_code/tools/brain_tools.py,sha256=3KUJ_XHXEtpLutJXd0Eos5R7Z5MI4nqtFiSR0EBZUFU,5122
95
+ luckyd_code/tools/browser.py,sha256=11uN6VanMM2JJ9iBjocJC2uj-YXAr2aGUhLXiwaH51g,12546
96
+ luckyd_code/tools/datetime_tool.py,sha256=GwLezvuSw9v6-UQznbRsCTuWJZZgHwWocZskc7fOLOo,1062
97
+ luckyd_code/tools/dockerfile_gen.py,sha256=sTh6HLx8n35tfGifwIt2DP-WdfC3rEI4MTfUqZPA8lg,7461
98
+ luckyd_code/tools/file_ops.py,sha256=drIhUqa5dh7CfLdHQ-5wyBh65mTO9D_MJ_AwfXdAEkM,13604
99
+ luckyd_code/tools/game_gen.py,sha256=bHqJHZ2-0hKAyVrQrCwDvIx8NMUV88RIoJF9UjLHy5c,13931
100
+ luckyd_code/tools/git_tools.py,sha256=EcDgZqGBNz5bVLWpbqGrGVmV0tdAfVgQpgKJP5dtdlg,3558
101
+ luckyd_code/tools/git_worktree.py,sha256=7rwU13AXe-t56m6BCQNMVuUT9Le21ZFhlHmoPywO_Ts,2191
102
+ luckyd_code/tools/path_validate.py,sha256=qriGBn5tKAGtfV_ky26Evmk7uSgAH2zIljRtXqUN_iI,1988
103
+ luckyd_code/tools/project_gen.py,sha256=rOMfCB3M5BIbcBQ-WlsAQP5DFnjwo-qmrsvbi04CCOM,7024
104
+ luckyd_code/tools/readme_gen.py,sha256=TeIbHujuTDoILoIu_R8HbtBVs0bDmOzEaBvCs5CfVng,8061
105
+ luckyd_code/tools/registry.py,sha256=NLua9vzfZ4QK1Ek8GNN-C4OKMaOWecbv3jatZb46cZ8,5267
106
+ luckyd_code/tools/shell_detect.py,sha256=TnWLYSQLm_QjIxwF1C5Z56-grDHGOzhYqr929tqofvU,3168
107
+ luckyd_code/tools/web.py,sha256=Ax6iyTvn04ARJqb5w41nw4EwAIFfzEIrwRrhuRf_HqY,3201
108
+ luckyd_code/tools/youtube.py,sha256=tcru64GaDbm4qF_8-Vecivclp61qSbKioYSPO-JHmB0,6762
109
+ luckyd_code/web_routes/__init__.py,sha256=_9cJdsjcrR4aZsT39gDXdNXK08lhgNmEuw3fBmCmq3I,721
110
+ luckyd_code/web_routes/background.py,sha256=QxlYNa7w-eLFzp6D5q5e0raQ1Od8FBpk_EPDTSIAQTw,2420
111
+ luckyd_code/web_routes/brain.py,sha256=kM5I7cowVGnOUytc3HIoHXsf_CvZIEGj2P8pwqDjr6k,3323
112
+ luckyd_code/web_routes/cost.py,sha256=DoyCosb8GYSQLTBv0ODTXeTPVTf7Dunt8uY0w0LDoJY,256
113
+ luckyd_code/web_routes/files.py,sha256=gqs0yQHJOJf3PJpbKbbJUGY_P3G0X3UYtRHz5B35Dkc,5136
114
+ luckyd_code/web_routes/memories.py,sha256=hv6LecGM75bZTat-v27Vie-CZgolapUeuvGZS7BlMSE,3045
115
+ luckyd_code/web_routes/misc.py,sha256=38la5tejKtyHBzXMqAPCZEC2wbKg6mKx5QsZTL4zaug,2092
116
+ luckyd_code/web_routes/project.py,sha256=c9GgRZSAuJFiSsEPmcpd24uZhVM7l6SCPAZCPpTiyVs,1515
117
+ luckyd_code/web_routes/review.py,sha256=_q-f4BiRsuba-v03uf3rhLksNa8ORoaRUAFlh4ashOs,462
118
+ luckyd_code/web_routes/sessions.py,sha256=VdrS13q91x6UVYC4ugRCmcYLuXtFSlBdSaG1uBvsJ2I,1136
119
+ luckyd_code/web_routes/settings.py,sha256=XYb9QfwI1l96p8Piz5E4zx0YvP10k37SzsueHQFHNMs,982
120
+ luckyd_code/web_routes/static.py,sha256=ZiMP0XxB0ifKu6WBsMsdABD38d0SMj_b9Betom2V99w,1982
121
+ luckyd_code/web_routes/update.py,sha256=596InmLi0fKCpF46bnAIZg5LuUMZIGprKQJH203h4-0,418
122
+ luckyd_code/web_routes/ws.py,sha256=8m44b4fFCUKCjrYIykgOPuGlJ914vNhGrf4GGNM7xWo,9955
123
+ luckyd_code-1.2.2.dist-info/METADATA,sha256=G1ZB4FQ0m8l9vKUimzLSNvqk0MErLADdHHwxakgaUsk,9184
124
+ luckyd_code-1.2.2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
125
+ luckyd_code-1.2.2.dist-info/entry_points.txt,sha256=yKIYwVTUMAQkkypS4MVpX9rJqRP3hmQ7214pKLop-n0,92
126
+ luckyd_code-1.2.2.dist-info/licenses/LICENSE,sha256=BCKpWQ4QZD0PD1q2iWThm1JbS6Pk-irbN48eSO-tcfE,1084
127
+ luckyd_code-1.2.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ ldc = luckyd_code.cli_entry:main
3
+ luckyd-code = luckyd_code.cli_entry:main