agentirc-cli 1.0.6__tar.gz → 1.0.7__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-1.0.6 → agentirc_cli-1.0.7}/CHANGELOG.md +8 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/PKG-INFO +1 -1
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/cli.py +29 -2
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/pidfile.py +21 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/pyproject.toml +1 -1
- agentirc_cli-1.0.7/tests/test_pidfile.py +122 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/uv.lock +1 -1
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/.claude/skills/pr-review/SKILL.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/.claude/skills/run-tests/SKILL.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/.flake8 +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/.github/workflows/pages.yml +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/.github/workflows/security-checks.yml +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/.gitignore +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/.markdownlint-cli2.yaml +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/.pr_agent.toml +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/.pre-commit-config.yaml +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/.pylintrc +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/CLAUDE.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/CNAME +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/Gemfile +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/Gemfile.lock +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/LICENSE +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/README.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/SECURITY.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/_config.yml +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/_sass/color_schemes/anthropic.scss +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/_sass/custom/custom.scss +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/__main__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/bots/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/bots/bot.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/bots/bot_manager.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/bots/config.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/bots/http_listener.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/bots/template_engine.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/bots/virtual_client.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/acp/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/acp/agent_runner.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/acp/config.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/acp/daemon.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/acp/ipc.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/acp/irc_transport.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/acp/message_buffer.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/acp/skill/SKILL.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/acp/skill/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/acp/skill/irc_client.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/acp/socket_server.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/acp/supervisor.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/acp/webhook.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/claude/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/claude/__main__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/claude/agent_runner.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/claude/config.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/claude/daemon.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/claude/ipc.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/claude/irc_transport.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/claude/message_buffer.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/claude/skill/SKILL.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/claude/skill/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/claude/skill/irc_client.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/claude/socket_server.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/claude/supervisor.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/claude/webhook.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/codex/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/codex/agent_runner.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/codex/config.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/codex/daemon.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/codex/ipc.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/codex/irc_transport.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/codex/message_buffer.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/codex/skill/SKILL.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/codex/skill/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/codex/skill/irc_client.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/codex/socket_server.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/codex/supervisor.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/codex/webhook.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/copilot/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/copilot/agent_runner.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/copilot/config.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/copilot/daemon.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/copilot/ipc.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/copilot/irc_transport.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/copilot/message_buffer.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/copilot/skill/SKILL.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/copilot/skill/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/copilot/skill/irc_client.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/copilot/socket_server.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/copilot/supervisor.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/clients/copilot/webhook.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/credentials.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/learn_prompt.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/mesh_config.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/observer.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/overview/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/overview/collector.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/overview/model.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/overview/renderer_text.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/overview/renderer_web.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/overview/web/style.css +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/persistence.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/protocol/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/protocol/commands.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/protocol/extensions/federation.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/protocol/extensions/history.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/protocol/extensions/rooms.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/protocol/extensions/tags.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/protocol/extensions/threads.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/protocol/message.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/protocol/protocol-index.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/protocol/replies.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/__main__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/channel.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/client.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/config.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/ircd.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/remote_client.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/room_store.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/rooms_util.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/server_link.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/skill.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/skills/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/skills/history.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/skills/rooms.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/skills/threads.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/server/thread_store.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/culture/skills/culture/SKILL.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/SECURITY.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/agent-client.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/agent-harness-spec.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/agentic-self-learn.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/bots.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/ci.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/cli.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/acp/overview.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/claude/configuration.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/claude/context-management.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/claude/irc-tools.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/claude/overview.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/claude/setup.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/claude/supervisor.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/claude/webhooks.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/codex/configuration.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/codex/context-management.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/codex/irc-tools.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/codex/overview.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/codex/setup.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/codex/supervisor.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/codex/webhooks.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/copilot/configuration.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/copilot/context-management.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/copilot/irc-tools.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/copilot/overview.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/copilot/setup.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/copilot/supervisor.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/clients/copilot/webhooks.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/codex-backend.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/copilot-backend.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/design.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/docs-site.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/getting-started.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/grow-your-agent.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/harness-conformance.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/layer1-core-irc.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/layer2-attention.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/layer3-skills.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/layer4-federation.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/layer5-agent-harness.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/ops-tooling.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/overview.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/publishing.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/resources/github-copilot-sdk-instructions.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/rooms.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/server-architecture.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/threads.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/use-cases/01-pair-programming.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/use-cases/02-code-review-ensemble.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/use-cases/03-cross-server-delegation.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/use-cases/04-knowledge-propagation.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/use-cases/05-the-observer.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/use-cases/06-cross-server-ops.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/use-cases/07-supervisor-intervention.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/use-cases/08-apps-as-agents.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/use-cases/09-research-swarm.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/use-cases/10-grow-your-agent.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/docs/use-cases-index.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/index.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/packages/agent-harness/README.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/packages/agent-harness/config.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/packages/agent-harness/daemon.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/packages/agent-harness/ipc.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/packages/agent-harness/irc_transport.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/packages/agent-harness/message_buffer.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/packages/agent-harness/skill/SKILL.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/packages/agent-harness/skill/irc_client.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/packages/agent-harness/socket_server.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/packages/agent-harness/webhook.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/plugins/claude-code/skills/culture/SKILL.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/plugins/claude-code/skills/irc/SKILL.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/sonar-project.properties +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/__init__.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/conftest.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_acp_daemon.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_agent_runner.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_bot.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_bot_config.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_bot_manager.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_bots_integration.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_channel.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_codex_daemon.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_connection.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_copilot_daemon.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_daemon.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_daemon_config.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_daemon_ipc.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_discovery.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_federation.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_history.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_http_listener.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_integration_layer5.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_ipc.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_irc_transport.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_link_reconnect.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_mentions.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_mesh_config.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_message.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_message_buffer.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_messaging.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_modes.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_overview_cli.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_overview_collector.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_overview_model.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_overview_renderer.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_overview_web.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_persistence.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_room_persistence.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_rooms.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_rooms_federation.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_rooms_integration.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_setup_update_cli.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_skill_client.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_skills.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_socket_server.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_supervisor.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_template_engine.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_thread_buffer.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_threads.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_virtual_client.py +0 -0
- {agentirc_cli-1.0.6 → agentirc_cli-1.0.7}/tests/test_webhook.py +0 -0
|
@@ -4,6 +4,14 @@ 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
|
+
## [1.0.7] - 2026-04-05
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Validate PID ownership via /proc/<pid>/cmdline before os.kill() to prevent signaling unrelated processes after PID reuse (SonarCloud S4828)
|
|
13
|
+
- Wrap initial SIGTERM in try/except ProcessLookupError for race condition safety
|
|
14
|
+
|
|
7
15
|
## [1.0.6] - 2026-04-05
|
|
8
16
|
|
|
9
17
|
|
|
@@ -38,7 +38,13 @@ from culture.clients.claude.config import (
|
|
|
38
38
|
load_config_or_default,
|
|
39
39
|
sanitize_agent_name,
|
|
40
40
|
)
|
|
41
|
-
from culture.pidfile import
|
|
41
|
+
from culture.pidfile import (
|
|
42
|
+
is_culture_process,
|
|
43
|
+
is_process_alive,
|
|
44
|
+
read_pid,
|
|
45
|
+
remove_pid,
|
|
46
|
+
write_pid,
|
|
47
|
+
)
|
|
42
48
|
|
|
43
49
|
logger = logging.getLogger("culture")
|
|
44
50
|
|
|
@@ -902,13 +908,27 @@ def _try_pid_shutdown(nick: str) -> None:
|
|
|
902
908
|
print(f"No PID file for agent '{nick}'")
|
|
903
909
|
return
|
|
904
910
|
|
|
911
|
+
if pid <= 0:
|
|
912
|
+
print(f"Invalid PID {pid} for agent '{nick}' — removing corrupt PID file")
|
|
913
|
+
remove_pid(pid_name)
|
|
914
|
+
return
|
|
915
|
+
|
|
905
916
|
if not is_process_alive(pid):
|
|
906
917
|
print(f"Agent '{nick}' is not running (stale PID {pid})")
|
|
907
918
|
remove_pid(pid_name)
|
|
908
919
|
return
|
|
909
920
|
|
|
921
|
+
if not is_culture_process(pid):
|
|
922
|
+
print(f"PID {pid} is not a culture process — removing stale PID file")
|
|
923
|
+
remove_pid(pid_name)
|
|
924
|
+
return
|
|
925
|
+
|
|
910
926
|
print(f"Stopping agent '{nick}' (PID {pid})...")
|
|
911
|
-
|
|
927
|
+
try:
|
|
928
|
+
os.kill(pid, signal.SIGTERM)
|
|
929
|
+
except ProcessLookupError:
|
|
930
|
+
remove_pid(pid_name)
|
|
931
|
+
return
|
|
912
932
|
|
|
913
933
|
for _ in range(50):
|
|
914
934
|
if not is_process_alive(pid):
|
|
@@ -917,6 +937,13 @@ def _try_pid_shutdown(nick: str) -> None:
|
|
|
917
937
|
return
|
|
918
938
|
time.sleep(0.1)
|
|
919
939
|
|
|
940
|
+
# Re-validate ownership before escalating — the original process may have
|
|
941
|
+
# exited and the PID may have been reused during the 5s wait.
|
|
942
|
+
if not is_culture_process(pid):
|
|
943
|
+
print(f"PID {pid} is no longer a culture process — aborting kill")
|
|
944
|
+
remove_pid(pid_name)
|
|
945
|
+
return
|
|
946
|
+
|
|
920
947
|
# Force kill
|
|
921
948
|
if sys.platform == "win32":
|
|
922
949
|
print(f"Agent '{nick}' did not stop gracefully, terminating")
|
|
@@ -72,6 +72,27 @@ def remove_port(name: str) -> None:
|
|
|
72
72
|
pass
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
def is_culture_process(pid: int) -> bool:
|
|
76
|
+
"""Check whether the given PID belongs to a culture process.
|
|
77
|
+
|
|
78
|
+
Reads /proc/<pid>/cmdline on Linux and checks NUL-separated argv
|
|
79
|
+
tokens for an exact "culture" match (e.g. argv[0] basename or a
|
|
80
|
+
``-m culture`` argument). On platforms without /proc, returns True
|
|
81
|
+
(assumes valid). On Linux, read/parse failures return False (fail
|
|
82
|
+
closed) to avoid killing unrelated processes after PID reuse.
|
|
83
|
+
"""
|
|
84
|
+
if not os.path.isdir("/proc"):
|
|
85
|
+
# /proc not available (macOS / Windows) — can't verify, assume valid
|
|
86
|
+
return True
|
|
87
|
+
try:
|
|
88
|
+
raw = Path(f"/proc/{pid}/cmdline").read_bytes()
|
|
89
|
+
tokens = [t for t in raw.decode(errors="replace").split("\x00") if t]
|
|
90
|
+
return any(os.path.basename(t) == "culture" or t == "culture" for t in tokens)
|
|
91
|
+
except (OSError, PermissionError):
|
|
92
|
+
# On Linux /proc exists but we can't read this PID — fail closed
|
|
93
|
+
return False
|
|
94
|
+
|
|
95
|
+
|
|
75
96
|
def is_process_alive(pid: int) -> bool:
|
|
76
97
|
"""Check whether a process with the given PID is alive."""
|
|
77
98
|
try:
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Tests for culture.pidfile — PID file management and process validation."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from unittest.mock import patch
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from culture.pidfile import (
|
|
10
|
+
is_culture_process,
|
|
11
|
+
is_process_alive,
|
|
12
|
+
read_pid,
|
|
13
|
+
remove_pid,
|
|
14
|
+
write_pid,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.fixture()
|
|
19
|
+
def pid_dir(tmp_path):
|
|
20
|
+
"""Use a temporary directory for PID files."""
|
|
21
|
+
with patch("culture.pidfile.PID_DIR", str(tmp_path)):
|
|
22
|
+
yield tmp_path
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TestWriteReadRemove:
|
|
26
|
+
def test_write_and_read(self, pid_dir):
|
|
27
|
+
write_pid("agent-bot", 12345)
|
|
28
|
+
assert read_pid("agent-bot") == 12345
|
|
29
|
+
|
|
30
|
+
def test_read_missing(self, pid_dir):
|
|
31
|
+
assert read_pid("nonexistent") is None
|
|
32
|
+
|
|
33
|
+
def test_remove(self, pid_dir):
|
|
34
|
+
write_pid("agent-bot", 12345)
|
|
35
|
+
remove_pid("agent-bot")
|
|
36
|
+
assert read_pid("agent-bot") is None
|
|
37
|
+
|
|
38
|
+
def test_remove_missing_is_noop(self, pid_dir):
|
|
39
|
+
remove_pid("nonexistent") # should not raise
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TestIsProcessAlive:
|
|
43
|
+
def test_current_process_alive(self):
|
|
44
|
+
assert is_process_alive(os.getpid()) is True
|
|
45
|
+
|
|
46
|
+
def test_nonexistent_pid(self):
|
|
47
|
+
# Use a very high PID unlikely to exist.
|
|
48
|
+
assert is_process_alive(4_000_000) is False
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class TestIsCultureProcess:
|
|
52
|
+
@pytest.mark.skipif(
|
|
53
|
+
not Path("/proc/self/cmdline").exists(),
|
|
54
|
+
reason="/proc not available",
|
|
55
|
+
)
|
|
56
|
+
def test_current_process_is_python(self):
|
|
57
|
+
# We're running under pytest — cmdline won't contain "culture"
|
|
58
|
+
# as an exact argv token, so this should return False.
|
|
59
|
+
result = is_culture_process(os.getpid())
|
|
60
|
+
assert isinstance(result, bool)
|
|
61
|
+
|
|
62
|
+
def test_cmdline_with_culture_token(self):
|
|
63
|
+
"""Exact argv token 'culture' is recognized (e.g. -m culture)."""
|
|
64
|
+
raw = b"/usr/bin/python3\x00-m\x00culture\x00start\x00"
|
|
65
|
+
with (
|
|
66
|
+
patch("culture.pidfile.Path") as mock_path,
|
|
67
|
+
patch("culture.pidfile.os.path.isdir", return_value=True),
|
|
68
|
+
):
|
|
69
|
+
mock_path.return_value.read_bytes.return_value = raw
|
|
70
|
+
assert is_culture_process(999) is True
|
|
71
|
+
|
|
72
|
+
def test_cmdline_with_culture_basename(self):
|
|
73
|
+
"""argv[0] with basename 'culture' is recognized."""
|
|
74
|
+
raw = b"/home/user/.local/bin/culture\x00server\x00start\x00"
|
|
75
|
+
with (
|
|
76
|
+
patch("culture.pidfile.Path") as mock_path,
|
|
77
|
+
patch("culture.pidfile.os.path.isdir", return_value=True),
|
|
78
|
+
):
|
|
79
|
+
mock_path.return_value.read_bytes.return_value = raw
|
|
80
|
+
assert is_culture_process(999) is True
|
|
81
|
+
|
|
82
|
+
def test_cmdline_without_culture(self):
|
|
83
|
+
"""Process with unrelated cmdline is rejected."""
|
|
84
|
+
raw = b"/sbin/init\x00--system\x00"
|
|
85
|
+
with (
|
|
86
|
+
patch("culture.pidfile.Path") as mock_path,
|
|
87
|
+
patch("culture.pidfile.os.path.isdir", return_value=True),
|
|
88
|
+
):
|
|
89
|
+
mock_path.return_value.read_bytes.return_value = raw
|
|
90
|
+
assert is_culture_process(999) is False
|
|
91
|
+
|
|
92
|
+
def test_cmdline_substring_not_matched(self):
|
|
93
|
+
"""Substring 'culture' inside another token is NOT matched."""
|
|
94
|
+
raw = b"/usr/bin/agriculture-daemon\x00--flag\x00"
|
|
95
|
+
with (
|
|
96
|
+
patch("culture.pidfile.Path") as mock_path,
|
|
97
|
+
patch("culture.pidfile.os.path.isdir", return_value=True),
|
|
98
|
+
):
|
|
99
|
+
mock_path.return_value.read_bytes.return_value = raw
|
|
100
|
+
assert is_culture_process(999) is False
|
|
101
|
+
|
|
102
|
+
def test_no_proc_returns_true(self):
|
|
103
|
+
"""When /proc doesn't exist (macOS/Windows), assume valid."""
|
|
104
|
+
with patch("culture.pidfile.os.path.isdir", return_value=False):
|
|
105
|
+
assert is_culture_process(999) is True
|
|
106
|
+
|
|
107
|
+
def test_oserror_on_linux_returns_false(self):
|
|
108
|
+
"""On Linux, read failures fail closed (return False)."""
|
|
109
|
+
with (
|
|
110
|
+
patch("culture.pidfile.Path") as mock_path,
|
|
111
|
+
patch("culture.pidfile.os.path.isdir", return_value=True),
|
|
112
|
+
):
|
|
113
|
+
mock_path.return_value.read_bytes.side_effect = OSError("denied")
|
|
114
|
+
assert is_culture_process(999) is False
|
|
115
|
+
|
|
116
|
+
@pytest.mark.skipif(
|
|
117
|
+
not Path("/proc/self/cmdline").exists(),
|
|
118
|
+
reason="/proc not available",
|
|
119
|
+
)
|
|
120
|
+
def test_nonexistent_pid_fails_closed(self):
|
|
121
|
+
"""/proc exists but PID doesn't — should return False."""
|
|
122
|
+
assert is_culture_process(4_000_000) is False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|