diff --git a/docs/agent/PLUGIN.md b/docs/agent/PLUGIN.md index 85ed0ca5..0f912622 100644 --- a/docs/agent/PLUGIN.md +++ b/docs/agent/PLUGIN.md @@ -51,7 +51,7 @@ Claude Code plugin at `packages/plugin/`. MCP server + queued auto-analysis hook | `lib/native-deps.ts` | Shared `ensureNativeDeps()` — idempotent better-sqlite3 installer used by both server-entry and SessionStart hook | | `hooks/session-start-handler.ts` | `SessionStart` hook, first-run detection + queued `/bp-analyze` context | | `hooks/hooks.json` | Hook registration (`SessionStart` + `SessionEnd`) | -| `skills/bp-setup/SKILL.md` | Guided onboarding wizard skill | +| `skills/bp-setup/SKILL.md` | Guided onboarding wizard skill (includes mid-session MCP registration via `claude mcp add`) | | `skills/bp-analyze/SKILL.md` | Full analysis pipeline orchestrator skill | | `.claude-plugin/plugin.json` | Plugin metadata + config schema | | `.mcp.json` | MCP server config (stdio transport) | @@ -146,6 +146,16 @@ Evaluated in order by `shouldTriggerAnalysis(sessionDurationMs)`: Session count: scans `~/.claude/projects/*/` for `.jsonl` files (filesystem only, no content reading). +## Single-Session Install Flow + +After `claude plugin install`, the MCP server isn't running yet (plugin lifecycle starts next session). The `/bp-setup` skill handles this with Step 0.5: + +``` +install plugin → /bp-setup → Step 0.5: `claude mcp add -s user -e NODE_PATH=... -e CLAUDE_PLUGIN_DATA=... betterprompt -- node ` → MCP tools available → continue setup +``` + +The `claude mcp add` command registers the server at user scope with `NODE_PATH` and `CLAUDE_PLUGIN_DATA` env vars pointing to `~/.betterprompt/`. On subsequent sessions, the plugin's `.mcp.json` handles server startup normally — Claude Code deduplicates by server name, so the user-scoped entry and the plugin-managed `.mcp.json` entry do not conflict. + ## Queued Auto-Analysis Flow ``` diff --git a/packages/plugin/.claude-plugin/plugin.json b/packages/plugin/.claude-plugin/plugin.json index c55cb237..781e804c 100644 --- a/packages/plugin/.claude-plugin/plugin.json +++ b/packages/plugin/.claude-plugin/plugin.json @@ -7,6 +7,8 @@ "name": "BetterPrompt" }, "repository": "https://github.com/onlycastle/BetterPrompt", + "skills": "./skills/", + "mcpServers": "./.mcp.json", "configuration": { "serverUrl": { "type": "string", diff --git a/packages/plugin/dist/chunk-IEEHTH2R.js.map b/packages/plugin/dist/chunk-IEEHTH2R.js.map deleted file mode 100644 index 585588de..00000000 --- a/packages/plugin/dist/chunk-IEEHTH2R.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../lib/native-deps.ts"],"sourcesContent":["/**\n * Shared native dependency installer.\n *\n * Ensures `better-sqlite3` is compiled and available in the persistent\n * plugin data directory. Safe to call from multiple entry points\n * (SessionStart hook, MCP server startup) — a marker file prevents\n * redundant installs.\n */\n\nimport { existsSync } from 'node:fs';\nimport { execFileSync } from 'node:child_process';\nimport { join } from 'node:path';\n\nexport function ensureNativeDeps(opts?: { fatal?: boolean }): void {\n const pluginDataDir = process.env.CLAUDE_PLUGIN_DATA;\n if (!pluginDataDir) return;\n\n const marker = join(pluginDataDir, 'node_modules', 'better-sqlite3', 'build', 'Release', 'better_sqlite3.node');\n if (existsSync(marker)) return;\n\n try {\n execFileSync('npm', ['install', '--prefix', pluginDataDir, 'better-sqlite3@12.8.0'], {\n stdio: 'ignore',\n timeout: 60_000,\n });\n } catch (err) {\n const msg = `[betterprompt] Failed to install better-sqlite3: ${err instanceof Error ? err.message : err}`;\n if (opts?.fatal) {\n throw new Error(msg);\n }\n process.stderr.write(msg + '\\n');\n }\n}\n"],"mappings":";AASA,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,YAAY;AAEd,SAAS,iBAAiB,MAAkC;AACjE,QAAM,gBAAgB,QAAQ,IAAI;AAClC,MAAI,CAAC,cAAe;AAEpB,QAAM,SAAS,KAAK,eAAe,gBAAgB,kBAAkB,SAAS,WAAW,qBAAqB;AAC9G,MAAI,WAAW,MAAM,EAAG;AAExB,MAAI;AACF,iBAAa,OAAO,CAAC,WAAW,YAAY,eAAe,uBAAuB,GAAG;AAAA,MACnF,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,UAAM,MAAM,oDAAoD,eAAe,QAAQ,IAAI,UAAU,GAAG;AACxG,QAAI,MAAM,OAAO;AACf,YAAM,IAAI,MAAM,GAAG;AAAA,IACrB;AACA,YAAQ,OAAO,MAAM,MAAM,IAAI;AAAA,EACjC;AACF;","names":[]} \ No newline at end of file diff --git a/packages/plugin/dist/chunk-IEEHTH2R.js b/packages/plugin/dist/chunk-KH675EAB.js similarity index 81% rename from packages/plugin/dist/chunk-IEEHTH2R.js rename to packages/plugin/dist/chunk-KH675EAB.js index e2c73472..55a0d484 100644 --- a/packages/plugin/dist/chunk-IEEHTH2R.js +++ b/packages/plugin/dist/chunk-KH675EAB.js @@ -2,9 +2,9 @@ import { existsSync } from "fs"; import { execFileSync } from "child_process"; import { join } from "path"; +import { homedir } from "os"; function ensureNativeDeps(opts) { - const pluginDataDir = process.env.CLAUDE_PLUGIN_DATA; - if (!pluginDataDir) return; + const pluginDataDir = process.env.CLAUDE_PLUGIN_DATA || join(homedir(), ".betterprompt"); const marker = join(pluginDataDir, "node_modules", "better-sqlite3", "build", "Release", "better_sqlite3.node"); if (existsSync(marker)) return; try { @@ -24,4 +24,4 @@ function ensureNativeDeps(opts) { export { ensureNativeDeps }; -//# sourceMappingURL=chunk-IEEHTH2R.js.map \ No newline at end of file +//# sourceMappingURL=chunk-KH675EAB.js.map \ No newline at end of file diff --git a/packages/plugin/dist/chunk-KH675EAB.js.map b/packages/plugin/dist/chunk-KH675EAB.js.map new file mode 100644 index 00000000..1e09d46d --- /dev/null +++ b/packages/plugin/dist/chunk-KH675EAB.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../lib/native-deps.ts"],"sourcesContent":["/**\n * Shared native dependency installer.\n *\n * Ensures `better-sqlite3` is compiled and available in the persistent\n * plugin data directory. Safe to call from multiple entry points\n * (SessionStart hook, MCP server startup) — a marker file prevents\n * redundant installs.\n */\n\nimport { existsSync } from 'node:fs';\nimport { execFileSync } from 'node:child_process';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\nexport function ensureNativeDeps(opts?: { fatal?: boolean }): void {\n const pluginDataDir = process.env.CLAUDE_PLUGIN_DATA || join(homedir(), '.betterprompt');\n\n const marker = join(pluginDataDir, 'node_modules', 'better-sqlite3', 'build', 'Release', 'better_sqlite3.node');\n if (existsSync(marker)) return;\n\n try {\n execFileSync('npm', ['install', '--prefix', pluginDataDir, 'better-sqlite3@12.8.0'], {\n stdio: 'ignore',\n timeout: 60_000,\n });\n } catch (err) {\n const msg = `[betterprompt] Failed to install better-sqlite3: ${err instanceof Error ? err.message : err}`;\n if (opts?.fatal) {\n throw new Error(msg);\n }\n process.stderr.write(msg + '\\n');\n }\n}\n"],"mappings":";AASA,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,YAAY;AACrB,SAAS,eAAe;AAEjB,SAAS,iBAAiB,MAAkC;AACjE,QAAM,gBAAgB,QAAQ,IAAI,sBAAsB,KAAK,QAAQ,GAAG,eAAe;AAEvF,QAAM,SAAS,KAAK,eAAe,gBAAgB,kBAAkB,SAAS,WAAW,qBAAqB;AAC9G,MAAI,WAAW,MAAM,EAAG;AAExB,MAAI;AACF,iBAAa,OAAO,CAAC,WAAW,YAAY,eAAe,uBAAuB,GAAG;AAAA,MACnF,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,UAAM,MAAM,oDAAoD,eAAe,QAAQ,IAAI,UAAU,GAAG;AACxG,QAAI,MAAM,OAAO;AACf,YAAM,IAAI,MAAM,GAAG;AAAA,IACrB;AACA,YAAQ,OAAO,MAAM,MAAM,IAAI;AAAA,EACjC;AACF;","names":[]} \ No newline at end of file diff --git a/packages/plugin/dist/hooks/session-start-handler.js b/packages/plugin/dist/hooks/session-start-handler.js index a43e229d..ce205561 100755 --- a/packages/plugin/dist/hooks/session-start-handler.js +++ b/packages/plugin/dist/hooks/session-start-handler.js @@ -1,7 +1,7 @@ #!/usr/bin/env node import { ensureNativeDeps -} from "../chunk-IEEHTH2R.js"; +} from "../chunk-KH675EAB.js"; import { buildFirstRunAdditionalContext, buildPendingAnalysisAdditionalContext diff --git a/packages/plugin/dist/mcp/server-entry.js b/packages/plugin/dist/mcp/server-entry.js index 8b03d64e..1198d9c8 100755 --- a/packages/plugin/dist/mcp/server-entry.js +++ b/packages/plugin/dist/mcp/server-entry.js @@ -1,7 +1,7 @@ #!/usr/bin/env node import { ensureNativeDeps -} from "../chunk-IEEHTH2R.js"; +} from "../chunk-KH675EAB.js"; import "../chunk-PR4QN5HX.js"; // mcp/server-entry.ts diff --git a/packages/plugin/lib/native-deps.ts b/packages/plugin/lib/native-deps.ts index d4cb7fee..889a915d 100644 --- a/packages/plugin/lib/native-deps.ts +++ b/packages/plugin/lib/native-deps.ts @@ -10,10 +10,10 @@ import { existsSync } from 'node:fs'; import { execFileSync } from 'node:child_process'; import { join } from 'node:path'; +import { homedir } from 'node:os'; export function ensureNativeDeps(opts?: { fatal?: boolean }): void { - const pluginDataDir = process.env.CLAUDE_PLUGIN_DATA; - if (!pluginDataDir) return; + const pluginDataDir = process.env.CLAUDE_PLUGIN_DATA || join(homedir(), '.betterprompt'); const marker = join(pluginDataDir, 'node_modules', 'better-sqlite3', 'build', 'Release', 'better_sqlite3.node'); if (existsSync(marker)) return; diff --git a/packages/plugin/skills/bp-setup/SKILL.md b/packages/plugin/skills/bp-setup/SKILL.md index d296f145..f11d5998 100644 --- a/packages/plugin/skills/bp-setup/SKILL.md +++ b/packages/plugin/skills/bp-setup/SKILL.md @@ -37,6 +37,29 @@ your machine. This setup takes about 1 minute. ``` +### Step 0.5: Ensure MCP Server + +After a fresh plugin installation, the MCP server is not running yet in the current session. This step registers it so all BetterPrompt tools become available immediately. + +1. Run `claude mcp list` via Bash and check whether `betterprompt` appears in the output. +2. If `betterprompt` is already listed, skip to Step 1. +3. If NOT listed: + a. Find the plugin's server entry point — search for the file matching this glob pattern: + ``` + ~/.claude/plugins/cache/betterprompt/betterprompt/*/dist/mcp/server-entry.js + ``` + b. Create the data directory if it doesn't exist: `mkdir -p ~/.betterprompt` + c. Register the MCP server: + ```bash + claude mcp add -s user \ + -e NODE_PATH="$HOME/.betterprompt/node_modules" \ + -e CLAUDE_PLUGIN_DATA="$HOME/.betterprompt" \ + betterprompt -- \ + node "" + ``` + d. Tell the user: "MCP server registered. BetterPrompt tools are now available." +4. If the server entry point cannot be found, inform the user and suggest reinstalling the plugin. + ### Step 1: Verify Installation Call the `scan_sessions` MCP tool to confirm the plugin is working. @@ -147,7 +170,8 @@ Use `AskUserQuestion` with these options: ## Important Notes -- Never skip Step 1 (verification) -- this confirms the plugin works. +- Never skip Step 0.5 (MCP server) or Step 1 (verification) -- these confirm the plugin works. +- Step 0.5 makes install-then-setup possible in a single Claude Code session. - Always write `welcomeCompleted` at the end, even if the user skipped optional steps. - If any step fails, log the error but continue to the next step. Do not abort the entire wizard for a non-critical failure (Steps 2, 3 are non-critical). - The CLAUDE.md block uses HTML comment markers (`` / ``) so it can be cleanly replaced or removed later.