|
| 1 | +#!/usr/bin/env node |
| 2 | + |
| 3 | +import { |
| 4 | + copyFileSync, |
| 5 | + mkdirSync, |
| 6 | + readFileSync, |
| 7 | + readdirSync, |
| 8 | + rmSync, |
| 9 | + writeFileSync, |
| 10 | +} from "node:fs"; |
| 11 | +import os from "node:os"; |
| 12 | +import path from "node:path"; |
| 13 | +import { fileURLToPath } from "node:url"; |
| 14 | + |
| 15 | +const __dirname = path.dirname(fileURLToPath(import.meta.url)); |
| 16 | +const pluginDir = path.resolve(__dirname, ".."); |
| 17 | +const homeDir = process.env.HOME || os.homedir(); |
| 18 | + |
| 19 | +function readVersion() { |
| 20 | + const packageJson = JSON.parse( |
| 21 | + readFileSync(path.join(pluginDir, "package.json"), "utf8"), |
| 22 | + ); |
| 23 | + if (!packageJson.version) { |
| 24 | + throw new Error("Failed to read version from package.json"); |
| 25 | + } |
| 26 | + return packageJson.version; |
| 27 | +} |
| 28 | + |
| 29 | +function installCommands(version) { |
| 30 | + const commandDir = path.join(homeDir, ".claude", "commands"); |
| 31 | + const sentinel = path.join(commandDir, `.brooks-lint-v${version}`); |
| 32 | + |
| 33 | + try { |
| 34 | + readFileSync(sentinel, "utf8"); |
| 35 | + return; |
| 36 | + } catch { |
| 37 | + // Sentinel does not exist yet. |
| 38 | + } |
| 39 | + |
| 40 | + mkdirSync(commandDir, { recursive: true }); |
| 41 | + |
| 42 | + const commandsDir = path.join(pluginDir, "commands"); |
| 43 | + for (const entry of readdirSync(commandsDir)) { |
| 44 | + if (/^brooks-.*\.md$/.test(entry)) { |
| 45 | + copyFileSync(path.join(commandsDir, entry), path.join(commandDir, entry)); |
| 46 | + } |
| 47 | + } |
| 48 | + |
| 49 | + for (const entry of readdirSync(commandDir)) { |
| 50 | + if (entry === ".brooks-lint-installed" || entry.startsWith(".brooks-lint-v")) { |
| 51 | + rmSync(path.join(commandDir, entry), { force: true }); |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 55 | + writeFileSync(sentinel, ""); |
| 56 | +} |
| 57 | + |
| 58 | +function buildContext() { |
| 59 | + return [ |
| 60 | + "You have the brooks-lint plugin installed. It provides six independent skills - load the relevant one via the Skill tool:", |
| 61 | + " brooks-lint:brooks-review -> PR code review", |
| 62 | + " brooks-lint:brooks-audit -> Architecture audit", |
| 63 | + " brooks-lint:brooks-debt -> Tech debt assessment", |
| 64 | + " brooks-lint:brooks-test -> Test quality review", |
| 65 | + " brooks-lint:brooks-health -> Codebase health dashboard", |
| 66 | + " brooks-lint:brooks-sweep -> Full sweep: analyse all dimensions and auto-fix findings", |
| 67 | + "", |
| 68 | + "Triggers when the user asks to review code, discuss architecture, assess tech debt, or discuss test quality. Also triggers when the user mentions: Brooks's Law / Mythical Man-Month / conceptual integrity / second system effect / Hyrum's Law / deep modules / tactical programming / code smells / refactoring / clean architecture / DDD.", |
| 69 | + ].join("\n"); |
| 70 | +} |
| 71 | + |
| 72 | +function buildOutput(context) { |
| 73 | + if (process.env.CLAUDE_PLUGIN_ROOT) { |
| 74 | + return { |
| 75 | + hookSpecificOutput: { |
| 76 | + hookEventName: "SessionStart", |
| 77 | + additionalContext: context, |
| 78 | + }, |
| 79 | + }; |
| 80 | + } |
| 81 | + |
| 82 | + return { |
| 83 | + additional_context: context, |
| 84 | + }; |
| 85 | +} |
| 86 | + |
| 87 | +try { |
| 88 | + installCommands(readVersion()); |
| 89 | + process.stdout.write(`${JSON.stringify(buildOutput(buildContext()), null, 2)}\n`); |
| 90 | +} catch (error) { |
| 91 | + process.stderr.write(`${error.message}\n`); |
| 92 | + process.exit(1); |
| 93 | +} |
0 commit comments