@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
package/onecode/config.py
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import os
|
|
2
3
|
from dataclasses import dataclass, field
|
|
3
4
|
from pathlib import Path
|
|
4
|
-
from typing import
|
|
5
|
+
from typing import Optional
|
|
5
6
|
|
|
6
7
|
import yaml
|
|
7
8
|
|
|
9
|
+
logger = logging.getLogger("onecode.config")
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
|
|
12
|
+
ONECODE_DIR = Path.home() / ".onecode"
|
|
13
|
+
GLOBAL_CONFIG_PATH = ONECODE_DIR / "onecode.config.yaml"
|
|
11
14
|
|
|
12
15
|
|
|
13
16
|
@dataclass
|
|
@@ -22,7 +25,7 @@ class ObservabilityConfig:
|
|
|
22
25
|
trace_enabled: bool = True
|
|
23
26
|
trace_exporter: str = "file"
|
|
24
27
|
otlp_endpoint: str = "http://localhost:4317"
|
|
25
|
-
trace_dir: str = "~/.
|
|
28
|
+
trace_dir: str = "~/.onecode/traces"
|
|
26
29
|
|
|
27
30
|
|
|
28
31
|
@dataclass
|
|
@@ -86,6 +89,13 @@ class CodebaseConfig:
|
|
|
86
89
|
])
|
|
87
90
|
|
|
88
91
|
|
|
92
|
+
@dataclass
|
|
93
|
+
class MemoryConfig:
|
|
94
|
+
enabled: bool = True
|
|
95
|
+
auto_recall: bool = True
|
|
96
|
+
top_k: int = 5
|
|
97
|
+
|
|
98
|
+
|
|
89
99
|
@dataclass
|
|
90
100
|
class GlobalConfig:
|
|
91
101
|
default_mode: str = "build"
|
|
@@ -102,6 +112,7 @@ class GlobalConfig:
|
|
|
102
112
|
log_level: str = "info"
|
|
103
113
|
session_auto_save: bool = True
|
|
104
114
|
codebase: CodebaseConfig = field(default_factory=CodebaseConfig)
|
|
115
|
+
memory: MemoryConfig = field(default_factory=MemoryConfig)
|
|
105
116
|
|
|
106
117
|
|
|
107
118
|
def _dict_to_dataclass(cls, data: dict):
|
|
@@ -133,22 +144,40 @@ def _dict_to_dataclass(cls, data: dict):
|
|
|
133
144
|
|
|
134
145
|
def ensure_dirs():
|
|
135
146
|
dirs = [
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
ONECODE_DIR,
|
|
148
|
+
ONECODE_DIR / "sessions",
|
|
149
|
+
ONECODE_DIR / "skills",
|
|
150
|
+
ONECODE_DIR / "mcps",
|
|
151
|
+
ONECODE_DIR / "traces",
|
|
152
|
+
ONECODE_DIR / "logs",
|
|
153
|
+
ONECODE_DIR / "models",
|
|
154
|
+
ONECODE_DIR / "codebase" / "indexes",
|
|
155
|
+
ONECODE_DIR / "memory",
|
|
143
156
|
]
|
|
144
157
|
for d in dirs:
|
|
145
158
|
d.mkdir(parents=True, exist_ok=True)
|
|
146
159
|
|
|
160
|
+
# One-time migration from legacy ~/.cdh/ to ~/.onecode/
|
|
161
|
+
try:
|
|
162
|
+
from onecode.migrate import migrate_legacy_cdh_to_onecode
|
|
163
|
+
result = migrate_legacy_cdh_to_onecode()
|
|
164
|
+
if result:
|
|
165
|
+
logger.warning("Legacy migration: %s", result)
|
|
166
|
+
except Exception as exc:
|
|
167
|
+
logger.warning("Legacy migration failed: %s", exc)
|
|
168
|
+
|
|
147
169
|
|
|
148
170
|
def load_config() -> GlobalConfig:
|
|
149
171
|
ensure_dirs()
|
|
150
172
|
if not GLOBAL_CONFIG_PATH.exists():
|
|
151
173
|
_write_default_config()
|
|
174
|
+
# Bootstrap ai-dlc-skill from source into cdh platform pool
|
|
175
|
+
try:
|
|
176
|
+
from onecode.skills.bootstrap import ensure_ai_dlc_skill, ensure_onecode_default_skills
|
|
177
|
+
ensure_onecode_default_skills()
|
|
178
|
+
ensure_ai_dlc_skill()
|
|
179
|
+
except Exception:
|
|
180
|
+
pass
|
|
152
181
|
raw = yaml.safe_load(GLOBAL_CONFIG_PATH.read_text()) or {}
|
|
153
182
|
return _dict_to_dataclass(GlobalConfig, raw)
|
|
154
183
|
|
|
@@ -201,7 +230,7 @@ def _write_default_config():
|
|
|
201
230
|
"trace_enabled": True,
|
|
202
231
|
"trace_exporter": "file",
|
|
203
232
|
"otlp_endpoint": "http://localhost:4317",
|
|
204
|
-
"trace_dir": "~/.
|
|
233
|
+
"trace_dir": "~/.onecode/traces",
|
|
205
234
|
},
|
|
206
235
|
"attachments": {
|
|
207
236
|
"max_size_mb": 10,
|
package/onecode/config_screen.py
CHANGED
|
@@ -7,7 +7,7 @@ from textual.containers import Horizontal, Vertical
|
|
|
7
7
|
from textual.reactive import var
|
|
8
8
|
from textual.screen import ModalScreen
|
|
9
9
|
from textual.widget import Widget
|
|
10
|
-
from textual.widgets import Button, Input, Label,
|
|
10
|
+
from textual.widgets import Button, Input, Label, Static
|
|
11
11
|
|
|
12
12
|
from onecode.config import GlobalConfig, load_config, resolve_env, save_config
|
|
13
13
|
|
|
@@ -143,7 +143,7 @@ class EditFieldScreen(ModalScreen[str]):
|
|
|
143
143
|
EditFieldScreen { background: rgba(0,0,0,0.7); align: center middle; }
|
|
144
144
|
#edit-dialog { width: 50; height: 7; background: #111; border: solid #555; }
|
|
145
145
|
#edit-label { height: 1; background: #333; color: #fff; padding: 0 1; }
|
|
146
|
-
#edit-input { height: 3; padding: 0 1; }
|
|
146
|
+
#edit-input { height: 3; padding: 0 1; background: #111; color: #fff; }
|
|
147
147
|
#edit-buttons { height: 3; background: #222; align: center middle; }
|
|
148
148
|
Button { margin: 0 1; background: #444; color: #fff; }
|
|
149
149
|
Button:hover { background: #666; }
|
|
@@ -158,6 +158,11 @@ class EditFieldScreen(ModalScreen[str]):
|
|
|
158
158
|
yield Button("Cancel", id="edit-cancel")
|
|
159
159
|
yield Button("Save", id="edit-save")
|
|
160
160
|
|
|
161
|
+
def on_mount(self) -> None:
|
|
162
|
+
inp = self.query_one("#edit-input", Input)
|
|
163
|
+
inp.selection_color = "#000"
|
|
164
|
+
inp.selection_background = "#fff"
|
|
165
|
+
|
|
161
166
|
@on(Button.Pressed, "#edit-save")
|
|
162
167
|
def on_save(self) -> None:
|
|
163
168
|
value = self.query_one("#edit-input", Input).value.strip()
|
|
@@ -333,7 +338,6 @@ class ConfigScreen(App):
|
|
|
333
338
|
self._section_fields.append(ConfigItem("log_level", "Log", cfg.log_level))
|
|
334
339
|
case "providers":
|
|
335
340
|
for pid, pcfg in cfg.providers.items():
|
|
336
|
-
n_models = len(pcfg.models)
|
|
337
341
|
ak = _truncate(_display_val(pcfg.api_key), 24)
|
|
338
342
|
ep = _truncate(_display_val(pcfg.endpoint), 20)
|
|
339
343
|
status = f"ep:{ep} ak:{ak}"
|
|
Binary file
|
package/onecode/mcp/manager.py
CHANGED
|
@@ -3,13 +3,12 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import json
|
|
5
5
|
import logging
|
|
6
|
-
from pathlib import Path
|
|
7
6
|
from typing import Any, Optional
|
|
8
7
|
|
|
9
8
|
import httpx
|
|
10
9
|
import yaml
|
|
11
10
|
|
|
12
|
-
from onecode.config import
|
|
11
|
+
from onecode.config import ONECODE_DIR
|
|
13
12
|
from onecode.mcp.client import MCPClient, MCPTool
|
|
14
13
|
|
|
15
14
|
logger = logging.getLogger("onecode.mcp")
|
|
@@ -83,7 +82,7 @@ class MCPSSEClient:
|
|
|
83
82
|
|
|
84
83
|
class MCPManager:
|
|
85
84
|
def __init__(self):
|
|
86
|
-
self.config_dir =
|
|
85
|
+
self.config_dir = ONECODE_DIR / "mcps"
|
|
87
86
|
self.config_dir.mkdir(parents=True, exist_ok=True)
|
|
88
87
|
self.config_path = self.config_dir / "mcps.yaml"
|
|
89
88
|
self._data: dict = {}
|
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
"""
|
|
2
|
-
CDH Memory -
|
|
2
|
+
CDH Memory - Long-term Conversation Memory for AI Agents
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
- L0: Raw conversation
|
|
6
|
-
- L1: Atomic facts
|
|
7
|
-
- L2: Scenarios
|
|
8
|
-
- L3: Personas
|
|
9
|
-
|
|
10
|
-
With symbolic short-term memory (Mermaid canvas + context offloading)
|
|
4
|
+
L0: Raw conversation storage with BM25 keyword recall.
|
|
11
5
|
"""
|
|
12
|
-
|
|
13
6
|
from onecode.memory.pyramid import MemoryPyramid, MemoryLayer, MemoryEntry
|
|
14
|
-
from onecode.memory.symbolic import SymbolicMemory, MermaidCanvas, NodeType
|
|
15
|
-
from onecode.memory.offload import ContextOffloader, OffloadConfig, ToolCallExtractor
|
|
16
7
|
from onecode.memory.recall import HybridRecall, BM25, RecallResult
|
|
17
8
|
from onecode.memory.backend import MemoryBackend
|
|
18
9
|
|
|
@@ -20,32 +11,25 @@ __all__ = [
|
|
|
20
11
|
"MemoryPyramid",
|
|
21
12
|
"MemoryLayer",
|
|
22
13
|
"MemoryEntry",
|
|
23
|
-
"SymbolicMemory",
|
|
24
|
-
"MermaidCanvas",
|
|
25
|
-
"NodeType",
|
|
26
|
-
"ContextOffloader",
|
|
27
|
-
"OffloadConfig",
|
|
28
|
-
"ToolCallExtractor",
|
|
29
14
|
"HybridRecall",
|
|
30
15
|
"BM25",
|
|
31
16
|
"RecallResult",
|
|
32
17
|
"MemoryBackend",
|
|
18
|
+
"AgentMemory",
|
|
33
19
|
]
|
|
34
20
|
|
|
35
21
|
|
|
36
22
|
class AgentMemory:
|
|
37
23
|
"""
|
|
38
|
-
Unified memory interface combining
|
|
39
|
-
with
|
|
24
|
+
Unified memory interface combining persistent L0 conversation storage
|
|
25
|
+
with BM25 keyword recall for long-term context.
|
|
40
26
|
"""
|
|
41
27
|
|
|
42
28
|
def __init__(self, storage_path=None):
|
|
43
|
-
from onecode.config import
|
|
44
|
-
self.storage_path = storage_path or (
|
|
29
|
+
from onecode.config import ONECODE_DIR
|
|
30
|
+
self.storage_path = storage_path or (ONECODE_DIR / "memory")
|
|
45
31
|
self.backend = MemoryBackend(self.storage_path / "memory.db")
|
|
46
32
|
self.pyramid = MemoryPyramid(self.storage_path)
|
|
47
|
-
self.symbolic = SymbolicMemory()
|
|
48
|
-
self.offloader = ContextOffloader(self.storage_path / "refs")
|
|
49
33
|
self.recall = HybridRecall()
|
|
50
34
|
|
|
51
35
|
def remember(self, layer: MemoryLayer, content: str, metadata: dict = None, parent_id: str = None) -> MemoryEntry:
|
|
@@ -63,33 +47,3 @@ class AgentMemory:
|
|
|
63
47
|
|
|
64
48
|
def search_memories(self, query: str, top_k: int = 5) -> list[RecallResult]:
|
|
65
49
|
return self.recall.hybrid_recall(query, top_k)
|
|
66
|
-
|
|
67
|
-
def remember_conversation(self, conversation_id: str, messages: list[dict]) -> MemoryEntry:
|
|
68
|
-
content = "\n".join(f"{m.get('role', 'user')}: {m.get('content', '')}" for m in messages)
|
|
69
|
-
entry = self.remember(MemoryLayer.L0_CONVERSATION, content, {"conversation_id": conversation_id})
|
|
70
|
-
return entry
|
|
71
|
-
|
|
72
|
-
def extract_atoms(self, conversation_id: str, max_atoms: int = 20) -> list[MemoryEntry]:
|
|
73
|
-
return self.pyramid.extract_atoms_from_conversation(conversation_id, max_atoms)
|
|
74
|
-
|
|
75
|
-
def build_scenario(self, name: str, atom_ids: list[str]) -> MemoryEntry:
|
|
76
|
-
scenario = self.pyramid.build_scenario_from_atoms(atom_ids, name)
|
|
77
|
-
self.backend.add_entry(scenario.id, MemoryLayer.L2_SCENARIO.value, scenario.content, scenario.metadata)
|
|
78
|
-
return scenario
|
|
79
|
-
|
|
80
|
-
def build_persona(self, name: str, scenario_ids: list[str]) -> MemoryEntry:
|
|
81
|
-
persona = self.pyramid.build_persona_from_scenarios(scenario_ids, name)
|
|
82
|
-
self.backend.add_entry(persona.id, MemoryLayer.L3_PERSONA.value, persona.content, persona.metadata)
|
|
83
|
-
return persona
|
|
84
|
-
|
|
85
|
-
def offload_and_symbolize(self, node_id: str, verbose_content: str, task_label: str) -> str:
|
|
86
|
-
ref_path = self.symbolic.offload_log(node_id, verbose_content)
|
|
87
|
-
self.backend.save_ref(node_id, verbose_content)
|
|
88
|
-
self.symbolic.create_result_node(task_label, verbose_content)
|
|
89
|
-
return ref_path
|
|
90
|
-
|
|
91
|
-
def get_canvas(self) -> str:
|
|
92
|
-
return self.symbolic.get_canvas_markdown()
|
|
93
|
-
|
|
94
|
-
def drill_down(self, entry_id: str) -> list[MemoryEntry]:
|
|
95
|
-
return self.pyramid.drill_down(entry_id)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
|
-
import uuid
|
|
5
3
|
from contextlib import contextmanager
|
|
6
|
-
from dataclasses import dataclass
|
|
7
4
|
from datetime import datetime, timezone
|
|
8
5
|
from pathlib import Path
|
|
9
6
|
from typing import Optional
|
|
@@ -11,7 +8,7 @@ from typing import Optional
|
|
|
11
8
|
from sqlalchemy import Column, String, Text, DateTime, JSON, create_engine, Index
|
|
12
9
|
from sqlalchemy.orm import declarative_base, sessionmaker, Session
|
|
13
10
|
|
|
14
|
-
from onecode.config import
|
|
11
|
+
from onecode.config import ONECODE_DIR
|
|
15
12
|
|
|
16
13
|
|
|
17
14
|
Base = declarative_base()
|
|
@@ -34,20 +31,10 @@ class MemoryRecord(Base):
|
|
|
34
31
|
)
|
|
35
32
|
|
|
36
33
|
|
|
37
|
-
class RefRecord(Base):
|
|
38
|
-
__tablename__ = "ref_records"
|
|
39
|
-
|
|
40
|
-
id = Column(String(16), primary_key=True)
|
|
41
|
-
node_id = Column(String(16), nullable=False, index=True)
|
|
42
|
-
content = Column(Text, nullable=False)
|
|
43
|
-
file_path = Column(String(512), nullable=True)
|
|
44
|
-
created_at = Column(DateTime, nullable=False)
|
|
45
|
-
|
|
46
|
-
|
|
47
34
|
class MemoryBackend:
|
|
48
35
|
def __init__(self, db_path: Optional[Path] = None):
|
|
49
36
|
if db_path is None:
|
|
50
|
-
db_path =
|
|
37
|
+
db_path = ONECODE_DIR / "memory" / "memory.db"
|
|
51
38
|
db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
52
39
|
self.engine = create_engine(f"sqlite:///{db_path}", echo=False)
|
|
53
40
|
Base.metadata.create_all(self.engine)
|
|
@@ -87,12 +74,20 @@ class MemoryBackend:
|
|
|
87
74
|
s.merge(record)
|
|
88
75
|
return True
|
|
89
76
|
|
|
90
|
-
def get_entry(self, entry_id: str) -> Optional[
|
|
77
|
+
def get_entry(self, entry_id: str) -> Optional[dict]:
|
|
91
78
|
with self.session() as s:
|
|
92
79
|
record = s.query(MemoryRecord).filter_by(id=entry_id).first()
|
|
93
80
|
if record:
|
|
94
|
-
|
|
95
|
-
|
|
81
|
+
return {
|
|
82
|
+
"id": record.id,
|
|
83
|
+
"layer": record.layer,
|
|
84
|
+
"content": record.content,
|
|
85
|
+
"timestamp": record.timestamp.isoformat() if record.timestamp else "",
|
|
86
|
+
"metadata_json": record.metadata_json,
|
|
87
|
+
"parent_id": record.parent_id,
|
|
88
|
+
"result_ref": record.result_ref,
|
|
89
|
+
}
|
|
90
|
+
return None
|
|
96
91
|
|
|
97
92
|
def get_entries_by_layer(self, layer: str, limit: int = 100) -> list[MemoryRecord]:
|
|
98
93
|
with self.session() as s:
|
|
@@ -103,8 +98,6 @@ class MemoryBackend:
|
|
|
103
98
|
.limit(limit)
|
|
104
99
|
.all()
|
|
105
100
|
)
|
|
106
|
-
for r in records:
|
|
107
|
-
s.refresh(r)
|
|
108
101
|
return records
|
|
109
102
|
|
|
110
103
|
def get_entries_by_parent(self, parent_id: str) -> list[MemoryRecord]:
|
|
@@ -123,23 +116,6 @@ class MemoryBackend:
|
|
|
123
116
|
query = query.filter_by(layer=layer)
|
|
124
117
|
return query.limit(limit).all()
|
|
125
118
|
|
|
126
|
-
def save_ref(self, node_id: str, content: str, file_path: Optional[str] = None) -> bool:
|
|
127
|
-
with self.session() as s:
|
|
128
|
-
record = RefRecord(
|
|
129
|
-
id=str(uuid.uuid4())[:16],
|
|
130
|
-
node_id=node_id,
|
|
131
|
-
content=content,
|
|
132
|
-
file_path=file_path,
|
|
133
|
-
created_at=datetime.now(timezone.utc),
|
|
134
|
-
)
|
|
135
|
-
s.merge(record)
|
|
136
|
-
return True
|
|
137
|
-
|
|
138
|
-
def get_ref(self, node_id: str) -> Optional[str]:
|
|
139
|
-
with self.session() as s:
|
|
140
|
-
record = s.query(RefRecord).filter_by(node_id=node_id).order_by(RefRecord.created_at.desc()).first()
|
|
141
|
-
return record.content if record else None
|
|
142
|
-
|
|
143
119
|
def search_content(self, query: str, layer: Optional[str] = None, limit: int = 10) -> list[MemoryRecord]:
|
|
144
120
|
with self.session() as s:
|
|
145
121
|
q = s.query(MemoryRecord)
|
|
@@ -149,8 +125,6 @@ class MemoryBackend:
|
|
|
149
125
|
pattern = f"%{escaped}%"
|
|
150
126
|
q = q.filter(MemoryRecord.content.like(pattern, escape="\\"))
|
|
151
127
|
records = q.order_by(MemoryRecord.timestamp.desc()).limit(limit).all()
|
|
152
|
-
for r in records:
|
|
153
|
-
s.refresh(r)
|
|
154
128
|
return records
|
|
155
129
|
|
|
156
130
|
def delete_entry(self, entry_id: str) -> bool:
|
|
@@ -11,9 +11,6 @@ from typing import Optional
|
|
|
11
11
|
|
|
12
12
|
class MemoryLayer(Enum):
|
|
13
13
|
L0_CONVERSATION = "l0_conversation"
|
|
14
|
-
L1_ATOM = "l1_atom"
|
|
15
|
-
L2_SCENARIO = "l2_scenario"
|
|
16
|
-
L3_PERSONA = "l3_persona"
|
|
17
14
|
|
|
18
15
|
|
|
19
16
|
@dataclass
|
|
@@ -69,8 +66,8 @@ class MemoryEntry:
|
|
|
69
66
|
|
|
70
67
|
class MemoryPyramid:
|
|
71
68
|
def __init__(self, storage_path: Optional[Path] = None):
|
|
72
|
-
from onecode.config import
|
|
73
|
-
self.storage_path = storage_path or (
|
|
69
|
+
from onecode.config import ONECODE_DIR
|
|
70
|
+
self.storage_path = storage_path or (ONECODE_DIR / "memory")
|
|
74
71
|
self.storage_path.mkdir(parents=True, exist_ok=True)
|
|
75
72
|
self._layers = {layer: [] for layer in MemoryLayer}
|
|
76
73
|
self._load_index()
|
|
@@ -134,69 +131,3 @@ class MemoryPyramid:
|
|
|
134
131
|
def list_recent(self, layer: MemoryLayer, limit: int = 10) -> list[MemoryEntry]:
|
|
135
132
|
entries = self._layers.get(layer, [])
|
|
136
133
|
return sorted(entries, key=lambda e: e.timestamp, reverse=True)[:limit]
|
|
137
|
-
|
|
138
|
-
def drill_down(self, entry_id: str) -> list[MemoryEntry]:
|
|
139
|
-
chain = []
|
|
140
|
-
current = self._find_entry_anywhere(entry_id)
|
|
141
|
-
while current:
|
|
142
|
-
chain.append(current)
|
|
143
|
-
if current.parent_id:
|
|
144
|
-
current = self._find_entry_anywhere(current.parent_id)
|
|
145
|
-
else:
|
|
146
|
-
current = None
|
|
147
|
-
return list(reversed(chain))
|
|
148
|
-
|
|
149
|
-
def _find_entry_anywhere(self, entry_id: str) -> Optional[MemoryEntry]:
|
|
150
|
-
for entries in self._layers.values():
|
|
151
|
-
for entry in entries:
|
|
152
|
-
if entry.id == entry_id:
|
|
153
|
-
return entry
|
|
154
|
-
return None
|
|
155
|
-
|
|
156
|
-
def extract_atoms_from_conversation(
|
|
157
|
-
self, conversation_id: str, max_atoms: int = 20
|
|
158
|
-
) -> list[MemoryEntry]:
|
|
159
|
-
l0_entries = [e for e in self._layers[MemoryLayer.L0_CONVERSATION] if e.parent_id == conversation_id]
|
|
160
|
-
atoms = []
|
|
161
|
-
for l0 in l0_entries:
|
|
162
|
-
content = self.get_content(l0)
|
|
163
|
-
if len(content) > 100:
|
|
164
|
-
atom_content = content[:200] + "..."
|
|
165
|
-
else:
|
|
166
|
-
atom_content = content
|
|
167
|
-
atom = self.add(
|
|
168
|
-
MemoryLayer.L1_ATOM,
|
|
169
|
-
atom_content,
|
|
170
|
-
metadata={"source": "auto_extract", "source_id": l0.id},
|
|
171
|
-
parent_id=l0.id,
|
|
172
|
-
)
|
|
173
|
-
atoms.append(atom)
|
|
174
|
-
if len(atoms) >= max_atoms:
|
|
175
|
-
break
|
|
176
|
-
return atoms
|
|
177
|
-
|
|
178
|
-
def build_scenario_from_atoms(self, atom_ids: list[str], name: str) -> MemoryEntry:
|
|
179
|
-
atom_contents = []
|
|
180
|
-
for atom_id in atom_ids:
|
|
181
|
-
atom = self._find_entry_anywhere(atom_id)
|
|
182
|
-
if atom:
|
|
183
|
-
atom_contents.append(f"- {atom.content}")
|
|
184
|
-
content = f"## {name}\n\n" + "\n".join(atom_contents)
|
|
185
|
-
return self.add(
|
|
186
|
-
MemoryLayer.L2_SCENARIO,
|
|
187
|
-
content,
|
|
188
|
-
metadata={"atom_count": len(atom_ids)},
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
def build_persona_from_scenarios(self, scenario_ids: list[str], name: str) -> MemoryEntry:
|
|
192
|
-
scenario_contents = []
|
|
193
|
-
for sc_id in scenario_ids:
|
|
194
|
-
sc = self._find_entry_anywhere(sc_id)
|
|
195
|
-
if sc:
|
|
196
|
-
scenario_contents.append(f"### {sc.content[:100]}...\n{sc.content}")
|
|
197
|
-
content = f"# {name}\n\n" + "\n".join(scenario_contents)
|
|
198
|
-
return self.add(
|
|
199
|
-
MemoryLayer.L3_PERSONA,
|
|
200
|
-
content,
|
|
201
|
-
metadata={"scenario_count": len(scenario_ids)},
|
|
202
|
-
)
|
package/onecode/memory/recall.py
CHANGED
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import math
|
|
4
4
|
import re
|
|
5
|
-
from collections import Counter
|
|
6
5
|
from dataclasses import dataclass
|
|
7
6
|
from typing import Optional
|
|
8
7
|
|
|
@@ -17,6 +16,16 @@ class RecallResult:
|
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
class BM25:
|
|
19
|
+
# Hard cap above which ``index()`` refuses to build the inverted index.
|
|
20
|
+
# The original O(V × N) construction of ``doc_freqs`` (set membership
|
|
21
|
+
# over Python lists) becomes effectively uncallable above a few
|
|
22
|
+
# thousand docs — a 79k-chunk corpus (observed in production where
|
|
23
|
+
# sessions kept re-indexing without cleaning the SQLite store) took
|
|
24
|
+
# ~1 hour, blocking the event loop and freezing streaming + cancel.
|
|
25
|
+
# Above this cap we degrade to a no-op index so callers fall back to
|
|
26
|
+
# other retrievers / explicit search instead of hanging the process.
|
|
27
|
+
_MAX_DOCS = 5000
|
|
28
|
+
|
|
20
29
|
def __init__(self, k1: float = 1.5, b: float = 0.75):
|
|
21
30
|
self.k1 = k1
|
|
22
31
|
self.b = b
|
|
@@ -25,17 +34,43 @@ class BM25:
|
|
|
25
34
|
self.doc_freqs: dict[str, int] = {}
|
|
26
35
|
self.num_docs: int = 0
|
|
27
36
|
self.doc_tokens: list[list[str]] = []
|
|
37
|
+
# Tokenize once per doc as ``set`` (not list) so the
|
|
38
|
+
# ``token in doc`` membership check inside the doc_freqs
|
|
39
|
+
# comprehension is O(1) instead of O(doc_len). This alone takes
|
|
40
|
+
# the 79k-doc construction from ~hours to ~seconds; the hard cap
|
|
41
|
+
# above is a safety net for pathological corpora.
|
|
42
|
+
self._doc_token_sets: list[set[str]] = []
|
|
28
43
|
|
|
29
44
|
def index(self, documents: list[str]) -> None:
|
|
45
|
+
if len(documents) > self._MAX_DOCS:
|
|
46
|
+
import logging
|
|
47
|
+
logging.getLogger(__name__).warning(
|
|
48
|
+
"BM25.index refused: %d docs exceeds cap=%d (would block "
|
|
49
|
+
"the event loop for minutes); BM25 search will return no "
|
|
50
|
+
"results. Clean the codebase index or use a smaller "
|
|
51
|
+
"project scope.", len(documents), self._MAX_DOCS,
|
|
52
|
+
)
|
|
53
|
+
self.doc_tokens = []
|
|
54
|
+
self._doc_token_sets = []
|
|
55
|
+
self.doc_lengths = []
|
|
56
|
+
self.avgdl = 0
|
|
57
|
+
self.num_docs = 0
|
|
58
|
+
self.doc_freqs = {}
|
|
59
|
+
return
|
|
60
|
+
|
|
30
61
|
self.doc_tokens = [self._tokenize(doc) for doc in documents]
|
|
62
|
+
self._doc_token_sets = [set(t) for t in self.doc_tokens]
|
|
31
63
|
self.doc_lengths = [len(tokens) for tokens in self.doc_tokens]
|
|
32
64
|
self.avgdl = sum(self.doc_lengths) / len(self.doc_lengths) if self.doc_lengths else 0
|
|
33
65
|
self.num_docs = len(documents)
|
|
34
66
|
|
|
35
67
|
all_tokens = set()
|
|
36
|
-
for tokens in self.
|
|
68
|
+
for tokens in self._doc_token_sets:
|
|
37
69
|
all_tokens.update(tokens)
|
|
38
|
-
self.doc_freqs = {
|
|
70
|
+
self.doc_freqs = {
|
|
71
|
+
token: sum(1 for ts in self._doc_token_sets if token in ts)
|
|
72
|
+
for token in all_tokens
|
|
73
|
+
}
|
|
39
74
|
|
|
40
75
|
def _tokenize(self, text: str) -> list[str]:
|
|
41
76
|
text = text.lower()
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""onecode legacy migration — move onecode's private state from ~/.cdh/ to ~/.onecode/.
|
|
2
|
+
|
|
3
|
+
This is a ONE-TIME migration triggered at first startup after upgrade.
|
|
4
|
+
It only moves directories owned by onecode (logs, traces, memory,
|
|
5
|
+
snapshots, mcps, models, onecode.config.yaml). cdh platform directories
|
|
6
|
+
(skills, projects, state, sessions) are left untouched. Session JSON is owned
|
|
7
|
+
by the cdh platform layer (B mapping mode), not by individual engines.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
import shutil
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger("onecode.migrate")
|
|
18
|
+
|
|
19
|
+
# Directories/state that belong to onecode and should be migrated
|
|
20
|
+
# NOTE: sessions is deliberately excluded — it belongs to the cdh
|
|
21
|
+
# platform layer (B mapping mode), not to any individual engine.
|
|
22
|
+
_ONECODE_PRIVATE_DIRS = {
|
|
23
|
+
"logs",
|
|
24
|
+
"traces",
|
|
25
|
+
"memory",
|
|
26
|
+
"snapshots",
|
|
27
|
+
"mcps",
|
|
28
|
+
"models",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# cdh platform directories — NEVER touch
|
|
32
|
+
_CDH_PLATFORM_DIRS = {"skills", "projects", "state"}
|
|
33
|
+
|
|
34
|
+
# Dotfiles that belong to onecode (at the ~/.cdh/ root level)
|
|
35
|
+
_ONECODE_DOTFILES = {"onecode.config.yaml"}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def migrate_legacy_cdh_to_onecode(
|
|
39
|
+
legacy_dir: Path | None = None,
|
|
40
|
+
target_dir: Path | None = None,
|
|
41
|
+
) -> str | None:
|
|
42
|
+
"""Migrate onecode's private state from ~/.cdh/ to ~/.onecode/.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
legacy_dir: Source directory (default ~/.cdh/).
|
|
46
|
+
target_dir: Target directory (default ~/.onecode/).
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Info/warning message string, or None if no migration needed.
|
|
50
|
+
"""
|
|
51
|
+
legacy = (legacy_dir or Path.home() / ".cdh").resolve()
|
|
52
|
+
target = (target_dir or Path.home() / ".onecode").resolve()
|
|
53
|
+
|
|
54
|
+
# Nothing to migrate
|
|
55
|
+
if not legacy.exists():
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
# Already migrated (marker exists)
|
|
59
|
+
migrated_marker = target / ".migrated_from"
|
|
60
|
+
if migrated_marker.exists():
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
# Both exist but no marker → user may have manually set up ~/.onecode/
|
|
64
|
+
if target.exists():
|
|
65
|
+
# Check if target has any onecode dirs already
|
|
66
|
+
has_content = any(
|
|
67
|
+
(target / d).exists() for d in _ONECODE_PRIVATE_DIRS
|
|
68
|
+
) or target / "onecode.config.yaml" in target.iterdir()
|
|
69
|
+
if has_content:
|
|
70
|
+
return None # 各自独立运行,无需迁移
|
|
71
|
+
# Target exists but empty — safe to proceed
|
|
72
|
+
|
|
73
|
+
# Perform migration
|
|
74
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
75
|
+
migrated_items = []
|
|
76
|
+
|
|
77
|
+
# Migrate private subdirectories
|
|
78
|
+
for dir_name in _ONECODE_PRIVATE_DIRS:
|
|
79
|
+
src = legacy / dir_name
|
|
80
|
+
if src.exists() and src.is_dir():
|
|
81
|
+
dst = target / dir_name
|
|
82
|
+
if dst.exists():
|
|
83
|
+
# Merge contents (don't overwrite existing)
|
|
84
|
+
for item in src.iterdir():
|
|
85
|
+
dst_item = dst / item.name
|
|
86
|
+
if not dst_item.exists():
|
|
87
|
+
shutil.move(str(item), str(dst_item))
|
|
88
|
+
else:
|
|
89
|
+
shutil.move(str(src), str(dst))
|
|
90
|
+
migrated_items.append(dir_name)
|
|
91
|
+
|
|
92
|
+
# Migrate onecode dotfiles
|
|
93
|
+
for fname in _ONECODE_DOTFILES:
|
|
94
|
+
src = legacy / fname
|
|
95
|
+
if src.exists() and src.is_file():
|
|
96
|
+
dst = target / fname
|
|
97
|
+
if not dst.exists():
|
|
98
|
+
shutil.move(str(src), str(dst))
|
|
99
|
+
migrated_items.append(fname)
|
|
100
|
+
|
|
101
|
+
# Write migration marker
|
|
102
|
+
marker_data = {
|
|
103
|
+
"migrated_from": str(legacy),
|
|
104
|
+
"migrated_to": str(target),
|
|
105
|
+
"items": sorted(migrated_items),
|
|
106
|
+
"preserved_on_legacy": sorted(_CDH_PLATFORM_DIRS),
|
|
107
|
+
}
|
|
108
|
+
migrated_marker.write_text(
|
|
109
|
+
json.dumps(marker_data, ensure_ascii=False, indent=2),
|
|
110
|
+
encoding="utf-8",
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if migrated_items:
|
|
114
|
+
msg = (
|
|
115
|
+
f"⚠ Migrated onecode state from {legacy} → {target}: "
|
|
116
|
+
f"{', '.join(migrated_items)}. "
|
|
117
|
+
f"cdh platform dirs ({', '.join(_CDH_PLATFORM_DIRS)}) preserved at {legacy}."
|
|
118
|
+
)
|
|
119
|
+
logger.warning(msg)
|
|
120
|
+
return msg
|
|
121
|
+
|
|
122
|
+
return None
|
|
Binary file
|
|
Binary file
|