shellwise 0.2.3 → 0.2.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shellwise",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Smart command history with inline auto-suggest and fuzzy search for your terminal",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli/import.ts CHANGED
@@ -1,34 +1,65 @@
1
1
  import { existsSync, readFileSync } from "fs";
2
2
  import { homedir } from "os";
3
3
  import { join } from "path";
4
- import { insertCommand } from "../db/queries";
4
+ import { insertCommand, getExistingHashes, hashCommand } from "../db/queries";
5
5
  import { getHostname } from "../utils/platform";
6
6
 
7
7
  export function runImport(shell?: string): void {
8
8
  const home = homedir();
9
+ const existing = getExistingHashes();
10
+ const seen = new Set<string>();
9
11
  let imported = 0;
12
+ let skipped = 0;
10
13
 
11
14
  if (!shell || shell === "zsh") {
12
15
  const zshPath = join(home, ".zsh_history");
13
16
  if (existsSync(zshPath)) {
14
- imported += importZshHistory(zshPath);
17
+ const result = importZshHistory(zshPath, existing, seen);
18
+ imported += result.imported;
19
+ skipped += result.skipped;
15
20
  }
16
21
  }
17
22
 
18
23
  if (!shell || shell === "bash") {
19
24
  const bashPath = join(home, ".bash_history");
20
25
  if (existsSync(bashPath)) {
21
- imported += importBashHistory(bashPath);
26
+ const result = importBashHistory(bashPath, existing, seen);
27
+ imported += result.imported;
28
+ skipped += result.skipped;
22
29
  }
23
30
  }
24
31
 
25
- console.log(`Imported ${imported} commands.`);
32
+ console.log(`Imported ${imported} commands. Skipped ${skipped} duplicates.`);
26
33
  }
27
34
 
28
- function importZshHistory(path: string): number {
35
+ interface ImportResult {
36
+ imported: number;
37
+ skipped: number;
38
+ }
39
+
40
+ function tryInsert(
41
+ cmd: string,
42
+ shell: string,
43
+ hostname: string,
44
+ existing: Set<string>,
45
+ seen: Set<string>
46
+ ): boolean {
47
+ const hash = hashCommand(cmd);
48
+ if (existing.has(hash) || seen.has(hash)) return false;
49
+ seen.add(hash);
50
+ insertCommand({ command: cmd, hostname, shell });
51
+ return true;
52
+ }
53
+
54
+ function importZshHistory(
55
+ path: string,
56
+ existing: Set<string>,
57
+ seen: Set<string>
58
+ ): ImportResult {
29
59
  const content = readFileSync(path, "utf-8");
30
60
  const lines = content.split("\n");
31
- let count = 0;
61
+ let imported = 0;
62
+ let skipped = 0;
32
63
  const hostname = getHostname();
33
64
 
34
65
  for (const line of lines) {
@@ -37,12 +68,11 @@ function importZshHistory(path: string): number {
37
68
  if (extMatch) {
38
69
  const cmd = extMatch[2].trim();
39
70
  if (cmd && cmd.length >= 2) {
40
- insertCommand({
41
- command: cmd,
42
- hostname,
43
- shell: "zsh",
44
- });
45
- count++;
71
+ if (tryInsert(cmd, "zsh", hostname, existing, seen)) {
72
+ imported++;
73
+ } else {
74
+ skipped++;
75
+ }
46
76
  }
47
77
  continue;
48
78
  }
@@ -50,27 +80,38 @@ function importZshHistory(path: string): number {
50
80
  // Plain format
51
81
  const cmd = line.trim();
52
82
  if (cmd && cmd.length >= 2 && !cmd.startsWith("#")) {
53
- insertCommand({ command: cmd, hostname, shell: "zsh" });
54
- count++;
83
+ if (tryInsert(cmd, "zsh", hostname, existing, seen)) {
84
+ imported++;
85
+ } else {
86
+ skipped++;
87
+ }
55
88
  }
56
89
  }
57
90
 
58
- return count;
91
+ return { imported, skipped };
59
92
  }
60
93
 
61
- function importBashHistory(path: string): number {
94
+ function importBashHistory(
95
+ path: string,
96
+ existing: Set<string>,
97
+ seen: Set<string>
98
+ ): ImportResult {
62
99
  const content = readFileSync(path, "utf-8");
63
100
  const lines = content.split("\n");
64
- let count = 0;
101
+ let imported = 0;
102
+ let skipped = 0;
65
103
  const hostname = getHostname();
66
104
 
67
105
  for (const line of lines) {
68
106
  const cmd = line.trim();
69
107
  if (cmd && cmd.length >= 2 && !cmd.startsWith("#")) {
70
- insertCommand({ command: cmd, hostname, shell: "bash" });
71
- count++;
108
+ if (tryInsert(cmd, "bash", hostname, existing, seen)) {
109
+ imported++;
110
+ } else {
111
+ skipped++;
112
+ }
72
113
  }
73
114
  }
74
115
 
75
- return count;
116
+ return { imported, skipped };
76
117
  }
package/src/db/queries.ts CHANGED
@@ -151,7 +151,12 @@ export function pruneOlderThan(days: number): number {
151
151
  const db = getDb();
152
152
  const cutoff = Date.now() - days * 24 * 3600_000;
153
153
 
154
- const result = db.run("DELETE FROM commands WHERE created_at < ?", [cutoff]);
154
+ const result = db.run(
155
+ `DELETE FROM commands WHERE command_hash IN (
156
+ SELECT command_hash FROM command_stats WHERE last_used_at < ?
157
+ )`,
158
+ [cutoff]
159
+ );
155
160
 
156
161
  // Cleanup orphaned stats
157
162
  db.run(
@@ -161,6 +166,16 @@ export function pruneOlderThan(days: number): number {
161
166
  return result.changes;
162
167
  }
163
168
 
169
+ export function getExistingHashes(): Set<string> {
170
+ const db = getDb();
171
+ const rows = db
172
+ .query<{ command_hash: string }, []>(
173
+ "SELECT command_hash FROM command_stats"
174
+ )
175
+ .all();
176
+ return new Set(rows.map((r) => r.command_hash));
177
+ }
178
+
164
179
  export function refreshAllFrecency(): void {
165
180
  const db = getDb();
166
181
  const now = Date.now();