agentirc-cli 1.0.2__tar.gz → 1.0.4__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 (267) hide show
  1. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/.github/workflows/publish.yml +1 -1
  2. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/.github/workflows/security-checks.yml +0 -4
  3. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/.github/workflows/tests.yml +1 -1
  4. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/CHANGELOG.md +14 -0
  5. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/PKG-INFO +1 -1
  6. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/cli.py +199 -141
  7. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/acp/daemon.py +78 -95
  8. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/claude/daemon.py +82 -101
  9. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/codex/daemon.py +78 -95
  10. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/copilot/daemon.py +78 -95
  11. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/ircd.py +62 -35
  12. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/server_link.py +129 -87
  13. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/skills/threads.py +161 -118
  14. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/packages/agent-harness/daemon.py +108 -101
  15. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/pyproject.toml +2 -1
  16. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/sonar-project.properties +4 -0
  17. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_daemon_ipc.py +6 -6
  18. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/uv.lock +25 -1
  19. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/.claude/skills/pr-review/SKILL.md +0 -0
  20. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/.flake8 +0 -0
  21. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/.github/workflows/pages.yml +0 -0
  22. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/.gitignore +0 -0
  23. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/.markdownlint-cli2.yaml +0 -0
  24. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/.pr_agent.toml +0 -0
  25. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/.pre-commit-config.yaml +0 -0
  26. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/.pylintrc +0 -0
  27. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/CLAUDE.md +0 -0
  28. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/CNAME +0 -0
  29. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/Gemfile +0 -0
  30. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/Gemfile.lock +0 -0
  31. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/LICENSE +0 -0
  32. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/README.md +0 -0
  33. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/SECURITY.md +0 -0
  34. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/_config.yml +0 -0
  35. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/_sass/color_schemes/anthropic.scss +0 -0
  36. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/_sass/custom/custom.scss +0 -0
  37. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/__init__.py +0 -0
  38. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/__main__.py +0 -0
  39. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/bots/__init__.py +0 -0
  40. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/bots/bot.py +0 -0
  41. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/bots/bot_manager.py +0 -0
  42. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/bots/config.py +0 -0
  43. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/bots/http_listener.py +0 -0
  44. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/bots/template_engine.py +0 -0
  45. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/bots/virtual_client.py +0 -0
  46. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/__init__.py +0 -0
  47. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/acp/__init__.py +0 -0
  48. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/acp/agent_runner.py +0 -0
  49. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/acp/config.py +0 -0
  50. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/acp/ipc.py +0 -0
  51. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/acp/irc_transport.py +0 -0
  52. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/acp/message_buffer.py +0 -0
  53. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/acp/skill/SKILL.md +0 -0
  54. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/acp/skill/__init__.py +0 -0
  55. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/acp/skill/irc_client.py +0 -0
  56. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/acp/socket_server.py +0 -0
  57. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/acp/supervisor.py +0 -0
  58. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/acp/webhook.py +0 -0
  59. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/claude/__init__.py +0 -0
  60. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/claude/__main__.py +0 -0
  61. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/claude/agent_runner.py +0 -0
  62. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/claude/config.py +0 -0
  63. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/claude/ipc.py +0 -0
  64. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/claude/irc_transport.py +0 -0
  65. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/claude/message_buffer.py +0 -0
  66. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/claude/skill/SKILL.md +0 -0
  67. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/claude/skill/__init__.py +0 -0
  68. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/claude/skill/irc_client.py +0 -0
  69. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/claude/socket_server.py +0 -0
  70. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/claude/supervisor.py +0 -0
  71. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/claude/webhook.py +0 -0
  72. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/codex/__init__.py +0 -0
  73. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/codex/agent_runner.py +0 -0
  74. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/codex/config.py +0 -0
  75. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/codex/ipc.py +0 -0
  76. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/codex/irc_transport.py +0 -0
  77. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/codex/message_buffer.py +0 -0
  78. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/codex/skill/SKILL.md +0 -0
  79. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/codex/skill/__init__.py +0 -0
  80. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/codex/skill/irc_client.py +0 -0
  81. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/codex/socket_server.py +0 -0
  82. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/codex/supervisor.py +0 -0
  83. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/codex/webhook.py +0 -0
  84. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/copilot/__init__.py +0 -0
  85. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/copilot/agent_runner.py +0 -0
  86. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/copilot/config.py +0 -0
  87. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/copilot/ipc.py +0 -0
  88. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/copilot/irc_transport.py +0 -0
  89. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/copilot/message_buffer.py +0 -0
  90. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/copilot/skill/SKILL.md +0 -0
  91. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/copilot/skill/__init__.py +0 -0
  92. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/copilot/skill/irc_client.py +0 -0
  93. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/copilot/socket_server.py +0 -0
  94. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/copilot/supervisor.py +0 -0
  95. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/clients/copilot/webhook.py +0 -0
  96. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/credentials.py +0 -0
  97. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/learn_prompt.py +0 -0
  98. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/mesh_config.py +0 -0
  99. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/observer.py +0 -0
  100. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/overview/__init__.py +0 -0
  101. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/overview/collector.py +0 -0
  102. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/overview/model.py +0 -0
  103. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/overview/renderer_text.py +0 -0
  104. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/overview/renderer_web.py +0 -0
  105. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/overview/web/style.css +0 -0
  106. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/persistence.py +0 -0
  107. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/pidfile.py +0 -0
  108. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/protocol/__init__.py +0 -0
  109. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/protocol/commands.py +0 -0
  110. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/protocol/extensions/federation.md +0 -0
  111. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/protocol/extensions/history.md +0 -0
  112. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/protocol/extensions/rooms.md +0 -0
  113. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/protocol/extensions/tags.md +0 -0
  114. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/protocol/extensions/threads.md +0 -0
  115. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/protocol/message.py +0 -0
  116. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/protocol/protocol-index.md +0 -0
  117. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/protocol/replies.py +0 -0
  118. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/__init__.py +0 -0
  119. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/__main__.py +0 -0
  120. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/channel.py +0 -0
  121. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/client.py +0 -0
  122. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/config.py +0 -0
  123. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/remote_client.py +0 -0
  124. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/room_store.py +0 -0
  125. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/rooms_util.py +0 -0
  126. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/skill.py +0 -0
  127. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/skills/__init__.py +0 -0
  128. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/skills/history.py +0 -0
  129. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/skills/rooms.py +0 -0
  130. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/server/thread_store.py +0 -0
  131. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/culture/skills/culture/SKILL.md +0 -0
  132. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/SECURITY.md +0 -0
  133. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/agent-client.md +0 -0
  134. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/agent-harness-spec.md +0 -0
  135. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/agentic-self-learn.md +0 -0
  136. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/bots.md +0 -0
  137. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/ci.md +0 -0
  138. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/cli.md +0 -0
  139. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/acp/overview.md +0 -0
  140. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/claude/configuration.md +0 -0
  141. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/claude/context-management.md +0 -0
  142. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/claude/irc-tools.md +0 -0
  143. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/claude/overview.md +0 -0
  144. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/claude/setup.md +0 -0
  145. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/claude/supervisor.md +0 -0
  146. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/claude/webhooks.md +0 -0
  147. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/codex/configuration.md +0 -0
  148. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/codex/context-management.md +0 -0
  149. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/codex/irc-tools.md +0 -0
  150. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/codex/overview.md +0 -0
  151. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/codex/setup.md +0 -0
  152. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/codex/supervisor.md +0 -0
  153. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/codex/webhooks.md +0 -0
  154. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/copilot/configuration.md +0 -0
  155. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/copilot/context-management.md +0 -0
  156. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/copilot/irc-tools.md +0 -0
  157. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/copilot/overview.md +0 -0
  158. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/copilot/setup.md +0 -0
  159. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/copilot/supervisor.md +0 -0
  160. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/clients/copilot/webhooks.md +0 -0
  161. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/codex-backend.md +0 -0
  162. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/copilot-backend.md +0 -0
  163. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/design.md +0 -0
  164. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/docs-site.md +0 -0
  165. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/getting-started.md +0 -0
  166. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/grow-your-agent.md +0 -0
  167. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/harness-conformance.md +0 -0
  168. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/layer1-core-irc.md +0 -0
  169. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/layer2-attention.md +0 -0
  170. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/layer3-skills.md +0 -0
  171. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/layer4-federation.md +0 -0
  172. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/layer5-agent-harness.md +0 -0
  173. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/ops-tooling.md +0 -0
  174. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/overview.md +0 -0
  175. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/publishing.md +0 -0
  176. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  177. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/rooms.md +0 -0
  178. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/server-architecture.md +0 -0
  179. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  180. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  181. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  182. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  183. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
  184. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
  185. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
  186. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  187. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  188. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  189. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  190. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
  191. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
  192. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
  193. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
  194. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/threads.md +0 -0
  195. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/use-cases/01-pair-programming.md +0 -0
  196. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/use-cases/02-code-review-ensemble.md +0 -0
  197. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/use-cases/03-cross-server-delegation.md +0 -0
  198. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/use-cases/04-knowledge-propagation.md +0 -0
  199. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/use-cases/05-the-observer.md +0 -0
  200. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/use-cases/06-cross-server-ops.md +0 -0
  201. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/use-cases/07-supervisor-intervention.md +0 -0
  202. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/use-cases/08-apps-as-agents.md +0 -0
  203. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/use-cases/09-research-swarm.md +0 -0
  204. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/use-cases/10-grow-your-agent.md +0 -0
  205. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/docs/use-cases-index.md +0 -0
  206. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/index.md +0 -0
  207. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/packages/agent-harness/README.md +0 -0
  208. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/packages/agent-harness/config.py +0 -0
  209. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/packages/agent-harness/ipc.py +0 -0
  210. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/packages/agent-harness/irc_transport.py +0 -0
  211. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/packages/agent-harness/message_buffer.py +0 -0
  212. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/packages/agent-harness/skill/SKILL.md +0 -0
  213. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/packages/agent-harness/skill/irc_client.py +0 -0
  214. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/packages/agent-harness/socket_server.py +0 -0
  215. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/packages/agent-harness/webhook.py +0 -0
  216. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  217. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/plugins/claude-code/skills/culture/SKILL.md +0 -0
  218. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  219. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
  220. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/__init__.py +0 -0
  221. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/conftest.py +0 -0
  222. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_acp_daemon.py +0 -0
  223. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_agent_runner.py +0 -0
  224. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_bot.py +0 -0
  225. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_bot_config.py +0 -0
  226. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_bot_manager.py +0 -0
  227. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_bots_integration.py +0 -0
  228. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_channel.py +0 -0
  229. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_codex_daemon.py +0 -0
  230. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_connection.py +0 -0
  231. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_copilot_daemon.py +0 -0
  232. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_daemon.py +0 -0
  233. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_daemon_config.py +0 -0
  234. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_discovery.py +0 -0
  235. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_federation.py +0 -0
  236. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_history.py +0 -0
  237. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_http_listener.py +0 -0
  238. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_integration_layer5.py +0 -0
  239. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_ipc.py +0 -0
  240. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_irc_transport.py +0 -0
  241. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_link_reconnect.py +0 -0
  242. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_mentions.py +0 -0
  243. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_mesh_config.py +0 -0
  244. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_message.py +0 -0
  245. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_message_buffer.py +0 -0
  246. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_messaging.py +0 -0
  247. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_modes.py +0 -0
  248. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_overview_cli.py +0 -0
  249. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_overview_collector.py +0 -0
  250. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_overview_model.py +0 -0
  251. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_overview_renderer.py +0 -0
  252. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_overview_web.py +0 -0
  253. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_persistence.py +0 -0
  254. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_room_persistence.py +0 -0
  255. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_rooms.py +0 -0
  256. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_rooms_federation.py +0 -0
  257. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_rooms_integration.py +0 -0
  258. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_setup_update_cli.py +0 -0
  259. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_skill_client.py +0 -0
  260. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_skills.py +0 -0
  261. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_socket_server.py +0 -0
  262. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_supervisor.py +0 -0
  263. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_template_engine.py +0 -0
  264. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_thread_buffer.py +0 -0
  265. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_threads.py +0 -0
  266. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_virtual_client.py +0 -0
  267. {agentirc_cli-1.0.2 → agentirc_cli-1.0.4}/tests/test_webhook.py +0 -0
@@ -27,7 +27,7 @@ jobs:
27
27
 
28
28
  - run: uv sync
29
29
 
30
- - run: uv run pytest -v
30
+ - run: uv run pytest -n auto -v
31
31
 
32
32
  test-publish:
33
33
  if: github.event_name == 'pull_request'
@@ -46,10 +46,6 @@ jobs:
46
46
  pylint-results.json
47
47
  safety-results.json
48
48
 
49
- - name: Run test coverage
50
- run: |
51
- uv run pytest --cov=culture --cov-report=xml:coverage.xml --cov-report=term -v
52
- continue-on-error: true
53
49
 
54
50
  dependency-review:
55
51
  name: Dependency Review
@@ -19,7 +19,7 @@ jobs:
19
19
 
20
20
  - run: uv sync
21
21
 
22
- - run: uv run pytest -v
22
+ - run: uv run pytest -n auto --cov=culture --cov-report=xml:coverage.xml --cov-report=term -v
23
23
 
24
24
  version-check:
25
25
  runs-on: ubuntu-latest
@@ -4,6 +4,20 @@ 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
+ ## [1.0.4] - 2026-04-05
8
+
9
+
10
+ ### Changed
11
+
12
+ - Reduced cognitive complexity of 76 high-complexity functions across daemon.py (5 files), server_link.py, threads.py, cli.py, and ircd.py by replacing if/elif chains with dispatch tables and extracting named logic units
13
+
14
+ ## [1.0.3] - 2026-04-05
15
+
16
+
17
+ ### Changed
18
+
19
+ - Parallelize test suite with pytest-xdist for ~15x speedup (10min → 40s)
20
+
7
21
  ## [1.0.2] - 2026-04-05
