@scemoon/cdh 1.0.0 → 1.0.4
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.
- package/cdh/__pycache__/cli.cpython-314.pyc +0 -0
- package/cdh/__pycache__/scaffold.cpython-314.pyc +0 -0
- package/cdh/cli.py +473 -87
- package/cdh/scaffold.py +386 -121
- package/onecode/__pycache__/cli.cpython-314.pyc +0 -0
- package/onecode/__pycache__/commands.cpython-314.pyc +0 -0
- package/onecode/__pycache__/config.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/cdh_loader.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/engine.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/onecode_agent_acp.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/session.cpython-314.pyc +0 -0
- package/onecode/agent/agents/__pycache__/types.cpython-314.pyc +0 -0
- package/onecode/agent/agents/types.py +123 -79
- package/onecode/agent/cdh_loader.py +26 -13
- package/onecode/agent/engine.py +426 -235
- package/onecode/agent/onecode_agent_acp.py +202 -75
- package/onecode/agent/session.py +8 -13
- package/onecode/agent/tools/__pycache__/agent_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/todo_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/agent_tools.py +4 -4
- package/onecode/agent/tools/todo_tools.py +188 -0
- package/onecode/cli.py +3 -3
- package/onecode/commands.py +439 -194
- package/onecode/config.py +3 -3
- package/onecode/config_screen.py +4 -2
- package/onecode/models/__pycache__/errors.cpython-314.pyc +0 -0
- package/onecode/models/__pycache__/messages.cpython-314.pyc +0 -0
- package/onecode/models/__pycache__/provider.cpython-314.pyc +0 -0
- package/onecode/models/errors.py +11 -1
- package/onecode/models/messages.py +35 -16
- package/onecode/models/provider.py +243 -12
- package/onecode/models/providers/__pycache__/anthropic_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/deepseek_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/glm_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/minimax_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/minimaxi_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/ollama_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/openai_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/anthropic_provider.py +4 -2
- package/onecode/models/providers/deepseek_provider.py +2 -0
- package/onecode/models/providers/glm_provider.py +2 -1
- package/onecode/models/providers/minimax_provider.py +2 -1
- package/onecode/models/providers/minimaxi_provider.py +76 -25
- package/onecode/models/providers/ollama_provider.py +2 -1
- package/onecode/models/providers/openai_provider.py +4 -0
- package/onecode/tasks/manager.py +1 -1
- package/package.json +1 -1
- package/pyproject.toml +10 -0
- package/run.js +24 -7
- package/tui/__pycache__/agents.cpython-314.pyc +0 -0
- package/tui/__pycache__/app.cpython-314.pyc +0 -0
- package/tui/__pycache__/db.cpython-314.pyc +0 -0
- package/tui/__pycache__/directory_watcher.cpython-314.pyc +0 -0
- package/tui/__pycache__/messages.cpython-314.pyc +0 -0
- package/tui/__pycache__/paths.cpython-314.pyc +0 -0
- package/tui/__pycache__/settings_schema.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/agent.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/api.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/messages.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/prompt.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/protocol.cpython-314.pyc +0 -0
- package/tui/acp/agent.py +60 -11
- package/tui/acp/api.py +6 -0
- package/tui/acp/messages.py +11 -0
- package/tui/acp/prompt.py +1 -1
- package/tui/acp/protocol.py +31 -0
- package/tui/agents.py +8 -7
- package/tui/app.py +60 -8
- package/tui/data/agents/ampcode.com.toml +1 -0
- package/tui/data/agents/augmentcode.com.toml +1 -0
- package/tui/data/agents/cdh.cloud-dev-harness.toml +3 -2
- package/tui/data/agents/crowai.dev.toml +1 -0
- package/tui/data/agents/deepagents.langchain.com +1 -0
- package/tui/data/agents/docker.com.toml +1 -0
- package/tui/data/agents/goose.ai.toml +1 -0
- package/tui/data/agents/inference.huggingface.co.toml +1 -1
- package/tui/data/agents/kiro.dev.toml +1 -0
- package/tui/data/agents/onecode.dev.toml +22 -0
- package/tui/data/agents/openclaw.ai.toml +1 -1
- package/tui/data/agents/openhands.dev.toml +1 -0
- package/tui/data/agents/stakpak.dev.toml +1 -0
- package/tui/data/agents/vibe.mistral.ai.toml +1 -0
- package/tui/data/agents/vtcode.dev.toml +1 -0
- package/tui/db.py +23 -0
- package/tui/directory_watcher.py +35 -1
- package/tui/messages.py +4 -1
- package/tui/paths.py +3 -3
- package/tui/screens/__pycache__/main.cpython-314.pyc +0 -0
- package/tui/screens/component_picker.py +148 -0
- package/tui/screens/main.py +47 -95
- package/tui/screens/projects.tcss +10 -0
- package/tui/screens/projects_app.py +33 -5
- package/tui/screens/projects_screen.py +171 -9
- package/tui/screens/store.py +1 -1
- package/tui/settings_schema.py +1 -1
- package/tui/widgets/__pycache__/ask_user.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/conversation.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/modified_files.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/plan.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/project_directory_tree.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/question.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/subagent.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/tool_call.cpython-314.pyc +0 -0
- package/tui/widgets/ask_user.py +1 -1
- package/tui/widgets/conversation.py +99 -42
- package/tui/widgets/modified_files.py +191 -65
- package/tui/widgets/plan.py +22 -0
- package/tui/widgets/project_directory_tree.py +22 -10
- package/tui/widgets/project_grid_select.py +23 -1
- package/tui/widgets/question.py +1 -1
- package/tui/widgets/subagent.py +202 -36
- package/tui/widgets/subagent_screen.py +186 -0
- package/tui/widgets/tool_call.py +5 -8
- package/cdh/__pycache__/__init__.cpython-313.pyc +0 -0
- package/cdh/__pycache__/scaffold.cpython-313.pyc +0 -0
- package/onecode/__pycache__/config_screen.cpython-314.pyc +0 -0
- package/onecode/__pycache__/mock_data.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/cdh_agent_acp.cpython-313.pyc +0 -0
- package/onecode/agent/__pycache__/cdh_agent_acp.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/harness_skill.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/task_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/task_tools.py +0 -249
- package/onecode/codebase/__pycache__/config.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/mock_provider.cpython-314.pyc +0 -0
- package/onecode/skills/__pycache__/manager.cpython-314.pyc +0 -0
- package/tui/__pycache__/danger.cpython-314.pyc +0 -0
- package/tui/__pycache__/directory_suggester.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/log.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/projects_screen.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/sessions.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/settings.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/store.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/acp_content.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/conversation.cpython-313.pyc +0 -0
- package/tui/widgets/__pycache__/diff_view.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/directory_input.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/future_text.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/grid_select.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/mandelbrot.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/markdown_note.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/project_grid_select.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/project_summary.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/session_grid_select.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/session_summary.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/shell_result.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/terminal.cpython-313.pyc +0 -0
- package/tui/widgets/__pycache__/tool_call.cpython-313.pyc +0 -0
package/tui/screens/main.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
from functools import partial
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
import random
|
|
@@ -8,10 +9,10 @@ from textual import getters
|
|
|
8
9
|
from textual.binding import Binding
|
|
9
10
|
from textual.command import Hit, Hits, Provider, DiscoveryHit
|
|
10
11
|
from textual.content import Content
|
|
11
|
-
from textual.events import ScreenResume
|
|
12
|
+
from textual.events import ScreenResume
|
|
12
13
|
from textual.screen import Screen
|
|
13
14
|
from textual.reactive import var, reactive
|
|
14
|
-
from textual.widgets import Footer, OptionList, DirectoryTree, Tree
|
|
15
|
+
from textual.widgets import Footer, OptionList, DirectoryTree, Tree
|
|
15
16
|
from textual import containers
|
|
16
17
|
from textual.widget import Widget
|
|
17
18
|
|
|
@@ -29,7 +30,6 @@ from tui.widgets.conversation import Conversation
|
|
|
29
30
|
from tui.widgets.project_directory_tree import ProjectDirectoryTree
|
|
30
31
|
from tui.widgets.side_bar import SideBar
|
|
31
32
|
from tui.widgets.session_tabs import SessionsTabs, SessionLabel
|
|
32
|
-
from onecode.agent.cdh_loader import CdhProjectLoader
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
class ModeProvider(Provider):
|
|
@@ -102,7 +102,7 @@ class MainScreen(Screen, can_focus=False):
|
|
|
102
102
|
column = reactive(False)
|
|
103
103
|
column_width = reactive(80)
|
|
104
104
|
scrollbar = reactive("")
|
|
105
|
-
project_path: var[Path] = var(Path("./").expanduser().absolute())
|
|
105
|
+
project_path: var[Path | None] = var(Path("./").expanduser().absolute())
|
|
106
106
|
|
|
107
107
|
app = getters.app(A2TUIApp)
|
|
108
108
|
|
|
@@ -116,6 +116,7 @@ class MainScreen(Screen, can_focus=False):
|
|
|
116
116
|
initial_prompt: str | None = None,
|
|
117
117
|
) -> None:
|
|
118
118
|
super().__init__()
|
|
119
|
+
self._project_update_lock = asyncio.Lock()
|
|
119
120
|
self.set_reactive(MainScreen.project_path, project_path)
|
|
120
121
|
self._agent = agent
|
|
121
122
|
self._agent_session_id = agent_session_id
|
|
@@ -154,30 +155,17 @@ class MainScreen(Screen, can_focus=False):
|
|
|
154
155
|
id="context-panel",
|
|
155
156
|
),
|
|
156
157
|
]
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
),
|
|
169
|
-
)
|
|
170
|
-
else:
|
|
171
|
-
panels.append(
|
|
172
|
-
SideBar.Panel(
|
|
173
|
-
"No Project",
|
|
174
|
-
Static(
|
|
175
|
-
"[bold]No .cdh project[/]\n\nClick to initialize .cdh in this directory",
|
|
176
|
-
id="init-cdh-hint",
|
|
177
|
-
),
|
|
178
|
-
id="no-project-panel",
|
|
179
|
-
),
|
|
180
|
-
)
|
|
158
|
+
# panels.append(
|
|
159
|
+
# SideBar.Panel(
|
|
160
|
+
# "Project",
|
|
161
|
+
# ProjectDirectoryTree(
|
|
162
|
+
# self.project_path,
|
|
163
|
+
# id="project_directory_tree",
|
|
164
|
+
# ),
|
|
165
|
+
# flex=True,
|
|
166
|
+
# id="project-panel",
|
|
167
|
+
# ),
|
|
168
|
+
# )
|
|
181
169
|
panels.append(
|
|
182
170
|
SideBar.Panel(
|
|
183
171
|
"Modified Files",
|
|
@@ -217,12 +205,22 @@ class MainScreen(Screen, can_focus=False):
|
|
|
217
205
|
self.post_message(messages.SessionNavigate(self.screen.id, +1))
|
|
218
206
|
|
|
219
207
|
@on(messages.ProjectDirectoryUpdated)
|
|
220
|
-
async def on_project_directory_update(self) -> None:
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
208
|
+
async def on_project_directory_update(self, event: messages.ProjectDirectoryUpdated) -> None:
|
|
209
|
+
async with self._project_update_lock:
|
|
210
|
+
new_dir = event.project_dir
|
|
211
|
+
sidebar = self.query_one(SideBar)
|
|
212
|
+
if new_dir is None:
|
|
213
|
+
sidebar.remove_panel("project-panel")
|
|
214
|
+
self.project_path = None
|
|
215
|
+
if mf := sidebar.query_one_optional("#modified_files", ModifiedFiles):
|
|
216
|
+
mf.refresh_files()
|
|
217
|
+
self._swap_directory_watcher(None)
|
|
218
|
+
return
|
|
219
|
+
if self.project_path == new_dir and sidebar.has_panel("project-panel"):
|
|
220
|
+
if mf := sidebar.query_one_optional("#modified_files", ModifiedFiles):
|
|
221
|
+
mf.refresh_files()
|
|
222
|
+
return
|
|
223
|
+
self.project_path = new_dir
|
|
226
224
|
if not sidebar.has_panel("project-panel"):
|
|
227
225
|
collapsible = await sidebar.add_panel(
|
|
228
226
|
SideBar.Panel(
|
|
@@ -238,62 +236,23 @@ class MainScreen(Screen, can_focus=False):
|
|
|
238
236
|
tree = collapsible.query_one(ProjectDirectoryTree)
|
|
239
237
|
tree.data_bind(path=MainScreen.project_path)
|
|
240
238
|
tree.guide_depth = 3
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
else:
|
|
246
|
-
sidebar.remove_panel("project-panel")
|
|
247
|
-
if not sidebar.has_panel("no-project-panel"):
|
|
248
|
-
await sidebar.add_panel(
|
|
249
|
-
SideBar.Panel(
|
|
250
|
-
"No Project",
|
|
251
|
-
Static(
|
|
252
|
-
"[bold]No .cdh project[/]\n\nClick to initialize .cdh in this directory",
|
|
253
|
-
id="init-cdh-hint",
|
|
254
|
-
),
|
|
255
|
-
id="no-project-panel",
|
|
256
|
-
),
|
|
257
|
-
)
|
|
258
|
-
if mf := sidebar.query_one_optional("#modified_files", ModifiedFiles):
|
|
259
|
-
mf.refresh_files()
|
|
260
|
-
|
|
261
|
-
@on(Click, "#init-cdh-hint")
|
|
262
|
-
def on_init_cdh_click(self) -> None:
|
|
263
|
-
from pathlib import Path
|
|
264
|
-
from onecode.config_screen import EditFieldScreen
|
|
265
|
-
from onecode.agent.cdh_loader import CdhProjectLoader
|
|
266
|
-
|
|
267
|
-
default_path = str(self.project_path.resolve())
|
|
268
|
-
self.app.push_screen(
|
|
269
|
-
EditFieldScreen("Directory to initialize .cdh", default_path),
|
|
270
|
-
self._on_init_cdh_path,
|
|
271
|
-
)
|
|
239
|
+
|
|
240
|
+
if mf := sidebar.query_one_optional("#modified_files", ModifiedFiles):
|
|
241
|
+
mf.refresh_files()
|
|
242
|
+
self._swap_directory_watcher(new_dir)
|
|
272
243
|
|
|
273
|
-
def
|
|
274
|
-
|
|
275
|
-
|
|
244
|
+
def _swap_directory_watcher(self, project_path: Path | None) -> None:
|
|
245
|
+
try:
|
|
246
|
+
self.conversation.directory_watcher_swap(project_path)
|
|
247
|
+
except Exception:
|
|
248
|
+
pass
|
|
276
249
|
|
|
277
|
-
|
|
278
|
-
return
|
|
250
|
+
def _has_project_panel(self) -> bool:
|
|
279
251
|
try:
|
|
280
|
-
|
|
281
|
-
if not target.is_dir():
|
|
282
|
-
self.notify(f"Not a directory: {target}", severity="error")
|
|
283
|
-
return
|
|
252
|
+
sidebar = self.query_one(SideBar)
|
|
284
253
|
except Exception:
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
existing = CdhProjectLoader.find_cdh_dir(target)
|
|
288
|
-
if existing is not None:
|
|
289
|
-
self.notify(f".cdh already exists at {existing}", severity="warning")
|
|
290
|
-
return
|
|
291
|
-
name = target.name
|
|
292
|
-
from cdh.scaffold import scaffold_dlc_project
|
|
293
|
-
scaffold_dlc_project(target, name)
|
|
294
|
-
CdhProjectLoader.init_project(target, name)
|
|
295
|
-
self.notify(f"Initialized .cdh in {target}")
|
|
296
|
-
self.post_message(messages.ProjectDirectoryUpdated())
|
|
254
|
+
return False
|
|
255
|
+
return sidebar.has_panel("project-panel")
|
|
297
256
|
|
|
298
257
|
@on(DirectoryTree.FileSelected, "ProjectDirectoryTree")
|
|
299
258
|
def on_project_directory_tree_selected(self, event: Tree.NodeSelected):
|
|
@@ -303,15 +262,8 @@ class MainScreen(Screen, can_focus=False):
|
|
|
303
262
|
@on(acp_messages.Plan)
|
|
304
263
|
async def on_acp_plan(self, message: acp_messages.Plan):
|
|
305
264
|
message.stop()
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
Content(entry["content"]),
|
|
309
|
-
entry.get("priority", "medium"),
|
|
310
|
-
entry.get("status", "pending"),
|
|
311
|
-
)
|
|
312
|
-
for entry in message.entries
|
|
313
|
-
]
|
|
314
|
-
self.query_one("SideBar Plan", Plan).entries = entries
|
|
265
|
+
from tui.widgets.plan import entries_from_dicts
|
|
266
|
+
self.query_one("SideBar Plan", Plan).entries = entries_from_dicts(message.entries)
|
|
315
267
|
|
|
316
268
|
@on(messages.SessionUpdate)
|
|
317
269
|
async def on_session_update(self, event: messages.SessionUpdate) -> None:
|
|
@@ -226,13 +226,24 @@ class ProjectsApp(App):
|
|
|
226
226
|
except Exception:
|
|
227
227
|
ws = Path.cwd().resolve()
|
|
228
228
|
name = ws.name
|
|
229
|
-
from cdh.scaffold import scaffold_dlc_project
|
|
230
|
-
scaffold_dlc_project(ws, name)
|
|
231
|
-
CdhProjectLoader.init_project(ws, name)
|
|
232
229
|
projects_dir = CLOUD_DEV_HARNESS_DIR / "projects"
|
|
233
230
|
projects_dir.mkdir(parents=True, exist_ok=True)
|
|
231
|
+
proj_file = projects_dir / f"{name}.yaml"
|
|
232
|
+
if proj_file.exists():
|
|
233
|
+
self.notify(
|
|
234
|
+
f"Project '{name}' already exists in the project list",
|
|
235
|
+
severity="error",
|
|
236
|
+
)
|
|
237
|
+
return
|
|
238
|
+
from cdh.scaffold import scaffold_dlc_project
|
|
239
|
+
try:
|
|
240
|
+
scaffold_dlc_project(ws, name)
|
|
241
|
+
except (ValueError, RuntimeError) as e:
|
|
242
|
+
self.notify(str(e), severity="error")
|
|
243
|
+
return
|
|
244
|
+
CdhProjectLoader.init_project(ws, name)
|
|
234
245
|
proj_data = {"name": name, "path": str(ws), "description": ""}
|
|
235
|
-
|
|
246
|
+
proj_file.write_text(yaml.dump(proj_data))
|
|
236
247
|
cfg = load_config()
|
|
237
248
|
cfg.current_project = name
|
|
238
249
|
cfg.current_project_path = str(ws)
|
|
@@ -262,9 +273,26 @@ class ProjectsApp(App):
|
|
|
262
273
|
self.notify(f".cdh already exists at {existing}", severity="warning")
|
|
263
274
|
return
|
|
264
275
|
name = target.name
|
|
276
|
+
projects_dir = CLOUD_DEV_HARNESS_DIR / "projects"
|
|
277
|
+
projects_dir.mkdir(parents=True, exist_ok=True)
|
|
278
|
+
proj_file = projects_dir / f"{name}.yaml"
|
|
279
|
+
if proj_file.exists():
|
|
280
|
+
self.notify(
|
|
281
|
+
f"Project '{name}' already exists in the project list",
|
|
282
|
+
severity="error",
|
|
283
|
+
)
|
|
284
|
+
return
|
|
265
285
|
from cdh.scaffold import init_dlc_project
|
|
266
|
-
|
|
286
|
+
try:
|
|
287
|
+
init_dlc_project(target, name)
|
|
288
|
+
except (ValueError, RuntimeError) as e:
|
|
289
|
+
self.notify(str(e), severity="error")
|
|
290
|
+
return
|
|
267
291
|
CdhProjectLoader.init_project(target, name)
|
|
292
|
+
import yaml
|
|
293
|
+
proj_data = {"name": name, "path": str(target), "description": ""}
|
|
294
|
+
proj_file.write_text(yaml.dump(proj_data))
|
|
295
|
+
self._refresh()
|
|
268
296
|
self.notify(f"Initialized .cdh in {target}")
|
|
269
297
|
|
|
270
298
|
def _load_project(self) -> None:
|
|
@@ -2,7 +2,6 @@ from time import monotonic
|
|
|
2
2
|
|
|
3
3
|
from textual.app import ComposeResult
|
|
4
4
|
from textual.binding import Binding
|
|
5
|
-
from textual.events import ScreenResume
|
|
6
5
|
from textual.screen import ModalScreen
|
|
7
6
|
from textual import getters
|
|
8
7
|
from textual.widget import Widget
|
|
@@ -17,12 +16,26 @@ from tui.app import A2TUIApp
|
|
|
17
16
|
from tui.widgets.grid_select import GridSelect
|
|
18
17
|
from tui.widgets.project_grid_select import ProjectGridSelect
|
|
19
18
|
from tui.widgets.project_summary import ProjectSummary
|
|
19
|
+
from tui.screens.component_picker import ComponentPickerScreen
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
CONFIRM_TIMEOUT = 5.0
|
|
23
23
|
INSTRUCTIONS_NO_PROJECTS = "Your projects will be shown here."
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
def _project_db_path(name: str):
|
|
27
|
+
"""Return the path of the project entry in ``~/.cdh/projects/``
|
|
28
|
+
for *name* across all supported extensions, or ``None`` if the
|
|
29
|
+
project is not registered yet.
|
|
30
|
+
"""
|
|
31
|
+
projects_dir = CLOUD_DEV_HARNESS_DIR / "projects"
|
|
32
|
+
for ext in ("yaml", "yml", "json"):
|
|
33
|
+
pf = projects_dir / f"{name}.{ext}"
|
|
34
|
+
if pf.exists():
|
|
35
|
+
return pf
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
26
39
|
class ProjectsScreen(ModalScreen[str]):
|
|
27
40
|
CSS_PATH = "projects.tcss"
|
|
28
41
|
BINDINGS = [
|
|
@@ -41,7 +54,7 @@ class ProjectsScreen(ModalScreen[str]):
|
|
|
41
54
|
yield widgets.Label("Projects")
|
|
42
55
|
yield widgets.Static(INSTRUCTIONS_NO_PROJECTS, classes="instructions")
|
|
43
56
|
yield ProjectGridSelect()
|
|
44
|
-
with containers.
|
|
57
|
+
with containers.Horizontal(id="actions"):
|
|
45
58
|
yield widgets.Button("+ New Project", id="new-project", variant="primary")
|
|
46
59
|
yield widgets.Button("Init .cdh (i)", id="init-project")
|
|
47
60
|
yield widgets.Footer()
|
|
@@ -89,12 +102,95 @@ class ProjectsScreen(ModalScreen[str]):
|
|
|
89
102
|
self.notify(f"Deleted project: {project_name}")
|
|
90
103
|
|
|
91
104
|
def action_new_project(self) -> None:
|
|
92
|
-
|
|
105
|
+
from pathlib import Path
|
|
106
|
+
from onecode.config_screen import EditFieldScreen
|
|
107
|
+
|
|
108
|
+
default_path = str(Path.cwd().resolve())
|
|
109
|
+
self.app.push_screen(
|
|
110
|
+
EditFieldScreen("Project path", default_path),
|
|
111
|
+
self._on_new_project_path,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def _on_new_project_path(self, path_str: str | None) -> None:
|
|
115
|
+
from pathlib import Path
|
|
116
|
+
|
|
117
|
+
if not path_str:
|
|
118
|
+
return
|
|
119
|
+
try:
|
|
120
|
+
target = Path(path_str).expanduser().resolve()
|
|
121
|
+
except Exception:
|
|
122
|
+
self.notify("Invalid path", severity="error")
|
|
123
|
+
return
|
|
124
|
+
if _project_db_path(target.name) is not None:
|
|
125
|
+
self.notify(
|
|
126
|
+
f"Project '{target.name}' is already in the project list",
|
|
127
|
+
severity="warning",
|
|
128
|
+
)
|
|
129
|
+
return
|
|
130
|
+
self.app.push_screen(
|
|
131
|
+
ComponentPickerScreen(
|
|
132
|
+
title="New Project — Select Components",
|
|
133
|
+
subtitle=(
|
|
134
|
+
f"Project path: {target}\n"
|
|
135
|
+
"Cross-cutting items (contracts, shared types, etc.) are created automatically."
|
|
136
|
+
),
|
|
137
|
+
allow_empty=False,
|
|
138
|
+
),
|
|
139
|
+
lambda picked: self._on_new_project_components(target, picked),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def _on_new_project_components(
|
|
143
|
+
self,
|
|
144
|
+
target: "Path",
|
|
145
|
+
picked: list[str] | None,
|
|
146
|
+
) -> None:
|
|
147
|
+
if picked is None:
|
|
148
|
+
self.notify("New project cancelled", severity="warning")
|
|
149
|
+
return
|
|
150
|
+
if not picked:
|
|
151
|
+
return
|
|
152
|
+
self._do_new_project(target, picked)
|
|
153
|
+
|
|
154
|
+
def _do_new_project(
|
|
155
|
+
self,
|
|
156
|
+
target: "Path",
|
|
157
|
+
components: list[str],
|
|
158
|
+
) -> None:
|
|
159
|
+
from pathlib import Path
|
|
160
|
+
from onecode.agent.cdh_loader import CdhProjectLoader
|
|
161
|
+
|
|
162
|
+
name = target.name
|
|
163
|
+
from cdh.scaffold import scaffold_dlc_project
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
scaffold_dlc_project(target, name, components=components)
|
|
167
|
+
except (ValueError, RuntimeError) as e:
|
|
168
|
+
self.notify(str(e), severity="error")
|
|
169
|
+
return
|
|
170
|
+
if _project_db_path(name) is not None:
|
|
171
|
+
self.notify(
|
|
172
|
+
f"Project '{name}' is already in the project list",
|
|
173
|
+
severity="error",
|
|
174
|
+
)
|
|
175
|
+
return
|
|
176
|
+
projects_dir = CLOUD_DEV_HARNESS_DIR / "projects"
|
|
177
|
+
projects_dir.mkdir(parents=True, exist_ok=True)
|
|
178
|
+
proj_file = projects_dir / f"{name}.yaml"
|
|
179
|
+
CdhProjectLoader.init_project(target, name)
|
|
180
|
+
import yaml
|
|
181
|
+
proj_data = {"name": name, "path": str(target), "description": ""}
|
|
182
|
+
proj_file.write_text(yaml.dump(proj_data))
|
|
183
|
+
widget = ProjectSummary(name, str(target), id=name)
|
|
184
|
+
self.project_grid_select.mount(widget)
|
|
185
|
+
self.project_grid_select.highlighted = len(self.project_grid_select.children) - 1
|
|
186
|
+
self.notify(
|
|
187
|
+
f"Created project '{name}' at {target} "
|
|
188
|
+
f"(components: {', '.join(components)})"
|
|
189
|
+
)
|
|
93
190
|
|
|
94
191
|
def action_init_project(self) -> None:
|
|
95
192
|
from pathlib import Path
|
|
96
193
|
from onecode.config_screen import EditFieldScreen
|
|
97
|
-
from onecode.agent.cdh_loader import CdhProjectLoader
|
|
98
194
|
|
|
99
195
|
default_path = str(Path.cwd().resolve())
|
|
100
196
|
self.app.push_screen(
|
|
@@ -117,14 +213,80 @@ class ProjectsScreen(ModalScreen[str]):
|
|
|
117
213
|
self.notify("Invalid path", severity="error")
|
|
118
214
|
return
|
|
119
215
|
existing = CdhProjectLoader.find_cdh_dir(target)
|
|
120
|
-
if existing is not None:
|
|
216
|
+
if existing is not None and existing.parent == target:
|
|
121
217
|
self.notify(f".cdh already exists at {existing}", severity="warning")
|
|
122
218
|
return
|
|
219
|
+
if _project_db_path(target.name) is not None:
|
|
220
|
+
self.notify(
|
|
221
|
+
f"Project '{target.name}' is already in the project list",
|
|
222
|
+
severity="warning",
|
|
223
|
+
)
|
|
224
|
+
return
|
|
225
|
+
self.app.push_screen(
|
|
226
|
+
ComponentPickerScreen(
|
|
227
|
+
title="Init .cdh — Select Components",
|
|
228
|
+
subtitle=(
|
|
229
|
+
f"Directory: {target}\n"
|
|
230
|
+
"Pick the components already present in this directory. "
|
|
231
|
+
"Empty selection is allowed (no apps/ created)."
|
|
232
|
+
),
|
|
233
|
+
allow_empty=True,
|
|
234
|
+
),
|
|
235
|
+
lambda picked: self._on_init_components(target, picked),
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
def _on_init_components(
|
|
239
|
+
self,
|
|
240
|
+
target: "Path",
|
|
241
|
+
picked: list[str] | None,
|
|
242
|
+
) -> None:
|
|
243
|
+
if picked is None:
|
|
244
|
+
self.notify("Init cancelled", severity="warning")
|
|
245
|
+
return
|
|
246
|
+
self._do_init_project(target, picked)
|
|
247
|
+
|
|
248
|
+
def _do_init_project(
|
|
249
|
+
self,
|
|
250
|
+
target: "Path",
|
|
251
|
+
components: list[str],
|
|
252
|
+
) -> None:
|
|
253
|
+
from pathlib import Path
|
|
254
|
+
from onecode.agent.cdh_loader import CdhProjectLoader
|
|
255
|
+
from cdh.scaffold import add_component, init_dlc_project
|
|
256
|
+
|
|
123
257
|
name = target.name
|
|
124
|
-
|
|
125
|
-
|
|
258
|
+
if _project_db_path(name) is not None:
|
|
259
|
+
self.notify(
|
|
260
|
+
f"Project '{name}' is already in the project list",
|
|
261
|
+
severity="error",
|
|
262
|
+
)
|
|
263
|
+
return
|
|
264
|
+
try:
|
|
265
|
+
init_dlc_project(target, name)
|
|
266
|
+
except (ValueError, RuntimeError) as e:
|
|
267
|
+
self.notify(str(e), severity="error")
|
|
268
|
+
return
|
|
269
|
+
for cid in components:
|
|
270
|
+
try:
|
|
271
|
+
add_component(target, cid)
|
|
272
|
+
except (ValueError, FileNotFoundError) as e:
|
|
273
|
+
self.notify(str(e), severity="error")
|
|
126
274
|
CdhProjectLoader.init_project(target, name)
|
|
127
|
-
|
|
275
|
+
import yaml
|
|
276
|
+
projects_dir = CLOUD_DEV_HARNESS_DIR / "projects"
|
|
277
|
+
projects_dir.mkdir(parents=True, exist_ok=True)
|
|
278
|
+
proj_file = projects_dir / f"{name}.yaml"
|
|
279
|
+
proj_data = {"name": name, "path": str(target), "description": ""}
|
|
280
|
+
proj_file.write_text(yaml.dump(proj_data))
|
|
281
|
+
widget = ProjectSummary(name, str(target), id=name)
|
|
282
|
+
self.project_grid_select.mount(widget)
|
|
283
|
+
self.project_grid_select.highlighted = len(self.project_grid_select.children) - 1
|
|
284
|
+
suffix = (
|
|
285
|
+
f" (components: {', '.join(components)})"
|
|
286
|
+
if components
|
|
287
|
+
else " (no components)"
|
|
288
|
+
)
|
|
289
|
+
self.notify(f"Initialized .cdh in {target}{suffix}")
|
|
128
290
|
|
|
129
291
|
@on(GridSelect.Selected)
|
|
130
292
|
def on_selected(self, event: GridSelect.Selected) -> None:
|
|
@@ -135,7 +297,7 @@ class ProjectsScreen(ModalScreen[str]):
|
|
|
135
297
|
|
|
136
298
|
@on(widgets.Button.Pressed, "#new-project")
|
|
137
299
|
def on_new_project(self) -> None:
|
|
138
|
-
self.
|
|
300
|
+
self.action_new_project()
|
|
139
301
|
|
|
140
302
|
@on(widgets.Button.Pressed, "#init-project")
|
|
141
303
|
def on_init_project(self) -> None:
|
package/tui/screens/store.py
CHANGED
|
@@ -421,7 +421,7 @@ class StoreScreen(Screen):
|
|
|
421
421
|
async def on_mount(self) -> None:
|
|
422
422
|
self.app.settings_changed_signal.subscribe(self, self.setting_updated)
|
|
423
423
|
if not self.app.settings.get("launcher.agents", str).strip():
|
|
424
|
-
self.app.settings.set("launcher.agents", "
|
|
424
|
+
self.app.settings.set("launcher.agents", "onecode.dev")
|
|
425
425
|
await self.app.save_settings()
|
|
426
426
|
try:
|
|
427
427
|
self._agents = await read_agents()
|
package/tui/settings_schema.py
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/tui/widgets/ask_user.py
CHANGED
|
@@ -260,5 +260,5 @@ class AskUserWidget(VerticalGroup):
|
|
|
260
260
|
def _finish(self, value: str) -> None:
|
|
261
261
|
self._answer = value
|
|
262
262
|
self._done = True
|
|
263
|
-
self.refresh(recompose=True
|
|
263
|
+
self.refresh(recompose=True)
|
|
264
264
|
self.post_message(AskUserSubmitted(value, self._tool_id))
|