@goobits/sherpa 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-PXNRR7WN.js → chunk-KBEAWVRK.js} +53 -17
- package/dist/chunk-KBEAWVRK.js.map +7 -0
- package/dist/cli.js +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/reviewer/index.js +7 -1
- package/dist/reviewer/index.js.map +2 -2
- package/package.json +1 -1
- package/dist/chunk-PXNRR7WN.js.map +0 -7
- package/dist/chunk-WBU2V2G7.js +0 -364
- package/dist/chunk-WBU2V2G7.js.map +0 -7
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
// src/commands/init.ts
|
|
2
2
|
import { execSync } from "child_process";
|
|
3
3
|
import { createRequire } from "module";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
appendFileSync,
|
|
6
|
+
chmodSync,
|
|
7
|
+
existsSync,
|
|
8
|
+
mkdirSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
writeFileSync
|
|
11
|
+
} from "fs";
|
|
5
12
|
import { dirname, join, resolve } from "path";
|
|
6
13
|
import { fileURLToPath } from "url";
|
|
7
14
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
15
|
var CLAUDE_HOOK_CONFIG = {
|
|
9
|
-
PreToolUse: [
|
|
10
|
-
|
|
16
|
+
PreToolUse: [
|
|
17
|
+
{ matcher: "Bash", hooks: [{ type: "command", command: "sherpa pre" }] }
|
|
18
|
+
],
|
|
19
|
+
PostToolUse: [
|
|
20
|
+
{ matcher: "Bash", hooks: [{ type: "command", command: "sherpa post" }] }
|
|
21
|
+
]
|
|
11
22
|
};
|
|
12
23
|
var GUARD_CONFIG = {
|
|
13
24
|
maxTokens: 2e3,
|
|
@@ -52,7 +63,15 @@ function commandExists(command) {
|
|
|
52
63
|
}
|
|
53
64
|
}
|
|
54
65
|
function getLocalReviewerPath(cwd) {
|
|
55
|
-
const localPath = join(
|
|
66
|
+
const localPath = join(
|
|
67
|
+
cwd,
|
|
68
|
+
"node_modules",
|
|
69
|
+
"@goobits",
|
|
70
|
+
"sherpa",
|
|
71
|
+
"dist",
|
|
72
|
+
"reviewer",
|
|
73
|
+
"index.js"
|
|
74
|
+
);
|
|
56
75
|
if (existsSync(localPath)) {
|
|
57
76
|
return "./node_modules/@goobits/sherpa/dist/reviewer/index.js";
|
|
58
77
|
}
|
|
@@ -112,7 +131,9 @@ function installLocalSherpa(cwd) {
|
|
|
112
131
|
console.log(`Installing @goobits/sherpa locally with ${packageManager}...`);
|
|
113
132
|
execSync(installCommand, { cwd, stdio: "pipe" });
|
|
114
133
|
} catch {
|
|
115
|
-
console.warn(
|
|
134
|
+
console.warn(
|
|
135
|
+
`Warning: Failed to install @goobits/sherpa with ${packageManager}.`
|
|
136
|
+
);
|
|
116
137
|
return false;
|
|
117
138
|
}
|
|
118
139
|
return true;
|
|
@@ -194,10 +215,15 @@ function setupClaudeHooks(cwd, force) {
|
|
|
194
215
|
for (const [hookType, hooks] of Object.entries(CLAUDE_HOOK_CONFIG)) {
|
|
195
216
|
const existing = settings.hooks[hookType] || [];
|
|
196
217
|
const hasSherpa = existing.some(
|
|
197
|
-
(h) => h.hooks?.some(
|
|
218
|
+
(h) => h.hooks?.some(
|
|
219
|
+
(hook) => hook.command?.startsWith("sherpa ")
|
|
220
|
+
)
|
|
198
221
|
);
|
|
199
222
|
if (!hasSherpa) {
|
|
200
|
-
settings.hooks[hookType] = [
|
|
223
|
+
settings.hooks[hookType] = [
|
|
224
|
+
...existing,
|
|
225
|
+
...hooks
|
|
226
|
+
];
|
|
201
227
|
hooksUpdated = true;
|
|
202
228
|
}
|
|
203
229
|
}
|
|
@@ -212,7 +238,7 @@ function setupClaudeHooks(cwd, force) {
|
|
|
212
238
|
function setupMcpConfig(cwd, force) {
|
|
213
239
|
const mcpPath = join(cwd, ".mcp.json");
|
|
214
240
|
const settingsPath = join(cwd, ".claude/settings.local.json");
|
|
215
|
-
if (!getLocalReviewerPath(cwd)
|
|
241
|
+
if (!getLocalReviewerPath(cwd)) {
|
|
216
242
|
if (installLocalSherpa(cwd)) {
|
|
217
243
|
console.log("Installed @goobits/sherpa for portable MCP config.");
|
|
218
244
|
}
|
|
@@ -238,17 +264,22 @@ function setupMcpConfig(cwd, force) {
|
|
|
238
264
|
};
|
|
239
265
|
if (!mcpCommand.isPortable) {
|
|
240
266
|
console.warn("Warning: reviewer path is outside the project.");
|
|
241
|
-
console.warn(
|
|
267
|
+
console.warn(
|
|
268
|
+
"Install @goobits/sherpa locally (devDependency) to keep .mcp.json portable."
|
|
269
|
+
);
|
|
242
270
|
}
|
|
243
271
|
try {
|
|
244
272
|
execSync("claude mcp remove reviewer -s project 2>/dev/null || true", {
|
|
245
273
|
cwd,
|
|
246
274
|
stdio: "pipe"
|
|
247
275
|
});
|
|
248
|
-
execSync(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
276
|
+
execSync(
|
|
277
|
+
"claude mcp remove cerebras-reviewer -s project 2>/dev/null || true",
|
|
278
|
+
{
|
|
279
|
+
cwd,
|
|
280
|
+
stdio: "pipe"
|
|
281
|
+
}
|
|
282
|
+
);
|
|
252
283
|
const addArgs = [
|
|
253
284
|
"claude",
|
|
254
285
|
"mcp",
|
|
@@ -288,7 +319,9 @@ function setupMcpConfig(cwd, force) {
|
|
|
288
319
|
});
|
|
289
320
|
console.log("Verified MCP server responds correctly");
|
|
290
321
|
} catch {
|
|
291
|
-
console.warn(
|
|
322
|
+
console.warn(
|
|
323
|
+
"Warning: MCP server test failed - check paths and try restarting Claude Code"
|
|
324
|
+
);
|
|
292
325
|
}
|
|
293
326
|
}
|
|
294
327
|
function setupHusky(cwd, force) {
|
|
@@ -348,8 +381,11 @@ function setupHusky(cwd, force) {
|
|
|
348
381
|
function setupLintStaged(cwd, force) {
|
|
349
382
|
const configPath = join(cwd, ".lintstagedrc.json");
|
|
350
383
|
if (!existsSync(configPath) || force) {
|
|
351
|
-
writeFileSync(
|
|
352
|
-
|
|
384
|
+
writeFileSync(
|
|
385
|
+
configPath,
|
|
386
|
+
`${JSON.stringify(LINT_STAGED_CONFIG, null, 2)}
|
|
387
|
+
`
|
|
388
|
+
);
|
|
353
389
|
console.log("Created .lintstagedrc.json");
|
|
354
390
|
} else {
|
|
355
391
|
console.log(".lintstagedrc.json already exists");
|
|
@@ -373,4 +409,4 @@ function checkGitleaks() {
|
|
|
373
409
|
export {
|
|
374
410
|
runInit
|
|
375
411
|
};
|
|
376
|
-
//# sourceMappingURL=chunk-
|
|
412
|
+
//# sourceMappingURL=chunk-KBEAWVRK.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/commands/init.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * sherpa init - Set up repo with husky, lint-staged, gitleaks, and claude hooks\n *\n * Usage: sherpa init [--force]\n */\n\nimport { execSync } from \"child_process\";\nimport { createRequire } from \"module\";\nimport {\n appendFileSync,\n chmodSync,\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"fs\";\nimport { dirname, join, resolve } from \"path\";\nimport { fileURLToPath } from \"url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\ninterface HookEntry {\n matcher: string;\n hooks: Array<{ type: string; command: string }>;\n}\n\ninterface McpServer {\n type: \"stdio\";\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\ninterface McpJson {\n mcpServers: Record<string, McpServer>;\n}\n\ninterface ClaudeSettings {\n hooks?: {\n PreToolUse?: HookEntry[];\n PostToolUse?: HookEntry[];\n };\n [key: string]: unknown;\n}\n\nconst CLAUDE_HOOK_CONFIG = {\n PreToolUse: [\n { matcher: \"Bash\", hooks: [{ type: \"command\", command: \"sherpa pre\" }] },\n ],\n PostToolUse: [\n { matcher: \"Bash\", hooks: [{ type: \"command\", command: \"sherpa post\" }] },\n ],\n};\n\nconst GUARD_CONFIG = {\n maxTokens: 2000,\n previewTokens: 500,\n scratchDir: \".claude/scratch\",\n maxAgeMinutes: 60,\n maxScratchSizeMB: 50,\n};\n\nconst LINT_STAGED_CONFIG = {\n \"*.{js,jsx,ts,tsx,json,md,yml,yaml}\": [\"prettier --write\"],\n};\n\nconst HUSKY_PRE_COMMIT = `npx lint-staged\nif command -v gitleaks >/dev/null 2>&1; then\n\tgitleaks protect --staged --verbose\nelse\n\techo \"gitleaks not found - skipping\"\nfi\n`;\n\n/**\n * Get absolute path to reviewer dist\n */\nfunction getReviewerPath(): string {\n const require = createRequire(import.meta.url);\n const bundledPath = resolve(__dirname, \"..\", \"reviewer\", \"index.js\");\n if (existsSync(bundledPath)) {\n return bundledPath;\n }\n try {\n return require.resolve(\"@goobits/sherpa-reviewer/dist/index.js\");\n } catch {\n // Fall through to monorepo path\n }\n\n // __dirname is packages/sherpa/dist/commands in compiled code\n // Reviewer is at packages/reviewer/dist/index.js\n const reviewerPath = resolve(__dirname, \"../../../reviewer/dist/index.js\");\n if (existsSync(reviewerPath)) {\n return reviewerPath;\n }\n\n return reviewerPath;\n}\n\nfunction commandExists(command: string): boolean {\n try {\n const checkCmd =\n process.platform === \"win32\"\n ? `where ${command}`\n : `command -v ${command}`;\n execSync(checkCmd, { stdio: \"pipe\" });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction getLocalReviewerPath(cwd: string): string | null {\n const localPath = join(\n cwd,\n \"node_modules\",\n \"@goobits\",\n \"sherpa\",\n \"dist\",\n \"reviewer\",\n \"index.js\",\n );\n if (existsSync(localPath)) {\n return \"./node_modules/@goobits/sherpa/dist/reviewer/index.js\";\n }\n\n return null;\n}\n\nfunction getPackageManager(\n cwd: string,\n): \"pnpm\" | \"npm\" | \"yarn\" | \"bun\" | null {\n const pkgPath = join(cwd, \"package.json\");\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as {\n packageManager?: string;\n };\n if (pkg.packageManager) {\n const name = pkg.packageManager.split(\"@\")[0];\n if (\n name === \"pnpm\" ||\n name === \"npm\" ||\n name === \"yarn\" ||\n name === \"bun\"\n ) {\n return name;\n }\n }\n } catch {}\n }\n\n if (existsSync(join(cwd, \"pnpm-lock.yaml\"))) {\n return \"pnpm\";\n }\n if (existsSync(join(cwd, \"yarn.lock\"))) {\n return \"yarn\";\n }\n if (existsSync(join(cwd, \"package-lock.json\"))) {\n return \"npm\";\n }\n if (existsSync(join(cwd, \"bun.lockb\"))) {\n return \"bun\";\n }\n\n if (commandExists(\"npm\")) {\n return \"npm\";\n }\n if (commandExists(\"pnpm\")) {\n return \"pnpm\";\n }\n if (commandExists(\"yarn\")) {\n return \"yarn\";\n }\n if (commandExists(\"bun\")) {\n return \"bun\";\n }\n\n return null;\n}\n\nfunction installLocalSherpa(cwd: string): boolean {\n const pkgPath = join(cwd, \"package.json\");\n if (!existsSync(pkgPath)) {\n return false;\n }\n\n const packageManager = getPackageManager(cwd);\n if (!packageManager || !commandExists(packageManager)) {\n return false;\n }\n\n const installCommand =\n packageManager === \"pnpm\"\n ? \"pnpm add -D @goobits/sherpa\"\n : packageManager === \"yarn\"\n ? \"yarn add -D @goobits/sherpa\"\n : packageManager === \"bun\"\n ? \"bun add -D @goobits/sherpa\"\n : \"npm install -D @goobits/sherpa\";\n\n try {\n console.log(`Installing @goobits/sherpa locally with ${packageManager}...`);\n execSync(installCommand, { cwd, stdio: \"pipe\" });\n } catch {\n console.warn(\n `Warning: Failed to install @goobits/sherpa with ${packageManager}.`,\n );\n return false;\n }\n\n return true;\n}\n\nfunction quoteArg(arg: string): string {\n if (/^[A-Za-z0-9_./:@-]+$/.test(arg)) {\n return arg;\n }\n\n return `\"${arg.replace(/\"/g, '\\\\\"')}\"`;\n}\n\nfunction getMcpCommand(cwd: string): {\n command: string;\n args: string[];\n isPortable: boolean;\n} {\n const localReviewer = getLocalReviewerPath(cwd);\n if (localReviewer) {\n return { command: \"node\", args: [localReviewer], isPortable: true };\n }\n\n if (commandExists(\"reviewer\")) {\n return { command: \"reviewer\", args: [], isPortable: true };\n }\n\n return { command: \"node\", args: [getReviewerPath()], isPortable: false };\n}\n\nexport function runInit(): void {\n const isInitCommand = process.argv[2] === \"init\";\n const initArgs = isInitCommand ? process.argv.slice(3) : [];\n const force = initArgs.includes(\"--force\");\n const cwd = process.cwd();\n\n console.log(\"Setting up sherpa...\\n\");\n\n // 1. Create .claude directory and hooks config\n setupClaudeHooks(cwd, force);\n\n // 2. Set up MCP server in .mcp.json\n setupMcpConfig(cwd, force);\n\n // 3. Set up husky\n setupHusky(cwd, force);\n\n // 4. Set up lint-staged\n setupLintStaged(cwd, force);\n\n // 6. Check for gitleaks\n checkGitleaks();\n\n // Print success\n console.log(`\\n${\"=\".repeat(50)}`);\n console.log(\"Sherpa setup complete!\\n\");\n console.log(\"What was configured:\");\n console.log(\" [x] .claude/settings.local.json - Hooks\");\n console.log(\" [x] .claude/guard.json - Guard config\");\n console.log(\" [x] .mcp.json - MCP servers\");\n console.log(\" [x] .husky/pre-commit - Git pre-commit hook\");\n console.log(\" [x] .lintstagedrc.json - Lint staged files\");\n console.log(\"\");\n console.log(\"Pre-commit will run:\");\n console.log(\" 1. lint-staged (lint/format changed files)\");\n console.log(\" 2. gitleaks (scan for secrets)\");\n console.log(\"\");\n console.log(\"Claude Code:\");\n console.log(\" - sherpa pre: Block dangerous bash commands\");\n console.log(\" - sherpa post: Offload large outputs\");\n console.log(\" - reviewer: AI code review (MCP)\");\n console.log(\"\");\n console.log(\"IMPORTANT: Restart Claude Code to load the MCP server.\");\n console.log(\"=\".repeat(50));\n}\n\nfunction setupClaudeHooks(cwd: string, force: boolean): void {\n const claudeDir = join(cwd, \".claude\");\n const configPath = join(claudeDir, \"guard.json\");\n const settingsPath = join(claudeDir, \"settings.local.json\");\n\n // Create .claude directory\n if (!existsSync(claudeDir)) {\n mkdirSync(claudeDir, { recursive: true });\n console.log(\"Created .claude/ directory\");\n }\n\n // Create guard.json\n if (!existsSync(configPath) || force) {\n writeFileSync(configPath, `${JSON.stringify(GUARD_CONFIG, null, 2)}\\n`);\n console.log(\"Created .claude/guard.json\");\n } else {\n console.log(\".claude/guard.json already exists (use --force to overwrite)\");\n }\n\n // Update settings.local.json with hooks only (not MCP)\n let settings: ClaudeSettings = {};\n if (existsSync(settingsPath)) {\n try {\n settings = JSON.parse(readFileSync(settingsPath, \"utf-8\"));\n } catch {\n console.warn(\"Warning: Could not parse existing settings.local.json\");\n }\n }\n\n // Merge hook config\n settings.hooks = settings.hooks || {};\n let hooksUpdated = false;\n\n for (const [hookType, hooks] of Object.entries(CLAUDE_HOOK_CONFIG)) {\n const existing =\n settings.hooks[hookType as keyof typeof CLAUDE_HOOK_CONFIG] || [];\n const hasSherpa = existing.some((h) =>\n h.hooks?.some((hook: { command?: string }) =>\n hook.command?.startsWith(\"sherpa \"),\n ),\n );\n\n if (!hasSherpa) {\n settings.hooks[hookType as keyof typeof CLAUDE_HOOK_CONFIG] = [\n ...existing,\n ...hooks,\n ];\n hooksUpdated = true;\n }\n }\n\n if (hooksUpdated) {\n writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\\n`);\n console.log(\"Updated .claude/settings.local.json with hooks\");\n } else {\n console.log(\"Claude hooks already configured\");\n }\n}\n\nfunction setupMcpConfig(cwd: string, force: boolean): void {\n const mcpPath = join(cwd, \".mcp.json\");\n const settingsPath = join(cwd, \".claude/settings.local.json\");\n\n if (!getLocalReviewerPath(cwd)) {\n if (installLocalSherpa(cwd)) {\n console.log(\"Installed @goobits/sherpa for portable MCP config.\");\n }\n }\n\n const mcpCommand = getMcpCommand(cwd);\n\n // 1. Clean up stale MCP config from settings.local.json (wrong location)\n if (existsSync(settingsPath)) {\n try {\n const settings = JSON.parse(readFileSync(settingsPath, \"utf-8\"));\n if (settings.mcpServers) {\n delete settings.mcpServers;\n writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\\n`);\n console.log(\"Cleaned up stale MCP config from settings.local.json\");\n }\n } catch {\n // Ignore parse errors\n }\n }\n\n const mcpConfig: McpServer = {\n type: \"stdio\",\n command: mcpCommand.command,\n args: mcpCommand.args,\n env: {},\n };\n\n if (!mcpCommand.isPortable) {\n console.warn(\"Warning: reviewer path is outside the project.\");\n console.warn(\n \"Install @goobits/sherpa locally (devDependency) to keep .mcp.json portable.\",\n );\n }\n\n // 2. Try using claude CLI first (most reliable)\n try {\n // Remove existing and add fresh (always, to ensure correct config)\n execSync(\"claude mcp remove reviewer -s project 2>/dev/null || true\", {\n cwd,\n stdio: \"pipe\",\n });\n execSync(\n \"claude mcp remove cerebras-reviewer -s project 2>/dev/null || true\",\n {\n cwd,\n stdio: \"pipe\",\n },\n );\n const addArgs = [\n \"claude\",\n \"mcp\",\n \"add\",\n \"reviewer\",\n \"-s\",\n \"project\",\n mcpCommand.command,\n ...mcpCommand.args,\n ];\n execSync(addArgs.map(quoteArg).join(\" \"), {\n cwd,\n stdio: \"pipe\",\n });\n console.log(\"Configured MCP server via claude CLI\");\n return;\n } catch {\n // Claude CLI not available, fall back to manual config\n }\n\n // 3. Manual .mcp.json creation (always overwrite reviewer to fix any issues)\n let mcpJson: McpJson = { mcpServers: {} };\n if (existsSync(mcpPath)) {\n try {\n mcpJson = JSON.parse(readFileSync(mcpPath, \"utf-8\"));\n } catch {\n // Start fresh if parse fails\n }\n }\n\n delete mcpJson.mcpServers[\"cerebras-reviewer\"];\n mcpJson.mcpServers[\"reviewer\"] = mcpConfig;\n writeFileSync(mcpPath, `${JSON.stringify(mcpJson, null, 2)}\\n`);\n console.log(\"Configured .mcp.json with reviewer\");\n\n // 4. Verify it works\n try {\n const testMsg =\n '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{},\"clientInfo\":{\"name\":\"test\",\"version\":\"1.0.0\"}}}';\n const runCommand = [mcpCommand.command, ...mcpCommand.args]\n .map(quoteArg)\n .join(\" \");\n execSync(`echo '${testMsg}' | ${runCommand}`, {\n stdio: \"pipe\",\n timeout: 5000,\n });\n console.log(\"Verified MCP server responds correctly\");\n } catch {\n console.warn(\n \"Warning: MCP server test failed - check paths and try restarting Claude Code\",\n );\n }\n}\n\nfunction setupHusky(cwd: string, force: boolean): void {\n const huskyDir = join(cwd, \".husky\");\n const preCommitPath = join(huskyDir, \"pre-commit\");\n const pkgPath = join(cwd, \"package.json\");\n\n if (!existsSync(pkgPath)) {\n console.log(\"No package.json found - skipping husky setup\");\n return;\n }\n\n try {\n JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n } catch {\n console.warn(\"Warning: Could not parse package.json\");\n return;\n }\n\n const hasHusky = existsSync(huskyDir);\n\n if (!hasHusky) {\n try {\n console.log(\"Initializing husky...\");\n execSync(\"npx husky init\", { cwd, stdio: \"pipe\" });\n console.log(\"Initialized husky\");\n } catch {\n console.warn(\"Could not initialize husky automatically\");\n console.warn(\"Run: npx husky init\");\n return;\n }\n }\n\n if (!existsSync(preCommitPath) || force) {\n writeFileSync(preCommitPath, HUSKY_PRE_COMMIT);\n chmodSync(preCommitPath, \"755\");\n console.log(\"Created .husky/pre-commit\");\n } else {\n const existing = readFileSync(preCommitPath, \"utf-8\");\n if (existing.includes(\"npm test\")) {\n writeFileSync(preCommitPath, HUSKY_PRE_COMMIT);\n chmodSync(preCommitPath, \"755\");\n console.log(\"Replaced default pre-commit with lint-staged + gitleaks\");\n return;\n }\n\n let updated = false;\n\n if (!existing.includes(\"lint-staged\")) {\n appendFileSync(preCommitPath, \"\\nnpx lint-staged\\n\");\n updated = true;\n }\n\n if (!existing.includes(\"gitleaks\")) {\n appendFileSync(preCommitPath, \"\\ngitleaks protect --staged --verbose\\n\");\n updated = true;\n }\n\n if (updated) {\n console.log(\"Updated .husky/pre-commit with lint-staged + gitleaks\");\n } else {\n console.log(\".husky/pre-commit already configured\");\n }\n }\n}\n\nfunction setupLintStaged(cwd: string, force: boolean): void {\n const configPath = join(cwd, \".lintstagedrc.json\");\n\n if (!existsSync(configPath) || force) {\n writeFileSync(\n configPath,\n `${JSON.stringify(LINT_STAGED_CONFIG, null, 2)}\\n`,\n );\n console.log(\"Created .lintstagedrc.json\");\n } else {\n console.log(\".lintstagedrc.json already exists\");\n }\n}\n\nfunction checkGitleaks(): void {\n try {\n const checkCmd =\n process.platform === \"win32\" ? \"where gitleaks\" : \"command -v gitleaks\";\n execSync(checkCmd, { stdio: \"pipe\" });\n console.log(\"gitleaks found\");\n } catch {\n console.log(\"\");\n console.log(\"NOTE: gitleaks not found. Install it:\");\n console.log(\" brew install gitleaks # macOS\");\n console.log(\" apt install gitleaks # Debian/Ubuntu\");\n console.log(\" choco install gitleaks # Windows\");\n console.log(\" https://github.com/gitleaks/gitleaks#installing\");\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["require"]
|
|
7
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA2OH,wBAAgB,OAAO,IAAI,IAAI,CA4C9B"}
|
package/dist/index.js
CHANGED
package/dist/reviewer/index.js
CHANGED
|
@@ -9,7 +9,10 @@ import {
|
|
|
9
9
|
// ../reviewer/src/index.ts
|
|
10
10
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
11
11
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
CallToolRequestSchema,
|
|
14
|
+
ListToolsRequestSchema
|
|
15
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
13
16
|
|
|
14
17
|
// ../reviewer/src/tools/review.ts
|
|
15
18
|
import { statSync } from "fs";
|
|
@@ -537,6 +540,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
537
540
|
}
|
|
538
541
|
});
|
|
539
542
|
async function main() {
|
|
543
|
+
process.stdin.resume();
|
|
544
|
+
setInterval(() => {
|
|
545
|
+
}, 6e4);
|
|
540
546
|
const transport = new StdioServerTransport();
|
|
541
547
|
await server.connect(transport);
|
|
542
548
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../reviewer/src/index.ts", "../../../reviewer/src/tools/review.ts", "../../../reviewer/src/prompts.ts", "../../../reviewer/src/tools/tree.ts"],
|
|
4
|
-
"sourcesContent": ["#!/usr/bin/env node\n/**\n * reviewer MCP server\n *\n * Provides code review tools powered by Cerebras LLM\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'\n\nimport { review, type ReviewArgs, reviewTool } from './tools/review.js'\nimport { tree, type TreeArgs, treeTool } from './tools/tree.js'\n\n// Create MCP server\nconst server = new Server(\n {\n name: 'reviewer',\n version: '1.0.0'\n },\n {\n capabilities: {\n tools: {}\n }\n }\n)\n\n// List available tools\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [reviewTool, treeTool]\n}))\n\nfunction tokenizeArgs(input: string): string[] {\n const tokens: string[] = []\n let current = ''\n let quote: '\"' | \"'\" | null = null\n let escape = false\n\n for (const char of input) {\n if (escape) {\n current += char\n escape = false\n continue\n }\n\n if (char === '\\\\') {\n escape = true\n continue\n }\n\n if (quote) {\n if (char === quote) {\n quote = null\n } else {\n current += char\n }\n continue\n }\n\n if (char === '\"' || char === \"'\") {\n quote = char\n continue\n }\n\n if (/\\s/.test(char)) {\n if (current) {\n tokens.push(current)\n current = ''\n }\n continue\n }\n\n current += char\n }\n\n if (current) {\n tokens.push(current)\n }\n\n return tokens\n}\n\nfunction parseReviewArgs(args: unknown): ReviewArgs {\n if (typeof args !== 'string') {\n return (args || {}) as ReviewArgs\n }\n\n const tokens = tokenizeArgs(args)\n const parsed: ReviewArgs = { mode: 'files' }\n const remaining: string[] = []\n\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i]\n\n switch (token) {\n case '--dry':\n case '--dry-run':\n parsed.dryRun = true\n break\n case '--diff':\n parsed.mode = 'diff'\n break\n case '--ask':\n parsed.mode = 'ask'\n break\n case '--base':\n parsed.base = tokens[i + 1]\n i += 1\n break\n case '--path':\n parsed.path = tokens[i + 1]\n i += 1\n break\n case '--focus':\n parsed.focus = tokens[i + 1] as ReviewArgs['focus']\n i += 1\n break\n case '--question':\n parsed.question = tokens[i + 1]\n i += 1\n break\n case '--system':\n parsed.system = tokens[i + 1]\n i += 1\n break\n case '--provider':\n parsed.provider = tokens[i + 1] as ReviewArgs['provider']\n i += 1\n break\n case '--prompt':\n parsed.prompt = tokens[i + 1]\n i += 1\n break\n default:\n remaining.push(token)\n break\n }\n }\n\n if (parsed.mode === 'ask') {\n if (!parsed.prompt && remaining.length > 0) {\n parsed.prompt = remaining.join(' ')\n }\n } else if (parsed.mode === 'diff') {\n if (!parsed.path && remaining.length > 0) {\n parsed.path = remaining.join(' ')\n }\n } else if (remaining.length > 0) {\n parsed.paths = remaining.join(',')\n }\n\n return parsed\n}\n\nfunction parseTreeArgs(args: unknown): TreeArgs {\n if (typeof args !== 'string') {\n return (args || {}) as TreeArgs\n }\n\n const tokens = tokenizeArgs(args)\n const parsed: TreeArgs = {}\n const remaining: string[] = []\n\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i]\n\n switch (token) {\n case '--summary':\n parsed.summary = true\n break\n case '--stats':\n parsed.stats = true\n break\n case '--depth':\n parsed.depth = Number.parseInt(tokens[i + 1] || '', 10)\n i += 1\n break\n default:\n remaining.push(token)\n break\n }\n }\n\n if (remaining.length > 0) {\n parsed.pattern = remaining.join(' ')\n }\n\n return parsed\n}\n\n// Handle tool calls\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params\n\n try {\n let result: string\n\n switch (name) {\n case 'review':\n result = await review(parseReviewArgs(args))\n break\n case 'tree':\n result = await tree(parseTreeArgs(args))\n break\n default:\n throw new Error(`Unknown tool: ${name}`)\n }\n\n return {\n content: [{ type: 'text', text: result }]\n }\n } catch (error) {\n return {\n content: [{ type: 'text', text: `Error: ${(error as Error).message}` }],\n isError: true\n }\n }\n})\n\n// Start server\nasync function main() {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n // Note: No console output - it interferes with MCP protocol\n}\n\nmain().catch(() => process.exit(1))\n", "import { chat, findFiles, getDiff, type Provider, readFilesWithLimit } from '@goobits/sherpa-core'\nimport { statSync } from 'fs'\n\nimport {\n ARCHITECTURE_SYSTEM,\n ASK_SYSTEM,\n CODE_REVIEW_SYSTEM,\n DIFF_REVIEW_SYSTEM,\n FOCUS_INSTRUCTIONS\n} from '../prompts.js'\n\nexport interface ReviewArgs {\n mode?: 'files' | 'diff' | 'ask'\n paths?: string\n question?: string\n focus?: 'general' | 'security' | 'performance' | 'architecture' | 'style'\n base?: string\n path?: string\n prompt?: string\n system?: string | null\n provider?: Provider | null\n dryRun?: boolean\n}\n\nfunction estimateTokens(text: string): number {\n if (!text) {\n return 0\n }\n\n return Math.ceil(text.length / 4)\n}\n\nfunction formatDryRunSummary(options: {\n mode: 'files' | 'diff' | 'ask'\n system: string\n prompt: string\n fileCount?: number\n truncated?: number\n}): string {\n const combined = `${options.system}\\n\\n${options.prompt}`\n const lines = [\n 'Dry run only (no model call).',\n `Mode: ${options.mode}`,\n `System chars: ${options.system.length}`,\n `Prompt chars: ${options.prompt.length}`,\n `Estimated tokens: ${estimateTokens(combined)}`\n ]\n\n if (typeof options.fileCount === 'number') {\n lines.push(`Files: ${options.fileCount}`)\n }\n\n if (options.truncated && options.truncated > 0) {\n lines.push(`Truncated files: ${options.truncated}`)\n }\n\n return lines.join('\\n')\n}\n\nexport async function review(args: ReviewArgs): Promise<string> {\n const {\n mode = 'files',\n paths,\n question,\n focus = 'general',\n base = 'HEAD~1',\n path,\n prompt: askPrompt,\n system: askSystem,\n provider,\n dryRun = false\n } = args\n\n if (mode === 'ask') {\n if (!askPrompt) {\n return 'Missing required \"prompt\" for ask mode.'\n }\n\n const systemPrompt = askSystem || ASK_SYSTEM\n\n if (dryRun) {\n return formatDryRunSummary({\n mode,\n system: systemPrompt,\n prompt: askPrompt\n })\n }\n\n return chat(askPrompt, { system: systemPrompt, provider: provider || undefined })\n }\n\n if (mode === 'diff') {\n try {\n const diff = await getDiff(base, path)\n\n if (!diff.trim()) {\n return 'No changes found.'\n }\n\n const diffPrompt = `Review this diff:\\n\\n\\`\\`\\`diff\\n${diff}\\n\\`\\`\\``\n\n if (dryRun) {\n return formatDryRunSummary({\n mode,\n system: DIFF_REVIEW_SYSTEM,\n prompt: diffPrompt\n })\n }\n\n return chat(diffPrompt, { system: DIFF_REVIEW_SYSTEM })\n } catch (error) {\n return `Git error: ${(error as Error).message}`\n }\n }\n\n if (!paths) {\n return 'Missing required \"paths\" for files mode.'\n }\n\n const pathList = paths.split(',').map((p) => p.trim())\n const filesToReview: string[] = []\n\n for (const p of pathList) {\n if (p.includes('*') || p.includes('?')) {\n const matched = await findFiles(p)\n filesToReview.push(...matched)\n continue\n }\n\n try {\n const stat = statSync(p)\n if (stat.isFile()) {\n filesToReview.push(p)\n } else if (stat.isDirectory()) {\n const pattern = `${p}/**/*`\n const matched = await findFiles(pattern, { codeOnly: true })\n filesToReview.push(...matched)\n }\n } catch {\n return `Path not found: ${p}`\n }\n }\n\n if (filesToReview.length === 0) {\n return 'No code files found to review.'\n }\n\n const { files, truncated } = readFilesWithLimit(filesToReview)\n\n let formattedContent = files.join('\\n')\n if (truncated > 0) {\n formattedContent += `\\n\\n... truncated (${truncated} more files)`\n }\n\n // Build prompt\n const focusText = FOCUS_INSTRUCTIONS[focus] || FOCUS_INSTRUCTIONS.general\n const instruction = question ? `${focusText}\\n\\nSpecific question: ${question}` : focusText\n\n const promptText = `${instruction}\n\nReview the following ${files.length} file(s). For each issue, cite the specific \\`filename:line_number\\`.\n\n${formattedContent}`\n\n const systemPrompt = focus === 'architecture' ? ARCHITECTURE_SYSTEM : CODE_REVIEW_SYSTEM\n\n if (dryRun) {\n return formatDryRunSummary({\n mode,\n system: systemPrompt,\n prompt: promptText,\n fileCount: files.length,\n truncated\n })\n }\n\n return chat(promptText, { system: systemPrompt })\n}\n\nexport const reviewTool = {\n name: 'review',\n description: 'Review code files, diffs, or prompts with Cerebras',\n inputSchema: {\n type: 'object' as const,\n properties: {\n mode: {\n type: 'string',\n enum: ['files', 'diff', 'ask'],\n description: 'Review mode (default: files)'\n },\n paths: {\n type: 'string',\n description:\n 'File paths, directory, or glob pattern (e.g., \"**/*.py\"). Comma-separated for multiple.'\n },\n question: {\n type: 'string',\n description: 'Optional specific question to ask'\n },\n focus: {\n type: 'string',\n enum: ['general', 'security', 'performance', 'architecture', 'style'],\n description: 'Review focus (default: general)'\n },\n base: {\n type: 'string',\n description: 'Diff base commit/branch (default: HEAD~1)'\n },\n path: {\n type: 'string',\n description: 'Optional path filter for diff mode'\n },\n prompt: {\n type: 'string',\n description: 'Prompt text for ask mode'\n },\n system: {\n type: 'string',\n description: 'Optional system prompt override for ask mode'\n },\n provider: {\n type: 'string',\n enum: ['cerebras', 'groq', 'openai'],\n description: 'Provider override for ask mode'\n },\n dryRun: {\n type: 'boolean',\n description: 'Return token estimate only (no model call)'\n }\n },\n required: []\n }\n}\n", "/**\n * System prompts for Cerebras code review\n */\n\nexport const CODE_REVIEW_SYSTEM = `You are an expert code reviewer. For each issue you find, cite the specific location as \\`filename:line_number\\`.\n\nAnalyze the provided code for:\n1. **Correctness** - Logic errors, edge cases, potential bugs\n2. **Security** - Vulnerabilities, injection risks, data exposure\n3. **Performance** - Inefficiencies, unnecessary allocations, O(n) concerns\n4. **Maintainability** - Readability, naming, complexity, SOLID principles\n\nFormat your response as:\n## Summary\n[1-2 sentence overview]\n\n## Issues\n- **[severity]** \\`file:line\\` - description\n\n## Verdict\n[production-ready / needs fixes / major concerns]\n\nBe concise. Prioritize actionable feedback over praise.`\n\nexport const DIFF_REVIEW_SYSTEM = `You are reviewing a git diff. Focus on:\n\n1. **What changed** - Summarize the intent in 1-2 sentences\n2. **Issues** - Bugs, security concerns, or regressions introduced\n3. **Suggestions** - Concrete improvements (if any)\n\nBe brief. Skip obvious or trivial observations. Flag anything that could break production.`\n\nexport const ARCHITECTURE_SYSTEM = `You are a software architect analyzing code structure.\n\nFor each observation, cite the specific location as \\`filename:line_number\\`.\n\nFocus on:\n1. **Organization** - Module boundaries, dependency direction, coupling\n2. **Patterns** - Design patterns in use, antipatterns detected\n3. **Scalability** - Bottlenecks, extension points, rigidity\n\nAnswer the specific question asked. Be direct and technical.`\n\nexport const ASK_SYSTEM = `You are a helpful AI assistant with expertise in software engineering.\nBe concise and direct. Provide code examples when helpful.`\n\n/** Focus-specific instructions */\nexport const FOCUS_INSTRUCTIONS: Record<string, string> = {\n\tgeneral: 'Perform a comprehensive code review.',\n\tsecurity:\n\t\t'Focus especially on security vulnerabilities, injection risks, and data exposure.',\n\tperformance:\n\t\t'Focus especially on performance issues, algorithmic complexity, and resource usage.',\n\tarchitecture:\n\t\t'Focus especially on design patterns, SOLID principles, and code organization.',\n\tstyle: 'Focus especially on code style, readability, and maintainability.'\n}\n", "import { chat, findFiles } from '@goobits/sherpa-core'\n\nimport { ASK_SYSTEM } from '../prompts.js'\n\nexport interface TreeArgs {\n pattern?: string\n depth?: number\n summary?: boolean\n stats?: boolean\n}\n\ntype TreeNode = {\n children: Map<string, TreeNode>\n}\n\nfunction createNode(): TreeNode {\n return { children: new Map() }\n}\n\nfunction buildTree(paths: string[], depth: number): string {\n const root = createNode()\n\n for (const path of paths) {\n const parts = path.split('/').filter(Boolean)\n let node = root\n for (const part of parts) {\n if (!node.children.has(part)) {\n node.children.set(part, createNode())\n }\n node = node.children.get(part) as TreeNode\n }\n }\n\n if (depth <= 0) {\n return '.'\n }\n\n const lines = ['.']\n lines.push(...renderTree(root, '', depth, 0))\n return lines.join('\\n')\n}\n\nfunction renderTree(node: TreeNode, prefix: string, depth: number, level: number): string[] {\n const entries = Array.from(node.children.entries()).sort((a, b) => a[0].localeCompare(b[0]))\n const lines: string[] = []\n\n for (let i = 0; i < entries.length; i += 1) {\n const [name, child] = entries[i]\n const isLast = i === entries.length - 1\n const connector = isLast ? '`-- ' : '|-- '\n const nextPrefix = prefix + (isLast ? ' ' : '| ')\n\n lines.push(`${prefix}${connector}${name}`)\n\n if (child.children.size > 0) {\n if (level + 1 < depth) {\n lines.push(...renderTree(child, nextPrefix, depth, level + 1))\n } else {\n lines.push(`${nextPrefix}\\`-- ...`)\n }\n }\n }\n\n return lines\n}\n\nfunction formatStats(paths: string[]): string {\n const counts = new Map<string, number>()\n\n for (const filePath of paths) {\n const base = filePath.split('/').pop() || ''\n const dotIndex = base.lastIndexOf('.')\n const ext = dotIndex > 0 ? base.slice(dotIndex) : '(none)'\n counts.set(ext, (counts.get(ext) || 0) + 1)\n }\n\n const sorted = Array.from(counts.entries()).sort((a, b) => b[1] - a[1])\n return sorted.map(([ext, count]) => `${ext}: ${count}`).join('\\n')\n}\n\nexport async function tree(args: TreeArgs): Promise<string> {\n const pattern = args.pattern || '**/*'\n const depth = Number.isFinite(args.depth) ? (args.depth as number) : 3\n const summary = Boolean(args.summary)\n const stats = Boolean(args.stats)\n\n const files = await findFiles(pattern)\n\n if (files.length === 0) {\n return 'No files found.'\n }\n\n const treeOutput = buildTree(files, depth)\n const statsOutput = stats ? formatStats(files) : ''\n\n let output = treeOutput\n\n if (stats && statsOutput) {\n output += `\\n\\nStats:\\n${statsOutput}`\n }\n\n if (summary) {\n const summaryPrompt = `Summarize this repository structure for a non-technical reader.\n\nTree:\n${treeOutput}\n\nStats:\n${statsOutput || 'n/a'}`\n const summaryText = await chat(summaryPrompt, { system: ASK_SYSTEM })\n output += `\\n\\nSummary:\\n${summaryText}`\n }\n\n return output\n}\n\nexport const treeTool = {\n name: 'tree',\n description: 'Show a gitignore-aware repository tree (optional summary or stats)',\n inputSchema: {\n type: 'object' as const,\n properties: {\n pattern: {\n type: 'string',\n description: 'Optional glob pattern (e.g., \"**/*.js\")'\n },\n depth: {\n type: 'number',\n description: 'Tree depth (default: 3)'\n },\n summary: {\n type: 'boolean',\n description: 'Include a layman summary (uses LLM)'\n },\n stats: {\n type: 'boolean',\n description: 'Include file extension counts'\n }\n },\n required: []\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;AAOA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC,
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n/**\n * reviewer MCP server\n *\n * Provides code review tools powered by Cerebras LLM\n */\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\nimport { review, type ReviewArgs, reviewTool } from \"./tools/review.js\";\nimport { tree, type TreeArgs, treeTool } from \"./tools/tree.js\";\n\n// Create MCP server\nconst server = new Server(\n {\n name: \"reviewer\",\n version: \"1.0.0\",\n },\n {\n capabilities: {\n tools: {},\n },\n },\n);\n\n// List available tools\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [reviewTool, treeTool],\n}));\n\nfunction tokenizeArgs(input: string): string[] {\n const tokens: string[] = [];\n let current = \"\";\n let quote: '\"' | \"'\" | null = null;\n let escape = false;\n\n for (const char of input) {\n if (escape) {\n current += char;\n escape = false;\n continue;\n }\n\n if (char === \"\\\\\") {\n escape = true;\n continue;\n }\n\n if (quote) {\n if (char === quote) {\n quote = null;\n } else {\n current += char;\n }\n continue;\n }\n\n if (char === '\"' || char === \"'\") {\n quote = char;\n continue;\n }\n\n if (/\\s/.test(char)) {\n if (current) {\n tokens.push(current);\n current = \"\";\n }\n continue;\n }\n\n current += char;\n }\n\n if (current) {\n tokens.push(current);\n }\n\n return tokens;\n}\n\nfunction parseReviewArgs(args: unknown): ReviewArgs {\n if (typeof args !== \"string\") {\n return (args || {}) as ReviewArgs;\n }\n\n const tokens = tokenizeArgs(args);\n const parsed: ReviewArgs = { mode: \"files\" };\n const remaining: string[] = [];\n\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i];\n\n switch (token) {\n case \"--dry\":\n case \"--dry-run\":\n parsed.dryRun = true;\n break;\n case \"--diff\":\n parsed.mode = \"diff\";\n break;\n case \"--ask\":\n parsed.mode = \"ask\";\n break;\n case \"--base\":\n parsed.base = tokens[i + 1];\n i += 1;\n break;\n case \"--path\":\n parsed.path = tokens[i + 1];\n i += 1;\n break;\n case \"--focus\":\n parsed.focus = tokens[i + 1] as ReviewArgs[\"focus\"];\n i += 1;\n break;\n case \"--question\":\n parsed.question = tokens[i + 1];\n i += 1;\n break;\n case \"--system\":\n parsed.system = tokens[i + 1];\n i += 1;\n break;\n case \"--provider\":\n parsed.provider = tokens[i + 1] as ReviewArgs[\"provider\"];\n i += 1;\n break;\n case \"--prompt\":\n parsed.prompt = tokens[i + 1];\n i += 1;\n break;\n default:\n remaining.push(token);\n break;\n }\n }\n\n if (parsed.mode === \"ask\") {\n if (!parsed.prompt && remaining.length > 0) {\n parsed.prompt = remaining.join(\" \");\n }\n } else if (parsed.mode === \"diff\") {\n if (!parsed.path && remaining.length > 0) {\n parsed.path = remaining.join(\" \");\n }\n } else if (remaining.length > 0) {\n parsed.paths = remaining.join(\",\");\n }\n\n return parsed;\n}\n\nfunction parseTreeArgs(args: unknown): TreeArgs {\n if (typeof args !== \"string\") {\n return (args || {}) as TreeArgs;\n }\n\n const tokens = tokenizeArgs(args);\n const parsed: TreeArgs = {};\n const remaining: string[] = [];\n\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i];\n\n switch (token) {\n case \"--summary\":\n parsed.summary = true;\n break;\n case \"--stats\":\n parsed.stats = true;\n break;\n case \"--depth\":\n parsed.depth = Number.parseInt(tokens[i + 1] || \"\", 10);\n i += 1;\n break;\n default:\n remaining.push(token);\n break;\n }\n }\n\n if (remaining.length > 0) {\n parsed.pattern = remaining.join(\" \");\n }\n\n return parsed;\n}\n\n// Handle tool calls\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n let result: string;\n\n switch (name) {\n case \"review\":\n result = await review(parseReviewArgs(args));\n break;\n case \"tree\":\n result = await tree(parseTreeArgs(args));\n break;\n default:\n throw new Error(`Unknown tool: ${name}`);\n }\n\n return {\n content: [{ type: \"text\", text: result }],\n };\n } catch (error) {\n return {\n content: [{ type: \"text\", text: `Error: ${(error as Error).message}` }],\n isError: true,\n };\n }\n});\n\n// Start server\nasync function main() {\n process.stdin.resume();\n setInterval(() => {}, 60_000);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n // Note: No console output - it interferes with MCP protocol\n}\n\nmain().catch(() => process.exit(1));\n", "import { chat, findFiles, getDiff, type Provider, readFilesWithLimit } from '@goobits/sherpa-core'\nimport { statSync } from 'fs'\n\nimport {\n ARCHITECTURE_SYSTEM,\n ASK_SYSTEM,\n CODE_REVIEW_SYSTEM,\n DIFF_REVIEW_SYSTEM,\n FOCUS_INSTRUCTIONS\n} from '../prompts.js'\n\nexport interface ReviewArgs {\n mode?: 'files' | 'diff' | 'ask'\n paths?: string\n question?: string\n focus?: 'general' | 'security' | 'performance' | 'architecture' | 'style'\n base?: string\n path?: string\n prompt?: string\n system?: string | null\n provider?: Provider | null\n dryRun?: boolean\n}\n\nfunction estimateTokens(text: string): number {\n if (!text) {\n return 0\n }\n\n return Math.ceil(text.length / 4)\n}\n\nfunction formatDryRunSummary(options: {\n mode: 'files' | 'diff' | 'ask'\n system: string\n prompt: string\n fileCount?: number\n truncated?: number\n}): string {\n const combined = `${options.system}\\n\\n${options.prompt}`\n const lines = [\n 'Dry run only (no model call).',\n `Mode: ${options.mode}`,\n `System chars: ${options.system.length}`,\n `Prompt chars: ${options.prompt.length}`,\n `Estimated tokens: ${estimateTokens(combined)}`\n ]\n\n if (typeof options.fileCount === 'number') {\n lines.push(`Files: ${options.fileCount}`)\n }\n\n if (options.truncated && options.truncated > 0) {\n lines.push(`Truncated files: ${options.truncated}`)\n }\n\n return lines.join('\\n')\n}\n\nexport async function review(args: ReviewArgs): Promise<string> {\n const {\n mode = 'files',\n paths,\n question,\n focus = 'general',\n base = 'HEAD~1',\n path,\n prompt: askPrompt,\n system: askSystem,\n provider,\n dryRun = false\n } = args\n\n if (mode === 'ask') {\n if (!askPrompt) {\n return 'Missing required \"prompt\" for ask mode.'\n }\n\n const systemPrompt = askSystem || ASK_SYSTEM\n\n if (dryRun) {\n return formatDryRunSummary({\n mode,\n system: systemPrompt,\n prompt: askPrompt\n })\n }\n\n return chat(askPrompt, { system: systemPrompt, provider: provider || undefined })\n }\n\n if (mode === 'diff') {\n try {\n const diff = await getDiff(base, path)\n\n if (!diff.trim()) {\n return 'No changes found.'\n }\n\n const diffPrompt = `Review this diff:\\n\\n\\`\\`\\`diff\\n${diff}\\n\\`\\`\\``\n\n if (dryRun) {\n return formatDryRunSummary({\n mode,\n system: DIFF_REVIEW_SYSTEM,\n prompt: diffPrompt\n })\n }\n\n return chat(diffPrompt, { system: DIFF_REVIEW_SYSTEM })\n } catch (error) {\n return `Git error: ${(error as Error).message}`\n }\n }\n\n if (!paths) {\n return 'Missing required \"paths\" for files mode.'\n }\n\n const pathList = paths.split(',').map((p) => p.trim())\n const filesToReview: string[] = []\n\n for (const p of pathList) {\n if (p.includes('*') || p.includes('?')) {\n const matched = await findFiles(p)\n filesToReview.push(...matched)\n continue\n }\n\n try {\n const stat = statSync(p)\n if (stat.isFile()) {\n filesToReview.push(p)\n } else if (stat.isDirectory()) {\n const pattern = `${p}/**/*`\n const matched = await findFiles(pattern, { codeOnly: true })\n filesToReview.push(...matched)\n }\n } catch {\n return `Path not found: ${p}`\n }\n }\n\n if (filesToReview.length === 0) {\n return 'No code files found to review.'\n }\n\n const { files, truncated } = readFilesWithLimit(filesToReview)\n\n let formattedContent = files.join('\\n')\n if (truncated > 0) {\n formattedContent += `\\n\\n... truncated (${truncated} more files)`\n }\n\n // Build prompt\n const focusText = FOCUS_INSTRUCTIONS[focus] || FOCUS_INSTRUCTIONS.general\n const instruction = question ? `${focusText}\\n\\nSpecific question: ${question}` : focusText\n\n const promptText = `${instruction}\n\nReview the following ${files.length} file(s). For each issue, cite the specific \\`filename:line_number\\`.\n\n${formattedContent}`\n\n const systemPrompt = focus === 'architecture' ? ARCHITECTURE_SYSTEM : CODE_REVIEW_SYSTEM\n\n if (dryRun) {\n return formatDryRunSummary({\n mode,\n system: systemPrompt,\n prompt: promptText,\n fileCount: files.length,\n truncated\n })\n }\n\n return chat(promptText, { system: systemPrompt })\n}\n\nexport const reviewTool = {\n name: 'review',\n description: 'Review code files, diffs, or prompts with Cerebras',\n inputSchema: {\n type: 'object' as const,\n properties: {\n mode: {\n type: 'string',\n enum: ['files', 'diff', 'ask'],\n description: 'Review mode (default: files)'\n },\n paths: {\n type: 'string',\n description:\n 'File paths, directory, or glob pattern (e.g., \"**/*.py\"). Comma-separated for multiple.'\n },\n question: {\n type: 'string',\n description: 'Optional specific question to ask'\n },\n focus: {\n type: 'string',\n enum: ['general', 'security', 'performance', 'architecture', 'style'],\n description: 'Review focus (default: general)'\n },\n base: {\n type: 'string',\n description: 'Diff base commit/branch (default: HEAD~1)'\n },\n path: {\n type: 'string',\n description: 'Optional path filter for diff mode'\n },\n prompt: {\n type: 'string',\n description: 'Prompt text for ask mode'\n },\n system: {\n type: 'string',\n description: 'Optional system prompt override for ask mode'\n },\n provider: {\n type: 'string',\n enum: ['cerebras', 'groq', 'openai'],\n description: 'Provider override for ask mode'\n },\n dryRun: {\n type: 'boolean',\n description: 'Return token estimate only (no model call)'\n }\n },\n required: []\n }\n}\n", "/**\n * System prompts for Cerebras code review\n */\n\nexport const CODE_REVIEW_SYSTEM = `You are an expert code reviewer. For each issue you find, cite the specific location as \\`filename:line_number\\`.\n\nAnalyze the provided code for:\n1. **Correctness** - Logic errors, edge cases, potential bugs\n2. **Security** - Vulnerabilities, injection risks, data exposure\n3. **Performance** - Inefficiencies, unnecessary allocations, O(n) concerns\n4. **Maintainability** - Readability, naming, complexity, SOLID principles\n\nFormat your response as:\n## Summary\n[1-2 sentence overview]\n\n## Issues\n- **[severity]** \\`file:line\\` - description\n\n## Verdict\n[production-ready / needs fixes / major concerns]\n\nBe concise. Prioritize actionable feedback over praise.`\n\nexport const DIFF_REVIEW_SYSTEM = `You are reviewing a git diff. Focus on:\n\n1. **What changed** - Summarize the intent in 1-2 sentences\n2. **Issues** - Bugs, security concerns, or regressions introduced\n3. **Suggestions** - Concrete improvements (if any)\n\nBe brief. Skip obvious or trivial observations. Flag anything that could break production.`\n\nexport const ARCHITECTURE_SYSTEM = `You are a software architect analyzing code structure.\n\nFor each observation, cite the specific location as \\`filename:line_number\\`.\n\nFocus on:\n1. **Organization** - Module boundaries, dependency direction, coupling\n2. **Patterns** - Design patterns in use, antipatterns detected\n3. **Scalability** - Bottlenecks, extension points, rigidity\n\nAnswer the specific question asked. Be direct and technical.`\n\nexport const ASK_SYSTEM = `You are a helpful AI assistant with expertise in software engineering.\nBe concise and direct. Provide code examples when helpful.`\n\n/** Focus-specific instructions */\nexport const FOCUS_INSTRUCTIONS: Record<string, string> = {\n\tgeneral: 'Perform a comprehensive code review.',\n\tsecurity:\n\t\t'Focus especially on security vulnerabilities, injection risks, and data exposure.',\n\tperformance:\n\t\t'Focus especially on performance issues, algorithmic complexity, and resource usage.',\n\tarchitecture:\n\t\t'Focus especially on design patterns, SOLID principles, and code organization.',\n\tstyle: 'Focus especially on code style, readability, and maintainability.'\n}\n", "import { chat, findFiles } from '@goobits/sherpa-core'\n\nimport { ASK_SYSTEM } from '../prompts.js'\n\nexport interface TreeArgs {\n pattern?: string\n depth?: number\n summary?: boolean\n stats?: boolean\n}\n\ntype TreeNode = {\n children: Map<string, TreeNode>\n}\n\nfunction createNode(): TreeNode {\n return { children: new Map() }\n}\n\nfunction buildTree(paths: string[], depth: number): string {\n const root = createNode()\n\n for (const path of paths) {\n const parts = path.split('/').filter(Boolean)\n let node = root\n for (const part of parts) {\n if (!node.children.has(part)) {\n node.children.set(part, createNode())\n }\n node = node.children.get(part) as TreeNode\n }\n }\n\n if (depth <= 0) {\n return '.'\n }\n\n const lines = ['.']\n lines.push(...renderTree(root, '', depth, 0))\n return lines.join('\\n')\n}\n\nfunction renderTree(node: TreeNode, prefix: string, depth: number, level: number): string[] {\n const entries = Array.from(node.children.entries()).sort((a, b) => a[0].localeCompare(b[0]))\n const lines: string[] = []\n\n for (let i = 0; i < entries.length; i += 1) {\n const [name, child] = entries[i]\n const isLast = i === entries.length - 1\n const connector = isLast ? '`-- ' : '|-- '\n const nextPrefix = prefix + (isLast ? ' ' : '| ')\n\n lines.push(`${prefix}${connector}${name}`)\n\n if (child.children.size > 0) {\n if (level + 1 < depth) {\n lines.push(...renderTree(child, nextPrefix, depth, level + 1))\n } else {\n lines.push(`${nextPrefix}\\`-- ...`)\n }\n }\n }\n\n return lines\n}\n\nfunction formatStats(paths: string[]): string {\n const counts = new Map<string, number>()\n\n for (const filePath of paths) {\n const base = filePath.split('/').pop() || ''\n const dotIndex = base.lastIndexOf('.')\n const ext = dotIndex > 0 ? base.slice(dotIndex) : '(none)'\n counts.set(ext, (counts.get(ext) || 0) + 1)\n }\n\n const sorted = Array.from(counts.entries()).sort((a, b) => b[1] - a[1])\n return sorted.map(([ext, count]) => `${ext}: ${count}`).join('\\n')\n}\n\nexport async function tree(args: TreeArgs): Promise<string> {\n const pattern = args.pattern || '**/*'\n const depth = Number.isFinite(args.depth) ? (args.depth as number) : 3\n const summary = Boolean(args.summary)\n const stats = Boolean(args.stats)\n\n const files = await findFiles(pattern)\n\n if (files.length === 0) {\n return 'No files found.'\n }\n\n const treeOutput = buildTree(files, depth)\n const statsOutput = stats ? formatStats(files) : ''\n\n let output = treeOutput\n\n if (stats && statsOutput) {\n output += `\\n\\nStats:\\n${statsOutput}`\n }\n\n if (summary) {\n const summaryPrompt = `Summarize this repository structure for a non-technical reader.\n\nTree:\n${treeOutput}\n\nStats:\n${statsOutput || 'n/a'}`\n const summaryText = await chat(summaryPrompt, { system: ASK_SYSTEM })\n output += `\\n\\nSummary:\\n${summaryText}`\n }\n\n return output\n}\n\nexport const treeTool = {\n name: 'tree',\n description: 'Show a gitignore-aware repository tree (optional summary or stats)',\n inputSchema: {\n type: 'object' as const,\n properties: {\n pattern: {\n type: 'string',\n description: 'Optional glob pattern (e.g., \"**/*.js\")'\n },\n depth: {\n type: 'number',\n description: 'Tree depth (default: 3)'\n },\n summary: {\n type: 'boolean',\n description: 'Include a layman summary (uses LLM)'\n },\n stats: {\n type: 'boolean',\n description: 'Include file extension counts'\n }\n },\n required: []\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["systemPrompt"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/commands/init.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * sherpa init - Set up repo with husky, lint-staged, gitleaks, and claude hooks\n *\n * Usage: sherpa init [--force]\n */\n\nimport { execSync } from 'child_process'\nimport { createRequire } from 'module'\nimport { appendFileSync, chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'\nimport { dirname, join, resolve } from 'path'\nimport { fileURLToPath } from 'url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\ninterface HookEntry {\n matcher: string\n hooks: Array<{ type: string; command: string }>\n}\n\ninterface McpServer {\n type: 'stdio'\n command: string\n args: string[]\n env?: Record<string, string>\n}\n\ninterface McpJson {\n mcpServers: Record<string, McpServer>\n}\n\ninterface ClaudeSettings {\n hooks?: {\n PreToolUse?: HookEntry[]\n PostToolUse?: HookEntry[]\n }\n [key: string]: unknown\n}\n\nconst CLAUDE_HOOK_CONFIG = {\n PreToolUse: [{ matcher: 'Bash', hooks: [{ type: 'command', command: 'sherpa pre' }] }],\n PostToolUse: [{ matcher: 'Bash', hooks: [{ type: 'command', command: 'sherpa post' }] }]\n}\n\nconst GUARD_CONFIG = {\n maxTokens: 2000,\n previewTokens: 500,\n scratchDir: '.claude/scratch',\n maxAgeMinutes: 60,\n maxScratchSizeMB: 50\n}\n\nconst LINT_STAGED_CONFIG = {\n '*.{js,jsx,ts,tsx,json,md,yml,yaml}': ['prettier --write']\n}\n\nconst HUSKY_PRE_COMMIT = `npx lint-staged\nif command -v gitleaks >/dev/null 2>&1; then\n\tgitleaks protect --staged --verbose\nelse\n\techo \"gitleaks not found - skipping\"\nfi\n`\n\n/**\n * Get absolute path to reviewer dist\n */\nfunction getReviewerPath(): string {\n const require = createRequire(import.meta.url)\n const bundledPath = resolve(__dirname, '..', 'reviewer', 'index.js')\n if (existsSync(bundledPath)) {\n return bundledPath\n }\n try {\n return require.resolve('@goobits/sherpa-reviewer/dist/index.js')\n } catch {\n // Fall through to monorepo path\n }\n\n // __dirname is packages/sherpa/dist/commands in compiled code\n // Reviewer is at packages/reviewer/dist/index.js\n const reviewerPath = resolve(__dirname, '../../../reviewer/dist/index.js')\n if (existsSync(reviewerPath)) {\n return reviewerPath\n }\n\n return reviewerPath\n}\n\nfunction commandExists(command: string): boolean {\n try {\n const checkCmd = process.platform === 'win32' ? `where ${command}` : `command -v ${command}`\n execSync(checkCmd, { stdio: 'pipe' })\n return true\n } catch {\n return false\n }\n}\n\nfunction getLocalReviewerPath(cwd: string): string | null {\n const localPath = join(cwd, 'node_modules', '@goobits', 'sherpa', 'dist', 'reviewer', 'index.js')\n if (existsSync(localPath)) {\n return './node_modules/@goobits/sherpa/dist/reviewer/index.js'\n }\n\n return null\n}\n\nfunction getPackageManager(cwd: string): 'pnpm' | 'npm' | 'yarn' | 'bun' | null {\n const pkgPath = join(cwd, 'package.json')\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { packageManager?: string }\n if (pkg.packageManager) {\n const name = pkg.packageManager.split('@')[0]\n if (name === 'pnpm' || name === 'npm' || name === 'yarn' || name === 'bun') {\n return name\n }\n }\n } catch {}\n }\n\n if (existsSync(join(cwd, 'pnpm-lock.yaml'))) {\n return 'pnpm'\n }\n if (existsSync(join(cwd, 'yarn.lock'))) {\n return 'yarn'\n }\n if (existsSync(join(cwd, 'package-lock.json'))) {\n return 'npm'\n }\n if (existsSync(join(cwd, 'bun.lockb'))) {\n return 'bun'\n }\n\n if (commandExists('npm')) {\n return 'npm'\n }\n if (commandExists('pnpm')) {\n return 'pnpm'\n }\n if (commandExists('yarn')) {\n return 'yarn'\n }\n if (commandExists('bun')) {\n return 'bun'\n }\n\n return null\n}\n\nfunction installLocalSherpa(cwd: string): boolean {\n const pkgPath = join(cwd, 'package.json')\n if (!existsSync(pkgPath)) {\n return false\n }\n\n const packageManager = getPackageManager(cwd)\n if (!packageManager || !commandExists(packageManager)) {\n return false\n }\n\n const installCommand =\n packageManager === 'pnpm'\n ? 'pnpm add -D @goobits/sherpa'\n : packageManager === 'yarn'\n ? 'yarn add -D @goobits/sherpa'\n : packageManager === 'bun'\n ? 'bun add -D @goobits/sherpa'\n : 'npm install -D @goobits/sherpa'\n\n try {\n console.log(`Installing @goobits/sherpa locally with ${packageManager}...`)\n execSync(installCommand, { cwd, stdio: 'pipe' })\n } catch {\n console.warn(`Warning: Failed to install @goobits/sherpa with ${packageManager}.`)\n return false\n }\n\n return true\n}\n\nfunction quoteArg(arg: string): string {\n if (/^[A-Za-z0-9_./:@-]+$/.test(arg)) {\n return arg\n }\n\n return `\"${arg.replace(/\"/g, '\\\\\"')}\"`\n}\n\nfunction getMcpCommand(cwd: string): { command: string; args: string[]; isPortable: boolean } {\n const localReviewer = getLocalReviewerPath(cwd)\n if (localReviewer) {\n return { command: 'node', args: [localReviewer], isPortable: true }\n }\n\n if (commandExists('reviewer')) {\n return { command: 'reviewer', args: [], isPortable: true }\n }\n\n return { command: 'node', args: [getReviewerPath()], isPortable: false }\n}\n\nexport function runInit(): void {\n const isInitCommand = process.argv[2] === 'init'\n const initArgs = isInitCommand ? process.argv.slice(3) : []\n const force = initArgs.includes('--force')\n const cwd = process.cwd()\n\n console.log('Setting up sherpa...\\n')\n\n // 1. Create .claude directory and hooks config\n setupClaudeHooks(cwd, force)\n\n // 2. Set up MCP server in .mcp.json\n setupMcpConfig(cwd, force)\n\n // 3. Set up husky\n setupHusky(cwd, force)\n\n // 4. Set up lint-staged\n setupLintStaged(cwd, force)\n\n // 6. Check for gitleaks\n checkGitleaks()\n\n // Print success\n console.log(`\\n${'='.repeat(50)}`)\n console.log('Sherpa setup complete!\\n')\n console.log('What was configured:')\n console.log(' [x] .claude/settings.local.json - Hooks')\n console.log(' [x] .claude/guard.json - Guard config')\n console.log(' [x] .mcp.json - MCP servers')\n console.log(' [x] .husky/pre-commit - Git pre-commit hook')\n console.log(' [x] .lintstagedrc.json - Lint staged files')\n console.log('')\n console.log('Pre-commit will run:')\n console.log(' 1. lint-staged (lint/format changed files)')\n console.log(' 2. gitleaks (scan for secrets)')\n console.log('')\n console.log('Claude Code:')\n console.log(' - sherpa pre: Block dangerous bash commands')\n console.log(' - sherpa post: Offload large outputs')\n console.log(' - reviewer: AI code review (MCP)')\n console.log('')\n console.log('IMPORTANT: Restart Claude Code to load the MCP server.')\n console.log('='.repeat(50))\n}\n\nfunction setupClaudeHooks(cwd: string, force: boolean): void {\n const claudeDir = join(cwd, '.claude')\n const configPath = join(claudeDir, 'guard.json')\n const settingsPath = join(claudeDir, 'settings.local.json')\n\n // Create .claude directory\n if (!existsSync(claudeDir)) {\n mkdirSync(claudeDir, { recursive: true })\n console.log('Created .claude/ directory')\n }\n\n // Create guard.json\n if (!existsSync(configPath) || force) {\n writeFileSync(configPath, `${JSON.stringify(GUARD_CONFIG, null, 2)}\\n`)\n console.log('Created .claude/guard.json')\n } else {\n console.log('.claude/guard.json already exists (use --force to overwrite)')\n }\n\n // Update settings.local.json with hooks only (not MCP)\n let settings: ClaudeSettings = {}\n if (existsSync(settingsPath)) {\n try {\n settings = JSON.parse(readFileSync(settingsPath, 'utf-8'))\n } catch {\n console.warn('Warning: Could not parse existing settings.local.json')\n }\n }\n\n // Merge hook config\n settings.hooks = settings.hooks || {}\n let hooksUpdated = false\n\n for (const [hookType, hooks] of Object.entries(CLAUDE_HOOK_CONFIG)) {\n const existing = settings.hooks[hookType as keyof typeof CLAUDE_HOOK_CONFIG] || []\n const hasSherpa = existing.some((h) =>\n h.hooks?.some((hook: { command?: string }) => hook.command?.startsWith('sherpa '))\n )\n\n if (!hasSherpa) {\n settings.hooks[hookType as keyof typeof CLAUDE_HOOK_CONFIG] = [...existing, ...hooks]\n hooksUpdated = true\n }\n }\n\n if (hooksUpdated) {\n writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\\n`)\n console.log('Updated .claude/settings.local.json with hooks')\n } else {\n console.log('Claude hooks already configured')\n }\n}\n\nfunction setupMcpConfig(cwd: string, force: boolean): void {\n const mcpPath = join(cwd, '.mcp.json')\n const settingsPath = join(cwd, '.claude/settings.local.json')\n\n if (!getLocalReviewerPath(cwd) && !commandExists('reviewer')) {\n if (installLocalSherpa(cwd)) {\n console.log('Installed @goobits/sherpa for portable MCP config.')\n }\n }\n\n const mcpCommand = getMcpCommand(cwd)\n\n // 1. Clean up stale MCP config from settings.local.json (wrong location)\n if (existsSync(settingsPath)) {\n try {\n const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'))\n if (settings.mcpServers) {\n delete settings.mcpServers\n writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\\n`)\n console.log('Cleaned up stale MCP config from settings.local.json')\n }\n } catch {\n // Ignore parse errors\n }\n }\n\n const mcpConfig: McpServer = {\n type: 'stdio',\n command: mcpCommand.command,\n args: mcpCommand.args,\n env: {}\n }\n\n if (!mcpCommand.isPortable) {\n console.warn('Warning: reviewer path is outside the project.')\n console.warn('Install @goobits/sherpa locally (devDependency) to keep .mcp.json portable.')\n }\n\n // 2. Try using claude CLI first (most reliable)\n try {\n // Remove existing and add fresh (always, to ensure correct config)\n execSync('claude mcp remove reviewer -s project 2>/dev/null || true', {\n cwd,\n stdio: 'pipe'\n })\n execSync('claude mcp remove cerebras-reviewer -s project 2>/dev/null || true', {\n cwd,\n stdio: 'pipe'\n })\n const addArgs = [\n 'claude',\n 'mcp',\n 'add',\n 'reviewer',\n '-s',\n 'project',\n mcpCommand.command,\n ...mcpCommand.args\n ]\n execSync(addArgs.map(quoteArg).join(' '), {\n cwd,\n stdio: 'pipe'\n })\n console.log('Configured MCP server via claude CLI')\n return\n } catch {\n // Claude CLI not available, fall back to manual config\n }\n\n // 3. Manual .mcp.json creation (always overwrite reviewer to fix any issues)\n let mcpJson: McpJson = { mcpServers: {} }\n if (existsSync(mcpPath)) {\n try {\n mcpJson = JSON.parse(readFileSync(mcpPath, 'utf-8'))\n } catch {\n // Start fresh if parse fails\n }\n }\n\n delete mcpJson.mcpServers['cerebras-reviewer']\n mcpJson.mcpServers['reviewer'] = mcpConfig\n writeFileSync(mcpPath, `${JSON.stringify(mcpJson, null, 2)}\\n`)\n console.log('Configured .mcp.json with reviewer')\n\n // 4. Verify it works\n try {\n const testMsg =\n '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{},\"clientInfo\":{\"name\":\"test\",\"version\":\"1.0.0\"}}}'\n const runCommand = [mcpCommand.command, ...mcpCommand.args].map(quoteArg).join(' ')\n execSync(`echo '${testMsg}' | ${runCommand}`, {\n stdio: 'pipe',\n timeout: 5000\n })\n console.log('Verified MCP server responds correctly')\n } catch {\n console.warn('Warning: MCP server test failed - check paths and try restarting Claude Code')\n }\n}\n\nfunction setupHusky(cwd: string, force: boolean): void {\n const huskyDir = join(cwd, '.husky')\n const preCommitPath = join(huskyDir, 'pre-commit')\n const pkgPath = join(cwd, 'package.json')\n\n if (!existsSync(pkgPath)) {\n console.log('No package.json found - skipping husky setup')\n return\n }\n\n try {\n JSON.parse(readFileSync(pkgPath, 'utf-8'))\n } catch {\n console.warn('Warning: Could not parse package.json')\n return\n }\n\n const hasHusky = existsSync(huskyDir)\n\n if (!hasHusky) {\n try {\n console.log('Initializing husky...')\n execSync('npx husky init', { cwd, stdio: 'pipe' })\n console.log('Initialized husky')\n } catch {\n console.warn('Could not initialize husky automatically')\n console.warn('Run: npx husky init')\n return\n }\n }\n\n if (!existsSync(preCommitPath) || force) {\n writeFileSync(preCommitPath, HUSKY_PRE_COMMIT)\n chmodSync(preCommitPath, '755')\n console.log('Created .husky/pre-commit')\n } else {\n const existing = readFileSync(preCommitPath, 'utf-8')\n if (existing.includes('npm test')) {\n writeFileSync(preCommitPath, HUSKY_PRE_COMMIT)\n chmodSync(preCommitPath, '755')\n console.log('Replaced default pre-commit with lint-staged + gitleaks')\n return\n }\n\n let updated = false\n\n if (!existing.includes('lint-staged')) {\n appendFileSync(preCommitPath, '\\nnpx lint-staged\\n')\n updated = true\n }\n\n if (!existing.includes('gitleaks')) {\n appendFileSync(preCommitPath, '\\ngitleaks protect --staged --verbose\\n')\n updated = true\n }\n\n if (updated) {\n console.log('Updated .husky/pre-commit with lint-staged + gitleaks')\n } else {\n console.log('.husky/pre-commit already configured')\n }\n }\n}\n\nfunction setupLintStaged(cwd: string, force: boolean): void {\n const configPath = join(cwd, '.lintstagedrc.json')\n\n if (!existsSync(configPath) || force) {\n writeFileSync(configPath, `${JSON.stringify(LINT_STAGED_CONFIG, null, 2)}\\n`)\n console.log('Created .lintstagedrc.json')\n } else {\n console.log('.lintstagedrc.json already exists')\n }\n}\n\nfunction checkGitleaks(): void {\n try {\n const checkCmd = process.platform === 'win32' ? 'where gitleaks' : 'command -v gitleaks'\n execSync(checkCmd, { stdio: 'pipe' })\n console.log('gitleaks found')\n } catch {\n console.log('')\n console.log('NOTE: gitleaks not found. Install it:')\n console.log(' brew install gitleaks # macOS')\n console.log(' apt install gitleaks # Debian/Ubuntu')\n console.log(' choco install gitleaks # Windows')\n console.log(' https://github.com/gitleaks/gitleaks#installing')\n }\n}\n"],
|
|
5
|
-
"mappings": ";AAMA,SAAS,gBAAgB;AACzB,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB,WAAW,YAAY,WAAW,cAAc,qBAAqB;AAC9F,SAAS,SAAS,MAAM,eAAe;AACvC,SAAS,qBAAqB;AAE9B,IAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AA0BxD,IAAM,qBAAqB;AAAA,EACzB,YAAY,CAAC,EAAE,SAAS,QAAQ,OAAO,CAAC,EAAE,MAAM,WAAW,SAAS,aAAa,CAAC,EAAE,CAAC;AAAA,EACrF,aAAa,CAAC,EAAE,SAAS,QAAQ,OAAO,CAAC,EAAE,MAAM,WAAW,SAAS,cAAc,CAAC,EAAE,CAAC;AACzF;AAEA,IAAM,eAAe;AAAA,EACnB,WAAW;AAAA,EACX,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,kBAAkB;AACpB;AAEA,IAAM,qBAAqB;AAAA,EACzB,sCAAsC,CAAC,kBAAkB;AAC3D;AAEA,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWzB,SAAS,kBAA0B;AACjC,QAAMA,WAAU,cAAc,YAAY,GAAG;AAC7C,QAAM,cAAc,QAAQ,WAAW,MAAM,YAAY,UAAU;AACnE,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO;AAAA,EACT;AACA,MAAI;AACF,WAAOA,SAAQ,QAAQ,wCAAwC;AAAA,EACjE,QAAQ;AAAA,EAER;AAIA,QAAM,eAAe,QAAQ,WAAW,iCAAiC;AACzE,MAAI,WAAW,YAAY,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,cAAc,SAA0B;AAC/C,MAAI;AACF,UAAM,WAAW,QAAQ,aAAa,UAAU,SAAS,OAAO,KAAK,cAAc,OAAO;AAC1F,aAAS,UAAU,EAAE,OAAO,OAAO,CAAC;AACpC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,qBAAqB,KAA4B;AACxD,QAAM,YAAY,KAAK,KAAK,gBAAgB,YAAY,UAAU,QAAQ,YAAY,UAAU;AAChG,MAAI,WAAW,SAAS,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,kBAAkB,KAAqD;AAC9E,QAAM,UAAU,KAAK,KAAK,cAAc;AACxC,MAAI,WAAW,OAAO,GAAG;AACvB,QAAI;AACF,YAAM,MAAM,KAAK,MAAM,aAAa,SAAS,OAAO,CAAC;AACrD,UAAI,IAAI,gBAAgB;AACtB,cAAM,OAAO,IAAI,eAAe,MAAM,GAAG,EAAE,CAAC;AAC5C,YAAI,SAAS,UAAU,SAAS,SAAS,SAAS,UAAU,SAAS,OAAO;AAC1E,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAAC;AAAA,EACX;AAEA,MAAI,WAAW,KAAK,KAAK,gBAAgB,CAAC,GAAG;AAC3C,WAAO;AAAA,EACT;AACA,MAAI,WAAW,KAAK,KAAK,WAAW,CAAC,GAAG;AACtC,WAAO;AAAA,EACT;AACA,MAAI,WAAW,KAAK,KAAK,mBAAmB,CAAC,GAAG;AAC9C,WAAO;AAAA,EACT;AACA,MAAI,WAAW,KAAK,KAAK,WAAW,CAAC,GAAG;AACtC,WAAO;AAAA,EACT;AAEA,MAAI,cAAc,KAAK,GAAG;AACxB,WAAO;AAAA,EACT;AACA,MAAI,cAAc,MAAM,GAAG;AACzB,WAAO;AAAA,EACT;AACA,MAAI,cAAc,MAAM,GAAG;AACzB,WAAO;AAAA,EACT;AACA,MAAI,cAAc,KAAK,GAAG;AACxB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,mBAAmB,KAAsB;AAChD,QAAM,UAAU,KAAK,KAAK,cAAc;AACxC,MAAI,CAAC,WAAW,OAAO,GAAG;AACxB,WAAO;AAAA,EACT;AAEA,QAAM,iBAAiB,kBAAkB,GAAG;AAC5C,MAAI,CAAC,kBAAkB,CAAC,cAAc,cAAc,GAAG;AACrD,WAAO;AAAA,EACT;AAEA,QAAM,iBACJ,mBAAmB,SACf,gCACA,mBAAmB,SACjB,gCACA,mBAAmB,QACjB,+BACA;AAEV,MAAI;AACF,YAAQ,IAAI,2CAA2C,cAAc,KAAK;AAC1E,aAAS,gBAAgB,EAAE,KAAK,OAAO,OAAO,CAAC;AAAA,EACjD,QAAQ;AACN,YAAQ,KAAK,mDAAmD,cAAc,GAAG;AACjF,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,SAAS,KAAqB;AACrC,MAAI,uBAAuB,KAAK,GAAG,GAAG;AACpC,WAAO;AAAA,EACT;AAEA,SAAO,IAAI,IAAI,QAAQ,MAAM,KAAK,CAAC;AACrC;AAEA,SAAS,cAAc,KAAuE;AAC5F,QAAM,gBAAgB,qBAAqB,GAAG;AAC9C,MAAI,eAAe;AACjB,WAAO,EAAE,SAAS,QAAQ,MAAM,CAAC,aAAa,GAAG,YAAY,KAAK;AAAA,EACpE;AAEA,MAAI,cAAc,UAAU,GAAG;AAC7B,WAAO,EAAE,SAAS,YAAY,MAAM,CAAC,GAAG,YAAY,KAAK;AAAA,EAC3D;AAEA,SAAO,EAAE,SAAS,QAAQ,MAAM,CAAC,gBAAgB,CAAC,GAAG,YAAY,MAAM;AACzE;AAEO,SAAS,UAAgB;AAC9B,QAAM,gBAAgB,QAAQ,KAAK,CAAC,MAAM;AAC1C,QAAM,WAAW,gBAAgB,QAAQ,KAAK,MAAM,CAAC,IAAI,CAAC;AAC1D,QAAM,QAAQ,SAAS,SAAS,SAAS;AACzC,QAAM,MAAM,QAAQ,IAAI;AAExB,UAAQ,IAAI,wBAAwB;AAGpC,mBAAiB,KAAK,KAAK;AAG3B,iBAAe,KAAK,KAAK;AAGzB,aAAW,KAAK,KAAK;AAGrB,kBAAgB,KAAK,KAAK;AAG1B,gBAAc;AAGd,UAAQ,IAAI;AAAA,EAAK,IAAI,OAAO,EAAE,CAAC,EAAE;AACjC,UAAQ,IAAI,0BAA0B;AACtC,UAAQ,IAAI,sBAAsB;AAClC,UAAQ,IAAI,2CAA2C;AACvD,UAAQ,IAAI,yCAAyC;AACrD,UAAQ,IAAI,+BAA+B;AAC3C,UAAQ,IAAI,+CAA+C;AAC3D,UAAQ,IAAI,8CAA8C;AAC1D,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,sBAAsB;AAClC,UAAQ,IAAI,8CAA8C;AAC1D,UAAQ,IAAI,kCAAkC;AAC9C,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,cAAc;AAC1B,UAAQ,IAAI,+CAA+C;AAC3D,UAAQ,IAAI,wCAAwC;AACpD,UAAQ,IAAI,oCAAoC;AAChD,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,wDAAwD;AACpE,UAAQ,IAAI,IAAI,OAAO,EAAE,CAAC;AAC5B;AAEA,SAAS,iBAAiB,KAAa,OAAsB;AAC3D,QAAM,YAAY,KAAK,KAAK,SAAS;AACrC,QAAM,aAAa,KAAK,WAAW,YAAY;AAC/C,QAAM,eAAe,KAAK,WAAW,qBAAqB;AAG1D,MAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,cAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AACxC,YAAQ,IAAI,4BAA4B;AAAA,EAC1C;AAGA,MAAI,CAAC,WAAW,UAAU,KAAK,OAAO;AACpC,kBAAc,YAAY,GAAG,KAAK,UAAU,cAAc,MAAM,CAAC,CAAC;AAAA,CAAI;AACtE,YAAQ,IAAI,4BAA4B;AAAA,EAC1C,OAAO;AACL,YAAQ,IAAI,8DAA8D;AAAA,EAC5E;AAGA,MAAI,WAA2B,CAAC;AAChC,MAAI,WAAW,YAAY,GAAG;AAC5B,QAAI;AACF,iBAAW,KAAK,MAAM,aAAa,cAAc,OAAO,CAAC;AAAA,IAC3D,QAAQ;AACN,cAAQ,KAAK,uDAAuD;AAAA,IACtE;AAAA,EACF;AAGA,WAAS,QAAQ,SAAS,SAAS,CAAC;AACpC,MAAI,eAAe;AAEnB,aAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,kBAAkB,GAAG;AAClE,UAAM,WAAW,SAAS,MAAM,QAA2C,KAAK,CAAC;AACjF,UAAM,YAAY,SAAS;AAAA,MAAK,CAAC,MAC/B,EAAE,OAAO,KAAK,CAAC,SAA+B,KAAK,SAAS,WAAW,SAAS,CAAC;AAAA,IACnF;AAEA,QAAI,CAAC,WAAW;AACd,eAAS,MAAM,QAA2C,IAAI,CAAC,GAAG,UAAU,GAAG,KAAK;AACpF,qBAAe;AAAA,IACjB;AAAA,EACF;AAEA,MAAI,cAAc;AAChB,kBAAc,cAAc,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,CAAI;AACpE,YAAQ,IAAI,gDAAgD;AAAA,EAC9D,OAAO;AACL,YAAQ,IAAI,iCAAiC;AAAA,EAC/C;AACF;AAEA,SAAS,eAAe,KAAa,OAAsB;AACzD,QAAM,UAAU,KAAK,KAAK,WAAW;AACrC,QAAM,eAAe,KAAK,KAAK,6BAA6B;AAE5D,MAAI,CAAC,qBAAqB,GAAG,KAAK,CAAC,cAAc,UAAU,GAAG;AAC5D,QAAI,mBAAmB,GAAG,GAAG;AAC3B,cAAQ,IAAI,oDAAoD;AAAA,IAClE;AAAA,EACF;AAEA,QAAM,aAAa,cAAc,GAAG;AAGpC,MAAI,WAAW,YAAY,GAAG;AAC5B,QAAI;AACF,YAAM,WAAW,KAAK,MAAM,aAAa,cAAc,OAAO,CAAC;AAC/D,UAAI,SAAS,YAAY;AACvB,eAAO,SAAS;AAChB,sBAAc,cAAc,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,CAAI;AACpE,gBAAQ,IAAI,sDAAsD;AAAA,MACpE;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,YAAuB;AAAA,IAC3B,MAAM;AAAA,IACN,SAAS,WAAW;AAAA,IACpB,MAAM,WAAW;AAAA,IACjB,KAAK,CAAC;AAAA,EACR;AAEA,MAAI,CAAC,WAAW,YAAY;AAC1B,YAAQ,KAAK,gDAAgD;AAC7D,YAAQ,KAAK,6EAA6E;AAAA,EAC5F;AAGA,MAAI;AAEF,aAAS,6DAA6D;AAAA,MACpE;AAAA,MACA,OAAO;AAAA,IACT,CAAC;AACD,aAAS,sEAAsE;AAAA,MAC7E;AAAA,MACA,OAAO;AAAA,IACT,CAAC;AACD,UAAM,UAAU;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX,GAAG,WAAW;AAAA,IAChB;AACA,aAAS,QAAQ,IAAI,QAAQ,EAAE,KAAK,GAAG,GAAG;AAAA,MACxC;AAAA,MACA,OAAO;AAAA,IACT,CAAC;AACD,YAAQ,IAAI,sCAAsC;AAClD;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,MAAI,UAAmB,EAAE,YAAY,CAAC,EAAE;AACxC,MAAI,WAAW,OAAO,GAAG;AACvB,QAAI;AACF,gBAAU,KAAK,MAAM,aAAa,SAAS,OAAO,CAAC;AAAA,IACrD,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO,QAAQ,WAAW,mBAAmB;AAC7C,UAAQ,WAAW,UAAU,IAAI;AACjC,gBAAc,SAAS,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,CAAI;AAC9D,UAAQ,IAAI,oCAAoC;AAGhD,MAAI;AACF,UAAM,UACJ;AACF,UAAM,aAAa,CAAC,WAAW,SAAS,GAAG,WAAW,IAAI,EAAE,IAAI,QAAQ,EAAE,KAAK,GAAG;AAClF,aAAS,SAAS,OAAO,OAAO,UAAU,IAAI;AAAA,MAC5C,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AACD,YAAQ,IAAI,wCAAwC;AAAA,EACtD,QAAQ;AACN,YAAQ,KAAK,8EAA8E;AAAA,EAC7F;AACF;AAEA,SAAS,WAAW,KAAa,OAAsB;AACrD,QAAM,WAAW,KAAK,KAAK,QAAQ;AACnC,QAAM,gBAAgB,KAAK,UAAU,YAAY;AACjD,QAAM,UAAU,KAAK,KAAK,cAAc;AAExC,MAAI,CAAC,WAAW,OAAO,GAAG;AACxB,YAAQ,IAAI,8CAA8C;AAC1D;AAAA,EACF;AAEA,MAAI;AACF,SAAK,MAAM,aAAa,SAAS,OAAO,CAAC;AAAA,EAC3C,QAAQ;AACN,YAAQ,KAAK,uCAAuC;AACpD;AAAA,EACF;AAEA,QAAM,WAAW,WAAW,QAAQ;AAEpC,MAAI,CAAC,UAAU;AACb,QAAI;AACF,cAAQ,IAAI,uBAAuB;AACnC,eAAS,kBAAkB,EAAE,KAAK,OAAO,OAAO,CAAC;AACjD,cAAQ,IAAI,mBAAmB;AAAA,IACjC,QAAQ;AACN,cAAQ,KAAK,0CAA0C;AACvD,cAAQ,KAAK,qBAAqB;AAClC;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,WAAW,aAAa,KAAK,OAAO;AACvC,kBAAc,eAAe,gBAAgB;AAC7C,cAAU,eAAe,KAAK;AAC9B,YAAQ,IAAI,2BAA2B;AAAA,EACzC,OAAO;AACL,UAAM,WAAW,aAAa,eAAe,OAAO;AACpD,QAAI,SAAS,SAAS,UAAU,GAAG;AACjC,oBAAc,eAAe,gBAAgB;AAC7C,gBAAU,eAAe,KAAK;AAC9B,cAAQ,IAAI,yDAAyD;AACrE;AAAA,IACF;AAEA,QAAI,UAAU;AAEd,QAAI,CAAC,SAAS,SAAS,aAAa,GAAG;AACrC,qBAAe,eAAe,qBAAqB;AACnD,gBAAU;AAAA,IACZ;AAEA,QAAI,CAAC,SAAS,SAAS,UAAU,GAAG;AAClC,qBAAe,eAAe,yCAAyC;AACvE,gBAAU;AAAA,IACZ;AAEA,QAAI,SAAS;AACX,cAAQ,IAAI,uDAAuD;AAAA,IACrE,OAAO;AACL,cAAQ,IAAI,sCAAsC;AAAA,IACpD;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,KAAa,OAAsB;AAC1D,QAAM,aAAa,KAAK,KAAK,oBAAoB;AAEjD,MAAI,CAAC,WAAW,UAAU,KAAK,OAAO;AACpC,kBAAc,YAAY,GAAG,KAAK,UAAU,oBAAoB,MAAM,CAAC,CAAC;AAAA,CAAI;AAC5E,YAAQ,IAAI,4BAA4B;AAAA,EAC1C,OAAO;AACL,YAAQ,IAAI,mCAAmC;AAAA,EACjD;AACF;AAEA,SAAS,gBAAsB;AAC7B,MAAI;AACF,UAAM,WAAW,QAAQ,aAAa,UAAU,mBAAmB;AACnE,aAAS,UAAU,EAAE,OAAO,OAAO,CAAC;AACpC,YAAQ,IAAI,gBAAgB;AAAA,EAC9B,QAAQ;AACN,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,uCAAuC;AACnD,YAAQ,IAAI,uCAAuC;AACnD,YAAQ,IAAI,+CAA+C;AAC3D,YAAQ,IAAI,yCAAyC;AACrD,YAAQ,IAAI,mDAAmD;AAAA,EACjE;AACF;",
|
|
6
|
-
"names": ["require"]
|
|
7
|
-
}
|
package/dist/chunk-WBU2V2G7.js
DELETED
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
// src/commands/init.ts
|
|
2
|
-
import { execSync } from "child_process";
|
|
3
|
-
import { createRequire } from "module";
|
|
4
|
-
import { appendFileSync, chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
5
|
-
import { dirname, join, resolve } from "path";
|
|
6
|
-
import { fileURLToPath } from "url";
|
|
7
|
-
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
var CLAUDE_HOOK_CONFIG = {
|
|
9
|
-
PreToolUse: [{ matcher: "Bash", hooks: [{ type: "command", command: "sherpa pre" }] }],
|
|
10
|
-
PostToolUse: [{ matcher: "Bash", hooks: [{ type: "command", command: "sherpa post" }] }]
|
|
11
|
-
};
|
|
12
|
-
var GUARD_CONFIG = {
|
|
13
|
-
maxTokens: 2e3,
|
|
14
|
-
previewTokens: 500,
|
|
15
|
-
scratchDir: ".claude/scratch",
|
|
16
|
-
maxAgeMinutes: 60,
|
|
17
|
-
maxScratchSizeMB: 50
|
|
18
|
-
};
|
|
19
|
-
var LINT_STAGED_CONFIG = {
|
|
20
|
-
"*.{js,jsx,ts,tsx,json,md,yml,yaml}": ["prettier --write"]
|
|
21
|
-
};
|
|
22
|
-
var HUSKY_PRE_COMMIT = `npx lint-staged
|
|
23
|
-
if command -v gitleaks >/dev/null 2>&1; then
|
|
24
|
-
gitleaks protect --staged --verbose
|
|
25
|
-
else
|
|
26
|
-
echo "gitleaks not found - skipping"
|
|
27
|
-
fi
|
|
28
|
-
`;
|
|
29
|
-
function getReviewerPath() {
|
|
30
|
-
const require2 = createRequire(import.meta.url);
|
|
31
|
-
const bundledPath = resolve(__dirname, "..", "reviewer", "index.js");
|
|
32
|
-
if (existsSync(bundledPath)) {
|
|
33
|
-
return bundledPath;
|
|
34
|
-
}
|
|
35
|
-
try {
|
|
36
|
-
return require2.resolve("@goobits/sherpa-reviewer/dist/index.js");
|
|
37
|
-
} catch {
|
|
38
|
-
}
|
|
39
|
-
const reviewerPath = resolve(__dirname, "../../../reviewer/dist/index.js");
|
|
40
|
-
if (existsSync(reviewerPath)) {
|
|
41
|
-
return reviewerPath;
|
|
42
|
-
}
|
|
43
|
-
return reviewerPath;
|
|
44
|
-
}
|
|
45
|
-
function commandExists(command) {
|
|
46
|
-
try {
|
|
47
|
-
const checkCmd = process.platform === "win32" ? `where ${command}` : `command -v ${command}`;
|
|
48
|
-
execSync(checkCmd, { stdio: "pipe" });
|
|
49
|
-
return true;
|
|
50
|
-
} catch {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
function getLocalReviewerPath(cwd) {
|
|
55
|
-
const localPath = join(cwd, "node_modules", "@goobits", "sherpa", "dist", "reviewer", "index.js");
|
|
56
|
-
if (existsSync(localPath)) {
|
|
57
|
-
return "./node_modules/@goobits/sherpa/dist/reviewer/index.js";
|
|
58
|
-
}
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
function getPackageManager(cwd) {
|
|
62
|
-
const pkgPath = join(cwd, "package.json");
|
|
63
|
-
if (existsSync(pkgPath)) {
|
|
64
|
-
try {
|
|
65
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
66
|
-
if (pkg.packageManager) {
|
|
67
|
-
const name = pkg.packageManager.split("@")[0];
|
|
68
|
-
if (name === "pnpm" || name === "npm" || name === "yarn" || name === "bun") {
|
|
69
|
-
return name;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
} catch {
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (existsSync(join(cwd, "pnpm-lock.yaml"))) {
|
|
76
|
-
return "pnpm";
|
|
77
|
-
}
|
|
78
|
-
if (existsSync(join(cwd, "yarn.lock"))) {
|
|
79
|
-
return "yarn";
|
|
80
|
-
}
|
|
81
|
-
if (existsSync(join(cwd, "package-lock.json"))) {
|
|
82
|
-
return "npm";
|
|
83
|
-
}
|
|
84
|
-
if (existsSync(join(cwd, "bun.lockb"))) {
|
|
85
|
-
return "bun";
|
|
86
|
-
}
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
function installLocalSherpa(cwd) {
|
|
90
|
-
const pkgPath = join(cwd, "package.json");
|
|
91
|
-
if (!existsSync(pkgPath)) {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
const packageManager = getPackageManager(cwd);
|
|
95
|
-
if (!packageManager || !commandExists(packageManager)) {
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
const installCommand = packageManager === "pnpm" ? "pnpm add -D @goobits/sherpa" : packageManager === "yarn" ? "yarn add -D @goobits/sherpa" : packageManager === "bun" ? "bun add -D @goobits/sherpa" : "npm install -D @goobits/sherpa";
|
|
99
|
-
try {
|
|
100
|
-
console.log(`Installing @goobits/sherpa locally with ${packageManager}...`);
|
|
101
|
-
execSync(installCommand, { cwd, stdio: "pipe" });
|
|
102
|
-
} catch {
|
|
103
|
-
console.warn(`Warning: Failed to install @goobits/sherpa with ${packageManager}.`);
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
function quoteArg(arg) {
|
|
109
|
-
if (/^[A-Za-z0-9_./:@-]+$/.test(arg)) {
|
|
110
|
-
return arg;
|
|
111
|
-
}
|
|
112
|
-
return `"${arg.replace(/"/g, '\\"')}"`;
|
|
113
|
-
}
|
|
114
|
-
function getMcpCommand(cwd) {
|
|
115
|
-
const localReviewer = getLocalReviewerPath(cwd);
|
|
116
|
-
if (localReviewer) {
|
|
117
|
-
return { command: "node", args: [localReviewer], isPortable: true };
|
|
118
|
-
}
|
|
119
|
-
if (commandExists("reviewer")) {
|
|
120
|
-
return { command: "reviewer", args: [], isPortable: true };
|
|
121
|
-
}
|
|
122
|
-
return { command: "node", args: [getReviewerPath()], isPortable: false };
|
|
123
|
-
}
|
|
124
|
-
function runInit() {
|
|
125
|
-
const isInitCommand = process.argv[2] === "init";
|
|
126
|
-
const initArgs = isInitCommand ? process.argv.slice(3) : [];
|
|
127
|
-
const force = initArgs.includes("--force");
|
|
128
|
-
const cwd = process.cwd();
|
|
129
|
-
console.log("Setting up sherpa...\n");
|
|
130
|
-
setupClaudeHooks(cwd, force);
|
|
131
|
-
setupMcpConfig(cwd, force);
|
|
132
|
-
setupHusky(cwd, force);
|
|
133
|
-
setupLintStaged(cwd, force);
|
|
134
|
-
checkGitleaks();
|
|
135
|
-
console.log(`
|
|
136
|
-
${"=".repeat(50)}`);
|
|
137
|
-
console.log("Sherpa setup complete!\n");
|
|
138
|
-
console.log("What was configured:");
|
|
139
|
-
console.log(" [x] .claude/settings.local.json - Hooks");
|
|
140
|
-
console.log(" [x] .claude/guard.json - Guard config");
|
|
141
|
-
console.log(" [x] .mcp.json - MCP servers");
|
|
142
|
-
console.log(" [x] .husky/pre-commit - Git pre-commit hook");
|
|
143
|
-
console.log(" [x] .lintstagedrc.json - Lint staged files");
|
|
144
|
-
console.log("");
|
|
145
|
-
console.log("Pre-commit will run:");
|
|
146
|
-
console.log(" 1. lint-staged (lint/format changed files)");
|
|
147
|
-
console.log(" 2. gitleaks (scan for secrets)");
|
|
148
|
-
console.log("");
|
|
149
|
-
console.log("Claude Code:");
|
|
150
|
-
console.log(" - sherpa pre: Block dangerous bash commands");
|
|
151
|
-
console.log(" - sherpa post: Offload large outputs");
|
|
152
|
-
console.log(" - reviewer: AI code review (MCP)");
|
|
153
|
-
console.log("");
|
|
154
|
-
console.log("IMPORTANT: Restart Claude Code to load the MCP server.");
|
|
155
|
-
console.log("=".repeat(50));
|
|
156
|
-
}
|
|
157
|
-
function setupClaudeHooks(cwd, force) {
|
|
158
|
-
const claudeDir = join(cwd, ".claude");
|
|
159
|
-
const configPath = join(claudeDir, "guard.json");
|
|
160
|
-
const settingsPath = join(claudeDir, "settings.local.json");
|
|
161
|
-
if (!existsSync(claudeDir)) {
|
|
162
|
-
mkdirSync(claudeDir, { recursive: true });
|
|
163
|
-
console.log("Created .claude/ directory");
|
|
164
|
-
}
|
|
165
|
-
if (!existsSync(configPath) || force) {
|
|
166
|
-
writeFileSync(configPath, `${JSON.stringify(GUARD_CONFIG, null, 2)}
|
|
167
|
-
`);
|
|
168
|
-
console.log("Created .claude/guard.json");
|
|
169
|
-
} else {
|
|
170
|
-
console.log(".claude/guard.json already exists (use --force to overwrite)");
|
|
171
|
-
}
|
|
172
|
-
let settings = {};
|
|
173
|
-
if (existsSync(settingsPath)) {
|
|
174
|
-
try {
|
|
175
|
-
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
176
|
-
} catch {
|
|
177
|
-
console.warn("Warning: Could not parse existing settings.local.json");
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
settings.hooks = settings.hooks || {};
|
|
181
|
-
let hooksUpdated = false;
|
|
182
|
-
for (const [hookType, hooks] of Object.entries(CLAUDE_HOOK_CONFIG)) {
|
|
183
|
-
const existing = settings.hooks[hookType] || [];
|
|
184
|
-
const hasSherpa = existing.some(
|
|
185
|
-
(h) => h.hooks?.some((hook) => hook.command?.startsWith("sherpa "))
|
|
186
|
-
);
|
|
187
|
-
if (!hasSherpa) {
|
|
188
|
-
settings.hooks[hookType] = [...existing, ...hooks];
|
|
189
|
-
hooksUpdated = true;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
if (hooksUpdated) {
|
|
193
|
-
writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
194
|
-
`);
|
|
195
|
-
console.log("Updated .claude/settings.local.json with hooks");
|
|
196
|
-
} else {
|
|
197
|
-
console.log("Claude hooks already configured");
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
function setupMcpConfig(cwd, force) {
|
|
201
|
-
const mcpPath = join(cwd, ".mcp.json");
|
|
202
|
-
const settingsPath = join(cwd, ".claude/settings.local.json");
|
|
203
|
-
if (!getLocalReviewerPath(cwd) && !commandExists("reviewer")) {
|
|
204
|
-
if (installLocalSherpa(cwd)) {
|
|
205
|
-
console.log("Installed @goobits/sherpa for portable MCP config.");
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
const mcpCommand = getMcpCommand(cwd);
|
|
209
|
-
if (existsSync(settingsPath)) {
|
|
210
|
-
try {
|
|
211
|
-
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
212
|
-
if (settings.mcpServers) {
|
|
213
|
-
delete settings.mcpServers;
|
|
214
|
-
writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
215
|
-
`);
|
|
216
|
-
console.log("Cleaned up stale MCP config from settings.local.json");
|
|
217
|
-
}
|
|
218
|
-
} catch {
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
const mcpConfig = {
|
|
222
|
-
type: "stdio",
|
|
223
|
-
command: mcpCommand.command,
|
|
224
|
-
args: mcpCommand.args,
|
|
225
|
-
env: {}
|
|
226
|
-
};
|
|
227
|
-
if (!mcpCommand.isPortable) {
|
|
228
|
-
console.warn("Warning: reviewer path is outside the project.");
|
|
229
|
-
console.warn("Install @goobits/sherpa locally (devDependency) to keep .mcp.json portable.");
|
|
230
|
-
}
|
|
231
|
-
try {
|
|
232
|
-
execSync("claude mcp remove reviewer -s project 2>/dev/null || true", {
|
|
233
|
-
cwd,
|
|
234
|
-
stdio: "pipe"
|
|
235
|
-
});
|
|
236
|
-
execSync("claude mcp remove cerebras-reviewer -s project 2>/dev/null || true", {
|
|
237
|
-
cwd,
|
|
238
|
-
stdio: "pipe"
|
|
239
|
-
});
|
|
240
|
-
const addArgs = [
|
|
241
|
-
"claude",
|
|
242
|
-
"mcp",
|
|
243
|
-
"add",
|
|
244
|
-
"reviewer",
|
|
245
|
-
"-s",
|
|
246
|
-
"project",
|
|
247
|
-
mcpCommand.command,
|
|
248
|
-
...mcpCommand.args
|
|
249
|
-
];
|
|
250
|
-
execSync(addArgs.map(quoteArg).join(" "), {
|
|
251
|
-
cwd,
|
|
252
|
-
stdio: "pipe"
|
|
253
|
-
});
|
|
254
|
-
console.log("Configured MCP server via claude CLI");
|
|
255
|
-
return;
|
|
256
|
-
} catch {
|
|
257
|
-
}
|
|
258
|
-
let mcpJson = { mcpServers: {} };
|
|
259
|
-
if (existsSync(mcpPath)) {
|
|
260
|
-
try {
|
|
261
|
-
mcpJson = JSON.parse(readFileSync(mcpPath, "utf-8"));
|
|
262
|
-
} catch {
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
delete mcpJson.mcpServers["cerebras-reviewer"];
|
|
266
|
-
mcpJson.mcpServers["reviewer"] = mcpConfig;
|
|
267
|
-
writeFileSync(mcpPath, `${JSON.stringify(mcpJson, null, 2)}
|
|
268
|
-
`);
|
|
269
|
-
console.log("Configured .mcp.json with reviewer");
|
|
270
|
-
try {
|
|
271
|
-
const testMsg = '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}';
|
|
272
|
-
const runCommand = [mcpCommand.command, ...mcpCommand.args].map(quoteArg).join(" ");
|
|
273
|
-
execSync(`echo '${testMsg}' | ${runCommand}`, {
|
|
274
|
-
stdio: "pipe",
|
|
275
|
-
timeout: 5e3
|
|
276
|
-
});
|
|
277
|
-
console.log("Verified MCP server responds correctly");
|
|
278
|
-
} catch {
|
|
279
|
-
console.warn("Warning: MCP server test failed - check paths and try restarting Claude Code");
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
function setupHusky(cwd, force) {
|
|
283
|
-
const huskyDir = join(cwd, ".husky");
|
|
284
|
-
const preCommitPath = join(huskyDir, "pre-commit");
|
|
285
|
-
const pkgPath = join(cwd, "package.json");
|
|
286
|
-
if (!existsSync(pkgPath)) {
|
|
287
|
-
console.log("No package.json found - skipping husky setup");
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
try {
|
|
291
|
-
JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
292
|
-
} catch {
|
|
293
|
-
console.warn("Warning: Could not parse package.json");
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
const hasHusky = existsSync(huskyDir);
|
|
297
|
-
if (!hasHusky) {
|
|
298
|
-
try {
|
|
299
|
-
console.log("Initializing husky...");
|
|
300
|
-
execSync("npx husky init", { cwd, stdio: "pipe" });
|
|
301
|
-
console.log("Initialized husky");
|
|
302
|
-
} catch {
|
|
303
|
-
console.warn("Could not initialize husky automatically");
|
|
304
|
-
console.warn("Run: npx husky init");
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
if (!existsSync(preCommitPath) || force) {
|
|
309
|
-
writeFileSync(preCommitPath, HUSKY_PRE_COMMIT);
|
|
310
|
-
chmodSync(preCommitPath, "755");
|
|
311
|
-
console.log("Created .husky/pre-commit");
|
|
312
|
-
} else {
|
|
313
|
-
const existing = readFileSync(preCommitPath, "utf-8");
|
|
314
|
-
if (existing.includes("npm test")) {
|
|
315
|
-
writeFileSync(preCommitPath, HUSKY_PRE_COMMIT);
|
|
316
|
-
chmodSync(preCommitPath, "755");
|
|
317
|
-
console.log("Replaced default pre-commit with lint-staged + gitleaks");
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
let updated = false;
|
|
321
|
-
if (!existing.includes("lint-staged")) {
|
|
322
|
-
appendFileSync(preCommitPath, "\nnpx lint-staged\n");
|
|
323
|
-
updated = true;
|
|
324
|
-
}
|
|
325
|
-
if (!existing.includes("gitleaks")) {
|
|
326
|
-
appendFileSync(preCommitPath, "\ngitleaks protect --staged --verbose\n");
|
|
327
|
-
updated = true;
|
|
328
|
-
}
|
|
329
|
-
if (updated) {
|
|
330
|
-
console.log("Updated .husky/pre-commit with lint-staged + gitleaks");
|
|
331
|
-
} else {
|
|
332
|
-
console.log(".husky/pre-commit already configured");
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
function setupLintStaged(cwd, force) {
|
|
337
|
-
const configPath = join(cwd, ".lintstagedrc.json");
|
|
338
|
-
if (!existsSync(configPath) || force) {
|
|
339
|
-
writeFileSync(configPath, `${JSON.stringify(LINT_STAGED_CONFIG, null, 2)}
|
|
340
|
-
`);
|
|
341
|
-
console.log("Created .lintstagedrc.json");
|
|
342
|
-
} else {
|
|
343
|
-
console.log(".lintstagedrc.json already exists");
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
function checkGitleaks() {
|
|
347
|
-
try {
|
|
348
|
-
const checkCmd = process.platform === "win32" ? "where gitleaks" : "command -v gitleaks";
|
|
349
|
-
execSync(checkCmd, { stdio: "pipe" });
|
|
350
|
-
console.log("gitleaks found");
|
|
351
|
-
} catch {
|
|
352
|
-
console.log("");
|
|
353
|
-
console.log("NOTE: gitleaks not found. Install it:");
|
|
354
|
-
console.log(" brew install gitleaks # macOS");
|
|
355
|
-
console.log(" apt install gitleaks # Debian/Ubuntu");
|
|
356
|
-
console.log(" choco install gitleaks # Windows");
|
|
357
|
-
console.log(" https://github.com/gitleaks/gitleaks#installing");
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
export {
|
|
362
|
-
runInit
|
|
363
|
-
};
|
|
364
|
-
//# sourceMappingURL=chunk-WBU2V2G7.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/commands/init.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * sherpa init - Set up repo with husky, lint-staged, gitleaks, and claude hooks\n *\n * Usage: sherpa init [--force]\n */\n\nimport { execSync } from 'child_process'\nimport { createRequire } from 'module'\nimport { appendFileSync, chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'\nimport { dirname, join, resolve } from 'path'\nimport { fileURLToPath } from 'url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\ninterface HookEntry {\n matcher: string\n hooks: Array<{ type: string; command: string }>\n}\n\ninterface McpServer {\n type: 'stdio'\n command: string\n args: string[]\n env?: Record<string, string>\n}\n\ninterface McpJson {\n mcpServers: Record<string, McpServer>\n}\n\ninterface ClaudeSettings {\n hooks?: {\n PreToolUse?: HookEntry[]\n PostToolUse?: HookEntry[]\n }\n [key: string]: unknown\n}\n\nconst CLAUDE_HOOK_CONFIG = {\n PreToolUse: [{ matcher: 'Bash', hooks: [{ type: 'command', command: 'sherpa pre' }] }],\n PostToolUse: [{ matcher: 'Bash', hooks: [{ type: 'command', command: 'sherpa post' }] }]\n}\n\nconst GUARD_CONFIG = {\n maxTokens: 2000,\n previewTokens: 500,\n scratchDir: '.claude/scratch',\n maxAgeMinutes: 60,\n maxScratchSizeMB: 50\n}\n\nconst LINT_STAGED_CONFIG = {\n '*.{js,jsx,ts,tsx,json,md,yml,yaml}': ['prettier --write']\n}\n\nconst HUSKY_PRE_COMMIT = `npx lint-staged\nif command -v gitleaks >/dev/null 2>&1; then\n\tgitleaks protect --staged --verbose\nelse\n\techo \"gitleaks not found - skipping\"\nfi\n`\n\n/**\n * Get absolute path to reviewer dist\n */\nfunction getReviewerPath(): string {\n const require = createRequire(import.meta.url)\n const bundledPath = resolve(__dirname, '..', 'reviewer', 'index.js')\n if (existsSync(bundledPath)) {\n return bundledPath\n }\n try {\n return require.resolve('@goobits/sherpa-reviewer/dist/index.js')\n } catch {\n // Fall through to monorepo path\n }\n\n // __dirname is packages/sherpa/dist/commands in compiled code\n // Reviewer is at packages/reviewer/dist/index.js\n const reviewerPath = resolve(__dirname, '../../../reviewer/dist/index.js')\n if (existsSync(reviewerPath)) {\n return reviewerPath\n }\n\n return reviewerPath\n}\n\nfunction commandExists(command: string): boolean {\n try {\n const checkCmd = process.platform === 'win32' ? `where ${command}` : `command -v ${command}`\n execSync(checkCmd, { stdio: 'pipe' })\n return true\n } catch {\n return false\n }\n}\n\nfunction getLocalReviewerPath(cwd: string): string | null {\n const localPath = join(cwd, 'node_modules', '@goobits', 'sherpa', 'dist', 'reviewer', 'index.js')\n if (existsSync(localPath)) {\n return './node_modules/@goobits/sherpa/dist/reviewer/index.js'\n }\n\n return null\n}\n\nfunction getPackageManager(cwd: string): 'pnpm' | 'npm' | 'yarn' | 'bun' | null {\n const pkgPath = join(cwd, 'package.json')\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { packageManager?: string }\n if (pkg.packageManager) {\n const name = pkg.packageManager.split('@')[0]\n if (name === 'pnpm' || name === 'npm' || name === 'yarn' || name === 'bun') {\n return name\n }\n }\n } catch {}\n }\n\n if (existsSync(join(cwd, 'pnpm-lock.yaml'))) {\n return 'pnpm'\n }\n if (existsSync(join(cwd, 'yarn.lock'))) {\n return 'yarn'\n }\n if (existsSync(join(cwd, 'package-lock.json'))) {\n return 'npm'\n }\n if (existsSync(join(cwd, 'bun.lockb'))) {\n return 'bun'\n }\n\n return null\n}\n\nfunction installLocalSherpa(cwd: string): boolean {\n const pkgPath = join(cwd, 'package.json')\n if (!existsSync(pkgPath)) {\n return false\n }\n\n const packageManager = getPackageManager(cwd)\n if (!packageManager || !commandExists(packageManager)) {\n return false\n }\n\n const installCommand =\n packageManager === 'pnpm'\n ? 'pnpm add -D @goobits/sherpa'\n : packageManager === 'yarn'\n ? 'yarn add -D @goobits/sherpa'\n : packageManager === 'bun'\n ? 'bun add -D @goobits/sherpa'\n : 'npm install -D @goobits/sherpa'\n\n try {\n console.log(`Installing @goobits/sherpa locally with ${packageManager}...`)\n execSync(installCommand, { cwd, stdio: 'pipe' })\n } catch {\n console.warn(`Warning: Failed to install @goobits/sherpa with ${packageManager}.`)\n return false\n }\n\n return true\n}\n\nfunction quoteArg(arg: string): string {\n if (/^[A-Za-z0-9_./:@-]+$/.test(arg)) {\n return arg\n }\n\n return `\"${arg.replace(/\"/g, '\\\\\"')}\"`\n}\n\nfunction getMcpCommand(cwd: string): { command: string; args: string[]; isPortable: boolean } {\n const localReviewer = getLocalReviewerPath(cwd)\n if (localReviewer) {\n return { command: 'node', args: [localReviewer], isPortable: true }\n }\n\n if (commandExists('reviewer')) {\n return { command: 'reviewer', args: [], isPortable: true }\n }\n\n return { command: 'node', args: [getReviewerPath()], isPortable: false }\n}\n\nexport function runInit(): void {\n const isInitCommand = process.argv[2] === 'init'\n const initArgs = isInitCommand ? process.argv.slice(3) : []\n const force = initArgs.includes('--force')\n const cwd = process.cwd()\n\n console.log('Setting up sherpa...\\n')\n\n // 1. Create .claude directory and hooks config\n setupClaudeHooks(cwd, force)\n\n // 2. Set up MCP server in .mcp.json\n setupMcpConfig(cwd, force)\n\n // 3. Set up husky\n setupHusky(cwd, force)\n\n // 4. Set up lint-staged\n setupLintStaged(cwd, force)\n\n // 6. Check for gitleaks\n checkGitleaks()\n\n // Print success\n console.log(`\\n${'='.repeat(50)}`)\n console.log('Sherpa setup complete!\\n')\n console.log('What was configured:')\n console.log(' [x] .claude/settings.local.json - Hooks')\n console.log(' [x] .claude/guard.json - Guard config')\n console.log(' [x] .mcp.json - MCP servers')\n console.log(' [x] .husky/pre-commit - Git pre-commit hook')\n console.log(' [x] .lintstagedrc.json - Lint staged files')\n console.log('')\n console.log('Pre-commit will run:')\n console.log(' 1. lint-staged (lint/format changed files)')\n console.log(' 2. gitleaks (scan for secrets)')\n console.log('')\n console.log('Claude Code:')\n console.log(' - sherpa pre: Block dangerous bash commands')\n console.log(' - sherpa post: Offload large outputs')\n console.log(' - reviewer: AI code review (MCP)')\n console.log('')\n console.log('IMPORTANT: Restart Claude Code to load the MCP server.')\n console.log('='.repeat(50))\n}\n\nfunction setupClaudeHooks(cwd: string, force: boolean): void {\n const claudeDir = join(cwd, '.claude')\n const configPath = join(claudeDir, 'guard.json')\n const settingsPath = join(claudeDir, 'settings.local.json')\n\n // Create .claude directory\n if (!existsSync(claudeDir)) {\n mkdirSync(claudeDir, { recursive: true })\n console.log('Created .claude/ directory')\n }\n\n // Create guard.json\n if (!existsSync(configPath) || force) {\n writeFileSync(configPath, `${JSON.stringify(GUARD_CONFIG, null, 2)}\\n`)\n console.log('Created .claude/guard.json')\n } else {\n console.log('.claude/guard.json already exists (use --force to overwrite)')\n }\n\n // Update settings.local.json with hooks only (not MCP)\n let settings: ClaudeSettings = {}\n if (existsSync(settingsPath)) {\n try {\n settings = JSON.parse(readFileSync(settingsPath, 'utf-8'))\n } catch {\n console.warn('Warning: Could not parse existing settings.local.json')\n }\n }\n\n // Merge hook config\n settings.hooks = settings.hooks || {}\n let hooksUpdated = false\n\n for (const [hookType, hooks] of Object.entries(CLAUDE_HOOK_CONFIG)) {\n const existing = settings.hooks[hookType as keyof typeof CLAUDE_HOOK_CONFIG] || []\n const hasSherpa = existing.some((h) =>\n h.hooks?.some((hook: { command?: string }) => hook.command?.startsWith('sherpa '))\n )\n\n if (!hasSherpa) {\n settings.hooks[hookType as keyof typeof CLAUDE_HOOK_CONFIG] = [...existing, ...hooks]\n hooksUpdated = true\n }\n }\n\n if (hooksUpdated) {\n writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\\n`)\n console.log('Updated .claude/settings.local.json with hooks')\n } else {\n console.log('Claude hooks already configured')\n }\n}\n\nfunction setupMcpConfig(cwd: string, force: boolean): void {\n const mcpPath = join(cwd, '.mcp.json')\n const settingsPath = join(cwd, '.claude/settings.local.json')\n\n if (!getLocalReviewerPath(cwd) && !commandExists('reviewer')) {\n if (installLocalSherpa(cwd)) {\n console.log('Installed @goobits/sherpa for portable MCP config.')\n }\n }\n\n const mcpCommand = getMcpCommand(cwd)\n\n // 1. Clean up stale MCP config from settings.local.json (wrong location)\n if (existsSync(settingsPath)) {\n try {\n const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'))\n if (settings.mcpServers) {\n delete settings.mcpServers\n writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\\n`)\n console.log('Cleaned up stale MCP config from settings.local.json')\n }\n } catch {\n // Ignore parse errors\n }\n }\n\n const mcpConfig: McpServer = {\n type: 'stdio',\n command: mcpCommand.command,\n args: mcpCommand.args,\n env: {}\n }\n\n if (!mcpCommand.isPortable) {\n console.warn('Warning: reviewer path is outside the project.')\n console.warn('Install @goobits/sherpa locally (devDependency) to keep .mcp.json portable.')\n }\n\n // 2. Try using claude CLI first (most reliable)\n try {\n // Remove existing and add fresh (always, to ensure correct config)\n execSync('claude mcp remove reviewer -s project 2>/dev/null || true', {\n cwd,\n stdio: 'pipe'\n })\n execSync('claude mcp remove cerebras-reviewer -s project 2>/dev/null || true', {\n cwd,\n stdio: 'pipe'\n })\n const addArgs = [\n 'claude',\n 'mcp',\n 'add',\n 'reviewer',\n '-s',\n 'project',\n mcpCommand.command,\n ...mcpCommand.args\n ]\n execSync(addArgs.map(quoteArg).join(' '), {\n cwd,\n stdio: 'pipe'\n })\n console.log('Configured MCP server via claude CLI')\n return\n } catch {\n // Claude CLI not available, fall back to manual config\n }\n\n // 3. Manual .mcp.json creation (always overwrite reviewer to fix any issues)\n let mcpJson: McpJson = { mcpServers: {} }\n if (existsSync(mcpPath)) {\n try {\n mcpJson = JSON.parse(readFileSync(mcpPath, 'utf-8'))\n } catch {\n // Start fresh if parse fails\n }\n }\n\n delete mcpJson.mcpServers['cerebras-reviewer']\n mcpJson.mcpServers['reviewer'] = mcpConfig\n writeFileSync(mcpPath, `${JSON.stringify(mcpJson, null, 2)}\\n`)\n console.log('Configured .mcp.json with reviewer')\n\n // 4. Verify it works\n try {\n const testMsg =\n '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{},\"clientInfo\":{\"name\":\"test\",\"version\":\"1.0.0\"}}}'\n const runCommand = [mcpCommand.command, ...mcpCommand.args].map(quoteArg).join(' ')\n execSync(`echo '${testMsg}' | ${runCommand}`, {\n stdio: 'pipe',\n timeout: 5000\n })\n console.log('Verified MCP server responds correctly')\n } catch {\n console.warn('Warning: MCP server test failed - check paths and try restarting Claude Code')\n }\n}\n\nfunction setupHusky(cwd: string, force: boolean): void {\n const huskyDir = join(cwd, '.husky')\n const preCommitPath = join(huskyDir, 'pre-commit')\n const pkgPath = join(cwd, 'package.json')\n\n if (!existsSync(pkgPath)) {\n console.log('No package.json found - skipping husky setup')\n return\n }\n\n try {\n JSON.parse(readFileSync(pkgPath, 'utf-8'))\n } catch {\n console.warn('Warning: Could not parse package.json')\n return\n }\n\n const hasHusky = existsSync(huskyDir)\n\n if (!hasHusky) {\n try {\n console.log('Initializing husky...')\n execSync('npx husky init', { cwd, stdio: 'pipe' })\n console.log('Initialized husky')\n } catch {\n console.warn('Could not initialize husky automatically')\n console.warn('Run: npx husky init')\n return\n }\n }\n\n if (!existsSync(preCommitPath) || force) {\n writeFileSync(preCommitPath, HUSKY_PRE_COMMIT)\n chmodSync(preCommitPath, '755')\n console.log('Created .husky/pre-commit')\n } else {\n const existing = readFileSync(preCommitPath, 'utf-8')\n if (existing.includes('npm test')) {\n writeFileSync(preCommitPath, HUSKY_PRE_COMMIT)\n chmodSync(preCommitPath, '755')\n console.log('Replaced default pre-commit with lint-staged + gitleaks')\n return\n }\n\n let updated = false\n\n if (!existing.includes('lint-staged')) {\n appendFileSync(preCommitPath, '\\nnpx lint-staged\\n')\n updated = true\n }\n\n if (!existing.includes('gitleaks')) {\n appendFileSync(preCommitPath, '\\ngitleaks protect --staged --verbose\\n')\n updated = true\n }\n\n if (updated) {\n console.log('Updated .husky/pre-commit with lint-staged + gitleaks')\n } else {\n console.log('.husky/pre-commit already configured')\n }\n }\n}\n\nfunction setupLintStaged(cwd: string, force: boolean): void {\n const configPath = join(cwd, '.lintstagedrc.json')\n\n if (!existsSync(configPath) || force) {\n writeFileSync(configPath, `${JSON.stringify(LINT_STAGED_CONFIG, null, 2)}\\n`)\n console.log('Created .lintstagedrc.json')\n } else {\n console.log('.lintstagedrc.json already exists')\n }\n}\n\nfunction checkGitleaks(): void {\n try {\n const checkCmd = process.platform === 'win32' ? 'where gitleaks' : 'command -v gitleaks'\n execSync(checkCmd, { stdio: 'pipe' })\n console.log('gitleaks found')\n } catch {\n console.log('')\n console.log('NOTE: gitleaks not found. Install it:')\n console.log(' brew install gitleaks # macOS')\n console.log(' apt install gitleaks # Debian/Ubuntu')\n console.log(' choco install gitleaks # Windows')\n console.log(' https://github.com/gitleaks/gitleaks#installing')\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["require"]
|
|
7
|
-
}
|