agentirc-cli 6.2.0__tar.gz → 6.2.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 (355) hide show
  1. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/CHANGELOG.md +16 -0
  2. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/PKG-INFO +1 -1
  3. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/cli/channel.py +45 -2
  4. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/console/app.py +24 -10
  5. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/console/widgets/chat.py +24 -1
  6. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/observer.py +16 -3
  7. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/cli/index.md +17 -0
  8. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/pyproject.toml +1 -1
  9. agentirc_cli-6.2.1/tests/test_console_fixes_224_227.py +199 -0
  10. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/uv.lock +1 -1
  11. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/.claude/skills/pr-review/SKILL.md +0 -0
  12. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/.claude/skills/run-tests/SKILL.md +0 -0
  13. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/.claude/skills/run-tests/scripts/test.sh +0 -0
  14. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/.flake8 +0 -0
  15. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/.github/workflows/docs-check.yml +0 -0
  16. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/.github/workflows/publish.yml +0 -0
  17. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/.github/workflows/security-checks.yml +0 -0
  18. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/.github/workflows/tests.yml +0 -0
  19. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/.gitignore +0 -0
  20. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/.markdownlint-cli2.yaml +0 -0
  21. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/.pr_agent.toml +0 -0
  22. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/.pre-commit-config.yaml +0 -0
  23. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/.pylintrc +0 -0
  24. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/CLAUDE.md +0 -0
  25. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/Gemfile +0 -0
  26. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/Gemfile.lock +0 -0
  27. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/LICENSE +0 -0
  28. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/README.md +0 -0
  29. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/SECURITY.md +0 -0
  30. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/_config.agentirc.yml +0 -0
  31. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/_config.base.yml +0 -0
  32. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/_config.culture.yml +0 -0
  33. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/_data/sites.yml +0 -0
  34. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/_includes/head_custom.html +0 -0
  35. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/_plugins/site_filter.rb +0 -0
  36. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/_sass/color_schemes/anthropic.scss +0 -0
  37. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/_sass/color_schemes/dark-terminal.scss +0 -0
  38. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/_sass/custom/custom.scss +0 -0
  39. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/assets/images/IMG_3183.png +0 -0
  40. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/assets/images/apple-touch-icon.png +0 -0
  41. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/assets/images/favicon-16x16.png +0 -0
  42. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/assets/images/favicon-32x32.png +0 -0
  43. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/assets/images/favicon.ico +0 -0
  44. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/assets/images/og-agentirc.png +0 -0
  45. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/assets/images/og-culture.png +0 -0
  46. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/__init__.py +0 -0
  47. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/__main__.py +0 -0
  48. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/CLAUDE.md +0 -0
  49. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/__init__.py +0 -0
  50. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/__main__.py +0 -0
  51. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/channel.py +0 -0
  52. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/client.py +0 -0
  53. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/config.py +0 -0
  54. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/docs/agentirc-architecture.md +0 -0
  55. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/docs/agentirc-features.md +0 -0
  56. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/docs/agentirc-skill.md +0 -0
  57. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/docs/agentirc.md +0 -0
  58. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/history_store.py +0 -0
  59. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/ircd.py +0 -0
  60. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/remote_client.py +0 -0
  61. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/room_store.py +0 -0
  62. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/rooms_util.py +0 -0
  63. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/server_link.py +0 -0
  64. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/skill.py +0 -0
  65. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/skills/__init__.py +0 -0
  66. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/skills/history.py +0 -0
  67. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/skills/icon.py +0 -0
  68. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/skills/rooms.py +0 -0
  69. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/skills/threads.py +0 -0
  70. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/agentirc/thread_store.py +0 -0
  71. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/aio.py +0 -0
  72. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/bots/__init__.py +0 -0
  73. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/bots/bot.py +0 -0
  74. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/bots/bot_manager.py +0 -0
  75. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/bots/config.py +0 -0
  76. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/bots/http_listener.py +0 -0
  77. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/bots/template_engine.py +0 -0
  78. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/bots/virtual_client.py +0 -0
  79. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/cli/__init__.py +0 -0
  80. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/cli/agent.py +0 -0
  81. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/cli/bot.py +0 -0
  82. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/cli/mesh.py +0 -0
  83. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/cli/server.py +0 -0
  84. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/cli/shared/__init__.py +0 -0
  85. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/cli/shared/constants.py +0 -0
  86. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/cli/shared/display.py +0 -0
  87. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/cli/shared/formatting.py +0 -0
  88. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/cli/shared/ipc.py +0 -0
  89. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/cli/shared/mesh.py +0 -0
  90. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/cli/shared/process.py +0 -0
  91. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/cli/skills.py +0 -0
  92. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/__init__.py +0 -0
  93. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/acp/__init__.py +0 -0
  94. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/acp/agent_runner.py +0 -0
  95. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/acp/config.py +0 -0
  96. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/acp/culture.yaml +0 -0
  97. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/acp/daemon.py +0 -0
  98. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/acp/ipc.py +0 -0
  99. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/acp/irc_transport.py +0 -0
  100. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/acp/message_buffer.py +0 -0
  101. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/acp/skill/SKILL.md +0 -0
  102. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/acp/skill/__init__.py +0 -0
  103. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/acp/skill/irc_client.py +0 -0
  104. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/acp/socket_server.py +0 -0
  105. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/acp/supervisor.py +0 -0
  106. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/acp/webhook.py +0 -0
  107. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/__init__.py +0 -0
  108. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/__main__.py +0 -0
  109. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/agent_runner.py +0 -0
  110. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/config.py +0 -0
  111. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/culture.yaml +0 -0
  112. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/daemon.py +0 -0
  113. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/ipc.py +0 -0
  114. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/irc_transport.py +0 -0
  115. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/message_buffer.py +0 -0
  116. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/skill/SKILL.md +0 -0
  117. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/skill/__init__.py +0 -0
  118. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/skill/irc_client.py +0 -0
  119. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/socket_server.py +0 -0
  120. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/supervisor.py +0 -0
  121. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/claude/webhook.py +0 -0
  122. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/codex/__init__.py +0 -0
  123. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/codex/agent_runner.py +0 -0
  124. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/codex/config.py +0 -0
  125. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/codex/culture.yaml +0 -0
  126. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/codex/daemon.py +0 -0
  127. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/codex/ipc.py +0 -0
  128. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/codex/irc_transport.py +0 -0
  129. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/codex/message_buffer.py +0 -0
  130. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/codex/skill/SKILL.md +0 -0
  131. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/codex/skill/__init__.py +0 -0
  132. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/codex/skill/irc_client.py +0 -0
  133. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/codex/socket_server.py +0 -0
  134. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/codex/supervisor.py +0 -0
  135. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/codex/webhook.py +0 -0
  136. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/copilot/__init__.py +0 -0
  137. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/copilot/agent_runner.py +0 -0
  138. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/copilot/config.py +0 -0
  139. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/copilot/culture.yaml +0 -0
  140. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/copilot/daemon.py +0 -0
  141. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/copilot/ipc.py +0 -0
  142. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/copilot/irc_transport.py +0 -0
  143. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/copilot/message_buffer.py +0 -0
  144. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/copilot/skill/SKILL.md +0 -0
  145. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/copilot/skill/__init__.py +0 -0
  146. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/copilot/skill/irc_client.py +0 -0
  147. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/copilot/socket_server.py +0 -0
  148. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/copilot/supervisor.py +0 -0
  149. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/clients/copilot/webhook.py +0 -0
  150. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/config.py +0 -0
  151. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/console/__init__.py +0 -0
  152. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/console/client.py +0 -0
  153. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/console/commands.py +0 -0
  154. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/console/status.py +0 -0
  155. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/console/widgets/__init__.py +0 -0
  156. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/console/widgets/info_panel.py +0 -0
  157. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/console/widgets/sidebar.py +0 -0
  158. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/credentials.py +0 -0
  159. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/formatting.py +0 -0
  160. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/learn_prompt.py +0 -0
  161. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/mesh_config.py +0 -0
  162. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/overview/__init__.py +0 -0
  163. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/overview/collector.py +0 -0
  164. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/overview/model.py +0 -0
  165. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/overview/renderer_text.py +0 -0
  166. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/overview/renderer_web.py +0 -0
  167. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/overview/web/style.css +0 -0
  168. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/persistence.py +0 -0
  169. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/pidfile.py +0 -0
  170. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/protocol/__init__.py +0 -0
  171. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/protocol/commands.py +0 -0
  172. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/protocol/extensions/federation.md +0 -0
  173. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/protocol/extensions/history.md +0 -0
  174. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/protocol/extensions/icons.md +0 -0
  175. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/protocol/extensions/rooms.md +0 -0
  176. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/protocol/extensions/tags.md +0 -0
  177. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/protocol/extensions/threads.md +0 -0
  178. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/protocol/message.py +0 -0
  179. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/protocol/protocol-index.md +0 -0
  180. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/protocol/replies.py +0 -0
  181. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/culture/skills/culture/SKILL.md +0 -0
  182. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/README.md +0 -0
  183. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/agentirc/architecture-overview.md +0 -0
  184. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/agentirc/index.md +0 -0
  185. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/agentirc/why-agentirc.md +0 -0
  186. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/culture/agent-lifecycle.md +0 -0
  187. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/culture/choose-a-harness.md +0 -0
  188. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/culture/index.md +0 -0
  189. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/culture/mental-model.md +0 -0
  190. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/culture/operate.md +0 -0
  191. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/culture/patterns.md +0 -0
  192. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/culture/quickstart.md +0 -0
  193. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/culture/reflective-development.md +0 -0
  194. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/culture/vision-patterns-index.md +0 -0
  195. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/culture/vision.md +0 -0
  196. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/culture/why-culture.md +0 -0
  197. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/architecture/agent-harness-spec.md +0 -0
  198. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/architecture/index.md +0 -0
  199. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/architecture/layers.md +0 -0
  200. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/architecture/threads.md +0 -0
  201. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/cli/commands.md +0 -0
  202. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/harnesses/acp.md +0 -0
  203. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/harnesses/claude.md +0 -0
  204. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/harnesses/codex.md +0 -0
  205. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/harnesses/copilot.md +0 -0
  206. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/harnesses/index.md +0 -0
  207. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/index.md +0 -0
  208. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/server/architecture.md +0 -0
  209. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/server/config.md +0 -0
  210. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/server/deployment.md +0 -0
  211. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/server/index.md +0 -0
  212. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/reference/server/security.md +0 -0
  213. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/resources/github-copilot-sdk-instructions.md +0 -0
  214. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/concepts/federation.md +0 -0
  215. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/concepts/harnesses.md +0 -0
  216. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/concepts/humans-and-agents.md +0 -0
  217. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/concepts/index.md +0 -0
  218. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/concepts/persistence.md +0 -0
  219. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/concepts/rooms.md +0 -0
  220. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/demos/magic-demo.md +0 -0
  221. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/guides/first-session.md +0 -0
  222. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/guides/index.md +0 -0
  223. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/guides/join-as-human.md +0 -0
  224. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/guides/local-setup.md +0 -0
  225. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/guides/multi-machine.md +0 -0
  226. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/use-cases/01-pair-programming.md +0 -0
  227. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/use-cases/02-code-review-ensemble.md +0 -0
  228. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/use-cases/03-cross-server-delegation.md +0 -0
  229. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/use-cases/04-knowledge-propagation.md +0 -0
  230. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/use-cases/05-the-observer.md +0 -0
  231. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/use-cases/06-cross-server-ops.md +0 -0
  232. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/use-cases/07-supervisor-intervention.md +0 -0
  233. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/use-cases/08-apps-as-agents.md +0 -0
  234. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/use-cases/09-research-swarm.md +0 -0
  235. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/use-cases/10-agent-lifecycle.md +0 -0
  236. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/shared/use-cases-index.md +0 -0
  237. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
  238. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
  239. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
  240. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
  241. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
  242. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
  243. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
  244. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
  245. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
  246. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/plans/2026-04-09-decentralized-agent-config.md +0 -0
  247. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/plans/2026-04-12-console-enhancements.md +0 -0
  248. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
  249. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
  250. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
  251. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
  252. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
  253. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
  254. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
  255. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
  256. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
  257. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
  258. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
  259. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
  260. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
  261. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-04-07-reflective-development-reframe-design.md +0 -0
  262. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-04-08-reflective-development-deepening-design.md +0 -0
  263. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-04-09-decentralized-agent-config-design.md +0 -0
  264. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/docs/superpowers/specs/2026-04-12-console-enhancements-design.md +0 -0
  265. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/favicon.ico +0 -0
  266. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/packages/agent-harness/README.md +0 -0
  267. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/packages/agent-harness/config.py +0 -0
  268. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/packages/agent-harness/culture.yaml +0 -0
  269. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/packages/agent-harness/daemon.py +0 -0
  270. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/packages/agent-harness/ipc.py +0 -0
  271. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/packages/agent-harness/irc_transport.py +0 -0
  272. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/packages/agent-harness/message_buffer.py +0 -0
  273. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/packages/agent-harness/skill/SKILL.md +0 -0
  274. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/packages/agent-harness/skill/irc_client.py +0 -0
  275. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/packages/agent-harness/socket_server.py +0 -0
  276. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/packages/agent-harness/webhook.py +0 -0
  277. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  278. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/plugins/claude-code/skills/culture/SKILL.md +0 -0
  279. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/plugins/claude-code/skills/irc/SKILL.md +0 -0
  280. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
  281. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/robots.txt +0 -0
  282. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/sonar-project.properties +0 -0
  283. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/__init__.py +0 -0
  284. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/conftest.py +0 -0
  285. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_acp_daemon.py +0 -0
  286. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_agent_runner.py +0 -0
  287. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_archive.py +0 -0
  288. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_bot.py +0 -0
  289. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_bot_config.py +0 -0
  290. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_bot_manager.py +0 -0
  291. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_bots_integration.py +0 -0
  292. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_channel.py +0 -0
  293. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_channel_cli.py +0 -0
  294. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_codex_daemon.py +0 -0
  295. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_connection.py +0 -0
  296. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_console_client.py +0 -0
  297. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_console_commands.py +0 -0
  298. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_console_connection.py +0 -0
  299. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_console_icons.py +0 -0
  300. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_console_integration.py +0 -0
  301. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_console_status.py +0 -0
  302. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_copilot_daemon.py +0 -0
  303. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_credentials.py +0 -0
  304. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_culture_config.py +0 -0
  305. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_daemon.py +0 -0
  306. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_daemon_config.py +0 -0
  307. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_daemon_ipc.py +0 -0
  308. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_discovery.py +0 -0
  309. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_display.py +0 -0
  310. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_federation.py +0 -0
  311. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_history.py +0 -0
  312. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_http_listener.py +0 -0
  313. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_integration_layer5.py +0 -0
  314. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_ipc.py +0 -0
  315. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_irc_transport.py +0 -0
  316. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_learn_prompt.py +0 -0
  317. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_link_reconnect.py +0 -0
  318. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_manifest_config.py +0 -0
  319. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_mention_alias.py +0 -0
  320. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_mention_target_cleanup.py +0 -0
  321. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_mention_warning.py +0 -0
  322. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_mentions.py +0 -0
  323. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_mesh_config.py +0 -0
  324. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_mesh_readiness.py +0 -0
  325. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_message.py +0 -0
  326. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_message_buffer.py +0 -0
  327. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_messaging.py +0 -0
  328. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_migrate_cli.py +0 -0
  329. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_modes.py +0 -0
  330. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_overview_cli.py +0 -0
  331. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_overview_collector.py +0 -0
  332. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_overview_model.py +0 -0
  333. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_overview_renderer.py +0 -0
  334. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_overview_web.py +0 -0
  335. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_persistence.py +0 -0
  336. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_pidfile.py +0 -0
  337. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_poll_loop.py +0 -0
  338. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_register_cli.py +0 -0
  339. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_room_persistence.py +0 -0
  340. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_rooms.py +0 -0
  341. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_rooms_federation.py +0 -0
  342. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_rooms_integration.py +0 -0
  343. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_server_icon_skill.py +0 -0
  344. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_setup_update_cli.py +0 -0
  345. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_skill_client.py +0 -0
  346. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_skill_docs.py +0 -0
  347. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_skills.py +0 -0
  348. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_socket_server.py +0 -0
  349. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_supervisor.py +0 -0
  350. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_template_engine.py +0 -0
  351. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_thread_buffer.py +0 -0
  352. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_threads.py +0 -0
  353. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_virtual_client.py +0 -0
  354. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_wait_for_port.py +0 -0
  355. {agentirc_cli-6.2.0 → agentirc_cli-6.2.1}/tests/test_webhook.py +0 -0
