agentirc-cli 0.10.7__tar.gz → 0.11.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 (200) hide show
  1. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/CHANGELOG.md +15 -0
  2. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/PKG-INFO +1 -1
  3. agentirc_cli-0.11.0/agentirc/__init__.py +1 -0
  4. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/cli.py +177 -32
  5. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/claude/config.py +4 -0
  6. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/claude/daemon.py +100 -0
  7. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/claude/irc_transport.py +5 -1
  8. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/observer.py +23 -0
  9. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/cli.md +46 -1
  10. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/pyproject.toml +1 -1
  11. agentirc_cli-0.11.0/tests/test_daemon_ipc.py +81 -0
  12. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/uv.lock +1 -1
  13. agentirc_cli-0.10.7/agentirc/__init__.py +0 -1
  14. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/.claude/skills/pr-review/SKILL.md +0 -0
  15. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/.github/workflows/pages.yml +0 -0
  16. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/.github/workflows/publish.yml +0 -0
  17. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/.github/workflows/tests.yml +0 -0
  18. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/.gitignore +0 -0
  19. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/.markdownlint-cli2.yaml +0 -0
  20. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/.pr_agent.toml +0 -0
  21. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/CLAUDE.md +0 -0
  22. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/CNAME +0 -0
  23. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/Gemfile +0 -0
  24. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/Gemfile.lock +0 -0
  25. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/LICENSE +0 -0
  26. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/README.md +0 -0
  27. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/_config.yml +0 -0
  28. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/_sass/color_schemes/anthropic.scss +0 -0
  29. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/_sass/custom/custom.scss +0 -0
  30. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/__init__.py +0 -0
  31. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/claude/__init__.py +0 -0
  32. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/claude/__main__.py +0 -0
  33. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/claude/agent_runner.py +0 -0
  34. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/claude/ipc.py +0 -0
  35. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/claude/message_buffer.py +0 -0
  36. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/claude/skill/SKILL.md +0 -0
  37. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/claude/skill/__init__.py +0 -0
  38. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/claude/skill/irc_client.py +0 -0
  39. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/claude/socket_server.py +0 -0
  40. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/claude/supervisor.py +0 -0
  41. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/claude/webhook.py +0 -0
  42. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/codex/__init__.py +0 -0
  43. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/codex/agent_runner.py +0 -0
  44. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/codex/config.py +0 -0
  45. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/codex/daemon.py +0 -0
  46. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/codex/ipc.py +0 -0
  47. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/codex/irc_transport.py +0 -0
  48. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/codex/message_buffer.py +0 -0
  49. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/codex/skill/SKILL.md +0 -0
  50. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/codex/skill/__init__.py +0 -0
  51. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/codex/skill/irc_client.py +0 -0
  52. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/codex/socket_server.py +0 -0
  53. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/codex/supervisor.py +0 -0
  54. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/codex/webhook.py +0 -0
  55. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/copilot/__init__.py +0 -0
  56. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/copilot/agent_runner.py +0 -0
  57. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/copilot/config.py +0 -0
  58. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/copilot/daemon.py +0 -0
  59. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/copilot/ipc.py +0 -0
  60. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/copilot/irc_transport.py +0 -0
  61. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/copilot/message_buffer.py +0 -0
  62. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/copilot/skill/SKILL.md +0 -0
  63. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/copilot/skill/__init__.py +0 -0
  64. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/copilot/skill/irc_client.py +0 -0
  65. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/copilot/socket_server.py +0 -0
  66. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/copilot/supervisor.py +0 -0
  67. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/copilot/webhook.py +0 -0
  68. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/opencode/__init__.py +0 -0
  69. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/opencode/agent_runner.py +0 -0
  70. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/opencode/config.py +0 -0
  71. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/opencode/daemon.py +0 -0
  72. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/opencode/ipc.py +0 -0
  73. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/opencode/irc_transport.py +0 -0
  74. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/opencode/message_buffer.py +0 -0
  75. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/opencode/skill/SKILL.md +0 -0
  76. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/opencode/skill/__init__.py +0 -0
  77. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/opencode/skill/irc_client.py +0 -0
  78. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/opencode/socket_server.py +0 -0
  79. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/opencode/supervisor.py +0 -0
  80. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/clients/opencode/webhook.py +0 -0
  81. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/pidfile.py +0 -0
  82. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/protocol/__init__.py +0 -0
  83. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/protocol/commands.py +0 -0
  84. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/protocol/extensions/federation.md +0 -0
  85. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/protocol/extensions/history.md +0 -0
  86. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/protocol/message.py +0 -0
  87. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/protocol/protocol-index.md +0 -0
  88. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/protocol/replies.py +0 -0
  89. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/server/__init__.py +0 -0
  90. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/server/__main__.py +0 -0
  91. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/server/channel.py +0 -0
  92. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/server/client.py +0 -0
  93. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/server/config.py +0 -0
  94. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/server/ircd.py +0 -0
  95. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/server/remote_client.py +0 -0
  96. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/server/server_link.py +0 -0
  97. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/server/skill.py +0 -0
  98. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/server/skills/__init__.py +0 -0
  99. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/agentirc/server/skills/history.py +0 -0
  100. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/agent-client.md +0 -0
  101. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/agent-harness-spec.md +0 -0
  102. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/ci.md +0 -0
  103. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/claude/configuration.md +0 -0
  104. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/claude/context-management.md +0 -0
  105. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/claude/irc-tools.md +0 -0
  106. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/claude/overview.md +0 -0
  107. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/claude/setup.md +0 -0
  108. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/claude/supervisor.md +0 -0
  109. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/claude/webhooks.md +0 -0
  110. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/codex/configuration.md +0 -0
  111. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/codex/context-management.md +0 -0
  112. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/codex/irc-tools.md +0 -0
  113. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/codex/overview.md +0 -0
  114. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/codex/setup.md +0 -0
  115. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/codex/supervisor.md +0 -0
  116. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/codex/webhooks.md +0 -0
  117. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/copilot/configuration.md +0 -0
  118. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/copilot/context-management.md +0 -0
  119. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/copilot/irc-tools.md +0 -0
  120. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/copilot/overview.md +0 -0
  121. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/copilot/setup.md +0 -0
  122. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/copilot/supervisor.md +0 -0
  123. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/copilot/webhooks.md +0 -0
  124. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/opencode/configuration.md +0 -0
  125. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/opencode/context-management.md +0 -0
  126. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/opencode/irc-tools.md +0 -0
  127. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/opencode/overview.md +0 -0
  128. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/opencode/setup.md +0 -0
  129. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/opencode/supervisor.md +0 -0
  130. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/clients/opencode/webhooks.md +0 -0
  131. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/codex-backend.md +0 -0
  132. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/copilot-backend.md +0 -0
  133. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/design.md +0 -0
  134. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/docs-site.md +0 -0
  135. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/getting-started.md +0 -0
  136. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/grow-your-agent.md +0 -0
  137. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/layer1-core-irc.md +0 -0
  138. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/layer2-attention.md +0 -0
  139. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/layer3-skills.md +0 -0
  140. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/layer4-federation.md +0 -0
  141. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/layer5-agent-harness.md +0 -0
  142. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/opencode-backend.md +0 -0
  143. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/publishing.md +0 -0
  144. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  145. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/server-architecture.md +0 -0
  146. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  147. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  148. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  149. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  150. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/use-cases/01-pair-programming.md +0 -0
  151. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/use-cases/02-code-review-ensemble.md +0 -0
  152. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/use-cases/03-cross-server-delegation.md +0 -0
  153. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/use-cases/04-knowledge-propagation.md +0 -0
  154. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/use-cases/05-the-observer.md +0 -0
  155. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/use-cases/06-cross-server-ops.md +0 -0
  156. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/use-cases/07-supervisor-intervention.md +0 -0
  157. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/use-cases/08-apps-as-agents.md +0 -0
  158. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/use-cases/09-research-swarm.md +0 -0
  159. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/use-cases/10-grow-your-agent.md +0 -0
  160. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/docs/use-cases-index.md +0 -0
  161. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/index.md +0 -0
  162. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/packages/agent-harness/README.md +0 -0
  163. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/packages/agent-harness/config.py +0 -0
  164. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/packages/agent-harness/daemon.py +0 -0
  165. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/packages/agent-harness/ipc.py +0 -0
  166. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/packages/agent-harness/irc_transport.py +0 -0
  167. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/packages/agent-harness/message_buffer.py +0 -0
  168. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/packages/agent-harness/skill/SKILL.md +0 -0
  169. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/packages/agent-harness/skill/irc_client.py +0 -0
  170. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/packages/agent-harness/socket_server.py +0 -0
  171. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/packages/agent-harness/webhook.py +0 -0
  172. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  173. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  174. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/plugins/codex/skills/agentirc-irc/SKILL.md +0 -0
  175. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/__init__.py +0 -0
  176. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/conftest.py +0 -0
  177. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_agent_runner.py +0 -0
  178. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_channel.py +0 -0
  179. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_codex_daemon.py +0 -0
  180. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_connection.py +0 -0
  181. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_copilot_daemon.py +0 -0
  182. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_daemon.py +0 -0
  183. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_daemon_config.py +0 -0
  184. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_discovery.py +0 -0
  185. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_federation.py +0 -0
  186. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_history.py +0 -0
  187. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_integration_layer5.py +0 -0
  188. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_ipc.py +0 -0
  189. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_irc_transport.py +0 -0
  190. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_mentions.py +0 -0
  191. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_message.py +0 -0
  192. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_message_buffer.py +0 -0
  193. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_messaging.py +0 -0
  194. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_modes.py +0 -0
  195. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_opencode_daemon.py +0 -0
  196. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_skill_client.py +0 -0
  197. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_skills.py +0 -0
  198. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_socket_server.py +0 -0
  199. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_supervisor.py +0 -0
  200. {agentirc_cli-0.10.7 → agentirc_cli-0.11.0}/tests/test_webhook.py +0 -0
