@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
|
@@ -16,6 +16,90 @@ All notable changes to pi-lens will be documented in this file.
|
|
|
16
16
|
|
|
17
17
|
### Security
|
|
18
18
|
|
|
19
|
+
## [4.1.5] - 2026-09-08
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **Dogfood knip on pi-lens's own source (closes #2698)** — `knip.jsonc` now resolves the compiled-sibling layout (`clients/*.js` sitting gitignored beside `clients/*.ts` after `npm run build`) that previously made knip report 491 source files "unused"; `npm run knip` (advisory in `lint.yml`, `continue-on-error`) purges those build artifacts before every run, declares the real `mcp/`/`scripts/` entry points, and documents each dependency/binary/file exemption it needed (husky, `@biomejs/biome`, `typescript-language-server`, `markdownlint-cli2`, two parked/dynamically-loaded files) with the reason knip can't see the usage on its own.
|
|
24
|
+
|
|
25
|
+
- **`PreToolUse` Bash guard hook for the fixer/reviewer non-negotiables (closes #2699)** — `scripts/hooks/guard-bash.mjs`, registered in `.claude/settings.json` on the Bash tool via `${CLAUDE_PROJECT_DIR}`, mechanically denies `git stash` in any form, `git reset --soft origin/<branch>` / `--hard`, a hand-typed `git worktree remove` with two force flags, and an unpinned `node` probe that loads runtime code from `clients/`/`dist/` with no `PI_LENS_HOME` — the same rules a fixer ran afoul of by hand on 2026-09-07 and 2026-09-02. Command text is read by a single region pass that subtracts what bash cannot execute (comments, and heredoc bodies — a PR description, an issue comment, a written file) before anything is tokenized, so a forbidden command *mentioned* in a document is never mistaken for one being run; a heredoc body with an unquoted delimiter still has its `$( )`/backtick substitutions scanned, because bash does expand those. Never blocks the tool on its own failure: malformed or missing stdin degrades to allow.
|
|
26
|
+
|
|
27
|
+
- **Add per-analyzer `knip.enabled`, `jscpd.enabled`, `madge.enabled`, `gitleaks.enabled`, `govulncheck.enabled`, `deadCode.enabled`, and `complexity.enabled` keys plus `startup.mode` and `startup.scans.enabled` controls (refs #2721)** — project and global configuration can disable startup analyzers or heavyweight startup scans while preserving diagnostics and LSP.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- **Nightly tracking-issue updates share one reusable CLI (closes #2683)** — compat-smoke, install-smoke, and clean-signal drift use one exact-title issue seam that fails loudly on a GitHub error and searches beyond the first page; both smoke workflows now auto-close their tracker on a clean run.
|
|
32
|
+
|
|
33
|
+
- **oxlint advisory tier (categories, plugins, type-aware) and publint on the packed tarball (refs #2700, refs #2697)** — `npm run lint:js` (gating) additionally denies 32 individually-named rules from the `suspicious`/`perf` categories and the `import`/`promise`/`typescript`/`unicorn`/`oxc` plugins that produce zero findings on master today. A new `npm run lint:js:advisory` runs the full intended set — categories `correctness`+`suspicious`+`perf`, plugins `typescript`+`unicorn`+`promise`+`import`+`oxc`, and `--type-aware` (paired `oxlint-tsgolint@7.0.2001` devDependency, per oxlint 1.80.0's own `peerDependencies` pin) — over the whole repo including tests, with eight rules policy-allowed for named reasons (four are overwhelming test-tree noise, four are documented false positives on deliberate patterns: a `void`-ignored promise in a callback, an explicit `.call(bus, …)` this-binding, a side-effect import, and an AbortSignal property poll). It runs as a new `oxlint (advisory)` job in `lint.yml`, classified advisory by the repo's `(advisory)` name-suffix convention (no `continue-on-error`) — the job's own conclusion is green whenever the type-aware sweep actually ran (a rule-count floor guards against a silently-degraded `oxlint-tsgolint` peer) regardless of how many un-triaged findings it reports, and red only on that degradation. A governance test resolves both scripts' real enabled-rule sets via oxlint's own `--print-config` and asserts the gating set stays a subset of the advisory set.
|
|
34
|
+
|
|
35
|
+
`ci.yml`'s `prod-install-build` job now packs the built package (`npm pack`) and runs `publint` on the tarball (gating) — the check #2587's manifest regression was missing. `attw` (arethetypeswrong) is not added: the package ships no `.d.ts` at all, so there is nothing for it to grade; `docs/release-qa-baseline.md` records both facts under the `npm-pack` modality.
|
|
36
|
+
|
|
37
|
+
- **Internal: 295 unused exports, 235 unused exported types and 4 duplicate exports removed, with the 85 declarations that became dead (refs #2708)** — no user-facing behaviour changes; the public entry points, the MCP server and every script keep their exports. Found by knip with tests counted as entry points, so nothing a test imports was touched.
|
|
38
|
+
|
|
39
|
+
- **knip advisory job green: the last three unused test-support exports are private, the dead `scripts/hooks/**/*.ts` entry pattern is gone, and the path-spawned `jscpd` devDependency is credited (closes #2708)** — `testSourceFiles`, `testsRelative` and `SCAN_INFRASTRUCTURE` in `tests/support/flake-shape-scan.ts` had no importer after #2749; the hooks are `.mjs`, so the `.ts` entry pattern matched nothing.
|
|
40
|
+
|
|
41
|
+
- **Cleared 51 of the 54 knip rows (23 of the 26 unused test exports; the 3 flake-shape rows stay for #2742) and 28 unused exported test types (refs #2708)** — test-support APIs are now private where their consumers are local, while the packaging workflow's pinned `publint` dependency remains declared and documented to Knip.
|
|
42
|
+
|
|
43
|
+
- **Advisory CI adds jscpd, yamllint, typos, and taplo (refs #2706)** — pinned tooling jobs make duplication, YAML style, spelling, and TOML checks visible without blocking the merge train.
|
|
44
|
+
|
|
45
|
+
- **Delegated-worker contract: handoff files for workers without Git authority (refs #2733, #2721)** — `docs/pi-lens-subagent.md` and the AGENTS.md role-contract section now name the `PR_BODY.md` / `COMMIT_MSG.txt` worktree-root handoff the orchestrator commits from, and the sandbox test incantation (`--configLoader runner`, offline grammar prefetch) that plegma codex workers need.
|
|
46
|
+
|
|
47
|
+
- **Reviewer contract diffs from the merge base** — two reviews this week reported another lane's merge in reverse as a HIGH regression because they diffed two-dot against a newer `origin/master`; the contract now names the three-dot form.
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- Keep admission registry ordering checks mutation-sensitive.
|
|
52
|
+
|
|
53
|
+
- **Turn-end session test doubles use the shared runner-error predicate (closes #2673)** — partial runner errors with `failed === 0` now exercise the same classification as production instead of requiring `passed === 0` in four local `formatResult` doubles.
|
|
54
|
+
|
|
55
|
+
- **yamllint, ruff, spellcheck, psscriptanalyzer, oxlint, and shellcheck now lint from the project's cwd, not the extension host's (closes #2691)** — six dispatch runners already computed `ctx.cwd` for their availability probe and config-detection helper, then spawned the actual lint/analysis process without passing that same `cwd`. yamllint's config discovery walks upward from the process's cwd, so it could silently pick up the wrong `.yamllint` (or none) when the extension host's own working directory differed from the project being linted; the other five get the same `cwd` for consistency with their probes, and typos additionally resolves any `extend-exclude` patterns against it. A new sweep over every `safeSpawnAsync`/`safeSpawnSync` call site under `clients/dispatch/runners/*.ts` — including spawns routed through a same-file helper, which is checked at the helper's own call sites — fails by file:line on any that omit `cwd`, so the next tool with this shape can't slip through a symbol-only check the way this one did for five runners after #1731 fixed sqlfluff.
|
|
56
|
+
|
|
57
|
+
- **Call-site sweeps now share the AST-backed `sweep-kit.callSites` seam (refs #2694)** — runner cwd and real-process detectors no longer hand-roll balanced-parenthesis scans, so nested syntax, comments, and strings cannot change a call's boundaries.
|
|
58
|
+
|
|
59
|
+
- **The sweep-floor meta-sweep now matches floor calls over comment- and string-blanked source (closes #2710)** — the meta-sweep registered a test file as floor-covered whenever `/assertNonEmptyScan\s*\(/` or the `auditRegistry({…minScanned…})` pattern matched its raw source, so a docblock merely quoting the helper name excused a sweep that made no real floor call (AGENTS.md shape 38; the same raw-vs-blanked mismatch #2693 round 2 F1 fixed in the runner-spawn-cwd sweep). Registration now runs through `stripSource`, and fixture tests pin that a prose-only mention is reported uncovered while a real call still registers.
|
|
60
|
+
|
|
61
|
+
- **PHP LSP (intelephense) now installs (closes #2722)** — the managed installer verified every npm tool by spawning `--version`, and intelephense has no CLI: it prints ~4 MB of bundled source before the "connection input stream is not set" line that proves it is a healthy stdio server, and Node drops everything past 1 MiB of a piped stderr, so that proof never reached pi-lens and the installer deleted the package it had just installed — every time, since 2026-04-17. A managed npm server can now declare that it is verified from the installed tree instead (package manifest + entry module on disk, no spawn at all), which intelephense does; and for every tool still verified by spawning, a probe that ran out of readable output before it could decide now keeps the installation when the installed tree is intact (an incomplete tree is still cleaned up so the next install can repair it) instead of treating "cannot tell" as "broken".
|
|
62
|
+
|
|
63
|
+
- **Custom LSP servers that never publish diagnostics are navigation-only, not timed out (closes #2765)** — a custom server with no pull-diagnostics provider gets one bounded first-contact push wait per session; silence for the full push budget latches it navigation-only (no wait on later files, a distinct summary bucket, never counted clean), a publish latches it push-capable, concurrent first touches share the one probe, and a hook-deadline cutoff stays unconfirmed instead of latching; a rejected or shutdown-aborted shared probe fails closed for every waiting touch, clears its single-flight entry and is retried without latching.
|
|
64
|
+
|
|
65
|
+
- **Read-guard exemption globs and the file-utils directory glob no longer backtrack on adjacent wildcards (closes #2622)** — runs of adjacent `*` are collapsed to one before compiling, which is semantically identical for the `*`-only dialect and removes the measured backtracking (a 12-star pattern against a 40-component path took >15 s before the fix and ~2 ms after it); a differential corpus pins both matchers' answers unchanged.
|
|
66
|
+
|
|
67
|
+
- **The install-time warm-loader log now honors `PI_LENS_HOME` (closes #2628)** — agent-worktree installs no longer write `warm_loader_cache` records to the maintainer's real `~/.pi-lens/install.log` when the worktree home is pinned.
|
|
68
|
+
|
|
69
|
+
- Require title close keywords to be repeated in the PR body, preventing the four 2026-09-06 incidents including #2610's merge commit from appearing to close issues that GitHub left open.
|
|
70
|
+
|
|
71
|
+
- **The flake-shape ratchet now sees child processes routed through test support helpers (refs #2653)** — code-channel calls to `gitFixtureSpawnAsync`, `safeSpawnAsync`, and the other support helpers that reach `node:child_process` now join the real-process census.
|
|
72
|
+
|
|
73
|
+
- **Managed npm refreshes deduplicate shared packages (closes #2666)** — one package update now covers every registered tool id that shares its package, preserving refresh budget slots for other packages.
|
|
74
|
+
|
|
75
|
+
- **Hold the merge train for unconcluded gating checks (closes #2679)** — Keep approved PRs pending until every non-advisory check reaches a terminal conclusion.
|
|
76
|
+
|
|
77
|
+
- **`scripts/npm-retry.mjs` annotates `infra: registry unreachable` only for network-shaped failures (closes #2684)** — a deterministic npm failure (`ERESOLVE`, `E404`, `EINTEGRITY`, `ETARGET`) now stops after one attempt with a plain "failed after N attempt(s)" line and the exit code preserved, while timeouts, spawn errors, and the restored shared `NET_PATTERN` plus npm-local transient patterns keep the retry backoff and origin/master's `failed 3 times` infra annotation; network evidence wins over a mixed deterministic code, and unknown non-network failures retain retry behavior.
|
|
78
|
+
|
|
79
|
+
- **LSP and smoke harness scratch directories use liveness-gated cleanup (closes #2688, closes #2687)** — shared per-run scratch directories record their owner process, preserve live workspaces, remove dead or aged orphaned entries, and announce each scratch home for reliable telemetry discovery.
|
|
80
|
+
|
|
81
|
+
- **Guard-bash preserves here-string boundaries and classifies heredoc substitutions the way bash expands them (closes #2705, refs #2726)** — `<<<` is consumed as a here-string operator instead of being re-read as a phantom heredoc delimiter; an unclosed `$(` or backtick in an unquoted heredoc body is treated as non-executable while later live commands remain classified; and a valid `$()` or backtick substitution discovered before a later unclosed one stays classified, with substitutions nested inside the malformed span kept inert like bash.
|
|
82
|
+
|
|
83
|
+
- **Fail the oxlint advisory tier when type-aware support is unavailable (refs #2709)** — `lint:js:advisory` now checks that `oxlint-tsgolint` resolves, exposes its binary, and satisfies oxlint's declared peer range before running.
|
|
84
|
+
|
|
85
|
+
- **A red `Tool smoke (nightly)` run now notifies through a persistent tracking issue, on the failure path too (closes #2723)** — the workflow's only tracking-issue writer (`Notify on silentOnClean drift`, #529/#594) sat behind the LSP handshake layer step with `continue-on-error: true` but no `if: always()`, so GitHub skipped it — and everything after it — exactly when an earlier step failed: 13 consecutive red nights (2026-08-26 → 2026-09-07) with no automated notice, and that notifier is scoped to unrelated `silentOnClean` telemetry regardless. The three gating steps (Tool layer, LSP handshake layer, Format layer — the only three without `continue-on-error`) now each carry an `id:` and a captured `tee` log, feeding a new, final `if: always()` step that reads all three outcomes into `scripts/notify-tool-smoke-red.mjs`: it upserts a single persistent tracking issue naming the failing layer, the `✗` rows with reason text, and a consecutive-red count, closing it automatically on the first fully green run. Mirrors `install-smoke.yml`'s existing `Notify install drift` step and reuses its four-outcome (`success`/`failure`/`cancelled`/`skipped`) classification and drift-issue title lookup rather than re-deriving them; cross-references #2683's not-yet-landed consolidation of these notifiers.
|
|
86
|
+
|
|
87
|
+
- **Heredoc bodies no longer leak into Bash command analysis (refs #2726)** — `tokenizeShellCommand` now consumes `<<` and `<<-` bodies, stops delimiter parsing at shell metacharacters, drops quoted body text, and retains substitutions from unquoted bodies so git-guard does not classify prose as an executable command.
|
|
88
|
+
|
|
89
|
+
- **Turn-end test failures now reach the next model context without a terminal entry (closes #2733)** — settled failures keep their provenance and advisory framing, survive session reset until delivery, then deliver once through the next context build. Rehydration matches the stable session id only, so `--session` quit-and-resume works across activation owners while in-process activation isolation stays in the pending map. Pull diagnostics and MCP turn-end handling remain unchanged.
|
|
90
|
+
|
|
91
|
+
- **Test ratchets now ignore comment and string laundering when matching source requirements (closes #2736)** — the Git-fixture, NDJSON writer, runner outcome, flake-shape, and LSP double-admission header detectors now use `stripSource`-based evidence, preserving string literals only where they carry the command or import path being checked.
|
|
92
|
+
|
|
93
|
+
- **Formatters now run from the project root (closes #2756)** — `formatFile` uses the nearest formatter config or `.gitignore`, with a real `.git` fallback capped at `$HOME`, so `.prettierignore`, `.gitignore`, and cwd-discovered configs resolve as at the CLI. Prettier, Biome, and oxfmt are covered.
|
|
94
|
+
|
|
95
|
+
- **Remove the unused scratch export and declaration, and fix oxfmt violations in two tests (refs #2759).**
|
|
96
|
+
|
|
97
|
+
- **Nightly tool smoke: the six `--install` steps share one tool tree again (refs #2762, refs #2687)** — #2755 dropped the job-level `PI_LENS_HOME`/`PILENS_DATA_DIR` pin from `tool-smoke.yml` and `parser-smoke.yml`, so the Format layer started from an empty scratch home and seven managed formatters ran against nothing; the pin and its workflow test are restored, per-run scratch isolation stays the local default.
|
|
98
|
+
|
|
99
|
+
- **Format smoke rows classify typed unavailable outcomes as skips (refs #2767)** — The format smoke harness no longer reports missing formatter executables as formatting failures.
|
|
100
|
+
|
|
101
|
+
- **Ledger fields no longer read `[object Object]`, and a rejected LSP push-wait no longer surfaces as an unhandled rejection (refs #2700)** — `normalizeForLedger` serialises plain objects and arrays instead of `String()`-ing them (a value with its own throwing `toString` still raises into the ledger's corrupted-input failsafe); the LSP push-wait settle marker attaches on both the resolve and the reject path. Found by the oxlint type-aware pass (`no-base-to-string`, `no-floating-promises`); the same pass's `preserve-caught-error` sites now carry `{ cause }`.
|
|
102
|
+
|
|
19
103
|
## [4.1.4] - 2026-09-07
|
|
20
104
|
|
|
21
105
|
Patch release carrying everything merged since 4.1.3 (152 entries, including 12 under Added and 2 under Deprecated — the additions are extension-internal and CI/test-harness surfaces, no new user-facing commands). Release-QA (`scripts/release-qa.mjs`, #2606) reports SHIP on pi 0.80.10 and pi 0.85.1: 11 of 11 rows witnessed, 0 failed, 0 skipped, git-install row included.
|
|
@@ -33,10 +33,3 @@ export function logActionableWarningsEvent(entry) {
|
|
|
33
33
|
: {}),
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
|
-
export function getActionableWarningsLogPath() {
|
|
37
|
-
return AW_LOG_FILE;
|
|
38
|
-
}
|
|
39
|
-
/** Resolve once all enqueued actionable-warnings writes are on disk. */
|
|
40
|
-
export function flushActionableWarningsLog() {
|
|
41
|
-
return writer.flush();
|
|
42
|
-
}
|
|
@@ -223,7 +223,7 @@ export function validateAdvisoryProvenance(record, cwd, runtime) {
|
|
|
223
223
|
// of which any #1461 slice writes yet, so nothing here currently touches
|
|
224
224
|
// it.
|
|
225
225
|
/** Stat budget: one per unique cited path, sharing the envelope's own cap. */
|
|
226
|
-
|
|
226
|
+
const MAX_FINDING_PATH_STATS = MAX_ADVISORY_AFFECTED_FILES;
|
|
227
227
|
/** How many dead paths a single drop record names before it stops. */
|
|
228
228
|
const MAX_LOGGED_DEAD_PATHS = 3;
|
|
229
229
|
/**
|
|
@@ -61,7 +61,7 @@ function reportAstGrepRulesHealth(bundledRuleDir) {
|
|
|
61
61
|
* Exported for tests: they drive it against a registered bridge instead of
|
|
62
62
|
* spawning ast-grep.
|
|
63
63
|
*/
|
|
64
|
-
|
|
64
|
+
function recordAstGrepApply(matches) {
|
|
65
65
|
const bridge = getMutationBridge();
|
|
66
66
|
if (!bridge || matches.length === 0)
|
|
67
67
|
return;
|
|
@@ -115,11 +115,4 @@ export function logAstGrepToolEvent(event) {
|
|
|
115
115
|
};
|
|
116
116
|
writer.log({ ts: new Date().toISOString(), ...payload });
|
|
117
117
|
}
|
|
118
|
-
export function getAstGrepToolLogPath() {
|
|
119
|
-
return AG_LOG_FILE;
|
|
120
|
-
}
|
|
121
|
-
/** Resolve once all enqueued ast-grep-tool writes are on disk. */
|
|
122
|
-
export function flushAstGrepToolLog() {
|
|
123
|
-
return writer.flush();
|
|
124
|
-
}
|
|
125
118
|
export { countLines as _countLinesForTest };
|
|
@@ -29,8 +29,121 @@ function stripQuotes(token) {
|
|
|
29
29
|
}
|
|
30
30
|
return token;
|
|
31
31
|
}
|
|
32
|
+
function parseHeredocDelimiter(command, start) {
|
|
33
|
+
let i = start;
|
|
34
|
+
const stripTabs = command[i] === "-";
|
|
35
|
+
if (stripTabs)
|
|
36
|
+
i += 1;
|
|
37
|
+
while (/\s/.test(command[i] ?? ""))
|
|
38
|
+
i += 1;
|
|
39
|
+
let delimiter = "";
|
|
40
|
+
let quoted = false;
|
|
41
|
+
while (i < command.length && !/[\s;&|<>]/.test(command[i] ?? "")) {
|
|
42
|
+
const ch = command[i];
|
|
43
|
+
if (ch === "'" || ch === '"') {
|
|
44
|
+
quoted = true;
|
|
45
|
+
const quote = ch;
|
|
46
|
+
i += 1;
|
|
47
|
+
while (i < command.length && command[i] !== quote) {
|
|
48
|
+
delimiter += command[i++];
|
|
49
|
+
}
|
|
50
|
+
if (command[i] !== quote)
|
|
51
|
+
return undefined;
|
|
52
|
+
i += 1;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (ch === "\\") {
|
|
56
|
+
quoted = true;
|
|
57
|
+
i += 1;
|
|
58
|
+
if (i >= command.length)
|
|
59
|
+
return undefined;
|
|
60
|
+
delimiter += command[i++];
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
delimiter += ch;
|
|
64
|
+
i += 1;
|
|
65
|
+
}
|
|
66
|
+
if (!delimiter)
|
|
67
|
+
return undefined;
|
|
68
|
+
return { heredoc: { delimiter, stripTabs, quoted }, end: i - 1 };
|
|
69
|
+
}
|
|
70
|
+
function heredocSubstitutionBodies(body) {
|
|
71
|
+
const substitutions = [];
|
|
72
|
+
for (let i = 0; i < body.length; i += 1) {
|
|
73
|
+
if (body.startsWith("$(", i)) {
|
|
74
|
+
let depth = 1;
|
|
75
|
+
let quote;
|
|
76
|
+
for (let end = i + 2; end < body.length; end += 1) {
|
|
77
|
+
const ch = body[end];
|
|
78
|
+
if (quote === "single") {
|
|
79
|
+
if (ch === "'")
|
|
80
|
+
quote = undefined;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (quote === "double") {
|
|
84
|
+
if (ch === '"' && body[end - 1] !== "\\")
|
|
85
|
+
quote = undefined;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (ch === "'" || ch === '"') {
|
|
89
|
+
quote = ch === "'" ? "single" : "double";
|
|
90
|
+
}
|
|
91
|
+
else if (body.startsWith("$(", end)) {
|
|
92
|
+
depth += 1;
|
|
93
|
+
end += 1;
|
|
94
|
+
}
|
|
95
|
+
else if (ch === ")") {
|
|
96
|
+
depth -= 1;
|
|
97
|
+
if (depth === 0) {
|
|
98
|
+
substitutions.push(body.slice(i + 2, end));
|
|
99
|
+
i = end;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else if (body[i] === "`" && body[i - 1] !== "\\") {
|
|
106
|
+
const end = body.indexOf("`", i + 1);
|
|
107
|
+
if (end >= 0) {
|
|
108
|
+
substitutions.push(body.slice(i + 1, end));
|
|
109
|
+
i = end;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return substitutions;
|
|
114
|
+
}
|
|
115
|
+
function consumeHeredocBodies(command, start, heredocs) {
|
|
116
|
+
let cursor = start;
|
|
117
|
+
const substitutions = [];
|
|
118
|
+
for (const heredoc of heredocs) {
|
|
119
|
+
const bodyStart = cursor;
|
|
120
|
+
let found = false;
|
|
121
|
+
while (cursor < command.length) {
|
|
122
|
+
const lineEnd = command.indexOf("\n", cursor);
|
|
123
|
+
const end = lineEnd < 0 ? command.length : lineEnd;
|
|
124
|
+
let line = command.slice(cursor, end);
|
|
125
|
+
if (heredoc.stripTabs)
|
|
126
|
+
line = line.replace(/^\t+/, "");
|
|
127
|
+
if (line.replace(/\r$/, "") === heredoc.delimiter) {
|
|
128
|
+
if (!heredoc.quoted)
|
|
129
|
+
substitutions.push(...heredocSubstitutionBodies(command.slice(bodyStart, cursor)));
|
|
130
|
+
cursor = lineEnd < 0 ? end : end + 1;
|
|
131
|
+
found = true;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
cursor = lineEnd < 0 ? end : end + 1;
|
|
135
|
+
}
|
|
136
|
+
if (!found) {
|
|
137
|
+
if (!heredoc.quoted)
|
|
138
|
+
substitutions.push(...heredocSubstitutionBodies(command.slice(bodyStart)));
|
|
139
|
+
return { end: command.length - 1, substitutions };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { end: cursor - 1, substitutions };
|
|
143
|
+
}
|
|
32
144
|
export function tokenizeShellCommand(command) {
|
|
33
145
|
const segments = [];
|
|
146
|
+
const pendingHeredocs = [];
|
|
34
147
|
let tokens = [];
|
|
35
148
|
let word = "";
|
|
36
149
|
let quote;
|
|
@@ -114,12 +227,21 @@ export function tokenizeShellCommand(command) {
|
|
|
114
227
|
i++;
|
|
115
228
|
continue;
|
|
116
229
|
}
|
|
117
|
-
if (/\s/.test(ch)) {
|
|
230
|
+
if (/\s/.test(ch) && ch !== "\n") {
|
|
118
231
|
flushWord();
|
|
119
232
|
continue;
|
|
120
233
|
}
|
|
121
234
|
if (ch === ";" || ch === "\n" || ch === "|" || ch === "&") {
|
|
122
235
|
flushWord();
|
|
236
|
+
if (ch === "\n" && pendingHeredocs.length > 0) {
|
|
237
|
+
const heredocs = pendingHeredocs.splice(0);
|
|
238
|
+
flushSegment();
|
|
239
|
+
const consumed = consumeHeredocBodies(command, i + 1, heredocs);
|
|
240
|
+
for (const substitution of consumed.substitutions)
|
|
241
|
+
segments.push(...tokenizeShellCommand(substitution));
|
|
242
|
+
i = consumed.end;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
123
245
|
let terminator;
|
|
124
246
|
if (ch === "|" && next === "|")
|
|
125
247
|
i++;
|
|
@@ -136,6 +258,16 @@ export function tokenizeShellCommand(command) {
|
|
|
136
258
|
}
|
|
137
259
|
if (ch === "<" || ch === ">") {
|
|
138
260
|
flushWord();
|
|
261
|
+
if (ch === "<" &&
|
|
262
|
+
next === "<" &&
|
|
263
|
+
command[i - 1] !== "<" &&
|
|
264
|
+
command[i + 2] !== "<") {
|
|
265
|
+
const parsed = parseHeredocDelimiter(command, i + 2);
|
|
266
|
+
if (parsed) {
|
|
267
|
+
pendingHeredocs.push(parsed.heredoc);
|
|
268
|
+
i = parsed.end;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
139
271
|
unsupported = true;
|
|
140
272
|
continue;
|
|
141
273
|
}
|
|
@@ -167,12 +167,6 @@ async function resolveForwardImportsMemoized(cwd, filePath, resolveForwardImport
|
|
|
167
167
|
});
|
|
168
168
|
return imports;
|
|
169
169
|
}
|
|
170
|
-
/** Test-only: clear the extractor and import-resolution memos. */
|
|
171
|
-
export function _resetBlockerFreshnessForTests() {
|
|
172
|
-
extractorCache.clear();
|
|
173
|
-
importResolutionMemo.clear();
|
|
174
|
-
importResolutionMemoTurnIndex = undefined;
|
|
175
|
-
}
|
|
176
170
|
/**
|
|
177
171
|
* Production forward-import resolver. Parses `filePath` with its tree-sitter grammar
|
|
178
172
|
* and resolves every extracted import source to in-project files. Degrades to an
|
|
@@ -114,7 +114,7 @@ const bootstrapFlight = createSingleFlight();
|
|
|
114
114
|
* budget — it is the liveness bound AGENTS.md's both-bounds rule requires, so
|
|
115
115
|
* a wedged module evaluation cannot park a `tool_call` handler forever.
|
|
116
116
|
*/
|
|
117
|
-
|
|
117
|
+
const BOOTSTRAP_LOAD_TIMEOUT_MS = 10_000;
|
|
118
118
|
/**
|
|
119
119
|
* How many consecutive failed builds before the seam stops rebuilding.
|
|
120
120
|
*
|
|
@@ -129,7 +129,7 @@ export const BOOTSTRAP_LOAD_TIMEOUT_MS = 10_000;
|
|
|
129
129
|
* the demand fails OPEN immediately, and `session_start` re-arms the latch so
|
|
130
130
|
* a repaired environment is never locked out for the life of the process.
|
|
131
131
|
*/
|
|
132
|
-
|
|
132
|
+
const BOOTSTRAP_FAILURE_STRIKE_LIMIT = 3;
|
|
133
133
|
/**
|
|
134
134
|
* A stand-in for an analysis client whose module failed to load (an unresolved
|
|
135
135
|
* runtime dependency under a package-manager layout the resolver can't traverse
|
|
@@ -268,7 +268,7 @@ async function buildBootstrapClients() {
|
|
|
268
268
|
* {@link requestBootstrapClients} never says which on its own, so the reason
|
|
269
269
|
* travels in the bounded record and the ledger entry instead.
|
|
270
270
|
*/
|
|
271
|
-
|
|
271
|
+
const BOOTSTRAP_UNAVAILABLE_REASONS = [
|
|
272
272
|
"shutdown",
|
|
273
273
|
"timeout",
|
|
274
274
|
"aborted",
|
|
@@ -276,7 +276,7 @@ export const BOOTSTRAP_UNAVAILABLE_REASONS = [
|
|
|
276
276
|
"latched",
|
|
277
277
|
];
|
|
278
278
|
/** A demand that could not be served. Carries the discriminating reason. */
|
|
279
|
-
|
|
279
|
+
class BootstrapUnavailableError extends Error {
|
|
280
280
|
unavailableReason;
|
|
281
281
|
constructor(unavailableReason, message) {
|
|
282
282
|
super(message);
|
|
@@ -194,7 +194,7 @@ export function acquireBoundedPidFileLock(lockPath, options) {
|
|
|
194
194
|
options.logContention();
|
|
195
195
|
return null;
|
|
196
196
|
}
|
|
197
|
-
throw new Error(options.timeoutMessage);
|
|
197
|
+
throw new Error(options.timeoutMessage, { cause: error });
|
|
198
198
|
}
|
|
199
199
|
Atomics.wait(waitArray, 0, 0, options.retryMs);
|
|
200
200
|
}
|
|
@@ -171,11 +171,6 @@ export const BOUNDED_TELEMETRY_PHASES = [
|
|
|
171
171
|
*/
|
|
172
172
|
"observed_handled_evicted",
|
|
173
173
|
];
|
|
174
|
-
const REGISTERED_PHASES = new Set(BOUNDED_TELEMETRY_PHASES);
|
|
175
|
-
/** Report whether a phase name is in the registry (used by the sweep). */
|
|
176
|
-
export function isBoundedTelemetryPhase(phase) {
|
|
177
|
-
return REGISTERED_PHASES.has(phase);
|
|
178
|
-
}
|
|
179
174
|
/**
|
|
180
175
|
* Per-turn admission counters, keyed by turn and phase. Multiple turns remain
|
|
181
176
|
* live because async work can finish T1 after T2 has already emitted. The map
|
|
@@ -22,8 +22,7 @@
|
|
|
22
22
|
* This module gives bus events the same durable trace every other pi-lens
|
|
23
23
|
* subsystem already has (latency.log, cascade.log, read-guard.log, ...) —
|
|
24
24
|
* see clients/latency-logger.ts for the house pattern this mirrors exactly:
|
|
25
|
-
* one shared `createNdjsonLogger` writer, `isTestMode()` no-op guard
|
|
26
|
-
* `getBusEventsLogPath()` for testability.
|
|
25
|
+
* one shared `createNdjsonLogger` writer, `isTestMode()` no-op guard.
|
|
27
26
|
*
|
|
28
27
|
* Logging volume: `emitted` and `emit_failed` are logged on every call —
|
|
29
28
|
* they're the two outcomes an operator actually needs a per-event trace for.
|
|
@@ -104,10 +103,3 @@ export function emitBusEventRollupAtSessionEnd(cwd) {
|
|
|
104
103
|
}
|
|
105
104
|
resetBusEventRollupCounts();
|
|
106
105
|
}
|
|
107
|
-
export function getBusEventsLogPath() {
|
|
108
|
-
return BUS_EVENTS_LOG_FILE;
|
|
109
|
-
}
|
|
110
|
-
/** Resolve once all enqueued bus-event writes are on disk (tests/shutdown). */
|
|
111
|
-
export function flushBusEventsLog() {
|
|
112
|
-
return writer.flush();
|
|
113
|
-
}
|
|
@@ -91,7 +91,7 @@ const CHARS_PER_ESTIMATED_TOKEN = 4;
|
|
|
91
91
|
*/
|
|
92
92
|
export const DEFAULT_PROVIDER_CACHE_TTL_MS = 60_000;
|
|
93
93
|
const _providerCacheTtl = lazyEnvNumber("PI_LENS_PROVIDER_CACHE_TTL_MS", DEFAULT_PROVIDER_CACHE_TTL_MS);
|
|
94
|
-
|
|
94
|
+
const getProviderCacheTtlMs = _providerCacheTtl.get;
|
|
95
95
|
export const _resetProviderCacheTtlForTests = _providerCacheTtl._resetForTests;
|
|
96
96
|
/**
|
|
97
97
|
* A turn counts as "anomalously low" when it reads back less than half of what
|
|
@@ -1165,11 +1165,6 @@ export function resetCachePrefixObservation() {
|
|
|
1165
1165
|
prefixHashBySession.clear();
|
|
1166
1166
|
attributionBySession.clear();
|
|
1167
1167
|
}
|
|
1168
|
-
/** #2442 test-only membership reads, bypassing emitCacheUsageSummaryAtSessionEnd's
|
|
1169
|
-
* usageRecords>0 gate and log side effects. */
|
|
1170
|
-
export function _attributionKeyForTests(sessionId, sessionRole) {
|
|
1171
|
-
return attributionKey(sessionId, sessionRole);
|
|
1172
|
-
}
|
|
1173
1168
|
export function _attributionBySessionHasForTests(key) {
|
|
1174
1169
|
return attributionBySession.has(key);
|
|
1175
1170
|
}
|
|
@@ -177,7 +177,7 @@ export function parseTomlStringArray(content, key) {
|
|
|
177
177
|
* true`) never matches either — the literal key is immediately followed by
|
|
178
178
|
* `=`, not `.`.
|
|
179
179
|
*/
|
|
180
|
-
|
|
180
|
+
function parseTomlScalarString(content, key) {
|
|
181
181
|
if (content === undefined)
|
|
182
182
|
return undefined;
|
|
183
183
|
const match = content.match(new RegExp(`^[ \\t]*${key}[ \\t]*=[ \\t]*(?:"([^"]*)"|'([^']*)')`, "m"));
|
|
@@ -292,7 +292,7 @@ export function readCargoDependencyNames(content) {
|
|
|
292
292
|
* (`edition.workspace = true`) or the inline-table form
|
|
293
293
|
* (`edition = { workspace = true }`).
|
|
294
294
|
*/
|
|
295
|
-
|
|
295
|
+
function isTomlKeyWorkspaceInherited(content, key) {
|
|
296
296
|
if (content === undefined)
|
|
297
297
|
return false;
|
|
298
298
|
const dotted = new RegExp(`^[ \\t]*${key}\\.workspace[ \\t]*=[ \\t]*true`, "m");
|
|
@@ -29,10 +29,3 @@ export function logCascade(entry) {
|
|
|
29
29
|
filePath: normalizeLoggedPath(entry.filePath),
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
|
-
export function getCascadeLogPath() {
|
|
33
|
-
return CASCADE_LOG_FILE;
|
|
34
|
-
}
|
|
35
|
-
/** Resolve once all enqueued cascade writes are on disk (tests/shutdown). */
|
|
36
|
-
export function flushCascadeLog() {
|
|
37
|
-
return writer.flush();
|
|
38
|
-
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Shared plumbing for best-effort, fire-and-forget child-process spawns
|
|
3
3
|
* (shape 4 of the recurring-defect catalog in AGENTS.md: "a timer / promise /
|
|
4
4
|
* worker / child that outlives its one-shot settle"). Extracted from the
|
|
5
|
-
* orphan reaper's `unrefReaperChild
|
|
5
|
+
* orphan reaper's `unrefReaperChild` (#1153/#1160) into a
|
|
6
6
|
* shared, dependency-free module so every one-shot
|
|
7
7
|
* `spawn(..., { stdio: ["ignore","pipe",...] })` call site in the codebase —
|
|
8
8
|
* the reaper's enumeration/kill spawns AND the resource sampler's Windows
|
|
@@ -46,24 +46,6 @@ export function unrefChildAndPipes(child) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
49
|
-
* Spawn a best-effort, fire-and-forget child, accumulate its full stdout, and
|
|
50
|
-
* resolve with the collected text (empty string on a synchronous spawn
|
|
51
|
-
* failure or an `error` event). Consolidates the spawn → unref →
|
|
52
|
-
* pipe-stdout → `close` plumbing shared by every one-shot OS-process-table
|
|
53
|
-
* query in the codebase — each caller supplies only its command/args/options
|
|
54
|
-
* and does its own output parse. The child + its stdio pipes are `unref`'d
|
|
55
|
-
* here (via `unrefChildAndPipes`) so a settled one-shot `pi --print` process
|
|
56
|
-
* can exit without waiting, and both the unref and the collect plumbing live
|
|
57
|
-
* in exactly ONE place rather than being re-derived at each spawn site.
|
|
58
|
-
* Never rejects — any failure resolves to `""`, which every caller's parse
|
|
59
|
-
* turns into an empty/absent result (the best-effort contract every caller
|
|
60
|
-
* here already has).
|
|
61
|
-
*/
|
|
62
|
-
export function spawnCollectStdout(command, args, options) {
|
|
63
|
-
return spawnCollectStdoutResult(command, args, options).then((result) => result.stdout);
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* `spawnCollectStdout` plus the reason the output is what it is. Same spawn,
|
|
67
49
|
* unref, collect, settle plumbing; the only additions are an optional hard
|
|
68
50
|
* timeout and a status discriminator. Never rejects.
|
|
69
51
|
*/
|
|
@@ -31,17 +31,20 @@
|
|
|
31
31
|
* (`loadPiLensProjectConfig`) are now projections of one resolution. #2416
|
|
32
32
|
* brings the `lsp.servers.<id>` shape the schema currently only reserves.
|
|
33
33
|
*/
|
|
34
|
-
export {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
export { SOURCE_TIERS } from "./provenance.js";
|
|
35
|
+
// `merge()` itself is NOT re-exported. Its input type only PROMISES a
|
|
36
|
+
// post-`validate()` value; nothing in the language enforces that a caller
|
|
37
|
+
// who imports it directly honors the promise, and `merge()`'s own bounds are
|
|
38
|
+
// a narrow backstop, not a second validator (see `merge.ts`'s module doc).
|
|
39
|
+
// `resolveConfig` (re-exported at the bottom of this file, from `resolve.js`)
|
|
40
|
+
// is the one supported way in: it always validates
|
|
41
|
+
// every source before merging. `merge()` stays exported from `merge.ts`
|
|
42
|
+
// itself — marked `@internal` there — for this module's own use and for
|
|
43
|
+
// tests that probe it directly.
|
|
41
44
|
// `resolveConfig` and its types live in `./resolve.js` (#2426) so a caller can
|
|
42
45
|
// import the front door without also importing this barrel's width — in
|
|
43
46
|
// particular `process-spec.js` -> `project-trust.js`, which closed three
|
|
44
47
|
// import cycles once the config loaders (all downstream of `file-utils.ts`)
|
|
45
48
|
// started resolving through the core. The public surface is unchanged: this is
|
|
46
49
|
// still where the supported entry point is exported from.
|
|
47
|
-
export { resolveConfig
|
|
50
|
+
export { resolveConfig } from "./resolve.js";
|
|
@@ -36,7 +36,7 @@ import { compareKeys, isRepoTier, } from "./provenance.js";
|
|
|
36
36
|
/** Longest argv a spec may carry. A command line, not a file list. */
|
|
37
37
|
export const MAX_ARGV_ENTRIES = 128;
|
|
38
38
|
/** Total argv bytes a spec may carry. */
|
|
39
|
-
|
|
39
|
+
const MAX_ARGV_BYTES = 32 * 1024;
|
|
40
40
|
/** Most env entries a spec may carry. */
|
|
41
41
|
export const MAX_ENV_ENTRIES = 64;
|
|
42
42
|
/** Total env bytes (names plus values) a spec may carry. */
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Two orderings live here, and they are NOT the same ordering:
|
|
13
13
|
*
|
|
14
|
-
* - `
|
|
14
|
+
* - `SOURCE_TIERS` decides who wins a plain value. Later beats earlier, so a
|
|
15
15
|
* CLI flag beats a project file beats a global file beats a built-in default.
|
|
16
16
|
* - `TIER_CLASS` decides who may LIFT a deny. `deny.ts` builds monotonic deny
|
|
17
17
|
* precedence on this split, and nothing else re-derives it.
|
|
@@ -34,13 +34,6 @@ export const SOURCE_TIERS = [
|
|
|
34
34
|
"cli",
|
|
35
35
|
"host",
|
|
36
36
|
];
|
|
37
|
-
/**
|
|
38
|
-
* Value precedence, lowest first. A later tier's value replaces an earlier
|
|
39
|
-
* tier's value for the same leaf. `merge()` sorts its sources by this order
|
|
40
|
-
* rather than trusting caller order, so a caller that assembles sources in a
|
|
41
|
-
* different order cannot silently invert precedence.
|
|
42
|
-
*/
|
|
43
|
-
export const TIER_PRECEDENCE = SOURCE_TIERS;
|
|
44
37
|
/**
|
|
45
38
|
* Who each tier speaks for. THREE classes, not two (#2440 review finding F3).
|
|
46
39
|
*
|
|
@@ -79,7 +72,7 @@ export const TIER_CLASS = {
|
|
|
79
72
|
};
|
|
80
73
|
/** Numeric precedence of a tier. Higher wins. */
|
|
81
74
|
export function tierPrecedence(tier) {
|
|
82
|
-
return
|
|
75
|
+
return SOURCE_TIERS.indexOf(tier);
|
|
83
76
|
}
|
|
84
77
|
/** True for tiers whose content arrives with the checkout. */
|
|
85
78
|
export function isRepoTier(tier) {
|
|
@@ -101,7 +101,7 @@ export function jsonTypeName(value) {
|
|
|
101
101
|
* A list with no records cannot overflow, so this is the floor under an
|
|
102
102
|
* anchorless producer rather than a case production reaches.
|
|
103
103
|
*/
|
|
104
|
-
|
|
104
|
+
const UNANCHORED_RECORD_LABEL = "(config resolution)";
|
|
105
105
|
/**
|
|
106
106
|
* A bounded collector for one resolution.
|
|
107
107
|
*
|
|
@@ -21,8 +21,6 @@
|
|
|
21
21
|
*
|
|
22
22
|
* Pure accessors only. Nothing here reads a file, and nothing holds state.
|
|
23
23
|
*/
|
|
24
|
-
import { STABILITY_TIER_KEY } from "../config-diagnostic-codes.js";
|
|
25
|
-
export { STABILITY_TIER_KEY };
|
|
26
24
|
/** Narrows to the recursive domain type, not to `unknown`. */
|
|
27
25
|
export function isConfigObject(value) {
|
|
28
26
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -30,7 +28,7 @@ export function isConfigObject(value) {
|
|
|
30
28
|
// --- x-merge-strategy ---
|
|
31
29
|
/** The annotation key selecting how an array node combines across tiers. */
|
|
32
30
|
export const MERGE_STRATEGY_KEY = "x-merge-strategy";
|
|
33
|
-
|
|
31
|
+
const DEFAULT_MERGE_STRATEGY = "replace";
|
|
34
32
|
const KEYED_PREFIX = "keyed:";
|
|
35
33
|
export function isMergeStrategy(value) {
|
|
36
34
|
if (typeof value !== "string")
|
|
@@ -71,7 +69,7 @@ export const DENY_KEY = "x-deny";
|
|
|
71
69
|
* the field someone spelled differently, and silently claims one that merely
|
|
72
70
|
* looks the part. A schema says what it means.
|
|
73
71
|
*/
|
|
74
|
-
|
|
72
|
+
const DENY_POLICIES = ["boolean-false", "array-union"];
|
|
75
73
|
export function isDenyPolicy(value) {
|
|
76
74
|
return (typeof value === "string" &&
|
|
77
75
|
DENY_POLICIES.includes(value));
|
|
@@ -104,7 +102,7 @@ export function schemaType(node) {
|
|
|
104
102
|
* field while the merger replaced it whole, so the same schema typo produced
|
|
105
103
|
* two different merge semantics.
|
|
106
104
|
*/
|
|
107
|
-
|
|
105
|
+
const SCHEMA_TYPES = [
|
|
108
106
|
"object",
|
|
109
107
|
"array",
|
|
110
108
|
"string",
|
|
@@ -34,7 +34,7 @@ export const STABILITY_TIER_KEY = "x-stability";
|
|
|
34
34
|
* The closed tier vocabulary. `experimental` fields may change or be removed
|
|
35
35
|
* in a minor; `stable` fields are covered by the compatibility guarantee.
|
|
36
36
|
*/
|
|
37
|
-
|
|
37
|
+
const STABILITY_TIERS = ["experimental", "stable"];
|
|
38
38
|
export function isStabilityTier(value) {
|
|
39
39
|
return (typeof value === "string" &&
|
|
40
40
|
STABILITY_TIERS.includes(value));
|