@junhoyeo/prompthistory 0.1.0

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) 2026 Junho Yeo
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,215 @@
1
+ # prompthistory
2
+
3
+ > CLI tool to search and navigate your OpenCode prompt history
4
+
5
+ [![npm version](https://badge.fury.io/js/prompthistory.svg)](https://www.npmjs.com/package/prompthistory)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ - ๐Ÿ” **Fuzzy search** through all your prompts
11
+ - ๐Ÿ“ **Filter by project** - find prompts from specific projects
12
+ - ๐Ÿ“… **Date range filtering** - search within time ranges
13
+ - ๐ŸŽจ **Beautiful output** - formatted tables with colors
14
+ - โšก **Fast** - streaming parser handles large history files efficiently
15
+ - ๐Ÿงน **Smart filtering** - automatically excludes slash commands, optional deduplication
16
+ - ๐Ÿ“‹ **Clipboard support** - copy any prompt with `--copy`
17
+ - ๐Ÿ–ฑ๏ธ **Interactive mode** - arrow key navigation with `--interactive`
18
+ - ๐Ÿ“ค **Export** - save results as JSON, CSV, or plain text
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ # Using npm
24
+ npm install -g prompthistory
25
+
26
+ # Using pnpm
27
+ pnpm add -g prompthistory
28
+
29
+ # Using bunx (no install needed)
30
+ bunx prompthistory
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ### List recent prompts
36
+
37
+ ```bash
38
+ prompthistory list --limit 10
39
+ ```
40
+
41
+ ### Search by keyword
42
+
43
+ ```bash
44
+ prompthistory search "bug fix"
45
+ prompthistory search "api"
46
+ ```
47
+
48
+ ### Filter by project
49
+
50
+ ```bash
51
+ prompthistory search "refactor" --project tokscale
52
+ ```
53
+
54
+ ### Filter by date
55
+
56
+ ```bash
57
+ prompthistory search "commit" --from 2026-01-01 --to 2026-01-13
58
+ ```
59
+
60
+ ### Show detailed information
61
+
62
+ ```bash
63
+ prompthistory show 5
64
+ ```
65
+
66
+ ## Commands
67
+
68
+ ### `prompthistory search [query]`
69
+
70
+ Search through your prompt history with optional filters.
71
+
72
+ **Options:**
73
+ - `-p, --project <project>` - Filter by project path (partial match)
74
+ - `-f, --from <date>` - Filter from date (YYYY-MM-DD)
75
+ - `-t, --to <date>` - Filter to date (YYYY-MM-DD)
76
+ - `-l, --limit <number>` - Limit number of results (default: 20)
77
+ - `-u, --unique` - Show only unique prompts (deduplicate)
78
+ - `-c, --copy` - Copy selected result to clipboard
79
+ - `-i, --interactive` - Interactive mode with arrow key navigation
80
+ - `--include-slash-commands` - Include slash commands in results (excluded by default)
81
+
82
+ **Examples:**
83
+
84
+ ```bash
85
+ # Basic search
86
+ prompthistory search "commit"
87
+
88
+ # Search with project filter
89
+ prompthistory search "api" --project my-project
90
+
91
+ # Search with date range
92
+ prompthistory search "bug" --from 2026-01-01
93
+
94
+ # Deduplicated results
95
+ prompthistory search "refactor" --unique --limit 10
96
+
97
+ # Interactive mode with copy
98
+ prompthistory search "api" --interactive --copy
99
+ ```
100
+
101
+ ### `prompthistory list`
102
+
103
+ List recent prompts without searching.
104
+
105
+ **Options:**
106
+ - `-l, --limit <number>` - Number of prompts to show (default: 10)
107
+ - `-p, --project <project>` - Filter by project path
108
+ - `--include-slash-commands` - Include slash commands
109
+
110
+ **Examples:**
111
+
112
+ ```bash
113
+ # Show 10 most recent prompts
114
+ prompthistory list
115
+
116
+ # Show 50 recent prompts
117
+ prompthistory list --limit 50
118
+
119
+ # Show recent prompts from specific project
120
+ prompthistory list --project my-project --limit 20
121
+ ```
122
+
123
+ ### `prompthistory show <index>`
124
+
125
+ Show detailed information about a specific prompt by its index from search/list results.
126
+
127
+ **Options:**
128
+ - `-c, --copy` - Copy prompt to clipboard
129
+
130
+ **Examples:**
131
+
132
+ ```bash
133
+ prompthistory list --limit 5
134
+ prompthistory show 3
135
+
136
+ # Copy prompt to clipboard
137
+ prompthistory show 3 --copy
138
+ ```
139
+
140
+ ### `prompthistory export [query]`
141
+
142
+ Export search results to a file or stdout.
143
+
144
+ **Options:**
145
+ - `-p, --project <project>` - Filter by project path
146
+ - `-f, --from <date>` - Filter from date (YYYY-MM-DD)
147
+ - `-t, --to <date>` - Filter to date (YYYY-MM-DD)
148
+ - `-l, --limit <number>` - Limit number of results (default: 100)
149
+ - `--format <format>` - Output format: `json`, `csv`, or `txt` (default: json)
150
+ - `-o, --output <path>` - Output file path (prints to stdout if not specified)
151
+
152
+ **Examples:**
153
+
154
+ ```bash
155
+ # Export as JSON to stdout
156
+ prompthistory export --limit 50 --format json
157
+
158
+ # Export to file
159
+ prompthistory export --format csv --output prompts.csv
160
+
161
+ # Export filtered results
162
+ prompthistory export "api" --project my-project --format txt -o api-prompts.txt
163
+ ```
164
+
165
+ ## Data Source
166
+
167
+ This tool reads from your OpenCode history file:
168
+ - **Location**: `~/.claude/history.jsonl`
169
+ - **Format**: JSON Lines (one JSON object per line)
170
+ - **Read-only**: Never modifies your history file
171
+
172
+ ## Development
173
+
174
+ ```bash
175
+ # Clone the repo
176
+ git clone https://github.com/junhoyeo/prompthistory.git
177
+ cd prompthistory
178
+
179
+ # Install dependencies
180
+ bun install
181
+
182
+ # Build
183
+ bun run build
184
+
185
+ # Test locally
186
+ bun run dev search "test"
187
+ ```
188
+
189
+ ## Tech Stack
190
+
191
+ - **CLI Framework**: [Commander.js](https://github.com/tj/commander.js)
192
+ - **Fuzzy Search**: [Fuse.js](https://fusejs.io)
193
+ - **Output Formatting**: [chalk](https://github.com/chalk/chalk), [cli-table3](https://github.com/cli-table/cli-table3)
194
+ - **Interactive UI**: [@inquirer/prompts](https://github.com/SBoudrias/Inquirer.js)
195
+ - **Clipboard**: [clipboardy](https://github.com/sindresorhus/clipboardy)
196
+ - **Date Handling**: [date-fns](https://date-fns.org)
197
+ - **Schema Validation**: [Zod](https://zod.dev)
198
+ - **Build Tool**: [tsup](https://tsup.egoist.dev)
199
+
200
+ ## License
201
+
202
+ MIT ยฉ [junhoyeo](https://github.com/junhoyeo)
203
+
204
+ ## Related
205
+
206
+ - [OpenCode](https://github.com/cline/cline) - AI coding assistant
207
+ - [tokscale](https://github.com/junhoyeo/tokscale) - Track token usage from coding agents
208
+
209
+ ## Contributing
210
+
211
+ Contributions are welcome! Please feel free to submit a Pull Request.
212
+
213
+ ---
214
+
215
+ Made with โค๏ธ by [@junhoyeo](https://github.com/junhoyeo)
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,342 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { program } from "commander";
5
+ import { homedir } from "os";
6
+ import { join } from "path";
7
+ import { writeFileSync } from "fs";
8
+ import chalk2 from "chalk";
9
+ import clipboard from "clipboardy";
10
+ import { select } from "@inquirer/prompts";
11
+
12
+ // src/core/parser.ts
13
+ import { createReadStream } from "fs";
14
+ import { createInterface } from "readline";
15
+
16
+ // src/core/schema.ts
17
+ import { z } from "zod/v4";
18
+ var MIN_TIMESTAMP = 1e12;
19
+ var MAX_DISPLAY_LENGTH = 500;
20
+ var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
21
+ var PastedContentSchema = z.object({
22
+ id: z.number(),
23
+ type: z.literal("text"),
24
+ content: z.string().optional(),
25
+ contentHash: z.string().optional()
26
+ });
27
+ var HistoryEntrySchema = z.object({
28
+ display: z.string(),
29
+ pastedContents: z.record(z.string(), PastedContentSchema).or(z.object({})),
30
+ timestamp: z.number().min(MIN_TIMESTAMP),
31
+ project: z.string(),
32
+ sessionId: z.string().regex(UUID_REGEX).optional()
33
+ }).passthrough();
34
+ function truncateDisplay(display) {
35
+ if (display.length <= MAX_DISPLAY_LENGTH) {
36
+ return display;
37
+ }
38
+ return display.substring(0, MAX_DISPLAY_LENGTH - 3) + "...";
39
+ }
40
+ function isSlashCommand(display) {
41
+ return display.trimStart().startsWith("/");
42
+ }
43
+ function parseEntry(line, lineNumber) {
44
+ let parsed;
45
+ try {
46
+ parsed = JSON.parse(line);
47
+ } catch {
48
+ console.warn(`[parseEntry] Malformed JSON at line ${lineNumber}: ${line.substring(0, 50)}...`);
49
+ return null;
50
+ }
51
+ const result = HistoryEntrySchema.safeParse(parsed);
52
+ if (!result.success) {
53
+ console.warn(
54
+ `[parseEntry] Schema validation failed at line ${lineNumber}: ${result.error.message}`
55
+ );
56
+ return null;
57
+ }
58
+ const entry = result.data;
59
+ return {
60
+ ...entry,
61
+ pastedContents: entry.pastedContents,
62
+ _lineNumber: lineNumber,
63
+ _truncatedDisplay: truncateDisplay(entry.display),
64
+ _isSlashCommand: isSlashCommand(entry.display)
65
+ };
66
+ }
67
+
68
+ // src/core/parser.ts
69
+ async function parseHistory(filePath) {
70
+ const entries = [];
71
+ const fileStream = createReadStream(filePath);
72
+ const rl = createInterface({
73
+ input: fileStream,
74
+ crlfDelay: Infinity
75
+ });
76
+ let lineNumber = 0;
77
+ for await (const line of rl) {
78
+ lineNumber++;
79
+ if (!line.trim()) continue;
80
+ const entry = parseEntry(line, lineNumber);
81
+ if (entry) {
82
+ entries.push(entry);
83
+ }
84
+ }
85
+ return entries;
86
+ }
87
+
88
+ // src/core/search.ts
89
+ import Fuse from "fuse.js";
90
+ var HistorySearchEngine = class {
91
+ constructor(entries) {
92
+ this.entries = entries;
93
+ this.fuse = new Fuse(entries, {
94
+ keys: ["display", "project"],
95
+ threshold: 0.3,
96
+ includeScore: true,
97
+ includeMatches: true,
98
+ ignoreLocation: true,
99
+ minMatchCharLength: 2
100
+ });
101
+ }
102
+ search(options) {
103
+ let results;
104
+ if (options.query) {
105
+ const fuseResults = this.fuse.search(options.query);
106
+ results = fuseResults.map((r) => ({
107
+ entry: r.item,
108
+ score: r.score,
109
+ matches: r.matches?.map((m) => ({
110
+ key: m.key || "",
111
+ value: m.value || "",
112
+ indices: m.indices || []
113
+ }))
114
+ }));
115
+ } else {
116
+ results = this.entries.map((entry) => ({ entry }));
117
+ }
118
+ results = this.applyFilters(results, options);
119
+ if (options.limit) {
120
+ results = results.slice(0, options.limit);
121
+ }
122
+ return results;
123
+ }
124
+ applyFilters(results, options) {
125
+ let filtered = results;
126
+ if (options.project) {
127
+ const projectFilter = options.project.toLowerCase();
128
+ filtered = filtered.filter(
129
+ (r) => r.entry.project.toLowerCase().includes(projectFilter)
130
+ );
131
+ }
132
+ if (options.from) {
133
+ filtered = filtered.filter((r) => r.entry.timestamp >= options.from.getTime());
134
+ }
135
+ if (options.to) {
136
+ const endOfDay = options.to.getTime() + 86399999;
137
+ filtered = filtered.filter((r) => r.entry.timestamp <= endOfDay);
138
+ }
139
+ if (!options.includeSlashCommands) {
140
+ filtered = filtered.filter((r) => !r.entry.display.trim().startsWith("/"));
141
+ }
142
+ if (options.unique) {
143
+ const seen = /* @__PURE__ */ new Set();
144
+ filtered = filtered.filter((r) => {
145
+ const key = r.entry.display.trim().toLowerCase();
146
+ if (seen.has(key)) return false;
147
+ seen.add(key);
148
+ return true;
149
+ });
150
+ }
151
+ return filtered;
152
+ }
153
+ };
154
+
155
+ // src/utils/formatter.ts
156
+ import chalk from "chalk";
157
+ import Table from "cli-table3";
158
+ import { formatDistanceToNow } from "date-fns";
159
+ function formatSearchResults(results, detailed = false) {
160
+ if (results.length === 0) {
161
+ return chalk.yellow("No results found");
162
+ }
163
+ const table = new Table({
164
+ head: [chalk.cyan("#"), chalk.cyan("Prompt"), chalk.cyan("Project"), chalk.cyan("Time")],
165
+ colWidths: [5, 60, 30, 15],
166
+ wordWrap: true
167
+ });
168
+ results.forEach((result, index) => {
169
+ const { entry } = result;
170
+ const display = truncate(entry.display, 200);
171
+ const project = entry.project.split("/").pop() || entry.project;
172
+ const timeAgo = formatDistanceToNow(new Date(entry.timestamp), { addSuffix: true });
173
+ table.push([
174
+ chalk.gray((index + 1).toString()),
175
+ display,
176
+ chalk.dim(project),
177
+ chalk.dim(timeAgo)
178
+ ]);
179
+ });
180
+ return table.toString();
181
+ }
182
+ function formatDetailedResult(result) {
183
+ const { entry } = result;
184
+ const lines = [
185
+ chalk.bold.cyan("Prompt:"),
186
+ entry.display,
187
+ "",
188
+ chalk.bold.cyan("Project:"),
189
+ entry.project,
190
+ "",
191
+ chalk.bold.cyan("Timestamp:"),
192
+ new Date(entry.timestamp).toLocaleString(),
193
+ "",
194
+ chalk.bold.cyan("Time Ago:"),
195
+ formatDistanceToNow(new Date(entry.timestamp), { addSuffix: true })
196
+ ];
197
+ if (entry.sessionId) {
198
+ lines.push("", chalk.bold.cyan("Session ID:"), entry.sessionId);
199
+ }
200
+ return lines.join("\n");
201
+ }
202
+ function truncate(text, maxLength) {
203
+ if (text.length <= maxLength) return text;
204
+ return text.substring(0, maxLength - 3) + "...";
205
+ }
206
+
207
+ // src/index.ts
208
+ var DEFAULT_HISTORY_PATH = join(homedir(), ".claude", "history.jsonl");
209
+ function exportResults(results, format, outputPath) {
210
+ let content;
211
+ switch (format) {
212
+ case "json":
213
+ content = JSON.stringify(results.map((r) => r.entry), null, 2);
214
+ break;
215
+ case "csv":
216
+ const headers = "timestamp,project,display,sessionId";
217
+ const rows = results.map((r) => {
218
+ const e = r.entry;
219
+ const escapedDisplay = `"${e.display.replace(/"/g, '""').replace(/\n/g, "\\n")}"`;
220
+ return `${e.timestamp},${e.project},${escapedDisplay},${e.sessionId || ""}`;
221
+ });
222
+ content = [headers, ...rows].join("\n");
223
+ break;
224
+ case "txt":
225
+ default:
226
+ content = results.map((r) => r.entry.display).join("\n\n---\n\n");
227
+ break;
228
+ }
229
+ if (outputPath) {
230
+ writeFileSync(outputPath, content);
231
+ return `Exported ${results.length} results to ${outputPath}`;
232
+ }
233
+ return content;
234
+ }
235
+ async function interactiveSelect(results) {
236
+ if (results.length === 0) {
237
+ console.log(chalk2.yellow("No results to select from"));
238
+ return null;
239
+ }
240
+ const choices = results.map((r, i) => ({
241
+ name: `${i + 1}. ${r.entry.display.substring(0, 80)}${r.entry.display.length > 80 ? "..." : ""}`,
242
+ value: i,
243
+ description: `${r.entry.project} - ${new Date(r.entry.timestamp).toLocaleDateString()}`
244
+ }));
245
+ const selectedIndex = await select({
246
+ message: "Select a prompt:",
247
+ choices,
248
+ pageSize: 15
249
+ });
250
+ return results[selectedIndex];
251
+ }
252
+ program.name("prompthistory").description("CLI tool to search and navigate OpenCode prompt history").version("0.1.0");
253
+ program.command("search").description("Search through prompt history").argument("[query]", "search term").option("-p, --project <project>", "filter by project path").option("-f, --from <date>", "filter from date (YYYY-MM-DD)").option("-t, --to <date>", "filter to date (YYYY-MM-DD)").option("-l, --limit <number>", "limit number of results", "20").option("-u, --unique", "show only unique prompts").option("--include-slash-commands", "include slash commands in results").option("-c, --copy", "copy selected result to clipboard").option("-i, --interactive", "interactive mode with arrow key navigation").action(async (query, options) => {
254
+ try {
255
+ const entries = await parseHistory(DEFAULT_HISTORY_PATH);
256
+ const searchEngine = new HistorySearchEngine(entries);
257
+ const searchOptions = {
258
+ query,
259
+ project: options.project,
260
+ from: options.from ? new Date(options.from) : void 0,
261
+ to: options.to ? new Date(options.to) : void 0,
262
+ limit: parseInt(options.limit, 10),
263
+ unique: options.unique,
264
+ includeSlashCommands: options.includeSlashCommands
265
+ };
266
+ const results = searchEngine.search(searchOptions);
267
+ if (options.interactive || options.copy) {
268
+ const selected = await interactiveSelect(results);
269
+ if (selected) {
270
+ if (options.copy) {
271
+ await clipboard.write(selected.entry.display);
272
+ console.log(chalk2.green("Copied to clipboard!"));
273
+ }
274
+ console.log(formatDetailedResult(selected));
275
+ }
276
+ } else {
277
+ console.log(formatSearchResults(results));
278
+ console.log(chalk2.dim(`
279
+ Found ${results.length} results`));
280
+ }
281
+ } catch (error) {
282
+ console.error(chalk2.red("Error:"), error instanceof Error ? error.message : error);
283
+ process.exit(1);
284
+ }
285
+ });
286
+ program.command("list").description("List recent prompts").option("-l, --limit <number>", "number of prompts to show", "10").option("-p, --project <project>", "filter by project path").option("--include-slash-commands", "include slash commands in results").action(async (options) => {
287
+ try {
288
+ const entries = await parseHistory(DEFAULT_HISTORY_PATH);
289
+ const sorted = entries.sort((a, b) => b.timestamp - a.timestamp);
290
+ const searchEngine = new HistorySearchEngine(sorted);
291
+ const results = searchEngine.search({
292
+ project: options.project,
293
+ limit: parseInt(options.limit, 10),
294
+ includeSlashCommands: options.includeSlashCommands
295
+ });
296
+ console.log(formatSearchResults(results));
297
+ } catch (error) {
298
+ console.error(chalk2.red("Error:"), error instanceof Error ? error.message : error);
299
+ process.exit(1);
300
+ }
301
+ });
302
+ program.command("show").description("Show detailed information about a specific prompt").argument("<index>", "index from search/list results").option("-c, --copy", "copy prompt to clipboard").action(async (index, options) => {
303
+ try {
304
+ const entries = await parseHistory(DEFAULT_HISTORY_PATH);
305
+ const idx = parseInt(index, 10) - 1;
306
+ if (idx < 0 || idx >= entries.length) {
307
+ console.error(chalk2.red("Error: Invalid index"));
308
+ process.exit(1);
309
+ }
310
+ const sorted = entries.sort((a, b) => b.timestamp - a.timestamp);
311
+ const entry = sorted[idx];
312
+ if (options.copy) {
313
+ await clipboard.write(entry.display);
314
+ console.log(chalk2.green("Copied to clipboard!"));
315
+ }
316
+ console.log(formatDetailedResult({ entry }));
317
+ } catch (error) {
318
+ console.error(chalk2.red("Error:"), error instanceof Error ? error.message : error);
319
+ process.exit(1);
320
+ }
321
+ });
322
+ program.command("export").description("Export search results to file").argument("[query]", "search term").option("-p, --project <project>", "filter by project path").option("-f, --from <date>", "filter from date (YYYY-MM-DD)").option("-t, --to <date>", "filter to date (YYYY-MM-DD)").option("-l, --limit <number>", "limit number of results", "100").option("--format <format>", "output format: json, csv, txt", "json").option("-o, --output <path>", "output file path").action(async (query, options) => {
323
+ try {
324
+ const entries = await parseHistory(DEFAULT_HISTORY_PATH);
325
+ const searchEngine = new HistorySearchEngine(entries);
326
+ const searchOptions = {
327
+ query,
328
+ project: options.project,
329
+ from: options.from ? new Date(options.from) : void 0,
330
+ to: options.to ? new Date(options.to) : void 0,
331
+ limit: parseInt(options.limit, 10)
332
+ };
333
+ const results = searchEngine.search(searchOptions);
334
+ const output = exportResults(results, options.format, options.output);
335
+ console.log(output);
336
+ } catch (error) {
337
+ console.error(chalk2.red("Error:"), error instanceof Error ? error.message : error);
338
+ process.exit(1);
339
+ }
340
+ });
341
+ program.parse();
342
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/core/parser.ts","../src/core/schema.ts","../src/core/search.ts","../src/utils/formatter.ts"],"sourcesContent":["import { program } from 'commander';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { writeFileSync } from 'node:fs';\nimport chalk from 'chalk';\nimport clipboard from 'clipboardy';\nimport { select } from '@inquirer/prompts';\nimport { parseHistory } from './core/parser.js';\nimport { HistorySearchEngine } from './core/search.js';\nimport { formatSearchResults, formatDetailedResult } from './utils/formatter.js';\nimport type { SearchOptions, SearchResult } from './types/history.js';\n\nconst DEFAULT_HISTORY_PATH = join(homedir(), '.claude', 'history.jsonl');\n\nfunction exportResults(results: SearchResult[], format: string, outputPath?: string): string {\n let content: string;\n \n switch (format) {\n case 'json':\n content = JSON.stringify(results.map(r => r.entry), null, 2);\n break;\n case 'csv':\n const headers = 'timestamp,project,display,sessionId';\n const rows = results.map(r => {\n const e = r.entry;\n const escapedDisplay = `\"${e.display.replace(/\"/g, '\"\"').replace(/\\n/g, '\\\\n')}\"`;\n return `${e.timestamp},${e.project},${escapedDisplay},${e.sessionId || ''}`;\n });\n content = [headers, ...rows].join('\\n');\n break;\n case 'txt':\n default:\n content = results.map(r => r.entry.display).join('\\n\\n---\\n\\n');\n break;\n }\n \n if (outputPath) {\n writeFileSync(outputPath, content);\n return `Exported ${results.length} results to ${outputPath}`;\n }\n \n return content;\n}\n\nasync function interactiveSelect(results: SearchResult[]): Promise<SearchResult | null> {\n if (results.length === 0) {\n console.log(chalk.yellow('No results to select from'));\n return null;\n }\n\n const choices = results.map((r, i) => ({\n name: `${i + 1}. ${r.entry.display.substring(0, 80)}${r.entry.display.length > 80 ? '...' : ''}`,\n value: i,\n description: `${r.entry.project} - ${new Date(r.entry.timestamp).toLocaleDateString()}`,\n }));\n\n const selectedIndex = await select({\n message: 'Select a prompt:',\n choices,\n pageSize: 15,\n });\n\n return results[selectedIndex];\n}\n\nprogram\n .name('prompthistory')\n .description('CLI tool to search and navigate OpenCode prompt history')\n .version('0.1.0');\n\nprogram\n .command('search')\n .description('Search through prompt history')\n .argument('[query]', 'search term')\n .option('-p, --project <project>', 'filter by project path')\n .option('-f, --from <date>', 'filter from date (YYYY-MM-DD)')\n .option('-t, --to <date>', 'filter to date (YYYY-MM-DD)')\n .option('-l, --limit <number>', 'limit number of results', '20')\n .option('-u, --unique', 'show only unique prompts')\n .option('--include-slash-commands', 'include slash commands in results')\n .option('-c, --copy', 'copy selected result to clipboard')\n .option('-i, --interactive', 'interactive mode with arrow key navigation')\n .action(async (query, options) => {\n try {\n const entries = await parseHistory(DEFAULT_HISTORY_PATH);\n const searchEngine = new HistorySearchEngine(entries);\n\n const searchOptions: SearchOptions = {\n query,\n project: options.project,\n from: options.from ? new Date(options.from) : undefined,\n to: options.to ? new Date(options.to) : undefined,\n limit: parseInt(options.limit, 10),\n unique: options.unique,\n includeSlashCommands: options.includeSlashCommands,\n };\n\n const results = searchEngine.search(searchOptions);\n\n if (options.interactive || options.copy) {\n const selected = await interactiveSelect(results);\n if (selected) {\n if (options.copy) {\n await clipboard.write(selected.entry.display);\n console.log(chalk.green('Copied to clipboard!'));\n }\n console.log(formatDetailedResult(selected));\n }\n } else {\n console.log(formatSearchResults(results));\n console.log(chalk.dim(`\\nFound ${results.length} results`));\n }\n } catch (error) {\n console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n\nprogram\n .command('list')\n .description('List recent prompts')\n .option('-l, --limit <number>', 'number of prompts to show', '10')\n .option('-p, --project <project>', 'filter by project path')\n .option('--include-slash-commands', 'include slash commands in results')\n .action(async (options) => {\n try {\n const entries = await parseHistory(DEFAULT_HISTORY_PATH);\n const sorted = entries\n .sort((a, b) => b.timestamp - a.timestamp);\n\n const searchEngine = new HistorySearchEngine(sorted);\n const results = searchEngine.search({\n project: options.project,\n limit: parseInt(options.limit, 10),\n includeSlashCommands: options.includeSlashCommands,\n });\n\n console.log(formatSearchResults(results));\n } catch (error) {\n console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n\nprogram\n .command('show')\n .description('Show detailed information about a specific prompt')\n .argument('<index>', 'index from search/list results')\n .option('-c, --copy', 'copy prompt to clipboard')\n .action(async (index, options) => {\n try {\n const entries = await parseHistory(DEFAULT_HISTORY_PATH);\n const idx = parseInt(index, 10) - 1;\n \n if (idx < 0 || idx >= entries.length) {\n console.error(chalk.red('Error: Invalid index'));\n process.exit(1);\n }\n\n const sorted = entries.sort((a, b) => b.timestamp - a.timestamp);\n const entry = sorted[idx];\n \n if (options.copy) {\n await clipboard.write(entry.display);\n console.log(chalk.green('Copied to clipboard!'));\n }\n \n console.log(formatDetailedResult({ entry }));\n } catch (error) {\n console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n\nprogram\n .command('export')\n .description('Export search results to file')\n .argument('[query]', 'search term')\n .option('-p, --project <project>', 'filter by project path')\n .option('-f, --from <date>', 'filter from date (YYYY-MM-DD)')\n .option('-t, --to <date>', 'filter to date (YYYY-MM-DD)')\n .option('-l, --limit <number>', 'limit number of results', '100')\n .option('--format <format>', 'output format: json, csv, txt', 'json')\n .option('-o, --output <path>', 'output file path')\n .action(async (query, options) => {\n try {\n const entries = await parseHistory(DEFAULT_HISTORY_PATH);\n const searchEngine = new HistorySearchEngine(entries);\n\n const searchOptions: SearchOptions = {\n query,\n project: options.project,\n from: options.from ? new Date(options.from) : undefined,\n to: options.to ? new Date(options.to) : undefined,\n limit: parseInt(options.limit, 10),\n };\n\n const results = searchEngine.search(searchOptions);\n const output = exportResults(results, options.format, options.output);\n console.log(output);\n } catch (error) {\n console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n\nprogram.parse();\n","import { createReadStream } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { EnrichedEntry } from '../types/history.js';\nimport { parseEntry } from './schema.js';\n\nexport async function parseHistory(filePath: string): Promise<EnrichedEntry[]> {\n const entries: EnrichedEntry[] = [];\n \n const fileStream = createReadStream(filePath);\n const rl = createInterface({\n input: fileStream,\n crlfDelay: Infinity,\n });\n\n let lineNumber = 0;\n for await (const line of rl) {\n lineNumber++;\n if (!line.trim()) continue;\n \n const entry = parseEntry(line, lineNumber);\n if (entry) {\n entries.push(entry);\n }\n }\n\n return entries;\n}\n","import { z } from 'zod/v4';\nimport type { EnrichedEntry } from '../types/history.js';\n\nconst MIN_TIMESTAMP = 1_000_000_000_000; // Milliseconds (year ~2001)\nconst MAX_DISPLAY_LENGTH = 500;\nconst UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nconst PastedContentSchema = z.object({\n id: z.number(),\n type: z.literal('text'),\n content: z.string().optional(),\n contentHash: z.string().optional(),\n});\n\nexport const HistoryEntrySchema = z\n .object({\n display: z.string(),\n pastedContents: z.record(z.string(), PastedContentSchema).or(z.object({})),\n timestamp: z.number().min(MIN_TIMESTAMP),\n project: z.string(),\n sessionId: z.string().regex(UUID_REGEX).optional(),\n })\n .passthrough();\n\nexport type ParsedHistoryEntry = z.infer<typeof HistoryEntrySchema>;\n\nfunction truncateDisplay(display: string): string {\n if (display.length <= MAX_DISPLAY_LENGTH) {\n return display;\n }\n return display.substring(0, MAX_DISPLAY_LENGTH - 3) + '...';\n}\n\nfunction isSlashCommand(display: string): boolean {\n return display.trimStart().startsWith('/');\n}\n\nexport function parseEntry(\n line: string,\n lineNumber: number,\n): EnrichedEntry | null {\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(line);\n } catch {\n console.warn(`[parseEntry] Malformed JSON at line ${lineNumber}: ${line.substring(0, 50)}...`);\n return null;\n }\n\n const result = HistoryEntrySchema.safeParse(parsed);\n\n if (!result.success) {\n console.warn(\n `[parseEntry] Schema validation failed at line ${lineNumber}: ${result.error.message}`,\n );\n return null;\n }\n\n const entry = result.data;\n\n return {\n ...entry,\n pastedContents: entry.pastedContents as EnrichedEntry['pastedContents'],\n _lineNumber: lineNumber,\n _truncatedDisplay: truncateDisplay(entry.display),\n _isSlashCommand: isSlashCommand(entry.display),\n };\n}\n","import Fuse from 'fuse.js';\nimport type { EnrichedEntry, SearchOptions, SearchResult } from '../types/history.js';\n\nexport class HistorySearchEngine {\n private fuse: Fuse<EnrichedEntry>;\n private entries: EnrichedEntry[];\n\n constructor(entries: EnrichedEntry[]) {\n this.entries = entries;\n this.fuse = new Fuse(entries, {\n keys: ['display', 'project'],\n threshold: 0.3,\n includeScore: true,\n includeMatches: true,\n ignoreLocation: true,\n minMatchCharLength: 2,\n });\n }\n\n search(options: SearchOptions): SearchResult[] {\n let results: SearchResult[];\n\n if (options.query) {\n const fuseResults = this.fuse.search(options.query);\n results = fuseResults.map((r) => ({\n entry: r.item,\n score: r.score,\n matches: r.matches?.map((m) => ({\n key: m.key || '',\n value: m.value || '',\n indices: (m.indices || []) as Array<[number, number]>,\n })),\n }));\n } else {\n // Use stored entries array instead of accessing Fuse internals\n results = this.entries.map((entry) => ({ entry }));\n }\n\n // Apply filters\n results = this.applyFilters(results, options);\n\n // Apply limit\n if (options.limit) {\n results = results.slice(0, options.limit);\n }\n\n return results;\n }\n\n private applyFilters(results: SearchResult[], options: SearchOptions): SearchResult[] {\n let filtered = results;\n\n // Filter by project\n if (options.project) {\n const projectFilter = options.project.toLowerCase();\n filtered = filtered.filter((r) =>\n r.entry.project.toLowerCase().includes(projectFilter)\n );\n }\n\n // Filter by date range\n if (options.from) {\n filtered = filtered.filter((r) => r.entry.timestamp >= options.from!.getTime());\n }\n if (options.to) {\n // Add end-of-day (23:59:59.999) to include the entire day\n const endOfDay = options.to!.getTime() + 86399999;\n filtered = filtered.filter((r) => r.entry.timestamp <= endOfDay);\n }\n\n // Filter out slash commands\n if (!options.includeSlashCommands) {\n filtered = filtered.filter((r) => !r.entry.display.trim().startsWith('/'));\n }\n\n // Deduplicate\n if (options.unique) {\n const seen = new Set<string>();\n filtered = filtered.filter((r) => {\n const key = r.entry.display.trim().toLowerCase();\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n }\n\n return filtered;\n }\n}\n","import chalk from 'chalk';\nimport Table from 'cli-table3';\nimport { formatDistanceToNow } from 'date-fns';\nimport type { SearchResult } from '../types/history.js';\n\nexport function formatSearchResults(results: SearchResult[], detailed = false): string {\n if (results.length === 0) {\n return chalk.yellow('No results found');\n }\n\n const table = new Table({\n head: [chalk.cyan('#'), chalk.cyan('Prompt'), chalk.cyan('Project'), chalk.cyan('Time')],\n colWidths: [5, 60, 30, 15],\n wordWrap: true,\n });\n\n results.forEach((result, index) => {\n const { entry } = result;\n const display = truncate(entry.display, 200);\n const project = entry.project.split('/').pop() || entry.project;\n const timeAgo = formatDistanceToNow(new Date(entry.timestamp), { addSuffix: true });\n\n table.push([\n chalk.gray((index + 1).toString()),\n display,\n chalk.dim(project),\n chalk.dim(timeAgo),\n ]);\n });\n\n return table.toString();\n}\n\nexport function formatDetailedResult(result: SearchResult): string {\n const { entry } = result;\n const lines = [\n chalk.bold.cyan('Prompt:'),\n entry.display,\n '',\n chalk.bold.cyan('Project:'),\n entry.project,\n '',\n chalk.bold.cyan('Timestamp:'),\n new Date(entry.timestamp).toLocaleString(),\n '',\n chalk.bold.cyan('Time Ago:'),\n formatDistanceToNow(new Date(entry.timestamp), { addSuffix: true }),\n ];\n\n if (entry.sessionId) {\n lines.push('', chalk.bold.cyan('Session ID:'), entry.sessionId);\n }\n\n return lines.join('\\n');\n}\n\nfunction truncate(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return text.substring(0, maxLength - 3) + '...';\n}\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,qBAAqB;AAC9B,OAAOA,YAAW;AAClB,OAAO,eAAe;AACtB,SAAS,cAAc;;;ACNvB,SAAS,wBAAwB;AACjC,SAAS,uBAAuB;;;ACDhC,SAAS,SAAS;AAGlB,IAAM,gBAAgB;AACtB,IAAM,qBAAqB;AAC3B,IAAM,aAAa;AAEnB,IAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,IAAI,EAAE,OAAO;AAAA,EACb,MAAM,EAAE,QAAQ,MAAM;AAAA,EACtB,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,aAAa,EAAE,OAAO,EAAE,SAAS;AACnC,CAAC;AAEM,IAAM,qBAAqB,EAC/B,OAAO;AAAA,EACN,SAAS,EAAE,OAAO;AAAA,EAClB,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,mBAAmB,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;AAAA,EACzE,WAAW,EAAE,OAAO,EAAE,IAAI,aAAa;AAAA,EACvC,SAAS,EAAE,OAAO;AAAA,EAClB,WAAW,EAAE,OAAO,EAAE,MAAM,UAAU,EAAE,SAAS;AACnD,CAAC,EACA,YAAY;AAIf,SAAS,gBAAgB,SAAyB;AAChD,MAAI,QAAQ,UAAU,oBAAoB;AACxC,WAAO;AAAA,EACT;AACA,SAAO,QAAQ,UAAU,GAAG,qBAAqB,CAAC,IAAI;AACxD;AAEA,SAAS,eAAe,SAA0B;AAChD,SAAO,QAAQ,UAAU,EAAE,WAAW,GAAG;AAC3C;AAEO,SAAS,WACd,MACA,YACsB;AACtB,MAAI;AAEJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,YAAQ,KAAK,uCAAuC,UAAU,KAAK,KAAK,UAAU,GAAG,EAAE,CAAC,KAAK;AAC7F,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,mBAAmB,UAAU,MAAM;AAElD,MAAI,CAAC,OAAO,SAAS;AACnB,YAAQ;AAAA,MACN,iDAAiD,UAAU,KAAK,OAAO,MAAM,OAAO;AAAA,IACtF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,OAAO;AAErB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,gBAAgB,MAAM;AAAA,IACtB,aAAa;AAAA,IACb,mBAAmB,gBAAgB,MAAM,OAAO;AAAA,IAChD,iBAAiB,eAAe,MAAM,OAAO;AAAA,EAC/C;AACF;;;AD/DA,eAAsB,aAAa,UAA4C;AAC7E,QAAM,UAA2B,CAAC;AAElC,QAAM,aAAa,iBAAiB,QAAQ;AAC5C,QAAM,KAAK,gBAAgB;AAAA,IACzB,OAAO;AAAA,IACP,WAAW;AAAA,EACb,CAAC;AAED,MAAI,aAAa;AACjB,mBAAiB,QAAQ,IAAI;AAC3B;AACA,QAAI,CAAC,KAAK,KAAK,EAAG;AAElB,UAAM,QAAQ,WAAW,MAAM,UAAU;AACzC,QAAI,OAAO;AACT,cAAQ,KAAK,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SAAO;AACT;;;AE1BA,OAAO,UAAU;AAGV,IAAM,sBAAN,MAA0B;AAAA,EAI/B,YAAY,SAA0B;AACpC,SAAK,UAAU;AACf,SAAK,OAAO,IAAI,KAAK,SAAS;AAAA,MAC5B,MAAM,CAAC,WAAW,SAAS;AAAA,MAC3B,WAAW;AAAA,MACX,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,gBAAgB;AAAA,MAChB,oBAAoB;AAAA,IACtB,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,SAAwC;AAC7C,QAAI;AAEJ,QAAI,QAAQ,OAAO;AACjB,YAAM,cAAc,KAAK,KAAK,OAAO,QAAQ,KAAK;AAClD,gBAAU,YAAY,IAAI,CAAC,OAAO;AAAA,QAChC,OAAO,EAAE;AAAA,QACT,OAAO,EAAE;AAAA,QACT,SAAS,EAAE,SAAS,IAAI,CAAC,OAAO;AAAA,UAC9B,KAAK,EAAE,OAAO;AAAA,UACd,OAAO,EAAE,SAAS;AAAA,UAClB,SAAU,EAAE,WAAW,CAAC;AAAA,QAC1B,EAAE;AAAA,MACJ,EAAE;AAAA,IACJ,OAAO;AAEL,gBAAU,KAAK,QAAQ,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE;AAAA,IACnD;AAGA,cAAU,KAAK,aAAa,SAAS,OAAO;AAG5C,QAAI,QAAQ,OAAO;AACjB,gBAAU,QAAQ,MAAM,GAAG,QAAQ,KAAK;AAAA,IAC1C;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,aAAa,SAAyB,SAAwC;AACpF,QAAI,WAAW;AAGf,QAAI,QAAQ,SAAS;AACnB,YAAM,gBAAgB,QAAQ,QAAQ,YAAY;AAClD,iBAAW,SAAS;AAAA,QAAO,CAAC,MAC1B,EAAE,MAAM,QAAQ,YAAY,EAAE,SAAS,aAAa;AAAA,MACtD;AAAA,IACF;AAGA,QAAI,QAAQ,MAAM;AAChB,iBAAW,SAAS,OAAO,CAAC,MAAM,EAAE,MAAM,aAAa,QAAQ,KAAM,QAAQ,CAAC;AAAA,IAChF;AACA,QAAI,QAAQ,IAAI;AAEd,YAAM,WAAW,QAAQ,GAAI,QAAQ,IAAI;AACzC,iBAAW,SAAS,OAAO,CAAC,MAAM,EAAE,MAAM,aAAa,QAAQ;AAAA,IACjE;AAGA,QAAI,CAAC,QAAQ,sBAAsB;AACjC,iBAAW,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,QAAQ,KAAK,EAAE,WAAW,GAAG,CAAC;AAAA,IAC3E;AAGA,QAAI,QAAQ,QAAQ;AAClB,YAAM,OAAO,oBAAI,IAAY;AAC7B,iBAAW,SAAS,OAAO,CAAC,MAAM;AAChC,cAAM,MAAM,EAAE,MAAM,QAAQ,KAAK,EAAE,YAAY;AAC/C,YAAI,KAAK,IAAI,GAAG,EAAG,QAAO;AAC1B,aAAK,IAAI,GAAG;AACZ,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AACF;;;ACxFA,OAAO,WAAW;AAClB,OAAO,WAAW;AAClB,SAAS,2BAA2B;AAG7B,SAAS,oBAAoB,SAAyB,WAAW,OAAe;AACrF,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,MAAM,OAAO,kBAAkB;AAAA,EACxC;AAEA,QAAM,QAAQ,IAAI,MAAM;AAAA,IACtB,MAAM,CAAC,MAAM,KAAK,GAAG,GAAG,MAAM,KAAK,QAAQ,GAAG,MAAM,KAAK,SAAS,GAAG,MAAM,KAAK,MAAM,CAAC;AAAA,IACvF,WAAW,CAAC,GAAG,IAAI,IAAI,EAAE;AAAA,IACzB,UAAU;AAAA,EACZ,CAAC;AAED,UAAQ,QAAQ,CAAC,QAAQ,UAAU;AACjC,UAAM,EAAE,MAAM,IAAI;AAClB,UAAM,UAAU,SAAS,MAAM,SAAS,GAAG;AAC3C,UAAM,UAAU,MAAM,QAAQ,MAAM,GAAG,EAAE,IAAI,KAAK,MAAM;AACxD,UAAM,UAAU,oBAAoB,IAAI,KAAK,MAAM,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AAElF,UAAM,KAAK;AAAA,MACT,MAAM,MAAM,QAAQ,GAAG,SAAS,CAAC;AAAA,MACjC;AAAA,MACA,MAAM,IAAI,OAAO;AAAA,MACjB,MAAM,IAAI,OAAO;AAAA,IACnB,CAAC;AAAA,EACH,CAAC;AAED,SAAO,MAAM,SAAS;AACxB;AAEO,SAAS,qBAAqB,QAA8B;AACjE,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,QAAQ;AAAA,IACZ,MAAM,KAAK,KAAK,SAAS;AAAA,IACzB,MAAM;AAAA,IACN;AAAA,IACA,MAAM,KAAK,KAAK,UAAU;AAAA,IAC1B,MAAM;AAAA,IACN;AAAA,IACA,MAAM,KAAK,KAAK,YAAY;AAAA,IAC5B,IAAI,KAAK,MAAM,SAAS,EAAE,eAAe;AAAA,IACzC;AAAA,IACA,MAAM,KAAK,KAAK,WAAW;AAAA,IAC3B,oBAAoB,IAAI,KAAK,MAAM,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,EACpE;AAEA,MAAI,MAAM,WAAW;AACnB,UAAM,KAAK,IAAI,MAAM,KAAK,KAAK,aAAa,GAAG,MAAM,SAAS;AAAA,EAChE;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,SAAS,MAAc,WAA2B;AACzD,MAAI,KAAK,UAAU,UAAW,QAAO;AACrC,SAAO,KAAK,UAAU,GAAG,YAAY,CAAC,IAAI;AAC5C;;;AJ/CA,IAAM,uBAAuB,KAAK,QAAQ,GAAG,WAAW,eAAe;AAEvE,SAAS,cAAc,SAAyB,QAAgB,YAA6B;AAC3F,MAAI;AAEJ,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,gBAAU,KAAK,UAAU,QAAQ,IAAI,OAAK,EAAE,KAAK,GAAG,MAAM,CAAC;AAC3D;AAAA,IACF,KAAK;AACH,YAAM,UAAU;AAChB,YAAM,OAAO,QAAQ,IAAI,OAAK;AAC5B,cAAM,IAAI,EAAE;AACZ,cAAM,iBAAiB,IAAI,EAAE,QAAQ,QAAQ,MAAM,IAAI,EAAE,QAAQ,OAAO,KAAK,CAAC;AAC9E,eAAO,GAAG,EAAE,SAAS,IAAI,EAAE,OAAO,IAAI,cAAc,IAAI,EAAE,aAAa,EAAE;AAAA,MAC3E,CAAC;AACD,gBAAU,CAAC,SAAS,GAAG,IAAI,EAAE,KAAK,IAAI;AACtC;AAAA,IACF,KAAK;AAAA,IACL;AACE,gBAAU,QAAQ,IAAI,OAAK,EAAE,MAAM,OAAO,EAAE,KAAK,aAAa;AAC9D;AAAA,EACJ;AAEA,MAAI,YAAY;AACd,kBAAc,YAAY,OAAO;AACjC,WAAO,YAAY,QAAQ,MAAM,eAAe,UAAU;AAAA,EAC5D;AAEA,SAAO;AACT;AAEA,eAAe,kBAAkB,SAAuD;AACtF,MAAI,QAAQ,WAAW,GAAG;AACxB,YAAQ,IAAIC,OAAM,OAAO,2BAA2B,CAAC;AACrD,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,QAAQ,IAAI,CAAC,GAAG,OAAO;AAAA,IACrC,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,QAAQ,UAAU,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,QAAQ,SAAS,KAAK,QAAQ,EAAE;AAAA,IAC9F,OAAO;AAAA,IACP,aAAa,GAAG,EAAE,MAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,SAAS,EAAE,mBAAmB,CAAC;AAAA,EACvF,EAAE;AAEF,QAAM,gBAAgB,MAAM,OAAO;AAAA,IACjC,SAAS;AAAA,IACT;AAAA,IACA,UAAU;AAAA,EACZ,CAAC;AAED,SAAO,QAAQ,aAAa;AAC9B;AAEA,QACG,KAAK,eAAe,EACpB,YAAY,yDAAyD,EACrE,QAAQ,OAAO;AAElB,QACG,QAAQ,QAAQ,EAChB,YAAY,+BAA+B,EAC3C,SAAS,WAAW,aAAa,EACjC,OAAO,2BAA2B,wBAAwB,EAC1D,OAAO,qBAAqB,+BAA+B,EAC3D,OAAO,mBAAmB,6BAA6B,EACvD,OAAO,wBAAwB,2BAA2B,IAAI,EAC9D,OAAO,gBAAgB,0BAA0B,EACjD,OAAO,4BAA4B,mCAAmC,EACtE,OAAO,cAAc,mCAAmC,EACxD,OAAO,qBAAqB,4CAA4C,EACxE,OAAO,OAAO,OAAO,YAAY;AAChC,MAAI;AACF,UAAM,UAAU,MAAM,aAAa,oBAAoB;AACvD,UAAM,eAAe,IAAI,oBAAoB,OAAO;AAEpD,UAAM,gBAA+B;AAAA,MACnC;AAAA,MACA,SAAS,QAAQ;AAAA,MACjB,MAAM,QAAQ,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI;AAAA,MAC9C,IAAI,QAAQ,KAAK,IAAI,KAAK,QAAQ,EAAE,IAAI;AAAA,MACxC,OAAO,SAAS,QAAQ,OAAO,EAAE;AAAA,MACjC,QAAQ,QAAQ;AAAA,MAChB,sBAAsB,QAAQ;AAAA,IAChC;AAEA,UAAM,UAAU,aAAa,OAAO,aAAa;AAEjD,QAAI,QAAQ,eAAe,QAAQ,MAAM;AACvC,YAAM,WAAW,MAAM,kBAAkB,OAAO;AAChD,UAAI,UAAU;AACZ,YAAI,QAAQ,MAAM;AAChB,gBAAM,UAAU,MAAM,SAAS,MAAM,OAAO;AAC5C,kBAAQ,IAAIA,OAAM,MAAM,sBAAsB,CAAC;AAAA,QACjD;AACA,gBAAQ,IAAI,qBAAqB,QAAQ,CAAC;AAAA,MAC5C;AAAA,IACF,OAAO;AACL,cAAQ,IAAI,oBAAoB,OAAO,CAAC;AACxC,cAAQ,IAAIA,OAAM,IAAI;AAAA,QAAW,QAAQ,MAAM,UAAU,CAAC;AAAA,IAC5D;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAMA,OAAM,IAAI,QAAQ,GAAG,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,qBAAqB,EACjC,OAAO,wBAAwB,6BAA6B,IAAI,EAChE,OAAO,2BAA2B,wBAAwB,EAC1D,OAAO,4BAA4B,mCAAmC,EACtE,OAAO,OAAO,YAAY;AACzB,MAAI;AACF,UAAM,UAAU,MAAM,aAAa,oBAAoB;AACvD,UAAM,SAAS,QACZ,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AAE3C,UAAM,eAAe,IAAI,oBAAoB,MAAM;AACnD,UAAM,UAAU,aAAa,OAAO;AAAA,MAClC,SAAS,QAAQ;AAAA,MACjB,OAAO,SAAS,QAAQ,OAAO,EAAE;AAAA,MACjC,sBAAsB,QAAQ;AAAA,IAChC,CAAC;AAED,YAAQ,IAAI,oBAAoB,OAAO,CAAC;AAAA,EAC1C,SAAS,OAAO;AACd,YAAQ,MAAMA,OAAM,IAAI,QAAQ,GAAG,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,mDAAmD,EAC/D,SAAS,WAAW,gCAAgC,EACpD,OAAO,cAAc,0BAA0B,EAC/C,OAAO,OAAO,OAAO,YAAY;AAChC,MAAI;AACF,UAAM,UAAU,MAAM,aAAa,oBAAoB;AACvD,UAAM,MAAM,SAAS,OAAO,EAAE,IAAI;AAElC,QAAI,MAAM,KAAK,OAAO,QAAQ,QAAQ;AACpC,cAAQ,MAAMA,OAAM,IAAI,sBAAsB,CAAC;AAC/C,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AAC/D,UAAM,QAAQ,OAAO,GAAG;AAExB,QAAI,QAAQ,MAAM;AAChB,YAAM,UAAU,MAAM,MAAM,OAAO;AACnC,cAAQ,IAAIA,OAAM,MAAM,sBAAsB,CAAC;AAAA,IACjD;AAEA,YAAQ,IAAI,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAAA,EAC7C,SAAS,OAAO;AACd,YAAQ,MAAMA,OAAM,IAAI,QAAQ,GAAG,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,+BAA+B,EAC3C,SAAS,WAAW,aAAa,EACjC,OAAO,2BAA2B,wBAAwB,EAC1D,OAAO,qBAAqB,+BAA+B,EAC3D,OAAO,mBAAmB,6BAA6B,EACvD,OAAO,wBAAwB,2BAA2B,KAAK,EAC/D,OAAO,qBAAqB,iCAAiC,MAAM,EACnE,OAAO,uBAAuB,kBAAkB,EAChD,OAAO,OAAO,OAAO,YAAY;AAChC,MAAI;AACF,UAAM,UAAU,MAAM,aAAa,oBAAoB;AACvD,UAAM,eAAe,IAAI,oBAAoB,OAAO;AAEpD,UAAM,gBAA+B;AAAA,MACnC;AAAA,MACA,SAAS,QAAQ;AAAA,MACjB,MAAM,QAAQ,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI;AAAA,MAC9C,IAAI,QAAQ,KAAK,IAAI,KAAK,QAAQ,EAAE,IAAI;AAAA,MACxC,OAAO,SAAS,QAAQ,OAAO,EAAE;AAAA,IACnC;AAEA,UAAM,UAAU,aAAa,OAAO,aAAa;AACjD,UAAM,SAAS,cAAc,SAAS,QAAQ,QAAQ,QAAQ,MAAM;AACpE,YAAQ,IAAI,MAAM;AAAA,EACpB,SAAS,OAAO;AACd,YAAQ,MAAMA,OAAM,IAAI,QAAQ,GAAG,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QAAQ,MAAM;","names":["chalk","chalk"]}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@junhoyeo/prompthistory",
3
+ "version": "0.1.0",
4
+ "description": "CLI tool to search and navigate OpenCode prompt history",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "prompthistory": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "dev": "bun run src/index.ts",
17
+ "type-check": "tsc --noEmit",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "prepublishOnly": "bun run build"
21
+ },
22
+ "keywords": [
23
+ "cli",
24
+ "opencode",
25
+ "history",
26
+ "prompt",
27
+ "search",
28
+ "fuzzy-search"
29
+ ],
30
+ "author": "junhoyeo",
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/junhoyeo/prompthistory.git"
35
+ },
36
+ "homepage": "https://github.com/junhoyeo/prompthistory#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/junhoyeo/prompthistory/issues"
39
+ },
40
+ "dependencies": {
41
+ "@inquirer/prompts": "^8.2.0",
42
+ "chalk": "^5.4.1",
43
+ "cli-table3": "^0.6.5",
44
+ "clipboardy": "^5.0.2",
45
+ "commander": "^12.1.0",
46
+ "date-fns": "^3.3.1",
47
+ "fuse.js": "^7.0.0",
48
+ "zod": "^4.3.5"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^20.17.0",
52
+ "@types/react": "^19.2.8",
53
+ "tsup": "^8.4.0",
54
+ "tsx": "^4.19.2",
55
+ "typescript": "^5.7.3",
56
+ "vitest": "^2.1.8"
57
+ },
58
+ "engines": {
59
+ "node": ">=18.0.0"
60
+ }
61
+ }