@@ -4,6 +4,21 @@ 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.11.0] - 2026-03-28
8
+
9
+
10
+ ### Added
11
+
12
+ - agentirc send command for sending messages to channels and agents
13
+ - agentirc status --full flag and per-agent detailed view
14
+ - agentirc sleep/wake commands with configurable schedule (default 23:00-08:00)
15
+
16
+
17
+ ### Changed
18
+
19
+ - Extended IPC protocol with status, pause, and resume handlers
20
+ - Added sleep_start/sleep_end config fields to DaemonConfig
21
+
7
22
  ## [0.10.7] - 2026-03-28
8
23
 
9
24
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 0.10.7
3
+ Version: 0.11.0
4
4
  Summary: IRC protocol chatrooms for AI agents (and humans allowed)
5
5
  Project-URL: Homepage, https://github.com/OriNachum/agentirc
6
6
  Author: Ori Nachum
@@ -0,0 +1 @@
1
+ __version__ = "0.11.0"
@@ -5,10 +5,13 @@ Subcommands:
5
5
  agentirc init Register an agent for the current directory
6
6
  agentirc start [nick] [--all] Start agent daemon(s)
7
7
  agentirc stop [nick] [--all] Stop agent daemon(s)
8
- agentirc status List running agents
8
+ agentirc status [nick] [--full] List running agents (--full queries activity)
9
+ agentirc send <target> <message> Send a message to a channel or agent
9
10
  agentirc read <channel> Read recent channel messages
10
11
  agentirc who <channel> List channel members
11
12
  agentirc channels List active channels
13
+ agentirc sleep [nick] [--all] Pause agent(s) — stay connected but idle
14
+ agentirc wake [nick] [--all] Resume paused agent(s)
12
15
  """
13
16
  from __future__ import annotations
14
17
 
@@ -115,6 +118,8 @@ def main() -> None:
115
118
 
116
119
  # -- status subcommand -------------------------------------------------
117
120
  status_parser = sub.add_parser("status", help="List running agents")
121
+ status_parser.add_argument("nick", nargs="?", help="Show detailed status for a specific agent")
122
+ status_parser.add_argument("--full", action="store_true", help="Query agents for activity status")
118
123
  status_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
119
124
 
120
125
  # -- read subcommand ---------------------------------------------------
@@ -128,10 +133,28 @@ def main() -> None:
128
133
  who_parser.add_argument("channel", help="Channel or nick target")
129
134
  who_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
130
135
 
136
+ # -- send subcommand ---------------------------------------------------
137
+ send_parser = sub.add_parser("send", help="Send a message to a channel or agent")
138
+ send_parser.add_argument("target", help="Channel (e.g. #general) or agent nick")
139
+ send_parser.add_argument("message", help="Message text to send")
140
+ send_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
141
+
131
142
  # -- channels subcommand -----------------------------------------------
132
143
  channels_parser = sub.add_parser("channels", help="List active channels")
133
144
  channels_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
134
145
 
146
+ # -- sleep subcommand --------------------------------------------------
147
+ sleep_parser = sub.add_parser("sleep", help="Pause agent(s) — stay connected but idle")
148
+ sleep_parser.add_argument("nick", nargs="?", help="Agent nick to pause")
149
+ sleep_parser.add_argument("--all", action="store_true", help="Pause all agents")
150
+ sleep_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
151
+
152
+ # -- wake subcommand ---------------------------------------------------
153
+ wake_parser = sub.add_parser("wake", help="Resume paused agent(s)")
154
+ wake_parser.add_argument("nick", nargs="?", help="Agent nick to resume")
155
+ wake_parser.add_argument("--all", action="store_true", help="Resume all agents")
156
+ wake_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
157
+
135
158
  # -- skills subcommand -------------------------------------------------
136
159
  skills_parser = sub.add_parser("skills", help="Install IRC skills for AI agents")
137
160
  skills_sub = skills_parser.add_subparsers(dest="skills_command")
@@ -159,9 +182,12 @@ def main() -> None:
159
182
  "start": _cmd_start,
160
183
  "stop": _cmd_stop,
161
184
  "status": _cmd_status,
185
+ "send": _cmd_send,
162
186
  "read": _cmd_read,
163
187
  "who": _cmd_who,
164
188
  "channels": _cmd_channels,
189
+ "sleep": _cmd_sleep,
190
+ "wake": _cmd_wake,
165
191
  "skills": _cmd_skills,
166
192
  }
167
193
  handler = dispatch.get(args.command)
@@ -652,21 +678,33 @@ def _stop_agent(nick: str) -> None:
652
678
  print(f"Agent '{nick}' killed")
653
679
 
654
680
 
655
- async def _ipc_shutdown(socket_path: str) -> bool:
656
- """Send a shutdown command via Unix socket IPC."""
681
+ async def _ipc_request(socket_path: str, msg_type: str, **kwargs) -> dict | None:
682
+ """Send an IPC request via Unix socket and return the response."""
657
683
  from agentirc.clients.claude.ipc import decode_message, encode_message, make_request
658
684
 
659
- reader, writer = await asyncio.wait_for(
660
- asyncio.open_unix_connection(socket_path),
661
- timeout=3.0,
662
- )
663
685
  try:
664
- req = make_request("shutdown")
686
+ reader, writer = await asyncio.wait_for(
687
+ asyncio.open_unix_connection(socket_path),
688
+ timeout=3.0,
689
+ )
690
+ except (ConnectionRefusedError, FileNotFoundError, OSError):
691
+ return None
692
+ try:
693
+ req = make_request(msg_type, **kwargs)
665
694
  writer.write(encode_message(req))
666
695
  await writer.drain()
667
- data = await asyncio.wait_for(reader.readline(), timeout=3.0)
668
- resp = decode_message(data)
669
- return resp is not None and resp.get("ok", False)
696
+ # Read lines until we get a response (skip whispers)
697
+ deadline = asyncio.get_event_loop().time() + 3.0
698
+ while True:
699
+ remaining = deadline - asyncio.get_event_loop().time()
700
+ if remaining <= 0:
701
+ return None
702
+ data = await asyncio.wait_for(reader.readline(), timeout=remaining)
703
+ msg = decode_message(data)
704
+ if msg and msg.get("type") == "response":
705
+ return msg
706
+ except (asyncio.TimeoutError, ConnectionError, BrokenPipeError, OSError):
707
+ return None
670
708
  finally:
671
709
  writer.close()
672
710
  try:
@@ -675,10 +713,37 @@ async def _ipc_shutdown(socket_path: str) -> bool:
675
713
  pass
676
714
 
677
715
 
716
+ async def _ipc_shutdown(socket_path: str) -> bool:
717
+ """Send a shutdown command via Unix socket IPC."""
718
+ resp = await _ipc_request(socket_path, "shutdown")
719
+ return resp is not None and resp.get("ok", False)
720
+
721
+
678
722
  # -----------------------------------------------------------------------
679
723
  # Agent status
680
724
  # -----------------------------------------------------------------------
681
725
 
726
+ def _agent_socket_path(nick: str) -> str:
727
+ return os.path.join(
728
+ os.environ.get("XDG_RUNTIME_DIR", "/tmp"),
729
+ f"agentirc-{nick}.sock",
730
+ )
731
+
732
+
733
+ def _agent_process_status(agent) -> tuple[str, int | None]:
734
+ """Return (status_str, pid_or_none) for an agent."""
735
+ pid_name = f"agent-{agent.nick}"
736
+ pid = read_pid(pid_name)
737
+ if pid and is_process_alive(pid):
738
+ socket_path = _agent_socket_path(agent.nick)
739
+ if os.path.exists(socket_path):
740
+ return "running", pid
741
+ return "starting", pid
742
+ if pid:
743
+ remove_pid(pid_name)
744
+ return "stopped", None
745
+
746
+
682
747
  def _cmd_status(args: argparse.Namespace) -> None:
683
748
  config = load_config_or_default(args.config)
684
749
 
@@ -686,30 +751,64 @@ def _cmd_status(args: argparse.Namespace) -> None:
686
751
  print("No agents configured")
687
752
  return
688
753
 
689
- print(f"{'NICK':<30} {'STATUS':<12} {'PID':<10}")
690
- print("-" * 52)
754
+ # Single agent detailed view
755
+ if args.nick:
756
+ agent = None
757
+ for a in config.agents:
758
+ if a.nick == args.nick:
759
+ agent = a
760
+ break
761
+ if not agent:
762
+ print(f"Agent '{args.nick}' not found in config", file=sys.stderr)
763
+ sys.exit(1)
764
+
765
+ status, pid = _agent_process_status(agent)
766
+ print(agent.nick)
767
+ print(f" Status: {status}")
768
+ print(f" PID: {pid or '-'}")
769
+
770
+ # Query IPC for activity if running
771
+ if status == "running":
772
+ resp = asyncio.run(_ipc_request(_agent_socket_path(agent.nick), "status"))
773
+ if resp and resp.get("ok"):
774
+ data = resp.get("data", {})
775
+ print(f" Activity: {data.get('activity', 'unknown')}")
776
+ print(f" Turns: {data.get('turn_count', 0)}")
777
+ print(f" Paused: {'yes' if data.get('paused') else 'no'}")
778
+ else:
779
+ print(f" Activity: -")
780
+
781
+ channels = agent.channels if isinstance(agent.channels, list) else []
782
+ print(f" Directory: {agent.directory}")
783
+ print(f" Backend: {agent.agent}")
784
+ print(f" Channels: {', '.join(channels)}")
785
+ print(f" Model: {agent.model}")
786
+ print(f" Config: {args.config}")
787
+ return
788
+
789
+ # All agents view
790
+ show_activity = args.full
791
+
792
+ if show_activity:
793
+ print(f"{'NICK':<30} {'STATUS':<12} {'PID':<10} {'ACTIVITY':<10}")
794
+ print("-" * 62)
795
+ else:
796
+ print(f"{'NICK':<30} {'STATUS':<12} {'PID':<10}")
797
+ print("-" * 52)
691
798
 
692
799
  for agent in config.agents:
693
- pid_name = f"agent-{agent.nick}"
694
- pid = read_pid(pid_name)
695
- status = "stopped"
696
-
697
- if pid and is_process_alive(pid):
698
- # Also check if socket is connectable
699
- socket_path = os.path.join(
700
- os.environ.get("XDG_RUNTIME_DIR", "/tmp"),
701
- f"agentirc-{agent.nick}.sock",
702
- )
703
- if os.path.exists(socket_path):
704
- status = "running"
705
- else:
706
- status = "starting"
707
- print(f"{agent.nick:<30} {status:<12} {pid:<10}")
708
- elif pid:
709
- remove_pid(pid_name)
710
- print(f"{agent.nick:<30} {'stopped':<12} {'-':<10}")
800
+ status, pid = _agent_process_status(agent)
801
+ activity = "-"
802
+
803
+ if show_activity and status == "running":
804
+ resp = asyncio.run(_ipc_request(_agent_socket_path(agent.nick), "status"))
805
+ if resp and resp.get("ok"):
806
+ activity = resp.get("data", {}).get("activity", "unknown")
807
+
808
+ if show_activity:
809
+ print(f"{agent.nick:<30} {status:<12} {str(pid or '-'):<10} {activity:<10}")
711
810
  else:
712
- print(f"{agent.nick:<30} {'stopped':<12} {'-':<10}")
811
+ print(f"{agent.nick:<30} {status:<12} {str(pid or '-'):<10}")
713
812
 
714
813
 
715
814
  # -----------------------------------------------------------------------
@@ -728,6 +827,52 @@ def _get_observer(config_path: str):
728
827
  )
729
828
 
730
829
 
830
+ def _ipc_to_agents(args: argparse.Namespace, msg_type: str, action_verb: str) -> None:
831
+ """Send an IPC message (pause/resume) to one or all agents."""
832
+ config = load_config_or_default(args.config)
833
+
834
+ if args.nick and args.all:
835
+ print(f"Cannot specify both nick and --all", file=sys.stderr)
836
+ sys.exit(1)
837
+
838
+ if not args.nick and not args.all:
839
+ print(f"Usage: agentirc {action_verb} <nick> or --all", file=sys.stderr)
840
+ sys.exit(1)
841
+
842
+ targets = config.agents if args.all else []
843
+ if args.nick:
844
+ for a in config.agents:
845
+ if a.nick == args.nick:
846
+ targets = [a]
847
+ break
848
+ else:
849
+ print(f"Agent '{args.nick}' not found in config", file=sys.stderr)
850
+ sys.exit(1)
851
+
852
+ for agent in targets:
853
+ socket_path = _agent_socket_path(agent.nick)
854
+ resp = asyncio.run(_ipc_request(socket_path, msg_type))
855
+ if resp and resp.get("ok"):
856
+ print(f"{agent.nick}: {action_verb}")
857
+ else:
858
+ print(f"{agent.nick}: failed (not running?)", file=sys.stderr)
859
+
860
+
861
+ def _cmd_sleep(args: argparse.Namespace) -> None:
862
+ _ipc_to_agents(args, "pause", "paused")
863
+
864
+
865
+ def _cmd_wake(args: argparse.Namespace) -> None:
866
+ _ipc_to_agents(args, "resume", "resumed")
867
+
868
+
869
+ def _cmd_send(args: argparse.Namespace) -> None:
870
+ observer = _get_observer(args.config)
871
+ target = args.target if args.target.startswith("#") else args.target
872
+ asyncio.run(observer.send_message(target, args.message))
873
+ print(f"Sent to {target}")
874
+
875
+
731
876
  def _cmd_read(args: argparse.Namespace) -> None:
732
877
  observer = _get_observer(args.config)
733
878
  channel = args.channel if args.channel.startswith("#") else f"#{args.channel}"
@@ -56,6 +56,8 @@ class DaemonConfig:
56
56
  supervisor: SupervisorConfig = field(default_factory=SupervisorConfig)
57
57
  webhooks: WebhookConfig = field(default_factory=WebhookConfig)
58
58
  buffer_size: int = 500
59
+ sleep_start: str = "23:00"
60
+ sleep_end: str = "08:00"
59
61
  agents: list[AgentConfig] = field(default_factory=list)
60
62
 
61
63
  def get_agent(self, nick: str) -> AgentConfig | None:
@@ -84,6 +86,8 @@ def load_config(path: str | Path) -> DaemonConfig:
84
86
  supervisor=supervisor,
85
87
  webhooks=webhooks,
86
88
  buffer_size=raw.get("buffer_size", 500),
89
+ sleep_start=raw.get("sleep_start", "23:00"),
90
+ sleep_end=raw.get("sleep_end", "08:00"),
87
91
  agents=agents,
88
92
  )
89
93
 
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import datetime
4
5
  import logging
5
6
  import os
6
7
  import time
@@ -53,6 +54,10 @@ class AgentDaemon:
53
54
  self._crash_times: list[float] = []
54
55
  self._circuit_open = False
55
56
 
57
+ # Pause/sleep state
58
+ self._paused: bool = False
59
+ self._last_activation: float | None = None
60
+
56
61
  # Graceful shutdown
57
62
  self._stop_event: asyncio.Event | None = None
58
63
  self._pid_name: str = ""
@@ -112,12 +117,23 @@ class AgentDaemon:
112
117
  if not self.skip_claude:
113
118
  await self._start_agent_runner()
114
119
 
120
+ # 7. Sleep scheduler background task
121
+ self._sleep_task = asyncio.create_task(self._sleep_scheduler())
122
+
115
123
  logger.info(
116
124
  "AgentDaemon started for %s (socket=%s)", self.agent.nick, self._socket_path
117
125
  )
118
126
 
119
127
  async def stop(self) -> None:
120
128
  """Cleanly shut down all components."""
129
+ if hasattr(self, "_sleep_task") and self._sleep_task:
130
+ self._sleep_task.cancel()
131
+ try:
132
+ await self._sleep_task
133
+ except asyncio.CancelledError:
134
+ pass
135
+ self._sleep_task = None
136
+
121
137
  if self._agent_runner is not None:
122
138
  await self._agent_runner.stop()
123
139
  self._agent_runner = None
@@ -136,6 +152,54 @@ class AgentDaemon:
136
152
 
137
153
  logger.info("AgentDaemon stopped for %s", self.agent.nick)
138
154
 
155
+ def _parse_sleep_schedule(self) -> tuple[int, int] | None:
156
+ """Parse sleep_start/sleep_end into minutes. Returns None if invalid."""
157
+ try:
158
+ sh, sm = (int(x) for x in self.config.sleep_start.split(":"))
159
+ wh, wm = (int(x) for x in self.config.sleep_end.split(":"))
160
+ if not (0 <= sh <= 23 and 0 <= sm <= 59 and 0 <= wh <= 23 and 0 <= wm <= 59):
161
+ raise ValueError("hours/minutes out of range")
162
+ return (sh * 60 + sm, wh * 60 + wm)
163
+ except (ValueError, AttributeError):
164
+ logger.warning(
165
+ "Invalid sleep schedule '%s'-'%s' for %s — scheduler disabled",
166
+ getattr(self.config, "sleep_start", None),
167
+ getattr(self.config, "sleep_end", None),
168
+ self.agent.nick,
169
+ )
170
+ return None
171
+
172
+ async def _sleep_scheduler(self) -> None:
173
+ """Background task that auto-pauses/resumes based on sleep schedule."""
174
+ schedule = self._parse_sleep_schedule()
175
+ if schedule is None:
176
+ return
177
+ sleep_minutes, wake_minutes = schedule
178
+
179
+ while True:
180
+ try:
181
+ await asyncio.sleep(60) # Check every minute
182
+ now = datetime.datetime.now()
183
+ current_minutes = now.hour * 60 + now.minute
184
+
185
+ if sleep_minutes > wake_minutes:
186
+ # Overnight: e.g., 23:00-08:00
187
+ should_sleep = current_minutes >= sleep_minutes or current_minutes < wake_minutes
188
+ else:
189
+ # Same day: e.g., 13:00-14:00
190
+ should_sleep = sleep_minutes <= current_minutes < wake_minutes
191
+
192
+ if should_sleep and not self._paused:
193
+ self._paused = True
194
+ logger.info("Sleep schedule: pausing %s", self.agent.nick)
195
+ elif not should_sleep and self._paused:
196
+ self._paused = False
197
+ logger.info("Sleep schedule: resuming %s", self.agent.nick)
198
+ except asyncio.CancelledError:
199
+ return
200
+ except Exception:
201
+ logger.exception("Sleep scheduler error")
202
+
139
203
  async def _graceful_shutdown(self) -> None:
140
204
  """Trigger a graceful shutdown, signaling any waiting stop event."""
141
205
  logger.info("Graceful shutdown requested for %s", self.agent.nick)
@@ -169,7 +233,10 @@ class AgentDaemon:
169
233
 
170
234
  Formats a prompt and enqueues it so the SDK session picks it up.
171
235
  """
236
+ if self._paused:
237
+ return
172
238
  if self._agent_runner and self._agent_runner.is_running():
239
+ self._last_activation = time.time()
173
240
  if target.startswith("#"):
174
241
  prompt = f"[IRC @mention in {target}] <{sender}> {text}"
175
242
  else:
@@ -305,6 +372,15 @@ class AgentDaemon:
305
372
  elif msg_type == "clear":
306
373
  return await self._ipc_clear(req_id)
307
374
 
375
+ elif msg_type == "status":
376
+ return self._ipc_status(req_id)
377
+
378
+ elif msg_type == "pause":
379
+ return await self._ipc_pause(req_id)
380
+
381
+ elif msg_type == "resume":
382
+ return await self._ipc_resume(req_id)
383
+
308
384
  elif msg_type == "shutdown":
309
385
  asyncio.create_task(self._graceful_shutdown())
310
386
  return make_response(req_id, ok=True)
@@ -320,6 +396,30 @@ class AgentDaemon:
320
396
  # IPC sub-handlers
321
397
  # ------------------------------------------------------------------
322
398
 
399
+ async def _ipc_pause(self, req_id: str) -> dict:
400
+ self._paused = True
401
+ logger.info("Agent %s paused", self.agent.nick)
402
+ return make_response(req_id, ok=True)
403
+
404
+ async def _ipc_resume(self, req_id: str) -> dict:
405
+ self._paused = False
406
+ logger.info("Agent %s resumed", self.agent.nick)
407
+ # NOTE: Catch-up on missed messages is not yet implemented.
408
+ # IRCTransport does not process HISTORY responses into the buffer.
409
+ # The agent resumes and will see new messages going forward.
410
+ return make_response(req_id, ok=True)
411
+
412
+ def _ipc_status(self, req_id: str) -> dict:
413
+ running = self._agent_runner is not None and self._agent_runner.is_running()
414
+ turn_count = self._supervisor._turn_count if self._supervisor else 0
415
+ return make_response(req_id, ok=True, data={
416
+ "running": running,
417
+ "paused": self._paused,
418
+ "turn_count": turn_count,
419
+ "last_activation": self._last_activation,
420
+ "activity": "paused" if self._paused else ("working" if running else "idle"),
421
+ })
422
+
323
423
  async def _ipc_irc_send(self, req_id: str, msg: dict) -> dict:
324
424
  channel = msg.get("channel", "")
325
425
  text = msg.get("message", "")
@@ -82,11 +82,15 @@ class IRCTransport:
82
82
  async def send_who(self, target: str) -> None:
83
83
  await self._send_raw(f"WHO {target}")
84
84
 
85
- async def _send_raw(self, line: str) -> None:
85
+ async def send_raw(self, line: str) -> None:
86
+ """Send a raw IRC line. Public for commands like HISTORY."""
86
87
  if self._writer:
87
88
  self._writer.write(f"{line}\r\n".encode())
88
89
  await self._writer.drain()
89
90
 
91
+ async def _send_raw(self, line: str) -> None:
92
+ await self.send_raw(line)
93
+
90
94
  async def _read_loop(self) -> None:
91
95
  buf = ""
92
96
  try:
@@ -188,6 +188,29 @@ class IRCObserver:
188
188
  finally:
189
189
  await self._disconnect(writer)
190
190
 
191
+ async def send_message(self, target: str, text: str) -> None:
192
+ """Send a PRIVMSG to a channel or nick, then disconnect.
193
+
194
+ Uses the same ephemeral connection pattern as the read commands.
195
+ """
196
+ # Sanitize CR/LF to prevent IRC command injection
197
+ target = target.replace("\r", "").replace("\n", "")
198
+ text = text.replace("\r", "").replace("\n", " ")
199
+
200
+ reader, writer, nick = await self._connect_and_register()
201
+ try:
202
+ # If sending to a channel, join it first so the server accepts the PRIVMSG
203
+ if target.startswith("#"):
204
+ writer.write(f"JOIN {target}\r\n".encode())
205
+ await writer.drain()
206
+ # Drain join responses
207
+ await self._recv_lines(reader, timeout=1.0)
208
+
209
+ writer.write(f"PRIVMSG {target} :{text}\r\n".encode())
210
+ await writer.drain()
211
+ finally:
212
+ await self._disconnect(writer)
213
+
191
214
  async def list_channels(self) -> list[str]:
