@ebay-dt/bonfire-cli 0.1.0-alpha.4 → 0.1.0

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.
Files changed (36) hide show
  1. package/README.md +26 -0
  2. package/dist/command.d.ts +5 -1
  3. package/dist/command.d.ts.map +1 -1
  4. package/dist/command.js +103 -1
  5. package/dist/command.js.map +1 -1
  6. package/dist/convert.d.ts +47 -0
  7. package/dist/convert.d.ts.map +1 -0
  8. package/dist/convert.js +509 -0
  9. package/dist/convert.js.map +1 -0
  10. package/package.json +5 -4
  11. package/templates/instance/context/PROTOTYPE.md +108 -0
  12. package/templates/instance/context/ROOT.md +23 -0
  13. package/templates/instance/context/hooks/block-env-reads.mjs +144 -0
  14. package/templates/instance/context/mcp.json +8 -0
  15. package/templates/instance/context/settings.json +19 -0
  16. package/templates/instance/context/skills/color-scheme/SKILL.md +98 -0
  17. package/templates/instance/context/skills/item-data/SKILL.md +145 -0
  18. package/templates/instance/context/skills/setup-evomcp/SKILL.md +73 -0
  19. package/templates/instance/next.config.ts +10 -0
  20. package/templates/instance/src/actions/agents.ts +15 -0
  21. package/templates/instance/src/actions/prototypes.ts +37 -0
  22. package/templates/instance/src/app/(prototypes)/layout.tsx +40 -0
  23. package/templates/instance/src/app/(system)/api/prototype-dirty/route.ts +4 -0
  24. package/templates/instance/src/app/(system)/auth/login/route.ts +4 -0
  25. package/templates/instance/src/app/(system)/auth/logout/route.ts +4 -0
  26. package/templates/instance/src/app/(system)/auth/status/route.ts +4 -0
  27. package/templates/instance/src/app/(system)/layout.tsx +20 -0
  28. package/templates/instance/src/app/(system)/login/page.tsx +14 -0
  29. package/templates/instance/src/app/(system)/page.tsx +33 -0
  30. package/templates/instance/src/app/api/items/[itemId]/route.ts +21 -0
  31. package/templates/instance/src/app/api/items/route.ts +11 -0
  32. package/templates/instance/src/app/layout.tsx +56 -0
  33. package/templates/instance/src/bonfire.ts +25 -0
  34. package/templates/instance/src/lib/items/gateway-schemas.ts +100 -0
  35. package/templates/instance/src/lib/items/server.ts +220 -0
  36. package/templates/instance/src/proxy.ts +8 -0
