@posthog/wizard 2.24.1 → 2.25.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 (64) hide show
  1. package/dist/{AiOptInRequiredScreen-_33FOcVo.js → AiOptInRequiredScreen-C-D9tN6r.js} +20 -16
  2. package/dist/AiOptInRequiredScreen-C-D9tN6r.js.map +1 -0
  3. package/dist/{add-mcp-server-to-clients-CfwEQT_z.js → add-mcp-server-to-clients-t0xe8gn1.js} +4 -4
  4. package/dist/{add-mcp-server-to-clients-CfwEQT_z.js.map → add-mcp-server-to-clients-t0xe8gn1.js.map} +1 -1
  5. package/dist/{agent-interface-D1vtN6Wn.js → agent-interface-BsuUUPle.js} +403 -40
  6. package/dist/agent-interface-BsuUUPle.js.map +1 -0
  7. package/dist/{agent-runner-CBbkS0Ro.js → agent-runner-L_-kJ3y3.js} +624 -17
  8. package/dist/agent-runner-L_-kJ3y3.js.map +1 -0
  9. package/dist/{analytics-CUr82BDl.js → analytics-CDOujOSQ.js} +51 -17
  10. package/dist/analytics-CDOujOSQ.js.map +1 -0
  11. package/dist/{api-CI3Z74NG.js → api-DNS-L-1U.js} +3 -3
  12. package/dist/{api-CI3Z74NG.js.map → api-DNS-L-1U.js.map} +1 -1
  13. package/dist/bin.js +279 -33
  14. package/dist/bin.js.map +1 -1
  15. package/dist/{ci-install-D_kxNmbJ.js → ci-install-_9A7tL36.js} +4 -4
  16. package/dist/{ci-install-D_kxNmbJ.js.map → ci-install-_9A7tL36.js.map} +1 -1
  17. package/dist/{debug-DxA_f5QT.js → debug-BwC7UkGH.js} +16 -8
  18. package/dist/debug-BwC7UkGH.js.map +1 -0
  19. package/dist/{debug-zMvpNYb2.js → debug-CZQcMAJT.js} +1 -1
  20. package/dist/{environment-CyS37cmM.js → environment-DQPoj9sU.js} +3 -3
  21. package/dist/{environment-CyS37cmM.js.map → environment-DQPoj9sU.js.map} +1 -1
  22. package/dist/{interactive-CG6FFqSw.js → interactive-DT5dLd7N.js} +2 -2
  23. package/dist/{interactive-CG6FFqSw.js.map → interactive-DT5dLd7N.js.map} +1 -1
  24. package/dist/{mcp-prompt-streaming-DQz4FSb1.js → mcp-prompt-streaming-CBMr458Q.js} +7 -26
  25. package/dist/mcp-prompt-streaming-CBMr458Q.js.map +1 -0
  26. package/dist/{non-interactive-DWtHX3ZR.js → non-interactive-csP4yGdA.js} +2 -2
  27. package/dist/{non-interactive-DWtHX3ZR.js.map → non-interactive-csP4yGdA.js.map} +1 -1
  28. package/dist/{package-manager-BWUS4CP0.js → package-manager-CB4c2euf.js} +2 -2
  29. package/dist/{package-manager-BWUS4CP0.js.map → package-manager-CB4c2euf.js.map} +1 -1
  30. package/dist/{playground-D7AhMMF5.js → playground-C-lpKoKC.js} +5 -5
  31. package/dist/{playground-D7AhMMF5.js.map → playground-C-lpKoKC.js.map} +1 -1
  32. package/dist/{posthog-integration-DexZ2uHU.js → posthog-integration-BL8-vC0V.js} +11 -11
  33. package/dist/{posthog-integration-DexZ2uHU.js.map → posthog-integration-BL8-vC0V.js.map} +1 -1
  34. package/dist/{provisioning-9c-AQbsa.js → provisioning-DLOiFSM9.js} +3 -3
  35. package/dist/{provisioning-9c-AQbsa.js.map → provisioning-DLOiFSM9.js.map} +1 -1
  36. package/dist/{registry-CO7JVZyE.js → registry-BbRzCV5l.js} +4 -4
  37. package/dist/{registry-CO7JVZyE.js.map → registry-BbRzCV5l.js.map} +1 -1
  38. package/dist/{setup-utils-0U-_Md2G.js → setup-utils-D87CyNkw.js} +8 -8
  39. package/dist/{setup-utils-0U-_Md2G.js.map → setup-utils-D87CyNkw.js.map} +1 -1
  40. package/dist/smoke-test.sh +36 -1
  41. package/dist/{start-tui-WNb3ET14.js → start-tui-DnAG57vY.js} +13 -13
  42. package/dist/{start-tui-WNb3ET14.js.map → start-tui-DnAG57vY.js.map} +1 -1
  43. package/dist/{steps-BAUXDCC4.js → steps-JaxH6u0f.js} +6 -6
  44. package/dist/{steps-BAUXDCC4.js.map → steps-JaxH6u0f.js.map} +1 -1
  45. package/dist/{task-stream-CZawuzlz.js → task-stream-BQNSp0qR.js} +4 -3
  46. package/dist/task-stream-BQNSp0qR.js.map +1 -0
  47. package/dist/{telemetry-ycqCpNPr.js → telemetry-DL28cCwY.js} +3 -3
  48. package/dist/{telemetry-ycqCpNPr.js.map → telemetry-DL28cCwY.js.map} +1 -1
  49. package/dist/{urls-C8aJWvgh.js → urls-vkJ5c0ix.js} +2 -2
  50. package/dist/{urls-C8aJWvgh.js.map → urls-vkJ5c0ix.js.map} +1 -1
  51. package/dist/{wizard-abort-DWXyJdws.js → wizard-abort-BRXKRL4F.js} +1 -1
  52. package/dist/{wizard-abort-C6gRLxUE.js → wizard-abort-CLGgMAEe.js} +3 -3
  53. package/dist/{wizard-abort-C6gRLxUE.js.map → wizard-abort-CLGgMAEe.js.map} +1 -1
  54. package/dist/{wizard-ui-YdGFRyu_.js → wizard-ui-WZ48rUgr.js} +2 -1
  55. package/dist/wizard-ui-WZ48rUgr.js.map +1 -0
  56. package/package.json +1 -1
  57. package/dist/AiOptInRequiredScreen-_33FOcVo.js.map +0 -1
  58. package/dist/agent-interface-D1vtN6Wn.js.map +0 -1
  59. package/dist/agent-runner-CBbkS0Ro.js.map +0 -1
  60. package/dist/analytics-CUr82BDl.js.map +0 -1
  61. package/dist/debug-DxA_f5QT.js.map +0 -1
  62. package/dist/mcp-prompt-streaming-DQz4FSb1.js.map +0 -1
  63. package/dist/task-stream-CZawuzlz.js.map +0 -1
  64. package/dist/wizard-ui-YdGFRyu_.js.map +0 -1
@@ -1,13 +1,583 @@
1
- import { Q as getSkillsBaseUrl, _ as SIGNUP_WIZARD_READINESS_CONFIG, a as getLogFilePath, c as WIZARD_BENCHMARK_FILE, g as SERVICE_LABELS, i as enableDebugLogs, l as WIZARD_LOG_FILE, o as initLogFile, p as getUI, s as logToFile, t as configureLogFile, tt as runtimeEnv, v as evaluateWizardReadiness, y as getBlockingServiceKeys } from "./debug-DxA_f5QT.js";
2
- import { n as groupsFromUser, t as analytics } from "./analytics-CUr82BDl.js";
3
- import { t as getOrAskForProjectData } from "./setup-utils-0U-_Md2G.js";
4
- import { n as getCloudUrlFromRegion } from "./urls-C8aJWvgh.js";
5
- import { i as wizardAbort, n as registerCleanup, t as WizardError } from "./wizard-abort-C6gRLxUE.js";
6
- import { a as backupAndFixClaudeSettings, c as restoreClaudeSettings, d as writeScanReport, h as installSkillById, i as runAgent$1, l as AgentSignals, n as buildWizardMetadata, o as checkAllSettingsConflicts, r as initializeAgent, u as formatScanReport } from "./agent-interface-D1vtN6Wn.js";
7
- import { r as detectNodePackageManagers } from "./package-manager-BWUS4CP0.js";
8
- import fs from "fs";
1
+ import { $ as getSkillsBaseUrl, _ as SIGNUP_WIZARD_READINESS_CONFIG, a as getLogFilePath, c as WIZARD_BENCHMARK_FILE, g as SERVICE_LABELS, i as enableDebugLogs, l as WIZARD_LOG_FILE, nt as runtimeEnv, o as initLogFile, p as getUI, s as logToFile, t as configureLogFile, v as evaluateWizardReadiness, y as getBlockingServiceKeys } from "./debug-BwC7UkGH.js";
2
+ import { i as ciExcludedTaskTypes, n as groupsFromUser, t as analytics } from "./analytics-CDOujOSQ.js";
3
+ import { t as getOrAskForProjectData } from "./setup-utils-D87CyNkw.js";
4
+ import { n as getCloudUrlFromRegion } from "./urls-vkJ5c0ix.js";
5
+ import { i as wizardAbort, n as registerCleanup, t as WizardError } from "./wizard-abort-CLGgMAEe.js";
6
+ import { n as isNonInteractiveEnvironment } from "./environment-DQPoj9sU.js";
7
+ import { _ as QUEUE_DIR_NAME, a as runAgent$1, d as formatScanReport, f as writeScanReport, g as installSkillById, h as fetchSkillMenu, i as isOrchestratorEnabled, l as restoreClaudeSettings, n as buildWizardMetadata, o as backupAndFixClaudeSettings, r as initializeAgent, s as checkAllSettingsConflicts, u as AgentSignals, v as QueueStore, y as TaskStatus } from "./agent-interface-BsuUUPle.js";
8
+ import { r as detectNodePackageManagers } from "./package-manager-CB4c2euf.js";
9
+ import fs, { existsSync, rmSync } from "fs";
10
+ import * as path$1 from "path";
9
11
  import path from "path";
10
12
  import { randomUUID } from "crypto";
