@octanejs/mcp-server 0.2.2 → 0.2.3

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
@@ -72,12 +72,10 @@ exists, an overall verdict (`bridgeable`, `bridgeable-with-rewrites`,
72
72
 
73
73
  ### `octane_bindings`
74
74
 
75
- Returns the map of React packages with maintained `@octanejs/*` ports
76
- (zustand, tanstack-query, motion, stylex, tanstack-router, lexical,
77
- floating-ui, radix, hook-form, base-ui, recharts, redux, testing-library,
78
- mdx). The map lives in `src/bridge.js` (`KNOWN_BINDINGS`) and its tests derive
79
- the expected set from the workspace manifests, so it cannot silently drift from
80
- the published bindings.
75
+ Returns the map of React packages with maintained `@octanejs/*` ports. The map
76
+ lives in `src/bridge.js` (`KNOWN_BINDINGS`), and its tests derive the complete
77
+ binding package set from the workspace manifests, so adding a published binding
78
+ without registering its React-package mapping fails CI.
81
79
 
82
80
  ### `octane_skill`
83
81
 
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "@octanejs/mcp-server",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
+ "engines": {
6
+ "node": ">=22"
7
+ },
5
8
  "description": "MCP server exposing Octane repository automation for coding agents.",
6
9
  "license": "MIT",
7
10
  "author": {
@@ -11,15 +11,19 @@ of bridging by hand:
11
11
  | --- | --- |
12
12
  | `zustand` | `@octanejs/zustand` |
13
13
  | `jotai` | `@octanejs/jotai` |
14
+ | `@apollo/client` | `@octanejs/apollo-client` |
14
15
  | `@tanstack/react-query` | `@octanejs/tanstack-query` |
16
+ | `@tanstack/react-router` | `@octanejs/tanstack-router` |
15
17
  | `@tanstack/react-table` | `@octanejs/tanstack-table` |
16
18
  | `@tanstack/react-virtual` | `@octanejs/tanstack-virtual` |
17
19
  | `framer-motion` / `motion` | `@octanejs/motion` |
18
20
  | `@stylexjs/stylex` | `@octanejs/stylex` |
19
- | `react-router` / `react-router-dom` | `@octanejs/tanstack-router` |
21
+ | `react-router` / `react-router-dom` | `@octanejs/remix-router` |
20
22
  | `@lexical/react` | `@octanejs/lexical` |
23
+ | `lucide-react` | `@octanejs/lucide` |
21
24
  | `@floating-ui/react` | `@octanejs/floating-ui` |
22
25
  | `radix-ui` | `@octanejs/radix` |
26
+ | `react-i18next` | `@octanejs/i18next` |
23
27
 
24
28
  For anything else, run the `octane_bridge_react_package` tool to get a scan of the
25
29
  package's React API usage and a tailored plan, then follow the workflow below.
package/src/bridge.js CHANGED
@@ -7,34 +7,51 @@ import { join, resolve } from 'node:path';
7
7
  export const KNOWN_BINDINGS = {
8
8
  zustand: '@octanejs/zustand',
9
9
  jotai: '@octanejs/jotai',
10
+ '@apollo/client': '@octanejs/apollo-client',
10
11
  '@tanstack/react-query': '@octanejs/tanstack-query',
12
+ '@tanstack/react-router': '@octanejs/tanstack-router',
11
13
  '@tanstack/react-table': '@octanejs/tanstack-table',
12
14
  '@tanstack/react-virtual': '@octanejs/tanstack-virtual',
13
15
  'framer-motion': '@octanejs/motion',
14
16
  motion: '@octanejs/motion',
15
17
  '@stylexjs/stylex': '@octanejs/stylex',
16
- 'react-router': '@octanejs/tanstack-router',
17
- 'react-router-dom': '@octanejs/tanstack-router',
18
+ 'react-router': '@octanejs/remix-router',
19
+ 'react-router-dom': '@octanejs/remix-router',
18
20
  '@lexical/react': '@octanejs/lexical',
21
+ 'lucide-react': '@octanejs/lucide',
19
22
  '@floating-ui/react': '@octanejs/floating-ui',
20
23
  'radix-ui': '@octanejs/radix',
21
24
  'react-hook-form': '@octanejs/hook-form',
22
25
  '@base-ui-components/react': '@octanejs/base-ui',
26
+ '@dnd-kit/react': '@octanejs/dnd-kit',
27
+ sonner: '@octanejs/sonner',
23
28
  recharts: '@octanejs/recharts',
24
29
  'react-redux': '@octanejs/redux',
30
+ '@reduxjs/toolkit': '@octanejs/redux-toolkit',
25
31
  '@testing-library/react': '@octanejs/testing-library',
32
+ 'react-i18next': '@octanejs/i18next',
26
33
  '@mdx-js/react': '@octanejs/mdx',
27
34
  };
28
35
 
36
+ // Workspace directory names for the maintained bindings. Keep this derived
37
+ // from KNOWN_BINDINGS so repository path routing cannot drift from the public
38
+ // binding list (aliases such as `motion` intentionally collapse to one dir).
39
+ export const KNOWN_BINDING_PACKAGE_DIRS = new Set(
40
+ Object.values(KNOWN_BINDINGS).map((name) => name.slice('@octanejs/'.length)),
41
+ );
42
+
29
43
  export const KNOWN_VANILLA_CORES = {
44
+ '@apollo/client': '@apollo/client',
30
45
  '@tanstack/react-query': '@tanstack/query-core',
31
46
  '@tanstack/react-table': '@tanstack/table-core',
32
47
  '@tanstack/react-virtual': '@tanstack/virtual-core',
33
48
  '@tanstack/react-form': '@tanstack/form-core',
34
49
  '@floating-ui/react': '@floating-ui/dom',
50
+ '@dnd-kit/react': '@dnd-kit/dom',
35
51
  '@xstate/react': 'xstate',
36
52
  'react-redux': 'redux',
37
53
  '@reduxjs/toolkit': 'redux',
54
+ 'react-i18next': 'i18next',
38
55
  'react-hook-form': null,
39
56
  zustand: 'zustand/vanilla',
40
57
  valtio: 'valtio/vanilla',
@@ -45,9 +62,12 @@ export const KNOWN_VANILLA_CORES = {
45
62
  export const REACT_API_MAP = {
46
63
  useState: {
47
64
  status: 'same',
48
- note: 'Identical, including lazy initializer and functional updates.',
65
+ note: 'Same lazy initializer and functional-update semantics; Octane additionally exposes a stable current-state getter at tuple index 2.',
66
+ },
67
+ useReducer: {
68
+ status: 'same',
69
+ note: 'Same reducer and lazy-init semantics; Octane additionally exposes a stable current-state getter at tuple index 2.',
49
70
  },
50
- useReducer: { status: 'same', note: 'Identical, including lazy init third argument.' },
51
71
  useEffect: { status: 'same', note: 'Identical deps/cleanup semantics.' },
52
72
  useLayoutEffect: { status: 'same', note: 'Identical: synchronous after DOM mutation.' },
53
73
  useInsertionEffect: { status: 'same', note: 'Supported.' },
@@ -3,7 +3,13 @@ import { mkdir, mkdtemp, readdir, readFile, writeFile } from 'node:fs/promises';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { tmpdir } from 'node:os';
5
5
  import { join } from 'node:path';
6
- import { bridgeReport, detectVanillaCore, scanSource, KNOWN_BINDINGS } from './bridge.js';
6
+ import {
7
+ bridgeReport,
8
+ detectVanillaCore,
9
+ scanSource,
10
+ KNOWN_BINDINGS,
11
+ KNOWN_BINDING_PACKAGE_DIRS,
12
+ } from './bridge.js';
7
13
 
8
14
  describe('scanSource', () => {
9
15
  it('collects React API usage counts and import specifiers', () => {
@@ -34,6 +40,7 @@ describe('scanSource', () => {
34
40
 
35
41
  describe('detectVanillaCore', () => {
36
42
  it('prefers the known-core table', () => {
43
+ expect(detectVanillaCore('@apollo/client', {})).toBe('@apollo/client');
37
44
  expect(detectVanillaCore('@tanstack/react-query', {})).toBe('@tanstack/query-core');
38
45
  expect(detectVanillaCore('zustand', {})).toBe('zustand/vanilla');
39
46
  });
@@ -146,12 +153,16 @@ describe('KNOWN_BINDINGS', () => {
146
153
  // excluded by name.
147
154
  const NON_BINDINGS = new Set([
148
155
  'octane',
156
+ '@octanejs/app-core',
157
+ '@octanejs/rspack-plugin',
158
+ '@octanejs/rsbuild-plugin',
149
159
  '@octanejs/vite-plugin',
150
160
  '@octanejs/adapter-vercel',
151
161
  '@octanejs/mcp-server',
152
162
  ]);
153
163
  const packagesRoot = fileURLToPath(new URL('../..', import.meta.url));
154
164
  const bindings = [];
165
+ const bindingDirectories = [];
155
166
  for (const entry of await readdir(packagesRoot, { withFileTypes: true })) {
156
167
  if (!entry.isDirectory()) continue;
157
168
  let manifest;
@@ -164,8 +175,10 @@ describe('KNOWN_BINDINGS', () => {
164
175
  }
165
176
  if (manifest.private || NON_BINDINGS.has(manifest.name)) continue;
166
177
  bindings.push(manifest.name);
178
+ bindingDirectories.push(entry.name);
167
179
  }
168
180
  expect(bindings.length).toBeGreaterThan(0);
169
181
  expect(new Set(Object.values(KNOWN_BINDINGS))).toEqual(new Set(bindings));
182
+ expect(KNOWN_BINDING_PACKAGE_DIRS).toEqual(new Set(bindingDirectories));
170
183
  });
171
184
  });
package/src/index.js CHANGED
@@ -7,7 +7,7 @@ import { readFile } from 'node:fs/promises';
7
7
  import { dirname, join, resolve } from 'node:path';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { z } from 'zod';
10
- import { bridgeReport, KNOWN_BINDINGS } from './bridge.js';
10
+ import { bridgeReport, KNOWN_BINDINGS, KNOWN_BINDING_PACKAGE_DIRS } from './bridge.js';
11
11
 
12
12
  const __filename = fileURLToPath(import.meta.url);
13
13
  const PACKAGE_ROOT = resolve(dirname(__filename), '..');
@@ -59,9 +59,13 @@ export function areaForPath(path) {
59
59
  return 'core-runtime';
60
60
  }
61
61
  if (path.startsWith('packages/octane/tests/')) return 'core-tests';
62
+ if (path.startsWith('packages/app-core/')) return 'metaframework-core';
63
+ if (path.startsWith('packages/rspack-plugin-octane/')) return 'rspack-plugin';
64
+ if (path.startsWith('packages/rsbuild-plugin-octane/')) return 'rsbuild-plugin';
62
65
  if (path.startsWith('packages/vite-plugin-octane/')) return 'vite-plugin';
63
66
  if (path.startsWith('packages/octane-mcp-server/')) return 'mcp-server';
64
- if (/^packages\/(zustand|query|motion|stylex|router|lexical|floating-ui|radix)\//.test(path)) {
67
+ const packageMatch = path.match(/^packages\/([^/]+)\//);
68
+ if (packageMatch && KNOWN_BINDING_PACKAGE_DIRS.has(packageMatch[1])) {
65
69
  return 'ecosystem-binding';
66
70
  }
67
71
  if (path.startsWith('benchmarks/')) return 'benchmark';
@@ -88,13 +92,12 @@ export function validationFor(paths, taskKind) {
88
92
  }
89
93
  if (areas.has('ecosystem-binding')) {
90
94
  for (const path of paths) {
91
- const match = path.match(
92
- /^packages\/(zustand|query|motion|stylex|router|lexical|floating-ui|radix)\//,
93
- );
94
- if (match)
95
+ const match = path.match(/^packages\/([^/]+)\//);
96
+ if (match && KNOWN_BINDING_PACKAGE_DIRS.has(match[1])) {
95
97
  commands.add(
96
98
  `./node_modules/.bin/vitest run packages/${match[1]}/tests --project ${match[1]}`,
97
99
  );
100
+ }
98
101
  }
99
102
  }
100
103
  if (areas.has('mcp-server')) {
@@ -102,7 +105,14 @@ export function validationFor(paths, taskKind) {
102
105
  './node_modules/.bin/vitest run packages/octane-mcp-server --project octane-mcp-server',
103
106
  );
104
107
  }
105
- if (areas.has('vite-plugin')) commands.add('pnpm typecheck');
108
+ if (
109
+ areas.has('metaframework-core') ||
110
+ areas.has('rspack-plugin') ||
111
+ areas.has('rsbuild-plugin') ||
112
+ areas.has('vite-plugin')
113
+ ) {
114
+ commands.add('pnpm typecheck');
115
+ }
106
116
  if (areas.has('benchmark') || taskKind === 'performance') commands.add('pnpm bench');
107
117
  if (taskKind === 'api' || taskKind === 'core' || taskKind === 'package')
108
118
  commands.add('pnpm typecheck');
package/src/index.test.js CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  scaffoldReactPort,
12
12
  validationFor,
13
13
  } from './index.js';
14
+ import { KNOWN_BINDING_PACKAGE_DIRS } from './bridge.js';
14
15
 
15
16
  const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
16
17
 
@@ -26,6 +27,20 @@ describe('@octanejs/mcp-server helpers', () => {
26
27
  expect(areaForPath('.rulesync/rules/project.md')).toBe('rulesync-source');
27
28
  });
28
29
 
30
+ it('classifies every maintained binding and recommends its test project', () => {
31
+ const paths = [...KNOWN_BINDING_PACKAGE_DIRS].map(
32
+ (directory) => `packages/${directory}/src/index.ts`,
33
+ );
34
+ const commands = validationFor(paths, 'binding');
35
+
36
+ for (const directory of KNOWN_BINDING_PACKAGE_DIRS) {
37
+ expect(areaForPath(`packages/${directory}/src/index.ts`)).toBe('ecosystem-binding');
38
+ expect(commands).toContain(
39
+ `./node_modules/.bin/vitest run packages/${directory}/tests --project ${directory}`,
40
+ );
41
+ }
42
+ });
43
+
29
44
  it('recommends validation commands from changed paths', () => {
30
45
  const commands = validationFor(
31
46
  [