@@ -0,0 +1,108 @@
1
+ ## Prototype patterns and practices
2
+
3
+ ### General principles
4
+
5
+ - Optimize for coherent, understandable prototype code—not production-scale architecture.
6
+ - Prefer the simplest implementation that faithfully demonstrates the intended experience while preserving responsive behavior and repository conventions.
7
+ - Do not over-engineer for speculative reuse, but do not concentrate an entire experience into one large component when clear local boundaries exist.
8
+ - To minimize implementation friction, the `app.tsx` prototype entrypoint is intentionally a Client Component. Components imported by `app.tsx` generally do not need their own `'use client'` directive.
9
+
10
+ ### Prototype scope
11
+
12
+ Keep every change inside the current prototype directory.
13
+
14
+ Do not modify shared application infrastructure, other prototypes, repository configuration, dependencies, or the repository's local design-system components. If the prototype appears to require a shared change, stop and explain what is needed. Ask the user to have the repository owner make that change separately.
15
+
16
+ If a design-system component needs different presentation:
17
+
18
+ 1. Prefer its supported props and variants.
19
+ 2. If necessary, apply a prototype-local CSS override.
20
+ 3. As a last resort, copy the component into the prototype and adapt the local copy.
21
+
22
+ Do not alter the shared component to satisfy the needs of one prototype.
23
+
24
+ ### Design-system components
25
+
26
+ Prefer existing design-system components over recreating equivalent controls from scratch.
27
+
28
+ When Evo MCP provides component guidance, use that guidance for API and behavior. Prefer supported components from `@evo-web/react/...`; use direct `@ebay/ui-core-react/...` imports when Evo does not provide a suitable component.
29
+
30
+ Evo component entrypoints load their own component CSS. The application root
31
+ loads the published `@ebay-dt/bonfire-preset-ebay` stylesheet, which supplies
32
+ shared Skin/Core UI CSS and theme tokens. Do not add direct `@ebay/skin` imports
33
+ or duplicate global CSS for Evo components.
34
+
35
+ Preserve the component's intended semantics and accessibility behavior. Do not replace a design-system component with custom markup solely to make styling easier. Import icons directly from the selected library:
36
+
37
+ ```ts
38
+ import { EvoIconSettings16 } from '@evo-web/react/icons/settings-16'
39
+ import { EbayIconArrowRight16 } from '@ebay/ui-core-react/icons/ebay-icon-arrow-right-16'
40
+ ```
41
+
42
+ Keep prototype-specific composition helpers—such as item-tile price, media, or treatment mappings—inside the prototype that owns them. They may compose upstream components, but they must not become shared compatibility wrappers or substitute exports for the design-system packages.
43
+
44
+ ### Prototype organization
45
+
46
+ Keep components focused and files easy to navigate. When a section has its own behavior, repeated markup, or substantial styling, extract it into a prototype-local component rather than allowing the main prototype file to grow indefinitely.
47
+
48
+ Place extracted prototype-local components under the prototype's `components` directory:
49
+
50
+ ```
51
+ components/
52
+ my-component/
53
+ index.tsx
54
+ styles.module.css
55
+ ```
56
+
57
+ Keep a component's implementation, types, and styles together when they are only used by that component. Avoid extracting tiny one-use wrappers that make the interaction harder to follow.
58
+
59
+ ### Data fetching
60
+
61
+ Use the published item hooks instead of fetching their backing API routes directly from prototype components.
62
+
63
+ - Import `useItemsSearch`, `useInfiniteItemsSearch`, and `useItemById` from `@ebay-dt/bonfire-preset-ebay/data/items/react`.
64
+ - Import normalized item types such as `SearchItem` and `ItemDetail` from `@ebay-dt/bonfire-preset-ebay/data/items`.
65
+ - Mount `QueryProvider` from the `/data/items/react` entrypoint around the prototype's item UI. The application root does not supply a query provider; reuse an existing prototype-local provider rather than nesting another one.
66
+
67
+ Before implementing item search, infinite loading, item selection, or item details, load and follow the `item-data` skill. Treat the installed package's exports, type declarations, and documentation as the canonical API. Gateway endpoints, credentials, and wire schemas belong to the instance server adapter, not prototype code.
68
+
69
+ ### Viewport sizing
70
+
71
+ The sandbox exposes `--app-header-block-size` globally. It resolves to the development editor bar height when editor UI is enabled and to `0px` in production.
72
+
73
+ When a prototype should fill the viewport without extending beneath the editor bar, account for it rather than hard-coding a header size:
74
+
75
+ ```css
76
+ .prototype {
77
+ min-block-size: calc(100svh - var(--app-header-block-size));
78
+ }
79
+ ```
80
+
81
+ Use the variable for viewport-height calculations even though production has no editor bar; the same CSS will then resolve to the full viewport in production.
82
+
83
+ ### Visitor color-scheme controls
84
+
85
+ Before implementing a visitor-facing Light/Dark control, load and follow the `color-scheme` skill.
86
+
87
+ Controls must use `useColorSchemeToggle` from `@ebay-dt/bonfire-react` within the shared layout's `PrototypeEnvironment`. Check the prototype's current theme settings first; if they do not permit switching, get user confirmation before changing `metadata.json`. Never enable switching autonomously.
88
+
89
+ Do not bypass the hook by accessing `localStorage`, calling `matchMedia`, or manipulating `data-color-scheme`. Keep server-rendered control markup hydration-stable and use a scheme-neutral accessible name.
90
+
91
+ ### Styling
92
+
93
+ Keep prototype styles local. Prefer CSS Modules and existing design tokens over global selectors, hard-coded theme values, or changes to shared stylesheets.
94
+
95
+ Use logical properties where practical and ensure layouts work at both narrow and wide viewport sizes. Account for the sandbox editor header in viewport-height layouts as described in the viewport-sizing section.
96
+
97
+ Avoid `!important` unless it is required for a narrowly scoped design-system override. Keep overrides under a prototype-specific class so they cannot affect other prototypes.
98
+
99
+ ### Prototype validation
100
+
101
+ Do not add automated tests for prototype-specific code unless the user requests them. Prototypes prioritize iteration speed over production-level test coverage.
102
+
103
+ Before finishing:
104
+
105
+ - Run the relevant check and lint commands, and format only files changed in the current prototype.
106
+ - Confirm that all changes are confined to the current prototype directory.
107
+
108
+ Do not spend substantial time repairing unrelated pre-existing failures outside the current prototype. Report them separately.
@@ -0,0 +1,23 @@
1
+ ## Code Standards
2
+
3
+ **When working in React:**
4
+
5
+ - Prefer straightforward React composition and derived values over duplicated state or unnecessary effects.
6
+ - Follow React 19 conventions. Treat `ref` as a regular prop instead of using `forwardRef`, and use `useEffectEvent` when an Effect needs stable access to non-reactive logic.
7
+ - Keep state as close as practical to the components that use it.
8
+ - Do not store values in state when they can be derived during rendering.
9
+
10
+ **When working in TypeScript:**
11
+
12
+ - Prefer inferred function return types, including for exported functions.
13
+ - When a returned value should be checked against a contract, prefer `satisfies` so TypeScript validates the value without replacing its inferred type.
14
+ - Add an explicit function return type only when inference cannot express the required contract or TypeScript requires one (e.g., type guards, recursive functions).
15
+ - Avoid creating types that merely restate values TypeScript can already infer.
16
+ - Treat type assertions as a last resort. Do not use `as any` to bypass type errors.
17
+
18
+ **In general:**
19
+
20
+ - Run the relevant check, format, and lint commands after making changes.
21
+ - Do not start or restart the development server. Assume the user manages the running development environment.
22
+ - When asking questions, ask them one at a time and offer a recommendation.
23
+ - Use dependencies and utilities already available in the repository. Do not add a package unless the user explicitly requests or approves it.
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Block agent reads of .env files, except committed example templates.
4
+ * Allowed: .env.example, .env.local.example, .env.*.example
5
+ *
6
+ * Compatible with:
7
+ * - Cursor: beforeReadFile, beforeShellExecution, preToolUse
8
+ * - Claude Code: PreToolUse (Read / Bash)
9
+ *
10
+ * Reads hook JSON from stdin and prints a decision JSON to stdout.
11
+ */
12
+
13
+ import path from 'node:path'
14
+
15
+ const DENY_MESSAGE =
16
+ 'Reading .env files is blocked by a project hook. Use an .env*.example template instead.'
17
+
18
+ /** .env.example, .env.local.example, .env.development.local.example, etc. */
19
+ function isAllowedEnvExampleBasename(basename) {
20
+ return /^\.env(\.[A-Za-z0-9_-]+)*\.example$/.test(basename)
21
+ }
22
+
23
+ function isBlockedEnvBasename(basename) {
24
+ if (isAllowedEnvExampleBasename(basename)) return false
25
+ // .env, .env.local, .env.development, etc. — not .environment
26
+ return /^\.env($|\.)/.test(basename)
27
+ }
28
+
29
+ function isBlockedEnvPath(filePath) {
30
+ if (!filePath || typeof filePath !== 'string') return false
31
+ return isBlockedEnvBasename(path.basename(filePath))
32
+ }
33
+
34
+ function commandTouchesBlockedEnv(command) {
35
+ if (!command || typeof command !== 'string') return false
36
+ // Match .env and .env.* tokens; allow .env.example / .env.*.example
37
+ for (const match of command.matchAll(
38
+ /(?<![A-Za-z0-9_-])\.env(?:\.[A-Za-z0-9_-]+)*/g
39
+ )) {
40
+ if (!isAllowedEnvExampleBasename(match[0])) return true
41
+ }
42
+ return false
43
+ }
44
+
45
+ function collectCandidatePaths(input) {
46
+ const paths = []
47
+ if (typeof input.file_path === 'string') paths.push(input.file_path)
48
+
49
+ const toolInput = input.tool_input
50
+ if (toolInput && typeof toolInput === 'object') {
51
+ if (typeof toolInput.file_path === 'string') paths.push(toolInput.file_path)
52
+ if (typeof toolInput.path === 'string') paths.push(toolInput.path)
53
+ }
54
+
55
+ return paths
56
+ }
57
+
58
+ function collectCandidateCommands(input) {
59
+ const commands = []
60
+ if (typeof input.command === 'string') commands.push(input.command)
61
+
62
+ const toolInput = input.tool_input
63
+ if (
64
+ toolInput &&
65
+ typeof toolInput === 'object' &&
66
+ typeof toolInput.command === 'string'
67
+ ) {
68
+ commands.push(toolInput.command)
69
+ }
70
+
71
+ return commands
72
+ }
73
+
74
+ function shouldDeny(input) {
75
+ for (const filePath of collectCandidatePaths(input)) {
76
+ if (isBlockedEnvPath(filePath)) return true
77
+ }
78
+ for (const command of collectCandidateCommands(input)) {
79
+ if (commandTouchesBlockedEnv(command)) return true
80
+ }
81
+ return false
82
+ }
83
+
84
+ function isClaudeStyle(input) {
85
+ return (
86
+ typeof input.hook_event_name === 'string' &&
87
+ /^[A-Z]/.test(input.hook_event_name)
88
+ )
89
+ }
90
+
91
+ function denyResponse(input) {
92
+ if (isClaudeStyle(input)) {
93
+ return {
94
+ hookSpecificOutput: {
95
+ hookEventName: input.hook_event_name || 'PreToolUse',
96
+ permissionDecision: 'deny',
97
+ permissionDecisionReason: DENY_MESSAGE,
98
+ },
99
+ }
100
+ }
101
+
102
+ return {
103
+ permission: 'deny',
104
+ user_message: DENY_MESSAGE,
105
+ agent_message: DENY_MESSAGE,
106
+ }
107
+ }
108
+
109
+ function allowResponse(input) {
110
+ if (isClaudeStyle(input)) {
111
+ return {}
112
+ }
113
+ return { permission: 'allow' }
114
+ }
115
+
116
+ async function main() {
117
+ const chunks = []
118
+ for await (const chunk of process.stdin) chunks.push(chunk)
119
+ const raw = Buffer.concat(chunks).toString('utf8').trim()
120
+
121
+ if (!raw) {
122
+ process.stdout.write(JSON.stringify({ permission: 'allow' }))
123
+ return
124
+ }
125
+
126
+ let input
127
+ try {
128
+ input = JSON.parse(raw)
129
+ } catch {
130
+ // Fail open on malformed input unless the host sets failClosed.
131
+ process.stdout.write(JSON.stringify({ permission: 'allow' }))
132
+ process.exit(0)
133
+ }
134
+
135
+ const response = shouldDeny(input)
136
+ ? denyResponse(input)
137
+ : allowResponse(input)
138
+ process.stdout.write(JSON.stringify(response))
139
+ }
140
+
141
+ main().catch(() => {
142
+ process.stdout.write(JSON.stringify({ permission: 'allow' }))
143
+ process.exit(0)
144
+ })
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "evomcp": {
4
+ "type": "http",
5
+ "url": "https://evomcp2.vip.qa.ebay.com/mcp"
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "enableAllProjectMcpServers": true,
3
+ "permissions": {
4
+ "allow": ["mcp__evomcp__*"]
5
+ },
6
+ "hooks": {
7
+ "PreToolUse": [
8
+ {
9
+ "matcher": "Read|Bash",
10
+ "hooks": [
11
+ {
12
+ "type": "command",
13
+ "command": "node \"{{BONFIRE_REPO_ROOT}}\"/context/hooks/block-env-reads.mjs"
14
+ }
15
+ ]
16
+ }
17
+ ]
18
+ }
19
+ }
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: color-scheme
3
+ description: Implement visitor-facing Light and Dark color-scheme controls in Design Sandbox prototypes using useColorSchemeToggle. Use when adding theme toggles, appearance controls, sun/moon controls, or prototype UI that lets visitors switch the active color scheme.
4
+ ---
5
+
6
+ # Visitor color-scheme controls
7
+
8
+ Use the published Bonfire hook for prototype-authored controls that let visitors switch between Light and Dark:
9
+
10
+ ```tsx
11
+ 'use client'
12
+
13
+ import { useColorSchemeToggle } from '@ebay-dt/bonfire-react'
14
+
15
+ export function ColorSchemeToggle() {
16
+ const { canToggle, toggle } = useColorSchemeToggle()
17
+
18
+ return (
19
+ <button
20
+ type="button"
21
+ aria-label="Toggle color scheme"
22
+ onClick={toggle}
23
+ disabled={!canToggle}
24
+ >
25
+ Toggle color scheme
26
+ </button>
27
+ )
28
+ }
29
+ ```
30
+
31
+ This is a client hook and must be called from a Client Component inside `PrototypeEnvironment`. The shared prototype layout supplies that boundary; do not add a second provider or independent theme state. The installed `@ebay-dt/bonfire-react` exports, type declarations, and README are the canonical API.
32
+
33
+ ## Check settings before implementation
34
+
35
+ First inspect the prototype's `metadata.json`, especially:
36
+
37
+ - `theme.defaultColorScheme`
38
+ - `theme.allowedColorSchemes`
39
+
40
+ If the current settings do not permit Light/Dark switching, ask the user to confirm that the settings should be changed. Do not modify `metadata.json` or any other prototype settings and do not enable switching autonomously.
41
+
42
+ If settings remain unchanged and permit only the current outcome, `canToggle` is `false` and `toggle` is a no-op. The control should respect `canToggle`, normally by being disabled or omitted when the product requirement allows it.
43
+
44
+ Theme-family switching is outside this hook's scope.
45
+
46
+ ## Hook contract
47
+
48
+ `useColorSchemeToggle()` returns:
49
+
50
+ - `colorScheme` — the currently rendered `'light' | 'dark'` scheme
51
+ - `canToggle` — whether the configured settings allow switching
52
+ - `toggle()` — switches the rendered scheme when allowed; otherwise a no-op
53
+
54
+ The hook deliberately does not expose the internal System/default/override state. It automatically:
55
+
56
+ - follows the configured prototype default;
57
+ - resolves and tracks the OS preference when the default is System;
58
+ - persists explicit visitor overrides per prototype;
59
+ - returns to configured-default behavior when appropriate;
60
+ - discards stored overrides after relevant prototype theme policy or instance registry changes; and
61
+ - keeps multiple controls on the same page synchronized.
62
+
63
+ Do not reimplement any of this behavior. In particular, do not read or write `localStorage`, call `matchMedia`, or manipulate `data-color-scheme` directly.
64
+
65
+ ## Hydration stability
66
+
67
+ The server cannot read a persisted browser override or resolve the visitor's System preference. Keep the initial server-rendered control markup identical regardless of `colorScheme`.
68
+
69
+ Do not conditionally render initial:
70
+
71
+ - button text;
72
+ - accessible names or other ARIA attributes; or
73
+ - different icon elements
74
+
75
+ from `colorScheme`. Doing so can produce a hydration mismatch even though the theme boundary is restored before paint.
76
+
77
+ Use a scheme-neutral accessible name such as `Toggle color scheme`.
78
+
79
+ If the visual icon must reflect the active scheme before hydration:
80
+
81
+ 1. Render both visual states with identical React markup on the server and client.
82
+ 2. Hide the inactive state with CSS scoped to the ancestor `data-color-scheme` attribute.
83
+ 3. Include the appropriate `prefers-color-scheme` CSS fallback for a System default.
84
+ 4. Hide decorative icons from assistive technology.
85
+
86
+ The hook's `colorScheme` remains appropriate for client-side behavior that does not alter the initially hydrated markup.
87
+
88
+ ## Implementation checklist
89
+
90
+ - Inspect theme settings before writing the control.
91
+ - Ask before changing settings that do not currently allow switching.
92
+ - Use `useColorSchemeToggle`; do not create independent theme state.
93
+ - Respect `canToggle`.
94
+ - Use a scheme-neutral accessible name.
95
+ - Keep initial markup independent of `colorScheme`.
96
+ - Verify Light and Dark visuals and keyboard operation.
97
+ - Verify the page hydrates without warnings with a System default.
98
+ - If this hook's contract or theme settings format changes, update this skill and the pointer in `context/PROTOTYPE.md`, then run `pnpm context:sync` from the repository root to regenerate `AGENTS.md` and the skill projections.
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: item-data
3
+ description: Implement item search, paginated or infinite item results, item selection, and item detail experiences in prototypes using the repository's supported React Query hooks. Use whenever a prototype needs to find, list, load more, paginate, select, or retrieve details for items.
4
+ ---
5
+
6
+ # Item data
7
+
8
+ Use the published item hooks rather than calling `/api/items` directly. They own request cancellation, validation, caching, retries, search normalization, and search debouncing.
9
+
10
+ These are client hooks. Call them only from a Client Component (`'use client'`). The installed preset's exports, type declarations, and README are the canonical API; inspect them when behavior or types matter.
11
+
12
+ ## Hook inventory
13
+
14
+ All three hooks are exported from `@ebay-dt/bonfire-preset-ebay/data/items/react`:
15
+
16
+ | Need | Hook |
17
+ | ---------------------------- | ------------------------ |
18
+ | Numbered pages | `useItemsSearch` |
19
+ | Load more / infinite results | `useInfiniteItemsSearch` |
20
+ | Full details for one item | `useItemById` |
21
+
22
+ Normalized types such as `SearchItem`, `ItemSearchPage`, and `ItemDetail` are exported from `@ebay-dt/bonfire-preset-ebay/data/items`. These are presentation contracts, not raw gateway response types.
23
+
24
+ ## Query provider
25
+
26
+ The application root does not supply a React Query provider. Mount the preset's `QueryProvider` around the prototype's item UI, normally in its `page.tsx`:
27
+
28
+ ```tsx
29
+ import { QueryProvider } from '@ebay-dt/bonfire-preset-ebay/data/items/react'
30
+ import App from './app'
31
+
32
+ export default function Page() {
33
+ return (
34
+ <QueryProvider>
35
+ <App />
36
+ </QueryProvider>
37
+ )
38
+ }
39
+ ```
40
+
41
+ Reuse an existing prototype-local provider rather than nesting another one. The provider owns one stable Query Client for its subtree and defaults to `refetchOnWindowFocus: false`. It accepts `clientConfig` for query defaults or a host-owned `client` when explicit cache ownership is needed.
42
+
43
+ Gateway endpoints, GraphQL documents, credentials, wire schemas, and normalization belong to the instance's server adapter in `src/lib/items/server.ts` and `gateway-schemas.ts`. Do not copy those concerns into a prototype or modify shared infrastructure to implement an item UI.
44
+
45
+ ## Search behavior shared by both search hooks
46
+
47
+ - Pass the visitor's raw query to the hook; do not add another debounce layer.
48
+ - Queries are trimmed; subsequent query changes are debounced by 300 ms. A valid initial query can fetch immediately.
49
+ - Search results use a 30-second stale time and retry once.
50
+ - A trimmed query shorter than two characters is idle and does not fetch. Use `isIdle` to render search guidance instead of a loading or empty state.
51
+ - `data` can temporarily contain the previous result while a new request is fetching. Compare `resultQuery` with `debouncedQuery` when UI behavior must apply only to the current query.
52
+ - Render distinct idle, initial loading, error, empty, and populated states. Use the returned `error` and `refetch()` for retry UI.
53
+ - Search result objects are summaries. Use `useItemById` when an experience needs complete item data.
54
+
55
+ ## Numbered pagination
56
+
57
+ ```tsx
58
+ 'use client'
59
+
60
+ import { useItemsSearch } from '@ebay-dt/bonfire-preset-ebay/data/items/react'
61
+
62
+ export function Results({ query, page }: { query: string; page: number }) {
63
+ const search = useItemsSearch(query, { page, pageSize: 20 })
64
+ const items = search.data ?? []
65
+ const isCurrentResult = search.resultQuery === search.debouncedQuery
66
+
67
+ // Render search.isIdle, search.isPending, search.isError, and items here.
68
+ // Use search.hasPreviousPage, search.hasNextPage, and search.totalPages
69
+ // for controls. Disable or label controls while !isCurrentResult.
70
+ return null
71
+ }
72
+ ```
73
+
74
+ `useItemsSearch(query, options?)` accepts:
75
+
76
+ - `page` — one-based, defaults to `1`
77
+ - `pageSize` — defaults to `20`; the API supports at most `50`
78
+
79
+ In addition to standard React Query fields, it returns `data`, `page`, `pageSize`, `resultPage`, `total`, `totalPages`, `hasPreviousPage`, `hasNextPage`, `debouncedQuery`, `resultQuery`, and `isIdle`.
80
+
81
+ Keep page state in the component or URL. Reset it to page 1 when a current query result shows that the requested page is out of range. The API's maximum offset is 10,000, so do not offer pages beyond that boundary.
82
+
83
+ ## Infinite results
84
+
85
+ ```tsx
86
+ 'use client'
87
+
88
+ import { useInfiniteItemsSearch } from '@ebay-dt/bonfire-preset-ebay/data/items/react'
89
+
90
+ export function Results({ query }: { query: string }) {
91
+ const search = useInfiniteItemsSearch(query, { pageSize: 20 })
92
+ const items = search.data ?? []
93
+
94
+ // Render items, then call search.loadMore() from a button or observer.
95
+ // Disable the trigger while search.isLoadingMore or when !search.hasMore.
96
+ return null
97
+ }
98
+ ```
99
+
100
+ `useInfiniteItemsSearch(query, options?)` accepts `pageSize`, defaulting to `20`. It returns a deduplicated flat `data` array plus `total`, `loadMore`, `hasMore`, `isLoadingMore`, `debouncedQuery`, `resultQuery`, and `isIdle`, along with standard infinite-query fields.
101
+
102
+ Prefer an explicit, accessible “Load more” button unless the prototype specifically calls for automatic infinite scrolling. Do not call `loadMore` when `hasMore` is false or while `isLoadingMore` is true.
103
+
104
+ ## Item details
105
+
106
+ ```tsx
107
+ 'use client'
108
+
109
+ import { useItemById } from '@ebay-dt/bonfire-preset-ebay/data/items/react'
110
+
111
+ export function ItemDetails({ itemId }: { itemId: string | null }) {
112
+ const detail = useItemById(itemId)
113
+
114
+ if (!itemId) return null
115
+ if (detail.isPending) return <p role="status">Loading item details…</p>
116
+ if (detail.isError) {
117
+ return (
118
+ <div role="alert">
119
+ <p>{detail.error.message}</p>
120
+ <button type="button" onClick={() => detail.refetch()}>
121
+ Try again
122
+ </button>
123
+ </div>
124
+ )
125
+ }
126
+
127
+ return detail.data ? <h2>{detail.data.title}</h2> : null
128
+ }
129
+ ```
130
+
131
+ `useItemById(itemId, options?)` accepts a string, `null`, or `undefined`. It does not fetch without an ID. Options are:
132
+
133
+ - `enabled` — defaults to `true`; combined with the presence of an ID
134
+ - `staleTime` — defaults to 30 seconds
135
+
136
+ Use the selected search item's summary as immediate fallback content where helpful, then replace or augment it with detail data. Do not assume summary-only fields and detail fields are identical.
137
+
138
+ ## Maintaining this skill
139
+
140
+ Whenever the supported package version or item-data API changes:
141
+
142
+ 1. Update the hook inventory and relevant guidance here.
143
+ 2. Keep examples aligned with the published exports and return shapes.
144
+ 3. Keep the short pointer in `context/PROTOTYPE.md` accurate, then run `pnpm context:sync` from the repository root to regenerate `AGENTS.md` and the skill projections.
145
+ 4. Prefer adding related item hooks to this skill rather than creating a skill for each hook.
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: setup-evomcp
3
+ description: Set up the hosted Evo MCP server for Cursor or Claude Code in the design sandbox. Registers the unauthenticated remote endpoint for prototype-only sessions and verifies that its design-system tools are available. Use when Evo MCP tools are missing, when onboarding to the design sandbox, or when the user mentions setting up evomcp, Evo MCP, or Evo design system tools.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Set up Evo MCP
8
+
9
+ Evo MCP is hosted at `https://evomcp2.vip.qa.ebay.com/mcp`. It does not require
10
+ user authentication, a local `evomcp` checkout, Yarn, or a local server build.
11
+
12
+ ## Compatibility
13
+
14
+ - **Claude Code:** the committed repo `.mcp.json` is sufficient. Claude Code
15
+ discovers it through the git root, including from a prototype directory.
16
+ Approve the project server if prompted, then verify its tools.
17
+ - **Cursor:** prototype directories are opened as workspace roots, so the repo
18
+ `.mcp.json` is not visible to those sessions. Register the same definition in
19
+ Cursor's global MCP config using the workflow below.
20
+
21
+ ## Cursor workflow
22
+
23
+ ```
24
+ - [ ] Step 1: Locate the sandbox repo
25
+ - [ ] Step 2: Register the hosted server
26
+ - [ ] Step 3: Approve or enable it in Cursor
27
+ - [ ] Step 4: Verify the tools
28
+ ```
29
+
30
+ ### Step 1: Locate the sandbox repo
31
+
32
+ Find `design-sandbox-next`, which contains `context/mcp.json`, the canonical
33
+ MCP definition.
34
+
35
+ ### Step 2: Register the hosted server
36
+
37
+ ```bash
38
+ cd <design-sandbox-next> && pnpm context:install
39
+ ```
40
+
41
+ This merges the committed HTTP server definition into `~/.cursor/mcp.json` and
42
+ installs the shared skills. No token or other secret is written to disk.
43
+
44
+ ### Step 3: Approve or enable it
45
+
46
+ If Cursor does not show the server, run `Cmd+Shift+P` → **View: Open MCP
47
+ Settings** and toggle `evomcp` on.
48
+
49
+ A server discovered only by editing the config file may initially be disabled.
50
+ Installation is once per machine because the global config gives the hosted
51
+ server a stable identity from every prototype folder.
52
+
53
+ ### Step 4: Verify
54
+
55
+ Confirm the `evomcp` entry points to:
56
+
57
+ ```text
58
+ https://evomcp2.vip.qa.ebay.com/mcp
59
+ ```
60
+
61
+ Then list or call the server's tools. A working connection exposes
62
+ `search-components`, `search-tokens`, `get-token`, and `search-icons`, among
63
+ others. If the endpoint is reachable but Cursor does not show the tools, reload
64
+ the Cursor window and check MCP settings again.
65
+
66
+ ## Troubleshooting
67
+
68
+ - The endpoint uses Streamable HTTP and expects POST requests. A GET response
69
+ saying SSE streams are unsupported does not indicate an authentication
70
+ failure.
71
+ - No Evo-specific login is required. Do not add authorization headers or ask
72
+ the user to generate a token.
73
+ - If the host cannot be reached, check eBay network/BPN connectivity and retry.
@@ -0,0 +1,10 @@
1
+ import type { NextConfig } from 'next'
2
+
3
+ const nextConfig: NextConfig = {
4
+ /* Bonfire discovers prototype metadata from the filesystem at request time. */
5
+ outputFileTracingIncludes: {
6
+ '/*': ['./src/app/(prototypes)/**/metadata.json'],
7
+ },
8
+ }
9
+
10
+ export default nextConfig
@@ -0,0 +1,15 @@
1
+ 'use server'
2
+
3
+ import type {
4
+ OpenInAgentInput,
5
+ OpenInClaudeCodeInput,
6
+ } from '@ebay-dt/bonfire-core'
7
+ import { bonfire } from '@/bonfire'
8
+
9
+ export async function openInCursor(input: OpenInAgentInput) {
10
+ return bonfire.prototypes.agents.openInCursor(input)
11
+ }
12
+
13
+ export async function openInClaudeCode(input: OpenInClaudeCodeInput) {
14
+ return bonfire.prototypes.agents.openInClaudeCode(input)
15
+ }