@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
|
@@ -37,7 +37,7 @@ import { getProjectDataDir } from "./file-utils.js";
|
|
|
37
37
|
import { normalizeFilePath } from "./path-utils.js";
|
|
38
38
|
export const RECENT_TOUCHES_MAX_ENTRIES = 50;
|
|
39
39
|
/** Freshness window for the CHILD session_start consumer (15 minutes). */
|
|
40
|
-
|
|
40
|
+
const RECENT_TOUCHES_FRESHNESS_MS = 15 * 60 * 1000;
|
|
41
41
|
function recentTouchesPath(cwd) {
|
|
42
42
|
return path.join(getProjectDataDir(cwd), "recent-touches.json");
|
|
43
43
|
}
|
|
@@ -46,7 +46,7 @@ function recentTouchesPath(cwd) {
|
|
|
46
46
|
// INTO the same nudge pipeline, so one switch governs the whole path
|
|
47
47
|
// (producer, both consumers) as specified in the issue.
|
|
48
48
|
let _enabledCache;
|
|
49
|
-
|
|
49
|
+
function isRecentTouchesEnabled() {
|
|
50
50
|
if (_enabledCache === undefined) {
|
|
51
51
|
_enabledCache = process.env.PI_LENS_AGENT_NUDGE !== "0";
|
|
52
52
|
}
|
|
@@ -92,8 +92,6 @@ const MAIN_KINDS = new Set([
|
|
|
92
92
|
// so a repo heavy in JSON/YAML/Markdown doesn't trip the cap on files the graph
|
|
93
93
|
// would have filtered out anyway (the cap is on the walk, not on noise). #250.
|
|
94
94
|
const MAIN_KIND_EXTENSIONS = Array.from(MAIN_KINDS).flatMap((kind) => KIND_EXTENSIONS[kind] ?? []);
|
|
95
|
-
/** The bounded, source-filtered extension set shared by graph cache readers. */
|
|
96
|
-
export const REVIEW_GRAPH_SOURCE_EXTENSIONS = MAIN_KIND_EXTENSIONS;
|
|
97
95
|
const CHANGED_SYMBOLS_PREFIX = "session.reviewGraph.changedSymbols:";
|
|
98
96
|
const extractorCache = new Map();
|
|
99
97
|
const REVIEW_GRAPH_MAX_WARM_WORKSPACES = 8;
|
|
@@ -407,7 +405,7 @@ export function estimateReviewGraphStoreBytes(totalNodes, totalEdges) {
|
|
|
407
405
|
// cleared). Bounding only the cache left the full graph resident on the fact, so
|
|
408
406
|
// both sites go through `retainedGraph` below, memoized per graph instance so the
|
|
409
407
|
// two sites share ONE bounded object instead of trimming twice (#2255 review F2).
|
|
410
|
-
|
|
408
|
+
const GRAPH_MAX_IN_MEMORY_BYTES_DEFAULT = 512 * 1024 * 1024;
|
|
411
409
|
function graphMaxInMemoryBytes() {
|
|
412
410
|
const raw = Number(process.env.PI_LENS_GRAPH_MAX_IN_MEMORY_BYTES);
|
|
413
411
|
return Number.isFinite(raw) && raw > 0
|
|
@@ -967,7 +965,7 @@ function diffSignatureMaps(previous, next) {
|
|
|
967
965
|
// `RUNTIME_CONFIG.reviewGraph.maxFiles` constant as the fallback — the ratio
|
|
968
966
|
// table reproduces that same 1,000-file default at the default base, so this
|
|
969
967
|
// is behavior-neutral when nothing is configured.
|
|
970
|
-
|
|
968
|
+
function getReviewGraphMaxFiles(cwd) {
|
|
971
969
|
const override = Number.parseInt(process.env.PI_LENS_REVIEW_GRAPH_MAX_FILES ?? "", 10);
|
|
972
970
|
return Number.isFinite(override) && override > 0
|
|
973
971
|
? override
|
|
@@ -3481,7 +3479,7 @@ function addTreeSitterFile(graph, cwd, filePath, languageId, extracted, ignoredI
|
|
|
3481
3479
|
* SymbolInformation results (including native TypeScript 7) recover the same
|
|
3482
3480
|
* containment through `containerName` when the owner is present in the result.
|
|
3483
3481
|
*/
|
|
3484
|
-
|
|
3482
|
+
function addLspFallbackSymbols(graph, filePath, languageId, symbols) {
|
|
3485
3483
|
const normalized = normalizeMapKey(filePath);
|
|
3486
3484
|
const fileNodeId = `file:${normalized}`;
|
|
3487
3485
|
let added = 0;
|
|
@@ -90,7 +90,7 @@ function resolveDart(cwd, filePath, source) {
|
|
|
90
90
|
// JS/TS extension from the specifier before re-appending candidate extensions
|
|
91
91
|
// lets that universal pattern resolve to the real source file. Exported so
|
|
92
92
|
// builder.ts's warm `localImportToFile` shares the exact same regex (#694).
|
|
93
|
-
|
|
93
|
+
const JS_TS_EXT_RE = /\.(mjs|cjs|mts|cts|jsx?|tsx?)$/i;
|
|
94
94
|
/**
|
|
95
95
|
* Ordered candidate list for resolving a relative JS/TS import specifier
|
|
96
96
|
* (already resolved to an absolute base path) to an on-disk file, with
|
|
@@ -19,7 +19,7 @@ import { getAutofixPolicyForFile, hasBiomeConfig, hasDetektConfig, hasEslintConf
|
|
|
19
19
|
* path at all (they match on `ownerSessionId`), so this only trades a little
|
|
20
20
|
* extra staleness for guaranteed eventual formatting. (#791)
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
const DEFERRED_FORMAT_STALE_AFTER_MS = 10 * 60_000;
|
|
23
23
|
const DEFERRED_FORMAT_CONCURRENCY = 3;
|
|
24
24
|
function recordProjectChange(args) {
|
|
25
25
|
// One mutation seam (#2000 phase 1): bump + receipt + change-log live in
|
|
@@ -5,7 +5,7 @@ import { rotateMessageEndAttribution } from "./message-end-attribution.js";
|
|
|
5
5
|
import { createDeadline, yieldIfOverBudget } from "./cooperative-budget.js";
|
|
6
6
|
import { deadCodeIssueCount } from "./dead-code-client.js";
|
|
7
7
|
import { logDeadCodeScan } from "./dead-code-logger.js";
|
|
8
|
-
import { incrementDegradationCount, resetDegradationLedger, } from "./degradation-ledger.js";
|
|
8
|
+
import { incrementDegradationCount, recordDegradationOnce, resetDegradationLedger, } from "./degradation-ledger.js";
|
|
9
9
|
import { getDiagnosticTracker } from "./diagnostic-tracker.js";
|
|
10
10
|
import { resetPsScriptAnalyzerAvailability } from "./dispatch/runners/psscriptanalyzer.js";
|
|
11
11
|
import { resetInstallRetryLatches } from "./dispatch/runners/utils/availability-policy.js";
|
|
@@ -256,11 +256,14 @@ function logProjectSnapshotProbe(args) {
|
|
|
256
256
|
args.dbg(`project_snapshot: miss reason=${args.missReason}`);
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
|
-
function resolveStartupMode() {
|
|
259
|
+
function resolveStartupMode(projectConfig, globalConfig) {
|
|
260
260
|
const envMode = (process.env.PI_LENS_STARTUP_MODE ?? "").trim().toLowerCase();
|
|
261
261
|
if (envMode === "full" || envMode === "minimal" || envMode === "quick") {
|
|
262
262
|
return envMode;
|
|
263
263
|
}
|
|
264
|
+
const configMode = projectConfig?.startup?.mode ?? globalConfig?.startup?.mode;
|
|
265
|
+
if (configMode)
|
|
266
|
+
return configMode;
|
|
264
267
|
if (isPrintMode()) {
|
|
265
268
|
return "quick";
|
|
266
269
|
}
|
|
@@ -813,6 +816,15 @@ function scheduleStartupScans(deps, runtime, sessionGeneration, analysisRoot, sn
|
|
|
813
816
|
/** The scan bodies, once the analyzers they need are resolved. */
|
|
814
817
|
function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analysisRoot, snapshotRoot, languageProfile, dbg) {
|
|
815
818
|
const { todoScanner, cacheManager, knipClient, jscpdClient, deadCodeClients, govulncheckClient, gitleaksClient, trivyClient, opengrepClient, astGrepClient, depChecker, } = deps;
|
|
819
|
+
const analyzerEnabled = (flag) => !deps.getFlag(flag);
|
|
820
|
+
if (!analyzerEnabled("no-complexity")) {
|
|
821
|
+
dbg("session_start complexity: skipped (disabled by config)");
|
|
822
|
+
recordDegradationOnce({
|
|
823
|
+
kind: "startup-analyzer-disabled",
|
|
824
|
+
subject: "complexity",
|
|
825
|
+
reason: "skipped (disabled by config)",
|
|
826
|
+
});
|
|
827
|
+
}
|
|
816
828
|
// Some background scans are CPU-heavy and arrive on the event loop
|
|
817
829
|
// just as the user is most likely typing (right after /new). Defer
|
|
818
830
|
// those by a few seconds so the perceptible 50-100ms sync bursts they
|
|
@@ -869,7 +881,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
|
|
|
869
881
|
scanNames.push("knip", "jscpd", "ast-grep exports", "project index");
|
|
870
882
|
}
|
|
871
883
|
dbg(`session_start: launching background scans (${scanNames.join(", ")})`);
|
|
872
|
-
runTask("todo", async () => {
|
|
884
|
+
void runTask("todo", async () => {
|
|
873
885
|
if (!runtime.isCurrentSession(sessionGeneration))
|
|
874
886
|
return;
|
|
875
887
|
// The original implementation called todoScanner.scanDirectory(), which
|
|
@@ -906,9 +918,19 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
|
|
|
906
918
|
const isSubagent = isSubagentSession();
|
|
907
919
|
const skipHeavyweightScans = isSlowFs(analysisRoot) || isSubagent;
|
|
908
920
|
const runHeavyweightTask = (name, task) => {
|
|
921
|
+
const flag = `no-${name}`;
|
|
922
|
+
if (name !== "opengrep" && name !== "trivy" && !analyzerEnabled(flag)) {
|
|
923
|
+
dbg(`session_start ${name}: skipped (disabled by config)`);
|
|
924
|
+
recordDegradationOnce({
|
|
925
|
+
kind: "startup-analyzer-disabled",
|
|
926
|
+
subject: name,
|
|
927
|
+
reason: "skipped (disabled by config)",
|
|
928
|
+
});
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
909
931
|
if (skipHeavyweightScans)
|
|
910
932
|
return;
|
|
911
|
-
runTask(name, task);
|
|
933
|
+
void runTask(name, task);
|
|
912
934
|
};
|
|
913
935
|
if (skipHeavyweightScans) {
|
|
914
936
|
dbg(`session_start: skipping knip/jscpd/madge/dead-code/govulncheck/gitleaks/trivy/opengrep (${isSubagent ? "subagent" : "slow-fs"})`);
|
|
@@ -1239,7 +1261,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
|
|
|
1239
1261
|
// racing its independently deferred timer. Otherwise a slow graph build leaves
|
|
1240
1262
|
// `runtime.callGraph` unset at this task's start and loses the model for the
|
|
1241
1263
|
// entire session (#1070).
|
|
1242
|
-
runTask("codebase-model", async () => {
|
|
1264
|
+
void runTask("codebase-model", async () => {
|
|
1243
1265
|
await callGraphTask;
|
|
1244
1266
|
if (!runtime.isCurrentSession(sessionGeneration))
|
|
1245
1267
|
return;
|
|
@@ -1258,7 +1280,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
|
|
|
1258
1280
|
`${model.totalTokens} tokens, top symbols: ${top3 || "(none)"}`);
|
|
1259
1281
|
});
|
|
1260
1282
|
// ast-grep — export scan for duplicate detection
|
|
1261
|
-
runTask("ast-grep-exports", async () => {
|
|
1283
|
+
void runTask("ast-grep-exports", async () => {
|
|
1262
1284
|
if (await astGrepClient.ensureAvailable()) {
|
|
1263
1285
|
if (!runtime.isCurrentSession(sessionGeneration))
|
|
1264
1286
|
return;
|
|
@@ -1275,7 +1297,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
|
|
|
1275
1297
|
// word-index — identifier inverted index + BM25 for ranked symbol search
|
|
1276
1298
|
// (#162). Load -> rebuild-if-stale -> persist lifecycle (#348), shared with
|
|
1277
1299
|
// the quick-mode cold-start warmup pass below.
|
|
1278
|
-
runTask("word-index", async () => {
|
|
1300
|
+
void runTask("word-index", async () => {
|
|
1279
1301
|
await buildOrRefreshWordIndex({
|
|
1280
1302
|
runtime,
|
|
1281
1303
|
sessionGeneration,
|
|
@@ -1423,7 +1445,7 @@ export async function handleSessionStart(deps) {
|
|
|
1423
1445
|
// entirely — but only when PI_LENS_STARTUP_MODE is unset in the env
|
|
1424
1446
|
// (an explicit env var still takes highest precedence).
|
|
1425
1447
|
// Tunable: PI_LENS_WARMUP_DELAY_MS adjusts the warmup delay.
|
|
1426
|
-
let startupMode = resolveStartupMode();
|
|
1448
|
+
let startupMode = resolveStartupMode(deps.projectConfig, deps.globalConfig);
|
|
1427
1449
|
// SAFETY: these two flags are process-lifetime state pi-lens stashes on
|
|
1428
1450
|
// `globalThis` so a second extension instance in the same process sees the
|
|
1429
1451
|
// first one's warmup. There is no ambient declaration for them, and adding
|
|
@@ -1433,7 +1455,9 @@ export async function handleSessionStart(deps) {
|
|
|
1433
1455
|
const isFirstSessionOfProcess = !processGlobals.__piLensFirstSessionDone;
|
|
1434
1456
|
if (isFirstSessionOfProcess &&
|
|
1435
1457
|
process.env.PI_LENS_COLD_START_QUICK !== "0" &&
|
|
1436
|
-
!process.env.PI_LENS_STARTUP_MODE
|
|
1458
|
+
!process.env.PI_LENS_STARTUP_MODE &&
|
|
1459
|
+
!deps.projectConfig?.startup?.mode &&
|
|
1460
|
+
!deps.globalConfig?.startup?.mode) {
|
|
1437
1461
|
// Apply host-provided override (e.g. MCP server forces "full") before
|
|
1438
1462
|
// falling back to the TUI quick-mode heuristic.
|
|
1439
1463
|
startupMode = deps.startupModeOverride ?? "quick";
|
|
@@ -2271,7 +2295,10 @@ export async function handleSessionStart(deps) {
|
|
|
2271
2295
|
else {
|
|
2272
2296
|
dbg("session_start: no language defaults selected for pre-install");
|
|
2273
2297
|
}
|
|
2274
|
-
const
|
|
2298
|
+
const startupScansEnabled = deps.projectConfig?.startup?.scans?.enabled ??
|
|
2299
|
+
deps.globalConfig?.startup?.scans?.enabled ??
|
|
2300
|
+
true;
|
|
2301
|
+
const startupScansWillRun = allowBootstrapTasks && startupScan.canWarmCaches && startupScansEnabled;
|
|
2275
2302
|
const jstsHeavyScansWillRun = startupScansWillRun && canRunStartupHeavyScans(languageProfile, "jsts");
|
|
2276
2303
|
if (allowBootstrapTasks) {
|
|
2277
2304
|
scheduleDeferredToolProbes(deps, languageProfile, startupDefaults, jstsHeavyScansWillRun, dbg);
|
|
@@ -2326,6 +2353,14 @@ export async function handleSessionStart(deps) {
|
|
|
2326
2353
|
if (!allowBootstrapTasks) {
|
|
2327
2354
|
dbg("session_start: skipping startup background scans (startup mode)");
|
|
2328
2355
|
}
|
|
2356
|
+
else if (!startupScansEnabled) {
|
|
2357
|
+
dbg("session_start: skipping startup background scans (disabled by config)");
|
|
2358
|
+
recordDegradationOnce({
|
|
2359
|
+
kind: "startup-analyzer-disabled",
|
|
2360
|
+
subject: "startup-scans",
|
|
2361
|
+
reason: "skipped (disabled by config)",
|
|
2362
|
+
});
|
|
2363
|
+
}
|
|
2329
2364
|
else if (!startupScan.canWarmCaches) {
|
|
2330
2365
|
dbg(`session_start: skipping heavy scans (${startupScan.reason ?? "unknown"})`);
|
|
2331
2366
|
dbg(`session_start: skipping TODO scan (${startupScan.reason ?? "unknown"})`);
|
|
@@ -773,7 +773,8 @@ async function handleToolCallImpl(deps) {
|
|
|
773
773
|
// is the client's own answer, hoisted into `tree-sitter-shared.ts` where it
|
|
774
774
|
// needs nothing but the extension registry; `ComplexityClient.isSupportedFile`
|
|
775
775
|
// delegates to it, so this is not a second copy.
|
|
776
|
-
if (!
|
|
776
|
+
if (!getFlag("no-complexity") &&
|
|
777
|
+
!isExternalOrVendor &&
|
|
777
778
|
filePath &&
|
|
778
779
|
!runtime.complexityBaselines.has(filePath) &&
|
|
779
780
|
isComplexitySupportedFile(filePath)) {
|
|
@@ -803,6 +804,15 @@ async function handleToolCallImpl(deps) {
|
|
|
803
804
|
});
|
|
804
805
|
}
|
|
805
806
|
}
|
|
807
|
+
else if (getFlag("no-complexity") &&
|
|
808
|
+
filePath &&
|
|
809
|
+
isComplexitySupportedFile(filePath)) {
|
|
810
|
+
recordDegradationOnce({
|
|
811
|
+
kind: "startup-analyzer-disabled",
|
|
812
|
+
subject: "complexity",
|
|
813
|
+
reason: "skipped (disabled by config)",
|
|
814
|
+
});
|
|
815
|
+
}
|
|
806
816
|
// --- Read-Before-Edit Guard: check edits ---
|
|
807
817
|
// write = full replacement; no prior read needed (you're starting fresh).
|
|
808
818
|
// edit = partial modification; guard enforced to prevent blind overwrites.
|
|
@@ -43,17 +43,6 @@ const DETAIL_PATTERNS = [
|
|
|
43
43
|
export function stripAnsi(text) {
|
|
44
44
|
return text.replace(ANSI_ESCAPE, "").replace(ANSI_ESCAPE_EXTENDED, "");
|
|
45
45
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Normalize whitespace in a string.
|
|
48
|
-
* Collapses multiple spaces/tabs to single space, trims lines.
|
|
49
|
-
*/
|
|
50
|
-
export function normalizeWhitespace(text) {
|
|
51
|
-
return text
|
|
52
|
-
.split(/\r?\n/)
|
|
53
|
-
.map((line) => line.replace(/\s+/g, " ").trim())
|
|
54
|
-
.filter((line) => line.length > 0)
|
|
55
|
-
.join("\n");
|
|
56
|
-
}
|
|
57
46
|
/**
|
|
58
47
|
* Check if a line contains error indicators.
|
|
59
48
|
*/
|
|
@@ -1,59 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { collectSourceFiles, collectSourceFilesAsync, isBuildArtifact, } from "./source-filter.js";
|
|
3
|
-
/**
|
|
4
|
-
* Common parsing logic for ast-grep JSON output (handles both array and NDJSON).
|
|
5
|
-
*/
|
|
6
|
-
// biome-ignore lint/suspicious/noExplicitAny: ast-grep JSON output is untyped
|
|
7
|
-
export function parseAstGrepJson(raw) {
|
|
8
|
-
if (!raw)
|
|
9
|
-
return [];
|
|
10
|
-
const trimmed = raw.trim();
|
|
11
|
-
if (trimmed.startsWith("[")) {
|
|
12
|
-
try {
|
|
13
|
-
return JSON.parse(trimmed);
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
return [];
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return trimmed.split("\n").flatMap((l) => {
|
|
20
|
-
try {
|
|
21
|
-
return [JSON.parse(l)];
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return [];
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Check if a file should be ignored based on project type and common patterns.
|
|
30
|
-
*
|
|
31
|
-
* @deprecated Use `isBuildArtifact()` from `source-filter.js` instead for artifact
|
|
32
|
-
* detection, or compose your own filter using `collectSourceFiles()`. This function
|
|
33
|
-
* is kept for backward compatibility.
|
|
34
|
-
*/
|
|
35
|
-
export function shouldIgnoreFile(filePath, isTsProject) {
|
|
36
|
-
const relPath = filePath.replace(/\\/g, "/");
|
|
37
|
-
// Use new source-filter module for artifact detection
|
|
38
|
-
if (isTsProject && isBuildArtifact(filePath))
|
|
39
|
-
return true;
|
|
40
|
-
// Legacy: simple JS check for non-TS projects (hand-written JS)
|
|
41
|
-
const isJs = relPath.endsWith(".js") ||
|
|
42
|
-
relPath.endsWith(".mjs") ||
|
|
43
|
-
relPath.endsWith(".cjs");
|
|
44
|
-
if (isTsProject && isJs)
|
|
45
|
-
return true;
|
|
46
|
-
// Ignore test scripts and common test patterns
|
|
47
|
-
if (isTestFile(filePath))
|
|
48
|
-
return true;
|
|
49
|
-
// Ignore hidden directories and common build outputs
|
|
50
|
-
const pathParts = relPath.split("/").filter(Boolean);
|
|
51
|
-
for (const segment of pathParts.slice(0, -1)) {
|
|
52
|
-
if (isExcludedDirName(segment))
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
1
|
+
import { collectSourceFiles, collectSourceFilesAsync, } from "./source-filter.js";
|
|
57
2
|
/**
|
|
58
3
|
* Recursively find source files in a directory, respecting common excludes.
|
|
59
4
|
*
|
|
@@ -1,30 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Session Summary — generates turn_end and session-end summaries
|
|
3
3
|
*/
|
|
4
|
-
export function formatSessionSummary(stats) {
|
|
5
|
-
if (stats.totalShown === 0)
|
|
6
|
-
return "";
|
|
7
|
-
const lines = [
|
|
8
|
-
"## Diagnostic Summary",
|
|
9
|
-
"",
|
|
10
|
-
`Shown to agent: ${stats.totalShown}`,
|
|
11
|
-
`Auto-fixed: ${stats.totalAutoFixed} (${pct(stats.totalAutoFixed, stats.totalShown)}%)`,
|
|
12
|
-
`Fixed by agent: ${stats.totalAgentFixed} (${pct(stats.totalAgentFixed, stats.totalShown)}%)`,
|
|
13
|
-
"",
|
|
14
|
-
];
|
|
15
|
-
if (stats.topViolations.length > 0) {
|
|
16
|
-
lines.push("Top violations:");
|
|
17
|
-
for (const { ruleId, count } of stats.topViolations.slice(0, 5)) {
|
|
18
|
-
lines.push(` - ${ruleId} (${count}x)`);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return lines.join("\n");
|
|
22
|
-
}
|
|
23
|
-
function pct(part, total) {
|
|
24
|
-
if (total === 0)
|
|
25
|
-
return "0";
|
|
26
|
-
return Math.round((part / total) * 100).toString();
|
|
27
|
-
}
|
|
28
4
|
export function formatSlopScoreSummary(summary) {
|
|
29
5
|
if (summary.totalRuleDiagnostics === 0 || summary.totalKlocWritten <= 0) {
|
|
30
6
|
return "";
|
|
@@ -8,7 +8,7 @@ import { findLocalToolConfig } from "./path-utils.js";
|
|
|
8
8
|
// ast-grep's root config marker. The `ast-grep lsp` server is workspace-gated:
|
|
9
9
|
// it only operates in a project that has an `sgconfig.y[a]ml` at (or above) the
|
|
10
10
|
// file. These are the names ast-grep itself looks for as root markers.
|
|
11
|
-
|
|
11
|
+
const SGCONFIG_NAMES = ["sgconfig.yml", "sgconfig.yaml"];
|
|
12
12
|
/**
|
|
13
13
|
* Nearest `sgconfig.y[a]ml` walking up from `startDir`, or undefined if none.
|
|
14
14
|
* Deliberately UNCEILINGED at $HOME (refs #2472 review round 3, F1): ast-grep
|
|
@@ -65,10 +65,6 @@ export function getAstGrepRuleSources(projectRoot = process.cwd()) {
|
|
|
65
65
|
return true;
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
|
-
/** Compatibility export retained for callers that only need ordered dirs. */
|
|
69
|
-
export function shippedRuleDirsInPrecedenceOrder(projectRoot = process.cwd()) {
|
|
70
|
-
return getAstGrepRuleSources(projectRoot).map((source) => source.dir);
|
|
71
|
-
}
|
|
72
68
|
function findYamlFiles(dir) {
|
|
73
69
|
const files = [];
|
|
74
70
|
let entries;
|
|
@@ -115,7 +115,7 @@ function isCleanDryRun(argsAfterVerb) {
|
|
|
115
115
|
* replacement for the forbidden `git stash`. Blocking it would push
|
|
116
116
|
* operators back toward stash.
|
|
117
117
|
*/
|
|
118
|
-
|
|
118
|
+
const WORKTREE_MUTATING_GIT_MATCHER = {
|
|
119
119
|
id: "worktree-mutating",
|
|
120
120
|
indirectAlwaysMatches: false,
|
|
121
121
|
// `git checkout --help` opens documentation and changes nothing. This
|
|
@@ -58,11 +58,11 @@ import * as path from "node:path";
|
|
|
58
58
|
import { getGlobalPiLensLogDir } from "./probe-home-state.js";
|
|
59
59
|
/** Bounded tail-read budget PER source log file — never a full-file scan. */
|
|
60
60
|
export const SMELLS_TAIL_BYTES_PER_FILE = 64 * 1024;
|
|
61
|
-
|
|
61
|
+
const SMELLS_ROLLING_WINDOW_MS = 24 * 60 * 60_000;
|
|
62
62
|
/** Re-check cadence at `turn_end`, mirroring `memory-sampler.ts`'s pattern —
|
|
63
63
|
* cheap enough (bounded ~128KB I/O) not to need finer throttling, but a
|
|
64
64
|
* fixed interval keeps it from running on every single turn. */
|
|
65
|
-
|
|
65
|
+
const SMELLS_TURN_CHECK_INTERVAL = 20;
|
|
66
66
|
/** Trivial threshold constants (#1123 item 3's explicit design ask). Counts
|
|
67
67
|
* strictly below these never surface anywhere. */
|
|
68
68
|
export const SMELLS_THRESHOLDS = {
|
|
@@ -16,7 +16,7 @@ import { isAtOrAboveHomeDir } from "./path-utils.js";
|
|
|
16
16
|
import { getStartupScanMaxSourceFilesDerived } from "./project-scale.js";
|
|
17
17
|
import { shouldRecurseIntoDir, walkTreeStackAsync, walkTreeStackSync, } from "./source-walker.js";
|
|
18
18
|
import { getDirectoryMarkers, registerWorkspaceTopologyReset, } from "./workspace-topology.js";
|
|
19
|
-
|
|
19
|
+
const PROJECT_ROOT_MARKERS = [
|
|
20
20
|
".git",
|
|
21
21
|
"package.json",
|
|
22
22
|
"pyproject.toml",
|
|
@@ -24,16 +24,8 @@ export const PROJECT_ROOT_MARKERS = [
|
|
|
24
24
|
"go.mod",
|
|
25
25
|
"composer.json",
|
|
26
26
|
];
|
|
27
|
-
// Deprecated (#776): no longer read directly below — `computeStartupScanContext`
|
|
28
|
-
// / `resolveStartupScanContextAsync` now default `maxSourceFiles` to
|
|
29
|
-
// `getStartupScanMaxSourceFilesDerived(cwd)` (project-scale.ts's
|
|
30
|
-
// `maxProjectFiles` knob), which reproduces this same 2,000 value at the
|
|
31
|
-
// default base. Kept exported for tests/callers that still reference the
|
|
32
|
-
// literal.
|
|
33
|
-
export const MAX_STARTUP_SOURCE_FILES = 2000;
|
|
34
27
|
// #758: hard ceiling on the number of directory entries the startup source
|
|
35
28
|
// count walk will visit before it gives up and declares the tree too big to
|
|
36
|
-
// warm. The source-file early-exit (MAX_STARTUP_SOURCE_FILES) only fires when
|
|
37
29
|
// a project has MANY source files — a repo with FEW source files but a huge
|
|
38
30
|
// pile of non-source files (e.g. a game mod: 300 scripts among 84k data files)
|
|
39
31
|
// never trips it, so the pre-#758 walk traversed the entire tree, dominated by
|
|
@@ -77,7 +69,7 @@ export const _resetStartupScanMaxEntriesForTests = _maxEntries._resetForTests;
|
|
|
77
69
|
*
|
|
78
70
|
* The content-derived reasons `too-many-source-files` and `too-many-entries`
|
|
79
71
|
* (#758) are the ones that are TTL'd. They can go stale on their own: the repo
|
|
80
|
-
* can shrink below
|
|
72
|
+
* can shrink below the source-file cap (or below the entry ceiling)
|
|
81
73
|
* between sessions, and nothing else would notice
|
|
82
74
|
* — the seq-based freshness check that guards every other
|
|
83
75
|
* `project-snapshot.json` field never fires for them, because pi-lens never
|