@hasna/terminal 0.5.3 → 0.6.1
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/.claude/scheduled_tasks.lock +1 -1
- package/dist/cli.js +90 -0
- package/dist/command-rewriter.js +64 -0
- package/dist/diff-cache.js +21 -1
- package/dist/expand-store.js +38 -0
- package/dist/lazy-executor.js +41 -0
- package/dist/mcp/server.js +60 -3
- package/dist/noise-filter.js +70 -0
- package/dist/search/semantic.js +39 -1
- package/package.json +1 -1
- package/src/cli.tsx +91 -0
- package/src/command-rewriter.ts +80 -0
- package/src/compression.test.ts +2 -3
- package/src/diff-cache.ts +25 -2
- package/src/expand-store.ts +51 -0
- package/src/hooks/claude-hook.sh +52 -0
- package/src/lazy-executor.ts +57 -0
- package/src/mcp/server.ts +76 -4
- package/src/noise-filter.ts +83 -0
- package/src/search/semantic.ts +35 -1
- package/dist/compression.test.js +0 -42
- package/dist/diff-cache.test.js +0 -27
- package/dist/economy.test.js +0 -13
- package/dist/parsers/parsers.test.js +0 -136
- package/dist/providers/providers.test.js +0 -14
- package/dist/recipes/recipes.test.js +0 -36
- package/dist/search/search.test.js +0 -22
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { parseOutput, tokenSavings, estimateTokens } from "./index.js";
|
|
3
|
-
describe("parseOutput", () => {
|
|
4
|
-
it("parses ls -la output", () => {
|
|
5
|
-
const output = `total 32
|
|
6
|
-
drwxr-xr-x 5 user staff 160 Mar 10 09:00 src
|
|
7
|
-
-rw-r--r-- 1 user staff 450 Mar 10 09:00 package.json
|
|
8
|
-
lrwxr-xr-x 1 user staff 20 Mar 10 09:00 link -> target`;
|
|
9
|
-
const result = parseOutput("ls -la", output);
|
|
10
|
-
expect(result).not.toBeNull();
|
|
11
|
-
expect(result.parser).toBe("ls");
|
|
12
|
-
const data = result.data;
|
|
13
|
-
expect(data.length).toBe(3);
|
|
14
|
-
expect(data[0].name).toBe("src");
|
|
15
|
-
expect(data[0].type).toBe("dir");
|
|
16
|
-
expect(data[1].name).toBe("package.json");
|
|
17
|
-
expect(data[1].type).toBe("file");
|
|
18
|
-
expect(data[2].type).toBe("symlink");
|
|
19
|
-
});
|
|
20
|
-
it("parses find output and filters node_modules", () => {
|
|
21
|
-
const output = `./src/lib/webhooks.ts
|
|
22
|
-
./node_modules/@types/node/async_hooks.d.ts
|
|
23
|
-
./node_modules/@types/node/perf_hooks.d.ts
|
|
24
|
-
./dist/lib/webhooks.d.ts
|
|
25
|
-
./src/routes/api.ts`;
|
|
26
|
-
const result = parseOutput("find . -name '*hooks*' -type f", output);
|
|
27
|
-
expect(result).not.toBeNull();
|
|
28
|
-
expect(result.parser).toBe("find");
|
|
29
|
-
const data = result.data;
|
|
30
|
-
expect(data.source.length).toBe(2); // webhooks.ts and api.ts
|
|
31
|
-
expect(data.filtered.length).toBeGreaterThan(0);
|
|
32
|
-
expect(data.filtered.find((f) => f.reason === "node_modules")?.count).toBe(2);
|
|
33
|
-
});
|
|
34
|
-
it("parses test output (jest style)", () => {
|
|
35
|
-
const output = `PASS src/auth.test.ts
|
|
36
|
-
FAIL src/db.test.ts
|
|
37
|
-
✗ should connect to database
|
|
38
|
-
Error: Connection refused
|
|
39
|
-
Tests: 5 passed, 1 failed, 1 skipped, 7 total
|
|
40
|
-
Time: 3.2s`;
|
|
41
|
-
const result = parseOutput("npm test", output);
|
|
42
|
-
expect(result).not.toBeNull();
|
|
43
|
-
expect(result.parser).toBe("test");
|
|
44
|
-
const data = result.data;
|
|
45
|
-
expect(data.passed).toBe(5);
|
|
46
|
-
expect(data.failed).toBe(1);
|
|
47
|
-
expect(data.skipped).toBe(1);
|
|
48
|
-
expect(data.total).toBe(7);
|
|
49
|
-
});
|
|
50
|
-
it("parses git status", () => {
|
|
51
|
-
const output = `On branch main
|
|
52
|
-
Changes to be committed:
|
|
53
|
-
new file: src/mcp/server.ts
|
|
54
|
-
modified: src/ai.ts
|
|
55
|
-
|
|
56
|
-
Changes not staged for commit:
|
|
57
|
-
modified: package.json
|
|
58
|
-
|
|
59
|
-
Untracked files:
|
|
60
|
-
src/tree.ts`;
|
|
61
|
-
const result = parseOutput("git status", output);
|
|
62
|
-
expect(result).not.toBeNull();
|
|
63
|
-
expect(result.parser).toBe("git-status");
|
|
64
|
-
const data = result.data;
|
|
65
|
-
expect(data.branch).toBe("main");
|
|
66
|
-
expect(data.staged.length).toBe(2);
|
|
67
|
-
expect(data.unstaged.length).toBe(1);
|
|
68
|
-
expect(data.untracked.length).toBe(1);
|
|
69
|
-
});
|
|
70
|
-
it("parses git log", () => {
|
|
71
|
-
const output = `commit af19ce3456789
|
|
72
|
-
Author: Andrei Hasna <andrei@hasna.com>
|
|
73
|
-
Date: Sat Mar 15 10:00:00 2026
|
|
74
|
-
|
|
75
|
-
feat: add MCP server
|
|
76
|
-
|
|
77
|
-
commit 3963db5123456
|
|
78
|
-
Author: Andrei Hasna <andrei@hasna.com>
|
|
79
|
-
Date: Fri Mar 14 09:00:00 2026
|
|
80
|
-
|
|
81
|
-
feat: tabs and browse mode`;
|
|
82
|
-
const result = parseOutput("git log", output);
|
|
83
|
-
expect(result).not.toBeNull();
|
|
84
|
-
expect(result.parser).toBe("git-log");
|
|
85
|
-
const data = result.data;
|
|
86
|
-
expect(data.length).toBe(2);
|
|
87
|
-
expect(data[0].hash).toBe("af19ce34");
|
|
88
|
-
expect(data[0].message).toBe("feat: add MCP server");
|
|
89
|
-
});
|
|
90
|
-
it("parses npm install output", () => {
|
|
91
|
-
const output = `added 47 packages in 3.2s
|
|
92
|
-
2 vulnerabilities found`;
|
|
93
|
-
const result = parseOutput("npm install", output);
|
|
94
|
-
expect(result).not.toBeNull();
|
|
95
|
-
expect(result.parser).toBe("npm-install");
|
|
96
|
-
const data = result.data;
|
|
97
|
-
expect(data.installed).toBe(47);
|
|
98
|
-
expect(data.duration).toBe("3.2s");
|
|
99
|
-
expect(data.vulnerabilities).toBe(2);
|
|
100
|
-
});
|
|
101
|
-
it("parses build output", () => {
|
|
102
|
-
const output = `Compiling...
|
|
103
|
-
1 warning
|
|
104
|
-
Found 0 errors
|
|
105
|
-
Done in 2.5s`;
|
|
106
|
-
const result = parseOutput("npm run build", output);
|
|
107
|
-
expect(result).not.toBeNull();
|
|
108
|
-
expect(result.parser).toBe("build");
|
|
109
|
-
const data = result.data;
|
|
110
|
-
expect(data.status).toBe("success");
|
|
111
|
-
expect(data.warnings).toBe(1);
|
|
112
|
-
});
|
|
113
|
-
it("detects errors", () => {
|
|
114
|
-
const output = `Error: EADDRINUSE: address already in use :3000`;
|
|
115
|
-
const result = parseOutput("node server.js", output);
|
|
116
|
-
expect(result).not.toBeNull();
|
|
117
|
-
expect(result.parser).toBe("error");
|
|
118
|
-
const data = result.data;
|
|
119
|
-
expect(data.type).toBe("port_in_use");
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
describe("estimateTokens", () => {
|
|
123
|
-
it("estimates roughly 4 chars per token", () => {
|
|
124
|
-
expect(estimateTokens("hello world")).toBe(3); // 11 chars / 4 = 2.75 → 3
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
describe("tokenSavings", () => {
|
|
128
|
-
it("calculates savings correctly", () => {
|
|
129
|
-
const raw = "a".repeat(400); // 100 tokens
|
|
130
|
-
const parsed = { status: "ok" };
|
|
131
|
-
const result = tokenSavings(raw, parsed);
|
|
132
|
-
expect(result.rawTokens).toBe(100);
|
|
133
|
-
expect(result.saved).toBeGreaterThan(0);
|
|
134
|
-
expect(result.percent).toBeGreaterThan(0);
|
|
135
|
-
});
|
|
136
|
-
});
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { availableProviders, resetProvider } from "./index.js";
|
|
3
|
-
describe("providers", () => {
|
|
4
|
-
it("lists available providers", () => {
|
|
5
|
-
const providers = availableProviders();
|
|
6
|
-
expect(providers.length).toBe(2);
|
|
7
|
-
expect(providers[0].name).toBe("cerebras");
|
|
8
|
-
expect(providers[1].name).toBe("anthropic");
|
|
9
|
-
});
|
|
10
|
-
it("resetProvider clears cache", () => {
|
|
11
|
-
// Should not throw
|
|
12
|
-
resetProvider();
|
|
13
|
-
});
|
|
14
|
-
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { genId, substituteVariables, extractVariables } from "./model.js";
|
|
3
|
-
describe("genId", () => {
|
|
4
|
-
it("generates unique ids", () => {
|
|
5
|
-
const a = genId();
|
|
6
|
-
const b = genId();
|
|
7
|
-
expect(a).not.toBe(b);
|
|
8
|
-
expect(a.length).toBe(8);
|
|
9
|
-
});
|
|
10
|
-
});
|
|
11
|
-
describe("substituteVariables", () => {
|
|
12
|
-
it("replaces {var} placeholders", () => {
|
|
13
|
-
expect(substituteVariables("kill -9 {pid}", { pid: "1234" })).toBe("kill -9 1234");
|
|
14
|
-
});
|
|
15
|
-
it("replaces multiple variables", () => {
|
|
16
|
-
expect(substituteVariables("curl {host}:{port}", { host: "localhost", port: "3000" }))
|
|
17
|
-
.toBe("curl localhost:3000");
|
|
18
|
-
});
|
|
19
|
-
it("replaces all occurrences of same variable", () => {
|
|
20
|
-
expect(substituteVariables("{x} and {x}", { x: "y" })).toBe("y and y");
|
|
21
|
-
});
|
|
22
|
-
it("leaves unmatched vars alone", () => {
|
|
23
|
-
expect(substituteVariables("{a} {b}", { a: "1" })).toBe("1 {b}");
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
describe("extractVariables", () => {
|
|
27
|
-
it("extracts variable names from command", () => {
|
|
28
|
-
expect(extractVariables("lsof -i :{port} -t | xargs kill")).toEqual(["port"]);
|
|
29
|
-
});
|
|
30
|
-
it("deduplicates", () => {
|
|
31
|
-
expect(extractVariables("{x} {x} {y}")).toEqual(["x", "y"]);
|
|
32
|
-
});
|
|
33
|
-
it("returns empty for no variables", () => {
|
|
34
|
-
expect(extractVariables("ls -la")).toEqual([]);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { isSourceFile, isExcludedDir, relevanceScore } from "./filters.js";
|
|
3
|
-
describe("filters", () => {
|
|
4
|
-
it("identifies source files", () => {
|
|
5
|
-
expect(isSourceFile("src/app.ts")).toBe(true);
|
|
6
|
-
expect(isSourceFile("index.tsx")).toBe(true);
|
|
7
|
-
expect(isSourceFile("main.py")).toBe(true);
|
|
8
|
-
expect(isSourceFile("image.png")).toBe(false);
|
|
9
|
-
expect(isSourceFile("binary")).toBe(false);
|
|
10
|
-
});
|
|
11
|
-
it("identifies excluded directories", () => {
|
|
12
|
-
expect(isExcludedDir("./node_modules/foo/bar.js")).toBe(true);
|
|
13
|
-
expect(isExcludedDir("./dist/index.js")).toBe(true);
|
|
14
|
-
expect(isExcludedDir("./.git/config")).toBe(true);
|
|
15
|
-
expect(isExcludedDir("./src/lib/utils.ts")).toBe(false);
|
|
16
|
-
});
|
|
17
|
-
it("scores source files highest", () => {
|
|
18
|
-
expect(relevanceScore("src/app.ts")).toBe(10);
|
|
19
|
-
expect(relevanceScore("./node_modules/foo.js")).toBe(0);
|
|
20
|
-
expect(relevanceScore("binary")).toBe(3);
|
|
21
|
-
});
|
|
22
|
-
});
|