Skip to content

Skills M5a: CLI: Add the public storybook tools command derived at runtime from the OSA toolsets - #35719

Closed
kasperpeulen wants to merge 13 commits into
m2b-core-toolsetsfrom
m5a-tools-cli-osa
Closed

kasperpeulen wants to merge 13 commits into
m2b-core-toolsetsfrom
m5a-tools-cli-osa

Conversation

@kasperpeulen

@kasperpeulen kasperpeulen commented Aug 3, 2026 •

Copy link
Copy Markdown
Member

Closes #35716 · Closes #35724 · Closes #35747

Note

Stacked on #35677 (which stacks on #35726). This PR targets m4-addon-mcp-core-services, so its diff is the CLI work only — 54 files, +3,384 −467. Review #35726 (the toolset layer) and #35677 (the MCP adapters) first; this PR is the third consumer of that same layer. Merge order: #35726 → #35677 → this one; GitHub retargets automatically.

What I did

storybook tools — a public, unflagged CLI whose entire command surface is derived at runtime from the core toolsets, running in its own process, with no dev server. This is what npx storybook tools --help prints in a react-vite sandbox:

Usage: npx storybook tools [options] [toolset] [tool] [args...]

Storybook tools from the Storybook configuration at ./.storybook.

Options:
  --cwd <path>                 Project directory of the target Storybook
  -c, --config-dir <dir-name>  Storybook config directory of the target Storybook
  --input <object>             Raw JSON object with the tool arguments (escape hatch for complex values)
  --json                       Print the tool's structured result data as JSON instead of markdown
  -o, --output <path>          Write the result to a file instead of stdout
  -h, --help                   Show every tool of the target Storybook, or one tool with its arguments

Commands:
  stories preview            Get one or more Storybook preview URLs.  [requires running Storybook]
  stories changed            Get Storybook stories marked as new, modified, or related.  [local]
  stories find-by-component  Map component source files to the stories that render them…  [local]
  review create              Publish a curated review to Storybook's review page…  [requires running Storybook]
  docs list                  List all available UI components and documentation entries…  [local]
  docs show                  Get documentation for a UI component or docs entry.  [local]
  docs show-story            Get detailed documentation for a specific story variant…  [local]
  test run                   Run story tests.  [local]

Not one of these commands is defined in the CLI. It loads the target project's Storybook configuration (experimental_loadStorybook), which applies the services preset and registers every toolset in-process; commands, help text, schemas and summaries all come from getRegisteredToolsets() at runtime. Install addon-vitest and test run appears; edit a description in a toolset and the help changes. The CLI's own job is mechanical:

tokens → parse (--key value flags, JSON-coerced; --input; -o) → validate against the method's schema
       → handler(input, ctx) → { ok, data, markdown }
markdown → stdout    data → --json    ok → exit code    agentFacing error → verbatim, exit 1
requiresDevServer → look up the running instance in the runtime registry
                  → one uniform "start the dev server first" message when there is none

"Own process, no dev server" required solving three real problems, and each is its own commit:

  1. The module graph (stories changed, stories find-by-component): builder-vite implements its already-declared changeDetectionAdapter hook for the server-less case, and the CLI repeats the dev server's change-detection bootstrap against it.
  2. Story tests (test run): the responder answering test-run requests moves out of addon-vitest's dev-server-only channel hook onto the same services hook that registers the test toolset — every consumer that can offer the tool can now answer it.
  3. Review (review create) genuinely needs the dev server's state. It is the one exception: the CLI forwards it to the running Storybook's @storybook/addon-mcp endpoint — the same mechanism storybook ai uses — as an explicitly disposable stopgap until connect mode (Milestone 5b) exists.

(stories preview needs no exception: it only needs a live origin, which the runtime instance registry provides.)

Along the way, the modules both CLIs share (instance registry reader, instance matcher, MCP client, config-dir resolution, schema help renderer) move out of cli/ai/ into cli/tools/, because storybook ai is slated for removal and living code must not sit in a doomed tree. After this PR, deleting storybook ai is a pure deletion of its directory plus two registration lines — and a lint rule fails any future cli/tools → cli/ai import.

The one review question

Is the CLI a consumer with zero opinions — or does anything in cli/tools/ re-derive meaning that belongs in a toolset definition? If you find dispatch branching on domain data, help rendering prose a definition should own, or proxy-awareness leaking outside its one branch — that is the review finding to raise. Everything else should read as parsing, lookup, and mechanical mapping.

How to review (~1½ hours)

The branch's six commits are six blocks, in dependency order — review one commit at a time; each heading links to that commit's diff.

  1. The contract — 10 min
  2. The headless module graph — 10 min
  3. The storybook tools command — 40 min
  4. Local story tests — 15 min
  5. The relocation out of storybook ai — 10 min
  6. The review create proxy — 10 min

Block 1 · The contract — 10 min

Files: toolset-definition.ts, toolset-names.ts, the three definition annotations, telemetry types, core-server/load.ts (11 files, +76 −12)

Everything the branch builds on, in ~90 lines: the optional requiresDevServer trait on ToolsetMethod (declared by stories.preview and review.create; deliberately not by test.run); toCliMethodName as the single authority for how a method is spelled on the CLI (getRef renders cross-references through it, so prose and dispatch cannot disagree); the tools-command telemetry event; and experimental_loadStorybook honoring a caller-supplied channel.

What to check: is the trait on the right methods, and is one boolean enough to express "needs a dev server"? The channel parameter looks innocent but is load-bearing for block 4.

Block 2 · The headless module graph — 10 min

Files: builder-vite/src/change-detection-adapter/headless.ts (+ test), builder-vite/src/index.ts (3 files, +129 −10)

