pi-lens 3.6.3 → 3.6.4

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.
Files changed (206) hide show
  1. package/package.json +3 -3
  2. package/tsconfig.json +1 -1
  3. package/clients/__tests__/file-time.test.js +0 -216
  4. package/clients/__tests__/file-time.test.ts +0 -276
  5. package/clients/__tests__/format-service.test.js +0 -245
  6. package/clients/__tests__/format-service.test.ts +0 -339
  7. package/clients/__tests__/formatters.test.js +0 -271
  8. package/clients/__tests__/formatters.test.ts +0 -401
  9. package/clients/agent-behavior-client.js +0 -110
  10. package/clients/agent-behavior-client.test.js +0 -94
  11. package/clients/agent-behavior-client.test.ts +0 -116
  12. package/clients/amain-types.js +0 -164
  13. package/clients/architect-client.js +0 -291
  14. package/clients/ast-grep-client.js +0 -253
  15. package/clients/ast-grep-parser.js +0 -84
  16. package/clients/ast-grep-rule-manager.js +0 -89
  17. package/clients/ast-grep-types.js +0 -9
  18. package/clients/auto-loop.js +0 -131
  19. package/clients/biome-client.js +0 -420
  20. package/clients/biome-client.test.js +0 -144
  21. package/clients/biome-client.test.ts +0 -163
  22. package/clients/cache/rule-cache.js +0 -72
  23. package/clients/cache-manager.js +0 -245
  24. package/clients/cache-manager.test.js +0 -197
  25. package/clients/cache-manager.test.ts +0 -299
  26. package/clients/complexity-client.js +0 -675
  27. package/clients/complexity-client.test.js +0 -234
  28. package/clients/complexity-client.test.ts +0 -255
  29. package/clients/config-validator.js +0 -465
  30. package/clients/dependency-checker.js +0 -325
  31. package/clients/dependency-checker.test.js +0 -60
  32. package/clients/dependency-checker.test.ts +0 -71
  33. package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
  34. package/clients/dispatch/__tests__/autofix-integration.test.ts +0 -300
  35. package/clients/dispatch/__tests__/runner-registration.test.js +0 -234
  36. package/clients/dispatch/__tests__/runner-registration.test.ts +0 -286
  37. package/clients/dispatch/debug.log +0 -1
  38. package/clients/dispatch/dispatcher.edge.test.js +0 -82
  39. package/clients/dispatch/dispatcher.edge.test.ts +0 -100
  40. package/clients/dispatch/dispatcher.format.test.js +0 -46
  41. package/clients/dispatch/dispatcher.format.test.ts +0 -58
  42. package/clients/dispatch/dispatcher.inline.test.js +0 -74
  43. package/clients/dispatch/dispatcher.inline.test.ts +0 -93
  44. package/clients/dispatch/dispatcher.js +0 -381
  45. package/clients/dispatch/dispatcher.test.js +0 -116
  46. package/clients/dispatch/dispatcher.test.ts +0 -149
  47. package/clients/dispatch/integration.js +0 -108
  48. package/clients/dispatch/plan.js +0 -183
  49. package/clients/dispatch/runners/architect.js +0 -83
  50. package/clients/dispatch/runners/architect.test.js +0 -138
  51. package/clients/dispatch/runners/architect.test.ts +0 -162
  52. package/clients/dispatch/runners/ast-grep-napi.js +0 -405
  53. package/clients/dispatch/runners/ast-grep-napi.test.js +0 -107
  54. package/clients/dispatch/runners/ast-grep-napi.test.ts +0 -129
  55. package/clients/dispatch/runners/ast-grep.js +0 -157
  56. package/clients/dispatch/runners/biome.js +0 -55
  57. package/clients/dispatch/runners/config-validation.js +0 -67
  58. package/clients/dispatch/runners/go-vet.js +0 -48
  59. package/clients/dispatch/runners/index.js +0 -47
  60. package/clients/dispatch/runners/lsp.js +0 -102
  61. package/clients/dispatch/runners/oxlint.js +0 -67
  62. package/clients/dispatch/runners/oxlint.test.js +0 -230
  63. package/clients/dispatch/runners/oxlint.test.ts +0 -303
  64. package/clients/dispatch/runners/pyright.js +0 -100
  65. package/clients/dispatch/runners/pyright.test.js +0 -98
  66. package/clients/dispatch/runners/pyright.test.ts +0 -121
  67. package/clients/dispatch/runners/python-slop.js +0 -97
  68. package/clients/dispatch/runners/python-slop.test.js +0 -203
  69. package/clients/dispatch/runners/python-slop.test.ts +0 -298
  70. package/clients/dispatch/runners/ruff.js +0 -48
  71. package/clients/dispatch/runners/rust-clippy.js +0 -102
  72. package/clients/dispatch/runners/scan_codebase.test.js +0 -89
  73. package/clients/dispatch/runners/scan_codebase.test.ts +0 -105
  74. package/clients/dispatch/runners/shellcheck.js +0 -147
  75. package/clients/dispatch/runners/shellcheck.test.js +0 -98
  76. package/clients/dispatch/runners/shellcheck.test.ts +0 -129
  77. package/clients/dispatch/runners/similarity.js +0 -230
  78. package/clients/dispatch/runners/spellcheck.js +0 -106
  79. package/clients/dispatch/runners/spellcheck.test.js +0 -158
  80. package/clients/dispatch/runners/spellcheck.test.ts +0 -214
  81. package/clients/dispatch/runners/tree-sitter.js +0 -246
  82. package/clients/dispatch/runners/ts-lsp.js +0 -125
  83. package/clients/dispatch/runners/ts-slop.js +0 -113
  84. package/clients/dispatch/runners/type-safety.js +0 -142
  85. package/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -134
  86. package/clients/dispatch/runners/utils/runner-helpers.js +0 -115
  87. package/clients/dispatch/runners/utils.js +0 -51
  88. package/clients/dispatch/runners/yaml-rule-parser.js +0 -360
  89. package/clients/dispatch/types.js +0 -16
  90. package/clients/dispatch/utils/format-utils.js +0 -44
  91. package/clients/dogfood.test.js +0 -201
  92. package/clients/dogfood.test.ts +0 -269
  93. package/clients/file-kinds.js +0 -177
  94. package/clients/file-kinds.test.js +0 -169
  95. package/clients/file-kinds.test.ts +0 -210
  96. package/clients/file-time.js +0 -152
  97. package/clients/file-utils.js +0 -40
  98. package/clients/fix-scanners.js +0 -204
  99. package/clients/format-service.js +0 -184
  100. package/clients/formatters.js +0 -488
  101. package/clients/go-client.js +0 -203
  102. package/clients/go-client.test.js +0 -127
  103. package/clients/go-client.test.ts +0 -143
  104. package/clients/installer/index.js +0 -403
  105. package/clients/interviewer-templates.js +0 -75
  106. package/clients/interviewer.js +0 -173
  107. package/clients/jscpd-client.js +0 -196
  108. package/clients/jscpd-client.test.js +0 -127
  109. package/clients/jscpd-client.test.ts +0 -145
  110. package/clients/knip-client.js +0 -239
  111. package/clients/knip-client.test.js +0 -112
  112. package/clients/knip-client.test.ts +0 -128
  113. package/clients/latency-logger.js +0 -40
  114. package/clients/lsp/__tests__/client.test.js +0 -310
  115. package/clients/lsp/__tests__/client.test.ts +0 -412
  116. package/clients/lsp/__tests__/config.test.js +0 -167
  117. package/clients/lsp/__tests__/config.test.ts +0 -217
  118. package/clients/lsp/__tests__/error-recovery.test.js +0 -213
  119. package/clients/lsp/__tests__/error-recovery.test.ts +0 -279
  120. package/clients/lsp/__tests__/integration.test.js +0 -127
  121. package/clients/lsp/__tests__/integration.test.ts +0 -160
  122. package/clients/lsp/__tests__/launch.test.js +0 -313
  123. package/clients/lsp/__tests__/launch.test.ts +0 -394
  124. package/clients/lsp/__tests__/server.test.js +0 -259
  125. package/clients/lsp/__tests__/server.test.ts +0 -332
  126. package/clients/lsp/__tests__/service.test.js +0 -438
  127. package/clients/lsp/__tests__/service.test.ts +0 -530
  128. package/clients/lsp/client.js +0 -350
  129. package/clients/lsp/config.js +0 -112
  130. package/clients/lsp/index.js +0 -318
  131. package/clients/lsp/installer/index.js +0 -391
  132. package/clients/lsp/interactive-install.js +0 -221
  133. package/clients/lsp/language.js +0 -170
  134. package/clients/lsp/launch.js +0 -329
  135. package/clients/lsp/lsp/launch.js +0 -116
  136. package/clients/lsp/lsp/server.js +0 -532
  137. package/clients/lsp/lsp-index.js +0 -10
  138. package/clients/lsp/path-utils.js +0 -5
  139. package/clients/lsp/server.js +0 -725
  140. package/clients/lsp/test-py-spawn/requirements.txt +0 -1
  141. package/clients/lsp/test-py-spawn/test.py +0 -3
  142. package/clients/lsp/test-py-svc/requirements.txt +0 -1
  143. package/clients/lsp/test-py-svc/test.py +0 -3
  144. package/clients/lsp/test-python-project/requirements.txt +0 -1
  145. package/clients/lsp/test-python-project/test.py +0 -5
  146. package/clients/metrics-client.js +0 -107
  147. package/clients/metrics-client.test.js +0 -128
  148. package/clients/metrics-client.test.ts +0 -163
  149. package/clients/metrics-history.js +0 -367
  150. package/clients/path-utils.js +0 -142
  151. package/clients/pipeline.js +0 -272
  152. package/clients/production-readiness.js +0 -522
  153. package/clients/project-index.js +0 -255
  154. package/clients/project-metadata.js +0 -531
  155. package/clients/ruff-client.js +0 -325
  156. package/clients/ruff-client.test.js +0 -132
  157. package/clients/ruff-client.test.ts +0 -153
  158. package/clients/rules-scanner.js +0 -97
  159. package/clients/runner-tracker.js +0 -152
  160. package/clients/rust-client.js +0 -205
  161. package/clients/rust-client.test.js +0 -108
  162. package/clients/rust-client.test.ts +0 -130
  163. package/clients/safe-spawn-async.js +0 -163
  164. package/clients/safe-spawn.js +0 -241
  165. package/clients/sanitize.js +0 -291
  166. package/clients/sanitize.test.js +0 -177
  167. package/clients/sanitize.test.ts +0 -223
  168. package/clients/scan-architectural-debt.js +0 -167
  169. package/clients/scan-utils.js +0 -83
  170. package/clients/secrets-scanner.js +0 -119
  171. package/clients/secrets-scanner.test.js +0 -100
  172. package/clients/secrets-scanner.test.ts +0 -113
  173. package/clients/sg-runner.js +0 -292
  174. package/clients/state-matrix.js +0 -160
  175. package/clients/subprocess-client.js +0 -65
  176. package/clients/symbol-types.js +0 -5
  177. package/clients/test-runner-client.js +0 -523
  178. package/clients/test-runner-client.test.js +0 -192
  179. package/clients/test-runner-client.test.ts +0 -253
  180. package/clients/test-utils.js +0 -27
  181. package/clients/test-utils.ts +0 -36
  182. package/clients/todo-scanner.js +0 -200
  183. package/clients/todo-scanner.test.js +0 -301
  184. package/clients/todo-scanner.test.ts +0 -352
  185. package/clients/tool-availability.js +0 -207
  186. package/clients/tree-sitter-client.js +0 -601
  187. package/clients/tree-sitter-query-loader.js +0 -355
  188. package/clients/tree-sitter-symbol-extractor.js +0 -289
  189. package/clients/ts-service.js +0 -129
  190. package/clients/type-coverage-client.js +0 -127
  191. package/clients/type-coverage-client.test.js +0 -105
  192. package/clients/type-coverage-client.test.ts +0 -125
  193. package/clients/type-safety-client.js +0 -138
  194. package/clients/types.js +0 -11
  195. package/clients/typescript-client.codefix.test.js +0 -157
  196. package/clients/typescript-client.codefix.test.ts +0 -186
  197. package/clients/typescript-client.js +0 -509
  198. package/clients/typescript-client.test.js +0 -105
  199. package/clients/typescript-client.test.ts +0 -126
  200. package/commands/booboo.js +0 -1007
  201. package/commands/fix-from-booboo.js +0 -398
  202. package/commands/fix-simplified.js +0 -618
  203. package/commands/rate.js +0 -281
  204. package/commands/rate.test.js +0 -119
  205. package/commands/rate.test.ts +0 -131
  206. package/commands/refactor.js +0 -130
@@ -1,44 +0,0 @@
1
- /**
2
- * Shared formatting utilities for the dispatch system.
3
- */
4
- export const EMOJI = {
5
- blocking: "🔴",
6
- warning: "🟡",
7
- fixed: "✅",
8
- info: "ℹ️",
9
- silent: "📊",
10
- none: "",
11
- };
12
- /**
13
- * Format a single diagnostic for display
14
- */
15
- export function formatDiagnostic(d) {
16
- const line = d.line ? `L${d.line}: ` : "";
17
- const indented = d.message.split("\n").join("\n ");
18
- return ` ${line}${indented}`;
19
- }
20
- /**
21
- * Format a group of diagnostics with semantic header
22
- */
23
- export function formatDiagnostics(diagnostics, semantic, maxDisplay = 10) {
24
- if (diagnostics.length === 0)
25
- return "";
26
- const emoji = EMOJI[semantic] ?? EMOJI.warning;
27
- let output = "";
28
- if (semantic === "blocking") {
29
- output += `\n${emoji} STOP — ${diagnostics.length} issue(s) must be fixed:\n`;
30
- }
31
- else if (semantic === "warning") {
32
- output += `\n${emoji} ${diagnostics.length} warning(s):\n`;
33
- }
34
- else if (semantic === "fixed") {
35
- output += `\n${emoji} Auto-fixed ${diagnostics.length} issue(s):\n`;
36
- }
37
- for (const d of diagnostics.slice(0, maxDisplay)) {
38
- output += `${formatDiagnostic(d)}\n`;
39
- }
40
- if (diagnostics.length > maxDisplay) {
41
- output += ` ... and ${diagnostics.length - maxDisplay} more\n`;
42
- }
43
- return output;
44
- }
@@ -1,201 +0,0 @@
1
- /**
2
- * Meta-test: Run similarity detection on pi-lens codebase
3
- *
4
- * This is a "dogfood" test - we run the reuse detection on our own code
5
- * to see what it finds. Educational and useful for improving the algorithm!
6
- */
7
- import * as fs from "node:fs/promises";
8
- import * as path from "node:path";
9
- import { fileURLToPath } from "node:url";
10
- import { glob } from "glob";
11
- import { beforeAll, describe, expect, it } from "vitest";
12
- import { buildProjectIndex, findSimilarFunctions, } from "./project-index.js";
13
- import { calculateSimilarity as calcMatrixSimilarity } from "./state-matrix.js";
14
- // Find project root by looking for package.json
15
- async function findProjectRoot(startDir) {
16
- let dir = startDir;
17
- while (dir !== path.dirname(dir)) {
18
- try {
19
- await fs.access(path.join(dir, "package.json"));
20
- return dir;
21
- }
22
- catch {
23
- dir = path.dirname(dir);
24
- }
25
- }
26
- throw new Error("Could not find project root (no package.json)");
27
- }
28
- // Test a known similar pair
29
- const _SIMILAR_FUNCTIONS = {
30
- description: "Extracting similar logic patterns in pi-lens",
31
- pairs: [
32
- {
33
- name: "runners/index.ts pattern",
34
- files: [
35
- "clients/dispatch/runners/index.ts",
36
- "clients/dispatch/runners/architect.ts",
37
- ],
38
- expected: "High similarity in runner registration patterns",
39
- },
40
- {
41
- name: "Client pattern",
42
- files: ["clients/typescript-client.ts", "clients/biome-client.ts"],
43
- expected: "Similar client structures",
44
- },
45
- ],
46
- };
47
- describe("🐶 Dogfood Test: Similarity on pi-lens codebase", () => {
48
- let index;
49
- let projectRoot;
50
- beforeAll(async () => {
51
- // Find project root
52
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
53
- projectRoot = await findProjectRoot(__dirname);
54
- // Build index of the entire codebase
55
- console.log("\n🏗️ Building index of pi-lens codebase...");
56
- console.log(` Project root: ${projectRoot}`);
57
- const files = await glob("clients/**/*.ts", {
58
- cwd: projectRoot,
59
- ignore: ["**/*.test.ts", "**/*.spec.ts", "**/node_modules/**"],
60
- });
61
- console.log(` Found ${files.length} source files`);
62
- const absoluteFiles = files.map((f) => path.join(projectRoot, f));
63
- index = await buildProjectIndex(projectRoot, absoluteFiles);
64
- console.log(` Indexed ${index.entries.size} functions`);
65
- // Show some indexed functions
66
- const sample = Array.from(index.entries.values()).slice(0, 5);
67
- console.log("\n📋 Sample indexed functions:");
68
- sample.forEach((e, i) => {
69
- console.log(` ${i + 1}. ${e.id} (${e.transitionCount} transitions)`);
70
- });
71
- }, 30000); // 30s timeout for indexing
72
- describe("Index validation", () => {
73
- it("should have indexed functions", () => {
74
- expect(index.entries.size).toBeGreaterThan(0);
75
- console.log(`\n✅ Indexed ${index.entries.size} functions`);
76
- });
77
- it("should have functions with >20 transitions", () => {
78
- const complex = Array.from(index.entries.values()).filter((e) => e.transitionCount >= 20);
79
- expect(complex.length).toBeGreaterThan(0);
80
- console.log(`\n✅ ${complex.length} functions pass complexity guardrail`);
81
- });
82
- });
83
- describe("Find similar functions in our own codebase", () => {
84
- it("should find similar patterns among runners", async () => {
85
- // Find runner files
86
- const runnerEntries = Array.from(index.entries.values()).filter((e) => e.filePath.includes("dispatch/runners/"));
87
- console.log(`\n🔍 Testing ${runnerEntries.length} runner functions`);
88
- const similarities = [];
89
- // Compare each pair
90
- for (let i = 0; i < runnerEntries.length; i++) {
91
- for (let j = i + 1; j < runnerEntries.length; j++) {
92
- const entry1 = runnerEntries[i];
93
- const entry2 = runnerEntries[j];
94
- // Skip if same file
95
- if (entry1.filePath === entry2.filePath)
96
- continue;
97
- const sim = calcMatrixSimilarity(entry1.matrix, entry2.matrix);
98
- if (sim >= 0.75) {
99
- similarities.push({
100
- func1: entry1.id,
101
- func2: entry2.id,
102
- similarity: sim,
103
- });
104
- }
105
- }
106
- }
107
- // Sort by similarity
108
- similarities.sort((a, b) => b.similarity - a.similarity);
109
- console.log(`\n📊 Found ${similarities.length} similar pairs (>75%):`);
110
- similarities.slice(0, 5).forEach((s, i) => {
111
- console.log(` ${i + 1}. ${s.func1} ↔ ${s.func2}`);
112
- console.log(` Similarity: ${(s.similarity * 100).toFixed(1)}%`);
113
- });
114
- // Log findings but don't fail - this is exploratory
115
- expect(similarities.length).toBeGreaterThanOrEqual(0);
116
- });
117
- it("should find similar client patterns", async () => {
118
- const clientEntries = Array.from(index.entries.values()).filter((e) => e.filePath.includes("clients/") &&
119
- e.filePath.includes("-client.ts") &&
120
- !e.filePath.includes("test"));
121
- console.log(`\n🔍 Testing ${clientEntries.length} client functions`);
122
- const similarities = [];
123
- for (let i = 0; i < clientEntries.length; i++) {
124
- for (let j = i + 1; j < clientEntries.length; j++) {
125
- const entry1 = clientEntries[i];
126
- const entry2 = clientEntries[j];
127
- if (entry1.filePath === entry2.filePath)
128
- continue;
129
- const sim = calcMatrixSimilarity(entry1.matrix, entry2.matrix);
130
- if (sim >= 0.75) {
131
- similarities.push({
132
- func1: entry1.id,
133
- func2: entry2.id,
134
- similarity: sim,
135
- });
136
- }
137
- }
138
- }
139
- similarities.sort((a, b) => b.similarity - a.similarity);
140
- console.log(`\n📊 Found ${similarities.length} similar client patterns (>75%):`);
141
- similarities.slice(0, 3).forEach((s, i) => {
142
- console.log(` ${i + 1}. ${s.func1} ↔ ${s.func2}`);
143
- console.log(` Similarity: ${(s.similarity * 100).toFixed(1)}%`);
144
- });
145
- expect(similarities.length).toBeGreaterThanOrEqual(0);
146
- });
147
- });
148
- describe("Find potential refactor opportunities", () => {
149
- it("should identify duplicate utility functions", () => {
150
- // Look for functions with very high similarity (>90%)
151
- const entries = Array.from(index.entries.values());
152
- const seenPairs = new Set(); // Deduplicate A→B and B→A
153
- const duplicates = [];
154
- for (const entry of entries) {
155
- const matches = findSimilarFunctions(entry.matrix, index, 0.9, 3);
156
- for (const match of matches) {
157
- if (match.targetId === entry.id)
158
- continue;
159
- // Canonical pair key (sorted to avoid A,B and B,A)
160
- const pairKey = [entry.id, match.targetId].sort().join("::");
161
- if (seenPairs.has(pairKey))
162
- continue;
163
- seenPairs.add(pairKey);
164
- duplicates.push({
165
- func: entry.id,
166
- similarTo: match.targetId,
167
- similarity: match.similarity,
168
- });
169
- }
170
- }
171
- console.log(`\n🎯 Found ${duplicates.length} unique potential duplicates (>90%):`);
172
- duplicates.slice(0, 5).forEach((d, i) => {
173
- console.log(` ${i + 1}. ${d.func}`);
174
- console.log(` Similar to: ${d.similarTo}`);
175
- console.log(` Match: ${(d.similarity * 100).toFixed(1)}%`);
176
- });
177
- // This is informational - we don't assert on it
178
- expect(true).toBe(true);
179
- });
180
- });
181
- describe("Complexity distribution", () => {
182
- it("should show transition count distribution", () => {
183
- const entries = Array.from(index.entries.values());
184
- const transitionCounts = entries.map((e) => e.transitionCount);
185
- const avg = transitionCounts.reduce((a, b) => a + b, 0) / transitionCounts.length;
186
- const min = Math.min(...transitionCounts);
187
- const max = Math.max(...transitionCounts);
188
- const belowThreshold = transitionCounts.filter((c) => c < 20).length;
189
- const aboveThreshold = transitionCounts.filter((c) => c >= 20).length;
190
- console.log("\n📊 Complexity Distribution:");
191
- console.log(` Total functions: ${entries.length}`);
192
- console.log(` Below threshold (<20): ${belowThreshold}`);
193
- console.log(` Above threshold (≥20): ${aboveThreshold}`);
194
- console.log(` Min transitions: ${min}`);
195
- console.log(` Max transitions: ${max}`);
196
- console.log(` Average: ${avg.toFixed(1)}`);
197
- // Most functions should pass the guardrail
198
- expect(aboveThreshold).toBeGreaterThan(0);
199
- });
200
- });
201
- });
@@ -1,269 +0,0 @@
1
- /**
2
- * Meta-test: Run similarity detection on pi-lens codebase
3
- *
4
- * This is a "dogfood" test - we run the reuse detection on our own code
5
- * to see what it finds. Educational and useful for improving the algorithm!
6
- */
7
-
8
- import * as fs from "node:fs/promises";
9
- import * as path from "node:path";
10
- import { fileURLToPath } from "node:url";
11
- import { glob } from "glob";
12
- import { beforeAll, describe, expect, it } from "vitest";
13
- import {
14
- buildProjectIndex,
15
- findSimilarFunctions,
16
- type IndexEntry,
17
- type ProjectIndex,
18
- } from "./project-index.js";
19
- import { calculateSimilarity as calcMatrixSimilarity } from "./state-matrix.js";
20
-
21
- // Find project root by looking for package.json
22
- async function findProjectRoot(startDir: string): Promise<string> {
23
- let dir = startDir;
24
- while (dir !== path.dirname(dir)) {
25
- try {
26
- await fs.access(path.join(dir, "package.json"));
27
- return dir;
28
- } catch {
29
- dir = path.dirname(dir);
30
- }
31
- }
32
- throw new Error("Could not find project root (no package.json)");
33
- }
34
-
35
- // Test a known similar pair
36
- const _SIMILAR_FUNCTIONS = {
37
- description: "Extracting similar logic patterns in pi-lens",
38
- pairs: [
39
- {
40
- name: "runners/index.ts pattern",
41
- files: [
42
- "clients/dispatch/runners/index.ts",
43
- "clients/dispatch/runners/architect.ts",
44
- ],
45
- expected: "High similarity in runner registration patterns",
46
- },
47
- {
48
- name: "Client pattern",
49
- files: ["clients/typescript-client.ts", "clients/biome-client.ts"],
50
- expected: "Similar client structures",
51
- },
52
- ],
53
- };
54
-
55
- describe("🐶 Dogfood Test: Similarity on pi-lens codebase", () => {
56
- let index: ProjectIndex;
57
- let projectRoot: string;
58
-
59
- beforeAll(async () => {
60
- // Find project root
61
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
62
- projectRoot = await findProjectRoot(__dirname);
63
-
64
- // Build index of the entire codebase
65
- console.log("\n🏗️ Building index of pi-lens codebase...");
66
- console.log(` Project root: ${projectRoot}`);
67
-
68
- const files = await glob("clients/**/*.ts", {
69
- cwd: projectRoot,
70
- ignore: ["**/*.test.ts", "**/*.spec.ts", "**/node_modules/**"],
71
- });
72
-
73
- console.log(` Found ${files.length} source files`);
74
-
75
- const absoluteFiles = files.map((f) => path.join(projectRoot, f));
76
- index = await buildProjectIndex(projectRoot, absoluteFiles);
77
-
78
- console.log(` Indexed ${index.entries.size} functions`);
79
-
80
- // Show some indexed functions
81
- const sample = Array.from(index.entries.values()).slice(0, 5);
82
- console.log("\n📋 Sample indexed functions:");
83
- sample.forEach((e: IndexEntry, i: number) => {
84
- console.log(` ${i + 1}. ${e.id} (${e.transitionCount} transitions)`);
85
- });
86
- }, 30000); // 30s timeout for indexing
87
-
88
- describe("Index validation", () => {
89
- it("should have indexed functions", () => {
90
- expect(index.entries.size).toBeGreaterThan(0);
91
- console.log(`\n✅ Indexed ${index.entries.size} functions`);
92
- });
93
-
94
- it("should have functions with >20 transitions", () => {
95
- const complex = Array.from(index.entries.values()).filter(
96
- (e) => e.transitionCount >= 20,
97
- );
98
- expect(complex.length).toBeGreaterThan(0);
99
- console.log(`\n✅ ${complex.length} functions pass complexity guardrail`);
100
- });
101
- });
102
-
103
- describe("Find similar functions in our own codebase", () => {
104
- it("should find similar patterns among runners", async () => {
105
- // Find runner files
106
- const runnerEntries = Array.from(index.entries.values()).filter(
107
- (e: IndexEntry) => e.filePath.includes("dispatch/runners/"),
108
- );
109
-
110
- console.log(`\n🔍 Testing ${runnerEntries.length} runner functions`);
111
-
112
- const similarities: {
113
- func1: string;
114
- func2: string;
115
- similarity: number;
116
- }[] = [];
117
-
118
- // Compare each pair
119
- for (let i = 0; i < runnerEntries.length; i++) {
120
- for (let j = i + 1; j < runnerEntries.length; j++) {
121
- const entry1 = runnerEntries[i];
122
- const entry2 = runnerEntries[j];
123
-
124
- // Skip if same file
125
- if (entry1.filePath === entry2.filePath) continue;
126
-
127
- const sim = calcMatrixSimilarity(entry1.matrix, entry2.matrix);
128
-
129
- if (sim >= 0.75) {
130
- similarities.push({
131
- func1: entry1.id,
132
- func2: entry2.id,
133
- similarity: sim,
134
- });
135
- }
136
- }
137
- }
138
-
139
- // Sort by similarity
140
- similarities.sort((a, b) => b.similarity - a.similarity);
141
-
142
- console.log(`\n📊 Found ${similarities.length} similar pairs (>75%):`);
143
- similarities.slice(0, 5).forEach((s, i) => {
144
- console.log(` ${i + 1}. ${s.func1} ↔ ${s.func2}`);
145
- console.log(` Similarity: ${(s.similarity * 100).toFixed(1)}%`);
146
- });
147
-
148
- // Log findings but don't fail - this is exploratory
149
- expect(similarities.length).toBeGreaterThanOrEqual(0);
150
- });
151
-
152
- it("should find similar client patterns", async () => {
153
- const clientEntries = Array.from(index.entries.values()).filter(
154
- (e: IndexEntry) =>
155
- e.filePath.includes("clients/") &&
156
- e.filePath.includes("-client.ts") &&
157
- !e.filePath.includes("test"),
158
- );
159
-
160
- console.log(`\n🔍 Testing ${clientEntries.length} client functions`);
161
-
162
- const similarities: {
163
- func1: string;
164
- func2: string;
165
- similarity: number;
166
- }[] = [];
167
-
168
- for (let i = 0; i < clientEntries.length; i++) {
169
- for (let j = i + 1; j < clientEntries.length; j++) {
170
- const entry1 = clientEntries[i];
171
- const entry2 = clientEntries[j];
172
-
173
- if (entry1.filePath === entry2.filePath) continue;
174
-
175
- const sim = calcMatrixSimilarity(entry1.matrix, entry2.matrix);
176
-
177
- if (sim >= 0.75) {
178
- similarities.push({
179
- func1: entry1.id,
180
- func2: entry2.id,
181
- similarity: sim,
182
- });
183
- }
184
- }
185
- }
186
-
187
- similarities.sort((a, b) => b.similarity - a.similarity);
188
-
189
- console.log(
190
- `\n📊 Found ${similarities.length} similar client patterns (>75%):`,
191
- );
192
- similarities.slice(0, 3).forEach((s, i) => {
193
- console.log(` ${i + 1}. ${s.func1} ↔ ${s.func2}`);
194
- console.log(` Similarity: ${(s.similarity * 100).toFixed(1)}%`);
195
- });
196
-
197
- expect(similarities.length).toBeGreaterThanOrEqual(0);
198
- });
199
- });
200
-
201
- describe("Find potential refactor opportunities", () => {
202
- it("should identify duplicate utility functions", () => {
203
- // Look for functions with very high similarity (>90%)
204
- const entries = Array.from(index.entries.values());
205
- const seenPairs = new Set<string>(); // Deduplicate A→B and B→A
206
- const duplicates: {
207
- func: string;
208
- similarTo: string;
209
- similarity: number;
210
- }[] = [];
211
-
212
- for (const entry of entries) {
213
- const matches = findSimilarFunctions(entry.matrix, index, 0.9, 3);
214
- for (const match of matches) {
215
- if (match.targetId === entry.id) continue;
216
-
217
- // Canonical pair key (sorted to avoid A,B and B,A)
218
- const pairKey = [entry.id, match.targetId].sort().join("::");
219
- if (seenPairs.has(pairKey)) continue;
220
-
221
- seenPairs.add(pairKey);
222
- duplicates.push({
223
- func: entry.id,
224
- similarTo: match.targetId,
225
- similarity: match.similarity,
226
- });
227
- }
228
- }
229
-
230
- console.log(
231
- `\n🎯 Found ${duplicates.length} unique potential duplicates (>90%):`,
232
- );
233
- duplicates.slice(0, 5).forEach((d, i) => {
234
- console.log(` ${i + 1}. ${d.func}`);
235
- console.log(` Similar to: ${d.similarTo}`);
236
- console.log(` Match: ${(d.similarity * 100).toFixed(1)}%`);
237
- });
238
-
239
- // This is informational - we don't assert on it
240
- expect(true).toBe(true);
241
- });
242
- });
243
-
244
- describe("Complexity distribution", () => {
245
- it("should show transition count distribution", () => {
246
- const entries = Array.from(index.entries.values());
247
- const transitionCounts = entries.map((e) => e.transitionCount);
248
-
249
- const avg =
250
- transitionCounts.reduce((a, b) => a + b, 0) / transitionCounts.length;
251
- const min = Math.min(...transitionCounts);
252
- const max = Math.max(...transitionCounts);
253
-
254
- const belowThreshold = transitionCounts.filter((c) => c < 20).length;
255
- const aboveThreshold = transitionCounts.filter((c) => c >= 20).length;
256
-
257
- console.log("\n📊 Complexity Distribution:");
258
- console.log(` Total functions: ${entries.length}`);
259
- console.log(` Below threshold (<20): ${belowThreshold}`);
260
- console.log(` Above threshold (≥20): ${aboveThreshold}`);
261
- console.log(` Min transitions: ${min}`);
262
- console.log(` Max transitions: ${max}`);
263
- console.log(` Average: ${avg.toFixed(1)}`);
264
-
265
- // Most functions should pass the guardrail
266
- expect(aboveThreshold).toBeGreaterThan(0);
267
- });
268
- });
269
- });
@@ -1,177 +0,0 @@
1
- /**
2
- * File Kind Detection for pi-lens
3
- *
4
- * Centralized file type detection to avoid duplication across clients.
5
- * Maps file extensions and paths to semantic file kinds.
6
- */
7
- import { basename, extname } from "node:path";
8
- // --- Extension Maps ---
9
- const KIND_EXTENSIONS = {
10
- jsts: [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".svelte"],
11
- python: [".py"],
12
- go: [".go"],
13
- rust: [".rs"],
14
- cxx: [
15
- ".c",
16
- ".cc",
17
- ".cpp",
18
- ".cxx",
19
- ".h",
20
- ".hh",
21
- ".hpp",
22
- ".hxx",
23
- ".ixx",
24
- ".ipp",
25
- ".inl",
26
- ".tpp",
27
- ".txx",
28
- ],
29
- cmake: [".cmake"],
30
- shell: [".sh", ".bash", ".zsh", ".fish"],
31
- json: [".json", ".jsonc", ".json5"],
32
- markdown: [".md", ".mdx"],
33
- css: [".css", ".scss", ".sass", ".less"],
34
- yaml: [".yaml", ".yml"],
35
- };
36
- // Reverse map: extension → file kind (for fast lookup)
37
- const EXT_TO_KIND = new Map();
38
- for (const [kind, exts] of Object.entries(KIND_EXTENSIONS)) {
39
- for (const ext of exts) {
40
- EXT_TO_KIND.set(ext.toLowerCase(), kind);
41
- }
42
- // Also register without leading dot
43
- for (const ext of exts) {
44
- if (ext.startsWith(".")) {
45
- EXT_TO_KIND.set(ext.slice(1).toLowerCase(), kind);
46
- }
47
- }
48
- }
49
- // Special filenames that indicate a file kind
50
- const SPECIAL_FILENAMES = [
51
- { pattern: /^CMakeLists\.txt$/i, kind: "cmake" },
52
- { pattern: /^Makefile$/i, kind: "shell" },
53
- { pattern: /^ Dockerfile(\.\w+)?$/i, kind: "shell" },
54
- ];
55
- // --- Detection Functions ---
56
- /**
57
- * Detect the file kind from a file path.
58
- * Returns the semantic file kind or undefined if unknown.
59
- */
60
- export function detectFileKind(filePath) {
61
- if (!filePath || typeof filePath !== "string") {
62
- return undefined;
63
- }
64
- // Check special filenames first
65
- const base = basename(filePath);
66
- for (const { pattern, kind } of SPECIAL_FILENAMES) {
67
- if (pattern.test(base)) {
68
- return kind;
69
- }
70
- }
71
- // Check by extension
72
- const ext = extname(filePath).toLowerCase();
73
- return EXT_TO_KIND.get(ext);
74
- }
75
- /**
76
- * Check if a file kind is supported by a specific tool or capability.
77
- *
78
- * @example
79
- * // Check if TypeScript file
80
- * if (isFileKind(filePath, "jsts")) { ... }
81
- *
82
- * // Check for multiple kinds
83
- * if (isFileKind(filePath, ["jsts", "python"])) { ... }
84
- */
85
- export function isFileKind(filePath, kind) {
86
- const detected = detectFileKind(filePath);
87
- if (!detected)
88
- return false;
89
- if (Array.isArray(kind)) {
90
- return kind.includes(detected);
91
- }
92
- return detected === kind;
93
- }
94
- /**
95
- * Get all file kinds that match a given file extension.
96
- * Useful for listing which tools might handle a file.
97
- */
98
- export function getFileKindsForExtension(ext) {
99
- const normalizedExt = ext.startsWith(".") ? ext : `.${ext}`;
100
- const kind = EXT_TO_KIND.get(normalizedExt.toLowerCase());
101
- return kind ? [kind] : [];
102
- }
103
- /**
104
- * Check if a file kind represents a code file (not config/markdown).
105
- */
106
- export function isCodeKind(kind) {
107
- return ["jsts", "python", "go", "rust", "cxx", "shell"].includes(kind);
108
- }
109
- /**
110
- * Check if a file kind represents a text/config file.
111
- */
112
- export function isConfigKind(kind) {
113
- return ["json", "yaml", "markdown", "css"].includes(kind);
114
- }
115
- /**
116
- * Get human-readable description of a file kind.
117
- */
118
- export function getFileKindLabel(kind) {
119
- const labels = {
120
- jsts: "JavaScript/TypeScript",
121
- python: "Python",
122
- go: "Go",
123
- rust: "Rust",
124
- cxx: "C/C++",
125
- cmake: "CMake",
126
- shell: "Shell",
127
- json: "JSON",
128
- markdown: "Markdown",
129
- css: "CSS",
130
- yaml: "YAML",
131
- };
132
- return labels[kind] ?? kind;
133
- }
134
- /**
135
- * Get file extensions for a file kind.
136
- */
137
- export function getExtensionsForKind(kind) {
138
- return [...(KIND_EXTENSIONS[kind] ?? [])];
139
- }
140
- /**
141
- * Check if a file should be scanned for linting/formatting.
142
- * Excludes test files, generated files, etc.
143
- */
144
- export function isScannableFile(filePath) {
145
- const kind = detectFileKind(filePath);
146
- if (!kind)
147
- return false;
148
- // Exclude test files for most kinds
149
- const base = basename(filePath);
150
- if (base.includes(".test.") ||
151
- base.includes(".spec.") ||
152
- base.startsWith("test-") ||
153
- base.startsWith("spec-")) {
154
- return false;
155
- }
156
- // Only scan code and config files
157
- return isCodeKind(kind) || isConfigKind(kind);
158
- }
159
- /**
160
- * Get the language identifier for LSP/tools that use language IDs.
161
- */
162
- export function getLanguageId(kind) {
163
- const languageIds = {
164
- jsts: "typescript",
165
- python: "python",
166
- go: "go",
167
- rust: "rust",
168
- cxx: "cpp",
169
- cmake: "cmake",
170
- shell: "shell",
171
- json: "json",
172
- markdown: "markdown",
173
- css: "css",
174
- yaml: "yaml",
175
- };
176
- return languageIds[kind] ?? "plaintext";
177
- }