@posthog/wizard 2.13.0 → 2.13.1

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.
Files changed (45) hide show
  1. package/dist/{AuditChecksViewer-B0J7zcY2.js → AuditChecksViewer-CjBCZjxG.js} +4 -4
  2. package/dist/{AuditChecksViewer-B0J7zcY2.js.map → AuditChecksViewer-CjBCZjxG.js.map} +1 -1
  3. package/dist/{add-mcp-server-to-clients-CUNR00bB.js → add-mcp-server-to-clients-D1IyBa9u.js} +4 -4
  4. package/dist/{add-mcp-server-to-clients-CUNR00bB.js.map → add-mcp-server-to-clients-D1IyBa9u.js.map} +1 -1
  5. package/dist/{agent-interface-CV0-vtxj.js → agent-interface-D9DeIikl.js} +6 -6
  6. package/dist/{agent-interface-CV0-vtxj.js.map → agent-interface-D9DeIikl.js.map} +1 -1
  7. package/dist/{agent-runner-LvVQH31D.js → agent-runner-B41-Iig3.js} +7 -7
  8. package/dist/{agent-runner-LvVQH31D.js.map → agent-runner-B41-Iig3.js.map} +1 -1
  9. package/dist/{analytics-VM7laaFx.js → analytics-Cek5hIwm.js} +2 -2
  10. package/dist/{analytics-VM7laaFx.js.map → analytics-Cek5hIwm.js.map} +1 -1
  11. package/dist/analytics-CpbY05Lf.js +2 -0
  12. package/dist/bin.js +25 -25
  13. package/dist/{debug-Cqi6nVfX.js → debug-B2BH87dh.js} +2 -2
  14. package/dist/{debug-Cqi6nVfX.js.map → debug-B2BH87dh.js.map} +1 -1
  15. package/dist/{debug-BdcTB7EF.js → debug-BNWsxaDm.js} +1 -1
  16. package/dist/{detection-CSjmal-X.js → detection-BFl2AYV6.js} +3 -3
  17. package/dist/{detection-CSjmal-X.js.map → detection-BFl2AYV6.js.map} +1 -1
  18. package/dist/{package-manager-CD8RQW-e.js → package-manager-BBTvHn9i.js} +2 -2
  19. package/dist/{package-manager-CD8RQW-e.js.map → package-manager-BBTvHn9i.js.map} +1 -1
  20. package/dist/{posthog-integration-BL21S3T6.js → posthog-integration-vFBuSN5U.js} +9 -9
  21. package/dist/{posthog-integration-BL21S3T6.js.map → posthog-integration-vFBuSN5U.js.map} +1 -1
  22. package/dist/provisioning--RCv39tI.js +2 -0
  23. package/dist/{provisioning-g9aoVIEd.js → provisioning-DRwH4skH.js} +3 -3
  24. package/dist/{provisioning-g9aoVIEd.js.map → provisioning-DRwH4skH.js.map} +1 -1
  25. package/dist/{registry-BaMEaAKd.js → registry-CZjMhhsK.js} +4 -4
  26. package/dist/{registry-BaMEaAKd.js.map → registry-CZjMhhsK.js.map} +1 -1
  27. package/dist/{setup-utils-CNV7FSlY.js → setup-utils-DGUR4Djo.js} +6 -6
  28. package/dist/{setup-utils-CNV7FSlY.js.map → setup-utils-DGUR4Djo.js.map} +1 -1
  29. package/dist/setup-utils-eh1450iu.js +2 -0
  30. package/dist/{start-playground-C9GWnVdM.js → start-playground-DPYl5WR-.js} +3 -3
  31. package/dist/{start-playground-C9GWnVdM.js.map → start-playground-DPYl5WR-.js.map} +1 -1
  32. package/dist/{start-tui-B_zwutLe.js → start-tui-Cj_4BhK8.js} +10 -10
  33. package/dist/{start-tui-B_zwutLe.js.map → start-tui-Cj_4BhK8.js.map} +1 -1
  34. package/dist/{steps-Dawz7k3T.js → steps-BFD76-MP.js} +6 -6
  35. package/dist/{steps-Dawz7k3T.js.map → steps-BFD76-MP.js.map} +1 -1
  36. package/dist/{telemetry-D6bjWA-A.js → telemetry-DCyjsXhw.js} +2 -2
  37. package/dist/{telemetry-D6bjWA-A.js.map → telemetry-DCyjsXhw.js.map} +1 -1
  38. package/dist/{wizard-abort-CJkNkSjT.js → wizard-abort-54DpTnUi.js} +3 -3
  39. package/dist/{wizard-abort-CJkNkSjT.js.map → wizard-abort-54DpTnUi.js.map} +1 -1
  40. package/dist/{wizard-abort-Dl0BkqhT.js → wizard-abort-CZH03nD0.js} +1 -1
  41. package/npm-shrinkwrap.json +2 -2
  42. package/package.json +1 -1
  43. package/dist/analytics-BH7bEHQR.js +0 -2
  44. package/dist/provisioning-BdQ1ONIg.js +0 -2
  45. package/dist/setup-utils-CU4FIqjB.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"agent-interface-CV0-vtxj.js","names":["_sdkModule","getSDKModule","fs"],"sources":["../src/utils/custom-headers.ts","../src/lib/safe-tools.ts","../src/lib/workflows/audit/types.ts","../src/lib/wizard-tools.ts","../src/lib/yara-scanner.ts","../src/lib/skill-install.ts","../src/lib/yara-hooks.ts","../src/lib/agent/commandments.ts","../src/lib/agent/agent-interface.ts"],"sourcesContent":["import { POSTHOG_FLAG_HEADER_PREFIX } from '../lib/constants';\n\n/**\n * Builds a list of custom headers for ANTHROPIC_CUSTOM_HEADERS.\n */\nexport function createCustomHeaders(): {\n add(key: string, value: string): void;\n /** Add a feature flag for PostHog ($feature/<flagKey>: variant). */\n addFlag(flagKey: string, variant: string): void;\n encode(): string;\n} {\n const entries: Array<{ key: string; value: string }> = [];\n\n return {\n add(key: string, value: string): void {\n const name =\n key.startsWith('x-') || key.startsWith('X-') ? key : `X-${key}`;\n entries.push({ key: name, value });\n },\n\n addFlag(flagKey: string, variant: string): void {\n const headerName = POSTHOG_FLAG_HEADER_PREFIX + flagKey.toUpperCase();\n entries.push({ key: headerName, value: variant });\n },\n\n encode(): string {\n return entries.map(({ key, value }) => `${key}: ${value}`).join('\\n');\n },\n };\n}\n","export const LINTING_TOOLS: string[] = [\n // All (general purpose)\n 'codespell',\n 'cspell',\n 'git-diff-check',\n 'gitleaks',\n 'trufflehog',\n\n // Amazon States Language\n 'asl-validator',\n\n // Ansible\n 'ansible-lint',\n\n // Apex, Java\n 'pmd',\n\n // Astro, CSS, GraphQL, GritQL, HTML, JavaScript, JSON, JSONC, JSON5, JSX, TSX, Svelte, TypeScript, Vue\n 'biome',\n\n // AWS CloudFormation templates\n 'cfn-lint',\n 'cfnlint',\n\n // AWS CloudFormation, Azure ARM, Dockerfile, Helm, Kubernetes, Security, Terraform\n 'checkov',\n\n // AWS CloudFormation, Azure ARM, Dockerfile, Kubernetes, Secrets, Security, Terraform, Vulnerabilities\n 'trivy',\n\n // Azure Resource Manager (ARM)\n 'test-aztemplate',\n\n // Bash / Shell\n 'shellcheck',\n 'shfmt',\n\n // Bazel, Starlark\n 'buildifier',\n\n // C, C++\n 'cpplint',\n 'clang-format',\n 'clang-tidy',\n 'cmake-format',\n 'iwyu',\n 'pragma-once',\n\n // C#, Dotnet (.NET)\n 'dotnet-format',\n\n // CircleCI Config\n 'circleci',\n\n // Clojure\n 'clj-kondo',\n\n // CoffeeScript\n 'coffeelint',\n\n // Commit messages\n 'commitlint',\n\n // Copy/paste detection\n 'jscpd',\n\n // CSS, SCSS, Sass\n 'stylelint',\n\n // CSS, GraphQL, HTML, JavaScript, JSON, JSONC, JSON5, JSX, TSX, Markdown, TypeScript, Vue, YAML\n 'prettier',\n\n // Cue\n 'cue-fmt',\n\n // Dart\n 'dart',\n\n // Dockerfile / Docker\n 'hadolint',\n\n // Dotenv\n 'dotenv-linter',\n\n // EditorConfig\n 'editorconfig-checker',\n\n // GitHub Actions\n 'actionlint',\n 'zizmor',\n\n // Go\n 'gofmt',\n 'gofumpt',\n 'goimports',\n 'gokart',\n 'golangci-lint',\n 'golines',\n\n // Go, Java, JavaScript, JSON, Python, Ruby, TypeScript, YAML\n 'semgrep',\n\n // GoReleaser\n 'goreleaser',\n\n // GraphQL\n 'graphql-schema-linter',\n\n // Groovy\n 'npm-groovy-lint',\n\n // HAML\n 'haml-lint',\n\n // HTML\n 'htmlhint',\n\n // HTML Templates\n 'djlint',\n\n // Java\n 'checkstyle',\n 'google-java-format',\n\n // JavaScript, JSON, TypeScript\n 'eslint',\n\n // Next.js\n 'next lint',\n\n // JavaScript, JSON, Markdown, TypeScript\n 'deno',\n\n // JavaScript, TypeScript\n 'rome',\n\n // JSON, JSONC, JSON5\n 'eslint-plugin-jsonc',\n 'eslint-plugin-json',\n\n // JSX, TSX\n 'eslint-plugin-jsx-a11y',\n 'eslint-plugin-react',\n\n // Jupyter Notebook\n 'nbqa',\n\n // Kotlin\n 'detekt',\n 'ktlint',\n\n // Kubernetes\n 'kubeconform',\n 'kube-linter',\n\n // LaTeX\n 'chktex',\n\n // Lua\n 'luacheck',\n 'stylua',\n\n // Markdown\n 'markdownlint',\n 'markdownlint-cli2',\n 'markdown-link-check',\n 'markdown-table-prettify',\n 'remark-lint',\n\n // Natural language / Prose\n 'textlint',\n 'vale',\n\n // Nix\n 'nixpkgs-fmt',\n\n // OpenAPI\n 'spectral',\n\n // package.json\n 'sort-package-json',\n\n // Perl\n 'perlcritic',\n 'perltidy',\n\n // PHP\n 'php-cs-fixer',\n 'phpcs',\n 'phpstan',\n 'psalm',\n\n // PNG\n 'oxipng',\n\n // PowerShell\n 'psscriptanalyzer',\n\n // Prisma\n 'prisma',\n\n // Protocol Buffers (Protobuf)\n 'protolint',\n 'buf',\n\n // Python\n 'pylint',\n 'flake8',\n 'isort',\n 'ruff',\n 'black',\n 'autopep8',\n 'bandit',\n 'mypy',\n 'pyright',\n 'sourcery',\n 'yapf',\n\n // R\n 'lintr',\n\n // Rego\n 'opa',\n 'regal',\n\n // Ruby\n 'rubocop',\n 'brakeman',\n 'rufo',\n 'standardrb',\n\n // Rust\n 'clippy',\n 'rustfmt',\n\n // Scala\n 'scalafmt',\n\n // Security, Vulnerabilities\n 'osv-scanner',\n\n // Security, Terraform\n 'terrascan',\n 'tfsec',\n\n // Snakemake\n 'snakemake --lint',\n 'snakefmt',\n\n // SQL\n 'sqlfluff',\n 'sql-formatter',\n 'sqlfmt',\n 'squawk',\n\n // SVG\n 'svgo',\n\n // Swift\n 'stringslint',\n 'swiftformat',\n 'swiftlint',\n\n // Terraform\n 'tflint',\n 'terraform',\n 'tofu',\n\n // Terragrunt\n 'terragrunt',\n\n // Textproto\n 'txtpbfmt',\n\n // TOML\n 'taplo',\n\n // Vue\n 'eslint-plugin-vue',\n\n // XML\n 'xmllint',\n\n // YAML\n 'yamllint',\n] as const;\n\nexport type LintingTool = (typeof LINTING_TOOLS)[number];\n","import type { WizardSession } from '../../wizard-session.js';\n\nexport type AuditStatus =\n | 'pending'\n | 'pass'\n | 'error'\n | 'warning'\n | 'suggestion';\n\nexport interface AuditCheck {\n id: string;\n area: string;\n label: string;\n status: AuditStatus;\n file?: string;\n details?: string;\n}\n\nexport interface AuditSeverityStyle {\n glyph: string;\n color: string;\n}\n\n/** Single source of truth for status glyph + color across audit views. */\nexport const AUDIT_SEVERITY_STYLE: Record<AuditStatus, AuditSeverityStyle> = {\n pending: { glyph: '◌', color: 'gray' },\n pass: { glyph: '✔', color: 'green' },\n error: { glyph: '✘', color: 'red' },\n warning: { glyph: '⚠', color: 'yellow' },\n suggestion: { glyph: '•', color: 'cyan' },\n};\n\nexport const AUDIT_CHECKS_FILE = '.posthog-audit-checks.json';\nexport const AUDIT_REPORT_FILE = 'posthog-audit-report.md';\nexport const AUDIT_CHECKS_KEY = 'auditChecks';\n\nexport function getAuditChecks(session: WizardSession): AuditCheck[] {\n const raw = session.frameworkContext[AUDIT_CHECKS_KEY];\n return Array.isArray(raw) ? (raw as AuditCheck[]) : [];\n}\n\n/**\n * Read the audit checks ledger off disk. Validation lives at write time —\n * every writer (`audit_seed_checks` / `audit_add_checks` / `audit_resolve_checks`\n * MCP tools, `seedAuditLedger`) zod-parses entries before the atomic write,\n * so by the time the file watcher fires we trust the shape and only guard\n * against the file not being a JSON array (corrupted / hand-edited / not yet\n * seeded).\n */\nexport function coerceAuditChecks(parsed: unknown): AuditCheck[] {\n return Array.isArray(parsed) ? (parsed as AuditCheck[]) : [];\n}\n","/**\n * Unified in-process MCP server for the PostHog wizard.\n *\n * Provides tools that run locally (secret values never leave the machine):\n * - check_env_keys: Check which env var keys exist in a .env file\n * - set_env_values: Create/update env vars in a .env file\n * - detect_package_manager: Detect the project's package manager(s)\n * - load_skill_menu / install_skill: Skill installation\n * - audit_seed_checks / audit_add_checks / audit_resolve_checks: Audit ledger ownership\n */\n\nimport path from 'path';\nimport fs from 'fs';\nimport { execFileSync } from 'child_process';\nimport { z } from 'zod';\nimport { logToFile } from '../utils/debug';\nimport { skillTmpPath } from '../utils/paths';\nimport type { PackageManagerDetector } from './detection/package-manager';\nimport {\n AUDIT_CHECKS_FILE,\n coerceAuditChecks,\n type AuditCheck,\n type AuditStatus,\n} from './workflows/audit/types';\n\n// ---------------------------------------------------------------------------\n// SDK dynamic import (ESM module loaded once, cached)\n// ---------------------------------------------------------------------------\n\nlet _sdkModule: any = null;\nasync function getSDKModule(): Promise<any> {\n if (!_sdkModule) {\n _sdkModule = await import('@anthropic-ai/claude-agent-sdk');\n }\n return _sdkModule;\n}\n\n// ---------------------------------------------------------------------------\n// Skill types\n// ---------------------------------------------------------------------------\n\nexport type SkillEntry = { id: string; name: string; downloadUrl: string };\n\nexport interface SkillMenu {\n categories: Record<string, SkillEntry[]>;\n}\n\n// ---------------------------------------------------------------------------\n// Standalone skill helpers (usable before the MCP server is created)\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch the skill menu from the skills server.\n * Returns parsed data on success, `null` on failure.\n */\nexport async function fetchSkillMenu(\n skillsBaseUrl: string,\n): Promise<SkillMenu | null> {\n try {\n const menuUrl = `${skillsBaseUrl}/skill-menu.json`;\n logToFile(`fetchSkillMenu: fetching from ${menuUrl}`);\n const resp = await fetch(menuUrl);\n if (resp.ok) {\n const data = (await resp.json()) as SkillMenu;\n logToFile(\n `fetchSkillMenu: loaded (${\n Object.keys(data.categories).length\n } categories)`,\n );\n return data;\n }\n logToFile(`fetchSkillMenu: failed with HTTP ${resp.status}`);\n return null;\n } catch (err: any) {\n logToFile(`fetchSkillMenu: error: ${err.message}`);\n return null;\n }\n}\n\n/**\n * Download and extract a skill.\n * By default installs to `<installDir>/.claude/skills/<id>/`.\n * Pass `skillsRoot` to override the base directory (e.g. `.posthog/skills`).\n */\nexport function downloadSkill(\n skillEntry: SkillEntry,\n installDir: string,\n skillsRoot?: string,\n): { success: boolean; error?: string } {\n const skillDir = skillsRoot\n ? path.join(installDir, skillsRoot, skillEntry.id)\n : path.join(installDir, '.claude', 'skills', skillEntry.id);\n const tmpFile = skillTmpPath(skillEntry.id);\n\n try {\n fs.mkdirSync(skillDir, { recursive: true });\n execFileSync('curl', ['-sL', skillEntry.downloadUrl, '-o', tmpFile], {\n timeout: 30000,\n });\n execFileSync('unzip', ['-o', tmpFile, '-d', skillDir], {\n timeout: 30000,\n });\n fs.writeFileSync(path.join(skillDir, '.posthog-wizard'), '');\n try {\n fs.unlinkSync(tmpFile);\n } catch {\n /* ignore cleanup errors */\n }\n\n logToFile(\n `downloadSkill: installed ${skillEntry.id} from ${skillEntry.downloadUrl}`,\n );\n return { success: true };\n } catch (err: any) {\n logToFile(`downloadSkill: error: ${err.message}`);\n return { success: false, error: err.message };\n }\n}\n\n/**\n * Structured result for installSkillById.\n * - `ok`: the skill was fetched and extracted; `path` is where it lives\n * relative to installDir.\n * - `menu-fetch-failed`: couldn't fetch or parse the skill menu.\n * - `skill-not-found`: the menu didn't contain a skill with this id.\n * - `download-failed`: found the skill but download/extract failed;\n * `message` has the underlying error.\n */\nexport type InstallSkillResult =\n | { kind: 'ok'; path: string }\n | { kind: 'menu-fetch-failed' }\n | { kind: 'skill-not-found'; skillId: string }\n | { kind: 'download-failed'; message: string };\n\n/**\n * High-level \"install a skill by ID\" helper. Fetches the skill menu,\n * finds the skill, downloads and extracts it. Workflows should use this\n * instead of composing fetchSkillMenu + downloadSkill themselves.\n */\nexport async function installSkillById(\n skillId: string,\n installDir: string,\n skillsBaseUrl: string,\n skillsRoot?: string,\n): Promise<InstallSkillResult> {\n const menu = await fetchSkillMenu(skillsBaseUrl);\n if (!menu) return { kind: 'menu-fetch-failed' };\n\n const skill = Object.values(menu.categories)\n .flat()\n .find((s) => s.id === skillId);\n if (!skill) return { kind: 'skill-not-found', skillId };\n\n const result = downloadSkill(skill, installDir, skillsRoot);\n if (!result.success) {\n return { kind: 'download-failed', message: result.error ?? 'unknown' };\n }\n\n const relPath = skillsRoot\n ? `${skillsRoot}/${skillId}`\n : `.claude/skills/${skillId}`;\n return { kind: 'ok', path: relPath };\n}\n\n// ---------------------------------------------------------------------------\n// Options for creating the wizard tools server\n// ---------------------------------------------------------------------------\n\nexport interface WizardToolsOptions {\n /** Root directory of the project being analyzed */\n workingDirectory: string;\n\n /** Framework-specific package manager detector */\n detectPackageManager: PackageManagerDetector;\n\n /** Base URL for the skills server (e.g. http://localhost:8765 or GitHub releases URL) */\n skillsBaseUrl: string;\n}\n\n// ---------------------------------------------------------------------------\n// Env file helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve filePath relative to workingDirectory, rejecting path traversal.\n */\nexport function resolveEnvPath(\n workingDirectory: string,\n filePath: string,\n): string {\n const resolved = path.resolve(workingDirectory, filePath);\n if (\n !resolved.startsWith(workingDirectory + path.sep) &&\n resolved !== workingDirectory\n ) {\n throw new Error(\n `Path traversal rejected: \"${filePath}\" resolves outside working directory`,\n );\n }\n return resolved;\n}\n\n/**\n * Ensure the given env file basename is covered by .gitignore in the working directory.\n * Creates .gitignore if it doesn't exist; appends the entry if missing.\n */\nexport function ensureGitignoreCoverage(\n workingDirectory: string,\n envFileName: string,\n): void {\n const gitignorePath = path.join(workingDirectory, '.gitignore');\n\n if (fs.existsSync(gitignorePath)) {\n const content = fs.readFileSync(gitignorePath, 'utf8');\n // Check if the file (or a glob covering it) is already listed\n if (content.split('\\n').some((line) => line.trim() === envFileName)) {\n return;\n }\n const newContent = content.endsWith('\\n')\n ? `${content}${envFileName}\\n`\n : `${content}\\n${envFileName}\\n`;\n fs.writeFileSync(gitignorePath, newContent, 'utf8');\n } else {\n fs.writeFileSync(gitignorePath, `${envFileName}\\n`, 'utf8');\n }\n}\n\n/**\n * Parse a .env file's content and return the set of defined key names.\n */\nexport function parseEnvKeys(content: string): Set<string> {\n const keys = new Set<string>();\n for (const line of content.split('\\n')) {\n const match = line.match(/^\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*=/);\n if (match) {\n keys.add(match[1]);\n }\n }\n return keys;\n}\n\n/**\n * Merge key-value pairs into existing .env content.\n * Updates existing keys in-place, appends new keys at the end.\n */\nexport function mergeEnvValues(\n content: string,\n values: Record<string, string>,\n): string {\n let result = content;\n const updatedKeys = new Set<string>();\n\n for (const [key, value] of Object.entries(values)) {\n const regex = new RegExp(`^(\\\\s*${key}\\\\s*=).*$`, 'm');\n if (regex.test(result)) {\n result = result.replace(regex, `$1${value}`);\n updatedKeys.add(key);\n }\n }\n\n const newKeys = Object.entries(values).filter(\n ([key]) => !updatedKeys.has(key),\n );\n if (newKeys.length > 0) {\n if (result.length > 0 && !result.endsWith('\\n')) {\n result += '\\n';\n }\n for (const [key, value] of newKeys) {\n result += `${key}=${value}\\n`;\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Audit ledger helpers\n// ---------------------------------------------------------------------------\n\nconst AUDIT_STATUSES: readonly AuditStatus[] = [\n 'pending',\n 'pass',\n 'error',\n 'warning',\n 'suggestion',\n];\n\nconst auditCheckSchema = z.object({\n id: z.string().min(1),\n area: z.string().min(1),\n label: z.string().min(1),\n status: z.enum(AUDIT_STATUSES as [AuditStatus, ...AuditStatus[]]),\n file: z.string().optional(),\n details: z.string().optional(),\n});\n\nconst auditUpdateSchema = z.object({\n id: z.string().min(1),\n status: z.enum(AUDIT_STATUSES as [AuditStatus, ...AuditStatus[]]),\n file: z.string().optional(),\n details: z.string().optional(),\n});\n\n/**\n * Atomically write JSON: write to .tmp then rename. The rename is what bumps\n * the file's mtime, which is what the UI's file watcher polls on.\n */\nfunction writeLedgerAtomic(targetPath: string, checks: AuditCheck[]): void {\n const tmpPath = `${targetPath}.tmp`;\n fs.writeFileSync(tmpPath, JSON.stringify(checks, null, 2), 'utf8');\n fs.renameSync(tmpPath, targetPath);\n}\n\n/**\n * Apply a batch of patches to the ledger by id. Returns the new array and the\n * list of update ids that didn't match any existing check.\n */\nfunction applyAuditUpdates(\n current: AuditCheck[],\n updates: Array<{\n id: string;\n status: AuditStatus;\n file?: string;\n details?: string;\n }>,\n): { next: AuditCheck[]; unknown: string[] } {\n const byId = new Map(current.map((c) => [c.id, c]));\n const unknown: string[] = [];\n\n for (const u of updates) {\n const existing = byId.get(u.id);\n if (!existing) {\n unknown.push(u.id);\n continue;\n }\n byId.set(u.id, {\n ...existing,\n status: u.status,\n ...(u.file !== undefined ? { file: u.file } : {}),\n ...(u.details !== undefined ? { details: u.details } : {}),\n });\n }\n\n return {\n next: current.map((c) => byId.get(c.id) ?? c),\n unknown,\n };\n}\n\n/**\n * Append new checks to a seeded ledger. Duplicate ids are reported without\n * mutating the current ledger, including duplicates inside the additions.\n */\nfunction applyAuditAdditions(\n current: AuditCheck[],\n additions: AuditCheck[],\n): { next: AuditCheck[]; duplicates: string[] } {\n const existingIds = new Set(current.map((c) => c.id));\n const additionIds = new Set<string>();\n const duplicates: string[] = [];\n\n for (const check of additions) {\n if (existingIds.has(check.id) || additionIds.has(check.id)) {\n duplicates.push(check.id);\n continue;\n }\n additionIds.add(check.id);\n }\n\n if (duplicates.length > 0) {\n return { next: current, duplicates };\n }\n\n return { next: [...current, ...additions], duplicates: [] };\n}\n\nfunction readLedger(targetPath: string): AuditCheck[] {\n if (!fs.existsSync(targetPath)) return [];\n try {\n const parsed = JSON.parse(fs.readFileSync(targetPath, 'utf8'));\n return coerceAuditChecks(parsed);\n } catch {\n return [];\n }\n}\n\ntype AppendAuditChecksResult =\n | { ok: true; added: number }\n | { ok: false; reason: 'missing-ledger' }\n | { ok: false; reason: 'duplicate-ids'; ids: string[] };\n\nfunction appendAuditChecksToLedger(\n targetPath: string,\n additions: AuditCheck[],\n): AppendAuditChecksResult {\n if (!fs.existsSync(targetPath)) {\n return { ok: false, reason: 'missing-ledger' };\n }\n\n const current = readLedger(targetPath);\n const { next, duplicates } = applyAuditAdditions(current, additions);\n if (duplicates.length > 0) {\n return { ok: false, reason: 'duplicate-ids', ids: duplicates };\n }\n\n writeLedgerAtomic(targetPath, next);\n return { ok: true, added: additions.length };\n}\n\n/**\n * Single async mutex shared by audit tools — guarantees a read-modify-write\n * cycle on the ledger is atomic across concurrent tool calls (e.g. future subagents).\n */\nfunction makeMutex() {\n let chain: Promise<unknown> = Promise.resolve();\n return async function run<T>(fn: () => Promise<T> | T): Promise<T> {\n const next = chain.then(() => fn());\n chain = next.catch(() => undefined);\n return next;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Server factory\n// ---------------------------------------------------------------------------\n\nconst SERVER_NAME = 'wizard-tools';\n\n/**\n * Create the unified in-process MCP server with all wizard tools.\n * Must be called asynchronously because the SDK is an ESM module loaded via dynamic import.\n */\nexport async function createWizardToolsServer(options: WizardToolsOptions) {\n const { workingDirectory, detectPackageManager, skillsBaseUrl } = options;\n const sdk = await getSDKModule();\n const { tool, createSdkMcpServer } = sdk;\n\n // Pre-fetch skill menu so category names are available in the tool schema\n let cachedSkillMenu: Record<string, SkillEntry[]> = {};\n let categoryNames: [string, ...string[]] = ['integration'];\n\n const menu = await fetchSkillMenu(skillsBaseUrl);\n if (menu) {\n cachedSkillMenu = menu.categories;\n }\n\n const keys = Object.keys(cachedSkillMenu);\n if (keys.length > 0) {\n categoryNames = keys as [string, ...string[]];\n }\n\n // -- check_env_keys -------------------------------------------------------\n\n const checkEnvKeys = tool(\n 'check_env_keys',\n 'Check which environment variable keys are present or missing in a .env file. Never reveals values.',\n {\n filePath: z\n .string()\n .describe('Path to the .env file, relative to the project root'),\n keys: z\n .array(z.string())\n .describe('Environment variable key names to check'),\n },\n (args: { filePath: string; keys: string[] }) => {\n const resolved = resolveEnvPath(workingDirectory, args.filePath);\n logToFile(`check_env_keys: ${resolved}, keys: ${args.keys.join(', ')}`);\n\n const existingKeys: Set<string> = fs.existsSync(resolved)\n ? parseEnvKeys(fs.readFileSync(resolved, 'utf8'))\n : new Set<string>();\n\n const results: Record<string, 'present' | 'missing'> = {};\n for (const key of args.keys) {\n results[key] = existingKeys.has(key) ? 'present' : 'missing';\n }\n\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(results, null, 2) },\n ],\n };\n },\n );\n\n // -- set_env_values -------------------------------------------------------\n\n const setEnvValues = tool(\n 'set_env_values',\n 'Create or update environment variable keys in a .env file. Creates the file if it does not exist. Ensures .gitignore coverage.',\n {\n filePath: z\n .string()\n .describe('Path to the .env file, relative to the project root'),\n values: z\n .record(z.string(), z.string())\n .describe('Key-value pairs to set'),\n },\n (args: { filePath: string; values: Record<string, string> }) => {\n // Block the wrong key name — the correct key is NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN or similar\n const forbidden = Object.keys(args.values).find(\n (k) => k.toUpperCase() === 'POSTHOG_KEY',\n );\n if (forbidden) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: \"${forbidden}\" is not a valid PostHog env var name. Use the project-specific key name from your framework's integration guide (e.g. NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN).`,\n },\n ],\n isError: true,\n };\n }\n\n const resolved = resolveEnvPath(workingDirectory, args.filePath);\n logToFile(\n `set_env_values: ${resolved}, keys: ${Object.keys(args.values).join(\n ', ',\n )}`,\n );\n\n const existing = fs.existsSync(resolved)\n ? fs.readFileSync(resolved, 'utf8')\n : '';\n const content = mergeEnvValues(existing, args.values);\n\n // Ensure parent directory exists\n const dir = path.dirname(resolved);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n fs.writeFileSync(resolved, content, 'utf8');\n\n // Ensure .gitignore coverage for this env file\n const envFileName = path.basename(resolved);\n ensureGitignoreCoverage(workingDirectory, envFileName);\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Updated ${Object.keys(args.values).length} key(s) in ${\n args.filePath\n }`,\n },\n ],\n };\n },\n );\n\n // -- detect_package_manager -----------------------------------------------\n\n const detectPM = tool(\n 'detect_package_manager',\n 'Detect which package manager(s) the project uses. Returns the name, install command, and run command for each detected package manager. Call this before running any install commands.',\n {},\n async () => {\n logToFile(`detect_package_manager: scanning ${workingDirectory}`);\n\n const result = await detectPackageManager(workingDirectory);\n\n logToFile(\n `detect_package_manager: detected ${result.detected.length} package manager(s)`,\n );\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n },\n );\n\n // -- load_skill_menu ------------------------------------------------------\n\n const loadSkillMenu = tool(\n 'load_skill_menu',\n 'Load available PostHog skills for a category. Returns skill IDs and names. Call this first, then use install_skill with the chosen ID.',\n {\n category: z.enum(categoryNames).describe('Skill category'),\n },\n (args: { category: string }) => {\n const skills = cachedSkillMenu[args.category];\n if (!skills || skills.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `No skills found for category \"${args.category}\".`,\n },\n ],\n isError: true,\n };\n }\n\n const menuText = skills.map((s) => `- ${s.id}: ${s.name}`).join('\\n');\n\n logToFile(\n `load_skill_menu: returning ${skills.length} skills for \"${args.category}\"`,\n );\n\n return {\n content: [{ type: 'text' as const, text: menuText }],\n };\n },\n );\n\n // -- install_skill --------------------------------------------------------\n\n const installSkill = tool(\n 'install_skill',\n 'Download and install a PostHog skill by ID. Call load_skill_menu first to see available skills. Extracts the skill to .claude/skills/<skillId>/.',\n {\n skillId: z\n .string()\n .describe(\n 'Skill ID from the skill menu (e.g., \"integration-nextjs-app-router\")',\n ),\n },\n (args: { skillId: string }) => {\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(args.skillId)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Error: skillId must be lowercase alphanumeric with hyphens.',\n },\n ],\n isError: true,\n };\n }\n\n // Look up download URL from cached menu\n const allSkills: SkillEntry[] = Object.values(cachedSkillMenu).flat();\n const skill = allSkills.find((s) => s.id === args.skillId);\n if (!skill) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: skill \"${args.skillId}\" not found. Use load_skill_menu to see available skills.`,\n },\n ],\n isError: true,\n };\n }\n\n const result = downloadSkill(skill, workingDirectory);\n if (result.success) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Skill installed to .claude/skills/${args.skillId}/`,\n },\n ],\n };\n } else {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error installing skill: ${result.error}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n\n // -- audit_seed_checks ----------------------------------------------------\n\n const auditLedgerPath = path.join(workingDirectory, AUDIT_CHECKS_FILE);\n const auditMutex = makeMutex();\n\n const auditSeedChecks = tool(\n 'audit_seed_checks',\n 'Seed the audit ledger at .posthog-audit-checks.json with the full set of pending checks. Call this once at the start of the audit. Atomically replaces any existing ledger.',\n {\n checks: z\n .array(auditCheckSchema)\n .describe('Full pending checklist to write to the ledger'),\n },\n async (args: { checks: AuditCheck[] }) => {\n return auditMutex(() => {\n writeLedgerAtomic(auditLedgerPath, args.checks);\n logToFile(`audit_seed_checks: wrote ${args.checks.length} entries`);\n return {\n content: [\n {\n type: 'text' as const,\n text: `Seeded ${args.checks.length} audit checks.`,\n },\n ],\n };\n });\n },\n );\n\n // -- audit_add_checks -----------------------------------------------------\n\n const auditAddChecks = tool(\n 'audit_add_checks',\n 'Append one or more pending checks to the existing audit ledger at .posthog-audit-checks.json. Call audit_seed_checks first. Atomically rejects duplicate ids without changing the ledger.',\n {\n checks: z\n .array(auditCheckSchema)\n .min(1)\n .describe('Additional checks to append to the existing ledger'),\n },\n async (args: { checks: AuditCheck[] }) => {\n return auditMutex(() => {\n const result = appendAuditChecksToLedger(auditLedgerPath, args.checks);\n\n if (!result.ok) {\n if (result.reason === 'missing-ledger') {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Error: audit ledger does not exist. Run audit_seed_checks first.',\n },\n ],\n isError: true,\n };\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: duplicate check id(s): ${result.ids.join(\n ', ',\n )}. Check ids must be unique.`,\n },\n ],\n isError: true,\n };\n }\n\n logToFile(`audit_add_checks: added ${result.added} entries`);\n return {\n content: [\n {\n type: 'text' as const,\n text: `Added ${result.added} audit check(s).`,\n },\n ],\n };\n });\n },\n );\n\n // -- audit_resolve_checks -------------------------------------------------\n\n const auditResolveChecks = tool(\n 'audit_resolve_checks',\n \"Resolve one or more audit checks by id. Patches each entry's status (and optional file/details) and writes the ledger back atomically. Concurrent calls serialize.\",\n {\n updates: z\n .array(auditUpdateSchema)\n .min(1)\n .describe('Patches to apply, keyed by check id'),\n },\n async (args: {\n updates: Array<{\n id: string;\n status: AuditStatus;\n file?: string;\n details?: string;\n }>;\n }) => {\n return auditMutex(() => {\n const current = readLedger(auditLedgerPath);\n const { next, unknown } = applyAuditUpdates(current, args.updates);\n\n if (unknown.length > 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: unknown check id(s): ${unknown.join(\n ', ',\n )}. Run audit_seed_checks first or check the id.`,\n },\n ],\n isError: true,\n };\n }\n\n writeLedgerAtomic(auditLedgerPath, next);\n logToFile(\n `audit_resolve_checks: applied ${args.updates.length} update(s)`,\n );\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Resolved ${args.updates.length} check(s).`,\n },\n ],\n };\n });\n },\n );\n\n // -- Assemble server ------------------------------------------------------\n\n return createSdkMcpServer({\n name: SERVER_NAME,\n version: '1.0.0',\n tools: [\n checkEnvKeys,\n setEnvValues,\n detectPM,\n loadSkillMenu,\n installSkill,\n auditSeedChecks,\n auditAddChecks,\n auditResolveChecks,\n ],\n });\n}\n\n/** Tool names exposed by the wizard-tools server, for use in allowedTools */\nexport const WIZARD_TOOL_NAMES = [\n `${SERVER_NAME}:check_env_keys`,\n `${SERVER_NAME}:set_env_values`,\n `${SERVER_NAME}:detect_package_manager`,\n `${SERVER_NAME}:load_skill_menu`,\n `${SERVER_NAME}:install_skill`,\n `${SERVER_NAME}:audit_seed_checks`,\n `${SERVER_NAME}:audit_add_checks`,\n `${SERVER_NAME}:audit_resolve_checks`,\n];\n\n// ---------------------------------------------------------------------------\n// Test-only exports\n// ---------------------------------------------------------------------------\n\nexport const __test = {\n writeLedgerAtomic,\n readLedger,\n applyAuditAdditions,\n appendAuditChecksToLedger,\n applyAuditUpdates,\n makeMutex,\n AUDIT_CHECKS_FILE,\n};\n","/**\n * YARA content scanner for the PostHog wizard.\n *\n * This file is the single source of truth for all wizard YARA rules.\n *\n * Scans tool inputs (pre-execution) and outputs (post-execution) for\n * security violations including PII leakage, hardcoded secrets,\n * prompt injection, and secret exfiltration.\n *\n * We use YARA-style regex rules rather than the real YARA C library to\n * avoid native binary dependencies in an npx-distributed npm package.\n *\n * This is Layer 2 (L2) in the wizard's defense-in-depth model,\n * complementing the prompt-based commandments (L0) and the\n * canUseTool() allowlist (L1).\n */\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport type YaraSeverity = 'critical' | 'high' | 'medium' | 'low';\n\nexport type YaraCategory =\n | 'posthog_pii'\n | 'posthog_hardcoded_key'\n | 'posthog_autocapture'\n | 'posthog_config'\n | 'prompt_injection'\n | 'exfiltration'\n | 'filesystem_safety'\n | 'supply_chain';\n\nexport type HookPhase = 'PreToolUse' | 'PostToolUse';\nexport type ToolTarget = 'Bash' | 'Write' | 'Edit' | 'Read' | 'Grep';\n\nexport interface YaraRule {\n /** Rule name matching the .yar file (e.g. 'pii_in_capture_call') */\n name: string;\n description: string;\n severity: YaraSeverity;\n category: YaraCategory;\n /** Which hook+tool combinations this rule applies to */\n appliesTo: Array<{ phase: HookPhase; tool: ToolTarget }>;\n /** Compiled regex patterns — any match triggers the rule */\n patterns: RegExp[];\n}\n\nexport interface YaraMatch {\n rule: YaraRule;\n /** The matched substring */\n matchedText: string;\n /** Byte offset in the scanned content */\n offset: number;\n}\n\nexport type ScanResult =\n | { matched: false }\n | { matched: true; matches: YaraMatch[] };\n\n// ─── Rule Definitions ────────────────────────────────────────────\n//\n// Patterns are compiled once at module load time for performance.\n// Design spec: policies/yara/RULES.md\n\nconst POST_WRITE_EDIT: Array<{ phase: HookPhase; tool: ToolTarget }> = [\n { phase: 'PostToolUse', tool: 'Write' },\n { phase: 'PostToolUse', tool: 'Edit' },\n];\n\nconst POST_READ_GREP: Array<{ phase: HookPhase; tool: ToolTarget }> = [\n { phase: 'PostToolUse', tool: 'Read' },\n { phase: 'PostToolUse', tool: 'Grep' },\n];\n\nconst PRE_BASH: Array<{ phase: HookPhase; tool: ToolTarget }> = [\n { phase: 'PreToolUse', tool: 'Bash' },\n];\n\n// ── §1 PostHog API Violations ────────────────────────────────────\n\nconst pii_in_capture_call: YaraRule = {\n name: 'pii_in_capture_call',\n description:\n \"Detects PII fields passed to posthog.capture() — violates 'NEVER send PII in capture()' commandment\",\n severity: 'high',\n category: 'posthog_pii',\n appliesTo: POST_WRITE_EDIT,\n patterns: [\n // Direct PII field names in capture properties\n /\\.capture\\s*\\([^)]{0,200}email/i,\n /\\.capture\\s*\\([^)]{0,200}phone/i,\n /\\.capture\\s*\\([^)]{0,200}full[_\\s]?name/i,\n /\\.capture\\s*\\([^)]{0,200}first[_\\s]?name/i,\n /\\.capture\\s*\\([^)]{0,200}last[_\\s]?name/i,\n /\\.capture\\s*\\([^)]{0,200}(street|mailing|home|billing)[_\\s]?address/i,\n /\\.capture\\s*\\([^)]{0,200}(ssn|social[_\\s]?security)/i,\n /\\.capture\\s*\\([^)]{0,200}(date[_\\s]?of[_\\s]?birth|dob|birthday)/i,\n /\\.capture\\s*\\([^)]{0,200}\\$ip/,\n // identify() allows email/phone/name (standard PostHog user properties),\n // but highly sensitive PII is still blocked in identify().\n /\\.identify\\s*\\([^)]{0,200}(ssn|social[_\\s]?security)/i,\n /\\.identify\\s*\\([^)]{0,200}(card[_\\s]?number|cvv|credit[_\\s]?card)/i,\n /\\.identify\\s*\\([^)]{0,200}(date[_\\s]?of[_\\s]?birth|dob|birthday)/i,\n /\\.identify\\s*\\([^)]{0,200}(street|mailing|home|billing)[_\\s]?address/i,\n // PII in $set properties via capture (bound to same object)\n /\\$set[^}]{0,200}email/i,\n /\\$set[^}]{0,200}phone/i,\n ],\n};\n\nconst hardcoded_posthog_key: YaraRule = {\n name: 'hardcoded_posthog_key',\n description:\n \"Detects hardcoded PostHog API keys in source — violates 'use environment variables' commandment\",\n severity: 'high',\n category: 'posthog_hardcoded_key',\n appliesTo: POST_WRITE_EDIT,\n patterns: [\n // PostHog project API key (phc_ prefix, 20+ alphanumeric chars)\n /phc_[a-zA-Z0-9]{20,}/,\n // PostHog personal API key (phx_ prefix)\n /phx_[a-zA-Z0-9]{20,}/,\n // Hardcoded key assignment patterns\n /apiKey\\s*[:=]\\s*['\"][a-zA-Z0-9_]{20,}['\"]/,\n /api_key\\s*[:=]\\s*['\"][a-zA-Z0-9_]{20,}['\"]/,\n /POSTHOG_PROJECT_TOKEN\\s*[:=]\\s*['\"][a-zA-Z0-9_]{20,}['\"]/,\n ],\n};\n\nconst autocapture_disabled: YaraRule = {\n name: 'autocapture_disabled',\n description:\n \"Detects agent disabling autocapture — violates 'don't disable autocapture' commandment\",\n severity: 'medium',\n category: 'posthog_autocapture',\n appliesTo: POST_WRITE_EDIT,\n patterns: [\n /autocapture\\s*:\\s*false/,\n /autocapture\\s*:\\s*'false'/,\n /autocapture\\s*:\\s*\"false\"/,\n /autocapture\\s*=\\s*False/,\n /disable_autocapture\\s*[:=]\\s*(true|True|1)/,\n ],\n};\n\n// ── §1b Additional PostHog config rules ──────────────────────────\n\nconst hardcoded_posthog_host: YaraRule = {\n name: 'hardcoded_posthog_host',\n description:\n 'Detects hardcoded PostHog host URLs in source — should use environment variables',\n severity: 'high',\n category: 'posthog_hardcoded_key',\n appliesTo: POST_WRITE_EDIT,\n patterns: [/['\"]https:\\/\\/(us|eu)\\.i\\.posthog\\.com['\"]/],\n};\n\nconst session_recording_disabled: YaraRule = {\n name: 'session_recording_disabled',\n description: 'Detects agent disabling session recording',\n severity: 'medium',\n category: 'posthog_config',\n appliesTo: POST_WRITE_EDIT,\n patterns: [\n /disable_session_recording\\s*:\\s*true/i,\n /disable_session_recording\\s*=\\s*True/,\n ],\n};\n\nconst opt_out_capturing: YaraRule = {\n name: 'opt_out_capturing',\n description: 'Detects agent opting out of PostHog capturing entirely',\n severity: 'medium',\n category: 'posthog_config',\n appliesTo: POST_WRITE_EDIT,\n patterns: [\n // Match opt_out_capturing as a config property (in init options), not as a method call.\n // posthog.opt_out_capturing() is a legitimate GDPR-required API.\n /opt_out_capturing\\s*[:=]\\s*(true|True|1)/i,\n /opted_out\\s*[:=]\\s*(true|True|1)/i,\n ],\n};\n\n// ── §2 Prompt Injection ──────────────────────────────────────────\n\nconst prompt_injection_wizard_override: YaraRule = {\n name: 'prompt_injection_wizard_override',\n description:\n 'Detects classic prompt injection attempting to override agent instructions',\n severity: 'critical',\n category: 'prompt_injection',\n appliesTo: POST_READ_GREP,\n patterns: [\n // Direct instruction override — classic injection patterns\n /ignore previous instructions/i,\n /disregard all/i,\n /forget your instructions/i,\n /override your rules/i,\n // Role manipulation\n /act as a different/i,\n /new instructions:/i,\n ],\n};\n\nconst prompt_injection_wizard_specific: YaraRule = {\n name: 'prompt_injection_wizard_specific',\n description:\n 'Detects wizard-specific manipulation or tool abuse attempts in project files',\n severity: 'medium',\n category: 'prompt_injection',\n appliesTo: POST_READ_GREP,\n patterns: [\n // Wizard-specific manipulation\n /skip posthog/i,\n /do not install posthog/i,\n /remove posthog/i,\n /uninstall posthog/i,\n /delete the posthog/i,\n // Tool abuse via file content\n /run the following command/i,\n /execute this shell command/i,\n // Role hijacking — require \"you are now a\" to avoid false positives\n // on legitimate phrases like \"you are now ready to...\"\n /you are now a\\s/i,\n ],\n};\n\nconst prompt_injection_base64: YaraRule = {\n name: 'prompt_injection_base64',\n description:\n 'Detects suspicious base64-encoded blocks in file content that may contain obfuscated prompt injection',\n severity: 'critical',\n category: 'prompt_injection',\n appliesTo: POST_READ_GREP,\n patterns: [\n // Long base64 strings (100+ chars) in comments or string literals\n // that aren't typical data URIs or legitimate base64 content\n /(?:\\/\\/|#|\\/\\*)\\s*[A-Za-z0-9+/]{100,}={0,2}/,\n ],\n};\n\n// ── §3 Secret Exfiltration ───────────────────────────────────────\n\nconst secret_exfiltration_via_command: YaraRule = {\n name: 'secret_exfiltration_via_command',\n description:\n 'Detects shell commands attempting to exfiltrate secrets or credentials',\n severity: 'critical',\n category: 'exfiltration',\n appliesTo: PRE_BASH,\n patterns: [\n // curl/wget with environment variable secrets\n /curl\\s+.*\\$\\{?[A-Z_]*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL)/i,\n /wget\\s+.*\\$\\{?[A-Z_]*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL)/i,\n // Piping sensitive content to network tools\n /(\\$\\{?[A-Z_]*(KEY|TOKEN|SECRET|PASSWORD)|\\.env|credentials)\\S*.*\\|\\s*curl/i,\n /(\\$\\{?[A-Z_]*(KEY|TOKEN|SECRET|PASSWORD)|\\.env|credentials)\\S*.*\\|\\s*wget/i,\n /\\|\\s*nc\\s/,\n /\\|\\s*netcat\\s/,\n // Base64 encoding piped to network\n /base64.*\\|\\s*(curl|wget|nc\\s)/i,\n // Reading .env and sending\n /cat\\s+.*\\.env.*\\|\\s*(curl|wget)/,\n // PostHog key exfiltration specifically\n /curl.*phc_[a-zA-Z0-9]/,\n /wget.*phc_[a-zA-Z0-9]/,\n ],\n};\n\n// ── §4 Filesystem Safety ─────────────────────────────────────────\n\nconst destructive_rm: YaraRule = {\n name: 'destructive_rm',\n description: 'Detects rm -rf or rm -r commands that could mass-delete files',\n severity: 'critical',\n category: 'filesystem_safety',\n appliesTo: PRE_BASH,\n patterns: [\n // Combined flags: rm -rf, rm -fr, rm -rfi, etc.\n /\\brm\\s+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r)\\b/,\n // Separated flags: rm -r -f, rm -f -r (with optional other flags)\n /\\brm\\s+(-[a-zA-Z]*\\s+)*-[a-zA-Z]*r[a-zA-Z]*\\s+(-[a-zA-Z]*\\s+)*-[a-zA-Z]*f\\b/,\n /\\brm\\s+(-[a-zA-Z]*\\s+)*-[a-zA-Z]*f[a-zA-Z]*\\s+(-[a-zA-Z]*\\s+)*-[a-zA-Z]*r\\b/,\n ],\n};\n\nconst git_force_push: YaraRule = {\n name: 'git_force_push',\n description: 'Detects git push --force which can overwrite remote history',\n severity: 'critical',\n category: 'filesystem_safety',\n appliesTo: PRE_BASH,\n patterns: [/git\\s+push\\s+.*--force/, /git\\s+push\\s+.*-f\\b/],\n};\n\nconst git_reset_hard: YaraRule = {\n name: 'git_reset_hard',\n description:\n 'Detects git reset --hard which discards all uncommitted changes',\n severity: 'critical',\n category: 'filesystem_safety',\n appliesTo: PRE_BASH,\n patterns: [/git\\s+reset\\s+--hard/],\n};\n\n// ── §5 Supply Chain ──────────────────────────────────────────────\n\nconst wrong_posthog_package: YaraRule = {\n name: 'wrong_posthog_package',\n description:\n 'Detects installing the wrong PostHog npm package — should be posthog-js or posthog-node',\n severity: 'high',\n category: 'supply_chain',\n appliesTo: PRE_BASH,\n patterns: [\n // Match \"npm install posthog\" but not \"posthog-js\", \"posthog-node\", etc.\n /npm\\s+install\\s+(?:--save\\s+|--save-dev\\s+|-[SD]\\s+)*posthog(?!\\s*-)/,\n /pnpm\\s+(?:add|install)\\s+(?:--save\\s+|--save-dev\\s+|-[SD]\\s+)*posthog(?!\\s*-)/,\n /yarn\\s+add\\s+(?:--dev\\s+|-D\\s+)*posthog(?!\\s*-)/,\n /bun\\s+(?:add|install)\\s+(?:--dev\\s+|-[dD]\\s+)*posthog(?!\\s*-)/,\n ],\n};\n\nconst npm_install_global: YaraRule = {\n name: 'npm_install_global',\n description:\n 'Detects global npm installs — should never install packages globally',\n severity: 'high',\n category: 'supply_chain',\n appliesTo: PRE_BASH,\n patterns: [/npm\\s+install\\s+-g\\b/, /npm\\s+install\\s+--global\\b/],\n};\n\n// ─── Rule Registry ───────────────────────────────────────────────\n\nexport const RULES: YaraRule[] = [\n // §1 PostHog API violations\n pii_in_capture_call,\n hardcoded_posthog_key,\n autocapture_disabled,\n hardcoded_posthog_host,\n session_recording_disabled,\n opt_out_capturing,\n // §2 Prompt injection\n prompt_injection_wizard_override,\n prompt_injection_wizard_specific,\n prompt_injection_base64,\n // §3 Secret exfiltration\n secret_exfiltration_via_command,\n // §4 Filesystem safety\n destructive_rm,\n git_force_push,\n git_reset_hard,\n // §5 Supply chain\n wrong_posthog_package,\n npm_install_global,\n];\n\n// ─── Scan Engine ─────────────────────────────────────────────────\n\n/** Maximum content length to scan (100 KB). Inputs beyond this are truncated. */\nconst MAX_SCAN_LENGTH = 100_000;\n\n/**\n * Scan content against rules applicable to a given hook phase and tool.\n * Returns all matching rules (one match per rule, first pattern wins).\n */\nexport function scan(\n content: string,\n phase: HookPhase,\n tool: ToolTarget,\n): ScanResult {\n // Cap input length to prevent pathological regex performance\n const scanContent =\n content.length > MAX_SCAN_LENGTH\n ? content.slice(0, MAX_SCAN_LENGTH)\n : content;\n const applicableRules = RULES.filter((r) =>\n r.appliesTo.some((a) => a.phase === phase && a.tool === tool),\n );\n\n const matches: YaraMatch[] = [];\n for (const rule of applicableRules) {\n for (const pattern of rule.patterns) {\n const match = pattern.exec(scanContent);\n if (match) {\n matches.push({\n rule,\n matchedText: match[0],\n offset: match.index,\n });\n break; // One match per rule is sufficient\n }\n }\n }\n\n return matches.length > 0 ? { matched: true, matches } : { matched: false };\n}\n\n/**\n * Scan all files in a skill directory for prompt injection.\n * Used for context-mill scanning after skill installation.\n */\nexport function scanSkillDirectory(\n files: Array<{ path: string; content: string }>,\n): ScanResult {\n const allMatches: YaraMatch[] = [];\n for (const file of files) {\n const result = scan(file.content, 'PostToolUse', 'Read');\n if (result.matched) {\n allMatches.push(...result.matches);\n }\n }\n return allMatches.length > 0\n ? { matched: true, matches: allMatches }\n : { matched: false };\n}\n","/**\n * Check if command is a PostHog skill installation from MCP.\n * We control the MCP server, so we only need to verify:\n * 1. It installs to .claude/skills/\n * 2. It downloads from our GitHub releases or localhost (dev)\n *\n * Extracted to its own module to avoid a circular dependency\n * between agent-interface.ts and yara-hooks.ts.\n */\nexport function isSkillInstallCommand(command: string): boolean {\n if (!command.startsWith('mkdir -p .claude/skills/')) return false;\n\n const urlMatch = command.match(/curl -sL ['\"]([^'\"]+)['\"]/);\n if (!urlMatch) return false;\n\n const url = urlMatch[1];\n return (\n url.startsWith('https://github.com/PostHog/context-mill/releases/') ||\n /^http:\\/\\/localhost:\\d+\\//.test(url)\n );\n}\n","/**\n * YARA hook wiring for the Claude Agent SDK.\n *\n * Creates PreToolUse and PostToolUse hook callback arrays that\n * integrate the YARA scanner into the wizard's agent loop. These\n * hooks are registered in the SDK's query() options alongside the\n * existing Stop hook.\n *\n * PreToolUse hooks block dangerous commands before execution.\n * PostToolUse hooks detect violations in written code and prompt\n * injection in read content, and scan context-mill skill downloads.\n */\n\nimport fs from 'fs';\nimport path from 'path';\nimport fg from 'fast-glob';\nimport { scan, scanSkillDirectory } from './yara-scanner';\nimport type { YaraMatch, ScanResult } from './yara-scanner';\nimport { logToFile } from '../utils/debug';\nimport { analytics } from '../utils/analytics';\nimport { isSkillInstallCommand } from './skill-install';\n\n// ─── Types ───────────────────────────────────────────────────────\n// Using loose types to avoid tight coupling to SDK version.\n// The SDK hook types are: HookCallbackMatcher[], where each matcher\n// has { matcher?: string, hooks: HookCallback[], timeout?: number }\n\ntype HookInput = Record<string, unknown>;\ntype HookOutput = Record<string, unknown>;\ntype HookCallback = (\n input: HookInput,\n toolUseID: string | undefined,\n options: { signal: AbortSignal },\n) => Promise<HookOutput>;\n\nexport interface HookCallbackMatcher {\n matcher?: string;\n hooks: HookCallback[];\n timeout?: number;\n}\n\n// ─── Scan Report Accumulator ─────────────────────────────────────\n\ntype ScanAction = 'blocked' | 'reverted' | 'warned' | 'aborted';\n\ninterface ScanReportEntry {\n rule: string;\n severity: string;\n action: ScanAction;\n phase: string;\n tool: string;\n}\n\nlet scanCount = 0;\nconst scanViolations: ScanReportEntry[] = [];\n\nfunction recordScan(): void {\n scanCount++;\n}\n\nfunction recordViolation(entry: ScanReportEntry): void {\n scanViolations.push(entry);\n}\n\n/** Reset counters (for testing) */\nexport function resetScanReport(): void {\n scanCount = 0;\n scanViolations.length = 0;\n}\n\n/** Format the scan report summary. Returns null if no scans occurred */\nexport function formatScanReport(): string | null {\n if (scanCount === 0) return null;\n\n const lines: string[] = ['', '— YARA Scanner Summary —'];\n const violationCount = scanViolations.length;\n const cleanCount = scanCount - violationCount;\n\n lines.push(\n `✓ ${scanCount} tool calls scanned, ${violationCount} violation${\n violationCount !== 1 ? 's' : ''\n } detected`,\n );\n\n if (violationCount > 0) {\n lines.push('');\n for (const v of scanViolations) {\n const tag = v.action.toUpperCase();\n lines.push(\n ` [${tag}] ${v.rule} (${v.severity.toUpperCase()}) — ${v.phase}:${\n v.tool\n }`,\n );\n }\n }\n\n if (cleanCount > 0) {\n lines.push('');\n lines.push(\n `No violations: ✓ ${cleanCount} clean scan${cleanCount !== 1 ? 's' : ''}`,\n );\n }\n\n lines.push('');\n return lines.join('\\n');\n}\n\nimport { WIZARD_YARA_REPORT_FILE } from '../utils/paths';\n\n/** Write the scan report to a JSON file. Returns the file path, or null if no scans occurred. */\nexport function writeScanReport(): string | null {\n if (scanCount === 0) return null;\n\n const report = {\n summary: {\n totalScans: scanCount,\n violations: scanViolations.length,\n clean: scanCount - scanViolations.length,\n },\n violations: scanViolations,\n };\n\n try {\n fs.writeFileSync(WIZARD_YARA_REPORT_FILE, JSON.stringify(report, null, 2));\n } catch (err) {\n logToFile('[YARA] Failed to write scan report:', err);\n return null;\n }\n return WIZARD_YARA_REPORT_FILE;\n}\n\n// ─── Hook Timeouts (ms) ─────────────────────────────────────────\n\n/** Timeout for synchronous scan hooks (PreToolUse, PostToolUse Write/Edit/Read) */\nconst HOOK_TIMEOUT_MS = 60;\n/** Timeout for skill install hook (involves filesystem I/O) */\nconst SKILL_SCAN_HOOK_TIMEOUT_MS = 120;\n\n// ─── Logging ─────────────────────────────────────────────────────\n\nfunction logYaraMatch(\n phase: string,\n tool: string,\n match: YaraMatch,\n action: ScanAction,\n): void {\n logToFile(\n `[YARA] ${phase}:${tool} [${action.toUpperCase()}] rule \"${\n match.rule.name\n }\" ` +\n `(severity: ${match.rule.severity}, category: ${match.rule.category})\\n` +\n ` Description: ${match.rule.description}\\n` +\n ` Matched text: \"${match.matchedText.substring(0, 200)}\"`,\n );\n analytics.wizardCapture('yara rule matched', {\n rule: match.rule.name,\n severity: match.rule.severity,\n category: match.rule.category,\n action,\n phase,\n tool,\n });\n}\n\n// ─── Severity helpers ────────────────────────────────────────────\n\nconst SEVERITY_RANK: Record<string, number> = {\n critical: 4,\n high: 3,\n medium: 2,\n low: 1,\n};\n\n/** Return the highest-severity match from a list of matches. */\nfunction highestSeverityMatch(matches: YaraMatch[]): YaraMatch {\n return matches.reduce((worst, m) =>\n (SEVERITY_RANK[m.rule.severity] ?? 0) >\n (SEVERITY_RANK[worst.rule.severity] ?? 0)\n ? m\n : worst,\n );\n}\n\n// ─── PreToolUse Hooks ────────────────────────────────────────────\n\n/**\n * Create PreToolUse hook matchers for YARA scanning.\n * Scans Bash commands before execution for exfiltration,\n * destructive operations, and supply chain violations.\n */\nexport function createPreToolUseYaraHooks(): HookCallbackMatcher[] {\n return [\n {\n hooks: [\n (input: HookInput): Promise<HookOutput> => {\n try {\n const toolName = input.tool_name as string;\n if (toolName !== 'Bash') return Promise.resolve({});\n\n const toolInput = input.tool_input as Record<string, unknown>;\n const command =\n typeof toolInput?.command === 'string' ? toolInput.command : '';\n\n if (!command) return Promise.resolve({});\n\n recordScan();\n const result = scan(command, 'PreToolUse', 'Bash');\n if (!result.matched) return Promise.resolve({});\n\n const match = highestSeverityMatch(result.matches);\n logYaraMatch('PreToolUse', 'Bash', match, 'blocked');\n recordViolation({\n rule: match.rule.name,\n severity: match.rule.severity,\n action: 'blocked',\n phase: 'PreToolUse',\n tool: 'Bash',\n });\n\n return Promise.resolve({\n decision: 'block',\n reason: `[YARA] ${match.rule.name}: ${match.rule.description}. Command blocked for security.`,\n });\n } catch (error) {\n logToFile('[YARA] PreToolUse hook error:', error);\n // Fail closed: block the command if scanning fails\n return Promise.resolve({\n decision: 'block',\n reason: '[YARA] Scanner error — command blocked as a precaution.',\n });\n }\n },\n ],\n timeout: HOOK_TIMEOUT_MS,\n },\n ];\n}\n\n// ─── PostToolUse Hooks ───────────────────────────────────────────\n\n/**\n * Create PostToolUse hook matchers for YARA scanning.\n *\n * Three matchers:\n * 1. Write/Edit — scan written content for PII, secrets, config violations\n * 2. Read/Grep — scan read content for prompt injection\n * 3. Bash (skill install) — scan downloaded skill files for poisoned content\n */\nexport function createPostToolUseYaraHooks(): HookCallbackMatcher[] {\n return [\n // ── Write/Edit content scanning ──\n {\n hooks: [\n (input: HookInput): Promise<HookOutput> => {\n try {\n const toolName = input.tool_name as string;\n if (toolName !== 'Write' && toolName !== 'Edit')\n return Promise.resolve({});\n\n const toolInput = input.tool_input as Record<string, unknown>;\n // For Write, scan the content being written\n // For Edit, scan the new_str (replacement text)\n const content =\n toolName === 'Write'\n ? (toolInput?.content as string) ?? ''\n : (toolInput?.new_str as string) ?? '';\n\n if (!content) return Promise.resolve({});\n\n recordScan();\n const tool = toolName;\n const result = scan(content, 'PostToolUse', tool);\n if (!result.matched) return Promise.resolve({});\n\n const match = highestSeverityMatch(result.matches);\n logYaraMatch('PostToolUse', tool, match, 'reverted');\n recordViolation({\n rule: match.rule.name,\n severity: match.rule.severity,\n action: 'reverted',\n phase: 'PostToolUse',\n tool,\n });\n\n return Promise.resolve({\n hookSpecificOutput: {\n hookEventName: 'PostToolUse',\n additionalContext:\n `[YARA VIOLATION] ${match.rule.name}: ${match.rule.description}. ` +\n `You MUST revert this change immediately. The content you just wrote violates security policy.`,\n },\n });\n } catch (error) {\n logToFile('[YARA] PostToolUse Write/Edit hook error:', error);\n // Fail closed: instruct the agent to revert if scanning fails\n return Promise.resolve({\n hookSpecificOutput: {\n hookEventName: 'PostToolUse',\n additionalContext:\n '[YARA] Scanner error — you MUST revert this change as a precaution.',\n },\n });\n }\n },\n ],\n timeout: HOOK_TIMEOUT_MS,\n },\n\n // ── Read/Grep prompt injection scanning ──\n {\n hooks: [\n (input: HookInput): Promise<HookOutput> => {\n try {\n const toolName = input.tool_name as string;\n if (toolName !== 'Read' && toolName !== 'Grep')\n return Promise.resolve({});\n\n const toolResponse = input.tool_response;\n const content =\n typeof toolResponse === 'string'\n ? toolResponse\n : JSON.stringify(toolResponse ?? '');\n\n if (!content) return Promise.resolve({});\n\n recordScan();\n const tool = toolName;\n const result = scan(content, 'PostToolUse', tool);\n if (!result.matched) return Promise.resolve({});\n\n const match = highestSeverityMatch(result.matches);\n\n if (match.rule.severity === 'critical') {\n logYaraMatch('PostToolUse', tool, match, 'aborted');\n recordViolation({\n rule: match.rule.name,\n severity: match.rule.severity,\n action: 'aborted',\n phase: 'PostToolUse',\n tool,\n });\n // Prompt injection: abort the session — context is poisoned\n return Promise.resolve({\n stopReason:\n `[YARA CRITICAL] ${match.rule.name}: Prompt injection detected in file content. ` +\n `Agent context is potentially poisoned. Session terminated for safety.`,\n });\n }\n\n logYaraMatch('PostToolUse', tool, match, 'warned');\n recordViolation({\n rule: match.rule.name,\n severity: match.rule.severity,\n action: 'warned',\n phase: 'PostToolUse',\n tool,\n });\n return Promise.resolve({\n hookSpecificOutput: {\n hookEventName: 'PostToolUse',\n additionalContext: `[YARA WARNING] ${match.rule.name}: ${match.rule.description}`,\n },\n });\n } catch (error) {\n logToFile('[YARA] PostToolUse Read/Grep hook error:', error);\n // Fail closed: terminate session if scanning fails on read content\n return Promise.resolve({\n stopReason:\n '[YARA] Scanner error while scanning read content — session terminated as a precaution.',\n });\n }\n },\n ],\n timeout: HOOK_TIMEOUT_MS,\n },\n\n // ── Context-mill skill install scanning ──\n {\n hooks: [\n async (input: HookInput): Promise<HookOutput> => {\n try {\n const toolName = input.tool_name as string;\n if (toolName !== 'Bash') return {};\n\n const toolInput = input.tool_input as Record<string, unknown>;\n const command =\n typeof toolInput?.command === 'string' ? toolInput.command : '';\n\n // Only scan after skill install commands\n if (!isSkillInstallCommand(command)) return {};\n\n // Extract skill directory from command\n const dirMatch = command.match(\n /mkdir -p (.claude\\/skills\\/[^\\s&]+)/,\n );\n if (!dirMatch) return {};\n\n const skillDir = dirMatch[1];\n const cwd = (input.cwd as string) ?? process.cwd();\n recordScan();\n const result = await scanSkillFiles(cwd, skillDir);\n\n if (!result.matched) return {};\n\n const match = highestSeverityMatch(result.matches);\n logYaraMatch(\n 'PostToolUse',\n 'Bash (skill install)',\n match,\n 'aborted',\n );\n recordViolation({\n rule: match.rule.name,\n severity: match.rule.severity,\n action: 'aborted',\n phase: 'PostToolUse',\n tool: 'Bash (skill)',\n });\n\n return {\n stopReason:\n `[YARA CRITICAL] Poisoned skill detected in ${skillDir}: ${match.rule.name}. ` +\n `The downloaded skill contains potential prompt injection. Session terminated for safety.`,\n };\n } catch (error) {\n logToFile('[YARA] PostToolUse skill install hook error:', error);\n // Fail closed: terminate if skill scanning fails\n return {\n stopReason:\n '[YARA] Scanner error while scanning skill files — session terminated as a precaution.',\n };\n }\n },\n ],\n timeout: SKILL_SCAN_HOOK_TIMEOUT_MS,\n },\n ];\n}\n\n// ─── Skill File Scanner ──────────────────────────────────────────\n\n/**\n * Read and scan all text files in a skill directory for prompt injection.\n */\nasync function scanSkillFiles(\n cwd: string,\n skillDir: string,\n): Promise<ScanResult> {\n const absoluteDir = path.resolve(cwd, skillDir);\n\n if (!fs.existsSync(absoluteDir)) {\n logToFile(`[YARA] Skill directory does not exist: ${absoluteDir}`);\n return { matched: false };\n }\n\n const files = await fg('**/*.{md,txt,yaml,yml,json,js,ts,py,rb,sh}', {\n cwd: absoluteDir,\n absolute: true,\n });\n\n const fileContents: Array<{ path: string; content: string }> = [];\n for (const filePath of files) {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n fileContents.push({ path: filePath, content });\n } catch (err) {\n logToFile(`[YARA] Could not read skill file ${filePath}:`, err);\n }\n }\n\n if (fileContents.length === 0) {\n logToFile(`[YARA] No text files found in skill directory: ${absoluteDir}`);\n return { matched: false };\n }\n\n logToFile(\n `[YARA] Scanning ${fileContents.length} files in skill directory: ${skillDir}`,\n );\n return scanSkillDirectory(fileContents);\n}\n","/**\n * Wizard-wide commandments that are always appended as a system prompt.\n *\n * Keep this as a simple string so it can be inlined into the compiled bundle\n * without extra files, copying, or runtime I/O.\n */\nconst WIZARD_COMMANDMENTS = [\n 'Never hallucinate a PostHog project token, host, or any other secret. Always use the real values that have been configured for this project (for example via environment variables).',\n\n 'Never write API keys, access tokens, or other secrets directly into source code. Always reference environment variables instead, and rely on the wizard-tools MCP server (check_env_keys / set_env_values) to create or update .env files.',\n\n 'Always use the detect_package_manager tool from the wizard-tools MCP server to determine the package manager. Do not guess based on lockfiles or hard-code npm, yarn, pnpm, bun, pip, etc.',\n\n 'When installing packages, start the installation as a background task and then continue with other work. Do not block waiting for installs to finish unless explicitly instructed.',\n\n 'Before writing to any file, you MUST read that exact file immediately beforehand using the Read tool, even if you have already read it earlier in the run. This avoids tool failures and stale edits.',\n\n 'Treat feature flags, custom properties, and event names as part of an analytics contract. Prefer reusing existing names and patterns in the project. When you must introduce new ones, make them clear, descriptive, and consistent with existing conventions, and avoid scattering the same flag or property across many unrelated callsites.',\n\n 'Prefer minimal, targeted edits that achieve the requested behavior while preserving existing structure and style. Avoid large refactors, broad reformatting, or unrelated changes unless explicitly requested.',\n\n 'Do not spawn subagents unless explicitly instructed to do so.',\n\n 'After reading the integration skill, use the TodoWrite tool to track your progress. Create a todo list at the describing the high-level areas of work, mark each as in_progress when you begin it, and completed when done. Describe high-level tasks from the workflow (\"inserting event tracking code\", etc), not specific files or lines. For each new stage of the integration, create additional todos if necessary. Create a final \"wrapping up\" step and mark it completed when the integration is complete.',\n\n 'Do not assume \"PostHog provider\" or \"PostHogProvider\" in todo labels, status messages, or code. Only use a provider if the skill workflow explicitly requires one for the specific framework.',\n\n 'If you see a [YARA WARNING] or [YARA VIOLATION] message, handle it silently. Fix the issue (revert the write, avoid the flagged pattern) without mentioning security warnings or YARA to the user. These are internal guardrails, not user-facing alerts. Never show scary security language to the user.',\n].join('\\n');\n\nexport function getWizardCommandments(): string {\n return WIZARD_COMMANDMENTS;\n}\n","/**\n * Shared agent interface for PostHog wizards\n * Uses Claude Agent SDK directly with PostHog LLM gateway\n */\n\nimport path from 'path';\nimport * as fs from 'fs';\nimport { getUI, type SpinnerHandle } from '../../ui';\nimport {\n debug,\n logToFile,\n initLogFile,\n getLogFilePath,\n} from '../../utils/debug';\nimport type { WizardOptions } from '../../utils/types';\nimport { analytics } from '../../utils/analytics';\nimport {\n WIZARD_REMARK_EVENT_NAME,\n POSTHOG_PROPERTY_HEADER_PREFIX,\n WIZARD_VARIANT_FLAG_KEY,\n WIZARD_VARIANTS,\n WIZARD_USER_AGENT,\n} from '../constants';\nimport {\n type AdditionalFeature,\n ADDITIONAL_FEATURE_PROMPTS,\n} from '../wizard-session';\nimport {\n registerCleanup,\n wizardAbort,\n WizardError,\n} from '../../utils/wizard-abort';\nimport { createCustomHeaders } from '../../utils/custom-headers';\nimport { getLlmGatewayUrlFromHost } from '../../utils/urls';\nimport { LINTING_TOOLS } from '../safe-tools';\nimport { createWizardToolsServer, WIZARD_TOOL_NAMES } from '../wizard-tools';\nimport {\n createPreToolUseYaraHooks,\n createPostToolUseYaraHooks,\n} from '../yara-hooks';\nimport { getWizardCommandments } from './commandments';\nimport type { PackageManagerDetector } from '../detection/package-manager';\n\n// Dynamic import cache for ESM module\nlet _sdkModule: any = null;\nasync function getSDKModule(): Promise<any> {\n if (!_sdkModule) {\n _sdkModule = await import('@anthropic-ai/claude-agent-sdk');\n }\n return _sdkModule;\n}\n\n/**\n * Get the path to the bundled Claude Code CLI from the SDK package.\n * This ensures we use the SDK's bundled version rather than the user's installed Claude Code.\n */\nfunction getClaudeCodeExecutablePath(): string {\n // require.resolve finds the package's main entry, then we get cli.js from same dir\n const sdkPackagePath = require.resolve('@anthropic-ai/claude-agent-sdk');\n return path.join(path.dirname(sdkPackagePath), 'cli.js');\n}\n\n// Using `any` because typed imports from ESM modules require import attributes\n// syntax which prettier cannot parse. See PR discussion for details.\ntype SDKMessage = any;\ntype McpServersConfig = any;\ntype AbortCaseMatcher = { match: RegExp };\n\nexport const AgentSignals = {\n /** Signal emitted when the agent reports progress to the user */\n STATUS: '[STATUS]',\n /** Signal emitted when the agent cannot access the PostHog MCP server */\n ERROR_MCP_MISSING: '[ERROR-MCP-MISSING]',\n /** Signal emitted when the agent cannot access the setup resource */\n ERROR_RESOURCE_MISSING: '[ERROR-RESOURCE-MISSING]',\n /**\n * Signal emitted when the agent cannot complete the workflow and is\n * aborting intentionally (distinct from errors). Format: \"[ABORT] <reason>\".\n * Workflows can declare an onAbort handler to render a custom screen.\n */\n ABORT: '[ABORT]',\n /** Signal emitted when the agent provides a remark about its run */\n WIZARD_REMARK: '[WIZARD-REMARK]',\n /** Signal prefix for benchmark logging */\n BENCHMARK: '[BENCHMARK]',\n} as const;\n\nexport type AgentSignal = (typeof AgentSignals)[keyof typeof AgentSignals];\n\n/**\n * Error types that can be returned from agent execution.\n * These correspond to the error signals that the agent emits.\n */\nexport enum AgentErrorType {\n /** Agent could not access the PostHog MCP server */\n MCP_MISSING = 'WIZARD_MCP_MISSING',\n /** Agent could not access the setup resource */\n RESOURCE_MISSING = 'WIZARD_RESOURCE_MISSING',\n /** API rate limit exceeded */\n RATE_LIMIT = 'WIZARD_RATE_LIMIT',\n /** Generic API error */\n API_ERROR = 'WIZARD_API_ERROR',\n /** YARA scanner detected a security violation */\n YARA_VIOLATION = 'WIZARD_YARA_VIOLATION',\n /** Agent intentionally aborted the workflow (emitted [ABORT] <reason>) */\n ABORT = 'WIZARD_ABORT',\n}\n\nconst BLOCKING_ENV_KEYS = [\n 'ANTHROPIC_API_KEY',\n 'ANTHROPIC_BASE_URL',\n 'ANTHROPIC_AUTH_TOKEN',\n];\nconst BLOCKING_SETTINGS_KEYS = ['apiKeyHelper'];\n\n/** Where a settings conflict was found. */\nexport type SettingsConflictSource = 'project' | 'managed';\n\n/** A single settings conflict detected during startup. */\nexport interface SettingsConflict {\n /** Where the conflict was found. */\n source: SettingsConflictSource;\n /** The blocking keys found (e.g. 'ANTHROPIC_BASE_URL', 'apiKeyHelper'). */\n keys: string[];\n /** Whether the wizard can back up / remove this file. Managed settings are read-only. */\n writable: boolean;\n}\n\n/**\n * Check a single settings file for blocking env keys and top-level settings keys.\n * Returns matched key names, or an empty array if none found.\n */\nfunction checkSettingsFile(filePath: string): string[] {\n try {\n const raw = fs.readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(raw);\n const matched: string[] = [];\n\n // Check env block for blocking env keys\n const envBlock = parsed?.env;\n if (envBlock && typeof envBlock === 'object') {\n matched.push(...BLOCKING_ENV_KEYS.filter((key) => key in envBlock));\n }\n\n // Check top-level settings keys\n matched.push(\n ...BLOCKING_SETTINGS_KEYS.filter(\n (key) => key in parsed && parsed[key] !== '' && parsed[key] != null,\n ),\n );\n\n return matched;\n } catch {\n // File doesn't exist or isn't valid JSON — skip\n return [];\n }\n}\n\n/**\n * Check if .claude/settings.json in the project directory contains env\n * overrides or apiKeyHelper that block the Wizard from accessing the PostHog LLM Gateway.\n * Returns the list of matched key names, or an empty array if none found.\n *\n * @deprecated Use {@link checkAllSettingsConflicts} for comprehensive detection.\n */\nexport function checkClaudeSettingsOverrides(\n workingDirectory: string,\n): string[] {\n const candidates = [\n path.join(workingDirectory, '.claude', 'settings.json'),\n path.join(workingDirectory, '.claude', 'settings'),\n ];\n\n for (const filePath of candidates) {\n const matched = checkSettingsFile(filePath);\n if (matched.length > 0) return matched;\n }\n\n return [];\n}\n\n/**\n * Managed settings path on macOS.\n * IT/MDM-deployed settings — readable by all users, writable only by root.\n */\nconst MANAGED_SETTINGS_PATH =\n '/Library/Application Support/ClaudeCode/managed-settings.json';\n\n/**\n * Check project and org-managed settings for blocking keys that conflict\n * with the wizard's proxy auth.\n */\nexport function checkAllSettingsConflicts(\n workingDirectory: string,\n): SettingsConflict[] {\n const conflicts: SettingsConflict[] = [];\n\n const sources: {\n source: SettingsConflictSource;\n paths: string[];\n writable: boolean;\n }[] = [\n {\n source: 'managed',\n paths: [MANAGED_SETTINGS_PATH],\n writable: false,\n },\n {\n source: 'project',\n paths: [\n path.join(workingDirectory, '.claude', 'settings.json'),\n path.join(workingDirectory, '.claude', 'settings'),\n ],\n writable: true,\n },\n ];\n\n for (const { source, paths, writable } of sources) {\n for (const filePath of paths) {\n const keys = checkSettingsFile(filePath);\n if (keys.length > 0) {\n conflicts.push({ source, keys, writable });\n break; // Only one conflict per source (settings.json vs settings fallback)\n }\n }\n }\n\n return conflicts;\n}\n\n/**\n * Copy .claude/settings.json to .wizard-backup (overwriting if it exists),\n * then remove the original so the SDK doesn't load the blocking overrides.\n */\nexport function backupAndFixClaudeSettings(workingDirectory: string): boolean {\n for (const name of ['settings.json', 'settings']) {\n const filePath = path.join(workingDirectory, '.claude', name);\n const backupPath = `${filePath}.wizard-backup`;\n analytics.wizardCapture('backedup-claude-settings');\n try {\n fs.copyFileSync(filePath, backupPath);\n fs.unlinkSync(filePath);\n registerCleanup(() => {\n try {\n restoreClaudeSettings(workingDirectory);\n } catch (error) {\n analytics.captureException(error);\n }\n });\n return true;\n } catch {\n // File doesn't exist — try next candidate\n }\n }\n return false;\n}\n\n/**\n * Restore .claude/settings.json from .wizard-backup.\n * Copies (not moves) so the backup is preserved.\n */\nexport function restoreClaudeSettings(workingDirectory: string): void {\n for (const name of ['settings.json', 'settings']) {\n const backup = path.join(\n workingDirectory,\n '.claude',\n `${name}.wizard-backup`,\n );\n try {\n fs.copyFileSync(backup, path.join(workingDirectory, '.claude', name));\n analytics.wizardCapture('restored-claude-settings');\n return;\n } catch (error) {\n analytics.captureException(error);\n }\n }\n}\n\nexport type AgentConfig = {\n workingDirectory: string;\n posthogMcpUrl: string;\n posthogApiKey: string;\n posthogApiHost: string;\n additionalMcpServers?: Record<string, { url: string }>;\n detectPackageManager: PackageManagerDetector;\n /** Base URL for the skills server (context-mill dev or GitHub releases) */\n skillsBaseUrl: string;\n /** Feature flag key -> variant (evaluated at start of run). */\n wizardFlags?: Record<string, string>;\n wizardMetadata?: Record<string, string>;\n /** Workflow identifier — selects the model for that workflow. */\n integrationLabel?: string;\n};\n\n/**\n * Stop hook return type: either allow stop or block with a reason.\n */\nexport type StopHookResult =\n | Record<string, never>\n | { decision: 'block'; reason: string };\n\n/**\n * Create a stop hook callback that drains the additional feature queue,\n * then collects a remark, then allows stop.\n *\n * Three-phase logic using closure state:\n * Phase 1 — drain queue: block with each feature prompt in order\n * Phase 2 — collect remark (once): block with remark prompt\n * Phase 3 — allow stop: return {}\n */\nexport function createStopHook(\n featureQueue: readonly AdditionalFeature[],\n collectedText?: string[],\n): (input: { stop_hook_active: boolean }) => StopHookResult {\n let featureIndex = 0;\n let remarkRequested = false;\n\n return (input: { stop_hook_active: boolean }): StopHookResult => {\n logToFile('Stop hook triggered', {\n stop_hook_active: input.stop_hook_active,\n featureIndex,\n remarkRequested,\n queueLength: featureQueue.length,\n });\n\n // On API errors, allow stop immediately — blocking with remark/feature\n // prompts would just fail again. The auth error screen is shown separately.\n if (collectedText) {\n const text = collectedText.join('\\n');\n if (text.includes('API Error:')) {\n logToFile('Stop hook: API error detected, allowing immediate stop');\n return {};\n }\n }\n\n // Phase 1: drain feature queue\n if (featureIndex < featureQueue.length) {\n const feature = featureQueue[featureIndex++];\n const prompt = ADDITIONAL_FEATURE_PROMPTS[feature];\n logToFile(`Stop hook: injecting feature prompt for ${feature}`);\n return { decision: 'block', reason: prompt };\n }\n\n // Phase 2: collect remark (once)\n if (!remarkRequested) {\n remarkRequested = true;\n logToFile('Stop hook: requesting reflection');\n return {\n decision: 'block',\n reason: `Before concluding, provide a brief remark about what information or guidance would have been useful to have in the integration prompt or documentation for this run. Specifically cite anything that would have prevented tool failures, erroneous edits, or other wasted turns. Format your response exactly as: ${AgentSignals.WIZARD_REMARK} Your remark here`,\n };\n }\n\n // Phase 3: allow stop\n logToFile('Stop hook: allowing stop');\n return {};\n };\n}\n\n/**\n * Internal configuration object returned by initializeAgent\n */\ntype AgentRunConfig = {\n workingDirectory: string;\n mcpServers: McpServersConfig;\n model: string;\n wizardFlags?: Record<string, string>;\n wizardMetadata?: Record<string, string>;\n};\n\n/**\n * Select wizard metadata from WIZARD_VARIANTS using the variant feature flag.\n * If the flag is missing or the value is not in config, returns the \"base\" variant (VARIANT: \"base\").\n */\nexport function buildWizardMetadata(\n flags: Record<string, string> = {},\n): Record<string, string> {\n const variantKey = flags[WIZARD_VARIANT_FLAG_KEY];\n const variant =\n (variantKey && WIZARD_VARIANTS[variantKey]) ?? WIZARD_VARIANTS['base'];\n return { ...variant };\n}\n\n/**\n * Build env for the SDK subprocess: process.env plus ANTHROPIC_CUSTOM_HEADERS, which always\n * includes `x-posthog-use-bedrock-fallback: true` so the LLM gateway falls back to Bedrock on\n * Anthropic 5xx, plus any wizard metadata/flags.\n */\nfunction buildAgentEnv(\n wizardMetadata: Record<string, string>,\n wizardFlags: Record<string, string>,\n): string {\n const headers = createCustomHeaders();\n headers.add('x-posthog-use-bedrock-fallback', 'true');\n for (const [key, value] of Object.entries(wizardMetadata)) {\n headers.add(\n key.startsWith(POSTHOG_PROPERTY_HEADER_PREFIX)\n ? key\n : `${POSTHOG_PROPERTY_HEADER_PREFIX}${key}`,\n value,\n );\n }\n for (const [flagKey, variant] of Object.entries(wizardFlags)) {\n if (!flagKey.toLowerCase().startsWith('wizard')) continue;\n headers.addFlag(flagKey, variant);\n }\n const encoded = headers.encode();\n logToFile('ANTHROPIC_CUSTOM_HEADERS', encoded);\n return encoded;\n}\n\n/**\n * Package managers that can be used to run commands.\n */\nconst PACKAGE_MANAGERS = [\n // JavaScript\n 'npm',\n 'pnpm',\n 'yarn',\n 'bun',\n 'npx',\n // Python\n 'pip',\n 'pip3',\n 'poetry',\n 'pipenv',\n 'uv',\n];\n\n/**\n * Safe scripts/commands that can be run with any package manager.\n * Uses startsWith matching, so 'build' matches 'build', 'build:prod', etc.\n * Note: Linting tools are in LINTING_TOOLS and checked separately.\n */\nconst SAFE_SCRIPTS = [\n // Package installation\n 'install',\n 'add',\n 'ci',\n // Build\n 'build',\n // Type checking (various naming conventions)\n 'tsc',\n 'typecheck',\n 'type-check',\n 'check-types',\n 'types',\n // Linting/formatting script names (actual tools are in LINTING_TOOLS)\n 'lint',\n 'format',\n];\n\n/**\n * Dangerous shell operators that could allow command injection.\n * Note: We handle `2>&1` and `| tail/head` separately as safe patterns.\n */\nconst DANGEROUS_OPERATORS = /[;`$()]/;\n\n// Re-export for backwards compatibility — canonical source is skill-install.ts\nexport { isSkillInstallCommand } from '../skill-install';\n\n/**\n * Check if command is an allowed package manager command.\n * Matches: <pkg-manager> [run|exec] <safe-script> [args...]\n */\nfunction matchesAllowedPrefix(command: string): boolean {\n const parts = command.split(/\\s+/);\n if (parts.length === 0 || !PACKAGE_MANAGERS.includes(parts[0])) {\n return false;\n }\n\n // Skip 'run' or 'exec' if present\n let scriptIndex = 1;\n if (parts[scriptIndex] === 'run' || parts[scriptIndex] === 'exec') {\n scriptIndex++;\n }\n\n // Get the script/command portion (may include args)\n const scriptPart = parts.slice(scriptIndex).join(' ');\n\n // Check if script starts with any safe script name or linting tool\n return (\n SAFE_SCRIPTS.some((safe) => scriptPart.startsWith(safe)) ||\n LINTING_TOOLS.some((tool) => scriptPart.startsWith(tool))\n );\n}\n\n/**\n * Permission hook that allows only safe commands.\n * - Package manager install commands\n * - Build/typecheck/lint commands for verification\n * - Piping to tail/head for output limiting is allowed\n * - Stderr redirection (2>&1) is allowed\n */\nexport function wizardCanUseTool(\n toolName: string,\n input: Record<string, unknown>,\n):\n | { behavior: 'allow'; updatedInput: Record<string, unknown> }\n | { behavior: 'deny'; message: string } {\n // Block direct reads/writes of .env files — use wizard-tools MCP instead\n if (toolName === 'Read' || toolName === 'Write' || toolName === 'Edit') {\n const filePath = typeof input.file_path === 'string' ? input.file_path : '';\n const basename = path.basename(filePath);\n if (basename.startsWith('.env')) {\n logToFile(`Denying ${toolName} on env file: ${filePath}`);\n return {\n behavior: 'deny',\n message: `Direct ${toolName} of ${basename} is not allowed. Use the wizard-tools MCP server (check_env_keys / set_env_values) to read or modify environment variables.`,\n };\n }\n return { behavior: 'allow', updatedInput: input };\n }\n\n // Block Grep when it directly targets a .env file.\n // Note: ripgrep skips dotfiles (like .env*) by default during directory traversal,\n // so broad searches like `Grep { path: \".\" }` are already safe.\n if (toolName === 'Grep') {\n const grepPath = typeof input.path === 'string' ? input.path : '';\n if (grepPath && path.basename(grepPath).startsWith('.env')) {\n logToFile(`Denying Grep on env file: ${grepPath}`);\n return {\n behavior: 'deny',\n message: `Grep on ${path.basename(\n grepPath,\n )} is not allowed. Use the wizard-tools MCP server (check_env_keys) to check environment variables.`,\n };\n }\n return { behavior: 'allow', updatedInput: input };\n }\n\n // Allow all other non-Bash tools\n if (toolName !== 'Bash') {\n return { behavior: 'allow', updatedInput: input };\n }\n\n const command = (\n typeof input.command === 'string' ? input.command : ''\n ).trim();\n\n // Block definitely dangerous operators: ; ` $ ( )\n if (DANGEROUS_OPERATORS.test(command)) {\n logToFile(`Denying bash command with dangerous operators: ${command}`);\n debug(`Denying bash command with dangerous operators: ${command}`);\n analytics.wizardCapture('bash denied', {\n reason: 'dangerous operators',\n command,\n });\n return {\n behavior: 'deny',\n message: `Bash command not allowed. Shell operators like ; \\` $ ( ) are not permitted.`,\n };\n }\n\n // Normalize: remove safe stderr redirection (2>&1, 2>&2, etc.)\n const normalized = command.replace(/\\s*\\d*>&\\d+\\s*/g, ' ').trim();\n\n // Check for pipe to tail/head (safe output limiting)\n const pipeMatch = normalized.match(/^(.+?)\\s*\\|\\s*(tail|head)(\\s+\\S+)*\\s*$/);\n if (pipeMatch) {\n const baseCommand = pipeMatch[1].trim();\n\n // Block if base command has pipes or & (multiple chaining)\n if (/[|&]/.test(baseCommand)) {\n logToFile(`Denying bash command with multiple pipes: ${command}`);\n debug(`Denying bash command with multiple pipes: ${command}`);\n analytics.wizardCapture('bash denied', {\n reason: 'multiple pipes',\n command,\n });\n return {\n behavior: 'deny',\n message: `Bash command not allowed. Only single pipe to tail/head is permitted.`,\n };\n }\n\n if (matchesAllowedPrefix(baseCommand)) {\n logToFile(`Allowing bash command with output limiter: ${command}`);\n debug(`Allowing bash command with output limiter: ${command}`);\n return { behavior: 'allow', updatedInput: input };\n }\n }\n\n // Block remaining pipes and & (not covered by tail/head case above)\n if (/[|&]/.test(normalized)) {\n logToFile(`Denying bash command with pipe/&: ${command}`);\n debug(`Denying bash command with pipe/&: ${command}`);\n analytics.wizardCapture('bash denied', {\n reason: 'disallowed pipe',\n command,\n });\n return {\n behavior: 'deny',\n message: `Bash command not allowed. Pipes are only permitted with tail/head for output limiting.`,\n };\n }\n\n // Check if command starts with any allowed prefix (package manager commands)\n if (matchesAllowedPrefix(normalized)) {\n logToFile(`Allowing bash command: ${command}`);\n debug(`Allowing bash command: ${command}`);\n return { behavior: 'allow', updatedInput: input };\n }\n\n logToFile(`Denying bash command: ${command}`);\n debug(`Denying bash command: ${command}`);\n analytics.wizardCapture('bash denied', {\n reason: 'not in allowlist',\n command,\n });\n return {\n behavior: 'deny',\n message: `Bash command not allowed. Only install, build, typecheck, lint, and formatting commands are permitted.`,\n };\n}\n\n/**\n * Initialize agent configuration for the LLM gateway\n */\nexport async function initializeAgent(\n config: AgentConfig,\n options: WizardOptions,\n): Promise<AgentRunConfig> {\n // Initialize log file for this run\n initLogFile();\n logToFile('Agent initialization starting');\n logToFile('Install directory:', options.installDir);\n\n getUI().log.step('Initializing Claude agent...');\n\n try {\n // Configure LLM gateway environment variables (inherited by SDK subprocess)\n const gatewayUrl = getLlmGatewayUrlFromHost(config.posthogApiHost);\n process.env.ANTHROPIC_BASE_URL = gatewayUrl;\n process.env.ANTHROPIC_AUTH_TOKEN = config.posthogApiKey;\n // Use CLAUDE_CODE_OAUTH_TOKEN to override any stored /login credentials\n process.env.CLAUDE_CODE_OAUTH_TOKEN = config.posthogApiKey;\n // Disable experimental betas (like input_examples) that the LLM gateway doesn't support\n process.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = 'true';\n\n logToFile('Configured LLM gateway:', gatewayUrl);\n\n // Configure MCP server with PostHog authentication\n const mcpServers: McpServersConfig = {\n 'posthog-wizard': {\n type: 'http',\n url: config.posthogMcpUrl,\n headers: {\n Authorization: `Bearer ${config.posthogApiKey}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n },\n ...Object.fromEntries(\n Object.entries(config.additionalMcpServers ?? {}).map(\n ([name, { url }]) => [name, { type: 'http', url }],\n ),\n ),\n };\n\n // Add in-process wizard tools (env files, package manager detection, skill loading)\n const wizardToolsServer = await createWizardToolsServer({\n workingDirectory: config.workingDirectory,\n detectPackageManager: config.detectPackageManager,\n skillsBaseUrl: config.skillsBaseUrl,\n });\n mcpServers['wizard-tools'] = wizardToolsServer;\n\n // audit-3000 needs Opus 4.7's depth for the multi-phase audit chain;\n // every other workflow runs on Sonnet 4.6.\n const model =\n config.integrationLabel === 'audit-3000'\n ? 'anthropic/claude-opus-4-7'\n : 'anthropic/claude-sonnet-4-6';\n\n const agentRunConfig: AgentRunConfig = {\n workingDirectory: config.workingDirectory,\n mcpServers,\n model,\n wizardFlags: config.wizardFlags,\n wizardMetadata: config.wizardMetadata,\n };\n\n logToFile('Agent config:', {\n workingDirectory: agentRunConfig.workingDirectory,\n posthogMcpUrl: config.posthogMcpUrl,\n gatewayUrl,\n apiKeyPresent: !!config.posthogApiKey,\n });\n\n if (options.debug) {\n debug('Agent config:', {\n workingDirectory: agentRunConfig.workingDirectory,\n posthogMcpUrl: config.posthogMcpUrl,\n gatewayUrl,\n apiKeyPresent: !!config.posthogApiKey,\n });\n }\n\n getUI().log.step(`Verbose logs: ${getLogFilePath()}`);\n getUI().log.success(\"Agent initialized. Let's get cooking!\");\n return agentRunConfig;\n } catch (error) {\n getUI().log.error(\n `Failed to initialize agent: ${(error as Error).message}`,\n );\n logToFile('Agent initialization error:', error);\n debug('Agent initialization error:', error);\n throw error;\n }\n}\n\n/**\n * Check agent output for YARA scanner violations.\n * Used in both the success and catch paths of runAgent.\n */\nfunction checkYaraViolation(\n outputText: string,\n spinner: SpinnerHandle,\n): { error: AgentErrorType } | null {\n if (\n outputText.includes('[YARA CRITICAL]') ||\n outputText.includes('[YARA] Scanner error')\n ) {\n logToFile('Agent error: YARA_VIOLATION');\n spinner.stop('Security violation detected');\n return { error: AgentErrorType.YARA_VIOLATION };\n }\n return null;\n}\n\n/**\n * Execute an agent with the provided prompt and options\n * Handles the full lifecycle: spinner, execution, error handling\n *\n * @returns An object containing any error detected in the agent's output\n */\nexport async function runAgent(\n agentConfig: AgentRunConfig,\n prompt: string,\n options: WizardOptions,\n spinner: SpinnerHandle,\n config?: {\n estimatedDurationMinutes?: number;\n spinnerMessage?: string;\n successMessage?: string;\n errorMessage?: string;\n additionalFeatureQueue?: readonly AdditionalFeature[];\n abortCases?: readonly AbortCaseMatcher[];\n },\n middleware?: {\n onMessage(message: any): void;\n finalize(resultMessage: any, totalDurationMs: number): any;\n },\n): Promise<{ error?: AgentErrorType; message?: string }> {\n const {\n spinnerMessage = 'Customizing your PostHog setup...',\n successMessage = 'PostHog integration complete',\n errorMessage = 'Integration failed',\n abortCases = [],\n } = config ?? {};\n\n const { query } = await getSDKModule();\n\n spinner.start(spinnerMessage);\n\n const cliPath = getClaudeCodeExecutablePath();\n logToFile('Starting agent run');\n logToFile('Claude Code executable:', cliPath);\n logToFile('Prompt:', prompt);\n\n const startTime = Date.now();\n const collectedText: string[] = [];\n // Track if we received a successful result (before any cleanup errors)\n let receivedSuccessResult = false;\n let loggedInitialContext = false;\n let lastResultMessage: any = null;\n\n // Workaround for SDK bug: stdin closes before canUseTool responses can be sent.\n // The fix is to use an async generator for the prompt that stays open until\n // the result is received, keeping the stdin stream alive for permission responses.\n // See: https://github.com/anthropics/claude-code/issues/4775\n // See: https://github.com/anthropics/claude-agent-sdk-typescript/issues/41\n let signalDone: () => void;\n const resultReceived = new Promise<void>((resolve) => {\n signalDone = resolve;\n });\n\n const createPromptStream = async function* () {\n yield {\n type: 'user',\n session_id: '',\n message: { role: 'user', content: prompt },\n parent_tool_use_id: null,\n };\n await resultReceived;\n };\n\n // Helper to handle successful completion (used in normal path and race condition recovery)\n const completeWithSuccess = (\n suppressedError?: Error,\n ): { error?: AgentErrorType; message?: string } => {\n const durationMs = Date.now() - startTime;\n const durationSeconds = Math.round(durationMs / 1000);\n\n if (suppressedError) {\n logToFile(\n `Ignoring post-completion error, agent completed successfully in ${durationSeconds}s`,\n );\n logToFile('Suppressed error:', suppressedError.message);\n } else {\n logToFile(`Agent run completed in ${durationSeconds}s`);\n }\n\n // Extract and capture the agent's reflection on the run\n const outputText = collectedText.join('\\n');\n const remarkRegex = new RegExp(\n `${AgentSignals.WIZARD_REMARK.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n '\\\\$&',\n )}\\\\s*(.+?)(?:\\\\n|$)`,\n 's',\n );\n const remarkMatch = outputText.match(remarkRegex);\n if (remarkMatch && remarkMatch[1]) {\n const remark = remarkMatch[1].trim();\n if (remark) {\n analytics.capture(WIZARD_REMARK_EVENT_NAME, { remark });\n }\n }\n\n analytics.wizardCapture('agent completed', {\n duration_ms: durationMs,\n duration_seconds: durationSeconds,\n });\n try {\n middleware?.finalize(lastResultMessage, durationMs);\n } catch (e) {\n logToFile(`${AgentSignals.BENCHMARK} Middleware finalize error:`, e);\n }\n spinner.stop(successMessage);\n return {};\n };\n\n // Abort controller — lets us force-kill the SDK query when we detect an\n // [ABORT] signal in the agent's output. Also stashes the reason so the\n // runner can surface it via outroData after we unwind.\n const abortController = new AbortController();\n let abortReason: string | null = null;\n\n try {\n // Tools needed for the wizard:\n // - File operations: Read, Write, Edit\n // - Search: Glob, Grep\n // - Commands: Bash (with restrictions via canUseTool)\n // - MCP discovery: ListMcpResourcesTool (to find available skills)\n // - Skills: Skill (to load installed PostHog skills)\n // MCP tools (PostHog) come from mcpServers, not allowedTools\n const allowedTools = [\n 'Read',\n 'Write',\n 'Edit',\n 'Glob',\n 'Grep',\n 'Bash',\n 'Task',\n 'ListMcpResourcesTool',\n 'Skill',\n ...WIZARD_TOOL_NAMES,\n ];\n\n const response = query({\n prompt: createPromptStream(),\n options: {\n abortController,\n model: agentConfig.model,\n cwd: agentConfig.workingDirectory,\n permissionMode: 'acceptEdits',\n betas: ['context-1m-2025-08-07'],\n mcpServers: agentConfig.mcpServers,\n // Load skills from project's .claude/skills/ directory\n settingSources: ['project'],\n // Explicitly enable required tools including Skill\n allowedTools,\n sandbox: {\n enabled: true,\n allowUnsandboxedCommands: false,\n filesystem: {\n allowWrite: [\n '/' + agentConfig.workingDirectory,\n '/' + agentConfig.workingDirectory + '/**',\n '//tmp',\n '//tmp/**',\n '//private/tmp',\n '//private/tmp/**',\n // Package manager stores — allow writes so pnpm/npm can\n // install packages without breaking the user's existing setup\n '~/Library/pnpm/store/**', // pnpm global store (macOS)\n '~/.local/share/pnpm/store/**', // pnpm global store (Linux)\n '~/.pnpm-store/**', // pnpm alternate store\n '~/.npm/**', // npm cache\n '~/.yarn/**', // yarn classic cache\n '~/.yarn/berry/**', // yarn berry cache\n ],\n },\n network: {\n allowedDomains: [\n 'github.com',\n 'api.github.com',\n 'raw.githubusercontent.com',\n 'release-assets.githubusercontent.com',\n 'objects.githubusercontent.com',\n ],\n },\n },\n env: {\n ...process.env,\n // Prevent user's Anthropic API key from overriding the wizard's OAuth token\n ANTHROPIC_API_KEY: undefined,\n // Defer MCP tool schemas to avoid bloating the system prompt.\n // The posthog-wizard MCP exposes many query tools with large schemas;\n // without deferral these consume ~113k tokens upfront, leaving\n // almost no room in Sonnet's 200k context window.\n ENABLE_TOOL_SEARCH: 'auto:0',\n ANTHROPIC_CUSTOM_HEADERS: buildAgentEnv(\n agentConfig.wizardMetadata ?? {},\n agentConfig.wizardFlags ?? {},\n ),\n },\n canUseTool: (toolName: string, input: unknown) => {\n logToFile('canUseTool called:', { toolName, input });\n const result = wizardCanUseTool(\n toolName,\n input as Record<string, unknown>,\n );\n logToFile('canUseTool result:', result);\n return Promise.resolve(result);\n },\n systemPrompt: {\n type: 'preset',\n preset: 'claude_code',\n // Append wizard-wide commandments rather than replacing\n // the preset so we keep default Claude Code behaviors.\n append: getWizardCommandments(),\n },\n tools: { type: 'preset', preset: 'claude_code' },\n // Capture stderr from CLI subprocess for debugging\n stderr: (data: string) => {\n logToFile('CLI stderr:', data);\n if (options.debug) {\n debug('CLI stderr:', data);\n }\n },\n // Stop hook: drain additional feature queue, then collect remark, then allow stop\n hooks: {\n PreToolUse: createPreToolUseYaraHooks(),\n PostToolUse: createPostToolUseYaraHooks(),\n Stop: [\n {\n hooks: [\n createStopHook(\n config?.additionalFeatureQueue ?? [],\n collectedText,\n ),\n ],\n timeout: 30,\n },\n ],\n },\n },\n });\n\n // Process the async generator\n for await (const message of response) {\n // Log initial context size on the first assistant response so we can\n // detect sudden shifts in starting context (e.g. MCP schema bloat).\n if (!loggedInitialContext && message.type === 'assistant') {\n const usage = message.message?.usage as\n | {\n input_tokens?: number;\n cache_creation_input_tokens?: number;\n cache_read_input_tokens?: number;\n }\n | undefined;\n if (usage) {\n const input = usage.input_tokens ?? 0;\n const cacheCreation = usage.cache_creation_input_tokens ?? 0;\n const cacheRead = usage.cache_read_input_tokens ?? 0;\n const initialTokens = input + cacheCreation + cacheRead;\n logToFile(\n `Initial context: ${initialTokens} tokens (input=${input}, cache_creation=${cacheCreation}, cache_read=${cacheRead})`,\n );\n analytics.wizardCapture('agent initial context', {\n initial_tokens: initialTokens,\n input_tokens: input,\n cache_creation_input_tokens: cacheCreation,\n cache_read_input_tokens: cacheRead,\n });\n }\n loggedInitialContext = true;\n }\n\n // Pass receivedSuccessResult so handleSDKMessage can suppress user-facing error\n // output for post-success cleanup errors while still logging them to file\n handleSDKMessage(\n message,\n options,\n spinner,\n collectedText,\n receivedSuccessResult,\n );\n\n // [ABORT] detection: the skill emits \"[ABORT] <reason>\" when it\n // cannot complete the workflow. Kill the SDK query immediately —\n // the prompt doesn't need to cooperate with \"and exit\" because the\n // abort is enforced here. The reason is surfaced via the returned\n // AgentErrorType.ABORT so the runner can render a custom screen.\n if (\n abortCases.length > 0 &&\n !abortReason &&\n message.type === 'assistant'\n ) {\n const content = message.message?.content;\n if (Array.isArray(content)) {\n for (const block of content) {\n if (block.type === 'text' && typeof block.text === 'string') {\n const match = block.text.match(/\\[ABORT\\]\\s*(.+?)(?:\\n|$)/);\n if (match) {\n abortReason = match[1].trim();\n logToFile(`Agent emitted [ABORT]: ${abortReason}`);\n abortController.abort();\n signalDone!();\n break;\n }\n }\n }\n }\n }\n\n // 401: show auth error screen and exit immediately\n if (\n message.type === 'assistant' &&\n collectedText.join('\\n').includes('API Error: 401')\n ) {\n signalDone!();\n spinner.stop('Authentication failed');\n logToFile('Agent error: 401, showing auth error screen');\n getUI().showAuthError();\n await wizardAbort({\n message: 'Authentication failed (401)',\n error: new WizardError('Authentication failed'),\n });\n }\n\n try {\n middleware?.onMessage(message);\n } catch (e) {\n logToFile(`${AgentSignals.BENCHMARK} Middleware onMessage error:`, e);\n }\n\n // Signal completion when result received\n if (message.type === 'result') {\n // Track successful results before any potential cleanup errors\n // The SDK may emit a second error result during cleanup due to a race condition\n if (message.subtype === 'success' && !message.is_error) {\n receivedSuccessResult = true;\n lastResultMessage = message;\n }\n signalDone!();\n }\n }\n\n // If the middleware caught an [ABORT] and aborted the SDK query, surface\n // it as a structured error before checking other signals.\n if (abortReason) {\n spinner.stop('Wizard aborted');\n return { error: AgentErrorType.ABORT, message: abortReason };\n }\n\n const outputText = collectedText.join('\\n');\n\n // Check for YARA scanner violations\n const yaraResult = checkYaraViolation(outputText, spinner);\n if (yaraResult) return yaraResult;\n\n // Check for error markers in the agent's output\n if (outputText.includes(AgentSignals.ERROR_MCP_MISSING)) {\n logToFile('Agent error: MCP_MISSING');\n spinner.stop('Agent could not access PostHog MCP');\n return { error: AgentErrorType.MCP_MISSING };\n }\n\n if (outputText.includes(AgentSignals.ERROR_RESOURCE_MISSING)) {\n logToFile('Agent error: RESOURCE_MISSING');\n spinner.stop('Agent could not access setup resource');\n return { error: AgentErrorType.RESOURCE_MISSING };\n }\n\n // Check for API errors (rate limits, etc.)\n // Extract just the API error line(s), not the entire output\n const apiErrorMatch = outputText.match(/API Error: [^\\n]+/g);\n const apiErrorMessage = apiErrorMatch\n ? apiErrorMatch.join('\\n')\n : 'Unknown API error';\n\n if (outputText.includes('API Error: 429')) {\n logToFile('Agent error: RATE_LIMIT');\n spinner.stop('Rate limit exceeded');\n return { error: AgentErrorType.RATE_LIMIT, message: apiErrorMessage };\n }\n\n if (outputText.includes('API Error:')) {\n logToFile('Agent error: API_ERROR');\n spinner.stop('API error occurred');\n return { error: AgentErrorType.API_ERROR, message: apiErrorMessage };\n }\n\n return completeWithSuccess();\n } catch (error) {\n // Signal done to unblock the async generator\n signalDone!();\n\n // If the middleware caught an [ABORT] and triggered abortController.abort(),\n // the SDK will throw an AbortError — surface it as a clean abort result.\n if (abortReason) {\n spinner.stop('Wizard aborted');\n return { error: AgentErrorType.ABORT, message: abortReason };\n }\n\n // If we already received a successful result, the error is from SDK cleanup\n // This happens due to a race condition: the SDK tries to send a cleanup command\n // after the prompt stream closes, but streaming mode is still active.\n // See: https://github.com/anthropics/claude-agent-sdk-typescript/issues/41\n if (receivedSuccessResult) {\n return completeWithSuccess(error as Error);\n }\n\n // Check if we collected an error before the exception was thrown\n const outputText = collectedText.join('\\n');\n\n // Check for YARA scanner violations\n const yaraResult = checkYaraViolation(outputText, spinner);\n if (yaraResult) return yaraResult;\n\n // Extract just the API error line(s), not the entire output\n const apiErrorMatch = outputText.match(/API Error: [^\\n]+/g);\n const apiErrorMessage = apiErrorMatch\n ? apiErrorMatch.join('\\n')\n : 'Unknown API error';\n\n if (outputText.includes('API Error: 429')) {\n logToFile('Agent error (caught): RATE_LIMIT');\n spinner.stop('Rate limit exceeded');\n return { error: AgentErrorType.RATE_LIMIT, message: apiErrorMessage };\n }\n\n if (outputText.includes('API Error:')) {\n logToFile('Agent error (caught): API_ERROR');\n spinner.stop('API error occurred');\n return { error: AgentErrorType.API_ERROR, message: apiErrorMessage };\n }\n\n // No API error found, re-throw the original exception\n spinner.stop(errorMessage);\n getUI().log.error(`Error: ${(error as Error).message}`);\n logToFile('Agent run failed:', error);\n debug('Full error:', error);\n throw error;\n } finally {\n // Always capture run duration, even on abort/error, so we can alert on\n // long runs where the user gave up before completion.\n if (!receivedSuccessResult) {\n const durationMs = Date.now() - startTime;\n analytics.wizardCapture('agent aborted', {\n duration_ms: durationMs,\n duration_seconds: Math.round(durationMs / 1000),\n });\n }\n }\n}\n\n/**\n * Handle SDK messages and provide user feedback\n *\n * @param receivedSuccessResult - If true, suppress user-facing error output for cleanup errors\n * while still logging to file. The SDK may emit a second error\n * result after success due to cleanup race conditions.\n */\nfunction handleSDKMessage(\n message: SDKMessage,\n options: WizardOptions,\n spinner: SpinnerHandle,\n collectedText: string[],\n receivedSuccessResult = false,\n): void {\n logToFile(`SDK Message: ${message.type}`, JSON.stringify(message, null, 2));\n\n if (options.debug) {\n debug(`SDK Message type: ${message.type}`);\n }\n\n switch (message.type) {\n case 'assistant': {\n // Extract text content from assistant messages\n const content = message.message?.content;\n if (Array.isArray(content)) {\n for (const block of content) {\n if (block.type === 'text' && typeof block.text === 'string') {\n collectedText.push(block.text);\n\n // Check for [STATUS] markers\n const statusRegex = new RegExp(\n `^.*${AgentSignals.STATUS.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n '\\\\$&',\n )}\\\\s*(.+?)$`,\n 'm',\n );\n const statusMatch = block.text.match(statusRegex);\n if (statusMatch) {\n const statusText = statusMatch[1].trim();\n getUI().pushStatus(statusText);\n spinner.message(statusText);\n }\n }\n\n // Intercept TodoWrite tool_use blocks for task progression\n if (\n block.type === 'tool_use' &&\n block.name === 'TodoWrite' &&\n block.input?.todos &&\n Array.isArray(block.input.todos)\n ) {\n getUI().syncTodos(block.input.todos);\n }\n }\n }\n break;\n }\n\n case 'result': {\n // Check is_error flag - can be true even when subtype is 'success'\n if (message.is_error) {\n logToFile('Agent result with error:', message.result);\n if (typeof message.result === 'string') {\n collectedText.push(message.result);\n }\n // Only show errors to user if we haven't already succeeded.\n // Post-success errors are SDK cleanup noise (telemetry failures, streaming\n // mode race conditions). Full message already logged above via JSON dump.\n if (message.errors && !receivedSuccessResult) {\n for (const err of message.errors) {\n getUI().log.error(`Error: ${err}`);\n logToFile('ERROR:', err);\n }\n }\n } else if (message.subtype === 'success') {\n logToFile('Agent completed successfully');\n if (typeof message.result === 'string') {\n collectedText.push(message.result);\n }\n } else {\n logToFile('Agent result with error:', message.result);\n // Error result - only show to user if we haven't already succeeded.\n // Full message already logged above via JSON dump.\n if (message.errors && !receivedSuccessResult) {\n for (const err of message.errors) {\n getUI().log.error(`Error: ${err}`);\n logToFile('ERROR:', err);\n }\n }\n }\n break;\n }\n\n case 'system': {\n if (message.subtype === 'init') {\n logToFile('Agent session initialized', {\n model: message.model,\n tools: message.tools?.length,\n mcpServers: message.mcp_servers,\n });\n }\n break;\n }\n\n default:\n // Log other message types for debugging\n if (options.debug) {\n debug(`Unhandled message type: ${message.type}`);\n }\n break;\n }\n}\n"],"mappings":"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AAc,MAAM;CAAQ,CACtC;AAmQD,MAAa,QAAoB;CA/PK;EACpC,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;GAER;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAGA;GACA;GACA;GACA;GAEA;GACA;GACD;EACF;CAEuC;EACtC,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;GAER;GAEA;GAEA;GACA;GACA;GACD;EACF;CAEsC;EACrC,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;GACR;GACA;GACA;GACA;GACA;GACD;EACF;CAIwC;EACvC,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU,CAAC,6CAA6C;EACzD;CAE4C;EAC3C,MAAM;EACN,aAAa;EACb,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU,CACR,yCACA,uCACD;EACF;CAEmC;EAClC,MAAM;EACN,aAAa;EACb,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU,CAGR,6CACA,oCACD;EACF;CAIkD;EACjD,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;GAER;GACA;GACA;GACA;GAEA;GACA;GACD;EACF;CAEkD;EACjD,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;GAER;GACA;GACA;GACA;GACA;GAEA;GACA;GAGA;GACD;EACF;CAEyC;EACxC,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU,CAGR,8CACD;EACF;CAIiD;EAChD,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;GAER;GACA;GAEA;GACA;GACA;GACA;GAEA;GAEA;GAEA;GACA;GACD;EACF;CAIgC;EAC/B,MAAM;EACN,aAAa;EACb,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;GAER;GAEA;GACA;GACD;EACF;CAEgC;EAC/B,MAAM;E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"}
