agentirc-cli 4.3.5__tar.gz → 4.3.7__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 (318) hide show
  1. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/CHANGELOG.md +37 -0
  2. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/PKG-INFO +1 -1
  3. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/__init__.py +4 -1
  4. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/bots/bot_manager.py +7 -3
  5. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/bots/config.py +1 -0
  6. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/cli/__init__.py +5 -3
  7. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/cli/agent.py +26 -7
  8. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/cli/bot.py +5 -0
  9. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/cli/channel.py +30 -3
  10. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/cli/mesh.py +1 -2
  11. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/cli/server.py +53 -7
  12. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/cli/shared/constants.py +2 -1
  13. agentirc_cli-4.3.7/culture/cli/shared/formatting.py +5 -0
  14. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/cli/shared/ipc.py +3 -3
  15. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/acp/agent_runner.py +8 -4
  16. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/acp/config.py +5 -5
  17. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/acp/daemon.py +20 -14
  18. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/acp/irc_transport.py +5 -5
  19. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/acp/skill/irc_client.py +2 -2
  20. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/acp/socket_server.py +4 -4
  21. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/claude/agent_runner.py +2 -1
  22. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/claude/config.py +8 -8
  23. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/claude/daemon.py +20 -14
  24. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/claude/irc_transport.py +5 -5
  25. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/claude/skill/irc_client.py +2 -2
  26. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/claude/socket_server.py +4 -4
  27. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/codex/agent_runner.py +8 -4
  28. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/codex/config.py +5 -5
  29. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/codex/daemon.py +20 -14
  30. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/codex/irc_transport.py +5 -5
  31. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/codex/skill/irc_client.py +2 -2
  32. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/codex/socket_server.py +4 -4
  33. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/copilot/config.py +5 -5
  34. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/copilot/daemon.py +20 -14
  35. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/copilot/irc_transport.py +5 -5
  36. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/copilot/skill/irc_client.py +2 -2
  37. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/copilot/socket_server.py +4 -4
  38. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/console/client.py +1 -1
  39. agentirc_cli-4.3.7/culture/formatting.py +19 -0
  40. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/observer.py +10 -4
  41. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/overview/collector.py +18 -18
  42. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/overview/renderer_text.py +1 -13
  43. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/overview/web/style.css +1 -1
  44. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/pidfile.py +1 -1
  45. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/client.py +1 -1
  46. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/ircd.py +2 -2
  47. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/server_link.py +6 -6
  48. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/skill.py +3 -3
  49. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/packages/agent-harness/daemon.py +11 -10
  50. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/packages/agent-harness/irc_transport.py +5 -5
  51. agentirc_cli-4.3.7/packages/agent-harness/message_buffer.py +63 -0
  52. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/packages/agent-harness/skill/irc_client.py +2 -2
  53. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/packages/agent-harness/socket_server.py +4 -4
  54. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/pyproject.toml +1 -1
  55. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/conftest.py +8 -5
  56. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_acp_daemon.py +0 -6
  57. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_agent_runner.py +2 -1
  58. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_console_commands.py +0 -2
  59. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_console_connection.py +0 -4
  60. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_console_icons.py +1 -2
  61. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_federation.py +40 -34
  62. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_link_reconnect.py +16 -15
  63. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_mentions.py +3 -7
  64. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_rooms.py +5 -5
  65. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_supervisor.py +2 -1
  66. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_webhook.py +1 -1
  67. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/uv.lock +1 -1
  68. agentirc_cli-4.3.5/packages/agent-harness/message_buffer.py +0 -65
  69. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/.claude/skills/pr-review/SKILL.md +0 -0
  70. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/.claude/skills/run-tests/SKILL.md +0 -0
  71. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/.claude/skills/run-tests/scripts/test.sh +0 -0
  72. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/.flake8 +0 -0
  73. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/.github/workflows/pages.yml +0 -0
  74. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/.github/workflows/publish.yml +0 -0
  75. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/.github/workflows/security-checks.yml +0 -0
  76. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/.github/workflows/tests.yml +0 -0
  77. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/.gitignore +0 -0
  78. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/.markdownlint-cli2.yaml +0 -0
  79. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/.pr_agent.toml +0 -0
  80. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/.pre-commit-config.yaml +0 -0
  81. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/.pylintrc +0 -0
  82. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/CLAUDE.md +0 -0
  83. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/CNAME +0 -0
  84. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/Gemfile +0 -0
  85. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/Gemfile.lock +0 -0
  86. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/LICENSE +0 -0
  87. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/README.md +0 -0
  88. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/SECURITY.md +0 -0
  89. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/_config.yml +0 -0
  90. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/_sass/color_schemes/anthropic.scss +0 -0
  91. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/_sass/custom/custom.scss +0 -0
  92. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/__main__.py +0 -0
  93. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/aio.py +0 -0
  94. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/bots/__init__.py +0 -0
  95. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/bots/bot.py +0 -0
  96. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/bots/http_listener.py +0 -0
  97. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/bots/template_engine.py +0 -0
  98. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/bots/virtual_client.py +0 -0
  99. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/cli/shared/__init__.py +0 -0
  100. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/cli/shared/display.py +0 -0
  101. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/cli/shared/mesh.py +0 -0
  102. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/cli/shared/process.py +0 -0
  103. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/cli/skills.py +0 -0
  104. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/__init__.py +0 -0
  105. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/acp/__init__.py +0 -0
  106. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/acp/ipc.py +0 -0
  107. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/acp/message_buffer.py +0 -0
  108. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/acp/skill/SKILL.md +0 -0
  109. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/acp/skill/__init__.py +0 -0
  110. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/acp/supervisor.py +0 -0
  111. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/acp/webhook.py +0 -0
  112. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/claude/__init__.py +0 -0
  113. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/claude/__main__.py +0 -0
  114. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/claude/ipc.py +0 -0
  115. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/claude/message_buffer.py +0 -0
  116. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/claude/skill/SKILL.md +0 -0
  117. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/claude/skill/__init__.py +0 -0
  118. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/claude/supervisor.py +0 -0
  119. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/claude/webhook.py +0 -0
  120. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/codex/__init__.py +0 -0
  121. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/codex/ipc.py +0 -0
  122. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/codex/message_buffer.py +0 -0
  123. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/codex/skill/SKILL.md +0 -0
  124. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/codex/skill/__init__.py +0 -0
  125. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/codex/supervisor.py +0 -0
  126. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/codex/webhook.py +0 -0
  127. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/copilot/__init__.py +0 -0
  128. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/copilot/agent_runner.py +0 -0
  129. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/copilot/ipc.py +0 -0
  130. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/copilot/message_buffer.py +0 -0
  131. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/copilot/skill/SKILL.md +0 -0
  132. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/copilot/skill/__init__.py +0 -0
  133. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/copilot/supervisor.py +0 -0
  134. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/clients/copilot/webhook.py +0 -0
  135. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/console/__init__.py +0 -0
  136. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/console/app.py +0 -0
  137. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/console/commands.py +0 -0
  138. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/console/widgets/__init__.py +0 -0
  139. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/console/widgets/chat.py +0 -0
  140. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/console/widgets/info_panel.py +0 -0
  141. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/console/widgets/sidebar.py +0 -0
  142. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/credentials.py +0 -0
  143. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/learn_prompt.py +0 -0
  144. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/mesh_config.py +0 -0
  145. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/overview/__init__.py +0 -0
  146. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/overview/model.py +0 -0
  147. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/overview/renderer_web.py +0 -0
  148. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/persistence.py +0 -0
  149. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/protocol/__init__.py +0 -0
  150. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/protocol/commands.py +0 -0
  151. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/protocol/extensions/federation.md +0 -0
  152. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/protocol/extensions/history.md +0 -0
  153. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/protocol/extensions/icons.md +0 -0
  154. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/protocol/extensions/rooms.md +0 -0
  155. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/protocol/extensions/tags.md +0 -0
  156. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/protocol/extensions/threads.md +0 -0
  157. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/protocol/message.py +0 -0
  158. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/protocol/protocol-index.md +0 -0
  159. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/protocol/replies.py +0 -0
  160. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/__init__.py +0 -0
  161. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/__main__.py +0 -0
  162. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/channel.py +0 -0
  163. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/config.py +0 -0
  164. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/remote_client.py +0 -0
  165. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/room_store.py +0 -0
  166. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/rooms_util.py +0 -0
  167. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/skills/__init__.py +0 -0
  168. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/skills/history.py +0 -0
  169. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/skills/icon.py +0 -0
  170. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/skills/rooms.py +0 -0
  171. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/skills/threads.py +0 -0
  172. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/server/thread_store.py +0 -0
  173. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/culture/skills/culture/SKILL.md +0 -0
  174. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/agent-lifecycle.md +0 -0
  175. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/agentic-self-learn.md +0 -0
  176. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/architecture/agent-client.md +0 -0
  177. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/architecture/agent-harness-spec.md +0 -0
  178. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/architecture/design.md +0 -0
  179. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/architecture/harness-conformance.md +0 -0
  180. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/architecture/index.md +0 -0
  181. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/architecture/layer1-core-irc.md +0 -0
  182. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/architecture/layer2-attention.md +0 -0
  183. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/architecture/layer3-skills.md +0 -0
  184. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/architecture/layer4-federation.md +0 -0
  185. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/architecture/layer5-agent-harness.md +0 -0
  186. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/architecture/server-architecture.md +0 -0
  187. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/architecture/threads.md +0 -0
  188. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/channel-polling.md +0 -0
  189. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/acp/overview.md +0 -0
  190. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/claude/configuration.md +0 -0
  191. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/claude/context-management.md +0 -0
  192. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/claude/irc-tools.md +0 -0
  193. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/claude/overview.md +0 -0
  194. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/claude/setup.md +0 -0
  195. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/claude/supervisor.md +0 -0
  196. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/claude/webhooks.md +0 -0
  197. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/codex/configuration.md +0 -0
  198. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/codex/context-management.md +0 -0
  199. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/codex/irc-tools.md +0 -0
  200. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/codex/overview.md +0 -0
  201. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/codex/setup.md +0 -0
  202. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/codex/supervisor.md +0 -0
  203. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/codex/webhooks.md +0 -0
  204. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/copilot/configuration.md +0 -0
  205. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/copilot/context-management.md +0 -0
  206. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/copilot/irc-tools.md +0 -0
  207. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/copilot/overview.md +0 -0
  208. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/copilot/setup.md +0 -0
  209. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/copilot/supervisor.md +0 -0
  210. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/clients/copilot/webhooks.md +0 -0
  211. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/culture-cli.md +0 -0
  212. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/getting-started.md +0 -0
  213. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/index.md +0 -0
  214. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/operations/SECURITY.md +0 -0
  215. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/operations/bots.md +0 -0
  216. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/operations/ci.md +0 -0
  217. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/operations/cli.md +0 -0
  218. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/operations/docs-site.md +0 -0
  219. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/operations/index.md +0 -0
  220. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/operations/ops-tooling.md +0 -0
  221. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/operations/overview.md +0 -0
  222. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/operations/publishing.md +0 -0
  223. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  224. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/rooms.md +0 -0
  225. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/server-rename.md +0 -0
  226. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  227. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  228. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  229. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  230. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
  231. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
  232. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
  233. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
  234. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
  235. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  236. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  237. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  238. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  239. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
  240. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
  241. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
  242. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
  243. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
  244. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
  245. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
  246. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
  247. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
  248. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/use-cases/01-pair-programming.md +0 -0
  249. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/use-cases/02-code-review-ensemble.md +0 -0
  250. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/use-cases/03-cross-server-delegation.md +0 -0
  251. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/use-cases/04-knowledge-propagation.md +0 -0
  252. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/use-cases/05-the-observer.md +0 -0
  253. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/use-cases/06-cross-server-ops.md +0 -0
  254. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/use-cases/07-supervisor-intervention.md +0 -0
  255. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/use-cases/08-apps-as-agents.md +0 -0
  256. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/use-cases/09-research-swarm.md +0 -0
  257. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/use-cases/10-agent-lifecycle.md +0 -0
  258. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/use-cases-index.md +0 -0
  259. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/docs/what-is-culture.md +0 -0
  260. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/packages/agent-harness/README.md +0 -0
  261. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/packages/agent-harness/config.py +0 -0
  262. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/packages/agent-harness/ipc.py +0 -0
  263. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/packages/agent-harness/skill/SKILL.md +0 -0
  264. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/packages/agent-harness/webhook.py +0 -0
  265. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  266. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/plugins/claude-code/skills/culture/SKILL.md +0 -0
  267. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  268. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
  269. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/sonar-project.properties +0 -0
  270. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/__init__.py +0 -0
  271. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_archive.py +0 -0
  272. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_bot.py +0 -0
  273. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_bot_config.py +0 -0
  274. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_bot_manager.py +0 -0
  275. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_bots_integration.py +0 -0
  276. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_channel.py +0 -0
  277. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_codex_daemon.py +0 -0
  278. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_connection.py +0 -0
  279. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_console_client.py +0 -0
  280. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_console_integration.py +0 -0
  281. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_copilot_daemon.py +0 -0
  282. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_daemon.py +0 -0
  283. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_daemon_config.py +0 -0
  284. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_daemon_ipc.py +0 -0
  285. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_discovery.py +0 -0
  286. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_history.py +0 -0
  287. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_http_listener.py +0 -0
  288. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_integration_layer5.py +0 -0
  289. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_ipc.py +0 -0
  290. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_irc_transport.py +0 -0
  291. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_mention_alias.py +0 -0
  292. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_mention_target_cleanup.py +0 -0
  293. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_mesh_config.py +0 -0
  294. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_message.py +0 -0
  295. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_message_buffer.py +0 -0
  296. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_messaging.py +0 -0
  297. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_modes.py +0 -0
  298. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_overview_cli.py +0 -0
  299. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_overview_collector.py +0 -0
  300. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_overview_model.py +0 -0
  301. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_overview_renderer.py +0 -0
  302. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_overview_web.py +0 -0
  303. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_persistence.py +0 -0
  304. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_pidfile.py +0 -0
  305. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_poll_loop.py +0 -0
  306. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_room_persistence.py +0 -0
  307. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_rooms_federation.py +0 -0
  308. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_rooms_integration.py +0 -0
  309. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_server_icon_skill.py +0 -0
  310. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_setup_update_cli.py +0 -0
  311. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_skill_client.py +0 -0
  312. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_skills.py +0 -0
  313. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_socket_server.py +0 -0
  314. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_template_engine.py +0 -0
  315. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_thread_buffer.py +0 -0
  316. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_threads.py +0 -0
  317. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_virtual_client.py +0 -0
  318. {agentirc_cli-4.3.5 → agentirc_cli-4.3.7}/tests/test_wait_for_port.py +0 -0
@@ -4,6 +4,43 @@ 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
+ ## [4.3.7] - 2026-04-07
8
+
9
+
10
+ ### Fixed
11
+
12
+ - Extract duplicate string constants (S1192, #85)
13
+ - Remove redundant exception classes in except clauses (S5713, #86)
14
+ - Clean up unused variables and function parameters (S1481/S1172, #88)
15
+ - Remove f-strings without replacement fields (S3457, #89)
16
+ - Address hardcoded credential warnings with test constants (S2068, #90)
17
+ - Fix miscellaneous code quality issues: asyncio.timeout, nested ternaries, empty methods, CSS contrast (S7483/S3358/S1186/S7924, #91)
18
+
19
+ ## [4.3.6] - 2026-04-07
20
+
21
+
22
+ ### Changed
23
+
24
+ - CLI module docstring updated with current subcommand sets (#147)
25
+
26
+
27
+ ### Fixed
28
+
29
+ - agent message silently succeeds for nonexistent targets (#132)
30
+ - channel message silently succeeds for nonexistent channels (#133)
31
+ - agent sleep/wake error messages use wrong command names (#134)
32
+ - server subcommands ignore default server, hardcode culture (#135)
33
+ - agent start/stop inconsistent behavior with no nick argument (#137)
34
+ - channel message and bot create accept empty strings (#138)
35
+ - bot archive/unarchive missing --config flag (#139)
36
+ - inconsistent error message casing in agent archive vs unarchive (#140)
37
+ - channel commands show confusing timeout error when server is down (#141)
38
+ - uncaught PackageNotFoundError in version fallback (#142)
39
+ - culture --version flag not supported (#143)
40
+ - agent/channel message silently succeeds for nonexistent or empty targets (#144)
41
+ - channel read displays raw Unix timestamps instead of human-readable format (#145)
42
+ - server default accepts nonexistent server names without validation (#146)
43
+
7
44
  ## [4.3.5] - 2026-04-07
8
45
 
9
46
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 4.3.5
3
+ Version: 4.3.7
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
@@ -7,4 +7,7 @@ except PackageNotFoundError:
7
7
  try:
8
8
  __version__ = _v("agentirc-cli")
9
9
  except PackageNotFoundError:
10
- __version__ = _v("agentirc")
10
+ try:
11
+ __version__ = _v("agentirc")
12
+ except PackageNotFoundError:
13
+ __version__ = "0.0.0-dev"
@@ -6,15 +6,19 @@ import logging
6
6
  from typing import TYPE_CHECKING
7
7
 
8
8
  from culture.bots.bot import Bot
9
- from culture.bots.config import BOTS_DIR, BotConfig, load_bot_config, save_bot_config
9
+ from culture.bots.config import (
10
+ BOT_CONFIG_FILE,
11
+ BOTS_DIR,
12
+ BotConfig,
13
+ load_bot_config,
14
+ save_bot_config,
15
+ )
10
16
 
11
17
  if TYPE_CHECKING:
12
18
  from culture.server.ircd import IRCd
13
19
 
14
20
  logger = logging.getLogger(__name__)
15
21
 
16
- BOT_CONFIG_FILE = "bot.yaml"
17
-
18
22
 
19
23
  class BotManager:
20
24
  """Loads, starts, stops, and dispatches webhooks to bots."""
@@ -10,6 +10,7 @@ from pathlib import Path
10
10
  import yaml
11
11
 
12
12
  BOTS_DIR = Path(os.path.expanduser("~/.culture/bots"))
13
+ BOT_CONFIG_FILE = "bot.yaml"
13
14
 
14
15
 
15
16
  @dataclass
@@ -1,11 +1,11 @@
1
1
  """Unified CLI entry point for culture.
2
2
 
3
3
  Commands are organized into noun-based groups:
4
- culture agent {create,join,start,stop,status,rename,assign,sleep,wake,learn,message,read}
5
- culture server {start,stop,status,default,rename}
4
+ culture agent {create,join,start,stop,status,rename,assign,sleep,wake,learn,message,read,archive,unarchive,delete}
5
+ culture server {start,stop,status,default,rename,archive,unarchive}
6
6
  culture mesh {overview,setup,update,console}
7
7
  culture channel {list,read,message,who}
8
- culture bot {create,start,stop,list,inspect}
8
+ culture bot {create,start,stop,list,inspect,archive,unarchive}
9
9
  culture skills {install}
10
10
  """
11
11
 
@@ -15,6 +15,7 @@ import argparse
15
15
  import logging
16
16
  import sys
17
17
 
18
+ from culture import __version__
18
19
  from culture.cli import agent, bot, channel, mesh, server, skills
19
20
 
20
21
  GROUPS = [agent, server, mesh, channel, bot, skills]
@@ -25,6 +26,7 @@ def _build_parser() -> argparse.ArgumentParser:
25
26
  prog="culture",
26
27
  description="culture — AI agent IRC mesh",
27
28
  )
29
+ parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
28
30
  sub = parser.add_subparsers(dest="command")
29
31
  for group in GROUPS:
30
32
  group.register(sub)
@@ -399,7 +399,7 @@ def _probe_server_connection(host: str, port: int, server_name: str) -> None:
399
399
  try:
400
400
  with _socket.create_connection((host, port), timeout=2):
401
401
  pass
402
- except (ConnectionRefusedError, OSError):
402
+ except OSError:
403
403
  hint = ""
404
404
  server_pid = read_pid(f"server-{server_name}")
405
405
  if not server_pid or not is_process_alive(server_pid):
@@ -591,10 +591,17 @@ def _resolve_agents_to_stop(config, args) -> list:
591
591
  if len(config.agents) == 0:
592
592
  print(NO_AGENTS_MSG, file=sys.stderr)
593
593
  sys.exit(1)
594
+ # Multiple agents: try to match by current working directory
595
+ cwd_real = os.path.realpath(os.getcwd())
596
+ cwd_matches = [a for a in config.agents if os.path.realpath(a.directory) == cwd_real]
597
+ if len(cwd_matches) == 1:
598
+ return cwd_matches
594
599
  print(
595
600
  "Multiple agents configured. Specify a nick or use --all.",
596
601
  file=sys.stderr,
597
602
  )
603
+ for a in config.agents:
604
+ print(f" {a.nick}", file=sys.stderr)
598
605
  sys.exit(1)
599
606
 
600
607
 
@@ -763,13 +770,13 @@ def _cmd_assign(args: argparse.Namespace) -> None:
763
770
  # -----------------------------------------------------------------------
764
771
 
765
772
 
766
- def _resolve_ipc_targets(config, args, action_verb: str) -> list:
773
+ def _resolve_ipc_targets(config, args, command_name: str) -> list:
767
774
  """Resolve which agents to send IPC messages to."""
768
775
  if args.nick and args.all:
769
776
  print("Cannot specify both nick and --all", file=sys.stderr)
770
777
  sys.exit(1)
771
778
  if not args.nick and not args.all:
772
- print(f"Usage: culture agent {action_verb} <nick> or --all", file=sys.stderr)
779
+ print(f"Usage: culture agent {command_name} <nick> or --all", file=sys.stderr)
773
780
  sys.exit(1)
774
781
  if args.all:
775
782
  return config.agents
@@ -790,20 +797,22 @@ def _send_ipc(agent, msg_type: str, action_verb: str) -> None:
790
797
  print(f"{agent.nick}: failed (not running?)", file=sys.stderr)
791
798
 
792
799
 
793
- def _ipc_to_agents(args: argparse.Namespace, msg_type: str, action_verb: str) -> None:
800
+ def _ipc_to_agents(
801
+ args: argparse.Namespace, msg_type: str, action_verb: str, command_name: str
802
+ ) -> None:
794
803
  """Send an IPC message (pause/resume) to one or all agents."""
795
804
  config = load_config_or_default(args.config)
796
- targets = _resolve_ipc_targets(config, args, action_verb)
805
+ targets = _resolve_ipc_targets(config, args, command_name)
797
806
  for agent in targets:
798
807
  _send_ipc(agent, msg_type, action_verb)
799
808
 
800
809
 
801
810
  def _cmd_sleep(args: argparse.Namespace) -> None:
802
- _ipc_to_agents(args, "pause", "paused")
811
+ _ipc_to_agents(args, "pause", "paused", "sleep")
803
812
 
804
813
 
805
814
  def _cmd_wake(args: argparse.Namespace) -> None:
806
- _ipc_to_agents(args, "resume", "resumed")
815
+ _ipc_to_agents(args, "resume", "resumed", "wake")
807
816
 
808
817
 
809
818
  def _cmd_learn(args: argparse.Namespace) -> None:
@@ -838,6 +847,16 @@ def _cmd_learn(args: argparse.Namespace) -> None:
838
847
 
839
848
 
840
849
  def _cmd_message(args: argparse.Namespace) -> None:
850
+ if not args.target.strip():
851
+ print("Error: target nick cannot be empty", file=sys.stderr)
852
+ sys.exit(1)
853
+ if not args.text.strip():
854
+ print("Error: message text cannot be empty", file=sys.stderr)
855
+ sys.exit(1)
856
+ config = load_config_or_default(args.config)
857
+ if not config.get_agent(args.target):
858
+ print(f"Agent '{args.target}' not found in config", file=sys.stderr)
859
+ sys.exit(1)
841
860
  observer = get_observer(args.config)
842
861
  asyncio.run(observer.send_message(args.target, args.text))
843
862
  print(f"Sent to {args.target}")
@@ -49,9 +49,11 @@ def register(subparsers: argparse._SubParsersAction) -> None:
49
49
  bot_archive = bot_sub.add_parser("archive", help="Archive a bot")
50
50
  bot_archive.add_argument("name", help="Bot name to archive")
51
51
  bot_archive.add_argument("--reason", default="", help="Reason for archiving")
52
+ bot_archive.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
52
53
 
53
54
  bot_unarchive = bot_sub.add_parser("unarchive", help="Restore an archived bot")
54
55
  bot_unarchive.add_argument("name", help="Bot name to unarchive")
56
+ bot_unarchive.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
55
57
 
56
58
 
57
59
  def dispatch(args: argparse.Namespace) -> None:
@@ -87,6 +89,9 @@ def dispatch(args: argparse.Namespace) -> None:
87
89
  def _bot_create(args: argparse.Namespace) -> None:
88
90
  from culture.bots.config import BOTS_DIR, BotConfig, save_bot_config
89
91
 
92
+ if not args.name.strip():
93
+ print("Error: bot name cannot be empty", file=sys.stderr)
94
+ sys.exit(1)
90
95
  name = args.name
91
96
  config = load_config_or_default(args.config)
92
97
  server_name = config.server.name
@@ -50,11 +50,26 @@ def dispatch(args: argparse.Namespace) -> None:
50
50
  "who": _cmd_who,
51
51
  }
52
52
  handler = handlers.get(args.channel_command)
53
- if handler:
54
- handler(args)
55
- else:
53
+ if not handler:
56
54
  print(f"Unknown channel command: {args.channel_command}", file=sys.stderr)
57
55
  sys.exit(1)
56
+ try:
57
+ handler(args)
58
+ except (TimeoutError, OSError) as exc:
59
+ msg = str(exc)
60
+ if (
61
+ "Timed out" in msg
62
+ or "Connection refused" in msg
63
+ or "Connect call failed" in msg
64
+ or not msg # TimeoutError from asyncio often has empty message
65
+ ):
66
+ print(
67
+ "Error: cannot connect to IRC server. Is the server running?\n"
68
+ " Start it with: culture server start",
69
+ file=sys.stderr,
70
+ )
71
+ sys.exit(1)
72
+ raise
58
73
 
59
74
 
60
75
  # -----------------------------------------------------------------------
@@ -76,6 +91,9 @@ def _cmd_list(args: argparse.Namespace) -> None:
76
91
 
77
92
 
78
93
  def _cmd_read(args: argparse.Namespace) -> None:
94
+ if not args.target.strip():
95
+ print("Error: channel name cannot be empty", file=sys.stderr)
96
+ sys.exit(1)
79
97
  observer = get_observer(args.config)
80
98
  channel = args.target if args.target.startswith("#") else f"#{args.target}"
81
99
  messages = asyncio.run(observer.read_channel(channel, limit=args.limit))
@@ -89,6 +107,12 @@ def _cmd_read(args: argparse.Namespace) -> None:
89
107
 
90
108
 
91
109
  def _cmd_message(args: argparse.Namespace) -> None:
110
+ if not args.target.strip():
111
+ print("Error: channel name cannot be empty", file=sys.stderr)
112
+ sys.exit(1)
113
+ if not args.text.strip():
114
+ print("Error: message text cannot be empty", file=sys.stderr)
115
+ sys.exit(1)
92
116
  observer = get_observer(args.config)
93
117
  target = args.target if args.target.startswith("#") else f"#{args.target}"
94
118
  asyncio.run(observer.send_message(target, args.text))
@@ -96,6 +120,9 @@ def _cmd_message(args: argparse.Namespace) -> None:
96
120
 
97
121
 
98
122
  def _cmd_who(args: argparse.Namespace) -> None:
123
+ if not args.target.strip():
124
+ print("Error: channel name cannot be empty", file=sys.stderr)
125
+ sys.exit(1)
99
126
  observer = get_observer(args.config)
100
127
  target = args.target
101
128
  nicks = asyncio.run(observer.who(target))
@@ -15,7 +15,6 @@ from culture.clients.claude.config import (
15
15
  load_config,
16
16
  load_config_or_default,
17
17
  )
18
- from culture.pidfile import is_process_alive, read_pid
19
18
 
20
19
  from .shared.constants import AGENTS_YAML, CULTURE_DIR, DEFAULT_CONFIG
21
20
  from .shared.mesh import build_server_start_cmd, generate_mesh_from_agents
@@ -469,7 +468,7 @@ def _wait_for_server_port(port: int, retries: int = 50, interval: float = 0.1) -
469
468
  try:
470
469
  with _socket.create_connection(("localhost", port), timeout=1):
471
470
  return
472
- except (ConnectionRefusedError, OSError):
471
+ except OSError:
473
472
  time.sleep(interval)
474
473
 
475
474
 
@@ -1,4 +1,4 @@
1
- """Server subcommands: culture server {start,stop,status,default,rename}."""
1
+ """Server subcommands: culture server {start,stop,status,default,rename,archive,unarchive}."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -14,6 +14,7 @@ import time
14
14
  from culture.pidfile import (
15
15
  is_culture_process,
16
16
  is_process_alive,
17
+ read_default_server,
17
18
  read_pid,
18
19
  remove_pid,
19
20
  write_pid,
@@ -32,13 +33,22 @@ logger = logging.getLogger("culture")
32
33
 
33
34
  NAME = "server"
34
35
 
36
+ _DEFAULT_SERVER = "culture"
37
+
38
+
39
+ def _resolve_server_name(args: argparse.Namespace) -> str:
40
+ """Resolve the server name from args, default server file, or fallback."""
41
+ if args.name is not None:
42
+ return args.name
43
+ return read_default_server() or _DEFAULT_SERVER
44
+
35
45
 
36
46
  def register(subparsers: argparse._SubParsersAction) -> None:
37
47
  server_parser = subparsers.add_parser("server", help="Manage the IRC server")
38
48
  server_sub = server_parser.add_subparsers(dest="server_command")
39
49
 
40
50
  srv_start = server_sub.add_parser("start", help="Start the IRC server daemon")
41
- srv_start.add_argument("--name", default="culture", help=_SERVER_NAME_HELP)
51
+ srv_start.add_argument("--name", default=None, help=_SERVER_NAME_HELP)
42
52
  srv_start.add_argument("--host", default="0.0.0.0", help="Listen address")
43
53
  srv_start.add_argument("--port", type=int, default=6667, help="Listen port")
44
54
  srv_start.add_argument(
@@ -66,10 +76,10 @@ def register(subparsers: argparse._SubParsersAction) -> None:
66
76
  )
67
77
 
68
78
  srv_stop = server_sub.add_parser("stop", help="Stop the IRC server daemon")
69
- srv_stop.add_argument("--name", default="culture", help=_SERVER_NAME_HELP)
79
+ srv_stop.add_argument("--name", default=None, help=_SERVER_NAME_HELP)
70
80
 
71
81
  srv_status = server_sub.add_parser("status", help="Check server daemon status")
72
- srv_status.add_argument("--name", default="culture", help=_SERVER_NAME_HELP)
82
+ srv_status.add_argument("--name", default=None, help=_SERVER_NAME_HELP)
73
83
 
74
84
  srv_default = server_sub.add_parser("default", help="Set default server")
75
85
  srv_default.add_argument("name", help="Server name to set as default")
@@ -87,7 +97,7 @@ def register(subparsers: argparse._SubParsersAction) -> None:
87
97
  srv_archive = server_sub.add_parser(
88
98
  "archive", help="Archive the server and all its agents/bots"
89
99
  )
90
- srv_archive.add_argument("--name", default="culture", help=_SERVER_NAME_HELP)
100
+ srv_archive.add_argument("--name", default=None, help=_SERVER_NAME_HELP)
91
101
  srv_archive.add_argument("--reason", default="", help="Reason for archiving")
92
102
  srv_archive.add_argument(
93
103
  "--config",
@@ -98,7 +108,7 @@ def register(subparsers: argparse._SubParsersAction) -> None:
98
108
  srv_unarchive = server_sub.add_parser(
99
109
  "unarchive", help="Restore an archived server and all its agents/bots"
100
110
  )
101
- srv_unarchive.add_argument("--name", default="culture", help=_SERVER_NAME_HELP)
111
+ srv_unarchive.add_argument("--name", default=None, help=_SERVER_NAME_HELP)
102
112
  srv_unarchive.add_argument(
103
113
  "--config",
104
114
  default=DEFAULT_CONFIG,
@@ -114,6 +124,10 @@ def dispatch(args: argparse.Namespace) -> None:
114
124
  )
115
125
  sys.exit(1)
116
126
 
127
+ # Resolve --name for commands that use it (all except default/rename)
128
+ if args.server_command not in ("default", "rename") and hasattr(args, "name"):
129
+ args.name = _resolve_server_name(args)
130
+
117
131
  if args.server_command == "start":
118
132
  _server_start(args)
119
133
  elif args.server_command == "stop":
@@ -121,8 +135,40 @@ def dispatch(args: argparse.Namespace) -> None:
121
135
  elif args.server_command == "status":
122
136
  _server_status(args)
123
137
  elif args.server_command == "default":
124
- from culture.pidfile import write_default_server
138
+ from pathlib import Path
139
+
140
+ from culture.pidfile import PID_DIR, list_servers, write_default_server
125
141
 
142
+ from .shared.constants import DEFAULT_CONFIG
143
+
144
+ # Accept the name if: it matches a running server, has a PID file,
145
+ # or matches the configured server name.
146
+ known_running = {s["name"] for s in list_servers()}
147
+ pid_dir = Path(PID_DIR)
148
+ known_pids = set()
149
+ if pid_dir.exists():
150
+ prefix = "server-"
151
+ for p in pid_dir.glob(f"{prefix}*.pid"):
152
+ known_pids.add(p.stem[len(prefix) :])
153
+ known_names = known_running | known_pids
154
+
155
+ # Also accept the configured server name
156
+ try:
157
+ from culture.clients.claude.config import load_config_or_default
158
+
159
+ config = load_config_or_default(DEFAULT_CONFIG)
160
+ known_names.add(config.server.name)
161
+ except Exception:
162
+ pass
163
+
164
+ if args.name not in known_names:
165
+ print(f"Server '{args.name}' not found.", file=sys.stderr)
166
+ if known_names:
167
+ print(
168
+ f"Known servers: {', '.join(sorted(known_names))}",
169
+ file=sys.stderr,
170
+ )
171
+ sys.exit(1)
126
172
  write_default_server(args.name)
127
173
  print(f"Default server set to '{args.name}'")
128
174
  elif args.server_command == "rename":
@@ -2,6 +2,8 @@
2
2
 
3
3
  import os
4
4
 
5
+ from culture.bots.config import BOT_CONFIG_FILE # noqa: F401
6
+
5
7
  DEFAULT_CONFIG = os.path.expanduser("~/.culture/agents.yaml")
6
8
  LOG_DIR = os.path.expanduser("~/.culture/logs")
7
9
 
@@ -9,7 +11,6 @@ _CONFIG_HELP = "Config file path"
9
11
  _SERVER_NAME_HELP = "Server name"
10
12
  _BOT_NAME_HELP = "Bot name"
11
13
 
12
- BOT_CONFIG_FILE = "bot.yaml"
13
14
  DEFAULT_CHANNEL = "#general"
14
15
  NO_AGENTS_MSG = "No agents configured"
15
16
  CULTURE_DIR = ".culture"
@@ -0,0 +1,5 @@
1
+ """Re-export formatting utilities from culture.formatting."""
2
+
3
+ from culture.formatting import relative_time
4
+
5
+ __all__ = ["relative_time"]
@@ -24,7 +24,7 @@ async def ipc_request(socket_path: str, msg_type: str, **kwargs) -> dict | None:
24
24
  asyncio.open_unix_connection(socket_path),
25
25
  timeout=3.0,
26
26
  )
27
- except (ConnectionRefusedError, FileNotFoundError, OSError):
27
+ except OSError:
28
28
  return None
29
29
  try:
30
30
  req = make_request(msg_type, **kwargs)
@@ -40,13 +40,13 @@ async def ipc_request(socket_path: str, msg_type: str, **kwargs) -> dict | None:
40
40
  msg = decode_message(data)
41
41
  if msg and msg.get("type") == "response":
42
42
  return msg
43
- except (asyncio.TimeoutError, ConnectionError, BrokenPipeError, OSError):
43
+ except OSError:
44
44
  return None
45
45
  finally:
46
46
  writer.close()
47
47
  try:
48
48
  await writer.wait_closed()
49
- except (ConnectionError, BrokenPipeError, OSError):
49
+ except OSError:
50
50
  pass
51
51
 
52
52
 
@@ -151,7 +151,8 @@ class ACPAgentRunner:
151
151
  return
152
152
  try:
153
153
  self._process.terminate()
154
- await asyncio.wait_for(self._process.wait(), timeout=5)
154
+ async with asyncio.timeout(5):
155
+ await self._process.wait()
155
156
  except (asyncio.TimeoutError, ProcessLookupError):
156
157
  try:
157
158
  self._process.kill()
@@ -209,7 +210,8 @@ class ACPAgentRunner:
209
210
  await self._process.stdin.drain()
210
211
 
211
212
  try:
212
- return await asyncio.wait_for(future, timeout=timeout)
213
+ async with asyncio.timeout(timeout):
214
+ return await future
213
215
  except (asyncio.TimeoutError, asyncio.CancelledError):
214
216
  self._pending.pop(req_id, None)
215
217
  if not future.done():
@@ -244,14 +246,16 @@ class ACPAgentRunner:
244
246
  if not self._process:
245
247
  return -1
246
248
  try:
247
- return await asyncio.wait_for(self._process.wait(), timeout=5)
249
+ async with asyncio.timeout(5):
250
+ return await self._process.wait()
248
251
  except asyncio.TimeoutError:
249
252
  try:
250
253
  self._process.kill()
251
254
  except ProcessLookupError:
252
255
  pass
253
256
  try:
254
- return await asyncio.wait_for(self._process.wait(), timeout=1)
257
+ async with asyncio.timeout(1):
258
+ return await self._process.wait()
255
259
  except asyncio.TimeoutError:
256
260
  return -1
257
261
 
@@ -175,7 +175,7 @@ def add_agent_to_config(
175
175
  # Check for nick collision
176
176
  for existing in config.agents:
177
177
  if existing.nick == agent.nick:
178
- raise ValueError(f"agent with nick {agent.nick!r} already exists in config")
178
+ raise ValueError(f"Agent with nick {agent.nick!r} already exists in config")
179
179
 
180
180
  config.agents.append(agent)
181
181
  save_config(path, config)
@@ -238,7 +238,7 @@ def rename_agent(
238
238
  # Check new nick doesn't collide
239
239
  for agent in config.agents:
240
240
  if agent.nick == new_nick:
241
- raise ValueError(f"agent with nick {new_nick!r} already exists in config")
241
+ raise ValueError(f"Agent with nick {new_nick!r} already exists in config")
242
242
 
243
243
  # Find and rename
244
244
  for agent in config.agents:
@@ -247,7 +247,7 @@ def rename_agent(
247
247
  save_config(path, config)
248
248
  return
249
249
 
250
- raise ValueError(f"agent {old_nick!r} not found in config")
250
+ raise ValueError(f"Agent {old_nick!r} not found in config")
251
251
 
252
252
 
253
253
  def remove_agent(
@@ -262,7 +262,7 @@ def remove_agent(
262
262
  """
263
263
  path = Path(path)
264
264
  if not path.exists():
265
- raise ValueError(f"agent {nick!r} not found in config")
265
+ raise ValueError(f"Agent {nick!r} not found in config")
266
266
 
267
267
  with open(path) as f:
268
268
  raw = yaml.safe_load(f) or {}
@@ -274,4 +274,4 @@ def remove_agent(
274
274
  with open(path, "w") as f:
275
275
  yaml.dump(raw, f, default_flow_style=False, sort_keys=False)
276
276
  return
277
- raise ValueError(f"agent {nick!r} not found in config")
277
+ raise ValueError(f"Agent {nick!r} not found in config")
@@ -31,6 +31,8 @@ logger = logging.getLogger(__name__)
31
31
 
32
32
  # IPC validation error messages
33
33
  _ERR_MISSING_CHANNEL = "Missing 'channel'"
34
+ _ERR_MISSING_CHANNEL_THREAD = "Missing 'channel' or 'thread'"
35
+ _ERR_MISSING_CHANNEL_THREAD_MSG = "Missing 'channel', 'thread', or 'message'"
34
36
 
35
37
  MAX_CRASH_COUNT = 3
36
38
  CRASH_WINDOW_SECONDS = 300
@@ -304,7 +306,7 @@ class ACPDaemon:
304
306
  prompt = (
305
307
  f"[IRC Channel Poll: {channel}] Recent unread messages:\n"
306
308
  f"{lines}\n\n"
307
- f"Respond naturally if any messages need your attention."
309
+ "Respond naturally if any messages need your attention."
308
310
  )
309
311
  self._mention_targets.append(channel)
310
312
  task = asyncio.create_task(self._agent_runner.send_prompt(prompt))
@@ -485,10 +487,10 @@ class ACPDaemon:
485
487
  return self.agent.system_prompt
486
488
  return (
487
489
  f"You are {self.agent.nick}, an AI agent on the culture IRC network.\n"
488
- f"You have IRC tools available via the irc skill. Use them to communicate.\n"
490
+ "You have IRC tools available via the irc skill. Use them to communicate.\n"
489
491
  f"Your working directory is {self.agent.directory}.\n"
490
- f"Check IRC channels periodically with irc_read() for new messages.\n"
491
- f"When you finish a task, share results in the appropriate channel with irc_send()."
492
+ "Check IRC channels periodically with irc_read() for new messages.\n"
493
+ "When you finish a task, share results in the appropriate channel with irc_send()."
492
494
  )
493
495
 
494
496
  async def _record_crash_time(self, exit_code: int) -> None:
@@ -642,6 +644,13 @@ class ACPDaemon:
642
644
  if query and running and not self._paused:
643
645
  description = await self._query_agent_status()
644
646
 
647
+ if self._paused:
648
+ activity = "paused"
649
+ elif running:
650
+ activity = "working"
651
+ else:
652
+ activity = "idle"
653
+
645
654
  return make_response(
646
655
  req_id,
647
656
  ok=True,
@@ -650,7 +659,7 @@ class ACPDaemon:
650
659
  "paused": self._paused,
651
660
  "turn_count": turn_count,
652
661
  "last_activation": self._last_activation,
653
- "activity": "paused" if self._paused else ("working" if running else "idle"),
662
+ "activity": activity,
654
663
  "description": description,
655
664
  },
656
665
  )
@@ -687,7 +696,8 @@ class ACPDaemon:
687
696
  "[SYSTEM] Briefly describe what you are currently working on "
688
697
  "in one sentence. Reply with just the description, no preamble."
689
698
  )
690
- await asyncio.wait_for(self._status_query_event.wait(), timeout=10.0)
699
+ async with asyncio.timeout(10.0):
700
+ await self._status_query_event.wait()
691
701
  return self._truncate_first_line(self._status_query_response) or "nothing"
692
702
  except asyncio.TimeoutError:
693
703
  return "busy (no response)"
@@ -744,9 +754,7 @@ class ACPDaemon:
744
754
  thread_name = msg.get("thread", "")
745
755
  text = msg.get("message", "")
746
756
  if not channel or not thread_name or not text:
747
- return make_response(
748
- req_id, ok=False, error="Missing 'channel', 'thread', or 'message'"
749
- )
757
+ return make_response(req_id, ok=False, error=_ERR_MISSING_CHANNEL_THREAD_MSG)
750
758
  assert self._transport is not None
751
759
  await self._transport.send_thread_create(channel, thread_name, text)
752
760
  return make_response(req_id, ok=True)
@@ -756,9 +764,7 @@ class ACPDaemon:
756
764
  thread_name = msg.get("thread", "")
757
765
  text = msg.get("message", "")
758
766
  if not channel or not thread_name or not text:
759
- return make_response(
760
- req_id, ok=False, error="Missing 'channel', 'thread', or 'message'"
761
- )
767
+ return make_response(req_id, ok=False, error=_ERR_MISSING_CHANNEL_THREAD_MSG)
762
768
  assert self._transport is not None
763
769
  await self._transport.send_thread_reply(channel, thread_name, text)
764
770
  return make_response(req_id, ok=True)
@@ -776,7 +782,7 @@ class ACPDaemon:
776
782
  thread_name = msg.get("thread", "")
777
783
  summary = msg.get("summary", "")
778
784
  if not channel or not thread_name:
779
- return make_response(req_id, ok=False, error="Missing 'channel' or 'thread'")
785
+ return make_response(req_id, ok=False, error=_ERR_MISSING_CHANNEL_THREAD)
780
786
  assert self._transport is not None
781
787
  await self._transport.send_thread_close(channel, thread_name, summary)
782
788
  return make_response(req_id, ok=True)
@@ -786,7 +792,7 @@ class ACPDaemon:
786
792
  thread_name = msg.get("thread", "")
787
793
  limit = int(msg.get("limit", 50))
788
794
  if not channel or not thread_name:
789
- return make_response(req_id, ok=False, error="Missing 'channel' or 'thread'")
795
+ return make_response(req_id, ok=False, error=_ERR_MISSING_CHANNEL_THREAD)
790
796
  assert self._buffer is not None
791
797
  messages = self._buffer.read_thread(channel, thread_name, limit=limit)
792
798
  return make_response(