pi-lens 3.1.2 → 3.2.0

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 (154) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +16 -12
  3. package/clients/ast-grep-client.js +8 -1
  4. package/clients/ast-grep-client.ts +9 -1
  5. package/clients/biome-client.js +51 -38
  6. package/clients/biome-client.ts +60 -58
  7. package/clients/dependency-checker.js +30 -1
  8. package/clients/dependency-checker.ts +35 -1
  9. package/clients/dispatch/__tests__/runner-registration.test.ts +286 -282
  10. package/clients/dispatch/bus-dispatcher.js +15 -14
  11. package/clients/dispatch/bus-dispatcher.ts +32 -25
  12. package/clients/dispatch/dispatcher.js +18 -25
  13. package/clients/dispatch/dispatcher.test.ts +2 -1
  14. package/clients/dispatch/dispatcher.ts +17 -28
  15. package/clients/dispatch/plan.js +77 -32
  16. package/clients/dispatch/plan.ts +78 -32
  17. package/clients/dispatch/runners/ast-grep-napi.js +36 -376
  18. package/clients/dispatch/runners/ast-grep-napi.ts +60 -433
  19. package/clients/dispatch/runners/index.js +8 -4
  20. package/clients/dispatch/runners/index.ts +8 -4
  21. package/clients/dispatch/runners/lsp.js +65 -0
  22. package/clients/dispatch/runners/lsp.ts +125 -0
  23. package/clients/dispatch/runners/oxlint.js +2 -2
  24. package/clients/dispatch/runners/oxlint.ts +2 -2
  25. package/clients/dispatch/runners/pyright.js +24 -8
  26. package/clients/dispatch/runners/pyright.ts +28 -14
  27. package/clients/dispatch/runners/rust-clippy.js +2 -2
  28. package/clients/dispatch/runners/rust-clippy.ts +2 -4
  29. package/clients/dispatch/runners/tree-sitter.js +14 -2
  30. package/clients/dispatch/runners/tree-sitter.ts +15 -2
  31. package/clients/dispatch/runners/ts-lsp.js +3 -3
  32. package/clients/dispatch/runners/ts-lsp.ts +8 -5
  33. package/clients/dispatch/runners/yaml-rule-parser.js +292 -0
  34. package/clients/dispatch/runners/yaml-rule-parser.ts +338 -0
  35. package/clients/dispatch/types.js +3 -0
  36. package/clients/dispatch/types.ts +3 -0
  37. package/clients/formatters.js +67 -14
  38. package/clients/formatters.ts +68 -15
  39. package/clients/installer/index.js +78 -10
  40. package/clients/installer/index.ts +519 -426
  41. package/clients/jscpd-client.js +28 -0
  42. package/clients/jscpd-client.ts +41 -3
  43. package/clients/knip-client.js +30 -1
  44. package/clients/knip-client.ts +34 -2
  45. package/clients/lsp/__tests__/client.test.ts +64 -41
  46. package/clients/lsp/__tests__/config.test.ts +25 -17
  47. package/clients/lsp/__tests__/launch.test.ts +108 -43
  48. package/clients/lsp/__tests__/service.test.ts +76 -48
  49. package/clients/lsp/client.js +87 -2
  50. package/clients/lsp/client.ts +150 -6
  51. package/clients/lsp/config.js +8 -11
  52. package/clients/lsp/config.ts +24 -21
  53. package/clients/lsp/index.js +69 -0
  54. package/clients/lsp/index.ts +82 -0
  55. package/clients/lsp/interactive-install.js +19 -8
  56. package/clients/lsp/interactive-install.ts +52 -27
  57. package/clients/lsp/launch.js +182 -32
  58. package/clients/lsp/launch.ts +241 -38
  59. package/clients/lsp/path-utils.js +3 -46
  60. package/clients/lsp/path-utils.ts +11 -51
  61. package/clients/lsp/server.js +93 -71
  62. package/clients/lsp/server.ts +173 -131
  63. package/clients/path-utils.js +142 -0
  64. package/clients/path-utils.ts +153 -0
  65. package/clients/ruff-client.js +33 -4
  66. package/clients/ruff-client.ts +44 -13
  67. package/clients/safe-spawn.js +3 -1
  68. package/clients/safe-spawn.ts +3 -1
  69. package/clients/services/effect-integration.js +11 -7
  70. package/clients/services/effect-integration.ts +34 -26
  71. package/clients/sg-runner.js +51 -9
  72. package/clients/sg-runner.ts +58 -15
  73. package/clients/tree-sitter-client.js +12 -0
  74. package/clients/tree-sitter-client.ts +12 -0
  75. package/clients/typescript-client.js +6 -2
  76. package/clients/typescript-client.ts +9 -2
  77. package/commands/booboo.js +2 -4
  78. package/commands/booboo.ts +2 -4
  79. package/index.ts +377 -93
  80. package/package.json +2 -1
  81. package/rules/tree-sitter-queries/tsx/no-nested-links.yml +45 -0
  82. package/rules/tree-sitter-queries/typescript/constructor-super.yml +55 -0
  83. package/rules/tree-sitter-queries/typescript/debugger.yml +1 -1
  84. package/rules/tree-sitter-queries/typescript/no-dupe-class-members.yml +47 -0
  85. package/tsconfig.json +1 -1
  86. package/clients/__tests__/file-time.test.js +0 -216
  87. package/clients/__tests__/format-service.test.js +0 -245
  88. package/clients/__tests__/formatters.test.js +0 -271
  89. package/clients/agent-behavior-client.test.js +0 -94
  90. package/clients/ast-grep-client.test.js +0 -129
  91. package/clients/ast-grep-client.test.ts +0 -155
  92. package/clients/biome-client.test.js +0 -144
  93. package/clients/cache-manager.test.js +0 -197
  94. package/clients/complexity-client.test.js +0 -234
  95. package/clients/dependency-checker.test.js +0 -60
  96. package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
  97. package/clients/dispatch/__tests__/runner-registration.test.js +0 -236
  98. package/clients/dispatch/dispatcher.edge.test.js +0 -82
  99. package/clients/dispatch/dispatcher.format.test.js +0 -46
  100. package/clients/dispatch/dispatcher.inline.test.js +0 -74
  101. package/clients/dispatch/dispatcher.test.js +0 -115
  102. package/clients/dispatch/runners/architect.test.js +0 -138
  103. package/clients/dispatch/runners/ast-grep-napi.test.js +0 -106
  104. package/clients/dispatch/runners/oxlint.test.js +0 -230
  105. package/clients/dispatch/runners/pyright.test.js +0 -98
  106. package/clients/dispatch/runners/python-slop.test.js +0 -203
  107. package/clients/dispatch/runners/scan_codebase.test.js +0 -89
  108. package/clients/dispatch/runners/shellcheck.test.js +0 -98
  109. package/clients/dispatch/runners/spellcheck.test.js +0 -158
  110. package/clients/dispatch/runners/ts-slop.test.js +0 -180
  111. package/clients/dispatch/runners/ts-slop.test.ts +0 -230
  112. package/clients/dogfood.test.js +0 -201
  113. package/clients/file-kinds.test.js +0 -169
  114. package/clients/go-client.test.js +0 -127
  115. package/clients/jscpd-client.test.js +0 -127
  116. package/clients/knip-client.test.js +0 -112
  117. package/clients/lsp/__tests__/client.test.js +0 -325
  118. package/clients/lsp/__tests__/config.test.js +0 -166
  119. package/clients/lsp/__tests__/error-recovery.test.js +0 -213
  120. package/clients/lsp/__tests__/integration.test.js +0 -127
  121. package/clients/lsp/__tests__/launch.test.js +0 -260
  122. package/clients/lsp/__tests__/server.test.js +0 -259
  123. package/clients/lsp/__tests__/service.test.js +0 -417
  124. package/clients/metrics-client.test.js +0 -141
  125. package/clients/ruff-client.test.js +0 -132
  126. package/clients/rust-client.test.js +0 -108
  127. package/clients/sanitize.test.js +0 -177
  128. package/clients/secrets-scanner.test.js +0 -100
  129. package/clients/services/__tests__/effect-integration.test.js +0 -86
  130. package/clients/test-runner-client.test.js +0 -192
  131. package/clients/todo-scanner.test.js +0 -301
  132. package/clients/type-coverage-client.test.js +0 -105
  133. package/clients/typescript-client.codefix.test.js +0 -157
  134. package/clients/typescript-client.test.js +0 -105
  135. package/commands/clients/ast-grep-client.js +0 -250
  136. package/commands/clients/ast-grep-parser.js +0 -86
  137. package/commands/clients/ast-grep-rule-manager.js +0 -91
  138. package/commands/clients/ast-grep-types.js +0 -9
  139. package/commands/clients/biome-client.js +0 -380
  140. package/commands/clients/complexity-client.js +0 -667
  141. package/commands/clients/file-kinds.js +0 -177
  142. package/commands/clients/file-utils.js +0 -40
  143. package/commands/clients/jscpd-client.js +0 -169
  144. package/commands/clients/knip-client.js +0 -211
  145. package/commands/clients/ruff-client.js +0 -297
  146. package/commands/clients/safe-spawn.js +0 -88
  147. package/commands/clients/scan-utils.js +0 -83
  148. package/commands/clients/sg-runner.js +0 -190
  149. package/commands/clients/types.js +0 -11
  150. package/commands/clients/typescript-client.js +0 -505
  151. package/commands/rate.test.js +0 -119
  152. package/rules/ast-grep-rules/rules/no-dangerously-set-inner-html.yml +0 -13
  153. package/rules/ast-grep-rules/rules/no-debugger.yml +0 -12
  154. package/rules/ast-grep-rules/rules/no-eval.yml +0 -13
@@ -15,25 +15,67 @@ import { safeSpawn } from "./safe-spawn.js";
15
15
  */
16
16
  function escapeWindowsArg(arg) {
17
17
  // If no special characters, return as-is
18
- if (!/[\s\"]/.test(arg))
18
+ if (!/[\s"]/.test(arg))
19
19
  return arg;
20
20
  // Escape quotes by doubling them
21
- return `"${arg.replace(/"/g, "\"\"")}"`;
21
+ return `"${arg.replace(/"/g, '""')}"`;
22
22
  }
23
23
  export class SgRunner {
24
24
  constructor(verbose = false) {
25
+ this.sgPath = null;
26
+ this.available = null;
25
27
  this.log = verbose
26
28
  ? (msg) => console.error(`[sg-runner] ${msg}`)
27
29
  : () => { };
28
30
  }
29
31
  /**
30
- * Check if ast-grep CLI is available
32
+ * Check if ast-grep CLI is available, auto-install if not
33
+ */
34
+ async ensureAvailable() {
35
+ // Fast path: already checked
36
+ if (this.available !== null)
37
+ return this.available;
38
+ // Check if available in PATH (fast)
39
+ const pathResult = safeSpawn("sg", ["--version"], {
40
+ timeout: 5000,
41
+ });
42
+ if (!pathResult.error && pathResult.status === 0) {
43
+ this.sgPath = "sg";
44
+ this.available = true;
45
+ this.log("ast-grep found in PATH");
46
+ return true;
47
+ }
48
+ // Auto-install via pi-lens installer
49
+ this.log("ast-grep not found, attempting auto-install...");
50
+ const { ensureTool } = await import("./installer/index.js");
51
+ const installedPath = await ensureTool("ast-grep");
52
+ if (installedPath) {
53
+ this.sgPath = installedPath;
54
+ this.available = true;
55
+ this.log(`ast-grep auto-installed: ${installedPath}`);
56
+ return true;
57
+ }
58
+ this.available = false;
59
+ return false;
60
+ }
61
+ /**
62
+ * Check if ast-grep CLI is available (legacy sync method)
63
+ * Prefer ensureAvailable() for auto-install behavior
31
64
  */
32
65
  isAvailable() {
66
+ if (this.available !== null)
67
+ return this.available;
33
68
  const result = safeSpawn("npx", ["sg", "--version"], {
34
69
  timeout: 10000,
35
70
  });
36
- return !result.error && result.status === 0;
71
+ this.available = !result.error && result.status === 0;
72
+ return this.available;
73
+ }
74
+ /**
75
+ * Get the sg command to use (local binary or "sg" from PATH)
76
+ */
77
+ getSgCommand() {
78
+ return this.sgPath || "sg";
37
79
  }
38
80
  /**
39
81
  * Run ast-grep asynchronously, return parsed matches
@@ -54,12 +96,12 @@ export class SgRunner {
54
96
  return `'${arg.replace(/'/g, "'\\''")}'`;
55
97
  }
56
98
  // For other args with spaces/special chars, use double quotes
57
- if (/[\s\"]/.test(arg)) {
58
- return `"${arg.replace(/"/g, "\\\"")}"`;
99
+ if (/[\s"]/.test(arg)) {
100
+ return `"${arg.replace(/"/g, '\\"')}"`;
59
101
  }
60
102
  return arg;
61
103
  });
62
- const bashCommand = `npx sg ${escapedArgs.join(" ")}`;
104
+ const bashCommand = `${this.getSgCommand()} ${escapedArgs.join(" ")}`;
63
105
  proc = spawn("bash", ["-c", bashCommand], {
64
106
  stdio: ["ignore", "pipe", "pipe"],
65
107
  windowsHide: true,
@@ -67,7 +109,7 @@ export class SgRunner {
67
109
  }
68
110
  else if (isWindows) {
69
111
  // Fallback: use cmd.exe with standard escaping
70
- const fullCommand = `npx sg ${args.map(escapeWindowsArg).join(" ")}`;
112
+ const fullCommand = `${this.getSgCommand()} ${args.map(escapeWindowsArg).join(" ")}`;
71
113
  proc = spawn(fullCommand, {
72
114
  stdio: ["ignore", "pipe", "pipe"],
73
115
  shell: true,
@@ -76,7 +118,7 @@ export class SgRunner {
76
118
  }
77
119
  else {
78
120
  // Unix: normal spawn without shell
79
- proc = spawn("npx", ["sg", ...args], {
121
+ proc = spawn(this.getSgCommand(), args, {
80
122
  stdio: ["ignore", "pipe", "pipe"],
81
123
  });
82
124
  }
@@ -5,7 +5,7 @@
5
5
  * Handles: spawn, spawnSync, temp dir management, JSON parsing.
6
6
  */
7
7
 
8
- import { spawn, spawnSync } from "node:child_process";
8
+ import { spawn } from "node:child_process";
9
9
  import * as fs from "node:fs";
10
10
  import * as os from "node:os";
11
11
  import * as path from "node:path";
@@ -17,10 +17,10 @@ import { safeSpawn } from "./safe-spawn.js";
17
17
  */
18
18
  function escapeWindowsArg(arg: string): string {
19
19
  // If no special characters, return as-is
20
- if (!/[\s\"]/.test(arg)) return arg;
20
+ if (!/[\s"]/.test(arg)) return arg;
21
21
 
22
22
  // Escape quotes by doubling them
23
- return `"${arg.replace(/"/g, "\"\"")}"`;
23
+ return `"${arg.replace(/"/g, '""')}"`;
24
24
  }
25
25
 
26
26
  export interface SgMatch {
@@ -40,6 +40,8 @@ export interface SgResult {
40
40
 
41
41
  export class SgRunner {
42
42
  private log: (msg: string) => void;
43
+ private sgPath: string | null = null;
44
+ private available: boolean | null = null;
43
45
 
44
46
  constructor(verbose = false) {
45
47
  this.log = verbose
@@ -48,13 +50,58 @@ export class SgRunner {
48
50
  }
49
51
 
50
52
  /**
51
- * Check if ast-grep CLI is available
53
+ * Check if ast-grep CLI is available, auto-install if not
54
+ */
55
+ async ensureAvailable(): Promise<boolean> {
56
+ // Fast path: already checked
57
+ if (this.available !== null) return this.available;
58
+
59
+ // Check if available in PATH (fast)
60
+ const pathResult = safeSpawn("sg", ["--version"], {
61
+ timeout: 5000,
62
+ });
63
+ if (!pathResult.error && pathResult.status === 0) {
64
+ this.sgPath = "sg";
65
+ this.available = true;
66
+ this.log("ast-grep found in PATH");
67
+ return true;
68
+ }
69
+
70
+ // Auto-install via pi-lens installer
71
+ this.log("ast-grep not found, attempting auto-install...");
72
+ const { ensureTool } = await import("./installer/index.js");
73
+ const installedPath = await ensureTool("ast-grep");
74
+
75
+ if (installedPath) {
76
+ this.sgPath = installedPath;
77
+ this.available = true;
78
+ this.log(`ast-grep auto-installed: ${installedPath}`);
79
+ return true;
80
+ }
81
+
82
+ this.available = false;
83
+ return false;
84
+ }
85
+
86
+ /**
87
+ * Check if ast-grep CLI is available (legacy sync method)
88
+ * Prefer ensureAvailable() for auto-install behavior
52
89
  */
53
90
  isAvailable(): boolean {
91
+ if (this.available !== null) return this.available;
92
+
54
93
  const result = safeSpawn("npx", ["sg", "--version"], {
55
94
  timeout: 10000,
56
95
  });
57
- return !result.error && result.status === 0;
96
+ this.available = !result.error && result.status === 0;
97
+ return this.available;
98
+ }
99
+
100
+ /**
101
+ * Get the sg command to use (local binary or "sg" from PATH)
102
+ */
103
+ private getSgCommand(): string {
104
+ return this.sgPath || "sg";
58
105
  }
59
106
 
60
107
  /**
@@ -77,19 +124,19 @@ export class SgRunner {
77
124
  return `'${arg.replace(/'/g, "'\\''")}'`;
78
125
  }
79
126
  // For other args with spaces/special chars, use double quotes
80
- if (/[\s\"]/.test(arg)) {
81
- return `"${arg.replace(/"/g, "\\\"")}"`;
127
+ if (/[\s"]/.test(arg)) {
128
+ return `"${arg.replace(/"/g, '\\"')}"`;
82
129
  }
83
130
  return arg;
84
131
  });
85
- const bashCommand = `npx sg ${escapedArgs.join(" ")}`;
132
+ const bashCommand = `${this.getSgCommand()} ${escapedArgs.join(" ")}`;
86
133
  proc = spawn("bash", ["-c", bashCommand], {
87
134
  stdio: ["ignore", "pipe", "pipe"],
88
135
  windowsHide: true,
89
136
  });
90
137
  } else if (isWindows) {
91
138
  // Fallback: use cmd.exe with standard escaping
92
- const fullCommand = `npx sg ${args.map(escapeWindowsArg).join(" ")}`;
139
+ const fullCommand = `${this.getSgCommand()} ${args.map(escapeWindowsArg).join(" ")}`;
93
140
  proc = spawn(fullCommand, {
94
141
  stdio: ["ignore", "pipe", "pipe"],
95
142
  shell: true,
@@ -97,7 +144,7 @@ export class SgRunner {
97
144
  });
98
145
  } else {
99
146
  // Unix: normal spawn without shell
100
- proc = spawn("npx", ["sg", ...args], {
147
+ proc = spawn(this.getSgCommand(), args, {
101
148
  stdio: ["ignore", "pipe", "pipe"],
102
149
  });
103
150
  }
@@ -206,11 +253,7 @@ export class SgRunner {
206
253
  /**
207
254
  * Run a rule file scan (temporary config approach) - alias for tempScan
208
255
  */
209
- scanWithRule(
210
- ruleYaml: string,
211
- dir: string,
212
- timeout = 30000,
213
- ): SgMatch[] {
256
+ scanWithRule(ruleYaml: string, dir: string, timeout = 30000): SgMatch[] {
214
257
  const sessionDir = path.join(os.tmpdir(), `sg-scan-${Date.now()}`);
215
258
  const rulesSubdir = path.join(sessionDir, "rules");
216
259
  const configFile = path.join(sessionDir, ".sgconfig.yml");
@@ -446,6 +446,18 @@ export class TreeSitterClient {
446
446
  continue; // Skip console.dbg()
447
447
  }
448
448
  }
449
+ if (postFilter === "no_super_call") {
450
+ const bodyNode = captures.BODY;
451
+ if (bodyNode) {
452
+ // Check if body contains actual super() call (not in comments)
453
+ const bodyText = bodyNode.text;
454
+ // Match super() or super.method() but not // super() in comments
455
+ const superCallRegex = /(?<!\/\/.*)super\s*\(/;
456
+ const hasSuperCall = superCallRegex.test(bodyText);
457
+ if (hasSuperCall)
458
+ continue; // Skip if has super() - this is the GOOD case
459
+ }
460
+ }
449
461
  // Use first capture for position info
450
462
  if (match.captures.length > 0) {
451
463
  const firstNode = match.captures[0].node;
@@ -650,6 +650,18 @@ export class TreeSitterClient {
650
650
  }
651
651
  }
652
652
 
653
+ if (postFilter === "no_super_call") {
654
+ const bodyNode = captures.BODY;
655
+ if (bodyNode) {
656
+ // Check if body contains actual super() call (not in comments)
657
+ const bodyText = bodyNode.text;
658
+ // Match super() or super.method() but not // super() in comments
659
+ const superCallRegex = /(?<!\/\/.*)super\s*\(/;
660
+ const hasSuperCall = superCallRegex.test(bodyText);
661
+ if (hasSuperCall) continue; // Skip if has super() - this is the GOOD case
662
+ }
663
+ }
664
+
653
665
  // Use first capture for position info
654
666
  if (match.captures.length > 0) {
655
667
  const firstNode = match.captures[0].node;
@@ -462,9 +462,13 @@ export class TypeScriptClient {
462
462
  * Get all quick fixes for all diagnostics in a file.
463
463
  * Returns a map of diagnostic line → fixes.
464
464
  */
465
- getAllCodeFixes(filePath) {
465
+ /**
466
+ * Accept pre-computed diagnostics to avoid a second getSemanticDiagnostics call
467
+ * when the caller already has them (saves ~1–2s on large files).
468
+ */
469
+ getAllCodeFixes(filePath, precomputedDiags) {
466
470
  const fixesByLine = new Map();
467
- const diagnostics = this.getDiagnostics(filePath);
471
+ const diagnostics = precomputedDiags ?? this.getDiagnostics(filePath);
468
472
  for (const diag of diagnostics) {
469
473
  if (diag.severity !== 1 || diag.code === undefined)
470
474
  continue;
@@ -617,7 +617,14 @@ export class TypeScriptClient {
617
617
  * Get all quick fixes for all diagnostics in a file.
618
618
  * Returns a map of diagnostic line → fixes.
619
619
  */
620
- getAllCodeFixes(filePath: string): Map<
620
+ /**
621
+ * Accept pre-computed diagnostics to avoid a second getSemanticDiagnostics call
622
+ * when the caller already has them (saves ~1–2s on large files).
623
+ */
624
+ getAllCodeFixes(
625
+ filePath: string,
626
+ precomputedDiags?: Diagnostic[],
627
+ ): Map<
621
628
  number,
622
629
  Array<{
623
630
  description: string;
@@ -644,7 +651,7 @@ export class TypeScriptClient {
644
651
  }>
645
652
  >();
646
653
 
647
- const diagnostics = this.getDiagnostics(filePath);
654
+ const diagnostics = precomputedDiags ?? this.getDiagnostics(filePath);
648
655
  for (const diag of diagnostics) {
649
656
  if (diag.severity !== 1 || diag.code === undefined) continue;
650
657
  const fixes = this.getCodeFixes(
@@ -44,8 +44,8 @@ export async function handleBooboo(args, ctx, clients, pi) {
44
44
  const targetPath = args.trim() || ctx.cwd || process.cwd();
45
45
  // Detect project metadata for richer reporting
46
46
  const projectMeta = detectProjectMetadata(targetPath);
47
- const metaDisplay = formatProjectMetadata(projectMeta);
48
- ctx.ui.notify(`🔍 Running full codebase review...\n${metaDisplay}`, "info");
47
+ const _metaDisplay = formatProjectMetadata(projectMeta);
48
+ // No noisy notification at start - just run the review silently
49
49
  // Detect project type once for all runners
50
50
  const isTsProject = nodeFs.existsSync(path.join(targetPath, "tsconfig.json"));
51
51
  // Get available commands for the project
@@ -966,9 +966,7 @@ ${fullReport.join("\n")}`;
966
966
  const summaryLines = [
967
967
  `📊 Code Review: ${totalIssues} issues`,
968
968
  ...runnerLines,
969
- ` 🔧 ${fixableCount} fixable | 🏗️ ${refactorNeeded} refactor`,
970
969
  ` ⏱️ Total: ${jsonReport.meta.totalTime}`,
971
- `📄 JSON: ${jsonPath}`,
972
970
  `📄 MD: ${mdPath}`,
973
971
  ];
974
972
  ctx.ui.notify(summaryLines.join("\n"), "info");
@@ -82,9 +82,9 @@ export async function handleBooboo(
82
82
 
83
83
  // Detect project metadata for richer reporting
84
84
  const projectMeta = detectProjectMetadata(targetPath);
85
- const metaDisplay = formatProjectMetadata(projectMeta);
85
+ const _metaDisplay = formatProjectMetadata(projectMeta);
86
86
 
87
- ctx.ui.notify(`🔍 Running full codebase review...\n${metaDisplay}`, "info");
87
+ // No noisy notification at start - just run the review silently
88
88
 
89
89
  // Detect project type once for all runners
90
90
  const isTsProject = nodeFs.existsSync(path.join(targetPath, "tsconfig.json"));
@@ -1229,9 +1229,7 @@ ${fullReport.join("\n")}`;
1229
1229
  const summaryLines = [
1230
1230
  `📊 Code Review: ${totalIssues} issues`,
1231
1231
  ...runnerLines,
1232
- ` 🔧 ${fixableCount} fixable | 🏗️ ${refactorNeeded} refactor`,
1233
1232
  ` ⏱️ Total: ${jsonReport.meta.totalTime}`,
1234
- `📄 JSON: ${jsonPath}`,
1235
1233
  `📄 MD: ${mdPath}`,
1236
1234
  ];
1237
1235