13
+ //#region src/lib/programs/orchestrator/executor.ts
14
+ /**
15
+ * The executor drains the queue. It starts every runnable task (dependencies
16
+ * satisfied) as soon as it becomes runnable — parallelism is decided by the
17
+ * task graph, not by an executor knob. Each task runs through an injected
18
+ * `runTask` function and reports its outcome via `complete_task`; a task that
19
+ * ends without reporting is retried while attempts remain, then failed. A
20
+ * `maxStarts` backstop guarantees termination.
21
+ *
22
+ * The drain loop is independent of how a task actually runs. `runTask` is
23
+ * injected: the real one spins up a fresh agent, the tests use a fake.
24
+ */
25
+ const DEFAULT_DRAIN_OPTIONS = { maxStarts: 200 };
26
+ async function runOne(store, runTask, task) {
27
+ store.start(task.id);
28
+ try {
29
+ await runTask(task);
30
+ } catch (error) {
31
+ logToFile(`[executor] runTask threw for ${task.type}:`, error);
32
+ analytics.captureException(error instanceof Error ? error : new Error(String(error)), {
33
+ step: "orchestrator_run_task",
34
+ task_type: task.type
35
+ });
36
+ }
37
+ const after = store.get(task.id);
38
+ if (!after) return;
39
+ if (after.status === TaskStatus.Running) {
40
+ if (after.attempts < after.maxAttempts) store.requeue(task.id);
41
+ else store.fail(task.id, {
42
+ type: "no-report",
43
+ message: "Task ended without calling complete_task."
44
+ });
45
+ return;
46
+ }
47
+ if (after.status === TaskStatus.Failed && after.attempts < after.maxAttempts) store.requeue(task.id);
48
+ }
49
+ /**
50
+ * Drain the queue to a terminal state. Every runnable task starts the moment
51
+ * its dependencies finish; independent branches run concurrently. Returns when
52
+ * every task is done, failed, or blocked by a failed dependency, or when the
53
+ * start backstop trips.
54
+ */
55
+ async function drainQueue(store, runTask, opts = DEFAULT_DRAIN_OPTIONS) {
56
+ const running = /* @__PURE__ */ new Map();
57
+ let starts = 0;
58
+ for (;;) {
59
+ for (const task of store.nextRunnable()) {
60
+ if (++starts > opts.maxStarts) break;
61
+ const p = runOne(store, runTask, task).finally(() => running.delete(task.id));
62
+ running.set(task.id, p);
63
+ }
64
+ if (running.size === 0) break;
65
+ await Promise.race(running.values());
66
+ }
67
+ }
68
+ //#endregion
69
+ //#region src/lib/programs/orchestrator/run-metrics.ts
70
+ var RunMetrics = class {
71
+ firstStartMs;
72
+ lastStartMs;
73
+ firstCompleteMs;
74
+ lastVisibleMs;
75
+ maxGapMs = 0;
76
+ constructor(runStartMs) {
77
+ this.runStartMs = runStartMs;
78
+ }
79
+ /** A task started. Returns the per-start-event timing for the start event. */
80
+ recordStart(nowMs) {
81
+ const timing = {
82
+ ms_since_run_start: nowMs - this.runStartMs,
83
+ gap_since_prev_start_ms: this.lastStartMs === void 0 ? void 0 : nowMs - this.lastStartMs
84
+ };
85
+ this.firstStartMs ??= nowMs;
86
+ this.lastStartMs = nowMs;
87
+ this.markVisible(nowMs);
88
+ return timing;
89
+ }
90
+ /** A task completed. */
91
+ recordComplete(nowMs) {
92
+ this.firstCompleteMs ??= nowMs;
93
+ this.markVisible(nowMs);
94
+ }
95
+ /** A task reached a terminal non-complete state the user sees (skip/fail). */
96
+ recordTerminal(nowMs) {
97
+ this.markVisible(nowMs);
98
+ }
99
+ /**
100
+ * The run-level responsiveness summary. Timings are `undefined` when the
101
+ * relevant transition never happened (e.g. a run that started no task), so a
102
+ * no-task run stays distinguishable from a genuine zero.
103
+ */
104
+ summary() {
105
+ return {
106
+ time_to_first_task_ms: this.firstStartMs === void 0 ? void 0 : this.firstStartMs - this.runStartMs,
107
+ time_to_first_completion_ms: this.firstCompleteMs === void 0 ? void 0 : this.firstCompleteMs - this.runStartMs,
108
+ max_gap_ms: this.lastVisibleMs === void 0 ? void 0 : this.maxGapMs
109
+ };
110
+ }
111
+ /** requeue is not user-visible, so a retry stall counts as silence here. */
112
+ markVisible(nowMs) {
113
+ if (this.lastVisibleMs !== void 0) this.maxGapMs = Math.max(this.maxGapMs, nowMs - this.lastVisibleMs);
114
+ this.lastVisibleMs = nowMs;
115
+ }
116
+ };
117
+ //#endregion
118
+ //#region src/lib/agent/agent-prompt-loader.ts
119
+ function projectContext(ctx) {
120
+ return `You have access to the PostHog MCP server and the wizard tools.
121
+
122
+ Project context:
123
+ - PostHog Project ID: ${ctx.projectId}
124
+ - PostHog public token: ${ctx.projectApiKey}
125
+ - PostHog Host: ${ctx.host}`;
126
+ }
127
+ /** Points the agent at the framework's reference integration to learn patterns from. */
128
+ function exampleReference(ctx) {
129
+ if (!ctx.examplePath) return null;
130
+ return `A reference PostHog integration for this framework is at \`${ctx.examplePath}\`. It shows the target implementation pattern. Reference its patterns and conventions, adapting them to this codebase.`;
131
+ }
132
+ /** The framework's rules ship with the reference skill; every task follows them. */
133
+ function commandmentsReference(ctx) {
134
+ if (!ctx.commandmentsPath) return null;
135
+ return `Framework rules for this integration are at \`${ctx.commandmentsPath}\`. Read them before you edit and follow them.`;
136
+ }
137
+ const TASK_BASICS = `You are one isolated task in a larger PostHog workflow, run as a fresh agent with no memory of the other tasks beyond the context you are given. Do only your task, then report exactly once by calling complete_task with a structured handoff: what your goal was, what you did, and what the next agent should know. When you are given context from previous steps, trust it — those agents already did their work, so do not re-verify or re-read what their handoffs tell you. Build on it and move fast. Read a file before you edit it, so your own changes do not duplicate what is already there. Work only inside this project's own directory: never read, list, or search (find, ls, grep, glob) outside it — not the OS, not other projects, not global package caches. If your task seems to need something outside this directory, it does not — skip that part and say so in your handoff rather than hunting across the filesystem. If your task does not apply to this project — there is genuinely nothing for it to do — report it with status \`skipped\` and say why, rather than marking it done.`;
138
+ const SEED_BASICS = `You are the orchestrator. Plan the work and seed the queue with enqueue_task — each call returns an id you can pass as a dependency to a later task. Give each task a short label for the UI — the action in a few words, not file names, class names, or other specifics. You are not a task yourself: do not call complete_task and do not edit the project.`;
139
+ /**
140
+ * Points the agent at its installed task instructions (the HOW). They live under
141
+ * the wizard's run dir, not `.claude/skills/`, so the SDK does not auto-load
142
+ * them — the prompt has to name them.
143
+ */
144
+ function skillReference(paths) {
145
+ if (paths.length === 0) return null;
146
+ return `Your task instructions are at ${paths.map((p) => `\`${p}\``).join(", ")}. Read them before you start and follow them. They are wizard scaffolding, not part of the project.`;
147
+ }
148
+ /** A task agent's full prompt: injected basics, then the authored intent. */
149
+ function assembleTaskPrompt(ctx, body, skillPaths = []) {
150
+ return [
151
+ projectContext(ctx),
152
+ exampleReference(ctx),
153
+ commandmentsReference(ctx),
154
+ skillReference(skillPaths),
155
+ TASK_BASICS,
156
+ body
157
+ ].filter(Boolean).join("\n\n");
158
+ }
159
+ /** The seed agent's full prompt: injected basics, then the authored intent. */
160
+ function assembleSeedPrompt(ctx, body) {
161
+ return [
162
+ projectContext(ctx),
163
+ SEED_BASICS,
164
+ body
165
+ ].join("\n\n");
166
+ }
167
+ /** Used when neither the enqueue call nor the prompt frontmatter names a model. */
168
+ const DEFAULT_TASK_MODEL = "claude-sonnet-4-6";
169
+ /** Orchestrator tools are MCP tools under the `posthog-wizard` server. Frontmatter
170
+ * names them short (e.g. `enqueue_task`); the SDK gates on the full name. */
171
+ const ORCHESTRATOR_TOOL_PREFIX = "mcp__posthog-wizard__";
172
+ const ORCHESTRATOR_TOOLS = new Set([
173
+ "enqueue_task",
174
+ "complete_task",
175
+ "read_handoffs"
176
+ ]);
177
+ /** The registry for one flow's prompts. Pure; the loader feeds it the fetched set. */
178
+ function buildRegistry(prompts, flow, opts) {
179
+ const excluded = new Set(opts?.exclude ?? []);
180
+ const inFlow = prompts.filter((p) => p.flow === flow && !excluded.has(p.type));
181
+ const byType = new Map(inFlow.map((p) => [p.type, p]));
182
+ return {
183
+ types: inFlow.filter((p) => !p.seed).map((p) => p.type),
184
+ seed: inFlow.find((p) => p.seed),
185
+ get: (type) => byType.get(type)
186
+ };
187
+ }
188
+ /** A native tool passes through; an orchestrator tool gets its MCP-qualified name. */
189
+ function expandToolName(name) {
190
+ return ORCHESTRATOR_TOOLS.has(name) ? `${ORCHESTRATOR_TOOL_PREFIX}${name}` : name;
191
+ }
192
+ /** A prompt's allow/disallow lists with orchestrator tool names MCP-qualified. */
193
+ function agentRunTools(prompt) {
194
+ return {
195
+ allowedTools: prompt.allowedTools.map(expandToolName),
196
+ disallowedTools: prompt.disallowedTools.map(expandToolName)
197
+ };
198
+ }
199
+ function toStringArray(value) {
200
+ if (!Array.isArray(value)) return [];
201
+ return value.filter((v) => typeof v === "string");
202
+ }
203
+ /**
204
+ * Parse the leading `---` frontmatter block and the markdown body. The
205
+ * frontmatter is a small, known schema (scalars and inline `[a, b]` arrays), so
206
+ * a tiny parser covers it without a YAML dependency. Inline `# comments` after a
207
+ * value are stripped. `fallbackType` is the menu id, used when the body omits
208
+ * `type:`.
209
+ */
210
+ function parseAgentPrompt(text, fallbackType) {
211
+ const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
212
+ const frontmatter = match ? match[1] : "";
213
+ const body = (match ? match[2] : text).trim();
214
+ const fields = {};
215
+ for (const rawLine of frontmatter.split(/\r?\n/)) {
216
+ const line = rawLine.replace(/\s+#.*$/, "").trim();
217
+ if (!line || line.startsWith("#")) continue;
218
+ const kv = line.match(/^([\w-]+):\s*(.*)$/);
219
+ if (!kv) continue;
220
+ const [, key, raw] = kv;
221
+ if (raw.startsWith("[") && raw.endsWith("]")) fields[key] = raw.slice(1, -1).split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
222
+ else fields[key] = raw.replace(/^['"]|['"]$/g, "");
223
+ }
224
+ const model = typeof fields.model === "string" ? fields.model : void 0;
225
+ return {
226
+ type: typeof fields.type === "string" ? fields.type : fallbackType,
227
+ label: typeof fields.label === "string" ? fields.label : void 0,
228
+ flow: typeof fields.flow === "string" ? fields.flow : void 0,
229
+ seed: fields.seed === "true",
230
+ model,
231
+ skills: toStringArray(fields.skills),
232
+ allowedTools: toStringArray(fields.allowedTools),
233
+ disallowedTools: toStringArray(fields.disallowedTools),
234
+ dependsOn: toStringArray(fields.dependsOn),
235
+ body
236
+ };
237
+ }
238
+ async function fetchText(url) {
239
+ const res = await fetch(url);
240
+ if (!res.ok) throw new Error(`Fetch ${url} failed: ${res.status} ${res.statusText}`);
241
+ return res.text();
242
+ }
243
+ /**
244
+ * Fetch the agent menu and every agent prompt it lists, parse them, and build
245
+ * the registry for one flow. Throws if the menu cannot be fetched — the
246
+ * orchestrator cannot run without its prompts.
247
+ */
248
+ async function loadAgentRegistry(skillsBaseUrl, flow, opts) {
249
+ const menuRaw = await fetchText(`${skillsBaseUrl}/agent-menu.json`);
250
+ const menu = JSON.parse(menuRaw);
251
+ return buildRegistry(await Promise.all((menu.agents ?? []).map(async (entry) => {
252
+ return parseAgentPrompt(await fetchText(entry.downloadUrl), entry.id);
253
+ })), flow, opts);
254
+ }
255
+ /**
256
+ * Render a task's own inputs into a section, so a fanned-out task (e.g. one
257
+ * `capture` per event) sees the specific thing it owns. Empty when there are none.
258
+ */
259
+ function renderInputs(task) {
260
+ const entries = Object.entries(task.inputs ?? {});
261
+ if (entries.length === 0) return "";
262
+ return `## Your task input\n\n${entries.map(([k, v]) => `- ${k}: ${formatInputValue(v)}`).join("\n")}`;
263
+ }
264
+ function formatInputValue(value) {
265
+ if (typeof value === "string") return value;
266
+ return JSON.stringify(value);
267
+ }
268
+ /**
269
+ * The ids of every task `task` transitively depends on — the full upstream
270
+ * chain, not just direct dependencies — ordered roots-first, each once. A `seen`
271
+ * set dedupes diamonds and guards against cycles.
272
+ */
273
+ function ancestorIds(task, store) {
274
+ const seen = /* @__PURE__ */ new Set();
275
+ const ordered = [];
276
+ const visit = (id) => {
277
+ if (seen.has(id)) return;
278
+ seen.add(id);
279
+ const t = store.get(id);
280
+ if (!t) return;
281
+ for (const dep of t.dependsOn) visit(dep);
282
+ ordered.push(id);
283
+ };
284
+ for (const dep of task.dependsOn) visit(dep);
285
+ return ordered;
286
+ }
287
+ /**
288
+ * Render the handoffs of every step `task` transitively depends on into a context
289
+ * section, so a fresh agent sees the whole upstream chain — not just its direct
290
+ * dependencies. Reliability over token economy: a step must never have to
291
+ * re-discover what any ancestor already established just because an intermediate
292
+ * handoff happened to omit it. Empty when there are no completed ancestors.
293
+ */
294
+ function renderHandoffContext(task, store) {
295
+ const lines = [];
296
+ for (const id of ancestorIds(task, store)) {
297
+ const dep = store.get(id);
298
+ const handoff = store.readHandoff(id);
299
+ if (!dep || !handoff) continue;
300
+ lines.push(`### ${dep.type}`);
301
+ lines.push(`- did: ${handoff.did}`);
302
+ lines.push(`- for you: ${handoff.forNextAgent}`);
303
+ if (handoff.filesTouched?.length) lines.push(`- files: ${handoff.filesTouched.join(", ")}`);
304
+ lines.push("");
305
+ }
306
+ if (lines.length === 0) return "";
307
+ return `## Context from previous steps\n\n${lines.join("\n")}`.trim();
308
+ }
309
+ /**
310
+ * Resolve a queued task to its run config: the prompt body (with upstream
311
+ * handoffs appended), the model, and the tool lists with orchestrator tool names
312
+ * MCP-qualified. The model precedence is enqueue override, then prompt, then
313
+ * default. Throws if no prompt is registered for the task's type.
314
+ */
315
+ function resolveTask(registry, task, store) {
316
+ const prompt = registry.get(task.type);
317
+ if (!prompt) throw new Error(`No agent prompt registered for task type "${task.type}"`);
318
+ const body = [
319
+ renderInputs(task),
320
+ prompt.body,
321
+ renderHandoffContext(task, store)
322
+ ].filter(Boolean).join("\n\n");
323
+ return {
324
+ model: taskModel(registry, task),
325
+ ...agentRunTools(prompt),
326
+ prompt: body,
327
+ skills: prompt.skills
328
+ };
329
+ }
330
+ /** The model a task runs on: enqueue override, then prompt frontmatter, then default. */
331
+ function taskModel(registry, task) {
332
+ return task.model ?? registry.get(task.type)?.model ?? DEFAULT_TASK_MODEL;
333
+ }
334
+ //#endregion
335
+ //#region src/lib/programs/orchestrator/orchestrator-runner.ts
336
+ /**
337
+ * Experimental task-queue orchestrator runner.
338
+ *
339
+ * Branches from the linear runner when the `wizard-orchestrator` flag is on. An
340
+ * orchestrator agent inspects the repo and seeds an in-memory task queue; an
341
+ * executor drains it, running one fresh agent per task.
342
+ *
343
+ * Both the WHAT (agent prompts: model, goal, success criteria, tools) and the
344
+ * HOW (mini-skills) are markdown served from context-mill — the seed and every
345
+ * task resolve to a prompt fetched at startup into the registry. The wizard side
346
+ * stays product-ignorant: it is the queue, the executor, and the loader.
347
+ */
348
+ function toTodoStatus(status) {
349
+ switch (status) {
350
+ case TaskStatus.Running: return "in_progress";
351
+ case TaskStatus.Done:
352
+ case TaskStatus.Failed: return "completed";
353
+ case TaskStatus.Skipped: return "skipped";
354
+ default: return "pending";
355
+ }
356
+ }
357
+ function sessionRunOptions(session) {
358
+ return {
359
+ installDir: session.installDir,
360
+ debug: session.debug,
361
+ default: false,
362
+ signup: session.signup,
363
+ localMcp: session.localMcp,
364
+ ci: session.ci,
365
+ benchmark: session.benchmark,
366
+ projectId: session.projectId,
367
+ apiKey: session.apiKey,
368
+ yaraReport: session.yaraReport
369
+ };
370
+ }
371
+ /**
372
+ * The framework reference is the full `integration` skill. `session.skillId` is
373
+ * the bare framework (e.g. `django`), but the skill menu ids it as
374
+ * `integration-<variant>`. Resolve to the menu id: exact `integration-<framework>`
375
+ * (the 1:1 frameworks — django, python, flask, …), else the first granular variant
376
+ * under it (e.g. `integration-nextjs-app-router`). Undefined when none exists.
377
+ */
378
+ async function resolveReferenceSkillId(skillsBaseUrl, framework) {
379
+ const menu = await fetchSkillMenu(skillsBaseUrl);
380
+ if (!menu) return void 0;
381
+ const ids = Object.values(menu.categories).flat().map((s) => s.id);
382
+ const exact = `integration-${framework}`;
383
+ if (ids.includes(exact)) return exact;
384
+ return ids.find((id) => id.startsWith(`integration-${framework}-`));
385
+ }
386
+ async function runOrchestrator(session, programConfig, boot) {
387
+ const runId = randomUUID();
388
+ const options = sessionRunOptions(session);
389
+ const registry = await loadAgentRegistry(boot.skillsBaseUrl, programConfig.id, { exclude: ciExcludedTaskTypes() });
390
+ const seedPrompt = registry.seed;
391
+ if (!seedPrompt) throw new Error(`No seed agent prompt (frontmatter \`seed: true\`) for flow "${programConfig.id}" is available from ${boot.skillsBaseUrl}.`);
392
+ analytics.setTag("variant", "orchestrator");
393
+ const runStartMs = Date.now();
394
+ const metrics = new RunMetrics(runStartMs);
395
+ const durationMs = (t) => t.startedAt && t.finishedAt ? Date.parse(t.finishedAt) - Date.parse(t.startedAt) : void 0;
396
+ const store = new QueueStore(session.installDir, runId, { onTransition: (event, task) => {
397
+ const base = {
398
+ type: task.type,
399
+ model: taskModel(registry, task),
400
+ attempts: task.attempts
401
+ };
402
+ switch (event) {
403
+ case "enqueue":
404
+ analytics.wizardCapture("orchestrator task enqueued", {
405
+ type: task.type,
406
+ enqueued_by: task.enqueuedBy,
407
+ dynamic: task.enqueuedBy !== "orchestrator"
408
+ });
409
+ break;
410
+ case "start":
411
+ analytics.wizardCapture("orchestrator task started", {
412
+ ...base,
413
+ ...metrics.recordStart(Date.now())
414
+ });
415
+ break;
416
+ case "complete":
417
+ metrics.recordComplete(Date.now());
418
+ analytics.wizardCapture("orchestrator task completed", {
419
+ ...base,
420
+ duration_ms: durationMs(task)
421
+ });
422
+ break;
423
+ case "skip":
424
+ metrics.recordTerminal(Date.now());
425
+ analytics.wizardCapture("orchestrator task skipped", {
426
+ ...base,
427
+ duration_ms: durationMs(task)
428
+ });
429
+ break;
430
+ case "fail":
431
+ metrics.recordTerminal(Date.now());
432
+ analytics.wizardCapture("orchestrator task failed", {
433
+ ...base,
434
+ duration_ms: durationMs(task),
435
+ error: task.error?.type
436
+ });
437
+ break;
438
+ case "requeue": break;
439
+ }
440
+ } });
441
+ let examplePath;
442
+ let commandmentsPath;
443
+ const referenceSkillId = session.skillId ? await resolveReferenceSkillId(boot.skillsBaseUrl, session.skillId) : void 0;
444
+ if (referenceSkillId) {
445
+ const ref = await installSkillById(referenceSkillId, session.installDir, boot.skillsBaseUrl, path$1.join(QUEUE_DIR_NAME, "reference"));
446
+ if (ref.kind === "ok") {
447
+ const example = path$1.join(ref.path, "references", "EXAMPLE.md");
448
+ if (existsSync(path$1.join(session.installDir, example))) examplePath = example;
449
+ const commandments = path$1.join(ref.path, "references", "COMMANDMENTS.md");
450
+ if (existsSync(path$1.join(session.installDir, commandments))) commandmentsPath = commandments;
451
+ } else logToFile(`[orchestrator] reference unavailable: ${ref.kind} (${referenceSkillId})`);
452
+ } else if (session.skillId) logToFile(`[orchestrator] no integration skill for framework "${session.skillId}"`);
453
+ const promptContext = {
454
+ projectId: boot.projectId,
455
+ projectApiKey: boot.projectApiKey,
456
+ host: boot.host,
457
+ examplePath,
458
+ commandmentsPath
459
+ };
460
+ logToFile(`[orchestrator] START program=${programConfig.id} dir=${session.installDir} run=${runId}`);
461
+ analytics.wizardCapture("orchestrator started", { program_id: programConfig.id });
462
+ getUI().startRun();
463
+ const labelFor = (t) => t.label ?? registry.get(t.type)?.label ?? t.type;
464
+ const renderQueue = () => getUI().syncTodos(store.list().map((t) => ({
465
+ content: labelFor(t),
466
+ status: toTodoStatus(t.status),
467
+ activeForm: labelFor(t)
468
+ })));
469
+ const agentConfigFor = (currentTaskId) => ({
470
+ workingDirectory: session.installDir,
471
+ posthogMcpUrl: boot.mcpUrl,
472
+ posthogApiKey: boot.accessToken,
473
+ posthogApiHost: boot.host,
474
+ detectPackageManager: detectNodePackageManagers,
475
+ skillsBaseUrl: boot.skillsBaseUrl,
476
+ wizardFlags: boot.wizardFlags,
477
+ wizardMetadata: {
478
+ ...boot.wizardMetadata,
479
+ VARIANT: "orchestrator"
480
+ },
481
+ integrationLabel: programConfig.id,
482
+ orchestrator: {
483
+ store,
484
+ validTypes: registry.types,
485
+ currentTaskId
486
+ }
487
+ });
488
+ const spinner = getUI().spinner();
489
+ const seedAgent = await initializeAgent(agentConfigFor(), options);
490
+ const seedResult = await runAgent$1({
491
+ ...seedAgent,
492
+ model: seedPrompt.model ?? seedAgent.model,
493
+ ...agentRunTools(seedPrompt)
494
+ }, assembleSeedPrompt(promptContext, seedPrompt.body), options, spinner, {
495
+ spinnerMessage: "Planning the integration...",
496
+ successMessage: "Planned the integration",
497
+ additionalFeatureQueue: [],
498
+ requestRemark: false,
499
+ analyticsProperties: { task_type: "seed" }
500
+ });
501
+ if (seedResult.error) logToFile(`[orchestrator] seed error: ${seedResult.error} ${seedResult.message ?? ""}`);
502
+ analytics.wizardCapture("orchestrator seeded", {
503
+ task_count: store.list().length,
504
+ types: store.list().map((t) => t.type)
505
+ });
506
+ renderQueue();
507
+ const taskSkillsRoot = path$1.join(QUEUE_DIR_NAME, "skills");
508
+ let remarkRequested = false;
509
+ const runTask = async (task) => {
510
+ renderQueue();
511
+ try {
512
+ const resolved = resolveTask(registry, task, store);
513
+ const agent = await initializeAgent(agentConfigFor(task.id), options);
514
+ const skillPaths = [];
515
+ for (const skillId of resolved.skills) {
516
+ const result = await installSkillById(skillId, session.installDir, boot.skillsBaseUrl, taskSkillsRoot);
517
+ if (result.kind === "ok") skillPaths.push(path$1.join(result.path, "SKILL.md"));
518
+ else logToFile(`[orchestrator] skill install failed type=${task.type} skill=${skillId} ${result.kind}`);
519
+ }
520
+ const requestRemark = !store.list().some((t) => t.id !== task.id && (t.status === TaskStatus.Pending || t.status === TaskStatus.Running)) && !remarkRequested;
521
+ if (requestRemark) remarkRequested = true;
522
+ await runAgent$1({
523
+ ...agent,
524
+ model: resolved.model,
525
+ allowedTools: resolved.allowedTools,
526
+ disallowedTools: resolved.disallowedTools
527
+ }, assembleTaskPrompt(promptContext, resolved.prompt, skillPaths), options, spinner, {
528
+ spinnerMessage: "",
529
+ successMessage: "",
530
+ additionalFeatureQueue: [],
531
+ requestRemark,
532
+ analyticsProperties: {
533
+ task_type: task.type,
534
+ task_id: task.id
535
+ }
536
+ });
537
+ } finally {
538
+ renderQueue();
539
+ }
540
+ };
541
+ try {
542
+ await drainQueue(store, runTask);
543
+ } finally {
544
+ try {
545
+ rmSync(path$1.join(session.installDir, QUEUE_DIR_NAME), {
546
+ recursive: true,
547
+ force: true
548
+ });
549
+ } catch (err) {
550
+ analytics.captureException(err instanceof Error ? err : new Error(String(err)), { step: "orchestrator_cache_cleanup" });
551
+ }
552
+ }
553
+ renderQueue();
554
+ const summary = store.summary();
555
+ logToFile(`[orchestrator] DONE done=${summary.done} failed=${summary.failed} total=${summary.total}`);
556
+ analytics.wizardCapture("orchestrator run finished", {
557
+ tasks_total: summary.total,
558
+ tasks_done: summary.done,
559
+ tasks_failed: summary.failed,
560
+ tasks_skipped: summary.skipped,
561
+ total_duration_ms: Date.now() - runStartMs,
562
+ ...metrics.summary(),
563
+ dynamic_enqueue_count: store.list().filter((t) => t.enqueuedBy !== "orchestrator").length,
564
+ retried_task_count: store.list().filter((t) => t.attempts > 1).length
565
+ });
566
+ const buildTask = store.list().find((t) => t.type === "build");
567
+ const conflict = buildTask ? store.readHandoff(buildTask.id)?.conflict : void 0;
568
+ const reportFile = existsSync(path$1.join(session.installDir, "posthog-setup-report.md")) ? "posthog-setup-report.md" : store.queuePath;
569
+ const message = conflict ? "PostHog set up, with one conflict to review." : `PostHog set up: ${summary.done}/${summary.total} steps completed.`;
570
+ getUI().setOutroData({
571
+ kind: "success",
572
+ message,
573
+ body: conflict ? `⚠ Build conflict: ${conflict}\nFull details are in the report.` : void 0,
574
+ reportFile,
575
+ docsUrl: "https://posthog.com/docs/ai-engineering/ai-wizard"
576
+ });
577
+ getUI().outro(message);
578
+ await analytics.shutdown("success");
579
+ }
580
+ //#endregion
11
581
  //#region src/lib/middleware/phase-detector.ts
12
582
  /** Phase transitions from [STATUS] in assistant text. Keep in sync with program "Status to report" bullets. */
13
583
  const PHASES_ORDER = [
@@ -948,12 +1518,25 @@ async function runAgent(programConfig, session) {
948
1518
  /**
949
1519
  * Run a program's agent pipeline.
950
1520
  *
951
- * This is the single execution path for all programs both skill-based
952
- * (revenue analytics) and framework-based (core integration). The
953
- * `ProgramRun` controls what varies between them; `programConfig` carries
954
- * the program-level static metadata (tool allow/disallow lists, etc.).
1521
+ * Runs the shared bootstrap, then forks on the `wizard-variant` flag. The
1522
+ * `orchestrator` variant routes to the experimental task-queue runner; every
1523
+ * other variant runs the linear pipeline.
955
1524
  */
956
1525
  async function runProgram(session, config, programConfig) {
1526
+ const boot = await bootstrapProgram(session, config, programConfig);
1527
+ if (isOrchestratorEnabled(boot.wizardFlags)) {
1528
+ getUI().log.info("Task-queue orchestrator enabled.");
1529
+ return runOrchestrator(session, programConfig, boot);
1530
+ }
1531
+ return runLinearProgram(session, config, programConfig, boot);
1532
+ }
1533
+ /**
1534
+ * Shared setup for both arms: logging, health check, settings conflicts, OAuth
1535
+ * and credentials, then the feature flags, variant metadata, and MCP url. Sets
1536
+ * `session.credentials`, role, and user as a side effect. Returns the values the
1537
+ * arms still need.
1538
+ */
1539
+ async function bootstrapProgram(session, config, programConfig) {
957
1540
  initLogFile();
958
1541
  session.skillId = config.skillId ?? config.integrationLabel;
959
1542
  logToFile(`[agent-runner] START ${config.integrationLabel}`);
@@ -970,7 +1553,7 @@ async function runProgram(session, config, programConfig) {
970
1553
  const blockingLabels = getBlockingServiceKeys(readiness.health, readinessConfig).map((k) => `${SERVICE_LABELS[k]} (${readiness.health[k].status})`);
971
1554
  logToFile(`[agent-runner] blocked by: ${blockingLabels.join(", ")}`);
972
1555
  await getUI().showBlockingOutage(readiness);
973
- await wizardAbort({ message: "Cannot start — external services are down:\n" + blockingLabels.map((l) => ` - ${l}`).join("\n") + "\n\nPlease try again later." });
1556
+ if (!isNonInteractiveEnvironment()) await wizardAbort({ message: "Cannot start — external services are down:\n" + blockingLabels.map((l) => ` - ${l}`).join("\n") + "\n\nPlease try again later." });
974
1557
  } else if (readiness.decision === "yes_with_warnings") getUI().setReadinessWarnings(readiness);
975
1558
  }
976
1559
  const settingsConflicts = checkAllSettingsConflicts(session.installDir);
@@ -1012,10 +1595,34 @@ async function runProgram(session, config, programConfig) {
1012
1595
  getUI().setCredentials(session.credentials);
1013
1596
  getUI().setRoleAtOrganization(roleAtOrganization);
1014
1597
  getUI().setApiUser(user);
1598
+ if (user) analytics.identifyUser(user);
1015
1599
  analytics.setGroups(groupsFromUser(user, host));
1016
1600
  logToFile("[agent-runner] checking AI opt-in gate");
1017
1601
  await getUI().waitForAiOptIn();
1018
1602
  logToFile("[agent-runner] AI opt-in gate cleared");
1603
+ const wizardFlags = await analytics.getAllFlagsForWizard();
1604
+ const wizardMetadata = buildWizardMetadata(wizardFlags);
1605
+ analytics.setTag("variant", wizardMetadata.VARIANT);
1606
+ return {
1607
+ skillsBaseUrl,
1608
+ projectApiKey,
1609
+ host,
1610
+ accessToken,
1611
+ projectId,
1612
+ cloudRegion,
1613
+ mcpUrl: session.localMcp ? "http://localhost:8787/mcp" : runtimeEnv("MCP_URL") || "https://mcp.posthog.com/mcp",
1614
+ wizardFlags,
1615
+ wizardMetadata
1616
+ };
1617
+ }
1618
+ /**
1619
+ * The linear pipeline. Single execution path for all non-orchestrator programs,
1620
+ * both skill-based (revenue analytics) and framework-based (core integration).
1621
+ * The `ProgramRun` controls what varies between them; `programConfig` carries the
1622
+ * program-level static metadata (tool allow/disallow lists, etc.).
1623
+ */
1624
+ async function runLinearProgram(session, config, programConfig, boot) {
1625
+ const { skillsBaseUrl, projectApiKey, host, accessToken, projectId, cloudRegion, mcpUrl, wizardFlags, wizardMetadata } = boot;
1019
1626
  let skillPath;
1020
1627
  if (config.skillId) {
1021
1628
  logToFile(`[agent-runner] installing skill ${config.skillId}`);
@@ -1028,9 +1635,6 @@ async function runProgram(session, config, programConfig) {
1028
1635
  logToFile(`[agent-runner] skill installed at ${skillPath}`);
1029
1636
  }
1030
1637
  const spinner = getUI().spinner();
1031
- const wizardFlags = await analytics.getAllFlagsForWizard();
1032
- const wizardMetadata = buildWizardMetadata(wizardFlags);
1033
- const mcpUrl = session.localMcp ? "http://localhost:8787/mcp" : runtimeEnv("MCP_URL") || (cloudRegion === "eu" ? "https://mcp-eu.posthog.com/mcp" : "https://mcp.posthog.com/mcp");
1034
1638
  const restoreSettings = () => restoreClaudeSettings(session.installDir);
1035
1639
  getUI().onEnterScreen("outro", restoreSettings);
1036
1640
  if (session.yaraReport) registerCleanup(() => {
@@ -1046,6 +1650,7 @@ async function runProgram(session, config, programConfig) {
1046
1650
  showQuestion: (q) => getUI().requestQuestion(q),
1047
1651
  timeoutMs: config.askTimeoutMs
1048
1652
  });
1653
+ getUI().log.step("Initializing Claude agent...");
1049
1654
  const agent = await initializeAgent({
1050
1655
  workingDirectory: session.installDir,
1051
1656
  posthogMcpUrl: mcpUrl,
@@ -1063,6 +1668,8 @@ async function runProgram(session, config, programConfig) {
1063
1668
  disallowedTools: programConfig.disallowedTools,
1064
1669
  getPendingQuestion: () => session.pendingQuestion
1065
1670
  }, sessionToOptions(session));
1671
+ getUI().log.step(`Verbose logs: ${getLogFilePath()}`);
1672
+ getUI().log.success("Agent initialized. Let's get cooking!");
1066
1673
  logToFile("[agent-runner] agent initialized");
1067
1674
  const middleware = session.benchmark ? createBenchmarkPipeline(spinner, sessionToOptions(session)) : void 0;
1068
1675
  const prompt = assemblePrompt(config, {
@@ -1195,4 +1802,4 @@ async function abortOnInstallFailure(integrationLabel, result) {
1195
1802
  //#endregion
1196
1803
  export { runAgent };
1197
1804
 
1198
- //# sourceMappingURL=agent-runner-CBbkS0Ro.js.map
1805
+ //# sourceMappingURL=agent-runner-L_-kJ3y3.js.map