@octanejs/mcp-server 0.2.16 → 0.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -121,7 +121,7 @@ Returns a skill by name. Bundled skills (shipped with this package):
121
121
  - `setup-ssr`: server rendering and hydration setup.
122
122
 
123
123
  When running inside the octane monorepo, the skills from `.rulesync/skills` are
124
- also available: `authoring-tsrx`, `react-library-port`, `bug-hunter`,
124
+ also available: `authoring-tsrx`, `octane-react-library-port`, `bug-hunter`,
125
125
  `create-a-pr`, `handle-issue`, `octane-core-extend`, `triage`,
126
126
  `performance-audit`. A test compares this map against the directory in both
127
127
  directions, so a new skill cannot stay unreachable here. This tool reads the RuleSync source, and
@@ -162,8 +162,8 @@ one manifest suite by name (`js-framework`, `todomvc`, `weather-app`,
162
162
  `hydration-interactivity`, `hydration-stress`, `lifecycle-memory`,
163
163
  `controlled-form`, `external-store-fanout`, `external-store-integrations`,
164
164
  `scheduler-responsiveness`, `suspense-recovery`, `event-delegation`,
165
- `application-composition`, `scaling-curves`, `streaming-ssr`,
166
- `streaming-backpressure`, `compiler-throughput`, `codegen-size`,
165
+ `application-composition`, `scaling-curves`, `activity`, `streaming-ssr`,
166
+ `streaming-backpressure`, `compiler-throughput`, `codegen-size`, `hook-memo`,
167
167
  `bundle-size`, `bundle-reachability`, `three-renderer`, `three-bundle-size`, …)
168
168
  or every suite with `all`; `quick` selects the reduced-iteration smoke pass. The
169
169
  suite list mirrors the runner manifest and `node benchmarks/bench.mjs --list`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octanejs/mcp-server",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22.22.2"
@@ -45,6 +45,7 @@ of bridging by hand:
45
45
  | `@reduxjs/toolkit` | `@octanejs/redux-toolkit` |
46
46
  | `@testing-library/react` | `@octanejs/testing-library` |
47
47
  | `@mdx-js/react` | `@octanejs/mdx` |
48
+ | `ink` | `@octanejs/ink` |
48
49
 
49
50
  The `octane_bindings` tool returns the same map machine-readably. For anything
50
51
  else, run the `octane_bridge_react_package` tool to get a scan of the
