@octanejs/mcp-server 0.2.17 → 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,7 +162,7 @@ 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`,
165
+ `application-composition`, `scaling-curves`, `activity`, `streaming-ssr`,
166
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octanejs/mcp-server",
3
- "version": "0.2.17",
3
+ "version": "0.2.18",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22.22.2"
package/src/bridge.js CHANGED
@@ -264,11 +264,11 @@ export const REACT_API_MAP = {
264
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).",
265
265
  },
266
266
  Component: {
267
- status: 'unsupported',
267
+ status: 'rewrite',
268
268
  note: 'No class components. Rewrite as a function component.',
269
269
  },
270
270
  PureComponent: {
271
- status: 'unsupported',
271
+ status: 'rewrite',
272
272
  note: 'No class components. Rewrite as a function component with memo.',
273
273
  },
274
274
  StrictMode: {
@@ -277,7 +277,7 @@ export const REACT_API_MAP = {
277
277
  },
278
278
  Profiler: { status: 'unsupported', note: 'Not present.' },
279
279
  SuspenseList: { status: 'unsupported', note: 'Not present.' },
280
- findDOMNode: { status: 'unsupported', note: 'Removed in React 19 too. Use refs.' },
280
+ findDOMNode: { status: 'rewrite', note: 'Removed in React 19 too. Use refs.' },
281
281
  renderToString: {
282
282
  status: 'rewrite',
283
283
  note: 'Use renderToString() from octane/server (sync) or prerender() from octane/static (async, awaits Suspense); both return { html, css }.',
@@ -496,8 +496,8 @@ function apiRows(totals) {
496
496
  }
497
497
 
498
498
  function verdictFor(rows, classComponents) {
499
- if (classComponents || rows.some((row) => row.status === 'unsupported')) return 'needs-rework';
500
- 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')) {
501
501
  return 'bridgeable-with-rewrites';
502
502
  }
503
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
 
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
 
@@ -66,6 +68,7 @@ export const BENCHMARK_SUITES = [
66
68
  'scaling-curves',
67
69
  'store-selector-fanout',
68
70
  'hook-store-composition',
71
+ 'activity',
69
72
  'effectful-list',
70
73
  'list-clear',
71
74
  'memo-wall',
@@ -295,7 +298,7 @@ export function engineeringPlanFor(input, repoMode = false) {
295
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.',
296
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.",
297
300
  ];
298
- if (repoMode) plan.requiredSkills.push('react-library-port');
301
+ if (repoMode) plan.requiredSkills.push('octane-react-library-port');
299
302
  }
300
303
  if (scope === 'framework-core' && !repoMode) {
301
304
  plan.blockingConditions = [
package/src/index.test.js CHANGED
@@ -165,7 +165,7 @@ describe('@octanejs/mcp-server helpers', () => {
165
165
  true,
166
166
  );
167
167
 
168
- expect(plan.requiredSkills).toContain('react-library-port');
168
+ expect(plan.requiredSkills).toContain('octane-react-library-port');
169
169
  expect(plan.gates.parity.join('\n')).toContain('packages/<name>/UPSTREAM.md');
170
170
  expect(plan.gates.parity.join('\n')).toContain('divergence');
171
171
  expect(plan.gates.parity.join('\n')).toContain("pinned release's own suite");
@@ -175,7 +175,7 @@ describe('@octanejs/mcp-server helpers', () => {
175
175
  true,
176
176
  );
177
177
  expect(applicationPlan.gates.parity).toBeUndefined();
178
- expect(applicationPlan.requiredSkills).not.toContain('react-library-port');
178
+ expect(applicationPlan.requiredSkills).not.toContain('octane-react-library-port');
179
179
  });
180
180
 
181
181
  it('blocks framework-core plans when maintainer tools are unavailable', () => {
@@ -283,6 +283,7 @@ describe('@octanejs/mcp-server helpers', () => {
283
283
  .map((entry) => entry.name)
284
284
  .sort();
285
285
 
286
+ expect(REPO_SKILLS['react-library-port']).toBe('.rulesync/skills/react-library-port/SKILL.md');
286
287
  expect(Object.keys(REPO_SKILLS).sort()).toEqual(onDisk);
287
288
 
288
289
  for (const file of Object.values(REPO_SKILLS)) {