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 +1 -1
- package/src/cli/import.ts +61 -20
- package/src/db/queries.ts +16 -1
package/package.json
CHANGED
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
54
|
-
|
|
83
|
+
if (tryInsert(cmd, "zsh", hostname, existing, seen)) {
|
|
84
|
+
imported++;
|
|
85
|
+
} else {
|
|
86
|
+
skipped++;
|
|
87
|
+
}
|
|
55
88
|
}
|
|
56
89
|
}
|
|
57
90
|
|
|
58
|
-
return
|
|
91
|
+
return { imported, skipped };
|
|
59
92
|
}
|
|
60
93
|
|
|
61
|
-
function importBashHistory(
|
|
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
|
|
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
|
-
|
|
71
|
-
|
|
108
|
+
if (tryInsert(cmd, "bash", hostname, existing, seen)) {
|
|
109
|
+
imported++;
|
|
110
|
+
} else {
|
|
111
|
+
skipped++;
|
|
112
|
+
}
|
|
72
113
|
}
|
|
73
114
|
}
|
|
74
115
|
|
|
75
|
-
return
|
|
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(
|
|
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();
|