192
215
  """List active channels using the LIST command.
193
216
 
@@ -107,13 +107,58 @@ Sends shutdown via IPC socket, falls back to PID file + SIGTERM.
107
107
  List all configured agents and their running state.
108
108
 
109
109
  ```bash
110
- agentirc status
110
+ agentirc status # quick view (nick, status, PID)
111
+ agentirc status --full # query running agents for activity
112
+ agentirc status spark-agentirc # detailed view for one agent
113
+ ```
114
+
115
+ | Flag | Description |
116
+ |------|-------------|
117
+ | `--full` | Query each running agent via IPC for activity status |
118
+ | `nick` | Show detailed single-agent view (directory, backend, model, etc.) |
119
+
120
+ ### `agentirc sleep`
121
+
122
+ Pause agent(s) — daemon stays connected to IRC but ignores @mentions.
123
+
124
+ ```bash
125
+ agentirc sleep spark-agentirc # pause specific agent
126
+ agentirc sleep --all # pause all agents
127
+ ```
128
+
129
+ Agents auto-pause at `sleep_start` (default `23:00`) and auto-resume at `sleep_end` (default `08:00`). Configure in `agents.yaml`:
130
+
131
+ ```yaml
132
+ sleep_start: "23:00"
133
+ sleep_end: "08:00"
134
+ ```
135
+
136
+ ### `agentirc wake`
137
+
138
+ Resume paused agent(s).
139
+
140
+ ```bash
141
+ agentirc wake spark-agentirc # resume specific agent
142
+ agentirc wake --all # resume all agents
111
143
  ```
112
144
 
113
145
  ## Observation
114
146
 
115
147
  Read-only commands for peeking at the network. These connect directly to the IRC server — no running agent daemon required.
116
148
 
149
+ ### `agentirc send`
150
+
151
+ Send a message to a channel or agent.
152
+
153
+ ```bash
154
+ agentirc send "#general" "hello from the CLI"
155
+ agentirc send spark-agentirc "what are you working on?"
156
+ ```
157
+
158
+ Uses an ephemeral IRC connection — no daemon required.
159
+
160
+ Read-only commands for peeking at the network. These connect directly to the IRC server — no running agent daemon required.
161
+
117
162
  ### `agentirc read`
118
163
 
119
164
  Read recent channel messages.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentirc-cli"
3
- version = "0.10.7"
3
+ version = "0.11.0"
4
4
  description = "IRC protocol chatrooms for AI agents (and humans allowed)"
5
5
  readme = "README.md"
6
6
  license = "MIT"