pickem 1.0.0 → 1.0.1

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 +66 -69
  2. package/package.json +1 -9
package/README.md CHANGED
@@ -1,8 +1,14 @@
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
+
7
+ - **Search as you type** — filter on labels, descriptions, or any field you name
8
+ - **Usage-aware ordering** — frequently chosen items sort to the top automatically
9
+ - **Single- and multi-select** — with optional free-text entry for ad-hoc values
10
+ - **Wizard flows** — chain prompts with branching and conditional steps
11
+ - **Light and fully typed** — one runtime dependency, ESM, ships with types
6
12
 
7
13
  ## Install
8
14
 
@@ -10,30 +16,23 @@ pickem runs on its **own native picker engine** — no `@inquirer`, no prompt-li
10
16
  npm install pickem
11
17
  ```
12
18
 
13
- ## Demo
14
-
15
- An interactive tour of every feature ships with the repo:
16
-
17
- ```bash
18
- git clone https://github.com/calebogden/pickem
19
- cd pickem && npm install && npm run demo
20
- ```
21
-
22
- ## Quick Start
19
+ ## Quick start
23
20
 
24
21
  ```typescript
25
22
  import { pickem } from 'pickem'
26
23
 
27
24
  const choice = await pickem([
28
25
  { 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' },
26
+ { label: 'Test', value: 'test', description: 'Run test suite' },
27
+ { label: 'Lint', value: 'lint', description: 'Check formatting' },
31
28
  ])
32
29
  ```
33
30
 
34
- ## Multi-select (checkbox)
31
+ Type to filter, arrow keys to move, Enter to choose. The selected value is returned.
32
+
33
+ ## Multi-select
35
34
 
36
- `pickem.checkbox(items, opts)` returns `string[]` — the values of every checked item.
35
+ `pickem.checkbox(items, opts)` returns the values of every checked item.
37
36
 
38
37
  ```typescript
39
38
  import { pickem } from 'pickem'
@@ -46,35 +45,37 @@ const choices = await pickem.checkbox([
46
45
  // => ['deploy', 'test']
47
46
  ```
48
47
 
49
- Key bindings: Space toggles a selection, Enter submits, type to filter, Esc clears the filter, Backspace removes filter characters.
48
+ Space toggles a selection, Enter submits, type to filter, Esc clears the filter, Backspace removes filter characters.
50
49
 
51
- Pass `searchable: false` to fall back to the plain native checkbox picker (no search bar):
50
+ Pass `searchable: false` to fall back to a plain checkbox list with no search bar:
52
51
 
53
52
  ```typescript
54
53
  const choices = await pickem.checkbox(items, { searchable: false })
55
54
  ```
56
55
 
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.
56
+ 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
57
 
59
58
  ```typescript
60
59
  const tags = await pickem.checkbox(KNOWN_TAGS, { allowFreeText: true })
61
60
  ```
62
61
 
63
- ## Usage Tracking
62
+ ## Usage-aware ordering
64
63
 
65
- Items sort by how often they're used. Enable with `track: true`:
64
+ 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
65
 
67
66
  ```typescript
68
67
  const choice = await pickem(items, {
69
- track: true, // Uses ~/.pickem/usage.json
70
- // or:
68
+ track: true, // stored in ~/.pickem/usage.json
69
+ // or point it somewhere of your own:
71
70
  track: { storePath: '~/.myapp/usage.json' },
72
71
  })
73
72
  ```
74
73
 
75
- 3-tier sort: count desc, lastUsed desc, alphabetical.
74
+ Ordering is a 3-tier sort: usage count, then most-recently-used, then alphabetical.
76
75
 
77
- ## Multiple Sources
76
+ ## Multiple sources
77
+
78
+ Pull items from several places at once — they load in parallel and merge into one picker.
78
79
 
79
80
  ```typescript
80
81
  import { pickem, defineSource } from 'pickem'
@@ -86,45 +87,45 @@ const scripts = defineSource('scripts', async () => [
86
87
 
87
88
  const npm = defineSource('npm', async () => [
88
89
  { label: 'build', value: 'npm run build' },
89
- { label: 'test', value: 'npm test' },
90
+ { label: 'test', value: 'npm test' },
90
91
  ])
91
92
 
92
93
  const choice = await pickem.from([scripts, npm], { track: true })
93
- // Items get group badges when badgeStyle is set: [scripts], [npm]
94
+ // Set badgeStyle to label each item by source, e.g. [scripts], [npm]
94
95
  ```
95
96
 
96
- ## Wizard Flows
97
+ ## Wizard flows
97
98
 
98
- Chain multiple prompts with branching logic:
99
+ Chain multiple prompts together, with branching and conditional steps.
99
100
 
100
101
  ```typescript
101
102
  import { wizard } from 'pickem'
102
103
 
103
104
  const result = await wizard([
104
- { id: 'action', type: 'pick', message: 'What to do?', items: allScripts },
105
- { id: 'env', type: 'select', message: 'Environment:', choices: [
105
+ { id: 'action', type: 'pick', message: 'What to do?', items: allScripts },
106
+ { id: 'env', type: 'select', message: 'Environment:', choices: [
106
107
  { label: 'Production', value: 'prod' },
107
- { label: 'Staging', value: 'staging' },
108
+ { label: 'Staging', value: 'staging' },
108
109
  ], when: ctx => ctx.action === 'deploy' },
109
110
  { id: 'confirm', type: 'confirm', message: 'Are you sure?' },
110
- { id: 'route', type: 'branch', on: ctx => ctx.confirm ? 'done' : 'action' },
111
+ { id: 'route', type: 'branch', on: ctx => ctx.confirm ? 'done' : 'action' },
111
112
  ])
112
113
  ```
113
114
 
114
- ### Step types
115
-
116
- | Type | Purpose |
117
- |------|---------|
118
- | `pick` | Searchable usage-sorted selection |
115
+ | Step type | Purpose |
116
+ |------------|---------|
117
+ | `pick` | Searchable, usage-sorted selection |
119
118
  | `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 |
119
+ | `select` | Simple choice list |
120
+ | `input` | Free text entry |
121
+ | `confirm` | Yes / no |
122
+ | `branch` | Route to a step ID or inject steps dynamically |
124
123
 
125
- Each step supports `when` (conditional) and `before` (pre-step hook).
124
+ Every step supports `when` (run conditionally) and `before` (a pre-step hook).
126
125
 
127
- ## Standalone Usage Tracker
126
+ ## Standalone usage tracker
127
+
128
+ The usage tracker works on its own, too — handy for ranking anything by frequency.
128
129
 
129
130
  ```typescript
130
131
  import { UsageTracker } from 'pickem'
@@ -134,26 +135,26 @@ await tracker.track('deploy')
134
135
  await tracker.track('deploy')
135
136
 
136
137
  const sorted = await tracker.sortItems(items)
137
- const top = await tracker.getTop(10)
138
- const stats = await tracker.getStats('deploy') // { count: 2, lastUsed: ... }
138
+ const top = await tracker.getTop(10)
139
+ const stats = await tracker.getStats('deploy') // { count: 2, lastUsed: ... }
139
140
  ```
140
141
 
141
142
  ## Options
142
143
 
143
144
  ```typescript
144
145
  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
146
+ message: 'Pick one:', // Prompt message
147
+ pageSize: 15, // Visible items per page
148
+ track: true, // Enable usage tracking (true | TrackOptions)
149
+ searchable: true, // false skips the search bar
150
+ searchFields: ['label', 'description'], // Fields to search (dot-notation for nested meta)
151
+ search: (item, term) => boolean, // Custom match function
152
+ format: (item, stats) => string, // Custom row formatter
153
+ badgeStyle: 'none', // Group badge style: 'none' | 'bracket' | 'dot' | fn
154
+ badgeColors: { npm: 'red' }, // Per-group badge colors
155
+ sort: (a, b) => number, // Tiebreaker applied after usage sort
156
+ onSelect: (item) => {}, // Callback fired on selection
157
+ allowFreeText: false, // Surface unmatched input as a selectable option
157
158
  })
158
159
  ```
159
160
 
@@ -162,20 +163,16 @@ await pickem(items, {
162
163
  ```typescript
163
164
  await pickem.checkbox(items, {
164
165
  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
166
+ defaultChecked: ['test'], // Values checked on open
167
+ allowFreeText: true, // Type unmatched text + Enter to add it as a checked item
167
168
  })
168
169
  ```
169
170
 
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
171
  ## Requirements
179
172
 
180
- - Node >= 18
173
+ - Node.js >= 18
181
174
  - ESM only
175
+
176
+ ## License
177
+
178
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pickem",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
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": {