uilint 0.2.140 → 0.2.142
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-RYRHWTLC.js → chunk-7GQXW4CT.js} +14 -10
- package/dist/chunk-7GQXW4CT.js.map +1 -0
- package/dist/{chunk-VSBVUS56.js → chunk-JWSZKDZY.js} +77 -77
- package/dist/chunk-JWSZKDZY.js.map +1 -0
- package/dist/{chunk-JIFAHBJ2.js → chunk-OW7Y4RTR.js} +4 -4
- package/dist/chunk-OW7Y4RTR.js.map +1 -0
- package/dist/{chunk-TKJ27W62.js → chunk-VKI3SQMQ.js} +2 -2
- package/dist/{chunk-TKJ27W62.js.map → chunk-VKI3SQMQ.js.map} +1 -1
- package/dist/chunk-WG2WZTB2.js +56 -0
- package/dist/chunk-WG2WZTB2.js.map +1 -0
- package/dist/{chunk-BFHNMKBU.js → chunk-ZUOFUPGT.js} +27 -27
- package/dist/chunk-ZUOFUPGT.js.map +1 -0
- package/dist/index.js +163 -58
- package/dist/index.js.map +1 -1
- package/dist/{init-ui-YAXRGBIJ.js → init-ui-KJYYI5DH.js} +42 -17
- package/dist/init-ui-KJYYI5DH.js.map +1 -0
- package/dist/{plan-2ISQNZBR.js → plan-ZJSVJL5X.js} +4 -4
- package/dist/plugin-loader-O6PNFN6D.js +11 -0
- package/dist/plugin-loader-O6PNFN6D.js.map +1 -0
- package/dist/{remove-ui-RHRSEUWJ.js → remove-ui-ZHW4GUFL.js} +7 -7
- package/dist/remove-ui-ZHW4GUFL.js.map +1 -0
- package/dist/{render-3GGNWYTF.js → render-43OMCORR.js} +1 -1
- package/dist/render-43OMCORR.js.map +1 -0
- package/dist/{upgrade-2EVTOYTJ.js → upgrade-TPZ62BT2.js} +8 -6
- package/dist/upgrade-TPZ62BT2.js.map +1 -0
- package/package.json +12 -7
- package/dist/chunk-BFHNMKBU.js.map +0 -1
- package/dist/chunk-JIFAHBJ2.js.map +0 -1
- package/dist/chunk-RUMBVNPY.js +0 -29
- package/dist/chunk-RUMBVNPY.js.map +0 -1
- package/dist/chunk-RYRHWTLC.js.map +0 -1
- package/dist/chunk-VSBVUS56.js.map +0 -1
- package/dist/init-ui-YAXRGBIJ.js.map +0 -1
- package/dist/remove-ui-RHRSEUWJ.js.map +0 -1
- package/dist/render-3GGNWYTF.js.map +0 -1
- package/dist/upgrade-2EVTOYTJ.js.map +0 -1
- /package/dist/{plan-2ISQNZBR.js.map → plan-ZJSVJL5X.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/remove-ui.tsx","../src/commands/remove/components/RemoveApp.tsx"],"sourcesContent":["/**\n * Remove command with Ink-based UI\n *\n * Dedicated flow for removing UILint components from a project:\n * 1. Analyze project to find installed components\n * 2. Show selection UI with only installed items\n * 3. Confirm selection\n * 4. Execute removal\n */\n\nimport React from \"react\";\nimport { render } from \"ink\";\nimport { RemoveApp } from \"./remove/components/RemoveApp.js\";\nimport { analyze } from \"./init/analyze.js\";\nimport { execute } from \"./init/execute.js\";\nimport type { InstallAction } from \"./init/types.js\";\nimport type { InstallerSelection } from \"./init/installers/types.js\";\nimport { pc } from \"../utils/prompts.js\";\n\n// Import installers to trigger registration\nimport \"./init/installers/index.js\";\n\nfunction limitList(items: string[], max: number): string[] {\n if (items.length <= max) return items;\n return [...items.slice(0, max), pc.dim(`…and ${items.length - max} more`)];\n}\n\nfunction printRemoveReport(\n result: Awaited<ReturnType<typeof execute>>\n): void {\n const failedActions = result.actionsPerformed.filter((r) => !r.success);\n const okActions = result.actionsPerformed.filter((r) => r.success);\n\n // High-level header\n if (result.success) {\n console.log(`\\n${pc.green(\"✓\")} Removal completed successfully`);\n } else {\n console.log(`\\n${pc.yellow(\"⚠\")} Removal completed with errors`);\n }\n\n // Files changed\n const { summary } = result;\n const modified = summary.filesModified;\n const deleted = summary.filesDeleted;\n\n if (modified.length + deleted.length > 0) {\n console.log(`\\n${pc.bold(\"Files:\")}`);\n for (const p of limitList(modified, 20))\n console.log(`- ${pc.yellow(\"~\")} ${p}`);\n for (const p of limitList(deleted, 20))\n console.log(`- ${pc.red(\"-\")} ${p}`);\n }\n\n // Failures\n if (failedActions.length > 0) {\n console.log(`\\n${pc.bold(pc.red(\"Failures:\"))}`);\n for (const a of failedActions) {\n const action = a.action as unknown as Record<string, unknown>;\n const type = String(action.type || \"unknown\");\n const pathish =\n (typeof action.path === \"string\" && action.path) ||\n (typeof action.projectPath === \"string\" && action.projectPath) ||\n (typeof action.packagePath === \"string\" && action.packagePath) ||\n \"\";\n\n console.error(`- ${type}${pathish ? ` (${pathish})` : \"\"}`);\n if (a.error) console.error(` ${a.error}`);\n }\n }\n\n // Quick stats\n console.log(\n pc.dim(\n `\\nSummary: ${okActions.length} action(s) ok, ${failedActions.length} failed`\n )\n );\n}\n\n/**\n * Build removal plan from installer selections\n */\nfunction buildRemovalPlan(\n selections: InstallerSelection[],\n project: Awaited<ReturnType<typeof analyze>>\n): InstallAction[] {\n const actions: InstallAction[] = [];\n\n for (const selection of selections) {\n if (!selection.selected || selection.targets.length === 0) continue;\n const { installer, targets } = selection;\n\n // Call planRemove if the installer supports it\n if (installer.planRemove) {\n const removePlan = installer.planRemove(targets, project);\n actions.push(...removePlan.actions);\n }\n }\n\n return actions;\n}\n\nexport interface RemoveOptions {\n /** Preview changes without actually removing */\n dryRun?: boolean;\n /** Skip confirmation prompt */\n yes?: boolean;\n}\n\n/**\n * Check if terminal supports interactive mode\n */\nfunction isInteractiveTerminal(): boolean {\n return Boolean(process.stdin.isTTY && process.stdout.isTTY);\n}\n\n/**\n * Main remove function with Ink UI\n */\nexport async function removeUI(\n options: RemoveOptions = {}\n): Promise<void> {\n const projectPath = process.cwd();\n\n // Check if terminal supports interactive mode\n if (!isInteractiveTerminal()) {\n console.error(\"\\n✗ Interactive mode requires a TTY terminal.\");\n console.error(\"Run uilint remove in an interactive terminal.\\n\");\n process.exit(1);\n }\n\n // Start project analysis\n const projectPromise = analyze(projectPath);\n\n // Render the Ink app\n const { waitUntilExit } = render(\n <RemoveApp\n projectPromise={projectPromise}\n skipConfirmation={options.yes}\n dryRun={options.dryRun}\n onComplete={async (selections) => {\n const project = await projectPromise;\n\n if (selections.length === 0) {\n console.log(\"\\nNo components selected for removal\");\n process.exit(0);\n }\n\n // Build removal plan\n const actions = buildRemovalPlan(selections, project);\n\n if (actions.length === 0) {\n console.log(\"\\nNo removal actions to perform\");\n process.exit(0);\n }\n\n // Execute the plan\n const result = await execute(\n { actions, dependencies: [] },\n {\n dryRun: options.dryRun,\n projectPath: project.projectPath,\n }\n );\n\n // Display results\n printRemoveReport(result);\n\n process.exit(result.success ? 0 : 1);\n }}\n onError={(error) => {\n console.error(\"\\n✗ Error:\", error.message);\n process.exit(1);\n }}\n />\n );\n\n // Wait for the app to exit\n await waitUntilExit();\n}\n","/**\n * RemoveApp - Ink React component for the remove UI\n *\n * Simplified flow for removing UILint components:\n * 1. Loading (analyze project)\n * 2. Project selection (if multiple projects have installed components)\n * 3. Selection (show installed items with checkboxes)\n * 4. Confirmation (preview what will be removed)\n * 5. Executing (progress indicator)\n */\n\nimport React, { useState, useEffect } from \"react\";\nimport { Box, Text, useApp, useInput } from \"ink\";\nimport { Spinner } from \"../../init/components/Spinner.js\";\nimport type { ProjectState } from \"../../init/types.js\";\nimport type { InstallerSelection, InstallTarget, Installer } from \"../../init/installers/types.js\";\nimport { getAllInstallers } from \"../../init/installers/registry.js\";\n\ntype RemovePhase = \"loading\" | \"select-project\" | \"select\" | \"confirm\" | \"executing\" | \"done\" | \"error\";\n\n/**\n * Detected project with installed components\n */\ninterface ProjectWithInstalls {\n /** Unique ID */\n id: string;\n /** Project name/label (relative path) */\n name: string;\n /** Project path (absolute) */\n path: string;\n /** Framework type */\n type: \"nextjs\" | \"vite\" | \"eslint\" | \"global\";\n /** Framework badge/hint */\n hint: string;\n /** Number of installed components */\n installedCount: number;\n}\n\n/**\n * Item to display in the selection list\n */\ninterface RemoveItem {\n id: string;\n installerId: string;\n installer: Installer;\n label: string;\n hint?: string;\n target: InstallTarget;\n}\n\nexport interface RemoveAppProps {\n /** Project scan promise (resolves to ProjectState) */\n projectPromise: Promise<ProjectState>;\n /** Skip confirmation prompt */\n skipConfirmation?: boolean;\n /** Dry run mode - preview only */\n dryRun?: boolean;\n /** Callback when removal is complete */\n onComplete: (selections: InstallerSelection[]) => void;\n /** Callback when error occurs */\n onError?: (error: Error) => void;\n}\n\n/**\n * Get all projects that have installed UILint components\n */\nfunction getProjectsWithInstalls(project: ProjectState): ProjectWithInstalls[] {\n const installers = getAllInstallers();\n const projects: ProjectWithInstalls[] = [];\n const seenPaths = new Set<string>();\n\n for (const installer of installers) {\n if (!installer.isApplicable(project)) continue;\n\n const targets = installer.getTargets(project);\n for (const target of targets) {\n if (!target.isInstalled) continue;\n\n // Determine project type and path\n let projectPath = target.path;\n let type: ProjectWithInstalls[\"type\"] = \"global\";\n let hint = \"Other\";\n\n if (installer.id === \"next\") {\n type = \"nextjs\";\n hint = \"Next.js App Router\";\n } else if (installer.id === \"vite\") {\n type = \"vite\";\n hint = \"Vite + React\";\n } else if (installer.id === \"eslint\") {\n type = \"eslint\";\n hint = \"ESLint\";\n } else if (installer.id === \"genstyleguide\" || installer.id === \"skill\") {\n // Global features - group them under workspace root\n projectPath = project.workspaceRoot;\n type = \"global\";\n hint = \"Global\";\n }\n\n // Avoid duplicates\n if (seenPaths.has(projectPath)) {\n // Increment count for existing project\n const existing = projects.find(p => p.path === projectPath);\n if (existing) {\n existing.installedCount++;\n }\n continue;\n }\n\n seenPaths.add(projectPath);\n const relativePath = projectPath.replace(project.workspaceRoot + \"/\", \"\");\n\n projects.push({\n id: `${type}:${projectPath}`,\n name: relativePath || \".\",\n path: projectPath,\n type,\n hint,\n installedCount: 1,\n });\n }\n }\n\n return projects;\n}\n\n/**\n * Get installed items for a specific project path\n */\nfunction getInstalledItemsForProject(\n project: ProjectState,\n selectedProject: ProjectWithInstalls | null\n): RemoveItem[] {\n const installers = getAllInstallers();\n const items: RemoveItem[] = [];\n\n for (const installer of installers) {\n if (!installer.isApplicable(project)) continue;\n\n const targets = installer.getTargets(project);\n for (const target of targets) {\n if (!target.isInstalled) continue;\n\n // Filter by selected project if one is selected\n if (selectedProject) {\n // For global installers, only show if \"global\" project is selected\n if (installer.id === \"genstyleguide\" || installer.id === \"skill\") {\n if (selectedProject.type !== \"global\") continue;\n } else {\n // For project-specific installers, match by path\n if (target.path !== selectedProject.path) continue;\n }\n }\n\n items.push({\n id: `${installer.id}:${target.id}`,\n installerId: installer.id,\n installer,\n label: installer.name,\n hint: target.hint,\n target,\n });\n }\n }\n\n return items;\n}\n\n/**\n * Header component with app branding\n */\nfunction Header({ subtitle }: { subtitle?: string }): React.ReactElement {\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text bold color=\"cyan\">◆ UILint</Text>\n <Text dimColor> v0.5.0</Text>\n {subtitle && <Text dimColor> · {subtitle}</Text>}\n </Box>\n </Box>\n );\n}\n\n/**\n * Framework badge for project selector\n */\nfunction FrameworkBadge({ type }: { type: ProjectWithInstalls[\"type\"] }): React.ReactElement {\n switch (type) {\n case \"nextjs\":\n return <Text color=\"white\" backgroundColor=\"black\"> Next.js </Text>;\n case \"vite\":\n return <Text color=\"black\" backgroundColor=\"yellow\"> Vite </Text>;\n case \"eslint\":\n return <Text color=\"white\" backgroundColor=\"blue\"> ESLint </Text>;\n case \"global\":\n return <Text color=\"white\" backgroundColor=\"magenta\"> Global </Text>;\n default:\n return <Text dimColor>Other</Text>;\n }\n}\n\n/**\n * Project selector for choosing which project to remove from\n */\nfunction ProjectSelector({\n projects,\n cursor,\n onSelect: _onSelect,\n onCancel: _onCancel,\n}: {\n projects: ProjectWithInstalls[];\n cursor: number;\n onSelect: (project: ProjectWithInstalls) => void;\n onCancel: () => void;\n}): React.ReactElement {\n return (\n <Box flexDirection=\"column\">\n <Box marginBottom={1}>\n <Text bold>Select a project to remove components from:</Text>\n </Box>\n\n {projects.map((project, index) => {\n const isCursor = index === cursor;\n return (\n <Box key={project.id} paddingLeft={1}>\n {/* Cursor */}\n <Text color={isCursor ? \"cyan\" : undefined}>\n {isCursor ? \"› \" : \" \"}\n </Text>\n\n {/* Framework badge */}\n <Box width={12}>\n <FrameworkBadge type={project.type} />\n </Box>\n\n {/* Project name */}\n <Box width={30}>\n <Text color={isCursor ? \"cyan\" : undefined} bold={isCursor}>\n {project.name}\n </Text>\n </Box>\n\n {/* Installed count */}\n <Text color=\"green\" dimColor>\n {project.installedCount} installed\n </Text>\n </Box>\n );\n })}\n\n {/* Footer */}\n <Box marginTop={1} borderStyle=\"single\" borderColor=\"gray\" paddingX={1}>\n <Text dimColor>\n <Text color=\"cyan\">↑↓</Text> navigate{\" \"}\n <Text color=\"cyan\">enter</Text> select{\" \"}\n <Text color=\"cyan\">q</Text> quit\n </Text>\n </Box>\n </Box>\n );\n}\n\n/**\n * Selection item with checkbox\n */\nfunction SelectionItem({\n item,\n isSelected,\n isCursor,\n}: {\n item: RemoveItem;\n isSelected: boolean;\n isCursor: boolean;\n}): React.ReactElement {\n return (\n <Box paddingLeft={2}>\n <Text color={isCursor ? \"cyan\" : undefined}>\n {isCursor ? \"› \" : \" \"}\n </Text>\n <Box width={2}>\n <Text color={isSelected ? \"red\" : \"gray\"}>\n {isSelected ? \"✗\" : \"○\"}\n </Text>\n </Box>\n <Box width={28}>\n <Text\n color={isSelected ? \"red\" : isCursor ? \"cyan\" : undefined}\n >\n {item.label}\n </Text>\n </Box>\n <Box width={20}>\n <Text dimColor>{item.hint || \"\"}</Text>\n </Box>\n <Text color=\"green\" dimColor>installed</Text>\n </Box>\n );\n}\n\n/**\n * Selection list component\n */\nfunction SelectionList({\n items,\n selectedIds,\n cursor,\n selectedProject,\n canGoBack,\n onBack: _onBack,\n}: {\n items: RemoveItem[];\n selectedIds: Set<string>;\n cursor: number;\n selectedProject: ProjectWithInstalls | null;\n canGoBack: boolean;\n onBack: () => void;\n}): React.ReactElement {\n return (\n <Box flexDirection=\"column\">\n {/* Show selected project context */}\n {selectedProject && (\n <Box marginBottom={1}>\n <Text dimColor>Project: </Text>\n <Text bold color=\"cyan\">{selectedProject.name}</Text>\n <Text dimColor> ({selectedProject.hint})</Text>\n </Box>\n )}\n\n <Box marginBottom={1}>\n <Text>Select components to remove:</Text>\n </Box>\n\n {items.map((item, index) => (\n <SelectionItem\n key={item.id}\n item={item}\n isSelected={selectedIds.has(item.id)}\n isCursor={index === cursor}\n />\n ))}\n\n {/* Footer with keyboard hints */}\n <Box marginTop={1} borderStyle=\"single\" borderColor=\"gray\" paddingX={1}>\n <Text dimColor>\n <Text color=\"cyan\">↑↓</Text> navigate{\" \"}\n <Text color=\"cyan\">space</Text> toggle{\" \"}\n <Text color=\"cyan\">a</Text> all{\" \"}\n <Text color=\"cyan\">n</Text> none{\" \"}\n <Text color=\"cyan\">enter</Text> remove{\" \"}\n <Text color=\"cyan\">q</Text> quit\n </Text>\n </Box>\n\n {/* Back hint if multiple projects */}\n {canGoBack && (\n <Box marginTop={1}>\n <Text dimColor>\n Press <Text color=\"cyan\">b</Text> or <Text color=\"cyan\">←</Text> to select a different project\n </Text>\n </Box>\n )}\n\n {/* Selection summary */}\n <Box marginTop={1}>\n <Text>\n <Text color=\"red\">{selectedIds.size}</Text>\n <Text dimColor> to remove</Text>\n </Text>\n </Box>\n </Box>\n );\n}\n\n/**\n * Confirmation component\n */\nfunction Confirmation({\n items,\n selectedIds,\n dryRun,\n onConfirm,\n onBack,\n}: {\n items: RemoveItem[];\n selectedIds: Set<string>;\n dryRun?: boolean;\n onConfirm: () => void;\n onBack: () => void;\n}): React.ReactElement {\n const selectedItems = items.filter((item) => selectedIds.has(item.id));\n\n useInput((input, key) => {\n if (input === \"y\" || key.return) {\n onConfirm();\n } else if (input === \"n\" || key.escape || input === \"b\") {\n onBack();\n }\n });\n\n return (\n <Box flexDirection=\"column\">\n <Box marginBottom={1}>\n <Text bold>\n {dryRun ? \"Preview: Would remove\" : \"Confirm removal of\"} {selectedItems.length} component(s):\n </Text>\n </Box>\n\n {selectedItems.map((item) => (\n <Box key={item.id} paddingLeft={2}>\n <Text color=\"red\">✗ </Text>\n <Text>{item.label}</Text>\n {item.hint && <Text dimColor> ({item.hint})</Text>}\n </Box>\n ))}\n\n <Box marginTop={1}>\n {dryRun ? (\n <Text dimColor>\n Dry run mode - no changes will be made.{\" \"}\n Press <Text color=\"cyan\">enter</Text> to see details or <Text color=\"cyan\">b</Text> to go back.\n </Text>\n ) : (\n <Text dimColor>\n Press <Text color=\"cyan\">y</Text> or <Text color=\"cyan\">enter</Text> to confirm, <Text color=\"cyan\">n</Text> or <Text color=\"cyan\">b</Text> to go back.\n </Text>\n )}\n </Box>\n </Box>\n );\n}\n\nexport function RemoveApp({\n projectPromise,\n skipConfirmation = false,\n dryRun = false,\n onComplete,\n onError,\n}: RemoveAppProps): React.ReactElement {\n const { exit } = useApp();\n const [phase, setPhase] = useState<RemovePhase>(\"loading\");\n const [project, setProject] = useState<ProjectState | null>(null);\n const [projectsWithInstalls, setProjectsWithInstalls] = useState<ProjectWithInstalls[]>([]);\n const [selectedProject, setSelectedProject] = useState<ProjectWithInstalls | null>(null);\n const [items, setItems] = useState<RemoveItem[]>([]);\n const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());\n const [cursor, setCursor] = useState(0);\n const [projectCursor, setProjectCursor] = useState(0);\n const [error, setError] = useState<Error | null>(null);\n\n // Phase 1: Load project\n useEffect(() => {\n if (phase !== \"loading\") return;\n\n projectPromise\n .then((proj) => {\n setProject(proj);\n\n const projects = getProjectsWithInstalls(proj);\n setProjectsWithInstalls(projects);\n\n if (projects.length === 0) {\n setError(new Error(\"No UILint components are installed in this project.\"));\n setPhase(\"error\");\n return;\n }\n\n // If only one project has installed components, skip project selection\n if (projects.length === 1) {\n const singleProject = projects[0]!;\n setSelectedProject(singleProject);\n const installedItems = getInstalledItemsForProject(proj, singleProject);\n setItems(installedItems);\n setPhase(\"select\");\n } else {\n setPhase(\"select-project\");\n }\n })\n .catch((err) => {\n setError(err as Error);\n setPhase(\"error\");\n onError?.(err as Error);\n });\n }, [phase, projectPromise, onError]);\n\n // Keyboard input for project selection phase\n useInput((input, key) => {\n if (phase === \"select-project\") {\n if (key.upArrow) {\n setProjectCursor((prev) => (prev > 0 ? prev - 1 : projectsWithInstalls.length - 1));\n } else if (key.downArrow) {\n setProjectCursor((prev) => (prev < projectsWithInstalls.length - 1 ? prev + 1 : 0));\n } else if (key.return) {\n const selected = projectsWithInstalls[projectCursor];\n if (selected && project) {\n setSelectedProject(selected);\n const installedItems = getInstalledItemsForProject(project, selected);\n setItems(installedItems);\n setCursor(0);\n setSelectedIds(new Set());\n setPhase(\"select\");\n }\n } else if (input === \"q\" || key.escape) {\n exit();\n }\n return;\n }\n\n if (phase !== \"select\") return;\n\n if (key.upArrow) {\n setCursor((prev) => (prev > 0 ? prev - 1 : items.length - 1));\n } else if (key.downArrow) {\n setCursor((prev) => (prev < items.length - 1 ? prev + 1 : 0));\n } else if (input === \" \") {\n // Toggle selection\n const item = items[cursor];\n if (item) {\n setSelectedIds((prev) => {\n const next = new Set(prev);\n if (next.has(item.id)) {\n next.delete(item.id);\n } else {\n next.add(item.id);\n }\n return next;\n });\n }\n } else if (input === \"a\") {\n // Select all\n setSelectedIds(new Set(items.map((item) => item.id)));\n } else if (input === \"n\") {\n // Select none\n setSelectedIds(new Set());\n } else if (key.return) {\n // Submit\n if (selectedIds.size === 0) {\n // Nothing selected - exit\n exit();\n return;\n }\n if (skipConfirmation) {\n finishSelection();\n } else {\n setPhase(\"confirm\");\n }\n } else if ((input === \"b\" || key.leftArrow) && projectsWithInstalls.length > 1) {\n // Go back to project selection\n setSelectedProject(null);\n setPhase(\"select-project\");\n } else if (input === \"q\" || key.escape) {\n exit();\n }\n });\n\n // Build installer selections from selected IDs\n const finishSelection = () => {\n if (!project) return;\n\n const selections: InstallerSelection[] = [];\n\n // Group selected items by installer\n const installerMap = new Map<string, { installer: Installer; targets: InstallTarget[] }>();\n\n for (const item of items) {\n if (!selectedIds.has(item.id)) continue;\n\n const existing = installerMap.get(item.installerId);\n if (existing) {\n existing.targets.push(item.target);\n } else {\n installerMap.set(item.installerId, {\n installer: item.installer,\n targets: [item.target],\n });\n }\n }\n\n for (const [, { installer, targets }] of installerMap) {\n selections.push({\n installer,\n targets,\n selected: true,\n });\n }\n\n onComplete(selections);\n };\n\n // Render: Loading\n if (phase === \"loading\") {\n return (\n <Box flexDirection=\"column\">\n <Header subtitle=\"Remove\" />\n <Box>\n <Spinner />\n <Text> Scanning project...</Text>\n </Box>\n </Box>\n );\n }\n\n // Render: Error\n if (phase === \"error\") {\n return (\n <Box flexDirection=\"column\">\n <Header />\n <Box>\n <Text color=\"red\">✗ </Text>\n <Text color=\"red\">{error?.message || \"An unknown error occurred\"}</Text>\n </Box>\n </Box>\n );\n }\n\n // Render: Project selection\n if (phase === \"select-project\") {\n return (\n <Box flexDirection=\"column\">\n <Header subtitle=\"Remove\" />\n <ProjectSelector\n projects={projectsWithInstalls}\n cursor={projectCursor}\n onSelect={(selected) => {\n if (project) {\n setSelectedProject(selected);\n const installedItems = getInstalledItemsForProject(project, selected);\n setItems(installedItems);\n setCursor(0);\n setSelectedIds(new Set());\n setPhase(\"select\");\n }\n }}\n onCancel={() => exit()}\n />\n </Box>\n );\n }\n\n // Render: Selection\n if (phase === \"select\") {\n return (\n <Box flexDirection=\"column\">\n <Header subtitle=\"Remove\" />\n <SelectionList\n items={items}\n selectedIds={selectedIds}\n cursor={cursor}\n selectedProject={selectedProject}\n canGoBack={projectsWithInstalls.length > 1}\n onBack={() => {\n setSelectedProject(null);\n setPhase(\"select-project\");\n }}\n />\n </Box>\n );\n }\n\n // Render: Confirmation\n if (phase === \"confirm\") {\n return (\n <Box flexDirection=\"column\">\n <Header subtitle=\"Remove\" />\n <Confirmation\n items={items}\n selectedIds={selectedIds}\n dryRun={dryRun}\n onConfirm={finishSelection}\n onBack={() => setPhase(\"select\")}\n />\n </Box>\n );\n }\n\n // Fallback\n return (\n <Box flexDirection=\"column\">\n <Header />\n <Text dimColor>Loading...</Text>\n </Box>\n );\n}\n"],"mappings":"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","names":["jsx"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/serve/dashboard/render.tsx","../src/commands/serve/dashboard/ServeDashboard.tsx","../src/commands/serve/dashboard/components/ServerHeader.tsx","../src/commands/serve/dashboard/components/WorkspaceInfo.tsx","../src/commands/serve/dashboard/components/StatsBar.tsx","../src/commands/serve/dashboard/components/BackgroundTasks.tsx","../src/commands/serve/dashboard/components/ActivityLog.tsx","../src/commands/serve/dashboard/components/HelpBar.tsx","../src/commands/serve/dashboard/components/OllamaStatus.tsx"],"sourcesContent":["/**\n * Render the dashboard using Ink\n */\n\nimport React from \"react\";\nimport { render } from \"ink\";\nimport { ServeDashboard } from \"./ServeDashboard.js\";\nimport { getDashboardStore } from \"./store.js\";\n\nexport interface RenderOptions {\n onQuit?: () => void;\n onRebuildIndex?: () => void;\n}\n\n/**\n * Intercept console.log/error/warn so stray output from ESLint rules,\n * child processes, etc. is routed to the dashboard activity log instead\n * of corrupting the Ink terminal UI. Returns a restore function.\n */\nfunction interceptConsole(): () => void {\n const origLog = console.log;\n const origError = console.error;\n const origWarn = console.warn;\n\n const route = (type: \"info\" | \"error\" | \"warning\", args: unknown[]) => {\n const message = args.map(String).join(\" \");\n if (!message.trim()) return;\n getDashboardStore().addActivity({\n type,\n message,\n isError: type === \"error\",\n isWarning: type === \"warning\",\n });\n };\n\n console.log = (...args: unknown[]) => route(\"info\", args);\n console.error = (...args: unknown[]) => route(\"error\", args);\n console.warn = (...args: unknown[]) => route(\"warning\", args);\n\n return () => {\n console.log = origLog;\n console.error = origError;\n console.warn = origWarn;\n };\n}\n\n/**\n * Render the dashboard and return cleanup function\n */\nexport function renderDashboard(options: RenderOptions = {}): {\n unmount: () => void;\n waitUntilExit: () => Promise<void>;\n} {\n // Clear the terminal so prior output doesn't mix with the Ink dashboard\n process.stdout.write(\"\\x1B[2J\\x1B[H\");\n\n // Intercept console methods so stray output goes to the activity log\n const restoreConsole = interceptConsole();\n\n const { unmount: inkUnmount, waitUntilExit: inkWaitUntilExit } = render(\n <ServeDashboard\n onQuit={options.onQuit}\n onRebuildIndex={options.onRebuildIndex}\n />\n );\n\n return {\n unmount: () => {\n restoreConsole();\n inkUnmount();\n },\n waitUntilExit: () => inkWaitUntilExit().finally(restoreConsole),\n };\n}\n","/**\n * ServeDashboard - main Ink component for the WebSocket server CLI dashboard\n */\n\nimport React, { useState, useEffect, useCallback } from \"react\";\nimport { Box, useInput, useApp } from \"ink\";\nimport {\n ServerHeader,\n WorkspaceInfo,\n StatsBar,\n BackgroundTasks,\n ActivityLog,\n HelpBar,\n} from \"./components/index.js\";\nimport type { DashboardState } from \"./types.js\";\nimport { getDashboardStore } from \"./store.js\";\n\nexport interface ServeDashboardProps {\n /** Callback when user requests to quit */\n onQuit?: () => void;\n /** Callback when user requests to rebuild index */\n onRebuildIndex?: () => void;\n}\n\nexport function ServeDashboard({\n onQuit,\n onRebuildIndex,\n}: ServeDashboardProps): React.ReactElement {\n const { exit } = useApp();\n const store = getDashboardStore();\n\n // Subscribe to store updates\n const [state, setState] = useState<DashboardState>(store.getState());\n const [scrollOffset, setScrollOffset] = useState(0);\n\n useEffect(() => {\n const unsubscribe = store.subscribe(() => {\n setState(store.getState());\n });\n return unsubscribe;\n }, [store]);\n\n // Clamp scrollOffset when activities change\n useEffect(() => {\n setScrollOffset((prev) => {\n const maxOffset = Math.max(0, state.activities.length - 15);\n return Math.min(prev, maxOffset);\n });\n }, [state.activities.length]);\n\n // Check Ollama status periodically\n useEffect(() => {\n const checkOllamaStatus = async () => {\n try {\n const response = await fetch(\"http://localhost:11434/api/tags\", {\n method: \"GET\",\n signal: AbortSignal.timeout(5000),\n });\n\n if (response.ok) {\n const data = (await response.json()) as { models?: Array<{ name?: string }> };\n const models = data.models || [];\n // Get the first model name as the \"active\" model indicator\n const modelName = models.length > 0 ? models[0].name : undefined;\n store.setOllamaStatus({\n status: \"connected\",\n model: modelName,\n lastChecked: new Date(),\n });\n } else {\n store.setOllamaStatus({\n status: \"error\",\n lastChecked: new Date(),\n });\n }\n } catch {\n store.setOllamaStatus({\n status: \"offline\",\n lastChecked: new Date(),\n });\n }\n };\n\n // Initial check\n checkOllamaStatus();\n\n // Check every 30 seconds\n const interval = setInterval(checkOllamaStatus, 30000);\n return () => clearInterval(interval);\n }, [store]);\n\n // Handle keyboard input\n useInput(\n useCallback(\n (input, key) => {\n if (input === \"q\" || (key.ctrl && input === \"c\")) {\n onQuit?.();\n exit();\n } else if (input === \"c\") {\n store.clearActivities();\n setScrollOffset(0);\n } else if (input === \"v\") {\n store.toggleVerbose();\n } else if (input === \"f\") {\n store.cycleFilter();\n setScrollOffset(0);\n } else if (input === \"r\") {\n onRebuildIndex?.();\n } else if (key.downArrow || input === \"j\") {\n setScrollOffset((prev) => {\n const maxOffset = Math.max(0, state.activities.length - 15);\n return Math.min(maxOffset, prev + 1);\n });\n } else if (key.upArrow || input === \"k\") {\n setScrollOffset((prev) => Math.max(0, prev - 1));\n }\n },\n [exit, onQuit, onRebuildIndex, store, state.activities.length]\n )\n );\n\n return (\n <Box flexDirection=\"column\" width=\"100%\">\n <ServerHeader port={state.port} isRunning={state.isRunning} />\n\n <WorkspaceInfo\n workspaceRoot={state.workspace?.workspaceRoot ?? null}\n appRoot={state.workspace?.appRoot ?? null}\n />\n\n <StatsBar\n connectedClients={state.stats.connectedClients}\n subscriptions={state.stats.subscriptions}\n cacheEntries={state.stats.cacheEntries}\n startTime={state.stats.startTime}\n ollamaStatus={state.ollamaStatus.status}\n ollamaModel={state.ollamaStatus.model}\n />\n\n <BackgroundTasks tasks={state.backgroundTasks} />\n\n <ActivityLog\n activities={state.activities}\n maxVisible={15}\n verbose={state.verbose}\n scrollOffset={scrollOffset}\n activeFilter={state.activeFilter}\n />\n\n <HelpBar verbose={state.verbose} activeFilter={state.activeFilter} />\n </Box>\n );\n}\n","/**\n * ServerHeader component - displays server status and URL\n */\n\nimport React from \"react\";\nimport { Box, Text } from \"ink\";\n\nexport interface ServerHeaderProps {\n port: number;\n isRunning: boolean;\n}\n\nexport function ServerHeader({\n port,\n isRunning,\n}: ServerHeaderProps): React.ReactElement {\n const url = `ws://localhost:${port}`;\n const statusColor = isRunning ? \"green\" : \"red\";\n const statusIcon = isRunning ? \"\\u25CF\" : \"\\u25CB\"; // Filled/hollow circle\n\n return (\n <Box\n borderStyle=\"single\"\n borderColor=\"cyan\"\n paddingX={1}\n justifyContent=\"space-between\"\n >\n <Text bold color=\"cyan\">\n UILint Server\n </Text>\n <Box>\n <Text dimColor>{url}</Text>\n <Text> </Text>\n <Text color={statusColor}>{statusIcon}</Text>\n </Box>\n </Box>\n );\n}\n","/**\n * WorkspaceInfo component - displays workspace and app root paths\n */\n\nimport React from \"react\";\nimport { Box, Text } from \"ink\";\n\nexport interface WorkspaceInfoProps {\n workspaceRoot: string | null;\n appRoot: string | null;\n}\n\nfunction truncatePath(path: string, maxLen: number = 60): string {\n if (path.length <= maxLen) return path;\n return \"...\" + path.slice(-(maxLen - 3));\n}\n\nexport function WorkspaceInfo({\n workspaceRoot,\n appRoot,\n}: WorkspaceInfoProps): React.ReactElement {\n if (!workspaceRoot && !appRoot) {\n return (\n <Box paddingX={1}>\n <Text dimColor>Initializing...</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" paddingX={1}>\n <Box>\n <Text dimColor>Workspace: </Text>\n <Text>{truncatePath(workspaceRoot || \"\")}</Text>\n </Box>\n {appRoot && appRoot !== workspaceRoot && (\n <Box>\n <Text dimColor>App Root: </Text>\n <Text>{truncatePath(appRoot)}</Text>\n </Box>\n )}\n </Box>\n );\n}\n","/**\n * StatsBar component - displays server statistics\n */\n\nimport React, { useState, useEffect } from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { OllamaStatusState } from \"../types.js\";\n\nexport interface StatsBarProps {\n connectedClients: number;\n subscriptions: number;\n cacheEntries: number;\n startTime: Date;\n ollamaStatus?: OllamaStatusState;\n ollamaModel?: string;\n}\n\nfunction formatUptime(startTime: Date): string {\n const diff = Date.now() - startTime.getTime();\n const seconds = Math.floor(diff / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n\n if (hours > 0) {\n return `${hours}h ${minutes % 60}m`;\n }\n if (minutes > 0) {\n return `${minutes}m ${seconds % 60}s`;\n }\n return `${seconds}s`;\n}\n\nconst ollamaStatusConfig: Record<\n OllamaStatusState,\n { icon: string; color: string }\n> = {\n checking: { icon: \"\\u25CB\", color: \"gray\" },\n connected: { icon: \"\\u25CF\", color: \"green\" },\n offline: { icon: \"\\u25CF\", color: \"red\" },\n error: { icon: \"\\u25CF\", color: \"yellow\" },\n};\n\nexport function StatsBar({\n connectedClients,\n subscriptions,\n cacheEntries,\n startTime,\n ollamaStatus,\n ollamaModel,\n}: StatsBarProps): React.ReactElement {\n const [, setTick] = useState(0);\n\n // Update uptime display every second\n useEffect(() => {\n const timer = setInterval(() => {\n setTick((t) => t + 1);\n }, 1000);\n return () => clearInterval(timer);\n }, []);\n\n const ollamaConfig = ollamaStatus\n ? ollamaStatusConfig[ollamaStatus]\n : ollamaStatusConfig.checking;\n\n return (\n <Box\n borderStyle=\"single\"\n borderColor=\"gray\"\n paddingX={1}\n justifyContent=\"space-between\"\n >\n <Box gap={2}>\n <Box>\n <Text dimColor>Clients: </Text>\n <Text bold color={connectedClients > 0 ? \"green\" : \"gray\"}>\n {connectedClients}\n </Text>\n </Box>\n <Box>\n <Text dimColor>Subs: </Text>\n <Text>{subscriptions}</Text>\n </Box>\n <Box>\n <Text dimColor>Ollama: </Text>\n <Text color={ollamaConfig.color}>{ollamaConfig.icon}</Text>\n {ollamaModel && ollamaStatus === \"connected\" && (\n <Text dimColor> {ollamaModel}</Text>\n )}\n </Box>\n </Box>\n <Box gap={2}>\n <Box>\n <Text dimColor>Cache: </Text>\n <Text>{cacheEntries}</Text>\n </Box>\n <Box>\n <Text dimColor>Uptime: </Text>\n <Text>{formatUptime(startTime)}</Text>\n </Box>\n </Box>\n </Box>\n );\n}\n","/**\n * BackgroundTasks component - displays background task progress\n */\n\nimport React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { BackgroundTask } from \"../types.js\";\n\nexport interface BackgroundTasksProps {\n tasks: Map<string, BackgroundTask>;\n}\n\nfunction ProgressBar({\n progress,\n width = 20,\n}: {\n progress: number;\n width?: number;\n}): React.ReactElement {\n const filled = Math.round((progress / 100) * width);\n const empty = width - filled;\n\n return (\n <Text>\n <Text color=\"cyan\">{\"\\u2588\".repeat(filled)}</Text>\n <Text dimColor>{\"\\u2591\".repeat(empty)}</Text>\n </Text>\n );\n}\n\nfunction TaskRow({ task }: { task: BackgroundTask }): React.ReactElement {\n const statusIcon = {\n idle: \"\\u25CB\", // hollow circle\n running: \"\\u25CF\", // filled circle\n complete: \"\\u2713\", // checkmark\n error: \"\\u2717\", // x mark\n }[task.status];\n\n const statusColor = {\n idle: \"gray\",\n running: \"cyan\",\n complete: \"green\",\n error: \"red\",\n }[task.status] as \"gray\" | \"cyan\" | \"green\" | \"red\";\n\n return (\n <Box>\n <Text color={statusColor}>{statusIcon} </Text>\n <Text>{task.name}: </Text>\n {task.status === \"running\" && task.progress !== undefined ? (\n <Box gap={1}>\n <ProgressBar progress={task.progress} width={15} />\n <Text dimColor>\n {task.progress.toFixed(0)}%\n {task.current !== undefined && task.total !== undefined\n ? ` (${task.current}/${task.total})`\n : \"\"}\n </Text>\n </Box>\n ) : task.status === \"complete\" ? (\n <Text color=\"green\">Complete</Text>\n ) : task.status === \"error\" ? (\n <Text color=\"red\">{task.error || \"Failed\"}</Text>\n ) : (\n <Text dimColor>{task.message || \"Idle\"}</Text>\n )}\n </Box>\n );\n}\n\nexport function BackgroundTasks({\n tasks,\n}: BackgroundTasksProps): React.ReactElement | null {\n const taskArray = Array.from(tasks.values());\n\n if (taskArray.length === 0) {\n return null;\n }\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor=\"gray\"\n paddingX={1}\n >\n <Text bold dimColor>\n Background Tasks\n </Text>\n {taskArray.map((task) => (\n <TaskRow key={task.id} task={task} />\n ))}\n </Box>\n );\n}\n","/**\n * ActivityLog component - displays recent server activity\n *\n * Supports filtering by category (errors, vision, semantic, lint)\n * and auto-expanding error details.\n */\n\nimport React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { ActivityEntry, ActivityType, ActivityCategory } from \"../types.js\";\n\nexport interface ActivityLogProps {\n activities: ActivityEntry[];\n maxVisible?: number;\n verbose?: boolean;\n scrollOffset?: number;\n activeFilter?: ActivityCategory;\n}\n\nconst filterLabels: Record<ActivityCategory, string> = {\n all: \"All\",\n errors: \"Errors\",\n vision: \"Vision\",\n semantic: \"Semantic\",\n lint: \"Lint\",\n system: \"System\",\n};\n\nfunction formatTime(date: Date): string {\n return date.toLocaleTimeString(\"en-US\", {\n hour12: false,\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n}\n\n/** Strip ANSI escape sequences from a string */\nfunction stripAnsi(str: string): string {\n // eslint-disable-next-line no-control-regex\n return str.replace(/\\x1B\\[[0-9;]*[a-zA-Z]/g, \"\").replace(/\\r/g, \"\");\n}\n\n/** Truncate a string to maxLen, appending \"…\" if truncated */\nfunction truncate(str: string, maxLen: number): string {\n const clean = stripAnsi(str).replace(/\\n/g, \" \");\n if (clean.length <= maxLen) return clean;\n return clean.slice(0, maxLen - 1) + \"…\";\n}\n\ntype InkColor =\n | \"black\" | \"red\" | \"green\" | \"yellow\" | \"blue\"\n | \"magenta\" | \"cyan\" | \"white\" | \"gray\" | \"grey\";\n\nfunction getTypeDisplay(type: ActivityType): { label: string; color: InkColor } {\n const displays: Record<ActivityType, { label: string; color: InkColor }> = {\n \"lint:file\": { label: \"lint\", color: \"blue\" },\n \"lint:element\": { label: \"lint\", color: \"blue\" },\n \"lint:done\": { label: \"lint\", color: \"green\" },\n subscribe: { label: \"sub\", color: \"cyan\" },\n \"cache:invalidate\": { label: \"cache\", color: \"yellow\" },\n \"vision:analyze\": { label: \"vision\", color: \"magenta\" },\n \"vision:done\": { label: \"vision\", color: \"green\" },\n \"vision:check\": { label: \"vision\", color: \"magenta\" },\n \"semantic:analyze\": { label: \"semant\", color: \"blue\" },\n \"semantic:done\": { label: \"semant\", color: \"green\" },\n \"semantic:error\": { label: \"semant\", color: \"red\" },\n \"config:set\": { label: \"config\", color: \"yellow\" },\n \"rule:config:set\": { label: \"rule\", color: \"yellow\" },\n \"screenshot:save\": { label: \"screen\", color: \"cyan\" },\n \"screenshot:saved\": { label: \"screen\", color: \"green\" },\n \"coverage:request\": { label: \"cov\", color: \"blue\" },\n \"coverage:result\": { label: \"cov\", color: \"green\" },\n \"file:changed\": { label: \"change\", color: \"yellow\" },\n \"client:connect\": { label: \"client\", color: \"green\" },\n \"client:disconnect\": { label: \"client\", color: \"red\" },\n error: { label: \"error\", color: \"red\" },\n warning: { label: \"warn\", color: \"yellow\" },\n info: { label: \"info\", color: \"gray\" },\n };\n\n return displays[type] || { label: type, color: \"gray\" };\n}\n\n// Max width for message/detail lines (leaves room for time + type label + padding)\nconst MAX_MSG_WIDTH = 120;\nconst MAX_DETAIL_WIDTH = 100;\n\nfunction ActivityRow({\n entry,\n verbose,\n}: {\n entry: ActivityEntry;\n verbose?: boolean;\n}): React.ReactElement {\n const { label, color } = getTypeDisplay(entry.type);\n\n // Always show detail for errors, otherwise respect verbose mode\n const showDetail = (verbose || entry.isError) && entry.detail;\n\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text dimColor>{formatTime(entry.timestamp)} </Text>\n <Text color={color} bold>\n {label.padEnd(7)}\n </Text>\n <Text color={entry.isError ? \"red\" : entry.isWarning ? \"yellow\" : undefined}>\n {truncate(entry.message, MAX_MSG_WIDTH)}\n </Text>\n </Box>\n {showDetail && (\n <Box paddingLeft={16}>\n <Text dimColor>{truncate(entry.detail!, MAX_DETAIL_WIDTH)}</Text>\n </Box>\n )}\n </Box>\n );\n}\n\nexport function ActivityLog({\n activities,\n maxVisible = 15,\n verbose = false,\n scrollOffset = 0,\n activeFilter = \"all\",\n}: ActivityLogProps): React.ReactElement {\n // Apply category filter\n const filtered =\n activeFilter !== \"all\"\n ? activities.filter((a) => a.category === activeFilter)\n : activities;\n\n const total = filtered.length;\n const start = Math.min(scrollOffset, Math.max(0, total - 1));\n const end = Math.min(start + maxVisible, total);\n const visibleActivities = filtered.slice(start, end);\n\n const isScrolled = start > 0;\n const hasMoreBelow = end < total;\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor=\"gray\"\n paddingX={1}\n flexGrow={1}\n >\n <Box justifyContent=\"space-between\">\n <Box gap={1}>\n <Text bold dimColor>\n Activity\n </Text>\n {activeFilter !== \"all\" && (\n <Text color=\"yellow\">[{filterLabels[activeFilter]}]</Text>\n )}\n </Box>\n <Box gap={1}>\n {isScrolled && (\n <Text color=\"cyan\">{\"\\u2191\"}</Text>\n )}\n {total > maxVisible && (\n <Text dimColor>\n {start + 1}-{end} of {total}\n </Text>\n )}\n {hasMoreBelow && (\n <Text color=\"cyan\">{\"\\u2193\"}</Text>\n )}\n </Box>\n </Box>\n {visibleActivities.length === 0 ? (\n <Text dimColor>\n {activeFilter !== \"all\"\n ? `No ${filterLabels[activeFilter].toLowerCase()} activity yet...`\n : \"No activity yet...\"}\n </Text>\n ) : (\n visibleActivities.map((entry) => (\n <ActivityRow key={entry.id} entry={entry} verbose={verbose} />\n ))\n )}\n </Box>\n );\n}\n","/**\n * HelpBar component - displays keyboard shortcuts\n */\n\nimport React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { ActivityCategory } from \"../types.js\";\n\nexport interface HelpBarProps {\n verbose: boolean;\n activeFilter?: ActivityCategory;\n}\n\nconst filterLabels: Record<ActivityCategory, string> = {\n all: \"all\",\n errors: \"errors\",\n vision: \"vision\",\n semantic: \"semantic\",\n lint: \"lint\",\n system: \"system\",\n};\n\nexport function HelpBar({ verbose, activeFilter = \"all\" }: HelpBarProps): React.ReactElement {\n return (\n <Box borderStyle=\"single\" borderColor=\"gray\" paddingX={1} gap={2}>\n <Box>\n <Text bold color=\"cyan\">\n q\n </Text>\n <Text dimColor> quit</Text>\n </Box>\n <Box>\n <Text bold color=\"cyan\">\n c\n </Text>\n <Text dimColor> clear log</Text>\n </Box>\n <Box>\n <Text bold color=\"cyan\">\n v\n </Text>\n <Text dimColor> verbose {verbose ? \"(on)\" : \"(off)\"}</Text>\n </Box>\n <Box>\n <Text bold color=\"cyan\">\n f\n </Text>\n <Text dimColor> filter ({filterLabels[activeFilter]})</Text>\n </Box>\n <Box>\n <Text bold color=\"cyan\">\n r\n </Text>\n <Text dimColor> rebuild index</Text>\n </Box>\n <Box>\n <Text bold color=\"cyan\">\n {\"\\u2191\\u2193\"}\n </Text>\n <Text dimColor> scroll</Text>\n </Box>\n </Box>\n );\n}\n","/**\n * OllamaStatus component - displays Ollama connection status\n */\n\nimport React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { OllamaStatusState } from \"../types.js\";\n\nexport interface OllamaStatusProps {\n status: OllamaStatusState;\n model?: string;\n}\n\nconst statusConfig: Record<\n OllamaStatusState,\n { icon: string; color: string; label: string }\n> = {\n checking: { icon: \"\\u25CB\", color: \"gray\", label: \"checking\" },\n connected: { icon: \"\\u25CF\", color: \"green\", label: \"connected\" },\n offline: { icon: \"\\u25CF\", color: \"red\", label: \"offline\" },\n error: { icon: \"\\u25CF\", color: \"red\", label: \"error\" },\n};\n\nexport function OllamaStatus({\n status,\n model,\n}: OllamaStatusProps): React.ReactElement {\n const config = statusConfig[status];\n\n return (\n <Box gap={1}>\n <Text dimColor>Ollama:</Text>\n <Text color={config.color}>{config.icon}</Text>\n <Text color={config.color}>{config.label}</Text>\n {model && status === \"connected\" && (\n <Text dimColor>({model})</Text>\n )}\n </Box>\n );\n}\n"],"mappings":"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","names":["useState","useEffect","Box","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","filterLabels","Box","Text","jsx","jsxs","jsx","jsxs","useState","useEffect","Box","jsx"]}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
discoverPlugins,
|
|
3
4
|
loadPluginESLintRules
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
loadSelectedRules
|
|
7
|
-
} from "./chunk-TKJ27W62.js";
|
|
5
|
+
} from "./chunk-WG2WZTB2.js";
|
|
8
6
|
import {
|
|
9
7
|
getInstalledRuleVersions,
|
|
10
8
|
updateManifestRule
|
|
@@ -19,6 +17,9 @@ import {
|
|
|
19
17
|
note,
|
|
20
18
|
outro
|
|
21
19
|
} from "./chunk-CZNPG4UI.js";
|
|
20
|
+
import {
|
|
21
|
+
loadSelectedRules
|
|
22
|
+
} from "./chunk-VKI3SQMQ.js";
|
|
22
23
|
import {
|
|
23
24
|
detectPackageManager,
|
|
24
25
|
getInstalledUilintPackages,
|
|
@@ -455,7 +456,8 @@ function getCliVersion() {
|
|
|
455
456
|
// src/commands/upgrade.ts
|
|
456
457
|
async function upgrade(options) {
|
|
457
458
|
intro("UILint Upgrade");
|
|
458
|
-
await
|
|
459
|
+
const pluginManifests = await discoverPlugins();
|
|
460
|
+
await loadPluginESLintRules(pluginManifests);
|
|
459
461
|
try {
|
|
460
462
|
const projectPath = process.cwd();
|
|
461
463
|
logInfo("Analyzing installed rules...");
|
|
@@ -588,4 +590,4 @@ async function upgrade(options) {
|
|
|
588
590
|
export {
|
|
589
591
|
upgrade
|
|
590
592
|
};
|
|
591
|
-
//# sourceMappingURL=upgrade-
|
|
593
|
+
//# sourceMappingURL=upgrade-TPZ62BT2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/update/analyze.ts","../src/utils/migration-engine.ts","../src/commands/update/plan.ts","../src/commands/update/execute.ts","../src/commands/upgrade.ts"],"sourcesContent":["/**\n * Analyze phase for the update command\n *\n * Compares installed rule versions against available versions\n * and identifies what can be updated.\n */\n\nimport { basename } from \"path\";\nimport { getRuleMetadata } from \"uilint-eslint\";\nimport {\n detectPackageManager,\n getInstalledUilintPackages,\n} from \"../../utils/package-manager.js\";\nimport { findWorkspaceRoot } from \"uilint-core/node\";\nimport { getInstalledRuleVersions } from \"../../utils/manifest.js\";\nimport {\n findMigrationPath,\n getMigrationsForRule,\n hasBreakingMigrations,\n} from \"../../utils/migration-engine.js\";\nimport type {\n UpdateAnalysis,\n PackageUpdateInfo,\n RuleUpdateInfo,\n InstalledPackageInfo,\n} from \"./types.js\";\n\n/**\n * Compare two semver versions\n * Returns true if available > installed\n */\nfunction isNewerVersion(installed: string, available: string): boolean {\n const installedParts = installed.split(\".\").map(Number);\n const availableParts = available.split(\".\").map(Number);\n\n for (let i = 0; i < 3; i++) {\n const a = availableParts[i] ?? 0;\n const b = installedParts[i] ?? 0;\n if (a > b) return true;\n if (a < b) return false;\n }\n\n return false;\n}\n\n/**\n * Analyze a single rule for updates\n */\nfunction analyzeRule(\n ruleId: string,\n installedVersion: string\n): RuleUpdateInfo | null {\n const ruleMeta = getRuleMetadata(ruleId);\n if (!ruleMeta) {\n // Rule no longer exists in registry\n return null;\n }\n\n const availableVersion = ruleMeta.version ?? \"1.0.0\";\n const hasUpdate = isNewerVersion(installedVersion, availableVersion);\n\n // Find migration path if update is available\n const migrations = hasUpdate\n ? findMigrationPath(\n installedVersion,\n availableVersion,\n getMigrationsForRule(ruleId)\n )\n : [];\n\n return {\n ruleId,\n installedVersion,\n availableVersion,\n hasUpdate,\n migrations,\n hasBreakingChanges: hasBreakingMigrations(migrations),\n };\n}\n\n/**\n * Analyze a package for rule updates\n */\nfunction analyzePackage(packagePath: string): PackageUpdateInfo {\n const installedVersions = getInstalledRuleVersions(packagePath);\n const rules: RuleUpdateInfo[] = [];\n\n // Check each installed rule\n for (const [ruleId, version] of Object.entries(installedVersions)) {\n const ruleInfo = analyzeRule(ruleId, version);\n if (ruleInfo) {\n rules.push(ruleInfo);\n }\n }\n\n const updatableRules = rules.filter((r) => r.hasUpdate);\n\n // Get installed uilint npm packages\n const installedPackagesMap = getInstalledUilintPackages(packagePath);\n const installedPackages: InstalledPackageInfo[] = [];\n for (const [name, version] of installedPackagesMap) {\n installedPackages.push({ name, installedVersion: version });\n }\n\n return {\n packagePath,\n displayName: basename(packagePath) || packagePath,\n eslintConfigPath: null, // TODO: detect ESLint config\n packageManager: detectPackageManager(packagePath),\n installedPackages,\n rules,\n updatableCount: updatableRules.length,\n hasBreakingChanges: updatableRules.some((r) => r.hasBreakingChanges),\n };\n}\n\n/**\n * Analyze a project for available rule updates\n *\n * @param projectPath - Path to the project (or package in a monorepo)\n * @returns Analysis of available updates\n */\nexport function analyzeForUpdates(projectPath: string): UpdateAnalysis {\n const workspaceRoot = findWorkspaceRoot(projectPath);\n const packageManager = detectPackageManager(projectPath);\n\n // For now, just analyze the single project path\n // TODO: In monorepo, scan all packages with manifests\n const packageInfo = analyzePackage(projectPath);\n const packages = [packageInfo];\n\n const totalUpdates = packages.reduce((sum, p) => sum + p.updatableCount, 0);\n const hasBreakingChanges = packages.some((p) => p.hasBreakingChanges);\n\n return {\n workspaceRoot,\n packageManager,\n packages,\n totalUpdates,\n hasBreakingChanges,\n };\n}\n\n/**\n * Get a summary of available updates for display\n */\nexport function formatUpdateSummary(analysis: UpdateAnalysis): string {\n const hasRuleUpdates = analysis.totalUpdates > 0;\n const hasPackages = analysis.packages.some(\n (p) => p.installedPackages.length > 0\n );\n\n if (!hasRuleUpdates && !hasPackages) {\n return \"No uilint packages or rules found.\";\n }\n\n const lines: string[] = [];\n\n for (const pkg of analysis.packages) {\n const hasUpdates =\n pkg.updatableCount > 0 || pkg.installedPackages.length > 0;\n if (!hasUpdates) continue;\n\n lines.push(`${pkg.displayName}:`);\n\n // Show installed npm packages\n if (pkg.installedPackages.length > 0) {\n lines.push(\" npm packages:\");\n for (const npmPkg of pkg.installedPackages) {\n lines.push(` ${npmPkg.name}: ${npmPkg.installedVersion} → latest`);\n }\n }\n\n // Show rule updates\n if (pkg.updatableCount > 0) {\n lines.push(\" rules:\");\n for (const rule of pkg.rules) {\n if (!rule.hasUpdate) continue;\n\n const breaking = rule.hasBreakingChanges ? \" (BREAKING)\" : \"\";\n lines.push(\n ` ${rule.ruleId}: ${rule.installedVersion} → ${rule.availableVersion}${breaking}`\n );\n }\n }\n\n lines.push(\"\");\n }\n\n if (analysis.hasBreakingChanges) {\n lines.push(\"⚠️ Some rule updates contain breaking changes.\");\n }\n\n return lines.join(\"\\n\");\n}\n","/**\n * Migration Engine\n *\n * Handles migrating rule options between versions using migration definitions.\n */\n\nimport { getRuleMetadata, type RuleMigration } from \"uilint-eslint\";\n\n/**\n * Find a path of migrations to get from one version to another.\n *\n * Uses BFS to find the shortest path through the migration graph.\n *\n * @param from - Starting version (installed version)\n * @param to - Target version (available version)\n * @param migrations - Available migrations for the rule\n * @returns Array of migrations to apply in order, or empty array if no path exists\n */\nexport function findMigrationPath(\n from: string,\n to: string,\n migrations: RuleMigration[]\n): RuleMigration[] {\n // No migration needed if versions are the same\n if (from === to) {\n return [];\n }\n\n // No migrations available\n if (migrations.length === 0) {\n return [];\n }\n\n // Build a map of version -> outgoing migrations\n const migrationMap = new Map<string, RuleMigration[]>();\n for (const migration of migrations) {\n const existing = migrationMap.get(migration.from) ?? [];\n existing.push(migration);\n migrationMap.set(migration.from, existing);\n }\n\n // BFS to find path\n const visited = new Set<string>();\n const queue: { version: string; path: RuleMigration[] }[] = [\n { version: from, path: [] },\n ];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n\n if (visited.has(current.version)) {\n continue;\n }\n visited.add(current.version);\n\n const outgoing = migrationMap.get(current.version) ?? [];\n for (const migration of outgoing) {\n const newPath = [...current.path, migration];\n\n // Found the target\n if (migration.to === to) {\n return newPath;\n }\n\n // Add to queue for further exploration\n if (!visited.has(migration.to)) {\n queue.push({ version: migration.to, path: newPath });\n }\n }\n }\n\n // No path found\n return [];\n}\n\n/**\n * Apply a sequence of migrations to transform options.\n *\n * @param options - The current rule options array\n * @param migrations - Migrations to apply in order\n * @returns Transformed options array\n */\nexport function applyMigrations(\n options: unknown[],\n migrations: RuleMigration[]\n): unknown[] {\n let current = options;\n\n for (const migration of migrations) {\n current = migration.migrate(current);\n }\n\n return current;\n}\n\n/**\n * Get migrations defined for a specific rule.\n *\n * @param ruleId - The rule ID to look up\n * @returns Array of migrations, or empty array if none defined\n */\nexport function getMigrationsForRule(ruleId: string): RuleMigration[] {\n const ruleMeta = getRuleMetadata(ruleId);\n if (!ruleMeta) {\n return [];\n }\n\n return ruleMeta.migrations ?? [];\n}\n\n/**\n * Check if there are any breaking migrations in a migration path.\n *\n * @param migrations - The migration path to check\n * @returns True if any migration is marked as breaking\n */\nexport function hasBreakingMigrations(migrations: RuleMigration[]): boolean {\n return migrations.some((m) => m.breaking === true);\n}\n\n/**\n * Get descriptions of all migrations in a path.\n *\n * @param migrations - The migration path\n * @returns Array of migration descriptions\n */\nexport function getMigrationDescriptions(migrations: RuleMigration[]): string[] {\n return migrations.map(\n (m) => `${m.from} → ${m.to}: ${m.description}${m.breaking ? \" (BREAKING)\" : \"\"}`\n );\n}\n","/**\n * Plan phase for the update command\n *\n * Creates a plan of actions to update rules based on analysis and user choices.\n */\n\nimport type {\n UpdateAnalysis,\n UpdatePlan,\n UpdateAction,\n UpdateChoices,\n RuleUpdateInfo,\n} from \"./types.js\";\n\n/**\n * Create an update plan based on analysis and user choices\n *\n * @param analysis - Result from the analyze phase\n * @param choices - User selections for what to update\n * @returns Plan of actions to execute\n */\nexport function createUpdatePlan(\n analysis: UpdateAnalysis,\n choices: UpdateChoices\n): UpdatePlan {\n const actions: UpdateAction[] = [];\n const rules: RuleUpdateInfo[] = [];\n\n for (const pkg of analysis.packages) {\n // Skip packages not selected\n if (!choices.packagePaths.includes(pkg.packagePath)) {\n continue;\n }\n\n // Action: Update npm packages (do this first)\n if (pkg.installedPackages.length > 0) {\n actions.push({\n type: \"update_npm_packages\",\n packagePath: pkg.packagePath,\n packageManager: pkg.packageManager,\n packages: pkg.installedPackages.map((p) => p.name),\n });\n }\n\n for (const rule of pkg.rules) {\n // Skip rules that don't have updates\n if (!rule.hasUpdate) {\n continue;\n }\n\n // Skip rules not in the specific rule filter (if set)\n if (choices.ruleIds.length > 0 && !choices.ruleIds.includes(rule.ruleId)) {\n continue;\n }\n\n // Track this rule for the plan\n rules.push(rule);\n\n // Action 1: Copy updated rule files\n actions.push({\n type: \"copy_rule_files\",\n packagePath: pkg.packagePath,\n ruleId: rule.ruleId,\n version: rule.availableVersion,\n });\n\n // Action 2: Migrate options in ESLint config (if migrations exist)\n if (rule.migrations.length > 0 && pkg.eslintConfigPath) {\n actions.push({\n type: \"migrate_rule_options\",\n configPath: pkg.eslintConfigPath,\n ruleId: rule.ruleId,\n migrations: rule.migrations,\n });\n }\n\n // Action 3: Update manifest with new version\n actions.push({\n type: \"update_manifest_version\",\n packagePath: pkg.packagePath,\n ruleId: rule.ruleId,\n version: rule.availableVersion,\n });\n }\n }\n\n return { actions, rules };\n}\n\n/**\n * Get a summary of what the plan will do\n */\nexport function formatPlanSummary(plan: UpdatePlan): string {\n if (plan.actions.length === 0) {\n return \"No updates to apply.\";\n }\n\n const lines: string[] = [];\n lines.push(`Update plan (${plan.rules.length} rule(s)):\\n`);\n\n for (const rule of plan.rules) {\n const breaking = rule.hasBreakingChanges ? \" ⚠️ BREAKING\" : \"\";\n lines.push(\n ` ${rule.ruleId}: ${rule.installedVersion} → ${rule.availableVersion}${breaking}`\n );\n\n // Show migration descriptions\n if (rule.migrations.length > 0) {\n for (const migration of rule.migrations) {\n lines.push(` - ${migration.description}`);\n }\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Check if the plan has any breaking changes that require confirmation\n */\nexport function planHasBreakingChanges(plan: UpdatePlan): boolean {\n return plan.rules.some((r) => r.hasBreakingChanges);\n}\n\n/**\n * Filter plan to only include specific rules\n */\nexport function filterPlanByRules(\n plan: UpdatePlan,\n ruleIds: string[]\n): UpdatePlan {\n if (ruleIds.length === 0) {\n return plan;\n }\n\n const ruleIdSet = new Set(ruleIds);\n const filteredRules = plan.rules.filter((r) => ruleIdSet.has(r.ruleId));\n const filteredActions = plan.actions.filter((a) => {\n if (\"ruleId\" in a) {\n return ruleIdSet.has(a.ruleId);\n }\n return true;\n });\n\n return {\n actions: filteredActions,\n rules: filteredRules,\n };\n}\n","/**\n * Execute phase for the update command\n *\n * Performs the actual file operations to update rules.\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { loadSelectedRules } from \"../../utils/rule-loader.js\";\nimport { updateManifestRule } from \"../../utils/manifest.js\";\nimport { updatePackages } from \"../../utils/package-manager.js\";\nimport type {\n UpdatePlan,\n UpdateAction,\n UpdateResult,\n UpdateActionResult,\n UpdateSummary,\n CopyRuleFilesAction,\n MigrateRuleOptionsAction,\n UpdateManifestVersionAction,\n UpdateNpmPackagesAction,\n} from \"./types.js\";\n\nexport interface ExecuteUpdateOptions {\n dryRun?: boolean;\n}\n\n/**\n * Execute an update plan\n *\n * @param plan - The plan to execute\n * @param options - Execution options\n * @returns Result of the execution\n */\nexport async function executeUpdatePlan(\n plan: UpdatePlan,\n options: ExecuteUpdateOptions = {}\n): Promise<UpdateResult> {\n const { dryRun = false } = options;\n const results: UpdateActionResult[] = [];\n const filesModified: string[] = [];\n\n for (const action of plan.actions) {\n const result = await executeAction(action, { dryRun });\n results.push(result);\n\n if (result.success && !dryRun) {\n // Track modified files\n if (action.type === \"copy_rule_files\") {\n filesModified.push(\n join(action.packagePath, \".uilint\", \"rules\", action.ruleId)\n );\n } else if (action.type === \"migrate_rule_options\") {\n filesModified.push(action.configPath);\n } else if (action.type === \"update_manifest_version\") {\n filesModified.push(\n join(action.packagePath, \".uilint\", \"rules\", \"manifest.json\")\n );\n }\n }\n }\n\n const success = results.every((r) => r.success);\n const rulesUpdated = new Set(plan.rules.map((r) => r.ruleId)).size;\n const packagesUpdated = new Set(\n plan.actions\n .filter((a) => \"packagePath\" in a)\n .map((a) => (a as { packagePath: string }).packagePath)\n ).size;\n\n const summary: UpdateSummary = {\n rulesUpdated,\n packagesUpdated,\n filesModified: [...new Set(filesModified)],\n breakingChanges: plan.rules\n .filter((r) => r.hasBreakingChanges)\n .map((r) => r.ruleId),\n };\n\n return {\n success,\n actionsPerformed: results,\n summary,\n };\n}\n\n/**\n * Execute a single update action\n */\nasync function executeAction(\n action: UpdateAction,\n options: ExecuteUpdateOptions\n): Promise<UpdateActionResult> {\n const { dryRun = false } = options;\n\n switch (action.type) {\n case \"update_npm_packages\":\n return await executeUpdateNpmPackages(action, dryRun);\n\n case \"copy_rule_files\":\n return executeCopyRuleFiles(action, dryRun);\n\n case \"migrate_rule_options\":\n return executeMigrateRuleOptions(action, dryRun);\n\n case \"update_manifest_version\":\n return executeUpdateManifestVersion(action, dryRun);\n\n default: {\n const _exhaustive: never = action;\n return {\n action: _exhaustive,\n success: false,\n error: `Unknown action type`,\n };\n }\n }\n}\n\n/**\n * Update npm packages to latest versions\n */\nasync function executeUpdateNpmPackages(\n action: UpdateNpmPackagesAction,\n dryRun: boolean\n): Promise<UpdateActionResult> {\n if (dryRun) {\n return {\n action,\n success: true,\n wouldDo: `Update npm packages: ${action.packages.join(\", \")} to latest`,\n };\n }\n\n try {\n await updatePackages(\n action.packageManager,\n action.packagePath,\n action.packages,\n { dev: true }\n );\n\n return { action, success: true };\n } catch (error) {\n return {\n action,\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n/**\n * Copy updated rule files\n */\nfunction executeCopyRuleFiles(\n action: CopyRuleFilesAction,\n dryRun: boolean\n): UpdateActionResult {\n if (dryRun) {\n return {\n action,\n success: true,\n wouldDo: `Copy rule files for ${action.ruleId} v${action.version} to ${action.packagePath}`,\n };\n }\n\n try {\n const rulesDir = join(action.packagePath, \".uilint\", \"rules\");\n\n // Ensure directory exists\n if (!existsSync(rulesDir)) {\n mkdirSync(rulesDir, { recursive: true });\n }\n\n // Load rule files (TypeScript version - can adjust based on project config)\n const [ruleFile] = loadSelectedRules([action.ruleId], {\n typescript: true,\n });\n\n if (!ruleFile) {\n return {\n action,\n success: false,\n error: `Rule ${action.ruleId} not found in registry`,\n };\n }\n\n // Write implementation file\n const implPath = join(rulesDir, ruleFile.implementation.relativePath);\n mkdirSync(dirname(implPath), { recursive: true });\n writeFileSync(implPath, ruleFile.implementation.content, \"utf-8\");\n\n // Write additional files for directory-based rules\n if (ruleFile.additionalFiles) {\n for (const file of ruleFile.additionalFiles) {\n const filePath = join(rulesDir, file.relativePath);\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, file.content, \"utf-8\");\n }\n }\n\n return { action, success: true };\n } catch (error) {\n return {\n action,\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n/**\n * Migrate rule options in ESLint config\n */\nfunction executeMigrateRuleOptions(\n action: MigrateRuleOptionsAction,\n dryRun: boolean\n): UpdateActionResult {\n if (dryRun) {\n return {\n action,\n success: true,\n wouldDo: `Migrate options for ${action.ruleId} in ${action.configPath}`,\n };\n }\n\n try {\n // TODO: Implement actual ESLint config modification\n // For now, this is a placeholder that succeeds without modifying the config\n // The actual implementation requires AST parsing and modification\n\n // Note: The migration engine is already implemented in migration-engine.ts\n // What's needed here is:\n // 1. Read the ESLint config\n // 2. Find the rule options for action.ruleId\n // 3. Apply migrations using applyMigrations()\n // 4. Write the modified config back\n\n // For now, we'll skip this and just succeed\n // Users can manually update their config if migrations are needed\n if (action.migrations.length === 0) {\n return { action, success: true };\n }\n\n // Log that manual migration may be needed\n console.warn(\n `Note: Rule ${action.ruleId} has migrations. Manual config update may be needed.`\n );\n\n return { action, success: true };\n } catch (error) {\n return {\n action,\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n/**\n * Update manifest with new version\n */\nfunction executeUpdateManifestVersion(\n action: UpdateManifestVersionAction,\n dryRun: boolean\n): UpdateActionResult {\n if (dryRun) {\n return {\n action,\n success: true,\n wouldDo: `Update manifest for ${action.ruleId} to v${action.version}`,\n };\n }\n\n try {\n const cliVersion = getCliVersion();\n updateManifestRule(\n action.packagePath,\n action.ruleId,\n action.version,\n cliVersion\n );\n\n return { action, success: true };\n } catch (error) {\n return {\n action,\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n/**\n * Get CLI version from package.json\n */\nfunction getCliVersion(): string {\n try {\n const __dirname = dirname(fileURLToPath(import.meta.url));\n const pkgPath = join(__dirname, \"..\", \"..\", \"..\", \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as {\n version?: string;\n };\n return pkg.version || \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n","/**\n * Upgrade command - update installed rules to latest versions\n *\n * Usage:\n * uilint upgrade # Interactive upgrade\n * uilint upgrade --check # Show available updates only\n * uilint upgrade --yes # Auto-confirm all updates\n * uilint upgrade --dry-run # Show what would change\n * uilint upgrade --rule <id> # Upgrade specific rule\n */\n\nimport {\n analyzeForUpdates,\n formatUpdateSummary,\n} from \"./update/analyze.js\";\nimport {\n createUpdatePlan,\n formatPlanSummary,\n planHasBreakingChanges,\n} from \"./update/plan.js\";\nimport { executeUpdatePlan } from \"./update/execute.js\";\nimport type { UpdateChoices } from \"./update/types.js\";\nimport {\n intro,\n outro,\n logInfo,\n logSuccess,\n logError,\n logWarning,\n note,\n confirm,\n} from \"../utils/prompts.js\";\nimport { discoverPlugins, loadPluginESLintRules } from \"../utils/plugin-loader.js\";\n\nexport interface UpgradeCommandOptions {\n check?: boolean;\n yes?: boolean;\n dryRun?: boolean;\n rule?: string;\n}\n\n/**\n * Run the upgrade command\n */\nexport async function upgrade(options: UpgradeCommandOptions): Promise<void> {\n intro(\"UILint Upgrade\");\n\n // Load plugin ESLint rules so plugin rules appear in the registry\n const pluginManifests = await discoverPlugins();\n await loadPluginESLintRules(pluginManifests);\n\n try {\n const projectPath = process.cwd();\n\n // Phase 1: Analyze\n logInfo(\"Analyzing installed rules...\");\n const analysis = analyzeForUpdates(projectPath);\n\n const hasPackageUpdates = analysis.packages.some(\n (p) => p.installedPackages.length > 0\n );\n\n if (analysis.totalUpdates === 0 && !hasPackageUpdates) {\n logSuccess(\"All packages and rules are up to date!\");\n outro(\"No upgrades needed\");\n return;\n }\n\n // Display available updates\n note(formatUpdateSummary(analysis), \"Available Updates\");\n\n // --check mode: just show what's available\n if (options.check) {\n outro(\"Run 'uilint upgrade' to apply updates\");\n return;\n }\n\n // Filter by specific rule if requested\n let ruleIds: string[] = [];\n if (options.rule) {\n const ruleExists = analysis.packages.some((pkg) =>\n pkg.rules.some((r) => r.ruleId === options.rule && r.hasUpdate)\n );\n\n if (!ruleExists) {\n logError(`Rule '${options.rule}' not found or already up to date`);\n outro(\"Upgrade cancelled\");\n process.exit(1);\n }\n\n ruleIds = [options.rule];\n logInfo(`Upgrading only: ${options.rule}`);\n }\n\n // Phase 2: Plan\n const choices: UpdateChoices = {\n packagePaths: analysis.packages.map((p) => p.packagePath),\n ruleIds,\n confirmBreaking: false,\n };\n\n const plan = createUpdatePlan(analysis, choices);\n\n if (plan.actions.length === 0) {\n logSuccess(\"No updates to apply\");\n outro(\"Done\");\n return;\n }\n\n // Display plan\n note(formatPlanSummary(plan), \"Upgrade Plan\");\n\n // Handle breaking changes\n if (planHasBreakingChanges(plan) && !options.yes) {\n logWarning(\"This upgrade includes breaking changes!\");\n const confirmed = await confirm({\n message: \"Continue with breaking changes?\",\n initialValue: false,\n });\n\n if (!confirmed) {\n outro(\"Upgrade cancelled\");\n return;\n }\n }\n\n // Confirm upgrade (unless --yes)\n if (!options.yes && !options.dryRun) {\n const npmPkgCount = plan.actions.filter(\n (a) => a.type === \"update_npm_packages\"\n ).length;\n const rulePkgCount = plan.rules.length;\n\n const parts: string[] = [];\n if (npmPkgCount > 0) {\n parts.push(`${npmPkgCount} package(s)`);\n }\n if (rulePkgCount > 0) {\n parts.push(`${rulePkgCount} rule(s)`);\n }\n\n const confirmed = await confirm({\n message: `Apply ${parts.join(\" and \")} upgrade?`,\n initialValue: true,\n });\n\n if (!confirmed) {\n outro(\"Upgrade cancelled\");\n return;\n }\n }\n\n // Phase 3: Execute\n if (options.dryRun) {\n logInfo(\"Dry run mode - no changes will be made\");\n }\n\n const result = await executeUpdatePlan(plan, {\n dryRun: options.dryRun,\n });\n\n // Report results\n if (options.dryRun) {\n logInfo(\"Dry run complete. Would perform:\");\n for (const action of result.actionsPerformed) {\n if (action.wouldDo) {\n console.log(` • ${action.wouldDo}`);\n }\n }\n } else if (result.success) {\n // Count npm package updates\n const npmUpdates = plan.actions.filter(\n (a) => a.type === \"update_npm_packages\"\n ).length;\n\n const parts: string[] = [];\n if (npmUpdates > 0) {\n parts.push(`${npmUpdates} package(s)`);\n }\n if (result.summary.rulesUpdated > 0) {\n parts.push(`${result.summary.rulesUpdated} rule(s)`);\n }\n\n logSuccess(`Upgraded ${parts.join(\" and \")}`);\n\n if (result.summary.filesModified.length > 0) {\n note(\n result.summary.filesModified.map((f) => ` ${f}`).join(\"\\n\"),\n \"Modified files\"\n );\n }\n\n if (result.summary.breakingChanges.length > 0) {\n logWarning(\"Breaking changes applied to:\");\n for (const rule of result.summary.breakingChanges) {\n console.log(` • ${rule}`);\n }\n console.log(\n \"\\nPlease review your ESLint config for any manual adjustments.\"\n );\n }\n } else {\n logError(\"Some upgrades failed\");\n for (const action of result.actionsPerformed) {\n if (!action.success && action.error) {\n console.log(` • ${action.error}`);\n }\n }\n process.exit(1);\n }\n\n outro(\"Upgrade complete\");\n } catch (error) {\n logError(error instanceof Error ? error.message : \"Upgrade failed\");\n process.exit(1);\n }\n}\n"],"mappings":"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","names":["getRuleMetadata","getRuleMetadata"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uilint",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.142",
|
|
4
4
|
"description": "CLI for UILint - AI-powered UI consistency checking",
|
|
5
5
|
"author": "Peter Suggate",
|
|
6
6
|
"repository": {
|
|
@@ -49,13 +49,14 @@
|
|
|
49
49
|
"react": "^19.2.3",
|
|
50
50
|
"typescript": "^5.9.3",
|
|
51
51
|
"ws": "^8.19.0",
|
|
52
|
-
"uilint-core": "0.2.
|
|
53
|
-
"uilint-duplicates": "0.2.
|
|
54
|
-
"uilint-eslint": "0.2.
|
|
52
|
+
"uilint-core": "0.2.142",
|
|
53
|
+
"uilint-duplicates": "0.2.142",
|
|
54
|
+
"uilint-eslint": "0.2.142"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"uilint-vision": "0.2.
|
|
58
|
-
"uilint-
|
|
57
|
+
"uilint-vision": "0.2.142",
|
|
58
|
+
"uilint-duplicates": "0.2.142",
|
|
59
|
+
"uilint-semantic": "0.2.142"
|
|
59
60
|
},
|
|
60
61
|
"peerDependenciesMeta": {
|
|
61
62
|
"uilint-vision": {
|
|
@@ -63,6 +64,9 @@
|
|
|
63
64
|
},
|
|
64
65
|
"uilint-semantic": {
|
|
65
66
|
"optional": true
|
|
67
|
+
},
|
|
68
|
+
"uilint-duplicates": {
|
|
69
|
+
"optional": true
|
|
66
70
|
}
|
|
67
71
|
},
|
|
68
72
|
"optionalDependencies": {
|
|
@@ -79,7 +83,7 @@
|
|
|
79
83
|
"ink-testing-library": "^4.0.0",
|
|
80
84
|
"tsup": "^8.5.1",
|
|
81
85
|
"vitest": "^4.0.17",
|
|
82
|
-
"uilint-react": "0.2.
|
|
86
|
+
"uilint-react": "0.2.142"
|
|
83
87
|
},
|
|
84
88
|
"keywords": [
|
|
85
89
|
"cli",
|
|
@@ -98,6 +102,7 @@
|
|
|
98
102
|
"dev": "tsup --watch",
|
|
99
103
|
"typecheck": "tsc --noEmit",
|
|
100
104
|
"lint": "eslint src/",
|
|
105
|
+
"lint:strict": "eslint src/ --max-warnings 0",
|
|
101
106
|
"test": "vitest",
|
|
102
107
|
"test:watch": "vitest --watch",
|
|
103
108
|
"test:unit": "vitest run test/unit",
|