agentirc-cli 3.0.1__tar.gz → 3.1.0__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-3.0.1 → agentirc_cli-3.1.0}/CHANGELOG.md +17 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/CLAUDE.md +1 -1
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/PKG-INFO +1 -1
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/cli.py +260 -27
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/config.py +68 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/config.py +68 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/config.py +68 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/config.py +68 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/pidfile.py +20 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/ircd.py +20 -6
- agentirc_cli-3.1.0/docs/server-rename.md +87 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/config.py +68 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/pyproject.toml +1 -1
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_daemon_config.py +273 -0
- agentirc_cli-3.1.0/tests/test_wait_for_port.py +89 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/uv.lock +1 -1
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.claude/skills/pr-review/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.claude/skills/run-tests/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.flake8 +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.github/workflows/pages.yml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.github/workflows/security-checks.yml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.gitignore +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.markdownlint-cli2.yaml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.pr_agent.toml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.pre-commit-config.yaml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/.pylintrc +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/CNAME +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/Gemfile +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/Gemfile.lock +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/LICENSE +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/README.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/SECURITY.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/_config.yml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/_sass/color_schemes/anthropic.scss +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/_sass/custom/custom.scss +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/__main__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/bots/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/bots/bot.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/bots/bot_manager.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/bots/config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/bots/http_listener.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/bots/template_engine.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/bots/virtual_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/agent_runner.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/ipc.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/irc_transport.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/message_buffer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/skill/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/skill/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/skill/irc_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/socket_server.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/supervisor.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/acp/webhook.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/__main__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/agent_runner.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/ipc.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/irc_transport.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/message_buffer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/skill/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/skill/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/skill/irc_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/socket_server.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/supervisor.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/claude/webhook.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/agent_runner.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/ipc.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/irc_transport.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/message_buffer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/skill/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/skill/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/skill/irc_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/socket_server.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/supervisor.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/codex/webhook.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/agent_runner.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/ipc.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/irc_transport.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/message_buffer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/skill/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/skill/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/skill/irc_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/socket_server.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/supervisor.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/clients/copilot/webhook.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/app.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/commands.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/widgets/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/widgets/chat.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/widgets/info_panel.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/console/widgets/sidebar.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/credentials.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/learn_prompt.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/mesh_config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/observer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/overview/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/overview/collector.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/overview/model.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/overview/renderer_text.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/overview/renderer_web.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/overview/web/style.css +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/persistence.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/commands.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/extensions/federation.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/extensions/history.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/extensions/icons.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/extensions/rooms.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/extensions/tags.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/extensions/threads.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/message.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/protocol-index.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/protocol/replies.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/__main__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/channel.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/remote_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/room_store.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/rooms_util.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/server_link.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/skill.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/skills/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/skills/history.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/skills/icon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/skills/rooms.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/skills/threads.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/server/thread_store.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/culture/skills/culture/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/agent-lifecycle.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/agentic-self-learn.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/agent-client.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/agent-harness-spec.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/harness-conformance.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/index.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/layer1-core-irc.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/layer2-attention.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/layer3-skills.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/layer4-federation.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/layer5-agent-harness.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/server-architecture.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/architecture/threads.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/acp/overview.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/claude/configuration.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/claude/context-management.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/claude/irc-tools.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/claude/overview.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/claude/setup.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/claude/supervisor.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/claude/webhooks.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/codex/configuration.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/codex/context-management.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/codex/irc-tools.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/codex/overview.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/codex/setup.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/codex/supervisor.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/codex/webhooks.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/copilot/configuration.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/copilot/context-management.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/copilot/irc-tools.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/copilot/overview.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/copilot/setup.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/copilot/supervisor.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/clients/copilot/webhooks.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/culture-cli.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/getting-started.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/index.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/SECURITY.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/bots.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/ci.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/cli.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/docs-site.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/index.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/ops-tooling.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/overview.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/operations/publishing.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/rooms.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/01-pair-programming.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/02-code-review-ensemble.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/03-cross-server-delegation.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/04-knowledge-propagation.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/05-the-observer.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/06-cross-server-ops.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/07-supervisor-intervention.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/08-apps-as-agents.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/09-research-swarm.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases/10-agent-lifecycle.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/use-cases-index.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/docs/what-is-culture.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/README.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/ipc.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/irc_transport.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/message_buffer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/skill/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/skill/irc_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/socket_server.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/packages/agent-harness/webhook.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/plugins/claude-code/skills/culture/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/sonar-project.properties +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/conftest.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_acp_daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_agent_runner.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_bot.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_bot_config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_bot_manager.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_bots_integration.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_channel.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_codex_daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_connection.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_console_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_console_commands.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_console_connection.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_console_icons.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_console_integration.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_copilot_daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_daemon_ipc.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_discovery.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_federation.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_history.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_http_listener.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_integration_layer5.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_ipc.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_irc_transport.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_link_reconnect.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_mentions.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_mesh_config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_message.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_message_buffer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_messaging.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_modes.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_overview_cli.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_overview_collector.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_overview_model.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_overview_renderer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_overview_web.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_persistence.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_pidfile.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_room_persistence.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_rooms.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_rooms_federation.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_rooms_integration.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_server_icon_skill.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_setup_update_cli.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_skill_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_skills.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_socket_server.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_supervisor.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_template_engine.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_thread_buffer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_threads.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_virtual_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.1.0}/tests/test_webhook.py +0 -0
|
@@ -4,6 +4,23 @@ 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
|
+
## [3.1.0] - 2026-04-06
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- culture server rename — rename server and all its agent nick prefixes
|
|
13
|
+
- culture rename — rename an agent suffix within the same server
|
|
14
|
+
- culture assign — move an agent to a different server
|
|
15
|
+
|
|
16
|
+
## [3.0.2] - 2026-04-06
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Server startup readiness — culture server start now waits for port to accept connections before returning
|
|
22
|
+
- Added startup phase logging to server log for diagnosing slow starts
|
|
23
|
+
|
|
7
24
|
## [3.0.1] - 2026-04-06
|
|
8
25
|
|
|
9
26
|
|
|
@@ -42,7 +42,7 @@ When implementing features, write a corresponding markdown doc in `docs/` descri
|
|
|
42
42
|
|
|
43
43
|
## Testing
|
|
44
44
|
|
|
45
|
-
- `pytest` + `pytest-asyncio`
|
|
45
|
+
- `pytest` + `pytest-asyncio`, always run with `-n auto` for parallel execution
|
|
46
46
|
- No mocks for the server — tests spin up real server instances on random ports with real TCP connections
|
|
47
47
|
- Validate each layer with real IRC clients (weechat/irssi)
|
|
48
48
|
|
|
@@ -29,6 +29,7 @@ import logging
|
|
|
29
29
|
import os
|
|
30
30
|
import shutil
|
|
31
31
|
import signal
|
|
32
|
+
import socket
|
|
32
33
|
import subprocess
|
|
33
34
|
import sys
|
|
34
35
|
import time
|
|
@@ -79,6 +80,7 @@ def _parse_link(value: str):
|
|
|
79
80
|
|
|
80
81
|
|
|
81
82
|
DEFAULT_CONFIG = os.path.expanduser("~/.culture/agents.yaml")
|
|
83
|
+
_CONFIG_HELP = "Config file path"
|
|
82
84
|
LOG_DIR = os.path.expanduser("~/.culture/logs")
|
|
83
85
|
|
|
84
86
|
|
|
@@ -135,6 +137,12 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
135
137
|
srv_default = server_sub.add_parser("default", help="Set default server")
|
|
136
138
|
srv_default.add_argument("name", help="Server name to set as default")
|
|
137
139
|
|
|
140
|
+
srv_rename = server_sub.add_parser(
|
|
141
|
+
"rename", help="Rename the server (updates config and agent nicks)"
|
|
142
|
+
)
|
|
143
|
+
srv_rename.add_argument("new_name", help="New server name")
|
|
144
|
+
srv_rename.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
145
|
+
|
|
138
146
|
# -- create / join subcommands -----------------------------------------
|
|
139
147
|
# 'create' registers an agent definition; 'join' adds it to the mesh.
|
|
140
148
|
# 'init' is a deprecated alias for 'create'.
|
|
@@ -168,11 +176,23 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
168
176
|
for flag, kwargs in _agent_args:
|
|
169
177
|
init_parser.add_argument(flag, **kwargs)
|
|
170
178
|
|
|
179
|
+
# -- rename subcommand -------------------------------------------------
|
|
180
|
+
rename_parser = sub.add_parser("rename", help="Rename an agent (same server)")
|
|
181
|
+
rename_parser.add_argument("nick", help="Current agent nick (e.g. spark-culture)")
|
|
182
|
+
rename_parser.add_argument("new_name", help="New agent name suffix (e.g. claude)")
|
|
183
|
+
rename_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
184
|
+
|
|
185
|
+
# -- assign subcommand -------------------------------------------------
|
|
186
|
+
assign_parser = sub.add_parser("assign", help="Move an agent to a different server")
|
|
187
|
+
assign_parser.add_argument("nick", help="Current agent nick (e.g. culture-culture)")
|
|
188
|
+
assign_parser.add_argument("server", help="Target server name (e.g. spark)")
|
|
189
|
+
assign_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
190
|
+
|
|
171
191
|
# -- start subcommand --------------------------------------------------
|
|
172
192
|
start_parser = sub.add_parser("start", help="Start agent daemon(s)")
|
|
173
193
|
start_parser.add_argument("nick", nargs="?", help="Agent nick to start")
|
|
174
194
|
start_parser.add_argument("--all", action="store_true", help="Start all agents")
|
|
175
|
-
start_parser.add_argument("--config", default=DEFAULT_CONFIG, help=
|
|
195
|
+
start_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
176
196
|
start_parser.add_argument(
|
|
177
197
|
"--foreground",
|
|
178
198
|
action="store_true",
|
|
@@ -183,7 +203,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
183
203
|
stop_parser = sub.add_parser("stop", help="Stop agent daemon(s)")
|
|
184
204
|
stop_parser.add_argument("nick", nargs="?", help="Agent nick to stop")
|
|
185
205
|
stop_parser.add_argument("--all", action="store_true", help="Stop all agents")
|
|
186
|
-
stop_parser.add_argument("--config", default=DEFAULT_CONFIG, help=
|
|
206
|
+
stop_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
187
207
|
|
|
188
208
|
# -- status subcommand -------------------------------------------------
|
|
189
209
|
status_parser = sub.add_parser("status", help="List running agents")
|
|
@@ -191,45 +211,45 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
191
211
|
status_parser.add_argument(
|
|
192
212
|
"--full", action="store_true", help="Query agents for activity status"
|
|
193
213
|
)
|
|
194
|
-
status_parser.add_argument("--config", default=DEFAULT_CONFIG, help=
|
|
214
|
+
status_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
195
215
|
|
|
196
216
|
# -- read subcommand ---------------------------------------------------
|
|
197
217
|
read_parser = sub.add_parser("read", help="Read recent channel messages")
|
|
198
218
|
read_parser.add_argument("channel", help="Channel name (e.g. #general)")
|
|
199
219
|
read_parser.add_argument("--limit", "-n", type=int, default=50, help="Number of messages")
|
|
200
|
-
read_parser.add_argument("--config", default=DEFAULT_CONFIG, help=
|
|
220
|
+
read_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
201
221
|
|
|
202
222
|
# -- who subcommand ----------------------------------------------------
|
|
203
223
|
who_parser = sub.add_parser("who", help="List members of a channel")
|
|
204
224
|
who_parser.add_argument("channel", help="Channel or nick target")
|
|
205
|
-
who_parser.add_argument("--config", default=DEFAULT_CONFIG, help=
|
|
225
|
+
who_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
206
226
|
|
|
207
227
|
# -- send subcommand ---------------------------------------------------
|
|
208
228
|
send_parser = sub.add_parser("send", help="Send a message to a channel or agent")
|
|
209
229
|
send_parser.add_argument("target", help="Channel (e.g. #general) or agent nick")
|
|
210
230
|
send_parser.add_argument("message", help="Message text to send")
|
|
211
|
-
send_parser.add_argument("--config", default=DEFAULT_CONFIG, help=
|
|
231
|
+
send_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
212
232
|
|
|
213
233
|
# -- channels subcommand -----------------------------------------------
|
|
214
234
|
channels_parser = sub.add_parser("channels", help="List active channels")
|
|
215
|
-
channels_parser.add_argument("--config", default=DEFAULT_CONFIG, help=
|
|
235
|
+
channels_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
216
236
|
|
|
217
237
|
# -- learn subcommand --------------------------------------------------
|
|
218
238
|
learn_parser = sub.add_parser("learn", help="Print self-teaching prompt for your agent")
|
|
219
239
|
learn_parser.add_argument("--nick", default=None, help="Agent nick (auto-detects from cwd)")
|
|
220
|
-
learn_parser.add_argument("--config", default=DEFAULT_CONFIG, help=
|
|
240
|
+
learn_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
221
241
|
|
|
222
242
|
# -- sleep subcommand --------------------------------------------------
|
|
223
243
|
sleep_parser = sub.add_parser("sleep", help="Pause agent(s) — stay connected but idle")
|
|
224
244
|
sleep_parser.add_argument("nick", nargs="?", help="Agent nick to pause")
|
|
225
245
|
sleep_parser.add_argument("--all", action="store_true", help="Pause all agents")
|
|
226
|
-
sleep_parser.add_argument("--config", default=DEFAULT_CONFIG, help=
|
|
246
|
+
sleep_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
227
247
|
|
|
228
248
|
# -- wake subcommand ---------------------------------------------------
|
|
229
249
|
wake_parser = sub.add_parser("wake", help="Resume paused agent(s)")
|
|
230
250
|
wake_parser.add_argument("nick", nargs="?", help="Agent nick to resume")
|
|
231
251
|
wake_parser.add_argument("--all", action="store_true", help="Resume all agents")
|
|
232
|
-
wake_parser.add_argument("--config", default=DEFAULT_CONFIG, help=
|
|
252
|
+
wake_parser.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
233
253
|
|
|
234
254
|
# -- skills subcommand -------------------------------------------------
|
|
235
255
|
skills_parser = sub.add_parser("skills", help="Install IRC skills for AI agents")
|
|
@@ -303,22 +323,22 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
303
323
|
bot_create.add_argument("--template", default=None, help="Message template")
|
|
304
324
|
bot_create.add_argument("--dm-owner", action="store_true", help="DM the owner on trigger")
|
|
305
325
|
bot_create.add_argument("--description", default="", help="Bot description")
|
|
306
|
-
bot_create.add_argument("--config", default=DEFAULT_CONFIG, help=
|
|
326
|
+
bot_create.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
307
327
|
|
|
308
328
|
bot_start = bot_sub.add_parser("start", help="Start a bot")
|
|
309
329
|
bot_start.add_argument("name", help="Bot name")
|
|
310
|
-
bot_start.add_argument("--config", default=DEFAULT_CONFIG, help=
|
|
330
|
+
bot_start.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
311
331
|
|
|
312
332
|
bot_stop = bot_sub.add_parser("stop", help="Stop a bot")
|
|
313
333
|
bot_stop.add_argument("name", help="Bot name")
|
|
314
|
-
bot_stop.add_argument("--config", default=DEFAULT_CONFIG, help=
|
|
334
|
+
bot_stop.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
315
335
|
|
|
316
336
|
bot_list = bot_sub.add_parser("list", help="List bots")
|
|
317
337
|
bot_list.add_argument("owner", nargs="?", default=None, help="Filter by owner nick")
|
|
318
338
|
|
|
319
339
|
bot_inspect = bot_sub.add_parser("inspect", help="Show bot details")
|
|
320
340
|
bot_inspect.add_argument("name", help="Bot name")
|
|
321
|
-
bot_inspect.add_argument("--config", default=DEFAULT_CONFIG, help=
|
|
341
|
+
bot_inspect.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
322
342
|
|
|
323
343
|
# -- console subcommand ------------------------------------------------
|
|
324
344
|
console_parser = sub.add_parser("console", help="Interactive admin console")
|
|
@@ -331,7 +351,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
331
351
|
console_parser.add_argument(
|
|
332
352
|
"--config",
|
|
333
353
|
default=DEFAULT_CONFIG,
|
|
334
|
-
help=
|
|
354
|
+
help=_CONFIG_HELP,
|
|
335
355
|
)
|
|
336
356
|
|
|
337
357
|
return parser
|
|
@@ -356,6 +376,8 @@ def main() -> None:
|
|
|
356
376
|
"create": _cmd_init,
|
|
357
377
|
"join": _cmd_join,
|
|
358
378
|
"init": _cmd_init_deprecated,
|
|
379
|
+
"rename": _cmd_rename,
|
|
380
|
+
"assign": _cmd_assign,
|
|
359
381
|
"start": _cmd_start,
|
|
360
382
|
"stop": _cmd_stop,
|
|
361
383
|
"status": _cmd_status,
|
|
@@ -491,6 +513,98 @@ def _cmd_server(args: argparse.Namespace) -> None:
|
|
|
491
513
|
|
|
492
514
|
write_default_server(args.name)
|
|
493
515
|
print(f"Default server set to '{args.name}'")
|
|
516
|
+
elif args.server_command == "rename":
|
|
517
|
+
_server_rename(args)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def _server_rename(args: argparse.Namespace) -> None:
|
|
521
|
+
"""Rename the server: update config, agent nicks, and PID files."""
|
|
522
|
+
from culture.clients.claude.config import rename_server, sanitize_agent_name
|
|
523
|
+
from culture.pidfile import (
|
|
524
|
+
is_process_alive,
|
|
525
|
+
read_default_server,
|
|
526
|
+
read_pid,
|
|
527
|
+
rename_pid,
|
|
528
|
+
write_default_server,
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
try:
|
|
532
|
+
new_name = sanitize_agent_name(args.new_name)
|
|
533
|
+
except ValueError:
|
|
534
|
+
print(f"Invalid server name: {args.new_name!r}", file=sys.stderr)
|
|
535
|
+
sys.exit(1)
|
|
536
|
+
|
|
537
|
+
try:
|
|
538
|
+
old_name, renamed = rename_server(args.config, new_name)
|
|
539
|
+
except ValueError as exc:
|
|
540
|
+
print(str(exc), file=sys.stderr)
|
|
541
|
+
sys.exit(1)
|
|
542
|
+
|
|
543
|
+
if old_name == new_name:
|
|
544
|
+
print(f"Server is already named '{new_name}'")
|
|
545
|
+
return
|
|
546
|
+
|
|
547
|
+
# Rename PID/port files for the server daemon
|
|
548
|
+
rename_pid(f"server-{old_name}", f"server-{new_name}")
|
|
549
|
+
|
|
550
|
+
# Rename PID files for agents
|
|
551
|
+
for old_nick, new_nick in renamed:
|
|
552
|
+
rename_pid(f"agent-{old_nick}", f"agent-{new_nick}")
|
|
553
|
+
|
|
554
|
+
# Update default server if it pointed to the old name
|
|
555
|
+
if read_default_server() == old_name:
|
|
556
|
+
write_default_server(new_name)
|
|
557
|
+
|
|
558
|
+
print(f"Server renamed: {old_name} → {new_name}")
|
|
559
|
+
for old_nick, new_nick in renamed:
|
|
560
|
+
print(f" Agent: {old_nick} → {new_nick}")
|
|
561
|
+
|
|
562
|
+
# Check if the server process is still running
|
|
563
|
+
server_pid = read_pid(f"server-{new_name}")
|
|
564
|
+
server_running = server_pid and is_process_alive(server_pid)
|
|
565
|
+
|
|
566
|
+
print()
|
|
567
|
+
if server_running:
|
|
568
|
+
print("The server is still running under the old name.")
|
|
569
|
+
print("Restart it for the rename to take effect:")
|
|
570
|
+
print(f" culture server stop --name {new_name}")
|
|
571
|
+
print(f" culture server start --name {new_name}")
|
|
572
|
+
if renamed:
|
|
573
|
+
print("Restart agents for the new nicks to take effect:")
|
|
574
|
+
print(" culture stop --all && culture start --all")
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
def _wait_for_port(
|
|
578
|
+
host: str,
|
|
579
|
+
port: int,
|
|
580
|
+
pid: int,
|
|
581
|
+
timeout: float = 30,
|
|
582
|
+
) -> tuple[bool, str]:
|
|
583
|
+
"""Poll *host*:*port* until a TCP connect succeeds or *timeout* expires.
|
|
584
|
+
|
|
585
|
+
Returns ``(True, "")`` on success, or ``(False, reason)`` on failure.
|
|
586
|
+
Checks that *pid* is still alive on every iteration so we fail fast if
|
|
587
|
+
the child crashes (e.g. because the port was already in use).
|
|
588
|
+
"""
|
|
589
|
+
check_host = "127.0.0.1" if host == "0.0.0.0" else host
|
|
590
|
+
deadline = time.monotonic() + timeout
|
|
591
|
+
while time.monotonic() < deadline:
|
|
592
|
+
if not is_process_alive(pid):
|
|
593
|
+
return False, "failed to start"
|
|
594
|
+
try:
|
|
595
|
+
s = socket.create_connection((check_host, port), timeout=0.5)
|
|
596
|
+
s.close()
|
|
597
|
+
except OSError:
|
|
598
|
+
time.sleep(0.2)
|
|
599
|
+
continue
|
|
600
|
+
# Port responded — give the child a moment, then confirm it's
|
|
601
|
+
# still alive (guards against connecting to a *stale* listener
|
|
602
|
+
# on the same port while our child crashes).
|
|
603
|
+
time.sleep(0.1)
|
|
604
|
+
if not is_process_alive(pid):
|
|
605
|
+
return False, "failed to start"
|
|
606
|
+
return True, ""
|
|
607
|
+
return False, "started but not yet accepting connections"
|
|
494
608
|
|
|
495
609
|
|
|
496
610
|
def _server_start(args: argparse.Namespace) -> None:
|
|
@@ -532,19 +646,33 @@ def _server_start(args: argparse.Namespace) -> None:
|
|
|
532
646
|
# Fork to daemonize
|
|
533
647
|
pid = os.fork()
|
|
534
648
|
if pid > 0:
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
write_default_server(args.name)
|
|
649
|
+
log_hint = f"{LOG_DIR}/server-{args.name}.log"
|
|
650
|
+
|
|
651
|
+
if args.port == 0:
|
|
652
|
+
# Ephemeral port — can't probe; fall back to process-alive check
|
|
653
|
+
time.sleep(0.5)
|
|
654
|
+
if not is_process_alive(pid):
|
|
655
|
+
print(f"Server '{args.name}' failed to start", file=sys.stderr)
|
|
656
|
+
print(f" Check logs: {log_hint}", file=sys.stderr)
|
|
657
|
+
sys.exit(1)
|
|
545
658
|
else:
|
|
546
|
-
|
|
547
|
-
|
|
659
|
+
ok, err = _wait_for_port(args.host, args.port, pid, timeout=30)
|
|
660
|
+
if not ok:
|
|
661
|
+
print(
|
|
662
|
+
f"Server '{args.name}' {err}",
|
|
663
|
+
file=sys.stderr,
|
|
664
|
+
)
|
|
665
|
+
print(f" Check logs: {log_hint}", file=sys.stderr)
|
|
666
|
+
sys.exit(1)
|
|
667
|
+
|
|
668
|
+
print(f"Server '{args.name}' started (PID {pid})")
|
|
669
|
+
print(f" Listening on {args.host}:{args.port}")
|
|
670
|
+
print(f" Logs: {log_hint}")
|
|
671
|
+
# Auto-set default server if none is set
|
|
672
|
+
from culture.pidfile import read_default_server, write_default_server
|
|
673
|
+
|
|
674
|
+
if read_default_server() is None:
|
|
675
|
+
write_default_server(args.name)
|
|
548
676
|
return
|
|
549
677
|
|
|
550
678
|
# Child: detach from parent session
|
|
@@ -562,6 +690,13 @@ def _server_start(args: argparse.Namespace) -> None:
|
|
|
562
690
|
os.dup2(devnull, 0)
|
|
563
691
|
os.close(devnull)
|
|
564
692
|
|
|
693
|
+
# Reconfigure logging so handlers write to the redirected stderr (log file)
|
|
694
|
+
logging.basicConfig(
|
|
695
|
+
level=logging.INFO,
|
|
696
|
+
format="%(asctime)s %(name)s %(levelname)s %(message)s",
|
|
697
|
+
force=True,
|
|
698
|
+
)
|
|
699
|
+
|
|
565
700
|
write_pid(pid_name, os.getpid())
|
|
566
701
|
|
|
567
702
|
try:
|
|
@@ -785,6 +920,104 @@ def _cmd_init(args: argparse.Namespace) -> None:
|
|
|
785
920
|
print(f"Or join the mesh: culture join {full_nick}")
|
|
786
921
|
|
|
787
922
|
|
|
923
|
+
def _cmd_rename(args: argparse.Namespace) -> None:
|
|
924
|
+
"""Rename an agent's suffix within the same server."""
|
|
925
|
+
from culture.clients.claude.config import (
|
|
926
|
+
load_config_or_default,
|
|
927
|
+
rename_agent,
|
|
928
|
+
sanitize_agent_name,
|
|
929
|
+
)
|
|
930
|
+
from culture.pidfile import rename_pid
|
|
931
|
+
|
|
932
|
+
config = load_config_or_default(args.config)
|
|
933
|
+
old_nick = args.nick
|
|
934
|
+
server_name = config.server.name
|
|
935
|
+
expected_prefix = f"{server_name}-"
|
|
936
|
+
|
|
937
|
+
if not old_nick.startswith(expected_prefix):
|
|
938
|
+
print(
|
|
939
|
+
f"Agent '{old_nick}' does not belong to server '{server_name}'",
|
|
940
|
+
file=sys.stderr,
|
|
941
|
+
)
|
|
942
|
+
sys.exit(1)
|
|
943
|
+
|
|
944
|
+
try:
|
|
945
|
+
new_suffix = sanitize_agent_name(args.new_name)
|
|
946
|
+
except ValueError:
|
|
947
|
+
print(f"Invalid agent name: {args.new_name!r}", file=sys.stderr)
|
|
948
|
+
sys.exit(1)
|
|
949
|
+
|
|
950
|
+
new_nick = f"{server_name}-{new_suffix}"
|
|
951
|
+
|
|
952
|
+
if old_nick == new_nick:
|
|
953
|
+
print(f"Agent is already named '{old_nick}'")
|
|
954
|
+
return
|
|
955
|
+
|
|
956
|
+
try:
|
|
957
|
+
rename_agent(args.config, old_nick, new_nick)
|
|
958
|
+
except ValueError as exc:
|
|
959
|
+
print(str(exc), file=sys.stderr)
|
|
960
|
+
sys.exit(1)
|
|
961
|
+
|
|
962
|
+
rename_pid(f"agent-{old_nick}", f"agent-{new_nick}")
|
|
963
|
+
|
|
964
|
+
print(f"Agent renamed: {old_nick} → {new_nick}")
|
|
965
|
+
print()
|
|
966
|
+
print("Restart the agent for the new nick to take effect:")
|
|
967
|
+
print(f" culture stop {old_nick} # if still running under old name")
|
|
968
|
+
print(f" culture start {new_nick}")
|
|
969
|
+
|
|
970
|
+
|
|
971
|
+
def _cmd_assign(args: argparse.Namespace) -> None:
|
|
972
|
+
"""Move an agent to a different server (change nick prefix)."""
|
|
973
|
+
from culture.clients.claude.config import (
|
|
974
|
+
load_config_or_default,
|
|
975
|
+
rename_agent,
|
|
976
|
+
sanitize_agent_name,
|
|
977
|
+
)
|
|
978
|
+
from culture.pidfile import rename_pid
|
|
979
|
+
|
|
980
|
+
config = load_config_or_default(args.config)
|
|
981
|
+
old_nick = args.nick
|
|
982
|
+
server_name = config.server.name
|
|
983
|
+
expected_prefix = f"{server_name}-"
|
|
984
|
+
|
|
985
|
+
if not old_nick.startswith(expected_prefix):
|
|
986
|
+
print(
|
|
987
|
+
f"Agent '{old_nick}' does not belong to server '{server_name}'",
|
|
988
|
+
file=sys.stderr,
|
|
989
|
+
)
|
|
990
|
+
sys.exit(1)
|
|
991
|
+
|
|
992
|
+
suffix = old_nick[len(expected_prefix) :]
|
|
993
|
+
|
|
994
|
+
try:
|
|
995
|
+
new_server = sanitize_agent_name(args.server)
|
|
996
|
+
except ValueError:
|
|
997
|
+
print(f"Invalid server name: {args.server!r}", file=sys.stderr)
|
|
998
|
+
sys.exit(1)
|
|
999
|
+
|
|
1000
|
+
new_nick = f"{new_server}-{suffix}"
|
|
1001
|
+
|
|
1002
|
+
if old_nick == new_nick:
|
|
1003
|
+
print(f"Agent already belongs to server '{new_server}'")
|
|
1004
|
+
return
|
|
1005
|
+
|
|
1006
|
+
try:
|
|
1007
|
+
rename_agent(args.config, old_nick, new_nick)
|
|
1008
|
+
except ValueError as exc:
|
|
1009
|
+
print(str(exc), file=sys.stderr)
|
|
1010
|
+
sys.exit(1)
|
|
1011
|
+
|
|
1012
|
+
rename_pid(f"agent-{old_nick}", f"agent-{new_nick}")
|
|
1013
|
+
|
|
1014
|
+
print(f"Agent reassigned: {old_nick} → {new_nick}")
|
|
1015
|
+
print()
|
|
1016
|
+
print("Restart the agent for the new nick to take effect:")
|
|
1017
|
+
print(f" culture stop {old_nick} # if still running under old name")
|
|
1018
|
+
print(f" culture start {new_nick}")
|
|
1019
|
+
|
|
1020
|
+
|
|
788
1021
|
# -----------------------------------------------------------------------
|
|
789
1022
|
# Agent start
|
|
790
1023
|
# -----------------------------------------------------------------------
|
|
@@ -178,3 +178,71 @@ def add_agent_to_config(
|
|
|
178
178
|
config.agents.append(agent)
|
|
179
179
|
save_config(path, config)
|
|
180
180
|
return config
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def rename_server(
|
|
184
|
+
path: str | Path,
|
|
185
|
+
new_name: str,
|
|
186
|
+
) -> tuple[str, list[tuple[str, str]]]:
|
|
187
|
+
"""Rename the server and update all agent nick prefixes.
|
|
188
|
+
|
|
189
|
+
Returns (old_name, [(old_nick, new_nick), ...]).
|
|
190
|
+
"""
|
|
191
|
+
config = load_config_or_default(path)
|
|
192
|
+
old_name = config.server.name
|
|
193
|
+
|
|
194
|
+
if old_name == new_name:
|
|
195
|
+
return old_name, []
|
|
196
|
+
|
|
197
|
+
# Plan renames and check for collisions before mutating
|
|
198
|
+
prefix = f"{old_name}-"
|
|
199
|
+
plan: list[tuple[int, str, str]] = []
|
|
200
|
+
for i, agent in enumerate(config.agents):
|
|
201
|
+
if agent.nick.startswith(prefix):
|
|
202
|
+
new_nick = f"{new_name}-{agent.nick[len(prefix):]}"
|
|
203
|
+
plan.append((i, agent.nick, new_nick))
|
|
204
|
+
|
|
205
|
+
planned_nicks = {new_nick for _, _, new_nick in plan}
|
|
206
|
+
existing_nicks = {a.nick for a in config.agents} - {old for _, old, _ in plan}
|
|
207
|
+
collisions = planned_nicks & existing_nicks
|
|
208
|
+
if collisions:
|
|
209
|
+
raise ValueError(
|
|
210
|
+
f"renaming server {old_name!r} to {new_name!r} would create "
|
|
211
|
+
f"duplicate nick(s): {', '.join(sorted(collisions))}"
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
config.server.name = new_name
|
|
215
|
+
|
|
216
|
+
renamed: list[tuple[str, str]] = []
|
|
217
|
+
for i, old_nick, new_nick in plan:
|
|
218
|
+
config.agents[i].nick = new_nick
|
|
219
|
+
renamed.append((old_nick, new_nick))
|
|
220
|
+
|
|
221
|
+
save_config(path, config)
|
|
222
|
+
return old_name, renamed
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def rename_agent(
|
|
226
|
+
path: str | Path,
|
|
227
|
+
old_nick: str,
|
|
228
|
+
new_nick: str,
|
|
229
|
+
) -> None:
|
|
230
|
+
"""Rename an agent's nick in the config.
|
|
231
|
+
|
|
232
|
+
Raises ValueError if old_nick is not found or new_nick already exists.
|
|
233
|
+
"""
|
|
234
|
+
config = load_config_or_default(path)
|
|
235
|
+
|
|
236
|
+
# Check new nick doesn't collide
|
|
237
|
+
for agent in config.agents:
|
|
238
|
+
if agent.nick == new_nick:
|
|
239
|
+
raise ValueError(f"agent with nick {new_nick!r} already exists in config")
|
|
240
|
+
|
|
241
|
+
# Find and rename
|
|
242
|
+
for agent in config.agents:
|
|
243
|
+
if agent.nick == old_nick:
|
|
244
|
+
agent.nick = new_nick
|
|
245
|
+
save_config(path, config)
|
|
246
|
+
return
|
|
247
|
+
|
|
248
|
+
raise ValueError(f"agent {old_nick!r} not found in config")
|
|
@@ -182,3 +182,71 @@ def add_agent_to_config(
|
|
|
182
182
|
config.agents.append(agent)
|
|
183
183
|
save_config(path, config)
|
|
184
184
|
return config
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def rename_server(
|
|
188
|
+
path: str | Path,
|
|
189
|
+
new_name: str,
|
|
190
|
+
) -> tuple[str, list[tuple[str, str]]]:
|
|
191
|
+
"""Rename the server and update all agent nick prefixes.
|
|
192
|
+
|
|
193
|
+
Returns (old_name, [(old_nick, new_nick), ...]).
|
|
194
|
+
"""
|
|
195
|
+
config = load_config_or_default(path)
|
|
196
|
+
old_name = config.server.name
|
|
197
|
+
|
|
198
|
+
if old_name == new_name:
|
|
199
|
+
return old_name, []
|
|
200
|
+
|
|
201
|
+
# Plan renames and check for collisions before mutating
|
|
202
|
+
prefix = f"{old_name}-"
|
|
203
|
+
plan: list[tuple[int, str, str]] = []
|
|
204
|
+
for i, agent in enumerate(config.agents):
|
|
205
|
+
if agent.nick.startswith(prefix):
|
|
206
|
+
new_nick = f"{new_name}-{agent.nick[len(prefix):]}"
|
|
207
|
+
plan.append((i, agent.nick, new_nick))
|
|
208
|
+
|
|
209
|
+
planned_nicks = {new_nick for _, _, new_nick in plan}
|
|
210
|
+
existing_nicks = {a.nick for a in config.agents} - {old for _, old, _ in plan}
|
|
211
|
+
collisions = planned_nicks & existing_nicks
|
|
212
|
+
if collisions:
|
|
213
|
+
raise ValueError(
|
|
214
|
+
f"renaming server {old_name!r} to {new_name!r} would create "
|
|
215
|
+
f"duplicate nick(s): {', '.join(sorted(collisions))}"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
config.server.name = new_name
|
|
219
|
+
|
|
220
|
+
renamed: list[tuple[str, str]] = []
|
|
221
|
+
for i, old_nick, new_nick in plan:
|
|
222
|
+
config.agents[i].nick = new_nick
|
|
223
|
+
renamed.append((old_nick, new_nick))
|
|
224
|
+
|
|
225
|
+
save_config(path, config)
|
|
226
|
+
return old_name, renamed
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def rename_agent(
|
|
230
|
+
path: str | Path,
|
|
231
|
+
old_nick: str,
|
|
232
|
+
new_nick: str,
|
|
233
|
+
) -> None:
|
|
234
|
+
"""Rename an agent's nick in the config.
|
|
235
|
+
|
|
236
|
+
Raises ValueError if old_nick is not found or new_nick already exists.
|
|
237
|
+
"""
|
|
238
|
+
config = load_config_or_default(path)
|
|
239
|
+
|
|
240
|
+
# Check new nick doesn't collide
|
|
241
|
+
for agent in config.agents:
|
|
242
|
+
if agent.nick == new_nick:
|
|
243
|
+
raise ValueError(f"agent with nick {new_nick!r} already exists in config")
|
|
244
|
+
|
|
245
|
+
# Find and rename
|
|
246
|
+
for agent in config.agents:
|
|
247
|
+
if agent.nick == old_nick:
|
|
248
|
+
agent.nick = new_nick
|
|
249
|
+
save_config(path, config)
|
|
250
|
+
return
|
|
251
|
+
|
|
252
|
+
raise ValueError(f"agent {old_nick!r} not found in config")
|
|
@@ -178,3 +178,71 @@ def add_agent_to_config(
|
|
|
178
178
|
config.agents.append(agent)
|
|
179
179
|
save_config(path, config)
|
|
180
180
|
return config
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def rename_server(
|
|
184
|
+
path: str | Path,
|
|
185
|
+
new_name: str,
|
|
186
|
+
) -> tuple[str, list[tuple[str, str]]]:
|
|
187
|
+
"""Rename the server and update all agent nick prefixes.
|
|
188
|
+
|
|
189
|
+
Returns (old_name, [(old_nick, new_nick), ...]).
|
|
190
|
+
"""
|
|
191
|
+
config = load_config_or_default(path)
|
|
192
|
+
old_name = config.server.name
|
|
193
|
+
|
|
194
|
+
if old_name == new_name:
|
|
195
|
+
return old_name, []
|
|
196
|
+
|
|
197
|
+
# Plan renames and check for collisions before mutating
|
|
198
|
+
prefix = f"{old_name}-"
|
|
199
|
+
plan: list[tuple[int, str, str]] = []
|
|
200
|
+
for i, agent in enumerate(config.agents):
|
|
201
|
+
if agent.nick.startswith(prefix):
|
|
202
|
+
new_nick = f"{new_name}-{agent.nick[len(prefix):]}"
|
|
203
|
+
plan.append((i, agent.nick, new_nick))
|
|
204
|
+
|
|
205
|
+
planned_nicks = {new_nick for _, _, new_nick in plan}
|
|
206
|
+
existing_nicks = {a.nick for a in config.agents} - {old for _, old, _ in plan}
|
|
207
|
+
collisions = planned_nicks & existing_nicks
|
|
208
|
+
if collisions:
|
|
209
|
+
raise ValueError(
|
|
210
|
+
f"renaming server {old_name!r} to {new_name!r} would create "
|
|
211
|
+
f"duplicate nick(s): {', '.join(sorted(collisions))}"
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
config.server.name = new_name
|
|
215
|
+
|
|
216
|
+
renamed: list[tuple[str, str]] = []
|
|
217
|
+
for i, old_nick, new_nick in plan:
|
|
218
|
+
config.agents[i].nick = new_nick
|
|
219
|
+
renamed.append((old_nick, new_nick))
|
|
220
|
+
|
|
221
|
+
save_config(path, config)
|
|
222
|
+
return old_name, renamed
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def rename_agent(
|
|
226
|
+
path: str | Path,
|
|
227
|
+
old_nick: str,
|
|
228
|
+
new_nick: str,
|
|
229
|
+
) -> None:
|
|
230
|
+
"""Rename an agent's nick in the config.
|
|
231
|
+
|
|
232
|
+
Raises ValueError if old_nick is not found or new_nick already exists.
|
|
233
|
+
"""
|
|
234
|
+
config = load_config_or_default(path)
|
|
235
|
+
|
|
236
|
+
# Check new nick doesn't collide
|
|
237
|
+
for agent in config.agents:
|
|
238
|
+
if agent.nick == new_nick:
|
|
239
|
+
raise ValueError(f"agent with nick {new_nick!r} already exists in config")
|
|
240
|
+
|
|
241
|
+
# Find and rename
|
|
242
|
+
for agent in config.agents:
|
|
243
|
+
if agent.nick == old_nick:
|
|
244
|
+
agent.nick = new_nick
|
|
245
|
+
save_config(path, config)
|
|
246
|
+
return
|
|
247
|
+
|
|
248
|
+
raise ValueError(f"agent {old_nick!r} not found in config")
|