headless.ts is 38 lines: the same commonConfig + viteFinal assembly the dev server uses, resolved with Vite's server-less resolveConfig, and a no-op watcher. Non-Vite builders keep failing with the existing typed error.

What to check: the test pins the same three resolved-config fields as the server-bound adapter, so the two assemblies cannot drift silently.

Block 3 · The storybook tools command — 40 min

Files: cli/tools/{run,help,bootstrap,register,tool-tokens,discover-instance}.ts + tests + bin/ wiring (15 files, +1,975 −10)

The heart of the PR. Read run.ts (355 lines — the whole command behind the commander wiring) and help.ts (209 lines) in full. run.test.ts doubles as the behavior spec: tokens in, { exitCode, output } out, against the real core toolsets with stubbed dependencies — including a parity test asserting docs list prints byte-for-byte what an MCP client receives for the same call.

Two deliberate oddities, so they don't read as bugs:

  • The command holds a keep-alive interval while running: some awaited native work in the module-graph engine holds no libuv handle, and without a server socket the event loop can drain mid-await and exit 0 silently.
  • The action exits explicitly once the result is printed: the vitest child's IPC pipe would otherwise hold the event loop open forever.

What to check: the one review question, applied line by line. Telemetry: one tools-command event per invocation, help lookups excluded so they cannot skew success rates.

Block 4 · Local story tests — 15 min

Files: addons/vitest/src/node/test-run-responder.ts (+ test), preset.ts (+ test), AGENTS.md (5 files, +637 −185)

The responder was the only dev-server-bound piece of test run. It moves to node/test-run-responder.ts, wired from the same services hook that registers the test toolset, behind the same gate. Heavy machinery (fs cache, leader UniversalStore, vitest child) is created lazily on first request; the dev server's channel hook runs the same memoized setup eagerly and keeps its dev-only extras, so dev behavior is unchanged.

What to check: the gates — skipped inside the vitest child (a run can never recursively boot another child); non-Vite builders get an immediate error response instead of an unanswered request. And the channel invariant: the responder relays the vitest child's store events onto the channel its preset hook received — leader stores only hear the channel they were prepared on, so a second channel on either side hangs the run silently (that is what block 1's channel parameter prevents).

Block 5 · The relocation out of storybook ai — 10 min

Files: cli/tools/{instances/,mcp-client,config-dir,schema-lines}.ts + moved tests, the cli/ai/ import rewires, the lint rule (27 files, +392 −265)

Behavior-neutral by construction: git mv plus import rewrites, no logic changes. cli/ai/mcp/types.ts is deleted, split across the new homes; schema-lines.ts gains the unit tests it never had (its only coverage used to live in the ai CLI's suite — the tree slated for deletion).

What to check: skim for a logic change hiding among the moves (there are none); the new no-restricted-imports rule in code/.oxlintrc.json that fails any cli/tools → cli/ai import — dependency direction is dying-code-depends-on-living-code, never the reverse.

Block 6 · The review create proxy — 10 min

Files: run.ts, mcp-client.ts, run.test.ts, AGENTS.md (4 files, +193 −3)

One self-contained branch in the dispatch:

PROXY_VIA_MCP_METHODS = { 'review.create' }
discover instance → no MCP endpoint? → "add @storybook/addon-mcp" (nothing sent)
validate input locally → call the frozen `display-review` tool (trusted-local-client header)
reply → text to stdout · structuredContent for --json · isError → exit code

The handler runs exactly once, in the dev server, so its telemetry and side effects stay in the process that owns them. Milestone 5b deletes the set, the branch, and the injectable dependency wholesale.

What to check: disposability — nothing outside the branch may know a method is proxied. Five seam tests pin the contract (frozen name + args, --json, isError → exit 1, validation before any send, missing-endpoint guidance).

Known limitation, on purpose: the proxied handler executes server-side where ctx.consumer is mcp, so review create prints its MCP prose variant on the CLI (descriptions and --help render locally and are unaffected). Fixing it means mapping the header to a cli consumer in addon-mcp, which would also change what storybook ai and the Claude/Codex plugins receive — a separate call, and 5b removes the mismatch entirely. Noted on #35747.

What you do not need to review, and why that is safe

Claim Evidence
The CLI prints what MCP clients receive The docs list parity test asserts byte-equality with the handler markdown the MCP adapter renders verbatim
The command behaves as specced 35 tests at the run seam (help, dispatch, validation, trait intercepts, outcome mapping, the proxy) — 414 tests across the CLI area
Nothing else regressed Full unit suite green (9,269 passed, 867 files); TS 7 per-package checks across all 47 projects
It works on a real project Fresh react-vite sandbox QA: every command above exercised, test run 247/247 matching the manager UI, review create publishing end-to-end with the review page visually verified (including the pending-review → Update flow), unknown-ID refusal, missing-addon guidance, no-dev-server contract
storybook ai is unchanged Same engine, now importing the shared modules from their new home; smoke-tested live (ai display-review, ai list-all-documentation) behind its env flag

Out of scope (per #35747): connect mode (5b), deprecating storybook ai (a later PR — this PR only makes that deletion pure), proxying any method beyond review.create, mapping the trusted-local-client header to a cli consumer.

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

  1. yarn nx run-many -t compile, then in any project (a sandbox works): npx storybook tools — the Commands listing and full tool reference print from that project's configuration, with [local] / [requires running Storybook] badges.
  2. npx storybook tools docs list and npx storybook tools stories changed — both work with no dev server (docs from manifests, the module graph hosted in-process).
  3. npx storybook tools test run --stories '[{"storyId":"example-button--primary"}]' — addon-vitest boots its vitest child, the report prints, the process exits with the mapped code.
  4. Start storybook dev (with @storybook/addon-mcp): npx storybook tools review create --input '<review object>' publishes the review; the page renders it at /?path=/review/; --json prints { "reviewUrl": … }. Without the addon, the command names it and exits 1. Without a dev server, the uniform "start the dev server first" message.
  5. STORYBOOK_FEATURE_AI_CLI=true npx storybook ai list-all-documentation — the ai CLI still works identically on the relocated modules.

Documentation

  • Add or update documentation reflecting your changes (AGENTS.md; --help is the CLI's documentation until the surface stabilizes, per the spec's out-of-scope list)
  • If you are deprecating/removing a feature, make sure to update MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Declare whether manual QA will be needed for this PR during the next release, through qa:needed or qa:skip

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the @storybookjs/core team here.

core team members can create a canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=<PR_NUMBER>

@kasperpeulen kasperpeulen added feature request ci:normal Run our default set of CI jobs (choose this for most PRs). qa:needed Pull Requests that will need manual QA prior to release. labels Aug 3, 2026
@storybook-app-bot

storybook-app-bot Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Package Benchmarks

Commit: b537099, ran on 11 August 2026 at 11:45:23 UTC

The following packages have significant changes to their size or dependencies:

storybook

Before After Difference
Dependency count 73 73 0
Self size 21.77 MB 21.82 MB 🚨 +50 KB 🚨
Dependency size 31.24 MB 31.24 MB 0 B
Bundle Size Analyzer Link Link

@storybook/cli

Before After Difference
Dependency count 205 205 0
Self size 833 KB 833 KB 0 B
Dependency size 86.50 MB 86.55 MB 🚨 +50 KB 🚨
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 198 198 0
Self size 32 KB 32 KB 0 B
Dependency size 84.98 MB 85.03 MB 🚨 +50 KB 🚨
Bundle Size Analyzer Link Link

create-storybook

Before After Difference
Dependency count 74 74 0
Self size 1.09 MB 1.09 MB 🎉 -66 B 🎉
Dependency size 53.00 MB 53.05 MB 🚨 +50 KB 🚨
Bundle Size Analyzer node node

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The PR adds the public storybook tools CLI. It loads registered toolsets, parses arguments, handles dev-server and module-graph requirements, executes methods, reports telemetry, and supports headless Vite and in-process Vitest execution.

Changes

Storybook tools CLI

Layer / File(s) Summary
CLI contracts and argument parsing
code/core/src/shared/open-service/..., code/core/src/cli/tools/tool-tokens.ts, code/core/src/types/modules/core-common.ts
Adds tool method requirements, CLI naming, token parsing, telemetry types, schema exports, and headless builder options.
Runtime bootstrap and instance discovery
code/core/src/cli/tools/bootstrap.ts, code/core/src/cli/tools/discover-instance.ts, code/core/src/core-server/..., code/builders/builder-vite/src/...
Loads configuration, prepares headless stores, hosts the module graph, discovers instances, and adds headless Vite change detection.
Tool discovery and schema help
code/core/src/cli/tools/help.ts, code/core/src/cli/ai/mcp/run-tool.ts, code/core/package.json
Adds complete and method-specific help with CLI names, execution badges, and schema details.
Command dispatch and outcome mapping
code/core/src/cli/tools/run.ts, code/core/src/cli/tools/test-support/*, code/core/src/cli/tools/*.test.ts
Parses invocations, enforces runtime requirements, executes handlers, maps outcomes, and tests the CLI flow.
CLI registration and telemetry
code/core/src/cli/tools/register.ts, code/core/src/bin/*, code/core/src/shared/open-service/README.md, AGENTS.md
Registers storybook tools, routes it through the core binary, writes results, reports telemetry, and updates documentation.
In-process Vitest test execution
code/addons/vitest/src/node/*, code/addons/vitest/src/preset.*, code/core/src/shared/open-service/toolsets/test/definition.ts
Moves test-run handling into a shared responder and supports headless requests through a shared channel.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant StorybookDispatcher
  participant ToolsPassthrough
  participant runToolsCommand
  participant StorybookRuntime
  participant VitestResponder
  User->>StorybookDispatcher: invoke storybook tools
  StorybookDispatcher->>ToolsPassthrough: route tools command
  ToolsPassthrough->>runToolsCommand: pass toolset, method, and flags
  runToolsCommand->>StorybookRuntime: load configuration and resolve runtime
  StorybookRuntime-->>runToolsCommand: toolset context and optional origin
  runToolsCommand->>VitestResponder: dispatch test.run request
  VitestResponder-->>runToolsCommand: test result, error, or cancellation
  runToolsCommand-->>ToolsPassthrough: outcome, output, and exit code
  ToolsPassthrough-->>User: print stdout or write output file
Loading

Possibly related issues

Possibly related PRs

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 5

🧹 Nitpick comments (5)
code/core/src/cli/tools/bootstrap.ts (2)

99-105: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider surfacing why the builder adapter failed, not just at debug level.

The try/catch treats "builder doesn't implement changeDetectionAdapter" the same as "builder threw an unexpected error," and only records the real cause via logger.debug, which most users never see. If a builder throws because of a genuine misconfiguration (not because it lacks support), the resulting "module graph unavailable" message gives no hint why.

Consider logging at a more visible level (or attaching the caught error's message to the readiness reason) so a misconfigured project doesn't look identical to "builder doesn't support change detection."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/core/src/cli/tools/bootstrap.ts` around lines 99 - 105, Update the catch
handling around previewBuilder.changeDetectionAdapter in the bootstrap flow to
surface unexpected adapter-construction failures beyond debug logging, while
preserving the unsupported-builder behavior for absent adapters. Include the
caught error’s message or stack in a visible log or in the readiness reason
passed through resolveChangeDetectionAdapter so misconfiguration is
distinguishable from lack of support.

1-111: 🎯 Functional Correctness | 🔵 Trivial

Add direct unit tests for bootstrapToolsRuntime/hostModuleGraphInProcess.

run.test.ts only exercises runToolsCommand against a mocked bootstrap, so this file's own branching (hosting vs. not hosting the module graph, the workingDir value passed to ChangeDetectionService, and the deferred-adapter-must-always-settle invariant called out in the doc comment) has no direct test coverage. Given the doc comment's explicit warning that "any graph query would hang forever" if the deferred adapter never settles, a focused test asserting the resolution path in both branches would meaningfully reduce risk.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/core/src/cli/tools/bootstrap.ts` around lines 1 - 111, Add focused unit
tests for bootstrapToolsRuntime and hostModuleGraphInProcess, covering both
hostModuleGraph branches and asserting the deferred change-detection adapter is
resolved so readiness does not hang. Verify the hosted path constructs
ChangeDetectionService with process.cwd() as workingDir, while the non-hosted
path resolves the adapter with undefined. Mock the Storybook loading, builder,
preset, store, and readiness dependencies to isolate these flows.
code/builders/builder-vite/src/change-detection-adapter/headless.test.ts (1)

10-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Align mocking with the project's Vitest guidelines.

This file mocks vite and ../vite-config.ts without spy: true (Lines 15-16), accesses the mocks through raw vi.hoisted() references instead of vi.mocked(), and sets mock return values (mockResolvedValue) inside individual it() bodies (Lines 31-35, 49-50) instead of a shared beforeEach.

Move the mock setup into a beforeEach block, and use vi.mocked() to access the mocked functions with type safety.

As per coding guidelines: "Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests," "Implement mock behaviors in beforeEach blocks in Vitest tests," and "Use vi.mocked() to type and access the mocked functions in Vitest tests."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/builders/builder-vite/src/change-detection-adapter/headless.test.ts`
around lines 10 - 63, Align the Vitest setup in
createHeadlessViteChangeDetectionAdapter tests with project conventions: add
spy: true to the vite and vite-config mocks, access resolveConfig and
commonConfig through vi.mocked(), and move their shared mockResolvedValue setup
into beforeEach. Keep each test focused on assertions, retaining only
test-specific setup such as createOptions and apply expectations.

Source: Coding guidelines

code/core/src/cli/tools/register.ts (1)

167-172: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Drop the issue-number references from comments in both files. Both comments cite a GitHub issue number as a provenance claim. As per coding guidelines for **/*.{ts,tsx}, comments must carry maintenance-relevant rationale, not ticket codes: "not investigation transcripts, ticket codes, acceptance-criteria codes, provenance claims, or cross-file line references."

  • code/core/src/cli/tools/register.ts#L167-L172: keep "modeled on the ai-command event" and remove (storybookjs/storybook#35131).
  • code/core/src/bin/core.ts#L279-L280: keep the "disconnected from any dev server" rationale and remove (storybookjs/storybook#35716).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/core/src/cli/tools/register.ts` around lines 167 - 172, Remove the
GitHub issue reference from the comment around the tools-command event in
code/core/src/cli/tools/register.ts lines 167-172, preserving the rationale that
it is modeled on the ai-command event. Also remove the GitHub issue reference
from the comment in code/core/src/bin/core.ts lines 279-280, preserving the
rationale about being disconnected from any dev server.

Source: Coding guidelines

code/core/src/cli/tools/run.ts (1)

68-82: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add method-level traits for CLI requirements.

MODULE_GRAPH_METHODS and ORIGIN_ONLY_METHODS duplicate metadata that is absent from ToolsetMethod. Add explicit traits and derive these dispatch decisions from each method definition. Otherwise, new methods can use core/module-graph or require only an origin without updating code/core/src/cli/tools/run.ts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/core/src/cli/tools/run.ts` around lines 68 - 82, Extend ToolsetMethod
with explicit CLI requirement traits for module-graph and origin-only behavior,
then remove the hardcoded MODULE_GRAPH_METHODS and ORIGIN_ONLY_METHODS sets.
Update the dispatch logic in the CLI runner to derive these decisions from each
method definition’s traits, ensuring newly added core/module-graph or
origin-only methods are handled automatically.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@code/core/src/cli/tools/bootstrap.ts`:
- Around line 47-78: Thread the resolved cwd from bootstrapToolsRuntime into
hostModuleGraphInProcess instead of relying on process.cwd(). Update the
ChangeDetectionService construction within hostModuleGraphInProcess to use that
passed cwd as workingDir, while preserving the existing target.cwd fallback
resolution.

In `@code/core/src/cli/tools/help.ts`:
- Around line 115-119: Update toJsonSchemaObject to call toJsonSchema with
errorMode: 'throw' so unsupported schemas enter the existing catch path and
return undefined, allowing methodBodyLines to print the unavailable-schema
message instead of treating them as having no arguments.

In `@code/core/src/cli/tools/run.test.ts`:
- Around line 68-81: Update makeDeps so a caller-supplied bootstrap override is
preserved, matching discoverInstance’s override behavior. Resolve
overrides.bootstrap before creating the default mock and ensure the returned
deps and bootstrap reference the caller’s implementation when provided.

In `@code/core/src/cli/tools/tool-tokens.ts`:
- Around line 103-109: Update the `key === 'output'` handling in the token
parser to reject both undefined and empty-string values with the existing
“`--output` requires a file path.” error, while preserving valid paths. Add a
focused test beside the existing `-o`/`--output` tests covering `--output=` and
asserting the parser returns the expected failure.

In `@code/core/src/shared/open-service/toolsets/review/definition.ts`:
- Around line 147-148: Add review.create to the ORIGIN_ONLY_METHODS allowlist
used by runToolsCommand, alongside stories.preview, so its requiresDevServer
requirement does not produce attach-unavailable. Preserve the existing handling
for other methods.

---

Nitpick comments:
In `@code/builders/builder-vite/src/change-detection-adapter/headless.test.ts`:
- Around line 10-63: Align the Vitest setup in
createHeadlessViteChangeDetectionAdapter tests with project conventions: add
spy: true to the vite and vite-config mocks, access resolveConfig and
commonConfig through vi.mocked(), and move their shared mockResolvedValue setup
into beforeEach. Keep each test focused on assertions, retaining only
test-specific setup such as createOptions and apply expectations.

In `@code/core/src/cli/tools/bootstrap.ts`:
- Around line 99-105: Update the catch handling around
previewBuilder.changeDetectionAdapter in the bootstrap flow to surface
unexpected adapter-construction failures beyond debug logging, while preserving
the unsupported-builder behavior for absent adapters. Include the caught error’s
message or stack in a visible log or in the readiness reason passed through
resolveChangeDetectionAdapter so misconfiguration is distinguishable from lack
of support.
- Around line 1-111: Add focused unit tests for bootstrapToolsRuntime and
hostModuleGraphInProcess, covering both hostModuleGraph branches and asserting
the deferred change-detection adapter is resolved so readiness does not hang.
Verify the hosted path constructs ChangeDetectionService with process.cwd() as
workingDir, while the non-hosted path resolves the adapter with undefined. Mock
the Storybook loading, builder, preset, store, and readiness dependencies to
isolate these flows.

In `@code/core/src/cli/tools/register.ts`:
- Around line 167-172: Remove the GitHub issue reference from the comment around
the tools-command event in code/core/src/cli/tools/register.ts lines 167-172,
preserving the rationale that it is modeled on the ai-command event. Also remove
the GitHub issue reference from the comment in code/core/src/bin/core.ts lines
279-280, preserving the rationale about being disconnected from any dev server.

In `@code/core/src/cli/tools/run.ts`:
- Around line 68-82: Extend ToolsetMethod with explicit CLI requirement traits
for module-graph and origin-only behavior, then remove the hardcoded
MODULE_GRAPH_METHODS and ORIGIN_ONLY_METHODS sets. Update the dispatch logic in
the CLI runner to derive these decisions from each method definition’s traits,
ensuring newly added core/module-graph or origin-only methods are handled
automatically.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 20603d34-543b-49a4-9ab1-26c23b46674d

📥 Commits

Reviewing files that changed from the base of the PR and between 1ee3380 and fb04ad2.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (26)
  • AGENTS.md
  • code/builders/builder-vite/src/change-detection-adapter/headless.test.ts
  • code/builders/builder-vite/src/change-detection-adapter/headless.ts
  • code/builders/builder-vite/src/index.ts
  • code/core/package.json
  • code/core/src/bin/core.ts
  • code/core/src/bin/dispatcher.ts
  • code/core/src/cli/ai/mcp/run-tool.ts
  • code/core/src/cli/tools/bootstrap.ts
  • code/core/src/cli/tools/discover-instance.ts
  • code/core/src/cli/tools/help.ts
  • code/core/src/cli/tools/register.ts
  • code/core/src/cli/tools/run.test.ts
  • code/core/src/cli/tools/run.ts
  • code/core/src/cli/tools/test-support/register-core-toolsets.ts
  • code/core/src/cli/tools/tool-tokens.test.ts
  • code/core/src/cli/tools/tool-tokens.ts
  • code/core/src/core-server/index.ts
  • code/core/src/shared/open-service/README.md
  • code/core/src/shared/open-service/toolset-definition.ts
  • code/core/src/shared/open-service/toolset-names.ts
  • code/core/src/shared/open-service/toolsets/review/definition.ts
  • code/core/src/shared/open-service/toolsets/stories/definition.ts
  • code/core/src/shared/open-service/toolsets/test/definition.ts
  • code/core/src/telemetry/types.ts
  • code/core/src/types/modules/core-common.ts

Comment thread code/core/src/cli/tools/bootstrap.ts
Comment thread code/core/src/cli/tools/help.ts
Comment thread code/core/src/cli/tools/run.test.ts
Comment thread code/core/src/cli/tools/tool-tokens.ts
Comment thread code/core/src/shared/open-service/toolsets/review/definition.ts

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🧹 Nitpick comments (3)
code/addons/vitest/src/node/test-run-responder.ts (1)

42-52: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Reset the memo when setup fails, so a later request can retry.

storePromise ??= also caches a rejected promise. If the first call to createTestRunnerStore fails, for example because story-index generation throws, every later request receives the same "Failed to set up the test runner" error for the lifetime of the process. The dev server shares this memo through ensureTestRunnerStore in preset.ts, so a transient startup failure disables the responder permanently.

♻️ Proposed fix
 export const ensureTestRunnerStore = ({ channel, options }: ResponderOptions): Promise<Store> =>
-  (storePromise ??= createTestRunnerStore({ channel, options }));
+  (storePromise ??= createTestRunnerStore({ channel, options }).catch((error) => {
+    // Allow a later request to retry instead of replaying the cached rejection forever.
+    storePromise = undefined;
+    throw error;
+  }));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/addons/vitest/src/node/test-run-responder.ts` around lines 42 - 52,
Update ensureTestRunnerStore so a rejected createTestRunnerStore promise clears
storePromise before propagating the setup error, allowing subsequent requests to
retry while preserving successful store memoization for the responder and dev
server.
code/addons/vitest/src/node/test-run-responder.test.ts (1)

15-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Align the module mocks with the repository spy-mocking rules.

The four vi.mock() calls use plain factories. The guidelines require the spy: true option and mock behaviors defined in beforeEach with vi.mocked(). For ./boot-test-runner.ts and ../logger.ts the migration is mechanical, because spy mode keeps the real module shape and only needs the behavior assigned per test.

♻️ Example for `./boot-test-runner.ts`
-vi.mock('./boot-test-runner.ts', () => ({
-  runTestRunner: vi.fn(),
-}));
+vi.mock('./boot-test-runner.ts', { spy: true });
 beforeEach(() => {
   vi.clearAllMocks();
+  vi.mocked(runTestRunner).mockResolvedValue(undefined);
 });

As per coding guidelines: "Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests" and "Implement mock behaviors in beforeEach blocks".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/addons/vitest/src/node/test-run-responder.test.ts` around lines 15 - 46,
Update all four vi.mock calls in the test setup to use spy mode, preserving the
real module shapes instead of supplying plain factory replacements. Move the
mock behaviors for experimental_UniversalStore.create,
experimental_getTestProviderStore, createFileSystemCache,
loadPreviewOrConfigFile, runTestRunner, and log into beforeEach blocks using
vi.mocked(), while retaining the existing per-test behavior.

Source: Coding guidelines

code/core/src/cli/tools/bootstrap.test.ts (1)

14-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move mock return values into beforeEach.

Lines 26-28 set mockReturnValue/mockResolvedValue inside the it block. The coding guidelines for Vitest tests require implementing mock behaviors in beforeEach blocks and avoiding inline mock implementations within test cases.

Move the channel constant and the two mock setups into beforeEach, then reference channel from outer scope in the assertion.

As per coding guidelines, "Implement mock behaviors in beforeEach blocks in Vitest tests" and "Avoid inline mock implementations within test cases in Vitest tests."

♻️ Proposed refactor
+let channel: Channel;
+
 beforeEach(() => {
   vi.mocked(prepareHeadlessUniversalStores).mockReset();
   vi.mocked(experimental_loadStorybook).mockReset();
   vi.mocked(resolveChangeDetectionAdapter).mockImplementation(() => {});
+  channel = { isPreparedChannel: true } as unknown as Channel;
+  vi.mocked(prepareHeadlessUniversalStores).mockReturnValue(channel);
+  vi.mocked(experimental_loadStorybook).mockResolvedValue({} as never);
 });

 describe('bootstrapToolsRuntime', () => {
   it('loads the configuration on the same channel the stores were prepared on', async () => {
-    const channel = { isPreparedChannel: true } as unknown as Channel;
-    vi.mocked(prepareHeadlessUniversalStores).mockReturnValue(channel);
-    vi.mocked(experimental_loadStorybook).mockResolvedValue({} as never);
-
     await bootstrapToolsRuntime(
       { cwd: process.cwd(), configDir: '.storybook' },
       { hostModuleGraph: false }
     );

     expect(experimental_loadStorybook).toHaveBeenCalledWith(expect.objectContaining({ channel }));
   });
 });

Also applies to: 26-28

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/core/src/cli/tools/bootstrap.test.ts` around lines 14 - 18, Move the
channel fixture and the mockReturnValue/mockResolvedValue setups from the
affected test into the existing beforeEach block, keeping the mocks reset and
configured before each test. Declare channel in the surrounding scope so the
test assertion can reference it without defining mock behavior inline.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@code/addons/vitest/src/node/test-run-responder.ts`:
- Around line 231-272: Add a module-level in-flight request marker around the
run-start handling to prevent concurrent handlers from both passing the
startedAt guard; reject subsequent requests as already running, set the marker
before dispatching TRIGGER_RUN, and clear it in each terminal
TEST_RUN_COMPLETED, FATAL_ERROR, and CANCEL_RUN branch alongside unsubscribe().
- Around line 99-113: Update the TRIGGER_RUN subscription’s runTestRunner
invocation to consume rejected promises by attaching a rejection handler, while
avoiding any additional FATAL_ERROR dispatch because bootTestRunner already
reports startup failures. Preserve the existing runner arguments and state-reset
behavior.

In `@code/core/src/core-server/load.ts`:
- Around line 41-43: Update the returned Options object in the load flow to use
the resolved channel variable declared as channel, rather than preserving
options.channel. Ensure preset callbacks and callers receive this initialized
channel when no caller-supplied channel exists.

---

Nitpick comments:
In `@code/addons/vitest/src/node/test-run-responder.test.ts`:
- Around line 15-46: Update all four vi.mock calls in the test setup to use spy
mode, preserving the real module shapes instead of supplying plain factory
replacements. Move the mock behaviors for experimental_UniversalStore.create,
experimental_getTestProviderStore, createFileSystemCache,
loadPreviewOrConfigFile, runTestRunner, and log into beforeEach blocks using
vi.mocked(), while retaining the existing per-test behavior.

In `@code/addons/vitest/src/node/test-run-responder.ts`:
- Around line 42-52: Update ensureTestRunnerStore so a rejected
createTestRunnerStore promise clears storePromise before propagating the setup
error, allowing subsequent requests to retry while preserving successful store
memoization for the responder and dev server.

In `@code/core/src/cli/tools/bootstrap.test.ts`:
- Around line 14-18: Move the channel fixture and the
mockReturnValue/mockResolvedValue setups from the affected test into the
existing beforeEach block, keeping the mocks reset and configured before each
test. Declare channel in the surrounding scope so the test assertion can
reference it without defining mock behavior inline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0fb9297a-0f7e-47dc-8693-af375e46e8e3

📥 Commits

Reviewing files that changed from the base of the PR and between 2036381 and efd0ba9.

📒 Files selected for processing (12)
  • AGENTS.md
  • code/addons/vitest/src/node/test-run-responder.test.ts
  • code/addons/vitest/src/node/test-run-responder.ts
  • code/addons/vitest/src/preset.test.ts
  • code/addons/vitest/src/preset.ts
  • code/core/src/cli/tools/bootstrap.test.ts
  • code/core/src/cli/tools/bootstrap.ts
  • code/core/src/cli/tools/register.ts
  • code/core/src/cli/tools/run.test.ts
  • code/core/src/core-server/load.ts
  • code/core/src/core-server/utils/get-server-channel.ts
  • code/core/src/shared/open-service/toolsets/test/definition.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • code/core/src/core-server/utils/get-server-channel.ts
  • AGENTS.md
  • code/core/src/cli/tools/bootstrap.ts

Comment thread code/addons/vitest/src/node/test-run-responder.ts
Comment thread code/addons/vitest/src/node/test-run-responder.ts
Comment thread code/core/src/core-server/load.ts

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
code/addons/vitest/src/node/test-run-responder.test.ts (1)

240-246: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the preset mock behavior into beforeEach.

Line 240 installs a mock implementation inside the test case. Configure this implementation in beforeEach, then let the test select the failure state. This keeps mock setup consistent and isolated.

As per coding guidelines, “Implement mock behaviors in beforeEach blocks in Vitest tests.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/addons/vitest/src/node/test-run-responder.test.ts` around lines 240 -
246, Move the options.presets.apply mockImplementation setup from the individual
test into its surrounding beforeEach block, while preserving the
storyIndexGenerator failure behavior. Let each test configure failSetup to
select whether the mocked setup throws, and keep the mock reset and defaultApply
behavior isolated per test.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@code/addons/vitest/src/node/test-run-responder.test.ts`:
- Around line 240-246: Move the options.presets.apply mockImplementation setup
from the individual test into its surrounding beforeEach block, while preserving
the storyIndexGenerator failure behavior. Let each test configure failSetup to
select whether the mocked setup throws, and keep the mock reset and defaultApply
behavior isolated per test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 93bf9ee0-44d6-4be1-9a3e-201862795e0d

📥 Commits

Reviewing files that changed from the base of the PR and between efd0ba9 and 890bd91.

📒 Files selected for processing (3)
  • code/addons/vitest/src/node/test-run-responder.test.ts
  • code/addons/vitest/src/node/test-run-responder.ts
  • code/core/src/core-server/load.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • code/addons/vitest/src/node/test-run-responder.ts
  • code/core/src/core-server/load.ts

@kasperpeulen kasperpeulen changed the title CLI: Add the public storybook tools command derived at runtime from the OSA toolsets Skills M5a: CLI: Add the public storybook tools command derived at runtime from the OSA toolsets Aug 4, 2026
@kasperpeulen
kasperpeulen force-pushed the m4-addon-mcp-core-services branch from b1a8dee to 28d2853 Compare August 4, 2026 11:54
@kasperpeulen kasperpeulen mentioned this pull request Aug 4, 2026
@ghengeveld ghengeveld mentioned this pull request Aug 4, 2026
2 of 9 tasks
@JReinhold JReinhold self-assigned this Aug 9, 2026
Comment thread code/core/src/shared/open-service/toolsets/test/definition.ts
Comment thread code/core/src/shared/open-service/toolset-names.ts
Comment thread code/core/src/cli/tools/bootstrap.ts
Comment thread code/core/src/cli/tools/discover-instance.ts
Comment thread code/core/src/cli/tools/help.ts
Comment thread code/core/src/cli/tools/help.ts
Comment thread code/core/src/cli/tools/run.ts
Comment thread AGENTS.md
@JReinhold
JReinhold force-pushed the m4-addon-mcp-core-services branch from 28d2853 to 1da8a9f Compare August 10, 2026 11:23
…server plumbing

What the rest of the branch builds on, all of it small and declarative:

- `ToolsetMethod` gains the optional `requiresDevServer` trait; `stories.preview`
  and `review.create` declare it (with their rationale), `test.run` explicitly
  does not. The CLI derives its one uniform dev-server contract from this trait
  alone.
- `toolset-names.ts` exports `toCliMethodName` — the single authority for how a
  method is spelled on the CLI — and `getRef` renders CLI cross-references
  through it, so command names and description prose cannot disagree.
- The `tools-command` telemetry event type, modeled on `ai-command`.
- Core-server plumbing the CLI's own-process bootstrap needs:
  `experimental_loadStorybook` honors a caller-supplied channel, and
  `prepareHeadlessUniversalStores` returns the channel it prepared the
  UniversalStore singleton on. One bus from store preparation to presets is
  load-bearing for local test runs (review 4/6).
builder-vite implements its already-declared `changeDetectionAdapter` hook for
the server-less case: the same `commonConfig` + `viteFinal` assembly the dev
server uses, resolved with Vite's server-less `resolveConfig`, and a no-op
watcher. This is what lets the CLI host the module graph in its own process, so
`stories changed` and `stories find-by-component` work with no dev server on
Vite projects. Non-Vite builders keep failing with the existing typed
`OpenServiceModuleGraphUnavailableError`.

The test pins the same three resolve-config fields the server-bound adapter is
pinned on, so the two assemblies cannot drift apart silently.
The public, unflagged CLI, running the OSA core toolsets in its own process,
fully disconnected from any dev server:

- `run.ts` is the whole command behind the commander wiring: it derives the
  surface from `getRegisteredToolsets()` at runtime, parses `--key value`
  flags (JSON-coerced, `--input` escape hatch, `-o/--output`), validates
  against the method's schema, and maps `ToolsetOutcome` mechanically —
  markdown to stdout, `--json` prints `data`, `ok` drives the exit code,
  agent-facing errors surface verbatim. The `requiresDevServer` trait renders
  as one uniform contract; `stories preview` resolves its origin from the
  runtime instance registry.
- `bootstrap.ts` loads the target Storybook configuration (registering every
  service and toolset via the `services` hook) and hosts the module graph for
  the graph-bound methods by repeating the dev server's change-detection
  bootstrap against the review-2/6 adapter.
- `help.ts` renders commander's conventional shape — Usage, Options, a
  Commands listing with per-command summaries and execution badges — followed
  by the full per-tool reference derived from live descriptions and schemas
  (valibot to JSON Schema, reusing the ai CLI's schema-line renderer, exported
  rather than copied).
- `register.ts` + `bin/` wire `tools` into the program and dispatcher, with
  one `tools-command` telemetry event per invocation and an explicit exit once
  the result is printed (the vitest child's IPC pipe would otherwise hold the
  event loop open forever).

The primary test seam is the run function: tokens in, `{ exitCode, output }`
out, against the real core toolsets with stubbed dependencies.
…vices hook

`storybook tools test run` runs story tests end to end with no Storybook
running, so `test.run` carries no `requiresDevServer` trait and shows as
`[local]` in help.

The only dev-server-bound piece was the responder answering
`TRIGGER_TEST_RUN_REQUEST` over the channel, which lived exclusively in
addon-vitest's `experimental_serverChannel` hook. It moves to
`node/test-run-responder.ts` and is wired from the same `services` hook that
registers the `test` toolset, behind the same gate — every consumer that can
offer the tool can also answer it. The heavy machinery (fs cache, leader
UniversalStore seeded with the story index, vitest child boot) is created
lazily on first request; the dev server's channel hook runs the same memoized
setup eagerly and keeps its dev-only extras, so dev behavior is unchanged.

Gates: skipped inside the vitest child process (a run can never recursively
boot another child); non-Vite builders get an immediate error response instead
of an unanswered request.

Includes the AGENTS.md section documenting the CLI-and-toolsets end state.
…e tools CLI

The tools CLI reached into `cli/ai/mcp/` for instance discovery, config-dir
resolution and the schema help renderer, so the surviving code lived inside
the tree that is slated for deletion. Relocate everything both CLIs need into
`cli/tools/` and have `storybook ai` import it from there, so removing the ai
CLI later is a pure deletion of its directory plus its registration lines.

  ai/mcp/registry.ts         -> tools/instances/registry.ts
  ai/mcp/resolve-instance.ts -> tools/instances/resolve.ts
  ai/mcp/types.ts            -> split: instance records to tools/instances/types.ts,
                                MCP wire shapes into tools/mcp-client.ts,
                                JSON Schema nodes to tools/schema-lines.ts
  ai/mcp/client.ts           -> tools/mcp-client.ts
  resolveStorybookConfigDir  -> tools/config-dir.ts
  schemaLines                -> tools/schema-lines.ts
  CommandFailureHandler      -> tools/register.ts

Tests move with their modules. Behavior-neutral: no runtime change on either
CLI, and no `cli/tools` -> `cli/ai` import remains.
…er's MCP endpoint

`review create` needs the running dev server's review state, which the tools
CLI has no way to reach until connect mode (Milestone 5b). Until then it was
the one tool in the surface that could not work at all.

Forward it to that Storybook's `@storybook/addon-mcp` endpoint instead: the
dispatch discovers the instance, validates the arguments locally against the
method schema, calls the frozen `display-review` tool, and unwraps the reply
mechanically — text to stdout, `structuredContent` for `--json`, `isError` to
the exit code. The handler still runs exactly once, in the dev server, so its
telemetry and side effects stay in the process that owns them.

A running Storybook without the addon gets its own message naming the addon to
install, rather than the generic cannot-attach one; nothing is sent until the
input validates.

Deliberately disposable: 5b deletes `PROXY_VIA_MCP_METHODS`, its dispatch
branch and the injectable dependency, so nothing outside that branch knows a
method is proxied.

One consequence worth knowing: the handler executes server-side, where
`ctx.consumer` is `mcp`, so a proxied method renders its MCP prose variant
even on the CLI. Descriptions and `--help` are unaffected — those render
locally with the CLI consumer.
@JReinhold
JReinhold force-pushed the m4-addon-mcp-core-services branch from 1da8a9f to 0a5ad88 Compare August 11, 2026 11:34
Base automatically changed from m4-addon-mcp-core-services to m2b-core-toolsets August 12, 2026 11:16
@JReinhold

Copy link
Copy Markdown
Contributor

Closing to restore stack base onto m4-addon-mcp-core-services after #35677 was accidentally marked merged by a merges-API probe. Replacement PR incoming; same head branch m5a-tools-cli-osa.

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

Labels

ci:normal Run our default set of CI jobs (choose this for most PRs). feature request qa:needed Pull Requests that will need manual QA prior to release.

Projects

None yet

2 participants