agentirc-cli 3.0.1__tar.gz → 3.1.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 (295) hide show
  1. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/CHANGELOG.md +17 -0
  2. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/CLAUDE.md +1 -1
  3. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/PKG-INFO +1 -1
  4. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/cli.py +260 -27
  5. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/config.py +68 -0
  6. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/config.py +68 -0
  7. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/config.py +68 -0
  8. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/config.py +68 -0
  9. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/pidfile.py +20 -0
  10. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/ircd.py +20 -6
  11. agentirc_cli-3.1.0/docs/server-rename.md +87 -0
  12. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/config.py +68 -0
  13. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/pyproject.toml +1 -1
  14. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_daemon_config.py +273 -0
  15. agentirc_cli-3.1.0/tests/test_wait_for_port.py +89 -0
  16. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/uv.lock +1 -1
  17. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.claude/skills/pr-review/SKILL.md +0 -0
  18. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.claude/skills/run-tests/SKILL.md +0 -0
  19. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
  20. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.flake8 +0 -0
  21. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.github/workflows/pages.yml +0 -0
  22. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.github/workflows/publish.yml +0 -0
  23. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.github/workflows/security-checks.yml +0 -0
  24. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.github/workflows/tests.yml +0 -0
  25. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.gitignore +0 -0
  26. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.markdownlint-cli2.yaml +0 -0
  27. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.pr_agent.toml +0 -0
  28. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.pre-commit-config.yaml +0 -0
  29. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.pylintrc +0 -0
  30. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/CNAME +0 -0
  31. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/Gemfile +0 -0
  32. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/Gemfile.lock +0 -0
  33. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/LICENSE +0 -0
  34. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/README.md +0 -0
  35. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/SECURITY.md +0 -0
  36. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/_config.yml +0 -0
  37. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/_sass/color_schemes/anthropic.scss +0 -0
  38. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/_sass/custom/custom.scss +0 -0
  39. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/__init__.py +0 -0
  40. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/__main__.py +0 -0
  41. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/bots/__init__.py +0 -0
  42. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/bots/bot.py +0 -0
  43. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/bots/bot_manager.py +0 -0
  44. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/bots/config.py +0 -0
  45. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/bots/http_listener.py +0 -0
  46. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/bots/template_engine.py +0 -0
  47. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/bots/virtual_client.py +0 -0
  48. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/__init__.py +0 -0
  49. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/__init__.py +0 -0
  50. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/agent_runner.py +0 -0
  51. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/daemon.py +0 -0
  52. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/ipc.py +0 -0
  53. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/irc_transport.py +0 -0
  54. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/message_buffer.py +0 -0
  55. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/skill/SKILL.md +0 -0
  56. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/skill/__init__.py +0 -0
  57. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/skill/irc_client.py +0 -0
  58. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/socket_server.py +0 -0
  59. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/supervisor.py +0 -0
  60. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/webhook.py +0 -0
  61. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/__init__.py +0 -0
  62. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/__main__.py +0 -0
  63. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/agent_runner.py +0 -0
  64. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/daemon.py +0 -0
  65. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/ipc.py +0 -0
  66. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/irc_transport.py +0 -0
  67. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/message_buffer.py +0 -0
  68. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/skill/SKILL.md +0 -0
  69. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/skill/__init__.py +0 -0
  70. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/skill/irc_client.py +0 -0
  71. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/socket_server.py +0 -0
  72. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/supervisor.py +0 -0
  73. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/webhook.py +0 -0
  74. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/__init__.py +0 -0
  75. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/agent_runner.py +0 -0
  76. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/daemon.py +0 -0
  77. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/ipc.py +0 -0
  78. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/irc_transport.py +0 -0
  79. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/message_buffer.py +0 -0
  80. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/skill/SKILL.md +0 -0
  81. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/skill/__init__.py +0 -0
  82. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/skill/irc_client.py +0 -0
  83. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/socket_server.py +0 -0
  84. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/supervisor.py +0 -0
  85. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/webhook.py +0 -0
  86. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/__init__.py +0 -0
  87. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/agent_runner.py +0 -0
  88. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/daemon.py +0 -0
  89. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/ipc.py +0 -0
  90. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/irc_transport.py +0 -0
  91. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/message_buffer.py +0 -0
  92. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/skill/SKILL.md +0 -0
  93. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/skill/__init__.py +0 -0
  94. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/skill/irc_client.py +0 -0
  95. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/socket_server.py +0 -0
  96. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/supervisor.py +0 -0
  97. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/webhook.py +0 -0
  98. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/__init__.py +0 -0
  99. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/app.py +0 -0
  100. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/client.py +0 -0
  101. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/commands.py +0 -0
  102. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/widgets/__init__.py +0 -0
  103. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/widgets/chat.py +0 -0
  104. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/widgets/info_panel.py +0 -0
  105. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/widgets/sidebar.py +0 -0
  106. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/credentials.py +0 -0
  107. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/learn_prompt.py +0 -0
  108. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/mesh_config.py +0 -0
  109. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/observer.py +0 -0
  110. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/overview/__init__.py +0 -0
  111. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/overview/collector.py +0 -0
  112. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/overview/model.py +0 -0
  113. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/overview/renderer_text.py +0 -0
  114. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/overview/renderer_web.py +0 -0
  115. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/overview/web/style.css +0 -0
  116. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/persistence.py +0 -0
  117. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/__init__.py +0 -0
  118. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/commands.py +0 -0
  119. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/extensions/federation.md +0 -0
  120. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/extensions/history.md +0 -0
  121. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/extensions/icons.md +0 -0
  122. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/extensions/rooms.md +0 -0
  123. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/extensions/tags.md +0 -0
  124. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/extensions/threads.md +0 -0
  125. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/message.py +0 -0
  126. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/protocol-index.md +0 -0
  127. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/replies.py +0 -0
  128. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/__init__.py +0 -0
  129. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/__main__.py +0 -0
  130. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/channel.py +0 -0
  131. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/client.py +0 -0
  132. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/config.py +0 -0
  133. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/remote_client.py +0 -0
  134. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/room_store.py +0 -0
  135. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/rooms_util.py +0 -0
  136. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/server_link.py +0 -0
  137. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/skill.py +0 -0
  138. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/skills/__init__.py +0 -0
  139. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/skills/history.py +0 -0
  140. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/skills/icon.py +0 -0
  141. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/skills/rooms.py +0 -0
  142. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/skills/threads.py +0 -0
  143. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/thread_store.py +0 -0
  144. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/skills/culture/SKILL.md +0 -0
  145. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/agent-lifecycle.md +0 -0
  146. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/agentic-self-learn.md +0 -0
  147. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/agent-client.md +0 -0
  148. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/agent-harness-spec.md +0 -0
  149. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/design.md +0 -0
  150. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/harness-conformance.md +0 -0
  151. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/index.md +0 -0
  152. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/layer1-core-irc.md +0 -0
  153. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/layer2-attention.md +0 -0
  154. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/layer3-skills.md +0 -0
  155. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/layer4-federation.md +0 -0
  156. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/layer5-agent-harness.md +0 -0
  157. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/server-architecture.md +0 -0
  158. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/threads.md +0 -0
  159. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/acp/overview.md +0 -0
  160. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/claude/configuration.md +0 -0
  161. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/claude/context-management.md +0 -0
  162. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/claude/irc-tools.md +0 -0
  163. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/claude/overview.md +0 -0
  164. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/claude/setup.md +0 -0
  165. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/claude/supervisor.md +0 -0
  166. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/claude/webhooks.md +0 -0
  167. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/codex/configuration.md +0 -0
  168. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/codex/context-management.md +0 -0
  169. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/codex/irc-tools.md +0 -0
  170. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/codex/overview.md +0 -0
  171. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/codex/setup.md +0 -0
  172. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/codex/supervisor.md +0 -0
  173. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/codex/webhooks.md +0 -0
  174. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/copilot/configuration.md +0 -0
  175. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/copilot/context-management.md +0 -0
  176. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/copilot/irc-tools.md +0 -0
  177. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/copilot/overview.md +0 -0
  178. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/copilot/setup.md +0 -0
  179. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/copilot/supervisor.md +0 -0
  180. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/copilot/webhooks.md +0 -0
  181. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/culture-cli.md +0 -0
  182. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/getting-started.md +0 -0
  183. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/index.md +0 -0
  184. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/SECURITY.md +0 -0
  185. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/bots.md +0 -0
  186. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/ci.md +0 -0
  187. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/cli.md +0 -0
  188. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/docs-site.md +0 -0
  189. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/index.md +0 -0
  190. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/ops-tooling.md +0 -0
  191. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/overview.md +0 -0
  192. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/publishing.md +0 -0
  193. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  194. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/rooms.md +0 -0
  195. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  196. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  197. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  198. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  199. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
  200. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
  201. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
  202. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
  203. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
  204. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  205. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  206. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  207. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  208. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
  209. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
  210. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
  211. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
  212. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
  213. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
  214. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
  215. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/01-pair-programming.md +0 -0
  216. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/02-code-review-ensemble.md +0 -0
  217. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/03-cross-server-delegation.md +0 -0
  218. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/04-knowledge-propagation.md +0 -0
  219. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/05-the-observer.md +0 -0
  220. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/06-cross-server-ops.md +0 -0
  221. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/07-supervisor-intervention.md +0 -0
  222. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/08-apps-as-agents.md +0 -0
  223. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/09-research-swarm.md +0 -0
  224. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/10-agent-lifecycle.md +0 -0
  225. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases-index.md +0 -0
  226. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/what-is-culture.md +0 -0
  227. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/README.md +0 -0
  228. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/daemon.py +0 -0
  229. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/ipc.py +0 -0
  230. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/irc_transport.py +0 -0
  231. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/message_buffer.py +0 -0
  232. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/skill/SKILL.md +0 -0
  233. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/skill/irc_client.py +0 -0
  234. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/socket_server.py +0 -0
  235. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/webhook.py +0 -0
  236. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  237. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/plugins/claude-code/skills/culture/SKILL.md +0 -0
  238. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  239. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
  240. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/sonar-project.properties +0 -0
  241. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/__init__.py +0 -0
  242. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/conftest.py +0 -0
  243. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_acp_daemon.py +0 -0
  244. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_agent_runner.py +0 -0
  245. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_bot.py +0 -0
  246. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_bot_config.py +0 -0
  247. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_bot_manager.py +0 -0
  248. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_bots_integration.py +0 -0
  249. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_channel.py +0 -0
  250. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_codex_daemon.py +0 -0
  251. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_connection.py +0 -0
  252. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_console_client.py +0 -0
  253. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_console_commands.py +0 -0
  254. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_console_connection.py +0 -0
  255. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_console_icons.py +0 -0
  256. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_console_integration.py +0 -0
  257. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_copilot_daemon.py +0 -0
  258. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_daemon.py +0 -0
  259. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_daemon_ipc.py +0 -0
  260. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_discovery.py +0 -0
  261. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_federation.py +0 -0
  262. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_history.py +0 -0
  263. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_http_listener.py +0 -0
  264. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_integration_layer5.py +0 -0
  265. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_ipc.py +0 -0
  266. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_irc_transport.py +0 -0
  267. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_link_reconnect.py +0 -0
  268. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_mentions.py +0 -0
  269. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_mesh_config.py +0 -0
  270. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_message.py +0 -0
  271. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_message_buffer.py +0 -0
  272. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_messaging.py +0 -0
  273. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_modes.py +0 -0
  274. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_overview_cli.py +0 -0
  275. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_overview_collector.py +0 -0
  276. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_overview_model.py +0 -0
  277. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_overview_renderer.py +0 -0
  278. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_overview_web.py +0 -0
  279. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_persistence.py +0 -0
  280. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_pidfile.py +0 -0
  281. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_room_persistence.py +0 -0
  282. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_rooms.py +0 -0
  283. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_rooms_federation.py +0 -0
  284. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_rooms_integration.py +0 -0
  285. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_server_icon_skill.py +0 -0
  286. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_setup_update_cli.py +0 -0
  287. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_skill_client.py +0 -0
  288. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_skills.py +0 -0
  289. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_socket_server.py +0 -0
  290. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_supervisor.py +0 -0
  291. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_template_engine.py +0 -0
  292. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_thread_buffer.py +0 -0
  293. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_threads.py +0 -0
  294. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_virtual_client.py +0 -0
  295. {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_webhook.py +0 -0
@@ -4,6 +4,23 @@ 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
+ ## [3.1.0] - 2026-04-06
8
+
9
+
10
+ ### Added
11
+
12
+ - culture server rename — rename server and all its agent nick prefixes
13
+ - culture rename — rename an agent suffix within the same server
14
+ - culture assign — move an agent to a different server
15
+
16
+ ## [3.0.2] - 2026-04-06
17
+
18
+
19
+ ### Fixed
20
+
21
+ - Server startup readiness — culture server start now waits for port to accept connections before returning
22
+ - Added startup phase logging to server log for diagnosing slow starts
23
+
7
24
  ## [3.0.1] - 2026-04-06
8
25
 
9
26
 
@@ -42,7 +42,7 @@ When implementing features, write a corresponding markdown doc in `docs/` descri
42
42
 
43
43
  ## Testing
44
44
 
45
- - `pytest` + `pytest-asyncio`
45
+ - `pytest` + `pytest-asyncio`, always run with `-n auto` for parallel execution
46
46
  - No mocks for the server — tests spin up real server instances on random ports with real TCP connections
47
47
  - Validate each layer with real IRC clients (weechat/irssi)
48
48
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 3.0.1
3
+ Version: 3.1.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
@@ -29,6 +29,7 @@ import logging
29
29
  import os
30
30
  import shutil
31
31
  import signal
32
+ import socket
32
33
  import subprocess
33
34
  import sys
34
35
  import time
@@ -79,6 +80,7 @@ def _parse_link(value: str):
79
80
 
80
81
 
81
82
  DEFAULT_CONFIG = os.path.expanduser("~/.culture/agents.yaml")
83
+ _CONFIG_HELP = "Config file path"
82
84
  LOG_DIR = os.path.expanduser("~/.culture/logs")
83
85
 
84
86
 
@@ -135,6 +137,12 @@ def _build_parser() -> argparse.ArgumentParser:
135
137
  srv_default = server_sub.add_parser("default", help="Set default server")
136
138
  srv_default.add_argument("name", help="Server name to set as default")
137
139
 
140
+ srv_rename = server_sub.add_parser(
141
+ "rename", help="Rename the server (updates config and agent nicks)"
142
+ )
143
+ srv_rename.add_argument("new_name", help="New server name")
144
+ srv_rename.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
145
+
138
146
  # -- create / join subcommands -----------------------------------------
139
147
  # 'create' registers an agent definition; 'join' adds it to the mesh.
140
148
  # 'init' is a deprecated alias for 'create'.
@@ -168,11 +176,23 @@ def _build_parser() -> argparse.ArgumentParser:
168
176
  for flag, kwargs in _agent_args:
169
177
  init_parser.add_argument(flag, **kwargs)
170
178
 
179
+ # -- rename subcommand -------------------------------------------------
180
+ rename_parser = sub.add_parser("rename", help="Rename an agent (same server)")
181
+ rename_parser.add_argument("nick", help="Current agent nick (e.g. spark-culture)")
182
+ rename_parser.add_argument("new_name", help="New agent name suffix (e.g. claude)")
183
+ rename_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
184
+
185
+ # -- assign subcommand -------------------------------------------------
186
+ assign_parser = sub.add_parser("assign", help="Move an agent to a different server")
187
+ assign_parser.add_argument("nick", help="Current agent nick (e.g. culture-culture)")
188
+ assign_parser.add_argument("server", help="Target server name (e.g. spark)")
189
+ assign_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
190
+
171
191
  # -- start subcommand --------------------------------------------------
172
192
  start_parser = sub.add_parser("start", help="Start agent daemon(s)")
173
193
  start_parser.add_argument("nick", nargs="?", help="Agent nick to start")
174
194
  start_parser.add_argument("--all", action="store_true", help="Start all agents")
175
- start_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
195
+ start_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
176
196
  start_parser.add_argument(
177
197
  "--foreground",
178
198
  action="store_true",
@@ -183,7 +203,7 @@ def _build_parser() -> argparse.ArgumentParser:
183
203
  stop_parser = sub.add_parser("stop", help="Stop agent daemon(s)")
184
204
  stop_parser.add_argument("nick", nargs="?", help="Agent nick to stop")
185
205
  stop_parser.add_argument("--all", action="store_true", help="Stop all agents")
186
- stop_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
206
+ stop_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
187
207
 
188
208
  # -- status subcommand -------------------------------------------------
189
209
  status_parser = sub.add_parser("status", help="List running agents")
@@ -191,45 +211,45 @@ def _build_parser() -> argparse.ArgumentParser:
191
211
  status_parser.add_argument(
192
212
  "--full", action="store_true", help="Query agents for activity status"
193
213
  )
194
- status_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
214
+ status_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
195
215
 
196
216
  # -- read subcommand ---------------------------------------------------
197
217
  read_parser = sub.add_parser("read", help="Read recent channel messages")
198
218
  read_parser.add_argument("channel", help="Channel name (e.g. #general)")
199
219
  read_parser.add_argument("--limit", "-n", type=int, default=50, help="Number of messages")
200
- read_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
220
+ read_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
201
221
 
202
222
  # -- who subcommand ----------------------------------------------------
203
223
  who_parser = sub.add_parser("who", help="List members of a channel")
204
224
  who_parser.add_argument("channel", help="Channel or nick target")
205
- who_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
225
+ who_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
206
226
 
207
227
  # -- send subcommand ---------------------------------------------------
208
228
  send_parser = sub.add_parser("send", help="Send a message to a channel or agent")
209
229
  send_parser.add_argument("target", help="Channel (e.g. #general) or agent nick")
210
230
  send_parser.add_argument("message", help="Message text to send")
211
- send_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
231
+ send_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
212
232
 
213
233
  # -- channels subcommand -----------------------------------------------
214
234
  channels_parser = sub.add_parser("channels", help="List active channels")
215
- channels_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
235
+ channels_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
216
236
 
217
237
  # -- learn subcommand --------------------------------------------------
218
238
  learn_parser = sub.add_parser("learn", help="Print self-teaching prompt for your agent")
219
239
  learn_parser.add_argument("--nick", default=None, help="Agent nick (auto-detects from cwd)")
220
- learn_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
240
+ learn_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
221
241
 
222
242
  # -- sleep subcommand --------------------------------------------------
223
243
  sleep_parser = sub.add_parser("sleep", help="Pause agent(s) — stay connected but idle")
224
244
  sleep_parser.add_argument("nick", nargs="?", help="Agent nick to pause")
225
245
  sleep_parser.add_argument("--all", action="store_true", help="Pause all agents")
226
- sleep_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
246
+ sleep_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
227
247
 
228
248
  # -- wake subcommand ---------------------------------------------------
229
249
  wake_parser = sub.add_parser("wake", help="Resume paused agent(s)")
230
250
  wake_parser.add_argument("nick", nargs="?", help="Agent nick to resume")
231
251
  wake_parser.add_argument("--all", action="store_true", help="Resume all agents")
232
- wake_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
252
+ wake_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
233
253
 
234
254
  # -- skills subcommand -------------------------------------------------
235
255
  skills_parser = sub.add_parser("skills", help="Install IRC skills for AI agents")
@@ -303,22 +323,22 @@ def _build_parser() -> argparse.ArgumentParser:
303
323
  bot_create.add_argument("--template", default=None, help="Message template")
304
324
  bot_create.add_argument("--dm-owner", action="store_true", help="DM the owner on trigger")
305
325
  bot_create.add_argument("--description", default="", help="Bot description")
306
- bot_create.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
326
+ bot_create.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
307
327
 
308
328
  bot_start = bot_sub.add_parser("start", help="Start a bot")
309
329
  bot_start.add_argument("name", help="Bot name")
310
- bot_start.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
330
+ bot_start.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
311
331
 
312
332
  bot_stop = bot_sub.add_parser("stop", help="Stop a bot")
313
333
  bot_stop.add_argument("name", help="Bot name")
314
- bot_stop.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
334
+ bot_stop.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
315
335
 
316
336
  bot_list = bot_sub.add_parser("list", help="List bots")
317
337
  bot_list.add_argument("owner", nargs="?", default=None, help="Filter by owner nick")
318
338
 
319
339
  bot_inspect = bot_sub.add_parser("inspect", help="Show bot details")
320
340
  bot_inspect.add_argument("name", help="Bot name")
321
- bot_inspect.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
341
+ bot_inspect.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
322
342
 
323
343
  # -- console subcommand ------------------------------------------------
324
344
  console_parser = sub.add_parser("console", help="Interactive admin console")
@@ -331,7 +351,7 @@ def _build_parser() -> argparse.ArgumentParser:
331
351
  console_parser.add_argument(
332
352
  "--config",
333
353
  default=DEFAULT_CONFIG,
334
- help="Config file path",
354
+ help=_CONFIG_HELP,
335
355
  )
336
356
 
337
357
  return parser
@@ -356,6 +376,8 @@ def main() -> None:
356
376
  "create": _cmd_init,
357
377
  "join": _cmd_join,
358
378
  "init": _cmd_init_deprecated,
379
+ "rename": _cmd_rename,
380
+ "assign": _cmd_assign,
359
381
  "start": _cmd_start,
360
382
  "stop": _cmd_stop,
361
383
  "status": _cmd_status,
@@ -491,6 +513,98 @@ def _cmd_server(args: argparse.Namespace) -> None:
491
513
 
492
514
  write_default_server(args.name)
493
515
  print(f"Default server set to '{args.name}'")
516
+ elif args.server_command == "rename":
517
+ _server_rename(args)
518
+
519
+
520
+ def _server_rename(args: argparse.Namespace) -> None:
521
+ """Rename the server: update config, agent nicks, and PID files."""
522
+ from culture.clients.claude.config import rename_server, sanitize_agent_name
523
+ from culture.pidfile import (
524
+ is_process_alive,
525
+ read_default_server,
526
+ read_pid,
527
+ rename_pid,
528
+ write_default_server,
529
+ )
530
+
531
+ try:
532
+ new_name = sanitize_agent_name(args.new_name)
533
+ except ValueError:
534
+ print(f"Invalid server name: {args.new_name!r}", file=sys.stderr)
535
+ sys.exit(1)
536
+
537
+ try:
538
+ old_name, renamed = rename_server(args.config, new_name)
539
+ except ValueError as exc:
540
+ print(str(exc), file=sys.stderr)
541
+ sys.exit(1)
542
+
543
+ if old_name == new_name:
544
+ print(f"Server is already named '{new_name}'")
545
+ return
546
+
547
+ # Rename PID/port files for the server daemon
548
+ rename_pid(f"server-{old_name}", f"server-{new_name}")
549
+
550
+ # Rename PID files for agents
551
+ for old_nick, new_nick in renamed:
552
+ rename_pid(f"agent-{old_nick}", f"agent-{new_nick}")
553
+
554
+ # Update default server if it pointed to the old name
555
+ if read_default_server() == old_name:
556
+ write_default_server(new_name)
557
+
558
+ print(f"Server renamed: {old_name} → {new_name}")
559
+ for old_nick, new_nick in renamed:
560
+ print(f" Agent: {old_nick} → {new_nick}")
561
+
562
+ # Check if the server process is still running
563
+ server_pid = read_pid(f"server-{new_name}")
564
+ server_running = server_pid and is_process_alive(server_pid)
565
+
566
+ print()
567
+ if server_running:
568
+ print("The server is still running under the old name.")
569
+ print("Restart it for the rename to take effect:")
570
+ print(f" culture server stop --name {new_name}")
571
+ print(f" culture server start --name {new_name}")
572
+ if renamed:
573
+ print("Restart agents for the new nicks to take effect:")
574
+ print(" culture stop --all && culture start --all")
575
+
576
+
577
+ def _wait_for_port(
578
+ host: str,
579
+ port: int,
580
+ pid: int,
581
+ timeout: float = 30,
582
+ ) -> tuple[bool, str]:
583
+ """Poll *host*:*port* until a TCP connect succeeds or *timeout* expires.
584
+
585
+ Returns ``(True, "")`` on success, or ``(False, reason)`` on failure.
586
+ Checks that *pid* is still alive on every iteration so we fail fast if
587
+ the child crashes (e.g. because the port was already in use).
588
+ """
589
+ check_host = "127.0.0.1" if host == "0.0.0.0" else host
590
+ deadline = time.monotonic() + timeout
591
+ while time.monotonic() < deadline:
592
+ if not is_process_alive(pid):
593
+ return False, "failed to start"
594
+ try:
595
+ s = socket.create_connection((check_host, port), timeout=0.5)
596
+ s.close()
597
+ except OSError:
598
+ time.sleep(0.2)
599
+ continue
600
+ # Port responded — give the child a moment, then confirm it's
601
+ # still alive (guards against connecting to a *stale* listener
602
+ # on the same port while our child crashes).
603
+ time.sleep(0.1)
604
+ if not is_process_alive(pid):
605
+ return False, "failed to start"
606
+ return True, ""
607
+ return False, "started but not yet accepting connections"
494
608
 
495
609
 
496
610
  def _server_start(args: argparse.Namespace) -> None:
@@ -532,19 +646,33 @@ def _server_start(args: argparse.Namespace) -> None:
532
646
  # Fork to daemonize
533
647
  pid = os.fork()
534
648
  if pid > 0:
535
- time.sleep(0.2)
536
- if is_process_alive(pid):
537
- print(f"Server '{args.name}' started (PID {pid})")
538
- print(f" Listening on {args.host}:{args.port}")
539
- print(f" Logs: {LOG_DIR}/server-{args.name}.log")
540
- # Auto-set default server if none is set
541
- from culture.pidfile import read_default_server, write_default_server
542
-
543
- if read_default_server() is None:
544
- write_default_server(args.name)
649
+ log_hint = f"{LOG_DIR}/server-{args.name}.log"
650
+
651
+ if args.port == 0:
652
+ # Ephemeral port — can't probe; fall back to process-alive check
653
+ time.sleep(0.5)
654
+ if not is_process_alive(pid):
655
+ print(f"Server '{args.name}' failed to start", file=sys.stderr)
656
+ print(f" Check logs: {log_hint}", file=sys.stderr)
657
+ sys.exit(1)
545
658
  else:
546
- print(f"Server '{args.name}' failed to start", file=sys.stderr)
547
- sys.exit(1)
659
+ ok, err = _wait_for_port(args.host, args.port, pid, timeout=30)
660
+ if not ok:
661
+ print(
662
+ f"Server '{args.name}' {err}",
663
+ file=sys.stderr,
664
+ )
665
+ print(f" Check logs: {log_hint}", file=sys.stderr)
666
+ sys.exit(1)
667
+
668
+ print(f"Server '{args.name}' started (PID {pid})")
669
+ print(f" Listening on {args.host}:{args.port}")
670
+ print(f" Logs: {log_hint}")
671
+ # Auto-set default server if none is set
672
+ from culture.pidfile import read_default_server, write_default_server
673
+
674
+ if read_default_server() is None:
675
+ write_default_server(args.name)
548
676
  return
549
677
 
550
678
  # Child: detach from parent session
@@ -562,6 +690,13 @@ def _server_start(args: argparse.Namespace) -> None:
562
690
  os.dup2(devnull, 0)
563
691
  os.close(devnull)
564
692
 
693
+ # Reconfigure logging so handlers write to the redirected stderr (log file)
694
+ logging.basicConfig(
695
+ level=logging.INFO,
696
+ format="%(asctime)s %(name)s %(levelname)s %(message)s",
697
+ force=True,
698
+ )
699
+
565
700
  write_pid(pid_name, os.getpid())
566
701
 
567
702
  try:
@@ -785,6 +920,104 @@ def _cmd_init(args: argparse.Namespace) -> None:
785
920
  print(f"Or join the mesh: culture join {full_nick}")
786
921
 
787
922
 
923
+ def _cmd_rename(args: argparse.Namespace) -> None:
924
+ """Rename an agent's suffix within the same server."""
925
+ from culture.clients.claude.config import (
926
+ load_config_or_default,
927
+ rename_agent,
928
+ sanitize_agent_name,
929
+ )
930
+ from culture.pidfile import rename_pid
931
+
932
+ config = load_config_or_default(args.config)
933
+ old_nick = args.nick
934
+ server_name = config.server.name
935
+ expected_prefix = f"{server_name}-"
936
+
937
+ if not old_nick.startswith(expected_prefix):
938
+ print(
939
+ f"Agent '{old_nick}' does not belong to server '{server_name}'",
940
+ file=sys.stderr,
941
+ )
942
+ sys.exit(1)
943
+
944
+ try:
945
+ new_suffix = sanitize_agent_name(args.new_name)
946
+ except ValueError:
947
+ print(f"Invalid agent name: {args.new_name!r}", file=sys.stderr)
948
+ sys.exit(1)
949
+
950
+ new_nick = f"{server_name}-{new_suffix}"
951
+
952
+ if old_nick == new_nick:
953
+ print(f"Agent is already named '{old_nick}'")
954
+ return
955
+
956
+ try:
957
+ rename_agent(args.config, old_nick, new_nick)
958
+ except ValueError as exc:
959
+ print(str(exc), file=sys.stderr)
960
+ sys.exit(1)
961
+
962
+ rename_pid(f"agent-{old_nick}", f"agent-{new_nick}")
963
+
964
+ print(f"Agent renamed: {old_nick} → {new_nick}")
965
+ print()
966
+ print("Restart the agent for the new nick to take effect:")
967
+ print(f" culture stop {old_nick} # if still running under old name")
968
+ print(f" culture start {new_nick}")
969
+
970
+
971
+ def _cmd_assign(args: argparse.Namespace) -> None:
972
+ """Move an agent to a different server (change nick prefix)."""
973
+ from culture.clients.claude.config import (
974
+ load_config_or_default,
975
+ rename_agent,
976
+ sanitize_agent_name,
977
+ )
978
+ from culture.pidfile import rename_pid
979
+
980
+ config = load_config_or_default(args.config)
981
+ old_nick = args.nick
982
+ server_name = config.server.name
983
+ expected_prefix = f"{server_name}-"
984
+
985
+ if not old_nick.startswith(expected_prefix):
986
+ print(
987
+ f"Agent '{old_nick}' does not belong to server '{server_name}'",
988
+ file=sys.stderr,
989
+ )
990
+ sys.exit(1)
991
+
992
+ suffix = old_nick[len(expected_prefix) :]
993
+
994
+ try:
995
+ new_server = sanitize_agent_name(args.server)
996
+ except ValueError:
997
+ print(f"Invalid server name: {args.server!r}", file=sys.stderr)
998
+ sys.exit(1)
999
+
1000
+ new_nick = f"{new_server}-{suffix}"
1001
+
1002
+ if old_nick == new_nick:
1003
+ print(f"Agent already belongs to server '{new_server}'")
1004
+ return
1005
+
1006
+ try:
1007
+ rename_agent(args.config, old_nick, new_nick)
1008
+ except ValueError as exc:
1009
+ print(str(exc), file=sys.stderr)
1010
+ sys.exit(1)
1011
+
1012
+ rename_pid(f"agent-{old_nick}", f"agent-{new_nick}")
1013
+
1014
+ print(f"Agent reassigned: {old_nick} → {new_nick}")
1015
+ print()
1016
+ print("Restart the agent for the new nick to take effect:")
1017
+ print(f" culture stop {old_nick} # if still running under old name")
1018
+ print(f" culture start {new_nick}")
1019
+
1020
+
788
1021
  # -----------------------------------------------------------------------
789
1022
  # Agent start
790
1023
  # -----------------------------------------------------------------------
@@ -178,3 +178,71 @@ def add_agent_to_config(
178
178
  config.agents.append(agent)
179
179
  save_config(path, config)
180
180
  return config
181
+
182
+
183
+ def rename_server(
184
+ path: str | Path,
185
+ new_name: str,
186
+ ) -> tuple[str, list[tuple[str, str]]]:
187
+ """Rename the server and update all agent nick prefixes.
188
+
189
+ Returns (old_name, [(old_nick, new_nick), ...]).
190
+ """
191
+ config = load_config_or_default(path)
192
+ old_name = config.server.name
193
+
194
+ if old_name == new_name:
195
+ return old_name, []
196
+
197
+ # Plan renames and check for collisions before mutating
198
+ prefix = f"{old_name}-"
199
+ plan: list[tuple[int, str, str]] = []
200
+ for i, agent in enumerate(config.agents):
201
+ if agent.nick.startswith(prefix):
202
+ new_nick = f"{new_name}-{agent.nick[len(prefix):]}"
203
+ plan.append((i, agent.nick, new_nick))
204
+
205
+ planned_nicks = {new_nick for _, _, new_nick in plan}
206
+ existing_nicks = {a.nick for a in config.agents} - {old for _, old, _ in plan}
207
+ collisions = planned_nicks & existing_nicks
208
+ if collisions:
209
+ raise ValueError(
210
+ f"renaming server {old_name!r} to {new_name!r} would create "
211
+ f"duplicate nick(s): {', '.join(sorted(collisions))}"
212
+ )
213
+
214
+ config.server.name = new_name
215
+
216
+ renamed: list[tuple[str, str]] = []
217
+ for i, old_nick, new_nick in plan:
218
+ config.agents[i].nick = new_nick
219
+ renamed.append((old_nick, new_nick))
220
+
221
+ save_config(path, config)
222
+ return old_name, renamed
223
+
224
+
225
+ def rename_agent(
226
+ path: str | Path,
227
+ old_nick: str,
228
+ new_nick: str,
229
+ ) -> None:
230
+ """Rename an agent's nick in the config.
231
+
232
+ Raises ValueError if old_nick is not found or new_nick already exists.
233
+ """
234
+ config = load_config_or_default(path)
235
+
236
+ # Check new nick doesn't collide
237
+ for agent in config.agents:
238
+ if agent.nick == new_nick:
239
+ raise ValueError(f"agent with nick {new_nick!r} already exists in config")
240
+
241
+ # Find and rename
242
+ for agent in config.agents:
243
+ if agent.nick == old_nick:
244
+ agent.nick = new_nick
245
+ save_config(path, config)
246
+ return
247
+
248
+ raise ValueError(f"agent {old_nick!r} not found in config")
@@ -182,3 +182,71 @@ def add_agent_to_config(
182
182
  config.agents.append(agent)
183
183
  save_config(path, config)
184
184
  return config
185
+
186
+
187
+ def rename_server(
188
+ path: str | Path,
189
+ new_name: str,
190
+ ) -> tuple[str, list[tuple[str, str]]]:
191
+ """Rename the server and update all agent nick prefixes.
192
+
193
+ Returns (old_name, [(old_nick, new_nick), ...]).
194
+ """
195
+ config = load_config_or_default(path)
196
+ old_name = config.server.name
197
+
198
+ if old_name == new_name:
199
+ return old_name, []
200
+
201
+ # Plan renames and check for collisions before mutating
202
+ prefix = f"{old_name}-"
203
+ plan: list[tuple[int, str, str]] = []
204
+ for i, agent in enumerate(config.agents):
205
+ if agent.nick.startswith(prefix):
206
+ new_nick = f"{new_name}-{agent.nick[len(prefix):]}"
207
+ plan.append((i, agent.nick, new_nick))
208
+
209
+ planned_nicks = {new_nick for _, _, new_nick in plan}
210
+ existing_nicks = {a.nick for a in config.agents} - {old for _, old, _ in plan}
211
+ collisions = planned_nicks & existing_nicks
212
+ if collisions:
213
+ raise ValueError(
214
+ f"renaming server {old_name!r} to {new_name!r} would create "
215
+ f"duplicate nick(s): {', '.join(sorted(collisions))}"
216
+ )
217
+
218
+ config.server.name = new_name
219
+
220
+ renamed: list[tuple[str, str]] = []
221
+ for i, old_nick, new_nick in plan:
222
+ config.agents[i].nick = new_nick
223
+ renamed.append((old_nick, new_nick))
224
+
225
+ save_config(path, config)
226
+ return old_name, renamed
227
+
228
+
229
+ def rename_agent(
230
+ path: str | Path,
231
+ old_nick: str,
232
+ new_nick: str,
233
+ ) -> None:
234
+ """Rename an agent's nick in the config.
235
+
236
+ Raises ValueError if old_nick is not found or new_nick already exists.
237
+ """
238
+ config = load_config_or_default(path)
239
+
240
+ # Check new nick doesn't collide
241
+ for agent in config.agents:
242
+ if agent.nick == new_nick:
243
+ raise ValueError(f"agent with nick {new_nick!r} already exists in config")
244
+
245
+ # Find and rename
246
+ for agent in config.agents:
247
+ if agent.nick == old_nick:
248
+ agent.nick = new_nick
249
+ save_config(path, config)
250
+ return
251
+
252
+ raise ValueError(f"agent {old_nick!r} not found in config")
@@ -178,3 +178,71 @@ def add_agent_to_config(
178
178
  config.agents.append(agent)
179
179
  save_config(path, config)
180
180
  return config
181
+
182
+
183
+ def rename_server(
184
+ path: str | Path,
185
+ new_name: str,
186
+ ) -> tuple[str, list[tuple[str, str]]]:
187
+ """Rename the server and update all agent nick prefixes.
188
+
189
+ Returns (old_name, [(old_nick, new_nick), ...]).
190
+ """
191
+ config = load_config_or_default(path)
192
+ old_name = config.server.name
193
+
194
+ if old_name == new_name:
195
+ return old_name, []
196
+
197
+ # Plan renames and check for collisions before mutating
198
+ prefix = f"{old_name}-"
199
+ plan: list[tuple[int, str, str]] = []
200
+ for i, agent in enumerate(config.agents):
201
+ if agent.nick.startswith(prefix):
202
+ new_nick = f"{new_name}-{agent.nick[len(prefix):]}"
203
+ plan.append((i, agent.nick, new_nick))
204
+
205
+ planned_nicks = {new_nick for _, _, new_nick in plan}
206
+ existing_nicks = {a.nick for a in config.agents} - {old for _, old, _ in plan}
207
+ collisions = planned_nicks & existing_nicks
208
+ if collisions:
209
+ raise ValueError(
210
+ f"renaming server {old_name!r} to {new_name!r} would create "
211
+ f"duplicate nick(s): {', '.join(sorted(collisions))}"
212
+ )
213
+
214
+ config.server.name = new_name
215
+
216
+ renamed: list[tuple[str, str]] = []
217
+ for i, old_nick, new_nick in plan:
218
+ config.agents[i].nick = new_nick
219
+ renamed.append((old_nick, new_nick))
220
+
221
+ save_config(path, config)
222
+ return old_name, renamed
223
+
224
+
225
+ def rename_agent(
226
+ path: str | Path,
227
+ old_nick: str,
228
+ new_nick: str,
229
+ ) -> None:
230
+ """Rename an agent's nick in the config.
231
+
232
+ Raises ValueError if old_nick is not found or new_nick already exists.
233
+ """
234
+ config = load_config_or_default(path)
235
+
236
+ # Check new nick doesn't collide
237
+ for agent in config.agents:
238
+ if agent.nick == new_nick:
239
+ raise ValueError(f"agent with nick {new_nick!r} already exists in config")
240
+
241
+ # Find and rename
242
+ for agent in config.agents:
243
+ if agent.nick == old_nick:
244
+ agent.nick = new_nick
245
+ save_config(path, config)
246
+ return
247
+
248
+ raise ValueError(f"agent {old_nick!r} not found in config")