Emit Claude Code plugin hint on CLI invocations - #8193
Conversation
1b31155 to
13ab68c
Compare
Assisted-By: devx/f7eeb23f-1e44-45b6-8486-65a383fc1918
13ab68c to
4dfb4ea
Compare
Assisted-By: devx/e5b7002e-1154-4597-9d58-f24fc29b6ad7
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 declarationspackages/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 declarationsWe found no diffs with existing type declarations |
teddyhwang
left a comment
There was a problem hiding this comment.
Interesting, is this a Claude Code feature?
There was a problem hiding this comment.
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
prerunhook (best-effort; failures swallowed). - Introduce a private
plugin-hintsmodule 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.
| afterEach(() => { | ||
| vi.unstubAllEnvs() | ||
| }) |
| // 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. | ||
| } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Commented in the other file, but if we remove the imports from plugin-hints this issue disappears and we can import the file statically
Yes, I should have linked to these docs in the description: https://code.claude.com/docs/en/plugin-hints |
| 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}`) |
There was a problem hiding this comment.
how can process.stderr.write fail? and if it does, outputDebug will fail too, it also calls process.stderr.write
There was a problem hiding this comment.
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
What
Emit the Claude Code plugin hint for the Shopify AI Toolkit from the all-command oclif
prerunhook:The hint is written to stderr only when
CLAUDECODEorCLAUDE_CODE_CHILD_SESSIONis 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
prerunhook does not run for early exits such as--help,--version, or parse errors, so those paths do not emit.