agentirc-cli 9.0.0__tar.gz → 9.2.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-9.2.0/CHANGELOG.md +118 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/CLAUDE.md +43 -17
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/PKG-INFO +5 -1
- agentirc_cli-9.2.0/agentirc/_internal/aio.py +12 -0
- agentirc_cli-9.2.0/agentirc/_internal/bots/__init__.py +0 -0
- agentirc_cli-9.2.0/agentirc/_internal/bots/bot_manager.py +38 -0
- agentirc_cli-9.2.0/agentirc/_internal/bots/http_listener.py +28 -0
- agentirc_cli-9.2.0/agentirc/_internal/cli_shared/__init__.py +0 -0
- agentirc_cli-9.2.0/agentirc/_internal/cli_shared/constants.py +56 -0
- agentirc_cli-9.2.0/agentirc/_internal/cli_shared/mesh.py +39 -0
- agentirc_cli-9.2.0/agentirc/_internal/constants.py +17 -0
- agentirc_cli-9.2.0/agentirc/_internal/pidfile.py +238 -0
- agentirc_cli-9.2.0/agentirc/_internal/protocol/__init__.py +0 -0
- agentirc_cli-9.2.0/agentirc/_internal/protocol/message.py +128 -0
- agentirc_cli-9.2.0/agentirc/_internal/protocol/replies.py +52 -0
- agentirc_cli-9.2.0/agentirc/_internal/telemetry/__init__.py +34 -0
- agentirc_cli-9.2.0/agentirc/_internal/telemetry/audit.py +394 -0
- agentirc_cli-9.2.0/agentirc/_internal/telemetry/context.py +121 -0
- agentirc_cli-9.2.0/agentirc/_internal/telemetry/metrics.py +250 -0
- agentirc_cli-9.2.0/agentirc/_internal/telemetry/tracing.py +117 -0
- agentirc_cli-9.2.0/agentirc/_internal/virtual_client.py +231 -0
- agentirc_cli-9.2.0/agentirc/channel.py +80 -0
- agentirc_cli-9.2.0/agentirc/cli.py +659 -0
- agentirc_cli-9.2.0/agentirc/client.py +1070 -0
- agentirc_cli-9.2.0/agentirc/config.py +49 -0
- agentirc_cli-9.2.0/agentirc/events.py +117 -0
- agentirc_cli-9.2.0/agentirc/history_store.py +91 -0
- agentirc_cli-9.2.0/agentirc/ircd.py +718 -0
- agentirc_cli-9.2.0/agentirc/protocol.py +259 -0
- agentirc_cli-9.2.0/agentirc/remote_client.py +43 -0
- agentirc_cli-9.2.0/agentirc/room_store.py +71 -0
- agentirc_cli-9.2.0/agentirc/rooms_util.py +56 -0
- agentirc_cli-9.2.0/agentirc/server_link.py +1166 -0
- agentirc_cli-9.2.0/agentirc/skill.py +61 -0
- agentirc_cli-9.2.0/agentirc/skills/__init__.py +0 -0
- agentirc_cli-9.2.0/agentirc/skills/history.py +225 -0
- agentirc_cli-9.2.0/agentirc/skills/icon.py +52 -0
- agentirc_cli-9.2.0/agentirc/skills/rooms.py +834 -0
- agentirc_cli-9.2.0/agentirc/skills/threads.py +709 -0
- agentirc_cli-9.2.0/agentirc/thread_store.py +52 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/docs/superpowers/specs/2026-04-30-bootstrap-design.md +35 -21
- agentirc_cli-9.2.0/pyproject.toml +304 -0
- agentirc_cli-9.2.0/tests/__init__.py +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/tests/test_cli.py +35 -6
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/uv.lock +227 -2
- agentirc_cli-9.0.0/agentirc/cli.py +0 -104
- agentirc_cli-9.0.0/pyproject.toml +0 -51
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/.claude/skills/pr-review/SKILL.md +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/.claude/skills/pr-review/scripts/portability-lint.sh +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/.claude/skills/pr-review/scripts/pr-batch.sh +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/.claude/skills/pr-review/scripts/pr-comments.sh +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/.claude/skills/pr-review/scripts/pr-reply.sh +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/.claude/skills/pr-review/scripts/pr-status.sh +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/.claude/skills/pr-review/scripts/workflow.sh +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/.claude/skills.local.yaml.example +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/.gitignore +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/LICENSE +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/README.md +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/agentirc/__init__.py +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/agentirc/__main__.py +0 -0
- {agentirc_cli-9.0.0/tests → agentirc_cli-9.2.0/agentirc/_internal}/__init__.py +0 -0
- {agentirc_cli-9.0.0 → agentirc_cli-9.2.0}/docs/steward/onboarding.md +0 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/).
|
|
6
|
+
|
|
7
|
+
## [9.2.0] - 2026-05-01
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `agentirc/protocol.py` — public, semver-tracked module consolidating
|
|
12
|
+
IRC verb names, numeric reply codes (re-exported from
|
|
13
|
+
`_internal.protocol.replies`), and IRCv3 / agentirc tag names. Wire-
|
|
14
|
+
format quirks (`ROOMETAEND`, `ROOMETASET` typos; `ERR_NOSUCHCHANNEL`
|
|
15
|
+
semantic misuse; `STHREAD` verb collapse) are preserved verbatim with
|
|
16
|
+
explanatory comments — they require coordinated cross-repo bumps to
|
|
17
|
+
fix.
|
|
18
|
+
- `agentirc/client.py` — IRC client transport vendored from
|
|
19
|
+
`culture/agentirc/client.py` at SHA `df50942`. Body unchanged; only
|
|
20
|
+
imports rewritten. The bootstrap spec originally said this would
|
|
21
|
+
"stay in culture" but its dependency surface (already-vendored
|
|
22
|
+
`_internal` support modules + opentelemetry) made vendoring the
|
|
23
|
+
cleaner path. Without it, `agentirc/ircd.py:580`'s runtime
|
|
24
|
+
`from agentirc.client import Client` raised `ImportError` on the
|
|
25
|
+
first TCP IRC connection.
|
|
26
|
+
- Real `agentirc/cli.py` — verb dispatch extracted from
|
|
27
|
+
`culture/cli/server.py`. New verbs: `serve` (foreground, no PID;
|
|
28
|
+
for systemd `Type=simple` and containers), `restart`, `link`
|
|
29
|
+
(peer-spec validator), `logs` (cat / tail of `~/.culture/logs/server-
|
|
30
|
+
<name>.log`). Existing verbs `start`/`stop`/`status` reuse culture's
|
|
31
|
+
proven daemonize / `_wait_for_port` / `_wait_for_graceful_stop` /
|
|
32
|
+
`_force_kill` helpers.
|
|
33
|
+
- Internal support modules:
|
|
34
|
+
- `agentirc/_internal/pidfile.py` — PID/port file management.
|
|
35
|
+
`is_managed_process()` recognizes both `culture` and
|
|
36
|
+
`agentirc`/`agentirc-cli` argv tokens; `is_culture_process` is
|
|
37
|
+
preserved as a thin alias.
|
|
38
|
+
- `agentirc/_internal/cli_shared/{constants,mesh}.py` — minimal
|
|
39
|
+
subset of `culture/cli/shared`. Keeps `DEFAULT_CONFIG`, `LOG_DIR`,
|
|
40
|
+
`culture_runtime_dir()`, `parse_link()`. Drops everything that
|
|
41
|
+
touched `culture.bots.config`, `culture.credentials`,
|
|
42
|
+
`culture.mesh_config`.
|
|
43
|
+
- Citations recorded in `[tool.citation]`: `culture-pidfile`,
|
|
44
|
+
`culture-cli-shared`, `culture-client`, `culture-cli-server`.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- Default server name changed from `culture` to `agentirc` in
|
|
49
|
+
`agentirc.cli`. PID/port files at `~/.culture/pids/server-<name>.{pid,
|
|
50
|
+
port}` keep their existing layout per the "Defaults preserve culture
|
|
51
|
+
continuity" rule, but the default fallback name no longer collides
|
|
52
|
+
with culture's daemon when both run on the same host.
|
|
53
|
+
- Dropped culture-only verbs (`default`, `rename`, `archive`,
|
|
54
|
+
`unarchive`) from agentirc's CLI surface — they manage culture's
|
|
55
|
+
agent manifest, which agentirc does not own.
|
|
56
|
+
- Dropped `--mesh-config` from `agentirc start` — depends on
|
|
57
|
+
`culture.credentials` / `culture.mesh_config` (out of scope).
|
|
58
|
+
|
|
59
|
+
### Notes
|
|
60
|
+
|
|
61
|
+
- End-to-end smoke verified: `agentirc start --port 16667` boots,
|
|
62
|
+
TCP NICK/USER handshake returns `001 RPL_WELCOME` from a real
|
|
63
|
+
`IRCd`, `agentirc stop` shuts cleanly. `agentirc serve` is now
|
|
64
|
+
byte-indistinguishable from `culture server start` for the lifecycle
|
|
65
|
+
contract culture's shim relies on.
|
|
66
|
+
- Test suite migration (PR-B3) is the only remaining bootstrap slice.
|
|
67
|
+
|
|
68
|
+
## [9.1.0] - 2026-04-30
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
|
|
72
|
+
- Server-core vendored from `culture` at SHA `df50942`. The `agentirc`
|
|
73
|
+
package now contains the IRCd (`ircd.py`), server-to-server linking
|
|
74
|
+
(`server_link.py`), channel/event/store/skill modules, `remote_client.py`
|
|
75
|
+
(peer-server ghost client), and the four built-in skills
|
|
76
|
+
(`skills/{rooms,threads,history,icon}.py`).
|
|
77
|
+
- Internal vendored support modules under `agentirc/_internal/`:
|
|
78
|
+
- `aio` (`maybe_await`)
|
|
79
|
+
- `constants` (system user/channel constants)
|
|
80
|
+
- `protocol/` (IRC `Message` and numeric `replies`)
|
|
81
|
+
- `telemetry/` (OpenTelemetry audit/tracing/metrics — full subpackage)
|
|
82
|
+
- `virtual_client` (`VirtualClient` for in-process bot integration)
|
|
83
|
+
- `bots/{bot_manager,http_listener}` (no-op stubs; culture replaces
|
|
84
|
+
these at runtime when wrapping an `IRCd`)
|
|
85
|
+
- `[tool.citation]` block in `pyproject.toml` enumerating every vendored
|
|
86
|
+
file with a quote/paraphrase/synthesize status, source URL, and
|
|
87
|
+
sha256, validated by `cite check`.
|
|
88
|
+
- Runtime dependencies: `opentelemetry-api`, `opentelemetry-sdk`,
|
|
89
|
+
`opentelemetry-exporter-otlp-proto-grpc` (all `>=1.22`).
|
|
90
|
+
- Dev dependency: `citation-cli` (provides the `cite` console script).
|
|
91
|
+
|
|
92
|
+
### Changed
|
|
93
|
+
|
|
94
|
+
- Bootstrap spec deviation: `remote_client.py` was originally listed as
|
|
95
|
+
"do not copy" but turned out to be server-side (used by `server_link`
|
|
96
|
+
and `virtual_client` for peer-server users in channel member lists).
|
|
97
|
+
Vendored as public `agentirc/remote_client.py`. See commit `8b4a6d8`.
|
|
98
|
+
|
|
99
|
+
### Notes
|
|
100
|
+
|
|
101
|
+
- `agentirc/cli.py` still ships only `version`; the `serve|start|stop|
|
|
102
|
+
restart|status|link|logs` lifecycle verbs remain stubs. The real CLI
|
|
103
|
+
is the next slice (PR-B2).
|
|
104
|
+
- Tests are not migrated yet (PR-B3).
|
|
105
|
+
|
|
106
|
+
## [9.0.0] - 2026-04-30
|
|
107
|
+
|
|
108
|
+
### Added
|
|
109
|
+
|
|
110
|
+
- Initial bootstrap of `agentirc-cli` as an installable Python package.
|
|
111
|
+
- Skeleton `agentirc/{__init__,__main__,cli}.py` with `version` verb
|
|
112
|
+
wired up and lifecycle verbs (`serve|start|stop|restart|status|link|
|
|
113
|
+
logs`) as stubs.
|
|
114
|
+
- Console scripts: both `agentirc` and `agentirc-cli` map to
|
|
115
|
+
`agentirc.cli:main`.
|
|
116
|
+
- Major version starts at `9.0.0` to leapfrog the
|
|
117
|
+
`agentirc-cli==8.7.X.devN` squat that culture previously published to
|
|
118
|
+
TestPyPI, so dev releases sort as the actual "Latest".
|
|
@@ -2,11 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
4
|
|
|
5
|
-
## Current state:
|
|
5
|
+
## Current state: server-core + real CLI + protocol module landed (9.2.0)
|
|
6
6
|
|
|
7
|
-
This repo is
|
|
7
|
+
This repo is the agentirc server-core extraction out of the sibling project [`culture`](https://github.com/OriNachum/culture). As of 9.2.0:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- **Server-core** (`agentirc/{ircd,server_link,channel,events,skill,remote_client,…}.py`, `agentirc/skills/{rooms,threads,history,icon}.py`) — vendored from `culture@df50942` via the `cite-don't-copy` pattern (see `[tool.citation]` in `pyproject.toml`).
|
|
10
|
+
- **Client transport** (`agentirc/client.py`) — vendored from `culture/agentirc/client.py` in PR-B2. The bootstrap spec originally said this would "stay in culture", but the dependency-boundary analysis after PR-B1 showed `client.py` only imports already-vendored support modules plus opentelemetry. Without it, `agentirc/ircd.py:580`'s runtime `from agentirc.client import Client` raised `ImportError` on the first TCP IRC connection.
|
|
11
|
+
- **Public CLI** (`agentirc/cli.py`) — real verb dispatch extracted from `culture/cli/server.py`. Verbs: `serve` (foreground, no PID; for systemd `Type=simple` and containers), `start`/`stop`/`status` (lifecycle), `restart`, `link` (peer-spec validator), `logs` (cat / tail of `~/.culture/logs/server-<name>.log`), `version`.
|
|
12
|
+
- **Public protocol** (`agentirc/protocol.py`) — verb name constants, numerics, IRCv3 tag names. Wire-format quirks (`ROOMETAEND`, `ROOMETASET` typos, `ERR_NOSUCHCHANNEL` semantic misuse, `STHREAD` verb collapse) preserved verbatim — they need coordinated cross-repo bumps to fix.
|
|
13
|
+
- **Internal support** (`agentirc/_internal/`) — `aio`, `constants`, `protocol/`, `telemetry/`, `virtual_client`, `pidfile` (PR-B2), `cli_shared/` (PR-B2), `bots/` stubs.
|
|
14
|
+
|
|
15
|
+
End-to-end verified: `agentirc start --port <p>` boots a real IRCd, TCP NICK/USER handshake returns `001 RPL_WELCOME`, `agentirc stop` shuts cleanly. `agentirc serve` is byte-indistinguishable from `culture server start` for the lifecycle contract culture's shim relies on.
|
|
16
|
+
|
|
17
|
+
What is **not** done yet:
|
|
18
|
+
- **Test suite migration** is PR-B3 — only remaining bootstrap slice.
|
|
19
|
+
|
|
20
|
+
Read the bootstrap spec at `docs/superpowers/specs/2026-04-30-bootstrap-design.md` for the full plan; it is the operative source of truth and is intentionally self-contained. The culture-side counterpart spec is at `../culture/docs/superpowers/specs/2026-04-30-agentirc-extraction-design.md` — not normally needed, but explains *why* if a decision looks arbitrary.
|
|
21
|
+
|
|
22
|
+
### Cite-don't-copy
|
|
23
|
+
|
|
24
|
+
Vendored culture code is tracked under `[tool.citation]` in `pyproject.toml` using the workspace's [`citation-cli`](https://github.com/OriNachum/citation-cli) tool. Each vendored file has a `quote` (verbatim copy), `paraphrase` (copied with import rewrites), or `synthesize` (rewritten as agentirc-native) status. Run `cite check` to verify integrity. When pulling new culture changes, update the citation entries' source URLs and sha256s — the manifest is the provenance ledger.
|
|
10
25
|
|
|
11
26
|
## Three names, one project
|
|
12
27
|
|
|
@@ -22,11 +37,12 @@ There are three different names in play. Don't conflate them:
|
|
|
22
37
|
|
|
23
38
|
## What lives here vs. in culture
|
|
24
39
|
|
|
25
|
-
- **Server-core (here):** `ircd.py`, `server_link.py`, `channel.py`, `config.py`, `events.py`, the stores (`room_store`, `thread_store`, `history_store`), `rooms_util.py`, `skill.py`, and the `skills/` directory (`rooms`, `threads`, `history`, `icon`).
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
40
|
+
- **Server-core (here):** `ircd.py`, `server_link.py`, `channel.py`, `config.py`, `events.py`, the stores (`room_store`, `thread_store`, `history_store`), `rooms_util.py`, `skill.py`, `remote_client.py`, and the `skills/` directory (`rooms`, `threads`, `history`, `icon`).
|
|
41
|
+
- **Client transport (here, since 9.2.0):** `client.py` — vendored in PR-B2. Pre-9.2 the bootstrap spec said "stays in culture"; that assumption broke down once we found `client.py` only imports already-vendored modules and that the IRCd needs it at runtime to accept TCP clients.
|
|
42
|
+
- **Internal support (here):** `agentirc/_internal/{aio, constants, protocol/, telemetry/, virtual_client, pidfile, cli_shared/}` plus `bots/` no-op stubs (the real `culture.bots.*` depends on backend SDKs forbidden by agentirc's dependency boundary; culture replaces the stubs at runtime when wrapping an IRCd).
|
|
43
|
+
- **Stays in culture:** `culture.bots.*` (the real bot manager), `culture.config` / `culture.bots.config` (agent-manifest concerns), `culture.cli.shared.{ipc,display,formatting,process}` (CLI ergonomics agentirc doesn't need), `culture.credentials` / `culture.mesh_config` (OS-keyring + mesh.yaml).
|
|
28
44
|
|
|
29
|
-
When migrating tests, the rule is: pure server tests come here, transport tests
|
|
45
|
+
When migrating tests, the rule is: pure server tests come here, transport tests **also** come here now that we own `client.py`, mixed tests stay in culture and get rewritten to drive `agentirc serve` as a subprocess fixture rather than importing `IRCd` directly. When unsure, **prefer copying the test here** — this repo owns the IRCd and the client transport.
|
|
30
46
|
|
|
31
47
|
## Public API contract (semver-tracked)
|
|
32
48
|
|
|
@@ -34,12 +50,17 @@ Only three modules are public. Everything else is internal and may be refactored
|
|
|
34
50
|
|
|
35
51
|
| Module | Members |
|
|
36
52
|
|---|---|
|
|
37
|
-
| `agentirc.config` | `ServerConfig`, `LinkConfig`, `
|
|
53
|
+
| `agentirc.config` | `ServerConfig`, `LinkConfig`, `TelemetryConfig` |
|
|
38
54
|
| `agentirc.cli` | `main()`, `dispatch(argv) -> int` |
|
|
39
55
|
| `agentirc.protocol` | verb name constants, numeric reply codes, extension tag names |
|
|
40
56
|
|
|
41
57
|
`agentirc.cli.dispatch(argv)` is the function `culture`'s `culture server` shim calls — it must accept the exact same flag set, exit codes, and stderr formatting that `culture server` produces today. Do not "improve" CLI ergonomics during the bootstrap; that breaks the transparency contract culture relies on. `dispatch()` returns `int` on successful command dispatch and lets argparse's `SystemExit` propagate on `--help`/`--version`/parse-errors per Python convention; in-process callers (i.e. culture's shim) must catch `SystemExit` themselves or use `subprocess`.
|
|
42
58
|
|
|
59
|
+
Two intentional, additive deltas vs. culture's CLI:
|
|
60
|
+
|
|
61
|
+
- `agentirc status` prints `Server 'X': running (PID N, port P)` when a port file is present — culture only prints `(PID N)`. Strictly a superset; culture's shim relies on exit codes, not output parsing.
|
|
62
|
+
- `agentirc start` no longer accepts `--mesh-config` (depends on `culture.credentials` and `culture.mesh_config`, out of agentirc's scope). Use `--link name:host:port:password[:trust]` flags instead.
|
|
63
|
+
|
|
43
64
|
## Defaults preserve culture continuity
|
|
44
65
|
|
|
45
66
|
- Default `--config` path: `~/.culture/server.yaml` (yes, `.culture/`, not `.agentirc/`).
|
|
@@ -51,7 +72,7 @@ Do not rename on-disk artifacts during the bootstrap. That is explicitly out of
|
|
|
51
72
|
## Hard invariants
|
|
52
73
|
|
|
53
74
|
- **No imports back into culture.** After the bootstrap, `git grep -E '^(from|import) culture' agentirc/ tests/` must return nothing. CI should enforce this.
|
|
54
|
-
- **
|
|
75
|
+
- **Cite-don't-copy adaptation only.** Files copy from `../culture/` with import paths rewritten and minimal adaptation where the dependency boundary forces it (e.g. `culture.bots.{bot_manager,http_listener}` are stubbed to no-ops in `agentirc/_internal/bots/`). All adaptations are recorded in `[tool.citation]` with status `paraphrase` or `synthesize`. Improvements beyond what's needed for the dependency boundary ship in follow-up PRs.
|
|
55
76
|
- **Single synthetic first commit.** Message format: `Initial import from culture@<SHA>` where `<SHA>` is the culture commit ID the caller provides. No cherry-picked history.
|
|
56
77
|
- **No backend SDKs, no `culture` console script.** agentirc must not depend on `claude-agent-sdk`, `anthropic`, `agex-cli`, `afi-cli`, `github-copilot-sdk`, or any other agent/backend SDK, and must not declare a `culture` console script. Those are culture concerns — agent backends and the `culture` command live in `../culture` and stay there.
|
|
57
78
|
|
|
@@ -67,14 +88,19 @@ pytest -n auto
|
|
|
67
88
|
# Run a single test
|
|
68
89
|
pytest tests/path/to/test_file.py::test_name -v
|
|
69
90
|
|
|
70
|
-
# CLI smoke
|
|
91
|
+
# CLI smoke
|
|
71
92
|
agentirc --help
|
|
72
93
|
agentirc-cli --help # alias of agentirc
|
|
73
|
-
agentirc version # prints "agentirc 9.
|
|
94
|
+
agentirc version # prints "agentirc 9.2.0"
|
|
74
95
|
python -m agentirc version # equivalent
|
|
75
96
|
|
|
76
|
-
# Lifecycle
|
|
77
|
-
agentirc serve --config ~/.culture/server.yaml
|
|
97
|
+
# Lifecycle (functional since 9.2.0)
|
|
98
|
+
agentirc serve --config ~/.culture/server.yaml # foreground, no PID
|
|
99
|
+
agentirc start --name spark --host 127.0.0.1 --port 6667 # daemonize
|
|
100
|
+
agentirc status --name spark
|
|
101
|
+
agentirc stop --name spark
|
|
102
|
+
agentirc logs --name spark -f # tail -f the daemon log
|
|
103
|
+
agentirc link 'peer1:host:6667:secret:full' # parse + validate spec
|
|
78
104
|
```
|
|
79
105
|
|
|
80
106
|
CLI verbs: `serve`, `start`, `stop`, `restart`, `status`, `link`, `logs`, `version`. Of these, only `start`, `stop`, `status` have a `culture server …` analogue today; the rest are agentirc-only additions. Culture's pure-passthrough shim only ever emits its existing verbs, so the additions don't break it.
|
|
@@ -120,7 +146,7 @@ Per-machine paths for these skills go in `.claude/skills.local.yaml` (gitignored
|
|
|
120
146
|
|
|
121
147
|
The full list lives in §"Acceptance criteria" of the bootstrap spec. The non-obvious ones:
|
|
122
148
|
|
|
123
|
-
- `pip install agentirc-cli==9.
|
|
124
|
-
- `agentirc serve` is byte-indistinguishable from `culture server start` (same socket, same logs, same systemd integration).
|
|
125
|
-
- `agentirc.config.LinkConfig
|
|
126
|
-
- `docs/api-stability.md` names the three public modules.
|
|
149
|
+
- `pip install agentirc-cli==9.2.0` on a clean venv produces working `agentirc` *and* `agentirc-cli` binaries (both pointing at `agentirc.cli:main`). ✅ since 9.0.0.
|
|
150
|
+
- `agentirc serve` is byte-indistinguishable from `culture server start` (same socket, same logs, same systemd integration). ✅ since 9.2.0.
|
|
151
|
+
- `agentirc.config.{ServerConfig, LinkConfig, TelemetryConfig}`, `agentirc.cli.{main, dispatch}`, `agentirc.protocol.*` all import from a clean Python session. ✅ since 9.2.0.
|
|
152
|
+
- `docs/api-stability.md` names the three public modules. ⏳ pending.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentirc-cli
|
|
3
|
-
Version: 9.
|
|
3
|
+
Version: 9.2.0
|
|
4
4
|
Summary: Agent-friendly IRCd: server core for AI agent meshes
|
|
5
5
|
Project-URL: Homepage, https://github.com/OriNachum/agentirc
|
|
6
6
|
Project-URL: Issues, https://github.com/OriNachum/agentirc/issues
|
|
@@ -37,9 +37,13 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
37
37
|
Classifier: Programming Language :: Python :: 3.12
|
|
38
38
|
Classifier: Topic :: Communications :: Chat :: Internet Relay Chat
|
|
39
39
|
Requires-Python: >=3.10
|
|
40
|
+
Requires-Dist: opentelemetry-api>=1.22
|
|
41
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.22
|
|
42
|
+
Requires-Dist: opentelemetry-sdk>=1.22
|
|
40
43
|
Provides-Extra: dev
|
|
41
44
|
Requires-Dist: bandit; extra == 'dev'
|
|
42
45
|
Requires-Dist: black; extra == 'dev'
|
|
46
|
+
Requires-Dist: citation-cli; extra == 'dev'
|
|
43
47
|
Requires-Dist: flake8; extra == 'dev'
|
|
44
48
|
Requires-Dist: isort; extra == 'dev'
|
|
45
49
|
Requires-Dist: pylint; extra == 'dev'
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Async utilities for culture."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def maybe_await(result):
|
|
9
|
+
"""Await the result only if it's a coroutine, otherwise return directly."""
|
|
10
|
+
if asyncio.iscoroutine(result):
|
|
11
|
+
return await result
|
|
12
|
+
return result
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""No-op ``BotManager`` stub.
|
|
2
|
+
|
|
3
|
+
agentirc is a pure IRCd; bot infrastructure (loading agent backends from
|
|
4
|
+
config, dispatching events to them, graceful shutdown) is a culture concern
|
|
5
|
+
and lives in ``culture.bots.bot_manager``. This stub keeps ``IRCd.start()``
|
|
6
|
+
import-clean for standalone agentirc deployments. Culture's
|
|
7
|
+
:class:`culture.bots.bot_manager.BotManager` is API-compatible and replaces
|
|
8
|
+
this stub when culture wraps an ``IRCd`` (today by subclassing / attribute
|
|
9
|
+
replacement; eventually via a real injection point).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from agentirc.ircd import IRCd
|
|
18
|
+
from agentirc.skill import Event
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BotManager:
|
|
22
|
+
def __init__(self, server: "IRCd") -> None:
|
|
23
|
+
self.server = server
|
|
24
|
+
|
|
25
|
+
async def load_bots(self) -> None: # NOSONAR S7503: stub method must remain async to match the abstract contract real implementations override.
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
def load_system_bots(self) -> None:
|
|
29
|
+
return None
|
|
30
|
+
|
|
31
|
+
def get_bot(self, _nick: str):
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
async def on_event(self, _event: "Event") -> None: # NOSONAR S7503: stub method must remain async to match the abstract contract real implementations override.
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
async def stop_all(self) -> None: # NOSONAR S7503: stub method must remain async to match the abstract contract real implementations override.
|
|
38
|
+
return None
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""No-op ``HttpListener`` stub.
|
|
2
|
+
|
|
3
|
+
Pairs with the no-op :class:`agentirc._internal.bots.bot_manager.BotManager`.
|
|
4
|
+
The real implementation lives in ``culture.bots.http_listener`` and exposes
|
|
5
|
+
a webhook surface for triggering bot events. In a standalone agentirc
|
|
6
|
+
deployment there is nothing to listen for, so ``start()`` and ``stop()``
|
|
7
|
+
are no-ops.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from agentirc._internal.bots.bot_manager import BotManager
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class HttpListener:
|
|
19
|
+
def __init__(self, bot_manager: "BotManager", host: str, port: int) -> None:
|
|
20
|
+
self.bot_manager = bot_manager
|
|
21
|
+
self.host = host
|
|
22
|
+
self.port = port
|
|
23
|
+
|
|
24
|
+
async def start(self) -> None: # NOSONAR S7503: stub method must remain async to match the abstract contract real implementations override.
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
async def stop(self) -> None: # NOSONAR S7503: stub method must remain async to match the abstract contract real implementations override.
|
|
28
|
+
return None
|
|
File without changes
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Shared constants for the agentirc CLI.
|
|
2
|
+
|
|
3
|
+
Vendored from culture@df50942 (`culture/cli/shared/constants.py`) and
|
|
4
|
+
trimmed to the subset agentirc actually consumes. Bot-related constants
|
|
5
|
+
(``BOT_CONFIG_FILE``, ``LEGACY_CONFIG``, ``AGENTS_YAML``, etc.) are
|
|
6
|
+
dropped; agentirc has no bot configuration concept.
|
|
7
|
+
|
|
8
|
+
Default paths (``~/.culture/server.yaml``, ``~/.culture/logs``) are kept
|
|
9
|
+
intact per the "Defaults preserve culture continuity" rule in
|
|
10
|
+
CLAUDE.md, so agentirc and culture daemons share state directories on a
|
|
11
|
+
host without separate config trees.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
import stat
|
|
18
|
+
|
|
19
|
+
DEFAULT_CONFIG = os.path.expanduser("~/.culture/server.yaml")
|
|
20
|
+
LOG_DIR = os.path.expanduser("~/.culture/logs")
|
|
21
|
+
|
|
22
|
+
_CONFIG_HELP = "Config file path"
|
|
23
|
+
_SERVER_NAME_HELP = "Server name"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def culture_runtime_dir() -> str:
|
|
27
|
+
"""Return a user-private directory for daemon sockets.
|
|
28
|
+
|
|
29
|
+
Resolution order:
|
|
30
|
+
|
|
31
|
+
1. ``$XDG_RUNTIME_DIR`` when set (Linux/systemd default — already
|
|
32
|
+
user-private at ``/run/user/<uid>``).
|
|
33
|
+
2. ``~/.culture/run/`` otherwise (typical macOS path), created mode
|
|
34
|
+
0700 if missing and re-tightened to 0700 on every call so a
|
|
35
|
+
hand-created or pre-existing dir cannot leak sockets.
|
|
36
|
+
|
|
37
|
+
Raises ``RuntimeError`` when neither ``XDG_RUNTIME_DIR`` nor a
|
|
38
|
+
resolvable home directory is available — silently writing a literal
|
|
39
|
+
``~/.culture/run`` directory in CWD would surprise callers and the
|
|
40
|
+
daemons (which now route through this resolver) would fail at
|
|
41
|
+
socket-bind time anyway.
|
|
42
|
+
"""
|
|
43
|
+
xdg = os.environ.get("XDG_RUNTIME_DIR")
|
|
44
|
+
if xdg:
|
|
45
|
+
return xdg
|
|
46
|
+
home = os.path.expanduser("~")
|
|
47
|
+
if not home or home == "~" or not os.path.isabs(home):
|
|
48
|
+
raise RuntimeError(
|
|
49
|
+
"culture_runtime_dir(): cannot resolve a home directory "
|
|
50
|
+
"(os.path.expanduser('~') returned %r). Set $HOME or "
|
|
51
|
+
"$XDG_RUNTIME_DIR before running agentirc commands." % home
|
|
52
|
+
)
|
|
53
|
+
fallback = os.path.join(home, ".culture", "run")
|
|
54
|
+
os.makedirs(fallback, mode=0o700, exist_ok=True)
|
|
55
|
+
os.chmod(fallback, stat.S_IRWXU)
|
|
56
|
+
return fallback
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Mesh and link helpers for the agentirc CLI.
|
|
2
|
+
|
|
3
|
+
Vendored from culture@df50942 (`culture/cli/shared/mesh.py`), reduced
|
|
4
|
+
to the single helper agentirc needs: ``parse_link``. The upstream
|
|
5
|
+
helpers ``resolve_links_from_mesh`` and ``generate_mesh_from_agents``
|
|
6
|
+
depend on culture's ``mesh_config`` / ``credentials`` modules, which
|
|
7
|
+
manage agent mesh and OS-keyring credential lookup — concepts that do
|
|
8
|
+
not belong in agentirc's dependency-bounded surface.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def parse_link(value: str):
|
|
17
|
+
"""Parse a link spec: ``name:host:port:password[:trust]``.
|
|
18
|
+
|
|
19
|
+
Trust is extracted from the end if it matches a known value. This
|
|
20
|
+
allows passwords containing colons to round-trip through argparse
|
|
21
|
+
without escaping.
|
|
22
|
+
"""
|
|
23
|
+
from agentirc.config import LinkConfig
|
|
24
|
+
|
|
25
|
+
trust = "full"
|
|
26
|
+
if value.endswith(":full") or value.endswith(":restricted"):
|
|
27
|
+
value, trust = value.rsplit(":", 1)
|
|
28
|
+
|
|
29
|
+
parts = value.split(":", 3)
|
|
30
|
+
if len(parts) != 4:
|
|
31
|
+
raise argparse.ArgumentTypeError(
|
|
32
|
+
f"Link must be name:host:port:password[:trust], got: {value}"
|
|
33
|
+
)
|
|
34
|
+
name, host, port_str, password = parts
|
|
35
|
+
try:
|
|
36
|
+
port = int(port_str)
|
|
37
|
+
except ValueError as exc:
|
|
38
|
+
raise argparse.ArgumentTypeError(f"Invalid port: {port_str}") from exc
|
|
39
|
+
return LinkConfig(name=name, host=host, port=port, password=password, trust=trust)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Project-wide constants. Keep strings here, never in source code."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
# System pseudo-user and channel
|
|
8
|
+
SYSTEM_USER_PREFIX = "system-"
|
|
9
|
+
SYSTEM_CHANNEL = "#system"
|
|
10
|
+
SYSTEM_USER_REALNAME = "Culture system messages"
|
|
11
|
+
|
|
12
|
+
# IRCv3 message-tag keys we emit/consume
|
|
13
|
+
EVENT_TAG_TYPE = "event"
|
|
14
|
+
EVENT_TAG_DATA = "event-data"
|
|
15
|
+
|
|
16
|
+
# Event-type name regex (dotted lowercase, ≥2 segments)
|
|
17
|
+
EVENT_TYPE_RE = re.compile(r"^[a-z][a-z0-9_-]*(\.[a-z][a-z0-9_-]*)+$")
|