Skip to main content

extend-commands-api

Add or extend Redis commands in the Lettuce client API end-to-end — a new core command, a family of new commands, an extension to an existing command's options, or a module/area command (Search/JSON/Bloom/VectorSet). Gathers evidence first (HLD document, the server-side PR in the repo owning the command family, live verification against the Dockerized test environment with redis-cli), plans the full implementation matrix in plan mode, then implements across all API flavors with unit and integration tests. Trigger on "add support for the <X> command", "implement <REDIS COMMAND> in Lettuce", "extend <command> with <option>", or adding a new argument/overload to an existing command.

소스 정보

저장소
redis/lettuce
최근 소스 활동
2026년 8월 6일 14:21
감지된 SKILL.md 언어
영어
스타
5,780
포크
1,109

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
extend-commands-api
description
Add or extend Redis commands in the Lettuce client API end-to-end — a new core command, a family of new commands, an extension to an existing command's options, or a module/area command (Search/JSON/Bloom/VectorSet). Gathers evidence first (HLD document, the server-side PR in the repo owning the command family, live verification against the Dockerized test environment with redis-cli), plans the full implementation matrix in plan mode, then implements across all API flavors with unit and integration tests. Trigger on "add support for the <X> command", "implement <REDIS COMMAND> in Lettuce", "extend <command> with <option>", or adding a new argument/overload to an existing command.
allowed-tools
Bash(mvn *), Bash(make *), Bash(redis-cli *), Bash(gh *)
# Extend the Lettuce Commands API Implement a new Redis command (or extend an existing one) in Lettuce, following the conventions maintainers enforce in review. The workflow is evidence-first: prove the command's behavior on a real server before designing the Java API. Read [.agents/docs/architecture.md](../../../.agents/docs/architecture.md) first for the model behind this flow — especially that the sync/async/reactive/Kotlin command **interfaces are hand-edited source files kept in lockstep by the API consistency test suite** (see [.agents/docs/api-consistency.md](../../../.agents/docs/api-consistency.md)). > **Historical caveat when reading old PRs**: PRs merged before the generator removal > also edit `src/test/java/io/lettuce/core/api/<Group>Commands.java` — the old > template source files. Those files **no longer exist**; do not recreate them. The > flavor interfaces are now edited directly. ## Phase 0 — Gather evidence BEFORE planning Do all of the following before writing any plan or code: 1. **Ask the user for the HLD.** Interactively ask for the path to a markdown file containing the High-Level Design for the command(s) (or confirm none exists). If a path is given, read it fully — it is the primary source for syntax, semantics, reply shape per RESP2/RESP3, and edge cases. 2. **Find the server-side PR in the repo that owns the command.** Route the search by command family — module command families are developed in their owning repositories, not in `redis/redis`: | Command family | Repository | Syntax source | |----------------|------------|---------------| | Core commands, vector sets (`VADD`, …) | `redis/redis` | `src/commands/*.json` | | Search (`FT.*`) | `RediSearch/RediSearch` | PR diff + command docs (no `src/commands/*.json`) | | JSON (`JSON.*`) | `RedisJSON/RedisJSON` | PR diff + command docs | | Probabilistic (`BF.*`, `CF.*`, `CMS.*`, `TOPK.*`, `TDIGEST.*`) | `RedisBloom/RedisBloom` | PR diff + command docs | | Time series (`TS.*`) | `RedisTimeSeries/RedisTimeSeries` | PR diff + command docs | If the search comes up empty in the routed repo, fall back to `redis/redis` (and vice versa) before concluding there is no server PR. First verify that `gh` works in the current (sandboxed) environment: ```bash gh auth status ``` Sandboxes often block access to credential files, so `gh` may report unauthenticated here even though it works on the user's machine. If so, ask the user for permission to run these specific read-only `gh` commands outside the sandbox; only fall back to the unauthenticated GitHub REST API if they decline. Then search for the PR that adds/extends the command on the server: ```bash gh search prs --repo <owning-repo> "<COMMAND NAME>" --limit 10 gh pr view <num> --repo <owning-repo> gh pr diff <num> --repo <owning-repo> # in redis/redis: src/commands/*.json has exact syntax ``` Extract: exact wire syntax (argument order and optionality), reply type per **RESP2 and RESP3** (they can differ — this determines the `CommandOutput` and the reactive `Mono`/`Flux` mapping), error conditions, and the **first server version carrying the feature** (drives `@EnabledOnCommand` gating and the test env version). Note whether the server marks the feature *experimental/preview*. 3. **Verify the command exists on the "next" Redis OSS version.** Start the integration environment using the **highest** version the Makefile supports (check `SUPPORTED_TEST_ENV_VERSIONS` in the `Makefile`, or the `src/test/resources/docker-env/.env.vX.XX` files — pick numerically, `8.10` > `8.8`): ```bash grep SUPPORTED_TEST_ENV_VERSIONS Makefile make start version=8.10 ``` Then probe the running servers. Test connection defaults live in `src/test/java/io/lettuce/test/settings/TestSettings.java` — the main standalone node is `localhost:6479` (no auth); module commands (`FT.*`, `JSON.*`, `BF.*`, `VADD`, …) run on the stack node at `localhost:16379`: ```bash redis-cli -p 6479 INFO server | grep redis_version redis-cli -p 6479 COMMAND INFO <COMMAND> # non-empty → command exists redis-cli -p 6479 COMMAND DOCS <COMMAND> # arity/args — compare with the server PR ``` For a NEW command, `COMMAND INFO` must return a non-empty reply. For an EXTENDED command, additionally invoke it with the new syntax against a scratch key and confirm the server accepts it (no `ERR syntax error` / `ERR unknown argument`). **If the command/option is missing on the latest env version**, the image tag pinned in the `.env.vX.XX` file is too old. Ask the user for a `redislabs/client-libs-test` image tag that contains it (e.g. an RC/edge build — tags are listed at https://hub.docker.com/r/redislabs/client-libs-test/tags; you may check and suggest candidates), then: ```bash make stop CLIENT_LIBS_TEST_IMAGE_TAG=<tag> make start ``` If the PR should also move CI to that build, update the newest `.env.vX.XX` pin as part of the change — command PRs do this when they need a fresh server (cf. the hotkeys PR bumping `.env.v8.6`). If no tag carries the feature, report it and proceed: implementation continues, integration tests stay gated until an image ships the change. Keep the environment running for later test runs. 4. **Create redis-cli showcase test cases.** Once the command is available, derive a small set of redis-cli scenarios from the HLD and the server PR and run them. These serve two purposes: - **Smoke test**: prove the documented behavior holds — happy path, each new option/keyword, reply shape (run with `redis-cli -3` too when RESP3 replies differ), edge cases and error conditions (missing key, out-of-range args, conflicting options) — so the Java implementation is built against observed replies, not assumptions. - **Showcase**: each scenario should read as a mini use-case explaining WHY the command/option exists. Prefer realistic data over `foo`/`bar`. Save the scenarios as a commented script in a scratch file (one block per use-case: a "what this demonstrates" comment, the commands, the observed reply pasted back as comments). Carry this forward: it feeds the Phase 1 plan, the test assertions, and the PR description. If the command could not be made available on any image, still write the scenarios as *expected* transcripts and mark them unverified. 5. **Read the testing and consistency docs**: [.agents/docs/integration-testing.md](../../../.agents/docs/integration-testing.md) (environment, `*UnitTests` vs `*IntegrationTests` naming, base/overload test structure, running one test) and [.agents/docs/api-consistency.md](../../../.agents/docs/api-consistency.md) (the flavors and their return-type mapping rules). 6. **Trace one analogous existing command** end-to-end (same command group, similar reply shape) so the plan mirrors real code, not guesswork. Good reference PRs, validated against git history: | Commit | What it exemplifies | |--------|---------------------| | `7ca3e9cc0` (CLIENT NO-TOUCH #3776) | Minimal new command: all 6 flavors + builder + 2 dispatch layers + Kotlin impl + builder unit test + integration test | | `312ecc7b4` (INCREX #3746) | New command with args: self-typed abstract args base, new value type, new `CommandOutput`s, per-type args/output unit tests, **creating a missing RESP2 overload class** | | `b009df37b` (XNACK #3728) | New command with an enum-valued argument | | `9a0899875` (BITOP extensions #3334) | New operations/overloads on an existing command | | `c603c8120` (HSCAN NOVALUES #2816) | New overload rippling into helpers (`ScanIterator`, `ScanStream`, `ScanFlow`) | | `aa7b4b0be` (stream idempotency #3637) | Option added to an existing `*Args` class flows through with **no interface change** (the `XAddArgs` part) | | `a209fba70` (CAS/CAD #3512) | Read-only command registered in `ReadOnlyCommands` + count test update | | `838a4d39a` (HOTKEYS #3638) | Map-shaped reply via `ComplexOutput` + a `*ReplyParser`, cluster interface additions, full integration overload set, `.env` image bump | | `6567f2d5e` (RediSearch #3375) | Whole new command area: own `Redis<Area>CommandBuilder`, `arguments`/reply-parser packages | **Conventions beat precedent.** Traced reference commands can predate the current conventions; when the reference code conflicts with a written rule in this skill or the linked docs, the rule wins. Example: `sintercard(K...)` ships without a single-key overload because it predates the one-overload-per-varargs rule — a new command mirroring it must still add the single-argument overload. 7. **Determine the `@since` version** — recipe owned by [.agents/docs/javadoc.md](../../../.agents/docs/javadoc.md): ```bash mvn help:evaluate -Dexpression=project.version -q -DforceStdout ``` Drop `-SNAPSHOT` and the patch digit (`7.7.0-SNAPSHOT` → `@since 7.7`). **The evidence is your working context, not a deliverable.** Hold it in mind to drive the work — do not paste it back to the maintainer or pause for "spec approval." The only stop for the maintainer is the Phase 1 plan approval; surface genuinely ambiguous design choices *there*, with the proposed sync signatures, rather than interrupting earlier. ## Phase 1 — Plan mode, then explicit approval Enter **plan mode**. Using the evidence, classify the change with the decision tree below and enumerate the exact file-by-file touch list, the test matrix, and the gating annotations. The plan must contain: - A short "what this feature enables" section built from the showcase scenarios (Phase 0 step 4), including one or two representative command/reply transcripts. - **The proposed sync interface signature(s) with their Javadoc.** The sync interface is the human-authored **API contract**; every other flavor is derived from it and it is costly to change once mirrored. Plan approval is the maintainer's sign-off on that contract — if the signatures must deviate later during implementation, stop and re-confirm before mirroring. - **The complete overload set, enumerated.** For every varargs parameter, list the matching single-argument overload; for a multi-key command with an `*Args` object, use the `List<K>` shape (no varargs) and list the fixed-arity convenience overloads instead — both house rules live in "Types & args conventions". Justify any overload you omit so the maintainer signs off on the exception. An overload discovered missing in review means reworking all six flavors. Present the plan and **explicitly ask permission to execute** before implementing. Do not start editing files until the user approves. Once approved, copy this checklist into your working notes and tick items off: ``` Extend-commands progress: - [ ] 0. Evidence: HLD, server PR, live probe + showcase, RESP2/RESP3 replies, @since - [ ] 1. Plan approved — incl. the sync signature(s), the API contract - [ ] 2. Types: argument & response types (they must exist before any interface edit) - [ ] 3. Sync interface: full overload set (varargs → single-arg too) + Javadoc (@param constraints + @throws for builder-validated preconditions) - [ ] 4. Mirror: async, reactive, Kotlin, NodeSelection×2 — consistency tests pass - [ ] 5. Implementations: CommandType/Keyword, builder, async, reactive, Kotlin impl - [ ] 6. Tests: args/builder/output unit tests + integration base/overloads - [ ] 7. Docs: entry in the current-release section of docs/new-features.md - [ ] 8. Verify: mvn clean test + a single integration test run; then make stop ``` ## Decision tree — what kind of change is this? **A. Extension of an existing command that fits an existing `*Args` class** (new option token / new field): - Touch ONLY the `*Args` class (+ `CommandKeyword` for new tokens) + tests. Do NOT touch the command interfaces, builder signatures, or dispatch layers — the existing `args.build(commandArgs)` delegation carries the new option through automatically (cf. the `XAddArgs` part of the stream-idempotency PR). - Add a fluent setter returning `this`; register new tokens in `src/main/java/io/lettuce/core/protocol/CommandKeyword.java`. - If the reply shape grows, extend the response model/output backward-compatibly. **B. New command(s) in an existing group — core or module area** — the FULL matrix, in this order (types first — every flavor references them, so they must exist to compile). For a command joining an **existing module area** (a new `FT.*` method in the Search group, a new `JSON.*` method, …) the same matrix applies with the area substitutions: the group is the area's flavor interfaces, the builder is the area's `Redis<Area>CommandBuilder` (+ its `Redis<Area>CommandBuilderUnitTests`), argument/reply types go in the area package, and gating/tests follow the module rules in D (capability probe, stack node). The dispatch layers are the same `AbstractRedisAsyncCommands` / `AbstractRedisReactiveCommands` and Kotlin `*Impl.kt` as for core commands. 1. **Argument/response types** — see "Types & args conventions" below. 2. **Sync interface** `src/main/java/io/lettuce/core/api/sync/<Group>Commands.java` — pick the group by command family (STRING → `RedisStringCommands`, HASH → `RedisHashCommands`, generic-key → `RedisKeyCommands`, …). This is the reference signature + Javadoc all flavors mirror (see the [writing-javadoc](../writing-javadoc/SKILL.md) skill; `@since` mandatory): ```java /** * Returns the length of the string value stored at {@code key}. * * @param key the key, must not be {@code null}. * @return the length of the string at {@code key}, or {@code 0} when {@code key} does * not exist. * @since 7.7 */ Long strlen(K key); ``` Three contract rules to apply while designing the signatures and their Javadoc: - **Multi-key commands that also take an `*Args` object take the keys as `List<K>`, not varargs.** A varargs parameter must come last, so a trailing options object can only follow it via an awkward leading placement (cf. the older `sintercard(long limit, K... keys)`). For new commands, take the keys as `List<K>` so the `*Args` argument comes last — `sunioncard(List<K> keys, SUnionCardArgs args)` — and add fixed-arity convenience overloads (`(K key1, K key2)` and their `*Args` variants) instead of a varargs form. Since there is no varargs parameter here, the single-argument-overload rule below does not apply; add only the overloads that are meaningful — e.g. **no single-key `sunioncard`/`sdiffcard`**, whose one-key union/difference is just `SCARD`. - **Every varargs parameter gets a single-argument overload** — `foo(K key, V value)` alongside `foo(K key, V... values)`. This is a hard convention for new commands and overrides any traced precedent that lacks it (older commands predate the rule). Does not apply to the `List<K>`-shaped multi-key-with-`*Args` commands above, which take no varargs. - **Validated preconditions are contract.** Any constraint the builder will enforce (null checks, non-empty varargs) must be stated in the `@param` text with the house phrases (`must not be {@code null}.`, `must not be empty.`) *and* documented with a matching `@throws IllegalArgumentException if …` tag — forms owned by [.agents/docs/javadoc.md](../../../.agents/docs/javadoc.md). Both are then mirrored to every flavor. 3. **Mirror to every flavor**: async, reactive, Kotlin coroutines, and the two cluster node-selection interfaces (`NodeSelection<Group>Commands` / `NodeSelection<Group>AsyncCommands`), applying the per-flavor return-type mapping rules from [.agents/docs/api-consistency.md](../../../.agents/docs/api-consistency.md). 4. **Protocol enums** — command name in `src/main/java/io/lettuce/core/protocol/CommandType.java` (wire bytes derive from the enum name); subcommand tokens in `CommandKeyword`. **Never add a `CommandKeyword` that duplicates a name already in `CommandType`** (e.g. `SET`, `DISCARD`) — the builder static-imports both enums and the bare name becomes ambiguous; reference `CommandType.<NAME>` directly instead. 5. **Builder** — `RedisCommandBuilder.java` (or the area builder, see D): null checks via `LettuceAssert`/`notNullKey`, `CommandArgs` in **wire order**, the `CommandOutput` chosen from the observed RESP2/RESP3 replies: ```java public Command<K, V, Long> strlen(K key) { notNullKey(key); return createCommand(STRLEN, new IntegerOutput<>(codec), key); }
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기