agentirc-cli 6.2.1__tar.gz → 6.2.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 (356) hide show
  1. agentirc_cli-6.2.3/.claude/agents/doc-test-alignment.md +154 -0
  2. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/.claude/skills/pr-review/SKILL.md +9 -1
  3. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/CHANGELOG.md +14 -0
  4. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/CLAUDE.md +10 -1
  5. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/PKG-INFO +1 -1
  6. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/console/app.py +33 -9
  7. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/console/client.py +77 -35
  8. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/pyproject.toml +1 -1
  9. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_console_client.py +99 -1
  10. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/uv.lock +1 -1
  11. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/.claude/skills/run-tests/SKILL.md +0 -0
  12. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/.claude/skills/run-tests/scripts/test.sh +0 -0
  13. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/.flake8 +0 -0
  14. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/.github/workflows/docs-check.yml +0 -0
  15. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/.github/workflows/publish.yml +0 -0
  16. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/.github/workflows/security-checks.yml +0 -0
  17. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/.github/workflows/tests.yml +0 -0
  18. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/.gitignore +0 -0
  19. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/.markdownlint-cli2.yaml +0 -0
  20. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/.pr_agent.toml +0 -0
  21. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/.pre-commit-config.yaml +0 -0
  22. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/.pylintrc +0 -0
  23. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/Gemfile +0 -0
  24. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/Gemfile.lock +0 -0
  25. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/LICENSE +0 -0
  26. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/README.md +0 -0
  27. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/SECURITY.md +0 -0
  28. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/_config.agentirc.yml +0 -0
  29. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/_config.base.yml +0 -0
  30. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/_config.culture.yml +0 -0
  31. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/_data/sites.yml +0 -0
  32. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/_includes/head_custom.html +0 -0
  33. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/_plugins/site_filter.rb +0 -0
  34. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/_sass/color_schemes/anthropic.scss +0 -0
  35. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/_sass/color_schemes/dark-terminal.scss +0 -0
  36. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/_sass/custom/custom.scss +0 -0
  37. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/assets/images/IMG_3183.png +0 -0
  38. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/assets/images/apple-touch-icon.png +0 -0
  39. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/assets/images/favicon-16x16.png +0 -0
  40. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/assets/images/favicon-32x32.png +0 -0
  41. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/assets/images/favicon.ico +0 -0
  42. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/assets/images/og-agentirc.png +0 -0
  43. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/assets/images/og-culture.png +0 -0
  44. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/__init__.py +0 -0
  45. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/__main__.py +0 -0
  46. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/CLAUDE.md +0 -0
  47. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/__init__.py +0 -0
  48. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/__main__.py +0 -0
  49. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/channel.py +0 -0
  50. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/client.py +0 -0
  51. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/config.py +0 -0
  52. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/docs/agentirc-architecture.md +0 -0
  53. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/docs/agentirc-features.md +0 -0
  54. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/docs/agentirc-skill.md +0 -0
  55. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/docs/agentirc.md +0 -0
  56. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/history_store.py +0 -0
  57. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/ircd.py +0 -0
  58. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/remote_client.py +0 -0
  59. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/room_store.py +0 -0
  60. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/rooms_util.py +0 -0
  61. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/server_link.py +0 -0
  62. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/skill.py +0 -0
  63. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/skills/__init__.py +0 -0
  64. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/skills/history.py +0 -0
  65. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/skills/icon.py +0 -0
  66. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/skills/rooms.py +0 -0
  67. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/skills/threads.py +0 -0
  68. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/agentirc/thread_store.py +0 -0
  69. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/aio.py +0 -0
  70. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/bots/__init__.py +0 -0
  71. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/bots/bot.py +0 -0
  72. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/bots/bot_manager.py +0 -0
  73. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/bots/config.py +0 -0
  74. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/bots/http_listener.py +0 -0
  75. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/bots/template_engine.py +0 -0
  76. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/bots/virtual_client.py +0 -0
  77. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/cli/__init__.py +0 -0
  78. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/cli/agent.py +0 -0
  79. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/cli/bot.py +0 -0
  80. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/cli/channel.py +0 -0
  81. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/cli/mesh.py +0 -0
  82. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/cli/server.py +0 -0
  83. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/cli/shared/__init__.py +0 -0
  84. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/cli/shared/constants.py +0 -0
  85. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/cli/shared/display.py +0 -0
  86. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/cli/shared/formatting.py +0 -0
  87. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/cli/shared/ipc.py +0 -0
  88. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/cli/shared/mesh.py +0 -0
  89. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/cli/shared/process.py +0 -0
  90. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/cli/skills.py +0 -0
  91. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/__init__.py +0 -0
  92. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/acp/__init__.py +0 -0
  93. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/acp/agent_runner.py +0 -0
  94. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/acp/config.py +0 -0
  95. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/acp/culture.yaml +0 -0
  96. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/acp/daemon.py +0 -0
  97. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/acp/ipc.py +0 -0
  98. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/acp/irc_transport.py +0 -0
  99. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/acp/message_buffer.py +0 -0
  100. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/acp/skill/SKILL.md +0 -0
  101. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/acp/skill/__init__.py +0 -0
  102. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/acp/skill/irc_client.py +0 -0
  103. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/acp/socket_server.py +0 -0
  104. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/acp/supervisor.py +0 -0
  105. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/acp/webhook.py +0 -0
  106. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/__init__.py +0 -0
  107. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/__main__.py +0 -0
  108. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/agent_runner.py +0 -0
  109. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/config.py +0 -0
  110. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/culture.yaml +0 -0
  111. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/daemon.py +0 -0
  112. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/ipc.py +0 -0
  113. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/irc_transport.py +0 -0
  114. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/message_buffer.py +0 -0
  115. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/skill/SKILL.md +0 -0
  116. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/skill/__init__.py +0 -0
  117. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/skill/irc_client.py +0 -0
  118. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/socket_server.py +0 -0
  119. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/supervisor.py +0 -0
  120. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/claude/webhook.py +0 -0
  121. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/codex/__init__.py +0 -0
  122. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/codex/agent_runner.py +0 -0
  123. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/codex/config.py +0 -0
  124. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/codex/culture.yaml +0 -0
  125. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/codex/daemon.py +0 -0
  126. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/codex/ipc.py +0 -0
  127. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/codex/irc_transport.py +0 -0
  128. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/codex/message_buffer.py +0 -0
  129. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/codex/skill/SKILL.md +0 -0
  130. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/codex/skill/__init__.py +0 -0
  131. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/codex/skill/irc_client.py +0 -0
  132. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/codex/socket_server.py +0 -0
  133. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/codex/supervisor.py +0 -0
  134. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/codex/webhook.py +0 -0
  135. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/copilot/__init__.py +0 -0
  136. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/copilot/agent_runner.py +0 -0
  137. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/copilot/config.py +0 -0
  138. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/copilot/culture.yaml +0 -0
  139. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/copilot/daemon.py +0 -0
  140. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/copilot/ipc.py +0 -0
  141. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/copilot/irc_transport.py +0 -0
  142. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/copilot/message_buffer.py +0 -0
  143. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/copilot/skill/SKILL.md +0 -0
  144. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/copilot/skill/__init__.py +0 -0
  145. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/copilot/skill/irc_client.py +0 -0
  146. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/copilot/socket_server.py +0 -0
  147. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/copilot/supervisor.py +0 -0
  148. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/clients/copilot/webhook.py +0 -0
  149. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/config.py +0 -0
  150. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/console/__init__.py +0 -0
  151. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/console/commands.py +0 -0
  152. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/console/status.py +0 -0
  153. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/console/widgets/__init__.py +0 -0
  154. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/console/widgets/chat.py +0 -0
  155. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/console/widgets/info_panel.py +0 -0
  156. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/console/widgets/sidebar.py +0 -0
  157. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/credentials.py +0 -0
  158. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/formatting.py +0 -0
  159. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/learn_prompt.py +0 -0
  160. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/mesh_config.py +0 -0
  161. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/observer.py +0 -0
  162. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/overview/__init__.py +0 -0
  163. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/overview/collector.py +0 -0
  164. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/overview/model.py +0 -0
  165. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/overview/renderer_text.py +0 -0
  166. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/overview/renderer_web.py +0 -0
  167. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/overview/web/style.css +0 -0
  168. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/persistence.py +0 -0
  169. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/pidfile.py +0 -0
  170. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/protocol/__init__.py +0 -0
  171. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/protocol/commands.py +0 -0
  172. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/protocol/extensions/federation.md +0 -0
  173. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/protocol/extensions/history.md +0 -0
  174. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/protocol/extensions/icons.md +0 -0
  175. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/protocol/extensions/rooms.md +0 -0
  176. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/protocol/extensions/tags.md +0 -0
  177. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/protocol/extensions/threads.md +0 -0
  178. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/protocol/message.py +0 -0
  179. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/protocol/protocol-index.md +0 -0
  180. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/protocol/replies.py +0 -0
  181. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/culture/skills/culture/SKILL.md +0 -0
  182. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/README.md +0 -0
  183. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/agentirc/architecture-overview.md +0 -0
  184. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/agentirc/index.md +0 -0
  185. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/agentirc/why-agentirc.md +0 -0
  186. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/culture/agent-lifecycle.md +0 -0
  187. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/culture/choose-a-harness.md +0 -0
  188. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/culture/index.md +0 -0
  189. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/culture/mental-model.md +0 -0
  190. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/culture/operate.md +0 -0
  191. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/culture/patterns.md +0 -0
  192. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/culture/quickstart.md +0 -0
  193. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/culture/reflective-development.md +0 -0
  194. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/culture/vision-patterns-index.md +0 -0
  195. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/culture/vision.md +0 -0
  196. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/culture/why-culture.md +0 -0
  197. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/architecture/agent-harness-spec.md +0 -0
  198. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/architecture/index.md +0 -0
  199. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/architecture/layers.md +0 -0
  200. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/architecture/threads.md +0 -0
  201. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/cli/commands.md +0 -0
  202. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/cli/index.md +0 -0
  203. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/harnesses/acp.md +0 -0
  204. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/harnesses/claude.md +0 -0
  205. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/harnesses/codex.md +0 -0
  206. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/harnesses/copilot.md +0 -0
  207. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/harnesses/index.md +0 -0
  208. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/index.md +0 -0
  209. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/server/architecture.md +0 -0
  210. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/server/config.md +0 -0
  211. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/server/deployment.md +0 -0
  212. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/server/index.md +0 -0
  213. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/reference/server/security.md +0 -0
  214. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  215. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/concepts/federation.md +0 -0
  216. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/concepts/harnesses.md +0 -0
  217. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/concepts/humans-and-agents.md +0 -0
  218. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/concepts/index.md +0 -0
  219. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/concepts/persistence.md +0 -0
  220. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/concepts/rooms.md +0 -0
  221. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/demos/magic-demo.md +0 -0
  222. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/guides/first-session.md +0 -0
  223. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/guides/index.md +0 -0
  224. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/guides/join-as-human.md +0 -0
  225. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/guides/local-setup.md +0 -0
  226. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/guides/multi-machine.md +0 -0
  227. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/use-cases/01-pair-programming.md +0 -0
  228. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/use-cases/02-code-review-ensemble.md +0 -0
  229. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/use-cases/03-cross-server-delegation.md +0 -0
  230. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/use-cases/04-knowledge-propagation.md +0 -0
  231. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/use-cases/05-the-observer.md +0 -0
  232. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/use-cases/06-cross-server-ops.md +0 -0
  233. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/use-cases/07-supervisor-intervention.md +0 -0
  234. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/use-cases/08-apps-as-agents.md +0 -0
  235. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/use-cases/09-research-swarm.md +0 -0
  236. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/use-cases/10-agent-lifecycle.md +0 -0
  237. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/shared/use-cases-index.md +0 -0
  238. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  239. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  240. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  241. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  242. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
  243. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
  244. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
  245. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
  246. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
  247. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/plans/2026-04-09-decentralized-agent-config.md +0 -0
  248. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/plans/2026-04-12-console-enhancements.md +0 -0
  249. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  250. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  251. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  252. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  253. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
  254. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
  255. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
  256. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
  257. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
  258. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
  259. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
  260. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
  261. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
  262. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-04-07-reflective-development-reframe-design.md +0 -0
  263. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-04-08-reflective-development-deepening-design.md +0 -0
  264. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-04-09-decentralized-agent-config-design.md +0 -0
  265. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/docs/superpowers/specs/2026-04-12-console-enhancements-design.md +0 -0
  266. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/favicon.ico +0 -0
  267. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/packages/agent-harness/README.md +0 -0
  268. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/packages/agent-harness/config.py +0 -0
  269. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/packages/agent-harness/culture.yaml +0 -0
  270. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/packages/agent-harness/daemon.py +0 -0
  271. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/packages/agent-harness/ipc.py +0 -0
  272. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/packages/agent-harness/irc_transport.py +0 -0
  273. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/packages/agent-harness/message_buffer.py +0 -0
  274. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/packages/agent-harness/skill/SKILL.md +0 -0
  275. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/packages/agent-harness/skill/irc_client.py +0 -0
  276. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/packages/agent-harness/socket_server.py +0 -0
  277. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/packages/agent-harness/webhook.py +0 -0
  278. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  279. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/plugins/claude-code/skills/culture/SKILL.md +0 -0
  280. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  281. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
  282. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/robots.txt +0 -0
  283. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/sonar-project.properties +0 -0
  284. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/__init__.py +0 -0
  285. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/conftest.py +0 -0
  286. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_acp_daemon.py +0 -0
  287. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_agent_runner.py +0 -0
  288. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_archive.py +0 -0
  289. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_bot.py +0 -0
  290. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_bot_config.py +0 -0
  291. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_bot_manager.py +0 -0
  292. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_bots_integration.py +0 -0
  293. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_channel.py +0 -0
  294. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_channel_cli.py +0 -0
  295. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_codex_daemon.py +0 -0
  296. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_connection.py +0 -0
  297. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_console_commands.py +0 -0
  298. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_console_connection.py +0 -0
  299. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_console_fixes_224_227.py +0 -0
  300. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_console_icons.py +0 -0
  301. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_console_integration.py +0 -0
  302. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_console_status.py +0 -0
  303. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_copilot_daemon.py +0 -0
  304. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_credentials.py +0 -0
  305. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_culture_config.py +0 -0
  306. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_daemon.py +0 -0
  307. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_daemon_config.py +0 -0
  308. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_daemon_ipc.py +0 -0
  309. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_discovery.py +0 -0
  310. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_display.py +0 -0
  311. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_federation.py +0 -0
  312. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_history.py +0 -0
  313. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_http_listener.py +0 -0
  314. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_integration_layer5.py +0 -0
  315. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_ipc.py +0 -0
  316. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_irc_transport.py +0 -0
  317. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_learn_prompt.py +0 -0
  318. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_link_reconnect.py +0 -0
  319. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_manifest_config.py +0 -0
  320. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_mention_alias.py +0 -0
  321. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_mention_target_cleanup.py +0 -0
  322. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_mention_warning.py +0 -0
  323. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_mentions.py +0 -0
  324. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_mesh_config.py +0 -0
  325. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_mesh_readiness.py +0 -0
  326. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_message.py +0 -0
  327. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_message_buffer.py +0 -0
  328. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_messaging.py +0 -0
  329. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_migrate_cli.py +0 -0
  330. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_modes.py +0 -0
  331. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_overview_cli.py +0 -0
  332. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_overview_collector.py +0 -0
  333. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_overview_model.py +0 -0
  334. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_overview_renderer.py +0 -0
  335. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_overview_web.py +0 -0
  336. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_persistence.py +0 -0
  337. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_pidfile.py +0 -0
  338. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_poll_loop.py +0 -0
  339. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_register_cli.py +0 -0
  340. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_room_persistence.py +0 -0
  341. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_rooms.py +0 -0
  342. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_rooms_federation.py +0 -0
  343. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_rooms_integration.py +0 -0
  344. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_server_icon_skill.py +0 -0
  345. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_setup_update_cli.py +0 -0
  346. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_skill_client.py +0 -0
  347. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_skill_docs.py +0 -0
  348. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_skills.py +0 -0
  349. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_socket_server.py +0 -0
  350. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_supervisor.py +0 -0
  351. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_template_engine.py +0 -0
  352. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_thread_buffer.py +0 -0
  353. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_threads.py +0 -0
  354. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_virtual_client.py +0 -0
  355. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_wait_for_port.py +0 -0
  356. {agentirc_cli-6.2.1 → agentirc_cli-6.2.3}/tests/test_webhook.py +0 -0
@@ -0,0 +1,154 @@
1
+ ---
2
+ name: doc-test-alignment
3
+ description: Audits a staged/branch diff for new public API surface (exceptions, classes, public functions, CLI commands, IRC protocol verbs, backend config fields) and reports whether `docs/` and `protocol/extensions/` mention them. Use at the end of a plan, before the first push, or when the user says "doc audit", "doc-test alignment", "check docs coverage".
4
+ tools: Read, Grep, Glob, Bash
5
+ model: sonnet
6
+ color: cyan
7
+ ---
8
+
9
+ # Doc-Test Alignment Auditor
10
+
11
+ You audit a code change for **doc drift**: new public API surface that
12
+ `docs/` doesn't mention. Run at the end of a plan (after tests pass, before
13
+ push). Your job is to surface omissions, not to write docs.
14
+
15
+ ## Step 1 — Determine the diff
16
+
17
+ If the caller passed a specific ref or file list, use that. Otherwise, audit
18
+ the current branch against `main`:
19
+
20
+ ```bash
21
+ git diff --name-only main...HEAD
22
+ git diff main...HEAD -- '*.py' 'culture/cli/*' 'culture/agentirc/*'
23
+ ```
24
+
25
+ If the branch has no commits ahead of `main`, fall back to staged changes:
26
+
27
+ ```bash
28
+ git diff --cached --name-only
29
+ git diff --cached -- '*.py'
30
+ ```
31
+
32
+ If neither yields a diff, report "no changes to audit" and stop.
33
+
34
+ ## Step 2 — Extract new public API surface
35
+
36
+ Run the patterns via the **Grep tool** (ripgrep / Rust regex engine — the
37
+ Claude Code default). Patterns below are written in the Rust regex flavor
38
+ that ripgrep accepts without any extra flags: `\s`, `(?:...)`, and character
39
+ classes all work as written. Do **not** pipe these into POSIX `grep` / BRE
40
+ — they will silently fail to match.
41
+
42
+ Save the diff first, then search it:
43
+
44
+ ```bash
45
+ git diff main...HEAD -- '*.py' 'culture/cli/*' 'culture/agentirc/*' > /tmp/branch-diff.patch
46
+ ```
47
+
48
+ Then feed `/tmp/branch-diff.patch` to the Grep tool with each pattern below.
49
+
50
+ | Surface type | Ripgrep pattern |
51
+ |--------------|-----------------|
52
+ | New exception class | `^\+class [A-Z][A-Za-z0-9_]*(\(.*(?:Error\|Exception).*\))?:` |
53
+ | New public class | `^\+class [A-Z][A-Za-z0-9_]*` (exclude leading `_`) |
54
+ | New public function | `^\+(async )?def [a-z][a-z0-9_]*\(` (exclude leading `_`) |
55
+ | New CLI command | `^\+.*add_parser\(['"]` in `culture/cli/` |
56
+ | New IRC verb | `^\+.*(_msg_handlers\[['"]\|commands\s*=).*['"][A-Z0-9]+['"]` |
57
+ | New config field | `^\+.*@dataclass` or `^\+\s+[a-z_]+:\s+[A-Z].*=` in config modules |
58
+
59
+ Ignore private symbols (leading `_`), test helpers (inside `tests/`), and
60
+ internal-only classes (docstring contains "internal" or "private").
61
+
62
+ List every match with:
63
+
64
+ - symbol name
65
+ - file path + starting line
66
+ - kind (exception / class / function / CLI command / IRC verb / config field)
67
+
68
+ ## Step 3 — Check doc coverage
69
+
70
+ For each extracted symbol, grep the documentation tree for a mention:
71
+
72
+ ```bash
73
+ grep -r -l '<symbol>' docs/ protocol/extensions/ 2>/dev/null
74
+ ```
75
+
76
+ Match rules:
77
+
78
+ - **Exception**: must be referenced in at least one `docs/**/*.md` page. If
79
+ it's a new public exception raised across module boundaries, that's a
80
+ stronger obligation — flag loudly.
81
+ - **CLI command**: must appear in `docs/` (user-facing command reference)
82
+ AND in the command's own module docstring.
83
+ - **IRC verb / protocol extension**: MUST have a page under
84
+ `protocol/extensions/` (per the repository-root `CLAUDE.md`: "Extensions
85
+ use new verbs (never redefine existing commands), documented in
86
+ `protocol/extensions/`").
87
+ - **Config field**: must appear in the relevant backend's `culture.yaml`
88
+ reference under `packages/agent-harness/culture.yaml` or
89
+ `docs/configuration.md`.
90
+ - **Public function/class**: soft obligation — flag if the symbol is
91
+ imported from outside its module, skip if it's used only within its
92
+ package.
93
+
94
+ ## Step 4 — Check test coverage (light)
95
+
96
+ For each public surface symbol, grep `tests/` for at least one reference:
97
+
98
+ ```bash
99
+ grep -r -l '<symbol>' tests/ 2>/dev/null
100
+ ```
101
+
102
+ If zero test files reference the symbol, flag it. This is a weak signal —
103
+ the caller may have tested behavior without naming the symbol directly.
104
+ Report as "likely untested" rather than "untested".
105
+
106
+ ## Step 5 — All-backends check (culture-specific)
107
+
108
+ If the diff touches `culture/clients/<backend>/` OR `packages/agent-harness/`,
109
+ list the other backends (`claude`, `codex`, `copilot`, `acp`) and report
110
+ whether the same change appears in each. This enforces the project's
111
+ "all-backends rule" (per the repository-root `CLAUDE.md`). A change in
112
+ only one backend is a bug.
113
+
114
+ ## Step 6 — Report
115
+
116
+ Output a concise table. Factual only: what symbols, what's missing,
117
+ what's drifted. Do **not** write prose narration and do **not** suggest
118
+ what the docs should say — leave doc wording to the caller. A brief
119
+ counts-only summary line at the end is fine; skip it if everything is
120
+ clean.
121
+
122
+ ```text
123
+ ## Doc-Test Alignment — <branch> vs main
124
+
125
+ ### New public API surface
126
+ | Symbol | Kind | Location | Docs | Tests |
127
+ |--------|------|----------|------|-------|
128
+ | ConsoleConnectionLost | exception | culture/console/client.py:42 | MISSING | test_console_client.py |
129
+ | _handle_reconnect | private | culture/console/app.py:412 | (skip — private) | — |
130
+
131
+ ### Protocol extensions
132
+ (none in this diff)
133
+
134
+ ### All-backends drift
135
+ Change touches `packages/agent-harness/irc_transport.py`:
136
+ - claude: ✓ updated
137
+ - codex: ✓ updated
138
+ - copilot: ✗ NOT updated (divergent)
139
+ - acp: ✗ NOT updated (divergent)
140
+
141
+ ### Summary
142
+ Doc gaps: 1 · Likely untested: 0 · Backend drift: 2
143
+ ```
144
+
145
+ If nothing is missing, output one line: `Doc-test alignment clean — no gaps detected.`
146
+
147
+ ## What NOT to do
148
+
149
+ - Do not write documentation. Report gaps only.
150
+ - Do not modify code or tests.
151
+ - Do not fail loudly on borderline cases (internal-only helpers, test
152
+ utilities) — the caller is smarter than the heuristics; your value is in
153
+ catching the obvious omissions.
154
+ - Do not re-run the test suite — `/run-tests` is the tool for that.
@@ -171,7 +171,15 @@ bash ~/.claude/skills/pr-review/scripts/pr-reply.sh --resolve <PR_NUMBER> <COMME
171
171
  - Always use `--resolve` to resolve the thread after replying
172
172
  - Every comment must get a reply — no silent fixes
173
173
 
174
- ## Step 9 — Wait for merge
174
+ ## Step 9 — Check SonarCloud before declaring ready
175
+
176
+ After CI is green and all inline threads are resolved, query SonarCloud
177
+ for the branch via the `/sonarclaude` skill. SonarCloud findings do not
178
+ always arrive as inline PR comments — a fully-resolved thread list plus an
179
+ all-green `gh pr checks` is **not** sufficient evidence that the PR is
180
+ clean. If `/sonarclaude` surfaces new findings, loop back to Step 7.
181
+
182
+ ## Step 10 — Wait for merge
175
183
 
176
184
  **Never merge the PR yourself.** The PR is merged manually on the GitHub site.
177
185
 
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  Format follows [Keep a Changelog](https://keepachangelog.com/).
6
6
 
7
+ ## [6.2.3] - 2026-04-15
8
+
9
+
10
+ ### Changed
11
+
12
+ - docs: post-#231 retrospective — CLAUDE.md guidance for pre-branch checklist, format-before-commit, pre-push code review, SonarCloud pre-ready; new doc-test-alignment subagent; /pr-review skill step for SonarCloud query
13
+
14
+ ## [6.2.2] - 2026-04-14
15
+
16
+
17
+ ### Fixed
18
+
19
+ - console: handle BrokenPipeError/ConnectionResetError in _send_raw; surface a red system notice in the chat panel instead of letting the asyncio task crash (#230)
20
+
7
21
  ## [6.2.1] - 2026-04-13
8
22
 
9
23
 
@@ -49,13 +49,18 @@ Each backend has its own `culture.yaml` in `culture/clients/<backend>/`.
49
49
 
50
50
  When implementing features, write a corresponding markdown doc in `docs/` describing the feature — its purpose, usage, and any protocol details. Keep `docs/` as the living reference for the project.
51
51
 
52
+ Before the first push on a branch that adds public API surface (new exceptions, CLI commands, IRC verbs, backend config fields), invoke the `doc-test-alignment` subagent to surface doc gaps: `Agent(subagent_type="doc-test-alignment", ...)`. It reads the branch diff and reports missing `docs/` coverage, missing protocol extension pages, and all-backends drift — it does not write docs, only flags omissions.
53
+
52
54
  ## Git Workflow
53
55
 
56
+ - **Before branching, run `git status`.** If `CHANGELOG.md`, any `CLAUDE.md`, or other files carry pre-existing unstaged changes on `main`, decide up front whether to stash, commit separately, or hand-split. `/version-bump` inserts a new section at the top of `CHANGELOG.md` and will interleave awkwardly with an existing `[Unreleased]` block if you don't.
54
57
  - Branch out for all changes
55
- - **Bump the version before creating a PR** — use `/version-bump patch` (bug fix), `minor` (new feature), or `major` (breaking change). This updates `pyproject.toml`, `culture/__init__.py`, and `CHANGELOG.md` in one step. Forgetting will fail the version-check CI job.
58
+ - **Bump the version before creating a PR** — use `/version-bump patch` (bug fix), `minor` (new feature), or `major` (breaking change). This updates `pyproject.toml` and `CHANGELOG.md` (and `uv.lock` when applicable) in one step. Forgetting will fail the version-check CI job.
59
+ - **Pre-push review for library/protocol code.** When the diff touches shared choke points (transport, `_send_raw`-style I/O, protocol parsers, anything in `packages/` or `culture/agentirc/`), invoke a code reviewer on the staged diff before the first push — typed exceptions and new error paths routinely create caller cleanup obligations that Qodo/human reviewers otherwise surface in the first review round. Use `Agent(subagent_type="superpowers:code-reviewer", ...)` or `/review-and-fix`.
56
60
  - Push to GitHub for agentic code review
57
61
  - Pull review comments, address feedback, push fixes
58
62
  - Reply to comments after pushing, resolve threads
63
+ - **Before declaring the PR ready**, check SonarCloud for the branch via the `/sonarclaude` skill. SonarCloud findings do not always arrive as inline PR comments, so an all-green `gh pr checks` + all-resolved threads is not sufficient.
59
64
 
60
65
  ## Testing
61
66
 
@@ -64,6 +69,10 @@ When implementing features, write a corresponding markdown doc in `docs/` descri
64
69
  - No mocks for the server — tests spin up real server instances on random ports with real TCP connections
65
70
  - Validate each layer with real IRC clients (weechat/irssi)
66
71
 
72
+ ## Format Before Commit
73
+
74
+ Pre-commit runs `black`, `isort`, `flake8`, `pylint`, `bandit`. `black`/`isort` failures reformat the file and reject the commit — you then have to `git add` the reformatted file and commit again. To avoid the re-commit loop, run `uv run black <files>` and `uv run isort <files>` on staged Python files **before** `git commit`.
75
+
67
76
  ## Nick Format
68
77
 
69
78
  `<server>-<agent>` (e.g., `thor-claude`, `spark-ori`). Globally unique by construction.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 6.2.1
3
+ Version: 6.2.3
4
4
  Summary: Legacy alias for culture — install culture instead
5
5
  Project-URL: Homepage, https://github.com/OriNachum/culture
6
6
  Author: Ori Nachum
@@ -13,7 +13,7 @@ from textual.containers import Horizontal
13
13
  from textual.widgets import Footer, Header
14
14
 
15
15
  from culture.aio import maybe_await
16
- from culture.console.client import ConsoleIRCClient
16
+ from culture.console.client import ConsoleConnectionLost, ConsoleIRCClient
17
17
  from culture.console.commands import CommandType, parse_command
18
18
  from culture.console.status import query_all_agents
19
19
  from culture.console.widgets.chat import ChatPanel
@@ -74,6 +74,10 @@ class ConsoleApp(App):
74
74
  self._background_tasks: set[asyncio.Task] = set()
75
75
  self._status_poll_task: asyncio.Task | None = None
76
76
 
77
+ # Once the connection drops, show the "connection lost" notice exactly
78
+ # once — subsequent failing commands/channel-switches stay quiet.
79
+ self._connection_lost_notified: bool = False
80
+
77
81
  # Dispatch table for command execution
78
82
  self._command_handlers: dict[CommandType, Any] = {
79
83
  CommandType.CHAT: self._handle_chat,
@@ -205,13 +209,29 @@ class ConsoleApp(App):
205
209
  async def _execute_command(self, cmd) -> None: # noqa: ANN001
206
210
  """Dispatch a ParsedCommand to the appropriate handler."""
207
211
  handler = self._command_handlers.get(cmd.type)
208
- if handler:
209
- await maybe_await(handler(cmd))
210
- elif cmd.type in (CommandType.START, CommandType.STOP, CommandType.RESTART):
211
- self._handle_agent_management(cmd)
212
- elif cmd.type == CommandType.UNKNOWN:
213
- chat: ChatPanel = self.query_one(ChatPanel)
214
- chat.add_message(time.time(), "", "system", f"[red]Unknown command: {cmd.text}[/]")
212
+ try:
213
+ if handler:
214
+ await maybe_await(handler(cmd))
215
+ elif cmd.type in (CommandType.START, CommandType.STOP, CommandType.RESTART):
216
+ self._handle_agent_management(cmd)
217
+ elif cmd.type == CommandType.UNKNOWN:
218
+ chat: ChatPanel = self.query_one(ChatPanel)
219
+ chat.add_message(time.time(), "", "system", f"[red]Unknown command: {cmd.text}[/]")
220
+ except ConsoleConnectionLost:
221
+ self._notify_connection_lost()
222
+
223
+ def _notify_connection_lost(self) -> None:
224
+ """Post the 'connection lost' notice once per disconnect."""
225
+ if self._connection_lost_notified:
226
+ return
227
+ self._connection_lost_notified = True
228
+ chat: ChatPanel = self.query_one(ChatPanel)
229
+ chat.add_message(
230
+ time.time(),
231
+ "",
232
+ "system",
233
+ "[red]Connection to server lost. Restart the console to reconnect.[/]",
234
+ )
215
235
 
216
236
  # ------------------------------------------------------------------
217
237
  # Command handlers
@@ -689,7 +709,11 @@ class ConsoleApp(App):
689
709
  pass
690
710
 
691
711
  # Fetch recent history
692
- entries = await self._client.history(channel, limit=20)
712
+ try:
713
+ entries = await self._client.history(channel, limit=20)
714
+ except ConsoleConnectionLost:
715
+ self._notify_connection_lost()
716
+ return
693
717
  # Stale check: if user switched away during fetch, discard results
694
718
  if self._current_channel != channel:
695
719
  return
@@ -24,6 +24,10 @@ QUERY_TIMEOUT = 10.0
24
24
  REGISTER_TIMEOUT = 15.0
25
25
 
26
26
 
27
+ class ConsoleConnectionLost(ConnectionError):
28
+ """Raised by ConsoleIRCClient when the underlying socket is broken mid-send."""
29
+
30
+
27
31
  @dataclass
28
32
  class ChatMessage:
29
33
  """A buffered chat message from a channel or DM."""
@@ -100,35 +104,49 @@ class ConsoleIRCClient:
100
104
  timeout=REGISTER_TIMEOUT,
101
105
  )
102
106
 
103
- await self._send_raw(f"NICK {self.nick}")
104
- await self._send_raw(f"USER {self.nick} 0 * :{self.nick}")
105
-
106
- # Wait for RPL_WELCOME (001) before proceeding
107
- welcome_future: asyncio.Future[Message] = asyncio.get_running_loop().create_future()
108
- self._pending["001"] = welcome_future
107
+ try:
108
+ await self._send_raw(f"NICK {self.nick}")
109
+ await self._send_raw(f"USER {self.nick} 0 * :{self.nick}")
109
110
 
110
- # Start the read loop so the future can be resolved
111
- self._read_task = asyncio.create_task(self._read_loop())
111
+ # Wait for RPL_WELCOME (001) before proceeding
112
+ welcome_future: asyncio.Future[Message] = asyncio.get_running_loop().create_future()
113
+ self._pending["001"] = welcome_future
112
114
 
113
- try:
114
- await asyncio.wait_for(welcome_future, timeout=REGISTER_TIMEOUT)
115
- except asyncio.TimeoutError:
116
- self._pending.clear()
117
- if self._read_task:
118
- self._read_task.cancel()
119
- if self._writer:
120
- self._writer.close()
121
- self._writer = None
122
- self._reader = None
123
- raise ConnectionError("Timed out waiting for server welcome (001)")
115
+ # Start the read loop so the future can be resolved
116
+ self._read_task = asyncio.create_task(self._read_loop())
124
117
 
125
- # Set user mode
126
- if self.mode:
127
- await self._send_raw(f"MODE {self.nick} +{self.mode}")
118
+ try:
119
+ await asyncio.wait_for(welcome_future, timeout=REGISTER_TIMEOUT)
120
+ except asyncio.TimeoutError as e:
121
+ raise ConnectionError("Timed out waiting for server welcome (001)") from e
122
+
123
+ # Set user mode
124
+ if self.mode:
125
+ await self._send_raw(f"MODE {self.nick} +{self.mode}")
126
+
127
+ # Send ICON if provided
128
+ if self.icon:
129
+ await self._send_raw(f"ICON {self.icon}")
130
+ except BaseException:
131
+ # Any failure after open_connection: tear down the half-open state.
132
+ await self._teardown_connection()
133
+ raise
128
134
 
129
- # Send ICON if provided
130
- if self.icon:
131
- await self._send_raw(f"ICON {self.icon}")
135
+ async def _teardown_connection(self) -> None:
136
+ """Close writer, cancel reader, clear pending futures. Idempotent."""
137
+ self._pending.clear()
138
+ if self._read_task:
139
+ self._read_task.cancel()
140
+ await asyncio.gather(self._read_task, return_exceptions=True)
141
+ self._read_task = None
142
+ if self._writer:
143
+ try:
144
+ self._writer.close()
145
+ await self._writer.wait_closed()
146
+ except OSError:
147
+ pass
148
+ self._writer = None
149
+ self._reader = None
132
150
 
133
151
  async def disconnect(self) -> None:
134
152
  """Send QUIT and close the connection."""
@@ -186,18 +204,24 @@ class ConsoleIRCClient:
186
204
  Returns a sorted list of channel names.
187
205
  """
188
206
  key = "LIST"
207
+ pending_key = "323"
189
208
  self._collect_buffers[key] = []
190
209
  end_future: asyncio.Future[None] = asyncio.get_running_loop().create_future()
191
- self._pending["323"] = end_future
210
+ self._pending[pending_key] = end_future
192
211
 
193
- await self._send_raw("LIST")
212
+ try:
213
+ await self._send_raw("LIST")
214
+ except ConsoleConnectionLost:
215
+ self._pending.pop(pending_key, None)
216
+ self._collect_buffers.pop(key, None)
217
+ raise
194
218
 
195
219
  try:
196
220
  await asyncio.wait_for(end_future, timeout=QUERY_TIMEOUT)
197
221
  except asyncio.TimeoutError:
198
222
  pass
199
223
  finally:
200
- self._pending.pop("323", None)
224
+ self._pending.pop(pending_key, None)
201
225
 
202
226
  channels = self._collect_buffers.pop(key, [])
203
227
  return sorted(channels)
@@ -208,18 +232,24 @@ class ConsoleIRCClient:
208
232
  Returns a list of dicts with nick, user, host, server, flags, realname.
209
233
  """
210
234
  key = f"WHO {target}"
235
+ pending_key = f"315:{target}"
211
236
  self._collect_buffers[key] = []
212
237
  end_future: asyncio.Future[None] = asyncio.get_running_loop().create_future()
213
- self._pending[f"315:{target}"] = end_future
238
+ self._pending[pending_key] = end_future
214
239
 
215
- await self._send_raw(f"WHO {target}")
240
+ try:
241
+ await self._send_raw(f"WHO {target}")
242
+ except ConsoleConnectionLost:
243
+ self._pending.pop(pending_key, None)
244
+ self._collect_buffers.pop(key, None)
245
+ raise
216
246
 
217
247
  try:
218
248
  await asyncio.wait_for(end_future, timeout=QUERY_TIMEOUT)
219
249
  except asyncio.TimeoutError:
220
250
  pass
221
251
  finally:
222
- self._pending.pop(f"315:{target}", None)
252
+ self._pending.pop(pending_key, None)
223
253
 
224
254
  entries = self._collect_buffers.pop(key, [])
225
255
  return entries
@@ -230,18 +260,24 @@ class ConsoleIRCClient:
230
260
  Returns a list of dicts with channel, nick, timestamp, text.
231
261
  """
232
262
  key = f"HISTORY {channel}"
263
+ pending_key = f"HISTORYEND:{channel}"
233
264
  self._collect_buffers[key] = []
234
265
  end_future: asyncio.Future[None] = asyncio.get_running_loop().create_future()
235
- self._pending[f"HISTORYEND:{channel}"] = end_future
266
+ self._pending[pending_key] = end_future
236
267
 
237
- await self._send_raw(f"HISTORY RECENT {channel} {limit}")
268
+ try:
269
+ await self._send_raw(f"HISTORY RECENT {channel} {limit}")
270
+ except ConsoleConnectionLost:
271
+ self._pending.pop(pending_key, None)
272
+ self._collect_buffers.pop(key, None)
273
+ raise
238
274
 
239
275
  try:
240
276
  await asyncio.wait_for(end_future, timeout=QUERY_TIMEOUT)
241
277
  except asyncio.TimeoutError:
242
278
  pass
243
279
  finally:
244
- self._pending.pop(f"HISTORYEND:{channel}", None)
280
+ self._pending.pop(pending_key, None)
245
281
 
246
282
  entries = self._collect_buffers.pop(key, [])
247
283
  return entries
@@ -252,9 +288,15 @@ class ConsoleIRCClient:
252
288
 
253
289
  async def _send_raw(self, line: str) -> None:
254
290
  """Write a raw IRC line to the socket."""
255
- if self._writer:
291
+ if not self._writer:
292
+ return
293
+ try:
256
294
  self._writer.write(f"{line}\r\n".encode())
257
295
  await self._writer.drain()
296
+ except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError) as e:
297
+ self.connected = False
298
+ logger.warning("ConsoleIRCClient: send failed (%s)", e.__class__.__name__)
299
+ raise ConsoleConnectionLost(str(e)) from e
258
300
 
259
301
  async def _read_loop(self) -> None:
260
302
  """Background task: read lines from socket and dispatch to _handle."""
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentirc-cli"
3
- version = "6.2.1"
3
+ version = "6.2.3"
4
4
  description = "Legacy alias for culture — install culture instead"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -10,7 +10,7 @@ import asyncio
10
10
 
11
11
  import pytest
12
12
 
13
- from culture.console.client import ChatMessage, ConsoleIRCClient
13
+ from culture.console.client import ChatMessage, ConsoleConnectionLost, ConsoleIRCClient
14
14
 
15
15
  # ---------------------------------------------------------------------------
16
16
  # Helpers
@@ -296,3 +296,101 @@ async def test_history_returns_messages(server, make_client):
296
296
  assert any(e.get("text") == "history test message" for e in entries)
297
297
 
298
298
  await client.disconnect()
299
+
300
+
301
+ # ---------------------------------------------------------------------------
302
+ # Broken-pipe handling (issue #230)
303
+ # ---------------------------------------------------------------------------
304
+
305
+
306
+ @pytest.mark.asyncio
307
+ async def test_send_raw_raises_console_connection_lost_when_socket_broken(server):
308
+ """Writes to a broken socket surface as ConsoleConnectionLost, not BrokenPipeError.
309
+
310
+ Reproduces issue #230: iTerm / idle disconnects caused asyncio's
311
+ `StreamWriter.drain()` to raise `BrokenPipeError` which was never caught,
312
+ crashing the console command task. The fix wraps the write in
313
+ `_send_raw` and re-raises a typed `ConsoleConnectionLost`.
314
+ """
315
+ nick = "testserv-pipetest"
316
+ client = make_console_client(server, nick=nick)
317
+ await client.connect()
318
+ assert client.connected is True
319
+
320
+ # Force-close the server side of this client's socket. The server
321
+ # retains the asyncio StreamWriter on its Client object; closing it
322
+ # sends FIN to the console client and the next drain() there fails.
323
+ server_client = server.clients[nick]
324
+ server_client.writer.close()
325
+ try:
326
+ await server_client.writer.wait_closed()
327
+ except OSError:
328
+ pass
329
+
330
+ # Repeated writes are required — the first drain() often succeeds because
331
+ # data lands in the local kernel buffer before the RST is observed.
332
+ with pytest.raises(ConsoleConnectionLost):
333
+ for _ in range(20):
334
+ await client.send_privmsg("#nowhere", "x" * 512)
335
+ await asyncio.sleep(0.02)
336
+
337
+ assert client.connected is False
338
+ await client.disconnect()
339
+
340
+
341
+ @pytest.mark.asyncio
342
+ async def test_history_cleans_up_pending_buffers_on_connection_lost(server):
343
+ """A failed history() send must not leak _pending / _collect_buffers entries."""
344
+ nick = "testserv-histleak"
345
+ client = make_console_client(server, nick=nick)
346
+ await client.connect()
347
+
348
+ # Break the socket from the server side.
349
+ server.clients[nick].writer.close()
350
+ try:
351
+ await server.clients[nick].writer.wait_closed()
352
+ except OSError:
353
+ pass
354
+
355
+ # Drain writes until ConsoleConnectionLost is raised by history().
356
+ raised = False
357
+ for _ in range(20):
358
+ try:
359
+ await client.history("#ghost", limit=5)
360
+ except ConsoleConnectionLost:
361
+ raised = True
362
+ break
363
+ await asyncio.sleep(0.02)
364
+
365
+ assert raised, "history() should eventually raise ConsoleConnectionLost"
366
+ # No stale state left behind — would otherwise hang future queries / leak memory.
367
+ assert "HISTORYEND:#ghost" not in client._pending
368
+ assert "HISTORY #ghost" not in client._collect_buffers
369
+ await client.disconnect()
370
+
371
+
372
+ @pytest.mark.asyncio
373
+ async def test_connect_cleans_up_on_registration_failure(monkeypatch, server):
374
+ """A ConsoleConnectionLost mid-registration must close the writer, not leak it."""
375
+ client = make_console_client(server, nick="testserv-leaktest")
376
+
377
+ # Simulate a server drop between open_connection and the first NICK write.
378
+ original_send_raw = client._send_raw
379
+ calls = {"n": 0}
380
+
381
+ async def failing_send_raw(line: str) -> None:
382
+ calls["n"] += 1
383
+ if calls["n"] == 1:
384
+ raise ConsoleConnectionLost("simulated drop during registration")
385
+ await original_send_raw(line)
386
+
387
+ monkeypatch.setattr(client, "_send_raw", failing_send_raw)
388
+
389
+ with pytest.raises(ConsoleConnectionLost):
390
+ await client.connect()
391
+
392
+ # After failure, no half-open socket or dangling state.
393
+ assert client._writer is None
394
+ assert client._reader is None
395
+ assert client._read_task is None
396
+ assert client._pending == {}
@@ -561,7 +561,7 @@ wheels = [
561
561
 
562
562
  [[package]]
563
563
  name = "culture"
564
- version = "6.2.1"
564
+ version = "6.2.3"
565
565
  source = { editable = "." }
566
566
  dependencies = [
567
567
  { name = "aiohttp" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes