agentirc-cli 0.15.0__tar.gz → 0.15.1__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 (227) hide show
  1. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/CHANGELOG.md +8 -0
  2. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/PKG-INFO +1 -1
  3. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/overview/renderer_web.py +69 -3
  4. agentirc_cli-0.15.1/agentirc/pidfile.py +84 -0
  5. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/overview.md +14 -0
  6. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/pyproject.toml +1 -1
  7. agentirc_cli-0.15.1/tests/test_overview_web.py +165 -0
  8. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/uv.lock +1 -1
  9. agentirc_cli-0.15.0/agentirc/pidfile.py +0 -49
  10. agentirc_cli-0.15.0/tests/test_overview_web.py +0 -58
  11. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/.claude/skills/pr-review/SKILL.md +0 -0
  12. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/.github/workflows/pages.yml +0 -0
  13. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/.github/workflows/publish.yml +0 -0
  14. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/.github/workflows/tests.yml +0 -0
  15. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/.gitignore +0 -0
  16. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/.markdownlint-cli2.yaml +0 -0
  17. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/.pr_agent.toml +0 -0
  18. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/CLAUDE.md +0 -0
  19. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/CNAME +0 -0
  20. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/Gemfile +0 -0
  21. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/Gemfile.lock +0 -0
  22. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/LICENSE +0 -0
  23. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/README.md +0 -0
  24. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/_config.yml +0 -0
  25. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/_sass/color_schemes/anthropic.scss +0 -0
  26. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/_sass/custom/custom.scss +0 -0
  27. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/__init__.py +0 -0
  28. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/__main__.py +0 -0
  29. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/cli.py +0 -0
  30. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/__init__.py +0 -0
  31. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/claude/__init__.py +0 -0
  32. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/claude/__main__.py +0 -0
  33. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/claude/agent_runner.py +0 -0
  34. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/claude/config.py +0 -0
  35. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/claude/daemon.py +0 -0
  36. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/claude/ipc.py +0 -0
  37. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/claude/irc_transport.py +0 -0
  38. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/claude/message_buffer.py +0 -0
  39. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/claude/skill/SKILL.md +0 -0
  40. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/claude/skill/__init__.py +0 -0
  41. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/claude/skill/irc_client.py +0 -0
  42. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/claude/socket_server.py +0 -0
  43. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/claude/supervisor.py +0 -0
  44. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/claude/webhook.py +0 -0
  45. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/codex/__init__.py +0 -0
  46. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/codex/agent_runner.py +0 -0
  47. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/codex/config.py +0 -0
  48. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/codex/daemon.py +0 -0
  49. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/codex/ipc.py +0 -0
  50. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/codex/irc_transport.py +0 -0
  51. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/codex/message_buffer.py +0 -0
  52. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/codex/skill/SKILL.md +0 -0
  53. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/codex/skill/__init__.py +0 -0
  54. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/codex/skill/irc_client.py +0 -0
  55. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/codex/socket_server.py +0 -0
  56. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/codex/supervisor.py +0 -0
  57. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/codex/webhook.py +0 -0
  58. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/copilot/__init__.py +0 -0
  59. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/copilot/agent_runner.py +0 -0
  60. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/copilot/config.py +0 -0
  61. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/copilot/daemon.py +0 -0
  62. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/copilot/ipc.py +0 -0
  63. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/copilot/irc_transport.py +0 -0
  64. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/copilot/message_buffer.py +0 -0
  65. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/copilot/skill/SKILL.md +0 -0
  66. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/copilot/skill/__init__.py +0 -0
  67. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/copilot/skill/irc_client.py +0 -0
  68. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/copilot/socket_server.py +0 -0
  69. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/copilot/supervisor.py +0 -0
  70. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/copilot/webhook.py +0 -0
  71. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/opencode/__init__.py +0 -0
  72. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/opencode/agent_runner.py +0 -0
  73. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/opencode/config.py +0 -0
  74. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/opencode/daemon.py +0 -0
  75. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/opencode/ipc.py +0 -0
  76. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/opencode/irc_transport.py +0 -0
  77. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/opencode/message_buffer.py +0 -0
  78. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/opencode/skill/SKILL.md +0 -0
  79. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/opencode/skill/__init__.py +0 -0
  80. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/opencode/skill/irc_client.py +0 -0
  81. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/opencode/socket_server.py +0 -0
  82. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/opencode/supervisor.py +0 -0
  83. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/clients/opencode/webhook.py +0 -0
  84. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/learn_prompt.py +0 -0
  85. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/observer.py +0 -0
  86. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/overview/__init__.py +0 -0
  87. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/overview/collector.py +0 -0
  88. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/overview/model.py +0 -0
  89. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/overview/renderer_text.py +0 -0
  90. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/overview/web/style.css +0 -0
  91. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/protocol/__init__.py +0 -0
  92. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/protocol/commands.py +0 -0
  93. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/protocol/extensions/federation.md +0 -0
  94. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/protocol/extensions/history.md +0 -0
  95. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/protocol/extensions/rooms.md +0 -0
  96. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/protocol/extensions/tags.md +0 -0
  97. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/protocol/message.py +0 -0
  98. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/protocol/protocol-index.md +0 -0
  99. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/protocol/replies.py +0 -0
  100. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/server/__init__.py +0 -0
  101. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/server/__main__.py +0 -0
  102. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/server/channel.py +0 -0
  103. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/server/client.py +0 -0
  104. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/server/config.py +0 -0
  105. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/server/ircd.py +0 -0
  106. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/server/remote_client.py +0 -0
  107. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/server/room_store.py +0 -0
  108. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/server/rooms_util.py +0 -0
  109. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/server/server_link.py +0 -0
  110. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/server/skill.py +0 -0
  111. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/server/skills/__init__.py +0 -0
  112. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/server/skills/history.py +0 -0
  113. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/agentirc/server/skills/rooms.py +0 -0
  114. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/agent-client.md +0 -0
  115. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/agent-harness-spec.md +0 -0
  116. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/ci.md +0 -0
  117. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/cli.md +0 -0
  118. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/claude/configuration.md +0 -0
  119. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/claude/context-management.md +0 -0
  120. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/claude/irc-tools.md +0 -0
  121. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/claude/overview.md +0 -0
  122. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/claude/setup.md +0 -0
  123. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/claude/supervisor.md +0 -0
  124. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/claude/webhooks.md +0 -0
  125. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/codex/configuration.md +0 -0
  126. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/codex/context-management.md +0 -0
  127. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/codex/irc-tools.md +0 -0
  128. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/codex/overview.md +0 -0
  129. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/codex/setup.md +0 -0
  130. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/codex/supervisor.md +0 -0
  131. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/codex/webhooks.md +0 -0
  132. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/copilot/configuration.md +0 -0
  133. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/copilot/context-management.md +0 -0
  134. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/copilot/irc-tools.md +0 -0
  135. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/copilot/overview.md +0 -0
  136. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/copilot/setup.md +0 -0
  137. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/copilot/supervisor.md +0 -0
  138. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/copilot/webhooks.md +0 -0
  139. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/opencode/configuration.md +0 -0
  140. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/opencode/context-management.md +0 -0
  141. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/opencode/irc-tools.md +0 -0
  142. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/opencode/overview.md +0 -0
  143. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/opencode/setup.md +0 -0
  144. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/opencode/supervisor.md +0 -0
  145. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/clients/opencode/webhooks.md +0 -0
  146. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/codex-backend.md +0 -0
  147. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/copilot-backend.md +0 -0
  148. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/design.md +0 -0
  149. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/docs-site.md +0 -0
  150. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/getting-started.md +0 -0
  151. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/grow-your-agent.md +0 -0
  152. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/layer1-core-irc.md +0 -0
  153. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/layer2-attention.md +0 -0
  154. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/layer3-skills.md +0 -0
  155. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/layer4-federation.md +0 -0
  156. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/layer5-agent-harness.md +0 -0
  157. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/opencode-backend.md +0 -0
  158. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/publishing.md +0 -0
  159. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  160. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/rooms.md +0 -0
  161. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/server-architecture.md +0 -0
  162. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  163. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  164. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  165. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  166. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  167. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  168. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/use-cases/01-pair-programming.md +0 -0
  169. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/use-cases/02-code-review-ensemble.md +0 -0
  170. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/use-cases/03-cross-server-delegation.md +0 -0
  171. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/use-cases/04-knowledge-propagation.md +0 -0
  172. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/use-cases/05-the-observer.md +0 -0
  173. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/use-cases/06-cross-server-ops.md +0 -0
  174. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/use-cases/07-supervisor-intervention.md +0 -0
  175. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/use-cases/08-apps-as-agents.md +0 -0
  176. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/use-cases/09-research-swarm.md +0 -0
  177. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/use-cases/10-grow-your-agent.md +0 -0
  178. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/docs/use-cases-index.md +0 -0
  179. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/index.md +0 -0
  180. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/packages/agent-harness/README.md +0 -0
  181. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/packages/agent-harness/config.py +0 -0
  182. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/packages/agent-harness/daemon.py +0 -0
  183. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/packages/agent-harness/ipc.py +0 -0
  184. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/packages/agent-harness/irc_transport.py +0 -0
  185. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/packages/agent-harness/message_buffer.py +0 -0
  186. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/packages/agent-harness/skill/SKILL.md +0 -0
  187. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/packages/agent-harness/skill/irc_client.py +0 -0
  188. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/packages/agent-harness/socket_server.py +0 -0
  189. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/packages/agent-harness/webhook.py +0 -0
  190. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  191. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  192. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/plugins/codex/skills/agentirc-irc/SKILL.md +0 -0
  193. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/__init__.py +0 -0
  194. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/conftest.py +0 -0
  195. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_agent_runner.py +0 -0
  196. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_channel.py +0 -0
  197. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_codex_daemon.py +0 -0
  198. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_connection.py +0 -0
  199. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_copilot_daemon.py +0 -0
  200. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_daemon.py +0 -0
  201. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_daemon_config.py +0 -0
  202. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_daemon_ipc.py +0 -0
  203. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_discovery.py +0 -0
  204. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_federation.py +0 -0
  205. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_history.py +0 -0
  206. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_integration_layer5.py +0 -0
  207. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_ipc.py +0 -0
  208. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_irc_transport.py +0 -0
  209. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_mentions.py +0 -0
  210. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_message.py +0 -0
  211. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_message_buffer.py +0 -0
  212. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_messaging.py +0 -0
  213. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_modes.py +0 -0
  214. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_opencode_daemon.py +0 -0
  215. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_overview_cli.py +0 -0
  216. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_overview_collector.py +0 -0
  217. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_overview_model.py +0 -0
  218. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_overview_renderer.py +0 -0
  219. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_room_persistence.py +0 -0
  220. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_rooms.py +0 -0
  221. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_rooms_federation.py +0 -0
  222. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_rooms_integration.py +0 -0
  223. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_skill_client.py +0 -0
  224. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_skills.py +0 -0
  225. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_socket_server.py +0 -0
  226. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_supervisor.py +0 -0
  227. {agentirc_cli-0.15.0 → agentirc_cli-0.15.1}/tests/test_webhook.py +0 -0
