uilint 0.2.49 → 0.2.50

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.
@@ -0,0 +1,1691 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ Spinner,
4
+ analyze,
5
+ execute,
6
+ getAllInstallers,
7
+ getInjectionPoints
8
+ } from "./chunk-4PRDR64J.js";
9
+ import {
10
+ detectCoverageSetup
11
+ } from "./chunk-MSOAR5VP.js";
12
+ import "./chunk-VSBVUS56.js";
13
+ import "./chunk-ZDSDZNIB.js";
14
+ import {
15
+ pc
16
+ } from "./chunk-CZNPG4UI.js";
17
+ import {
18
+ detectPackageManager,
19
+ runTestsWithCoverage
20
+ } from "./chunk-JPE27ROY.js";
21
+
22
+ // src/commands/init-ui.tsx
23
+ import { render } from "ink";
24
+
25
+ // src/commands/init/components/InstallApp.tsx
26
+ import { useState as useState6, useEffect } from "react";
27
+ import { Box as Box6, Text as Text6, useApp as useApp5, useInput as useInput6 } from "ink";
28
+
29
+ // src/commands/init/components/ProjectSelector.tsx
30
+ import { useState } from "react";
31
+ import { Box, Text, useInput, useApp } from "ink";
32
+ import { jsx, jsxs } from "react/jsx-runtime";
33
+ function getDetectedProjects(project) {
34
+ const projects = [];
35
+ for (const app of project.nextApps) {
36
+ const relativePath = app.projectPath.replace(project.workspaceRoot + "/", "");
37
+ projects.push({
38
+ id: `next:${app.projectPath}`,
39
+ name: relativePath || ".",
40
+ path: app.projectPath,
41
+ type: "nextjs",
42
+ hint: "Next.js App Router",
43
+ isConfigured: false
44
+ // TODO: detect if overlay is installed
45
+ });
46
+ }
47
+ for (const app of project.viteApps) {
48
+ const relativePath = app.projectPath.replace(project.workspaceRoot + "/", "");
49
+ projects.push({
50
+ id: `vite:${app.projectPath}`,
51
+ name: relativePath || ".",
52
+ path: app.projectPath,
53
+ type: "vite",
54
+ hint: "Vite + React",
55
+ isConfigured: false
56
+ });
57
+ }
58
+ return projects;
59
+ }
60
+ function FrameworkBadge({ type }) {
61
+ switch (type) {
62
+ case "nextjs":
63
+ return /* @__PURE__ */ jsx(Text, { color: "white", backgroundColor: "black", children: " Next.js " });
64
+ case "vite":
65
+ return /* @__PURE__ */ jsx(Text, { color: "black", backgroundColor: "yellow", children: " Vite " });
66
+ default:
67
+ return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Other" });
68
+ }
69
+ }
70
+ function ProjectSelector({
71
+ projects,
72
+ onSelect,
73
+ onCancel
74
+ }) {
75
+ const { exit } = useApp();
76
+ const [cursor, setCursor] = useState(0);
77
+ useInput((input, key) => {
78
+ if (key.upArrow) {
79
+ setCursor((prev) => prev > 0 ? prev - 1 : projects.length - 1);
80
+ } else if (key.downArrow) {
81
+ setCursor((prev) => prev < projects.length - 1 ? prev + 1 : 0);
82
+ } else if (key.return) {
83
+ const selected = projects[cursor];
84
+ if (selected) {
85
+ onSelect(selected);
86
+ }
87
+ } else if (input === "q" || key.escape) {
88
+ onCancel?.();
89
+ exit();
90
+ }
91
+ });
92
+ if (projects.length === 0) {
93
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
94
+ /* @__PURE__ */ jsx(Text, { color: "yellow", children: "No projects detected." }),
95
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "UILint works with Next.js (App Router) and Vite + React projects." })
96
+ ] });
97
+ }
98
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
99
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, children: "Select a project to configure:" }) }),
100
+ projects.map((project, index) => {
101
+ const isCursor = index === cursor;
102
+ return /* @__PURE__ */ jsxs(Box, { paddingLeft: 1, children: [
103
+ /* @__PURE__ */ jsx(Text, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u203A " : " " }),
104
+ /* @__PURE__ */ jsx(Box, { width: 12, children: /* @__PURE__ */ jsx(FrameworkBadge, { type: project.type }) }),
105
+ /* @__PURE__ */ jsx(Box, { width: 30, children: /* @__PURE__ */ jsx(Text, { color: isCursor ? "cyan" : void 0, bold: isCursor, children: project.name }) }),
106
+ project.isConfigured && /* @__PURE__ */ jsx(Text, { color: "green", dimColor: true, children: "configured" })
107
+ ] }, project.id);
108
+ }),
109
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
110
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u2191\u2193" }),
111
+ " navigate",
112
+ " ",
113
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: "enter" }),
114
+ " select",
115
+ " ",
116
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: "q" }),
117
+ " quit"
118
+ ] }) })
119
+ ] });
120
+ }
121
+
122
+ // src/commands/init/components/MultiSelect.tsx
123
+ import { useState as useState2, useCallback } from "react";
124
+ import { Box as Box2, Text as Text2, useInput as useInput2, useApp as useApp2 } from "ink";
125
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
126
+ function StatusIndicator({ status, isSelected, isMarkedForRemoval }) {
127
+ if (isMarkedForRemoval) {
128
+ return /* @__PURE__ */ jsx2(Text2, { color: "red", children: "\u2717" });
129
+ }
130
+ if (status === "installed") {
131
+ return /* @__PURE__ */ jsx2(Text2, { color: "green", children: "\u2713" });
132
+ }
133
+ if (status === "upgradeable") {
134
+ return isSelected ? /* @__PURE__ */ jsx2(Text2, { color: "blue", children: "\u2B06" }) : /* @__PURE__ */ jsx2(Text2, { color: "green", children: "\u2713" });
135
+ }
136
+ if (isSelected || status === "selected") {
137
+ return /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "\u25C9" });
138
+ }
139
+ if (status === "partial") {
140
+ return /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "\u25D0" });
141
+ }
142
+ return /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u25CB" });
143
+ }
144
+ function StatusLabel({ status, isMarkedForRemoval }) {
145
+ if (isMarkedForRemoval) {
146
+ return /* @__PURE__ */ jsx2(Text2, { color: "red", dimColor: true, children: "remove" });
147
+ }
148
+ if (status === "installed") {
149
+ return /* @__PURE__ */ jsx2(Text2, { color: "green", dimColor: true, children: "installed" });
150
+ }
151
+ if (status === "upgradeable") {
152
+ return /* @__PURE__ */ jsx2(Text2, { color: "blue", dimColor: true, children: "upgrade available" });
153
+ }
154
+ if (status === "partial") {
155
+ return /* @__PURE__ */ jsx2(Text2, { color: "yellow", dimColor: true, children: "partial" });
156
+ }
157
+ return /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "-" });
158
+ }
159
+ function ConfigSelector({
160
+ items,
161
+ onSubmit,
162
+ onCancel
163
+ }) {
164
+ const { exit } = useApp2();
165
+ const [cursor, setCursor] = useState2(0);
166
+ const [selected, setSelected] = useState2(() => {
167
+ return new Set(
168
+ items.filter((item) => item.status !== "installed" && !item.disabled).map((item) => item.id)
169
+ );
170
+ });
171
+ const [markedForRemoval, setMarkedForRemoval] = useState2(/* @__PURE__ */ new Set());
172
+ const categories = Array.from(new Set(items.map((item) => item.category)));
173
+ const itemsByCategory = /* @__PURE__ */ new Map();
174
+ for (const cat of categories) {
175
+ itemsByCategory.set(cat, items.filter((item) => item.category === cat));
176
+ }
177
+ const flatItems = items;
178
+ const handleToggle = useCallback(() => {
179
+ const item = flatItems[cursor];
180
+ if (!item || item.disabled) return;
181
+ if (item.status === "installed") {
182
+ setMarkedForRemoval((prev) => {
183
+ const next = new Set(prev);
184
+ if (next.has(item.id)) {
185
+ next.delete(item.id);
186
+ } else {
187
+ next.add(item.id);
188
+ }
189
+ return next;
190
+ });
191
+ return;
192
+ }
193
+ setSelected((prev) => {
194
+ const next = new Set(prev);
195
+ if (next.has(item.id)) {
196
+ next.delete(item.id);
197
+ } else {
198
+ next.add(item.id);
199
+ }
200
+ return next;
201
+ });
202
+ }, [cursor, flatItems]);
203
+ useInput2((input, key) => {
204
+ if (key.upArrow) {
205
+ setCursor((prev) => prev > 0 ? prev - 1 : flatItems.length - 1);
206
+ } else if (key.downArrow) {
207
+ setCursor((prev) => prev < flatItems.length - 1 ? prev + 1 : 0);
208
+ } else if (input === " ") {
209
+ handleToggle();
210
+ } else if (key.return) {
211
+ onSubmit(Array.from(selected), Array.from(markedForRemoval));
212
+ } else if (input === "q" || key.escape) {
213
+ onCancel?.();
214
+ exit();
215
+ } else if (input === "a") {
216
+ setSelected(
217
+ new Set(
218
+ items.filter((item) => item.status !== "installed" && !item.disabled).map((item) => item.id)
219
+ )
220
+ );
221
+ setMarkedForRemoval(/* @__PURE__ */ new Set());
222
+ } else if (input === "n") {
223
+ setSelected(/* @__PURE__ */ new Set());
224
+ setMarkedForRemoval(/* @__PURE__ */ new Set());
225
+ }
226
+ });
227
+ let globalIndex = 0;
228
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
229
+ categories.map((category) => {
230
+ const categoryItems = itemsByCategory.get(category) || [];
231
+ const categoryIcon = categoryItems[0]?.categoryIcon || "\u2022";
232
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
233
+ /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { bold: true, color: "white", children: [
234
+ categoryIcon,
235
+ " ",
236
+ category
237
+ ] }) }),
238
+ categoryItems.map((item) => {
239
+ const itemIndex = globalIndex++;
240
+ const isCursor = itemIndex === cursor;
241
+ const isItemSelected = selected.has(item.id);
242
+ const isItemMarkedForRemoval = markedForRemoval.has(item.id);
243
+ const isDisabled = item.disabled === true;
244
+ return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 2, children: [
245
+ /* @__PURE__ */ jsx2(Text2, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u203A " : " " }),
246
+ /* @__PURE__ */ jsx2(Box2, { width: 2, children: /* @__PURE__ */ jsx2(StatusIndicator, { status: item.status, isSelected: isItemSelected, isMarkedForRemoval: isItemMarkedForRemoval }) }),
247
+ /* @__PURE__ */ jsx2(Box2, { width: 28, children: /* @__PURE__ */ jsx2(
248
+ Text2,
249
+ {
250
+ color: isItemMarkedForRemoval ? "red" : isDisabled ? void 0 : isCursor ? "cyan" : void 0,
251
+ dimColor: isDisabled && !isItemMarkedForRemoval,
252
+ children: item.label
253
+ }
254
+ ) }),
255
+ /* @__PURE__ */ jsx2(Box2, { width: 20, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: item.hint || "" }) }),
256
+ /* @__PURE__ */ jsx2(StatusLabel, { status: item.status, isMarkedForRemoval: isItemMarkedForRemoval })
257
+ ] }, item.id);
258
+ })
259
+ ] }, category);
260
+ }),
261
+ /* @__PURE__ */ jsx2(Box2, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
262
+ /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "\u2191\u2193" }),
263
+ " navigate",
264
+ " ",
265
+ /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "space" }),
266
+ " toggle",
267
+ " ",
268
+ /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "a" }),
269
+ " all",
270
+ " ",
271
+ /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "n" }),
272
+ " none",
273
+ " ",
274
+ /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "enter" }),
275
+ " apply",
276
+ " ",
277
+ /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "q" }),
278
+ " quit"
279
+ ] }) }),
280
+ /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { children: [
281
+ /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: selected.size }),
282
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " to install" }),
283
+ markedForRemoval.size > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
284
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: ", " }),
285
+ /* @__PURE__ */ jsx2(Text2, { color: "red", children: markedForRemoval.size }),
286
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " to remove" })
287
+ ] })
288
+ ] }) })
289
+ ] });
290
+ }
291
+
292
+ // src/commands/init/components/RuleSelector.tsx
293
+ import { useState as useState4, useMemo } from "react";
294
+ import { Box as Box4, Text as Text4, useInput as useInput4, useApp as useApp3 } from "ink";
295
+ import { getRulesByCategory, getCategoryMeta } from "uilint-eslint";
296
+
297
+ // src/commands/init/components/OptionField.tsx
298
+ import { useState as useState3 } from "react";
299
+ import { Box as Box3, Text as Text3, useInput as useInput3 } from "ink";
300
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
301
+ function BooleanField({
302
+ field,
303
+ value,
304
+ onChange,
305
+ isActive
306
+ }) {
307
+ const checked = Boolean(value);
308
+ useInput3(
309
+ (input) => {
310
+ if (isActive && input === " ") {
311
+ onChange(!checked);
312
+ }
313
+ },
314
+ { isActive }
315
+ );
316
+ return /* @__PURE__ */ jsxs3(Box3, { children: [
317
+ /* @__PURE__ */ jsx3(Text3, { color: isActive ? "cyan" : void 0, children: isActive ? "\u203A " : " " }),
318
+ /* @__PURE__ */ jsx3(Text3, { color: checked ? "green" : "gray", children: checked ? "[\u2713]" : "[ ]" }),
319
+ /* @__PURE__ */ jsxs3(Text3, { children: [
320
+ " ",
321
+ field.label
322
+ ] }),
323
+ field.description && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
324
+ " - ",
325
+ field.description
326
+ ] })
327
+ ] });
328
+ }
329
+ function NumberField({
330
+ field,
331
+ value,
332
+ onChange,
333
+ isActive
334
+ }) {
335
+ const [inputValue, setInputValue] = useState3(String(value ?? field.defaultValue ?? ""));
336
+ const [error, setError] = useState3(null);
337
+ useInput3(
338
+ (input, key) => {
339
+ if (!isActive) return;
340
+ if (key.backspace || key.delete) {
341
+ const newValue = inputValue.slice(0, -1);
342
+ setInputValue(newValue);
343
+ const num = Number(newValue);
344
+ if (!isNaN(num) && newValue !== "") {
345
+ onChange(num);
346
+ setError(null);
347
+ } else if (newValue === "") {
348
+ setError(null);
349
+ } else {
350
+ setError("Enter a valid number");
351
+ }
352
+ } else if (/^[0-9.-]$/.test(input)) {
353
+ const newValue = inputValue + input;
354
+ setInputValue(newValue);
355
+ const num = Number(newValue);
356
+ if (!isNaN(num)) {
357
+ onChange(num);
358
+ setError(null);
359
+ } else {
360
+ setError("Enter a valid number");
361
+ }
362
+ }
363
+ },
364
+ { isActive }
365
+ );
366
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
367
+ /* @__PURE__ */ jsxs3(Box3, { children: [
368
+ /* @__PURE__ */ jsx3(Text3, { color: isActive ? "cyan" : void 0, children: isActive ? "\u203A " : " " }),
369
+ /* @__PURE__ */ jsxs3(Text3, { children: [
370
+ field.label,
371
+ ": "
372
+ ] }),
373
+ /* @__PURE__ */ jsx3(Text3, { color: isActive ? "cyan" : void 0, underline: isActive, children: inputValue || field.placeholder || "" }),
374
+ field.description && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
375
+ " (",
376
+ field.description,
377
+ ")"
378
+ ] })
379
+ ] }),
380
+ error && /* @__PURE__ */ jsx3(Box3, { paddingLeft: 4, children: /* @__PURE__ */ jsx3(Text3, { color: "red", children: error }) })
381
+ ] });
382
+ }
383
+ function TextField({
384
+ field,
385
+ value,
386
+ onChange,
387
+ isActive
388
+ }) {
389
+ const displayValue = Array.isArray(value) ? value.join(", ") : String(value ?? field.defaultValue ?? "");
390
+ const [inputValue, setInputValue] = useState3(displayValue);
391
+ useInput3(
392
+ (input, key) => {
393
+ if (!isActive) return;
394
+ if (key.backspace || key.delete) {
395
+ const newValue = inputValue.slice(0, -1);
396
+ setInputValue(newValue);
397
+ onChange(newValue);
398
+ } else if (input && !key.ctrl && !key.meta) {
399
+ const newValue = inputValue + input;
400
+ setInputValue(newValue);
401
+ onChange(newValue);
402
+ }
403
+ },
404
+ { isActive }
405
+ );
406
+ return /* @__PURE__ */ jsxs3(Box3, { children: [
407
+ /* @__PURE__ */ jsx3(Text3, { color: isActive ? "cyan" : void 0, children: isActive ? "\u203A " : " " }),
408
+ /* @__PURE__ */ jsxs3(Text3, { children: [
409
+ field.label,
410
+ ": "
411
+ ] }),
412
+ /* @__PURE__ */ jsx3(Text3, { color: isActive ? "cyan" : void 0, underline: isActive, children: inputValue || field.placeholder || "(empty)" }),
413
+ field.description && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
414
+ " (",
415
+ field.description,
416
+ ")"
417
+ ] })
418
+ ] });
419
+ }
420
+ function SelectField({
421
+ field,
422
+ value,
423
+ onChange,
424
+ isActive
425
+ }) {
426
+ const options = field.options ?? [];
427
+ const currentIndex = options.findIndex(
428
+ (opt) => String(opt.value) === String(value)
429
+ );
430
+ const selectedIndex = currentIndex >= 0 ? currentIndex : 0;
431
+ useInput3(
432
+ (input, key) => {
433
+ if (!isActive || options.length === 0) return;
434
+ if (input === " " || key.rightArrow) {
435
+ const newIndex = selectedIndex < options.length - 1 ? selectedIndex + 1 : 0;
436
+ onChange(options[newIndex].value);
437
+ } else if (key.leftArrow) {
438
+ const newIndex = selectedIndex > 0 ? selectedIndex - 1 : options.length - 1;
439
+ onChange(options[newIndex].value);
440
+ }
441
+ },
442
+ { isActive }
443
+ );
444
+ const selectedOption = options[selectedIndex];
445
+ return /* @__PURE__ */ jsxs3(Box3, { children: [
446
+ /* @__PURE__ */ jsx3(Text3, { color: isActive ? "cyan" : void 0, children: isActive ? "\u203A " : " " }),
447
+ /* @__PURE__ */ jsxs3(Text3, { children: [
448
+ field.label,
449
+ ": "
450
+ ] }),
451
+ /* @__PURE__ */ jsxs3(Text3, { color: isActive ? "cyan" : void 0, bold: isActive, children: [
452
+ "\u25C0 ",
453
+ selectedOption?.label ?? "(none)",
454
+ " \u25B6"
455
+ ] }),
456
+ field.description && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
457
+ " (",
458
+ field.description,
459
+ ")"
460
+ ] })
461
+ ] });
462
+ }
463
+ function MultiselectField({
464
+ field,
465
+ value,
466
+ onChange,
467
+ isActive
468
+ }) {
469
+ const options = field.options ?? [];
470
+ const selectedValues = new Set(
471
+ Array.isArray(value) ? value.map(String) : []
472
+ );
473
+ const [cursor, setCursor] = useState3(0);
474
+ useInput3(
475
+ (input, key) => {
476
+ if (!isActive || options.length === 0) return;
477
+ if (key.upArrow) {
478
+ setCursor((prev) => prev > 0 ? prev - 1 : options.length - 1);
479
+ } else if (key.downArrow) {
480
+ setCursor((prev) => prev < options.length - 1 ? prev + 1 : 0);
481
+ } else if (input === " ") {
482
+ const opt = options[cursor];
483
+ const newSelected = new Set(selectedValues);
484
+ if (newSelected.has(String(opt.value))) {
485
+ newSelected.delete(String(opt.value));
486
+ } else {
487
+ newSelected.add(String(opt.value));
488
+ }
489
+ onChange(Array.from(newSelected));
490
+ }
491
+ },
492
+ { isActive }
493
+ );
494
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
495
+ /* @__PURE__ */ jsxs3(Box3, { children: [
496
+ /* @__PURE__ */ jsx3(Text3, { color: isActive ? "cyan" : void 0, children: isActive ? "\u203A " : " " }),
497
+ /* @__PURE__ */ jsx3(Text3, { children: field.label }),
498
+ field.description && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
499
+ " - ",
500
+ field.description
501
+ ] })
502
+ ] }),
503
+ isActive && /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", paddingLeft: 4, children: options.map((opt, i) => {
504
+ const isSelected = selectedValues.has(String(opt.value));
505
+ const isCursor = i === cursor;
506
+ return /* @__PURE__ */ jsxs3(Box3, { children: [
507
+ /* @__PURE__ */ jsx3(Text3, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u203A " : " " }),
508
+ /* @__PURE__ */ jsx3(Text3, { color: isSelected ? "green" : void 0, children: isSelected ? "[\u2713]" : "[ ]" }),
509
+ /* @__PURE__ */ jsxs3(Text3, { color: isCursor ? "cyan" : void 0, children: [
510
+ " ",
511
+ opt.label
512
+ ] })
513
+ ] }, String(opt.value));
514
+ }) }),
515
+ !isActive && /* @__PURE__ */ jsx3(Box3, { paddingLeft: 4, children: /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
516
+ "Selected: ",
517
+ Array.from(selectedValues).length > 0 ? Array.from(selectedValues).join(", ") : "(none)"
518
+ ] }) })
519
+ ] });
520
+ }
521
+ function OptionField(props) {
522
+ const { field } = props;
523
+ switch (field.type) {
524
+ case "boolean":
525
+ return /* @__PURE__ */ jsx3(BooleanField, { ...props });
526
+ case "number":
527
+ return /* @__PURE__ */ jsx3(NumberField, { ...props });
528
+ case "text":
529
+ return /* @__PURE__ */ jsx3(TextField, { ...props });
530
+ case "select":
531
+ return /* @__PURE__ */ jsx3(SelectField, { ...props });
532
+ case "multiselect":
533
+ return /* @__PURE__ */ jsx3(MultiselectField, { ...props });
534
+ default:
535
+ return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
536
+ "Unknown field type: ",
537
+ field.type
538
+ ] }) });
539
+ }
540
+ }
541
+ function convertFieldValue(value, field, defaultValue) {
542
+ if (Array.isArray(defaultValue) && field.type === "text" && typeof value === "string") {
543
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
544
+ }
545
+ return value;
546
+ }
547
+
548
+ // src/commands/init/components/RuleSelector.tsx
549
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
550
+ function SeverityBadge({ severity }) {
551
+ if (severity === "error") {
552
+ return /* @__PURE__ */ jsx4(Text4, { color: "red", children: "error" });
553
+ }
554
+ if (severity === "warn") {
555
+ return /* @__PURE__ */ jsx4(Text4, { color: "yellow", children: "warn" });
556
+ }
557
+ return /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "off" });
558
+ }
559
+ function CategoryHeader({ name, icon }) {
560
+ return /* @__PURE__ */ jsx4(Box4, { marginTop: 1, marginBottom: 0, children: /* @__PURE__ */ jsxs4(Text4, { bold: true, color: "white", children: [
561
+ icon,
562
+ " ",
563
+ name
564
+ ] }) });
565
+ }
566
+ function RuleSelector({
567
+ onSubmit,
568
+ onBack,
569
+ onCancel
570
+ }) {
571
+ const { exit } = useApp3();
572
+ const staticRules = useMemo(() => getRulesByCategory("static"), []);
573
+ const semanticRules = useMemo(() => getRulesByCategory("semantic"), []);
574
+ const allRules = useMemo(() => [...staticRules, ...semanticRules], [staticRules, semanticRules]);
575
+ const [cursor, setCursor] = useState4(0);
576
+ const [viewMode, setViewMode] = useState4("list");
577
+ const [ruleStates, setRuleStates] = useState4(
578
+ () => {
579
+ const map = /* @__PURE__ */ new Map();
580
+ for (const rule of allRules) {
581
+ const categoryMeta = getCategoryMeta(rule.category);
582
+ const enabled = rule.defaultEnabled ?? categoryMeta?.defaultEnabled ?? false;
583
+ map.set(rule.id, {
584
+ enabled,
585
+ severity: rule.defaultSeverity === "off" ? "warn" : rule.defaultSeverity
586
+ });
587
+ }
588
+ return map;
589
+ }
590
+ );
591
+ const [customOptions, setCustomOptions] = useState4(/* @__PURE__ */ new Map());
592
+ const [editingRuleIndex, setEditingRuleIndex] = useState4(0);
593
+ const [editingFieldCursor, setEditingFieldCursor] = useState4(0);
594
+ const [confirmCursor, setConfirmCursor] = useState4(1);
595
+ const currentRule = allRules[cursor];
596
+ const enabledRulesWithOptions = useMemo(() => {
597
+ return allRules.filter((rule) => {
598
+ const state = ruleStates.get(rule.id);
599
+ return state?.enabled && rule.optionSchema && rule.optionSchema.fields.length > 0;
600
+ });
601
+ }, [allRules, ruleStates]);
602
+ const currentEditingRule = enabledRulesWithOptions[editingRuleIndex];
603
+ const currentEditingFields = currentEditingRule?.optionSchema?.fields ?? [];
604
+ const toggleRule = () => {
605
+ if (!currentRule) return;
606
+ setRuleStates((prev) => {
607
+ const next = new Map(prev);
608
+ const current = next.get(currentRule.id);
609
+ next.set(currentRule.id, { ...current, enabled: !current.enabled });
610
+ return next;
611
+ });
612
+ };
613
+ const cycleSeverity = () => {
614
+ if (!currentRule) return;
615
+ setRuleStates((prev) => {
616
+ const next = new Map(prev);
617
+ const current = next.get(currentRule.id);
618
+ const newSeverity = current.severity === "error" ? "warn" : "error";
619
+ next.set(currentRule.id, { ...current, severity: newSeverity });
620
+ return next;
621
+ });
622
+ };
623
+ const finalSubmit = () => {
624
+ const configuredRules = [];
625
+ for (const rule of allRules) {
626
+ const state = ruleStates.get(rule.id);
627
+ if (state?.enabled) {
628
+ const custom = customOptions.get(rule.id);
629
+ const baseOptions = rule.defaultOptions?.[0] ?? {};
630
+ configuredRules.push({
631
+ rule,
632
+ severity: state.severity,
633
+ options: custom ? [{ ...baseOptions, ...custom }] : rule.defaultOptions
634
+ });
635
+ }
636
+ }
637
+ onSubmit(configuredRules);
638
+ };
639
+ const handleListSubmit = () => {
640
+ if (enabledRulesWithOptions.length > 0) {
641
+ setViewMode("confirm-options");
642
+ setConfirmCursor(1);
643
+ } else {
644
+ finalSubmit();
645
+ }
646
+ };
647
+ const handleConfirmSelection = () => {
648
+ if (confirmCursor === 0) {
649
+ setEditingRuleIndex(0);
650
+ setEditingFieldCursor(0);
651
+ if (enabledRulesWithOptions[0]) {
652
+ initializeRuleOptions(enabledRulesWithOptions[0]);
653
+ }
654
+ setViewMode("edit-options");
655
+ } else {
656
+ finalSubmit();
657
+ }
658
+ };
659
+ const initializeRuleOptions = (rule) => {
660
+ if (!customOptions.has(rule.id) && rule.optionSchema) {
661
+ const baseOptions = rule.defaultOptions?.[0] ?? {};
662
+ const initial = {};
663
+ for (const field of rule.optionSchema.fields) {
664
+ initial[field.key] = baseOptions[field.key] ?? field.defaultValue;
665
+ }
666
+ setCustomOptions((prev) => {
667
+ const next = new Map(prev);
668
+ next.set(rule.id, initial);
669
+ return next;
670
+ });
671
+ }
672
+ };
673
+ const updateFieldValue = (fieldKey, value) => {
674
+ if (!currentEditingRule) return;
675
+ setCustomOptions((prev) => {
676
+ const next = new Map(prev);
677
+ const current = next.get(currentEditingRule.id) ?? {};
678
+ next.set(currentEditingRule.id, { ...current, [fieldKey]: value });
679
+ return next;
680
+ });
681
+ };
682
+ const nextEditingRule = () => {
683
+ if (editingRuleIndex < enabledRulesWithOptions.length - 1) {
684
+ const nextIndex = editingRuleIndex + 1;
685
+ setEditingRuleIndex(nextIndex);
686
+ setEditingFieldCursor(0);
687
+ if (enabledRulesWithOptions[nextIndex]) {
688
+ initializeRuleOptions(enabledRulesWithOptions[nextIndex]);
689
+ }
690
+ } else {
691
+ finalSubmit();
692
+ }
693
+ };
694
+ const skipCurrentRule = () => {
695
+ if (currentEditingRule) {
696
+ setCustomOptions((prev) => {
697
+ const next = new Map(prev);
698
+ next.delete(currentEditingRule.id);
699
+ return next;
700
+ });
701
+ }
702
+ nextEditingRule();
703
+ };
704
+ useInput4((input, key) => {
705
+ if (viewMode === "docs") {
706
+ if (key.escape || key.return || input === "d" || input === "q") {
707
+ setViewMode("list");
708
+ }
709
+ return;
710
+ }
711
+ if (viewMode === "confirm-options") {
712
+ if (key.upArrow || key.downArrow) {
713
+ setConfirmCursor((prev) => prev === 0 ? 1 : 0);
714
+ } else if (key.return || input === " ") {
715
+ handleConfirmSelection();
716
+ } else if (key.escape || input === "q") {
717
+ setViewMode("list");
718
+ }
719
+ return;
720
+ }
721
+ if (viewMode === "edit-options") {
722
+ if (key.upArrow) {
723
+ setEditingFieldCursor(
724
+ (prev) => prev > 0 ? prev - 1 : currentEditingFields.length - 1
725
+ );
726
+ } else if (key.downArrow) {
727
+ setEditingFieldCursor(
728
+ (prev) => prev < currentEditingFields.length - 1 ? prev + 1 : 0
729
+ );
730
+ } else if (key.return) {
731
+ nextEditingRule();
732
+ } else if (key.escape) {
733
+ skipCurrentRule();
734
+ } else if (input === "q") {
735
+ setViewMode("list");
736
+ }
737
+ return;
738
+ }
739
+ if (key.upArrow) {
740
+ setCursor((prev) => prev > 0 ? prev - 1 : allRules.length - 1);
741
+ } else if (key.downArrow) {
742
+ setCursor((prev) => prev < allRules.length - 1 ? prev + 1 : 0);
743
+ } else if (input === " ") {
744
+ toggleRule();
745
+ } else if (input === "s") {
746
+ cycleSeverity();
747
+ } else if (input === "d") {
748
+ setViewMode("docs");
749
+ } else if (key.return) {
750
+ handleListSubmit();
751
+ } else if (key.escape || input === "q") {
752
+ onCancel?.();
753
+ exit();
754
+ } else if ((input === "b" || key.leftArrow) && onBack) {
755
+ onBack();
756
+ } else if (input === "a") {
757
+ setRuleStates((prev) => {
758
+ const next = new Map(prev);
759
+ for (const rule of allRules) {
760
+ const current = next.get(rule.id);
761
+ next.set(rule.id, { ...current, enabled: true });
762
+ }
763
+ return next;
764
+ });
765
+ } else if (input === "n") {
766
+ setRuleStates((prev) => {
767
+ const next = new Map(prev);
768
+ for (const rule of allRules) {
769
+ const current = next.get(rule.id);
770
+ next.set(rule.id, { ...current, enabled: false });
771
+ }
772
+ return next;
773
+ });
774
+ }
775
+ });
776
+ if (viewMode === "docs" && currentRule) {
777
+ const docLines = currentRule.docs.trim().split("\n").slice(0, 20);
778
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
779
+ /* @__PURE__ */ jsxs4(Box4, { marginBottom: 1, children: [
780
+ /* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: currentRule.name }),
781
+ /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
782
+ " - ",
783
+ currentRule.description
784
+ ] })
785
+ ] }),
786
+ /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, children: [
787
+ docLines.map((line, i) => /* @__PURE__ */ jsx4(Text4, { dimColor: line.startsWith("#"), children: line }, i)),
788
+ currentRule.docs.split("\n").length > 20 && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "... (truncated)" })
789
+ ] }),
790
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Press any key to return to list" }) })
791
+ ] });
792
+ }
793
+ if (viewMode === "confirm-options") {
794
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
795
+ /* @__PURE__ */ jsx4(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: "Configure Rule Options" }) }),
796
+ /* @__PURE__ */ jsx4(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsxs4(Text4, { children: [
797
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: enabledRulesWithOptions.length }),
798
+ /* @__PURE__ */ jsx4(Text4, { children: " selected rules have configurable options. Would you like to customize them?" })
799
+ ] }) }),
800
+ /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingLeft: 2, children: [
801
+ /* @__PURE__ */ jsxs4(Box4, { children: [
802
+ /* @__PURE__ */ jsx4(Text4, { color: confirmCursor === 0 ? "cyan" : void 0, children: confirmCursor === 0 ? "\u203A " : " " }),
803
+ /* @__PURE__ */ jsx4(Text4, { color: confirmCursor === 0 ? "cyan" : void 0, bold: confirmCursor === 0, children: "Yes" }),
804
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " - Configure each rule's options" })
805
+ ] }),
806
+ /* @__PURE__ */ jsxs4(Box4, { children: [
807
+ /* @__PURE__ */ jsx4(Text4, { color: confirmCursor === 1 ? "cyan" : void 0, children: confirmCursor === 1 ? "\u203A " : " " }),
808
+ /* @__PURE__ */ jsx4(Text4, { color: confirmCursor === 1 ? "cyan" : void 0, bold: confirmCursor === 1, children: "No" }),
809
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " - Use defaults for all rules" })
810
+ ] })
811
+ ] }),
812
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
813
+ "Rules with options:",
814
+ " ",
815
+ enabledRulesWithOptions.map((r) => r.name).join(", ")
816
+ ] }) }),
817
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
818
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "\u2191\u2193" }),
819
+ " select",
820
+ " ",
821
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "enter" }),
822
+ " confirm",
823
+ " ",
824
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "esc" }),
825
+ " back"
826
+ ] }) })
827
+ ] });
828
+ }
829
+ if (viewMode === "edit-options" && currentEditingRule) {
830
+ const ruleOptions = customOptions.get(currentEditingRule.id) ?? {};
831
+ const baseOptions = currentEditingRule.defaultOptions?.[0] ?? {};
832
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
833
+ /* @__PURE__ */ jsxs4(Box4, { marginBottom: 1, children: [
834
+ /* @__PURE__ */ jsxs4(Text4, { bold: true, color: "cyan", children: [
835
+ currentEditingRule.icon ?? "\u2699\uFE0F",
836
+ " ",
837
+ currentEditingRule.name
838
+ ] }),
839
+ /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
840
+ " (",
841
+ editingRuleIndex + 1,
842
+ "/",
843
+ enabledRulesWithOptions.length,
844
+ ")"
845
+ ] })
846
+ ] }),
847
+ /* @__PURE__ */ jsx4(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: currentEditingRule.description }) }),
848
+ /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", children: currentEditingFields.map((field, i) => {
849
+ const currentValue = ruleOptions[field.key] ?? baseOptions[field.key] ?? field.defaultValue;
850
+ const defaultValue = baseOptions[field.key] ?? field.defaultValue;
851
+ return /* @__PURE__ */ jsx4(
852
+ OptionField,
853
+ {
854
+ field,
855
+ value: currentValue,
856
+ onChange: (newValue) => {
857
+ const converted = convertFieldValue(newValue, field, defaultValue);
858
+ updateFieldValue(field.key, converted);
859
+ },
860
+ isActive: i === editingFieldCursor
861
+ },
862
+ field.key
863
+ );
864
+ }) }),
865
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
866
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "\u2191\u2193" }),
867
+ " navigate",
868
+ " ",
869
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "space" }),
870
+ " toggle",
871
+ " ",
872
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "enter" }),
873
+ " next rule",
874
+ " ",
875
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "esc" }),
876
+ " skip (use defaults)"
877
+ ] }) })
878
+ ] });
879
+ }
880
+ const enabledCount = Array.from(ruleStates.values()).filter((s) => s.enabled).length;
881
+ const errorCount = Array.from(ruleStates.entries()).filter(
882
+ ([, s]) => s.enabled && s.severity === "error"
883
+ ).length;
884
+ const warnCount = enabledCount - errorCount;
885
+ let globalIndex = 0;
886
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
887
+ /* @__PURE__ */ jsx4(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: "Configure ESLint Rules" }) }),
888
+ (() => {
889
+ const cat = getCategoryMeta("static");
890
+ return cat ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
891
+ /* @__PURE__ */ jsx4(CategoryHeader, { name: cat.name, icon: cat.icon }),
892
+ /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
893
+ " ",
894
+ cat.description
895
+ ] })
896
+ ] }) : null;
897
+ })(),
898
+ staticRules.map((rule) => {
899
+ const itemIndex = globalIndex++;
900
+ const isCursor = itemIndex === cursor;
901
+ const state = ruleStates.get(rule.id);
902
+ const hasOptions = rule.optionSchema && rule.optionSchema.fields.length > 0;
903
+ return /* @__PURE__ */ jsxs4(Box4, { paddingLeft: 2, children: [
904
+ /* @__PURE__ */ jsx4(Text4, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u203A " : " " }),
905
+ /* @__PURE__ */ jsx4(Box4, { width: 3, children: /* @__PURE__ */ jsx4(Text4, { color: state.enabled ? "green" : void 0, dimColor: !state.enabled, children: state.enabled ? "\u2713" : "\u25CB" }) }),
906
+ /* @__PURE__ */ jsx4(Box4, { width: 30, children: /* @__PURE__ */ jsx4(
907
+ Text4,
908
+ {
909
+ color: isCursor ? "cyan" : void 0,
910
+ dimColor: !state.enabled,
911
+ bold: isCursor,
912
+ children: rule.name
913
+ }
914
+ ) }),
915
+ /* @__PURE__ */ jsx4(Box4, { width: 8, children: state.enabled ? /* @__PURE__ */ jsx4(SeverityBadge, { severity: state.severity }) : /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "-" }) }),
916
+ hasOptions && state.enabled && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " \u2699" })
917
+ ] }, rule.id);
918
+ }),
919
+ (() => {
920
+ const cat = getCategoryMeta("semantic");
921
+ return cat ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
922
+ /* @__PURE__ */ jsx4(CategoryHeader, { name: cat.name, icon: cat.icon }),
923
+ /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
924
+ " ",
925
+ cat.description
926
+ ] })
927
+ ] }) : null;
928
+ })(),
929
+ semanticRules.map((rule) => {
930
+ const itemIndex = globalIndex++;
931
+ const isCursor = itemIndex === cursor;
932
+ const state = ruleStates.get(rule.id);
933
+ const hasOptions = rule.optionSchema && rule.optionSchema.fields.length > 0;
934
+ return /* @__PURE__ */ jsxs4(Box4, { paddingLeft: 2, children: [
935
+ /* @__PURE__ */ jsx4(Text4, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u203A " : " " }),
936
+ /* @__PURE__ */ jsx4(Box4, { width: 3, children: /* @__PURE__ */ jsx4(Text4, { color: state.enabled ? "green" : void 0, dimColor: !state.enabled, children: state.enabled ? "\u2713" : "\u25CB" }) }),
937
+ /* @__PURE__ */ jsx4(Box4, { width: 30, children: /* @__PURE__ */ jsx4(
938
+ Text4,
939
+ {
940
+ color: isCursor ? "cyan" : void 0,
941
+ dimColor: !state.enabled,
942
+ bold: isCursor,
943
+ children: rule.name
944
+ }
945
+ ) }),
946
+ /* @__PURE__ */ jsx4(Box4, { width: 8, children: state.enabled ? /* @__PURE__ */ jsx4(SeverityBadge, { severity: state.severity }) : /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "-" }) }),
947
+ hasOptions && state.enabled && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " \u2699" })
948
+ ] }, rule.id);
949
+ }),
950
+ currentRule && /* @__PURE__ */ jsx4(Box4, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: currentRule.description }) }),
951
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
952
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "\u2191\u2193" }),
953
+ " navigate",
954
+ " ",
955
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "space" }),
956
+ " toggle",
957
+ " ",
958
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "s" }),
959
+ " severity",
960
+ " ",
961
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "d" }),
962
+ " docs",
963
+ " ",
964
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "a" }),
965
+ " all",
966
+ " ",
967
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "n" }),
968
+ " none",
969
+ " ",
970
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "enter" }),
971
+ " confirm"
972
+ ] }) }),
973
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsxs4(Text4, { children: [
974
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: enabledCount }),
975
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " rules enabled (" }),
976
+ /* @__PURE__ */ jsx4(Text4, { color: "red", children: errorCount }),
977
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " errors, " }),
978
+ /* @__PURE__ */ jsx4(Text4, { color: "yellow", children: warnCount }),
979
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " warnings)" }),
980
+ enabledRulesWithOptions.length > 0 && /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
981
+ ", ",
982
+ enabledRulesWithOptions.length,
983
+ " with options \u2699"
984
+ ] })
985
+ ] }) })
986
+ ] });
987
+ }
988
+
989
+ // src/commands/init/components/InjectionPointSelector.tsx
990
+ import { useState as useState5 } from "react";
991
+ import { Box as Box5, Text as Text5, useInput as useInput5, useApp as useApp4 } from "ink";
992
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
993
+ function InjectionPointSelector({
994
+ points,
995
+ onSubmit,
996
+ onBack,
997
+ onCancel
998
+ }) {
999
+ const { exit } = useApp4();
1000
+ const recommendedIndex = points.findIndex((p) => p.recommended);
1001
+ const [cursor, setCursor] = useState5(recommendedIndex >= 0 ? recommendedIndex : 0);
1002
+ useInput5((input, key) => {
1003
+ if (key.upArrow) {
1004
+ setCursor((prev) => prev > 0 ? prev - 1 : points.length - 1);
1005
+ } else if (key.downArrow) {
1006
+ setCursor((prev) => prev < points.length - 1 ? prev + 1 : 0);
1007
+ } else if (key.return) {
1008
+ const selected = points[cursor];
1009
+ if (selected) {
1010
+ onSubmit(selected);
1011
+ }
1012
+ } else if (input === "b" || key.leftArrow) {
1013
+ onBack?.();
1014
+ } else if (input === "q" || key.escape) {
1015
+ onCancel?.();
1016
+ exit();
1017
+ }
1018
+ });
1019
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
1020
+ /* @__PURE__ */ jsx5(Box5, { marginBottom: 1, children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: "Where should the devtools component be injected?" }) }),
1021
+ points.map((point, index) => {
1022
+ const isCursor = index === cursor;
1023
+ return /* @__PURE__ */ jsxs5(Box5, { paddingLeft: 1, children: [
1024
+ /* @__PURE__ */ jsx5(Text5, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u203A " : " " }),
1025
+ /* @__PURE__ */ jsx5(Box5, { width: 2, children: /* @__PURE__ */ jsx5(Text5, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u25C9" : "\u25CB" }) }),
1026
+ /* @__PURE__ */ jsxs5(Box5, { children: [
1027
+ /* @__PURE__ */ jsx5(Text5, { color: isCursor ? "cyan" : void 0, children: point.label }),
1028
+ point.hint && /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
1029
+ " (",
1030
+ point.hint,
1031
+ ")"
1032
+ ] })
1033
+ ] })
1034
+ ] }, point.id);
1035
+ }),
1036
+ /* @__PURE__ */ jsx5(Box5, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
1037
+ /* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "\u2191\u2193" }),
1038
+ " navigate",
1039
+ " ",
1040
+ /* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "enter" }),
1041
+ " select",
1042
+ " ",
1043
+ /* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "b" }),
1044
+ " back",
1045
+ " ",
1046
+ /* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "q" }),
1047
+ " quit"
1048
+ ] }) })
1049
+ ] });
1050
+ }
1051
+
1052
+ // src/commands/init/components/InstallApp.tsx
1053
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1054
+ function getTargetStatus(target) {
1055
+ if (!target.isInstalled) {
1056
+ return "not_installed";
1057
+ }
1058
+ if (target.canUpgrade) {
1059
+ return "upgradeable";
1060
+ }
1061
+ return "installed";
1062
+ }
1063
+ function buildConfigItemsForProject(project, selectedProject, selections) {
1064
+ const items = [];
1065
+ for (const selection of selections) {
1066
+ const { installer, targets } = selection;
1067
+ const relevantTargets = targets.filter((target) => {
1068
+ if (installer.id === "next" || installer.id === "vite") {
1069
+ return target.path === selectedProject.path;
1070
+ }
1071
+ if (installer.id === "eslint") {
1072
+ return target.path === selectedProject.path;
1073
+ }
1074
+ return true;
1075
+ });
1076
+ if (relevantTargets.length === 0) continue;
1077
+ const displayInfo = {
1078
+ next: { category: "UI Analysis", icon: "\u{1F50D}" },
1079
+ vite: { category: "UI Analysis", icon: "\u{1F50D}" },
1080
+ eslint: { category: "ESLint Rules", icon: "\u{1F4CB}" },
1081
+ genstyleguide: { category: "Cursor Integration", icon: "\u{1F4DD}" },
1082
+ skill: { category: "Cursor Integration", icon: "\u26A1" }
1083
+ };
1084
+ const info = displayInfo[installer.id] || { category: "Other", icon: "\u2022" };
1085
+ for (const target of relevantTargets) {
1086
+ items.push({
1087
+ id: `${installer.id}:${target.id}`,
1088
+ label: installer.name,
1089
+ hint: target.hint,
1090
+ status: getTargetStatus(target),
1091
+ category: info.category,
1092
+ categoryIcon: info.icon
1093
+ });
1094
+ }
1095
+ }
1096
+ return items;
1097
+ }
1098
+ function buildGlobalConfigItems(selections) {
1099
+ const items = [];
1100
+ for (const selection of selections) {
1101
+ const { installer, targets } = selection;
1102
+ if (installer.id !== "genstyleguide" && installer.id !== "skill") {
1103
+ continue;
1104
+ }
1105
+ const displayInfo = {
1106
+ genstyleguide: { category: "Cursor Integration", icon: "\u{1F4DD}" },
1107
+ skill: { category: "Cursor Integration", icon: "\u26A1" }
1108
+ };
1109
+ const info = displayInfo[installer.id] || { category: "Other", icon: "\u2022" };
1110
+ for (const target of targets) {
1111
+ items.push({
1112
+ id: `${installer.id}:${target.id}`,
1113
+ label: installer.name,
1114
+ hint: target.hint,
1115
+ status: getTargetStatus(target),
1116
+ category: info.category,
1117
+ categoryIcon: info.icon
1118
+ });
1119
+ }
1120
+ }
1121
+ return items;
1122
+ }
1123
+ var MIN_NODE_MAJOR = 20;
1124
+ var MIN_NODE_MINOR = 19;
1125
+ function checkNodeVersion() {
1126
+ const ver = process.versions.node || "";
1127
+ const parts = ver.split(".");
1128
+ const major = Number.parseInt(parts[0] || "", 10);
1129
+ const minor = Number.parseInt(parts[1] || "", 10);
1130
+ const meetsRequirement = Number.isFinite(major) && Number.isFinite(minor) && (major > MIN_NODE_MAJOR || major === MIN_NODE_MAJOR && minor >= MIN_NODE_MINOR);
1131
+ return {
1132
+ ok: meetsRequirement,
1133
+ current: ver,
1134
+ required: `${MIN_NODE_MAJOR}.${MIN_NODE_MINOR}.0`
1135
+ };
1136
+ }
1137
+ function Header({ subtitle }) {
1138
+ return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs6(Box6, { children: [
1139
+ /* @__PURE__ */ jsx6(Text6, { bold: true, color: "cyan", children: "\u25C6 UILint" }),
1140
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " v0.5.0" }),
1141
+ subtitle && /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1142
+ " \xB7 ",
1143
+ subtitle
1144
+ ] })
1145
+ ] }) });
1146
+ }
1147
+ function FeatureConfig({
1148
+ selectedProject,
1149
+ configItems,
1150
+ canGoBack,
1151
+ onSubmit,
1152
+ onBack,
1153
+ onCancel
1154
+ }) {
1155
+ useInput6((input, key) => {
1156
+ if ((input === "b" || key.leftArrow) && canGoBack) {
1157
+ onBack();
1158
+ }
1159
+ });
1160
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1161
+ selectedProject && /* @__PURE__ */ jsxs6(Box6, { marginBottom: 1, children: [
1162
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Project: " }),
1163
+ /* @__PURE__ */ jsx6(Text6, { bold: true, color: "cyan", children: selectedProject.name }),
1164
+ /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1165
+ " (",
1166
+ selectedProject.hint,
1167
+ ")"
1168
+ ] })
1169
+ ] }),
1170
+ /* @__PURE__ */ jsx6(
1171
+ ConfigSelector,
1172
+ {
1173
+ items: configItems,
1174
+ onSubmit,
1175
+ onCancel
1176
+ }
1177
+ ),
1178
+ canGoBack && /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1179
+ "Press ",
1180
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "b" }),
1181
+ " or ",
1182
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u2190" }),
1183
+ " to select a different project"
1184
+ ] }) })
1185
+ ] });
1186
+ }
1187
+ function InstallApp({
1188
+ projectPromise,
1189
+ onComplete,
1190
+ onError
1191
+ }) {
1192
+ const { exit } = useApp5();
1193
+ const [phase, setPhase] = useState6("checking-requirements");
1194
+ const [nodeVersionCheck, setNodeVersionCheck] = useState6(null);
1195
+ const [project, setProject] = useState6(null);
1196
+ const [detectedProjects, setDetectedProjects] = useState6([]);
1197
+ const [selectedProject, setSelectedProject] = useState6(null);
1198
+ const [selections, setSelections] = useState6([]);
1199
+ const [configItems, setConfigItems] = useState6([]);
1200
+ const [selectedFeatureIds, setSelectedFeatureIds] = useState6([]);
1201
+ const [removeFeatureIds, setRemoveFeatureIds] = useState6([]);
1202
+ const [error, setError] = useState6(null);
1203
+ const [injectionPoints, setInjectionPoints] = useState6([]);
1204
+ const [selectedInjectionPoint, setSelectedInjectionPoint] = useState6(void 0);
1205
+ const isEslintSelected = selectedFeatureIds.some((id) => id.startsWith("eslint:"));
1206
+ const isNextSelected = selectedFeatureIds.some((id) => id.startsWith("next:"));
1207
+ useEffect(() => {
1208
+ if (phase !== "checking-requirements") return;
1209
+ const timer = setTimeout(() => {
1210
+ const result = checkNodeVersion();
1211
+ setNodeVersionCheck(result);
1212
+ if (result.ok) {
1213
+ setPhase("scanning");
1214
+ } else {
1215
+ setError(
1216
+ new Error(
1217
+ `Node.js v${result.required}+ is required. You are running v${result.current}.`
1218
+ )
1219
+ );
1220
+ setPhase("error");
1221
+ }
1222
+ }, 300);
1223
+ return () => clearTimeout(timer);
1224
+ }, [phase]);
1225
+ useEffect(() => {
1226
+ if (phase !== "scanning") return;
1227
+ projectPromise.then((proj) => {
1228
+ setProject(proj);
1229
+ const projects = getDetectedProjects(proj);
1230
+ setDetectedProjects(projects);
1231
+ const installers = getAllInstallers();
1232
+ const initialSelections = installers.filter((installer) => installer.isApplicable(proj)).map((installer) => {
1233
+ const targets = installer.getTargets(proj);
1234
+ const actionableTargets = targets.filter(
1235
+ (t) => !t.isInstalled || t.canUpgrade
1236
+ );
1237
+ return {
1238
+ installer,
1239
+ targets,
1240
+ selected: actionableTargets.length > 0
1241
+ };
1242
+ });
1243
+ setSelections(initialSelections);
1244
+ if (projects.length === 1) {
1245
+ const singleProject = projects[0];
1246
+ setSelectedProject(singleProject);
1247
+ const items = buildConfigItemsForProject(proj, singleProject, initialSelections);
1248
+ setConfigItems(items);
1249
+ setPhase("configure-features");
1250
+ } else if (projects.length === 0) {
1251
+ setPhase("configure-features");
1252
+ const items = buildGlobalConfigItems(initialSelections);
1253
+ setConfigItems(items);
1254
+ } else {
1255
+ setPhase("select-project");
1256
+ }
1257
+ }).catch((err) => {
1258
+ setError(err);
1259
+ setPhase("error");
1260
+ onError?.(err);
1261
+ });
1262
+ }, [phase, projectPromise, onError]);
1263
+ const handleProjectSelect = (selected) => {
1264
+ setSelectedProject(selected);
1265
+ if (project) {
1266
+ const items = buildConfigItemsForProject(project, selected, selections);
1267
+ setConfigItems(items);
1268
+ }
1269
+ setPhase("configure-features");
1270
+ };
1271
+ const handleBackToProject = () => {
1272
+ if (detectedProjects.length > 1) {
1273
+ setSelectedProject(null);
1274
+ setPhase("select-project");
1275
+ }
1276
+ };
1277
+ const handleFeatureSubmit = (selectedIds, removeIds) => {
1278
+ setSelectedFeatureIds(selectedIds);
1279
+ setRemoveFeatureIds(removeIds);
1280
+ const nextSelected = selectedIds.some((id) => id.startsWith("next:"));
1281
+ if (nextSelected && project && selectedProject) {
1282
+ const appInfo = project.nextApps.find(
1283
+ (app) => app.projectPath === selectedProject.path
1284
+ );
1285
+ if (appInfo) {
1286
+ const points = getInjectionPoints(appInfo.projectPath, appInfo.detection.appRoot);
1287
+ if (points.length === 1) {
1288
+ const point = points[0];
1289
+ setSelectedInjectionPoint({
1290
+ targetFile: point.filePath,
1291
+ createProviders: point.createProviders
1292
+ });
1293
+ proceedAfterInjectionPoint(selectedIds, {
1294
+ targetFile: point.filePath,
1295
+ createProviders: point.createProviders
1296
+ });
1297
+ return;
1298
+ }
1299
+ if (points.length > 1) {
1300
+ setInjectionPoints(points);
1301
+ setPhase("configure-injection-point");
1302
+ return;
1303
+ }
1304
+ }
1305
+ }
1306
+ proceedAfterInjectionPoint(selectedIds, void 0);
1307
+ };
1308
+ const proceedAfterInjectionPoint = (selectedIds, injectionConfig) => {
1309
+ const eslintSelected = selectedIds.some((id) => id.startsWith("eslint:"));
1310
+ if (eslintSelected) {
1311
+ setPhase("configure-eslint");
1312
+ } else {
1313
+ finishInstallation(selectedIds, void 0, injectionConfig);
1314
+ }
1315
+ };
1316
+ const handleInjectionPointSubmit = (point) => {
1317
+ const config = {
1318
+ targetFile: point.filePath,
1319
+ createProviders: point.createProviders
1320
+ };
1321
+ setSelectedInjectionPoint(config);
1322
+ proceedAfterInjectionPoint(selectedFeatureIds, config);
1323
+ };
1324
+ const handleBackFromInjectionPoint = () => {
1325
+ setPhase("configure-features");
1326
+ };
1327
+ const handleRuleSubmit = (configuredRules) => {
1328
+ finishInstallation(selectedFeatureIds, configuredRules, selectedInjectionPoint);
1329
+ };
1330
+ const handleBackToFeatures = () => {
1331
+ setPhase("configure-features");
1332
+ };
1333
+ const finishInstallation = (selectedIds, eslintRules, injectionConfig) => {
1334
+ const selectedSet = new Set(selectedIds);
1335
+ const removeSet = new Set(removeFeatureIds);
1336
+ const updatedSelections = selections.map((sel) => {
1337
+ const selectedTargets = sel.targets.filter(
1338
+ (t) => selectedSet.has(`${sel.installer.id}:${t.id}`)
1339
+ );
1340
+ return {
1341
+ ...sel,
1342
+ targets: selectedTargets,
1343
+ selected: selectedTargets.length > 0
1344
+ };
1345
+ });
1346
+ const removeSelections = selections.map((sel) => {
1347
+ const removeTargets = sel.targets.filter(
1348
+ (t) => removeSet.has(`${sel.installer.id}:${t.id}`)
1349
+ );
1350
+ return {
1351
+ ...sel,
1352
+ targets: removeTargets,
1353
+ selected: removeTargets.length > 0
1354
+ };
1355
+ }).filter((sel) => sel.selected);
1356
+ setSelections(updatedSelections);
1357
+ onComplete(updatedSelections, eslintRules, injectionConfig, removeSelections.length > 0 ? removeSelections : void 0);
1358
+ };
1359
+ const handleCancel = () => {
1360
+ exit();
1361
+ };
1362
+ if (phase === "checking-requirements") {
1363
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1364
+ /* @__PURE__ */ jsx6(Header, { subtitle: "Install" }),
1365
+ /* @__PURE__ */ jsxs6(Box6, { children: [
1366
+ /* @__PURE__ */ jsx6(Spinner, {}),
1367
+ /* @__PURE__ */ jsx6(Text6, { children: " Checking requirements..." })
1368
+ ] })
1369
+ ] });
1370
+ }
1371
+ if (phase === "scanning") {
1372
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1373
+ /* @__PURE__ */ jsx6(Header, { subtitle: "Install" }),
1374
+ /* @__PURE__ */ jsxs6(Box6, { children: [
1375
+ /* @__PURE__ */ jsx6(Spinner, {}),
1376
+ /* @__PURE__ */ jsx6(Text6, { children: " Scanning project..." })
1377
+ ] })
1378
+ ] });
1379
+ }
1380
+ if (phase === "error") {
1381
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1382
+ /* @__PURE__ */ jsx6(Header, {}),
1383
+ /* @__PURE__ */ jsxs6(Box6, { children: [
1384
+ /* @__PURE__ */ jsx6(Text6, { color: "red", children: "\u2717 " }),
1385
+ /* @__PURE__ */ jsx6(Text6, { color: "red", children: error?.message || "An unknown error occurred" })
1386
+ ] })
1387
+ ] });
1388
+ }
1389
+ if (phase === "select-project") {
1390
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1391
+ /* @__PURE__ */ jsx6(Header, { subtitle: "Install" }),
1392
+ /* @__PURE__ */ jsx6(
1393
+ ProjectSelector,
1394
+ {
1395
+ projects: detectedProjects,
1396
+ onSelect: handleProjectSelect,
1397
+ onCancel: handleCancel
1398
+ }
1399
+ )
1400
+ ] });
1401
+ }
1402
+ if (phase === "configure-features" && project && configItems.length > 0) {
1403
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1404
+ /* @__PURE__ */ jsx6(Header, { subtitle: "Features" }),
1405
+ /* @__PURE__ */ jsx6(
1406
+ FeatureConfig,
1407
+ {
1408
+ selectedProject,
1409
+ configItems,
1410
+ canGoBack: detectedProjects.length > 1,
1411
+ onSubmit: handleFeatureSubmit,
1412
+ onBack: handleBackToProject,
1413
+ onCancel: handleCancel
1414
+ }
1415
+ )
1416
+ ] });
1417
+ }
1418
+ if (phase === "configure-injection-point" && injectionPoints.length > 0) {
1419
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1420
+ /* @__PURE__ */ jsx6(Header, { subtitle: "Injection Point" }),
1421
+ selectedProject && /* @__PURE__ */ jsxs6(Box6, { marginBottom: 1, children: [
1422
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Project: " }),
1423
+ /* @__PURE__ */ jsx6(Text6, { bold: true, color: "cyan", children: selectedProject.name })
1424
+ ] }),
1425
+ /* @__PURE__ */ jsx6(
1426
+ InjectionPointSelector,
1427
+ {
1428
+ points: injectionPoints,
1429
+ onSubmit: handleInjectionPointSubmit,
1430
+ onBack: handleBackFromInjectionPoint,
1431
+ onCancel: handleCancel
1432
+ }
1433
+ )
1434
+ ] });
1435
+ }
1436
+ if (phase === "configure-eslint") {
1437
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1438
+ /* @__PURE__ */ jsx6(Header, { subtitle: "ESLint Rules" }),
1439
+ selectedProject && /* @__PURE__ */ jsxs6(Box6, { marginBottom: 1, children: [
1440
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Project: " }),
1441
+ /* @__PURE__ */ jsx6(Text6, { bold: true, color: "cyan", children: selectedProject.name })
1442
+ ] }),
1443
+ /* @__PURE__ */ jsx6(
1444
+ RuleSelector,
1445
+ {
1446
+ onSubmit: handleRuleSubmit,
1447
+ onBack: handleBackToFeatures,
1448
+ onCancel: handleCancel
1449
+ }
1450
+ )
1451
+ ] });
1452
+ }
1453
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1454
+ /* @__PURE__ */ jsx6(Header, {}),
1455
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Loading..." })
1456
+ ] });
1457
+ }
1458
+
1459
+ // src/commands/init-ui.tsx
1460
+ import { ruleRegistry } from "uilint-eslint";
1461
+ import { jsx as jsx7 } from "react/jsx-runtime";
1462
+ function limitList(items, max) {
1463
+ if (items.length <= max) return items;
1464
+ return [...items.slice(0, max), pc.dim(`\u2026and ${items.length - max} more`)];
1465
+ }
1466
+ function printInstallReport(result, testCoverageResult) {
1467
+ const failedDeps = result.dependencyResults.filter((r) => !r.success);
1468
+ const okDeps = result.dependencyResults.filter((r) => r.success);
1469
+ const failedActions = result.actionsPerformed.filter((r) => !r.success);
1470
+ const okActions = result.actionsPerformed.filter((r) => r.success);
1471
+ if (result.success) {
1472
+ console.log(`
1473
+ ${pc.green("\u2713")} Operation completed successfully`);
1474
+ } else {
1475
+ console.log(`
1476
+ ${pc.yellow("\u26A0")} Operation completed with errors`);
1477
+ }
1478
+ const { summary } = result;
1479
+ const installed = summary.installedItems.map((x) => String(x));
1480
+ const created = summary.filesCreated;
1481
+ const modified = summary.filesModified;
1482
+ const deleted = summary.filesDeleted;
1483
+ if (installed.length > 0) {
1484
+ console.log(`
1485
+ ${pc.bold("Installed:")}`);
1486
+ for (const item of installed) console.log(`- ${pc.green("\u2713")} ${item}`);
1487
+ }
1488
+ if (summary.eslintTargets.length > 0) {
1489
+ console.log(`
1490
+ ${pc.bold("ESLint configured:")}`);
1491
+ for (const t of summary.eslintTargets) {
1492
+ console.log(
1493
+ `- ${pc.green("\u2713")} ${t.displayName} ${pc.dim(`(${t.configFile})`)}`
1494
+ );
1495
+ }
1496
+ }
1497
+ if (created.length + modified.length + deleted.length > 0) {
1498
+ console.log(`
1499
+ ${pc.bold("Files:")}`);
1500
+ for (const p of limitList(created, 20))
1501
+ console.log(`- ${pc.green("+")} ${p}`);
1502
+ for (const p of limitList(modified, 20))
1503
+ console.log(`- ${pc.yellow("~")} ${p}`);
1504
+ for (const p of limitList(deleted, 20))
1505
+ console.log(`- ${pc.red("-")} ${p}`);
1506
+ }
1507
+ if (summary.dependenciesInstalled.length > 0) {
1508
+ console.log(`
1509
+ ${pc.bold("Dependencies installed:")}`);
1510
+ for (const d of summary.dependenciesInstalled) {
1511
+ console.log(
1512
+ `- ${pc.green("\u2713")} ${d.packagePath} ${pc.dim(`\u2190 ${d.packages.join(", ")}`)}`
1513
+ );
1514
+ }
1515
+ }
1516
+ if (testCoverageResult?.ran) {
1517
+ console.log(`
1518
+ ${pc.bold("Test coverage:")}`);
1519
+ if (testCoverageResult.success) {
1520
+ console.log(`- ${pc.green("\u2713")} Coverage data generated successfully`);
1521
+ } else {
1522
+ console.log(`- ${pc.yellow("\u26A0")} Tests ran with errors`);
1523
+ if (testCoverageResult.error) {
1524
+ console.log(pc.dim(` ${testCoverageResult.error.split("\n")[0]}`));
1525
+ }
1526
+ }
1527
+ }
1528
+ if (failedDeps.length > 0 || failedActions.length > 0) {
1529
+ console.log(`
1530
+ ${pc.bold(pc.red("Failures:"))}`);
1531
+ }
1532
+ if (failedDeps.length > 0) {
1533
+ console.log(`
1534
+ ${pc.bold("Dependency installs failed:")}`);
1535
+ for (const dep of failedDeps) {
1536
+ const pkgs = dep.install.packages.join(", ");
1537
+ console.log(
1538
+ `- ${pc.red("\u2717")} ${dep.install.packageManager} in ${dep.install.packagePath} ${pc.dim(`\u2190 ${pkgs}`)}`
1539
+ );
1540
+ if (dep.error) console.log(pc.dim(dep.error.split("\n").slice(0, 30).join("\n")));
1541
+ }
1542
+ }
1543
+ if (failedActions.length > 0) {
1544
+ console.log(`
1545
+ ${pc.bold("Actions failed:")}`);
1546
+ for (const a of failedActions) {
1547
+ const action = a.action;
1548
+ const type = String(action.type || "unknown");
1549
+ const pathish = typeof action.path === "string" && action.path || typeof action.projectPath === "string" && action.projectPath || typeof action.packagePath === "string" && action.packagePath || "";
1550
+ console.error(`- ${type}${pathish ? ` (${pathish})` : ""}`);
1551
+ if (a.error) console.error(` ${a.error}`);
1552
+ }
1553
+ }
1554
+ console.log(
1555
+ pc.dim(
1556
+ `
1557
+ Summary: ${okActions.length} action(s) ok, ${failedActions.length} failed \xB7 ${okDeps.length} dep install(s) ok, ${failedDeps.length} failed`
1558
+ )
1559
+ );
1560
+ }
1561
+ function selectionsToUserChoices(selections, project, eslintRules, injectionPointConfig) {
1562
+ const items = [];
1563
+ const choices = { items };
1564
+ for (const selection of selections) {
1565
+ if (!selection.selected || selection.targets.length === 0) continue;
1566
+ const { installer, targets } = selection;
1567
+ if (installer.id === "genstyleguide") {
1568
+ items.push("genstyleguide");
1569
+ } else if (installer.id === "skill") {
1570
+ items.push("skill");
1571
+ } else if (installer.id === "eslint") {
1572
+ items.push("eslint");
1573
+ choices.eslint = {
1574
+ packagePaths: targets.map((t) => t.path),
1575
+ // Use configured rules if provided, otherwise fall back to all rules
1576
+ selectedRules: eslintRules ? eslintRules.map((cr) => ({
1577
+ ...cr.rule,
1578
+ // Override severity with user's selection
1579
+ defaultSeverity: cr.severity,
1580
+ defaultOptions: cr.options
1581
+ })) : ruleRegistry
1582
+ };
1583
+ } else if (installer.id === "next") {
1584
+ items.push("next");
1585
+ const target = targets[0];
1586
+ const appInfo = project.nextApps.find(
1587
+ (app) => app.projectPath === target?.path
1588
+ );
1589
+ if (appInfo) {
1590
+ choices.next = {
1591
+ projectPath: appInfo.projectPath,
1592
+ detection: appInfo.detection,
1593
+ // Use injection point from follow-up UI selection
1594
+ targetFile: injectionPointConfig?.targetFile,
1595
+ createProviders: injectionPointConfig?.createProviders
1596
+ };
1597
+ }
1598
+ } else if (installer.id === "vite") {
1599
+ items.push("vite");
1600
+ const target = targets[0];
1601
+ const appInfo = project.viteApps.find(
1602
+ (app) => app.projectPath === target?.path
1603
+ );
1604
+ if (appInfo) {
1605
+ choices.vite = {
1606
+ projectPath: appInfo.projectPath,
1607
+ detection: appInfo.detection
1608
+ };
1609
+ }
1610
+ }
1611
+ }
1612
+ return choices;
1613
+ }
1614
+ function isInteractiveTerminal() {
1615
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY);
1616
+ }
1617
+ async function initUI(options = {}, executeOptions = {}) {
1618
+ const projectPath = process.cwd();
1619
+ if (!isInteractiveTerminal()) {
1620
+ console.error("\n\u2717 Interactive mode requires a TTY terminal.");
1621
+ console.error("Run uilint init in an interactive terminal.\n");
1622
+ process.exit(1);
1623
+ }
1624
+ const projectPromise = analyze(projectPath);
1625
+ const { waitUntilExit } = render(
1626
+ /* @__PURE__ */ jsx7(
1627
+ InstallApp,
1628
+ {
1629
+ projectPromise,
1630
+ onComplete: async (selections, eslintRules, injectionPointConfig, removeSelections) => {
1631
+ const project = await projectPromise;
1632
+ const choices = selectionsToUserChoices(selections, project, eslintRules, injectionPointConfig);
1633
+ const hasInstalls = choices.items.length > 0;
1634
+ const hasRemovals = removeSelections && removeSelections.length > 0;
1635
+ if (!hasInstalls && !hasRemovals) {
1636
+ console.log("\nNo changes selected");
1637
+ process.exit(0);
1638
+ }
1639
+ const { createPlan } = await import("./plan-ZWGKTWQ3.js");
1640
+ const plan = createPlan(project, choices, { force: options.force });
1641
+ if (hasRemovals && removeSelections) {
1642
+ for (const selection of removeSelections) {
1643
+ if (!selection.selected || selection.targets.length === 0) continue;
1644
+ const { installer, targets } = selection;
1645
+ if (installer.planRemove) {
1646
+ const removePlan = installer.planRemove(targets, project);
1647
+ plan.actions = [...removePlan.actions, ...plan.actions];
1648
+ }
1649
+ }
1650
+ }
1651
+ const result = await execute(plan, {
1652
+ ...executeOptions,
1653
+ projectPath: project.projectPath
1654
+ });
1655
+ let testCoverageResult;
1656
+ if (result.success && eslintRules?.some((r) => r.rule.id === "require-test-coverage")) {
1657
+ const eslintTargetPaths = choices.eslint?.packagePaths ?? [];
1658
+ for (const targetPath of eslintTargetPaths) {
1659
+ const coverageSetup = detectCoverageSetup(targetPath);
1660
+ if (coverageSetup.hasVitest && coverageSetup.hasCoverageConfig) {
1661
+ console.log(`
1662
+ ${pc.blue("Running tests with coverage...")}`);
1663
+ const pm = detectPackageManager(targetPath);
1664
+ try {
1665
+ await runTestsWithCoverage(pm, targetPath);
1666
+ testCoverageResult = { ran: true, success: true };
1667
+ console.log(`${pc.green("\u2713")} Coverage data generated`);
1668
+ } catch (error) {
1669
+ const errorMsg = error instanceof Error ? error.message : String(error);
1670
+ testCoverageResult = { ran: true, success: false, error: errorMsg };
1671
+ console.log(`${pc.yellow("\u26A0")} Tests completed with errors`);
1672
+ }
1673
+ }
1674
+ }
1675
+ }
1676
+ printInstallReport(result, testCoverageResult);
1677
+ process.exit(result.success ? 0 : 1);
1678
+ },
1679
+ onError: (error) => {
1680
+ console.error("\n\u2717 Error:", error.message);
1681
+ process.exit(1);
1682
+ }
1683
+ }
1684
+ )
1685
+ );
1686
+ await waitUntilExit();
1687
+ }
1688
+ export {
1689
+ initUI
1690
+ };
1691
+ //# sourceMappingURL=init-ui-GKA2KU3I.js.map