agentirc-cli 4.3.4__tar.gz → 4.3.6__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 (317) hide show
  1. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/CHANGELOG.md +32 -0
  2. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/PKG-INFO +1 -1
  3. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/__init__.py +4 -1
  4. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/cli/__init__.py +5 -3
  5. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/cli/agent.py +213 -141
  6. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/cli/bot.py +16 -5
  7. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/cli/channel.py +30 -3
  8. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/cli/mesh.py +84 -65
  9. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/cli/server.py +133 -47
  10. agentirc_cli-4.3.6/culture/cli/shared/formatting.py +5 -0
  11. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/acp/agent_runner.py +79 -67
  12. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/acp/config.py +5 -5
  13. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/acp/daemon.py +43 -36
  14. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/acp/irc_transport.py +15 -6
  15. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/acp/skill/irc_client.py +8 -5
  16. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/claude/config.py +8 -8
  17. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/claude/daemon.py +41 -34
  18. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/claude/irc_transport.py +15 -6
  19. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/codex/agent_runner.py +83 -70
  20. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/codex/config.py +5 -5
  21. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/codex/daemon.py +43 -36
  22. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/codex/irc_transport.py +15 -6
  23. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/codex/supervisor.py +13 -11
  24. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/copilot/agent_runner.py +14 -8
  25. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/copilot/config.py +5 -5
  26. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/copilot/daemon.py +43 -36
  27. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/copilot/irc_transport.py +15 -6
  28. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/copilot/skill/irc_client.py +8 -5
  29. agentirc_cli-4.3.6/culture/formatting.py +19 -0
  30. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/observer.py +29 -10
  31. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/overview/collector.py +27 -8
  32. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/overview/renderer_text.py +1 -13
  33. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/overview/renderer_web.py +100 -40
  34. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/persistence.py +130 -97
  35. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/server_link.py +92 -57
  36. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/skills/rooms.py +93 -61
  37. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/skills/threads.py +114 -97
  38. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/packages/agent-harness/daemon.py +35 -21
  39. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/pyproject.toml +1 -1
  40. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/uv.lock +1 -1
  41. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/.claude/skills/pr-review/SKILL.md +0 -0
  42. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/.claude/skills/run-tests/SKILL.md +0 -0
  43. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/.claude/skills/run-tests/scripts/test.sh +0 -0
  44. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/.flake8 +0 -0
  45. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/.github/workflows/pages.yml +0 -0
  46. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/.github/workflows/publish.yml +0 -0
  47. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/.github/workflows/security-checks.yml +0 -0
  48. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/.github/workflows/tests.yml +0 -0
  49. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/.gitignore +0 -0
  50. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/.markdownlint-cli2.yaml +0 -0
  51. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/.pr_agent.toml +0 -0
  52. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/.pre-commit-config.yaml +0 -0
  53. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/.pylintrc +0 -0
  54. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/CLAUDE.md +0 -0
  55. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/CNAME +0 -0
  56. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/Gemfile +0 -0
  57. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/Gemfile.lock +0 -0
  58. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/LICENSE +0 -0
  59. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/README.md +0 -0
  60. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/SECURITY.md +0 -0
  61. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/_config.yml +0 -0
  62. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/_sass/color_schemes/anthropic.scss +0 -0
  63. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/_sass/custom/custom.scss +0 -0
  64. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/__main__.py +0 -0
  65. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/aio.py +0 -0
  66. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/bots/__init__.py +0 -0
  67. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/bots/bot.py +0 -0
  68. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/bots/bot_manager.py +0 -0
  69. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/bots/config.py +0 -0
  70. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/bots/http_listener.py +0 -0
  71. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/bots/template_engine.py +0 -0
  72. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/bots/virtual_client.py +0 -0
  73. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/cli/shared/__init__.py +0 -0
  74. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/cli/shared/constants.py +0 -0
  75. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/cli/shared/display.py +0 -0
  76. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/cli/shared/ipc.py +0 -0
  77. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/cli/shared/mesh.py +0 -0
  78. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/cli/shared/process.py +0 -0
  79. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/cli/skills.py +0 -0
  80. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/__init__.py +0 -0
  81. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/acp/__init__.py +0 -0
  82. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/acp/ipc.py +0 -0
  83. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/acp/message_buffer.py +0 -0
  84. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/acp/skill/SKILL.md +0 -0
  85. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/acp/skill/__init__.py +0 -0
  86. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/acp/socket_server.py +0 -0
  87. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/acp/supervisor.py +0 -0
  88. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/acp/webhook.py +0 -0
  89. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/claude/__init__.py +0 -0
  90. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/claude/__main__.py +0 -0
  91. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/claude/agent_runner.py +0 -0
  92. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/claude/ipc.py +0 -0
  93. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/claude/message_buffer.py +0 -0
  94. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/claude/skill/SKILL.md +0 -0
  95. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/claude/skill/__init__.py +0 -0
  96. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/claude/skill/irc_client.py +0 -0
  97. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/claude/socket_server.py +0 -0
  98. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/claude/supervisor.py +0 -0
  99. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/claude/webhook.py +0 -0
  100. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/codex/__init__.py +0 -0
  101. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/codex/ipc.py +0 -0
  102. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/codex/message_buffer.py +0 -0
  103. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/codex/skill/SKILL.md +0 -0
  104. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/codex/skill/__init__.py +0 -0
  105. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/codex/skill/irc_client.py +0 -0
  106. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/codex/socket_server.py +0 -0
  107. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/codex/webhook.py +0 -0
  108. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/copilot/__init__.py +0 -0
  109. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/copilot/ipc.py +0 -0
  110. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/copilot/message_buffer.py +0 -0
  111. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/copilot/skill/SKILL.md +0 -0
  112. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/copilot/skill/__init__.py +0 -0
  113. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/copilot/socket_server.py +0 -0
  114. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/copilot/supervisor.py +0 -0
  115. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/clients/copilot/webhook.py +0 -0
  116. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/console/__init__.py +0 -0
  117. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/console/app.py +0 -0
  118. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/console/client.py +0 -0
  119. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/console/commands.py +0 -0
  120. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/console/widgets/__init__.py +0 -0
  121. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/console/widgets/chat.py +0 -0
  122. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/console/widgets/info_panel.py +0 -0
  123. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/console/widgets/sidebar.py +0 -0
  124. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/credentials.py +0 -0
  125. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/learn_prompt.py +0 -0
  126. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/mesh_config.py +0 -0
  127. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/overview/__init__.py +0 -0
  128. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/overview/model.py +0 -0
  129. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/overview/web/style.css +0 -0
  130. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/pidfile.py +0 -0
  131. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/protocol/__init__.py +0 -0
  132. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/protocol/commands.py +0 -0
  133. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/protocol/extensions/federation.md +0 -0
  134. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/protocol/extensions/history.md +0 -0
  135. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/protocol/extensions/icons.md +0 -0
  136. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/protocol/extensions/rooms.md +0 -0
  137. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/protocol/extensions/tags.md +0 -0
  138. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/protocol/extensions/threads.md +0 -0
  139. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/protocol/message.py +0 -0
  140. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/protocol/protocol-index.md +0 -0
  141. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/protocol/replies.py +0 -0
  142. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/__init__.py +0 -0
  143. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/__main__.py +0 -0
  144. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/channel.py +0 -0
  145. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/client.py +0 -0
  146. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/config.py +0 -0
  147. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/ircd.py +0 -0
  148. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/remote_client.py +0 -0
  149. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/room_store.py +0 -0
  150. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/rooms_util.py +0 -0
  151. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/skill.py +0 -0
  152. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/skills/__init__.py +0 -0
  153. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/skills/history.py +0 -0
  154. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/skills/icon.py +0 -0
  155. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/server/thread_store.py +0 -0
  156. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/culture/skills/culture/SKILL.md +0 -0
  157. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/agent-lifecycle.md +0 -0
  158. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/agentic-self-learn.md +0 -0
  159. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/architecture/agent-client.md +0 -0
  160. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/architecture/agent-harness-spec.md +0 -0
  161. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/architecture/design.md +0 -0
  162. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/architecture/harness-conformance.md +0 -0
  163. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/architecture/index.md +0 -0
  164. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/architecture/layer1-core-irc.md +0 -0
  165. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/architecture/layer2-attention.md +0 -0
  166. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/architecture/layer3-skills.md +0 -0
  167. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/architecture/layer4-federation.md +0 -0
  168. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/architecture/layer5-agent-harness.md +0 -0
  169. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/architecture/server-architecture.md +0 -0
  170. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/architecture/threads.md +0 -0
  171. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/channel-polling.md +0 -0
  172. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/acp/overview.md +0 -0
  173. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/claude/configuration.md +0 -0
  174. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/claude/context-management.md +0 -0
  175. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/claude/irc-tools.md +0 -0
  176. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/claude/overview.md +0 -0
  177. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/claude/setup.md +0 -0
  178. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/claude/supervisor.md +0 -0
  179. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/claude/webhooks.md +0 -0
  180. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/codex/configuration.md +0 -0
  181. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/codex/context-management.md +0 -0
  182. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/codex/irc-tools.md +0 -0
  183. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/codex/overview.md +0 -0
  184. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/codex/setup.md +0 -0
  185. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/codex/supervisor.md +0 -0
  186. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/codex/webhooks.md +0 -0
  187. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/copilot/configuration.md +0 -0
  188. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/copilot/context-management.md +0 -0
  189. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/copilot/irc-tools.md +0 -0
  190. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/copilot/overview.md +0 -0
  191. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/copilot/setup.md +0 -0
  192. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/copilot/supervisor.md +0 -0
  193. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/clients/copilot/webhooks.md +0 -0
  194. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/culture-cli.md +0 -0
  195. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/getting-started.md +0 -0
  196. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/index.md +0 -0
  197. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/operations/SECURITY.md +0 -0
  198. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/operations/bots.md +0 -0
  199. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/operations/ci.md +0 -0
  200. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/operations/cli.md +0 -0
  201. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/operations/docs-site.md +0 -0
  202. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/operations/index.md +0 -0
  203. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/operations/ops-tooling.md +0 -0
  204. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/operations/overview.md +0 -0
  205. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/operations/publishing.md +0 -0
  206. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  207. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/rooms.md +0 -0
  208. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/server-rename.md +0 -0
  209. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  210. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  211. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  212. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  213. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
  214. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
  215. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
  216. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
  217. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
  218. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  219. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  220. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  221. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  222. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
  223. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
  224. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
  225. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
  226. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
  227. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
  228. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
  229. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
  230. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
  231. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/use-cases/01-pair-programming.md +0 -0
  232. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/use-cases/02-code-review-ensemble.md +0 -0
  233. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/use-cases/03-cross-server-delegation.md +0 -0
  234. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/use-cases/04-knowledge-propagation.md +0 -0
  235. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/use-cases/05-the-observer.md +0 -0
  236. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/use-cases/06-cross-server-ops.md +0 -0
  237. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/use-cases/07-supervisor-intervention.md +0 -0
  238. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/use-cases/08-apps-as-agents.md +0 -0
  239. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/use-cases/09-research-swarm.md +0 -0
  240. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/use-cases/10-agent-lifecycle.md +0 -0
  241. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/use-cases-index.md +0 -0
  242. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/docs/what-is-culture.md +0 -0
  243. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/packages/agent-harness/README.md +0 -0
  244. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/packages/agent-harness/config.py +0 -0
  245. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/packages/agent-harness/ipc.py +0 -0
  246. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/packages/agent-harness/irc_transport.py +0 -0
  247. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/packages/agent-harness/message_buffer.py +0 -0
  248. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/packages/agent-harness/skill/SKILL.md +0 -0
  249. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/packages/agent-harness/skill/irc_client.py +0 -0
  250. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/packages/agent-harness/socket_server.py +0 -0
  251. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/packages/agent-harness/webhook.py +0 -0
  252. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  253. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/plugins/claude-code/skills/culture/SKILL.md +0 -0
  254. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  255. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
  256. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/sonar-project.properties +0 -0
  257. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/__init__.py +0 -0
  258. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/conftest.py +0 -0
  259. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_acp_daemon.py +0 -0
  260. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_agent_runner.py +0 -0
  261. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_archive.py +0 -0
  262. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_bot.py +0 -0
  263. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_bot_config.py +0 -0
  264. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_bot_manager.py +0 -0
  265. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_bots_integration.py +0 -0
  266. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_channel.py +0 -0
  267. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_codex_daemon.py +0 -0
  268. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_connection.py +0 -0
  269. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_console_client.py +0 -0
  270. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_console_commands.py +0 -0
  271. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_console_connection.py +0 -0
  272. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_console_icons.py +0 -0
  273. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_console_integration.py +0 -0
  274. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_copilot_daemon.py +0 -0
  275. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_daemon.py +0 -0
  276. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_daemon_config.py +0 -0
  277. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_daemon_ipc.py +0 -0
  278. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_discovery.py +0 -0
  279. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_federation.py +0 -0
  280. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_history.py +0 -0
  281. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_http_listener.py +0 -0
  282. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_integration_layer5.py +0 -0
  283. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_ipc.py +0 -0
  284. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_irc_transport.py +0 -0
  285. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_link_reconnect.py +0 -0
  286. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_mention_alias.py +0 -0
  287. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_mention_target_cleanup.py +0 -0
  288. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_mentions.py +0 -0
  289. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_mesh_config.py +0 -0
  290. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_message.py +0 -0
  291. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_message_buffer.py +0 -0
  292. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_messaging.py +0 -0
  293. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_modes.py +0 -0
  294. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_overview_cli.py +0 -0
  295. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_overview_collector.py +0 -0
  296. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_overview_model.py +0 -0
  297. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_overview_renderer.py +0 -0
  298. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_overview_web.py +0 -0
  299. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_persistence.py +0 -0
  300. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_pidfile.py +0 -0
  301. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_poll_loop.py +0 -0
  302. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_room_persistence.py +0 -0
  303. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_rooms.py +0 -0
  304. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_rooms_federation.py +0 -0
  305. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_rooms_integration.py +0 -0
  306. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_server_icon_skill.py +0 -0
  307. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_setup_update_cli.py +0 -0
  308. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_skill_client.py +0 -0
  309. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_skills.py +0 -0
  310. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_socket_server.py +0 -0
  311. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_supervisor.py +0 -0
  312. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_template_engine.py +0 -0
  313. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_thread_buffer.py +0 -0
  314. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_threads.py +0 -0
  315. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_virtual_client.py +0 -0
  316. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_wait_for_port.py +0 -0
  317. {agentirc_cli-4.3.4 → agentirc_cli-4.3.6}/tests/test_webhook.py +0 -0
