Skip to content

Commit 0cbf4dc

Browse files
[Internal] Update AGENTS.md (#4110)
* Update AGENTS.md * add rational * Update AGENTS.md Co-authored-by: célina <hanouticelina@gmail.com> * add suggestions --------- Co-authored-by: célina <hanouticelina@gmail.com>
1 parent b3a8eb5 commit 0cbf4dc

1 file changed

Lines changed: 57 additions & 25 deletions

File tree

AGENTS.md

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -65,36 +65,50 @@ Python client library for the Hugging Face Hub. Source code is in `src/huggingfa
6565
- `tqdm.py` — Custom progress bar wrappers.
6666
- Other: `_datetime.py`, `_dotenv.py`, `_git_credential.py`, `_subprocess.py`, `_telemetry.py`, `sha.py`, `_xet.py`, ...
6767

68-
### Serialization (`src/huggingface_hub/serialization/`)
68+
### CLI (`src/huggingface_hub/cli/`)
6969

70-
- `_torch.py``save_torch_model()`, `load_torch_model()`, state-dict splitting, safetensors support.
71-
- `_dduf.py` — DDUF format support.
70+
Entry point: `hf.py` (Typer app). Subcommands split into modules: `auth.py`, `repos.py`, `spaces.py`, `models.py`, `datasets.py`, `buckets.py`, `jobs.py`, `collections.py`, `webhooks.py`, etc.
7271

73-
### CLI (`src/huggingface_hub/cli/`)
72+
#### Adding CLI commands
73+
74+
**Structure & naming conventions:**
75+
76+
- Commands are organized as **Typer groups** registered in `hf.py` (e.g. `app.add_typer(spaces_cli, name="spaces")`).
77+
- Each module creates its app with `typer_factory(help="...")` and defines commands with `@app.command("name")`.
78+
- Use **pipe-separated aliases**: `@app.command("list | ls")` registers both `list` and `ls`.
79+
- Use standard **verb names**: `ls`/`list`, `info`, `create`, `set`, `delete`, `update`.
80+
- For **sub-resources** with multiple operations, create a nested subgroup: `volumes_cli = typer_factory(...)` then `spaces_cli.add_typer(volumes_cli, name="volumes")` → gives `hf spaces volumes ls/set/delete`. See `repos.py` for examples (`tag_cli`, `branch_cli`).
81+
82+
**Reusable option types** (from `_cli_utils.py` — import and use these, don't reinvent):
83+
84+
- `TokenOpt`, `RepoIdArg`, `RepoTypeOpt`, `RevisionOpt` — standard auth/repo options.
85+
- `SearchOpt`, `AuthorOpt`, `FilterOpt`, `LimitOpt` — list/search options.
86+
- `FormatWithAutoOpt` / `FormatOpt` — output format (`auto|json|human|quiet|agent`).
87+
- `VolumesOpt` + `parse_volumes()``-v`/`--volume` flag with `hf://[TYPE/]SOURCE:/MOUNT_PATH[:ro]` syntax.
88+
- `get_hf_api(token=token)` — creates an `HfApi` instance with token.
89+
- `api_object_to_dict(obj)` — converts dataclass API objects to dicts for output.
90+
91+
**Output** (from `_output.py` — use the `out` singleton):
7492

75-
Entry point: `hf.py` (Typer app). Subcommands split into modules: `auth.py`, `repo.py`, `repo_files.py`, etc.
93+
Convention: stderr for prompts/warnings, stdout for data. Warnings go to stderr even in quiet/json modes.
7694

77-
### Tests (`tests/`)
95+
- `out.table(items)` — list results (auto-formats as padded table / TSV / JSON depending on `--format`).
96+
- `out.dict(data)` — single-item detail view.
97+
- `out.result("Message", key=value, ...)` — success summary with green checkmark.
98+
- `out.confirm("Prompt?", yes=yes)` — confirmation for destructive operations. Pair with a `-y`/`--yes` flag.
99+
- `out.hint("...")` — actionable follow-up suggestion. Try to add hints when adding new commands or refactoring a command. Hints should preferably reuse the input args to be specific to the current use case. Example: `out.hint(f"Use 'hf buckets ls {bucket_id}' to list files from the bucket.")` after a bucket creation.
100+
- `out.text()`, `out.warning()`, `out.error()` — free-form output.
78101

79-
- One `test_<module>.py` per source module (e.g. `test_hf_api.py`, `test_file_download.py`, `test_inference_client.py`).
80-
- `conftest.py` — Fixtures (temp cache dirs, env patching).
81-
- `testing_utils.py` / `testing_constants.py` — Shared test helpers and staging-repo constants.
82-
- `cassettes/` — Recorded HTTP responses for offline tests (`@pytest.mark.vcr`). **Do not add new cassettes.**
83-
- `fixtures/` — Static test data.
102+
**Destructive operations** should use `out.confirm()` with a `yes: Annotated[bool, typer.Option("-y", "--yes", help="Answer Yes to prompt automatically.")]` parameter.
84103

85-
### Dev scripts (`utils/`)
104+
**Errors**: raise `CLIError("message")` for user-facing errors. Never wrap API calls with try/except for `RepositoryNotFoundError`, `RevisionNotFoundError`, etc. (already done globally)
86105

87-
- `check_static_imports.py` — Ensures `__init__.py` static imports match the lazy loader.
88-
- `check_all_variable.py` — Validates `__all__` exports.
89-
- `generate_async_inference_client.py` — Generates `AsyncInferenceClient` from sync client.
90-
- `generate_inference_types.py` — Generates inference type definitions.
91-
- `generate_cli_reference.py` — Generates CLI docs.
106+
**Generated docs**: `make style` auto-regenerates `docs/source/en/package_reference/cli.md` via `utils/generate_cli_reference.py`. Don't edit that file by hand.
92107

93-
## Style
108+
**Guides**: update the CLI guide `docs/sources/en/guides/cli.md` when adding / updating CLI commands. If the command is specific to a topic which has its own guide in `docs/sources/en/guides`, add a mention in the guide as well, using the same tone as the existing guide.
109+
110+
**CLI tests**: add tests in `tests/test_cli.py`. Try to group tests into classes when relevant. Do not add a test for each specific use case / parameter set. Usually testing the 1-2 main use cases is enough.
94111

95-
- Max line length: 119 chars.
96-
- Linter/formatter: `ruff`.
97-
- Imports sorted by `ruff` (isort-compatible).
98112

99113
### Type checking: local vs CI
100114

@@ -105,8 +119,26 @@ Locally, `make quality` runs `ty check src` using whatever version of `ty` is in
105119

106120
If CI fails on type checks that pass locally, the likely cause is a newer `ty` version or a `mypy`-only diagnostic. Fix the reported errors rather than downgrading the checker.
107121

108-
## Testing notes
122+
## Commits & PRs
123+
124+
- **Commit message prefix**: use `[Area]` prefix matching the scope, e.g. `[CLI] Add ...`, `[CLI] Fix ...`, `[Inference] ...`.
125+
- **PR title**: short (under 70 chars), same `[Area]` prefix convention.
126+
- **PR description**: keep it casual. Include a `## Summary` with a few bullet points and real CLI/code **examples** from manual testing (copy-paste terminal output). No need for a formal "Test plan" section. Call out breaking changes explicitly if any. It is important to document any decision taken in the PR or instructions provided in the prompt while working on the PR, ideally with the rationale behind it.
127+
128+
## Code conventions
129+
130+
### Simplicity is the #1 priority
131+
132+
- No premature abstractions. No unnecessary generalization.
133+
- Don't implement features until they're actually needed.
134+
- Don't accept parameters without a use case.
135+
- No redundant `try`/`except` - don't catch errors already handled up the call stack.
136+
- Prefer strictness now, relax later.
137+
138+
### Follow Python 3.10+ idioms
109139

110-
- Tests use `pytest` with `pytest-env` setting `HUGGINGFACE_CO_STAGING=1` (tests hit staging Hub by default).
111-
- Most integration tests require `HF_TOKEN` to be set. Unit tests don't.
112-
- do not register or commit new HTTP cassettes.
140+
- `match`/`case` over `if`/`elif` chains when dispatching on a value.
141+
- `str | None` not `Optional[str]`.
142+
- f-strings over `.format()` or `%`.
143+
- Walrus operator (`:=`) when it improves readability.
144+
- Comprehensions over `map`/`filter` with lambdas.

0 commit comments

Comments
 (0)