agentirc-cli 4.3.5__tar.gz → 4.3.6__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-4.3.5 → agentirc_cli-4.3.6}/CHANGELOG.md +25 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/PKG-INFO +1 -1
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/__init__.py +4 -1
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/cli/__init__.py +5 -3
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/cli/agent.py +25 -6
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/cli/bot.py +5 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/cli/channel.py +30 -3
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/cli/server.py +53 -7
- agentirc_cli-4.3.6/culture/cli/shared/formatting.py +5 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/acp/config.py +5 -5
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/claude/config.py +8 -8
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/codex/config.py +5 -5
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/copilot/config.py +5 -5
- agentirc_cli-4.3.6/culture/formatting.py +19 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/observer.py +7 -1
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/overview/renderer_text.py +1 -13
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/pyproject.toml +1 -1
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/uv.lock +1 -1
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/.claude/skills/pr-review/SKILL.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/.claude/skills/run-tests/SKILL.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/.flake8 +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/.github/workflows/pages.yml +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/.github/workflows/security-checks.yml +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/.gitignore +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/.markdownlint-cli2.yaml +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/.pr_agent.toml +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/.pre-commit-config.yaml +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/.pylintrc +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/CLAUDE.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/CNAME +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/Gemfile +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/Gemfile.lock +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/LICENSE +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/README.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/SECURITY.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/_config.yml +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/_sass/color_schemes/anthropic.scss +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/_sass/custom/custom.scss +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/__main__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/aio.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/bots/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/bots/bot.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/bots/bot_manager.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/bots/config.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/bots/http_listener.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/bots/template_engine.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/bots/virtual_client.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/cli/mesh.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/cli/shared/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/cli/shared/constants.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/cli/shared/display.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/cli/shared/ipc.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/cli/shared/mesh.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/cli/shared/process.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/cli/skills.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/acp/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/acp/agent_runner.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/acp/daemon.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/acp/ipc.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/acp/irc_transport.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/acp/message_buffer.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/acp/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/acp/skill/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/acp/skill/irc_client.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/acp/socket_server.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/acp/supervisor.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/acp/webhook.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/claude/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/claude/__main__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/claude/agent_runner.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/claude/daemon.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/claude/ipc.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/claude/irc_transport.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/claude/message_buffer.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/claude/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/claude/skill/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/claude/skill/irc_client.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/claude/socket_server.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/claude/supervisor.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/claude/webhook.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/codex/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/codex/agent_runner.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/codex/daemon.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/codex/ipc.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/codex/irc_transport.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/codex/message_buffer.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/codex/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/codex/skill/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/codex/skill/irc_client.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/codex/socket_server.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/codex/supervisor.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/codex/webhook.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/copilot/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/copilot/agent_runner.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/copilot/daemon.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/copilot/ipc.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/copilot/irc_transport.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/copilot/message_buffer.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/copilot/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/copilot/skill/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/copilot/skill/irc_client.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/copilot/socket_server.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/copilot/supervisor.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/clients/copilot/webhook.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/console/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/console/app.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/console/client.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/console/commands.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/console/widgets/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/console/widgets/chat.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/console/widgets/info_panel.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/console/widgets/sidebar.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/credentials.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/learn_prompt.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/mesh_config.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/overview/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/overview/collector.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/overview/model.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/overview/renderer_web.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/overview/web/style.css +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/persistence.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/pidfile.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/protocol/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/protocol/commands.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/protocol/extensions/federation.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/protocol/extensions/history.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/protocol/extensions/icons.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/protocol/extensions/rooms.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/protocol/extensions/tags.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/protocol/extensions/threads.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/protocol/message.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/protocol/protocol-index.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/protocol/replies.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/__main__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/channel.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/client.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/config.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/ircd.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/remote_client.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/room_store.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/rooms_util.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/server_link.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/skill.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/skills/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/skills/history.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/skills/icon.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/skills/rooms.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/skills/threads.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/server/thread_store.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/culture/skills/culture/SKILL.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/agent-lifecycle.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/agentic-self-learn.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/architecture/agent-client.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/architecture/agent-harness-spec.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/architecture/design.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/architecture/harness-conformance.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/architecture/index.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/architecture/layer1-core-irc.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/architecture/layer2-attention.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/architecture/layer3-skills.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/architecture/layer4-federation.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/architecture/layer5-agent-harness.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/architecture/server-architecture.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/architecture/threads.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/channel-polling.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/acp/overview.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/claude/configuration.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/claude/context-management.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/claude/irc-tools.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/claude/overview.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/claude/setup.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/claude/supervisor.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/claude/webhooks.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/codex/configuration.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/codex/context-management.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/codex/irc-tools.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/codex/overview.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/codex/setup.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/codex/supervisor.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/codex/webhooks.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/copilot/configuration.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/copilot/context-management.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/copilot/irc-tools.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/copilot/overview.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/copilot/setup.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/copilot/supervisor.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/clients/copilot/webhooks.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/culture-cli.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/getting-started.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/index.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/operations/SECURITY.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/operations/bots.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/operations/ci.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/operations/cli.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/operations/docs-site.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/operations/index.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/operations/ops-tooling.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/operations/overview.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/operations/publishing.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/resources/github-copilot-sdk-instructions.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/rooms.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/server-rename.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/use-cases/01-pair-programming.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/use-cases/02-code-review-ensemble.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/use-cases/03-cross-server-delegation.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/use-cases/04-knowledge-propagation.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/use-cases/05-the-observer.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/use-cases/06-cross-server-ops.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/use-cases/07-supervisor-intervention.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/use-cases/08-apps-as-agents.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/use-cases/09-research-swarm.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/use-cases/10-agent-lifecycle.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/use-cases-index.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/docs/what-is-culture.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/packages/agent-harness/README.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/packages/agent-harness/config.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/packages/agent-harness/daemon.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/packages/agent-harness/ipc.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/packages/agent-harness/irc_transport.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/packages/agent-harness/message_buffer.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/packages/agent-harness/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/packages/agent-harness/skill/irc_client.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/packages/agent-harness/socket_server.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/packages/agent-harness/webhook.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/plugins/claude-code/skills/culture/SKILL.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/plugins/claude-code/skills/irc/SKILL.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/sonar-project.properties +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/__init__.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/conftest.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_acp_daemon.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_agent_runner.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_archive.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_bot.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_bot_config.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_bot_manager.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_bots_integration.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_channel.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_codex_daemon.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_connection.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_console_client.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_console_commands.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_console_connection.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_console_icons.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_console_integration.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_copilot_daemon.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_daemon.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_daemon_config.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_daemon_ipc.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_discovery.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_federation.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_history.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_http_listener.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_integration_layer5.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_ipc.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_irc_transport.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_link_reconnect.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_mention_alias.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_mention_target_cleanup.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_mentions.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_mesh_config.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_message.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_message_buffer.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_messaging.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_modes.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_overview_cli.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_overview_collector.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_overview_model.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_overview_renderer.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_overview_web.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_persistence.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_pidfile.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_poll_loop.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_room_persistence.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_rooms.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_rooms_federation.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_rooms_integration.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_server_icon_skill.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_setup_update_cli.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_skill_client.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_skills.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_socket_server.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_supervisor.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_template_engine.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_thread_buffer.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_threads.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_virtual_client.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_wait_for_port.py +0 -0
- {agentirc_cli-4.3.5 → agentirc_cli-4.3.6}/tests/test_webhook.py +0 -0
|
@@ -4,6 +4,31 @@ 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
|
+
## [4.3.6] - 2026-04-07
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- CLI module docstring updated with current subcommand sets (#147)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- agent message silently succeeds for nonexistent targets (#132)
|
|
18
|
+
- channel message silently succeeds for nonexistent channels (#133)
|
|
19
|
+
- agent sleep/wake error messages use wrong command names (#134)
|
|
20
|
+
- server subcommands ignore default server, hardcode culture (#135)
|
|
21
|
+
- agent start/stop inconsistent behavior with no nick argument (#137)
|
|
22
|
+
- channel message and bot create accept empty strings (#138)
|
|
23
|
+
- bot archive/unarchive missing --config flag (#139)
|
|
24
|
+
- inconsistent error message casing in agent archive vs unarchive (#140)
|
|
25
|
+
- channel commands show confusing timeout error when server is down (#141)
|
|
26
|
+
- uncaught PackageNotFoundError in version fallback (#142)
|
|
27
|
+
- culture --version flag not supported (#143)
|
|
28
|
+
- agent/channel message silently succeeds for nonexistent or empty targets (#144)
|
|
29
|
+
- channel read displays raw Unix timestamps instead of human-readable format (#145)
|
|
30
|
+
- server default accepts nonexistent server names without validation (#146)
|
|
31
|
+
|
|
7
32
|
## [4.3.5] - 2026-04-07
|
|
8
33
|
|
|
9
34
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"""Unified CLI entry point for culture.
|
|
2
2
|
|
|
3
3
|
Commands are organized into noun-based groups:
|
|
4
|
-
culture agent {create,join,start,stop,status,rename,assign,sleep,wake,learn,message,read}
|
|
5
|
-
culture server {start,stop,status,default,rename}
|
|
4
|
+
culture agent {create,join,start,stop,status,rename,assign,sleep,wake,learn,message,read,archive,unarchive,delete}
|
|
5
|
+
culture server {start,stop,status,default,rename,archive,unarchive}
|
|
6
6
|
culture mesh {overview,setup,update,console}
|
|
7
7
|
culture channel {list,read,message,who}
|
|
8
|
-
culture bot {create,start,stop,list,inspect}
|
|
8
|
+
culture bot {create,start,stop,list,inspect,archive,unarchive}
|
|
9
9
|
culture skills {install}
|
|
10
10
|
"""
|
|
11
11
|
|
|
@@ -15,6 +15,7 @@ import argparse
|
|
|
15
15
|
import logging
|
|
16
16
|
import sys
|
|
17
17
|
|
|
18
|
+
from culture import __version__
|
|
18
19
|
from culture.cli import agent, bot, channel, mesh, server, skills
|
|
19
20
|
|
|
20
21
|
GROUPS = [agent, server, mesh, channel, bot, skills]
|
|
@@ -25,6 +26,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
25
26
|
prog="culture",
|
|
26
27
|
description="culture — AI agent IRC mesh",
|
|
27
28
|
)
|
|
29
|
+
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
28
30
|
sub = parser.add_subparsers(dest="command")
|
|
29
31
|
for group in GROUPS:
|
|
30
32
|
group.register(sub)
|
|
@@ -591,10 +591,17 @@ def _resolve_agents_to_stop(config, args) -> list:
|
|
|
591
591
|
if len(config.agents) == 0:
|
|
592
592
|
print(NO_AGENTS_MSG, file=sys.stderr)
|
|
593
593
|
sys.exit(1)
|
|
594
|
+
# Multiple agents: try to match by current working directory
|
|
595
|
+
cwd_real = os.path.realpath(os.getcwd())
|
|
596
|
+
cwd_matches = [a for a in config.agents if os.path.realpath(a.directory) == cwd_real]
|
|
597
|
+
if len(cwd_matches) == 1:
|
|
598
|
+
return cwd_matches
|
|
594
599
|
print(
|
|
595
600
|
"Multiple agents configured. Specify a nick or use --all.",
|
|
596
601
|
file=sys.stderr,
|
|
597
602
|
)
|
|
603
|
+
for a in config.agents:
|
|
604
|
+
print(f" {a.nick}", file=sys.stderr)
|
|
598
605
|
sys.exit(1)
|
|
599
606
|
|
|
600
607
|
|
|
@@ -763,13 +770,13 @@ def _cmd_assign(args: argparse.Namespace) -> None:
|
|
|
763
770
|
# -----------------------------------------------------------------------
|
|
764
771
|
|
|
765
772
|
|
|
766
|
-
def _resolve_ipc_targets(config, args,
|
|
773
|
+
def _resolve_ipc_targets(config, args, command_name: str) -> list:
|
|
767
774
|
"""Resolve which agents to send IPC messages to."""
|
|
768
775
|
if args.nick and args.all:
|
|
769
776
|
print("Cannot specify both nick and --all", file=sys.stderr)
|
|
770
777
|
sys.exit(1)
|
|
771
778
|
if not args.nick and not args.all:
|
|
772
|
-
print(f"Usage: culture agent {
|
|
779
|
+
print(f"Usage: culture agent {command_name} <nick> or --all", file=sys.stderr)
|
|
773
780
|
sys.exit(1)
|
|
774
781
|
if args.all:
|
|
775
782
|
return config.agents
|
|
@@ -790,20 +797,22 @@ def _send_ipc(agent, msg_type: str, action_verb: str) -> None:
|
|
|
790
797
|
print(f"{agent.nick}: failed (not running?)", file=sys.stderr)
|
|
791
798
|
|
|
792
799
|
|
|
793
|
-
def _ipc_to_agents(
|
|
800
|
+
def _ipc_to_agents(
|
|
801
|
+
args: argparse.Namespace, msg_type: str, action_verb: str, command_name: str
|
|
802
|
+
) -> None:
|
|
794
803
|
"""Send an IPC message (pause/resume) to one or all agents."""
|
|
795
804
|
config = load_config_or_default(args.config)
|
|
796
|
-
targets = _resolve_ipc_targets(config, args,
|
|
805
|
+
targets = _resolve_ipc_targets(config, args, command_name)
|
|
797
806
|
for agent in targets:
|
|
798
807
|
_send_ipc(agent, msg_type, action_verb)
|
|
799
808
|
|
|
800
809
|
|
|
801
810
|
def _cmd_sleep(args: argparse.Namespace) -> None:
|
|
802
|
-
_ipc_to_agents(args, "pause", "paused")
|
|
811
|
+
_ipc_to_agents(args, "pause", "paused", "sleep")
|
|
803
812
|
|
|
804
813
|
|
|
805
814
|
def _cmd_wake(args: argparse.Namespace) -> None:
|
|
806
|
-
_ipc_to_agents(args, "resume", "resumed")
|
|
815
|
+
_ipc_to_agents(args, "resume", "resumed", "wake")
|
|
807
816
|
|
|
808
817
|
|
|
809
818
|
def _cmd_learn(args: argparse.Namespace) -> None:
|
|
@@ -838,6 +847,16 @@ def _cmd_learn(args: argparse.Namespace) -> None:
|
|
|
838
847
|
|
|
839
848
|
|
|
840
849
|
def _cmd_message(args: argparse.Namespace) -> None:
|
|
850
|
+
if not args.target.strip():
|
|
851
|
+
print("Error: target nick cannot be empty", file=sys.stderr)
|
|
852
|
+
sys.exit(1)
|
|
853
|
+
if not args.text.strip():
|
|
854
|
+
print("Error: message text cannot be empty", file=sys.stderr)
|
|
855
|
+
sys.exit(1)
|
|
856
|
+
config = load_config_or_default(args.config)
|
|
857
|
+
if not config.get_agent(args.target):
|
|
858
|
+
print(f"Agent '{args.target}' not found in config", file=sys.stderr)
|
|
859
|
+
sys.exit(1)
|
|
841
860
|
observer = get_observer(args.config)
|
|
842
861
|
asyncio.run(observer.send_message(args.target, args.text))
|
|
843
862
|
print(f"Sent to {args.target}")
|
|
@@ -49,9 +49,11 @@ def register(subparsers: argparse._SubParsersAction) -> None:
|
|
|
49
49
|
bot_archive = bot_sub.add_parser("archive", help="Archive a bot")
|
|
50
50
|
bot_archive.add_argument("name", help="Bot name to archive")
|
|
51
51
|
bot_archive.add_argument("--reason", default="", help="Reason for archiving")
|
|
52
|
+
bot_archive.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
52
53
|
|
|
53
54
|
bot_unarchive = bot_sub.add_parser("unarchive", help="Restore an archived bot")
|
|
54
55
|
bot_unarchive.add_argument("name", help="Bot name to unarchive")
|
|
56
|
+
bot_unarchive.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
55
57
|
|
|
56
58
|
|
|
57
59
|
def dispatch(args: argparse.Namespace) -> None:
|
|
@@ -87,6 +89,9 @@ def dispatch(args: argparse.Namespace) -> None:
|
|
|
87
89
|
def _bot_create(args: argparse.Namespace) -> None:
|
|
88
90
|
from culture.bots.config import BOTS_DIR, BotConfig, save_bot_config
|
|
89
91
|
|
|
92
|
+
if not args.name.strip():
|
|
93
|
+
print("Error: bot name cannot be empty", file=sys.stderr)
|
|
94
|
+
sys.exit(1)
|
|
90
95
|
name = args.name
|
|
91
96
|
config = load_config_or_default(args.config)
|
|
92
97
|
server_name = config.server.name
|
|
@@ -50,11 +50,26 @@ def dispatch(args: argparse.Namespace) -> None:
|
|
|
50
50
|
"who": _cmd_who,
|
|
51
51
|
}
|
|
52
52
|
handler = handlers.get(args.channel_command)
|
|
53
|
-
if handler:
|
|
54
|
-
handler(args)
|
|
55
|
-
else:
|
|
53
|
+
if not handler:
|
|
56
54
|
print(f"Unknown channel command: {args.channel_command}", file=sys.stderr)
|
|
57
55
|
sys.exit(1)
|
|
56
|
+
try:
|
|
57
|
+
handler(args)
|
|
58
|
+
except (ConnectionError, ConnectionRefusedError, TimeoutError, OSError) as exc:
|
|
59
|
+
msg = str(exc)
|
|
60
|
+
if (
|
|
61
|
+
"Timed out" in msg
|
|
62
|
+
or "Connection refused" in msg
|
|
63
|
+
or "Connect call failed" in msg
|
|
64
|
+
or not msg # TimeoutError from asyncio often has empty message
|
|
65
|
+
):
|
|
66
|
+
print(
|
|
67
|
+
"Error: cannot connect to IRC server. Is the server running?\n"
|
|
68
|
+
" Start it with: culture server start",
|
|
69
|
+
file=sys.stderr,
|
|
70
|
+
)
|
|
71
|
+
sys.exit(1)
|
|
72
|
+
raise
|
|
58
73
|
|
|
59
74
|
|
|
60
75
|
# -----------------------------------------------------------------------
|
|
@@ -76,6 +91,9 @@ def _cmd_list(args: argparse.Namespace) -> None:
|
|
|
76
91
|
|
|
77
92
|
|
|
78
93
|
def _cmd_read(args: argparse.Namespace) -> None:
|
|
94
|
+
if not args.target.strip():
|
|
95
|
+
print("Error: channel name cannot be empty", file=sys.stderr)
|
|
96
|
+
sys.exit(1)
|
|
79
97
|
observer = get_observer(args.config)
|
|
80
98
|
channel = args.target if args.target.startswith("#") else f"#{args.target}"
|
|
81
99
|
messages = asyncio.run(observer.read_channel(channel, limit=args.limit))
|
|
@@ -89,6 +107,12 @@ def _cmd_read(args: argparse.Namespace) -> None:
|
|
|
89
107
|
|
|
90
108
|
|
|
91
109
|
def _cmd_message(args: argparse.Namespace) -> None:
|
|
110
|
+
if not args.target.strip():
|
|
111
|
+
print("Error: channel name cannot be empty", file=sys.stderr)
|
|
112
|
+
sys.exit(1)
|
|
113
|
+
if not args.text.strip():
|
|
114
|
+
print("Error: message text cannot be empty", file=sys.stderr)
|
|
115
|
+
sys.exit(1)
|
|
92
116
|
observer = get_observer(args.config)
|
|
93
117
|
target = args.target if args.target.startswith("#") else f"#{args.target}"
|
|
94
118
|
asyncio.run(observer.send_message(target, args.text))
|
|
@@ -96,6 +120,9 @@ def _cmd_message(args: argparse.Namespace) -> None:
|
|
|
96
120
|
|
|
97
121
|
|
|
98
122
|
def _cmd_who(args: argparse.Namespace) -> None:
|
|
123
|
+
if not args.target.strip():
|
|
124
|
+
print("Error: channel name cannot be empty", file=sys.stderr)
|
|
125
|
+
sys.exit(1)
|
|
99
126
|
observer = get_observer(args.config)
|
|
100
127
|
target = args.target
|
|
101
128
|
nicks = asyncio.run(observer.who(target))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Server subcommands: culture server {start,stop,status,default,rename}."""
|
|
1
|
+
"""Server subcommands: culture server {start,stop,status,default,rename,archive,unarchive}."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -14,6 +14,7 @@ import time
|
|
|
14
14
|
from culture.pidfile import (
|
|
15
15
|
is_culture_process,
|
|
16
16
|
is_process_alive,
|
|
17
|
+
read_default_server,
|
|
17
18
|
read_pid,
|
|
18
19
|
remove_pid,
|
|
19
20
|
write_pid,
|
|
@@ -32,13 +33,22 @@ logger = logging.getLogger("culture")
|
|
|
32
33
|
|
|
33
34
|
NAME = "server"
|
|
34
35
|
|
|
36
|
+
_DEFAULT_SERVER = "culture"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _resolve_server_name(args: argparse.Namespace) -> str:
|
|
40
|
+
"""Resolve the server name from args, default server file, or fallback."""
|
|
41
|
+
if args.name is not None:
|
|
42
|
+
return args.name
|
|
43
|
+
return read_default_server() or _DEFAULT_SERVER
|
|
44
|
+
|
|
35
45
|
|
|
36
46
|
def register(subparsers: argparse._SubParsersAction) -> None:
|
|
37
47
|
server_parser = subparsers.add_parser("server", help="Manage the IRC server")
|
|
38
48
|
server_sub = server_parser.add_subparsers(dest="server_command")
|
|
39
49
|
|
|
40
50
|
srv_start = server_sub.add_parser("start", help="Start the IRC server daemon")
|
|
41
|
-
srv_start.add_argument("--name", default=
|
|
51
|
+
srv_start.add_argument("--name", default=None, help=_SERVER_NAME_HELP)
|
|
42
52
|
srv_start.add_argument("--host", default="0.0.0.0", help="Listen address")
|
|
43
53
|
srv_start.add_argument("--port", type=int, default=6667, help="Listen port")
|
|
44
54
|
srv_start.add_argument(
|
|
@@ -66,10 +76,10 @@ def register(subparsers: argparse._SubParsersAction) -> None:
|
|
|
66
76
|
)
|
|
67
77
|
|
|
68
78
|
srv_stop = server_sub.add_parser("stop", help="Stop the IRC server daemon")
|
|
69
|
-
srv_stop.add_argument("--name", default=
|
|
79
|
+
srv_stop.add_argument("--name", default=None, help=_SERVER_NAME_HELP)
|
|
70
80
|
|
|
71
81
|
srv_status = server_sub.add_parser("status", help="Check server daemon status")
|
|
72
|
-
srv_status.add_argument("--name", default=
|
|
82
|
+
srv_status.add_argument("--name", default=None, help=_SERVER_NAME_HELP)
|
|
73
83
|
|
|
74
84
|
srv_default = server_sub.add_parser("default", help="Set default server")
|
|
75
85
|
srv_default.add_argument("name", help="Server name to set as default")
|
|
@@ -87,7 +97,7 @@ def register(subparsers: argparse._SubParsersAction) -> None:
|
|
|
87
97
|
srv_archive = server_sub.add_parser(
|
|
88
98
|
"archive", help="Archive the server and all its agents/bots"
|
|
89
99
|
)
|
|
90
|
-
srv_archive.add_argument("--name", default=
|
|
100
|
+
srv_archive.add_argument("--name", default=None, help=_SERVER_NAME_HELP)
|
|
91
101
|
srv_archive.add_argument("--reason", default="", help="Reason for archiving")
|
|
92
102
|
srv_archive.add_argument(
|
|
93
103
|
"--config",
|
|
@@ -98,7 +108,7 @@ def register(subparsers: argparse._SubParsersAction) -> None:
|
|
|
98
108
|
srv_unarchive = server_sub.add_parser(
|
|
99
109
|
"unarchive", help="Restore an archived server and all its agents/bots"
|
|
100
110
|
)
|
|
101
|
-
srv_unarchive.add_argument("--name", default=
|
|
111
|
+
srv_unarchive.add_argument("--name", default=None, help=_SERVER_NAME_HELP)
|
|
102
112
|
srv_unarchive.add_argument(
|
|
103
113
|
"--config",
|
|
104
114
|
default=DEFAULT_CONFIG,
|
|
@@ -114,6 +124,10 @@ def dispatch(args: argparse.Namespace) -> None:
|
|
|
114
124
|
)
|
|
115
125
|
sys.exit(1)
|
|
116
126
|
|
|
127
|
+
# Resolve --name for commands that use it (all except default/rename)
|
|
128
|
+
if args.server_command not in ("default", "rename") and hasattr(args, "name"):
|
|
129
|
+
args.name = _resolve_server_name(args)
|
|
130
|
+
|
|
117
131
|
if args.server_command == "start":
|
|
118
132
|
_server_start(args)
|
|
119
133
|
elif args.server_command == "stop":
|
|
@@ -121,8 +135,40 @@ def dispatch(args: argparse.Namespace) -> None:
|
|
|
121
135
|
elif args.server_command == "status":
|
|
122
136
|
_server_status(args)
|
|
123
137
|
elif args.server_command == "default":
|
|
124
|
-
from
|
|
138
|
+
from pathlib import Path
|
|
139
|
+
|
|
140
|
+
from culture.pidfile import PID_DIR, list_servers, write_default_server
|
|
125
141
|
|
|
142
|
+
from .shared.constants import DEFAULT_CONFIG
|
|
143
|
+
|
|
144
|
+
# Accept the name if: it matches a running server, has a PID file,
|
|
145
|
+
# or matches the configured server name.
|
|
146
|
+
known_running = {s["name"] for s in list_servers()}
|
|
147
|
+
pid_dir = Path(PID_DIR)
|
|
148
|
+
known_pids = set()
|
|
149
|
+
if pid_dir.exists():
|
|
150
|
+
prefix = "server-"
|
|
151
|
+
for p in pid_dir.glob(f"{prefix}*.pid"):
|
|
152
|
+
known_pids.add(p.stem[len(prefix) :])
|
|
153
|
+
known_names = known_running | known_pids
|
|
154
|
+
|
|
155
|
+
# Also accept the configured server name
|
|
156
|
+
try:
|
|
157
|
+
from culture.clients.claude.config import load_config_or_default
|
|
158
|
+
|
|
159
|
+
config = load_config_or_default(DEFAULT_CONFIG)
|
|
160
|
+
known_names.add(config.server.name)
|
|
161
|
+
except Exception:
|
|
162
|
+
pass
|
|
163
|
+
|
|
164
|
+
if args.name not in known_names:
|
|
165
|
+
print(f"Server '{args.name}' not found.", file=sys.stderr)
|
|
166
|
+
if known_names:
|
|
167
|
+
print(
|
|
168
|
+
f"Known servers: {', '.join(sorted(known_names))}",
|
|
169
|
+
file=sys.stderr,
|
|
170
|
+
)
|
|
171
|
+
sys.exit(1)
|
|
126
172
|
write_default_server(args.name)
|
|
127
173
|
print(f"Default server set to '{args.name}'")
|
|
128
174
|
elif args.server_command == "rename":
|
|
@@ -175,7 +175,7 @@ def add_agent_to_config(
|
|
|
175
175
|
# Check for nick collision
|
|
176
176
|
for existing in config.agents:
|
|
177
177
|
if existing.nick == agent.nick:
|
|
178
|
-
raise ValueError(f"
|
|
178
|
+
raise ValueError(f"Agent with nick {agent.nick!r} already exists in config")
|
|
179
179
|
|
|
180
180
|
config.agents.append(agent)
|
|
181
181
|
save_config(path, config)
|
|
@@ -238,7 +238,7 @@ def rename_agent(
|
|
|
238
238
|
# Check new nick doesn't collide
|
|
239
239
|
for agent in config.agents:
|
|
240
240
|
if agent.nick == new_nick:
|
|
241
|
-
raise ValueError(f"
|
|
241
|
+
raise ValueError(f"Agent with nick {new_nick!r} already exists in config")
|
|
242
242
|
|
|
243
243
|
# Find and rename
|
|
244
244
|
for agent in config.agents:
|
|
@@ -247,7 +247,7 @@ def rename_agent(
|
|
|
247
247
|
save_config(path, config)
|
|
248
248
|
return
|
|
249
249
|
|
|
250
|
-
raise ValueError(f"
|
|
250
|
+
raise ValueError(f"Agent {old_nick!r} not found in config")
|
|
251
251
|
|
|
252
252
|
|
|
253
253
|
def remove_agent(
|
|
@@ -262,7 +262,7 @@ def remove_agent(
|
|
|
262
262
|
"""
|
|
263
263
|
path = Path(path)
|
|
264
264
|
if not path.exists():
|
|
265
|
-
raise ValueError(f"
|
|
265
|
+
raise ValueError(f"Agent {nick!r} not found in config")
|
|
266
266
|
|
|
267
267
|
with open(path) as f:
|
|
268
268
|
raw = yaml.safe_load(f) or {}
|
|
@@ -274,4 +274,4 @@ def remove_agent(
|
|
|
274
274
|
with open(path, "w") as f:
|
|
275
275
|
yaml.dump(raw, f, default_flow_style=False, sort_keys=False)
|
|
276
276
|
return
|
|
277
|
-
raise ValueError(f"
|
|
277
|
+
raise ValueError(f"Agent {nick!r} not found in config")
|
|
@@ -185,7 +185,7 @@ def add_agent_to_config(
|
|
|
185
185
|
# Check for nick collision
|
|
186
186
|
for existing in config.agents:
|
|
187
187
|
if existing.nick == agent.nick:
|
|
188
|
-
raise ValueError(f"
|
|
188
|
+
raise ValueError(f"Agent with nick {agent.nick!r} already exists in config")
|
|
189
189
|
|
|
190
190
|
config.agents.append(agent)
|
|
191
191
|
save_config(path, config)
|
|
@@ -248,7 +248,7 @@ def rename_agent(
|
|
|
248
248
|
# Check new nick doesn't collide
|
|
249
249
|
for agent in config.agents:
|
|
250
250
|
if agent.nick == new_nick:
|
|
251
|
-
raise ValueError(f"
|
|
251
|
+
raise ValueError(f"Agent with nick {new_nick!r} already exists in config")
|
|
252
252
|
|
|
253
253
|
# Find and rename
|
|
254
254
|
for agent in config.agents:
|
|
@@ -257,7 +257,7 @@ def rename_agent(
|
|
|
257
257
|
save_config(path, config)
|
|
258
258
|
return
|
|
259
259
|
|
|
260
|
-
raise ValueError(f"
|
|
260
|
+
raise ValueError(f"Agent {old_nick!r} not found in config")
|
|
261
261
|
|
|
262
262
|
|
|
263
263
|
def archive_agent(
|
|
@@ -279,7 +279,7 @@ def archive_agent(
|
|
|
279
279
|
agent.archived_reason = reason
|
|
280
280
|
save_config(path, config)
|
|
281
281
|
return
|
|
282
|
-
raise ValueError(f"
|
|
282
|
+
raise ValueError(f"Agent {nick!r} not found in config")
|
|
283
283
|
|
|
284
284
|
|
|
285
285
|
def unarchive_agent(
|
|
@@ -294,13 +294,13 @@ def unarchive_agent(
|
|
|
294
294
|
for agent in config.agents:
|
|
295
295
|
if agent.nick == nick:
|
|
296
296
|
if not agent.archived:
|
|
297
|
-
raise ValueError(f"
|
|
297
|
+
raise ValueError(f"Agent {nick!r} is not archived")
|
|
298
298
|
agent.archived = False
|
|
299
299
|
agent.archived_at = ""
|
|
300
300
|
agent.archived_reason = ""
|
|
301
301
|
save_config(path, config)
|
|
302
302
|
return
|
|
303
|
-
raise ValueError(f"
|
|
303
|
+
raise ValueError(f"Agent {nick!r} not found in config")
|
|
304
304
|
|
|
305
305
|
|
|
306
306
|
def remove_agent(
|
|
@@ -315,7 +315,7 @@ def remove_agent(
|
|
|
315
315
|
"""
|
|
316
316
|
path = Path(path)
|
|
317
317
|
if not path.exists():
|
|
318
|
-
raise ValueError(f"
|
|
318
|
+
raise ValueError(f"Agent {nick!r} not found in config")
|
|
319
319
|
|
|
320
320
|
with open(path) as f:
|
|
321
321
|
raw = yaml.safe_load(f) or {}
|
|
@@ -327,7 +327,7 @@ def remove_agent(
|
|
|
327
327
|
with open(path, "w") as f:
|
|
328
328
|
yaml.dump(raw, f, default_flow_style=False, sort_keys=False)
|
|
329
329
|
return
|
|
330
|
-
raise ValueError(f"
|
|
330
|
+
raise ValueError(f"Agent {nick!r} not found in config")
|
|
331
331
|
|
|
332
332
|
|
|
333
333
|
def archive_server(
|
|
@@ -175,7 +175,7 @@ def add_agent_to_config(
|
|
|
175
175
|
# Check for nick collision
|
|
176
176
|
for existing in config.agents:
|
|
177
177
|
if existing.nick == agent.nick:
|
|
178
|
-
raise ValueError(f"
|
|
178
|
+
raise ValueError(f"Agent with nick {agent.nick!r} already exists in config")
|
|
179
179
|
|
|
180
180
|
config.agents.append(agent)
|
|
181
181
|
save_config(path, config)
|
|
@@ -238,7 +238,7 @@ def rename_agent(
|
|
|
238
238
|
# Check new nick doesn't collide
|
|
239
239
|
for agent in config.agents:
|
|
240
240
|
if agent.nick == new_nick:
|
|
241
|
-
raise ValueError(f"
|
|
241
|
+
raise ValueError(f"Agent with nick {new_nick!r} already exists in config")
|
|
242
242
|
|
|
243
243
|
# Find and rename
|
|
244
244
|
for agent in config.agents:
|
|
@@ -247,7 +247,7 @@ def rename_agent(
|
|
|
247
247
|
save_config(path, config)
|
|
248
248
|
return
|
|
249
249
|
|
|
250
|
-
raise ValueError(f"
|
|
250
|
+
raise ValueError(f"Agent {old_nick!r} not found in config")
|
|
251
251
|
|
|
252
252
|
|
|
253
253
|
def remove_agent(
|
|
@@ -262,7 +262,7 @@ def remove_agent(
|
|
|
262
262
|
"""
|
|
263
263
|
path = Path(path)
|
|
264
264
|
if not path.exists():
|
|
265
|
-
raise ValueError(f"
|
|
265
|
+
raise ValueError(f"Agent {nick!r} not found in config")
|
|
266
266
|
|
|
267
267
|
with open(path) as f:
|
|
268
268
|
raw = yaml.safe_load(f) or {}
|
|
@@ -274,4 +274,4 @@ def remove_agent(
|
|
|
274
274
|
with open(path, "w") as f:
|
|
275
275
|
yaml.dump(raw, f, default_flow_style=False, sort_keys=False)
|
|
276
276
|
return
|
|
277
|
-
raise ValueError(f"
|
|
277
|
+
raise ValueError(f"Agent {nick!r} not found in config")
|
|
@@ -175,7 +175,7 @@ def add_agent_to_config(
|
|
|
175
175
|
# Check for nick collision
|
|
176
176
|
for existing in config.agents:
|
|
177
177
|
if existing.nick == agent.nick:
|
|
178
|
-
raise ValueError(f"
|
|
178
|
+
raise ValueError(f"Agent with nick {agent.nick!r} already exists in config")
|
|
179
179
|
|
|
180
180
|
config.agents.append(agent)
|
|
181
181
|
save_config(path, config)
|
|
@@ -238,7 +238,7 @@ def rename_agent(
|
|
|
238
238
|
# Check new nick doesn't collide
|
|
239
239
|
for agent in config.agents:
|
|
240
240
|
if agent.nick == new_nick:
|
|
241
|
-
raise ValueError(f"
|
|
241
|
+
raise ValueError(f"Agent with nick {new_nick!r} already exists in config")
|
|
242
242
|
|
|
243
243
|
# Find and rename
|
|
244
244
|
for agent in config.agents:
|
|
@@ -247,7 +247,7 @@ def rename_agent(
|
|
|
247
247
|
save_config(path, config)
|
|
248
248
|
return
|
|
249
249
|
|
|
250
|
-
raise ValueError(f"
|
|
250
|
+
raise ValueError(f"Agent {old_nick!r} not found in config")
|
|
251
251
|
|
|
252
252
|
|
|
253
253
|
def remove_agent(
|
|
@@ -262,7 +262,7 @@ def remove_agent(
|
|
|
262
262
|
"""
|
|
263
263
|
path = Path(path)
|
|
264
264
|
if not path.exists():
|
|
265
|
-
raise ValueError(f"
|
|
265
|
+
raise ValueError(f"Agent {nick!r} not found in config")
|
|
266
266
|
|
|
267
267
|
with open(path) as f:
|
|
268
268
|
raw = yaml.safe_load(f) or {}
|
|
@@ -274,4 +274,4 @@ def remove_agent(
|
|
|
274
274
|
with open(path, "w") as f:
|
|
275
275
|
yaml.dump(raw, f, default_flow_style=False, sort_keys=False)
|
|
276
276
|
return
|
|
277
|
-
raise ValueError(f"
|
|
277
|
+
raise ValueError(f"Agent {nick!r} not found in config")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Shared formatting utilities used across culture modules."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def relative_time(timestamp: float) -> str:
|
|
9
|
+
"""Format a Unix timestamp as relative time (e.g., '2m ago', '1h ago')."""
|
|
10
|
+
delta = int(time.time() - timestamp)
|
|
11
|
+
if delta < 0:
|
|
12
|
+
return "just now"
|
|
13
|
+
if delta < 60:
|
|
14
|
+
return f"{delta}s ago"
|
|
15
|
+
if delta < 3600:
|
|
16
|
+
return f"{delta // 60}m ago"
|
|
17
|
+
if delta < 86400:
|
|
18
|
+
return f"{delta // 3600}h ago"
|
|
19
|
+
return f"{delta // 86400}d ago"
|
|
@@ -167,11 +167,17 @@ class IRCObserver:
|
|
|
167
167
|
|
|
168
168
|
@staticmethod
|
|
169
169
|
def _parse_history_line(msg):
|
|
170
|
+
from culture.formatting import relative_time
|
|
171
|
+
|
|
170
172
|
if msg.command != "HISTORY":
|
|
171
173
|
return None
|
|
172
174
|
if len(msg.params) >= 4:
|
|
173
175
|
entry_nick, ts, text = msg.params[1], msg.params[2], msg.params[3]
|
|
174
|
-
|
|
176
|
+
try:
|
|
177
|
+
label = relative_time(float(ts))
|
|
178
|
+
except (ValueError, TypeError):
|
|
179
|
+
label = ts
|
|
180
|
+
return f"[{label}] <{entry_nick}> {text}"
|
|
175
181
|
if len(msg.params) >= 3:
|
|
176
182
|
return f"<{msg.params[1]}> {msg.params[2]}"
|
|
177
183
|
return None
|
|
@@ -2,23 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
from culture.formatting import relative_time as _relative_time
|
|
6
6
|
|
|
7
7
|
from .model import Agent, MeshState, Message, Room
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def _relative_time(timestamp: float) -> str:
|
|
11
|
-
"""Format a timestamp as relative time (e.g., '2m ago', '1h ago')."""
|
|
12
|
-
delta = int(time.time() - timestamp)
|
|
13
|
-
if delta < 60:
|
|
14
|
-
return f"{delta}s ago"
|
|
15
|
-
if delta < 3600:
|
|
16
|
-
return f"{delta // 60}m ago"
|
|
17
|
-
if delta < 86400:
|
|
18
|
-
return f"{delta // 3600}h ago"
|
|
19
|
-
return f"{delta // 86400}d ago"
|
|
20
|
-
|
|
21
|
-
|
|
22
10
|
def _escape_cell(text: str) -> str:
|
|
23
11
|
"""Escape pipe and newline characters for markdown table cells."""
|
|
24
12
|
return text.replace("|", "\\|").replace("\n", " ")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|