agentirc-cli 6.2.2__tar.gz → 7.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentirc_cli-7.0.4/.claude/agents/doc-test-alignment.md +154 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/.claude/skills/pr-review/SKILL.md +9 -1
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/.github/workflows/security-checks.yml +1 -1
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/CHANGELOG.md +87 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/CLAUDE.md +10 -1
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/PKG-INFO +1 -1
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/client.py +123 -17
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/config.py +1 -0
- agentirc_cli-7.0.4/culture/agentirc/events.py +117 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/ircd.py +257 -26
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/server_link.py +131 -31
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/skill.py +19 -9
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/skills/history.py +28 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/skills/rooms.py +17 -1
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/bots/bot.py +121 -16
- agentirc_cli-7.0.4/culture/bots/bot_manager.py +197 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/bots/config.py +44 -10
- agentirc_cli-7.0.4/culture/bots/filter_dsl.py +339 -0
- agentirc_cli-7.0.4/culture/bots/system/__init__.py +36 -0
- agentirc_cli-7.0.4/culture/bots/system/welcome/__init__.py +1 -0
- agentirc_cli-7.0.4/culture/bots/system/welcome/bot.yaml +11 -0
- agentirc_cli-7.0.4/culture/bots/system/welcome/handler.py +1 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/bots/template_engine.py +11 -6
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/bots/virtual_client.py +43 -5
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/cli/agent.py +11 -5
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/cli/channel.py +14 -12
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/cli/mesh.py +40 -30
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/cli/server.py +53 -49
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/cli/shared/display.py +49 -36
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/acp/agent_runner.py +52 -47
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/acp/daemon.py +16 -11
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/acp/irc_transport.py +13 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/agent_runner.py +19 -16
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/daemon.py +36 -29
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/irc_transport.py +13 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/codex/daemon.py +34 -20
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/codex/irc_transport.py +13 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/copilot/daemon.py +16 -11
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/copilot/irc_transport.py +13 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/console/client.py +1 -1
- agentirc_cli-7.0.4/culture/constants.py +20 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/observer.py +34 -18
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/protocol/commands.py +1 -0
- agentirc_cli-7.0.4/culture/protocol/extensions/events.md +145 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/protocol/extensions/federation.md +4 -0
- agentirc_cli-7.0.4/culture/protocol/extensions/icons.md +117 -0
- agentirc_cli-7.0.4/culture/protocol/message.py +128 -0
- agentirc_cli-7.0.4/docs/agentirc/bots.md +216 -0
- agentirc_cli-7.0.4/docs/agentirc/events.md +146 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/server/architecture.md +4 -1
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/server/config.md +18 -0
- agentirc_cli-7.0.4/docs/superpowers/plans/2026-04-15-mesh-events.md +3252 -0
- agentirc_cli-7.0.4/docs/superpowers/specs/2026-04-15-mesh-events-design.md +380 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/packages/agent-harness/daemon.py +38 -31
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/packages/agent-harness/irc_transport.py +18 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/pyproject.toml +2 -1
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/conftest.py +121 -6
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_agent_runner.py +1 -2
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_console_client.py +8 -6
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_console_fixes_224_227.py +4 -1
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_daemon_config.py +3 -7
- agentirc_cli-7.0.4/tests/test_events_basic.py +292 -0
- agentirc_cli-7.0.4/tests/test_events_bot_chain.py +59 -0
- agentirc_cli-7.0.4/tests/test_events_bot_trigger.py +99 -0
- agentirc_cli-7.0.4/tests/test_events_cap_fallback.py +59 -0
- agentirc_cli-7.0.4/tests/test_events_catalog.py +70 -0
- agentirc_cli-7.0.4/tests/test_events_federation.py +110 -0
- agentirc_cli-7.0.4/tests/test_events_history.py +64 -0
- agentirc_cli-7.0.4/tests/test_events_lifecycle.py +343 -0
- agentirc_cli-7.0.4/tests/test_events_reserved_nick.py +68 -0
- agentirc_cli-7.0.4/tests/test_filter_dsl.py +99 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_history.py +21 -19
- agentirc_cli-7.0.4/tests/test_irc_transport_tags.py +106 -0
- agentirc_cli-7.0.4/tests/test_message_tags.py +75 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_overview_collector.py +26 -12
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_skills.py +7 -3
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_supervisor.py +1 -2
- agentirc_cli-7.0.4/tests/test_welcome_bot.py +87 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/uv.lock +1 -1
- agentirc_cli-6.2.2/culture/bots/bot_manager.py +0 -111
- agentirc_cli-6.2.2/culture/protocol/extensions/icons.md +0 -89
- agentirc_cli-6.2.2/culture/protocol/message.py +0 -58
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/.claude/skills/run-tests/SKILL.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/.flake8 +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/.github/workflows/docs-check.yml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/.gitignore +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/.markdownlint-cli2.yaml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/.pr_agent.toml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/.pre-commit-config.yaml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/.pylintrc +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/Gemfile +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/Gemfile.lock +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/LICENSE +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/README.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/SECURITY.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/_config.agentirc.yml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/_config.base.yml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/_config.culture.yml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/_data/sites.yml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/_includes/head_custom.html +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/_plugins/site_filter.rb +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/_sass/color_schemes/anthropic.scss +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/_sass/color_schemes/dark-terminal.scss +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/_sass/custom/custom.scss +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/assets/images/IMG_3183.png +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/assets/images/apple-touch-icon.png +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/assets/images/favicon-16x16.png +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/assets/images/favicon-32x32.png +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/assets/images/favicon.ico +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/assets/images/og-agentirc.png +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/assets/images/og-culture.png +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/__main__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/CLAUDE.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/__main__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/channel.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/docs/agentirc-architecture.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/docs/agentirc-features.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/docs/agentirc-skill.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/docs/agentirc.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/history_store.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/remote_client.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/room_store.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/rooms_util.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/skills/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/skills/icon.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/skills/threads.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/agentirc/thread_store.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/aio.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/bots/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/bots/http_listener.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/cli/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/cli/bot.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/cli/shared/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/cli/shared/constants.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/cli/shared/formatting.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/cli/shared/ipc.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/cli/shared/mesh.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/cli/shared/process.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/cli/skills.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/acp/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/acp/config.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/acp/culture.yaml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/acp/ipc.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/acp/message_buffer.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/acp/skill/SKILL.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/acp/skill/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/acp/skill/irc_client.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/acp/socket_server.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/acp/supervisor.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/acp/webhook.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/__main__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/config.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/culture.yaml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/ipc.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/message_buffer.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/skill/SKILL.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/skill/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/skill/irc_client.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/socket_server.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/supervisor.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/claude/webhook.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/codex/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/codex/agent_runner.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/codex/config.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/codex/culture.yaml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/codex/ipc.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/codex/message_buffer.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/codex/skill/SKILL.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/codex/skill/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/codex/skill/irc_client.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/codex/socket_server.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/codex/supervisor.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/codex/webhook.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/copilot/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/copilot/agent_runner.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/copilot/config.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/copilot/culture.yaml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/copilot/ipc.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/copilot/message_buffer.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/copilot/skill/SKILL.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/copilot/skill/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/copilot/skill/irc_client.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/copilot/socket_server.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/copilot/supervisor.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/clients/copilot/webhook.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/config.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/console/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/console/app.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/console/commands.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/console/status.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/console/widgets/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/console/widgets/chat.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/console/widgets/info_panel.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/console/widgets/sidebar.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/credentials.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/formatting.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/learn_prompt.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/mesh_config.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/overview/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/overview/collector.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/overview/model.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/overview/renderer_text.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/overview/renderer_web.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/overview/web/style.css +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/persistence.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/pidfile.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/protocol/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/protocol/extensions/history.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/protocol/extensions/rooms.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/protocol/extensions/tags.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/protocol/extensions/threads.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/protocol/protocol-index.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/protocol/replies.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/culture/skills/culture/SKILL.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/README.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/agentirc/architecture-overview.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/agentirc/index.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/agentirc/why-agentirc.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/culture/agent-lifecycle.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/culture/choose-a-harness.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/culture/index.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/culture/mental-model.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/culture/operate.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/culture/patterns.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/culture/quickstart.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/culture/reflective-development.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/culture/vision-patterns-index.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/culture/vision.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/culture/why-culture.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/architecture/agent-harness-spec.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/architecture/index.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/architecture/layers.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/architecture/threads.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/cli/commands.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/cli/index.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/harnesses/acp.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/harnesses/claude.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/harnesses/codex.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/harnesses/copilot.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/harnesses/index.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/index.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/server/deployment.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/server/index.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/reference/server/security.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/resources/github-copilot-sdk-instructions.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/concepts/federation.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/concepts/harnesses.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/concepts/humans-and-agents.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/concepts/index.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/concepts/persistence.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/concepts/rooms.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/demos/magic-demo.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/guides/first-session.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/guides/index.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/guides/join-as-human.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/guides/local-setup.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/guides/multi-machine.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/use-cases/01-pair-programming.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/use-cases/02-code-review-ensemble.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/use-cases/03-cross-server-delegation.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/use-cases/04-knowledge-propagation.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/use-cases/05-the-observer.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/use-cases/06-cross-server-ops.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/use-cases/07-supervisor-intervention.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/use-cases/08-apps-as-agents.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/use-cases/09-research-swarm.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/use-cases/10-agent-lifecycle.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/shared/use-cases-index.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/plans/2026-04-09-decentralized-agent-config.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/plans/2026-04-12-console-enhancements.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-04-07-reflective-development-reframe-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-04-08-reflective-development-deepening-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-04-09-decentralized-agent-config-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/docs/superpowers/specs/2026-04-12-console-enhancements-design.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/favicon.ico +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/packages/agent-harness/README.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/packages/agent-harness/config.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/packages/agent-harness/culture.yaml +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/packages/agent-harness/ipc.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/packages/agent-harness/message_buffer.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/packages/agent-harness/skill/SKILL.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/packages/agent-harness/skill/irc_client.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/packages/agent-harness/socket_server.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/packages/agent-harness/webhook.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/plugins/claude-code/skills/culture/SKILL.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/plugins/claude-code/skills/irc/SKILL.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/robots.txt +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/sonar-project.properties +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/__init__.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_acp_daemon.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_archive.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_bot.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_bot_config.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_bot_manager.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_bots_integration.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_channel.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_channel_cli.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_codex_daemon.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_connection.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_console_commands.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_console_connection.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_console_icons.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_console_integration.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_console_status.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_copilot_daemon.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_credentials.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_culture_config.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_daemon.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_daemon_ipc.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_discovery.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_display.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_federation.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_http_listener.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_integration_layer5.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_ipc.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_irc_transport.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_learn_prompt.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_link_reconnect.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_manifest_config.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_mention_alias.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_mention_target_cleanup.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_mention_warning.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_mentions.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_mesh_config.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_mesh_readiness.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_message.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_message_buffer.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_messaging.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_migrate_cli.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_modes.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_overview_cli.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_overview_model.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_overview_renderer.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_overview_web.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_persistence.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_pidfile.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_poll_loop.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_register_cli.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_room_persistence.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_rooms.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_rooms_federation.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_rooms_integration.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_server_icon_skill.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_setup_update_cli.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_skill_client.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_skill_docs.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_socket_server.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_template_engine.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_thread_buffer.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_threads.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_virtual_client.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_wait_for_port.py +0 -0
- {agentirc_cli-6.2.2 → agentirc_cli-7.0.4}/tests/test_webhook.py +0 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: doc-test-alignment
|
|
3
|
+
description: Audits a staged/branch diff for new public API surface (exceptions, classes, public functions, CLI commands, IRC protocol verbs, backend config fields) and reports whether `docs/` and `protocol/extensions/` mention them. Use at the end of a plan, before the first push, or when the user says "doc audit", "doc-test alignment", "check docs coverage".
|
|
4
|
+
tools: Read, Grep, Glob, Bash
|
|
5
|
+
model: sonnet
|
|
6
|
+
color: cyan
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Doc-Test Alignment Auditor
|
|
10
|
+
|
|
11
|
+
You audit a code change for **doc drift**: new public API surface that
|
|
12
|
+
`docs/` doesn't mention. Run at the end of a plan (after tests pass, before
|
|
13
|
+
push). Your job is to surface omissions, not to write docs.
|
|
14
|
+
|
|
15
|
+
## Step 1 — Determine the diff
|
|
16
|
+
|
|
17
|
+
If the caller passed a specific ref or file list, use that. Otherwise, audit
|
|
18
|
+
the current branch against `main`:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
git diff --name-only main...HEAD
|
|
22
|
+
git diff main...HEAD -- '*.py' 'culture/cli/*' 'culture/agentirc/*'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
If the branch has no commits ahead of `main`, fall back to staged changes:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
git diff --cached --name-only
|
|
29
|
+
git diff --cached -- '*.py'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If neither yields a diff, report "no changes to audit" and stop.
|
|
33
|
+
|
|
34
|
+
## Step 2 — Extract new public API surface
|
|
35
|
+
|
|
36
|
+
Run the patterns via the **Grep tool** (ripgrep / Rust regex engine — the
|
|
37
|
+
Claude Code default). Patterns below are written in the Rust regex flavor
|
|
38
|
+
that ripgrep accepts without any extra flags: `\s`, `(?:...)`, and character
|
|
39
|
+
classes all work as written. Do **not** pipe these into POSIX `grep` / BRE
|
|
40
|
+
— they will silently fail to match.
|
|
41
|
+
|
|
42
|
+
Save the diff first, then search it:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git diff main...HEAD -- '*.py' 'culture/cli/*' 'culture/agentirc/*' > /tmp/branch-diff.patch
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Then feed `/tmp/branch-diff.patch` to the Grep tool with each pattern below.
|
|
49
|
+
|
|
50
|
+
| Surface type | Ripgrep pattern |
|
|
51
|
+
|--------------|-----------------|
|
|
52
|
+
| New exception class | `^\+class [A-Z][A-Za-z0-9_]*(\(.*(?:Error\|Exception).*\))?:` |
|
|
53
|
+
| New public class | `^\+class [A-Z][A-Za-z0-9_]*` (exclude leading `_`) |
|
|
54
|
+
| New public function | `^\+(async )?def [a-z][a-z0-9_]*\(` (exclude leading `_`) |
|
|
55
|
+
| New CLI command | `^\+.*add_parser\(['"]` in `culture/cli/` |
|
|
56
|
+
| New IRC verb | `^\+.*(_msg_handlers\[['"]\|commands\s*=).*['"][A-Z0-9]+['"]` |
|
|
57
|
+
| New config field | `^\+.*@dataclass` or `^\+\s+[a-z_]+:\s+[A-Z].*=` in config modules |
|
|
58
|
+
|
|
59
|
+
Ignore private symbols (leading `_`), test helpers (inside `tests/`), and
|
|
60
|
+
internal-only classes (docstring contains "internal" or "private").
|
|
61
|
+
|
|
62
|
+
List every match with:
|
|
63
|
+
|
|
64
|
+
- symbol name
|
|
65
|
+
- file path + starting line
|
|
66
|
+
- kind (exception / class / function / CLI command / IRC verb / config field)
|
|
67
|
+
|
|
68
|
+
## Step 3 — Check doc coverage
|
|
69
|
+
|
|
70
|
+
For each extracted symbol, grep the documentation tree for a mention:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
grep -r -l '<symbol>' docs/ protocol/extensions/ 2>/dev/null
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Match rules:
|
|
77
|
+
|
|
78
|
+
- **Exception**: must be referenced in at least one `docs/**/*.md` page. If
|
|
79
|
+
it's a new public exception raised across module boundaries, that's a
|
|
80
|
+
stronger obligation — flag loudly.
|
|
81
|
+
- **CLI command**: must appear in `docs/` (user-facing command reference)
|
|
82
|
+
AND in the command's own module docstring.
|
|
83
|
+
- **IRC verb / protocol extension**: MUST have a page under
|
|
84
|
+
`protocol/extensions/` (per the repository-root `CLAUDE.md`: "Extensions
|
|
85
|
+
use new verbs (never redefine existing commands), documented in
|
|
86
|
+
`protocol/extensions/`").
|
|
87
|
+
- **Config field**: must appear in the relevant backend's `culture.yaml`
|
|
88
|
+
reference under `packages/agent-harness/culture.yaml` or
|
|
89
|
+
`docs/configuration.md`.
|
|
90
|
+
- **Public function/class**: soft obligation — flag if the symbol is
|
|
91
|
+
imported from outside its module, skip if it's used only within its
|
|
92
|
+
package.
|
|
93
|
+
|
|
94
|
+
## Step 4 — Check test coverage (light)
|
|
95
|
+
|
|
96
|
+
For each public surface symbol, grep `tests/` for at least one reference:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
grep -r -l '<symbol>' tests/ 2>/dev/null
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
If zero test files reference the symbol, flag it. This is a weak signal —
|
|
103
|
+
the caller may have tested behavior without naming the symbol directly.
|
|
104
|
+
Report as "likely untested" rather than "untested".
|
|
105
|
+
|
|
106
|
+
## Step 5 — All-backends check (culture-specific)
|
|
107
|
+
|
|
108
|
+
If the diff touches `culture/clients/<backend>/` OR `packages/agent-harness/`,
|
|
109
|
+
list the other backends (`claude`, `codex`, `copilot`, `acp`) and report
|
|
110
|
+
whether the same change appears in each. This enforces the project's
|
|
111
|
+
"all-backends rule" (per the repository-root `CLAUDE.md`). A change in
|
|
112
|
+
only one backend is a bug.
|
|
113
|
+
|
|
114
|
+
## Step 6 — Report
|
|
115
|
+
|
|
116
|
+
Output a concise table. Factual only: what symbols, what's missing,
|
|
117
|
+
what's drifted. Do **not** write prose narration and do **not** suggest
|
|
118
|
+
what the docs should say — leave doc wording to the caller. A brief
|
|
119
|
+
counts-only summary line at the end is fine; skip it if everything is
|
|
120
|
+
clean.
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
## Doc-Test Alignment — <branch> vs main
|
|
124
|
+
|
|
125
|
+
### New public API surface
|
|
126
|
+
| Symbol | Kind | Location | Docs | Tests |
|
|
127
|
+
|--------|------|----------|------|-------|
|
|
128
|
+
| ConsoleConnectionLost | exception | culture/console/client.py:42 | MISSING | test_console_client.py |
|
|
129
|
+
| _handle_reconnect | private | culture/console/app.py:412 | (skip — private) | — |
|
|
130
|
+
|
|
131
|
+
### Protocol extensions
|
|
132
|
+
(none in this diff)
|
|
133
|
+
|
|
134
|
+
### All-backends drift
|
|
135
|
+
Change touches `packages/agent-harness/irc_transport.py`:
|
|
136
|
+
- claude: ✓ updated
|
|
137
|
+
- codex: ✓ updated
|
|
138
|
+
- copilot: ✗ NOT updated (divergent)
|
|
139
|
+
- acp: ✗ NOT updated (divergent)
|
|
140
|
+
|
|
141
|
+
### Summary
|
|
142
|
+
Doc gaps: 1 · Likely untested: 0 · Backend drift: 2
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
If nothing is missing, output one line: `Doc-test alignment clean — no gaps detected.`
|
|
146
|
+
|
|
147
|
+
## What NOT to do
|
|
148
|
+
|
|
149
|
+
- Do not write documentation. Report gaps only.
|
|
150
|
+
- Do not modify code or tests.
|
|
151
|
+
- Do not fail loudly on borderline cases (internal-only helpers, test
|
|
152
|
+
utilities) — the caller is smarter than the heuristics; your value is in
|
|
153
|
+
catching the obvious omissions.
|
|
154
|
+
- Do not re-run the test suite — `/run-tests` is the tool for that.
|
|
@@ -171,7 +171,15 @@ bash ~/.claude/skills/pr-review/scripts/pr-reply.sh --resolve <PR_NUMBER> <COMME
|
|
|
171
171
|
- Always use `--resolve` to resolve the thread after replying
|
|
172
172
|
- Every comment must get a reply — no silent fixes
|
|
173
173
|
|
|
174
|
-
## Step 9 —
|
|
174
|
+
## Step 9 — Check SonarCloud before declaring ready
|
|
175
|
+
|
|
176
|
+
After CI is green and all inline threads are resolved, query SonarCloud
|
|
177
|
+
for the branch via the `/sonarclaude` skill. SonarCloud findings do not
|
|
178
|
+
always arrive as inline PR comments — a fully-resolved thread list plus an
|
|
179
|
+
all-green `gh pr checks` is **not** sufficient evidence that the PR is
|
|
180
|
+
clean. If `/sonarclaude` surfaces new findings, loop back to Step 7.
|
|
181
|
+
|
|
182
|
+
## Step 10 — Wait for merge
|
|
175
183
|
|
|
176
184
|
**Never merge the PR yourself.** The PR is merged manually on the GitHub site.
|
|
177
185
|
|
|
@@ -33,7 +33,7 @@ jobs:
|
|
|
33
33
|
continue-on-error: true
|
|
34
34
|
|
|
35
35
|
- name: Run Safety dependency check
|
|
36
|
-
run: uv run safety check --
|
|
36
|
+
run: uv run safety check --output json > safety-results.json
|
|
37
37
|
continue-on-error: true
|
|
38
38
|
|
|
39
39
|
- name: Upload Security Results
|
|
@@ -4,6 +4,93 @@ 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
|
+
## [7.0.4] - 2026-04-17
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Reduce cognitive complexity and fix code quality issues across CLI modules (SonarCloud S3776, S1192, S5886, S108)
|
|
13
|
+
|
|
14
|
+
## [7.0.3] - 2026-04-17
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Duplicate _ERR_CHANNEL_PREFIX string in all 5 daemon files (S1192)
|
|
20
|
+
- Cognitive complexity in claude/daemon.py _poll_loop (CC 22, S3776)
|
|
21
|
+
- Cognitive complexity in agent-harness/daemon.py _poll_loop (CC 22, S3776)
|
|
22
|
+
- Cognitive complexity in codex/daemon.py _relay_response_to_irc (CC 44, S3776)
|
|
23
|
+
- Cognitive complexity in copilot/daemon.py _relay_response_to_irc (CC 23, S3776)
|
|
24
|
+
- Cognitive complexity in acp/daemon.py _relay_response_to_irc (CC 23, S3776)
|
|
25
|
+
- Cognitive complexity in acp/agent_runner.py start() (CC 17, S3776)
|
|
26
|
+
|
|
27
|
+
## [7.0.2] - 2026-04-17
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- Cognitive complexity in observer.py (CC 22→~13)
|
|
33
|
+
- Cognitive complexity in ircd.py (CC 18→~12)
|
|
34
|
+
- Cognitive complexity in server_link.py (CC 33→~14)
|
|
35
|
+
|
|
36
|
+
## [7.0.1] - 2026-04-17
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- Wrong AgentConfig/DaemonConfig import types in test helpers (S5655)
|
|
42
|
+
- Constant if-False condition replaced with unreachable yield pattern (S5797)
|
|
43
|
+
|
|
44
|
+
## [7.0.0] - 2026-04-17
|
|
45
|
+
|
|
46
|
+
Mesh Events (issue #123) — lifecycle and activity notifications as IRCv3-tagged
|
|
47
|
+
PRIVMSGs, event-triggered bots, and pub/sub composition chains.
|
|
48
|
+
|
|
49
|
+
> Versions 6.3.0 through 6.11.2 were development increments for this feature
|
|
50
|
+
> and were never published. Their changes are consolidated here as 7.0.0.
|
|
51
|
+
|
|
52
|
+
### Breaking Changes
|
|
53
|
+
|
|
54
|
+
None. Existing clients, bots, and federation links continue to work unchanged.
|
|
55
|
+
The major bump reflects the scope of the feature addition (new protocol verb,
|
|
56
|
+
new subsystem, new bot trigger type).
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
|
|
60
|
+
- **Event system** — `system-<server>` pseudo-user surfaces lifecycle events
|
|
61
|
+
as IRCv3-tagged PRIVMSGs with `@event=<type>;event-data=<b64json>` tags
|
|
62
|
+
- **Built-in event catalog** — 18 event types across channel-scoped
|
|
63
|
+
(`user.join/part/quit`, `room.create/archive/meta`, `tags.update`) and global
|
|
64
|
+
(`agent.connect/disconnect`, `server.wake/sleep/link/unlink`,
|
|
65
|
+
`console.open/close`)
|
|
66
|
+
- **IRCv3 message-tags** — `Message.parse()` extracts tags; CAP negotiation
|
|
67
|
+
(`CAP REQ :message-tags`) in all agent backends and server
|
|
68
|
+
- **`#system` channel** — auto-created at startup for global event delivery;
|
|
69
|
+
`system-<server>` VirtualClient auto-joined
|
|
70
|
+
- **Reserved `system-*` nicks** — rejected for non-server clients
|
|
71
|
+
(`432 ERR_ERRONEUSNICKNAME`)
|
|
72
|
+
- **SEVENT S2S verb** — generic federation relay for lifecycle events with
|
|
73
|
+
`_origin` loop prevention and trust policy filtering
|
|
74
|
+
- **HistorySkill** stores lifecycle events — `HISTORY RECENT` replays
|
|
75
|
+
agent.connect, server.wake, room.create, etc.
|
|
76
|
+
- **Filter DSL** — safe recursive-descent expression parser (`==`, `!=`, `in`,
|
|
77
|
+
`and`, `or`, `not`, dotted field access) for bot event triggers
|
|
78
|
+
- **Bot event triggers** — `trigger.type: event` with filter DSL evaluation;
|
|
79
|
+
`fires_event` output for pub/sub bot chains with rate limiting (10/sec)
|
|
80
|
+
- **System bots** — package-bundled bots discovered at startup from
|
|
81
|
+
`culture/bots/system/<name>/bot.yaml`; welcome bot greets on `user.join`
|
|
82
|
+
- **All-backends CAP** — claude, codex, copilot, acp, and agent-harness
|
|
83
|
+
transports negotiate `message-tags` during connection
|
|
84
|
+
- **Documentation** — `docs/agentirc/events.md`, `docs/agentirc/bots.md`,
|
|
85
|
+
`culture/protocol/extensions/events.md`
|
|
86
|
+
|
|
87
|
+
## [6.2.3] - 2026-04-15
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
### Changed
|
|
91
|
+
|
|
92
|
+
- docs: post-#231 retrospective — CLAUDE.md guidance for pre-branch checklist, format-before-commit, pre-push code review, SonarCloud pre-ready; new doc-test-alignment subagent; /pr-review skill step for SonarCloud query
|
|
93
|
+
|
|
7
94
|
## [6.2.2] - 2026-04-14
|
|
8
95
|
|
|
9
96
|
|
|
@@ -49,13 +49,18 @@ Each backend has its own `culture.yaml` in `culture/clients/<backend>/`.
|
|
|
49
49
|
|
|
50
50
|
When implementing features, write a corresponding markdown doc in `docs/` describing the feature — its purpose, usage, and any protocol details. Keep `docs/` as the living reference for the project.
|
|
51
51
|
|
|
52
|
+
Before the first push on a branch that adds public API surface (new exceptions, CLI commands, IRC verbs, backend config fields), invoke the `doc-test-alignment` subagent to surface doc gaps: `Agent(subagent_type="doc-test-alignment", ...)`. It reads the branch diff and reports missing `docs/` coverage, missing protocol extension pages, and all-backends drift — it does not write docs, only flags omissions.
|
|
53
|
+
|
|
52
54
|
## Git Workflow
|
|
53
55
|
|
|
56
|
+
- **Before branching, run `git status`.** If `CHANGELOG.md`, any `CLAUDE.md`, or other files carry pre-existing unstaged changes on `main`, decide up front whether to stash, commit separately, or hand-split. `/version-bump` inserts a new section at the top of `CHANGELOG.md` and will interleave awkwardly with an existing `[Unreleased]` block if you don't.
|
|
54
57
|
- Branch out for all changes
|
|
55
|
-
- **Bump the version before creating a PR** — use `/version-bump patch` (bug fix), `minor` (new feature), or `major` (breaking change). This updates `pyproject.toml
|
|
58
|
+
- **Bump the version before creating a PR** — use `/version-bump patch` (bug fix), `minor` (new feature), or `major` (breaking change). This updates `pyproject.toml` and `CHANGELOG.md` (and `uv.lock` when applicable) in one step. Forgetting will fail the version-check CI job.
|
|
59
|
+
- **Pre-push review for library/protocol code.** When the diff touches shared choke points (transport, `_send_raw`-style I/O, protocol parsers, anything in `packages/` or `culture/agentirc/`), invoke a code reviewer on the staged diff before the first push — typed exceptions and new error paths routinely create caller cleanup obligations that Qodo/human reviewers otherwise surface in the first review round. Use `Agent(subagent_type="superpowers:code-reviewer", ...)` or `/review-and-fix`.
|
|
56
60
|
- Push to GitHub for agentic code review
|
|
57
61
|
- Pull review comments, address feedback, push fixes
|
|
58
62
|
- Reply to comments after pushing, resolve threads
|
|
63
|
+
- **Before declaring the PR ready**, check SonarCloud for the branch via the `/sonarclaude` skill. SonarCloud findings do not always arrive as inline PR comments, so an all-green `gh pr checks` + all-resolved threads is not sufficient.
|
|
59
64
|
|
|
60
65
|
## Testing
|
|
61
66
|
|
|
@@ -64,6 +69,10 @@ When implementing features, write a corresponding markdown doc in `docs/` descri
|
|
|
64
69
|
- No mocks for the server — tests spin up real server instances on random ports with real TCP connections
|
|
65
70
|
- Validate each layer with real IRC clients (weechat/irssi)
|
|
66
71
|
|
|
72
|
+
## Format Before Commit
|
|
73
|
+
|
|
74
|
+
Pre-commit runs `black`, `isort`, `flake8`, `pylint`, `bandit`. `black`/`isort` failures reformat the file and reject the commit — you then have to `git add` the reformatted file and commit again. To avoid the re-commit loop, run `uv run black <files>` and `uv run isort <files>` on staged Python files **before** `git commit`.
|
|
75
|
+
|
|
67
76
|
## Nick Format
|
|
68
77
|
|
|
69
78
|
`<server>-<agent>` (e.g., `thor-claude`, `spark-ori`). Globally unique by construction.
|
|
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING
|
|
|
9
9
|
from culture.agentirc.channel import Channel
|
|
10
10
|
from culture.agentirc.skill import Event, EventType
|
|
11
11
|
from culture.aio import maybe_await
|
|
12
|
+
from culture.constants import SYSTEM_USER_PREFIX
|
|
12
13
|
from culture.protocol import replies
|
|
13
14
|
from culture.protocol.message import Message
|
|
14
15
|
|
|
@@ -35,6 +36,7 @@ class Client:
|
|
|
35
36
|
self.channels: set[Channel] = set()
|
|
36
37
|
self._registered = False
|
|
37
38
|
self.tags: list[str] = []
|
|
39
|
+
self.caps: set[str] = set()
|
|
38
40
|
self.modes: set[str] = set()
|
|
39
41
|
self.icon: str | None = None
|
|
40
42
|
|
|
@@ -49,6 +51,28 @@ class Client:
|
|
|
49
51
|
except OSError:
|
|
50
52
|
pass # Client disconnected; cleanup happens in ircd._handle_connection
|
|
51
53
|
|
|
54
|
+
async def send_raw(self, line: str) -> None:
|
|
55
|
+
"""Write a pre-formatted IRC line to the client socket.
|
|
56
|
+
|
|
57
|
+
Appends CRLF internally, matching ServerLink.send_raw convention.
|
|
58
|
+
"""
|
|
59
|
+
try:
|
|
60
|
+
self.writer.write(f"{line}\r\n".encode("utf-8"))
|
|
61
|
+
await self.writer.drain()
|
|
62
|
+
except OSError:
|
|
63
|
+
pass # Client disconnected; cleanup happens in ircd._handle_connection
|
|
64
|
+
|
|
65
|
+
async def send_tagged(self, msg: Message) -> None:
|
|
66
|
+
"""Send a Message, stripping tags for clients that haven't negotiated message-tags."""
|
|
67
|
+
if msg.tags and "message-tags" not in self.caps:
|
|
68
|
+
msg = Message(
|
|
69
|
+
tags={},
|
|
70
|
+
prefix=msg.prefix,
|
|
71
|
+
command=msg.command,
|
|
72
|
+
params=list(msg.params),
|
|
73
|
+
)
|
|
74
|
+
await self.send(msg)
|
|
75
|
+
|
|
52
76
|
async def send_numeric(self, code: str, *params: str) -> None:
|
|
53
77
|
target = self.nick or "*"
|
|
54
78
|
msg = Message(
|
|
@@ -114,6 +138,29 @@ class Client:
|
|
|
114
138
|
def _handle_pong(self, msg: Message) -> None:
|
|
115
139
|
pass # Client responding to our ping
|
|
116
140
|
|
|
141
|
+
async def _handle_cap(self, msg: Message) -> None:
|
|
142
|
+
sub = msg.params[0].upper() if msg.params else ""
|
|
143
|
+
if sub == "LS":
|
|
144
|
+
await self.send_raw(
|
|
145
|
+
f":{self.server.config.name} CAP {self.nick or '*'} LS :message-tags"
|
|
146
|
+
)
|
|
147
|
+
elif sub == "REQ":
|
|
148
|
+
requested = msg.params[1].split() if len(msg.params) >= 2 else []
|
|
149
|
+
supported = {"message-tags"}
|
|
150
|
+
if all(cap in supported for cap in requested):
|
|
151
|
+
self.caps.update(requested)
|
|
152
|
+
await self.send_raw(
|
|
153
|
+
f":{self.server.config.name} CAP {self.nick or '*'}"
|
|
154
|
+
f" ACK :{' '.join(requested)}"
|
|
155
|
+
)
|
|
156
|
+
else:
|
|
157
|
+
await self.send_raw(
|
|
158
|
+
f":{self.server.config.name} CAP {self.nick or '*'}"
|
|
159
|
+
f" NAK :{' '.join(requested)}"
|
|
160
|
+
)
|
|
161
|
+
elif sub == "END":
|
|
162
|
+
pass # no registration-gating in v1
|
|
163
|
+
|
|
117
164
|
async def _handle_nick(self, msg: Message) -> None:
|
|
118
165
|
if not msg.params:
|
|
119
166
|
await self.send_numeric(replies.ERR_NONICKNAMEGIVEN, "No nickname given")
|
|
@@ -121,6 +168,15 @@ class Client:
|
|
|
121
168
|
|
|
122
169
|
nick = msg.params[0]
|
|
123
170
|
|
|
171
|
+
# Reject reserved system-* nick prefix
|
|
172
|
+
if nick.startswith(SYSTEM_USER_PREFIX):
|
|
173
|
+
await self.send_numeric(
|
|
174
|
+
replies.ERR_ERRONEUSNICKNAME,
|
|
175
|
+
nick,
|
|
176
|
+
"Nickname prefix 'system-' is reserved",
|
|
177
|
+
)
|
|
178
|
+
return
|
|
179
|
+
|
|
124
180
|
# Enforce server name prefix
|
|
125
181
|
expected_prefix = f"{self.server.config.name}-"
|
|
126
182
|
if not nick.startswith(expected_prefix):
|
|
@@ -227,10 +283,6 @@ class Client:
|
|
|
227
283
|
for member in list(channel.members):
|
|
228
284
|
await member.send(join_msg)
|
|
229
285
|
|
|
230
|
-
await self.server.emit_event(
|
|
231
|
-
Event(type=EventType.JOIN, channel=channel_name, nick=self.nick)
|
|
232
|
-
)
|
|
233
|
-
|
|
234
286
|
# Send topic if set
|
|
235
287
|
if channel.topic:
|
|
236
288
|
await self.send_numeric(replies.RPL_TOPIC, channel_name, channel.topic)
|
|
@@ -238,6 +290,12 @@ class Client:
|
|
|
238
290
|
# Send names list
|
|
239
291
|
await self._send_names(channel)
|
|
240
292
|
|
|
293
|
+
# Emit event AFTER delivering all join-related numerics (topic, NAMES)
|
|
294
|
+
# so that the event PRIVMSG doesn't interleave with 353/366 in client buffers.
|
|
295
|
+
await self.server.emit_event(
|
|
296
|
+
Event(type=EventType.JOIN, channel=channel_name, nick=self.nick)
|
|
297
|
+
)
|
|
298
|
+
|
|
241
299
|
async def _handle_part(self, msg: Message) -> None:
|
|
242
300
|
if not msg.params:
|
|
243
301
|
await self.send_numeric(replies.ERR_NEEDMOREPARAMS, "PART", replies.MSG_NEEDMOREPARAMS)
|
|
@@ -451,7 +509,63 @@ class Client:
|
|
|
451
509
|
for member in list(channel.members):
|
|
452
510
|
await member.send(mode_msg)
|
|
453
511
|
|
|
512
|
+
_VALID_USER_MODE_CHARS = frozenset("HABC")
|
|
513
|
+
_USER_MODE_EDGE_EVENTS: dict[tuple[str, bool], EventType] = {
|
|
514
|
+
("A", True): EventType.AGENT_CONNECT,
|
|
515
|
+
("A", False): EventType.AGENT_DISCONNECT,
|
|
516
|
+
("C", True): EventType.CONSOLE_OPEN,
|
|
517
|
+
("C", False): EventType.CONSOLE_CLOSE,
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
def _apply_user_mode_char(self, ch: str, adding: bool) -> EventType | None:
|
|
521
|
+
"""Mutate ``self.modes`` for a single mode char and return the edge event, if any.
|
|
522
|
+
|
|
523
|
+
Returns None if the char is unknown or the transition was a no-op
|
|
524
|
+
(setting an already-set mode or clearing an already-clear one).
|
|
525
|
+
"""
|
|
526
|
+
if ch not in self._VALID_USER_MODE_CHARS:
|
|
527
|
+
return None
|
|
528
|
+
had = ch in self.modes
|
|
529
|
+
if adding:
|
|
530
|
+
self.modes.add(ch)
|
|
531
|
+
else:
|
|
532
|
+
self.modes.discard(ch)
|
|
533
|
+
if had == adding:
|
|
534
|
+
return None
|
|
535
|
+
return self._USER_MODE_EDGE_EVENTS.get((ch, adding))
|
|
536
|
+
|
|
537
|
+
def _parse_mode_edges(self, modestring: str) -> list[EventType]:
|
|
538
|
+
"""Apply each char of ``modestring`` and collect the emitted edge events."""
|
|
539
|
+
pending: list[EventType] = []
|
|
540
|
+
adding = True
|
|
541
|
+
for ch in modestring:
|
|
542
|
+
if ch == "+":
|
|
543
|
+
adding = True
|
|
544
|
+
elif ch == "-":
|
|
545
|
+
adding = False
|
|
546
|
+
else:
|
|
547
|
+
event = self._apply_user_mode_char(ch, adding)
|
|
548
|
+
if event is not None:
|
|
549
|
+
pending.append(event)
|
|
550
|
+
return pending
|
|
551
|
+
|
|
552
|
+
async def _emit_user_mode_events(self, pending: list[EventType]) -> None:
|
|
553
|
+
for event_type in pending:
|
|
554
|
+
await self.server.emit_event(
|
|
555
|
+
Event(
|
|
556
|
+
type=event_type,
|
|
557
|
+
channel=None,
|
|
558
|
+
nick=self.nick,
|
|
559
|
+
data={"nick": self.nick},
|
|
560
|
+
)
|
|
561
|
+
)
|
|
562
|
+
|
|
454
563
|
async def _handle_user_mode(self, msg: Message) -> None:
|
|
564
|
+
# Reject pre-registration so an unregistered socket cannot inject
|
|
565
|
+
# agent.connect / console.open into #system by sending MODE after NICK
|
|
566
|
+
# but before USER.
|
|
567
|
+
if not self._registered:
|
|
568
|
+
return
|
|
455
569
|
target_nick = msg.params[0]
|
|
456
570
|
if target_nick != self.nick:
|
|
457
571
|
await self.send_numeric(
|
|
@@ -459,19 +573,11 @@ class Client:
|
|
|
459
573
|
"Can't change mode for other users",
|
|
460
574
|
)
|
|
461
575
|
return
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
adding = True
|
|
468
|
-
elif ch == "-":
|
|
469
|
-
adding = False
|
|
470
|
-
elif ch in ("H", "A", "B"):
|
|
471
|
-
if adding:
|
|
472
|
-
self.modes.add(ch)
|
|
473
|
-
else:
|
|
474
|
-
self.modes.discard(ch)
|
|
576
|
+
|
|
577
|
+
modestring = msg.params[1] if len(msg.params) > 1 else ""
|
|
578
|
+
pending = self._parse_mode_edges(modestring)
|
|
579
|
+
await self._emit_user_mode_events(pending)
|
|
580
|
+
|
|
475
581
|
mode_str = "+" + "".join(sorted(self.modes)) if self.modes else "+"
|
|
476
582
|
await self.send_numeric(replies.RPL_UMODEIS, mode_str)
|
|
477
583
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Event type catalog and render-template registry.
|
|
2
|
+
|
|
3
|
+
Event type names follow the dotted-lowercase convention enforced by
|
|
4
|
+
`EVENT_TYPE_RE` in `culture.constants`. Render templates map a type to a
|
|
5
|
+
function that produces the human-readable PRIVMSG body for humans and
|
|
6
|
+
vanilla IRC clients. The structured payload is attached as IRCv3 message
|
|
7
|
+
tags by the server's emit path (see `culture/agentirc/ircd.py`); this
|
|
8
|
+
module is presentation-only.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
from typing import Any, Callable
|
|
15
|
+
|
|
16
|
+
from culture.agentirc.skill import EventType
|
|
17
|
+
from culture.constants import EVENT_TYPE_RE
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
# Event types whose content is already delivered to clients via the normal
|
|
22
|
+
# IRC path (PRIVMSG, NOTICE, TOPIC) or has dedicated storage (threads).
|
|
23
|
+
# Used by both IRCd._surface_event_privmsg (to avoid double-delivery) and
|
|
24
|
+
# HistorySkill (to avoid double-storage). Keep this as the single source
|
|
25
|
+
# of truth — do not duplicate.
|
|
26
|
+
NO_SURFACE_EVENT_TYPES: frozenset[str] = frozenset(
|
|
27
|
+
{
|
|
28
|
+
EventType.MESSAGE.value,
|
|
29
|
+
EventType.THREAD_CREATE.value,
|
|
30
|
+
EventType.THREAD_MESSAGE.value,
|
|
31
|
+
EventType.THREAD_CLOSE.value,
|
|
32
|
+
EventType.TOPIC.value,
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
RenderFn = Callable[[dict[str, Any], str | None], str]
|
|
37
|
+
|
|
38
|
+
_TEMPLATES: dict[str, RenderFn] = {}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def register(event_type: str, fn: RenderFn) -> None:
|
|
42
|
+
_TEMPLATES[event_type] = fn
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def validate_event_type(name: str) -> bool:
|
|
46
|
+
return bool(EVENT_TYPE_RE.match(name))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def render_event(event_type: str, data: dict[str, Any], channel: str | None) -> str:
|
|
50
|
+
fn = _TEMPLATES.get(event_type)
|
|
51
|
+
if fn is None:
|
|
52
|
+
return f"{event_type} {data}"
|
|
53
|
+
try:
|
|
54
|
+
return fn(data, channel)
|
|
55
|
+
except Exception:
|
|
56
|
+
logger.exception("render template for %s failed", event_type)
|
|
57
|
+
return f"{event_type} {data}"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# -------- built-in render templates --------
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _nick_action(verb: str) -> RenderFn:
|
|
64
|
+
def _render(data, channel):
|
|
65
|
+
nick = data.get("nick", "<unknown>")
|
|
66
|
+
if channel:
|
|
67
|
+
return f"{nick} {verb} {channel}"
|
|
68
|
+
return f"{nick} {verb}"
|
|
69
|
+
|
|
70
|
+
return _render
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
register("user.join", _nick_action("joined"))
|
|
74
|
+
register("user.part", _nick_action("left"))
|
|
75
|
+
register(
|
|
76
|
+
"user.quit",
|
|
77
|
+
lambda d, c: f"{d.get('nick', '<unknown>')} quit: {d.get('reason', '')}".rstrip(": "),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
register("agent.connect", lambda d, c: f"{d.get('nick', '<unknown>')} connected")
|
|
81
|
+
register(
|
|
82
|
+
"agent.disconnect",
|
|
83
|
+
lambda d, c: f"{d.get('nick', '<unknown>')} disconnected",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
register("console.open", lambda d, c: f"{d.get('nick', '<unknown>')} opened a console")
|
|
87
|
+
register("console.close", lambda d, c: f"{d.get('nick', '<unknown>')} closed their console")
|
|
88
|
+
|
|
89
|
+
register("server.wake", lambda d, c: f"server {d.get('server', '<unknown>')} is up")
|
|
90
|
+
register(
|
|
91
|
+
"server.sleep",
|
|
92
|
+
lambda d, c: f"server {d.get('server', '<unknown>')} is shutting down",
|
|
93
|
+
)
|
|
94
|
+
register("server.link", lambda d, c: f"linked to {d.get('peer', '<unknown>')}")
|
|
95
|
+
register("server.unlink", lambda d, c: f"unlinked from {d.get('peer', '<unknown>')}")
|
|
96
|
+
|
|
97
|
+
register("room.create", lambda d, c: f"{d.get('nick', '<unknown>')} created room {c}")
|
|
98
|
+
register("room.archive", lambda d, c: f"{d.get('nick', '<unknown>')} archived {c}")
|
|
99
|
+
register("room.meta", lambda d, c: f"{d.get('nick', '<unknown>')} updated {c} metadata")
|
|
100
|
+
|
|
101
|
+
register(
|
|
102
|
+
"thread.create",
|
|
103
|
+
lambda d, c: f"{d.get('nick', '<unknown>')} started thread [{d.get('thread', '?')}] in {c}",
|
|
104
|
+
)
|
|
105
|
+
register(
|
|
106
|
+
"thread.message",
|
|
107
|
+
lambda d, c: f"[{d.get('thread', '?')}] {d.get('nick', '<unknown>')}: {d.get('text', '')}",
|
|
108
|
+
)
|
|
109
|
+
register(
|
|
110
|
+
"thread.close",
|
|
111
|
+
lambda d, c: f"thread [{d.get('thread', '?')}] in {c} closed",
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
register(
|
|
115
|
+
"tags.update",
|
|
116
|
+
lambda d, c: f"{d.get('nick', '<unknown>')} tags → {', '.join(d.get('tags', []))}",
|
|
117
|
+
)
|