@@ -4,6 +4,38 @@ 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.6] - 2026-04-07
8
+
9
+
10
+ ### Changed
11
+
12
+ - CLI module docstring updated with current subcommand sets (#147)
13
+
14
+
15
+ ### Fixed
16
+
17
+ - agent message silently succeeds for nonexistent targets (#132)
18
+ - channel message silently succeeds for nonexistent channels (#133)
19
+ - agent sleep/wake error messages use wrong command names (#134)
20
+ - server subcommands ignore default server, hardcode culture (#135)
21
+ - agent start/stop inconsistent behavior with no nick argument (#137)
22
+ - channel message and bot create accept empty strings (#138)
23
+ - bot archive/unarchive missing --config flag (#139)
24
+ - inconsistent error message casing in agent archive vs unarchive (#140)
25
+ - channel commands show confusing timeout error when server is down (#141)
26
+ - uncaught PackageNotFoundError in version fallback (#142)
27
+ - culture --version flag not supported (#143)
28
+ - agent/channel message silently succeeds for nonexistent or empty targets (#144)
29
+ - channel read displays raw Unix timestamps instead of human-readable format (#145)
30
+ - server default accepts nonexistent server names without validation (#146)
31
+
32
+ ## [4.3.5] - 2026-04-07
33
+
34
+
35
+ ### Changed
36
+
37
+ - Reduce cognitive complexity in 30+ functions across backend clients, server code, CLI submodules, and standalone files to meet SonarCloud threshold (≤15)
38
+
7
39
  ## [4.3.4] - 2026-04-07
8
40
 
9
41
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 4.3.4
3
+ Version: 4.3.6
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"
@@ -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)
@@ -201,59 +201,83 @@ def dispatch(args: argparse.Namespace) -> None:
201
201
  # -----------------------------------------------------------------------
202
202
 
203
203
 
204
- def _create_agent_config(args: argparse.Namespace, full_nick: str) -> AgentConfig:
205
- """Build a backend-specific AgentConfig from CLI args."""
206
- if args.agent == "codex":
207
- from culture.clients.codex.config import AgentConfig as CodexAgentConfig
208
-
209
- return CodexAgentConfig(
210
- nick=full_nick,
211
- agent="codex",
212
- directory=os.getcwd(),
213
- channels=[DEFAULT_CHANNEL],
214
- )
215
- if args.agent == "copilot":
216
- from culture.clients.copilot.config import AgentConfig as CopilotAgentConfig
217
-
218
- return CopilotAgentConfig(
219
- nick=full_nick,
220
- agent="copilot",
221
- directory=os.getcwd(),
222
- channels=[DEFAULT_CHANNEL],
223
- )
224
- if args.agent == "acp":
225
- import json as _json
204
+ def _create_codex_config(full_nick: str) -> AgentConfig:
205
+ """Build a CodexAgentConfig."""
206
+ from culture.clients.codex.config import AgentConfig as CodexAgentConfig
226
207
 
227
- from culture.clients.acp.config import AgentConfig as ACPAgentConfig
208
+ return CodexAgentConfig(
209
+ nick=full_nick,
210
+ agent="codex",
211
+ directory=os.getcwd(),
212
+ channels=[DEFAULT_CHANNEL],
213
+ )
228
214
 
229
- acp_cmd = ["opencode", "acp"]
230
- if args.acp_command:
231
- try:
232
- acp_cmd = _json.loads(args.acp_command)
233
- except _json.JSONDecodeError:
234
- acp_cmd = args.acp_command.split()
235
- if (
236
- not isinstance(acp_cmd, list)
237
- or not acp_cmd
238
- or not all(isinstance(s, str) for s in acp_cmd)
239
- ):
240
- print("Error: --acp-command must be a non-empty list of strings", file=sys.stderr)
241
- sys.exit(1)
242
- return ACPAgentConfig(
243
- nick=full_nick,
244
- agent="acp",
245
- acp_command=acp_cmd,
246
- directory=os.getcwd(),
247
- channels=[DEFAULT_CHANNEL],
248
- )
215
+
216
+ def _create_copilot_config(full_nick: str) -> AgentConfig:
217
+ """Build a CopilotAgentConfig."""
218
+ from culture.clients.copilot.config import AgentConfig as CopilotAgentConfig
219
+
220
+ return CopilotAgentConfig(
221
+ nick=full_nick,
222
+ agent="copilot",
223
+ directory=os.getcwd(),
224
+ channels=[DEFAULT_CHANNEL],
225
+ )
226
+
227
+
228
+ def _parse_acp_command(raw_command: str | None) -> list[str]:
229
+ """Parse and validate the ACP command from CLI args."""
230
+ import json as _json
231
+
232
+ acp_cmd = ["opencode", "acp"]
233
+ if raw_command:
234
+ try:
235
+ acp_cmd = _json.loads(raw_command)
236
+ except _json.JSONDecodeError:
237
+ acp_cmd = raw_command.split()
238
+ if not isinstance(acp_cmd, list) or not acp_cmd or not all(isinstance(s, str) for s in acp_cmd):
239
+ print("Error: --acp-command must be a non-empty list of strings", file=sys.stderr)
240
+ sys.exit(1)
241
+ return acp_cmd
242
+
243
+
244
+ def _create_acp_config(full_nick: str, args: argparse.Namespace) -> AgentConfig:
245
+ """Build an ACPAgentConfig."""
246
+ from culture.clients.acp.config import AgentConfig as ACPAgentConfig
247
+
248
+ acp_cmd = _parse_acp_command(args.acp_command)
249
+ return ACPAgentConfig(
250
+ nick=full_nick,
251
+ agent="acp",
252
+ acp_command=acp_cmd,
253
+ directory=os.getcwd(),
254
+ channels=[DEFAULT_CHANNEL],
255
+ )
256
+
257
+
258
+ def _create_default_config(full_nick: str, backend: str) -> AgentConfig:
259
+ """Build a default (claude) AgentConfig."""
249
260
  return AgentConfig(
250
261
  nick=full_nick,
251
- agent=args.agent,
262
+ agent=backend,
252
263
  directory=os.getcwd(),
253
264
  channels=[DEFAULT_CHANNEL],
254
265
  )
255
266
 
256
267
 
268
+ def _create_agent_config(args: argparse.Namespace, full_nick: str) -> AgentConfig:
269
+ """Build a backend-specific AgentConfig from CLI args."""
270
+ factories = {
271
+ "codex": lambda: _create_codex_config(full_nick),
272
+ "copilot": lambda: _create_copilot_config(full_nick),
273
+ "acp": lambda: _create_acp_config(full_nick, args),
274
+ }
275
+ factory = factories.get(args.agent)
276
+ if factory:
277
+ return factory()
278
+ return _create_default_config(full_nick, args.agent)
279
+
280
+
257
281
  def _cmd_create(args: argparse.Namespace) -> None:
258
282
  config = load_config_or_default(args.config)
259
283
 
@@ -387,6 +411,32 @@ def _probe_server_connection(host: str, port: int, server_name: str) -> None:
387
411
  sys.exit(1)
388
412
 
389
413
 
414
+ def _start_foreground(config: DaemonConfig, agents: list[AgentConfig]) -> None:
415
+ """Start a single agent in the foreground."""
416
+ if len(agents) != 1:
417
+ print("--foreground requires a single agent nick, not --all", file=sys.stderr)
418
+ sys.exit(1)
419
+ agent = agents[0]
420
+ print(f"Starting agent {agent.nick} in foreground...")
421
+ asyncio.run(_run_single_agent(config, agent))
422
+
423
+
424
+ def _start_background(config: DaemonConfig, agents: list[AgentConfig]) -> None:
425
+ """Start agents in background mode (fork on Unix, single on Windows)."""
426
+ if sys.platform == "win32":
427
+ if len(agents) != 1:
428
+ print(
429
+ "Multi-agent daemon mode not supported on Windows. Start agents individually.",
430
+ file=sys.stderr,
431
+ )
432
+ sys.exit(1)
433
+ agent = agents[0]
434
+ print(f"Starting agent {agent.nick}...")
435
+ asyncio.run(_run_single_agent(config, agent))
436
+ else:
437
+ _run_multi_agents(config, agents)
438
+
439
+
390
440
  def _cmd_start(args: argparse.Namespace) -> None:
391
441
  config = load_config(args.config)
392
442
 
@@ -395,86 +445,88 @@ def _cmd_start(args: argparse.Namespace) -> None:
395
445
  server_name = config.server.name
396
446
  _probe_server_connection(config.server.host, config.server.port, server_name)
397
447
 
398
- foreground = getattr(args, "foreground", False)
399
-
400
- if foreground:
401
- if len(agents) != 1:
402
- print("--foreground requires a single agent nick, not --all", file=sys.stderr)
403
- sys.exit(1)
404
- agent = agents[0]
405
- print(f"Starting agent {agent.nick} in foreground...")
406
- asyncio.run(_run_single_agent(config, agent))
448
+ if getattr(args, "foreground", False):
449
+ _start_foreground(config, agents)
407
450
  else:
408
- if sys.platform == "win32":
409
- if len(agents) == 1:
410
- agent = agents[0]
411
- print(f"Starting agent {agent.nick}...")
412
- asyncio.run(_run_single_agent(config, agent))
413
- else:
414
- print(
415
- "Multi-agent daemon mode not supported on Windows. Start agents individually.",
416
- file=sys.stderr,
417
- )
418
- sys.exit(1)
419
- else:
420
- _run_multi_agents(config, agents)
451
+ _start_background(config, agents)
452
+
453
+
454
+ def _make_backend_config(config: DaemonConfig, backend_daemon_config_cls):
455
+ """Build a backend-specific DaemonConfig from the base config."""
456
+ return backend_daemon_config_cls(
457
+ server=config.server,
458
+ webhooks=config.webhooks,
459
+ buffer_size=config.buffer_size,
460
+ agents=config.agents,
461
+ )
462
+
463
+
464
+ def _create_codex_daemon(config: DaemonConfig, agent: AgentConfig):
465
+ """Create a Codex backend daemon."""
466
+ from culture.clients.codex.config import DaemonConfig as CodexDaemonConfig
467
+ from culture.clients.codex.daemon import CodexDaemon
468
+
469
+ return CodexDaemon(_make_backend_config(config, CodexDaemonConfig), agent)
470
+
471
+
472
+ def _coerce_to_acp_agent(agent: AgentConfig):
473
+ """Ensure agent is an ACPAgentConfig, converting if necessary."""
474
+ from culture.clients.acp.config import AgentConfig as ACPAgentConfig
475
+
476
+ if isinstance(agent, ACPAgentConfig):
477
+ return agent
478
+ return ACPAgentConfig(
479
+ nick=agent.nick,
480
+ agent="acp",
481
+ acp_command=getattr(agent, "acp_command", None) or ["opencode", "acp"],
482
+ directory=agent.directory,
483
+ channels=agent.channels,
484
+ model=agent.model,
485
+ system_prompt=agent.system_prompt,
486
+ tags=agent.tags,
487
+ )
488
+
489
+
490
+ def _create_acp_daemon(config: DaemonConfig, agent: AgentConfig):
491
+ """Create an ACP backend daemon."""
492
+ from culture.clients.acp.config import DaemonConfig as ACPDaemonConfig
493
+ from culture.clients.acp.daemon import ACPDaemon
494
+
495
+ return ACPDaemon(
496
+ _make_backend_config(config, ACPDaemonConfig),
497
+ _coerce_to_acp_agent(agent),
498
+ )
499
+
500
+
501
+ def _create_copilot_daemon(config: DaemonConfig, agent: AgentConfig):
502
+ """Create a Copilot backend daemon."""
503
+ from culture.clients.copilot.config import DaemonConfig as CopilotDaemonConfig
504
+ from culture.clients.copilot.daemon import CopilotDaemon
505
+
506
+ return CopilotDaemon(_make_backend_config(config, CopilotDaemonConfig), agent)
507
+
508
+
509
+ def _create_claude_daemon(config: DaemonConfig, agent: AgentConfig):
510
+ """Create the default Claude backend daemon."""
511
+ from culture.clients.claude.daemon import AgentDaemon
512
+
513
+ return AgentDaemon(config, agent)
514
+
515
+
516
+ _BACKEND_DAEMON_FACTORIES = {
517
+ "codex": _create_codex_daemon,
518
+ "acp": _create_acp_daemon,
519
+ "opencode": _create_acp_daemon,
520
+ "copilot": _create_copilot_daemon,
521
+ }
421
522
 
422
523
 
423
524
  async def _run_single_agent(config: DaemonConfig, agent: AgentConfig) -> None:
424
525
  """Run a single agent daemon in the foreground."""
425
526
  backend = getattr(agent, "agent", "claude")
426
527
 
427
- if backend == "codex":
428
- from culture.clients.codex.config import DaemonConfig as CodexDaemonConfig
429
- from culture.clients.codex.daemon import CodexDaemon
430
-
431
- codex_config = CodexDaemonConfig(
432
- server=config.server,
433
- webhooks=config.webhooks,
434
- buffer_size=config.buffer_size,
435
- agents=config.agents,
436
- )
437
- daemon = CodexDaemon(codex_config, agent)
438
- elif backend in ("acp", "opencode"):
439
- from culture.clients.acp.config import AgentConfig as ACPAgentConfig
440
- from culture.clients.acp.config import DaemonConfig as ACPDaemonConfig
441
- from culture.clients.acp.daemon import ACPDaemon
442
-
443
- acp_config = ACPDaemonConfig(
444
- server=config.server,
445
- webhooks=config.webhooks,
446
- buffer_size=config.buffer_size,
447
- agents=config.agents,
448
- )
449
- if not isinstance(agent, ACPAgentConfig):
450
- acp_agent = ACPAgentConfig(
451
- nick=agent.nick,
452
- agent="acp",
453
- acp_command=getattr(agent, "acp_command", None) or ["opencode", "acp"],
454
- directory=agent.directory,
455
- channels=agent.channels,
456
- model=agent.model,
457
- system_prompt=agent.system_prompt,
458
- tags=agent.tags,
459
- )
460
- else:
461
- acp_agent = agent
462
- daemon = ACPDaemon(acp_config, acp_agent)
463
- elif backend == "copilot":
464
- from culture.clients.copilot.config import DaemonConfig as CopilotDaemonConfig
465
- from culture.clients.copilot.daemon import CopilotDaemon
466
-
467
- copilot_config = CopilotDaemonConfig(
468
- server=config.server,
469
- webhooks=config.webhooks,
470
- buffer_size=config.buffer_size,
471
- agents=config.agents,
472
- )
473
- daemon = CopilotDaemon(copilot_config, agent)
474
- else:
475
- from culture.clients.claude.daemon import AgentDaemon
476
-
477
- daemon = AgentDaemon(config, agent)
528
+ factory = _BACKEND_DAEMON_FACTORIES.get(backend, _create_claude_daemon)
529
+ daemon = factory(config, agent)
478
530
 
479
531
  stop_event = asyncio.Event()
480
532
  daemon.set_stop_event(stop_event)
@@ -524,30 +576,38 @@ def _run_multi_agents(config: DaemonConfig, agents: list[AgentConfig]) -> None:
524
576
  print(f"Started {agent.nick} (PID {pid})")
525
577
 
526
578
 
527
- def _cmd_stop(args: argparse.Namespace) -> None:
528
- config = load_config_or_default(args.config)
529
-
579
+ def _resolve_agents_to_stop(config, args) -> list:
580
+ """Resolve which agents should be stopped, or exit with an error."""
530
581
  if args.all:
531
- agents = config.agents
532
- elif args.nick:
582
+ return config.agents
583
+ if args.nick:
533
584
  agent = config.get_agent(args.nick)
534
585
  if not agent:
535
586
  print(f"Agent '{args.nick}' not found in config", file=sys.stderr)
536
587
  sys.exit(1)
537
- agents = [agent]
538
- else:
539
- if len(config.agents) == 1:
540
- agents = config.agents
541
- elif len(config.agents) == 0:
542
- print(NO_AGENTS_MSG, file=sys.stderr)
543
- sys.exit(1)
544
- else:
545
- print(
546
- "Multiple agents configured. Specify a nick or use --all.",
547
- file=sys.stderr,
548
- )
549
- sys.exit(1)
588
+ return [agent]
589
+ if len(config.agents) == 1:
590
+ return config.agents
591
+ if len(config.agents) == 0:
592
+ print(NO_AGENTS_MSG, file=sys.stderr)
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
599
+ print(
600
+ "Multiple agents configured. Specify a nick or use --all.",
601
+ file=sys.stderr,
602
+ )
603
+ for a in config.agents:
604
+ print(f" {a.nick}", file=sys.stderr)
605
+ sys.exit(1)
550
606
 
607
+
608
+ def _cmd_stop(args: argparse.Namespace) -> None:
609
+ config = load_config_or_default(args.config)
610
+ agents = _resolve_agents_to_stop(config, args)
551
611
  for agent in agents:
552
612
  stop_agent(agent.nick)
553
613
 
@@ -710,13 +770,13 @@ def _cmd_assign(args: argparse.Namespace) -> None:
710
770
  # -----------------------------------------------------------------------
711
771
 
712
772
 
713
- def _resolve_ipc_targets(config, args, action_verb: str) -> list:
773
+ def _resolve_ipc_targets(config, args, command_name: str) -> list:
714
774
  """Resolve which agents to send IPC messages to."""
715
775
  if args.nick and args.all:
716
776
  print("Cannot specify both nick and --all", file=sys.stderr)
717
777
  sys.exit(1)
718
778
  if not args.nick and not args.all:
719
- 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)
720
780
  sys.exit(1)
721
781
  if args.all:
722
782
  return config.agents
@@ -737,20 +797,22 @@ def _send_ipc(agent, msg_type: str, action_verb: str) -> None:
737
797
  print(f"{agent.nick}: failed (not running?)", file=sys.stderr)
738
798
 
739
799
 
740
- 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:
741
803
  """Send an IPC message (pause/resume) to one or all agents."""
742
804
  config = load_config_or_default(args.config)
743
- targets = _resolve_ipc_targets(config, args, action_verb)
805
+ targets = _resolve_ipc_targets(config, args, command_name)
744
806
  for agent in targets:
745
807
  _send_ipc(agent, msg_type, action_verb)
746
808
 
747
809
 
748
810
  def _cmd_sleep(args: argparse.Namespace) -> None:
749
- _ipc_to_agents(args, "pause", "paused")
811
+ _ipc_to_agents(args, "pause", "paused", "sleep")
750
812
 
751
813
 
752
814
  def _cmd_wake(args: argparse.Namespace) -> None:
753
- _ipc_to_agents(args, "resume", "resumed")
815
+ _ipc_to_agents(args, "resume", "resumed", "wake")
754
816
 
755
817
 
756
818
  def _cmd_learn(args: argparse.Namespace) -> None:
@@ -785,6 +847,16 @@ def _cmd_learn(args: argparse.Namespace) -> None:
785
847
 
786
848
 
787
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)
788
860
  observer = get_observer(args.config)
789
861
  asyncio.run(observer.send_message(args.target, args.text))
790
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
@@ -152,6 +157,15 @@ def _bot_stop(args: argparse.Namespace) -> None:
152
157
  print("(Live reload via IPC will be available in a future release.)")
153
158
 
154
159
 
160
+ def _should_include_bot(bot_config, owner: str | None, show_archived: bool) -> bool:
161
+ """Return True if the bot should be included in the listing."""
162
+ if owner and bot_config.owner != owner:
163
+ return False
164
+ if not show_archived and bot_config.archived:
165
+ return False
166
+ return True
167
+
168
+
155
169
  def _load_and_filter_bots(args) -> list:
156
170
  """Load bot configs, filtering by owner and archived status."""
157
171
  from culture.bots.config import BOTS_DIR, load_bot_config
@@ -168,11 +182,8 @@ def _load_and_filter_bots(args) -> list:
168
182
  config = load_bot_config(yaml_path)
169
183
  except Exception:
170
184
  continue
171
- if args.owner and config.owner != args.owner:
172
- continue
173
- if not show_all and config.archived:
174
- continue
175
- bots.append(config)
185
+ if _should_include_bot(config, args.owner, show_all):
186
+ bots.append(config)
176
187
  return bots
177
188
 
178
189
 
@@ -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 (ConnectionError, ConnectionRefusedError, 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))