agentirc-cli 9.1.0__tar.gz → 9.3.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.
Files changed (107) hide show
  1. agentirc_cli-9.3.0/.claude/skills/pr-review/scripts/pr-sonar.sh +93 -0
  2. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/.claude/skills/pr-review/scripts/workflow.sh +8 -1
  3. agentirc_cli-9.3.0/CHANGELOG.md +204 -0
  4. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/CLAUDE.md +44 -18
  5. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/PKG-INFO +3 -3
  6. agentirc_cli-9.3.0/agentirc/_internal/cli_shared/constants.py +56 -0
  7. agentirc_cli-9.3.0/agentirc/_internal/cli_shared/mesh.py +39 -0
  8. agentirc_cli-9.3.0/agentirc/_internal/pidfile.py +238 -0
  9. agentirc_cli-9.3.0/agentirc/cli.py +659 -0
  10. agentirc_cli-9.3.0/agentirc/client.py +1070 -0
  11. agentirc_cli-9.3.0/agentirc/protocol.py +259 -0
  12. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/server_link.py +1 -1
  13. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/docs/superpowers/specs/2026-04-30-bootstrap-design.md +29 -23
  14. agentirc_cli-9.3.0/pyproject.toml +482 -0
  15. agentirc_cli-9.3.0/tests/__init__.py +0 -0
  16. agentirc_cli-9.3.0/tests/_helpers.py +96 -0
  17. agentirc_cli-9.3.0/tests/conftest.py +295 -0
  18. agentirc_cli-9.3.0/tests/telemetry/__init__.py +0 -0
  19. agentirc_cli-9.3.0/tests/telemetry/_fakes.py +26 -0
  20. agentirc_cli-9.3.0/tests/telemetry/_metrics_helpers.py +75 -0
  21. agentirc_cli-9.3.0/tests/telemetry/test_audit_emit.py +145 -0
  22. agentirc_cli-9.3.0/tests/telemetry/test_audit_lifecycle.py +69 -0
  23. agentirc_cli-9.3.0/tests/telemetry/test_audit_module.py +306 -0
  24. agentirc_cli-9.3.0/tests/telemetry/test_audit_parse_error.py +177 -0
  25. agentirc_cli-9.3.0/tests/telemetry/test_config.py +26 -0
  26. agentirc_cli-9.3.0/tests/telemetry/test_dispatch_span.py +45 -0
  27. agentirc_cli-9.3.0/tests/telemetry/test_emit_event_span.py +42 -0
  28. agentirc_cli-9.3.0/tests/telemetry/test_metrics_init.py +58 -0
  29. agentirc_cli-9.3.0/tests/telemetry/test_metrics_s2s.py +160 -0
  30. agentirc_cli-9.3.0/tests/telemetry/test_outbound_inject.py +70 -0
  31. agentirc_cli-9.3.0/tests/telemetry/test_parse_error.py +33 -0
  32. agentirc_cli-9.3.0/tests/telemetry/test_s2s_relay_span.py +114 -0
  33. agentirc_cli-9.3.0/tests/telemetry/test_server_init.py +29 -0
  34. agentirc_cli-9.3.0/tests/telemetry/test_server_link_inject.py +124 -0
  35. agentirc_cli-9.3.0/tests/telemetry/test_tracing.py +49 -0
  36. agentirc_cli-9.3.0/tests/test_channel.py +131 -0
  37. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/tests/test_cli.py +35 -6
  38. agentirc_cli-9.3.0/tests/test_connection.py +135 -0
  39. agentirc_cli-9.3.0/tests/test_discovery.py +169 -0
  40. agentirc_cli-9.3.0/tests/test_events_basic.py +284 -0
  41. agentirc_cli-9.3.0/tests/test_events_catalog.py +70 -0
  42. agentirc_cli-9.3.0/tests/test_events_federation.py +110 -0
  43. agentirc_cli-9.3.0/tests/test_events_history.py +64 -0
  44. agentirc_cli-9.3.0/tests/test_events_lifecycle.py +343 -0
  45. agentirc_cli-9.3.0/tests/test_events_reserved_nick.py +68 -0
  46. agentirc_cli-9.3.0/tests/test_federation.py +1163 -0
  47. agentirc_cli-9.3.0/tests/test_history.py +553 -0
  48. agentirc_cli-9.3.0/tests/test_link_reconnect.py +141 -0
  49. agentirc_cli-9.3.0/tests/test_mentions.py +155 -0
  50. agentirc_cli-9.3.0/tests/test_messaging.py +103 -0
  51. agentirc_cli-9.3.0/tests/test_modes.py +327 -0
  52. agentirc_cli-9.3.0/tests/test_room_persistence.py +75 -0
  53. agentirc_cli-9.3.0/tests/test_rooms_federation.py +63 -0
  54. agentirc_cli-9.3.0/tests/test_rooms_integration.py +110 -0
  55. agentirc_cli-9.3.0/tests/test_server_icon_skill.py +214 -0
  56. agentirc_cli-9.3.0/tests/test_skills.py +212 -0
  57. agentirc_cli-9.3.0/tests/test_threads.py +384 -0
  58. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/uv.lock +3 -118
  59. agentirc_cli-9.1.0/CHANGELOG.md +0 -57
  60. agentirc_cli-9.1.0/agentirc/cli.py +0 -104
  61. agentirc_cli-9.1.0/pyproject.toml +0 -248
  62. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/.claude/skills/pr-review/SKILL.md +0 -0
  63. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/.claude/skills/pr-review/scripts/portability-lint.sh +0 -0
  64. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/.claude/skills/pr-review/scripts/pr-batch.sh +0 -0
  65. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/.claude/skills/pr-review/scripts/pr-comments.sh +0 -0
  66. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/.claude/skills/pr-review/scripts/pr-reply.sh +0 -0
  67. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/.claude/skills/pr-review/scripts/pr-status.sh +0 -0
  68. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/.claude/skills.local.yaml.example +0 -0
  69. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/.github/workflows/publish.yml +0 -0
  70. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/.github/workflows/tests.yml +0 -0
  71. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/.gitignore +0 -0
  72. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/LICENSE +0 -0
  73. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/README.md +0 -0
  74. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/__init__.py +0 -0
  75. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/__main__.py +0 -0
  76. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/_internal/__init__.py +0 -0
  77. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/_internal/aio.py +0 -0
  78. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/_internal/bots/__init__.py +0 -0
  79. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/_internal/bots/bot_manager.py +0 -0
  80. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/_internal/bots/http_listener.py +0 -0
  81. {agentirc_cli-9.1.0/agentirc/_internal/protocol → agentirc_cli-9.3.0/agentirc/_internal/cli_shared}/__init__.py +0 -0
  82. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/_internal/constants.py +0 -0
  83. {agentirc_cli-9.1.0/agentirc/skills → agentirc_cli-9.3.0/agentirc/_internal/protocol}/__init__.py +0 -0
  84. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/_internal/protocol/message.py +0 -0
  85. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/_internal/protocol/replies.py +0 -0
  86. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/_internal/telemetry/__init__.py +0 -0
  87. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/_internal/telemetry/audit.py +0 -0
  88. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/_internal/telemetry/context.py +0 -0
  89. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/_internal/telemetry/metrics.py +0 -0
  90. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/_internal/telemetry/tracing.py +0 -0
  91. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/_internal/virtual_client.py +0 -0
  92. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/channel.py +0 -0
  93. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/config.py +0 -0
  94. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/events.py +0 -0
  95. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/history_store.py +0 -0
  96. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/ircd.py +0 -0
  97. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/remote_client.py +0 -0
  98. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/room_store.py +0 -0
  99. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/rooms_util.py +0 -0
  100. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/skill.py +0 -0
  101. {agentirc_cli-9.1.0/tests → agentirc_cli-9.3.0/agentirc/skills}/__init__.py +0 -0
  102. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/skills/history.py +0 -0
  103. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/skills/icon.py +0 -0
  104. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/skills/rooms.py +0 -0
  105. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/skills/threads.py +0 -0
  106. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/agentirc/thread_store.py +0 -0
  107. {agentirc_cli-9.1.0 → agentirc_cli-9.3.0}/docs/steward/onboarding.md +0 -0
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env bash
2
+ # pr-sonar.sh — list every SonarCloud finding attached to a PR.
3
+ #
4
+ # Surfaces what the GitHub-side `poll` cannot: SonarCloud issues,
5
+ # security hotspots, and the duplication breakdown. The
6
+ # sonarqubecloud[bot] PR comment only links to the dashboard — actual
7
+ # findings live behind the SonarCloud API.
8
+ #
9
+ # Usage: pr-sonar.sh [--repo OWNER/REPO] [--sonar-key KEY] PR_NUMBER
10
+ #
11
+ # Defaults:
12
+ # --repo auto-detected via `gh repo view`
13
+ # --sonar-key derived from repo as `<owner>_<name>`
14
+ #
15
+ # Anonymous SonarCloud access is sufficient for public projects.
16
+ # Requires: gh, jq, curl, python3.
17
+
18
+ set -euo pipefail
19
+
20
+ REPO=""
21
+ SONAR_KEY=""
22
+
23
+ while [[ $# -gt 0 ]]; do
24
+ case "$1" in
25
+ --repo) REPO="$2"; shift 2 ;;
26
+ --sonar-key) SONAR_KEY="$2"; shift 2 ;;
27
+ *) break ;;
28
+ esac
29
+ done
30
+
31
+ PR_NUMBER="${1:?Usage: pr-sonar.sh [--repo OWNER/REPO] [--sonar-key KEY] PR_NUMBER}"
32
+
33
+ if [[ -z "$REPO" ]]; then
34
+ REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
35
+ fi
36
+ if [[ -z "$SONAR_KEY" ]]; then
37
+ SONAR_KEY="${REPO%%/*}_${REPO##*/}"
38
+ fi
39
+
40
+ echo "════════════════ SONARCLOUD (project=$SONAR_KEY, PR=$PR_NUMBER) ════════════════"
41
+
42
+ # ── Quality gate
43
+ QG=$(curl -fsS "https://sonarcloud.io/api/qualitygates/project_status?projectKey=${SONAR_KEY}&pullRequest=${PR_NUMBER}" || echo '{}')
44
+ QG_STATUS=$(echo "$QG" | jq -r '.projectStatus.status // "UNKNOWN"')
45
+ echo "Quality Gate: $QG_STATUS"
46
+ echo "$QG" | jq -r '.projectStatus.conditions[]? | select(.status != "OK") | " ✗ \(.metricKey) = \(.actualValue) (threshold \(.comparator) \(.errorThreshold))"' || true
47
+
48
+ # ── Issues (BUG / VULNERABILITY / CODE_SMELL)
49
+ ISSUES=$(curl -fsS "https://sonarcloud.io/api/issues/search?componentKeys=${SONAR_KEY}&pullRequest=${PR_NUMBER}&statuses=OPEN,CONFIRMED&ps=500" || echo '{}')
50
+ ISSUE_TOTAL=$(echo "$ISSUES" | jq -r '.total // 0')
51
+ echo
52
+ echo "── Issues ($ISSUE_TOTAL OPEN/CONFIRMED) ─────────────────────────────"
53
+ if [[ "$ISSUE_TOTAL" != "0" ]]; then
54
+ echo "$ISSUES" | jq -r '
55
+ .issues[] |
56
+ " [\(.rule)] \(.severity) \(.component | sub("^[^:]+:"; ""))(:\(.line // "?"))\n \(.message)"
57
+ '
58
+ fi
59
+
60
+ # ── Security hotspots
61
+ HOTSPOTS=$(curl -fsS "https://sonarcloud.io/api/hotspots/search?projectKey=${SONAR_KEY}&pullRequest=${PR_NUMBER}&status=TO_REVIEW&ps=500" || echo '{}')
62
+ HOTSPOT_TOTAL=$(echo "$HOTSPOTS" | jq -r '.paging.total // 0')
63
+ echo
64
+ echo "── Hotspots ($HOTSPOT_TOTAL TO_REVIEW) ──────────────────────────────"
65
+ if [[ "$HOTSPOT_TOTAL" != "0" ]]; then
66
+ echo "$HOTSPOTS" | jq -r '
67
+ .hotspots[] |
68
+ " [\(.ruleKey)] \(.vulnerabilityProbability) \(.component | sub("^[^:]+:"; ""))(:\(.line // "?"))\n \(.message)"
69
+ '
70
+ fi
71
+
72
+ # ── Duplication
73
+ MEAS=$(curl -fsS "https://sonarcloud.io/api/measures/component?component=${SONAR_KEY}&pullRequest=${PR_NUMBER}&metricKeys=new_duplicated_lines_density,new_duplicated_lines,new_duplicated_blocks" || echo '{}')
74
+ DUP_PCT=$(echo "$MEAS" | jq -r '.component.measures[]? | select(.metric == "new_duplicated_lines_density") | .periods[0].value // ""')
75
+ DUP_LINES=$(echo "$MEAS" | jq -r '.component.measures[]? | select(.metric == "new_duplicated_lines") | .periods[0].value // ""')
76
+ DUP_BLOCKS=$(echo "$MEAS" | jq -r '.component.measures[]? | select(.metric == "new_duplicated_blocks") | .periods[0].value // ""')
77
+ echo
78
+ echo "── Duplication on new code ──────────────────────────────────────────"
79
+ echo " density: ${DUP_PCT:-?}% lines: ${DUP_LINES:-?} blocks: ${DUP_BLOCKS:-?}"
80
+
81
+ if [[ "${DUP_BLOCKS:-0}" != "0" && -n "${DUP_BLOCKS:-}" ]]; then
82
+ # Per-file duplication: list every file with duplicated_lines on new code.
83
+ # Sonar doesn't expose per-PR duplication-by-file directly; surface the
84
+ # files that appear in the issues + hotspots as a weak proxy.
85
+ DUP_FILES=$(echo "$ISSUES" | jq -r '[.issues[] | .component | sub("^[^:]+:"; "")] | unique | .[]')
86
+ if [[ -n "$DUP_FILES" ]]; then
87
+ echo " files with findings (likely duplication source):"
88
+ echo "$DUP_FILES" | sed 's/^/ - /'
89
+ fi
90
+ fi
91
+
92
+ echo
93
+ echo "Dashboard: https://sonarcloud.io/dashboard?id=${SONAR_KEY}&pullRequest=${PR_NUMBER}"
@@ -3,7 +3,8 @@
3
3
  #
4
4
  # Subcommands:
5
5
  # lint run the portability lint on the current diff (staged + unstaged)
6
- # poll <PR> fetch and display review comments
6
+ # poll <PR> fetch and display review comments + SonarCloud findings
7
+ # sonar <PR> list every SonarCloud issue / hotspot / duplication on a PR
7
8
  # reply <PR> batch reply to review comments (JSONL on stdin), --resolve
8
9
  # delta dump CLAUDE.md head + culture.yaml for each sibling project
9
10
  # listed in skills.local.yaml (alignment-delta check)
@@ -53,6 +54,12 @@ case "$cmd" in
53
54
  poll)
54
55
  PR="${1:?Usage: workflow.sh poll <PR>}"
55
56
  bash "$SCRIPT_DIR/pr-comments.sh" "$PR"
57
+ echo
58
+ bash "$SCRIPT_DIR/pr-sonar.sh" "$PR"
59
+ ;;
60
+ sonar)
61
+ PR="${1:?Usage: workflow.sh sonar <PR>}"
62
+ bash "$SCRIPT_DIR/pr-sonar.sh" "$PR"
56
63
  ;;
57
64
  reply)
58
65
  PR="${1:?Usage: workflow.sh reply <PR> (JSONL on stdin)}"
@@ -0,0 +1,204 @@
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.3.0] - 2026-05-01
8
+
9
+ ### Added
10
+
11
+ - Test suite migration (PR-B3): 36 server-core / telemetry tests
12
+ vendored from `culture@df50942` (~6,500 LOC). 315 tests run under
13
+ `pytest -n auto` in ~29 seconds.
14
+ - 21 server-core tests in `tests/` cover IRC lifecycle, channels,
15
+ rooms, threads, history, federation (S2S), events, mentions,
16
+ and the icon skill.
17
+ - 15 telemetry integration tests in `tests/telemetry/` cover audit
18
+ JSONL emission, OTLP span injection on dispatch, S2S relay
19
+ spans, metrics initialization, and trace-context propagation.
20
+ - Test helper modules `_fakes.py` and `_metrics_helpers.py` also
21
+ vendored verbatim under the same package.
22
+ - `tests/conftest.py` — adapted from culture. Drops the
23
+ `_BOTS_DIR_*` patches and the `server_with_bot` /
24
+ `server_with_bots` fixtures (see Changed below). Keeps the
25
+ `IRCTestClient` raw-TCP helper and the IRCd lifecycle, telemetry,
26
+ and audit fixtures.
27
+ - `.claude/skills/pr-review/scripts/pr-sonar.sh` — new script that
28
+ fetches every SonarCloud issue, security hotspot, and duplication
29
+ measure for a PR via the SonarCloud API. `workflow.sh poll` now
30
+ runs it after `pr-comments.sh`; `workflow.sh sonar <PR>` runs it
31
+ standalone. Closes the gap where the GitHub-side poll only saw
32
+ the SonarCloud bot's "Quality Gate failed" link without the
33
+ underlying findings. Re-vendor to `steward` after this PR merges.
34
+ - Three `[tool.citation]` packages:
35
+ `culture-tests-conftest` (paraphrase),
36
+ `culture-tests-server-core` (mostly quote, paraphrase for
37
+ `test_events_basic.py`), and `culture-tests-telemetry` (mostly
38
+ quote, paraphrase for `test_tracing.py`).
39
+
40
+ ### Changed
41
+
42
+ - `agentirc/server_link.py:_replay_event` — parameter renamed from
43
+ `_seq` (PR-B1's unused-arg compliance variant) back to `seq` to
44
+ match the upstream signature culture's tests assume; signature
45
+ carries `# noqa: ARG002 # NOSONAR S1172`. Hash refreshed.
46
+
47
+ ### Fixed (post-review)
48
+
49
+ - `requires-python = ">=3.10"` → `">=3.11"`; classifiers updated
50
+ (drop 3.10, add 3.13). Resolves Copilot/Qodo "asyncio.timeout
51
+ not in 3.10" findings. The 3.10 floor was inherited from PR-B1/B2
52
+ and would have ImportError'd on the first test run.
53
+ - 2 SonarCloud `python:S5332` security hotspots cleared via
54
+ `# NOSONAR S5332` annotations on `tests/telemetry/test_config.py`'s
55
+ localhost OTLP test fixtures (URLs never reach the wire).
56
+ - New `tests/_helpers.py` (agentirc-native, not cited) extracts the
57
+ duplicated "boot two linked IRCds" pattern into `boot_linked_pair`
58
+ + `link_pair`. Refactored 9 sites that previously inlined ~22
59
+ lines of identical scaffolding: 5 in `test_federation.py`, 3 in
60
+ `test_link_reconnect.py`, plus the `linked_servers` conftest
61
+ fixture. Drops ~180 duplicated lines, addressing SonarCloud's
62
+ >3% duplication threshold while keeping the cited test bodies
63
+ readable. Re-snapshots from culture replay this extraction
64
+ mechanically.
65
+ - 42 SonarCloud OPEN issues cleared via inline `# NOSONAR <rule>`
66
+ annotations: `python:S1172` (server_link `_replay_event`'s upstream
67
+ signature compat), `python:S2068` (3 test fixture passwords),
68
+ `python:S7483` (3 `IRCTestClient.recv*` / `_wait_for_span` timeout
69
+ params kept by upstream design — see `RECV_TIMEOUT_SECONDS`
70
+ module-level note), `python:S7494` (2 `dict(t.split("=") for t in
71
+ tags)` patterns vendored verbatim from culture), `python:S125`
72
+ (1 `# 311 RPL_WHOISUSER` documentation comment SonarCloud
73
+ misclassified as commented-out code), `python:S1481` × 21
74
+ (`server_a, server_b = linked_servers` tuple-unpack sites where
75
+ one or both vars are used by the federation fixture but unread in
76
+ the test body — same idiom across 3 files; underscore-prefix
77
+ rename would diverge from culture upstream and complicate
78
+ re-snapshots).
79
+
80
+ ### Deferred / out of scope
81
+
82
+ - Three bot-fixtured telemetry tests
83
+ (`test_bot_event_dispatch_span.py`, `test_bot_run_span.py`,
84
+ `test_metrics_bots.py`) and `test_welcome_bot.py` stay in culture
85
+ — they depend on the real `BotManager` (forbidden by agentirc's
86
+ dependency boundary). Bucket C tests (cli/console/daemon/clients)
87
+ also remain in culture indefinitely.
88
+ - `tests/telemetry/conftest.py` is not migrated; its only consumer
89
+ was the deferred bot-fixtured tests above.
90
+ - Bootstrap docs (`docs/api-stability.md`, `docs/cli.md`,
91
+ `docs/deployment.md`) ship in PR-B4.
92
+
93
+ ## [9.2.0] - 2026-05-01
94
+
95
+ ### Added
96
+
97
+ - `agentirc/protocol.py` — public, semver-tracked module consolidating
98
+ IRC verb names, numeric reply codes (re-exported from
99
+ `_internal.protocol.replies`), and IRCv3 / agentirc tag names. Wire-
100
+ format quirks (`ROOMETAEND`, `ROOMETASET` typos; `ERR_NOSUCHCHANNEL`
101
+ semantic misuse; `STHREAD` verb collapse) are preserved verbatim with
102
+ explanatory comments — they require coordinated cross-repo bumps to
103
+ fix.
104
+ - `agentirc/client.py` — IRC client transport vendored from
105
+ `culture/agentirc/client.py` at SHA `df50942`. Body unchanged; only
106
+ imports rewritten. The bootstrap spec originally said this would
107
+ "stay in culture" but its dependency surface (already-vendored
108
+ `_internal` support modules + opentelemetry) made vendoring the
109
+ cleaner path. Without it, `agentirc/ircd.py:580`'s runtime
110
+ `from agentirc.client import Client` raised `ImportError` on the
111
+ first TCP IRC connection.
112
+ - Real `agentirc/cli.py` — verb dispatch extracted from
113
+ `culture/cli/server.py`. New verbs: `serve` (foreground, no PID;
114
+ for systemd `Type=simple` and containers), `restart`, `link`
115
+ (peer-spec validator), `logs` (cat / tail of `~/.culture/logs/server-
116
+ <name>.log`). Existing verbs `start`/`stop`/`status` reuse culture's
117
+ proven daemonize / `_wait_for_port` / `_wait_for_graceful_stop` /
118
+ `_force_kill` helpers.
119
+ - Internal support modules:
120
+ - `agentirc/_internal/pidfile.py` — PID/port file management.
121
+ `is_managed_process()` recognizes both `culture` and
122
+ `agentirc`/`agentirc-cli` argv tokens; `is_culture_process` is
123
+ preserved as a thin alias.
124
+ - `agentirc/_internal/cli_shared/{constants,mesh}.py` — minimal
125
+ subset of `culture/cli/shared`. Keeps `DEFAULT_CONFIG`, `LOG_DIR`,
126
+ `culture_runtime_dir()`, `parse_link()`. Drops everything that
127
+ touched `culture.bots.config`, `culture.credentials`,
128
+ `culture.mesh_config`.
129
+ - Citations recorded in `[tool.citation]`: `culture-pidfile`,
130
+ `culture-cli-shared`, `culture-client`, `culture-cli-server`.
131
+
132
+ ### Changed
133
+
134
+ - Default server name changed from `culture` to `agentirc` in
135
+ `agentirc.cli`. PID/port files at `~/.culture/pids/server-<name>.{pid,
136
+ port}` keep their existing layout per the "Defaults preserve culture
137
+ continuity" rule, but the default fallback name no longer collides
138
+ with culture's daemon when both run on the same host.
139
+ - Dropped culture-only verbs (`default`, `rename`, `archive`,
140
+ `unarchive`) from agentirc's CLI surface — they manage culture's
141
+ agent manifest, which agentirc does not own.
142
+ - Dropped `--mesh-config` from `agentirc start` — depends on
143
+ `culture.credentials` / `culture.mesh_config` (out of scope).
144
+
145
+ ### Notes
146
+
147
+ - End-to-end smoke verified: `agentirc start --port 16667` boots,
148
+ TCP NICK/USER handshake returns `001 RPL_WELCOME` from a real
149
+ `IRCd`, `agentirc stop` shuts cleanly. `agentirc serve` is now
150
+ byte-indistinguishable from `culture server start` for the lifecycle
151
+ contract culture's shim relies on.
152
+ - Test suite migration (PR-B3) is the only remaining bootstrap slice.
153
+
154
+ ## [9.1.0] - 2026-04-30
155
+
156
+ ### Added
157
+
158
+ - Server-core vendored from `culture` at SHA `df50942`. The `agentirc`
159
+ package now contains the IRCd (`ircd.py`), server-to-server linking
160
+ (`server_link.py`), channel/event/store/skill modules, `remote_client.py`
161
+ (peer-server ghost client), and the four built-in skills
162
+ (`skills/{rooms,threads,history,icon}.py`).
163
+ - Internal vendored support modules under `agentirc/_internal/`:
164
+ - `aio` (`maybe_await`)
165
+ - `constants` (system user/channel constants)
166
+ - `protocol/` (IRC `Message` and numeric `replies`)
167
+ - `telemetry/` (OpenTelemetry audit/tracing/metrics — full subpackage)
168
+ - `virtual_client` (`VirtualClient` for in-process bot integration)
169
+ - `bots/{bot_manager,http_listener}` (no-op stubs; culture replaces
170
+ these at runtime when wrapping an `IRCd`)
171
+ - `[tool.citation]` block in `pyproject.toml` enumerating every vendored
172
+ file with a quote/paraphrase/synthesize status, source URL, and
173
+ sha256, validated by `cite check`.
174
+ - Runtime dependencies: `opentelemetry-api`, `opentelemetry-sdk`,
175
+ `opentelemetry-exporter-otlp-proto-grpc` (all `>=1.22`).
176
+ - Dev dependency: `citation-cli` (provides the `cite` console script).
177
+
178
+ ### Changed
179
+
180
+ - Bootstrap spec deviation: `remote_client.py` was originally listed as
181
+ "do not copy" but turned out to be server-side (used by `server_link`
182
+ and `virtual_client` for peer-server users in channel member lists).
183
+ Vendored as public `agentirc/remote_client.py`. See commit `8b4a6d8`.
184
+
185
+ ### Notes
186
+
187
+ - `agentirc/cli.py` still ships only `version`; the `serve|start|stop|
188
+ restart|status|link|logs` lifecycle verbs remain stubs. The real CLI
189
+ is the next slice (PR-B2).
190
+ - Tests are not migrated yet (PR-B3).
191
+
192
+ ## [9.0.0] - 2026-04-30
193
+
194
+ ### Added
195
+
196
+ - Initial bootstrap of `agentirc-cli` as an installable Python package.
197
+ - Skeleton `agentirc/{__init__,__main__,cli}.py` with `version` verb
198
+ wired up and lifecycle verbs (`serve|start|stop|restart|status|link|
199
+ logs`) as stubs.
200
+ - Console scripts: both `agentirc` and `agentirc-cli` map to
201
+ `agentirc.cli:main`.
202
+ - Major version starts at `9.0.0` to leapfrog the
203
+ `agentirc-cli==8.7.X.devN` squat that culture previously published to
204
+ TestPyPI, so dev releases sort as the actual "Latest".
@@ -2,14 +2,21 @@
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: server-core landed (9.1.0), CLI stubbed (PR-B2 next)
5
+ ## Current state: bootstrap functionally complete (9.3.0); docs slice (PR-B4) remains
6
6
 
7
- This repo is the agentirc server-core extraction out of the sibling project [`culture`](https://github.com/OriNachum/culture). As of 9.1.0, the IRCd server core is in place — `agentirc/{ircd,server_link,channel,events,skill,remote_client,…}.py` and `agentirc/skills/{rooms,threads,history,icon}.py` are all present, copied from `culture@df50942` via the `cite-don't-copy` pattern (see `[tool.citation]` in `pyproject.toml`). Internal vendored support modules live under `agentirc/_internal/` (`aio`, `constants`, `protocol/`, `telemetry/`, `virtual_client`, `bots/` stubs).
7
+ This repo is the agentirc server-core extraction out of the sibling project [`culture`](https://github.com/OriNachum/culture). As of 9.3.0:
8
+
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
+ - **Test suite** (PR-B3, 9.3.0) — 36 tests vendored from `culture@df50942` (~6.5kloc), 315 tests run under `pytest -n auto` in ~29s on default workers. Three telemetry tests (`test_bot_event_dispatch_span`, `test_bot_run_span`, `test_metrics_bots`) and `test_welcome_bot` stay in culture because they depend on the real `BotManager`. `tests/conftest.py` was adapted to drop bot-loader sandboxing and the `server_with_bot` / `server_with_bots` fixtures.
14
+ - **Internal support** (`agentirc/_internal/`) — `aio`, `constants`, `protocol/`, `telemetry/`, `virtual_client`, `pidfile` (PR-B2), `cli_shared/` (PR-B2), `bots/` stubs.
15
+
16
+ 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.
8
17
 
9
18
  What is **not** done yet:
10
- - **`agentirc/cli.py`** is still the Shape A stub. All lifecycle verbs (`serve`, `start`, `stop`, `restart`, `status`, `link`, `logs`) print "not yet implemented" and exit 1. The real CLI lands in PR-B2 (extracted from `../culture/culture/cli/server.py`).
11
- - **`agentirc/protocol.py`** does not exist yet (PR-B2 also).
12
- - **Test suite migration** is PR-B3.
19
+ - **Bootstrap docs (PR-B4)** `docs/api-stability.md`, `docs/cli.md`, `docs/deployment.md`. Pure prose; not gating on culture's cutover (the public API surface is already importable; PR-B4 just documents the contract).
13
20
 
14
21
  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.
15
22
 
@@ -31,11 +38,19 @@ There are three different names in play. Don't conflate them:
31
38
 
32
39
  ## What lives here vs. in culture
33
40
 
34
- - **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`). Vendored support under `agentirc/_internal/` (`aio`, `constants`, `protocol/`, `telemetry/`, `virtual_client`, `bots/` stubs).
35
- - **Stays in culture:** `client.py` (full IRC client transport, used by bots) and any test that exercises the IRC *client transport* rather than the server. Note: the bootstrap spec originally also listed `remote_client.py` as "stays in culture", but it turned out to be a 43-line server-side ghost-client stub used by `server_link.py`; it's vendored here. See PR-B1 commit history.
36
- - **Newly created here:** `agentirc/cli.py` (today: skeleton stub from PR Shape A; PR-B2 extracts the real one from `../culture/culture/cli/server.py`), `agentirc/__main__.py`. **Coming in PR-B2:** `agentirc/protocol.py` (consolidates verb names, numerics, and extension tag names currently inlined as string literals in `ircd.py` / `client.py`).
41
+ - **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`).
42
+ - **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.
43
+ - **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).
44
+ - **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).
45
+
46
+ 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.
37
47
 
38
- When migrating tests, the rule is: pure server tests come here, transport tests stay in culture, 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.
48
+ ### Test layout (since 9.3.0)
49
+
50
+ - `tests/conftest.py` — paraphrase of culture's conftest. Drops the `_BOTS_DIR_*` `unittest.mock.patch` calls (no-op against agentirc's bot stubs) and the `server_with_bot` / `server_with_bots` fixtures. Keeps `IRCTestClient`, the IRCd lifecycle fixtures (`server`, `linked_servers`, `make_client*`, `server_welcome_disabled`), telemetry fixtures (`tracing_exporter`, `metrics_reader`, `audit_dir`), and the `TEST_LINK_PASSWORD` constant.
51
+ - `tests/test_*.py` — 21 server-core tests + the agentirc-native `test_cli.py`. Cover IRC lifecycle, channels, rooms, threads, history, federation, events, mentions, the icon skill.
52
+ - `tests/telemetry/test_*.py` — 15 telemetry integration tests covering audit JSONL emission, OTLP span injection on dispatch, S2S relay spans, metrics initialization, trace-context propagation. Uses two private helper modules `_fakes.py` (FakeWriter etc.) and `_metrics_helpers.py`. No `tests/telemetry/conftest.py` (the upstream one was bot-coupled).
53
+ - Tests left in culture: `test_bot_event_dispatch_span.py`, `test_bot_run_span.py`, `test_metrics_bots.py`, `test_welcome_bot.py` (bot-manager-coupled), plus the entire bucket-C surface (cli, console, daemon, clients, credentials).
39
54
 
40
55
  ## Public API contract (semver-tracked)
41
56
 
@@ -49,6 +64,11 @@ Only three modules are public. Everything else is internal and may be refactored
49
64
 
50
65
  `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`.
51
66
 
67
+ Two intentional, additive deltas vs. culture's CLI:
68
+
69
+ - `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.
70
+ - `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.
71
+
52
72
  ## Defaults preserve culture continuity
53
73
 
54
74
  - Default `--config` path: `~/.culture/server.yaml` (yes, `.culture/`, not `.agentirc/`).
@@ -70,20 +90,25 @@ Do not rename on-disk artifacts during the bootstrap. That is explicitly out of
70
90
  # Dev setup
71
91
  uv venv && uv pip install -e ".[dev]"
72
92
 
73
- # Tests (the spec mandates parallel; no tests yet — collected 0)
93
+ # Tests (315 collected, ~29s on default workers)
74
94
  pytest -n auto
75
95
 
76
96
  # Run a single test
77
97
  pytest tests/path/to/test_file.py::test_name -v
78
98
 
79
- # CLI smoke (works today against the skeleton)
99
+ # CLI smoke
80
100
  agentirc --help
81
101
  agentirc-cli --help # alias of agentirc
82
- agentirc version # prints "agentirc 9.0.0"
102
+ agentirc version # prints "agentirc 9.3.0"
83
103
  python -m agentirc version # equivalent
84
104
 
85
- # Lifecycle verbs (stubs in 9.0.0; real impls land with the IRCd extraction)
86
- agentirc serve --config ~/.culture/server.yaml
105
+ # Lifecycle (functional since 9.2.0)
106
+ agentirc serve --config ~/.culture/server.yaml # foreground, no PID
107
+ agentirc start --name spark --host 127.0.0.1 --port 6667 # daemonize
108
+ agentirc status --name spark
109
+ agentirc stop --name spark
110
+ agentirc logs --name spark -f # tail -f the daemon log
111
+ agentirc link 'peer1:host:6667:secret:full' # parse + validate spec
87
112
  ```
88
113
 
89
114
  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.
@@ -129,7 +154,8 @@ Per-machine paths for these skills go in `.claude/skills.local.yaml` (gitignored
129
154
 
130
155
  The full list lives in §"Acceptance criteria" of the bootstrap spec. The non-obvious ones:
131
156
 
132
- - `pip install agentirc-cli==9.0.0` on a clean venv produces working `agentirc` *and* `agentirc-cli` binaries (both pointing at `agentirc.cli:main`).
133
- - `agentirc serve` is byte-indistinguishable from `culture server start` (same socket, same logs, same systemd integration).
134
- - `agentirc.config.LinkConfig`, `agentirc.config.PeerSpec`, `agentirc.cli.dispatch`, `agentirc.protocol.*` all import from a clean Python session.
135
- - `docs/api-stability.md` names the three public modules.
157
+ - `pip install agentirc-cli==9.3.0` on a clean venv produces working `agentirc` *and* `agentirc-cli` binaries (both pointing at `agentirc.cli:main`). ✅ since 9.0.0.
158
+ - `agentirc serve` is byte-indistinguishable from `culture server start` (same socket, same logs, same systemd integration). ✅ since 9.2.0.
159
+ - `agentirc.config.{ServerConfig, LinkConfig, TelemetryConfig}`, `agentirc.cli.{main, dispatch}`, `agentirc.protocol.*` all import from a clean Python session. ✅ since 9.2.0.
160
+ - `pytest -n auto` passes for the migrated suite (315 tests, ~29s). ✅ since 9.3.0.
161
+ - `docs/api-stability.md` names the three public modules. ⏳ pending PR-B4.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 9.1.0
3
+ Version: 9.3.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
@@ -32,11 +32,11 @@ Classifier: Development Status :: 3 - Alpha
32
32
  Classifier: Intended Audience :: Developers
33
33
  Classifier: License :: OSI Approved :: MIT License
34
34
  Classifier: Programming Language :: Python :: 3
35
- Classifier: Programming Language :: Python :: 3.10
36
35
  Classifier: Programming Language :: Python :: 3.11
37
36
  Classifier: Programming Language :: Python :: 3.12
37
+ Classifier: Programming Language :: Python :: 3.13
38
38
  Classifier: Topic :: Communications :: Chat :: Internet Relay Chat
39
- Requires-Python: >=3.10
39
+ Requires-Python: >=3.11
40
40
  Requires-Dist: opentelemetry-api>=1.22
41
41
  Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.22
42
42
  Requires-Dist: opentelemetry-sdk>=1.22
@@ -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)