uilint 0.2.9 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-FRNXXIEM.js +197 -0
- package/dist/chunk-FRNXXIEM.js.map +1 -0
- package/dist/index.js +105 -151
- package/dist/index.js.map +1 -1
- package/dist/{install-ui-OEFHX4FG.js → install-ui-KI7YHOVZ.js} +939 -259
- package/dist/install-ui-KI7YHOVZ.js.map +1 -0
- package/package.json +3 -3
- package/dist/chunk-RHTG6DUD.js +0 -89
- package/dist/chunk-RHTG6DUD.js.map +0 -1
- package/dist/install-ui-OEFHX4FG.js.map +0 -1
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
confirm,
|
|
3
4
|
detectNextAppRouter,
|
|
4
|
-
findNextAppRouterProjects
|
|
5
|
-
|
|
5
|
+
findNextAppRouterProjects,
|
|
6
|
+
log,
|
|
7
|
+
multiselect,
|
|
8
|
+
note,
|
|
9
|
+
pc,
|
|
10
|
+
select
|
|
11
|
+
} from "./chunk-FRNXXIEM.js";
|
|
6
12
|
import {
|
|
7
13
|
GENSTYLEGUIDE_COMMAND_MD,
|
|
8
14
|
loadSkill
|
|
@@ -12,8 +18,8 @@ import {
|
|
|
12
18
|
import { render } from "ink";
|
|
13
19
|
|
|
14
20
|
// src/commands/install/components/InstallApp.tsx
|
|
15
|
-
import { useState as
|
|
16
|
-
import { Box as
|
|
21
|
+
import { useState as useState5, useEffect as useEffect2 } from "react";
|
|
22
|
+
import { Box as Box4, Text as Text5, useApp as useApp4, useInput as useInput4 } from "ink";
|
|
17
23
|
|
|
18
24
|
// src/commands/install/components/Spinner.tsx
|
|
19
25
|
import { useState, useEffect } from "react";
|
|
@@ -33,39 +39,132 @@ function Spinner() {
|
|
|
33
39
|
return /* @__PURE__ */ jsx(Text, { color: "cyan", children: frames[frame] });
|
|
34
40
|
}
|
|
35
41
|
|
|
36
|
-
// src/commands/install/components/
|
|
37
|
-
import { useState as useState2
|
|
42
|
+
// src/commands/install/components/ProjectSelector.tsx
|
|
43
|
+
import { useState as useState2 } from "react";
|
|
38
44
|
import { Box, Text as Text2, useInput, useApp } from "ink";
|
|
39
45
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
46
|
+
function getDetectedProjects(project) {
|
|
47
|
+
const projects = [];
|
|
48
|
+
for (const app of project.nextApps) {
|
|
49
|
+
const relativePath = app.projectPath.replace(project.workspaceRoot + "/", "");
|
|
50
|
+
projects.push({
|
|
51
|
+
id: `next:${app.projectPath}`,
|
|
52
|
+
name: relativePath || ".",
|
|
53
|
+
path: app.projectPath,
|
|
54
|
+
type: "nextjs",
|
|
55
|
+
hint: "Next.js App Router",
|
|
56
|
+
isConfigured: false
|
|
57
|
+
// TODO: detect if overlay is installed
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
for (const app of project.viteApps) {
|
|
61
|
+
const relativePath = app.projectPath.replace(project.workspaceRoot + "/", "");
|
|
62
|
+
projects.push({
|
|
63
|
+
id: `vite:${app.projectPath}`,
|
|
64
|
+
name: relativePath || ".",
|
|
65
|
+
path: app.projectPath,
|
|
66
|
+
type: "vite",
|
|
67
|
+
hint: "Vite + React",
|
|
68
|
+
isConfigured: false
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return projects;
|
|
72
|
+
}
|
|
73
|
+
function FrameworkBadge({ type }) {
|
|
74
|
+
switch (type) {
|
|
75
|
+
case "nextjs":
|
|
76
|
+
return /* @__PURE__ */ jsx2(Text2, { color: "white", backgroundColor: "black", children: " Next.js " });
|
|
77
|
+
case "vite":
|
|
78
|
+
return /* @__PURE__ */ jsx2(Text2, { color: "black", backgroundColor: "yellow", children: " Vite " });
|
|
79
|
+
default:
|
|
80
|
+
return /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Other" });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function ProjectSelector({
|
|
84
|
+
projects,
|
|
85
|
+
onSelect,
|
|
86
|
+
onCancel
|
|
87
|
+
}) {
|
|
88
|
+
const { exit } = useApp();
|
|
89
|
+
const [cursor, setCursor] = useState2(0);
|
|
90
|
+
useInput((input, key) => {
|
|
91
|
+
if (key.upArrow) {
|
|
92
|
+
setCursor((prev) => prev > 0 ? prev - 1 : projects.length - 1);
|
|
93
|
+
} else if (key.downArrow) {
|
|
94
|
+
setCursor((prev) => prev < projects.length - 1 ? prev + 1 : 0);
|
|
95
|
+
} else if (key.return) {
|
|
96
|
+
const selected = projects[cursor];
|
|
97
|
+
if (selected) {
|
|
98
|
+
onSelect(selected);
|
|
99
|
+
}
|
|
100
|
+
} else if (input === "q" || key.escape) {
|
|
101
|
+
onCancel?.();
|
|
102
|
+
exit();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
if (projects.length === 0) {
|
|
106
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
107
|
+
/* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "No projects detected." }),
|
|
108
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "UILint works with Next.js (App Router) and Vite + React projects." })
|
|
109
|
+
] });
|
|
110
|
+
}
|
|
111
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
112
|
+
/* @__PURE__ */ jsx2(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx2(Text2, { bold: true, children: "Select a project to configure:" }) }),
|
|
113
|
+
projects.map((project, index) => {
|
|
114
|
+
const isCursor = index === cursor;
|
|
115
|
+
return /* @__PURE__ */ jsxs(Box, { paddingLeft: 1, children: [
|
|
116
|
+
/* @__PURE__ */ jsx2(Text2, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u203A " : " " }),
|
|
117
|
+
/* @__PURE__ */ jsx2(Box, { width: 12, children: /* @__PURE__ */ jsx2(FrameworkBadge, { type: project.type }) }),
|
|
118
|
+
/* @__PURE__ */ jsx2(Box, { width: 30, children: /* @__PURE__ */ jsx2(Text2, { color: isCursor ? "cyan" : void 0, bold: isCursor, children: project.name }) }),
|
|
119
|
+
project.isConfigured && /* @__PURE__ */ jsx2(Text2, { color: "green", dimColor: true, children: "configured" })
|
|
120
|
+
] }, project.id);
|
|
121
|
+
}),
|
|
122
|
+
/* @__PURE__ */ jsx2(Box, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsxs(Text2, { dimColor: true, children: [
|
|
123
|
+
/* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "\u2191\u2193" }),
|
|
124
|
+
" navigate",
|
|
125
|
+
" ",
|
|
126
|
+
/* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "enter" }),
|
|
127
|
+
" select",
|
|
128
|
+
" ",
|
|
129
|
+
/* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "q" }),
|
|
130
|
+
" quit"
|
|
131
|
+
] }) })
|
|
132
|
+
] });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/commands/install/components/MultiSelect.tsx
|
|
136
|
+
import { useState as useState3, useCallback } from "react";
|
|
137
|
+
import { Box as Box2, Text as Text3, useInput as useInput2, useApp as useApp2 } from "ink";
|
|
138
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
40
139
|
function StatusIndicator({ status, isSelected }) {
|
|
41
140
|
if (status === "installed") {
|
|
42
|
-
return /* @__PURE__ */
|
|
141
|
+
return /* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u2713" });
|
|
43
142
|
}
|
|
44
143
|
if (isSelected || status === "selected") {
|
|
45
|
-
return /* @__PURE__ */
|
|
144
|
+
return /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "\u25C9" });
|
|
46
145
|
}
|
|
47
146
|
if (status === "partial") {
|
|
48
|
-
return /* @__PURE__ */
|
|
147
|
+
return /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "\u25D0" });
|
|
49
148
|
}
|
|
50
|
-
return /* @__PURE__ */
|
|
149
|
+
return /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u25CB" });
|
|
51
150
|
}
|
|
52
151
|
function StatusLabel({ status }) {
|
|
53
152
|
if (status === "installed") {
|
|
54
|
-
return /* @__PURE__ */
|
|
153
|
+
return /* @__PURE__ */ jsx3(Text3, { color: "green", dimColor: true, children: "installed" });
|
|
55
154
|
}
|
|
56
155
|
if (status === "partial") {
|
|
57
|
-
return /* @__PURE__ */
|
|
156
|
+
return /* @__PURE__ */ jsx3(Text3, { color: "yellow", dimColor: true, children: "partial" });
|
|
58
157
|
}
|
|
59
|
-
return /* @__PURE__ */
|
|
158
|
+
return /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "-" });
|
|
60
159
|
}
|
|
61
160
|
function ConfigSelector({
|
|
62
161
|
items,
|
|
63
162
|
onSubmit,
|
|
64
163
|
onCancel
|
|
65
164
|
}) {
|
|
66
|
-
const { exit } =
|
|
67
|
-
const [cursor, setCursor] =
|
|
68
|
-
const [selected, setSelected] =
|
|
165
|
+
const { exit } = useApp2();
|
|
166
|
+
const [cursor, setCursor] = useState3(0);
|
|
167
|
+
const [selected, setSelected] = useState3(() => {
|
|
69
168
|
return new Set(
|
|
70
169
|
items.filter((item) => item.status !== "installed" && !item.disabled).map((item) => item.id)
|
|
71
170
|
);
|
|
@@ -89,7 +188,7 @@ function ConfigSelector({
|
|
|
89
188
|
return next;
|
|
90
189
|
});
|
|
91
190
|
}, [cursor, flatItems]);
|
|
92
|
-
|
|
191
|
+
useInput2((input, key) => {
|
|
93
192
|
if (key.upArrow) {
|
|
94
193
|
setCursor((prev) => prev > 0 ? prev - 1 : flatItems.length - 1);
|
|
95
194
|
} else if (key.downArrow) {
|
|
@@ -112,12 +211,12 @@ function ConfigSelector({
|
|
|
112
211
|
}
|
|
113
212
|
});
|
|
114
213
|
let globalIndex = 0;
|
|
115
|
-
return /* @__PURE__ */
|
|
214
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
116
215
|
categories.map((category) => {
|
|
117
216
|
const categoryItems = itemsByCategory.get(category) || [];
|
|
118
217
|
const categoryIcon = categoryItems[0]?.categoryIcon || "\u2022";
|
|
119
|
-
return /* @__PURE__ */
|
|
120
|
-
/* @__PURE__ */
|
|
218
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
|
|
219
|
+
/* @__PURE__ */ jsx3(Box2, { children: /* @__PURE__ */ jsxs2(Text3, { bold: true, color: "white", children: [
|
|
121
220
|
categoryIcon,
|
|
122
221
|
" ",
|
|
123
222
|
category
|
|
@@ -127,45 +226,45 @@ function ConfigSelector({
|
|
|
127
226
|
const isCursor = itemIndex === cursor;
|
|
128
227
|
const isItemSelected = selected.has(item.id);
|
|
129
228
|
const isDisabled = item.disabled || item.status === "installed";
|
|
130
|
-
return /* @__PURE__ */
|
|
131
|
-
/* @__PURE__ */
|
|
132
|
-
/* @__PURE__ */
|
|
133
|
-
/* @__PURE__ */
|
|
134
|
-
|
|
229
|
+
return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 2, children: [
|
|
230
|
+
/* @__PURE__ */ jsx3(Text3, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u203A " : " " }),
|
|
231
|
+
/* @__PURE__ */ jsx3(Box2, { width: 2, children: /* @__PURE__ */ jsx3(StatusIndicator, { status: item.status, isSelected: isItemSelected }) }),
|
|
232
|
+
/* @__PURE__ */ jsx3(Box2, { width: 28, children: /* @__PURE__ */ jsx3(
|
|
233
|
+
Text3,
|
|
135
234
|
{
|
|
136
235
|
color: isDisabled ? void 0 : isCursor ? "cyan" : void 0,
|
|
137
236
|
dimColor: isDisabled,
|
|
138
237
|
children: item.label
|
|
139
238
|
}
|
|
140
239
|
) }),
|
|
141
|
-
/* @__PURE__ */
|
|
142
|
-
/* @__PURE__ */
|
|
240
|
+
/* @__PURE__ */ jsx3(Box2, { width: 20, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: item.hint || "" }) }),
|
|
241
|
+
/* @__PURE__ */ jsx3(StatusLabel, { status: item.status })
|
|
143
242
|
] }, item.id);
|
|
144
243
|
})
|
|
145
244
|
] }, category);
|
|
146
245
|
}),
|
|
147
|
-
/* @__PURE__ */
|
|
148
|
-
/* @__PURE__ */
|
|
246
|
+
/* @__PURE__ */ jsx3(Box2, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsxs2(Text3, { dimColor: true, children: [
|
|
247
|
+
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "\u2191\u2193" }),
|
|
149
248
|
" navigate",
|
|
150
249
|
" ",
|
|
151
|
-
/* @__PURE__ */
|
|
250
|
+
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "space" }),
|
|
152
251
|
" toggle",
|
|
153
252
|
" ",
|
|
154
|
-
/* @__PURE__ */
|
|
253
|
+
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "a" }),
|
|
155
254
|
" all",
|
|
156
255
|
" ",
|
|
157
|
-
/* @__PURE__ */
|
|
256
|
+
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "n" }),
|
|
158
257
|
" none",
|
|
159
258
|
" ",
|
|
160
|
-
/* @__PURE__ */
|
|
259
|
+
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "enter" }),
|
|
161
260
|
" apply",
|
|
162
261
|
" ",
|
|
163
|
-
/* @__PURE__ */
|
|
262
|
+
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "q" }),
|
|
164
263
|
" quit"
|
|
165
264
|
] }) }),
|
|
166
|
-
/* @__PURE__ */
|
|
167
|
-
/* @__PURE__ */
|
|
168
|
-
/* @__PURE__ */
|
|
265
|
+
/* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text3, { children: [
|
|
266
|
+
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: selected.size }),
|
|
267
|
+
/* @__PURE__ */ jsxs2(Text3, { dimColor: true, children: [
|
|
169
268
|
" item",
|
|
170
269
|
selected.size !== 1 ? "s" : "",
|
|
171
270
|
" selected"
|
|
@@ -174,109 +273,234 @@ function ConfigSelector({
|
|
|
174
273
|
] });
|
|
175
274
|
}
|
|
176
275
|
|
|
177
|
-
// src/commands/install/components/
|
|
178
|
-
import {
|
|
179
|
-
import {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
/* @__PURE__ */ jsxs2(Text3, { children: [
|
|
191
|
-
" ",
|
|
192
|
-
task.message
|
|
193
|
-
] })
|
|
194
|
-
] }),
|
|
195
|
-
task.detail && /* @__PURE__ */ jsx3(Box2, { paddingLeft: 2, children: /* @__PURE__ */ jsxs2(Text3, { dimColor: true, children: [
|
|
196
|
-
"\u2514\u2500 ",
|
|
197
|
-
task.detail
|
|
198
|
-
] }) })
|
|
199
|
-
] });
|
|
200
|
-
}
|
|
201
|
-
function ErrorTask({ task }) {
|
|
202
|
-
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
203
|
-
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
204
|
-
/* @__PURE__ */ jsx3(Text3, { color: "red", children: "\u2717 " }),
|
|
205
|
-
/* @__PURE__ */ jsx3(Text3, { color: "red", children: task.message })
|
|
206
|
-
] }),
|
|
207
|
-
task.error && /* @__PURE__ */ jsx3(Box2, { paddingLeft: 2, children: /* @__PURE__ */ jsxs2(Text3, { color: "red", dimColor: true, children: [
|
|
208
|
-
"\u2514\u2500 ",
|
|
209
|
-
task.error
|
|
210
|
-
] }) })
|
|
211
|
-
] });
|
|
276
|
+
// src/commands/install/components/RuleSelector.tsx
|
|
277
|
+
import { useState as useState4, useMemo } from "react";
|
|
278
|
+
import { Box as Box3, Text as Text4, useInput as useInput3, useApp as useApp3 } from "ink";
|
|
279
|
+
import { getRulesByCategory } from "uilint-eslint";
|
|
280
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
281
|
+
function SeverityBadge({ severity }) {
|
|
282
|
+
if (severity === "error") {
|
|
283
|
+
return /* @__PURE__ */ jsx4(Text4, { color: "red", children: "error" });
|
|
284
|
+
}
|
|
285
|
+
if (severity === "warn") {
|
|
286
|
+
return /* @__PURE__ */ jsx4(Text4, { color: "yellow", children: "warn" });
|
|
287
|
+
}
|
|
288
|
+
return /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "off" });
|
|
212
289
|
}
|
|
213
|
-
function
|
|
214
|
-
return /* @__PURE__ */
|
|
215
|
-
|
|
216
|
-
|
|
290
|
+
function CategoryHeader({ name, icon }) {
|
|
291
|
+
return /* @__PURE__ */ jsx4(Box3, { marginTop: 1, marginBottom: 0, children: /* @__PURE__ */ jsxs3(Text4, { bold: true, color: "white", children: [
|
|
292
|
+
icon,
|
|
293
|
+
" ",
|
|
294
|
+
name
|
|
217
295
|
] }) });
|
|
218
296
|
}
|
|
219
|
-
function
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
]
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
id: `task-${taskIdCounter++}`,
|
|
238
|
-
status: "running",
|
|
239
|
-
message: event.message,
|
|
240
|
-
detail: event.detail
|
|
241
|
-
});
|
|
242
|
-
} else if (event.type === "progress") {
|
|
243
|
-
const lastRunning = tasks.reverse().find((t) => t.status === "running");
|
|
244
|
-
tasks.reverse();
|
|
245
|
-
if (lastRunning) {
|
|
246
|
-
lastRunning.message = event.message;
|
|
247
|
-
lastRunning.detail = event.detail;
|
|
248
|
-
} else {
|
|
249
|
-
tasks.push({
|
|
250
|
-
id: `task-${taskIdCounter++}`,
|
|
251
|
-
status: "running",
|
|
252
|
-
message: event.message,
|
|
253
|
-
detail: event.detail
|
|
297
|
+
function RuleSelector({
|
|
298
|
+
onSubmit,
|
|
299
|
+
onBack,
|
|
300
|
+
onCancel
|
|
301
|
+
}) {
|
|
302
|
+
const { exit } = useApp3();
|
|
303
|
+
const staticRules = useMemo(() => getRulesByCategory("static"), []);
|
|
304
|
+
const semanticRules = useMemo(() => getRulesByCategory("semantic"), []);
|
|
305
|
+
const allRules = useMemo(() => [...staticRules, ...semanticRules], [staticRules, semanticRules]);
|
|
306
|
+
const [cursor, setCursor] = useState4(0);
|
|
307
|
+
const [viewMode, setViewMode] = useState4("list");
|
|
308
|
+
const [ruleStates, setRuleStates] = useState4(
|
|
309
|
+
() => {
|
|
310
|
+
const map = /* @__PURE__ */ new Map();
|
|
311
|
+
for (const rule of staticRules) {
|
|
312
|
+
map.set(rule.id, {
|
|
313
|
+
enabled: true,
|
|
314
|
+
severity: rule.defaultSeverity === "off" ? "warn" : rule.defaultSeverity
|
|
254
315
|
});
|
|
255
316
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
lastRunning.detail = void 0;
|
|
317
|
+
for (const rule of semanticRules) {
|
|
318
|
+
map.set(rule.id, {
|
|
319
|
+
enabled: false,
|
|
320
|
+
severity: rule.defaultSeverity === "off" ? "warn" : rule.defaultSeverity
|
|
321
|
+
});
|
|
262
322
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
323
|
+
return map;
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
const currentRule = allRules[cursor];
|
|
327
|
+
const currentState = currentRule ? ruleStates.get(currentRule.id) : void 0;
|
|
328
|
+
const toggleRule = () => {
|
|
329
|
+
if (!currentRule) return;
|
|
330
|
+
setRuleStates((prev) => {
|
|
331
|
+
const next = new Map(prev);
|
|
332
|
+
const current = next.get(currentRule.id);
|
|
333
|
+
next.set(currentRule.id, { ...current, enabled: !current.enabled });
|
|
334
|
+
return next;
|
|
335
|
+
});
|
|
336
|
+
};
|
|
337
|
+
const cycleSeverity = () => {
|
|
338
|
+
if (!currentRule) return;
|
|
339
|
+
setRuleStates((prev) => {
|
|
340
|
+
const next = new Map(prev);
|
|
341
|
+
const current = next.get(currentRule.id);
|
|
342
|
+
const newSeverity = current.severity === "error" ? "warn" : "error";
|
|
343
|
+
next.set(currentRule.id, { ...current, severity: newSeverity });
|
|
344
|
+
return next;
|
|
345
|
+
});
|
|
346
|
+
};
|
|
347
|
+
const handleSubmit = () => {
|
|
348
|
+
const configuredRules = [];
|
|
349
|
+
for (const rule of allRules) {
|
|
350
|
+
const state = ruleStates.get(rule.id);
|
|
351
|
+
if (state?.enabled) {
|
|
352
|
+
configuredRules.push({
|
|
353
|
+
rule,
|
|
354
|
+
severity: state.severity,
|
|
355
|
+
options: rule.defaultOptions
|
|
275
356
|
});
|
|
276
357
|
}
|
|
277
358
|
}
|
|
359
|
+
onSubmit(configuredRules);
|
|
360
|
+
};
|
|
361
|
+
useInput3((input, key) => {
|
|
362
|
+
if (viewMode === "docs") {
|
|
363
|
+
if (key.escape || key.return || input === "d" || input === "q") {
|
|
364
|
+
setViewMode("list");
|
|
365
|
+
}
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (key.upArrow) {
|
|
369
|
+
setCursor((prev) => prev > 0 ? prev - 1 : allRules.length - 1);
|
|
370
|
+
} else if (key.downArrow) {
|
|
371
|
+
setCursor((prev) => prev < allRules.length - 1 ? prev + 1 : 0);
|
|
372
|
+
} else if (input === " ") {
|
|
373
|
+
toggleRule();
|
|
374
|
+
} else if (input === "s") {
|
|
375
|
+
cycleSeverity();
|
|
376
|
+
} else if (input === "d") {
|
|
377
|
+
setViewMode("docs");
|
|
378
|
+
} else if (key.return) {
|
|
379
|
+
handleSubmit();
|
|
380
|
+
} else if (key.escape || input === "q") {
|
|
381
|
+
onCancel?.();
|
|
382
|
+
exit();
|
|
383
|
+
} else if ((input === "b" || key.leftArrow) && onBack) {
|
|
384
|
+
onBack();
|
|
385
|
+
} else if (input === "a") {
|
|
386
|
+
setRuleStates((prev) => {
|
|
387
|
+
const next = new Map(prev);
|
|
388
|
+
for (const rule of allRules) {
|
|
389
|
+
const current = next.get(rule.id);
|
|
390
|
+
next.set(rule.id, { ...current, enabled: true });
|
|
391
|
+
}
|
|
392
|
+
return next;
|
|
393
|
+
});
|
|
394
|
+
} else if (input === "n") {
|
|
395
|
+
setRuleStates((prev) => {
|
|
396
|
+
const next = new Map(prev);
|
|
397
|
+
for (const rule of allRules) {
|
|
398
|
+
const current = next.get(rule.id);
|
|
399
|
+
next.set(rule.id, { ...current, enabled: false });
|
|
400
|
+
}
|
|
401
|
+
return next;
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
if (viewMode === "docs" && currentRule) {
|
|
406
|
+
const docLines = currentRule.docs.trim().split("\n").slice(0, 20);
|
|
407
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
408
|
+
/* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
|
|
409
|
+
/* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: currentRule.name }),
|
|
410
|
+
/* @__PURE__ */ jsxs3(Text4, { dimColor: true, children: [
|
|
411
|
+
" - ",
|
|
412
|
+
currentRule.description
|
|
413
|
+
] })
|
|
414
|
+
] }),
|
|
415
|
+
/* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, children: [
|
|
416
|
+
docLines.map((line, i) => /* @__PURE__ */ jsx4(Text4, { dimColor: line.startsWith("#"), children: line }, i)),
|
|
417
|
+
currentRule.docs.split("\n").length > 20 && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "... (truncated)" })
|
|
418
|
+
] }),
|
|
419
|
+
/* @__PURE__ */ jsx4(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Press any key to return to list" }) })
|
|
420
|
+
] });
|
|
278
421
|
}
|
|
279
|
-
|
|
422
|
+
const enabledCount = Array.from(ruleStates.values()).filter((s) => s.enabled).length;
|
|
423
|
+
const errorCount = Array.from(ruleStates.entries()).filter(
|
|
424
|
+
([id, s]) => s.enabled && s.severity === "error"
|
|
425
|
+
).length;
|
|
426
|
+
const warnCount = enabledCount - errorCount;
|
|
427
|
+
let globalIndex = 0;
|
|
428
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
429
|
+
/* @__PURE__ */ jsx4(Box3, { marginBottom: 1, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: "Configure ESLint Rules" }) }),
|
|
430
|
+
/* @__PURE__ */ jsx4(CategoryHeader, { name: "Static Rules", icon: "\u{1F4CB}" }),
|
|
431
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " Pattern-based, fast analysis" }),
|
|
432
|
+
staticRules.map((rule) => {
|
|
433
|
+
const itemIndex = globalIndex++;
|
|
434
|
+
const isCursor = itemIndex === cursor;
|
|
435
|
+
const state = ruleStates.get(rule.id);
|
|
436
|
+
return /* @__PURE__ */ jsxs3(Box3, { paddingLeft: 2, children: [
|
|
437
|
+
/* @__PURE__ */ jsx4(Text4, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u203A " : " " }),
|
|
438
|
+
/* @__PURE__ */ jsx4(Box3, { width: 3, children: /* @__PURE__ */ jsx4(Text4, { color: state.enabled ? "green" : void 0, dimColor: !state.enabled, children: state.enabled ? "\u2713" : "\u25CB" }) }),
|
|
439
|
+
/* @__PURE__ */ jsx4(Box3, { width: 30, children: /* @__PURE__ */ jsx4(
|
|
440
|
+
Text4,
|
|
441
|
+
{
|
|
442
|
+
color: isCursor ? "cyan" : void 0,
|
|
443
|
+
dimColor: !state.enabled,
|
|
444
|
+
bold: isCursor,
|
|
445
|
+
children: rule.name
|
|
446
|
+
}
|
|
447
|
+
) }),
|
|
448
|
+
/* @__PURE__ */ jsx4(Box3, { width: 8, children: state.enabled ? /* @__PURE__ */ jsx4(SeverityBadge, { severity: state.severity }) : /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "-" }) })
|
|
449
|
+
] }, rule.id);
|
|
450
|
+
}),
|
|
451
|
+
/* @__PURE__ */ jsx4(CategoryHeader, { name: "Semantic Rules", icon: "\u{1F9E0}" }),
|
|
452
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " LLM-powered analysis (requires Ollama)" }),
|
|
453
|
+
semanticRules.map((rule) => {
|
|
454
|
+
const itemIndex = globalIndex++;
|
|
455
|
+
const isCursor = itemIndex === cursor;
|
|
456
|
+
const state = ruleStates.get(rule.id);
|
|
457
|
+
return /* @__PURE__ */ jsxs3(Box3, { paddingLeft: 2, children: [
|
|
458
|
+
/* @__PURE__ */ jsx4(Text4, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u203A " : " " }),
|
|
459
|
+
/* @__PURE__ */ jsx4(Box3, { width: 3, children: /* @__PURE__ */ jsx4(Text4, { color: state.enabled ? "green" : void 0, dimColor: !state.enabled, children: state.enabled ? "\u2713" : "\u25CB" }) }),
|
|
460
|
+
/* @__PURE__ */ jsx4(Box3, { width: 30, children: /* @__PURE__ */ jsx4(
|
|
461
|
+
Text4,
|
|
462
|
+
{
|
|
463
|
+
color: isCursor ? "cyan" : void 0,
|
|
464
|
+
dimColor: !state.enabled,
|
|
465
|
+
bold: isCursor,
|
|
466
|
+
children: rule.name
|
|
467
|
+
}
|
|
468
|
+
) }),
|
|
469
|
+
/* @__PURE__ */ jsx4(Box3, { width: 8, children: state.enabled ? /* @__PURE__ */ jsx4(SeverityBadge, { severity: state.severity }) : /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "-" }) })
|
|
470
|
+
] }, rule.id);
|
|
471
|
+
}),
|
|
472
|
+
currentRule && /* @__PURE__ */ jsx4(Box3, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: currentRule.description }) }),
|
|
473
|
+
/* @__PURE__ */ jsx4(Box3, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsxs3(Text4, { dimColor: true, children: [
|
|
474
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "\u2191\u2193" }),
|
|
475
|
+
" navigate",
|
|
476
|
+
" ",
|
|
477
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "space" }),
|
|
478
|
+
" toggle",
|
|
479
|
+
" ",
|
|
480
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "s" }),
|
|
481
|
+
" severity",
|
|
482
|
+
" ",
|
|
483
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "d" }),
|
|
484
|
+
" docs",
|
|
485
|
+
" ",
|
|
486
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "a" }),
|
|
487
|
+
" all",
|
|
488
|
+
" ",
|
|
489
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "n" }),
|
|
490
|
+
" none",
|
|
491
|
+
" ",
|
|
492
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "enter" }),
|
|
493
|
+
" confirm"
|
|
494
|
+
] }) }),
|
|
495
|
+
/* @__PURE__ */ jsx4(Box3, { marginTop: 1, children: /* @__PURE__ */ jsxs3(Text4, { children: [
|
|
496
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: enabledCount }),
|
|
497
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " rules enabled (" }),
|
|
498
|
+
/* @__PURE__ */ jsx4(Text4, { color: "red", children: errorCount }),
|
|
499
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " errors, " }),
|
|
500
|
+
/* @__PURE__ */ jsx4(Text4, { color: "yellow", children: warnCount }),
|
|
501
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " warnings)" })
|
|
502
|
+
] }) })
|
|
503
|
+
] });
|
|
280
504
|
}
|
|
281
505
|
|
|
282
506
|
// src/commands/install/installers/registry.ts
|
|
@@ -293,76 +517,138 @@ function getAllInstallers() {
|
|
|
293
517
|
}
|
|
294
518
|
|
|
295
519
|
// src/commands/install/components/InstallApp.tsx
|
|
296
|
-
import { jsx as
|
|
297
|
-
function
|
|
520
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
521
|
+
function buildConfigItemsForProject(project, selectedProject, selections) {
|
|
298
522
|
const items = [];
|
|
299
523
|
for (const selection of selections) {
|
|
300
524
|
const { installer, targets } = selection;
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
525
|
+
const relevantTargets = targets.filter((target) => {
|
|
526
|
+
if (installer.id === "next" || installer.id === "vite") {
|
|
527
|
+
return target.path === selectedProject.path;
|
|
528
|
+
}
|
|
529
|
+
if (installer.id === "eslint") {
|
|
530
|
+
return target.path === selectedProject.path;
|
|
531
|
+
}
|
|
532
|
+
return true;
|
|
533
|
+
});
|
|
534
|
+
if (relevantTargets.length === 0) continue;
|
|
535
|
+
const displayInfo = {
|
|
536
|
+
next: { category: "UI Analysis", icon: "\u{1F50D}" },
|
|
537
|
+
vite: { category: "UI Analysis", icon: "\u{1F50D}" },
|
|
538
|
+
eslint: { category: "ESLint Rules", icon: "\u{1F4CB}" },
|
|
539
|
+
genstyleguide: { category: "Cursor Integration", icon: "\u{1F4DD}" },
|
|
540
|
+
skill: { category: "Cursor Integration", icon: "\u26A1" }
|
|
307
541
|
};
|
|
308
|
-
const
|
|
542
|
+
const info = displayInfo[installer.id] || { category: "Other", icon: "\u2022" };
|
|
543
|
+
for (const target of relevantTargets) {
|
|
544
|
+
items.push({
|
|
545
|
+
id: `${installer.id}:${target.id}`,
|
|
546
|
+
label: installer.name,
|
|
547
|
+
hint: target.hint,
|
|
548
|
+
status: target.isInstalled ? "installed" : "not_installed",
|
|
549
|
+
category: info.category,
|
|
550
|
+
categoryIcon: info.icon
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return items;
|
|
555
|
+
}
|
|
556
|
+
function buildGlobalConfigItems(selections) {
|
|
557
|
+
const items = [];
|
|
558
|
+
for (const selection of selections) {
|
|
559
|
+
const { installer, targets } = selection;
|
|
560
|
+
if (installer.id !== "genstyleguide" && installer.id !== "skill") {
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
const displayInfo = {
|
|
564
|
+
genstyleguide: { category: "Cursor Integration", icon: "\u{1F4DD}" },
|
|
565
|
+
skill: { category: "Cursor Integration", icon: "\u26A1" }
|
|
566
|
+
};
|
|
567
|
+
const info = displayInfo[installer.id] || { category: "Other", icon: "\u2022" };
|
|
309
568
|
for (const target of targets) {
|
|
310
569
|
items.push({
|
|
311
570
|
id: `${installer.id}:${target.id}`,
|
|
312
|
-
label:
|
|
571
|
+
label: installer.name,
|
|
313
572
|
hint: target.hint,
|
|
314
573
|
status: target.isInstalled ? "installed" : "not_installed",
|
|
315
|
-
category: category
|
|
316
|
-
categoryIcon:
|
|
574
|
+
category: info.category,
|
|
575
|
+
categoryIcon: info.icon
|
|
317
576
|
});
|
|
318
577
|
}
|
|
319
578
|
}
|
|
320
579
|
return items;
|
|
321
580
|
}
|
|
322
581
|
function Header({ subtitle }) {
|
|
323
|
-
return /* @__PURE__ */
|
|
324
|
-
/* @__PURE__ */
|
|
325
|
-
/* @__PURE__ */
|
|
326
|
-
subtitle && /* @__PURE__ */
|
|
582
|
+
return /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
583
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, color: "cyan", children: "\u25C6 UILint" }),
|
|
584
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: " v0.5.0" }),
|
|
585
|
+
subtitle && /* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
|
|
327
586
|
" \xB7 ",
|
|
328
587
|
subtitle
|
|
329
588
|
] })
|
|
330
589
|
] }) });
|
|
331
590
|
}
|
|
332
|
-
function
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
return /* @__PURE__ */
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
591
|
+
function FeatureConfig({
|
|
592
|
+
selectedProject,
|
|
593
|
+
configItems,
|
|
594
|
+
canGoBack,
|
|
595
|
+
onSubmit,
|
|
596
|
+
onBack,
|
|
597
|
+
onCancel
|
|
598
|
+
}) {
|
|
599
|
+
useInput4((input, key) => {
|
|
600
|
+
if ((input === "b" || key.leftArrow) && canGoBack) {
|
|
601
|
+
onBack();
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
605
|
+
selectedProject && /* @__PURE__ */ jsxs4(Box4, { marginBottom: 1, children: [
|
|
606
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Project: " }),
|
|
607
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, color: "cyan", children: selectedProject.name }),
|
|
608
|
+
/* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
|
|
609
|
+
" (",
|
|
610
|
+
selectedProject.hint,
|
|
611
|
+
")"
|
|
612
|
+
] })
|
|
613
|
+
] }),
|
|
614
|
+
/* @__PURE__ */ jsx5(
|
|
615
|
+
ConfigSelector,
|
|
616
|
+
{
|
|
617
|
+
items: configItems,
|
|
618
|
+
onSubmit,
|
|
619
|
+
onCancel
|
|
620
|
+
}
|
|
621
|
+
),
|
|
622
|
+
canGoBack && /* @__PURE__ */ jsx5(Box4, { marginTop: 1, children: /* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
|
|
623
|
+
"Press ",
|
|
624
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "b" }),
|
|
625
|
+
" or ",
|
|
626
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "\u2190" }),
|
|
627
|
+
" to select a different project"
|
|
628
|
+
] }) })
|
|
629
|
+
] });
|
|
349
630
|
}
|
|
350
631
|
function InstallApp({
|
|
351
632
|
projectPromise,
|
|
352
633
|
onComplete,
|
|
353
634
|
onError
|
|
354
635
|
}) {
|
|
355
|
-
const { exit } =
|
|
356
|
-
const [
|
|
357
|
-
const [project, setProject] =
|
|
358
|
-
const [
|
|
359
|
-
const [
|
|
360
|
-
const [
|
|
361
|
-
const [
|
|
636
|
+
const { exit } = useApp4();
|
|
637
|
+
const [phase, setPhase] = useState5("scanning");
|
|
638
|
+
const [project, setProject] = useState5(null);
|
|
639
|
+
const [detectedProjects, setDetectedProjects] = useState5([]);
|
|
640
|
+
const [selectedProject, setSelectedProject] = useState5(null);
|
|
641
|
+
const [selections, setSelections] = useState5([]);
|
|
642
|
+
const [configItems, setConfigItems] = useState5([]);
|
|
643
|
+
const [selectedFeatureIds, setSelectedFeatureIds] = useState5([]);
|
|
644
|
+
const [error, setError] = useState5(null);
|
|
645
|
+
const isEslintSelected = selectedFeatureIds.some((id) => id.startsWith("eslint:"));
|
|
362
646
|
useEffect2(() => {
|
|
363
|
-
if (
|
|
647
|
+
if (phase !== "scanning") return;
|
|
364
648
|
projectPromise.then((proj) => {
|
|
365
649
|
setProject(proj);
|
|
650
|
+
const projects = getDetectedProjects(proj);
|
|
651
|
+
setDetectedProjects(projects);
|
|
366
652
|
const installers2 = getAllInstallers();
|
|
367
653
|
const initialSelections = installers2.filter((installer) => installer.isApplicable(proj)).map((installer) => {
|
|
368
654
|
const targets = installer.getTargets(proj);
|
|
@@ -374,16 +660,55 @@ function InstallApp({
|
|
|
374
660
|
};
|
|
375
661
|
});
|
|
376
662
|
setSelections(initialSelections);
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
663
|
+
if (projects.length === 1) {
|
|
664
|
+
const singleProject = projects[0];
|
|
665
|
+
setSelectedProject(singleProject);
|
|
666
|
+
const items = buildConfigItemsForProject(proj, singleProject, initialSelections);
|
|
667
|
+
setConfigItems(items);
|
|
668
|
+
setPhase("configure-features");
|
|
669
|
+
} else if (projects.length === 0) {
|
|
670
|
+
setPhase("configure-features");
|
|
671
|
+
const items = buildGlobalConfigItems(initialSelections);
|
|
672
|
+
setConfigItems(items);
|
|
673
|
+
} else {
|
|
674
|
+
setPhase("select-project");
|
|
675
|
+
}
|
|
380
676
|
}).catch((err) => {
|
|
381
677
|
setError(err);
|
|
382
|
-
|
|
678
|
+
setPhase("error");
|
|
383
679
|
onError?.(err);
|
|
384
680
|
});
|
|
385
|
-
}, [
|
|
386
|
-
const
|
|
681
|
+
}, [phase, projectPromise, onError]);
|
|
682
|
+
const handleProjectSelect = (selected) => {
|
|
683
|
+
setSelectedProject(selected);
|
|
684
|
+
if (project) {
|
|
685
|
+
const items = buildConfigItemsForProject(project, selected, selections);
|
|
686
|
+
setConfigItems(items);
|
|
687
|
+
}
|
|
688
|
+
setPhase("configure-features");
|
|
689
|
+
};
|
|
690
|
+
const handleBackToProject = () => {
|
|
691
|
+
if (detectedProjects.length > 1) {
|
|
692
|
+
setSelectedProject(null);
|
|
693
|
+
setPhase("select-project");
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
const handleFeatureSubmit = (selectedIds) => {
|
|
697
|
+
setSelectedFeatureIds(selectedIds);
|
|
698
|
+
const eslintSelected = selectedIds.some((id) => id.startsWith("eslint:"));
|
|
699
|
+
if (eslintSelected) {
|
|
700
|
+
setPhase("configure-eslint");
|
|
701
|
+
} else {
|
|
702
|
+
finishInstallation(selectedIds, void 0);
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
const handleRuleSubmit = (configuredRules) => {
|
|
706
|
+
finishInstallation(selectedFeatureIds, configuredRules);
|
|
707
|
+
};
|
|
708
|
+
const handleBackToFeatures = () => {
|
|
709
|
+
setPhase("configure-features");
|
|
710
|
+
};
|
|
711
|
+
const finishInstallation = (selectedIds, eslintRules) => {
|
|
387
712
|
const selectedSet = new Set(selectedIds);
|
|
388
713
|
const updatedSelections = selections.map((sel) => {
|
|
389
714
|
const selectedTargets = sel.targets.filter(
|
|
@@ -396,62 +721,78 @@ function InstallApp({
|
|
|
396
721
|
};
|
|
397
722
|
});
|
|
398
723
|
setSelections(updatedSelections);
|
|
399
|
-
onComplete(updatedSelections);
|
|
724
|
+
onComplete(updatedSelections, eslintRules);
|
|
400
725
|
};
|
|
401
726
|
const handleCancel = () => {
|
|
402
727
|
exit();
|
|
403
728
|
};
|
|
404
|
-
if (
|
|
405
|
-
return /* @__PURE__ */
|
|
406
|
-
/* @__PURE__ */
|
|
407
|
-
/* @__PURE__ */
|
|
408
|
-
/* @__PURE__ */
|
|
409
|
-
/* @__PURE__ */
|
|
729
|
+
if (phase === "scanning") {
|
|
730
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
731
|
+
/* @__PURE__ */ jsx5(Header, { subtitle: "Install" }),
|
|
732
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
733
|
+
/* @__PURE__ */ jsx5(Spinner, {}),
|
|
734
|
+
/* @__PURE__ */ jsx5(Text5, { children: " Scanning project..." })
|
|
410
735
|
] })
|
|
411
736
|
] });
|
|
412
737
|
}
|
|
413
|
-
if (
|
|
414
|
-
return /* @__PURE__ */
|
|
415
|
-
/* @__PURE__ */
|
|
416
|
-
/* @__PURE__ */
|
|
417
|
-
/* @__PURE__ */
|
|
418
|
-
/* @__PURE__ */
|
|
738
|
+
if (phase === "error") {
|
|
739
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
740
|
+
/* @__PURE__ */ jsx5(Header, {}),
|
|
741
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
742
|
+
/* @__PURE__ */ jsx5(Text5, { color: "red", children: "\u2717 " }),
|
|
743
|
+
/* @__PURE__ */ jsx5(Text5, { color: "red", children: error?.message || "An unknown error occurred" })
|
|
419
744
|
] })
|
|
420
745
|
] });
|
|
421
746
|
}
|
|
422
|
-
if (
|
|
423
|
-
return /* @__PURE__ */
|
|
424
|
-
/* @__PURE__ */
|
|
425
|
-
/* @__PURE__ */
|
|
426
|
-
|
|
427
|
-
ConfigSelector,
|
|
747
|
+
if (phase === "select-project") {
|
|
748
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
749
|
+
/* @__PURE__ */ jsx5(Header, { subtitle: "Install" }),
|
|
750
|
+
/* @__PURE__ */ jsx5(
|
|
751
|
+
ProjectSelector,
|
|
428
752
|
{
|
|
429
|
-
|
|
430
|
-
|
|
753
|
+
projects: detectedProjects,
|
|
754
|
+
onSelect: handleProjectSelect,
|
|
431
755
|
onCancel: handleCancel
|
|
432
756
|
}
|
|
433
757
|
)
|
|
434
758
|
] });
|
|
435
759
|
}
|
|
436
|
-
if (
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
/* @__PURE__ */
|
|
440
|
-
|
|
760
|
+
if (phase === "configure-features" && project && configItems.length > 0) {
|
|
761
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
762
|
+
/* @__PURE__ */ jsx5(Header, { subtitle: "Features" }),
|
|
763
|
+
/* @__PURE__ */ jsx5(
|
|
764
|
+
FeatureConfig,
|
|
765
|
+
{
|
|
766
|
+
selectedProject,
|
|
767
|
+
configItems,
|
|
768
|
+
canGoBack: detectedProjects.length > 1,
|
|
769
|
+
onSubmit: handleFeatureSubmit,
|
|
770
|
+
onBack: handleBackToProject,
|
|
771
|
+
onCancel: handleCancel
|
|
772
|
+
}
|
|
773
|
+
)
|
|
441
774
|
] });
|
|
442
775
|
}
|
|
443
|
-
if (
|
|
444
|
-
return /* @__PURE__ */
|
|
445
|
-
/* @__PURE__ */
|
|
446
|
-
/* @__PURE__ */
|
|
447
|
-
/* @__PURE__ */
|
|
448
|
-
/* @__PURE__ */
|
|
449
|
-
] })
|
|
776
|
+
if (phase === "configure-eslint") {
|
|
777
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
778
|
+
/* @__PURE__ */ jsx5(Header, { subtitle: "ESLint Rules" }),
|
|
779
|
+
selectedProject && /* @__PURE__ */ jsxs4(Box4, { marginBottom: 1, children: [
|
|
780
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Project: " }),
|
|
781
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, color: "cyan", children: selectedProject.name })
|
|
782
|
+
] }),
|
|
783
|
+
/* @__PURE__ */ jsx5(
|
|
784
|
+
RuleSelector,
|
|
785
|
+
{
|
|
786
|
+
onSubmit: handleRuleSubmit,
|
|
787
|
+
onBack: handleBackToFeatures,
|
|
788
|
+
onCancel: handleCancel
|
|
789
|
+
}
|
|
790
|
+
)
|
|
450
791
|
] });
|
|
451
792
|
}
|
|
452
|
-
return /* @__PURE__ */
|
|
453
|
-
/* @__PURE__ */
|
|
454
|
-
/* @__PURE__ */
|
|
793
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
794
|
+
/* @__PURE__ */ jsx5(Header, {}),
|
|
795
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Loading..." })
|
|
455
796
|
] });
|
|
456
797
|
}
|
|
457
798
|
|
|
@@ -1393,14 +1734,14 @@ async function analyze(projectPath = process.cwd()) {
|
|
|
1393
1734
|
|
|
1394
1735
|
// src/commands/install/execute.ts
|
|
1395
1736
|
import {
|
|
1396
|
-
existsSync as
|
|
1737
|
+
existsSync as existsSync11,
|
|
1397
1738
|
mkdirSync,
|
|
1398
1739
|
writeFileSync as writeFileSync5,
|
|
1399
1740
|
readFileSync as readFileSync8,
|
|
1400
1741
|
unlinkSync,
|
|
1401
1742
|
chmodSync
|
|
1402
1743
|
} from "fs";
|
|
1403
|
-
import { dirname as
|
|
1744
|
+
import { dirname as dirname4 } from "path";
|
|
1404
1745
|
|
|
1405
1746
|
// src/utils/react-inject.ts
|
|
1406
1747
|
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -2441,6 +2782,145 @@ async function installNextUILintRoutes(opts) {
|
|
|
2441
2782
|
);
|
|
2442
2783
|
}
|
|
2443
2784
|
|
|
2785
|
+
// src/utils/prettier.ts
|
|
2786
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2787
|
+
import { spawn as spawn2 } from "child_process";
|
|
2788
|
+
import { join as join10, dirname as dirname3 } from "path";
|
|
2789
|
+
function getPrettierPath(projectPath) {
|
|
2790
|
+
const localPath = join10(projectPath, "node_modules", ".bin", "prettier");
|
|
2791
|
+
if (existsSync10(localPath)) return localPath;
|
|
2792
|
+
let dir = projectPath;
|
|
2793
|
+
for (let i = 0; i < 10; i++) {
|
|
2794
|
+
const binPath = join10(dir, "node_modules", ".bin", "prettier");
|
|
2795
|
+
if (existsSync10(binPath)) return binPath;
|
|
2796
|
+
const parent = dirname3(dir);
|
|
2797
|
+
if (parent === dir) break;
|
|
2798
|
+
dir = parent;
|
|
2799
|
+
}
|
|
2800
|
+
return null;
|
|
2801
|
+
}
|
|
2802
|
+
function getPmRunner(pm) {
|
|
2803
|
+
switch (pm) {
|
|
2804
|
+
case "pnpm":
|
|
2805
|
+
return { command: "pnpm", args: ["exec"] };
|
|
2806
|
+
case "yarn":
|
|
2807
|
+
return { command: "yarn", args: [] };
|
|
2808
|
+
case "bun":
|
|
2809
|
+
return { command: "bunx", args: [] };
|
|
2810
|
+
case "npm":
|
|
2811
|
+
default:
|
|
2812
|
+
return { command: "npx", args: [] };
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
async function formatWithPrettier(filePath, projectPath) {
|
|
2816
|
+
const prettierPath = getPrettierPath(projectPath);
|
|
2817
|
+
if (!prettierPath) {
|
|
2818
|
+
const pm = detectPackageManager(projectPath);
|
|
2819
|
+
const runner = getPmRunner(pm);
|
|
2820
|
+
return new Promise((resolve) => {
|
|
2821
|
+
const args = [...runner.args, "prettier", "--write", filePath];
|
|
2822
|
+
const child = spawn2(runner.command, args, {
|
|
2823
|
+
cwd: projectPath,
|
|
2824
|
+
stdio: "pipe",
|
|
2825
|
+
shell: process.platform === "win32"
|
|
2826
|
+
});
|
|
2827
|
+
let stderr = "";
|
|
2828
|
+
child.stderr?.on("data", (data) => {
|
|
2829
|
+
stderr += data.toString();
|
|
2830
|
+
});
|
|
2831
|
+
child.on("error", () => {
|
|
2832
|
+
resolve({ formatted: false, error: "prettier not available" });
|
|
2833
|
+
});
|
|
2834
|
+
child.on("close", (code) => {
|
|
2835
|
+
if (code === 0) {
|
|
2836
|
+
resolve({ formatted: true });
|
|
2837
|
+
} else {
|
|
2838
|
+
resolve({ formatted: false, error: stderr || "prettier failed" });
|
|
2839
|
+
}
|
|
2840
|
+
});
|
|
2841
|
+
});
|
|
2842
|
+
}
|
|
2843
|
+
return new Promise((resolve) => {
|
|
2844
|
+
const child = spawn2(prettierPath, ["--write", filePath], {
|
|
2845
|
+
cwd: projectPath,
|
|
2846
|
+
stdio: "pipe",
|
|
2847
|
+
shell: process.platform === "win32"
|
|
2848
|
+
});
|
|
2849
|
+
let stderr = "";
|
|
2850
|
+
child.stderr?.on("data", (data) => {
|
|
2851
|
+
stderr += data.toString();
|
|
2852
|
+
});
|
|
2853
|
+
child.on("error", (err) => {
|
|
2854
|
+
resolve({ formatted: false, error: err.message });
|
|
2855
|
+
});
|
|
2856
|
+
child.on("close", (code) => {
|
|
2857
|
+
if (code === 0) {
|
|
2858
|
+
resolve({ formatted: true });
|
|
2859
|
+
} else {
|
|
2860
|
+
resolve({ formatted: false, error: stderr || `exit code ${code}` });
|
|
2861
|
+
}
|
|
2862
|
+
});
|
|
2863
|
+
});
|
|
2864
|
+
}
|
|
2865
|
+
async function formatFilesWithPrettier(filePaths, projectPath) {
|
|
2866
|
+
if (filePaths.length === 0) {
|
|
2867
|
+
return { formatted: [], failed: [] };
|
|
2868
|
+
}
|
|
2869
|
+
const prettierPath = getPrettierPath(projectPath);
|
|
2870
|
+
const formatted = [];
|
|
2871
|
+
const failed = [];
|
|
2872
|
+
if (!prettierPath) {
|
|
2873
|
+
const pm = detectPackageManager(projectPath);
|
|
2874
|
+
const runner = getPmRunner(pm);
|
|
2875
|
+
return new Promise((resolve) => {
|
|
2876
|
+
const args = [...runner.args, "prettier", "--write", ...filePaths];
|
|
2877
|
+
const child = spawn2(runner.command, args, {
|
|
2878
|
+
cwd: projectPath,
|
|
2879
|
+
stdio: "pipe",
|
|
2880
|
+
shell: process.platform === "win32"
|
|
2881
|
+
});
|
|
2882
|
+
child.on("error", () => {
|
|
2883
|
+
resolve({ formatted: [], failed: filePaths });
|
|
2884
|
+
});
|
|
2885
|
+
child.on("close", (code) => {
|
|
2886
|
+
if (code === 0) {
|
|
2887
|
+
resolve({ formatted: filePaths, failed: [] });
|
|
2888
|
+
} else {
|
|
2889
|
+
resolve({ formatted: [], failed: filePaths });
|
|
2890
|
+
}
|
|
2891
|
+
});
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
return new Promise((resolve) => {
|
|
2895
|
+
const child = spawn2(prettierPath, ["--write", ...filePaths], {
|
|
2896
|
+
cwd: projectPath,
|
|
2897
|
+
stdio: "pipe",
|
|
2898
|
+
shell: process.platform === "win32"
|
|
2899
|
+
});
|
|
2900
|
+
child.on("error", () => {
|
|
2901
|
+
resolve({ formatted: [], failed: filePaths });
|
|
2902
|
+
});
|
|
2903
|
+
child.on("close", (code) => {
|
|
2904
|
+
if (code === 0) {
|
|
2905
|
+
resolve({ formatted: filePaths, failed: [] });
|
|
2906
|
+
} else {
|
|
2907
|
+
Promise.all(
|
|
2908
|
+
filePaths.map(async (fp) => {
|
|
2909
|
+
const result = await formatWithPrettier(fp, projectPath);
|
|
2910
|
+
if (result.formatted) {
|
|
2911
|
+
formatted.push(fp);
|
|
2912
|
+
} else {
|
|
2913
|
+
failed.push(fp);
|
|
2914
|
+
}
|
|
2915
|
+
})
|
|
2916
|
+
).then(() => {
|
|
2917
|
+
resolve({ formatted, failed });
|
|
2918
|
+
});
|
|
2919
|
+
}
|
|
2920
|
+
});
|
|
2921
|
+
});
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2444
2924
|
// src/commands/install/execute.ts
|
|
2445
2925
|
async function executeAction(action, options) {
|
|
2446
2926
|
const { dryRun = false } = options;
|
|
@@ -2454,7 +2934,7 @@ async function executeAction(action, options) {
|
|
|
2454
2934
|
wouldDo: `Create directory: ${action.path}`
|
|
2455
2935
|
};
|
|
2456
2936
|
}
|
|
2457
|
-
if (!
|
|
2937
|
+
if (!existsSync11(action.path)) {
|
|
2458
2938
|
mkdirSync(action.path, { recursive: true });
|
|
2459
2939
|
}
|
|
2460
2940
|
return { action, success: true };
|
|
@@ -2467,8 +2947,8 @@ async function executeAction(action, options) {
|
|
|
2467
2947
|
wouldDo: `Create file: ${action.path}${action.permissions ? ` (mode: ${action.permissions.toString(8)})` : ""}`
|
|
2468
2948
|
};
|
|
2469
2949
|
}
|
|
2470
|
-
const dir =
|
|
2471
|
-
if (!
|
|
2950
|
+
const dir = dirname4(action.path);
|
|
2951
|
+
if (!existsSync11(dir)) {
|
|
2472
2952
|
mkdirSync(dir, { recursive: true });
|
|
2473
2953
|
}
|
|
2474
2954
|
writeFileSync5(action.path, action.content, "utf-8");
|
|
@@ -2486,15 +2966,15 @@ async function executeAction(action, options) {
|
|
|
2486
2966
|
};
|
|
2487
2967
|
}
|
|
2488
2968
|
let existing = {};
|
|
2489
|
-
if (
|
|
2969
|
+
if (existsSync11(action.path)) {
|
|
2490
2970
|
try {
|
|
2491
2971
|
existing = JSON.parse(readFileSync8(action.path, "utf-8"));
|
|
2492
2972
|
} catch {
|
|
2493
2973
|
}
|
|
2494
2974
|
}
|
|
2495
2975
|
const merged = deepMerge(existing, action.merge);
|
|
2496
|
-
const dir =
|
|
2497
|
-
if (!
|
|
2976
|
+
const dir = dirname4(action.path);
|
|
2977
|
+
if (!existsSync11(dir)) {
|
|
2498
2978
|
mkdirSync(dir, { recursive: true });
|
|
2499
2979
|
}
|
|
2500
2980
|
writeFileSync5(action.path, JSON.stringify(merged, null, 2), "utf-8");
|
|
@@ -2508,7 +2988,7 @@ async function executeAction(action, options) {
|
|
|
2508
2988
|
wouldDo: `Delete file: ${action.path}`
|
|
2509
2989
|
};
|
|
2510
2990
|
}
|
|
2511
|
-
if (
|
|
2991
|
+
if (existsSync11(action.path)) {
|
|
2512
2992
|
unlinkSync(action.path);
|
|
2513
2993
|
}
|
|
2514
2994
|
return { action, success: true };
|
|
@@ -2521,7 +3001,7 @@ async function executeAction(action, options) {
|
|
|
2521
3001
|
wouldDo: `Append to file: ${action.path}`
|
|
2522
3002
|
};
|
|
2523
3003
|
}
|
|
2524
|
-
if (
|
|
3004
|
+
if (existsSync11(action.path)) {
|
|
2525
3005
|
const content = readFileSync8(action.path, "utf-8");
|
|
2526
3006
|
if (action.ifNotContains && content.includes(action.ifNotContains)) {
|
|
2527
3007
|
return { action, success: true };
|
|
@@ -2739,8 +3219,71 @@ function buildSummary(actionsPerformed, dependencyResults, items) {
|
|
|
2739
3219
|
viteApp
|
|
2740
3220
|
};
|
|
2741
3221
|
}
|
|
3222
|
+
function collectFormattableFiles(actionsPerformed) {
|
|
3223
|
+
const formattableExtensions = /* @__PURE__ */ new Set([
|
|
3224
|
+
".ts",
|
|
3225
|
+
".tsx",
|
|
3226
|
+
".js",
|
|
3227
|
+
".jsx",
|
|
3228
|
+
".mjs",
|
|
3229
|
+
".cjs",
|
|
3230
|
+
".json"
|
|
3231
|
+
]);
|
|
3232
|
+
const files = [];
|
|
3233
|
+
for (const result of actionsPerformed) {
|
|
3234
|
+
if (!result.success) continue;
|
|
3235
|
+
const { action } = result;
|
|
3236
|
+
let filePath;
|
|
3237
|
+
switch (action.type) {
|
|
3238
|
+
case "create_file":
|
|
3239
|
+
filePath = action.path;
|
|
3240
|
+
break;
|
|
3241
|
+
case "merge_json":
|
|
3242
|
+
filePath = action.path;
|
|
3243
|
+
break;
|
|
3244
|
+
case "append_to_file":
|
|
3245
|
+
filePath = action.path;
|
|
3246
|
+
break;
|
|
3247
|
+
case "inject_eslint":
|
|
3248
|
+
filePath = action.configPath;
|
|
3249
|
+
break;
|
|
3250
|
+
case "inject_next_config":
|
|
3251
|
+
case "inject_vite_config":
|
|
3252
|
+
case "inject_react":
|
|
3253
|
+
break;
|
|
3254
|
+
}
|
|
3255
|
+
if (filePath) {
|
|
3256
|
+
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
3257
|
+
if (formattableExtensions.has(ext)) {
|
|
3258
|
+
files.push(filePath);
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
return files;
|
|
3263
|
+
}
|
|
3264
|
+
function getProjectPathFromActions(actionsPerformed) {
|
|
3265
|
+
for (const result of actionsPerformed) {
|
|
3266
|
+
if (!result.success) continue;
|
|
3267
|
+
const { action } = result;
|
|
3268
|
+
switch (action.type) {
|
|
3269
|
+
case "inject_eslint":
|
|
3270
|
+
return action.packagePath;
|
|
3271
|
+
case "inject_react":
|
|
3272
|
+
case "inject_next_config":
|
|
3273
|
+
case "inject_vite_config":
|
|
3274
|
+
case "install_next_routes":
|
|
3275
|
+
return action.projectPath;
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
return void 0;
|
|
3279
|
+
}
|
|
2742
3280
|
async function execute(plan, options = {}) {
|
|
2743
|
-
const {
|
|
3281
|
+
const {
|
|
3282
|
+
dryRun = false,
|
|
3283
|
+
installDependencies: installDependencies2 = installDependencies,
|
|
3284
|
+
projectPath,
|
|
3285
|
+
skipPrettier = false
|
|
3286
|
+
} = options;
|
|
2744
3287
|
const actionsPerformed = [];
|
|
2745
3288
|
const dependencyResults = [];
|
|
2746
3289
|
for (const action of plan.actions) {
|
|
@@ -2774,6 +3317,18 @@ async function execute(plan, options = {}) {
|
|
|
2774
3317
|
});
|
|
2775
3318
|
}
|
|
2776
3319
|
}
|
|
3320
|
+
if (!dryRun && !skipPrettier) {
|
|
3321
|
+
const filesToFormat = collectFormattableFiles(actionsPerformed);
|
|
3322
|
+
if (filesToFormat.length > 0) {
|
|
3323
|
+
const formatProjectPath = projectPath || plan.dependencies[0]?.packagePath || getProjectPathFromActions(actionsPerformed);
|
|
3324
|
+
if (formatProjectPath) {
|
|
3325
|
+
await formatFilesWithPrettier(filesToFormat, formatProjectPath).catch(
|
|
3326
|
+
() => {
|
|
3327
|
+
}
|
|
3328
|
+
);
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
2777
3332
|
const actionsFailed = actionsPerformed.filter((r) => !r.success);
|
|
2778
3333
|
const depsFailed = dependencyResults.filter((r) => !r.success);
|
|
2779
3334
|
const success = actionsFailed.length === 0 && depsFailed.length === 0;
|
|
@@ -2807,10 +3362,10 @@ async function execute(plan, options = {}) {
|
|
|
2807
3362
|
}
|
|
2808
3363
|
|
|
2809
3364
|
// src/commands/install-ui.tsx
|
|
2810
|
-
import { ruleRegistry as
|
|
3365
|
+
import { ruleRegistry as ruleRegistry3 } from "uilint-eslint";
|
|
2811
3366
|
|
|
2812
3367
|
// src/commands/install/installers/genstyleguide.ts
|
|
2813
|
-
import { join as
|
|
3368
|
+
import { join as join11 } from "path";
|
|
2814
3369
|
var genstyleguideInstaller = {
|
|
2815
3370
|
id: "genstyleguide",
|
|
2816
3371
|
name: "/genstyleguide command",
|
|
@@ -2820,7 +3375,7 @@ var genstyleguideInstaller = {
|
|
|
2820
3375
|
return true;
|
|
2821
3376
|
},
|
|
2822
3377
|
getTargets(project) {
|
|
2823
|
-
const commandPath =
|
|
3378
|
+
const commandPath = join11(project.cursorDir.path, "commands", "genstyleguide.md");
|
|
2824
3379
|
const isInstalled = project.commands.genstyleguide;
|
|
2825
3380
|
return [
|
|
2826
3381
|
{
|
|
@@ -2833,7 +3388,7 @@ var genstyleguideInstaller = {
|
|
|
2833
3388
|
},
|
|
2834
3389
|
plan(targets, config, project) {
|
|
2835
3390
|
const actions = [];
|
|
2836
|
-
const commandsDir =
|
|
3391
|
+
const commandsDir = join11(project.cursorDir.path, "commands");
|
|
2837
3392
|
if (!project.cursorDir.exists) {
|
|
2838
3393
|
actions.push({
|
|
2839
3394
|
type: "create_directory",
|
|
@@ -2846,7 +3401,7 @@ var genstyleguideInstaller = {
|
|
|
2846
3401
|
});
|
|
2847
3402
|
actions.push({
|
|
2848
3403
|
type: "create_file",
|
|
2849
|
-
path:
|
|
3404
|
+
path: join11(commandsDir, "genstyleguide.md"),
|
|
2850
3405
|
content: GENSTYLEGUIDE_COMMAND_MD
|
|
2851
3406
|
});
|
|
2852
3407
|
return {
|
|
@@ -2876,8 +3431,8 @@ var genstyleguideInstaller = {
|
|
|
2876
3431
|
};
|
|
2877
3432
|
|
|
2878
3433
|
// src/commands/install/installers/skill.ts
|
|
2879
|
-
import { existsSync as
|
|
2880
|
-
import { join as
|
|
3434
|
+
import { existsSync as existsSync12 } from "fs";
|
|
3435
|
+
import { join as join12 } from "path";
|
|
2881
3436
|
var skillInstaller = {
|
|
2882
3437
|
id: "skill",
|
|
2883
3438
|
name: "UI Consistency Agent skill",
|
|
@@ -2887,9 +3442,9 @@ var skillInstaller = {
|
|
|
2887
3442
|
return true;
|
|
2888
3443
|
},
|
|
2889
3444
|
getTargets(project) {
|
|
2890
|
-
const skillsDir =
|
|
2891
|
-
const skillMdPath =
|
|
2892
|
-
const isInstalled =
|
|
3445
|
+
const skillsDir = join12(project.cursorDir.path, "skills", "ui-consistency-enforcer");
|
|
3446
|
+
const skillMdPath = join12(skillsDir, "SKILL.md");
|
|
3447
|
+
const isInstalled = existsSync12(skillMdPath);
|
|
2893
3448
|
return [
|
|
2894
3449
|
{
|
|
2895
3450
|
id: "ui-consistency-skill",
|
|
@@ -2908,21 +3463,21 @@ var skillInstaller = {
|
|
|
2908
3463
|
path: project.cursorDir.path
|
|
2909
3464
|
});
|
|
2910
3465
|
}
|
|
2911
|
-
const skillsDir =
|
|
3466
|
+
const skillsDir = join12(project.cursorDir.path, "skills");
|
|
2912
3467
|
actions.push({
|
|
2913
3468
|
type: "create_directory",
|
|
2914
3469
|
path: skillsDir
|
|
2915
3470
|
});
|
|
2916
3471
|
try {
|
|
2917
3472
|
const skill = loadSkill("ui-consistency-enforcer");
|
|
2918
|
-
const skillDir =
|
|
3473
|
+
const skillDir = join12(skillsDir, skill.name);
|
|
2919
3474
|
actions.push({
|
|
2920
3475
|
type: "create_directory",
|
|
2921
3476
|
path: skillDir
|
|
2922
3477
|
});
|
|
2923
3478
|
for (const file of skill.files) {
|
|
2924
|
-
const filePath =
|
|
2925
|
-
const fileDir =
|
|
3479
|
+
const filePath = join12(skillDir, file.relativePath);
|
|
3480
|
+
const fileDir = join12(
|
|
2926
3481
|
skillDir,
|
|
2927
3482
|
file.relativePath.split("/").slice(0, -1).join("/")
|
|
2928
3483
|
);
|
|
@@ -2979,13 +3534,19 @@ var skillInstaller = {
|
|
|
2979
3534
|
};
|
|
2980
3535
|
|
|
2981
3536
|
// src/commands/install/installers/eslint.ts
|
|
2982
|
-
import { join as
|
|
2983
|
-
import { ruleRegistry } from "uilint-eslint";
|
|
2984
|
-
import { createRequire } from "module";
|
|
2985
|
-
var require2 = createRequire(import.meta.url);
|
|
3537
|
+
import { join as join13 } from "path";
|
|
3538
|
+
import { ruleRegistry as ruleRegistry2, getRulesByCategory as getRulesByCategory2 } from "uilint-eslint";
|
|
2986
3539
|
function toInstallSpecifier(pkgName) {
|
|
2987
3540
|
return pkgName;
|
|
2988
3541
|
}
|
|
3542
|
+
function formatRuleOption(rule) {
|
|
3543
|
+
const severityBadge = rule.defaultSeverity === "error" ? pc.red("error") : pc.yellow("warn");
|
|
3544
|
+
return {
|
|
3545
|
+
value: rule.id,
|
|
3546
|
+
label: `${rule.name}`,
|
|
3547
|
+
hint: `${rule.description} [${severityBadge}]`
|
|
3548
|
+
};
|
|
3549
|
+
}
|
|
2989
3550
|
var eslintInstaller = {
|
|
2990
3551
|
id: "eslint",
|
|
2991
3552
|
name: "ESLint plugin",
|
|
@@ -3004,20 +3565,130 @@ var eslintInstaller = {
|
|
|
3004
3565
|
}));
|
|
3005
3566
|
},
|
|
3006
3567
|
async configure(targets, project) {
|
|
3007
|
-
const
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3568
|
+
const staticRules = getRulesByCategory2("static");
|
|
3569
|
+
const semanticRules = getRulesByCategory2("semantic");
|
|
3570
|
+
const installMode = await select({
|
|
3571
|
+
message: "How would you like to configure UILint ESLint rules?",
|
|
3572
|
+
options: [
|
|
3573
|
+
{
|
|
3574
|
+
value: "recommended",
|
|
3575
|
+
label: "Recommended (static rules only)",
|
|
3576
|
+
hint: "Best for most projects - fast and reliable"
|
|
3577
|
+
},
|
|
3578
|
+
{
|
|
3579
|
+
value: "strict",
|
|
3580
|
+
label: "Strict (all rules including semantic)",
|
|
3581
|
+
hint: "Includes LLM-powered analysis - requires Ollama"
|
|
3582
|
+
},
|
|
3583
|
+
{
|
|
3584
|
+
value: "custom",
|
|
3585
|
+
label: "Custom selection",
|
|
3586
|
+
hint: "Choose individual rules and configure severity"
|
|
3587
|
+
}
|
|
3588
|
+
]
|
|
3589
|
+
});
|
|
3590
|
+
let selectedRuleIds;
|
|
3591
|
+
let configuredRules;
|
|
3592
|
+
if (installMode === "recommended") {
|
|
3593
|
+
configuredRules = staticRules.map((rule) => ({
|
|
3594
|
+
rule,
|
|
3595
|
+
severity: rule.defaultSeverity,
|
|
3596
|
+
options: rule.defaultOptions
|
|
3597
|
+
}));
|
|
3598
|
+
} else if (installMode === "strict") {
|
|
3599
|
+
configuredRules = ruleRegistry2.map((rule) => ({
|
|
3600
|
+
rule,
|
|
3601
|
+
severity: rule.defaultSeverity,
|
|
3602
|
+
options: rule.defaultOptions
|
|
3603
|
+
}));
|
|
3604
|
+
} else {
|
|
3605
|
+
log(pc.dim("\n\u{1F4CB} Static rules (pattern-based, fast):"));
|
|
3606
|
+
const selectedStaticIds = await multiselect({
|
|
3607
|
+
message: "Select static rules to enable:",
|
|
3608
|
+
options: staticRules.map(formatRuleOption),
|
|
3609
|
+
initialValues: staticRules.map((r) => r.id)
|
|
3610
|
+
});
|
|
3611
|
+
const includeSemanticRules = await confirm({
|
|
3612
|
+
message: `Include semantic rules? ${pc.dim(
|
|
3613
|
+
"(requires Ollama for LLM analysis)"
|
|
3614
|
+
)}`,
|
|
3615
|
+
initialValue: false
|
|
3616
|
+
});
|
|
3617
|
+
let selectedSemanticIds = [];
|
|
3618
|
+
if (includeSemanticRules) {
|
|
3619
|
+
log(
|
|
3620
|
+
pc.dim("\n\u{1F9E0} Semantic rules (LLM-powered, slower):")
|
|
3621
|
+
);
|
|
3622
|
+
selectedSemanticIds = await multiselect({
|
|
3623
|
+
message: "Select semantic rules:",
|
|
3624
|
+
options: semanticRules.map(formatRuleOption),
|
|
3625
|
+
initialValues: semanticRules.map((r) => r.id)
|
|
3626
|
+
});
|
|
3627
|
+
}
|
|
3628
|
+
selectedRuleIds = [...selectedStaticIds, ...selectedSemanticIds];
|
|
3629
|
+
const configureSeverity = await confirm({
|
|
3630
|
+
message: "Would you like to customize severity levels?",
|
|
3631
|
+
initialValue: false
|
|
3632
|
+
});
|
|
3633
|
+
configuredRules = [];
|
|
3634
|
+
for (const ruleId of selectedRuleIds) {
|
|
3635
|
+
const rule = ruleRegistry2.find((r) => r.id === ruleId);
|
|
3636
|
+
if (configureSeverity) {
|
|
3637
|
+
const severity = await select({
|
|
3638
|
+
message: `${rule.name} - severity:`,
|
|
3639
|
+
options: [
|
|
3640
|
+
{
|
|
3641
|
+
value: rule.defaultSeverity,
|
|
3642
|
+
label: `${rule.defaultSeverity} (default)`
|
|
3643
|
+
},
|
|
3644
|
+
...rule.defaultSeverity !== "error" ? [{ value: "error", label: "error" }] : [],
|
|
3645
|
+
...rule.defaultSeverity !== "warn" ? [{ value: "warn", label: "warn" }] : []
|
|
3646
|
+
],
|
|
3647
|
+
initialValue: rule.defaultSeverity
|
|
3648
|
+
});
|
|
3649
|
+
configuredRules.push({
|
|
3650
|
+
rule,
|
|
3651
|
+
severity,
|
|
3652
|
+
options: rule.defaultOptions
|
|
3653
|
+
});
|
|
3654
|
+
} else {
|
|
3655
|
+
configuredRules.push({
|
|
3656
|
+
rule,
|
|
3657
|
+
severity: rule.defaultSeverity,
|
|
3658
|
+
options: rule.defaultOptions
|
|
3659
|
+
});
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
const errorCount = configuredRules.filter(
|
|
3664
|
+
(r) => r.severity === "error"
|
|
3665
|
+
).length;
|
|
3666
|
+
const warnCount = configuredRules.filter(
|
|
3667
|
+
(r) => r.severity === "warn"
|
|
3668
|
+
).length;
|
|
3669
|
+
log("");
|
|
3670
|
+
note(
|
|
3671
|
+
configuredRules.map(
|
|
3672
|
+
(cr) => `${cr.severity === "error" ? "\u{1F534}" : "\u{1F7E1}"} ${cr.rule.name} (${cr.severity})`
|
|
3673
|
+
).join("\n"),
|
|
3674
|
+
`Selected ${configuredRules.length} rules (${errorCount} errors, ${warnCount} warnings)`
|
|
3675
|
+
);
|
|
3676
|
+
return { configuredRules };
|
|
3011
3677
|
},
|
|
3012
3678
|
plan(targets, config, project) {
|
|
3013
3679
|
const actions = [];
|
|
3014
3680
|
const dependencies = [];
|
|
3015
3681
|
const eslintConfig = config;
|
|
3016
|
-
const {
|
|
3682
|
+
const { configuredRules } = eslintConfig;
|
|
3683
|
+
const rulesWithSeverity = configuredRules.map((cr) => ({
|
|
3684
|
+
...cr.rule,
|
|
3685
|
+
defaultSeverity: cr.severity,
|
|
3686
|
+
defaultOptions: cr.options
|
|
3687
|
+
}));
|
|
3017
3688
|
for (const target of targets) {
|
|
3018
3689
|
const pkgInfo = project.packages.find((p) => p.path === target.path);
|
|
3019
3690
|
if (!pkgInfo || !pkgInfo.eslintConfigPath) continue;
|
|
3020
|
-
const rulesDir =
|
|
3691
|
+
const rulesDir = join13(target.path, ".uilint", "rules");
|
|
3021
3692
|
actions.push({
|
|
3022
3693
|
type: "create_directory",
|
|
3023
3694
|
path: rulesDir
|
|
@@ -3031,11 +3702,11 @@ var eslintInstaller = {
|
|
|
3031
3702
|
type: "inject_eslint",
|
|
3032
3703
|
packagePath: target.path,
|
|
3033
3704
|
configPath: pkgInfo.eslintConfigPath,
|
|
3034
|
-
rules:
|
|
3705
|
+
rules: rulesWithSeverity,
|
|
3035
3706
|
hasExistingRules: pkgInfo.hasUilintRules
|
|
3036
3707
|
});
|
|
3037
3708
|
}
|
|
3038
|
-
const gitignorePath =
|
|
3709
|
+
const gitignorePath = join13(project.workspaceRoot, ".gitignore");
|
|
3039
3710
|
actions.push({
|
|
3040
3711
|
type: "append_to_file",
|
|
3041
3712
|
path: gitignorePath,
|
|
@@ -3058,7 +3729,7 @@ var eslintInstaller = {
|
|
|
3058
3729
|
};
|
|
3059
3730
|
yield {
|
|
3060
3731
|
type: "progress",
|
|
3061
|
-
message: `Injecting ${eslintConfig.
|
|
3732
|
+
message: `Injecting ${eslintConfig.configuredRules.length} rules`,
|
|
3062
3733
|
detail: `\u2192 ${target.hint}`
|
|
3063
3734
|
};
|
|
3064
3735
|
}
|
|
@@ -3238,8 +3909,8 @@ registerInstaller(nextOverlayInstaller);
|
|
|
3238
3909
|
registerInstaller(viteOverlayInstaller);
|
|
3239
3910
|
|
|
3240
3911
|
// src/commands/install-ui.tsx
|
|
3241
|
-
import { jsx as
|
|
3242
|
-
function selectionsToUserChoices(selections, project) {
|
|
3912
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
3913
|
+
function selectionsToUserChoices(selections, project, eslintRules) {
|
|
3243
3914
|
const items = [];
|
|
3244
3915
|
const choices = { items };
|
|
3245
3916
|
for (const selection of selections) {
|
|
@@ -3253,7 +3924,13 @@ function selectionsToUserChoices(selections, project) {
|
|
|
3253
3924
|
items.push("eslint");
|
|
3254
3925
|
choices.eslint = {
|
|
3255
3926
|
packagePaths: targets.map((t) => t.path),
|
|
3256
|
-
|
|
3927
|
+
// Use configured rules if provided, otherwise fall back to all rules
|
|
3928
|
+
selectedRules: eslintRules ? eslintRules.map((cr) => ({
|
|
3929
|
+
...cr.rule,
|
|
3930
|
+
// Override severity with user's selection
|
|
3931
|
+
defaultSeverity: cr.severity,
|
|
3932
|
+
defaultOptions: cr.options
|
|
3933
|
+
})) : ruleRegistry3
|
|
3257
3934
|
};
|
|
3258
3935
|
} else if (installer.id === "next") {
|
|
3259
3936
|
items.push("next");
|
|
@@ -3295,20 +3972,23 @@ async function installUI(options = {}, executeOptions = {}) {
|
|
|
3295
3972
|
}
|
|
3296
3973
|
const projectPromise = analyze(projectPath);
|
|
3297
3974
|
const { waitUntilExit } = render(
|
|
3298
|
-
/* @__PURE__ */
|
|
3975
|
+
/* @__PURE__ */ jsx6(
|
|
3299
3976
|
InstallApp,
|
|
3300
3977
|
{
|
|
3301
3978
|
projectPromise,
|
|
3302
|
-
onComplete: async (selections) => {
|
|
3979
|
+
onComplete: async (selections, eslintRules) => {
|
|
3303
3980
|
const project = await projectPromise;
|
|
3304
|
-
const choices = selectionsToUserChoices(selections, project);
|
|
3981
|
+
const choices = selectionsToUserChoices(selections, project, eslintRules);
|
|
3305
3982
|
if (choices.items.length === 0) {
|
|
3306
3983
|
console.log("\nNo items selected for installation");
|
|
3307
3984
|
process.exit(0);
|
|
3308
3985
|
}
|
|
3309
3986
|
const { createPlan } = await import("./plan-PX7FFJ25.js");
|
|
3310
3987
|
const plan = createPlan(project, choices, { force: options.force });
|
|
3311
|
-
const result = await execute(plan,
|
|
3988
|
+
const result = await execute(plan, {
|
|
3989
|
+
...executeOptions,
|
|
3990
|
+
projectPath: project.projectPath
|
|
3991
|
+
});
|
|
3312
3992
|
if (result.success) {
|
|
3313
3993
|
console.log("\n\u2713 Installation completed successfully!");
|
|
3314
3994
|
} else {
|
|
@@ -3328,4 +4008,4 @@ async function installUI(options = {}, executeOptions = {}) {
|
|
|
3328
4008
|
export {
|
|
3329
4009
|
installUI
|
|
3330
4010
|
};
|
|
3331
|
-
//# sourceMappingURL=install-ui-
|
|
4011
|
+
//# sourceMappingURL=install-ui-KI7YHOVZ.js.map
|