devflow-engine 1.0.0__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.
- devflow_engine/__init__.py +3 -0
- devflow_engine/agentic_prompts.py +100 -0
- devflow_engine/agentic_runtime.py +398 -0
- devflow_engine/api_key_flow_harness.py +539 -0
- devflow_engine/api_keys.py +357 -0
- devflow_engine/bootstrap/__init__.py +2 -0
- devflow_engine/bootstrap/provision_from_template.py +84 -0
- devflow_engine/cli/__init__.py +0 -0
- devflow_engine/cli/app.py +7270 -0
- devflow_engine/core/__init__.py +0 -0
- devflow_engine/core/config.py +86 -0
- devflow_engine/core/logging.py +29 -0
- devflow_engine/core/paths.py +45 -0
- devflow_engine/core/toml_kv.py +33 -0
- devflow_engine/devflow_event_worker.py +1292 -0
- devflow_engine/devflow_state.py +201 -0
- devflow_engine/devin2/__init__.py +9 -0
- devflow_engine/devin2/agent_definition.py +120 -0
- devflow_engine/devin2/pi_runner.py +204 -0
- devflow_engine/devin_orchestration.py +69 -0
- devflow_engine/docs/prompts/anti-patterns.md +42 -0
- devflow_engine/docs/prompts/devin-agent-prompt.md +55 -0
- devflow_engine/docs/prompts/devin2-agent-prompt.md +81 -0
- devflow_engine/docs/prompts/examples/devin-vapi-clone-reference-exchange.json +85 -0
- devflow_engine/doctor/__init__.py +2 -0
- devflow_engine/doctor/triage.py +140 -0
- devflow_engine/error/__init__.py +0 -0
- devflow_engine/error/remediation.py +21 -0
- devflow_engine/errors/error_solver_dag.py +522 -0
- devflow_engine/errors/runtime_observability.py +67 -0
- devflow_engine/idea/__init__.py +4 -0
- devflow_engine/idea/actors.py +481 -0
- devflow_engine/idea/agentic.py +465 -0
- devflow_engine/idea/analyze.py +93 -0
- devflow_engine/idea/devin_chat_dag.py +1 -0
- devflow_engine/idea/diff.py +99 -0
- devflow_engine/idea/drafts.py +446 -0
- devflow_engine/idea/idea_creation_dag.py +643 -0
- devflow_engine/idea/ideation_enrichment.py +355 -0
- devflow_engine/idea/ideation_enrichment_worker.py +19 -0
- devflow_engine/idea/paths.py +28 -0
- devflow_engine/idea/promote.py +53 -0
- devflow_engine/idea/redaction.py +27 -0
- devflow_engine/idea/repo_tools.py +1277 -0
- devflow_engine/idea/response_mode.py +30 -0
- devflow_engine/idea/story_pipeline.py +1585 -0
- devflow_engine/idea/sufficiency.py +376 -0
- devflow_engine/idea/traditional_stories.py +1257 -0
- devflow_engine/implementation/__init__.py +0 -0
- devflow_engine/implementation/alembic_preflight.py +700 -0
- devflow_engine/implementation/dag.py +8450 -0
- devflow_engine/implementation/green_gate.py +93 -0
- devflow_engine/implementation/prompts.py +108 -0
- devflow_engine/implementation/test_runtime.py +623 -0
- devflow_engine/integration/__init__.py +19 -0
- devflow_engine/integration/agentic.py +66 -0
- devflow_engine/integration/dag.py +3539 -0
- devflow_engine/integration/prompts.py +114 -0
- devflow_engine/integration/supabase_schema.sql +31 -0
- devflow_engine/integration/supabase_sync.py +177 -0
- devflow_engine/llm/__init__.py +1 -0
- devflow_engine/llm/cli_one_shot.py +84 -0
- devflow_engine/llm/cli_stream.py +371 -0
- devflow_engine/llm/execution_context.py +26 -0
- devflow_engine/llm/invoke.py +1322 -0
- devflow_engine/llm/provider_api.py +304 -0
- devflow_engine/llm/repo_knowledge.py +588 -0
- devflow_engine/llm_primitives.py +315 -0
- devflow_engine/orchestration.py +62 -0
- devflow_engine/planning/__init__.py +0 -0
- devflow_engine/planning/analyze_repo.py +92 -0
- devflow_engine/planning/render_drafts.py +133 -0
- devflow_engine/playground/__init__.py +0 -0
- devflow_engine/playground/hooks.py +26 -0
- devflow_engine/playwright_workflow/__init__.py +5 -0
- devflow_engine/playwright_workflow/dag.py +1317 -0
- devflow_engine/process/__init__.py +5 -0
- devflow_engine/process/dag.py +59 -0
- devflow_engine/project_registration/__init__.py +3 -0
- devflow_engine/project_registration/dag.py +1581 -0
- devflow_engine/project_registry.py +109 -0
- devflow_engine/prompts/devin/generic/prompt.md +6 -0
- devflow_engine/prompts/devin/ideation/prompt.md +263 -0
- devflow_engine/prompts/devin/ideation/scenarios.md +5 -0
- devflow_engine/prompts/devin/ideation_loop/prompt.md +6 -0
- devflow_engine/prompts/devin/insight/prompt.md +11 -0
- devflow_engine/prompts/devin/insight/scenarios.md +5 -0
- devflow_engine/prompts/devin/intake/prompt.md +15 -0
- devflow_engine/prompts/devin/iterate/prompt.md +12 -0
- devflow_engine/prompts/devin/shared/eval_doctrine.md +9 -0
- devflow_engine/prompts/devin/shared/principles.md +246 -0
- devflow_engine/prompts/devin_eval/assessment/prompt.md +18 -0
- devflow_engine/prompts/idea/api_ideation_agent/prompt.md +8 -0
- devflow_engine/prompts/idea/api_insight_agent/prompt.md +8 -0
- devflow_engine/prompts/idea/response_doctrine/prompt.md +18 -0
- devflow_engine/prompts/implementation/dependency_assessment/prompt.md +12 -0
- devflow_engine/prompts/implementation/green/green/prompt.md +11 -0
- devflow_engine/prompts/implementation/green/node_config/prompt.md +3 -0
- devflow_engine/prompts/implementation/green_review/outcome_review/prompt.md +5 -0
- devflow_engine/prompts/implementation/green_review/prior_run_review/prompt.md +5 -0
- devflow_engine/prompts/implementation/red/prompt.md +27 -0
- devflow_engine/prompts/implementation/redreview/prompt.md +23 -0
- devflow_engine/prompts/implementation/redreview_repair/prompt.md +16 -0
- devflow_engine/prompts/implementation/setupdoc/prompt.md +10 -0
- devflow_engine/prompts/implementation/story_planning/prompt.md +13 -0
- devflow_engine/prompts/implementation/test_design/prompt.md +27 -0
- devflow_engine/prompts/integration/README.md +185 -0
- devflow_engine/prompts/integration/green/example.md +67 -0
- devflow_engine/prompts/integration/green/green/prompt.md +10 -0
- devflow_engine/prompts/integration/green/node_config/prompt.md +42 -0
- devflow_engine/prompts/integration/green/past_prompts/20260417T212300/green/prompt.md +15 -0
- devflow_engine/prompts/integration/green/past_prompts/20260417T212300/node_config/prompt.md +42 -0
- devflow_engine/prompts/integration/green_enrich/example.md +79 -0
- devflow_engine/prompts/integration/green_enrich/green_enrich/prompt.md +9 -0
- devflow_engine/prompts/integration/green_enrich/node_config/prompt.md +41 -0
- devflow_engine/prompts/integration/green_enrich/past_prompts/20260417T212300/green_enrich/prompt.md +14 -0
- devflow_engine/prompts/integration/green_enrich/past_prompts/20260417T212300/node_config/prompt.md +41 -0
- devflow_engine/prompts/integration/red/code_repair/prompt.md +12 -0
- devflow_engine/prompts/integration/red/example.md +152 -0
- devflow_engine/prompts/integration/red/node_config/prompt.md +86 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T212300/code_repair/prompt.md +19 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T212300/node_config/prompt.md +84 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T212300/red/prompt.md +16 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T212300/red_repair/prompt.md +15 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T215032/code_repair/prompt.md +10 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T215032/node_config/prompt.md +84 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T215032/red_repair/prompt.md +11 -0
- devflow_engine/prompts/integration/red/red/prompt.md +11 -0
- devflow_engine/prompts/integration/red/red_repair/prompt.md +12 -0
- devflow_engine/prompts/integration/red_review/example.md +71 -0
- devflow_engine/prompts/integration/red_review/node_config/prompt.md +41 -0
- devflow_engine/prompts/integration/red_review/past_prompts/20260417T212300/node_config/prompt.md +41 -0
- devflow_engine/prompts/integration/red_review/past_prompts/20260417T212300/red_review/prompt.md +15 -0
- devflow_engine/prompts/integration/red_review/red_review/prompt.md +9 -0
- devflow_engine/prompts/integration/resolve/example.md +111 -0
- devflow_engine/prompts/integration/resolve/node_config/prompt.md +64 -0
- devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/node_config/prompt.md +64 -0
- devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/resolve_implicated_users/prompt.md +15 -0
- devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/resolve_side_effects/prompt.md +15 -0
- devflow_engine/prompts/integration/resolve/resolve_implicated_users/prompt.md +10 -0
- devflow_engine/prompts/integration/resolve/resolve_side_effects/prompt.md +10 -0
- devflow_engine/prompts/integration/validate/build_idea_acceptance_coverage/prompt.md +12 -0
- devflow_engine/prompts/integration/validate/code_repair/prompt.md +13 -0
- devflow_engine/prompts/integration/validate/example.md +143 -0
- devflow_engine/prompts/integration/validate/node_config/prompt.md +87 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/code_repair/prompt.md +19 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/node_config/prompt.md +67 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/validate_enrich_gate/prompt.md +17 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/validate_repair/prompt.md +16 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/code_repair/prompt.md +10 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/node_config/prompt.md +67 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/validate_repair/prompt.md +9 -0
- devflow_engine/prompts/integration/validate/validate_enrich_gate/prompt.md +10 -0
- devflow_engine/prompts/integration/validate/validate_repair/prompt.md +20 -0
- devflow_engine/prompts/integration/write_workflows/example.md +100 -0
- devflow_engine/prompts/integration/write_workflows/node_config/prompt.md +44 -0
- devflow_engine/prompts/integration/write_workflows/past_prompts/20260417T212300/node_config/prompt.md +44 -0
- devflow_engine/prompts/integration/write_workflows/past_prompts/20260417T212300/write_workflows/prompt.md +17 -0
- devflow_engine/prompts/integration/write_workflows/write_workflows/prompt.md +11 -0
- devflow_engine/prompts/iterate/README.md +7 -0
- devflow_engine/prompts/iterate/coder/prompt.md +11 -0
- devflow_engine/prompts/iterate/framer/prompt.md +11 -0
- devflow_engine/prompts/iterate/iterator/prompt.md +13 -0
- devflow_engine/prompts/iterate/observer/prompt.md +11 -0
- devflow_engine/prompts/recovery/diagnosis/prompt.md +7 -0
- devflow_engine/prompts/recovery/execution/prompt.md +8 -0
- devflow_engine/prompts/recovery/execution_verification/prompt.md +7 -0
- devflow_engine/prompts/recovery/failure_investigation/prompt.md +10 -0
- devflow_engine/prompts/recovery/preflight_health_repo_repair/prompt.md +8 -0
- devflow_engine/prompts/recovery/remediation_execution/prompt.md +11 -0
- devflow_engine/prompts/recovery/root_cause_investigation/prompt.md +12 -0
- devflow_engine/prompts/scope_idea/doctrine/prompt.md +7 -0
- devflow_engine/prompts/source_doc_eval/document/prompt.md +6 -0
- devflow_engine/prompts/source_doc_eval/targeted_mutation/prompt.md +9 -0
- devflow_engine/prompts/source_doc_mutation/domain_entities/prompt.md +6 -0
- devflow_engine/prompts/source_doc_mutation/product_brief/prompt.md +6 -0
- devflow_engine/prompts/source_doc_mutation/project_doc_coherence/prompt.md +7 -0
- devflow_engine/prompts/source_doc_mutation/project_doc_render/prompt.md +9 -0
- devflow_engine/prompts/source_doc_mutation/source_doc_coherence/prompt.md +5 -0
- devflow_engine/prompts/source_doc_mutation/source_doc_enrichment_coherence/prompt.md +6 -0
- devflow_engine/prompts/source_doc_mutation/user_workflows/prompt.md +6 -0
- devflow_engine/prompts/source_scope/doctrine/prompt.md +10 -0
- devflow_engine/prompts/ui_grounding/doctrine/prompt.md +7 -0
- devflow_engine/recovery/__init__.py +3 -0
- devflow_engine/recovery/dag.py +2609 -0
- devflow_engine/recovery/models.py +220 -0
- devflow_engine/refactor.py +93 -0
- devflow_engine/registry/__init__.py +1 -0
- devflow_engine/registry/cards.py +238 -0
- devflow_engine/registry/domain_normalize.py +60 -0
- devflow_engine/registry/effects.py +65 -0
- devflow_engine/registry/enforce_report.py +150 -0
- devflow_engine/registry/module_cards_classify.py +164 -0
- devflow_engine/registry/module_cards_draft.py +184 -0
- devflow_engine/registry/module_cards_gate.py +59 -0
- devflow_engine/registry/packages.py +347 -0
- devflow_engine/registry/pathways.py +323 -0
- devflow_engine/review/__init__.py +11 -0
- devflow_engine/review/dag.py +588 -0
- devflow_engine/review/review_story.py +67 -0
- devflow_engine/scope_idea/__init__.py +3 -0
- devflow_engine/scope_idea/agentic.py +39 -0
- devflow_engine/scope_idea/dag.py +1069 -0
- devflow_engine/scope_idea/models.py +175 -0
- devflow_engine/skills/builtins/devflow/queue_failure_investigation/SKILL.md +112 -0
- devflow_engine/skills/builtins/devflow/queue_idea_to_story/SKILL.md +120 -0
- devflow_engine/skills/builtins/devflow/queue_integration/SKILL.md +105 -0
- devflow_engine/skills/builtins/devflow/queue_recovery/SKILL.md +108 -0
- devflow_engine/skills/builtins/devflow/queue_runtime_core/SKILL.md +155 -0
- devflow_engine/skills/builtins/devflow/queue_story_implementation/SKILL.md +122 -0
- devflow_engine/skills/builtins/devin/idea_to_story_handoff/SKILL.md +120 -0
- devflow_engine/skills/builtins/devin/ideation/SKILL.md +168 -0
- devflow_engine/skills/builtins/devin/ideation/state-and-phrasing-reference.md +18 -0
- devflow_engine/skills/builtins/devin/insight/SKILL.md +22 -0
- devflow_engine/skills/registry.example.yaml +42 -0
- devflow_engine/source_doc_assumptions.py +291 -0
- devflow_engine/source_doc_mutation_dag.py +1606 -0
- devflow_engine/source_doc_mutation_eval.py +417 -0
- devflow_engine/source_doc_mutation_worker.py +25 -0
- devflow_engine/source_docs_schema.py +207 -0
- devflow_engine/source_docs_updater.py +309 -0
- devflow_engine/source_scope/__init__.py +15 -0
- devflow_engine/source_scope/agentic.py +45 -0
- devflow_engine/source_scope/dag.py +1626 -0
- devflow_engine/source_scope/models.py +177 -0
- devflow_engine/stores/__init__.py +0 -0
- devflow_engine/stores/execution_store.py +3534 -0
- devflow_engine/story/__init__.py +0 -0
- devflow_engine/story/contracts.py +160 -0
- devflow_engine/story/discovery.py +47 -0
- devflow_engine/story/evidence.py +118 -0
- devflow_engine/story/hashing.py +27 -0
- devflow_engine/story/implemented_queue_purge.py +148 -0
- devflow_engine/story/indexer.py +105 -0
- devflow_engine/story/io.py +20 -0
- devflow_engine/story/markdown_contracts.py +298 -0
- devflow_engine/story/reconciliation.py +408 -0
- devflow_engine/story/validate_stories.py +149 -0
- devflow_engine/story/validate_tests_story.py +512 -0
- devflow_engine/story/validation.py +133 -0
- devflow_engine/ui_grounding/__init__.py +11 -0
- devflow_engine/ui_grounding/agentic.py +31 -0
- devflow_engine/ui_grounding/dag.py +874 -0
- devflow_engine/ui_grounding/models.py +224 -0
- devflow_engine/ui_grounding/pencil_bridge.py +247 -0
- devflow_engine/vendor/__init__.py +0 -0
- devflow_engine/vendor/datalumina_genai/__init__.py +11 -0
- devflow_engine/vendor/datalumina_genai/core/__init__.py +0 -0
- devflow_engine/vendor/datalumina_genai/core/exceptions.py +9 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/__init__.py +0 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/agent.py +48 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/agent_streaming_node.py +26 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/base.py +89 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/concurrent.py +30 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/router.py +69 -0
- devflow_engine/vendor/datalumina_genai/core/schema.py +72 -0
- devflow_engine/vendor/datalumina_genai/core/task.py +52 -0
- devflow_engine/vendor/datalumina_genai/core/validate.py +139 -0
- devflow_engine/vendor/datalumina_genai/core/workflow.py +200 -0
- devflow_engine/worker.py +1086 -0
- devflow_engine/worker_guard.py +233 -0
- devflow_engine-1.0.0.dist-info/METADATA +235 -0
- devflow_engine-1.0.0.dist-info/RECORD +393 -0
- devflow_engine-1.0.0.dist-info/WHEEL +4 -0
- devflow_engine-1.0.0.dist-info/entry_points.txt +3 -0
- devin/__init__.py +6 -0
- devin/dag.py +58 -0
- devin/dag_two_arm.py +138 -0
- devin/devin_chat_scenario_catalog.json +588 -0
- devin/devin_eval.py +677 -0
- devin/nodes/__init__.py +0 -0
- devin/nodes/ideation/__init__.py +0 -0
- devin/nodes/ideation/node.py +195 -0
- devin/nodes/ideation/playground.py +267 -0
- devin/nodes/ideation/prompt.md +65 -0
- devin/nodes/ideation/scenarios/continue_refinement.py +13 -0
- devin/nodes/ideation/scenarios/continue_refinement_evals.py +18 -0
- devin/nodes/ideation/scenarios/idea_fits_existing_patterns.py +17 -0
- devin/nodes/ideation/scenarios/idea_fits_existing_patterns_evals.py +16 -0
- devin/nodes/ideation/scenarios/large_idea_split.py +4 -0
- devin/nodes/ideation/scenarios/large_idea_split_evals.py +17 -0
- devin/nodes/ideation/scenarios/source_documentation_added.py +4 -0
- devin/nodes/ideation/scenarios/source_documentation_added_evals.py +16 -0
- devin/nodes/ideation/scenarios/user_says_create_it.py +30 -0
- devin/nodes/ideation/scenarios/user_says_create_it_evals.py +23 -0
- devin/nodes/ideation/scenarios/vague_idea.py +16 -0
- devin/nodes/ideation/scenarios/vague_idea_evals.py +47 -0
- devin/nodes/ideation/tools.json +312 -0
- devin/nodes/insight/__init__.py +0 -0
- devin/nodes/insight/node.py +49 -0
- devin/nodes/insight/playground.py +154 -0
- devin/nodes/insight/prompt.md +61 -0
- devin/nodes/insight/scenarios/architecture_pattern_query.py +15 -0
- devin/nodes/insight/scenarios/architecture_pattern_query_evals.py +25 -0
- devin/nodes/insight/scenarios/codebase_exploration.py +15 -0
- devin/nodes/insight/scenarios/codebase_exploration_evals.py +23 -0
- devin/nodes/insight/scenarios/devin_ideation_routing.py +19 -0
- devin/nodes/insight/scenarios/devin_ideation_routing_evals.py +39 -0
- devin/nodes/insight/scenarios/devin_insight_routing.py +20 -0
- devin/nodes/insight/scenarios/devin_insight_routing_evals.py +40 -0
- devin/nodes/insight/scenarios/operational_debugging.py +15 -0
- devin/nodes/insight/scenarios/operational_debugging_evals.py +23 -0
- devin/nodes/insight/scenarios/operational_question.py +9 -0
- devin/nodes/insight/scenarios/operational_question_evals.py +8 -0
- devin/nodes/insight/scenarios/queue_status.py +15 -0
- devin/nodes/insight/scenarios/queue_status_evals.py +23 -0
- devin/nodes/insight/scenarios/source_doc_explanation.py +14 -0
- devin/nodes/insight/scenarios/source_doc_explanation_evals.py +21 -0
- devin/nodes/insight/scenarios/worker_state_check.py +15 -0
- devin/nodes/insight/scenarios/worker_state_check_evals.py +22 -0
- devin/nodes/insight/tools.json +126 -0
- devin/nodes/intake/__init__.py +0 -0
- devin/nodes/intake/node.py +27 -0
- devin/nodes/intake/playground.py +47 -0
- devin/nodes/intake/prompt.md +12 -0
- devin/nodes/intake/scenarios/ideation_routing.py +4 -0
- devin/nodes/intake/scenarios/ideation_routing_evals.py +5 -0
- devin/nodes/intake/scenarios/insight_routing.py +4 -0
- devin/nodes/intake/scenarios/insight_routing_evals.py +5 -0
- devin/nodes/iterate/README.md +44 -0
- devin/nodes/iterate/__init__.py +1 -0
- devin/nodes/iterate/_archived_design_stages/01-objectives-requirements.md +112 -0
- devin/nodes/iterate/_archived_design_stages/02-evals.md +131 -0
- devin/nodes/iterate/_archived_design_stages/03-tools-and-boundaries.md +110 -0
- devin/nodes/iterate/_archived_design_stages/04-harness-and-playground.md +32 -0
- devin/nodes/iterate/_archived_design_stages/05-prompt-deferred.md +11 -0
- devin/nodes/iterate/_archived_design_stages/coder_agent_design/01-objectives-requirements.md +20 -0
- devin/nodes/iterate/_archived_design_stages/coder_agent_design/02-evals.md +8 -0
- devin/nodes/iterate/_archived_design_stages/coder_agent_design/03-tools-and-boundaries.md +14 -0
- devin/nodes/iterate/_archived_design_stages/coder_agent_design/04-harness-and-playground.md +12 -0
- devin/nodes/iterate/_archived_design_stages/framer_agent_design/01-objectives-requirements.md +20 -0
- devin/nodes/iterate/_archived_design_stages/framer_agent_design/02-evals.md +8 -0
- devin/nodes/iterate/_archived_design_stages/framer_agent_design/03-tools-and-boundaries.md +13 -0
- devin/nodes/iterate/_archived_design_stages/framer_agent_design/04-harness-and-playground.md +12 -0
- devin/nodes/iterate/_archived_design_stages/iterator_agent_design/01-objectives-requirements.md +25 -0
- devin/nodes/iterate/_archived_design_stages/iterator_agent_design/02-evals.md +9 -0
- devin/nodes/iterate/_archived_design_stages/iterator_agent_design/03-tools-and-boundaries.md +14 -0
- devin/nodes/iterate/_archived_design_stages/iterator_agent_design/04-harness-and-playground.md +12 -0
- devin/nodes/iterate/_archived_design_stages/observer_agent_design/01-objectives-requirements.md +20 -0
- devin/nodes/iterate/_archived_design_stages/observer_agent_design/02-evals.md +8 -0
- devin/nodes/iterate/_archived_design_stages/observer_agent_design/03-tools-and-boundaries.md +14 -0
- devin/nodes/iterate/_archived_design_stages/observer_agent_design/04-harness-and-playground.md +13 -0
- devin/nodes/iterate/agent-roles.md +89 -0
- devin/nodes/iterate/agents/README.md +10 -0
- devin/nodes/iterate/artifacts.md +504 -0
- devin/nodes/iterate/contract.md +100 -0
- devin/nodes/iterate/eval-plan.md +74 -0
- devin/nodes/iterate/node.py +100 -0
- devin/nodes/iterate/pipeline/README.md +13 -0
- devin/nodes/iterate/playground-contract.md +76 -0
- devin/nodes/iterate/prompt.md +11 -0
- devin/nodes/iterate/scenarios/README.md +38 -0
- devin/nodes/iterate/scenarios/artifact-and-loop-scenarios.md +101 -0
- devin/nodes/iterate/scenarios/coder_artifact_alignment.py +32 -0
- devin/nodes/iterate/scenarios/coder_artifact_alignment_evals.py +45 -0
- devin/nodes/iterate/scenarios/coder_bounded_fix.py +27 -0
- devin/nodes/iterate/scenarios/coder_bounded_fix_evals.py +45 -0
- devin/nodes/iterate/scenarios/devin_iterate_routing.py +21 -0
- devin/nodes/iterate/scenarios/devin_iterate_routing_evals.py +36 -0
- devin/nodes/iterate/scenarios/framer_scope_boundary.py +25 -0
- devin/nodes/iterate/scenarios/framer_scope_boundary_evals.py +57 -0
- devin/nodes/iterate/scenarios/framer_task_framing.py +25 -0
- devin/nodes/iterate/scenarios/framer_task_framing_evals.py +58 -0
- devin/nodes/iterate/scenarios/iterate_error_fix.py +21 -0
- devin/nodes/iterate/scenarios/iterate_error_fix_evals.py +39 -0
- devin/nodes/iterate/scenarios/iterate_quick_change.py +21 -0
- devin/nodes/iterate/scenarios/iterate_quick_change_evals.py +35 -0
- devin/nodes/iterate/scenarios/iterate_to_idea_promotion.py +23 -0
- devin/nodes/iterate/scenarios/iterate_to_idea_promotion_evals.py +53 -0
- devin/nodes/iterate/scenarios/iterate_to_insight_reroute.py +23 -0
- devin/nodes/iterate/scenarios/iterate_to_insight_reroute_evals.py +53 -0
- devin/nodes/iterate/scenarios/observer_evidence_seam.py +28 -0
- devin/nodes/iterate/scenarios/observer_evidence_seam_evals.py +55 -0
- devin/nodes/iterate/scenarios/observer_repro_creation.py +28 -0
- devin/nodes/iterate/scenarios/observer_repro_creation_evals.py +45 -0
- devin/nodes/iterate/scenarios/routing-matrix.md +45 -0
- devin/nodes/shared/__init__.py +0 -0
- devin/nodes/shared/filemaker_expert.md +80 -0
- devin/nodes/shared/filemaker_expert.py +354 -0
- devin/nodes/shared/filemaker_expert_eval/runner.py +176 -0
- devin/nodes/shared/filemaker_expert_eval/scenarios.json +65 -0
- devin/nodes/shared/goldilocks_advisor_eval/runner.py +214 -0
- devin/nodes/shared/goldilocks_advisor_eval/scenarios.json +58 -0
- devin/nodes/shared/helpers.py +156 -0
- devin/nodes/shared/idea_compliance_advisor_eval/runner.py +252 -0
- devin/nodes/shared/idea_compliance_advisor_eval/scenarios.json +75 -0
- devin/nodes/shared/models.py +44 -0
- devin/nodes/shared/post.py +40 -0
- devin/nodes/shared/router.py +107 -0
- devin/nodes/shared/tools.py +191 -0
- devin/shared/devin-chat-rubric.md +237 -0
- devin/shared/devin-chat-scenario-suite.md +90 -0
- devin/shared/eval_doctrine.md +9 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
QueueItemKind = Literal["scope", "idea_creation", "idea", "story", "integration", "recovery"]
|
|
9
|
+
RecoveryDisposition = Literal["replayable", "blocked"]
|
|
10
|
+
RecoveryAction = Literal[
|
|
11
|
+
"requeue_without_fix",
|
|
12
|
+
"repair_artifact_then_requeue",
|
|
13
|
+
"adjust_story_shaping_policy_then_requeue",
|
|
14
|
+
"manual_review_required",
|
|
15
|
+
"dead_letter",
|
|
16
|
+
"none",
|
|
17
|
+
]
|
|
18
|
+
FixScope = Literal["item_local", "systemic"]
|
|
19
|
+
ReplayScope = Literal["single_item", "similar_failed_items"]
|
|
20
|
+
FailureType = Literal["code_error", "process_error"]
|
|
21
|
+
RecoveryStrategy = Literal[
|
|
22
|
+
"handoff_to_code_error",
|
|
23
|
+
"replay_boundary_recovery",
|
|
24
|
+
"state_reconciliation_recovery",
|
|
25
|
+
"artifact_regeneration_recovery",
|
|
26
|
+
"preflight_health_repair_recovery",
|
|
27
|
+
"escalate_process_bug",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class FailedQueueItemArtifact(BaseModel):
|
|
32
|
+
queue_type: QueueItemKind
|
|
33
|
+
item_id: str
|
|
34
|
+
project_id: str | None = None
|
|
35
|
+
dfs_project_id: str | None = None
|
|
36
|
+
enqueue_run_id: str
|
|
37
|
+
status: str
|
|
38
|
+
title: str
|
|
39
|
+
payload_ref: str | None = None
|
|
40
|
+
payload_exists: bool = False
|
|
41
|
+
story_id: str | None = None
|
|
42
|
+
failure_message: str | None = None
|
|
43
|
+
failure_context: dict = Field(default_factory=dict)
|
|
44
|
+
raw_row: dict = Field(default_factory=dict)
|
|
45
|
+
discovered_artifacts: dict[str, Any] = Field(default_factory=dict)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class RecoverySuccessCriterion(BaseModel):
|
|
49
|
+
criterion: str
|
|
50
|
+
oracle: str
|
|
51
|
+
evidence_ref: str | None = None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class RecoveryNonConvergenceArtifact(BaseModel):
|
|
55
|
+
summary: str
|
|
56
|
+
reason: str
|
|
57
|
+
attempts_reviewed: int = 0
|
|
58
|
+
same_failure_surface: bool = False
|
|
59
|
+
unchanged_test_surface: bool = False
|
|
60
|
+
wrong_seam: bool = False
|
|
61
|
+
downstream_blocker: bool = False
|
|
62
|
+
downstream_blocker_stage: str | None = None
|
|
63
|
+
schema_or_runtime_drift: bool = False
|
|
64
|
+
measurable_progress: bool = True
|
|
65
|
+
evidence: list[str] = Field(default_factory=list)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class RecoveryInvestigationArtifact(BaseModel):
|
|
69
|
+
queue_type: QueueItemKind
|
|
70
|
+
item_id: str
|
|
71
|
+
summary: str
|
|
72
|
+
failure_nature: str
|
|
73
|
+
evidence: list[str] = Field(default_factory=list)
|
|
74
|
+
primary_evidence_source: str | None = None
|
|
75
|
+
primary_evidence_refs: list[str] = Field(default_factory=list)
|
|
76
|
+
primary_log_insight: str | None = None
|
|
77
|
+
affected_boundary: str | None = None
|
|
78
|
+
likely_failed_stage: str | None = None
|
|
79
|
+
confidence: str = "medium"
|
|
80
|
+
recovery_goal: str
|
|
81
|
+
success_criteria: list[RecoverySuccessCriterion] = Field(default_factory=list)
|
|
82
|
+
verification_evidence: list[str] = Field(default_factory=list)
|
|
83
|
+
replay_path: str | None = None
|
|
84
|
+
escalation_conditions: list[str] = Field(default_factory=list)
|
|
85
|
+
non_convergence: RecoveryNonConvergenceArtifact | None = None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class RecoveryDiagnosisArtifact(BaseModel):
|
|
89
|
+
queue_type: QueueItemKind
|
|
90
|
+
item_id: str
|
|
91
|
+
strategy: RecoveryStrategy
|
|
92
|
+
summary: str
|
|
93
|
+
rationale: str
|
|
94
|
+
verification_targets: list[RecoverySuccessCriterion] = Field(default_factory=list)
|
|
95
|
+
replay_path: str | None = None
|
|
96
|
+
suggested_action: RecoveryAction = "none"
|
|
97
|
+
evidence: list[str] = Field(default_factory=list)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class RemediationPlanArtifact(BaseModel):
|
|
101
|
+
queue_type: QueueItemKind
|
|
102
|
+
action: RecoveryAction
|
|
103
|
+
summary: str
|
|
104
|
+
steps: list[str] = Field(default_factory=list)
|
|
105
|
+
bounded: bool = True
|
|
106
|
+
preserve_failure_context: bool = True
|
|
107
|
+
fix_scope: FixScope = "item_local"
|
|
108
|
+
replay_scope: ReplayScope = "single_item"
|
|
109
|
+
replay_path: str | None = None
|
|
110
|
+
remediation_artifact: str | None = None
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class FixApplicationArtifact(BaseModel):
|
|
114
|
+
queue_type: QueueItemKind
|
|
115
|
+
action_taken: RecoveryAction
|
|
116
|
+
changed: bool
|
|
117
|
+
notes: list[str] = Field(default_factory=list)
|
|
118
|
+
remediation_artifact: str | None = None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class RecoveryExecutionArtifact(BaseModel):
|
|
122
|
+
queue_type: QueueItemKind
|
|
123
|
+
item_id: str
|
|
124
|
+
outcome: Literal["reenqueued", "delegated", "blocked"]
|
|
125
|
+
execution_summary: str
|
|
126
|
+
preserve_failure_context: bool = True
|
|
127
|
+
delegation_summary: str | None = None
|
|
128
|
+
attempts_used: int = 1
|
|
129
|
+
success_criteria: list[RecoverySuccessCriterion] = Field(default_factory=list)
|
|
130
|
+
verification_summary: str | None = None
|
|
131
|
+
primary_evidence_source: str | None = None
|
|
132
|
+
primary_evidence_refs: list[str] = Field(default_factory=list)
|
|
133
|
+
primary_log_insight: str | None = None
|
|
134
|
+
recovery_handoff_artifact_path: str | None = None
|
|
135
|
+
recovery_handoff_summary: str | None = None
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class PreReplayCheckArtifact(BaseModel):
|
|
139
|
+
queue_type: QueueItemKind
|
|
140
|
+
ready: bool
|
|
141
|
+
checks: list[str] = Field(default_factory=list)
|
|
142
|
+
blocking_reasons: list[str] = Field(default_factory=list)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class ReenqueueArtifact(BaseModel):
|
|
146
|
+
queue_type: QueueItemKind
|
|
147
|
+
item_id: str
|
|
148
|
+
status: str
|
|
149
|
+
failure_context: dict | None = None
|
|
150
|
+
replay_metadata: dict[str, Any] | None = None
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class RecoveryHandoffArtifact(BaseModel):
|
|
154
|
+
queue_type: QueueItemKind
|
|
155
|
+
item_id: str
|
|
156
|
+
story_id: str | None = None
|
|
157
|
+
implementation_run_id: str | None = None
|
|
158
|
+
failed_stage: str | None = None
|
|
159
|
+
primary_evidence_source: str | None = None
|
|
160
|
+
primary_evidence_refs: list[str] = Field(default_factory=list)
|
|
161
|
+
key_log_insight: str | None = None
|
|
162
|
+
failing_surface_summary: str
|
|
163
|
+
likely_seam: str | None = None
|
|
164
|
+
disproven_dead_ends: list[str] = Field(default_factory=list)
|
|
165
|
+
verification_blockers: list[str] = Field(default_factory=list)
|
|
166
|
+
non_convergence_insight: str | None = None
|
|
167
|
+
produced_by_recovery_run_id: str | None = None
|
|
168
|
+
consumer_hint: str = "Attach this artifact to the next Green attempt for the same story when present."
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class RecoveryOutcomeArtifact(BaseModel):
|
|
172
|
+
queue_type: QueueItemKind
|
|
173
|
+
item_id: str
|
|
174
|
+
project_id: str | None = None
|
|
175
|
+
outcome: Literal["reenqueued", "blocked", "delegated", "recovered"]
|
|
176
|
+
summary: str
|
|
177
|
+
investigation: RecoveryInvestigationArtifact | None = None
|
|
178
|
+
plan: RemediationPlanArtifact | None = None
|
|
179
|
+
reenqueue: ReenqueueArtifact | None = None
|
|
180
|
+
recovery_handoff_artifact_path: str | None = None
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class SystemicPatternArtifact(BaseModel):
|
|
184
|
+
failure_signature: str
|
|
185
|
+
is_systemic: bool
|
|
186
|
+
affected_queue_type: str
|
|
187
|
+
affected_item_ids: list[str]
|
|
188
|
+
sample_failure_messages: list[str]
|
|
189
|
+
failed_stages: list[str]
|
|
190
|
+
total_affected: int
|
|
191
|
+
pattern_summary: str
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class FilePatch(BaseModel):
|
|
195
|
+
path: str
|
|
196
|
+
content: str
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class CodeRootCauseArtifact(BaseModel):
|
|
200
|
+
failure_signature: str
|
|
201
|
+
root_cause_location: str
|
|
202
|
+
root_cause_description: str
|
|
203
|
+
fix_type: Literal["code_fix", "config_fix", "environment_fix", "dead_letter", "item_local_only"]
|
|
204
|
+
specific_fix_plan: str
|
|
205
|
+
files_inspected: list[str]
|
|
206
|
+
confidence: Literal["high", "medium", "low"]
|
|
207
|
+
affects_all_items: bool
|
|
208
|
+
residual_items_needing_attention: list[str]
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class RemediationResultArtifact(BaseModel):
|
|
212
|
+
fix_type: str
|
|
213
|
+
fix_applied: bool
|
|
214
|
+
files_changed: list[str]
|
|
215
|
+
fix_summary: str
|
|
216
|
+
verification_notes: str
|
|
217
|
+
ready_to_requeue: bool
|
|
218
|
+
items_to_requeue: list[str]
|
|
219
|
+
items_to_dead_letter: list[str]
|
|
220
|
+
file_patches: list[FilePatch] = Field(default_factory=list)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""Unified refactor runner.
|
|
4
|
+
|
|
5
|
+
Consumes a `devflow enforce` report (missing + directives) and asks an LLM to
|
|
6
|
+
produce a unified diff to fix the issues.
|
|
7
|
+
|
|
8
|
+
This intentionally does not attempt to be clever per-kind; it is one refactor
|
|
9
|
+
loop for packages + modules.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import subprocess
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from .llm.cli_stream import run_streaming
|
|
19
|
+
from .llm.cli_one_shot import run_one_shot
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _extract_diff(text: str) -> str | None:
|
|
23
|
+
# Prefer fenced ```diff blocks.
|
|
24
|
+
if "```" in text:
|
|
25
|
+
parts = text.split("```")
|
|
26
|
+
for i in range(len(parts) - 1):
|
|
27
|
+
body = parts[i + 1]
|
|
28
|
+
lines = body.splitlines()
|
|
29
|
+
if lines and lines[0].strip().lower() in {"diff", "patch"}:
|
|
30
|
+
body = "\n".join(lines[1:])
|
|
31
|
+
body = body.strip("\n")
|
|
32
|
+
if body.startswith("diff --git"):
|
|
33
|
+
return body + "\n"
|
|
34
|
+
# Fallback: find first diff header.
|
|
35
|
+
idx = text.find("diff --git")
|
|
36
|
+
if idx != -1:
|
|
37
|
+
return text[idx:].strip("\n") + "\n"
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _run(cmd: list[str], *, cwd: Path) -> subprocess.CompletedProcess[str]:
|
|
42
|
+
return subprocess.run(cmd, cwd=str(cwd), text=True, capture_output=True, check=False)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def refactor_from_enforce_report(
|
|
46
|
+
*,
|
|
47
|
+
repo_root: Path,
|
|
48
|
+
report: dict[str, object],
|
|
49
|
+
base_cmd: str,
|
|
50
|
+
delivery: str,
|
|
51
|
+
stream: bool,
|
|
52
|
+
) -> dict[str, object]:
|
|
53
|
+
prompt = {
|
|
54
|
+
"task": "refactor_to_satisfy_devflow_enforce",
|
|
55
|
+
"repo_root": str(repo_root),
|
|
56
|
+
"enforce_report": report,
|
|
57
|
+
"instructions": [
|
|
58
|
+
"You must output ONLY a unified git diff (no commentary).",
|
|
59
|
+
"Fix the enforce_report issues by refactoring code:",
|
|
60
|
+
"- For refactor_internal_module: rewrite imports/specifiers to use the canonical module path.",
|
|
61
|
+
"- For refactor_external_package: remove usage/imports of the forbidden package and migrate to the canonical one.",
|
|
62
|
+
"- For missing imports: either remove the dependency or replace it with already-registered canonical pathways.",
|
|
63
|
+
"Prefer small, safe edits.",
|
|
64
|
+
],
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
prompt_text = json.dumps(prompt, indent=2, sort_keys=True)
|
|
68
|
+
|
|
69
|
+
if stream:
|
|
70
|
+
sres = run_streaming(provider="cli", base_cmd=base_cmd, delivery=delivery, prompt=prompt_text, cwd=repo_root)
|
|
71
|
+
if not sres.ok:
|
|
72
|
+
return {"ok": False, "stderr": sres.stderr or sres.stdout, "stdout": sres.stdout, "log_path": sres.log_path}
|
|
73
|
+
out_text = sres.stdout
|
|
74
|
+
else:
|
|
75
|
+
r = run_one_shot(base_cmd=base_cmd, delivery=delivery, prompt=prompt_text, cwd=repo_root)
|
|
76
|
+
if not r.ok:
|
|
77
|
+
return {"ok": False, "stderr": r.stderr or r.stdout, "stdout": r.stdout}
|
|
78
|
+
out_text = r.stdout
|
|
79
|
+
|
|
80
|
+
diff = _extract_diff(out_text)
|
|
81
|
+
if not diff:
|
|
82
|
+
return {"ok": False, "stderr": "LLM did not return a diff", "stdout": out_text}
|
|
83
|
+
|
|
84
|
+
# Apply diff.
|
|
85
|
+
patch_path = repo_root / ".devflow" / "refactor" / "last.patch"
|
|
86
|
+
patch_path.parent.mkdir(parents=True, exist_ok=True)
|
|
87
|
+
patch_path.write_text(diff, encoding="utf-8")
|
|
88
|
+
|
|
89
|
+
cp = _run(["git", "apply", "--whitespace=nowarn", str(patch_path)], cwd=repo_root)
|
|
90
|
+
if cp.returncode != 0:
|
|
91
|
+
return {"ok": False, "stderr": cp.stderr, "stdout": cp.stdout, "patch": str(patch_path)}
|
|
92
|
+
|
|
93
|
+
return {"ok": True, "patch": str(patch_path)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Registry helpers (module cards + package policy gate)."""
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import re
|
|
5
|
+
from collections import Counter, defaultdict
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_TS_EXPORT_RE = re.compile(
|
|
11
|
+
r"^\s*export\s+(?:default\s+)?(?:(?:async\s+)?function\s+(?P<fn>[A-Za-z_\$][\w\$]*)|class\s+(?P<class>[A-Za-z_\$][\w\$]*)|const\s+(?P<const>[A-Za-z_\$][\w\$]*)|let\s+(?P<let>[A-Za-z_\$][\w\$]*)|var\s+(?P<var>[A-Za-z_\$][\w\$]*)|type\s+(?P<type>[A-Za-z_\$][\w\$]*)|interface\s+(?P<iface>[A-Za-z_\$][\w\$]*))\b"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
_TS_EXPORT_LIST_RE = re.compile(r"^\s*export\s*\{(?P<body>[^}]+)\}\s*;?\s*$")
|
|
15
|
+
|
|
16
|
+
_PY_DEF_RE = re.compile(r"^def\s+(?P<fn>[A-Za-z_][\w]*)\s*\(")
|
|
17
|
+
_PY_CLASS_RE = re.compile(r"^class\s+(?P<class>[A-Za-z_][\w]*)\s*(\(|:)")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class ExportSymbol:
|
|
22
|
+
name: str
|
|
23
|
+
kind: str # function|class|const|type|interface|reexport
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _iter_project_files(repo_root: Path, roots: list[Path]) -> list[Path]:
|
|
27
|
+
exts = {".ts", ".tsx", ".js", ".jsx", ".py"}
|
|
28
|
+
|
|
29
|
+
ignore_dirnames = {
|
|
30
|
+
"node_modules",
|
|
31
|
+
".next",
|
|
32
|
+
"dist",
|
|
33
|
+
"build",
|
|
34
|
+
"coverage",
|
|
35
|
+
".turbo",
|
|
36
|
+
".git",
|
|
37
|
+
".venv",
|
|
38
|
+
"__pycache__",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
import os
|
|
42
|
+
|
|
43
|
+
out: list[Path] = []
|
|
44
|
+
for r in roots:
|
|
45
|
+
if not r.exists():
|
|
46
|
+
continue
|
|
47
|
+
for dirpath, dirnames, filenames in os.walk(r):
|
|
48
|
+
dirnames[:] = [d for d in dirnames if d not in ignore_dirnames and not d.startswith(".")]
|
|
49
|
+
for fn in filenames:
|
|
50
|
+
p = Path(dirpath) / fn
|
|
51
|
+
if p.suffix in exts:
|
|
52
|
+
out.append(p)
|
|
53
|
+
return sorted(out)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def extract_exports(path: Path) -> list[ExportSymbol]:
|
|
57
|
+
"""Best-effort export extraction.
|
|
58
|
+
|
|
59
|
+
Deterministic + cheap; not a full parser.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
lines = path.read_text(encoding="utf-8").splitlines()
|
|
64
|
+
except Exception:
|
|
65
|
+
return []
|
|
66
|
+
|
|
67
|
+
out: list[ExportSymbol] = []
|
|
68
|
+
|
|
69
|
+
if path.suffix == ".py":
|
|
70
|
+
for ln in lines:
|
|
71
|
+
if ln.startswith(" ") or ln.startswith("\t"):
|
|
72
|
+
continue
|
|
73
|
+
if m := _PY_DEF_RE.match(ln):
|
|
74
|
+
out.append(ExportSymbol(name=m.group("fn"), kind="function"))
|
|
75
|
+
elif m := _PY_CLASS_RE.match(ln):
|
|
76
|
+
out.append(ExportSymbol(name=m.group("class"), kind="class"))
|
|
77
|
+
return out
|
|
78
|
+
|
|
79
|
+
# TS/JS
|
|
80
|
+
for ln in lines:
|
|
81
|
+
if m := _TS_EXPORT_RE.match(ln):
|
|
82
|
+
for kind, key in [
|
|
83
|
+
("function", "fn"),
|
|
84
|
+
("class", "class"),
|
|
85
|
+
("const", "const"),
|
|
86
|
+
("const", "let"),
|
|
87
|
+
("const", "var"),
|
|
88
|
+
("type", "type"),
|
|
89
|
+
("interface", "iface"),
|
|
90
|
+
]:
|
|
91
|
+
name = m.groupdict().get(key)
|
|
92
|
+
if name:
|
|
93
|
+
out.append(ExportSymbol(name=name, kind=kind))
|
|
94
|
+
break
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
if m := _TS_EXPORT_LIST_RE.match(ln):
|
|
98
|
+
body = m.group("body")
|
|
99
|
+
parts = [p.strip() for p in body.split(",") if p.strip()]
|
|
100
|
+
for p in parts:
|
|
101
|
+
# handle `a as b`
|
|
102
|
+
name = p.split(" as ")[-1].strip()
|
|
103
|
+
if name:
|
|
104
|
+
out.append(ExportSymbol(name=name, kind="reexport"))
|
|
105
|
+
|
|
106
|
+
# stable
|
|
107
|
+
out.sort(key=lambda s: (s.kind, s.name))
|
|
108
|
+
return out
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def make_module_card(
|
|
112
|
+
*,
|
|
113
|
+
repo_root: Path,
|
|
114
|
+
importer_rel: str,
|
|
115
|
+
specifier: str,
|
|
116
|
+
export_symbols: list[ExportSymbol],
|
|
117
|
+
used_by: list[str],
|
|
118
|
+
snippets: list[str],
|
|
119
|
+
external_packages: list[str],
|
|
120
|
+
internal_deps: list[str],
|
|
121
|
+
) -> dict[str, object]:
|
|
122
|
+
return {
|
|
123
|
+
"item_id": f"module:{importer_rel}",
|
|
124
|
+
"path": importer_rel,
|
|
125
|
+
"exports": [s.__dict__ for s in export_symbols],
|
|
126
|
+
"usage": {
|
|
127
|
+
"importer_count": len(used_by),
|
|
128
|
+
"top_importers": used_by,
|
|
129
|
+
"snippets": snippets,
|
|
130
|
+
},
|
|
131
|
+
"imports": {
|
|
132
|
+
"external_packages": external_packages,
|
|
133
|
+
"internal_modules": internal_deps,
|
|
134
|
+
},
|
|
135
|
+
"heuristic_summary": {
|
|
136
|
+
"export_count": len(export_symbols),
|
|
137
|
+
"external_pkg_count": len(external_packages),
|
|
138
|
+
"internal_dep_count": len(internal_deps),
|
|
139
|
+
"used_by_count": len(used_by),
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def extract_import_snippets(*, text: str, needle: str, context: int = 2, limit: int = 3) -> list[str]:
|
|
145
|
+
lines = text.splitlines()
|
|
146
|
+
out: list[str] = []
|
|
147
|
+
for i, ln in enumerate(lines):
|
|
148
|
+
if needle not in ln:
|
|
149
|
+
continue
|
|
150
|
+
start = max(0, i - context)
|
|
151
|
+
end = min(len(lines), i + context + 1)
|
|
152
|
+
snippet = "\n".join(lines[start:end])
|
|
153
|
+
out.append(snippet)
|
|
154
|
+
if len(out) >= limit:
|
|
155
|
+
break
|
|
156
|
+
return out
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def build_module_cards(
|
|
160
|
+
*,
|
|
161
|
+
repo_root: Path,
|
|
162
|
+
roots: list[Path],
|
|
163
|
+
imports_rows: list[tuple[str, str, str, str]],
|
|
164
|
+
usage_snippet_limit: int = 3,
|
|
165
|
+
) -> list[dict[str, object]]:
|
|
166
|
+
"""Build module cards for internal modules and their usage.
|
|
167
|
+
|
|
168
|
+
imports_rows matches schema: (importer, specifier, kind, resolved_id).
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
# Build reverse index: resolved_id -> list[(importer, specifier)]
|
|
172
|
+
used_by: dict[str, list[tuple[str, str]]] = defaultdict(list)
|
|
173
|
+
for importer, spec, kind, resolved in imports_rows:
|
|
174
|
+
if kind != "internal":
|
|
175
|
+
continue
|
|
176
|
+
used_by[str(resolved)].append((str(importer), str(spec)))
|
|
177
|
+
|
|
178
|
+
# Build per-file imports summary
|
|
179
|
+
per_importer_external: dict[str, Counter[str]] = defaultdict(Counter)
|
|
180
|
+
per_importer_internal: dict[str, Counter[str]] = defaultdict(Counter)
|
|
181
|
+
for importer, spec, kind, resolved in imports_rows:
|
|
182
|
+
if kind == "external":
|
|
183
|
+
pkg = str(resolved).split(":", 1)[1]
|
|
184
|
+
per_importer_external[str(importer)][pkg] += 1
|
|
185
|
+
elif kind == "internal":
|
|
186
|
+
per_importer_internal[str(importer)][str(resolved)] += 1
|
|
187
|
+
|
|
188
|
+
cards: list[dict[str, object]] = []
|
|
189
|
+
for p in _iter_project_files(repo_root, roots):
|
|
190
|
+
rel = p.relative_to(repo_root).as_posix()
|
|
191
|
+
rel_no_ext = re.sub(r"\.[a-zA-Z0-9]+$", "", rel)
|
|
192
|
+
item_id = f"module:{rel_no_ext}"
|
|
193
|
+
|
|
194
|
+
exports = extract_exports(p)
|
|
195
|
+
|
|
196
|
+
# usage (where used)
|
|
197
|
+
importers = sorted({imp for (imp, _spec) in used_by.get(item_id, [])})
|
|
198
|
+
importers_top = importers[:10]
|
|
199
|
+
|
|
200
|
+
snippets: list[str] = []
|
|
201
|
+
for imp, spec in used_by.get(item_id, [])[:50]:
|
|
202
|
+
imp_path = repo_root / imp
|
|
203
|
+
if not imp_path.exists():
|
|
204
|
+
continue
|
|
205
|
+
try:
|
|
206
|
+
text = imp_path.read_text(encoding="utf-8")
|
|
207
|
+
except Exception:
|
|
208
|
+
continue
|
|
209
|
+
snippets.extend(extract_import_snippets(text=text, needle=spec, limit=usage_snippet_limit))
|
|
210
|
+
if len(snippets) >= usage_snippet_limit:
|
|
211
|
+
snippets = snippets[:usage_snippet_limit]
|
|
212
|
+
break
|
|
213
|
+
|
|
214
|
+
external_pkgs = sorted(per_importer_external.get(rel, Counter()).keys())
|
|
215
|
+
internal_mods = sorted({rid for rid in per_importer_internal.get(rel, Counter()).keys()})
|
|
216
|
+
|
|
217
|
+
cards.append(
|
|
218
|
+
make_module_card(
|
|
219
|
+
repo_root=repo_root,
|
|
220
|
+
importer_rel=rel_no_ext,
|
|
221
|
+
specifier="",
|
|
222
|
+
export_symbols=exports,
|
|
223
|
+
used_by=importers_top,
|
|
224
|
+
snippets=snippets,
|
|
225
|
+
external_packages=external_pkgs,
|
|
226
|
+
internal_deps=internal_mods,
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
cards.sort(key=lambda c: str(c.get("path") or ""))
|
|
231
|
+
return cards
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def write_cards_jsonl(cards: list[dict[str, object]], out_path: Path) -> None:
|
|
235
|
+
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
236
|
+
with out_path.open("w", encoding="utf-8") as f:
|
|
237
|
+
for c in cards:
|
|
238
|
+
f.write(json.dumps(c, sort_keys=True) + "\n")
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""Deterministic domain name normalization.
|
|
4
|
+
|
|
5
|
+
Goal: reduce taxonomy entropy without requiring a human in the loop.
|
|
6
|
+
|
|
7
|
+
Rules:
|
|
8
|
+
- slugify (lowercase, hyphens)
|
|
9
|
+
- apply small synonym map (config->configuration, etc.)
|
|
10
|
+
- collapse trivial plurals/singulars (contract/contracts)
|
|
11
|
+
|
|
12
|
+
This is intentionally conservative.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import re
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
_SYNONYMS: dict[str, str] = {
|
|
19
|
+
"config": "configuration",
|
|
20
|
+
"configs": "configuration",
|
|
21
|
+
"configuration": "configuration",
|
|
22
|
+
"planning": "planning",
|
|
23
|
+
"plan": "planning",
|
|
24
|
+
"plans": "planning",
|
|
25
|
+
"validate": "validation",
|
|
26
|
+
"validations": "validation",
|
|
27
|
+
"validation": "validation",
|
|
28
|
+
"contract": "contracts",
|
|
29
|
+
"contracts": "contracts",
|
|
30
|
+
"testing": "testing",
|
|
31
|
+
"test": "testing",
|
|
32
|
+
"tests": "testing",
|
|
33
|
+
"workflow": "workflow",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def slugify_domain(name: str) -> str:
|
|
38
|
+
s = (name or "").strip().lower()
|
|
39
|
+
s = s.replace("_", "-")
|
|
40
|
+
s = re.sub(r"[^a-z0-9-]+", "-", s)
|
|
41
|
+
s = re.sub(r"-+", "-", s).strip("-")
|
|
42
|
+
return s
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def normalize_domain(name: str) -> str:
|
|
46
|
+
s = slugify_domain(name)
|
|
47
|
+
if not s:
|
|
48
|
+
return "unknown"
|
|
49
|
+
|
|
50
|
+
# Direct synonym
|
|
51
|
+
if s in _SYNONYMS:
|
|
52
|
+
return _SYNONYMS[s]
|
|
53
|
+
|
|
54
|
+
# Singular/plural fold for simple cases.
|
|
55
|
+
if s.endswith("s") and s[:-1] in _SYNONYMS:
|
|
56
|
+
return _SYNONYMS[s[:-1]]
|
|
57
|
+
if (s + "s") in _SYNONYMS:
|
|
58
|
+
return _SYNONYMS[s + "s"]
|
|
59
|
+
|
|
60
|
+
return s
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""Cheap static effect-signature extraction.
|
|
4
|
+
|
|
5
|
+
Goal: provide a general-purpose signal for "what this module does" without
|
|
6
|
+
building a full parser or hardcoding repo-specific detectors.
|
|
7
|
+
|
|
8
|
+
This intentionally uses conservative heuristics (regex-ish token matches).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
_EFFECT_PATTERNS: list[tuple[str, re.Pattern[str]]] = [
|
|
16
|
+
("subprocess.run", re.compile(r"\bsubprocess\.run\b")),
|
|
17
|
+
("filesystem.write", re.compile(r"\bwrite_text\b|\bwrite_bytes\b|\bopen\([^)]*['\"]w")),
|
|
18
|
+
("filesystem.read", re.compile(r"\bread_text\b|\bread_bytes\b|\bopen\([^)]*['\"]r")),
|
|
19
|
+
("execution_store", re.compile(r"\bExecutionStore\b|\bstores\.execution_store\b")),
|
|
20
|
+
("timeline.events", re.compile(r"\badd_event\b|\badd_error\b")),
|
|
21
|
+
("story.contract", re.compile(r"\bget_story\b|\bupsert_story\b|\bstory\b.*\bcontract\b")),
|
|
22
|
+
("devflow.artifacts", re.compile(r"\.devflow/")),
|
|
23
|
+
("cli.typer", re.compile(r"\btyper\.")),
|
|
24
|
+
("network.http", re.compile(r"\brequests\b|\bhttpx\b|\burllib\b")),
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def extract_effect_tokens_from_text(text: str) -> list[str]:
|
|
29
|
+
toks: list[str] = []
|
|
30
|
+
for name, pat in _EFFECT_PATTERNS:
|
|
31
|
+
if pat.search(text):
|
|
32
|
+
toks.append(name)
|
|
33
|
+
return sorted(set(toks))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def read_module_text(repo_root: Path, module_id: str) -> str | None:
|
|
37
|
+
if not module_id.startswith("module:"):
|
|
38
|
+
return None
|
|
39
|
+
rel = module_id.split(":", 1)[1]
|
|
40
|
+
|
|
41
|
+
# Try py first, then ts/tsx/js/jsx.
|
|
42
|
+
for ext in [".py", ".ts", ".tsx", ".js", ".jsx"]:
|
|
43
|
+
p = repo_root / (rel + ext)
|
|
44
|
+
if p.exists():
|
|
45
|
+
try:
|
|
46
|
+
return p.read_text(encoding="utf-8")
|
|
47
|
+
except Exception:
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
# Also allow package __init__.py for python packages.
|
|
51
|
+
init = repo_root / rel / "__init__.py"
|
|
52
|
+
if init.exists():
|
|
53
|
+
try:
|
|
54
|
+
return init.read_text(encoding="utf-8")
|
|
55
|
+
except Exception:
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def extract_effect_tokens(repo_root: Path, module_id: str) -> list[str]:
|
|
62
|
+
text = read_module_text(repo_root, module_id)
|
|
63
|
+
if not text:
|
|
64
|
+
return []
|
|
65
|
+
return extract_effect_tokens_from_text(text)
|