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.
Files changed (393) hide show
  1. devflow_engine/__init__.py +3 -0
  2. devflow_engine/agentic_prompts.py +100 -0
  3. devflow_engine/agentic_runtime.py +398 -0
  4. devflow_engine/api_key_flow_harness.py +539 -0
  5. devflow_engine/api_keys.py +357 -0
  6. devflow_engine/bootstrap/__init__.py +2 -0
  7. devflow_engine/bootstrap/provision_from_template.py +84 -0
  8. devflow_engine/cli/__init__.py +0 -0
  9. devflow_engine/cli/app.py +7270 -0
  10. devflow_engine/core/__init__.py +0 -0
  11. devflow_engine/core/config.py +86 -0
  12. devflow_engine/core/logging.py +29 -0
  13. devflow_engine/core/paths.py +45 -0
  14. devflow_engine/core/toml_kv.py +33 -0
  15. devflow_engine/devflow_event_worker.py +1292 -0
  16. devflow_engine/devflow_state.py +201 -0
  17. devflow_engine/devin2/__init__.py +9 -0
  18. devflow_engine/devin2/agent_definition.py +120 -0
  19. devflow_engine/devin2/pi_runner.py +204 -0
  20. devflow_engine/devin_orchestration.py +69 -0
  21. devflow_engine/docs/prompts/anti-patterns.md +42 -0
  22. devflow_engine/docs/prompts/devin-agent-prompt.md +55 -0
  23. devflow_engine/docs/prompts/devin2-agent-prompt.md +81 -0
  24. devflow_engine/docs/prompts/examples/devin-vapi-clone-reference-exchange.json +85 -0
  25. devflow_engine/doctor/__init__.py +2 -0
  26. devflow_engine/doctor/triage.py +140 -0
  27. devflow_engine/error/__init__.py +0 -0
  28. devflow_engine/error/remediation.py +21 -0
  29. devflow_engine/errors/error_solver_dag.py +522 -0
  30. devflow_engine/errors/runtime_observability.py +67 -0
  31. devflow_engine/idea/__init__.py +4 -0
  32. devflow_engine/idea/actors.py +481 -0
  33. devflow_engine/idea/agentic.py +465 -0
  34. devflow_engine/idea/analyze.py +93 -0
  35. devflow_engine/idea/devin_chat_dag.py +1 -0
  36. devflow_engine/idea/diff.py +99 -0
  37. devflow_engine/idea/drafts.py +446 -0
  38. devflow_engine/idea/idea_creation_dag.py +643 -0
  39. devflow_engine/idea/ideation_enrichment.py +355 -0
  40. devflow_engine/idea/ideation_enrichment_worker.py +19 -0
  41. devflow_engine/idea/paths.py +28 -0
  42. devflow_engine/idea/promote.py +53 -0
  43. devflow_engine/idea/redaction.py +27 -0
  44. devflow_engine/idea/repo_tools.py +1277 -0
  45. devflow_engine/idea/response_mode.py +30 -0
  46. devflow_engine/idea/story_pipeline.py +1585 -0
  47. devflow_engine/idea/sufficiency.py +376 -0
  48. devflow_engine/idea/traditional_stories.py +1257 -0
  49. devflow_engine/implementation/__init__.py +0 -0
  50. devflow_engine/implementation/alembic_preflight.py +700 -0
  51. devflow_engine/implementation/dag.py +8450 -0
  52. devflow_engine/implementation/green_gate.py +93 -0
  53. devflow_engine/implementation/prompts.py +108 -0
  54. devflow_engine/implementation/test_runtime.py +623 -0
  55. devflow_engine/integration/__init__.py +19 -0
  56. devflow_engine/integration/agentic.py +66 -0
  57. devflow_engine/integration/dag.py +3539 -0
  58. devflow_engine/integration/prompts.py +114 -0
  59. devflow_engine/integration/supabase_schema.sql +31 -0
  60. devflow_engine/integration/supabase_sync.py +177 -0
  61. devflow_engine/llm/__init__.py +1 -0
  62. devflow_engine/llm/cli_one_shot.py +84 -0
  63. devflow_engine/llm/cli_stream.py +371 -0
  64. devflow_engine/llm/execution_context.py +26 -0
  65. devflow_engine/llm/invoke.py +1322 -0
  66. devflow_engine/llm/provider_api.py +304 -0
  67. devflow_engine/llm/repo_knowledge.py +588 -0
  68. devflow_engine/llm_primitives.py +315 -0
  69. devflow_engine/orchestration.py +62 -0
  70. devflow_engine/planning/__init__.py +0 -0
  71. devflow_engine/planning/analyze_repo.py +92 -0
  72. devflow_engine/planning/render_drafts.py +133 -0
  73. devflow_engine/playground/__init__.py +0 -0
  74. devflow_engine/playground/hooks.py +26 -0
  75. devflow_engine/playwright_workflow/__init__.py +5 -0
  76. devflow_engine/playwright_workflow/dag.py +1317 -0
  77. devflow_engine/process/__init__.py +5 -0
  78. devflow_engine/process/dag.py +59 -0
  79. devflow_engine/project_registration/__init__.py +3 -0
  80. devflow_engine/project_registration/dag.py +1581 -0
  81. devflow_engine/project_registry.py +109 -0
  82. devflow_engine/prompts/devin/generic/prompt.md +6 -0
  83. devflow_engine/prompts/devin/ideation/prompt.md +263 -0
  84. devflow_engine/prompts/devin/ideation/scenarios.md +5 -0
  85. devflow_engine/prompts/devin/ideation_loop/prompt.md +6 -0
  86. devflow_engine/prompts/devin/insight/prompt.md +11 -0
  87. devflow_engine/prompts/devin/insight/scenarios.md +5 -0
  88. devflow_engine/prompts/devin/intake/prompt.md +15 -0
  89. devflow_engine/prompts/devin/iterate/prompt.md +12 -0
  90. devflow_engine/prompts/devin/shared/eval_doctrine.md +9 -0
  91. devflow_engine/prompts/devin/shared/principles.md +246 -0
  92. devflow_engine/prompts/devin_eval/assessment/prompt.md +18 -0
  93. devflow_engine/prompts/idea/api_ideation_agent/prompt.md +8 -0
  94. devflow_engine/prompts/idea/api_insight_agent/prompt.md +8 -0
  95. devflow_engine/prompts/idea/response_doctrine/prompt.md +18 -0
  96. devflow_engine/prompts/implementation/dependency_assessment/prompt.md +12 -0
  97. devflow_engine/prompts/implementation/green/green/prompt.md +11 -0
  98. devflow_engine/prompts/implementation/green/node_config/prompt.md +3 -0
  99. devflow_engine/prompts/implementation/green_review/outcome_review/prompt.md +5 -0
  100. devflow_engine/prompts/implementation/green_review/prior_run_review/prompt.md +5 -0
  101. devflow_engine/prompts/implementation/red/prompt.md +27 -0
  102. devflow_engine/prompts/implementation/redreview/prompt.md +23 -0
  103. devflow_engine/prompts/implementation/redreview_repair/prompt.md +16 -0
  104. devflow_engine/prompts/implementation/setupdoc/prompt.md +10 -0
  105. devflow_engine/prompts/implementation/story_planning/prompt.md +13 -0
  106. devflow_engine/prompts/implementation/test_design/prompt.md +27 -0
  107. devflow_engine/prompts/integration/README.md +185 -0
  108. devflow_engine/prompts/integration/green/example.md +67 -0
  109. devflow_engine/prompts/integration/green/green/prompt.md +10 -0
  110. devflow_engine/prompts/integration/green/node_config/prompt.md +42 -0
  111. devflow_engine/prompts/integration/green/past_prompts/20260417T212300/green/prompt.md +15 -0
  112. devflow_engine/prompts/integration/green/past_prompts/20260417T212300/node_config/prompt.md +42 -0
  113. devflow_engine/prompts/integration/green_enrich/example.md +79 -0
  114. devflow_engine/prompts/integration/green_enrich/green_enrich/prompt.md +9 -0
  115. devflow_engine/prompts/integration/green_enrich/node_config/prompt.md +41 -0
  116. devflow_engine/prompts/integration/green_enrich/past_prompts/20260417T212300/green_enrich/prompt.md +14 -0
  117. devflow_engine/prompts/integration/green_enrich/past_prompts/20260417T212300/node_config/prompt.md +41 -0
  118. devflow_engine/prompts/integration/red/code_repair/prompt.md +12 -0
  119. devflow_engine/prompts/integration/red/example.md +152 -0
  120. devflow_engine/prompts/integration/red/node_config/prompt.md +86 -0
  121. devflow_engine/prompts/integration/red/past_prompts/20260417T212300/code_repair/prompt.md +19 -0
  122. devflow_engine/prompts/integration/red/past_prompts/20260417T212300/node_config/prompt.md +84 -0
  123. devflow_engine/prompts/integration/red/past_prompts/20260417T212300/red/prompt.md +16 -0
  124. devflow_engine/prompts/integration/red/past_prompts/20260417T212300/red_repair/prompt.md +15 -0
  125. devflow_engine/prompts/integration/red/past_prompts/20260417T215032/code_repair/prompt.md +10 -0
  126. devflow_engine/prompts/integration/red/past_prompts/20260417T215032/node_config/prompt.md +84 -0
  127. devflow_engine/prompts/integration/red/past_prompts/20260417T215032/red_repair/prompt.md +11 -0
  128. devflow_engine/prompts/integration/red/red/prompt.md +11 -0
  129. devflow_engine/prompts/integration/red/red_repair/prompt.md +12 -0
  130. devflow_engine/prompts/integration/red_review/example.md +71 -0
  131. devflow_engine/prompts/integration/red_review/node_config/prompt.md +41 -0
  132. devflow_engine/prompts/integration/red_review/past_prompts/20260417T212300/node_config/prompt.md +41 -0
  133. devflow_engine/prompts/integration/red_review/past_prompts/20260417T212300/red_review/prompt.md +15 -0
  134. devflow_engine/prompts/integration/red_review/red_review/prompt.md +9 -0
  135. devflow_engine/prompts/integration/resolve/example.md +111 -0
  136. devflow_engine/prompts/integration/resolve/node_config/prompt.md +64 -0
  137. devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/node_config/prompt.md +64 -0
  138. devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/resolve_implicated_users/prompt.md +15 -0
  139. devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/resolve_side_effects/prompt.md +15 -0
  140. devflow_engine/prompts/integration/resolve/resolve_implicated_users/prompt.md +10 -0
  141. devflow_engine/prompts/integration/resolve/resolve_side_effects/prompt.md +10 -0
  142. devflow_engine/prompts/integration/validate/build_idea_acceptance_coverage/prompt.md +12 -0
  143. devflow_engine/prompts/integration/validate/code_repair/prompt.md +13 -0
  144. devflow_engine/prompts/integration/validate/example.md +143 -0
  145. devflow_engine/prompts/integration/validate/node_config/prompt.md +87 -0
  146. devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/code_repair/prompt.md +19 -0
  147. devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/node_config/prompt.md +67 -0
  148. devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/validate_enrich_gate/prompt.md +17 -0
  149. devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/validate_repair/prompt.md +16 -0
  150. devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/code_repair/prompt.md +10 -0
  151. devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/node_config/prompt.md +67 -0
  152. devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/validate_repair/prompt.md +9 -0
  153. devflow_engine/prompts/integration/validate/validate_enrich_gate/prompt.md +10 -0
  154. devflow_engine/prompts/integration/validate/validate_repair/prompt.md +20 -0
  155. devflow_engine/prompts/integration/write_workflows/example.md +100 -0
  156. devflow_engine/prompts/integration/write_workflows/node_config/prompt.md +44 -0
  157. devflow_engine/prompts/integration/write_workflows/past_prompts/20260417T212300/node_config/prompt.md +44 -0
  158. devflow_engine/prompts/integration/write_workflows/past_prompts/20260417T212300/write_workflows/prompt.md +17 -0
  159. devflow_engine/prompts/integration/write_workflows/write_workflows/prompt.md +11 -0
  160. devflow_engine/prompts/iterate/README.md +7 -0
  161. devflow_engine/prompts/iterate/coder/prompt.md +11 -0
  162. devflow_engine/prompts/iterate/framer/prompt.md +11 -0
  163. devflow_engine/prompts/iterate/iterator/prompt.md +13 -0
  164. devflow_engine/prompts/iterate/observer/prompt.md +11 -0
  165. devflow_engine/prompts/recovery/diagnosis/prompt.md +7 -0
  166. devflow_engine/prompts/recovery/execution/prompt.md +8 -0
  167. devflow_engine/prompts/recovery/execution_verification/prompt.md +7 -0
  168. devflow_engine/prompts/recovery/failure_investigation/prompt.md +10 -0
  169. devflow_engine/prompts/recovery/preflight_health_repo_repair/prompt.md +8 -0
  170. devflow_engine/prompts/recovery/remediation_execution/prompt.md +11 -0
  171. devflow_engine/prompts/recovery/root_cause_investigation/prompt.md +12 -0
  172. devflow_engine/prompts/scope_idea/doctrine/prompt.md +7 -0
  173. devflow_engine/prompts/source_doc_eval/document/prompt.md +6 -0
  174. devflow_engine/prompts/source_doc_eval/targeted_mutation/prompt.md +9 -0
  175. devflow_engine/prompts/source_doc_mutation/domain_entities/prompt.md +6 -0
  176. devflow_engine/prompts/source_doc_mutation/product_brief/prompt.md +6 -0
  177. devflow_engine/prompts/source_doc_mutation/project_doc_coherence/prompt.md +7 -0
  178. devflow_engine/prompts/source_doc_mutation/project_doc_render/prompt.md +9 -0
  179. devflow_engine/prompts/source_doc_mutation/source_doc_coherence/prompt.md +5 -0
  180. devflow_engine/prompts/source_doc_mutation/source_doc_enrichment_coherence/prompt.md +6 -0
  181. devflow_engine/prompts/source_doc_mutation/user_workflows/prompt.md +6 -0
  182. devflow_engine/prompts/source_scope/doctrine/prompt.md +10 -0
  183. devflow_engine/prompts/ui_grounding/doctrine/prompt.md +7 -0
  184. devflow_engine/recovery/__init__.py +3 -0
  185. devflow_engine/recovery/dag.py +2609 -0
  186. devflow_engine/recovery/models.py +220 -0
  187. devflow_engine/refactor.py +93 -0
  188. devflow_engine/registry/__init__.py +1 -0
  189. devflow_engine/registry/cards.py +238 -0
  190. devflow_engine/registry/domain_normalize.py +60 -0
  191. devflow_engine/registry/effects.py +65 -0
  192. devflow_engine/registry/enforce_report.py +150 -0
  193. devflow_engine/registry/module_cards_classify.py +164 -0
  194. devflow_engine/registry/module_cards_draft.py +184 -0
  195. devflow_engine/registry/module_cards_gate.py +59 -0
  196. devflow_engine/registry/packages.py +347 -0
  197. devflow_engine/registry/pathways.py +323 -0
  198. devflow_engine/review/__init__.py +11 -0
  199. devflow_engine/review/dag.py +588 -0
  200. devflow_engine/review/review_story.py +67 -0
  201. devflow_engine/scope_idea/__init__.py +3 -0
  202. devflow_engine/scope_idea/agentic.py +39 -0
  203. devflow_engine/scope_idea/dag.py +1069 -0
  204. devflow_engine/scope_idea/models.py +175 -0
  205. devflow_engine/skills/builtins/devflow/queue_failure_investigation/SKILL.md +112 -0
  206. devflow_engine/skills/builtins/devflow/queue_idea_to_story/SKILL.md +120 -0
  207. devflow_engine/skills/builtins/devflow/queue_integration/SKILL.md +105 -0
  208. devflow_engine/skills/builtins/devflow/queue_recovery/SKILL.md +108 -0
  209. devflow_engine/skills/builtins/devflow/queue_runtime_core/SKILL.md +155 -0
  210. devflow_engine/skills/builtins/devflow/queue_story_implementation/SKILL.md +122 -0
  211. devflow_engine/skills/builtins/devin/idea_to_story_handoff/SKILL.md +120 -0
  212. devflow_engine/skills/builtins/devin/ideation/SKILL.md +168 -0
  213. devflow_engine/skills/builtins/devin/ideation/state-and-phrasing-reference.md +18 -0
  214. devflow_engine/skills/builtins/devin/insight/SKILL.md +22 -0
  215. devflow_engine/skills/registry.example.yaml +42 -0
  216. devflow_engine/source_doc_assumptions.py +291 -0
  217. devflow_engine/source_doc_mutation_dag.py +1606 -0
  218. devflow_engine/source_doc_mutation_eval.py +417 -0
  219. devflow_engine/source_doc_mutation_worker.py +25 -0
  220. devflow_engine/source_docs_schema.py +207 -0
  221. devflow_engine/source_docs_updater.py +309 -0
  222. devflow_engine/source_scope/__init__.py +15 -0
  223. devflow_engine/source_scope/agentic.py +45 -0
  224. devflow_engine/source_scope/dag.py +1626 -0
  225. devflow_engine/source_scope/models.py +177 -0
  226. devflow_engine/stores/__init__.py +0 -0
  227. devflow_engine/stores/execution_store.py +3534 -0
  228. devflow_engine/story/__init__.py +0 -0
  229. devflow_engine/story/contracts.py +160 -0
  230. devflow_engine/story/discovery.py +47 -0
  231. devflow_engine/story/evidence.py +118 -0
  232. devflow_engine/story/hashing.py +27 -0
  233. devflow_engine/story/implemented_queue_purge.py +148 -0
  234. devflow_engine/story/indexer.py +105 -0
  235. devflow_engine/story/io.py +20 -0
  236. devflow_engine/story/markdown_contracts.py +298 -0
  237. devflow_engine/story/reconciliation.py +408 -0
  238. devflow_engine/story/validate_stories.py +149 -0
  239. devflow_engine/story/validate_tests_story.py +512 -0
  240. devflow_engine/story/validation.py +133 -0
  241. devflow_engine/ui_grounding/__init__.py +11 -0
  242. devflow_engine/ui_grounding/agentic.py +31 -0
  243. devflow_engine/ui_grounding/dag.py +874 -0
  244. devflow_engine/ui_grounding/models.py +224 -0
  245. devflow_engine/ui_grounding/pencil_bridge.py +247 -0
  246. devflow_engine/vendor/__init__.py +0 -0
  247. devflow_engine/vendor/datalumina_genai/__init__.py +11 -0
  248. devflow_engine/vendor/datalumina_genai/core/__init__.py +0 -0
  249. devflow_engine/vendor/datalumina_genai/core/exceptions.py +9 -0
  250. devflow_engine/vendor/datalumina_genai/core/nodes/__init__.py +0 -0
  251. devflow_engine/vendor/datalumina_genai/core/nodes/agent.py +48 -0
  252. devflow_engine/vendor/datalumina_genai/core/nodes/agent_streaming_node.py +26 -0
  253. devflow_engine/vendor/datalumina_genai/core/nodes/base.py +89 -0
  254. devflow_engine/vendor/datalumina_genai/core/nodes/concurrent.py +30 -0
  255. devflow_engine/vendor/datalumina_genai/core/nodes/router.py +69 -0
  256. devflow_engine/vendor/datalumina_genai/core/schema.py +72 -0
  257. devflow_engine/vendor/datalumina_genai/core/task.py +52 -0
  258. devflow_engine/vendor/datalumina_genai/core/validate.py +139 -0
  259. devflow_engine/vendor/datalumina_genai/core/workflow.py +200 -0
  260. devflow_engine/worker.py +1086 -0
  261. devflow_engine/worker_guard.py +233 -0
  262. devflow_engine-1.0.0.dist-info/METADATA +235 -0
  263. devflow_engine-1.0.0.dist-info/RECORD +393 -0
  264. devflow_engine-1.0.0.dist-info/WHEEL +4 -0
  265. devflow_engine-1.0.0.dist-info/entry_points.txt +3 -0
  266. devin/__init__.py +6 -0
  267. devin/dag.py +58 -0
  268. devin/dag_two_arm.py +138 -0
  269. devin/devin_chat_scenario_catalog.json +588 -0
  270. devin/devin_eval.py +677 -0
  271. devin/nodes/__init__.py +0 -0
  272. devin/nodes/ideation/__init__.py +0 -0
  273. devin/nodes/ideation/node.py +195 -0
  274. devin/nodes/ideation/playground.py +267 -0
  275. devin/nodes/ideation/prompt.md +65 -0
  276. devin/nodes/ideation/scenarios/continue_refinement.py +13 -0
  277. devin/nodes/ideation/scenarios/continue_refinement_evals.py +18 -0
  278. devin/nodes/ideation/scenarios/idea_fits_existing_patterns.py +17 -0
  279. devin/nodes/ideation/scenarios/idea_fits_existing_patterns_evals.py +16 -0
  280. devin/nodes/ideation/scenarios/large_idea_split.py +4 -0
  281. devin/nodes/ideation/scenarios/large_idea_split_evals.py +17 -0
  282. devin/nodes/ideation/scenarios/source_documentation_added.py +4 -0
  283. devin/nodes/ideation/scenarios/source_documentation_added_evals.py +16 -0
  284. devin/nodes/ideation/scenarios/user_says_create_it.py +30 -0
  285. devin/nodes/ideation/scenarios/user_says_create_it_evals.py +23 -0
  286. devin/nodes/ideation/scenarios/vague_idea.py +16 -0
  287. devin/nodes/ideation/scenarios/vague_idea_evals.py +47 -0
  288. devin/nodes/ideation/tools.json +312 -0
  289. devin/nodes/insight/__init__.py +0 -0
  290. devin/nodes/insight/node.py +49 -0
  291. devin/nodes/insight/playground.py +154 -0
  292. devin/nodes/insight/prompt.md +61 -0
  293. devin/nodes/insight/scenarios/architecture_pattern_query.py +15 -0
  294. devin/nodes/insight/scenarios/architecture_pattern_query_evals.py +25 -0
  295. devin/nodes/insight/scenarios/codebase_exploration.py +15 -0
  296. devin/nodes/insight/scenarios/codebase_exploration_evals.py +23 -0
  297. devin/nodes/insight/scenarios/devin_ideation_routing.py +19 -0
  298. devin/nodes/insight/scenarios/devin_ideation_routing_evals.py +39 -0
  299. devin/nodes/insight/scenarios/devin_insight_routing.py +20 -0
  300. devin/nodes/insight/scenarios/devin_insight_routing_evals.py +40 -0
  301. devin/nodes/insight/scenarios/operational_debugging.py +15 -0
  302. devin/nodes/insight/scenarios/operational_debugging_evals.py +23 -0
  303. devin/nodes/insight/scenarios/operational_question.py +9 -0
  304. devin/nodes/insight/scenarios/operational_question_evals.py +8 -0
  305. devin/nodes/insight/scenarios/queue_status.py +15 -0
  306. devin/nodes/insight/scenarios/queue_status_evals.py +23 -0
  307. devin/nodes/insight/scenarios/source_doc_explanation.py +14 -0
  308. devin/nodes/insight/scenarios/source_doc_explanation_evals.py +21 -0
  309. devin/nodes/insight/scenarios/worker_state_check.py +15 -0
  310. devin/nodes/insight/scenarios/worker_state_check_evals.py +22 -0
  311. devin/nodes/insight/tools.json +126 -0
  312. devin/nodes/intake/__init__.py +0 -0
  313. devin/nodes/intake/node.py +27 -0
  314. devin/nodes/intake/playground.py +47 -0
  315. devin/nodes/intake/prompt.md +12 -0
  316. devin/nodes/intake/scenarios/ideation_routing.py +4 -0
  317. devin/nodes/intake/scenarios/ideation_routing_evals.py +5 -0
  318. devin/nodes/intake/scenarios/insight_routing.py +4 -0
  319. devin/nodes/intake/scenarios/insight_routing_evals.py +5 -0
  320. devin/nodes/iterate/README.md +44 -0
  321. devin/nodes/iterate/__init__.py +1 -0
  322. devin/nodes/iterate/_archived_design_stages/01-objectives-requirements.md +112 -0
  323. devin/nodes/iterate/_archived_design_stages/02-evals.md +131 -0
  324. devin/nodes/iterate/_archived_design_stages/03-tools-and-boundaries.md +110 -0
  325. devin/nodes/iterate/_archived_design_stages/04-harness-and-playground.md +32 -0
  326. devin/nodes/iterate/_archived_design_stages/05-prompt-deferred.md +11 -0
  327. devin/nodes/iterate/_archived_design_stages/coder_agent_design/01-objectives-requirements.md +20 -0
  328. devin/nodes/iterate/_archived_design_stages/coder_agent_design/02-evals.md +8 -0
  329. devin/nodes/iterate/_archived_design_stages/coder_agent_design/03-tools-and-boundaries.md +14 -0
  330. devin/nodes/iterate/_archived_design_stages/coder_agent_design/04-harness-and-playground.md +12 -0
  331. devin/nodes/iterate/_archived_design_stages/framer_agent_design/01-objectives-requirements.md +20 -0
  332. devin/nodes/iterate/_archived_design_stages/framer_agent_design/02-evals.md +8 -0
  333. devin/nodes/iterate/_archived_design_stages/framer_agent_design/03-tools-and-boundaries.md +13 -0
  334. devin/nodes/iterate/_archived_design_stages/framer_agent_design/04-harness-and-playground.md +12 -0
  335. devin/nodes/iterate/_archived_design_stages/iterator_agent_design/01-objectives-requirements.md +25 -0
  336. devin/nodes/iterate/_archived_design_stages/iterator_agent_design/02-evals.md +9 -0
  337. devin/nodes/iterate/_archived_design_stages/iterator_agent_design/03-tools-and-boundaries.md +14 -0
  338. devin/nodes/iterate/_archived_design_stages/iterator_agent_design/04-harness-and-playground.md +12 -0
  339. devin/nodes/iterate/_archived_design_stages/observer_agent_design/01-objectives-requirements.md +20 -0
  340. devin/nodes/iterate/_archived_design_stages/observer_agent_design/02-evals.md +8 -0
  341. devin/nodes/iterate/_archived_design_stages/observer_agent_design/03-tools-and-boundaries.md +14 -0
  342. devin/nodes/iterate/_archived_design_stages/observer_agent_design/04-harness-and-playground.md +13 -0
  343. devin/nodes/iterate/agent-roles.md +89 -0
  344. devin/nodes/iterate/agents/README.md +10 -0
  345. devin/nodes/iterate/artifacts.md +504 -0
  346. devin/nodes/iterate/contract.md +100 -0
  347. devin/nodes/iterate/eval-plan.md +74 -0
  348. devin/nodes/iterate/node.py +100 -0
  349. devin/nodes/iterate/pipeline/README.md +13 -0
  350. devin/nodes/iterate/playground-contract.md +76 -0
  351. devin/nodes/iterate/prompt.md +11 -0
  352. devin/nodes/iterate/scenarios/README.md +38 -0
  353. devin/nodes/iterate/scenarios/artifact-and-loop-scenarios.md +101 -0
  354. devin/nodes/iterate/scenarios/coder_artifact_alignment.py +32 -0
  355. devin/nodes/iterate/scenarios/coder_artifact_alignment_evals.py +45 -0
  356. devin/nodes/iterate/scenarios/coder_bounded_fix.py +27 -0
  357. devin/nodes/iterate/scenarios/coder_bounded_fix_evals.py +45 -0
  358. devin/nodes/iterate/scenarios/devin_iterate_routing.py +21 -0
  359. devin/nodes/iterate/scenarios/devin_iterate_routing_evals.py +36 -0
  360. devin/nodes/iterate/scenarios/framer_scope_boundary.py +25 -0
  361. devin/nodes/iterate/scenarios/framer_scope_boundary_evals.py +57 -0
  362. devin/nodes/iterate/scenarios/framer_task_framing.py +25 -0
  363. devin/nodes/iterate/scenarios/framer_task_framing_evals.py +58 -0
  364. devin/nodes/iterate/scenarios/iterate_error_fix.py +21 -0
  365. devin/nodes/iterate/scenarios/iterate_error_fix_evals.py +39 -0
  366. devin/nodes/iterate/scenarios/iterate_quick_change.py +21 -0
  367. devin/nodes/iterate/scenarios/iterate_quick_change_evals.py +35 -0
  368. devin/nodes/iterate/scenarios/iterate_to_idea_promotion.py +23 -0
  369. devin/nodes/iterate/scenarios/iterate_to_idea_promotion_evals.py +53 -0
  370. devin/nodes/iterate/scenarios/iterate_to_insight_reroute.py +23 -0
  371. devin/nodes/iterate/scenarios/iterate_to_insight_reroute_evals.py +53 -0
  372. devin/nodes/iterate/scenarios/observer_evidence_seam.py +28 -0
  373. devin/nodes/iterate/scenarios/observer_evidence_seam_evals.py +55 -0
  374. devin/nodes/iterate/scenarios/observer_repro_creation.py +28 -0
  375. devin/nodes/iterate/scenarios/observer_repro_creation_evals.py +45 -0
  376. devin/nodes/iterate/scenarios/routing-matrix.md +45 -0
  377. devin/nodes/shared/__init__.py +0 -0
  378. devin/nodes/shared/filemaker_expert.md +80 -0
  379. devin/nodes/shared/filemaker_expert.py +354 -0
  380. devin/nodes/shared/filemaker_expert_eval/runner.py +176 -0
  381. devin/nodes/shared/filemaker_expert_eval/scenarios.json +65 -0
  382. devin/nodes/shared/goldilocks_advisor_eval/runner.py +214 -0
  383. devin/nodes/shared/goldilocks_advisor_eval/scenarios.json +58 -0
  384. devin/nodes/shared/helpers.py +156 -0
  385. devin/nodes/shared/idea_compliance_advisor_eval/runner.py +252 -0
  386. devin/nodes/shared/idea_compliance_advisor_eval/scenarios.json +75 -0
  387. devin/nodes/shared/models.py +44 -0
  388. devin/nodes/shared/post.py +40 -0
  389. devin/nodes/shared/router.py +107 -0
  390. devin/nodes/shared/tools.py +191 -0
  391. devin/shared/devin-chat-rubric.md +237 -0
  392. devin/shared/devin-chat-scenario-suite.md +90 -0
  393. 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)