@@ -4,6 +4,22 @@ 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
+ ## [6.2.1] - 2026-04-13
8
+
9
+
10
+ ### Added
11
+
12
+ - Copy-paste guidance in help screen (Shift+drag bypasses TUI mouse capture in modern terminals)
13
+
14
+
15
+ ### Fixed
16
+
17
+ - #227: Tab now cycles channels (added priority=True to override Textual Screen focus-cycling)
18
+ - #226: Alt+Left/Right jump by word in chat input; Alt+Backspace deletes previous word
19
+ - #225: `culture channel message` interprets literal \n, \t, and \\ (escape-an-escape); observer splits multi-line text into one PRIVMSG per line and rejects all-empty-after-interpretation input with a non-zero exit
20
+ - #224: Exiting overview now reloads the current channel history (was empty)
21
+ - Help screen now opens on F1 (Ctrl+H stays as secondary — most terminals forward it as Backspace)
22
+
7
23
  ## [6.2.0] - 2026-04-12
8
24
 
9
25
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 6.2.0
3
+ Version: 6.2.1
4
4
  Summary: Legacy alias for culture — install culture instead
5
5
  Project-URL: Homepage, https://github.com/OriNachum/culture
6
6
  Author: Ori Nachum
@@ -208,6 +208,40 @@ def _cmd_read(args: argparse.Namespace) -> None:
208
208
  print(msg)
