@amplitude/wizard 1.0.0-beta.0 → 1.0.0-beta.1
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 +42 -8
- package/dist/package.json +2 -1
- 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 +0 -3
- package/dist/src/lib/constants.js +1 -4
- package/dist/src/lib/wizard-session.d.ts +6 -0
- package/dist/src/lib/wizard-session.js +1 -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/components/ConsoleView.js +16 -2
- package/dist/src/ui/tui/console-commands.d.ts +5 -0
- package/dist/src/ui/tui/console-commands.js +15 -0
- package/dist/src/ui/tui/screens/AuthScreen.js +72 -14
- package/dist/src/ui/tui/screens/ChecklistScreen.js +1 -1
- 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 -1
- package/dist/src/ui/tui/store.d.ts +17 -0
- package/dist/src/ui/tui/store.js +55 -19
- package/dist/src/utils/analytics.d.ts +27 -3
- package/dist/src/utils/analytics.js +83 -44
- 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 +2 -1
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 {
|
|
@@ -476,11 +472,19 @@ void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
|
476
472
|
const localDetection = detectAmplitudeInProject(installDir);
|
|
477
473
|
if (localDetection.confidence !== 'none') {
|
|
478
474
|
const { logToFile: log } = await import('./src/utils/debug.js');
|
|
479
|
-
log(`[bin] Amplitude already detected (${localDetection.reason ?? 'unknown'}) —
|
|
475
|
+
log(`[bin] Amplitude already detected (${localDetection.reason ?? 'unknown'}) — prompting on MCP screen (continue vs run wizard)`);
|
|
480
476
|
const { RunPhase, OutroKind } = await import('./src/lib/wizard-session.js');
|
|
481
477
|
tui.store.setAmplitudePreDetected();
|
|
482
|
-
tui.store.setOutroData({ kind: OutroKind.Success });
|
|
483
478
|
tui.store.setRunPhase(RunPhase.Completed);
|
|
479
|
+
const runWizardAnyway = await tui.store.waitForPreDetectedChoice();
|
|
480
|
+
if (runWizardAnyway) {
|
|
481
|
+
log('[bin] user chose to run setup wizard despite pre-detection');
|
|
482
|
+
tui.store.resetForAgentAfterPreDetected();
|
|
483
|
+
await (0, run_1.runWizard)(options, tui.store.session);
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
tui.store.setOutroData({ kind: OutroKind.Success });
|
|
487
|
+
}
|
|
484
488
|
}
|
|
485
489
|
else {
|
|
486
490
|
await (0, run_1.runWizard)(options, tui.store.session);
|
|
@@ -589,6 +593,36 @@ void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
|
589
593
|
}
|
|
590
594
|
process.exit(0);
|
|
591
595
|
})();
|
|
596
|
+
})
|
|
597
|
+
.command('feedback', 'Send product feedback (Amplitude event: wizard: Feedback Submitted)', (yargs) => {
|
|
598
|
+
return yargs.options({
|
|
599
|
+
message: {
|
|
600
|
+
alias: 'm',
|
|
601
|
+
describe: 'Feedback message',
|
|
602
|
+
type: 'string',
|
|
603
|
+
},
|
|
604
|
+
});
|
|
605
|
+
}, (argv) => {
|
|
606
|
+
void (async () => {
|
|
607
|
+
(0, ui_1.setUI)(new logging_ui_1.LoggingUI());
|
|
608
|
+
const fromFlag = typeof argv.message === 'string' ? argv.message.trim() : '';
|
|
609
|
+
const argvRest = argv._.slice(1).join(' ').trim();
|
|
610
|
+
const message = (fromFlag || argvRest).trim();
|
|
611
|
+
if (!message) {
|
|
612
|
+
(0, ui_1.getUI)().log.error('Usage: amplitude-wizard feedback <message> or feedback --message <message>');
|
|
613
|
+
process.exit(1);
|
|
614
|
+
}
|
|
615
|
+
try {
|
|
616
|
+
const { trackWizardFeedback } = await import('./src/utils/track-wizard-feedback.js');
|
|
617
|
+
await trackWizardFeedback(message);
|
|
618
|
+
console.log(chalk_1.default.green('✔ Thanks — your feedback was sent.'));
|
|
619
|
+
process.exit(0);
|
|
620
|
+
}
|
|
621
|
+
catch (e) {
|
|
622
|
+
console.error(chalk_1.default.red(`Feedback failed: ${e instanceof Error ? e.message : String(e)}`));
|
|
623
|
+
process.exit(1);
|
|
624
|
+
}
|
|
625
|
+
})();
|
|
592
626
|
})
|
|
593
627
|
.command('slack', 'Set up Amplitude Slack integration', (y) => y, (argv) => {
|
|
594
628
|
void (async () => {
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amplitude/wizard",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
4
|
"homepage": "https://github.com/amplitude/wizard",
|
|
5
5
|
"repository": "https://github.com/amplitude/wizard",
|
|
6
6
|
"description": "The Amplitude wizard helps you to configure your project",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@amplitude/analytics-browser": "^2.0.0",
|
|
40
|
+
"@amplitude/analytics-node": "^1.5.51",
|
|
40
41
|
"@anthropic-ai/claude-agent-sdk": "0.2.7",
|
|
41
42
|
"@inkjs/ui": "^2.0.0",
|
|
42
43
|
"@inquirer/prompts": "^8.3.0",
|
|
@@ -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.',
|
|
@@ -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
|
/**
|
|
@@ -145,12 +145,9 @@ exports.OUTBOUND_URLS = {
|
|
|
145
145
|
/** Bug reports and feedback. */
|
|
146
146
|
githubIssues: 'https://github.com/amplitude/wizard/issues',
|
|
147
147
|
};
|
|
148
|
-
/** @deprecated Use OUTBOUND_URLS.githubIssues */
|
|
149
|
-
exports.ISSUES_URL = exports.OUTBOUND_URLS.githubIssues;
|
|
150
148
|
/** Placeholder embedded in generated code when the user skips key entry. */
|
|
151
149
|
exports.DUMMY_PROJECT_API_KEY = '_YOUR_AMPLITUDE_API_KEY_';
|
|
152
150
|
// ── Wizard run / variants ───────────────────────────────────────────
|
|
153
|
-
exports.WIZARD_REMARK_EVENT_NAME = 'wizard remark';
|
|
154
151
|
/** Feature flag key whose value selects a variant from WIZARD_VARIANTS. */
|
|
155
152
|
exports.WIZARD_VARIANT_FLAG_KEY = 'wizard-variant';
|
|
156
153
|
/** Variant key -> metadata for wizard run (VARIANT flag selects which entry to use). */
|
|
@@ -185,6 +185,12 @@ export interface WizardSession {
|
|
|
185
185
|
* runPhase is set to Completed.
|
|
186
186
|
*/
|
|
187
187
|
amplitudePreDetected: boolean;
|
|
188
|
+
/**
|
|
189
|
+
* While true, McpScreen shows a prompt: skip the agent (default) or run
|
|
190
|
+
* the setup wizard anyway. Cleared when the user chooses or when resetting
|
|
191
|
+
* for a forced wizard run.
|
|
192
|
+
*/
|
|
193
|
+
amplitudePreDetectedChoicePending: boolean;
|
|
188
194
|
/**
|
|
189
195
|
* True once the activation check confirms events are flowing into the project.
|
|
190
196
|
* Set immediately if activationLevel is already 'full', otherwise set after
|
|
@@ -119,6 +119,7 @@ function buildSession(args) {
|
|
|
119
119
|
additionalFeatureQueue: [],
|
|
120
120
|
frameworkConfig: null,
|
|
121
121
|
amplitudePreDetected: false,
|
|
122
|
+
amplitudePreDetectedChoicePending: false,
|
|
122
123
|
dataIngestionConfirmed: false,
|
|
123
124
|
checklistChartComplete: false,
|
|
124
125
|
checklistDashboardComplete: false,
|
package/dist/src/run.js
CHANGED
|
@@ -59,7 +59,7 @@ async function runWizard(argv, session) {
|
|
|
59
59
|
const integration = session.integration ?? (await detectAndResolveIntegration(session));
|
|
60
60
|
session.integration = integration;
|
|
61
61
|
analytics_1.analytics.setTag('integration', integration);
|
|
62
|
-
analytics_1.analytics.wizardCapture('
|
|
62
|
+
analytics_1.analytics.wizardCapture('Session Started', {
|
|
63
63
|
integration,
|
|
64
64
|
ci: session.ci ?? false,
|
|
65
65
|
});
|
|
@@ -58,7 +58,7 @@ const addMCPServerToClientsStep = async ({ integration, local = false, ci = fals
|
|
|
58
58
|
});
|
|
59
59
|
ui.log.success(`Added the MCP server to:
|
|
60
60
|
${supportedClients.map((c) => `- ${c.name}`).join('\n ')} `);
|
|
61
|
-
analytics_1.analytics.wizardCapture('
|
|
61
|
+
analytics_1.analytics.wizardCapture('MCP Servers Added', {
|
|
62
62
|
clients: supportedClients.map((c) => c.name),
|
|
63
63
|
integration,
|
|
64
64
|
});
|
|
@@ -68,7 +68,7 @@ exports.addMCPServerToClientsStep = addMCPServerToClientsStep;
|
|
|
68
68
|
const removeMCPServerFromClientsStep = async ({ integration, local = false, }) => {
|
|
69
69
|
const installedClients = await (0, exports.getInstalledClients)(local);
|
|
70
70
|
if (installedClients.length === 0) {
|
|
71
|
-
analytics_1.analytics.wizardCapture('
|
|
71
|
+
analytics_1.analytics.wizardCapture('MCP No Servers To Remove', {
|
|
72
72
|
integration,
|
|
73
73
|
});
|
|
74
74
|
return [];
|
|
@@ -78,7 +78,7 @@ const removeMCPServerFromClientsStep = async ({ integration, local = false, }) =
|
|
|
78
78
|
await (0, exports.removeMCPServer)(installedClients, local);
|
|
79
79
|
return installedClients.map((c) => c.name);
|
|
80
80
|
});
|
|
81
|
-
analytics_1.analytics.wizardCapture('
|
|
81
|
+
analytics_1.analytics.wizardCapture('MCP Servers Removed', {
|
|
82
82
|
clients: results,
|
|
83
83
|
integration,
|
|
84
84
|
});
|
|
@@ -90,10 +90,7 @@ async function addOrUpdateEnvironmentVariablesStep({ installDir, variables, inte
|
|
|
90
90
|
}
|
|
91
91
|
catch (error) {
|
|
92
92
|
(0, ui_1.getUI)().log.warn(`Failed to update environment variables in ${chalk_1.default.bold.cyan(relativeEnvFilePath)}. Please update them manually.`);
|
|
93
|
-
analytics_1.
|
|
94
|
-
integration,
|
|
95
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
96
|
-
});
|
|
93
|
+
(0, analytics_1.captureWizardError)('Environment Variables', error instanceof Error ? error.message : 'Unknown error', 'add-or-update-env:update-existing', { integration });
|
|
97
94
|
return {
|
|
98
95
|
relativeEnvFilePath,
|
|
99
96
|
addedEnvVariables,
|
|
@@ -112,10 +109,7 @@ async function addOrUpdateEnvironmentVariablesStep({ installDir, variables, inte
|
|
|
112
109
|
}
|
|
113
110
|
catch (error) {
|
|
114
111
|
(0, ui_1.getUI)().log.warn(`Failed to create ${chalk_1.default.bold.cyan(relativeEnvFilePath)} with environment variables. Please add them manually.`);
|
|
115
|
-
analytics_1.
|
|
116
|
-
integration,
|
|
117
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
118
|
-
});
|
|
112
|
+
(0, analytics_1.captureWizardError)('Environment Variables', error instanceof Error ? error.message : 'Unknown error', 'add-or-update-env:create-new', { integration });
|
|
119
113
|
return {
|
|
120
114
|
relativeEnvFilePath,
|
|
121
115
|
addedEnvVariables,
|
|
@@ -141,10 +135,7 @@ async function addOrUpdateEnvironmentVariablesStep({ installDir, variables, inte
|
|
|
141
135
|
}
|
|
142
136
|
catch (error) {
|
|
143
137
|
(0, ui_1.getUI)().log.warn(`Failed to update ${chalk_1.default.bold.cyan('.gitignore')} to include ${chalk_1.default.bold.cyan(envFileName)}.`);
|
|
144
|
-
analytics_1.
|
|
145
|
-
integration,
|
|
146
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
147
|
-
});
|
|
138
|
+
(0, analytics_1.captureWizardError)('Environment Variables', error instanceof Error ? error.message : 'Unknown error', 'add-or-update-env:gitignore-update', { integration });
|
|
148
139
|
return {
|
|
149
140
|
relativeEnvFilePath,
|
|
150
141
|
addedEnvVariables,
|
|
@@ -165,10 +156,7 @@ async function addOrUpdateEnvironmentVariablesStep({ installDir, variables, inte
|
|
|
165
156
|
}
|
|
166
157
|
catch (error) {
|
|
167
158
|
(0, ui_1.getUI)().log.warn(`Failed to create ${chalk_1.default.bold.cyan('.gitignore')} with environment files.`);
|
|
168
|
-
analytics_1.
|
|
169
|
-
integration,
|
|
170
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
171
|
-
});
|
|
159
|
+
(0, analytics_1.captureWizardError)('Environment Variables', error instanceof Error ? error.message : 'Unknown error', 'add-or-update-env:gitignore-create', { integration });
|
|
172
160
|
return {
|
|
173
161
|
relativeEnvFilePath,
|
|
174
162
|
addedEnvVariables,
|
|
@@ -176,7 +164,7 @@ async function addOrUpdateEnvironmentVariablesStep({ installDir, variables, inte
|
|
|
176
164
|
};
|
|
177
165
|
}
|
|
178
166
|
}
|
|
179
|
-
analytics_1.analytics.wizardCapture('
|
|
167
|
+
analytics_1.analytics.wizardCapture('Environment Variables Added', {
|
|
180
168
|
integration,
|
|
181
169
|
});
|
|
182
170
|
return {
|
|
@@ -83,7 +83,7 @@ async function runPrettierStep({ installDir, integration, }) {
|
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
prettierSpinner.stop('Prettier has formatted your files.');
|
|
86
|
-
analytics_1.analytics.wizardCapture('
|
|
86
|
+
analytics_1.analytics.wizardCapture('Prettier Ran', {
|
|
87
87
|
integration,
|
|
88
88
|
});
|
|
89
89
|
});
|
|
@@ -17,7 +17,7 @@ const uploadEnvironmentVariablesStep = async (envVars, { integration, session, }
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
if (!provider) {
|
|
20
|
-
analytics_1.analytics.wizardCapture('
|
|
20
|
+
analytics_1.analytics.wizardCapture('Env Upload Skipped', {
|
|
21
21
|
reason: 'no environment provider found',
|
|
22
22
|
integration,
|
|
23
23
|
});
|
|
@@ -28,7 +28,7 @@ const uploadEnvironmentVariablesStep = async (envVars, { integration, session, }
|
|
|
28
28
|
const results = await (0, telemetry_1.traceStep)('uploading environment variables', async () => {
|
|
29
29
|
return await provider.uploadEnvVars(envVars);
|
|
30
30
|
});
|
|
31
|
-
analytics_1.analytics.wizardCapture('
|
|
31
|
+
analytics_1.analytics.wizardCapture('Env Uploaded', {
|
|
32
32
|
provider: provider.name,
|
|
33
33
|
integration,
|
|
34
34
|
});
|
|
@@ -21,8 +21,9 @@ import { PickerMenu } from '../primitives/PickerMenu.js';
|
|
|
21
21
|
import { Colors, Icons } from '../styles.js';
|
|
22
22
|
import { Overlay } from '../router.js';
|
|
23
23
|
import { queryConsole, resolveConsoleCredentials, buildSessionContext, } from '../../../lib/console-query.js';
|
|
24
|
-
import { COMMANDS, getWhoamiText, getHelpText, TEST_PROMPT, } from '../console-commands.js';
|
|
24
|
+
import { COMMANDS, getWhoamiText, getHelpText, parseFeedbackSlashInput, TEST_PROMPT, } from '../console-commands.js';
|
|
25
25
|
import { analytics } from '../../../utils/analytics.js';
|
|
26
|
+
import { trackWizardFeedback } from '../../../utils/track-wizard-feedback.js';
|
|
26
27
|
function executeCommand(raw, store) {
|
|
27
28
|
const [cmd] = raw.trim().split(/\s+/);
|
|
28
29
|
switch (cmd) {
|
|
@@ -41,6 +42,19 @@ function executeCommand(raw, store) {
|
|
|
41
42
|
case '/slack':
|
|
42
43
|
store.showSlackOverlay();
|
|
43
44
|
break;
|
|
45
|
+
case '/feedback': {
|
|
46
|
+
const message = parseFeedbackSlashInput(raw);
|
|
47
|
+
if (!message) {
|
|
48
|
+
store.setCommandFeedback('Usage: /feedback <your message>');
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
void trackWizardFeedback(message)
|
|
52
|
+
.then(() => store.setCommandFeedback('Thanks — your feedback was sent.'))
|
|
53
|
+
.catch((err) => {
|
|
54
|
+
store.setCommandFeedback(`Could not send feedback: ${err instanceof Error ? err.message : String(err)}`);
|
|
55
|
+
});
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
44
58
|
case '/test':
|
|
45
59
|
return TEST_PROMPT;
|
|
46
60
|
case '/mcp':
|
|
@@ -116,7 +130,7 @@ export const ConsoleView = ({ store, width, height, children, }) => {
|
|
|
116
130
|
}, { isActive: !inputActive });
|
|
117
131
|
const handleSubmit = (value) => {
|
|
118
132
|
const isSlashCommand = value.startsWith('/');
|
|
119
|
-
analytics.wizardCapture('
|
|
133
|
+
analytics.wizardCapture('Agent Message Sent', {
|
|
120
134
|
message_length: value.length,
|
|
121
135
|
is_slash_command: isSlashCommand,
|
|
122
136
|
});
|
|
@@ -14,3 +14,8 @@ export declare const TEST_PROMPT: string;
|
|
|
14
14
|
export declare function getWhoamiText(session: Pick<WizardSession, 'selectedOrgName' | 'selectedWorkspaceName' | 'region'>): string;
|
|
15
15
|
/** Returns the feedback text for the /help command. */
|
|
16
16
|
export declare function getHelpText(): string;
|
|
17
|
+
/**
|
|
18
|
+
* Parses `/feedback <message>` from a slash command line.
|
|
19
|
+
* Returns `undefined` if the line is not a feedback command or the message is empty.
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseFeedbackSlashInput(raw: string): string | undefined;
|
|
@@ -11,6 +11,10 @@ export const COMMANDS = [
|
|
|
11
11
|
{ cmd: '/whoami', desc: 'Show current user, org, and project' },
|
|
12
12
|
{ cmd: '/mcp', desc: 'Install or remove the Amplitude MCP server' },
|
|
13
13
|
{ cmd: '/slack', desc: 'Set up Amplitude Slack integration' },
|
|
14
|
+
{
|
|
15
|
+
cmd: '/feedback',
|
|
16
|
+
desc: 'Send product feedback (event: wizard: feedback submitted)',
|
|
17
|
+
},
|
|
14
18
|
{ cmd: '/test', desc: 'Run a prompt-skill demo (confirm + choose)' },
|
|
15
19
|
{ cmd: '/snake', desc: 'Play Snake' },
|
|
16
20
|
{ cmd: '/exit', desc: 'Exit the wizard' },
|
|
@@ -29,3 +33,14 @@ export function getHelpText() {
|
|
|
29
33
|
const maxCmd = Math.max(...COMMANDS.map((c) => c.cmd.length));
|
|
30
34
|
return COMMANDS.map((c) => `${c.cmd.padEnd(maxCmd)} ${c.desc}`).join('\n');
|
|
31
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Parses `/feedback <message>` from a slash command line.
|
|
38
|
+
* Returns `undefined` if the line is not a feedback command or the message is empty.
|
|
39
|
+
*/
|
|
40
|
+
export function parseFeedbackSlashInput(raw) {
|
|
41
|
+
const m = /^\s*\/feedback(?:\s+(.*))?\s*$/i.exec(raw);
|
|
42
|
+
if (!m)
|
|
43
|
+
return undefined;
|
|
44
|
+
const body = m[1]?.trim();
|
|
45
|
+
return body || undefined;
|
|
46
|
+
}
|
|
@@ -16,7 +16,7 @@ import { useState, useEffect, useSyncExternalStore } from 'react';
|
|
|
16
16
|
import { TextInput } from '@inkjs/ui';
|
|
17
17
|
import { LoadingBox, PickerMenu } from '../primitives/index.js';
|
|
18
18
|
import { Colors } from '../styles.js';
|
|
19
|
-
import { DEFAULT_HOST_URL } from '../../../lib/constants.js';
|
|
19
|
+
import { DEFAULT_HOST_URL, } from '../../../lib/constants.js';
|
|
20
20
|
import { analytics } from '../../../utils/analytics.js';
|
|
21
21
|
export const AuthScreen = ({ store }) => {
|
|
22
22
|
useSyncExternalStore((cb) => store.subscribe(cb), () => store.getSnapshot());
|
|
@@ -37,28 +37,85 @@ export const AuthScreen = ({ store }) => {
|
|
|
37
37
|
}, [effectiveOrg?.id, singleWorkspace?.id, session.selectedWorkspaceId]);
|
|
38
38
|
const workspaceChosen = session.selectedWorkspaceId !== null ||
|
|
39
39
|
(effectiveOrg !== null && effectiveOrg.workspaces.length === 1);
|
|
40
|
-
//
|
|
40
|
+
// Resolve API key: local storage first, then Amplitude backend (same as bin.ts
|
|
41
|
+
// for returning users) once org/workspace are on the session after OAuth.
|
|
41
42
|
useEffect(() => {
|
|
42
43
|
if (!workspaceChosen || session.credentials !== null)
|
|
43
44
|
return;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
let cancelled = false;
|
|
46
|
+
void (async () => {
|
|
47
|
+
const s = store.session;
|
|
48
|
+
if (s.credentials !== null)
|
|
49
|
+
return;
|
|
50
|
+
const { readApiKeyWithSource, persistApiKey } = await import('../../../utils/api-key-store.js');
|
|
51
|
+
if (cancelled)
|
|
52
|
+
return;
|
|
53
|
+
const local = readApiKeyWithSource(s.installDir);
|
|
54
|
+
if (local) {
|
|
55
|
+
setSavedKeySource(local.source);
|
|
56
|
+
analytics.wizardCapture('API Key Submitted', {
|
|
57
|
+
key_source: local.source,
|
|
50
58
|
});
|
|
51
59
|
store.setCredentials({
|
|
52
|
-
accessToken:
|
|
53
|
-
idToken:
|
|
54
|
-
projectApiKey:
|
|
60
|
+
accessToken: s.pendingAuthAccessToken ?? '',
|
|
61
|
+
idToken: s.pendingAuthIdToken ?? undefined,
|
|
62
|
+
projectApiKey: local.key,
|
|
55
63
|
host: DEFAULT_HOST_URL,
|
|
56
64
|
projectId: 0,
|
|
57
65
|
});
|
|
58
66
|
store.setProjectHasData(false);
|
|
67
|
+
store.setApiKeyNotice(null);
|
|
68
|
+
return;
|
|
59
69
|
}
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
const idToken = s.pendingAuthIdToken;
|
|
71
|
+
if (!idToken)
|
|
72
|
+
return;
|
|
73
|
+
const zone = (s.region ??
|
|
74
|
+
s.pendingAuthCloudRegion ??
|
|
75
|
+
'us');
|
|
76
|
+
const { getAPIKey } = await import('../../../utils/get-api-key.js');
|
|
77
|
+
const { getHostFromRegion } = await import('../../../utils/urls.js');
|
|
78
|
+
const projectApiKey = await getAPIKey({
|
|
79
|
+
installDir: s.installDir,
|
|
80
|
+
idToken,
|
|
81
|
+
zone,
|
|
82
|
+
workspaceId: s.selectedWorkspaceId ?? undefined,
|
|
83
|
+
});
|
|
84
|
+
if (cancelled || store.session.credentials !== null)
|
|
85
|
+
return;
|
|
86
|
+
if (projectApiKey) {
|
|
87
|
+
persistApiKey(projectApiKey, s.installDir);
|
|
88
|
+
analytics.wizardCapture('API Key Submitted', {
|
|
89
|
+
key_source: 'backend_fetch',
|
|
90
|
+
});
|
|
91
|
+
store.setCredentials({
|
|
92
|
+
accessToken: s.pendingAuthAccessToken ?? '',
|
|
93
|
+
idToken: s.pendingAuthIdToken ?? undefined,
|
|
94
|
+
projectApiKey,
|
|
95
|
+
host: getHostFromRegion(zone),
|
|
96
|
+
projectId: 0,
|
|
97
|
+
});
|
|
98
|
+
store.setProjectHasData(false);
|
|
99
|
+
store.setApiKeyNotice(null);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
store.setApiKeyNotice("Your API key couldn't be fetched automatically. " +
|
|
103
|
+
'Only organization admins can access project API keys — ' +
|
|
104
|
+
'if you need one, ask an admin to share it with you.');
|
|
105
|
+
}
|
|
106
|
+
})();
|
|
107
|
+
return () => {
|
|
108
|
+
cancelled = true;
|
|
109
|
+
};
|
|
110
|
+
}, [
|
|
111
|
+
workspaceChosen,
|
|
112
|
+
session.credentials,
|
|
113
|
+
session.selectedWorkspaceId,
|
|
114
|
+
session.pendingAuthIdToken,
|
|
115
|
+
session.region,
|
|
116
|
+
session.pendingAuthCloudRegion,
|
|
117
|
+
session.installDir,
|
|
118
|
+
]);
|
|
62
119
|
const needsOrgPick = pendingOrgs !== null && pendingOrgs.length > 1 && effectiveOrg === null;
|
|
63
120
|
const needsWorkspacePick = effectiveOrg !== null &&
|
|
64
121
|
effectiveOrg.workspaces.length > 1 &&
|
|
@@ -71,9 +128,10 @@ export const AuthScreen = ({ store }) => {
|
|
|
71
128
|
return;
|
|
72
129
|
}
|
|
73
130
|
setApiKeyError('');
|
|
74
|
-
analytics.wizardCapture('
|
|
131
|
+
analytics.wizardCapture('API Key Submitted', {
|
|
75
132
|
key_source: 'manual_entry',
|
|
76
133
|
});
|
|
134
|
+
store.setApiKeyNotice(null);
|
|
77
135
|
store.setCredentials({
|
|
78
136
|
accessToken: session.pendingAuthAccessToken ?? '',
|
|
79
137
|
idToken: session.pendingAuthIdToken ?? undefined,
|
|
@@ -56,7 +56,7 @@ export const ChecklistScreen = ({ store }) => {
|
|
|
56
56
|
const dashboardUrl = OUTBOUND_URLS.newDashboard(zone, selectedOrgId);
|
|
57
57
|
function openInBrowser(url, item) {
|
|
58
58
|
setOpening(item);
|
|
59
|
-
analytics.wizardCapture('
|
|
59
|
+
analytics.wizardCapture('Checklist Step Opened', { item });
|
|
60
60
|
opn(url, { wait: false })
|
|
61
61
|
.catch(() => {
|
|
62
62
|
/* fire-and-forget */
|