@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.
@@ -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 { appendFileSync, chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
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: [{ matcher: "Bash", hooks: [{ type: "command", command: "sherpa pre" }] }],
10
- PostToolUse: [{ matcher: "Bash", hooks: [{ type: "command", command: "sherpa post" }] }]
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(cwd, "node_modules", "@goobits", "sherpa", "dist", "reviewer", "index.js");
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(`Warning: Failed to install @goobits/sherpa with ${packageManager}.`);
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((hook) => hook.command?.startsWith("sherpa "))
218
+ (h) => h.hooks?.some(
219
+ (hook) => hook.command?.startsWith("sherpa ")
220
+ )
198
221
  );
199
222
  if (!hasSherpa) {
200
- settings.hooks[hookType] = [...existing, ...hooks];
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) && !commandExists("reviewer")) {
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("Install @goobits/sherpa locally (devDependency) to keep .mcp.json portable.");
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("claude mcp remove cerebras-reviewer -s project 2>/dev/null || true", {
249
- cwd,
250
- stdio: "pipe"
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("Warning: MCP server test failed - check paths and try restarting Claude Code");
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(configPath, `${JSON.stringify(LINT_STAGED_CONFIG, null, 2)}
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-PXNRR7WN.js.map
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,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runInit
4
- } from "./chunk-PXNRR7WN.js";
4
+ } from "./chunk-KBEAWVRK.js";
5
5
  import {
6
6
  runPost,
7
7
  runPre
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAsMH,wBAAgB,OAAO,IAAI,IAAI,CA4C9B"}
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
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runInit
3
- } from "./chunk-PXNRR7WN.js";
3
+ } from "./chunk-KBEAWVRK.js";
4
4
  import {
5
5
  DEFAULT_CONFIG,
6
6
  checkBashCommand,
@@ -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 { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
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": "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",
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@goobits/sherpa",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "CLI guard for safer AI coding - blocks dangerous bash commands and manages output context",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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": "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",
6
- "names": ["require"]
7
- }
@@ -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
- }