@herbertgao/pi-extensions 2026.9.1 → 2026.9.2
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/node_modules/@narumitw/pi-btw/README.md +57 -141
- package/node_modules/@narumitw/pi-btw/dist/index.ts +466 -41
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +4 -4
- package/node_modules/@narumitw/pi-btw/docs/workflows.md +74 -0
- package/node_modules/@narumitw/pi-btw/package.json +2 -1
- package/node_modules/@narumitw/pi-btw/src/btw.ts +15 -1
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +32 -2
- package/node_modules/@narumitw/pi-btw/src/keybindings.ts +330 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +136 -19
- package/node_modules/@narumitw/pi-btw/src/settings.ts +32 -0
- package/node_modules/@narumitw/pi-btw/src/side-thread.ts +57 -5
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +52 -30
- package/node_modules/pi-lens/CHANGELOG.md +84 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +1 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +2 -0
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +133 -1
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +0 -6
- package/node_modules/pi-lens/dist/clients/bootstrap.js +4 -4
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +1 -1
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +0 -5
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +1 -9
- package/node_modules/pi-lens/dist/clients/cache-observability.js +1 -6
- package/node_modules/pi-lens/dist/clients/cargo-manifest.js +2 -2
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/child-unref.js +1 -19
- package/node_modules/pi-lens/dist/clients/config-core/index.js +11 -8
- package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +1 -1
- package/node_modules/pi-lens/dist/clients/config-core/provenance.js +2 -9
- package/node_modules/pi-lens/dist/clients/config-core/records.js +1 -1
- package/node_modules/pi-lens/dist/clients/config-core/schema.js +3 -5
- package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +1 -1
- package/node_modules/pi-lens/dist/clients/config-resolve.js +4 -4
- package/node_modules/pi-lens/dist/clients/config-schema.js +39 -2
- package/node_modules/pi-lens/dist/clients/dead-code-logger.js +0 -4
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +1 -1
- package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +0 -26
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +18 -1
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -6
- package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -3
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +2 -2
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +1 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +6 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +4 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +1 -64
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -21
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +2 -20
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +1 -0
- package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +2 -2
- package/node_modules/pi-lens/dist/clients/disposition-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +2 -2
- package/node_modules/pi-lens/dist/clients/file-kinds.js +1 -38
- package/node_modules/pi-lens/dist/clients/file-time.js +1 -1
- package/node_modules/pi-lens/dist/clients/file-utils.js +5 -1
- package/node_modules/pi-lens/dist/clients/format-service.js +0 -1
- package/node_modules/pi-lens/dist/clients/formatters.js +110 -3
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +3 -4
- package/node_modules/pi-lens/dist/clients/git-guard.js +5 -0
- package/node_modules/pi-lens/dist/clients/grammar-source.js +3 -3
- package/node_modules/pi-lens/dist/clients/installer/index.js +220 -9
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +84 -18
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +6 -6
- package/node_modules/pi-lens/dist/clients/language-profile.js +0 -9
- package/node_modules/pi-lens/dist/clients/language-registry.js +5 -1
- package/node_modules/pi-lens/dist/clients/latency-logger.js +5 -7
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -1
- package/node_modules/pi-lens/dist/clients/lens-config.js +19 -0
- package/node_modules/pi-lens/dist/clients/lens-engine.js +3 -4
- package/node_modules/pi-lens/dist/clients/lens-events.js +1 -1
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +18 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +1 -1
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +0 -36
- package/node_modules/pi-lens/dist/clients/lsp/client.js +4 -3
- package/node_modules/pi-lens/dist/clients/lsp/config.js +1 -0
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +3 -3
- package/node_modules/pi-lens/dist/clients/lsp/index.js +216 -25
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/language.js +0 -8
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +0 -2
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -1
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +5 -5
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +6 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +1 -1
- package/node_modules/pi-lens/dist/clients/metrics-history.js +28 -107
- package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +1 -1
- package/node_modules/pi-lens/dist/clients/module-report.js +1 -1
- package/node_modules/pi-lens/dist/clients/mutating-tool.js +3 -3
- package/node_modules/pi-lens/dist/clients/mutation-attribution.js +3 -3
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +0 -4
- package/node_modules/pi-lens/dist/clients/observed-mutation.js +9 -9
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +2 -6
- package/node_modules/pi-lens/dist/clients/opengrep-config.js +1 -1
- package/node_modules/pi-lens/dist/clients/path-utils.js +23 -2
- package/node_modules/pi-lens/dist/clients/performance-report.js +2 -2
- package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +1 -1
- package/node_modules/pi-lens/dist/clients/pipeline.js +2 -2
- package/node_modules/pi-lens/dist/clients/project-changes.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +46 -1
- package/node_modules/pi-lens/dist/clients/project-scan-policy.js +3 -51
- package/node_modules/pi-lens/dist/clients/python-provenance.js +1 -1
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
- package/node_modules/pi-lens/dist/clients/read-guard.js +5 -2
- package/node_modules/pi-lens/dist/clients/recent-touches.js +2 -2
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +3 -5
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +1 -1
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +1 -1
- package/node_modules/pi-lens/dist/clients/runtime-session.js +45 -10
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +11 -1
- package/node_modules/pi-lens/dist/clients/sanitize.js +0 -11
- package/node_modules/pi-lens/dist/clients/scan-utils.js +1 -56
- package/node_modules/pi-lens/dist/clients/session-summary.js +0 -24
- package/node_modules/pi-lens/dist/clients/sgconfig.js +1 -5
- package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +1 -1
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
- package/node_modules/pi-lens/dist/clients/startup-scan.js +2 -10
- package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +77 -72
- package/node_modules/pi-lens/dist/clients/tool-policy.js +1 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +1 -52
- package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -4
- package/node_modules/pi-lens/dist/clients/user-notify.js +0 -4
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/word-index-store.js +3 -7
- package/node_modules/pi-lens/dist/clients/word-index.js +9 -16
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +1 -1
- package/node_modules/pi-lens/dist/index.js +1062 -538
- package/node_modules/pi-lens/dist/mcp/build-staleness.js +1 -1
- package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +1 -1
- package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +1 -1
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +1 -1
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +37 -6
- package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +3 -3
- package/node_modules/pi-lens/dist/tools/render-compact.js +1 -1
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -2
- package/node_modules/pi-lens/docs/globalconfig.md +20 -0
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +9 -1
- package/node_modules/pi-lens/docs/pi-lens-reviewer.md +6 -0
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +12 -1
- package/node_modules/pi-lens/docs/release-qa-baseline.md +12 -4
- package/node_modules/pi-lens/docs/servercapabilities.md +7 -3
- package/node_modules/pi-lens/docs/word-index.md +1 -1
- package/node_modules/pi-lens/package.json +6 -15
- package/node_modules/pi-lens/rules/typos/_typos.toml +4 -2
- package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +1 -1
- package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +1 -1
- package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +4 -2
- package/package.json +3 -3
|
@@ -93,7 +93,7 @@ export function loadHistory() {
|
|
|
93
93
|
/**
|
|
94
94
|
* Save history to disk
|
|
95
95
|
*/
|
|
96
|
-
|
|
96
|
+
function saveHistory(history) {
|
|
97
97
|
const historyDir = getProjectDataDir(process.cwd());
|
|
98
98
|
if (!fs.existsSync(historyDir)) {
|
|
99
99
|
fs.mkdirSync(historyDir, { recursive: true });
|
|
@@ -161,10 +161,35 @@ export function captureSnapshot(filePath, metrics) {
|
|
|
161
161
|
}, SAVE_DEBOUNCE_MS);
|
|
162
162
|
saveTimer.unref?.();
|
|
163
163
|
}
|
|
164
|
+
// --- Trend Analysis ---
|
|
164
165
|
/**
|
|
165
|
-
*
|
|
166
|
-
*
|
|
166
|
+
* Compute trend direction from history snapshots
|
|
167
|
+
* Uses last 3 snapshots for stability (or 2 if only 2 available)
|
|
167
168
|
*/
|
|
169
|
+
function computeTrend(history) {
|
|
170
|
+
if (history.length < 2)
|
|
171
|
+
return "stable";
|
|
172
|
+
const recent = history.slice(-3);
|
|
173
|
+
const first = recent[0];
|
|
174
|
+
const last = recent[recent.length - 1];
|
|
175
|
+
// Use MI as primary indicator, cognitive as secondary
|
|
176
|
+
const miDelta = last.mi - first.mi;
|
|
177
|
+
const cogDelta = last.cognitive - first.cognitive;
|
|
178
|
+
// Thresholds (MI changes < 2 are noise)
|
|
179
|
+
if (miDelta > 2)
|
|
180
|
+
return "improving";
|
|
181
|
+
if (miDelta < -2)
|
|
182
|
+
return "regressing";
|
|
183
|
+
// If MI is stable, check cognitive
|
|
184
|
+
if (cogDelta < -10)
|
|
185
|
+
return "improving";
|
|
186
|
+
if (cogDelta > 10)
|
|
187
|
+
return "regressing";
|
|
188
|
+
return "stable";
|
|
189
|
+
}
|
|
190
|
+
// --- Technical Debt Index (TDI) ---
|
|
191
|
+
// Test-reached through a child-process `require` string
|
|
192
|
+
/** @public — reached through a child-process `require` string in tests/clients/metrics-history-stderr.test.ts; knip cannot see it. */
|
|
168
193
|
export function captureSnapshots(files) {
|
|
169
194
|
const history = loadHistory();
|
|
170
195
|
for (const file of files) {
|
|
@@ -200,110 +225,6 @@ export function captureSnapshots(files) {
|
|
|
200
225
|
saveHistory(history);
|
|
201
226
|
return history;
|
|
202
227
|
}
|
|
203
|
-
// --- Trend Analysis ---
|
|
204
|
-
/**
|
|
205
|
-
* Compute trend direction from history snapshots
|
|
206
|
-
* Uses last 3 snapshots for stability (or 2 if only 2 available)
|
|
207
|
-
*/
|
|
208
|
-
export function computeTrend(history) {
|
|
209
|
-
if (history.length < 2)
|
|
210
|
-
return "stable";
|
|
211
|
-
const recent = history.slice(-3);
|
|
212
|
-
const first = recent[0];
|
|
213
|
-
const last = recent[recent.length - 1];
|
|
214
|
-
// Use MI as primary indicator, cognitive as secondary
|
|
215
|
-
const miDelta = last.mi - first.mi;
|
|
216
|
-
const cogDelta = last.cognitive - first.cognitive;
|
|
217
|
-
// Thresholds (MI changes < 2 are noise)
|
|
218
|
-
if (miDelta > 2)
|
|
219
|
-
return "improving";
|
|
220
|
-
if (miDelta < -2)
|
|
221
|
-
return "regressing";
|
|
222
|
-
// If MI is stable, check cognitive
|
|
223
|
-
if (cogDelta < -10)
|
|
224
|
-
return "improving";
|
|
225
|
-
if (cogDelta > 10)
|
|
226
|
-
return "regressing";
|
|
227
|
-
return "stable";
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Get delta between current snapshot and previous
|
|
231
|
-
*/
|
|
232
|
-
export function getDelta(history) {
|
|
233
|
-
if (!history || history.history.length < 2)
|
|
234
|
-
return null;
|
|
235
|
-
const current = history.history[history.history.length - 1];
|
|
236
|
-
const previous = history.history[history.history.length - 2];
|
|
237
|
-
return {
|
|
238
|
-
mi: Math.round((current.mi - previous.mi) * 10) / 10,
|
|
239
|
-
cognitive: current.cognitive - previous.cognitive,
|
|
240
|
-
trend: history.trend,
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Get trend emoji for display
|
|
245
|
-
*/
|
|
246
|
-
export function getTrendEmoji(trend) {
|
|
247
|
-
switch (trend) {
|
|
248
|
-
case "improving":
|
|
249
|
-
return "📈";
|
|
250
|
-
case "regressing":
|
|
251
|
-
return "📉";
|
|
252
|
-
default:
|
|
253
|
-
return "➡️";
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* Get trend summary across all files
|
|
258
|
-
*/
|
|
259
|
-
export function getTrendSummary(history) {
|
|
260
|
-
let improving = 0;
|
|
261
|
-
let regressing = 0;
|
|
262
|
-
let stable = 0;
|
|
263
|
-
const regressions = [];
|
|
264
|
-
for (const [file, fileHistory] of Object.entries(history.files)) {
|
|
265
|
-
switch (fileHistory.trend) {
|
|
266
|
-
case "improving":
|
|
267
|
-
improving++;
|
|
268
|
-
break;
|
|
269
|
-
case "regressing": {
|
|
270
|
-
regressing++;
|
|
271
|
-
const delta = getDelta(fileHistory);
|
|
272
|
-
if (delta) {
|
|
273
|
-
regressions.push({ file, miDelta: delta.mi });
|
|
274
|
-
}
|
|
275
|
-
break;
|
|
276
|
-
}
|
|
277
|
-
default:
|
|
278
|
-
stable++;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
// Sort regressions by MI delta (worst first)
|
|
282
|
-
regressions.sort((a, b) => a.miDelta - b.miDelta);
|
|
283
|
-
return {
|
|
284
|
-
improving,
|
|
285
|
-
regressing,
|
|
286
|
-
stable,
|
|
287
|
-
worstRegressions: regressions.slice(0, 5),
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
/**
|
|
291
|
-
* Format trend for metrics table
|
|
292
|
-
*/
|
|
293
|
-
export function formatTrendCell(filePath, history) {
|
|
294
|
-
const relativePath = path.relative(process.cwd(), filePath);
|
|
295
|
-
const fileHistory = history.files[relativePath];
|
|
296
|
-
if (!fileHistory || fileHistory.history.length < 2) {
|
|
297
|
-
return "—"; // No history
|
|
298
|
-
}
|
|
299
|
-
const delta = getDelta(fileHistory);
|
|
300
|
-
if (!delta)
|
|
301
|
-
return "—";
|
|
302
|
-
const emoji = getTrendEmoji(delta.trend);
|
|
303
|
-
const miSign = delta.mi > 0 ? "+" : "";
|
|
304
|
-
const miColor = delta.mi > 0 ? "🟢" : delta.mi < 0 ? "🔴" : "⚪";
|
|
305
|
-
return `${emoji} ${miColor}${miSign}${delta.mi}`;
|
|
306
|
-
}
|
|
307
228
|
/**
|
|
308
229
|
* Calculate Technical Debt Index for the project.
|
|
309
230
|
* Score: 0 = perfect, 100 = maximum debt.
|
|
@@ -159,7 +159,7 @@ function isAccessorLine(rawLine) {
|
|
|
159
159
|
* extracted data — no re-parsing, no file I/O beyond the lines already read
|
|
160
160
|
* by the caller.
|
|
161
161
|
*/
|
|
162
|
-
|
|
162
|
+
function analyzeMiddleMan(classEntry, lines, languageId) {
|
|
163
163
|
if (!languageId)
|
|
164
164
|
return undefined;
|
|
165
165
|
const selfSpec = SELF_TOKEN[languageId];
|
|
@@ -48,7 +48,7 @@ import { symbolExtractionGrammar, TreeSitterSymbolExtractor, } from "./tree-sitt
|
|
|
48
48
|
// lsp.ts) to be re-homed in #236, where LSP writes provenance-tagged edges INTO
|
|
49
49
|
// the review graph (computed once, persisted) so this path just reads them.
|
|
50
50
|
/** Hard payload bound for the per-symbol who-uses-this section. */
|
|
51
|
-
|
|
51
|
+
const MAX_MODULE_REPORT_REFS = 100;
|
|
52
52
|
function normalizeMaxRefsPerSymbol(value) {
|
|
53
53
|
if (value === undefined || !Number.isFinite(value))
|
|
54
54
|
return 10;
|
|
@@ -113,7 +113,7 @@ function asRecord(value) {
|
|
|
113
113
|
* field list the seam classifies with — a second spelling list would arm on
|
|
114
114
|
* inputs the seam cannot resolve, and miss ones it can.
|
|
115
115
|
*/
|
|
116
|
-
|
|
116
|
+
function resolveMutationPath(input) {
|
|
117
117
|
for (const key of ["path", "filePath", "file_path"]) {
|
|
118
118
|
const value = input[key];
|
|
119
119
|
if (typeof value === "string" && value.length > 0)
|
|
@@ -141,7 +141,7 @@ export function readMutationPathField(event) {
|
|
|
141
141
|
* base62 anchor — handled by `clients/hashline-anchor.ts`. Do not reach for
|
|
142
142
|
* this function there.
|
|
143
143
|
*/
|
|
144
|
-
|
|
144
|
+
function parseHashlineAnchor(anchor) {
|
|
145
145
|
if (typeof anchor !== "string")
|
|
146
146
|
return undefined;
|
|
147
147
|
const trimmed = anchor.trim();
|
|
@@ -153,7 +153,7 @@ export function parseHashlineAnchor(anchor) {
|
|
|
153
153
|
return Number.isInteger(line) && line > 0 ? line : undefined;
|
|
154
154
|
}
|
|
155
155
|
/** Bounding box plus per-range detail for a multi-range edit. */
|
|
156
|
-
|
|
156
|
+
function combineRanges(ranges) {
|
|
157
157
|
const starts = ranges.map(([start]) => start);
|
|
158
158
|
const ends = ranges.map(([, end]) => end);
|
|
159
159
|
return {
|
|
@@ -45,7 +45,7 @@ import { BoundedFifoMap } from "./bounded-cache.js";
|
|
|
45
45
|
import { getProjectDataDir } from "./file-utils.js";
|
|
46
46
|
import { getProcessSingleton } from "./process-singletons.js";
|
|
47
47
|
/** Persisted-file schema version. A file of any other version is ignored. */
|
|
48
|
-
|
|
48
|
+
const MUTATION_ATTRIBUTION_FILE_VERSION = 1;
|
|
49
49
|
/** File under `getProjectDataDir(cwd)` holding the learned attributions. */
|
|
50
50
|
export const MUTATION_ATTRIBUTION_FILE = "observed-mutating-tools.json";
|
|
51
51
|
/**
|
|
@@ -55,7 +55,7 @@ export const MUTATION_ATTRIBUTION_FILE = "observed-mutating-tools.json";
|
|
|
55
55
|
* co-occurrence — an unrelated tool call that happened to overlap a background
|
|
56
56
|
* write — cannot durably mislabel a tool for every future session.
|
|
57
57
|
*/
|
|
58
|
-
|
|
58
|
+
const PERSIST_AFTER_OBSERVATIONS = 2;
|
|
59
59
|
/**
|
|
60
60
|
* Consecutive clean observations after which an UNATTRIBUTED tool stops being
|
|
61
61
|
* armed for the session. Two, not one: a mutating tool whose FIRST call
|
|
@@ -88,7 +88,7 @@ export const CLEAN_OBSERVATION_ARM_LIMIT = 2;
|
|
|
88
88
|
*/
|
|
89
89
|
export const DEATTRIBUTE_AFTER_CLEAN_OBSERVATIONS = 3;
|
|
90
90
|
/** Bound on distinct tool names remembered, in-memory and on disk. */
|
|
91
|
-
|
|
91
|
+
const MUTATION_ATTRIBUTION_MAX_TOOLS = 64;
|
|
92
92
|
const ATTRIBUTION_FAMILY = "mutation-attribution";
|
|
93
93
|
/**
|
|
94
94
|
* Bumped to 2 (#2449 review round 5, F3): `session` changed shape from a
|
|
@@ -127,10 +127,6 @@ export function _exitFlushersForTest() {
|
|
|
127
127
|
export function getRegisteredLogFiles() {
|
|
128
128
|
return registeredLogFiles;
|
|
129
129
|
}
|
|
130
|
-
/** Test-only reset — each test file gets a clean registry (see ndjson-logger.test.ts). */
|
|
131
|
-
export function _resetRegisteredLogFilesForTest() {
|
|
132
|
-
registeredLogFiles.clear();
|
|
133
|
-
}
|
|
134
130
|
/**
|
|
135
131
|
* Snapshot of unrecovered write losses, one entry per sink that has any
|
|
136
132
|
* (#1970). Pure in-memory read — no I/O, so a caller (`degradation-ledger.ts`)
|
|
@@ -119,7 +119,7 @@ import { lineContentHash } from "./read-guard.js";
|
|
|
119
119
|
* must not turn every first observation into a timeout, which is the least
|
|
120
120
|
* useful possible outcome.
|
|
121
121
|
*/
|
|
122
|
-
|
|
122
|
+
const OBSERVED_CAPTURE_BUDGET_MS = 200;
|
|
123
123
|
/**
|
|
124
124
|
* Cumulative ceiling for every ARMED capture in one turn. A turn that calls
|
|
125
125
|
* twenty unclassified tools pays this once, not twenty times. The SETTLE does
|
|
@@ -141,7 +141,7 @@ export const OBSERVED_TURN_BUDGET_MS = 600;
|
|
|
141
141
|
* holds regardless of the clock. What it CAN cut is a directory target's tail,
|
|
142
142
|
* and that is reported as `stoppedEarly` rather than scored as clean.
|
|
143
143
|
*/
|
|
144
|
-
|
|
144
|
+
const OBSERVED_SETTLE_DEADLINE_MS = 50;
|
|
145
145
|
/**
|
|
146
146
|
* Entries taken from a DIRECTORY-shaped target path, non-recursively.
|
|
147
147
|
*
|
|
@@ -175,7 +175,7 @@ export const OBSERVED_TRACKED_MAX_FILES = 400;
|
|
|
175
175
|
* closes it as soon as anything moves the size or the tick, and the armed
|
|
176
176
|
* observation (which hashes the target unconditionally) never depended on it.
|
|
177
177
|
*/
|
|
178
|
-
|
|
178
|
+
const OBSERVED_LEDGER_SETTLE_MS = 150;
|
|
179
179
|
/**
|
|
180
180
|
* Path keys the "pi-lens already recorded this" set may hold.
|
|
181
181
|
*
|
|
@@ -198,19 +198,19 @@ export const OBSERVED_HANDLED_MAX = 1000;
|
|
|
198
198
|
*/
|
|
199
199
|
export const OBSERVED_SWEEP_STAT_WINDOW = 128;
|
|
200
200
|
/** Cumulative content-hash budget for ONE capture. */
|
|
201
|
-
|
|
201
|
+
const OBSERVED_HASH_BUDGET_BYTES = 2 * 1024 * 1024;
|
|
202
202
|
/** Cumulative bytes ONE settled sweep may read to seed or verify a hash. */
|
|
203
203
|
export const OBSERVED_SWEEP_HASH_BUDGET_BYTES = 2 * 1024 * 1024;
|
|
204
204
|
/** Cumulative bytes ONE settled sweep may read to derive edit RANGES. */
|
|
205
|
-
|
|
205
|
+
const OBSERVED_SWEEP_RANGE_BUDGET_BYTES = 1024 * 1024;
|
|
206
206
|
/** Largest file whose per-line hashes are captured for range derivation. */
|
|
207
|
-
|
|
207
|
+
const OBSERVED_LINE_HASH_MAX_BYTES = 512 * 1024;
|
|
208
208
|
/** Ranges reported per file before they collapse to one bounding box. */
|
|
209
|
-
|
|
209
|
+
const OBSERVED_MAX_EDIT_RANGES = 32;
|
|
210
210
|
/** Pending baselines held between `tool_call` and `tool_result`. */
|
|
211
|
-
|
|
211
|
+
const OBSERVED_PENDING_MAX = 32;
|
|
212
212
|
/** Files remembered by the settled-sweep content ledger. */
|
|
213
|
-
|
|
213
|
+
const OBSERVED_LEDGER_MAX = 1000;
|
|
214
214
|
const OBSERVED_FAMILY = "observed-mutation-net";
|
|
215
215
|
const OBSERVED_VERSION = 3;
|
|
216
216
|
function state() {
|
|
@@ -43,11 +43,11 @@ import { freshnessFromMtime } from "./freshness.js";
|
|
|
43
43
|
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
44
44
|
import { truncatedByOutputCap } from "./spawn-output-cap.js";
|
|
45
45
|
/** Stop hashing once this many cumulative bytes were read (per capture). */
|
|
46
|
-
|
|
46
|
+
const OPAQUE_HASH_BUDGET_BYTES = 8 * 1024 * 1024;
|
|
47
47
|
/** Hard cap on scanned files — beyond it the verdict is coverage-unknown. */
|
|
48
48
|
export const OPAQUE_SCAN_MAX_FILES = 2000;
|
|
49
49
|
/** How far before recorded start an earlier write may still be attributed. */
|
|
50
|
-
|
|
50
|
+
const OPAQUE_MTIME_TOLERANCE_MS = 150;
|
|
51
51
|
// `--untracked-files=all` lists untracked files individually instead of
|
|
52
52
|
// collapsing them per directory (it does NOT add ignored paths — that needs
|
|
53
53
|
// `--ignored`), so the worst realistic case is a working tree with a large
|
|
@@ -220,10 +220,6 @@ export async function isGitWorktree(root) {
|
|
|
220
220
|
gitRepoMemo.set(key, isRepo === true);
|
|
221
221
|
return isRepo === true;
|
|
222
222
|
}
|
|
223
|
-
export function _resetGitWorktreeMemoForTests() {
|
|
224
|
-
gitRepoMemo.clear();
|
|
225
|
-
gitToplevelMemo.clear();
|
|
226
|
-
}
|
|
227
223
|
const gitToplevelMemo = new Map();
|
|
228
224
|
/**
|
|
229
225
|
* The root of the working tree `root` belongs to, or `undefined` when it is
|
|
@@ -3,7 +3,7 @@ import { findLocalToolConfig } from "./path-utils.js";
|
|
|
3
3
|
// Opengrep is a fork of Semgrep and natively consumes the same rule format, so
|
|
4
4
|
// we discover both `.opengrep.*` (preferred) and the de-facto `.semgrep.*` rule
|
|
5
5
|
// files an existing repo may already carry.
|
|
6
|
-
|
|
6
|
+
const LOCAL_OPENGREP_CONFIG_NAMES = [
|
|
7
7
|
".opengrep.yml",
|
|
8
8
|
".opengrep.yaml",
|
|
9
9
|
"opengrep.yml",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - On non-Windows: return path as-is (case-sensitive filesystem)
|
|
11
11
|
* - Always convert backslashes to forward slashes for Map key consistency
|
|
12
12
|
*/
|
|
13
|
-
import { existsSync, realpathSync } from "node:fs";
|
|
13
|
+
import { existsSync, readFileSync, realpathSync, statSync, } from "node:fs";
|
|
14
14
|
import * as os from "node:os";
|
|
15
15
|
import * as path from "node:path";
|
|
16
16
|
import { win32 } from "node:path";
|
|
@@ -413,6 +413,25 @@ export function findLocalToolConfig(startDir, names, options = {}) {
|
|
|
413
413
|
}
|
|
414
414
|
return undefined;
|
|
415
415
|
}
|
|
416
|
+
/**
|
|
417
|
+
* Accept only a real Git repository marker: a directory with HEAD, or a
|
|
418
|
+
* worktree/submodule marker file whose first line starts with `gitdir:`.
|
|
419
|
+
*/
|
|
420
|
+
export function isRealGitMarker(markerPath) {
|
|
421
|
+
try {
|
|
422
|
+
const marker = statSync(markerPath);
|
|
423
|
+
if (marker.isDirectory())
|
|
424
|
+
return existsSync(path.join(markerPath, "HEAD"));
|
|
425
|
+
if (!marker.isFile())
|
|
426
|
+
return false;
|
|
427
|
+
return readFileSync(markerPath, "utf8")
|
|
428
|
+
.split(/\r?\n/, 1)[0]
|
|
429
|
+
.startsWith("gitdir:");
|
|
430
|
+
}
|
|
431
|
+
catch {
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
416
435
|
/**
|
|
417
436
|
* Walk up from `startDir` looking for a directory containing any of
|
|
418
437
|
* `markers`, the same containment-aware climb `knip-client.ts` and
|
|
@@ -438,11 +457,13 @@ export function findLocalToolConfig(startDir, names, options = {}) {
|
|
|
438
457
|
export function findNearestMarkerRoot(startDir, markers, options = {}) {
|
|
439
458
|
const boundaries = options.boundaries ?? [];
|
|
440
459
|
const homeDir = path.resolve(options.homeDir ?? os.homedir());
|
|
460
|
+
const markerPredicate = options.markerPredicate ?? (() => true);
|
|
441
461
|
let current = path.resolve(startDir);
|
|
442
462
|
for (let depth = 0; depth < 64; depth++) {
|
|
443
463
|
if (isAtOrAboveHomeDir(current, homeDir))
|
|
444
464
|
return null;
|
|
445
|
-
if (markers.some((m) => existsSync(path.join(current, m))
|
|
465
|
+
if (markers.some((m) => existsSync(path.join(current, m)) &&
|
|
466
|
+
markerPredicate(path.join(current, m))))
|
|
446
467
|
return current;
|
|
447
468
|
if (boundaries.some((m) => existsSync(path.join(current, m))))
|
|
448
469
|
return null;
|
|
@@ -2,8 +2,8 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as readline from "node:readline";
|
|
3
3
|
import { flushLatencyLog, getLatencyLogPath, } from "./latency-logger.js";
|
|
4
4
|
import { getMaxLogSizeMB } from "./log-cleanup.js";
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const DEFAULT_PERF_TOP_N = 5;
|
|
6
|
+
const MAX_PERF_TOP_N = 50;
|
|
7
7
|
export const MAX_PERF_PHASE_SAMPLES = 20_000;
|
|
8
8
|
const PARSE_YIELD_EVERY = 500;
|
|
9
9
|
// The window follows the same threshold that rotates the log out from under it.
|
|
@@ -98,7 +98,7 @@ import { findLocalToolConfig } from "./path-utils.js";
|
|
|
98
98
|
* before `.php-cs-fixer.dist.php` — so a same-directory precedence check can
|
|
99
99
|
* just take the first existing name.
|
|
100
100
|
*/
|
|
101
|
-
|
|
101
|
+
const PHP_CS_FIXER_CONFIG_NAMES = [
|
|
102
102
|
".php-cs-fixer.php",
|
|
103
103
|
".php-cs-fixer.dist.php",
|
|
104
104
|
];
|
|
@@ -179,7 +179,7 @@ function createPhaseTracker(toolName, filePath) {
|
|
|
179
179
|
};
|
|
180
180
|
}
|
|
181
181
|
// --- ESLint autofix helpers ---
|
|
182
|
-
export { hasEslintConfig,
|
|
182
|
+
export { hasEslintConfig, hasSqlfluffConfig, hasStylelintConfig };
|
|
183
183
|
/**
|
|
184
184
|
* eslint autofix availability, per cwd + PATH. The verdict is owned by the
|
|
185
185
|
* shared availability latch (#1494): a `missing` / `non-installable` outcome
|
|
@@ -338,7 +338,7 @@ async function tryDetektFix(filePath, cwd) {
|
|
|
338
338
|
const absPath = path.resolve(cwd, filePath);
|
|
339
339
|
return detectFileChangedAfterCommand(filePath, cmd, ["--auto-correct", "--input", absPath, "--config", configPath], cwd, [1, 2]);
|
|
340
340
|
}
|
|
341
|
-
|
|
341
|
+
async function tryMarkdownlintFix(filePath, cwd) {
|
|
342
342
|
const cmd = await resolveToolCommandWithInstallFallback(cwd, "markdownlint");
|
|
343
343
|
if (!cmd)
|
|
344
344
|
return 0;
|
|
@@ -54,7 +54,7 @@ export function readProjectChanges(cwd) {
|
|
|
54
54
|
* version — a missing/corrupt log is the normal cold-start case, not a
|
|
55
55
|
* caller-visible error.
|
|
56
56
|
*/
|
|
57
|
-
|
|
57
|
+
async function readProjectChangesAsync(cwd) {
|
|
58
58
|
const logPath = getProjectChangeLogPath(cwd);
|
|
59
59
|
try {
|
|
60
60
|
const content = await fs.promises.readFile(logPath, "utf-8");
|
|
@@ -15,7 +15,7 @@ const WARM_TRIGGER = {
|
|
|
15
15
|
"dead-code": "runs at session-start (Python projects only)",
|
|
16
16
|
"test-runner": "fires per-edit at turn_end (only after a source file with a discoverable test companion is edited)",
|
|
17
17
|
};
|
|
18
|
-
|
|
18
|
+
function warmTriggerFor(analyzerId) {
|
|
19
19
|
return WARM_TRIGGER[analyzerId] ?? "runs at session-start";
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
@@ -8,7 +8,7 @@ function resolveClonePath(cwd, file) {
|
|
|
8
8
|
* BOTH ends so the duplication surfaces on whichever file the agent is looking
|
|
9
9
|
* at, each one naming the other end.
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
function jscpdCloneToProjectDiagnostics(cwd, clone) {
|
|
12
12
|
const a = resolveClonePath(cwd, clone.fileA);
|
|
13
13
|
const b = resolveClonePath(cwd, clone.fileB);
|
|
14
14
|
const make = (filePath, line, otherFile, otherLine) => ({
|
|
@@ -27,7 +27,7 @@ function knipIssueMessage(issue) {
|
|
|
27
27
|
}
|
|
28
28
|
return `${issue.type}: ${issue.name}`;
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
function knipIssueToProjectDiagnostic(cwd, issue) {
|
|
31
31
|
const blocking = issue.type === "unlisted" || issue.type === "bin";
|
|
32
32
|
return {
|
|
33
33
|
filePath: knipIssueFile(cwd, issue),
|
|
@@ -12,7 +12,7 @@ function cycleMembers(dep) {
|
|
|
12
12
|
* single-point knip finding. Emit a diagnostic on EACH file in the cycle so it
|
|
13
13
|
* surfaces wherever the agent is looking, each one rendering the whole cycle.
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
function circularDepToProjectDiagnostics(cwd, dep) {
|
|
16
16
|
const members = cycleMembers(dep);
|
|
17
17
|
const rendered = `${members.map((f) => path.basename(f)).join(" → ")} → ${path.basename(members[0] ?? "")}`;
|
|
18
18
|
return members.map((file) => ({
|
|
@@ -15,7 +15,7 @@ function severityFor(raw) {
|
|
|
15
15
|
return { severity: "warning", blocking: false };
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
function opengrepFindingToProjectDiagnostic(cwd, finding) {
|
|
19
19
|
const { severity, blocking } = severityFor(finding.severity);
|
|
20
20
|
const cweSuffix = finding.cwe?.length ? ` (${finding.cwe[0]})` : "";
|
|
21
21
|
return {
|
package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js
CHANGED
|
@@ -43,7 +43,7 @@ function parseLocation(location) {
|
|
|
43
43
|
* caller deserves the same "this may already be superseded" honesty the
|
|
44
44
|
* per-edit path already gives.
|
|
45
45
|
*/
|
|
46
|
-
|
|
46
|
+
function testResultToProjectDiagnostics(result, stale = false) {
|
|
47
47
|
if (result.failed === 0 && !result.error)
|
|
48
48
|
return [];
|
|
49
49
|
const stalePrefix = stale ? "[stale — from a prior turn] " : "";
|
|
@@ -162,8 +162,15 @@ export function loadPiLensProjectConfig(startDir, preloadedInfo) {
|
|
|
162
162
|
// a pi-lens-owned file were then produced by nobody at all. The pi-lens
|
|
163
163
|
// loader now enumerates those documents itself, for records only: nothing
|
|
164
164
|
// here is merged, and which file supplies a VALUE is unchanged.
|
|
165
|
-
reportPiLensConfigRecords(entry.legacyRecords);
|
|
166
165
|
const configInfo = preloadedInfo ?? freshInfoFor(entry);
|
|
166
|
+
// The selected legacy document is resolved below and its bounded migration
|
|
167
|
+
// records are cached with that projection. Reporting the discovery copy as
|
|
168
|
+
// well would produce two suppression records with different totals, because
|
|
169
|
+
// the discovery walk and the single-document resolution see different
|
|
170
|
+
// record populations. Keep discovery-only documents here; the selected file
|
|
171
|
+
// is reported by `loadCachedConfigFile`.
|
|
172
|
+
const selectedLegacyPath = configInfo?.path;
|
|
173
|
+
reportPiLensConfigRecords(entry.legacyRecords.filter((record) => record.file !== selectedLegacyPath));
|
|
167
174
|
if (!configInfo)
|
|
168
175
|
return EMPTY_PROJECT_CONFIG;
|
|
169
176
|
return loadCachedConfigFile(configInfo);
|
|
@@ -634,6 +641,43 @@ function parseConfigFile(configPath) {
|
|
|
634
641
|
}
|
|
635
642
|
}
|
|
636
643
|
}
|
|
644
|
+
let startup;
|
|
645
|
+
if (obj.startup !== undefined) {
|
|
646
|
+
if (!obj.startup ||
|
|
647
|
+
typeof obj.startup !== "object" ||
|
|
648
|
+
Array.isArray(obj.startup)) {
|
|
649
|
+
note("startup must be an object");
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
const section = obj.startup;
|
|
653
|
+
if (section.mode === "quick" ||
|
|
654
|
+
section.mode === "full" ||
|
|
655
|
+
section.mode === "minimal") {
|
|
656
|
+
startup = { mode: section.mode };
|
|
657
|
+
}
|
|
658
|
+
else if ("mode" in section) {
|
|
659
|
+
note('startup.mode must be "quick", "full", or "minimal"');
|
|
660
|
+
}
|
|
661
|
+
if (section.scans !== undefined) {
|
|
662
|
+
if (!section.scans ||
|
|
663
|
+
typeof section.scans !== "object" ||
|
|
664
|
+
Array.isArray(section.scans)) {
|
|
665
|
+
note("startup.scans must be an object");
|
|
666
|
+
}
|
|
667
|
+
else if (typeof section.scans.enabled ===
|
|
668
|
+
"boolean") {
|
|
669
|
+
startup ??= {};
|
|
670
|
+
startup.scans = {
|
|
671
|
+
enabled: section.scans
|
|
672
|
+
.enabled,
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
else if ("enabled" in section.scans) {
|
|
676
|
+
note("startup.scans.enabled must be a boolean");
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
637
681
|
// #533 hygiene: mirror the global loader's unknown-key warn so a typo in a
|
|
638
682
|
// shared `.pi-lens.json` (e.g. `maxProjectFile`, `lps`) produces a signal
|
|
639
683
|
// instead of silently doing nothing. The recognized set is single-sourced
|
|
@@ -694,6 +738,7 @@ function parseConfigFile(configPath) {
|
|
|
694
738
|
}
|
|
695
739
|
return {
|
|
696
740
|
config: {
|
|
741
|
+
...(startup === undefined ? {} : { startup }),
|
|
697
742
|
ignore,
|
|
698
743
|
rules,
|
|
699
744
|
...mutations,
|
|
@@ -1,55 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { getProjectIgnoreMatcher, isExcludedDirName, } from "./file-utils.js";
|
|
4
|
-
import { isGeneratedArtifactDirectoryName, isGeneratedOrArtifact, } from "./generated-artifacts.js";
|
|
5
|
-
import { collectSourceFiles, collectSourceFilesAsync, collectSourceFilesWithBudgetAsync, } from "./source-filter.js";
|
|
6
|
-
export function shouldSkipProjectPath(filePath, options) {
|
|
7
|
-
const isDirectory = options.isDirectory === true;
|
|
8
|
-
const matcher = options.ignoreMatcher ?? getProjectIgnoreMatcher(options.rootDir);
|
|
9
|
-
if (matcher.isIgnored(filePath, isDirectory))
|
|
10
|
-
return true;
|
|
11
|
-
const name = path.basename(filePath);
|
|
12
|
-
if (isDirectory) {
|
|
13
|
-
if (isExcludedDirName(name))
|
|
14
|
-
return true;
|
|
15
|
-
return (options.includeGenerated !== true &&
|
|
16
|
-
isGeneratedArtifactDirectoryName(name));
|
|
17
|
-
}
|
|
18
|
-
return (options.includeGenerated !== true &&
|
|
19
|
-
isGeneratedOrArtifact(filePath, {
|
|
20
|
-
readContentHeader: options.inspectGeneratedHeaders !== false,
|
|
21
|
-
includeDeclarations: options.includeDeclarationFiles !== true,
|
|
22
|
-
}));
|
|
23
|
-
}
|
|
24
|
-
export function shouldScanProjectSourceFile(filePath, options) {
|
|
25
|
-
try {
|
|
26
|
-
if (!fs.statSync(filePath).isFile())
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
return !shouldSkipProjectPath(filePath, {
|
|
33
|
-
...options,
|
|
34
|
-
isDirectory: false,
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
export function collectProjectSourceFiles(rootDir, options) {
|
|
38
|
-
return collectSourceFiles(rootDir, options);
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Async, chunked-yield twin of {@link collectProjectSourceFiles}. Produces the
|
|
42
|
-
* exact same file list (it shares `classifyEntry` with the sync collector) but
|
|
43
|
-
* yields to the event loop every N entries so a large-tree walk on a hot hook
|
|
44
|
-
* tick (e.g. the per-edit cascade graph rebuild) never holds the loop in one
|
|
45
|
-
* synchronous burst. Prefer this on the per-edit / per-cascade path.
|
|
46
|
-
*/
|
|
47
|
-
export function collectProjectSourceFilesAsync(rootDir, options) {
|
|
48
|
-
return collectSourceFilesAsync(rootDir, options);
|
|
49
|
-
}
|
|
1
|
+
import { collectSourceFilesWithBudgetAsync, } from "./source-filter.js";
|
|
50
2
|
/**
|
|
51
|
-
* Budget-aware
|
|
52
|
-
*
|
|
3
|
+
* Budget-aware source walk (#760): the same walk as
|
|
4
|
+
* `collectProjectSourceFiles`, but returns `{ files, entryBudgetExceeded }` so a caller on a hot
|
|
53
5
|
* path (e.g. the per-edit cascade graph rebuild) can observe that the walk
|
|
54
6
|
* stopped at its `maxScanEntries` entry budget and got a truncated
|
|
55
7
|
* best-effort list rather than a complete enumeration.
|
|
@@ -18,7 +18,7 @@ const VERBOSE_READ_GUARD_LOG = process.env.PI_LENS_READ_GUARD_VERBOSE === "1" ||
|
|
|
18
18
|
const LOG_ALLOWED_EDITS = process.env.PI_LENS_READ_GUARD_LOG_ALLOWS === "1";
|
|
19
19
|
const SNAPSHOT_LOG_SETTING = (process.env.PI_LENS_READ_GUARD_LOG_SNAPSHOTS ?? "1").toLowerCase();
|
|
20
20
|
const LOG_SNAPSHOT_VALIDATION = !["0", "false", "off"].includes(SNAPSHOT_LOG_SETTING);
|
|
21
|
-
|
|
21
|
+
const MAX_EDIT_BATCH_ITEMS = 100;
|
|
22
22
|
let generatedCorrelationCounter = 0;
|
|
23
23
|
export function sanitizeCorrelationId(value) {
|
|
24
24
|
if (typeof value !== "string" && typeof value !== "number")
|
|
@@ -1396,8 +1396,11 @@ export class ReadGuard {
|
|
|
1396
1396
|
return filePath.endsWith(suffix);
|
|
1397
1397
|
}
|
|
1398
1398
|
if (pattern.includes("*")) {
|
|
1399
|
-
//
|
|
1400
|
-
|
|
1399
|
+
// Adjacent stars are equivalent to one star in this dialect. Collapse
|
|
1400
|
+
// them before compiling so a non-match cannot backtrack over every
|
|
1401
|
+
// nullable `.*` group (#2622).
|
|
1402
|
+
const collapsedPattern = pattern.replace(/\*+/g, "*");
|
|
1403
|
+
const regex = new RegExp(`^${collapsedPattern.replace(/\\/g, "\\\\").replace(/\./g, "\\.").replace(/\*/g, ".*")}$`);
|
|
1401
1404
|
return regex.test(filePath);
|
|
1402
1405
|
}
|
|
1403
1406
|
return filePath === pattern;
|