8
22
 
9
23
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 1.0.2
3
+ Version: 1.0.4
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
@@ -622,9 +622,8 @@ def _cmd_init(args: argparse.Namespace) -> None:
622
622
  # -----------------------------------------------------------------------
623
623
 
624
624
 
625
- def _cmd_start(args: argparse.Namespace) -> None:
626
- config = load_config(args.config)
627
-
625
+ def _resolve_agents_to_start(config, args) -> list:
626
+ """Return the list of agents to start, or exit with an error message."""
628
627
  if args.all:
629
628
  agents = config.agents
630
629
  elif args.nick:
@@ -653,11 +652,13 @@ def _cmd_start(args: argparse.Namespace) -> None:
653
652
  print("No agents configured", file=sys.stderr)
654
653
  sys.exit(1)
655
654
 
656
- # Best-effort check that the IRC server is reachable before starting agent(s)
655
+ return agents
656
+
657
+
658
+ def _probe_server_connection(host: str, port: int, server_name: str) -> None:
659
+ """Check that the IRC server is reachable; exit with an error message if not."""
657
660
  import socket as _socket
658
661
 
659
- server_name = config.server.name
660
- host, port = config.server.host, config.server.port
661
662
  try:
662
663
  with _socket.create_connection((host, port), timeout=2):
663
664
  pass
@@ -673,6 +674,16 @@ def _cmd_start(args: argparse.Namespace) -> None:
673
674
  )
674
675
  sys.exit(1)
675
676
 
677
+
678
+ def _cmd_start(args: argparse.Namespace) -> None:
679
+ config = load_config(args.config)
680
+
681
+ agents = _resolve_agents_to_start(config, args)
682
+
683
+ # Best-effort check that the IRC server is reachable before starting agent(s)
684
+ server_name = config.server.name
685
+ _probe_server_connection(config.server.host, config.server.port, server_name)
686
+
676
687
  foreground = getattr(args, "foreground", False)
677
688
 
678
689
  if foreground:
@@ -977,53 +988,36 @@ def _agent_process_status(agent) -> tuple[str, int | None]:
977
988
  return "stopped", None
978
989
 
979
990
 
980
- def _cmd_status(args: argparse.Namespace) -> None:
981
- config = load_config_or_default(args.config)
982
-
983
- if not config.agents:
984
- print("No agents configured")
985
- return
991
+ def _print_agent_detail(agent, config_path: str, args: argparse.Namespace) -> None:
992
+ """Print detailed status for a single agent, including live IPC activity query."""
993
+ status, pid = _agent_process_status(agent)
994
+ print(agent.nick)
995
+ print(f" Status: {status}")
996
+ print(f" PID: {pid or '-'}")
986
997
 
987
- # Single agent detailed view
988
- if args.nick:
989
- agent = None
990
- for a in config.agents:
991
- if a.nick == args.nick:
992
- agent = a
993
- break
994
- if not agent:
995
- print(f"Agent '{args.nick}' not found in config", file=sys.stderr)
996
- sys.exit(1)
997
-
998
- status, pid = _agent_process_status(agent)
999
- print(agent.nick)
1000
- print(f" Status: {status}")
1001
- print(f" PID: {pid or '-'}")
1002
-
1003
- # Query IPC for activity if running — ask the agent directly
1004
- if status == "running":
1005
- resp = asyncio.run(_ipc_request(_agent_socket_path(agent.nick), "status", query=True))
1006
- if resp and resp.get("ok"):
1007
- data = resp.get("data", {})
1008
- print(f" Activity: {data.get('description', 'nothing')}")
1009
- print(f" Turns: {data.get('turn_count', 0)}")
1010
- print(f" Paused: {'yes' if data.get('paused') else 'no'}")
1011
- else:
1012
- print(" Activity: unknown (daemon may need restart)")
998
+ # Query IPC for activity if running — ask the agent directly
999
+ if status == "running":
1000
+ resp = asyncio.run(_ipc_request(_agent_socket_path(agent.nick), "status", query=True))
1001
+ if resp and resp.get("ok"):
1002
+ data = resp.get("data", {})
1003
+ print(f" Activity: {data.get('description', 'nothing')}")
1004
+ print(f" Turns: {data.get('turn_count', 0)}")
1005
+ print(f" Paused: {'yes' if data.get('paused') else 'no'}")
1013
1006
  else:
