agentirc-cli 3.1.1__tar.gz → 4.0.0__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 (305) hide show
  1. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/CHANGELOG.md +22 -0
  2. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/PKG-INFO +1 -1
  3. agentirc_cli-4.0.0/culture/cli/__init__.py +58 -0
  4. agentirc_cli-4.0.0/culture/cli/_helpers.py +427 -0
  5. agentirc_cli-4.0.0/culture/cli/agent.py +740 -0
  6. agentirc_cli-4.0.0/culture/cli/bot.py +203 -0
  7. agentirc_cli-4.0.0/culture/cli/channel.py +108 -0
  8. agentirc_cli-4.0.0/culture/cli/mesh.py +571 -0
  9. agentirc_cli-4.0.0/culture/cli/server.py +365 -0
  10. agentirc_cli-4.0.0/culture/cli/skills.py +143 -0
  11. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/claude/__main__.py +2 -2
  12. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/learn_prompt.py +8 -8
  13. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/skills/culture/SKILL.md +36 -36
  14. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/acp/overview.md +5 -5
  15. agentirc_cli-4.0.0/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +212 -0
  16. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/pyproject.toml +1 -1
  17. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_overview_cli.py +14 -10
  18. agentirc_cli-4.0.0/tests/test_setup_update_cli.py +45 -0
  19. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_wait_for_port.py +1 -1
  20. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/uv.lock +1 -1
  21. agentirc_cli-3.1.1/culture/cli.py +0 -2432
  22. agentirc_cli-3.1.1/tests/test_setup_update_cli.py +0 -44
  23. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/.claude/skills/pr-review/SKILL.md +0 -0
  24. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/.claude/skills/run-tests/SKILL.md +0 -0
  25. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
  26. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/.flake8 +0 -0
  27. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/.github/workflows/pages.yml +0 -0
  28. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/.github/workflows/publish.yml +0 -0
  29. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/.github/workflows/security-checks.yml +0 -0
  30. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/.github/workflows/tests.yml +0 -0
  31. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/.gitignore +0 -0
  32. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/.markdownlint-cli2.yaml +0 -0
  33. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/.pr_agent.toml +0 -0
  34. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/.pre-commit-config.yaml +0 -0
  35. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/.pylintrc +0 -0
  36. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/CLAUDE.md +0 -0
  37. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/CNAME +0 -0
  38. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/Gemfile +0 -0
  39. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/Gemfile.lock +0 -0
  40. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/LICENSE +0 -0
  41. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/README.md +0 -0
  42. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/SECURITY.md +0 -0
  43. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/_config.yml +0 -0
  44. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/_sass/color_schemes/anthropic.scss +0 -0
  45. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/_sass/custom/custom.scss +0 -0
  46. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/__init__.py +0 -0
  47. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/__main__.py +0 -0
  48. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/bots/__init__.py +0 -0
  49. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/bots/bot.py +0 -0
  50. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/bots/bot_manager.py +0 -0
  51. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/bots/config.py +0 -0
  52. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/bots/http_listener.py +0 -0
  53. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/bots/template_engine.py +0 -0
  54. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/bots/virtual_client.py +0 -0
  55. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/__init__.py +0 -0
  56. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/acp/__init__.py +0 -0
  57. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/acp/agent_runner.py +0 -0
  58. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/acp/config.py +0 -0
  59. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/acp/daemon.py +0 -0
  60. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/acp/ipc.py +0 -0
  61. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/acp/irc_transport.py +0 -0
  62. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/acp/message_buffer.py +0 -0
  63. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/acp/skill/SKILL.md +0 -0
  64. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/acp/skill/__init__.py +0 -0
  65. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/acp/skill/irc_client.py +0 -0
  66. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/acp/socket_server.py +0 -0
  67. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/acp/supervisor.py +0 -0
  68. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/acp/webhook.py +0 -0
  69. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/claude/__init__.py +0 -0
  70. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/claude/agent_runner.py +0 -0
  71. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/claude/config.py +0 -0
  72. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/claude/daemon.py +0 -0
  73. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/claude/ipc.py +0 -0
  74. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/claude/irc_transport.py +0 -0
  75. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/claude/message_buffer.py +0 -0
  76. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/claude/skill/SKILL.md +0 -0
  77. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/claude/skill/__init__.py +0 -0
  78. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/claude/skill/irc_client.py +0 -0
  79. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/claude/socket_server.py +0 -0
  80. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/claude/supervisor.py +0 -0
  81. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/claude/webhook.py +0 -0
  82. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/codex/__init__.py +0 -0
  83. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/codex/agent_runner.py +0 -0
  84. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/codex/config.py +0 -0
  85. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/codex/daemon.py +0 -0
  86. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/codex/ipc.py +0 -0
  87. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/codex/irc_transport.py +0 -0
  88. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/codex/message_buffer.py +0 -0
  89. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/codex/skill/SKILL.md +0 -0
  90. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/codex/skill/__init__.py +0 -0
  91. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/codex/skill/irc_client.py +0 -0
  92. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/codex/socket_server.py +0 -0
  93. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/codex/supervisor.py +0 -0
  94. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/codex/webhook.py +0 -0
  95. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/copilot/__init__.py +0 -0
  96. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/copilot/agent_runner.py +0 -0
  97. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/copilot/config.py +0 -0
  98. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/copilot/daemon.py +0 -0
  99. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/copilot/ipc.py +0 -0
  100. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/copilot/irc_transport.py +0 -0
  101. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/copilot/message_buffer.py +0 -0
  102. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/copilot/skill/SKILL.md +0 -0
  103. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/copilot/skill/__init__.py +0 -0
  104. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/copilot/skill/irc_client.py +0 -0
  105. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/copilot/socket_server.py +0 -0
  106. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/copilot/supervisor.py +0 -0
  107. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/clients/copilot/webhook.py +0 -0
  108. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/console/__init__.py +0 -0
  109. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/console/app.py +0 -0
  110. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/console/client.py +0 -0
  111. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/console/commands.py +0 -0
  112. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/console/widgets/__init__.py +0 -0
  113. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/console/widgets/chat.py +0 -0
  114. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/console/widgets/info_panel.py +0 -0
  115. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/console/widgets/sidebar.py +0 -0
  116. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/credentials.py +0 -0
  117. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/mesh_config.py +0 -0
  118. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/observer.py +0 -0
  119. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/overview/__init__.py +0 -0
  120. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/overview/collector.py +0 -0
  121. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/overview/model.py +0 -0
  122. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/overview/renderer_text.py +0 -0
  123. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/overview/renderer_web.py +0 -0
  124. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/overview/web/style.css +0 -0
  125. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/persistence.py +0 -0
  126. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/pidfile.py +0 -0
  127. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/protocol/__init__.py +0 -0
  128. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/protocol/commands.py +0 -0
  129. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/protocol/extensions/federation.md +0 -0
  130. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/protocol/extensions/history.md +0 -0
  131. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/protocol/extensions/icons.md +0 -0
  132. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/protocol/extensions/rooms.md +0 -0
  133. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/protocol/extensions/tags.md +0 -0
  134. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/protocol/extensions/threads.md +0 -0
  135. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/protocol/message.py +0 -0
  136. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/protocol/protocol-index.md +0 -0
  137. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/protocol/replies.py +0 -0
  138. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/__init__.py +0 -0
  139. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/__main__.py +0 -0
  140. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/channel.py +0 -0
  141. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/client.py +0 -0
  142. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/config.py +0 -0
  143. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/ircd.py +0 -0
  144. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/remote_client.py +0 -0
  145. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/room_store.py +0 -0
  146. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/rooms_util.py +0 -0
  147. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/server_link.py +0 -0
  148. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/skill.py +0 -0
  149. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/skills/__init__.py +0 -0
  150. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/skills/history.py +0 -0
  151. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/skills/icon.py +0 -0
  152. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/skills/rooms.py +0 -0
  153. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/skills/threads.py +0 -0
  154. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/culture/server/thread_store.py +0 -0
  155. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/agent-lifecycle.md +0 -0
  156. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/agentic-self-learn.md +0 -0
  157. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/architecture/agent-client.md +0 -0
  158. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/architecture/agent-harness-spec.md +0 -0
  159. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/architecture/design.md +0 -0
  160. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/architecture/harness-conformance.md +0 -0
  161. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/architecture/index.md +0 -0
  162. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/architecture/layer1-core-irc.md +0 -0
  163. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/architecture/layer2-attention.md +0 -0
  164. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/architecture/layer3-skills.md +0 -0
  165. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/architecture/layer4-federation.md +0 -0
  166. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/architecture/layer5-agent-harness.md +0 -0
  167. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/architecture/server-architecture.md +0 -0
  168. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/architecture/threads.md +0 -0
  169. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/claude/configuration.md +0 -0
  170. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/claude/context-management.md +0 -0
  171. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/claude/irc-tools.md +0 -0
  172. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/claude/overview.md +0 -0
  173. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/claude/setup.md +0 -0
  174. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/claude/supervisor.md +0 -0
  175. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/claude/webhooks.md +0 -0
  176. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/codex/configuration.md +0 -0
  177. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/codex/context-management.md +0 -0
  178. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/codex/irc-tools.md +0 -0
  179. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/codex/overview.md +0 -0
  180. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/codex/setup.md +0 -0
  181. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/codex/supervisor.md +0 -0
  182. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/codex/webhooks.md +0 -0
  183. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/copilot/configuration.md +0 -0
  184. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/copilot/context-management.md +0 -0
  185. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/copilot/irc-tools.md +0 -0
  186. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/copilot/overview.md +0 -0
  187. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/copilot/setup.md +0 -0
  188. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/copilot/supervisor.md +0 -0
  189. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/clients/copilot/webhooks.md +0 -0
  190. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/culture-cli.md +0 -0
  191. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/getting-started.md +0 -0
  192. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/index.md +0 -0
  193. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/operations/SECURITY.md +0 -0
  194. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/operations/bots.md +0 -0
  195. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/operations/ci.md +0 -0
  196. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/operations/cli.md +0 -0
  197. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/operations/docs-site.md +0 -0
  198. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/operations/index.md +0 -0
  199. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/operations/ops-tooling.md +0 -0
  200. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/operations/overview.md +0 -0
  201. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/operations/publishing.md +0 -0
  202. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  203. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/rooms.md +0 -0
  204. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/server-rename.md +0 -0
  205. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  206. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  207. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  208. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  209. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
  210. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
  211. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
  212. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
  213. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
  214. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  215. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  216. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  217. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  218. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
  219. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
  220. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
  221. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
  222. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
  223. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
  224. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
  225. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/use-cases/01-pair-programming.md +0 -0
  226. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/use-cases/02-code-review-ensemble.md +0 -0
  227. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/use-cases/03-cross-server-delegation.md +0 -0
  228. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/use-cases/04-knowledge-propagation.md +0 -0
  229. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/use-cases/05-the-observer.md +0 -0
  230. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/use-cases/06-cross-server-ops.md +0 -0
  231. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/use-cases/07-supervisor-intervention.md +0 -0
  232. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/use-cases/08-apps-as-agents.md +0 -0
  233. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/use-cases/09-research-swarm.md +0 -0
  234. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/use-cases/10-agent-lifecycle.md +0 -0
  235. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/use-cases-index.md +0 -0
  236. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/docs/what-is-culture.md +0 -0
  237. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/packages/agent-harness/README.md +0 -0
  238. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/packages/agent-harness/config.py +0 -0
  239. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/packages/agent-harness/daemon.py +0 -0
  240. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/packages/agent-harness/ipc.py +0 -0
  241. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/packages/agent-harness/irc_transport.py +0 -0
  242. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/packages/agent-harness/message_buffer.py +0 -0
  243. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/packages/agent-harness/skill/SKILL.md +0 -0
  244. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/packages/agent-harness/skill/irc_client.py +0 -0
  245. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/packages/agent-harness/socket_server.py +0 -0
  246. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/packages/agent-harness/webhook.py +0 -0
  247. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  248. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/plugins/claude-code/skills/culture/SKILL.md +0 -0
  249. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  250. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
  251. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/sonar-project.properties +0 -0
  252. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/__init__.py +0 -0
  253. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/conftest.py +0 -0
  254. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_acp_daemon.py +0 -0
  255. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_agent_runner.py +0 -0
  256. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_bot.py +0 -0
  257. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_bot_config.py +0 -0
  258. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_bot_manager.py +0 -0
  259. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_bots_integration.py +0 -0
  260. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_channel.py +0 -0
  261. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_codex_daemon.py +0 -0
  262. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_connection.py +0 -0
  263. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_console_client.py +0 -0
  264. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_console_commands.py +0 -0
  265. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_console_connection.py +0 -0
  266. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_console_icons.py +0 -0
  267. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_console_integration.py +0 -0
  268. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_copilot_daemon.py +0 -0
  269. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_daemon.py +0 -0
  270. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_daemon_config.py +0 -0
  271. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_daemon_ipc.py +0 -0
  272. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_discovery.py +0 -0
  273. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_federation.py +0 -0
  274. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_history.py +0 -0
  275. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_http_listener.py +0 -0
  276. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_integration_layer5.py +0 -0
  277. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_ipc.py +0 -0
  278. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_irc_transport.py +0 -0
  279. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_link_reconnect.py +0 -0
  280. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_mentions.py +0 -0
  281. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_mesh_config.py +0 -0
  282. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_message.py +0 -0
  283. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_message_buffer.py +0 -0
  284. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_messaging.py +0 -0
  285. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_modes.py +0 -0
  286. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_overview_collector.py +0 -0
  287. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_overview_model.py +0 -0
  288. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_overview_renderer.py +0 -0
  289. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_overview_web.py +0 -0
  290. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_persistence.py +0 -0
  291. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_pidfile.py +0 -0
  292. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_room_persistence.py +0 -0
  293. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_rooms.py +0 -0
  294. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_rooms_federation.py +0 -0
  295. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_rooms_integration.py +0 -0
  296. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_server_icon_skill.py +0 -0
  297. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_skill_client.py +0 -0
  298. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_skills.py +0 -0
  299. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_socket_server.py +0 -0
  300. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_supervisor.py +0 -0
  301. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_template_engine.py +0 -0
  302. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_thread_buffer.py +0 -0
  303. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_threads.py +0 -0
  304. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_virtual_client.py +0 -0
  305. {agentirc_cli-3.1.1 → agentirc_cli-4.0.0}/tests/test_webhook.py +0 -0
@@ -4,6 +4,28 @@ 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.0.0] - 2026-04-06
8
+
9
+
10
+ ### Added
11
+
12
+ - culture agent message and culture agent read for DM operations
13
+ - culture channel message and culture channel who for channel operations
14
+
15
+
16
+ ### Changed
17
+
18
+ - Reorganized CLI into noun-first command groups: agent, server, mesh, channel, bot, skills
19
+ - Split monolithic cli.py (2432 lines) into focused modules under culture/cli/
20
+ - Mirrored message and read commands under both agent and channel groups
21
+
22
+ ## [3.1.2] - 2026-04-06
23
+
24
+
25
+ ### Fixed
26
+
27
+ - culture update used wrong package name (culture-cli) for uv tool upgrade
28
+
7
29
  ## [3.1.1] - 2026-04-06
8
30
 
9
31
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 3.1.1
3
+ Version: 4.0.0
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,58 @@
1
+ """Unified CLI entry point for culture.
2
+
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}
6
+ culture mesh {overview,setup,update,console}
7
+ culture channel {list,read,message,who}
8
+ culture bot {create,start,stop,list,inspect}
9
+ culture skills {install}
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import argparse
15
+ import logging
16
+ import sys
17
+
18
+ from culture.cli import agent, bot, channel, mesh, server, skills
19
+
20
+ GROUPS = [agent, server, mesh, channel, bot, skills]
21
+
22
+
23
+ def _build_parser() -> argparse.ArgumentParser:
24
+ parser = argparse.ArgumentParser(
25
+ prog="culture",
26
+ description="culture — AI agent IRC mesh",
27
+ )
28
+ sub = parser.add_subparsers(dest="command")
29
+ for group in GROUPS:
30
+ group.register(sub)
31
+ return parser
32
+
33
+
34
+ def main() -> None:
35
+ parser = _build_parser()
36
+ args = parser.parse_args()
37
+
38
+ if args.command is None:
39
+ parser.print_help()
40
+ sys.exit(1)
41
+
42
+ logging.basicConfig(
43
+ level=logging.INFO,
44
+ format="%(asctime)s %(name)s %(levelname)s %(message)s",
45
+ )
46
+
47
+ try:
48
+ for group in GROUPS:
49
+ if args.command == group.NAME:
50
+ group.dispatch(args)
51
+ return
52
+ parser.print_help()
53
+ sys.exit(1)
54
+ except KeyboardInterrupt:
55
+ pass
56
+ except Exception as exc:
57
+ print(f"Error: {exc}", file=sys.stderr)
58
+ sys.exit(1)
@@ -0,0 +1,427 @@
1
+ """Shared helpers for culture CLI modules."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import asyncio
7
+ import logging
8
+ import os
9
+ import signal
10
+ import sys
11
+ import time
12
+
13
+ from culture.clients.claude.config import (
14
+ load_config,
15
+ load_config_or_default,
16
+ )
17
+ from culture.pidfile import (
18
+ is_culture_process,
19
+ is_process_alive,
20
+ read_pid,
21
+ remove_pid,
22
+ write_pid,
23
+ )
24
+
25
+ logger = logging.getLogger("culture")
26
+
27
+ DEFAULT_CONFIG = os.path.expanduser("~/.culture/agents.yaml")
28
+ _CONFIG_HELP = "Config file path"
29
+ LOG_DIR = os.path.expanduser("~/.culture/logs")
30
+
31
+
32
+ # -----------------------------------------------------------------------
33
+ # Link / credential helpers
34
+ # -----------------------------------------------------------------------
35
+
36
+
37
+ def parse_link(value: str):
38
+ """Parse a link spec: name:host:port:password[:trust]
39
+
40
+ Trust is extracted from the end if it matches a known value.
41
+ This allows passwords containing colons.
42
+ """
43
+ from culture.server.config import LinkConfig
44
+
45
+ trust = "full"
46
+ if value.endswith(":full") or value.endswith(":restricted"):
47
+ value, trust = value.rsplit(":", 1)
48
+
49
+ parts = value.split(":", 3)
50
+ if len(parts) != 4:
51
+ raise argparse.ArgumentTypeError(
52
+ f"Link must be name:host:port:password[:trust], got: {value}"
53
+ )
54
+ name, host, port_str, password = parts
55
+ try:
56
+ port = int(port_str)
57
+ except ValueError:
58
+ raise argparse.ArgumentTypeError(f"Invalid port: {port_str}")
59
+ return LinkConfig(name=name, host=host, port=port, password=password, trust=trust)
60
+
61
+
62
+ def resolve_links_from_mesh(mesh_config_path: str) -> list:
63
+ """Load link configs from mesh.yaml, looking up passwords from OS keyring."""
64
+ from culture.credentials import lookup_credential
65
+ from culture.mesh_config import load_mesh_config
66
+ from culture.server.config import LinkConfig
67
+
68
+ mesh = load_mesh_config(mesh_config_path)
69
+ links = []
70
+ for lc in mesh.server.links:
71
+ password = lookup_credential(lc.name)
72
+ if not password:
73
+ logger.warning(
74
+ "No credential found for peer '%s' — link will not be established. "
75
+ "Run 'culture mesh setup' to store link passwords.",
76
+ lc.name,
77
+ )
78
+ continue
79
+ links.append(
80
+ LinkConfig(
81
+ name=lc.name,
82
+ host=lc.host,
83
+ port=lc.port,
84
+ password=password,
85
+ trust=lc.trust,
86
+ )
87
+ )
88
+ return links
89
+
90
+
91
+ # -----------------------------------------------------------------------
92
+ # IPC helpers
93
+ # -----------------------------------------------------------------------
94
+
95
+
96
+ def agent_socket_path(nick: str) -> str:
97
+ return os.path.join(
98
+ os.environ.get("XDG_RUNTIME_DIR", "/tmp"),
99
+ f"culture-{nick}.sock",
100
+ )
101
+
102
+
103
+ async def ipc_request(socket_path: str, msg_type: str, **kwargs) -> dict | None:
104
+ """Send an IPC request via Unix socket and return the response."""
105
+ from culture.clients.claude.ipc import decode_message, encode_message, make_request
106
+
107
+ try:
108
+ reader, writer = await asyncio.wait_for(
109
+ asyncio.open_unix_connection(socket_path),
110
+ timeout=3.0,
111
+ )
112
+ except (ConnectionRefusedError, FileNotFoundError, OSError):
113
+ return None
114
+ try:
115
+ req = make_request(msg_type, **kwargs)
116
+ writer.write(encode_message(req))
117
+ await writer.drain()
118
+ deadline = asyncio.get_event_loop().time() + 3.0
119
+ while True:
120
+ remaining = deadline - asyncio.get_event_loop().time()
121
+ if remaining <= 0:
122
+ return None
123
+ data = await asyncio.wait_for(reader.readline(), timeout=remaining)
124
+ msg = decode_message(data)
125
+ if msg and msg.get("type") == "response":
126
+ return msg
127
+ except (asyncio.TimeoutError, ConnectionError, BrokenPipeError, OSError):
128
+ return None
129
+ finally:
130
+ writer.close()
131
+ try:
132
+ await writer.wait_closed()
133
+ except (ConnectionError, BrokenPipeError, OSError):
134
+ pass
135
+
136
+
137
+ async def ipc_shutdown(socket_path: str) -> bool:
138
+ """Send a shutdown command via Unix socket IPC."""
139
+ resp = await ipc_request(socket_path, "shutdown")
140
+ return resp is not None and resp.get("ok", False)
141
+
142
+
143
+ # -----------------------------------------------------------------------
144
+ # Agent stop helpers (used by agent.py and mesh.py)
145
+ # -----------------------------------------------------------------------
146
+
147
+
148
+ def stop_agent(nick: str) -> None:
149
+ """Stop a single agent by trying IPC shutdown first, then PID file."""
150
+ socket_path = os.path.join(
151
+ os.environ.get("XDG_RUNTIME_DIR", "/tmp"),
152
+ f"culture-{nick}.sock",
153
+ )
154
+
155
+ if _try_ipc_shutdown(nick, socket_path):
156
+ return
157
+
158
+ _try_pid_shutdown(nick)
159
+
160
+
161
+ def _try_ipc_shutdown(nick: str, socket_path: str) -> bool:
162
+ """Attempt graceful IPC shutdown. Return True if the agent stopped."""
163
+ if not os.path.exists(socket_path):
164
+ return False
165
+ try:
166
+ success = asyncio.run(ipc_shutdown(socket_path))
167
+ if not success:
168
+ return False
169
+ except Exception:
170
+ return False
171
+
172
+ print(f"Agent '{nick}' shutdown requested via IPC")
173
+ pid_name = f"agent-{nick}"
174
+ pid = read_pid(pid_name)
175
+ if not pid:
176
+ print(f"Agent '{nick}' stopped")
177
+ return True
178
+ for _ in range(50):
179
+ if not is_process_alive(pid):
180
+ remove_pid(pid_name)
181
+ print(f"Agent '{nick}' stopped")
182
+ return True
183
+ time.sleep(0.1)
184
+ return False
185
+
186
+
187
+ def _try_pid_shutdown(nick: str) -> None:
188
+ """Stop an agent via PID file with SIGTERM/SIGKILL fallback."""
189
+ pid_name = f"agent-{nick}"
190
+ pid = read_pid(pid_name)
191
+
192
+ if pid is None:
193
+ print(f"No PID file for agent '{nick}'")
194
+ return
195
+
196
+ if pid <= 0:
197
+ print(f"Invalid PID {pid} for agent '{nick}' — removing corrupt PID file")
198
+ remove_pid(pid_name)
199
+ return
200
+
201
+ if not is_process_alive(pid):
202
+ print(f"Agent '{nick}' is not running (stale PID {pid})")
203
+ remove_pid(pid_name)
204
+ return
205
+
206
+ if not is_culture_process(pid):
207
+ print(f"PID {pid} is not a culture process — removing stale PID file")
208
+ remove_pid(pid_name)
209
+ return
210
+
211
+ print(f"Stopping agent '{nick}' (PID {pid})...")
212
+ try:
213
+ os.kill(pid, signal.SIGTERM)
214
+ except ProcessLookupError:
215
+ remove_pid(pid_name)
216
+ return
217
+
218
+ for _ in range(50):
219
+ if not is_process_alive(pid):
220
+ print(f"Agent '{nick}' stopped")
221
+ remove_pid(pid_name)
222
+ return
223
+ time.sleep(0.1)
224
+
225
+ if not is_culture_process(pid):
226
+ print(f"PID {pid} is no longer a culture process — aborting kill")
227
+ remove_pid(pid_name)
228
+ return
229
+
230
+ if sys.platform == "win32":
231
+ print(f"Agent '{nick}' did not stop gracefully, terminating")
232
+ sig = signal.SIGTERM
233
+ else:
234
+ print(f"Agent '{nick}' did not stop gracefully, sending SIGKILL")
235
+ sig = signal.SIGKILL
236
+ try:
237
+ os.kill(pid, sig)
238
+ except ProcessLookupError:
239
+ pass
240
+ remove_pid(pid_name)
241
+ print(f"Agent '{nick}' killed")
242
+
243
+
244
+ # -----------------------------------------------------------------------
245
+ # Server stop helper (used by mesh.py)
246
+ # -----------------------------------------------------------------------
247
+
248
+
249
+ def server_stop_by_name(name: str) -> None:
250
+ """Stop a server by name (helper for setup --uninstall and update)."""
251
+ pid_name = f"server-{name}"
252
+ pid = read_pid(pid_name)
253
+ if not pid or not is_process_alive(pid):
254
+ if pid:
255
+ remove_pid(pid_name)
256
+ return
257
+
258
+ if not is_culture_process(pid):
259
+ remove_pid(pid_name)
260
+ return
261
+
262
+ os.kill(pid, signal.SIGTERM)
263
+ for _ in range(50):
264
+ if not is_process_alive(pid):
265
+ remove_pid(pid_name)
266
+ return
267
+ time.sleep(0.1)
268
+
269
+ if sys.platform == "win32":
270
+ try:
271
+ os.kill(pid, signal.SIGTERM)
272
+ except ProcessLookupError:
273
+ pass
274
+ else:
275
+ try:
276
+ os.kill(pid, signal.SIGKILL)
277
+ except ProcessLookupError:
278
+ pass
279
+ remove_pid(pid_name)
280
+
281
+
282
+ # -----------------------------------------------------------------------
283
+ # Mesh config helpers (used by server.py and mesh.py)
284
+ # -----------------------------------------------------------------------
285
+
286
+
287
+ def generate_mesh_from_agents(mesh_config_path: str):
288
+ """Fall back to generating mesh.yaml from agents.yaml when mesh.yaml is missing."""
289
+ from culture.mesh_config import from_daemon_config, save_mesh_config
290
+
291
+ if not os.path.isfile(DEFAULT_CONFIG):
292
+ print(f"Mesh config not found: {mesh_config_path}", file=sys.stderr)
293
+ print(f"Agent config not found either: {DEFAULT_CONFIG}", file=sys.stderr)
294
+ return None
295
+
296
+ daemon_config = load_config(DEFAULT_CONFIG)
297
+ mesh = from_daemon_config(daemon_config)
298
+ save_mesh_config(mesh, mesh_config_path)
299
+ print(f"No mesh.yaml found — generated from {DEFAULT_CONFIG}")
300
+ return mesh
301
+
302
+
303
+ def build_server_start_cmd(mesh, culture_bin: str, mesh_config_path: str) -> list[str]:
304
+ """Build the server start command with --foreground and --mesh-config."""
305
+ return [
306
+ culture_bin,
307
+ "server",
308
+ "start",
309
+ "--foreground",
310
+ "--name",
311
+ mesh.server.name,
312
+ "--host",
313
+ mesh.server.host,
314
+ "--port",
315
+ str(mesh.server.port),
316
+ "--mesh-config",
317
+ mesh_config_path,
318
+ ]
319
+
320
+
321
+ # -----------------------------------------------------------------------
322
+ # Observer helper (used by channel.py and agent.py)
323
+ # -----------------------------------------------------------------------
324
+
325
+
326
+ def get_observer(config_path: str):
327
+ """Create an IRCObserver from the config file."""
328
+ from culture.observer import IRCObserver
329
+
330
+ config = load_config_or_default(config_path)
331
+ return IRCObserver(
332
+ host=config.server.host,
333
+ port=config.server.port,
334
+ server_name=config.server.name,
335
+ )
336
+
337
+
338
+ # -----------------------------------------------------------------------
339
+ # Agent status display helpers
340
+ # -----------------------------------------------------------------------
341
+
342
+
343
+ def agent_process_status(agent) -> tuple[str, int | None]:
344
+ """Return (status_str, pid_or_none) for an agent."""
345
+ pid_name = f"agent-{agent.nick}"
346
+ pid = read_pid(pid_name)
347
+ if pid and is_process_alive(pid):
348
+ socket_path = agent_socket_path(agent.nick)
349
+ if os.path.exists(socket_path):
350
+ return "running", pid
351
+ return "starting", pid
352
+ if pid:
353
+ remove_pid(pid_name)
354
+ return "stopped", None
355
+
356
+
357
+ def print_agent_detail(agent, config_path: str, args: argparse.Namespace) -> None:
358
+ """Print detailed status for a single agent, including live IPC activity query."""
359
+ status, pid = agent_process_status(agent)
360
+ print(agent.nick)
361
+ print(f" Status: {status}")
362
+ print(f" PID: {pid or '-'}")
363
+
364
+ if status == "running":
365
+ resp = asyncio.run(ipc_request(agent_socket_path(agent.nick), "status", query=True))
366
+ if resp and resp.get("ok"):
367
+ data = resp.get("data", {})
368
+ print(f" Activity: {data.get('description', 'nothing')}")
369
+ print(f" Turns: {data.get('turn_count', 0)}")
370
+ print(f" Paused: {'yes' if data.get('paused') else 'no'}")
371
+ else:
372
+ print(" Activity: unknown (daemon may need restart)")
373
+ else:
374
+ print(" Activity: -")
375
+
376
+ channels = agent.channels if isinstance(agent.channels, list) else []
377
+ print(f" Directory: {agent.directory}")
378
+ print(f" Backend: {agent.agent}")
379
+ print(f" Channels: {', '.join(channels)}")
380
+ print(f" Model: {agent.model}")
381
+ print(f" Config: {config_path}")
382
+
383
+
384
+ def print_agents_overview(agents: list, show_activity: bool) -> None:
385
+ """Print a table of all agents with status, PID, and optionally activity."""
386
+ if show_activity:
387
+ print(f"{'NICK':<30} {'STATUS':<12} {'PID':<10} {'ACTIVITY'}")
388
+ print("-" * 72)
389
+ else:
390
+ print(f"{'NICK':<30} {'STATUS':<12} {'PID':<10}")
391
+ print("-" * 52)
392
+
393
+ for agent in agents:
394
+ status, pid = agent_process_status(agent)
395
+ activity = "-"
396
+
397
+ if show_activity and status == "running":
398
+ resp = asyncio.run(ipc_request(agent_socket_path(agent.nick), "status"))
399
+ if resp and resp.get("ok"):
400
+ activity = resp.get("data", {}).get("description", "nothing")
401
+
402
+ if show_activity:
403
+ print(f"{agent.nick:<30} {status:<12} {str(pid or '-'):<10} {activity}")
404
+ else:
405
+ print(f"{agent.nick:<30} {status:<12} {str(pid or '-'):<10}")
406
+
407
+
408
+ def print_bot_listing() -> None:
409
+ """Print a table of configured bots (if any exist)."""
410
+ from culture.bots.config import BOTS_DIR, load_bot_config
411
+
412
+ if BOTS_DIR.is_dir():
413
+ bot_configs = []
414
+ for bot_dir in sorted(BOTS_DIR.iterdir()):
415
+ yaml_path = bot_dir / "bot.yaml"
416
+ if yaml_path.is_file():
417
+ try:
418
+ bot_configs.append(load_bot_config(yaml_path))
419
+ except Exception:
420
+ pass
421
+ if bot_configs:
422
+ print()
423
+ print(f"{'BOT':<30} {'TRIGGER':<12} {'CHANNELS'}")
424
+ print("-" * 60)
425
+ for bc in bot_configs:
426
+ channels = ", ".join(bc.channels) if bc.channels else "-"
427
+ print(f"{bc.name:<30} {bc.trigger_type:<12} {channels}")