
Security News
npm Adopts OIDC for Trusted Publishing in CI/CD Workflows
npm now supports Trusted Publishing with OIDC, enabling secure package publishing directly from CI/CD workflows without relying on long-lived tokens.
clack-tree-select
Advanced tools
Beautiful, interactive tree selection prompts for Clack CLI apps
"Make your React & Vue apps multilingual in minutes!"
Beautiful, interactive tree selection prompts for command-line applications. Built on top of Clack for the best CLI experience.
โถ๏ธ Watch Demo on Asciinema
See navigation, hierarchical selection, and smart keyboard shortcuts in action.
npm install clack-tree-select
yarn add clack-tree-select
pnpm add clack-tree-select
Add tree selection to your existing Clack CLI:
import { treeSelect } from 'clack-tree-select';
import { intro, outro, text, isCancel } from '@clack/prompts';
async function main() {
intro('๐ Project Setup');
// Your existing Clack prompts
const projectName = await text({
message: 'Project name?',
placeholder: 'my-project'
});
if (isCancel(projectName)) return;
// Add tree selection seamlessly
const files = await treeSelect({
message: 'Select files to process:',
tree: [
{
value: 'src',
name: 'src',
children: [
{ value: 'src/components', name: 'components' },
{ value: 'src/pages', name: 'pages' },
{ value: 'src/utils', name: 'utils' }
]
},
{ value: 'package.json', name: 'package.json' }
]
});
if (isCancel(files)) return;
outro(`โ
${projectName}: Selected ${files.length} files`);
}
main();
Perfect integration - works alongside all your existing Clack prompts! ๐ฏ
While clack-tree-select
works great with file systems, it's designed as a generic tree selection tool. You can create hierarchical selection prompts for any data structure! Here are some powerful examples:
const components = await treeSelect({
message: 'Select UI components to include:',
tree: [
{
value: 'forms',
name: 'Form Components',
children: [
{ value: 'forms/input', name: 'Text Input' },
{ value: 'forms/select', name: 'Select Dropdown' },
{ value: 'forms/checkbox', name: 'Checkbox' },
{ value: 'forms/radio', name: 'Radio Button' }
]
},
{
value: 'navigation',
name: 'Navigation',
children: [
{ value: 'nav/header', name: 'Header' },
{ value: 'nav/sidebar', name: 'Sidebar' },
{ value: 'nav/breadcrumb', name: 'Breadcrumb' },
{ value: 'nav/pagination', name: 'Pagination' }
]
},
{
value: 'feedback',
name: 'Feedback',
children: [
{ value: 'feedback/alert', name: 'Alert' },
{ value: 'feedback/toast', name: 'Toast' },
{ value: 'feedback/modal', name: 'Modal' },
{ value: 'feedback/tooltip', name: 'Tooltip' }
]
}
]
});
interface Department {
id: string;
name: string;
manager?: string;
}
const departments = await treeSelect<Department>({
message: 'Select departments for the training program:',
tree: [
{
value: { id: 'eng', name: 'Engineering', manager: 'Alice' },
name: 'Engineering',
children: [
{ value: { id: 'frontend', name: 'Frontend' }, name: 'Frontend Team' },
{ value: { id: 'backend', name: 'Backend' }, name: 'Backend Team' },
{ value: { id: 'devops', name: 'DevOps' }, name: 'DevOps Team' }
]
},
{
value: { id: 'product', name: 'Product', manager: 'Bob' },
name: 'Product',
children: [
{ value: { id: 'design', name: 'Design' }, name: 'Design Team' },
{ value: { id: 'pm', name: 'Product Management' }, name: 'Product Managers' },
{ value: { id: 'research', name: 'User Research' }, name: 'Research Team' }
]
}
]
});
const categories = await treeSelect({
message: 'Select product categories to feature:',
tree: [
{
value: 'electronics',
name: 'Electronics',
children: [
{
value: 'computers',
name: 'Computers',
children: [
{ value: 'laptops', name: 'Laptops' },
{ value: 'desktops', name: 'Desktops' },
{ value: 'tablets', name: 'Tablets' }
]
},
{
value: 'phones',
name: 'Mobile Phones',
children: [
{ value: 'smartphones', name: 'Smartphones' },
{ value: 'accessories', name: 'Phone Accessories' }
]
}
]
},
{
value: 'clothing',
name: 'Clothing',
children: [
{ value: 'mens', name: "Men's Clothing" },
{ value: 'womens', name: "Women's Clothing" },
{ value: 'kids', name: "Kids' Clothing" }
]
}
]
});
interface Permission {
id: string;
scope: string;
level: 'read' | 'write' | 'admin';
}
const permissions = await treeSelect<Permission>({
message: 'Select permissions for this role:',
tree: [
{
value: { id: 'users', scope: 'users', level: 'admin' },
name: 'User Management',
children: [
{
value: { id: 'users-read', scope: 'users', level: 'read' },
name: 'View Users'
},
{
value: { id: 'users-write', scope: 'users', level: 'write' },
name: 'Edit Users'
},
{
value: { id: 'users-admin', scope: 'users', level: 'admin' },
name: 'Manage Users'
}
]
},
{
value: { id: 'content', scope: 'content', level: 'admin' },
name: 'Content Management',
children: [
{
value: { id: 'content-read', scope: 'content', level: 'read' },
name: 'View Content'
},
{
value: { id: 'content-write', scope: 'content', level: 'write' },
name: 'Edit Content'
}
]
}
]
});
name
property controls what users see, value
is what you get backThe generic TreeItem<T>
interface makes it perfect for any hierarchical data structure in your applications!
treeSelect(options)
Creates an interactive tree selection prompt.
Option | Type | Default | Description |
---|---|---|---|
message | string | required | The prompt message to display |
tree | TreeItem[] | required | The tree structure to display |
multiple | boolean | true | Allow multiple selections |
initialValues | T[] | [] | Pre-selected values |
required | boolean | false | Require at least one selection |
maxItems | number | undefined | Maximum visible items (scrolling) |
icons | IconOptions | default icons | Custom icons for tree items |
showHelp | boolean | true | Show keyboard shortcuts in validation |
interface TreeItem<T = any> {
value: T; // Unique identifier
name?: string; // Display name (falls back to value)
open?: boolean; // Initially expanded
children?: TreeItem<T>[] | string[] | T[]; // Child items
}
interface IconOptions {
directory?: string; // Default: '๐'
file?: string; // Default: '๐'
expanded?: string; // Default: 'โผ'
collapsed?: string; // Default: 'โถ'
}
fileSystemTreeSelect(options)
Creates a tree selection prompt from a file system directory.
const files = await fileSystemTreeSelect({
message: 'Select files from your project:',
root: './src',
includeFiles: true,
includeHidden: false,
maxDepth: 3,
filter: (path) => !path.includes('node_modules')
});
Shortcut | Action |
---|---|
โ / โ | Navigate up/down |
โ / โ | Collapse/expand directory |
Space | Toggle selection |
Ctrl+E | Toggle expand/collapse all |
Ctrl+A | Toggle select/deselect all |
Enter | Submit selection |
Ctrl+C | Cancel prompt |
const result = await treeSelect({
message: 'Choose components to generate:',
tree: components,
icons: {
directory: '๐',
file: 'โ๏ธ',
expanded: '๐',
collapsed: '๐'
},
multiple: true,
required: true
});
const configFile = await treeSelect({
message: 'Choose a configuration file:',
tree: [
{
value: 'config',
name: 'config',
open: true,
children: [
{ value: 'tsconfig.json', name: 'TypeScript Config' },
{ value: 'package.json', name: 'Package Config' },
{ value: 'vite.config.js', name: 'Vite Config' }
]
}
],
multiple: false,
required: true
});
Note: In single selection mode, only leaf nodes (files without children) can be selected. Parent directories are shown but not selectable, ensuring users select actual items rather than containers.
import { fileSystemTreeSelect } from 'clack-tree-select';
const selectedFiles = await fileSystemTreeSelect({
message: 'Select files to process:',
root: process.cwd(),
includeFiles: true,
includeHidden: false,
maxDepth: 3,
filter: (path) => {
// Exclude common non-source directories
return !path.includes('node_modules') &&
!path.includes('.git') &&
!path.includes('dist');
}
});
const result = await treeSelect({
message: 'Select at least 2 components:',
tree: componentTree,
validate: (selected) => {
if (!selected || selected.length < 2) {
return 'Please select at least 2 components to continue.';
}
}
});
// Ask user to select packages to install
const packages = await treeSelect({
message: 'Select packages to install:',
tree: [
{
value: 'react',
name: 'React',
children: [
{ value: 'react-dom', name: 'React DOM' },
{ value: 'react-router', name: 'React Router' }
]
},
{
value: 'build-tools',
name: 'Build Tools',
children: [
{ value: 'vite', name: 'Vite' },
{ value: 'typescript', name: 'TypeScript' }
]
}
]
});
// Select packages in a monorepo
const workspaces = await fileSystemTreeSelect({
message: 'Select workspaces to build:',
root: './packages',
includeFiles: false, // Only directories
maxDepth: 1,
filter: (path) => {
// Only include directories with package.json
return fs.existsSync(`${path}/package.json`);
}
});
The tree select automatically adapts to your terminal's color scheme and follows Clack's beautiful styling conventions:
This package is designed as a drop-in addition to your existing Clack CLI. No changes needed to your current setup!
// Your existing CLI
import { intro, text, select, multiselect, outro } from '@clack/prompts';
// Just add this import โ
import { treeSelect } from 'clack-tree-select';
async function myCLI() {
intro('My Existing CLI');
const name = await text({ message: 'Project name?' });
const framework = await select({ /* ... */ });
// New: Add tree selection anywhere in your flow
const files = await treeSelect({
message: 'Select files to process:',
tree: myFileTree
});
const tools = await multiselect({ /* ... */ });
outro('Done!');
}
Zero breaking changes - just add treeSelect
where you need hierarchical selection!
Check out the examples/
directory for interactive demos:
cd examples
pnpm install
# Basic tree-select functionality
pnpm tree-select
# Complete integration with other Clack prompts
pnpm integration
The integration demo shows a real CLI workflow mixing treeSelect
with text
, select
, multiselect
, and confirm
prompts.
We welcome contributions! Please see our Contributing Guide for details.
git checkout -b feature/amazing-feature
)git commit -m 'Add some amazing feature'
)git push origin feature/amazing-feature
)This project is licensed under the MIT License - see the LICENSE file for details.
FAQs
Beautiful, interactive tree selection prompts for Clack CLI apps
The npm package clack-tree-select receives a total of 441 weekly downloads. As such, clack-tree-select popularity was classified as not popular.
We found that clack-tree-select demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.ย It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
npm now supports Trusted Publishing with OIDC, enabling secure package publishing directly from CI/CD workflows without relying on long-lived tokens.
Research
/Security News
A RubyGems malware campaign used 60 malicious packages posing as automation tools to steal credentials from social media and marketing tool users.
Security News
The CNA Scorecard ranks CVE issuers by data completeness, revealing major gaps in patch info and software identifiers across thousands of vulnerabilities.