agentirc-cli 5.0.2__tar.gz → 5.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-5.0.2 → agentirc_cli-5.0.4}/.claude/skills/pr-review/SKILL.md +1 -2
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/CHANGELOG.md +28 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/CLAUDE.md +1 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/PKG-INFO +1 -1
- agentirc_cli-5.0.4/culture/cli/channel.py +341 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/cli/mesh.py +20 -3
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/acp/skill/SKILL.md +19 -19
- {agentirc_cli-5.0.2/plugins/claude-code/skills/irc → agentirc_cli-5.0.4/culture/clients/claude/skill}/SKILL.md +20 -20
- {agentirc_cli-5.0.2/plugins/codex/skills/culture-irc → agentirc_cli-5.0.4/culture/clients/codex/skill}/SKILL.md +19 -19
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/copilot/skill/SKILL.md +19 -19
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/learn_prompt.py +13 -13
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/architecture/agent-harness-spec.md +2 -5
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/getting-started.md +7 -7
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/operations/cli.md +18 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/operations/ops-tooling.md +15 -0
- agentirc_cli-5.0.4/packages/agent-harness/skill/SKILL.md +57 -0
- {agentirc_cli-5.0.2/culture/clients/claude/skill → agentirc_cli-5.0.4/plugins/claude-code/skills/irc}/SKILL.md +20 -20
- {agentirc_cli-5.0.2/culture/clients/codex/skill → agentirc_cli-5.0.4/plugins/codex/skills/culture-irc}/SKILL.md +19 -19
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/pyproject.toml +1 -1
- agentirc_cli-5.0.4/tests/test_channel_cli.py +224 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_learn_prompt.py +7 -6
- agentirc_cli-5.0.4/tests/test_mesh_readiness.py +93 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/uv.lock +1 -1
- agentirc_cli-5.0.2/culture/cli/channel.py +0 -136
- agentirc_cli-5.0.2/packages/agent-harness/skill/SKILL.md +0 -57
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/.claude/skills/run-tests/SKILL.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/.flake8 +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/.github/workflows/pages.yml +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/.github/workflows/security-checks.yml +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/.gitignore +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/.markdownlint-cli2.yaml +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/.pr_agent.toml +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/.pre-commit-config.yaml +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/.pylintrc +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/CNAME +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/Gemfile +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/Gemfile.lock +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/LICENSE +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/README.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/SECURITY.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/_config.yml +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/_includes/head_custom.html +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/_sass/color_schemes/anthropic.scss +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/_sass/custom/custom.scss +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/assets/images/apple-touch-icon.png +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/assets/images/favicon-16x16.png +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/assets/images/favicon-32x32.png +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/assets/images/favicon.ico +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/__main__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/aio.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/bots/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/bots/bot.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/bots/bot_manager.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/bots/config.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/bots/http_listener.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/bots/template_engine.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/bots/virtual_client.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/cli/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/cli/agent.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/cli/bot.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/cli/server.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/cli/shared/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/cli/shared/constants.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/cli/shared/display.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/cli/shared/formatting.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/cli/shared/ipc.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/cli/shared/mesh.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/cli/shared/process.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/cli/skills.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/acp/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/acp/agent_runner.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/acp/config.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/acp/culture.yaml +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/acp/daemon.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/acp/ipc.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/acp/irc_transport.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/acp/message_buffer.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/acp/skill/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/acp/skill/irc_client.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/acp/socket_server.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/acp/supervisor.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/acp/webhook.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/claude/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/claude/__main__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/claude/agent_runner.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/claude/config.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/claude/culture.yaml +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/claude/daemon.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/claude/ipc.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/claude/irc_transport.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/claude/message_buffer.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/claude/skill/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/claude/skill/irc_client.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/claude/socket_server.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/claude/supervisor.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/claude/webhook.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/codex/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/codex/agent_runner.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/codex/config.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/codex/culture.yaml +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/codex/daemon.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/codex/ipc.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/codex/irc_transport.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/codex/message_buffer.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/codex/skill/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/codex/skill/irc_client.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/codex/socket_server.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/codex/supervisor.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/codex/webhook.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/copilot/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/copilot/agent_runner.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/copilot/config.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/copilot/culture.yaml +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/copilot/daemon.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/copilot/ipc.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/copilot/irc_transport.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/copilot/message_buffer.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/copilot/skill/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/copilot/skill/irc_client.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/copilot/socket_server.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/copilot/supervisor.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/clients/copilot/webhook.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/config.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/console/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/console/app.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/console/client.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/console/commands.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/console/widgets/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/console/widgets/chat.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/console/widgets/info_panel.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/console/widgets/sidebar.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/credentials.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/formatting.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/mesh_config.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/observer.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/overview/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/overview/collector.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/overview/model.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/overview/renderer_text.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/overview/renderer_web.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/overview/web/style.css +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/persistence.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/pidfile.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/protocol/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/protocol/commands.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/protocol/extensions/federation.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/protocol/extensions/history.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/protocol/extensions/icons.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/protocol/extensions/rooms.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/protocol/extensions/tags.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/protocol/extensions/threads.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/protocol/message.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/protocol/protocol-index.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/protocol/replies.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/__main__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/channel.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/client.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/config.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/history_store.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/ircd.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/remote_client.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/room_store.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/rooms_util.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/server_link.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/skill.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/skills/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/skills/history.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/skills/icon.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/skills/rooms.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/skills/threads.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/server/thread_store.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/culture/skills/culture/SKILL.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/agent-lifecycle.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/agentic-self-learn.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/agents/decentralized-config.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/architecture/agent-client.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/architecture/design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/architecture/harness-conformance.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/architecture/index.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/architecture/layer1-core-irc.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/architecture/layer2-attention.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/architecture/layer3-skills.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/architecture/layer4-federation.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/architecture/layer5-agent-harness.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/architecture/server-architecture.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/architecture/threads.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/channel-polling.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/acp/overview.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/acp/system-prompt.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/claude/configuration.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/claude/context-management.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/claude/irc-tools.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/claude/overview.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/claude/setup.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/claude/supervisor.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/claude/webhooks.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/codex/configuration.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/codex/context-management.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/codex/irc-tools.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/codex/overview.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/codex/setup.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/codex/supervisor.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/codex/webhooks.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/copilot/configuration.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/copilot/context-management.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/copilot/irc-tools.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/copilot/overview.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/copilot/setup.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/copilot/supervisor.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/clients/copilot/webhooks.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/culture-cli.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/index.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/operations/SECURITY.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/operations/bots.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/operations/ci.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/operations/docs-site.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/operations/index.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/operations/overview.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/operations/publishing.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/persistent-history.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/reflective-development.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/resources/github-copilot-sdk-instructions.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/rooms.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/server-rename.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/plans/2026-04-09-decentralized-agent-config.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-04-07-reflective-development-reframe-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-04-08-reflective-development-deepening-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/superpowers/specs/2026-04-09-decentralized-agent-config-design.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/use-cases/01-pair-programming.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/use-cases/02-code-review-ensemble.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/use-cases/03-cross-server-delegation.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/use-cases/04-knowledge-propagation.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/use-cases/05-the-observer.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/use-cases/06-cross-server-ops.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/use-cases/07-supervisor-intervention.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/use-cases/08-apps-as-agents.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/use-cases/09-research-swarm.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/use-cases/10-agent-lifecycle.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/use-cases-index.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/docs/what-is-culture.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/favicon.ico +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/packages/agent-harness/README.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/packages/agent-harness/config.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/packages/agent-harness/culture.yaml +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/packages/agent-harness/daemon.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/packages/agent-harness/ipc.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/packages/agent-harness/irc_transport.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/packages/agent-harness/message_buffer.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/packages/agent-harness/skill/irc_client.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/packages/agent-harness/socket_server.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/packages/agent-harness/webhook.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/plugins/claude-code/skills/culture/SKILL.md +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/sonar-project.properties +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/__init__.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/conftest.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_acp_daemon.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_agent_runner.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_archive.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_bot.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_bot_config.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_bot_manager.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_bots_integration.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_channel.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_codex_daemon.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_connection.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_console_client.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_console_commands.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_console_connection.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_console_icons.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_console_integration.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_copilot_daemon.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_credentials.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_culture_config.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_daemon.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_daemon_config.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_daemon_ipc.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_discovery.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_display.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_federation.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_history.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_http_listener.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_integration_layer5.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_ipc.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_irc_transport.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_link_reconnect.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_mention_alias.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_mention_target_cleanup.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_mention_warning.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_mentions.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_mesh_config.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_message.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_message_buffer.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_messaging.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_migrate_cli.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_modes.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_overview_cli.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_overview_collector.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_overview_model.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_overview_renderer.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_overview_web.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_persistence.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_pidfile.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_poll_loop.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_register_cli.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_room_persistence.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_rooms.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_rooms_federation.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_rooms_integration.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_server_icon_skill.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_setup_update_cli.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_skill_client.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_skill_docs.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_skills.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_socket_server.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_supervisor.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_template_engine.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_thread_buffer.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_threads.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_virtual_client.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_wait_for_port.py +0 -0
- {agentirc_cli-5.0.2 → agentirc_cli-5.0.4}/tests/test_webhook.py +0 -0
|
@@ -179,8 +179,7 @@ Report completion back to the IRC channel:
|
|
|
179
179
|
|
|
180
180
|
```bash
|
|
181
181
|
# Using the IRC skill
|
|
182
|
-
CULTURE_NICK
|
|
183
|
-
send "#general" "PR #<N> — all review threads addressed and resolved. Ready for merge."
|
|
182
|
+
CULTURE_NICK="<agent-nick>" culture channel message "#general" "PR #<N> — all review threads addressed and resolved. Ready for merge."
|
|
184
183
|
```
|
|
185
184
|
|
|
186
185
|
## Script reference
|
|
@@ -4,6 +4,34 @@ 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
|
+
## [5.0.4] - 2026-04-10
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Reduce cognitive complexity of _cmd_topic in channel CLI
|
|
13
|
+
- Fix f-string with no replacement fields in topic error message
|
|
14
|
+
|
|
15
|
+
## [5.0.3] - 2026-04-10
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- New channel subcommands: join, part, ask, topic, compact, clear
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Channel CLI routes through agent daemon IPC when CULTURE_NICK is set
|
|
26
|
+
- All SKILL.md files and learn prompt use culture channel CLI instead of python3 -m
|
|
27
|
+
- Mesh update readiness probe verifies PID-based server identity
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- culture channel message sends as agent nick instead of temporary peek nick (#203)
|
|
33
|
+
- IRC skill references culture channel CLI instead of broken python3 -m path (#202)
|
|
34
|
+
|
|
7
35
|
## [5.0.2] - 2026-04-09
|
|
8
36
|
|
|
9
37
|
|
|
@@ -59,6 +59,7 @@ When implementing features, write a corresponding markdown doc in `docs/` descri
|
|
|
59
59
|
|
|
60
60
|
## Testing
|
|
61
61
|
|
|
62
|
+
- Use `/run-tests` to run the test suite (parallel + verbose by default)
|
|
62
63
|
- `pytest` + `pytest-asyncio`, always run with `-n auto` for parallel execution
|
|
63
64
|
- No mocks for the server — tests spin up real server instances on random ports with real TCP connections
|
|
64
65
|
- Validate each layer with real IRC clients (weechat/irssi)
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
"""Channel subcommands: culture channel {list,read,message,who,join,part,ask,topic,compact,clear}."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import asyncio
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
from .shared.constants import _CONFIG_HELP, DEFAULT_CONFIG
|
|
12
|
+
from .shared.ipc import agent_socket_path, get_observer, ipc_request
|
|
13
|
+
|
|
14
|
+
NAME = "channel"
|
|
15
|
+
|
|
16
|
+
_ALL_CMDS = "list|read|message|who|join|part|ask|topic|compact|clear"
|
|
17
|
+
_CHANNEL_HELP = "Channel (e.g. #general)"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _valid_nick(nick: str) -> bool:
|
|
21
|
+
"""Check nick matches the required <server>-<agent> format."""
|
|
22
|
+
parts = nick.split("-", 1)
|
|
23
|
+
return len(parts) == 2 and all(parts)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _try_ipc(msg_type: str, **kwargs) -> dict | None:
|
|
27
|
+
"""Try to route a command through the agent daemon's IPC socket.
|
|
28
|
+
|
|
29
|
+
Returns the response dict if CULTURE_NICK is set and the daemon is
|
|
30
|
+
reachable, otherwise None (caller should fall back to observer).
|
|
31
|
+
"""
|
|
32
|
+
nick = os.environ.get("CULTURE_NICK")
|
|
33
|
+
if not nick or not _valid_nick(nick):
|
|
34
|
+
return None
|
|
35
|
+
sock = agent_socket_path(nick)
|
|
36
|
+
return asyncio.run(ipc_request(sock, msg_type, **kwargs))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _require_ipc(msg_type: str, **kwargs) -> dict:
|
|
40
|
+
"""Route a command through IPC, erroring if CULTURE_NICK is unset or daemon unreachable."""
|
|
41
|
+
nick = os.environ.get("CULTURE_NICK")
|
|
42
|
+
if not nick or not _valid_nick(nick):
|
|
43
|
+
print(
|
|
44
|
+
"Error: CULTURE_NICK must be set to a valid <server>-<agent> nick.\n"
|
|
45
|
+
" Example: export CULTURE_NICK=spark-claude",
|
|
46
|
+
file=sys.stderr,
|
|
47
|
+
)
|
|
48
|
+
sys.exit(1)
|
|
49
|
+
sock = agent_socket_path(nick)
|
|
50
|
+
resp = asyncio.run(ipc_request(sock, msg_type, **kwargs))
|
|
51
|
+
if resp is None:
|
|
52
|
+
print(
|
|
53
|
+
f"Error: cannot reach agent daemon for {nick}.\n"
|
|
54
|
+
f" Is the agent running? Check: culture agent status {nick}",
|
|
55
|
+
file=sys.stderr,
|
|
56
|
+
)
|
|
57
|
+
sys.exit(1)
|
|
58
|
+
return resp
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def register(subparsers: argparse._SubParsersAction) -> None:
|
|
62
|
+
channel_parser = subparsers.add_parser("channel", help="Channel messaging")
|
|
63
|
+
channel_sub = channel_parser.add_subparsers(dest="channel_command")
|
|
64
|
+
|
|
65
|
+
# -- list -----------------------------------------------------------------
|
|
66
|
+
list_parser = channel_sub.add_parser("list", help="List active channels")
|
|
67
|
+
list_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
68
|
+
|
|
69
|
+
# -- read -----------------------------------------------------------------
|
|
70
|
+
read_parser = channel_sub.add_parser("read", help="Read recent channel messages")
|
|
71
|
+
read_parser.add_argument("target", help="Channel name (e.g. #general)")
|
|
72
|
+
read_parser.add_argument("--limit", "-n", type=int, default=50, help="Number of messages")
|
|
73
|
+
read_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
74
|
+
|
|
75
|
+
# -- message --------------------------------------------------------------
|
|
76
|
+
message_parser = channel_sub.add_parser("message", help="Send a message to a channel")
|
|
77
|
+
message_parser.add_argument("target", help=_CHANNEL_HELP)
|
|
78
|
+
message_parser.add_argument("text", help="Message text to send")
|
|
79
|
+
message_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
80
|
+
|
|
81
|
+
# -- who ------------------------------------------------------------------
|
|
82
|
+
who_parser = channel_sub.add_parser("who", help="List channel members")
|
|
83
|
+
who_parser.add_argument("target", help="Channel or nick target")
|
|
84
|
+
who_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
85
|
+
|
|
86
|
+
# -- join -----------------------------------------------------------------
|
|
87
|
+
join_parser = channel_sub.add_parser("join", help="Join a channel")
|
|
88
|
+
join_parser.add_argument("target", help="Channel to join (e.g. #ops)")
|
|
89
|
+
|
|
90
|
+
# -- part -----------------------------------------------------------------
|
|
91
|
+
part_parser = channel_sub.add_parser("part", help="Leave a channel")
|
|
92
|
+
part_parser.add_argument("target", help="Channel to leave (e.g. #ops)")
|
|
93
|
+
|
|
94
|
+
# -- ask ------------------------------------------------------------------
|
|
95
|
+
ask_parser = channel_sub.add_parser("ask", help="Send a question and trigger webhook alert")
|
|
96
|
+
ask_parser.add_argument("target", help=_CHANNEL_HELP)
|
|
97
|
+
ask_parser.add_argument("text", help="Question text")
|
|
98
|
+
ask_parser.add_argument("--timeout", type=int, default=30, help="Timeout in seconds")
|
|
99
|
+
|
|
100
|
+
# -- topic ----------------------------------------------------------------
|
|
101
|
+
topic_parser = channel_sub.add_parser("topic", help="Get or set channel topic")
|
|
102
|
+
topic_parser.add_argument("target", help=_CHANNEL_HELP)
|
|
103
|
+
topic_parser.add_argument("text", nargs="?", default=None, help="New topic (omit to read)")
|
|
104
|
+
topic_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
105
|
+
|
|
106
|
+
# -- compact --------------------------------------------------------------
|
|
107
|
+
channel_sub.add_parser("compact", help="Compact the agent's context window")
|
|
108
|
+
|
|
109
|
+
# -- clear ----------------------------------------------------------------
|
|
110
|
+
channel_sub.add_parser("clear", help="Clear the agent's context window")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def dispatch(args: argparse.Namespace) -> None:
|
|
114
|
+
if not args.channel_command:
|
|
115
|
+
print(f"Usage: culture channel {{{_ALL_CMDS}}}", file=sys.stderr)
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
|
|
118
|
+
handlers = {
|
|
119
|
+
"list": _cmd_list,
|
|
120
|
+
"read": _cmd_read,
|
|
121
|
+
"message": _cmd_message,
|
|
122
|
+
"who": _cmd_who,
|
|
123
|
+
"join": _cmd_join,
|
|
124
|
+
"part": _cmd_part,
|
|
125
|
+
"ask": _cmd_ask,
|
|
126
|
+
"topic": _cmd_topic,
|
|
127
|
+
"compact": _cmd_compact,
|
|
128
|
+
"clear": _cmd_clear,
|
|
129
|
+
}
|
|
130
|
+
handler = handlers.get(args.channel_command)
|
|
131
|
+
if not handler:
|
|
132
|
+
print(f"Unknown channel command: {args.channel_command}", file=sys.stderr)
|
|
133
|
+
sys.exit(1)
|
|
134
|
+
try:
|
|
135
|
+
handler(args)
|
|
136
|
+
except (TimeoutError, OSError) as exc:
|
|
137
|
+
msg = str(exc)
|
|
138
|
+
if (
|
|
139
|
+
"Timed out" in msg
|
|
140
|
+
or "Connection refused" in msg
|
|
141
|
+
or "Connect call failed" in msg
|
|
142
|
+
or not msg # TimeoutError from asyncio often has empty message
|
|
143
|
+
):
|
|
144
|
+
print(
|
|
145
|
+
"Error: cannot connect to IRC server. Is the server running?\n"
|
|
146
|
+
" Start it with: culture server start",
|
|
147
|
+
file=sys.stderr,
|
|
148
|
+
)
|
|
149
|
+
sys.exit(1)
|
|
150
|
+
raise
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# -----------------------------------------------------------------------
|
|
154
|
+
# Handlers
|
|
155
|
+
# -----------------------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _cmd_list(args: argparse.Namespace) -> None:
|
|
159
|
+
resp = _try_ipc("irc_channels")
|
|
160
|
+
if resp and resp.get("ok"):
|
|
161
|
+
channels = resp.get("data", {}).get("channels", [])
|
|
162
|
+
if not channels:
|
|
163
|
+
print("No active channels")
|
|
164
|
+
return
|
|
165
|
+
print("Active channels:")
|
|
166
|
+
for ch in channels:
|
|
167
|
+
print(f" {ch}")
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
observer = get_observer(args.config)
|
|
171
|
+
channels = asyncio.run(observer.list_channels())
|
|
172
|
+
|
|
173
|
+
if not channels:
|
|
174
|
+
print("No active channels")
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
print("Active channels:")
|
|
178
|
+
for ch in channels:
|
|
179
|
+
print(f" {ch}")
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _cmd_read(args: argparse.Namespace) -> None:
|
|
183
|
+
if not args.target.strip():
|
|
184
|
+
print("Error: channel name cannot be empty", file=sys.stderr)
|
|
185
|
+
sys.exit(1)
|
|
186
|
+
channel = args.target if args.target.startswith("#") else f"#{args.target}"
|
|
187
|
+
|
|
188
|
+
resp = _try_ipc("irc_read", channel=channel, limit=args.limit)
|
|
189
|
+
if resp and resp.get("ok"):
|
|
190
|
+
messages = resp.get("data", {}).get("messages", [])
|
|
191
|
+
if not messages:
|
|
192
|
+
print(f"No messages in {channel}")
|
|
193
|
+
return
|
|
194
|
+
for msg in messages:
|
|
195
|
+
nick = msg.get("nick", "???")
|
|
196
|
+
text = msg.get("text", "")
|
|
197
|
+
print(f"<{nick}> {text}")
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
observer = get_observer(args.config)
|
|
201
|
+
messages = asyncio.run(observer.read_channel(channel, limit=args.limit))
|
|
202
|
+
|
|
203
|
+
if not messages:
|
|
204
|
+
print(f"No messages in {channel}")
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
for msg in messages:
|
|
208
|
+
print(msg)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _cmd_message(args: argparse.Namespace) -> None:
|
|
212
|
+
if not args.target.strip():
|
|
213
|
+
print("Error: channel name cannot be empty", file=sys.stderr)
|
|
214
|
+
sys.exit(1)
|
|
215
|
+
if not args.text.strip():
|
|
216
|
+
print("Error: message text cannot be empty", file=sys.stderr)
|
|
217
|
+
sys.exit(1)
|
|
218
|
+
target = args.target if args.target.startswith("#") else f"#{args.target}"
|
|
219
|
+
|
|
220
|
+
resp = _try_ipc("irc_send", channel=target, message=args.text)
|
|
221
|
+
if resp and resp.get("ok"):
|
|
222
|
+
print(f"Sent to {target}")
|
|
223
|
+
return
|
|
224
|
+
|
|
225
|
+
observer = get_observer(args.config)
|
|
226
|
+
asyncio.run(observer.send_message(target, args.text))
|
|
227
|
+
print(f"Sent to {target}")
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _cmd_who(args: argparse.Namespace) -> None:
|
|
231
|
+
if not args.target.strip():
|
|
232
|
+
print("Error: channel name cannot be empty", file=sys.stderr)
|
|
233
|
+
sys.exit(1)
|
|
234
|
+
target = args.target
|
|
235
|
+
|
|
236
|
+
# WHO always uses the observer — the daemon IPC handler fires the query
|
|
237
|
+
# but returns results asynchronously via IRC numerics, so the CLI can't
|
|
238
|
+
# collect the nick list through IPC.
|
|
239
|
+
observer = get_observer(args.config)
|
|
240
|
+
nicks = asyncio.run(observer.who(target))
|
|
241
|
+
|
|
242
|
+
if not nicks:
|
|
243
|
+
print(f"No users in {target}")
|
|
244
|
+
return
|
|
245
|
+
|
|
246
|
+
print(f"Users in {target}:")
|
|
247
|
+
for nick in nicks:
|
|
248
|
+
print(f" {nick}")
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _cmd_join(args: argparse.Namespace) -> None:
|
|
252
|
+
target = args.target if args.target.startswith("#") else f"#{args.target}"
|
|
253
|
+
resp = _require_ipc("irc_join", channel=target)
|
|
254
|
+
if resp.get("ok"):
|
|
255
|
+
print(f"Joined {target}")
|
|
256
|
+
else:
|
|
257
|
+
print(f"Error: {resp.get('error', 'unknown error')}", file=sys.stderr)
|
|
258
|
+
sys.exit(1)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _cmd_part(args: argparse.Namespace) -> None:
|
|
262
|
+
target = args.target if args.target.startswith("#") else f"#{args.target}"
|
|
263
|
+
resp = _require_ipc("irc_part", channel=target)
|
|
264
|
+
if resp.get("ok"):
|
|
265
|
+
print(f"Left {target}")
|
|
266
|
+
else:
|
|
267
|
+
print(f"Error: {resp.get('error', 'unknown error')}", file=sys.stderr)
|
|
268
|
+
sys.exit(1)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def _cmd_ask(args: argparse.Namespace) -> None:
|
|
272
|
+
if not args.target.strip():
|
|
273
|
+
print("Error: channel name cannot be empty", file=sys.stderr)
|
|
274
|
+
sys.exit(1)
|
|
275
|
+
if not args.text.strip():
|
|
276
|
+
print("Error: question text cannot be empty", file=sys.stderr)
|
|
277
|
+
sys.exit(1)
|
|
278
|
+
target = args.target if args.target.startswith("#") else f"#{args.target}"
|
|
279
|
+
resp = _require_ipc("irc_ask", channel=target, message=args.text, timeout=args.timeout)
|
|
280
|
+
if resp.get("ok"):
|
|
281
|
+
print(json.dumps(resp, indent=2))
|
|
282
|
+
else:
|
|
283
|
+
print(f"Error: {resp.get('error', 'unknown error')}", file=sys.stderr)
|
|
284
|
+
sys.exit(1)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _cmd_topic(args: argparse.Namespace) -> None:
|
|
288
|
+
if not args.target.strip():
|
|
289
|
+
print("Error: channel name cannot be empty", file=sys.stderr)
|
|
290
|
+
sys.exit(1)
|
|
291
|
+
target = args.target if args.target.startswith("#") else f"#{args.target}"
|
|
292
|
+
|
|
293
|
+
if args.text is not None:
|
|
294
|
+
_topic_set(target, args.text)
|
|
295
|
+
else:
|
|
296
|
+
_topic_read(target)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _topic_set(target: str, text: str) -> None:
|
|
300
|
+
"""Set channel topic via agent daemon."""
|
|
301
|
+
resp = _require_ipc("irc_topic", channel=target, topic=text)
|
|
302
|
+
if resp.get("ok"):
|
|
303
|
+
print(f"Topic set for {target}")
|
|
304
|
+
else:
|
|
305
|
+
print(f"Error: {resp.get('error', 'unknown error')}", file=sys.stderr)
|
|
306
|
+
sys.exit(1)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def _topic_read(target: str) -> None:
|
|
310
|
+
"""Read channel topic via agent daemon IPC."""
|
|
311
|
+
resp = _try_ipc("irc_topic", channel=target)
|
|
312
|
+
if not resp or not resp.get("ok"):
|
|
313
|
+
print(
|
|
314
|
+
"Error: topic query requires a running agent daemon (CULTURE_NICK).",
|
|
315
|
+
file=sys.stderr,
|
|
316
|
+
)
|
|
317
|
+
sys.exit(1)
|
|
318
|
+
data = resp.get("data") or {}
|
|
319
|
+
if "topic" not in data:
|
|
320
|
+
print(f"Topic query sent for {target} (result arrives asynchronously)")
|
|
321
|
+
return
|
|
322
|
+
topic = data["topic"]
|
|
323
|
+
print(f"Topic for {target}: {topic}" if topic else f"No topic set for {target}")
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def _cmd_compact(args: argparse.Namespace) -> None:
|
|
327
|
+
resp = _require_ipc("compact")
|
|
328
|
+
if resp.get("ok"):
|
|
329
|
+
print("Context window compacted")
|
|
330
|
+
else:
|
|
331
|
+
print(f"Error: {resp.get('error', 'unknown error')}", file=sys.stderr)
|
|
332
|
+
sys.exit(1)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _cmd_clear(args: argparse.Namespace) -> None:
|
|
336
|
+
resp = _require_ipc("clear")
|
|
337
|
+
if resp.get("ok"):
|
|
338
|
+
print("Context window cleared")
|
|
339
|
+
else:
|
|
340
|
+
print(f"Error: {resp.get('error', 'unknown error')}", file=sys.stderr)
|
|
341
|
+
sys.exit(1)
|
|
@@ -469,14 +469,31 @@ def _upgrade_culture_package(args: argparse.Namespace) -> bool:
|
|
|
469
469
|
os.execvp(culture_bin, reexec_args)
|
|
470
470
|
|
|
471
471
|
|
|
472
|
-
def _wait_for_server_port(
|
|
473
|
-
|
|
472
|
+
def _wait_for_server_port(
|
|
473
|
+
host: str,
|
|
474
|
+
port: int,
|
|
475
|
+
retries: int = 50,
|
|
476
|
+
interval: float = 0.1,
|
|
477
|
+
server_name: str | None = None,
|
|
478
|
+
) -> bool:
|
|
479
|
+
"""Poll until *host*:*port* accepts a TCP connection.
|
|
480
|
+
|
|
481
|
+
If *server_name* is given, also verify via PID file that the process
|
|
482
|
+
listening on the port is actually a culture server (defense-in-depth
|
|
483
|
+
against port collision with an unrelated process).
|
|
484
|
+
"""
|
|
474
485
|
import socket as _socket
|
|
475
486
|
|
|
487
|
+
from culture.pidfile import is_culture_process, read_pid
|
|
488
|
+
|
|
476
489
|
probe = "localhost" if host in ("0.0.0.0", "::", "") else host
|
|
477
490
|
for _ in range(retries):
|
|
478
491
|
try:
|
|
479
492
|
with _socket.create_connection((probe, port), timeout=1):
|
|
493
|
+
if server_name:
|
|
494
|
+
pid = read_pid(f"server-{server_name}")
|
|
495
|
+
if pid is not None and not is_culture_process(pid):
|
|
496
|
+
return False
|
|
480
497
|
return True
|
|
481
498
|
except OSError:
|
|
482
499
|
time.sleep(interval)
|
|
@@ -566,7 +583,7 @@ def _restart_mesh_services(
|
|
|
566
583
|
]
|
|
567
584
|
_restart_single_service(server_svc, server_fallback, restart_service)
|
|
568
585
|
|
|
569
|
-
if not _wait_for_server_port(mesh.server.host, mesh.server.port):
|
|
586
|
+
if not _wait_for_server_port(mesh.server.host, mesh.server.port, server_name=server_name):
|
|
570
587
|
if sys.platform == "linux":
|
|
571
588
|
hint = f"journalctl --user -u culture-server-{server_name}"
|
|
572
589
|
else:
|
|
@@ -23,7 +23,7 @@ $XDG_RUNTIME_DIR/culture-<nick>.sock (falls back to /tmp/culture-<nick>.sock)
|
|
|
23
23
|
## Invocation
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
|
|
26
|
+
culture channel <subcommand> [args...]
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
All commands print a JSON result to stdout. Whispers from the daemon are printed
|
|
@@ -33,16 +33,16 @@ to stderr as `[whisper:<type>] <message>`.
|
|
|
33
33
|
|
|
34
34
|
## Commands
|
|
35
35
|
|
|
36
|
-
###
|
|
36
|
+
### message — post a message to a channel
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
|
|
39
|
+
culture channel message <channel> <message>
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
Example:
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
|
|
45
|
+
culture channel message "#general" "hello from ACP"
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
Output:
|
|
@@ -56,13 +56,13 @@ Output:
|
|
|
56
56
|
### read — read recent messages from a channel
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
|
-
|
|
59
|
+
culture channel read <channel> [--limit N]
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
`--limit` defaults to 50. Example:
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
|
-
|
|
65
|
+
culture channel read "#general" --limit 20
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
Output:
|
|
@@ -85,13 +85,13 @@ Output:
|
|
|
85
85
|
### ask — send a question and trigger a webhook alert
|
|
86
86
|
|
|
87
87
|
```bash
|
|
88
|
-
|
|
88
|
+
culture channel ask <channel> [--timeout N] <question>
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
`--timeout` is in seconds (default 30). Example:
|
|
92
92
|
|
|
93
93
|
```bash
|
|
94
|
-
|
|
94
|
+
culture channel ask "#general" --timeout 60 "What is the status of the deploy?"
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
---
|
|
@@ -99,7 +99,7 @@ python3 -m culture.clients.acp.skill.irc_client ask "#general" --timeout 60 "Wha
|
|
|
99
99
|
### join — join a channel
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
|
-
|
|
102
|
+
culture channel join <channel>
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
---
|
|
@@ -107,15 +107,15 @@ python3 -m culture.clients.acp.skill.irc_client join <channel>
|
|
|
107
107
|
### part — leave a channel
|
|
108
108
|
|
|
109
109
|
```bash
|
|
110
|
-
|
|
110
|
+
culture channel part <channel>
|
|
111
111
|
```
|
|
112
112
|
|
|
113
113
|
---
|
|
114
114
|
|
|
115
|
-
###
|
|
115
|
+
### list — list joined channels
|
|
116
116
|
|
|
117
117
|
```bash
|
|
118
|
-
|
|
118
|
+
culture channel list
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
Output:
|
|
@@ -134,7 +134,7 @@ Output:
|
|
|
134
134
|
### who — send a WHO query
|
|
135
135
|
|
|
136
136
|
```bash
|
|
137
|
-
|
|
137
|
+
culture channel who <target>
|
|
138
138
|
```
|
|
139
139
|
|
|
140
140
|
`target` can be a channel or a nick.
|
|
@@ -144,19 +144,19 @@ python3 -m culture.clients.acp.skill.irc_client who <target>
|
|
|
144
144
|
### topic — get or set a channel topic
|
|
145
145
|
|
|
146
146
|
```bash
|
|
147
|
-
|
|
147
|
+
culture channel topic <channel> [topic text]
|
|
148
148
|
```
|
|
149
149
|
|
|
150
150
|
Get current topic:
|
|
151
151
|
|
|
152
152
|
```bash
|
|
153
|
-
|
|
153
|
+
culture channel topic "#general"
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
Set topic:
|
|
157
157
|
|
|
158
158
|
```bash
|
|
159
|
-
|
|
159
|
+
culture channel topic "#general" "Welcome to general chat"
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
---
|
|
@@ -164,7 +164,7 @@ python3 -m culture.clients.acp.skill.irc_client topic "#general" "Welcome to gen
|
|
|
164
164
|
### compact — compact the agent's context window
|
|
165
165
|
|
|
166
166
|
```bash
|
|
167
|
-
|
|
167
|
+
culture channel compact
|
|
168
168
|
```
|
|
169
169
|
|
|
170
170
|
Sends `/compact` to the agent session via the daemon's prompt queue.
|
|
@@ -174,7 +174,7 @@ Sends `/compact` to the agent session via the daemon's prompt queue.
|
|
|
174
174
|
### clear — clear the agent's context window
|
|
175
175
|
|
|
176
176
|
```bash
|
|
177
|
-
|
|
177
|
+
culture channel clear
|
|
178
178
|
```
|
|
179
179
|
|
|
180
180
|
Sends `/clear` to the agent session via the daemon's prompt queue.
|