Official agent skill

Extend Commands API

by redis in redis/lettuce

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…

OfficialMITAuto-check: notesDatabases

Install Extend Commands API

skills CLI
$ npx skills add redis/lettuce --skill extend-commands-api -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install redis/lettuce extend-commands-api --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/redis/lettuce.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/extend-commands-api .claude/skills/extend-commands-api && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
extend-commands-api
GitHub stars
5.8k
Token cost
~7.7k tokens
SKILL.md length
3,287 words
Files
1
Skills in repo
2
Repo updated
First seen
Licence
MIT

At a glance

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…

  • Works in 2 steps: Gather evidence BEFORE planning → Plan mode, then explicit approval
  • Add support for the <X command
  • SKILL.md covers Phase 0 — Gather evidence…, Phase 1 — Plan mode, then…, Decision tree — what kind of… and Types & args conventions, plus 5 more sections
  • Calls mvn, make and gh

What it does

Extend Commands API is an agent skill from redis/lettuce, published by the product's own GitHub organization. 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…

Its SKILL.md is about 7.7k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Databases, covering Backend development, Planning and Integration testing. It works with Redis, Java, Amazon Web Services and Microsoft Azure. The licence is MIT.

When your agent uses it

  • Add support for the <X command
  • Implement <REDIS COMMAND in Lettuce
  • Extend <command with <option
  • Adding a new argument/overload to an existing command

Example prompts

  • “add support for the <X command”
  • “implement <REDIS COMMAND in Lettuce”
  • “extend <command with <option”
  • “/extend-commands-api”

Requirements

  • Docker
  • Pre-approved tools (allowed-tools): Bash(mvn *), Bash(make *), Bash(redis-cli *), Bash(gh *)

Workflow steps

2 steps, taken from the step headings in SKILL.md.

  1. Gather evidence BEFORE planning
  2. Plan mode, then explicit approval

What it can do on your machine

Read from SKILL.md and the folder at commit 114a3ef. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves these tools, so the agent can use them without asking each time:

    • Bash(mvn *)
    • Bash(make *)
    • Bash(redis-cli *)
    • Bash(gh *)

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • mvn
    • make
    • gh
    • redis-cli

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Links to these hosts (documentation or services it may open):

    • hub.docker.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Extend Commands API loads about 7.7k tokens when it runs. Until then it costs about 177 tokens; SKILL.md has 3,287 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~177
When it runs · the whole SKILL.md, loaded when a task matches
~7.7k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check: notes

The automated check noted patterns worth knowing about, such as sudo or a known installer.

  • NoteMentions a .env fileSKILL.md:71
    `src/test/resources/docker-env/.env.vX.XX` files — pick numerically, `8.10` >
  • NoteMentions a .env fileSKILL.md:91
    pinned in the `.env.vX.XX` file is too old. Ask the user for a
  • NoteMentions a .env fileSKILL.md:99
    ove CI to that build, update the newest `.env.vX.XX`
  • NoteMentions a .env fileSKILL.md:101
    (cf. the hotkeys PR bumping `.env.v8.6`). If no tag carries the feature, report
  • NoteMentions a .env fileSKILL.md:143
    ditions, full integration overload set, `.env` image bump |
  • NoteMentions a .env fileSKILL.md:497
    - [ ] `.env.vX.XX` image pin bumped if the feature needed a newer server build.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from redis/lettuce at commit 114a3ef, republished under its MIT licence (© redis). 3,287 words, ~7,726 tokens.

