Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
5.4 kB · 164 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165/** * Plugin-hint recommendations. * * Companion to lspRecommendation.ts: where LSP recommendations are triggered * by file edits, plugin hints are triggered by CLIs/SDKs emitting a * `<claude-code-hint />` tag to stderr (detected by the Bash/PowerShell tools). * * State persists in GlobalConfig.claudeCodeHints — a show-once record per * plugin and a disabled flag (user picked "don't show again"). Official- * marketplace filtering is hardcoded for v1. */
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js'import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, type AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, logEvent,} from '../../services/analytics/index.js'import { type ClaudeCodeHint, hasShownHintThisSession, setPendingHint,} from '../claudeCodeHints.js'import { getGlobalConfig, saveGlobalConfig } from '../config.js'import { logForDebugging } from '../debug.js'import { isPluginInstalled } from './installedPluginsManager.js'import { getPluginById } from './marketplaceManager.js'import { isOfficialMarketplaceName, parsePluginIdentifier,} from './pluginIdentifier.js'import { isPluginBlockedByPolicy } from './pluginPolicy.js'
/** * Hard cap on `claudeCodeHints.plugin[]` — bounds config growth. Each shown * plugin appends one slug; past this point we stop prompting (and stop * appending) rather than let the config grow without limit. */const MAX_SHOWN_PLUGINS = 100
export type PluginHintRecommendation = { pluginId: string pluginName: string marketplaceName: string pluginDescription?: string sourceCommand: string}
/** * Pre-store gate called by shell tools when a `type="plugin"` hint is detected. * Drops the hint if: * * - a dialog has already been shown this session * - user has disabled hints * - the shown-plugins list has hit the config-growth cap * - plugin slug doesn't parse as `name@marketplace` * - marketplace isn't official (hardcoded for v1) * - plugin is already installed * - plugin was already shown in a prior session * * Synchronous on purpose — shell tools shouldn't await a marketplace lookup * just to strip a stderr line. The async marketplace-cache check happens * later in resolvePluginHint (hook side). */export function maybeRecordPluginHint(hint: ClaudeCodeHint): void { if (!getFeatureValue_CACHED_MAY_BE_STALE('tengu_lapis_finch', false)) return if (hasShownHintThisSession()) return
const state = getGlobalConfig().claudeCodeHints if (state?.disabled) return
const shown = state?.plugin ?? [] if (shown.length >= MAX_SHOWN_PLUGINS) return
const pluginId = hint.value const { name, marketplace } = parsePluginIdentifier(pluginId) if (!name || !marketplace) return if (!isOfficialMarketplaceName(marketplace)) return if (shown.includes(pluginId)) return if (isPluginInstalled(pluginId)) return if (isPluginBlockedByPolicy(pluginId)) return
// Bound repeat lookups on the same slug — a CLI that emits on every // invocation shouldn't trigger N resolve cycles for the same plugin. if (triedThisSession.has(pluginId)) return triedThisSession.add(pluginId)
setPendingHint(hint)}
const triedThisSession = new Set<string>()
/** Test-only reset. */export function _resetHintRecommendationForTesting(): void { triedThisSession.clear()}
/** * Resolve the pending hint to a renderable recommendation. Runs the async * marketplace lookup that the sync pre-store gate skipped. Returns null if * the plugin isn't in the marketplace cache — the hint is discarded. */export async function resolvePluginHint( hint: ClaudeCodeHint,): Promise<PluginHintRecommendation | null> { const pluginId = hint.value const { name, marketplace } = parsePluginIdentifier(pluginId)
const pluginData = await getPluginById(pluginId)
logEvent('tengu_plugin_hint_detected', { _PROTO_plugin_name: (name ?? '') as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, _PROTO_marketplace_name: (marketplace ?? '') as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, result: (pluginData ? 'passed' : 'not_in_cache') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, })
if (!pluginData) { logForDebugging( `[hintRecommendation] ${pluginId} not found in marketplace cache`, ) return null }
return { pluginId, pluginName: pluginData.entry.name, marketplaceName: marketplace ?? '', pluginDescription: pluginData.entry.description, sourceCommand: hint.sourceCommand, }}
/** * Record that a prompt for this plugin was surfaced. Called regardless of * the user's yes/no response — show-once semantics. */export function markHintPluginShown(pluginId: string): void { saveGlobalConfig(current => { const existing = current.claudeCodeHints?.plugin ?? [] if (existing.includes(pluginId)) return current return { ...current, claudeCodeHints: { ...current.claudeCodeHints, plugin: [...existing, pluginId], }, } })}
/** Called when the user picks "don't show plugin installation hints again". */export function disableHintRecommendations(): void { saveGlobalConfig(current => { if (current.claudeCodeHints?.disabled) return current return { ...current, claudeCodeHints: { ...current.claudeCodeHints, disabled: true }, } })}