209
209
 
210
210
 
211
+ def _interpret_escapes(text: str) -> str:
212
+ """Convert shell-literal ``\\n`` / ``\\t`` / ``\\\\`` sequences to real chars.
213
+
214
+ Walks the string left-to-right so a preceding backslash escapes the next
215
+ character — ``\\\\n`` stays as the two chars ``\\`` + ``n``, while ``\\n``
216
+ becomes a real newline. Supported escapes: ``\\n`` → newline, ``\\t`` →
217
+ tab, ``\\\\`` → single backslash. Any other ``\\x`` pair is passed through
218
+ unchanged so we don't surprise users with ``\\x..`` / ``\\u....`` style
219
+ interpretation that ``codecs.decode(..., "unicode_escape")`` would do.
220
+ """
221
+ out: list[str] = []
222
+ i = 0
223
+ n = len(text)
224
+ while i < n:
225
+ ch = text[i]
226
+ if ch == "\\" and i + 1 < n:
227
+ nxt = text[i + 1]
228
+ if nxt == "n":
229
+ out.append("\n")
230
+ i += 2
231
+ continue
232
+ if nxt == "t":
233
+ out.append("\t")
234
+ i += 2
235
+ continue
236
+ if nxt == "\\":
237
+ out.append("\\")
238
+ i += 2
239
+ continue
240
+ out.append(ch)
241
+ i += 1
242
+ return "".join(out)
243
+
244
+
211
245
  def _cmd_message(args: argparse.Namespace) -> None:
212
246
  if not args.target.strip():
213
247
  print("Error: channel name cannot be empty", file=sys.stderr)
@@ -216,14 +250,23 @@ def _cmd_message(args: argparse.Namespace) -> None:
216
250
  print("Error: message text cannot be empty", file=sys.stderr)
217
251
  sys.exit(1)
218
252
  target = args.target if args.target.startswith("#") else f"#{args.target}"
253
+ text = _interpret_escapes(args.text)
254
+
255
+ # After escape interpretation, reject input that has no non-empty line —
256
+ # otherwise we'd print "Sent to ..." while nothing actually goes out.
257
+ if not any(line.strip() for line in text.split("\n")):
258
+ print(
259
+ "Error: message text has no non-empty line after escape interpretation", file=sys.stderr
260
+ )
261
+ sys.exit(1)
219
262
 
220
- resp = _try_ipc("irc_send", channel=target, message=args.text)
263
+ resp = _try_ipc("irc_send", channel=target, message=text)
221
264
  if resp and resp.get("ok"):
222
265
  print(f"Sent to {target}")
223
266
  return
224
267
 
225
268
  observer = get_observer(args.config)
226
- asyncio.run(observer.send_message(target, args.text))
269
+ asyncio.run(observer.send_message(target, text))
227
270
  print(f"Sent to {target}")