1014
- print(" Activity: -")
1015
-
1016
- channels = agent.channels if isinstance(agent.channels, list) else []
1017
- print(f" Directory: {agent.directory}")
1018
- print(f" Backend: {agent.agent}")
1019
- print(f" Channels: {', '.join(channels)}")
1020
- print(f" Model: {agent.model}")
1021
- print(f" Config: {args.config}")
1022
- return
1007
+ print(" Activity: unknown (daemon may need restart)")
1008
+ else:
1009
+ print(" Activity: -")
1010
+
1011
+ channels = agent.channels if isinstance(agent.channels, list) else []
1012
+ print(f" Directory: {agent.directory}")
1013
+ print(f" Backend: {agent.agent}")
1014
+ print(f" Channels: {', '.join(channels)}")
1015
+ print(f" Model: {agent.model}")
1016
+ print(f" Config: {config_path}")
1023
1017
 
1024
- # All agents view
1025
- show_activity = args.full
1026
1018
 
1019
+ def _print_agents_overview(agents: list, show_activity: bool) -> None:
1020
+ """Print a table of all agents with status, PID, and optionally activity."""
1027
1021
  if show_activity:
1028
1022
  print(f"{'NICK':<30} {'STATUS':<12} {'PID':<10} {'ACTIVITY'}")
1029
1023
  print("-" * 72)
@@ -1031,7 +1025,7 @@ def _cmd_status(args: argparse.Namespace) -> None:
1031
1025
  print(f"{'NICK':<30} {'STATUS':<12} {'PID':<10}")
1032
1026
  print("-" * 52)
1033
1027
 
1034
- for agent in config.agents:
1028
+ for agent in agents:
1035
1029
  status, pid = _agent_process_status(agent)
1036
1030
  activity = "-"
1037
1031
 
@@ -1046,7 +1040,9 @@ def _cmd_status(args: argparse.Namespace) -> None:
1046
1040
  else:
1047
1041
  print(f"{agent.nick:<30} {status:<12} {str(pid or '-'):<10}")
1048
1042
 
1049
- # Show bots
1043
+
1044
+ def _print_bot_listing() -> None:
1045
+ """Print a table of configured bots (if any exist)."""
1050
1046
  from culture.bots.config import BOTS_DIR, load_bot_config
1051
1047
 
1052
1048
  if BOTS_DIR.is_dir():
@@ -1067,6 +1063,34 @@ def _cmd_status(args: argparse.Namespace) -> None:
1067
1063
  print(f"{bc.name:<30} {bc.trigger_type:<12} {channels}")
1068
1064
 
1069
1065
 
1066
+ def _cmd_status(args: argparse.Namespace) -> None:
1067
+ config = load_config_or_default(args.config)
1068
+
1069
+ if not config.agents:
1070
+ print("No agents configured")
1071
+ return
1072
+
1073
+ # Single agent detailed view
1074
+ if args.nick:
1075
+ agent = None
1076
+ for a in config.agents:
1077
+ if a.nick == args.nick:
1078
+ agent = a
1079
+ break
1080
+ if not agent:
1081
+ print(f"Agent '{args.nick}' not found in config", file=sys.stderr)
1082
+ sys.exit(1)
1083
+
1084
+ _print_agent_detail(agent, args.config, args)
1085
+ return
1086
+
1087
+ # All agents view
1088
+ _print_agents_overview(config.agents, args.full)
1089
+
1090
+ # Show bots
1091
+ _print_bot_listing()
1092
+
1093
+
1070
1094
  # -----------------------------------------------------------------------
1071
1095
  # Observation subcommands
1072
1096
  # -----------------------------------------------------------------------
@@ -1454,39 +1478,10 @@ def _build_server_start_cmd(mesh, culture_bin: str, mesh_config_path: str) -> li
1454
1478
  # -----------------------------------------------------------------------
1455
1479
 
1456
1480
 
1457
- def _cmd_setup(args: argparse.Namespace) -> None:
1481
+ def _store_mesh_credentials(mesh) -> None:
1482
+ """Prompt for link passwords and store them in the OS keyring (never in files)."""
1458
1483
  import getpass
1459
1484
 
