pi-lens 3.8.19 → 3.8.21

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.
@@ -82,7 +82,9 @@ export async function handleTurnEnd(deps: TurnEndDeps): Promise<void> {
82
82
  blockerParts.push(runtime.consumeLastCascadeOutput());
83
83
  }
84
84
 
85
- if (await jscpdClient.ensureAvailable()) {
85
+ if (runtime.isStartupScanInFlight("jscpd")) {
86
+ dbg("turn_end: skipping jscpd (startup scan still in flight)");
87
+ } else if (await jscpdClient.ensureAvailable()) {
86
88
  const jscpdFiles = cacheManager.getFilesForJscpd(cwd);
87
89
  if (jscpdFiles.length > 0) {
88
90
  dbg(`turn_end: jscpd scanning ${jscpdFiles.length} file(s)`);
@@ -135,7 +137,7 @@ export async function handleTurnEnd(deps: TurnEndDeps): Promise<void> {
135
137
  report += ` ${displayA}:${clone.startA} ↔ ${displayB}:${clone.startB} (${clone.lines} lines)\n`;
136
138
  }
137
139
  if (firstPath) {
138
- report += ` Inspect first location with read ${firstPath}\n`;
140
+ report += ` First location: ${firstPath}\n`;
139
141
  }
140
142
  blockerParts.push(report);
141
143
  }
@@ -143,7 +145,9 @@ export async function handleTurnEnd(deps: TurnEndDeps): Promise<void> {
143
145
  }
144
146
  }
145
147
 
146
- if (await knipClient.ensureAvailable()) {
148
+ if (runtime.isStartupScanInFlight("knip")) {
149
+ dbg("turn_end: skipping knip (startup scan still in flight)");
150
+ } else if (await knipClient.ensureAvailable()) {
147
151
  const knipResult = knipClient.analyze(cwd, getKnipIgnorePatterns());
148
152
  const prevKnip = cacheManager.readCache<ReturnType<KnipClient["analyze"]>>(
149
153
  "knip",
@@ -178,7 +182,7 @@ export async function handleTurnEnd(deps: TurnEndDeps): Promise<void> {
178
182
  report += ` ${display}${issue.line ? `:${issue.line}` : ""} — ${issue.type}: ${issue.name}\n`;
179
183
  }
180
184
  if (firstPath) {
181
- report += ` Inspect first location with read ${firstPath}\n`;
185
+ report += ` First location: ${firstPath}\n`;
182
186
  }
183
187
  blockerParts.push(report);
184
188
  }
@@ -231,7 +235,20 @@ export async function handleTurnEnd(deps: TurnEndDeps): Promise<void> {
231
235
  `turn_end: ${blockerParts.length} blocker section(s) found, persisting for next context`,
232
236
  );
233
237
  const content = capTurnEndMessage(blockerParts.join("\n\n"));
238
+ const signature = `${files.slice().sort().join("|")}::${content}`;
239
+ const last = cacheManager.readCache<{ signature: string }>(
240
+ "turn-end-findings-last",
241
+ cwd,
242
+ );
243
+ if (last?.data?.signature === signature) {
244
+ dbg("turn_end: duplicate blocker findings detected, suppressing re-prompt");
245
+ cacheManager.clearTurnState(cwd);
246
+ runtime.fixedThisTurn.clear();
247
+ resetFormatService();
248
+ return;
249
+ }
234
250
  cacheManager.writeCache("turn-end-findings", { content }, cwd);
251
+ cacheManager.writeCache("turn-end-findings-last", { signature }, cwd);
235
252
  } else {
236
253
  cacheManager.clearTurnState(cwd);
237
254
  }
@@ -91,7 +91,12 @@ export class TodoScanner {
91
91
  const absolutePath = path.resolve(filePath);
92
92
  if (!fs.existsSync(absolutePath)) return [];
93
93
 
94
- const content = fs.readFileSync(absolutePath, "utf-8");
94
+ let content: string;
95
+ try {
96
+ content = fs.readFileSync(absolutePath, "utf-8");
97
+ } catch {
98
+ return [];
99
+ }
95
100
  const lines = content.split("\n");
96
101
  const items: TodoItem[] = [];
97
102
 
package/index.ts CHANGED
@@ -29,7 +29,10 @@ import { captureSnapshot } from "./clients/metrics-history.js";
29
29
  import { findSimilarFunctions } from "./clients/project-index.js";
30
30
  import { RuffClient } from "./clients/ruff-client.js";
31
31
  import { RuntimeCoordinator } from "./clients/runtime-coordinator.js";
32
- import { consumeTurnEndFindings } from "./clients/runtime-context.js";
32
+ import {
33
+ consumeSessionStartGuidance,
34
+ consumeTurnEndFindings,
35
+ } from "./clients/runtime-context.js";
33
36
  import { handleSessionStart } from "./clients/runtime-session.js";
34
37
  import { handleToolResult } from "./clients/runtime-tool-result.js";
35
38
  import { handleTurnEnd } from "./clients/runtime-turn.js";
@@ -51,10 +54,12 @@ const _getExtensionDir = () => {
51
54
  return ".";
52
55
  };
53
56
 
54
- const DEBUG_LOG = path.join(os.homedir(), "pi-lens-debug.log");
57
+ const DEBUG_LOG_DIR = path.join(os.homedir(), ".pi-lens");
58
+ const DEBUG_LOG = path.join(DEBUG_LOG_DIR, "sessionstart.log");
55
59
  function dbg(msg: string) {
56
60
  const line = `[${new Date().toISOString()}] ${msg}\n`;
57
61
  try {
62
+ nodeFs.mkdirSync(DEBUG_LOG_DIR, { recursive: true });
58
63
  nodeFs.appendFileSync(DEBUG_LOG, line);
59
64
  } catch (e) {
60
65
  // Pipeline error logged
@@ -733,7 +738,14 @@ pi.on("turn_end", async (_event, ctx) => {
733
738
  (pi as any).on("context", async (_event: unknown, ctx: { cwd?: string }) => {
734
739
  try {
735
740
  const cwd = ctx.cwd ?? process.cwd();
736
- return consumeTurnEndFindings(cacheManager, cwd);
741
+ const turnEndFindings = consumeTurnEndFindings(cacheManager, cwd);
742
+ const sessionGuidance = consumeSessionStartGuidance(cacheManager, cwd);
743
+ const messages = [
744
+ ...(sessionGuidance?.messages ?? []),
745
+ ...(turnEndFindings?.messages ?? []),
746
+ ];
747
+ if (messages.length === 0) return;
748
+ return { messages };
737
749
  } catch (err) {
738
750
  dbg(`context event error: ${err}`);
739
751
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-lens",
3
- "version": "3.8.19",
3
+ "version": "3.8.21",
4
4
  "type": "module",
5
5
  "description": "Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo",
6
6
  "repository": {