feat: create and maintain plugin-config.json from sdk publish and plugin generate - #318
Open
MuHamza30 wants to merge 6 commits into
Open
feat: create and maintain plugin-config.json from sdk publish and plugin generate#318MuHamza30 wants to merge 6 commits into
MuHamza30 wants to merge 6 commits into
Conversation
The file is written by two commands with different jobs: `sdk publish` records languages, `plugin generate` owns the plugin's own identity. `validate()` reports those two facts separately so each caller can act on its own half, and both upserts preserve keys this CLI version does not model, so a hand-written config survives a round trip. `pluginKey` is read but never written: codegen-v2 parses and validates it, then never reads it, so writing one would only be a promise the plugin cannot keep. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A published SDK is the only thing that can name a language for the plugin, so `sdk publish` is where the entry comes from. It writes languages alone and never metadata, which keeps publishing free of plugin questions for anyone who does not want a context plugin. Interactive runs are asked. Non-interactive runs answer with `--update-plugin-config` instead: that path is documented for CI/CD, where a prompt is either never answered or silently declined at EOF. Nothing here may change the publish result, so the action returns void and stays silent when the profile names no source repository. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`sdk publish` leaves a config with languages and no identity, and a fresh project has no config at all. Both are now filled in here rather than failing at the backend: the run asks for an id, name and version, writes them beside whatever languages are already recorded, and only then generates. A config that names no language stops with next steps instead of uploading a build the backend will reject. Also renames the language error key to `languages`, following codegen-v2. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`--dry-run` generates locally and publishes nothing, but `SdkPublishAction` returns success for it, so `--update-plugin-config` wrote a languages entry claiming a published SDK. `plugin generate` would then build a plugin from a repository holding nothing. Ignoring the flag in silence would repeat the failure it fixes, so the run says why it was ignored. Also drops a `CodeGenerationVersion` import left unused when this call switched to `codegenOption.codeGenerationVersion()`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A package-only profile names no repository, so no language entry can be built. That return happens before the confirm, so an interactive publish simply never showed the prompt and gave no reason. `upsertLanguage` refusing a config it cannot parse was equally silent, and that one lands after the user has already agreed to record. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
MuHamza30
requested review from
Shield-Jaguar,
aliasghar98 and
saeedjamshaid
as code owners
August 13, 2026 14:58
Four fixes that interleave across the same files, so they land together. Record only what the run published. `buildLanguageEntry` read whatever the profile enabled, so `--publish-type package` recorded a source repository this run never pushed to, and `--publish-type sourcecode` recorded a package that was never released. Both halves are now gated on the requested types. Never let recording crash the publish. `merge()` left `write()` unguarded, so a read-only `src/`, a locked file or a full disk turned an already-successful publish into a stack trace with exit 1, and `outro` never ran — despite the action documenting that it never throws. Reporting the fault meant the write result had to say *which* fault, so `upsertMetadata`/`upsertLanguage` return `written | unreadable | unwritable` instead of a boolean that silently meant "unreadable". Refuse to invent a repository URL. A publishing profile carries no git host, so only an absolute http(s) URL or a plain `owner/repo` can be resolved. An SSH remote previously became `https://github.com/git@github.com:acme/sdk.git` and was written as a clone target; the backend validates only that the URL is non-blank, so a guess survived to a failed clone. Anything unresolvable is now reported with the offending value. Backfill `schemaVersion`, and refuse one this CLI does not model. A hand-written config that omitted it round-tripped still missing it and was rejected server side after the CLI said the config was saved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
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.



Follows #314, which added
apimatic plugin generatefor the happy path only — it assumedsrc/plugin-config.jsonalready existed and named at least one language. This adds everything that creates and maintains that file.The split
sdk publishrecords languages.plugin generateowns metadata. Publishing never asks for a plugin ID and never calls the account API, so a project can be published from long before anyone decides to build a context plugin.sdk publish— after a successful publish, records the published language. Interactive asks; non-interactive answers with--update-plugin-config, because a prompt on the CI path would never be answered.plugin generate— prompts for plugin ID / name / version when they are missing, then generates; or stops with next steps when no language has been recorded yet.What gets written
{ "schemaVersion": 1, "pluginId": "acme-payments", // metadata: plugin generate only "pluginName": "Acme Payments", "pluginVersion": "0.1.0", "author": { "name": "Acme", "email": "developers@acme.com" }, "license": "MIT", // written unprompted; the backend consumes it "languages": { // sdk publish only "csharp": { "source": { "repositoryUrl": "https://github.com/acme/acme-payments-csharp", "branch": "main" }, "package": { "packageId": "Acme.Payments.Sdk" }, "version": "v3" // the codegen version the publish actually used } } }Package shape is per language, mapped from the publishing profile:
csharp: packageId·typescript|python|ruby: name·java: groupId+artifactId·php: vendorName+projectName·go: packageName.pluginKeyis deliberately never written — codegen-v2 parses and validates it, butSdkReposExtensions.ResolveAgainstnever reads it. A read-modify-write round-trip preserves fields this CLI version does not model, and backfillsschemaVersionwhen a hand-written file omits it.plugin generatestatesschemaVersionthis CLI does not model)failed()success(), no generationAn entry only ever claims what was published
Three cases where the CLI deliberately records nothing rather than record something untrue — each says so rather than exiting quietly:
--dry-runpublishes nothing but returns success, so--update-plugin-configis ignored with a notice.--publish-type, not from whatever the profile enables — so a package-only run records nosource, and a source-only run records nopackage.GitConfigurationItemhas onlycredentialsId,repositoryName,branch), so only an absolutehttp(s)URL or a plainowner/repocan be turned into a URL. Anything else — an SSH remote, a bare single-segment name — is refused and reported with the offending value, rather than guessed at. This is the design decision most worth a second opinion: the backend validates only thatrepositoryUrlis non-blank, so a wrong guess would survive to a failed clone, but the guard also means a profile shape we have not seen in the wild records nothing.Notes for review
--update-plugin-configis non-interactive only.sdk publishtreats any argument as non-interactive (this.argv.length === 0), so the flag cannot be combined with the interactive flow; interactive asks instead.license: "MIT"is written unprompted, and re-added if a user removes it. Flagging rather than deciding.failed()would make the no-op detectable by a script.Known follow-ups
--update-plugin-configis undocumented.pnpm readmeduplicates every section on a CRLF checkout, so this needs care.source; a malformed hand-written config is caught by the backend rather than locally.isLanguageEntrywas written for that check and is currently unused.publishTypesgating sits inPluginRecordSdkActionwhile the rest of the entry's shape is decided inbuildLanguageEntry— one rule in two places. AndbuildLanguageEntryis a pure transformation living insrc/types/rather thansrc/application/.PluginRecordSdkAction.executereturnsvoidrather thanActionResult, so that recording cannot alter the publish result. Callers ignoring the result would be the better shape.executeas an arrow-function field, so it cannot be stubbed on the prototype.plugin-config.jsonappears as a literal in several prompts classes rather than one shared constant.Testing
pnpm buildandpnpm lintare clean. 194 passing, 11 pending — excludingtest/prompts/sdk/generate.test.tsandtest/prompts/sdk/publish/publishing-details.test.ts, which callCodegenOption.resolveand abort the suite. That breakage is pre-existing ondevfrom #317 — the method is namedcreate— and is not introduced here.🤖 Generated with Claude Code