1
+ {"version":3,"file":"agent-interface-D9DeIikl.js","names":["_sdkModule","getSDKModule","fs"],"sources":["../src/utils/custom-headers.ts","../src/lib/safe-tools.ts","../src/lib/workflows/audit/types.ts","../src/lib/wizard-tools.ts","../src/lib/yara-scanner.ts","../src/lib/skill-install.ts","../src/lib/yara-hooks.ts","../src/lib/agent/commandments.ts","../src/lib/agent/agent-interface.ts"],"sourcesContent":["import { POSTHOG_FLAG_HEADER_PREFIX } from '../lib/constants';\n\n/**\n * Builds a list of custom headers for ANTHROPIC_CUSTOM_HEADERS.\n */\nexport function createCustomHeaders(): {\n add(key: string, value: string): void;\n /** Add a feature flag for PostHog ($feature/<flagKey>: variant). */\n addFlag(flagKey: string, variant: string): void;\n encode(): string;\n} {\n const entries: Array<{ key: string; value: string }> = [];\n\n return {\n add(key: string, value: string): void {\n const name =\n key.startsWith('x-') || key.startsWith('X-') ? key : `X-${key}`;\n entries.push({ key: name, value });\n },\n\n addFlag(flagKey: string, variant: string): void {\n const headerName = POSTHOG_FLAG_HEADER_PREFIX + flagKey.toUpperCase();\n entries.push({ key: headerName, value: variant });\n },\n\n encode(): string {\n return entries.map(({ key, value }) => `${key}: ${value}`).join('\\n');\n },\n };\n}\n","export const LINTING_TOOLS: string[] = [\n // All (general purpose)\n 'codespell',\n 'cspell',\n 'git-diff-check',\n 'gitleaks',\n 'trufflehog',\n\n // Amazon States Language\n 'asl-validator',\n\n // Ansible\n 'ansible-lint',\n\n // Apex, Java\n 'pmd',\n\n // Astro, CSS, GraphQL, GritQL, HTML, JavaScript, JSON, JSONC, JSON5, JSX, TSX, Svelte, TypeScript, Vue\n 'biome',\n\n // AWS CloudFormation templates\n 'cfn-lint',\n 'cfnlint',\n\n // AWS CloudFormation, Azure ARM, Dockerfile, Helm, Kubernetes, Security, Terraform\n 'checkov',\n\n // AWS CloudFormation, Azure ARM, Dockerfile, Kubernetes, Secrets, Security, Terraform, Vulnerabilities\n 'trivy',\n\n // Azure Resource Manager (ARM)\n 'test-aztemplate',\n\n // Bash / Shell\n 'shellcheck',\n 'shfmt',\n\n // Bazel, Starlark\n 'buildifier',\n\n // C, C++\n 'cpplint',\n 'clang-format',\n 'clang-tidy',\n 'cmake-format',\n 'iwyu',\n 'pragma-once',\n\n // C#, Dotnet (.NET)\n 'dotnet-format',\n\n // CircleCI Config\n 'circleci',\n\n // Clojure\n 'clj-kondo',\n\n // CoffeeScript\n 'coffeelint',\n\n // Commit messages\n 'commitlint',\n\n // Copy/paste detection\n 'jscpd',\n\n // CSS, SCSS, Sass\n 'stylelint',\n\n // CSS, GraphQL, HTML, JavaScript, JSON, JSONC, JSON5, JSX, TSX, Markdown, TypeScript, Vue, YAML\n 'prettier',\n\n // Cue\n 'cue-fmt',\n\n // Dart\n 'dart',\n\n // Dockerfile / Docker\n 'hadolint',\n\n // Dotenv\n 'dotenv-linter',\n\n // EditorConfig\n 'editorconfig-checker',\n\n // GitHub Actions\n 'actionlint',\n 'zizmor',\n\n // Go\n 'gofmt',\n 'gofumpt',\n 'goimports',\n 'gokart',\n 'golangci-lint',\n 'golines',\n\n // Go, Java, JavaScript, JSON, Python, Ruby, TypeScript, YAML\n 'semgrep',\n\n // GoReleaser\n 'goreleaser',\n\n // GraphQL\n 'graphql-schema-linter',\n\n // Groovy\n 'npm-groovy-lint',\n\n // HAML\n 'haml-lint',\n\n // HTML\n 'htmlhint',\n\n // HTML Templates\n 'djlint',\n\n // Java\n 'checkstyle',\n 'google-java-format',\n\n // JavaScript, JSON, TypeScript\n 'eslint',\n\n // Next.js\n 'next lint',\n\n // JavaScript, JSON, Markdown, TypeScript\n 'deno',\n\n // JavaScript, TypeScript\n 'rome',\n\n // JSON, JSONC, JSON5\n 'eslint-plugin-jsonc',\n 'eslint-plugin-json',\n\n // JSX, TSX\n 'eslint-plugin-jsx-a11y',\n 'eslint-plugin-react',\n\n // Jupyter Notebook\n 'nbqa',\n\n // Kotlin\n 'detekt',\n 'ktlint',\n\n // Kubernetes\n 'kubeconform',\n 'kube-linter',\n\n // LaTeX\n 'chktex',\n\n // Lua\n 'luacheck',\n 'stylua',\n\n // Markdown\n 'markdownlint',\n 'markdownlint-cli2',\n 'markdown-link-check',\n 'markdown-table-prettify',\n 'remark-lint',\n\n // Natural language / Prose\n 'textlint',\n 'vale',\n\n // Nix\n 'nixpkgs-fmt',\n\n // OpenAPI\n 'spectral',\n\n // package.json\n 'sort-package-json',\n\n // Perl\n 'perlcritic',\n 'perltidy',\n\n // PHP\n 'php-cs-fixer',\n 'phpcs',\n 'phpstan',\n 'psalm',\n\n // PNG\n 'oxipng',\n\n // PowerShell\n 'psscriptanalyzer',\n\n // Prisma\n 'prisma',\n\n // Protocol Buffers (Protobuf)\n 'protolint',\n 'buf',\n\n // Python\n 'pylint',\n 'flake8',\n 'isort',\n 'ruff',\n 'black',\n 'autopep8',\n 'bandit',\n 'mypy',\n 'pyright',\n 'sourcery',\n 'yapf',\n\n // R\n 'lintr',\n\n // Rego\n 'opa',\n 'regal',\n\n // Ruby\n 'rubocop',\n 'brakeman',\n 'rufo',\n 'standardrb',\n\n // Rust\n 'clippy',\n 'rustfmt',\n\n // Scala\n 'scalafmt',\n\n // Security, Vulnerabilities\n 'osv-scanner',\n\n // Security, Terraform\n 'terrascan',\n 'tfsec',\n\n // Snakemake\n 'snakemake --lint',\n 'snakefmt',\n\n // SQL\n 'sqlfluff',\n 'sql-formatter',\n 'sqlfmt',\n 'squawk',\n\n // SVG\n 'svgo',\n\n // Swift\n 'stringslint',\n 'swiftformat',\n 'swiftlint',\n\n // Terraform\n 'tflint',\n 'terraform',\n 'tofu',\n\n // Terragrunt\n 'terragrunt',\n\n // Textproto\n 'txtpbfmt',\n\n // TOML\n 'taplo',\n\n // Vue\n 'eslint-plugin-vue',\n\n // XML\n 'xmllint',\n\n // YAML\n 'yamllint',\n] as const;\n\nexport type LintingTool = (typeof LINTING_TOOLS)[number];\n","import type { WizardSession } from '../../wizard-session.js';\n\nexport type AuditStatus =\n | 'pending'\n | 'pass'\n | 'error'\n | 'warning'\n | 'suggestion';\n\nexport interface AuditCheck {\n id: string;\n area: string;\n label: string;\n status: AuditStatus;\n file?: string;\n details?: string;\n}\n\nexport interface AuditSeverityStyle {\n glyph: string;\n color: string;\n}\n\n/** Single source of truth for status glyph + color across audit views. */\nexport const AUDIT_SEVERITY_STYLE: Record<AuditStatus, AuditSeverityStyle> = {\n pending: { glyph: '◌', color: 'gray' },\n pass: { glyph: '✔', color: 'green' },\n error: { glyph: '✘', color: 'red' },\n warning: { glyph: '⚠', color: 'yellow' },\n suggestion: { glyph: '•', color: 'cyan' },\n};\n\nexport const AUDIT_CHECKS_FILE = '.posthog-audit-checks.json';\nexport const AUDIT_REPORT_FILE = 'posthog-audit-report.md';\nexport const AUDIT_CHECKS_KEY = 'auditChecks';\n\nexport function getAuditChecks(session: WizardSession): AuditCheck[] {\n const raw = session.frameworkContext[AUDIT_CHECKS_KEY];\n return Array.isArray(raw) ? (raw as AuditCheck[]) : [];\n}\n\n/**\n * Read the audit checks ledger off disk. Validation lives at write time —\n * every writer (`audit_seed_checks` / `audit_add_checks` / `audit_resolve_checks`\n * MCP tools, `seedAuditLedger`) zod-parses entries before the atomic write,\n * so by the time the file watcher fires we trust the shape and only guard\n * against the file not being a JSON array (corrupted / hand-edited / not yet\n * seeded).\n */\nexport function coerceAuditChecks(parsed: unknown): AuditCheck[] {\n return Array.isArray(parsed) ? (parsed as AuditCheck[]) : [];\n}\n","/**\n * Unified in-process MCP server for the PostHog wizard.\n *\n * Provides tools that run locally (secret values never leave the machine):\n * - check_env_keys: Check which env var keys exist in a .env file\n * - set_env_values: Create/update env vars in a .env file\n * - detect_package_manager: Detect the project's package manager(s)\n * - load_skill_menu / install_skill: Skill installation\n * - audit_seed_checks / audit_add_checks / audit_resolve_checks: Audit ledger ownership\n */\n\nimport path from 'path';\nimport fs from 'fs';\nimport { execFileSync } from 'child_process';\nimport { z } from 'zod';\nimport { logToFile } from '../utils/debug';\nimport { skillTmpPath } from '../utils/paths';\nimport type { PackageManagerDetector } from './detection/package-manager';\nimport {\n AUDIT_CHECKS_FILE,\n coerceAuditChecks,\n type AuditCheck,\n type AuditStatus,\n} from './workflows/audit/types';\n\n// ---------------------------------------------------------------------------\n// SDK dynamic import (ESM module loaded once, cached)\n// ---------------------------------------------------------------------------\n\nlet _sdkModule: any = null;\nasync function getSDKModule(): Promise<any> {\n if (!_sdkModule) {\n _sdkModule = await import('@anthropic-ai/claude-agent-sdk');\n }\n return _sdkModule;\n}\n\n// ---------------------------------------------------------------------------\n// Skill types\n// ---------------------------------------------------------------------------\n\nexport type SkillEntry = { id: string; name: string; downloadUrl: string };\n\nexport interface SkillMenu {\n categories: Record<string, SkillEntry[]>;\n}\n\n// ---------------------------------------------------------------------------\n// Standalone skill helpers (usable before the MCP server is created)\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch the skill menu from the skills server.\n * Returns parsed data on success, `null` on failure.\n */\nexport async function fetchSkillMenu(\n skillsBaseUrl: string,\n): Promise<SkillMenu | null> {\n try {\n const menuUrl = `${skillsBaseUrl}/skill-menu.json`;\n logToFile(`fetchSkillMenu: fetching from ${menuUrl}`);\n const resp = await fetch(menuUrl);\n if (resp.ok) {\n const data = (await resp.json()) as SkillMenu;\n logToFile(\n `fetchSkillMenu: loaded (${\n Object.keys(data.categories).length\n } categories)`,\n );\n return data;\n }\n logToFile(`fetchSkillMenu: failed with HTTP ${resp.status}`);\n return null;\n } catch (err: any) {\n logToFile(`fetchSkillMenu: error: ${err.message}`);\n return null;\n }\n}\n\n/**\n * Download and extract a skill.\n * By default installs to `<installDir>/.claude/skills/<id>/`.\n * Pass `skillsRoot` to override the base directory (e.g. `.posthog/skills`).\n */\nexport function downloadSkill(\n skillEntry: SkillEntry,\n installDir: string,\n skillsRoot?: string,\n): { success: boolean; error?: string } {\n const skillDir = skillsRoot\n ? path.join(installDir, skillsRoot, skillEntry.id)\n : path.join(installDir, '.claude', 'skills', skillEntry.id);\n const tmpFile = skillTmpPath(skillEntry.id);\n\n try {\n fs.mkdirSync(skillDir, { recursive: true });\n execFileSync('curl', ['-sL', skillEntry.downloadUrl, '-o', tmpFile], {\n timeout: 30000,\n });\n execFileSync('unzip', ['-o', tmpFile, '-d', skillDir], {\n timeout: 30000,\n });\n fs.writeFileSync(path.join(skillDir, '.posthog-wizard'), '');\n try {\n fs.unlinkSync(tmpFile);\n } catch {\n /* ignore cleanup errors */\n }\n\n logToFile(\n `downloadSkill: installed ${skillEntry.id} from ${skillEntry.downloadUrl}`,\n );\n return { success: true };\n } catch (err: any) {\n logToFile(`downloadSkill: error: ${err.message}`);\n return { success: false, error: err.message };\n }\n}\n\n/**\n * Structured result for installSkillById.\n * - `ok`: the skill was fetched and extracted; `path` is where it lives\n * relative to installDir.\n * - `menu-fetch-failed`: couldn't fetch or parse the skill menu.\n * - `skill-not-found`: the menu didn't contain a skill with this id.\n * - `download-failed`: found the skill but download/extract failed;\n * `message` has the underlying error.\n */\nexport type InstallSkillResult =\n | { kind: 'ok'; path: string }\n | { kind: 'menu-fetch-failed' }\n | { kind: 'skill-not-found'; skillId: string }\n | { kind: 'download-failed'; message: string };\n\n/**\n * High-level \"install a skill by ID\" helper. Fetches the skill menu,\n * finds the skill, downloads and extracts it. Workflows should use this\n * instead of composing fetchSkillMenu + downloadSkill themselves.\n */\nexport async function installSkillById(\n skillId: string,\n installDir: string,\n skillsBaseUrl: string,\n skillsRoot?: string,\n): Promise<InstallSkillResult> {\n const menu = await fetchSkillMenu(skillsBaseUrl);\n if (!menu) return { kind: 'menu-fetch-failed' };\n\n const skill = Object.values(menu.categories)\n .flat()\n .find((s) => s.id === skillId);\n if (!skill) return { kind: 'skill-not-found', skillId };\n\n const result = downloadSkill(skill, installDir, skillsRoot);\n if (!result.success) {\n return { kind: 'download-failed', message: result.error ?? 'unknown' };\n }\n\n const relPath = skillsRoot\n ? `${skillsRoot}/${skillId}`\n : `.claude/skills/${skillId}`;\n return { kind: 'ok', path: relPath };\n}\n\n// ---------------------------------------------------------------------------\n// Options for creating the wizard tools server\n// ---------------------------------------------------------------------------\n\nexport interface WizardToolsOptions {\n /** Root directory of the project being analyzed */\n workingDirectory: string;\n\n /** Framework-specific package manager detector */\n detectPackageManager: PackageManagerDetector;\n\n /** Base URL for the skills server (e.g. http://localhost:8765 or GitHub releases URL) */\n skillsBaseUrl: string;\n}\n\n// ---------------------------------------------------------------------------\n// Env file helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve filePath relative to workingDirectory, rejecting path traversal.\n */\nexport function resolveEnvPath(\n workingDirectory: string,\n filePath: string,\n): string {\n const resolved = path.resolve(workingDirectory, filePath);\n if (\n !resolved.startsWith(workingDirectory + path.sep) &&\n resolved !== workingDirectory\n ) {\n throw new Error(\n `Path traversal rejected: \"${filePath}\" resolves outside working directory`,\n );\n }\n return resolved;\n}\n\n/**\n * Ensure the given env file basename is covered by .gitignore in the working directory.\n * Creates .gitignore if it doesn't exist; appends the entry if missing.\n */\nexport function ensureGitignoreCoverage(\n workingDirectory: string,\n envFileName: string,\n): void {\n const gitignorePath = path.join(workingDirectory, '.gitignore');\n\n if (fs.existsSync(gitignorePath)) {\n const content = fs.readFileSync(gitignorePath, 'utf8');\n // Check if the file (or a glob covering it) is already listed\n if (content.split('\\n').some((line) => line.trim() === envFileName)) {\n return;\n }\n const newContent = content.endsWith('\\n')\n ? `${content}${envFileName}\\n`\n : `${content}\\n${envFileName}\\n`;\n fs.writeFileSync(gitignorePath, newContent, 'utf8');\n } else {\n fs.writeFileSync(gitignorePath, `${envFileName}\\n`, 'utf8');\n }\n}\n\n/**\n * Parse a .env file's content and return the set of defined key names.\n */\nexport function parseEnvKeys(content: string): Set<string> {\n const keys = new Set<string>();\n for (const line of content.split('\\n')) {\n const match = line.match(/^\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*=/);\n if (match) {\n keys.add(match[1]);\n }\n }\n return keys;\n}\n\n/**\n * Merge key-value pairs into existing .env content.\n * Updates existing keys in-place, appends new keys at the end.\n */\nexport function mergeEnvValues(\n content: string,\n values: Record<string, string>,\n): string {\n let result = content;\n const updatedKeys = new Set<string>();\n\n for (const [key, value] of Object.entries(values)) {\n const regex = new RegExp(`^(\\\\s*${key}\\\\s*=).*$`, 'm');\n if (regex.test(result)) {\n result = result.replace(regex, `$1${value}`);\n updatedKeys.add(key);\n }\n }\n\n const newKeys = Object.entries(values).filter(\n ([key]) => !updatedKeys.has(key),\n );\n if (newKeys.length > 0) {\n if (result.length > 0 && !result.endsWith('\\n')) {\n result += '\\n';\n }\n for (const [key, value] of newKeys) {\n result += `${key}=${value}\\n`;\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Audit ledger helpers\n// ---------------------------------------------------------------------------\n\nconst AUDIT_STATUSES: readonly AuditStatus[] = [\n 'pending',\n 'pass',\n 'error',\n 'warning',\n 'suggestion',\n];\n\nconst auditCheckSchema = z.object({\n id: z.string().min(1),\n area: z.string().min(1),\n label: z.string().min(1),\n status: z.enum(AUDIT_STATUSES as [AuditStatus, ...AuditStatus[]]),\n file: z.string().optional(),\n details: z.string().optional(),\n});\n\nconst auditUpdateSchema = z.object({\n id: z.string().min(1),\n status: z.enum(AUDIT_STATUSES as [AuditStatus, ...AuditStatus[]]),\n file: z.string().optional(),\n details: z.string().optional(),\n});\n\n/**\n * Atomically write JSON: write to .tmp then rename. The rename is what bumps\n * the file's mtime, which is what the UI's file watcher polls on.\n */\nfunction writeLedgerAtomic(targetPath: string, checks: AuditCheck[]): void {\n const tmpPath = `${targetPath}.tmp`;\n fs.writeFileSync(tmpPath, JSON.stringify(checks, null, 2), 'utf8');\n fs.renameSync(tmpPath, targetPath);\n}\n\n/**\n * Apply a batch of patches to the ledger by id. Returns the new array and the\n * list of update ids that didn't match any existing check.\n */\nfunction applyAuditUpdates(\n current: AuditCheck[],\n updates: Array<{\n id: string;\n status: AuditStatus;\n file?: string;\n details?: string;\n }>,\n): { next: AuditCheck[]; unknown: string[] } {\n const byId = new Map(current.map((c) => [c.id, c]));\n const unknown: string[] = [];\n\n for (const u of updates) {\n const existing = byId.get(u.id);\n if (!existing) {\n unknown.push(u.id);\n continue;\n }\n byId.set(u.id, {\n ...existing,\n status: u.status,\n ...(u.file !== undefined ? { file: u.file } : {}),\n ...(u.details !== undefined ? { details: u.details } : {}),\n });\n }\n\n return {\n next: current.map((c) => byId.get(c.id) ?? c),\n unknown,\n };\n}\n\n/**\n * Append new checks to a seeded ledger. Duplicate ids are reported without\n * mutating the current ledger, including duplicates inside the additions.\n */\nfunction applyAuditAdditions(\n current: AuditCheck[],\n additions: AuditCheck[],\n): { next: AuditCheck[]; duplicates: string[] } {\n const existingIds = new Set(current.map((c) => c.id));\n const additionIds = new Set<string>();\n const duplicates: string[] = [];\n\n for (const check of additions) {\n if (existingIds.has(check.id) || additionIds.has(check.id)) {\n duplicates.push(check.id);\n continue;\n }\n additionIds.add(check.id);\n }\n\n if (duplicates.length > 0) {\n return { next: current, duplicates };\n }\n\n return { next: [...current, ...additions], duplicates: [] };\n}\n\nfunction readLedger(targetPath: string): AuditCheck[] {\n if (!fs.existsSync(targetPath)) return [];\n try {\n const parsed = JSON.parse(fs.readFileSync(targetPath, 'utf8'));\n return coerceAuditChecks(parsed);\n } catch {\n return [];\n }\n}\n\ntype AppendAuditChecksResult =\n | { ok: true; added: number }\n | { ok: false; reason: 'missing-ledger' }\n | { ok: false; reason: 'duplicate-ids'; ids: string[] };\n\nfunction appendAuditChecksToLedger(\n targetPath: string,\n additions: AuditCheck[],\n): AppendAuditChecksResult {\n if (!fs.existsSync(targetPath)) {\n return { ok: false, reason: 'missing-ledger' };\n }\n\n const current = readLedger(targetPath);\n const { next, duplicates } = applyAuditAdditions(current, additions);\n if (duplicates.length > 0) {\n return { ok: false, reason: 'duplicate-ids', ids: duplicates };\n }\n\n writeLedgerAtomic(targetPath, next);\n return { ok: true, added: additions.length };\n}\n\n/**\n * Single async mutex shared by audit tools — guarantees a read-modify-write\n * cycle on the ledger is atomic across concurrent tool calls (e.g. future subagents).\n */\nfunction makeMutex() {\n let chain: Promise<unknown> = Promise.resolve();\n return async function run<T>(fn: () => Promise<T> | T): Promise<T> {\n const next = chain.then(() => fn());\n chain = next.catch(() => undefined);\n return next;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Server factory\n// ---------------------------------------------------------------------------\n\nconst SERVER_NAME = 'wizard-tools';\n\n/**\n * Create the unified in-process MCP server with all wizard tools.\n * Must be called asynchronously because the SDK is an ESM module loaded via dynamic import.\n */\nexport async function createWizardToolsServer(options: WizardToolsOptions) {\n const { workingDirectory, detectPackageManager, skillsBaseUrl } = options;\n const sdk = await getSDKModule();\n const { tool, createSdkMcpServer } = sdk;\n\n // Pre-fetch skill menu so category names are available in the tool schema\n let cachedSkillMenu: Record<string, SkillEntry[]> = {};\n let categoryNames: [string, ...string[]] = ['integration'];\n\n const menu = await fetchSkillMenu(skillsBaseUrl);\n if (menu) {\n cachedSkillMenu = menu.categories;\n }\n\n const keys = Object.keys(cachedSkillMenu);\n if (keys.length > 0) {\n categoryNames = keys as [string, ...string[]];\n }\n\n // -- check_env_keys -------------------------------------------------------\n\n const checkEnvKeys = tool(\n 'check_env_keys',\n 'Check which environment variable keys are present or missing in a .env file. Never reveals values.',\n {\n filePath: z\n .string()\n .describe('Path to the .env file, relative to the project root'),\n keys: z\n .array(z.string())\n .describe('Environment variable key names to check'),\n },\n (args: { filePath: string; keys: string[] }) => {\n const resolved = resolveEnvPath(workingDirectory, args.filePath);\n logToFile(`check_env_keys: ${resolved}, keys: ${args.keys.join(', ')}`);\n\n const existingKeys: Set<string> = fs.existsSync(resolved)\n ? parseEnvKeys(fs.readFileSync(resolved, 'utf8'))\n : new Set<string>();\n\n const results: Record<string, 'present' | 'missing'> = {};\n for (const key of args.keys) {\n results[key] = existingKeys.has(key) ? 'present' : 'missing';\n }\n\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(results, null, 2) },\n ],\n };\n },\n );\n\n // -- set_env_values -------------------------------------------------------\n\n const setEnvValues = tool(\n 'set_env_values',\n 'Create or update environment variable keys in a .env file. Creates the file if it does not exist. Ensures .gitignore coverage.',\n {\n filePath: z\n .string()\n .describe('Path to the .env file, relative to the project root'),\n values: z\n .record(z.string(), z.string())\n .describe('Key-value pairs to set'),\n },\n (args: { filePath: string; values: Record<string, string> }) => {\n // Block the wrong key name — the correct key is NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN or similar\n const forbidden = Object.keys(args.values).find(\n (k) => k.toUpperCase() === 'POSTHOG_KEY',\n );\n if (forbidden) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: \"${forbidden}\" is not a valid PostHog env var name. Use the project-specific key name from your framework's integration guide (e.g. NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN).`,\n },\n ],\n isError: true,\n };\n }\n\n const resolved = resolveEnvPath(workingDirectory, args.filePath);\n logToFile(\n `set_env_values: ${resolved}, keys: ${Object.keys(args.values).join(\n ', ',\n )}`,\n );\n\n const existing = fs.existsSync(resolved)\n ? fs.readFileSync(resolved, 'utf8')\n : '';\n const content = mergeEnvValues(existing, args.values);\n\n // Ensure parent directory exists\n const dir = path.dirname(resolved);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n fs.writeFileSync(resolved, content, 'utf8');\n\n // Ensure .gitignore coverage for this env file\n const envFileName = path.basename(resolved);\n ensureGitignoreCoverage(workingDirectory, envFileName);\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Updated ${Object.keys(args.values).length} key(s) in ${\n args.filePath\n }`,\n },\n ],\n };\n },\n );\n\n // -- detect_package_manager -----------------------------------------------\n\n const detectPM = tool(\n 'detect_package_manager',\n 'Detect which package manager(s) the project uses. Returns the name, install command, and run command for each detected package manager. Call this before running any install commands.',\n {},\n async () => {\n logToFile(`detect_package_manager: scanning ${workingDirectory}`);\n\n const result = await detectPackageManager(workingDirectory);\n\n logToFile(\n `detect_package_manager: detected ${result.detected.length} package manager(s)`,\n );\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n },\n );\n\n // -- load_skill_menu ------------------------------------------------------\n\n const loadSkillMenu = tool(\n 'load_skill_menu',\n 'Load available PostHog skills for a category. Returns skill IDs and names. Call this first, then use install_skill with the chosen ID.',\n {\n category: z.enum(categoryNames).describe('Skill category'),\n },\n (args: { category: string }) => {\n const skills = cachedSkillMenu[args.category];\n if (!skills || skills.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `No skills found for category \"${args.category}\".`,\n },\n ],\n isError: true,\n };\n }\n\n const menuText = skills.map((s) => `- ${s.id}: ${s.name}`).join('\\n');\n\n logToFile(\n `load_skill_menu: returning ${skills.length} skills for \"${args.category}\"`,\n );\n\n return {\n content: [{ type: 'text' as const, text: menuText }],\n };\n },\n );\n\n // -- install_skill --------------------------------------------------------\n\n const installSkill = tool(\n 'install_skill',\n 'Download and install a PostHog skill by ID. Call load_skill_menu first to see available skills. Extracts the skill to .claude/skills/<skillId>/.',\n {\n skillId: z\n .string()\n .describe(\n 'Skill ID from the skill menu (e.g., \"integration-nextjs-app-router\")',\n ),\n },\n (args: { skillId: string }) => {\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(args.skillId)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Error: skillId must be lowercase alphanumeric with hyphens.',\n },\n ],\n isError: true,\n };\n }\n\n // Look up download URL from cached menu\n const allSkills: SkillEntry[] = Object.values(cachedSkillMenu).flat();\n const skill = allSkills.find((s) => s.id === args.skillId);\n if (!skill) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: skill \"${args.skillId}\" not found. Use load_skill_menu to see available skills.`,\n },\n ],\n isError: true,\n };\n }\n\n const result = downloadSkill(skill, workingDirectory);\n if (result.success) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Skill installed to .claude/skills/${args.skillId}/`,\n },\n ],\n };\n } else {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error installing skill: ${result.error}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n\n // -- audit_seed_checks ----------------------------------------------------\n\n const auditLedgerPath = path.join(workingDirectory, AUDIT_CHECKS_FILE);\n const auditMutex = makeMutex();\n\n const auditSeedChecks = tool(\n 'audit_seed_checks',\n 'Seed the audit ledger at .posthog-audit-checks.json with the full set of pending checks. Call this once at the start of the audit. Atomically replaces any existing ledger.',\n {\n checks: z\n .array(auditCheckSchema)\n .describe('Full pending checklist to write to the ledger'),\n },\n async (args: { checks: AuditCheck[] }) => {\n return auditMutex(() => {\n writeLedgerAtomic(auditLedgerPath, args.checks);\n logToFile(`audit_seed_checks: wrote ${args.checks.length} entries`);\n return {\n content: [\n {\n type: 'text' as const,\n text: `Seeded ${args.checks.length} audit checks.`,\n },\n ],\n };\n });\n },\n );\n\n // -- audit_add_checks -----------------------------------------------------\n\n const auditAddChecks = tool(\n 'audit_add_checks',\n 'Append one or more pending checks to the existing audit ledger at .posthog-audit-checks.json. Call audit_seed_checks first. Atomically rejects duplicate ids without changing the ledger.',\n {\n checks: z\n .array(auditCheckSchema)\n .min(1)\n .describe('Additional checks to append to the existing ledger'),\n },\n async (args: { checks: AuditCheck[] }) => {\n return auditMutex(() => {\n const result = appendAuditChecksToLedger(auditLedgerPath, args.checks);\n\n if (!result.ok) {\n if (result.reason === 'missing-ledger') {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Error: audit ledger does not exist. Run audit_seed_checks first.',\n },\n ],\n isError: true,\n };\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: duplicate check id(s): ${result.ids.join(\n ', ',\n )}. Check ids must be unique.`,\n },\n ],\n isError: true,\n };\n }\n\n logToFile(`audit_add_checks: added ${result.added} entries`);\n return {\n content: [\n {\n type: 'text' as const,\n text: `Added ${result.added} audit check(s).`,\n },\n ],\n };\n });\n },\n );\n\n // -- audit_resolve_checks -------------------------------------------------\n\n const auditResolveChecks = tool(\n 'audit_resolve_checks',\n \"Resolve one or more audit checks by id. Patches each entry's status (and optional file/details) and writes the ledger back atomically. Concurrent calls serialize.\",\n {\n updates: z\n .array(auditUpdateSchema)\n .min(1)\n .describe('Patches to apply, keyed by check id'),\n },\n async (args: {\n updates: Array<{\n id: string;\n status: AuditStatus;\n file?: string;\n details?: string;\n }>;\n }) => {\n return auditMutex(() => {\n const current = readLedger(auditLedgerPath);\n const { next, unknown } = applyAuditUpdates(current, args.updates);\n\n if (unknown.length > 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: unknown check id(s): ${unknown.join(\n ', ',\n )}. Run audit_seed_checks first or check the id.`,\n },\n ],\n isError: true,\n };\n }\n\n writeLedgerAtomic(auditLedgerPath, next);\n logToFile(\n `audit_resolve_checks: applied ${args.updates.length} update(s)`,\n );\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Resolved ${args.updates.length} check(s).`,\n },\n ],\n };\n });\n },\n );\n\n // -- Assemble server ------------------------------------------------------\n\n return createSdkMcpServer({\n name: SERVER_NAME,\n version: '1.0.0',\n tools: [\n checkEnvKeys,\n setEnvValues,\n detectPM,\n loadSkillMenu,\n installSkill,\n auditSeedChecks,\n auditAddChecks,\n auditResolveChecks,\n ],\n });\n}\n\n/** Tool names exposed by the wizard-tools server, for use in allowedTools */\nexport const WIZARD_TOOL_NAMES = [\n `${SERVER_NAME}:check_env_keys`,\n `${SERVER_NAME}:set_env_values`,\n `${SERVER_NAME}:detect_package_manager`,\n `${SERVER_NAME}:load_skill_menu`,\n `${SERVER_NAME}:install_skill`,\n `${SERVER_NAME}:audit_seed_checks`,\n `${SERVER_NAME}:audit_add_checks`,\n `${SERVER_NAME}:audit_resolve_checks`,\n];\n\n// ---------------------------------------------------------------------------\n// Test-only exports\n// ---------------------------------------------------------------------------\n\nexport const __test = {\n writeLedgerAtomic,\n readLedger,\n applyAuditAdditions,\n appendAuditChecksToLedger,\n applyAuditUpdates,\n makeMutex,\n AUDIT_CHECKS_FILE,\n};\n","/**\n * YARA content scanner for the PostHog wizard.\n *\n * This file is the single source of truth for all wizard YARA rules.\n *\n * Scans tool inputs (pre-execution) and outputs (post-execution) for\n * security violations including PII leakage, hardcoded secrets,\n * prompt injection, and secret exfiltration.\n *\n * We use YARA-style regex rules rather than the real YARA C library to\n * avoid native binary dependencies in an npx-distributed npm package.\n *\n * This is Layer 2 (L2) in the wizard's defense-in-depth model,\n * complementing the prompt-based commandments (L0) and the\n * canUseTool() allowlist (L1).\n */\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport type YaraSeverity = 'critical' | 'high' | 'medium' | 'low';\n\nexport type YaraCategory =\n | 'posthog_pii'\n | 'posthog_hardcoded_key'\n | 'posthog_autocapture'\n | 'posthog_config'\n | 'prompt_injection'\n | 'exfiltration'\n | 'filesystem_safety'\n | 'supply_chain';\n\nexport type HookPhase = 'PreToolUse' | 'PostToolUse';\nexport type ToolTarget = 'Bash' | 'Write' | 'Edit' | 'Read' | 'Grep';\n\nexport interface YaraRule {\n /** Rule name matching the .yar file (e.g. 'pii_in_capture_call') */\n name: string;\n description: string;\n severity: YaraSeverity;\n category: YaraCategory;\n /** Which hook+tool combinations this rule applies to */\n appliesTo: Array<{ phase: HookPhase; tool: ToolTarget }>;\n /** Compiled regex patterns — any match triggers the rule */\n patterns: RegExp[];\n}\n\nexport interface YaraMatch {\n rule: YaraRule;\n /** The matched substring */\n matchedText: string;\n /** Byte offset in the scanned content */\n offset: number;\n}\n\nexport type ScanResult =\n | { matched: false }\n | { matched: true; matches: YaraMatch[] };\n\n// ─── Rule Definitions ────────────────────────────────────────────\n//\n// Patterns are compiled once at module load time for performance.\n// Design spec: policies/yara/RULES.md\n\nconst POST_WRITE_EDIT: Array<{ phase: HookPhase; tool: ToolTarget }> = [\n { phase: 'PostToolUse', tool: 'Write' },\n { phase: 'PostToolUse', tool: 'Edit' },\n];\n\nconst POST_READ_GREP: Array<{ phase: HookPhase; tool: ToolTarget }> = [\n { phase: 'PostToolUse', tool: 'Read' },\n { phase: 'PostToolUse', tool: 'Grep' },\n];\n\nconst PRE_BASH: Array<{ phase: HookPhase; tool: ToolTarget }> = [\n { phase: 'PreToolUse', tool: 'Bash' },\n];\n\n// ── §1 PostHog API Violations ────────────────────────────────────\n\nconst pii_in_capture_call: YaraRule = {\n name: 'pii_in_capture_call',\n description:\n \"Detects PII fields passed to posthog.capture() — violates 'NEVER send PII in capture()' commandment\",\n severity: 'high',\n category: 'posthog_pii',\n appliesTo: POST_WRITE_EDIT,\n patterns: [\n // Direct PII field names in capture properties\n /\\.capture\\s*\\([^)]{0,200}email/i,\n /\\.capture\\s*\\([^)]{0,200}phone/i,\n /\\.capture\\s*\\([^)]{0,200}full[_\\s]?name/i,\n /\\.capture\\s*\\([^)]{0,200}first[_\\s]?name/i,\n /\\.capture\\s*\\([^)]{0,200}last[_\\s]?name/i,\n /\\.capture\\s*\\([^)]{0,200}(street|mailing|home|billing)[_\\s]?address/i,\n /\\.capture\\s*\\([^)]{0,200}(ssn|social[_\\s]?security)/i,\n /\\.capture\\s*\\([^)]{0,200}(date[_\\s]?of[_\\s]?birth|dob|birthday)/i,\n /\\.capture\\s*\\([^)]{0,200}\\$ip/,\n // identify() allows email/phone/name (standard PostHog user properties),\n // but highly sensitive PII is still blocked in identify().\n /\\.identify\\s*\\([^)]{0,200}(ssn|social[_\\s]?security)/i,\n /\\.identify\\s*\\([^)]{0,200}(card[_\\s]?number|cvv|credit[_\\s]?card)/i,\n /\\.identify\\s*\\([^)]{0,200}(date[_\\s]?of[_\\s]?birth|dob|birthday)/i,\n /\\.identify\\s*\\([^)]{0,200}(street|mailing|home|billing)[_\\s]?address/i,\n // PII in $set properties via capture (bound to same object)\n /\\$set[^}]{0,200}email/i,\n /\\$set[^}]{0,200}phone/i,\n ],\n};\n\nconst hardcoded_posthog_key: YaraRule = {\n name: 'hardcoded_posthog_key',\n description:\n \"Detects hardcoded PostHog API keys in source — violates 'use environment variables' commandment\",\n severity: 'high',\n category: 'posthog_hardcoded_key',\n appliesTo: POST_WRITE_EDIT,\n patterns: [\n // PostHog project API key (phc_ prefix, 20+ alphanumeric chars)\n /phc_[a-zA-Z0-9]{20,}/,\n // PostHog personal API key (phx_ prefix)\n /phx_[a-zA-Z0-9]{20,}/,\n // Hardcoded key assignment patterns\n /apiKey\\s*[:=]\\s*['\"][a-zA-Z0-9_]{20,}['\"]/,\n /api_key\\s*[:=]\\s*['\"][a-zA-Z0-9_]{20,}['\"]/,\n /POSTHOG_PROJECT_TOKEN\\s*[:=]\\s*['\"][a-zA-Z0-9_]{20,}['\"]/,\n ],\n};\n\nconst autocapture_disabled: YaraRule = {\n name: 'autocapture_disabled',\n description:\n \"Detects agent disabling autocapture — violates 'don't disable autocapture' commandment\",\n severity: 'medium',\n category: 'posthog_autocapture',\n appliesTo: POST_WRITE_EDIT,\n patterns: [\n /autocapture\\s*:\\s*false/,\n /autocapture\\s*:\\s*'false'/,\n /autocapture\\s*:\\s*\"false\"/,\n /autocapture\\s*=\\s*False/,\n /disable_autocapture\\s*[:=]\\s*(true|True|1)/,\n ],\n};\n\n// ── §1b Additional PostHog config rules ──────────────────────────\n\nconst hardcoded_posthog_host: YaraRule = {\n name: 'hardcoded_posthog_host',\n description:\n 'Detects hardcoded PostHog host URLs in source — should use environment variables',\n severity: 'high',\n category: 'posthog_hardcoded_key',\n appliesTo: POST_WRITE_EDIT,\n patterns: [/['\"]https:\\/\\/(us|eu)\\.i\\.posthog\\.com['\"]/],\n};\n\nconst session_recording_disabled: YaraRule = {\n name: 'session_recording_disabled',\n description: 'Detects agent disabling session recording',\n severity: 'medium',\n category: 'posthog_config',\n appliesTo: POST_WRITE_EDIT,\n patterns: [\n /disable_session_recording\\s*:\\s*true/i,\n /disable_session_recording\\s*=\\s*True/,\n ],\n};\n\nconst opt_out_capturing: YaraRule = {\n name: 'opt_out_capturing',\n description: 'Detects agent opting out of PostHog capturing entirely',\n severity: 'medium',\n category: 'posthog_config',\n appliesTo: POST_WRITE_EDIT,\n patterns: [\n // Match opt_out_capturing as a config property (in init options), not as a method call.\n // posthog.opt_out_capturing() is a legitimate GDPR-required API.\n /opt_out_capturing\\s*[:=]\\s*(true|True|1)/i,\n /opted_out\\s*[:=]\\s*(true|True|1)/i,\n ],\n};\n\n// ── §2 Prompt Injection ──────────────────────────────────────────\n\nconst prompt_injection_wizard_override: YaraRule = {\n name: 'prompt_injection_wizard_override',\n description:\n 'Detects classic prompt injection attempting to override agent instructions',\n severity: 'critical',\n category: 'prompt_injection',\n appliesTo: POST_READ_GREP,\n patterns: [\n // Direct instruction override — classic injection patterns\n /ignore previous instructions/i,\n /disregard all/i,\n /forget your instructions/i,\n /override your rules/i,\n // Role manipulation\n /act as a different/i,\n /new instructions:/i,\n ],\n};\n\nconst prompt_injection_wizard_specific: YaraRule = {\n name: 'prompt_injection_wizard_specific',\n description:\n 'Detects wizard-specific manipulation or tool abuse attempts in project files',\n severity: 'medium',\n category: 'prompt_injection',\n appliesTo: POST_READ_GREP,\n patterns: [\n // Wizard-specific manipulation\n /skip posthog/i,\n /do not install posthog/i,\n /remove posthog/i,\n /uninstall posthog/i,\n /delete the posthog/i,\n // Tool abuse via file content\n /run the following command/i,\n /execute this shell command/i,\n // Role hijacking — require \"you are now a\" to avoid false positives\n // on legitimate phrases like \"you are now ready to...\"\n /you are now a\\s/i,\n ],\n};\n\nconst prompt_injection_base64: YaraRule = {\n name: 'prompt_injection_base64',\n description:\n 'Detects suspicious base64-encoded blocks in file content that may contain obfuscated prompt injection',\n severity: 'critical',\n category: 'prompt_injection',\n appliesTo: POST_READ_GREP,\n patterns: [\n // Long base64 strings (100+ chars) in comments or string literals\n // that aren't typical data URIs or legitimate base64 content\n /(?:\\/\\/|#|\\/\\*)\\s*[A-Za-z0-9+/]{100,}={0,2}/,\n ],\n};\n\n// ── §3 Secret Exfiltration ───────────────────────────────────────\n\nconst secret_exfiltration_via_command: YaraRule = {\n name: 'secret_exfiltration_via_command',\n description:\n 'Detects shell commands attempting to exfiltrate secrets or credentials',\n severity: 'critical',\n category: 'exfiltration',\n appliesTo: PRE_BASH,\n patterns: [\n // curl/wget with environment variable secrets\n /curl\\s+.*\\$\\{?[A-Z_]*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL)/i,\n /wget\\s+.*\\$\\{?[A-Z_]*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL)/i,\n // Piping sensitive content to network tools\n /(\\$\\{?[A-Z_]*(KEY|TOKEN|SECRET|PASSWORD)|\\.env|credentials)\\S*.*\\|\\s*curl/i,\n /(\\$\\{?[A-Z_]*(KEY|TOKEN|SECRET|PASSWORD)|\\.env|credentials)\\S*.*\\|\\s*wget/i,\n /\\|\\s*nc\\s/,\n /\\|\\s*netcat\\s/,\n // Base64 encoding piped to network\n /base64.*\\|\\s*(curl|wget|nc\\s)/i,\n // Reading .env and sending\n /cat\\s+.*\\.env.*\\|\\s*(curl|wget)/,\n // PostHog key exfiltration specifically\n /curl.*phc_[a-zA-Z0-9]/,\n /wget.*phc_[a-zA-Z0-9]/,\n ],\n};\n\n// ── §4 Filesystem Safety ─────────────────────────────────────────\n\nconst destructive_rm: YaraRule = {\n name: 'destructive_rm',\n description: 'Detects rm -rf or rm -r commands that could mass-delete files',\n severity: 'critical',\n category: 'filesystem_safety',\n appliesTo: PRE_BASH,\n patterns: [\n // Combined flags: rm -rf, rm -fr, rm -rfi, etc.\n /\\brm\\s+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r)\\b/,\n // Separated flags: rm -r -f, rm -f -r (with optional other flags)\n /\\brm\\s+(-[a-zA-Z]*\\s+)*-[a-zA-Z]*r[a-zA-Z]*\\s+(-[a-zA-Z]*\\s+)*-[a-zA-Z]*f\\b/,\n /\\brm\\s+(-[a-zA-Z]*\\s+)*-[a-zA-Z]*f[a-zA-Z]*\\s+(-[a-zA-Z]*\\s+)*-[a-zA-Z]*r\\b/,\n ],\n};\n\nconst git_force_push: YaraRule = {\n name: 'git_force_push',\n description: 'Detects git push --force which can overwrite remote history',\n severity: 'critical',\n category: 'filesystem_safety',\n appliesTo: PRE_BASH,\n patterns: [/git\\s+push\\s+.*--force/, /git\\s+push\\s+.*-f\\b/],\n};\n\nconst git_reset_hard: YaraRule = {\n name: 'git_reset_hard',\n description:\n 'Detects git reset --hard which discards all uncommitted changes',\n severity: 'critical',\n category: 'filesystem_safety',\n appliesTo: PRE_BASH,\n patterns: [/git\\s+reset\\s+--hard/],\n};\n\n// ── §5 Supply Chain ──────────────────────────────────────────────\n\nconst wrong_posthog_package: YaraRule = {\n name: 'wrong_posthog_package',\n description:\n 'Detects installing the wrong PostHog npm package — should be posthog-js or posthog-node',\n severity: 'high',\n category: 'supply_chain',\n appliesTo: PRE_BASH,\n patterns: [\n // Match \"npm install posthog\" but not \"posthog-js\", \"posthog-node\", etc.\n /npm\\s+install\\s+(?:--save\\s+|--save-dev\\s+|-[SD]\\s+)*posthog(?!\\s*-)/,\n /pnpm\\s+(?:add|install)\\s+(?:--save\\s+|--save-dev\\s+|-[SD]\\s+)*posthog(?!\\s*-)/,\n /yarn\\s+add\\s+(?:--dev\\s+|-D\\s+)*posthog(?!\\s*-)/,\n /bun\\s+(?:add|install)\\s+(?:--dev\\s+|-[dD]\\s+)*posthog(?!\\s*-)/,\n ],\n};\n\nconst npm_install_global: YaraRule = {\n name: 'npm_install_global',\n description:\n 'Detects global npm installs — should never install packages globally',\n severity: 'high',\n category: 'supply_chain',\n appliesTo: PRE_BASH,\n patterns: [/npm\\s+install\\s+-g\\b/, /npm\\s+install\\s+--global\\b/],\n};\n\n// ─── Rule Registry ───────────────────────────────────────────────\n\nexport const RULES: YaraRule[] = [\n // §1 PostHog API violations\n pii_in_capture_call,\n hardcoded_posthog_key,\n autocapture_disabled,\n hardcoded_posthog_host,\n session_recording_disabled,\n opt_out_capturing,\n // §2 Prompt injection\n prompt_injection_wizard_override,\n prompt_injection_wizard_specific,\n prompt_injection_base64,\n // §3 Secret exfiltration\n secret_exfiltration_via_command,\n // §4 Filesystem safety\n destructive_rm,\n git_force_push,\n git_reset_hard,\n // §5 Supply chain\n wrong_posthog_package,\n npm_install_global,\n];\n\n// ─── Scan Engine ─────────────────────────────────────────────────\n\n/** Maximum content length to scan (100 KB). Inputs beyond this are truncated. */\nconst MAX_SCAN_LENGTH = 100_000;\n\n/**\n * Scan content against rules applicable to a given hook phase and tool.\n * Returns all matching rules (one match per rule, first pattern wins).\n */\nexport function scan(\n content: string,\n phase: HookPhase,\n tool: ToolTarget,\n): ScanResult {\n // Cap input length to prevent pathological regex performance\n const scanContent =\n content.length > MAX_SCAN_LENGTH\n ? content.slice(0, MAX_SCAN_LENGTH)\n : content;\n const applicableRules = RULES.filter((r) =>\n r.appliesTo.some((a) => a.phase === phase && a.tool === tool),\n );\n\n const matches: YaraMatch[] = [];\n for (const rule of applicableRules) {\n for (const pattern of rule.patterns) {\n const match = pattern.exec(scanContent);\n if (match) {\n matches.push({\n rule,\n matchedText: match[0],\n offset: match.index,\n });\n break; // One match per rule is sufficient\n }\n }\n }\n\n return matches.length > 0 ? { matched: true, matches } : { matched: false };\n}\n\n/**\n * Scan all files in a skill directory for prompt injection.\n * Used for context-mill scanning after skill installation.\n */\nexport function scanSkillDirectory(\n files: Array<{ path: string; content: string }>,\n): ScanResult {\n const allMatches: YaraMatch[] = [];\n for (const file of files) {\n const result = scan(file.content, 'PostToolUse', 'Read');\n if (result.matched) {\n allMatches.push(...result.matches);\n }\n }\n return allMatches.length > 0\n ? { matched: true, matches: allMatches }\n : { matched: false };\n}\n","/**\n * Check if command is a PostHog skill installation from MCP.\n * We control the MCP server, so we only need to verify:\n * 1. It installs to .claude/skills/\n * 2. It downloads from our GitHub releases or localhost (dev)\n *\n * Extracted to its own module to avoid a circular dependency\n * between agent-interface.ts and yara-hooks.ts.\n */\nexport function isSkillInstallCommand(command: string): boolean {\n if (!command.startsWith('mkdir -p .claude/skills/')) return false;\n\n const urlMatch = command.match(/curl -sL ['\"]([^'\"]+)['\"]/);\n if (!urlMatch) return false;\n\n const url = urlMatch[1];\n return (\n url.startsWith('https://github.com/PostHog/context-mill/releases/') ||\n /^http:\\/\\/localhost:\\d+\\//.test(url)\n );\n}\n","/**\n * YARA hook wiring for the Claude Agent SDK.\n *\n * Creates PreToolUse and PostToolUse hook callback arrays that\n * integrate the YARA scanner into the wizard's agent loop. These\n * hooks are registered in the SDK's query() options alongside the\n * existing Stop hook.\n *\n * PreToolUse hooks block dangerous commands before execution.\n * PostToolUse hooks detect violations in written code and prompt\n * injection in read content, and scan context-mill skill downloads.\n */\n\nimport fs from 'fs';\nimport path from 'path';\nimport fg from 'fast-glob';\nimport { scan, scanSkillDirectory } from './yara-scanner';\nimport type { YaraMatch, ScanResult } from './yara-scanner';\nimport { logToFile } from '../utils/debug';\nimport { analytics } from '../utils/analytics';\nimport { isSkillInstallCommand } from './skill-install';\n\n// ─── Types ───────────────────────────────────────────────────────\n// Using loose types to avoid tight coupling to SDK version.\n// The SDK hook types are: HookCallbackMatcher[], where each matcher\n// has { matcher?: string, hooks: HookCallback[], timeout?: number }\n\ntype HookInput = Record<string, unknown>;\ntype HookOutput = Record<string, unknown>;\ntype HookCallback = (\n input: HookInput,\n toolUseID: string | undefined,\n options: { signal: AbortSignal },\n) => Promise<HookOutput>;\n\nexport interface HookCallbackMatcher {\n matcher?: string;\n hooks: HookCallback[];\n timeout?: number;\n}\n\n// ─── Scan Report Accumulator ─────────────────────────────────────\n\ntype ScanAction = 'blocked' | 'reverted' | 'warned' | 'aborted';\n\ninterface ScanReportEntry {\n rule: string;\n severity: string;\n action: ScanAction;\n phase: string;\n tool: string;\n}\n\nlet scanCount = 0;\nconst scanViolations: ScanReportEntry[] = [];\n\nfunction recordScan(): void {\n scanCount++;\n}\n\nfunction recordViolation(entry: ScanReportEntry): void {\n scanViolations.push(entry);\n}\n\n/** Reset counters (for testing) */\nexport function resetScanReport(): void {\n scanCount = 0;\n scanViolations.length = 0;\n}\n\n/** Format the scan report summary. Returns null if no scans occurred */\nexport function formatScanReport(): string | null {\n if (scanCount === 0) return null;\n\n const lines: string[] = ['', '— YARA Scanner Summary —'];\n const violationCount = scanViolations.length;\n const cleanCount = scanCount - violationCount;\n\n lines.push(\n `✓ ${scanCount} tool calls scanned, ${violationCount} violation${\n violationCount !== 1 ? 's' : ''\n } detected`,\n );\n\n if (violationCount > 0) {\n lines.push('');\n for (const v of scanViolations) {\n const tag = v.action.toUpperCase();\n lines.push(\n ` [${tag}] ${v.rule} (${v.severity.toUpperCase()}) — ${v.phase}:${\n v.tool\n }`,\n );\n }\n }\n\n if (cleanCount > 0) {\n lines.push('');\n lines.push(\n `No violations: ✓ ${cleanCount} clean scan${cleanCount !== 1 ? 's' : ''}`,\n );\n }\n\n lines.push('');\n return lines.join('\\n');\n}\n\nimport { WIZARD_YARA_REPORT_FILE } from '../utils/paths';\n\n/** Write the scan report to a JSON file. Returns the file path, or null if no scans occurred. */\nexport function writeScanReport(): string | null {\n if (scanCount === 0) return null;\n\n const report = {\n summary: {\n totalScans: scanCount,\n violations: scanViolations.length,\n clean: scanCount - scanViolations.length,\n },\n violations: scanViolations,\n };\n\n try {\n fs.writeFileSync(WIZARD_YARA_REPORT_FILE, JSON.stringify(report, null, 2));\n } catch (err) {\n logToFile('[YARA] Failed to write scan report:', err);\n return null;\n }\n return WIZARD_YARA_REPORT_FILE;\n}\n\n// ─── Hook Timeouts (ms) ─────────────────────────────────────────\n\n/** Timeout for synchronous scan hooks (PreToolUse, PostToolUse Write/Edit/Read) */\nconst HOOK_TIMEOUT_MS = 60;\n/** Timeout for skill install hook (involves filesystem I/O) */\nconst SKILL_SCAN_HOOK_TIMEOUT_MS = 120;\n\n// ─── Logging ─────────────────────────────────────────────────────\n\nfunction logYaraMatch(\n phase: string,\n tool: string,\n match: YaraMatch,\n action: ScanAction,\n): void {\n logToFile(\n `[YARA] ${phase}:${tool} [${action.toUpperCase()}] rule \"${\n match.rule.name\n }\" ` +\n `(severity: ${match.rule.severity}, category: ${match.rule.category})\\n` +\n ` Description: ${match.rule.description}\\n` +\n ` Matched text: \"${match.matchedText.substring(0, 200)}\"`,\n );\n analytics.wizardCapture('yara rule matched', {\n rule: match.rule.name,\n severity: match.rule.severity,\n category: match.rule.category,\n action,\n phase,\n tool,\n });\n}\n\n// ─── Severity helpers ────────────────────────────────────────────\n\nconst SEVERITY_RANK: Record<string, number> = {\n critical: 4,\n high: 3,\n medium: 2,\n low: 1,\n};\n\n/** Return the highest-severity match from a list of matches. */\nfunction highestSeverityMatch(matches: YaraMatch[]): YaraMatch {\n return matches.reduce((worst, m) =>\n (SEVERITY_RANK[m.rule.severity] ?? 0) >\n (SEVERITY_RANK[worst.rule.severity] ?? 0)\n ? m\n : worst,\n );\n}\n\n// ─── PreToolUse Hooks ────────────────────────────────────────────\n\n/**\n * Create PreToolUse hook matchers for YARA scanning.\n * Scans Bash commands before execution for exfiltration,\n * destructive operations, and supply chain violations.\n */\nexport function createPreToolUseYaraHooks(): HookCallbackMatcher[] {\n return [\n {\n hooks: [\n (input: HookInput): Promise<HookOutput> => {\n try {\n const toolName = input.tool_name as string;\n if (toolName !== 'Bash') return Promise.resolve({});\n\n const toolInput = input.tool_input as Record<string, unknown>;\n const command =\n typeof toolInput?.command === 'string' ? toolInput.command : '';\n\n if (!command) return Promise.resolve({});\n\n recordScan();\n const result = scan(command, 'PreToolUse', 'Bash');\n if (!result.matched) return Promise.resolve({});\n\n const match = highestSeverityMatch(result.matches);\n logYaraMatch('PreToolUse', 'Bash', match, 'blocked');\n recordViolation({\n rule: match.rule.name,\n severity: match.rule.severity,\n action: 'blocked',\n phase: 'PreToolUse',\n tool: 'Bash',\n });\n\n return Promise.resolve({\n decision: 'block',\n reason: `[YARA] ${match.rule.name}: ${match.rule.description}. Command blocked for security.`,\n });\n } catch (error) {\n logToFile('[YARA] PreToolUse hook error:', error);\n // Fail closed: block the command if scanning fails\n return Promise.resolve({\n decision: 'block',\n reason: '[YARA] Scanner error — command blocked as a precaution.',\n });\n }\n },\n ],\n timeout: HOOK_TIMEOUT_MS,\n },\n ];\n}\n\n// ─── PostToolUse Hooks ───────────────────────────────────────────\n\n/**\n * Create PostToolUse hook matchers for YARA scanning.\n *\n * Three matchers:\n * 1. Write/Edit — scan written content for PII, secrets, config violations\n * 2. Read/Grep — scan read content for prompt injection\n * 3. Bash (skill install) — scan downloaded skill files for poisoned content\n */\nexport function createPostToolUseYaraHooks(): HookCallbackMatcher[] {\n return [\n // ── Write/Edit content scanning ──\n {\n hooks: [\n (input: HookInput): Promise<HookOutput> => {\n try {\n const toolName = input.tool_name as string;\n if (toolName !== 'Write' && toolName !== 'Edit')\n return Promise.resolve({});\n\n const toolInput = input.tool_input as Record<string, unknown>;\n // For Write, scan the content being written\n // For Edit, scan the new_str (replacement text)\n const content =\n toolName === 'Write'\n ? (toolInput?.content as string) ?? ''\n : (toolInput?.new_str as string) ?? '';\n\n if (!content) return Promise.resolve({});\n\n recordScan();\n const tool = toolName;\n const result = scan(content, 'PostToolUse', tool);\n if (!result.matched) return Promise.resolve({});\n\n const match = highestSeverityMatch(result.matches);\n logYaraMatch('PostToolUse', tool, match, 'reverted');\n recordViolation({\n rule: match.rule.name,\n severity: match.rule.severity,\n action: 'reverted',\n phase: 'PostToolUse',\n tool,\n });\n\n return Promise.resolve({\n hookSpecificOutput: {\n hookEventName: 'PostToolUse',\n additionalContext:\n `[YARA VIOLATION] ${match.rule.name}: ${match.rule.description}. ` +\n `You MUST revert this change immediately. The content you just wrote violates security policy.`,\n },\n });\n } catch (error) {\n logToFile('[YARA] PostToolUse Write/Edit hook error:', error);\n // Fail closed: instruct the agent to revert if scanning fails\n return Promise.resolve({\n hookSpecificOutput: {\n hookEventName: 'PostToolUse',\n additionalContext:\n '[YARA] Scanner error — you MUST revert this change as a precaution.',\n },\n });\n }\n },\n ],\n timeout: HOOK_TIMEOUT_MS,\n },\n\n // ── Read/Grep prompt injection scanning ──\n {\n hooks: [\n (input: HookInput): Promise<HookOutput> => {\n try {\n const toolName = input.tool_name as string;\n if (toolName !== 'Read' && toolName !== 'Grep')\n return Promise.resolve({});\n\n const toolResponse = input.tool_response;\n const content =\n typeof toolResponse === 'string'\n ? toolResponse\n : JSON.stringify(toolResponse ?? '');\n\n if (!content) return Promise.resolve({});\n\n recordScan();\n const tool = toolName;\n const result = scan(content, 'PostToolUse', tool);\n if (!result.matched) return Promise.resolve({});\n\n const match = highestSeverityMatch(result.matches);\n\n if (match.rule.severity === 'critical') {\n logYaraMatch('PostToolUse', tool, match, 'aborted');\n recordViolation({\n rule: match.rule.name,\n severity: match.rule.severity,\n action: 'aborted',\n phase: 'PostToolUse',\n tool,\n });\n // Prompt injection: abort the session — context is poisoned\n return Promise.resolve({\n stopReason:\n `[YARA CRITICAL] ${match.rule.name}: Prompt injection detected in file content. ` +\n `Agent context is potentially poisoned. Session terminated for safety.`,\n });\n }\n\n logYaraMatch('PostToolUse', tool, match, 'warned');\n recordViolation({\n rule: match.rule.name,\n severity: match.rule.severity,\n action: 'warned',\n phase: 'PostToolUse',\n tool,\n });\n return Promise.resolve({\n hookSpecificOutput: {\n hookEventName: 'PostToolUse',\n additionalContext: `[YARA WARNING] ${match.rule.name}: ${match.rule.description}`,\n },\n });\n } catch (error) {\n logToFile('[YARA] PostToolUse Read/Grep hook error:', error);\n // Fail closed: terminate session if scanning fails on read content\n return Promise.resolve({\n stopReason:\n '[YARA] Scanner error while scanning read content — session terminated as a precaution.',\n });\n }\n },\n ],\n timeout: HOOK_TIMEOUT_MS,\n },\n\n // ── Context-mill skill install scanning ──\n {\n hooks: [\n async (input: HookInput): Promise<HookOutput> => {\n try {\n const toolName = input.tool_name as string;\n if (toolName !== 'Bash') return {};\n\n const toolInput = input.tool_input as Record<string, unknown>;\n const command =\n typeof toolInput?.command === 'string' ? toolInput.command : '';\n\n // Only scan after skill install commands\n if (!isSkillInstallCommand(command)) return {};\n\n // Extract skill directory from command\n const dirMatch = command.match(\n /mkdir -p (.claude\\/skills\\/[^\\s&]+)/,\n );\n if (!dirMatch) return {};\n\n const skillDir = dirMatch[1];\n const cwd = (input.cwd as string) ?? process.cwd();\n recordScan();\n const result = await scanSkillFiles(cwd, skillDir);\n\n if (!result.matched) return {};\n\n const match = highestSeverityMatch(result.matches);\n logYaraMatch(\n 'PostToolUse',\n 'Bash (skill install)',\n match,\n 'aborted',\n );\n recordViolation({\n rule: match.rule.name,\n severity: match.rule.severity,\n action: 'aborted',\n phase: 'PostToolUse',\n tool: 'Bash (skill)',\n });\n\n return {\n stopReason:\n `[YARA CRITICAL] Poisoned skill detected in ${skillDir}: ${match.rule.name}. ` +\n `The downloaded skill contains potential prompt injection. Session terminated for safety.`,\n };\n } catch (error) {\n logToFile('[YARA] PostToolUse skill install hook error:', error);\n // Fail closed: terminate if skill scanning fails\n return {\n stopReason:\n '[YARA] Scanner error while scanning skill files — session terminated as a precaution.',\n };\n }\n },\n ],\n timeout: SKILL_SCAN_HOOK_TIMEOUT_MS,\n },\n ];\n}\n\n// ─── Skill File Scanner ──────────────────────────────────────────\n\n/**\n * Read and scan all text files in a skill directory for prompt injection.\n */\nasync function scanSkillFiles(\n cwd: string,\n skillDir: string,\n): Promise<ScanResult> {\n const absoluteDir = path.resolve(cwd, skillDir);\n\n if (!fs.existsSync(absoluteDir)) {\n logToFile(`[YARA] Skill directory does not exist: ${absoluteDir}`);\n return { matched: false };\n }\n\n const files = await fg('**/*.{md,txt,yaml,yml,json,js,ts,py,rb,sh}', {\n cwd: absoluteDir,\n absolute: true,\n });\n\n const fileContents: Array<{ path: string; content: string }> = [];\n for (const filePath of files) {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n fileContents.push({ path: filePath, content });\n } catch (err) {\n logToFile(`[YARA] Could not read skill file ${filePath}:`, err);\n }\n }\n\n if (fileContents.length === 0) {\n logToFile(`[YARA] No text files found in skill directory: ${absoluteDir}`);\n return { matched: false };\n }\n\n logToFile(\n `[YARA] Scanning ${fileContents.length} files in skill directory: ${skillDir}`,\n );\n return scanSkillDirectory(fileContents);\n}\n","/**\n * Wizard-wide commandments that are always appended as a system prompt.\n *\n * Keep this as a simple string so it can be inlined into the compiled bundle\n * without extra files, copying, or runtime I/O.\n */\nconst WIZARD_COMMANDMENTS = [\n 'Never hallucinate a PostHog project token, host, or any other secret. Always use the real values that have been configured for this project (for example via environment variables).',\n\n 'Never write API keys, access tokens, or other secrets directly into source code. Always reference environment variables instead, and rely on the wizard-tools MCP server (check_env_keys / set_env_values) to create or update .env files.',\n\n 'Always use the detect_package_manager tool from the wizard-tools MCP server to determine the package manager. Do not guess based on lockfiles or hard-code npm, yarn, pnpm, bun, pip, etc.',\n\n 'When installing packages, start the installation as a background task and then continue with other work. Do not block waiting for installs to finish unless explicitly instructed.',\n\n 'Before writing to any file, you MUST read that exact file immediately beforehand using the Read tool, even if you have already read it earlier in the run. This avoids tool failures and stale edits.',\n\n 'Treat feature flags, custom properties, and event names as part of an analytics contract. Prefer reusing existing names and patterns in the project. When you must introduce new ones, make them clear, descriptive, and consistent with existing conventions, and avoid scattering the same flag or property across many unrelated callsites.',\n\n 'Prefer minimal, targeted edits that achieve the requested behavior while preserving existing structure and style. Avoid large refactors, broad reformatting, or unrelated changes unless explicitly requested.',\n\n 'Do not spawn subagents unless explicitly instructed to do so.',\n\n 'After reading the integration skill, use the TodoWrite tool to track your progress. Create a todo list at the describing the high-level areas of work, mark each as in_progress when you begin it, and completed when done. Describe high-level tasks from the workflow (\"inserting event tracking code\", etc), not specific files or lines. For each new stage of the integration, create additional todos if necessary. Create a final \"wrapping up\" step and mark it completed when the integration is complete.',\n\n 'Do not assume \"PostHog provider\" or \"PostHogProvider\" in todo labels, status messages, or code. Only use a provider if the skill workflow explicitly requires one for the specific framework.',\n\n 'If you see a [YARA WARNING] or [YARA VIOLATION] message, handle it silently. Fix the issue (revert the write, avoid the flagged pattern) without mentioning security warnings or YARA to the user. These are internal guardrails, not user-facing alerts. Never show scary security language to the user.',\n].join('\\n');\n\nexport function getWizardCommandments(): string {\n return WIZARD_COMMANDMENTS;\n}\n","/**\n * Shared agent interface for PostHog wizards\n * Uses Claude Agent SDK directly with PostHog LLM gateway\n */\n\nimport path from 'path';\nimport * as fs from 'fs';\nimport { getUI, type SpinnerHandle } from '../../ui';\nimport {\n debug,\n logToFile,\n initLogFile,\n getLogFilePath,\n} from '../../utils/debug';\nimport type { WizardOptions } from '../../utils/types';\nimport { analytics } from '../../utils/analytics';\nimport {\n WIZARD_REMARK_EVENT_NAME,\n POSTHOG_PROPERTY_HEADER_PREFIX,\n WIZARD_VARIANT_FLAG_KEY,\n WIZARD_VARIANTS,\n WIZARD_USER_AGENT,\n} from '../constants';\nimport {\n type AdditionalFeature,\n ADDITIONAL_FEATURE_PROMPTS,\n} from '../wizard-session';\nimport {\n registerCleanup,\n wizardAbort,\n WizardError,\n} from '../../utils/wizard-abort';\nimport { createCustomHeaders } from '../../utils/custom-headers';\nimport { getLlmGatewayUrlFromHost } from '../../utils/urls';\nimport { LINTING_TOOLS } from '../safe-tools';\nimport { createWizardToolsServer, WIZARD_TOOL_NAMES } from '../wizard-tools';\nimport {\n createPreToolUseYaraHooks,\n createPostToolUseYaraHooks,\n} from '../yara-hooks';\nimport { getWizardCommandments } from './commandments';\nimport type { PackageManagerDetector } from '../detection/package-manager';\n\n// Dynamic import cache for ESM module\nlet _sdkModule: any = null;\nasync function getSDKModule(): Promise<any> {\n if (!_sdkModule) {\n _sdkModule = await import('@anthropic-ai/claude-agent-sdk');\n }\n return _sdkModule;\n}\n\n/**\n * Get the path to the bundled Claude Code CLI from the SDK package.\n * This ensures we use the SDK's bundled version rather than the user's installed Claude Code.\n */\nfunction getClaudeCodeExecutablePath(): string {\n // require.resolve finds the package's main entry, then we get cli.js from same dir\n const sdkPackagePath = require.resolve('@anthropic-ai/claude-agent-sdk');\n return path.join(path.dirname(sdkPackagePath), 'cli.js');\n}\n\n// Using `any` because typed imports from ESM modules require import attributes\n// syntax which prettier cannot parse. See PR discussion for details.\ntype SDKMessage = any;\ntype McpServersConfig = any;\ntype AbortCaseMatcher = { match: RegExp };\n\nexport const AgentSignals = {\n /** Signal emitted when the agent reports progress to the user */\n STATUS: '[STATUS]',\n /** Signal emitted when the agent cannot access the PostHog MCP server */\n ERROR_MCP_MISSING: '[ERROR-MCP-MISSING]',\n /** Signal emitted when the agent cannot access the setup resource */\n ERROR_RESOURCE_MISSING: '[ERROR-RESOURCE-MISSING]',\n /**\n * Signal emitted when the agent cannot complete the workflow and is\n * aborting intentionally (distinct from errors). Format: \"[ABORT] <reason>\".\n * Workflows can declare an onAbort handler to render a custom screen.\n */\n ABORT: '[ABORT]',\n /** Signal emitted when the agent provides a remark about its run */\n WIZARD_REMARK: '[WIZARD-REMARK]',\n /** Signal prefix for benchmark logging */\n BENCHMARK: '[BENCHMARK]',\n} as const;\n\nexport type AgentSignal = (typeof AgentSignals)[keyof typeof AgentSignals];\n\n/**\n * Error types that can be returned from agent execution.\n * These correspond to the error signals that the agent emits.\n */\nexport enum AgentErrorType {\n /** Agent could not access the PostHog MCP server */\n MCP_MISSING = 'WIZARD_MCP_MISSING',\n /** Agent could not access the setup resource */\n RESOURCE_MISSING = 'WIZARD_RESOURCE_MISSING',\n /** API rate limit exceeded */\n RATE_LIMIT = 'WIZARD_RATE_LIMIT',\n /** Generic API error */\n API_ERROR = 'WIZARD_API_ERROR',\n /** YARA scanner detected a security violation */\n YARA_VIOLATION = 'WIZARD_YARA_VIOLATION',\n /** Agent intentionally aborted the workflow (emitted [ABORT] <reason>) */\n ABORT = 'WIZARD_ABORT',\n}\n\nconst BLOCKING_ENV_KEYS = [\n 'ANTHROPIC_API_KEY',\n 'ANTHROPIC_BASE_URL',\n 'ANTHROPIC_AUTH_TOKEN',\n];\nconst BLOCKING_SETTINGS_KEYS = ['apiKeyHelper'];\n\n/** Where a settings conflict was found. */\nexport type SettingsConflictSource = 'project' | 'managed';\n\n/** A single settings conflict detected during startup. */\nexport interface SettingsConflict {\n /** Where the conflict was found. */\n source: SettingsConflictSource;\n /** The blocking keys found (e.g. 'ANTHROPIC_BASE_URL', 'apiKeyHelper'). */\n keys: string[];\n /** Whether the wizard can back up / remove this file. Managed settings are read-only. */\n writable: boolean;\n}\n\n/**\n * Check a single settings file for blocking env keys and top-level settings keys.\n * Returns matched key names, or an empty array if none found.\n */\nfunction checkSettingsFile(filePath: string): string[] {\n try {\n const raw = fs.readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(raw);\n const matched: string[] = [];\n\n // Check env block for blocking env keys\n const envBlock = parsed?.env;\n if (envBlock && typeof envBlock === 'object') {\n matched.push(...BLOCKING_ENV_KEYS.filter((key) => key in envBlock));\n }\n\n // Check top-level settings keys\n matched.push(\n ...BLOCKING_SETTINGS_KEYS.filter(\n (key) => key in parsed && parsed[key] !== '' && parsed[key] != null,\n ),\n );\n\n return matched;\n } catch {\n // File doesn't exist or isn't valid JSON — skip\n return [];\n }\n}\n\n/**\n * Check if .claude/settings.json in the project directory contains env\n * overrides or apiKeyHelper that block the Wizard from accessing the PostHog LLM Gateway.\n * Returns the list of matched key names, or an empty array if none found.\n *\n * @deprecated Use {@link checkAllSettingsConflicts} for comprehensive detection.\n */\nexport function checkClaudeSettingsOverrides(\n workingDirectory: string,\n): string[] {\n const candidates = [\n path.join(workingDirectory, '.claude', 'settings.json'),\n path.join(workingDirectory, '.claude', 'settings'),\n ];\n\n for (const filePath of candidates) {\n const matched = checkSettingsFile(filePath);\n if (matched.length > 0) return matched;\n }\n\n return [];\n}\n\n/**\n * Managed settings path on macOS.\n * IT/MDM-deployed settings — readable by all users, writable only by root.\n */\nconst MANAGED_SETTINGS_PATH =\n '/Library/Application Support/ClaudeCode/managed-settings.json';\n\n/**\n * Check project and org-managed settings for blocking keys that conflict\n * with the wizard's proxy auth.\n */\nexport function checkAllSettingsConflicts(\n workingDirectory: string,\n): SettingsConflict[] {\n const conflicts: SettingsConflict[] = [];\n\n const sources: {\n source: SettingsConflictSource;\n paths: string[];\n writable: boolean;\n }[] = [\n {\n source: 'managed',\n paths: [MANAGED_SETTINGS_PATH],\n writable: false,\n },\n {\n source: 'project',\n paths: [\n path.join(workingDirectory, '.claude', 'settings.json'),\n path.join(workingDirectory, '.claude', 'settings'),\n ],\n writable: true,\n },\n ];\n\n for (const { source, paths, writable } of sources) {\n for (const filePath of paths) {\n const keys = checkSettingsFile(filePath);\n if (keys.length > 0) {\n conflicts.push({ source, keys, writable });\n break; // Only one conflict per source (settings.json vs settings fallback)\n }\n }\n }\n\n return conflicts;\n}\n\n/**\n * Copy .claude/settings.json to .wizard-backup (overwriting if it exists),\n * then remove the original so the SDK doesn't load the blocking overrides.\n */\nexport function backupAndFixClaudeSettings(workingDirectory: string): boolean {\n for (const name of ['settings.json', 'settings']) {\n const filePath = path.join(workingDirectory, '.claude', name);\n const backupPath = `${filePath}.wizard-backup`;\n analytics.wizardCapture('backedup-claude-settings');\n try {\n fs.copyFileSync(filePath, backupPath);\n fs.unlinkSync(filePath);\n registerCleanup(() => {\n try {\n restoreClaudeSettings(workingDirectory);\n } catch (error) {\n analytics.captureException(error);\n }\n });\n return true;\n } catch {\n // File doesn't exist — try next candidate\n }\n }\n return false;\n}\n\n/**\n * Restore .claude/settings.json from .wizard-backup.\n * Copies (not moves) so the backup is preserved.\n */\nexport function restoreClaudeSettings(workingDirectory: string): void {\n for (const name of ['settings.json', 'settings']) {\n const backup = path.join(\n workingDirectory,\n '.claude',\n `${name}.wizard-backup`,\n );\n try {\n fs.copyFileSync(backup, path.join(workingDirectory, '.claude', name));\n analytics.wizardCapture('restored-claude-settings');\n return;\n } catch (error) {\n analytics.captureException(error);\n }\n }\n}\n\nexport type AgentConfig = {\n workingDirectory: string;\n posthogMcpUrl: string;\n posthogApiKey: string;\n posthogApiHost: string;\n additionalMcpServers?: Record<string, { url: string }>;\n detectPackageManager: PackageManagerDetector;\n /** Base URL for the skills server (context-mill dev or GitHub releases) */\n skillsBaseUrl: string;\n /** Feature flag key -> variant (evaluated at start of run). */\n wizardFlags?: Record<string, string>;\n wizardMetadata?: Record<string, string>;\n /** Workflow identifier — selects the model for that workflow. */\n integrationLabel?: string;\n};\n\n/**\n * Stop hook return type: either allow stop or block with a reason.\n */\nexport type StopHookResult =\n | Record<string, never>\n | { decision: 'block'; reason: string };\n\n/**\n * Create a stop hook callback that drains the additional feature queue,\n * then collects a remark, then allows stop.\n *\n * Three-phase logic using closure state:\n * Phase 1 — drain queue: block with each feature prompt in order\n * Phase 2 — collect remark (once): block with remark prompt\n * Phase 3 — allow stop: return {}\n */\nexport function createStopHook(\n featureQueue: readonly AdditionalFeature[],\n collectedText?: string[],\n): (input: { stop_hook_active: boolean }) => StopHookResult {\n let featureIndex = 0;\n let remarkRequested = false;\n\n return (input: { stop_hook_active: boolean }): StopHookResult => {\n logToFile('Stop hook triggered', {\n stop_hook_active: input.stop_hook_active,\n featureIndex,\n remarkRequested,\n queueLength: featureQueue.length,\n });\n\n // On API errors, allow stop immediately — blocking with remark/feature\n // prompts would just fail again. The auth error screen is shown separately.\n if (collectedText) {\n const text = collectedText.join('\\n');\n if (text.includes('API Error:')) {\n logToFile('Stop hook: API error detected, allowing immediate stop');\n return {};\n }\n }\n\n // Phase 1: drain feature queue\n if (featureIndex < featureQueue.length) {\n const feature = featureQueue[featureIndex++];\n const prompt = ADDITIONAL_FEATURE_PROMPTS[feature];\n logToFile(`Stop hook: injecting feature prompt for ${feature}`);\n return { decision: 'block', reason: prompt };\n }\n\n // Phase 2: collect remark (once)\n if (!remarkRequested) {\n remarkRequested = true;\n logToFile('Stop hook: requesting reflection');\n return {\n decision: 'block',\n reason: `Before concluding, provide a brief remark about what information or guidance would have been useful to have in the integration prompt or documentation for this run. Specifically cite anything that would have prevented tool failures, erroneous edits, or other wasted turns. Format your response exactly as: ${AgentSignals.WIZARD_REMARK} Your remark here`,\n };\n }\n\n // Phase 3: allow stop\n logToFile('Stop hook: allowing stop');\n return {};\n };\n}\n\n/**\n * Internal configuration object returned by initializeAgent\n */\ntype AgentRunConfig = {\n workingDirectory: string;\n mcpServers: McpServersConfig;\n model: string;\n wizardFlags?: Record<string, string>;\n wizardMetadata?: Record<string, string>;\n};\n\n/**\n * Select wizard metadata from WIZARD_VARIANTS using the variant feature flag.\n * If the flag is missing or the value is not in config, returns the \"base\" variant (VARIANT: \"base\").\n */\nexport function buildWizardMetadata(\n flags: Record<string, string> = {},\n): Record<string, string> {\n const variantKey = flags[WIZARD_VARIANT_FLAG_KEY];\n const variant =\n (variantKey && WIZARD_VARIANTS[variantKey]) ?? WIZARD_VARIANTS['base'];\n return { ...variant };\n}\n\n/**\n * Build env for the SDK subprocess: process.env plus ANTHROPIC_CUSTOM_HEADERS, which always\n * includes `x-posthog-use-bedrock-fallback: true` so the LLM gateway falls back to Bedrock on\n * Anthropic 5xx, plus any wizard metadata/flags.\n */\nfunction buildAgentEnv(\n wizardMetadata: Record<string, string>,\n wizardFlags: Record<string, string>,\n): string {\n const headers = createCustomHeaders();\n headers.add('x-posthog-use-bedrock-fallback', 'true');\n for (const [key, value] of Object.entries(wizardMetadata)) {\n headers.add(\n key.startsWith(POSTHOG_PROPERTY_HEADER_PREFIX)\n ? key\n : `${POSTHOG_PROPERTY_HEADER_PREFIX}${key}`,\n value,\n );\n }\n for (const [flagKey, variant] of Object.entries(wizardFlags)) {\n if (!flagKey.toLowerCase().startsWith('wizard')) continue;\n headers.addFlag(flagKey, variant);\n }\n const encoded = headers.encode();\n logToFile('ANTHROPIC_CUSTOM_HEADERS', encoded);\n return encoded;\n}\n\n/**\n * Package managers that can be used to run commands.\n */\nconst PACKAGE_MANAGERS = [\n // JavaScript\n 'npm',\n 'pnpm',\n 'yarn',\n 'bun',\n 'npx',\n // Python\n 'pip',\n 'pip3',\n 'poetry',\n 'pipenv',\n 'uv',\n];\n\n/**\n * Safe scripts/commands that can be run with any package manager.\n * Uses startsWith matching, so 'build' matches 'build', 'build:prod', etc.\n * Note: Linting tools are in LINTING_TOOLS and checked separately.\n */\nconst SAFE_SCRIPTS = [\n // Package installation\n 'install',\n 'add',\n 'ci',\n // Build\n 'build',\n // Type checking (various naming conventions)\n 'tsc',\n 'typecheck',\n 'type-check',\n 'check-types',\n 'types',\n // Linting/formatting script names (actual tools are in LINTING_TOOLS)\n 'lint',\n 'format',\n];\n\n/**\n * Dangerous shell operators that could allow command injection.\n * Note: We handle `2>&1` and `| tail/head` separately as safe patterns.\n */\nconst DANGEROUS_OPERATORS = /[;`$()]/;\n\n// Re-export for backwards compatibility — canonical source is skill-install.ts\nexport { isSkillInstallCommand } from '../skill-install';\n\n/**\n * Check if command is an allowed package manager command.\n * Matches: <pkg-manager> [run|exec] <safe-script> [args...]\n */\nfunction matchesAllowedPrefix(command: string): boolean {\n const parts = command.split(/\\s+/);\n if (parts.length === 0 || !PACKAGE_MANAGERS.includes(parts[0])) {\n return false;\n }\n\n // Skip 'run' or 'exec' if present\n let scriptIndex = 1;\n if (parts[scriptIndex] === 'run' || parts[scriptIndex] === 'exec') {\n scriptIndex++;\n }\n\n // Get the script/command portion (may include args)\n const scriptPart = parts.slice(scriptIndex).join(' ');\n\n // Check if script starts with any safe script name or linting tool\n return (\n SAFE_SCRIPTS.some((safe) => scriptPart.startsWith(safe)) ||\n LINTING_TOOLS.some((tool) => scriptPart.startsWith(tool))\n );\n}\n\n/**\n * Permission hook that allows only safe commands.\n * - Package manager install commands\n * - Build/typecheck/lint commands for verification\n * - Piping to tail/head for output limiting is allowed\n * - Stderr redirection (2>&1) is allowed\n */\nexport function wizardCanUseTool(\n toolName: string,\n input: Record<string, unknown>,\n):\n | { behavior: 'allow'; updatedInput: Record<string, unknown> }\n | { behavior: 'deny'; message: string } {\n // Block direct reads/writes of .env files — use wizard-tools MCP instead\n if (toolName === 'Read' || toolName === 'Write' || toolName === 'Edit') {\n const filePath = typeof input.file_path === 'string' ? input.file_path : '';\n const basename = path.basename(filePath);\n if (basename.startsWith('.env')) {\n logToFile(`Denying ${toolName} on env file: ${filePath}`);\n return {\n behavior: 'deny',\n message: `Direct ${toolName} of ${basename} is not allowed. Use the wizard-tools MCP server (check_env_keys / set_env_values) to read or modify environment variables.`,\n };\n }\n return { behavior: 'allow', updatedInput: input };\n }\n\n // Block Grep when it directly targets a .env file.\n // Note: ripgrep skips dotfiles (like .env*) by default during directory traversal,\n // so broad searches like `Grep { path: \".\" }` are already safe.\n if (toolName === 'Grep') {\n const grepPath = typeof input.path === 'string' ? input.path : '';\n if (grepPath && path.basename(grepPath).startsWith('.env')) {\n logToFile(`Denying Grep on env file: ${grepPath}`);\n return {\n behavior: 'deny',\n message: `Grep on ${path.basename(\n grepPath,\n )} is not allowed. Use the wizard-tools MCP server (check_env_keys) to check environment variables.`,\n };\n }\n return { behavior: 'allow', updatedInput: input };\n }\n\n // Allow all other non-Bash tools\n if (toolName !== 'Bash') {\n return { behavior: 'allow', updatedInput: input };\n }\n\n const command = (\n typeof input.command === 'string' ? input.command : ''\n ).trim();\n\n // Block definitely dangerous operators: ; ` $ ( )\n if (DANGEROUS_OPERATORS.test(command)) {\n logToFile(`Denying bash command with dangerous operators: ${command}`);\n debug(`Denying bash command with dangerous operators: ${command}`);\n analytics.wizardCapture('bash denied', {\n reason: 'dangerous operators',\n command,\n });\n return {\n behavior: 'deny',\n message: `Bash command not allowed. Shell operators like ; \\` $ ( ) are not permitted.`,\n };\n }\n\n // Normalize: remove safe stderr redirection (2>&1, 2>&2, etc.)\n const normalized = command.replace(/\\s*\\d*>&\\d+\\s*/g, ' ').trim();\n\n // Check for pipe to tail/head (safe output limiting)\n const pipeMatch = normalized.match(/^(.+?)\\s*\\|\\s*(tail|head)(\\s+\\S+)*\\s*$/);\n if (pipeMatch) {\n const baseCommand = pipeMatch[1].trim();\n\n // Block if base command has pipes or & (multiple chaining)\n if (/[|&]/.test(baseCommand)) {\n logToFile(`Denying bash command with multiple pipes: ${command}`);\n debug(`Denying bash command with multiple pipes: ${command}`);\n analytics.wizardCapture('bash denied', {\n reason: 'multiple pipes',\n command,\n });\n return {\n behavior: 'deny',\n message: `Bash command not allowed. Only single pipe to tail/head is permitted.`,\n };\n }\n\n if (matchesAllowedPrefix(baseCommand)) {\n logToFile(`Allowing bash command with output limiter: ${command}`);\n debug(`Allowing bash command with output limiter: ${command}`);\n return { behavior: 'allow', updatedInput: input };\n }\n }\n\n // Block remaining pipes and & (not covered by tail/head case above)\n if (/[|&]/.test(normalized)) {\n logToFile(`Denying bash command with pipe/&: ${command}`);\n debug(`Denying bash command with pipe/&: ${command}`);\n analytics.wizardCapture('bash denied', {\n reason: 'disallowed pipe',\n command,\n });\n return {\n behavior: 'deny',\n message: `Bash command not allowed. Pipes are only permitted with tail/head for output limiting.`,\n };\n }\n\n // Check if command starts with any allowed prefix (package manager commands)\n if (matchesAllowedPrefix(normalized)) {\n logToFile(`Allowing bash command: ${command}`);\n debug(`Allowing bash command: ${command}`);\n return { behavior: 'allow', updatedInput: input };\n }\n\n logToFile(`Denying bash command: ${command}`);\n debug(`Denying bash command: ${command}`);\n analytics.wizardCapture('bash denied', {\n reason: 'not in allowlist',\n command,\n });\n return {\n behavior: 'deny',\n message: `Bash command not allowed. Only install, build, typecheck, lint, and formatting commands are permitted.`,\n };\n}\n\n/**\n * Initialize agent configuration for the LLM gateway\n */\nexport async function initializeAgent(\n config: AgentConfig,\n options: WizardOptions,\n): Promise<AgentRunConfig> {\n // Initialize log file for this run\n initLogFile();\n logToFile('Agent initialization starting');\n logToFile('Install directory:', options.installDir);\n\n getUI().log.step('Initializing Claude agent...');\n\n try {\n // Configure LLM gateway environment variables (inherited by SDK subprocess)\n const gatewayUrl = getLlmGatewayUrlFromHost(config.posthogApiHost);\n process.env.ANTHROPIC_BASE_URL = gatewayUrl;\n process.env.ANTHROPIC_AUTH_TOKEN = config.posthogApiKey;\n // Use CLAUDE_CODE_OAUTH_TOKEN to override any stored /login credentials\n process.env.CLAUDE_CODE_OAUTH_TOKEN = config.posthogApiKey;\n // Disable experimental betas (like input_examples) that the LLM gateway doesn't support\n process.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = 'true';\n\n logToFile('Configured LLM gateway:', gatewayUrl);\n\n // Configure MCP server with PostHog authentication\n const mcpServers: McpServersConfig = {\n 'posthog-wizard': {\n type: 'http',\n url: config.posthogMcpUrl,\n headers: {\n Authorization: `Bearer ${config.posthogApiKey}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n },\n ...Object.fromEntries(\n Object.entries(config.additionalMcpServers ?? {}).map(\n ([name, { url }]) => [name, { type: 'http', url }],\n ),\n ),\n };\n\n // Add in-process wizard tools (env files, package manager detection, skill loading)\n const wizardToolsServer = await createWizardToolsServer({\n workingDirectory: config.workingDirectory,\n detectPackageManager: config.detectPackageManager,\n skillsBaseUrl: config.skillsBaseUrl,\n });\n mcpServers['wizard-tools'] = wizardToolsServer;\n\n // audit-3000 needs Opus 4.7's depth for the multi-phase audit chain;\n // every other workflow runs on Sonnet 4.6.\n // Bare model IDs (no `anthropic/` prefix) so the LLM gateway's Bedrock\n // fallback can match map_to_bedrock_model()'s strict lookup.\n const model =\n config.integrationLabel === 'audit-3000'\n ? 'claude-opus-4-6'\n : 'claude-sonnet-4-6';\n\n const agentRunConfig: AgentRunConfig = {\n workingDirectory: config.workingDirectory,\n mcpServers,\n model,\n wizardFlags: config.wizardFlags,\n wizardMetadata: config.wizardMetadata,\n };\n\n logToFile('Agent config:', {\n workingDirectory: agentRunConfig.workingDirectory,\n posthogMcpUrl: config.posthogMcpUrl,\n gatewayUrl,\n apiKeyPresent: !!config.posthogApiKey,\n });\n\n if (options.debug) {\n debug('Agent config:', {\n workingDirectory: agentRunConfig.workingDirectory,\n posthogMcpUrl: config.posthogMcpUrl,\n gatewayUrl,\n apiKeyPresent: !!config.posthogApiKey,\n });\n }\n\n getUI().log.step(`Verbose logs: ${getLogFilePath()}`);\n getUI().log.success(\"Agent initialized. Let's get cooking!\");\n return agentRunConfig;\n } catch (error) {\n getUI().log.error(\n `Failed to initialize agent: ${(error as Error).message}`,\n );\n logToFile('Agent initialization error:', error);\n debug('Agent initialization error:', error);\n throw error;\n }\n}\n\n/**\n * Check agent output for YARA scanner violations.\n * Used in both the success and catch paths of runAgent.\n */\nfunction checkYaraViolation(\n outputText: string,\n spinner: SpinnerHandle,\n): { error: AgentErrorType } | null {\n if (\n outputText.includes('[YARA CRITICAL]') ||\n outputText.includes('[YARA] Scanner error')\n ) {\n logToFile('Agent error: YARA_VIOLATION');\n spinner.stop('Security violation detected');\n return { error: AgentErrorType.YARA_VIOLATION };\n }\n return null;\n}\n\n/**\n * Execute an agent with the provided prompt and options\n * Handles the full lifecycle: spinner, execution, error handling\n *\n * @returns An object containing any error detected in the agent's output\n */\nexport async function runAgent(\n agentConfig: AgentRunConfig,\n prompt: string,\n options: WizardOptions,\n spinner: SpinnerHandle,\n config?: {\n estimatedDurationMinutes?: number;\n spinnerMessage?: string;\n successMessage?: string;\n errorMessage?: string;\n additionalFeatureQueue?: readonly AdditionalFeature[];\n abortCases?: readonly AbortCaseMatcher[];\n },\n middleware?: {\n onMessage(message: any): void;\n finalize(resultMessage: any, totalDurationMs: number): any;\n },\n): Promise<{ error?: AgentErrorType; message?: string }> {\n const {\n spinnerMessage = 'Customizing your PostHog setup...',\n successMessage = 'PostHog integration complete',\n errorMessage = 'Integration failed',\n abortCases = [],\n } = config ?? {};\n\n const { query } = await getSDKModule();\n\n spinner.start(spinnerMessage);\n\n const cliPath = getClaudeCodeExecutablePath();\n logToFile('Starting agent run');\n logToFile('Claude Code executable:', cliPath);\n logToFile('Prompt:', prompt);\n\n const startTime = Date.now();\n const collectedText: string[] = [];\n // Track if we received a successful result (before any cleanup errors)\n let receivedSuccessResult = false;\n let loggedInitialContext = false;\n let lastResultMessage: any = null;\n\n // Workaround for SDK bug: stdin closes before canUseTool responses can be sent.\n // The fix is to use an async generator for the prompt that stays open until\n // the result is received, keeping the stdin stream alive for permission responses.\n // See: https://github.com/anthropics/claude-code/issues/4775\n // See: https://github.com/anthropics/claude-agent-sdk-typescript/issues/41\n let signalDone: () => void;\n const resultReceived = new Promise<void>((resolve) => {\n signalDone = resolve;\n });\n\n const createPromptStream = async function* () {\n yield {\n type: 'user',\n session_id: '',\n message: { role: 'user', content: prompt },\n parent_tool_use_id: null,\n };\n await resultReceived;\n };\n\n // Helper to handle successful completion (used in normal path and race condition recovery)\n const completeWithSuccess = (\n suppressedError?: Error,\n ): { error?: AgentErrorType; message?: string } => {\n const durationMs = Date.now() - startTime;\n const durationSeconds = Math.round(durationMs / 1000);\n\n if (suppressedError) {\n logToFile(\n `Ignoring post-completion error, agent completed successfully in ${durationSeconds}s`,\n );\n logToFile('Suppressed error:', suppressedError.message);\n } else {\n logToFile(`Agent run completed in ${durationSeconds}s`);\n }\n\n // Extract and capture the agent's reflection on the run\n const outputText = collectedText.join('\\n');\n const remarkRegex = new RegExp(\n `${AgentSignals.WIZARD_REMARK.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n '\\\\$&',\n )}\\\\s*(.+?)(?:\\\\n|$)`,\n 's',\n );\n const remarkMatch = outputText.match(remarkRegex);\n if (remarkMatch && remarkMatch[1]) {\n const remark = remarkMatch[1].trim();\n if (remark) {\n analytics.capture(WIZARD_REMARK_EVENT_NAME, { remark });\n }\n }\n\n analytics.wizardCapture('agent completed', {\n duration_ms: durationMs,\n duration_seconds: durationSeconds,\n });\n try {\n middleware?.finalize(lastResultMessage, durationMs);\n } catch (e) {\n logToFile(`${AgentSignals.BENCHMARK} Middleware finalize error:`, e);\n }\n spinner.stop(successMessage);\n return {};\n };\n\n // Abort controller — lets us force-kill the SDK query when we detect an\n // [ABORT] signal in the agent's output. Also stashes the reason so the\n // runner can surface it via outroData after we unwind.\n const abortController = new AbortController();\n let abortReason: string | null = null;\n\n try {\n // Tools needed for the wizard:\n // - File operations: Read, Write, Edit\n // - Search: Glob, Grep\n // - Commands: Bash (with restrictions via canUseTool)\n // - MCP discovery: ListMcpResourcesTool (to find available skills)\n // - Skills: Skill (to load installed PostHog skills)\n // MCP tools (PostHog) come from mcpServers, not allowedTools\n const allowedTools = [\n 'Read',\n 'Write',\n 'Edit',\n 'Glob',\n 'Grep',\n 'Bash',\n 'Task',\n 'ListMcpResourcesTool',\n 'Skill',\n ...WIZARD_TOOL_NAMES,\n ];\n\n const response = query({\n prompt: createPromptStream(),\n options: {\n abortController,\n model: agentConfig.model,\n cwd: agentConfig.workingDirectory,\n permissionMode: 'acceptEdits',\n betas: ['context-1m-2025-08-07'],\n mcpServers: agentConfig.mcpServers,\n // Load skills from project's .claude/skills/ directory\n settingSources: ['project'],\n // Explicitly enable required tools including Skill\n allowedTools,\n sandbox: {\n enabled: true,\n allowUnsandboxedCommands: false,\n filesystem: {\n allowWrite: [\n '/' + agentConfig.workingDirectory,\n '/' + agentConfig.workingDirectory + '/**',\n '//tmp',\n '//tmp/**',\n '//private/tmp',\n '//private/tmp/**',\n // Package manager stores — allow writes so pnpm/npm can\n // install packages without breaking the user's existing setup\n '~/Library/pnpm/store/**', // pnpm global store (macOS)\n '~/.local/share/pnpm/store/**', // pnpm global store (Linux)\n '~/.pnpm-store/**', // pnpm alternate store\n '~/.npm/**', // npm cache\n '~/.yarn/**', // yarn classic cache\n '~/.yarn/berry/**', // yarn berry cache\n ],\n },\n network: {\n allowedDomains: [\n 'github.com',\n 'api.github.com',\n 'raw.githubusercontent.com',\n 'release-assets.githubusercontent.com',\n 'objects.githubusercontent.com',\n ],\n },\n },\n env: {\n ...process.env,\n // Prevent user's Anthropic API key from overriding the wizard's OAuth token\n ANTHROPIC_API_KEY: undefined,\n // Defer MCP tool schemas to avoid bloating the system prompt.\n // The posthog-wizard MCP exposes many query tools with large schemas;\n // without deferral these consume ~113k tokens upfront, leaving\n // almost no room in Sonnet's 200k context window.\n ENABLE_TOOL_SEARCH: 'auto:0',\n ANTHROPIC_CUSTOM_HEADERS: buildAgentEnv(\n agentConfig.wizardMetadata ?? {},\n agentConfig.wizardFlags ?? {},\n ),\n },\n canUseTool: (toolName: string, input: unknown) => {\n logToFile('canUseTool called:', { toolName, input });\n const result = wizardCanUseTool(\n toolName,\n input as Record<string, unknown>,\n );\n logToFile('canUseTool result:', result);\n return Promise.resolve(result);\n },\n systemPrompt: {\n type: 'preset',\n preset: 'claude_code',\n // Append wizard-wide commandments rather than replacing\n // the preset so we keep default Claude Code behaviors.\n append: getWizardCommandments(),\n },\n tools: { type: 'preset', preset: 'claude_code' },\n // Capture stderr from CLI subprocess for debugging\n stderr: (data: string) => {\n logToFile('CLI stderr:', data);\n if (options.debug) {\n debug('CLI stderr:', data);\n }\n },\n // Stop hook: drain additional feature queue, then collect remark, then allow stop\n hooks: {\n PreToolUse: createPreToolUseYaraHooks(),\n PostToolUse: createPostToolUseYaraHooks(),\n Stop: [\n {\n hooks: [\n createStopHook(\n config?.additionalFeatureQueue ?? [],\n collectedText,\n ),\n ],\n timeout: 30,\n },\n ],\n },\n },\n });\n\n // Process the async generator\n for await (const message of response) {\n // Log initial context size on the first assistant response so we can\n // detect sudden shifts in starting context (e.g. MCP schema bloat).\n if (!loggedInitialContext && message.type === 'assistant') {\n const usage = message.message?.usage as\n | {\n input_tokens?: number;\n cache_creation_input_tokens?: number;\n cache_read_input_tokens?: number;\n }\n | undefined;\n if (usage) {\n const input = usage.input_tokens ?? 0;\n const cacheCreation = usage.cache_creation_input_tokens ?? 0;\n const cacheRead = usage.cache_read_input_tokens ?? 0;\n const initialTokens = input + cacheCreation + cacheRead;\n logToFile(\n `Initial context: ${initialTokens} tokens (input=${input}, cache_creation=${cacheCreation}, cache_read=${cacheRead})`,\n );\n analytics.wizardCapture('agent initial context', {\n initial_tokens: initialTokens,\n input_tokens: input,\n cache_creation_input_tokens: cacheCreation,\n cache_read_input_tokens: cacheRead,\n });\n }\n loggedInitialContext = true;\n }\n\n // Pass receivedSuccessResult so handleSDKMessage can suppress user-facing error\n // output for post-success cleanup errors while still logging them to file\n handleSDKMessage(\n message,\n options,\n spinner,\n collectedText,\n receivedSuccessResult,\n );\n\n // [ABORT] detection: the skill emits \"[ABORT] <reason>\" when it\n // cannot complete the workflow. Kill the SDK query immediately —\n // the prompt doesn't need to cooperate with \"and exit\" because the\n // abort is enforced here. The reason is surfaced via the returned\n // AgentErrorType.ABORT so the runner can render a custom screen.\n if (\n abortCases.length > 0 &&\n !abortReason &&\n message.type === 'assistant'\n ) {\n const content = message.message?.content;\n if (Array.isArray(content)) {\n for (const block of content) {\n if (block.type === 'text' && typeof block.text === 'string') {\n const match = block.text.match(/\\[ABORT\\]\\s*(.+?)(?:\\n|$)/);\n if (match) {\n abortReason = match[1].trim();\n logToFile(`Agent emitted [ABORT]: ${abortReason}`);\n abortController.abort();\n signalDone!();\n break;\n }\n }\n }\n }\n }\n\n // 401: show auth error screen and exit immediately\n if (\n message.type === 'assistant' &&\n collectedText.join('\\n').includes('API Error: 401')\n ) {\n signalDone!();\n spinner.stop('Authentication failed');\n logToFile('Agent error: 401, showing auth error screen');\n getUI().showAuthError();\n await wizardAbort({\n message: 'Authentication failed (401)',\n error: new WizardError('Authentication failed'),\n });\n }\n\n try {\n middleware?.onMessage(message);\n } catch (e) {\n logToFile(`${AgentSignals.BENCHMARK} Middleware onMessage error:`, e);\n }\n\n // Signal completion when result received\n if (message.type === 'result') {\n // Track successful results before any potential cleanup errors\n // The SDK may emit a second error result during cleanup due to a race condition\n if (message.subtype === 'success' && !message.is_error) {\n receivedSuccessResult = true;\n lastResultMessage = message;\n }\n signalDone!();\n }\n }\n\n // If the middleware caught an [ABORT] and aborted the SDK query, surface\n // it as a structured error before checking other signals.\n if (abortReason) {\n spinner.stop('Wizard aborted');\n return { error: AgentErrorType.ABORT, message: abortReason };\n }\n\n const outputText = collectedText.join('\\n');\n\n // Check for YARA scanner violations\n const yaraResult = checkYaraViolation(outputText, spinner);\n if (yaraResult) return yaraResult;\n\n // Check for error markers in the agent's output\n if (outputText.includes(AgentSignals.ERROR_MCP_MISSING)) {\n logToFile('Agent error: MCP_MISSING');\n spinner.stop('Agent could not access PostHog MCP');\n return { error: AgentErrorType.MCP_MISSING };\n }\n\n if (outputText.includes(AgentSignals.ERROR_RESOURCE_MISSING)) {\n logToFile('Agent error: RESOURCE_MISSING');\n spinner.stop('Agent could not access setup resource');\n return { error: AgentErrorType.RESOURCE_MISSING };\n }\n\n // Check for API errors (rate limits, etc.)\n // Extract just the API error line(s), not the entire output\n const apiErrorMatch = outputText.match(/API Error: [^\\n]+/g);\n const apiErrorMessage = apiErrorMatch\n ? apiErrorMatch.join('\\n')\n : 'Unknown API error';\n\n if (outputText.includes('API Error: 429')) {\n logToFile('Agent error: RATE_LIMIT');\n spinner.stop('Rate limit exceeded');\n return { error: AgentErrorType.RATE_LIMIT, message: apiErrorMessage };\n }\n\n if (outputText.includes('API Error:')) {\n logToFile('Agent error: API_ERROR');\n spinner.stop('API error occurred');\n return { error: AgentErrorType.API_ERROR, message: apiErrorMessage };\n }\n\n return completeWithSuccess();\n } catch (error) {\n // Signal done to unblock the async generator\n signalDone!();\n\n // If the middleware caught an [ABORT] and triggered abortController.abort(),\n // the SDK will throw an AbortError — surface it as a clean abort result.\n if (abortReason) {\n spinner.stop('Wizard aborted');\n return { error: AgentErrorType.ABORT, message: abortReason };\n }\n\n // If we already received a successful result, the error is from SDK cleanup\n // This happens due to a race condition: the SDK tries to send a cleanup command\n // after the prompt stream closes, but streaming mode is still active.\n // See: https://github.com/anthropics/claude-agent-sdk-typescript/issues/41\n if (receivedSuccessResult) {\n return completeWithSuccess(error as Error);\n }\n\n // Check if we collected an error before the exception was thrown\n const outputText = collectedText.join('\\n');\n\n // Check for YARA scanner violations\n const yaraResult = checkYaraViolation(outputText, spinner);\n if (yaraResult) return yaraResult;\n\n // Extract just the API error line(s), not the entire output\n const apiErrorMatch = outputText.match(/API Error: [^\\n]+/g);\n const apiErrorMessage = apiErrorMatch\n ? apiErrorMatch.join('\\n')\n : 'Unknown API error';\n\n if (outputText.includes('API Error: 429')) {\n logToFile('Agent error (caught): RATE_LIMIT');\n spinner.stop('Rate limit exceeded');\n return { error: AgentErrorType.RATE_LIMIT, message: apiErrorMessage };\n }\n\n if (outputText.includes('API Error:')) {\n logToFile('Agent error (caught): API_ERROR');\n spinner.stop('API error occurred');\n return { error: AgentErrorType.API_ERROR, message: apiErrorMessage };\n }\n\n // No API error found, re-throw the original exception\n spinner.stop(errorMessage);\n getUI().log.error(`Error: ${(error as Error).message}`);\n logToFile('Agent run failed:', error);\n debug('Full error:', error);\n throw error;\n } finally {\n // Always capture run duration, even on abort/error, so we can alert on\n // long runs where the user gave up before completion.\n if (!receivedSuccessResult) {\n const durationMs = Date.now() - startTime;\n analytics.wizardCapture('agent aborted', {\n duration_ms: durationMs,\n duration_seconds: Math.round(durationMs / 1000),\n });\n }\n }\n}\n\n/**\n * Handle SDK messages and provide user feedback\n *\n * @param receivedSuccessResult - If true, suppress user-facing error output for cleanup errors\n * while still logging to file. The SDK may emit a second error\n * result after success due to cleanup race conditions.\n */\nfunction handleSDKMessage(\n message: SDKMessage,\n options: WizardOptions,\n spinner: SpinnerHandle,\n collectedText: string[],\n receivedSuccessResult = false,\n): void {\n logToFile(`SDK Message: ${message.type}`, JSON.stringify(message, null, 2));\n\n if (options.debug) {\n debug(`SDK Message type: ${message.type}`);\n }\n\n switch (message.type) {\n case 'assistant': {\n // Extract text content from assistant messages\n const content = message.message?.content;\n if (Array.isArray(content)) {\n for (const block of content) {\n if (block.type === 'text' && typeof block.text === 'string') {\n collectedText.push(block.text);\n\n // Check for [STATUS] markers\n const statusRegex = new RegExp(\n `^.*${AgentSignals.STATUS.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n '\\\\$&',\n )}\\\\s*(.+?)$`,\n 'm',\n );\n const statusMatch = block.text.match(statusRegex);\n if (statusMatch) {\n const statusText = statusMatch[1].trim();\n getUI().pushStatus(statusText);\n spinner.message(statusText);\n }\n }\n\n // Intercept TodoWrite tool_use blocks for task progression\n if (\n block.type === 'tool_use' &&\n block.name === 'TodoWrite' &&\n block.input?.todos &&\n Array.isArray(block.input.todos)\n ) {\n getUI().syncTodos(block.input.todos);\n }\n }\n }\n break;\n }\n\n case 'result': {\n // Check is_error flag - can be true even when subtype is 'success'\n if (message.is_error) {\n logToFile('Agent result with error:', message.result);\n if (typeof message.result === 'string') {\n collectedText.push(message.result);\n }\n // Only show errors to user if we haven't already succeeded.\n // Post-success errors are SDK cleanup noise (telemetry failures, streaming\n // mode race conditions). Full message already logged above via JSON dump.\n if (message.errors && !receivedSuccessResult) {\n for (const err of message.errors) {\n getUI().log.error(`Error: ${err}`);\n logToFile('ERROR:', err);\n }\n }\n } else if (message.subtype === 'success') {\n logToFile('Agent completed successfully');\n if (typeof message.result === 'string') {\n collectedText.push(message.result);\n }\n } else {\n logToFile('Agent result with error:', message.result);\n // Error result - only show to user if we haven't already succeeded.\n // Full message already logged above via JSON dump.\n if (message.errors && !receivedSuccessResult) {\n for (const err of message.errors) {\n getUI().log.error(`Error: ${err}`);\n logToFile('ERROR:', err);\n }\n }\n }\n break;\n }\n\n case 'system': {\n if (message.subtype === 'init') {\n logToFile('Agent session initialized', {\n model: message.model,\n tools: message.tools?.length,\n mcpServers: message.mcp_servers,\n });\n }\n break;\n }\n\n default:\n // Log other message types for debugging\n if (options.debug) {\n debug(`Unhandled message type: ${message.type}`);\n }\n break;\n }\n}\n"],"mappings":"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AAc,MAAM;CAAQ,CACtC;AAmQD,MAAa,QAAoB;CA/PK;EACpC,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;GAER;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAGA;GACA;GACA;GACA;GAEA;GACA;GACD;EACF;CAEuC;EACtC,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;GAER;GAEA;GAEA;GACA;GACA;GACD;EACF;CAEsC;EACrC,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;GACR;GACA;GACA;GACA;GACA;GACD;EACF;CAIwC;EACvC,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU,CAAC,6CAA6C;EACzD;CAE4C;EAC3C,MAAM;EACN,aAAa;EACb,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU,CACR,yCACA,uCACD;EACF;CAEmC;EAClC,MAAM;EACN,aAAa;EACb,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU,CAGR,6CACA,oCACD;EACF;CAIkD;EACjD,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;GAER;GACA;GACA;GACA;GAEA;GACA;GACD;EACF;CAEkD;EACjD,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;GAER;GACA;GACA;GACA;GACA;GAEA;GACA;GAGA;GACD;EACF;CAEyC;EACxC,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU,CAGR,8CACD;EACF;CAIiD;EAChD,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;GAER;GACA;GAEA;GACA;GACA;GACA;GAEA;GAEA;GAEA;GACA;GACD;EACF;CAIgC;EAC/B,MAAM;EACN,aAAa;EACb,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;GAER;GAEA;GACA;GACD;EACF;CAEgC;EAC/B,MAAM;E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"}