From 54dc92afd28a59dea59efdabe2f82bd4023f4ddc Mon Sep 17 00:00:00 2001 From: Ori Nachum Date: Mon, 24 Aug 2026 07:37:23 +0300 Subject: [PATCH] docs: init CLAUDE.md runtime prompt, correct README to match reality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-initializes CLAUDE.md from the bootstrap seed into a full runtime prompt and brings README.md in line with what the repo actually is. CLAUDE.md: repo status (the analyse-propose-apply lane is scaffold-only today), build/lint/test commands as CI runs them, the three-layer CLI architecture and the contracts the teken rubric gate enforces, an 8-step checklist for adding a noun/verb, and the working conventions (version-bump-every-PR, the cicd lane, cite-don't-import skills, worktree layout, memory discipline). README.md: adds a Status section stating the refactoring lane is not implemented yet, corrects the vendored skill count (11 -> 18), and replaces the clone-a-template "Make it your own" checklist with a Contributing section. Fixes a broken Quickstart: README instructed `uv run refactoring-cli`, which is the distribution name rather than the console script, so every documented command failed with `Failed to spawn: refactoring-cli`. The scaffold rename is incomplete (package dir and script are `refactor`; dist name, argparse prog, help text, explain catalog and tests all say `refactoring-cli`) — corrected to `refactor` and the divergence is now documented in both files with a discovery command and target list. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01PoE5ij1LNYZYi52xaogwRJ --- CHANGELOG.md | 11 +++ CLAUDE.md | 261 +++++++++++++++++++++++++++++++++++++++++++++---- README.md | 54 ++++++---- pyproject.toml | 2 +- uv.lock | 62 ++++++------ 5 files changed, 321 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2fd59c..2c7f8f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ 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.6.2] - 2026-08-24 + +### Changed + +- Re-initialized `CLAUDE.md` from the bootstrap seed into a full runtime prompt: repo status (the refactoring lane is scaffold-only today), build/lint/test commands as CI runs them, the three-layer CLI architecture and its rubric-enforced contracts, an 8-step checklist for adding a noun/verb, and the working conventions (version-bump-every-PR, the cicd lane, cite-don't-import skills, worktree layout, memory discipline). +- Updated `README.md` to describe this agent rather than the template it was scaffolded from: added a Status section stating the analyse-propose-apply surface is not implemented yet, corrected the vendored skill count from 11 to 18, and replaced the clone-a-template `Make it your own` checklist with a Contributing section. + +### Fixed + +- `README.md` Quickstart invoked `refactoring-cli`, which is the distribution name, not the console script — the commands failed with `Failed to spawn: refactoring-cli`. Corrected to `refactor`, and documented the incomplete template rename (package/script `refactor` vs `refactoring-cli` everywhere else) in both `README.md` and `CLAUDE.md`. + ## [0.6.1] - 2026-07-20 ### Added diff --git a/CLAUDE.md b/CLAUDE.md index 6799384..6c6c075 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,28 +1,251 @@ -# CLAUDE.md — seed / bootstrap placeholder +# CLAUDE.md -> **This is a self-initializing seed, not a finished runtime prompt.** -> Run `/init` (or describe the agent's domain to your AI assistant) to -> re-initialize this file into a full runtime prompt, using the description -> below and the scaffolded repo as context. +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. -## Agent +## What this repo is — and what it is not yet -This repository hosts the **refactoring-cli** agent. +**refactoring-cli** is an AgentCulture mesh agent whose intended domain is: +analyse a Python codebase for structural problems and code smells, propose +behaviour-preserving refactorings with a rationale, and apply them under test. +It is the successor to the retired `refactor-cli`, whose atomic-transformation +lane it absorbs. -## Description +**None of that domain logic exists yet.** The tree is still the +`culture-agent-template` scaffold (`git log`: two commits, the second being +`scaffold refactoring-cli from culture-agent-template`). What ships today is the +agent-first CLI skeleton — `whoami`, `learn`, `explain`, `overview`, `doctor`, +`cli overview` — plus a mesh identity, a vendored skill kit, and a full CI/deploy +baseline. Several docstrings and catalog entries still describe the repo as "a +clonable template for AgentCulture mesh agents"; that prose is inherited, not a +statement about this agent's purpose. -Refactors Python code. Analyses a Python codebase for structural problems and code smells, proposes behaviour-preserving refactorings with a rationale, and applies them under test. Successor to the retired refactor-cli, whose atomic-transformation lane it absorbs. +Treat the scaffold as the *contract* a future `refactor`/`analyse`/`apply` noun +group must satisfy, not as throwaway code — the `teken` rubric gate in CI enforces +its shape. -## Re-init instruction +## Commands -This file is a seed. To expand it into your full runtime prompt: +```bash +uv sync # install (dev group included) -1. Open this repo in Claude Code (or your preferred AI assistant). -2. Run `/init` — the assistant will read the repo, incorporate the description - above, and replace this seed with a complete `CLAUDE.md`. -3. Commit the result. +# Tests +uv run pytest # full suite +uv run pytest -n auto # parallel (what CI runs) +uv run pytest tests/test_cli.py::test_whoami_json -v # a single test +uv run pytest -k "doctor" -v # by name +uv run pytest -n auto --cov=refactor --cov-report=term # with coverage +bash .claude/skills/run-tests/scripts/test.sh --ci # exact CI invocation -Until you run `/init`, `refactoring-cli` satisfies the `steward doctor` -`prompt-file-present` and `backend-consistency` invariants (a `CLAUDE.md` -exists and `culture.yaml` declares `backend: claude`) but the prompt is not -yet tailored to this agent's domain. +# Lint — CI runs all five, all must pass +uv run black --check refactor tests +uv run isort --check-only refactor tests +uv run flake8 refactor tests +uv run bandit -c pyproject.toml -r refactor +markdownlint-cli2 "**/*.md" "#node_modules" "#.local" "#.claude/skills" + +# The agent-first rubric gate (CI job `lint` → "afi rubric gate") +uv run teken cli doctor . --strict + +# Run the CLI +uv run refactor whoami # note: `refactor`, not `refactoring-cli` — see below +uv run python -m refactor learn --json +``` + +`black` and `isort` are checked but not auto-run — apply fixes with +`uv run black refactor tests && uv run isort refactor tests`. Line length is 100 +everywhere (`.flake8`, `[tool.black]`, `[tool.isort]` agree). + +Coverage has `fail_under = 60` in `pyproject.toml`, so `--cov` runs fail below +that threshold. `relative_files = true` is load-bearing: absolute paths in +`coverage.xml` don't map to `sonar.sources=refactor` and SonarCloud reports empty +coverage. + +## Known inconsistency: `refactor` vs `refactoring-cli` + +The scaffold rename is incomplete. Today: + +| Thing | Value | +|-------|-------| +| Python package directory | `refactor/` | +| Console script (`[project.scripts]`) | `refactor` | +| Distribution / project name | `refactoring-cli` | +| argparse `prog=`, all help text, `explain` catalog, tests | `refactoring-cli` | + +So `uv run refactoring-cli whoami` — as `README.md` instructs — **fails**; the +working invocation is `uv run refactor whoami` or `python -m refactor`. The +rubric gate passes because `teken` discovers the console script name from +`pyproject.toml`. `whoami` reports `nick: refactoring-cli` because the nick comes +from `culture.yaml`, not from the package name. + +Before adding domain code, decide which name wins and finish the rename. Discover +every occurrence first — the name is hard-coded in ~100 places: + +```bash +git grep -n -E 'refactoring[-_]cli|refactoring_cli' -- . ':!uv.lock' ':!.claude/skills' +git grep -n -w -E 'refactor' -- refactor tests pyproject.toml sonar-project.properties +``` + +Targets: `pyproject.toml` (`name`, `[project.scripts]`, `[tool.hatch...packages]`, +`[tool.coverage.run] source`, `[tool.isort] known_first_party`, `[tool.bandit]`), +the package directory itself, every import in `refactor/` and `tests/`, +`_ISSUES_URL` in `refactor/cli/__init__.py`, `sonar-project.properties` +(`sonar.projectKey` must match the registered SonarCloud project), `README.md`, +and the `refactor/explain/catalog.py` `ENTRIES` keys (which currently register +*both* `("refactoring-cli",)` and `("refactor",)` as root aliases). + +## Architecture + +Three layers, each with a contract the rubric gate checks: + +**1. Entry + dispatch — `refactor/cli/__init__.py`** + +`main(argv)` → `_build_parser()` → `parse_args` → `_dispatch(args)`. Two things +here are subtle and easy to break: + +- `_CliArgumentParser` overrides `argparse.ArgumentParser.error()` so *parse-time* + failures (unknown verb, bad flag) render through the same `error:` / `hint:` + contract as runtime failures and exit `1`, not argparse's default exit `2`. + Subparsers must be created with `parser_class=_CliArgumentParser` or they fall + back to argparse's behaviour and the rubric's `error_has_hint` check fails. The + `cli` noun group does this explicitly via `parser_class=type(p)` — copy that + pattern for any nested noun. +- `_json_hint` is a class attribute set by scanning raw argv *before* parsing, + because `args.json` doesn't exist yet when a parse error fires. That is how + `--json` is honoured for argparse-level errors. +- `_dispatch` wraps *any* non-`CliError` exception into a `CliError`, so no Python + traceback ever reaches stderr (rubric: `no_traceback`). + +**2. Commands — `refactor/cli/_commands/*.py`** + +Every module exposes `register(sub: argparse._SubParsersAction) -> None` and is +wired in `_build_parser()`. Handlers return `int | None` (None ⇒ 0) and raise +`CliError` on failure — never `sys.exit`, never print to stderr directly. + +- `whoami.py` owns identity resolution and is imported by `doctor.py` and + `overview.py`. `find_culture_yaml()` walks up from `__file__`, **not** the cwd — + the agent's identity must be its own, not whatever `culture.yaml` sits in the + caller's directory. In a wheel install no `culture.yaml` ships and everything + falls back to literals. +- `read_agent_fields()` hand-parses `culture.yaml` line by line. This is + deliberate: `[project] dependencies = []` — the runtime package has **zero** + third-party dependencies, so there is no PyYAML to lean on. Keep it that way; + new deps belong in `[dependency-groups] dev`. +- `doctor.py` mirrors the two invariants `steward doctor` checks — + `prompt_file_present` and backend-consistency — via the `_PROMPT_FILE` map + (`claude`→`CLAUDE.md`, `colleague`→`AGENTS.colleague.md`, `acp`→`AGENTS.md`, + `gemini`→`GEMINI.md`), plus `skills_present`. It emits the rubric-shaped + `{healthy, checks: [{id, passed, severity, message, remediation}]}`. +- `overview.py` holds the shared section/render helpers that `cli.py` reuses. + Descriptive verbs must never hard-fail on a bad target path — hence `overview`'s + ignored optional `target` positional (rubric: + `overview_graceful_on_bad_path`). + +**3. Explain catalog — `refactor/explain/`** + +`ENTRIES: dict[tuple[str, ...], str]` maps command-path tuples to verbatim +markdown; `resolve()` raises `CliError` with a hint on a miss. Bodies are meant +to be self-contained — an agent reading one entry shouldn't need to chain reads. +`tests/test_cli.py::test_every_catalog_path_resolves` walks every key. + +**The output contract — `refactor/cli/_output.py`** + +Results to **stdout**, errors and diagnostics to **stderr**, never mixed, in both +text and JSON mode. Text errors are exactly two lines (`error:` then `hint:`); the +`hint:` prefix is what the rubric greps for. Exit codes live in `_errors.py`: +`0` success, `1` user error, `2` environment error, `3+` reserved. This contract +is a stable API for agent consumers — the docstrings say `stable-contract` for a +reason. + +### Adding a noun group or verb + +The rubric and tests span several files, so a new verb touches all of these: + +1. New module in `refactor/cli/_commands/` with `register(sub)`; add + `--json` to every parser you create. +2. Register it in `_build_parser()` (there's a marked comment block for it). +3. Add a `refactor/explain/catalog.py` entry — and one for each sub-verb path. +4. Add it to `_TEXT` **and** `_as_json_payload()["commands"]` in `learn.py`. +5. Add it to `_VERBS` in `overview.py` (feeds both `overview` and `cli overview`). +6. Tests in `tests/` covering text mode, `--json` shape, and the error path. +7. If the noun takes action-verbs, it must also expose ` overview` — the + rubric's `overview_cli_noun_exists` check. `_commands/cli.py` exists solely as + the reference implementation of that pattern. +8. Re-run `uv run teken cli doctor . --strict`. + +## Conventions and workflow + +**Every PR bumps the version — including docs-only, config-only, and CI-only +changes.** The `version-check` CI job compares `pyproject.toml` against +`origin/main` and fails (with a PR comment) when they match. Use the skill: + +```bash +echo '{"added":["..."],"changed":["..."],"fixed":["..."]}' \ + | python3 .claude/skills/version-bump/scripts/bump.py minor +``` + +It updates `pyproject.toml` and prepends a Keep-a-Changelog entry in one step. + +**PR lane.** Use the `cicd` skill (`.claude/skills/cicd/scripts/workflow.sh`), +which delegates `lint | open | read | reply | delta` to `devex pr` and adds +`status` (SonarCloud gate + hotspots + unresolved-thread tally) and `await` +(poll then gate, non-zero on Sonar `ERROR` or unresolved threads). Requires +`devex >= 0.21`, `gh`, `jq`, `curl` on PATH. Don't hand-sign PR replies — the +scripts resolve the nick from `culture.yaml` and append `- refactoring-cli +(Claude)` themselves. For issue bodies and PR descriptions the scripts don't +author, sign manually as `- refactoring-cli (Claude)`. + +**Vendored skills are cited, not owned.** Everything under `.claude/skills/` is +copied verbatim from `guildmaster`, `devague`, or `colleague` — **do not edit +them**. `docs/skill-sources.md` is the provenance ledger, including three tracked +local divergences (`agex`→`devex` in-place patch; `ask-colleague` vendored direct +from `colleague`; `scope`/`challenge`/`deviate`/`summarize-delivery` vendored +direct from `devague` to avoid guildmaster's added wrapper scripts). Follow its +re-sync procedure when pulling updates, and lift wanted changes upstream rather +than patching locally. Both `markdownlint` and Sonar exclude the directory. +Note that every vendored `SKILL.md` must carry `type: command` in its frontmatter +— `core.skill_loader` silently skips any that lacks it. + +**Worktrees** you create by hand (workforce fan-out lanes, scratch checkouts) go +in `../.worktrees.refactoring-cli//` — one repo-named directory beside the +checkout, so a stale-tree sweep across this multi-project workspace can tell +which trees belong to which repo. Scope branch prefixes to the work at hand; a +plain `agent/*` prefix collides with leftovers from earlier fan-outs and fails +`git worktree add -b`. Tear down with `git worktree remove ` — `prune` only +clears metadata for directories already gone. The vendored `assign-to-workforce` +skill's example uses a shared `../worktrees/` path and `agent/` +branches; it's cited verbatim and must not be edited, so override both when +following it. `ask-colleague`'s read-only verbs make their own throwaway +worktrees under `$TMPDIR` and reap them on exit — out of scope. + +**Memory.** Recall before, remember after. Run `/recall` before non-trivial work +so prior decisions get built on rather than re-derived, and `/remember` when a +non-obvious decision, constraint, fix-and-why, or hard-won gotcha surfaces. The +vendored wrappers default to this agent's personal private scope (`--scope +refactoring-cli --visibility private`, store `~/.eidetic/memory`); pass +`--visibility public` to contribute to the shared pool. Requires `eidetic` on +PATH. + +**Second opinions.** Reach for `ask-colleague` reflexively, not only when asked: +`review` before presenting or opening a PR on a non-trivial committed diff, and +`explore` for a fresh read of an unfamiliar area. Both are read-only and isolated +in a throwaway worktree, so they're always safe. The side-effecting +`write --apply` / `write --pr` needs the user's go-ahead first. + +**Local config.** Copy `.claude/skills.local.yaml.example` → +`.claude/skills.local.yaml` (git-ignored) to point skills at the Culture server +manifest and sibling checkouts. + +## Identity and deploy + +`culture.yaml` declares one agent: `suffix: refactoring-cli`, `backend: +colleague`, with a pinned model. Because the backend is `colleague`, the +load-bearing resident prompt file is **`AGENTS.colleague.md`**, not this file — +`doctor`'s backend-consistency check will fail if it goes missing. Changing +`backend` means changing the prompt file to match *and* teaching `doctor`'s +`_PROMPT_FILE` map; `test_doctor_recognizes_declared_backend` guards exactly that. + +`.github/workflows/publish.yml` publishes to PyPI via Trusted Publishing on push +to `main` (env `pypi`) and to TestPyPI on same-repo PRs (env `testpypi`, +version suffixed `.devN`). Both are path-filtered on `pyproject.toml` and +`refactor/**`, and fork PRs skip publishing. SonarCloud gating in `tests.yml` is +guarded by `env.SONAR_TOKEN != ''`, so token-less and fork runs stay green. diff --git a/README.md b/README.md index 0b56054..7f2da22 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,30 @@ Refactors Python code. Analyses a Python codebase for structural problems and code smells, proposes behaviour-preserving refactorings with a rationale, and applies them under test. Successor to the retired refactor-cli, whose atomic-transformation lane it absorbs. -## What you get +## Status + +**The refactoring lane is not implemented yet.** This repo currently carries the +scaffold it was minted from: an agent-first CLI skeleton (identity and +introspection verbs), a mesh identity, the vendored skill kit, and a working +build/test/deploy baseline. The analyse-propose-apply surface described above is +the target, not today's behaviour. + +What that scaffold buys is a contract for the domain verbs to land inside — +`--json` on every command, a strict stdout/stderr split, structured errors with +remediation hints, and a documented exit-code policy — all enforced in CI by the +agent-first rubric gate. + +## What's here - **An agent-first CLI** cited from [teken](https://github.com/agentculture/teken) (`afi-cli`) — the runtime package has no third-party dependencies. - **A mesh identity** — `culture.yaml` (`suffix` + `backend`) and the matching - resident prompt file (`AGENTS.colleague.md`, since this template runs + resident prompt file (`AGENTS.colleague.md`, since this agent runs `backend: colleague`). -- **The canonical guildmaster skill kit** (11 skills) under `.claude/skills/`, - vendored cite-don't-import. See [`docs/skill-sources.md`](docs/skill-sources.md). +- **The vendored skill kit** (18 skills) under `.claude/skills/`, cited + cite-don't-import from guildmaster, devague, and colleague. See + [`docs/skill-sources.md`](docs/skill-sources.md) for provenance and the + re-sync procedure. - **A build + deploy baseline** — pytest, lint, the agent-first rubric gate, and PyPI Trusted Publishing wired into GitHub Actions. @@ -19,13 +34,16 @@ Refactors Python code. Analyses a Python codebase for structural problems and co ```bash uv sync uv run pytest -n auto # run the test suite -uv run refactoring-cli whoami # identity from culture.yaml -uv run refactoring-cli learn # self-teaching prompt (add --json) +uv run refactor whoami # identity from culture.yaml +uv run refactor learn # self-teaching prompt (add --json) uv run teken cli doctor . --strict # the agent-first rubric gate CI runs ``` ## CLI +The console script is **`refactor`**; `refactoring-cli` is the distribution name +on PyPI. `python -m refactor` works too. + | Verb | What it does | |------|--------------| | `whoami` | Report this agent's nick, version, backend, and model from `culture.yaml`. | @@ -39,20 +57,20 @@ Every command supports `--json`. Results go to stdout, errors/diagnostics to stderr (never mixed). Exit codes: `0` success, `1` user error, `2` environment error, `3+` reserved. -## Make it your own +### Naming + +The rename from the template this repo was scaffolded from is incomplete: the +package directory and console script are `refactor`, while the distribution +name, argparse `prog`, help text, `explain` catalog, and tests all say +`refactoring-cli`. Finish the rename in one pass before the domain verbs land — +[`CLAUDE.md`](CLAUDE.md) carries the discovery command and the full target list. -1. Rename the package `refactor/` and the `refactoring-cli` - CLI/dist name throughout `pyproject.toml`, the package, `tests/`, - `sonar-project.properties`, and this `README.md`. The name is hard-coded in - ~100 places, so list every occurrence first — see the `git grep` discovery - command in [`CLAUDE.md`](CLAUDE.md), the authoritative rename procedure. -2. Edit `culture.yaml` with your `suffix` and `backend`. -3. Rewrite `CLAUDE.md` for your agent and run `/init`. -4. Re-vendor only the skills you need from guildmaster (see - [`docs/skill-sources.md`](docs/skill-sources.md)). +## Contributing -See [`CLAUDE.md`](CLAUDE.md) for the full conventions (version-bump-every-PR, -the `cicd` PR lane, deploy setup). +Every PR bumps the version, including docs-only and CI-only changes — a +`version-check` job blocks the merge otherwise. See [`CLAUDE.md`](CLAUDE.md) for +the full conventions (the `cicd` PR lane, the vendored-skills rule, worktree +layout, deploy setup). ## License diff --git a/pyproject.toml b/pyproject.toml index 72f8bfa..941f407 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "refactoring-cli" -version = "0.6.1" +version = "0.6.2" description = "Refactors Python code. Analyses a Python codebase for structural problems and code smells, proposes behaviour-preserving refactorings with a rationale, and applies them under test. Successor to the retired refactor-cli, whose atomic-transformation lane it absorbs." readme = "README.md" license = "Apache-2.0" diff --git a/uv.lock b/uv.lock index 3f0b61d..5a91da4 100644 --- a/uv.lock +++ b/uv.lock @@ -154,37 +154,6 @@ wheels = [ { 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" }, ] -[[package]] -name = "refactoring-cli" -version = "0.6.1" -source = { editable = "." } - -[package.dev-dependencies] -dev = [ - { name = "bandit" }, - { name = "black" }, - { name = "flake8" }, - { name = "isort" }, - { name = "pytest" }, - { name = "pytest-cov" }, - { name = "pytest-xdist" }, - { name = "teken" }, -] - -[package.metadata] - -[package.metadata.requires-dev] -dev = [ - { name = "bandit", specifier = ">=1.7.5" }, - { name = "black", specifier = ">=23.7.0" }, - { name = "flake8", specifier = ">=6.1" }, - { name = "isort", specifier = ">=5.12.0" }, - { name = "pytest", specifier = ">=8.0" }, - { name = "pytest-cov", specifier = ">=4.1" }, - { name = "pytest-xdist", specifier = ">=3.0" }, - { name = "teken", specifier = ">=0.8" }, -] - [[package]] name = "execnet" version = "2.1.2" @@ -446,6 +415,37 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "refactoring-cli" +version = "0.6.2" +source = { editable = "." } + +[package.dev-dependencies] +dev = [ + { name = "bandit" }, + { name = "black" }, + { name = "flake8" }, + { name = "isort" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "pytest-xdist" }, + { name = "teken" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [ + { name = "bandit", specifier = ">=1.7.5" }, + { name = "black", specifier = ">=23.7.0" }, + { name = "flake8", specifier = ">=6.1" }, + { name = "isort", specifier = ">=5.12.0" }, + { name = "pytest", specifier = ">=8.0" }, + { name = "pytest-cov", specifier = ">=4.1" }, + { name = "pytest-xdist", specifier = ">=3.0" }, + { name = "teken", specifier = ">=0.8" }, +] + [[package]] name = "rich" version = "15.0.0"