Skip to content

Emit Claude Code plugin hint on CLI invocations - #8193

Open
dmerand wants to merge 2 commits into
mainfrom
feat/claude-code-plugin-hint
Open

Emit Claude Code plugin hint on CLI invocations#8193
dmerand wants to merge 2 commits into
mainfrom
feat/claude-code-plugin-hint

Conversation

@dmerand

@dmerand dmerand commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

What

Emit the Claude Code plugin hint for the Shopify AI Toolkit from the all-command oclif prerun hook:

<claude-code-hint v="1" type="plugin" value="shopify-ai-toolkit@claude-plugins-official" />

The hint is written to stderr only when CLAUDECODE or CLAUDE_CODE_CHILD_SESSION is truthy. The emitter is private, and failures are swallowed so the optional integration cannot break a CLI command.

Why

This lets Claude Code suggest Shopify's AI Toolkit plugin to users who run the CLI under Claude Code. Emission happens on every invocation because Claude Code owns deduplication and persistence; a client-side once-ever gate could lose the only useful prompt in a session.

The oclif prerun hook does not run for early exits such as --help, --version, or parse errors, so those paths do not emit.

@github-actions github-actions Bot added the Area: @shopify/cli @shopify/cli package issues label Jul 28, 2026
@dmerand
dmerand force-pushed the feat/claude-code-plugin-hint branch from 1b31155 to 13ab68c Compare July 29, 2026 00:17
@dmerand dmerand changed the title Add Claude Code plugin hint on first CLI boot Emit Claude Code plugin hint on CLI invocations Jul 29, 2026
Assisted-By: devx/f7eeb23f-1e44-45b6-8486-65a383fc1918
@dmerand
dmerand force-pushed the feat/claude-code-plugin-hint branch from 13ab68c to 4dfb4ea Compare July 29, 2026 00:18
Assisted-By: devx/e5b7002e-1154-4597-9d58-f24fc29b6ad7
@github-actions

Copy link
Copy Markdown
Contributor

Differences in type declarations

We 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:

  • Some seemingly private modules might be re-exported through public modules.
  • If the branch is behind main you might see odd diffs, rebase main into this branch.

New type declarations

packages/cli-kit/dist/private/node/plugin-hints.d.ts
/** The Claude Code plugin hint protocol marker. */
export declare const SHOPIFY_AI_TOOLKIT_PLUGIN_HINT = "<claude-code-hint v=\"1\" type=\"plugin\" value=\"shopify-ai-toolkit@claude-plugins-official\" />";
/**
 * Returns whether this process was launched by Claude Code.
 *
 * @param environment - Environment variables to inspect.
 * @returns Whether Claude Code environment markers are truthy.
 */
export declare function runningUnderClaudeCode(environment?: NodeJS.ProcessEnv): boolean;
/**
 * Emits the Claude Code plugin hint on every command invocation under Claude Code.
 * Claude Code handles deduplication and persistence. A failed optional integration
 * must not make the user's command fail.
 *
 * @param environment - Environment variables to inspect.
 */
export declare function emitClaudeCodePluginHint(environment?: NodeJS.ProcessEnv): void;

Existing type declarations

We found no diffs with existing type declarations

@dmerand
dmerand marked this pull request as ready for review July 29, 2026 00:47
@dmerand
dmerand requested a review from a team as a code owner July 29, 2026 00:47
Copilot AI review requested due to automatic review settings July 29, 2026 00:47

@teddyhwang teddyhwang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, is this a Claude Code feature?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an optional integration to emit a Claude Code “plugin hint” marker to stderr during Shopify CLI invocations when Claude Code environment markers are present, enabling Claude Code to recommend the Shopify AI Toolkit plugin.

Changes:

  • Emit the Claude Code plugin hint from the global oclif prerun hook (best-effort; failures swallowed).
  • Introduce a private plugin-hints module to detect Claude Code and write the hint marker to stderr.
  • Add unit + integration tests covering detection, emission, and failure-to-load behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
packages/cli-kit/src/public/node/hooks/prerun.ts Calls the private hint emitter during prerun for every resolved command invocation.
packages/cli-kit/src/public/node/hooks/prerun.integration.test.ts Verifies prerun writes the marker under Claude Code and remains resilient to emitter import failures.
packages/cli-kit/src/private/node/plugin-hints.ts Implements detection and stderr emission of the Claude Code plugin hint marker.
packages/cli-kit/src/private/node/plugin-hints.test.ts Unit tests for environment detection and hint emission behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +24 to +26
afterEach(() => {
vi.unstubAllEnvs()
})
Comment on lines +18 to +26
// Emit on every resolved command invocation; --help, --version, and parse errors exit before prerun.
// Claude Code owns deduplication and covers first-party and plugin commands.
try {
const {emitClaudeCodePluginHint} = await import('../../../private/node/plugin-hints.js')
emitClaudeCodePluginHint()
// eslint-disable-next-line no-catch-all/no-catch-all
} catch {
// This optional integration must never make a command fail.
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot is right here, there is no point in making this import dynamic if we are importing it always and the dependency tree will be big here. It will slow down startup time for every command.

It'd be better to:

  • check here for CLAUDE's env var.
  • only import the plugin-hints.js file if the env var is present

Even with this, it will make invocations from Claude slower, so we should try to paralelize as much as possible.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented in the other file, but if we remove the imports from plugin-hints this issue disappears and we can import the file statically

dmerand commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Interesting, is this a Claude Code feature?

​Yes, I should have linked to these docs in the description: https://code.claude.com/docs/en/plugin-hints

Comment on lines +30 to +34
process.stderr.write(`${SHOPIFY_AI_TOOLKIT_PLUGIN_HINT}\n`)
// eslint-disable-next-line no-catch-all/no-catch-all
} catch (error) {
try {
outputDebug(`Unable to emit Claude Code plugin hint: ${(error as Error).message}`)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how can process.stderr.write fail? and if it does, outputDebug will fail too, it also calls process.stderr.write

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also using outputDebug forces you to import output.js which is huge and will affect CLI startup time (since this is done in the prerun stage).

If we want to make this even simpler, we could avoid all imports. As a special case, just because this is a prerun

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: @shopify/cli @shopify/cli package issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants