@herbertgao/pi-extensions 2026.9.6 → 2026.9.7
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/README.md +3 -8
- package/THIRD_PARTY_NOTICES.md +0 -4
- package/node_modules/@narumitw/pi-btw/README.md +2 -0
- package/node_modules/@narumitw/pi-btw/dist/index.ts +284 -365
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
- package/node_modules/@narumitw/pi-btw/package.json +52 -53
- package/node_modules/@narumitw/pi-btw/src/bring-to-main.ts +494 -547
- package/node_modules/@narumitw/pi-btw/src/btw.ts +814 -856
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +673 -674
- package/node_modules/@narumitw/pi-btw/src/keybindings.ts +228 -270
- package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +309 -318
- package/node_modules/@narumitw/pi-btw/src/menu.ts +416 -454
- package/node_modules/@narumitw/pi-btw/src/settings.ts +203 -219
- package/node_modules/@narumitw/pi-btw/src/side-thread.ts +173 -201
- package/node_modules/@narumitw/pi-btw/src/text.ts +21 -23
- package/node_modules/@narumitw/pi-btw/src/transcript-markdown.ts +65 -0
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +611 -662
- package/node_modules/pi-lens/CHANGELOG.md +239 -0
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +3 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +30 -4
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +2 -0
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +26 -0
- package/node_modules/pi-lens/dist/clients/biome-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +403 -35
- package/node_modules/pi-lens/dist/clients/bootstrap.js +6 -1
- package/node_modules/pi-lens/dist/clients/cascade-format.js +200 -4
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +10 -2
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +16 -1
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +12 -1
- package/node_modules/pi-lens/dist/clients/dispatch/ast-grep-catalog.js +65 -0
- package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +28 -0
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +9 -2
- package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +222 -0
- package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +389 -13
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +175 -25
- package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +62 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +21 -41
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +6 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +7 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -12
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +2 -21
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/types.js +4 -1
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +1 -0
- package/node_modules/pi-lens/dist/clients/effective-config.js +1 -1
- package/node_modules/pi-lens/dist/clients/file-kinds.js +26 -1
- package/node_modules/pi-lens/dist/clients/file-role.js +21 -21
- package/node_modules/pi-lens/dist/clients/file-utils.js +156 -10
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +56 -9
- package/node_modules/pi-lens/dist/clients/format-service.js +39 -21
- package/node_modules/pi-lens/dist/clients/formatters.js +60 -13
- package/node_modules/pi-lens/dist/clients/generation-guard.js +2 -2
- package/node_modules/pi-lens/dist/clients/git-guard.js +11 -3
- package/node_modules/pi-lens/dist/clients/indent-retarget.js +78 -8
- package/node_modules/pi-lens/dist/clients/installer/index.js +333 -114
- package/node_modules/pi-lens/dist/clients/instance-reaper-state.js +7 -0
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +20 -10
- package/node_modules/pi-lens/dist/clients/language-profile.js +23 -1
- package/node_modules/pi-lens/dist/clients/language-registry.js +5 -1
- package/node_modules/pi-lens/dist/clients/latency-logger.js +4 -1
- package/node_modules/pi-lens/dist/clients/lens-engine.js +12 -3
- package/node_modules/pi-lens/dist/clients/lens-events.js +2 -1
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +79 -0
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/client.js +25 -21
- package/node_modules/pi-lens/dist/clients/lsp/config.js +2 -2
- package/node_modules/pi-lens/dist/clients/lsp/index.js +7 -0
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +16 -4
- package/node_modules/pi-lens/dist/clients/lsp/server.js +19 -2
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +5 -3
- package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +12 -8
- package/node_modules/pi-lens/dist/clients/mcp/session.js +36 -4
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +24 -7
- package/node_modules/pi-lens/dist/clients/observed-mutation.js +40 -2
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +23 -3
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +110 -15
- package/node_modules/pi-lens/dist/clients/path-utils.js +123 -3
- package/node_modules/pi-lens/dist/clients/performance-report.js +5 -1
- package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +1 -1
- package/node_modules/pi-lens/dist/clients/pipeline.js +74 -5
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +37 -5
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +145 -18
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +4 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +59 -3
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +45 -2
- package/node_modules/pi-lens/dist/clients/read-guard.js +70 -11
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +147 -9
- package/node_modules/pi-lens/dist/clients/ruff-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +33 -5
- package/node_modules/pi-lens/dist/clients/runtime-context.js +6 -2
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +77 -7
- package/node_modules/pi-lens/dist/clients/runtime-session.js +21 -2
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +43 -12
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +330 -85
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +147 -17
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +258 -5
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +6 -0
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +50 -5
- package/node_modules/pi-lens/dist/clients/sg-runner.js +23 -11
- package/node_modules/pi-lens/dist/clients/sgconfig.js +79 -2
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +6 -41
- package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +43 -0
- package/node_modules/pi-lens/dist/clients/tool-agreement.js +423 -0
- package/node_modules/pi-lens/dist/clients/tool-config.js +50 -0
- package/node_modules/pi-lens/dist/clients/tool-cwd.js +47 -76
- package/node_modules/pi-lens/dist/clients/tool-policy.js +163 -7
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +120 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +127 -189
- package/node_modules/pi-lens/dist/clients/widget-state.js +98 -0
- package/node_modules/pi-lens/dist/clients/word-index.js +5 -2
- package/node_modules/pi-lens/dist/clients/write-ordering-guard.js +7 -0
- package/node_modules/pi-lens/dist/index.js +27416 -24153
- package/node_modules/pi-lens/dist/mcp/server.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +7 -1
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +168 -53
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +244 -72
- package/node_modules/pi-lens/docs/audit1.md +5 -5
- package/node_modules/pi-lens/docs/dependencies.md +11 -0
- package/node_modules/pi-lens/docs/dispositions.md +52 -0
- package/node_modules/pi-lens/docs/environment-variables.md +2 -2
- package/node_modules/pi-lens/docs/features.md +4 -4
- package/node_modules/pi-lens/docs/globalconfig.md +3 -1
- package/node_modules/pi-lens/docs/language-coverage.md +1 -0
- package/node_modules/pi-lens/docs/pi-lens-fixer.md +53 -94
- package/node_modules/pi-lens/docs/pi-lens-investigator.md +43 -38
- package/node_modules/pi-lens/docs/pi-lens-retro.md +97 -0
- package/node_modules/pi-lens/docs/pi-lens-reviewer.md +53 -66
- package/node_modules/pi-lens/docs/release-qa-baseline.md +22 -0
- package/node_modules/pi-lens/docs/settings.md +7 -4
- package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +1 -1
- package/node_modules/pi-lens/docs/usage.md +2 -0
- package/node_modules/pi-lens/package.json +1 -1
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +24 -12
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +40 -0
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +15 -0
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +7 -0
- package/package.json +5 -11
- package/node_modules/@luxusai/pi-hindsight/CHANGELOG.md +0 -376
- package/node_modules/@luxusai/pi-hindsight/README.md +0 -92
- package/node_modules/@luxusai/pi-hindsight/docs/adr/001-memory-lifecycle-and-scope.md +0 -56
- package/node_modules/@luxusai/pi-hindsight/docs/adr/002-explicit-routing-strategy-seam.md +0 -169
- package/node_modules/@luxusai/pi-hindsight/docs/adr/003-tui-memory-mode-vocabulary.md +0 -107
- package/node_modules/@luxusai/pi-hindsight/docs/adr/004-lifeos-dual-bank-design.md +0 -89
- package/node_modules/@luxusai/pi-hindsight/docs/adr/005-domain-banks-and-agent-first-surface.md +0 -189
- package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark.webp +0 -0
- package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark@2x.webp +0 -0
- package/node_modules/@luxusai/pi-hindsight/docs/coding-memory-evaluation.md +0 -54
- package/node_modules/@luxusai/pi-hindsight/docs/compatibility.md +0 -75
- package/node_modules/@luxusai/pi-hindsight/docs/hindsight-core-functions.md +0 -300
- package/node_modules/@luxusai/pi-hindsight/docs/mission-and-mental-model-quality.md +0 -158
- package/node_modules/@luxusai/pi-hindsight/docs/next-opt-out-design.md +0 -164
- package/node_modules/@luxusai/pi-hindsight/docs/risky-memory-modes.md +0 -139
- package/node_modules/@luxusai/pi-hindsight/docs/starter-mental-model-suggestions.md +0 -74
- package/node_modules/@luxusai/pi-hindsight/docs/surface-reference.md +0 -261
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-operations.ts +0 -151
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-selection.ts +0 -18
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-settings-presenter.ts +0 -46
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-templates.ts +0 -383
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/banking.ts +0 -240
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/knowledge-page-seed.ts +0 -176
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/retain-strategies.ts +0 -178
- package/node_modules/@luxusai/pi-hindsight/extensions/client/client-retry.ts +0 -41
- package/node_modules/@luxusai/pi-hindsight/extensions/client/client.ts +0 -386
- package/node_modules/@luxusai/pi-hindsight/extensions/client/fetch-compat.ts +0 -39
- package/node_modules/@luxusai/pi-hindsight/extensions/client/timeout.ts +0 -34
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-defaults.ts +0 -140
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-model.ts +0 -61
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-registry.ts +0 -925
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-field-paths.ts +0 -247
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-normalize.ts +0 -547
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-writer.ts +0 -480
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config.ts +0 -182
- package/node_modules/@luxusai/pi-hindsight/extensions/config/setup-gate.ts +0 -86
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-execute.ts +0 -1010
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-parse.ts +0 -175
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-plan.ts +0 -425
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-presentation.ts +0 -210
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-sessions.ts +0 -817
- package/node_modules/@luxusai/pi-hindsight/extensions/index.ts +0 -25
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/git-seed.ts +0 -319
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-recall.ts +0 -196
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-retain.ts +0 -189
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-runtime.ts +0 -65
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle.ts +0 -260
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/mental-models.ts +0 -244
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/observation-scopes.ts +0 -59
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-cleanup.ts +0 -82
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-visibility.ts +0 -45
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall.ts +0 -331
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-cursor.ts +0 -354
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-job-builder.ts +0 -66
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-receipts.ts +0 -132
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain.ts +0 -228
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-bank-template-operations.ts +0 -74
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-config-operations.ts +0 -16
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-control-operations.ts +0 -648
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-diagnostics-operations.ts +0 -105
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-identity.ts +0 -68
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-service.ts +0 -71
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-types.ts +0 -13
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-recall-operations.ts +0 -162
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-retain-operations.ts +0 -131
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-scope.ts +0 -104
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-session-operations.ts +0 -37
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/operation-catalog.ts +0 -893
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/reflect-presenter.ts +0 -35
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/scope-migrate.ts +0 -177
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/tools.ts +0 -7
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/flush-presenter.ts +0 -21
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/jsonl-queue-store.ts +0 -112
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-delivery.ts +0 -111
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-lock.ts +0 -214
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-operations.ts +0 -70
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue.ts +0 -392
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/bank-template-presentation.ts +0 -45
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/commands.ts +0 -9
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/guided-setup.ts +0 -860
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/prefill-input.ts +0 -105
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-flow.ts +0 -189
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-server-probe.ts +0 -299
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-actions.ts +0 -201
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-facts.ts +0 -47
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-render.ts +0 -239
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-types.ts +0 -50
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui.ts +0 -259
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/tool-presenters.ts +0 -77
- package/node_modules/@luxusai/pi-hindsight/extensions/types.ts +0 -534
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/diagnostics.ts +0 -319
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/messages.ts +0 -325
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/sanitize.ts +0 -42
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-memory-meta.ts +0 -244
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-operations.ts +0 -56
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/session.ts +0 -50
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-fields.ts +0 -167
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-health.ts +0 -207
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/status.ts +0 -120
- package/node_modules/@luxusai/pi-hindsight/extensions/version.ts +0 -8
- package/node_modules/@luxusai/pi-hindsight/package.json +0 -118
- package/node_modules/@luxusai/pi-hindsight/skills/hindsight-memory-doctor/SKILL.md +0 -75
|
@@ -15,7 +15,7 @@ import * as os from "node:os";
|
|
|
15
15
|
import * as path from "node:path";
|
|
16
16
|
import { BoundedLruCache } from "./bounded-cache.js";
|
|
17
17
|
import { createGenerationSource } from "./generation-guard.js";
|
|
18
|
-
import { normalizeMapKey } from "./path-utils.js";
|
|
18
|
+
import { findNearestContaining, normalizeMapKey } from "./path-utils.js";
|
|
19
19
|
import { resolveToolCwd } from "./tool-cwd.js";
|
|
20
20
|
import { resolveCargoPackageEdition } from "./cargo-manifest.js";
|
|
21
21
|
import { resolveKtfmtGradleStyle } from "./gradle-ktfmt-style.js";
|
|
@@ -28,6 +28,8 @@ import { compareOrdinal } from "./string-utils.js";
|
|
|
28
28
|
import { classifyProbeFailure, createAvailabilityLatch, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
|
|
29
29
|
import { findGlobalBinary, findLocalBinUpwards, VENDOR_BIN_DIRS, VENV_BIN_DIRS, } from "./package-manager.js";
|
|
30
30
|
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
31
|
+
import { establishToolAgreement } from "./tool-agreement.js";
|
|
32
|
+
import { recordDegradationOnce } from "./degradation-ledger.js";
|
|
31
33
|
import { probeToolAsync } from "./tool-probe.js";
|
|
32
34
|
import { assertInstallAllowed } from "./project-trust.js";
|
|
33
35
|
import { tryLazyInstallForFormatter } from "./dispatch/runners/utils/lazy-installer.js";
|
|
@@ -71,6 +73,7 @@ export const SKIP_FORMATTING = "skip-formatting";
|
|
|
71
73
|
* producer collapsed into an ordinary failed result).
|
|
72
74
|
*/
|
|
73
75
|
export const FORMATTER_UNAVAILABLE = "formatter-unavailable";
|
|
76
|
+
export const FORMATTER_AGREEMENT_UNAVAILABLE = "formatter-agreement-unavailable";
|
|
74
77
|
// --- Utility Functions ---
|
|
75
78
|
async function fileExists(filePath) {
|
|
76
79
|
try {
|
|
@@ -464,11 +467,17 @@ async function canUseBundleExec(cwd) {
|
|
|
464
467
|
async function resolveManagedSmartDefaultCommand(formatterName, filePath, args) {
|
|
465
468
|
const toolId = getAutoInstallToolIdForFormatter(formatterName);
|
|
466
469
|
if (!toolId)
|
|
467
|
-
return
|
|
470
|
+
return FORMATTER_UNAVAILABLE;
|
|
468
471
|
const { ensureTool } = await import("./installer/index.js");
|
|
469
472
|
const installed = await ensureTool(toolId);
|
|
470
|
-
if (!installed)
|
|
471
|
-
|
|
473
|
+
if (!installed) {
|
|
474
|
+
recordDegradationOnce({
|
|
475
|
+
kind: "formatter-unavailable",
|
|
476
|
+
subject: formatterName,
|
|
477
|
+
reason: "managed formatter lookup and installation returned no executable",
|
|
478
|
+
});
|
|
479
|
+
return FORMATTER_UNAVAILABLE;
|
|
480
|
+
}
|
|
472
481
|
return [installed, ...args, filePath];
|
|
473
482
|
}
|
|
474
483
|
/** Resolve an explicitly selected formatter from the shared managed-tool seam. */
|
|
@@ -568,13 +577,7 @@ function hasExplicitFormatterConfig(formatterName, cwd, ext) {
|
|
|
568
577
|
export const FORMATTERS_WITH_EXPLICIT_CONFIG_CHECK = new Set(EXPLICIT_FORMATTER_CONFIG_CHECKS.keys());
|
|
569
578
|
// --- Formatter Definitions ---
|
|
570
579
|
async function hasEditorConfig(cwd) {
|
|
571
|
-
|
|
572
|
-
await fs.access(path.join(cwd, ".editorconfig"));
|
|
573
|
-
return true;
|
|
574
|
-
}
|
|
575
|
-
catch {
|
|
576
|
-
return false;
|
|
577
|
-
}
|
|
580
|
+
return findNearestContaining(cwd, [".editorconfig"]) !== undefined;
|
|
578
581
|
}
|
|
579
582
|
async function indentationArgs(filePath, tool, cwd) {
|
|
580
583
|
if ((await hasEditorConfig(cwd)) ||
|
|
@@ -592,6 +595,20 @@ async function indentationArgs(filePath, tool, cwd) {
|
|
|
592
595
|
if (!hasDetectableIndentation(content))
|
|
593
596
|
return null;
|
|
594
597
|
const indentation = detectIndentation(content);
|
|
598
|
+
if (!indentation) {
|
|
599
|
+
// #3038: the file HAS indentation, but only nested runs, so no unit can be
|
|
600
|
+
// proven. The caller turns this into SKIP_FORMATTING — a file the user
|
|
601
|
+
// expected to be formatted silently is not — so the refusal gets one
|
|
602
|
+
// bounded row. Subject is the tool (four possible values, recorded once
|
|
603
|
+
// per session each) rather than the path: a session that edits many such
|
|
604
|
+
// files must not write one ledger key per file.
|
|
605
|
+
recordDegradationOnce({
|
|
606
|
+
kind: "formatter-skip",
|
|
607
|
+
subject: tool,
|
|
608
|
+
reason: "indentation evidence is ambiguous; formatter style not pinned",
|
|
609
|
+
});
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
595
612
|
if (tool === "shfmt")
|
|
596
613
|
return indentation.style === "tab"
|
|
597
614
|
? ["-i", "0"]
|
|
@@ -634,7 +651,7 @@ export function resolveFormatterCwd(absolutePath, formatterName, homeDir = os.ho
|
|
|
634
651
|
// ceiling prevents it from escaping the user's workspace.
|
|
635
652
|
cwd: path.parse(path.resolve(absolutePath)).root,
|
|
636
653
|
homeDir,
|
|
637
|
-
});
|
|
654
|
+
}).cwd;
|
|
638
655
|
}
|
|
639
656
|
/**
|
|
640
657
|
* Every biome invocation must carry this. Biome exits 1 with "No files were
|
|
@@ -1123,6 +1140,18 @@ export const gleamFormatter = {
|
|
|
1123
1140
|
return false;
|
|
1124
1141
|
},
|
|
1125
1142
|
};
|
|
1143
|
+
export const typstyleFormatter = {
|
|
1144
|
+
name: "typstyle",
|
|
1145
|
+
command: ["typstyle", "-i", "$FILE"],
|
|
1146
|
+
extensions: [".typ", ".typc"],
|
|
1147
|
+
async resolveCommand(filePath) {
|
|
1148
|
+
const inPath = await which("typstyle");
|
|
1149
|
+
if (inPath)
|
|
1150
|
+
return [inPath, "-i", filePath];
|
|
1151
|
+
return resolveManagedSmartDefaultCommand("typstyle", filePath, ["-i"]);
|
|
1152
|
+
},
|
|
1153
|
+
detect: managedToolDetect("typstyle", undefined, async () => (await which("typstyle")) !== null),
|
|
1154
|
+
};
|
|
1126
1155
|
export const terraformFormatter = {
|
|
1127
1156
|
name: "terraform",
|
|
1128
1157
|
command: ["terraform", "fmt", "$FILE"],
|
|
@@ -1395,6 +1424,7 @@ export const ALL_FORMATTERS = [
|
|
|
1395
1424
|
rubocopFormatter,
|
|
1396
1425
|
standardrbFormatter,
|
|
1397
1426
|
gleamFormatter,
|
|
1427
|
+
typstyleFormatter,
|
|
1398
1428
|
taploFormatter,
|
|
1399
1429
|
googleJavaFormatFormatter,
|
|
1400
1430
|
cljfmtFormatter,
|
|
@@ -1829,6 +1859,15 @@ export function firstDiagnosticLine(text) {
|
|
|
1829
1859
|
* materialized.
|
|
1830
1860
|
*/
|
|
1831
1861
|
async function resolveFormatterCommand(formatter, absolutePath, cwd) {
|
|
1862
|
+
const agreement = establishToolAgreement(formatter.name, cwd);
|
|
1863
|
+
if (agreement.decision === "decline") {
|
|
1864
|
+
recordDegradationOnce({
|
|
1865
|
+
kind: "formatter-agreement-unavailable",
|
|
1866
|
+
subject: agreement.subject,
|
|
1867
|
+
reason: agreement.reason,
|
|
1868
|
+
});
|
|
1869
|
+
return FORMATTER_AGREEMENT_UNAVAILABLE;
|
|
1870
|
+
}
|
|
1832
1871
|
const resolved = formatter.resolveCommand
|
|
1833
1872
|
? await formatter.resolveCommand(absolutePath, cwd)
|
|
1834
1873
|
: null;
|
|
@@ -1859,7 +1898,7 @@ export async function formatFile(filePath, formatter) {
|
|
|
1859
1898
|
try {
|
|
1860
1899
|
const absolutePath = path.resolve(filePath);
|
|
1861
1900
|
const cwd = path.dirname(absolutePath);
|
|
1862
|
-
const formatterCwd =
|
|
1901
|
+
const formatterCwd = resolveToolCwd("formatter", formatter.name, absolutePath, { cwd: path.parse(absolutePath).root }).cwd;
|
|
1863
1902
|
const contentBefore = await fs.readFile(absolutePath, "utf-8");
|
|
1864
1903
|
// Resolve command: prefer local (venv/vendor/node_modules) over global.
|
|
1865
1904
|
// The shared seam must honor SKIP_FORMATTING before selecting the static
|
|
@@ -1882,6 +1921,14 @@ export async function formatFile(filePath, formatter) {
|
|
|
1882
1921
|
error: `${formatter.name}: formatter executable not found`,
|
|
1883
1922
|
};
|
|
1884
1923
|
}
|
|
1924
|
+
if (cmd === FORMATTER_AGREEMENT_UNAVAILABLE) {
|
|
1925
|
+
return {
|
|
1926
|
+
success: true,
|
|
1927
|
+
changed: false,
|
|
1928
|
+
outcome: "unavailable",
|
|
1929
|
+
error: `${formatter.name}: project tool agreement could not be established`,
|
|
1930
|
+
};
|
|
1931
|
+
}
|
|
1885
1932
|
// Run formatter without blocking the event loop.
|
|
1886
1933
|
const result = await safeSpawnAsync(cmd[0], cmd.slice(1), {
|
|
1887
1934
|
timeout: 15000,
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
* keyed map it is a compare while nothing has been invalidated, and a
|
|
46
46
|
* normalize plus one `Map.get` once something has. The hand-rolled form it
|
|
47
47
|
* replaces normalized on EVERY call, and this repo's normalizer runs
|
|
48
|
-
* `realpathSync.native` on Windows,
|
|
49
|
-
* syscall once rather than once per file. See
|
|
48
|
+
* `realpathSync.native` on Windows and, since #3098, on POSIX as well, so an
|
|
49
|
+
* uninterrupted sweep pays that syscall once rather than once per file. See
|
|
50
50
|
* `GenerationMapOptions.normalizeKey` for the correctness reason the slow
|
|
51
51
|
* path exists at all.
|
|
52
52
|
*/
|
|
@@ -3,6 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import { isPathIgnoredByProject } from "./file-utils.js";
|
|
4
4
|
import { tokenizeShellCommand } from "./bash-file-access.js";
|
|
5
5
|
import { logLatency } from "./latency-logger.js";
|
|
6
|
+
import { resolveLensToolName } from "./tool-config.js";
|
|
6
7
|
import { advisoryFileHash, advisoryPathKey, MAX_ADVISORY_AFFECTED_FILES, snapshotAdvisoryProvenance, } from "./advisory-provenance.js";
|
|
7
8
|
function resolveGuardPath(filePath, cwd) {
|
|
8
9
|
return path.resolve(cwd, filePath);
|
|
@@ -819,7 +820,14 @@ export function clearGitGuardTestFailure(cacheManager, cwd, runtime, passedFiles
|
|
|
819
820
|
fileContentHashes: {},
|
|
820
821
|
});
|
|
821
822
|
}
|
|
822
|
-
export function evaluateGitGuard(runtime, cacheManager, cwd) {
|
|
823
|
+
export function evaluateGitGuard(runtime, cacheManager, cwd, host = "pi") {
|
|
824
|
+
// #2535 F3: a known tool with no mapping on this host resolves to
|
|
825
|
+
// undefined — rephrase rather than naming a dead tool. `lens_diagnostics`
|
|
826
|
+
// is pinned available on both hosts, so the fallback is defensive only.
|
|
827
|
+
const diagnosticsTool = resolveLensToolName("lens_diagnostics", host);
|
|
828
|
+
const inspectLine = diagnosticsTool
|
|
829
|
+
? `Run ${diagnosticsTool} mode=all for full details, then commit again.`
|
|
830
|
+
: "Inspect the full diagnostics, then commit again.";
|
|
823
831
|
if (runtime.gitGuardHasBlockers) {
|
|
824
832
|
logDecision(cwd, "blocked", "runtime_blockers", {
|
|
825
833
|
projectSeq: runtime.projectSeq,
|
|
@@ -829,7 +837,7 @@ export function evaluateGitGuard(runtime, cacheManager, cwd) {
|
|
|
829
837
|
: "";
|
|
830
838
|
return {
|
|
831
839
|
block: true,
|
|
832
|
-
reason: `🔴 COMMIT BLOCKED (--lens-guard): unresolved blockers must be fixed before commit/push.${detail}\
|
|
840
|
+
reason: `🔴 COMMIT BLOCKED (--lens-guard): unresolved blockers must be fixed before commit/push.${detail}\n${inspectLine}`,
|
|
833
841
|
};
|
|
834
842
|
}
|
|
835
843
|
if (runtime.gitGuardCacheUnknownReason) {
|
|
@@ -910,6 +918,6 @@ export function evaluateGitGuard(runtime, cacheManager, cwd) {
|
|
|
910
918
|
});
|
|
911
919
|
return {
|
|
912
920
|
block: true,
|
|
913
|
-
reason:
|
|
921
|
+
reason: `🔴 COMMIT BLOCKED (--lens-guard): unresolved blockers must be fixed before commit/push.\n${inspectLine}`,
|
|
914
922
|
};
|
|
915
923
|
}
|
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
import { blockCommentInteriorMask, templateLiteralInteriorMask, } from "./dispatch/indent-detect.js";
|
|
2
|
+
/**
|
|
3
|
+
* Computes both interior masks for `oldLines`, anchored to the real file
|
|
4
|
+
* when `fileContext` resolves cleanly, falling back to the fragment alone
|
|
5
|
+
* otherwise. `blockCommentInteriorMask` has no fragment-ambiguity to correct
|
|
6
|
+
* (a lone `*\/` cannot be mistaken for an opener the way a lone backtick
|
|
7
|
+
* can), so it always runs file-anchored when a file is available — this is
|
|
8
|
+
* about `templateLiteralInteriorMask` specifically, but both take the same
|
|
9
|
+
* path for one lexing pass over consistent line numbers.
|
|
10
|
+
*/
|
|
11
|
+
function computeInteriorMasks(oldLines, fileContext) {
|
|
12
|
+
if (fileContext) {
|
|
13
|
+
const fileLines = fileContext.content.replace(/\r\n/g, "\n").split("\n");
|
|
14
|
+
const start = fileContext.startLine - 1;
|
|
15
|
+
const end = start + oldLines.length;
|
|
16
|
+
if (start >= 0 && end <= fileLines.length) {
|
|
17
|
+
return {
|
|
18
|
+
commentInterior: blockCommentInteriorMask(fileLines).slice(start, end),
|
|
19
|
+
templateInterior: templateLiteralInteriorMask(fileLines).slice(start, end),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// startLine/length don't fit inside content — the caller resolved a
|
|
23
|
+
// stale or mismatched range; fall through to the fragment-only path
|
|
24
|
+
// rather than slicing out of bounds.
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
commentInterior: blockCommentInteriorMask(oldLines),
|
|
28
|
+
templateInterior: templateLiteralInteriorMask(oldLines),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
1
31
|
/**
|
|
2
32
|
* Retargets the leading-whitespace style of newText to match the indentation
|
|
3
33
|
* correction that was applied to oldText.
|
|
@@ -7,39 +37,79 @@
|
|
|
7
37
|
* Returns undefined — leaving newText unchanged — when any non-blank line in
|
|
8
38
|
* newText has indentation that cannot be resolved, to avoid producing
|
|
9
39
|
* mixed-indentation output.
|
|
40
|
+
*
|
|
41
|
+
* A line inside a terminated `/* … *\/` block comment, or inside a
|
|
42
|
+
* multi-line template literal, still gets an exact entry in the map — a
|
|
43
|
+
* replacement that reintroduces that SAME indent (adding another JSDoc
|
|
44
|
+
* continuation, or another template line, say) still resolves by direct
|
|
45
|
+
* lookup — but that indent is never eligible to be picked as the shortest
|
|
46
|
+
* ("base") unit and extrapolated to a deeper level `newText` adds that
|
|
47
|
+
* oldText never showed: its leading space is alignment (on the comment
|
|
48
|
+
* opener's `*` column, or on the template string's own content), not a
|
|
49
|
+
* nesting unit, and that alignment's ratio can differ from the code's own
|
|
50
|
+
* indentation ratio, silently mis-scaling every such deeper line (#3052,
|
|
51
|
+
* #3116). Uses the same lexer as `clients/dispatch/indent-detect.ts`'s
|
|
52
|
+
* `detectIndentation` (#3039, #3059) rather than a second one.
|
|
53
|
+
*
|
|
54
|
+
* `fileContext`, when the caller has it, anchors that lexer to the real file
|
|
55
|
+
* (see {@link IndentRetargetFileContext}) so a template boundary the
|
|
56
|
+
* oldText fragment crosses is read correctly instead of ambiguously. Without
|
|
57
|
+
* it (match position unknown — the caller never found a unique file span,
|
|
58
|
+
* or is calling with a synthetic fragment that has no backing file), the
|
|
59
|
+
* masks fall back to running over the fragment alone, which is exact for a
|
|
60
|
+
* block comment (`*\/` cannot be mistaken for an opener) but can misread a
|
|
61
|
+
* template literal whose opener or closer lies outside the fragment.
|
|
10
62
|
*/
|
|
11
|
-
export function retargetReplacementIndentation(newText, oldText, correctedOldText) {
|
|
63
|
+
export function retargetReplacementIndentation(newText, oldText, correctedOldText, fileContext) {
|
|
12
64
|
const newline = newText.includes("\r\n") ? "\r\n" : "\n";
|
|
13
65
|
const oldLines = oldText.replace(/\r\n/g, "\n").split("\n");
|
|
14
66
|
const correctedLines = correctedOldText.replace(/\r\n/g, "\n").split("\n");
|
|
15
67
|
if (oldLines.length !== correctedLines.length)
|
|
16
68
|
return undefined;
|
|
69
|
+
const { commentInterior, templateInterior } = computeInteriorMasks(oldLines, fileContext);
|
|
17
70
|
const indentMap = new Map();
|
|
71
|
+
// oldIndent keys backed by at least one line OUTSIDE a comment or
|
|
72
|
+
// template-literal interior — the only keys eligible to be picked as the
|
|
73
|
+
// base unit below. An interior line still lands in indentMap (exact-width
|
|
74
|
+
// lookups must still resolve, #3052 F1), it just cannot anchor the
|
|
75
|
+
// extrapolation.
|
|
76
|
+
const structuralIndents = new Set();
|
|
18
77
|
const ambiguousIndents = new Set();
|
|
19
|
-
for (
|
|
20
|
-
|
|
21
|
-
|
|
78
|
+
for (const [i, oldLine] of oldLines.entries()) {
|
|
79
|
+
// oldLines.length === correctedLines.length is checked above; the "" is
|
|
80
|
+
// unreachable, only satisfying noUncheckedIndexedAccess.
|
|
81
|
+
const correctedLine = correctedLines[i] ?? "";
|
|
82
|
+
const oldIndent = oldLine.match(/^[\t ]*/)?.[0] ?? "";
|
|
83
|
+
const correctedIndent = correctedLine.match(/^[\t ]*/)?.[0] ?? "";
|
|
22
84
|
if (oldIndent === correctedIndent)
|
|
23
85
|
continue;
|
|
24
86
|
const previous = indentMap.get(oldIndent);
|
|
25
87
|
if (previous !== undefined && previous !== correctedIndent) {
|
|
26
88
|
indentMap.delete(oldIndent);
|
|
89
|
+
structuralIndents.delete(oldIndent);
|
|
27
90
|
ambiguousIndents.add(oldIndent);
|
|
28
91
|
continue;
|
|
29
92
|
}
|
|
30
93
|
if (!ambiguousIndents.has(oldIndent)) {
|
|
31
94
|
indentMap.set(oldIndent, correctedIndent);
|
|
95
|
+
if (!commentInterior[i] && !templateInterior[i]) {
|
|
96
|
+
structuralIndents.add(oldIndent);
|
|
97
|
+
}
|
|
32
98
|
}
|
|
33
99
|
}
|
|
34
100
|
if (indentMap.size === 0)
|
|
35
101
|
return undefined;
|
|
36
|
-
// Find the shortest
|
|
37
|
-
// levels in newText that are deeper than anything in
|
|
38
|
-
// as n × baseFrom → n × baseTo.
|
|
102
|
+
// Find the shortest structurally-backed mapped key as the base unit so
|
|
103
|
+
// that nesting levels in newText that are deeper than anything in
|
|
104
|
+
// oldText can be remapped as n × baseFrom → n × baseTo. A key backed
|
|
105
|
+
// only by comment- or template-interior lines is skipped here (but stays
|
|
106
|
+
// in indentMap for direct lookups above).
|
|
39
107
|
let baseFrom = "";
|
|
40
108
|
let baseTo = "";
|
|
41
109
|
for (const [from, to] of indentMap) {
|
|
42
|
-
if (from.length > 0 &&
|
|
110
|
+
if (from.length > 0 &&
|
|
111
|
+
structuralIndents.has(from) &&
|
|
112
|
+
(baseFrom === "" || from.length < baseFrom.length)) {
|
|
43
113
|
baseFrom = from;
|
|
44
114
|
baseTo = to;
|
|
45
115
|
}
|