agentic-dev-loop 1.0.0b1__tar.gz → 1.0.0rc3__tar.gz

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 (222) hide show
  1. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/PKG-INFO +5 -4
  2. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/README.md +3 -3
  3. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/pyproject.toml +6 -1
  4. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/core/config.py +1 -1
  5. agentic_dev_loop-1.0.0rc3/src/agentic_dev_loop/engine/claude_auth.py +301 -0
  6. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/cli.py +31 -2
  7. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/hostboundary.py +2 -1
  8. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/llm.py +20 -5
  9. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/onboarding/upgrade.py +5 -1
  10. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/provider_wire.py +41 -4
  11. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/run_status_command.md +1 -1
  12. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/secrets.py +5 -3
  13. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop.egg-info/PKG-INFO +5 -4
  14. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop.egg-info/SOURCES.txt +4 -0
  15. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop.egg-info/requires.txt +1 -0
  16. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_055_upgrade.py +19 -1
  17. agentic_dev_loop-1.0.0rc3/tests/test_claude_auth.py +337 -0
  18. agentic_dev_loop-1.0.0rc3/tests/test_claude_cli.py +43 -0
  19. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_config.py +9 -0
  20. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_provider_wire.py +98 -3
  21. agentic_dev_loop-1.0.0rc3/tests/test_pypi_yank.py +339 -0
  22. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_secrets.py +4 -0
  23. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_viz_wheel_contents.py +10 -3
  24. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/LICENSE +0 -0
  25. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/setup.cfg +0 -0
  26. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/__init__.py +0 -0
  27. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/core/__init__.py +0 -0
  28. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/core/failure.py +0 -0
  29. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/core/paths.py +0 -0
  30. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/core/profiles/__init__.py +0 -0
  31. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/core/profiles/data/dotnet-node.yaml +0 -0
  32. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/core/profiles/data/python.yaml +0 -0
  33. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/core/results.py +0 -0
  34. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/core/verdict.py +0 -0
  35. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/__init__.py +0 -0
  36. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/actions.py +0 -0
  37. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/agent_error_guide.md +0 -0
  38. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/agent_error_guide.py +0 -0
  39. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/agents.py +0 -0
  40. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/allowlist.py +0 -0
  41. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/brief_coverage.py +0 -0
  42. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/brief_prompt.md +0 -0
  43. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/brief_prompt.py +0 -0
  44. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/completion.py +0 -0
  45. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/containers.py +0 -0
  46. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/copilot_auth.py +0 -0
  47. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/deps.py +0 -0
  48. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/dryrun.py +0 -0
  49. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/egress_policy.py +0 -0
  50. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/egress_proxy.py +0 -0
  51. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/extensions/__init__.py +0 -0
  52. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/extensions/aidlc.py +0 -0
  53. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/forbidden.py +0 -0
  54. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/graph.py +0 -0
  55. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/isolation.py +0 -0
  56. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/loaders.py +0 -0
  57. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/mounts.py +0 -0
  58. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/onboarding/__init__.py +0 -0
  59. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/onboarding/checks.py +0 -0
  60. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/onboarding/doctor.py +0 -0
  61. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/onboarding/init.py +0 -0
  62. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/onboarding/recipes/dotnet-node.yaml +0 -0
  63. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/onboarding/recipes/python.yaml +0 -0
  64. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/onboarding/recipes.py +0 -0
  65. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/onboarding/templates/dev.agent.md +0 -0
  66. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/onboarding/templates/env.example +0 -0
  67. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/onboarding/templates/qa.agent.md +0 -0
  68. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/onboarding/templates/review.agent.md +0 -0
  69. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/onboarding/templates.py +0 -0
  70. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/outputs.py +0 -0
  71. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/presets.py +0 -0
  72. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/proc.py +0 -0
  73. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/registry.py +0 -0
  74. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/run_status.py +0 -0
  75. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/seccomp/__init__.py +0 -0
  76. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/seccomp/chromium-userns.json +0 -0
  77. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/stages.py +0 -0
  78. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/step_workspace.py +0 -0
  79. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/engine/sync_back.py +0 -0
  80. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/py.typed +0 -0
  81. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/reporting/__init__.py +0 -0
  82. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/reporting/metrics.py +0 -0
  83. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/reporting/render.py +0 -0
  84. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/reporting/sources.py +0 -0
  85. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/__init__.py +0 -0
  86. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/layout.py +0 -0
  87. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/page.py +0 -0
  88. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/runs_view.py +0 -0
  89. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/server.py +0 -0
  90. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/16-1pKPtbcx.js +0 -0
  91. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/16-2lMgbefg.js +0 -0
  92. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/16-B0WgRfVg.js +0 -0
  93. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/16-B3fCnGwv.js +0 -0
  94. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/16-Bm3KyEZC.js +0 -0
  95. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/16-DIfh6y3Q.js +0 -0
  96. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/16-DxEg5YJZ.js +0 -0
  97. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/20-CdViZ4xF.js +0 -0
  98. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/accordion.min.js +0 -0
  99. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/button-rSg_K8yf.js +0 -0
  100. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/button-skeleton-i-vqINhS.js +0 -0
  101. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/button.min.js +0 -0
  102. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/carbon-element-Bi7pQHAl.js +0 -0
  103. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/checkbox-D7PSLQCq.js +0 -0
  104. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/checkbox.min.js +0 -0
  105. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/class-map-t9xXbTFr.js +0 -0
  106. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/code-snippet.min.js +0 -0
  107. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/collection-helpers-BOkEorAG.js +0 -0
  108. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/copy-button.min.js +0 -0
  109. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/copy-tonlUGaV.js +0 -0
  110. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/data-table.min.js +0 -0
  111. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/decorate-1Ivfrp93.js +0 -0
  112. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/decorators-CNcdZSLL.js +0 -0
  113. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/deep-shadow-contains-Cs2rSnhr.js +0 -0
  114. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/definition-tooltip-FYe4J71p.js +0 -0
  115. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/directive-xDpGnViN.js +0 -0
  116. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/feature-flags-C7ZDYm5Q.js +0 -0
  117. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/floating-controller-DmTRVV0A.js +0 -0
  118. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/focus-D91D_iCB.js +0 -0
  119. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/form-Sg67yyAA.js +0 -0
  120. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/host-listener-CEd01uiB.js +0 -0
  121. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/host-listener-D_ODAJ3e.js +0 -0
  122. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/icon-button-Be2x1qrf.js +0 -0
  123. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/icon-loader-Cj1PggRN.js +0 -0
  124. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/icon-loader-utils-CVerE28u.js +0 -0
  125. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/if-defined-CV6i5LeB.js +0 -0
  126. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/if-non-empty-B5mz8bH9.js +0 -0
  127. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/link-aD3OYHCu.js +0 -0
  128. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/link.min.js +0 -0
  129. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/manifest.json +0 -0
  130. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/notification.min.js +0 -0
  131. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/on-DP_Dnlgd.js +0 -0
  132. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/popover-content-fajgXUvu.js +0 -0
  133. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/popover.min.js +0 -0
  134. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/query-DJR3NpB4.js +0 -0
  135. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/query-all-BuL-eXl_.js +0 -0
  136. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/radio-button.min.js +0 -0
  137. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/radio-group-manager-BE85t4HN.js +0 -0
  138. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/search-CVX2VAop.js +0 -0
  139. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/settings-DlHDxgf_.js +0 -0
  140. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/skeleton-text-s5gA9_CO.js +0 -0
  141. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/skeleton-text.min.js +0 -0
  142. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/tag.min.js +0 -0
  143. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/themes.css +0 -0
  144. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/tile.min.js +0 -0
  145. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/tooltip-DYtXwn-o.js +0 -0
  146. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/tooltip-content-DDo9ICPL.js +0 -0
  147. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/tooltip.min.js +0 -0
  148. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/ui-shell.min.js +0 -0
  149. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop/viz/vendor/unsafe-html-BDr7jYB8.js +0 -0
  150. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
  151. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
  152. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
  153. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_029_merge_on_conflict.py +0 -0
  154. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_033_brief_step.py +0 -0
  155. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_035_finalize_nudge.py +0 -0
  156. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_036_banned_commands_and_lane_boundary.py +0 -0
  157. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_041_brief_coverage.py +0 -0
  158. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_046_brief_prompt.py +0 -0
  159. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_055_cli.py +0 -0
  160. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_055_doctor.py +0 -0
  161. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_055_init.py +0 -0
  162. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_055_profile_loading.py +0 -0
  163. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_055_profiles.py +0 -0
  164. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_055_recipes.py +0 -0
  165. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_055_schema_lock.py +0 -0
  166. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_055_templates.py +0 -0
  167. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_actions.py +0 -0
  168. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_agent_error_guide.py +0 -0
  169. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_agents.py +0 -0
  170. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_aidlc.py +0 -0
  171. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_build_viz_page.py +0 -0
  172. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_cli.py +0 -0
  173. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_containers.py +0 -0
  174. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_copilot_auth.py +0 -0
  175. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_copilot_cli.py +0 -0
  176. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_deps.py +0 -0
  177. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_dry_run.py +0 -0
  178. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_e2e_fixtures.py +0 -0
  179. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_egress_proxy.py +0 -0
  180. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_graph.py +0 -0
  181. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_hostboundary.py +0 -0
  182. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_hostboundary_integration.py +0 -0
  183. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_isolation.py +0 -0
  184. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_isolation_integration.py +0 -0
  185. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_isolation_pbt.py +0 -0
  186. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_llm.py +0 -0
  187. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_loaders.py +0 -0
  188. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_module_contract.py +0 -0
  189. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_outputs.py +0 -0
  190. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_outputs_pbt.py +0 -0
  191. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_proc.py +0 -0
  192. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_publish.py +0 -0
  193. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_qa_prompt.py +0 -0
  194. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_registry.py +0 -0
  195. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_repo_ascii.py +0 -0
  196. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_results.py +0 -0
  197. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_run_status.py +0 -0
  198. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_runs_js_duration_charts.py +0 -0
  199. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_runs_js_filters.py +0 -0
  200. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_runs_js_key_stats.py +0 -0
  201. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_runs_js_metrics.py +0 -0
  202. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_runs_layout.py +0 -0
  203. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_runs_layout_key_stats.py +0 -0
  204. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_sdk_compat.py +0 -0
  205. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_secrets_integration.py +0 -0
  206. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_secrets_pipeline.py +0 -0
  207. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_source_resolution.py +0 -0
  208. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_stages.py +0 -0
  209. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_step_workspace.py +0 -0
  210. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_sync_back.py +0 -0
  211. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_unit11.py +0 -0
  212. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_unit11_pbt.py +0 -0
  213. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_unit12_reporting.py +0 -0
  214. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_unit13_runs_viz.py +0 -0
  215. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_unit5_config.py +0 -0
  216. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_unit5_pbt.py +0 -0
  217. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_unit5_pure.py +0 -0
  218. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_vendor_carbon_wc.py +0 -0
  219. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_verdict.py +0 -0
  220. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_viz.py +0 -0
  221. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_viz_demo.py +0 -0
  222. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc3}/tests/test_viz_layout.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-dev-loop
3
- Version: 1.0.0b1
3
+ Version: 1.0.0rc3
4
4
  Summary: Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.
5
5
  Author: Luke Wilson
6
6
  License-Expression: Apache-2.0
@@ -44,6 +44,7 @@ Requires-Dist: hypothesis>=6.0; extra == "dev"
44
44
  Requires-Dist: pytest>=8.0; extra == "dev"
45
45
  Requires-Dist: pip-audit>=2.0; extra == "dev"
46
46
  Requires-Dist: pre-commit>=3.0; extra == "dev"
47
+ Requires-Dist: setuptools>=78.1.1; extra == "dev"
47
48
  Provides-Extra: docs
48
49
  Requires-Dist: mkdocs-material~=9.6; extra == "docs"
49
50
  Requires-Dist: pymdown-extensions~=10.0; extra == "docs"
@@ -108,12 +109,12 @@ Docker engine does:
108
109
  | --- | --- |
109
110
  | Python | >= 3.12 |
110
111
  | Docker engine | Engine API >= 1.43 (Engine 24). Any compliant engine works - Docker Desktop, OrbStack, Colima, Rancher Desktop, podman docker-compat. On Windows, run under WSL2: the repo must live on the WSL filesystem, not /mnt/c. |
111
- | LLM API key | The env var named by your pipeline's `api_key_env` (e.g. `OPENAI_API_KEY`) must be set - a missing key fails fast, naming it. Alternatively, `agentic-dev-loop copilot-login` authenticates a Copilot subscription (see [LLM providers](https://lukedawilson.github.io/agentic-dev-loop/llm-providers/)). |
112
+ | LLM API key | The env var named by your pipeline's `api_key_env` (e.g. `OPENAI_API_KEY`) must be set - a missing key fails fast, naming it. Alternatively, `agentic-dev-loop copilot-login` authenticates a Copilot subscription, and a Claude Pro or Max subscription runs on Anthropic's own endpoint after `agentic-dev-loop claude-login` (see [LLM providers](https://lukedawilson.github.io/agentic-dev-loop/llm-providers/)). |
112
113
 
113
114
  ### Installation
114
115
 
115
116
  ```bash
116
- pipx install agentic-dev-loop
117
+ pipx install --pip-args="--pre" agentic-dev-loop # latest release is currently a pre-release
117
118
  agentic-dev-loop --version
118
119
  ```
119
120
 
@@ -181,7 +182,7 @@ policy, named LLM profiles - is in the
181
182
  | [Architecture](https://lukedawilson.github.io/agentic-dev-loop/architecture/) | The five layers, one full run end to end, and the host-boundary security model |
182
183
  | [CLI](https://lukedawilson.github.io/agentic-dev-loop/cli/) | Run flags, resuming, the live view, reports, container management, and onboarding commands |
183
184
  | [pipeline.yaml](https://lukedawilson.github.io/agentic-dev-loop/pipeline/) | The complete configuration reference: steps, isolation, secrets, networking, dependency policy |
184
- | [LLM providers](https://lukedawilson.github.io/agentic-dev-loop/llm-providers/) | OpenAI-compatible endpoints (OpenAI, DeepSeek, and others) and the GitHub Copilot subscription |
185
+ | [LLM providers](https://lukedawilson.github.io/agentic-dev-loop/llm-providers/) | OpenAI-compatible endpoints (OpenAI, DeepSeek, and others), the GitHub Copilot subscription, and the Claude subscription |
185
186
  | [Authoring a loop](https://lukedawilson.github.io/agentic-dev-loop/authoring/) | From `init` to a committed pipeline: agent files, stage lineup, adaptation checklist |
186
187
  | [Setup and troubleshooting](https://lukedawilson.github.io/agentic-dev-loop/setup/) | Docker engines, the dev image, caching, error codes |
187
188
  | [Browser sandbox](https://lukedawilson.github.io/agentic-dev-loop/browser-sandbox/) | Running a browser inside the sandbox |
@@ -57,12 +57,12 @@ Docker engine does:
57
57
  | --- | --- |
58
58
  | Python | >= 3.12 |
59
59
  | Docker engine | Engine API >= 1.43 (Engine 24). Any compliant engine works - Docker Desktop, OrbStack, Colima, Rancher Desktop, podman docker-compat. On Windows, run under WSL2: the repo must live on the WSL filesystem, not /mnt/c. |
60
- | LLM API key | The env var named by your pipeline's `api_key_env` (e.g. `OPENAI_API_KEY`) must be set - a missing key fails fast, naming it. Alternatively, `agentic-dev-loop copilot-login` authenticates a Copilot subscription (see [LLM providers](https://lukedawilson.github.io/agentic-dev-loop/llm-providers/)). |
60
+ | LLM API key | The env var named by your pipeline's `api_key_env` (e.g. `OPENAI_API_KEY`) must be set - a missing key fails fast, naming it. Alternatively, `agentic-dev-loop copilot-login` authenticates a Copilot subscription, and a Claude Pro or Max subscription runs on Anthropic's own endpoint after `agentic-dev-loop claude-login` (see [LLM providers](https://lukedawilson.github.io/agentic-dev-loop/llm-providers/)). |
61
61
 
62
62
  ### Installation
63
63
 
64
64
  ```bash
65
- pipx install agentic-dev-loop
65
+ pipx install --pip-args="--pre" agentic-dev-loop # latest release is currently a pre-release
66
66
  agentic-dev-loop --version
67
67
  ```
68
68
 
@@ -130,7 +130,7 @@ policy, named LLM profiles - is in the
130
130
  | [Architecture](https://lukedawilson.github.io/agentic-dev-loop/architecture/) | The five layers, one full run end to end, and the host-boundary security model |
131
131
  | [CLI](https://lukedawilson.github.io/agentic-dev-loop/cli/) | Run flags, resuming, the live view, reports, container management, and onboarding commands |
132
132
  | [pipeline.yaml](https://lukedawilson.github.io/agentic-dev-loop/pipeline/) | The complete configuration reference: steps, isolation, secrets, networking, dependency policy |
133
- | [LLM providers](https://lukedawilson.github.io/agentic-dev-loop/llm-providers/) | OpenAI-compatible endpoints (OpenAI, DeepSeek, and others) and the GitHub Copilot subscription |
133
+ | [LLM providers](https://lukedawilson.github.io/agentic-dev-loop/llm-providers/) | OpenAI-compatible endpoints (OpenAI, DeepSeek, and others), the GitHub Copilot subscription, and the Claude subscription |
134
134
  | [Authoring a loop](https://lukedawilson.github.io/agentic-dev-loop/authoring/) | From `init` to a committed pipeline: agent files, stage lineup, adaptation checklist |
135
135
  | [Setup and troubleshooting](https://lukedawilson.github.io/agentic-dev-loop/setup/) | Docker engines, the dev image, caching, error codes |
136
136
  | [Browser sandbox](https://lukedawilson.github.io/agentic-dev-loop/browser-sandbox/) | Running a browser inside the sandbox |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agentic-dev-loop"
7
- version = "1.0.0b1"
7
+ version = "1.0.0rc3"
8
8
  description = "Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -84,6 +84,11 @@ dev = [
84
84
  "pytest>=8.0",
85
85
  "pip-audit>=2.0",
86
86
  "pre-commit>=3.0",
87
+ # tests/test_viz_wheel_contents.py builds a wheel with
88
+ # --no-build-isolation so the suite never needs PyPI; the backend must be
89
+ # importable from this environment. Floor pinned past CVE-2025-47273
90
+ # (fixed in 78.1.1) so the restore step's pip-audit stays green.
91
+ "setuptools>=78.1.1",
87
92
  ]
88
93
  docs = [
89
94
  "mkdocs-material~=9.6",
@@ -903,7 +903,7 @@ def _custom_prompt_looks_like_path(value: str) -> bool:
903
903
  """040/D2: a value counts as path-shaped when it carries a path
904
904
  separator or a file extension (a dot). A path-shaped value that names no
905
905
  existing file is a hard error, never silent text."""
906
- return "/" in value or "\\" in value or Path(value).suffix != ""
906
+ return "/" in value or "\\" in value or Path(value.rstrip(".")).suffix != ""
907
907
 
908
908
 
909
909
  def resolve_custom_prompt(value: str, pipeline_dir: Path, step_name: str) -> str:
@@ -0,0 +1,301 @@
1
+ """Claude subscription OAuth PKCE flow (066).
2
+
3
+ The Claude subscription endpoint consumes a Claude Code OAuth access token.
4
+ ``claude-login`` runs the same first-party PKCE flow and writes the token
5
+ file the engine's ``api_key_secret`` FileProvider reads; it never runs
6
+ during a pipeline run. Login only: refresh-on-read is a follow-up plan, so
7
+ the refresh token is persisted now and the file schema stays put.
8
+
9
+ The token exchange uses a system ``curl`` subprocess, not urllib: the
10
+ Cloudflare edge in front of platform.claude.com answers urllib with 403
11
+ 1010 "Access denied based on your browser's signature", while system curl
12
+ reaches the API. A missing curl is a loud error, never a urllib fallback.
13
+
14
+ The default client id is Claude Code's own first-party OAuth app id, the
15
+ same borrow pattern the Copilot path uses with OpenCode's app, and is
16
+ overridable via ``AGENTIC_DEV_LOOP_CLAUDE_CLIENT_ID``.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import base64
22
+ import hashlib
23
+ import json
24
+ import os
25
+ import secrets
26
+ import subprocess
27
+ import threading
28
+ import urllib.parse
29
+ import webbrowser
30
+ from datetime import datetime, timedelta, timezone
31
+ from http.server import BaseHTTPRequestHandler, HTTPServer
32
+ from pathlib import Path
33
+
34
+ AUTHORIZE_URL = "https://claude.com/cai/oauth/authorize"
35
+ TOKEN_ENDPOINT = "https://platform.claude.com/v1/oauth/token"
36
+ TOKEN_PATH = Path("~/.agentic-dev-loop/auth/claude.json")
37
+
38
+ CLAUDE_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e"
39
+ CLIENT_ID_ENV = "AGENTIC_DEV_LOOP_CLAUDE_CLIENT_ID"
40
+ SCOPES = (
41
+ "org:create_api_key user:profile user:inference "
42
+ "user:sessions:claude_code user:mcp_servers user:file_upload "
43
+ "user:plugins")
44
+
45
+ CURL_TIMEOUT = 60
46
+ CALLBACK_TIMEOUT = 300
47
+ REQUIRED_TOKEN_FIELDS = ("access_token", "refresh_token", "expires_in")
48
+
49
+
50
+ class ClaudeAuthError(RuntimeError):
51
+ """A loud fail-fast auth failure naming the operator's next step."""
52
+
53
+
54
+ def client_id() -> str:
55
+ """The OAuth app id: the overridable env var, else the pinned default."""
56
+ return os.environ.get(CLIENT_ID_ENV) or CLAUDE_CLIENT_ID
57
+
58
+
59
+ def _b64url(raw: bytes) -> str:
60
+ return base64.urlsafe_b64encode(raw).rstrip(b"=").decode("ascii")
61
+
62
+
63
+ def pkce_pair(rng=os.urandom) -> tuple[str, str]:
64
+ """A 32-byte PKCE verifier and its S256 challenge, base64url unpadded."""
65
+ verifier = _b64url(rng(32))
66
+ challenge = _b64url(hashlib.sha256(verifier.encode("ascii")).digest())
67
+ return verifier, challenge
68
+
69
+
70
+ def authorize_url(redirect_uri: str, code_challenge: str, state: str,
71
+ client_id_value: str | None = None) -> str:
72
+ """The captured authorize URL shape, parameter order included."""
73
+ params = [
74
+ ("code", "true"),
75
+ ("client_id", client_id_value or client_id()),
76
+ ("response_type", "code"),
77
+ ("redirect_uri", redirect_uri),
78
+ ("scope", SCOPES),
79
+ ("code_challenge", code_challenge),
80
+ ("code_challenge_method", "S256"),
81
+ ("state", state),
82
+ ]
83
+ query = urllib.parse.urlencode(params, safe=":/")
84
+ return f"{AUTHORIZE_URL}?{query}"
85
+
86
+
87
+ def _run_curl(argv: list[str]) -> subprocess.CompletedProcess:
88
+ try:
89
+ return subprocess.run(argv, capture_output=True, text=True,
90
+ timeout=CURL_TIMEOUT, check=False)
91
+ except subprocess.TimeoutExpired as e:
92
+ raise ClaudeAuthError(
93
+ "the Claude token endpoint did not respond within "
94
+ f"{CURL_TIMEOUT}s - retry `agentic-dev-loop claude-login`") from e
95
+
96
+
97
+ def _missing_curl() -> ClaudeAuthError:
98
+ return ClaudeAuthError(
99
+ "curl is required for the Claude token exchange but was not found "
100
+ "on PATH - install curl and run `agentic-dev-loop claude-login` again")
101
+
102
+
103
+ def _parse_response(output: str, returncode: int, stderr: str,
104
+ token_endpoint: str) -> tuple[str, int]:
105
+ if "\n" not in output:
106
+ detail = output.strip() or stderr.strip() or f"curl exit {returncode}"
107
+ raise ClaudeAuthError(
108
+ f"curl did not return an HTTP status from {token_endpoint}: "
109
+ f"{detail}")
110
+ body, _, status_text = output.rpartition("\n")
111
+ try:
112
+ return body, int(status_text.strip())
113
+ except ValueError as e:
114
+ raise ClaudeAuthError(
115
+ f"could not read the HTTP status from curl output for "
116
+ f"{token_endpoint}: {status_text.strip() or 'empty'}") from e
117
+
118
+
119
+ def exchange_token(code: str, redirect_uri: str, code_verifier: str,
120
+ state: str, *, token_endpoint: str = TOKEN_ENDPOINT,
121
+ curl_runner=None) -> dict:
122
+ """Exchange the authorization code for tokens via system curl."""
123
+ body = json.dumps({
124
+ "grant_type": "authorization_code",
125
+ "code": code,
126
+ "redirect_uri": redirect_uri,
127
+ "client_id": client_id(),
128
+ "code_verifier": code_verifier,
129
+ "state": state,
130
+ }, separators=(",", ":"))
131
+ argv = [
132
+ "curl", "-sS",
133
+ "-X", "POST", token_endpoint,
134
+ "-H", "Content-Type: application/json",
135
+ "-H", "Accept: application/json",
136
+ "--data-binary", body,
137
+ "-w", "\n%{http_code}",
138
+ ]
139
+ runner = curl_runner or _run_curl
140
+ try:
141
+ completed = runner(argv)
142
+ except FileNotFoundError as e:
143
+ raise _missing_curl() from e
144
+ body_text, status = _parse_response(
145
+ completed.stdout or "", completed.returncode,
146
+ completed.stderr or "", token_endpoint)
147
+ detail = body_text.strip()[:500]
148
+ if status == 429:
149
+ raise ClaudeAuthError(
150
+ "the token endpoint is rate limited (HTTP 429): "
151
+ f"{detail or 'rate_limit_error'}; retry in a few minutes by "
152
+ "running `agentic-dev-loop claude-login` again")
153
+ if status != 200:
154
+ raise ClaudeAuthError(
155
+ f"the token exchange failed (HTTP {status}): "
156
+ f"{detail or 'no response body'}")
157
+ try:
158
+ payload = json.loads(body_text)
159
+ except json.JSONDecodeError as e:
160
+ raise ClaudeAuthError(
161
+ "the token endpoint returned a non-JSON response (HTTP 200): "
162
+ f"{detail}") from e
163
+ if not isinstance(payload, dict):
164
+ raise ClaudeAuthError(
165
+ "the token endpoint returned a JSON value that is not an object")
166
+ missing = [name for name in REQUIRED_TOKEN_FIELDS
167
+ if not _present(payload.get(name))]
168
+ if missing:
169
+ raise ClaudeAuthError(
170
+ "the token endpoint response is missing required field(s): "
171
+ + ", ".join(missing))
172
+ return payload
173
+
174
+
175
+ def _present(value) -> bool:
176
+ if isinstance(value, str):
177
+ return bool(value)
178
+ return isinstance(value, (int, float)) and not isinstance(value, bool)
179
+
180
+
181
+ def _expires_in_seconds(value) -> float:
182
+ try:
183
+ return float(value)
184
+ except (TypeError, ValueError) as e:
185
+ raise ClaudeAuthError(
186
+ f"the token endpoint returned a non-numeric expires_in: {value!r}"
187
+ ) from e
188
+
189
+
190
+ def write_token(token: dict, token_path: Path | str | None = None) -> Path:
191
+ """Write access token, refresh token, and absolute expiry with mode 600."""
192
+ access = token.get("access_token")
193
+ refresh = token.get("refresh_token")
194
+ if not isinstance(access, str) or not access:
195
+ raise ClaudeAuthError("the token payload has no access_token")
196
+ if not isinstance(refresh, str) or not refresh:
197
+ raise ClaudeAuthError("the token payload has no refresh_token")
198
+ expires_at = (datetime.now(timezone.utc)
199
+ + timedelta(seconds=_expires_in_seconds(
200
+ token.get("expires_in"))))
201
+ payload = {
202
+ "access_token": access,
203
+ "refresh_token": refresh,
204
+ "expires_at": expires_at.isoformat(),
205
+ }
206
+ path = (Path(token_path).expanduser() if token_path
207
+ else TOKEN_PATH.expanduser())
208
+ path.parent.mkdir(parents=True, exist_ok=True)
209
+ text = json.dumps(payload, indent=2, sort_keys=True) + "\n"
210
+ fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
211
+ with os.fdopen(fd, "w", encoding="utf-8") as handle:
212
+ handle.write(text)
213
+ os.chmod(path, 0o600)
214
+ return path
215
+
216
+
217
+ class _CallbackServer(HTTPServer):
218
+ expected_state: str
219
+ result: dict[str, str]
220
+
221
+
222
+ class _CallbackHandler(BaseHTTPRequestHandler):
223
+ def do_GET(self):
224
+ server = self.server
225
+ params = urllib.parse.parse_qs(
226
+ urllib.parse.urlsplit(self.path).query)
227
+ state = (params.get("state") or [""])[0]
228
+ if state != server.expected_state:
229
+ server.result["error"] = "state mismatch in the OAuth callback"
230
+ self._respond(400, "Claude login failed: state mismatch. "
231
+ "Close this window and run "
232
+ "`agentic-dev-loop claude-login` again.")
233
+ return
234
+ error = (params.get("error") or [""])[0]
235
+ if error:
236
+ server.result["error"] = error
237
+ self._respond(400, f"Claude login failed: {error}. Close this "
238
+ "window and run `agentic-dev-loop claude-login` "
239
+ "again.")
240
+ return
241
+ code = (params.get("code") or [""])[0]
242
+ if not code:
243
+ server.result["error"] = "the OAuth callback carried no code"
244
+ self._respond(400, "Claude login failed: no authorization code.")
245
+ return
246
+ server.result["code"] = code
247
+ self._respond(200, "Claude login complete. You can close this window.")
248
+
249
+ def _respond(self, status: int, message: str) -> None:
250
+ body = (message + "\n").encode("utf-8")
251
+ self.send_response(status)
252
+ self.send_header("Content-Type", "text/plain; charset=utf-8")
253
+ self.send_header("Content-Length", str(len(body)))
254
+ self.end_headers()
255
+ self.wfile.write(body)
256
+
257
+ def log_message(self, fmt, *args):
258
+ pass
259
+
260
+
261
+ def login(token_path: Path | str | None = None, *, opener=None,
262
+ token_endpoint: str = TOKEN_ENDPOINT, curl_runner=None,
263
+ printer=print,
264
+ callback_timeout: float = CALLBACK_TIMEOUT) -> dict:
265
+ """Run the PKCE flow and persist the token; never called by a pipeline."""
266
+ verifier, challenge = pkce_pair()
267
+ state = secrets.token_urlsafe(32)
268
+ result: dict[str, str] = {}
269
+ server = _CallbackServer(("127.0.0.1", 0), _CallbackHandler)
270
+ server.expected_state = state
271
+ server.result = result
272
+ port = server.server_address[1]
273
+ redirect_uri = f"http://localhost:{port}/callback"
274
+ url = authorize_url(redirect_uri, challenge, state)
275
+ open_browser = opener or webbrowser.open
276
+ printer(f"Open this URL to authorize Claude: {url}")
277
+ thread = threading.Thread(target=server.handle_request, daemon=True)
278
+ thread.start()
279
+ try:
280
+ open_browser(url)
281
+ thread.join(callback_timeout)
282
+ finally:
283
+ server.server_close()
284
+ if thread.is_alive():
285
+ raise ClaudeAuthError(
286
+ "timed out waiting for the browser callback - run "
287
+ "`agentic-dev-loop claude-login` again")
288
+ if result.get("error"):
289
+ raise ClaudeAuthError(
290
+ f"authorization failed: {result['error']} - run "
291
+ "`agentic-dev-loop claude-login` again")
292
+ code = result.get("code")
293
+ if not code:
294
+ raise ClaudeAuthError(
295
+ "the OAuth callback carried no authorization code - run "
296
+ "`agentic-dev-loop claude-login` again")
297
+ token = exchange_token(code, redirect_uri, verifier, state,
298
+ token_endpoint=token_endpoint,
299
+ curl_runner=curl_runner)
300
+ write_token(token, token_path)
301
+ return token
@@ -14,6 +14,9 @@ import threading
14
14
  from pathlib import Path
15
15
 
16
16
  from agentic_dev_loop.core.config import PipelineConfig, load_config
17
+ from .claude_auth import TOKEN_PATH as CLAUDE_TOKEN_PATH
18
+ from .claude_auth import ClaudeAuthError
19
+ from .claude_auth import login as claude_login
17
20
  from .copilot_auth import (TOKEN_PATH, CopilotAuthError, list_copilot_models,
18
21
  login, read_token)
19
22
  from .containers import containers_report
@@ -35,6 +38,7 @@ a loud error). Run `agentic-dev-loop <PIPELINE> --help` for
35
38
  loader-contributed options (e.g. --plan, --aidlc-unit).
36
39
 
37
40
  commands:
41
+ claude-login Run the Claude OAuth PKCE flow and store the token.
38
42
  containers Inspect engine sandbox containers and networks.
39
43
  copilot-login Run the GitHub device flow and store the Copilot OAuth token.
40
44
  copilot-models List the Copilot models available to this token's tier.
@@ -192,8 +196,31 @@ def _print_containers_report(report) -> None:
192
196
  f"run={run} pid={pid_s}")
193
197
 
194
198
 
195
- def _token_path(value: str | None) -> Path:
196
- return Path(value).expanduser() if value else TOKEN_PATH.expanduser()
199
+ def _token_path(value: str | None, default: Path = TOKEN_PATH) -> Path:
200
+ return Path(value).expanduser() if value else default.expanduser()
201
+
202
+
203
+ def _claude_login_parser() -> argparse.ArgumentParser:
204
+ parser = argparse.ArgumentParser(
205
+ prog="agentic-dev-loop claude-login",
206
+ description="Run the Claude subscription OAuth flow and store the "
207
+ "token.")
208
+ parser.add_argument("--token-file", default=None,
209
+ help="token path (default: "
210
+ "~/.agentic-dev-loop/auth/claude.json)")
211
+ return parser
212
+
213
+
214
+ def _run_claude_login(argv: list[str]) -> int:
215
+ args = _claude_login_parser().parse_args(argv)
216
+ path = _token_path(args.token_file, CLAUDE_TOKEN_PATH)
217
+ try:
218
+ claude_login(token_path=path)
219
+ except ClaudeAuthError as e:
220
+ print(f"claude-login: {e}", file=sys.stderr)
221
+ return 1
222
+ print(f"Claude token written to {path}")
223
+ return 0
197
224
 
198
225
 
199
226
  def _copilot_login_parser() -> argparse.ArgumentParser:
@@ -425,6 +452,8 @@ def main(argv: list[str] | None = None, *, keep_alive: bool = True) -> int:
425
452
  return 2
426
453
  print(_TOP_HELP)
427
454
  return 0
455
+ if argv and argv[0] == "claude-login":
456
+ return _run_claude_login(argv[1:])
428
457
  if argv and argv[0] == "containers":
429
458
  return _run_containers(argv[1:])
430
459
  if argv and argv[0] == "copilot-login":
@@ -58,7 +58,8 @@ def check_orchestrator_install(workspace: str | Path,
58
58
  raise HostBoundaryError(
59
59
  f"agentic_dev_loop resolves to {resolved}, inside the workspace "
60
60
  f"{ws} - the engine must be installed outside the workspace "
61
- "(e.g. `pipx install agentic-dev-loop`); refusing to run")
61
+ "(e.g. `pipx install --pip-args=--pre agentic-dev-loop`); "
62
+ "refusing to run")
62
63
 
63
64
 
64
65
  def tooling_rel_paths(cfg: "PipelineConfig", pipeline_path: str | Path,
@@ -48,6 +48,18 @@ def _reasoning_stripped_error(llm: LLM) -> RuntimeError:
48
48
  )
49
49
 
50
50
 
51
+ def _with_billing_prefix(llm: LLM, out: list[dict]) -> list[dict]:
52
+ """Claude subscription policy (provider_wire.py): the gateway demands an
53
+ ``x-anthropic-billing-header`` entry in the FIRST system text block of
54
+ every request (else 429). The adapter-carried prefix lands as its own
55
+ system dict at index 0, ahead of the pipeline's real system prompt;
56
+ litellm preserves system-message order into the Messages API body."""
57
+ prefix = getattr(llm, "_agentic_dev_loop_system_prefix", None)
58
+ if prefix:
59
+ out.insert(0, {"role": "system", "content": prefix})
60
+ return out
61
+
62
+
51
63
  if not getattr(_ORIGINAL_FORMAT_MESSAGES, "_agentic_dev_loop_reasoning_repair", False):
52
64
  def _config_gated_reasoning_serializer(self, messages: list) -> list[dict]:
53
65
  """The engine-owned reasoning passback serializer (054 D8).
@@ -68,7 +80,7 @@ if not getattr(_ORIGINAL_FORMAT_MESSAGES, "_agentic_dev_loop_reasoning_repair",
68
80
  raise _reasoning_stripped_error(self)
69
81
  validate_string_tool_results(
70
82
  messages, out, forced=bool(self.force_string_serializer))
71
- return out
83
+ return _with_billing_prefix(self, out)
72
84
 
73
85
  last = None
74
86
  for msg, d in zip(messages, out):
@@ -87,7 +99,7 @@ if not getattr(_ORIGINAL_FORMAT_MESSAGES, "_agentic_dev_loop_reasoning_repair",
87
99
  d["reasoning_content"] = rc
88
100
  validate_string_tool_results(
89
101
  messages, out, forced=bool(self.force_string_serializer))
90
- return out
102
+ return _with_billing_prefix(self, out)
91
103
 
92
104
  _config_gated_reasoning_serializer._agentic_dev_loop_reasoning_repair = True
93
105
  LLM.format_messages_for_llm = _config_gated_reasoning_serializer
@@ -161,10 +173,11 @@ def build_llm(cfg: LlmConfig, reasoning_effort: str | None = None) -> LLM:
161
173
  # off. reasoning_effort is always passed (None when unset) so the SDK
162
174
  # "high" default cannot engage; absent effort = reasoning off.
163
175
  effort = reasoning_effort if reasoning_effort is not None else cfg.reasoning_effort
164
- # Copilot gateway policy (provider_wire.py): the gateway silently drops
176
+ # Gateway policy (provider_wire.py): the Copilot gateway silently drops
165
177
  # ARRAY-encoded tool-result content, so Copilot-configured instances
166
- # force the SDK's documented string content serializer. Every other
167
- # gateway keeps the SDK default (None = auto-detect by model registry).
178
+ # force the SDK's documented string content serializer. The Claude
179
+ # subscription endpoint keeps the SDK default (None = auto-detect by
180
+ # model registry) and carries the billing-header system prefix instead.
168
181
  adapter = adapter_for(cfg.base_url)
169
182
  llm = LLM(
170
183
  model=cfg.model,
@@ -178,4 +191,6 @@ def build_llm(cfg: LlmConfig, reasoning_effort: str | None = None) -> LLM:
178
191
  llm._agentic_dev_loop_reasoning_effort = effort
179
192
  llm._agentic_dev_loop_merge_choices = (
180
193
  adapter.merge_choices if adapter else False)
194
+ llm._agentic_dev_loop_system_prefix = (
195
+ adapter.system_prefix if adapter else None)
181
196
  return llm
@@ -43,8 +43,12 @@ def run_upgrade(argv: list[str], *, workspace: Path | None = None,
43
43
  "pipx-managed", file=sys.stderr)
44
44
  return 1
45
45
 
46
+ # --pre is required: the release process yanks superseded releases, and
47
+ # the current latest (1.0.0b1) is a pre-release, so a plain install has
48
+ # no satisfiable version. pipx translates --pre on both backends (pip and
49
+ # uv); dropping it here breaks upgrade until a stable release ships.
46
50
  result = subprocess.run(
47
- [pipx, "install", "--force", "agentic-dev-loop"],
51
+ [pipx, "install", "--force", "--pip-args=--pre", "agentic-dev-loop"],
48
52
  capture_output=True, text=True, check=False, env=env)
49
53
  if result.returncode != 0:
50
54
  detail = (result.stderr or "unknown error").strip()
@@ -7,8 +7,9 @@ ARRAY-encoded tool-result content (probe-proven 2026-11-06: string tool
7
7
  content is answered, array tool content is invisible to the model - see
8
8
  aidlc-docs/audit.md). The SDK's own remedy is its documented
9
9
  ``force_string_serializer`` field on LLM, so this module is POLICY ONLY:
10
- which gateway base URL requires which wire shape. ``build_llm`` applies the
11
- policy to every instance it builds. Nothing is monkeypatched.
10
+ which gateway base URL requires which wire shape, plus the one Claude
11
+ subscription policy (the billing-header system prefix). ``build_llm`` applies
12
+ the policy to every instance it builds. Nothing is monkeypatched.
12
13
  """
13
14
 
14
15
  from __future__ import annotations
@@ -17,20 +18,54 @@ from dataclasses import dataclass
17
18
 
18
19
  from agentic_dev_loop.engine.copilot_auth import COPILOT_BASE_URL
19
20
 
21
+ # The Anthropic subscription endpoint. An llm block points its base_url at
22
+ # this exact value: litellm routes bare claude-* model names through its
23
+ # native Anthropic provider path (Messages API, Bearer auth auto-detected
24
+ # from a ``sk-ant-oat01-`` token), and the adapter keyed below engages.
25
+ # No /v1 suffix: litellm appends /v1/messages to a base URL that does not
26
+ # already end with it (a /v1 value would 404 on /v1/v1/messages).
27
+ CLAUDE_BASE_URL = "https://api.anthropic.com"
28
+
29
+ # Anthropic's gateway only accepts OAuth-subscription traffic that is shaped
30
+ # like a Claude Code request. The gate is the FIRST system text block: it
31
+ # must carry an ``x-anthropic-billing-header:`` entry, or the endpoint
32
+ # answers 429 rate_limit_error regardless of every other header and body
33
+ # field (probe-proven 2026-11-13 against the live endpoint: only the
34
+ # system field moved the replay between 200 and 429). The field values are
35
+ # unvalidated with one exception, cc_version: models that require a newer
36
+ # Claude Code are refused with claude_code_version_too_old, so pin the
37
+ # current CLI version and bump it when the gateway demands newer.
38
+ CLAUDE_BILLING_HEADER = (
39
+ "x-anthropic-billing-header: cc_version=2.1.323.c04; "
40
+ "cc_entrypoint=sdk-cli; cc_turn_origin=cli;"
41
+ )
42
+
20
43
 
21
44
  @dataclass(frozen=True)
22
45
  class WireAdapter:
23
- """The wire shape a gateway requires for message content."""
46
+ """Provider policy ``build_llm`` applies to every instance it builds.
47
+
48
+ ``system_prefix`` (Claude subscription): a system message prepended
49
+ ahead of the pipeline's real system prompt on every outgoing request."""
24
50
 
25
51
  name: str
26
52
  force_string_serializer: bool
27
53
  merge_choices: bool = False
54
+ system_prefix: str | None = None
28
55
 
29
56
 
30
57
  copilot_strings = WireAdapter(name="copilot-strings", force_string_serializer=True,
31
58
  merge_choices=True)
32
59
 
33
- WIRE_ADAPTERS: dict[str, WireAdapter] = {COPILOT_BASE_URL: copilot_strings}
60
+ claude_subscription = WireAdapter(name="claude-subscription",
61
+ force_string_serializer=False,
62
+ merge_choices=False,
63
+ system_prefix=CLAUDE_BILLING_HEADER)
64
+
65
+ WIRE_ADAPTERS: dict[str, WireAdapter] = {
66
+ COPILOT_BASE_URL: copilot_strings,
67
+ CLAUDE_BASE_URL: claude_subscription,
68
+ }
34
69
 
35
70
 
36
71
  def adapter_for(base_url: str | None) -> WireAdapter | None:
@@ -44,6 +79,8 @@ def provider_name(base_url: str | None) -> str:
44
79
  """Display name of the LLM provider behind a base URL."""
45
80
  if base_url is not None and base_url.rstrip("/") == COPILOT_BASE_URL:
46
81
  return "Copilot"
82
+ if base_url is not None and base_url.rstrip("/") == CLAUDE_BASE_URL:
83
+ return "Claude"
47
84
  return "OpenAI"
48
85
 
49
86
 
@@ -20,4 +20,4 @@ The command prints two sections: `===MERMAID===` then `===SUMMARY===`.
20
20
 
21
21
  ## Errors
22
22
 
23
- - If `agentic-dev-loop` is not on PATH, report that the engine is not installed (`pipx install agentic-dev-loop`) and stop.
23
+ - If `agentic-dev-loop` is not on PATH, report that the engine is not installed (`pipx install --pip-args="--pre" agentic-dev-loop`) and stop.
@@ -77,13 +77,15 @@ class FileProvider:
77
77
  if not path.is_file():
78
78
  raise SecretResolutionError(
79
79
  f"secret file not found: {path} - run "
80
- "`agentic-dev-loop copilot-login` to create it")
80
+ "`agentic-dev-loop copilot-login` (Copilot) or "
81
+ "`agentic-dev-loop claude-login` (Claude) to create it")
81
82
  try:
82
83
  text = path.read_text(encoding="utf-8")
83
84
  except OSError as e:
84
85
  raise SecretResolutionError(
85
- f"secret file unreadable: {path}: {e} - run "
86
- "`agentic-dev-loop copilot-login` to recreate it") from e
86
+ f"secret file unreadable: {path}: {e} - run the matching "
87
+ "login subcommand (`agentic-dev-loop copilot-login` or "
88
+ "`agentic-dev-loop claude-login`) to recreate it") from e
87
89
  if json_key is None:
88
90
  return text.strip()
89
91
  try: