@amplitude/wizard 1.0.0-beta.0 → 1.0.0-beta.2
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/dist/bin.js +191 -47
- package/dist/src/lib/agent-interface.js +10 -22
- package/dist/src/lib/agent-runner.js +4 -6
- package/dist/src/lib/commandments.js +1 -1
- package/dist/src/lib/constants.d.ts +1 -4
- package/dist/src/lib/constants.js +9 -8
- package/dist/src/lib/feature-flags.d.ts +37 -0
- package/dist/src/lib/feature-flags.js +119 -0
- package/dist/src/lib/wizard-session.d.ts +16 -0
- package/dist/src/lib/wizard-session.js +2 -0
- package/dist/src/run.js +1 -1
- package/dist/src/steps/add-mcp-server-to-clients/index.js +3 -3
- package/dist/src/steps/add-or-update-environment-variables.js +5 -17
- package/dist/src/steps/run-prettier.js +1 -1
- package/dist/src/steps/upload-environment-variables/index.js +2 -2
- package/dist/src/ui/tui/App.js +1 -1
- package/dist/src/ui/tui/components/ConsoleView.js +17 -6
- package/dist/src/ui/tui/components/TitleBar.d.ts +3 -1
- package/dist/src/ui/tui/components/TitleBar.js +17 -6
- package/dist/src/ui/tui/console-commands.d.ts +5 -2
- package/dist/src/ui/tui/console-commands.js +14 -5
- package/dist/src/ui/tui/screens/AuthScreen.d.ts +2 -1
- package/dist/src/ui/tui/screens/AuthScreen.js +166 -26
- package/dist/src/ui/tui/screens/ChecklistScreen.js +1 -1
- package/dist/src/ui/tui/screens/DataIngestionCheckScreen.js +13 -2
- package/dist/src/ui/tui/screens/IntroScreen.js +2 -2
- package/dist/src/ui/tui/screens/McpScreen.js +42 -27
- package/dist/src/ui/tui/screens/OutroScreen.js +1 -2
- package/dist/src/ui/tui/screens/SlackScreen.d.ts +0 -5
- package/dist/src/ui/tui/screens/SlackScreen.js +1 -11
- package/dist/src/ui/tui/store.d.ts +20 -0
- package/dist/src/ui/tui/store.js +68 -19
- package/dist/src/utils/analytics.d.ts +45 -3
- package/dist/src/utils/analytics.js +118 -47
- package/dist/src/utils/oauth.js +1 -1
- package/dist/src/utils/setup-utils.d.ts +11 -0
- package/dist/src/utils/setup-utils.js +81 -4
- package/dist/src/utils/shell-completions.d.ts +2 -2
- package/dist/src/utils/shell-completions.js +8 -1
- package/dist/src/utils/track-wizard-feedback.d.ts +5 -0
- package/dist/src/utils/track-wizard-feedback.js +25 -0
- package/package.json +13 -13
- package/dist/package.json +0 -144
package/dist/bin.js
CHANGED
|
@@ -142,11 +142,6 @@ void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
|
142
142
|
if (options.ci) {
|
|
143
143
|
// Use LoggingUI for CI mode (no dependencies, no prompts)
|
|
144
144
|
(0, ui_1.setUI)(new logging_ui_1.LoggingUI());
|
|
145
|
-
if (!options.apiKey) {
|
|
146
|
-
(0, ui_1.getUI)().intro(chalk_1.default.inverse(`Amplitude Wizard`));
|
|
147
|
-
(0, ui_1.getUI)().log.error('CI mode requires --api-key (Amplitude project API key)');
|
|
148
|
-
process.exit(1);
|
|
149
|
-
}
|
|
150
145
|
if (!options.installDir) {
|
|
151
146
|
(0, ui_1.getUI)().intro(chalk_1.default.inverse(`Amplitude Wizard`));
|
|
152
147
|
(0, ui_1.getUI)().log.error('CI mode requires --install-dir (directory to install Amplitude in)');
|
|
@@ -161,7 +156,8 @@ void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
|
161
156
|
'It appears you are running in a non-interactive environment.\n' +
|
|
162
157
|
'Please run the wizard in an interactive terminal.\n\n' +
|
|
163
158
|
'For CI/CD environments, use --ci mode:\n' +
|
|
164
|
-
' npx @amplitude/wizard --ci --api-key <your-key>
|
|
159
|
+
' npx @amplitude/wizard --ci --install-dir . [--api-key <your-key>]\n' +
|
|
160
|
+
' (--api-key is optional when a key can be resolved from env or stored credentials.)');
|
|
165
161
|
process.exit(1);
|
|
166
162
|
}
|
|
167
163
|
else {
|
|
@@ -205,12 +201,13 @@ void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
|
205
201
|
// returning users. This skips RegionSelect and Auth without requiring
|
|
206
202
|
// a persisted OAuth token.
|
|
207
203
|
const installDir = session.installDir;
|
|
208
|
-
const [{ getStoredUser, getStoredToken }, { readAmpliConfig }, { getAPIKey }, { getHostFromRegion }, { logToFile },] = await Promise.all([
|
|
204
|
+
const [{ getStoredUser, getStoredToken }, { readAmpliConfig }, { getAPIKey }, { getHostFromRegion }, { logToFile }, { fetchAmplitudeUser },] = await Promise.all([
|
|
209
205
|
import('./src/utils/ampli-settings.js'),
|
|
210
206
|
import('./src/lib/ampli-config.js'),
|
|
211
207
|
import('./src/utils/get-api-key.js'),
|
|
212
208
|
import('./src/utils/urls.js'),
|
|
213
209
|
import('./src/utils/debug.js'),
|
|
210
|
+
import('./src/lib/api.js'),
|
|
214
211
|
]);
|
|
215
212
|
// Zone: prefer a real (non-pending) stored user, fall back to
|
|
216
213
|
// the Zone field in the project-level ampli.json.
|
|
@@ -226,43 +223,108 @@ void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
|
226
223
|
}
|
|
227
224
|
// Skip Auth when we have a stored OAuth token — use it to fetch
|
|
228
225
|
// (or look up) the project API key, then pre-populate credentials.
|
|
226
|
+
// When the workspace has multiple environments (projects), defer to
|
|
227
|
+
// AuthScreen so the user can pick which project to instrument.
|
|
229
228
|
if (zone) {
|
|
230
229
|
const storedToken = realUser
|
|
231
230
|
? getStoredToken(realUser.id, realUser.zone)
|
|
232
231
|
: getStoredToken(undefined, zone);
|
|
233
232
|
if (storedToken) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
});
|
|
241
|
-
if (projectApiKey) {
|
|
242
|
-
logToFile('[bin] getAPIKey: resolved project API key');
|
|
243
|
-
(0, api_key_store_1.persistApiKey)(projectApiKey, installDir);
|
|
233
|
+
// Check local storage first — if a key is already persisted
|
|
234
|
+
// for this install dir, use it without fetching user data.
|
|
235
|
+
const { readApiKeyWithSource } = await import('./src/utils/api-key-store.js');
|
|
236
|
+
const localKey = readApiKeyWithSource(installDir);
|
|
237
|
+
if (localKey) {
|
|
238
|
+
logToFile('[bin] using locally stored API key');
|
|
244
239
|
session.credentials = {
|
|
245
240
|
accessToken: storedToken.idToken,
|
|
246
241
|
idToken: storedToken.idToken,
|
|
247
|
-
projectApiKey,
|
|
242
|
+
projectApiKey: localKey.key,
|
|
248
243
|
host: getHostFromRegion(zone),
|
|
249
244
|
projectId: 0,
|
|
250
245
|
};
|
|
251
|
-
// Pre-populate activationLevel so DataSetup is also skipped,
|
|
252
|
-
// giving a single wipe from Intro → Run/Setup.
|
|
253
|
-
// DataSetup would set 'none' anyway (projectId=0 prevents the
|
|
254
|
-
// real check), so this is equivalent — just earlier.
|
|
255
246
|
session.activationLevel = 'none';
|
|
256
247
|
session.projectHasData = false;
|
|
257
248
|
}
|
|
258
249
|
else {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
250
|
+
// Fetch user data to check how many environments are available.
|
|
251
|
+
const { fetchAmplitudeUser } = await import('./src/lib/api.js');
|
|
252
|
+
try {
|
|
253
|
+
const userInfo = await fetchAmplitudeUser(storedToken.idToken, zone);
|
|
254
|
+
const workspaceId = session.selectedWorkspaceId ?? undefined;
|
|
255
|
+
// Find the relevant workspace and its environments
|
|
256
|
+
let envsWithKey = [];
|
|
257
|
+
for (const org of userInfo.orgs) {
|
|
258
|
+
const ws = workspaceId
|
|
259
|
+
? org.workspaces.find((w) => w.id === workspaceId)
|
|
260
|
+
: org.workspaces[0];
|
|
261
|
+
if (ws?.environments) {
|
|
262
|
+
envsWithKey = ws.environments
|
|
263
|
+
.filter((env) => env.app?.apiKey)
|
|
264
|
+
.sort((a, b) => a.rank - b.rank);
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (envsWithKey.length === 1) {
|
|
269
|
+
// Single environment — auto-select as before
|
|
270
|
+
const apiKey = envsWithKey[0].app.apiKey;
|
|
271
|
+
session.selectedProjectName = envsWithKey[0].name;
|
|
272
|
+
logToFile('[bin] single environment — auto-selecting API key');
|
|
273
|
+
(0, api_key_store_1.persistApiKey)(apiKey, installDir);
|
|
274
|
+
session.credentials = {
|
|
275
|
+
accessToken: storedToken.idToken,
|
|
276
|
+
idToken: storedToken.idToken,
|
|
277
|
+
projectApiKey: apiKey,
|
|
278
|
+
host: getHostFromRegion(zone),
|
|
279
|
+
projectId: 0,
|
|
280
|
+
};
|
|
281
|
+
session.activationLevel = 'none';
|
|
282
|
+
session.projectHasData = false;
|
|
283
|
+
}
|
|
284
|
+
else if (envsWithKey.length > 1) {
|
|
285
|
+
// Multiple environments — show the project picker via
|
|
286
|
+
// AuthScreen instead of auto-selecting.
|
|
287
|
+
logToFile(`[bin] ${envsWithKey.length} environments found — deferring to project picker`);
|
|
288
|
+
session.pendingOrgs = userInfo.orgs;
|
|
289
|
+
session.pendingAuthIdToken = storedToken.idToken;
|
|
290
|
+
session.pendingAuthAccessToken = storedToken.idToken;
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
logToFile('[bin] no environments with API keys — showing apiKeyNotice');
|
|
294
|
+
session.apiKeyNotice =
|
|
295
|
+
"Your API key couldn't be fetched automatically. " +
|
|
296
|
+
'Only organization admins can access project API keys — ' +
|
|
297
|
+
'if you need one, ask an admin to share it with you.';
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
logToFile(`[bin] fetchAmplitudeUser failed: ${err instanceof Error ? err.message : 'unknown'}`);
|
|
302
|
+
// Fall back to getAPIKey for backward compatibility
|
|
303
|
+
const projectApiKey = await getAPIKey({
|
|
304
|
+
installDir,
|
|
305
|
+
idToken: storedToken.idToken,
|
|
306
|
+
zone,
|
|
307
|
+
workspaceId: session.selectedWorkspaceId ?? undefined,
|
|
308
|
+
});
|
|
309
|
+
if (projectApiKey) {
|
|
310
|
+
(0, api_key_store_1.persistApiKey)(projectApiKey, installDir);
|
|
311
|
+
session.credentials = {
|
|
312
|
+
accessToken: storedToken.idToken,
|
|
313
|
+
idToken: storedToken.idToken,
|
|
314
|
+
projectApiKey,
|
|
315
|
+
host: getHostFromRegion(zone),
|
|
316
|
+
projectId: 0,
|
|
317
|
+
};
|
|
318
|
+
session.activationLevel = 'none';
|
|
319
|
+
session.projectHasData = false;
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
session.apiKeyNotice =
|
|
323
|
+
"Your API key couldn't be fetched automatically. " +
|
|
324
|
+
'Only organization admins can access project API keys — ' +
|
|
325
|
+
'if you need one, ask an admin to share it with you.';
|
|
326
|
+
}
|
|
327
|
+
}
|
|
266
328
|
}
|
|
267
329
|
}
|
|
268
330
|
}
|
|
@@ -275,8 +337,46 @@ void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
|
275
337
|
if (projectConfig.ok && projectConfig.config.WorkspaceId) {
|
|
276
338
|
session.selectedWorkspaceId = projectConfig.config.WorkspaceId;
|
|
277
339
|
}
|
|
340
|
+
// Resolve org/workspace display names so /whoami shows them.
|
|
341
|
+
// Uses the stored token to fetch user info — fire-and-forget so it
|
|
342
|
+
// doesn't block startup.
|
|
343
|
+
if (zone && session.selectedOrgId) {
|
|
344
|
+
const storedToken = realUser
|
|
345
|
+
? getStoredToken(realUser.id, realUser.zone)
|
|
346
|
+
: getStoredToken(undefined, zone);
|
|
347
|
+
if (storedToken) {
|
|
348
|
+
fetchAmplitudeUser(storedToken.idToken, zone)
|
|
349
|
+
.then((userInfo) => {
|
|
350
|
+
const org = userInfo.orgs.find((o) => o.id === session.selectedOrgId);
|
|
351
|
+
if (org) {
|
|
352
|
+
session.selectedOrgName = org.name;
|
|
353
|
+
const ws = session.selectedWorkspaceId
|
|
354
|
+
? org.workspaces.find((w) => w.id === session.selectedWorkspaceId)
|
|
355
|
+
: undefined;
|
|
356
|
+
if (ws) {
|
|
357
|
+
session.selectedWorkspaceName = ws.name;
|
|
358
|
+
}
|
|
359
|
+
// Update the store if it's already been assigned
|
|
360
|
+
if (tui.store.session === session) {
|
|
361
|
+
tui.store.emitChange();
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
})
|
|
365
|
+
.catch(() => {
|
|
366
|
+
// Non-fatal — /whoami will just show (none)
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
278
370
|
}
|
|
279
371
|
tui.store.session = session;
|
|
372
|
+
// Initialize Amplitude Experiment feature flags (non-blocking).
|
|
373
|
+
const { initFeatureFlags } = await import('./src/lib/feature-flags.js');
|
|
374
|
+
await initFeatureFlags().catch(() => {
|
|
375
|
+
// Flag init failure is non-fatal — all flags default to off
|
|
376
|
+
});
|
|
377
|
+
// Apply SDK-level opt-out based on feature flags
|
|
378
|
+
const { analytics } = await import('./src/utils/analytics.js');
|
|
379
|
+
analytics.applyOptOut();
|
|
280
380
|
const { FRAMEWORK_REGISTRY } = await import('./src/lib/registry.js');
|
|
281
381
|
const { detectIntegration } = await import('./src/run.js');
|
|
282
382
|
const installDir = session.installDir ?? process.cwd();
|
|
@@ -431,22 +531,27 @@ void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
|
431
531
|
tui.store.addDiscoveredFeature(DiscoveredFeature.Stripe);
|
|
432
532
|
}
|
|
433
533
|
// LLM SDK detection — sourced from Amplitude LLM analytics skill
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
534
|
+
// Gated by the wizard-llm-analytics feature flag.
|
|
535
|
+
const { isFlagEnabled } = await import('./src/lib/feature-flags.js');
|
|
536
|
+
const { FLAG_LLM_ANALYTICS } = await import('./src/lib/feature-flags.js');
|
|
537
|
+
if (isFlagEnabled(FLAG_LLM_ANALYTICS)) {
|
|
538
|
+
const LLM_PACKAGES = [
|
|
539
|
+
'openai',
|
|
540
|
+
'@anthropic-ai/sdk',
|
|
541
|
+
'ai',
|
|
542
|
+
'@ai-sdk/openai',
|
|
543
|
+
'langchain',
|
|
544
|
+
'@langchain/openai',
|
|
545
|
+
'@langchain/langgraph',
|
|
546
|
+
'@google/generative-ai',
|
|
547
|
+
'@google/genai',
|
|
548
|
+
'@instructor-ai/instructor',
|
|
549
|
+
'@mastra/core',
|
|
550
|
+
'portkey-ai',
|
|
551
|
+
];
|
|
552
|
+
if (depNames.some((d) => LLM_PACKAGES.includes(d))) {
|
|
553
|
+
tui.store.addDiscoveredFeature(DiscoveredFeature.LLM);
|
|
554
|
+
}
|
|
450
555
|
}
|
|
451
556
|
}
|
|
452
557
|
catch {
|
|
@@ -476,11 +581,19 @@ void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
|
476
581
|
const localDetection = detectAmplitudeInProject(installDir);
|
|
477
582
|
if (localDetection.confidence !== 'none') {
|
|
478
583
|
const { logToFile: log } = await import('./src/utils/debug.js');
|
|
479
|
-
log(`[bin] Amplitude already detected (${localDetection.reason ?? 'unknown'}) —
|
|
584
|
+
log(`[bin] Amplitude already detected (${localDetection.reason ?? 'unknown'}) — prompting on MCP screen (continue vs run wizard)`);
|
|
480
585
|
const { RunPhase, OutroKind } = await import('./src/lib/wizard-session.js');
|
|
481
586
|
tui.store.setAmplitudePreDetected();
|
|
482
|
-
tui.store.setOutroData({ kind: OutroKind.Success });
|
|
483
587
|
tui.store.setRunPhase(RunPhase.Completed);
|
|
588
|
+
const runWizardAnyway = await tui.store.waitForPreDetectedChoice();
|
|
589
|
+
if (runWizardAnyway) {
|
|
590
|
+
log('[bin] user chose to run setup wizard despite pre-detection');
|
|
591
|
+
tui.store.resetForAgentAfterPreDetected();
|
|
592
|
+
await (0, run_1.runWizard)(options, tui.store.session);
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
tui.store.setOutroData({ kind: OutroKind.Success });
|
|
596
|
+
}
|
|
484
597
|
}
|
|
485
598
|
else {
|
|
486
599
|
await (0, run_1.runWizard)(options, tui.store.session);
|
|
@@ -589,6 +702,37 @@ void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
|
589
702
|
}
|
|
590
703
|
process.exit(0);
|
|
591
704
|
})();
|
|
705
|
+
})
|
|
706
|
+
.command('feedback', 'Send product feedback (Amplitude event: wizard: Feedback Submitted)', (yargs) => {
|
|
707
|
+
return yargs.options({
|
|
708
|
+
message: {
|
|
709
|
+
alias: 'm',
|
|
710
|
+
describe: 'Feedback message',
|
|
711
|
+
type: 'string',
|
|
712
|
+
},
|
|
713
|
+
});
|
|
714
|
+
}, (argv) => {
|
|
715
|
+
void (async () => {
|
|
716
|
+
(0, ui_1.setUI)(new logging_ui_1.LoggingUI());
|
|
717
|
+
const fromFlag = typeof argv.message === 'string' ? argv.message.trim() : '';
|
|
718
|
+
const argvRest = argv._.slice(1).join(' ').trim();
|
|
719
|
+
const message = (fromFlag || argvRest).trim();
|
|
720
|
+
if (!message) {
|
|
721
|
+
(0, ui_1.getUI)().log.error('Usage: amplitude-wizard feedback <message> or feedback --message <message>');
|
|
722
|
+
process.exit(1);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
try {
|
|
726
|
+
const { trackWizardFeedback } = await import('./src/utils/track-wizard-feedback.js');
|
|
727
|
+
await trackWizardFeedback(message);
|
|
728
|
+
console.log(chalk_1.default.green('✔ Thanks — your feedback was sent.'));
|
|
729
|
+
process.exit(0);
|
|
730
|
+
}
|
|
731
|
+
catch (e) {
|
|
732
|
+
console.error(chalk_1.default.red(`Feedback failed: ${e instanceof Error ? e.message : String(e)}`));
|
|
733
|
+
process.exit(1);
|
|
734
|
+
}
|
|
735
|
+
})();
|
|
592
736
|
})
|
|
593
737
|
.command('slack', 'Set up Amplitude Slack integration', (y) => y, (argv) => {
|
|
594
738
|
void (async () => {
|
|
@@ -153,7 +153,7 @@ function backupAndFixClaudeSettings(workingDirectory) {
|
|
|
153
153
|
for (const name of ['settings.json', 'settings']) {
|
|
154
154
|
const filePath = path_1.default.join(workingDirectory, '.claude', name);
|
|
155
155
|
const backupPath = `${filePath}.wizard-backup`;
|
|
156
|
-
analytics_1.analytics.wizardCapture('
|
|
156
|
+
analytics_1.analytics.wizardCapture('Claude Settings Backed Up');
|
|
157
157
|
try {
|
|
158
158
|
fs.copyFileSync(filePath, backupPath);
|
|
159
159
|
fs.unlinkSync(filePath);
|
|
@@ -182,7 +182,7 @@ function restoreClaudeSettings(workingDirectory) {
|
|
|
182
182
|
const backup = path_1.default.join(workingDirectory, '.claude', `${name}.wizard-backup`);
|
|
183
183
|
try {
|
|
184
184
|
fs.copyFileSync(backup, path_1.default.join(workingDirectory, '.claude', name));
|
|
185
|
-
analytics_1.analytics.wizardCapture('
|
|
185
|
+
analytics_1.analytics.wizardCapture('Claude Settings Restored');
|
|
186
186
|
return;
|
|
187
187
|
}
|
|
188
188
|
catch (error) {
|
|
@@ -490,10 +490,7 @@ function wizardCanUseTool(toolName, input) {
|
|
|
490
490
|
if (DANGEROUS_OPERATORS.test(command)) {
|
|
491
491
|
(0, debug_1.logToFile)(`Denying bash command with dangerous operators: ${command}`);
|
|
492
492
|
(0, debug_1.debug)(`Denying bash command with dangerous operators: ${command}`);
|
|
493
|
-
analytics_1.
|
|
494
|
-
reason: 'dangerous operators',
|
|
495
|
-
command,
|
|
496
|
-
});
|
|
493
|
+
(0, analytics_1.captureWizardError)('Bash Policy', 'Dangerous shell operators are not permitted', 'wizardCanUseBash', { deny_reason: 'dangerous operators', command });
|
|
497
494
|
return {
|
|
498
495
|
behavior: 'deny',
|
|
499
496
|
message: `Bash command not allowed. Shell operators like ; \` $ ( ) are not permitted.`,
|
|
@@ -509,10 +506,7 @@ function wizardCanUseTool(toolName, input) {
|
|
|
509
506
|
if (/[|&]/.test(baseCommand)) {
|
|
510
507
|
(0, debug_1.logToFile)(`Denying bash command with multiple pipes: ${command}`);
|
|
511
508
|
(0, debug_1.debug)(`Denying bash command with multiple pipes: ${command}`);
|
|
512
|
-
analytics_1.
|
|
513
|
-
reason: 'multiple pipes',
|
|
514
|
-
command,
|
|
515
|
-
});
|
|
509
|
+
(0, analytics_1.captureWizardError)('Bash Policy', 'Multiple pipes are not permitted', 'wizardCanUseBash', { deny_reason: 'multiple pipes', command });
|
|
516
510
|
return {
|
|
517
511
|
behavior: 'deny',
|
|
518
512
|
message: `Bash command not allowed. Only single pipe to tail/head is permitted.`,
|
|
@@ -528,10 +522,7 @@ function wizardCanUseTool(toolName, input) {
|
|
|
528
522
|
if (/[|&]/.test(normalized)) {
|
|
529
523
|
(0, debug_1.logToFile)(`Denying bash command with pipe/&: ${command}`);
|
|
530
524
|
(0, debug_1.debug)(`Denying bash command with pipe/&: ${command}`);
|
|
531
|
-
analytics_1.
|
|
532
|
-
reason: 'disallowed pipe',
|
|
533
|
-
command,
|
|
534
|
-
});
|
|
525
|
+
(0, analytics_1.captureWizardError)('Bash Policy', 'Pipes are only allowed with tail/head', 'wizardCanUseBash', { deny_reason: 'disallowed pipe', command });
|
|
535
526
|
return {
|
|
536
527
|
behavior: 'deny',
|
|
537
528
|
message: `Bash command not allowed. Pipes are only permitted with tail/head for output limiting.`,
|
|
@@ -545,10 +536,7 @@ function wizardCanUseTool(toolName, input) {
|
|
|
545
536
|
}
|
|
546
537
|
(0, debug_1.logToFile)(`Denying bash command: ${command}`);
|
|
547
538
|
(0, debug_1.debug)(`Denying bash command: ${command}`);
|
|
548
|
-
analytics_1.
|
|
549
|
-
reason: 'not in allowlist',
|
|
550
|
-
command,
|
|
551
|
-
});
|
|
539
|
+
(0, analytics_1.captureWizardError)('Bash Policy', 'Command not in allowlist', 'wizardCanUseBash', { deny_reason: 'not in allowlist', command });
|
|
552
540
|
return {
|
|
553
541
|
behavior: 'deny',
|
|
554
542
|
message: `Bash command not allowed. Only install, build, typecheck, lint, and formatting commands are permitted.`,
|
|
@@ -783,10 +771,10 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
|
|
|
783
771
|
if (remarkMatch && remarkMatch[1]) {
|
|
784
772
|
const remark = remarkMatch[1].trim();
|
|
785
773
|
if (remark) {
|
|
786
|
-
analytics_1.analytics.
|
|
774
|
+
analytics_1.analytics.wizardCapture('Wizard Remark', { remark });
|
|
787
775
|
}
|
|
788
776
|
}
|
|
789
|
-
analytics_1.analytics.wizardCapture('
|
|
777
|
+
analytics_1.analytics.wizardCapture('Agent Completed', {
|
|
790
778
|
duration_ms: durationMs,
|
|
791
779
|
duration_seconds: durationSeconds,
|
|
792
780
|
});
|
|
@@ -883,7 +871,7 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
|
|
|
883
871
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
884
872
|
if (attempt > 0) {
|
|
885
873
|
(0, debug_1.logToFile)(`Agent stall retry: attempt ${attempt + 1} of ${MAX_RETRIES + 1}`);
|
|
886
|
-
analytics_1.analytics.wizardCapture('
|
|
874
|
+
analytics_1.analytics.wizardCapture('Agent Stall Retry', { attempt });
|
|
887
875
|
// Clear per-attempt output so stale error markers don't affect the fresh run
|
|
888
876
|
collectedText.length = 0;
|
|
889
877
|
recentStatuses.length = 0;
|
|
@@ -910,7 +898,7 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
|
|
|
910
898
|
clearTimeout(staleTimer);
|
|
911
899
|
staleTimer = setTimeout(() => {
|
|
912
900
|
(0, debug_1.logToFile)(`Agent stalled — no message for ${STALL_TIMEOUT_MS / 1000}s (attempt ${attempt + 1})`);
|
|
913
|
-
analytics_1.analytics.wizardCapture('
|
|
901
|
+
analytics_1.analytics.wizardCapture('Agent Stall Detected', {
|
|
914
902
|
attempt: attempt + 1,
|
|
915
903
|
stall_timeout_ms: STALL_TIMEOUT_MS,
|
|
916
904
|
});
|
|
@@ -140,7 +140,7 @@ async function runAgentWizard(config, session) {
|
|
|
140
140
|
const versionBucket = config.detection.getVersionBucket(frameworkVersion);
|
|
141
141
|
analytics_1.analytics.setTag(`${config.metadata.integration}-version`, versionBucket);
|
|
142
142
|
}
|
|
143
|
-
analytics_1.analytics.wizardCapture('
|
|
143
|
+
analytics_1.analytics.wizardCapture('Agent Started', {
|
|
144
144
|
integration: config.metadata.integration,
|
|
145
145
|
});
|
|
146
146
|
// Credentials are pre-set by bin.ts (TUI mode) via the AuthScreen SUSI flow.
|
|
@@ -256,9 +256,7 @@ async function runAgentWizard(config, session) {
|
|
|
256
256
|
}, middleware);
|
|
257
257
|
// Handle error cases detected in agent output
|
|
258
258
|
if (agentResult.error === agent_interface_1.AgentErrorType.AUTH_ERROR) {
|
|
259
|
-
analytics_1.
|
|
260
|
-
integration: config.metadata.integration,
|
|
261
|
-
});
|
|
259
|
+
(0, analytics_1.captureWizardError)('Agent Authentication', 'Session expired or invalid during agent run', 'agent-runner', { integration: config.metadata.integration });
|
|
262
260
|
const authMessage = `Authentication failed\n\nYour Amplitude session has expired. Please run the wizard again to log in.`;
|
|
263
261
|
session.credentials = null;
|
|
264
262
|
session.outroData = {
|
|
@@ -297,10 +295,9 @@ async function runAgentWizard(config, session) {
|
|
|
297
295
|
}
|
|
298
296
|
if (agentResult.error === agent_interface_1.AgentErrorType.RATE_LIMIT ||
|
|
299
297
|
agentResult.error === agent_interface_1.AgentErrorType.API_ERROR) {
|
|
300
|
-
analytics_1.
|
|
298
|
+
(0, analytics_1.captureWizardError)('Agent API', agentResult.message ?? 'Unknown API error', 'agent-runner', {
|
|
301
299
|
integration: config.metadata.integration,
|
|
302
300
|
error_type: agentResult.error,
|
|
303
|
-
error_message: agentResult.message,
|
|
304
301
|
});
|
|
305
302
|
await (0, wizard_abort_1.wizardAbort)({
|
|
306
303
|
message: `API Error\n\n${agentResult.message || 'Unknown error'}\n\nPlease report this error to: wizard@amplitude.com`,
|
|
@@ -401,6 +398,7 @@ STEP 5: Set up environment variables for Amplitude using the wizard-tools MCP se
|
|
|
401
398
|
- Reference these environment variables in the code files you create instead of hardcoding the public token and host.
|
|
402
399
|
|
|
403
400
|
STEP 6: Add event tracking to this project using the instrumentation skills.
|
|
401
|
+
- Call load_skill_menu with category "taxonomy" and install **amplitude-quickstart-taxonomy-agent** using install_skill. Load its SKILL.md and follow it when **naming events**, choosing **properties**, and scoping a **starter-kit taxonomy** (business-outcome events, property limits, funnel/linkage rules). Keep using this skill alongside instrumentation so names stay analysis-ready.
|
|
404
402
|
- Call load_skill_menu with category "instrumentation" to see available instrumentation skills.
|
|
405
403
|
- Install the "add-analytics-instrumentation" skill using install_skill.
|
|
406
404
|
- Load the installed skill's SKILL.md file to understand the workflow.
|
|
@@ -13,7 +13,7 @@ const WIZARD_COMMANDMENTS = [
|
|
|
13
13
|
'Always use the detect_package_manager tool from the wizard-tools MCP server to determine the package manager. Do not guess based on lockfiles or hard-code npm, yarn, pnpm, bun, pip, etc.',
|
|
14
14
|
'When installing packages, start the installation as a background task and then continue with other work. Do not block waiting for installs to finish unless explicitly instructed.',
|
|
15
15
|
'Before writing to any file, you MUST read that exact file immediately beforehand using the Read tool, even if you have already read it earlier in the run. This avoids tool failures and stale edits.',
|
|
16
|
-
'Treat feature flags, custom properties, and event names as part of an analytics contract. Prefer reusing existing names and patterns in the project. When you must introduce new ones, make them clear, descriptive, and consistent with existing conventions, and avoid scattering the same flag or property across many unrelated callsites.',
|
|
16
|
+
'Treat feature flags, custom properties, and event names as part of an analytics contract. Prefer reusing existing names and patterns in the project. When you must introduce new ones, make them clear, descriptive, and consistent with existing conventions, and avoid scattering the same flag or property across many unrelated callsites. For instrumentation runs, load the bundled **amplitude-quickstart-taxonomy-agent** skill (taxonomy category via wizard-tools) and align new event names and properties with its starter-kit rules (business-outcome naming, small property sets, no redundant pageview events, funnel-friendly linkage).',
|
|
17
17
|
'Prefer minimal, targeted edits that achieve the requested behavior while preserving existing structure and style. Avoid large refactors, broad reformatting, or unrelated changes unless explicitly requested.',
|
|
18
18
|
'Do not spawn subagents unless explicitly instructed to do so.',
|
|
19
19
|
'Use the TodoWrite tool to track your progress. Create a todo list at the start describing the high-level areas of work, mark each as in_progress when you begin it, and completed when done.',
|
|
@@ -77,7 +77,7 @@ export declare const OUTBOUND_URLS: {
|
|
|
77
77
|
/** New dashboard — opened from the Checklist screen. */
|
|
78
78
|
newDashboard: (zone: AmplitudeZone, orgId?: string | null) => string;
|
|
79
79
|
/** Slack integration settings — opened from the Slack screen. */
|
|
80
|
-
slackSettings: (zone: AmplitudeZone, orgName?: string | null) => string;
|
|
80
|
+
slackSettings: (zone: AmplitudeZone, orgId?: string | null, orgName?: string | null) => string;
|
|
81
81
|
/** Products page — shown in the Outro for sign-up users. */
|
|
82
82
|
products: (zone: AmplitudeZone) => string;
|
|
83
83
|
/** SDK overview — opened from the Activation Options screen. */
|
|
@@ -105,11 +105,8 @@ export declare const OUTBOUND_URLS: {
|
|
|
105
105
|
/** Bug reports and feedback. */
|
|
106
106
|
githubIssues: string;
|
|
107
107
|
};
|
|
108
|
-
/** @deprecated Use OUTBOUND_URLS.githubIssues */
|
|
109
|
-
export declare const ISSUES_URL: string;
|
|
110
108
|
/** Placeholder embedded in generated code when the user skips key entry. */
|
|
111
109
|
export declare const DUMMY_PROJECT_API_KEY = "_YOUR_AMPLITUDE_API_KEY_";
|
|
112
|
-
export declare const WIZARD_REMARK_EVENT_NAME = "wizard remark";
|
|
113
110
|
/** Feature flag key whose value selects a variant from WIZARD_VARIANTS. */
|
|
114
111
|
export declare const WIZARD_VARIANT_FLAG_KEY = "wizard-variant";
|
|
115
112
|
/** Variant key -> metadata for wizard run (VARIANT flag selects which entry to use). */
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Shared constants for the Amplitude wizard.
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DETECTION_TIMEOUT_MS = exports.AMPLITUDE_FLAG_HEADER_PREFIX = exports.AMPLITUDE_PROPERTY_HEADER_PREFIX = exports.WIZARD_USER_AGENT = exports.WIZARD_VARIANTS = exports.WIZARD_VARIANT_FLAG_KEY = exports.
|
|
6
|
+
exports.DETECTION_TIMEOUT_MS = exports.AMPLITUDE_FLAG_HEADER_PREFIX = exports.AMPLITUDE_PROPERTY_HEADER_PREFIX = exports.WIZARD_USER_AGENT = exports.WIZARD_VARIANTS = exports.WIZARD_VARIANT_FLAG_KEY = exports.DUMMY_PROJECT_API_KEY = exports.OUTBOUND_URLS = exports.DEFAULT_AMPLITUDE_ZONE = exports.AMPLITUDE_ZONE_SETTINGS = exports.OAUTH_PORT = exports.ANALYTICS_TEAM_TAG = exports.ANALYTICS_HOST_URL = exports.ANALYTICS_AMPLITUDE_PUBLIC_PROJECT_WRITE_KEY = exports.DEFAULT_HOST_URL = exports.DEFAULT_URL = exports.DEBUG = exports.IS_DEV = exports.Integration = void 0;
|
|
7
7
|
const package_json_1 = require("../../package.json");
|
|
8
8
|
// ── Integration / CLI ───────────────────────────────────────────────
|
|
9
9
|
/**
|
|
@@ -108,11 +108,15 @@ exports.OUTBOUND_URLS = {
|
|
|
108
108
|
},
|
|
109
109
|
// ── Post-setup ────────────────────────────────────────────────────────────
|
|
110
110
|
/** Slack integration settings — opened from the Slack screen. */
|
|
111
|
-
slackSettings: (zone, orgName) => {
|
|
111
|
+
slackSettings: (zone, orgId, orgName) => {
|
|
112
112
|
const base = exports.OUTBOUND_URLS.overview[zone];
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
if (orgName) {
|
|
114
|
+
return `${base}/analytics/${encodeURIComponent(orgName)}/settings/profile`;
|
|
115
|
+
}
|
|
116
|
+
if (orgId) {
|
|
117
|
+
return `${base}/${orgId}/settings/profile`;
|
|
118
|
+
}
|
|
119
|
+
return `${base}/settings/profile`;
|
|
116
120
|
},
|
|
117
121
|
/** Products page — shown in the Outro for sign-up users. */
|
|
118
122
|
products: (zone) => `${exports.OUTBOUND_URLS.overview[zone]}/products?source=wizard`,
|
|
@@ -145,12 +149,9 @@ exports.OUTBOUND_URLS = {
|
|
|
145
149
|
/** Bug reports and feedback. */
|
|
146
150
|
githubIssues: 'https://github.com/amplitude/wizard/issues',
|
|
147
151
|
};
|
|
148
|
-
/** @deprecated Use OUTBOUND_URLS.githubIssues */
|
|
149
|
-
exports.ISSUES_URL = exports.OUTBOUND_URLS.githubIssues;
|
|
150
152
|
/** Placeholder embedded in generated code when the user skips key entry. */
|
|
151
153
|
exports.DUMMY_PROJECT_API_KEY = '_YOUR_AMPLITUDE_API_KEY_';
|
|
152
154
|
// ── Wizard run / variants ───────────────────────────────────────────
|
|
153
|
-
exports.WIZARD_REMARK_EVENT_NAME = 'wizard remark';
|
|
154
155
|
/** Feature flag key whose value selects a variant from WIZARD_VARIANTS. */
|
|
155
156
|
exports.WIZARD_VARIANT_FLAG_KEY = 'wizard-variant';
|
|
156
157
|
/** Variant key -> metadata for wizard run (VARIANT flag selects which entry to use). */
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature flags powered by Amplitude Experiment (server-side local evaluation).
|
|
3
|
+
*
|
|
4
|
+
* Flags are fetched once at startup via `initFeatureFlags()` and evaluated
|
|
5
|
+
* synchronously thereafter — no per-check network calls.
|
|
6
|
+
*/
|
|
7
|
+
/** Gate for the LLM analytics additional-feature flow. */
|
|
8
|
+
export declare const FLAG_LLM_ANALYTICS = "wizard-llm-analytics";
|
|
9
|
+
/** Gate for agent-level analytics / telemetry instrumented by the wizard. */
|
|
10
|
+
export declare const FLAG_AGENT_ANALYTICS = "wizard-agent-analytics";
|
|
11
|
+
/**
|
|
12
|
+
* Initialize the Experiment local-evaluation client and pre-fetch flag configs.
|
|
13
|
+
* Safe to call multiple times — subsequent calls are no-ops.
|
|
14
|
+
*
|
|
15
|
+
* @param userId Optional user ID for targeted flag evaluation.
|
|
16
|
+
* @param deviceId Optional device ID for targeted flag evaluation.
|
|
17
|
+
*/
|
|
18
|
+
export declare function initFeatureFlags(userId?: string, deviceId?: string): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Evaluate a single feature flag. Returns the string variant value,
|
|
21
|
+
* or `undefined` if the flag is not set / client not initialized.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getFlag(flagKey: string): string | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Check whether a flag is enabled (variant is `'on'` or `'true'`).
|
|
26
|
+
* Returns `false` when the flag is absent or the client is not initialized.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isFlagEnabled(flagKey: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Return a snapshot of all evaluated flags (key -> string value).
|
|
31
|
+
*/
|
|
32
|
+
export declare function getAllFlags(): Record<string, string>;
|
|
33
|
+
/**
|
|
34
|
+
* Re-evaluate flags for a specific user (e.g. after login).
|
|
35
|
+
* Updates the cached flags in place.
|
|
36
|
+
*/
|
|
37
|
+
export declare function refreshFlags(userId?: string, deviceId?: string): Promise<void>;
|