agentirc-cli 5.0.2__tar.gz → 5.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/.claude/skills/pr-review/SKILL.md +1 -2
  2. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/CHANGELOG.md +20 -0
  3. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/CLAUDE.md +1 -0
  4. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/PKG-INFO +1 -1
  5. agentirc_cli-5.0.3/culture/cli/channel.py +336 -0
  6. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/cli/mesh.py +20 -3
  7. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/acp/skill/SKILL.md +19 -19
  8. {agentirc_cli-5.0.2/plugins/claude-code/skills/irc → agentirc_cli-5.0.3/culture/clients/claude/skill}/SKILL.md +20 -20
  9. {agentirc_cli-5.0.2/plugins/codex/skills/culture-irc → agentirc_cli-5.0.3/culture/clients/codex/skill}/SKILL.md +19 -19
  10. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/copilot/skill/SKILL.md +19 -19
  11. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/learn_prompt.py +13 -13
  12. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/architecture/agent-harness-spec.md +2 -5
  13. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/getting-started.md +7 -7
  14. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/operations/cli.md +18 -0
  15. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/operations/ops-tooling.md +15 -0
  16. agentirc_cli-5.0.3/packages/agent-harness/skill/SKILL.md +57 -0
  17. {agentirc_cli-5.0.2/culture/clients/claude/skill → agentirc_cli-5.0.3/plugins/claude-code/skills/irc}/SKILL.md +20 -20
  18. {agentirc_cli-5.0.2/culture/clients/codex/skill → agentirc_cli-5.0.3/plugins/codex/skills/culture-irc}/SKILL.md +19 -19
  19. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/pyproject.toml +1 -1
  20. agentirc_cli-5.0.3/tests/test_channel_cli.py +224 -0
  21. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_learn_prompt.py +7 -6
  22. agentirc_cli-5.0.3/tests/test_mesh_readiness.py +93 -0
  23. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/uv.lock +1 -1
  24. agentirc_cli-5.0.2/culture/cli/channel.py +0 -136
  25. agentirc_cli-5.0.2/packages/agent-harness/skill/SKILL.md +0 -57
  26. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/.claude/skills/run-tests/SKILL.md +0 -0
  27. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/.claude/skills/run-tests/scripts/test.sh +0 -0
  28. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/.flake8 +0 -0
  29. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/.github/workflows/pages.yml +0 -0
  30. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/.github/workflows/publish.yml +0 -0
  31. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/.github/workflows/security-checks.yml +0 -0
  32. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/.github/workflows/tests.yml +0 -0
  33. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/.gitignore +0 -0
  34. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/.markdownlint-cli2.yaml +0 -0
  35. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/.pr_agent.toml +0 -0
  36. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/.pre-commit-config.yaml +0 -0
  37. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/.pylintrc +0 -0
  38. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/CNAME +0 -0
  39. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/Gemfile +0 -0
  40. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/Gemfile.lock +0 -0
  41. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/LICENSE +0 -0
  42. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/README.md +0 -0
  43. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/SECURITY.md +0 -0
  44. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/_config.yml +0 -0
  45. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/_includes/head_custom.html +0 -0
  46. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/_sass/color_schemes/anthropic.scss +0 -0
  47. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/_sass/custom/custom.scss +0 -0
  48. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/assets/images/apple-touch-icon.png +0 -0
  49. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/assets/images/favicon-16x16.png +0 -0
  50. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/assets/images/favicon-32x32.png +0 -0
  51. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/assets/images/favicon.ico +0 -0
  52. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/__init__.py +0 -0
  53. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/__main__.py +0 -0
  54. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/aio.py +0 -0
  55. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/bots/__init__.py +0 -0
  56. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/bots/bot.py +0 -0
  57. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/bots/bot_manager.py +0 -0
  58. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/bots/config.py +0 -0
  59. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/bots/http_listener.py +0 -0
  60. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/bots/template_engine.py +0 -0
  61. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/bots/virtual_client.py +0 -0
  62. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/cli/__init__.py +0 -0
  63. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/cli/agent.py +0 -0
  64. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/cli/bot.py +0 -0
  65. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/cli/server.py +0 -0
  66. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/cli/shared/__init__.py +0 -0
  67. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/cli/shared/constants.py +0 -0
  68. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/cli/shared/display.py +0 -0
  69. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/cli/shared/formatting.py +0 -0
  70. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/cli/shared/ipc.py +0 -0
  71. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/cli/shared/mesh.py +0 -0
  72. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/cli/shared/process.py +0 -0
  73. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/cli/skills.py +0 -0
  74. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/__init__.py +0 -0
  75. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/acp/__init__.py +0 -0
  76. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/acp/agent_runner.py +0 -0
  77. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/acp/config.py +0 -0
  78. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/acp/culture.yaml +0 -0
  79. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/acp/daemon.py +0 -0
  80. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/acp/ipc.py +0 -0
  81. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/acp/irc_transport.py +0 -0
  82. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/acp/message_buffer.py +0 -0
  83. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/acp/skill/__init__.py +0 -0
  84. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/acp/skill/irc_client.py +0 -0
  85. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/acp/socket_server.py +0 -0
  86. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/acp/supervisor.py +0 -0
  87. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/acp/webhook.py +0 -0
  88. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/claude/__init__.py +0 -0
  89. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/claude/__main__.py +0 -0
  90. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/claude/agent_runner.py +0 -0
  91. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/claude/config.py +0 -0
  92. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/claude/culture.yaml +0 -0
  93. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/claude/daemon.py +0 -0
  94. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/claude/ipc.py +0 -0
  95. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/claude/irc_transport.py +0 -0
  96. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/claude/message_buffer.py +0 -0
  97. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/claude/skill/__init__.py +0 -0
  98. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/claude/skill/irc_client.py +0 -0
  99. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/claude/socket_server.py +0 -0
  100. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/claude/supervisor.py +0 -0
  101. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/claude/webhook.py +0 -0
  102. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/codex/__init__.py +0 -0
  103. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/codex/agent_runner.py +0 -0
  104. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/codex/config.py +0 -0
  105. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/codex/culture.yaml +0 -0
  106. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/codex/daemon.py +0 -0
  107. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/codex/ipc.py +0 -0
  108. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/codex/irc_transport.py +0 -0
  109. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/codex/message_buffer.py +0 -0
  110. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/codex/skill/__init__.py +0 -0
  111. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/codex/skill/irc_client.py +0 -0
  112. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/codex/socket_server.py +0 -0
  113. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/codex/supervisor.py +0 -0
  114. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/codex/webhook.py +0 -0
  115. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/copilot/__init__.py +0 -0
  116. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/copilot/agent_runner.py +0 -0
  117. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/copilot/config.py +0 -0
  118. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/copilot/culture.yaml +0 -0
  119. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/copilot/daemon.py +0 -0
  120. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/copilot/ipc.py +0 -0
  121. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/copilot/irc_transport.py +0 -0
  122. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/copilot/message_buffer.py +0 -0
  123. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/copilot/skill/__init__.py +0 -0
  124. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/copilot/skill/irc_client.py +0 -0
  125. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/copilot/socket_server.py +0 -0
  126. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/copilot/supervisor.py +0 -0
  127. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/clients/copilot/webhook.py +0 -0
  128. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/config.py +0 -0
  129. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/console/__init__.py +0 -0
  130. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/console/app.py +0 -0
  131. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/console/client.py +0 -0
  132. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/console/commands.py +0 -0
  133. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/console/widgets/__init__.py +0 -0
  134. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/console/widgets/chat.py +0 -0
  135. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/console/widgets/info_panel.py +0 -0
  136. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/console/widgets/sidebar.py +0 -0
  137. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/credentials.py +0 -0
  138. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/formatting.py +0 -0
  139. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/mesh_config.py +0 -0
  140. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/observer.py +0 -0
  141. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/overview/__init__.py +0 -0
  142. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/overview/collector.py +0 -0
  143. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/overview/model.py +0 -0
  144. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/overview/renderer_text.py +0 -0
  145. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/overview/renderer_web.py +0 -0
  146. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/overview/web/style.css +0 -0
  147. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/persistence.py +0 -0
  148. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/pidfile.py +0 -0
  149. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/protocol/__init__.py +0 -0
  150. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/protocol/commands.py +0 -0
  151. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/protocol/extensions/federation.md +0 -0
  152. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/protocol/extensions/history.md +0 -0
  153. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/protocol/extensions/icons.md +0 -0
  154. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/protocol/extensions/rooms.md +0 -0
  155. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/protocol/extensions/tags.md +0 -0
  156. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/protocol/extensions/threads.md +0 -0
  157. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/protocol/message.py +0 -0
  158. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/protocol/protocol-index.md +0 -0
  159. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/protocol/replies.py +0 -0
  160. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/__init__.py +0 -0
  161. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/__main__.py +0 -0
  162. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/channel.py +0 -0
  163. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/client.py +0 -0
  164. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/config.py +0 -0
  165. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/history_store.py +0 -0
  166. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/ircd.py +0 -0
  167. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/remote_client.py +0 -0
  168. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/room_store.py +0 -0
  169. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/rooms_util.py +0 -0
  170. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/server_link.py +0 -0
  171. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/skill.py +0 -0
  172. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/skills/__init__.py +0 -0
  173. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/skills/history.py +0 -0
  174. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/skills/icon.py +0 -0
  175. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/skills/rooms.py +0 -0
  176. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/skills/threads.py +0 -0
  177. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/server/thread_store.py +0 -0
  178. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/culture/skills/culture/SKILL.md +0 -0
  179. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/agent-lifecycle.md +0 -0
  180. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/agentic-self-learn.md +0 -0
  181. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/agents/decentralized-config.md +0 -0
  182. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/architecture/agent-client.md +0 -0
  183. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/architecture/design.md +0 -0
  184. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/architecture/harness-conformance.md +0 -0
  185. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/architecture/index.md +0 -0
  186. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/architecture/layer1-core-irc.md +0 -0
  187. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/architecture/layer2-attention.md +0 -0
  188. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/architecture/layer3-skills.md +0 -0
  189. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/architecture/layer4-federation.md +0 -0
  190. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/architecture/layer5-agent-harness.md +0 -0
  191. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/architecture/server-architecture.md +0 -0
  192. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/architecture/threads.md +0 -0
  193. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/channel-polling.md +0 -0
  194. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/acp/overview.md +0 -0
  195. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/acp/system-prompt.md +0 -0
  196. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/claude/configuration.md +0 -0
  197. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/claude/context-management.md +0 -0
  198. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/claude/irc-tools.md +0 -0
  199. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/claude/overview.md +0 -0
  200. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/claude/setup.md +0 -0
  201. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/claude/supervisor.md +0 -0
  202. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/claude/webhooks.md +0 -0
  203. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/codex/configuration.md +0 -0
  204. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/codex/context-management.md +0 -0
  205. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/codex/irc-tools.md +0 -0
  206. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/codex/overview.md +0 -0
  207. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/codex/setup.md +0 -0
  208. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/codex/supervisor.md +0 -0
  209. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/codex/webhooks.md +0 -0
  210. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/copilot/configuration.md +0 -0
  211. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/copilot/context-management.md +0 -0
  212. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/copilot/irc-tools.md +0 -0
  213. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/copilot/overview.md +0 -0
  214. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/copilot/setup.md +0 -0
  215. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/copilot/supervisor.md +0 -0
  216. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/clients/copilot/webhooks.md +0 -0
  217. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/culture-cli.md +0 -0
  218. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/index.md +0 -0
  219. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/operations/SECURITY.md +0 -0
  220. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/operations/bots.md +0 -0
  221. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/operations/ci.md +0 -0
  222. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/operations/docs-site.md +0 -0
  223. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/operations/index.md +0 -0
  224. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/operations/overview.md +0 -0
  225. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/operations/publishing.md +0 -0
  226. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/persistent-history.md +0 -0
  227. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/reflective-development.md +0 -0
  228. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  229. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/rooms.md +0 -0
  230. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/server-rename.md +0 -0
  231. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  232. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  233. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  234. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  235. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
  236. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
  237. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
  238. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
  239. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
  240. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/plans/2026-04-09-decentralized-agent-config.md +0 -0
  241. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  242. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  243. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  244. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  245. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
  246. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
  247. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
  248. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
  249. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
  250. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
  251. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
  252. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
  253. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
  254. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-04-07-reflective-development-reframe-design.md +0 -0
  255. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-04-08-reflective-development-deepening-design.md +0 -0
  256. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/superpowers/specs/2026-04-09-decentralized-agent-config-design.md +0 -0
  257. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/use-cases/01-pair-programming.md +0 -0
  258. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/use-cases/02-code-review-ensemble.md +0 -0
  259. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/use-cases/03-cross-server-delegation.md +0 -0
  260. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/use-cases/04-knowledge-propagation.md +0 -0
  261. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/use-cases/05-the-observer.md +0 -0
  262. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/use-cases/06-cross-server-ops.md +0 -0
  263. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/use-cases/07-supervisor-intervention.md +0 -0
  264. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/use-cases/08-apps-as-agents.md +0 -0
  265. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/use-cases/09-research-swarm.md +0 -0
  266. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/use-cases/10-agent-lifecycle.md +0 -0
  267. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/use-cases-index.md +0 -0
  268. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/docs/what-is-culture.md +0 -0
  269. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/favicon.ico +0 -0
  270. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/packages/agent-harness/README.md +0 -0
  271. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/packages/agent-harness/config.py +0 -0
  272. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/packages/agent-harness/culture.yaml +0 -0
  273. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/packages/agent-harness/daemon.py +0 -0
  274. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/packages/agent-harness/ipc.py +0 -0
  275. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/packages/agent-harness/irc_transport.py +0 -0
  276. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/packages/agent-harness/message_buffer.py +0 -0
  277. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/packages/agent-harness/skill/irc_client.py +0 -0
  278. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/packages/agent-harness/socket_server.py +0 -0
  279. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/packages/agent-harness/webhook.py +0 -0
  280. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  281. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/plugins/claude-code/skills/culture/SKILL.md +0 -0
  282. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/sonar-project.properties +0 -0
  283. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/__init__.py +0 -0
  284. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/conftest.py +0 -0
  285. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_acp_daemon.py +0 -0
  286. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_agent_runner.py +0 -0
  287. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_archive.py +0 -0
  288. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_bot.py +0 -0
  289. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_bot_config.py +0 -0
  290. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_bot_manager.py +0 -0
  291. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_bots_integration.py +0 -0
  292. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_channel.py +0 -0
  293. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_codex_daemon.py +0 -0
  294. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_connection.py +0 -0
  295. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_console_client.py +0 -0
  296. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_console_commands.py +0 -0
  297. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_console_connection.py +0 -0
  298. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_console_icons.py +0 -0
  299. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_console_integration.py +0 -0
  300. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_copilot_daemon.py +0 -0
  301. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_credentials.py +0 -0
  302. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_culture_config.py +0 -0
  303. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_daemon.py +0 -0
  304. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_daemon_config.py +0 -0
  305. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_daemon_ipc.py +0 -0
  306. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_discovery.py +0 -0
  307. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_display.py +0 -0
  308. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_federation.py +0 -0
  309. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_history.py +0 -0
  310. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_http_listener.py +0 -0
  311. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_integration_layer5.py +0 -0
  312. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_ipc.py +0 -0
  313. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_irc_transport.py +0 -0
  314. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_link_reconnect.py +0 -0
  315. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_mention_alias.py +0 -0
  316. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_mention_target_cleanup.py +0 -0
  317. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_mention_warning.py +0 -0
  318. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_mentions.py +0 -0
  319. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_mesh_config.py +0 -0
  320. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_message.py +0 -0
  321. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_message_buffer.py +0 -0
  322. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_messaging.py +0 -0
  323. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_migrate_cli.py +0 -0
  324. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_modes.py +0 -0
  325. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_overview_cli.py +0 -0
  326. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_overview_collector.py +0 -0
  327. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_overview_model.py +0 -0
  328. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_overview_renderer.py +0 -0
  329. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_overview_web.py +0 -0
  330. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_persistence.py +0 -0
  331. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_pidfile.py +0 -0
  332. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_poll_loop.py +0 -0
  333. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_register_cli.py +0 -0
  334. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_room_persistence.py +0 -0
  335. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_rooms.py +0 -0
  336. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_rooms_federation.py +0 -0
  337. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_rooms_integration.py +0 -0
  338. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_server_icon_skill.py +0 -0
  339. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_setup_update_cli.py +0 -0
  340. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_skill_client.py +0 -0
  341. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_skill_docs.py +0 -0
  342. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_skills.py +0 -0
  343. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_socket_server.py +0 -0
  344. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_supervisor.py +0 -0
  345. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_template_engine.py +0 -0
  346. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_thread_buffer.py +0 -0
  347. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_threads.py +0 -0
  348. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_virtual_client.py +0 -0
  349. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_wait_for_port.py +0 -0
  350. {agentirc_cli-5.0.2 → agentirc_cli-5.0.3}/tests/test_webhook.py +0 -0
