pickem
v0.1.0
Published
Usage-sorted searchable autocomplete for CLI tools
Maintainers
Readme
pickem
Usage-sorted searchable autocomplete for CLI tools. One dependency-light module that gives every CLI the same interactive picker pattern: discover items, sort by usage, search, select, track.
Install
npm install pickemQuick Start
import { pickem } from 'pickem'
const choice = await pickem([
{ label: 'Deploy', value: 'deploy', description: 'Push to production' },
{ label: 'Test', value: 'test', description: 'Run test suite' },
{ label: 'Lint', value: 'lint', description: 'Check formatting' },
])Usage Tracking
Items sort by how often they're used. Enable with track: true:
const choice = await pickem(items, {
track: true, // Uses ~/.pickem/usage.json
// or:
track: { storePath: '~/.myapp/usage.json' },
})3-tier sort: count desc, lastUsed desc, alphabetical.
Multiple Sources
import { pickem, defineSource } from 'pickem'
const scripts = defineSource('scripts', async () => [
{ label: 'deploy.sh', value: './scripts/deploy.sh' },
{ label: 'backup.sh', value: './scripts/backup.sh' },
])
const npm = defineSource('npm', async () => [
{ label: 'build', value: 'npm run build' },
{ label: 'test', value: 'npm test' },
])
const choice = await pickem.from([scripts, npm], { track: true })
// Items get group badges: [scripts], [npm]Wizard Flows
Chain multiple prompts with branching logic:
import { wizard } from 'pickem'
const result = await wizard([
{ id: 'action', type: 'pick', message: 'What to do?', items: allScripts },
{ id: 'env', type: 'select', message: 'Environment:', choices: [
{ label: 'Production', value: 'prod' },
{ label: 'Staging', value: 'staging' },
], when: ctx => ctx.action === 'deploy' },
{ id: 'confirm', type: 'confirm', message: 'Are you sure?' },
{ id: 'route', type: 'branch', on: ctx => ctx.confirm ? 'done' : 'action' },
])Step types
| Type | Purpose |
|------|---------|
| pick | Searchable usage-sorted selection |
| select | Simple choice list |
| input | Free text entry |
| confirm | Yes/no |
| branch | Route to a step ID or inject dynamic steps |
Each step supports when (conditional) and before (pre-step hook).
Standalone Usage Tracker
import { UsageTracker } from 'pickem'
const tracker = new UsageTracker({ storePath: '~/.myapp/usage.json' })
await tracker.track('deploy')
await tracker.track('deploy')
const sorted = await tracker.sortItems(items)
const top = await tracker.getTop(10)
const stats = await tracker.getStats('deploy') // { count: 2, lastUsed: ... }Options
await pickem(items, {
message: 'Pick one:', // Prompt message
pageSize: 15, // Visible items
track: true | TrackOptions, // Enable usage tracking
searchFields: ['label', 'description'], // Fields to search (dot-notation for meta)
search: (item, term) => boolean, // Custom search function
format: (item, stats) => string, // Custom display formatter
badgeStyle: 'bracket' | 'dot' | fn, // Group badge style
badgeColors: { npm: 'red' }, // Badge color overrides
sort: (a, b) => number, // Tiebreaker after usage sort
onSelect: (item) => {}, // Post-selection callback
})Requirements
- Node >= 18
- ESM only
