Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
Format follows [Keep a Changelog](https://keepachangelog.com/). This project
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.1] - 2026-07-10

### Added

- `explain` now resolves the console-script name (`fleet`) as well as the distribution name (`fleet-cli`); both key the root catalog entry.
- Regression test `test_explain_self_name_matches_console_script` pins that both spellings and the bare root render identically.

### Changed

- `CLAUDE.md` expanded from the scaffold seed into full repo guidance: agent-first CLI contracts, the noun-group specialization checklist, test-module map, and the `fleet` vs `fleet-cli` naming split.

### Fixed

- The agent-first rubric gate (`teken cli doctor . --strict`) now passes 26/26; it was failing `explain_self` and turning the CI `lint` job red on every PR.
- The `explain` remediation hint pointed at `fleet-cli explain fleet-cli`, which is not a runnable command; it now names `fleet explain fleet`.

## [0.4.0] - 2026-06-23

### Added
Expand Down
275 changes: 256 additions & 19 deletions CLAUDE.md

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ Agent and CLI to control a fleet of drones (multiple UAVs), built on drone-cli
```bash
uv sync
uv run pytest -n auto # run the test suite
uv run fleet-cli whoami # identity from culture.yaml
uv run fleet-cli learn # self-teaching prompt (add --json)
uv run fleet whoami # identity from culture.yaml
uv run fleet learn # self-teaching prompt (add --json)
uv run teken cli doctor . --strict # the agent-first rubric gate CI runs
```

The console script is **`fleet`** — `fleet-cli` is the distribution name on PyPI,
not a runnable command. `uv run python -m fleet …` works too.

## CLI

| Verb | What it does |
Expand Down
5 changes: 4 additions & 1 deletion fleet/explain/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ def resolve(path: tuple[str, ...]) -> str:
raise CliError(
code=EXIT_USER_ERROR,
message=f"no explain entry for: {display}",
remediation="list entries with: fleet-cli explain fleet-cli",
# Name the console script, not the distribution: `fleet-cli` is not a
# runnable command, so a hint spelling it that way sends the reader to
# a "command not found".
remediation="list entries with: fleet explain fleet",
)


Expand Down
13 changes: 11 additions & 2 deletions fleet/explain/catalog.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
"""Markdown catalog for ``fleet-cli explain <path>``.

Each entry is verbatim markdown. Keys are command-path tuples. The empty tuple
and ``("fleet-cli",)`` both resolve to the root entry.
resolves to the root entry, as do both spellings of this CLI's name: the
distribution name ``("fleet-cli",)`` and the console-script name ``("fleet",)``.
Both are registered because the agent-first rubric derives the CLI's self-name
from the ``[project.scripts]`` entry point (``fleet``) and probes
``explain fleet``, while the doc text throughout addresses it as ``fleet-cli``.

Keep bodies self-contained: an agent reading one entry should get enough
context without chaining reads.
Expand Down Expand Up @@ -118,7 +122,12 @@

ENTRIES: dict[tuple[str, ...], str] = {
(): _ROOT,
("fleet-cli",): _ROOT,
("fleet-cli",): _ROOT, # distribution name (prog=, doc text)
# Console-script name. RESERVED: `resolve()` does exact-tuple lookup, so this
# key cannot also document a noun group named `fleet` — and the rubric's
# `explain_self` bundle requires it to render the root entry. Name any future
# noun `drone`/`swarm`/`mission`, never `fleet`. See CLAUDE.md.
("fleet",): _ROOT,
("whoami",): _WHOAMI,
("learn",): _LEARN,
("explain",): _EXPLAIN,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "fleet-cli"
version = "0.4.0"
version = "0.4.1"
description = "Agent and CLI to control a fleet of drones (multiple UAVs), built on drone-cli — coordinate, command, and monitor many drones at once."
readme = "README.md"
license = "Apache-2.0"
Expand Down
18 changes: 18 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,21 @@ def test_every_catalog_path_resolves(capsys: pytest.CaptureFixture[str]) -> None
rc = main(["explain", *path])
assert rc == 0, f"explain {' '.join(path)} failed"
capsys.readouterr()


def test_explain_self_name_matches_console_script(capsys: pytest.CaptureFixture[str]) -> None:
"""Both spellings of this CLI's name resolve to the root entry.

The agent-first rubric derives the CLI's self-name from the
``[project.scripts]`` entry point (``fleet``) and probes ``explain fleet``,
but the doc text addresses the CLI as ``fleet-cli``. Registering only the
latter fails the rubric's ``explain_self`` bundle. Pinned here so the gap
surfaces in pytest (fast) rather than only in `teken cli doctor` (slow).
"""
assert main(["explain"]) == 0
root = capsys.readouterr().out

for name in ("fleet", "fleet-cli"):
rc = main(["explain", name])
assert rc == 0, f"explain {name} failed"
assert capsys.readouterr().out == root, f"explain {name} != bare root"
48 changes: 24 additions & 24 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading