@posthog/wizard 2.40.0 → 2.42.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.
Files changed (105) hide show
  1. package/dist/{add-mcp-server-to-clients-Dtvqw5DO.js → add-mcp-server-to-clients-fGAJ6wRG.js} +5 -5
  2. package/dist/{add-mcp-server-to-clients-Dtvqw5DO.js.map → add-mcp-server-to-clients-fGAJ6wRG.js.map} +1 -1
  3. package/dist/{agent-interface-BTVAlvLW.js → agent-interface-dPcuJv8g.js} +12 -11
  4. package/dist/agent-interface-dPcuJv8g.js.map +1 -0
  5. package/dist/{agent-runner-CqNL9x2D.js → agent-runner-3bDJ_WLl.js} +122 -64
  6. package/dist/agent-runner-3bDJ_WLl.js.map +1 -0
  7. package/dist/analytics-B33Hy7zS.js +2 -0
  8. package/dist/{analytics-__4kRqZr.js → analytics-jQRtyqzG.js} +5 -2
  9. package/dist/analytics-jQRtyqzG.js.map +1 -0
  10. package/dist/{api-D0RqJPWO.js → api-Bweisj9K.js} +3 -3
  11. package/dist/{api-D0RqJPWO.js.map → api-Bweisj9K.js.map} +1 -1
  12. package/dist/bin.js +66 -65
  13. package/dist/bin.js.map +1 -1
  14. package/dist/{ci-install-SXJrlScZ.js → ci-install-BoATzVVs.js} +4 -4
  15. package/dist/{ci-install-SXJrlScZ.js.map → ci-install-BoATzVVs.js.map} +1 -1
  16. package/dist/{debug-D_mboDpg.js → debug--QPXcpzv.js} +4 -5
  17. package/dist/{debug-D_mboDpg.js.map → debug--QPXcpzv.js.map} +1 -1
  18. package/dist/{debug-dh-I1XvJ.js → debug-DCQHRXu9.js} +1 -1
  19. package/dist/{defaults-BxiMLkQY.js → defaults-CeNThA-1.js} +1 -1
  20. package/dist/{defaults-BxiMLkQY.js.map → defaults-CeNThA-1.js.map} +1 -1
  21. package/dist/{env-api-key-DDVkPQmo.js → env-api-key-cjD4yfGT.js} +1 -1
  22. package/dist/{env-api-key-DDVkPQmo.js.map → env-api-key-cjD4yfGT.js.map} +1 -1
  23. package/dist/{environment-4zKc7kWr.js → environment-Xdauxvph.js} +3 -3
  24. package/dist/{environment-4zKc7kWr.js.map → environment-Xdauxvph.js.map} +1 -1
  25. package/dist/{file-utils-Cb6HLPhu.js → file-utils-BJhGBl-W.js} +2 -2
  26. package/dist/{file-utils-Cb6HLPhu.js.map → file-utils-BJhGBl-W.js.map} +1 -1
  27. package/dist/{headless-ui-CjdRKkTG.js → headless-ui-XdCxxQ0b.js} +2 -2
  28. package/dist/{headless-ui-CjdRKkTG.js.map → headless-ui-XdCxxQ0b.js.map} +1 -1
  29. package/dist/{interactive-C3Ty8H5M.js → interactive-CwtUeGJT.js} +2 -2
  30. package/dist/{interactive-C3Ty8H5M.js.map → interactive-CwtUeGJT.js.map} +1 -1
  31. package/dist/{mcp-B_utnKAq.js → mcp-DIuzSJ8f.js} +15 -19
  32. package/dist/mcp-DIuzSJ8f.js.map +1 -0
  33. package/dist/{mcp-prompt-streaming-BzFu8dP6.js → mcp-prompt-streaming-CCS2EkfY.js} +10 -12
  34. package/dist/mcp-prompt-streaming-CCS2EkfY.js.map +1 -0
  35. package/dist/{non-interactive-B2a00UId.js → non-interactive-Cjb5uLLn.js} +2 -2
  36. package/dist/{non-interactive-B2a00UId.js.map → non-interactive-Cjb5uLLn.js.map} +1 -1
  37. package/dist/{package-manager-CRYusNaP.js → package-manager-D_05Bmr0.js} +2 -2
  38. package/dist/{package-manager-CRYusNaP.js.map → package-manager-D_05Bmr0.js.map} +1 -1
  39. package/dist/{playground-CVIGzLGh.js → playground-BtB7RvUT.js} +26 -21
  40. package/dist/playground-BtB7RvUT.js.map +1 -0
  41. package/dist/{posthog-BHbtnNQE.js → posthog-DwKlq0HM.js} +2 -2
  42. package/dist/posthog-DwKlq0HM.js.map +1 -0
  43. package/dist/{posthog-integration-D2S9mCAv.js → posthog-integration-Cw1QDMFi.js} +24 -25
  44. package/dist/posthog-integration-Cw1QDMFi.js.map +1 -0
  45. package/dist/{provisioning-B1KoQFFK.js → provisioning-CmpiiTaR.js} +67 -6
  46. package/dist/provisioning-CmpiiTaR.js.map +1 -0
  47. package/dist/{registry-CgrvMaYt.js → registry-DPriFJQI.js} +4 -4
  48. package/dist/{registry-CgrvMaYt.js.map → registry-DPriFJQI.js.map} +1 -1
  49. package/dist/{security-DX5ey9g8.js → security-DJrWWO0M.js} +101 -5
  50. package/dist/security-DJrWWO0M.js.map +1 -0
  51. package/dist/{setup-utils-CUeGnxBk.js → setup-utils-C8yipMUt.js} +137 -32
  52. package/dist/setup-utils-C8yipMUt.js.map +1 -0
  53. package/dist/{start-tui-BC3yp_wW.js → start-tui-BMZL7mKh.js} +33 -30
  54. package/dist/start-tui-BMZL7mKh.js.map +1 -0
  55. package/dist/{steps-5YGQK7la.js → steps-Dhq5VReA.js} +7 -7
  56. package/dist/{steps-5YGQK7la.js.map → steps-Dhq5VReA.js.map} +1 -1
  57. package/dist/{store-DQ0-oE_Y.js → store-Bu1xO0_5.js} +5 -5
  58. package/dist/{store-DQ0-oE_Y.js.map → store-Bu1xO0_5.js.map} +1 -1
  59. package/dist/{subagent-DkTSX2ew.js → subagent-mVY-kDU0.js} +2 -2
  60. package/dist/{subagent-DkTSX2ew.js.map → subagent-mVY-kDU0.js.map} +1 -1
  61. package/dist/{task-stream-d-R9J0g4.js → task-stream-Ne-9ZOnf.js} +3 -3
  62. package/dist/{task-stream-d-R9J0g4.js.map → task-stream-Ne-9ZOnf.js.map} +1 -1
  63. package/dist/{tasks-SbUmyQPS.js → tasks-2af2kDE3.js} +2 -2
  64. package/dist/{tasks-SbUmyQPS.js.map → tasks-2af2kDE3.js.map} +1 -1
  65. package/dist/{telemetry-_7NXwx7o.js → telemetry-wGk3UsDw.js} +3 -3
  66. package/dist/{telemetry-_7NXwx7o.js.map → telemetry-wGk3UsDw.js.map} +1 -1
  67. package/dist/{terminal-NhR2Seu_.js → terminal-CnYduoIp.js} +49 -25
  68. package/dist/terminal-CnYduoIp.js.map +1 -0
  69. package/dist/{tools-WDPzlrXc.js → tools-BTq5sLH9.js} +4 -4
  70. package/dist/{tools-WDPzlrXc.js.map → tools-BTq5sLH9.js.map} +1 -1
  71. package/dist/{types-evL_rPob.js → types-y3HkjtSq.js} +1 -1
  72. package/dist/{types-evL_rPob.js.map → types-y3HkjtSq.js.map} +1 -1
  73. package/dist/{wizard-abort-CWaKUNMI.js → wizard-abort-CSMnla3D.js} +4 -4
  74. package/dist/{wizard-abort-CWaKUNMI.js.map → wizard-abort-CSMnla3D.js.map} +1 -1
  75. package/dist/{wizard-abort-DGM_sAA9.js → wizard-abort-D8Lj609C.js} +1 -1
  76. package/dist/{wizard-session-BoXAZEOf.js → wizard-session-BudcixsA.js} +1 -2
  77. package/dist/wizard-session-BudcixsA.js.map +1 -0
  78. package/dist/{wizard-session-C8W_6X0j.js → wizard-session-DXivb7kv.js} +1 -1
  79. package/dist/{wizard-tools-CrpRaYkE.js → wizard-tools-BBLQk1YC.js} +4 -4
  80. package/dist/{wizard-tools-CrpRaYkE.js.map → wizard-tools-BBLQk1YC.js.map} +1 -1
  81. package/dist/wizard-ui-WZ48rUgr.js.map +1 -1
  82. package/dist/yara-hooks-B42cYxIF.js +2 -0
  83. package/dist/{yara-hooks-BPQ3VE-l.js → yara-hooks-Ciy8d0ok.js} +4 -4
  84. package/dist/{yara-hooks-BPQ3VE-l.js.map → yara-hooks-Ciy8d0ok.js.map} +1 -1
  85. package/package.json +1 -1
  86. package/dist/agent-interface-BTVAlvLW.js.map +0 -1
  87. package/dist/agent-runner-CqNL9x2D.js.map +0 -1
  88. package/dist/analytics-D6RSYOPO.js +0 -2
  89. package/dist/analytics-__4kRqZr.js.map +0 -1
  90. package/dist/host-resolution-Czw5O33G.js +0 -115
  91. package/dist/host-resolution-Czw5O33G.js.map +0 -1
  92. package/dist/mcp-B_utnKAq.js.map +0 -1
  93. package/dist/mcp-prompt-streaming-BzFu8dP6.js.map +0 -1
  94. package/dist/playground-CVIGzLGh.js.map +0 -1
  95. package/dist/posthog-BHbtnNQE.js.map +0 -1
  96. package/dist/posthog-integration-D2S9mCAv.js.map +0 -1
  97. package/dist/provisioning-B1KoQFFK.js.map +0 -1
  98. package/dist/security-DX5ey9g8.js.map +0 -1
  99. package/dist/setup-utils-CUeGnxBk.js.map +0 -1
  100. package/dist/start-tui-BC3yp_wW.js.map +0 -1
  101. package/dist/terminal-NhR2Seu_.js.map +0 -1
  102. package/dist/urls-DYIMfv29.js +0 -67
  103. package/dist/urls-DYIMfv29.js.map +0 -1
  104. package/dist/wizard-session-BoXAZEOf.js.map +0 -1
  105. package/dist/yara-hooks-yy8OLJ43.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"wizard-tools-CrpRaYkE.js","names":["path"],"sources":["../src/utils/atomic-ledger.ts","../src/lib/wizard-ask-bridge.ts","../src/lib/secret-vault.ts","../src/lib/agent/runner/sequence/orchestrator/queue.ts","../src/lib/agent/runner/sequence/orchestrator/queue-tools.ts","../src/lib/wizard-tools.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","/**\n * WizardAskBridge — host-side promise broker for the `wizard_ask` MCP tool.\n *\n * The `wizard_ask` tool needs to (a) read information from the wizard\n * session (the active skill id, used as the analytics `source`) and\n * (b) drive the TUI overlay. Wiring `wizard-tools.ts` directly to either\n * would couple our pure-data MCP server to the runtime UI layer.\n *\n * The bridge is the seam: `wizard-tools.ts` depends on this interface,\n * and `agent-runner.ts` constructs an implementation that knows about\n * both the session and `getUI()`.\n */\nimport { randomUUID } from 'crypto';\n\nimport { analytics } from '@utils/analytics';\nimport type {\n AskAnswers,\n AskQuestion,\n PendingQuestion,\n} from './wizard-session';\n\nexport interface WizardAskRequest {\n questions: AskQuestion[];\n}\n\nexport interface WizardAskBridge {\n /**\n * Open the WizardAsk overlay and resolve with the user's answers.\n * One answer per question id (string for `single`/`text`, string[] for\n * `multi`). Cancelled fields come back as the literal `\"__cancelled__\"`.\n */\n request(req: WizardAskRequest): Promise<AskAnswers>;\n}\n\nexport interface WizardAskBridgeOptions {\n /** Returns the active skill id, used as the analytics `source` on the request. */\n getSource: () => string;\n /** Opens the overlay and resolves once the user submits or cancels. */\n showQuestion: (question: PendingQuestion) => Promise<AskAnswers>;\n /**\n * Per-question timeout in milliseconds. When the user takes longer than\n * this to answer, every unanswered field resolves with the\n * {@link CANCELLED_SENTINEL} value. Defaults to {@link DEFAULT_ASK_TIMEOUT_MS}.\n */\n timeoutMs?: number;\n /**\n * Opt the rendered overlay into rich link handling (OSC 8 hyperlinks +\n * clipboard copy for prompt URLs). Set per program; defaults to false.\n * Propagated onto every {@link PendingQuestion} this bridge creates.\n */\n richLinks?: boolean;\n}\n\n/** Sentinel returned for unanswered fields on cancellation or timeout. */\nexport const CANCELLED_SENTINEL = '__cancelled__';\n\n/** Default per-question timeout (5 minutes). */\nexport const DEFAULT_ASK_TIMEOUT_MS = 5 * 60 * 1000;\n\nfunction buildCancelledAnswers(questions: AskQuestion[]): AskAnswers {\n const out: AskAnswers = {};\n for (const q of questions) {\n out[q.id] = CANCELLED_SENTINEL;\n }\n return out;\n}\n\nexport function isFullyCancelled(answers: AskAnswers): boolean {\n const values = Object.values(answers);\n if (values.length === 0) return false;\n return values.every((v) => v === CANCELLED_SENTINEL);\n}\n\nexport function createWizardAskBridge(\n opts: WizardAskBridgeOptions,\n): WizardAskBridge {\n const timeoutMs = opts.timeoutMs ?? DEFAULT_ASK_TIMEOUT_MS;\n\n return {\n async request({ questions }) {\n const pending: PendingQuestion = {\n id: randomUUID(),\n questions,\n source: opts.getSource(),\n richLinks: opts.richLinks ?? false,\n };\n\n const startedAt = Date.now();\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n // Race the user against the timeout. Whichever fires first wins; the\n // other branch is harmless because the overlay still resolves via the\n // store when the user eventually submits (and the answers are simply\n // discarded).\n const timeoutPromise = new Promise<AskAnswers>((resolve) => {\n timer = setTimeout(() => {\n resolve(buildCancelledAnswers(questions));\n }, timeoutMs);\n });\n\n try {\n const answers = await Promise.race([\n opts.showQuestion(pending),\n timeoutPromise,\n ]);\n const durationMs = Date.now() - startedAt;\n\n if (isFullyCancelled(answers)) {\n analytics.wizardCapture('wizard_ask cancelled', {\n source: pending.source,\n question_count: questions.length,\n duration_ms: durationMs,\n timed_out: durationMs >= timeoutMs,\n });\n } else {\n analytics.wizardCapture('wizard_ask answered', {\n source: pending.source,\n question_count: questions.length,\n duration_ms: durationMs,\n });\n }\n\n return answers;\n } finally {\n if (timer) clearTimeout(timer);\n }\n },\n };\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 { unzipSync } from 'fflate';\nimport { z } from 'zod';\nimport { logToFile } from '@utils/debug';\nimport { analytics } from '@utils/analytics';\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, isFullyCancelled } from './wizard-ask-bridge';\nimport { createSecretVault, type SecretVault } from './secret-vault';\nimport {\n buildOrchestratorTools,\n type OrchestratorToolsContext,\n} from './agent/runner/sequence/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/** Extract a zip buffer, refusing entries that escape destDir (zip-slip). */\nfunction extractZipArchive(zip: Uint8Array, destDir: string): number {\n const root = path.resolve(destDir);\n let written = 0;\n for (const [entryPath, data] of Object.entries(unzipSync(zip))) {\n const target = path.resolve(root, entryPath);\n if (target !== root && !target.startsWith(root + path.sep)) {\n throw new Error(`zip entry escapes destination: ${entryPath}`);\n }\n if (entryPath.endsWith('/')) {\n fs.mkdirSync(target, { recursive: true });\n continue;\n }\n fs.mkdirSync(path.dirname(target), { recursive: true });\n fs.writeFileSync(target, data);\n written++;\n }\n return written;\n}\n\nconst DOWNLOAD_TIMEOUT_MS = 60000; // per attempt\nconst DOWNLOAD_MAX_ATTEMPTS = 3;\nconst DOWNLOAD_BACKOFF_MS = 500; // doubles each retry\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Download a URL to a buffer, retrying transient failures with backoff. */\nasync function downloadWithRetry(\n url: string,\n opts: {\n fetchImpl?: typeof fetch;\n sleepImpl?: (ms: number) => Promise<void>;\n timeoutMs?: number;\n maxAttempts?: number;\n backoffMs?: number;\n } = {},\n): Promise<Uint8Array> {\n const {\n fetchImpl = fetch,\n sleepImpl = sleep,\n timeoutMs = DOWNLOAD_TIMEOUT_MS,\n maxAttempts = DOWNLOAD_MAX_ATTEMPTS,\n backoffMs = DOWNLOAD_BACKOFF_MS,\n } = opts;\n\n const failures: string[] = [];\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const resp = await fetchImpl(url, {\n signal: AbortSignal.timeout(timeoutMs),\n });\n if (!resp.ok) throw new Error(`HTTP ${resp.status} ${resp.statusText}`);\n return new Uint8Array(await resp.arrayBuffer());\n } catch (err: any) {\n failures.push(`attempt ${attempt}: ${err.message}`);\n if (attempt < maxAttempts) {\n await sleepImpl(backoffMs * 2 ** (attempt - 1));\n }\n }\n }\n throw new Error(`download failed — ${failures.join('; ')}`);\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 async function downloadSkill(\n skillEntry: SkillEntry,\n installDir: string,\n skillsRoot?: string,\n): Promise<{ success: boolean; error?: string }> {\n const skillDir = skillsRoot\n ? path.join(installDir, skillsRoot, skillEntry.id)\n : path.join(installDir, '.claude', 'skills', skillEntry.id);\n let step: 'download' | 'extract' = 'download';\n\n try {\n fs.mkdirSync(skillDir, { recursive: true });\n const zip = await downloadWithRetry(skillEntry.downloadUrl);\n step = 'extract';\n const fileCount = extractZipArchive(zip, skillDir);\n fs.writeFileSync(path.join(skillDir, '.posthog-wizard'), '');\n\n logToFile(\n `downloadSkill: installed ${skillEntry.id} from ${skillEntry.downloadUrl} (${fileCount} files)`,\n );\n return { success: true };\n } catch (err: any) {\n logToFile(`downloadSkill: error: ${err.message}`);\n // A skill-less run still reports success — keep the failure visible.\n analytics.wizardCapture('skill install failed', {\n skill_id: skillEntry.id,\n step,\n platform: process.platform,\n error: String(err.message).slice(0, 500),\n });\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 = await 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 async (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 = await 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 // The agent only sees a tool-result string — report the failure too.\n analytics.captureException(\n new Error('Skill install failed: download-failed'),\n {\n source: 'install_skill_tool',\n skill_id: args.skillId,\n error_detail: String(result.error).slice(0, 500),\n platform: process.platform,\n },\n );\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. The secretRef is only resolved by wizard-tools that accept it (e.g. set_env_values) — it is NOT resolved when passed to other MCP tools (e.g. PostHog data-warehouse tools), which will reject it. For a secret that must reach another tool, write it to the env with set_env_values first, or use that tool's own credential-reference flow.\",\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 (up to 8) rather than asking one at a ' +\n 'time; sequential calls are fine when later questions genuinely depend on earlier ' +\n 'answers. A fully cancelled or timed-out response does NOT count against the per-run ' +\n 'cap — treat it as \"the user declined\" and fall back gracefully (e.g. hand over a ' +\n 'deep link) without worrying about a wasted call.',\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 // A fully cancelled/timed-out ask (the user dismissed the overlay or let\n // it time out) shouldn't burn the per-run cap. Otherwise one cancellation\n // exhausts the budget for every remaining source and forces a deep-link\n // fallback even when the user was willing to answer. Refund the slot we\n // optimistically took so cancellation is free.\n if (isFullyCancelled(answers)) {\n askCallCount -= 1;\n }\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 extractZipArchive,\n downloadWithRetry,\n writeLedgerAtomic,\n readLedger,\n applyAuditAdditions,\n appendAuditChecksToLedger,\n applyAuditUpdates,\n makeMutex,\n AUDIT_CHECKS_FILE,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"wizard-tools-BBLQk1YC.js","names":["path"],"sources":["../src/utils/atomic-ledger.ts","../src/lib/wizard-ask-bridge.ts","../src/lib/secret-vault.ts","../src/lib/agent/runner/sequence/orchestrator/queue.ts","../src/lib/agent/runner/sequence/orchestrator/queue-tools.ts","../src/lib/wizard-tools.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","/**\n * WizardAskBridge — host-side promise broker for the `wizard_ask` MCP tool.\n *\n * The `wizard_ask` tool needs to (a) read information from the wizard\n * session (the active skill id, used as the analytics `source`) and\n * (b) drive the TUI overlay. Wiring `wizard-tools.ts` directly to either\n * would couple our pure-data MCP server to the runtime UI layer.\n *\n * The bridge is the seam: `wizard-tools.ts` depends on this interface,\n * and `agent-runner.ts` constructs an implementation that knows about\n * both the session and `getUI()`.\n */\nimport { randomUUID } from 'crypto';\n\nimport { analytics } from '@utils/analytics';\nimport type {\n AskAnswers,\n AskQuestion,\n PendingQuestion,\n} from './wizard-session';\n\nexport interface WizardAskRequest {\n questions: AskQuestion[];\n}\n\nexport interface WizardAskBridge {\n /**\n * Open the WizardAsk overlay and resolve with the user's answers.\n * One answer per question id (string for `single`/`text`, string[] for\n * `multi`). Cancelled fields come back as the literal `\"__cancelled__\"`.\n */\n request(req: WizardAskRequest): Promise<AskAnswers>;\n}\n\nexport interface WizardAskBridgeOptions {\n /** Returns the active skill id, used as the analytics `source` on the request. */\n getSource: () => string;\n /** Opens the overlay and resolves once the user submits or cancels. */\n showQuestion: (question: PendingQuestion) => Promise<AskAnswers>;\n /**\n * Per-question timeout in milliseconds. When the user takes longer than\n * this to answer, every unanswered field resolves with the\n * {@link CANCELLED_SENTINEL} value. Defaults to {@link DEFAULT_ASK_TIMEOUT_MS}.\n */\n timeoutMs?: number;\n /**\n * Opt the rendered overlay into rich link handling (OSC 8 hyperlinks +\n * clipboard copy for prompt URLs). Set per program; defaults to false.\n * Propagated onto every {@link PendingQuestion} this bridge creates.\n */\n richLinks?: boolean;\n}\n\n/** Sentinel returned for unanswered fields on cancellation or timeout. */\nexport const CANCELLED_SENTINEL = '__cancelled__';\n\n/** Default per-question timeout (5 minutes). */\nexport const DEFAULT_ASK_TIMEOUT_MS = 5 * 60 * 1000;\n\nfunction buildCancelledAnswers(questions: AskQuestion[]): AskAnswers {\n const out: AskAnswers = {};\n for (const q of questions) {\n out[q.id] = CANCELLED_SENTINEL;\n }\n return out;\n}\n\nexport function isFullyCancelled(answers: AskAnswers): boolean {\n const values = Object.values(answers);\n if (values.length === 0) return false;\n return values.every((v) => v === CANCELLED_SENTINEL);\n}\n\nexport function createWizardAskBridge(\n opts: WizardAskBridgeOptions,\n): WizardAskBridge {\n const timeoutMs = opts.timeoutMs ?? DEFAULT_ASK_TIMEOUT_MS;\n\n return {\n async request({ questions }) {\n const pending: PendingQuestion = {\n id: randomUUID(),\n questions,\n source: opts.getSource(),\n richLinks: opts.richLinks ?? false,\n };\n\n const startedAt = Date.now();\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n // Race the user against the timeout. Whichever fires first wins; the\n // other branch is harmless because the overlay still resolves via the\n // store when the user eventually submits (and the answers are simply\n // discarded).\n const timeoutPromise = new Promise<AskAnswers>((resolve) => {\n timer = setTimeout(() => {\n resolve(buildCancelledAnswers(questions));\n }, timeoutMs);\n });\n\n try {\n const answers = await Promise.race([\n opts.showQuestion(pending),\n timeoutPromise,\n ]);\n const durationMs = Date.now() - startedAt;\n\n if (isFullyCancelled(answers)) {\n analytics.wizardCapture('wizard_ask cancelled', {\n source: pending.source,\n question_count: questions.length,\n duration_ms: durationMs,\n timed_out: durationMs >= timeoutMs,\n });\n } else {\n analytics.wizardCapture('wizard_ask answered', {\n source: pending.source,\n question_count: questions.length,\n duration_ms: durationMs,\n });\n }\n\n return answers;\n } finally {\n if (timer) clearTimeout(timer);\n }\n },\n };\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 { unzipSync } from 'fflate';\nimport { z } from 'zod';\nimport { logToFile } from '@utils/debug';\nimport { analytics } from '@utils/analytics';\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, isFullyCancelled } from './wizard-ask-bridge';\nimport { createSecretVault, type SecretVault } from './secret-vault';\nimport {\n buildOrchestratorTools,\n type OrchestratorToolsContext,\n} from './agent/runner/sequence/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/** Extract a zip buffer, refusing entries that escape destDir (zip-slip). */\nfunction extractZipArchive(zip: Uint8Array, destDir: string): number {\n const root = path.resolve(destDir);\n let written = 0;\n for (const [entryPath, data] of Object.entries(unzipSync(zip))) {\n const target = path.resolve(root, entryPath);\n if (target !== root && !target.startsWith(root + path.sep)) {\n throw new Error(`zip entry escapes destination: ${entryPath}`);\n }\n if (entryPath.endsWith('/')) {\n fs.mkdirSync(target, { recursive: true });\n continue;\n }\n fs.mkdirSync(path.dirname(target), { recursive: true });\n fs.writeFileSync(target, data);\n written++;\n }\n return written;\n}\n\nconst DOWNLOAD_TIMEOUT_MS = 60000; // per attempt\nconst DOWNLOAD_MAX_ATTEMPTS = 3;\nconst DOWNLOAD_BACKOFF_MS = 500; // doubles each retry\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Download a URL to a buffer, retrying transient failures with backoff. */\nasync function downloadWithRetry(\n url: string,\n opts: {\n fetchImpl?: typeof fetch;\n sleepImpl?: (ms: number) => Promise<void>;\n timeoutMs?: number;\n maxAttempts?: number;\n backoffMs?: number;\n } = {},\n): Promise<Uint8Array> {\n const {\n fetchImpl = fetch,\n sleepImpl = sleep,\n timeoutMs = DOWNLOAD_TIMEOUT_MS,\n maxAttempts = DOWNLOAD_MAX_ATTEMPTS,\n backoffMs = DOWNLOAD_BACKOFF_MS,\n } = opts;\n\n const failures: string[] = [];\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const resp = await fetchImpl(url, {\n signal: AbortSignal.timeout(timeoutMs),\n });\n if (!resp.ok) throw new Error(`HTTP ${resp.status} ${resp.statusText}`);\n return new Uint8Array(await resp.arrayBuffer());\n } catch (err: any) {\n failures.push(`attempt ${attempt}: ${err.message}`);\n if (attempt < maxAttempts) {\n await sleepImpl(backoffMs * 2 ** (attempt - 1));\n }\n }\n }\n throw new Error(`download failed — ${failures.join('; ')}`);\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 async function downloadSkill(\n skillEntry: SkillEntry,\n installDir: string,\n skillsRoot?: string,\n): Promise<{ success: boolean; error?: string }> {\n const skillDir = skillsRoot\n ? path.join(installDir, skillsRoot, skillEntry.id)\n : path.join(installDir, '.claude', 'skills', skillEntry.id);\n let step: 'download' | 'extract' = 'download';\n\n try {\n fs.mkdirSync(skillDir, { recursive: true });\n const zip = await downloadWithRetry(skillEntry.downloadUrl);\n step = 'extract';\n const fileCount = extractZipArchive(zip, skillDir);\n fs.writeFileSync(path.join(skillDir, '.posthog-wizard'), '');\n\n logToFile(\n `downloadSkill: installed ${skillEntry.id} from ${skillEntry.downloadUrl} (${fileCount} files)`,\n );\n return { success: true };\n } catch (err: any) {\n logToFile(`downloadSkill: error: ${err.message}`);\n // A skill-less run still reports success — keep the failure visible.\n analytics.wizardCapture('skill install failed', {\n skill_id: skillEntry.id,\n step,\n platform: process.platform,\n error: String(err.message).slice(0, 500),\n });\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 = await 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 async (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 = await 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 // The agent only sees a tool-result string — report the failure too.\n analytics.captureException(\n new Error('Skill install failed: download-failed'),\n {\n source: 'install_skill_tool',\n skill_id: args.skillId,\n error_detail: String(result.error).slice(0, 500),\n platform: process.platform,\n },\n );\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. The secretRef is only resolved by wizard-tools that accept it (e.g. set_env_values) — it is NOT resolved when passed to other MCP tools (e.g. PostHog data-warehouse tools), which will reject it. For a secret that must reach another tool, write it to the env with set_env_values first, or use that tool's own credential-reference flow.\",\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 (up to 8) rather than asking one at a ' +\n 'time; sequential calls are fine when later questions genuinely depend on earlier ' +\n 'answers. A fully cancelled or timed-out response does NOT count against the per-run ' +\n 'cap — treat it as \"the user declined\" and fall back gracefully (e.g. hand over a ' +\n 'deep link) without worrying about a wasted call.',\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 // A fully cancelled/timed-out ask (the user dismissed the overlay or let\n // it time out) shouldn't burn the per-run cap. Otherwise one cancellation\n // exhausts the budget for every remaining source and forces a deep-link\n // fallback even when the user was willing to answer. Refund the slot we\n // optimistically took so cancellation is free.\n if (isFullyCancelled(answers)) {\n askCallCount -= 1;\n }\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 extractZipArchive,\n downloadWithRetry,\n writeLedgerAtomic,\n readLedger,\n applyAuditAdditions,\n appendAuditChecksToLedger,\n applyAuditUpdates,\n makeMutex,\n AUDIT_CHECKS_FILE,\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"wizard-ui-WZ48rUgr.js","names":[],"sources":["../src/ui/wizard-ui.ts"],"sourcesContent":["/**\n * WizardUI — abstraction layer for all user-facing operations.\n *\n * Business logic calls `getUI()` instead of importing the store directly.\n * Implementations: InkUI (TUI), LoggingUI (CI).\n *\n * No prompt methods — the TUI screens own all user input.\n * Session-mutating methods trigger reactive screen resolution in the TUI.\n */\n\nimport type { SettingsConflict } from '@lib/agent/claude-settings';\nimport type { WizardReadinessResult } from '@lib/health-checks/readiness';\nimport type { ApiUser } from '@lib/api';\nimport type {\n AskAnswers,\n OutroData,\n PendingQuestion,\n} from '@lib/wizard-session';\n\nexport enum TaskStatus {\n Pending = 'pending',\n InProgress = 'in_progress',\n Completed = 'completed',\n Skipped = 'skipped',\n}\n\nexport function isTaskStatus(value: string): value is TaskStatus {\n return (Object.values(TaskStatus) as string[]).includes(value);\n}\n\n/**\n * One assistant turn's token usage, for the hidden Ctrl+T token/cost HUD.\n * `model` is the model that produced *this* turn (e.g. the SDK's\n * `message.message.model`) — a subagent can run on a different model than\n * the main session, and some programs override to Haiku, so pricing must key\n * off the per-turn model rather than a single run-wide assumption. Omit only\n * when the caller genuinely has no model context (falls back to Sonnet\n * pricing — see `pricePerMtokForModel` in `@lib/agent/token-pricing`).\n */\nexport interface TokenUsageDelta {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheCreationTokens: number;\n cacheCreation5m: number;\n cacheCreation1h: number;\n model?: string;\n}\n\nexport interface SpinnerHandle {\n start(message?: string): void;\n stop(message?: string): void;\n message(msg?: string): void;\n}\n\n/**\n * Context passed to `showAuthError` so the screen can pick the right copy.\n *\n * `hasSettingsConflict` is true when a Claude Code settings file (project,\n * project-local, the user's global config, or managed) actually overrides the\n * LLM Gateway auth. `conflicts` carries the exact files and keys so the screen\n * can name them. When there is no conflict, the 401 has a different cause (bad\n * PAT prefix, missing scope, expired key, region mismatch) and we should not\n * advise the user to log out of Claude Code.\n */\nexport interface AuthErrorDetail {\n hasSettingsConflict: boolean;\n conflicts?: SettingsConflict[];\n /**\n * True when the agent SDK authenticated from a stored Claude login\n * (`apiKeySource: \"/login managed key\"`) instead of the wizard's gateway\n * token — conflicting Anthropic credentials. Takes priority in the screen.\n */\n usingManagedLogin?: boolean;\n /** Human-readable places a conflicting Anthropic credential may live. */\n credentialPlaces?: string[];\n logFilePath: string;\n}\n\nexport interface WizardUI {\n // ── Lifecycle messages ────────────────────────────────────────────\n intro(message: string): void;\n /** Success outro with a plain text message. */\n outro(message: string): void;\n /**\n * Error outro. Sets structured outroData and transitions run phase so\n * the router advances to the outro screen. Use for abort/failure paths\n * that need a custom error render — do NOT build the outroData by\n * mutating session directly (nanostore holds a shallow copy).\n */\n outroError(data: OutroData): void;\n /** Resolves when the user dismisses the outro screen (presses any key).\n * Lets the abort path wait for the user to read the error before the\n * process exits. Resolves immediately in non-TUI environments. */\n waitForOutroDismissed(): Promise<void>;\n cancel(message: string): void;\n\n // ── Logging ───────────────────────────────────────────────────────\n log: {\n info(message: string): void;\n warn(message: string): void;\n error(message: string): void;\n success(message: string): void;\n step(message: string): void;\n };\n\n note(message: string): void;\n pushStatus(message: string): void;\n\n // ── Spinner ───────────────────────────────────────────────────────\n spinner(): SpinnerHandle;\n\n // ── Session state (triggers reactive screen resolution in TUI) ────\n /** Signal that the main work (agent run) has started. */\n startRun(): void;\n\n /** Store OAuth/API credentials. Resolves past AuthScreen in TUI. */\n setCredentials(credentials: {\n accessToken: string;\n projectApiKey: string;\n host: string;\n projectId: number;\n }): void;\n\n /**\n * Persist the user's `role_at_organization` once it's been fetched from\n * `/api/users/@me/`. Drives role-tailored prompt suggestions on the\n * McpSuggestedPromptsScreen. Pass `null` to clear / when unknown.\n */\n setRoleAtOrganization(role: string | null): void;\n\n /**\n * Persist the full user payload from `/api/users/@me/` so downstream\n * screens can read account context (current org, team, plan, email,\n * preferences, etc.) without re-fetching. Pass `null` to clear or\n * when the request failed.\n */\n setApiUser(user: ApiUser | null): void;\n\n /**\n * Park until the org's AI opt-in gate clears\n * (`organization.is_ai_data_processing_approved === true`, or the\n * program never registered the gate — requiresAi: false / no auth\n * step / CI session). The agent runner awaits this after setApiUser\n * and BEFORE skill install or agent start: this is the enforcement\n * point that keeps source on the machine while the TUI shows\n * AiOptInRequiredScreen. Resolves immediately in non-TUI\n * environments (CI auto-consents to AI usage).\n */\n waitForAiOptIn(): Promise<void>;\n\n /** Show blocking service outage (pushes outage overlay in TUI). Blocks until dismissed. */\n showBlockingOutage(result: WizardReadinessResult): Promise<void>;\n\n /** Store non-blocking readiness warnings (shown as Health tab in RunScreen). */\n setReadinessWarnings(result: WizardReadinessResult): void;\n\n /** Warn that another process is blocking the OAuth port (pushes overlay in TUI). */\n showPortConflict(processInfo: {\n command: string;\n pid: string;\n port: number;\n user: string;\n }): Promise<void>;\n\n /**\n * Resolve with an OAuth authorization code the user enters by hand — the\n * fallback for headless/remote shells where the browser can't reach the\n * local callback server. The OAuth flow races this against the callback\n * server. Implementations that can't prompt (CI/logging) never resolve.\n */\n waitForManualAuthCode(): Promise<string>;\n\n showSettingsOverride(\n conflicts: SettingsConflict[],\n backupAndFix: () => boolean,\n ): Promise<void>;\n\n /** Show auth error overlay when Anthropic API returns 401. */\n showAuthError(detail?: AuthErrorDetail): void;\n\n /** Show the session-timeout overlay when the OAuth login window expires. */\n showSessionTimeout(): void;\n\n /**\n * Open the wizard_ask overlay and resolve with the user's answers.\n * Implementations that can't ask (CI/logging) reject so the bridge can\n * surface a clear \"not available\" error to the agent.\n */\n requestQuestion(question: PendingQuestion): Promise<AskAnswers>;\n\n // ── Display state ──────────────────────────────────────────────────\n /** Set the detected framework label (e.g., \"Django with Wagtail CMS\") */\n setDetectedFramework(label: string): void;\n\n /** Register a callback to run when the TUI transitions onto the given screen. */\n onEnterScreen(screen: string, fn: () => void): void;\n\n setLoginUrl(url: string | null): void;\n\n /** Direct PostHog authorize URL, shown in the manual-paste modal. */\n setAuthorizeUrl(url: string | null): void;\n\n // ── Task tracking from SDK TaskCreate/TaskUpdate events ───────────\n // Receives the full materialised task list each call. The caller (agent\n // loop) maintains a Map<taskId, …> from incremental Task* events and\n // re-emits the snapshot here, preserving the existing store semantics.\n syncTodos(\n todos: Array<{ content: string; status: string; activeForm?: string }>,\n ): void;\n\n // ── Event plan from .posthog-events.json ────────────────────\n setEventPlan(events: Array<{ name: string; description: string }>): void;\n\n // ── Dashboard URL emitted by the agent via [DASHBOARD_URL] marker ──\n setDashboardUrl(url: string): void;\n\n /** Current \"stage of work\" — derived from the active tool call. Drives the\n * Visualizer tab's NOW PLAYING display. Pass an AgentPhase value. */\n setStage(stage: string): void;\n\n // ── Notebook URL emitted by the agent via [NOTEBOOK_URL] marker ──\n setNotebookUrl(url: string): void;\n\n /** Accumulate one assistant turn's token usage into the hidden Ctrl+T\n * token/cost HUD's running estimate. No-op outside the TUI. */\n addTokenUsage(delta: TokenUsageDelta): void;\n\n /** Reconcile the HUD's running cost estimate to the SDK's authoritative\n * `total_cost_usd` once the agent run completes. No-op outside the TUI. */\n setFinalTokenCostUsd(costUsd: number): void;\n\n // ── Outro payload built by agent-runner ──\n // Replaces the direct `session.outroData = X` mutation that breaks once\n // setKey-based store mutations have forked the session reference.\n setOutroData(data: OutroData): void;\n\n // ── Generic frameworkContext setter for program file watchers ─────\n setFrameworkContext(key: string, value: unknown): void;\n\n /** Read a frameworkContext value from the LIVE session (store may have\n * forked the reference the runner holds). Used by run configs to read\n * values written by post-auth screens (e.g. the source-maps picker). */\n getFrameworkContext(key: string): unknown;\n\n /** Park until the named program step's gate predicate flips true. Resolves\n * immediately if the step has no gate. Mirrors waitForAiOptIn for any\n * post-auth interactive step the agent run must wait on. */\n waitForGate(stepId: string): Promise<void>;\n}\n"],"mappings":";AAmBA,IAAY,aAAL,yBAAA,YAAA;AACL,YAAA,aAAA;AACA,YAAA,gBAAA;AACA,YAAA,eAAA;AACA,YAAA,aAAA;;KACD;AAED,SAAgB,aAAa,OAAoC;AAC/D,QAAQ,OAAO,OAAO,WAAW,CAAc,SAAS,MAAM"}
1
+ {"version":3,"file":"wizard-ui-WZ48rUgr.js","names":[],"sources":["../src/ui/wizard-ui.ts"],"sourcesContent":["/**\n * WizardUI — abstraction layer for all user-facing operations.\n *\n * Business logic calls `getUI()` instead of importing the store directly.\n * Implementations: InkUI (TUI), LoggingUI (CI).\n *\n * No prompt methods — the TUI screens own all user input.\n * Session-mutating methods trigger reactive screen resolution in the TUI.\n */\n\nimport type { SettingsConflict } from '@lib/agent/claude-settings';\nimport type { WizardReadinessResult } from '@lib/health-checks/readiness';\nimport type { ApiUser } from '@lib/api';\nimport type { Credentials } from '@lib/wizard-session';\nimport type {\n AskAnswers,\n OutroData,\n PendingQuestion,\n} from '@lib/wizard-session';\n\nexport enum TaskStatus {\n Pending = 'pending',\n InProgress = 'in_progress',\n Completed = 'completed',\n Skipped = 'skipped',\n}\n\nexport function isTaskStatus(value: string): value is TaskStatus {\n return (Object.values(TaskStatus) as string[]).includes(value);\n}\n\n/**\n * One assistant turn's token usage, for the hidden Ctrl+T token/cost HUD.\n * `model` is the model that produced *this* turn (e.g. the SDK's\n * `message.message.model`) — a subagent can run on a different model than\n * the main session, and some programs override to Haiku, so pricing must key\n * off the per-turn model rather than a single run-wide assumption. Omit only\n * when the caller genuinely has no model context (falls back to Sonnet\n * pricing — see `pricePerMtokForModel` in `@lib/agent/token-pricing`).\n */\nexport interface TokenUsageDelta {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheCreationTokens: number;\n cacheCreation5m: number;\n cacheCreation1h: number;\n model?: string;\n}\n\nexport interface SpinnerHandle {\n start(message?: string): void;\n stop(message?: string): void;\n message(msg?: string): void;\n}\n\n/**\n * Context passed to `showAuthError` so the screen can pick the right copy.\n *\n * `hasSettingsConflict` is true when a Claude Code settings file (project,\n * project-local, the user's global config, or managed) actually overrides the\n * LLM Gateway auth. `conflicts` carries the exact files and keys so the screen\n * can name them. When there is no conflict, the 401 has a different cause (bad\n * PAT prefix, missing scope, expired key, region mismatch) and we should not\n * advise the user to log out of Claude Code.\n */\nexport interface AuthErrorDetail {\n hasSettingsConflict: boolean;\n conflicts?: SettingsConflict[];\n /**\n * True when the agent SDK authenticated from a stored Claude login\n * (`apiKeySource: \"/login managed key\"`) instead of the wizard's gateway\n * token — conflicting Anthropic credentials. Takes priority in the screen.\n */\n usingManagedLogin?: boolean;\n /** Human-readable places a conflicting Anthropic credential may live. */\n credentialPlaces?: string[];\n logFilePath: string;\n}\n\nexport interface WizardUI {\n // ── Lifecycle messages ────────────────────────────────────────────\n intro(message: string): void;\n /** Success outro with a plain text message. */\n outro(message: string): void;\n /**\n * Error outro. Sets structured outroData and transitions run phase so\n * the router advances to the outro screen. Use for abort/failure paths\n * that need a custom error render — do NOT build the outroData by\n * mutating session directly (nanostore holds a shallow copy).\n */\n outroError(data: OutroData): void;\n /** Resolves when the user dismisses the outro screen (presses any key).\n * Lets the abort path wait for the user to read the error before the\n * process exits. Resolves immediately in non-TUI environments. */\n waitForOutroDismissed(): Promise<void>;\n cancel(message: string): void;\n\n // ── Logging ───────────────────────────────────────────────────────\n log: {\n info(message: string): void;\n warn(message: string): void;\n error(message: string): void;\n success(message: string): void;\n step(message: string): void;\n };\n\n note(message: string): void;\n pushStatus(message: string): void;\n\n // ── Spinner ───────────────────────────────────────────────────────\n spinner(): SpinnerHandle;\n\n // ── Session state (triggers reactive screen resolution in TUI) ────\n /** Signal that the main work (agent run) has started. */\n startRun(): void;\n\n /** Store OAuth/API credentials. Resolves past AuthScreen in TUI. */\n setCredentials(credentials: Credentials): void;\n\n /**\n * Persist the user's `role_at_organization` once it's been fetched from\n * `/api/users/@me/`. Drives role-tailored prompt suggestions on the\n * McpSuggestedPromptsScreen. Pass `null` to clear / when unknown.\n */\n setRoleAtOrganization(role: string | null): void;\n\n /**\n * Persist the full user payload from `/api/users/@me/` so downstream\n * screens can read account context (current org, team, plan, email,\n * preferences, etc.) without re-fetching. Pass `null` to clear or\n * when the request failed.\n */\n setApiUser(user: ApiUser | null): void;\n\n /**\n * Park until the org's AI opt-in gate clears\n * (`organization.is_ai_data_processing_approved === true`, or the\n * program never registered the gate — requiresAi: false / no auth\n * step / CI session). The agent runner awaits this after setApiUser\n * and BEFORE skill install or agent start: this is the enforcement\n * point that keeps source on the machine while the TUI shows\n * AiOptInRequiredScreen. Resolves immediately in non-TUI\n * environments (CI auto-consents to AI usage).\n */\n waitForAiOptIn(): Promise<void>;\n\n /** Show blocking service outage (pushes outage overlay in TUI). Blocks until dismissed. */\n showBlockingOutage(result: WizardReadinessResult): Promise<void>;\n\n /** Store non-blocking readiness warnings (shown as Health tab in RunScreen). */\n setReadinessWarnings(result: WizardReadinessResult): void;\n\n /** Warn that another process is blocking the OAuth port (pushes overlay in TUI). */\n showPortConflict(processInfo: {\n command: string;\n pid: string;\n port: number;\n user: string;\n }): Promise<void>;\n\n /**\n * Resolve with an OAuth authorization code the user enters by hand — the\n * fallback for headless/remote shells where the browser can't reach the\n * local callback server. The OAuth flow races this against the callback\n * server. Implementations that can't prompt (CI/logging) never resolve.\n */\n waitForManualAuthCode(): Promise<string>;\n\n showSettingsOverride(\n conflicts: SettingsConflict[],\n backupAndFix: () => boolean,\n ): Promise<void>;\n\n /** Show auth error overlay when Anthropic API returns 401. */\n showAuthError(detail?: AuthErrorDetail): void;\n\n /** Show the session-timeout overlay when the OAuth login window expires. */\n showSessionTimeout(): void;\n\n /**\n * Open the wizard_ask overlay and resolve with the user's answers.\n * Implementations that can't ask (CI/logging) reject so the bridge can\n * surface a clear \"not available\" error to the agent.\n */\n requestQuestion(question: PendingQuestion): Promise<AskAnswers>;\n\n // ── Display state ──────────────────────────────────────────────────\n /** Set the detected framework label (e.g., \"Django with Wagtail CMS\") */\n setDetectedFramework(label: string): void;\n\n /** Register a callback to run when the TUI transitions onto the given screen. */\n onEnterScreen(screen: string, fn: () => void): void;\n\n setLoginUrl(url: string | null): void;\n\n /** Direct PostHog authorize URL, shown in the manual-paste modal. */\n setAuthorizeUrl(url: string | null): void;\n\n // ── Task tracking from SDK TaskCreate/TaskUpdate events ───────────\n // Receives the full materialised task list each call. The caller (agent\n // loop) maintains a Map<taskId, …> from incremental Task* events and\n // re-emits the snapshot here, preserving the existing store semantics.\n syncTodos(\n todos: Array<{ content: string; status: string; activeForm?: string }>,\n ): void;\n\n // ── Event plan from .posthog-events.json ────────────────────\n setEventPlan(events: Array<{ name: string; description: string }>): void;\n\n // ── Dashboard URL emitted by the agent via [DASHBOARD_URL] marker ──\n setDashboardUrl(url: string): void;\n\n /** Current \"stage of work\" — derived from the active tool call. Drives the\n * Visualizer tab's NOW PLAYING display. Pass an AgentPhase value. */\n setStage(stage: string): void;\n\n // ── Notebook URL emitted by the agent via [NOTEBOOK_URL] marker ──\n setNotebookUrl(url: string): void;\n\n /** Accumulate one assistant turn's token usage into the hidden Ctrl+T\n * token/cost HUD's running estimate. No-op outside the TUI. */\n addTokenUsage(delta: TokenUsageDelta): void;\n\n /** Reconcile the HUD's running cost estimate to the SDK's authoritative\n * `total_cost_usd` once the agent run completes. No-op outside the TUI. */\n setFinalTokenCostUsd(costUsd: number): void;\n\n // ── Outro payload built by agent-runner ──\n // Replaces the direct `session.outroData = X` mutation that breaks once\n // setKey-based store mutations have forked the session reference.\n setOutroData(data: OutroData): void;\n\n // ── Generic frameworkContext setter for program file watchers ─────\n setFrameworkContext(key: string, value: unknown): void;\n\n /** Read a frameworkContext value from the LIVE session (store may have\n * forked the reference the runner holds). Used by run configs to read\n * values written by post-auth screens (e.g. the source-maps picker). */\n getFrameworkContext(key: string): unknown;\n\n /** Park until the named program step's gate predicate flips true. Resolves\n * immediately if the step has no gate. Mirrors waitForAiOptIn for any\n * post-auth interactive step the agent run must wait on. */\n waitForGate(stepId: string): Promise<void>;\n}\n"],"mappings":";AAoBA,IAAY,aAAL,yBAAA,YAAA;AACL,YAAA,aAAA;AACA,YAAA,gBAAA;AACA,YAAA,eAAA;AACA,YAAA,aAAA;;KACD;AAED,SAAgB,aAAa,OAAoC;AAC/D,QAAQ,OAAO,OAAO,WAAW,CAAc,SAAS,MAAM"}
@@ -0,0 +1,2 @@
1
+ import { l as prewarmYaraScanner } from "./yara-hooks-Ciy8d0ok.js";
2
+ export { prewarmYaraScanner };
@@ -1,6 +1,6 @@
1
- import { f as getUI, s as logToFile, u as WIZARD_YARA_REPORT_FILE } from "./debug-D_mboDpg.js";
2
- import { n as analytics } from "./analytics-__4kRqZr.js";
3
- import { r as AUDIT_REPORT_FILE } from "./types-evL_rPob.js";
1
+ import { f as getUI, s as logToFile, u as WIZARD_YARA_REPORT_FILE } from "./debug--QPXcpzv.js";
2
+ import { n as analytics } from "./analytics-jQRtyqzG.js";
3
+ import { r as AUDIT_REPORT_FILE } from "./types-y3HkjtSq.js";
4
4
  import fs from "fs";
5
5
  import path from "path";
6
6
  import fg from "fast-glob";
@@ -632,4 +632,4 @@ async function scanSkillFiles(cwd, skillDir, llmProvider) {
632
632
  //#endregion
633
633
  export { flushScanReport as a, isWizardDocumentationPath as c, repeatBlockReason as d, scanAndTriage as f, SETUP_REPORT_FILE as h, createRepeatBlockTracker as i, prewarmYaraScanner as l, EVENT_PLAN_FILE as m, createPostToolUseYaraHooks as n, formatScanReport as o, writeScanReport as p, createPreToolUseYaraHooks as r, formatYaraAbortMessage as s, captureScanReport as t, recordExternalScan as u };
634
634
 
635
- //# sourceMappingURL=yara-hooks-BPQ3VE-l.js.map
635
+ //# sourceMappingURL=yara-hooks-Ciy8d0ok.js.map