pickem 0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 pickone contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # pickone
2
+
3
+ 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.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install pickone
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { pickone } from 'pickone'
15
+
16
+ const choice = await pickone([
17
+ { label: 'Deploy', value: 'deploy', description: 'Push to production' },
18
+ { label: 'Test', value: 'test', description: 'Run test suite' },
19
+ { label: 'Lint', value: 'lint', description: 'Check formatting' },
20
+ ])
21
+ ```
22
+
23
+ ## Usage Tracking
24
+
25
+ Items sort by how often they're used. Enable with `track: true`:
26
+
27
+ ```typescript
28
+ const choice = await pickone(items, {
29
+ track: true, // Uses ~/.pickone/usage.json
30
+ // or:
31
+ track: { storePath: '~/.myapp/usage.json' },
32
+ })
33
+ ```
34
+
35
+ 3-tier sort: count desc, lastUsed desc, alphabetical.
36
+
37
+ ## Multiple Sources
38
+
39
+ ```typescript
40
+ import { pickone, defineSource } from 'pickone'
41
+
42
+ const scripts = defineSource('scripts', async () => [
43
+ { label: 'deploy.sh', value: './scripts/deploy.sh' },
44
+ { label: 'backup.sh', value: './scripts/backup.sh' },
45
+ ])
46
+
47
+ const npm = defineSource('npm', async () => [
48
+ { label: 'build', value: 'npm run build' },
49
+ { label: 'test', value: 'npm test' },
50
+ ])
51
+
52
+ const choice = await pickone.from([scripts, npm], { track: true })
53
+ // Items get group badges: [scripts], [npm]
54
+ ```
55
+
56
+ ## Wizard Flows
57
+
58
+ Chain multiple prompts with branching logic:
59
+
60
+ ```typescript
61
+ import { wizard } from 'pickone'
62
+
63
+ const result = await wizard([
64
+ { id: 'action', type: 'pick', message: 'What to do?', items: allScripts },
65
+ { id: 'env', type: 'select', message: 'Environment:', choices: [
66
+ { label: 'Production', value: 'prod' },
67
+ { label: 'Staging', value: 'staging' },
68
+ ], when: ctx => ctx.action === 'deploy' },
69
+ { id: 'confirm', type: 'confirm', message: 'Are you sure?' },
70
+ { id: 'route', type: 'branch', on: ctx => ctx.confirm ? 'done' : 'action' },
71
+ ])
72
+ ```
73
+
74
+ ### Step types
75
+
76
+ | Type | Purpose |
77
+ |------|---------|
78
+ | `pick` | Searchable usage-sorted selection |
79
+ | `select` | Simple choice list |
80
+ | `input` | Free text entry |
81
+ | `confirm` | Yes/no |
82
+ | `branch` | Route to a step ID or inject dynamic steps |
83
+
84
+ Each step supports `when` (conditional) and `before` (pre-step hook).
85
+
86
+ ## Standalone Usage Tracker
87
+
88
+ ```typescript
89
+ import { UsageTracker } from 'pickone'
90
+
91
+ const tracker = new UsageTracker({ storePath: '~/.myapp/usage.json' })
92
+ await tracker.track('deploy')
93
+ await tracker.track('deploy')
94
+
95
+ const sorted = await tracker.sortItems(items)
96
+ const top = await tracker.getTop(10)
97
+ const stats = await tracker.getStats('deploy') // { count: 2, lastUsed: ... }
98
+ ```
99
+
100
+ ## Options
101
+
102
+ ```typescript
103
+ await pickone(items, {
104
+ message: 'Pick one:', // Prompt message
105
+ pageSize: 15, // Visible items
106
+ track: true | TrackOptions, // Enable usage tracking
107
+ searchFields: ['label', 'description'], // Fields to search (dot-notation for meta)
108
+ search: (item, term) => boolean, // Custom search function
109
+ format: (item, stats) => string, // Custom display formatter
110
+ badgeStyle: 'bracket' | 'dot' | fn, // Group badge style
111
+ badgeColors: { npm: 'red' }, // Badge color overrides
112
+ sort: (a, b) => number, // Tiebreaker after usage sort
113
+ onSelect: (item) => {}, // Post-selection callback
114
+ })
115
+ ```
116
+
117
+ ## Requirements
118
+
119
+ - Node >= 18
120
+ - ESM only
@@ -0,0 +1,9 @@
1
+ import type { PickItem, UsageStats } from '../types.js';
2
+ type BadgeStyle = 'bracket' | 'dot' | ((group: string) => string);
3
+ export interface FormatOptions {
4
+ badgeStyle?: BadgeStyle;
5
+ badgeColors?: Record<string, string>;
6
+ }
7
+ export declare function createFormatter<V>(options?: FormatOptions): (item: PickItem<V>, stats: UsageStats | null) => string;
8
+ export {};
9
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/display/format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEvD,KAAK,UAAU,GAAG,SAAS,GAAG,KAAK,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;AAmCjE,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACrC;AAED,wBAAgB,eAAe,CAAC,CAAC,EAC/B,OAAO,GAAE,aAAkB,GAC1B,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,MAAM,CAoBzD"}
@@ -0,0 +1,42 @@
1
+ import chalk from 'chalk';
2
+ const DEFAULT_BADGE_COLORS = {
3
+ npm: 'red',
4
+ script: 'blue',
5
+ builtin: 'green',
6
+ workflow: 'magenta',
7
+ tool: 'yellow',
8
+ };
9
+ function renderBadge(group, style, colors) {
10
+ if (typeof style === 'function') {
11
+ return style(group);
12
+ }
13
+ const colorName = colors[group] ?? 'gray';
14
+ const colorFn = chalk[colorName] ?? chalk.gray;
15
+ if (style === 'dot') {
16
+ return colorFn('●') + ' ';
17
+ }
18
+ // bracket (default)
19
+ return colorFn(`[${group}]`) + ' ';
20
+ }
21
+ function renderUsageCount(count) {
22
+ if (count === 0)
23
+ return '';
24
+ return chalk.dim(` (${count})`);
25
+ }
26
+ export function createFormatter(options = {}) {
27
+ const style = options.badgeStyle ?? 'bracket';
28
+ const colors = { ...DEFAULT_BADGE_COLORS, ...options.badgeColors };
29
+ return (item, stats) => {
30
+ let result = '';
31
+ if (item.group) {
32
+ result += renderBadge(item.group, style, colors);
33
+ }
34
+ result += item.label;
35
+ result += renderUsageCount(stats?.count ?? 0);
36
+ if (item.description) {
37
+ result += chalk.dim(` — ${item.description}`);
38
+ }
39
+ return result;
40
+ };
41
+ }
42
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/display/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,MAAM,oBAAoB,GAA2B;IACnD,GAAG,EAAE,KAAK;IACV,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,OAAO;IAChB,QAAQ,EAAE,SAAS;IACnB,IAAI,EAAE,QAAQ;CACf,CAAA;AAED,SAAS,WAAW,CAClB,KAAa,EACb,KAAiB,EACjB,MAA8B;IAE9B,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAA;IACrB,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAA;IACzC,MAAM,OAAO,GAAI,KAAa,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,IAAI,CAAA;IAEvD,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QACpB,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;IAC3B,CAAC;IAED,oBAAoB;IACpB,OAAO,OAAO,CAAC,IAAI,KAAK,GAAG,CAAC,GAAG,GAAG,CAAA;AACpC,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa;IACrC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAC1B,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,CAAA;AACjC,CAAC;AAOD,MAAM,UAAU,eAAe,CAC7B,UAAyB,EAAE;IAE3B,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,IAAI,SAAS,CAAA;IAC7C,MAAM,MAAM,GAAG,EAAE,GAAG,oBAAoB,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAA;IAElE,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACrB,IAAI,MAAM,GAAG,EAAE,CAAA;QAEf,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;QAClD,CAAC;QAED,MAAM,IAAI,IAAI,CAAC,KAAK,CAAA;QACpB,MAAM,IAAI,gBAAgB,CAAC,KAAK,EAAE,KAAK,IAAI,CAAC,CAAC,CAAA;QAE7C,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;QAC/C,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;AACH,CAAC"}
package/index.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ export { pickone } from './pickone.js';
2
+ export { wizard } from './wizard.js';
3
+ export { UsageTracker } from './usage/tracker.js';
4
+ export { JsonFileStorage, MemoryStorage } from './usage/storage.js';
5
+ export { sortByUsage } from './usage/sort.js';
6
+ export { defineSource, loadSources } from './source/source.js';
7
+ export { createSearchFn } from './search/search.js';
8
+ export { createFormatter } from './display/format.js';
9
+ export type { PickItem, PickOptions, TrackOptions, UsageStats, UsageRecord, UsageData, UsageStorage, Source, WizardContext, WizardStep, PickStep, SelectStep, InputStep, ConfirmStep, BranchStep, SelectChoice, } from './types.js';
10
+ //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGpC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAG7C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAG9D,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAGnD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAGrD,YAAY,EACV,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,UAAU,EACV,WAAW,EACX,SAAS,EACT,YAAY,EACZ,MAAM,EACN,aAAa,EACb,UAAU,EACV,QAAQ,EACR,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,YAAY,GACb,MAAM,YAAY,CAAA"}
package/index.js ADDED
@@ -0,0 +1,14 @@
1
+ // Core
2
+ export { pickone } from './pickone.js';
3
+ export { wizard } from './wizard.js';
4
+ // Usage tracking
5
+ export { UsageTracker } from './usage/tracker.js';
6
+ export { JsonFileStorage, MemoryStorage } from './usage/storage.js';
7
+ export { sortByUsage } from './usage/sort.js';
8
+ // Sources
9
+ export { defineSource, loadSources } from './source/source.js';
10
+ // Search
11
+ export { createSearchFn } from './search/search.js';
12
+ // Display
13
+ export { createFormatter } from './display/format.js';
14
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO;AACP,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,iBAAiB;AACjB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAE7C,UAAU;AACV,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAE9D,SAAS;AACT,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEnD,UAAU;AACV,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA"}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "pickem",
3
+ "version": "0.0.1",
4
+ "description": "Usage-sorted searchable autocomplete for CLI tools",
5
+ "type": "module",
6
+ "main": "./index.js",
7
+ "types": "./index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./index.js",
11
+ "types": "./index.d.ts"
12
+ }
13
+ },
14
+ "keywords": [
15
+ "cli",
16
+ "autocomplete",
17
+ "interactive",
18
+ "usage-sorted",
19
+ "searchable",
20
+ "picker",
21
+ "wizard"
22
+ ],
23
+ "license": "MIT",
24
+ "engines": {
25
+ "node": ">=18"
26
+ },
27
+ "dependencies": {
28
+ "@inquirer/prompts": "^7.0.0",
29
+ "chalk": "^5.3.0"
30
+ }
31
+ }
package/pickone.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import type { PickItem, PickOptions, Source } from './types.js';
2
+ declare function pick<V = string>(items: PickItem<V>[], options?: PickOptions<V>): Promise<V>;
3
+ /**
4
+ * Pick from multiple sources loaded in parallel.
5
+ */
6
+ declare function pickFrom<V = string>(sources: Source<V>[], options?: PickOptions<V>): Promise<V>;
7
+ declare const pickone: typeof pick & {
8
+ from: typeof pickFrom;
9
+ };
10
+ export { pickone };
11
+ //# sourceMappingURL=pickone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pickone.d.ts","sourceRoot":"","sources":["../src/pickone.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAgB,MAAM,YAAY,CAAA;AAY7E,iBAAe,IAAI,CAAC,CAAC,GAAG,MAAM,EAC5B,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,EACpB,OAAO,GAAE,WAAW,CAAC,CAAC,CAAM,GAC3B,OAAO,CAAC,CAAC,CAAC,CA6DZ;AAED;;GAEG;AACH,iBAAe,QAAQ,CAAC,CAAC,GAAG,MAAM,EAChC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EACpB,OAAO,GAAE,WAAW,CAAC,CAAC,CAAM,GAC3B,OAAO,CAAC,CAAC,CAAC,CAGZ;AAGD,QAAA,MAAM,OAAO;;CAA0C,CAAA;AAEvD,OAAO,EAAE,OAAO,EAAE,CAAA"}
package/pickone.js ADDED
@@ -0,0 +1,79 @@
1
+ import { search } from '@inquirer/prompts';
2
+ import { UsageTracker } from './usage/tracker.js';
3
+ import { createSearchFn } from './search/search.js';
4
+ import { createFormatter } from './display/format.js';
5
+ import { loadSources } from './source/source.js';
6
+ function resolveTrackOptions(track) {
7
+ if (!track)
8
+ return null;
9
+ if (track === true)
10
+ return {};
11
+ return track;
12
+ }
13
+ async function pick(items, options = {}) {
14
+ const trackOpts = resolveTrackOptions(options.track);
15
+ const tracker = trackOpts ? new UsageTracker(trackOpts) : null;
16
+ const keyFn = trackOpts?.key ?? ((item) => item.label);
17
+ // Sort by usage
18
+ let sorted;
19
+ if (tracker) {
20
+ sorted = await tracker.sortItems(items, keyFn, options.sort);
21
+ }
22
+ else if (options.sort) {
23
+ sorted = [...items].sort(options.sort);
24
+ }
25
+ else {
26
+ sorted = items;
27
+ }
28
+ // Search function
29
+ const searchFn = options.search ?? createSearchFn(options.searchFields);
30
+ // Formatter
31
+ const formatFn = options.format ??
32
+ createFormatter({
33
+ badgeStyle: options.badgeStyle,
34
+ badgeColors: options.badgeColors,
35
+ });
36
+ const result = await search({
37
+ message: options.message ?? 'Pick one:',
38
+ pageSize: options.pageSize ?? 15,
39
+ source: async (term) => {
40
+ const filtered = term ? sorted.filter((item) => searchFn(item, term)) : sorted;
41
+ return filtered.map((item) => {
42
+ const stats = tracker
43
+ ? { count: 0, lastUsed: 0, ...tracker.cache?.[keyFn(item)] }
44
+ : null;
45
+ return {
46
+ name: formatFn(item, stats),
47
+ value: item.value,
48
+ description: undefined, // We handle description in formatFn
49
+ };
50
+ });
51
+ },
52
+ });
53
+ // Track usage
54
+ if (tracker) {
55
+ const selected = sorted.find((item) => item.value === result);
56
+ if (selected) {
57
+ await tracker.track(keyFn(selected), trackOpts?.source);
58
+ }
59
+ }
60
+ // onSelect callback
61
+ if (options.onSelect) {
62
+ const selected = sorted.find((item) => item.value === result);
63
+ if (selected) {
64
+ await options.onSelect(selected);
65
+ }
66
+ }
67
+ return result;
68
+ }
69
+ /**
70
+ * Pick from multiple sources loaded in parallel.
71
+ */
72
+ async function pickFrom(sources, options = {}) {
73
+ const items = await loadSources(sources);
74
+ return pick(items, options);
75
+ }
76
+ // Attach .from as a method
77
+ const pickone = Object.assign(pick, { from: pickFrom });
78
+ export { pickone };
79
+ //# sourceMappingURL=pickone.js.map
package/pickone.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pickone.js","sourceRoot":"","sources":["../src/pickone.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { PickItem } from '../types.js';
2
+ /**
3
+ * Case-insensitive substring search across configured fields.
4
+ * Each term (space-separated) must match somewhere in the search text.
5
+ */
6
+ export declare function createSearchFn<V>(fields?: string[]): (item: PickItem<V>, term: string) => boolean;
7
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/search/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAsC3C;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,MAAM,GAAE,MAAM,EAA6B,GAC1C,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAM9C"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Get a nested value from an object using dot-notation path.
3
+ * e.g. getByPath({ a: { b: 'hello' } }, 'a.b') → 'hello'
4
+ */
5
+ function getByPath(obj, path) {
6
+ let current = obj;
7
+ for (const part of path.split('.')) {
8
+ if (current == null || typeof current !== 'object')
9
+ return undefined;
10
+ current = current[part];
11
+ }
12
+ return current;
13
+ }
14
+ /**
15
+ * Extract searchable text from an item based on field paths.
16
+ * Fields can reference top-level item properties or nested meta properties via dot-notation.
17
+ */
18
+ function extractSearchText(item, fields) {
19
+ if (item.searchText)
20
+ return item.searchText;
21
+ const parts = [];
22
+ for (const field of fields) {
23
+ let val;
24
+ if (field.includes('.')) {
25
+ // dot-notation — look in meta first, then top-level
26
+ val = getByPath(item.meta, field) ?? getByPath(item, field);
27
+ }
28
+ else {
29
+ val = item[field] ?? getByPath(item.meta, field);
30
+ }
31
+ if (typeof val === 'string') {
32
+ parts.push(val);
33
+ }
34
+ }
35
+ return parts.join(' ');
36
+ }
37
+ /**
38
+ * Case-insensitive substring search across configured fields.
39
+ * Each term (space-separated) must match somewhere in the search text.
40
+ */
41
+ export function createSearchFn(fields = ['label', 'description']) {
42
+ return (item, term) => {
43
+ const text = extractSearchText(item, fields).toLowerCase();
44
+ const terms = term.toLowerCase().split(/\s+/).filter(Boolean);
45
+ return terms.every((t) => text.includes(t));
46
+ };
47
+ }
48
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/search/search.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,SAAS,SAAS,CAAC,GAAQ,EAAE,IAAY;IACvC,IAAI,OAAO,GAAG,GAAG,CAAA;IACjB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACnC,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QACpE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAI,IAAiB,EAAE,MAAgB;IAC/D,IAAI,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC,UAAU,CAAA;IAE3C,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,GAAY,CAAA;QAChB,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,oDAAoD;YACpD,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QAC7D,CAAC;aAAM,CAAC;YACN,GAAG,GAAI,IAAY,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QAC3D,CAAC;QACD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACjB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,SAAmB,CAAC,OAAO,EAAE,aAAa,CAAC;IAE3C,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;QACpB,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,WAAW,EAAE,CAAA;QAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAC7D,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;IAC7C,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { PickItem, Source } from '../types.js';
2
+ export declare function defineSource<V = string>(name: string, load: () => Promise<PickItem<V>[]>): Source<V>;
3
+ /**
4
+ * Load items from multiple sources in parallel.
5
+ * Each item gets its group set to the source name (unless already set).
6
+ */
7
+ export declare function loadSources<V>(sources: Source<V>[]): Promise<PickItem<V>[]>;
8
+ //# sourceMappingURL=source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/source/source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEnD,wBAAgB,YAAY,CAAC,CAAC,GAAG,MAAM,EACrC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,GACjC,MAAM,CAAC,CAAC,CAAC,CAEX;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAWjF"}
@@ -0,0 +1,18 @@
1
+ export function defineSource(name, load) {
2
+ return { name, load };
3
+ }
4
+ /**
5
+ * Load items from multiple sources in parallel.
6
+ * Each item gets its group set to the source name (unless already set).
7
+ */
8
+ export async function loadSources(sources) {
9
+ const results = await Promise.all(sources.map(async (source) => {
10
+ const items = await source.load();
11
+ return items.map((item) => ({
12
+ ...item,
13
+ group: item.group ?? source.name,
14
+ }));
15
+ }));
16
+ return results.flat();
17
+ }
18
+ //# sourceMappingURL=source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.js","sourceRoot":"","sources":["../../src/source/source.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,IAAkC;IAElC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAI,OAAoB;IACvD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;QAC3B,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;QACjC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC1B,GAAG,IAAI;YACP,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI;SACjC,CAAC,CAAC,CAAA;IACL,CAAC,CAAC,CACH,CAAA;IACD,OAAO,OAAO,CAAC,IAAI,EAAE,CAAA;AACvB,CAAC"}
package/types.d.ts ADDED
@@ -0,0 +1,89 @@
1
+ export interface PickItem<V = string> {
2
+ label: string;
3
+ value: V;
4
+ description?: string;
5
+ group?: string;
6
+ meta?: Record<string, any>;
7
+ searchText?: string;
8
+ }
9
+ export interface PickOptions<V = string> {
10
+ message?: string;
11
+ pageSize?: number;
12
+ track?: boolean | TrackOptions;
13
+ searchFields?: string[];
14
+ search?: (item: PickItem<V>, term: string) => boolean;
15
+ format?: (item: PickItem<V>, stats: UsageStats | null) => string;
16
+ badgeStyle?: 'bracket' | 'dot' | ((group: string) => string);
17
+ badgeColors?: Record<string, string>;
18
+ sort?: (a: PickItem<V>, b: PickItem<V>) => number;
19
+ onSelect?: (item: PickItem<V>) => void | Promise<void>;
20
+ }
21
+ export interface TrackOptions {
22
+ storePath?: string;
23
+ namespace?: string;
24
+ key?: (item: PickItem<any>) => string;
25
+ storage?: UsageStorage;
26
+ source?: string;
27
+ }
28
+ export interface UsageStats {
29
+ count: number;
30
+ lastUsed: number;
31
+ }
32
+ export interface UsageRecord {
33
+ count: number;
34
+ lastUsed: number;
35
+ source?: string;
36
+ }
37
+ export interface UsageData {
38
+ [key: string]: UsageRecord;
39
+ }
40
+ export interface UsageStorage {
41
+ read(): Promise<UsageData>;
42
+ write(data: UsageData): Promise<void>;
43
+ }
44
+ export interface Source<V = string> {
45
+ name: string;
46
+ load: () => Promise<PickItem<V>[]>;
47
+ }
48
+ export interface WizardContext {
49
+ [key: string]: any;
50
+ }
51
+ export type WizardStep<V = string> = PickStep<V> | SelectStep | InputStep | ConfirmStep | BranchStep;
52
+ interface BaseStep {
53
+ id: string;
54
+ when?: (ctx: WizardContext) => boolean | Promise<boolean>;
55
+ before?: (ctx: WizardContext) => void | Promise<void>;
56
+ }
57
+ export interface PickStep<V = string> extends BaseStep {
58
+ type: 'pick';
59
+ message?: string;
60
+ items: PickItem<V>[] | ((ctx: WizardContext) => PickItem<V>[] | Promise<PickItem<V>[]>);
61
+ options?: Omit<PickOptions<V>, 'message'>;
62
+ }
63
+ export interface SelectStep extends BaseStep {
64
+ type: 'select';
65
+ message?: string;
66
+ choices: SelectChoice[] | ((ctx: WizardContext) => SelectChoice[] | Promise<SelectChoice[]>);
67
+ }
68
+ export interface SelectChoice {
69
+ label: string;
70
+ value: string;
71
+ description?: string;
72
+ }
73
+ export interface InputStep extends BaseStep {
74
+ type: 'input';
75
+ message?: string;
76
+ default?: string | ((ctx: WizardContext) => string);
77
+ validate?: (value: string, ctx: WizardContext) => boolean | string | Promise<boolean | string>;
78
+ }
79
+ export interface ConfirmStep extends BaseStep {
80
+ type: 'confirm';
81
+ message?: string;
82
+ default?: boolean;
83
+ }
84
+ export interface BranchStep extends BaseStep {
85
+ type: 'branch';
86
+ on: (ctx: WizardContext) => string | WizardStep[] | Promise<string | WizardStep[]>;
87
+ }
88
+ export {};
89
+ //# sourceMappingURL=types.d.ts.map
package/types.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
package/types.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ import type { PickItem, UsageData } from '../types.js';
2
+ /**
3
+ * 3-tier sort: count desc → lastUsed desc → label alpha.
4
+ * Items with no usage data sort after items with usage.
5
+ */
6
+ export declare function sortByUsage<V>(items: PickItem<V>[], usage: UsageData, keyFn?: (item: PickItem<V>) => string, tiebreaker?: (a: PickItem<V>, b: PickItem<V>) => number): PickItem<V>[];
7
+ //# sourceMappingURL=sort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sort.d.ts","sourceRoot":"","sources":["../../src/usage/sort.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEtD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,EACpB,KAAK,EAAE,SAAS,EAChB,KAAK,GAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,MAA6B,EAC3D,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,MAAM,GACtD,QAAQ,CAAC,CAAC,CAAC,EAAE,CA0Bf"}
package/usage/sort.js ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * 3-tier sort: count desc → lastUsed desc → label alpha.
3
+ * Items with no usage data sort after items with usage.
4
+ */
5
+ export function sortByUsage(items, usage, keyFn = (item) => item.label, tiebreaker) {
6
+ return [...items].sort((a, b) => {
7
+ const aKey = keyFn(a);
8
+ const bKey = keyFn(b);
9
+ const aUsage = usage[aKey];
10
+ const bUsage = usage[bKey];
11
+ const aCount = aUsage?.count ?? 0;
12
+ const bCount = bUsage?.count ?? 0;
13
+ // Count descending
14
+ if (aCount !== bCount)
15
+ return bCount - aCount;
16
+ // Last used descending
17
+ const aLast = aUsage?.lastUsed ?? 0;
18
+ const bLast = bUsage?.lastUsed ?? 0;
19
+ if (aLast !== bLast)
20
+ return bLast - aLast;
21
+ // Custom tiebreaker
22
+ if (tiebreaker) {
23
+ const tie = tiebreaker(a, b);
24
+ if (tie !== 0)
25
+ return tie;
26
+ }
27
+ // Alpha ascending
28
+ return a.label.localeCompare(b.label);
29
+ });
30
+ }
31
+ //# sourceMappingURL=sort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sort.js","sourceRoot":"","sources":["../../src/usage/sort.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,KAAoB,EACpB,KAAgB,EAChB,QAAuC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EAC3D,UAAuD;IAEvD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACrB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACrB,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAA;QAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAA;QAC1B,MAAM,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC,CAAA;QACjC,MAAM,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC,CAAA;QAEjC,mBAAmB;QACnB,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO,MAAM,GAAG,MAAM,CAAA;QAE7C,uBAAuB;QACvB,MAAM,KAAK,GAAG,MAAM,EAAE,QAAQ,IAAI,CAAC,CAAA;QACnC,MAAM,KAAK,GAAG,MAAM,EAAE,QAAQ,IAAI,CAAC,CAAA;QACnC,IAAI,KAAK,KAAK,KAAK;YAAE,OAAO,KAAK,GAAG,KAAK,CAAA;QAEzC,oBAAoB;QACpB,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,GAAG,GAAG,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;YAC5B,IAAI,GAAG,KAAK,CAAC;gBAAE,OAAO,GAAG,CAAA;QAC3B,CAAC;QAED,kBAAkB;QAClB,OAAO,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IACvC,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { UsageData, UsageStorage } from '../types.js';
2
+ export declare class JsonFileStorage implements UsageStorage {
3
+ private path;
4
+ constructor(path: string);
5
+ read(): Promise<UsageData>;
6
+ write(data: UsageData): Promise<void>;
7
+ }
8
+ export declare class MemoryStorage implements UsageStorage {
9
+ private data;
10
+ read(): Promise<UsageData>;
11
+ write(data: UsageData): Promise<void>;
12
+ }
13
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/usage/storage.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAS1D,qBAAa,eAAgB,YAAW,YAAY;IAClD,OAAO,CAAC,IAAI,CAAQ;gBAER,IAAI,EAAE,MAAM;IAIlB,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC;IAS1B,KAAK,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAQ5C;AAED,qBAAa,aAAc,YAAW,YAAY;IAChD,OAAO,CAAC,IAAI,CAAgB;IAEtB,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC;IAI1B,KAAK,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAG5C"}
@@ -0,0 +1,43 @@
1
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ function expandHome(p) {
5
+ if (p.startsWith('~/') || p === '~') {
6
+ return p.replace('~', homedir());
7
+ }
8
+ return p;
9
+ }
10
+ export class JsonFileStorage {
11
+ path;
12
+ constructor(path) {
13
+ this.path = expandHome(path);
14
+ }
15
+ async read() {
16
+ try {
17
+ const raw = await readFile(this.path, 'utf-8');
18
+ return JSON.parse(raw);
19
+ }
20
+ catch {
21
+ return {};
22
+ }
23
+ }
24
+ async write(data) {
25
+ try {
26
+ await mkdir(dirname(this.path), { recursive: true });
27
+ await writeFile(this.path, JSON.stringify(data, null, 2) + '\n', 'utf-8');
28
+ }
29
+ catch {
30
+ // Silent failure — usage tracking should never crash the picker
31
+ }
32
+ }
33
+ }
34
+ export class MemoryStorage {
35
+ data = {};
36
+ async read() {
37
+ return { ...this.data };
38
+ }
39
+ async write(data) {
40
+ this.data = { ...data };
41
+ }
42
+ }
43
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/usage/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAGjC,SAAS,UAAU,CAAC,CAAS;IAC3B,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QACpC,OAAO,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,CAAA;IAClC,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC;AAED,MAAM,OAAO,eAAe;IAClB,IAAI,CAAQ;IAEpB,YAAY,IAAY;QACtB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;IAC9B,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAA;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAe;QACzB,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YACpD,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAA;QAC3E,CAAC;QAAC,MAAM,CAAC;YACP,gEAAgE;QAClE,CAAC;IACH,CAAC;CACF;AAED,MAAM,OAAO,aAAa;IAChB,IAAI,GAAc,EAAE,CAAA;IAE5B,KAAK,CAAC,IAAI;QACR,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IACzB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAe;QACzB,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,EAAE,CAAA;IACzB,CAAC;CACF"}
@@ -0,0 +1,23 @@
1
+ import type { PickItem, UsageData, UsageStats, UsageStorage } from '../types.js';
2
+ export interface TrackerOptions {
3
+ storePath?: string;
4
+ namespace?: string;
5
+ storage?: UsageStorage;
6
+ }
7
+ export declare class UsageTracker {
8
+ private storage;
9
+ private namespace;
10
+ private cache;
11
+ constructor(options?: TrackerOptions);
12
+ private prefixKey;
13
+ load(): Promise<UsageData>;
14
+ track(key: string, source?: string): Promise<void>;
15
+ getStats(key: string): Promise<UsageStats | null>;
16
+ sortItems<V>(items: PickItem<V>[], keyFn?: (item: PickItem<V>) => string, tiebreaker?: (a: PickItem<V>, b: PickItem<V>) => number): Promise<PickItem<V>[]>;
17
+ getTop(n: number): Promise<Array<{
18
+ key: string;
19
+ count: number;
20
+ lastUsed: number;
21
+ }>>;
22
+ }
23
+ //# sourceMappingURL=tracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/usage/tracker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAe,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAM7F,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,KAAK,CAAyB;gBAE1B,OAAO,GAAE,cAAmB;IAKxC,OAAO,CAAC,SAAS;IAIX,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC;IAM1B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAalD,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAOjD,SAAS,CAAC,CAAC,EACf,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,EACpB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,MAAM,EACrC,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,MAAM,GACtD,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAgBnB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAa1F"}
@@ -0,0 +1,69 @@
1
+ import { JsonFileStorage } from './storage.js';
2
+ import { sortByUsage } from './sort.js';
3
+ const DEFAULT_STORE_PATH = '~/.pickone/usage.json';
4
+ export class UsageTracker {
5
+ storage;
6
+ namespace;
7
+ cache = null;
8
+ constructor(options = {}) {
9
+ this.storage = options.storage ?? new JsonFileStorage(options.storePath ?? DEFAULT_STORE_PATH);
10
+ this.namespace = options.namespace;
11
+ }
12
+ prefixKey(key) {
13
+ return this.namespace ? `${this.namespace}:${key}` : key;
14
+ }
15
+ async load() {
16
+ if (this.cache)
17
+ return this.cache;
18
+ this.cache = await this.storage.read();
19
+ return this.cache;
20
+ }
21
+ async track(key, source) {
22
+ const data = await this.load();
23
+ const prefixed = this.prefixKey(key);
24
+ const existing = data[prefixed];
25
+ data[prefixed] = {
26
+ count: (existing?.count ?? 0) + 1,
27
+ lastUsed: Date.now(),
28
+ source: source ?? existing?.source,
29
+ };
30
+ this.cache = data;
31
+ await this.storage.write(data);
32
+ }
33
+ async getStats(key) {
34
+ const data = await this.load();
35
+ const record = data[this.prefixKey(key)];
36
+ if (!record)
37
+ return null;
38
+ return { count: record.count, lastUsed: record.lastUsed };
39
+ }
40
+ async sortItems(items, keyFn, tiebreaker) {
41
+ const data = await this.load();
42
+ // If namespace, we need to remap keys for sort
43
+ if (this.namespace) {
44
+ const remapped = {};
45
+ for (const [k, v] of Object.entries(data)) {
46
+ const prefix = `${this.namespace}:`;
47
+ if (k.startsWith(prefix)) {
48
+ remapped[k.slice(prefix.length)] = v;
49
+ }
50
+ }
51
+ return sortByUsage(items, remapped, keyFn, tiebreaker);
52
+ }
53
+ return sortByUsage(items, data, keyFn, tiebreaker);
54
+ }
55
+ async getTop(n) {
56
+ const data = await this.load();
57
+ const prefix = this.namespace ? `${this.namespace}:` : '';
58
+ return Object.entries(data)
59
+ .filter(([k]) => !this.namespace || k.startsWith(prefix))
60
+ .map(([k, v]) => ({
61
+ key: this.namespace ? k.slice(prefix.length) : k,
62
+ count: v.count,
63
+ lastUsed: v.lastUsed,
64
+ }))
65
+ .sort((a, b) => b.count - a.count || b.lastUsed - a.lastUsed)
66
+ .slice(0, n);
67
+ }
68
+ }
69
+ //# sourceMappingURL=tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../src/usage/tracker.ts"],"names":[],"mappings":"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"}
package/wizard.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { WizardContext, WizardStep } from './types.js';
2
+ export declare function wizard<T extends WizardContext = WizardContext>(steps: WizardStep[], initialContext?: Partial<T>): Promise<T>;
3
+ //# sourceMappingURL=wizard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wizard.d.ts","sourceRoot":"","sources":["../src/wizard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,aAAa,EACb,UAAU,EAMX,MAAM,YAAY,CAAA;AA8FnB,wBAAsB,MAAM,CAAC,CAAC,SAAS,aAAa,GAAG,aAAa,EAClE,KAAK,EAAE,UAAU,EAAE,EACnB,cAAc,GAAE,OAAO,CAAC,CAAC,CAAM,GAC9B,OAAO,CAAC,CAAC,CAAC,CA8BZ"}
package/wizard.js ADDED
@@ -0,0 +1,105 @@
1
+ import { select, input, confirm } from '@inquirer/prompts';
2
+ import { pickone } from './pickone.js';
3
+ async function executePick(step, ctx) {
4
+ const items = typeof step.items === 'function' ? await step.items(ctx) : step.items;
5
+ return pickone(items, {
6
+ message: step.message,
7
+ ...step.options,
8
+ });
9
+ }
10
+ async function executeSelect(step, ctx) {
11
+ const choices = typeof step.choices === 'function' ? await step.choices(ctx) : step.choices;
12
+ return select({
13
+ message: step.message ?? 'Select:',
14
+ choices: choices.map((c) => ({
15
+ name: c.label,
16
+ value: c.value,
17
+ description: c.description,
18
+ })),
19
+ });
20
+ }
21
+ async function executeInput(step, ctx) {
22
+ const defaultVal = typeof step.default === 'function' ? step.default(ctx) : step.default;
23
+ return input({
24
+ message: step.message ?? 'Enter value:',
25
+ default: defaultVal,
26
+ validate: step.validate
27
+ ? (value) => step.validate(value, ctx)
28
+ : undefined,
29
+ });
30
+ }
31
+ async function executeConfirm(step) {
32
+ return confirm({
33
+ message: step.message ?? 'Confirm?',
34
+ default: step.default,
35
+ });
36
+ }
37
+ async function executeStep(step, ctx) {
38
+ // Run before hook
39
+ if (step.before) {
40
+ await step.before(ctx);
41
+ }
42
+ // Check when condition
43
+ if (step.when) {
44
+ const shouldRun = await step.when(ctx);
45
+ if (!shouldRun)
46
+ return;
47
+ }
48
+ switch (step.type) {
49
+ case 'pick':
50
+ ctx[step.id] = await executePick(step, ctx);
51
+ break;
52
+ case 'select':
53
+ ctx[step.id] = await executeSelect(step, ctx);
54
+ break;
55
+ case 'input':
56
+ ctx[step.id] = await executeInput(step, ctx);
57
+ break;
58
+ case 'confirm':
59
+ ctx[step.id] = await executeConfirm(step);
60
+ break;
61
+ case 'branch':
62
+ await executeBranch(step, ctx);
63
+ break;
64
+ }
65
+ }
66
+ async function executeBranch(step, ctx) {
67
+ const result = await step.on(ctx);
68
+ if (Array.isArray(result)) {
69
+ // Inject dynamic steps
70
+ for (const dynamicStep of result) {
71
+ await executeStep(dynamicStep, ctx);
72
+ }
73
+ }
74
+ // If result is a string, it's a step ID — handled by the main loop as a goto
75
+ // We store it in context so the main loop can jump
76
+ if (typeof result === 'string') {
77
+ ctx.__branch_target = result;
78
+ }
79
+ }
80
+ export async function wizard(steps, initialContext = {}) {
81
+ const ctx = { ...initialContext };
82
+ let i = 0;
83
+ while (i < steps.length) {
84
+ const step = steps[i];
85
+ await executeStep(step, ctx);
86
+ // Handle branch jump
87
+ if (ctx.__branch_target) {
88
+ const target = ctx.__branch_target;
89
+ delete ctx.__branch_target;
90
+ if (target === 'done')
91
+ break;
92
+ const targetIndex = steps.findIndex((s) => s.id === target);
93
+ if (targetIndex >= 0) {
94
+ i = targetIndex;
95
+ continue;
96
+ }
97
+ // If target not found, just move forward
98
+ }
99
+ i++;
100
+ }
101
+ // Clean up internal keys
102
+ delete ctx.__branch_target;
103
+ return ctx;
104
+ }
105
+ //# sourceMappingURL=wizard.js.map
package/wizard.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wizard.js","sourceRoot":"","sources":["../src/wizard.ts"],"names":[],"mappings":"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"}