Download SKILL.mdSave it as .claude/skills/extend-commands-api/SKILL.md (or your agent's skills folder).
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 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).

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 familyRepositorySyntax source
    Core commands, vector sets (VADD, …)redis/redissrc/commands/*.json
    Search (FT.*)RediSearch/RediSearchPR diff + command docs (no src/commands/*.json)
    JSON (JSON.*)RedisJSON/RedisJSONPR diff + command docs
    Probabilistic (BF.*, CF.*, CMS.*, TOPK.*, TDIGEST.*)RedisBloom/RedisBloomPR diff + command docs
    Time series (TS.*)RedisTimeSeries/RedisTimeSeriesPR 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 (environment, *UnitTests vs *IntegrationTests naming, base/overload test structure, running one test) and .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:

    CommitWhat 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 CommandOutputs, 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:

    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 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. 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.

  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);
    }
    
    public Command<K, V, Boolean> copy(K source, K destination, CopyArgs copyArgs) {
        LettuceAssert.notNull(source, "Source " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);
        CommandArgs<K, V> args = new CommandArgs<>(codec).addKey(source).addKey(destination);
        copyArgs.build(args);
        return createCommand(COPY, new BooleanOutput<>(codec), args);
    }

    Every LettuceAssert precondition written here is public contract: if the interface Javadoc (step 2) does not already state the constraint and its @throws IllegalArgumentException, go back and add it — on every flavor.

  6. Dispatch layers — one-liners in both AbstractRedisAsyncCommands and AbstractRedisReactiveCommands (createMono for scalars, createDissolvingFlux for List/Set replies, matching the interface's Mono/Flux):

    java
    // AbstractRedisAsyncCommands
    public RedisFuture<Long> strlen(K key) { return dispatch(commandBuilder.strlen(key)); }
    // AbstractRedisReactiveCommands
    public Mono<Long> strlen(K key) { return createMono(() -> commandBuilder.strlen(key)); }
  7. Kotlin impl — src/main/kotlin/io/lettuce/core/api/coroutines/<Group>CoroutinesCommandsImpl.kt:

    kotlin
    override suspend fun strlen(key: K): Long = ops.strlen(key).awaitSingle()
  8. Cluster — pick the routing shape deliberately; there are three cases:

    • A single-key command flows through automatically (routed by slot).
    • A broadcast/all-shards command (its answer is the aggregate over the whole cluster — cf. dbsize, flushall) needs hand-coded fan-out overrides in RedisAdvancedClusterAsyncCommandsImpl and its reactive sibling (executeOnUpstream + a MultiNodeExecution aggregator), and may need methods on the cluster aggregate interfaces.
    • A node-specific command (keyless, but its result is only meaningful per node — cf. HOTKEYS) must not be fanned out: add default overrides on the cluster aggregate interfaces that throw UnsupportedOperationException and direct callers to the node-selection API or getConnection(nodeId) (cf. RedisClusterCommands.hotkeysReset()); only the node-selection flavors execute it.

    See "Cluster routing" in .agents/docs/architecture.md.

  9. Read-only command? Register it in src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java (CommandName enum) so replica-read routing knows, and bump the size assertion in ClusterReadOnlyCommandsUnitTests.

C. Extension needing new overloads / a new *Args class (hybrid): the new methods go through the full matrix of B; the option plumbing follows A. Check whether command helpers must follow (ScanIterator/ScanStream/ScanFlow for scan-family commands).

D. New command group / module area (Search/JSON/Bloom/VectorSet-style):

  • Unlike Jedis, Lettuce module areas are full citizens: every group gets all six flavors — sync, async, reactive, Kotlin coroutines, and both node-selection interfaces — plus Kotlin impls.
  • Create the flavor interfaces by mirroring an existing area end-to-end, register the group in the CommandInterfaces enum (src/test/java/io/lettuce/core/api/consistency/), and wire the group into the hand-written aggregate interfaces (RedisCommands, RedisAsyncCommands, RedisReactiveCommands, and the cluster variants) so they extend it — the consistency tests enforce the aggregate wiring.
  • Areas get their own builder (Redis<Area>CommandBuilder, cf. RediSearchCommandBuilder) with a matching Redis<Area>CommandBuilderUnitTests, and keep their argument types and reply parsers in an area package (e.g. core/search/arguments/).
  • Module commands still gate on server capability, not version: @EnabledOnCommand("FT.CREATE")-style probes; integration tests target the stack node.
Show full SKILL.md (1,086 more words)Show less

Types & args conventions

  • Argument types: an options object is a *Args implements CompositeArgument class (e.g. io.lettuce.core.CopyArgs) whose build(CommandArgs) appends its tokens. Fluent setters return this. If two overloads share options but differ in a typed field (long vs double), use a self-typed abstract base (BaseFooArgs<T extends BaseFooArgs<T>>) with concrete subclasses — cf. BaseIncrexArgs/IncrexArgs/IncrexFloatArgs.
  • @since goes on every new public element, not just the class. A class-level @since is not inherited: the nested Builder type, each of its static factory methods, and each public fluent setter needs its own @since tag, or the generated API docs lose the release provenance for those members.
  • Token-valued argument enums are plain enums whose values the builder/args class appends (cf. XNackMode).
  • Response types: reuse existing models where possible — Value, KeyValue, ScoredValue, GeoCoordinates, GeoWithin, StreamMessage, KeyScanCursor (all in io.lettuce.core) — and add a new one only when the reply genuinely doesn't map. For a map-shaped/structured reply, pair a model class with a ComplexDataParser consumed via ComplexOutput (cf. HotkeysReply + HotkeysReplyParser).
  • Return-type idioms (established conventions): 1/0 integer reply → Boolean (cf. copy, expire, hsetnx); count → Long; status → String; bulk value → V. And the overload rules from step B.2: every varargs parameter also gets a single-argument overload, while a multi-key command with an *Args object takes List<K> (not varargs) plus fixed-arity overloads.
  • The CommandOutput (the reply parser) is chosen at the builder step from the observed RESP2/RESP3 replies of Phase 0 — if none fits, add one under io.lettuce.core.output with a unit test (cf. IncrexLongOutput).

The consistency suite is the safety net

After mirroring, run:

bash
mvn -Dtest='*ConsistencyUnitTests,CommandBuilderCoverageUnitTests' \
    -Dsurefire.failIfNoSpecifiedTests=false test

It names exactly the flavor/signature you missed. For a genuinely unusual return type (e.g. Flux<Value<Long>>, or Mono<List<Double>> because Redis returns nulls), register it in the registry that owns the flavor — src/test/java/io/lettuce/core/api/consistency/KnownApiDeviations.java for the Java flavors, src/test/kotlin/io/lettuce/core/api/consistency/KnownKotlinApiDeviations.kt for the coroutine flavor — with a comment justifying it. Never use a deviation entry to paper over a sync/async signature mismatch — that breaks the sync-over-async runtime proxy.

Format before you build: run mvn formatter:format after hand-editing — the build's formatter:validate step fails the compile on unformatted code. Do not submit formatting-only diffs.

Test matrix — what to write

Naming, placement, and the base/overload structure are owned by .agents/docs/integration-testing.md — follow it. The established per-command layers (write all that apply):

  1. Args unit tests (*Args classes): assert the exact encoded tokens and wire order, setter validation, and overload equivalence — e.g. IncrexArgsUnitTests, XAddArgsUnitTests. No server needed.
  2. Builder unit test: assert the constructed command and encoded args — including the RESP2/RESP3 output shape observed in Phase 0. Core commands go in src/test/java/io/lettuce/core/RedisCommandBuilderUnitTests.java; area commands in their Redis<Area>CommandBuilderUnitTests.
  3. Output unit tests when a new CommandOutput was added (cf. IncrexOutputUnitTests).
  4. Integration tests: add methods to the sync base class (<Group>CommandIntegrationTests), gated per-test with @EnabledOnCommand("<NAME>"). Use assertions derived from the redis-cli showcase transcripts — real semantics, not just "no error", covering the whole family the option touches (with/without optional args, error cases):
    java
    @Test
    @EnabledOnCommand("COPY")
    void copy() {
        redis.set(key, value);
        assertThat(redis.copy(key, key + "2")).isTrue();
    }
  5. Overloads: the base's @Test methods re-run automatically under the group's existing RESP2/cluster/reactive/Tx overload classes — but only the ones that exist. Check the target group against peer groups and create a missing overload class when it matters for the command (INCREX created StringCommandResp2IntegrationTests because its reply differs by protocol). Provide the base test at minimum; add overloads that carry real risk (RESP2 when replies differ, cluster when routing matters).

Running the tests

The build pins a specific JDK to match CI — check .github/workflows/ and the local-gotchas section of .agents/docs/integration-testing.md (pin JAVA_HOME, worktree git-commit-id-plugin skip, TEST_WORK_FOLDER).

  • Unit tests (Surefire, no server): mvn clean test, or mvn -Dtest=FooUnitTests test.
  • Integration tests (Failsafe) need the Docker env from Phase 0 and the verify lifecycle — -Dit.test= filters Failsafe, -Dtest= does not:
    bash
    TEST_WORK_FOLDER=./work/docker mvn -DskipITs=false -DskipUnitTests=true \
      -Dit.test=FooIntegrationTests verify -Pci
    If the command isn't in any published redislabs/client-libs-test tag yet, say so: the integration tests will be skipped by @EnabledOnCommand (expected and acceptable), but they must still be written and compile.

Tear the environment down when you are done. The Docker topology started in Phase 0 keeps running (and holds the test ports) until stopped. After the final verification run — and equally when the task is aborted or fails partway — run:

bash
make stop

PR hygiene checklist (verify before finishing)

  • Every layer of the chosen matrix updated consistently; the consistency suite and CommandBuilderCoverageUnitTests pass.
  • Every varargs parameter has its single-argument overload, and every multi-key command with an *Args object uses List<K> (not varargs) with fixed-arity overloads — mirrored across all flavors (or a maintainer-approved justification from the plan).
  • @since on every new public element — including the nested Builder, static factories, and fluent setters of new *Args classes; class-level tags are not inherited (see .agents/docs/javadoc.md). Javadoc written on the sync interface and mirrored with flavor-appropriate @return phrasing.
  • Builder-validated preconditions documented on all flavors: @param constraint phrases + @throws IllegalArgumentException if ….
  • No dead CommandKeyword constants; no keyword duplicating a CommandType.
  • Read-only commands registered in ReadOnlyCommands (+ count test bumped).
  • mvn formatter:format run; no formatting-only noise in the diff.
  • Tests at every applicable layer, gated with @EnabledOnCommand; missing integration overload classes created where the command needs them.
  • docs/ (MkDocs) updated — a new command or option is user-facing: add a one-line entry to the current-release section of docs/new-features.md (follow its existing "Support for [X](redis.io link) …" pattern), plus any feature page the change affects.
  • .env.vX.XX image pin bumped if the feature needed a newer server build.
  • PR description states: server PR link, HLD link, gating choice and why, behavior against older servers, and includes a showcase transcript. (Draft with the draft-pr-description skill; remember the guardrail — the agent never creates the PR itself.)
  • Docker test environment stopped (make stop) after the final verification run.

Top pitfalls

  1. Skipping the live verification / not checking RESP2 vs RESP3. The reply shape can differ between protocols; it determines the CommandOutput and the reactive mapping. Confirm against a running server, don't assume.
  2. Adding Args/response types after the interface edits — every flavor references them; the project won't compile. Types come first.
  3. Editing only some flavors, or silencing the consistency suite with a deviations-registry entry instead of fixing the signature.
  4. Forgetting a dispatch layer — both AbstractRedisAsyncCommands and AbstractRedisReactiveCommands, plus the Kotlin *Impl.kt.
  5. Recreating the removed generator source files under src/test/java/io/lettuce/core/api/ because an old reference PR touched them.
  6. Wrong CommandArgs order or CommandOutput, missing @since, missing @EnabledOnCommand gating, or missing the read-only registry entry.
  7. Letting a traced precedent override a written convention — e.g. skipping the single-argument overload because sintercard(K...) doesn't have one, or stopping at a class-level @since because an old *Args class did. Older code predates the rules; the conventions win.

© redis, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .agents/skills/extend-commands-api of redis/lettuce.

Open the folder on GitHubat commit 114a3ef

Compare with similar skills

Extend Commands API next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Extend Commands API compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Extend Commands API this skillredis/lettuce5.8k—~7.7kAutomated safety check: NotesMIT
Extend Commands APIredis/jedis12k—~7.2kAutomated safety check: WarnMIT
Writing Testsvert-x3/vertx-redis-client142—~1.6kAutomated safety check: PassApache-2.0
Azure Upgrademicrosoft/GitHub-Copilot-for-Azure2551 repos~1.5kAutomated safety check: PassMIT
AWS SDK Java V2 Dynamodbgiuseppe-trisciuoglio/developer-kit3551 repos~3.3kAutomated safety check: NotesMIT
Add Garnet RESP Commandmicrosoft/garnet12k—~9.7kAutomated safety check: PassMIT

Similar skills

  • Official

    Add or extend Redis commands in the Jedis client API — a new core command, a family of new commands, an extension to an existing command's options, or a module command (Search/TimeSeries/JSON/Bloom).

    12k GitHub stars~7.2k tokensUpdated today
    DatabasesAuto-check: warnings
  • Writing Tests

    vert-x3/vertx-redis-client

    Testing patterns for Vert.x Redis Client: test frameworks, async testing, test locations, container setup, and how to run tests.

    142 GitHub stars~1.6k tokensUpdated 19 days ago
    DatabasesAuto-check passed
  • Azure Upgrade

    microsoft/GitHub-Copilot-for-Azure

    Official

    Assess and upgrade Azure workloads between plans, tiers, or SKUs, or modernize Azure SDK dependencies in source code.

    255 GitHub starsUsed in 1 repo~1.5k tokens
    DatabasesAuto-check passed
  • AWS SDK Java V2 Dynamodb

    giuseppe-trisciuoglio/developer-kit

    Provides Amazon DynamoDB patterns using AWS SDK for Java 2.x.

    355 GitHub starsUsed in 1 repo~3.3k tokens
    DatabasesAuto-check: notes
  • Add Garnet RESP Command

    microsoft/garnet

    Official

    Step-by-step guide for adding a new built-in RESP command to Garnet, from the command enum and parser to storage callbacks, command metadata JSON and tests.

    12k GitHub stars~9.7k tokensUpdated today
    DevelopmentAuto-check passed
  • A skill your agent uses when you need to write or improve integration tests — including Testcontainers with @ServiceConnection, @DataJdbcTest persistence slices, TestRestTemplate or MockMvcTester…

    445 GitHub stars~935 tokensUpdated today
    Testing & QAAuto-check passed

More from redis/lettuce

  • Writing Javadoc

    redis/lettuce

    Official

    A skill your agent uses when writing or editing Javadoc for Lettuce public API — new methods, classes, deprecations, or when a reviewer asks to fix or improve doc comments.

    5.8k GitHub stars~809 tokensUpdated today
    Auto-check passed

Questions about Extend Commands API

What does Extend Commands API do?

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…. Extend Commands API is an agent skill from redis/lettuce, published by the product's own GitHub organization. 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).

When should I use Extend Commands API?

Extend Commands API fits situations like: add support for the <X command; implement <REDIS COMMAND in Lettuce; extend <command with <option; adding a new argument/overload to an existing command.

How do I install Extend Commands API in Claude Code?

Run `npx skills add redis/lettuce --skill extend-commands-api -a claude-code`. Or copy the skill folder (.agents/skills/extend-commands-api in redis/lettuce) into .claude/skills/extend-commands-api in your project. Claude Code loads it when a task matches its description.

How do I install Extend Commands API in Codex?

Run `npx skills add redis/lettuce --skill extend-commands-api -a codex`. Or copy the skill folder (.agents/skills/extend-commands-api in redis/lettuce) into .agents/skills/extend-commands-api in your project. Codex loads it when a task matches its description.

Can I use Extend Commands API in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add redis/lettuce --skill extend-commands-api -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/extend-commands-api, .gemini/skills/extend-commands-api, .github/skills/extend-commands-api and .opencode/skills/extend-commands-api in your project.

What does Extend Commands API need to run?

Going by SKILL.md and its folder, Extend Commands API needs the command-line tools its instructions call (mvn, make, gh and redis-cli). Our summary lists: Docker. Its frontmatter pre-approves these tools: Bash(mvn *), Bash(make *), Bash(redis-cli *), Bash(gh *).

Does Extend Commands API access the network?

SKILL.md names 1 domain. As links in the text: hub.docker.com. This is read from the text; nothing was executed.

Is Extend Commands API safe to install?

Our automated static check of SKILL.md found notes only (mentions a .env file), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.

What licence does Extend Commands API use?

Extend Commands API is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Extend Commands API use?

About 7.7k tokens (SKILL.md is roughly 31k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Extend Commands API?

Skills that share tags, products or a category with Extend Commands API: Extend Commands API (redis/jedis, 12k stars), Writing Tests (vert-x3/vertx-redis-client, 142 stars), Azure Upgrade (microsoft/GitHub-Copilot-for-Azure, 255 stars) and AWS SDK Java V2 Dynamodb (giuseppe-trisciuoglio/developer-kit, 355 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Extend Commands API?

redis (a GitHub organization, an official publisher) maintains it in redis/lettuce, which has 5,780 GitHub stars. The repository holds 2 skills in this directory. The repository was last updated on October 7, 2026.

Source: redis/lettuce on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.