@@ -179,8 +179,7 @@ Report completion back to the IRC channel:
179
179
 
180
180
  ```bash
181
181
  # Using the IRC skill
182
- CULTURE_NICK=<your-nick> python3 -m culture.clients.claude.skill.irc_client \
183
- send "#general" "PR #<N> — all review threads addressed and resolved. Ready for merge."
182
+ CULTURE_NICK="<agent-nick>" culture channel message "#general" "PR #<N> — all review threads addressed and resolved. Ready for merge."
184
183
  ```
185
184
 
186
185
  ## Script reference
@@ -4,6 +4,26 @@ 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
+ ## [5.0.3] - 2026-04-10
8
+
9
+
10
+ ### Added
11
+
12
+ - New channel subcommands: join, part, ask, topic, compact, clear
13
+
14
+
15
+ ### Changed
16
+
17
+ - Channel CLI routes through agent daemon IPC when CULTURE_NICK is set
18
+ - All SKILL.md files and learn prompt use culture channel CLI instead of python3 -m
19
+ - Mesh update readiness probe verifies PID-based server identity
20
+
21
+
22
+ ### Fixed
23
+
24
+ - culture channel message sends as agent nick instead of temporary peek nick (#203)
25
+ - IRC skill references culture channel CLI instead of broken python3 -m path (#202)
26
+
7
27
  ## [5.0.2] - 2026-04-09
8
28
 
9
29
 
@@ -59,6 +59,7 @@ When implementing features, write a corresponding markdown doc in `docs/` descri
59
59
 
60
60
  ## Testing
61
61
 
62
+ - Use `/run-tests` to run the test suite (parallel + verbose by default)
62
63
  - `pytest` + `pytest-asyncio`, always run with `-n auto` for parallel execution
63
64
  - No mocks for the server — tests spin up real server instances on random ports with real TCP connections
64
65
  - Validate each layer with real IRC clients (weechat/irssi)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 5.0.2
3
+ Version: 5.0.3
4
4
  Summary: Legacy alias for culture — install culture instead
5
5
  Project-URL: Homepage, https://github.com/OriNachum/culture
6
6
  Author: Ori Nachum
@@ -0,0 +1,336 @@
1
+ """Channel subcommands: culture channel {list,read,message,who,join,part,ask,topic,compact,clear}."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import asyncio
7
+ import json
8
+ import os
9
+ import sys
10
+
11
+ from .shared.constants import _CONFIG_HELP, DEFAULT_CONFIG
12
+ from .shared.ipc import agent_socket_path, get_observer, ipc_request
13
+
14
+ NAME = "channel"
15
+
16
+ _ALL_CMDS = "list|read|message|who|join|part|ask|topic|compact|clear"
17
+ _CHANNEL_HELP = "Channel (e.g. #general)"
18
+
19
+
20
+ def _valid_nick(nick: str) -> bool:
21
+ """Check nick matches the required <server>-<agent> format."""
22
+ parts = nick.split("-", 1)
23
+ return len(parts) == 2 and all(parts)
24
+
25
+
26
+ def _try_ipc(msg_type: str, **kwargs) -> dict | None:
27
+ """Try to route a command through the agent daemon's IPC socket.
28
+
29
+ Returns the response dict if CULTURE_NICK is set and the daemon is
30
+ reachable, otherwise None (caller should fall back to observer).
31
+ """
32
+ nick = os.environ.get("CULTURE_NICK")
33
+ if not nick or not _valid_nick(nick):
34
+ return None
35
+ sock = agent_socket_path(nick)
36
+ return asyncio.run(ipc_request(sock, msg_type, **kwargs))
37
+
38
+
39
+ def _require_ipc(msg_type: str, **kwargs) -> dict:
40
+ """Route a command through IPC, erroring if CULTURE_NICK is unset or daemon unreachable."""
41
+ nick = os.environ.get("CULTURE_NICK")
42
+ if not nick or not _valid_nick(nick):
43
+ print(
44
+ "Error: CULTURE_NICK must be set to a valid <server>-<agent> nick.\n"
45
+ " Example: export CULTURE_NICK=spark-claude",
46
+ file=sys.stderr,
47
+ )
48
+ sys.exit(1)
49
+ sock = agent_socket_path(nick)
50
+ resp = asyncio.run(ipc_request(sock, msg_type, **kwargs))
51
+ if resp is None:
52
+ print(
53
+ f"Error: cannot reach agent daemon for {nick}.\n"
54
+ f" Is the agent running? Check: culture agent status {nick}",
55
+ file=sys.stderr,
56
+ )
57
+ sys.exit(1)
58
+ return resp
59
+
60
+
61
+ def register(subparsers: argparse._SubParsersAction) -> None:
62
+ channel_parser = subparsers.add_parser("channel", help="Channel messaging")
63
+ channel_sub = channel_parser.add_subparsers(dest="channel_command")
64
+
65
+ # -- list -----------------------------------------------------------------
66
+ list_parser = channel_sub.add_parser("list", help="List active channels")
67
+ list_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
68
+
69
+ # -- read -----------------------------------------------------------------
70
+ read_parser = channel_sub.add_parser("read", help="Read recent channel messages")
71
+ read_parser.add_argument("target", help="Channel name (e.g. #general)")
72
+ read_parser.add_argument("--limit", "-n", type=int, default=50, help="Number of messages")
73
+ read_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
74
+
75
+ # -- message --------------------------------------------------------------
76
+ message_parser = channel_sub.add_parser("message", help="Send a message to a channel")
77
+ message_parser.add_argument("target", help=_CHANNEL_HELP)
78
+ message_parser.add_argument("text", help="Message text to send")
79
+ message_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
80
+
81
+ # -- who ------------------------------------------------------------------
82
+ who_parser = channel_sub.add_parser("who", help="List channel members")
83
+ who_parser.add_argument("target", help="Channel or nick target")
84
+ who_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
85
+
86
+ # -- join -----------------------------------------------------------------
87
+ join_parser = channel_sub.add_parser("join", help="Join a channel")
88
+ join_parser.add_argument("target", help="Channel to join (e.g. #ops)")
89
+
90
+ # -- part -----------------------------------------------------------------
91
+ part_parser = channel_sub.add_parser("part", help="Leave a channel")
92
+ part_parser.add_argument("target", help="Channel to leave (e.g. #ops)")
93
+
94
+ # -- ask ------------------------------------------------------------------
95
+ ask_parser = channel_sub.add_parser("ask", help="Send a question and trigger webhook alert")
96
+ ask_parser.add_argument("target", help=_CHANNEL_HELP)
97
+ ask_parser.add_argument("text", help="Question text")
98
+ ask_parser.add_argument("--timeout", type=int, default=30, help="Timeout in seconds")
99
+
100
+ # -- topic ----------------------------------------------------------------
101
+ topic_parser = channel_sub.add_parser("topic", help="Get or set channel topic")
102
+ topic_parser.add_argument("target", help=_CHANNEL_HELP)
103
+ topic_parser.add_argument("text", nargs="?", default=None, help="New topic (omit to read)")
104
+ topic_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
105
+
106
+ # -- compact --------------------------------------------------------------
107
+ channel_sub.add_parser("compact", help="Compact the agent's context window")
108
+
109
+ # -- clear ----------------------------------------------------------------
110
+ channel_sub.add_parser("clear", help="Clear the agent's context window")
111
+
112
+
113
+ def dispatch(args: argparse.Namespace) -> None:
114
+ if not args.channel_command:
115
+ print(f"Usage: culture channel {{{_ALL_CMDS}}}", file=sys.stderr)
116
+ sys.exit(1)
117
+
118
+ handlers = {
119
+ "list": _cmd_list,
120
+ "read": _cmd_read,
121
+ "message": _cmd_message,
122
+ "who": _cmd_who,
123
+ "join": _cmd_join,
124
+ "part": _cmd_part,
125
+ "ask": _cmd_ask,
126
+ "topic": _cmd_topic,
127
+ "compact": _cmd_compact,
128
+ "clear": _cmd_clear,
129
+ }
130
+ handler = handlers.get(args.channel_command)
131
+ if not handler:
132
+ print(f"Unknown channel command: {args.channel_command}", file=sys.stderr)
133
+ sys.exit(1)
134
+ try:
135
+ handler(args)
136
+ except (TimeoutError, OSError) as exc:
137
+ msg = str(exc)
138
+ if (
139
+ "Timed out" in msg
140
+ or "Connection refused" in msg
141
+ or "Connect call failed" in msg
142
+ or not msg # TimeoutError from asyncio often has empty message
143
+ ):
144
+ print(
145
+ "Error: cannot connect to IRC server. Is the server running?\n"
146
+ " Start it with: culture server start",
147
+ file=sys.stderr,
148
+ )
149
+ sys.exit(1)
150
+ raise
151
+
152
+
153
+ # -----------------------------------------------------------------------
154
+ # Handlers
155
+ # -----------------------------------------------------------------------
156
+
157
+
158
+ def _cmd_list(args: argparse.Namespace) -> None:
159
+ resp = _try_ipc("irc_channels")
160
+ if resp and resp.get("ok"):
161
+ channels = resp.get("data", {}).get("channels", [])
162
+ if not channels:
163
+ print("No active channels")
164
+ return
165
+ print("Active channels:")
166
+ for ch in channels:
167
+ print(f" {ch}")
168
+ return
169
+
170
+ observer = get_observer(args.config)
171
+ channels = asyncio.run(observer.list_channels())
172
+
173
+ if not channels:
174
+ print("No active channels")
175
+ return
176
+
177
+ print("Active channels:")
178
+ for ch in channels:
179
+ print(f" {ch}")
180
+
181
+
182
+ def _cmd_read(args: argparse.Namespace) -> None:
183
+ if not args.target.strip():
184
+ print("Error: channel name cannot be empty", file=sys.stderr)
185
+ sys.exit(1)
186
+ channel = args.target if args.target.startswith("#") else f"#{args.target}"
187
+
188
+ resp = _try_ipc("irc_read", channel=channel, limit=args.limit)
189
+ if resp and resp.get("ok"):
190
+ messages = resp.get("data", {}).get("messages", [])
191
+ if not messages:
192
+ print(f"No messages in {channel}")
193
+ return
194
+ for msg in messages:
195
+ nick = msg.get("nick", "???")
196
+ text = msg.get("text", "")
197
+ print(f"<{nick}> {text}")
198
+ return
199
+
200
+ observer = get_observer(args.config)
201
+ messages = asyncio.run(observer.read_channel(channel, limit=args.limit))
202
+
203
+ if not messages:
204
+ print(f"No messages in {channel}")
205
+ return
206
+
207
+ for msg in messages:
208
+ print(msg)
209
+
210
+
211
+ def _cmd_message(args: argparse.Namespace) -> None:
212
+ if not args.target.strip():
213
+ print("Error: channel name cannot be empty", file=sys.stderr)
214
+ sys.exit(1)
215
+ if not args.text.strip():
216
+ print("Error: message text cannot be empty", file=sys.stderr)
217
+ sys.exit(1)
218
+ target = args.target if args.target.startswith("#") else f"#{args.target}"
219
+
220
+ resp = _try_ipc("irc_send", channel=target, message=args.text)
221
+ if resp and resp.get("ok"):
222
+ print(f"Sent to {target}")
223
+ return
224
+
225
+ observer = get_observer(args.config)
226
+ asyncio.run(observer.send_message(target, args.text))
227
+ print(f"Sent to {target}")
228
+
229
+
230
+ def _cmd_who(args: argparse.Namespace) -> None:
231
+ if not args.target.strip():
232
+ print("Error: channel name cannot be empty", file=sys.stderr)
233
+ sys.exit(1)
234
+ target = args.target
235
+
236
+ # WHO always uses the observer — the daemon IPC handler fires the query
237
+ # but returns results asynchronously via IRC numerics, so the CLI can't
238
+ # collect the nick list through IPC.
239
+ observer = get_observer(args.config)
240
+ nicks = asyncio.run(observer.who(target))
241
+
242
+ if not nicks:
243
+ print(f"No users in {target}")
244
+ return
245
+
246
+ print(f"Users in {target}:")
247
+ for nick in nicks:
248
+ print(f" {nick}")
249
+
250
+
251
+ def _cmd_join(args: argparse.Namespace) -> None:
252
+ target = args.target if args.target.startswith("#") else f"#{args.target}"
253
+ resp = _require_ipc("irc_join", channel=target)
254
+ if resp.get("ok"):
255
+ print(f"Joined {target}")
256
+ else:
257
+ print(f"Error: {resp.get('error', 'unknown error')}", file=sys.stderr)
258
+ sys.exit(1)
259
+
260
+
261
+ def _cmd_part(args: argparse.Namespace) -> None:
262
+ target = args.target if args.target.startswith("#") else f"#{args.target}"
263
+ resp = _require_ipc("irc_part", channel=target)
264
+ if resp.get("ok"):
265
+ print(f"Left {target}")
266
+ else:
267
+ print(f"Error: {resp.get('error', 'unknown error')}", file=sys.stderr)
268
+ sys.exit(1)
269
+
270
+
271
+ def _cmd_ask(args: argparse.Namespace) -> None:
272
+ if not args.target.strip():
273
+ print("Error: channel name cannot be empty", file=sys.stderr)
274
+ sys.exit(1)
275
+ if not args.text.strip():
276
+ print("Error: question text cannot be empty", file=sys.stderr)
277
+ sys.exit(1)
278
+ target = args.target if args.target.startswith("#") else f"#{args.target}"
279
+ resp = _require_ipc("irc_ask", channel=target, message=args.text, timeout=args.timeout)
280
+ if resp.get("ok"):
281
+ print(json.dumps(resp, indent=2))
282
+ else:
283
+ print(f"Error: {resp.get('error', 'unknown error')}", file=sys.stderr)
284
+ sys.exit(1)
285
+
286
+
287
+ def _cmd_topic(args: argparse.Namespace) -> None:
288
+ if not args.target.strip():
289
+ print("Error: channel name cannot be empty", file=sys.stderr)
290
+ sys.exit(1)
291
+ target = args.target if args.target.startswith("#") else f"#{args.target}"
292
+
293
+ if args.text is not None:
294
+ # Setting topic requires the daemon (must act as agent nick)
295
+ resp = _require_ipc("irc_topic", channel=target, topic=args.text)
296
+ if resp.get("ok"):
297
+ print(f"Topic set for {target}")
298
+ else:
299
+ print(f"Error: {resp.get('error', 'unknown error')}", file=sys.stderr)
300
+ sys.exit(1)
301
+ else:
302
+ # Reading topic — the daemon IPC handler returns results
303
+ # asynchronously via IRC numerics, so use IPC to fire the query
304
+ # and report that it was sent.
305
+ resp = _try_ipc("irc_topic", channel=target)
306
+ if resp and resp.get("ok"):
307
+ data = resp.get("data") or {}
308
+ if "topic" in data:
309
+ topic = data["topic"]
310
+ print(f"Topic for {target}: {topic}" if topic else f"No topic set for {target}")
311
+ else:
312
+ print(f"Topic query sent for {target} (result arrives asynchronously)")
313
+ else:
314
+ print(
315
+ f"Error: topic query requires a running agent daemon (CULTURE_NICK).",
316
+ file=sys.stderr,
317
+ )
318
+ sys.exit(1)
319
+
320
+
321
+ def _cmd_compact(args: argparse.Namespace) -> None:
322
+ resp = _require_ipc("compact")
323
+ if resp.get("ok"):
324
+ print("Context window compacted")
325
+ else:
326
+ print(f"Error: {resp.get('error', 'unknown error')}", file=sys.stderr)
327
+ sys.exit(1)
328
+
329
+
330
+ def _cmd_clear(args: argparse.Namespace) -> None:
331
+ resp = _require_ipc("clear")
332
+ if resp.get("ok"):
333
+ print("Context window cleared")
334
+ else:
335
+ print(f"Error: {resp.get('error', 'unknown error')}", file=sys.stderr)
336
+ sys.exit(1)
@@ -469,14 +469,31 @@ def _upgrade_culture_package(args: argparse.Namespace) -> bool:
469
469
  os.execvp(culture_bin, reexec_args)
470
470
 
471
471
 
472
- def _wait_for_server_port(host: str, port: int, retries: int = 50, interval: float = 0.1) -> bool:
473
- """Poll until *host*:*port* accepts a TCP connection. Returns True on success."""
472
+ def _wait_for_server_port(
473
+ host: str,
474
+ port: int,
475
+ retries: int = 50,
476
+ interval: float = 0.1,
477
+ server_name: str | None = None,
478
+ ) -> bool:
479
+ """Poll until *host*:*port* accepts a TCP connection.
480
+
481
+ If *server_name* is given, also verify via PID file that the process
482
+ listening on the port is actually a culture server (defense-in-depth
483
+ against port collision with an unrelated process).
484
+ """
474
485
  import socket as _socket
475
486
 
487
+ from culture.pidfile import is_culture_process, read_pid
488
+
476
489
  probe = "localhost" if host in ("0.0.0.0", "::", "") else host
477
490
  for _ in range(retries):
478
491
  try:
479
492
  with _socket.create_connection((probe, port), timeout=1):
493
+ if server_name:
494
+ pid = read_pid(f"server-{server_name}")
495
+ if pid is not None and not is_culture_process(pid):
496
+ return False
480
497
  return True
481
498
  except OSError:
482
499
  time.sleep(interval)
@@ -566,7 +583,7 @@ def _restart_mesh_services(
566
583
  ]
567
584
  _restart_single_service(server_svc, server_fallback, restart_service)
568
585
 
569
- if not _wait_for_server_port(mesh.server.host, mesh.server.port):
586
+ if not _wait_for_server_port(mesh.server.host, mesh.server.port, server_name=server_name):
570
587
  if sys.platform == "linux":
571
588
  hint = f"journalctl --user -u culture-server-{server_name}"
572
589
  else:
@@ -23,7 +23,7 @@ $XDG_RUNTIME_DIR/culture-<nick>.sock (falls back to /tmp/culture-<nick>.sock)
23
23
  ## Invocation
24
24
 
25
25
  ```bash
26
- python3 -m culture.clients.acp.skill.irc_client <subcommand> [args...]
26
+ culture channel <subcommand> [args...]
27
27
  ```
28
28
 
29
29
  All commands print a JSON result to stdout. Whispers from the daemon are printed
@@ -33,16 +33,16 @@ to stderr as `[whisper:<type>] <message>`.
33
33
 
34
34
  ## Commands
35
35
 
36
- ### send — post a message to a channel
36
+ ### message — post a message to a channel
37
37
 
38
38
  ```bash
39
- python3 -m culture.clients.acp.skill.irc_client send <channel> <message>
39
+ culture channel message <channel> <message>
40
40
  ```
41
41
 
42
42
  Example:
43
43
 
44
44
  ```bash
45
- python3 -m culture.clients.acp.skill.irc_client send "#general" "hello from ACP"
45
+ culture channel message "#general" "hello from ACP"
46
46
  ```
47
47
 
48
48
  Output:
@@ -56,13 +56,13 @@ Output:
56
56
  ### read — read recent messages from a channel
57
57
 
58
58
  ```bash
59
- python3 -m culture.clients.acp.skill.irc_client read <channel> [limit]
59
+ culture channel read <channel> [--limit N]
60
60
  ```
61
61
 
62
- `limit` defaults to 50. Example:
62
+ `--limit` defaults to 50. Example:
63
63
 
64
64
  ```bash
65
- python3 -m culture.clients.acp.skill.irc_client read "#general" 20
65
+ culture channel read "#general" --limit 20
66
66
  ```
67
67
 
68
68
  Output:
@@ -85,13 +85,13 @@ Output:
85
85
  ### ask — send a question and trigger a webhook alert
86
86
 
87
87
  ```bash
88
- python3 -m culture.clients.acp.skill.irc_client ask <channel> [--timeout N] <question>
88
+ culture channel ask <channel> [--timeout N] <question>
89
89
  ```
90
90
 
91
91
  `--timeout` is in seconds (default 30). Example:
92
92
 
93
93
  ```bash
94
- python3 -m culture.clients.acp.skill.irc_client ask "#general" --timeout 60 "What is the status of the deploy?"
94
+ culture channel ask "#general" --timeout 60 "What is the status of the deploy?"
95
95
  ```
96
96
 
97
97
  ---
@@ -99,7 +99,7 @@ python3 -m culture.clients.acp.skill.irc_client ask "#general" --timeout 60 "Wha
99
99
  ### join — join a channel
100
100
 
101
101
  ```bash
102
- python3 -m culture.clients.acp.skill.irc_client join <channel>
102
+ culture channel join <channel>
103
103
  ```
104
104
 
105
105
  ---
@@ -107,15 +107,15 @@ python3 -m culture.clients.acp.skill.irc_client join <channel>
107
107
  ### part — leave a channel
108
108
 
109
109
  ```bash
110
- python3 -m culture.clients.acp.skill.irc_client part <channel>
110
+ culture channel part <channel>
111
111
  ```
112
112
 
113
113
  ---
114
114
 
115
- ### channels — list joined channels
115
+ ### list — list joined channels
116
116
 
117
117
  ```bash
118
- python3 -m culture.clients.acp.skill.irc_client channels
118
+ culture channel list
119
119
  ```
120
120
 
121
121
  Output:
@@ -134,7 +134,7 @@ Output:
134
134
  ### who — send a WHO query
135
135
 
136
136
  ```bash
137
- python3 -m culture.clients.acp.skill.irc_client who <target>
137
+ culture channel who <target>
138
138
  ```
139
139
 
140
140
  `target` can be a channel or a nick.
@@ -144,19 +144,19 @@ python3 -m culture.clients.acp.skill.irc_client who <target>
144
144
  ### topic — get or set a channel topic
145
145
 
146
146
  ```bash
147
- python3 -m culture.clients.acp.skill.irc_client topic <channel> [topic text]
147
+ culture channel topic <channel> [topic text]
148
148
  ```
149
149
 
150
150
  Get current topic:
151
151
 
152
152
  ```bash
153
- python3 -m culture.clients.acp.skill.irc_client topic "#general"
153
+ culture channel topic "#general"
154
154
  ```
155
155
 
156
156
  Set topic:
157
157
 
158
158
  ```bash
159
- python3 -m culture.clients.acp.skill.irc_client topic "#general" "Welcome to general chat"
159
+ culture channel topic "#general" "Welcome to general chat"
160
160
  ```
161
161
 
162
162
  ---
@@ -164,7 +164,7 @@ python3 -m culture.clients.acp.skill.irc_client topic "#general" "Welcome to gen
164
164
  ### compact — compact the agent's context window
165
165
 
166
166
  ```bash
167
- python3 -m culture.clients.acp.skill.irc_client compact
167
+ culture channel compact
168
168
  ```
169
169
 
170
170
  Sends `/compact` to the agent session via the daemon's prompt queue.
@@ -174,7 +174,7 @@ Sends `/compact` to the agent session via the daemon's prompt queue.
174
174
  ### clear — clear the agent's context window
175
175
 
176
176
  ```bash
177
- python3 -m culture.clients.acp.skill.irc_client clear
177
+ culture channel clear
178
178
  ```
179
179
 
180
180
  Sends `/clear` to the agent session via the daemon's prompt queue.