Add store list for organization stores#7708
Conversation
92fffc1 to
c7e152d
Compare
3ac3560 to
6fbe857
Compare
This stack of pull requests is managed by Graphite. Learn more about stacking. |
f516bbe to
79dbfa7
Compare
|
I believe you should be able to gitignore packages/store/src/cli/api/graphql/business-platform-organizations/organizations_schema.graphql |
5986997 to
b4c7faa
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces a new shopify store list command in the @shopify/store plugin to list stores available through the current CLI account’s Shopify organizations, backed by Business Platform GraphQL. It also extends organizations fetching to expose “current user resolved/email” metadata and hardens CLI session persistence reads against malformed JSON.
Changes:
- Add
shopify store listcommand + output formatting (text table + JSON), including--organization-id, truncation cap (250), and partial-organization failure warnings. - Add a Business Platform Organizations GraphQL query + codegen plumbing for store listing, and wire it to organization discovery via
@shopify/organizations. - Improve robustness of persisted CLI session reads by discarding malformed JSON and clearing the stored session.
Reviewed changes
Copilot reviewed 26 out of 33 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Adds lockfile entries for new @shopify/store dependencies. |
| packages/store/src/index.ts | Registers the new store:list command. |
| packages/store/src/cli/services/store/list/types.ts | Defines store list result/entry types and the 250-entry cap constant. |
| packages/store/src/cli/services/store/list/result.ts | Implements text/JSON presenters (table rendering, warnings, truncation messaging). |
| packages/store/src/cli/services/store/list/result.test.ts | Tests presenter behavior (table fields, empty states, JSON, truncation warning). |
| packages/store/src/cli/services/store/list/index.ts | Service entrypoint: handles discriminated results, org-not-found erroring, truncation. |
| packages/store/src/cli/services/store/list/index.test.ts | Tests service behavior (success, unresolved session, org-not-found, truncation). |
| packages/store/src/cli/services/store/list/bp-source.ts | Business Platform-backed store listing: org discovery + per-org store fetch + merge/sort. |
| packages/store/src/cli/services/store/list/bp-source.test.ts | Tests Business Platform source behavior (filters, merging, hasMore, partial failure). |
| packages/store/src/cli/commands/store/list.ts | Adds the shopify store list oclif command and --organization-id / --json. |
| packages/store/src/cli/commands/store/list.test.ts | Tests CLI wiring: flags passthrough and presenter selection. |
| packages/store/src/cli/api/graphql/business-platform-organizations/queries/list_accessible_shops.graphql | Adds query for newest stores per organization (sort: SHOP_CREATED_AT_DESC, first). |
| packages/store/src/cli/api/graphql/business-platform-organizations/generated/types.d.ts | Adds generated schema typings for the store Business Platform orgs project. |
| packages/store/src/cli/api/graphql/business-platform-organizations/generated/list_accessible_shops.ts | Adds generated typed document node for store listing query. |
| packages/store/project.json | Adds Nx targets for GraphQL codegen + formatting/postfix steps for store package. |
| packages/store/package.json | Adds dependencies required for typed GraphQL documents + organizations access info. |
| packages/organizations/src/index.ts | Exports new fetchOrganizationsWithAccessInfo. |
| packages/organizations/src/cli/services/fetch.ts | Implements fetchOrganizationsWithAccessInfo returning orgs + user-resolved/email metadata. |
| packages/organizations/src/cli/services/fetch.test.ts | Adds tests for the new access-info fetch behavior and updates existing fixtures. |
| packages/organizations/src/cli/api/graphql/business-platform-destinations/queries/organizations.graphql | Extends org query to include current user email. |
| packages/organizations/src/cli/api/graphql/business-platform-destinations/generated/organizations.ts | Updates generated query typings to include email. |
| packages/e2e/data/snapshots/commands.txt | Updates CLI command snapshot to include store list. |
| packages/cli/README.md | Updates CLI command reference docs to include shopify store list. |
| packages/cli/oclif.manifest.json | Adds manifest entry for store:list command. |
| packages/cli-kit/src/private/node/session/store.ts | Hardens session store parsing: on malformed JSON, clear persisted sessions and return undefined. |
| packages/cli-kit/src/private/node/session/store.test.ts | Adds test coverage for discarding malformed JSON session content. |
| package.json | Updates knip config for packages/store (ignore generated + mark typed-doc-node dep as ignored). |
| graphql.config.ts | Adds storeBusinessPlatformOrganizations graphql-codegen project configuration. |
| docs-shopify.dev/generated/generated_docs_data_v2.json | Updates generated docs data to include store list command interface metadata. |
| docs-shopify.dev/commands/interfaces/store-list.interface.ts | Adds generated TypeScript interface for store list flags. |
| bin/get-graphql-schemas.js | Ensures BP organizations schema is fetched/copied for both app + store packages. |
| .gitignore | Ignores Business Platform schema files under packages/store. |
| .changeset/store-list-bp-auto.md | Changeset for new command + partial failure behavior + session parsing hardening. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
cb1e68e to
4a2c0dd
Compare
4a2c0dd to
0974dfd
Compare
c61df52 to
429141b
Compare
4075110 to
cd562c1
Compare
6e10c3a to
000cd81
Compare
000cd81 to
8a8afc2
Compare
| static descriptionWithMarkdown = `Lists stores in a Shopify organization available to the current CLI account. | ||
|
|
||
| When more than one organization is available, the command prompts you to pick one unless you provide \`--organization-id\`. | ||
| In non-interactive environments, provide \`--organization-id\`. |
There was a problem hiding this comment.
Something about this language feels a bit forced. Maybe something like
| In non-interactive environments, provide \`--organization-id\`. | |
| In non-interactive environments, \`--organization-id\` is required. |
| When more than one organization is available, the command prompts you to pick one unless you provide \`--organization-id\`. | ||
| In non-interactive environments, provide \`--organization-id\`. | ||
|
|
||
| Run \`shopify organization list\` to find organization IDs.` |
There was a problem hiding this comment.
Can we use <%= config.bin %> here?
| static flags = { | ||
| ...globalFlags, | ||
| ...jsonFlag, | ||
| 'organization-id': Flags.string({ |
There was a problem hiding this comment.
Why isn't this an integer? Would we expect gid format sometimes?
| organizationId?: string | ||
| } | ||
|
|
||
| export async function listStores(options: ListStoresOptions = {}): Promise<ListStoresResult> { |
There was a problem hiding this comment.
I believe making --organization-id an integer flag would make some of this unnecessary.
| @@ -0,0 +1,70 @@ | |||
| import {listBusinessPlatformStores} from './bp-source.js' | |||
There was a problem hiding this comment.
I just noticed that we have a few barrel files like this one in this package. Unfortunately I introduced at least one, so mea culpa there... generally the practice in this repo is not to use barrel files except on the whole-package level (for package exports). We should probably build this into our agent guidelines.
In this case, the expectation would be that this file would live at packages/store/src/cli/services/store/list.ts with no difference in the import string in other files, but of course the import strings in this file will need to be updated.
| return selectOrganizationFromList(organizations, orgIdFromFlag) | ||
| } | ||
|
|
||
| export async function selectOrganizationFromList<T extends Organization>( |
There was a problem hiding this comment.
I don't really understand why this function was broken out. It's only used in 1 file, where it's immediately preceded by a call to fetchOrganizations() - exactly what's done in selectOrg - so why wouldn't we just align with what's being done here? We could drop many LoC from this PR if we don't make this change.
8a8afc2 to
67cfc8b
Compare
Differences in type declarationsWe detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:
New type declarationsWe found no new type declarations in this PR Existing type declarationspackages/cli-kit/dist/public/common/string.d.ts@@ -102,6 +102,16 @@ export declare function formatDate(date: Date): string;
* @returns The transformed string in local system time.
*/
export declare function formatLocalDate(dateString: string): string;
+/**
+ * Formats a date as a short calendar date like "May 22, 2026" () in UTC.
+ *
+ * UTC keeps the output deterministic regardless of the machine timezone. Returns an empty string
+ * when the value cannot be parsed into a valid date.
+ *
+ * @param value - A Date, epoch milliseconds, or a date string such as an ISO 8601 timestamp.
+ * @returns The formatted date, or an empty string when the value is invalid.
+ */
+export declare function formatShortDate(value: Date | number | string): string;
/**
* Given a list of items, it returns a string with the items joined by commas and the last item joined by "and".
* All items are wrapped in double quotes.
packages/cli-kit/dist/public/common/url.d.ts@@ -28,4 +28,13 @@ export declare function extractHost(value: string | null | undefined): string |
* @param value - A URL or host string, possibly null/undefined.
* @returns The myshopify subdomain handle, or undefined when the input isn't a URL.
*/
-export declare function extractMyshopifyHandle(value: string | null | undefined): string | undefined;
\ No newline at end of file
+export declare function extractMyshopifyHandle(value: string | null | undefined): string | undefined;
+/**
+ * Extracts the leading subdomain label from a URL or host, across environments — e.g.
+ * , , or local development hosts — rather than assuming a
+ * domain.
+ *
+ * @param value - A URL or host string, possibly null/undefined.
+ * @returns The first DNS label, or undefined when no host can be extracted.
+ */
+export declare function extractSubdomain(value: string | null | undefined): string | undefined;
\ No newline at end of file
|

Summary
Add
shopify store list, listing stores from one Shopify organization available to the current CLI account.Builds
store listout from the prototype in theshopify store create previewstack.Closes https://github.com/shop/issues-develop/issues/22723
Scope
shopify store listas an organization-backed store-management list--organization-idselects that organization and uses the shared integer organization flag--organization-id@shopify/organizations.selectOrganizationFromList()and use it from both app context and store listaccessibleShops(first: 250, sort: SHOP_CREATED_AT_DESC, filters: STORE_STATUS = active)Subdomain | Name | Type | Created, sorted newest firsttruncated: truein JSONorganization: {id, name}Review notes
--fromflag and no local store-auth fallback; Slack feedback on 2026-06-14/15 made the two jobs clearer as separate commands@shopify/organizationsNot changed / deferred
--from,auto, or store-auth fallback behavior. Those made one command responsible for two different jobs and were explicitly removed after Slack review.store listlist every organization. It still resolves one organization first to avoid broad fan-out, ambiguous output, and partial multi-org policy in this first command contract.--organization-idback to a string after follow-through review. There is a theoretical JS safe-integer concern, but the current reviewer direction is to use the shared integerorganization-idflag. Reopen only if Shopify organization IDs can exceed JS safe integer range.store listshow whether each store is already authenticated forstore execute. That belongs to the direct store-auth listing command upstack, not the organization store-management list.