shellwise 0.2.4 → 0.2.6

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
@@ -34,10 +34,13 @@ Tab/Shift+Tab to navigate, Enter to select, Esc to dismiss
34
34
  > **Important:** This is a CLI tool — install it **globally**.
35
35
 
36
36
  ```bash
37
- # Recommended
37
+ # Homebrew
38
+ brew install kurovu146/tap/shellwise
39
+
40
+ # Bun
38
41
  bun install -g shellwise
39
42
 
40
- # Or with npm
43
+ # npm
41
44
  npm install -g shellwise
42
45
  ```
43
46
 
@@ -55,6 +58,19 @@ eval "$(shellwise init zsh)"
55
58
  eval "$(shellwise init bash)"
56
59
  ```
57
60
 
61
+ ## Update
62
+
63
+ ```bash
64
+ # Homebrew
65
+ brew upgrade shellwise
66
+
67
+ # Bun
68
+ bun install -g shellwise@latest
69
+
70
+ # npm
71
+ npm install -g shellwise@latest
72
+ ```
73
+
58
74
  ## Usage
59
75
 
60
76
  ### Auto-suggest (while typing)
@@ -139,8 +155,14 @@ shellwise import bash # Import from ~/.bash_history
139
155
  ## Uninstall
140
156
 
141
157
  ```bash
158
+ # Homebrew
159
+ brew uninstall shellwise
160
+
161
+ # Bun
142
162
  bun remove -g shellwise
143
- # or: npm uninstall -g shellwise
163
+
164
+ # npm
165
+ npm uninstall -g shellwise
144
166
  ```
145
167
 
146
168
  Shell integration is automatically removed on uninstall. If you still see errors after uninstalling, manually remove these lines from your `~/.zshrc` (or `~/.bashrc`):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shellwise",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
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/add.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { insertCommand } from "../db/queries";
2
2
  import { getHostname } from "../utils/platform";
3
- import { IGNORED_COMMANDS } from "../utils/constants";
4
3
 
5
4
  interface AddOptions {
6
5
  command: string;
@@ -14,7 +13,7 @@ interface AddOptions {
14
13
  export function runAdd(opts: AddOptions): void {
15
14
  const cmd = opts.command.trim();
16
15
 
17
- // Skip empty, very short, or ignored commands
16
+ // Skip empty or very short commands
18
17
  if (!cmd || cmd.length < 2) return;
19
18
 
20
19
  // Skip commands starting with space (convention)
@@ -23,10 +22,6 @@ export function runAdd(opts: AddOptions): void {
23
22
  // Only save successful commands (exit code 0)
24
23
  if (opts.exitCode !== undefined && opts.exitCode !== 0) return;
25
24
 
26
- // Skip ignored commands (only the base command, not arguments)
27
- const baseCmd = cmd.split(/\s+/)[0];
28
- if (IGNORED_COMMANDS.has(baseCmd)) return;
29
-
30
25
  insertCommand({
31
26
  command: cmd,
32
27
  cwd: opts.cwd,
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
  }
@@ -2,7 +2,6 @@ import { getDb, closeDb } from "../db/connection";
2
2
  import { insertCommand } from "../db/queries";
3
3
  import { getHostname } from "../utils/platform";
4
4
  import { getCommonSuggestions } from "../data/common-commands";
5
- import { IGNORED_COMMANDS } from "../utils/constants";
6
5
  import { parseRequest, getSocketPath, getPidPath, getDaemonPort } from "./protocol";
7
6
  import { unlinkSync, writeFileSync, existsSync } from "fs";
8
7
  import type { Socket } from "bun";
@@ -95,9 +94,6 @@ function handleRequest(raw: string): string {
95
94
  const cmd = req.command.trim();
96
95
  if (!cmd || cmd.length < 2 || cmd.startsWith(" ")) return "OK\n\n";
97
96
  if (req.exitCode !== 0) return "OK\n\n"; // Only save successful commands
98
- const baseCmd = cmd.split(/\s+/)[0];
99
- if (IGNORED_COMMANDS.has(baseCmd)) return "OK\n\n";
100
-
101
97
  insertCommand({
102
98
  command: cmd,
103
99
  cwd: req.cwd || undefined,
package/src/db/queries.ts CHANGED
@@ -166,6 +166,16 @@ export function pruneOlderThan(days: number): number {
166
166
  return result.changes;
167
167
  }
168
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
+
169
179
  export function refreshAllFrecency(): void {
170
180
  const db = getDb();
171
181
  const now = Date.now();
@@ -1 +0,0 @@
1
- export const IGNORED_COMMANDS = new Set(["ls", "cd", "pwd", "exit", "clear", "sw"]);