arm101-cli 0.3.2__tar.gz → 0.3.3__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 (72) hide show
  1. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/CHANGELOG.md +11 -0
  2. arm101_cli-0.3.3/CLAUDE.md +155 -0
  3. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/PKG-INFO +4 -4
  4. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/README.md +3 -3
  5. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/arm101/explain/catalog.py +5 -1
  6. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/pyproject.toml +1 -1
  7. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/tests/test_cli.py +23 -0
  8. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/uv.lock +31 -31
  9. arm101_cli-0.3.2/CLAUDE.md +0 -28
  10. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/agent-config/SKILL.md +0 -0
  11. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/agent-config/data/backend-fingerprints.yaml +0 -0
  12. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/agent-config/scripts/show.sh +0 -0
  13. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/ask-colleague/SKILL.md +0 -0
  14. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/ask-colleague/prompts/explore.md +0 -0
  15. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/ask-colleague/prompts/review.md +0 -0
  16. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/ask-colleague/prompts/write.md +0 -0
  17. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/ask-colleague/scripts/ask-colleague.sh +0 -0
  18. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
  19. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
  20. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/cicd/SKILL.md +0 -0
  21. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  22. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  23. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  24. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  25. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  26. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/communicate/SKILL.md +0 -0
  27. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  28. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  29. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  30. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  31. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/communicate/scripts/templates/skill-new-brief.md +0 -0
  32. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
  33. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
  34. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
  35. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/pypi-maintainer/SKILL.md +0 -0
  36. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/pypi-maintainer/scripts/switch-source.sh +0 -0
  37. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/run-tests/SKILL.md +0 -0
  38. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/run-tests/scripts/test.sh +0 -0
  39. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/sonarclaude/SKILL.md +0 -0
  40. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
  41. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/spec-to-plan/SKILL.md +0 -0
  42. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
  43. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/think/SKILL.md +0 -0
  44. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/think/scripts/think.sh +0 -0
  45. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/version-bump/SKILL.md +0 -0
  46. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills/version-bump/scripts/bump.py +0 -0
  47. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.claude/skills.local.yaml.example +0 -0
  48. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.flake8 +0 -0
  49. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.github/workflows/publish.yml +0 -0
  50. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.github/workflows/tests.yml +0 -0
  51. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.gitignore +0 -0
  52. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/.markdownlint-cli2.yaml +0 -0
  53. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/AGENTS.colleague.md +0 -0
  54. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/LICENSE +0 -0
  55. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/arm101/__init__.py +0 -0
  56. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/arm101/__main__.py +0 -0
  57. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/arm101/cli/__init__.py +0 -0
  58. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/arm101/cli/_commands/__init__.py +0 -0
  59. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/arm101/cli/_commands/cli.py +0 -0
  60. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/arm101/cli/_commands/doctor.py +0 -0
  61. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/arm101/cli/_commands/explain.py +0 -0
  62. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/arm101/cli/_commands/learn.py +0 -0
  63. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/arm101/cli/_commands/overview.py +0 -0
  64. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/arm101/cli/_commands/whoami.py +0 -0
  65. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/arm101/cli/_errors.py +0 -0
  66. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/arm101/cli/_output.py +0 -0
  67. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/arm101/explain/__init__.py +0 -0
  68. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/culture.yaml +0 -0
  69. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/docs/skill-sources.md +0 -0
  70. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/sonar-project.properties +0 -0
  71. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/tests/__init__.py +0 -0
  72. {arm101_cli-0.3.2 → arm101_cli-0.3.3}/tests/test_cli_introspection.py +0 -0
@@ -5,6 +5,17 @@ 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.3.3] - 2026-06-20
9
+
10
+ ### Changed
11
+
12
+ - CLAUDE.md: replaced the pre-/init seed placeholder with a full runtime guide — documents the actual repo state (mesh-agent scaffold, no arm/gripper code yet), the CLI dispatch/registration/error/output contracts, the explain-catalog lockstep rule, the agent-first rubric, and the CI-gating AgentCulture conventions.
13
+
14
+ ### Fixed
15
+
16
+ - README: Quickstart now invokes the real console script `arm101` (was `arm101-cli`, which fails to spawn); mesh-identity line now reads `AGENTS.colleague.md` for this agent's `backend: colleague` (was a stale `CLAUDE.md` / `backend: claude`).
17
+ - explain catalog: add a root entry for the `arm101` console-script name. The agent-first rubric's `explain_self` check runs `explain <project-script-name>` (`arm101`), which was failing because the catalog only keyed the internal prog name `arm101-cli` — a latent scaffold bug the rubric gate only exercises via the script name. Locked in by a regression test asserting every `[project.scripts]` name resolves.
18
+
8
19
  ## [0.3.2] - 2026-06-18
