@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 +27 -11
- package/dist/index.js +204 -22
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
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
|
-
|
|
6
|
-
|
|
5
|
+
<div align="center">
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/prompthistory)
|
|
8
|
+
[](https://www.npmjs.com/package/prompthistory)
|
|
9
|
+
[](https://github.com/junhoyeo/prompthistory/stargazers)
|
|
10
|
+
[](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** -
|
|
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
|
|
168
|
-
- **Location**: `~/.
|
|
169
|
-
- **Format**:
|
|
170
|
-
- **Read-only**:
|
|
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/
|
|
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
|
-
}).
|
|
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
|
|
137
|
-
filtered = filtered.filter((r) => r.entry.timestamp <=
|
|
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,
|
|
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
|
|
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
|
-
|
|
253
|
-
|
|
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:
|
|
261
|
-
to:
|
|
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
|
-
|
|
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:
|
|
330
|
-
to:
|
|
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": "
|
|
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",
|