@junhoyeo/prompthistory 0.1.0 โ†’ 1.0.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/README.md CHANGED
@@ -2,8 +2,14 @@
2
2
 
3
3
  > CLI tool to search and navigate your OpenCode prompt history
4
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)
5
+ <div align="center">
6
+
7
+ [![npm Version](https://img.shields.io/npm/v/prompthistory?color=0073FF&labelColor=black&style=flat-square&logo=npm)](https://www.npmjs.com/package/prompthistory)
8
+ [![npm Downloads](https://img.shields.io/npm/dt/prompthistory?color=0073FF&labelColor=black&style=flat-square)](https://www.npmjs.com/package/prompthistory)
9
+ [![GitHub Stars](https://img.shields.io/github/stars/junhoyeo/prompthistory?color=0073FF&labelColor=black&style=flat-square)](https://github.com/junhoyeo/prompthistory/stargazers)
10
+ [![License](https://img.shields.io/badge/license-MIT-white?labelColor=black&style=flat-square)](https://github.com/junhoyeo/prompthistory/blob/main/LICENSE)
11
+
12
+ </div>
7
13
 
8
14
  ## Features
9
15
 
@@ -11,7 +17,7 @@
11
17
  - ๐Ÿ“ **Filter by project** - find prompts from specific projects
12
18
  - ๐Ÿ“… **Date range filtering** - search within time ranges
13
19
  - ๐ŸŽจ **Beautiful output** - formatted tables with colors
14
- - โšก **Fast** - streaming parser handles large history files efficiently
20
+ - โšก **Fast** - native SQLite queries for instant results
15
21
  - ๐Ÿงน **Smart filtering** - automatically excludes slash commands, optional deduplication
16
22
  - ๐Ÿ“‹ **Clipboard support** - copy any prompt with `--copy`
17
23
  - ๐Ÿ–ฑ๏ธ **Interactive mode** - arrow key navigation with `--interactive`
@@ -21,13 +27,13 @@
21
27
 
22
28
  ```bash
23
29
  # Using npm
24
- npm install -g prompthistory
30
+ npm install -g @junhoyeo/prompthistory
25
31
 
26
32
  # Using pnpm
27
- pnpm add -g prompthistory
33
+ pnpm add -g @junhoyeo/prompthistory
28
34
 
29
35
  # Using bunx (no install needed)
30
- bunx prompthistory
36
+ bunx @junhoyeo/prompthistory
31
37
  ```
32
38
 
33
39
  ## Usage
@@ -73,6 +79,8 @@ Search through your prompt history with optional filters.
73
79
  - `-p, --project <project>` - Filter by project path (partial match)
74
80
  - `-f, --from <date>` - Filter from date (YYYY-MM-DD)
75
81
  - `-t, --to <date>` - Filter to date (YYYY-MM-DD)
82
+ - `--today` - Filter to today only
83
+ - `--last-7d` - Filter to last 7 days
76
84
  - `-l, --limit <number>` - Limit number of results (default: 20)
77
85
  - `-u, --unique` - Show only unique prompts (deduplicate)
78
86
  - `-c, --copy` - Copy selected result to clipboard
@@ -105,6 +113,8 @@ List recent prompts without searching.
105
113
  **Options:**
106
114
  - `-l, --limit <number>` - Number of prompts to show (default: 10)
107
115
  - `-p, --project <project>` - Filter by project path
116
+ - `--today` - Filter to today only
117
+ - `--last-7d` - Filter to last 7 days
108
118
  - `--include-slash-commands` - Include slash commands
109
119
 
110
120
  **Examples:**
@@ -145,6 +155,8 @@ Export search results to a file or stdout.
145
155
  - `-p, --project <project>` - Filter by project path
146
156
  - `-f, --from <date>` - Filter from date (YYYY-MM-DD)
147
157
  - `-t, --to <date>` - Filter to date (YYYY-MM-DD)
158
+ - `--today` - Filter to today only
159
+ - `--last-7d` - Filter to last 7 days
148
160
  - `-l, --limit <number>` - Limit number of results (default: 100)
149
161
  - `--format <format>` - Output format: `json`, `csv`, or `txt` (default: json)
150
162
  - `-o, --output <path>` - Output file path (prints to stdout if not specified)
@@ -164,10 +176,12 @@ prompthistory export "api" --project my-project --format txt -o api-prompts.txt
164
176
 
165
177
  ## Data Source
166
178
 
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
179
+ This tool reads from your OpenCode SQLite database:
180
+ - **Location**: `~/.local/share/opencode/opencode.db` (OpenCode 1.2+)
181
+ - **Format**: SQLite database with `message`, `part`, `session`, and `project` tables
182
+ - **Read-only**: Opens database in read-only mode
183
+
184
+ > **Note**: Requires [Bun](https://bun.sh/) runtime due to use of `bun:sqlite` for native SQLite support.
171
185
 
172
186
  ## Development
173
187
 
@@ -188,6 +202,8 @@ bun run dev search "test"
188
202
 
189
203
  ## Tech Stack
190
204
 
205
+ - **Runtime**: [Bun](https://bun.sh/) (required for `bun:sqlite`)
206
+ - **Database**: [bun:sqlite](https://bun.sh/docs/api/sqlite) - Native SQLite bindings
191
207
  - **CLI Framework**: [Commander.js](https://github.com/tj/commander.js)
192
208
  - **Fuzzy Search**: [Fuse.js](https://fusejs.io)
193
209
  - **Output Formatting**: [chalk](https://github.com/chalk/chalk), [cli-table3](https://github.com/cli-table/cli-table3)
@@ -203,7 +219,7 @@ MIT ยฉ [junhoyeo](https://github.com/junhoyeo)
203
219
 
204
220
  ## Related
205
221
 
206
- - [OpenCode](https://github.com/cline/cline) - AI coding assistant
222
+ - [OpenCode](https://github.com/sst/opencode) - AI coding assistant
207
223
  - [tokscale](https://github.com/junhoyeo/tokscale) - Track token usage from coding agents
208
224
 
209
225
  ## Contributing
package/dist/index.js CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { program } from "commander";
5
- import { homedir } from "os";
6
- import { join } from "path";
5
+ import { homedir as homedir2 } from "os";
6
+ import { join as join2 } from "path";
7
7
  import { writeFileSync } from "fs";
8
8
  import chalk2 from "chalk";
9
9
  import clipboard from "clipboardy";
10
10
  import { select } from "@inquirer/prompts";
11
11
 
12
12
  // src/core/parser.ts
13
- import { createReadStream } from "fs";
13
+ import { createReadStream, existsSync as existsSync2 } from "fs";
14
14
  import { createInterface } from "readline";
15
15
 
16
16
  // src/core/schema.ts
@@ -30,7 +30,7 @@ var HistoryEntrySchema = z.object({
30
30
  timestamp: z.number().min(MIN_TIMESTAMP),
31
31
  project: z.string(),
32
32
  sessionId: z.string().regex(UUID_REGEX).optional()
33
- }).passthrough();
33
+ }).catchall(z.unknown());
34
34
  function truncateDisplay(display) {
35
35
  if (display.length <= MAX_DISPLAY_LENGTH) {
36
36
  return display;
@@ -65,8 +65,101 @@ function parseEntry(line, lineNumber) {
65
65
  };
66
66
  }
67
67
 
68
+ // src/core/opencode-parser.ts
69
+ import { Database } from "bun:sqlite";
70
+ import { existsSync } from "fs";
71
+ import { join } from "path";
72
+ import { homedir } from "os";
73
+ var DEFAULT_OPENCODE_DB_PATH = join(
74
+ homedir(),
75
+ ".local",
76
+ "share",
77
+ "opencode",
78
+ "opencode.db"
79
+ );
80
+ function parseOpenCodeHistory(dbPath) {
81
+ const path = dbPath || DEFAULT_OPENCODE_DB_PATH;
82
+ if (!existsSync(path)) {
83
+ console.warn(`[parseOpenCodeHistory] Database not found at ${path}`);
84
+ return [];
85
+ }
86
+ const db = new Database(path, { readonly: true });
87
+ try {
88
+ const query = `
89
+ SELECT
90
+ m.id as message_id,
91
+ m.session_id,
92
+ s.title as session_title,
93
+ p.worktree as project_worktree,
94
+ m.time_created,
95
+ m.data as message_data,
96
+ pt.data as part_data
97
+ FROM message m
98
+ JOIN session s ON m.session_id = s.id
99
+ JOIN project p ON s.project_id = p.id
100
+ LEFT JOIN part pt ON m.id = pt.message_id
101
+ WHERE json_extract(m.data, '$.role') = 'user'
102
+ AND pt.data IS NOT NULL
103
+ AND json_extract(pt.data, '$.type') = 'text'
104
+ ORDER BY m.time_created DESC
105
+ `;
106
+ const rows = db.query(query).all();
107
+ const entries = [];
108
+ for (let i = 0; i < rows.length; i++) {
109
+ const row = rows[i];
110
+ try {
111
+ const partData = JSON.parse(row.part_data);
112
+ if (!partData.text) {
113
+ continue;
114
+ }
115
+ const display = partData.text;
116
+ const isSlashCommand2 = display.trimStart().startsWith("/");
117
+ entries.push({
118
+ display,
119
+ pastedContents: {},
120
+ timestamp: row.time_created,
121
+ project: row.project_worktree,
122
+ sessionId: row.session_id,
123
+ _lineNumber: i + 1,
124
+ _truncatedDisplay: truncateDisplay2(display),
125
+ _isSlashCommand: isSlashCommand2
126
+ });
127
+ } catch {
128
+ continue;
129
+ }
130
+ }
131
+ return entries;
132
+ } finally {
133
+ db.close();
134
+ }
135
+ }
136
+ function truncateDisplay2(display, maxLength = 500) {
137
+ if (display.length <= maxLength) {
138
+ return display;
139
+ }
140
+ return display.substring(0, maxLength - 3) + "...";
141
+ }
142
+ function getOpenCodeDbPath() {
143
+ return DEFAULT_OPENCODE_DB_PATH;
144
+ }
145
+ function openCodeDbExists(dbPath) {
146
+ const path = dbPath || DEFAULT_OPENCODE_DB_PATH;
147
+ return existsSync(path);
148
+ }
149
+
68
150
  // src/core/parser.ts
69
151
  async function parseHistory(filePath) {
152
+ if (filePath.endsWith(".db") || filePath.endsWith("opencode.db")) {
153
+ return parseOpenCodeHistory(filePath);
154
+ }
155
+ if (filePath === getOpenCodeDbPath() || !existsSync2(filePath)) {
156
+ if (openCodeDbExists()) {
157
+ return parseOpenCodeHistory();
158
+ }
159
+ }
160
+ return parseHistoryJsonl(filePath);
161
+ }
162
+ async function parseHistoryJsonl(filePath) {
70
163
  const entries = [];
71
164
  const fileStream = createReadStream(filePath);
72
165
  const rl = createInterface({
@@ -133,8 +226,8 @@ var HistorySearchEngine = class {
133
226
  filtered = filtered.filter((r) => r.entry.timestamp >= options.from.getTime());
134
227
  }
135
228
  if (options.to) {
136
- const endOfDay = options.to.getTime() + 86399999;
137
- filtered = filtered.filter((r) => r.entry.timestamp <= endOfDay);
229
+ const endOfDay2 = options.to.getTime() + 86399999;
230
+ filtered = filtered.filter((r) => r.entry.timestamp <= endOfDay2);
138
231
  }
139
232
  if (!options.includeSlashCommands) {
140
233
  filtered = filtered.filter((r) => !r.entry.display.trim().startsWith("/"));
@@ -156,7 +249,7 @@ var HistorySearchEngine = class {
156
249
  import chalk from "chalk";
157
250
  import Table from "cli-table3";
158
251
  import { formatDistanceToNow } from "date-fns";
159
- function formatSearchResults(results, detailed = false) {
252
+ function formatSearchResults(results, options = {}) {
160
253
  if (results.length === 0) {
161
254
  return chalk.yellow("No results found");
162
255
  }
@@ -167,7 +260,7 @@ function formatSearchResults(results, detailed = false) {
167
260
  });
168
261
  results.forEach((result, index) => {
169
262
  const { entry } = result;
170
- const display = truncate(entry.display, 200);
263
+ const display = options.truncate ? truncate(entry.display, 200) : entry.display;
171
264
  const project = entry.project.split("/").pop() || entry.project;
172
265
  const timeAgo = formatDistanceToNow(new Date(entry.timestamp), { addSuffix: true });
173
266
  table.push([
@@ -204,15 +297,73 @@ function truncate(text, maxLength) {
204
297
  return text.substring(0, maxLength - 3) + "...";
205
298
  }
206
299
 
300
+ // src/utils/date.ts
301
+ import { subDays, startOfDay, endOfDay } from "date-fns";
302
+ function parseRelativeDate(dateStr) {
303
+ const now = /* @__PURE__ */ new Date();
304
+ switch (dateStr.toLowerCase()) {
305
+ case "today":
306
+ return {
307
+ from: startOfDay(now),
308
+ to: endOfDay(now)
309
+ };
310
+ case "yesterday":
311
+ const yesterday = subDays(now, 1);
312
+ return {
313
+ from: startOfDay(yesterday),
314
+ to: endOfDay(yesterday)
315
+ };
316
+ case "last-7d":
317
+ case "7d":
318
+ return {
319
+ from: startOfDay(subDays(now, 7)),
320
+ to: endOfDay(now)
321
+ };
322
+ case "last-30d":
323
+ case "30d":
324
+ return {
325
+ from: startOfDay(subDays(now, 30)),
326
+ to: endOfDay(now)
327
+ };
328
+ default:
329
+ return {};
330
+ }
331
+ }
332
+
207
333
  // src/index.ts
208
- var DEFAULT_HISTORY_PATH = join(homedir(), ".claude", "history.jsonl");
334
+ var LEGACY_HISTORY_PATH = join2(homedir2(), ".claude", "history.jsonl");
335
+ function getDefaultHistoryPath() {
336
+ if (openCodeDbExists()) {
337
+ return getOpenCodeDbPath();
338
+ }
339
+ return LEGACY_HISTORY_PATH;
340
+ }
341
+ var DEFAULT_HISTORY_PATH = getDefaultHistoryPath();
342
+ function resolveDateRange(options) {
343
+ if (options.today) {
344
+ const range = parseRelativeDate("today");
345
+ return { from: range.from, to: range.to };
346
+ }
347
+ if (options.last7d) {
348
+ const range = parseRelativeDate("last-7d");
349
+ return { from: range.from, to: range.to };
350
+ }
351
+ return {
352
+ from: options.from ? new Date(options.from) : void 0,
353
+ to: options.to ? new Date(options.to) : void 0
354
+ };
355
+ }
356
+ function stripInternalFields(entry) {
357
+ const { _lineNumber, _truncatedDisplay, _isSlashCommand, _isDuplicate, ...clean } = entry;
358
+ return clean;
359
+ }
209
360
  function exportResults(results, format, outputPath) {
210
361
  let content;
211
362
  switch (format) {
212
363
  case "json":
213
- content = JSON.stringify(results.map((r) => r.entry), null, 2);
364
+ content = JSON.stringify(results.map((r) => stripInternalFields(r.entry)), null, 2);
214
365
  break;
215
- case "csv":
366
+ case "csv": {
216
367
  const headers = "timestamp,project,display,sessionId";
217
368
  const rows = results.map((r) => {
218
369
  const e = r.entry;
@@ -221,6 +372,7 @@ function exportResults(results, format, outputPath) {
221
372
  });
222
373
  content = [headers, ...rows].join("\n");
223
374
  break;
375
+ }
224
376
  case "txt":
225
377
  default:
226
378
  content = results.map((r) => r.entry.display).join("\n\n---\n\n");
@@ -249,21 +401,43 @@ async function interactiveSelect(results) {
249
401
  });
250
402
  return results[selectedIndex];
251
403
  }
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) => {
404
+ async function interactiveCommand(options) {
405
+ try {
406
+ const entries = await parseHistory(DEFAULT_HISTORY_PATH);
407
+ const searchEngine = new HistorySearchEngine(entries);
408
+ const results = searchEngine.search({
409
+ project: options.project,
410
+ limit: options.limit || 20
411
+ });
412
+ const selected = await interactiveSelect(results);
413
+ if (selected) {
414
+ console.log(formatDetailedResult(selected));
415
+ }
416
+ } catch (error) {
417
+ console.error(chalk2.red("Error:"), error instanceof Error ? error.message : error);
418
+ process.exit(1);
419
+ }
420
+ }
421
+ program.name("prompthistory").description("CLI tool to search and navigate OpenCode prompt history").version("0.1.0").action(async () => {
422
+ await interactiveCommand({});
423
+ });
424
+ 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("--today", "filter to today only").option("--last-7d", "filter to last 7 days").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("--truncate", "truncate long prompts in output").option("-c, --copy", "copy selected result to clipboard").option("-i, --interactive", "interactive mode with arrow key navigation").action(async (query, options) => {
254
425
  try {
426
+ const startTime = performance.now();
255
427
  const entries = await parseHistory(DEFAULT_HISTORY_PATH);
256
428
  const searchEngine = new HistorySearchEngine(entries);
429
+ const dateRange = resolveDateRange(options);
257
430
  const searchOptions = {
258
431
  query,
259
432
  project: options.project,
260
- from: options.from ? new Date(options.from) : void 0,
261
- to: options.to ? new Date(options.to) : void 0,
433
+ from: dateRange.from,
434
+ to: dateRange.to,
262
435
  limit: parseInt(options.limit, 10),
263
436
  unique: options.unique,
264
437
  includeSlashCommands: options.includeSlashCommands
265
438
  };
266
439
  const results = searchEngine.search(searchOptions);
440
+ const elapsed = performance.now() - startTime;
267
441
  if (options.interactive || options.copy) {
268
442
  const selected = await interactiveSelect(results);
269
443
  if (selected) {
@@ -274,26 +448,33 @@ program.command("search").description("Search through prompt history").argument(
274
448
  console.log(formatDetailedResult(selected));
275
449
  }
276
450
  } else {
277
- console.log(formatSearchResults(results));
451
+ console.log(formatSearchResults(results, { truncate: options.truncate }));
278
452
  console.log(chalk2.dim(`
279
- Found ${results.length} results`));
453
+ Found ${results.length} results in ${elapsed.toFixed(0)}ms`));
280
454
  }
281
455
  } catch (error) {
282
456
  console.error(chalk2.red("Error:"), error instanceof Error ? error.message : error);
283
457
  process.exit(1);
284
458
  }
285
459
  });
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) => {
460
+ 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("-f, --from <date>", "filter from date (YYYY-MM-DD)").option("-t, --to <date>", "filter to date (YYYY-MM-DD)").option("--today", "filter to today only").option("--last-7d", "filter to last 7 days").option("--include-slash-commands", "include slash commands in results").option("--truncate", "truncate long prompts in output").action(async (options) => {
287
461
  try {
462
+ const startTime = performance.now();
288
463
  const entries = await parseHistory(DEFAULT_HISTORY_PATH);
289
464
  const sorted = entries.sort((a, b) => b.timestamp - a.timestamp);
465
+ const dateRange = resolveDateRange(options);
290
466
  const searchEngine = new HistorySearchEngine(sorted);
291
467
  const results = searchEngine.search({
292
468
  project: options.project,
469
+ from: dateRange.from,
470
+ to: dateRange.to,
293
471
  limit: parseInt(options.limit, 10),
294
472
  includeSlashCommands: options.includeSlashCommands
295
473
  });
296
- console.log(formatSearchResults(results));
474
+ const elapsed = performance.now() - startTime;
475
+ console.log(formatSearchResults(results, { truncate: options.truncate }));
476
+ console.log(chalk2.dim(`
477
+ Loaded ${entries.length} prompts in ${elapsed.toFixed(0)}ms`));
297
478
  } catch (error) {
298
479
  console.error(chalk2.red("Error:"), error instanceof Error ? error.message : error);
299
480
  process.exit(1);
@@ -319,15 +500,16 @@ program.command("show").description("Show detailed information about a specific
319
500
  process.exit(1);
320
501
  }
321
502
  });
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) => {
503
+ 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("--today", "filter to today only").option("--last-7d", "filter to last 7 days").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
504
  try {
324
505
  const entries = await parseHistory(DEFAULT_HISTORY_PATH);
325
506
  const searchEngine = new HistorySearchEngine(entries);
507
+ const dateRange = resolveDateRange(options);
326
508
  const searchOptions = {
327
509
  query,
328
510
  project: options.project,
329
- from: options.from ? new Date(options.from) : void 0,
330
- to: options.to ? new Date(options.to) : void 0,
511
+ from: dateRange.from,
512
+ to: dateRange.to,
331
513
  limit: parseInt(options.limit, 10)
332
514
  };
333
515
  const results = searchEngine.search(searchOptions);
package/dist/index.js.map CHANGED
@@ -1 +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":"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","names":["chalk","chalk"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/core/parser.ts","../src/core/schema.ts","../src/core/opencode-parser.ts","../src/core/search.ts","../src/utils/formatter.ts","../src/utils/date.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, getOpenCodeDbPath, openCodeDbExists } from './core/parser.js';\nimport { HistorySearchEngine } from './core/search.js';\nimport { formatSearchResults, formatDetailedResult } from './utils/formatter.js';\nimport { parseRelativeDate } from './utils/date.js';\nimport type { SearchOptions, SearchResult } from './types/history.js';\n\nconst LEGACY_HISTORY_PATH = join(homedir(), '.claude', 'history.jsonl');\n\nfunction getDefaultHistoryPath(): string {\n if (openCodeDbExists()) {\n return getOpenCodeDbPath();\n }\n return LEGACY_HISTORY_PATH;\n}\n\nconst DEFAULT_HISTORY_PATH = getDefaultHistoryPath();\n\ninterface DateOptions {\n from?: string;\n to?: string;\n today?: boolean;\n last7d?: boolean;\n}\n\nfunction resolveDateRange(options: DateOptions): { from?: Date; to?: Date } {\n if (options.today) {\n const range = parseRelativeDate('today');\n return { from: range.from, to: range.to };\n }\n if (options.last7d) {\n const range = parseRelativeDate('last-7d');\n return { from: range.from, to: range.to };\n }\n return {\n from: options.from ? new Date(options.from) : undefined,\n to: options.to ? new Date(options.to) : undefined,\n };\n}\n\nfunction stripInternalFields(entry: SearchResult['entry']): Record<string, unknown> {\n const { _lineNumber, _truncatedDisplay, _isSlashCommand, _isDuplicate, ...clean } = entry;\n return clean;\n}\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 => stripInternalFields(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 }\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\nasync function interactiveCommand(options: { project?: string; limit?: number }): Promise<void> {\n try {\n const entries = await parseHistory(DEFAULT_HISTORY_PATH);\n const searchEngine = new HistorySearchEngine(entries);\n \n const results = searchEngine.search({\n project: options.project,\n limit: options.limit || 20,\n });\n \n const selected = await interactiveSelect(results);\n if (selected) {\n console.log(formatDetailedResult(selected));\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 .name('prompthistory')\n .description('CLI tool to search and navigate OpenCode prompt history')\n .version('0.1.0')\n .action(async () => {\n // Launch interactive mode when no command specified\n await interactiveCommand({});\n });\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('--today', 'filter to today only')\n .option('--last-7d', 'filter to last 7 days')\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('--truncate', 'truncate long prompts in output')\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 startTime = performance.now();\n const entries = await parseHistory(DEFAULT_HISTORY_PATH);\n const searchEngine = new HistorySearchEngine(entries);\n\n const dateRange = resolveDateRange(options);\n const searchOptions: SearchOptions = {\n query,\n project: options.project,\n from: dateRange.from,\n to: dateRange.to,\n limit: parseInt(options.limit, 10),\n unique: options.unique,\n includeSlashCommands: options.includeSlashCommands,\n };\n\n const results = searchEngine.search(searchOptions);\n const elapsed = performance.now() - startTime;\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, { truncate: options.truncate }));\n console.log(chalk.dim(`\\nFound ${results.length} results in ${elapsed.toFixed(0)}ms`));\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('-f, --from <date>', 'filter from date (YYYY-MM-DD)')\n .option('-t, --to <date>', 'filter to date (YYYY-MM-DD)')\n .option('--today', 'filter to today only')\n .option('--last-7d', 'filter to last 7 days')\n .option('--include-slash-commands', 'include slash commands in results')\n .option('--truncate', 'truncate long prompts in output')\n .action(async (options) => {\n try {\n const startTime = performance.now();\n const entries = await parseHistory(DEFAULT_HISTORY_PATH);\n const sorted = entries\n .sort((a, b) => b.timestamp - a.timestamp);\n\n const dateRange = resolveDateRange(options);\n const searchEngine = new HistorySearchEngine(sorted);\n const results = searchEngine.search({\n project: options.project,\n from: dateRange.from,\n to: dateRange.to,\n limit: parseInt(options.limit, 10),\n includeSlashCommands: options.includeSlashCommands,\n });\n const elapsed = performance.now() - startTime;\n\n console.log(formatSearchResults(results, { truncate: options.truncate }));\n console.log(chalk.dim(`\\nLoaded ${entries.length} prompts in ${elapsed.toFixed(0)}ms`));\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('--today', 'filter to today only')\n .option('--last-7d', 'filter to last 7 days')\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 dateRange = resolveDateRange(options);\n const searchOptions: SearchOptions = {\n query,\n project: options.project,\n from: dateRange.from,\n to: dateRange.to,\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, existsSync } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { EnrichedEntry } from '../types/history.js';\nimport { parseEntry } from './schema.js';\nimport { parseOpenCodeHistory, getOpenCodeDbPath, openCodeDbExists } from './opencode-parser.js';\n\nexport { parseOpenCodeHistory, getOpenCodeDbPath, openCodeDbExists };\n\nexport async function parseHistory(filePath: string): Promise<EnrichedEntry[]> {\n if (filePath.endsWith('.db') || filePath.endsWith('opencode.db')) {\n return parseOpenCodeHistory(filePath);\n }\n\n if (filePath === getOpenCodeDbPath() || !existsSync(filePath)) {\n if (openCodeDbExists()) {\n return parseOpenCodeHistory();\n }\n }\n\n return parseHistoryJsonl(filePath);\n}\n\nexport async function parseHistoryJsonl(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 .catchall(z.unknown());\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 { Database } from 'bun:sqlite';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { EnrichedEntry } from '../types/history.js';\n\nconst DEFAULT_OPENCODE_DB_PATH = join(\n homedir(),\n '.local',\n 'share',\n 'opencode',\n 'opencode.db'\n);\n\ninterface RawMessageRow {\n message_id: string;\n session_id: string;\n session_title: string;\n project_worktree: string;\n time_created: number;\n message_data: string;\n part_data: string;\n}\n\ninterface PartData {\n type: string;\n text?: string;\n}\n\nexport function parseOpenCodeHistory(dbPath?: string): EnrichedEntry[] {\n const path = dbPath || DEFAULT_OPENCODE_DB_PATH;\n\n if (!existsSync(path)) {\n console.warn(`[parseOpenCodeHistory] Database not found at ${path}`);\n return [];\n }\n\n const db = new Database(path, { readonly: true });\n\n try {\n const query = `\n SELECT \n m.id as message_id,\n m.session_id,\n s.title as session_title,\n p.worktree as project_worktree,\n m.time_created,\n m.data as message_data,\n pt.data as part_data\n FROM message m\n JOIN session s ON m.session_id = s.id\n JOIN project p ON s.project_id = p.id\n LEFT JOIN part pt ON m.id = pt.message_id\n WHERE json_extract(m.data, '$.role') = 'user'\n AND pt.data IS NOT NULL\n AND json_extract(pt.data, '$.type') = 'text'\n ORDER BY m.time_created DESC\n `;\n\n const rows = db.query(query).all() as RawMessageRow[];\n const entries: EnrichedEntry[] = [];\n\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i];\n\n try {\n const partData: PartData = JSON.parse(row.part_data);\n\n if (!partData.text) {\n continue;\n }\n\n const display = partData.text;\n const isSlashCommand = display.trimStart().startsWith('/');\n\n entries.push({\n display,\n pastedContents: {},\n timestamp: row.time_created,\n project: row.project_worktree,\n sessionId: row.session_id,\n _lineNumber: i + 1,\n _truncatedDisplay: truncateDisplay(display),\n _isSlashCommand: isSlashCommand,\n });\n } catch {\n continue;\n }\n }\n\n return entries;\n } finally {\n db.close();\n }\n}\n\nfunction truncateDisplay(display: string, maxLength = 500): string {\n if (display.length <= maxLength) {\n return display;\n }\n return display.substring(0, maxLength - 3) + '...';\n}\n\nexport function getOpenCodeDbPath(): string {\n return DEFAULT_OPENCODE_DB_PATH;\n}\n\nexport function openCodeDbExists(dbPath?: string): boolean {\n const path = dbPath || DEFAULT_OPENCODE_DB_PATH;\n return existsSync(path);\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\ninterface FormatOptions {\n truncate?: boolean;\n}\n\nexport function formatSearchResults(results: SearchResult[], options: FormatOptions = {}): 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 = options.truncate ? truncate(entry.display, 200) : entry.display;\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","import { subDays, startOfDay, endOfDay } from 'date-fns';\n\nexport interface DateRange {\n from?: Date;\n to?: Date;\n}\n\nexport function parseRelativeDate(dateStr: string): DateRange {\n const now = new Date();\n \n switch (dateStr.toLowerCase()) {\n case 'today':\n return {\n from: startOfDay(now),\n to: endOfDay(now),\n };\n case 'yesterday':\n const yesterday = subDays(now, 1);\n return {\n from: startOfDay(yesterday),\n to: endOfDay(yesterday),\n };\n case 'last-7d':\n case '7d':\n return {\n from: startOfDay(subDays(now, 7)),\n to: endOfDay(now),\n };\n case 'last-30d':\n case '30d':\n return {\n from: startOfDay(subDays(now, 30)),\n to: endOfDay(now),\n };\n default:\n return {};\n }\n}\n\nexport function parseDateOption(dateStr: string | undefined): Date | undefined {\n if (!dateStr) return undefined;\n \n const relativeRange = parseRelativeDate(dateStr);\n if (relativeRange.from) {\n return relativeRange.from;\n }\n \n return new Date(dateStr);\n}\n"],"mappings":"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","names":["homedir","join","chalk","existsSync","isSlashCommand","truncateDisplay","existsSync","endOfDay","join","homedir","chalk"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@junhoyeo/prompthistory",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "CLI tool to search and navigate OpenCode prompt history",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -48,6 +48,7 @@
48
48
  "zod": "^4.3.5"
49
49
  },
50
50
  "devDependencies": {
51
+ "@types/bun": "^1.3.9",
51
52
  "@types/node": "^20.17.0",
52
53
  "@types/react": "^19.2.8",
53
54
  "tsup": "^8.4.0",