Skip to content

Release 2.0.0-beta.3 - #422

Merged
saeedjamshaid merged 18 commits into
betafrom
dev
Oct 5, 2026
Merged

saeedjamshaid merged 18 commits into
betafrom
dev

Conversation

@saeedjamshaid

Copy link
Copy Markdown
Collaborator

Promotes dev to beta: the first release on the new pipeline (#409), expected to publish 2.0.0-beta.3 to the npm beta dist-tag as a GitHub prerelease. The run commits nothing back to beta; the tag goes on this merge commit.

Merge with Create a merge commit.

What releases

The remaining commits since 2.0.0-beta.2 are ci, chore and test changes, plus the commits of #412, and release nothing.

🤖 Generated with Claude Code

razazarif and others added 17 commits September 30, 2026 16:45
Builds this commit of the CLI, packs it, installs the tarball globally, and runs
the portal suite from apimatic/v4-test-automation against it — so the tests
exercise the CLI you just changed rather than whatever is on npm.

Three stages: artifacts (what `apimatic portal generate` produced vs what the
build's src/apimatic.json declared — SDK archives, package identity, generated
pages), visual (screenshots against committed baselines), and functional
(behaviour; wired up, cases still being added).

Runs on pull requests as well as merges into dev, so a change to generation
shows up on the PR that caused it rather than after it lands.

windows-latest is required rather than preferred: the suite's visual baselines
were captured on win32/Chromium, and font rasterisation differs enough across
operating systems that a ubuntu runner would fail every snapshot.

Follows the conventions already in this repo — SHA-pinned actions, standalone
pnpm 11.0.8 because corepack isn't distributed with Node 25+, and
persist-credentials: false on checkout.

Needs two secrets before it can go green; see the pull request description.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ci: run the portal automation tests against this build
…round (#410)

* fix(portal): render auth scheme descriptions as Markdown in the playground

Upgrade fumadocs-openapi from 11.4.1 to 12.1.0, which renders a security
scheme's description as Markdown in the playground's auth selector
(fuma-nama/fumadocs#3607). 12.x removed the v11 hooks the template used,
so the example layout, usage tabs and code block move to useOpenAPI,
useOperation, useExampleRequests and useCodeUsage. The cURL tab is now a
registered generator under an id of its own.

12.1.0 is younger than the 7-day release age, so it is exempted in
pnpm-workspace.yaml until 2026-10-06.

Closes apimatic/apimatic-io#2249

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

* fix(portal): address review of the fumadocs-openapi 12.1.0 upgrade

The 12.1.0 upgrade also changes the playground's OAuth handling: the
client credentials flow can send the client ID and secret with HTTP
Basic (fuma-nama/fumadocs#3609), a relative tokenUrl resolves against
the selected server instead of failing the token fetch
(fuma-nama/fumadocs#3610), and the client secret is no longer sent to
the authorization server in the state parameter.

Pin @scalar/json-magic's undici to 7.29.1. json-magic 0.13.5 pins
undici 7.24.4, which carries 21 advisories, and Fumadocs loads it at
runtime through the bundler's fetchUrls plugin.

Drop the fumadocs-openapi release-age exemption: 12.1.0 is locked, and
an unversioned entry would exempt every later release too.

Build each Parameter from useOperation().parameters, which Fumadocs has
already resolved, deduplicated and grouped by location, instead of
re-reading the raw operation and path item. A parameter declared on
both is no longer listed twice.

Remove the e2e case for the image upload error screen, which 12.1.0 no
longer shows, and correct two media adapter comments the upgrade made
stale.

Refs apimatic/apimatic-io#2252

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

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* docs(plans): propose a tabs key in the root nav.json

The root nav.json's `pages` decides two things at once: which top-level
folders are tabs, and the order of Home's sidebar. A folder cannot be
ordered inside Home without becoming a tab.

The amendment gives the root file a `tabs` list for the tab bar and leaves
`pages` ordering Home, as it orders every other folder. The apimatic:
tokens can go in either list, and stay refused below the root. A beta.1
root file that relies on the old reading is refused until 2.0.0, and a
portal whose only tab is Home hides the tab bar.

Section 5 of portal-config.md states the rules and section 16 the
delivery; portal-navigation.md and generated-pages.md point to them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(plans): tighten the nav.json tabs proposal after a second review

The beta.1 refusal no longer counts a folder serving `/`, which beta.1
itself refused, so no beta.1 file can hold one. The navigation context
gains the subfolders with a page in them, since `childNames` cannot tell
a page from a folder and both new refusals need to.

Section 16 now names the doc comments and existing tests that state the
old rule, holds every setting the CLI accepts to the template's schema,
and says the e2e suite is red between the template and CLI steps. The
serve paragraph speaks of sections rather than tabs, and
portal-navigation.md counts `tabs` as the third setting now that `root`
is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(portal): read the root nav.json's tabs in the template

The root nav.json's `pages` carried two decisions: which top-level
folders are tabs, and where Home's nodes go. A folder could not be
ordered inside Home without becoming a tab. `tabs` now takes the first
decision, and `pages` orders Home's sidebar as every other nav.json
orders its folder (.ai/plans/portal-config.md, sections 5 and 16).

- navigation-schema.ts extends Fumadocs' metaSchema with `tabs`, typed
  as `pages`, and the docs collection validates nav.json against it,
  so the key survives. Unknown keys are still stripped.
- A folder is a tab when `tabs` lists it. The API reference and each
  generated section are tabs unless the root `pages` names them, which
  places them in Home at the address they always had.
- The tabs are ordered by `tabs` through `reorder`, with the defaults
  the root `pages` gave before for what it leaves unnamed.
- portalTabs returns no tab when Home is the only one, so the header
  shows no switcher over a single choice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(portal): validate the root nav.json's tabs in the CLI

The CLI holds `tabs` to the rules the template now reads it by
(.ai/plans/portal-config.md, sections 5 and 16).

- `tabs` is known in the root nav.json and refused below it with a
  sentence of its own. Its entries are a folder with a page under it,
  `api` or `apimatic:api`, `apimatic:sdks` and `apimatic:plugin`. A
  page, `...`, a folder serving `/`, an unknown name, a duplicate and a
  node `pages` also names are each refused.
- The root `pages` may now name a token, `api` or a folder serving `/`,
  each placed in Home. Below the root a token is still refused, and the
  sentence names both places it can go.
- A root file with no `tabs` whose `pages` names a folder, `api` or a
  token is refused, since 2.0.0-beta.1 made tabs of those entries and
  reading it under the new rule would move them into Home's sidebar.
  The check goes before 2.0.0 ships.
- The content tree takes folder tabs from `tabs`, and leaves a section
  placed in Home out of the tabs and out of the shared-name warning.
- The notice naming the folders the file made tabs of goes: `tabs` says
  so in its own name. makesSameTabsAs becomes makesSameSectionsAs.
- The default fixture moves its tabs from `pages` to `tabs`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(portal): build a portal with every section in Home, and document tabs

- A third e2e build, from the sections-in-home fixture, whose root
  nav.json places the API reference, the SDKs and the Context Plugin in
  Home with `pages` and names no tab. It checks the prerendered sidebar
  holds Home alone with each section where `pages` names it, that each
  section keeps its address, and that the header renders no tab bar.
- The README's bullet on tabs is rewritten for `tabs` and `pages`, the
  tokens placeable in either, and the refusal of a 2.0.0-beta.1 file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(portal): trim the duplication and complexity SonarCloud flagged

- entryErrors hands each entry to listEntryError, which brings the
  function under the cognitive-complexity limit.
- The e2e builds share portalBuilt, which registers the before and after
  hooks and reads the output, and tabPositions, which finds each tab's
  link in the header, instead of repeating both per describe block.
- The tab-order defaults in tabs.test.ts are one table-driven loop.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(portal): answer a root nav.json without tabs in words a new user can act on

Found reviewing the tabs change before handing it over.

- The scaffold writes `"tabs": []` beside `pages`, so a folder named in a
  scaffolded file is placed in Home rather than refused.
- The refusal of a root file with no `tabs` no longer leans on beta.1
  history: it says the file does not say whether the entries are tabs or
  in Home's sidebar, and how to say either.
- A misspelt setting at the root is answered with `tabs` among the
  settings, which the list left out.
- The README states the real default for a section named in neither
  list: after the folders `tabs` names, before a reference not named
  ahead of them.
- Comments trimmed to one line each; a dead guard in isTabFolder and a
  membership scan in the content walk are gone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(portal): keep unnamed section tabs behind everything tabs names

The unnamed sections' tab position reused the sidebar's anchor, which
sits after the last named user content. A tabs list naming only the
reference and tokens has none, so the anchor fell to the front and the
unnamed sections landed ahead of a reference the file deliberately put
first: tabs ["apimatic:api"] gave Home, SDKs, Context Plugin, API
Reference.

In the tab bar every folder is named, so the unnamed band now follows
everything `tabs` names, a named reference included, with an unnamed
reference still last. The sidebar keeps its own anchor. The README
states the rule as one sentence.

Found in review (PR #408, first comment).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(portal): refuse api as a tab when a page carries the name

With a page at `content/api.md`, naming `api` in `pages` is refused,
since the entry positions the mounted reference and the page could
never be placed. The same name in `tabs` was accepted before the
shared-name test ran, letting through the quietly wrong sidebar the
validator exists to refuse. The tab entry now gives the refusal
`pages` gives, from one shared sentence.

Found in review (PR #408, second comment).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(portal): point a mistyped tab at what tabs accepts

The not-found sentence for a tabs entry borrowed the pages hint, which
could point at an entry tabs then refuses: a capitalised page name was
answered with "Did you mean 'authentication'?", and following it earned
"'authentication' is a page" one run later. A near miss of `api` named
only the token, although the folder name is accepted too.

The tabs path now has a hint of its own: a folder near miss stays a
suggestion, a section is offered as its token, `api` is offered in both
spellings, and a page's name is answered in one message with where a
page can go.

Found in review (PR #408, third comment).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: keep the README's CRLF line endings

An edit on this branch rewrote every line ending from CRLF to LF, so
the whole file diffed and hid the 24 lines that actually changed, and
any other branch touching the README would conflict on every line. The
endings are back as dev stores them; the words are untouched.

Found in review (PR #408).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(portal): name the tabs in the order the tab bar shows them

The tab list behind the shared-name warning emitted the folders, then
the generated sections, then the reference, ignoring where `tabs` put
the tokens: with `tabs` naming a section before a folder, the warning
listed them the other way around, and the doc comment still promised
the report's order. The list now walks `tabs` and emits each entry in
place, then the unnamed sections and an unnamed reference, mirroring
what the template renders.

Found in review (PR #408).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(portal): answer a folder near miss with a stray extension, from one helper

The tabs hint matched a folder near miss on the lowered name alone,
while the page lookup beside it also stripped a stray extension, so
'Guides.md' got no suggestion in `tabs` although the same entry in
`pages` did. The lowering, stripping and matching now live in one
nearMiss helper that answers what an entry was meant as, and each list
phrases its own sentence over it, so the two cannot drift again.

Found in review (PR #408).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(portal): make tabs the whole tab bar, a section it does not name a folder in Home

A generated section and the API reference were tabs unless the root `pages`
placed them in Home, so `{}` and `"tabs": []` gave Home and three tabs while
every folder went into Home, and the tab bar was not what `tabs` said. Now a
node is a tab exactly when `tabs` names it: a folder by name, the reference
by `api` or its token, a section by its token. Everything else is in Home,
where `pages` orders it and, unnamed, Fumadocs' own order holds, a section
among the folders like any other. `reorder` loses its bands and anchor, and
the root is no longer reordered without a `pages` of its own. A file with no
`tabs`, or no file, leaves Home alone with no tab bar.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* feat(portal): read a root nav.json with no tabs as an empty list, and scaffold the sections as tabs

With `tabs` the whole tab bar, a root file without it has one reading, no
tab, so the refusal of a 2.0.0-beta.1 file goes: there is nothing left to
ask. The content tree names a tab for each `tabs` entry and for nothing
else, so a section the file does not name is compared with no tab's name.
The scaffold writes `apimatic:sdks`, `apimatic:plugin` and `apimatic:api`
in `tabs`, so a fresh portal keeps the tabs it had; the sample repository's
root file does the same. The default fixture names the plugin's token too,
and the branded build, which has no `nav.json`, asserts no tab bar.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(portal): state the tabs rule as it now stands

The README bullet and the plans said a section named in neither list kept a
tab at a default place, and described the beta.1 refusal. Both now say that
`tabs` is the whole tab bar after Home, that what it does not name is in
Home in Fumadocs' order, and that a file with no `tabs` makes no tab, with a
history entry for the review that decided it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* refactor(portal): resolve each nav.json entry once, for both lists, the tab list and the hints

`api` and `apimatic:api` being one node was ruled on in five places. Now
`target` says once what an entry names -- the rest entry, an `apimatic:`
string that positions nothing here, the API reference, a section, a folder,
a page, an empty folder or nothing -- and the duplicate check, each list's
rules, the tabs the content tree names and the near-miss hint read that
answer. `NavigationSettings.tabs` carries the resolved tabs, undefined for
a file with no list. The lists are typed `'pages' | 'tabs'` with a constant
each, an entry with its list is one named type, and the sentences a page in
`tabs` and an empty folder get are written once. A list of the wrong type no
longer hides the other list's entries, and a page named `api` is refused
under either spelling of the reference, since it could be positioned under
neither.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(portal): take the scaffold's tabs from the validator's token list

The scaffold rebuilt `TOKENS` from its parts; the one list now serves both.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test(portal): hold the template's schema to every root setting the CLI accepts

The test hard-coded `tabs`, so a setting added to `ROOT_FIELDS` would have
been validated by the CLI and stripped by the template with nothing failing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* feat(portal): say once when a root file with no tabs makes no tab

A root file with no `tabs`, or no root file, makes no tab, and a
2.0.0-beta.1 project had the sections as tabs, so the upgrade was silent.
The build now says so once, naming the sections in Home and both ways to
settle it: the tokens in `tabs` to have them back, or `"tabs": []` to keep
the sidebar, which says nothing. Under `portal serve` it is given once, like
the other notices, and again when the plugin block changes what is in Home.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* feat(portal): say when a Home title has no tab bar to show in

A root `title` names the Home tab, and one tab draws no tab bar, so with
nothing in `tabs` the name showed nowhere and the CLI accepted a setting
that did nothing. It now says so once. The sections-in-home fixture drops
the title it never showed, and its build looks for a Home link instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(portal): stop the prompts calling the SDK pages and the context plugin tabs

They are tabs only when `tabs` names them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* refactor(portal): draw no tab bar for one tab, whichever it is

`portalTabs` builds its list and returns none for a single entry, which
says "one choice switches nothing" directly and covers a tab dropped for
having no link. `HOME_TAB_ID` goes back to `navigation.ts`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* refactor(portal): claim the tabs as reorder claims an entry

`groupIntoTabs` matched each folder to a `tabs` entry and `reorder` then
matched the entries back to folders. `nodeNamed` now resolves an entry to
its node for both, and the tabs are the nodes `tabs` names, in its order;
`isTab`, `isTabFolder` and `lists` go. The section check runs the cheap
path test before the storage read.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* chore(portal): cut the comments that narrated the tabs rule

The rule lives in the code and in the plan; the doc comments keep only what
the code does not say.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(portal): record the third review

The two notices, the entry resolver, and the alphabetical place of an
unnamed section kept on purpose.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* chore(portal): quiet the two SonarCloud findings on the notice prompt and the schema test

The notice built its suggestion inside a nested template literal, and the
schema test passed the same field to the assertion twice.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(portal): refuse a page named api under api alone, not under the token

Resolving both spellings to the reference had the shared-name refusal fire
for `apimatic:api` too, so every scaffolded portal that added `content/api.md`
failed to build, though the entry was the token and the page was never meant
to be positioned. Only `api` spelled out might have meant the page, so only
that spelling is refused, as on dev; `target()` is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* feat(portal): name the folders pages made tabs in the no-tabs notice

Before `tabs` existed a folder the root `pages` named was a tab as well, so a
2.0.0-beta.1 project that followed the notice's suggested line got its sections
back and lost those tabs without a word. The notice now names them, and the
line leads with them in `pages` order, as beta.1 showed them; a `(group)` folder
serving the home page is left out, since `tabs` refuses it. Under `portal
serve` a folder newly named gives the notice again, once.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(portal): record the fourth review

The third-review entry said the default order is rarely seen; it is seen by
every existing portal the moment a release adds a section, since the scaffold
writes `nav.json` once, so that release must tell them to name its token. The
entry also claimed the `api` page was refused under either spelling, which the
fix before this corrects.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(portal): name only what nothing places in the no-tabs notice

The line the notice offered was refused when followed: a folder or token the
root `pages` named went into `tabs` too, and a node is in one list or the
other. A folder in `pages` is in Home because that is what `pages` means, so
the folders go, and the line names only the sections and the reference that
nothing places; once `pages` places them all, the notice says nothing. The
validator answers with what `pages` places, so the `api` spelling stays ruled
on in one place, and the text that leaned on beta.1 is in present terms: the
CLI's messages describe how `nav.json` works now, not what it did.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: saeedjamshaid <muhammad.saeed@apimatic.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…HTTP Basic (#416)

Upgrade fumadocs-openapi from 12.1.0 to 12.1.1. The playground's
Client Authentication selector now starts on "Send as Basic Auth
header", the method RFC 6749 §2.3.1 requires authorization servers to
support, instead of sending the client credentials in the body, which
the RFC calls NOT RECOMMENDED (fuma-nama/fumadocs#3629).

12.1.1 also offers the selector in the authorization code flow, sends
only client_id in the body when there is no client secret, stops
requiring client credentials in the password flow, and adds a Download
button to the playground's response panel.

Only fumadocs-openapi changes in the lockfile; its dependencies are
the same as 12.1.0's. 12.1.1 was taken before the 7-day release age
with a temporary exact-version exemption, which is not committed.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…er the base path (#413)

* fix(deps): take fumadocs 16.15.17, which links "View as Markdown" under the base path

From 16.15.13 the page actions popover handed out its "View as Markdown"
link as given, while Copy Markdown on the same page prefixed Vite's
base. A portal hosted under a path linked readers to the host's root,
where the Markdown companion is not (fuma-nama/fumadocs#3620).
16.15.17 runs the link through the same withBasePath, and Copy Markdown
now fails on an error response instead of copying the error page.

A new test renders the real popover through Vite with base '/docs/' and
'/' and reads the link off the markup; it fails on 16.15.15.

The loader now decodes both sides of a slug lookup, so a decoded splat
finds its page without slugsFromSplat; page-slugs.test.ts says so.

Part of apimatic/apimatic-io#2275.

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

* fix comment

* chore: address review on the fumadocs 16.15.17 bump

Drop the fumadocs release-age excludes, since the lockfile already pins
the versions taken. Stop the page-actions test's Vite server from
watching the checkout and writing a dependency cache, match the link's
href in any attribute position, and trim comments the names now carry.

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

* fix(deps): repair the fumadocs-openapi 12.1.1 lockfile entry from the dev merge

The merge renamed the 12.1.0 entry to 12.1.1 by hand, so it kept 12.1.0's
dependencies and missed @fumadocs/json-schema, and its peers still named
fumadocs-core and fumadocs-ui 16.15.15, which the lockfile no longer has.

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

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
#417)

fumadocs-openapi 12.1 skips a response example that has no `value`
(fuma-nama/fumadocs#3608), so `undefined` no longer reaches the code
block and the operation page no longer crashes. The template's own
CodeBlock, which only existed to skip that case (#373), is removed and
pages render through Fumadocs' default code block.

The usage tabs take the code block from useComponents(), and the cURL
tab renders nothing itself when no example request is available, which
the removed guard used to cover.

Refs #370

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat: handle zip service failures

* refactor: return unArchive's Result synchronously and import node:crypto

Extraction runs synchronously through adm-zip, so unArchive no longer
wraps its Result in a promise its callers had to await.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: use srcDir instead of build

* fix: update wording for sdk zipping

* refactor(zip): keep unArchive asynchronous

unArchive returns a Promise again, as archive does, so that adm-zip's
synchronous extraction stays out of the service contract and a later
switch to an asynchronous extractor leaves its callers unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(zip): list the directory before handing yazl any file

The walk added each file to yazl as it went, and the error listener was
attached only after the walk. When a directory deeper in the tree could
not be listed, the files added before it still had pending stat calls on
a ZipFile with no listener, so a dangling symlink among them crashed the
process even though archive had already reported the failure.

The walk now finishes before the ZipFile exists, and the listener is in
place before the first file is added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor: name the staged upload directory by SOURCE_DIRECTORY_NAME

ProjectContext.srcDirZip and PluginConfigContext.stageUpload spelled the
project's source directory name as a literal; both now take it from the
project layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: report zip and unzip failures as one FileProblem, with their reason

The contexts that zip or unzip returned the error's message as a bare
string, which no prompt could switch on. TempContext.zip,
ProjectContext.srcDirZip, SdkContext.save,
SdkContext.loadSdkInTempDirectory, PluginContext.save and
SpecContext.install now all return one shape, FileProblem, with a kind
(zipFailed or unzipFailed) and the reason.

That removes what the bare string forced on the actions:

- sdk generate and plugin generate no longer make up a
  ServiceError.InvalidResponse for a download they could not unzip, and
  no longer drop the reason. The user sees the same invalid-response
  message, now followed by the reason.
- plugin generate tags its config and service failures, so its whole
  chain carries one PluginGenerationProblem that a single prompt words
  by kind. UploadPackagingFailure, the instanceof/typeof switch in the
  action and the TODO above the dropped reason are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(sdk): name the language directory when the SDK cannot be saved

sdk generate --zip named the parent output directory when the SDK could
not be zipped into place, while the success message names the language
directory. SdkContext.save now reports the directory it was saving to,
and the message names it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Oct 5, 2026

Copy link
Copy Markdown

@saeedjamshaid
saeedjamshaid merged commit d6fc25b into beta Oct 5, 2026
11 checks passed
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown

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

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.

6 participants