@posthog/wizard 2.30.0 → 2.31.0
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 +35 -1
- package/dist/README.md +33 -0
- package/dist/{add-mcp-server-to-clients-D0G4cmPf.js → add-mcp-server-to-clients-BPBRx_Nz.js} +4 -4
- package/dist/{add-mcp-server-to-clients-D0G4cmPf.js.map → add-mcp-server-to-clients-BPBRx_Nz.js.map} +1 -1
- package/dist/{agent-interface-CYFyWCMj.js → agent-interface-DT_uyR45.js} +483 -416
- package/dist/agent-interface-DT_uyR45.js.map +1 -0
- package/dist/{agent-runner-HTfFCUrR.js → agent-runner-DfRka7f7.js} +39 -23
- package/dist/agent-runner-DfRka7f7.js.map +1 -0
- package/dist/{analytics-CdT0VV8s.js → analytics-3GR9OyE9.js} +14 -6
- package/dist/analytics-3GR9OyE9.js.map +1 -0
- package/dist/{api-D9CerM6x.js → api-Dmc76exl.js} +3 -3
- package/dist/{api-D9CerM6x.js.map → api-Dmc76exl.js.map} +1 -1
- package/dist/bin.js +32 -33
- package/dist/bin.js.map +1 -1
- package/dist/{ci-install-CTydrjHu.js → ci-install-QWrT_cW8.js} +4 -4
- package/dist/{ci-install-CTydrjHu.js.map → ci-install-QWrT_cW8.js.map} +1 -1
- package/dist/{debug-Bmq9KH4W.js → debug-aqoKImO6.js} +1 -1
- package/dist/{debug-D9giWww2.js → debug-n42RObru.js} +9 -10
- package/dist/debug-n42RObru.js.map +1 -0
- package/dist/{environment-C6j-a4Gz.js → environment-BKPsjOXk.js} +3 -3
- package/dist/{environment-C6j-a4Gz.js.map → environment-BKPsjOXk.js.map} +1 -1
- package/dist/{file-utils-BiElGS_N.js → file-utils-ALRqLr0x.js} +2 -2
- package/dist/{file-utils-BiElGS_N.js.map → file-utils-ALRqLr0x.js.map} +1 -1
- package/dist/{interactive-C0Vssetd.js → interactive-ChaxKwhe.js} +2 -2
- package/dist/{interactive-C0Vssetd.js.map → interactive-ChaxKwhe.js.map} +1 -1
- package/dist/{mcp-prompt-streaming-DQXxG2Pg.js → mcp-prompt-streaming-CNmYvvmk.js} +4 -4
- package/dist/{mcp-prompt-streaming-DQXxG2Pg.js.map → mcp-prompt-streaming-CNmYvvmk.js.map} +1 -1
- package/dist/{non-interactive-DX-N3ZEb.js → non-interactive-BM4hUmlI.js} +2 -2
- package/dist/{non-interactive-DX-N3ZEb.js.map → non-interactive-BM4hUmlI.js.map} +1 -1
- package/dist/{package-manager-cIPAT7g3.js → package-manager-l8N6VCPX.js} +2 -2
- package/dist/{package-manager-cIPAT7g3.js.map → package-manager-l8N6VCPX.js.map} +1 -1
- package/dist/{playground-DQI2vpr0.js → playground-C7SbDVI4.js} +4 -4
- package/dist/{playground-DQI2vpr0.js.map → playground-C7SbDVI4.js.map} +1 -1
- package/dist/{posthog-integration-EUokB9U1.js → posthog-integration-YDzQBfhq.js} +13 -14
- package/dist/{posthog-integration-EUokB9U1.js.map → posthog-integration-YDzQBfhq.js.map} +1 -1
- package/dist/{provisioning-BCCeBATw.js → provisioning-ql6mjOVq.js} +3 -3
- package/dist/{provisioning-BCCeBATw.js.map → provisioning-ql6mjOVq.js.map} +1 -1
- package/dist/{registry-DCxIW2G5.js → registry-5SphnyxS.js} +4 -4
- package/dist/{registry-DCxIW2G5.js.map → registry-5SphnyxS.js.map} +1 -1
- package/dist/{setup-utils-DmX3o2bT.js → setup-utils-CoblNeRY.js} +8 -8
- package/dist/{setup-utils-DmX3o2bT.js.map → setup-utils-CoblNeRY.js.map} +1 -1
- package/dist/{start-tui-B9dCp0hW.js → start-tui-D_woOYMc.js} +13 -13
- package/dist/{start-tui-B9dCp0hW.js.map → start-tui-D_woOYMc.js.map} +1 -1
- package/dist/{steps-3XbXMf0T.js → steps-2gR__rtG.js} +7 -7
- package/dist/{steps-3XbXMf0T.js.map → steps-2gR__rtG.js.map} +1 -1
- package/dist/{telemetry-GFq8wmz0.js → telemetry-CqysQT5U.js} +3 -3
- package/dist/{telemetry-GFq8wmz0.js.map → telemetry-CqysQT5U.js.map} +1 -1
- package/dist/{terminal-oI1dOWQI.js → terminal-CeokeMGP.js} +9 -9
- package/dist/{terminal-oI1dOWQI.js.map → terminal-CeokeMGP.js.map} +1 -1
- package/dist/{urls-93eQ-Rd0.js → urls-BOcViDhS.js} +2 -2
- package/dist/{urls-93eQ-Rd0.js.map → urls-BOcViDhS.js.map} +1 -1
- package/dist/{wizard-abort-dmkJqxAb.js → wizard-abort-C0siBgn5.js} +1 -1
- package/dist/{wizard-abort-BehJBPpy.js → wizard-abort-DFL5Um-M.js} +3 -3
- package/dist/{wizard-abort-BehJBPpy.js.map → wizard-abort-DFL5Um-M.js.map} +1 -1
- package/package.json +16 -52
- package/dist/agent-interface-CYFyWCMj.js.map +0 -1
- package/dist/agent-runner-HTfFCUrR.js.map +0 -1
- package/dist/analytics-CdT0VV8s.js.map +0 -1
- package/dist/debug-D9giWww2.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-interface-DT_uyR45.js","names":["path","_sdkModule","getSDKModule","EVENTS_AUDIT_REPORT_FILE","fs"],"sources":["../src/utils/atomic-ledger.ts","../src/lib/programs/audit/types.ts","../src/lib/secret-vault.ts","../src/lib/agent/runner/orchestrator/queue.ts","../src/lib/agent/runner/orchestrator/queue-tools.ts","../src/lib/wizard-tools.ts","../src/utils/custom-headers.ts","../src/lib/safe-tools.ts","../src/lib/skill-install.ts","../src/lib/programs/events-audit/constants.ts","../src/lib/programs/posthog-integration/constants.ts","../src/lib/yara-hooks.ts","../src/lib/agent/triage-provider.ts","../src/lib/agent/commandments.ts","../src/lib/agent/agent-phase.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":["/**\n * Small shared primitives for on-disk ledgers: an atomic JSON writer and a\n * single-chain async mutex. Used by the audit tools and by the orchestrator\n * queue. Lifted here so both share one implementation.\n */\nimport * as fs from 'fs';\n\n/**\n * Atomically write JSON: write to a `.tmp` file then rename over the target. The\n * rename bumps the file's mtime in one step, which is what a file watcher polls.\n */\nexport function writeJsonAtomic(targetPath: string, data: unknown): void {\n const tmpPath = `${targetPath}.tmp`;\n fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2), 'utf8');\n fs.renameSync(tmpPath, targetPath);\n}\n\n/**\n * A single async mutex. Serializes read-modify-write cycles so concurrent callers\n * (parallel task agents, audit tool calls) never interleave a mutation.\n */\nexport function 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","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 * The orchestrator task queue.\n *\n * In memory, synchronous, single-owner: one Node process drives the run, so\n * there is no locking. The queue imposes no execution policy — `nextRunnable`\n * returns every pending task whose dependencies are satisfied, and how many of\n * those run at once is decided by the task graph, not the queue.\n *\n * Every transition rewrites `<installDir>/.posthog-wizard-cache/queue.json`, a\n * small file holding the whole queue, handoffs included. It is the run's log\n * and the report's source. The whole cache folder is run-scoped and wiped when\n * the run ends.\n */\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { randomUUID } from 'crypto';\nimport { writeJsonAtomic } from '@utils/atomic-ledger';\nimport { analytics } from '@utils/analytics';\n\nexport const TaskStatus = {\n Pending: 'pending',\n Running: 'running',\n Done: 'done',\n Skipped: 'skipped',\n Failed: 'failed',\n} as const;\n\nexport type TaskStatus = (typeof TaskStatus)[keyof typeof TaskStatus];\n\nexport interface QueuedTask {\n id: string;\n type: string;\n /** Human-readable label for the TUI, set by the enqueuing agent. */\n label?: string;\n status: TaskStatus;\n /**\n * Ids of tasks that must finish before this one runs. Ids are generated at\n * enqueue and dependsOn is never mutated, so a task can only depend on tasks\n * created before it — the graph is a DAG by construction, cycles cannot\n * form. Unknown ids are rejected by the enqueue_task guard.\n */\n dependsOn: string[];\n inputs: Record<string, unknown>;\n model?: string;\n attempts: number;\n maxAttempts: number;\n /** The structured handoff the task reported on completion. */\n handoff?: TaskHandoff;\n /** 'orchestrator' for seeded tasks, or the id of the task that enqueued this one. */\n enqueuedBy: string;\n createdAt: string;\n startedAt?: string;\n finishedAt?: string;\n error?: { type: string; message: string };\n}\n\nexport interface QueueFile {\n version: 1;\n runId: string;\n tasks: QueuedTask[];\n}\n\n/** The structured handoff a task leaves for the next agent. */\nexport interface TaskHandoff {\n goals: string;\n did: string;\n forNextAgent: string;\n filesTouched?: string[];\n /** A one-line summary of any unresolved conflict, surfaced in the outro. */\n conflict?: string;\n}\n\nexport interface EnqueueInput {\n type: string;\n label?: string;\n inputs?: Record<string, unknown>;\n dependsOn?: string[];\n model?: string;\n maxAttempts?: number;\n enqueuedBy?: string;\n}\n\nexport const QUEUE_DIR_NAME = '.posthog-wizard-cache';\nconst DEFAULT_MAX_ATTEMPTS = 2;\n\nfunction nowIso(): string {\n return new Date().toISOString();\n}\n\n/** Dropped in the cache folder so an orphaned copy explains itself. */\nconst DELETE_ME_FILE = '.DELETE-ME.md';\nconst DELETE_ME_BODY = `# Safe to delete\n\nThis folder contains run artifacts from the PostHog Wizard. This should have\nbeen deleted if the Wizard has finished running. If this wasn't deleted for\nsome reason, you can safely delete the entire \\`${QUEUE_DIR_NAME}/\\` folder.\n`;\n\n/** Every queue transition, in the order it is reflected. */\nexport type TransitionEvent =\n | 'enqueue'\n | 'start'\n | 'complete'\n | 'skip'\n | 'fail'\n | 'requeue';\n\nexport interface QueueStoreOptions {\n /**\n * Called on every transition with the task's post-transition state. The\n * runner uses it for telemetry; the store itself stays analytics-free.\n * Listener errors are reported but cannot break a transition.\n */\n onTransition?: (event: TransitionEvent, task: QueuedTask) => void;\n}\n\nexport class QueueStore {\n private tasks: QueuedTask[] = [];\n private readonly onTransition?: (\n event: TransitionEvent,\n task: QueuedTask,\n ) => void;\n\n readonly runId: string;\n readonly queuePath: string;\n\n constructor(installDir: string, runId: string, opts?: QueueStoreOptions) {\n this.onTransition = opts?.onTransition;\n this.runId = runId;\n const dir = path.join(installDir, QUEUE_DIR_NAME);\n this.queuePath = path.join(dir, 'queue.json');\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(path.join(dir, DELETE_ME_FILE), DELETE_ME_BODY);\n }\n\n // ── Reads ───────────────────────────────────────────────────────────\n\n list(): readonly QueuedTask[] {\n return this.tasks;\n }\n\n get(id: string): QueuedTask | undefined {\n return this.tasks.find((t) => t.id === id);\n }\n\n /**\n * Every pending task whose dependencies are all satisfied (`done` or\n * `skipped`). A skipped dependency does not block downstream work.\n */\n nextRunnable(): QueuedTask[] {\n const doneIds = new Set(\n this.tasks\n .filter(\n (t) =>\n t.status === TaskStatus.Done || t.status === TaskStatus.Skipped,\n )\n .map((t) => t.id),\n );\n return this.tasks.filter(\n (t) =>\n t.status === TaskStatus.Pending &&\n t.dependsOn.every((d) => doneIds.has(d)),\n );\n }\n\n /**\n * True when no task is running and none can be started. Either everything\n * is terminal, or the only pending tasks are blocked by a failed dependency.\n */\n isDrained(): boolean {\n if (this.tasks.some((t) => t.status === TaskStatus.Running)) return false;\n return this.nextRunnable().length === 0;\n }\n\n summary(): Record<TaskStatus, number> & { total: number } {\n const counts: Record<TaskStatus, number> = {\n [TaskStatus.Pending]: 0,\n [TaskStatus.Running]: 0,\n [TaskStatus.Done]: 0,\n [TaskStatus.Skipped]: 0,\n [TaskStatus.Failed]: 0,\n };\n for (const t of this.tasks) counts[t.status] += 1;\n return { ...counts, total: this.tasks.length };\n }\n\n readHandoff(id: string): TaskHandoff | null {\n return this.get(id)?.handoff ?? null;\n }\n\n /** Handoffs of completed tasks of a given type, oldest first. */\n readHandoffsByType(type: string): TaskHandoff[] {\n return this.tasks\n .filter((t) => t.type === type && t.handoff)\n .map((t) => t.handoff as TaskHandoff);\n }\n\n // ── Transitions (each one reflected to queue.json) ──────────────────\n\n enqueue(input: EnqueueInput): QueuedTask {\n const task: QueuedTask = {\n id: randomUUID(),\n type: input.type,\n label: input.label,\n status: TaskStatus.Pending,\n dependsOn: input.dependsOn ?? [],\n inputs: input.inputs ?? {},\n model: input.model,\n attempts: 0,\n maxAttempts: input.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,\n enqueuedBy: input.enqueuedBy ?? 'orchestrator',\n createdAt: nowIso(),\n };\n this.tasks.push(task);\n this.reflect();\n this.notify('enqueue', task);\n return task;\n }\n\n start(id: string): QueuedTask {\n const t = this.require(id);\n t.status = TaskStatus.Running;\n t.startedAt = nowIso();\n t.attempts += 1;\n this.reflect();\n this.notify('start', t);\n return t;\n }\n\n complete(id: string, handoff?: TaskHandoff): QueuedTask {\n return this.finish(id, TaskStatus.Done, handoff);\n }\n\n /** Terminal: the agent could not do the task. Not done, not failed. */\n skip(id: string, handoff?: TaskHandoff): QueuedTask {\n return this.finish(id, TaskStatus.Skipped, handoff);\n }\n\n fail(\n id: string,\n error: { type: string; message: string },\n handoff?: TaskHandoff,\n ): QueuedTask {\n const t = this.require(id);\n t.error = error;\n return this.finish(id, TaskStatus.Failed, handoff);\n }\n\n /** Put a failed/running task back to pending for a retry within the run. */\n requeue(id: string): QueuedTask {\n const t = this.require(id);\n t.status = TaskStatus.Pending;\n t.startedAt = undefined;\n t.finishedAt = undefined;\n this.reflect();\n this.notify('requeue', t);\n return t;\n }\n\n // ── Internals ───────────────────────────────────────────────────────\n\n private finish(\n id: string,\n status: 'done' | 'skipped' | 'failed',\n handoff?: TaskHandoff,\n ): QueuedTask {\n const t = this.require(id);\n if (handoff) t.handoff = handoff;\n t.status = status;\n t.finishedAt = nowIso();\n this.reflect();\n this.notify(\n status === TaskStatus.Done\n ? 'complete'\n : status === TaskStatus.Skipped\n ? 'skip'\n : 'fail',\n t,\n );\n return t;\n }\n\n private reflect(): void {\n const file: QueueFile = {\n version: 1,\n runId: this.runId,\n tasks: this.tasks,\n };\n writeJsonAtomic(this.queuePath, file);\n }\n\n private notify(event: TransitionEvent, task: QueuedTask): void {\n try {\n this.onTransition?.(event, task);\n } catch (error) {\n // A listener must never break a transition, but its failure is a bug.\n analytics.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step: 'orchestrator_queue_listener', event },\n );\n }\n }\n\n private require(id: string): QueuedTask {\n const t = this.get(id);\n if (!t) throw new Error(`No task ${id} in the queue`);\n return t;\n }\n}\n","/**\n * Orchestrator MCP tools, registered into the existing `wizard-tools` server when\n * a queue is present. They let the orchestrator agent and task agents grow the\n * queue, report completion with a structured handoff, and read prior handoffs.\n *\n * The guard logic and the apply functions are plain, exported, and unit-tested.\n * `buildOrchestratorTools` wraps them in the SDK `tool()` shape.\n */\nimport { z } from 'zod';\nimport { analytics } from '@utils/analytics';\nimport {\n TaskStatus,\n type QueueStore,\n type QueuedTask,\n type TaskHandoff,\n} from './queue';\n\nexport interface OrchestratorToolsContext {\n store: QueueStore;\n /** Task types the registry knows about. enqueue_task rejects anything else. */\n validTypes: readonly string[];\n /**\n * The id of the task this tool server is bound to. Each task agent gets its\n * own wizard-tools server, so attribution holds when independent tasks run\n * in parallel. Absent for the seed, which is not a task.\n */\n currentTaskId?: string;\n}\n\nexport interface EnqueueArgs {\n type: string;\n label?: string;\n inputs?: Record<string, unknown>;\n dependsOn?: string[];\n model?: string;\n reason: string;\n}\n\nexport type GuardResult =\n | { ok: true }\n | { ok: false; guard: string; message: string };\n\nfunction stableStringify(value: unknown): string {\n if (value === null || typeof value !== 'object') return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;\n const entries = Object.entries(value as Record<string, unknown>).sort(\n ([a], [b]) => a.localeCompare(b),\n );\n return `{${entries\n .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`)\n .join(',')}}`;\n}\n\nfunction dedupKey(type: string, inputs: Record<string, unknown>): string {\n return `${type}::${stableStringify(inputs)}`;\n}\n\n/**\n * A backstop on total queue size. Tasks can enqueue tasks, so a misbehaving\n * type could grow the queue without bound. Keeping the graph small is the job\n * of good agent and skill design, not this number — it only stops a runaway.\n * The real flow is ~9 tasks, so this sits well clear of it.\n */\nconst MAX_QUEUE_TASKS = 30;\n\n/**\n * Validate an enqueue. Structural checks only — a real type, real dependencies,\n * not a literal duplicate, and not past the runaway backstop. How much runs,\n * and in what shape, is the task graph's business, not a knob's.\n */\nexport function checkEnqueueGuards(\n ctx: OrchestratorToolsContext,\n args: EnqueueArgs,\n): GuardResult {\n const tasks = ctx.store.list();\n\n if (tasks.length >= MAX_QUEUE_TASKS) {\n return {\n ok: false,\n guard: 'queue-full',\n message: `The queue already holds ${tasks.length} tasks (cap ${MAX_QUEUE_TASKS}). Refine the existing tasks rather than adding more.`,\n };\n }\n\n if (!ctx.validTypes.includes(args.type)) {\n return {\n ok: false,\n guard: 'unknown-type',\n message: `Unknown task type \"${\n args.type\n }\". Valid types: ${ctx.validTypes.join(', ')}.`,\n };\n }\n\n for (const dep of args.dependsOn ?? []) {\n if (!ctx.store.get(dep)) {\n return {\n ok: false,\n guard: 'unknown-dep',\n message: `Dependency \"${dep}\" is not a known task id.`,\n };\n }\n }\n\n const key = dedupKey(args.type, args.inputs ?? {});\n if (\n tasks.some(\n (t) =>\n t.status !== TaskStatus.Failed && dedupKey(t.type, t.inputs) === key,\n )\n ) {\n return {\n ok: false,\n guard: 'dedup',\n message: `A \"${args.type}\" task with these inputs already exists.`,\n };\n }\n\n return { ok: true };\n}\n\nexport type EnqueueResult =\n | { ok: true; task: QueuedTask }\n | { ok: false; guard: string; message: string };\n\nexport function applyEnqueue(\n ctx: OrchestratorToolsContext,\n args: EnqueueArgs,\n): EnqueueResult {\n const guard = checkEnqueueGuards(ctx, args);\n if (!guard.ok) return guard;\n\n const task = ctx.store.enqueue({\n type: args.type,\n label: args.label,\n inputs: args.inputs ?? {},\n dependsOn: args.dependsOn ?? [],\n model: args.model,\n enqueuedBy: ctx.currentTaskId ?? 'orchestrator',\n });\n return { ok: true, task };\n}\n\nexport type CompleteResult = { ok: true } | { ok: false; message: string };\n\nexport function applyComplete(\n ctx: OrchestratorToolsContext,\n args: { status: 'done' | 'failed' | 'skipped'; handoff: TaskHandoff },\n): CompleteResult {\n const id = ctx.currentTaskId;\n if (!id) {\n return {\n ok: false,\n message: 'complete_task can only be called by a running task agent.',\n };\n }\n if (args.status === TaskStatus.Failed) {\n ctx.store.fail(\n id,\n { type: 'self-reported', message: args.handoff.forNextAgent },\n args.handoff,\n );\n } else if (args.status === TaskStatus.Skipped) {\n ctx.store.skip(id, args.handoff);\n } else {\n ctx.store.complete(id, args.handoff);\n }\n return { ok: true };\n}\n\nexport function applyReadHandoffs(\n ctx: OrchestratorToolsContext,\n args: { type?: string; taskId?: string },\n): TaskHandoff[] {\n if (args.taskId) {\n const h = ctx.store.readHandoff(args.taskId);\n return h ? [h] : [];\n }\n if (args.type) {\n return ctx.store.readHandoffsByType(args.type);\n }\n // No filter: every handoff of a dependency of the current task.\n const currentId = ctx.currentTaskId;\n const current = currentId ? ctx.store.get(currentId) : undefined;\n if (!current) return [];\n return current.dependsOn\n .map((depId) => ctx.store.readHandoff(depId))\n .filter((h): h is TaskHandoff => h !== null);\n}\n\nconst HANDOFF_SHAPE = {\n goals: z.string().describe('What this task was asked to achieve.'),\n did: z.string().describe('What you actually did.'),\n forNextAgent: z.string().describe('What the next agent should know.'),\n filesTouched: z.array(z.string()).optional(),\n conflict: z\n .string()\n .optional()\n .describe(\n 'A one-line summary of any conflict you could not cleanly resolve (e.g. a dependency or build conflict). Put full detail in your work; this line is surfaced to the user.',\n ),\n};\n\ntype SdkTool = (\n name: string,\n description: string,\n // The SDK accepts a plain object of zod fields as the schema.\n schema: Record<string, z.ZodTypeAny>,\n handler: (args: never) => unknown,\n) => unknown;\n\nfunction textResult(text: string, isError = false) {\n return { isError, content: [{ type: 'text' as const, text }] };\n}\n\n/**\n * Build the orchestrator tools in the SDK `tool()` shape. Called from\n * createWizardToolsServer only when a queue context is present.\n */\nexport function buildOrchestratorTools(\n tool: SdkTool,\n ctx: OrchestratorToolsContext,\n): unknown[] {\n const enqueueTask = tool(\n 'enqueue_task',\n 'Add a task to the orchestrator queue. Use it to seed work and to enqueue follow-up work you discover. Keep tasks small and discrete.',\n {\n type: z\n .string()\n .describe(`The task type. One of: ${ctx.validTypes.join(', ')}.`),\n label: z\n .string()\n .optional()\n .describe(\n 'A short label for the UI — the action in a few words (e.g. \"Add the PostHog SDK\", \"Initialize PostHog\"). Leave out file names, class names, and other specifics.',\n ),\n inputs: z.record(z.unknown()).optional(),\n dependsOn: z\n .array(z.string())\n .optional()\n .describe('Task ids that must be done before this task runs.'),\n model: z.string().optional(),\n reason: z.string().describe('One line on why this task is needed.'),\n },\n ((args: EnqueueArgs) => {\n const res = applyEnqueue(ctx, args);\n if (!res.ok) {\n analytics.wizardCapture('orchestrator guard tripped', {\n guard: res.guard,\n type: args.type,\n });\n return textResult(res.message, true);\n }\n return textResult(JSON.stringify({ id: res.task.id }));\n }) as (args: never) => unknown,\n );\n\n const completeTask = tool(\n 'complete_task',\n \"Report the outcome of your task. Always call this exactly once when you finish, with a structured handoff for the next agent. Use status 'skipped' when the task does not apply to this project and you cannot do it (say why in the handoff) — not 'done'.\",\n {\n status: z.enum(['done', 'failed', 'skipped']),\n handoff: z.object(HANDOFF_SHAPE),\n },\n ((args: {\n status: 'done' | 'failed' | 'skipped';\n handoff: TaskHandoff;\n }) => {\n const res = applyComplete(ctx, args);\n if (!res.ok) return textResult(res.message, true);\n return textResult('ok');\n }) as (args: never) => unknown,\n );\n\n const readHandoffs = tool(\n 'read_handoffs',\n 'Read structured handoffs from earlier tasks. With no argument, returns the handoffs of your dependencies.',\n {\n type: z.string().optional(),\n taskId: z.string().optional(),\n },\n ((args: { type?: string; taskId?: string }) => {\n const handoffs = applyReadHandoffs(ctx, args);\n return textResult(JSON.stringify(handoffs, null, 2));\n }) as (args: never) => unknown,\n );\n\n return [enqueueTask, completeTask, readHandoffs];\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 { writeJsonAtomic, makeMutex } from '@utils/atomic-ledger';\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';\nimport {\n buildOrchestratorTools,\n type OrchestratorToolsContext,\n} from './agent/runner/orchestrator/queue-tools';\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\n/**\n * Entry in the wizard's runtime CLI registry. Mirrors the shape context-mill\n * publishes under `cliEntries` inside `skill-menu.json`. The wizard uses these\n * to register skill-backed subcommands at runtime instead of from a baked\n * build-time snapshot.\n */\nexport type CliEntry = {\n skillId: string;\n role: 'command' | 'skill' | 'internal';\n command?: string;\n parentCommand?: string;\n default?: boolean;\n displayName: string;\n description: string;\n};\n\nexport interface SkillMenu {\n categories: Record<string, SkillEntry[]>;\n /**\n * Skills exposed as CLI commands. Optional because context-mill releases\n * older than the runtime-resolver cutover don't emit this field.\n */\n cliEntries?: CliEntry[];\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 * Orchestrator queue context. Present only when the `wizard-orchestrator`\n * flag routes the run to the orchestrator; when set, the orchestrator tools\n * (enqueue_task, complete_task, read_handoffs) are registered. Absent on the\n * linear path.\n */\n orchestrator?: OrchestratorToolsContext;\n}\n\n/** Default per-run cap on wizard_ask calls when no override is provided. */\nexport const DEFAULT_ASK_MAX_QUESTIONS = 10;\n/** The call after this many returns a one-time batch-your-questions nudge. */\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 *\n * The adjacency nudge fires exactly once per run (the caller records it\n * via `adjacencyNudged`) — flows that legitimately need several\n * sequential, answer-dependent asks then proceed up to `maxQuestions`.\n * Without the flag the rejected call would never advance the counter and\n * every later call would be rejected, making caps above the threshold\n * unreachable.\n */\nexport function evaluateAskCap(\n callCount: number,\n maxQuestions: number,\n adjacencyNudged = false,\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 (!adjacencyNudged && 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. If the remaining questions truly depend on earlier answers, ask again and they will go through.`,\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/** Atomically write the audit ledger. Thin typed wrapper over writeJsonAtomic. */\nfunction writeLedgerAtomic(targetPath: string, checks: AuditCheck[]): void {\n writeJsonAtomic(targetPath, checks);\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// 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 orchestrator,\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 // The adjacency nudge fires once per run; after that only the total cap applies.\n let askAdjacencyNudged = false;\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(\n z.object({\n label: z.string(),\n value: z.string(),\n description: z\n .string()\n .optional()\n .describe(\n 'Optional secondary line shown dimmed and wrapped beneath the ' +\n 'label (multi-select only). Use when a choice needs more than a ' +\n 'title — e.g. what a custom scout watches and what makes it speak up.',\n ),\n }),\n )\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(\n askCallCount,\n askMaxQuestions,\n askAdjacencyNudged,\n );\n if (capDecision.kind === 'capped') {\n if (capDecision.reason === 'adjacency') {\n askAdjacencyNudged = true;\n }\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 const orchestratorTools = orchestrator\n ? buildOrchestratorTools(tool, orchestrator)\n : [];\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 ...orchestratorTools,\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 enqueueTask: `mcp__${SERVER_NAME}__enqueue_task`,\n completeTask: `mcp__${SERVER_NAME}__complete_task`,\n readHandoffs: `mcp__${SERVER_NAME}__read_handoffs`,\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 * 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 * Leaf-level constants for the events-audit program.\n *\n * Kept separate from `index.ts` so files like `yara-hooks.ts` can import\n * the filename constants without dragging in `index.ts`'s heavier imports\n * (agent-runner, audit/seed, etc.) — which can create import cycles.\n */\n\nexport const SETUP_REPORT_FILE = 'posthog-events-audit-report.md';\nexport const EVENT_INVENTORY_FILE = '.posthog-events-inventory.json';\n/** Per-part filename pattern emitted by events-audit subagents (e.g. `.posthog-events-inventory.part-3.json`). */\nexport const EVENT_INVENTORY_PART_PATTERN =\n /^\\.posthog-events-inventory\\.part-\\d+\\.json$/;\n","/**\n * Leaf-level constants for the posthog-integration program.\n *\n * Kept separate from `index.ts` so files like `yara-hooks.ts` can import\n * the filename constants without dragging in `index.ts`'s heavier imports\n * (agent-interface, framework-config, etc.) — which would create an import\n * cycle through agent-interface → yara-hooks.\n */\n\nexport const EVENT_PLAN_FILE = '.posthog-events.json';\n","/**\n * YARA hook wiring for the Claude Agent SDK.\n *\n * Creates PreToolUse and PostToolUse hook callback arrays that integrate the\n * real @posthog/warlock security scanner (YARA-X via WASM) into the wizard's\n * agent loop. These hooks are registered in the SDK's query() options alongside\n * the existing Stop hook.\n *\n * PreToolUse hooks block dangerous commands before execution. PostToolUse hooks\n * detect violations in written code and prompt injection in read content, and\n * scan context-mill skill downloads.\n *\n * Warlock owns the rules; this file owns the *policy* — how a match maps to a\n * block / revert / terminate response, plus the optional LLM triage pass that\n * filters false positives before we act.\n *\n * Naming: \"yara\" is the technique — scanning content against YARA rules — so the\n * wizard-side wiring keeps that name. \"warlock\" is the engine package that runs\n * the rules. Both names showing up here is intentional.\n *\n * This is Layer 2 (L2) in the wizard's defense-in-depth model, complementing the\n * prompt-based commandments (L0) and the canUseTool() allowlist (L1).\n */\n\nimport fs from 'fs';\nimport path from 'path';\nimport fg from 'fast-glob';\nimport type { ScanMatch, TriageMatch, LLMProvider } from '@posthog/warlock';\nimport { logToFile } from '@utils/debug';\nimport { analytics } from '@utils/analytics';\nimport { getUI } from '@ui';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { isSkillInstallCommand } from './skill-install';\nimport { WIZARD_YARA_REPORT_FILE } from '@utils/paths';\n// TODO(wizard#594): invert this dependency.\n// L2 infra (yara-hooks) imports product-specific filename constants from\n// individual programs. The leaf `constants.ts` modules break the *import*\n// cycle but don't fix the *layering* concern: this file knowing about\n// `events-audit`, `posthog-integration`, and `audit` violates \"product\n// knowledge never enters infrastructure code.\" Proper fix is inverted —\n// programs declare their own doc paths, the hooks read from a generic\n// registry. For now: every new program emitting a PII-shaped report has\n// to be added here. Land that cleanup before adding a fourth entry.\nimport {\n SETUP_REPORT_FILE as EVENTS_AUDIT_REPORT_FILE,\n EVENT_INVENTORY_FILE,\n EVENT_INVENTORY_PART_PATTERN,\n} from '@lib/programs/events-audit/constants';\nimport { AUDIT_REPORT_FILE } from '@lib/programs/audit/types';\nimport { EVENT_PLAN_FILE } from '@lib/programs/posthog-integration/constants';\n\n// ─── Warlock module accessor ─────────────────────────────────────\n// Warlock is ESM-only and lazily inits its WASM engine + compiles rules on the\n// first scan() call. We import it once and cache the module promise so the\n// warmed engine (a warlock-internal singleton) is shared across every hook.\n// Mirrors the lazy `await import(...)` pattern used for the agent SDK.\n\ntype WarlockModule = typeof import('@posthog/warlock');\n\nlet warlockModulePromise: Promise<WarlockModule> | null = null;\n\nfunction getWarlock(): Promise<WarlockModule> {\n if (!warlockModulePromise) {\n // Clear the cache on rejection so a transient failure (flaky disk, ESM\n // resolver hiccup) doesn't poison the cache for the rest of the process\n // lifetime. Without this, one rejected import permanently locks every\n // hook into fail-closed-terminate until restart.\n warlockModulePromise = import('@posthog/warlock').catch((err) => {\n warlockModulePromise = null;\n throw err;\n });\n }\n return warlockModulePromise;\n}\n\n/**\n * Pay the WASM-init + rule-compile cost up front, off the hook path, so the\n * first real tool-call scan doesn't eat cold-start under a hook timeout.\n * Best-effort: a failure here is non-fatal — hooks still fail closed per scan.\n */\nexport async function prewarmYaraScanner(): Promise<void> {\n try {\n const warlock = await getWarlock();\n await warlock.scan('');\n logToFile('[YARA] warlock pre-warmed');\n } catch (err) {\n logToFile('[YARA] warlock pre-warm failed:', err);\n }\n}\n\n// ─── Types ───────────────────────────────────────────────────────\n// Loose hook types to avoid tight coupling to the 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/** Which content surface a warlock rule applies to (from rule `scan_context`). */\ntype ScanContext = 'command' | 'input' | 'output';\n\n// ─── Scan Report Accumulator ─────────────────────────────────────\n\ntype ScanAction = 'blocked' | 'reverted' | 'warned' | 'aborted';\n\ninterface ScanReportEntry {\n rule: string;\n severity: string;\n category: string;\n action: ScanAction;\n phase: string;\n tool: string;\n /** Rule description — the human-readable \"why\". Rule-static, safe to report. */\n description: string;\n /** Triage outcome, when triage ran. */\n triageVerdict?: string;\n /**\n * Free-text triage rationale. May quote scanned content, so it is written to\n * the local report file only — NEVER sent to PostHog (see captureScanReport).\n */\n triageReason?: string;\n}\n\nlet scanCount = 0;\nconst scanViolations: ScanReportEntry[] = [];\n\nfunction recordScan(): void {\n scanCount++;\n}\n\n// recordMatch is the single entry point for a violation (log + telemetry +\n// report). scanViolations is appended only there.\n\n/**\n * Log a match to the run log + PostHog, and record it in the run's scan report.\n * Single entry point so every violation is reported consistently.\n */\nfunction recordMatch(\n phase: string,\n tool: string,\n match: ScanMatch,\n action: ScanAction,\n): void {\n const triage = (match as TriageMatch).triage;\n logYaraMatch(phase, tool, match, action);\n scanViolations.push({\n rule: match.rule,\n severity: match.metadata.severity ?? 'unknown',\n category: match.metadata.category ?? 'unknown',\n action,\n phase,\n tool,\n description: match.metadata.description ?? '',\n triageVerdict: triage?.verdict,\n triageReason: triage?.reason,\n });\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\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// Generous because triage adds an LLM round-trip, and because a *fired* hook\n// timeout means the protective action is dropped (fail-open). The triage HTTP\n// call has its own shorter internal timeout (see agent-interface), so a hung\n// triage fails inside our catch (fail-closed) before these fire.\n\n/** Timeout for scan hooks (PreToolUse, PostToolUse Write/Edit/Read/Grep) */\nconst HOOK_TIMEOUT_MS = 30_000;\n/** Timeout for the skill install hook (filesystem I/O + multiple scans) */\nconst SKILL_SCAN_HOOK_TIMEOUT_MS = 30_000;\n\n/**\n * Chunk size for scanning (100 KB). Oversized content is scanned in\n * overlapping chunks so coverage is complete — nothing is silently truncated.\n * The size also bounds what a single triage call pastes into the LLM prompt\n * (~25K tokens), so triage always sees the chunk its matches came from.\n */\nconst SCAN_CHUNK_SIZE = 100_000;\n/**\n * Overlap between adjacent chunks so a pattern straddling a chunk boundary\n * still lands whole inside at least one chunk. YARA rule strings are at most\n * a few hundred bytes; 4 KB is generous.\n */\nconst SCAN_CHUNK_OVERLAP = 4_096;\n\n// ─── Logging ─────────────────────────────────────────────────────\n\nfunction logYaraMatch(\n phase: string,\n tool: string,\n match: ScanMatch,\n action: ScanAction,\n): void {\n const severity = match.metadata.severity ?? 'unknown';\n const category = match.metadata.category ?? 'unknown';\n const ruleAction = match.metadata.action ?? 'unknown';\n const description = match.metadata.description ?? '';\n logToFile(\n `[YARA] ${phase}:${tool} [${action.toUpperCase()}] rule \"${match.rule}\" ` +\n `(severity: ${severity}, category: ${category}, action: ${ruleAction})\\n` +\n ` Description: ${description}`,\n );\n analytics.wizardCapture('yara rule matched', {\n rule: match.rule,\n severity: match.metadata.severity,\n category: match.metadata.category,\n rule_action: match.metadata.action,\n action,\n phase,\n tool,\n // Rule-static description — the \"why\". The free-text triage reason is\n // deliberately omitted (it can quote scanned content; stays local-only).\n description: match.metadata.description,\n triage_verdict: (match as TriageMatch).triage?.verdict,\n });\n}\n\n/**\n * Send the run's full scan report to PostHog so maintainers can see why a run\n * was flagged, warned, or aborted. This is the maintainer-facing report — it is\n * NEVER shown to the user. No-op if nothing was scanned. The free-text triage\n * reason is omitted to avoid sending scanned content off the user's machine.\n */\nexport function captureScanReport(): void {\n if (scanCount === 0) return;\n analytics.wizardCapture('yara scan report', {\n total_scans: scanCount,\n violation_count: scanViolations.length,\n clean_count: scanCount - scanViolations.length,\n violations: scanViolations.map((v) => ({\n rule: v.rule,\n severity: v.severity,\n category: v.category,\n action: v.action,\n phase: v.phase,\n tool: v.tool,\n description: v.description,\n triage_verdict: v.triageVerdict,\n })),\n });\n // Reset after sending so a second captureScanReport on this same run\n // (e.g. success-path call followed by an abort during shutdown) can't\n // ship duplicate telemetry. Also protects future chained-program runs.\n scanCount = 0;\n scanViolations.length = 0;\n}\n\n/**\n * End-of-run flush for the scan report. Wired once at the runner seam so every\n * harness (linear, orchestrator, and any future shape) reports without having\n * to know warlock exists — scanning is already harness-agnostic (it runs from\n * SDK Pre/PostToolUse hooks), and this keeps reporting at a single shared seam.\n *\n * Order matters: write the local --yara-report file FIRST (it reads scanCount /\n * scanViolations), THEN send telemetry. captureScanReport() zeroes scan state,\n * which is what makes this whole function idempotent — a second call from\n * another termination path (e.g. finally after an abort already flushed) finds\n * scanCount === 0 and every step no-ops.\n */\nexport function flushScanReport(\n session: Pick<WizardSession, 'yaraReport'>,\n): void {\n if (session.yaraReport) {\n const reportPath = writeScanReport();\n if (reportPath) {\n const summary = formatScanReport();\n getUI().log.info(`YARA scan report: ${reportPath}${summary ?? ''}`);\n }\n }\n captureScanReport();\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 EVENT_INVENTORY_FILE,\n EVENTS_AUDIT_REPORT_FILE,\n AUDIT_REPORT_FILE,\n EVENT_PLAN_FILE,\n]);\n\nconst WIZARD_DOC_PATTERNS: RegExp[] = [EVENT_INVENTORY_PART_PATTERN];\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: ScanMatch[]): ScanMatch {\n return matches.reduce((worst, m) =>\n (SEVERITY_RANK[m.metadata.severity ?? ''] ?? 0) >\n (SEVERITY_RANK[worst.metadata.severity ?? ''] ?? 0)\n ? m\n : worst,\n );\n}\n\n// ─── Scan + triage core ──────────────────────────────────────────\n\n/**\n * Keep only matches whose rule targets this content surface. Rules carry a\n * `scan_context` of 'command' | 'input' | 'output'. An undefined context is\n * treated as \"applies everywhere\" (fail-safe — a rule that forgets the tag\n * still gets enforced rather than silently skipped).\n */\nfunction matchesForContext(\n matches: ScanMatch[],\n ctx: ScanContext,\n): ScanMatch[] {\n return matches.filter((m) => {\n const c = m.metadata.scan_context as string | undefined;\n return c === ctx || c === undefined;\n });\n}\n\n/**\n * Drop false positives via warlock's LLM triage. Fail-closed: if no provider is\n * available, or the triage call throws, every match is treated as real — we\n * never silently suppress a flagged match.\n *\n * Every overruled match is reported to PostHog (rule metadata only, never the\n * free-text reason) so maintainers can alert on triage-overrule patterns — the\n * signal that someone is either tripping a noisy rule or trying to talk the\n * triage model out of a real finding.\n */\nasync function triageFilter(\n content: string,\n matches: ScanMatch[],\n ctx: ScanContext,\n llmProvider: LLMProvider | undefined,\n): Promise<ScanMatch[]> {\n if (matches.length === 0) return [];\n if (!llmProvider) {\n logToFile(\n `[YARA] triage skipped (no provider) — treating ${matches.length} match(es) as real`,\n );\n return matches;\n }\n try {\n const warlock = await getWarlock();\n const triaged = await warlock.triageMatches(content, matches, llmProvider);\n const kept = triaged.filter((m) => m.triage.verdict === 'true_positive');\n for (const m of triaged) {\n if (m.triage.verdict === 'true_positive') continue;\n logToFile(\n `[YARA] triage overruled rule \"${m.rule}\" (${\n m.metadata.severity ?? 'unknown'\n }) — not acting on it`,\n );\n analytics.wizardCapture('yara triage overruled', {\n rule: m.rule,\n severity: m.metadata.severity,\n category: m.metadata.category,\n scan_context: ctx,\n // The free-text triage reason is deliberately omitted — it can quote\n // scanned content, which must never leave the user's machine.\n });\n }\n logToFile(\n `[YARA] triage: ${matches.length} flagged → ${kept.length} kept as real`,\n );\n return kept;\n } catch (err) {\n logToFile('[YARA] triage failed — treating all matches as real:', err);\n return matches;\n }\n}\n\n/** A chunk of scanned content together with the matches found inside it. */\ninterface FlaggedChunk {\n chunk: string;\n matches: ScanMatch[];\n}\n\n/**\n * Split oversized content into overlapping chunks so the scanner covers all\n * of it. Content that fits in one chunk is returned as-is.\n */\nfunction chunkContent(content: string): string[] {\n if (content.length <= SCAN_CHUNK_SIZE) return [content];\n const chunks: string[] = [];\n const step = SCAN_CHUNK_SIZE - SCAN_CHUNK_OVERLAP;\n for (let start = 0; start < content.length; start += step) {\n chunks.push(content.slice(start, start + SCAN_CHUNK_SIZE));\n if (start + SCAN_CHUNK_SIZE >= content.length) break;\n }\n return chunks;\n}\n\n/**\n * Scan content against warlock rules — chunked when oversized, so nothing is\n * skipped — and keep only matches for this content surface. Chunks scan\n * sequentially (the WASM engine is single-threaded; parallelism buys nothing).\n * Returns each flagged chunk with its matches so triage can later judge every\n * match against the exact content it came from.\n */\nasync function scanForContext(\n content: string,\n ctx: ScanContext,\n): Promise<FlaggedChunk[]> {\n const warlock = await getWarlock();\n const chunks = chunkContent(content);\n if (chunks.length > 1) {\n logToFile(\n `[YARA] content is ${content.length} chars — scanning ${chunks.length} overlapping chunks`,\n );\n // Alertable signal: oversized content should be rare; a spike could mean\n // someone is padding content to probe the scanner.\n analytics.wizardCapture('yara scan chunked', {\n content_length: content.length,\n chunk_count: chunks.length,\n scan_context: ctx,\n });\n }\n const flagged: FlaggedChunk[] = [];\n for (const chunk of chunks) {\n const result = await warlock.scan(chunk);\n if (!result.matched) continue;\n const matches = matchesForContext(result.matches, ctx);\n if (matches.length === 0) continue;\n flagged.push({ chunk, matches });\n }\n return flagged;\n}\n\n/** The overlap between chunks can surface the same rule twice; count it once. */\nfunction dedupeByRule(matches: ScanMatch[]): ScanMatch[] {\n const seen = new Set<string>();\n return matches.filter((m) => {\n if (seen.has(m.rule)) return false;\n seen.add(m.rule);\n return true;\n });\n}\n\n/**\n * Triage each flagged chunk against its own content — the evidence is always\n * inside the window the LLM sees. Triage calls run in parallel (each is just\n * an HTTP round trip) so N flagged chunks cost ~1× triage latency, not N×.\n */\nasync function triageFlagged(\n flagged: FlaggedChunk[],\n ctx: ScanContext,\n llmProvider: LLMProvider | undefined,\n): Promise<ScanMatch[]> {\n const triaged = await Promise.all(\n flagged.map(({ chunk, matches }) =>\n triageFilter(chunk, matches, ctx, llmProvider),\n ),\n );\n return dedupeByRule(triaged.flat());\n}\n\n/** Scan content, filter to the relevant context, triage. Returns real matches. */\nasync function scanAndTriage(\n content: string,\n ctx: ScanContext,\n llmProvider: LLMProvider | undefined,\n): Promise<ScanMatch[]> {\n const flagged = await scanForContext(content, ctx);\n return triageFlagged(flagged, ctx, llmProvider);\n}\n\n// ─── PreToolUse Hooks ────────────────────────────────────────────\n\n/**\n * Create PreToolUse hook matchers for YARA scanning.\n * Scans Bash commands before execution for exfiltration, destructive\n * operations, and supply chain violations ('command'-context rules).\n */\nexport function createPreToolUseYaraHooks(\n // Accepted for API symmetry with createPostToolUseYaraHooks, but\n // intentionally unused: PreToolUse Bash always blocks on any flagged\n // command regardless of triage verdict, so the LLM call would be wasted.\n // Future PreToolUse hooks that need triage can wire this through.\n _llmProvider?: LLMProvider,\n): HookCallbackMatcher[] {\n return [\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 if (!command) return {};\n\n recordScan();\n // Skip triage on PreToolUse Bash: any flagged command is blocked\n // regardless of triage verdict, so the LLM call would be wasted.\n const matches = await scanAndTriage(command, 'command', undefined);\n if (matches.length === 0) return {};\n\n const match = highestSeverityMatch(matches);\n recordMatch('PreToolUse', 'Bash', match, 'blocked');\n\n return {\n decision: 'block',\n reason: `[YARA] ${match.rule}: ${\n match.metadata.description ?? 'security policy violation'\n }. 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 {\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 ('output'-context rules: PII, secrets)\n * 2. Read/Grep — scan read content ('input'-context rules: prompt injection)\n * 3. Bash (skill install) — scan downloaded skill files for poisoned content\n *\n * `onTerminate` is invoked on the terminal paths (critical prompt injection in\n * read content, a poisoned skill, or a scanner error under fail-closed). It is\n * what ACTUALLY stops the run — returning `stopReason` from a PostToolUse hook\n * is not honored by the SDK, so the caller wires this to the run's\n * AbortController (see agent-interface).\n */\nexport function createPostToolUseYaraHooks(\n llmProvider: LLMProvider | undefined,\n // REQUIRED, not optional: the SDK ignores `stopReason` from PostToolUse\n // hooks, so terminal paths (critical prompt injection, poisoned skill,\n // fail-closed scanner error) depend entirely on this callback firing.\n // Making it required at compile time prevents a future caller from\n // silently regressing every terminal path to a no-op.\n onTerminate: (reason: string) => void,\n): HookCallbackMatcher[] {\n return [\n // ── Write/Edit content scanning ──\n {\n hooks: [\n async (input: HookInput): Promise<HookOutput> => {\n try {\n const toolName = input.tool_name as string;\n if (toolName !== 'Write' && toolName !== 'Edit') return {};\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_string (replacement text).\n const content =\n toolName === 'Write'\n ? (toolInput?.content as string) ?? ''\n : (toolInput?.new_string as string) ?? '';\n\n if (!content) return {};\n\n recordScan();\n const flagged = await scanForContext(content, 'output');\n if (flagged.length === 0) return {};\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. Suppression runs BEFORE triage so\n // we never pay an LLM round trip for a match we're about to\n // discard anyway.\n // TODO(warlock#33): once warlock PR #33 lands with its\n // more-precise PII rules, the wizard-side suppression below\n // should become unnecessary — remove `WIZARD_DOC_BASENAMES`,\n // `WIZARD_DOC_PATTERNS`, and `isWizardDocumentationPath` and\n // verify the noisy-PII issue stays fixed.\n const filePath = toolInput?.file_path as string | undefined;\n const activeFlagged = isWizardDocumentationPath(filePath)\n ? flagged\n .map(({ chunk, matches }) => ({\n chunk,\n matches: matches.filter(\n (m) => m.metadata.category !== 'posthog_pii',\n ),\n }))\n .filter(({ matches }) => matches.length > 0)\n : flagged;\n if (activeFlagged.length === 0) {\n logToFile(\n `[YARA] posthog_pii match suppressed on wizard doc ${path.basename(\n filePath ?? '',\n )} (rule: ${flagged[0]?.matches[0]?.rule})`,\n );\n return {};\n }\n\n const matches = await triageFlagged(\n activeFlagged,\n 'output',\n llmProvider,\n );\n if (matches.length === 0) return {};\n\n const match = highestSeverityMatch(matches);\n recordMatch('PostToolUse', toolName, match, 'reverted');\n\n return {\n hookSpecificOutput: {\n hookEventName: 'PostToolUse',\n additionalContext:\n `[YARA VIOLATION] ${match.rule}: ${\n match.metadata.description ?? ''\n }. ` +\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: if scanning is broken on a Write/Edit, the next\n // Read/skill-install scan is also going to be broken — terminate\n // cleanly instead of looping the agent through \"please revert\"\n // until a Read finally aborts. Matches Read/Grep + skill-install\n // catch behavior; consistent fail-closed across all PostToolUse.\n const reason =\n '[YARA] Scanner error while scanning written content — session terminated as a precaution.';\n onTerminate(reason);\n return { stopReason: reason };\n }\n },\n ],\n timeout: HOOK_TIMEOUT_MS,\n },\n\n // ── Read/Grep prompt injection scanning ──\n {\n hooks: [\n async (input: HookInput): Promise<HookOutput> => {\n try {\n const toolName = input.tool_name as string;\n if (toolName !== 'Read' && toolName !== 'Grep') return {};\n\n const toolResponse = input.tool_response;\n // Guard before stringify: `JSON.stringify(undefined ?? '')`\n // returns the 2-char string '\"\"', which is truthy and slips\n // past the empty-content check below. Exit early when the SDK\n // gave us no response payload.\n if (toolResponse == null) return {};\n const content =\n typeof toolResponse === 'string'\n ? toolResponse\n : JSON.stringify(toolResponse);\n\n if (!content) return {};\n\n recordScan();\n const matches = await scanAndTriage(content, 'input', llmProvider);\n if (matches.length === 0) return {};\n\n const match = highestSeverityMatch(matches);\n\n // Critical severity or a rule asking us to block => terminate; the\n // agent's context may be poisoned by injected instructions.\n const isTerminal =\n match.metadata.severity === 'critical' ||\n match.metadata.action === 'block';\n\n if (isTerminal) {\n recordMatch('PostToolUse', toolName, match, 'aborted');\n const reason =\n `[YARA CRITICAL] ${match.rule}: Prompt injection detected in file content. ` +\n `Agent context is potentially poisoned. Session terminated for safety.`;\n onTerminate(reason);\n return { stopReason: reason };\n }\n\n recordMatch('PostToolUse', toolName, match, 'warned');\n return {\n hookSpecificOutput: {\n hookEventName: 'PostToolUse',\n additionalContext: `[YARA WARNING] ${match.rule}: ${\n match.metadata.description ?? ''\n }`,\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 const reason =\n '[YARA] Scanner error while scanning read content — session terminated as a precaution.';\n onTerminate(reason);\n return { stopReason: reason };\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 matches = await scanSkillFiles(cwd, skillDir, llmProvider);\n\n if (matches.length === 0) return {};\n\n // INTENTIONAL ASYMMETRY: skill-install terminates on ANY\n // post-triage match, while Read/Grep only terminates on\n // critical-or-block. Skills are downloaded code from an\n // external source; any flagged content in them is treated as\n // poisoned. Read/Grep, by contrast, may scan project files\n // the user wrote themselves where a medium-severity match\n // can warrant a warning instead of a terminate.\n // TODO(wizard#593): document / lock in this contract with a test.\n const match = highestSeverityMatch(matches);\n recordMatch(\n 'PostToolUse',\n 'Bash (skill install)',\n match,\n 'aborted',\n );\n\n const reason =\n `[YARA CRITICAL] Poisoned skill detected in ${skillDir}: ${match.rule}. ` +\n `The downloaded skill contains potential prompt injection. Session terminated for safety.`;\n onTerminate(reason);\n return { stopReason: reason };\n } catch (error) {\n logToFile('[YARA] PostToolUse skill install hook error:', error);\n // Fail closed: terminate if skill scanning fails\n const reason =\n '[YARA] Scanner error while scanning skill files — session terminated as a precaution.';\n onTerminate(reason);\n return { stopReason: reason };\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 * Scans each file sequentially (single-threaded WASM — parallelism buys\n * nothing), unions the 'input'-context matches, then triages once across the\n * combined content. Returns the real (post-triage) matches.\n */\nasync function scanSkillFiles(\n cwd: string,\n skillDir: string,\n llmProvider: LLMProvider | undefined,\n): Promise<ScanMatch[]> {\n const absoluteDir = path.resolve(cwd, skillDir);\n\n if (!fs.existsSync(absoluteDir)) {\n logToFile(`[YARA] Skill directory does not exist: ${absoluteDir}`);\n return [];\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: string[] = [];\n for (const filePath of files) {\n try {\n fileContents.push(fs.readFileSync(filePath, 'utf-8'));\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 [];\n }\n\n logToFile(\n `[YARA] Scanning ${fileContents.length} files in skill directory: ${skillDir}`,\n );\n\n // Pass 1 (sequential): scan each file through the chunk-aware path —\n // full coverage even for oversized files, and every flagged chunk keeps a\n // reference to the exact content its matches came from.\n const flagged: FlaggedChunk[] = [];\n for (const content of fileContents) {\n flagged.push(...(await scanForContext(content, 'input')));\n }\n\n // Pass 2 (parallel, inside triageFlagged): triage each flagged chunk\n // against its own content concurrently. Triage is just an axios call —\n // running them in parallel cuts total wall-clock from N × triage-timeout\n // to ~1×. Important because the hook's HOOK_TIMEOUT_MS is 30s; serial\n // triage on 2+ matched files could blow the hook timeout under a slow\n // Haiku.\n return triageFlagged(flagged, 'input', llmProvider);\n}\n","/**\n * LLM provider for warlock security-scan triage.\n *\n * Warlock's triageMatches() takes a consumer-supplied `(prompt) => Promise<string>`\n * to run a second pass that filters false positives out of YARA matches. This\n * builds that provider on top of the wizard's existing PostHog LLM gateway auth\n * — the same ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN that initializeAgent()\n * sets for the agent SDK. The gateway speaks the standard Anthropic Messages API,\n * so we POST to it directly with axios (the plain @anthropic-ai/sdk isn't a dep).\n */\n\nimport axios from 'axios';\nimport { logToFile } from '@utils/debug';\nimport type { LLMProvider } from '@posthog/warlock';\n\n// Haiku 4.5: triage is a fast, narrow \"true_positive | false_positive\" verdict\n// on already-flagged matches — Haiku's the right tier (cheap, fast, plenty\n// capable for boolean classification). Do NOT swap to Sonnet without reason;\n// the cost/latency difference matters on every flagged scan. temperature 0\n// keeps verdicts deterministic across identical inputs.\nconst TRIAGE_MODEL = 'claude-haiku-4-5';\nconst TRIAGE_MAX_TOKENS = 16_384;\n// Shorter than the hook timeout so a hung triage fails *inside* the hook's\n// try/catch (→ fail-closed) rather than tripping the SDK hook timeout.\nconst TRIAGE_TIMEOUT_MS = 20_000;\n\ninterface AnthropicTextBlock {\n type: string;\n text?: string;\n}\n\n/**\n * Build the triage LLM provider from the gateway auth on process.env (set by\n * initializeAgent before any agent run). Returns undefined if auth isn't\n * configured — callers then skip triage and fail closed (act on every flagged\n * match), so a missing key never silently disables the scanner.\n */\nexport function createTriageLLMProvider(): LLMProvider | undefined {\n const baseURL = process.env.ANTHROPIC_BASE_URL;\n const authToken = process.env.ANTHROPIC_AUTH_TOKEN;\n\n if (!baseURL || !authToken) {\n logToFile(\n '[YARA] triage provider unavailable (no gateway auth) — flagged scans will fail closed',\n );\n return undefined;\n }\n\n logToFile(`[YARA] triage provider ready (model: ${TRIAGE_MODEL})`);\n\n return async (prompt: string): Promise<string> => {\n const res = await axios.post(\n `${baseURL}/v1/messages`,\n {\n model: TRIAGE_MODEL,\n max_tokens: TRIAGE_MAX_TOKENS,\n temperature: 0,\n messages: [{ role: 'user', content: prompt }],\n },\n {\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'anthropic-version': '2023-06-01',\n 'content-type': 'application/json',\n },\n timeout: TRIAGE_TIMEOUT_MS,\n },\n );\n\n const data = res.data as { content?: AnthropicTextBlock[] } | undefined;\n const content = data?.content;\n if (Array.isArray(content)) {\n return content\n .filter(\n (b: AnthropicTextBlock) =>\n b?.type === 'text' && typeof b.text === 'string',\n )\n .map((b: AnthropicTextBlock) => b.text)\n .join('');\n }\n return '';\n };\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 'Keep PostHog data capture at its defaults unless the user explicitly asks otherwise. Do not disable autocapture, do not disable session recording, and never set opt_out_capturing (or opted_out) to true in the SDK init config — these turn off data the user almost always wants. Note: posthog.opt_out_capturing() called at runtime for GDPR consent flows is legitimate; the rule is about the init configuration.',\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 run phases — what the wizard agent is currently doing, derived from\n * the tool it's running. The Visualizer reads `store.currentStage.stage`\n * and renders the matching ASCII visual.\n */\n\nexport enum AgentPhase {\n CodebaseScan = 'codebase-scan',\n SkillInstall = 'skill-install',\n DepInstall = 'dep-install',\n CodeEdits = 'code-edits',\n EnvSetup = 'env-setup',\n Dashboards = 'dashboards',\n}\n\n/** Maps a Claude SDK tool name to the phase that tool implies. Returns null\n * when the tool doesn't drive the visualizer (Task*, TodoWrite, etc.). */\nexport function classifyToolToStage(toolName: string): AgentPhase | null {\n if (\n toolName.includes('install_skill') ||\n toolName.includes('load_skill_menu')\n ) {\n return AgentPhase.SkillInstall;\n }\n if (\n toolName.includes('set_env_values') ||\n toolName.includes('check_env_keys')\n ) {\n return AgentPhase.EnvSetup;\n }\n if (toolName.includes('mcp__posthog')) {\n return AgentPhase.Dashboards;\n }\n if (toolName === 'Bash') return AgentPhase.DepInstall;\n if (toolName === 'Write' || toolName === 'Edit' || toolName === 'MultiEdit') {\n return AgentPhase.CodeEdits;\n }\n if (toolName === 'Read' || toolName === 'Glob' || toolName === 'Grep') {\n return AgentPhase.CodebaseScan;\n }\n return null;\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, missing\n * MCP/resource, the end-of-run remark) by emitting marker strings inside\n * their prose. `AgentOutputSignals` keeps only the lines that carry such a\n * marker, so the buffer stays bounded regardless of run length.\n *\n * YARA violations are deliberately NOT detected here: scanning the agent's\n * prose for \"[YARA ...]\" markers false-positives when the agent merely\n * *mentions* a non-terminal block. Terminal YARA outcomes flow through the\n * hooks' onTerminate callback (`yaraViolationReason` in runAgent) instead.\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 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 /** 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 { runtimeEnv } from '@env';\nimport {\n WIZARD_REMARK_EVENT_NAME,\n POSTHOG_PROPERTY_HEADER_PREFIX,\n WIZARD_ORCHESTRATOR_FLAG_KEY,\n WIZARD_USER_AGENT,\n WIZARD_WARLOCK_DISABLED_FLAG_KEY,\n DEFAULT_AGENT_MODEL,\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 prewarmYaraScanner,\n} from '@lib/yara-hooks';\nimport { createTriageLLMProvider } from './triage-provider';\nimport { getWizardCommandments } from './commandments';\nimport { classifyToolToStage } from './agent-phase';\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 /**\n * Override the agent model for this run. Defaults to DEFAULT_AGENT_MODEL.\n * Use for cheap mechanical runs (e.g. source-map detection on HAIKU_MODEL).\n */\n modelOverride?: 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 * Orchestrator queue context. Present only when the `wizard-orchestrator`\n * flag routes the run here; threaded into wizard-tools so the orchestrator\n * tools register.\n */\n orchestrator?: import('@lib/agent/runner/orchestrator/queue-tools').OrchestratorToolsContext;\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 requestRemark = true,\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). Skipped when the caller opts out — the\n // orchestrator suppresses it per task so it does not fire on every agent.\n if (requestRemark && !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 * Global identifiers attached to every LLM gateway trace for a run. They ride on\n * each `$ai_generation` the gateway emits (as `X-POSTHOG-PROPERTY-*` headers via\n * `buildAgentEnv`), so traces are filterable by program, framework, run, and build\n * type for cost attribution and dashboards. `skill_id` is omitted when the run has\n * none.\n */\nexport function buildRunTags(args: {\n programId: string;\n integration: string;\n runId: string;\n build: string;\n skillId?: string;\n}): Record<string, string> {\n return {\n program_id: args.programId,\n integration: args.integration,\n run_id: args.runId,\n build: args.build,\n ...(args.skillId ? { skill_id: args.skillId } : {}),\n };\n}\n\n/**\n * Whether the Warlock/YARA kill switch is engaged for this run. Off by default:\n * scanning is disabled only when the feature flag resolves to the explicit\n * string 'true', or the local POSTHOG_WIZARD_WARLOCK_DISABLED env override is\n * set. A missing flag, an empty flag map (the safe default returned when the\n * flag fetch fails), or any other value all leave scanning ON — a network blip\n * must never silently disable a security control.\n */\nexport function isWarlockDisabled(flags: Record<string, string> = {}): boolean {\n return (\n flags[WIZARD_WARLOCK_DISABLED_FLAG_KEY] === 'true' ||\n runtimeEnv('POSTHOG_WIZARD_WARLOCK_DISABLED') === 'true'\n );\n}\n\n/**\n * Whether this run uses the experimental task-queue orchestrator. Gated by the\n * boolean `wizard-orchestrator` feature flag, targeted to the user in the wizard's\n * analytics project.\n */\nexport function isOrchestratorEnabled(\n flags: Record<string, string> = {},\n): boolean {\n return flags[WIZARD_ORCHESTRATOR_FLAG_KEY] === 'true';\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 try {\n // Configure model routing (inherited by the SDK subprocess). All model\n // calls route through the PostHog LLM gateway, authed with the user's\n // OAuth token.\n // Disable experimental betas (like input_examples) the gateway doesn't support.\n process.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = 'true';\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 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 orchestrator: config.orchestrator,\n });\n mcpServers['wizard-tools'] = wizardToolsServer;\n\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 = config.modelOverride ?? DEFAULT_AGENT_MODEL;\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 // Pre-warm the warlock scanner (WASM init + rule compile) off the hook path\n // so the first tool-call scan doesn't pay cold-start under a hook timeout.\n // Fire-and-forget: the warlock module promise is cached, so the first real\n // scan awaits the same in-flight promise. Awaiting here would just move\n // the cold-start cost to user-visible startup.\n // Best-effort — a failure is non-fatal (hooks still fail closed per scan).\n void prewarmYaraScanner();\n\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 * 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 * Emit a `wizard: step` event on each agent task transition. Threaded from\n * `ProgramRun.trackStepProgress`; defaults off for every other caller.\n */\n emitStepEvents?: boolean;\n /** Request the end-of-run reflection remark. Defaults to true. */\n requestRemark?: boolean;\n /**\n * Extra properties attached to this run's `agent completed` / `agent\n * aborted` events (e.g. the orchestrator's task type and id).\n */\n analyticsProperties?: Record<string, unknown>;\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 emitStepEvents = false,\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 // Token usage comes from the SDK result message and is per agent run —\n // for the orchestrator that means per task, the secondary cost to watch.\n const usage = lastResultMessage?.usage as\n | {\n input_tokens?: number;\n output_tokens?: number;\n cache_creation_input_tokens?: number;\n cache_read_input_tokens?: number;\n }\n | undefined;\n analytics.wizardCapture('agent completed', {\n duration_ms: durationMs,\n duration_seconds: durationSeconds,\n model: agentConfig.model,\n num_turns: lastResultMessage?.num_turns,\n total_cost_usd: lastResultMessage?.total_cost_usd,\n input_tokens: usage?.input_tokens,\n output_tokens: usage?.output_tokens,\n cache_creation_input_tokens: usage?.cache_creation_input_tokens,\n cache_read_input_tokens: usage?.cache_read_input_tokens,\n ...config?.analyticsProperties,\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 // Set when a YARA hook detects a terminal violation. Returning `stopReason`\n // from a PostToolUse hook does NOT stop the SDK, so we abort the query and\n // surface a YARA_VIOLATION below — mirroring the [ABORT] mechanism.\n let yaraViolationReason: 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 // LLM provider for warlock triage (reuses the gateway auth set on\n // process.env by initializeAgent). Undefined if auth is missing — hooks\n // then skip triage and fail closed.\n const triageProvider = createTriageLLMProvider();\n\n // Actually stop the run when a YARA hook hits a terminal violation. The SDK\n // ignores `stopReason` from PostToolUse hooks, so we abort the query (like\n // [ABORT]) and return YARA_VIOLATION from the loop-end / catch below.\n const onYaraTerminate = (reason: string) => {\n if (yaraViolationReason) return; // first violation wins\n yaraViolationReason = reason;\n logToFile(`[YARA] terminating run: ${reason}`);\n abortController.abort();\n signalDone!();\n };\n\n // Kill switch for Warlock/YARA scanning (off by default — see\n // isWarlockDisabled for the fail-safe semantics).\n const warlockDisabled = isWarlockDisabled(agentConfig.wizardFlags);\n if (warlockDisabled) {\n logToFile(\n '[warlock] kill switch active — YARA scanning disabled for run',\n );\n analytics.wizardCapture('warlock disabled', { reason: 'kill-switch' });\n }\n\n const response = query({\n prompt: createPromptStream(),\n options: {\n abortController,\n model: agentConfig.model,\n cwd: agentConfig.workingDirectory,\n permissionMode: 'acceptEdits',\n betas: ['context-1m-2025-08-07'],\n mcpServers: agentConfig.mcpServers,\n 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 // Drop any shell ANTHROPIC_API_KEY so it can't override the wizard's\n // OAuth gateway 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 // PostHog gateway headers: Bedrock fallback + property/flag tags.\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: warlockDisabled\n ? []\n : createPreToolUseYaraHooks(triageProvider),\n PostToolUse: warlockDisabled\n ? []\n : createPostToolUseYaraHooks(triageProvider, onYaraTerminate),\n Stop: [\n {\n hooks: [\n createStopHook(\n config?.additionalFeatureQueue ?? [],\n signals,\n config?.requestRemark ?? true,\n ),\n ],\n timeout: 30,\n },\n ],\n },\n },\n });\n\n // Process the async generator\n for await (const message of response) {\n // Log initial context size on the first assistant response so we can\n // detect sudden shifts in starting context (e.g. MCP schema bloat).\n if (!loggedInitialContext && message.type === 'assistant') {\n const usage = message.message?.usage as\n | {\n input_tokens?: number;\n cache_creation_input_tokens?: number;\n cache_read_input_tokens?: number;\n }\n | undefined;\n if (usage) {\n const input = usage.input_tokens ?? 0;\n const cacheCreation = usage.cache_creation_input_tokens ?? 0;\n const cacheRead = usage.cache_read_input_tokens ?? 0;\n const initialTokens = input + cacheCreation + cacheRead;\n logToFile(\n `Initial context: ${initialTokens} tokens (input=${input}, cache_creation=${cacheCreation}, cache_read=${cacheRead})`,\n );\n analytics.wizardCapture('agent initial context', {\n initial_tokens: initialTokens,\n input_tokens: input,\n cache_creation_input_tokens: cacheCreation,\n cache_read_input_tokens: cacheRead,\n });\n }\n loggedInitialContext = true;\n }\n\n // Pass receivedSuccessResult so handleSDKMessage can suppress user-facing error\n // output for post-success cleanup errors while still logging them to file\n handleSDKMessage(\n message,\n options,\n spinner,\n signals,\n receivedSuccessResult,\n tasks,\n isOrchestratorEnabled(agentConfig.wizardFlags ?? {}),\n emitStepEvents,\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 // A YARA hook detected a terminal violation and aborted the run.\n if (yaraViolationReason) {\n logToFile('Agent error: YARA_VIOLATION');\n spinner.stop('Security violation detected');\n return { error: AgentErrorType.YARA_VIOLATION };\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 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 // A YARA hook aborted the run (the SDK throws AbortError once the hook\n // calls abortController.abort()). Surface it before anything else so it is\n // never mistaken for a success-cleanup race or a generic abort.\n if (yaraViolationReason) {\n logToFile('Agent error: YARA_VIOLATION');\n spinner.stop('Security violation detected');\n return { error: AgentErrorType.YARA_VIOLATION };\n }\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 signal before the exception was thrown.\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 model: agentConfig.model,\n ...config?.analyticsProperties,\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 /** When true, emit a `wizard: step` event on each status transition. */\n emitStepEvents?: boolean;\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 // Per-step drop-off signal for programs that opt in via `trackStepProgress`\n // (threaded here as `emitStepEvents`). Emit `wizard: step` on each real\n // status transition so analytics can see how far a run got — even a silent\n // step (no wizard_ask) that dies mid-run surfaces as its last `in_progress`\n // with no matching `completed`. Generic: the step name is whatever the\n // agent set; the `command` tag already identifies the program.\n if (\n store.emitStepEvents &&\n input.status &&\n input.status !== existing.status &&\n (input.status === 'in_progress' || input.status === 'completed')\n ) {\n const keys = [...store.tasks.keys()];\n analytics.wizardCapture('step', {\n // The task's display label lives on `activeForm` (what the TUI renders,\n // e.g. \"Checking access\"); `content`/`subject` are typically empty on a\n // status-only TaskUpdate. Prefer the stored entry, then the update, so\n // the name is never null. Named `step_name` (not `step`): a bare\n // `properties.step` doesn't resolve in HogQL — `step_name` queries\n // cleanly, like `step_index` / `step_count`.\n step_name:\n existing.activeForm ??\n input.activeForm ??\n existing.content ??\n input.subject,\n status: input.status,\n step_index: keys.indexOf(input.taskId),\n step_count: keys.length,\n });\n }\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 // The orchestrator owns the TUI task panel (it renders its queue). Suppress the\n // agent's own TaskCreate/TaskUpdate rendering so it does not clobber the queue.\n suppressTaskRender = false,\n // Opt-in per-step analytics, threaded from runAgent's `emitStepEvents`\n // (ProgramRun.trackStepProgress). Off for every program that doesn't opt in.\n emitStepEvents = false,\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 || suppressTaskRender) 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 emitStepEvents,\n });\n }\n\n // Mirror the active tool into the Visualizer's \"stage\" indicator.\n if (block.type === 'tool_use') {\n const stage = classifyToolToStage((block as ToolUseBlock).name);\n if (stage) getUI().setStage(stage);\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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"}
|