@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
|
@@ -105,6 +105,7 @@ TOOL_CATEGORY_MAP: dict[str, ToolCategory] = {
|
|
|
105
105
|
"TodoUpdate": ToolCategory.TODO_MGMT,
|
|
106
106
|
"TodoOutput": ToolCategory.TODO_MGMT,
|
|
107
107
|
"TodoStop": ToolCategory.TODO_MGMT,
|
|
108
|
+
"TodoClear": ToolCategory.TODO_MGMT,
|
|
108
109
|
"AskUser": ToolCategory.INTERACTION,
|
|
109
110
|
}
|
|
110
111
|
|
|
@@ -280,6 +281,11 @@ class StreamEventType(str, Enum):
|
|
|
280
281
|
SUBAGENT_CHUNK = "subagent_chunk"
|
|
281
282
|
SUBAGENT_THINKING = "subagent_thinking"
|
|
282
283
|
SUBAGENT_END = "subagent_end"
|
|
284
|
+
# A tool invoked *inside* a subagent, forwarded to the parent so the TUI
|
|
285
|
+
# can render it as a real ToolCall card inside the SubAgent widget (same
|
|
286
|
+
# style as main-conversation tools) instead of a plain "[Tool: X]" line.
|
|
287
|
+
SUBAGENT_TOOL_CALL = "subagent_tool_call"
|
|
288
|
+
SUBAGENT_TOOL_RESULT = "subagent_tool_result"
|
|
283
289
|
|
|
284
290
|
|
|
285
291
|
@dataclass
|
|
@@ -327,6 +333,10 @@ class StreamEvent:
|
|
|
327
333
|
subagent_thinking_text: str = ""
|
|
328
334
|
subagent_status: str = ""
|
|
329
335
|
subagent_error: str = ""
|
|
336
|
+
# SUBAGENT_TOOL_CALL fields ("start" | "complete"; reuses tool_id/name/
|
|
337
|
+
# category/args). SUBAGENT_TOOL_RESULT reuses tool_id/result_* and needs
|
|
338
|
+
# tool_name for display when no prior TOOL_CALL_COMPLETE was captured.
|
|
339
|
+
subagent_tool_phase: str = ""
|
|
330
340
|
|
|
331
341
|
@classmethod
|
|
332
342
|
def text_delta(cls, text: str) -> "StreamEvent":
|
|
@@ -448,6 +458,54 @@ class StreamEvent:
|
|
|
448
458
|
subagent_error=error,
|
|
449
459
|
)
|
|
450
460
|
|
|
461
|
+
@classmethod
|
|
462
|
+
def subagent_tool_call(
|
|
463
|
+
cls,
|
|
464
|
+
subagent_id: str,
|
|
465
|
+
phase: str,
|
|
466
|
+
tool_id: str,
|
|
467
|
+
tool_name: str,
|
|
468
|
+
tool_category: ToolCategory = ToolCategory.UNKNOWN,
|
|
469
|
+
tool_args: dict[str, Any] | None = None,
|
|
470
|
+
) -> "StreamEvent":
|
|
471
|
+
"""A tool invocation *inside* a subagent.
|
|
472
|
+
|
|
473
|
+
``phase`` is ``"start"`` (model began the call) or ``"complete"``
|
|
474
|
+
(args fully streamed). Carries the parent Spawn ``subagent_id`` so
|
|
475
|
+
the ACP can tag the emitted ``tool_call`` sessionUpdate and the TUI
|
|
476
|
+
can mount the card inside the owning SubAgent widget.
|
|
477
|
+
"""
|
|
478
|
+
return cls(
|
|
479
|
+
type=StreamEventType.SUBAGENT_TOOL_CALL,
|
|
480
|
+
subagent_id=subagent_id,
|
|
481
|
+
subagent_tool_phase=phase,
|
|
482
|
+
tool_id=tool_id,
|
|
483
|
+
tool_name=tool_name,
|
|
484
|
+
tool_category=tool_category,
|
|
485
|
+
tool_args=tool_args or {},
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
@classmethod
|
|
489
|
+
def subagent_tool_result(
|
|
490
|
+
cls,
|
|
491
|
+
subagent_id: str,
|
|
492
|
+
tool_id: str,
|
|
493
|
+
tool_name: str,
|
|
494
|
+
result_content: str,
|
|
495
|
+
is_error: bool = False,
|
|
496
|
+
category: ToolCategory = ToolCategory.UNKNOWN,
|
|
497
|
+
) -> "StreamEvent":
|
|
498
|
+
"""Result of a tool invoked *inside* a subagent."""
|
|
499
|
+
return cls(
|
|
500
|
+
type=StreamEventType.SUBAGENT_TOOL_RESULT,
|
|
501
|
+
subagent_id=subagent_id,
|
|
502
|
+
tool_id=tool_id,
|
|
503
|
+
tool_name=tool_name,
|
|
504
|
+
result_content=result_content,
|
|
505
|
+
result_is_error=is_error,
|
|
506
|
+
result_category=category,
|
|
507
|
+
)
|
|
508
|
+
|
|
451
509
|
@staticmethod
|
|
452
510
|
def is_text(event: "StreamEvent") -> bool:
|
|
453
511
|
return event.type in (StreamEventType.TEXT_DELTA, StreamEventType.THINKING)
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import asyncio
|
|
5
6
|
import logging
|
|
6
7
|
from abc import ABC, abstractmethod
|
|
7
8
|
from dataclasses import dataclass, field
|
|
@@ -592,6 +593,15 @@ class Provider(ABC):
|
|
|
592
593
|
"""Whether this provider uses Anthropic-style 'system' kwarg."""
|
|
593
594
|
return False
|
|
594
595
|
|
|
596
|
+
def supports_native_tools(self) -> bool:
|
|
597
|
+
"""Whether this provider accepts structured tools param in API calls.
|
|
598
|
+
|
|
599
|
+
When True, TOOL_DESCRIPTIONS prose in the system prompt is skipped
|
|
600
|
+
because the provider's native tool schema (passed via the ``tools``
|
|
601
|
+
kwarg to ``chat_stream_response``) provides equivalent information.
|
|
602
|
+
"""
|
|
603
|
+
return True
|
|
604
|
+
|
|
595
605
|
async def chat_stream_response(
|
|
596
606
|
self,
|
|
597
607
|
messages: list[Message],
|
|
@@ -605,7 +615,15 @@ class Provider(ABC):
|
|
|
605
615
|
Default implementation: falls back to chat() for providers that
|
|
606
616
|
don't implement structured streaming.
|
|
607
617
|
"""
|
|
608
|
-
|
|
618
|
+
logging.getLogger("onecode.models.provider").warning(
|
|
619
|
+
"[PROVIDER-STREAM] %s does not override chat_stream_response — "
|
|
620
|
+
"falling back to non-streaming chat() (on_text_chunk will NOT fire)",
|
|
621
|
+
type(self).__name__,
|
|
622
|
+
)
|
|
623
|
+
response = await asyncio.wait_for(
|
|
624
|
+
self.chat(messages, model=model, **kwargs),
|
|
625
|
+
timeout=300,
|
|
626
|
+
)
|
|
609
627
|
return ChatResponse(
|
|
610
628
|
content=response.get_text(),
|
|
611
629
|
tool_uses=[
|
|
@@ -625,7 +643,7 @@ class Provider(ABC):
|
|
|
625
643
|
body: str,
|
|
626
644
|
*,
|
|
627
645
|
retry_after: Optional[float] = None,
|
|
628
|
-
) -> "ProviderError":
|
|
646
|
+
) -> "ProviderError": # noqa: F821
|
|
629
647
|
"""Convert an upstream HTTP error into the most specific
|
|
630
648
|
:class:`ProviderError` subclass.
|
|
631
649
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -58,6 +58,25 @@ class AnthropicProvider(Provider):
|
|
|
58
58
|
model=model,
|
|
59
59
|
)
|
|
60
60
|
async with httpx.AsyncClient(timeout=120) as client:
|
|
61
|
+
prepared = self.prepare_messages(messages)
|
|
62
|
+
system_content = None
|
|
63
|
+
filtered_messages = []
|
|
64
|
+
for msg in prepared:
|
|
65
|
+
if msg.get("role") == "system":
|
|
66
|
+
system_content = msg["content"]
|
|
67
|
+
else:
|
|
68
|
+
filtered_messages.append(msg)
|
|
69
|
+
api_body = {
|
|
70
|
+
"model": model,
|
|
71
|
+
"max_tokens": kwargs.get("max_tokens", 4096),
|
|
72
|
+
"messages": filtered_messages,
|
|
73
|
+
}
|
|
74
|
+
if system_content:
|
|
75
|
+
api_body["system"] = [{
|
|
76
|
+
"type": "text",
|
|
77
|
+
"text": system_content,
|
|
78
|
+
"cache_control": {"type": "ephemeral"},
|
|
79
|
+
}]
|
|
61
80
|
resp = await client.post(
|
|
62
81
|
f"{self._endpoint}/messages",
|
|
63
82
|
headers={
|
|
@@ -65,11 +84,7 @@ class AnthropicProvider(Provider):
|
|
|
65
84
|
"anthropic-version": "2023-06-01",
|
|
66
85
|
"content-type": "application/json",
|
|
67
86
|
},
|
|
68
|
-
json=
|
|
69
|
-
"model": model,
|
|
70
|
-
"max_tokens": kwargs.get("max_tokens", 4096),
|
|
71
|
-
"messages": self.prepare_messages(messages),
|
|
72
|
-
},
|
|
87
|
+
json=api_body,
|
|
73
88
|
)
|
|
74
89
|
data = resp.json()
|
|
75
90
|
return ModelResponse(
|
|
@@ -87,6 +102,26 @@ class AnthropicProvider(Provider):
|
|
|
87
102
|
yield "API key not configured."
|
|
88
103
|
return
|
|
89
104
|
async with httpx.AsyncClient(timeout=300) as client:
|
|
105
|
+
prepared_cs = self.prepare_messages(messages)
|
|
106
|
+
system_content_cs = None
|
|
107
|
+
filtered_cs = []
|
|
108
|
+
for msg in prepared_cs:
|
|
109
|
+
if msg.get("role") == "system":
|
|
110
|
+
system_content_cs = msg["content"]
|
|
111
|
+
else:
|
|
112
|
+
filtered_cs.append(msg)
|
|
113
|
+
body_cs = {
|
|
114
|
+
"model": model,
|
|
115
|
+
"max_tokens": kwargs.get("max_tokens", 4096),
|
|
116
|
+
"messages": filtered_cs,
|
|
117
|
+
"stream": True,
|
|
118
|
+
}
|
|
119
|
+
if system_content_cs:
|
|
120
|
+
body_cs["system"] = [{
|
|
121
|
+
"type": "text",
|
|
122
|
+
"text": system_content_cs,
|
|
123
|
+
"cache_control": {"type": "ephemeral"},
|
|
124
|
+
}]
|
|
90
125
|
async with client.stream(
|
|
91
126
|
"POST",
|
|
92
127
|
f"{self._endpoint}/messages",
|
|
@@ -95,12 +130,7 @@ class AnthropicProvider(Provider):
|
|
|
95
130
|
"anthropic-version": "2023-06-01",
|
|
96
131
|
"content-type": "application/json",
|
|
97
132
|
},
|
|
98
|
-
json=
|
|
99
|
-
"model": model,
|
|
100
|
-
"max_tokens": kwargs.get("max_tokens", 4096),
|
|
101
|
-
"messages": self.prepare_messages(messages),
|
|
102
|
-
"stream": True,
|
|
103
|
-
},
|
|
133
|
+
json=body_cs,
|
|
104
134
|
) as resp:
|
|
105
135
|
async for line in resp.aiter_lines():
|
|
106
136
|
if line.startswith("data: ") and line[6:] != "[DONE]":
|
|
@@ -131,12 +161,29 @@ class AnthropicProvider(Provider):
|
|
|
131
161
|
|
|
132
162
|
try:
|
|
133
163
|
async with httpx.AsyncClient(timeout=300) as client:
|
|
164
|
+
# Extract system message for Anthropic's separate ``system``
|
|
165
|
+
# parameter (enables prompt caching).
|
|
166
|
+
prepared = self.prepare_messages(messages)
|
|
167
|
+
system_content = None
|
|
168
|
+
filtered_messages = []
|
|
169
|
+
for msg in prepared:
|
|
170
|
+
if msg.get("role") == "system":
|
|
171
|
+
system_content = msg["content"]
|
|
172
|
+
else:
|
|
173
|
+
filtered_messages.append(msg)
|
|
174
|
+
|
|
134
175
|
body = {
|
|
135
176
|
"model": model,
|
|
136
177
|
"max_tokens": kwargs.get("max_tokens", 4096),
|
|
137
|
-
"messages":
|
|
178
|
+
"messages": filtered_messages,
|
|
138
179
|
"stream": True,
|
|
139
180
|
}
|
|
181
|
+
if system_content:
|
|
182
|
+
body["system"] = [{
|
|
183
|
+
"type": "text",
|
|
184
|
+
"text": system_content,
|
|
185
|
+
"cache_control": {"type": "ephemeral"},
|
|
186
|
+
}]
|
|
140
187
|
tools = kwargs.get("tools")
|
|
141
188
|
if tools:
|
|
142
189
|
body["tools"] = tools
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import logging
|
|
5
|
+
import time
|
|
4
6
|
from typing import AsyncIterator, Callable, Optional
|
|
5
7
|
|
|
6
8
|
import httpx
|
|
@@ -8,6 +10,8 @@ import httpx
|
|
|
8
10
|
from onecode.models.errors import safe_error_msg
|
|
9
11
|
from onecode.models.provider import ChatResponse, Message, ModelResponse, Provider, ProviderRegistry
|
|
10
12
|
|
|
13
|
+
logger = logging.getLogger("onecode.models.providers.glm")
|
|
14
|
+
|
|
11
15
|
|
|
12
16
|
class GLMProvider(Provider):
|
|
13
17
|
name = "glm"
|
|
@@ -92,6 +96,17 @@ class GLMProvider(Provider):
|
|
|
92
96
|
return ChatResponse(content="API key not configured.")
|
|
93
97
|
content_parts: list[str] = []
|
|
94
98
|
stream_tool_calls: dict[int, dict] = {}
|
|
99
|
+
_n_msgs = len(messages)
|
|
100
|
+
_t0 = time.monotonic()
|
|
101
|
+
_first_byte_t: float | None = None
|
|
102
|
+
_chunk_n = 0
|
|
103
|
+
_text_n = 0
|
|
104
|
+
_bytes = 0
|
|
105
|
+
_on_text_cb = bool(on_text_chunk)
|
|
106
|
+
logger.debug(
|
|
107
|
+
"[GLM-STREAM] open model=%s msgs=%d on_text_chunk_cb=%s",
|
|
108
|
+
model, _n_msgs, _on_text_cb,
|
|
109
|
+
)
|
|
95
110
|
try:
|
|
96
111
|
async with httpx.AsyncClient(timeout=300) as client:
|
|
97
112
|
async with client.stream(
|
|
@@ -108,12 +123,27 @@ class GLMProvider(Provider):
|
|
|
108
123
|
**kwargs,
|
|
109
124
|
},
|
|
110
125
|
) as resp:
|
|
126
|
+
logger.debug(
|
|
127
|
+
"[GLM-STREAM] stream opened status=%d t=%.2fs",
|
|
128
|
+
resp.status_code, time.monotonic() - _t0,
|
|
129
|
+
)
|
|
111
130
|
async for line in resp.aiter_lines():
|
|
131
|
+
if not line:
|
|
132
|
+
continue
|
|
133
|
+
_chunk_n += 1
|
|
112
134
|
if line.startswith("data: ") and line[6:] != "[DONE]":
|
|
113
135
|
chunk = json.loads(line[6:])
|
|
114
136
|
delta = chunk.get("choices", [{}])[0].get("delta", {})
|
|
115
137
|
text = delta.get("content", "")
|
|
116
138
|
if text:
|
|
139
|
+
if _first_byte_t is None:
|
|
140
|
+
_first_byte_t = time.monotonic() - _t0
|
|
141
|
+
logger.debug(
|
|
142
|
+
"[GLM-STREAM] first-byte t=%.2fs chunk#%d",
|
|
143
|
+
_first_byte_t, _chunk_n,
|
|
144
|
+
)
|
|
145
|
+
_text_n += 1
|
|
146
|
+
_bytes += len(text)
|
|
117
147
|
content_parts.append(text)
|
|
118
148
|
if on_text_chunk:
|
|
119
149
|
on_text_chunk(text)
|
|
@@ -132,7 +162,17 @@ class GLMProvider(Provider):
|
|
|
132
162
|
slot["arguments"] += args_delta
|
|
133
163
|
if on_tool_call_delta:
|
|
134
164
|
on_tool_call_delta(slot.get("id", ""), slot.get("name", ""), args_delta)
|
|
165
|
+
logger.debug(
|
|
166
|
+
"[GLM-STREAM] stream done chunks=%d text_chunks=%d "
|
|
167
|
+
"bytes=%d on_text_cb=%s t=%.2fs",
|
|
168
|
+
_chunk_n, _text_n, _bytes, _on_text_cb,
|
|
169
|
+
time.monotonic() - _t0,
|
|
170
|
+
)
|
|
135
171
|
except Exception as e:
|
|
172
|
+
logger.exception(
|
|
173
|
+
"[GLM-STREAM] EXCEPTION t=%.2fs chunks=%d text_chunks=%d: %s",
|
|
174
|
+
time.monotonic() - _t0, _chunk_n, _text_n, e,
|
|
175
|
+
)
|
|
136
176
|
return ChatResponse(content=f"Error: {safe_error_msg(e)}")
|
|
137
177
|
tool_uses = []
|
|
138
178
|
for slot in stream_tool_calls.values():
|
|
@@ -19,6 +19,9 @@ class MiniMaxProvider(Provider):
|
|
|
19
19
|
def is_anthropic_style(self) -> bool:
|
|
20
20
|
return False
|
|
21
21
|
|
|
22
|
+
def supports_native_tools(self) -> bool:
|
|
23
|
+
return False
|
|
24
|
+
|
|
22
25
|
async def chat(
|
|
23
26
|
self, messages: list[Message], model: str = "MiniMax-M2.7", **kwargs
|
|
24
27
|
) -> ModelResponse:
|
|
@@ -128,6 +128,9 @@ class MiniMaxiProvider(Provider):
|
|
|
128
128
|
def is_anthropic_style(self) -> bool:
|
|
129
129
|
return False
|
|
130
130
|
|
|
131
|
+
def supports_native_tools(self) -> bool:
|
|
132
|
+
return False
|
|
133
|
+
|
|
131
134
|
async def chat(
|
|
132
135
|
self, messages: list[Message], model: str = "", **kwargs
|
|
133
136
|
) -> ModelResponse:
|
|
@@ -18,6 +18,9 @@ class OllamaProvider(Provider):
|
|
|
18
18
|
def is_anthropic_style(self) -> bool:
|
|
19
19
|
return False
|
|
20
20
|
|
|
21
|
+
def supports_native_tools(self) -> bool:
|
|
22
|
+
return False
|
|
23
|
+
|
|
21
24
|
async def chat(
|
|
22
25
|
self, messages: list[Message], model: str = "llama2", **kwargs
|
|
23
26
|
) -> ModelResponse:
|
package/onecode/server/app.py
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""onecode bootstrap — ensures ai-dlc-skill is installed in the cdh platform pool.
|
|
2
|
+
|
|
3
|
+
Called at onecode startup (or cdh launch) to sync the ai-dlc-skill version
|
|
4
|
+
from the repository source (ai-dlc-skill/) to ~/.cdh/skills/ai-dlc-skill/.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
import yaml
|
|
14
|
+
|
|
15
|
+
from cdh.cdh_skill_manager import CdhSkillManager
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger("onecode.skills.bootstrap")
|
|
18
|
+
|
|
19
|
+
SKILL_SOURCE_NAME = "ai-dlc-skill"
|
|
20
|
+
DEFAULT_VERSION = "4.0.0"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_source_skill_dir(workspace_root: Path) -> Optional[Path]:
|
|
24
|
+
"""Walk up from workspace_root to find ai-dlc-skill/ directory."""
|
|
25
|
+
current = workspace_root.resolve()
|
|
26
|
+
# Check current directory
|
|
27
|
+
candidate = current / SKILL_SOURCE_NAME
|
|
28
|
+
if candidate.is_dir() and (candidate / "skill.yaml").exists():
|
|
29
|
+
return candidate
|
|
30
|
+
# Check parent (if in a subdirectory)
|
|
31
|
+
candidate = current.parent / SKILL_SOURCE_NAME
|
|
32
|
+
if candidate.is_dir() and (candidate / "skill.yaml").exists():
|
|
33
|
+
return candidate
|
|
34
|
+
# Check git root
|
|
35
|
+
try:
|
|
36
|
+
git_root = next(
|
|
37
|
+
p for p in current.parents if (p / ".git").exists()
|
|
38
|
+
)
|
|
39
|
+
candidate = git_root / SKILL_SOURCE_NAME
|
|
40
|
+
if candidate.is_dir() and (candidate / "skill.yaml").exists():
|
|
41
|
+
return candidate
|
|
42
|
+
except StopIteration:
|
|
43
|
+
pass
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_source_version(source_dir: Path) -> str:
|
|
48
|
+
"""Read metadata.version from source skill.yaml."""
|
|
49
|
+
try:
|
|
50
|
+
skill_yaml = source_dir / "skill.yaml"
|
|
51
|
+
data = yaml.safe_load(skill_yaml.read_text(encoding="utf-8")) or {}
|
|
52
|
+
return data.get("metadata", {}).get("version", DEFAULT_VERSION)
|
|
53
|
+
except Exception:
|
|
54
|
+
return DEFAULT_VERSION
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def ensure_onecode_default_skills() -> None:
|
|
58
|
+
"""Install built-in skills (git, shell) to ~/.onecode/skills/ on first run."""
|
|
59
|
+
from onecode.config import ONECODE_DIR
|
|
60
|
+
|
|
61
|
+
target_dir = ONECODE_DIR / "skills"
|
|
62
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
63
|
+
|
|
64
|
+
builtin_root = Path(__file__).resolve().parent.parent / "builtin_skills"
|
|
65
|
+
if not builtin_root.exists():
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
mgr = CdhSkillManager(skills_dir=target_dir)
|
|
69
|
+
|
|
70
|
+
for d in sorted(builtin_root.iterdir()):
|
|
71
|
+
if not d.is_dir():
|
|
72
|
+
continue
|
|
73
|
+
if not (d / "skill.yaml").exists():
|
|
74
|
+
continue
|
|
75
|
+
name = d.name
|
|
76
|
+
if mgr.get_installed_version(name) is not None:
|
|
77
|
+
continue
|
|
78
|
+
err = mgr.install(d)
|
|
79
|
+
if err:
|
|
80
|
+
logger.warning("default skill %s install failed: %s", name, err)
|
|
81
|
+
else:
|
|
82
|
+
logger.info("default skill %s installed \u2192 %s/%s", name, target_dir, name)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def ensure_ai_dlc_skill(workspace_root: Path | None = None) -> None:
|
|
86
|
+
"""Bootstrap ai-dlc-skill into cdh platform pool.
|
|
87
|
+
|
|
88
|
+
Compares source version with installed version. If missing or outdated,
|
|
89
|
+
installs via CdhSkillManager. Return value: None (logs warnings on failure).
|
|
90
|
+
"""
|
|
91
|
+
root = workspace_root or Path.cwd()
|
|
92
|
+
mgr = CdhSkillManager()
|
|
93
|
+
|
|
94
|
+
# Check if already installed and up-to-date
|
|
95
|
+
installed_version = mgr.get_installed_version(SKILL_SOURCE_NAME)
|
|
96
|
+
|
|
97
|
+
# Find source directory
|
|
98
|
+
source_dir = get_source_skill_dir(root)
|
|
99
|
+
if source_dir is None:
|
|
100
|
+
if installed_version is None:
|
|
101
|
+
logger.warning(
|
|
102
|
+
"ai-dlc-skill source not found at %s/ai-dlc-skill/ and not installed; "
|
|
103
|
+
"install from repository root to enable AI-DLC methodology.",
|
|
104
|
+
root,
|
|
105
|
+
)
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
source_version = get_source_version(source_dir)
|
|
109
|
+
|
|
110
|
+
if installed_version == source_version:
|
|
111
|
+
logger.debug("ai-dlc-skill v%s is current, skipping install.", source_version)
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
# Install or update
|
|
115
|
+
err = mgr.install(source_dir)
|
|
116
|
+
if err:
|
|
117
|
+
logger.warning("ai-dlc-skill install failed: %s", err)
|
|
118
|
+
else:
|
|
119
|
+
logger.info(
|
|
120
|
+
"ai-dlc-skill installed v%s → %s/%s",
|
|
121
|
+
source_version,
|
|
122
|
+
mgr.skills_dir,
|
|
123
|
+
SKILL_SOURCE_NAME,
|
|
124
|
+
)
|
package/onecode/skills/loader.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import re
|
|
4
3
|
from pathlib import Path
|
|
5
4
|
from typing import Optional
|
|
6
5
|
|
|
@@ -9,18 +8,10 @@ import yaml
|
|
|
9
8
|
from onecode.skills.frontmatter import parse_frontmatter
|
|
10
9
|
from onecode.skills.model import Skill
|
|
11
10
|
|
|
12
|
-
from onecode.config import
|
|
11
|
+
from onecode.config import ONECODE_DIR
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
USER_SKILLS_DIR =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
SKILL_LOCATIONS = [
|
|
19
|
-
("user", USER_SKILLS_DIR),
|
|
20
|
-
(".opencode", ".opencode/skills"),
|
|
21
|
-
(".claude", ".claude/skills"),
|
|
22
|
-
(".agents", ".agents/skills"),
|
|
23
|
-
]
|
|
14
|
+
USER_SKILLS_DIR = ONECODE_DIR / "skills"
|
|
24
15
|
|
|
25
16
|
|
|
26
17
|
class SkillLoader:
|
|
@@ -31,50 +22,9 @@ class SkillLoader:
|
|
|
31
22
|
def _get_search_dirs(self) -> list[tuple[str, Path]]:
|
|
32
23
|
dirs = []
|
|
33
24
|
if USER_SKILLS_DIR.exists():
|
|
34
|
-
dirs.append(("
|
|
35
|
-
|
|
36
|
-
# Built-in skills packaged with onecode
|
|
37
|
-
builtin_dir = Path(__file__).resolve().parent.parent / "builtin_skills"
|
|
38
|
-
if builtin_dir.exists():
|
|
39
|
-
dirs.append(("builtin", builtin_dir))
|
|
40
|
-
|
|
41
|
-
# Project root-level directories containing SKILL.md
|
|
42
|
-
try:
|
|
43
|
-
for d in sorted(self._workspace_root.iterdir()):
|
|
44
|
-
if d.is_dir() and (d / "SKILL.md").exists():
|
|
45
|
-
dirs.append(("project", self._workspace_root))
|
|
46
|
-
break
|
|
47
|
-
except Exception:
|
|
48
|
-
pass
|
|
49
|
-
|
|
50
|
-
search_paths = [
|
|
51
|
-
(".opencode", ".claude", ".agents"),
|
|
52
|
-
]
|
|
53
|
-
for group in search_paths:
|
|
54
|
-
for name in group:
|
|
55
|
-
pattern = f"{name}/skills"
|
|
56
|
-
for parent in self._walk_up_parents():
|
|
57
|
-
candidate = parent / pattern
|
|
58
|
-
if candidate.exists():
|
|
59
|
-
dirs.append((f"{name}/skills", candidate))
|
|
60
|
-
break
|
|
61
|
-
|
|
25
|
+
dirs.append(("onecode", USER_SKILLS_DIR))
|
|
62
26
|
return dirs
|
|
63
27
|
|
|
64
|
-
def _walk_up_parents(self):
|
|
65
|
-
current = self._workspace_root.resolve()
|
|
66
|
-
yield current
|
|
67
|
-
try:
|
|
68
|
-
git_root = next(
|
|
69
|
-
(p for p in current.parents if (p / ".git").exists() or (p / ".hg").exists()),
|
|
70
|
-
None,
|
|
71
|
-
)
|
|
72
|
-
if git_root:
|
|
73
|
-
yield git_root
|
|
74
|
-
except StopIteration:
|
|
75
|
-
pass
|
|
76
|
-
yield Path.home()
|
|
77
|
-
|
|
78
28
|
def _discover(self) -> dict[str, Skill]:
|
|
79
29
|
if self._cache is not None:
|
|
80
30
|
return self._cache
|
|
@@ -153,5 +103,37 @@ class SkillLoader:
|
|
|
153
103
|
if s.path and str(s.path).startswith(source_prefix)
|
|
154
104
|
]
|
|
155
105
|
|
|
106
|
+
def search(self, keyword: str) -> list[Skill]:
|
|
107
|
+
kw = keyword.lower()
|
|
108
|
+
results: list[tuple[Skill, int]] = []
|
|
109
|
+
|
|
110
|
+
for skill in self._discover().values():
|
|
111
|
+
score = 0
|
|
112
|
+
name_lower = skill.name.lower()
|
|
113
|
+
desc_lower = skill.description.lower()
|
|
114
|
+
|
|
115
|
+
if kw == name_lower:
|
|
116
|
+
score += 100
|
|
117
|
+
elif kw in name_lower:
|
|
118
|
+
score += 50
|
|
119
|
+
if kw in desc_lower:
|
|
120
|
+
score += 30
|
|
121
|
+
if desc_lower.startswith(kw):
|
|
122
|
+
score += 10
|
|
123
|
+
for trigger in skill.triggers:
|
|
124
|
+
if kw in trigger.lower():
|
|
125
|
+
score += 20
|
|
126
|
+
break
|
|
127
|
+
for phase in skill.phases:
|
|
128
|
+
if kw in phase.lower():
|
|
129
|
+
score += 10
|
|
130
|
+
break
|
|
131
|
+
|
|
132
|
+
if score > 0:
|
|
133
|
+
results.append((skill, score))
|
|
134
|
+
|
|
135
|
+
results.sort(key=lambda x: x[1], reverse=True)
|
|
136
|
+
return [s for s, _ in results]
|
|
137
|
+
|
|
156
138
|
def invalidate_cache(self) -> None:
|
|
157
139
|
self._cache = None
|
|
@@ -5,12 +5,12 @@ from typing import Optional
|
|
|
5
5
|
|
|
6
6
|
import yaml
|
|
7
7
|
|
|
8
|
-
from onecode.config import
|
|
8
|
+
from onecode.config import ONECODE_DIR
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class SkillManager:
|
|
12
12
|
def __init__(self):
|
|
13
|
-
self.skills_dir =
|
|
13
|
+
self.skills_dir = ONECODE_DIR / "skills"
|
|
14
14
|
self.skills_dir.mkdir(parents=True, exist_ok=True)
|
|
15
15
|
self._skills: dict[str, dict] = {}
|
|
16
16
|
|