agentirc-cli 0.16.4__tar.gz → 0.18.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 (232) hide show
  1. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/CHANGELOG.md +30 -0
  2. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/PKG-INFO +1 -1
  3. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/cli.py +461 -26
  4. agentirc_cli-0.18.0/agentirc/credentials.py +132 -0
  5. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/learn_prompt.py +70 -4
  6. agentirc_cli-0.18.0/agentirc/mesh_config.py +94 -0
  7. agentirc_cli-0.18.0/agentirc/persistence.py +233 -0
  8. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/ircd.py +93 -1
  9. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/server_link.py +4 -1
  10. agentirc_cli-0.18.0/agentirc/skills/agentirc/SKILL.md +287 -0
  11. agentirc_cli-0.18.0/docs/agentic-self-learn.md +110 -0
  12. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/cli.md +53 -0
  13. agentirc_cli-0.18.0/docs/ops-tooling.md +271 -0
  14. agentirc_cli-0.18.0/plugins/claude-code/skills/agentirc/SKILL.md +218 -0
  15. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/pyproject.toml +2 -1
  16. agentirc_cli-0.18.0/tests/test_link_reconnect.py +221 -0
  17. agentirc_cli-0.18.0/tests/test_mesh_config.py +89 -0
  18. agentirc_cli-0.18.0/tests/test_persistence.py +103 -0
  19. agentirc_cli-0.18.0/tests/test_setup_update_cli.py +42 -0
  20. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/uv.lock +1 -1
  21. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/.claude/skills/pr-review/SKILL.md +0 -0
  22. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/.github/workflows/pages.yml +0 -0
  23. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/.github/workflows/publish.yml +0 -0
  24. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/.github/workflows/tests.yml +0 -0
  25. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/.gitignore +0 -0
  26. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/.markdownlint-cli2.yaml +0 -0
  27. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/.pr_agent.toml +0 -0
  28. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/CLAUDE.md +0 -0
  29. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/CNAME +0 -0
  30. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/Gemfile +0 -0
  31. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/Gemfile.lock +0 -0
  32. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/LICENSE +0 -0
  33. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/README.md +0 -0
  34. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/_config.yml +0 -0
  35. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/_sass/color_schemes/anthropic.scss +0 -0
  36. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/_sass/custom/custom.scss +0 -0
  37. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/__init__.py +0 -0
  38. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/__main__.py +0 -0
  39. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/__init__.py +0 -0
  40. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/__init__.py +0 -0
  41. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/agent_runner.py +0 -0
  42. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/config.py +0 -0
  43. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/daemon.py +0 -0
  44. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/ipc.py +0 -0
  45. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/irc_transport.py +0 -0
  46. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/message_buffer.py +0 -0
  47. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/skill/SKILL.md +0 -0
  48. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/skill/__init__.py +0 -0
  49. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/skill/irc_client.py +0 -0
  50. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/socket_server.py +0 -0
  51. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/supervisor.py +0 -0
  52. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/webhook.py +0 -0
  53. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/__init__.py +0 -0
  54. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/__main__.py +0 -0
  55. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/agent_runner.py +0 -0
  56. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/config.py +0 -0
  57. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/daemon.py +0 -0
  58. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/ipc.py +0 -0
  59. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/irc_transport.py +0 -0
  60. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/message_buffer.py +0 -0
  61. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/skill/SKILL.md +0 -0
  62. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/skill/__init__.py +0 -0
  63. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/skill/irc_client.py +0 -0
  64. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/socket_server.py +0 -0
  65. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/supervisor.py +0 -0
  66. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/webhook.py +0 -0
  67. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/__init__.py +0 -0
  68. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/agent_runner.py +0 -0
  69. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/config.py +0 -0
  70. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/daemon.py +0 -0
  71. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/ipc.py +0 -0
  72. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/irc_transport.py +0 -0
  73. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/message_buffer.py +0 -0
  74. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/skill/SKILL.md +0 -0
  75. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/skill/__init__.py +0 -0
  76. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/skill/irc_client.py +0 -0
  77. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/socket_server.py +0 -0
  78. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/supervisor.py +0 -0
  79. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/webhook.py +0 -0
  80. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/__init__.py +0 -0
  81. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/agent_runner.py +0 -0
  82. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/config.py +0 -0
  83. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/daemon.py +0 -0
  84. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/ipc.py +0 -0
  85. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/irc_transport.py +0 -0
  86. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/message_buffer.py +0 -0
  87. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/skill/SKILL.md +0 -0
  88. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/skill/__init__.py +0 -0
  89. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/skill/irc_client.py +0 -0
  90. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/socket_server.py +0 -0
  91. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/supervisor.py +0 -0
  92. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/webhook.py +0 -0
  93. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/observer.py +0 -0
  94. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/overview/__init__.py +0 -0
  95. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/overview/collector.py +0 -0
  96. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/overview/model.py +0 -0
  97. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/overview/renderer_text.py +0 -0
  98. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/overview/renderer_web.py +0 -0
  99. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/overview/web/style.css +0 -0
  100. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/pidfile.py +0 -0
  101. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/__init__.py +0 -0
  102. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/commands.py +0 -0
  103. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/extensions/federation.md +0 -0
  104. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/extensions/history.md +0 -0
  105. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/extensions/rooms.md +0 -0
  106. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/extensions/tags.md +0 -0
  107. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/message.py +0 -0
  108. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/protocol-index.md +0 -0
  109. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/replies.py +0 -0
  110. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/__init__.py +0 -0
  111. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/__main__.py +0 -0
  112. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/channel.py +0 -0
  113. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/client.py +0 -0
  114. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/config.py +0 -0
  115. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/remote_client.py +0 -0
  116. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/room_store.py +0 -0
  117. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/rooms_util.py +0 -0
  118. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/skill.py +0 -0
  119. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/skills/__init__.py +0 -0
  120. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/skills/history.py +0 -0
  121. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/skills/rooms.py +0 -0
  122. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/agent-client.md +0 -0
  123. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/agent-harness-spec.md +0 -0
  124. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/ci.md +0 -0
  125. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/acp/overview.md +0 -0
  126. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/claude/configuration.md +0 -0
  127. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/claude/context-management.md +0 -0
  128. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/claude/irc-tools.md +0 -0
  129. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/claude/overview.md +0 -0
  130. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/claude/setup.md +0 -0
  131. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/claude/supervisor.md +0 -0
  132. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/claude/webhooks.md +0 -0
  133. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/codex/configuration.md +0 -0
  134. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/codex/context-management.md +0 -0
  135. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/codex/irc-tools.md +0 -0
  136. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/codex/overview.md +0 -0
  137. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/codex/setup.md +0 -0
  138. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/codex/supervisor.md +0 -0
  139. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/codex/webhooks.md +0 -0
  140. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/copilot/configuration.md +0 -0
  141. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/copilot/context-management.md +0 -0
  142. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/copilot/irc-tools.md +0 -0
  143. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/copilot/overview.md +0 -0
  144. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/copilot/setup.md +0 -0
  145. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/copilot/supervisor.md +0 -0
  146. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/copilot/webhooks.md +0 -0
  147. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/codex-backend.md +0 -0
  148. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/copilot-backend.md +0 -0
  149. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/design.md +0 -0
  150. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/docs-site.md +0 -0
  151. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/getting-started.md +0 -0
  152. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/grow-your-agent.md +0 -0
  153. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/harness-conformance.md +0 -0
  154. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/layer1-core-irc.md +0 -0
  155. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/layer2-attention.md +0 -0
  156. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/layer3-skills.md +0 -0
  157. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/layer4-federation.md +0 -0
  158. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/layer5-agent-harness.md +0 -0
  159. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/overview.md +0 -0
  160. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/publishing.md +0 -0
  161. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  162. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/rooms.md +0 -0
  163. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/server-architecture.md +0 -0
  164. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  165. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  166. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  167. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  168. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  169. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  170. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  171. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  172. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/01-pair-programming.md +0 -0
  173. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/02-code-review-ensemble.md +0 -0
  174. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/03-cross-server-delegation.md +0 -0
  175. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/04-knowledge-propagation.md +0 -0
  176. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/05-the-observer.md +0 -0
  177. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/06-cross-server-ops.md +0 -0
  178. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/07-supervisor-intervention.md +0 -0
  179. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/08-apps-as-agents.md +0 -0
  180. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/09-research-swarm.md +0 -0
  181. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/10-grow-your-agent.md +0 -0
  182. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases-index.md +0 -0
  183. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/index.md +0 -0
  184. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/README.md +0 -0
  185. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/config.py +0 -0
  186. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/daemon.py +0 -0
  187. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/ipc.py +0 -0
  188. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/irc_transport.py +0 -0
  189. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/message_buffer.py +0 -0
  190. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/skill/SKILL.md +0 -0
  191. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/skill/irc_client.py +0 -0
  192. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/socket_server.py +0 -0
  193. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/webhook.py +0 -0
  194. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  195. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  196. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/plugins/codex/skills/agentirc-irc/SKILL.md +0 -0
  197. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/__init__.py +0 -0
  198. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/conftest.py +0 -0
  199. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_acp_daemon.py +0 -0
  200. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_agent_runner.py +0 -0
  201. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_channel.py +0 -0
  202. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_codex_daemon.py +0 -0
  203. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_connection.py +0 -0
  204. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_copilot_daemon.py +0 -0
  205. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_daemon.py +0 -0
  206. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_daemon_config.py +0 -0
  207. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_daemon_ipc.py +0 -0
  208. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_discovery.py +0 -0
  209. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_federation.py +0 -0
  210. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_history.py +0 -0
  211. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_integration_layer5.py +0 -0
  212. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_ipc.py +0 -0
  213. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_irc_transport.py +0 -0
  214. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_mentions.py +0 -0
  215. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_message.py +0 -0
  216. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_message_buffer.py +0 -0
  217. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_messaging.py +0 -0
  218. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_modes.py +0 -0
  219. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_overview_cli.py +0 -0
  220. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_overview_collector.py +0 -0
  221. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_overview_model.py +0 -0
  222. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_overview_renderer.py +0 -0
  223. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_overview_web.py +0 -0
  224. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_room_persistence.py +0 -0
  225. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_rooms.py +0 -0
  226. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_rooms_federation.py +0 -0
  227. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_rooms_integration.py +0 -0
  228. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_skill_client.py +0 -0
  229. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_skills.py +0 -0
  230. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_socket_server.py +0 -0
  231. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_supervisor.py +0 -0
  232. {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_webhook.py +0 -0
@@ -4,6 +4,36 @@ 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
+ ## [0.18.0] - 2026-04-02
8
+
9
+
10
+ ### Added
11
+
12
+ - S2S link auto-reconnect with exponential backoff (5s to 120s)
13
+ - Declarative mesh.yaml configuration for multi-machine setup
14
+ - Cross-platform auto-start persistence (systemd, launchd, Windows schtasks)
15
+ - agentirc setup command — bootstrap a machine into the mesh from mesh.yaml
16
+ - agentirc update command — upgrade package and gracefully restart all services
17
+ - --foreground flag for server start and agent start (required by service managers)
18
+ - Windows platform support guards (no fork, SIGTERM fallback)
19
+
20
+
21
+ ### Changed
22
+
23
+ - S2S links now auto-retry on initial startup failure
24
+ - SQUIT (intentional delink) suppresses reconnect attempts
25
+ - Incoming peer connections cancel outbound retry tasks
26
+
27
+ ## [0.17.0] - 2026-04-01
28
+
29
+
30
+ ### Added
31
+
32
+ - Two-tier skill system: root-level admin skill (server setup, mesh linking, federation, agent lifecycle) and project-level messaging skill
33
+ - agentirc skills install now installs both admin and messaging skills for all backends
34
+ - Learn prompt includes server/mesh setup, agent lifecycle, and dual skill install instructions
35
+ - docs/agentic-self-learn.md documenting the two-tier skill system
36
+
7
37
  ## [0.16.4] - 2026-04-01
8
38
 
9
39
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 0.16.4
3
+ Version: 0.18.0
4
4
  Summary: 🌱 The space your agents deserve — an autonomous agent mesh where AI agents live, collaborate, and grow
5
5
  Project-URL: Homepage, https://github.com/OriNachum/agentirc
6
6
  Author: Ori Nachum
@@ -14,6 +14,8 @@ Subcommands:
14
14
  agentirc sleep [nick] [--all] Pause agent(s) — stay connected but idle
15
15
  agentirc wake [nick] [--all] Resume paused agent(s)
16
16
  agentirc overview [--room X] [--agent X] Show mesh overview
17
+ agentirc setup [--config X] [--uninstall] Set up mesh from mesh.yaml
18
+ agentirc update [--dry-run] [--skip-upgrade] Upgrade and restart mesh
17
19
  """
18
20
  from __future__ import annotations
19
21
 
@@ -22,7 +24,9 @@ import asyncio
22
24
  import json
23
25
  import logging
24
26
  import os
27
+ import shutil
25
28
  import signal
29
+ import subprocess
26
30
  import sys
27
31
  import time
28
32
 
@@ -92,6 +96,14 @@ def _build_parser() -> argparse.ArgumentParser:
92
96
  "--link", type=_parse_link, action="append", default=[],
93
97
  help="Link to peer: name:host:port:password",
94
98
  )
99
+ srv_start.add_argument(
100
+ "--mesh-config", default=None,
101
+ help="Read links from mesh.yaml + OS keyring (no passwords in CLI args)",
102
+ )
103
+ srv_start.add_argument(
104
+ "--foreground", action="store_true",
105
+ help="Run in foreground (for service managers)",
106
+ )
95
107
 
96
108
  srv_stop = server_sub.add_parser("stop", help="Stop the IRC server daemon")
97
109
  srv_stop.add_argument("--name", default="agentirc", help="Server name")
@@ -112,6 +124,10 @@ def _build_parser() -> argparse.ArgumentParser:
112
124
  start_parser.add_argument("nick", nargs="?", help="Agent nick to start")
113
125
  start_parser.add_argument("--all", action="store_true", help="Start all agents")
114
126
  start_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
127
+ start_parser.add_argument(
128
+ "--foreground", action="store_true",
129
+ help="Run in foreground (for service managers)",
130
+ )
115
131
 
116
132
  # -- stop subcommand ---------------------------------------------------
117
133
  stop_parser = sub.add_parser("stop", help="Stop agent daemon(s)")
@@ -181,6 +197,32 @@ def _build_parser() -> argparse.ArgumentParser:
181
197
  overview_parser.add_argument("--refresh", type=int, default=5, help="Web refresh interval in seconds (default: 5, min: 1)")
182
198
  overview_parser.add_argument("--config", default=DEFAULT_CONFIG)
183
199
 
200
+ # -- setup subcommand --------------------------------------------------
201
+ setup_parser = sub.add_parser("setup", help="Set up mesh from mesh.yaml")
202
+ setup_parser.add_argument(
203
+ "--config", default=os.path.expanduser("~/.agentirc/mesh.yaml"),
204
+ help="Path to mesh.yaml",
205
+ )
206
+ setup_parser.add_argument(
207
+ "--uninstall", action="store_true",
208
+ help="Remove auto-start entries and stop services",
209
+ )
210
+
211
+ # -- update subcommand -------------------------------------------------
212
+ update_parser = sub.add_parser("update", help="Upgrade and restart the mesh")
213
+ update_parser.add_argument(
214
+ "--dry-run", action="store_true",
215
+ help="Show what would happen without executing",
216
+ )
217
+ update_parser.add_argument(
218
+ "--skip-upgrade", action="store_true",
219
+ help="Just restart, don't upgrade the package",
220
+ )
221
+ update_parser.add_argument(
222
+ "--config", default=os.path.expanduser("~/.agentirc/mesh.yaml"),
223
+ help="Path to mesh.yaml",
224
+ )
225
+
184
226
  return parser
185
227
 
186
228
 
@@ -213,6 +255,8 @@ def main() -> None:
213
255
  "wake": _cmd_wake,
214
256
  "skills": _cmd_skills,
215
257
  "overview": _cmd_overview,
258
+ "setup": _cmd_setup,
259
+ "update": _cmd_update,
216
260
  }
217
261
  handler = dispatch.get(args.command)
218
262
  if handler:
@@ -253,10 +297,30 @@ def _server_start(args: argparse.Namespace) -> None:
253
297
  print(f"Server '{args.name}' is already running (PID {existing})")
254
298
  sys.exit(1)
255
299
 
300
+ # Resolve links: --mesh-config reads from mesh.yaml + OS keyring (no passwords in CLI)
301
+ links = list(args.link) # from --link args (may include passwords for manual use)
302
+ if getattr(args, "mesh_config", None):
303
+ links = _resolve_links_from_mesh(args.mesh_config)
304
+
305
+ if getattr(args, "foreground", False):
306
+ # Foreground mode — run directly (for service managers)
307
+ write_pid(pid_name, os.getpid())
308
+ os.makedirs(LOG_DIR, exist_ok=True)
309
+ print(f"Server '{args.name}' starting in foreground (PID {os.getpid()})")
310
+ print(f" Listening on {args.host}:{args.port}")
311
+ try:
312
+ asyncio.run(_run_server(args.name, args.host, args.port, links))
313
+ finally:
314
+ remove_pid(pid_name)
315
+ return
316
+
317
+ if sys.platform == "win32":
318
+ print("Daemon mode not supported on Windows. Use --foreground.", file=sys.stderr)
319
+ sys.exit(1)
320
+
256
321
  # Fork to daemonize
257
322
  pid = os.fork()
258
323
  if pid > 0:
259
- # Parent: wait briefly to check child started, then exit
260
324
  time.sleep(0.2)
261
325
  if is_process_alive(pid):
262
326
  print(f"Server '{args.name}' started (PID {pid})")
@@ -278,17 +342,14 @@ def _server_start(args: argparse.Namespace) -> None:
278
342
  os.dup2(log_fd, 2)
279
343
  os.close(log_fd)
280
344
 
281
- # Close stdin
282
345
  devnull = os.open(os.devnull, os.O_RDONLY)
283
346
  os.dup2(devnull, 0)
284
347
  os.close(devnull)
285
348
 
286
- # Write PID file
287
349
  write_pid(pid_name, os.getpid())
288
350
 
289
- # Run the server
290
351
  try:
291
- asyncio.run(_run_server(args.name, args.host, args.port, args.link))
352
+ asyncio.run(_run_server(args.name, args.host, args.port, links))
292
353
  finally:
293
354
  remove_pid(pid_name)
294
355
  os._exit(0)
@@ -310,12 +371,17 @@ async def _run_server(name: str, host: str, port: int, links: list | None = None
310
371
  await ircd.connect_to_peer(lc.host, lc.port, lc.password, lc.trust)
311
372
  logger.info("Linking to %s at %s:%d", lc.name, lc.host, lc.port)
312
373
  except Exception as e:
313
- logger.error("Failed to link to %s: %s", lc.name, e)
374
+ logger.error("Failed to link to %s: %s — will retry", lc.name, e)
375
+ ircd.maybe_retry_link(lc.name)
314
376
 
315
377
  stop_event = asyncio.Event()
316
378
  loop = asyncio.get_event_loop()
317
379
  for sig in (signal.SIGINT, signal.SIGTERM):
318
- loop.add_signal_handler(sig, stop_event.set)
380
+ try:
381
+ loop.add_signal_handler(sig, stop_event.set)
382
+ except (NotImplementedError, RuntimeError):
383
+ # Windows / unsupported event loop: fall back to stdlib signals
384
+ signal.signal(sig, lambda *_: stop_event.set())
319
385
 
320
386
  await stop_event.wait()
321
387
  logger.info("Server '%s' shutting down", name)
@@ -347,11 +413,18 @@ def _server_stop(args: argparse.Namespace) -> None:
347
413
  time.sleep(0.1)
348
414
 
349
415
  # Force kill
350
- print(f"Server '{args.name}' did not stop gracefully, sending SIGKILL")
351
- try:
352
- os.kill(pid, signal.SIGKILL)
353
- except ProcessLookupError:
354
- pass
416
+ if sys.platform == "win32":
417
+ print(f"Server '{args.name}' did not stop gracefully, terminating")
418
+ try:
419
+ os.kill(pid, signal.SIGTERM)
420
+ except ProcessLookupError:
421
+ pass
422
+ else:
423
+ print(f"Server '{args.name}' did not stop gracefully, sending SIGKILL")
424
+ try:
425
+ os.kill(pid, signal.SIGKILL)
426
+ except ProcessLookupError:
427
+ pass
355
428
  remove_pid(pid_name)
356
429
  print(f"Server '{args.name}' killed")
357
430
 
@@ -515,14 +588,28 @@ def _cmd_start(args: argparse.Namespace) -> None:
515
588
  )
516
589
  sys.exit(1)
517
590
 
518
- if len(agents) == 1:
519
- # Run in foreground (single agent)
591
+ foreground = getattr(args, "foreground", False)
592
+
593
+ if foreground:
594
+ if len(agents) != 1:
595
+ print("--foreground requires a single agent nick, not --all", file=sys.stderr)
596
+ sys.exit(1)
520
597
  agent = agents[0]
521
- print(f"Starting agent {agent.nick}...")
598
+ print(f"Starting agent {agent.nick} in foreground...")
522
599
  asyncio.run(_run_single_agent(config, agent))
523
600
  else:
524
- # Fork each agent into background
525
- _run_multi_agents(config, agents)
601
+ if sys.platform == "win32":
602
+ if len(agents) == 1:
603
+ # Windows has no fork — run single agent in foreground
604
+ agent = agents[0]
605
+ print(f"Starting agent {agent.nick}...")
606
+ asyncio.run(_run_single_agent(config, agent))
607
+ else:
608
+ print("Multi-agent daemon mode not supported on Windows. Start agents individually.", file=sys.stderr)
609
+ sys.exit(1)
610
+ else:
611
+ # Daemonize all agents (fork each into background)
612
+ _run_multi_agents(config, agents)
526
613
 
527
614
 
528
615
  async def _run_single_agent(config: DaemonConfig, agent: AgentConfig) -> None:
@@ -720,11 +807,18 @@ def _stop_agent(nick: str) -> None:
720
807
  time.sleep(0.1)
721
808
 
722
809
  # Force kill
723
- print(f"Agent '{nick}' did not stop gracefully, sending SIGKILL")
724
- try:
725
- os.kill(pid, signal.SIGKILL)
726
- except ProcessLookupError:
727
- pass
810
+ if sys.platform == "win32":
811
+ print(f"Agent '{nick}' did not stop gracefully, terminating")
812
+ try:
813
+ os.kill(pid, signal.SIGTERM)
814
+ except ProcessLookupError:
815
+ pass
816
+ else:
817
+ print(f"Agent '{nick}' did not stop gracefully, sending SIGKILL")
818
+ try:
819
+ os.kill(pid, signal.SIGKILL)
820
+ except ProcessLookupError:
821
+ pass
728
822
  remove_pid(pid_name)
729
823
  print(f"Agent '{nick}' killed")
730
824
 
@@ -1010,12 +1104,30 @@ def _cmd_channels(args: argparse.Namespace) -> None:
1010
1104
  # Skills install
1011
1105
  # -----------------------------------------------------------------------
1012
1106
 
1107
+ def _get_bundled_admin_skill_path() -> str:
1108
+ """Return the path to the bundled admin SKILL.md in the installed package."""
1109
+ import agentirc
1110
+ return os.path.join(os.path.dirname(agentirc.__file__), "skills", "agentirc", "SKILL.md")
1111
+
1112
+
1013
1113
  def _get_bundled_skill_path() -> str:
1014
1114
  """Return the path to the bundled SKILL.md in the installed package."""
1015
1115
  import agentirc
1016
1116
  return os.path.join(os.path.dirname(agentirc.__file__), "clients", "claude", "skill", "SKILL.md")
1017
1117
 
1018
1118
 
1119
+ def _install_admin_skill(root_dir: str, label: str) -> None:
1120
+ """Install the admin/ops skill to the given root skills directory."""
1121
+ src = _get_bundled_admin_skill_path()
1122
+ dest_dir = os.path.join(os.path.expanduser(root_dir), "agentirc")
1123
+ dest = os.path.join(dest_dir, "SKILL.md")
1124
+
1125
+ os.makedirs(dest_dir, exist_ok=True)
1126
+ import shutil
1127
+ shutil.copy2(src, dest)
1128
+ print(f"Installed {label} admin skill: {dest}")
1129
+
1130
+
1019
1131
  def _install_skill_claude() -> None:
1020
1132
  """Install IRC skill for Claude Code."""
1021
1133
  src = _get_bundled_skill_path()
@@ -1025,7 +1137,8 @@ def _install_skill_claude() -> None:
1025
1137
  os.makedirs(dest_dir, exist_ok=True)
1026
1138
  import shutil
1027
1139
  shutil.copy2(src, dest)
1028
- print(f"Installed Claude Code skill: {dest}")
1140
+ print(f"Installed Claude Code messaging skill: {dest}")
1141
+ _install_admin_skill("~/.claude/skills", "Claude Code")
1029
1142
 
1030
1143
 
1031
1144
  def _get_bundled_codex_skill_path() -> str:
@@ -1043,7 +1156,8 @@ def _install_skill_codex() -> None:
1043
1156
  os.makedirs(dest_dir, exist_ok=True)
1044
1157
  import shutil
1045
1158
  shutil.copy2(src, dest)
1046
- print(f"Installed Codex skill: {dest}")
1159
+ print(f"Installed Codex messaging skill: {dest}")
1160
+ _install_admin_skill("~/.agents/skills", "Codex")
1047
1161
 
1048
1162
 
1049
1163
  def _get_bundled_copilot_skill_path() -> str:
@@ -1061,7 +1175,8 @@ def _install_skill_copilot() -> None:
1061
1175
  os.makedirs(dest_dir, exist_ok=True)
1062
1176
  import shutil
1063
1177
  shutil.copy2(src, dest)
1064
- print(f"Installed Copilot skill: {dest}")
1178
+ print(f"Installed Copilot messaging skill: {dest}")
1179
+ _install_admin_skill("~/.copilot_skills", "Copilot")
1065
1180
 
1066
1181
 
1067
1182
  def _get_bundled_acp_skill_path() -> str:
@@ -1079,7 +1194,8 @@ def _install_skill_acp() -> None:
1079
1194
  os.makedirs(dest_dir, exist_ok=True)
1080
1195
  import shutil
1081
1196
  shutil.copy2(src, dest)
1082
- print(f"Installed ACP skill: {dest}")
1197
+ print(f"Installed ACP messaging skill: {dest}")
1198
+ _install_admin_skill("~/.acp/skills", "ACP")
1083
1199
 
1084
1200
 
1085
1201
  def _cmd_skills(args: argparse.Namespace) -> None:
@@ -1142,3 +1258,322 @@ def _cmd_overview(args: argparse.Namespace) -> None:
1142
1258
  message_limit=message_limit,
1143
1259
  )
1144
1260
  print(output, end="")
1261
+
1262
+
1263
+ # -----------------------------------------------------------------------
1264
+ # Credential helpers
1265
+ # -----------------------------------------------------------------------
1266
+
1267
+ def _resolve_links_from_mesh(mesh_config_path: str) -> list:
1268
+ """Load link configs from mesh.yaml, looking up passwords from OS keyring."""
1269
+ from agentirc.mesh_config import load_mesh_config
1270
+ from agentirc.credentials import lookup_credential
1271
+ from agentirc.server.config import LinkConfig
1272
+
1273
+ mesh = load_mesh_config(mesh_config_path)
1274
+ links = []
1275
+ for lc in mesh.server.links:
1276
+ password = lookup_credential(lc.name)
1277
+ if not password:
1278
+ logger.warning(
1279
+ "No credential found for peer '%s' — link will not be established. "
1280
+ "Run 'agentirc setup' to store link passwords.", lc.name
1281
+ )
1282
+ continue
1283
+ links.append(LinkConfig(
1284
+ name=lc.name, host=lc.host, port=lc.port,
1285
+ password=password, trust=lc.trust,
1286
+ ))
1287
+ return links
1288
+
1289
+
1290
+ # -----------------------------------------------------------------------
1291
+ # Shared helpers for setup / update
1292
+ # -----------------------------------------------------------------------
1293
+
1294
+ def _build_server_start_cmd(mesh, agentirc_bin: str, mesh_config_path: str) -> list[str]:
1295
+ """Build the server start command with --foreground and --mesh-config.
1296
+
1297
+ Passwords are NOT included in the command — they come from the OS keyring
1298
+ at startup via --mesh-config.
1299
+ """
1300
+ return [
1301
+ agentirc_bin, "server", "start", "--foreground",
1302
+ "--name", mesh.server.name,
1303
+ "--host", mesh.server.host,
1304
+ "--port", str(mesh.server.port),
1305
+ "--mesh-config", mesh_config_path,
1306
+ ]
1307
+
1308
+
1309
+ # -----------------------------------------------------------------------
1310
+ # Setup — mesh.yaml → auto-start services
1311
+ # -----------------------------------------------------------------------
1312
+
1313
+ def _cmd_setup(args: argparse.Namespace) -> None:
1314
+ import getpass
1315
+ from agentirc.mesh_config import load_mesh_config
1316
+ from agentirc.persistence import install_service, uninstall_service, list_services
1317
+
1318
+ try:
1319
+ mesh = load_mesh_config(args.config)
1320
+ except FileNotFoundError:
1321
+ print(f"Mesh config not found: {args.config}", file=sys.stderr)
1322
+ print("Create it manually or ask your AI agent to generate it.", file=sys.stderr)
1323
+ sys.exit(1)
1324
+
1325
+ server_name = mesh.server.name
1326
+
1327
+ if args.uninstall:
1328
+ print("Uninstalling agentirc services...")
1329
+ # Only remove services for this node (not other mesh nodes)
1330
+ expected = {f"agentirc-server-{server_name}"}
1331
+ for agent in mesh.agents:
1332
+ expected.add(f"agentirc-agent-{server_name}-{agent.nick}")
1333
+ for svc in list_services():
1334
+ if svc in expected:
1335
+ print(f" Removing {svc}")
1336
+ uninstall_service(svc)
1337
+ _server_stop_by_name(server_name)
1338
+ for agent in mesh.agents:
1339
+ full_nick = f"{server_name}-{agent.nick}"
1340
+ _stop_agent(full_nick)
1341
+ print("Done.")
1342
+ return
1343
+
1344
+ # Prompt for link passwords and store in OS keyring (never in files)
1345
+ from agentirc.credentials import store_credential, lookup_credential
1346
+
1347
+ for link in mesh.server.links:
1348
+ existing = lookup_credential(link.name)
1349
+ if existing:
1350
+ print(f" Credential for '{link.name}' already in keyring")
1351
+ else:
1352
+ password = getpass.getpass(f"Link password for {link.name}: ")
1353
+ if store_credential(link.name, password):
1354
+ print(f" Stored credential for '{link.name}' in OS keyring")
1355
+ else:
1356
+ print(f" Warning: failed to store credential for '{link.name}'", file=sys.stderr)
1357
+ print(f" You may need to install secret-tool (Linux) or check Keychain access (macOS)", file=sys.stderr)
1358
+
1359
+ # Generate agents.yaml for each workdir
1360
+ from agentirc.clients.claude.config import (
1361
+ AgentConfig as BaseAgentConfig,
1362
+ DaemonConfig,
1363
+ ServerConnConfig,
1364
+ save_config,
1365
+ )
1366
+
1367
+ workdir_agents: dict[str, list] = {}
1368
+ for agent in mesh.agents:
1369
+ workdir = os.path.expanduser(agent.workdir)
1370
+ workdir_agents.setdefault(workdir, []).append(agent)
1371
+
1372
+ for workdir, agents in workdir_agents.items():
1373
+ os.makedirs(workdir, exist_ok=True)
1374
+ config_path = os.path.join(workdir, ".agentirc", "agents.yaml")
1375
+ os.makedirs(os.path.dirname(config_path), exist_ok=True)
1376
+
1377
+ agent_configs = []
1378
+ for a in agents:
1379
+ full_nick = f"{server_name}-{a.nick}"
1380
+ agent_configs.append(BaseAgentConfig(
1381
+ nick=full_nick,
1382
+ agent=a.type,
1383
+ directory=workdir,
1384
+ channels=list(a.channels),
1385
+ ))
1386
+
1387
+ daemon_config = DaemonConfig(
1388
+ server=ServerConnConfig(name=server_name, host="localhost", port=mesh.server.port),
1389
+ agents=agent_configs,
1390
+ )
1391
+ save_config(config_path, daemon_config)
1392
+ print(f" Wrote {config_path}")
1393
+
1394
+ # Install auto-start services
1395
+ agentirc_bin = shutil.which("agentirc") or "agentirc"
1396
+ server_cmd = _build_server_start_cmd(mesh, agentirc_bin, args.config)
1397
+ svc_name = f"agentirc-server-{server_name}"
1398
+ path = install_service(svc_name, server_cmd, f"agentirc server {server_name}")
1399
+ print(f" Installed {svc_name} → {path}")
1400
+
1401
+ for agent in mesh.agents:
1402
+ full_nick = f"{server_name}-{agent.nick}"
1403
+ workdir = os.path.expanduser(agent.workdir)
1404
+ config_path = os.path.join(workdir, ".agentirc", "agents.yaml")
1405
+ agent_cmd = [agentirc_bin, "start", full_nick, "--foreground", "--config", config_path]
1406
+ agent_svc = f"agentirc-agent-{full_nick}"
1407
+ path = install_service(agent_svc, agent_cmd, f"agentirc agent {full_nick}")
1408
+ print(f" Installed {agent_svc} → {path}")
1409
+
1410
+ print(f"\nSetup complete for mesh node '{server_name}'.")
1411
+ print(f"Services installed. Start with your service manager or reboot.")
1412
+
1413
+
1414
+ def _server_stop_by_name(name: str) -> None:
1415
+ """Stop a server by name (helper for setup --uninstall and update)."""
1416
+ pid_name = f"server-{name}"
1417
+ pid = read_pid(pid_name)
1418
+ if not pid or not is_process_alive(pid):
1419
+ if pid:
1420
+ remove_pid(pid_name)
1421
+ return
1422
+
1423
+ os.kill(pid, signal.SIGTERM)
1424
+ for _ in range(50):
1425
+ if not is_process_alive(pid):
1426
+ remove_pid(pid_name)
1427
+ return
1428
+ time.sleep(0.1)
1429
+
1430
+ # Escalate to SIGKILL (SIGTERM on Windows)
1431
+ if sys.platform == "win32":
1432
+ try:
1433
+ os.kill(pid, signal.SIGTERM)
1434
+ except ProcessLookupError:
1435
+ pass
1436
+ else:
1437
+ try:
1438
+ os.kill(pid, signal.SIGKILL)
1439
+ except ProcessLookupError:
1440
+ pass
1441
+ remove_pid(pid_name)
1442
+
1443
+
1444
+ # -----------------------------------------------------------------------
1445
+ # Update — upgrade + restart
1446
+ # -----------------------------------------------------------------------
1447
+
1448
+ def _cmd_update(args: argparse.Namespace) -> None:
1449
+ from agentirc.mesh_config import load_mesh_config
1450
+
1451
+ try:
1452
+ mesh = load_mesh_config(args.config)
1453
+ except FileNotFoundError:
1454
+ print(f"Mesh config not found: {args.config}", file=sys.stderr)
1455
+ sys.exit(1)
1456
+
1457
+ server_name = mesh.server.name
1458
+
1459
+ if not args.skip_upgrade:
1460
+ if args.dry_run:
1461
+ print("[dry-run] Would run: uv tool upgrade agentirc-cli")
1462
+ print("[dry-run] Would re-exec with --skip-upgrade")
1463
+ return
1464
+
1465
+ # Upgrade the package
1466
+ uv = shutil.which("uv")
1467
+ if uv:
1468
+ print("Upgrading via uv...")
1469
+ result = subprocess.run(
1470
+ [uv, "tool", "upgrade", "agentirc-cli"],
1471
+ capture_output=True, text=True,
1472
+ )
1473
+ print(result.stdout.strip() if result.stdout else "")
1474
+ if result.returncode != 0:
1475
+ print(f"uv upgrade failed: {result.stderr}", file=sys.stderr)
1476
+ sys.exit(1)
1477
+ else:
1478
+ pip = shutil.which("pip") or shutil.which("pip3")
1479
+ if pip:
1480
+ print("Upgrading via pip...")
1481
+ result = subprocess.run(
1482
+ [pip, "install", "--upgrade", "agentirc-cli"],
1483
+ capture_output=True, text=True,
1484
+ )
1485
+ if result.returncode != 0:
1486
+ print(f"pip upgrade failed: {result.stderr}", file=sys.stderr)
1487
+ sys.exit(1)
1488
+ else:
1489
+ print("Neither uv nor pip found", file=sys.stderr)
1490
+ sys.exit(1)
1491
+
1492
+ # Re-exec with new binary so restart uses new code
1493
+ agentirc_bin = shutil.which("agentirc") or "agentirc"
1494
+ reexec_args = [agentirc_bin, "update", "--skip-upgrade", "--config", args.config]
1495
+ print("Re-executing with updated code...")
1496
+ if sys.platform == "win32":
1497
+ sys.exit(subprocess.run(reexec_args).returncode)
1498
+ else:
1499
+ os.execvp(agentirc_bin, reexec_args)
1500
+
1501
+ # --skip-upgrade path: restart everything
1502
+ print(f"Restarting mesh node '{server_name}'...")
1503
+
1504
+ if args.dry_run:
1505
+ for agent in mesh.agents:
1506
+ print(f"[dry-run] Would stop agent {server_name}-{agent.nick}")
1507
+ print(f"[dry-run] Would stop server {server_name}")
1508
+ print(f"[dry-run] Would regenerate auto-start entries")
1509
+ print(f"[dry-run] Would start server {server_name}")
1510
+ for agent in mesh.agents:
1511
+ print(f"[dry-run] Would start agent {server_name}-{agent.nick}")
1512
+ return
1513
+
1514
+ # Stop agents
1515
+ for agent in mesh.agents:
1516
+ full_nick = f"{server_name}-{agent.nick}"
1517
+ print(f" Stopping {full_nick}...")
1518
+ _stop_agent(full_nick)
1519
+
1520
+ # Stop server
1521
+ print(f" Stopping server {server_name}...")
1522
+ _server_stop_by_name(server_name)
1523
+
1524
+ # Regenerate auto-start entries
1525
+ from agentirc.persistence import install_service
1526
+
1527
+ agentirc_bin = shutil.which("agentirc") or "agentirc"
1528
+ server_cmd = _build_server_start_cmd(mesh, agentirc_bin, args.config)
1529
+ install_service(f"agentirc-server-{server_name}", server_cmd, f"agentirc server {server_name}")
1530
+
1531
+ for agent in mesh.agents:
1532
+ full_nick = f"{server_name}-{agent.nick}"
1533
+ workdir = os.path.expanduser(agent.workdir)
1534
+ config_path = os.path.join(workdir, ".agentirc", "agents.yaml")
1535
+ agent_cmd = [agentirc_bin, "start", full_nick, "--foreground", "--config", config_path]
1536
+ install_service(f"agentirc-agent-{full_nick}", agent_cmd, f"agentirc agent {full_nick}")
1537
+
1538
+ # Restart services via platform service manager
1539
+ from agentirc.persistence import restart_service
1540
+
1541
+ server_svc = f"agentirc-server-{server_name}"
1542
+ print(f" Restarting {server_svc}...")
1543
+ if not restart_service(server_svc):
1544
+ # Fallback: start via CLI if no service file installed
1545
+ if sys.platform == "win32":
1546
+ print(f" No service file found. Run 'agentirc setup' to install services.", file=sys.stderr)
1547
+ else:
1548
+ print(f" No service file found, starting via CLI...")
1549
+ subprocess.run([
1550
+ agentirc_bin, "server", "start",
1551
+ "--name", server_name,
1552
+ "--host", mesh.server.host,
1553
+ "--port", str(mesh.server.port),
1554
+ "--mesh-config", args.config,
1555
+ ], check=False)
1556
+
1557
+ # Wait for server to be ready
1558
+ import socket as _socket
1559
+ for _ in range(50):
1560
+ try:
1561
+ with _socket.create_connection(("localhost", mesh.server.port), timeout=1):
1562
+ break
1563
+ except (ConnectionRefusedError, OSError):
1564
+ time.sleep(0.1)
1565
+
1566
+ for agent in mesh.agents:
1567
+ full_nick = f"{server_name}-{agent.nick}"
1568
+ agent_svc = f"agentirc-agent-{full_nick}"
1569
+ print(f" Restarting {agent_svc}...")
1570
+ if not restart_service(agent_svc):
1571
+ # Fallback: start via CLI
1572
+ workdir = os.path.expanduser(agent.workdir)
1573
+ config_path = os.path.join(workdir, ".agentirc", "agents.yaml")
1574
+ subprocess.run(
1575
+ [agentirc_bin, "start", full_nick, "--config", config_path],
1576
+ check=False,
1577
+ )
1578
+
1579
+ print(f"\nUpdate complete. All services restarted.")