agentirc-cli 0.16.4__tar.gz → 0.18.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-0.16.4 → agentirc_cli-0.18.0}/CHANGELOG.md +30 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/PKG-INFO +1 -1
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/cli.py +461 -26
- agentirc_cli-0.18.0/agentirc/credentials.py +132 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/learn_prompt.py +70 -4
- agentirc_cli-0.18.0/agentirc/mesh_config.py +94 -0
- agentirc_cli-0.18.0/agentirc/persistence.py +233 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/ircd.py +93 -1
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/server_link.py +4 -1
- agentirc_cli-0.18.0/agentirc/skills/agentirc/SKILL.md +287 -0
- agentirc_cli-0.18.0/docs/agentic-self-learn.md +110 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/cli.md +53 -0
- agentirc_cli-0.18.0/docs/ops-tooling.md +271 -0
- agentirc_cli-0.18.0/plugins/claude-code/skills/agentirc/SKILL.md +218 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/pyproject.toml +2 -1
- agentirc_cli-0.18.0/tests/test_link_reconnect.py +221 -0
- agentirc_cli-0.18.0/tests/test_mesh_config.py +89 -0
- agentirc_cli-0.18.0/tests/test_persistence.py +103 -0
- agentirc_cli-0.18.0/tests/test_setup_update_cli.py +42 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/uv.lock +1 -1
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/.claude/skills/pr-review/SKILL.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/.github/workflows/pages.yml +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/.gitignore +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/.markdownlint-cli2.yaml +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/.pr_agent.toml +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/CLAUDE.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/CNAME +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/Gemfile +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/Gemfile.lock +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/LICENSE +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/README.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/_config.yml +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/_sass/color_schemes/anthropic.scss +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/_sass/custom/custom.scss +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/__main__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/agent_runner.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/config.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/daemon.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/ipc.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/irc_transport.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/message_buffer.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/skill/SKILL.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/skill/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/skill/irc_client.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/socket_server.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/supervisor.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/acp/webhook.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/__main__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/agent_runner.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/config.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/daemon.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/ipc.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/irc_transport.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/message_buffer.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/skill/SKILL.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/skill/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/skill/irc_client.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/socket_server.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/supervisor.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/claude/webhook.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/agent_runner.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/config.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/daemon.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/ipc.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/irc_transport.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/message_buffer.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/skill/SKILL.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/skill/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/skill/irc_client.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/socket_server.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/supervisor.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/codex/webhook.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/agent_runner.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/config.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/daemon.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/ipc.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/irc_transport.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/message_buffer.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/skill/SKILL.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/skill/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/skill/irc_client.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/socket_server.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/supervisor.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/clients/copilot/webhook.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/observer.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/overview/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/overview/collector.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/overview/model.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/overview/renderer_text.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/overview/renderer_web.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/overview/web/style.css +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/pidfile.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/commands.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/extensions/federation.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/extensions/history.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/extensions/rooms.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/extensions/tags.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/message.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/protocol-index.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/protocol/replies.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/__main__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/channel.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/client.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/config.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/remote_client.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/room_store.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/rooms_util.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/skill.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/skills/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/skills/history.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/agentirc/server/skills/rooms.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/agent-client.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/agent-harness-spec.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/ci.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/acp/overview.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/claude/configuration.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/claude/context-management.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/claude/irc-tools.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/claude/overview.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/claude/setup.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/claude/supervisor.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/claude/webhooks.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/codex/configuration.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/codex/context-management.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/codex/irc-tools.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/codex/overview.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/codex/setup.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/codex/supervisor.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/codex/webhooks.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/copilot/configuration.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/copilot/context-management.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/copilot/irc-tools.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/copilot/overview.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/copilot/setup.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/copilot/supervisor.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/clients/copilot/webhooks.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/codex-backend.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/copilot-backend.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/design.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/docs-site.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/getting-started.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/grow-your-agent.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/harness-conformance.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/layer1-core-irc.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/layer2-attention.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/layer3-skills.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/layer4-federation.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/layer5-agent-harness.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/overview.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/publishing.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/resources/github-copilot-sdk-instructions.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/rooms.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/server-architecture.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/01-pair-programming.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/02-code-review-ensemble.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/03-cross-server-delegation.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/04-knowledge-propagation.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/05-the-observer.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/06-cross-server-ops.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/07-supervisor-intervention.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/08-apps-as-agents.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/09-research-swarm.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases/10-grow-your-agent.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/docs/use-cases-index.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/index.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/README.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/config.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/daemon.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/ipc.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/irc_transport.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/message_buffer.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/skill/SKILL.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/skill/irc_client.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/socket_server.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/packages/agent-harness/webhook.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/plugins/claude-code/skills/irc/SKILL.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/plugins/codex/skills/agentirc-irc/SKILL.md +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/__init__.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/conftest.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_acp_daemon.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_agent_runner.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_channel.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_codex_daemon.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_connection.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_copilot_daemon.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_daemon.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_daemon_config.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_daemon_ipc.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_discovery.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_federation.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_history.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_integration_layer5.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_ipc.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_irc_transport.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_mentions.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_message.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_message_buffer.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_messaging.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_modes.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_overview_cli.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_overview_collector.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_overview_model.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_overview_renderer.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_overview_web.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_room_persistence.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_rooms.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_rooms_federation.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_rooms_integration.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_skill_client.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_skills.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_socket_server.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_supervisor.py +0 -0
- {agentirc_cli-0.16.4 → agentirc_cli-0.18.0}/tests/test_webhook.py +0 -0
|
@@ -4,6 +4,36 @@ 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
|
+
## [0.18.0] - 2026-04-02
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- S2S link auto-reconnect with exponential backoff (5s to 120s)
|
|
13
|
+
- Declarative mesh.yaml configuration for multi-machine setup
|
|
14
|
+
- Cross-platform auto-start persistence (systemd, launchd, Windows schtasks)
|
|
15
|
+
- agentirc setup command — bootstrap a machine into the mesh from mesh.yaml
|
|
16
|
+
- agentirc update command — upgrade package and gracefully restart all services
|
|
17
|
+
- --foreground flag for server start and agent start (required by service managers)
|
|
18
|
+
- Windows platform support guards (no fork, SIGTERM fallback)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- S2S links now auto-retry on initial startup failure
|
|
24
|
+
- SQUIT (intentional delink) suppresses reconnect attempts
|
|
25
|
+
- Incoming peer connections cancel outbound retry tasks
|
|
26
|
+
|
|
27
|
+
## [0.17.0] - 2026-04-01
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- Two-tier skill system: root-level admin skill (server setup, mesh linking, federation, agent lifecycle) and project-level messaging skill
|
|
33
|
+
- agentirc skills install now installs both admin and messaging skills for all backends
|
|
34
|
+
- Learn prompt includes server/mesh setup, agent lifecycle, and dual skill install instructions
|
|
35
|
+
- docs/agentic-self-learn.md documenting the two-tier skill system
|
|
36
|
+
|
|
7
37
|
## [0.16.4] - 2026-04-01
|
|
8
38
|
|
|
9
39
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentirc-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.18.0
|
|
4
4
|
Summary: 🌱 The space your agents deserve — an autonomous agent mesh where AI agents live, collaborate, and grow
|
|
5
5
|
Project-URL: Homepage, https://github.com/OriNachum/agentirc
|
|
6
6
|
Author: Ori Nachum
|
|
@@ -14,6 +14,8 @@ Subcommands:
|
|
|
14
14
|
agentirc sleep [nick] [--all] Pause agent(s) — stay connected but idle
|
|
15
15
|
agentirc wake [nick] [--all] Resume paused agent(s)
|
|
16
16
|
agentirc overview [--room X] [--agent X] Show mesh overview
|
|
17
|
+
agentirc setup [--config X] [--uninstall] Set up mesh from mesh.yaml
|
|
18
|
+
agentirc update [--dry-run] [--skip-upgrade] Upgrade and restart mesh
|
|
17
19
|
"""
|
|
18
20
|
from __future__ import annotations
|
|
19
21
|
|
|
@@ -22,7 +24,9 @@ import asyncio
|
|
|
22
24
|
import json
|
|
23
25
|
import logging
|
|
24
26
|
import os
|
|
27
|
+
import shutil
|
|
25
28
|
import signal
|
|
29
|
+
import subprocess
|
|
26
30
|
import sys
|
|
27
31
|
import time
|
|
28
32
|
|
|
@@ -92,6 +96,14 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
92
96
|
"--link", type=_parse_link, action="append", default=[],
|
|
93
97
|
help="Link to peer: name:host:port:password",
|
|
94
98
|
)
|
|
99
|
+
srv_start.add_argument(
|
|
100
|
+
"--mesh-config", default=None,
|
|
101
|
+
help="Read links from mesh.yaml + OS keyring (no passwords in CLI args)",
|
|
102
|
+
)
|
|
103
|
+
srv_start.add_argument(
|
|
104
|
+
"--foreground", action="store_true",
|
|
105
|
+
help="Run in foreground (for service managers)",
|
|
106
|
+
)
|
|
95
107
|
|
|
96
108
|
srv_stop = server_sub.add_parser("stop", help="Stop the IRC server daemon")
|
|
97
109
|
srv_stop.add_argument("--name", default="agentirc", help="Server name")
|
|
@@ -112,6 +124,10 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
112
124
|
start_parser.add_argument("nick", nargs="?", help="Agent nick to start")
|
|
113
125
|
start_parser.add_argument("--all", action="store_true", help="Start all agents")
|
|
114
126
|
start_parser.add_argument("--config", default=DEFAULT_CONFIG, help="Config file path")
|
|
127
|
+
start_parser.add_argument(
|
|
128
|
+
"--foreground", action="store_true",
|
|
129
|
+
help="Run in foreground (for service managers)",
|
|
130
|
+
)
|
|
115
131
|
|
|
116
132
|
# -- stop subcommand ---------------------------------------------------
|
|
117
133
|
stop_parser = sub.add_parser("stop", help="Stop agent daemon(s)")
|
|
@@ -181,6 +197,32 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
181
197
|
overview_parser.add_argument("--refresh", type=int, default=5, help="Web refresh interval in seconds (default: 5, min: 1)")
|
|
182
198
|
overview_parser.add_argument("--config", default=DEFAULT_CONFIG)
|
|
183
199
|
|
|
200
|
+
# -- setup subcommand --------------------------------------------------
|
|
201
|
+
setup_parser = sub.add_parser("setup", help="Set up mesh from mesh.yaml")
|
|
202
|
+
setup_parser.add_argument(
|
|
203
|
+
"--config", default=os.path.expanduser("~/.agentirc/mesh.yaml"),
|
|
204
|
+
help="Path to mesh.yaml",
|
|
205
|
+
)
|
|
206
|
+
setup_parser.add_argument(
|
|
207
|
+
"--uninstall", action="store_true",
|
|
208
|
+
help="Remove auto-start entries and stop services",
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
# -- update subcommand -------------------------------------------------
|
|
212
|
+
update_parser = sub.add_parser("update", help="Upgrade and restart the mesh")
|
|
213
|
+
update_parser.add_argument(
|
|
214
|
+
"--dry-run", action="store_true",
|
|
215
|
+
help="Show what would happen without executing",
|
|
216
|
+
)
|
|
217
|
+
update_parser.add_argument(
|
|
218
|
+
"--skip-upgrade", action="store_true",
|
|
219
|
+
help="Just restart, don't upgrade the package",
|
|
220
|
+
)
|
|
221
|
+
update_parser.add_argument(
|
|
222
|
+
"--config", default=os.path.expanduser("~/.agentirc/mesh.yaml"),
|
|
223
|
+
help="Path to mesh.yaml",
|
|
224
|
+
)
|
|
225
|
+
|
|
184
226
|
return parser
|
|
185
227
|
|
|
186
228
|
|
|
@@ -213,6 +255,8 @@ def main() -> None:
|
|
|
213
255
|
"wake": _cmd_wake,
|
|
214
256
|
"skills": _cmd_skills,
|
|
215
257
|
"overview": _cmd_overview,
|
|
258
|
+
"setup": _cmd_setup,
|
|
259
|
+
"update": _cmd_update,
|
|
216
260
|
}
|
|
217
261
|
handler = dispatch.get(args.command)
|
|
218
262
|
if handler:
|
|
@@ -253,10 +297,30 @@ def _server_start(args: argparse.Namespace) -> None:
|
|
|
253
297
|
print(f"Server '{args.name}' is already running (PID {existing})")
|
|
254
298
|
sys.exit(1)
|
|
255
299
|
|
|
300
|
+
# Resolve links: --mesh-config reads from mesh.yaml + OS keyring (no passwords in CLI)
|
|
301
|
+
links = list(args.link) # from --link args (may include passwords for manual use)
|
|
302
|
+
if getattr(args, "mesh_config", None):
|
|
303
|
+
links = _resolve_links_from_mesh(args.mesh_config)
|
|
304
|
+
|
|
305
|
+
if getattr(args, "foreground", False):
|
|
306
|
+
# Foreground mode — run directly (for service managers)
|
|
307
|
+
write_pid(pid_name, os.getpid())
|
|
308
|
+
os.makedirs(LOG_DIR, exist_ok=True)
|
|
309
|
+
print(f"Server '{args.name}' starting in foreground (PID {os.getpid()})")
|
|
310
|
+
print(f" Listening on {args.host}:{args.port}")
|
|
311
|
+
try:
|
|
312
|
+
asyncio.run(_run_server(args.name, args.host, args.port, links))
|
|
313
|
+
finally:
|
|
314
|
+
remove_pid(pid_name)
|
|
315
|
+
return
|
|
316
|
+
|
|
317
|
+
if sys.platform == "win32":
|
|
318
|
+
print("Daemon mode not supported on Windows. Use --foreground.", file=sys.stderr)
|
|
319
|
+
sys.exit(1)
|
|
320
|
+
|
|
256
321
|
# Fork to daemonize
|
|
257
322
|
pid = os.fork()
|
|
258
323
|
if pid > 0:
|
|
259
|
-
# Parent: wait briefly to check child started, then exit
|
|
260
324
|
time.sleep(0.2)
|
|
261
325
|
if is_process_alive(pid):
|
|
262
326
|
print(f"Server '{args.name}' started (PID {pid})")
|
|
@@ -278,17 +342,14 @@ def _server_start(args: argparse.Namespace) -> None:
|
|
|
278
342
|
os.dup2(log_fd, 2)
|
|
279
343
|
os.close(log_fd)
|
|
280
344
|
|
|
281
|
-
# Close stdin
|
|
282
345
|
devnull = os.open(os.devnull, os.O_RDONLY)
|
|
283
346
|
os.dup2(devnull, 0)
|
|
284
347
|
os.close(devnull)
|
|
285
348
|
|
|
286
|
-
# Write PID file
|
|
287
349
|
write_pid(pid_name, os.getpid())
|
|
288
350
|
|
|
289
|
-
# Run the server
|
|
290
351
|
try:
|
|
291
|
-
asyncio.run(_run_server(args.name, args.host, args.port,
|
|
352
|
+
asyncio.run(_run_server(args.name, args.host, args.port, links))
|
|
292
353
|
finally:
|
|
293
354
|
remove_pid(pid_name)
|
|
294
355
|
os._exit(0)
|
|
@@ -310,12 +371,17 @@ async def _run_server(name: str, host: str, port: int, links: list | None = None
|
|
|
310
371
|
await ircd.connect_to_peer(lc.host, lc.port, lc.password, lc.trust)
|
|
311
372
|
logger.info("Linking to %s at %s:%d", lc.name, lc.host, lc.port)
|
|
312
373
|
except Exception as e:
|
|
313
|
-
logger.error("Failed to link to %s: %s", lc.name, e)
|
|
374
|
+
logger.error("Failed to link to %s: %s — will retry", lc.name, e)
|
|
375
|
+
ircd.maybe_retry_link(lc.name)
|
|
314
376
|
|
|
315
377
|
stop_event = asyncio.Event()
|
|
316
378
|
loop = asyncio.get_event_loop()
|
|
317
379
|
for sig in (signal.SIGINT, signal.SIGTERM):
|
|
318
|
-
|
|
380
|
+
try:
|
|
381
|
+
loop.add_signal_handler(sig, stop_event.set)
|
|
382
|
+
except (NotImplementedError, RuntimeError):
|
|
383
|
+
# Windows / unsupported event loop: fall back to stdlib signals
|
|
384
|
+
signal.signal(sig, lambda *_: stop_event.set())
|
|
319
385
|
|
|
320
386
|
await stop_event.wait()
|
|
321
387
|
logger.info("Server '%s' shutting down", name)
|
|
@@ -347,11 +413,18 @@ def _server_stop(args: argparse.Namespace) -> None:
|
|
|
347
413
|
time.sleep(0.1)
|
|
348
414
|
|
|
349
415
|
# Force kill
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
416
|
+
if sys.platform == "win32":
|
|
417
|
+
print(f"Server '{args.name}' did not stop gracefully, terminating")
|
|
418
|
+
try:
|
|
419
|
+
os.kill(pid, signal.SIGTERM)
|
|
420
|
+
except ProcessLookupError:
|
|
421
|
+
pass
|
|
422
|
+
else:
|
|
423
|
+
print(f"Server '{args.name}' did not stop gracefully, sending SIGKILL")
|
|
424
|
+
try:
|
|
425
|
+
os.kill(pid, signal.SIGKILL)
|
|
426
|
+
except ProcessLookupError:
|
|
427
|
+
pass
|
|
355
428
|
remove_pid(pid_name)
|
|
356
429
|
print(f"Server '{args.name}' killed")
|
|
357
430
|
|
|
@@ -515,14 +588,28 @@ def _cmd_start(args: argparse.Namespace) -> None:
|
|
|
515
588
|
)
|
|
516
589
|
sys.exit(1)
|
|
517
590
|
|
|
518
|
-
|
|
519
|
-
|
|
591
|
+
foreground = getattr(args, "foreground", False)
|
|
592
|
+
|
|
593
|
+
if foreground:
|
|
594
|
+
if len(agents) != 1:
|
|
595
|
+
print("--foreground requires a single agent nick, not --all", file=sys.stderr)
|
|
596
|
+
sys.exit(1)
|
|
520
597
|
agent = agents[0]
|
|
521
|
-
print(f"Starting agent {agent.nick}...")
|
|
598
|
+
print(f"Starting agent {agent.nick} in foreground...")
|
|
522
599
|
asyncio.run(_run_single_agent(config, agent))
|
|
523
600
|
else:
|
|
524
|
-
|
|
525
|
-
|
|
601
|
+
if sys.platform == "win32":
|
|
602
|
+
if len(agents) == 1:
|
|
603
|
+
# Windows has no fork — run single agent in foreground
|
|
604
|
+
agent = agents[0]
|
|
605
|
+
print(f"Starting agent {agent.nick}...")
|
|
606
|
+
asyncio.run(_run_single_agent(config, agent))
|
|
607
|
+
else:
|
|
608
|
+
print("Multi-agent daemon mode not supported on Windows. Start agents individually.", file=sys.stderr)
|
|
609
|
+
sys.exit(1)
|
|
610
|
+
else:
|
|
611
|
+
# Daemonize all agents (fork each into background)
|
|
612
|
+
_run_multi_agents(config, agents)
|
|
526
613
|
|
|
527
614
|
|
|
528
615
|
async def _run_single_agent(config: DaemonConfig, agent: AgentConfig) -> None:
|
|
@@ -720,11 +807,18 @@ def _stop_agent(nick: str) -> None:
|
|
|
720
807
|
time.sleep(0.1)
|
|
721
808
|
|
|
722
809
|
# Force kill
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
810
|
+
if sys.platform == "win32":
|
|
811
|
+
print(f"Agent '{nick}' did not stop gracefully, terminating")
|
|
812
|
+
try:
|
|
813
|
+
os.kill(pid, signal.SIGTERM)
|
|
814
|
+
except ProcessLookupError:
|
|
815
|
+
pass
|
|
816
|
+
else:
|
|
817
|
+
print(f"Agent '{nick}' did not stop gracefully, sending SIGKILL")
|
|
818
|
+
try:
|
|
819
|
+
os.kill(pid, signal.SIGKILL)
|
|
820
|
+
except ProcessLookupError:
|
|
821
|
+
pass
|
|
728
822
|
remove_pid(pid_name)
|
|
729
823
|
print(f"Agent '{nick}' killed")
|
|
730
824
|
|
|
@@ -1010,12 +1104,30 @@ def _cmd_channels(args: argparse.Namespace) -> None:
|
|
|
1010
1104
|
# Skills install
|
|
1011
1105
|
# -----------------------------------------------------------------------
|
|
1012
1106
|
|
|
1107
|
+
def _get_bundled_admin_skill_path() -> str:
|
|
1108
|
+
"""Return the path to the bundled admin SKILL.md in the installed package."""
|
|
1109
|
+
import agentirc
|
|
1110
|
+
return os.path.join(os.path.dirname(agentirc.__file__), "skills", "agentirc", "SKILL.md")
|
|
1111
|
+
|
|
1112
|
+
|
|
1013
1113
|
def _get_bundled_skill_path() -> str:
|
|
1014
1114
|
"""Return the path to the bundled SKILL.md in the installed package."""
|
|
1015
1115
|
import agentirc
|
|
1016
1116
|
return os.path.join(os.path.dirname(agentirc.__file__), "clients", "claude", "skill", "SKILL.md")
|
|
1017
1117
|
|
|
1018
1118
|
|
|
1119
|
+
def _install_admin_skill(root_dir: str, label: str) -> None:
|
|
1120
|
+
"""Install the admin/ops skill to the given root skills directory."""
|
|
1121
|
+
src = _get_bundled_admin_skill_path()
|
|
1122
|
+
dest_dir = os.path.join(os.path.expanduser(root_dir), "agentirc")
|
|
1123
|
+
dest = os.path.join(dest_dir, "SKILL.md")
|
|
1124
|
+
|
|
1125
|
+
os.makedirs(dest_dir, exist_ok=True)
|
|
1126
|
+
import shutil
|
|
1127
|
+
shutil.copy2(src, dest)
|
|
1128
|
+
print(f"Installed {label} admin skill: {dest}")
|
|
1129
|
+
|
|
1130
|
+
|
|
1019
1131
|
def _install_skill_claude() -> None:
|
|
1020
1132
|
"""Install IRC skill for Claude Code."""
|
|
1021
1133
|
src = _get_bundled_skill_path()
|
|
@@ -1025,7 +1137,8 @@ def _install_skill_claude() -> None:
|
|
|
1025
1137
|
os.makedirs(dest_dir, exist_ok=True)
|
|
1026
1138
|
import shutil
|
|
1027
1139
|
shutil.copy2(src, dest)
|
|
1028
|
-
print(f"Installed Claude Code skill: {dest}")
|
|
1140
|
+
print(f"Installed Claude Code messaging skill: {dest}")
|
|
1141
|
+
_install_admin_skill("~/.claude/skills", "Claude Code")
|
|
1029
1142
|
|
|
1030
1143
|
|
|
1031
1144
|
def _get_bundled_codex_skill_path() -> str:
|
|
@@ -1043,7 +1156,8 @@ def _install_skill_codex() -> None:
|
|
|
1043
1156
|
os.makedirs(dest_dir, exist_ok=True)
|
|
1044
1157
|
import shutil
|
|
1045
1158
|
shutil.copy2(src, dest)
|
|
1046
|
-
print(f"Installed Codex skill: {dest}")
|
|
1159
|
+
print(f"Installed Codex messaging skill: {dest}")
|
|
1160
|
+
_install_admin_skill("~/.agents/skills", "Codex")
|
|
1047
1161
|
|
|
1048
1162
|
|
|
1049
1163
|
def _get_bundled_copilot_skill_path() -> str:
|
|
@@ -1061,7 +1175,8 @@ def _install_skill_copilot() -> None:
|
|
|
1061
1175
|
os.makedirs(dest_dir, exist_ok=True)
|
|
1062
1176
|
import shutil
|
|
1063
1177
|
shutil.copy2(src, dest)
|
|
1064
|
-
print(f"Installed Copilot skill: {dest}")
|
|
1178
|
+
print(f"Installed Copilot messaging skill: {dest}")
|
|
1179
|
+
_install_admin_skill("~/.copilot_skills", "Copilot")
|
|
1065
1180
|
|
|
1066
1181
|
|
|
1067
1182
|
def _get_bundled_acp_skill_path() -> str:
|
|
@@ -1079,7 +1194,8 @@ def _install_skill_acp() -> None:
|
|
|
1079
1194
|
os.makedirs(dest_dir, exist_ok=True)
|
|
1080
1195
|
import shutil
|
|
1081
1196
|
shutil.copy2(src, dest)
|
|
1082
|
-
print(f"Installed ACP skill: {dest}")
|
|
1197
|
+
print(f"Installed ACP messaging skill: {dest}")
|
|
1198
|
+
_install_admin_skill("~/.acp/skills", "ACP")
|
|
1083
1199
|
|
|
1084
1200
|
|
|
1085
1201
|
def _cmd_skills(args: argparse.Namespace) -> None:
|
|
@@ -1142,3 +1258,322 @@ def _cmd_overview(args: argparse.Namespace) -> None:
|
|
|
1142
1258
|
message_limit=message_limit,
|
|
1143
1259
|
)
|
|
1144
1260
|
print(output, end="")
|
|
1261
|
+
|
|
1262
|
+
|
|
1263
|
+
# -----------------------------------------------------------------------
|
|
1264
|
+
# Credential helpers
|
|
1265
|
+
# -----------------------------------------------------------------------
|
|
1266
|
+
|
|
1267
|
+
def _resolve_links_from_mesh(mesh_config_path: str) -> list:
|
|
1268
|
+
"""Load link configs from mesh.yaml, looking up passwords from OS keyring."""
|
|
1269
|
+
from agentirc.mesh_config import load_mesh_config
|
|
1270
|
+
from agentirc.credentials import lookup_credential
|
|
1271
|
+
from agentirc.server.config import LinkConfig
|
|
1272
|
+
|
|
1273
|
+
mesh = load_mesh_config(mesh_config_path)
|
|
1274
|
+
links = []
|
|
1275
|
+
for lc in mesh.server.links:
|
|
1276
|
+
password = lookup_credential(lc.name)
|
|
1277
|
+
if not password:
|
|
1278
|
+
logger.warning(
|
|
1279
|
+
"No credential found for peer '%s' — link will not be established. "
|
|
1280
|
+
"Run 'agentirc setup' to store link passwords.", lc.name
|
|
1281
|
+
)
|
|
1282
|
+
continue
|
|
1283
|
+
links.append(LinkConfig(
|
|
1284
|
+
name=lc.name, host=lc.host, port=lc.port,
|
|
1285
|
+
password=password, trust=lc.trust,
|
|
1286
|
+
))
|
|
1287
|
+
return links
|
|
1288
|
+
|
|
1289
|
+
|
|
1290
|
+
# -----------------------------------------------------------------------
|
|
1291
|
+
# Shared helpers for setup / update
|
|
1292
|
+
# -----------------------------------------------------------------------
|
|
1293
|
+
|
|
1294
|
+
def _build_server_start_cmd(mesh, agentirc_bin: str, mesh_config_path: str) -> list[str]:
|
|
1295
|
+
"""Build the server start command with --foreground and --mesh-config.
|
|
1296
|
+
|
|
1297
|
+
Passwords are NOT included in the command — they come from the OS keyring
|
|
1298
|
+
at startup via --mesh-config.
|
|
1299
|
+
"""
|
|
1300
|
+
return [
|
|
1301
|
+
agentirc_bin, "server", "start", "--foreground",
|
|
1302
|
+
"--name", mesh.server.name,
|
|
1303
|
+
"--host", mesh.server.host,
|
|
1304
|
+
"--port", str(mesh.server.port),
|
|
1305
|
+
"--mesh-config", mesh_config_path,
|
|
1306
|
+
]
|
|
1307
|
+
|
|
1308
|
+
|
|
1309
|
+
# -----------------------------------------------------------------------
|
|
1310
|
+
# Setup — mesh.yaml → auto-start services
|
|
1311
|
+
# -----------------------------------------------------------------------
|
|
1312
|
+
|
|
1313
|
+
def _cmd_setup(args: argparse.Namespace) -> None:
|
|
1314
|
+
import getpass
|
|
1315
|
+
from agentirc.mesh_config import load_mesh_config
|
|
1316
|
+
from agentirc.persistence import install_service, uninstall_service, list_services
|
|
1317
|
+
|
|
1318
|
+
try:
|
|
1319
|
+
mesh = load_mesh_config(args.config)
|
|
1320
|
+
except FileNotFoundError:
|
|
1321
|
+
print(f"Mesh config not found: {args.config}", file=sys.stderr)
|
|
1322
|
+
print("Create it manually or ask your AI agent to generate it.", file=sys.stderr)
|
|
1323
|
+
sys.exit(1)
|
|
1324
|
+
|
|
1325
|
+
server_name = mesh.server.name
|
|
1326
|
+
|
|
1327
|
+
if args.uninstall:
|
|
1328
|
+
print("Uninstalling agentirc services...")
|
|
1329
|
+
# Only remove services for this node (not other mesh nodes)
|
|
1330
|
+
expected = {f"agentirc-server-{server_name}"}
|
|
1331
|
+
for agent in mesh.agents:
|
|
1332
|
+
expected.add(f"agentirc-agent-{server_name}-{agent.nick}")
|
|
1333
|
+
for svc in list_services():
|
|
1334
|
+
if svc in expected:
|
|
1335
|
+
print(f" Removing {svc}")
|
|
1336
|
+
uninstall_service(svc)
|
|
1337
|
+
_server_stop_by_name(server_name)
|
|
1338
|
+
for agent in mesh.agents:
|
|
1339
|
+
full_nick = f"{server_name}-{agent.nick}"
|
|
1340
|
+
_stop_agent(full_nick)
|
|
1341
|
+
print("Done.")
|
|
1342
|
+
return
|
|
1343
|
+
|
|
1344
|
+
# Prompt for link passwords and store in OS keyring (never in files)
|
|
1345
|
+
from agentirc.credentials import store_credential, lookup_credential
|
|
1346
|
+
|
|
1347
|
+
for link in mesh.server.links:
|
|
1348
|
+
existing = lookup_credential(link.name)
|
|
1349
|
+
if existing:
|
|
1350
|
+
print(f" Credential for '{link.name}' already in keyring")
|
|
1351
|
+
else:
|
|
1352
|
+
password = getpass.getpass(f"Link password for {link.name}: ")
|
|
1353
|
+
if store_credential(link.name, password):
|
|
1354
|
+
print(f" Stored credential for '{link.name}' in OS keyring")
|
|
1355
|
+
else:
|
|
1356
|
+
print(f" Warning: failed to store credential for '{link.name}'", file=sys.stderr)
|
|
1357
|
+
print(f" You may need to install secret-tool (Linux) or check Keychain access (macOS)", file=sys.stderr)
|
|
1358
|
+
|
|
1359
|
+
# Generate agents.yaml for each workdir
|
|
1360
|
+
from agentirc.clients.claude.config import (
|
|
1361
|
+
AgentConfig as BaseAgentConfig,
|
|
1362
|
+
DaemonConfig,
|
|
1363
|
+
ServerConnConfig,
|
|
1364
|
+
save_config,
|
|
1365
|
+
)
|
|
1366
|
+
|
|
1367
|
+
workdir_agents: dict[str, list] = {}
|
|
1368
|
+
for agent in mesh.agents:
|
|
1369
|
+
workdir = os.path.expanduser(agent.workdir)
|
|
1370
|
+
workdir_agents.setdefault(workdir, []).append(agent)
|
|
1371
|
+
|
|
1372
|
+
for workdir, agents in workdir_agents.items():
|
|
1373
|
+
os.makedirs(workdir, exist_ok=True)
|
|
1374
|
+
config_path = os.path.join(workdir, ".agentirc", "agents.yaml")
|
|
1375
|
+
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
|
1376
|
+
|
|
1377
|
+
agent_configs = []
|
|
1378
|
+
for a in agents:
|
|
1379
|
+
full_nick = f"{server_name}-{a.nick}"
|
|
1380
|
+
agent_configs.append(BaseAgentConfig(
|
|
1381
|
+
nick=full_nick,
|
|
1382
|
+
agent=a.type,
|
|
1383
|
+
directory=workdir,
|
|
1384
|
+
channels=list(a.channels),
|
|
1385
|
+
))
|
|
1386
|
+
|
|
1387
|
+
daemon_config = DaemonConfig(
|
|
1388
|
+
server=ServerConnConfig(name=server_name, host="localhost", port=mesh.server.port),
|
|
1389
|
+
agents=agent_configs,
|
|
1390
|
+
)
|
|
1391
|
+
save_config(config_path, daemon_config)
|
|
1392
|
+
print(f" Wrote {config_path}")
|
|
1393
|
+
|
|
1394
|
+
# Install auto-start services
|
|
1395
|
+
agentirc_bin = shutil.which("agentirc") or "agentirc"
|
|
1396
|
+
server_cmd = _build_server_start_cmd(mesh, agentirc_bin, args.config)
|
|
1397
|
+
svc_name = f"agentirc-server-{server_name}"
|
|
1398
|
+
path = install_service(svc_name, server_cmd, f"agentirc server {server_name}")
|
|
1399
|
+
print(f" Installed {svc_name} → {path}")
|
|
1400
|
+
|
|
1401
|
+
for agent in mesh.agents:
|
|
1402
|
+
full_nick = f"{server_name}-{agent.nick}"
|
|
1403
|
+
workdir = os.path.expanduser(agent.workdir)
|
|
1404
|
+
config_path = os.path.join(workdir, ".agentirc", "agents.yaml")
|
|
1405
|
+
agent_cmd = [agentirc_bin, "start", full_nick, "--foreground", "--config", config_path]
|
|
1406
|
+
agent_svc = f"agentirc-agent-{full_nick}"
|
|
1407
|
+
path = install_service(agent_svc, agent_cmd, f"agentirc agent {full_nick}")
|
|
1408
|
+
print(f" Installed {agent_svc} → {path}")
|
|
1409
|
+
|
|
1410
|
+
print(f"\nSetup complete for mesh node '{server_name}'.")
|
|
1411
|
+
print(f"Services installed. Start with your service manager or reboot.")
|
|
1412
|
+
|
|
1413
|
+
|
|
1414
|
+
def _server_stop_by_name(name: str) -> None:
|
|
1415
|
+
"""Stop a server by name (helper for setup --uninstall and update)."""
|
|
1416
|
+
pid_name = f"server-{name}"
|
|
1417
|
+
pid = read_pid(pid_name)
|
|
1418
|
+
if not pid or not is_process_alive(pid):
|
|
1419
|
+
if pid:
|
|
1420
|
+
remove_pid(pid_name)
|
|
1421
|
+
return
|
|
1422
|
+
|
|
1423
|
+
os.kill(pid, signal.SIGTERM)
|
|
1424
|
+
for _ in range(50):
|
|
1425
|
+
if not is_process_alive(pid):
|
|
1426
|
+
remove_pid(pid_name)
|
|
1427
|
+
return
|
|
1428
|
+
time.sleep(0.1)
|
|
1429
|
+
|
|
1430
|
+
# Escalate to SIGKILL (SIGTERM on Windows)
|
|
1431
|
+
if sys.platform == "win32":
|
|
1432
|
+
try:
|
|
1433
|
+
os.kill(pid, signal.SIGTERM)
|
|
1434
|
+
except ProcessLookupError:
|
|
1435
|
+
pass
|
|
1436
|
+
else:
|
|
1437
|
+
try:
|
|
1438
|
+
os.kill(pid, signal.SIGKILL)
|
|
1439
|
+
except ProcessLookupError:
|
|
1440
|
+
pass
|
|
1441
|
+
remove_pid(pid_name)
|
|
1442
|
+
|
|
1443
|
+
|
|
1444
|
+
# -----------------------------------------------------------------------
|
|
1445
|
+
# Update — upgrade + restart
|
|
1446
|
+
# -----------------------------------------------------------------------
|
|
1447
|
+
|
|
1448
|
+
def _cmd_update(args: argparse.Namespace) -> None:
|
|
1449
|
+
from agentirc.mesh_config import load_mesh_config
|
|
1450
|
+
|
|
1451
|
+
try:
|
|
1452
|
+
mesh = load_mesh_config(args.config)
|
|
1453
|
+
except FileNotFoundError:
|
|
1454
|
+
print(f"Mesh config not found: {args.config}", file=sys.stderr)
|
|
1455
|
+
sys.exit(1)
|
|
1456
|
+
|
|
1457
|
+
server_name = mesh.server.name
|
|
1458
|
+
|
|
1459
|
+
if not args.skip_upgrade:
|
|
1460
|
+
if args.dry_run:
|
|
1461
|
+
print("[dry-run] Would run: uv tool upgrade agentirc-cli")
|
|
1462
|
+
print("[dry-run] Would re-exec with --skip-upgrade")
|
|
1463
|
+
return
|
|
1464
|
+
|
|
1465
|
+
# Upgrade the package
|
|
1466
|
+
uv = shutil.which("uv")
|
|
1467
|
+
if uv:
|
|
1468
|
+
print("Upgrading via uv...")
|
|
1469
|
+
result = subprocess.run(
|
|
1470
|
+
[uv, "tool", "upgrade", "agentirc-cli"],
|
|
1471
|
+
capture_output=True, text=True,
|
|
1472
|
+
)
|
|
1473
|
+
print(result.stdout.strip() if result.stdout else "")
|
|
1474
|
+
if result.returncode != 0:
|
|
1475
|
+
print(f"uv upgrade failed: {result.stderr}", file=sys.stderr)
|
|
1476
|
+
sys.exit(1)
|
|
1477
|
+
else:
|
|
1478
|
+
pip = shutil.which("pip") or shutil.which("pip3")
|
|
1479
|
+
if pip:
|
|
1480
|
+
print("Upgrading via pip...")
|
|
1481
|
+
result = subprocess.run(
|
|
1482
|
+
[pip, "install", "--upgrade", "agentirc-cli"],
|
|
1483
|
+
capture_output=True, text=True,
|
|
1484
|
+
)
|
|
1485
|
+
if result.returncode != 0:
|
|
1486
|
+
print(f"pip upgrade failed: {result.stderr}", file=sys.stderr)
|
|
1487
|
+
sys.exit(1)
|
|
1488
|
+
else:
|
|
1489
|
+
print("Neither uv nor pip found", file=sys.stderr)
|
|
1490
|
+
sys.exit(1)
|
|
1491
|
+
|
|
1492
|
+
# Re-exec with new binary so restart uses new code
|
|
1493
|
+
agentirc_bin = shutil.which("agentirc") or "agentirc"
|
|
1494
|
+
reexec_args = [agentirc_bin, "update", "--skip-upgrade", "--config", args.config]
|
|
1495
|
+
print("Re-executing with updated code...")
|
|
1496
|
+
if sys.platform == "win32":
|
|
1497
|
+
sys.exit(subprocess.run(reexec_args).returncode)
|
|
1498
|
+
else:
|
|
1499
|
+
os.execvp(agentirc_bin, reexec_args)
|
|
1500
|
+
|
|
1501
|
+
# --skip-upgrade path: restart everything
|
|
1502
|
+
print(f"Restarting mesh node '{server_name}'...")
|
|
1503
|
+
|
|
1504
|
+
if args.dry_run:
|
|
1505
|
+
for agent in mesh.agents:
|
|
1506
|
+
print(f"[dry-run] Would stop agent {server_name}-{agent.nick}")
|
|
1507
|
+
print(f"[dry-run] Would stop server {server_name}")
|
|
1508
|
+
print(f"[dry-run] Would regenerate auto-start entries")
|
|
1509
|
+
print(f"[dry-run] Would start server {server_name}")
|
|
1510
|
+
for agent in mesh.agents:
|
|
1511
|
+
print(f"[dry-run] Would start agent {server_name}-{agent.nick}")
|
|
1512
|
+
return
|
|
1513
|
+
|
|
1514
|
+
# Stop agents
|
|
1515
|
+
for agent in mesh.agents:
|
|
1516
|
+
full_nick = f"{server_name}-{agent.nick}"
|
|
1517
|
+
print(f" Stopping {full_nick}...")
|
|
1518
|
+
_stop_agent(full_nick)
|
|
1519
|
+
|
|
1520
|
+
# Stop server
|
|
1521
|
+
print(f" Stopping server {server_name}...")
|
|
1522
|
+
_server_stop_by_name(server_name)
|
|
1523
|
+
|
|
1524
|
+
# Regenerate auto-start entries
|
|
1525
|
+
from agentirc.persistence import install_service
|
|
1526
|
+
|
|
1527
|
+
agentirc_bin = shutil.which("agentirc") or "agentirc"
|
|
1528
|
+
server_cmd = _build_server_start_cmd(mesh, agentirc_bin, args.config)
|
|
1529
|
+
install_service(f"agentirc-server-{server_name}", server_cmd, f"agentirc server {server_name}")
|
|
1530
|
+
|
|
1531
|
+
for agent in mesh.agents:
|
|
1532
|
+
full_nick = f"{server_name}-{agent.nick}"
|
|
1533
|
+
workdir = os.path.expanduser(agent.workdir)
|
|
1534
|
+
config_path = os.path.join(workdir, ".agentirc", "agents.yaml")
|
|
1535
|
+
agent_cmd = [agentirc_bin, "start", full_nick, "--foreground", "--config", config_path]
|
|
1536
|
+
install_service(f"agentirc-agent-{full_nick}", agent_cmd, f"agentirc agent {full_nick}")
|
|
1537
|
+
|
|
1538
|
+
# Restart services via platform service manager
|
|
1539
|
+
from agentirc.persistence import restart_service
|
|
1540
|
+
|
|
1541
|
+
server_svc = f"agentirc-server-{server_name}"
|
|
1542
|
+
print(f" Restarting {server_svc}...")
|
|
1543
|
+
if not restart_service(server_svc):
|
|
1544
|
+
# Fallback: start via CLI if no service file installed
|
|
1545
|
+
if sys.platform == "win32":
|
|
1546
|
+
print(f" No service file found. Run 'agentirc setup' to install services.", file=sys.stderr)
|
|
1547
|
+
else:
|
|
1548
|
+
print(f" No service file found, starting via CLI...")
|
|
1549
|
+
subprocess.run([
|
|
1550
|
+
agentirc_bin, "server", "start",
|
|
1551
|
+
"--name", server_name,
|
|
1552
|
+
"--host", mesh.server.host,
|
|
1553
|
+
"--port", str(mesh.server.port),
|
|
1554
|
+
"--mesh-config", args.config,
|
|
1555
|
+
], check=False)
|
|
1556
|
+
|
|
1557
|
+
# Wait for server to be ready
|
|
1558
|
+
import socket as _socket
|
|
1559
|
+
for _ in range(50):
|
|
1560
|
+
try:
|
|
1561
|
+
with _socket.create_connection(("localhost", mesh.server.port), timeout=1):
|
|
1562
|
+
break
|
|
1563
|
+
except (ConnectionRefusedError, OSError):
|
|
1564
|
+
time.sleep(0.1)
|
|
1565
|
+
|
|
1566
|
+
for agent in mesh.agents:
|
|
1567
|
+
full_nick = f"{server_name}-{agent.nick}"
|
|
1568
|
+
agent_svc = f"agentirc-agent-{full_nick}"
|
|
1569
|
+
print(f" Restarting {agent_svc}...")
|
|
1570
|
+
if not restart_service(agent_svc):
|
|
1571
|
+
# Fallback: start via CLI
|
|
1572
|
+
workdir = os.path.expanduser(agent.workdir)
|
|
1573
|
+
config_path = os.path.join(workdir, ".agentirc", "agents.yaml")
|
|
1574
|
+
subprocess.run(
|
|
1575
|
+
[agentirc_bin, "start", full_nick, "--config", config_path],
|
|
1576
|
+
check=False,
|
|
1577
|
+
)
|
|
1578
|
+
|
|
1579
|
+
print(f"\nUpdate complete. All services restarted.")
|