|
| 1 | +# Plan: Plugin Recommender |
| 2 | + |
| 3 | +> Track: nuxt-session-hook-20260328 |
| 4 | +> Spec: [spec.md](./spec.md) |
| 5 | +
|
| 6 | +## Overview |
| 7 | + |
| 8 | +- **Source**: [spec.md](./spec.md) |
| 9 | +- **Issue**: TBD |
| 10 | +- **Created**: 2026-03-28 |
| 11 | +- **Approach**: Minimal Change |
| 12 | + |
| 13 | +## Purpose |
| 14 | + |
| 15 | +After this change, developers will automatically see recommendations to install relevant Claude Code plugins based on npm packages detected in their `package.json`. Recommendations appear at session start (async, non-blocking) and after running package install commands. They can verify it works by starting a Claude Code session in a project with `@nuxt/ui` and seeing the recommendation to install `nuxt-ui@pleaseai`. |
| 16 | + |
| 17 | +## Context |
| 18 | + |
| 19 | +The `pleaseai` marketplace offers many plugins that correspond to npm packages (nuxt, vue, pinia, vueuse, vitest, prisma, supabase, etc.), but users may not know these plugins exist. Currently there is no automatic discovery mechanism. |
| 20 | + |
| 21 | +This feature creates a standalone `plugin-recommender` plugin with two complementary hooks: |
| 22 | + |
| 23 | +1. **SessionStart hook** (`async: true`): Scans `package.json` at session start in the background, without blocking session initialization. Outputs `systemMessage` (user-facing) and `additionalContext` (Claude-facing). |
| 24 | + |
| 25 | +2. **PostToolUse hook** (`matcher: "Bash"`): Fires after `bun add`, `npm install/add`, `pnpm add/install` commands. When a user installs a matching package during the session, immediately recommends the corresponding plugin. |
| 26 | + |
| 27 | +Both hooks share detection logic in a single TypeScript file. The package-to-plugin mapping is a simple data array, making it trivial to add new mappings as plugins are added to the marketplace. |
| 28 | + |
| 29 | +The existing `worktree-context.ts` hook provides the reference TypeScript pattern: stdin-based JSON input, pure functions for testability, and `import.meta.main` guard. |
| 30 | + |
| 31 | +Non-goals: No auto-installation, no monorepo workspace scanning, no non-npm ecosystems. |
| 32 | + |
| 33 | +## Architecture Decision |
| 34 | + |
| 35 | +Standalone plugin at `plugins/plugin-recommender/` rather than embedding hooks in individual framework plugins. This centralizes the recommendation logic and avoids duplication across plugins. |
| 36 | + |
| 37 | +The hook uses `hooks/hooks.json` (auto-loaded by Claude Code) rather than `plugin.json`'s `hooks` field. Pure functions are exported for unit testing, and `import.meta.main` guard separates entry point from testable logic. |
| 38 | + |
| 39 | +For installed plugin detection, the hook checks `.claude/settings.json` (project-level) and `~/.claude/settings.json` (user-level) for `enabledPlugins` entries matching `pluginName@pleaseai`. |
| 40 | + |
| 41 | +A single script handles both events via `hook_event_name` field from stdin JSON. |
| 42 | + |
| 43 | +## Tasks |
| 44 | + |
| 45 | +- [x] T001 Create plugin manifest (file: plugins/plugin-recommender/.claude-plugin/plugin.json) |
| 46 | +- [x] T002 Create dependency detection hook (file: plugins/plugin-recommender/hooks/check-dependencies.ts) (depends on T001) |
| 47 | +- [x] T003 Create hooks.json registration (file: plugins/plugin-recommender/hooks/hooks.json) (depends on T002) |
| 48 | +- [x] T004 [P] Create unit tests for hook (file: plugins/plugin-recommender/hooks/check-dependencies.test.ts) (depends on T002) |
| 49 | +- [x] T005 Add plugin-recommender to marketplace.json (file: .claude-plugin/marketplace.json) (depends on T001) |
| 50 | + |
| 51 | +## Progress |
| 52 | + |
| 53 | +- [x] (2026-03-28 22:35 KST) T001 Create plugin manifest |
| 54 | +- [x] (2026-03-28 22:36 KST) T002 Create dependency detection hook |
| 55 | + Evidence: `bun test` → 32 tests passed (62ms) |
| 56 | +- [x] (2026-03-28 22:37 KST) T003 Create hooks.json registration |
| 57 | +- [x] (2026-03-28 22:36 KST) T004 Create unit tests for hook |
| 58 | + Evidence: `bun test` → 32 tests passed, 93 expect() calls |
| 59 | +- [x] (2026-03-28 22:38 KST) T005 Add plugin-recommender to marketplace.json |
| 60 | + |
| 61 | +## Key Files |
| 62 | + |
| 63 | +### Create |
| 64 | + |
| 65 | +- `plugins/plugin-recommender/.claude-plugin/plugin.json` — Plugin manifest with name, version, description |
| 66 | +- `plugins/plugin-recommender/hooks/check-dependencies.ts` — Main hook with package detection, plugin status checking, and JSON output |
| 67 | +- `plugins/plugin-recommender/hooks/hooks.json` — Hook registration: SessionStart (async) + PostToolUse (matcher: Bash) |
| 68 | +- `plugins/plugin-recommender/hooks/check-dependencies.test.ts` — Unit tests for detection/filtering functions |
| 69 | + |
| 70 | +### Modify |
| 71 | + |
| 72 | +- `.claude-plugin/marketplace.json` — Add `plugin-recommender` entry |
| 73 | + |
| 74 | +### Reuse (Reference) |
| 75 | + |
| 76 | +- `plugins/worktree/hooks/worktree-context.ts` — TypeScript hook pattern (stdin parsing, JSON output, `import.meta.main`) |
| 77 | +- `plugins/worktree/hooks/worktree-context.test.ts` — Test pattern for TypeScript hooks |
| 78 | + |
| 79 | +## Verification |
| 80 | + |
| 81 | +### Automated Tests |
| 82 | + |
| 83 | +- [ ] Detects `@nuxt/ui` in dependencies and returns `nuxt-ui` recommendation |
| 84 | +- [ ] Detects `pinia` or `@pinia/nuxt` and returns `pinia` recommendation |
| 85 | +- [ ] Detects `vitest` in devDependencies and returns `vitest` recommendation |
| 86 | +- [ ] Detects `@prisma/client` and returns `prisma` recommendation |
| 87 | +- [ ] Detects multiple packages and returns all recommendations |
| 88 | +- [ ] Returns empty result when no matching packages found |
| 89 | +- [ ] Returns empty result when all matching plugins are already installed |
| 90 | +- [ ] Handles missing `package.json` gracefully (no error, no output) |
| 91 | +- [ ] Handles malformed `package.json` gracefully |
| 92 | +- [ ] Output JSON has valid `hookSpecificOutput` with `systemMessage` and `additionalContext` |
| 93 | +- [ ] PostToolUse: extracts package name from `bun add @nuxt/ui` command |
| 94 | +- [ ] PostToolUse: extracts package name from `npm install prisma` command |
| 95 | +- [ ] PostToolUse: extracts package name from `pnpm add @vueuse/nuxt` command |
| 96 | +- [ ] Adding a new mapping requires only a data change |
| 97 | + |
| 98 | +### Observable Outcomes |
| 99 | + |
| 100 | +- After starting Claude Code in a project with `@nuxt/ui` installed, the session shows a recommendation to install `nuxt-ui@pleaseai` |
| 101 | +- After running `bun add @prisma/client`, the PostToolUse hook shows the prisma recommendation |
| 102 | + |
| 103 | +## Decision Log |
| 104 | + |
| 105 | +- Decision: Standalone `plugin-recommender` plugin instead of per-framework hooks |
| 106 | + Rationale: Centralizes recommendation logic, avoids duplication, covers all npm-based plugins in one place |
| 107 | + Date/Author: 2026-03-28 / user + Claude |
| 108 | +- Decision: Use `hooks/hooks.json` over `plugin.json` hooks field |
| 109 | + Rationale: CLAUDE.md convention — `hooks/hooks.json` is auto-loaded |
| 110 | + Date/Author: 2026-03-28 / Claude |
| 111 | +- Decision: Check both project-level and user-level `.claude/settings.json` for installed plugins |
| 112 | + Rationale: Users may have plugins enabled at project or global scope |
| 113 | + Date/Author: 2026-03-28 / Claude |
| 114 | +- Decision: Use `async: true` for SessionStart hook |
| 115 | + Rationale: Package detection is informational and should not block session initialization |
| 116 | + Date/Author: 2026-03-28 / user + Claude |
| 117 | +- Decision: Add PostToolUse hook for package install commands |
| 118 | + Rationale: Recommend plugins immediately when a matching package is installed |
| 119 | + Date/Author: 2026-03-28 / user + Claude |
| 120 | +- Decision: Single script handles both events via `hook_event_name` field |
| 121 | + Rationale: Shared detection logic avoids code duplication |
| 122 | + Date/Author: 2026-03-28 / Claude |
0 commit comments