pickem 1.0.0 → 1.0.2

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.
Files changed (2) hide show
  1. package/README.md +68 -69
  2. package/package.json +1 -9
package/README.md CHANGED
@@ -1,39 +1,40 @@
1
1
  # pickem
2
2
 
3
- Usage-sorted searchable autocomplete for CLI tools. One near-zero-dependency module that gives every CLI the same interactive picker pattern: discover items, sort by usage, search, select, track.
3
+ **Usage-sorted, searchable autocomplete for CLI tools.** Give every command-line tool the same fast, interactive picker: type to filter, arrow to choose and the options you reach for most float to the top on their own.
4
4
 
5
- pickem runs on its **own native picker engine** — no `@inquirer`, no prompt-library dependency. The only runtime dependency is [`string-width`](https://github.com/sindresorhus/string-width) (for correct CJK/emoji column width). The look is a Claude Code `/skills`-style selector: an accent title, a dim hint line, a rounded search box, columnar rows with a `❯` chevron on the active item, and a `↓ N more below` footer — truecolor with graceful 256/ASCII fallbacks. See [`docs/standards/selector-ui.md`](docs/standards/selector-ui.md) and [`docs/architecture/picker-engine.md`](docs/architecture/picker-engine.md).
5
+ ![npm version](https://img.shields.io/npm/v/pickem) ![node](https://img.shields.io/node/v/pickem) ![license](https://img.shields.io/npm/l/pickem)
6
6
 
7
- ## Install
8
-
9
- ```bash
10
- npm install pickem
11
- ```
7
+ ![pickem demo — searchable, usage-sorted CLI picker](https://raw.githubusercontent.com/calebogden/pickem-assets/main/pickem-demo.gif)
12
8
 
13
- ## Demo
9
+ - **Search as you type** — filter on labels, descriptions, or any field you name
10
+ - **Usage-aware ordering** — frequently chosen items sort to the top automatically
11
+ - **Single- and multi-select** — with optional free-text entry for ad-hoc values
12
+ - **Wizard flows** — chain prompts with branching and conditional steps
13
+ - **Light and fully typed** — one runtime dependency, ESM, ships with types
14
14
 
15
- An interactive tour of every feature ships with the repo:
15
+ ## Install
16
16
 
17
17
  ```bash
18
- git clone https://github.com/calebogden/pickem
19
- cd pickem && npm install && npm run demo
18
+ npm install pickem
20
19
  ```
21
20
 
22
- ## Quick Start
21
+ ## Quick start
23
22
 
24
23
  ```typescript
25
24
  import { pickem } from 'pickem'
26
25
 
27
26
  const choice = await pickem([
28
27
  { label: 'Deploy', value: 'deploy', description: 'Push to production' },
29
- { label: 'Test', value: 'test', description: 'Run test suite' },
30
- { label: 'Lint', value: 'lint', description: 'Check formatting' },
28
+ { label: 'Test', value: 'test', description: 'Run test suite' },
29
+ { label: 'Lint', value: 'lint', description: 'Check formatting' },
31
30
  ])
32
31
  ```
33
32
 
34
- ## Multi-select (checkbox)
33
+ Type to filter, arrow keys to move, Enter to choose. The selected value is returned.
35
34
 
36
- `pickem.checkbox(items, opts)` returns `string[]` — the values of every checked item.
35
+ ## Multi-select
36
+
37
+ `pickem.checkbox(items, opts)` returns the values of every checked item.
37
38
 
38
39
  ```typescript
39
40
  import { pickem } from 'pickem'
@@ -46,35 +47,37 @@ const choices = await pickem.checkbox([
46
47
  // => ['deploy', 'test']
47
48
  ```
48
49
 
49
- Key bindings: Space toggles a selection, Enter submits, type to filter, Esc clears the filter, Backspace removes filter characters.
50
+ Space toggles a selection, Enter submits, type to filter, Esc clears the filter, Backspace removes filter characters.
50
51
 
51
- Pass `searchable: false` to fall back to the plain native checkbox picker (no search bar):
52
+ Pass `searchable: false` to fall back to a plain checkbox list with no search bar:
52
53
 
53
54
  ```typescript
54
55
  const choices = await pickem.checkbox(items, { searchable: false })
55
56
  ```
56
57
 
57
- Pass `{ allowFreeText: true }` to let the user add ad-hoc entries: type text that doesn't match any item, press Enter, and the typed text is appended to the list as a checked item. The filter clears and the prompt stays open so they can keep toggling or add more.
58
+ Pass `allowFreeText: true` to let users add ad-hoc entries: type text that matches nothing, press Enter, and it's appended to the list as a checked item the filter clears and the picker stays open so they can keep going.
58
59
 
59
60
  ```typescript
60
61
  const tags = await pickem.checkbox(KNOWN_TAGS, { allowFreeText: true })
61
62
  ```
62
63
 
63
- ## Usage Tracking
64
+ ## Usage-aware ordering
64
65
 
65
- Items sort by how often they're used. Enable with `track: true`:
66
+ Items sort by how often they've been chosen, so the picker adapts to how each user actually works. Enable it with `track: true`:
66
67
 
67
68
  ```typescript
68
69
  const choice = await pickem(items, {
69
- track: true, // Uses ~/.pickem/usage.json
70
- // or:
70
+ track: true, // stored in ~/.pickem/usage.json
71
+ // or point it somewhere of your own:
71
72
  track: { storePath: '~/.myapp/usage.json' },
72
73
  })
73
74
  ```
74
75
 
75
- 3-tier sort: count desc, lastUsed desc, alphabetical.
76
+ Ordering is a 3-tier sort: usage count, then most-recently-used, then alphabetical.
77
+
78
+ ## Multiple sources
76
79
 
77
- ## Multiple Sources
80
+ Pull items from several places at once — they load in parallel and merge into one picker.
78
81
 
79
82
  ```typescript
80
83
  import { pickem, defineSource } from 'pickem'
@@ -86,45 +89,45 @@ const scripts = defineSource('scripts', async () => [
86
89
 
87
90
  const npm = defineSource('npm', async () => [
88
91
  { label: 'build', value: 'npm run build' },
89
- { label: 'test', value: 'npm test' },
92
+ { label: 'test', value: 'npm test' },
90
93
  ])
91
94
 
92
95
  const choice = await pickem.from([scripts, npm], { track: true })
93
- // Items get group badges when badgeStyle is set: [scripts], [npm]
96
+ // Set badgeStyle to label each item by source, e.g. [scripts], [npm]
94
97
  ```
95
98
 
96
- ## Wizard Flows
99
+ ## Wizard flows
97
100
 
98
- Chain multiple prompts with branching logic:
101
+ Chain multiple prompts together, with branching and conditional steps.
99
102
 
100
103
  ```typescript
101
104
  import { wizard } from 'pickem'
102
105
 
103
106
  const result = await wizard([
104
- { id: 'action', type: 'pick', message: 'What to do?', items: allScripts },
105
- { id: 'env', type: 'select', message: 'Environment:', choices: [
107
+ { id: 'action', type: 'pick', message: 'What to do?', items: allScripts },
108
+ { id: 'env', type: 'select', message: 'Environment:', choices: [
106
109
  { label: 'Production', value: 'prod' },
107
- { label: 'Staging', value: 'staging' },
110
+ { label: 'Staging', value: 'staging' },
108
111
  ], when: ctx => ctx.action === 'deploy' },
109
112
  { id: 'confirm', type: 'confirm', message: 'Are you sure?' },
110
- { id: 'route', type: 'branch', on: ctx => ctx.confirm ? 'done' : 'action' },
113
+ { id: 'route', type: 'branch', on: ctx => ctx.confirm ? 'done' : 'action' },
111
114
  ])
112
115
  ```
113
116
 
114
- ### Step types
115
-
116
- | Type | Purpose |
117
- |------|---------|
118
- | `pick` | Searchable usage-sorted selection |
117
+ | Step type | Purpose |
118
+ |------------|---------|
119
+ | `pick` | Searchable, usage-sorted selection |
119
120
  | `checkbox` | Searchable multi-select, returns an array |
120
- | `select` | Simple choice list |
121
- | `input` | Free text entry |
122
- | `confirm` | Yes/no |
123
- | `branch` | Route to a step ID or inject dynamic steps |
121
+ | `select` | Simple choice list |
122
+ | `input` | Free text entry |
123
+ | `confirm` | Yes / no |
124
+ | `branch` | Route to a step ID or inject steps dynamically |
125
+
126
+ Every step supports `when` (run conditionally) and `before` (a pre-step hook).
124
127
 
125
- Each step supports `when` (conditional) and `before` (pre-step hook).
128
+ ## Standalone usage tracker
126
129
 
127
- ## Standalone Usage Tracker
130
+ The usage tracker works on its own, too — handy for ranking anything by frequency.
128
131
 
129
132
  ```typescript
130
133
  import { UsageTracker } from 'pickem'
@@ -134,26 +137,26 @@ await tracker.track('deploy')
134
137
  await tracker.track('deploy')
135
138
 
136
139
  const sorted = await tracker.sortItems(items)
137
- const top = await tracker.getTop(10)
138
- const stats = await tracker.getStats('deploy') // { count: 2, lastUsed: ... }
140
+ const top = await tracker.getTop(10)
141
+ const stats = await tracker.getStats('deploy') // { count: 2, lastUsed: ... }
139
142
  ```
140
143
 
141
144
  ## Options
142
145
 
143
146
  ```typescript
144
147
  await pickem(items, {
145
- message: 'Pick one:', // Prompt message
146
- pageSize: 15, // Visible items
147
- track: true | TrackOptions, // Enable usage tracking
148
- searchable: true, // Set false to skip the search bar (falls back to select)
149
- searchFields: ['label', 'description'], // Fields to search (dot-notation for meta)
150
- search: (item, term) => boolean, // Custom search function
151
- format: (item, stats) => string, // Custom display formatter
152
- badgeStyle: 'none' | 'bracket' | 'dot' | fn, // Group badge style (default: 'none')
153
- badgeColors: { npm: 'red' }, // Badge color overrides
154
- sort: (a, b) => number, // Tiebreaker after usage sort
155
- onSelect: (item) => {}, // Post-selection callback
156
- allowFreeText: true, // Surface unmatched input as a selectable option
148
+ message: 'Pick one:', // Prompt message
149
+ pageSize: 15, // Visible items per page
150
+ track: true, // Enable usage tracking (true | TrackOptions)
151
+ searchable: true, // false skips the search bar
152
+ searchFields: ['label', 'description'], // Fields to search (dot-notation for nested meta)
153
+ search: (item, term) => boolean, // Custom match function
154
+ format: (item, stats) => string, // Custom row formatter
155
+ badgeStyle: 'none', // Group badge style: 'none' | 'bracket' | 'dot' | fn
156
+ badgeColors: { npm: 'red' }, // Per-group badge colors
157
+ sort: (a, b) => number, // Tiebreaker applied after usage sort
158
+ onSelect: (item) => {}, // Callback fired on selection
159
+ allowFreeText: false, // Surface unmatched input as a selectable option
157
160
  })
158
161
  ```
159
162
 
@@ -162,20 +165,16 @@ await pickem(items, {
162
165
  ```typescript
163
166
  await pickem.checkbox(items, {
164
167
  required: false, // Require at least one selection before Enter submits
165
- defaultChecked: ['test'], // Pre-checked values
166
- allowFreeText: true, // Type unmatched text + Enter to add as a checked item
168
+ defaultChecked: ['test'], // Values checked on open
169
+ allowFreeText: true, // Type unmatched text + Enter to add it as a checked item
167
170
  })
168
171
  ```
169
172
 
170
- ### `badgeStyle` default changed in v0.2.0
171
-
172
- The default `badgeStyle` is now `'none'` — group labels are hidden unless you opt in. This is a breaking change if your code relied on the old default of `'bracket'`. To restore the previous behavior:
173
-
174
- ```typescript
175
- await pickem(items, { badgeStyle: 'bracket' })
176
- ```
177
-
178
173
  ## Requirements
179
174
 
180
- - Node >= 18
175
+ - Node.js >= 18
181
176
  - ESM only
177
+
178
+ ## License
179
+
180
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pickem",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Usage-sorted searchable autocomplete for CLI tools",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -21,14 +21,6 @@
21
21
  "picker",
22
22
  "wizard"
23
23
  ],
24
- "repository": {
25
- "type": "git",
26
- "url": "git+https://github.com/calebogden/pickem.git"
27
- },
28
- "bugs": {
29
- "url": "https://github.com/calebogden/pickem/issues"
30
- },
31
- "homepage": "https://github.com/calebogden/pickem#readme",
32
24
  "author": "Caleb Ogden <co@ogden.co>",
33
25
  "license": "MIT",
34
26
  "engines": {