From ab5836f63300b89669f4f872b023d105f7fda956 Mon Sep 17 00:00:00 2001 From: Nelson Wittwer Date: Wed, 10 Jun 2026 10:05:55 -0400 Subject: [PATCH 1/5] Add `agent-search` command; deprecate `search` Introduce `shopify agent-search`, which queries the shopify.dev vector store and prints the most relevant documentation chunks as JSON to stdout (filters: --api-name, --api-version). This is the non-breaking counterpart to repurposing `search` itself: existing `shopify search` behavior is preserved (it still opens shopify.dev in the browser) but is now marked deprecated, so oclif emits a runtime warning pointing users to `agent-search` and the command is hidden from listings. Co-Authored-By: Claude Opus 4.8 (1M context) --- .changeset/add-agent-search-command.md | 7 ++ .../interfaces/agent-search.interface.ts | 30 +++++++ .../generated/generated_docs_data_v2.json | 47 +++++++++++ packages/cli/README.md | 32 ++++++++ packages/cli/oclif.manifest.json | 63 +++++++++++++++ packages/cli/src/cli/commands/agent-search.ts | 46 +++++++++++ packages/cli/src/cli/commands/search.test.ts | 18 +++++ packages/cli/src/cli/commands/search.ts | 6 ++ .../services/commands/agent-search.test.ts | 78 +++++++++++++++++++ .../src/cli/services/commands/agent-search.ts | 32 ++++++++ packages/cli/src/index.ts | 2 + packages/e2e/data/snapshots/commands.txt | 2 +- 12 files changed, 362 insertions(+), 1 deletion(-) create mode 100644 .changeset/add-agent-search-command.md create mode 100644 docs-shopify.dev/commands/interfaces/agent-search.interface.ts create mode 100644 packages/cli/src/cli/commands/agent-search.ts create mode 100644 packages/cli/src/cli/commands/search.test.ts create mode 100644 packages/cli/src/cli/services/commands/agent-search.test.ts create mode 100644 packages/cli/src/cli/services/commands/agent-search.ts diff --git a/.changeset/add-agent-search-command.md b/.changeset/add-agent-search-command.md new file mode 100644 index 00000000000..5258442e6c2 --- /dev/null +++ b/.changeset/add-agent-search-command.md @@ -0,0 +1,7 @@ +--- +'@shopify/cli': minor +--- + +Add `shopify agent-search`, which queries the shopify.dev vector store and prints the most relevant documentation chunks as JSON to stdout. This makes it usable for programmatic and agent-driven discovery, and supports two optional filters: `--api-name` (for example `admin`, `storefront`, `hydrogen`) and `--api-version` (for example `2025-10`, `latest`, `current`). To download a full document verbatim, use `fetch-doc`. + +`shopify search` is now deprecated. Its behavior is unchanged — it still opens shopify.dev in your browser — but it prints a deprecation notice pointing to `agent-search`. diff --git a/docs-shopify.dev/commands/interfaces/agent-search.interface.ts b/docs-shopify.dev/commands/interfaces/agent-search.interface.ts new file mode 100644 index 00000000000..a2255ba195c --- /dev/null +++ b/docs-shopify.dev/commands/interfaces/agent-search.interface.ts @@ -0,0 +1,30 @@ +// This is an autogenerated file. Don't edit this file manually. +/** + * The following flags are available for the `agent-search` command: + * @publicDocs + */ +export interface agentsearch { + /** + * Limit results to a specific API (for example: admin, storefront, hydrogen, functions). Unrecognized values are ignored. + * @environment SHOPIFY_FLAG_API_NAME + */ + '--api-name '?: string + + /** + * Limit results to a specific API version (for example: 2025-10, latest, current). + * @environment SHOPIFY_FLAG_API_VERSION + */ + '--api-version '?: string + + /** + * Disable color output. + * @environment SHOPIFY_FLAG_NO_COLOR + */ + '--no-color'?: '' + + /** + * Increase the verbosity of the output. + * @environment SHOPIFY_FLAG_VERBOSE + */ + '--verbose'?: '' +} diff --git a/docs-shopify.dev/generated/generated_docs_data_v2.json b/docs-shopify.dev/generated/generated_docs_data_v2.json index 613051d8d7d..5c27929fda8 100644 --- a/docs-shopify.dev/generated/generated_docs_data_v2.json +++ b/docs-shopify.dev/generated/generated_docs_data_v2.json @@ -1,4 +1,51 @@ { + "agentsearch": { + "docs-shopify.dev/commands/interfaces/agent-search.interface.ts": { + "filePath": "docs-shopify.dev/commands/interfaces/agent-search.interface.ts", + "name": "agentsearch", + "description": "The following flags are available for the `agent-search` command:", + "isPublicDocs": true, + "members": [ + { + "filePath": "docs-shopify.dev/commands/interfaces/agent-search.interface.ts", + "syntaxKind": "PropertySignature", + "name": "--api-name ", + "value": "string", + "description": "Limit results to a specific API (for example: admin, storefront, hydrogen, functions). Unrecognized values are ignored.", + "isOptional": true, + "environmentValue": "SHOPIFY_FLAG_API_NAME" + }, + { + "filePath": "docs-shopify.dev/commands/interfaces/agent-search.interface.ts", + "syntaxKind": "PropertySignature", + "name": "--api-version ", + "value": "string", + "description": "Limit results to a specific API version (for example: 2025-10, latest, current).", + "isOptional": true, + "environmentValue": "SHOPIFY_FLAG_API_VERSION" + }, + { + "filePath": "docs-shopify.dev/commands/interfaces/agent-search.interface.ts", + "syntaxKind": "PropertySignature", + "name": "--no-color", + "value": "''", + "description": "Disable color output.", + "isOptional": true, + "environmentValue": "SHOPIFY_FLAG_NO_COLOR" + }, + { + "filePath": "docs-shopify.dev/commands/interfaces/agent-search.interface.ts", + "syntaxKind": "PropertySignature", + "name": "--verbose", + "value": "''", + "description": "Increase the verbosity of the output.", + "isOptional": true, + "environmentValue": "SHOPIFY_FLAG_VERBOSE" + } + ], + "value": "export interface agentsearch {\n /**\n * Limit results to a specific API (for example: admin, storefront, hydrogen, functions). Unrecognized values are ignored.\n * @environment SHOPIFY_FLAG_API_NAME\n */\n '--api-name '?: string\n\n /**\n * Limit results to a specific API version (for example: 2025-10, latest, current).\n * @environment SHOPIFY_FLAG_API_VERSION\n */\n '--api-version '?: string\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" + } + }, "appbuild": { "docs-shopify.dev/commands/interfaces/app-build.interface.ts": { "filePath": "docs-shopify.dev/commands/interfaces/app-build.interface.ts", diff --git a/packages/cli/README.md b/packages/cli/README.md index e4853505068..ca76e0e868e 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -1,5 +1,6 @@ # Commands +* [`shopify agent-search [query]`](#shopify-agent-search-query) * [`shopify app build`](#shopify-app-build) * [`shopify app bulk cancel`](#shopify-app-bulk-cancel) * [`shopify app bulk execute`](#shopify-app-bulk-execute) @@ -100,6 +101,37 @@ * [`shopify upgrade`](#shopify-upgrade) * [`shopify version`](#shopify-version) +## `shopify agent-search [query]` + +Query the shopify.dev vector store and print the most relevant documentation chunks as JSON. Best for programmatic discovery — surfacing the relevant pieces of documentation for a topic, rather than retrieving a whole document. To download a full document verbatim, use `fetch-doc`. + +``` +USAGE + $ shopify agent-search [query] + +ARGUMENTS + QUERY The search query. + +FLAGS + --api-name= [env: SHOPIFY_FLAG_API_NAME] Limit results to a specific API (for example: admin, storefront, + hydrogen, functions). Unrecognized values are ignored. + --api-version= [env: SHOPIFY_FLAG_API_VERSION] Limit results to a specific API version (for example: 2025-10, + latest, current). + --no-color [env: SHOPIFY_FLAG_NO_COLOR] Disable color output. + --verbose [env: SHOPIFY_FLAG_VERBOSE] Increase the verbosity of the output. + +DESCRIPTION + Query the shopify.dev vector store and print the most relevant documentation chunks as JSON. Best for programmatic + discovery — surfacing the relevant pieces of documentation for a topic, rather than retrieving a whole document. To + download a full document verbatim, use `fetch-doc`. + +EXAMPLES + # search shopify.dev for a topic + shopify agent-search "subscribe to webhooks" + # narrow the search to a specific API and version + shopify agent-search "create a product" --api-name admin --api-version latest +``` + ## `shopify app build` Build the app, including extensions. diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index 48d3bc25cdf..97c9d295e9a 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -1,5 +1,64 @@ { "commands": { + "agent-search": { + "aliases": [ + ], + "args": { + "query": { + "description": "The search query.", + "name": "query", + "required": true + } + }, + "description": "Query the shopify.dev vector store and print the most relevant documentation chunks as JSON. Best for programmatic discovery — surfacing the relevant pieces of documentation for a topic, rather than retrieving a whole document. To download a full document verbatim, use `fetch-doc`.", + "enableJsonFlag": false, + "examples": [ + "# search shopify.dev for a topic\n shopify agent-search \"subscribe to webhooks\"\n\n # narrow the search to a specific API and version\n shopify agent-search \"create a product\" --api-name admin --api-version latest\n " + ], + "flags": { + "api-name": { + "description": "Limit results to a specific API (for example: admin, storefront, hydrogen, functions). Unrecognized values are ignored.", + "env": "SHOPIFY_FLAG_API_NAME", + "hasDynamicHelp": false, + "multiple": false, + "name": "api-name", + "type": "option" + }, + "api-version": { + "description": "Limit results to a specific API version (for example: 2025-10, latest, current).", + "env": "SHOPIFY_FLAG_API_VERSION", + "hasDynamicHelp": false, + "multiple": false, + "name": "api-version", + "type": "option" + }, + "no-color": { + "allowNo": false, + "description": "Disable color output.", + "env": "SHOPIFY_FLAG_NO_COLOR", + "hidden": false, + "name": "no-color", + "type": "boolean" + }, + "verbose": { + "allowNo": false, + "description": "Increase the verbosity of the output.", + "env": "SHOPIFY_FLAG_VERBOSE", + "hidden": false, + "name": "verbose", + "type": "boolean" + } + }, + "hasDynamicHelp": false, + "hiddenAliases": [ + ], + "id": "agent-search", + "pluginAlias": "@shopify/cli", + "pluginName": "@shopify/cli", + "pluginType": "core", + "strict": true, + "usage": "agent-search [query]" + }, "app:build": { "aliases": [ ], @@ -5652,6 +5711,9 @@ "name": "query" } }, + "deprecationOptions": { + "to": "agent-search" + }, "description": "Starts a search on shopify.dev.", "enableJsonFlag": false, "examples": [ @@ -5666,6 +5728,7 @@ "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "state": "deprecated", "strict": true, "usage": "search [query]" }, diff --git a/packages/cli/src/cli/commands/agent-search.ts b/packages/cli/src/cli/commands/agent-search.ts new file mode 100644 index 00000000000..94f835c01c6 --- /dev/null +++ b/packages/cli/src/cli/commands/agent-search.ts @@ -0,0 +1,46 @@ +import {agentSearchService} from '../services/commands/agent-search.js' +import Command from '@shopify/cli-kit/node/base-command' +import {globalFlags} from '@shopify/cli-kit/node/cli' +import {Args, Flags} from '@oclif/core' + +export default class AgentSearch extends Command { + static description = + 'Query the shopify.dev vector store and print the most relevant documentation chunks as JSON. Best for programmatic discovery — surfacing the relevant pieces of documentation for a topic, rather than retrieving a whole document. To download a full document verbatim, use `fetch-doc`.' + + static usage = `agent-search [query]` + + static examples = [ + `# search shopify.dev for a topic + shopify agent-search "subscribe to webhooks" + + # narrow the search to a specific API and version + shopify agent-search "create a product" --api-name admin --api-version latest + `, + ] + + static args = { + query: Args.string({ + name: 'query', + required: true, + description: 'The search query.', + }), + } + + static flags = { + ...globalFlags, + 'api-name': Flags.string({ + description: + 'Limit results to a specific API (for example: admin, storefront, hydrogen, functions). Unrecognized values are ignored.', + env: 'SHOPIFY_FLAG_API_NAME', + }), + 'api-version': Flags.string({ + description: 'Limit results to a specific API version (for example: 2025-10, latest, current).', + env: 'SHOPIFY_FLAG_API_VERSION', + }), + } + + async run(): Promise { + const {args, flags} = await this.parse(AgentSearch) + await agentSearchService(args.query, flags['api-name'], flags['api-version']) + } +} diff --git a/packages/cli/src/cli/commands/search.test.ts b/packages/cli/src/cli/commands/search.test.ts new file mode 100644 index 00000000000..f40f9591618 --- /dev/null +++ b/packages/cli/src/cli/commands/search.test.ts @@ -0,0 +1,18 @@ +import Search from './search.js' +import {searchService} from '../services/commands/search.js' +import {describe, expect, test, vi} from 'vitest' + +vi.mock('../services/commands/search.js') + +describe('search command', () => { + test('is marked deprecated and points users to agent-search', () => { + expect(Search.state).toBe('deprecated') + expect(Search.deprecationOptions).toEqual({to: 'agent-search'}) + }) + + test('still runs the browser search service (behavior is unchanged)', async () => { + await Search.run(['webhooks'], import.meta.url) + + expect(searchService).toHaveBeenCalledWith('webhooks') + }) +}) diff --git a/packages/cli/src/cli/commands/search.ts b/packages/cli/src/cli/commands/search.ts index bb1346b8341..e6e2c51b7ff 100644 --- a/packages/cli/src/cli/commands/search.ts +++ b/packages/cli/src/cli/commands/search.ts @@ -5,6 +5,12 @@ import {Args} from '@oclif/core' export default class Search extends Command { static description = 'Starts a search on shopify.dev.' + // Deprecated in favor of `agent-search`, which returns JSON for programmatic use. + // The browser behavior below is intentionally preserved so existing usage doesn't + // break; oclif emits a runtime deprecation warning pointing to `agent-search`. + static state = 'deprecated' + static deprecationOptions = {to: 'agent-search'} + static usage = `search [query]` static examples = [ diff --git a/packages/cli/src/cli/services/commands/agent-search.test.ts b/packages/cli/src/cli/services/commands/agent-search.test.ts new file mode 100644 index 00000000000..3ed4c20c123 --- /dev/null +++ b/packages/cli/src/cli/services/commands/agent-search.test.ts @@ -0,0 +1,78 @@ +import {agentSearchService} from './agent-search.js' +import {describe, expect, test, vi, beforeEach} from 'vitest' +import {fetch} from '@shopify/cli-kit/node/http' +import {outputResult} from '@shopify/cli-kit/node/output' +import {AbortError} from '@shopify/cli-kit/node/error' + +vi.mock('@shopify/cli-kit/node/http') +// Only stub `outputResult`; keep the rest of the module real. Blanket-mocking it +// would also mock `stringifyMessage`, which `AbortError`'s constructor relies on — +// that would silently empty out every thrown error message. +vi.mock('@shopify/cli-kit/node/output', async (importOriginal) => ({ + ...(await importOriginal()), + outputResult: vi.fn(), +})) + +const okResponse = (body: string) => + ({ok: true, status: 200, statusText: 'OK', text: () => Promise.resolve(body)}) as any + +const errorResponse = (status: number, statusText: string, body: string) => + ({ok: false, status, statusText, text: () => Promise.resolve(body)}) as any + +const resultsBody = + '[{"score":0.99,"content":"About webhooks","url":"https://shopify.dev/x","title":"Webhooks","domain":null}]' + +beforeEach(() => { + vi.mocked(fetch).mockResolvedValue(okResponse(resultsBody)) +}) + +describe('agentSearchService', () => { + test('requests the search endpoint with the query and prints the raw JSON body', async () => { + await agentSearchService('webhooks') + + expect(fetch).toHaveBeenCalledWith('https://shopify.dev/assistant/search?query=webhooks', { + headers: {Accept: 'application/json'}, + }) + expect(outputResult).toHaveBeenCalledWith(resultsBody) + }) + + test('includes api_name and api_version params when provided', async () => { + await agentSearchService('create a product', 'admin', 'latest') + + expect(fetch).toHaveBeenCalledWith( + 'https://shopify.dev/assistant/search?query=create+a+product&api_name=admin&api_version=latest', + {headers: {Accept: 'application/json'}}, + ) + }) + + test('URL-encodes queries with spaces and special characters', async () => { + await agentSearchService('a & b?') + + expect(fetch).toHaveBeenCalledWith('https://shopify.dev/assistant/search?query=a+%26+b%3F', { + headers: {Accept: 'application/json'}, + }) + }) + + test('surfaces the server error message from a non-ok JSON response', async () => { + vi.mocked(fetch).mockResolvedValue( + errorResponse( + 400, + 'Bad Request', + '{"error":"Invalid api_version \'2025-01\' for api_name \'admin\'. Available versions: 2026-07"}', + ), + ) + + await expect(agentSearchService('products', 'admin', '2025-01')).rejects.toThrowError( + /Invalid api_version '2025-01' for api_name 'admin'\. Available versions: 2026-07/, + ) + expect(outputResult).not.toHaveBeenCalled() + }) + + test('falls back to the status line when a non-ok response is not JSON', async () => { + vi.mocked(fetch).mockResolvedValue(errorResponse(500, 'Internal Server Error', 'nope')) + + await expect(agentSearchService('products')).rejects.toThrowError(AbortError) + await expect(agentSearchService('products')).rejects.toThrowError(/500 Internal Server Error/) + expect(outputResult).not.toHaveBeenCalled() + }) +}) diff --git a/packages/cli/src/cli/services/commands/agent-search.ts b/packages/cli/src/cli/services/commands/agent-search.ts new file mode 100644 index 00000000000..67229a2feaa --- /dev/null +++ b/packages/cli/src/cli/services/commands/agent-search.ts @@ -0,0 +1,32 @@ +import {fetch} from '@shopify/cli-kit/node/http' +import {outputResult} from '@shopify/cli-kit/node/output' +import {AbortError} from '@shopify/cli-kit/node/error' + +// The dev-assistant search endpoint queries the shopify.dev vector store and +// returns an array of matching documentation chunks as JSON. +const SEARCH_URL = 'https://shopify.dev/assistant/search' + +export async function agentSearchService(query: string, apiName?: string, apiVersion?: string) { + const params = new URLSearchParams({query}) + if (apiName) params.append('api_name', apiName) + if (apiVersion) params.append('api_version', apiVersion) + + const response = await fetch(`${SEARCH_URL}?${params.toString()}`, {headers: {Accept: 'application/json'}}) + const body = await response.text() + + if (!response.ok) { + // The endpoint returns a JSON `{error}` body for 400s (e.g. an invalid api_version + // lists the valid versions) — surface it directly instead of a bare status code. + let message = `${response.status} ${response.statusText}` + try { + const parsed = JSON.parse(body) + if (parsed?.error) message = parsed.error + } catch (parseError) { + // Body wasn't JSON; fall back to the status line. Rethrow anything unexpected. + if (!(parseError instanceof SyntaxError)) throw parseError + } + throw new AbortError(`Search failed: ${message}`) + } + + outputResult(body) +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index ff506419c63..aaa4882f7b2 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,5 +1,6 @@ import VersionCommand from './cli/commands/version.js' import Search from './cli/commands/search.js' +import AgentSearch from './cli/commands/agent-search.js' import Upgrade from './cli/commands/upgrade.js' import Logout from './cli/commands/auth/logout.js' import Login from './cli/commands/auth/login.js' @@ -145,6 +146,7 @@ export const COMMANDS: any = { ...HydrogenCommands, ...StoreCommands, search: Search, + 'agent-search': AgentSearch, upgrade: Upgrade, version: VersionCommand, help: HelpCommand, diff --git a/packages/e2e/data/snapshots/commands.txt b/packages/e2e/data/snapshots/commands.txt index 9e9afb6c946..03e54d3095f 100644 --- a/packages/e2e/data/snapshots/commands.txt +++ b/packages/e2e/data/snapshots/commands.txt @@ -1,3 +1,4 @@ +├─ agent-search ├─ app │ ├─ build │ ├─ bulk @@ -91,7 +92,6 @@ │ ├─ uninstall │ ├─ unlink │ └─ update -├─ search ├─ store │ ├─ auth │ └─ execute From a86cb0fd4180acfe7be101da163fd50c5c5fdc2c Mon Sep 17 00:00:00 2001 From: Nelson Wittwer Date: Wed, 10 Jun 2026 10:19:53 -0400 Subject: [PATCH 2/5] Fix `search` URL instead of deprecating it Rather than deprecating `shopify search`, restore the original browser experience by opening `https://shopify.dev/?search=`. The command is no longer marked deprecated, so it stays in the command listings and behaves as it did before. `agent-search` remains the JSON-returning command for programmatic/agent discovery. Co-Authored-By: Claude Opus 4.8 (1M context) --- .changeset/add-agent-search-command.md | 2 +- packages/cli/oclif.manifest.json | 4 ---- packages/cli/src/cli/commands/search.test.ts | 18 ------------------ packages/cli/src/cli/commands/search.ts | 6 ------ .../src/cli/services/commands/search.test.ts | 4 ++-- .../cli/src/cli/services/commands/search.ts | 2 +- packages/e2e/data/snapshots/commands.txt | 1 + 7 files changed, 5 insertions(+), 32 deletions(-) delete mode 100644 packages/cli/src/cli/commands/search.test.ts diff --git a/.changeset/add-agent-search-command.md b/.changeset/add-agent-search-command.md index 5258442e6c2..0623f73b88c 100644 --- a/.changeset/add-agent-search-command.md +++ b/.changeset/add-agent-search-command.md @@ -4,4 +4,4 @@ Add `shopify agent-search`, which queries the shopify.dev vector store and prints the most relevant documentation chunks as JSON to stdout. This makes it usable for programmatic and agent-driven discovery, and supports two optional filters: `--api-name` (for example `admin`, `storefront`, `hydrogen`) and `--api-version` (for example `2025-10`, `latest`, `current`). To download a full document verbatim, use `fetch-doc`. -`shopify search` is now deprecated. Its behavior is unchanged — it still opens shopify.dev in your browser — but it prints a deprecation notice pointing to `agent-search`. +Also fixes `shopify search` to open `https://shopify.dev/?search=` so it once again opens shopify.dev with your query. diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index 97c9d295e9a..34e9005b7af 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -5711,9 +5711,6 @@ "name": "query" } }, - "deprecationOptions": { - "to": "agent-search" - }, "description": "Starts a search on shopify.dev.", "enableJsonFlag": false, "examples": [ @@ -5728,7 +5725,6 @@ "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", - "state": "deprecated", "strict": true, "usage": "search [query]" }, diff --git a/packages/cli/src/cli/commands/search.test.ts b/packages/cli/src/cli/commands/search.test.ts deleted file mode 100644 index f40f9591618..00000000000 --- a/packages/cli/src/cli/commands/search.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Search from './search.js' -import {searchService} from '../services/commands/search.js' -import {describe, expect, test, vi} from 'vitest' - -vi.mock('../services/commands/search.js') - -describe('search command', () => { - test('is marked deprecated and points users to agent-search', () => { - expect(Search.state).toBe('deprecated') - expect(Search.deprecationOptions).toEqual({to: 'agent-search'}) - }) - - test('still runs the browser search service (behavior is unchanged)', async () => { - await Search.run(['webhooks'], import.meta.url) - - expect(searchService).toHaveBeenCalledWith('webhooks') - }) -}) diff --git a/packages/cli/src/cli/commands/search.ts b/packages/cli/src/cli/commands/search.ts index e6e2c51b7ff..bb1346b8341 100644 --- a/packages/cli/src/cli/commands/search.ts +++ b/packages/cli/src/cli/commands/search.ts @@ -5,12 +5,6 @@ import {Args} from '@oclif/core' export default class Search extends Command { static description = 'Starts a search on shopify.dev.' - // Deprecated in favor of `agent-search`, which returns JSON for programmatic use. - // The browser behavior below is intentionally preserved so existing usage doesn't - // break; oclif emits a runtime deprecation warning pointing to `agent-search`. - static state = 'deprecated' - static deprecationOptions = {to: 'agent-search'} - static usage = `search [query]` static examples = [ diff --git a/packages/cli/src/cli/services/commands/search.test.ts b/packages/cli/src/cli/services/commands/search.test.ts index 31c2ca30fd1..c211b7c7c5d 100644 --- a/packages/cli/src/cli/services/commands/search.test.ts +++ b/packages/cli/src/cli/services/commands/search.test.ts @@ -8,12 +8,12 @@ describe('searchService', () => { test('the right URL is open in the system when a query is passed', async () => { await searchService('deploy app') - expect(openURL).toBeCalledWith('https://shopify.dev?search=deploy+app') + expect(openURL).toBeCalledWith('https://shopify.dev/?search=deploy+app') }) test('the right URL is open in the system when a query is not passed', async () => { await searchService() - expect(openURL).toBeCalledWith('https://shopify.dev?search=') + expect(openURL).toBeCalledWith('https://shopify.dev/?search=') }) }) diff --git a/packages/cli/src/cli/services/commands/search.ts b/packages/cli/src/cli/services/commands/search.ts index 23109da229b..f4cbf8878a8 100644 --- a/packages/cli/src/cli/services/commands/search.ts +++ b/packages/cli/src/cli/services/commands/search.ts @@ -3,5 +3,5 @@ import {openURL} from '@shopify/cli-kit/node/system' export async function searchService(query?: string) { const searchParams = new URLSearchParams() searchParams.append('search', query ?? '') - await openURL(`https://shopify.dev?${searchParams.toString()}`) + await openURL(`https://shopify.dev/?${searchParams.toString()}`) } diff --git a/packages/e2e/data/snapshots/commands.txt b/packages/e2e/data/snapshots/commands.txt index 03e54d3095f..0fdf5e7605a 100644 --- a/packages/e2e/data/snapshots/commands.txt +++ b/packages/e2e/data/snapshots/commands.txt @@ -92,6 +92,7 @@ │ ├─ uninstall │ ├─ unlink │ └─ update +├─ search ├─ store │ ├─ auth │ └─ execute From 4b8aac857635d1b0e6dce8e29902a34613cd1d09 Mon Sep 17 00:00:00 2001 From: Nelson Wittwer Date: Wed, 10 Jun 2026 10:29:28 -0400 Subject: [PATCH 3/5] Point `search` at /docs directly to avoid the 301 `https://shopify.dev/?search=` 301-redirects to `https://shopify.dev/docs?search=`. Open that destination directly so the browser doesn't follow a redirect. Co-Authored-By: Claude Opus 4.8 (1M context) --- .changeset/add-agent-search-command.md | 2 +- packages/cli/src/cli/services/commands/search.test.ts | 4 ++-- packages/cli/src/cli/services/commands/search.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.changeset/add-agent-search-command.md b/.changeset/add-agent-search-command.md index 0623f73b88c..cb6a52cbfcc 100644 --- a/.changeset/add-agent-search-command.md +++ b/.changeset/add-agent-search-command.md @@ -4,4 +4,4 @@ Add `shopify agent-search`, which queries the shopify.dev vector store and prints the most relevant documentation chunks as JSON to stdout. This makes it usable for programmatic and agent-driven discovery, and supports two optional filters: `--api-name` (for example `admin`, `storefront`, `hydrogen`) and `--api-version` (for example `2025-10`, `latest`, `current`). To download a full document verbatim, use `fetch-doc`. -Also fixes `shopify search` to open `https://shopify.dev/?search=` so it once again opens shopify.dev with your query. +Also fixes `shopify search` to open `https://shopify.dev/docs?search=` directly (avoiding the redirect from the site root) so it once again opens shopify.dev with your query. diff --git a/packages/cli/src/cli/services/commands/search.test.ts b/packages/cli/src/cli/services/commands/search.test.ts index c211b7c7c5d..5233abaed2f 100644 --- a/packages/cli/src/cli/services/commands/search.test.ts +++ b/packages/cli/src/cli/services/commands/search.test.ts @@ -8,12 +8,12 @@ describe('searchService', () => { test('the right URL is open in the system when a query is passed', async () => { await searchService('deploy app') - expect(openURL).toBeCalledWith('https://shopify.dev/?search=deploy+app') + expect(openURL).toBeCalledWith('https://shopify.dev/docs?search=deploy+app') }) test('the right URL is open in the system when a query is not passed', async () => { await searchService() - expect(openURL).toBeCalledWith('https://shopify.dev/?search=') + expect(openURL).toBeCalledWith('https://shopify.dev/docs?search=') }) }) diff --git a/packages/cli/src/cli/services/commands/search.ts b/packages/cli/src/cli/services/commands/search.ts index f4cbf8878a8..7c5c82e47dc 100644 --- a/packages/cli/src/cli/services/commands/search.ts +++ b/packages/cli/src/cli/services/commands/search.ts @@ -3,5 +3,5 @@ import {openURL} from '@shopify/cli-kit/node/system' export async function searchService(query?: string) { const searchParams = new URLSearchParams() searchParams.append('search', query ?? '') - await openURL(`https://shopify.dev/?${searchParams.toString()}`) + await openURL(`https://shopify.dev/docs?${searchParams.toString()}`) } From 0380486c1b4d46c3c27a95eaf831f819eb4b0369 Mon Sep 17 00:00:00 2001 From: Nelson Wittwer Date: Wed, 10 Jun 2026 10:31:43 -0400 Subject: [PATCH 4/5] Clarify `search` is a browser command for human use Update the description to state that `search` opens shopify.dev in the browser for interactive human use, and point agents to `agent-search` for JSON results. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/cli/README.md | 6 ++++-- packages/cli/oclif.manifest.json | 2 +- packages/cli/src/cli/commands/search.ts | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index ca76e0e868e..fbd9a8cb197 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -2115,14 +2115,16 @@ DESCRIPTION ## `shopify search [query]` -Starts a search on shopify.dev. +Opens shopify.dev in your browser to search the documentation using the on-site search. Intended for interactive, human use. If you are an agent or need results programmatically, use `agent-search` instead, which returns the matching documentation as JSON. ``` USAGE $ shopify search [query] DESCRIPTION - Starts a search on shopify.dev. + Opens shopify.dev in your browser to search the documentation using the on-site search. Intended for interactive, + human use. If you are an agent or need results programmatically, use `agent-search` instead, which returns the + matching documentation as JSON. EXAMPLES # open the search modal on Shopify.dev diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index 34e9005b7af..1fa3aea87c4 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -5711,7 +5711,7 @@ "name": "query" } }, - "description": "Starts a search on shopify.dev.", + "description": "Opens shopify.dev in your browser to search the documentation using the on-site search. Intended for interactive, human use. If you are an agent or need results programmatically, use `agent-search` instead, which returns the matching documentation as JSON.", "enableJsonFlag": false, "examples": [ "# open the search modal on Shopify.dev\n shopify search\n\n # search for a term on Shopify.dev\n shopify search \n\n # search for a phrase on Shopify.dev\n shopify search \"\"\n " diff --git a/packages/cli/src/cli/commands/search.ts b/packages/cli/src/cli/commands/search.ts index bb1346b8341..5161411afe1 100644 --- a/packages/cli/src/cli/commands/search.ts +++ b/packages/cli/src/cli/commands/search.ts @@ -3,7 +3,8 @@ import Command from '@shopify/cli-kit/node/base-command' import {Args} from '@oclif/core' export default class Search extends Command { - static description = 'Starts a search on shopify.dev.' + static description = + 'Opens shopify.dev in your browser to search the documentation using the on-site search. Intended for interactive, human use. If you are an agent or need results programmatically, use `agent-search` instead, which returns the matching documentation as JSON.' static usage = `search [query]` From e389dbee1f8c7ee5913ed5be3aa566ba8b8497ee Mon Sep 17 00:00:00 2001 From: Nelson Wittwer Date: Wed, 10 Jun 2026 14:38:03 -0400 Subject: [PATCH 5/5] Deprecate `search`; drop top-level `agent-search` Per the doc-namespace plan, the JSON search will live at `doc search` (added in a separate PR), so this PR no longer introduces a top-level `agent-search` command. Instead it: - keeps the `search` URL fix (opens https://shopify.dev/docs?search= directly, no redirect), and - marks `search` deprecated with `deprecationOptions = {to: 'doc search'}`, so oclif warns users at runtime and the command is hidden from listings while still working as a browser search for humans. Co-Authored-By: Claude Opus 4.8 (1M context) --- .changeset/add-agent-search-command.md | 7 -- .changeset/deprecate-and-fix-search.md | 5 ++ .../interfaces/agent-search.interface.ts | 30 ------- .../generated/generated_docs_data_v2.json | 47 ----------- packages/cli/README.md | 38 +-------- packages/cli/oclif.manifest.json | 65 ++-------------- packages/cli/src/cli/commands/agent-search.ts | 46 ----------- packages/cli/src/cli/commands/search.ts | 8 +- .../services/commands/agent-search.test.ts | 78 ------------------- .../src/cli/services/commands/agent-search.ts | 32 -------- packages/cli/src/index.ts | 2 - packages/e2e/data/snapshots/commands.txt | 2 - 12 files changed, 20 insertions(+), 340 deletions(-) delete mode 100644 .changeset/add-agent-search-command.md create mode 100644 .changeset/deprecate-and-fix-search.md delete mode 100644 docs-shopify.dev/commands/interfaces/agent-search.interface.ts delete mode 100644 packages/cli/src/cli/commands/agent-search.ts delete mode 100644 packages/cli/src/cli/services/commands/agent-search.test.ts delete mode 100644 packages/cli/src/cli/services/commands/agent-search.ts diff --git a/.changeset/add-agent-search-command.md b/.changeset/add-agent-search-command.md deleted file mode 100644 index cb6a52cbfcc..00000000000 --- a/.changeset/add-agent-search-command.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@shopify/cli': minor ---- - -Add `shopify agent-search`, which queries the shopify.dev vector store and prints the most relevant documentation chunks as JSON to stdout. This makes it usable for programmatic and agent-driven discovery, and supports two optional filters: `--api-name` (for example `admin`, `storefront`, `hydrogen`) and `--api-version` (for example `2025-10`, `latest`, `current`). To download a full document verbatim, use `fetch-doc`. - -Also fixes `shopify search` to open `https://shopify.dev/docs?search=` directly (avoiding the redirect from the site root) so it once again opens shopify.dev with your query. diff --git a/.changeset/deprecate-and-fix-search.md b/.changeset/deprecate-and-fix-search.md new file mode 100644 index 00000000000..b0a40aad40a --- /dev/null +++ b/.changeset/deprecate-and-fix-search.md @@ -0,0 +1,5 @@ +--- +'@shopify/cli': patch +--- + +Fix and deprecate `shopify search`. It now opens `https://shopify.dev/docs?search=` directly (previously it opened a URL that 301-redirected there). The command is also marked deprecated in favor of `doc search`, which returns matching documentation as JSON for programmatic and agent-driven use. `search` keeps its existing browser behavior, so existing usage is unaffected. diff --git a/docs-shopify.dev/commands/interfaces/agent-search.interface.ts b/docs-shopify.dev/commands/interfaces/agent-search.interface.ts deleted file mode 100644 index a2255ba195c..00000000000 --- a/docs-shopify.dev/commands/interfaces/agent-search.interface.ts +++ /dev/null @@ -1,30 +0,0 @@ -// This is an autogenerated file. Don't edit this file manually. -/** - * The following flags are available for the `agent-search` command: - * @publicDocs - */ -export interface agentsearch { - /** - * Limit results to a specific API (for example: admin, storefront, hydrogen, functions). Unrecognized values are ignored. - * @environment SHOPIFY_FLAG_API_NAME - */ - '--api-name '?: string - - /** - * Limit results to a specific API version (for example: 2025-10, latest, current). - * @environment SHOPIFY_FLAG_API_VERSION - */ - '--api-version '?: string - - /** - * Disable color output. - * @environment SHOPIFY_FLAG_NO_COLOR - */ - '--no-color'?: '' - - /** - * Increase the verbosity of the output. - * @environment SHOPIFY_FLAG_VERBOSE - */ - '--verbose'?: '' -} diff --git a/docs-shopify.dev/generated/generated_docs_data_v2.json b/docs-shopify.dev/generated/generated_docs_data_v2.json index 5c27929fda8..613051d8d7d 100644 --- a/docs-shopify.dev/generated/generated_docs_data_v2.json +++ b/docs-shopify.dev/generated/generated_docs_data_v2.json @@ -1,51 +1,4 @@ { - "agentsearch": { - "docs-shopify.dev/commands/interfaces/agent-search.interface.ts": { - "filePath": "docs-shopify.dev/commands/interfaces/agent-search.interface.ts", - "name": "agentsearch", - "description": "The following flags are available for the `agent-search` command:", - "isPublicDocs": true, - "members": [ - { - "filePath": "docs-shopify.dev/commands/interfaces/agent-search.interface.ts", - "syntaxKind": "PropertySignature", - "name": "--api-name ", - "value": "string", - "description": "Limit results to a specific API (for example: admin, storefront, hydrogen, functions). Unrecognized values are ignored.", - "isOptional": true, - "environmentValue": "SHOPIFY_FLAG_API_NAME" - }, - { - "filePath": "docs-shopify.dev/commands/interfaces/agent-search.interface.ts", - "syntaxKind": "PropertySignature", - "name": "--api-version ", - "value": "string", - "description": "Limit results to a specific API version (for example: 2025-10, latest, current).", - "isOptional": true, - "environmentValue": "SHOPIFY_FLAG_API_VERSION" - }, - { - "filePath": "docs-shopify.dev/commands/interfaces/agent-search.interface.ts", - "syntaxKind": "PropertySignature", - "name": "--no-color", - "value": "''", - "description": "Disable color output.", - "isOptional": true, - "environmentValue": "SHOPIFY_FLAG_NO_COLOR" - }, - { - "filePath": "docs-shopify.dev/commands/interfaces/agent-search.interface.ts", - "syntaxKind": "PropertySignature", - "name": "--verbose", - "value": "''", - "description": "Increase the verbosity of the output.", - "isOptional": true, - "environmentValue": "SHOPIFY_FLAG_VERBOSE" - } - ], - "value": "export interface agentsearch {\n /**\n * Limit results to a specific API (for example: admin, storefront, hydrogen, functions). Unrecognized values are ignored.\n * @environment SHOPIFY_FLAG_API_NAME\n */\n '--api-name '?: string\n\n /**\n * Limit results to a specific API version (for example: 2025-10, latest, current).\n * @environment SHOPIFY_FLAG_API_VERSION\n */\n '--api-version '?: string\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" - } - }, "appbuild": { "docs-shopify.dev/commands/interfaces/app-build.interface.ts": { "filePath": "docs-shopify.dev/commands/interfaces/app-build.interface.ts", diff --git a/packages/cli/README.md b/packages/cli/README.md index fbd9a8cb197..4b1cdb6c07b 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -1,6 +1,5 @@ # Commands -* [`shopify agent-search [query]`](#shopify-agent-search-query) * [`shopify app build`](#shopify-app-build) * [`shopify app bulk cancel`](#shopify-app-bulk-cancel) * [`shopify app bulk execute`](#shopify-app-bulk-execute) @@ -101,37 +100,6 @@ * [`shopify upgrade`](#shopify-upgrade) * [`shopify version`](#shopify-version) -## `shopify agent-search [query]` - -Query the shopify.dev vector store and print the most relevant documentation chunks as JSON. Best for programmatic discovery — surfacing the relevant pieces of documentation for a topic, rather than retrieving a whole document. To download a full document verbatim, use `fetch-doc`. - -``` -USAGE - $ shopify agent-search [query] - -ARGUMENTS - QUERY The search query. - -FLAGS - --api-name= [env: SHOPIFY_FLAG_API_NAME] Limit results to a specific API (for example: admin, storefront, - hydrogen, functions). Unrecognized values are ignored. - --api-version= [env: SHOPIFY_FLAG_API_VERSION] Limit results to a specific API version (for example: 2025-10, - latest, current). - --no-color [env: SHOPIFY_FLAG_NO_COLOR] Disable color output. - --verbose [env: SHOPIFY_FLAG_VERBOSE] Increase the verbosity of the output. - -DESCRIPTION - Query the shopify.dev vector store and print the most relevant documentation chunks as JSON. Best for programmatic - discovery — surfacing the relevant pieces of documentation for a topic, rather than retrieving a whole document. To - download a full document verbatim, use `fetch-doc`. - -EXAMPLES - # search shopify.dev for a topic - shopify agent-search "subscribe to webhooks" - # narrow the search to a specific API and version - shopify agent-search "create a product" --api-name admin --api-version latest -``` - ## `shopify app build` Build the app, including extensions. @@ -2115,7 +2083,7 @@ DESCRIPTION ## `shopify search [query]` -Opens shopify.dev in your browser to search the documentation using the on-site search. Intended for interactive, human use. If you are an agent or need results programmatically, use `agent-search` instead, which returns the matching documentation as JSON. +Opens shopify.dev in your browser to search the documentation using the on-site search. Intended for interactive, human use. Deprecated: if you are an agent or need results programmatically, use `doc search` instead, which returns the matching documentation as JSON. ``` USAGE @@ -2123,8 +2091,8 @@ USAGE DESCRIPTION Opens shopify.dev in your browser to search the documentation using the on-site search. Intended for interactive, - human use. If you are an agent or need results programmatically, use `agent-search` instead, which returns the - matching documentation as JSON. + human use. Deprecated: if you are an agent or need results programmatically, use `doc search` instead, which returns + the matching documentation as JSON. EXAMPLES # open the search modal on Shopify.dev diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index 1fa3aea87c4..c1cd092a9db 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -1,64 +1,5 @@ { "commands": { - "agent-search": { - "aliases": [ - ], - "args": { - "query": { - "description": "The search query.", - "name": "query", - "required": true - } - }, - "description": "Query the shopify.dev vector store and print the most relevant documentation chunks as JSON. Best for programmatic discovery — surfacing the relevant pieces of documentation for a topic, rather than retrieving a whole document. To download a full document verbatim, use `fetch-doc`.", - "enableJsonFlag": false, - "examples": [ - "# search shopify.dev for a topic\n shopify agent-search \"subscribe to webhooks\"\n\n # narrow the search to a specific API and version\n shopify agent-search \"create a product\" --api-name admin --api-version latest\n " - ], - "flags": { - "api-name": { - "description": "Limit results to a specific API (for example: admin, storefront, hydrogen, functions). Unrecognized values are ignored.", - "env": "SHOPIFY_FLAG_API_NAME", - "hasDynamicHelp": false, - "multiple": false, - "name": "api-name", - "type": "option" - }, - "api-version": { - "description": "Limit results to a specific API version (for example: 2025-10, latest, current).", - "env": "SHOPIFY_FLAG_API_VERSION", - "hasDynamicHelp": false, - "multiple": false, - "name": "api-version", - "type": "option" - }, - "no-color": { - "allowNo": false, - "description": "Disable color output.", - "env": "SHOPIFY_FLAG_NO_COLOR", - "hidden": false, - "name": "no-color", - "type": "boolean" - }, - "verbose": { - "allowNo": false, - "description": "Increase the verbosity of the output.", - "env": "SHOPIFY_FLAG_VERBOSE", - "hidden": false, - "name": "verbose", - "type": "boolean" - } - }, - "hasDynamicHelp": false, - "hiddenAliases": [ - ], - "id": "agent-search", - "pluginAlias": "@shopify/cli", - "pluginName": "@shopify/cli", - "pluginType": "core", - "strict": true, - "usage": "agent-search [query]" - }, "app:build": { "aliases": [ ], @@ -5711,7 +5652,10 @@ "name": "query" } }, - "description": "Opens shopify.dev in your browser to search the documentation using the on-site search. Intended for interactive, human use. If you are an agent or need results programmatically, use `agent-search` instead, which returns the matching documentation as JSON.", + "deprecationOptions": { + "to": "doc search" + }, + "description": "Opens shopify.dev in your browser to search the documentation using the on-site search. Intended for interactive, human use. Deprecated: if you are an agent or need results programmatically, use `doc search` instead, which returns the matching documentation as JSON.", "enableJsonFlag": false, "examples": [ "# open the search modal on Shopify.dev\n shopify search\n\n # search for a term on Shopify.dev\n shopify search \n\n # search for a phrase on Shopify.dev\n shopify search \"\"\n " @@ -5725,6 +5669,7 @@ "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "state": "deprecated", "strict": true, "usage": "search [query]" }, diff --git a/packages/cli/src/cli/commands/agent-search.ts b/packages/cli/src/cli/commands/agent-search.ts deleted file mode 100644 index 94f835c01c6..00000000000 --- a/packages/cli/src/cli/commands/agent-search.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {agentSearchService} from '../services/commands/agent-search.js' -import Command from '@shopify/cli-kit/node/base-command' -import {globalFlags} from '@shopify/cli-kit/node/cli' -import {Args, Flags} from '@oclif/core' - -export default class AgentSearch extends Command { - static description = - 'Query the shopify.dev vector store and print the most relevant documentation chunks as JSON. Best for programmatic discovery — surfacing the relevant pieces of documentation for a topic, rather than retrieving a whole document. To download a full document verbatim, use `fetch-doc`.' - - static usage = `agent-search [query]` - - static examples = [ - `# search shopify.dev for a topic - shopify agent-search "subscribe to webhooks" - - # narrow the search to a specific API and version - shopify agent-search "create a product" --api-name admin --api-version latest - `, - ] - - static args = { - query: Args.string({ - name: 'query', - required: true, - description: 'The search query.', - }), - } - - static flags = { - ...globalFlags, - 'api-name': Flags.string({ - description: - 'Limit results to a specific API (for example: admin, storefront, hydrogen, functions). Unrecognized values are ignored.', - env: 'SHOPIFY_FLAG_API_NAME', - }), - 'api-version': Flags.string({ - description: 'Limit results to a specific API version (for example: 2025-10, latest, current).', - env: 'SHOPIFY_FLAG_API_VERSION', - }), - } - - async run(): Promise { - const {args, flags} = await this.parse(AgentSearch) - await agentSearchService(args.query, flags['api-name'], flags['api-version']) - } -} diff --git a/packages/cli/src/cli/commands/search.ts b/packages/cli/src/cli/commands/search.ts index 5161411afe1..0783b89c861 100644 --- a/packages/cli/src/cli/commands/search.ts +++ b/packages/cli/src/cli/commands/search.ts @@ -4,7 +4,13 @@ import {Args} from '@oclif/core' export default class Search extends Command { static description = - 'Opens shopify.dev in your browser to search the documentation using the on-site search. Intended for interactive, human use. If you are an agent or need results programmatically, use `agent-search` instead, which returns the matching documentation as JSON.' + 'Opens shopify.dev in your browser to search the documentation using the on-site search. Intended for interactive, human use. Deprecated: if you are an agent or need results programmatically, use `doc search` instead, which returns the matching documentation as JSON.' + + // Deprecated in favor of `doc search`, which returns JSON for programmatic use. + // The browser behavior is intentionally preserved so existing usage doesn't break; + // oclif emits a runtime deprecation warning pointing to `doc search`. + static state = 'deprecated' + static deprecationOptions = {to: 'doc search'} static usage = `search [query]` diff --git a/packages/cli/src/cli/services/commands/agent-search.test.ts b/packages/cli/src/cli/services/commands/agent-search.test.ts deleted file mode 100644 index 3ed4c20c123..00000000000 --- a/packages/cli/src/cli/services/commands/agent-search.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -import {agentSearchService} from './agent-search.js' -import {describe, expect, test, vi, beforeEach} from 'vitest' -import {fetch} from '@shopify/cli-kit/node/http' -import {outputResult} from '@shopify/cli-kit/node/output' -import {AbortError} from '@shopify/cli-kit/node/error' - -vi.mock('@shopify/cli-kit/node/http') -// Only stub `outputResult`; keep the rest of the module real. Blanket-mocking it -// would also mock `stringifyMessage`, which `AbortError`'s constructor relies on — -// that would silently empty out every thrown error message. -vi.mock('@shopify/cli-kit/node/output', async (importOriginal) => ({ - ...(await importOriginal()), - outputResult: vi.fn(), -})) - -const okResponse = (body: string) => - ({ok: true, status: 200, statusText: 'OK', text: () => Promise.resolve(body)}) as any - -const errorResponse = (status: number, statusText: string, body: string) => - ({ok: false, status, statusText, text: () => Promise.resolve(body)}) as any - -const resultsBody = - '[{"score":0.99,"content":"About webhooks","url":"https://shopify.dev/x","title":"Webhooks","domain":null}]' - -beforeEach(() => { - vi.mocked(fetch).mockResolvedValue(okResponse(resultsBody)) -}) - -describe('agentSearchService', () => { - test('requests the search endpoint with the query and prints the raw JSON body', async () => { - await agentSearchService('webhooks') - - expect(fetch).toHaveBeenCalledWith('https://shopify.dev/assistant/search?query=webhooks', { - headers: {Accept: 'application/json'}, - }) - expect(outputResult).toHaveBeenCalledWith(resultsBody) - }) - - test('includes api_name and api_version params when provided', async () => { - await agentSearchService('create a product', 'admin', 'latest') - - expect(fetch).toHaveBeenCalledWith( - 'https://shopify.dev/assistant/search?query=create+a+product&api_name=admin&api_version=latest', - {headers: {Accept: 'application/json'}}, - ) - }) - - test('URL-encodes queries with spaces and special characters', async () => { - await agentSearchService('a & b?') - - expect(fetch).toHaveBeenCalledWith('https://shopify.dev/assistant/search?query=a+%26+b%3F', { - headers: {Accept: 'application/json'}, - }) - }) - - test('surfaces the server error message from a non-ok JSON response', async () => { - vi.mocked(fetch).mockResolvedValue( - errorResponse( - 400, - 'Bad Request', - '{"error":"Invalid api_version \'2025-01\' for api_name \'admin\'. Available versions: 2026-07"}', - ), - ) - - await expect(agentSearchService('products', 'admin', '2025-01')).rejects.toThrowError( - /Invalid api_version '2025-01' for api_name 'admin'\. Available versions: 2026-07/, - ) - expect(outputResult).not.toHaveBeenCalled() - }) - - test('falls back to the status line when a non-ok response is not JSON', async () => { - vi.mocked(fetch).mockResolvedValue(errorResponse(500, 'Internal Server Error', 'nope')) - - await expect(agentSearchService('products')).rejects.toThrowError(AbortError) - await expect(agentSearchService('products')).rejects.toThrowError(/500 Internal Server Error/) - expect(outputResult).not.toHaveBeenCalled() - }) -}) diff --git a/packages/cli/src/cli/services/commands/agent-search.ts b/packages/cli/src/cli/services/commands/agent-search.ts deleted file mode 100644 index 67229a2feaa..00000000000 --- a/packages/cli/src/cli/services/commands/agent-search.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {fetch} from '@shopify/cli-kit/node/http' -import {outputResult} from '@shopify/cli-kit/node/output' -import {AbortError} from '@shopify/cli-kit/node/error' - -// The dev-assistant search endpoint queries the shopify.dev vector store and -// returns an array of matching documentation chunks as JSON. -const SEARCH_URL = 'https://shopify.dev/assistant/search' - -export async function agentSearchService(query: string, apiName?: string, apiVersion?: string) { - const params = new URLSearchParams({query}) - if (apiName) params.append('api_name', apiName) - if (apiVersion) params.append('api_version', apiVersion) - - const response = await fetch(`${SEARCH_URL}?${params.toString()}`, {headers: {Accept: 'application/json'}}) - const body = await response.text() - - if (!response.ok) { - // The endpoint returns a JSON `{error}` body for 400s (e.g. an invalid api_version - // lists the valid versions) — surface it directly instead of a bare status code. - let message = `${response.status} ${response.statusText}` - try { - const parsed = JSON.parse(body) - if (parsed?.error) message = parsed.error - } catch (parseError) { - // Body wasn't JSON; fall back to the status line. Rethrow anything unexpected. - if (!(parseError instanceof SyntaxError)) throw parseError - } - throw new AbortError(`Search failed: ${message}`) - } - - outputResult(body) -} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index aaa4882f7b2..ff506419c63 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,6 +1,5 @@ import VersionCommand from './cli/commands/version.js' import Search from './cli/commands/search.js' -import AgentSearch from './cli/commands/agent-search.js' import Upgrade from './cli/commands/upgrade.js' import Logout from './cli/commands/auth/logout.js' import Login from './cli/commands/auth/login.js' @@ -146,7 +145,6 @@ export const COMMANDS: any = { ...HydrogenCommands, ...StoreCommands, search: Search, - 'agent-search': AgentSearch, upgrade: Upgrade, version: VersionCommand, help: HelpCommand, diff --git a/packages/e2e/data/snapshots/commands.txt b/packages/e2e/data/snapshots/commands.txt index 0fdf5e7605a..45202424817 100644 --- a/packages/e2e/data/snapshots/commands.txt +++ b/packages/e2e/data/snapshots/commands.txt @@ -1,4 +1,3 @@ -├─ agent-search ├─ app │ ├─ build │ ├─ bulk @@ -92,7 +91,6 @@ │ ├─ uninstall │ ├─ unlink │ └─ update -├─ search ├─ store │ ├─ auth │ └─ execute