1460
- from culture.mesh_config import load_mesh_config
1461
- from culture.persistence import install_service, list_services, uninstall_service
1462
-
1463
- try:
1464
- mesh = load_mesh_config(args.config)
1465
- except FileNotFoundError:
1466
- print(f"Mesh config not found: {args.config}", file=sys.stderr)
1467
- print("Create it manually or ask your AI agent to generate it.", file=sys.stderr)
1468
- sys.exit(1)
1469
-
1470
- server_name = mesh.server.name
1471
-
1472
- if args.uninstall:
1473
- print("Uninstalling culture services...")
1474
- # Only remove services for this node (not other mesh nodes)
1475
- expected = {f"culture-server-{server_name}"}
1476
- for agent in mesh.agents:
1477
- expected.add(f"culture-agent-{server_name}-{agent.nick}")
1478
- for svc in list_services():
1479
- if svc in expected:
1480
- print(f" Removing {svc}")
1481
- uninstall_service(svc)
1482
- _server_stop_by_name(server_name)
1483
- for agent in mesh.agents:
1484
- full_nick = f"{server_name}-{agent.nick}"
1485
- _stop_agent(full_nick)
1486
- print("Done.")
1487
- return
1488
-
1489
- # Prompt for link passwords and store in OS keyring (never in files)
1490
1485
  from culture.credentials import lookup_credential, store_credential
1491
1486
 
1492
1487
  for link in mesh.server.links:
@@ -1505,7 +1500,9 @@ def _cmd_setup(args: argparse.Namespace) -> None:
1505
1500
  file=sys.stderr,
1506
1501
  )
1507
1502
 
1508
- # Generate agents.yaml for each workdir
1503
+
1504
+ def _generate_agent_configs(mesh, server_name: str) -> None:
1505
+ """Generate agents.yaml for each agent workdir defined in the mesh config."""
1509
1506
  from culture.clients.claude.config import AgentConfig as BaseAgentConfig
1510
1507
  from culture.clients.claude.config import (
1511
1508
  DaemonConfig,
@@ -1542,9 +1539,12 @@ def _cmd_setup(args: argparse.Namespace) -> None:
1542
1539
  save_config(config_path, daemon_config)
1543
1540
  print(f" Wrote {config_path}")
1544
1541
 
1545
- # Install auto-start services
1546
- culture_bin = shutil.which("culture") or "culture"
1547
- server_cmd = _build_server_start_cmd(mesh, culture_bin, args.config)
1542
+
1543
+ def _install_mesh_services(mesh, server_name: str, culture_bin: str, config_path: str) -> None:
1544
+ """Install auto-start service entries for the server and all agents."""
1545
+ from culture.persistence import install_service
1546
+
1547
+ server_cmd = _build_server_start_cmd(mesh, culture_bin, config_path)
1548
1548
  svc_name = f"culture-server-{server_name}"
1549
1549
  path = install_service(svc_name, server_cmd, f"culture server {server_name}")
1550
1550
  print(f" Installed {svc_name} → {path}")
@@ -1552,12 +1552,50 @@ def _cmd_setup(args: argparse.Namespace) -> None:
1552
1552
  for agent in mesh.agents:
1553
1553
  full_nick = f"{server_name}-{agent.nick}"
1554
1554
  workdir = os.path.expanduser(agent.workdir)
1555
- config_path = os.path.join(workdir, ".culture", "agents.yaml")
1556
- agent_cmd = [culture_bin, "start", full_nick, "--foreground", "--config", config_path]
1555
+ agent_config_path = os.path.join(workdir, ".culture", "agents.yaml")
1556
+ agent_cmd = [culture_bin, "start", full_nick, "--foreground", "--config", agent_config_path]
1557
1557
  agent_svc = f"culture-agent-{full_nick}"
1558
1558
  path = install_service(agent_svc, agent_cmd, f"culture agent {full_nick}")
1559
1559
  print(f" Installed {agent_svc} → {path}")
1560
1560
 
1561
+
1562
+ def _cmd_setup(args: argparse.Namespace) -> None:
1563
+ from culture.mesh_config import load_mesh_config
1564
+ from culture.persistence import list_services, uninstall_service
1565
+
1566
+ try:
1567
+ mesh = load_mesh_config(args.config)
1568
+ except FileNotFoundError:
1569
+ print(f"Mesh config not found: {args.config}", file=sys.stderr)
1570
+ print("Create it manually or ask your AI agent to generate it.", file=sys.stderr)
1571
+ sys.exit(1)
1572
+
1573
+ server_name = mesh.server.name
1574
+
1575
+ if args.uninstall:
1576
+ print("Uninstalling culture services...")
1577
+ # Only remove services for this node (not other mesh nodes)
1578
+ expected = {f"culture-server-{server_name}"}
1579
+ for agent in mesh.agents:
1580
+ expected.add(f"culture-agent-{server_name}-{agent.nick}")
1581
+ for svc in list_services():
1582
+ if svc in expected:
1583
+ print(f" Removing {svc}")
1584
+ uninstall_service(svc)
1585
+ _server_stop_by_name(server_name)
1586
+ for agent in mesh.agents:
1587
+ full_nick = f"{server_name}-{agent.nick}"
1588
+ _stop_agent(full_nick)
1589
+ print("Done.")
1590
+ return
1591
+
1592
+ _store_mesh_credentials(mesh)
1593
+
1594
+ _generate_agent_configs(mesh, server_name)
1595
+
1596
+ culture_bin = shutil.which("culture") or "culture"
1597
+ _install_mesh_services(mesh, server_name, culture_bin, args.config)
1598
+
1561
1599
  print(f"\nSetup complete for mesh node '{server_name}'.")
1562
1600
  print("Services installed. Start with your service manager or reboot.")
1563
1601
 
@@ -1597,65 +1635,67 @@ def _server_stop_by_name(name: str) -> None:
1597
1635
  # -----------------------------------------------------------------------
1598
1636
 
1599
1637
 
1600
- def _cmd_update(args: argparse.Namespace) -> None:
1601
- from culture.mesh_config import load_mesh_config
1638
+ def _upgrade_culture_package(args: argparse.Namespace) -> bool:
1639
+ """Upgrade the culture-cli package via uv or pip, then re-exec with --skip-upgrade.
1602
1640
 
1603
- try:
1604
- mesh = load_mesh_config(args.config)
1605
- except FileNotFoundError:
1606
- print(f"Mesh config not found: {args.config}", file=sys.stderr)
1607
- sys.exit(1)
1608
-
1609
- server_name = mesh.server.name
1610
-
1611
- if not args.skip_upgrade:
1612
- if args.dry_run:
1613
- print("[dry-run] Would run: uv tool upgrade culture-cli")
1614
- print("[dry-run] Would re-exec with --skip-upgrade")
1615
- return
1641
+ Returns True if the caller should proceed with the restart phase
1642
+ (``--skip-upgrade`` was set). Returns False for dry-run (caller should stop).
1643
+ On success, re-execs the process and never returns.
1644
+ """
1645
+ if args.skip_upgrade:
1646
+ return True
1616
1647
 
1617
- # Upgrade the package
1618
- uv = shutil.which("uv")
1619
- if uv:
1620
- print("Upgrading via uv...")
1648
+ if args.dry_run:
1649
+ print("[dry-run] Would run: uv tool upgrade culture-cli")
1650
+ print("[dry-run] Would re-exec with --skip-upgrade")
1651
+ return False
1652
+
1653
+ # Upgrade the package
1654
+ uv = shutil.which("uv")
1655
+ if uv:
1656
+ print("Upgrading via uv...")
1657
+ result = subprocess.run(
1658
+ [uv, "tool", "upgrade", "culture-cli"],
1659
+ capture_output=True,
1660
+ text=True,
1661
+ )
1662
+ print(result.stdout.strip() if result.stdout else "")
1663
+ if result.returncode != 0:
1664
+ print(f"uv upgrade failed: {result.stderr}", file=sys.stderr)
1665
+ sys.exit(1)
1666
+ else:
1667
+ pip = shutil.which("pip") or shutil.which("pip3")
1668
+ if pip:
1669
+ print("Upgrading via pip...")
1621
1670
  result = subprocess.run(
1622
- [uv, "tool", "upgrade", "culture-cli"],
1671
+ [pip, "install", "--upgrade", "culture-cli"],
1623
1672
  capture_output=True,
1624
1673
  text=True,
1625
1674
  )
1626
- print(result.stdout.strip() if result.stdout else "")
1627
1675
  if result.returncode != 0:
1628
- print(f"uv upgrade failed: {result.stderr}", file=sys.stderr)
1676
+ print(f"pip upgrade failed: {result.stderr}", file=sys.stderr)
1629
1677
  sys.exit(1)
1630
1678
  else:
1631
- pip = shutil.which("pip") or shutil.which("pip3")
1632
- if pip:
1633
- print("Upgrading via pip...")
1634
- result = subprocess.run(
1635
- [pip, "install", "--upgrade", "culture-cli"],
1636
- capture_output=True,
1637
- text=True,
1638
- )
1639
- if result.returncode != 0:
1640
- print(f"pip upgrade failed: {result.stderr}", file=sys.stderr)
1641
- sys.exit(1)
1642
- else:
1643
- print("Neither uv nor pip found", file=sys.stderr)
1644
- sys.exit(1)
1679
+ print("Neither uv nor pip found", file=sys.stderr)
1680
+ sys.exit(1)
1645
1681
 
1646
- # Re-exec with new binary so restart uses new code
1647
- culture_bin = shutil.which("culture") or "culture"
1648
- reexec_args = [culture_bin, "update", "--skip-upgrade", "--config", args.config]
1649
- print("Re-executing with updated code...")
1650
- if sys.platform == "win32":
1651
- sys.exit(subprocess.run(reexec_args).returncode)
1652
- else:
1653
- os.execvp(culture_bin, reexec_args)
1682
+ # Re-exec with new binary so restart uses new code
1683
+ culture_bin = shutil.which("culture") or "culture"
1684
+ reexec_args = [culture_bin, "update", "--skip-upgrade", "--config", args.config]
1685
+ print("Re-executing with updated code...")
1686
+ if sys.platform == "win32":
1687
+ sys.exit(subprocess.run(reexec_args).returncode)
1688
+ else:
1689
+ os.execvp(culture_bin, reexec_args)
1654
1690
 
1655
- # --skip-upgrade path: restart everything
1691
+
1692
+ def _restart_mesh_services(
1693
+ mesh, server_name: str, culture_bin: str, config_path: str, dry_run: bool
1694
+ ) -> None:
1695
+ """Stop agents and server, regenerate service entries, then restart everything."""
1656
1696
  print(f"Restarting mesh node '{server_name}'...")
1657
1697
 
1658
- if args.dry_run:
1698
+ if dry_run:
1659
1699
  for agent in mesh.agents:
1660
1700
  print(f"[dry-run] Would stop agent {server_name}-{agent.nick}")
1661
1701
  print(f"[dry-run] Would stop server {server_name}")
@@ -1678,15 +1718,14 @@ def _cmd_update(args: argparse.Namespace) -> None:
1678
1718
  # Regenerate auto-start entries
1679
1719
  from culture.persistence import install_service
1680
1720
 
1681
- culture_bin = shutil.which("culture") or "culture"
1682
- server_cmd = _build_server_start_cmd(mesh, culture_bin, args.config)
1721
+ server_cmd = _build_server_start_cmd(mesh, culture_bin, config_path)
1683
1722
  install_service(f"culture-server-{server_name}", server_cmd, f"culture server {server_name}")
1684
1723
 
1685
1724
  for agent in mesh.agents:
1686
1725
  full_nick = f"{server_name}-{agent.nick}"
1687
1726
  workdir = os.path.expanduser(agent.workdir)
1688
- config_path = os.path.join(workdir, ".culture", "agents.yaml")
1689
- agent_cmd = [culture_bin, "start", full_nick, "--foreground", "--config", config_path]
1727
+ agent_config_path = os.path.join(workdir, ".culture", "agents.yaml")
1728
+ agent_cmd = [culture_bin, "start", full_nick, "--foreground", "--config", agent_config_path]
1690
1729
  install_service(f"culture-agent-{full_nick}", agent_cmd, f"culture agent {full_nick}")
1691
1730
 
1692
1731
  # Restart services via platform service manager
@@ -1715,7 +1754,7 @@ def _cmd_update(args: argparse.Namespace) -> None:
1715
1754
  "--port",
1716
1755
  str(mesh.server.port),
1717
1756
  "--mesh-config",
1718
- args.config,
1757
+ config_path,
1719
1758
  ],
1720
1759
  check=False,
1721
1760
  )
@@ -1737,15 +1776,34 @@ def _cmd_update(args: argparse.Namespace) -> None:
1737
1776
  if not restart_service(agent_svc):
1738
1777
  # Fallback: start via CLI
1739
1778
  workdir = os.path.expanduser(agent.workdir)
1740
- config_path = os.path.join(workdir, ".culture", "agents.yaml")
1779
+ agent_config_path = os.path.join(workdir, ".culture", "agents.yaml")
1741
1780
  subprocess.run(
1742
- [culture_bin, "start", full_nick, "--config", config_path],
1781
+ [culture_bin, "start", full_nick, "--config", agent_config_path],
1743
1782
  check=False,
1744
1783
  )
1745
1784
 
1746
1785
  print("\nUpdate complete. All services restarted.")
1747
1786
 
1748
1787
 
1788
+ def _cmd_update(args: argparse.Namespace) -> None:
1789
+ from culture.mesh_config import load_mesh_config
1790
+
1791
+ try:
1792
+ mesh = load_mesh_config(args.config)
1793
+ except FileNotFoundError:
1794
+ print(f"Mesh config not found: {args.config}", file=sys.stderr)
1795
+ sys.exit(1)
1796
+
1797
+ server_name = mesh.server.name
1798
+
1799
+ if not _upgrade_culture_package(args):
1800
+ return
1801
+
1802
+ # --skip-upgrade path: restart everything
1803
+ culture_bin = shutil.which("culture") or "culture"
1804
+ _restart_mesh_services(mesh, server_name, culture_bin, args.config, args.dry_run)
1805
+
1806
+
1749
1807
  # -----------------------------------------------------------------------
1750
1808
  # Bot subcommands
1751
1809
  # -----------------------------------------------------------------------