Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion __tests__/e2e/skip-onboarding.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('when the user skips onboarding', () => {
await session.sendKey(ENTER);

// Done — no onboarding ran
await session.waitForText('Confidence is ready');
await session.waitForText('Onboarding skipped');
await session.waitForText("What's next?");

// Docs and dashboard links still appear
Expand Down
10 changes: 7 additions & 3 deletions __tests__/ui/screens/DoneScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ vi.mock('../../../src/integrations/chat.js', () => ({
}));

describe('DoneScreen', () => {
it('renders completion message', async () => {
it('renders skipped message when no code changes', async () => {
using sut = renderScreen(<DoneScreen />, { screen: ScreenId.Done });
await waitFor(() => {
expect(sut.lastFrame()).toContain('Confidence is ready');
expect(sut.lastFrame()).toContain('Onboarding skipped');
expect(sut.lastFrame()).toContain(
'You can always use Confidence AI plugin to run onboarding yourself later.',
);
});
});

Expand Down Expand Up @@ -39,10 +42,11 @@ describe('DoneScreen', () => {
});

describe('when code changes are present', () => {
it('shows code changes summary', async () => {
it('renders completion message and code changes summary', async () => {
using sut = renderScreen(<DoneScreen />, { screen: ScreenId.Done });
store.setCodeChanges(['Added @spotify-confidence/sdk', 'Created confidence.config.ts']);
await waitFor(() => {
expect(sut.lastFrame()).toContain('Confidence is ready');
expect(sut.lastFrame()).toContain('What we have set up');
expect(sut.lastFrame()).toContain('confidence.config.ts');
});
Expand Down
4 changes: 2 additions & 2 deletions __tests__/ui/screens/OnboardingFlow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('Onboarding flow', () => {
sut.stdin.write(ARROW_DOWN + ENTER);

await waitFor(() => {
expect(sut.lastFrame()).toContain('Confidence is ready');
expect(sut.lastFrame()).toContain('Onboarding skipped');
});
});
});
Expand Down Expand Up @@ -199,7 +199,7 @@ describe('Onboarding flow', () => {
sut.stdin.write(ESCAPE);

await waitFor(() => {
expect(sut.lastFrame()).toContain('Confidence is ready');
expect(sut.lastFrame()).toContain('Onboarding skipped');
});
});

Expand Down
8 changes: 2 additions & 6 deletions src/integrations/cursor/onboarding.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { type ChildProcess, spawn } from 'node:child_process';
import { createInterface } from 'node:readline';
import type { OnboardingOpts, OnboardingCallbacks } from '../types.js';
import { STATUS_PREFIX } from '../constants.js';
import { type StreamEvent, extractTextLines } from '../stream-json.js';
import { normalizeStatusLine } from '../utils.js';

export function runOnboarding(
opts: OnboardingOpts,
Expand Down Expand Up @@ -50,11 +50,7 @@ export function runOnboarding(
if (!stripped) continue;
allLines.push(stripped);
callbacks.onStdout(stripped);

const status = stripped.startsWith(STATUS_PREFIX)
? stripped.slice(STATUS_PREFIX.length)
: stripped;
callbacks.onStatus(status);
callbacks.onStatus(normalizeStatusLine(stripped));
}
});

Expand Down
1 change: 1 addition & 0 deletions src/integrations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ export {
} from './mcp/index.js';
export { launchChatSession } from './chat.js';
export { detectInstalledPlugins, prepareIde, installPlugin } from './plugins.js';
export { normalizeStatusLine } from './utils.js';
5 changes: 5 additions & 0 deletions src/integrations/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { STATUS_PREFIX } from './constants.js';

export function normalizeStatusLine(line: string) {
return line.startsWith(STATUS_PREFIX) ? line.slice(STATUS_PREFIX.length) : line;
}
3 changes: 2 additions & 1 deletion src/lib/onboarding-prompt/steps/rules.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Rules

- Prefix every progress update with "STATUS: " (e.g. "STATUS: Scanning for existing flag usage...", "STATUS: Installing dependencies..."). Print these before each step AND periodically within longer steps so the user sees what you're working on. Keep STATUS text short (~60 characters max). Only STATUS-prefixed lines are shown in the UI; everything else is logged silently.
- Prefix every progress update with "STATUS: " if it isn't prefixed already (e.g. "STATUS: Scanning for existing flag usage...", "STATUS: Installing dependencies..."). Print these before each step AND periodically within longer steps so the user sees what you're working on because STATUS-prefixed lines are shown in the UI; everything else is logged silently.
- Keep STATUS text short **(~60 characters max)**.
- Never show raw JSON payloads, MCP tool names, or secrets in output.
- Read the client secret from CONFIDENCE_CLIENT_SECRET env var in all generated code.
- Use the OpenFeature API with local resolve where supported. Access flag values via dot notation: `flag-name.property`.
Expand Down
16 changes: 13 additions & 3 deletions src/ui/tui/screens/done/DoneScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,27 @@ export function DoneScreen() {
const narrow = useIsNarrow();
const { reportFile, codeChanges, projectDir, ide } = session;
const align = narrow ? HAlign.Left : HAlign.Center;
const skipped = codeChanges.length === 0;

return (
<Box flexDirection="column" flexGrow={1} justifyContent="space-between">
<Box flexDirection="column" alignItems={align} flexGrow={1} justifyContent={VAlign.Center}>
<Box marginBottom={1}>
<Text color={Colors.success} bold>
{Icons.check} Confidence is ready!
<Text color={skipped ? Colors.muted : Colors.success} bold>
{skipped ? Icons.diamond : Icons.check}{' '}
{skipped ? 'Onboarding skipped' : 'Confidence is ready!'}
</Text>
</Box>

{codeChanges.length > 0 && !isShort && (
{skipped && !isShort && (
<Box marginBottom={1}>
<Text color={Colors.muted}>
You can always use Confidence AI plugin to run onboarding yourself later.
</Text>
</Box>
)}

{!skipped && !isShort && (
<>
<Box alignItems={align}>
<Text bold>What we have set up:</Text>
Expand Down
12 changes: 7 additions & 5 deletions src/ui/tui/screens/onboard-project/useOnboardingProcess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type ChildProcess } from 'node:child_process';
import { buildOnboardingPrompt } from '@lib/onboarding-prompt/index.js';
import { detectFramework } from '@frameworks/index.js';
import { ScreenId, type OnboardingGoal } from '@lib/session.js';
import { type IdeId, getIntegration } from '@integrations/index.js';
import { type IdeId, getIntegration, normalizeStatusLine } from '@integrations/index.js';
import type { DetectedProvider } from '@providers/types.js';
import { useLogger } from '../../hooks/useLog.js';
import { $session, store, isStaleSession } from '../../store.js';
Expand Down Expand Up @@ -61,10 +61,12 @@ export function useOnboardingProcess(): OnboardingProcess {
store.setReportFile('CONFIDENCE_QUICKSTART.md');
store.setCodeChanges(
lines
? lines.filter(
(line) =>
line.includes('Created') || line.includes('Modified') || line.includes('Added'),
)
? lines
.filter(
(line) =>
line.includes('Created') || line.includes('Modified') || line.includes('Added'),
)
.map(normalizeStatusLine)
: dryRunCodeChanges(goal),
);
setPhase('done');
Expand Down