pi-lens 2.0.7 → 2.0.8
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/clients/ast-grep-client.test.ts +146 -116
- package/clients/ast-grep-client.ts +645 -551
- package/clients/biome-client.test.ts +154 -137
- package/clients/biome-client.ts +397 -337
- package/clients/complexity-client.test.ts +188 -200
- package/clients/complexity-client.ts +815 -667
- package/clients/dependency-checker.test.ts +55 -55
- package/clients/dependency-checker.ts +358 -333
- package/clients/go-client.test.ts +121 -111
- package/clients/go-client.ts +218 -216
- package/clients/jscpd-client.test.ts +132 -132
- package/clients/jscpd-client.ts +155 -118
- package/clients/knip-client.test.ts +123 -133
- package/clients/knip-client.ts +231 -218
- package/clients/metrics-client.test.ts +171 -167
- package/clients/metrics-client.ts +283 -252
- package/clients/ruff-client.test.ts +128 -117
- package/clients/ruff-client.ts +300 -269
- package/clients/rust-client.test.ts +104 -85
- package/clients/rust-client.ts +241 -234
- package/clients/subprocess-client.ts +1 -1
- package/clients/test-runner-client.test.ts +248 -215
- package/clients/test-runner-client.ts +728 -608
- package/clients/test-utils.ts +10 -3
- package/clients/todo-scanner.test.ts +288 -202
- package/clients/todo-scanner.ts +225 -187
- package/clients/type-coverage-client.test.ts +119 -119
- package/clients/type-coverage-client.ts +142 -115
- package/clients/types.ts +28 -28
- package/clients/typescript-client.test.ts +99 -93
- package/clients/typescript-client.ts +527 -502
- package/index.ts +662 -212
- package/package.json +1 -1
- package/tsconfig.json +12 -12
|
@@ -1,134 +1,138 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
3
|
import { MetricsClient } from "./metrics-client.js";
|
|
3
4
|
import { createTempFile, setupTestEnvironment } from "./test-utils.js";
|
|
4
|
-
import * as fs from "node:fs";
|
|
5
|
-
import * as path from "node:path";
|
|
6
5
|
|
|
7
6
|
describe("MetricsClient", () => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
7
|
+
let client: MetricsClient;
|
|
8
|
+
let tmpDir: string;
|
|
9
|
+
let cleanup: () => void;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
client = new MetricsClient();
|
|
13
|
+
({ tmpDir, cleanup } = setupTestEnvironment("pi-lens-metrics-test-"));
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
cleanup();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
cleanup();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("calculateEntropy", () => {
|
|
25
|
+
it("should return 0 for empty string", () => {
|
|
26
|
+
expect(client.calculateEntropy("")).toBe(0);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should return 0 for single repeated character", () => {
|
|
30
|
+
expect(client.calculateEntropy("aaaaaa")).toBe(0);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should return 1 for two equally likely characters", () => {
|
|
34
|
+
expect(client.calculateEntropy("ababab")).toBe(1);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should return higher entropy for more diverse content", () => {
|
|
38
|
+
const lowEntropy = "aaaaaaaaaaaaaaaaaaaaaaaaaa";
|
|
39
|
+
const highEntropy = "abcdefghijklmnopqrstuvwxyz";
|
|
40
|
+
|
|
41
|
+
expect(client.calculateEntropy(highEntropy)).toBeGreaterThan(
|
|
42
|
+
client.calculateEntropy(lowEntropy),
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should match expected Shannon entropy for known input", () => {
|
|
47
|
+
// "aabb" has p(a)=0.5, p(b)=0.5, entropy = -2*(0.5*log2(0.5)) = 1
|
|
48
|
+
expect(client.calculateEntropy("aabb")).toBeCloseTo(1, 5);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("recordBaseline", () => {
|
|
53
|
+
it("should record baseline for existing file", () => {
|
|
54
|
+
const content = "const x = 1;\nconst y = 2;";
|
|
55
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
56
|
+
|
|
57
|
+
client.recordBaseline(filePath);
|
|
58
|
+
|
|
59
|
+
const metrics = client.getFileMetrics(filePath);
|
|
60
|
+
expect(metrics).not.toBeNull();
|
|
61
|
+
expect(metrics?.totalLines).toBeGreaterThanOrEqual(2);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should not record baseline for non-existent file", () => {
|
|
65
|
+
client.recordBaseline("/nonexistent/file.ts");
|
|
66
|
+
|
|
67
|
+
// Should not throw, just silently skip
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should not overwrite existing baseline", () => {
|
|
71
|
+
const content1 = "const x = 1;\n";
|
|
72
|
+
const content2 = "const x = 1;\nconst y = 2;\nconst z = 3;\n";
|
|
73
|
+
const filePath = createTempFile(tmpDir, "test.ts", content1);
|
|
74
|
+
|
|
75
|
+
client.recordBaseline(filePath);
|
|
76
|
+
|
|
77
|
+
// Modify file
|
|
78
|
+
fs.writeFileSync(filePath, content2);
|
|
79
|
+
|
|
80
|
+
// Record again - should not update baseline
|
|
81
|
+
client.recordBaseline(filePath);
|
|
82
|
+
|
|
83
|
+
const metrics = client.getFileMetrics(filePath);
|
|
84
|
+
expect(metrics?.entropyStart).toBe(client.calculateEntropy(content1));
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("recordWrite", () => {
|
|
89
|
+
it("should track agent-written lines", () => {
|
|
90
|
+
const original = "const x = 1;\n";
|
|
91
|
+
const filePath = createTempFile(tmpDir, "test.ts", original);
|
|
92
|
+
|
|
93
|
+
client.recordBaseline(filePath);
|
|
94
|
+
|
|
95
|
+
const modified = "const x = 1;\nconst y = 2;\nconst z = 3;\n";
|
|
96
|
+
fs.writeFileSync(filePath, modified);
|
|
97
|
+
client.recordWrite(filePath, modified);
|
|
98
|
+
|
|
99
|
+
const aiRatio = client.getAICodeRatio();
|
|
100
|
+
expect(aiRatio.agentLines).toBeGreaterThan(0);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("should calculate AI code ratio", () => {
|
|
104
|
+
const file1 = createTempFile(
|
|
105
|
+
tmpDir,
|
|
106
|
+
"file1.ts",
|
|
107
|
+
"original content line 1\noriginal content line 2\n",
|
|
108
|
+
);
|
|
109
|
+
const file2 = createTempFile(tmpDir, "file2.ts", "original\n");
|
|
110
|
+
|
|
111
|
+
client.recordBaseline(file1);
|
|
112
|
+
client.recordBaseline(file2);
|
|
113
|
+
|
|
114
|
+
// Simulate agent writing new content
|
|
115
|
+
const newContent1 =
|
|
116
|
+
"original content line 1\noriginal content line 2\nagent line 3\nagent line 4\n";
|
|
117
|
+
fs.writeFileSync(file1, newContent1);
|
|
118
|
+
client.recordWrite(file1, newContent1);
|
|
119
|
+
|
|
120
|
+
const aiRatio = client.getAICodeRatio();
|
|
121
|
+
expect(aiRatio.fileCount).toBe(2);
|
|
122
|
+
expect(aiRatio.ratio).toBeGreaterThanOrEqual(0);
|
|
123
|
+
expect(aiRatio.ratio).toBeLessThanOrEqual(1);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("getEntropyDeltas", () => {
|
|
128
|
+
it("should track entropy changes", () => {
|
|
129
|
+
const simple = "const x = 1;\n";
|
|
130
|
+
const filePath = createTempFile(tmpDir, "test.ts", simple);
|
|
131
|
+
|
|
132
|
+
client.recordBaseline(filePath);
|
|
133
|
+
|
|
134
|
+
// Make file more complex
|
|
135
|
+
const complex = `
|
|
132
136
|
function complex(a: number, b: number, c: number): number {
|
|
133
137
|
if (a > 0) {
|
|
134
138
|
if (b > 0) {
|
|
@@ -140,44 +144,44 @@ function complex(a: number, b: number, c: number): number {
|
|
|
140
144
|
return 0;
|
|
141
145
|
}
|
|
142
146
|
`;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
147
|
+
fs.writeFileSync(filePath, complex);
|
|
148
|
+
client.recordWrite(filePath, complex);
|
|
149
|
+
|
|
150
|
+
const deltas = client.getEntropyDeltas();
|
|
151
|
+
expect(deltas.length).toBe(1);
|
|
152
|
+
expect(deltas[0].delta).not.toBe(0);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe("formatSessionSummary", () => {
|
|
157
|
+
it("should return empty string when no files touched", () => {
|
|
158
|
+
expect(client.formatSessionSummary()).toBe("");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("should format AI code ratio when files are modified", () => {
|
|
162
|
+
const filePath = createTempFile(tmpDir, "test.ts", "original\n");
|
|
163
|
+
client.recordBaseline(filePath);
|
|
164
|
+
|
|
165
|
+
const modified = "original\nnew line 1\nnew line 2\n";
|
|
166
|
+
fs.writeFileSync(filePath, modified);
|
|
167
|
+
client.recordWrite(filePath, modified);
|
|
168
|
+
|
|
169
|
+
const summary = client.formatSessionSummary();
|
|
170
|
+
expect(summary).toContain("AI Code");
|
|
171
|
+
expect(summary).toContain("file(s)");
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe("reset", () => {
|
|
176
|
+
it("should clear all tracked data", () => {
|
|
177
|
+
const filePath = createTempFile(tmpDir, "test.ts", "content\n");
|
|
178
|
+
client.recordBaseline(filePath);
|
|
179
|
+
|
|
180
|
+
client.reset();
|
|
181
|
+
|
|
182
|
+
const aiRatio = client.getAICodeRatio();
|
|
183
|
+
expect(aiRatio.fileCount).toBe(0);
|
|
184
|
+
expect(client.formatSessionSummary()).toBe("");
|
|
185
|
+
});
|
|
186
|
+
});
|
|
183
187
|
});
|