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.
- {events_cli-0.6.1 → events_cli-0.7.0}/CHANGELOG.md +62 -0
- events_cli-0.7.0/CLAUDE.md +409 -0
- events_cli-0.7.0/PKG-INFO +118 -0
- events_cli-0.7.0/README.md +101 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/events/__init__.py +1 -1
- {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/__init__.py +8 -2
- {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_commands/cli.py +5 -5
- {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_commands/doctor.py +2 -2
- {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_commands/explain.py +2 -2
- {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_commands/learn.py +27 -16
- {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_commands/overview.py +3 -3
- {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_commands/whoami.py +4 -3
- {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_errors.py +1 -1
- events_cli-0.7.0/events/cli/_prog.py +39 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/events/explain/__init__.py +4 -1
- events_cli-0.7.0/events/explain/catalog.py +150 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/pyproject.toml +1 -1
- events_cli-0.7.0/tests/test_cli.py +213 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/tests/test_cli_introspection.py +5 -5
- {events_cli-0.6.1 → events_cli-0.7.0}/uv.lock +1 -1
- events_cli-0.6.1/CLAUDE.md +0 -28
- events_cli-0.6.1/PKG-INFO +0 -76
- events_cli-0.6.1/README.md +0 -59
- events_cli-0.6.1/events/explain/catalog.py +0 -130
- events_cli-0.6.1/tests/test_cli.py +0 -115
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/agent-config/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/agent-config/data/backend-fingerprints.yaml +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/agent-config/scripts/show.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/ask-colleague/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/ask-colleague/prompts/explore.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/ask-colleague/prompts/review.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/ask-colleague/prompts/write.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/ask-colleague/scripts/ask-colleague.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/challenge/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/cicd/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/cicd/scripts/workflow.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/communicate/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/communicate/scripts/templates/skill-new-brief.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/deviate/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/pypi-maintainer/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/pypi-maintainer/scripts/switch-source.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/recall/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/recall/scripts/recall.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/remember/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/remember/scripts/remember.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/run-tests/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/scope/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/sonarclaude/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/spec-to-plan/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/summarize-delivery/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/think/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/think/scripts/think.sh +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/version-bump/SKILL.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills/version-bump/scripts/bump.py +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.claude/skills.local.yaml.example +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.flake8 +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.github/workflows/publish.yml +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.github/workflows/tests.yml +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.gitignore +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/.markdownlint-cli2.yaml +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/AGENTS.colleague.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/LICENSE +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/culture.yaml +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/docs/skill-sources.md +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/events/__main__.py +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_commands/__init__.py +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/events/cli/_output.py +0 -0
- {events_cli-0.6.1 → events_cli-0.7.0}/sonar-project.properties +0 -0
- {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).
|