9
20
 
10
21
  ### Added
@@ -0,0 +1,155 @@
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 repo actually is right now
6
+
7
+ The package name and description say "Agent and CLI for controlling SO-ARM101
8
+ robotic arm grippers," but **no arm/gripper control code exists yet.** What is
9
+ here is the AgentCulture **mesh-agent scaffold** (cloned from
10
+ `culture-agent-template`): an agent-first introspection CLI, a mesh identity, the
11
+ vendored guildmaster skill kit, and a build/CI/deploy baseline. The gripper
12
+ domain is the *destination*; treat the current CLI as the chassis you extend to
13
+ get there, following the patterns below.
14
+
15
+ Two consequences worth internalizing before you touch anything:
16
+
17
+ - **The runtime agent prompt is `AGENTS.colleague.md`, not this file.**
18
+ `culture.yaml` declares `backend: colleague`, and the backend→prompt-file map
19
+ (see `doctor` below) resolves `colleague` → `AGENTS.colleague.md`. This
20
+ `CLAUDE.md` is guidance for *Claude Code working in the repo*; editing it does
21
+ not change the mesh agent's runtime behavior. (Note: the pre-`/init` seed text
22
+ and some README prose still say `backend: claude` / `CLAUDE.md` — that is stale;
23
+ the CHANGELOG `0.3.0` entry records the promotion to `colleague`.)
24
+ - **The installed console script is `arm101`, not `arm101-cli`.**
25
+ `[project.scripts]` defines `arm101 = "arm101.cli:main"`. The README quickstart
26
+ (`uv run arm101-cli whoami`) is wrong and will fail with "Failed to spawn." Use
27
+ `uv run arm101 …` or `python -m arm101 …`. The *internal* prog name is still
28
+ `arm101-cli`, so `--help` text, error messages, and JSON payloads all say
29
+ `arm101-cli` — that string is intentional in output, just not as the binary.
30
+
31
+ ## Common commands
32
+
33
+ ```bash
34
+ uv sync # create .venv, install runtime + dev deps
35
+ uv run pytest -n auto # full test suite (xdist parallel)
36
+ uv run pytest tests/test_cli.py::test_whoami_text # a single test
37
+ uv run pytest -n auto --cov=arm101 --cov-report=term # tests with coverage (CI gate: fail_under=60)
38
+ uv run arm101 whoami # run the CLI (note: 'arm101', not 'arm101-cli')
39
+
40
+ # Lint — CI runs all of these; run them before opening a PR
41
+ uv run black --check arm101 tests
42
+ uv run isort --check-only arm101 tests
43
+ uv run flake8 arm101 tests
44
+ uv run bandit -c pyproject.toml -r arm101
45
+ markdownlint-cli2 "**/*.md" "#node_modules" "#.local" "#.claude/skills" "#.teken"
46
+
47
+ uv run teken cli doctor . --strict # the agent-first rubric gate (see below)
48
+ ```
49
+
50
+ The runtime package has **zero third-party dependencies** (`dependencies = []`),
51
+ on purpose. `teken` and the test/lint tools are dev-only. Keep it that way unless
52
+ adding the gripper layer genuinely requires a hardware library — and if it does,
53
+ isolate that dependency so the introspection CLI still imports clean.
54
+
55
+ ## CLI architecture
56
+
57
+ Everything hangs off `arm101/cli/__init__.py:main()`. The shape is worth
58
+ understanding before adding commands, because three cross-cutting contracts are
59
+ enforced by tests and by the rubric gate.
60
+
61
+ **Registration pattern.** `_build_parser()` imports each module under
62
+ `arm101/cli/_commands/` and calls its `register(sub)` function, which adds a
63
+ subparser and wires `func` + `--json` via `set_defaults`. To add a global verb:
64
+ write `_commands/<verb>.py` with a `register()`, then add one import + call in
65
+ `_build_parser()`. To add a **noun group** (a subcommand with its own verbs —
66
+ this is how gripper control will likely land, e.g. `arm101 gripper open`), mirror
67
+ `_commands/cli.py`: create the noun's subparsers with
68
+ `parser_class=type(p)` so child parse errors route through the structured error
69
+ contract instead of argparse's default `exit 2`.
70
+
71
+ **Error contract** (`_errors.py` + the `_dispatch`/`_CliArgumentParser` plumbing).
72
+ Every failure raises `CliError(code, message, remediation)`; `main()` catches it
73
+ and renders via `_output.emit_error`. Any *other* exception is wrapped so no
74
+ Python traceback ever leaks to stderr. Argparse-level errors (unknown verb,
75
+ missing arg) are also captured: `_CliArgumentParser.error()` emits the structured
76
+ form. Because parse errors happen before `args.json` exists, `main()` pre-scans
77
+ raw argv for `--json` and stashes it on the class-level `_CliArgumentParser._json_hint`.
78
+ **Handlers must raise `CliError` on failure — never `sys.exit`, never a bare
79
+ print-and-return.**
80
+
81
+ **Output contract** (`_output.py`). Results → stdout, errors/diagnostics →
82
+ stderr, **never mixed**; JSON mode keeps the same split. Every command takes
83
+ `--json`. Exit codes: `0` success, `1` user-input error, `2` environment error,
84
+ `3+` reserved (constants in `_errors.py`). Use `emit_result` / `emit_error` /
85
+ `emit_diagnostic` rather than calling `print`.
86
+
87
+ **The explain catalog** (`arm101/explain/catalog.py`). `ENTRIES` is a dict keyed
88
+ by command-path tuples (`("whoami",)`, `("cli", "overview")`, `()` and
89
+ `("arm101-cli",)` both = root). `explain` resolves a path to verbatim markdown.
90
+ The test `test_every_catalog_path_resolves` asserts every entry renders, but
91
+ nothing forces a *new* verb to have one — so when you add a verb, you must update
92
+ **three places in lockstep** or the docs silently drift: the catalog entry, the
93
+ `_VERBS` list in `overview.py`, and the `_TEXT`/`_as_json_payload` blocks in
94
+ `learn.py`.
95
+
96
+ **Identity reading** (`whoami.py`). `culture.yaml` is parsed by hand (no YAML
97
+ dependency, to keep runtime deps empty) — only the documented flat
98
+ `suffix`/`backend`/`model` shape is understood; anything fancier falls back to
99
+ defaults. `find_culture_yaml()` walks up from `__file__`, so identity is the
100
+ agent's own even when invoked from another directory; a wheel install (no
101
+ `culture.yaml` alongside the package) falls back to literal defaults and `doctor`
102
+ reports a single info check.
103
+
104
+ ## The agent-first rubric (why some code looks the way it does)
105
+
106
+ `teken cli doctor . --strict` enforces a seven-bundle "agent-first" rubric in CI,
107
+ and several otherwise-odd shapes exist only to satisfy it — don't "simplify" them
108
+ away:
109
+
110
+ - `learn` must be ≥200 chars and mention purpose, command map, exit codes,
111
+ `--json`, and `explain`.
112
+ - Any noun with action-verbs must also expose `overview` — that's the entire
113
+ reason the `cli` noun group exists (`cli overview` describes the CLI surface,
114
+ distinct from the global `overview`, which describes the *agent*).
115
+ - Descriptive verbs must never hard-fail on a bad path — hence `overview` accepts
116
+ an ignored positional `target` and still exits 0.
117
+
118
+ This is separate from the in-package `arm101 doctor`, which checks **agent-identity
119
+ invariants**: `prompt-file-present` and `backend-consistency` (the
120
+ `backend → prompt file` map is `claude`→`CLAUDE.md`, `colleague`→`AGENTS.colleague.md`,
121
+ `acp`→`AGENTS.md`, `gemini`→`GEMINI.md`), plus `skills-present`. If you change the
122
+ backend in `culture.yaml`, teach `doctor` the matching prompt file or
123
+ `test_doctor_recognizes_declared_backend` fails.
124
+
125
+ ## AgentCulture conventions that gate CI
126
+
127
+ - **Bump the version on every PR — even docs/config/CI-only changes.** The
128
+ `version-check` job in `.github/workflows/tests.yml` fails the PR if
129
+ `pyproject.toml`'s version equals `main`'s. Use the `version-bump` skill (updates
130
+ `pyproject.toml` + prepends a Keep-a-Changelog entry to `CHANGELOG.md`).
131
+ - **PR lifecycle goes through the `cicd` skill**, which delegates to `devex pr` and
132
+ adds `status` (SonarCloud quality gate) and `await` (blocks until green / threads
133
+ resolved). PR comments auto-sign as `- arm101-cli (Claude)` via the skill's
134
+ `_resolve-nick.sh` (resolved from `culture.yaml`); don't hand-sign inside `cicd`.
135
+ - **SonarCloud gates the `test` job** when `SONAR_TOKEN` is set
136
+ (`sonar.qualitygate.wait=true`). Token-less repos and fork PRs skip the scan and
137
+ stay green. `coverage.run.relative_files = true` is load-bearing — without it
138
+ `coverage.xml` paths don't map to `sonar.sources=arm101` and coverage reports 0%.
139
+ - **PyPI publish is Trusted Publishing via OIDC** (`.github/workflows/publish.yml`):
140
+ push to `main` → PyPI; PR (same-repo) → a `.devN` build to TestPyPI. No tokens.
141
+
142
+ ## The vendored skill kit — cite-don't-import
143
+
144
+ `.claude/skills/` holds skills **vendored** from `guildmaster` (a few from
145
+ `colleague`/`devague`); `docs/skill-sources.md` is the authoritative provenance
146
+ ledger with the per-skill re-sync procedure. **Do not hand-edit skill script
147
+ bodies** — the only sanctioned local edits are (a) consumer-identifying prose in
148
+ `SKILL.md` and (b) adding `type: command` to frontmatter (load-bearing: the
149
+ culture backend's `core.skill_loader` silently skips any `SKILL.md` lacking it).
150
+ Two tracked divergences from "always cite guildmaster" are documented in the
151
+ ledger: the `agex`→`devex` rename and `outsource`→`ask-colleague` (vendored
152
+ directly from `colleague` until guildmaster re-broadcasts). Reach for
153
+ `ask-colleague` reflexively for a diverse second opinion — `review`/`explore` are
154
+ read-only and always safe; side-effecting `write --apply`/`--pr` needs the user's
155
+ go-ahead.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arm101-cli
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Agent and CLI for controlling SO-ARM101 robotic arm grippers
5
5
  Project-URL: Homepage, https://github.com/agentculture/arm101-cli
6
6
  Project-URL: Issues, https://github.com/agentculture/arm101-cli/issues
@@ -24,7 +24,7 @@ Agent and CLI for controlling SO-ARM101 robotic arm grippers
24
24
  - **An agent-first CLI** cited from [teken](https://github.com/agentculture/teken)
25
25
  (`afi-cli`) — the runtime package has no third-party dependencies.
26
26
  - **A mesh identity** — `culture.yaml` (`suffix` + `backend`) and the matching
27
- prompt file (`CLAUDE.md` for `backend: claude`).
27
+ prompt file (`AGENTS.colleague.md` for this agent's `backend: colleague`).
28
28
  - **The canonical guildmaster skill kit** (11 skills) under `.claude/skills/`,
29
29
  vendored cite-don't-import. See [`docs/skill-sources.md`](docs/skill-sources.md).
30
30
  - **A build + deploy baseline** — pytest, lint, the agent-first rubric gate, and
@@ -35,8 +35,8 @@ Agent and CLI for controlling SO-ARM101 robotic arm grippers
35
35
  ```bash
36
36
  uv sync
37
37
  uv run pytest -n auto # run the test suite
38
- uv run arm101-cli whoami # identity from culture.yaml
39
- uv run arm101-cli learn # self-teaching prompt (add --json)
38
+ uv run arm101 whoami # identity from culture.yaml (console script is 'arm101')
39
+ uv run arm101 learn # self-teaching prompt (add --json)
40
40
  uv run teken cli doctor . --strict # the agent-first rubric gate CI runs
41
41
  ```
42
42
 
@@ -7,7 +7,7 @@ Agent and CLI for controlling SO-ARM101 robotic arm grippers
7
7
  - **An agent-first CLI** cited from [teken](https://github.com/agentculture/teken)
8
8
  (`afi-cli`) — the runtime package has no third-party dependencies.
9
9
  - **A mesh identity** — `culture.yaml` (`suffix` + `backend`) and the matching
10
- prompt file (`CLAUDE.md` for `backend: claude`).
10
+ prompt file (`AGENTS.colleague.md` for this agent's `backend: colleague`).
11
11
  - **The canonical guildmaster skill kit** (11 skills) under `.claude/skills/`,
12
12
  vendored cite-don't-import. See [`docs/skill-sources.md`](docs/skill-sources.md).
13
13
  - **A build + deploy baseline** — pytest, lint, the agent-first rubric gate, and
@@ -18,8 +18,8 @@ Agent and CLI for controlling SO-ARM101 robotic arm grippers
18
18
  ```bash
19
19
  uv sync
20
20
  uv run pytest -n auto # run the test suite
21
- uv run arm101-cli whoami # identity from culture.yaml
22
- uv run arm101-cli learn # self-teaching prompt (add --json)
21
+ uv run arm101 whoami # identity from culture.yaml (console script is 'arm101')
22
+ uv run arm101 learn # self-teaching prompt (add --json)
23
23
  uv run teken cli doctor . --strict # the agent-first rubric gate CI runs
24
24
  ```
25
25
 
@@ -1,7 +1,10 @@
1
1
  """Markdown catalog for ``arm101-cli explain <path>``.
2
2
 
3
3
  Each entry is verbatim markdown. Keys are command-path tuples. The empty tuple
4
- and ``("arm101-cli",)`` both resolve to the root entry.
4
+ resolves to the root entry, as do both names the CLI answers to: the console
5
+ script ``("arm101",)`` (from ``[project.scripts]``) and the internal prog name
6
+ ``("arm101-cli",)``. The script-name key is load-bearing — the agent-first
7
+ rubric's ``explain_self`` check runs ``explain <project-script-name>``.
5
8
 
6
9
  Keep bodies self-contained: an agent reading one entry should get enough
7
10
  context without chaining reads.
@@ -118,6 +121,7 @@ itself (distinct from the global `overview`, which describes the agent).
118
121
 
119
122
  ENTRIES: dict[tuple[str, ...], str] = {
120
123
  (): _ROOT,
124
+ ("arm101",): _ROOT,
121
125
  ("arm101-cli",): _ROOT,
122
126
  ("whoami",): _WHOAMI,
123
127
  ("learn",): _LEARN,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "arm101-cli"
3
- version = "0.3.2"
3
+ version = "0.3.3"
4
4
  description = "Agent and CLI for controlling SO-ARM101 robotic arm grippers"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -113,3 +113,26 @@ def test_every_catalog_path_resolves(capsys: pytest.CaptureFixture[str]) -> None
113
113
  rc = main(["explain", *path])
114
114
  assert rc == 0, f"explain {' '.join(path)} failed"
115
115
  capsys.readouterr()
116
+
117
+
118
+ def test_explain_resolves_console_script_name(capsys: pytest.CaptureFixture[str]) -> None:
119
+ """Every `[project.scripts]` name must have an explain catalog entry.
120
+
121
+ The agent-first rubric's `explain_self` check runs `explain
122
+ <project-script-name>` — the console-script key (`arm101`), not the internal
123
+ prog name (`arm101-cli`). They differ here, so this guards the catalog
124
+ against drifting away from the script and re-breaking the rubric gate.
125
+ """
126
+ import tomllib
127
+ from pathlib import Path
128
+
129
+ pyproject = Path(__file__).resolve().parent.parent / "pyproject.toml"
130
+ if not pyproject.is_file(): # pragma: no cover - source checkout only
131
+ pytest.skip("pyproject.toml not alongside tests")
132
+ scripts = tomllib.loads(pyproject.read_text(encoding="utf-8"))["project"]["scripts"]
133
+ catalog = set(known_paths())
134
+ for name in scripts:
135
+ assert (name,) in catalog, f"explain catalog missing entry for console script '{name}'"
136
+ rc = main(["explain", name])
137
+ assert rc == 0
138
+ assert capsys.readouterr().out.startswith("#")
@@ -2,6 +2,37 @@ version = 1
2
2
  revision = 3
3
3
  requires-python = ">=3.12"
4
4
 
5
+ [[package]]
6
+ name = "arm101-cli"
7
+ version = "0.3.3"
8
+ source = { editable = "." }
9
+
10
+ [package.dev-dependencies]
11
+ dev = [
12
+ { name = "bandit" },
13
+ { name = "black" },
14
+ { name = "flake8" },
15
+ { name = "isort" },
16
+ { name = "pytest" },
17
+ { name = "pytest-cov" },
18
+ { name = "pytest-xdist" },
19
+ { name = "teken" },
20
+ ]
21
+
22
+ [package.metadata]
23
+
24
+ [package.metadata.requires-dev]
25
+ dev = [
26
+ { name = "bandit", specifier = ">=1.7.5" },
27
+ { name = "black", specifier = ">=23.7.0" },
28
+ { name = "flake8", specifier = ">=6.1" },
29
+ { name = "isort", specifier = ">=5.12.0" },
30
+ { name = "pytest", specifier = ">=8.0" },
31
+ { name = "pytest-cov", specifier = ">=4.1" },
32
+ { name = "pytest-xdist", specifier = ">=3.0" },
33
+ { name = "teken", specifier = ">=0.8" },
34
+ ]
35
+
5
36
  [[package]]
6
37
  name = "bandit"
7
38
  version = "1.9.4"
@@ -154,37 +185,6 @@ wheels = [
154
185
  { url = "https://files.pythonhosted.org/packages/61/e8/cb8e80d6f9f55b99588625062822bf946cf03ed06315df4bd8397f5632a1/coverage-7.14.0-py3-none-any.whl", hash = "sha256:8de5b61163aee3d05c8a2beab6f47913df7981dad1baf82c414d99158c286ab1", size = 211764, upload-time = "2026-05-10T18:02:29.538Z" },
155
186
  ]
156
187
 
157
- [[package]]
158
- name = "arm101-cli"
159
- version = "0.3.2"
160
- source = { editable = "." }
161
-
162
- [package.dev-dependencies]
163
- dev = [
164
- { name = "bandit" },
165
- { name = "black" },
166
- { name = "flake8" },
167
- { name = "isort" },
168
- { name = "pytest" },
169
- { name = "pytest-cov" },
170
- { name = "pytest-xdist" },
171
- { name = "teken" },
172
- ]
173
-
174
- [package.metadata]
175
-
176
- [package.metadata.requires-dev]
177
- dev = [
178
- { name = "bandit", specifier = ">=1.7.5" },
179
- { name = "black", specifier = ">=23.7.0" },
180
- { name = "flake8", specifier = ">=6.1" },
181
- { name = "isort", specifier = ">=5.12.0" },
182
- { name = "pytest", specifier = ">=8.0" },
183
- { name = "pytest-cov", specifier = ">=4.1" },
184
- { name = "pytest-xdist", specifier = ">=3.0" },
185
- { name = "teken", specifier = ">=0.8" },
186
- ]
187
-
188
188
  [[package]]
189
189
  name = "execnet"
190
190
  version = "2.1.2"
@@ -1,28 +0,0 @@
1
- # CLAUDE.md — seed / bootstrap placeholder
2
-
3
- > **This is a self-initializing seed, not a finished runtime prompt.**
4
- > Run `/init` (or describe the agent's domain to your AI assistant) to
5
- > re-initialize this file into a full runtime prompt, using the description
6
- > below and the scaffolded repo as context.
7
-
8
- ## Agent
9
-
10
- This repository hosts the **arm101-cli** agent.
11
-
12
- ## Description
13
-
14
- Agent and CLI for controlling SO-ARM101 robotic arm grippers
15
-
16
- ## Re-init instruction
17
-
18
- This file is a seed. To expand it into your full runtime prompt:
19
-
20
- 1. Open this repo in Claude Code (or your preferred AI assistant).
21
- 2. Run `/init` — the assistant will read the repo, incorporate the description
22
- above, and replace this seed with a complete `CLAUDE.md`.
23
- 3. Commit the result.
24
-
25
- Until you run `/init`, `arm101-cli` satisfies the `steward doctor`
26
- `prompt-file-present` and `backend-consistency` invariants (a `CLAUDE.md`
27
- exists and `culture.yaml` declares `backend: claude`) but the prompt is not
28
- yet tailored to this agent's domain.
File without changes
File without changes
File without changes
File without changes
File without changes