@scemoon/cdh 1.0.3 → 1.0.5
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 +14 -3
- 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 +462 -49
- 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/tui/__pycache__/about.cpython-314.pyc +0 -0
- package/tui/__pycache__/agents.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__/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/agents.py +8 -7
- package/tui/app.py +11 -33
- 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/messages.py +2 -0
- package/tui/path_complete.py +1 -1
- package/tui/paths.py +16 -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/screens/store.py +1 -1
- package/tui/session_tracker.py +3 -0
- package/tui/settings_schema.py +2 -2
- package/tui/visuals/__pycache__/columns.cpython-314.pyc +0 -0
- package/tui/visuals/columns.py +1 -2
- 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__/load_more.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__/session_grid_select.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 +173 -105
- 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/cdh/__pycache__/__init__.cpython-313.pyc +0 -0
- package/cdh/__pycache__/__main__.cpython-314.pyc +0 -0
- package/cdh/__pycache__/scaffold.cpython-313.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/codebase/__pycache__/config.cpython-314.pyc +0 -0
- package/onecode/memory/offload.py +0 -114
- package/onecode/memory/symbolic.py +0 -164
- package/onecode/models/providers/__pycache__/mock_provider.cpython-314.pyc +0 -0
- package/onecode/tasks/__pycache__/__init__.cpython-314.pyc +0 -0
- package/onecode/tasks/__pycache__/manager.cpython-314.pyc +0 -0
- package/onecode/tasks/__pycache__/models.cpython-314.pyc +0 -0
- package/tui/__pycache__/cli.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/settings.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/conversation.cpython-313.pyc +0 -0
- package/tui/widgets/__pycache__/terminal.cpython-313.pyc +0 -0
- package/tui/widgets/__pycache__/tool_call.cpython-313.pyc +0 -0
|
@@ -5,11 +5,18 @@ import subprocess
|
|
|
5
5
|
|
|
6
6
|
from textual import work
|
|
7
7
|
from textual.app import ComposeResult
|
|
8
|
+
from textual.binding import Binding
|
|
9
|
+
from textual.message import Message
|
|
8
10
|
from textual.reactive import reactive
|
|
9
11
|
from textual.timer import Timer
|
|
12
|
+
from textual.widget import Widget
|
|
10
13
|
from textual.widgets import Static
|
|
11
14
|
from textual import containers
|
|
12
15
|
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
CONTENT_WIDTH = 35
|
|
13
20
|
|
|
14
21
|
GIT_STATUS_TIMEOUT = 5.0
|
|
15
22
|
GIT_STATUS_DEBOUNCE = 0.2
|
|
@@ -33,53 +40,62 @@ IGNORED_DIRS = frozenset(
|
|
|
33
40
|
}
|
|
34
41
|
)
|
|
35
42
|
|
|
43
|
+
STATUS_ICONS = {"?": "?", "M": "M", "A": "A", "D": "D", "R": "R", "C": "C"}
|
|
44
|
+
STATUS_COLORS = {"?": "green", "M": "yellow", "A": "green", "D": "red", "R": "#888888", "C": "green"}
|
|
45
|
+
DIFF_COLORS: dict[str, str] = {"add": "green", "del": "red", "mix": "yellow", "dim": "#555555"}
|
|
36
46
|
|
|
37
|
-
class ModifiedFiles(containers.Vertical):
|
|
38
|
-
DEFAULT_CSS = """
|
|
39
|
-
ModifiedFiles {
|
|
40
|
-
height: auto;
|
|
41
|
-
max-height: 15;
|
|
42
|
-
overflow-y: auto;
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
def _status_info(raw: str) -> tuple[str, str]:
|
|
49
|
+
ch = raw.strip()[:1] or "M"
|
|
50
|
+
return STATUS_ICONS.get(ch, "M"), STATUS_COLORS.get(ch, "yellow")
|
|
47
51
|
|
|
48
|
-
.file-added {
|
|
49
|
-
color: $text-success;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.file-modified {
|
|
53
|
-
color: $text-warning;
|
|
54
|
-
}
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
def _tag_color(added: int, deleted: int) -> str:
|
|
54
|
+
if added and deleted:
|
|
55
|
+
return DIFF_COLORS["mix"]
|
|
56
|
+
if added:
|
|
57
|
+
return DIFF_COLORS["add"]
|
|
58
|
+
if deleted:
|
|
59
|
+
return DIFF_COLORS["del"]
|
|
60
|
+
return DIFF_COLORS["dim"]
|
|
59
61
|
|
|
60
|
-
.file-renamed {
|
|
61
|
-
color: $text-secondary;
|
|
62
|
-
}
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
class ModifiedFiles(containers.Vertical, can_focus=True):
|
|
64
|
+
DEFAULT_CSS = """
|
|
65
|
+
ModifiedFiles {
|
|
66
|
+
height: auto;
|
|
67
|
+
overflow-y: auto;
|
|
68
|
+
padding: 0 0 0 0;
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
text-style: italic;
|
|
70
|
+
&:focus {
|
|
71
|
+
background-tint: $surface 10%;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
#mf-files {
|
|
75
|
+
height: auto;
|
|
76
|
+
padding: 0 0 0 1;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
"""
|
|
80
80
|
|
|
81
|
+
BINDINGS = [
|
|
82
|
+
Binding("up", "cursor_up", "Up", group="Files"),
|
|
83
|
+
Binding("down", "cursor_down", "Down", group="Files"),
|
|
84
|
+
Binding("enter", "select", "Select"),
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
@dataclass
|
|
88
|
+
class FileSelected(Message):
|
|
89
|
+
filepath: str
|
|
90
|
+
modified_files: "ModifiedFiles"
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def control(self) -> Widget:
|
|
94
|
+
return self.modified_files
|
|
95
|
+
|
|
81
96
|
path: reactive[Path | None] = reactive(None)
|
|
82
97
|
files: reactive[list[str]] = reactive(list, recompose=False)
|
|
98
|
+
highlighted: reactive[int | None] = reactive(None)
|
|
83
99
|
|
|
84
100
|
def __init__(
|
|
85
101
|
self,
|
|
@@ -92,8 +108,9 @@ class ModifiedFiles(containers.Vertical):
|
|
|
92
108
|
self._debounce_timer: Timer | None = None
|
|
93
109
|
self._status_kind: str = "no-changes"
|
|
94
110
|
self._status_text: str = NO_CHANGES_TEXT
|
|
95
|
-
self._file_widgets: list[Static] = []
|
|
96
111
|
self._initial_path: Path | None = path
|
|
112
|
+
self._filepaths: list[str] = []
|
|
113
|
+
self._lines_data: tuple[list[str], dict[str, tuple[int, int]]] | None = None
|
|
97
114
|
|
|
98
115
|
def on_mount(self) -> None:
|
|
99
116
|
initial = self._initial_path
|
|
@@ -102,21 +119,13 @@ class ModifiedFiles(containers.Vertical):
|
|
|
102
119
|
self.path = initial
|
|
103
120
|
|
|
104
121
|
def watch_path(self, path: Path | None) -> None:
|
|
105
|
-
"""Schedule a (debounced, async) git status when path changes."""
|
|
106
122
|
self._cancel_debounce()
|
|
107
|
-
|
|
108
|
-
self._show_status("no-changes", NO_CHANGES_TEXT)
|
|
109
|
-
self._clear_file_widgets()
|
|
110
|
-
return
|
|
111
|
-
self._show_status("-loading", LOADING_TEXT)
|
|
112
|
-
self._schedule_run(path)
|
|
123
|
+
self._schedule_run(path or Path.cwd())
|
|
113
124
|
|
|
114
125
|
def refresh_files(self) -> None:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
self._show_status("-loading", LOADING_TEXT)
|
|
119
|
-
self._run_git_status(self.path)
|
|
126
|
+
target = self.path or Path.cwd()
|
|
127
|
+
self._cancel_debounce()
|
|
128
|
+
self._run_git_status(target)
|
|
120
129
|
|
|
121
130
|
def _schedule_run(self, path: Path) -> None:
|
|
122
131
|
timer = self.set_timer(
|
|
@@ -136,8 +145,6 @@ class ModifiedFiles(containers.Vertical):
|
|
|
136
145
|
|
|
137
146
|
@work(exclusive=True, group="modified-files-git-status", thread=True)
|
|
138
147
|
def _run_git_status(self, path: Path) -> None:
|
|
139
|
-
"""Worker entry: do the slow ``git status`` off the UI thread,
|
|
140
|
-
then marshal results back via ``app.call_from_thread``."""
|
|
141
148
|
app = self.app
|
|
142
149
|
outcome = self._do_git_status(path)
|
|
143
150
|
if outcome is None:
|
|
@@ -145,24 +152,18 @@ class ModifiedFiles(containers.Vertical):
|
|
|
145
152
|
kind, text, lines = outcome
|
|
146
153
|
if lines is None:
|
|
147
154
|
app.call_from_thread(self._show_status, kind, text)
|
|
148
|
-
app.call_from_thread(self._clear_file_widgets)
|
|
149
155
|
else:
|
|
150
156
|
app.call_from_thread(self._render_lines, lines)
|
|
151
157
|
|
|
152
158
|
def _do_git_status(
|
|
153
159
|
self, path: Path
|
|
154
160
|
) -> tuple[str, str, list[str] | None] | None:
|
|
155
|
-
"""Pure helper for ``git status``. Returns
|
|
156
|
-
``(kind, text, lines_or_None)`` for the UI to render, or ``None``
|
|
157
|
-
if the call should silently no-op (e.g. ignored path).
|
|
158
|
-
"""
|
|
159
161
|
if any(part in IGNORED_DIRS for part in path.parts):
|
|
160
162
|
return ("not-a-repo", NOT_A_REPO_TEXT, None)
|
|
161
163
|
try:
|
|
162
|
-
|
|
164
|
+
status = subprocess.run(
|
|
163
165
|
["git", "-C", str(path), "status", "--porcelain"],
|
|
164
|
-
capture_output=True,
|
|
165
|
-
text=True,
|
|
166
|
+
capture_output=True, text=True,
|
|
166
167
|
timeout=GIT_STATUS_TIMEOUT,
|
|
167
168
|
)
|
|
168
169
|
except subprocess.TimeoutExpired:
|
|
@@ -171,68 +172,135 @@ class ModifiedFiles(containers.Vertical):
|
|
|
171
172
|
return ("not-a-repo", NOT_A_REPO_TEXT, None)
|
|
172
173
|
except Exception:
|
|
173
174
|
return ("no-changes", NO_CHANGES_TEXT, None)
|
|
174
|
-
if
|
|
175
|
+
if status.returncode != 0:
|
|
175
176
|
return ("not-a-repo", NOT_A_REPO_TEXT, None)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
]
|
|
177
|
+
|
|
178
|
+
lines = [l for l in status.stdout.splitlines() if l.strip()]
|
|
179
179
|
if not lines:
|
|
180
|
-
return ("no-changes", NO_CHANGES_TEXT, [])
|
|
181
|
-
|
|
180
|
+
return ("no-changes", NO_CHANGES_TEXT, ([], {}))
|
|
181
|
+
|
|
182
|
+
diffmap: dict[str, tuple[int, int]] = {}
|
|
183
|
+
for diff_cmd in (
|
|
184
|
+
["git", "-C", str(path), "diff", "--numstat"],
|
|
185
|
+
["git", "-C", str(path), "diff", "--cached", "--numstat"],
|
|
186
|
+
):
|
|
187
|
+
try:
|
|
188
|
+
r = subprocess.run(
|
|
189
|
+
diff_cmd, capture_output=True, text=True,
|
|
190
|
+
timeout=GIT_STATUS_TIMEOUT,
|
|
191
|
+
)
|
|
192
|
+
except Exception:
|
|
193
|
+
continue
|
|
194
|
+
for dl in r.stdout.splitlines():
|
|
195
|
+
parts = dl.split("\t", 2)
|
|
196
|
+
if len(parts) == 3:
|
|
197
|
+
added, deleted = parts[0], parts[1]
|
|
198
|
+
if added != "-":
|
|
199
|
+
a, d = int(added), int(deleted)
|
|
200
|
+
fp = parts[2]
|
|
201
|
+
prev_a, prev_d = diffmap.get(fp, (0, 0))
|
|
202
|
+
diffmap[fp] = (prev_a + a, prev_d + d)
|
|
203
|
+
|
|
204
|
+
return ("file-modified", "", (lines, diffmap))
|
|
182
205
|
|
|
183
|
-
def _render_lines(self,
|
|
206
|
+
def _render_lines(self, data: tuple[list[str], dict[str, tuple[int, int]]]) -> None:
|
|
207
|
+
lines, diffmap = data
|
|
184
208
|
self.files = list(lines)
|
|
209
|
+
self._filepaths = [l[3:] for l in lines]
|
|
185
210
|
if not lines:
|
|
186
211
|
self._show_status("no-changes", NO_CHANGES_TEXT)
|
|
187
|
-
self.
|
|
212
|
+
self._lines_data = None
|
|
213
|
+
self.highlighted = None
|
|
188
214
|
return
|
|
189
215
|
self._show_status("file-modified", "")
|
|
190
|
-
self.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
216
|
+
self._lines_data = data
|
|
217
|
+
if self.highlighted is None or self.highlighted >= len(self._filepaths):
|
|
218
|
+
self.highlighted = 0
|
|
219
|
+
self._apply_rendering()
|
|
220
|
+
|
|
221
|
+
def on_focus(self) -> None:
|
|
222
|
+
if self.highlighted is None and self._filepaths:
|
|
223
|
+
self.highlighted = 0
|
|
224
|
+
|
|
225
|
+
def watch_highlighted(
|
|
226
|
+
self, old: int | None, highlighted: int | None
|
|
227
|
+
) -> None:
|
|
228
|
+
if self._lines_data is not None:
|
|
229
|
+
self._apply_rendering()
|
|
230
|
+
|
|
231
|
+
def _apply_rendering(self) -> None:
|
|
232
|
+
assert self._lines_data is not None
|
|
233
|
+
lines, diffmap = self._lines_data
|
|
234
|
+
out: list[str] = []
|
|
235
|
+
for idx, raw_line in enumerate(lines):
|
|
236
|
+
filepath = raw_line[3:]
|
|
237
|
+
icon, sc = _status_info(raw_line[:2])
|
|
238
|
+
|
|
239
|
+
added, deleted = diffmap.get(filepath, (0, 0))
|
|
240
|
+
if "?" in raw_line[:2]:
|
|
241
|
+
tag = "new"
|
|
242
|
+
tc = DIFF_COLORS["dim"]
|
|
243
|
+
elif "D" in raw_line[:2]:
|
|
244
|
+
tag = "del"
|
|
245
|
+
tc = DIFF_COLORS["del"]
|
|
246
|
+
elif added or deleted:
|
|
247
|
+
tag = f"+{added}/-{deleted}"
|
|
248
|
+
tc = _tag_color(added, deleted)
|
|
249
|
+
else:
|
|
250
|
+
tag = ""
|
|
251
|
+
tc = ""
|
|
252
|
+
|
|
253
|
+
tag_s = f" [{tc}]{tag}[/]" if tag else ""
|
|
254
|
+
tag_len = len(tag) + 1 if tag else 0
|
|
255
|
+
max_fn = CONTENT_WIDTH - 2 - tag_len
|
|
256
|
+
if max_fn < 3:
|
|
257
|
+
max_fn = 3
|
|
258
|
+
disp = filepath[:max_fn - 1] + "…" if len(filepath) > max_fn else filepath
|
|
259
|
+
pad = CONTENT_WIDTH - 2 - len(disp) - tag_len
|
|
260
|
+
pad_s = " " * pad if pad > 0 else ""
|
|
261
|
+
line = f"[{sc}]{icon} {disp}[/]{pad_s}{tag_s}"
|
|
262
|
+
|
|
263
|
+
if idx == self.highlighted:
|
|
264
|
+
line = f"[reverse]{line}[/]"
|
|
265
|
+
out.append(line)
|
|
266
|
+
|
|
267
|
+
files_widget = self.query_one("#mf-files", Static)
|
|
268
|
+
files_widget.update("\n".join(out))
|
|
269
|
+
self.call_after_refresh(self._scroll_to_highlighted)
|
|
270
|
+
|
|
271
|
+
def _scroll_to_highlighted(self) -> None:
|
|
272
|
+
if self.highlighted is not None:
|
|
273
|
+
self.scroll_to(y=self.highlighted, animate=False)
|
|
274
|
+
|
|
275
|
+
def action_cursor_up(self) -> None:
|
|
276
|
+
if not self._filepaths:
|
|
194
277
|
return
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
278
|
+
if self.highlighted is None or self.highlighted <= 0:
|
|
279
|
+
self.highlighted = 0
|
|
280
|
+
else:
|
|
281
|
+
self.highlighted -= 1
|
|
282
|
+
|
|
283
|
+
def action_cursor_down(self) -> None:
|
|
284
|
+
if not self._filepaths:
|
|
285
|
+
return
|
|
286
|
+
if self.highlighted is None:
|
|
287
|
+
self.highlighted = 0
|
|
288
|
+
elif self.highlighted >= len(self._filepaths) - 1:
|
|
289
|
+
self.highlighted = len(self._filepaths) - 1
|
|
290
|
+
else:
|
|
291
|
+
self.highlighted += 1
|
|
292
|
+
|
|
293
|
+
def action_select(self) -> None:
|
|
294
|
+
if self.highlighted is not None and self._filepaths:
|
|
295
|
+
filepath = self._filepaths[self.highlighted]
|
|
296
|
+
self.post_message(self.FileSelected(filepath, self))
|
|
202
297
|
|
|
203
298
|
def _show_status(self, kind: str, text: str) -> None:
|
|
204
299
|
self._status_kind = kind
|
|
205
300
|
self._status_text = text
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
except Exception:
|
|
209
|
-
return
|
|
210
|
-
header.set_classes(kind)
|
|
211
|
-
header.update(text)
|
|
212
|
-
if kind != "file-modified":
|
|
213
|
-
self._clear_file_widgets()
|
|
214
|
-
|
|
215
|
-
def _clear_file_widgets(self) -> None:
|
|
216
|
-
for w in self._file_widgets:
|
|
217
|
-
try:
|
|
218
|
-
w.remove()
|
|
219
|
-
except Exception:
|
|
220
|
-
pass
|
|
221
|
-
self._file_widgets = []
|
|
301
|
+
status = self.query_one("#mf-status", Static)
|
|
302
|
+
status.update(text)
|
|
222
303
|
|
|
223
304
|
def compose(self) -> ComposeResult:
|
|
224
|
-
yield Static(self._status_text,
|
|
225
|
-
|
|
226
|
-
@staticmethod
|
|
227
|
-
def _class_for_status(status: str) -> str:
|
|
228
|
-
if "?" in status:
|
|
229
|
-
return "file-added"
|
|
230
|
-
if "D" in status:
|
|
231
|
-
return "file-deleted"
|
|
232
|
-
if "A" in status or "C" in status:
|
|
233
|
-
return "file-added"
|
|
234
|
-
if "R" in status:
|
|
235
|
-
return "file-renamed"
|
|
236
|
-
if "M" in status:
|
|
237
|
-
return "file-modified"
|
|
238
|
-
return "file-modified"
|
|
305
|
+
yield Static(self._status_text, id="mf-status")
|
|
306
|
+
yield Static("", id="mf-files")
|
package/tui/widgets/plan.py
CHANGED
|
@@ -7,7 +7,6 @@ from textual import getters
|
|
|
7
7
|
from tui.app import A2TUIApp
|
|
8
8
|
from tui.widgets.grid_select import GridSelect
|
|
9
9
|
from tui.widgets.project_summary import ProjectSummary
|
|
10
|
-
from onecode.config import CLOUD_DEV_HARNESS_DIR
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
def _read_project_path(pf: Path) -> str:
|
|
@@ -47,11 +46,19 @@ class ProjectGridSelect(GridSelect):
|
|
|
47
46
|
return True
|
|
48
47
|
|
|
49
48
|
def compose(self) -> ComposeResult:
|
|
50
|
-
projects_dir =
|
|
49
|
+
projects_dir = Path.home() / ".cdh" / "projects"
|
|
51
50
|
projects_dir.mkdir(parents=True, exist_ok=True)
|
|
52
51
|
project_files = list(projects_dir.glob("*.yaml")) + list(projects_dir.glob("*.json"))
|
|
53
52
|
for pf in sorted(project_files):
|
|
54
53
|
yield ProjectSummary(pf.stem, _read_project_path(pf), id=pf.stem)
|
|
55
54
|
|
|
55
|
+
async def reload(self) -> None:
|
|
56
|
+
await self.remove_children()
|
|
57
|
+
projects_dir = Path.home() / ".cdh" / "projects"
|
|
58
|
+
projects_dir.mkdir(parents=True, exist_ok=True)
|
|
59
|
+
project_files = list(projects_dir.glob("*.yaml")) + list(projects_dir.glob("*.json"))
|
|
60
|
+
for pf in sorted(project_files):
|
|
61
|
+
await self.mount(ProjectSummary(pf.stem, _read_project_path(pf), id=pf.stem))
|
|
62
|
+
|
|
56
63
|
def refresh(self, **kwargs) -> None:
|
|
57
64
|
super().refresh(**kwargs)
|
|
@@ -111,6 +111,12 @@ class SessionGridSelect(GridSelect):
|
|
|
111
111
|
break
|
|
112
112
|
return
|
|
113
113
|
|
|
114
|
+
for i, existing in enumerate(self._all_sessions):
|
|
115
|
+
if existing.mode_name == mode_name:
|
|
116
|
+
self._all_sessions[i] = details
|
|
117
|
+
self._render_page()
|
|
118
|
+
return
|
|
119
|
+
|
|
114
120
|
if details.session_pk is not None:
|
|
115
121
|
for i, existing in enumerate(self._all_sessions):
|
|
116
122
|
if existing.session_pk == details.session_pk:
|
package/tui/widgets/side_bar.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
|
|
3
3
|
from textual.app import ComposeResult
|
|
4
|
+
from textual.binding import Binding
|
|
4
5
|
from textual.widget import Widget
|
|
5
6
|
from textual import containers
|
|
6
7
|
from textual import widgets
|
|
@@ -9,6 +10,9 @@ from textual.message import Message
|
|
|
9
10
|
|
|
10
11
|
class SideBarCollapsible(widgets.Collapsible):
|
|
11
12
|
BINDING_GROUP_TITLE = "Sidebar collapsible"
|
|
13
|
+
BINDINGS = [
|
|
14
|
+
Binding("down", "focus_content", "Focus content", show=False),
|
|
15
|
+
]
|
|
12
16
|
HELP = """\
|
|
13
17
|
## Sidebar
|
|
14
18
|
|
|
@@ -20,6 +24,12 @@ The Sidebar contains additonal information associated with the conversation.
|
|
|
20
24
|
- **enter** expand or collapse secions
|
|
21
25
|
"""
|
|
22
26
|
|
|
27
|
+
def action_focus_content(self) -> None:
|
|
28
|
+
"""Move focus into the content area when expanded."""
|
|
29
|
+
if self.collapsed:
|
|
30
|
+
return
|
|
31
|
+
self.screen.focus_next()
|
|
32
|
+
|
|
23
33
|
|
|
24
34
|
class SideBar(containers.Vertical):
|
|
25
35
|
BINDING_GROUP_TITLE = "Sidebar"
|