228
271
 
229
272
 
@@ -35,11 +35,15 @@ class ConsoleApp(App):
35
35
  BINDINGS = [
36
36
  Binding("ctrl+o", "show_overview", "Overview", show=True),
37
37
  Binding("ctrl+s", "show_status", "Status", show=True),
38
- Binding("ctrl+h", "show_help", "Help", show=True),
38
+ Binding("f1", "show_help", "Help", show=True),
39
+ # Most terminals send 0x08 (backspace) for Ctrl+H, so this secondary
40
+ # bind only fires under terminals with modifyOtherKeys enabled.
41
+ Binding("ctrl+h", "show_help", "Help", show=False),
39
42
  Binding("escape", "back_to_chat", "Chat", show=True),
40
43
  Binding("ctrl+q", "quit_app", "Quit", show=True),
41
- Binding("tab", "next_channel", "Next channel", show=False),
42
- Binding("shift+tab", "prev_channel", "Prev channel", show=False),
44
+ # priority=True so Tab wins against Screen's default focus-cycling.
45
+ Binding("tab", "next_channel", "Next channel", show=False, priority=True),
46
+ Binding("shift+tab", "prev_channel", "Prev channel", show=False, priority=True),
43
47
  ]
44
48
 
45
49
  DEFAULT_CSS = """
@@ -413,11 +417,19 @@ class ConsoleApp(App):
413
417
  "[bold $warning]KEYBINDINGS[/]",
414
418
  "",
415
419
  " [bold]Tab / Shift+Tab[/] Cycle channels",
420
+ " [bold]Alt+←/→[/] Jump by word in input",
421
+ " [bold]Alt+Backspace[/] Delete previous word",
416
422
  " [bold]Ctrl+O[/] Overview",
417
423
  " [bold]Ctrl+S[/] Status",
418
- " [bold]Ctrl+H[/] Help",
424
+ " [bold]F1[/] Help (Ctrl+H on terminals that forward it)",
419
425
  " [bold]Escape[/] Back to chat",
420
426
  " [bold]Ctrl+Q[/] Quit",
427
+ "",
428
+ "[bold $warning]COPY-PASTE[/]",
429
+ "",
430
+ " Hold [bold]Shift[/] while dragging with the mouse to select text for copy-paste.",
431
+ " Most modern terminals (iTerm2, Kitty, Alacritty, WezTerm, GNOME Terminal,",
432
+ " Windows Terminal) let Shift bypass the TUI's mouse capture.",
421
433
  ]
422
434
  chat.set_content("Help", lines)
423
435
 
@@ -567,15 +579,17 @@ class ConsoleApp(App):
567
579
  ]
568
580
  sidebar.entities = entity_items
569
581
 
570
- def action_back_to_chat(self) -> None:
571
- """Return to the normal chat view."""
582
+ async def action_back_to_chat(self) -> None:
583
+ """Return to the normal chat view, reloading current channel history."""
572
584
  if self._current_view == "chat":
573
585
  return
586
+ if self._current_channel:
587
+ # Delegate: resets view, shows input, and reloads recent history —
588
+ # equivalent to running /read on the current channel.
589
+ await self._switch_to_channel(self._current_channel)
590
+ return
591
+ # No channel yet — just restore chat view and show the input.
574
592
  self._current_view = "chat"
575
- chat: ChatPanel = self.query_one(ChatPanel)
576
- chat.set_channel(self._current_channel)
577
-
578
- # Re-show input
579
593
  try:
580
594
  input_widget = self.query_one(self._CHAT_INPUT_ID)
581
595
  input_widget.display = True
@@ -5,12 +5,35 @@ from __future__ import annotations
5
5
  from datetime import datetime
6
6
 
7
7
  from textual.app import ComposeResult
8
+ from textual.binding import Binding
8
9
  from textual.containers import Vertical
9
10
  from textual.message import Message
10
11
  from textual.widget import Widget
11
12
  from textual.widgets import Input, RichLog, Static
12
13
 
13
14
 
15
+ class ChatInput(Input):
16
+ """Input with Alt+Arrow word-jump and Alt+Backspace word-delete."""
17
+
18
+ BINDINGS = [
19
+ Binding("alt+left", "cursor_left_word", "Word left", show=False),
20
+ Binding("alt+right", "cursor_right_word", "Word right", show=False),
21
+ Binding(
22
+ "alt+shift+left",
23
+ "cursor_left_word(True)",
24
+ "Select word left",
25
+ show=False,
26
+ ),
27
+ Binding(
28
+ "alt+shift+right",
29
+ "cursor_right_word(True)",
30
+ "Select word right",
31
+ show=False,
32
+ ),
33
+ Binding("alt+backspace", "delete_left_word", "Delete word", show=False),
34
+ ]
35
+
36
+
14
37
  class ChatPanel(Widget):
15
38
  """Center panel showing the message log and an input field.
16
39
 
@@ -75,7 +98,7 @@ class ChatPanel(Widget):
75
98
  yield Static("", id="chat-header")
76
99
  with Vertical():
77
100
  yield RichLog(id="chat-log", markup=True, wrap=True, highlight=False)
78
- yield Input(placeholder="Type a message or /command…", id="chat-input")
101
+ yield ChatInput(placeholder="Type a message or /command…", id="chat-input")
79
102
 
80
103
  def on_mount(self) -> None:
81
104
  self._channel = ""
@@ -205,11 +205,23 @@ class IRCObserver:
205
205
  async def send_message(self, target: str, text: str) -> None:
206
206
  """Send a PRIVMSG to a channel or nick, then disconnect.
207
207
 
208
+ ``text`` is split on real ``\\n`` bytes into one PRIVMSG per line,
209
+ since an IRC PRIVMSG must be single-line per RFC 2812. Empty lines
210
+ (and any embedded ``\\r``) are dropped — IRC can't carry an empty
211
+ PRIVMSG body, and this keeps multi-line output from emitting no-op
212
+ frames. If every line is empty, the method returns without
213
+ connecting.
214
+
208
215
  Uses the same ephemeral connection pattern as the read commands.
209
216
  """
210
- # Sanitize CR/LF to prevent IRC command injection
217
+ # Strip CR and LF from the target to prevent IRC command injection
218
+ # (a newline in the target would let an attacker smuggle a second
219
+ # protocol line).
211
220
  target = target.replace("\r", "").replace("\n", "")
212
- text = text.replace("\r", "").replace("\n", " ")
221
+ # Split on real newlines; drop empty lines and strip CRs
222
+ lines = [ln for ln in text.replace("\r", "").split("\n") if ln]
223
+ if not lines:
224
+ return
213
225
 
214
226
  reader, writer, nick = await self._connect_and_register()
215
227
  try:
@@ -220,7 +232,8 @@ class IRCObserver:
220
232
  # Drain join responses
221
233
  await self._recv_lines(reader, timeout=1.0)
222
234
 
223
- writer.write(f"PRIVMSG {target} :{text}\r\n".encode())
235
+ for line in lines:
236
+ writer.write(f"PRIVMSG {target} :{line}\r\n".encode())
224
237
  await writer.drain()
225
238
  finally:
226
239
  await self._disconnect(writer)
@@ -244,6 +244,23 @@ culture channel message "#general" "hello from the CLI"
244
244
 
245
245
  Uses an ephemeral IRC connection — no daemon required.
246
246
 
