@posthog/wizard 2.23.0 → 2.24.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.
- package/README.md +61 -2
- package/dist/{AiOptInRequiredScreen-BOMyYFep.js → AiOptInRequiredScreen-_33FOcVo.js} +148 -685
- package/dist/AiOptInRequiredScreen-_33FOcVo.js.map +1 -0
- package/dist/{add-mcp-server-to-clients-BEziI3z9.js → add-mcp-server-to-clients-CfwEQT_z.js} +4 -4
- package/dist/{add-mcp-server-to-clients-BEziI3z9.js.map → add-mcp-server-to-clients-CfwEQT_z.js.map} +1 -1
- package/dist/{agent-interface-DjMPlXl0.js → agent-interface-D1vtN6Wn.js} +6 -7
- package/dist/agent-interface-D1vtN6Wn.js.map +1 -0
- package/dist/{agent-runner-Bv-7z-pQ.js → agent-runner-CBbkS0Ro.js} +8 -8
- package/dist/{agent-runner-Bv-7z-pQ.js.map → agent-runner-CBbkS0Ro.js.map} +1 -1
- package/dist/{analytics-9D4eGgmT.js → analytics-CUr82BDl.js} +11 -2
- package/dist/{analytics-9D4eGgmT.js.map → analytics-CUr82BDl.js.map} +1 -1
- package/dist/{api-Dwd0B-E9.js → api-CI3Z74NG.js} +3 -3
- package/dist/{api-Dwd0B-E9.js.map → api-CI3Z74NG.js.map} +1 -1
- package/dist/bin.js +887 -465
- package/dist/bin.js.map +1 -1
- package/dist/{ci-install-DGXCpvKh.js → ci-install-D_kxNmbJ.js} +5 -5
- package/dist/{ci-install-DGXCpvKh.js.map → ci-install-D_kxNmbJ.js.map} +1 -1
- package/dist/{debug-CgT5MmVB.js → debug-DxA_f5QT.js} +2 -2
- package/dist/{debug-CgT5MmVB.js.map → debug-DxA_f5QT.js.map} +1 -1
- package/dist/{debug-DayHBBST.js → debug-zMvpNYb2.js} +1 -1
- package/dist/{environment-CI2pTYTG.js → environment-CyS37cmM.js} +3 -3
- package/dist/{environment-CI2pTYTG.js.map → environment-CyS37cmM.js.map} +1 -1
- package/dist/{interactive-D52p_opJ.js → interactive-CG6FFqSw.js} +3 -3
- package/dist/{interactive-D52p_opJ.js.map → interactive-CG6FFqSw.js.map} +1 -1
- package/dist/{mcp-prompt-streaming-tdoy9UJ2.js → mcp-prompt-streaming-DQz4FSb1.js} +4 -4
- package/dist/{mcp-prompt-streaming-tdoy9UJ2.js.map → mcp-prompt-streaming-DQz4FSb1.js.map} +1 -1
- package/dist/{non-interactive-6hadW20x.js → non-interactive-DWtHX3ZR.js} +2 -2
- package/dist/{non-interactive-6hadW20x.js.map → non-interactive-DWtHX3ZR.js.map} +1 -1
- package/dist/{package-manager-BI0J5E7t.js → package-manager-BWUS4CP0.js} +2 -2
- package/dist/{package-manager-BI0J5E7t.js.map → package-manager-BWUS4CP0.js.map} +1 -1
- package/dist/{playground-z4A5dHPv.js → playground-D7AhMMF5.js} +9 -20
- package/dist/playground-D7AhMMF5.js.map +1 -0
- package/dist/{posthog-integration-BWbZU9Xq.js → posthog-integration-DexZ2uHU.js} +18 -18
- package/dist/{posthog-integration-BWbZU9Xq.js.map → posthog-integration-DexZ2uHU.js.map} +1 -1
- package/dist/{provisioning-B30Be2NA.js → provisioning-9c-AQbsa.js} +3 -3
- package/dist/{provisioning-B30Be2NA.js.map → provisioning-9c-AQbsa.js.map} +1 -1
- package/dist/{registry-CD_DplSQ.js → registry-CO7JVZyE.js} +4 -4
- package/dist/{registry-CD_DplSQ.js.map → registry-CO7JVZyE.js.map} +1 -1
- package/dist/{setup-utils-Dwgkk8AQ.js → setup-utils-0U-_Md2G.js} +8 -8
- package/dist/{setup-utils-Dwgkk8AQ.js.map → setup-utils-0U-_Md2G.js.map} +1 -1
- package/dist/{start-tui-SLeEzlJs.js → start-tui-WNb3ET14.js} +206 -1205
- package/dist/start-tui-WNb3ET14.js.map +1 -0
- package/dist/{steps-B1gzyRkC.js → steps-BAUXDCC4.js} +6 -6
- package/dist/{steps-B1gzyRkC.js.map → steps-BAUXDCC4.js.map} +1 -1
- package/dist/{telemetry-5rkeTd2_.js → telemetry-ycqCpNPr.js} +3 -3
- package/dist/{telemetry-5rkeTd2_.js.map → telemetry-ycqCpNPr.js.map} +1 -1
- package/dist/{urls-Cb4SI9kf.js → urls-C8aJWvgh.js} +2 -2
- package/dist/{urls-Cb4SI9kf.js.map → urls-C8aJWvgh.js.map} +1 -1
- package/dist/{wizard-abort-DovHQa-j.js → wizard-abort-C6gRLxUE.js} +3 -3
- package/dist/{wizard-abort-DovHQa-j.js.map → wizard-abort-C6gRLxUE.js.map} +1 -1
- package/dist/{wizard-abort-DW2-ZjiS.js → wizard-abort-DWXyJdws.js} +1 -1
- package/package.json +1 -1
- package/dist/AiOptInRequiredScreen-BOMyYFep.js.map +0 -1
- package/dist/agent-interface-DjMPlXl0.js.map +0 -1
- package/dist/playground-z4A5dHPv.js.map +0 -1
- package/dist/start-tui-SLeEzlJs.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"agent-interface-DjMPlXl0.js","names":["_sdkModule","getSDKModule","fs"],"sources":["../src/lib/programs/audit/types.ts","../src/lib/secret-vault.ts","../src/lib/wizard-tools.ts","../src/utils/custom-headers.ts","../src/lib/safe-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/signals.ts","../src/lib/agent/output-signals.ts","../src/lib/agent/claude-settings.ts","../src/lib/agent/agent-interface.ts"],"sourcesContent":["import type { WizardSession } from '@lib/wizard-session';\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 * Session-scoped secret vault.\n *\n * Tools that handle sensitive values (personal API keys pasted by the user\n * or minted by the wizard, OAuth tokens, etc.) store them here and return\n * an opaque `secret:<uuid>` ref to the agent. The agent passes the ref\n * around to subsequent tools — `set_env_values` resolves it host-side\n * before writing — but the raw value never enters the LLM conversation.\n *\n * The vault is created once per `createWizardToolsServer` call and lives\n * for the duration of a single wizard run. There is no persistence and\n * no cross-session sharing; refs minted in one run cannot be resolved in\n * another.\n */\n\nimport { randomUUID } from 'crypto';\n\nconst REF_PREFIX = 'secret:';\n\nexport interface SecretMeta {\n /** Opaque reference handed to the agent. */\n ref: string;\n /** Human-readable label shown to the user (e.g. \"Personal API key\"). */\n label: string;\n /** Where the secret came from (e.g. \"wizard_ask\"). */\n source: string;\n /** ms epoch when the secret was stored. */\n createdAt: number;\n}\n\nexport interface SecretVault {\n /** Store a value and return its ref. */\n put(value: string, meta: Omit<SecretMeta, 'ref' | 'createdAt'>): string;\n /** Resolve a ref to its value, or undefined if unknown. */\n get(ref: string): string | undefined;\n /** Whether the vault knows about this ref. */\n has(ref: string): boolean;\n /** Metadata for every stored secret (never the values). */\n list(): SecretMeta[];\n /** Drop every secret. Call at session teardown. */\n clear(): void;\n}\n\n/** True when the value looks like a secret ref. Does not assert resolvability. */\nexport function isSecretRef(value: unknown): value is string {\n return typeof value === 'string' && value.startsWith(REF_PREFIX);\n}\n\nexport function createSecretVault(): SecretVault {\n const store = new Map<string, { value: string; meta: SecretMeta }>();\n\n return {\n put(value, meta) {\n const ref = `${REF_PREFIX}${randomUUID()}`;\n store.set(ref, {\n value,\n meta: {\n ref,\n label: meta.label,\n source: meta.source,\n createdAt: Date.now(),\n },\n });\n return ref;\n },\n get(ref) {\n return store.get(ref)?.value;\n },\n has(ref) {\n return store.has(ref);\n },\n list() {\n return [...store.values()].map((s) => s.meta);\n },\n clear() {\n store.clear();\n },\n };\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 { analytics } from '@utils/analytics';\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 './programs/audit/types';\nimport type { WizardAskBridge } from './wizard-ask-bridge';\nimport { createSecretVault, type SecretVault } from './secret-vault';\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. Programs 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 * Bridge that drives the `wizard_ask` overlay. When omitted, the\n * `wizard_ask` tool is still registered but returns an error explaining\n * the host is non-interactive — keeps the tool surface stable across\n * CI/dev environments.\n */\n askBridge?: WizardAskBridge;\n\n /**\n * Per-run cap on `wizard_ask` invocations. Defaults to {@link DEFAULT_ASK_MAX_QUESTIONS}.\n * The 4th call always returns a \"batch your questions\" error regardless\n * of this cap — see {@link ASK_BATCH_THRESHOLD}.\n */\n askMaxQuestions?: number;\n\n /**\n * Optional secret vault. When provided, tools that handle sensitive\n * values (wizard_ask with `sensitive: true`, set_env_values) route\n * those values through the vault and return opaque refs to the agent\n * instead of raw strings — so the LLM never sees them. When omitted\n * (e.g. in unit tests), a fresh vault is created internally.\n */\n secretVault?: SecretVault;\n}\n\n/** Default per-run cap on wizard_ask calls when no override is provided. */\nexport const DEFAULT_ASK_MAX_QUESTIONS = 10;\n/** Calls past this number always return a batch-it error. */\nexport const ASK_BATCH_THRESHOLD = 3;\n\nexport type AskCapDecision =\n | { kind: 'ok' }\n | {\n kind: 'capped';\n reason: 'max_questions' | 'adjacency';\n message: string;\n };\n\n/**\n * Pure decision function for the wizard_ask caps. Returns whether the\n * upcoming call should proceed and, if not, the error message to surface\n * to the agent. Extracted so the policy can be unit-tested without\n * spinning up an MCP server.\n */\nexport function evaluateAskCap(\n callCount: number,\n maxQuestions: number,\n): AskCapDecision {\n if (callCount >= maxQuestions) {\n return {\n kind: 'capped',\n reason: 'max_questions',\n message: `Error: wizard_ask cap reached (${maxQuestions} calls in this run). Proceed with sensible defaults using the answers you already have, or emit [ABORT] requirements-incomplete.`,\n };\n }\n if (callCount >= ASK_BATCH_THRESHOLD) {\n return {\n kind: 'capped',\n reason: 'adjacency',\n message: `Error: too many wizard_ask calls in a row (${callCount} so far). Batch the remaining questions into a single call — the schema accepts up to 8 questions per invocation.`,\n };\n }\n return { kind: 'ok' };\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 {\n workingDirectory,\n detectPackageManager,\n skillsBaseUrl,\n askBridge,\n askMaxQuestions = DEFAULT_ASK_MAX_QUESTIONS,\n secretVault = createSecretVault(),\n } = options;\n const sdk = await getSDKModule();\n const { tool, createSdkMcpServer } = sdk;\n\n // Per-server counter for wizard_ask call accounting (adjacency + total cap).\n let askCallCount = 0;\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. Each value can be either a literal string or a secret reference of the form `{ \"secretRef\": \"secret:...\" }` returned by another tool (e.g. wizard_ask). Secret references are resolved locally — the actual value is written to the file but never returned to the agent.',\n {\n filePath: z\n .string()\n .describe('Path to the .env file, relative to the project root'),\n values: z\n .record(\n z.string(),\n z.union([z.string(), z.object({ secretRef: z.string() })]),\n )\n .describe(\n 'Key → (literal string OR { secretRef } pointing to a vaulted secret)',\n ),\n },\n (args: {\n filePath: string;\n values: Record<string, string | { secretRef: string }>;\n }) => {\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 // Resolve any secret refs from the vault before writing.\n const resolvedValues: Record<string, string> = {};\n const resolvedRefKeys: string[] = [];\n for (const [key, val] of Object.entries(args.values)) {\n if (typeof val === 'string') {\n resolvedValues[key] = val;\n } else {\n const secret = secretVault.get(val.secretRef);\n if (secret === undefined) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: secret reference \"${val.secretRef}\" for key \"${key}\" is not known to the vault. The ref may have expired, been minted in a different run, or been mistyped.`,\n },\n ],\n isError: true,\n };\n }\n resolvedValues[key] = secret;\n resolvedRefKeys.push(key);\n }\n }\n\n const resolved = resolveEnvPath(workingDirectory, args.filePath);\n logToFile(\n `set_env_values: ${resolved}, keys: ${Object.keys(resolvedValues).join(\n ', ',\n )}${\n resolvedRefKeys.length > 0\n ? ` (secret refs: ${resolvedRefKeys.join(', ')})`\n : ''\n }`,\n );\n\n const existing = fs.existsSync(resolved)\n ? fs.readFileSync(resolved, 'utf8')\n : '';\n const content = mergeEnvValues(existing, resolvedValues);\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 // -- wizard_ask -----------------------------------------------------------\n\n const askQuestionSchema = z.object({\n id: z\n .string()\n .min(1)\n .describe('Stable key for the answer in the response map'),\n prompt: z.string().min(1).describe('Question text shown to the user'),\n kind: z\n .enum(['single', 'multi', 'text'])\n .describe(\n \"'single' = pick one option, 'multi' = pick any, 'text' = free-form single-line answer\",\n ),\n options: z\n .array(z.object({ label: z.string(), value: z.string() }))\n .optional()\n .describe('Required for kind=single|multi; ignored for kind=text'),\n required: z.boolean().optional().describe('Defaults to true'),\n sensitive: z\n .boolean()\n .optional()\n .describe(\n \"Only valid for kind='text'. When true, the user's answer is stored in the wizard's secret vault and returned to you as { secretRef: 'secret:...' } instead of the raw string. Use for API keys, tokens, and any other secret the user types in.\",\n ),\n });\n\n const wizardAsk = tool(\n 'wizard_ask',\n 'Ask the user one or more structured questions and wait for their answers. ' +\n 'Use this whenever you would otherwise inline a question in your text output. ' +\n 'Batch related questions into a single call — do not call this multiple times in a row.',\n {\n questions: z.array(askQuestionSchema).min(1).max(8),\n },\n async (args: {\n questions: Array<{\n id: string;\n prompt: string;\n kind: 'single' | 'multi' | 'text';\n options?: { label: string; value: string }[];\n required?: boolean;\n sensitive?: boolean;\n }>;\n }) => {\n if (!askBridge) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Error: wizard_ask is not available in this environment (CI / non-interactive). Proceed with sensible defaults or emit [ABORT] requirements-incomplete.',\n },\n ],\n isError: true,\n };\n }\n\n const capDecision = evaluateAskCap(askCallCount, askMaxQuestions);\n if (capDecision.kind === 'capped') {\n analytics.wizardCapture('wizard_ask capped', {\n reason: capDecision.reason,\n call_count: askCallCount,\n max_questions: askMaxQuestions,\n });\n return {\n content: [{ type: 'text' as const, text: capDecision.message }],\n isError: true,\n };\n }\n\n // Validate that single/multi questions include options. The schema\n // alone can't enforce a per-kind requirement.\n for (const q of args.questions) {\n if (\n (q.kind === 'single' || q.kind === 'multi') &&\n (!q.options || q.options.length === 0)\n ) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: question \"${q.id}\" has kind=\"${q.kind}\" but no options. Provide at least one { label, value } option, or change kind to \"text\".`,\n },\n ],\n isError: true,\n };\n }\n if (q.sensitive && q.kind !== 'text') {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: question \"${q.id}\" sets sensitive=true but kind=\"${q.kind}\". Only kind=\"text\" answers can be vaulted as secrets.`,\n },\n ],\n isError: true,\n };\n }\n }\n\n const ids = new Set<string>();\n for (const q of args.questions) {\n if (ids.has(q.id)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: duplicate question id \"${q.id}\". Each question must have a unique id.`,\n },\n ],\n isError: true,\n };\n }\n ids.add(q.id);\n }\n\n askCallCount += 1;\n\n try {\n const answers = await askBridge.request({ questions: args.questions });\n\n // For any question marked sensitive, move the raw answer into the\n // vault and replace it with an opaque ref before returning to the\n // agent — so the secret never enters the LLM conversation.\n const sensitiveById = new Map(\n args.questions\n .filter((q) => q.sensitive)\n .map((q) => [q.id, q.prompt]),\n );\n const sanitised: Record<\n string,\n string | string[] | { secretRef: string }\n > = {};\n for (const [id, answer] of Object.entries(answers)) {\n const label = sensitiveById.get(id);\n if (\n label !== undefined &&\n typeof answer === 'string' &&\n answer !== '__cancelled__'\n ) {\n const ref = secretVault.put(answer, {\n label,\n source: 'wizard_ask',\n });\n sanitised[id] = { secretRef: ref };\n logToFile(`wizard_ask: vaulted answer for \"${id}\" as ${ref}`);\n } else {\n sanitised[id] = answer;\n }\n }\n\n logToFile(\n `wizard_ask: resolved ${Object.keys(answers).length} answer(s) for ${\n args.questions.length\n } question(s)`,\n );\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ answers: sanitised }, null, 2),\n },\n ],\n };\n } catch (err: any) {\n logToFile(`wizard_ask: error: ${err?.message ?? err}`);\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: wizard_ask failed: ${err?.message ?? String(err)}`,\n },\n ],\n isError: true,\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 wizardAsk,\n ],\n });\n}\n\n/** Tool names exposed by the wizard-tools server, keyed for selective use. */\n// SDK expects MCP tool names in allowedTools/disallowedTools to be the\n// fully-qualified `mcp__<server>__<tool>` form (sdk.d.ts: \"Fully-qualified\n// MCP tool name, e.g. mcp__server__tool_name.\"). The colon form silently\n// fails to match, which made every program's `disallowedTools` entry a no-op.\nexport const WIZARD_TOOL_NAMES = {\n checkEnvKeys: `mcp__${SERVER_NAME}__check_env_keys`,\n setEnvValues: `mcp__${SERVER_NAME}__set_env_values`,\n detectPackageManager: `mcp__${SERVER_NAME}__detect_package_manager`,\n loadSkillMenu: `mcp__${SERVER_NAME}__load_skill_menu`,\n installSkill: `mcp__${SERVER_NAME}__install_skill`,\n auditSeedChecks: `mcp__${SERVER_NAME}__audit_seed_checks`,\n auditAddChecks: `mcp__${SERVER_NAME}__audit_add_checks`,\n auditResolveChecks: `mcp__${SERVER_NAME}__audit_resolve_checks`,\n wizardAsk: `mcp__${SERVER_NAME}__wizard_ask`,\n} as const;\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","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","/**\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// ─── Wizard-documentation allowlist ───────────────────────────────\n//\n// Files the wizard's own programs write to describe events the user's\n// codebase already captures, or events the integration program is\n// proposing to add. When the agent copies a literal\n// `posthog.capture('event', { email: ... })` snippet (or a property\n// list including PII-shaped keys) into one of these files, the\n// `pii_in_capture_call` rule (category: posthog_pii) fires even though\n// the wizard is documenting / planning, not introducing, the pattern.\n// Suppress posthog_pii matches on these paths only; every other rule\n// (secrets, prompt injection, supply chain, destructive ops) still\n// fires normally so the file cannot be used as a smuggling vector for\n// actual violations.\n\nconst WIZARD_DOC_BASENAMES = new Set([\n // events-audit\n '.posthog-events-inventory.json',\n 'posthog-events-audit-report.md',\n // doctor (audit)\n 'posthog-audit-report.md',\n // posthog-integration event plan\n '.posthog-events.json',\n]);\n\nconst WIZARD_DOC_PATTERNS: RegExp[] = [\n // events-audit subagent part-files (e.g. `.posthog-events-inventory.part-3.json`)\n /^\\.posthog-events-inventory\\.part-\\d+\\.json$/,\n];\n\nfunction isWizardDocumentationPath(filePath: string | undefined): boolean {\n if (!filePath) return false;\n const basename = path.basename(filePath);\n if (WIZARD_DOC_BASENAMES.has(basename)) return true;\n return WIZARD_DOC_PATTERNS.some((re) => re.test(basename));\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 // Wizard-documentation paths: suppress posthog_pii matches that\n // come from the agent verbatim-copying the user's existing\n // capture calls into an inventory / report, or planning new\n // events with PII-shaped property keys. Every other category\n // still triggers the revert.\n const filePath = toolInput?.file_path as string | undefined;\n if (isWizardDocumentationPath(filePath)) {\n const nonPiiMatches = result.matches.filter(\n (m) => m.rule.category !== 'posthog_pii',\n );\n if (nonPiiMatches.length === 0) {\n logToFile(\n `[YARA] posthog_pii match suppressed on wizard doc ${path.basename(\n filePath ?? '',\n )} (rule: ${result.matches[0]?.rule.name})`,\n );\n return Promise.resolve({});\n }\n // Some non-PII rule also fired — fall through and revert on\n // that one. Replace the matches set so the user sees the\n // actually-actionable rule, not the suppressed PII one.\n result.matches = nonPiiMatches;\n }\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 'Create tasks as soon as you understand the work you are going to carry out. Break the list into distinct stages of work that the user can follow through. Create all tasks in a single tool call, in the order you will be performing them. Drive the work with TaskUpdate: status in_progress when you begin a task, completed when done.',\n\n 'Keep task titles broad and stage-oriented — describe the purpose or area of work, not the specific files, paths, or symbols involved. Do not name individual files, modules, or directories inside task titles, and do not include illustrative examples within a task title.',\n\n 'Do not assume \"PostHog provider\" or \"PostHogProvider\" in task 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\n // wizard_ask Path A — translate prose question lists into a single tool call.\n // The skill prose is intentionally underspecified; this commandment carries\n // most of the discipline. Tightening: prefer adding rules here over editing\n // individual skill markdown.\n [\n 'When a skill instructs you to gather information from the user, use the `wizard_ask` MCP tool from the wizard-tools server. Never inline questions in your text output expecting a reply — the user has no way to answer text.',\n 'When a skill provides a numbered or bulleted list of questions, translate the entire list into a single `wizard_ask` tool call:',\n ' - One tool call per skill step. Batch every question from that step into the `questions` array — never split into multiple calls.',\n ' - Infer `kind` from the question phrasing: comma-separated alternatives (\"React, Vue, or vanilla JS?\") → `single`; phrasing like \"all that apply\" or \"any of\" → `multi`; everything else → `text`.',\n ' - For `single` and `multi`, extract the alternatives from the prose into `options` as `{ label, value }` pairs. Use the human phrase as `label` and a lowercase-hyphenated form as `value` (e.g., `label: \"Vanilla JS\"`, `value: \"vanilla-js\"`).',\n ' - Use a kebab-case slug of the question label as `id` (e.g., \"Tech stack\" → `tech-stack`, \"Show frequency\" → `show-frequency`).',\n ' - Do not invent fields the schema does not define (no `source`, `category`, `priority`, etc.) — the tool rejects unknown fields and the wizard already knows which skill is running.',\n 'After `wizard_ask` returns, use the answers directly — do not re-ask in text or call `wizard_ask` again for the same fields.',\n ].join('\\n'),\n].join('\\n');\n\nexport function getWizardCommandments(): string {\n return WIZARD_COMMANDMENTS;\n}\n","/**\n * Agent signal vocabulary — the marker strings the agent emits and the error\n * taxonomy the runner returns. Kept as a dependency-free leaf module so both\n * `agent-interface.ts` and `output-signals.ts` can import it without a cycle.\n */\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 program and is\n * aborting intentionally (distinct from errors). Format: \"[ABORT] <reason>\".\n * Programs 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 /**\n * Signal emitted when the agent has created a PostHog dashboard for the\n * user. Format: `[DASHBOARD_URL] <full https url>`. The URL is captured\n * onto `session.dashboardUrl` and surfaced by programs in their outro.\n */\n DASHBOARD_URL: '[DASHBOARD_URL]',\n /**\n * Signal emitted when the agent has uploaded a report to a PostHog\n * notebook. Format: `[NOTEBOOK_URL] <full https url>`. The URL is captured\n * onto `session.notebookUrl` and surfaced by programs in their outro.\n */\n NOTEBOOK_URL: '[NOTEBOOK_URL]',\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 program (emitted [ABORT] <reason>) */\n ABORT = 'WIZARD_ABORT',\n}\n","/**\n * Parses the signal-bearing lines out of agent output and discards the rest.\n *\n * The agent and SDK communicate non-content events (auth/API errors, YARA\n * violations, missing MCP/resource, the end-of-run remark) by emitting marker\n * strings inside their prose. `AgentOutputSignals` keeps only the lines that\n * carry such a marker, so the buffer stays bounded regardless of run length.\n */\n\nimport { AgentSignals } from './signals';\n\n/**\n * Single source of truth for the substrings runAgent scans agent output for.\n * `push()` retains a line iff it contains one of these values; every query\n * reads the same table, so retention and consumers cannot drift. API-error\n * status codes are not separate entries — `API_ERROR` is the one needle and\n * the code is a parameter to `hasApiErrorStatus`.\n */\nconst OUTPUT_SIGNALS = {\n API_ERROR: 'API Error:',\n YARA_CRITICAL: '[YARA CRITICAL]',\n YARA_SCANNER_ERROR: '[YARA] Scanner error',\n MCP_MISSING: AgentSignals.ERROR_MCP_MISSING,\n RESOURCE_MISSING: AgentSignals.ERROR_RESOURCE_MISSING,\n WIZARD_REMARK: AgentSignals.WIZARD_REMARK,\n} as const;\n\ntype OutputSignal = keyof typeof OUTPUT_SIGNALS;\nconst SIGNAL_NEEDLES = Object.values(OUTPUT_SIGNALS);\n\nexport class AgentOutputSignals {\n private readonly lines: string[] = [];\n\n /** Parse step: keep the line only if it carries a known signal; drop prose. */\n push(text: string): void {\n if (SIGNAL_NEEDLES.some((n) => text.includes(n))) this.lines.push(text);\n }\n\n private get text(): string {\n return this.lines.join('\\n');\n }\n\n /** True if any retained line contains the given signal's marker. */\n has(signal: OutputSignal): boolean {\n return this.text.includes(OUTPUT_SIGNALS[signal]);\n }\n\n hasApiError(): boolean {\n return this.has('API_ERROR');\n }\n\n /** True for a specific HTTP status, e.g. 401 (auth) or 429 (rate limit). */\n hasApiErrorStatus(code: number): boolean {\n return this.text.includes(`${OUTPUT_SIGNALS.API_ERROR} ${code}`);\n }\n\n hasYaraViolation(): boolean {\n return this.has('YARA_CRITICAL') || this.has('YARA_SCANNER_ERROR');\n }\n\n /** Joined `API Error: …` lines for the user-facing message, or undefined. */\n apiErrorMessage(): string | undefined {\n const m = this.text.match(\n new RegExp(`${OUTPUT_SIGNALS.API_ERROR} [^\\\\n]+`, 'g'),\n );\n return m ? m.join('\\n') : undefined;\n }\n\n /** Text after the single `[WIZARD-REMARK]` marker, trimmed, or undefined. */\n remark(): string | undefined {\n const re = new RegExp(\n `${OUTPUT_SIGNALS.WIZARD_REMARK.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n '\\\\$&',\n )}\\\\s*(.+?)(?:\\\\n|$)`,\n 's',\n );\n return this.text.match(re)?.[1]?.trim() || undefined;\n }\n}\n","/**\n * Claude Code settings handling: conflict detection, backup/restore, and\n * orphan recovery.\n *\n * Lives apart from agent-interface so bin.ts can run orphan recovery at\n * process start without dragging in the agent stack (wizard-tools, yara\n * hooks, the SDK loader).\n */\n\nimport path from 'path';\nimport * as fs from 'fs';\nimport * as os from 'os';\nimport { analytics } from '@utils/analytics';\nimport { registerCleanup } from '@utils/wizard-abort';\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 =\n | 'project'\n | 'project-local'\n | 'user'\n | 'managed';\n\n/** A single settings conflict detected during startup. */\nexport interface SettingsConflict {\n /** Where the conflict was found. */\n source: SettingsConflictSource;\n /** Absolute path of the file holding the override. */\n path: string;\n /** The blocking keys found (e.g. 'ANTHROPIC_BASE_URL', 'apiKeyHelper'). */\n keys: string[];\n /**\n * Whether the wizard can back up / remove this file. Only the project\n * `settings.json` is writable: managed settings are root-owned, and the\n * user's global config and gitignored `*.local.json` are left untouched so\n * we never mutate config outside the project we were pointed at.\n */\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 every settings file Claude Code reads for blocking keys that conflict\n * with the wizard's gateway auth: org-managed, the user's global config, the\n * project config, and the gitignored project-local override. Each is a place\n * an `apiKeyHelper` or `ANTHROPIC_*` override commonly lives, and any of them\n * can outrank the env the wizard sets and make every agent call 401.\n */\nexport function checkAllSettingsConflicts(\n workingDirectory: string,\n homeDir: string = os.homedir(),\n): SettingsConflict[] {\n const conflicts: SettingsConflict[] = [];\n const home = homeDir;\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: 'user',\n paths: [\n path.join(home, '.claude', 'settings.json'),\n path.join(home, '.claude', 'settings.local.json'),\n ],\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 source: 'project-local',\n paths: [path.join(workingDirectory, '.claude', 'settings.local.json')],\n writable: false,\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, path: filePath, 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 * Ensure `.claude/.gitignore` excludes `*.wizard-backup` so a backup left\n * orphaned by an interrupted run can't get committed to git — settings files\n * commonly hold `apiKeyHelper` or `ANTHROPIC_API_KEY`, which we don't want\n * leaking into a repo.\n */\nfunction ensureBackupGitignored(claudeDir: string): void {\n const gitignorePath = path.join(claudeDir, '.gitignore');\n const entry = '*.wizard-backup';\n try {\n const existing = fs.existsSync(gitignorePath)\n ? fs.readFileSync(gitignorePath, 'utf-8')\n : '';\n const lines = existing.split('\\n').map((l) => l.trim());\n if (lines.includes(entry)) return;\n const sep = existing && !existing.endsWith('\\n') ? '\\n' : '';\n fs.writeFileSync(gitignorePath, `${existing}${sep}${entry}\\n`);\n } catch (error) {\n analytics.captureException(error);\n }\n}\n\n/**\n * Copy .claude/settings.json to .wizard-backup, then remove the original so\n * the SDK doesn't load the blocking overrides. Restored at outro and on\n * cleanup.\n */\nexport function backupAndFixClaudeSettings(workingDirectory: string): boolean {\n for (const name of ['settings.json', 'settings']) {\n const filePath = path.join(workingDirectory, '.claude', name);\n if (!fs.existsSync(filePath)) continue;\n const backupPath = `${filePath}.wizard-backup`;\n try {\n // Don't clobber a backup an earlier run already wrote — it holds the\n // pristine original. recoverOrphanedSettingsBackups resolves cross-run\n // orphans at process start, so a backup still present at this point\n // is from the current process and the current file is the modified one.\n if (!fs.existsSync(backupPath)) {\n fs.copyFileSync(filePath, backupPath);\n // Mirror the source's mode so a 0600 settings.json (apiKeyHelper,\n // API keys) doesn't become a 0644 backup readable by other local\n // users. copyFileSync would otherwise apply default umask.\n const mode = fs.statSync(filePath).mode & 0o777;\n fs.chmodSync(backupPath, mode);\n }\n ensureBackupGitignored(path.join(workingDirectory, '.claude'));\n fs.unlinkSync(filePath);\n analytics.wizardCapture('backedup-claude-settings');\n registerCleanup(() => {\n try {\n restoreClaudeSettings(workingDirectory);\n } catch (error) {\n analytics.captureException(error);\n }\n });\n return true;\n } catch (error) {\n analytics.captureException(error);\n }\n }\n return false;\n}\n\n/**\n * Restore .claude/settings.json from .wizard-backup and remove the backup.\n *\n * Runs on every outro and cleanup, including runs that never had a settings\n * conflict (and so never wrote a backup). A missing backup is the normal,\n * expected state for those runs, so skip it silently — only report a genuine\n * copy failure. Removing the backup after a successful restore keeps repeat\n * calls (outro then cleanup) idempotent and avoids leaving an orphan behind.\n */\nexport function restoreClaudeSettings(workingDirectory: string): void {\n for (const name of ['settings.json', 'settings']) {\n const original = path.join(workingDirectory, '.claude', name);\n const backup = `${original}.wizard-backup`;\n if (!fs.existsSync(backup)) continue;\n try {\n fs.copyFileSync(backup, original);\n fs.rmSync(backup, { force: true });\n analytics.wizardCapture('restored-claude-settings');\n return;\n } catch (error) {\n analytics.captureException(error);\n }\n }\n}\n\n/**\n * Restore an orphaned settings backup left by a previous interrupted run.\n *\n * If a run is killed after backupAndFixClaudeSettings moved the original\n * aside but before restore ran, the user is left with no settings.json and a\n * stray .wizard-backup. The next run's conflict check then sees no file and\n * skips restore, so the user's settings stay silently gone. Detect that exact\n * state (backup present, original absent) and put the original back.\n *\n * Runs once per process, from bin.ts, before anything reads Claude settings —\n * conflict detection must see the user's real settings file, and a recovery\n * any later would resurface the conflicting overrides after the backup step\n * already moved them aside.\n */\nexport function recoverOrphanedSettingsBackups(workingDirectory: string): void {\n for (const name of ['settings.json', 'settings']) {\n const original = path.join(workingDirectory, '.claude', name);\n const backup = `${original}.wizard-backup`;\n if (!fs.existsSync(backup) || fs.existsSync(original)) continue;\n try {\n fs.copyFileSync(backup, original);\n fs.rmSync(backup, { force: true });\n analytics.wizardCapture('recovered-orphaned-settings');\n } catch (error) {\n analytics.captureException(error);\n }\n }\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 os from 'os';\nimport { createRequire } from 'node:module';\nimport { getUI, type SpinnerHandle } from '@ui';\nimport { debug, logToFile, initLogFile, getLogFilePath } from '@utils/debug';\nimport type { WizardRunOptions } 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 '@lib/constants';\nimport {\n type AdditionalFeature,\n ADDITIONAL_FEATURE_PROMPTS,\n} from '@lib/wizard-session';\nimport { wizardAbort, WizardError } from '@utils/wizard-abort';\nimport { createCustomHeaders } from '@utils/custom-headers';\nimport { getLlmGatewayUrlFromHost } from '@utils/urls';\nimport { LINTING_TOOLS } from '@lib/safe-tools';\nimport { createWizardToolsServer, WIZARD_TOOL_NAMES } from '@lib/wizard-tools';\nimport {\n createPreToolUseYaraHooks,\n createPostToolUseYaraHooks,\n} from '@lib/yara-hooks';\nimport { getWizardCommandments } from './commandments';\nimport type { PackageManagerDetector } from '@lib/detection/package-manager';\nimport { AgentSignals, AgentErrorType } from './signals';\nimport { AgentOutputSignals } from './output-signals';\n\n// Signal vocabulary and the output parser live in dedicated modules; re-export\n// so existing importers of these from agent-interface keep working.\nexport { AgentSignals, AgentErrorType } from './signals';\nexport type { AgentSignal } from './signals';\nexport { AgentOutputSignals } from './output-signals';\nimport {\n checkAllSettingsConflicts,\n type SettingsConflict,\n type SettingsConflictSource,\n} from './claude-settings';\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 // Bare `require` is undefined in ESM (tsx dev runs) — fall back to createRequire.\n const resolver =\n typeof require !== 'undefined'\n ? require\n : createRequire(process.argv[1] ?? `${process.cwd()}/`);\n // resolve finds the package's main entry, then we get cli.js from same dir\n const sdkPackagePath = resolver.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\n/** Region implied by the resolved gateway URL, for telemetry and display. */\nfunction regionFromGatewayUrl(gatewayUrl: string): 'eu' | 'us' | 'local' {\n if (gatewayUrl.includes('localhost')) return 'local';\n return gatewayUrl.includes('gateway.eu.') ? 'eu' : 'us';\n}\n\n/**\n * Diagnostic context for a gateway 401, used by the auth-error screen and the\n * captured exception.\n *\n * A bare \"Authentication failed\" can't be triaged: the 401 could be a settings\n * file overriding the credential, a region mismatch, or a rejected key. This\n * records which, so the screen can name an actionable next step and telemetry\n * can tell the causes apart. Absolute conflict paths stay out of the telemetry\n * payload (they contain the user's home dir) — only sources/keys are reported.\n */\nexport interface AuthErrorContext {\n hasSettingsConflict: boolean;\n conflicts: SettingsConflict[];\n conflictSources: SettingsConflictSource[];\n conflictKeys: string[];\n gatewayUrl: string;\n region: 'eu' | 'us' | 'local';\n}\n\nexport function buildAuthErrorContext(\n workingDirectory: string,\n gatewayUrl: string,\n homeDir: string = os.homedir(),\n): AuthErrorContext {\n const conflicts = checkAllSettingsConflicts(workingDirectory, homeDir);\n return {\n hasSettingsConflict: conflicts.length > 0,\n conflicts,\n conflictSources: conflicts.map((c) => c.source),\n conflictKeys: [...new Set(conflicts.flatMap((c) => c.keys))],\n gatewayUrl,\n region: regionFromGatewayUrl(gatewayUrl),\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 /** Program identifier — selects the model for that program. */\n integrationLabel?: string;\n /** Bridge that drives the `wizard_ask` overlay. Omit in non-interactive hosts. */\n askBridge?: import('@lib/wizard-ask-bridge').WizardAskBridge;\n /** Per-run cap on `wizard_ask` invocations. Defaults to 10. */\n askMaxQuestions?: number;\n /** Extra tools added on top of BASE_ALLOWED_TOOLS for this run. */\n allowedTools?: readonly string[];\n /** Tools removed from BASE_ALLOWED_TOOLS for this run. */\n disallowedTools?: readonly string[];\n /**\n * Read accessor for the active pending question. Used by canUseTool to\n * block Write/Edit while the overlay is open (defense in depth).\n */\n getPendingQuestion?: () =>\n | import('@lib/wizard-session').PendingQuestion\n | null;\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 signals?: AgentOutputSignals,\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 (signals?.hasApiError()) {\n logToFile('Stop hook: API error detected, allowing immediate stop');\n return {};\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 /** Extra tools added on top of BASE_ALLOWED_TOOLS for this run. */\n allowedTools?: readonly string[];\n /** Tools removed from BASE_ALLOWED_TOOLS for this run. */\n disallowedTools?: readonly string[];\n /**\n * Read accessor for the active pending question. canUseTool reads this\n * to block Write/Edit while the overlay is open.\n */\n getPendingQuestion?: () =>\n | import('@lib/wizard-session').PendingQuestion\n | null;\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 */\nexport function 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 '@lib/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 *\n * `wizardAskPending` is true while a wizard_ask overlay is open — when set,\n * Write/Edit calls are denied as a defense-in-depth measure against a\n * misbehaving agent that races to mutate files before the question is\n * answered. The SDK's tool-result protocol already pauses the agent here;\n * this guard is a belt-and-suspenders second line.\n */\nexport function wizardCanUseTool(\n toolName: string,\n input: Record<string, unknown>,\n context: {\n wizardAskPending?: boolean;\n disallowedTools?: readonly string[];\n } = {},\n):\n | { behavior: 'allow'; updatedInput: Record<string, unknown> }\n | { behavior: 'deny'; message: string } {\n // Hard gate on the program's disallow list. The SDK's own disallowedTools\n // option blocks tools at the parent level, but does NOT reliably propagate\n // to dispatched subagents (their AgentDefinition has its own field which the\n // SDK appears to ignore for MCP tools). canUseTool is invoked for every\n // tool call regardless of which agent layer emitted it, so denying here is\n // the only certain block.\n if (context.disallowedTools?.includes(toolName)) {\n logToFile(`Denying disallowed tool: ${toolName}`);\n return {\n behavior: 'deny',\n message: `Tool ${toolName} is disabled for this program.`,\n };\n }\n\n if (\n context.wizardAskPending &&\n (toolName === 'Write' || toolName === 'Edit')\n ) {\n logToFile(`Denying ${toolName} while wizard_ask overlay is open`);\n return {\n behavior: 'deny',\n message: `${toolName} is paused while a wizard_ask question is open. Wait for the user's answer to come back as a tool result before writing files.`,\n };\n }\n\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: WizardRunOptions,\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 logToFile(\n 'API key prefix:',\n config.posthogApiKey\n ? `${config.posthogApiKey.slice(0, 4)}***`\n : '(missing)',\n );\n const initConflicts = checkAllSettingsConflicts(options.installDir);\n logToFile(\n 'Settings conflicts at agent init:',\n initConflicts.length > 0\n ? initConflicts\n .map((c) => `${c.source}(${c.keys.join(',')})`)\n .join('; ')\n : 'none',\n );\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 askBridge: config.askBridge,\n askMaxQuestions: config.askMaxQuestions,\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 program 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 allowedTools: config.allowedTools,\n disallowedTools: config.disallowedTools,\n getPendingQuestion: config.getPendingQuestion,\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 signals: AgentOutputSignals,\n spinner: SpinnerHandle,\n): { error: AgentErrorType } | null {\n if (signals.hasYaraViolation()) {\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: WizardRunOptions,\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 logToFile('Starting agent run');\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 signals = new AgentOutputSignals();\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 // SDK >=0.3.142 replaced TodoWrite (snapshot) with TaskCreate/TaskUpdate (accumulate by id).\n // The agent's TaskCreate tool_use doesn't know the assigned taskId — the SDK returns it\n // in the matching tool_result. Keep one map keyed by whatever id we have right now:\n // tool_use_id while pending, then rekeyed to taskId once the result arrives.\n const tasks = new Map<string, TaskEntry>();\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 remark = signals.remark();\n if (remark) {\n analytics.capture(WIZARD_REMARK_EVENT_NAME, { remark });\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 // Per-program allow/disallow lists tweak BASE_ALLOWED_TOOLS. Skills are\n // enabled via the `skills` query option; PostHog MCP tools come through\n // `mcpServers`. Neither belongs in this list.\n const disallow = new Set(agentConfig.disallowedTools ?? []);\n const allowedTools = [\n ...BASE_ALLOWED_TOOLS,\n ...(agentConfig.allowedTools ?? []),\n ].filter((t) => !disallow.has(t));\n\n // Subagents dispatched via the Agent tool don't inherit the parent's\n // MCP servers by default — so general-purpose subagents can't see the\n // PostHog MCP and fall back to curl with whatever key they can find\n // (then 401, then start asking the user for keys). Override\n // general-purpose to forward parent MCP servers by name; SDK resolves\n // each string against the parent's mcpServers map.\n const inheritedMcpServerNames = Object.keys(agentConfig.mcpServers);\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 agents: {\n 'general-purpose': {\n description:\n \"General-purpose subagent. Inherits the parent run's tools plus the PostHog and wizard-tools MCP servers, so it can call mcp__posthog-wizard__* directly instead of curling the REST API.\",\n prompt:\n 'You are a general-purpose subagent for the PostHog wizard. Prefer the authenticated mcp__posthog-wizard__* MCP tools over raw HTTP — they are already authenticated for this project. Only fall back to other transports if no MCP tool covers the operation.',\n mcpServers: inheritedMcpServerNames,\n // SDK does not propagate the parent's disallowedTools to subagents\n // (sdk.d.ts: AgentDefinition has its own disallowedTools, and\n // `tools: undefined` means \"inherit all\"). Without this, a program\n // that disallows wizard_ask still leaks it to dispatched subagents.\n disallowedTools: agentConfig.disallowedTools\n ? [...agentConfig.disallowedTools]\n : undefined,\n },\n },\n // Load skills from project's .claude/skills/ directory\n settingSources: ['project'],\n // Enable all discovered skills. Omitting this is NOT \"skills off\" —\n // it just means no SDK auto-config — so we set 'all' explicitly to\n // preserve the prior behavior where 'Skill' in allowedTools exposed\n // everything under .claude/skills/. (SDK ≥0.2.133 deprecates passing\n // 'Skill' in allowedTools in favor of this option.)\n skills: 'all',\n allowedTools,\n sandbox: {\n enabled: true,\n // SDK 0.2.91 made failIfUnavailable default to true when enabled is\n // set, which would abort wizard runs on hosts that lack sandbox\n // dependencies (e.g. Linux without bubblewrap). Wizard targets a\n // broad set of user machines, so prefer graceful degradation —\n // commands still respect allowUnsandboxedCommands below.\n failIfUnavailable: false,\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 and toolchain installs — allow writes\n // so pnpm/npm/yarn/bun and version managers (corepack, volta)\n // can install packages and self-update without breaking the\n // user's existing setup.\n '~/Library/pnpm/**', // pnpm root (macOS) — store + .tools/ for packageManager pinning\n '~/.local/share/pnpm/**', // pnpm root (Linux)\n '~/.pnpm-store/**', // pnpm alternate store\n '~/.npm/**', // npm cache (covers _npx too)\n '~/.yarn/**', // yarn classic + berry cache\n '~/.bun/install/**', // bun cache + global installs\n '~/.cache/node/corepack/**', // corepack version downloads (Linux/macOS)\n '~/Library/Caches/node/corepack/**', // corepack on older macOS layouts\n '~/.volta/**', // Volta toolchain (referenced by workbench package.json)\n // Python — used by django/flask/fastapi wizards\n '~/.cache/pip/**',\n '~/Library/Caches/pip/**',\n '~/.cache/uv/**',\n '~/Library/Caches/uv/**',\n '~/.cache/pypoetry/**',\n '~/Library/Caches/pypoetry/**',\n // Ruby — used by rails wizard\n '~/.bundle/**',\n '~/.gem/**',\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 // SDK 0.3.142 made MCP servers connect in the background by default;\n // the agent may start its first turn before posthog-wizard is ready\n // (audit programs call audit_seed_checks on turn 1, integration\n // programs call load_skill_menu / install_skill). Restore the prior\n // blocking behavior so the SDK waits up to 5s for MCP connect before\n // turn 1. `alwaysLoad: true` on the server would also work but it\n // disables tool search deferral and re-inflates the system prompt by\n // ~113k tokens (the reason ENABLE_TOOL_SEARCH=auto:0 is set above).\n MCP_CONNECTION_NONBLOCKING: '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 wizardAskPending: agentConfig.getPendingQuestion?.() != null,\n disallowedTools: agentConfig.disallowedTools,\n },\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(config?.additionalFeatureQueue ?? [], signals),\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 signals,\n receivedSuccessResult,\n tasks,\n );\n\n // [ABORT] detection: the skill emits \"[ABORT] <reason>\" when it\n // cannot complete the program. 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 (message.type === 'assistant' && signals.hasApiErrorStatus(401)) {\n signalDone!();\n spinner.stop('Authentication failed');\n // Re-check at error time: a settings conflict can be the *real* cause\n // of a 401, distinct from bad PAT / wrong region / expired key.\n // Only the conflict case warrants telling the user to log out of\n // Claude Code.\n const authError = buildAuthErrorContext(\n options.installDir,\n process.env.ANTHROPIC_BASE_URL ?? '',\n );\n logToFile('Agent error: 401, showing auth error screen', authError);\n getUI().showAuthError({\n hasSettingsConflict: authError.hasSettingsConflict,\n conflicts: authError.conflicts,\n logFilePath: getLogFilePath(),\n });\n await wizardAbort({\n message: 'Authentication failed (401)',\n error: new WizardError('Authentication failed', {\n hasSettingsConflict: authError.hasSettingsConflict,\n conflictSources: authError.conflictSources,\n conflictKeys: authError.conflictKeys,\n gatewayUrl: authError.gatewayUrl,\n region: authError.region,\n }),\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 // Check for YARA scanner violations\n const yaraResult = checkYaraViolation(signals, spinner);\n if (yaraResult) return yaraResult;\n\n // Check for error markers in the agent's output\n if (signals.has('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 (signals.has('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 // A clean success result already arrived. The Claude SDK can emit a second\n // error result during teardown (e.g. \"API Error: The socket connection was\n // closed unexpectedly\" when the streaming connection drops on cleanup),\n // whose text lands in `signals` — so the API-error checks below would\n // escalate that teardown noise to a fatal error. A finished run is\n // finished; mirror the catch-path guard and complete successfully.\n if (receivedSuccessResult) {\n return completeWithSuccess();\n }\n\n // Check for API errors (rate limits, etc.)\n // Surface just the API error line(s), not the entire output\n const apiErrorMessage = signals.apiErrorMessage() ?? 'Unknown API error';\n\n if (signals.hasApiErrorStatus(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 (signals.hasApiError()) {\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\n // Check for YARA scanner violations\n const yaraResult = checkYaraViolation(signals, spinner);\n if (yaraResult) return yaraResult;\n\n // Surface just the API error line(s), not the entire output\n const apiErrorMessage = signals.apiErrorMessage() ?? 'Unknown API error';\n\n if (signals.hasApiErrorStatus(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 (signals.hasApiError()) {\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 */\n/**\n * SDK >=0.3.142 replaced TodoWrite with four discrete Task* tools.\n * Create / Update mutate the task list; Get / List are read-only.\n */\nexport enum TaskTool {\n Create = 'TaskCreate',\n Update = 'TaskUpdate',\n Get = 'TaskGet',\n List = 'TaskList',\n}\n\n/**\n * Tools every program gets unless its ProgramConfig.disallowedTools says\n * otherwise. Programs add more via ProgramConfig.allowedTools (e.g.\n * `'Agent'` to opt into subagent dispatch). Skills and PostHog MCP tools\n * are enabled separately (skills option / mcpServers).\n */\nexport const BASE_ALLOWED_TOOLS: readonly string[] = [\n 'Read',\n 'Write',\n 'Edit',\n 'Glob',\n 'Grep',\n 'Bash',\n // Task list tools (replaced TodoWrite in 0.3.142). Commandments instruct\n // the agent to call TaskCreate/TaskUpdate to surface progress in the TUI.\n ...Object.values(TaskTool),\n 'ListMcpResourcesTool',\n ...Object.values(WIZARD_TOOL_NAMES),\n];\n\ntype TaskEntry = { content: string; status: string; activeForm?: string };\n\ninterface TaskStore {\n tasks: Map<string, TaskEntry>;\n sync: () => void;\n}\n\ninterface ToolUseBlock {\n type: 'tool_use';\n id: string;\n name: string;\n input?: unknown;\n}\n\nfunction handleTaskCreate(block: ToolUseBlock, store: TaskStore): void {\n const input = block.input as\n | { subject?: string; activeForm?: string }\n | undefined;\n if (!input?.subject) return;\n // Key by tool_use_id for now — the rekey to the SDK-assigned taskId happens\n // when the matching tool_result arrives.\n store.tasks.set(block.id, {\n content: input.subject,\n status: 'pending',\n activeForm: input.activeForm,\n });\n store.sync();\n}\n\nfunction handleTaskUpdate(block: ToolUseBlock, store: TaskStore): void {\n const input = block.input as\n | {\n taskId?: string;\n subject?: string;\n status?: string;\n activeForm?: string;\n }\n | undefined;\n if (!input?.taskId) return;\n const existing = store.tasks.get(input.taskId);\n if (!existing) return;\n if (input.status === 'deleted') {\n store.tasks.delete(input.taskId);\n } else {\n store.tasks.set(input.taskId, {\n content: input.subject ?? existing.content,\n status: input.status ?? existing.status,\n activeForm: input.activeForm ?? existing.activeForm,\n });\n }\n store.sync();\n}\n\nfunction handleTaskGet(_block: ToolUseBlock, _store: TaskStore): void {\n // Read-only — the agent is querying state, not mutating it.\n}\n\nfunction handleTaskList(_block: ToolUseBlock, _store: TaskStore): void {\n // Read-only — the agent is querying state, not mutating it.\n}\n\nfunction dispatchTaskToolUse(block: ToolUseBlock, store: TaskStore): void {\n switch (block.name as TaskTool) {\n case TaskTool.Create:\n return handleTaskCreate(block, store);\n case TaskTool.Update:\n return handleTaskUpdate(block, store);\n case TaskTool.Get:\n return handleTaskGet(block, store);\n case TaskTool.List:\n return handleTaskList(block, store);\n }\n}\n\n/**\n * Pull the SDK-assigned task id off a SDKUserMessage.tool_use_result payload.\n * The SDK already deserialises TaskCreateOutput here, so the shape is the\n * structured `{ task: { id, subject } }` object — no JSON parsing needed.\n */\nfunction extractTaskIdFromToolResult(result: unknown): string | undefined {\n if (!result || typeof result !== 'object') return undefined;\n const obj = result as Record<string, unknown>;\n const task = obj.task as Record<string, unknown> | undefined;\n if (task && typeof task.id === 'string') return task.id;\n if (typeof obj.taskId === 'string') return obj.taskId;\n if (typeof obj.id === 'string') return obj.id;\n return undefined;\n}\n\n/**\n * Fallback id extractor for the inner tool_result block.content — used only\n * when message.tool_use_result is absent. In current SDK versions the inner\n * content is a human-readable string (\"Task #1 created successfully: …\") so\n * this path almost always returns undefined.\n */\nfunction extractTaskIdFromResult(content: unknown): string | undefined {\n const tryParse = (s: string): string | undefined => {\n try {\n const parsed = JSON.parse(s);\n const id = parsed?.task?.id ?? parsed?.taskId ?? parsed?.id;\n return typeof id === 'string' ? id : undefined;\n } catch {\n return undefined;\n }\n };\n if (typeof content === 'string') return tryParse(content);\n if (Array.isArray(content)) {\n for (const block of content) {\n if (block?.type === 'text' && typeof block.text === 'string') {\n const id = tryParse(block.text);\n if (id) return id;\n }\n }\n }\n return undefined;\n}\n\nfunction handleSDKMessage(\n message: SDKMessage,\n options: WizardRunOptions,\n spinner: SpinnerHandle,\n signals: AgentOutputSignals,\n receivedSuccessResult = false,\n tasks?: Map<string, TaskEntry>,\n): void {\n // Map preserves insertion order (the order the agent created the tasks).\n // Within that, group by status: completed first, then in_progress, then\n // everything else (pending). Array.prototype.sort is stable, so creation\n // order is preserved inside each group.\n const STATUS_RANK: Record<string, number> = {\n completed: 0,\n in_progress: 1,\n };\n const rank = (status: string): number => STATUS_RANK[status] ?? 2;\n const syncTasks = (): void => {\n if (!tasks) return;\n const sorted = Array.from(tasks.values()).sort(\n (a, b) => rank(a.status) - rank(b.status),\n );\n getUI().syncTodos(sorted);\n };\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 signals.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 // Check for [DASHBOARD_URL] markers\n const dashboardRegex = new RegExp(\n `${AgentSignals.DASHBOARD_URL.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n '\\\\$&',\n )}\\\\s*(\\\\S+)`,\n 'm',\n );\n const dashboardMatch = block.text.match(dashboardRegex);\n if (dashboardMatch) {\n getUI().setDashboardUrl(dashboardMatch[1].trim());\n }\n\n // Check for [NOTEBOOK_URL] markers\n const notebookRegex = new RegExp(\n `${AgentSignals.NOTEBOOK_URL.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n '\\\\$&',\n )}\\\\s*(\\\\S+)`,\n 'm',\n );\n const notebookMatch = block.text.match(notebookRegex);\n if (notebookMatch) {\n getUI().setNotebookUrl(notebookMatch[1].trim());\n }\n }\n\n // Intercept Task* tool_use blocks for task progression.\n // SDK >=0.3.142 replaced TodoWrite with TaskCreate/TaskUpdate/TaskGet/TaskList;\n // consumers must accumulate by task id rather than replacing a snapshot list.\n if (\n block.type === 'tool_use' &&\n tasks &&\n (Object.values(TaskTool) as string[]).includes(block.name)\n ) {\n dispatchTaskToolUse(block as ToolUseBlock, {\n tasks,\n sync: syncTasks,\n });\n }\n }\n }\n break;\n }\n\n case 'user': {\n // Rekey pending TaskCreate entries from tool_use_id to the SDK-assigned\n // taskId. The structured `{task: {id, subject}}` payload is at\n // message.tool_use_result (top-level); the inner content[].tool_result\n // blocks only carry the human-readable status text, which is not JSON.\n if (tasks && tasks.size > 0) {\n const content = message.message?.content;\n if (Array.isArray(content)) {\n for (const block of content) {\n if (\n block.type !== 'tool_result' ||\n typeof block.tool_use_id !== 'string' ||\n !tasks.has(block.tool_use_id)\n ) {\n continue;\n }\n const taskId =\n extractTaskIdFromToolResult(\n (message as { tool_use_result?: unknown }).tool_use_result,\n ) ?? extractTaskIdFromResult(block.content);\n // No taskId means we leave the entry under tool_use_id so it stays\n // visible; later TaskUpdate calls won't match it, but at least the\n // task list doesn't vanish.\n if (!taskId) continue;\n const entry = tasks.get(block.tool_use_id)!;\n tasks.delete(block.tool_use_id);\n tasks.set(taskId, entry);\n syncTasks();\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 signals.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 signals.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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A;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;EACN,aAAa;EACb,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU,CAAC,0BAA0B,sBAAsB;EAC5D;CAEgC;EAC/B,MAAM;EACN,aACE;EACF,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU,CAAC,uBAAuB;EACnC;CAIuC;EA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"}
|