CLI tool to scan repositories for UI pattern annotations and generate usage reports
bash
Install globally
npm install -g uipool
Or use with npx
npx uipool scan
`
Usage
$3
Scan the current directory and generate a full insights report:
`bash
uipool scan
`
As of v0.3.0, scan includes auto-detection and usage analysis by default. This will:
1. Scan all supported files in the current directory
2. Extract pattern annotations from comments (// uipool-pattern: ...)
3. Auto-detect components by folder structure (no annotations required)
4. Analyze usage to find where components are imported
5. Generate rich insights: top used, unused, pattern suggestions, clusters
6. Write uipool-report.json to the current directory
To disable auto-detection or usage analysis, use --no-auto or --no-usage.
$3
`bash
uipool scan [options]
Options:
-r, --root Directory to scan (default: current directory)
-o, --out Output JSON file path (default: /uipool-report.json)
-p, --project Override project name (default: inferred from package.json)
-e, --ext Comma-separated file extensions to scan
-i, --ignore Comma-separated ignore globs to append to defaults
--strict Only accept namespaced markers (uipool-pattern, uipool-component)
--warn-legacy Warn about legacy pattern: annotations (default: true)
--no-warn-legacy Suppress warnings about legacy pattern: annotations
--auto Auto-scan mode: discover components by folder name (default: true)
--no-auto Disable auto-scan mode (only detect tagged annotations)
--usage Detect component usage across the codebase (default: true)
--no-usage Disable usage detection
--fail-on-unused Exit with code 1 if unused components are found
--summary-json Output a one-line JSON summary to stdout (CI-friendly)
--max-depth Maximum directory depth for scanning (auto mode)
--cluster-min-components Min components per cluster (default: 1)
--cluster-min-files Min files per cluster (default: 1)
--cluster-max Max clusters in output (default: 200)
--top-used Number of top used components to include (default: 20)
`
$3
`bash
Scan a specific directory
uipool scan --root ./src
Custom output path
uipool scan --out ./reports/patterns.json
Override project name
uipool scan --project "My App"
Scan only TypeScript and Vue files
uipool scan --ext .ts,.vue
Add custom ignore patterns
uipool scan --ignore "/legacy/,/deprecated/"
Strict mode - only namespaced annotations
uipool scan --strict
Suppress legacy warnings
uipool scan --no-warn-legacy
`
$3
Auto-scan discovers components automatically by folder structure, without requiring annotations. Combined with usage detection, it generates rich insights.
As of v0.3.0, auto-scan and usage detection are enabled by default. Simply run:
`bash
uipool scan -r ./app
`
To customize or tune the output:
`bash
Full insights with cluster tuning
uipool scan \
--cluster-min-components 2 \
--cluster-min-files 2 \
--top-used 10 \
-r ./app
Fail CI if unused components exist
uipool scan --fail-on-unused -r ./app
Disable auto-scan (only tagged patterns/components)
uipool scan --no-auto --no-usage -r ./app
`
What auto-scan provides:
- Components: Discovers Vue/React/Angular components by folder naming conventions
- Usage detection: Tracks where each component is imported/used across your codebase
- Candidate patterns: Suggests pattern groupings based on folder structure
- Usage clusters: Groups components by where they're used together (route-based)
- Top used: Ranks components by usage frequency
- Unused detection: Identifies components with no detected imports
- Risk signals: Flags potential issues (e.g., high unused ratio)
$3
Use --summary-json to output a compact one-line JSON summary for CI pipelines:
`bash
Auto-scan with usage detection and JSON summary
uipool scan --auto --usage --summary-json -r ./app -o report.json
`
This prints a single JSON line to stdout containing:
`json
{
"version": "0.3.0",
"mode": "auto",
"root": "./app",
"projectName": "app",
"generatedAt": "2026-01-13T12:00:00.000Z",
"filesScanned": 83,
"totalComponents": 15,
"unusedComponents": 2,
"unusedPercent": 13.3,
"totalCandidatePatterns": 3,
"totalUsageClusters": 9,
"topClusters": [
{ "clusterId": "pages/docs", "componentCount": 4, "fileCount": 14 }
],
"topUnused": ["copy-code", "preview-mode-banner"],
"riskSignals": ["unused-components-found"],
"reportPath": "./app/uipool-report.json",
"reportWritten": true,
"exitCodeHint": 0,
"flags": {
"auto": true,
"usage": true,
"failOnUnused": false,
"summaryJson": true
}
}
`
Summary fields:
| Field | Description |
| --------------- | ------------------------------------------------------------------------ |
| reportPath | Normalized path to the written report file (forward slashes) |
| reportWritten | true if report was written successfully, false if write failed |
| exitCodeHint | Expected exit code: 1 if --fail-on-unused and unused exist, else 0 |
When --summary-json is enabled:
- Normal verbose console output is suppressed
- Exactly one line of JSON is printed to stdout
- The full report is still written to the output file
With --fail-on-unused: The summary is printed before exiting with code 1 if unused components exist:
`bash
uipool scan --auto --usage --summary-json --fail-on-unused -r ./app
Prints JSON summary, then exits with code 1 if unused components found
`
Annotation Formats
UiPool supports two types of annotations: patterns and components.
$3
Use the namespaced uipool-pattern: annotation for future-proof scanning:
`javascript
// uipool-pattern: login-flow
function LoginPage() { ... }
/ uipool-pattern: data-table /
const TableComponent = ...
`
`python
uipool-pattern: user-dashboard
class DashboardView:
pass
`
$3
For backward compatibility, the original pattern: annotation is still supported:
`javascript
// pattern: login-flow
function LoginPage() { ... }
`
> Note: We recommend migrating to uipool-pattern: for future-proof scanning. Use --strict mode to enforce namespaced annotations only.
$3
Track component usage with uipool-component::
`javascript
// uipool-component: button-primary
export const PrimaryButton = () => { ... }
/ uipool-component: modal-dialog /
export function ModalDialog() { ... }
`
Components will appear in a separate components array in the report, enabling pattern-to-component mapping in UiPool.
$3
For Angular templates and HTML files, use HTML comments:
`html
`
$3
| Syntax | Languages |
| -------------- | ------------------------------------------------- |
| // ... | JavaScript, TypeScript, JSX, TSX, CSS, SCSS, LESS |
| / ... / | JavaScript, TypeScript, CSS, SCSS, LESS, Vue |
| # ... | Markdown, Python, YAML |
| | HTML, Vue templates, Angular templates, MDX |
$3
Extra spaces are allowed in all formats:
`javascript
// uipool-pattern: login-flow
/ uipool-component: button-primary /
`
`html
`
$3
The annotation keywords are case-insensitive:
`javascript
// uipool-pattern: login-flow
// UIPOOL-PATTERN: login-flow
// UiPool-Pattern: login-flow
`
$3
Pattern and component IDs can contain:
- Letters (a-z, A-Z)
- Numbers (0-9)
- Dashes (-)
- Underscores (\_)
Examples: login-flow, data_table, UserDashboard, form-wizard-v2, button-primary
Output Format
The generated report follows this structure:
`json
{
"generatedAt": "2026-01-13T21:43:49.381Z",
"projectName": "MyApp",
"entries": [
{
"patternId": "login-flow",
"count": 4,
"files": ["app/pages/auth/login.vue", "app/pages/index.vue"]
}
],
"components": [
{
"componentId": "button-primary",
"count": 2,
"files": ["src/components/Button.tsx"],
"usedCount": 5,
"usedInFiles": ["src/App.tsx", "src/Dashboard.tsx"],
"unused": false,
"usageSignals": {
"importRefs": 3,
"templateTagRefs": 5,
"dynamicImportRefs": 0,
"registryRefs": 0,
"globalRegRefs": 0
}
},
{
"componentId": "deprecated-widget",
"count": 1,
"files": ["src/components/DeprecatedWidget.tsx"],
"usedCount": 0,
"usedInFiles": [],
"unused": true,
"unusedReason": "No imports, template usage, or dynamic references detected",
"usageSignals": {
"importRefs": 0,
"templateTagRefs": 0,
"dynamicImportRefs": 0,
"registryRefs": 0,
"globalRegRefs": 0
}
}
],
"candidatePatterns": [
{
"patternId": "auth",
"componentIds": ["login-form", "signup-form"],
"files": [
"components/auth/LoginForm.vue",
"components/auth/SignupForm.vue"
],
"count": 2,
"source": "folder",
"rationale": "Components grouped by auth/ folder"
},
{
"patternId": "co-usage-1",
"componentIds": ["data-table", "pagination", "filter-bar"],
"files": ["pages/users.vue", "pages/orders.vue"],
"count": 3,
"source": "coUsage",
"confidence": 0.85,
"rationale": "Strong correlation: components frequently co-occur"
}
],
"usageClusters": [
{
"clusterId": "pages/auth",
"componentIds": ["login-form", "auth-header"],
"files": ["pages/auth/login.vue", "pages/auth/signup.vue"],
"componentCount": 2,
"fileCount": 2
}
],
"topUsedComponents": [
{
"componentId": "base-button",
"usedCount": 42,
"usedInFiles": ["src/App.vue", "src/Dashboard.vue"]
}
],
"unusedSummary": {
"totalComponents": 15,
"unusedCount": 2,
"unusedPercent": 13.3
},
"unusedByGroup": [
{
"groupId": "legacy",
"unusedCount": 2,
"componentIds": ["old-button", "old-card"]
}
],
"highlightClusters": [
{
"clusterId": "pages/docs",
"componentIds": ["docs-header", "docs-section"],
"componentCount": 4,
"fileCount": 14,
"rationale": "Shared set powering a route group"
}
],
"riskSignals": [
{
"type": "unused-components-found",
"message": "2 unused components detected",
"componentIds": ["old-button", "old-card"]
}
]
}
`
$3
| Field | Description |
| --------------------------- | ---------------------------------------------------------------- |
| generatedAt | ISO 8601 timestamp of when the report was generated |
| projectName | Name of the project (from package.json or folder name) |
| entries | Array of pattern entries, sorted alphabetically by patternId |
| entries[].patternId | The pattern identifier |
| entries[].count | Total number of occurrences across all files |
| entries[].files | Unique list of files containing the pattern |
| components | Array of component entries (from --auto mode or annotations) |
| components[].componentId | The component identifier |
| components[].source | "tagged" (via comment) or "auto" (folder detection) (v0.3.0) |
| components[].count | Total number of occurrences across all files |
| components[].files | Unique list of files containing the component |
| components[].usedCount | Number of files where component is used (with --usage) |
| components[].usedInFiles | Files where component is imported/used (with --usage) |
| components[].unused | true if component has no detected usage (with --usage) |
| components[].usageSignals | Detailed usage signal counts (v0.3.1) |
| components[].unusedReason | Human-readable explanation when unused=true (v0.3.1) |
$3
Each component includes a usageSignals object with detailed detection counts:
| Signal | Description |
| ------------------- | ------------------------------------------------------------------ |
| importRefs | Explicit import statements (default/named imports) |
| templateTagRefs | Template usage ( or ) |
| dynamicImportRefs | Dynamic imports (import('./MyComp.vue'), defineAsyncComponent) |
| registryRefs | Component registry references (components: { MyComp }) |
| globalRegRefs | Global registrations (app.component(), resolveComponent()) |
Unused detection logic: A component is marked unused=true only if ALL signals are 0 AND usedInFiles is empty. This multi-signal approach reduces false positives from dynamic components, auto-imports, or global registrations.
$3
These fields are included when using --auto --usage mode:
| Field | Description |
| ---------------------------------- | ------------------------------------------------------------------ |
| candidatePatterns | Suggested pattern groupings based on folder structure and co-usage |
| candidatePatterns[].patternId | Suggested pattern name (from folder or cluster ID) |
| candidatePatterns[].componentIds | Components that would belong to this pattern |
| candidatePatterns[].source | "folder" or "coUsage" (v0.3.1) |
| candidatePatterns[].confidence | Confidence score 0-1 for coUsage patterns (v0.3.1) |
| candidatePatterns[].rationale | Human-readable explanation (v0.3.1) |
| usageClusters | Groups of components used together in the same files |
| usageClusters[].clusterId | Cluster identifier (typically a route/page path) |
| usageClusters[].componentIds | Components used in this cluster |
| usageClusters[].componentCount | Number of components in the cluster |
| usageClusters[].fileCount | Number of files in the cluster |
| topUsedComponents | Most frequently used components (limit: --top-used) |
| unusedSummary | Summary of unused component statistics |
| unusedSummary.totalComponents | Total number of components scanned |
| unusedSummary.unusedCount | Number of unused components |
| unusedSummary.unusedPercent | Percentage of components that are unused |
| unusedByGroup | Unused components grouped by folder |
| highlightClusters | Notable clusters with high component reuse |
| highlightClusters[].rationale | Why this cluster is highlighted |
| riskSignals | Actionable warnings about codebase health |
| riskSignals[].type | Signal type (e.g., unused-components-found) |
| riskSignals[].message | Human-readable description |
> Note: Insight fields are only populated when using --auto --usage mode. They enable the Insights tab in the UiPool web dashboard.
Default Behavior
$3
By default, these extensions are scanned:
- .js - JavaScript
- .ts - TypeScript
- .vue - Vue components
- .jsx - React JSX
- .tsx - React TypeScript
- .md - Markdown
- .html - HTML / Angular templates
- .css - CSS
- .scss - SCSS
- .sass - Sass
- .less - Less
- .mdx - MDX
$3
These directories are ignored by default:
- node_modules
- dist
- build
- .output
- .nuxt
- .next
- .git
- coverage
- .turbo
- .cache
Framework Examples
$3
`tsx
// uipool-pattern: user-profile
// uipool-component: avatar
export function UserProfile({ user }) {
return (
{user.name}
);
}
`
$3
`html
`
$3
`vue
`
Integration with UiPool
Upload the generated uipool-report.json` to UiPool to: