@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.
Files changed (43) hide show
  1. package/dist/bin.js +191 -47
  2. package/dist/src/lib/agent-interface.js +10 -22
  3. package/dist/src/lib/agent-runner.js +4 -6
  4. package/dist/src/lib/commandments.js +1 -1
  5. package/dist/src/lib/constants.d.ts +1 -4
  6. package/dist/src/lib/constants.js +9 -8
  7. package/dist/src/lib/feature-flags.d.ts +37 -0
  8. package/dist/src/lib/feature-flags.js +119 -0
  9. package/dist/src/lib/wizard-session.d.ts +16 -0
  10. package/dist/src/lib/wizard-session.js +2 -0
  11. package/dist/src/run.js +1 -1
  12. package/dist/src/steps/add-mcp-server-to-clients/index.js +3 -3
  13. package/dist/src/steps/add-or-update-environment-variables.js +5 -17
  14. package/dist/src/steps/run-prettier.js +1 -1
  15. package/dist/src/steps/upload-environment-variables/index.js +2 -2
  16. package/dist/src/ui/tui/App.js +1 -1
  17. package/dist/src/ui/tui/components/ConsoleView.js +17 -6
  18. package/dist/src/ui/tui/components/TitleBar.d.ts +3 -1
  19. package/dist/src/ui/tui/components/TitleBar.js +17 -6
  20. package/dist/src/ui/tui/console-commands.d.ts +5 -2
  21. package/dist/src/ui/tui/console-commands.js +14 -5
  22. package/dist/src/ui/tui/screens/AuthScreen.d.ts +2 -1
  23. package/dist/src/ui/tui/screens/AuthScreen.js +166 -26
  24. package/dist/src/ui/tui/screens/ChecklistScreen.js +1 -1
  25. package/dist/src/ui/tui/screens/DataIngestionCheckScreen.js +13 -2
  26. package/dist/src/ui/tui/screens/IntroScreen.js +2 -2
  27. package/dist/src/ui/tui/screens/McpScreen.js +42 -27
  28. package/dist/src/ui/tui/screens/OutroScreen.js +1 -2
  29. package/dist/src/ui/tui/screens/SlackScreen.d.ts +0 -5
  30. package/dist/src/ui/tui/screens/SlackScreen.js +1 -11
  31. package/dist/src/ui/tui/store.d.ts +20 -0
  32. package/dist/src/ui/tui/store.js +68 -19
  33. package/dist/src/utils/analytics.d.ts +45 -3
  34. package/dist/src/utils/analytics.js +118 -47
  35. package/dist/src/utils/oauth.js +1 -1
  36. package/dist/src/utils/setup-utils.d.ts +11 -0
  37. package/dist/src/utils/setup-utils.js +81 -4
  38. package/dist/src/utils/shell-completions.d.ts +2 -2
  39. package/dist/src/utils/shell-completions.js +8 -1
  40. package/dist/src/utils/track-wizard-feedback.d.ts +5 -0
  41. package/dist/src/utils/track-wizard-feedback.js +25 -0
  42. package/package.json +13 -13
  43. package/dist/package.json +0 -144
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ /**
3
+ * Feature flags powered by Amplitude Experiment (server-side local evaluation).
4
+ *
5
+ * Flags are fetched once at startup via `initFeatureFlags()` and evaluated
6
+ * synchronously thereafter — no per-check network calls.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.FLAG_AGENT_ANALYTICS = exports.FLAG_LLM_ANALYTICS = void 0;
10
+ exports.initFeatureFlags = initFeatureFlags;
11
+ exports.getFlag = getFlag;
12
+ exports.isFlagEnabled = isFlagEnabled;
13
+ exports.getAllFlags = getAllFlags;
14
+ exports.refreshFlags = refreshFlags;
15
+ const experiment_node_server_1 = require("@amplitude/experiment-node-server");
16
+ const debug_1 = require("../utils/debug");
17
+ // ── Flag keys ────────────────────────────────────────────────────────
18
+ /** Gate for the LLM analytics additional-feature flow. */
19
+ exports.FLAG_LLM_ANALYTICS = 'wizard-llm-analytics';
20
+ /** Gate for agent-level analytics / telemetry instrumented by the wizard. */
21
+ exports.FLAG_AGENT_ANALYTICS = 'wizard-agent-analytics';
22
+ // ── Deployment key ───────────────────────────────────────────────────
23
+ /**
24
+ * Server deployment key for local evaluation.
25
+ * Override with `AMPLITUDE_EXPERIMENT_DEPLOYMENT_KEY` env var.
26
+ */
27
+ const DEFAULT_DEPLOYMENT_KEY = 'server-YOTsk4MS1RWzLyIf1DmvNDUsdOGqkRdM';
28
+ function resolveDeploymentKey() {
29
+ const fromEnv = process.env.AMPLITUDE_EXPERIMENT_DEPLOYMENT_KEY;
30
+ return (fromEnv ?? DEFAULT_DEPLOYMENT_KEY).trim();
31
+ }
32
+ // ── Singleton client ─────────────────────────────────────────────────
33
+ let client = null;
34
+ let cachedFlags = {};
35
+ /**
36
+ * Initialize the Experiment local-evaluation client and pre-fetch flag configs.
37
+ * Safe to call multiple times — subsequent calls are no-ops.
38
+ *
39
+ * @param userId Optional user ID for targeted flag evaluation.
40
+ * @param deviceId Optional device ID for targeted flag evaluation.
41
+ */
42
+ async function initFeatureFlags(userId, deviceId) {
43
+ if (client)
44
+ return; // already initialized
45
+ const deploymentKey = resolveDeploymentKey();
46
+ if (!deploymentKey) {
47
+ (0, debug_1.debug)('feature-flags: no deployment key — all flags default to off');
48
+ return;
49
+ }
50
+ try {
51
+ client = experiment_node_server_1.Experiment.initializeLocal(deploymentKey);
52
+ await client.start();
53
+ const user = {};
54
+ if (userId)
55
+ user.user_id = userId;
56
+ if (deviceId)
57
+ user.device_id = deviceId;
58
+ const variants = client.evaluateV2(user);
59
+ for (const [key, variant] of Object.entries(variants)) {
60
+ if (variant.value !== undefined && variant.value !== null) {
61
+ cachedFlags[key] = String(variant.value);
62
+ }
63
+ }
64
+ (0, debug_1.debug)('feature-flags: initialized with flags', cachedFlags);
65
+ }
66
+ catch (err) {
67
+ (0, debug_1.debug)('feature-flags: initialization failed, defaulting all flags off', err);
68
+ client = null;
69
+ }
70
+ }
71
+ /**
72
+ * Evaluate a single feature flag. Returns the string variant value,
73
+ * or `undefined` if the flag is not set / client not initialized.
74
+ */
75
+ function getFlag(flagKey) {
76
+ return cachedFlags[flagKey];
77
+ }
78
+ /**
79
+ * Check whether a flag is enabled (variant is `'on'` or `'true'`).
80
+ * Returns `false` when the flag is absent or the client is not initialized.
81
+ */
82
+ function isFlagEnabled(flagKey) {
83
+ const value = cachedFlags[flagKey];
84
+ return value === 'on' || value === 'true';
85
+ }
86
+ /**
87
+ * Return a snapshot of all evaluated flags (key -> string value).
88
+ */
89
+ function getAllFlags() {
90
+ return { ...cachedFlags };
91
+ }
92
+ /**
93
+ * Re-evaluate flags for a specific user (e.g. after login).
94
+ * Updates the cached flags in place.
95
+ */
96
+ async function refreshFlags(userId, deviceId) {
97
+ if (!client)
98
+ return;
99
+ try {
100
+ // Re-fetch flag configs in case they changed
101
+ await client.start();
102
+ const user = {};
103
+ if (userId)
104
+ user.user_id = userId;
105
+ if (deviceId)
106
+ user.device_id = deviceId;
107
+ const variants = client.evaluateV2(user);
108
+ cachedFlags = {};
109
+ for (const [key, variant] of Object.entries(variants)) {
110
+ if (variant.value !== undefined && variant.value !== null) {
111
+ cachedFlags[key] = String(variant.value);
112
+ }
113
+ }
114
+ (0, debug_1.debug)('feature-flags: refreshed flags', cachedFlags);
115
+ }
116
+ catch (err) {
117
+ (0, debug_1.debug)('feature-flags: refresh failed', err);
118
+ }
119
+ }
@@ -132,6 +132,14 @@ export interface WizardSession {
132
132
  workspaces: Array<{
133
133
  id: string;
134
134
  name: string;
135
+ environments?: Array<{
136
+ name: string;
137
+ rank: number;
138
+ app: {
139
+ id: string;
140
+ apiKey?: string | null;
141
+ } | null;
142
+ }> | null;
135
143
  }>;
136
144
  }> | null;
137
145
  /** OAuth id_token held during SUSI account-setup steps. */
@@ -146,6 +154,8 @@ export interface WizardSession {
146
154
  /** Workspace selected during SUSI (written to ampli.json). */
147
155
  selectedWorkspaceId: string | null;
148
156
  selectedWorkspaceName: string | null;
157
+ /** Project/environment selected during SUSI (displayed in TitleBar). */
158
+ selectedProjectName: string | null;
149
159
  /**
150
160
  * Notice shown on the API key entry step of AuthScreen.
151
161
  * Set when auto-fetch fails (e.g. user is not an org admin) so the user
@@ -185,6 +195,12 @@ export interface WizardSession {
185
195
  * runPhase is set to Completed.
186
196
  */
187
197
  amplitudePreDetected: boolean;
198
+ /**
199
+ * While true, McpScreen shows a prompt: skip the agent (default) or run
200
+ * the setup wizard anyway. Cleared when the user chooses or when resetting
201
+ * for a forced wizard run.
202
+ */
203
+ amplitudePreDetectedChoicePending: boolean;
188
204
  /**
189
205
  * True once the activation check confirms events are flowing into the project.
190
206
  * Set immediately if activationLevel is already 'full', otherwise set after
@@ -109,6 +109,7 @@ function buildSession(args) {
109
109
  selectedOrgName: null,
110
110
  selectedWorkspaceId: null,
111
111
  selectedWorkspaceName: null,
112
+ selectedProjectName: null,
112
113
  loginUrl: null,
113
114
  credentials: null,
114
115
  apiKeyNotice: null,
@@ -119,6 +120,7 @@ function buildSession(args) {
119
120
  additionalFeatureQueue: [],
120
121
  frameworkConfig: null,
121
122
  amplitudePreDetected: false,
123
+ amplitudePreDetectedChoicePending: false,
122
124
  dataIngestionConfirmed: false,
123
125
  checklistChartComplete: false,
124
126
  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('session started', {
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('mcp servers added', {
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('mcp no servers to remove', {
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('mcp servers removed', {
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.analytics.wizardCapture('env vars error', {
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.analytics.wizardCapture('env vars error', {
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.analytics.wizardCapture('env vars error', {
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.analytics.wizardCapture('env vars error', {
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('env vars added', {
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('ran prettier', {
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('env upload skipped', {
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('env uploaded', {
31
+ analytics_1.analytics.wizardCapture('Env Uploaded', {
32
32
  provider: provider.name,
33
33
  integration,
34
34
  });
@@ -30,5 +30,5 @@ export const App = ({ store }) => {
30
30
  const contentAreaWidth = Math.max(10, innerWidth - 2);
31
31
  const direction = store.lastNavDirection === 'pop' ? 'right' : 'left';
32
32
  const activeScreen = screens[store.currentScreen] ?? null;
33
- return (_jsx(CommandModeContext.Provider, { value: store.commandMode, children: _jsx(Box, { flexDirection: "column", height: rows, width: columns, alignItems: "center", justifyContent: "flex-start", children: _jsxs(ConsoleView, { store: store, width: width, height: rows, children: [_jsx(TitleBar, { version: store.version, width: innerWidth }), _jsx(Box, { height: 1 }), _jsx(Box, { flexDirection: "column", flexGrow: 1, paddingX: 1, overflow: "hidden", children: _jsx(DissolveTransition, { transitionKey: store.currentScreen, width: contentAreaWidth, height: contentHeight, direction: direction, children: _jsx(ScreenErrorBoundary, { store: store, retryToken: store.screenErrorRetry, children: activeScreen }) }) })] }) }) }));
33
+ return (_jsx(CommandModeContext.Provider, { value: store.commandMode, children: _jsx(Box, { flexDirection: "column", height: rows, width: columns, alignItems: "center", justifyContent: "flex-start", children: _jsxs(ConsoleView, { store: store, width: width, height: rows, children: [_jsx(TitleBar, { version: store.version, width: innerWidth, orgName: store.session.selectedOrgName, projectName: store.session.selectedProjectName }), _jsx(Box, { height: 1 }), _jsx(Box, { flexDirection: "column", flexGrow: 1, paddingX: 1, overflow: "hidden", children: _jsx(DissolveTransition, { transitionKey: store.currentScreen, width: contentAreaWidth, height: contentHeight, direction: direction, children: _jsx(ScreenErrorBoundary, { store: store, retryToken: store.screenErrorRetry, children: activeScreen }) }) })] }) }) }));
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, 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':
@@ -52,12 +66,9 @@ function executeCommand(raw, store) {
52
66
  case '/exit':
53
67
  store.setOutroData({ kind: OutroKind.Cancel, message: 'Exited.' });
54
68
  break;
55
- case '/help':
56
- store.setCommandFeedback(getHelpText());
57
- break;
58
69
  default:
59
70
  if (cmd)
60
- store.setCommandFeedback(`Unknown command: ${cmd}. Type /help.`);
71
+ store.setCommandFeedback(`Unknown command: ${cmd}. Type / to see available commands.`);
61
72
  }
62
73
  }
63
74
  export const ConsoleView = ({ store, width, height, children, }) => {
@@ -116,7 +127,7 @@ export const ConsoleView = ({ store, width, height, children, }) => {
116
127
  }, { isActive: !inputActive });
117
128
  const handleSubmit = (value) => {
118
129
  const isSlashCommand = value.startsWith('/');
119
- analytics.wizardCapture('agent message sent', {
130
+ analytics.wizardCapture('Agent Message Sent', {
120
131
  message_length: value.length,
121
132
  is_slash_command: isSlashCommand,
122
133
  });
@@ -1,6 +1,8 @@
1
1
  interface TitleBarProps {
2
2
  version: string;
3
3
  width: number;
4
+ orgName?: string | null;
5
+ projectName?: string | null;
4
6
  }
5
- export declare const TitleBar: ({ version, width }: TitleBarProps) => import("react/jsx-runtime").JSX.Element;
7
+ export declare const TitleBar: ({ version, width, orgName, projectName, }: TitleBarProps) => import("react/jsx-runtime").JSX.Element;
6
8
  export {};
@@ -1,16 +1,27 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
3
  import { Colors } from '../styles.js';
4
4
  const FEEDBACK = 'Feedback: wizard@amplitude.com ';
5
5
  const FEEDBACK_SHORT = ' wizard@amplitude.com ';
6
- export const TitleBar = ({ version, width }) => {
6
+ export const TitleBar = ({ version, width, orgName, projectName, }) => {
7
7
  const fullTitle = ` Amplitude Wizard v${version}`;
8
- const needShort = width < fullTitle.length + FEEDBACK.length;
8
+ // Build context string from org + project names
9
+ const contextParts = [];
10
+ if (orgName)
11
+ contextParts.push(orgName);
12
+ if (projectName)
13
+ contextParts.push(projectName);
14
+ const contextStr = contextParts.length > 0 ? ` ${contextParts.join(' / ')} ` : '';
15
+ const needShort = width < fullTitle.length + contextStr.length + FEEDBACK.length;
9
16
  const feedback = needShort ? FEEDBACK_SHORT : FEEDBACK;
10
- const title = needShort && fullTitle.length + feedback.length > width
17
+ const title = needShort && fullTitle.length + contextStr.length + feedback.length > width
11
18
  ? ` Wizard v${version}`
12
19
  : fullTitle;
13
- const gap = Math.max(0, width - title.length - feedback.length);
20
+ // If context doesn't fit even with the short title, drop it
21
+ const showContext = contextStr.length > 0 &&
22
+ title.length + contextStr.length + feedback.length <= width;
23
+ const middleText = showContext ? contextStr : '';
24
+ const gap = Math.max(0, width - title.length - middleText.length - feedback.length);
14
25
  const padding = ' '.repeat(gap);
15
- return (_jsx(Box, { width: width, overflow: "hidden", children: _jsxs(Text, { backgroundColor: Colors.accent, color: "white", bold: true, children: [title, padding, feedback] }) }));
26
+ return (_jsxs(Box, { width: width, overflow: "hidden", children: [_jsx(Text, { backgroundColor: Colors.accent, color: "white", bold: true, children: title }), middleText ? (_jsxs(Text, { backgroundColor: Colors.accent, color: "white", dimColor: true, children: [padding, middleText] })) : (_jsx(Text, { backgroundColor: Colors.accent, color: "white", children: padding })), _jsx(Text, { backgroundColor: Colors.accent, color: "white", bold: true, children: feedback })] }));
16
27
  };
@@ -12,5 +12,8 @@ export declare const COMMANDS: {
12
12
  export declare const TEST_PROMPT: string;
13
13
  /** Returns the feedback text for the /whoami command. */
14
14
  export declare function getWhoamiText(session: Pick<WizardSession, 'selectedOrgName' | 'selectedWorkspaceName' | 'region'>): string;
15
- /** Returns the feedback text for the /help command. */
16
- export declare function getHelpText(): string;
15
+ /**
16
+ * Parses `/feedback <message>` from a slash command line.
17
+ * Returns `undefined` if the line is not a feedback command or the message is empty.
18
+ */
19
+ export declare function parseFeedbackSlashInput(raw: string): string | undefined;
@@ -11,10 +11,13 @@ 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' },
17
- { cmd: '/help', desc: 'List available slash commands' },
18
21
  ];
19
22
  export const TEST_PROMPT = 'Demo the wizard prompt tools. ' +
20
23
  'First, use the wizard-tools:confirm tool to ask if I want to continue. ' +
@@ -24,8 +27,14 @@ export const TEST_PROMPT = 'Demo the wizard prompt tools. ' +
24
27
  export function getWhoamiText(session) {
25
28
  return `org: ${session.selectedOrgName ?? '(none)'} workspace: ${session.selectedWorkspaceName ?? '(none)'} region: ${session.region ?? '(none)'}`;
26
29
  }
27
- /** Returns the feedback text for the /help command. */
28
- export function getHelpText() {
29
- const maxCmd = Math.max(...COMMANDS.map((c) => c.cmd.length));
30
- return COMMANDS.map((c) => `${c.cmd.padEnd(maxCmd)} ${c.desc}`).join('\n');
30
+ /**
31
+ * Parses `/feedback <message>` from a slash command line.
32
+ * Returns `undefined` if the line is not a feedback command or the message is empty.
33
+ */
34
+ export function parseFeedbackSlashInput(raw) {
35
+ const m = /^\s*\/feedback(?:\s+(.*))?\s*$/i.exec(raw);
36
+ if (!m)
37
+ return undefined;
38
+ const body = m[1]?.trim();
39
+ return body || undefined;
31
40
  }
@@ -5,7 +5,8 @@
5
5
  * 1. OAuth waiting — spinner + login URL while browser auth happens
6
6
  * 2. Org selection — picker if the user belongs to multiple orgs
7
7
  * 3. Workspace selection — picker if the org has multiple workspaces
8
- * 4. API key entry text input for the Amplitude analytics write key
8
+ * 4. Project selectionpicker if the workspace has multiple environments
9
+ * 5. API key entry — text input (only if no project key could be resolved)
9
10
  *
10
11
  * The screen drives itself from session.pendingOrgs + session.credentials.
11
12
  * When credentials are set the router resolves past this screen.