247
+ **Multi-line messages.** The message text interprets `\n` as a newline and
248
+ `\t` as a tab, so the shell can pass multi-line input without needing
249
+ `$'...'` quoting. Each line is sent as a separate IRC `PRIVMSG` (required by
250
+ RFC 2812 — a single `PRIVMSG` can't span lines). Empty lines are dropped.
251
+
252
+ ```bash
253
+ culture channel message "#general" "line one\nline two\nline three"
254
+ # → three separate PRIVMSG lines on the channel
255
+ ```
256
+
257
+ To send a literal backslash-n (two characters) escape the backslash:
258
+
259
+ ```bash
260
+ culture channel message "#general" "use \\n in your string"
261
+ # → sends the text: use \n in your string
262
+ ```
263
+
247
264
  ### `culture agent message`
248
265
 
249
266
  Send a message directly to an agent.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentirc-cli"
3
- version = "6.2.0"
3
+ version = "6.2.1"
4
4
  description = "Legacy alias for culture — install culture instead"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -0,0 +1,199 @@
1
+ """Tests for console fixes covering issues #224, #225, #226, #227.
2
+
3
+ - #224 `/read` on exit overview + mouse copy-paste documentation
4
+ - #225 Newlines in IRC messages — CLI escape interpretation + observer split
5
+ - #226 Alt+Arrow word-jump (and Alt+Backspace) in chat input
6
+ - #227 Tab cycles channels (priority binding over Screen focus-cycling)
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+
13
+ import pytest
14
+
15
+ from culture.cli.channel import _cmd_message, _interpret_escapes
16
+ from culture.console.app import ConsoleApp
17
+ from culture.console.widgets.chat import ChatInput
18
+ from culture.observer import IRCObserver
19
+
20
+ # ---------------------------------------------------------------------------
21
+ # #225 — CLI escape interpretation
22
+ # ---------------------------------------------------------------------------
23
+
24
+
25
+ class TestInterpretEscapes:
26
+ """Convert shell-literal \\n / \\t / \\\\ sequences to real chars."""
27
+
28
+ def test_single_newline(self):
29
+ assert _interpret_escapes(r"a\nb") == "a\nb"
30
+
31
+ def test_multiple_newlines(self):
32
+ assert _interpret_escapes(r"a\nb\nc") == "a\nb\nc"
33
+
34
+ def test_tab(self):
35
+ assert _interpret_escapes(r"col1\tcol2") == "col1\tcol2"
36
+
37
+ def test_mixed(self):
38
+ assert _interpret_escapes(r"line1\n\tindented") == "line1\n\tindented"
39
+
40
+ def test_no_escapes(self):
41
+ assert _interpret_escapes("plain text") == "plain text"
42
+
43
+ def test_real_newlines_preserved(self):
44
+ # Real newlines (e.g., from heredoc) passthrough unchanged.
45
+ assert _interpret_escapes("a\nb") == "a\nb"
46
+
47
+ def test_double_backslash_is_literal_backslash(self):
48
+ # \\ collapses to a single backslash — so \\n stays as literal
49
+ # backslash-n (two chars), not as a newline.
50
+ assert _interpret_escapes(r"a\\b") == r"a\b"
51
+ assert _interpret_escapes(r"use \\n in your text") == r"use \n in your text"
52
+
53
+ def test_other_escapes_untouched(self):
54
+ # Only \n, \t, \\ are recognised. Any other \x pair is passed through
55
+ # with the backslash intact — no \x.. / \u.... / \r interpretation.
56
+ assert _interpret_escapes(r"a\fb") == r"a\fb"
57
+ assert _interpret_escapes(r"a\rb") == r"a\rb"
58
+ assert _interpret_escapes(r"a\xb") == r"a\xb"
59
+ # Trailing lone backslash passes through
60
+ assert _interpret_escapes("a\\") == "a\\"
61
+
62
+ def test_interpreted_newline_is_real(self):
63
+ # The literal \n we produce is a real newline, not a backslash-n.
64
+ assert "\\n" not in _interpret_escapes(r"a\nb")
65
+
66
+
67
+ class TestMessageEmptyAfterInterpret:
68
+ """_cmd_message must exit 1 if nothing would be sent after interpretation."""
69
+
70
+ def _args(self, text: str) -> argparse.Namespace:
71
+ return argparse.Namespace(
72
+ target="#test",
73
+ text=text,
74
+ config="~/.culture/server.yaml",
75
+ )
76
+
77
+ def test_only_whitespace_and_newlines_rejected(self, capsys):
78
+ # Pre-interpretation pass (non-empty string), but after interpretation
79
+ # the message is all empty lines — nothing would actually be sent.
80
+ with pytest.raises(SystemExit) as exc:
81
+ _cmd_message(self._args(r"\n\n\n"))
82
+ assert exc.value.code == 1
83
+ assert "no non-empty line" in capsys.readouterr().err
84
+
85
+ def test_whitespace_only_rejected_before_interpretation(self, capsys):
86
+ # The existing strip check rejects pure whitespace before we get to
87
+ # the interpretation step.
88
+ with pytest.raises(SystemExit) as exc:
89
+ _cmd_message(self._args(" "))
90
+ assert exc.value.code == 1
91
+ assert "cannot be empty" in capsys.readouterr().err
92
+
93
+
94
+ # ---------------------------------------------------------------------------
95
+ # #225 — observer multi-line PRIVMSG
96
+ # ---------------------------------------------------------------------------
97
+
98
+
99
+ @pytest.mark.asyncio
100
+ async def test_observer_send_message_splits_newlines(server, make_client):
101
+ """Issue #225: send_message emits one PRIVMSG per newline."""
102
+ # Receiver joins and listens
103
+ receiver = await make_client(nick="testserv-recv", user="recv")
104
+ await receiver.send("JOIN #multiline")
105
+ await receiver.recv_all(timeout=0.5)
106
+
107
+ observer = IRCObserver(
108
+ host="127.0.0.1",
109
+ port=server.config.port,
110
+ server_name=server.config.name,
111
+ )
112
+ await observer.send_message("#multiline", "line one\nline two\nline three")
113
+
114
+ lines = await receiver.recv_all(timeout=1.0)
115
+ joined = "\n".join(lines)
116
+
117
+ # Each line must arrive as a separate PRIVMSG
118
+ assert joined.count("PRIVMSG #multiline") >= 3
119
+ assert "line one" in joined
120
+ assert "line two" in joined
121
+ assert "line three" in joined
122
+
123
+
124
+ @pytest.mark.asyncio
125
+ async def test_observer_send_message_drops_empty_lines(server, make_client):
126
+ """Empty lines between real lines are dropped (IRC can't send empty PRIVMSG)."""
127
+ receiver = await make_client(nick="testserv-recv2", user="recv2")
128
+ await receiver.send("JOIN #drops")
129
+ await receiver.recv_all(timeout=0.5)
130
+
131
+ observer = IRCObserver(
132
+ host="127.0.0.1",
133
+ port=server.config.port,
134
+ server_name=server.config.name,
135
+ )
136
+ await observer.send_message("#drops", "a\n\n\nb")
137
+
138
+ lines = await receiver.recv_all(timeout=1.0)
139
+ joined = "\n".join(lines)
140
+ # Exactly two PRIVMSGs — the three blank segments are skipped
141
+ assert joined.count("PRIVMSG #drops") == 2
142
+
143
+
144
+ # ---------------------------------------------------------------------------
145
+ # #226 — ChatInput bindings
146
+ # ---------------------------------------------------------------------------
147
+
148
+
149
+ class TestChatInputBindings:
150
+ """ChatInput must expose Alt+Arrow word-jump and Alt+Backspace."""
151
+
152
+ def _binding_map(self) -> dict[str, str]:
153
+ return {b.key: b.action for b in ChatInput.BINDINGS}
154
+
155
+ def test_alt_left_word_jump(self):
156
+ assert self._binding_map().get("alt+left") == "cursor_left_word"
157
+
158
+ def test_alt_right_word_jump(self):
159
+ assert self._binding_map().get("alt+right") == "cursor_right_word"
160
+
161
+ def test_alt_shift_selects_word(self):
162
+ m = self._binding_map()
163
+ assert m.get("alt+shift+left") == "cursor_left_word(True)"
164
+ assert m.get("alt+shift+right") == "cursor_right_word(True)"
165
+
166
+ def test_alt_backspace_deletes_word(self):
167
+ assert self._binding_map().get("alt+backspace") == "delete_left_word"
168
+
169
+
170
+ # ---------------------------------------------------------------------------
171
+ # #227 — Tab cycles channels (priority binding)
172
+ # ---------------------------------------------------------------------------
173
+
174
+
175
+ class TestConsoleAppBindings:
176
+ """App-level bindings for #227 and the Ctrl+H help follow-up."""
177
+
178
+ def _bindings(self):
179
+ return {b.key: b for b in ConsoleApp.BINDINGS}
180
+
181
+ def test_tab_cycles_next_channel(self):
182
+ b = self._bindings()["tab"]
183
+ assert b.action == "next_channel"
184
+ assert b.priority is True, "Tab must use priority to beat Screen focus-cycling"
185
+
186
+ def test_shift_tab_cycles_prev_channel(self):
187
+ b = self._bindings()["shift+tab"]
188
+ assert b.action == "prev_channel"
189
+ assert b.priority is True
190
+
191
+ def test_help_is_bound_to_f1(self):
192
+ # F1 is the primary help key — most terminals eat Ctrl+H as backspace.
193
+ f1 = self._bindings()["f1"]
194
+ assert f1.action == "show_help"
195
+
196
+ def test_ctrl_h_remains_as_secondary_help(self):
197
+ # Secondary binding still present for terminals with modifyOtherKeys.
198
+ ctrl_h = self._bindings()["ctrl+h"]
199
+ assert ctrl_h.action == "show_help"
@@ -561,7 +561,7 @@ wheels = [
561
561
 
562
562
  [[package]]
563
563
  name = "culture"
564
- version = "6.2.0"
564
+ version = "6.2.1"
565
565
  source = { editable = "." }
566
566
  dependencies = [
567
567
  { name = "aiohttp" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes