events-cli 0.6.1__tar.gz → 0.7.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 (85) hide show
  1. {events_cli-0.6.1 → events_cli-0.7.0}/CHANGELOG.md +62 -0
  2. events_cli-0.7.0/CLAUDE.md +409 -0
  3. events_cli-0.7.0/PKG-INFO +118 -0
  4. events_cli-0.7.0/README.md +101 -0
  5. {events_cli-0.6.1 → events_cli-0.7.0}/events/__init__.py +1 -1
  6. {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/__init__.py +8 -2
  7. {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_commands/cli.py +5 -5
  8. {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_commands/doctor.py +2 -2
  9. {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_commands/explain.py +2 -2
  10. {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_commands/learn.py +27 -16
  11. {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_commands/overview.py +3 -3
  12. {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_commands/whoami.py +4 -3
  13. {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_errors.py +1 -1
  14. events_cli-0.7.0/events/cli/_prog.py +39 -0
  15. {events_cli-0.6.1 → events_cli-0.7.0}/events/explain/__init__.py +4 -1
  16. events_cli-0.7.0/events/explain/catalog.py +150 -0
  17. {events_cli-0.6.1 → events_cli-0.7.0}/pyproject.toml +1 -1
  18. events_cli-0.7.0/tests/test_cli.py +213 -0
  19. {events_cli-0.6.1 → events_cli-0.7.0}/tests/test_cli_introspection.py +5 -5
  20. {events_cli-0.6.1 → events_cli-0.7.0}/uv.lock +1 -1
  21. events_cli-0.6.1/CLAUDE.md +0 -28
  22. events_cli-0.6.1/PKG-INFO +0 -76
  23. events_cli-0.6.1/README.md +0 -59
  24. events_cli-0.6.1/events/explain/catalog.py +0 -130
  25. events_cli-0.6.1/tests/test_cli.py +0 -115
  26. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/agent-config/SKILL.md +0 -0
  27. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/agent-config/data/backend-fingerprints.yaml +0 -0
  28. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/agent-config/scripts/show.sh +0 -0
  29. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/ask-colleague/SKILL.md +0 -0
  30. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/ask-colleague/prompts/explore.md +0 -0
  31. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/ask-colleague/prompts/review.md +0 -0
  32. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/ask-colleague/prompts/write.md +0 -0
  33. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/ask-colleague/scripts/ask-colleague.sh +0 -0
  34. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
  35. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
  36. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/challenge/SKILL.md +0 -0
  37. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/cicd/SKILL.md +0 -0
  38. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  39. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  40. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  41. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  42. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  43. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/communicate/SKILL.md +0 -0
  44. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  45. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  46. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  47. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  48. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/communicate/scripts/templates/skill-new-brief.md +0 -0
  49. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
  50. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/deviate/SKILL.md +0 -0
  51. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
  52. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
  53. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/pypi-maintainer/SKILL.md +0 -0
  54. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/pypi-maintainer/scripts/switch-source.sh +0 -0
  55. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/recall/SKILL.md +0 -0
  56. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/recall/scripts/recall.sh +0 -0
  57. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/remember/SKILL.md +0 -0
  58. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/remember/scripts/remember.sh +0 -0
  59. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/run-tests/SKILL.md +0 -0
  60. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
  61. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/scope/SKILL.md +0 -0
  62. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/sonarclaude/SKILL.md +0 -0
  63. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
  64. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/spec-to-plan/SKILL.md +0 -0
  65. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
  66. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/summarize-delivery/SKILL.md +0 -0
  67. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/think/SKILL.md +0 -0
  68. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/think/scripts/think.sh +0 -0
  69. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/version-bump/SKILL.md +0 -0
  70. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/version-bump/scripts/bump.py +0 -0
  71. {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills.local.yaml.example +0 -0
  72. {events_cli-0.6.1 → events_cli-0.7.0}/.flake8 +0 -0
  73. {events_cli-0.6.1 → events_cli-0.7.0}/.github/workflows/publish.yml +0 -0
  74. {events_cli-0.6.1 → events_cli-0.7.0}/.github/workflows/tests.yml +0 -0
  75. {events_cli-0.6.1 → events_cli-0.7.0}/.gitignore +0 -0
  76. {events_cli-0.6.1 → events_cli-0.7.0}/.markdownlint-cli2.yaml +0 -0
  77. {events_cli-0.6.1 → events_cli-0.7.0}/AGENTS.colleague.md +0 -0
  78. {events_cli-0.6.1 → events_cli-0.7.0}/LICENSE +0 -0
  79. {events_cli-0.6.1 → events_cli-0.7.0}/culture.yaml +0 -0
  80. {events_cli-0.6.1 → events_cli-0.7.0}/docs/skill-sources.md +0 -0
  81. {events_cli-0.6.1 → events_cli-0.7.0}/events/__main__.py +0 -0
  82. {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_commands/__init__.py +0 -0
  83. {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_output.py +0 -0
  84. {events_cli-0.6.1 → events_cli-0.7.0}/sonar-project.properties +0 -0
  85. {events_cli-0.6.1 → events_cli-0.7.0}/tests/__init__.py +0 -0
@@ -5,6 +5,68 @@ All notable changes to this project will be documented in this file.
5
5
  Format follows [Keep a Changelog](https://keepachangelog.com/). This project
6
6
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.7.0] - 2026-07-23
9
+
10
+ ### Added
11
+
12
+ - **`CLAUDE.md` initialized into a real runtime prompt** (`/init`), replacing the
13
+ `guild create` seed placeholder. It now states the thing no single file in the
14
+ repo revealed: the event/broker domain is **not implemented** — everything in
15
+ `events/` is the `culture-agent-template` scaffold, renamed — and points at
16
+ issues #1/#2/#3 as the requirements baseline. Documents the CLI architecture
17
+ (single registration point, the `_dispatch` exit-code translation, the
18
+ stable-contract modules), the design constraints that are expensive to
19
+ retrofit (one-registry/four-surfaces, `watch` not fitting request/response,
20
+ keeping the raw MQTT port first-class, explicit `127.0.0.1:1883:1883`, the
21
+ Mosquitto facts, Docker-free unit tests), the culture-vs-events-cli lane
22
+ boundary, and the contributor conventions carried over from the template
23
+ (version-bump-every-PR, `cicd`, `ask-colleague`, worktree location, memory
24
+ discipline). Also reconciles the seed's stale `backend: claude` claim against
25
+ `culture.yaml`'s actual `backend: colleague`.
26
+ - **`events/cli/_prog.py`** — resolves the command name to name back at the
27
+ user, so remediation hints reference an invocation that actually exists. The
28
+ installed console script gets `events`; the documented no-install fallback
29
+ (`python -m events` from a checkout, where the console script is typically
30
+ absent) gets `python -m events`. Detection compares `sys.argv[0]` against
31
+ *this* package's `__main__.py` by full path — a basename check would match
32
+ every other `python -m` host, `python -m pytest` included. Consumed by
33
+ argparse's `prog` and by the `explain` catalog's unknown-path remediation.
34
+ - **Two regression tests for the command-name contract** —
35
+ `test_prog_matches_installed_console_script` reads `[project.scripts]` from
36
+ `pyproject.toml` and asserts argparse's `prog` matches it, so the two can
37
+ never drift apart again; `test_explain_root_keys_both_resolve_to_same_entry`
38
+ pins that `explain events` and `explain events-cli` reach the same root entry
39
+ (the rubric gate's `explain_self` check calls the former).
40
+
41
+ ### Changed
42
+
43
+ - **The CLI now calls itself `events`, the command that is actually installed.**
44
+ argparse's `prog` was `events-cli` — the PyPI distribution name — so `--help`
45
+ and every argparse error printed a command name that does not exist on PATH.
46
+ `prog`, the `learn` command map, the `explain` catalog, `doctor`'s status
47
+ line, and the `overview` / `cli overview` subjects all now say `events`. The
48
+ mesh nick stays `events-cli` (it is the `culture.yaml` suffix, deliberately
49
+ distinct from the console command), as does the distribution name.
50
+ - **User-facing strings describe the event fabric instead of the template.** The
51
+ parser description, `learn` prose and JSON payload, and every `explain` entry
52
+ described this as "a clonable template for AgentCulture mesh agents" — the
53
+ first thing an agent consuming this CLI reads. They now describe the broker /
54
+ contract purpose and state plainly that the event surface is not built yet.
55
+ `learn --json` gains a `distribution` field so the dist name is still
56
+ machine-discoverable alongside the new `tool: "events"`.
57
+ - **`README.md` rewritten** off the template's "make it your own" text, with the
58
+ scaffold status, the mesh lane boundary, and a working quickstart. Its
59
+ quickstart was broken: it invoked `uv run events-cli whoami`, which never
60
+ existed as a console script.
61
+
62
+ ### Fixed
63
+
64
+ - Tests that pinned the old command spelling (`"usage: events-cli"`,
65
+ `"events-cli doctor"`, `payload["tool"] == "events-cli"`, the `overview`
66
+ subjects) updated to the real command name, and several loose `in` assertions
67
+ tightened to anchored `startswith` checks so `# events` can no longer pass by
68
+ matching `# events cli`.
69
+
8
70
  ## [0.6.1] - 2026-07-20
9
71
 
10
72
  ### Added
@@ -0,0 +1,409 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What this project is
6
+
7
+ `events-cli` is the AgentCulture **event fabric**: an agent and CLI that runs and
8
+ maintains a Dockerised Eclipse Mosquitto MQTT broker and fronts it through
9
+ [`agentfront`](https://github.com/agentculture/agentfront) as a CLI, an HTTP API
10
+ and an MCP surface — so any app can `import events`, any service can call the
11
+ API, and an agent or a human can publish and subscribe to events the same way.
12
+
13
+ **Design principle (from [#1](https://github.com/agentculture/events-cli/issues/1)):**
14
+ *Mosquitto transports events. `events-cli` defines what they mean.* Consumers
15
+ depend on the `events-cli` contract — typed envelopes, correlation/causation,
16
+ pipeline runs — not on Mosquitto-specific topic conventions, so the transport
17
+ can be replaced later without changing how participants interact.
18
+
19
+ ### Read this before you build anything
20
+
21
+ **The event/broker domain is not implemented yet.** What is on disk today is the
22
+ agent-first CLI scaffold inherited from `culture-agent-template` — identity,
23
+ introspection verbs, CI, packaging. Nothing MQTT, Docker, envelope- or
24
+ pipeline-related exists in `events/`.
25
+
26
+ The domain is specified across three open issues, and they are the requirements
27
+ baseline. Read them before starting work; they are not summarised anywhere else
28
+ in the repo:
29
+
30
+ | Issue | What it settles |
31
+ |-------|-----------------|
32
+ | [#1](https://github.com/agentculture/events-cli/issues/1) | The spec: CloudEvents envelope, pipeline model, CLI verb surface, security defaults, acceptance criteria, non-goals. |
33
+ | [#2](https://github.com/agentculture/events-cli/issues/2) | The agentfront binding, the `watch` request/response constraint, mesh lane boundaries, Mosquitto specifics, repo-shape decisions. |
34
+ | [#3](https://github.com/agentculture/events-cli/issues/3) | First co-located consumer (`reachy-mini-cli`): direct-MQTT loopback slice on `spark-f8a9`, and the nova-stack migration. |
35
+
36
+ Keep this file describing the repository **as it exists on disk today**. When a
37
+ section describes something not yet built, it lives under
38
+ [Design constraints](#design-constraints-decide-these-before-the-data-plane) or
39
+ [Roadmap](#roadmap) and says so — do not let prose drift ahead of code.
40
+
41
+ ## Identity
42
+
43
+ Declared in `culture.yaml`:
44
+
45
+ ```yaml
46
+ agents:
47
+ - suffix: events-cli
48
+ backend: colleague
49
+ model: sakamakismile/Qwen3.6-27B-Text-NVFP4-MTP
50
+ ```
51
+
52
+ `backend: colleague` fixes the resident prompt file to **`AGENTS.colleague.md`**
53
+ — the mesh runtime reads that file, while `CLAUDE.md` (this file) is the Claude
54
+ Code guidance file and is *not* the backend's prompt. Together they satisfy the
55
+ two invariants `steward doctor` verifies: **prompt-file-present** and
56
+ **backend-consistency** (`colleague` ↔ `AGENTS.colleague.md`). `events doctor`
57
+ checks the same invariants locally, plus a skills-present check.
58
+
59
+ (The pre-`/init` seed of this file claimed `backend: claude`. That was template
60
+ drift; `colleague` is correct and is what `culture.yaml`, `doctor`, and the test
61
+ suite assert.)
62
+
63
+ ## The CLI
64
+
65
+ **The installed console command is `events`** (`[project.scripts]` in
66
+ `pyproject.toml`), not `events-cli` — `events-cli` is the PyPI distribution name
67
+ and the import package is `events`.
68
+
69
+ ```bash
70
+ events whoami # identity from culture.yaml
71
+ events learn # structured self-teaching prompt
72
+ events explain <path> # markdown docs for any noun/verb path
73
+ events overview # descriptive snapshot of the agent
74
+ events doctor # check the agent-identity invariants
75
+ events cli overview # describe the CLI surface itself
76
+ ```
77
+
78
+ Conventions, enforced by the agent-first rubric:
79
+
80
+ - Every command supports `--json`.
81
+ - **Results to stdout, errors and diagnostics to stderr — never mixed**
82
+ (`events/cli/_output.py`).
83
+ - Exit codes: `0` success, `1` user error, `2` environment error, `3+` reserved
84
+ (`events/cli/_errors.py`).
85
+ - No Python traceback ever reaches stderr; every failure is a `CliError` with a
86
+ `remediation` hint, and even argparse parse errors route through the same
87
+ `error:` / `hint:` format (`_CliArgumentParser` in `events/cli/__init__.py`).
88
+
89
+ CI enforces the rubric with `uv run teken cli doctor . --strict`.
90
+
91
+ ### How the CLI fits together
92
+
93
+ Reading these four files gives you the whole contract; the rest is content.
94
+
95
+ - `events/cli/__init__.py` — the only parser. `_build_parser()` imports each
96
+ command module lazily and calls its `register(sub)`. `_dispatch()` is the
97
+ single place exceptions become exit codes. **Register a new noun group by
98
+ adding one `register()` call here** — there is a marked comment at the
99
+ insertion point.
100
+ - `events/cli/_commands/<verb>.py` — one module per verb/noun. Each exposes
101
+ `register(sub)` (adds the subparser, `--json`, and `set_defaults(func=...)`)
102
+ and a `cmd_*` handler returning `None`/`int`. Handlers raise `CliError`; they
103
+ never print errors themselves.
104
+ - `events/cli/_output.py` / `_errors.py` — the stdout/stderr split and the
105
+ exit-code policy. Both are marked *stable-contract*: changing them changes the
106
+ agent-facing contract, so treat edits as breaking.
107
+ - `events/explain/catalog.py` — markdown keyed by command-path tuple. Every
108
+ noun/verb you register needs an entry, and `tests/test_cli.py`'s
109
+ `test_every_catalog_path_resolves` walks all of them.
110
+
111
+ **Load-bearing detail:** the catalog maps *both* `("events",)` and
112
+ `("events-cli",)` to the root entry. The rubric gate's `explain_self` check
113
+ invokes `events explain events`; the dist-name key keeps `events explain
114
+ events-cli` working for callers that know the repo by that name. **Do not remove
115
+ either key** — dropping `("events",)` fails CI.
116
+ `test_explain_root_keys_both_resolve_to_same_entry` pins both.
117
+
118
+ **Command name:** hints tell an agent what to run *next*, so they must name a
119
+ command that exists in the mode the caller is already using.
120
+ `events/cli/_prog.py` resolves it — `events` when installed, `python -m events`
121
+ under the no-install fallback — and feeds both argparse's `prog` and the
122
+ `explain` unknown-path remediation. It compares `sys.argv[0]` to *this*
123
+ package's `__main__.py` by full path; a basename check matches every `python -m`
124
+ host, `python -m pytest` included. `test_prog_matches_installed_console_script`
125
+ reads `[project.scripts]` from `pyproject.toml` to keep the console-script name
126
+ and `prog` in lockstep, so renaming either side fails there rather than shipping.
127
+
128
+ `whoami` parses `culture.yaml` with a hand-rolled line scanner
129
+ (`events/cli/_commands/whoami.py`) rather than PyYAML, deliberately: the runtime
130
+ package has **zero third-party dependencies**. It locates the file by walking up
131
+ from `__file__`, so identity is always *this agent's*, never whatever
132
+ `culture.yaml` sits in the caller's CWD; a wheel install finds none and falls
133
+ back to literal defaults (which is why `doctor` reports a single info check and
134
+ exits 0 there).
135
+
136
+ ## Design constraints (decide these before the data plane)
137
+
138
+ These come out of #2 and #3 and are expensive to retrofit. They are decisions,
139
+ not code — nothing below exists yet.
140
+
141
+ **One registry, four surfaces.** agentfront is an importable runtime, not a
142
+ scaffolder: declare docs and tools once on an `App`, and CLI, MCP and HTTP are
143
+ *derived* from that registry, so they cannot drift apart. agentfront derives
144
+ three surfaces; the request names four. The fourth — `import events` — is not
145
+ agentfront's and is yours to keep honest. Build a core module that owns event
146
+ semantics and populate the agentfront registry *from that core*. If the registry
147
+ is filled by separate adapter functions, the import lane becomes a fourth thing
148
+ that drifts, which is exactly what agentfront otherwise removes.
149
+
150
+ **`watch` does not fit request/response.** A long-lived MQTT subscription blocks
151
+ an MCP tool call and an HTTP request; the CLI is the only surface that can stream
152
+ forever. So `watch` cannot be exposed verbatim everywhere. The shape that works
153
+ identically on all surfaces: **durable server-side subscriptions** (named,
154
+ persistent, owned by an identity) that the control service drains from — `events
155
+ watch --since <cursor> --max N --timeout 30s` returning a bounded batch plus a
156
+ cursor. **Every agent-facing tool needs `--max` and `--timeout` with non-infinite
157
+ defaults**; an unbounded default eventually hangs an agent turn. Streaming stays
158
+ HTTP-only (SSE, or pass through Mosquitto's native WebSocket listener). This is
159
+ the same machinery as #1's "history survives a stack restart" — design them
160
+ together, not a cursor bolted onto a streaming-only `watch` later.
161
+
162
+ **The raw MQTT port stays first-class.** `reachy-mini-cli` publishes from inside
163
+ a 50 Hz robot control loop where a publish must be an O(1) in-process enqueue; it
164
+ connects **directly to TCP 1883** with `paho-mqtt >=2,<3` and cannot go through
165
+ the CLI/HTTP/MCP front. Direct broker access is a supported, documented surface
166
+ for co-located latency-sensitive producers, with the contract/pipeline layer as
167
+ an **optional value-add, not a mandatory gateway**. If a validation layer later
168
+ polices topic shapes, leave room for producer-owned topic trees
169
+ (`reachy/events/{source}/{type}`, retained `reachy/state/{key}`) that don't
170
+ participate in pipelines.
171
+
172
+ **Loopback binding is explicit, not implied.** The Compose port mapping must be
173
+ `127.0.0.1:1883:1883`. Docker publishes on `0.0.0.0` by default and its NAT
174
+ bypasses host firewalls, so a bare `1883:1883` is LAN-exposed regardless of
175
+ `ufw`. Remote access is an explicit, documented opt-in.
176
+
177
+ **Mosquitto facts worth knowing before writing `events init`:**
178
+
179
+ - Mosquitto 2.0 already defaults the way #1 wants — no listener means
180
+ localhost-only, and `allow_anonymous` defaults to `false`. Several security
181
+ requirements are "don't override the upstream default," not a hardening layer.
182
+ Verify against the version you pin and say so in the generated
183
+ `mosquitto.conf`.
184
+ - The **dynamic security plugin** (`mosquitto_ctrl dynsec`, 2.0+) is the right
185
+ answer for distinct identities + topic-level ACLs — runtime JSON config, no
186
+ broker reload per change, which is what an agent-facing CLI actually needs.
187
+ - **MQTT 5 shared subscriptions** (`$share/<group>/<topic>`) fan one event type
188
+ across competing consumers — relevant to pipeline stages with several workers.
189
+ - **QoS 1 is at-least-once**, matching #1's explicit non-goal of exactly-once.
190
+ Consumer-side idempotency keyed on event ID is therefore a *requirement* of
191
+ the contract, not an optimisation — state it so consumers know they must dedupe.
192
+ - **Retained messages are not history.** A retained message gives a new
193
+ subscriber the last value on a topic; it is not a replayable log. History and
194
+ survive-a-restart need the control service's own store.
195
+ - **Persistence needs `persistence true` *plus* a mounted volume** — easy to
196
+ omit, and only discovered on the first restart test.
197
+
198
+ **Docker must not be a unit-test dependency.** CI runs pytest with SonarCloud
199
+ coverage on every PR (`sonar.qualitygate.wait=true` blocks the merge). Keep
200
+ envelope validation and pipeline-transition logic pure and dockerless — they are
201
+ the parts worth high coverage anyway — and mark stack integration tests so they
202
+ can be selected separately. A suite that needs a live broker to collect coverage
203
+ makes the quality gate flaky.
204
+
205
+ **Dependency posture.** Runtime `dependencies = []` today, and agentfront keeps
206
+ CLI + HTTP dependency-free with MCP behind an extra (`agentfront[mcp]`). An MQTT
207
+ client is a real third-party dependency: decide whether `pip install events-cli`
208
+ pulls it or whether the data plane sits behind an extra. Matching agentfront's
209
+ convention is the path of least surprise.
210
+
211
+ **Do not build a store from scratch.** `eidetic-cli` (memory/recall) and
212
+ `data-refinery-cli` (data quality) already own that lane; evaluate them before a
213
+ bespoke history backend. Likewise, if `events up` shells out to `docker`, check
214
+ whether that belongs behind `shell-cli` (which owns shell confinement and the
215
+ approval policy) rather than a bare `subprocess` call — this repo should not grow
216
+ a second shell-confinement posture.
217
+
218
+ ### Mesh lane boundary
219
+
220
+ [`culture`](https://github.com/agentculture/culture) is the IRC-based agent mesh
221
+ and already moves messages between agents. `events-cli` is a **second messaging
222
+ substrate in the same ecosystem**, which is defensible but must be stated or
223
+ contributors will guess:
224
+
225
+ - **culture** carries *agent conversation* — peer-to-peer, human-readable,
226
+ presence-oriented, IRC-shaped.
227
+ - **events-cli** carries *machine events* — typed immutable envelopes,
228
+ correlation and causation, durable history, pipeline runs, app-to-app as much
229
+ as agent-to-agent.
230
+
231
+ [`devague`](https://github.com/agentculture/devague) is the likely first real
232
+ consumer: #1's motivating flow (`task.requested → scope.completed →
233
+ specification.ready → implementation.completed → validation.completed`) is almost
234
+ exactly the devague lifecycle, and `/deviate` exists precisely because mid-run
235
+ divergence has no transport today. Test the design against it — if events-cli is
236
+ the transport under devague, its stage names are the first real event-type
237
+ vocabulary and a far better validation than a synthetic example. If not, say why,
238
+ because the resemblance will otherwise be assumed.
239
+
240
+ ## Development
241
+
242
+ ```bash
243
+ uv sync
244
+ uv run pytest -n auto # full suite (24 tests today)
245
+ uv run pytest tests/test_cli.py -v # one file
246
+ uv run pytest -k whoami -v # one test / pattern
247
+ uv run pytest -n auto --cov=events --cov-report=term # with coverage
248
+ ```
249
+
250
+ Lint — all four run in CI and must pass:
251
+
252
+ ```bash
253
+ uv run black --check events tests # line length 100
254
+ uv run isort --check-only events tests # profile=black
255
+ uv run flake8 events tests
256
+ uv run bandit -c pyproject.toml -r events
257
+ markdownlint-cli2 "**/*.md" "#node_modules" "#.local" "#.claude/skills" "#.teken"
258
+ uv run teken cli doctor . --strict # the agent-first rubric gate
259
+ ```
260
+
261
+ Coverage floor is 60% (`[tool.coverage.report] fail_under`), and SonarCloud
262
+ gates the PR on top of that (`sonar-project.properties`,
263
+ `sonar.qualitygate.wait=true`). The Sonar step is skipped when `SONAR_TOKEN` is
264
+ absent, so fork PRs stay green.
265
+
266
+ The CLI has no runtime dependencies, so it also runs straight from a checkout
267
+ without `uv sync` — useful when the network is unavailable:
268
+
269
+ ```bash
270
+ PYTHONPATH=. python3 -m events doctor
271
+ PYTHONPATH=. python3 -m pytest tests -q
272
+ ```
273
+
274
+ ## Conventions
275
+
276
+ - **Every PR bumps the version** — even docs/config/CI-only PRs. Use the
277
+ `version-bump` skill; the `version-check` CI job comments on and blocks the PR
278
+ otherwise. This repo published to production PyPI on its genesis push, so the
279
+ Trusted Publisher is already registered and a push to `main` touching
280
+ `pyproject.toml` or `events/**` publishes for real.
281
+ - **PRs go through the `cicd` skill** (`devex pr` + SonarCloud gating). Sign
282
+ online posts as `- events-cli (Claude)`; the `cicd` / `communicate` scripts
283
+ resolve the nick from `culture.yaml` automatically, so don't hand-sign in
284
+ bodies they author.
285
+ - **Reach for `ask-colleague` reflexively.** Treat it as the teammate at the next
286
+ desk, not a last resort — its value is a *second, independent mind* (a
287
+ different backend/model), not a stronger one. Before presenting or opening a PR
288
+ on a non-trivial committed diff, run `review`; for a fresh read of an
289
+ unfamiliar area, run `explore`. Both are read-only (throwaway worktree, zero
290
+ side effects), so the reflex is always safe. The side-effecting `write
291
+ --apply` / `write --pr` still needs the user's go-ahead. Its output is a second
292
+ opinion to verify and own, never authority.
293
+ - **The vendored `.claude/skills/` are cited verbatim** — do not reformat or edit
294
+ their scripts. Re-sync from guildmaster (or the tracked direct-from-origin
295
+ exceptions) per `docs/skill-sources.md`.
296
+ - **Deploy**: pushing to `main` publishes to PyPI via Trusted Publishing
297
+ (`.github/workflows/publish.yml`); PRs from this repo do a TestPyPI dry-run
298
+ with a `.devN` suffix.
299
+
300
+ ### Worktrees
301
+
302
+ **Every worktree you create by hand lives in
303
+ `../.worktrees.events-cli/<name>/`** — one repo-named directory beside the
304
+ checkout, one subfolder per worktree:
305
+
306
+ ```bash
307
+ git worktree add ../.worktrees.events-cli/<name> -b <branch>
308
+ ```
309
+
310
+ Not a shared `../worktrees/`. This workspace holds many sibling projects, and a
311
+ generic shared folder accumulates orphaned trees from several repos with nothing
312
+ indicating who owns which — a stale-tree sweep cannot tell a live lane from junk.
313
+ Use a branch prefix scoped to the work (`broker/t2`, not `agent/t2`): plain
314
+ `agent/*` collides with leftovers from earlier fan-outs and `git worktree add -b`
315
+ fails on an existing branch.
316
+
317
+ The vendored `assign-to-workforce` skill's fan-out example uses *both* the shared
318
+ path and `agent/<task-id>` branches. It is cited verbatim and must not be edited,
319
+ so override both when following it.
320
+
321
+ Remove a worktree with `git worktree remove <path>`, which deletes the directory
322
+ and its bookkeeping together; `git worktree prune` only clears metadata for
323
+ directories that are already gone. Never `rm -rf` a worktree you did not create.
324
+ Exception: `ask-colleague`'s read-only verbs create their own detached worktree
325
+ under `${TMPDIR:-/tmp}` and reap it on an EXIT trap — tool-managed throwaways
326
+ never persist, so they are outside this rule.
327
+
328
+ ### Memory discipline — recall before, remember after
329
+
330
+ This repo keeps its eidetic memory **in-repo and public**: a plain `/remember`
331
+ lands in `<repo-root>/.eidetic/memory` — committed, and shared with the team and
332
+ mesh peers (the `claude` and `colleague` backends both resolve the `events-cli`
333
+ scope), so memory travels with the repo rather than a private home-dir store.
334
+ The vendored `remember.sh` applies this as a deliberate policy override of
335
+ eidetic's upstream private default.
336
+
337
+ - **`/recall` before you start** a non-trivial task — prior decisions, gotchas,
338
+ "have we done this before?" — so you build on what's known instead of
339
+ re-deriving it. Not just when asked.
340
+ - **`/remember` when something worth keeping surfaces** — a non-obvious decision
341
+ and its rationale, a constraint, a fix and *why*, a gotcha that cost time.
342
+ Capture it as it happens, not at the end.
343
+
344
+ Keep something out of the committed store with `--visibility private` (routes to
345
+ `$HOME/.eidetic/memory`); `/recall` reads both and merges. Don't store what the
346
+ repo already records — code structure, git history, this file, `CHANGELOG.md`.
347
+ Store what you'd otherwise re-derive.
348
+
349
+ ## Skills
350
+
351
+ `.claude/skills/` vendors the canonical **guildmaster** skill kit
352
+ (cite-don't-import); provenance and the re-sync procedure live in
353
+ `docs/skill-sources.md`. Seven skills originate in `devague` and one
354
+ (`ask-colleague`) in `colleague`; four devague skills and `ask-colleague` are
355
+ vendored **directly from their origin** as tracked divergences, because
356
+ guildmaster's re-broadcast copies differ. Every vendored `SKILL.md` must carry
357
+ `type: command` — `core.skill_loader` silently skips any that lacks it, even
358
+ where guildmaster's upstream copy omits the field.
359
+
360
+ Tooling prerequisites: **`devex`** (>=0.21) and **`agtag`** (>=0.1) on PATH;
361
+ **`colleague`** on PATH is optional and only needed when `ask-colleague` is
362
+ invoked. Copy `.claude/skills.local.yaml.example` to `skills.local.yaml`
363
+ (git-ignored) for per-machine sibling paths.
364
+
365
+ ## Layout
366
+
367
+ ```text
368
+ events/ agent-first CLI (cited from teken's python-cli reference)
369
+ cli/__init__.py the single parser + dispatch/exit-code translation
370
+ cli/_output.py stdout/stderr split (stable-contract)
371
+ cli/_errors.py CliError + exit-code policy (stable-contract)
372
+ cli/_commands/ one module per verb; each exposes register(sub)
373
+ explain/catalog.py markdown keyed by command-path tuple
374
+ tests/ pytest smoke + introspection tests
375
+ .claude/skills/ vendored guildmaster skill kit (cite-don't-import)
376
+ docs/skill-sources.md skill provenance ledger
377
+ culture.yaml mesh identity (suffix + backend)
378
+ .github/workflows/ tests.yml (test/lint/version-check), publish.yml (PyPI)
379
+ ```
380
+
381
+ ## Roadmap
382
+
383
+ Not built; tracked in the issues above. The rough order the constraints imply:
384
+
385
+ 1. **Core event semantics** — envelope + validation, pure and dockerless, as the
386
+ module the other surfaces are built from.
387
+ 2. **agentfront binding** — one registry deriving CLI/MCP/HTTP, plus the
388
+ `import events` lane fed from the same core. Requires migrating the dev
389
+ dependency (below).
390
+ 3. **Stack management** — `events init` / `up` / `status` / `logs` / `down`,
391
+ Compose with `127.0.0.1:1883:1883`, `persistence true` + volume, dynsec.
392
+ This is what unblocks [#3](https://github.com/agentculture/events-cli/issues/3)
393
+ on `spark-f8a9`, including stopping the `nova-mosquitto` /
394
+ `nova-nervous-system` stack so exactly one broker runs on the box.
395
+ 4. **Durable subscriptions + cursor drain**, designed together with history.
396
+ 5. **Pipelines** — apply/list/show/run/inspect over the event graph.
397
+
398
+ ## Known drift
399
+
400
+ Real, checked-in inconsistency. Fixing it is a normal PR (bump the version):
401
+
402
+ - **`teken` → `agentfront`.** The dev dependency is `teken>=0.8` and CI runs
403
+ `uv run teken cli doctor . --strict`, but teken was renamed: `agentfront`
404
+ 0.20.0 is the live package and `teken` survives only as a deprecated console
405
+ alias that prints a stderr note and forwards. Migrate the dev dep, the CI step,
406
+ and `uv.lock` together — and note that `docs/skill-sources.md`,
407
+ `.claude/skills.local.yaml.example` (`sibling_projects`), and
408
+ `.markdownlint-cli2.yaml` (`.teken/**` ignore) still reference the old name.
409
+ The rename is behaviourally inert today, so this is housekeeping, not a bug.
@@ -0,0 +1,118 @@
1
+ Metadata-Version: 2.4
2
+ Name: events-cli
3
+ Version: 0.7.0
4
+ Summary: Agent and CLI that runs and maintains a Dockerised Eclipse Mosquitto MQTT broker and fronts it through agentfront as a CLI, an HTTP API and an MCP surface — so any app can import it, any service can call the API, and an agent or a human can publish and subscribe to events the same way.
5
+ Project-URL: Homepage, https://github.com/agentculture/events-cli
6
+ Project-URL: Issues, https://github.com/agentculture/events-cli/issues
7
+ Author: AgentCulture
8
+ License-Expression: Apache-2.0
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Software Development
15
+ Requires-Python: >=3.12
16
+ Description-Content-Type: text/markdown
17
+
18
+ # events-cli
19
+
20
+ The AgentCulture **event fabric**. `events-cli` runs and maintains a Dockerised
21
+ Eclipse Mosquitto MQTT broker and fronts it through
22
+ [agentfront](https://github.com/agentculture/agentfront) as a CLI, an HTTP API
23
+ and an MCP surface — so any app can `import events`, any service can call the
24
+ API, and an agent or a human can publish and subscribe to events the same way.
25
+
26
+ **Mosquitto transports events. `events-cli` defines what they mean.** Consumers
27
+ depend on the `events-cli` contract — typed immutable envelopes, correlation and
28
+ causation, pipeline runs — not on Mosquitto-specific topic conventions, so the
29
+ transport can be replaced later without changing how participants interact.
30
+
31
+ ## Status: scaffold
32
+
33
+ The event and broker domain is **not implemented yet**. What ships today is the
34
+ agent-first CLI scaffold: identity, introspection verbs, packaging and CI. The
35
+ domain is specified in three open issues, which are the requirements baseline:
36
+
37
+ - [#1](https://github.com/agentculture/events-cli/issues/1) — the spec:
38
+ CloudEvents envelope, pipeline model, CLI surface, security defaults,
39
+ acceptance criteria, non-goals.
40
+ - [#2](https://github.com/agentculture/events-cli/issues/2) — the agentfront
41
+ binding, the `watch` request/response constraint, and mesh lane boundaries.
42
+ - [#3](https://github.com/agentculture/events-cli/issues/3) — the first
43
+ co-located consumer and the direct-MQTT loopback slice.
44
+
45
+ ## Where it sits in the mesh
46
+
47
+ [`culture`](https://github.com/agentculture/culture) already moves messages
48
+ between agents, so the boundary matters:
49
+
50
+ - **culture** carries *agent conversation* — peer-to-peer, human-readable,
51
+ presence-oriented, IRC-shaped.
52
+ - **events-cli** carries *machine events* — typed immutable envelopes,
53
+ correlation and causation, durable history, pipeline runs, app-to-app as much
54
+ as agent-to-agent.
55
+
56
+ ## Quickstart
57
+
58
+ The installed console command is **`events`** (`events-cli` is the PyPI
59
+ distribution name; the import package is `events`).
60
+
61
+ ```bash
62
+ uv sync
63
+ uv run pytest -n auto # run the test suite
64
+ uv run events whoami # identity from culture.yaml
65
+ uv run events learn # self-teaching prompt (add --json)
66
+ uv run teken cli doctor . --strict # the agent-first rubric gate CI runs
67
+ ```
68
+
69
+ The runtime package has no third-party dependencies, so it also runs straight
70
+ from a checkout:
71
+
72
+ ```bash
73
+ PYTHONPATH=. python3 -m events doctor
74
+ ```
75
+
76
+ ## CLI
77
+
78
+ | Verb | What it does |
79
+ |------|--------------|
80
+ | `whoami` | Report this agent's nick, version, backend, and model from `culture.yaml`. |
81
+ | `learn` | Print a structured self-teaching prompt. |
82
+ | `explain <path>` | Markdown docs for any noun/verb path. |
83
+ | `overview` | Read-only descriptive snapshot of the agent. |
84
+ | `doctor` | Check the agent-identity invariants (prompt-file-present, backend-consistency). |
85
+ | `cli overview` | Describe the CLI surface itself. |
86
+
87
+ Every command supports `--json`. Results go to stdout, errors and diagnostics to
88
+ stderr (never mixed). Exit codes: `0` success, `1` user error, `2` environment
89
+ error, `3+` reserved. No Python traceback ever reaches stderr — every failure
90
+ carries a `remediation` hint.
91
+
92
+ The stack, event and pipeline verbs sketched in
93
+ [#1](https://github.com/agentculture/events-cli/issues/1) (`events up`,
94
+ `events emit`, `events watch`, `events pipeline …`) are **not implemented yet**.
95
+
96
+ ## Development
97
+
98
+ ```bash
99
+ uv run pytest -n auto # full suite
100
+ uv run pytest tests/test_cli.py -v # one file
101
+ uv run pytest -k whoami -v # one test
102
+
103
+ uv run black --check events tests
104
+ uv run isort --check-only events tests
105
+ uv run flake8 events tests
106
+ uv run bandit -c pyproject.toml -r events
107
+ ```
108
+
109
+ Every PR bumps the version — the `version-check` CI job blocks merge otherwise.
110
+ Pushing to `main` publishes to PyPI via Trusted Publishing; PRs do a TestPyPI
111
+ dry-run.
112
+
113
+ See [`CLAUDE.md`](CLAUDE.md) for the architecture, the design constraints that
114
+ are expensive to retrofit, and the full contributor conventions.
115
+
116
+ ## License
117
+
118
+ Apache 2.0 — see [`LICENSE`](LICENSE).