qwen-sci 0.2.2__py3-none-any.whl
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.
- qwen_sci-0.2.2.dist-info/METADATA +492 -0
- qwen_sci-0.2.2.dist-info/RECORD +549 -0
- qwen_sci-0.2.2.dist-info/WHEEL +4 -0
- qwen_sci-0.2.2.dist-info/entry_points.txt +6 -0
- qwen_sci-0.2.2.dist-info/licenses/LICENSE +21 -0
- src/1.txt +1 -0
- src/__init__.py +1 -0
- src/__main__.py +5 -0
- src/agents/__init__.py +1 -0
- src/agents/experiment_design_agent/__init__.py +268 -0
- src/agents/experiment_design_agent/artifacts.py +747 -0
- src/agents/experiment_design_agent/cache.py +319 -0
- src/agents/experiment_design_agent/completeness.py +130 -0
- src/agents/experiment_design_agent/contracts.py +771 -0
- src/agents/experiment_design_agent/counterexample_analyzer.py +187 -0
- src/agents/experiment_design_agent/design_evidence_paper_screener.py +470 -0
- src/agents/experiment_design_agent/discipline_catalog.py +178 -0
- src/agents/experiment_design_agent/evidence_cards.py +338 -0
- src/agents/experiment_design_agent/evidence_planner.py +452 -0
- src/agents/experiment_design_agent/formal_reasoning_planner.py +884 -0
- src/agents/experiment_design_agent/fulltext_acquisition.py +863 -0
- src/agents/experiment_design_agent/idea_adapter.py +100 -0
- src/agents/experiment_design_agent/idea_intake.py +87 -0
- src/agents/experiment_design_agent/llm_json.py +328 -0
- src/agents/experiment_design_agent/orchestrator.py +1220 -0
- src/agents/experiment_design_agent/reasoning_context.py +183 -0
- src/agents/experiment_design_agent/reasoning_validation.py +546 -0
- src/agents/experiment_design_agent/run.py +481 -0
- src/agents/experiment_design_agent/run_logging.py +292 -0
- src/agents/experiment_design_agent/scope_gate.py +198 -0
- src/agents/experiment_design_agent/study_type_composer.py +1360 -0
- src/agents/experiment_design_agent/survey_evidence.py +1381 -0
- src/agents/experiment_design_agent/template_router.py +195 -0
- src/agents/experiment_design_agent/variable_claim_extractor.py +93 -0
- src/agents/idea_agent/__init__.py +1 -0
- src/agents/idea_agent/agent/__init__.py +6 -0
- src/agents/idea_agent/agent/artifacts.py +308 -0
- src/agents/idea_agent/agent/base.py +122 -0
- src/agents/idea_agent/agent/ligagent.py +488 -0
- src/agents/idea_agent/agent/mcts.py +2060 -0
- src/agents/idea_agent/agent/prompts/__init__.py +48 -0
- src/agents/idea_agent/agent/prompts/advanced_analysis.py +235 -0
- src/agents/idea_agent/agent/prompts/algorithm_alignment.py +55 -0
- src/agents/idea_agent/agent/prompts/algorithm_structuring.py +48 -0
- src/agents/idea_agent/agent/prompts/component_extraction.py +62 -0
- src/agents/idea_agent/agent/prompts/component_novelty_evaluation.py +54 -0
- src/agents/idea_agent/agent/prompts/experiment_findings_extraction.py +39 -0
- src/agents/idea_agent/agent/prompts/idea_fusion.py +295 -0
- src/agents/idea_agent/agent/prompts/idea_introduction.py +30 -0
- src/agents/idea_agent/agent/prompts/idea_result_alignment.py +39 -0
- src/agents/idea_agent/agent/prompts/input_interpreter.py +52 -0
- src/agents/idea_agent/agent/prompts/keynote_ops.py +78 -0
- src/agents/idea_agent/agent/prompts/mcts_evaluation.py +235 -0
- src/agents/idea_agent/agent/prompts/mcts_generation.py +140 -0
- src/agents/idea_agent/agent/prompts/mechanism_commit_query.py +99 -0
- src/agents/idea_agent/agent/prompts/memory_selection.py +0 -0
- src/agents/idea_agent/agent/prompts/prompt_modes.py +16 -0
- src/agents/idea_agent/agent/prompts/rag_query.py +34 -0
- src/agents/idea_agent/agent/prompts/re_analysis_replan.py +48 -0
- src/agents/idea_agent/agent/prompts/reference_grounding.py +34 -0
- src/agents/idea_agent/agent/prompts/root_domain_classification.py +32 -0
- src/agents/idea_agent/agent/prompts/scientific_debate.py +88 -0
- src/agents/idea_agent/agent/prompts/scientific_materialization.py +45 -0
- src/agents/idea_agent/agent/prompts/skill_instantiation.py +193 -0
- src/agents/idea_agent/agent/prompts/theory_transfer_query.py +93 -0
- src/agents/idea_agent/agent/prompts/topic_background.py +15 -0
- src/agents/idea_agent/agent/skills/DEFAULT_SKILL_TEMPLATES.json +263 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/alternative-path-contrast/SKILL.md +40 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/alternative-path-contrast/references/contrast_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/feedback-closed-loop/SKILL.md +40 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/feedback-closed-loop/references/mechanism_cards.md +24 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/hierarchical-decomposition/SKILL.md +39 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/hierarchical-decomposition/references/hierarchy_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/mechanism-commit-innovation/SKILL.md +39 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/mechanism-commit-innovation/references/mechanism_commit_patterns.md +24 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/multi-scale-coordinator/SKILL.md +39 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/multi-scale-coordinator/references/coordination_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/speculative-execution-with-repair/SKILL.md +41 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/speculative-execution-with-repair/references/speculation_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/surgical-modularity/SKILL.md +39 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/surgical-modularity/references/modularity_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/theory-transfer-injection/SKILL.md +40 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/theory-transfer-injection/references/transfer_patterns.md +24 -0
- src/agents/idea_agent/example/idea_result.json +900 -0
- src/agents/idea_agent/run.py +276 -0
- src/agents/idea_agent/tests/test_advanced_analysis_contract.py +90 -0
- src/agents/idea_agent/tests/test_gap_hypothesis_bridge.py +166 -0
- src/agents/idea_agent/tests/test_idea_debate.py +480 -0
- src/agents/idea_agent/tests/test_idea_result_v5.py +99 -0
- src/agents/idea_agent/tests/test_idea_synthesis.py +92 -0
- src/agents/idea_agent/tests/test_mature_idea_sources.py +27 -0
- src/agents/idea_agent/tests/test_multimodal_idea_integration.py +386 -0
- src/agents/idea_agent/tests/test_profile_materialization_and_scoring.py +219 -0
- src/agents/idea_agent/tests/test_profile_native_skill_rendering.py +122 -0
- src/agents/idea_agent/tests/test_result_persistence.py +183 -0
- src/agents/idea_agent/tests/test_scientific_intervention_profiles.py +241 -0
- src/agents/idea_agent/tests/test_scientific_rubric_and_prompts.py +559 -0
- src/agents/idea_agent/tests/test_strict_json_object_responses.py +48 -0
- src/agents/idea_agent/utils/core/__init__.py +1 -0
- src/agents/idea_agent/utils/core/ablation_inputs.py +60 -0
- src/agents/idea_agent/utils/core/chat_errors.py +95 -0
- src/agents/idea_agent/utils/core/chat_router.py +130 -0
- src/agents/idea_agent/utils/core/chat_transport.py +188 -0
- src/agents/idea_agent/utils/core/config_loader.py +166 -0
- src/agents/idea_agent/utils/core/json_utils.py +28 -0
- src/agents/idea_agent/utils/core/logger.py +287 -0
- src/agents/idea_agent/utils/core/progress.py +58 -0
- src/agents/idea_agent/utils/core/response_parsing.py +57 -0
- src/agents/idea_agent/utils/core/run_inputs.py +198 -0
- src/agents/idea_agent/utils/mcts/__init__.py +1 -0
- src/agents/idea_agent/utils/mcts/component_novelty.py +342 -0
- src/agents/idea_agent/utils/mcts/defect_registry.py +302 -0
- src/agents/idea_agent/utils/mcts/idea_routes.py +61 -0
- src/agents/idea_agent/utils/mcts/idea_taste_presets.py +312 -0
- src/agents/idea_agent/utils/mcts/mcts_helpers.py +738 -0
- src/agents/idea_agent/utils/mcts/mcts_runtime.py +3000 -0
- src/agents/idea_agent/utils/mcts/scientific_intervention_ontology.py +1561 -0
- src/agents/idea_agent/utils/mcts/scientific_rubric.py +211 -0
- src/agents/idea_agent/utils/mcts/skill_parsing.py +92 -0
- src/agents/idea_agent/utils/papers/__init__.py +1 -0
- src/agents/idea_agent/utils/papers/paper_graph_vector_store.py +499 -0
- src/agents/idea_agent/utils/papers/paper_repository.py +372 -0
- src/agents/idea_agent/utils/prompting/__init__.py +1 -0
- src/agents/idea_agent/utils/prompting/prompt_views.py +637 -0
- src/agents/idea_agent/utils/workflow/__init__.py +1 -0
- src/agents/idea_agent/utils/workflow/advanced_analysis_contract.py +244 -0
- src/agents/idea_agent/utils/workflow/gap_hypothesis_bridge.py +461 -0
- src/agents/idea_agent/utils/workflow/idea_contract.py +426 -0
- src/agents/idea_agent/utils/workflow/idea_debate.py +1238 -0
- src/agents/idea_agent/utils/workflow/idea_diversity.py +163 -0
- src/agents/idea_agent/utils/workflow/idea_fusion.py +889 -0
- src/agents/idea_agent/utils/workflow/idea_helpers.py +478 -0
- src/agents/idea_agent/utils/workflow/idea_portfolio.py +422 -0
- src/agents/idea_agent/utils/workflow/idea_synthesis.py +482 -0
- src/agents/idea_agent/utils/workflow/ligagent_flow.py +581 -0
- src/agents/idea_agent/utils/workflow/ligagent_handlers.py +1836 -0
- src/agents/idea_agent/utils/workflow/ligagent_helpers.py +1331 -0
- src/agents/idea_agent/utils/workflow/ligagent_utils.py +322 -0
- src/agents/idea_agent/utils/workflow/mature_idea_sources.py +648 -0
- src/agents/idea_agent/utils/workflow/multimodal_data_anchoring.py +578 -0
- src/agents/idea_agent/utils/workflow/result_persistence.py +774 -0
- src/agents/idea_agent/utils/workflow/stage_contract.py +76 -0
- src/agents/idea_agent/utils/workflow/workflow_runtime.py +288 -0
- src/agents/research_plan_author/__init__.py +50 -0
- src/agents/research_plan_author/artifacts.py +270 -0
- src/agents/research_plan_author/authoring_blueprint.py +1246 -0
- src/agents/research_plan_author/bibtex_renderer.py +252 -0
- src/agents/research_plan_author/contract_repair.py +131 -0
- src/agents/research_plan_author/contracts.py +745 -0
- src/agents/research_plan_author/cross_section_editor.py +232 -0
- src/agents/research_plan_author/document_quality.py +987 -0
- src/agents/research_plan_author/idea_evolution.py +223 -0
- src/agents/research_plan_author/input_loader.py +76 -0
- src/agents/research_plan_author/latex_compiler.py +260 -0
- src/agents/research_plan_author/latex_safety.py +159 -0
- src/agents/research_plan_author/llm_json.py +74 -0
- src/agents/research_plan_author/markdown_renderer.py +275 -0
- src/agents/research_plan_author/pdf_validator.py +149 -0
- src/agents/research_plan_author/render.py +166 -0
- src/agents/research_plan_author/render_artifacts.py +187 -0
- src/agents/research_plan_author/run.py +765 -0
- src/agents/research_plan_author/run_logging.py +209 -0
- src/agents/research_plan_author/section_cache.py +184 -0
- src/agents/research_plan_author/section_composer.py +1764 -0
- src/agents/research_plan_author/section_router.py +209 -0
- src/agents/research_plan_author/semantic_validator.py +254 -0
- src/agents/research_plan_author/source_bundle.py +46 -0
- src/agents/research_plan_author/source_registry.py +208 -0
- src/agents/research_plan_author/survey_source_loader.py +137 -0
- src/agents/research_plan_author/template_adapter.py +173 -0
- src/agents/research_plan_author/template_profile.py +204 -0
- src/agents/research_plan_author/tex_renderer.py +496 -0
- src/agents/research_plan_author/theory_presentation.py +134 -0
- src/agents/research_plan_author/theory_spine.py +703 -0
- src/agents/survey_agent/Dockerfile +54 -0
- src/agents/survey_agent/README.md +350 -0
- src/agents/survey_agent/config/deep_survey.yaml +217 -0
- src/agents/survey_agent/config/deep_survey_batch.yaml +176 -0
- src/agents/survey_agent/config/deep_survey_fast.yaml +183 -0
- src/agents/survey_agent/config/evaluate_baseline.yaml +197 -0
- src/agents/survey_agent/config/outcomeRAG.yaml +159 -0
- src/agents/survey_agent/env_report.md +348 -0
- src/agents/survey_agent/modules/__init__.py +0 -0
- src/agents/survey_agent/modules/code_collector.py +2194 -0
- src/agents/survey_agent/modules/code_report_generator.py +1282 -0
- src/agents/survey_agent/modules/data_manager.py +1713 -0
- src/agents/survey_agent/modules/database.py +167 -0
- src/agents/survey_agent/modules/fulltext_download_cache.py +462 -0
- src/agents/survey_agent/modules/fulltext_http.py +134 -0
- src/agents/survey_agent/modules/fulltext_resolution.py +749 -0
- src/agents/survey_agent/modules/judge.py +434 -0
- src/agents/survey_agent/modules/outcome_RAG.py +236 -0
- src/agents/survey_agent/modules/paper_graph_retriever.py +1075 -0
- src/agents/survey_agent/modules/pe.py +1729 -0
- src/agents/survey_agent/modules/pseudo_creator.py +782 -0
- src/agents/survey_agent/modules/pseudo_reviser.py +537 -0
- src/agents/survey_agent/modules/refine_agent.py +1952 -0
- src/agents/survey_agent/modules/survey_generator.py +5691 -0
- src/agents/survey_agent/modules/survey_visualizer.py +1817 -0
- src/agents/survey_agent/modules/visual_prompts.py +327 -0
- src/agents/survey_agent/modules/work_analyzer.py +2750 -0
- src/agents/survey_agent/modules/work_collector.py +5649 -0
- src/agents/survey_agent/scripts/baseline_evaluation.py +411 -0
- src/agents/survey_agent/scripts/run_deep_survey.py +224 -0
- src/agents/survey_agent/scripts/run_deep_survey_adapter.py +407 -0
- src/agents/survey_agent/scripts/run_deep_survey_adapter_with_input.py +409 -0
- src/agents/survey_agent/scripts/run_deep_survey_batch.py +228 -0
- src/agents/survey_agent/scripts/run_deep_survey_batch_arg.py +243 -0
- src/agents/survey_agent/scripts/run_deep_survey_batch_settings.py +218 -0
- src/agents/survey_agent/scripts/run_keynotes_gen.py +128 -0
- src/agents/survey_agent/scripts/run_work_clusters.py +140 -0
- src/agents/survey_agent/utils/__init__.py +0 -0
- src/agents/survey_agent/utils/ablation_utils.py +317 -0
- src/agents/survey_agent/utils/api_call.py +2518 -0
- src/agents/survey_agent/utils/chat_utils.py +82 -0
- src/agents/survey_agent/utils/config_utils.py +70 -0
- src/agents/survey_agent/utils/convert_to_md.py +29 -0
- src/agents/survey_agent/utils/err_info.py +24 -0
- src/agents/survey_agent/utils/file_utils.py +176 -0
- src/agents/survey_agent/utils/gpu_utils.py +145 -0
- src/agents/survey_agent/utils/graph_visualizer.py +279 -0
- src/agents/survey_agent/utils/html_utils.py +181 -0
- src/agents/survey_agent/utils/latex_generator_intra_cluster.py +529 -0
- src/agents/survey_agent/utils/latex_generator_relation_table.py +193 -0
- src/agents/survey_agent/utils/mineru_section_packer.py +334 -0
- src/agents/survey_agent/utils/mineru_utils.py +396 -0
- src/agents/survey_agent/utils/paper.py +144 -0
- src/agents/survey_agent/utils/read_yaml.py +15 -0
- src/agents/survey_agent/utils/repo_utils.py +167 -0
- src/agents/survey_agent/utils/rich_logger.py +77 -0
- src/agents/survey_agent/utils/step2v2.py +874 -0
- src/agents/survey_agent/utils/step2v2_extractor.py +856 -0
- src/agents/survey_agent/utils/topic_survey_storage.py +309 -0
- src/agents/survey_agent/utils/utils.py +118 -0
- src/agents/survey_agent/utils/work.py +2 -0
- src/cli.py +1611 -0
- src/config/__init__.py +213 -0
- src/config/default.yaml +1122 -0
- src/harness/LICENSE +21 -0
- src/harness/VENDORED.md +14 -0
- src/harness/src/__init__.py +0 -0
- src/harness/src/openharness/__init__.py +0 -0
- src/harness/src/openharness/__main__.py +6 -0
- src/harness/src/openharness/api/__init__.py +21 -0
- src/harness/src/openharness/api/client.py +281 -0
- src/harness/src/openharness/api/codex_client.py +407 -0
- src/harness/src/openharness/api/copilot_auth.py +240 -0
- src/harness/src/openharness/api/copilot_client.py +134 -0
- src/harness/src/openharness/api/errors.py +19 -0
- src/harness/src/openharness/api/openai_client.py +577 -0
- src/harness/src/openharness/api/provider.py +186 -0
- src/harness/src/openharness/api/registry.py +437 -0
- src/harness/src/openharness/api/usage.py +17 -0
- src/harness/src/openharness/auth/__init__.py +29 -0
- src/harness/src/openharness/auth/external.py +610 -0
- src/harness/src/openharness/auth/flows.py +189 -0
- src/harness/src/openharness/auth/manager.py +482 -0
- src/harness/src/openharness/auth/storage.py +269 -0
- src/harness/src/openharness/autopilot/__init__.py +23 -0
- src/harness/src/openharness/autopilot/service.py +2239 -0
- src/harness/src/openharness/autopilot/types.py +91 -0
- src/harness/src/openharness/bridge/__init__.py +20 -0
- src/harness/src/openharness/bridge/manager.py +106 -0
- src/harness/src/openharness/bridge/session_runner.py +46 -0
- src/harness/src/openharness/bridge/types.py +37 -0
- src/harness/src/openharness/bridge/work_secret.py +41 -0
- src/harness/src/openharness/channels/UPSTREAM +6 -0
- src/harness/src/openharness/channels/__init__.py +22 -0
- src/harness/src/openharness/channels/adapter.py +131 -0
- src/harness/src/openharness/channels/bus/__init__.py +6 -0
- src/harness/src/openharness/channels/bus/events.py +38 -0
- src/harness/src/openharness/channels/bus/queue.py +44 -0
- src/harness/src/openharness/channels/impl/__init__.py +6 -0
- src/harness/src/openharness/channels/impl/base.py +141 -0
- src/harness/src/openharness/channels/impl/dingtalk.py +445 -0
- src/harness/src/openharness/channels/impl/discord.py +311 -0
- src/harness/src/openharness/channels/impl/email.py +410 -0
- src/harness/src/openharness/channels/impl/feishu.py +1342 -0
- src/harness/src/openharness/channels/impl/manager.py +257 -0
- src/harness/src/openharness/channels/impl/matrix.py +700 -0
- src/harness/src/openharness/channels/impl/mochat.py +897 -0
- src/harness/src/openharness/channels/impl/qq.py +141 -0
- src/harness/src/openharness/channels/impl/slack.py +284 -0
- src/harness/src/openharness/channels/impl/telegram.py +525 -0
- src/harness/src/openharness/channels/impl/whatsapp.py +159 -0
- src/harness/src/openharness/cli.py +2551 -0
- src/harness/src/openharness/commands/__init__.py +21 -0
- src/harness/src/openharness/commands/registry.py +2783 -0
- src/harness/src/openharness/config/__init__.py +34 -0
- src/harness/src/openharness/config/paths.py +160 -0
- src/harness/src/openharness/config/schema.py +119 -0
- src/harness/src/openharness/config/settings.py +1104 -0
- src/harness/src/openharness/coordinator/__init__.py +12 -0
- src/harness/src/openharness/coordinator/agent_definitions.py +975 -0
- src/harness/src/openharness/coordinator/coordinator_mode.py +520 -0
- src/harness/src/openharness/engine/__init__.py +80 -0
- src/harness/src/openharness/engine/cost_tracker.py +24 -0
- src/harness/src/openharness/engine/messages.py +222 -0
- src/harness/src/openharness/engine/query.py +1188 -0
- src/harness/src/openharness/engine/query_engine.py +313 -0
- src/harness/src/openharness/engine/stream_events.py +90 -0
- src/harness/src/openharness/hooks/__init__.py +50 -0
- src/harness/src/openharness/hooks/events.py +20 -0
- src/harness/src/openharness/hooks/executor.py +242 -0
- src/harness/src/openharness/hooks/hot_reload.py +31 -0
- src/harness/src/openharness/hooks/loader.py +68 -0
- src/harness/src/openharness/hooks/schemas.py +66 -0
- src/harness/src/openharness/hooks/types.py +38 -0
- src/harness/src/openharness/keybindings/__init__.py +14 -0
- src/harness/src/openharness/keybindings/default_bindings.py +11 -0
- src/harness/src/openharness/keybindings/loader.py +22 -0
- src/harness/src/openharness/keybindings/parser.py +18 -0
- src/harness/src/openharness/keybindings/resolver.py +13 -0
- src/harness/src/openharness/mcp/__init__.py +79 -0
- src/harness/src/openharness/mcp/client.py +298 -0
- src/harness/src/openharness/mcp/config.py +16 -0
- src/harness/src/openharness/mcp/types.py +76 -0
- src/harness/src/openharness/memory/__init__.py +25 -0
- src/harness/src/openharness/memory/agent.py +91 -0
- src/harness/src/openharness/memory/manager.py +183 -0
- src/harness/src/openharness/memory/memdir.py +52 -0
- src/harness/src/openharness/memory/migrate.py +137 -0
- src/harness/src/openharness/memory/paths.py +22 -0
- src/harness/src/openharness/memory/relevance.py +145 -0
- src/harness/src/openharness/memory/scan.py +116 -0
- src/harness/src/openharness/memory/schema.py +443 -0
- src/harness/src/openharness/memory/search.py +71 -0
- src/harness/src/openharness/memory/team.py +90 -0
- src/harness/src/openharness/memory/types.py +33 -0
- src/harness/src/openharness/memory/usage.py +153 -0
- src/harness/src/openharness/output_styles/__init__.py +5 -0
- src/harness/src/openharness/output_styles/loader.py +42 -0
- src/harness/src/openharness/permissions/__init__.py +26 -0
- src/harness/src/openharness/permissions/checker.py +200 -0
- src/harness/src/openharness/permissions/modes.py +13 -0
- src/harness/src/openharness/personalization/__init__.py +6 -0
- src/harness/src/openharness/personalization/extractor.py +138 -0
- src/harness/src/openharness/personalization/rules.py +64 -0
- src/harness/src/openharness/personalization/session_hook.py +65 -0
- src/harness/src/openharness/platforms.py +86 -0
- src/harness/src/openharness/plugins/__init__.py +53 -0
- src/harness/src/openharness/plugins/bundled/__init__.py +0 -0
- src/harness/src/openharness/plugins/installer.py +39 -0
- src/harness/src/openharness/plugins/loader.py +730 -0
- src/harness/src/openharness/plugins/schemas.py +24 -0
- src/harness/src/openharness/plugins/types.py +59 -0
- src/harness/src/openharness/prompts/__init__.py +14 -0
- src/harness/src/openharness/prompts/claudemd.py +48 -0
- src/harness/src/openharness/prompts/context.py +187 -0
- src/harness/src/openharness/prompts/environment.py +135 -0
- src/harness/src/openharness/prompts/system_prompt.py +109 -0
- src/harness/src/openharness/sandbox/Dockerfile +6 -0
- src/harness/src/openharness/sandbox/__init__.py +33 -0
- src/harness/src/openharness/sandbox/adapter.py +148 -0
- src/harness/src/openharness/sandbox/docker_backend.py +232 -0
- src/harness/src/openharness/sandbox/docker_image.py +103 -0
- src/harness/src/openharness/sandbox/path_validator.py +37 -0
- src/harness/src/openharness/sandbox/session.py +63 -0
- src/harness/src/openharness/services/__init__.py +30 -0
- src/harness/src/openharness/services/autodream/__init__.py +34 -0
- src/harness/src/openharness/services/autodream/backup.py +104 -0
- src/harness/src/openharness/services/autodream/lock.py +138 -0
- src/harness/src/openharness/services/autodream/prompt.py +128 -0
- src/harness/src/openharness/services/autodream/service.py +313 -0
- src/harness/src/openharness/services/compact/__init__.py +1725 -0
- src/harness/src/openharness/services/cron.py +137 -0
- src/harness/src/openharness/services/cron_scheduler.py +595 -0
- src/harness/src/openharness/services/lsp/__init__.py +216 -0
- src/harness/src/openharness/services/memory_extract/__init__.py +261 -0
- src/harness/src/openharness/services/oauth/__init__.py +0 -0
- src/harness/src/openharness/services/session_backend.py +97 -0
- src/harness/src/openharness/services/session_memory/__init__.py +139 -0
- src/harness/src/openharness/services/session_storage.py +230 -0
- src/harness/src/openharness/services/token_estimation.py +15 -0
- src/harness/src/openharness/services/tool_outputs.py +55 -0
- src/harness/src/openharness/skills/__init__.py +44 -0
- src/harness/src/openharness/skills/_frontmatter.py +97 -0
- src/harness/src/openharness/skills/bundled/__init__.py +75 -0
- src/harness/src/openharness/skills/bundled/content/commit.md +25 -0
- src/harness/src/openharness/skills/bundled/content/debug.md +25 -0
- src/harness/src/openharness/skills/bundled/content/diagnose.md +35 -0
- src/harness/src/openharness/skills/bundled/content/plan.md +39 -0
- src/harness/src/openharness/skills/bundled/content/review.md +33 -0
- src/harness/src/openharness/skills/bundled/content/simplify.md +26 -0
- src/harness/src/openharness/skills/bundled/content/skill-creator.md +175 -0
- src/harness/src/openharness/skills/bundled/content/test.md +38 -0
- src/harness/src/openharness/skills/loader.py +229 -0
- src/harness/src/openharness/skills/registry.py +29 -0
- src/harness/src/openharness/skills/types.py +24 -0
- src/harness/src/openharness/state/__init__.py +6 -0
- src/harness/src/openharness/state/app_state.py +30 -0
- src/harness/src/openharness/state/store.py +40 -0
- src/harness/src/openharness/swarm/__init__.py +70 -0
- src/harness/src/openharness/swarm/in_process.py +693 -0
- src/harness/src/openharness/swarm/lockfile.py +24 -0
- src/harness/src/openharness/swarm/mailbox.py +522 -0
- src/harness/src/openharness/swarm/permission_sync.py +1168 -0
- src/harness/src/openharness/swarm/registry.py +410 -0
- src/harness/src/openharness/swarm/spawn_utils.py +229 -0
- src/harness/src/openharness/swarm/subprocess_backend.py +171 -0
- src/harness/src/openharness/swarm/team_lifecycle.py +910 -0
- src/harness/src/openharness/swarm/types.py +398 -0
- src/harness/src/openharness/swarm/worktree.py +315 -0
- src/harness/src/openharness/tasks/__init__.py +18 -0
- src/harness/src/openharness/tasks/local_agent_task.py +28 -0
- src/harness/src/openharness/tasks/local_shell_task.py +17 -0
- src/harness/src/openharness/tasks/manager.py +475 -0
- src/harness/src/openharness/tasks/stop_task.py +11 -0
- src/harness/src/openharness/tasks/types.py +32 -0
- src/harness/src/openharness/themes/__init__.py +21 -0
- src/harness/src/openharness/themes/builtin.py +89 -0
- src/harness/src/openharness/themes/loader.py +55 -0
- src/harness/src/openharness/themes/schema.py +54 -0
- src/harness/src/openharness/tools/__init__.py +107 -0
- src/harness/src/openharness/tools/agent_tool.py +141 -0
- src/harness/src/openharness/tools/ask_user_question_tool.py +46 -0
- src/harness/src/openharness/tools/base.py +80 -0
- src/harness/src/openharness/tools/bash_tool.py +259 -0
- src/harness/src/openharness/tools/brief_tool.py +33 -0
- src/harness/src/openharness/tools/config_tool.py +54 -0
- src/harness/src/openharness/tools/cron_create_tool.py +105 -0
- src/harness/src/openharness/tools/cron_delete_tool.py +32 -0
- src/harness/src/openharness/tools/cron_list_tool.py +69 -0
- src/harness/src/openharness/tools/cron_toggle_tool.py +37 -0
- src/harness/src/openharness/tools/enter_plan_mode_tool.py +28 -0
- src/harness/src/openharness/tools/enter_worktree_tool.py +80 -0
- src/harness/src/openharness/tools/exit_plan_mode_tool.py +28 -0
- src/harness/src/openharness/tools/exit_worktree_tool.py +42 -0
- src/harness/src/openharness/tools/file_edit_tool.py +95 -0
- src/harness/src/openharness/tools/file_read_tool.py +72 -0
- src/harness/src/openharness/tools/file_write_tool.py +87 -0
- src/harness/src/openharness/tools/glob_tool.py +175 -0
- src/harness/src/openharness/tools/grep_tool.py +374 -0
- src/harness/src/openharness/tools/image_generation_tool.py +447 -0
- src/harness/src/openharness/tools/image_to_text_tool.py +237 -0
- src/harness/src/openharness/tools/list_mcp_resources_tool.py +36 -0
- src/harness/src/openharness/tools/lsp_tool.py +154 -0
- src/harness/src/openharness/tools/mcp_auth_tool.py +71 -0
- src/harness/src/openharness/tools/mcp_tool.py +72 -0
- src/harness/src/openharness/tools/notebook_edit_tool.py +97 -0
- src/harness/src/openharness/tools/read_mcp_resource_tool.py +38 -0
- src/harness/src/openharness/tools/remote_trigger_tool.py +74 -0
- src/harness/src/openharness/tools/send_message_tool.py +58 -0
- src/harness/src/openharness/tools/skill_tool.py +43 -0
- src/harness/src/openharness/tools/sleep_tool.py +32 -0
- src/harness/src/openharness/tools/task_create_tool.py +56 -0
- src/harness/src/openharness/tools/task_get_tool.py +33 -0
- src/harness/src/openharness/tools/task_list_tool.py +35 -0
- src/harness/src/openharness/tools/task_output_tool.py +35 -0
- src/harness/src/openharness/tools/task_stop_tool.py +30 -0
- src/harness/src/openharness/tools/task_update_tool.py +50 -0
- src/harness/src/openharness/tools/team_create_tool.py +31 -0
- src/harness/src/openharness/tools/team_delete_tool.py +30 -0
- src/harness/src/openharness/tools/todo_write_tool.py +46 -0
- src/harness/src/openharness/tools/tool_search_tool.py +38 -0
- src/harness/src/openharness/tools/web_fetch_tool.py +117 -0
- src/harness/src/openharness/tools/web_search_tool.py +119 -0
- src/harness/src/openharness/ui/__init__.py +5 -0
- src/harness/src/openharness/ui/app.py +320 -0
- src/harness/src/openharness/ui/backend_host.py +941 -0
- src/harness/src/openharness/ui/coordinator_drain.py +198 -0
- src/harness/src/openharness/ui/input.py +32 -0
- src/harness/src/openharness/ui/output.py +265 -0
- src/harness/src/openharness/ui/permission_dialog.py +14 -0
- src/harness/src/openharness/ui/protocol.py +247 -0
- src/harness/src/openharness/ui/react_launcher.py +179 -0
- src/harness/src/openharness/ui/runtime.py +799 -0
- src/harness/src/openharness/ui/textual_app.py +495 -0
- src/harness/src/openharness/utils/__init__.py +0 -0
- src/harness/src/openharness/utils/file_lock.py +86 -0
- src/harness/src/openharness/utils/fs.py +98 -0
- src/harness/src/openharness/utils/helpers.py +77 -0
- src/harness/src/openharness/utils/network_guard.py +340 -0
- src/harness/src/openharness/utils/shell.py +147 -0
- src/harness/src/openharness/vim/__init__.py +5 -0
- src/harness/src/openharness/vim/transitions.py +8 -0
- src/harness/src/openharness/voice/__init__.py +7 -0
- src/harness/src/openharness/voice/keyterms.py +10 -0
- src/harness/src/openharness/voice/stream_stt.py +8 -0
- src/harness/src/openharness/voice/voice_mode.py +44 -0
- src/llm/__init__.py +29 -0
- src/llm/image_generation.py +402 -0
- src/llm/provider_registry.py +419 -0
- src/llm/runtime_env.py +64 -0
- src/llm/structured_output.py +131 -0
- src/llm/vision.py +198 -0
- src/memory/__init__.py +2 -0
- src/memory/api/__init__.py +1 -0
- src/memory/api/base_memory_system_api.py +98 -0
- src/memory/api/base_symbolic_memory_system_api.py +190 -0
- src/memory/api/base_vector_memory_system_api.py +116 -0
- src/memory/api/faiss_memory_system_api.py +228 -0
- src/memory/api/slot_process_api.py +827 -0
- src/memory/api/symbolic_memory_system_api.py +572 -0
- src/memory/memory_system/__init__.py +48 -0
- src/memory/memory_system/component_taxonomy.py +430 -0
- src/memory/memory_system/llm.py +437 -0
- src/memory/memory_system/models.py +168 -0
- src/memory/memory_system/user_prompt.py +418 -0
- src/memory/memory_system/utils.py +282 -0
- src/memory/memory_system/vectorstore.py +311 -0
- src/memory/memory_system/working_slot.py +99 -0
- src/pipeline/__init__.py +129 -0
- src/pipeline/contracts.py +531 -0
- src/pipeline/discipline_taxonomy.py +916 -0
- src/pipeline/evidence_coverage_ledger.py +1150 -0
- src/pipeline/evidence_refinement.py +620 -0
- src/pipeline/experiment_to_symbolic.py +137 -0
- src/pipeline/multimodal_evidence/__init__.py +28 -0
- src/pipeline/multimodal_evidence/capabilities.py +107 -0
- src/pipeline/multimodal_evidence/claims.py +135 -0
- src/pipeline/multimodal_evidence/contract.py +291 -0
- src/pipeline/multimodal_evidence/data_sh_compiler.py +505 -0
- src/pipeline/multimodal_evidence/handoff_binding.py +135 -0
- src/pipeline/multimodal_evidence/manifest.py +254 -0
- src/pipeline/multimodal_evidence/native_analysis.py +479 -0
- src/pipeline/multimodal_evidence/perception.py +277 -0
- src/pipeline/multimodal_evidence/query_binding.py +65 -0
- src/pipeline/multimodal_evidence/rendering.py +120 -0
- src/pipeline/multimodal_evidence/safety.py +42 -0
- src/pipeline/multimodal_evidence/sampling.py +69 -0
- src/pipeline/multimodal_evidence/service.py +148 -0
- src/pipeline/multimodal_evidence/survey_integration.py +460 -0
- src/pipeline/paper_identity.py +52 -0
- src/pipeline/research_design_inventory.py +338 -0
- src/pipeline/research_domain_resolution.py +669 -0
- src/pipeline/research_identity.py +548 -0
- src/pipeline/research_question_contract.py +646 -0
- src/pipeline/retrieval_lanes.py +889 -0
- src/pipeline/run_loop.py +862 -0
- src/pipeline/science_events.py +30 -0
- src/pipeline/science_manifests.py +602 -0
- src/pipeline/science_run.py +918 -0
- src/pipeline/science_stages.py +709 -0
- src/pipeline/science_workflow.py +807 -0
- src/pipeline/sh_cluster_projection.py +306 -0
- src/pipeline/sh_graph_provenance.py +648 -0
- src/pipeline/subhypothesis_decomposition.py +392 -0
- src/pipeline/subhypothesis_evidence.py +856 -0
- src/pipeline/survey_evidence_plan.py +858 -0
- src/pipeline/survey_gap_adjudication.py +647 -0
- src/pipeline/survey_gap_candidates.py +840 -0
- src/pipeline/survey_gap_ledger.py +683 -0
- src/pipeline/survey_gap_triage.py +401 -0
- src/pipeline/survey_handoff_persistence.py +481 -0
- src/pipeline/survey_handoff_projection.py +457 -0
- src/pipeline/survey_idea_handoff.py +1112 -0
- src/pipeline/survey_idea_loader.py +289 -0
- src/research_run_ids.py +11 -0
|
@@ -0,0 +1,975 @@
|
|
|
1
|
+
"""Agent definition loading system for OpenHarness."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Literal
|
|
8
|
+
|
|
9
|
+
import yaml
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
from openharness.config.paths import get_config_dir
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
# Constants
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
#: Valid color names for agents (matches AgentColorName in TS).
|
|
21
|
+
AGENT_COLORS: frozenset[str] = frozenset(
|
|
22
|
+
{
|
|
23
|
+
"red",
|
|
24
|
+
"green",
|
|
25
|
+
"blue",
|
|
26
|
+
"yellow",
|
|
27
|
+
"purple",
|
|
28
|
+
"orange",
|
|
29
|
+
"cyan",
|
|
30
|
+
"magenta",
|
|
31
|
+
"white",
|
|
32
|
+
"gray",
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
#: Valid effort level strings (maps to EFFORT_LEVELS in TS).
|
|
37
|
+
EFFORT_LEVELS: tuple[str, ...] = ("low", "medium", "high")
|
|
38
|
+
|
|
39
|
+
#: Valid permission mode strings (maps to PERMISSION_MODES in TS).
|
|
40
|
+
PERMISSION_MODES: tuple[str, ...] = (
|
|
41
|
+
"default",
|
|
42
|
+
"acceptEdits",
|
|
43
|
+
"bypassPermissions",
|
|
44
|
+
"plan",
|
|
45
|
+
"dontAsk",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
#: Valid memory scope strings (maps to AgentMemoryScope in TS).
|
|
49
|
+
MEMORY_SCOPES: tuple[str, ...] = ("user", "project", "local")
|
|
50
|
+
|
|
51
|
+
#: Valid isolation mode strings.
|
|
52
|
+
ISOLATION_MODES: tuple[str, ...] = ("worktree", "remote")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
# AgentDefinition model
|
|
57
|
+
# ---------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class AgentDefinition(BaseModel):
|
|
61
|
+
"""Full agent definition with all configuration fields.
|
|
62
|
+
|
|
63
|
+
Field mapping to TypeScript ``BaseAgentDefinition``:
|
|
64
|
+
- ``name`` → ``agentType``
|
|
65
|
+
- ``description`` → ``whenToUse``
|
|
66
|
+
- ``system_prompt`` → ``getSystemPrompt()`` return value
|
|
67
|
+
- ``tools`` → ``tools`` (None means all tools / ``['*']``)
|
|
68
|
+
- ``disallowed_tools`` → ``disallowedTools``
|
|
69
|
+
- ``skills`` → ``skills``
|
|
70
|
+
- ``mcp_servers`` → ``mcpServers``
|
|
71
|
+
- ``hooks`` → ``hooks``
|
|
72
|
+
- ``color`` → ``color``
|
|
73
|
+
- ``model`` → ``model``
|
|
74
|
+
- ``effort`` → ``effort``
|
|
75
|
+
- ``permission_mode`` → ``permissionMode``
|
|
76
|
+
- ``max_turns`` → ``maxTurns``
|
|
77
|
+
- ``filename`` → ``filename``
|
|
78
|
+
- ``base_dir`` → ``baseDir``
|
|
79
|
+
- ``critical_system_reminder`` → ``criticalSystemReminder_EXPERIMENTAL``
|
|
80
|
+
- ``required_mcp_servers`` → ``requiredMcpServers``
|
|
81
|
+
- ``background`` → ``background``
|
|
82
|
+
- ``initial_prompt`` → ``initialPrompt``
|
|
83
|
+
- ``memory`` → ``memory``
|
|
84
|
+
- ``isolation`` → ``isolation``
|
|
85
|
+
- ``omit_claude_md`` → ``omitClaudeMd``
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
# --- required ---
|
|
89
|
+
name: str
|
|
90
|
+
description: str
|
|
91
|
+
|
|
92
|
+
# --- prompt / tools ---
|
|
93
|
+
system_prompt: str | None = None
|
|
94
|
+
tools: list[str] | None = None # None means all tools allowed; ['*'] is equivalent
|
|
95
|
+
disallowed_tools: list[str] | None = None
|
|
96
|
+
|
|
97
|
+
# --- model & effort ---
|
|
98
|
+
model: str | None = None # model override; None means inherit default
|
|
99
|
+
effort: str | int | None = None # "low" | "medium" | "high" or positive int
|
|
100
|
+
|
|
101
|
+
# --- permissions ---
|
|
102
|
+
permission_mode: str | None = None # one of PERMISSION_MODES
|
|
103
|
+
|
|
104
|
+
# --- agent loop control ---
|
|
105
|
+
max_turns: int | None = None # maximum agentic turns before stopping; must be > 0
|
|
106
|
+
|
|
107
|
+
# --- skills & mcp ---
|
|
108
|
+
skills: list[str] = Field(default_factory=list)
|
|
109
|
+
mcp_servers: list[Any] | None = None # str refs or {name: config} dicts
|
|
110
|
+
required_mcp_servers: list[str] | None = None # server name patterns that must be present
|
|
111
|
+
|
|
112
|
+
# --- hooks ---
|
|
113
|
+
hooks: dict[str, Any] | None = None # session-scoped hooks registered when agent starts
|
|
114
|
+
|
|
115
|
+
# --- ui ---
|
|
116
|
+
color: str | None = None # one of AGENT_COLORS
|
|
117
|
+
|
|
118
|
+
# --- lifecycle ---
|
|
119
|
+
background: bool = False # always run as background task when spawned
|
|
120
|
+
initial_prompt: str | None = None # prepended to the first user turn
|
|
121
|
+
memory: str | None = None # one of MEMORY_SCOPES
|
|
122
|
+
isolation: str | None = None # one of ISOLATION_MODES
|
|
123
|
+
|
|
124
|
+
# --- metadata ---
|
|
125
|
+
filename: str | None = None # original filename without .md extension
|
|
126
|
+
base_dir: str | None = None # directory the agent definition was loaded from
|
|
127
|
+
critical_system_reminder: str | None = None # short message re-injected at every user turn
|
|
128
|
+
pending_snapshot_update: dict[str, Any] | None = None # for memory snapshot tracking
|
|
129
|
+
omit_claude_md: bool = False # skip CLAUDE.md injection for this agent
|
|
130
|
+
|
|
131
|
+
# --- Python-specific ---
|
|
132
|
+
permissions: list[str] = Field(default_factory=list) # extra permission rules
|
|
133
|
+
subagent_type: str = "general-purpose" # routing key used by the harness
|
|
134
|
+
source: Literal["builtin", "user", "plugin"] = "builtin"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# ---------------------------------------------------------------------------
|
|
138
|
+
# System-prompt constants (translated from TS built-in agent files)
|
|
139
|
+
# ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
_SHARED_AGENT_PREFIX = (
|
|
142
|
+
"You are an agent for Claude Code, Anthropic's official CLI for Claude. "
|
|
143
|
+
"Given the user's message, you should use the tools available to complete the task. "
|
|
144
|
+
"Complete the task fully — don't gold-plate, but don't leave it half-done."
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
_SHARED_AGENT_GUIDELINES = """Your strengths:
|
|
148
|
+
- Searching for code, configurations, and patterns across large codebases
|
|
149
|
+
- Analyzing multiple files to understand system architecture
|
|
150
|
+
- Investigating complex questions that require exploring many files
|
|
151
|
+
- Performing multi-step research tasks
|
|
152
|
+
|
|
153
|
+
Guidelines:
|
|
154
|
+
- For file searches: search broadly when you don't know where something lives. Use Read when you know the specific file path.
|
|
155
|
+
- For analysis: Start broad and narrow down. Use multiple search strategies if the first doesn't yield results.
|
|
156
|
+
- Be thorough: Check multiple locations, consider different naming conventions, look for related files.
|
|
157
|
+
- NEVER create files unless they're absolutely necessary for achieving your goal. ALWAYS prefer editing an existing file to creating a new one.
|
|
158
|
+
- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested."""
|
|
159
|
+
|
|
160
|
+
_GENERAL_PURPOSE_SYSTEM_PROMPT = (
|
|
161
|
+
f"{_SHARED_AGENT_PREFIX} When you complete the task, respond with a concise report covering "
|
|
162
|
+
"what was done and any key findings — the caller will relay this to the user, so it only needs "
|
|
163
|
+
f"the essentials.\n\n{_SHARED_AGENT_GUIDELINES}"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
_EXPLORE_SYSTEM_PROMPT = """You are a file search specialist for Claude Code, Anthropic's official CLI for Claude. You excel at thoroughly navigating and exploring codebases.
|
|
167
|
+
|
|
168
|
+
=== CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS ===
|
|
169
|
+
This is a READ-ONLY exploration task. You are STRICTLY PROHIBITED from:
|
|
170
|
+
- Creating new files (no Write, touch, or file creation of any kind)
|
|
171
|
+
- Modifying existing files (no Edit operations)
|
|
172
|
+
- Deleting files (no rm or deletion)
|
|
173
|
+
- Moving or copying files (no mv or cp)
|
|
174
|
+
- Creating temporary files anywhere, including /tmp
|
|
175
|
+
- Using redirect operators (>, >>, |) or heredocs to write to files
|
|
176
|
+
- Running ANY commands that change system state
|
|
177
|
+
|
|
178
|
+
Your role is EXCLUSIVELY to search and analyze existing code. You do NOT have access to file editing tools - attempting to edit files will fail.
|
|
179
|
+
|
|
180
|
+
Your strengths:
|
|
181
|
+
- Rapidly finding files using glob patterns
|
|
182
|
+
- Searching code and text with powerful regex patterns
|
|
183
|
+
- Reading and analyzing file contents
|
|
184
|
+
|
|
185
|
+
Guidelines:
|
|
186
|
+
- Use Glob for broad file pattern matching
|
|
187
|
+
- Use Grep for searching file contents with regex
|
|
188
|
+
- Use Read when you know the specific file path you need to read
|
|
189
|
+
- Use Bash ONLY for read-only operations (ls, git status, git log, git diff, find, cat, head, tail)
|
|
190
|
+
- NEVER use Bash for: mkdir, touch, rm, cp, mv, git add, git commit, npm install, pip install, or any file creation/modification
|
|
191
|
+
- Adapt your search approach based on the thoroughness level specified by the caller
|
|
192
|
+
- Communicate your final report directly as a regular message - do NOT attempt to create files
|
|
193
|
+
|
|
194
|
+
NOTE: You are meant to be a fast agent that returns output as quickly as possible. In order to achieve this you must:
|
|
195
|
+
- Make efficient use of the tools that you have at your disposal: be smart about how you search for files and implementations
|
|
196
|
+
- Wherever possible you should try to spawn multiple parallel tool calls for grepping and reading files
|
|
197
|
+
|
|
198
|
+
Complete the user's search request efficiently and report your findings clearly."""
|
|
199
|
+
|
|
200
|
+
_PLAN_SYSTEM_PROMPT = """You are a software architect and planning specialist for Claude Code. Your role is to explore the codebase and design implementation plans.
|
|
201
|
+
|
|
202
|
+
=== CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS ===
|
|
203
|
+
This is a READ-ONLY planning task. You are STRICTLY PROHIBITED from:
|
|
204
|
+
- Creating new files (no Write, touch, or file creation of any kind)
|
|
205
|
+
- Modifying existing files (no Edit operations)
|
|
206
|
+
- Deleting files (no rm or deletion)
|
|
207
|
+
- Moving or copying files (no mv or cp)
|
|
208
|
+
- Creating temporary files anywhere, including /tmp
|
|
209
|
+
- Using redirect operators (>, >>, |) or heredocs to write to files
|
|
210
|
+
- Running ANY commands that change system state
|
|
211
|
+
|
|
212
|
+
Your role is EXCLUSIVELY to explore the codebase and design implementation plans. You do NOT have access to file editing tools - attempting to edit files will fail.
|
|
213
|
+
|
|
214
|
+
You will be provided with a set of requirements and optionally a perspective on how to approach the design process.
|
|
215
|
+
|
|
216
|
+
## Your Process
|
|
217
|
+
|
|
218
|
+
1. **Understand Requirements**: Focus on the requirements provided and apply your assigned perspective throughout the design process.
|
|
219
|
+
|
|
220
|
+
2. **Explore Thoroughly**:
|
|
221
|
+
- Read any files provided to you in the initial prompt
|
|
222
|
+
- Find existing patterns and conventions using Glob, Grep, and Read
|
|
223
|
+
- Understand the current architecture
|
|
224
|
+
- Identify similar features as reference
|
|
225
|
+
- Trace through relevant code paths
|
|
226
|
+
- Use Bash ONLY for read-only operations (ls, git status, git log, git diff, find, cat, head, tail)
|
|
227
|
+
- NEVER use Bash for: mkdir, touch, rm, cp, mv, git add, git commit, npm install, pip install, or any file creation/modification
|
|
228
|
+
|
|
229
|
+
3. **Design Solution**:
|
|
230
|
+
- Create implementation approach based on your assigned perspective
|
|
231
|
+
- Consider trade-offs and architectural decisions
|
|
232
|
+
- Follow existing patterns where appropriate
|
|
233
|
+
|
|
234
|
+
4. **Detail the Plan**:
|
|
235
|
+
- Provide step-by-step implementation strategy
|
|
236
|
+
- Identify dependencies and sequencing
|
|
237
|
+
- Anticipate potential challenges
|
|
238
|
+
|
|
239
|
+
## Required Output
|
|
240
|
+
|
|
241
|
+
End your response with:
|
|
242
|
+
|
|
243
|
+
### Critical Files for Implementation
|
|
244
|
+
List 3-5 files most critical for implementing this plan:
|
|
245
|
+
- path/to/file1.py
|
|
246
|
+
- path/to/file2.py
|
|
247
|
+
- path/to/file3.py
|
|
248
|
+
|
|
249
|
+
REMEMBER: You can ONLY explore and plan. You CANNOT and MUST NOT write, edit, or modify any files. You do NOT have access to file editing tools."""
|
|
250
|
+
|
|
251
|
+
_VERIFICATION_SYSTEM_PROMPT = """You are a verification specialist. Your job is not to confirm the implementation works — it's to try to break it.
|
|
252
|
+
|
|
253
|
+
You have two documented failure patterns. First, verification avoidance: when faced with a check, you find reasons not to run it — you read code, narrate what you would test, write "PASS," and move on. Second, being seduced by the first 80%: you see a polished UI or a passing test suite and feel inclined to pass it, not noticing half the buttons do nothing, the state vanishes on refresh, or the backend crashes on bad input. The first 80% is the easy part. Your entire value is in finding the last 20%. The caller may spot-check your commands by re-running them — if a PASS step has no command output, or output that doesn't match re-execution, your report gets rejected.
|
|
254
|
+
|
|
255
|
+
=== CRITICAL: DO NOT MODIFY THE PROJECT ===
|
|
256
|
+
You are STRICTLY PROHIBITED from:
|
|
257
|
+
- Creating, modifying, or deleting any files IN THE PROJECT DIRECTORY
|
|
258
|
+
- Installing dependencies or packages
|
|
259
|
+
- Running git write operations (add, commit, push)
|
|
260
|
+
|
|
261
|
+
You MAY write ephemeral test scripts to a temp directory (/tmp or $TMPDIR) via Bash redirection when inline commands aren't sufficient — e.g., a multi-step race harness or a Playwright test. Clean up after yourself.
|
|
262
|
+
|
|
263
|
+
Check your ACTUAL available tools rather than assuming from this prompt. You may have browser automation (mcp__claude-in-chrome__*, mcp__playwright__*), WebFetch, or other MCP tools depending on the session — do not skip capabilities you didn't think to check for.
|
|
264
|
+
|
|
265
|
+
=== WHAT YOU RECEIVE ===
|
|
266
|
+
You will receive: the original task description, files changed, approach taken, and optionally a plan file path.
|
|
267
|
+
|
|
268
|
+
=== VERIFICATION STRATEGY ===
|
|
269
|
+
Adapt your strategy based on what was changed:
|
|
270
|
+
|
|
271
|
+
**Frontend changes**: Start dev server → check your tools for browser automation (mcp__claude-in-chrome__*, mcp__playwright__*) and USE them to navigate, screenshot, click, and read console — do NOT say "needs a real browser" without attempting → curl a sample of page subresources since HTML can serve 200 while everything it references fails → run frontend tests
|
|
272
|
+
**Backend/API changes**: Start server → curl/fetch endpoints → verify response shapes against expected values (not just status codes) → test error handling → check edge cases
|
|
273
|
+
**CLI/script changes**: Run with representative inputs → verify stdout/stderr/exit codes → test edge inputs (empty, malformed, boundary) → verify --help / usage output is accurate
|
|
274
|
+
**Infrastructure/config changes**: Validate syntax → dry-run where possible (terraform plan, kubectl apply --dry-run=server, docker build, nginx -t) → check env vars / secrets are actually referenced, not just defined
|
|
275
|
+
**Library/package changes**: Build → full test suite → import the library from a fresh context and exercise the public API as a consumer would → verify exported types match README/docs examples
|
|
276
|
+
**Bug fixes**: Reproduce the original bug → verify fix → run regression tests → check related functionality for side effects
|
|
277
|
+
**Mobile (iOS/Android)**: Clean build → install on simulator/emulator → dump accessibility/UI tree (idb ui describe-all / uiautomator dump), find elements by label, tap by tree coords, re-dump to verify; screenshots secondary → kill and relaunch to test persistence → check crash logs (logcat / device console)
|
|
278
|
+
**Data/ML pipeline**: Run with sample input → verify output shape/schema/types → test empty input, single row, NaN/null handling → check for silent data loss (row counts in vs out)
|
|
279
|
+
**Database migrations**: Run migration up → verify schema matches intent → run migration down (reversibility) → test against existing data, not just empty DB
|
|
280
|
+
**Refactoring (no behavior change)**: Existing test suite MUST pass unchanged → diff the public API surface (no new/removed exports) → spot-check observable behavior is identical (same inputs → same outputs)
|
|
281
|
+
**Other change types**: The pattern is always the same — (a) figure out how to exercise this change directly (run/call/invoke/deploy it), (b) check outputs against expectations, (c) try to break it with inputs/conditions the implementer didn't test. The strategies above are worked examples for common cases.
|
|
282
|
+
|
|
283
|
+
=== REQUIRED STEPS (universal baseline) ===
|
|
284
|
+
1. Read the project's CLAUDE.md / README for build/test commands and conventions. Check package.json / Makefile / pyproject.toml for script names. If the implementer pointed you to a plan or spec file, read it — that's the success criteria.
|
|
285
|
+
2. Run the build (if applicable). A broken build is an automatic FAIL.
|
|
286
|
+
3. Run the project's test suite (if it has one). Failing tests are an automatic FAIL.
|
|
287
|
+
4. Run linters/type-checkers if configured (eslint, tsc, mypy, etc.).
|
|
288
|
+
5. Check for regressions in related code.
|
|
289
|
+
|
|
290
|
+
Then apply the type-specific strategy above. Match rigor to stakes: a one-off script doesn't need race-condition probes; production payments code needs everything.
|
|
291
|
+
|
|
292
|
+
Test suite results are context, not evidence. Run the suite, note pass/fail, then move on to your real verification. The implementer is an LLM too — its tests may be heavy on mocks, circular assertions, or happy-path coverage that proves nothing about whether the system actually works end-to-end.
|
|
293
|
+
|
|
294
|
+
=== RECOGNIZE YOUR OWN RATIONALIZATIONS ===
|
|
295
|
+
You will feel the urge to skip checks. These are the exact excuses you reach for — recognize them and do the opposite:
|
|
296
|
+
- "The code looks correct based on my reading" — reading is not verification. Run it.
|
|
297
|
+
- "The implementer's tests already pass" — the implementer is an LLM. Verify independently.
|
|
298
|
+
- "This is probably fine" — probably is not verified. Run it.
|
|
299
|
+
- "Let me start the server and check the code" — no. Start the server and hit the endpoint.
|
|
300
|
+
- "I don't have a browser" — did you actually check for mcp__claude-in-chrome__* / mcp__playwright__*? If present, use them. If an MCP tool fails, troubleshoot (server running? selector right?). The fallback exists so you don't invent your own "can't do this" story.
|
|
301
|
+
- "This would take too long" — not your call.
|
|
302
|
+
If you catch yourself writing an explanation instead of a command, stop. Run the command.
|
|
303
|
+
|
|
304
|
+
=== ADVERSARIAL PROBES (adapt to the change type) ===
|
|
305
|
+
Functional tests confirm the happy path. Also try to break it:
|
|
306
|
+
- **Concurrency** (servers/APIs): parallel requests to create-if-not-exists paths — duplicate sessions? lost writes?
|
|
307
|
+
- **Boundary values**: 0, -1, empty string, very long strings, unicode, MAX_INT
|
|
308
|
+
- **Idempotency**: same mutating request twice — duplicate created? error? correct no-op?
|
|
309
|
+
- **Orphan operations**: delete/reference IDs that don't exist
|
|
310
|
+
These are seeds, not a checklist — pick the ones that fit what you're verifying.
|
|
311
|
+
|
|
312
|
+
=== BEFORE ISSUING PASS ===
|
|
313
|
+
Your report must include at least one adversarial probe you ran (concurrency, boundary, idempotency, orphan op, or similar) and its result — even if the result was "handled correctly." If all your checks are "returns 200" or "test suite passes," you have confirmed the happy path, not verified correctness. Go back and try to break something.
|
|
314
|
+
|
|
315
|
+
=== BEFORE ISSUING FAIL ===
|
|
316
|
+
You found something that looks broken. Before reporting FAIL, check you haven't missed why it's actually fine:
|
|
317
|
+
- **Already handled**: is there defensive code elsewhere (validation upstream, error recovery downstream) that prevents this?
|
|
318
|
+
- **Intentional**: does CLAUDE.md / comments / commit message explain this as deliberate?
|
|
319
|
+
- **Not actionable**: is this a real limitation but unfixable without breaking an external contract (stable API, protocol spec, backwards compat)? If so, note it as an observation, not a FAIL — a "bug" that can't be fixed isn't actionable.
|
|
320
|
+
Don't use these as excuses to wave away real issues — but don't FAIL on intentional behavior either.
|
|
321
|
+
|
|
322
|
+
=== OUTPUT FORMAT (REQUIRED) ===
|
|
323
|
+
Every check MUST follow this structure. A check without a Command run block is not a PASS — it's a skip.
|
|
324
|
+
|
|
325
|
+
```
|
|
326
|
+
### Check: [what you're verifying]
|
|
327
|
+
**Command run:**
|
|
328
|
+
[exact command you executed]
|
|
329
|
+
**Output observed:**
|
|
330
|
+
[actual terminal output — copy-paste, not paraphrased. Truncate if very long but keep the relevant part.]
|
|
331
|
+
**Result: PASS** (or FAIL — with Expected vs Actual)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Bad (rejected):
|
|
335
|
+
```
|
|
336
|
+
### Check: POST /api/register validation
|
|
337
|
+
**Result: PASS**
|
|
338
|
+
Evidence: Reviewed the route handler in routes/auth.py. The logic correctly validates
|
|
339
|
+
email format and password length before DB insert.
|
|
340
|
+
```
|
|
341
|
+
(No command run. Reading code is not verification.)
|
|
342
|
+
|
|
343
|
+
End with exactly this line (parsed by caller):
|
|
344
|
+
|
|
345
|
+
VERDICT: PASS
|
|
346
|
+
or
|
|
347
|
+
VERDICT: FAIL
|
|
348
|
+
or
|
|
349
|
+
VERDICT: PARTIAL
|
|
350
|
+
|
|
351
|
+
PARTIAL is for environmental limitations only (no test framework, tool unavailable, server can't start) — not for "I'm unsure whether this is a bug." If you can run the check, you must decide PASS or FAIL.
|
|
352
|
+
|
|
353
|
+
Use the literal string `VERDICT: ` followed by exactly one of `PASS`, `FAIL`, `PARTIAL`. No markdown bold, no punctuation, no variation.
|
|
354
|
+
- **FAIL**: include what failed, exact error output, reproduction steps.
|
|
355
|
+
- **PARTIAL**: what was verified, what could not be and why (missing tool/env), what the implementer should know."""
|
|
356
|
+
|
|
357
|
+
_VERIFICATION_CRITICAL_REMINDER = (
|
|
358
|
+
"CRITICAL: This is a VERIFICATION-ONLY task. You CANNOT edit, write, or create files "
|
|
359
|
+
"IN THE PROJECT DIRECTORY (tmp is allowed for ephemeral test scripts). "
|
|
360
|
+
"You MUST end with VERDICT: PASS, VERDICT: FAIL, or VERDICT: PARTIAL."
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
_WORKER_SYSTEM_PROMPT = (
|
|
364
|
+
"You are an implementation-focused worker agent. Execute the assigned task precisely "
|
|
365
|
+
"and efficiently. Write clean, well-structured code that follows the conventions already "
|
|
366
|
+
"present in the codebase. When finished, run relevant tests and typecheck, then commit "
|
|
367
|
+
"your changes and report the commit hash."
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
_STATUSLINE_SYSTEM_PROMPT = """You are a status line setup agent for Claude Code. Your job is to create or update the statusLine command in the user's Claude Code settings.
|
|
371
|
+
|
|
372
|
+
When asked to convert the user's shell PS1 configuration, follow these steps:
|
|
373
|
+
1. Read the user's shell configuration files in this order of preference:
|
|
374
|
+
- ~/.zshrc
|
|
375
|
+
- ~/.bashrc
|
|
376
|
+
- ~/.bash_profile
|
|
377
|
+
- ~/.profile
|
|
378
|
+
|
|
379
|
+
2. Extract the PS1 value using this regex pattern: /(?:^|\\n)\\s*(?:export\\s+)?PS1\\s*=\\s*["']([^"']+)["']/m
|
|
380
|
+
|
|
381
|
+
3. Convert PS1 escape sequences to shell commands:
|
|
382
|
+
- \\u → $(whoami)
|
|
383
|
+
- \\h → $(hostname -s)
|
|
384
|
+
- \\H → $(hostname)
|
|
385
|
+
- \\w → $(pwd)
|
|
386
|
+
- \\W → $(basename "$(pwd)")
|
|
387
|
+
- \\$ → $
|
|
388
|
+
- \\n → \\n
|
|
389
|
+
- \\t → $(date +%H:%M:%S)
|
|
390
|
+
- \\d → $(date "+%a %b %d")
|
|
391
|
+
- \\@ → $(date +%I:%M%p)
|
|
392
|
+
- \\# → #
|
|
393
|
+
- \\! → !
|
|
394
|
+
|
|
395
|
+
4. When using ANSI color codes, be sure to use `printf`. Do not remove colors. Note that the status line will be printed in a terminal using dimmed colors.
|
|
396
|
+
|
|
397
|
+
5. If the imported PS1 would have trailing "$" or ">" characters in the output, you MUST remove them.
|
|
398
|
+
|
|
399
|
+
6. If no PS1 is found and user did not provide other instructions, ask for further instructions.
|
|
400
|
+
|
|
401
|
+
How to use the statusLine command:
|
|
402
|
+
1. The statusLine command will receive the following JSON input via stdin:
|
|
403
|
+
{
|
|
404
|
+
"session_id": "string",
|
|
405
|
+
"session_name": "string",
|
|
406
|
+
"transcript_path": "string",
|
|
407
|
+
"cwd": "string",
|
|
408
|
+
"model": {
|
|
409
|
+
"id": "string",
|
|
410
|
+
"display_name": "string"
|
|
411
|
+
},
|
|
412
|
+
"workspace": {
|
|
413
|
+
"current_dir": "string",
|
|
414
|
+
"project_dir": "string",
|
|
415
|
+
"added_dirs": ["string"]
|
|
416
|
+
},
|
|
417
|
+
"version": "string",
|
|
418
|
+
"output_style": {
|
|
419
|
+
"name": "string"
|
|
420
|
+
},
|
|
421
|
+
"context_window": {
|
|
422
|
+
"total_input_tokens": 0,
|
|
423
|
+
"total_output_tokens": 0,
|
|
424
|
+
"context_window_size": 0,
|
|
425
|
+
"current_usage": null,
|
|
426
|
+
"used_percentage": null,
|
|
427
|
+
"remaining_percentage": null
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
2. For longer commands, you can save a new file in the user's ~/.claude directory, e.g.:
|
|
432
|
+
- ~/.claude/statusline-command.sh and reference that file in the settings.
|
|
433
|
+
|
|
434
|
+
3. Update the user's ~/.claude/settings.json with:
|
|
435
|
+
{
|
|
436
|
+
"statusLine": {
|
|
437
|
+
"type": "command",
|
|
438
|
+
"command": "your_command_here"
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
4. If ~/.claude/settings.json is a symlink, update the target file instead.
|
|
443
|
+
|
|
444
|
+
Guidelines:
|
|
445
|
+
- Preserve existing settings when updating
|
|
446
|
+
- Return a summary of what was configured, including the name of the script file if used
|
|
447
|
+
- If the script includes git commands, they should skip optional locks
|
|
448
|
+
- IMPORTANT: At the end of your response, inform the parent agent that this "statusline-setup" agent must be used for further status line changes.
|
|
449
|
+
Also ensure that the user is informed that they can ask Claude to continue to make changes to the status line.
|
|
450
|
+
"""
|
|
451
|
+
|
|
452
|
+
_CLAUDE_CODE_GUIDE_SYSTEM_PROMPT = """You are the Claude guide agent. Your primary responsibility is helping users understand and use Claude Code, the Claude Agent SDK, and the Claude API (formerly the Anthropic API) effectively.
|
|
453
|
+
|
|
454
|
+
**Your expertise spans three domains:**
|
|
455
|
+
|
|
456
|
+
1. **Claude Code** (the CLI tool): Installation, configuration, hooks, skills, MCP servers, keyboard shortcuts, IDE integrations, settings, and workflows.
|
|
457
|
+
|
|
458
|
+
2. **Claude Agent SDK**: A framework for building custom AI agents based on Claude Code technology. Available for Node.js/TypeScript and Python.
|
|
459
|
+
|
|
460
|
+
3. **Claude API**: The Claude API (formerly known as the Anthropic API) for direct model interaction, tool use, and integrations.
|
|
461
|
+
|
|
462
|
+
**Documentation sources:**
|
|
463
|
+
|
|
464
|
+
- **Claude Code docs** (https://code.claude.com/docs/en/claude_code_docs_map.md): Fetch this for questions about the Claude Code CLI tool, including:
|
|
465
|
+
- Installation, setup, and getting started
|
|
466
|
+
- Hooks (pre/post command execution)
|
|
467
|
+
- Custom skills
|
|
468
|
+
- MCP server configuration
|
|
469
|
+
- IDE integrations (VS Code, JetBrains)
|
|
470
|
+
- Settings files and configuration
|
|
471
|
+
- Keyboard shortcuts and hotkeys
|
|
472
|
+
- Subagents and plugins
|
|
473
|
+
- Sandboxing and security
|
|
474
|
+
|
|
475
|
+
- **Claude API/Agent SDK docs** (https://platform.claude.com/llms.txt): Fetch this for questions about:
|
|
476
|
+
- SDK overview and getting started (Python and TypeScript)
|
|
477
|
+
- Agent configuration + custom tools
|
|
478
|
+
- Session management and permissions
|
|
479
|
+
- MCP integration in agents
|
|
480
|
+
- Messages API and streaming
|
|
481
|
+
- Tool use (function calling)
|
|
482
|
+
- Vision, PDF support, and citations
|
|
483
|
+
- Extended thinking and structured outputs
|
|
484
|
+
- Cloud provider integrations (Bedrock, Vertex AI)
|
|
485
|
+
|
|
486
|
+
**Approach:**
|
|
487
|
+
1. Determine which domain the user's question falls into
|
|
488
|
+
2. Use WebFetch to fetch the appropriate docs map
|
|
489
|
+
3. Identify the most relevant documentation URLs from the map
|
|
490
|
+
4. Fetch the specific documentation pages
|
|
491
|
+
5. Provide clear, actionable guidance based on official documentation
|
|
492
|
+
6. Use WebSearch if docs don't cover the topic
|
|
493
|
+
7. Reference local project files (CLAUDE.md, .claude/ directory) when relevant using Read, Glob, and Grep
|
|
494
|
+
|
|
495
|
+
**Guidelines:**
|
|
496
|
+
- Always prioritize official documentation over assumptions
|
|
497
|
+
- Keep responses concise and actionable
|
|
498
|
+
- Include specific examples or code snippets when helpful
|
|
499
|
+
- Reference exact documentation URLs in your responses
|
|
500
|
+
- Help users discover features by proactively suggesting related commands, shortcuts, or capabilities
|
|
501
|
+
- When you cannot find an answer or the feature doesn't exist, direct the user to report the issue
|
|
502
|
+
|
|
503
|
+
Complete the user's request by providing accurate, documentation-based guidance."""
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
# ---------------------------------------------------------------------------
|
|
507
|
+
# Built-in agent definitions
|
|
508
|
+
# ---------------------------------------------------------------------------
|
|
509
|
+
|
|
510
|
+
_BUILTIN_AGENTS: list[AgentDefinition] = [
|
|
511
|
+
AgentDefinition(
|
|
512
|
+
name="general-purpose",
|
|
513
|
+
description=(
|
|
514
|
+
"General-purpose agent for researching complex questions, searching for code, "
|
|
515
|
+
"and executing multi-step tasks. When you are searching for a keyword or file "
|
|
516
|
+
"and are not confident that you will find the right match in the first few tries "
|
|
517
|
+
"use this agent to perform the search for you."
|
|
518
|
+
),
|
|
519
|
+
tools=["*"], # all tools
|
|
520
|
+
system_prompt=_GENERAL_PURPOSE_SYSTEM_PROMPT,
|
|
521
|
+
subagent_type="general-purpose",
|
|
522
|
+
source="builtin",
|
|
523
|
+
base_dir="built-in",
|
|
524
|
+
),
|
|
525
|
+
AgentDefinition(
|
|
526
|
+
name="statusline-setup",
|
|
527
|
+
description="Use this agent to configure the user's Claude Code status line setting.",
|
|
528
|
+
tools=["Read", "Edit"],
|
|
529
|
+
system_prompt=_STATUSLINE_SYSTEM_PROMPT,
|
|
530
|
+
model="sonnet",
|
|
531
|
+
color="orange",
|
|
532
|
+
subagent_type="statusline-setup",
|
|
533
|
+
source="builtin",
|
|
534
|
+
base_dir="built-in",
|
|
535
|
+
),
|
|
536
|
+
AgentDefinition(
|
|
537
|
+
name="claude-code-guide",
|
|
538
|
+
description=(
|
|
539
|
+
'Use this agent when the user asks questions ("Can Claude...", "Does Claude...", '
|
|
540
|
+
'"How do I...") about: (1) Claude Code (the CLI tool) - features, hooks, slash '
|
|
541
|
+
"commands, MCP servers, settings, IDE integrations, keyboard shortcuts; "
|
|
542
|
+
"(2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic "
|
|
543
|
+
"API) - API usage, tool use, Anthropic SDK usage. **IMPORTANT:** Before spawning a "
|
|
544
|
+
"new agent, check if there is already a running or recently completed claude-code-guide "
|
|
545
|
+
"agent that you can continue via SendMessage."
|
|
546
|
+
),
|
|
547
|
+
tools=["Glob", "Grep", "Read", "WebFetch", "WebSearch"],
|
|
548
|
+
system_prompt=_CLAUDE_CODE_GUIDE_SYSTEM_PROMPT,
|
|
549
|
+
model="inherit",
|
|
550
|
+
permission_mode="dontAsk",
|
|
551
|
+
subagent_type="claude-code-guide",
|
|
552
|
+
source="builtin",
|
|
553
|
+
base_dir="built-in",
|
|
554
|
+
),
|
|
555
|
+
AgentDefinition(
|
|
556
|
+
name="Explore",
|
|
557
|
+
description=(
|
|
558
|
+
"Fast agent specialized for exploring codebases. Use this when you need to "
|
|
559
|
+
"quickly find files by patterns (eg. \"src/components/**/*.tsx\"), search code "
|
|
560
|
+
"for keywords (eg. \"API endpoints\"), or answer questions about the codebase "
|
|
561
|
+
"(eg. \"how do API endpoints work?\"). When calling this agent, specify the "
|
|
562
|
+
"desired thoroughness level: \"quick\" for basic searches, \"medium\" for "
|
|
563
|
+
"moderate exploration, or \"very thorough\" for comprehensive analysis across "
|
|
564
|
+
"multiple locations and naming conventions."
|
|
565
|
+
),
|
|
566
|
+
disallowed_tools=["agent", "exit_plan_mode", "file_edit", "file_write", "notebook_edit"],
|
|
567
|
+
system_prompt=_EXPLORE_SYSTEM_PROMPT,
|
|
568
|
+
model="inherit",
|
|
569
|
+
omit_claude_md=True,
|
|
570
|
+
subagent_type="Explore",
|
|
571
|
+
source="builtin",
|
|
572
|
+
base_dir="built-in",
|
|
573
|
+
),
|
|
574
|
+
AgentDefinition(
|
|
575
|
+
name="Plan",
|
|
576
|
+
description=(
|
|
577
|
+
"Software architect agent for designing implementation plans. Use this when you "
|
|
578
|
+
"need to plan the implementation strategy for a task. Returns step-by-step plans, "
|
|
579
|
+
"identifies critical files, and considers architectural trade-offs."
|
|
580
|
+
),
|
|
581
|
+
disallowed_tools=["agent", "exit_plan_mode", "file_edit", "file_write", "notebook_edit"],
|
|
582
|
+
system_prompt=_PLAN_SYSTEM_PROMPT,
|
|
583
|
+
model="inherit",
|
|
584
|
+
omit_claude_md=True,
|
|
585
|
+
subagent_type="Plan",
|
|
586
|
+
source="builtin",
|
|
587
|
+
base_dir="built-in",
|
|
588
|
+
),
|
|
589
|
+
AgentDefinition(
|
|
590
|
+
name="worker",
|
|
591
|
+
description=(
|
|
592
|
+
"Implementation-focused worker agent. Use this for concrete coding tasks: "
|
|
593
|
+
"writing features, fixing bugs, refactoring code, and running tests."
|
|
594
|
+
),
|
|
595
|
+
tools=None, # all tools
|
|
596
|
+
system_prompt=_WORKER_SYSTEM_PROMPT,
|
|
597
|
+
subagent_type="worker",
|
|
598
|
+
source="builtin",
|
|
599
|
+
base_dir="built-in",
|
|
600
|
+
),
|
|
601
|
+
AgentDefinition(
|
|
602
|
+
name="verification",
|
|
603
|
+
description=(
|
|
604
|
+
"Use this agent to verify that implementation work is correct before reporting "
|
|
605
|
+
"completion. Invoke after non-trivial tasks (3+ file edits, backend/API changes, "
|
|
606
|
+
"infrastructure changes). Pass the ORIGINAL user task description, list of files "
|
|
607
|
+
"changed, and approach taken. The agent runs builds, tests, linters, and checks "
|
|
608
|
+
"to produce a PASS/FAIL/PARTIAL verdict with evidence."
|
|
609
|
+
),
|
|
610
|
+
disallowed_tools=["agent", "exit_plan_mode", "file_edit", "file_write", "notebook_edit"],
|
|
611
|
+
system_prompt=_VERIFICATION_SYSTEM_PROMPT,
|
|
612
|
+
critical_system_reminder=_VERIFICATION_CRITICAL_REMINDER,
|
|
613
|
+
color="red",
|
|
614
|
+
background=True,
|
|
615
|
+
model="inherit",
|
|
616
|
+
subagent_type="verification",
|
|
617
|
+
source="builtin",
|
|
618
|
+
base_dir="built-in",
|
|
619
|
+
),
|
|
620
|
+
]
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
def get_builtin_agent_definitions() -> list[AgentDefinition]:
|
|
624
|
+
"""Return the built-in agent definitions."""
|
|
625
|
+
return list(_BUILTIN_AGENTS)
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
# ---------------------------------------------------------------------------
|
|
629
|
+
# Markdown / YAML-frontmatter loader
|
|
630
|
+
# ---------------------------------------------------------------------------
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
def _parse_agent_frontmatter(content: str) -> tuple[dict[str, Any], str]:
|
|
634
|
+
"""Parse YAML frontmatter from a markdown file.
|
|
635
|
+
|
|
636
|
+
Returns a (frontmatter_dict, body) tuple. Uses ``yaml.safe_load`` for
|
|
637
|
+
proper YAML parsing (supports nested structures for hooks, mcpServers, etc.).
|
|
638
|
+
"""
|
|
639
|
+
frontmatter: dict[str, Any] = {}
|
|
640
|
+
body = content
|
|
641
|
+
|
|
642
|
+
lines = content.splitlines()
|
|
643
|
+
if not lines or lines[0].strip() != "---":
|
|
644
|
+
return frontmatter, body
|
|
645
|
+
|
|
646
|
+
end_index: int | None = None
|
|
647
|
+
for i, line in enumerate(lines[1:], start=1):
|
|
648
|
+
if line.strip() == "---":
|
|
649
|
+
end_index = i
|
|
650
|
+
break
|
|
651
|
+
|
|
652
|
+
if end_index is None:
|
|
653
|
+
return frontmatter, body
|
|
654
|
+
|
|
655
|
+
fm_text = "\n".join(lines[1:end_index])
|
|
656
|
+
try:
|
|
657
|
+
parsed = yaml.safe_load(fm_text)
|
|
658
|
+
if isinstance(parsed, dict):
|
|
659
|
+
frontmatter = parsed
|
|
660
|
+
except yaml.YAMLError:
|
|
661
|
+
# Fall back to simple key:value parsing
|
|
662
|
+
for fm_line in lines[1:end_index]:
|
|
663
|
+
if ":" in fm_line:
|
|
664
|
+
key, _, value = fm_line.partition(":")
|
|
665
|
+
frontmatter[key.strip()] = value.strip().strip("'\"")
|
|
666
|
+
|
|
667
|
+
# Body is everything after the closing ---
|
|
668
|
+
body = "\n".join(lines[end_index + 1 :]).strip()
|
|
669
|
+
return frontmatter, body
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
def _parse_str_list(raw: Any) -> list[str] | None:
|
|
673
|
+
"""Parse a comma-separated string or list into a list of strings."""
|
|
674
|
+
if raw is None:
|
|
675
|
+
return None
|
|
676
|
+
if isinstance(raw, list):
|
|
677
|
+
return [str(item).strip() for item in raw if str(item).strip()]
|
|
678
|
+
if isinstance(raw, str):
|
|
679
|
+
items = [t.strip() for t in raw.split(",") if t.strip()]
|
|
680
|
+
return items if items else None
|
|
681
|
+
return None
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def _parse_positive_int(raw: Any) -> int | None:
|
|
685
|
+
"""Parse a positive integer from frontmatter, returning None if invalid."""
|
|
686
|
+
if raw is None:
|
|
687
|
+
return None
|
|
688
|
+
try:
|
|
689
|
+
val = int(raw)
|
|
690
|
+
return val if val > 0 else None
|
|
691
|
+
except (TypeError, ValueError):
|
|
692
|
+
return None
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
def load_agents_dir(directory: Path) -> list[AgentDefinition]:
|
|
696
|
+
"""Load agent definitions from .md files in *directory*.
|
|
697
|
+
|
|
698
|
+
Each file should contain YAML frontmatter with at least ``name`` and
|
|
699
|
+
``description`` fields. The markdown body becomes the ``system_prompt``.
|
|
700
|
+
|
|
701
|
+
Supported frontmatter fields (all optional unless noted):
|
|
702
|
+
|
|
703
|
+
Required:
|
|
704
|
+
* ``name`` — agent type identifier
|
|
705
|
+
* ``description`` — when-to-use description shown to the spawning agent
|
|
706
|
+
|
|
707
|
+
Optional:
|
|
708
|
+
* ``tools`` — comma-separated or YAML list of allowed tool names
|
|
709
|
+
* ``disallowedTools`` / ``disallowed_tools`` — comma-separated or list of disallowed tools
|
|
710
|
+
* ``model`` — model override (e.g. "haiku", "inherit")
|
|
711
|
+
* ``effort`` — "low", "medium", "high", or a positive integer
|
|
712
|
+
* ``permissionMode`` / ``permission_mode`` — one of PERMISSION_MODES
|
|
713
|
+
* ``maxTurns`` / ``max_turns`` — positive integer turn limit
|
|
714
|
+
* ``skills`` — comma-separated or list of skill names
|
|
715
|
+
* ``mcpServers`` / ``mcp_servers`` — list of MCP server references or inline configs
|
|
716
|
+
* ``hooks`` — YAML dict of session-scoped hooks
|
|
717
|
+
* ``color`` — one of AGENT_COLORS
|
|
718
|
+
* ``background`` — true/false; run as background task
|
|
719
|
+
* ``initialPrompt`` / ``initial_prompt`` — string prepended to first user turn
|
|
720
|
+
* ``memory`` — one of MEMORY_SCOPES
|
|
721
|
+
* ``isolation`` — one of ISOLATION_MODES
|
|
722
|
+
* ``omitClaudeMd`` / ``omit_claude_md`` — true/false; skip CLAUDE.md injection
|
|
723
|
+
* ``criticalSystemReminder`` / ``critical_system_reminder`` — re-injected message
|
|
724
|
+
* ``requiredMcpServers`` / ``required_mcp_servers`` — list of required server patterns
|
|
725
|
+
* ``permissions`` — comma-separated extra permission rules (Python-specific)
|
|
726
|
+
* ``subagent_type`` — routing key (Python-specific, defaults to name)
|
|
727
|
+
"""
|
|
728
|
+
agents: list[AgentDefinition] = []
|
|
729
|
+
|
|
730
|
+
if not directory.is_dir():
|
|
731
|
+
return agents
|
|
732
|
+
|
|
733
|
+
for path in sorted(directory.glob("*.md")):
|
|
734
|
+
try:
|
|
735
|
+
content = path.read_text(encoding="utf-8")
|
|
736
|
+
frontmatter, body = _parse_agent_frontmatter(content)
|
|
737
|
+
|
|
738
|
+
name = str(frontmatter.get("name", "")).strip() or path.stem
|
|
739
|
+
description = str(frontmatter.get("description", "")).strip()
|
|
740
|
+
if not description:
|
|
741
|
+
description = f"Agent: {name}"
|
|
742
|
+
|
|
743
|
+
# Unescape literal \n in descriptions from YAML
|
|
744
|
+
description = description.replace("\\n", "\n")
|
|
745
|
+
|
|
746
|
+
# --- tools ---
|
|
747
|
+
tools = _parse_str_list(frontmatter.get("tools"))
|
|
748
|
+
|
|
749
|
+
# --- disallowed tools ---
|
|
750
|
+
disallowed_raw = frontmatter.get(
|
|
751
|
+
"disallowedTools", frontmatter.get("disallowed_tools")
|
|
752
|
+
)
|
|
753
|
+
disallowed_tools = _parse_str_list(disallowed_raw)
|
|
754
|
+
|
|
755
|
+
# --- model ---
|
|
756
|
+
model_raw = frontmatter.get("model")
|
|
757
|
+
model: str | None = None
|
|
758
|
+
if isinstance(model_raw, str) and model_raw.strip():
|
|
759
|
+
trimmed = model_raw.strip()
|
|
760
|
+
model = "inherit" if trimmed.lower() == "inherit" else trimmed
|
|
761
|
+
|
|
762
|
+
# --- effort ---
|
|
763
|
+
effort_raw = frontmatter.get("effort")
|
|
764
|
+
effort: str | int | None = None
|
|
765
|
+
if effort_raw is not None:
|
|
766
|
+
if isinstance(effort_raw, int):
|
|
767
|
+
effort = effort_raw if effort_raw > 0 else None
|
|
768
|
+
elif isinstance(effort_raw, str) and effort_raw in EFFORT_LEVELS:
|
|
769
|
+
effort = effort_raw
|
|
770
|
+
else:
|
|
771
|
+
logger.debug("Agent %s: invalid effort %r", name, effort_raw)
|
|
772
|
+
|
|
773
|
+
# --- permissionMode ---
|
|
774
|
+
perm_raw = frontmatter.get("permissionMode", frontmatter.get("permission_mode"))
|
|
775
|
+
permission_mode: str | None = None
|
|
776
|
+
if isinstance(perm_raw, str) and perm_raw in PERMISSION_MODES:
|
|
777
|
+
permission_mode = perm_raw
|
|
778
|
+
elif perm_raw is not None:
|
|
779
|
+
logger.debug("Agent %s: invalid permissionMode %r", name, perm_raw)
|
|
780
|
+
|
|
781
|
+
# --- maxTurns ---
|
|
782
|
+
max_turns_raw = frontmatter.get("maxTurns", frontmatter.get("max_turns"))
|
|
783
|
+
max_turns = _parse_positive_int(max_turns_raw)
|
|
784
|
+
if max_turns_raw is not None and max_turns is None:
|
|
785
|
+
logger.debug("Agent %s: invalid maxTurns %r", name, max_turns_raw)
|
|
786
|
+
|
|
787
|
+
# --- skills ---
|
|
788
|
+
skills_raw = frontmatter.get("skills")
|
|
789
|
+
skills = _parse_str_list(skills_raw) or []
|
|
790
|
+
|
|
791
|
+
# --- mcpServers ---
|
|
792
|
+
mcp_raw = frontmatter.get("mcpServers", frontmatter.get("mcp_servers"))
|
|
793
|
+
mcp_servers: list[Any] | None = None
|
|
794
|
+
if isinstance(mcp_raw, list):
|
|
795
|
+
mcp_servers = mcp_raw if mcp_raw else None
|
|
796
|
+
|
|
797
|
+
# --- hooks ---
|
|
798
|
+
hooks_raw = frontmatter.get("hooks")
|
|
799
|
+
hooks: dict[str, Any] | None = None
|
|
800
|
+
if isinstance(hooks_raw, dict):
|
|
801
|
+
hooks = hooks_raw
|
|
802
|
+
|
|
803
|
+
# --- color ---
|
|
804
|
+
color_raw = frontmatter.get("color")
|
|
805
|
+
color: str | None = None
|
|
806
|
+
if isinstance(color_raw, str) and color_raw in AGENT_COLORS:
|
|
807
|
+
color = color_raw
|
|
808
|
+
|
|
809
|
+
# --- background ---
|
|
810
|
+
bg_raw = frontmatter.get("background")
|
|
811
|
+
background = bg_raw is True or bg_raw == "true"
|
|
812
|
+
|
|
813
|
+
# --- initialPrompt ---
|
|
814
|
+
ip_raw = frontmatter.get("initialPrompt", frontmatter.get("initial_prompt"))
|
|
815
|
+
initial_prompt: str | None = None
|
|
816
|
+
if isinstance(ip_raw, str) and ip_raw.strip():
|
|
817
|
+
initial_prompt = ip_raw
|
|
818
|
+
|
|
819
|
+
# --- memory ---
|
|
820
|
+
memory_raw = frontmatter.get("memory")
|
|
821
|
+
memory: str | None = None
|
|
822
|
+
if isinstance(memory_raw, str) and memory_raw in MEMORY_SCOPES:
|
|
823
|
+
memory = memory_raw
|
|
824
|
+
elif memory_raw is not None:
|
|
825
|
+
logger.debug("Agent %s: invalid memory %r", name, memory_raw)
|
|
826
|
+
|
|
827
|
+
# --- isolation ---
|
|
828
|
+
iso_raw = frontmatter.get("isolation")
|
|
829
|
+
isolation: str | None = None
|
|
830
|
+
if isinstance(iso_raw, str) and iso_raw in ISOLATION_MODES:
|
|
831
|
+
isolation = iso_raw
|
|
832
|
+
elif iso_raw is not None:
|
|
833
|
+
logger.debug("Agent %s: invalid isolation %r", name, iso_raw)
|
|
834
|
+
|
|
835
|
+
# --- omitClaudeMd ---
|
|
836
|
+
ocm_raw = frontmatter.get("omitClaudeMd", frontmatter.get("omit_claude_md"))
|
|
837
|
+
omit_claude_md = ocm_raw is True or ocm_raw == "true"
|
|
838
|
+
|
|
839
|
+
# --- criticalSystemReminder ---
|
|
840
|
+
csr_raw = frontmatter.get(
|
|
841
|
+
"criticalSystemReminder", frontmatter.get("critical_system_reminder")
|
|
842
|
+
)
|
|
843
|
+
critical_system_reminder: str | None = None
|
|
844
|
+
if isinstance(csr_raw, str) and csr_raw.strip():
|
|
845
|
+
critical_system_reminder = csr_raw
|
|
846
|
+
|
|
847
|
+
# --- requiredMcpServers ---
|
|
848
|
+
rms_raw = frontmatter.get(
|
|
849
|
+
"requiredMcpServers", frontmatter.get("required_mcp_servers")
|
|
850
|
+
)
|
|
851
|
+
required_mcp_servers = _parse_str_list(rms_raw)
|
|
852
|
+
|
|
853
|
+
# --- permissions (Python-specific) ---
|
|
854
|
+
permissions: list[str] = []
|
|
855
|
+
raw_perms = frontmatter.get("permissions", "")
|
|
856
|
+
if raw_perms:
|
|
857
|
+
permissions = [p.strip() for p in str(raw_perms).split(",") if p.strip()]
|
|
858
|
+
|
|
859
|
+
agents.append(
|
|
860
|
+
AgentDefinition(
|
|
861
|
+
name=name,
|
|
862
|
+
description=description,
|
|
863
|
+
system_prompt=body or None,
|
|
864
|
+
tools=tools,
|
|
865
|
+
disallowed_tools=disallowed_tools,
|
|
866
|
+
model=model,
|
|
867
|
+
effort=effort,
|
|
868
|
+
permission_mode=permission_mode,
|
|
869
|
+
max_turns=max_turns,
|
|
870
|
+
skills=skills,
|
|
871
|
+
mcp_servers=mcp_servers,
|
|
872
|
+
hooks=hooks,
|
|
873
|
+
color=color,
|
|
874
|
+
background=background,
|
|
875
|
+
initial_prompt=initial_prompt,
|
|
876
|
+
memory=memory,
|
|
877
|
+
isolation=isolation,
|
|
878
|
+
omit_claude_md=omit_claude_md,
|
|
879
|
+
critical_system_reminder=critical_system_reminder,
|
|
880
|
+
required_mcp_servers=required_mcp_servers,
|
|
881
|
+
permissions=permissions,
|
|
882
|
+
filename=path.stem,
|
|
883
|
+
base_dir=str(directory),
|
|
884
|
+
subagent_type=str(frontmatter.get("subagent_type", name)),
|
|
885
|
+
source="user",
|
|
886
|
+
)
|
|
887
|
+
)
|
|
888
|
+
except Exception:
|
|
889
|
+
logger.debug("Failed to parse agent from %s", path, exc_info=True)
|
|
890
|
+
continue
|
|
891
|
+
|
|
892
|
+
return agents
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
# ---------------------------------------------------------------------------
|
|
896
|
+
# Public API
|
|
897
|
+
# ---------------------------------------------------------------------------
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
def _get_user_agents_dir() -> Path:
|
|
901
|
+
"""Return the user agent definitions directory."""
|
|
902
|
+
return get_config_dir() / "agents"
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
def get_all_agent_definitions() -> list[AgentDefinition]:
|
|
906
|
+
"""Return all agent definitions: built-in + user + plugin.
|
|
907
|
+
|
|
908
|
+
Merge order (last writer wins for same ``name``):
|
|
909
|
+
1. Built-in agents
|
|
910
|
+
2. User agents (~/.openharness/agents/)
|
|
911
|
+
3. Plugin agents (loaded from active plugins)
|
|
912
|
+
|
|
913
|
+
User definitions override built-ins with the same name; plugin definitions
|
|
914
|
+
override user definitions with the same name.
|
|
915
|
+
"""
|
|
916
|
+
agent_map: dict[str, AgentDefinition] = {}
|
|
917
|
+
|
|
918
|
+
# 1. Built-ins (lowest priority)
|
|
919
|
+
for agent in get_builtin_agent_definitions():
|
|
920
|
+
agent_map[agent.name] = agent
|
|
921
|
+
|
|
922
|
+
# 2. User-defined agents
|
|
923
|
+
user_agents = load_agents_dir(_get_user_agents_dir())
|
|
924
|
+
for agent in user_agents:
|
|
925
|
+
agent_map[agent.name] = agent
|
|
926
|
+
|
|
927
|
+
# 3. Plugin agents — loaded lazily to avoid import cycles
|
|
928
|
+
try:
|
|
929
|
+
from openharness.plugins.loader import load_plugins # noqa: PLC0415
|
|
930
|
+
from openharness.config.settings import load_settings # noqa: PLC0415
|
|
931
|
+
|
|
932
|
+
settings = load_settings()
|
|
933
|
+
import os # noqa: PLC0415
|
|
934
|
+
|
|
935
|
+
cwd = os.getcwd()
|
|
936
|
+
for plugin in load_plugins(settings, cwd):
|
|
937
|
+
if not plugin.enabled:
|
|
938
|
+
continue
|
|
939
|
+
for agent_def in getattr(plugin, "agents", []):
|
|
940
|
+
if isinstance(agent_def, AgentDefinition):
|
|
941
|
+
agent_map[agent_def.name] = agent_def
|
|
942
|
+
except Exception:
|
|
943
|
+
pass
|
|
944
|
+
|
|
945
|
+
return list(agent_map.values())
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
def get_agent_definition(name: str) -> AgentDefinition | None:
|
|
949
|
+
"""Return the agent definition for *name*, or ``None`` if not found."""
|
|
950
|
+
for agent in get_all_agent_definitions():
|
|
951
|
+
if agent.name == name:
|
|
952
|
+
return agent
|
|
953
|
+
return None
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
def has_required_mcp_servers(agent: AgentDefinition, available_servers: list[str]) -> bool:
|
|
957
|
+
"""Return True if the agent's required MCP servers are all available.
|
|
958
|
+
|
|
959
|
+
Each pattern in ``required_mcp_servers`` must match (case-insensitive
|
|
960
|
+
substring) at least one server in ``available_servers``.
|
|
961
|
+
"""
|
|
962
|
+
if not agent.required_mcp_servers:
|
|
963
|
+
return True
|
|
964
|
+
return all(
|
|
965
|
+
any(pattern.lower() in server.lower() for server in available_servers)
|
|
966
|
+
for pattern in agent.required_mcp_servers
|
|
967
|
+
)
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
def filter_agents_by_mcp_requirements(
|
|
971
|
+
agents: list[AgentDefinition],
|
|
972
|
+
available_servers: list[str],
|
|
973
|
+
) -> list[AgentDefinition]:
|
|
974
|
+
"""Return only agents whose required MCP servers are available."""
|
|
975
|
+
return [a for a in agents if has_required_mcp_servers(a, available_servers)]
|