Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/app-get-review-requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@shopify/app': minor
'@shopify/cli-kit': patch
---

Add `shopify app get-review-requirements`, a new command that prints the [App Store review requirements](https://shopify.dev/docs/apps/launch/app-store-review/app-store-ai-self-review-requirements), with agent verification guidance, as markdown. Designed to be invoked by the `/shopify-app-review` agent skill in the Shopify AI Toolkit to power a local pre-submission compliance check against an app's codebase.
32 changes: 32 additions & 0 deletions packages/app/src/cli/commands/app/get-review-requirements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {getReviewRequirements} from '../../services/app/get-review-requirements.js'
import {appFlags} from '../../flags.js'
import BaseCommand from '@shopify/cli-kit/node/base-command'
import {globalFlags} from '@shopify/cli-kit/node/cli'
import {outputResult} from '@shopify/cli-kit/node/output'

export default class AppGetReviewRequirements extends BaseCommand {
static summary = 'Print the App Store self-review requirements as markdown.'

static descriptionWithMarkdown = `Prints the [App Store review requirements](https://shopify.dev/docs/apps/launch/app-store-review/app-store-ai-self-review-requirements) to stdout as markdown, including the agent verification guidance an AI agent needs to evaluate each requirement against a local codebase.

Designed to be invoked by the \`shopify-app-review\` agent skill from the Shopify AI Toolkit as part of a local pre-submission compliance check, but you can also run it yourself to read the latest locally checkable requirements.`

static description = this.descriptionWithoutMarkdown()

static flags = {
...globalFlags,
path: appFlags.path,
config: appFlags.config,
}

public async run(): Promise<void> {
const {flags} = await this.parse(AppGetReviewRequirements)

const markdown = await getReviewRequirements({
directory: flags.path,
configName: flags.config,
})

outputResult(markdown)
}
}
2 changes: 2 additions & 0 deletions packages/app/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import FetchSchema from './commands/app/function/schema.js'
import FunctionTypegen from './commands/app/function/typegen.js'
import AppGenerateExtension from './commands/app/generate/extension.js'
import ImportExtensions from './commands/app/import-extensions.js'
import AppGetReviewRequirements from './commands/app/get-review-requirements.js'
import AppInfo from './commands/app/info.js'
import Init from './commands/app/init.js'
import ConfigValidate from './commands/app/config/validate.js'
Expand Down Expand Up @@ -70,6 +71,7 @@ export const commands: {[key: string]: typeof AppLinkedCommand | typeof AppUnlin
'app:function:schema': FetchSchema,
'app:function:typegen': FunctionTypegen,
'app:generate:extension': AppGenerateExtension,
'app:get-review-requirements': AppGetReviewRequirements,
'app:versions:list': VersionsList,
'app:webhook:trigger': WebhookTrigger,
'demo:watcher': DemoWatcher,
Expand Down
88 changes: 88 additions & 0 deletions packages/app/src/cli/services/app/get-review-requirements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import {loadApp} from '../../models/app/loader.js'
import {loadLocalExtensionsSpecifications} from '../../models/extensions/load-specifications.js'
import metadata from '../../metadata.js'
import {fetch} from '@shopify/cli-kit/node/http'
import {outputDebug} from '@shopify/cli-kit/node/output'
import {AbortError} from '@shopify/cli-kit/node/error'
import {CLI_KIT_VERSION} from '@shopify/cli-kit/common/version'

const REQUIREMENTS_URL = 'https://shopify.dev/docs/apps/launch/app-store-review/app-store-ai-self-review-requirements'

export interface GetReviewRequirementsOptions {
directory: string
configName?: string
}

/**
* Fetches the canonical App Store self-review requirements markdown from
* shopify.dev and returns it as a string. Best-effort loads the local app so
* the auto-emitted Monorail event carries app-level attribution (api_key,
* app_path_hash, app_scopes, etc.).
*
* Designed to be invoked by the `shopify-app-review` agent skill in the
* Shopify AI Toolkit: the skill shells out to
* `shopify app get-review-requirements`, captures stdout, and uses it as the
* live list of requirements to evaluate the codebase against.
*/
export async function getReviewRequirements(options: GetReviewRequirementsOptions): Promise<string> {
await loadAppForAttribution(options)

const markdown = await fetchRequirementsMarkdown()
return markdown
Comment on lines +27 to +31
}

async function loadAppForAttribution(options: GetReviewRequirementsOptions): Promise<void> {
try {
const app = await loadApp({
directory: options.directory,
userProvidedConfigName: options.configName,
specifications: await loadLocalExtensionsSpecifications(),
remoteFlags: undefined,
skipPrompts: true,
})

if (app.configuration.client_id) {
await metadata.addPublicMetadata(() => ({
api_key: String(app.configuration.client_id),
}))
}
// eslint-disable-next-line no-catch-all/no-catch-all
} catch (error) {
outputDebug(
`Skipping app attribution for review-requirements fetch: ${
error instanceof Error ? error.message : String(error)
}`,
)
}
}

async function fetchRequirementsMarkdown(): Promise<string> {
const response = await fetch(
REQUIREMENTS_URL,
{
headers: {
Accept: 'text/markdown, text/plain;q=0.9, */*;q=0.1',
'User-Agent': `Shopify CLI/${CLI_KIT_VERSION} (app get-review-requirements)`,
},
},
'non-blocking',
)

if (!response.ok) {
await metadata.addPublicMetadata(() => ({
cmd_app_review_requirements_fetch_status: response.status,
}))
throw new AbortError(
`Failed to fetch App Store self-review requirements from shopify.dev (HTTP ${response.status} ${response.statusText}).`,
`Check your network connection and try again, or visit ${REQUIREMENTS_URL} directly.`,
)
}

const body = await response.text()

await metadata.addPublicMetadata(() => ({
cmd_app_review_requirements_fetch_status: response.status,
}))

return body
}
1 change: 1 addition & 0 deletions packages/cli-kit/src/public/node/monorail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export interface Schemas {
cmd_app_validate_valid?: Optional<boolean>
cmd_app_validate_issue_count?: Optional<number>
cmd_app_validate_file_count?: Optional<number>
cmd_app_review_requirements_fetch_status?: Optional<number>

// Dev related commands
cmd_dev_tunnel_type?: Optional<string>
Expand Down
26 changes: 26 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* [`shopify app function schema`](#shopify-app-function-schema)
* [`shopify app function typegen`](#shopify-app-function-typegen)
* [`shopify app generate extension`](#shopify-app-generate-extension)
* [`shopify app get-review-requirements`](#shopify-app-get-review-requirements)
* [`shopify app import-custom-data-definitions`](#shopify-app-import-custom-data-definitions)
* [`shopify app import-extensions`](#shopify-app-import-extensions)
* [`shopify app info`](#shopify-app-info)
Expand Down Expand Up @@ -749,6 +750,31 @@ DESCRIPTION
your extension.
```

## `shopify app get-review-requirements`

Print the App Store self-review requirements as markdown.

```
USAGE
$ shopify app get-review-requirements [-c <value>] [--no-color] [--path <value>] [--verbose]

FLAGS
-c, --config=<value> [env: SHOPIFY_FLAG_APP_CONFIG] The name of the app configuration.
--no-color [env: SHOPIFY_FLAG_NO_COLOR] Disable color output.
--path=<value> [env: SHOPIFY_FLAG_PATH] The path to your app directory.
--verbose [env: SHOPIFY_FLAG_VERBOSE] Increase the verbosity of the output.

DESCRIPTION
Print the App Store self-review requirements as markdown.

Prints the "App Store review requirements"
(https://shopify.dev/docs/apps/launch/app-store-review/app-store-ai-self-review-requirements) to stdout as markdown,
including the agent verification guidance an AI agent needs to evaluate each requirement against a local codebase.

Designed to be invoked by the `shopify-app-review` agent skill from the Shopify AI Toolkit as part of a local
pre-submission compliance check, but you can also run it yourself to read the latest locally checkable requirements.
```

## `shopify app import-custom-data-definitions`

Import metafield and metaobject definitions.
Expand Down
58 changes: 57 additions & 1 deletion packages/cli/oclif.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2143,6 +2143,62 @@
"strict": true,
"summary": "Generate a new app Extension."
},
"app:get-review-requirements": {
"aliases": [
],
"args": {
},
"customPluginName": "@shopify/app",
"description": "Prints the \"App Store review requirements\" (https://shopify.dev/docs/apps/launch/app-store-review/app-store-ai-self-review-requirements) to stdout as markdown, including the agent verification guidance an AI agent needs to evaluate each requirement against a local codebase.\n\n Designed to be invoked by the `shopify-app-review` agent skill from the Shopify AI Toolkit as part of a local pre-submission compliance check, but you can also run it yourself to read the latest locally checkable requirements.",
"descriptionWithMarkdown": "Prints the [App Store review requirements](https://shopify.dev/docs/apps/launch/app-store-review/app-store-ai-self-review-requirements) to stdout as markdown, including the agent verification guidance an AI agent needs to evaluate each requirement against a local codebase.\n\n Designed to be invoked by the `shopify-app-review` agent skill from the Shopify AI Toolkit as part of a local pre-submission compliance check, but you can also run it yourself to read the latest locally checkable requirements.",
"enableJsonFlag": false,
"flags": {
"config": {
"char": "c",
"description": "The name of the app configuration.",
"env": "SHOPIFY_FLAG_APP_CONFIG",
"hasDynamicHelp": false,
"hidden": false,
"multiple": false,
"name": "config",
"type": "option"
},
"no-color": {
"allowNo": false,
"description": "Disable color output.",
"env": "SHOPIFY_FLAG_NO_COLOR",
"hidden": false,
"name": "no-color",
"type": "boolean"
},
"path": {
"description": "The path to your app directory.",
"env": "SHOPIFY_FLAG_PATH",
"hasDynamicHelp": false,
"multiple": false,
"name": "path",
"noCacheDefault": true,
"type": "option"
},
"verbose": {
"allowNo": false,
"description": "Increase the verbosity of the output.",
"env": "SHOPIFY_FLAG_VERBOSE",
"hidden": false,
"name": "verbose",
"type": "boolean"
}
},
"hasDynamicHelp": false,
"hiddenAliases": [
],
"id": "app:get-review-requirements",
"pluginAlias": "@shopify/cli",
"pluginName": "@shopify/cli",
"pluginType": "core",
"strict": true,
"summary": "Print the App Store self-review requirements as markdown."
},
"app:import-custom-data-definitions": {
"aliases": [
],
Expand Down Expand Up @@ -7987,4 +8043,4 @@
}
},
"version": "4.1.0"
}
}
Loading