@posthog/wizard 2.64.1 → 2.66.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/dist/{add-mcp-server-to-clients-BUqb93_I.js → add-mcp-server-to-clients-DWDEuDUR.js} +210 -46
- package/dist/add-mcp-server-to-clients-DWDEuDUR.js.map +1 -0
- package/dist/{agent-interface-NurnwA01.js → agent-interface-D3MNaDp3.js} +405 -47
- package/dist/agent-interface-D3MNaDp3.js.map +1 -0
- package/dist/{agent-prompt-loader-CbowPuWI.js → agent-prompt-loader-D8Bajo-L.js} +10 -5
- package/dist/agent-prompt-loader-D8Bajo-L.js.map +1 -0
- package/dist/{agent-runner-DisJWxJa.js → agent-runner-DEeMqJxy.js} +111 -53
- package/dist/agent-runner-DEeMqJxy.js.map +1 -0
- package/dist/{analytics-D3vKvr3l.js → analytics-D8JNuPAT.js} +2 -2
- package/dist/{analytics-D3vKvr3l.js.map → analytics-D8JNuPAT.js.map} +1 -1
- package/dist/analytics-DN3Kzf7H.js +2 -0
- package/dist/{api-Bhe8jpUR.js → api-CemGgyBv.js} +3 -3
- package/dist/{api-Bhe8jpUR.js.map → api-CemGgyBv.js.map} +1 -1
- package/dist/bin.js +245 -93
- package/dist/bin.js.map +1 -1
- package/dist/{bounded-fs-BeWc675I.js → bounded-fs-C6OjqZst.js} +3 -3
- package/dist/{bounded-fs-BeWc675I.js.map → bounded-fs-C6OjqZst.js.map} +1 -1
- package/dist/{ci-install-BpNzfghY.js → ci-install-DIB2n4Ju.js} +4 -4
- package/dist/{ci-install-BpNzfghY.js.map → ci-install-DIB2n4Ju.js.map} +1 -1
- package/dist/{debug-hy6aWAyk.js → debug-BKqv-yfT.js} +14 -9
- package/dist/debug-BKqv-yfT.js.map +1 -0
- package/dist/{debug-DGfRPr2A.js → debug-H-GTKvvU.js} +1 -1
- package/dist/{defaults-mrAre0xM.js → defaults-BHhXrX9F.js} +1 -1
- package/dist/{defaults-mrAre0xM.js.map → defaults-BHhXrX9F.js.map} +1 -1
- package/dist/{env-api-key-CMPe-WJp.js → env-api-key-CzHDwfV2.js} +2 -2
- package/dist/{env-api-key-CMPe-WJp.js.map → env-api-key-CzHDwfV2.js.map} +1 -1
- package/dist/{environment-Ktu0Ktjl.js → environment-Cl-fIQku.js} +4 -4
- package/dist/{environment-Ktu0Ktjl.js.map → environment-Cl-fIQku.js.map} +1 -1
- package/dist/{file-watcher-CqJ1dgjq.js → file-watcher-Cr-4tA-e.js} +2 -2
- package/dist/{file-watcher-CqJ1dgjq.js.map → file-watcher-Cr-4tA-e.js.map} +1 -1
- package/dist/{headless-ui-zZLgs4QL.js → headless-ui-DT3Db1bk.js} +2 -2
- package/dist/{headless-ui-zZLgs4QL.js.map → headless-ui-DT3Db1bk.js.map} +1 -1
- package/dist/{interactive-BUw1SEOz.js → interactive-D3LdFxZG.js} +2 -2
- package/dist/{interactive-BUw1SEOz.js.map → interactive-D3LdFxZG.js.map} +1 -1
- package/dist/local-dev-BN6NKQ0B.js +135 -0
- package/dist/local-dev-BN6NKQ0B.js.map +1 -0
- package/dist/{mcp-C7NMhhWa.js → mcp-BjckYQmP.js} +2 -2
- package/dist/{mcp-C7NMhhWa.js.map → mcp-BjckYQmP.js.map} +1 -1
- package/dist/mcp-install-smoke-test.ts +477 -0
- package/dist/{mcp-prompt-streaming-CKyqqB10.js → mcp-prompt-streaming--sRErHKv.js} +20 -4
- package/dist/mcp-prompt-streaming--sRErHKv.js.map +1 -0
- package/dist/{non-interactive-73xGtug6.js → non-interactive-CNoYoEZp.js} +2 -2
- package/dist/{non-interactive-73xGtug6.js.map → non-interactive-CNoYoEZp.js.map} +1 -1
- package/dist/{orchestrator-tools-DcMJzfDH.js → orchestrator-tools-8bGyDDhn.js} +3 -3
- package/dist/{orchestrator-tools-DcMJzfDH.js.map → orchestrator-tools-8bGyDDhn.js.map} +1 -1
- package/dist/{package-json-CDs1ZVKu.js → package-json-D0DCWpe5.js} +1 -1
- package/dist/{package-json-CDs1ZVKu.js.map → package-json-D0DCWpe5.js.map} +1 -1
- package/dist/{package-manager-KweKX3u_.js → package-manager-BQDHm7ER.js} +58 -3
- package/dist/package-manager-BQDHm7ER.js.map +1 -0
- package/dist/{pi-B4p4gpr0.js → pi-W-4RUPFY.js} +11 -11
- package/dist/{pi-B4p4gpr0.js.map → pi-W-4RUPFY.js.map} +1 -1
- package/dist/{playground-AEkWcB1E.js → playground-uHHgZChe.js} +7 -7
- package/dist/{playground-AEkWcB1E.js.map → playground-uHHgZChe.js.map} +1 -1
- package/dist/{posthog-4PljLcIz.js → posthog-ChzdlmCs.js} +2 -2
- package/dist/{posthog-4PljLcIz.js.map → posthog-ChzdlmCs.js.map} +1 -1
- package/dist/{posthog-integration-DKlEg8-Q.js → posthog-integration-LxEBGkoe.js} +680 -49
- package/dist/posthog-integration-LxEBGkoe.js.map +1 -0
- package/dist/{provisioning-QsG7HDCm.js → provisioning-BHTl91OZ.js} +3 -3
- package/dist/{provisioning-QsG7HDCm.js.map → provisioning-BHTl91OZ.js.map} +1 -1
- package/dist/{queue-tools-Jlz9dV_B.js → queue-tools-DABh1Ssc.js} +33 -7
- package/dist/queue-tools-DABh1Ssc.js.map +1 -0
- package/dist/{registry-PG_nV5Bf.js → registry-CxNpCNAj.js} +309 -6
- package/dist/registry-CxNpCNAj.js.map +1 -0
- package/dist/{security-BKUJk-Gg.js → security-B4jU_-7q.js} +5 -5
- package/dist/{security-BKUJk-Gg.js.map → security-B4jU_-7q.js.map} +1 -1
- package/dist/{setup-utils-DNpldqge.js → setup-utils-aWZEdfwh.js} +18 -13
- package/dist/setup-utils-aWZEdfwh.js.map +1 -0
- package/dist/smoke-test-ci.sh +12 -1
- package/dist/{start-tui-D4B0-2nr.js → start-tui-BQH4mQIZ.js} +138 -27
- package/dist/start-tui-BQH4mQIZ.js.map +1 -0
- package/dist/{steps-Dy0AXdGo.js → steps-5HAqjUvn.js} +7 -7
- package/dist/{steps-Dy0AXdGo.js.map → steps-5HAqjUvn.js.map} +1 -1
- package/dist/{store-DouLLelV.js → store-P_7zUfbj.js} +22 -7
- package/dist/store-P_7zUfbj.js.map +1 -0
- package/dist/{subagent-B4yVs5A2.js → subagent-uGPDXPfS.js} +2 -2
- package/dist/{subagent-B4yVs5A2.js.map → subagent-uGPDXPfS.js.map} +1 -1
- package/dist/{task-DRKYEbKM.js → task-oh8OAnvR.js} +29 -18
- package/dist/task-oh8OAnvR.js.map +1 -0
- package/dist/{task-stream-BGUP7yA-.js → task-stream-jUARpyB5.js} +5 -5
- package/dist/{task-stream-BGUP7yA-.js.map → task-stream-jUARpyB5.js.map} +1 -1
- package/dist/{tasks-B2GzXsZ0.js → tasks-2mu3lAvm.js} +2 -2
- package/dist/{tasks-B2GzXsZ0.js.map → tasks-2mu3lAvm.js.map} +1 -1
- package/dist/{telemetry-6zaFfCcy.js → telemetry-BllPiBKw.js} +3 -3
- package/dist/{telemetry-6zaFfCcy.js.map → telemetry-BllPiBKw.js.map} +1 -1
- package/dist/{terminal-Bu3eHFn0.js → terminal-aWQrDy8s.js} +99 -98
- package/dist/terminal-aWQrDy8s.js.map +1 -0
- package/dist/{tools-DQO5tKJg.js → tools-u8TaJFqa.js} +54 -47
- package/dist/tools-u8TaJFqa.js.map +1 -0
- package/dist/{wizard-abort-D6jJh8u5.js → wizard-abort-D9jiMkZA.js} +4 -4
- package/dist/{wizard-abort-D6jJh8u5.js.map → wizard-abort-D9jiMkZA.js.map} +1 -1
- package/dist/{wizard-abort-PgTdRS1l.js → wizard-abort-pFFH7dcx.js} +1 -1
- package/dist/{wizard-session-DZUDtS5d.js → wizard-session-Cw7-_OcZ.js} +1 -1
- package/dist/{wizard-session-BUhlJ1b8.js → wizard-session-DTyLMPsX.js} +17 -3
- package/dist/wizard-session-DTyLMPsX.js.map +1 -0
- package/dist/{yara-hooks-DjReXmTQ.js → yara-hooks-BXDBIOBq.js} +4 -4
- package/dist/{yara-hooks-DjReXmTQ.js.map → yara-hooks-BXDBIOBq.js.map} +1 -1
- package/dist/yara-hooks-i2jOabhI.js +2 -0
- package/package.json +2 -1
- package/dist/add-mcp-server-to-clients-BUqb93_I.js.map +0 -1
- package/dist/agent-interface-NurnwA01.js.map +0 -1
- package/dist/agent-prompt-loader-CbowPuWI.js.map +0 -1
- package/dist/agent-runner-DisJWxJa.js.map +0 -1
- package/dist/analytics-hNca7GLq.js +0 -2
- package/dist/debug-hy6aWAyk.js.map +0 -1
- package/dist/mcp-prompt-streaming-CKyqqB10.js.map +0 -1
- package/dist/package-manager-KweKX3u_.js.map +0 -1
- package/dist/posthog-integration-DKlEg8-Q.js.map +0 -1
- package/dist/queue-tools-Jlz9dV_B.js.map +0 -1
- package/dist/registry-PG_nV5Bf.js.map +0 -1
- package/dist/setup-utils-DNpldqge.js.map +0 -1
- package/dist/start-tui-D4B0-2nr.js.map +0 -1
- package/dist/store-DouLLelV.js.map +0 -1
- package/dist/task-DRKYEbKM.js.map +0 -1
- package/dist/terminal-Bu3eHFn0.js.map +0 -1
- package/dist/tools-DQO5tKJg.js.map +0 -1
- package/dist/wizard-session-BUhlJ1b8.js.map +0 -1
- package/dist/yara-hooks-CN_uFGCu.js +0 -2
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs a headless `wizard mcp add` against a real editor CLI in a throwaway HOME
|
|
3
|
+
* and asserts the config that comes out. Drives the built binary rather than
|
|
4
|
+
* importing client classes, so arg parsing and command wiring are covered too.
|
|
5
|
+
*
|
|
6
|
+
* Needs no credentials: the OAuth path writes a tokenless entry and defers auth
|
|
7
|
+
* to the editor's own login command.
|
|
8
|
+
*
|
|
9
|
+
* Usage: tsx scripts/mcp-install-smoke-test.ts --provider=claude-code|codex
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
13
|
+
import * as fs from 'node:fs';
|
|
14
|
+
import * as os from 'node:os';
|
|
15
|
+
import * as path from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
|
|
18
|
+
import { HEADLESS_FLAG } from '../src/lib/headless-mode';
|
|
19
|
+
|
|
20
|
+
const PROVIDERS = ['claude-code', 'codex'] as const;
|
|
21
|
+
type Provider = (typeof PROVIDERS)[number];
|
|
22
|
+
|
|
23
|
+
const MCP_URL = 'https://mcp.posthog.com/mcp';
|
|
24
|
+
const WIZARD = path.resolve(
|
|
25
|
+
fileURLToPath(import.meta.url),
|
|
26
|
+
'..',
|
|
27
|
+
'..',
|
|
28
|
+
'dist',
|
|
29
|
+
'bin.js',
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
interface ProviderSpec {
|
|
33
|
+
/** Editor CLI binary, looked up on PATH. */
|
|
34
|
+
binary: string;
|
|
35
|
+
/** The login subcommand the wizard tells the user to run, minus the server name. */
|
|
36
|
+
loginProbe: string[];
|
|
37
|
+
/** Config file relative to HOME, set only when the file is the contract. */
|
|
38
|
+
configPath?: string;
|
|
39
|
+
/** Exit code of a "is posthog installed" probe against the editor itself. */
|
|
40
|
+
isInstalled: () => boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const SPECS: Record<Provider, ProviderSpec> = {
|
|
44
|
+
'claude-code': {
|
|
45
|
+
binary: 'claude',
|
|
46
|
+
loginProbe: ['mcp', 'login', '--help'],
|
|
47
|
+
isInstalled: () => run('claude', ['mcp', 'get', 'posthog']).status === 0,
|
|
48
|
+
},
|
|
49
|
+
codex: {
|
|
50
|
+
binary: 'codex',
|
|
51
|
+
loginProbe: ['mcp', 'login', '--help'],
|
|
52
|
+
configPath: '.codex/config.toml',
|
|
53
|
+
isInstalled: () =>
|
|
54
|
+
(
|
|
55
|
+
readIfPresent(path.join(process.env.HOME!, '.codex', 'config.toml')) ??
|
|
56
|
+
''
|
|
57
|
+
).includes('[mcp_servers.posthog]'),
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// ── harness ───────────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
const failures: string[] = [];
|
|
64
|
+
let currentScenario = '';
|
|
65
|
+
|
|
66
|
+
function fail(message: string): void {
|
|
67
|
+
failures.push(`${currentScenario}: ${message}`);
|
|
68
|
+
console.log(` ✗ ${message}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function pass(message: string): void {
|
|
72
|
+
console.log(` ✔ ${message}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function check(condition: boolean, message: string): boolean {
|
|
76
|
+
if (condition) pass(message);
|
|
77
|
+
else fail(message);
|
|
78
|
+
return condition;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface Run {
|
|
82
|
+
status: number;
|
|
83
|
+
stdout: string;
|
|
84
|
+
stderr: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function run(binary: string, args: string[]): Run {
|
|
88
|
+
const r = spawnSync(binary, args, { encoding: 'utf-8' });
|
|
89
|
+
return {
|
|
90
|
+
status: r.error ? -1 : r.status ?? -1,
|
|
91
|
+
stdout: r.stdout ?? '',
|
|
92
|
+
stderr: r.stderr ?? '',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** `wizard <args>` against the built binary, inheriting the sandbox HOME. */
|
|
97
|
+
function wizard(...args: string[]): Run {
|
|
98
|
+
return run(process.execPath, [WIZARD, ...args]);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The non-interactive install. Reuses the run pipeline's headless flag, so the
|
|
103
|
+
* name is imported rather than spelled out — see @lib/headless-mode.
|
|
104
|
+
*/
|
|
105
|
+
function mcpAdd(...extra: string[]): Run {
|
|
106
|
+
return wizard('mcp', 'add', `--${HEADLESS_FLAG}`, ...extra);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function mcpRemove(): Run {
|
|
110
|
+
return wizard('mcp', 'remove', `--${HEADLESS_FLAG}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* `os.homedir()` reads $HOME on POSIX and both editor CLIs store config under
|
|
115
|
+
* it, so one env var isolates the wizard and the CLI it shells out to. A
|
|
116
|
+
* pristine dir per scenario is what a first-time user actually has.
|
|
117
|
+
*/
|
|
118
|
+
async function withSandboxHome(
|
|
119
|
+
spec: ProviderSpec,
|
|
120
|
+
fn: (home: string) => Promise<void>,
|
|
121
|
+
): Promise<void> {
|
|
122
|
+
const realHome = process.env.HOME;
|
|
123
|
+
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'wizard-mcp-'));
|
|
124
|
+
const before = failures.length;
|
|
125
|
+
process.env.HOME = home;
|
|
126
|
+
try {
|
|
127
|
+
await fn(home);
|
|
128
|
+
} finally {
|
|
129
|
+
// The sandbox is about to go and CI has nothing else to look at.
|
|
130
|
+
if (failures.length > before && spec.configPath) {
|
|
131
|
+
const contents = readIfPresent(path.join(home, spec.configPath));
|
|
132
|
+
console.log(
|
|
133
|
+
contents === null
|
|
134
|
+
? ` ── ${spec.configPath} was never written ──`
|
|
135
|
+
: ` ── ${spec.configPath} ──\n${contents.replace(/^/gm, ' │ ')}`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
process.env.HOME = realHome;
|
|
139
|
+
fs.rmSync(home, { recursive: true, force: true });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function seed(home: string, relative: string, contents: string): void {
|
|
144
|
+
const target = path.join(home, relative);
|
|
145
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
146
|
+
fs.writeFileSync(target, contents);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function readIfPresent(file: string): string | null {
|
|
150
|
+
try {
|
|
151
|
+
return fs.readFileSync(file, 'utf-8');
|
|
152
|
+
} catch {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Asserts a clean exit, quoting the reason only when it failed. */
|
|
158
|
+
function checkRun(r: Run, label: string): boolean {
|
|
159
|
+
if (r.status === 0) return check(true, label);
|
|
160
|
+
const lines = (r.stderr + r.stdout)
|
|
161
|
+
.split('\n')
|
|
162
|
+
.map((l) => l.trim())
|
|
163
|
+
.filter(Boolean);
|
|
164
|
+
// The step prints failures as `- <client> — <detail>` under a success header,
|
|
165
|
+
// so the first line is usually the wrong one to quote.
|
|
166
|
+
const reason =
|
|
167
|
+
lines.find((l) => l.includes('—')) ?? lines[0] ?? `exit ${r.status}`;
|
|
168
|
+
return check(false, `${label} — ${reason}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ── scenarios ─────────────────────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
/** A fresh box has no editor config directory at all — the write must create it. */
|
|
174
|
+
async function scenarioFreshInstall(spec: ProviderSpec): Promise<void> {
|
|
175
|
+
await withSandboxHome(spec, async (home) => {
|
|
176
|
+
check(!spec.isInstalled(), 'reports not-installed on a fresh HOME');
|
|
177
|
+
|
|
178
|
+
const r = mcpAdd();
|
|
179
|
+
if (!checkRun(r, 'mcp add exits 0')) return;
|
|
180
|
+
check(spec.isInstalled(), 'the editor reports the server installed');
|
|
181
|
+
|
|
182
|
+
if (spec.configPath) {
|
|
183
|
+
const contents = readIfPresent(path.join(home, spec.configPath));
|
|
184
|
+
if (!check(contents !== null, `wrote ${spec.configPath}`)) return;
|
|
185
|
+
// Match the whole configured value, not a URL substring — `includes`
|
|
186
|
+
// on a bare URL also accepts hosts that merely embed it.
|
|
187
|
+
check(
|
|
188
|
+
contents!.includes(`"${MCP_URL}"`),
|
|
189
|
+
'the written config carries exactly the MCP url',
|
|
190
|
+
);
|
|
191
|
+
check(
|
|
192
|
+
/startup_timeout_sec\s*=/.test(contents!),
|
|
193
|
+
'the written config carries a startup timeout',
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* A config the wizard is happy with and the editor rejects is the worst case:
|
|
201
|
+
* we report "installed" and the user loses every server in the file.
|
|
202
|
+
*/
|
|
203
|
+
async function scenarioEditorAcceptsConfig(spec: ProviderSpec): Promise<void> {
|
|
204
|
+
await withSandboxHome(spec, async () => {
|
|
205
|
+
const r = mcpAdd();
|
|
206
|
+
if (!checkRun(r, 'mcp add exits 0')) return;
|
|
207
|
+
|
|
208
|
+
check(
|
|
209
|
+
run(spec.binary, ['mcp', 'list']).status === 0,
|
|
210
|
+
`${spec.binary} mcp list parses the config (exit 0)`,
|
|
211
|
+
);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Re-running the wizard is the common case, not the edge case. */
|
|
216
|
+
async function scenarioIdempotent(spec: ProviderSpec): Promise<void> {
|
|
217
|
+
await withSandboxHome(spec, async (home) => {
|
|
218
|
+
const first = mcpAdd();
|
|
219
|
+
if (!checkRun(first, 'first add exits 0')) return;
|
|
220
|
+
|
|
221
|
+
const second = mcpAdd();
|
|
222
|
+
checkRun(second, 'second add exits 0');
|
|
223
|
+
check(
|
|
224
|
+
/already installed|nothing changed/i.test(second.stdout + second.stderr),
|
|
225
|
+
'second add reports it changed nothing',
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
if (spec.configPath) {
|
|
229
|
+
const contents = readIfPresent(path.join(home, spec.configPath)) ?? '';
|
|
230
|
+
const sections = contents.match(/^\s*\[mcp_servers\.posthog\]/gm) ?? [];
|
|
231
|
+
// Two definitions of one TOML table is a parse error on the whole file.
|
|
232
|
+
check(
|
|
233
|
+
sections.length === 1,
|
|
234
|
+
`exactly one [mcp_servers.posthog] section (found ${sections.length})`,
|
|
235
|
+
);
|
|
236
|
+
check(
|
|
237
|
+
run(spec.binary, ['mcp', 'list']).status === 0,
|
|
238
|
+
'the editor still parses the config after a re-run',
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* `--features` is a documented flag. Honouring it or rejecting it are both
|
|
246
|
+
* defensible; accepting it and installing full scope anyway is not.
|
|
247
|
+
*/
|
|
248
|
+
async function scenarioFeaturesFlag(spec: ProviderSpec): Promise<void> {
|
|
249
|
+
await withSandboxHome(spec, async (home) => {
|
|
250
|
+
const r = mcpAdd('--features', 'flags,insights');
|
|
251
|
+
if (!checkRun(r, 'mcp add --features exits 0')) return;
|
|
252
|
+
|
|
253
|
+
// Claude Code owns its own store, so read the URL back through its CLI.
|
|
254
|
+
const url = spec.configPath
|
|
255
|
+
? readIfPresent(path.join(home, spec.configPath)) ?? ''
|
|
256
|
+
: run(spec.binary, ['mcp', 'get', 'posthog']).stdout;
|
|
257
|
+
|
|
258
|
+
check(
|
|
259
|
+
url.includes('features=flags,insights'),
|
|
260
|
+
'the installed URL carries the requested features',
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** The update path is separate code from the create path, and can no-op silently. */
|
|
266
|
+
async function scenarioUpdatesExistingEntry(spec: ProviderSpec): Promise<void> {
|
|
267
|
+
if (!spec.configPath) return;
|
|
268
|
+
|
|
269
|
+
await withSandboxHome(spec, async (home) => {
|
|
270
|
+
seed(
|
|
271
|
+
home,
|
|
272
|
+
spec.configPath!,
|
|
273
|
+
[
|
|
274
|
+
'[mcp_servers.unrelated]',
|
|
275
|
+
'command = "some-other-server"',
|
|
276
|
+
'',
|
|
277
|
+
'[mcp_servers.posthog]',
|
|
278
|
+
'url = "https://mcp.posthog.com/mcp?features=flags"',
|
|
279
|
+
'startup_timeout_sec = 30',
|
|
280
|
+
'',
|
|
281
|
+
].join('\n'),
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
const r = mcpAdd();
|
|
285
|
+
if (!checkRun(r, 'mcp add exits 0')) return;
|
|
286
|
+
|
|
287
|
+
const contents = readIfPresent(path.join(home, spec.configPath!)) ?? '';
|
|
288
|
+
check(
|
|
289
|
+
contents.includes(`url = "${MCP_URL}"`),
|
|
290
|
+
'the stale features URL was replaced',
|
|
291
|
+
);
|
|
292
|
+
check(
|
|
293
|
+
contents.includes('[mcp_servers.unrelated]'),
|
|
294
|
+
"an unrelated user's server survived the write",
|
|
295
|
+
);
|
|
296
|
+
check(
|
|
297
|
+
run(spec.binary, ['mcp', 'list']).status === 0,
|
|
298
|
+
'the editor still parses the config after an update',
|
|
299
|
+
);
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* TOML does not care about key order, so anything assuming `url` sits on the
|
|
305
|
+
* line after the header reads this as "no match".
|
|
306
|
+
*/
|
|
307
|
+
async function scenarioUpdatesReorderedEntry(
|
|
308
|
+
spec: ProviderSpec,
|
|
309
|
+
): Promise<void> {
|
|
310
|
+
if (!spec.configPath) return;
|
|
311
|
+
|
|
312
|
+
await withSandboxHome(spec, async (home) => {
|
|
313
|
+
seed(
|
|
314
|
+
home,
|
|
315
|
+
spec.configPath!,
|
|
316
|
+
[
|
|
317
|
+
'[mcp_servers.posthog]',
|
|
318
|
+
'startup_timeout_sec = 30',
|
|
319
|
+
'url = "https://mcp.posthog.com/mcp?features=flags"',
|
|
320
|
+
'',
|
|
321
|
+
].join('\n'),
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
const r = mcpAdd();
|
|
325
|
+
const contents = readIfPresent(path.join(home, spec.configPath!)) ?? '';
|
|
326
|
+
|
|
327
|
+
// Rewrite it or refuse and say so; claiming success without writing is the
|
|
328
|
+
// only outcome that isn't defensible.
|
|
329
|
+
if (r.status !== 0) {
|
|
330
|
+
pass("declined to edit a key order it can't handle");
|
|
331
|
+
} else {
|
|
332
|
+
check(
|
|
333
|
+
contents.includes(`url = "${MCP_URL}"`),
|
|
334
|
+
'a reported success actually replaced the stale URL',
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* TOML permits a quoted table key. A substring scan for the bare form misses it
|
|
342
|
+
* and appends a second definition of the same table.
|
|
343
|
+
*/
|
|
344
|
+
async function scenarioQuotedTableKey(spec: ProviderSpec): Promise<void> {
|
|
345
|
+
if (!spec.configPath) return;
|
|
346
|
+
|
|
347
|
+
await withSandboxHome(spec, async (home) => {
|
|
348
|
+
seed(
|
|
349
|
+
home,
|
|
350
|
+
spec.configPath!,
|
|
351
|
+
['[mcp_servers."posthog"]', `url = "${MCP_URL}"`, ''].join('\n'),
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
mcpAdd();
|
|
355
|
+
|
|
356
|
+
check(
|
|
357
|
+
run(spec.binary, ['mcp', 'list']).status === 0,
|
|
358
|
+
'the editor still parses a config that used a quoted table key',
|
|
359
|
+
);
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** `mcp remove` reporting success while leaving the server in place is the bug. */
|
|
364
|
+
async function scenarioRemove(spec: ProviderSpec): Promise<void> {
|
|
365
|
+
await withSandboxHome(spec, async () => {
|
|
366
|
+
const added = mcpAdd();
|
|
367
|
+
if (!checkRun(added, 'mcp add exits 0')) return;
|
|
368
|
+
|
|
369
|
+
const removed = mcpRemove();
|
|
370
|
+
if (!checkRun(removed, 'mcp remove exits 0')) return;
|
|
371
|
+
|
|
372
|
+
check(!spec.isInstalled(), 'the editor reports the server gone');
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* The wizard prints a login command for the user to run by hand. If the editor
|
|
378
|
+
* renames that subcommand, nothing else in the build notices.
|
|
379
|
+
*/
|
|
380
|
+
async function scenarioLoginCommandExists(spec: ProviderSpec): Promise<void> {
|
|
381
|
+
const probe = run(spec.binary, spec.loginProbe);
|
|
382
|
+
check(
|
|
383
|
+
probe.status === 0,
|
|
384
|
+
`${spec.binary} ${spec.loginProbe.join(' ')} exists (exit 0)`,
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ── runner ────────────────────────────────────────────────────────────────
|
|
389
|
+
|
|
390
|
+
const SCENARIOS: Array<{
|
|
391
|
+
name: string;
|
|
392
|
+
run: (spec: ProviderSpec) => Promise<void>;
|
|
393
|
+
}> = [
|
|
394
|
+
{ name: 'fresh install on an empty HOME', run: scenarioFreshInstall },
|
|
395
|
+
{
|
|
396
|
+
name: 'the editor can parse what we wrote',
|
|
397
|
+
run: scenarioEditorAcceptsConfig,
|
|
398
|
+
},
|
|
399
|
+
{ name: 're-running the install is a no-op', run: scenarioIdempotent },
|
|
400
|
+
{ name: '--features reaches the installed URL', run: scenarioFeaturesFlag },
|
|
401
|
+
{ name: 'updates an existing entry', run: scenarioUpdatesExistingEntry },
|
|
402
|
+
{
|
|
403
|
+
name: 'updates an entry with reordered keys',
|
|
404
|
+
run: scenarioUpdatesReorderedEntry,
|
|
405
|
+
},
|
|
406
|
+
{ name: 'survives a quoted TOML table key', run: scenarioQuotedTableKey },
|
|
407
|
+
{ name: 'remove actually removes', run: scenarioRemove },
|
|
408
|
+
{
|
|
409
|
+
name: 'the login command it prints exists',
|
|
410
|
+
run: scenarioLoginCommandExists,
|
|
411
|
+
},
|
|
412
|
+
];
|
|
413
|
+
|
|
414
|
+
function parseProvider(): Provider {
|
|
415
|
+
const arg = process.argv
|
|
416
|
+
.find((a) => a.startsWith('--provider='))
|
|
417
|
+
?.split('=')[1];
|
|
418
|
+
if (!arg || !(PROVIDERS as readonly string[]).includes(arg)) {
|
|
419
|
+
console.error(`Pass --provider=<${PROVIDERS.join('|')}>.`);
|
|
420
|
+
process.exit(2);
|
|
421
|
+
}
|
|
422
|
+
return arg as Provider;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async function main(): Promise<void> {
|
|
426
|
+
const provider = parseProvider();
|
|
427
|
+
const spec = SPECS[provider];
|
|
428
|
+
|
|
429
|
+
if (!fs.existsSync(WIZARD)) {
|
|
430
|
+
console.error(`✗ ${WIZARD} not found — run \`pnpm build\` first.`);
|
|
431
|
+
process.exit(1);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Hard failure, not a skip — a skipped matrix leg looks green on the PR page.
|
|
435
|
+
let version: string;
|
|
436
|
+
try {
|
|
437
|
+
version = execFileSync(spec.binary, ['--version'], { encoding: 'utf-8' })
|
|
438
|
+
.trim()
|
|
439
|
+
.split('\n')[0]!;
|
|
440
|
+
} catch (err) {
|
|
441
|
+
console.error(
|
|
442
|
+
`✗ ${spec.binary} is not runnable on this machine — ${
|
|
443
|
+
(err as Error).message
|
|
444
|
+
}`,
|
|
445
|
+
);
|
|
446
|
+
process.exit(1);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
console.log(`\nMCP install smoke test — ${provider} (${version})\n`);
|
|
450
|
+
|
|
451
|
+
for (const scenario of SCENARIOS) {
|
|
452
|
+
currentScenario = scenario.name;
|
|
453
|
+
console.log(`▸ ${scenario.name}`);
|
|
454
|
+
try {
|
|
455
|
+
await scenario.run(spec);
|
|
456
|
+
} catch (err) {
|
|
457
|
+
fail(`threw — ${(err as Error).message}`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (failures.length > 0) {
|
|
462
|
+
console.error(
|
|
463
|
+
`\n✗ MCP install smoke test FAILED for ${provider} (${failures.length}):`,
|
|
464
|
+
);
|
|
465
|
+
for (const f of failures) console.error(` - ${f}`);
|
|
466
|
+
process.exit(1);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
console.log(
|
|
470
|
+
`\n✔ MCP install smoke test passed for ${provider} (${SCENARIOS.length} scenarios)`,
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
main().catch((err) => {
|
|
475
|
+
console.error('MCP install smoke test crashed:', err);
|
|
476
|
+
process.exit(1);
|
|
477
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { D as DEFAULT_AGENT_MODEL, pt as WIZARD_USER_AGENT, s as logToFile } from "./debug-
|
|
2
|
-
import {
|
|
1
|
+
import { D as DEFAULT_AGENT_MODEL, pt as WIZARD_USER_AGENT, s as logToFile } from "./debug-BKqv-yfT.js";
|
|
2
|
+
import { n as analytics } from "./analytics-D8JNuPAT.js";
|
|
3
|
+
import { d as sanitizeAgentSubprocessEnv, n as buildRunTags, t as buildAgentEnv } from "./agent-interface-D3MNaDp3.js";
|
|
3
4
|
//#region src/lib/agent/mcp-prompt-streaming.ts
|
|
4
5
|
let _sdkModule = null;
|
|
5
6
|
async function loadSdk() {
|
|
@@ -127,8 +128,23 @@ function buildTerminalFitPrompt() {
|
|
|
127
128
|
`- Avoid value-laden phrases like "worth investigating", "concerning", "red flag", "problematic", "suspicious", "alarming" when describing the user's metrics. Stick to what the numbers show; the user draws conclusions.`
|
|
128
129
|
].join("\n");
|
|
129
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Gateway trace tags for a tutorial prompt run — without them the gateway has
|
|
133
|
+
* nothing to attribute its `$ai_generation` events to. Returns `{}` when no
|
|
134
|
+
* program is supplied. Exported so the contract is testable without the SDK.
|
|
135
|
+
*/
|
|
136
|
+
function buildTutorialRunTags(args) {
|
|
137
|
+
if (!args.programId) return {};
|
|
138
|
+
return buildRunTags({
|
|
139
|
+
programId: args.programId,
|
|
140
|
+
integration: args.integration ?? args.programId,
|
|
141
|
+
runId: analytics.runId,
|
|
142
|
+
build: analytics.build
|
|
143
|
+
});
|
|
144
|
+
}
|
|
130
145
|
async function* runMcpPromptViaSdk(args) {
|
|
131
146
|
const { prompt, credentials, signal, resumeSessionId } = args;
|
|
147
|
+
const wizardMetadata = buildTutorialRunTags(args);
|
|
132
148
|
process.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = "true";
|
|
133
149
|
const gatewayUrl = credentials.host.gatewayUrl;
|
|
134
150
|
process.env.ANTHROPIC_BASE_URL = gatewayUrl;
|
|
@@ -205,7 +221,7 @@ async function* runMcpPromptViaSdk(args) {
|
|
|
205
221
|
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "true",
|
|
206
222
|
POSTHOG_MCP_TOKEN: credentials.accessToken,
|
|
207
223
|
MCP_CONNECTION_NONBLOCKING: "0",
|
|
208
|
-
ANTHROPIC_CUSTOM_HEADERS: buildAgentEnv({}, {})
|
|
224
|
+
ANTHROPIC_CUSTOM_HEADERS: buildAgentEnv(wizardMetadata ?? {}, {})
|
|
209
225
|
}
|
|
210
226
|
}
|
|
211
227
|
});
|
|
@@ -230,4 +246,4 @@ async function* runMcpPromptViaSdk(args) {
|
|
|
230
246
|
//#endregion
|
|
231
247
|
export { runMcpPromptViaSdk };
|
|
232
248
|
|
|
233
|
-
//# sourceMappingURL=mcp-prompt-streaming
|
|
249
|
+
//# sourceMappingURL=mcp-prompt-streaming--sRErHKv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-prompt-streaming--sRErHKv.js","names":[],"sources":["../src/lib/agent/mcp-prompt-streaming.ts"],"sourcesContent":["/**\n * Streaming prompt runner for the McpSuggestedPromptsScreen.\n *\n * Calls the Claude Agent SDK's `query()` directly with just the PostHog\n * MCP server configured — no skills, no sandbox, no settings sources,\n * no wizard-tools. This is the lightweight cousin of `runAgent` in\n * `agent-interface.ts`: same SDK, much narrower surface, suitable for\n * \"user asked a question, show the answer\" interactions.\n *\n * The function is an async generator that yields `AgentChunk`s extracted\n * from the SDK's message stream. Callers (the screen) consume them via\n * `for await (...)` and render as they arrive.\n */\n\nimport type { AgentChunk } from '@ui/tui/services/mcp-suggested-prompts-services';\nimport type { Credentials } from '@lib/wizard-session';\nimport { DEFAULT_AGENT_MODEL, WIZARD_USER_AGENT } from '@lib/constants';\nimport { logToFile } from '@utils/debug';\nimport { buildAgentEnv, buildRunTags } from '@lib/agent/agent-interface';\nimport { sanitizeAgentSubprocessEnv } from '@lib/agent/agent-env-isolation';\nimport { analytics } from '@utils/analytics';\n\n// Cached SDK module — first call pays the dynamic-import cost; later\n// calls reuse the same module.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet _sdkModule: any | null = null;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nasync function loadSdk(): Promise<any> {\n if (!_sdkModule) {\n _sdkModule = await import('@anthropic-ai/claude-agent-sdk');\n }\n return _sdkModule;\n}\n\nconst MODEL = DEFAULT_AGENT_MODEL;\n\n// Bounded turn count so a single prompt can't loop forever on the\n// user's nickel. 20 gives the agent room for non-trivial multi-step\n// chains (multi-tool reads → reason → write → verify → summarize) while\n// still capping runaway loops. Worth tuning down once we see real\n// telemetry on average turn counts per prompt.\nconst MAX_TURNS = 30;\n\n/**\n * Extract a short, single-line summary from an arbitrary value. Used\n * for tool-call args and tool-result bodies so the screen has something\n * compact to render.\n */\nfunction summarize(value: unknown, maxLen = 120): string {\n if (value == null) return '';\n let text: string;\n if (typeof value === 'string') text = value;\n else {\n try {\n text = JSON.stringify(value);\n } catch {\n text = String(value);\n }\n }\n text = text.replace(/\\s+/g, ' ').trim();\n if (text.length > maxLen) text = text.slice(0, maxLen - 1) + '…';\n return text;\n}\n\n/**\n * Convert one SDK message into zero or more AgentChunks. Mirrors the\n * subset of message shapes the wizard's main runAgent middleware\n * handles, but narrowed to just the kinds the screen needs to render.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction messageToChunks(message: any): AgentChunk[] {\n const chunks: AgentChunk[] = [];\n\n if (message?.type === 'assistant') {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const content = message.message?.content;\n if (Array.isArray(content)) {\n for (const block of content) {\n if (!block || typeof block !== 'object') continue;\n const type = (block as { type?: string }).type;\n if (type === 'text') {\n const text = (block as { text?: string }).text ?? '';\n if (text) chunks.push({ kind: 'text', text });\n } else if (type === 'tool_use') {\n const name = (block as { name?: string }).name ?? 'tool';\n const input = (block as { input?: unknown }).input;\n // CLI mode's exec tool takes a `command` string; keep it verbatim so\n // the screen can recover the inner tool name for follow-ups.\n const command = (input as { command?: unknown })?.command;\n chunks.push({\n kind: 'tool-call',\n toolName: name,\n detail: summarize(input),\n command: typeof command === 'string' ? command : undefined,\n });\n }\n }\n }\n }\n\n if (message?.type === 'user') {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const content = message.message?.content;\n if (Array.isArray(content)) {\n for (const block of content) {\n if (!block || typeof block !== 'object') continue;\n const type = (block as { type?: string }).type;\n if (type === 'tool_result') {\n const detail = summarize((block as { content?: unknown }).content);\n chunks.push({ kind: 'tool-result', toolName: 'tool', detail });\n }\n }\n }\n }\n\n if (message?.type === 'result') {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const sessionId = (message as { session_id?: string }).session_id;\n chunks.push({ kind: 'done', sessionId });\n }\n\n return chunks;\n}\n\n/**\n * Build a system-prompt append that nudges the agent to fit its response\n * inside the current terminal window. We can't actually constrain Claude\n * — this is a soft cap that the model usually honors. The screen also\n * applies a hard truncation cap as a fallback for non-compliant runs.\n *\n * Core principle nudged at the model: TALL CONTENT IS BAD, WIDE CONTENT\n * IS GOOD. Default terminal is 120 columns × 24 rows — that's a lot of\n * horizontal space, not much vertical. Spread data across columns, never\n * stack it down rows when a horizontal layout would work.\n */\nfunction buildTerminalFitPrompt(): string {\n const cols = process.stdout.columns ?? 120;\n const rows = process.stdout.rows ?? 24;\n // Reserve rows for wizard chrome (title, status, hint, margins).\n const messageBudget = Math.max(8, rows - 10);\n\n return [\n `You are responding inside a CLI window that is exactly ${cols} columns wide and ${rows} rows tall. The user CAN'T SCROLL — your entire reply must fit on screen.`,\n ``,\n `LAYOUT PRINCIPLE: tall content is the enemy, wide content is your friend. You have ${cols} columns of horizontal space; use them. Spread data across columns instead of stacking it down rows.`,\n ``,\n `Tables:`,\n `- Max 5 body rows. The prompts the user picks already constrain results to 5 or fewer — honor that and do not pad with extra rows.`,\n `- If a tool result returns more than 5 items, show the top 5 and mention the rest count inline (e.g. \"...and 12 more\").`,\n `- Prefer transposing wide-but-short data (items across columns, metrics down rows) when labels are short.`,\n `- A two-column table with many rows is the tall layout to AVOID.`,\n ``,\n `Other limits:`,\n `- Aim for 3-5 lines of prose. Maximum ${messageBudget} lines total.`,\n `- DO NOT announce what you are about to do. Skip preamble like \"I'll query…\", \"Let me check…\", \"Now I'll…\", \"I'm going to…\". Go straight to running tools and then the answer.`,\n `- Lists: if there are 6+ short items, format them inline (comma-separated), not as a vertical bullet list.`,\n `- For tool results, summarize the 1-3 numbers that matter. Do NOT echo raw JSON or the full payload.`,\n `- Code blocks: no language tag, no leading blank lines.`,\n `- No closing pleasantries (\"let me know if…\", \"feel free to…\"). Stop when the answer is delivered.`,\n `- No section headers unless the response actually has multiple sections.`,\n ``,\n `Naming saved artifacts:`,\n `- Every dashboard, insight, notebook, or annotation you create MUST include \"(wizard MCP tutorial)\" at the end of its title or name field. Examples: \"Weekly signups (wizard MCP tutorial)\", \"Top errors this week (wizard MCP tutorial)\", \"Onboarding analysis (wizard MCP tutorial)\".`,\n `- This applies on both create AND rename calls. If the user asks you to rename a saved artifact, preserve the \"(wizard MCP tutorial)\" suffix unless they explicitly ask you to drop it.`,\n `- Reason: lets the user find / clean up everything this tutorial created from one search in PostHog. Don't skip it — it's the only signal they'll have that an artifact came from the wizard.`,\n ``,\n `Tone & framing:`,\n `- This is a tutorial demoing PostHog (the product the user just installed). You are showing it off, not auditing it. Stay constructive and neutral about PostHog throughout.`,\n `- Don't editorialize about PostHog's reliability, performance, or cost. Describe what the data shows; treat anomalies as the user's data, not a platform issue.`,\n `- If a tool call fails or returns nothing, say \"the query didn't return data — try a different angle\" or similar. Do NOT speculate about outages, gateway issues, MCP problems, or service health.`,\n `- Avoid value-laden phrases like \"worth investigating\", \"concerning\", \"red flag\", \"problematic\", \"suspicious\", \"alarming\" when describing the user's metrics. Stick to what the numbers show; the user draws conclusions.`,\n ].join('\\n');\n}\n\n/**\n * Gateway trace tags for a tutorial prompt run — without them the gateway has\n * nothing to attribute its `$ai_generation` events to. Returns `{}` when no\n * program is supplied. Exported so the contract is testable without the SDK.\n */\nexport function buildTutorialRunTags(args: {\n programId?: string;\n integration?: string;\n}): Record<string, string> {\n if (!args.programId) return {};\n return buildRunTags({\n programId: args.programId,\n // The tutorial usually has no detected framework; fall back to the\n // program so the axis is never an empty string.\n integration: args.integration ?? args.programId,\n runId: analytics.runId,\n build: analytics.build,\n });\n}\n\nexport async function* runMcpPromptViaSdk(args: {\n prompt: string;\n credentials: Credentials;\n signal: AbortSignal;\n /** When set, the SDK loads the named session's prior turns as\n * context so the follow-up prompt can reference what the agent\n * already showed. */\n resumeSessionId?: string;\n /** Program this run's gateway spend attributes to; omitting it leaves the\n * spend unattributed. */\n programId?: string;\n /** Integration label for the trace tags; the tutorial usually has none. */\n integration?: string;\n}): AsyncIterable<AgentChunk> {\n const { prompt, credentials, signal, resumeSessionId } = args;\n\n // Assembled here rather than passed in so the TUI service layer doesn't\n // have to import this module's dependencies.\n const wizardMetadata = buildTutorialRunTags(args);\n\n // Route the SDK's LLM calls through the PostHog LLM gateway, authed\n // with the user's OAuth access token. Set BEFORE loading the SDK in\n // case any in-process code reads env at module init (cached base\n // URLs, OAuth setup, etc.) — same reason `initializeAgent` does this\n // before its query() call. Without these the SDK tries to\n // authenticate directly against Anthropic and 401s with \"Invalid\n // authentication credentials\".\n process.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = 'true';\n\n // Route through the PostHog LLM gateway, authed with the user's OAuth token.\n const gatewayUrl = credentials.host.gatewayUrl;\n process.env.ANTHROPIC_BASE_URL = gatewayUrl;\n process.env.ANTHROPIC_AUTH_TOKEN = credentials.accessToken;\n process.env.CLAUDE_CODE_OAUTH_TOKEN = credentials.accessToken;\n\n logToFile(\n `[runMcpPromptViaSdk] gatewayUrl=${gatewayUrl} tokenPrefix=${\n credentials.accessToken\n ? credentials.accessToken.slice(0, 4) + '***'\n : '(missing)'\n }`,\n );\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const { query } = await loadSdk();\n\n // Bridge external AbortSignal → SDK AbortController.\n const abortController = new AbortController();\n if (signal.aborted) abortController.abort();\n else\n signal.addEventListener('abort', () => abortController.abort(), {\n once: true,\n });\n\n const mcpUrl = credentials.host.mcpUrl;\n logToFile(\n `[runMcpPromptViaSdk] mcpUrl=${mcpUrl} model=${MODEL} resume=${\n resumeSessionId ?? '(none)'\n }`,\n );\n\n // The SDK expects an async generator for the prompt that stays open\n // until the result is received. For a single-turn prompt we yield one\n // user message and then await an abort (which fires when streaming\n // completes or the caller cancels).\n const createPromptStream = async function* () {\n yield {\n type: 'user' as const,\n session_id: '',\n message: { role: 'user' as const, content: prompt },\n parent_tool_use_id: null,\n };\n // Hold the stream open until abort. The SDK closes its end when it\n // sees a `result` message; we close ours via the abortController in\n // the finally block below.\n await new Promise<void>((resolve) => {\n if (abortController.signal.aborted) {\n resolve();\n return;\n }\n abortController.signal.addEventListener('abort', () => resolve(), {\n once: true,\n });\n });\n };\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n const response = query({\n prompt: createPromptStream(),\n options: {\n abortController,\n model: MODEL,\n cwd: process.cwd(),\n permissionMode: 'acceptEdits',\n maxTurns: MAX_TURNS,\n // Match agent-interface.ts — the 1M context beta is what keeps\n // resumed follow-up sessions from truncating after a few turns.\n betas: ['context-1m-2025-08-07'],\n // Only load project-level skills/settings. Without this the SDK\n // defaults to ['user', 'project'] and a user's\n // `~/.claude/settings.json` (apiKeyHelper / env block) can\n // override the OAuth routing we set above.\n settingSources: ['project'],\n // When set, the SDK replays the named session's turns into the\n // new query so the follow-up prompt has full conversation\n // context. Omit on the first prompt for a fresh session.\n ...(resumeSessionId ? { resume: resumeSessionId } : {}),\n // Without `canUseTool` the SDK falls back to \"ask the user\" on\n // every MCP tool call — `permissionMode: 'acceptEdits'` only\n // relaxes Edit/Write, not MCP. Our Ink TUI has no surface to\n // answer that prompt, so the agent would stall mid-stream\n // saying things like \"needs your approval to create dashboard\".\n // The tutorial's whole point is demoing the MCP tools against\n // the user's project, so we auto-allow everything that matches\n // the prefix and deny anything else (defense in depth — the\n // `allowedTools` filter above already enforces this).\n canUseTool: (toolName: string, input: unknown) => {\n if (toolName.startsWith('mcp__posthog-wizard__')) {\n return Promise.resolve({\n behavior: 'allow' as const,\n updatedInput: (input ?? {}) as Record<string, unknown>,\n });\n }\n logToFile(`[runMcpPromptViaSdk] denying non-MCP tool: ${toolName}`);\n return Promise.resolve({\n behavior: 'deny' as const,\n message: `${toolName} is not available in the MCP tutorial — only PostHog MCP tools are permitted.`,\n });\n },\n systemPrompt: {\n type: 'preset',\n preset: 'claude_code',\n append: buildTerminalFitPrompt(),\n },\n mcpServers: {\n 'posthog-wizard': {\n type: 'http',\n url: mcpUrl,\n headers: {\n // Env reference, not the token: the SDK puts this config on the\n // spawned CLI's argv, where `ps` shows it to any local process.\n Authorization: 'Bearer ${POSTHOG_MCP_TOKEN}',\n 'User-Agent': WIZARD_USER_AGENT,\n },\n // CLI mode's single `exec` tool carries the full command reference\n // on its schema — keep it in context, never deferred behind tool\n // search.\n alwaysLoad: true,\n },\n },\n // Only let the agent use MCP tools — no shell, no file I/O,\n // no Read/Edit/Write. This is a chat-with-MCP run, not a\n // wizard skill execution.\n allowedTools: ['mcp__posthog-wizard__*'],\n env: {\n // Drop the ENTIRE ANTHROPIC_*/CLAUDE_CODE_* namespace from the\n // inherited env so no shell/settings value can silently bypass the\n // PostHog gateway and defeat quota tracking / the OAuth flow; the\n // wizard's own gateway routing is injected fresh below. See\n // agent-env-isolation.ts.\n ...sanitizeAgentSubprocessEnv(process.env),\n // Gateway routing — injected explicitly (set on process.env above;\n // the strip removed them from the inherited copy, so re-add here).\n ANTHROPIC_BASE_URL: process.env.ANTHROPIC_BASE_URL,\n ANTHROPIC_AUTH_TOKEN: process.env.ANTHROPIC_AUTH_TOKEN,\n CLAUDE_CODE_OAUTH_TOKEN: process.env.CLAUDE_CODE_OAUTH_TOKEN,\n CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: 'true',\n // The MCP config resolves this in the child; sending the value would\n // put it on the CLI's argv.\n POSTHOG_MCP_TOKEN: credentials.accessToken,\n // SDK 0.3.142+ connects MCP servers in the background by\n // default; without this the agent may try to call tools\n // before posthog-wizard is connected on turn 1.\n MCP_CONNECTION_NONBLOCKING: '0',\n // Bedrock fallback plus this run's trace tags — the gateway reads\n // these to attribute its `$ai_generation` events. Flags stay empty:\n // the tutorial doesn't fork on any.\n ANTHROPIC_CUSTOM_HEADERS: buildAgentEnv(wizardMetadata ?? {}, {}),\n },\n },\n });\n\n for await (const message of response as AsyncIterable<unknown>) {\n if (signal.aborted) return;\n for (const chunk of messageToChunks(message)) {\n yield chunk;\n if (chunk.kind === 'done') return;\n }\n }\n } catch (err) {\n const text = err instanceof Error ? err.message : String(err);\n logToFile(`[runMcpPromptViaSdk] error: ${text}`);\n yield { kind: 'error', text };\n } finally {\n // Closes the prompt stream so `query()` shuts down cleanly even if\n // we never saw a 'result' message.\n abortController.abort();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { f as getUI } from "./debug-
|
|
1
|
+
import { f as getUI } from "./debug-BKqv-yfT.js";
|
|
2
2
|
//#region src/commands/basic-integration/non-interactive.ts
|
|
3
3
|
/** Print the "needs a TTY" error and exit. Used when no `--ci` flag and no TTY. */
|
|
4
4
|
function failNonInteractive() {
|
|
@@ -9,4 +9,4 @@ function failNonInteractive() {
|
|
|
9
9
|
//#endregion
|
|
10
10
|
export { failNonInteractive };
|
|
11
11
|
|
|
12
|
-
//# sourceMappingURL=non-interactive-
|
|
12
|
+
//# sourceMappingURL=non-interactive-CNoYoEZp.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"non-interactive-
|
|
1
|
+
{"version":3,"file":"non-interactive-CNoYoEZp.js","names":[],"sources":["../src/commands/basic-integration/non-interactive.ts"],"sourcesContent":["import { getUI } from '@ui';\n\n/** Print the \"needs a TTY\" error and exit. Used when no `--ci` flag and no TTY. */\nexport function failNonInteractive(): void {\n getUI().intro('PostHog Wizard');\n getUI().log.error(\n 'This installer requires an interactive terminal (TTY) to run.\\n' +\n 'It appears you are running in a non-interactive environment.\\n' +\n 'Please run the wizard in an interactive terminal.\\n\\n' +\n 'For CI/CD environments, use --ci mode:\\n' +\n ' npx @posthog/wizard --ci --region us --api-key phx_xxx',\n );\n process.exit(1);\n}\n"],"mappings":";;;AAGA,SAAgB,qBAA2B;AACzC,QAAO,CAAC,MAAM,iBAAiB;AAC/B,QAAO,CAAC,IAAI,MACV,qRAKD;AACD,SAAQ,KAAK,EAAE"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as analytics } from "./analytics-
|
|
2
|
-
import { a as applyReadHandoffs, i as applyEnqueue, n as REMARK_ASK, r as applyComplete, t as HANDOFF_FIELDS } from "./queue-tools-
|
|
1
|
+
import { n as analytics } from "./analytics-D8JNuPAT.js";
|
|
2
|
+
import { a as applyReadHandoffs, i as applyEnqueue, n as REMARK_ASK, r as applyComplete, t as HANDOFF_FIELDS } from "./queue-tools-DABh1Ssc.js";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
4
|
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
//#region src/lib/agent/runner/harness/pi/orchestrator-tools.ts
|
|
@@ -102,4 +102,4 @@ function createPiOrchestratorTools(ctx) {
|
|
|
102
102
|
//#endregion
|
|
103
103
|
export { createPiOrchestratorTools };
|
|
104
104
|
|
|
105
|
-
//# sourceMappingURL=orchestrator-tools-
|
|
105
|
+
//# sourceMappingURL=orchestrator-tools-8bGyDDhn.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator-tools-
|
|
1
|
+
{"version":3,"file":"orchestrator-tools-8bGyDDhn.js","names":[],"sources":["../src/lib/agent/runner/harness/pi/orchestrator-tools.ts"],"sourcesContent":["/**\n * Orchestrator queue tools as pi custom tools. The queue lives in-process, and\n * `applyEnqueue` / `applyComplete` / `applyReadHandoffs` are plain exported\n * functions — so pi needs no MCP transport, just `defineTool` wrappers around\n * the same guards and apply logic the anthropic path mounts through the\n * wizard-tools MCP server. Tool names match the MCP short names so the shared\n * agent prompts are unchanged.\n *\n * Lazily imported (typebox is ESM and must stay out of the static module graph\n * so CommonJS unit tests can load the backend seam without parsing it).\n */\n\nimport { Type } from 'typebox';\nimport { defineTool } from '@earendil-works/pi-coding-agent';\nimport type { ToolDefinition } from '@earendil-works/pi-coding-agent';\nimport { analytics } from '@utils/analytics';\nimport {\n applyComplete,\n applyEnqueue,\n applyReadHandoffs,\n HANDOFF_FIELDS,\n REMARK_ASK,\n type EnqueueArgs,\n type OrchestratorToolsContext,\n} from '../../sequence/orchestrator/queue-tools';\nimport type { TaskHandoff } from '../../sequence/orchestrator/queue';\n\nfunction text(s: string): {\n content: [{ type: 'text'; text: string }];\n details: unknown;\n} {\n return { content: [{ type: 'text', text: s }], details: {} };\n}\n\n/** Mirrors the zod `HANDOFF_SHAPE`; both read their text from HANDOFF_FIELDS. */\nconst HANDOFF_PARAMS = Type.Object({\n goals: Type.String({ description: HANDOFF_FIELDS.goals }),\n did: Type.String({ description: HANDOFF_FIELDS.did }),\n forNextAgent: Type.String({ description: HANDOFF_FIELDS.forNextAgent }),\n filesTouched: Type.Optional(\n Type.Array(Type.String(), { description: HANDOFF_FIELDS.filesTouched }),\n ),\n evidence: Type.Optional(\n Type.String({ description: HANDOFF_FIELDS.evidence }),\n ),\n assumptions: Type.Optional(\n Type.String({ description: HANDOFF_FIELDS.assumptions }),\n ),\n conflict: Type.Optional(\n Type.String({ description: HANDOFF_FIELDS.conflict }),\n ),\n reportSection: Type.Optional(\n Type.String({ description: HANDOFF_FIELDS.reportSection }),\n ),\n});\n\n/** Exported so the parity test can compare both harnesses' field sets. */\nexport const PI_HANDOFF_PARAM_KEYS: readonly string[] = Object.keys(\n HANDOFF_PARAMS.properties,\n);\n\n/** The three queue tools bound to one agent's orchestrator context. */\nexport function createPiOrchestratorTools(\n ctx: OrchestratorToolsContext,\n): ToolDefinition[] {\n const enqueueTask = defineTool({\n name: 'enqueue_task',\n label: 'Enqueue task',\n description:\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 promptSnippet:\n 'enqueue_task(type, label, dependsOn, reason) — add a task to the queue; returns its id',\n parameters: Type.Object({\n type: Type.String({\n description: `The task type. One of: ${ctx.validTypes.join(', ')}.`,\n }),\n label: Type.Optional(\n Type.String({\n description:\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 ),\n inputs: Type.Optional(Type.Record(Type.String(), Type.Unknown())),\n dependsOn: Type.Optional(\n Type.Array(Type.String(), {\n description: 'Task ids that must be done before this task runs.',\n }),\n ),\n model: Type.Optional(Type.String()),\n reason: Type.String({\n description: 'One line on why this task is needed.',\n }),\n }),\n execute(_id, args) {\n const res = applyEnqueue(ctx, args as EnqueueArgs);\n if (!res.ok) {\n analytics.wizardCapture('orchestrator guard tripped', {\n guard: res.guard,\n type: (args as EnqueueArgs).type,\n });\n return Promise.resolve(text(`Error: ${res.message}`));\n }\n return Promise.resolve(text(JSON.stringify({ id: res.task.id })));\n },\n });\n\n const completeTask = defineTool({\n name: 'complete_task',\n label: 'Complete task',\n description:\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 'not needed' when the task does not apply to this project and you cannot do it (say why in the handoff) — not 'done'.\",\n promptSnippet:\n 'complete_task(status, handoff) — report your outcome exactly once when done',\n parameters: Type.Object({\n status: Type.Union([\n Type.Literal('done'),\n Type.Literal('failed'),\n Type.Literal('not needed'),\n ]),\n handoff: HANDOFF_PARAMS,\n remark: Type.Optional(Type.String({ description: REMARK_ASK })),\n }),\n execute(_id, args) {\n const res = applyComplete(\n ctx,\n args as {\n status: 'done' | 'failed' | 'not needed';\n handoff: TaskHandoff;\n remark?: string;\n },\n );\n if (!res.ok) return Promise.resolve(text(`Error: ${res.message}`));\n return Promise.resolve(text('ok'));\n },\n });\n\n const readHandoffs = defineTool({\n name: 'read_handoffs',\n label: 'Read handoffs',\n description:\n 'Read structured handoffs from earlier tasks. With no argument, returns the handoffs of your dependencies.',\n promptSnippet:\n 'read_handoffs() — read what earlier tasks handed off (defaults to your dependencies)',\n parameters: Type.Object({\n type: Type.Optional(Type.String()),\n taskId: Type.Optional(Type.String()),\n }),\n execute(_id, args) {\n const handoffs = applyReadHandoffs(\n ctx,\n args as { type?: string; taskId?: string },\n );\n return Promise.resolve(text(JSON.stringify(handoffs, null, 2)));\n },\n });\n\n return [enqueueTask, completeTask, readHandoffs];\n}\n"],"mappings":"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"}
|
|
@@ -38,4 +38,4 @@ function getInstalledPackageVersion(packageName, installDir) {
|
|
|
38
38
|
//#endregion
|
|
39
39
|
export { package_json_exports as i, getInstalledPackageVersion as n, hasDeclaredDependency as r, getDeclaredVersion as t };
|
|
40
40
|
|
|
41
|
-
//# sourceMappingURL=package-json-
|
|
41
|
+
//# sourceMappingURL=package-json-D0DCWpe5.js.map
|