Generate and publish AI reference docs for rs.wordpress.api:kotlin#1416
Open
oguzkocer wants to merge 26 commits into
Open
Generate and publish AI reference docs for rs.wordpress.api:kotlin#1416oguzkocer wants to merge 26 commits into
rs.wordpress.api:kotlin#1416oguzkocer wants to merge 26 commits into
Conversation
Generate compact, structured markdown API reference from the UniFFI
bindings so tooling can consume endpoint signatures and types without
crawling the generated `wp_api.kt`.
- Add `buildSrc` `GenerateAiDocsTask` that parses `*RequestExecutorInterface`
blocks, `data class`/`sealed class`/`enum class` declarations from the
generated bindings into per-endpoint markdown
- Output is a build artifact under `build/ai-docs/ai-reference`; `stats*`
endpoints are grouped into a single `stats.md`
- Wire `aiDocs { from(...) }` to the generator output so the publish-to-s3
`zipAiDocs` task and `ai-docs` classifier artifact pick it up
Attach the generated AI reference docs to the `rs.wordpress.api:kotlin`
publication as an `ai-docs` classifier zip, and refactor the generator into
pure, testable pieces.
- Add the `buildSrc` precompiled script plugin `ai-docs.gradle.kts` (id `ai-docs`):
it generates the docs, zips them reproducibly, and attaches the zip to the
module's Maven publication under the `ai-docs` classifier.
- Apply `id("ai-docs")` in `api/kotlin/build.gradle.kts`, replacing the previous
`aiDocs { from(...) }` plugin DSL.
- Split `GenerateAiDocsTask.kt` into package `aidocs`: pure `BindingsParser`
(`parse(): ParsedBindings`) and `DocsGenerator` (`generate(): List<GeneratedDoc>`),
with all filesystem I/O isolated in `GenerateAiDocsTask`.
- Add `buildSrc` unit tests for `BindingsParser` (`kotlin("test")` on JUnit Platform)
and set `rootProject.name` for `buildSrc`.
- Replace positional `Pair`/`Triple` with `Param(name, type)` and
`Field(name, type, default)` in `MethodSignature.params` and
`DataClassInfo.fields`, so call sites read by name instead of `.first`/`.third`.
- Remove dead code in `BindingsParser`: the no-op `.substringBefore(" \n")`
calls in `parseField` (lines are already split) and the unreachable
`== "): Disposable{"` terminator clause in `parseDataClasses`.
Replace the four hand-threaded `var i` index loops in `BindingsParser` with a shared `blocks(isHeader)` helper plus `takeWhile`/`map`/`filter` per declaration kind. Removes the duplicated scan-until-terminator structure and the mutable cursor threading. Behavior is unchanged: the parser tests pass and the generated docs are byte-identical.
Covers the `}`-exclusive branch of `enumVariants`, which the existing `;`-terminated case did not exercise.
Replace the hardcoded eight-entry `replace` chain with a single `\bkotlin\.` strip, so any built-in (e.g. a future `kotlin.Float`) is handled instead of silently leaking into the docs. Generated output is unchanged.
Replaces the positional `Pair<String, List<String>>` with a named data class, consistent with the `Param`/`Field` types; call sites still destructure.
`split(", ")` would break a generic param like `filter: Map<String, Int>` into
two. Replace it with a depth-aware `splitParams` that ignores commas inside
`<...>`, and add a test. Current bindings are unaffected (output unchanged).
Move the structural string literals (`public interface `, `data class `, `enum class `, `val `, etc.) into a private companion object, centralizing the binding-format markers. No behavior change.
The AI-docs generator parser tests live in `buildSrc` and are not part of the main build task graph, so run them in a dedicated `:kotlin: buildSrc Unit Tests` step (`./gradlew -p buildSrc test`) with JUnit result collection.
Collaborator
XCFramework BuildThis PR's XCFramework is available for testing. Add to your .package(url: "https://github.com/automattic/wordpress-rs", branch: "pr-build/1416")Built from 60f9b5d |
Drop the `stats` special-case in `DocsGenerator`: every endpoint now gets its own `<domain>.md`, consistent with the rest and with the one-file-per-endpoint goal, and `index.md` lists them all alphabetically. Also removes the now-unused header `level` nesting that only existed to support the merged file.
Replace the nested for-loops over mutable sets with declarative `flatMap`, preserving iteration order. Output unchanged.
Replace `writeDataClass`, which mutated a passed-in `StringBuilder`, with `dataClassTable` that returns the markdown; callers append it. Output unchanged.
Extract the excluded method names, the data/headerMap response envelope fields, and the `Response`/`Params` suffixes into constants, and name the response-wrapper filter `isDocumentedType`. Output unchanged.
Use `buildString { }` (dropping the explicit `StringBuilder`/`toString()`) and
`forEach` over the element lists in `generateEndpointDoc` and `buildIndex`.
Output unchanged.
Extract `methodsSection`, `classesSection`, and `enumsSection` as pure functions returning a String (or empty), composed in `generateEndpointDoc`. Output unchanged.
The library exports 47 namespace-level functions (URL builders, enum converters, error localizers, etc.) that the generator previously ignored. - Parse them in `BindingsParser.parseFreeFunctions()`: every `fun` at brace-depth 0 (not inside an interface/class/object), located with a regex so leading `@Throws` / KDoc prefixes are dropped and the `public fun uniffi*` internals are skipped. - Clean the `kotlin.` prefix off function/method parameter and return types (previously only data-class fields were cleaned); endpoint docs are unaffected because their methods use custom types. - Render a single sorted `functions.md`, linked from `index.md` under `## Functions`. - Add a parser test covering depth filtering, `@Throws`/KDoc prefixes, and `suspend`.
`List<Foo>?` reduced to `Foo>` (the `>` survived because `?` was stripped last), so a data class reachable only via a `List<T>?` reference would miss the `dataClasses` lookup. Strip the trailing `?` first. No current output change — such types are not reached within the two-level type collection today — but the extraction is now correct.
A method return type (e.g. `PostListResponse`) is a consuming session's entry point; if it is not documented, the agent knows only the type name and must read the bindings for its shape. Drop the `isDocumentedType` envelope filter and stop excluding `*Response` from the "Types" section, so every referenced type is documented and the method -> response -> payload chain is navigable.
…ined `collectReferencedTypes` stopped at two levels, so a type reached through a documented type's field had no table of its own and the agent would still fall back to the bindings for it. Walk the full reachable closure over data-class fields (cycle-safe via the seen set) so every data-class type named in an endpoint doc has its own table.
A sealed type rendered only as a list of variant names, so a consuming session saw the cases but not each variant's fields — and had to read the source. - Model each variant as `SealedVariant(name, fields)`; parse `object`, `class`, and `data class` variant shapes (constructor params become the fields) and strip the `: Super()` from exception-style sealed names. - Render each variant as a signature (`Name(field: Type, ...)`), and extend the transitive type closure to follow variant field types so they are documented too. - Generalize `cleanType` to strip any package prefix (`kotlin.`, `uniffi.wp_api.`), since sealed variant params are fully qualified. - Rename the `## Enums` doc section to `## Enums & Sealed Types`.
Endpoint docs are now a flat structure: the method signatures followed by one block per referenced type, each headed by its declaration kind (`## data class`, `## sealed class`, `## enum class`) and separated by `---` so a consumer can split the file on that delimiter. Drops the old `## Methods`/`## Parameters`/`## Types`/ `## Enums & Sealed Types` sectioning and the `Params`-suffix split. Replace `extractTypeName` (which only unwrapped `List<>`/`?`) with `extractTypeNames`, extracting every identifier in a type expression via regex. This documents `Map<K, V>` value types (e.g. `JsonValue`) that were previously silently orphaned, and subsumes the earlier `List<Foo>?` handling. Also drop the redundant description line in `functions.md`.
The flat restructure left an endpoint's method signatures floating directly under the `# <domain>` title, unowned by any type — inconsistent with every other declaration, which carries its `## <kind> <Name>` header (`## data class`, `## sealed class`, `## enum class`). Head the method list with `## interface <ExecutorInterface.name>` so the methods are owned by their declaring interface. The name and kind are both present in the bindings, so this is extraction, not invented labeling.
Sealed-variant detection moved to `sealedVariantRegex`, leaving `OBJECT_PREFIX` unreferenced.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This adds a compact, per-endpoint Markdown API reference (the "AI docs") that is generated from the UniFFI-generated Kotlin bindings and published alongside
rs.wordpress.api:kotlinas anai-docsMaven classifier zip.The goal is to let agents and tooling fetch and read one small Markdown file per endpoint instead of crawling the ~191K-line generated
wp_api.kt(or the Rust source). A consumer resolves theai-docsartifact for thers.wordpress.apiversion it already depends on and unpacks one file per endpoint domain.All of the logic lives in
native/kotlin/buildSrcand is self-contained: the whole feature is wired into a module with a singleid("ai-docs")line.Changes
native/kotlin/buildSrc/src/main/kotlin/aidocs/): aGenerateAiDocsTaskthat parses the generatedwp_api.ktinto a structured model and renders Markdown — executor interfaces become method-signature lists,data classes become field tables, andsealed/enumclasses become variant lists.BindingsParser(parse()→ParsedBindings) andDocsGenerator(generate()→List<GeneratedDoc>) are pure, with all filesystem I/O isolated inGenerateAiDocsTask.ai-docs.gradle.kts(applied viaid("ai-docs")inapi/kotlin/build.gradle.kts) generates the docs, zips them reproducibly with Gradle's built-inZip(stable order, fixed timestamps), and attaches the zip to thers.wordpress.api:kotlinMaven publication under theai-docsclassifier.BindingsParserTestcovers the parser's non-trivial cases (method-signature parsing,data classfields and defaults,sealed/enumvariants, and generic-aware parameter splitting), usingkotlin("test")on the JUnit Platform.:kotlin: buildSrc Unit TestsBuildkite step runs./gradlew -p buildSrc test, sincebuildSrc's test task is not part of the main build's task graph.Changelog
CHANGELOG.mdunder## [Unreleased], using the Keep a Changelog categories (Added,Changed,Deprecated,Removed,Fixed,Security). Prefix breaking changes with**BREAKING:**.