@scemoon/cdh 1.0.4 → 1.0.6
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/ai-dlc-skill/SKILL.md +4 -2
- package/ai-dlc-skill/agents/master.md +6 -0
- package/ai-dlc-skill/agents/plan-agent.md +2 -2
- package/ai-dlc-skill/agents/understand-agent.md +4 -4
- package/ai-dlc-skill/agents/verify-agent.md +1 -1
- package/ai-dlc-skill/architecture/project-structure.md +10 -10
- package/ai-dlc-skill/brownfield/README.md +2 -2
- package/ai-dlc-skill/brownfield/generate-context.sh +3 -3
- package/ai-dlc-skill/brownfield/scripts/extract-api.sh +5 -5
- package/ai-dlc-skill/components/backend.md +1 -1
- package/ai-dlc-skill/components/web.md +2 -2
- package/ai-dlc-skill/contracts/README.md +3 -3
- package/ai-dlc-skill/cross-tool/cline/export.sh +1 -1
- package/ai-dlc-skill/cross-tool/copilot/export.sh +1 -1
- package/ai-dlc-skill/cross-tool/onecode/export-skills.sh +7 -3
- package/ai-dlc-skill/cross-tool/onecode/export.sh +14 -8
- package/ai-dlc-skill/phases/deliver/lifecycle.md +6 -6
- package/ai-dlc-skill/phases/deliver/prompt.md +2 -2
- package/ai-dlc-skill/phases/deliver/rules.md +1 -1
- package/ai-dlc-skill/phases/plan/lifecycle.md +11 -11
- package/ai-dlc-skill/phases/plan/prompt.md +6 -6
- package/ai-dlc-skill/phases/plan/rules.md +1 -1
- package/ai-dlc-skill/phases/understand/entry.md +1 -1
- package/ai-dlc-skill/phases/understand/lifecycle.md +7 -7
- package/ai-dlc-skill/phases/understand/prompt.md +8 -8
- package/ai-dlc-skill/phases/understand/rules.md +5 -5
- package/ai-dlc-skill/phases/verify/lifecycle.md +24 -24
- package/ai-dlc-skill/phases/verify/prompt.md +8 -8
- package/ai-dlc-skill/phases/verify/rules.md +9 -9
- package/ai-dlc-skill/providers/README.md +2 -2
- package/ai-dlc-skill/providers/aliyun/preview.yaml +2 -2
- package/ai-dlc-skill/providers/tcb/preview.yaml +2 -2
- package/ai-dlc-skill/requirements.md +7 -7
- package/ai-dlc-skill/skill.yaml +4 -4
- package/ai-dlc-skill/templates/integration/CHANGELOG.md +2 -2
- package/ai-dlc-skill/templates/integration/contract-diff.md +7 -7
- package/ai-dlc-skill/templates/integration/contract-spec.md +7 -7
- package/ai-dlc-skill/templates/integration/runtime-contract.yaml +1 -1
- package/ai-dlc-skill/templates/project/README.md +18 -19
- package/ai-dlc-skill/templates/project/template.md +21 -21
- package/ai-dlc-skill/walkthrough/collect.sh +1 -1
- package/ai-dlc-skill/walkthrough/template.md +2 -2
- package/ai-dlc-skill/workflows/ai-dlc.yaml +12 -12
- package/ai-dlc-skill/workflows/deployment.yaml +4 -4
- package/cdh/__pycache__/cdh_mcp_injector.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_mcp_loader.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_mcp_manager.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_session_aggregator.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_skill_loader.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_skill_manager.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cli.cpython-313.pyc +0 -0
- package/cdh/__pycache__/cli.cpython-314.pyc +0 -0
- package/cdh/__pycache__/scaffold.cpython-314.pyc +0 -0
- package/cdh/cdh_mcp_injector.py +115 -0
- package/cdh/cdh_mcp_loader.py +67 -0
- package/cdh/cdh_mcp_manager.py +73 -0
- package/cdh/cdh_session_aggregator.py +258 -0
- package/cdh/cdh_skill_manager.py +105 -0
- package/cdh/cli.py +64 -4
- package/cdh/scaffold.py +192 -41
- package/onecode/__init__.py +1 -1
- 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/__pycache__/config_screen.cpython-314.pyc +0 -0
- package/onecode/__pycache__/migrate.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/cdh_loader.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/context.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/engine.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/hooks.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/onecode_agent_acp.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/permissions.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/project_doc.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 +47 -164
- package/onecode/agent/attachments.py +0 -1
- package/onecode/agent/cdh_loader.py +17 -25
- package/onecode/agent/context.py +8 -3
- package/onecode/agent/engine.py +835 -220
- package/onecode/agent/hooks.py +1 -1
- package/onecode/agent/onecode_agent_acp.py +460 -47
- package/onecode/agent/onecode_agent_acp.py.bak +2447 -0
- package/onecode/agent/permissions.py +0 -1
- package/onecode/agent/project_doc.py +19 -0
- package/onecode/agent/session.py +17 -3
- package/onecode/agent/snapshot.py +3 -4
- package/onecode/agent/tools/__pycache__/agent_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/apply_patch_tool.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/bash_tool.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/communication_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/config_tool.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/cron_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/file_ops.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/file_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/git_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/lsp_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/mcp_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/permission_handler.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/protocol.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/registry.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/sandbox.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/skill_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/todo_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/web_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/agent_tools.py +4 -6
- package/onecode/agent/tools/apply_patch_tool.py +2 -8
- package/onecode/agent/tools/bash_tool.py +32 -12
- package/onecode/agent/tools/communication_tools.py +4 -5
- package/onecode/agent/tools/config_tool.py +3 -3
- package/onecode/agent/tools/cron_tools.py +4 -5
- package/onecode/agent/tools/file_ops.py +37 -3
- package/onecode/agent/tools/file_tools.py +10 -11
- package/onecode/agent/tools/git_tools.py +2 -2
- package/onecode/agent/tools/lsp_tools.py +2 -2
- package/onecode/agent/tools/mcp_tools.py +3 -5
- package/onecode/agent/tools/permission_handler.py +1 -1
- package/onecode/agent/tools/protocol.py +1 -1
- package/onecode/agent/tools/registry.py +69 -2
- package/onecode/agent/tools/sandbox.py +110 -16
- package/onecode/agent/tools/skill_tools.py +2 -2
- package/onecode/agent/tools/todo_tools.py +38 -19
- package/onecode/agent/tools/web_tools.py +6 -7
- package/onecode/builtin_skills/agent-browser/SKILL.md +212 -0
- package/onecode/builtin_skills/agent-browser/skill.yaml +8 -0
- package/onecode/builtin_skills/skill-creator/SKILL.md +106 -0
- package/onecode/builtin_skills/skill-creator/skill.yaml +8 -0
- package/onecode/cli.py +186 -6
- package/onecode/codebase/__pycache__/chunker.cpython-314.pyc +0 -0
- package/onecode/codebase/__pycache__/indexer.cpython-314.pyc +0 -0
- package/onecode/codebase/__pycache__/retriever.cpython-314.pyc +0 -0
- package/onecode/codebase/__pycache__/storage.cpython-314.pyc +0 -0
- package/onecode/codebase/chunker.py +0 -5
- package/onecode/codebase/indexer.py +14 -1
- package/onecode/codebase/retriever.py +13 -2
- package/onecode/codebase/storage.py +5 -4
- package/onecode/commands.py +81 -6
- package/onecode/config.py +41 -12
- package/onecode/config_screen.py +7 -3
- package/onecode/mcp/__pycache__/manager.cpython-314.pyc +0 -0
- package/onecode/mcp/manager.py +2 -3
- package/onecode/memory/__init__.py +7 -53
- package/onecode/memory/__pycache__/__init__.cpython-314.pyc +0 -0
- package/onecode/memory/__pycache__/backend.cpython-314.pyc +0 -0
- package/onecode/memory/__pycache__/offload.cpython-314.pyc +0 -0
- package/onecode/memory/__pycache__/pyramid.cpython-314.pyc +0 -0
- package/onecode/memory/__pycache__/recall.cpython-314.pyc +0 -0
- package/onecode/memory/backend.py +13 -39
- package/onecode/memory/pyramid.py +2 -71
- package/onecode/memory/recall.py +38 -3
- package/onecode/migrate.py +122 -0
- package/onecode/models/__pycache__/messages.cpython-314.pyc +0 -0
- package/onecode/models/__pycache__/provider.cpython-314.pyc +0 -0
- package/onecode/models/messages.py +58 -0
- package/onecode/models/provider.py +20 -2
- package/onecode/models/providers/__pycache__/anthropic_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/anthropic_provider.py +59 -12
- package/onecode/models/providers/glm_provider.py +40 -0
- package/onecode/models/providers/minimax_provider.py +3 -0
- package/onecode/models/providers/minimaxi_provider.py +3 -0
- package/onecode/models/providers/ollama_provider.py +3 -0
- package/onecode/server/app.py +1 -1
- package/onecode/skills/__pycache__/argument_substitution.cpython-314.pyc +0 -0
- package/onecode/skills/__pycache__/bootstrap.cpython-314.pyc +0 -0
- package/onecode/skills/__pycache__/loader.cpython-314.pyc +0 -0
- package/onecode/skills/__pycache__/manager.cpython-314.pyc +0 -0
- package/onecode/skills/argument_substitution.py +0 -1
- package/onecode/skills/bootstrap.py +124 -0
- package/onecode/skills/loader.py +35 -53
- package/onecode/skills/manager.py +2 -2
- package/onecode/storage/activity.py +3 -4
- package/onecode/storage/session.py +3 -4
- package/onecode/tasks/manager.py +23 -10
- package/onecode/trace/tracer.py +2 -3
- package/package.json +1 -1
- package/pyproject.toml +5 -1
- package/run.js +68 -14
- package/tui/__pycache__/about.cpython-314.pyc +0 -0
- package/tui/__pycache__/app.cpython-313.pyc +0 -0
- package/tui/__pycache__/app.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/__pycache__/messages.cpython-314.pyc +0 -0
- package/tui/__pycache__/path_complete.cpython-314.pyc +0 -0
- package/tui/__pycache__/paths.cpython-314.pyc +0 -0
- package/tui/__pycache__/session_tracker.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 +125 -58
- package/tui/acp/api.py +12 -0
- package/tui/acp/messages.py +3 -1
- package/tui/acp/prompt.py +0 -57
- package/tui/acp/protocol.py +6 -0
- package/tui/app.py +11 -33
- package/tui/messages.py +2 -0
- package/tui/path_complete.py +1 -1
- package/tui/paths.py +16 -0
- package/tui/screens/__pycache__/agent_modal.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/component_picker.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/diff_screen.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/log.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/main.cpython-313.pyc +0 -0
- package/tui/screens/__pycache__/main.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/permissions.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/projects_app.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__/store.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/tool_content_screen.cpython-314.pyc +0 -0
- package/tui/screens/component_picker.py +35 -15
- package/tui/screens/diff_screen.py +84 -0
- package/tui/screens/log.py +25 -25
- package/tui/screens/main.py +48 -26
- package/tui/screens/projects.tcss +5 -1
- package/tui/screens/projects_app.py +6 -6
- package/tui/screens/projects_screen.py +19 -39
- package/tui/screens/sessions.py +3 -5
- package/tui/session_tracker.py +3 -0
- package/tui/settings_schema.py +1 -1
- package/tui/visuals/__pycache__/columns.cpython-314.pyc +0 -0
- package/tui/visuals/columns.py +1 -2
- package/tui/widgets/__pycache__/acp_content.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/agent_response.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/agent_thought.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/conversation.cpython-314.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__/load_more.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__/modified_files.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/plan.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__/side_bar.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/subagent.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/subagent_screen.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/terminal.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/tool_call.cpython-314.pyc +0 -0
- package/tui/widgets/agent_response.py +0 -1
- package/tui/widgets/agent_thought.py +0 -1
- package/tui/widgets/conversation.py +157 -14
- package/tui/widgets/diff_view.py +10 -0
- package/tui/widgets/load_more.py +24 -0
- package/tui/widgets/modified_files.py +86 -12
- package/tui/widgets/plan.py +1 -1
- package/tui/widgets/project_grid_select.py +9 -2
- package/tui/widgets/session_grid_select.py +6 -0
- package/tui/widgets/side_bar.py +10 -0
- package/tui/widgets/subagent.py +92 -58
- package/tui/widgets/subagent_screen.py +638 -115
- package/tui/widgets/terminal.py +1 -2
- package/tui/widgets/terminal_tool.py +11 -2
- package/tui/widgets/tool_call.py +35 -2
- package/onecode/memory/offload.py +0 -114
- package/onecode/memory/symbolic.py +0 -164
package/tui/acp/prompt.py
CHANGED
|
@@ -6,54 +6,6 @@ from tui.prompt.extract import extract_paths_from_prompt
|
|
|
6
6
|
from tui.prompt.resource import load_resource, ResourceError
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
CLOUD_SPEC_SKILL_PATH = Path(__file__).resolve().parent.parent.parent / "cloud-spec-skill" / "SKILL.md"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def _get_cloud_spec_content() -> str:
|
|
13
|
-
"""Load cloud-spec-skill content."""
|
|
14
|
-
if CLOUD_SPEC_SKILL_PATH.exists():
|
|
15
|
-
return CLOUD_SPEC_SKILL_PATH.read_text(encoding="utf-8")
|
|
16
|
-
return ""
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def cloud_spec_skill_loaded() -> bool:
|
|
20
|
-
"""Check if cloud-spec-skill SKILL.md exists."""
|
|
21
|
-
return CLOUD_SPEC_SKILL_PATH.exists()
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def ensure_cloud_spec_skill_installed() -> str | None:
|
|
25
|
-
"""Ensure cloud-spec-skill is accessible to agents.
|
|
26
|
-
|
|
27
|
-
Checks if SKILL.md exists in repo and copies it to ~/.cdh/skills/ if needed,
|
|
28
|
-
or updates if the repo version is newer.
|
|
29
|
-
Returns error message if installation fails, None on success.
|
|
30
|
-
"""
|
|
31
|
-
from onecode.skills.loader import USER_SKILLS_DIR
|
|
32
|
-
|
|
33
|
-
if not CLOUD_SPEC_SKILL_PATH.exists():
|
|
34
|
-
return None
|
|
35
|
-
|
|
36
|
-
skill_dest = USER_SKILLS_DIR / "cloud-spec-skill"
|
|
37
|
-
dest_skills_md = skill_dest / "SKILL.md"
|
|
38
|
-
|
|
39
|
-
if skill_dest.exists() and dest_skills_md.exists():
|
|
40
|
-
repo_mtime = CLOUD_SPEC_SKILL_PATH.stat().st_mtime
|
|
41
|
-
dest_mtime = dest_skills_md.stat().st_mtime
|
|
42
|
-
if dest_mtime >= repo_mtime:
|
|
43
|
-
return None
|
|
44
|
-
|
|
45
|
-
try:
|
|
46
|
-
import shutil
|
|
47
|
-
|
|
48
|
-
if skill_dest.exists():
|
|
49
|
-
shutil.rmtree(skill_dest)
|
|
50
|
-
skill_dest.parent.mkdir(parents=True, exist_ok=True)
|
|
51
|
-
shutil.copytree(CLOUD_SPEC_SKILL_PATH.parent, skill_dest)
|
|
52
|
-
return None
|
|
53
|
-
except Exception as e:
|
|
54
|
-
return str(e)
|
|
55
|
-
|
|
56
|
-
|
|
57
9
|
def build(project_path: Path, prompt: str) -> list[protocol.ContentBlock]:
|
|
58
10
|
"""Build the prompt structure and extract paths with the @ syntax.
|
|
59
11
|
|
|
@@ -66,14 +18,6 @@ def build(project_path: Path, prompt: str) -> list[protocol.ContentBlock]:
|
|
|
66
18
|
"""
|
|
67
19
|
prompt_content: list[protocol.ContentBlock] = []
|
|
68
20
|
|
|
69
|
-
# Prepend cloud-spec-skill as system guidance
|
|
70
|
-
cloud_spec_content = _get_cloud_spec_content()
|
|
71
|
-
if cloud_spec_content:
|
|
72
|
-
prompt_content.append({
|
|
73
|
-
"type": "text",
|
|
74
|
-
"text": f"[System Guidance - Always follow these development standards]\n\n{cloud_spec_content}\n\n---\n\n"
|
|
75
|
-
})
|
|
76
|
-
|
|
77
21
|
prompt_content.append({"type": "text", "text": prompt})
|
|
78
22
|
for path, _, _ in extract_paths_from_prompt(prompt):
|
|
79
23
|
if path.endswith("/"):
|
|
@@ -81,7 +25,6 @@ def build(project_path: Path, prompt: str) -> list[protocol.ContentBlock]:
|
|
|
81
25
|
try:
|
|
82
26
|
resource = load_resource(project_path, Path(path))
|
|
83
27
|
except ResourceError:
|
|
84
|
-
# TODO: How should this be handled?
|
|
85
28
|
continue
|
|
86
29
|
uri = f"file://{resource.path.absolute().resolve()}"
|
|
87
30
|
if resource.text is not None:
|
package/tui/acp/protocol.py
CHANGED
|
@@ -217,6 +217,11 @@ class ToolCall(SchemaDict, total=False, extra_items=Any):
|
|
|
217
217
|
status: ToolCallStatus
|
|
218
218
|
title: Required[str]
|
|
219
219
|
toolCallId: Required[ToolCallId]
|
|
220
|
+
# Custom (non-ACP) field: when set, this tool call was invoked *inside* a
|
|
221
|
+
# subagent and the TUI should mount/update the ToolCall card inside the
|
|
222
|
+
# owning SubAgent widget (subagentId = the parent Spawn toolCallId) rather
|
|
223
|
+
# than in the main conversation.
|
|
224
|
+
subagentId: str
|
|
220
225
|
|
|
221
226
|
|
|
222
227
|
# https://agentclientprotocol.com/protocol/schema#toolcallupdate
|
|
@@ -231,6 +236,7 @@ class ToolCallUpdate(SchemaDict, total=False, extra_items=Any):
|
|
|
231
236
|
status: ToolCallStatus | None
|
|
232
237
|
title: str | None
|
|
233
238
|
toolCallId: Required[ToolCallId]
|
|
239
|
+
subagentId: str
|
|
234
240
|
|
|
235
241
|
|
|
236
242
|
# https://agentclientprotocol.com/protocol/schema#param-tool-call
|
package/tui/app.py
CHANGED
|
@@ -22,7 +22,6 @@ from textual.timer import Timer
|
|
|
22
22
|
from textual.notifications import Notify
|
|
23
23
|
from textual.screen import Screen
|
|
24
24
|
|
|
25
|
-
import tui
|
|
26
25
|
from tui.db import DB
|
|
27
26
|
from tui.settings import Schema, Settings
|
|
28
27
|
from tui.agent_schema import Agent as AgentData
|
|
@@ -234,12 +233,6 @@ def get_store_screen():
|
|
|
234
233
|
return StoreScreen()
|
|
235
234
|
|
|
236
235
|
|
|
237
|
-
def get_projects_screen() -> "ProjectsScreen":
|
|
238
|
-
from tui.screens.projects_screen import ProjectsScreen
|
|
239
|
-
|
|
240
|
-
return ProjectsScreen()
|
|
241
|
-
|
|
242
|
-
|
|
243
236
|
class A2TUIApp(App, inherit_bindings=False):
|
|
244
237
|
"""The top level app."""
|
|
245
238
|
|
|
@@ -249,7 +242,6 @@ class A2TUIApp(App, inherit_bindings=False):
|
|
|
249
242
|
"settings": get_settings_screen,
|
|
250
243
|
"sessions": get_sessions_screen,
|
|
251
244
|
"store": get_store_screen,
|
|
252
|
-
"projects": get_projects_screen,
|
|
253
245
|
}
|
|
254
246
|
MODES = {}
|
|
255
247
|
BINDING_GROUP_TITLE = "System"
|
|
@@ -361,9 +353,12 @@ class A2TUIApp(App, inherit_bindings=False):
|
|
|
361
353
|
return
|
|
362
354
|
session_time = monotonic() - conv.session_start_time
|
|
363
355
|
session_id = getattr(conv.agent, "session_id", None) or conv._agent_session_id or ""
|
|
356
|
+
session_pk = conv._session_pk
|
|
357
|
+
if session_pk is None and conv.agent is not None:
|
|
358
|
+
session_pk = getattr(conv.agent, "session_pk", None)
|
|
364
359
|
self._exit_metrics = {
|
|
365
360
|
"session_id": session_id,
|
|
366
|
-
"session_pk":
|
|
361
|
+
"session_pk": session_pk,
|
|
367
362
|
"tool_call_total": conv._tool_call_total,
|
|
368
363
|
"tool_call_success": conv._tool_call_success,
|
|
369
364
|
"tool_call_failed": conv._tool_call_failed,
|
|
@@ -838,15 +833,6 @@ class A2TUIApp(App, inherit_bindings=False):
|
|
|
838
833
|
self.capture_event("tui-run")
|
|
839
834
|
self.anon_id # Created on frst reference
|
|
840
835
|
|
|
841
|
-
from tui.acp.prompt import ensure_cloud_spec_skill_installed
|
|
842
|
-
|
|
843
|
-
if err := ensure_cloud_spec_skill_installed():
|
|
844
|
-
self.notify(
|
|
845
|
-
f"Failed to install cloud-spec-skill: {err}",
|
|
846
|
-
title="cloud-spec-skill",
|
|
847
|
-
severity="warning",
|
|
848
|
-
)
|
|
849
|
-
|
|
850
836
|
from pathlib import Path
|
|
851
837
|
|
|
852
838
|
if mode := self._initial_mode:
|
|
@@ -1015,7 +1001,7 @@ class A2TUIApp(App, inherit_bindings=False):
|
|
|
1015
1001
|
hint = f" Reload with: cdh session load {session_pk}\n"
|
|
1016
1002
|
elif sid:
|
|
1017
1003
|
display_id = sid
|
|
1018
|
-
hint = ""
|
|
1004
|
+
hint = f" Reload with: cdh session load {sid}\n"
|
|
1019
1005
|
else:
|
|
1020
1006
|
display_id = "-"
|
|
1021
1007
|
hint = ""
|
|
@@ -1142,15 +1128,16 @@ class A2TUIApp(App, inherit_bindings=False):
|
|
|
1142
1128
|
@work
|
|
1143
1129
|
async def action_projects(self) -> None:
|
|
1144
1130
|
from pathlib import Path
|
|
1145
|
-
from onecode.config import load_config, save_config
|
|
1131
|
+
from onecode.config import load_config, save_config
|
|
1146
1132
|
from onecode.config_screen import EditFieldScreen
|
|
1147
1133
|
import yaml
|
|
1148
1134
|
|
|
1149
|
-
|
|
1135
|
+
from tui.screens.projects_screen import ProjectsScreen
|
|
1136
|
+
result = await self.push_screen_wait(ProjectsScreen())
|
|
1150
1137
|
if result is None:
|
|
1151
1138
|
cfg = load_config()
|
|
1152
1139
|
if cfg.current_project:
|
|
1153
|
-
projects_dir =
|
|
1140
|
+
projects_dir = Path.home() / ".cdh" / "projects"
|
|
1154
1141
|
pf = projects_dir / f"{cfg.current_project}.yaml"
|
|
1155
1142
|
if not pf.exists():
|
|
1156
1143
|
cfg.current_project = None
|
|
@@ -1176,7 +1163,7 @@ class A2TUIApp(App, inherit_bindings=False):
|
|
|
1176
1163
|
from cdh.scaffold import scaffold_dlc_project
|
|
1177
1164
|
scaffold_dlc_project(project_path, name)
|
|
1178
1165
|
CdhProjectLoader.init_project(project_path, name)
|
|
1179
|
-
projects_dir =
|
|
1166
|
+
projects_dir = Path.home() / ".cdh" / "projects"
|
|
1180
1167
|
projects_dir.mkdir(parents=True, exist_ok=True)
|
|
1181
1168
|
proj_data = {"name": name, "path": str(project_path), "description": ""}
|
|
1182
1169
|
(projects_dir / f"{name}.yaml").write_text(yaml.dump(proj_data))
|
|
@@ -1191,7 +1178,7 @@ class A2TUIApp(App, inherit_bindings=False):
|
|
|
1191
1178
|
|
|
1192
1179
|
project_name = result if isinstance(result, str) else getattr(result, 'name', None)
|
|
1193
1180
|
if project_name:
|
|
1194
|
-
projects_dir =
|
|
1181
|
+
projects_dir = Path.home() / ".cdh" / "projects"
|
|
1195
1182
|
project_path = None
|
|
1196
1183
|
for ext in ["yaml", "yml", "json"]:
|
|
1197
1184
|
pf = projects_dir / f"{project_name}.{ext}"
|
|
@@ -1216,15 +1203,6 @@ class A2TUIApp(App, inherit_bindings=False):
|
|
|
1216
1203
|
|
|
1217
1204
|
@on(messages.LaunchAgent)
|
|
1218
1205
|
def on_launch_agent(self, message: messages.LaunchAgent) -> None:
|
|
1219
|
-
from tui.acp.prompt import ensure_cloud_spec_skill_installed
|
|
1220
|
-
|
|
1221
|
-
if err := ensure_cloud_spec_skill_installed():
|
|
1222
|
-
self.notify(
|
|
1223
|
-
f"Failed to install cloud-spec-skill: {err}",
|
|
1224
|
-
title="cloud-spec-skill",
|
|
1225
|
-
severity="warning",
|
|
1226
|
-
)
|
|
1227
|
-
|
|
1228
1206
|
self.launch_agent(
|
|
1229
1207
|
message.identity,
|
|
1230
1208
|
agent_session_id=message.session_id,
|
package/tui/messages.py
CHANGED
package/tui/path_complete.py
CHANGED
package/tui/paths.py
CHANGED
|
@@ -69,3 +69,19 @@ def get_log() -> Path:
|
|
|
69
69
|
with suppress(OSError):
|
|
70
70
|
path.mkdir(0o700, exist_ok=True, parents=True)
|
|
71
71
|
return path
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_sessions() -> Path:
|
|
75
|
+
"""Get the platform-level sessions directory (~/.cdh/sessions/).
|
|
76
|
+
|
|
77
|
+
Session JSON files are stored by the cdh platform, not by individual
|
|
78
|
+
agent engines. The DB index at ~/.cdh/state/tui/tui.db maps each
|
|
79
|
+
session to its owning engine (B mapping mode).
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Path to the sessions directory.
|
|
83
|
+
"""
|
|
84
|
+
path = Path.home() / ".cdh" / "sessions"
|
|
85
|
+
with suppress(OSError):
|
|
86
|
+
path.mkdir(0o700, exist_ok=True, parents=True)
|
|
87
|
+
return path
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -32,22 +32,22 @@ class ComponentPickerScreen(ModalScreen[Optional[list[str]]]):
|
|
|
32
32
|
width: 78;
|
|
33
33
|
height: auto;
|
|
34
34
|
max-height: 90%;
|
|
35
|
-
background:
|
|
36
|
-
border: solid
|
|
35
|
+
background: $surface;
|
|
36
|
+
border: solid $border;
|
|
37
37
|
padding: 0 1;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
#picker-title {
|
|
41
41
|
height: 1;
|
|
42
|
-
background:
|
|
43
|
-
color:
|
|
42
|
+
background: $panel;
|
|
43
|
+
color: $text;
|
|
44
44
|
padding: 0 1;
|
|
45
45
|
text-style: bold;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
#picker-subtitle {
|
|
49
49
|
height: 1;
|
|
50
|
-
color:
|
|
50
|
+
color: $text-muted;
|
|
51
51
|
padding: 0 1;
|
|
52
52
|
margin-bottom: 1;
|
|
53
53
|
}
|
|
@@ -56,43 +56,63 @@ class ComponentPickerScreen(ModalScreen[Optional[list[str]]]):
|
|
|
56
56
|
height: auto;
|
|
57
57
|
max-height: 22;
|
|
58
58
|
padding: 0 1;
|
|
59
|
+
background: $surface;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
.component-row {
|
|
62
63
|
height: 2;
|
|
63
64
|
padding: 0 1;
|
|
65
|
+
background: $surface;
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
.component-row Checkbox {
|
|
67
69
|
width: 1fr;
|
|
70
|
+
background: $surface;
|
|
71
|
+
color: $text;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.component-row Checkbox > .toggle--button {
|
|
75
|
+
background: $panel;
|
|
76
|
+
color: $text-muted;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.component-row Checkbox.-on > .toggle--button {
|
|
80
|
+
background: $primary;
|
|
81
|
+
color: $text;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.component-row Checkbox > .toggle--label {
|
|
85
|
+
color: $text;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
Footer {
|
|
89
|
+
background: $panel;
|
|
90
|
+
color: $text-muted;
|
|
68
91
|
}
|
|
69
92
|
|
|
70
93
|
#picker-buttons {
|
|
71
94
|
height: 3;
|
|
72
95
|
align: center middle;
|
|
73
|
-
background:
|
|
96
|
+
background: $panel;
|
|
74
97
|
}
|
|
75
98
|
|
|
76
99
|
#picker-buttons Button {
|
|
77
100
|
margin: 0 1;
|
|
78
|
-
background:
|
|
79
|
-
color:
|
|
101
|
+
background: $panel;
|
|
102
|
+
color: $text;
|
|
80
103
|
}
|
|
81
104
|
|
|
82
105
|
#picker-buttons Button:hover {
|
|
83
|
-
background:
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
#picker-buttons Button:focus {
|
|
87
|
-
background: #555;
|
|
106
|
+
background: $primary-darken-2;
|
|
88
107
|
}
|
|
89
108
|
|
|
90
109
|
#picker-buttons #picker-confirm {
|
|
91
|
-
background:
|
|
110
|
+
background: $primary-background;
|
|
111
|
+
color: $text;
|
|
92
112
|
}
|
|
93
113
|
|
|
94
114
|
#picker-buttons #picker-confirm:hover {
|
|
95
|
-
background:
|
|
115
|
+
background: $primary-darken-1;
|
|
96
116
|
}
|
|
97
117
|
"""
|
|
98
118
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from textual.app import ComposeResult
|
|
4
|
+
from textual.binding import Binding
|
|
5
|
+
from textual.containers import VerticalScroll
|
|
6
|
+
from textual.screen import ModalScreen
|
|
7
|
+
from textual.widgets import Static
|
|
8
|
+
|
|
9
|
+
from tui.widgets.diff_view import make_diff
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DiffScreen(ModalScreen[None]):
|
|
13
|
+
"""Full-screen view of a file diff.
|
|
14
|
+
|
|
15
|
+
Opened when a file is selected from the ModifiedFiles sidebar. Displays
|
|
16
|
+
the same DiffView widget that ``Conversation.post_diff`` would have mounted
|
|
17
|
+
inline, but as a dedicated modal screen instead of a conversation block.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
BINDINGS = [
|
|
21
|
+
Binding("escape", "dismiss(None)", "Close", show=False, priority=True),
|
|
22
|
+
Binding("q", "dismiss(None)", "Close", show=False, priority=True),
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
DEFAULT_CSS = """
|
|
26
|
+
DiffScreen {
|
|
27
|
+
background: $surface;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#diff-header {
|
|
31
|
+
dock: top;
|
|
32
|
+
width: 100%;
|
|
33
|
+
padding: 1 2;
|
|
34
|
+
background: $panel;
|
|
35
|
+
color: $text-primary;
|
|
36
|
+
text-style: bold;
|
|
37
|
+
height: 3;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#diff-header-meta {
|
|
41
|
+
color: $text-muted;
|
|
42
|
+
text-style: none;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#diff-scroll {
|
|
46
|
+
width: 100%;
|
|
47
|
+
height: 1fr;
|
|
48
|
+
padding: 0 1 0 1;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#diff-scroll > DiffView {
|
|
52
|
+
width: 100%;
|
|
53
|
+
height: auto;
|
|
54
|
+
}
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
path: str,
|
|
60
|
+
before: str | None,
|
|
61
|
+
after: str,
|
|
62
|
+
*,
|
|
63
|
+
name: str | None = None,
|
|
64
|
+
id: str | None = None,
|
|
65
|
+
classes: str | None = None,
|
|
66
|
+
) -> None:
|
|
67
|
+
super().__init__(name=name, id=id, classes=classes)
|
|
68
|
+
self._path = path
|
|
69
|
+
self._before = before
|
|
70
|
+
self._after = after
|
|
71
|
+
|
|
72
|
+
def compose(self) -> ComposeResult:
|
|
73
|
+
filename = self._path.rsplit("/", 1)[-1] or self._path
|
|
74
|
+
meta = "untracked" if self._before is None else "modified"
|
|
75
|
+
yield Static(
|
|
76
|
+
f"[b]📝 {filename}[/b] [#888888]{meta}[/]",
|
|
77
|
+
id="diff-header",
|
|
78
|
+
markup=True,
|
|
79
|
+
)
|
|
80
|
+
with VerticalScroll(id="diff-scroll"):
|
|
81
|
+
yield make_diff(self._path, self._path, self._before, self._after)
|
|
82
|
+
|
|
83
|
+
def on_mount(self) -> None:
|
|
84
|
+
self.title = f"Diff: {self._path}"
|
package/tui/screens/log.py
CHANGED
|
@@ -285,7 +285,7 @@ class LogScreen(ModalScreen[None]):
|
|
|
285
285
|
padding: 0 1;
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
LogScreen
|
|
288
|
+
LogScreen #log-list {
|
|
289
289
|
height: 1fr;
|
|
290
290
|
background: $surface;
|
|
291
291
|
}
|
|
@@ -308,7 +308,8 @@ class LogScreen(ModalScreen[None]):
|
|
|
308
308
|
yield Static(self._header_text(), id="log-header")
|
|
309
309
|
yield VerticalScroll(id="log-list")
|
|
310
310
|
|
|
311
|
-
def on_mount(self) -> None:
|
|
311
|
+
async def on_mount(self) -> None:
|
|
312
|
+
self.query_one("#log-list", VerticalScroll).can_focus = False
|
|
312
313
|
if self._log_path is None:
|
|
313
314
|
self._append_system("No log file yet. Start an agent to begin logging.")
|
|
314
315
|
return
|
|
@@ -318,7 +319,7 @@ class LogScreen(ModalScreen[None]):
|
|
|
318
319
|
)
|
|
319
320
|
self._poll_timer = self.set_interval(0.2, self._poll_new_lines)
|
|
320
321
|
return
|
|
321
|
-
self._begin_tailing()
|
|
322
|
+
await self._begin_tailing()
|
|
322
323
|
|
|
323
324
|
# ── Page helpers ───────────────────────────────────────────────
|
|
324
325
|
|
|
@@ -330,25 +331,24 @@ class LogScreen(ModalScreen[None]):
|
|
|
330
331
|
end = min(start + _PAGE_SIZE, len(self._entries))
|
|
331
332
|
return start, end
|
|
332
333
|
|
|
333
|
-
def _render_page(self) -> None:
|
|
334
|
+
async def _render_page(self) -> None:
|
|
334
335
|
scroller = self.query_one("#log-list", VerticalScroll)
|
|
335
|
-
scroller.remove_children()
|
|
336
|
+
await scroller.remove_children()
|
|
336
337
|
start, end = self._page_range()
|
|
337
338
|
page_entries = self._entries[start:end]
|
|
338
|
-
for tag, raw in page_entries
|
|
339
|
-
scroller.mount(LogEntry(tag, raw))
|
|
339
|
+
await scroller.mount(*[LogEntry(tag, raw) for tag, raw in page_entries])
|
|
340
340
|
self._current_index = 0 if page_entries else -1
|
|
341
341
|
self._apply_selection()
|
|
342
342
|
self._refresh_header()
|
|
343
343
|
|
|
344
|
-
def _navigate_to_page(self, page: int) -> None:
|
|
344
|
+
async def _navigate_to_page(self, page: int) -> None:
|
|
345
345
|
total = self._total_pages()
|
|
346
346
|
self._current_page = max(1, min(page, total))
|
|
347
|
-
self._render_page()
|
|
347
|
+
await self._render_page()
|
|
348
348
|
|
|
349
349
|
# ── File loading ───────────────────────────────────────────────
|
|
350
350
|
|
|
351
|
-
def _begin_tailing(self) -> None:
|
|
351
|
+
async def _begin_tailing(self) -> None:
|
|
352
352
|
"""Read entire log file and start polling."""
|
|
353
353
|
assert self._log_path is not None
|
|
354
354
|
try:
|
|
@@ -360,9 +360,9 @@ class LogScreen(ModalScreen[None]):
|
|
|
360
360
|
return
|
|
361
361
|
self._file_was_present = True
|
|
362
362
|
self._entries = []
|
|
363
|
-
self._ingest(data)
|
|
363
|
+
await self._ingest(data)
|
|
364
364
|
self._current_page = self._total_pages()
|
|
365
|
-
self._render_page()
|
|
365
|
+
await self._render_page()
|
|
366
366
|
if self._poll_timer is None:
|
|
367
367
|
self._poll_timer = self.set_interval(0.2, self._poll_new_lines)
|
|
368
368
|
|
|
@@ -413,14 +413,14 @@ class LogScreen(ModalScreen[None]):
|
|
|
413
413
|
|
|
414
414
|
# ── Polling for new lines ──────────────────────────────────────
|
|
415
415
|
|
|
416
|
-
def _poll_new_lines(self) -> None:
|
|
416
|
+
async def _poll_new_lines(self) -> None:
|
|
417
417
|
if self._log_path is None:
|
|
418
418
|
return
|
|
419
419
|
if not self._log_path.exists():
|
|
420
420
|
return
|
|
421
421
|
if not self._file_was_present:
|
|
422
422
|
self._append_system(f"── log file appeared: {self._log_path} ──")
|
|
423
|
-
self._begin_tailing()
|
|
423
|
+
await self._begin_tailing()
|
|
424
424
|
return
|
|
425
425
|
try:
|
|
426
426
|
size = self._log_path.stat().st_size
|
|
@@ -433,11 +433,11 @@ class LogScreen(ModalScreen[None]):
|
|
|
433
433
|
new_text = f.read()
|
|
434
434
|
self._file_pos = size
|
|
435
435
|
if new_text:
|
|
436
|
-
self._ingest(new_text)
|
|
436
|
+
await self._ingest(new_text)
|
|
437
437
|
except OSError:
|
|
438
438
|
pass
|
|
439
439
|
|
|
440
|
-
def _ingest(self, chunk: str) -> None:
|
|
440
|
+
async def _ingest(self, chunk: str) -> None:
|
|
441
441
|
was_on_last_page = (
|
|
442
442
|
len(self._entries) == 0
|
|
443
443
|
or self._current_page == self._total_pages()
|
|
@@ -466,7 +466,7 @@ class LogScreen(ModalScreen[None]):
|
|
|
466
466
|
self._entries.append(("error", line))
|
|
467
467
|
if was_on_last_page:
|
|
468
468
|
self._current_page = self._total_pages()
|
|
469
|
-
self._render_page()
|
|
469
|
+
await self._render_page()
|
|
470
470
|
else:
|
|
471
471
|
self._refresh_header()
|
|
472
472
|
|
|
@@ -479,19 +479,19 @@ class LogScreen(ModalScreen[None]):
|
|
|
479
479
|
return child
|
|
480
480
|
return None
|
|
481
481
|
|
|
482
|
-
def action_next_page(self) -> None:
|
|
482
|
+
async def action_next_page(self) -> None:
|
|
483
483
|
if self._current_page < self._total_pages():
|
|
484
|
-
self._navigate_to_page(self._current_page + 1)
|
|
484
|
+
await self._navigate_to_page(self._current_page + 1)
|
|
485
485
|
|
|
486
|
-
def action_prev_page(self) -> None:
|
|
486
|
+
async def action_prev_page(self) -> None:
|
|
487
487
|
if self._current_page > 1:
|
|
488
|
-
self._navigate_to_page(self._current_page - 1)
|
|
488
|
+
await self._navigate_to_page(self._current_page - 1)
|
|
489
489
|
|
|
490
|
-
def action_first_page(self) -> None:
|
|
491
|
-
self._navigate_to_page(1)
|
|
490
|
+
async def action_first_page(self) -> None:
|
|
491
|
+
await self._navigate_to_page(1)
|
|
492
492
|
|
|
493
|
-
def action_last_page(self) -> None:
|
|
494
|
-
self._navigate_to_page(self._total_pages())
|
|
493
|
+
async def action_last_page(self) -> None:
|
|
494
|
+
await self._navigate_to_page(self._total_pages())
|
|
495
495
|
|
|
496
496
|
def action_next_entry(self) -> None:
|
|
497
497
|
scroller = self.query_one("#log-list", VerticalScroll)
|