@@ -4,6 +4,14 @@ 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.15.1] - 2026-03-30
8
+
9
+
10
+ ### Fixed
11
+
12
+ - Overview serve: flush stdout so port URL is visible when backgrounded
13
+ - Overview serve: auto-kill previous instance for same server via PID/port files
14
+
7
15
  ## [0.15.0] - 2026-03-30
8
16
 
9
17
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 0.15.0
3
+ Version: 0.15.1
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
@@ -1,14 +1,23 @@
1
1
  """Render mesh overview as HTML and serve via HTTP."""
2
2
  from __future__ import annotations
3
3
 
4
+ import atexit
4
5
  import asyncio
5
6
  from http.server import HTTPServer, SimpleHTTPRequestHandler
7
+ import os
6
8
  from pathlib import Path
9
+ import signal
10
+ import threading
11
+ import time
7
12
 
8
13
  import re
9
14
 
10
15
  import mistune
11
16
 
17
+ from agentirc.pidfile import (
18
+ is_process_alive, read_pid, read_port,
19
+ remove_pid, remove_port, write_pid, write_port,
20
+ )
12
21
  from .collector import collect_mesh_state
13
22
  from .model import MeshState
14
23
  from .renderer_text import render_text
@@ -72,6 +81,45 @@ def render_html(
72
81
  </html>"""
73
82
 
74
83
 
84
+ def _stop_existing_overview(pid_name: str) -> None:
85
+ """Kill a previous overview instance if running, clean up its files."""
86
+ existing_pid = read_pid(pid_name)
87
+ if existing_pid and is_process_alive(existing_pid):
88
+ existing_port = read_port(pid_name)
89
+ try:
90
+ os.kill(existing_pid, signal.SIGTERM)
91
+ except (PermissionError, ProcessLookupError) as exc:
92
+ remove_pid(pid_name)
93
+ remove_port(pid_name)
94
+ port_msg = f", port {existing_port}" if existing_port else ""
95
+ print(
96
+ f"Warning: could not stop previous overview (PID {existing_pid}"
97
+ f"{port_msg}): {exc}",
98
+ flush=True,
99
+ )
100
+ return
101
+ for _ in range(20):
102
+ if not is_process_alive(existing_pid):
103
+ break
104
+ time.sleep(0.1)
105
+ else:
106
+ try:
107
+ os.kill(existing_pid, signal.SIGKILL)
108
+ except (PermissionError, ProcessLookupError):
109
+ pass
110
+ remove_pid(pid_name)
111
+ remove_port(pid_name)
112
+ port_msg = f", port {existing_port}" if existing_port else ""
113
+ print(
114
+ f"Stopped previous overview for '{pid_name.removeprefix('overview-')}'"
115
+ f" (PID {existing_pid}{port_msg})",
116
+ flush=True,
117
+ )
118
+ elif existing_pid:
119
+ remove_pid(pid_name)
120
+ remove_port(pid_name)
121
+
122
+
75
123
  def serve_web(
76
124
  host: str,
77
125
  port: int,
@@ -84,6 +132,8 @@ def serve_web(
84
132
  serve_port: int = 0,
85
133
  ) -> None:
86
134
  """Start a local HTTP server serving the live overview."""
135
+ pid_name = f"overview-{server_name}"
136
+ _stop_existing_overview(pid_name)
87
137
 
88
138
  class OverviewHandler(SimpleHTTPRequestHandler):
89
139
  def do_GET(self):
@@ -110,11 +160,27 @@ def serve_web(
110
160
 
111
161
  httpd = HTTPServer(("127.0.0.1", serve_port), OverviewHandler)
112
162
  actual_port = httpd.server_address[1]
113
- print(f"Overview dashboard: http://localhost:{actual_port}")
114
- print("Press Ctrl+C to stop.")
163
+
164
+ write_pid(pid_name, os.getpid())
165
+ write_port(pid_name, actual_port)
166
+
167
+ def _cleanup():
168
+ remove_pid(pid_name)
169
+ remove_port(pid_name)
170
+
171
+ atexit.register(_cleanup)
172
+ if threading.current_thread() is threading.main_thread():
173
+ def _handle_term(_sig, _frame):
174
+ threading.Thread(target=httpd.shutdown, daemon=True).start()
175
+ signal.signal(signal.SIGTERM, _handle_term)
176
+
177
+ print(f"Overview dashboard: http://localhost:{actual_port}", flush=True)
178
+ print("Press Ctrl+C to stop.", flush=True)
115
179
  try:
116
180
  httpd.serve_forever()
117
181
  except KeyboardInterrupt:
118
- print("\nStopped.")
182
+ print("\nStopped.", flush=True)
119
183
  finally:
120
184
  httpd.server_close()
185
+ _cleanup()
186
+ atexit.unregister(_cleanup)
@@ -0,0 +1,84 @@
1
+ """PID file management for agentirc daemon instances."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import re
7
+ from pathlib import Path
8
+
9
+ PID_DIR = os.path.expanduser("~/.agentirc/pids")
10
+
11
+
12
+ def _safe_name(name: str) -> str:
13
+ """Sanitize a daemon name to prevent path traversal."""
14
+ return re.sub(r"[^a-zA-Z0-9._-]", "_", Path(name).name)
15
+
16
+
17
+ def write_pid(name: str, pid: int) -> Path:
18
+ """Write a PID file for the named daemon. Creates the directory if needed."""
19
+ pid_dir = Path(PID_DIR)
20
+ pid_dir.mkdir(parents=True, exist_ok=True)
21
+ pid_path = pid_dir / f"{_safe_name(name)}.pid"
22
+ pid_path.write_text(str(pid))
23
+ return pid_path
24
+
25
+
26
+ def read_pid(name: str) -> int | None:
27
+ """Read the PID for the named daemon. Returns None if file is missing."""
28
+ pid_path = Path(PID_DIR) / f"{_safe_name(name)}.pid"
29
+ if not pid_path.exists():
30
+ return None
31
+ try:
32
+ return int(pid_path.read_text().strip())
33
+ except (ValueError, OSError):
34
+ return None
35
+
36
+
37
+ def remove_pid(name: str) -> None:
38
+ """Remove the PID file for the named daemon if it exists."""
39
+ pid_path = Path(PID_DIR) / f"{_safe_name(name)}.pid"
40
+ try:
41
+ pid_path.unlink()
42
+ except FileNotFoundError:
43
+ pass
44
+
45
+
46
+ def write_port(name: str, port: int) -> Path:
47
+ """Write a port file for the named daemon. Creates the directory if needed."""
48
+ pid_dir = Path(PID_DIR)
49
+ pid_dir.mkdir(parents=True, exist_ok=True)
50
+ port_path = pid_dir / f"{_safe_name(name)}.port"
51
+ port_path.write_text(str(port))
52
+ return port_path
53
+
54
+
55
+ def read_port(name: str) -> int | None:
56
+ """Read the port for the named daemon. Returns None if file is missing."""
57
+ port_path = Path(PID_DIR) / f"{_safe_name(name)}.port"
58
+ if not port_path.exists():
59
+ return None
60
+ try:
61
+ return int(port_path.read_text().strip())
62
+ except (ValueError, OSError):
63
+ return None
64
+
65
+
66
+ def remove_port(name: str) -> None:
67
+ """Remove the port file for the named daemon if it exists."""
68
+ port_path = Path(PID_DIR) / f"{_safe_name(name)}.port"
69
+ try:
70
+ port_path.unlink()
71
+ except FileNotFoundError:
72
+ pass
73
+
74
+
75
+ def is_process_alive(pid: int) -> bool:
76
+ """Check whether a process with the given PID is alive."""
77
+ try:
78
+ os.kill(pid, 0)
79
+ return True
80
+ except ProcessLookupError:
81
+ return False
82
+ except PermissionError:
83
+ # Process exists but we don't have permission to signal it
84
+ return True
@@ -48,6 +48,20 @@ memberships with roles, and cross-channel recent activity.
48
48
  styled HTML with the anthropic cream theme. Auto-refreshes at the interval
49
49
  set by `--refresh` (default: 5 seconds).
50
50
 
51
+ ### Instance Management
52
+
53
+ Each overview server registers itself with a PID and port file in
54
+ `~/.agentirc/pids/` (e.g., `overview-spark.pid`, `overview-spark.port`).
55
+
56
+ - **One per server**: Starting a new overview for the same IRC server
57
+ auto-kills the previous instance via SIGTERM (with SIGKILL fallback).
58
+ - **Multiple servers**: Different IRC servers can each have their own
59
+ overview site running simultaneously (keyed by `server_name`).
60
+ - **Graceful shutdown**: SIGTERM and Ctrl+C both trigger clean shutdown
61
+ with PID/port file removal.
62
+ - **Background visibility**: The dashboard URL is flushed to stdout
63
+ immediately, so it appears even when the process runs in the background.
64
+
51
65
  ## Data Sources
52
66
 
53
67
  - **IRC Observer**: Ephemeral connection queries LIST, NAMES, WHO, HISTORY
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentirc-cli"
3
- version = "0.15.0"
3
+ version = "0.15.1"
4
4
  description = "IRC protocol chatrooms for AI agents (and humans allowed)"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -0,0 +1,165 @@
1
+ """Tests for overview web renderer."""
2
+ import os
3
+ import threading
4
+ import time
5
+
6
+ from agentirc.overview.model import Agent, Message, MeshState, Room
7
+ from agentirc.overview.renderer_web import render_html, serve_web, _stop_existing_overview
8
+ from agentirc import pidfile
9
+
10
+
11
+ def _make_fixture() -> MeshState:
12
+ now = time.time()
13
+ agent = Agent(
14
+ nick="spark-claude", status="active", activity="working on: tests",
15
+ channels=["#general"], server="spark",
16
+ backend="claude", model="claude-opus-4-6",
17
+ )
18
+ msg = Message(nick="spark-claude", text="hello", timestamp=now - 60, channel="#general")
19
+ room = Room(
20
+ name="#general", topic="Testing",
21
+ members=[agent], operators=["spark-claude"],
22
+ federation_servers=[], messages=[msg],
23
+ )
24
+ return MeshState(server_name="spark", rooms=[room], agents=[agent], federation_links=[])
25
+
26
+
27
+ def test_render_html_produces_valid_html():
28
+ mesh = _make_fixture()
29
+ html = render_html(mesh, message_limit=4)
30
+ assert "<!DOCTYPE html>" in html
31
+ assert "<html" in html
32
+ assert "</html>" in html
33
+
34
+
35
+ def test_render_html_contains_content():
36
+ mesh = _make_fixture()
37
+ html = render_html(mesh, message_limit=4)
38
+ assert "spark mesh" in html
39
+ assert "#general" in html
40
+ assert "spark-claude" in html
41
+ assert "hello" in html
42
+
43
+
44
+ def test_render_html_has_cream_styles():
45
+ mesh = _make_fixture()
46
+ html = render_html(mesh, message_limit=4)
47
+ assert "#faf7f2" in html or "faf7f2" in html
48
+
49
+
50
+ def test_render_html_has_auto_refresh():
51
+ mesh = _make_fixture()
52
+ html = render_html(mesh, message_limit=4, refresh_interval=5)
53
+ assert 'http-equiv="refresh"' in html or "refresh" in html.lower()
54
+
55
+
56
+ def test_render_html_has_table_elements():
57
+ mesh = _make_fixture()
58
+ html = render_html(mesh, message_limit=4)
59
+ assert "<table" in html
60
+ assert "<th>" in html or "<th " in html
61
+ assert "status-active" in html
62
+
63
+
64
+ # --- serve_web PID/port management tests ---
65
+
66
+
67
+ def test_serve_web_writes_and_cleans_pid_port(tmp_path, monkeypatch):
68
+ """serve_web writes PID and port files, cleans up on shutdown."""
69
+ monkeypatch.setattr(pidfile, "PID_DIR", str(tmp_path))
70
+
71
+ pid_name = "overview-testserver"
72
+ pid_path = tmp_path / f"{pid_name}.pid"
73
+ port_path = tmp_path / f"{pid_name}.port"
74
+
75
+ # We need to stop the server after it starts. Patch HTTPServer to
76
+ # capture the instance so we can call shutdown() from a timer.
77
+ from http.server import HTTPServer
78
+ captured = {}
79
+ _orig_init = HTTPServer.__init__
80
+
81
+ def _patched_init(self, *args, **kwargs):
82
+ _orig_init(self, *args, **kwargs)
83
+ captured["httpd"] = self
84
+
85
+ monkeypatch.setattr(HTTPServer, "__init__", _patched_init)
86
+
87
+ def _run():
88
+ serve_web(
89
+ host="127.0.0.1", port=6667, server_name="testserver",
90
+ serve_port=0,
91
+ )
92
+
93
+ t = threading.Thread(target=_run, daemon=True)
94
+ t.start()
95
+
96
+ # Wait for PID file to appear
97
+ for _ in range(40):
98
+ if pid_path.exists() and port_path.exists():
99
+ break
100
+ time.sleep(0.05)
101
+
102
+ assert pid_path.exists(), "PID file should be created"
103
+ assert port_path.exists(), "Port file should be created"
104
+
105
+ stored_pid = int(pid_path.read_text().strip())
106
+ stored_port = int(port_path.read_text().strip())
107
+ assert stored_pid == os.getpid()
108
+ assert stored_port > 0
109
+
110
+ # Shut down the server gracefully
111
+ captured["httpd"].shutdown()
112
+ t.join(timeout=5)
113
+
114
+ assert not pid_path.exists(), "PID file should be cleaned up"
115
+ assert not port_path.exists(), "Port file should be cleaned up"
116
+
117
+
118
+ def test_stop_existing_overview_kills_previous(tmp_path, monkeypatch):
119
+ """_stop_existing_overview sends SIGTERM to a live previous instance."""
120
+ monkeypatch.setattr(pidfile, "PID_DIR", str(tmp_path))
121
+
122
+ pid_name = "overview-testserver"
123
+ (tmp_path / f"{pid_name}.pid").write_text("99999")
124
+ (tmp_path / f"{pid_name}.port").write_text("12345")
125
+
126
+ # Simulate: process alive on first check, dead after SIGTERM
127
+ alive_calls = {"count": 0}
128
+
129
+ def _fake_alive(pid):
130
+ alive_calls["count"] += 1
131
+ return alive_calls["count"] == 1 # alive first, dead after
132
+
133
+ killed_pids = []
134
+ monkeypatch.setattr(os, "kill", lambda pid, sig: killed_pids.append((pid, sig)))
135
+
136
+ import agentirc.overview.renderer_web as rweb
137
+ monkeypatch.setattr(rweb, "is_process_alive", _fake_alive)
138
+
139
+ _stop_existing_overview(pid_name)
140
+
141
+ import signal
142
+ assert (99999, signal.SIGTERM) in killed_pids, "SIGTERM should be sent"
143
+ assert not (tmp_path / f"{pid_name}.pid").exists()
144
+ assert not (tmp_path / f"{pid_name}.port").exists()
145
+
146
+
147
+ def test_stop_existing_overview_cleans_stale_pid(tmp_path, monkeypatch):
148
+ """Stale PID files (dead process) are cleaned without sending signals."""
149
+ monkeypatch.setattr(pidfile, "PID_DIR", str(tmp_path))
150
+
151
+ pid_name = "overview-stale"
152
+ (tmp_path / f"{pid_name}.pid").write_text("11111")
153
+ (tmp_path / f"{pid_name}.port").write_text("9999")
154
+
155
+ kills = []
156
+ monkeypatch.setattr(os, "kill", lambda pid, sig: kills.append((pid, sig)))
157
+
158
+ import agentirc.overview.renderer_web as rweb
159
+ monkeypatch.setattr(rweb, "is_process_alive", lambda pid: False)
160
+
161
+ _stop_existing_overview(pid_name)
162
+
163
+ assert not (tmp_path / f"{pid_name}.pid").exists()
164
+ assert not (tmp_path / f"{pid_name}.port").exists()
165
+ assert kills == [], "No signals should be sent for a dead process"
@@ -4,7 +4,7 @@ requires-python = ">=3.12"
4
4
 
5
5
  [[package]]
6
6
  name = "agentirc-cli"
7
- version = "0.15.0"
7
+ version = "0.15.1"
8
8
  source = { editable = "." }
9
9
  dependencies = [
10
10
  { name = "anthropic" },
@@ -1,49 +0,0 @@
1
- """PID file management for agentirc daemon instances."""
2
-
3
- from __future__ import annotations
4
-
5
- import os
6
- from pathlib import Path
7
-
8
- PID_DIR = os.path.expanduser("~/.agentirc/pids")
9
-
10
-
11
- def write_pid(name: str, pid: int) -> Path:
12
- """Write a PID file for the named daemon. Creates the directory if needed."""
13
- pid_dir = Path(PID_DIR)
14
- pid_dir.mkdir(parents=True, exist_ok=True)
15
- pid_path = pid_dir / f"{name}.pid"
16
- pid_path.write_text(str(pid))
17
- return pid_path
18
-
19
-
20
- def read_pid(name: str) -> int | None:
21
- """Read the PID for the named daemon. Returns None if file is missing."""
22
- pid_path = Path(PID_DIR) / f"{name}.pid"
23
- if not pid_path.exists():
24
- return None
25
- try:
26
- return int(pid_path.read_text().strip())
27
- except (ValueError, OSError):
28
- return None
29
-
30
-
31
- def remove_pid(name: str) -> None:
32
- """Remove the PID file for the named daemon if it exists."""
33
- pid_path = Path(PID_DIR) / f"{name}.pid"
34
- try:
35
- pid_path.unlink()
36
- except FileNotFoundError:
37
- pass
38
-
39
-
40
- def is_process_alive(pid: int) -> bool:
41
- """Check whether a process with the given PID is alive."""
42
- try:
43
- os.kill(pid, 0)
44
- return True
45
- except ProcessLookupError:
46
- return False
47
- except PermissionError:
48
- # Process exists but we don't have permission to signal it
49
- return True
@@ -1,58 +0,0 @@
1
- """Tests for overview web renderer."""
2
- import time
3
-
4
- from agentirc.overview.model import Agent, Message, MeshState, Room
5
- from agentirc.overview.renderer_web import render_html
6
-
7
-
8
- def _make_fixture() -> MeshState:
9
- now = time.time()
10
- agent = Agent(
11
- nick="spark-claude", status="active", activity="working on: tests",
12
- channels=["#general"], server="spark",
13
- backend="claude", model="claude-opus-4-6",
14
- )
15
- msg = Message(nick="spark-claude", text="hello", timestamp=now - 60, channel="#general")
16
- room = Room(
17
- name="#general", topic="Testing",
18
- members=[agent], operators=["spark-claude"],
19
- federation_servers=[], messages=[msg],
20
- )
21
- return MeshState(server_name="spark", rooms=[room], agents=[agent], federation_links=[])
22
-
23
-
24
- def test_render_html_produces_valid_html():
25
- mesh = _make_fixture()
26
- html = render_html(mesh, message_limit=4)
27
- assert "<!DOCTYPE html>" in html
28
- assert "<html" in html
29
- assert "</html>" in html
30
-
31
-
32
- def test_render_html_contains_content():
33
- mesh = _make_fixture()
34
- html = render_html(mesh, message_limit=4)
35
- assert "spark mesh" in html
36
- assert "#general" in html
37
- assert "spark-claude" in html
38
- assert "hello" in html
39
-
40
-
41
- def test_render_html_has_cream_styles():
42
- mesh = _make_fixture()
43
- html = render_html(mesh, message_limit=4)
44
- assert "#faf7f2" in html or "faf7f2" in html
45
-
46
-
47
- def test_render_html_has_auto_refresh():
48
- mesh = _make_fixture()
49
- html = render_html(mesh, message_limit=4, refresh_interval=5)
50
- assert 'http-equiv="refresh"' in html or "refresh" in html.lower()
51
-
52
-
53
- def test_render_html_has_table_elements():
54
- mesh = _make_fixture()
55
- html = render_html(mesh, message_limit=4)
56
- assert "<table" in html
57
- assert "<th>" in html or "<th " in html
58
- assert "status-active" in html
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes