Skip to content

Commit 7811a43

Browse files
betegonclaude
andcommitted
fix(init): lint, early-return guard, and test coverage for feature blurbs
- Fix Biome import order in logging-ui.ts (renderTextTable after markdown) - Fix Biome format: collapse single-item success array, split long JSX condition - Fix LoggingUI.summary() early-return guard to account for featureBlurbs - Fix buildSummary null guard to include featureBlurbs in the emptiness check - Add test coverage: formatters (blurb population, suppressed Features row, positional label matching, display-order sort), ink-report (blurbs section), logging-ui (blurbs table, no section when absent) Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent f783d96 commit 7811a43

7 files changed

Lines changed: 178 additions & 7 deletions

File tree

src/lib/init/feedback.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ const FEEDBACK_COMMANDS: Record<InitFeedbackOutcome, string> = {
77
};
88

99
const FEEDBACK_COPY: Record<InitFeedbackOutcome, string[]> = {
10-
success: [
11-
"Tell us what felt great or rough:",
12-
],
10+
success: ["Tell us what felt great or rough:"],
1311
cancelled: [
1412
"Sad to see setup stop. Was something going sideways?",
1513
"Tell us so we can fix it:",

src/lib/init/formatters.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ function buildSummary(output: WizardOutput): WizardSummary | null {
7575
})
7676
.filter((b): b is { label: string; blurb: string } => b !== null);
7777

78-
if (fields.length === 0 && changedFiles.length === 0) {
78+
if (fields.length === 0 && changedFiles.length === 0 && featureBlurbs.length === 0) {
7979
return null;
8080
}
8181

src/lib/init/ui/ink-app.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1176,7 +1176,8 @@ function SummaryPanel({
11761176
{summary.changedFiles !== undefined && summary.changedFiles.length > 0 ? (
11771177
<ChangedFilesTree files={summary.changedFiles} />
11781178
) : null}
1179-
{summary.featureBlurbs !== undefined && summary.featureBlurbs.length > 0 ? (
1179+
{summary.featureBlurbs !== undefined &&
1180+
summary.featureBlurbs.length > 0 ? (
11801181
<Box flexDirection="column" flexShrink={0} marginTop={1}>
11811182
<Text bold color={MUTED}>
11821183
Here&apos;s what we set up

src/lib/init/ui/logging-ui.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
* logs deterministic and free of carriage returns.
1919
*/
2020

21-
import { renderTextTable } from "../../formatters/text-table.js";
2221
import {
2322
renderInlineMarkdown,
2423
renderMarkdown,
2524
} from "../../formatters/markdown.js";
25+
import { renderTextTable } from "../../formatters/text-table.js";
2626
import { formatFeedbackHint, type InitFeedbackOutcome } from "../feedback.js";
2727
import { buildFileTree, flattenTree } from "./file-tree.js";
2828
import type {
@@ -95,7 +95,11 @@ export class LoggingUI implements WizardUI {
9595
}
9696

9797
summary(summary: WizardSummary): void {
98-
if (summary.fields.length === 0 && !summary.changedFiles?.length) {
98+
if (
99+
summary.fields.length === 0 &&
100+
!summary.changedFiles?.length &&
101+
!summary.featureBlurbs?.length
102+
) {
99103
return;
100104
}
101105
// Compact two-column key/value listing — one line per field. The

test/lib/init/formatters.test.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,120 @@ describe("formatResult", () => {
184184
});
185185
});
186186

187+
describe("formatResult with featureBlurbs", () => {
188+
test("populates featureBlurbs from output.featureBlurbs paired positionally with output.features", () => {
189+
const { ui, calls } = createMockUI();
190+
formatResult(
191+
{
192+
status: "success",
193+
result: {
194+
platform: "Next.js",
195+
projectDir: "/app",
196+
features: ["errorMonitoring", "performanceMonitoring"],
197+
featureBlurbs: [
198+
{ feature: "errorMonitoring", blurb: "Captures exceptions." },
199+
{ feature: "performanceMonitoring", blurb: "Traces requests." },
200+
],
201+
},
202+
},
203+
ui
204+
);
205+
206+
const summary = summaryCall(calls);
207+
expect(summary?.featureBlurbs).toEqual([
208+
{ label: "Error Monitoring", blurb: "Captures exceptions." },
209+
{ label: "Tracing", blurb: "Traces requests." },
210+
]);
211+
});
212+
213+
test("suppresses the Features row when featureBlurbs are present", () => {
214+
const { ui, calls } = createMockUI();
215+
formatResult(
216+
{
217+
status: "success",
218+
result: {
219+
platform: "Next.js",
220+
features: ["errorMonitoring"],
221+
featureBlurbs: [
222+
{ feature: "errorMonitoring", blurb: "Captures exceptions." },
223+
],
224+
},
225+
},
226+
ui
227+
);
228+
229+
const summary = summaryCall(calls);
230+
expect(summary?.fields.some((f) => f.label === "Features")).toBe(false);
231+
});
232+
233+
test("shows the Features row when featureBlurbs are absent", () => {
234+
const { ui, calls } = createMockUI();
235+
formatResult(
236+
{
237+
status: "success",
238+
result: {
239+
platform: "Next.js",
240+
features: ["errorMonitoring", "sessionReplay"],
241+
},
242+
},
243+
ui
244+
);
245+
246+
const summary = summaryCall(calls);
247+
expect(summary?.fields.some((f) => f.label === "Features")).toBe(true);
248+
});
249+
250+
test("uses output.features for labels regardless of what the agent echoed in feature field", () => {
251+
const { ui, calls } = createMockUI();
252+
formatResult(
253+
{
254+
status: "success",
255+
result: {
256+
platform: "Next.js",
257+
features: ["errorMonitoring", "sessionReplay"],
258+
// Agent echoed back wrong IDs
259+
featureBlurbs: [
260+
{ feature: "error_monitoring", blurb: "Blurb A." },
261+
{ feature: "session-replay", blurb: "Blurb B." },
262+
],
263+
},
264+
},
265+
ui
266+
);
267+
268+
const summary = summaryCall(calls);
269+
// Labels come from output.features positionally, not blurb.feature
270+
expect(summary?.featureBlurbs?.[0]?.label).toBe("Error Monitoring");
271+
expect(summary?.featureBlurbs?.[1]?.label).toBe("Session Replay");
272+
});
273+
274+
test("sorts featureBlurbs by canonical display order", () => {
275+
const { ui, calls } = createMockUI();
276+
formatResult(
277+
{
278+
status: "success",
279+
result: {
280+
platform: "Next.js",
281+
// Server returned performanceMonitoring before errorMonitoring
282+
features: ["performanceMonitoring", "errorMonitoring"],
283+
featureBlurbs: [
284+
{ feature: "performanceMonitoring", blurb: "Traces." },
285+
{ feature: "errorMonitoring", blurb: "Captures." },
286+
],
287+
},
288+
},
289+
ui
290+
);
291+
292+
const summary = summaryCall(calls);
293+
// errorMonitoring comes before performanceMonitoring in FEATURE_DISPLAY_ORDER
294+
expect(summary?.featureBlurbs?.map((b) => b.label)).toEqual([
295+
"Error Monitoring",
296+
"Tracing",
297+
]);
298+
});
299+
});
300+
187301
describe("formatError", () => {
188302
test("logs the error message", () => {
189303
const { ui, calls } = createMockUI();

test/lib/init/ui/ink-report.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,37 @@ describe("formatSuccessReport with summary fields", () => {
4949
});
5050
});
5151

52+
describe("formatSuccessReport with featureBlurbs", () => {
53+
test("renders Here's what we set up heading and blurb content", () => {
54+
const output = stripAnsi(
55+
formatSuccessReport("Done!", {
56+
fields: [],
57+
featureBlurbs: [
58+
{ label: "Error Monitoring", blurb: "Captures exceptions." },
59+
{ label: "Tracing", blurb: "Traces requests end-to-end." },
60+
],
61+
})
62+
);
63+
expect(output).toContain("Here's what we set up");
64+
expect(output).toContain("Error Monitoring");
65+
expect(output).toContain("Captures exceptions.");
66+
expect(output).toContain("Tracing");
67+
expect(output).toContain("Traces requests end-to-end.");
68+
});
69+
70+
test("no blurbs section when featureBlurbs is absent", () => {
71+
const output = stripAnsi(formatSuccessReport("Done!", { fields: [] }));
72+
expect(output).not.toContain("Here's what we set up");
73+
});
74+
75+
test("no blurbs section when featureBlurbs is empty", () => {
76+
const output = stripAnsi(
77+
formatSuccessReport("Done!", { fields: [], featureBlurbs: [] })
78+
);
79+
expect(output).not.toContain("Here's what we set up");
80+
});
81+
});
82+
5283
describe("formatSuccessReport with changedFiles", () => {
5384
test("shows Changed files heading and file paths", () => {
5485
const output = stripAnsi(

test/lib/init/ui/logging-ui.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,4 +307,27 @@ describe("LoggingUI summary", () => {
307307
expect(lines.some((l) => l.includes("−") && l.includes("b.ts"))).toBe(true);
308308
expect(lines.some((l) => l.includes("~") && l.includes("c.ts"))).toBe(true);
309309
});
310+
311+
test("featureBlurbs renders Here's what we set up table with label and blurb", () => {
312+
const { ui, stdout } = createUI();
313+
ui.summary({
314+
fields: [],
315+
featureBlurbs: [
316+
{ label: "Error Monitoring", blurb: "Captures exceptions." },
317+
{ label: "Tracing", blurb: "Traces requests." },
318+
],
319+
});
320+
const out = stdout();
321+
expect(out).toContain("Here's what we set up");
322+
expect(out).toContain("Error Monitoring");
323+
expect(out).toContain("Captures exceptions.");
324+
expect(out).toContain("Tracing");
325+
expect(out).toContain("Traces requests.");
326+
});
327+
328+
test("no featureBlurbs section when featureBlurbs is absent", () => {
329+
const { ui, stdout } = createUI();
330+
ui.summary({ fields: [{ label: "Platform", value: "Next.js" }] });
331+
expect(stdout()).not.toContain("Here's what we set up");
332+
});
310333
});

0 commit comments

Comments
 (0)