@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
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
*/
|
|
46
46
|
import * as fs from "node:fs/promises";
|
|
47
47
|
import * as path from "node:path";
|
|
48
|
+
import { HOOK_WALL_BUDGET_MS } from "../hook-budgets.js";
|
|
49
|
+
import { bounded } from "../deadline-utils.js";
|
|
48
50
|
import { recordDegradationOnce } from "../degradation-ledger.js";
|
|
49
51
|
import { commitDurableStoreAsync } from "../durable-store.js";
|
|
50
52
|
import { pmBinary, resolveNodePackageManager, updateArgs, } from "../package-manager.js";
|
|
@@ -263,9 +265,28 @@ async function installedRefreshCandidates() {
|
|
|
263
265
|
verificationTimeoutMs: tool.verificationTimeoutMs ?? 10_000,
|
|
264
266
|
...(tool.packageName !== undefined && { packageName: tool.packageName }),
|
|
265
267
|
...(tool.binaryName !== undefined && { binaryName: tool.binaryName }),
|
|
268
|
+
...(tool.packageEntryOf !== undefined && {
|
|
269
|
+
packageEntryOf: tool.packageEntryOf,
|
|
270
|
+
}),
|
|
266
271
|
});
|
|
267
272
|
}
|
|
268
|
-
|
|
273
|
+
const byPackage = new Map();
|
|
274
|
+
const distinct = [];
|
|
275
|
+
for (const candidate of candidates) {
|
|
276
|
+
if (candidate.strategy !== "npm" || !candidate.packageName) {
|
|
277
|
+
distinct.push(candidate);
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
const existing = byPackage.get(candidate.packageName);
|
|
281
|
+
if (existing) {
|
|
282
|
+
existing.coveredToolIds?.push(candidate.toolId);
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
const representative = { ...candidate, coveredToolIds: [candidate.toolId] };
|
|
286
|
+
byPackage.set(candidate.packageName, representative);
|
|
287
|
+
distinct.push(representative);
|
|
288
|
+
}
|
|
289
|
+
return distinct;
|
|
269
290
|
}
|
|
270
291
|
/**
|
|
271
292
|
* Which candidates are due, oldest stamp first. A tool with no stamp sorts
|
|
@@ -276,15 +297,17 @@ export function selectStaleTools(candidates, state, now) {
|
|
|
276
297
|
const interval = refreshIntervalMs();
|
|
277
298
|
const retry = retryIntervalMs();
|
|
278
299
|
const due = candidates.filter((candidate) => {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
300
|
+
return (candidate.coveredToolIds ?? [candidate.toolId]).some((toolId) => {
|
|
301
|
+
const entry = state.tools[toolId];
|
|
302
|
+
if (!entry)
|
|
303
|
+
return true;
|
|
304
|
+
const cooldown = entry.failed ? retry : interval;
|
|
305
|
+
return now - entry.checkedAt >= cooldown;
|
|
306
|
+
});
|
|
284
307
|
});
|
|
285
308
|
return due.sort((a, b) => {
|
|
286
|
-
const aAt =
|
|
287
|
-
const bAt =
|
|
309
|
+
const aAt = Math.min(...(a.coveredToolIds ?? [a.toolId]).map((toolId) => state.tools[toolId]?.checkedAt ?? 0));
|
|
310
|
+
const bAt = Math.min(...(b.coveredToolIds ?? [b.toolId]).map((toolId) => state.tools[toolId]?.checkedAt ?? 0));
|
|
288
311
|
return aAt !== bAt ? aAt - bAt : a.toolId.localeCompare(b.toolId);
|
|
289
312
|
});
|
|
290
313
|
}
|
|
@@ -346,6 +369,7 @@ async function refreshNonNpmOne(candidate, entry, now) {
|
|
|
346
369
|
changed: false,
|
|
347
370
|
verified: false,
|
|
348
371
|
ok: false,
|
|
372
|
+
attempted: false,
|
|
349
373
|
};
|
|
350
374
|
}
|
|
351
375
|
// A real failure may have left a stale cached path or probe entry behind
|
|
@@ -383,6 +407,7 @@ async function refreshNonNpmOne(candidate, entry, now) {
|
|
|
383
407
|
// touched the installed copy, so there is nothing new to check.
|
|
384
408
|
verified: false,
|
|
385
409
|
ok: false,
|
|
410
|
+
attempted: true,
|
|
386
411
|
};
|
|
387
412
|
}
|
|
388
413
|
const currentVersion = attempt.version;
|
|
@@ -412,6 +437,7 @@ async function refreshNonNpmOne(candidate, entry, now) {
|
|
|
412
437
|
// probe for pip/gem) — `ok: true` here already means "and it runs".
|
|
413
438
|
verified: true,
|
|
414
439
|
ok: true,
|
|
440
|
+
attempted: true,
|
|
415
441
|
};
|
|
416
442
|
}
|
|
417
443
|
async function refreshNpmOne(candidate, now) {
|
|
@@ -435,6 +461,7 @@ async function refreshNpmOne(candidate, now) {
|
|
|
435
461
|
changed: false,
|
|
436
462
|
verified: false,
|
|
437
463
|
ok: false,
|
|
464
|
+
attempted: false,
|
|
438
465
|
};
|
|
439
466
|
}
|
|
440
467
|
try {
|
|
@@ -468,6 +495,7 @@ async function performNpmRefresh(candidate, packageName, now) {
|
|
|
468
495
|
const elapsedMs = Date.now() - startedAt;
|
|
469
496
|
if (spawned.status !== 0 || spawned.error) {
|
|
470
497
|
const reason = (spawned.error?.message ?? spawned.stderr ?? "").slice(0, 200);
|
|
498
|
+
const coveredIds = candidate.coveredToolIds?.join(",") ?? candidate.toolId;
|
|
471
499
|
// "Keeping <old version>" would be an unchecked assertion (#1746 review
|
|
472
500
|
// F2). The spawn budget kills the package manager where it stands, which
|
|
473
501
|
// can be mid-write: the tree may hold a new version, a half-written one,
|
|
@@ -479,9 +507,9 @@ async function performNpmRefresh(candidate, packageName, now) {
|
|
|
479
507
|
recordDegradationOnce({
|
|
480
508
|
kind: "managed-tool-refresh",
|
|
481
509
|
subject: candidate.toolId,
|
|
482
|
-
reason:
|
|
510
|
+
reason: `package ${packageName}, covered ids ${coveredIds}; ${pm} update failed: ${reason || "non-zero exit"}`,
|
|
483
511
|
});
|
|
484
|
-
logSessionStart(`managed-tool-refresh ${candidate.toolId}: ${pm} update failed after ${elapsedMs}ms — on disk now ${onDisk ?? "unreadable"} (was ${previousVersion ?? "unknown"}); resolution cache cleared for re-probe (${reason || "non-zero exit"})`);
|
|
512
|
+
logSessionStart(`managed-tool-refresh ${candidate.toolId}: ${pm} update failed after ${elapsedMs}ms — package ${packageName}, covered ids ${coveredIds}; on disk now ${onDisk ?? "unreadable"} (was ${previousVersion ?? "unknown"}); resolution cache cleared for re-probe (${reason || "non-zero exit"})`);
|
|
485
513
|
await writeRefreshStamp(candidate.toolId, {
|
|
486
514
|
checkedAt: now,
|
|
487
515
|
failed: true,
|
|
@@ -496,6 +524,7 @@ async function performNpmRefresh(candidate, packageName, now) {
|
|
|
496
524
|
changed: onDisk !== undefined && onDisk !== previousVersion,
|
|
497
525
|
verified: false,
|
|
498
526
|
ok: false,
|
|
527
|
+
attempted: true,
|
|
499
528
|
};
|
|
500
529
|
}
|
|
501
530
|
const currentVersion = await readInstalledVersion(packageName);
|
|
@@ -515,12 +544,17 @@ async function performNpmRefresh(candidate, packageName, now) {
|
|
|
515
544
|
// drops npm entries that do not.
|
|
516
545
|
const binaryName = candidate.binaryName;
|
|
517
546
|
const binPath = resolveManagedNpmBinPath(binaryName);
|
|
518
|
-
const verified = await verifyToolBinary(binPath, undefined, undefined, candidate.verificationTimeoutMs, candidate.checkArgs
|
|
547
|
+
const verified = await verifyToolBinary(binPath, undefined, undefined, candidate.verificationTimeoutMs, candidate.checkArgs,
|
|
548
|
+
// #2722: the same verification the install used. Without this the daily
|
|
549
|
+
// refresh would demand a `--version` verdict from a server that can never
|
|
550
|
+
// give one — a ~4 MB doomed spawn that stamps the refresh failed forever.
|
|
551
|
+
candidate.packageEntryOf);
|
|
519
552
|
if (!verified) {
|
|
553
|
+
const coveredIds = candidate.coveredToolIds?.join(",") ?? candidate.toolId;
|
|
520
554
|
recordDegradationOnce({
|
|
521
555
|
kind: "managed-tool-refresh",
|
|
522
556
|
subject: candidate.toolId,
|
|
523
|
-
reason: `binary failed verification after ${pm} update (${previousVersion ?? "unknown"} → ${currentVersion ?? "unknown"})`,
|
|
557
|
+
reason: `package ${packageName}, covered ids ${coveredIds}; binary failed verification after ${pm} update (${previousVersion ?? "unknown"} → ${currentVersion ?? "unknown"})`,
|
|
524
558
|
});
|
|
525
559
|
// Deliberately NOT removed. `installNpmTool` deletes a freshly installed
|
|
526
560
|
// package that fails verification because nothing was working before it;
|
|
@@ -528,7 +562,7 @@ async function performNpmRefresh(candidate, packageName, now) {
|
|
|
528
562
|
// offline for certain rather than probably. The cache invalidation above is
|
|
529
563
|
// what lets `ensureTool`'s own probe-and-repair path see the breakage and
|
|
530
564
|
// reinstall. Stamped as failed so the shorter retry cooldown applies.
|
|
531
|
-
logSessionStart(`managed-tool-refresh ${candidate.toolId}: ${pm} update ran but ${binPath} failed verification — resolution cache cleared for re-probe (${elapsedMs}ms)`);
|
|
565
|
+
logSessionStart(`managed-tool-refresh ${candidate.toolId}: ${pm} update ran but ${binPath} failed verification — package ${packageName}, covered ids ${coveredIds}; resolution cache cleared for re-probe (${elapsedMs}ms)`);
|
|
532
566
|
await writeRefreshStamp(candidate.toolId, {
|
|
533
567
|
checkedAt: now,
|
|
534
568
|
failed: true,
|
|
@@ -543,6 +577,7 @@ async function performNpmRefresh(candidate, packageName, now) {
|
|
|
543
577
|
changed,
|
|
544
578
|
verified: false,
|
|
545
579
|
ok: false,
|
|
580
|
+
attempted: true,
|
|
546
581
|
};
|
|
547
582
|
}
|
|
548
583
|
await writeRefreshStamp(candidate.toolId, {
|
|
@@ -552,8 +587,8 @@ async function performNpmRefresh(candidate, packageName, now) {
|
|
|
552
587
|
// The observability row #1730 asks for: a wrong verdict from a managed tool
|
|
553
588
|
// can be traced to the version that produced it, and to the moment it moved.
|
|
554
589
|
logSessionStart(changed
|
|
555
|
-
? `managed-tool-refresh ${candidate.toolId}: ${previousVersion ?? "unknown"} → ${currentVersion} via ${pm} update, verified (${elapsedMs}ms)`
|
|
556
|
-
: `managed-tool-refresh ${candidate.toolId}: unchanged at ${currentVersion ?? previousVersion ?? "unknown"} (${elapsedMs}ms)`);
|
|
590
|
+
? `managed-tool-refresh ${candidate.toolId}: ${previousVersion ?? "unknown"} → ${currentVersion} via ${pm} update, verified; package ${packageName}, covered ids ${candidate.coveredToolIds?.join(",") ?? candidate.toolId} (${elapsedMs}ms)`
|
|
591
|
+
: `managed-tool-refresh ${candidate.toolId}: unchanged at ${currentVersion ?? previousVersion ?? "unknown"}; package ${packageName}, covered ids ${candidate.coveredToolIds?.join(",") ?? candidate.toolId} (${elapsedMs}ms)`);
|
|
557
592
|
return {
|
|
558
593
|
toolId: candidate.toolId,
|
|
559
594
|
strategy: "npm",
|
|
@@ -563,6 +598,7 @@ async function performNpmRefresh(candidate, packageName, now) {
|
|
|
563
598
|
changed,
|
|
564
599
|
verified: true,
|
|
565
600
|
ok: true,
|
|
601
|
+
attempted: true,
|
|
566
602
|
};
|
|
567
603
|
}
|
|
568
604
|
/**
|
|
@@ -682,13 +718,22 @@ async function executeManagedToolRefresh(now, tap = noopTap) {
|
|
|
682
718
|
// `/new` calls walked the entire 22-tool stale list. A local allowance
|
|
683
719
|
// cannot be re-armed by anyone: a mid-run reset restores the SESSION's
|
|
684
720
|
// right to start a new run, which is correct, without extending this one.
|
|
685
|
-
const
|
|
721
|
+
const stalePackages = new Set();
|
|
722
|
+
const distinctStale = stale.filter((candidate) => {
|
|
723
|
+
if (candidate.strategy !== "npm" || !candidate.packageName)
|
|
724
|
+
return true;
|
|
725
|
+
if (stalePackages.has(candidate.packageName))
|
|
726
|
+
return false;
|
|
727
|
+
stalePackages.add(candidate.packageName);
|
|
728
|
+
return true;
|
|
729
|
+
});
|
|
730
|
+
const want = Math.min(maxPerSession(), distinctStale.length);
|
|
686
731
|
while (held < want && reserveManagedToolRefreshSlot(maxPerSession())) {
|
|
687
732
|
held += 1;
|
|
688
733
|
}
|
|
689
734
|
let allowance = held;
|
|
690
735
|
const refreshed = [];
|
|
691
|
-
for (const candidate of
|
|
736
|
+
for (const candidate of distinctStale) {
|
|
692
737
|
// Once a spawn happens the slot is spent for good — a failing tool must
|
|
693
738
|
// not hand its slot to the next candidate and turn a budget of one into
|
|
694
739
|
// 22 spawns.
|
|
@@ -697,7 +742,28 @@ async function executeManagedToolRefresh(now, tap = noopTap) {
|
|
|
697
742
|
allowance -= 1;
|
|
698
743
|
held -= 1;
|
|
699
744
|
try {
|
|
700
|
-
|
|
745
|
+
const result = await refreshOne(candidate, read.state.tools[candidate.toolId], now);
|
|
746
|
+
refreshed.push(result);
|
|
747
|
+
if (result.attempted && candidate.coveredToolIds) {
|
|
748
|
+
for (const toolId of candidate.coveredToolIds) {
|
|
749
|
+
if (toolId === candidate.toolId)
|
|
750
|
+
continue;
|
|
751
|
+
await bounded(writeRefreshStamp(toolId, {
|
|
752
|
+
checkedAt: now,
|
|
753
|
+
...(result.currentVersion !== undefined && {
|
|
754
|
+
version: result.currentVersion,
|
|
755
|
+
}),
|
|
756
|
+
...(result.ok ? {} : { failed: true }),
|
|
757
|
+
}), {
|
|
758
|
+
ms: HOOK_WALL_BUDGET_MS.session_start,
|
|
759
|
+
// The unref'd timer runs after session_start returns, so no
|
|
760
|
+
// live turn signal belongs to this background refresh.
|
|
761
|
+
signal: undefined,
|
|
762
|
+
hook: "session_start",
|
|
763
|
+
label: "writeRefreshStamp:covered-tool",
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
}
|
|
701
767
|
await tap();
|
|
702
768
|
}
|
|
703
769
|
catch (err) {
|
|
@@ -370,8 +370,8 @@ export function buildIdentityMatcher(cmdlines) {
|
|
|
370
370
|
/** Post-kill liveness poll budget. `taskkill /F /T` returns before the kernel
|
|
371
371
|
* has finished tearing the tree down, so a single immediate check would
|
|
372
372
|
* report false `alive`s. */
|
|
373
|
-
|
|
374
|
-
|
|
373
|
+
const KILL_VERIFY_ATTEMPTS = 5;
|
|
374
|
+
const KILL_VERIFY_INTERVAL_MS = 100;
|
|
375
375
|
/** `setTimeout` as an awaitable, with the timer unref'd so a settled one-shot
|
|
376
376
|
* `pi --print` never waits on best-effort kill verification. */
|
|
377
377
|
function sleepUnref(ms) {
|
|
@@ -459,7 +459,7 @@ async function verifyPidGone(pid, options = {}) {
|
|
|
459
459
|
* (script path), not the process image name, exactly like the existing
|
|
460
460
|
* marker-search's WQL LIKE query below.
|
|
461
461
|
*/
|
|
462
|
-
|
|
462
|
+
const MANAGED_BINARIES = [
|
|
463
463
|
{ name: "ast-grep", launcher: "native" },
|
|
464
464
|
{ name: "opengrep-core", launcher: "native" },
|
|
465
465
|
{ name: "opengrep", launcher: "native" },
|
|
@@ -844,11 +844,11 @@ function classifyKillOutcome(killed, unverified) {
|
|
|
844
844
|
* not time-critical, so paying its cost once per half hour instead of once
|
|
845
845
|
* per session_start removes essentially all of its aggregate cost.
|
|
846
846
|
*/
|
|
847
|
-
|
|
847
|
+
const BACKSTOP_COOLDOWN_MS = 30 * 60 * 1000;
|
|
848
848
|
/** How long after session_start the deferred sweep fires. Chosen to clear the
|
|
849
849
|
* warmup window it was measured starving: `warmup_total` median 3288ms,
|
|
850
850
|
* worst on record 5480ms (#1857). */
|
|
851
|
-
|
|
851
|
+
const BACKSTOP_START_DELAY_MS = 30_000;
|
|
852
852
|
/** Cap on process identities embedded in one latency record. */
|
|
853
853
|
const BACKSTOP_IDENTITY_LOG_LIMIT = 10;
|
|
854
854
|
/**
|
|
@@ -856,7 +856,7 @@ const BACKSTOP_IDENTITY_LOG_LIMIT = 10;
|
|
|
856
856
|
* a candidate that was 1ms too fresh is comfortably past the grace by the
|
|
857
857
|
* time the retry looks at it again.
|
|
858
858
|
*/
|
|
859
|
-
|
|
859
|
+
const BACKSTOP_GRACE_RETRY_MARGIN_MS = 30_000;
|
|
860
860
|
/** How long the sweep lock may be held before another process reclaims it.
|
|
861
861
|
* Well above the scan timeout plus the kill budget, and irrelevant when the
|
|
862
862
|
* holder dies — the lock reclaims immediately on a dead owner pid
|
|
@@ -160,15 +160,6 @@ function computeProjectLanguageProfile(projectRoot, sourceFiles) {
|
|
|
160
160
|
detectedKinds,
|
|
161
161
|
};
|
|
162
162
|
}
|
|
163
|
-
export function hasLanguage(profile, kind) {
|
|
164
|
-
return !!profile.present[kind];
|
|
165
|
-
}
|
|
166
|
-
export function hasAnyLanguage(profile, kinds) {
|
|
167
|
-
return kinds.some((kind) => hasLanguage(profile, kind));
|
|
168
|
-
}
|
|
169
|
-
export function isLanguageConfigured(profile, kind) {
|
|
170
|
-
return !!profile.configured[kind];
|
|
171
|
-
}
|
|
172
163
|
export function getDefaultStartupTools(profile) {
|
|
173
164
|
const tools = new Set();
|
|
174
165
|
for (const kind of Object.keys(LANGUAGE_POLICY)) {
|
|
@@ -121,7 +121,11 @@ export const PINNED_LANGUAGE_IDS = [
|
|
|
121
121
|
* literal message below and `true` stops being assignable to it, so
|
|
122
122
|
* `npm run build` fails at THIS line with the reason spelled out.
|
|
123
123
|
*/
|
|
124
|
-
|
|
124
|
+
const LANGUAGE_ID_PIN_IS_EXHAUSTIVE = true;
|
|
125
|
+
// Not exported: its only job is to fail `npm run build`. The `void` reference
|
|
126
|
+
// is what keeps oxlint's no-unused-vars (CI-gating via lint:js) and knip
|
|
127
|
+
// quiet without giving it a consumer; tsc's noUnusedLocals does not flag it.
|
|
128
|
+
void LANGUAGE_ID_PIN_IS_EXHAUSTIVE;
|
|
125
129
|
/** The registry. Sorted by id; one entry per language, one owner per extension. */
|
|
126
130
|
export const LANGUAGES = [
|
|
127
131
|
{ id: "ada", extensions: [".adb", ".ads"] },
|
|
@@ -155,8 +155,6 @@ export function _recentPhasesStorageLengthForTest() {
|
|
|
155
155
|
export function _setRecentPhasesForTest(entries) {
|
|
156
156
|
recentPhases = entries;
|
|
157
157
|
}
|
|
158
|
-
/** Bound on the closed-bracket ring below — same size discipline as `recentPhases`. */
|
|
159
|
-
export const CLOSED_BRACKET_CAP = RECENT_PHASE_CAP;
|
|
160
158
|
/**
|
|
161
159
|
* Phases currently executing, keyed by their own start token (#1723 review
|
|
162
160
|
* round: this replaces an earlier single-slot design that broke two ways.
|
|
@@ -180,7 +178,7 @@ export const CLOSED_BRACKET_CAP = RECENT_PHASE_CAP;
|
|
|
180
178
|
*/
|
|
181
179
|
const liveBrackets = new Map();
|
|
182
180
|
/**
|
|
183
|
-
* Recently CLOSED brackets, newest first, bounded to `
|
|
181
|
+
* Recently CLOSED brackets, newest first, bounded to `RECENT_PHASE_CAP`
|
|
184
182
|
* (#1723 review round, F3 — the decisive finding). `phaseFinished` runs
|
|
185
183
|
* inside a `finally`, which resumes as a MICROTASK, while the host schedules
|
|
186
184
|
* `turn_end` as a MACROTASK — and microtasks always fully drain before the
|
|
@@ -210,7 +208,7 @@ export function phaseStarted(phase) {
|
|
|
210
208
|
}
|
|
211
209
|
/**
|
|
212
210
|
* Close a bracket: removes it from `liveBrackets` (one `Map.delete`, O(1))
|
|
213
|
-
* and records it on the closed-bracket ring, bounded to `
|
|
211
|
+
* and records it on the closed-bracket ring, bounded to `RECENT_PHASE_CAP`
|
|
214
212
|
* with the oldest entry dropped first — see the `closedBrackets` doc comment
|
|
215
213
|
* above for why a closed history is load-bearing, not just nice-to-have
|
|
216
214
|
* (#1723 review F3). `Map.delete` reports whether it actually removed
|
|
@@ -246,7 +244,7 @@ export function phaseFinished(token) {
|
|
|
246
244
|
closedAt: new Date().toISOString(),
|
|
247
245
|
},
|
|
248
246
|
...closedBrackets,
|
|
249
|
-
].slice(0,
|
|
247
|
+
].slice(0, RECENT_PHASE_CAP);
|
|
250
248
|
}
|
|
251
249
|
/**
|
|
252
250
|
* The OLDEST phase still executing, if any — the longest-running open
|
|
@@ -267,7 +265,7 @@ export function getCurrentPhase() {
|
|
|
267
265
|
* A candidate bracket is ignored outright if its OWN lifetime (`elapsedMs`)
|
|
268
266
|
* is under this fraction of the window's length (#1723 review round 3, N4).
|
|
269
267
|
* Overlap alone is not enough: the bounded closed-bracket ring can churn a
|
|
270
|
-
* real culprit out (busy siblings filling `
|
|
268
|
+
* real culprit out (busy siblings filling `RECENT_PHASE_CAP`), leaving only
|
|
271
269
|
* a 1ms bracket that happens to have SOME positive overlap with an 18-second
|
|
272
270
|
* window — reporting it would be a CONFIDENT WRONG ANSWER, worse than no
|
|
273
271
|
* answer. 5%: a genuine cause's own duration should be a meaningful fraction
|
|
@@ -489,7 +487,7 @@ export function getPhaseForWindow(windowStartMs, windowEndMs) {
|
|
|
489
487
|
/**
|
|
490
488
|
* Test-only: the closed-bracket ring's actual storage length, mirroring
|
|
491
489
|
* `_recentPhasesStorageLengthForTest` above — pins that `phaseFinished`'s
|
|
492
|
-
* `.slice(0,
|
|
490
|
+
* `.slice(0, RECENT_PHASE_CAP)` guard is intact independent of any
|
|
493
491
|
* read-side behavior (#1723 review: "ring unbounded" mutation).
|
|
494
492
|
*/
|
|
495
493
|
export function _closedBracketsStorageLengthForTest() {
|
|
@@ -23,7 +23,27 @@ export const LEDGER_FIELD_MAX = 200;
|
|
|
23
23
|
*/
|
|
24
24
|
export const DEGRADATION_ENTRIES_PER_KIND = 20;
|
|
25
25
|
export function normalizeForLedger(value) {
|
|
26
|
-
|
|
26
|
+
if (value === null || value === undefined)
|
|
27
|
+
return "unknown";
|
|
28
|
+
// A plain object or array has no `toString` of its own, so `String()`
|
|
29
|
+
// writes "[object Object]" — a ledger row that names nothing. Serialise
|
|
30
|
+
// those; everything else (Error, Date, URL, primitives, class instances)
|
|
31
|
+
// keeps its `String()` form. An object carrying its OWN `toString` goes
|
|
32
|
+
// through `String()` too, so a throwing one still raises into the
|
|
33
|
+
// ledger's corrupted-input failsafe (#2703 r1 F1) instead of being
|
|
34
|
+
// admitted as "{}". Circular input degrades to a fixed marker.
|
|
35
|
+
if (typeof value === "object" && !Object.hasOwn(value, "toString")) {
|
|
36
|
+
const proto = Object.getPrototypeOf(value);
|
|
37
|
+
if (proto === Object.prototype || proto === null || Array.isArray(value)) {
|
|
38
|
+
try {
|
|
39
|
+
return JSON.stringify(value) ?? "unknown";
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return "[unserializable object]";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return String(value);
|
|
27
47
|
}
|
|
28
48
|
/** Bound a value to `LEDGER_FIELD_MAX`, marking the elision. */
|
|
29
49
|
export function truncateForLedger(value) {
|
|
@@ -133,6 +133,25 @@ export function loadPiLensGlobalConfig(configPath = getPiLensGlobalConfigPath())
|
|
|
133
133
|
: undefined;
|
|
134
134
|
if (ignore && ignore.length > 0)
|
|
135
135
|
config.ignore = ignore;
|
|
136
|
+
const startup = asConfigObject(raw.startup);
|
|
137
|
+
if (startup) {
|
|
138
|
+
const startupConfig = {};
|
|
139
|
+
const mode = startup.mode;
|
|
140
|
+
const scans = asConfigObject(startup.scans);
|
|
141
|
+
if (mode === "quick" || mode === "full" || mode === "minimal")
|
|
142
|
+
startupConfig.mode = mode;
|
|
143
|
+
else if ("mode" in startup)
|
|
144
|
+
warnInvalid('startup.mode must be "quick", "full", or "minimal"');
|
|
145
|
+
if (scans) {
|
|
146
|
+
if (typeof scans.enabled === "boolean") {
|
|
147
|
+
startupConfig.scans = { enabled: scans.enabled };
|
|
148
|
+
}
|
|
149
|
+
else if ("enabled" in scans)
|
|
150
|
+
warnInvalid("startup.scans.enabled must be a boolean");
|
|
151
|
+
}
|
|
152
|
+
if (startupConfig.mode !== undefined || startupConfig.scans !== undefined)
|
|
153
|
+
config.startup = startupConfig;
|
|
154
|
+
}
|
|
136
155
|
const dispatch = asConfigObject(raw.dispatch);
|
|
137
156
|
if (dispatch) {
|
|
138
157
|
const floor = dispatch.runnerTimeoutFloorMs;
|
|
@@ -28,14 +28,13 @@ import { loadProjectSnapshotWithoutWordIndex } from "./project-snapshot.js";
|
|
|
28
28
|
import { getTreeSitterRuntimeStatus, } from "./tree-sitter-shared.js";
|
|
29
29
|
import { centralityFromReverseDeps, getWordIndexBuildStatus, searchWordIndex, triggerBackgroundWordIndexBuild, } from "./word-index.js";
|
|
30
30
|
// --- Facades (re-exported so adapters import only this module) ---------------
|
|
31
|
-
export { analyzeFile
|
|
31
|
+
export { analyzeFile } from "./mcp/analyze.js";
|
|
32
32
|
export { createMcpHost } from "./mcp/host-shim.js";
|
|
33
|
-
export { createWarmIpcLineReader, createWarmIpcRequestQueue, ipcPathForCwd, readTurnEndStatus,
|
|
34
|
-
export { analyzeFileFresh, canRebuildPiLens,
|
|
33
|
+
export { createWarmIpcLineReader, createWarmIpcRequestQueue, ipcPathForCwd, readTurnEndStatus, WARM_TURN_END_SCHEMA_VERSION, } from "./mcp/ipc.js";
|
|
34
|
+
export { analyzeFileFresh, canRebuildPiLens, resolveRebuildScript, runRebuild, summarizeScan, } from "./mcp/review.js";
|
|
35
35
|
export { acknowledgeTurnEnd, runSessionStart, runTurnEnd, runTurnEndForIpc, } from "./mcp/session.js";
|
|
36
36
|
export { moduleReport, readEnclosing, readSymbol, renderCompactModuleReport, } from "./module-report.js";
|
|
37
37
|
export { projectReport, renderCompactProjectReport, } from "./project-report.js";
|
|
38
|
-
export { createDefaultHostPorts, } from "./host-ports.js";
|
|
39
38
|
// Effective-config introspection (#2427). A facade rather than a wrapper for
|
|
40
39
|
// the same reason the others are: the computation reaches into the config
|
|
41
40
|
// resolution, the LSP registry and the dispatch plan at once, and putting that
|
|
@@ -2,7 +2,7 @@ import { logExtension } from "./extension-log.js";
|
|
|
2
2
|
import { logBusEvent } from "./bus-events-logger.js";
|
|
3
3
|
import { normalizeFilePath } from "./path-utils.js";
|
|
4
4
|
import { createLiveBusEmitter, recordStaleBusFailure, resolveLiveBusEmitter, } from "./live-bus-emitter.js";
|
|
5
|
-
|
|
5
|
+
const LENS_EVENT_VERSION = 1;
|
|
6
6
|
export const LENS_EVENT_NAMES = {
|
|
7
7
|
analysisComplete: "pi-lens/analysis-complete",
|
|
8
8
|
findings: "pi-lens/findings",
|
|
@@ -189,6 +189,22 @@ export const LENS_FLAGS = [
|
|
|
189
189
|
default: false,
|
|
190
190
|
scope: "global",
|
|
191
191
|
},
|
|
192
|
+
...[
|
|
193
|
+
"knip",
|
|
194
|
+
"jscpd",
|
|
195
|
+
"madge",
|
|
196
|
+
"gitleaks",
|
|
197
|
+
"govulncheck",
|
|
198
|
+
"deadCode",
|
|
199
|
+
"complexity",
|
|
200
|
+
].map((analyzer) => ({
|
|
201
|
+
name: `no-${analyzer.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}`,
|
|
202
|
+
description: `Disable the ${analyzer} session-start analyzer. Also via ${analyzer}.enabled=false in config.`,
|
|
203
|
+
configKey: `${analyzer}.enabled`,
|
|
204
|
+
negated: true,
|
|
205
|
+
default: false,
|
|
206
|
+
scope: "project",
|
|
207
|
+
})),
|
|
192
208
|
];
|
|
193
209
|
const byName = new Map(LENS_FLAGS.map((spec) => [spec.name, spec]));
|
|
194
210
|
export function getLensFlagSpec(name) {
|
|
@@ -216,6 +232,7 @@ export const GLOBAL_NON_FLAG_CONFIG_SECTIONS = [
|
|
|
216
232
|
"ignore",
|
|
217
233
|
"dispatch",
|
|
218
234
|
"widget",
|
|
235
|
+
"startup",
|
|
219
236
|
"$schema",
|
|
220
237
|
];
|
|
221
238
|
/**
|
|
@@ -238,6 +255,7 @@ export const PROJECT_NON_FLAG_CONFIG_SECTIONS = [
|
|
|
238
255
|
"reviewGraph",
|
|
239
256
|
"trivy",
|
|
240
257
|
"helm",
|
|
258
|
+
"startup",
|
|
241
259
|
];
|
|
242
260
|
/**
|
|
243
261
|
* The four LSP settings a PROJECT config file may carry — at its root during
|
|
@@ -24,7 +24,7 @@ import { normalizeMapKey, toProjectRelativePath } from "./path-utils.js";
|
|
|
24
24
|
import { canonicalSourceTwin } from "./source-filter.js";
|
|
25
25
|
import { buildOrUpdateGraph } from "./review-graph/service.js";
|
|
26
26
|
import { detectFileRole } from "./file-role.js";
|
|
27
|
-
|
|
27
|
+
const DEFAULT_MAX_MAP_NODES = 500;
|
|
28
28
|
/**
|
|
29
29
|
* Aggregates a symbol-level {@link ReviewGraph} into file-level nodes/edges.
|
|
30
30
|
* Pure and side-effect-free — no graph build here, no filesystem access — so
|
|
@@ -220,39 +220,3 @@ export function runLogCleanup(dbg) {
|
|
|
220
220
|
}
|
|
221
221
|
return results;
|
|
222
222
|
}
|
|
223
|
-
/**
|
|
224
|
-
* Get current log storage summary
|
|
225
|
-
*/
|
|
226
|
-
export function getLogStorageSummary() {
|
|
227
|
-
const files = [];
|
|
228
|
-
let totalMB = 0;
|
|
229
|
-
// Main logs
|
|
230
|
-
for (const name of getManagedLogFiles()) {
|
|
231
|
-
const filePath = path.join(LOG_DIR, name);
|
|
232
|
-
if (fs.existsSync(filePath)) {
|
|
233
|
-
const sizeMB = getFileSizeMB(filePath);
|
|
234
|
-
const ageDays = getFileAgeDays(filePath);
|
|
235
|
-
files.push({ name, sizeMB, ageDays });
|
|
236
|
-
totalMB += sizeMB;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
// Daily logs
|
|
240
|
-
try {
|
|
241
|
-
if (fs.existsSync(LOGS_SUBDIR)) {
|
|
242
|
-
const dailyFiles = fs.readdirSync(LOGS_SUBDIR);
|
|
243
|
-
for (const name of dailyFiles) {
|
|
244
|
-
if (!name.endsWith(".jsonl"))
|
|
245
|
-
continue;
|
|
246
|
-
const filePath = path.join(LOGS_SUBDIR, name);
|
|
247
|
-
const sizeMB = getFileSizeMB(filePath);
|
|
248
|
-
const ageDays = getFileAgeDays(filePath);
|
|
249
|
-
files.push({ name: `logs/${name}`, sizeMB, ageDays });
|
|
250
|
-
totalMB += sizeMB;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
catch {
|
|
255
|
-
// Ignore
|
|
256
|
-
}
|
|
257
|
-
return { totalMB, files };
|
|
258
|
-
}
|
|
@@ -1173,7 +1173,7 @@ export function resolveConfigurationSection(initialization, section) {
|
|
|
1173
1173
|
// Exported (only) so tests can invoke the publishDiagnostics notification
|
|
1174
1174
|
// handler directly against a mock LSPClientState/connection without spawning
|
|
1175
1175
|
// a real language server. Not part of the public client API surface.
|
|
1176
|
-
export function setupIncomingHandlers(state, initialization) {
|
|
1176
|
+
export function setupIncomingHandlers(state, initialization, onDiagnosticsPublished) {
|
|
1177
1177
|
state.connection.onNotification("textDocument/publishDiagnostics", (params) => {
|
|
1178
1178
|
// #1639: the settle operation's own clock for THIS publish. A
|
|
1179
1179
|
// "quiet-window" settle's durationMs below measures from here (when
|
|
@@ -1188,6 +1188,7 @@ export function setupIncomingHandlers(state, initialization) {
|
|
|
1188
1188
|
// that is no longer open on this client.
|
|
1189
1189
|
if (state.closedDocuments?.has(normalizedPath))
|
|
1190
1190
|
return;
|
|
1191
|
+
onDiagnosticsPublished?.(state.serverId);
|
|
1191
1192
|
const newDiags = normalizeLspDiagnostics(params.diagnostics || []);
|
|
1192
1193
|
const docVersion = params.version;
|
|
1193
1194
|
if (PUB_DEBUG) {
|
|
@@ -3498,7 +3499,7 @@ async function resolveCodeActionBestEffort(state, action) {
|
|
|
3498
3499
|
// --- Client Factory ---
|
|
3499
3500
|
export async function createLSPClient(options) {
|
|
3500
3501
|
installCrashGuard();
|
|
3501
|
-
const { serverId, process: lspProcess, root, sessionCwd, initialization, initializeTimeoutMs = INITIALIZE_TIMEOUT_MS, launchVariant, } = options;
|
|
3502
|
+
const { serverId, process: lspProcess, root, sessionCwd, initialization, initializeTimeoutMs = INITIALIZE_TIMEOUT_MS, launchVariant, onDiagnosticsPublished, } = options;
|
|
3502
3503
|
// #449/#472: register this LSP child in the cross-process instance registry
|
|
3503
3504
|
// as soon as we have a live pid — BEFORE `initialize` completes, not after.
|
|
3504
3505
|
// Registering early means a child that dies/hangs during initialize (the
|
|
@@ -3671,7 +3672,7 @@ export async function createLSPClient(options) {
|
|
|
3671
3672
|
return;
|
|
3672
3673
|
void safeSendNotification(state.connection, "workspace/didChangeWatchedFiles", { changes });
|
|
3673
3674
|
});
|
|
3674
|
-
setupIncomingHandlers(state, initialization);
|
|
3675
|
+
setupIncomingHandlers(state, initialization, onDiagnosticsPublished);
|
|
3675
3676
|
connection.listen();
|
|
3676
3677
|
setupConnectionLifecycle(state, recentStderr);
|
|
3677
3678
|
let initResult;
|
|
@@ -88,7 +88,7 @@ const SKIPPED = {
|
|
|
88
88
|
tracked: 0,
|
|
89
89
|
};
|
|
90
90
|
/** Documents stat'd per pass. Bounds the per-pass cost on a large workspace. */
|
|
91
|
-
|
|
91
|
+
const DRIFT_CHECK_BATCH = 64;
|
|
92
92
|
/** Resyncs issued per pass. Paces the heal so a bulk edit cannot burst. */
|
|
93
93
|
export const DRIFT_RESYNC_BATCH = 4;
|
|
94
94
|
/** Tracked-document ceiling. A workspace sweep can open far more than this. */
|
|
@@ -131,7 +131,7 @@ function parseWorkspaceEdit(edit) {
|
|
|
131
131
|
function formatRange(range) {
|
|
132
132
|
return `${range.start.line + 1}:${range.start.character + 1}-${range.end.line + 1}:${range.end.character + 1}`;
|
|
133
133
|
}
|
|
134
|
-
|
|
134
|
+
function rangesOverlap(a, b) {
|
|
135
135
|
return (comparePosition(a.start, b.end) < 0 && comparePosition(b.start, a.end) < 0);
|
|
136
136
|
}
|
|
137
137
|
function positionsEqual(a, b) {
|
|
@@ -351,7 +351,7 @@ export async function normalizeWorkspaceEditToUtf16(edit, positionEncoding, cwd)
|
|
|
351
351
|
...(edit.documentChanges !== undefined ? { documentChanges } : {}),
|
|
352
352
|
};
|
|
353
353
|
}
|
|
354
|
-
|
|
354
|
+
function flattenWorkspaceTextEdits(edit) {
|
|
355
355
|
parseWorkspaceEdit(edit);
|
|
356
356
|
const buckets = new Map();
|
|
357
357
|
const push = (uri, edits) => {
|
|
@@ -786,7 +786,7 @@ async function createWorkspaceUriConfiner(cwd) {
|
|
|
786
786
|
filePath = uriToPath(uri);
|
|
787
787
|
}
|
|
788
788
|
catch (err) {
|
|
789
|
-
throw new Error(`invalid workspace edit URI ${uri}: ${err instanceof Error ? err.message : String(err)}
|
|
789
|
+
throw new Error(`invalid workspace edit URI ${uri}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
790
790
|
}
|
|
791
791
|
if (!path.isAbsolute(filePath))
|
|
792
792
|
throw new Error(`workspace edit path escapes workspace: ${uri}`);
|