Skip to content

feat(sdk)!: retire v3 generation and let a publish record itself - #359

Merged
mrafnadeem-apimatic merged 13 commits into
devfrom
feat/journey-command-updates
Sep 25, 2026
Merged

mrafnadeem-apimatic merged 13 commits into
devfrom
feat/journey-command-updates

Conversation

@sohail2721

@sohail2721 sohail2721 commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Updates sdk generate, sdk publish and plugin publish to the journey doc.

#358 has merged, so this now sits directly on dev and its diff is its own.

sdk generate

  • --codegen-version stays, with one version in it. The flag is how a caller says which generator they want, and there will be a v5: removing it makes that move a release that changes what people get, rather than a value they pass. It accepts v4, defaults to v4, and refuses v3 by name rather than generating something else quietly.
  • --track-changes and --skip-changes are gone. v4 never supported customizations, so nothing produces a source tree to merge.
  • --stability stays. v4 renders each language at beta first and stable later, so the level is a choice that outlives the version it used to accompany.
  • C#, TypeScript and Python are what it renders. The other four are refused by name rather than by the flag parser, so a user whose language is coming back reads something other than a list of accepted values:
$ apimatic sdk generate -l ruby

■  Ruby isn't available yet.
│  Available now: C#, TypeScript, Python
│  Coming soon: Java, Ruby, Go, PHP
│
└  Failed
  • The success line prints ./sdk/typescript rather than the machine's full path.
  • The "V4 does not support SDK customizations" warning is gone: there is nothing left for it to contrast with.

sdk publish

  • --update-plugin-config removed; --codegen-version and --stability kept.
  • A publish records itself in src/apimatic.json every time and says so. Nobody publishes an SDK and then wants their plugin to go on describing a local copy, so the confirmRecordSdk() question is gone: there is nothing left to decline once the publish has happened.
  • What it records no longer includes the generator version. codegenVersion left apimatic.json in feat: local plugin generation #358, when the service took over deciding the generator. The flag says what to generate with; nothing writes down what was used, and the mismatch warning that compared the two goes with it.
  • The interactive flow offers only languages that can be generated, instead of letting a profile lead someone to a failure two prompts later. The generator-version question becomes a stability question, asked only when a language offers more than one level — which today is never, so the flow is one prompt shorter than it was.
  • The details block loses its generator row and names the stability level only when the user chose it, read from oclif's parse metadata (--stability stable and the default are the same string). Today's output matches the doc exactly.

plugin publish

Prints cd "./plugin" where it printed the full path. The reader is standing in the directory above it. Everything else already matched the doc, including the What is about to become public: 42 files, 9 dirs disclosure.

One constant where there were two

PLUGIN_LANGUAGES (the languages a plugin can carry) and AVAILABLE_LANGUAGES (the languages v4 renders) held the same three languages for the same reason — the plugin's copy said so in its own comment. They collapse into AVAILABLE_LANGUAGES, and isPluginLanguage/isAvailableLanguage into one predicate that takes a string: config files name their languages, and a file may name anything.

Deleted with v3

apimatic sdk save-changes, MergeSourceTreeAction, the source-tree download, both change-tracking contexts and their prompts, the three events only they fired, and CodegenOption. CodeGenerationVersion stays: something has to name the generator the service is asked for, even with one answer left in it.

codegenVersion also leaves apimatic.schema.json. It left the model in #358; the schema kept describing it, and its enum was what the schema test compared against CodeGenerationVersion — which this branch reduces to v4 alone. Narrowing the enum would have been the wrong repair: a file an older CLI wrote says v3, and an editor would then flag it. The field is removed instead, and additionalProperties keeps those files valid.

One thing worth a reviewer's eye

oclif readme works again. The 593-line rewrite #346/#348 hit is a line-ending bug: README.md is CRLF in git, and oclif's marker regex only matches <!-- usage --> with LF, so it appends a second copy instead of replacing. Convert to LF → run oclif readme → convert back to CRLF, and the diff is only the real lines. Worth a note in CONTRIBUTING.

Verification

tsc clean on src and test, eslint clean, 991 passing / 28 pending / 0 failing. Each of the three commits typechecks on its own. The error output above is real output from the built CLI.

🤖 Generated with Claude Code

@sohail2721
sohail2721 force-pushed the feat/journey-command-updates branch from b69608b to 98d3007 Compare September 24, 2026 13:18
@sohail2721
sohail2721 changed the base branch from dev to feat/local-plugin-generation September 24, 2026 13:18
Muhammad Sohail and others added 4 commits September 24, 2026 18:59
The journey doc settles three commands at once, and all three land on the
same decision: there is one code generator now.

`sdk generate` drops `--codegen-version` and `--stability`, because there
is no version left to pick, and with them `--track-changes` and
`--skip-changes`: v4 never supported customizations, so nothing produces a
source tree to merge. C#, TypeScript and Python are what it renders; the
other four are refused by name rather than by the flag parser, so a user
whose language is coming back reads something other than a list of accepted
values, and nothing points at a generator that no longer exists.

`sdk publish` drops the same two flags and `--update-plugin-config`. A
publish now records itself in `src/apimatic.json` every time and says so.
Nobody publishes an SDK and then wants their plugin to go on describing a
local copy, so the question interactive runs asked is gone — and with it
the confirmation that stood between a warning and a write that had already
happened. The interactive flow offers only languages that can be generated,
rather than letting a profile lead someone to a failure two prompts later.
The details block loses its generator row: one generator is not a choice
worth printing.

`plugin publish` prints `cd "./plugin"` where it printed the machine's full
path. The reader is standing in the directory above it.

The three languages a plugin can carry and the three v4 renders were two
constants holding one fact, and the plugin's copy said so in its comment.
`PLUGIN_LANGUAGES` and `isPluginLanguage` give way to `AVAILABLE_LANGUAGES`
and `isAvailableLanguage`, which takes a string: config files name their
languages, and a file may name anything.

Deleted with v3: `apimatic sdk save-changes`, `MergeSourceTreeAction`, the
source-tree download, both change-tracking contexts and their prompts, the
three events only they fired, and `CodegenOption`/`Stability`.
`CodeGenerationVersion` stays: something has to name the generator the
service is asked for, even with one answer left in it.

Stability is pinned to beta, which is the only level the CLI ever offered
for v4 and is named once in the service rather than threaded through four
signatures.

BREAKING CHANGE: `apimatic sdk save-changes` is removed, along with
`--codegen-version`, `--stability`, `--track-changes` and `--skip-changes`
on `sdk generate` and `--codegen-version`, `--stability` and
`--update-plugin-config` on `sdk publish`. Java, Ruby, Go and PHP cannot be
generated until they reach v4.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Retiring v3 took `--stability` with it on the grounds that there was one
answer left. There is one answer *today*: v4 renders every language at beta
and they reach stable one at a time, so the level is a choice that survives
the version being fixed.

The flag is back on both commands, defaulting to stable as before, and the
level a language offers is a table rather than a constant — `stableLevels`
of one is answered without asking, and the interactive flow puts the
question back the moment a language has two. `sdk publish` names the level
in its summary only when the user chose it, read from oclif's parse
metadata, because `--stability stable` and the default are the same string
and the doc's block has no such row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Retiring v3 removed the flag along with the version, on the grounds that a
choice of one is not a choice. But the flag is how a caller says which
generator they want, and there will be a v5: removing it makes that move a
release that changes what people get, rather than a value they pass.

`CodeGenerationVersion` now holds `V4` alone, and stays an enum so the next
one is a line in it. The flag is back on both commands, accepting `v4`,
defaulting to `v4`, and refusing `v3` by name rather than generating
something else quietly. `sdk publish` names it in its telemetry again.

It is not written down. `codegenVersion` left `apimatic.json` when the
service took over deciding the generator, so the flag says what to generate
with and nothing records what was used.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
It left the model in #358, when the service took over deciding the generator.
The schema kept describing it, and its enum was what the schema test compared
against `CodeGenerationVersion` -- which this branch reduces to v4 alone.

Narrowing the enum to `["v4"]` would have been the wrong repair: a file an
older CLI wrote says `v3`, and an editor would then flag it. The field is
removed instead, and `additionalProperties` keeps those files valid.

One test leaned on that enum to show the schema types the publishing record
more tightly than the portal's parser does. It now leans on `source`, which
the schema still types, and the published-language fixture is brought to the
shape #358 gave it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sohail2721
sohail2721 force-pushed the feat/journey-command-updates branch from 98d3007 to 3b6fa42 Compare September 24, 2026 14:05
@sohail2721
sohail2721 changed the base branch from feat/local-plugin-generation to dev September 24, 2026 14:05
@sohail2721 sohail2721 closed this Sep 24, 2026
@sohail2721 sohail2721 reopened this Sep 24, 2026
Comment thread README.md Outdated
Comment thread src/types/sdk/generate.ts Outdated
Muhammad Sohail and others added 4 commits September 24, 2026 20:54
…rating

Two review points.

`UPCOMING_LANGUAGES` was a second hand-written list that had to stay the exact
complement of the first: a language could end up in both, or in neither, and
nothing would have said so. It is derived now, so adding a renderer is one
edit. The order comes from `LANGUAGE_CHOICES`, which is the order every other
list shows, so the refusal reads `Ruby, Java, PHP, Go` rather than the order
the hand-written list happened to use. A test pins the properties the
derivation buys: every language accounted for exactly once, and none named
twice.

The README's account of what v3 took with it was changelog prose, and the
changelog is generated from the commit footers that already carry it. What
the generated command help cannot say is kept, because `--language` still
accepts seven values while three of them render: one short section says so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#360 landed and rebuilt `LANGUAGE_CHOICES` around a `LANGUAGE_NAMES` map, which
is the same display order reached a better way. Dev's construction wins, and
`UPCOMING_LANGUAGES` goes on deriving itself from it, so the refusal still
reads `Ruby, Java, PHP, Go`.

`languageLabel` was searching the choices for a label the map now holds
directly. It reads the map. The identical private copy in `code-samples.ts`
is left alone: #352 is still editing that file, and this is not the branch to
take it from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`apimatic sdk generate -l typescript` failed against the deployed service:

    The V4 Code Generator currently supports SDK generation for 'typescript'
    only in 'beta'.

`--stability` defaults to `stable`, one string for every language, and v4
renders all three languages it supports at beta alone. So the default could
not succeed for any language the CLI can generate -- every run that did not
pass the flag was refused for a choice the caller never made.

`defaultStability` was added for this and then not called. Both commands ask
oclif whether the value came from the flag or from its default: what a caller
typed is sent as typed, and what they left alone becomes what the language
actually offers.

Found by running the flow against dev rather than by a test, so there is a
test now: beta is the only level for every available language, and a language
that reaches stable needs no change here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#367 renamed the build directory to the source directory, per the glossary
#365 added, and it reached three files this branch had already rewritten. The
new name is taken everywhere; what stays is this branch's shape around it --
a publish records itself with no `--update-plugin-config` to gate it and no
`confirmRecordSdk()` to ask.

The rest was the same refactor reaching code that leaves with v3:
`sdk save-changes` and its prompts and action, and the two source-tree readers
on `BuildContext`. They stay deleted; a rename does not bring them back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread src/commands/sdk/generate.ts Outdated
Comment thread src/commands/sdk/generate.ts Outdated
Comment thread src/types/sdk/generate.ts Outdated
Comment thread src/types/sdk/generate.ts Outdated
Comment thread src/types/plugin-config-context.ts Outdated
Comment thread README.md Outdated
Comment thread src/actions/sdk/publish/non-interactive.ts Outdated
Comment thread src/types/sdk/generate.ts Outdated

@mrafnadeem-apimatic mrafnadeem-apimatic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing v3 is a big simplification, and GenerateAction reads much better for it. Build, tests (1138 passing) and lint are green locally at 3823506. Requesting changes for two behaviour issues and the comment convention; details are inline.

Must fix

  1. Quickstart offers languages it can't generate. SdkQuickstartPrompts.selectLanguagePrompt (src/prompts/sdk/quickstart.ts:157) still lists every language on the plan, including Java, Ruby, Go and PHP. Picking one runs all four steps and only then hits languageNotAvailable. The free-plan check at src/actions/sdk/quickstart.ts:61 also counts languages that aren't available yet. Both should narrow to AVAILABLE_LANGUAGES, the way SdkPublishInteractiveAction does, and it needs a test.
  2. --stability help says [default: stable], but a run without the flag sends beta (for every available language). That's the README and --help for both sdk generate and sdk publish.

Conventions (.ai/instructions.md)
3. Comments: the rule is one line of why, and never narrate a change. Several of the new ones are multi-paragraph narration; I've flagged the main ones inline.
4. The stability resolution is duplicated across both commands, and publish then passes stabilityWasProvided next to the resolved value (inline).
5. format.relative is path logic reading process.cwd() inside the prompts layer; per the value-object skill it belongs on DirectoryPath/FilePath (inline).

Smaller points

  • Stale references to deleted code: .ai/skills/action.md, prompt.md and event.md still use save-changes as their example (event.md:110 links src/commands/sdk/save-changes.ts), and .ai/plans/apimatic-config.md:152 names assertNoCodegenVersionMismatch, which is no longer in src/.
  • Retired values are handled inconsistently. A retired language gets a friendly refusal, but --codegen-version v3, --update-plugin-config, --track-changes and --skip-changes fail with oclif's generic errors, which will break CI scripts without explanation. The PR is marked !, so this is allowed, but hidden flags that print a clear message for one release would be kinder.
  • Every non-dry-run publish now writes (or creates) src/apimatic.json with no opt-out. That's probably intended, but it's a new side effect on the user's source directory and worth a line in the README's new "SDK languages" section and the release notes.

Comment thread src/actions/sdk/quickstart.ts Outdated
Comment thread src/commands/sdk/generate.ts Outdated
Comment thread src/commands/sdk/generate.ts Outdated
Comment thread src/commands/sdk/generate.ts Outdated
Comment thread src/actions/sdk/generate.ts Outdated
Comment thread src/prompts/format.ts Outdated
Comment thread src/prompts/plugin/publish.ts
Comment thread src/prompts/sdk/generate.ts Outdated
Comment thread src/infrastructure/services/sdk-generation-service.ts Outdated
Comment thread src/types/plugin-config-context.ts Outdated
Muhammad Sohail and others added 5 commits September 25, 2026 16:17
…view

`CODEGEN_OPTIONS` is back, keyed only by the languages something can
generate, and it is now the one table: `AVAILABLE_LANGUAGES` is its keys,
`UPCOMING_LANGUAGES` the rest of the enum, and the levels a language offers
are read off its options. `V4_STABILITY_LEVELS` was a second table saying half
of that.

`--stability` had a static default of `stable` that `--help` and the README
both printed, while an omitted flag sent whatever the language offers. The
default is gone: absent means unchosen, and `StabilityChoice` resolves it once
per run, so `sdk publish` takes one argument where it took a level and a
boolean about the level.

`sdk quickstart` offered every language on the plan, including the four no
generator renders. Picking one walked four steps to a refusal. The plan and
the generator now have to agree, and the free-plan check counts the same set.

Comments cut to one line of why, or dropped where the code already said it.
`format.relative` moves to `DirectoryPath`/`FilePath` as `asTypedFrom`, where
it stops reading `process.cwd()` itself and picks up two fixes: the current
directory prints as `.` as its doc always claimed, and a child named `..cache`
is no longer mistaken for a sibling.

Also: `isUnavailableLanguage`, `getSdkGenerationStatus`, escaped newlines in
the two multi-line messages, and the skills docs no longer point at
`sdk save-changes`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The plans name `assertNoCodegenVersionMismatch` and the `codegenVersion`
field it read, both of which left with v3. They are dated records of a
decision, so the reasoning stays and a correction is added beside it rather
than the history being rewritten to look like it always said this.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`PluginRecordSdkAction` wrote `apimatic.json` through `PluginConfigContext`,
which owns the `plugin` and `languages` blocks together. Recording a publish
touches neither the plugin block nor the plugin at all: it is
`RecordPublishedSdkAction` now, it lives beside the publish that calls it,
and it goes to `ApimaticConfigContext` naming the one block it writes.

Only `sdk publish` ever called it -- both paths -- so the name was wrong about
the caller as well as the file.

`PluginConfigContext.upsertLanguage` went with it: nothing but its own tests
called it once the action stopped. The merge it did moves to the action as
`withLanguage`, unchanged -- a package-only run still keeps the repository an
earlier source publish recorded, and the other way about.

The prompts move too, and lose the `plugin` in their names: `configUnreadable`
and `configNotWritten` say which file they mean without claiming whose it is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
apimatic-io#2233 added StabilityLevelTag.Stable to CodeGeneratorVersion.V4
for C#, Python and TypeScript, so the validator accepts both levels. Stable
is listed first, which is what a run that never asks now sends.

Verified against dev: all three languages generate at stable, and typescript
generates with no --stability flag at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#363 landed the same idea under different names. One vocabulary survives:
CODEGEN_OPTIONS is the table, AVAILABLE_LANGUAGES its keys, and
isAvailableLanguage the predicate, so the hand-written PLUGIN_LANGUAGES and
isPluginLanguage are gone rather than kept beside them.

languageLabel takes a string again: a config file names its own languages
and may name anything, which is what plugin generate labels. That removes
the local labelOf helper it needed to work around the narrower signature.

sdk quickstart went with the SDK branch of the wizard in #363, so its action
and the test covering its plan-and-generator intersection go too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@mrafnadeem-apimatic
mrafnadeem-apimatic merged commit c48f91b into dev Sep 25, 2026
10 checks passed
saeedjamshaid added a commit that referenced this pull request Sep 25, 2026
…in-pages

Brings in #363 (quickstart's one funnel and language question) and #359
(v3 retired, AVAILABLE_LANGUAGES). The portal keeps PORTAL_LANGUAGES as
its own list beside #359's; the schema test keeps its name and comment for
the leniently read publishing record, on #359's new sample; #363's
quickstart tests stub artifacts that back the pages, which the preview now
requires before it prepares a project.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
saeedjamshaid added a commit that referenced this pull request Sep 25, 2026
…ome-tab

Brings in #359 and #369. The conflicts kept both sides: PreparePortalProjectAction hands its
callback the artifacts and still asks to confirm before fetching them; the serve action checks an
edit against the artifacts and still tells the content watch when the generated tabs change; the
README keeps the tabs the root nav.json decides and #369's SDK and Context Plugin pages;
quickstart.test.ts takes dev's side, which was formatting only here.

Three tests follow #369: a plugin block added while serving now needs a restart, so the content
re-check is shown with a hosted plugin's portal.pluginUrl; GeneratedPages takes a plugin source
rather than a flag; and go is no longer a portal language. The nav.json note test moves back
into the live-preview describe the merge had put it beside.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
github-actions Bot pushed a commit that referenced this pull request Sep 28, 2026
# [2.0.0-beta.1](v1.5.0...v2.0.0-beta.1) (2026-09-28)

* build!: move to Node 24 and TypeScript 7 ([#347](#347)) ([2528d6e](2528d6e))
* feat(quickstart)!: one funnel, and the language question ([#363](#363)) ([c270135](c270135)), closes [#361](#361)
* feat(sdk)!: retire v3 generation and let a publish record itself ([#359](#359)) ([c48f91b](c48f91b)), closes [#358](#358) [#358](#358) [apimatic-io#2233](https://github.com/apimatic-io/issues/2233)

### Bug Fixes

* **config:** keep the package configuration a publish did not write ([#392](#392)) ([bc52735](bc52735))
* **config:** nest publishing under the language ([#350](#350)) ([fbacf74](fbacf74))
* **deps:** take fumadocs 16.15.15, which fixes bold text in a built portal ([#388](#388)) ([fa178ab](fa178ab))
* **plugin:** ask only for the plugin's identity when the languages are recorded ([#389](#389)) ([d721819](d721819)), closes [apimatic/apimatic-io#2258](https://github.com/apimatic/apimatic-io/issues/2258)
* **portal:** bundle the Geist fonts instead of loading them from Google Fonts ([#366](#366)) ([95f2f7e](95f2f7e))
* **portal:** drop the TypeScript definitions panel from operation pages ([#353](#353)) ([042eb89](042eb89))
* **portal:** give a run the temp directory the filesystem names, not an alias ([#377](#377)) ([d71d23f](d71d23f))
* **portal:** keep the encoding a page slug carries ([#396](#396)) ([0bec5f4](0bec5f4)), closes [apimatic/apimatic-io#2270](https://github.com/apimatic/apimatic-io/issues/2270)
* **portal:** keep the header's links on one line ([#398](#398)) ([40dc472](40dc472))
* **portal:** keep the portal name on one line in the header ([#395](#395)) ([f3d352c](f3d352c))
* **portal:** keep the preview starting until its first page answers ([#399](#399)) ([5359442](5359442))
* **portal:** name the operations a shared page comes from, in words a spec's author knows ([#387](#387)) ([01197a7](01197a7))
* **portal:** offer the page actions on API reference pages ([#382](#382)) ([22f4873](22f4873)), closes [apimatic/apimatic-io#2247](https://github.com/apimatic/apimatic-io/issues/2247) [#383](#383)
* **portal:** refuse tag and operationId names a URL or a Windows folder cannot hold ([#400](#400)) ([0ea01b2](0ea01b2)), closes [apimatic/apimatic-io#2262](https://github.com/apimatic/apimatic-io/issues/2262) [#396](#396)
* **portal:** render a request body in a media type Fumadocs has no adapter for ([#386](#386)) ([9f89ad3](9f89ad3))
* **portal:** render an operation whose response example has no value ([#373](#373)) ([1dc84ad](1dc84ad))
* **portal:** render split OpenAPI specifications correctly ([#354](#354)) ([d97cda6](d97cda6))
* **portal:** stop printing an operation's raw description under its title ([#383](#383)) ([25b9f6e](25b9f6e)), closes [apimatic/apimatic-io#2242](https://github.com/apimatic/apimatic-io/issues/2242)
* **quickstart:** download the sample spec from its new openapi.json path ([#401](#401)) ([ebc6743](ebc6743))
* the release test's high and medium findings ([#375](#375)) ([7fe41e1](7fe41e1))

### Features

* code samples support ([#352](#352)) ([7b82568](7b82568))
* **config:** replace portal.json and plugin-config.json with apimatic.json ([#348](#348)) ([506f67f](506f67f))
* local plugin generation ([#358](#358)) ([5ba70de](5ba70de)), closes [#351](#351) [#346](#346) [#348](#348) [#318](#318)
* **portal:** build the documentation portal locally with Fumadocs ([#343](#343)) ([073d09a](073d09a))
* **portal:** build the SDK and Context Plugin pages from real templates and the portal artifacts ([#369](#369)) ([0efd7b3](0efd7b3))
* **portal:** fetch the artifacts from /portal-artifacts ([#361](#361)) ([0e823f3](0e823f3))
* **portal:** generate the SDK and context plugin pages ([#360](#360)) ([338b14a](338b14a))
* **portal:** give the portal block its v2 shape, with branding and tabs ([#355](#355)) ([d0fb189](d0fb189))
* **portal:** lead each SDK card with its install command ([#376](#376)) ([119e9d6](119e9d6)), closes [#375](#375)
* **portal:** let the root nav.json decide the tabs, and fix the nav.json QA findings ([#371](#371)) ([ecf7666](ecf7666))
* **portal:** order the sidebar from nav.json ([#346](#346)) ([306e4f9](306e4f9))
* **portal:** point to api transform when src/spec has no OpenAPI 3.x document ([#364](#364)) ([eefc917](eefc917))
* **portal:** the SDK Overview design, on the SDKs, language and plugin pages ([#391](#391)) ([fd3fe34](fd3fe34))
* **quickstart:** suggest validating with an AI agent when a spec fails ([#385](#385)) ([36fd2bb](36fd2bb))

### Reverts

* **portal:** offer the page actions on API reference pages ([#382](#382)) ([#397](#397)) ([c087ef9](c087ef9)), closes [#388](#388) [apimatic/apimatic-io#2247](https://github.com/apimatic/apimatic-io/issues/2247)

### BREAKING CHANGES

* `apimatic sdk save-changes` is removed, along with
`--codegen-version`, `--stability`, `--track-changes` and `--skip-changes`
on `sdk generate` and `--codegen-version`, `--stability` and
`--update-plugin-config` on `sdk publish`. Java, Ruby, Go and PHP cannot be
generated until they reach v4.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(sdk): keep --stability, which outlived the version it came with

Retiring v3 took `--stability` with it on the grounds that there was one
answer left. There is one answer *today*: v4 renders every language at beta
and they reach stable one at a time, so the level is a choice that survives
the version being fixed.

The flag is back on both commands, defaulting to stable as before, and the
level a language offers is a table rather than a constant — `stableLevels`
of one is answered without asking, and the interactive flow puts the
question back the moment a language has two. `sdk publish` names the level
in its summary only when the user chose it, read from oclif's parse
metadata, because `--stability stable` and the default are the same string
and the doc's block has no such row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(sdk): keep --codegen-version, with one version in it

Retiring v3 removed the flag along with the version, on the grounds that a
choice of one is not a choice. But the flag is how a caller says which
generator they want, and there will be a v5: removing it makes that move a
release that changes what people get, rather than a value they pass.

`CodeGenerationVersion` now holds `V4` alone, and stays an enum so the next
one is a line in it. The flag is back on both commands, accepting `v4`,
defaulting to `v4`, and refusing `v3` by name rather than generating
something else quietly. `sdk publish` names it in its telemetry again.

It is not written down. `codegenVersion` left `apimatic.json` when the
service took over deciding the generator, so the flag says what to generate
with and nothing records what was used.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(config): drop codegenVersion from the published schema
* `apimatic quickstart` no longer offers a choice between a
portal and an SDK. It always builds a portal, and `apimatic sdk generate`
is the command for an SDK alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(quickstart): ask which languages the portal covers

The one question quickstart asks besides the spec. Every language a plugin
can carry comes up checked, because a first portal covering everything it
can is what a single Enter should give, and the four it cannot are named in
the prompt rather than left out in silence.

The answer is written to `apimatic.json` through `recordLanguages` before
anything is built, because that file is what says which SDKs a portal
documents — every command after this one reads it rather than the answer
that produced it.

`languageLabel` moves to `types/sdk/generate.ts`, where both prompts that
name a language can reach it, and TypeScript is spelled the way its own
project spells it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(quickstart): name the plugin after the folder, without asking

The `plugin` block is what asks the service for a context plugin, so
quickstart has to write one — and the journey allows it no question about
plugins. The identity comes from the directory the user is standing in.

A folder name is not a plugin id. `Acme Payments` and `acme_payments` are
ordinary names and neither is the kebab-case `pluginId` has to be, so the
id is made from the name while the name itself is kept as typed — the id
goes to `gh repo create`, the name is what a reader sees. A directory whose
name survives none of that falls back, because writing an id the config
would then refuse is worse than not deriving one.

`0.1.0` for the version: every plugin starts somewhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(quickstart): keep generated files out of the repository

Every command that builds a portal writes `src/static/sdk/`,
`src/static/plugin.zip` and an expanded `./plugin` into the project. None
of it belongs in a repository, and `portal serve` writes it too — so
without this a preview leaves a clean checkout dirty.

`/plugin/` is the one that matters. `plugin publish` runs `git init` inside
that directory and pushes it as its own repository; a parent tracking it
would nest one repository inside another.

Quickstart writes the entries because quickstart is what makes the project.
It appends what is missing and rewrites nothing, so a `.gitignore` the user
wrote survives and re-running in an adopted directory stacks no duplicates.

The paths are ignored here before anything on this branch produces them:
the placement lands with the `/portal-artifacts` call, and a user whose
first build dirties their tree has already been failed by then.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(quickstart): end in the preview again, and say the coming languages under the answer

Two things, both about where a line belongs.

The rebase onto dev quietly took dev's ending for the wizard -- the one that
stops at the scaffold because "the wizard does not ask for the project's SDK
languages yet". It does, three lines above that comment. The handoff to
`portal serve`, and the `onAfterServe` the serve action took for it, had both
gone; nothing failed, because no test described the funnel's own ending. One
does now.

`Next Steps` loses its first two items with them. It told the user to name
their SDK languages and then start a preview; the wizard has just done both.
What is left is what to change now that the portal is on screen.

The coming-soon languages move out of the multiselect's message and under the
answer, in grey. Inside the message they rendered above the languages the user
had picked, in the green a submitted answer is drawn in, reading like a fourth
option rather than a footnote to the three.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(portal): give quickstart the api transform hint, with the format it needs

Quickstart checks the spec itself before it writes the project, so the
transform hint portal generate and portal serve now give never reached it.
A Postman collection or a RAML file was told only that it was not an
OpenAPI document, with nothing to do about it.

Both now end on one sentence, which names --format=openapi3yaml: the
transform refuses to run without a format, so the command as printed
before would have failed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(quickstart): ignore only what is still generated into the project
* engines.node is now >=24.0.0. Node 22 is no longer supported.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(sdk): build the validation service once configDir is assigned

`validationService` was initialised from `this.configDir` in a property
initialiser, but `configDir` is a constructor parameter property. The code only
worked because TypeScript's downlevel emit assigns parameter properties before
property initialisers; under the native class-field semantics that a modern
target selects, `this.configDir` is still undefined at that point and the
service would be constructed with no config directory.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* build: compile with TypeScript 7, keeping TypeScript 6 for linting

TypeScript 7 is the native compiler. On this repo it builds `src` in ~1.3s
against ~4.7s, and type-checks the suite in ~1.0s against ~4.8s. Emitted
output is unchanged apart from sourcemaps, temp-variable naming and
declaration ordering.

typescript-eslint cannot run on it: every published version, canary included,
declares `typescript >=4.8.4 <6.1.0` and throws at module init on TS 7, which
would take out `lint`, `posttest` and the pre-commit hook. So the two sit side
by side in Microsoft's documented arrangement — `tsc` is TypeScript 7 and
builds, bare `typescript` is the TypeScript 6 shim that only the linter loads.
Naming them this way rather than aliasing TypeScript 7 keeps the compiler each
script gets written down, and survives `pnpm update --latest`.

TypeScript 6 stopped sweeping node_modules/@types, so the test project names
the globals the suite needs; the source project resolves its types through
`node:` imports and needs no list.

`target` moves to es2024 now that the engine floor is Node 24, which also
selects native class-field semantics. `ts-node` goes: it drives the classic
compiler API, which TypeScript 7 no longer ships, and the suite runs on tsx.
`@types/mocha` was six years behind mocha and had to be named explicitly.

The `typescript.tsdk` pin is dropped. It is deprecated in favour of
`js/ts.tsdk.path`, inert unless a developer opts in by hand, and would now
point at a TypeScript 6 build within a patch of what VS Code already bundles.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* ci: test on Node 24 and 26, and release on 24

The engine floor is Node 24, so the 22 legs go. The second leg is 26 rather
than the floor alone: 26 becomes LTS on 2026-10-28, inside this major's life,
so it is what most developers will be running. The job count is unchanged.

Releasing on Node 24 drops the `npm install -g npm@latest` step, whose only
purpose was that Node 22 bundles npm 10.9.8 while OIDC trusted publishing
needs 11.5.1. Node 24 bundles 11.19.0. The constraint moves to a comment on
the matrix, since pinning that job back to 22 would silently break publishing.

`npm-tag-latest.yml` carried a `node: [ '14' ]` matrix that no step consumed;
the job never sets up Node at all.

The standalone pnpm in test.yml is kept rather than reverted to corepack. Its
original reason is gone, but it is a SHA-pinned executable that needs no Node
of its own, where corepack would resolve pnpm from the registry at run time and
need the three-step setup-node dance the other workflows use. The comment now
states the reason that actually holds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore(vscode): repair the CLI debug configuration

`preLaunchTask` named a task the TypeScript extension provides, and that
provider is not registered when the extension stands down for TypeScript 7, so
the configuration would fail to start. The npm task provider supplies `build`
regardless, and still rebuilds before launching.

`program` pointed at `bin/run`, deleted in the ESM migration; it only launched
because Node appends `.js`. The forty commented-out argument lines carried
another machine's absolute paths.

`.gitignore` re-ignored `.vscode/launch.json` in its custom section, cancelling
the allowlist above it. Inert while the file is tracked, but it would have
refused a re-add.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: record the two-compiler toolchain and how to remove it

The `typescript` dependency no longer being the compiler is surprising enough
to look like a mistake, so state why it is there, which command gets which
compiler, and the condition for deleting the arrangement.

Also corrects the build commands, which documented `tsc -b` although the
project has no references and the scripts call plain `tsc`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* ci: remove unused action

* fix: broken test

* fix: mock fs broken in node 26

* ci: install pnpm standalone in the build and release workflows

Corepack is not distributed with Node 25 and newer, so the Node 26 leg of
check_build could not bootstrap pnpm. Also correct the release floor: npm
11.5.1, which trusted publishing needs, first ships in Node 24.5.0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(hooks): check the running Node against the declared engine range

Read engines.node from the manifest oclif has already parsed and compare
with semver, so package.json is the single source of the floor and a
minor-version floor is honoured. Test the hook's behaviour on both sides
of the range, and hold the CLI's range to the Node floors the portal
template's dependencies declare.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs: name the e2e test that depends on the two-compiler arrangement

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore: untrack launch.json and drop leftover config

launch.json goes back to being a personal, ignored file. mocha.opts named
ts-node and has been unused since mocha 8. ESLint now skips Claude Code
worktrees under .claude.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(portal): bind the prerender preview server to 127.0.0.1

The prerender pass fetched pages from a preview server on `localhost`.
On macOS it listened on `::1` only; a stalled connect under load fell
back to 127.0.0.1 and was refused, failing the portal build.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 2.0.0-beta.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants