From d36cd04a2a0a7f75183032b64350d1cd11a502cd Mon Sep 17 00:00:00 2001 From: Nick Wesselman <27013789+nickwesselman@users.noreply.github.com> Date: Wed, 2 Sep 2026 14:17:12 -0400 Subject: [PATCH] Explain the empty-organization store creation prompts in app dev An organization with no dev stores drops straight into "Name for the new development store", which arrives without context: the developer asked to run `app dev`, not to create a store. Render a short notice first. The store picker's "Create a new dev store" choice stays quiet, because there the developer already expressed the intent. Co-Authored-By: Claude Opus 5 (1M context) Assisted-By: devx/a27b4557-3ac8-448a-8860-7a5ed6b20687 --- .../app/src/cli/services/dev/select-store.test.ts | 7 ++++++- packages/app/src/cli/services/dev/select-store.ts | 11 ++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/app/src/cli/services/dev/select-store.test.ts b/packages/app/src/cli/services/dev/select-store.test.ts index 645b3e0fc13..24e46e78689 100644 --- a/packages/app/src/cli/services/dev/select-store.test.ts +++ b/packages/app/src/cli/services/dev/select-store.test.ts @@ -12,7 +12,7 @@ import { import {testDeveloperPlatformClient} from '../../models/app/app.test-data.js' import {ClientName} from '../../utilities/developer-platform-client.js' import {sleep} from '@shopify/cli-kit/node/system' -import {isTTY, renderSuccess, renderTasks, Task} from '@shopify/cli-kit/node/ui' +import {isTTY, renderInfo, renderSuccess, renderTasks, Task} from '@shopify/cli-kit/node/ui' import {AbortError, CancelExecution} from '@shopify/cli-kit/node/error' import {createDevStore} from '@shopify/organizations' import {beforeEach, describe, expect, vi, test} from 'vitest' @@ -404,6 +404,8 @@ describe('selectStore', async () => { json: false, summary: false, }) + // The developer picked the create choice, so no explanatory notice is needed. + expect(renderInfo).not.toHaveBeenCalled() expect(renderSuccess).toHaveBeenCalledWith({headline: 'Development store "store1" created successfully.'}) }) @@ -477,6 +479,9 @@ describe('selectStore', async () => { }) expect(fetchStore).toHaveBeenCalledTimes(2) expect(sleep).toHaveBeenCalledWith(3) + expect(renderInfo).toHaveBeenCalledWith({ + body: "You don't have any dev stores associated with org1's Dev Dashboard. Let's create one.", + }) expect(renderSuccess).toHaveBeenCalledWith({headline: 'Development store "store1" created successfully.'}) }) diff --git a/packages/app/src/cli/services/dev/select-store.ts b/packages/app/src/cli/services/dev/select-store.ts index 18eccf6c1de..fb6ce1c2f12 100644 --- a/packages/app/src/cli/services/dev/select-store.ts +++ b/packages/app/src/cli/services/dev/select-store.ts @@ -53,7 +53,12 @@ export async function selectStore( if (isTTY() === false) { throw new AbortError('No development store was specified.', createDevStoreTryMessage(org.id)) } - onCreateStoreWhenEmpty = createStoreInline + // The developer never asked to create a store here, so explain why they are being + // prompted. The picker's create choice is self-explanatory and stays quiet. + onCreateStoreWhenEmpty = async () => { + renderInfo({body: emptyOrgNoticeBody(org)}) + return createStoreInline() + } } else if (storeCreationEnabled && storeCreationMode === 'selection-option') { if (isTTY() === false && (storesSearch.stores.length > 1 || storesSearch.hasMorePages)) { throw new AbortError( @@ -175,6 +180,10 @@ async function waitForCreatedStore( const devStoreCapReachedMessage = 'Your organization has reached its development store limit.' +function emptyOrgNoticeBody(org: Organization): string { + return `You don't have any dev stores associated with ${org.businessName}'s Dev Dashboard. Let's create one.` +} + function devStoreCreationCommand(orgId: string): string { return `shopify store create dev --organization-id ${orgId} --name --plan ` }