package/src/bridge.js CHANGED
@@ -18,6 +18,7 @@ export const KNOWN_BINDINGS = {
18
18
  '@apollo/client': '@octanejs/apollo-client',
19
19
  '@solana/react': '@octanejs/solana-react',
20
20
  '@tanstack/ai-react': '@octanejs/tanstack-ai',
21
+ '@tanstack/react-db': '@octanejs/tanstack-db',
21
22
  '@tanstack/react-devtools': '@octanejs/tanstack-devtools',
22
23
  '@tanstack/react-form': '@octanejs/tanstack-form',
23
24
  '@tanstack/react-query': '@octanejs/tanstack-query',
@@ -145,6 +146,7 @@ export const KNOWN_BINDINGS = {
145
146
  '@testing-library/react': '@octanejs/testing-library',
146
147
  'react-i18next': '@octanejs/i18next',
147
148
  '@inertiajs/react': '@octanejs/inertia',
149
+ ink: '@octanejs/ink',
148
150
  '@mdx-js/react': '@octanejs/mdx',
149
151
  'dexie-react-hooks': '@octanejs/dexie',
150
152
  '@livestore/react': '@octanejs/livestore',
@@ -171,6 +173,7 @@ export const KNOWN_BINDING_PACKAGE_DIRS = new Set(
171
173
  export const KNOWN_VANILLA_CORES = {
172
174
  '@apollo/client': '@apollo/client',
173
175
  '@solana/react': '@solana/kit',
176
+ '@tanstack/react-db': '@tanstack/db',
174
177
  '@tanstack/react-query': '@tanstack/query-core',
175
178
  wagmi: '@wagmi/core',
176
179
  '@tanstack/react-table': '@tanstack/table-core',
@@ -261,11 +264,11 @@ export const REACT_API_MAP = {
261
264
  note: "Supported. Accepts React's { default } module shape and additionally a bare component from the loader; wrapping Suspense or ViewTransition in lazy() is valid (nested lazy wrappers are not).",
262
265
  },
263
266
  Component: {
264
- status: 'unsupported',
267
+ status: 'rewrite',
265
268
  note: 'No class components. Rewrite as a function component.',
266
269
  },
267
270
  PureComponent: {
268
- status: 'unsupported',
271
+ status: 'rewrite',
269
272
  note: 'No class components. Rewrite as a function component with memo.',
270
273
  },
271
274
  StrictMode: {
@@ -274,7 +277,7 @@ export const REACT_API_MAP = {
274
277
  },
275
278
  Profiler: { status: 'unsupported', note: 'Not present.' },
276
279
  SuspenseList: { status: 'unsupported', note: 'Not present.' },
277
- findDOMNode: { status: 'unsupported', note: 'Removed in React 19 too. Use refs.' },
280
+ findDOMNode: { status: 'rewrite', note: 'Removed in React 19 too. Use refs.' },
278
281
  renderToString: {
279
282
  status: 'rewrite',
280
283
  note: 'Use renderToString() from octane/server (sync) or prerender() from octane/static (async, awaits Suspense); both return { html, css }.',
@@ -493,8 +496,8 @@ function apiRows(totals) {
493
496
  }
494
497
 
495
498
  function verdictFor(rows, classComponents) {
496
- if (classComponents || rows.some((row) => row.status === 'unsupported')) return 'needs-rework';
497
- if (rows.some((row) => row.status === 'rewrite' || row.status === 'partial')) {
499
+ if (rows.some((row) => row.status === 'unsupported')) return 'needs-rework';
500
+ if (classComponents || rows.some((row) => row.status === 'rewrite' || row.status === 'partial')) {
498
501
  return 'bridgeable-with-rewrites';
499
502
  }
500
503
  return 'bridgeable';
@@ -166,7 +166,7 @@ describe('bridgeReport', () => {
166
166
  expect(report.plan.join('\n')).toContain('octane/server');
167
167
  });
168
168
 
169
- it('reports class components as needs-rework', async () => {
169
+ it('reports class components as bridgeable with mandatory rewrites', async () => {
170
170
  const root = await mkdtemp(join(tmpdir(), 'octane-bridge-'));
171
171
  await writeFakePackage(root, 'classy', {
172
172
  'index.js': `
@@ -176,6 +176,20 @@ describe('bridgeReport', () => {
176
176
  });
177
177
  const report = await bridgeReport({ packageName: 'classy', projectRoot: root });
178
178
  expect(report.classComponents).toBe(true);
179
+ expect(report.apis.find((row) => row.name === 'Component').status).toBe('rewrite');
180
+ expect(report.verdict).toBe('bridgeable-with-rewrites');
181
+ });
182
+
183
+ it('reserves needs-rework for a public React API with no Octane rewrite', async () => {
184
+ const root = await mkdtemp(join(tmpdir(), 'octane-bridge-'));
185
+ await writeFakePackage(root, 'profiler-lib', {
186
+ 'index.js': `
187
+ import { Profiler } from 'react';
188
+ export { Profiler };
189
+ `,
190
+ });
191
+ const report = await bridgeReport({ packageName: 'profiler-lib', projectRoot: root });
192
+ expect(report.apis.find((row) => row.name === 'Profiler').status).toBe('unsupported');
179
193
  expect(report.verdict).toBe('needs-rework');
180
194
  });
181
195
 
@@ -234,13 +248,13 @@ describe('bridgeReportFromSource', () => {
234
248
  expect(report.plan.join('\n')).toContain('forwardRef');
235
249
  });
236
250
 
237
- it('reports class components as needs-rework', () => {
251
+ it('reports class components as bridgeable with mandatory rewrites', () => {
238
252
  const report = bridgeReportFromSource(`
239
253
  import React from 'react';
240
254
  export class Panel extends React.Component { render() { return null; } }
241
255
  `);
242
256
  expect(report.classComponents).toBe(true);
243
- expect(report.verdict).toBe('needs-rework');
257
+ expect(report.verdict).toBe('bridgeable-with-rewrites');
244
258
  expect(report.plan.join('\n')).toContain('function component');
245
259
  });
246
260
 
@@ -288,6 +302,11 @@ describe('KNOWN_BINDINGS', () => {
288
302
  expect(KNOWN_BINDINGS['react-window']).toBe('@octanejs/window');
289
303
  });
290
304
 
305
+ it('maps TanStack React DB to the Octane binding and framework-neutral core', () => {
306
+ expect(KNOWN_BINDINGS['@tanstack/react-db']).toBe('@octanejs/tanstack-db');
307
+ expect(KNOWN_VANILLA_CORES['@tanstack/react-db']).toBe('@tanstack/db');
308
+ });
309
+
291
310
  it('maps Streamdown and every official plugin package to the consolidated binding', () => {
292
311
  const upstreamPackages = [
293
312
  'streamdown',
package/src/index.js CHANGED
@@ -32,7 +32,9 @@ export const REPO_SKILLS = {
32
32
  'handle-issue': '.rulesync/skills/handle-issue/SKILL.md',
33
33
  'octane-core-extend': '.rulesync/skills/octane-core-extend/SKILL.md',
34
34
  'performance-audit': '.rulesync/skills/performance-audit/SKILL.md',
35
+ // Temporary compatibility alias for callers using the pre-rename skill name.
35
36
  'react-library-port': '.rulesync/skills/react-library-port/SKILL.md',
37
+ 'octane-react-library-port': '.rulesync/skills/octane-react-library-port/SKILL.md',
36
38
  triage: '.rulesync/skills/triage/SKILL.md',
37
39
  };
38
40
 
@@ -65,6 +67,8 @@ export const BENCHMARK_SUITES = [
65
67
  'application-composition',
66
68
  'scaling-curves',
67
69
  'store-selector-fanout',
70
+ 'hook-store-composition',
71
+ 'activity',
68
72
  'effectful-list',
69
73
  'list-clear',
70
74
  'memo-wall',
@@ -82,11 +86,13 @@ export const BENCHMARK_SUITES = [
82
86
  'async-composition',
83
87
  'lynx-list',
84
88
  'universal-leaf-update',
89
+ 'universal-external-store',
85
90
  'lynx-render',
86
91
  'lynx-table',
87
92
  'lynx-table-web',
88
93
  'lynx-bundle-size',
89
94
  'codegen-size',
95
+ 'hook-memo',
90
96
  'compiler-throughput',
91
97
  'bundle-size',
92
98
  'bundle-reachability',
@@ -292,7 +298,7 @@ export function engineeringPlanFor(input, repoMode = false) {
292
298
  'Record what parity cannot reach as a divergence in UPSTREAM.md and status.json, with the reason, what the consumer should do instead, and a behavioral test pinning the Octane behavior.',
293
299
  "Run the pinned release's own suite as the parity oracle: its framework-neutral tests unmodified against the reused core, its React-binding tests ported case by case with the upstream case names and citations. Record every upstream test file as run as-is, ported, or out of scope with the reason, and never weaken an upstream assertion to make it pass.",
294
300
  ];
295
- if (repoMode) plan.requiredSkills.push('react-library-port');
301
+ if (repoMode) plan.requiredSkills.push('octane-react-library-port');
296
302
  }
297
303
  if (scope === 'framework-core' && !repoMode) {
298
304
  plan.blockingConditions = [
package/src/index.test.js CHANGED
@@ -77,6 +77,21 @@ describe('@octanejs/mcp-server helpers', () => {
77
77
  .map((line) => line.trim())
78
78
  .filter((line) => line && line !== 'Available suites:');
79
79
  expect(suites).toEqual(BENCHMARK_SUITES);
80
+
81
+ // The public MCP schema must accept every runner suite, not just keep
82
+ // an exported helper list in sync.
83
+ const server = createServer({ repoRoot });
84
+ const client = new Client({ name: 'octane-mcp-test', version: '1.0.0' });
85
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
86
+ try {
87
+ await Promise.all([server.connect(serverTransport), client.connect(clientTransport)]);
88
+ const tools = await client.listTools();
89
+ const benchmark = tools.tools.find((tool) => tool.name === 'octane_benchmark');
90
+ expect(benchmark?.inputSchema.properties?.benchmark?.enum).toEqual(['all', ...suites]);
91
+ } finally {
92
+ await client.close();
93
+ await server.close();
94
+ }
80
95
  });
81
96
 
82
97
  it('classifies every maintained binding and recommends its test project', () => {
@@ -150,7 +165,7 @@ describe('@octanejs/mcp-server helpers', () => {
150
165
  true,
151
166
  );
152
167
 
153
- expect(plan.requiredSkills).toContain('react-library-port');
168
+ expect(plan.requiredSkills).toContain('octane-react-library-port');
154
169
  expect(plan.gates.parity.join('\n')).toContain('packages/<name>/UPSTREAM.md');
155
170
  expect(plan.gates.parity.join('\n')).toContain('divergence');
156
171
  expect(plan.gates.parity.join('\n')).toContain("pinned release's own suite");
@@ -160,7 +175,7 @@ describe('@octanejs/mcp-server helpers', () => {
160
175
  true,
161
176
  );
162
177
  expect(applicationPlan.gates.parity).toBeUndefined();
163
- expect(applicationPlan.requiredSkills).not.toContain('react-library-port');
178
+ expect(applicationPlan.requiredSkills).not.toContain('octane-react-library-port');
164
179
  });
165
180
 
166
181
  it('blocks framework-core plans when maintainer tools are unavailable', () => {
@@ -268,6 +283,7 @@ describe('@octanejs/mcp-server helpers', () => {
268
283
  .map((entry) => entry.name)
269
284
  .sort();
270
285
 
286
+ expect(REPO_SKILLS['react-library-port']).toBe('.rulesync/skills/react-library-port/SKILL.md');
271
287
  expect(Object.keys(REPO_SKILLS).sort()).toEqual(onDisk);
272
288
 
273
289
  for (const file of Object.values(REPO_SKILLS)) {