ultimate-pi 0.18.0 → 0.19.0

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.
Files changed (314) hide show
  1. package/.agents/skills/harness-debate-plan/SKILL.md +1 -1
  2. package/.agents/skills/harness-decisions/SKILL.md +2 -3
  3. package/.agents/skills/harness-governor/SKILL.md +6 -5
  4. package/.agents/skills/harness-orchestration/SKILL.md +4 -4
  5. package/.agents/skills/harness-review/SKILL.md +7 -7
  6. package/.agents/skills/harness-sentrux-setup/SKILL.md +4 -3
  7. package/.agents/skills/harness-steer/SKILL.md +1 -1
  8. package/.agents/skills/sentrux/SKILL.md +9 -9
  9. package/.pi/PACKAGING.md +4 -4
  10. package/.pi/SYSTEM.md +54 -120
  11. package/.pi/agents/harness/incident-recorder.md +0 -1
  12. package/.pi/agents/harness/planning/decompose.md +1 -3
  13. package/.pi/agents/harness/planning/execution-plan-author.md +0 -2
  14. package/.pi/agents/harness/planning/hypothesis-validator.md +0 -2
  15. package/.pi/agents/harness/planning/hypothesis.md +0 -2
  16. package/.pi/agents/harness/planning/implementation-researcher.md +0 -2
  17. package/.pi/agents/harness/planning/plan-adversary.md +0 -2
  18. package/.pi/agents/harness/planning/plan-evaluator.md +1 -3
  19. package/.pi/agents/harness/planning/planning-context.md +0 -2
  20. package/.pi/agents/harness/planning/review-integrator.md +0 -2
  21. package/.pi/agents/harness/planning/sprint-contract-auditor.md +0 -2
  22. package/.pi/agents/harness/planning/stack-researcher.md +0 -2
  23. package/.pi/agents/harness/{adversary.md → reviewing/adversary.md} +0 -2
  24. package/.pi/agents/harness/{evaluator.md → reviewing/evaluator.md} +0 -2
  25. package/.pi/agents/harness/{tie-breaker.md → reviewing/tie-breaker.md} +0 -2
  26. package/.pi/agents/harness/{executor.md → running/executor.md} +0 -2
  27. package/.pi/agents/harness/sentrux-bootstrap.md +0 -1
  28. package/.pi/agents/harness/sentrux-steward.md +0 -2
  29. package/.pi/agents/harness/trace-librarian.md +0 -1
  30. package/.pi/extensions/00-harness-project-control.ts +133 -0
  31. package/.pi/extensions/00-posthog-network-bootstrap.ts +1 -1
  32. package/.pi/extensions/agt-kill-switch.ts +57 -0
  33. package/.pi/extensions/agt-prompt-guard.ts +32 -0
  34. package/.pi/extensions/budget-guard.ts +2 -0
  35. package/.pi/extensions/custom-footer.ts +46 -145
  36. package/.pi/extensions/custom-header.ts +1 -1
  37. package/.pi/extensions/custom-system-prompt.ts +1 -1
  38. package/.pi/extensions/debate-orchestrator.ts +7 -5
  39. package/.pi/extensions/harness-ask-user.ts +8 -8
  40. package/.pi/extensions/harness-debate-tools.ts +27 -43
  41. package/.pi/extensions/harness-lens.ts +94 -0
  42. package/.pi/extensions/harness-live-widget.ts +33 -2
  43. package/.pi/extensions/harness-plan-approval.ts +12 -12
  44. package/.pi/extensions/harness-run-context.ts +1214 -852
  45. package/.pi/extensions/harness-subagent-governance.ts +8 -0
  46. package/.pi/extensions/harness-subagent-submit.ts +36 -164
  47. package/.pi/extensions/harness-subagents.ts +4 -4
  48. package/.pi/extensions/harness-telemetry.ts +3 -1
  49. package/.pi/extensions/harness-web-tools.ts +3 -3
  50. package/.pi/extensions/observation-bus.ts +2 -0
  51. package/.pi/extensions/policy-gate.ts +27 -5
  52. package/.pi/extensions/review-integrity.ts +91 -10
  53. package/.pi/extensions/sentrux-rules-sync.ts +3 -1
  54. package/.pi/extensions/subagent-governance.ts +92 -0
  55. package/.pi/extensions/test-diff-integrity.ts +1 -0
  56. package/.pi/extensions/trace-recorder.ts +3 -1
  57. package/.pi/extensions/{ultimate-pi-vcc.ts → vcc-compaction.ts} +1 -1
  58. package/.pi/harness/README.md +6 -2
  59. package/.pi/harness/agents.manifest.json +38 -49
  60. package/.pi/harness/agents.policy.yaml +275 -0
  61. package/.pi/harness/corpus/graphify-kb-updater.config.json +55 -0
  62. package/.pi/harness/docs/adrs/0006-sentrux-dual-layer.md +2 -1
  63. package/.pi/harness/docs/adrs/0030-inhouse-vcc-compaction.md +1 -1
  64. package/.pi/harness/docs/adrs/0035-plan-phase-review-gate.md +1 -1
  65. package/.pi/harness/docs/adrs/0044-harness-steer-loop.md +3 -2
  66. package/.pi/harness/docs/adrs/0045-harness-lens-minimal-contract.md +49 -0
  67. package/.pi/harness/docs/adrs/0045-phase-scoped-agent-directories.md +33 -0
  68. package/.pi/harness/docs/adrs/0046-agt-policy-engine.md +51 -0
  69. package/.pi/harness/docs/adrs/0047-agt-layered-security.md +39 -0
  70. package/.pi/harness/docs/adrs/0048-tool-call-hook-order.md +25 -0
  71. package/.pi/harness/docs/adrs/0049-agents-policy-manifest.md +36 -0
  72. package/.pi/harness/docs/adrs/README.md +6 -0
  73. package/.pi/harness/docs/graphify-kb-updater-runbook.md +11 -5
  74. package/.pi/harness/docs/practice-map.md +2 -2
  75. package/.pi/harness/evolution/README.md +1 -2
  76. package/.pi/harness/examples/agents.policy.project.yaml +19 -0
  77. package/.pi/harness/examples/policies/custom-deny-bash.yaml +9 -0
  78. package/.pi/harness/policies/bash-denylists.yaml +5 -0
  79. package/.pi/harness/policies/defaults.yaml +51 -0
  80. package/.pi/harness/policies/orchestrator.yaml +18 -0
  81. package/.pi/harness/policies/phases.yaml +10 -0
  82. package/.pi/harness/policies/roles.yaml +5 -0
  83. package/.pi/harness/policies/web-guard.yaml +5 -0
  84. package/.pi/harness/policies/workflow-sequences.yaml +9 -0
  85. package/.pi/harness/sentrux/architecture.manifest.json +26 -4
  86. package/.pi/harness/specs/harness-spawn-context.schema.json +1 -1
  87. package/.pi/harness/specs/observation.schema.json +2 -1
  88. package/.pi/lib/agents-policy.d.mts +70 -0
  89. package/.pi/lib/agents-policy.mjs +325 -0
  90. package/.pi/lib/agents-policy.ts +19 -0
  91. package/.pi/lib/agt/audit-run-sink.ts +52 -0
  92. package/.pi/lib/agt/build-evaluation-context.ts +285 -0
  93. package/.pi/lib/agt/config.ts +28 -0
  94. package/.pi/lib/agt/delegation.ts +69 -0
  95. package/.pi/lib/agt/evaluate-policy.ts +56 -0
  96. package/.pi/lib/agt/identity-registry.ts +41 -0
  97. package/.pi/lib/agt/index.ts +55 -0
  98. package/.pi/lib/agt/kill-switch-state.ts +11 -0
  99. package/.pi/lib/agt/legacy-evaluate.ts +101 -0
  100. package/.pi/lib/agt/policy-engine.ts +154 -0
  101. package/.pi/lib/agt/rings.ts +21 -0
  102. package/.pi/lib/agt/sre-hooks.ts +45 -0
  103. package/.pi/lib/agt/trust-run-store.ts +26 -0
  104. package/.pi/lib/agt/workflow-history.ts +29 -0
  105. package/.pi/lib/agt-governance-active.ts +14 -0
  106. package/.pi/lib/agt-tool-guard.ts +78 -0
  107. package/.pi/lib/ask-user/dialog.ts +314 -0
  108. package/.pi/{extensions/lib → lib}/debate-bus-core.ts +10 -10
  109. package/.pi/{extensions/lib → lib}/debate-bus-state.ts +1 -1
  110. package/.pi/{extensions/lib → lib}/extension-load-guard.ts +21 -0
  111. package/.pi/lib/harness-agt-tool-guard.ts +5 -0
  112. package/.pi/{extensions/lib → lib}/harness-artifact-gate.ts +6 -16
  113. package/.pi/lib/harness-debate-core-deps.ts +14 -0
  114. package/.pi/lib/harness-debate-workflow-deps.ts +43 -0
  115. package/.pi/lib/harness-lens/.gitattributes +1 -0
  116. package/.pi/lib/harness-lens/clients/edit-autopatch.ts +88 -0
  117. package/.pi/lib/harness-lens/clients/file-kinds.ts +380 -0
  118. package/.pi/lib/harness-lens/clients/file-time.ts +215 -0
  119. package/.pi/lib/harness-lens/clients/file-utils.ts +484 -0
  120. package/.pi/lib/harness-lens/clients/format-service.ts +276 -0
  121. package/.pi/lib/harness-lens/clients/formatters.ts +1000 -0
  122. package/.pi/lib/harness-lens/clients/git-guard.ts +31 -0
  123. package/.pi/lib/harness-lens/clients/indent-retarget.ts +90 -0
  124. package/.pi/lib/harness-lens/clients/installer/index.ts +2368 -0
  125. package/.pi/lib/harness-lens/clients/latency-logger.ts +80 -0
  126. package/.pi/lib/harness-lens/clients/lens-config.ts +43 -0
  127. package/.pi/lib/harness-lens/clients/lens-events.ts +164 -0
  128. package/.pi/lib/harness-lens/clients/lsp/aggregation.ts +91 -0
  129. package/.pi/lib/harness-lens/clients/lsp/client.ts +1466 -0
  130. package/.pi/lib/harness-lens/clients/lsp/config.ts +216 -0
  131. package/.pi/lib/harness-lens/clients/lsp/edits.ts +297 -0
  132. package/.pi/lib/harness-lens/clients/lsp/index.ts +1355 -0
  133. package/.pi/lib/harness-lens/clients/lsp/interactive-install.ts +424 -0
  134. package/.pi/lib/harness-lens/clients/lsp/language.ts +223 -0
  135. package/.pi/lib/harness-lens/clients/lsp/launch.ts +939 -0
  136. package/.pi/lib/harness-lens/clients/lsp/lsp-index.ts +11 -0
  137. package/.pi/lib/harness-lens/clients/lsp/path-utils.ts +12 -0
  138. package/.pi/lib/harness-lens/clients/lsp/server-strategies.ts +81 -0
  139. package/.pi/lib/harness-lens/clients/lsp/server.ts +1971 -0
  140. package/.pi/lib/harness-lens/clients/path-utils.ts +182 -0
  141. package/.pi/lib/harness-lens/clients/pipeline.ts +360 -0
  142. package/.pi/lib/harness-lens/clients/project-profile.ts +117 -0
  143. package/.pi/lib/harness-lens/clients/runtime-agent-end.ts +112 -0
  144. package/.pi/lib/harness-lens/clients/runtime-config.ts +33 -0
  145. package/.pi/lib/harness-lens/clients/runtime-coordinator.ts +186 -0
  146. package/.pi/lib/harness-lens/clients/runtime-tool-result.ts +171 -0
  147. package/.pi/lib/harness-lens/clients/safe-spawn.ts +339 -0
  148. package/.pi/lib/harness-lens/clients/secrets-scanner.ts +214 -0
  149. package/.pi/lib/harness-lens/clients/tool-policy.ts +2072 -0
  150. package/.pi/lib/harness-lens/clients/types.ts +59 -0
  151. package/.pi/lib/harness-lens/clients/widget-state.ts +283 -0
  152. package/.pi/lib/harness-lens/index.ts +532 -0
  153. package/.pi/lib/harness-lens/tools/lsp-diagnostics.ts +706 -0
  154. package/.pi/lib/harness-lens/tools/lsp-navigation.ts +1246 -0
  155. package/.pi/{extensions/lib → lib}/harness-posthog.ts +3 -0
  156. package/.pi/lib/harness-project-config.ts +91 -0
  157. package/.pi/lib/harness-run-context-responses.ts +9 -0
  158. package/.pi/lib/harness-run-context.ts +1 -3
  159. package/.pi/{extensions/lib/spawn-policy.ts → lib/harness-spawn-policy.ts} +4 -3
  160. package/.pi/{extensions/lib → lib}/harness-spawn-topology.ts +5 -28
  161. package/.pi/lib/harness-subagent-auth.ts +51 -0
  162. package/.pi/{extensions/lib → lib}/harness-subagent-precheck.ts +13 -10
  163. package/.pi/{extensions/lib → lib}/harness-subagent-submit-pipeline.ts +3 -3
  164. package/.pi/lib/harness-subagent-submit-register.ts +163 -0
  165. package/.pi/{extensions/lib → lib}/harness-subagent-submit-registry.ts +1 -55
  166. package/.pi/{extensions/lib → lib}/harness-subagents-bridge.ts +53 -14
  167. package/.pi/{extensions/lib → lib}/harness-subprocess-bootstrap.ts +1 -1
  168. package/.pi/lib/harness-ui-state.ts +27 -12
  169. package/.pi/{extensions/lib → lib}/plan-approval/create-plan.ts +2 -2
  170. package/.pi/{extensions/lib → lib}/plan-approval/format-plan.ts +2 -2
  171. package/.pi/{extensions/lib → lib}/plan-approval/plan-review.ts +162 -201
  172. package/.pi/{extensions/lib → lib}/plan-approval/render.ts +1 -1
  173. package/.pi/{extensions/lib → lib}/plan-approval/resolve-disk.ts +2 -2
  174. package/.pi/{extensions/lib → lib}/plan-approval/types.ts +1 -1
  175. package/.pi/{extensions/lib → lib}/plan-approval/validate.ts +3 -3
  176. package/.pi/{extensions/lib → lib}/plan-approval-readiness.ts +3 -52
  177. package/.pi/{extensions/lib → lib}/plan-debate-envelope.ts +1 -1
  178. package/.pi/{extensions/lib → lib}/plan-debate-gate.ts +1 -1
  179. package/.pi/{extensions/lib → lib}/plan-debate-lane.ts +1 -4
  180. package/.pi/{extensions/lib → lib}/plan-messenger.ts +1 -1
  181. package/.pi/prompts/harness-auto.md +2 -2
  182. package/.pi/prompts/harness-plan.md +4 -6
  183. package/.pi/prompts/harness-review.md +9 -9
  184. package/.pi/prompts/harness-run.md +7 -7
  185. package/.pi/prompts/harness-setup.md +42 -68
  186. package/.pi/prompts/harness-steer.md +2 -2
  187. package/.pi/scripts/README.md +3 -5
  188. package/.pi/scripts/generate-agents-policy-yaml.mjs +148 -0
  189. package/.pi/scripts/graphify-kb-updater.mjs +48 -8
  190. package/.pi/scripts/harness-agents-manifest.mjs +61 -4
  191. package/.pi/scripts/harness-agt-doctor.ts +36 -0
  192. package/.pi/scripts/harness-cli-verify.sh +9 -2
  193. package/.pi/scripts/harness-project-toggle.mjs +129 -0
  194. package/.pi/scripts/harness-sentrux-cli.mjs +142 -0
  195. package/.pi/scripts/harness-verify.mjs +113 -39
  196. package/.pi/scripts/harness-web-policy-guard.mjs +2 -2
  197. package/.pi/scripts/validate-plan-dag.mjs +65 -74
  198. package/.pi/scripts/vendor-pi-vcc-settings.stub.ts +2 -2
  199. package/.pi/scripts/vendor-sync-pi-vcc.sh +1 -1
  200. package/.pi/skills/architecture/broker-domain/SKILL.md +65 -0
  201. package/.pi/skills/architecture/cqrs/SKILL.md +63 -0
  202. package/.pi/skills/architecture/event-driven/SKILL.md +60 -0
  203. package/.pi/skills/architecture/hexagonal-ports-adapters/SKILL.md +66 -0
  204. package/.pi/skills/architecture/layered/SKILL.md +68 -0
  205. package/.pi/skills/architecture/microkernel/SKILL.md +62 -0
  206. package/.pi/skills/architecture/microservices/SKILL.md +64 -0
  207. package/.pi/skills/architecture/modular-monolith/SKILL.md +65 -0
  208. package/.pi/skills/architecture/orchestration-driven-soa/SKILL.md +61 -0
  209. package/.pi/skills/architecture/pipeline/SKILL.md +63 -0
  210. package/.pi/skills/architecture/service-based/SKILL.md +64 -0
  211. package/.pi/skills/architecture/service-mesh/SKILL.md +60 -0
  212. package/.pi/skills/architecture/space-based/SKILL.md +60 -0
  213. package/.pi/skills/ast-grep/SKILL.md +40 -321
  214. package/.pi/skills/delivery/debugging-discipline/SKILL.md +36 -0
  215. package/.pi/skills/delivery/documentation-update/SKILL.md +33 -0
  216. package/.pi/skills/delivery/requirements-to-implementation/SKILL.md +34 -0
  217. package/.pi/skills/delivery/risk-based-verification/SKILL.md +43 -0
  218. package/.pi/skills/delivery/tradeoff-analysis/SKILL.md +34 -0
  219. package/.pi/skills/engineering/api-contract-design/SKILL.md +38 -0
  220. package/.pi/skills/engineering/cohesion-coupling/SKILL.md +43 -0
  221. package/.pi/skills/engineering/complexity-control/SKILL.md +31 -0
  222. package/.pi/skills/engineering/defensive-programming/SKILL.md +38 -0
  223. package/.pi/skills/engineering/dependency-management/SKILL.md +29 -0
  224. package/.pi/skills/engineering/domain-modeling/SKILL.md +32 -0
  225. package/.pi/skills/engineering/error-handling/SKILL.md +37 -0
  226. package/.pi/skills/engineering/legacy-code-seams/SKILL.md +35 -0
  227. package/.pi/skills/engineering/naming-and-intent/SKILL.md +29 -0
  228. package/.pi/skills/engineering/refactoring-safe-evolution/SKILL.md +35 -0
  229. package/.pi/skills/engineering/routine-function-design/SKILL.md +34 -0
  230. package/.pi/skills/engineering/small-change-discipline/SKILL.md +35 -0
  231. package/.pi/skills/lsp-navigation/SKILL.md +89 -0
  232. package/.pi/skills/quality/code-review-self-check/SKILL.md +35 -0
  233. package/.pi/skills/quality/privacy-data-handling/SKILL.md +26 -0
  234. package/.pi/skills/quality/security-review/SKILL.md +34 -0
  235. package/.pi/skills/quality/test-strategy/SKILL.md +33 -0
  236. package/.pi/skills/quality/testability-design/SKILL.md +33 -0
  237. package/.pi/skills/systems/concurrency-safety/SKILL.md +32 -0
  238. package/.pi/skills/systems/data-modeling-migrations/SKILL.md +31 -0
  239. package/.pi/skills/systems/observability-instrumentation/SKILL.md +32 -0
  240. package/.pi/skills/systems/performance-measurement/SKILL.md +35 -0
  241. package/.pi/skills/systems/reliability-design/SKILL.md +32 -0
  242. package/.sentrux/rules.toml +20 -4
  243. package/AGENTS.md +5 -0
  244. package/CHANGELOG.md +26 -0
  245. package/README.md +85 -58
  246. package/THIRD_PARTY_NOTICES.md +12 -21
  247. package/package.json +15 -7
  248. package/vendor/pi-subagents/src/agents.ts +45 -1
  249. package/vendor/pi-subagents/src/subagents.ts +866 -811
  250. package/vendor/pi-vcc/src/core/brief.ts +68 -99
  251. package/vendor/pi-vcc/src/core/settings.ts +2 -2
  252. package/.agents/skills/caveman/SKILL.md +0 -67
  253. package/.pi/agents/harness/meta-optimizer.md +0 -36
  254. package/.pi/agents/harness/planning/scout-graphify.md +0 -39
  255. package/.pi/agents/harness/planning/scout-semantic.md +0 -41
  256. package/.pi/agents/harness/planning/scout-structure.md +0 -37
  257. package/.pi/extensions/lib/ask-user/dialog.ts +0 -260
  258. package/.pi/extensions/lib/harness-subagent-auth.ts +0 -209
  259. package/.pi/extensions/lib/harness-subagent-policy.ts +0 -236
  260. package/.pi/extensions/pi-model-router-harness.ts +0 -42
  261. package/.pi/harness/evolution/meta-optimizer.mjs +0 -99
  262. package/.pi/harness/specs/router-tuning-proposal.schema.json +0 -114
  263. package/.pi/model-router.example.json +0 -36
  264. package/.pi/prompts/harness-critic.md +0 -10
  265. package/.pi/prompts/harness-eval.md +0 -10
  266. package/.pi/prompts/harness-router-tune.md +0 -52
  267. package/.pi/scripts/harness-generate-model-router.mjs +0 -327
  268. package/.pi/scripts/harness-model-router-routing.test.mjs +0 -97
  269. package/.pi/scripts/harness-sync-model-router.mjs +0 -97
  270. package/.pi/scripts/vendor-sync-pi-model-router.sh +0 -47
  271. package/vendor/pi-model-router/.prettierignore +0 -4
  272. package/vendor/pi-model-router/.prettierrc +0 -5
  273. package/vendor/pi-model-router/AGENTS.md +0 -39
  274. package/vendor/pi-model-router/LICENSE +0 -21
  275. package/vendor/pi-model-router/README.md +0 -99
  276. package/vendor/pi-model-router/UPSTREAM_PIN.md +0 -10
  277. package/vendor/pi-model-router/docs/ARCHITECTURE.md +0 -54
  278. package/vendor/pi-model-router/extensions/commands.ts +0 -720
  279. package/vendor/pi-model-router/extensions/config.ts +0 -348
  280. package/vendor/pi-model-router/extensions/constants.ts +0 -1
  281. package/vendor/pi-model-router/extensions/index.ts +0 -478
  282. package/vendor/pi-model-router/extensions/provider.ts +0 -580
  283. package/vendor/pi-model-router/extensions/routing.ts +0 -564
  284. package/vendor/pi-model-router/extensions/state.ts +0 -52
  285. package/vendor/pi-model-router/extensions/types.ts +0 -95
  286. package/vendor/pi-model-router/extensions/ui.ts +0 -144
  287. package/vendor/pi-model-router/model-router.example.json +0 -48
  288. package/vendor/pi-model-router/package.json +0 -48
  289. package/vendor/pi-model-router/tsconfig.json +0 -16
  290. /package/.pi/{prompts → harness/docs}/planning-rubrics.md +0 -0
  291. /package/.pi/{extensions/lib → lib}/ask-user/fallback.ts +0 -0
  292. /package/.pi/{extensions/lib → lib}/ask-user/render.ts +0 -0
  293. /package/.pi/{extensions/lib → lib}/ask-user/schema.ts +0 -0
  294. /package/.pi/{extensions/lib → lib}/ask-user/types.ts +0 -0
  295. /package/.pi/{extensions/lib → lib}/ask-user/validate-core.mjs +0 -0
  296. /package/.pi/{extensions/lib → lib}/ask-user/validate.ts +0 -0
  297. /package/.pi/{extensions/lib → lib}/harness-cocoindex-refresh.ts +0 -0
  298. /package/.pi/{extensions/lib → lib}/harness-paths.ts +0 -0
  299. /package/.pi/{extensions/lib → lib}/harness-spawn-budget.ts +0 -0
  300. /package/.pi/{extensions/lib → lib}/harness-vcc-settings.ts +0 -0
  301. /package/.pi/{extensions/lib → lib}/harness-web/run-cli.ts +0 -0
  302. /package/.pi/{extensions/lib → lib}/plan-approval/dialog.ts +0 -0
  303. /package/.pi/{extensions/lib → lib}/plan-approval/schema.ts +0 -0
  304. /package/.pi/{extensions/lib → lib}/plan-debate-eligibility.ts +0 -0
  305. /package/.pi/{extensions/lib → lib}/plan-debate-focus.ts +0 -0
  306. /package/.pi/{extensions/lib → lib}/plan-debate-id.ts +0 -0
  307. /package/.pi/{extensions/lib → lib}/plan-debate-lanes.ts +0 -0
  308. /package/.pi/{extensions/lib → lib}/plan-debate-round-status.ts +0 -0
  309. /package/.pi/{extensions/lib → lib}/plan-debate-write-guard.ts +0 -0
  310. /package/.pi/{extensions/lib → lib}/plan-review-gate.ts +0 -0
  311. /package/.pi/{extensions/lib → lib}/plan-review-integrator-rules.ts +0 -0
  312. /package/.pi/{extensions/lib → lib}/plan-scope-guard.ts +0 -0
  313. /package/.pi/{extensions/lib → lib}/posthog-client.ts +0 -0
  314. /package/.pi/{extensions/lib → lib}/posthog-node.d.ts +0 -0
@@ -0,0 +1,2072 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { logLatency } from "./latency-logger.js";
4
+
5
+ export type ToolGate = "config-first" | "smart-default" | "mixed";
6
+
7
+ export interface FormatterPolicy {
8
+ formatterNames: string[];
9
+ defaultFormatter?: string;
10
+ defaultWhenUnconfigured: boolean;
11
+ gate: ToolGate;
12
+ }
13
+
14
+ const FORMATTER_POLICY_BY_EXTENSION = new Map<string, FormatterPolicy>([
15
+ [
16
+ ".js",
17
+ {
18
+ formatterNames: ["biome", "prettier", "oxfmt"],
19
+ defaultFormatter: "biome",
20
+ defaultWhenUnconfigured: true,
21
+ gate: "smart-default",
22
+ },
23
+ ],
24
+ [
25
+ ".jsx",
26
+ {
27
+ formatterNames: ["biome", "prettier", "oxfmt"],
28
+ defaultFormatter: "biome",
29
+ defaultWhenUnconfigured: true,
30
+ gate: "smart-default",
31
+ },
32
+ ],
33
+ [
34
+ ".mjs",
35
+ {
36
+ formatterNames: ["biome", "prettier", "oxfmt"],
37
+ defaultFormatter: "biome",
38
+ defaultWhenUnconfigured: true,
39
+ gate: "smart-default",
40
+ },
41
+ ],
42
+ [
43
+ ".cjs",
44
+ {
45
+ formatterNames: ["biome", "prettier", "oxfmt"],
46
+ defaultFormatter: "biome",
47
+ defaultWhenUnconfigured: true,
48
+ gate: "smart-default",
49
+ },
50
+ ],
51
+ [
52
+ ".ts",
53
+ {
54
+ formatterNames: ["biome", "prettier", "oxfmt"],
55
+ defaultFormatter: "biome",
56
+ defaultWhenUnconfigured: true,
57
+ gate: "smart-default",
58
+ },
59
+ ],
60
+ [
61
+ ".tsx",
62
+ {
63
+ formatterNames: ["biome", "prettier", "oxfmt"],
64
+ defaultFormatter: "biome",
65
+ defaultWhenUnconfigured: true,
66
+ gate: "smart-default",
67
+ },
68
+ ],
69
+ [
70
+ ".mts",
71
+ {
72
+ formatterNames: ["biome", "prettier", "oxfmt"],
73
+ defaultFormatter: "biome",
74
+ defaultWhenUnconfigured: true,
75
+ gate: "smart-default",
76
+ },
77
+ ],
78
+ [
79
+ ".cts",
80
+ {
81
+ formatterNames: ["biome", "prettier", "oxfmt"],
82
+ defaultFormatter: "biome",
83
+ defaultWhenUnconfigured: true,
84
+ gate: "smart-default",
85
+ },
86
+ ],
87
+ [
88
+ ".py",
89
+ {
90
+ formatterNames: ["black", "ruff"],
91
+ defaultFormatter: "ruff",
92
+ defaultWhenUnconfigured: true,
93
+ gate: "smart-default",
94
+ },
95
+ ],
96
+ [
97
+ ".pyi",
98
+ {
99
+ formatterNames: ["black", "ruff"],
100
+ defaultFormatter: "ruff",
101
+ defaultWhenUnconfigured: true,
102
+ gate: "smart-default",
103
+ },
104
+ ],
105
+ [
106
+ ".json",
107
+ {
108
+ formatterNames: ["biome", "prettier"],
109
+ defaultFormatter: "biome",
110
+ defaultWhenUnconfigured: false,
111
+ gate: "mixed",
112
+ },
113
+ ],
114
+ [
115
+ ".jsonc",
116
+ {
117
+ formatterNames: ["biome", "prettier"],
118
+ defaultFormatter: "biome",
119
+ defaultWhenUnconfigured: false,
120
+ gate: "mixed",
121
+ },
122
+ ],
123
+ [
124
+ ".css",
125
+ {
126
+ formatterNames: ["biome", "prettier", "oxfmt"],
127
+ defaultFormatter: "biome",
128
+ defaultWhenUnconfigured: true,
129
+ gate: "smart-default",
130
+ },
131
+ ],
132
+ [
133
+ ".scss",
134
+ {
135
+ formatterNames: ["biome", "prettier", "oxfmt"],
136
+ defaultFormatter: "biome",
137
+ defaultWhenUnconfigured: true,
138
+ gate: "smart-default",
139
+ },
140
+ ],
141
+ [
142
+ ".sass",
143
+ {
144
+ formatterNames: ["biome", "prettier", "oxfmt"],
145
+ defaultFormatter: "biome",
146
+ defaultWhenUnconfigured: true,
147
+ gate: "smart-default",
148
+ },
149
+ ],
150
+ [
151
+ ".less",
152
+ {
153
+ formatterNames: ["prettier"],
154
+ defaultFormatter: "prettier",
155
+ defaultWhenUnconfigured: true,
156
+ gate: "smart-default",
157
+ },
158
+ ],
159
+ [
160
+ ".html",
161
+ {
162
+ formatterNames: ["prettier"],
163
+ defaultFormatter: "prettier",
164
+ defaultWhenUnconfigured: true,
165
+ gate: "smart-default",
166
+ },
167
+ ],
168
+ [
169
+ ".htm",
170
+ {
171
+ formatterNames: ["prettier"],
172
+ defaultFormatter: "prettier",
173
+ defaultWhenUnconfigured: true,
174
+ gate: "smart-default",
175
+ },
176
+ ],
177
+ [
178
+ ".yaml",
179
+ {
180
+ formatterNames: ["prettier"],
181
+ defaultFormatter: "prettier",
182
+ defaultWhenUnconfigured: true,
183
+ gate: "smart-default",
184
+ },
185
+ ],
186
+ [
187
+ ".yml",
188
+ {
189
+ formatterNames: ["prettier"],
190
+ defaultFormatter: "prettier",
191
+ defaultWhenUnconfigured: true,
192
+ gate: "smart-default",
193
+ },
194
+ ],
195
+ [
196
+ ".md",
197
+ {
198
+ formatterNames: ["prettier"],
199
+ defaultFormatter: "prettier",
200
+ // Prettier's markdown defaults reflow lines, normalize emphasis (* -> _),
201
+ // and restyle lists. Opt-in via project prettier config; do not run by default.
202
+ defaultWhenUnconfigured: false,
203
+ gate: "smart-default",
204
+ },
205
+ ],
206
+ [
207
+ ".mdx",
208
+ {
209
+ formatterNames: ["prettier"],
210
+ defaultFormatter: "prettier",
211
+ defaultWhenUnconfigured: false,
212
+ gate: "smart-default",
213
+ },
214
+ ],
215
+ [
216
+ ".graphql",
217
+ {
218
+ formatterNames: ["prettier"],
219
+ defaultFormatter: "prettier",
220
+ defaultWhenUnconfigured: true,
221
+ gate: "smart-default",
222
+ },
223
+ ],
224
+ [
225
+ ".gql",
226
+ {
227
+ formatterNames: ["prettier"],
228
+ defaultFormatter: "prettier",
229
+ defaultWhenUnconfigured: true,
230
+ gate: "smart-default",
231
+ },
232
+ ],
233
+ [
234
+ ".kt",
235
+ {
236
+ formatterNames: ["ktlint"],
237
+ defaultFormatter: "ktlint",
238
+ defaultWhenUnconfigured: true,
239
+ gate: "smart-default",
240
+ },
241
+ ],
242
+ [
243
+ ".kts",
244
+ {
245
+ formatterNames: ["ktlint"],
246
+ defaultFormatter: "ktlint",
247
+ defaultWhenUnconfigured: true,
248
+ gate: "smart-default",
249
+ },
250
+ ],
251
+ [
252
+ ".swift",
253
+ {
254
+ formatterNames: ["swiftformat"],
255
+ defaultFormatter: "swiftformat",
256
+ defaultWhenUnconfigured: true,
257
+ gate: "smart-default",
258
+ },
259
+ ],
260
+ [
261
+ ".fs",
262
+ {
263
+ formatterNames: ["fantomas"],
264
+ defaultFormatter: "fantomas",
265
+ defaultWhenUnconfigured: true,
266
+ gate: "smart-default",
267
+ },
268
+ ],
269
+ [
270
+ ".fsi",
271
+ {
272
+ formatterNames: ["fantomas"],
273
+ defaultFormatter: "fantomas",
274
+ defaultWhenUnconfigured: true,
275
+ gate: "smart-default",
276
+ },
277
+ ],
278
+ [
279
+ ".fsx",
280
+ {
281
+ formatterNames: ["fantomas"],
282
+ defaultFormatter: "fantomas",
283
+ defaultWhenUnconfigured: true,
284
+ gate: "smart-default",
285
+ },
286
+ ],
287
+ [
288
+ ".nix",
289
+ {
290
+ formatterNames: ["nixfmt"],
291
+ defaultFormatter: "nixfmt",
292
+ defaultWhenUnconfigured: true,
293
+ gate: "smart-default",
294
+ },
295
+ ],
296
+ [
297
+ ".ex",
298
+ {
299
+ formatterNames: ["mix"],
300
+ defaultFormatter: "mix",
301
+ defaultWhenUnconfigured: true,
302
+ gate: "smart-default",
303
+ },
304
+ ],
305
+ [
306
+ ".exs",
307
+ {
308
+ formatterNames: ["mix"],
309
+ defaultFormatter: "mix",
310
+ defaultWhenUnconfigured: true,
311
+ gate: "smart-default",
312
+ },
313
+ ],
314
+ [
315
+ ".eex",
316
+ {
317
+ formatterNames: ["mix"],
318
+ defaultFormatter: "mix",
319
+ defaultWhenUnconfigured: true,
320
+ gate: "smart-default",
321
+ },
322
+ ],
323
+ [
324
+ ".heex",
325
+ {
326
+ formatterNames: ["mix"],
327
+ defaultFormatter: "mix",
328
+ defaultWhenUnconfigured: true,
329
+ gate: "smart-default",
330
+ },
331
+ ],
332
+ [
333
+ ".leex",
334
+ {
335
+ formatterNames: ["mix"],
336
+ defaultFormatter: "mix",
337
+ defaultWhenUnconfigured: true,
338
+ gate: "smart-default",
339
+ },
340
+ ],
341
+ [
342
+ ".gleam",
343
+ {
344
+ formatterNames: ["gleam"],
345
+ defaultFormatter: "gleam",
346
+ defaultWhenUnconfigured: true,
347
+ gate: "smart-default",
348
+ },
349
+ ],
350
+ [
351
+ ".c",
352
+ {
353
+ formatterNames: ["clang-format"],
354
+ defaultFormatter: "clang-format",
355
+ defaultWhenUnconfigured: false,
356
+ gate: "config-first",
357
+ },
358
+ ],
359
+ [
360
+ ".cc",
361
+ {
362
+ formatterNames: ["clang-format"],
363
+ defaultFormatter: "clang-format",
364
+ defaultWhenUnconfigured: false,
365
+ gate: "config-first",
366
+ },
367
+ ],
368
+ [
369
+ ".cpp",
370
+ {
371
+ formatterNames: ["clang-format"],
372
+ defaultFormatter: "clang-format",
373
+ defaultWhenUnconfigured: false,
374
+ gate: "config-first",
375
+ },
376
+ ],
377
+ [
378
+ ".cxx",
379
+ {
380
+ formatterNames: ["clang-format"],
381
+ defaultFormatter: "clang-format",
382
+ defaultWhenUnconfigured: false,
383
+ gate: "config-first",
384
+ },
385
+ ],
386
+ [
387
+ ".h",
388
+ {
389
+ formatterNames: ["clang-format"],
390
+ defaultFormatter: "clang-format",
391
+ defaultWhenUnconfigured: false,
392
+ gate: "config-first",
393
+ },
394
+ ],
395
+ [
396
+ ".hpp",
397
+ {
398
+ formatterNames: ["clang-format"],
399
+ defaultFormatter: "clang-format",
400
+ defaultWhenUnconfigured: false,
401
+ gate: "config-first",
402
+ },
403
+ ],
404
+ [
405
+ ".ino",
406
+ {
407
+ formatterNames: ["clang-format"],
408
+ defaultFormatter: "clang-format",
409
+ defaultWhenUnconfigured: false,
410
+ gate: "config-first",
411
+ },
412
+ ],
413
+ [
414
+ ".php",
415
+ {
416
+ formatterNames: ["php-cs-fixer"],
417
+ defaultFormatter: "php-cs-fixer",
418
+ defaultWhenUnconfigured: false,
419
+ gate: "config-first",
420
+ },
421
+ ],
422
+ [
423
+ ".cs",
424
+ {
425
+ formatterNames: ["csharpier"],
426
+ defaultFormatter: "csharpier",
427
+ defaultWhenUnconfigured: true,
428
+ gate: "smart-default",
429
+ },
430
+ ],
431
+ [
432
+ ".lua",
433
+ {
434
+ formatterNames: ["stylua"],
435
+ defaultFormatter: "stylua",
436
+ defaultWhenUnconfigured: false,
437
+ gate: "config-first",
438
+ },
439
+ ],
440
+ [
441
+ ".hs",
442
+ {
443
+ formatterNames: ["ormolu"],
444
+ defaultFormatter: "ormolu",
445
+ defaultWhenUnconfigured: true,
446
+ gate: "smart-default",
447
+ },
448
+ ],
449
+ [
450
+ ".lhs",
451
+ {
452
+ formatterNames: ["ormolu"],
453
+ defaultFormatter: "ormolu",
454
+ defaultWhenUnconfigured: true,
455
+ gate: "smart-default",
456
+ },
457
+ ],
458
+ [
459
+ ".ml",
460
+ {
461
+ formatterNames: ["ocamlformat"],
462
+ defaultFormatter: "ocamlformat",
463
+ defaultWhenUnconfigured: false,
464
+ gate: "config-first",
465
+ },
466
+ ],
467
+ [
468
+ ".mli",
469
+ {
470
+ formatterNames: ["ocamlformat"],
471
+ defaultFormatter: "ocamlformat",
472
+ defaultWhenUnconfigured: false,
473
+ gate: "config-first",
474
+ },
475
+ ],
476
+ [
477
+ ".go",
478
+ {
479
+ formatterNames: ["gofmt"],
480
+ defaultFormatter: "gofmt",
481
+ defaultWhenUnconfigured: true,
482
+ gate: "smart-default",
483
+ },
484
+ ],
485
+ [
486
+ ".rs",
487
+ {
488
+ formatterNames: ["rustfmt"],
489
+ defaultFormatter: "rustfmt",
490
+ defaultWhenUnconfigured: true,
491
+ gate: "smart-default",
492
+ },
493
+ ],
494
+ [
495
+ ".sh",
496
+ {
497
+ formatterNames: ["shfmt"],
498
+ defaultFormatter: "shfmt",
499
+ defaultWhenUnconfigured: true,
500
+ gate: "smart-default",
501
+ },
502
+ ],
503
+ [
504
+ ".bash",
505
+ {
506
+ formatterNames: ["shfmt"],
507
+ defaultFormatter: "shfmt",
508
+ defaultWhenUnconfigured: true,
509
+ gate: "smart-default",
510
+ },
511
+ ],
512
+ [
513
+ ".fish",
514
+ {
515
+ formatterNames: ["fish-indent"],
516
+ defaultFormatter: "fish-indent",
517
+ defaultWhenUnconfigured: true,
518
+ gate: "smart-default",
519
+ },
520
+ ],
521
+ [
522
+ ".toml",
523
+ {
524
+ formatterNames: ["taplo"],
525
+ defaultFormatter: "taplo",
526
+ defaultWhenUnconfigured: true,
527
+ gate: "smart-default",
528
+ },
529
+ ],
530
+ [
531
+ ".tf",
532
+ {
533
+ formatterNames: ["terraform"],
534
+ defaultFormatter: "terraform",
535
+ defaultWhenUnconfigured: true,
536
+ gate: "smart-default",
537
+ },
538
+ ],
539
+ [
540
+ ".tfvars",
541
+ {
542
+ formatterNames: ["terraform"],
543
+ defaultFormatter: "terraform",
544
+ defaultWhenUnconfigured: true,
545
+ gate: "smart-default",
546
+ },
547
+ ],
548
+ [
549
+ ".dart",
550
+ {
551
+ formatterNames: ["dart"],
552
+ defaultFormatter: "dart",
553
+ defaultWhenUnconfigured: true,
554
+ gate: "smart-default",
555
+ },
556
+ ],
557
+ [
558
+ ".zig",
559
+ {
560
+ formatterNames: ["zig"],
561
+ defaultFormatter: "zig",
562
+ defaultWhenUnconfigured: true,
563
+ gate: "smart-default",
564
+ },
565
+ ],
566
+ [
567
+ ".zon",
568
+ {
569
+ formatterNames: ["zig"],
570
+ defaultFormatter: "zig",
571
+ defaultWhenUnconfigured: true,
572
+ gate: "smart-default",
573
+ },
574
+ ],
575
+ [
576
+ ".java",
577
+ {
578
+ formatterNames: ["google-java-format"],
579
+ defaultFormatter: "google-java-format",
580
+ defaultWhenUnconfigured: false,
581
+ gate: "config-first",
582
+ },
583
+ ],
584
+ [
585
+ ".clj",
586
+ {
587
+ formatterNames: ["cljfmt"],
588
+ defaultFormatter: "cljfmt",
589
+ defaultWhenUnconfigured: false,
590
+ gate: "config-first",
591
+ },
592
+ ],
593
+ [
594
+ ".cljc",
595
+ {
596
+ formatterNames: ["cljfmt"],
597
+ defaultFormatter: "cljfmt",
598
+ defaultWhenUnconfigured: false,
599
+ gate: "config-first",
600
+ },
601
+ ],
602
+ [
603
+ ".cljs",
604
+ {
605
+ formatterNames: ["cljfmt"],
606
+ defaultFormatter: "cljfmt",
607
+ defaultWhenUnconfigured: false,
608
+ gate: "config-first",
609
+ },
610
+ ],
611
+ [
612
+ ".cmake",
613
+ {
614
+ formatterNames: ["cmake-format"],
615
+ defaultFormatter: "cmake-format",
616
+ defaultWhenUnconfigured: false,
617
+ gate: "config-first",
618
+ },
619
+ ],
620
+ [
621
+ ".ps1",
622
+ {
623
+ formatterNames: ["psscriptanalyzer-format"],
624
+ defaultFormatter: "psscriptanalyzer-format",
625
+ defaultWhenUnconfigured: true,
626
+ gate: "smart-default",
627
+ },
628
+ ],
629
+ [
630
+ ".psm1",
631
+ {
632
+ formatterNames: ["psscriptanalyzer-format"],
633
+ defaultFormatter: "psscriptanalyzer-format",
634
+ defaultWhenUnconfigured: true,
635
+ gate: "smart-default",
636
+ },
637
+ ],
638
+ [
639
+ ".psd1",
640
+ {
641
+ formatterNames: ["psscriptanalyzer-format"],
642
+ defaultFormatter: "psscriptanalyzer-format",
643
+ defaultWhenUnconfigured: true,
644
+ gate: "smart-default",
645
+ },
646
+ ],
647
+ ]);
648
+
649
+ const AUTO_INSTALLABLE_DEFAULT_FORMATTERS = new Map<string, string>([
650
+ ["biome", "biome"],
651
+ ["ruff", "ruff"],
652
+ ["prettier", "prettier"],
653
+ ["shfmt", "shfmt"],
654
+ ["taplo", "taplo"],
655
+ ["ktlint", "ktlint"],
656
+ ]);
657
+
658
+ export function getFormatterPolicyForExtension(
659
+ ext: string,
660
+ ): FormatterPolicy | undefined {
661
+ return FORMATTER_POLICY_BY_EXTENSION.get(ext.toLowerCase());
662
+ }
663
+
664
+ export function getFormatterPolicyForFile(
665
+ filePath: string,
666
+ ): FormatterPolicy | undefined {
667
+ return getFormatterPolicyForExtension(path.extname(filePath));
668
+ }
669
+
670
+ export function getSmartDefaultFormatterName(
671
+ _filePath: string,
672
+ ): string | undefined {
673
+ return undefined;
674
+ }
675
+
676
+ export function getAutoInstallToolIdForFormatter(
677
+ formatterName: string,
678
+ ): string | undefined {
679
+ return AUTO_INSTALLABLE_DEFAULT_FORMATTERS.get(formatterName);
680
+ }
681
+
682
+ export function getToolExecutionPolicy(
683
+ toolId: string,
684
+ ): ToolExecutionPolicy | undefined {
685
+ return TOOL_EXECUTION_POLICY.get(toolId);
686
+ }
687
+
688
+ export function shouldAutoInstallTool(toolId: string): boolean {
689
+ return getToolExecutionPolicy(toolId)?.autoInstall ?? false;
690
+ }
691
+
692
+ export function getAutofixCapability(
693
+ toolId: string,
694
+ ): AutofixCapability | undefined {
695
+ return AUTOFIX_CAPABILITIES.get(toolId);
696
+ }
697
+
698
+ export function canToolAutoFix(toolId: string): boolean {
699
+ return getAutofixCapability(toolId)?.toolSupportsFix ?? false;
700
+ }
701
+
702
+ export function isSafePipelineAutofixTool(toolId: string): boolean {
703
+ return getAutofixCapability(toolId)?.safePipelineAutofix ?? false;
704
+ }
705
+
706
+ export function getToolCommandSpec(
707
+ toolId: string,
708
+ ): ToolCommandSpec | undefined {
709
+ return TOOL_COMMAND_SPECS.get(toolId);
710
+ }
711
+
712
+ export type AutofixToolName =
713
+ | "biome"
714
+ | "eslint"
715
+ | "ruff"
716
+ | "stylelint"
717
+ | "sqlfluff"
718
+ | "rubocop"
719
+ | "ktlint"
720
+ | "rust-clippy"
721
+ | "dart-analyze";
722
+
723
+ export type LintRunnerName =
724
+ | JstsLintRunnerName
725
+ | "ruff-lint"
726
+ | "stylelint"
727
+ | "sqlfluff"
728
+ | "rubocop"
729
+ | "yamllint"
730
+ | "actionlint"
731
+ | "markdownlint"
732
+ | "htmlhint"
733
+ | "hadolint"
734
+ | "golangci-lint"
735
+ | "phpstan"
736
+ | "ktlint"
737
+ | "taplo"
738
+ | "rust-clippy"
739
+ | "shellcheck"
740
+ | "fish-indent"
741
+ | "tflint"
742
+ | "credo"
743
+ | "cpp-check"
744
+ | "dart-analyze"
745
+ | "gleam-check"
746
+ | "psscriptanalyzer"
747
+ | "prisma-validate"
748
+ | "mypy"
749
+ | "detekt"
750
+ | "swiftlint"
751
+ | "vale";
752
+
753
+ export interface LinterPolicy {
754
+ runnerNames: LintRunnerName[];
755
+ preferredRunners: LintRunnerName[];
756
+ defaultRunner?: LintRunnerName;
757
+ defaultWhenUnconfigured: boolean;
758
+ gate: ToolGate;
759
+ }
760
+
761
+ export interface AutofixPolicy {
762
+ toolNames: AutofixToolName[];
763
+ preferredTools: AutofixToolName[];
764
+ defaultTool?: AutofixToolName;
765
+ defaultWhenUnconfigured: boolean;
766
+ gate: ToolGate;
767
+ safe: boolean;
768
+ }
769
+
770
+ export interface AutofixCapability {
771
+ toolSupportsFix: boolean;
772
+ safePipelineAutofix: boolean;
773
+ fixKind: "pipeline" | "manual" | "suggestion" | "none";
774
+ }
775
+
776
+ export interface ToolExecutionPolicy {
777
+ gate: ToolGate;
778
+ autoInstall: boolean;
779
+ }
780
+
781
+ export interface ToolCommandSpec {
782
+ command: string;
783
+ windowsExt?: string;
784
+ versionArgs?: string[];
785
+ managedToolId?: string;
786
+ }
787
+
788
+ const AUTOFIX_CAPABILITIES = new Map<string, AutofixCapability>([
789
+ [
790
+ "biome",
791
+ { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" },
792
+ ],
793
+ [
794
+ "eslint",
795
+ { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" },
796
+ ],
797
+ [
798
+ "ruff",
799
+ { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" },
800
+ ],
801
+ [
802
+ "stylelint",
803
+ { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" },
804
+ ],
805
+ [
806
+ "sqlfluff",
807
+ { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" },
808
+ ],
809
+ [
810
+ "rubocop",
811
+ { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" },
812
+ ],
813
+ [
814
+ "ktlint",
815
+ { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" },
816
+ ],
817
+ [
818
+ "rust-clippy",
819
+ { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" },
820
+ ],
821
+ [
822
+ "dart-analyze",
823
+ { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" },
824
+ ],
825
+ ]);
826
+
827
+ const TOOL_EXECUTION_POLICY = new Map<string, ToolExecutionPolicy>([
828
+ ["biome", { gate: "smart-default", autoInstall: true }],
829
+ ["ruff", { gate: "smart-default", autoInstall: true }],
830
+ ["oxlint", { gate: "smart-default", autoInstall: true }],
831
+ ["stylelint", { gate: "smart-default", autoInstall: true }],
832
+ ["sqlfluff", { gate: "smart-default", autoInstall: true }],
833
+ ["rubocop", { gate: "smart-default", autoInstall: true }],
834
+ ["yamllint", { gate: "smart-default", autoInstall: true }],
835
+ ["actionlint", { gate: "smart-default", autoInstall: true }],
836
+ ["markdownlint", { gate: "smart-default", autoInstall: true }],
837
+ ["mypy", { gate: "config-first", autoInstall: true }],
838
+ ["taplo", { gate: "smart-default", autoInstall: true }],
839
+ ["hadolint", { gate: "smart-default", autoInstall: true }],
840
+ ["htmlhint", { gate: "smart-default", autoInstall: true }],
841
+ ["ktlint", { gate: "smart-default", autoInstall: true }],
842
+ ["golangci-lint", { gate: "config-first", autoInstall: true }],
843
+ ["phpstan", { gate: "config-first", autoInstall: false }],
844
+ ["eslint", { gate: "config-first", autoInstall: false }],
845
+ ["prettier", { gate: "smart-default", autoInstall: true }],
846
+ ["vale", { gate: "config-first", autoInstall: false }],
847
+ ["swiftlint", { gate: "smart-default", autoInstall: true }],
848
+ ]);
849
+
850
+ const TOOL_COMMAND_SPECS = new Map<string, ToolCommandSpec>([
851
+ [
852
+ "eslint",
853
+ {
854
+ command: "eslint",
855
+ windowsExt: ".cmd",
856
+ versionArgs: ["--version"],
857
+ managedToolId: "eslint",
858
+ },
859
+ ],
860
+ [
861
+ "stylelint",
862
+ {
863
+ command: "stylelint",
864
+ windowsExt: ".cmd",
865
+ versionArgs: ["--version"],
866
+ managedToolId: "stylelint",
867
+ },
868
+ ],
869
+ [
870
+ "sqlfluff",
871
+ {
872
+ command: "sqlfluff",
873
+ windowsExt: ".exe",
874
+ versionArgs: ["--version"],
875
+ managedToolId: "sqlfluff",
876
+ },
877
+ ],
878
+ [
879
+ "oxlint",
880
+ {
881
+ command: "oxlint",
882
+ windowsExt: ".exe",
883
+ versionArgs: ["--version"],
884
+ managedToolId: "oxlint",
885
+ },
886
+ ],
887
+ [
888
+ "ruff",
889
+ {
890
+ command: "ruff",
891
+ windowsExt: ".exe",
892
+ versionArgs: ["--version"],
893
+ managedToolId: "ruff",
894
+ },
895
+ ],
896
+ [
897
+ "biome",
898
+ {
899
+ command: "biome",
900
+ windowsExt: ".cmd",
901
+ versionArgs: ["--version"],
902
+ managedToolId: "biome",
903
+ },
904
+ ],
905
+ [
906
+ "rubocop",
907
+ {
908
+ command: "rubocop",
909
+ versionArgs: ["--version"],
910
+ managedToolId: "rubocop",
911
+ },
912
+ ],
913
+ [
914
+ "yamllint",
915
+ {
916
+ command: "yamllint",
917
+ windowsExt: ".exe",
918
+ versionArgs: ["--version"],
919
+ managedToolId: "yamllint",
920
+ },
921
+ ],
922
+ [
923
+ "actionlint",
924
+ {
925
+ command: "actionlint",
926
+ windowsExt: ".exe",
927
+ versionArgs: ["--version"],
928
+ managedToolId: "actionlint",
929
+ },
930
+ ],
931
+ [
932
+ "markdownlint",
933
+ {
934
+ command: "markdownlint-cli2",
935
+ windowsExt: ".cmd",
936
+ versionArgs: ["--version"],
937
+ managedToolId: "markdownlint",
938
+ },
939
+ ],
940
+ [
941
+ "vale",
942
+ {
943
+ command: "vale",
944
+ windowsExt: ".exe",
945
+ versionArgs: ["--version"],
946
+ managedToolId: "vale",
947
+ },
948
+ ],
949
+ [
950
+ "swiftlint",
951
+ {
952
+ command: "swiftlint",
953
+ versionArgs: ["--version"],
954
+ managedToolId: "swiftlint",
955
+ },
956
+ ],
957
+ [
958
+ "mypy",
959
+ {
960
+ command: "mypy",
961
+ versionArgs: ["--version"],
962
+ managedToolId: "mypy",
963
+ },
964
+ ],
965
+ [
966
+ "phpstan",
967
+ {
968
+ command: "phpstan",
969
+ windowsExt: ".bat",
970
+ versionArgs: ["--version"],
971
+ managedToolId: "phpstan",
972
+ },
973
+ ],
974
+ [
975
+ "taplo",
976
+ {
977
+ command: "taplo",
978
+ windowsExt: ".exe",
979
+ versionArgs: ["--version"],
980
+ managedToolId: "taplo",
981
+ },
982
+ ],
983
+ [
984
+ "hadolint",
985
+ {
986
+ command: "hadolint",
987
+ windowsExt: ".exe",
988
+ versionArgs: ["--version"],
989
+ managedToolId: "hadolint",
990
+ },
991
+ ],
992
+ [
993
+ "htmlhint",
994
+ {
995
+ command: "htmlhint",
996
+ versionArgs: ["--version"],
997
+ managedToolId: "htmlhint",
998
+ },
999
+ ],
1000
+ [
1001
+ "ktlint",
1002
+ {
1003
+ command: "ktlint",
1004
+ windowsExt: ".exe",
1005
+ versionArgs: ["--version"],
1006
+ managedToolId: "ktlint",
1007
+ },
1008
+ ],
1009
+ [
1010
+ "prettier",
1011
+ {
1012
+ command: "prettier",
1013
+ windowsExt: ".cmd",
1014
+ versionArgs: ["--version"],
1015
+ managedToolId: "prettier",
1016
+ },
1017
+ ],
1018
+ ]);
1019
+
1020
+ const STYLELINT_CONFIGS = [
1021
+ ".stylelintrc",
1022
+ ".stylelintrc.json",
1023
+ ".stylelintrc.jsonc",
1024
+ ".stylelintrc.yaml",
1025
+ ".stylelintrc.yml",
1026
+ ".stylelintrc.js",
1027
+ ".stylelintrc.cjs",
1028
+ "stylelint.config.js",
1029
+ "stylelint.config.cjs",
1030
+ "stylelint.config.mjs",
1031
+ ];
1032
+
1033
+ const SQLFLUFF_CONFIGS = [
1034
+ ".sqlfluff",
1035
+ "pyproject.toml",
1036
+ "setup.cfg",
1037
+ "tox.ini",
1038
+ ];
1039
+
1040
+ const RUBOCOP_CONFIGS = [".rubocop.yml", ".rubocop.yaml"];
1041
+
1042
+ const MYPY_CONFIGS = ["mypy.ini", ".mypy.ini", "setup.cfg", "pyproject.toml"];
1043
+
1044
+ const YAMLLINT_CONFIGS = [
1045
+ ".yamllint",
1046
+ ".yamllint.yml",
1047
+ ".yamllint.yaml",
1048
+ "pyproject.toml",
1049
+ "setup.cfg",
1050
+ "tox.ini",
1051
+ ];
1052
+
1053
+ const MARKDOWNLINT_CONFIGS = [
1054
+ ".markdownlint.json",
1055
+ ".markdownlint.jsonc",
1056
+ ".markdownlint.yaml",
1057
+ ".markdownlint.yml",
1058
+ ".markdownlintrc",
1059
+ ];
1060
+
1061
+ const PRETTIER_CONFIGS = [
1062
+ ".prettierrc",
1063
+ ".prettierrc.json",
1064
+ ".prettierrc.yml",
1065
+ ".prettierrc.yaml",
1066
+ ".prettierrc.js",
1067
+ ".prettierrc.cjs",
1068
+ ".prettierrc.mjs",
1069
+ "prettier.config.js",
1070
+ "prettier.config.cjs",
1071
+ "prettier.config.mjs",
1072
+ "prettier.config.ts",
1073
+ ];
1074
+
1075
+ const RUFF_PROJECT_CONFIGS = ["ruff.toml", ".ruff.toml"];
1076
+
1077
+ const GOLANGCI_CONFIGS = [
1078
+ ".golangci.yml",
1079
+ ".golangci.yaml",
1080
+ ".golangci.toml",
1081
+ ".golangci.json",
1082
+ ];
1083
+
1084
+ const PHPSTAN_CONFIGS = [
1085
+ "phpstan.neon",
1086
+ "phpstan.neon.dist",
1087
+ "phpstan.dist.neon",
1088
+ ];
1089
+
1090
+ const VITE_CONFIGS = [
1091
+ "vite.config.ts",
1092
+ "vite.config.mts",
1093
+ "vite.config.cts",
1094
+ "vite.config.js",
1095
+ "vite.config.mjs",
1096
+ "vite.config.cjs",
1097
+ ];
1098
+
1099
+ export type JstsLintRunnerName = "eslint" | "oxlint" | "biome-check-json";
1100
+
1101
+ export interface JstsLintPolicyContext {
1102
+ hasEslintConfig?: boolean;
1103
+ hasOxlintConfig?: boolean;
1104
+ hasBiomeConfig?: boolean;
1105
+ }
1106
+
1107
+ export interface JstsLintPolicy extends Required<JstsLintPolicyContext> {
1108
+ preferredRunners: JstsLintRunnerName[];
1109
+ hasExplicitNonBiomeLinter: boolean;
1110
+ }
1111
+
1112
+ export interface LinterPolicyContext {
1113
+ hasEslintConfig?: boolean;
1114
+ hasOxlintConfig?: boolean;
1115
+ hasBiomeConfig?: boolean;
1116
+ hasStylelintConfig?: boolean;
1117
+ hasSqlfluffConfig?: boolean;
1118
+ hasRubocopConfig?: boolean;
1119
+ hasYamllintConfig?: boolean;
1120
+ hasMarkdownlintConfig?: boolean;
1121
+ hasGolangciConfig?: boolean;
1122
+ hasPhpstanConfig?: boolean;
1123
+ hasMypyConfig?: boolean;
1124
+ hasDetektConfig?: boolean;
1125
+ }
1126
+
1127
+ export interface AutofixPolicyContext {
1128
+ hasEslintConfig?: boolean;
1129
+ hasStylelintConfig?: boolean;
1130
+ hasSqlfluffConfig?: boolean;
1131
+ hasRubocopConfig?: boolean;
1132
+ hasBiomeConfig?: boolean;
1133
+ }
1134
+
1135
+ export function getLinterPolicyForFile(
1136
+ filePath: string,
1137
+ context: LinterPolicyContext = {},
1138
+ ): LinterPolicy | undefined {
1139
+ const ext = path.extname(filePath).toLowerCase();
1140
+
1141
+ if ([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"].includes(ext)) {
1142
+ const policy = getJstsLintPolicy({
1143
+ hasEslintConfig: context.hasEslintConfig,
1144
+ hasOxlintConfig: context.hasOxlintConfig,
1145
+ hasBiomeConfig: context.hasBiomeConfig,
1146
+ });
1147
+ return {
1148
+ runnerNames: ["eslint", "oxlint", "biome-check-json"],
1149
+ preferredRunners: policy.preferredRunners,
1150
+ defaultRunner: policy.preferredRunners[0],
1151
+ defaultWhenUnconfigured:
1152
+ !policy.hasEslintConfig && !policy.hasOxlintConfig,
1153
+ gate: policy.hasEslintConfig ? "config-first" : "smart-default",
1154
+ };
1155
+ }
1156
+
1157
+ if ([".py", ".pyi"].includes(ext)) {
1158
+ const preferredRunners: LintRunnerName[] = ["ruff-lint"];
1159
+ if (context.hasMypyConfig) preferredRunners.push("mypy");
1160
+ return {
1161
+ runnerNames: ["ruff-lint", "mypy"],
1162
+ preferredRunners,
1163
+ defaultRunner: "ruff-lint",
1164
+ defaultWhenUnconfigured: true,
1165
+ gate: context.hasMypyConfig ? "mixed" : "smart-default",
1166
+ };
1167
+ }
1168
+
1169
+ if ([".css", ".scss", ".sass", ".less"].includes(ext)) {
1170
+ return {
1171
+ runnerNames: ["stylelint"],
1172
+ preferredRunners: ["stylelint"],
1173
+ defaultRunner: "stylelint",
1174
+ defaultWhenUnconfigured: true,
1175
+ gate: "smart-default",
1176
+ };
1177
+ }
1178
+
1179
+ if (ext === ".sql") {
1180
+ return {
1181
+ runnerNames: ["sqlfluff"],
1182
+ preferredRunners: ["sqlfluff"],
1183
+ defaultRunner: "sqlfluff",
1184
+ defaultWhenUnconfigured: true,
1185
+ gate: "smart-default",
1186
+ };
1187
+ }
1188
+
1189
+ if ([".rb", ".rake", ".gemspec", ".ru"].includes(ext)) {
1190
+ return {
1191
+ runnerNames: ["rubocop"],
1192
+ preferredRunners: ["rubocop"],
1193
+ defaultRunner: "rubocop",
1194
+ defaultWhenUnconfigured: true,
1195
+ gate: "smart-default",
1196
+ };
1197
+ }
1198
+
1199
+ if ([".yaml", ".yml"].includes(ext)) {
1200
+ return {
1201
+ runnerNames: ["yamllint"],
1202
+ preferredRunners: ["yamllint"],
1203
+ defaultRunner: "yamllint",
1204
+ defaultWhenUnconfigured: true,
1205
+ gate: "smart-default",
1206
+ };
1207
+ }
1208
+
1209
+ if ([".md", ".mdx"].includes(ext)) {
1210
+ return {
1211
+ runnerNames: ["markdownlint"],
1212
+ preferredRunners: ["markdownlint"],
1213
+ defaultRunner: "markdownlint",
1214
+ defaultWhenUnconfigured: true,
1215
+ gate: "smart-default",
1216
+ };
1217
+ }
1218
+
1219
+ if ([".html", ".htm"].includes(ext)) {
1220
+ return {
1221
+ runnerNames: ["htmlhint"],
1222
+ preferredRunners: ["htmlhint"],
1223
+ defaultRunner: "htmlhint",
1224
+ defaultWhenUnconfigured: true,
1225
+ gate: "smart-default",
1226
+ };
1227
+ }
1228
+
1229
+ if (path.basename(filePath).toLowerCase() === "dockerfile") {
1230
+ return {
1231
+ runnerNames: ["hadolint"],
1232
+ preferredRunners: ["hadolint"],
1233
+ defaultRunner: "hadolint",
1234
+ defaultWhenUnconfigured: true,
1235
+ gate: "smart-default",
1236
+ };
1237
+ }
1238
+
1239
+ if ([".kt", ".kts"].includes(ext)) {
1240
+ const preferredRunners: LintRunnerName[] = ["ktlint"];
1241
+ if (context.hasDetektConfig) preferredRunners.push("detekt");
1242
+ return {
1243
+ runnerNames: ["ktlint", "detekt"],
1244
+ preferredRunners,
1245
+ defaultRunner: "ktlint",
1246
+ defaultWhenUnconfigured: true,
1247
+ gate: context.hasDetektConfig ? "mixed" : "smart-default",
1248
+ };
1249
+ }
1250
+
1251
+ if (ext === ".toml") {
1252
+ return {
1253
+ runnerNames: ["taplo"],
1254
+ preferredRunners: ["taplo"],
1255
+ defaultRunner: "taplo",
1256
+ defaultWhenUnconfigured: true,
1257
+ gate: "smart-default",
1258
+ };
1259
+ }
1260
+
1261
+ if (ext === ".go") {
1262
+ return {
1263
+ runnerNames: ["golangci-lint"],
1264
+ preferredRunners: context.hasGolangciConfig ? ["golangci-lint"] : [],
1265
+ defaultRunner: "golangci-lint",
1266
+ defaultWhenUnconfigured: false,
1267
+ gate: "config-first",
1268
+ };
1269
+ }
1270
+
1271
+ if (ext === ".php") {
1272
+ return {
1273
+ runnerNames: ["phpstan"],
1274
+ preferredRunners: context.hasPhpstanConfig ? ["phpstan"] : [],
1275
+ defaultRunner: "phpstan",
1276
+ defaultWhenUnconfigured: false,
1277
+ gate: "config-first",
1278
+ };
1279
+ }
1280
+
1281
+ if (ext === ".rs") {
1282
+ return {
1283
+ runnerNames: ["rust-clippy"],
1284
+ preferredRunners: ["rust-clippy"],
1285
+ defaultRunner: "rust-clippy",
1286
+ defaultWhenUnconfigured: true,
1287
+ gate: "smart-default",
1288
+ };
1289
+ }
1290
+
1291
+ if ([".sh", ".bash"].includes(ext)) {
1292
+ return {
1293
+ runnerNames: ["shellcheck"],
1294
+ preferredRunners: ["shellcheck"],
1295
+ defaultRunner: "shellcheck",
1296
+ defaultWhenUnconfigured: true,
1297
+ gate: "smart-default",
1298
+ };
1299
+ }
1300
+
1301
+ if (ext === ".fish") {
1302
+ return {
1303
+ runnerNames: ["fish-indent"],
1304
+ preferredRunners: ["fish-indent"],
1305
+ defaultRunner: "fish-indent",
1306
+ defaultWhenUnconfigured: true,
1307
+ gate: "smart-default",
1308
+ };
1309
+ }
1310
+
1311
+ if ([".tf", ".tfvars"].includes(ext)) {
1312
+ return {
1313
+ runnerNames: ["tflint"],
1314
+ preferredRunners: ["tflint"],
1315
+ defaultRunner: "tflint",
1316
+ defaultWhenUnconfigured: true,
1317
+ gate: "smart-default",
1318
+ };
1319
+ }
1320
+
1321
+ if ([".ex", ".exs", ".eex", ".heex", ".leex"].includes(ext)) {
1322
+ return {
1323
+ runnerNames: ["credo"],
1324
+ preferredRunners: ["credo"],
1325
+ defaultRunner: "credo",
1326
+ defaultWhenUnconfigured: true,
1327
+ gate: "smart-default",
1328
+ };
1329
+ }
1330
+
1331
+ if ([".c", ".cc", ".cpp", ".cxx", ".h", ".hpp", ".ino"].includes(ext)) {
1332
+ return {
1333
+ runnerNames: ["cpp-check"],
1334
+ preferredRunners: ["cpp-check"],
1335
+ defaultRunner: "cpp-check",
1336
+ defaultWhenUnconfigured: true,
1337
+ gate: "smart-default",
1338
+ };
1339
+ }
1340
+
1341
+ if (ext === ".dart") {
1342
+ return {
1343
+ runnerNames: ["dart-analyze"],
1344
+ preferredRunners: ["dart-analyze"],
1345
+ defaultRunner: "dart-analyze",
1346
+ defaultWhenUnconfigured: true,
1347
+ gate: "smart-default",
1348
+ };
1349
+ }
1350
+
1351
+ if (ext === ".gleam") {
1352
+ return {
1353
+ runnerNames: ["gleam-check"],
1354
+ preferredRunners: ["gleam-check"],
1355
+ defaultRunner: "gleam-check",
1356
+ defaultWhenUnconfigured: true,
1357
+ gate: "smart-default",
1358
+ };
1359
+ }
1360
+
1361
+ if ([".ps1", ".psm1", ".psd1"].includes(ext)) {
1362
+ return {
1363
+ runnerNames: ["psscriptanalyzer"],
1364
+ preferredRunners: ["psscriptanalyzer"],
1365
+ defaultRunner: "psscriptanalyzer",
1366
+ defaultWhenUnconfigured: true,
1367
+ gate: "smart-default",
1368
+ };
1369
+ }
1370
+
1371
+ if (ext === ".prisma") {
1372
+ return {
1373
+ runnerNames: ["prisma-validate"],
1374
+ preferredRunners: ["prisma-validate"],
1375
+ defaultRunner: "prisma-validate",
1376
+ defaultWhenUnconfigured: true,
1377
+ gate: "smart-default",
1378
+ };
1379
+ }
1380
+
1381
+ return undefined;
1382
+ }
1383
+
1384
+ export function getLinterPolicyForCwd(
1385
+ filePath: string,
1386
+ cwd: string,
1387
+ ): LinterPolicy | undefined {
1388
+ const context: LinterPolicyContext = {
1389
+ hasEslintConfig: hasEslintConfig(cwd),
1390
+ hasOxlintConfig: hasOxlintConfig(cwd),
1391
+ hasBiomeConfig: hasBiomeConfig(cwd),
1392
+ hasStylelintConfig: hasStylelintConfig(cwd),
1393
+ hasSqlfluffConfig: hasSqlfluffConfig(cwd),
1394
+ hasRubocopConfig: hasRubocopConfig(cwd),
1395
+ hasYamllintConfig: hasYamllintConfig(cwd),
1396
+ hasMarkdownlintConfig: hasMarkdownlintConfig(cwd),
1397
+ hasGolangciConfig: hasGolangciConfig(cwd),
1398
+ hasPhpstanConfig: hasPhpstanConfig(cwd),
1399
+ hasMypyConfig: hasMypyConfig(cwd),
1400
+ hasDetektConfig: hasDetektConfig(cwd),
1401
+ };
1402
+ const policy = getLinterPolicyForFile(filePath, context);
1403
+ logLatency({
1404
+ type: "phase",
1405
+ phase: "linter_selected",
1406
+ filePath,
1407
+ durationMs: 0,
1408
+ metadata: {
1409
+ runner: policy?.defaultRunner ?? null,
1410
+ gate: policy?.gate ?? null,
1411
+ cwd,
1412
+ context,
1413
+ },
1414
+ });
1415
+ return policy;
1416
+ }
1417
+
1418
+ export function getAutofixPolicyForFile(
1419
+ filePath: string,
1420
+ context: AutofixPolicyContext = {},
1421
+ ): AutofixPolicy | undefined {
1422
+ const ext = path.extname(filePath).toLowerCase();
1423
+
1424
+ if ([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"].includes(ext)) {
1425
+ if (context.hasEslintConfig) {
1426
+ return {
1427
+ toolNames: ["eslint", "biome"],
1428
+ preferredTools: ["eslint"],
1429
+ defaultTool: "eslint",
1430
+ defaultWhenUnconfigured: false,
1431
+ gate: "config-first",
1432
+ safe: true,
1433
+ };
1434
+ }
1435
+ return {
1436
+ toolNames: ["eslint", "biome"],
1437
+ preferredTools: ["biome"],
1438
+ defaultTool: "biome",
1439
+ defaultWhenUnconfigured: true,
1440
+ gate: "smart-default",
1441
+ safe: true,
1442
+ };
1443
+ }
1444
+
1445
+ if ([".json", ".jsonc"].includes(ext)) {
1446
+ if (!context.hasBiomeConfig) {
1447
+ return undefined;
1448
+ }
1449
+ return {
1450
+ toolNames: ["biome"],
1451
+ preferredTools: ["biome"],
1452
+ defaultTool: "biome",
1453
+ defaultWhenUnconfigured: false,
1454
+ gate: "config-first",
1455
+ safe: true,
1456
+ };
1457
+ }
1458
+
1459
+ if ([".py", ".pyi"].includes(ext)) {
1460
+ return {
1461
+ toolNames: ["ruff"],
1462
+ preferredTools: ["ruff"],
1463
+ defaultTool: "ruff",
1464
+ defaultWhenUnconfigured: true,
1465
+ gate: "smart-default",
1466
+ safe: true,
1467
+ };
1468
+ }
1469
+
1470
+ if ([".css", ".scss", ".sass", ".less"].includes(ext)) {
1471
+ return {
1472
+ toolNames: ["stylelint"],
1473
+ preferredTools: ["stylelint"],
1474
+ defaultTool: "stylelint",
1475
+ defaultWhenUnconfigured: true,
1476
+ gate: "smart-default",
1477
+ safe: true,
1478
+ };
1479
+ }
1480
+
1481
+ if (ext === ".sql") {
1482
+ return {
1483
+ toolNames: ["sqlfluff"],
1484
+ preferredTools: ["sqlfluff"],
1485
+ defaultTool: "sqlfluff",
1486
+ defaultWhenUnconfigured: true,
1487
+ gate: "smart-default",
1488
+ safe: true,
1489
+ };
1490
+ }
1491
+
1492
+ if ([".rb", ".rake", ".gemspec", ".ru"].includes(ext)) {
1493
+ return {
1494
+ toolNames: ["rubocop"],
1495
+ preferredTools: ["rubocop"],
1496
+ defaultTool: "rubocop",
1497
+ defaultWhenUnconfigured: true,
1498
+ gate: "smart-default",
1499
+ safe: true,
1500
+ };
1501
+ }
1502
+
1503
+ if ([".kt", ".kts"].includes(ext)) {
1504
+ return {
1505
+ toolNames: ["ktlint"],
1506
+ preferredTools: ["ktlint"],
1507
+ defaultTool: "ktlint",
1508
+ defaultWhenUnconfigured: true,
1509
+ gate: "smart-default",
1510
+ safe: true,
1511
+ };
1512
+ }
1513
+
1514
+ if (ext === ".rs") {
1515
+ return {
1516
+ toolNames: ["rust-clippy"],
1517
+ preferredTools: ["rust-clippy"],
1518
+ defaultTool: "rust-clippy",
1519
+ defaultWhenUnconfigured: true,
1520
+ gate: "smart-default",
1521
+ safe: true,
1522
+ };
1523
+ }
1524
+
1525
+ if (ext === ".dart") {
1526
+ return {
1527
+ toolNames: ["dart-analyze"],
1528
+ preferredTools: ["dart-analyze"],
1529
+ defaultTool: "dart-analyze",
1530
+ defaultWhenUnconfigured: true,
1531
+ gate: "smart-default",
1532
+ safe: true,
1533
+ };
1534
+ }
1535
+
1536
+ return undefined;
1537
+ }
1538
+
1539
+ export function getPreferredAutofixTools(
1540
+ filePath: string,
1541
+ context: AutofixPolicyContext,
1542
+ ): AutofixToolName[] {
1543
+ return getAutofixPolicyForFile(filePath, context)?.preferredTools ?? [];
1544
+ }
1545
+
1546
+ const ESLINT_CONFIGS = [
1547
+ ".eslintrc",
1548
+ ".eslintrc.js",
1549
+ ".eslintrc.cjs",
1550
+ ".eslintrc.json",
1551
+ ".eslintrc.yaml",
1552
+ ".eslintrc.yml",
1553
+ "eslint.config.js",
1554
+ "eslint.config.mjs",
1555
+ "eslint.config.cjs",
1556
+ "eslint.config.ts",
1557
+ ];
1558
+
1559
+ function walkUpDirs(cwd: string): string[] {
1560
+ const dirs: string[] = [];
1561
+ let dir = cwd;
1562
+ const root = path.parse(dir).root;
1563
+ while (true) {
1564
+ dirs.push(dir);
1565
+ if (dir === root) break;
1566
+ const parent = path.dirname(dir);
1567
+ if (parent === dir) break;
1568
+ dir = parent;
1569
+ }
1570
+ return dirs;
1571
+ }
1572
+
1573
+ function walkUpDirsUntilPackageJson(cwd: string): string[] {
1574
+ const dirs: string[] = [];
1575
+ for (const dir of walkUpDirs(cwd)) {
1576
+ dirs.push(dir);
1577
+ if (fs.existsSync(path.join(dir, "package.json"))) break;
1578
+ }
1579
+ return dirs;
1580
+ }
1581
+
1582
+ function findNearestPackageJsonPath(cwd: string): string | undefined {
1583
+ let dir = cwd;
1584
+ const root = path.parse(dir).root;
1585
+ while (true) {
1586
+ const pkgPath = path.join(dir, "package.json");
1587
+ if (fs.existsSync(pkgPath)) return pkgPath;
1588
+ if (dir === root) break;
1589
+ const parent = path.dirname(dir);
1590
+ if (parent === dir) break;
1591
+ dir = parent;
1592
+ }
1593
+ return undefined;
1594
+ }
1595
+
1596
+ export function hasNearestPackageJsonDependency(
1597
+ cwd: string,
1598
+ dependencyName: string,
1599
+ ): boolean {
1600
+ const pkgPath = findNearestPackageJsonPath(cwd);
1601
+ if (!pkgPath) return false;
1602
+ try {
1603
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")) as {
1604
+ dependencies?: Record<string, string>;
1605
+ devDependencies?: Record<string, string>;
1606
+ };
1607
+ return Boolean(
1608
+ pkg.dependencies?.[dependencyName] ??
1609
+ pkg.devDependencies?.[dependencyName],
1610
+ );
1611
+ } catch {}
1612
+ return false;
1613
+ }
1614
+
1615
+ export function hasNearestPackageJsonField(
1616
+ cwd: string,
1617
+ fieldName: string,
1618
+ ): boolean {
1619
+ const pkgPath = findNearestPackageJsonPath(cwd);
1620
+ if (!pkgPath) return false;
1621
+ try {
1622
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")) as Record<
1623
+ string,
1624
+ unknown
1625
+ >;
1626
+ return pkg[fieldName] !== undefined;
1627
+ } catch {}
1628
+ return false;
1629
+ }
1630
+
1631
+ export function hasEslintConfig(cwd: string): boolean {
1632
+ for (const dir of walkUpDirsUntilPackageJson(cwd)) {
1633
+ for (const cfg of ESLINT_CONFIGS) {
1634
+ if (fs.existsSync(path.join(dir, cfg))) return true;
1635
+ }
1636
+ const pkgPath = path.join(dir, "package.json");
1637
+ if (fs.existsSync(pkgPath)) {
1638
+ try {
1639
+ if (JSON.parse(fs.readFileSync(pkgPath, "utf-8")).eslintConfig)
1640
+ return true;
1641
+ } catch {}
1642
+ }
1643
+ }
1644
+ return false;
1645
+ }
1646
+
1647
+ export function hasBiomeConfig(cwd: string): boolean {
1648
+ return getBiomeConfigPath(cwd) !== undefined;
1649
+ }
1650
+
1651
+ export function getBiomeConfigPath(cwd: string): string | undefined {
1652
+ for (const dir of walkUpDirs(cwd)) {
1653
+ const jsoncPath = path.join(dir, "biome.jsonc");
1654
+ if (fs.existsSync(jsoncPath)) return jsoncPath;
1655
+ const jsonPath = path.join(dir, "biome.json");
1656
+ if (fs.existsSync(jsonPath)) return jsonPath;
1657
+ }
1658
+ return undefined;
1659
+ }
1660
+
1661
+ export function hasOxfmtConfig(cwd: string): boolean {
1662
+ let dir = cwd;
1663
+ const root = path.parse(dir).root;
1664
+ while (true) {
1665
+ if (fs.existsSync(path.join(dir, "oxfmt.toml"))) return true;
1666
+ if (fs.existsSync(path.join(dir, ".oxfmtrc.json"))) return true;
1667
+ if (hasVitePlusConfig(dir)) return true;
1668
+ const pkgPath = path.join(dir, "package.json");
1669
+ if (fs.existsSync(pkgPath)) {
1670
+ try {
1671
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")) as Record<
1672
+ string,
1673
+ unknown
1674
+ >;
1675
+ const deps = {
1676
+ ...(pkg.dependencies as Record<string, unknown> | undefined),
1677
+ ...(pkg.devDependencies as Record<string, unknown> | undefined),
1678
+ };
1679
+ if (deps["@oxc-project/oxfmt"]) return true;
1680
+ } catch {}
1681
+ }
1682
+ if (dir === root) break;
1683
+ const parent = path.dirname(dir);
1684
+ if (parent === dir) break;
1685
+ dir = parent;
1686
+ }
1687
+ return false;
1688
+ }
1689
+
1690
+ export function hasStylelintConfig(cwd: string): boolean {
1691
+ for (const dir of walkUpDirs(cwd)) {
1692
+ if (STYLELINT_CONFIGS.some((cfg) => fs.existsSync(path.join(dir, cfg)))) {
1693
+ return true;
1694
+ }
1695
+ const pkgPath = path.join(dir, "package.json");
1696
+ if (fs.existsSync(pkgPath)) {
1697
+ try {
1698
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
1699
+ if (pkg.stylelint) return true;
1700
+ } catch {}
1701
+ }
1702
+ }
1703
+ return false;
1704
+ }
1705
+
1706
+ export function hasSqlfluffConfig(cwd: string): boolean {
1707
+ for (const dir of walkUpDirs(cwd)) {
1708
+ for (const cfg of SQLFLUFF_CONFIGS) {
1709
+ const cfgPath = path.join(dir, cfg);
1710
+ if (!fs.existsSync(cfgPath)) continue;
1711
+ if (cfg === "pyproject.toml") {
1712
+ try {
1713
+ const content = fs.readFileSync(cfgPath, "utf-8");
1714
+ if (content.includes("[tool.sqlfluff]")) return true;
1715
+ } catch {}
1716
+ continue;
1717
+ }
1718
+ if (cfg === "setup.cfg" || cfg === "tox.ini") {
1719
+ try {
1720
+ const content = fs.readFileSync(cfgPath, "utf-8");
1721
+ if (content.includes("[sqlfluff]")) return true;
1722
+ } catch {}
1723
+ continue;
1724
+ }
1725
+ return true;
1726
+ }
1727
+ }
1728
+
1729
+ for (const dir of walkUpDirs(cwd)) {
1730
+ for (const depFile of ["requirements.txt", "Pipfile", "pyproject.toml"]) {
1731
+ const depPath = path.join(dir, depFile);
1732
+ if (!fs.existsSync(depPath)) continue;
1733
+ try {
1734
+ const content = fs.readFileSync(depPath, "utf-8").toLowerCase();
1735
+ if (content.includes("sqlfluff")) return true;
1736
+ } catch {}
1737
+ }
1738
+ }
1739
+
1740
+ return false;
1741
+ }
1742
+
1743
+ export function hasRubocopConfig(cwd: string): boolean {
1744
+ for (const dir of walkUpDirs(cwd)) {
1745
+ for (const cfg of RUBOCOP_CONFIGS) {
1746
+ if (fs.existsSync(path.join(dir, cfg))) return true;
1747
+ }
1748
+ const gemfile = path.join(dir, "Gemfile");
1749
+ if (fs.existsSync(gemfile)) {
1750
+ try {
1751
+ const content = fs.readFileSync(gemfile, "utf-8");
1752
+ if (content.includes("rubocop")) return true;
1753
+ } catch {}
1754
+ }
1755
+ }
1756
+ return false;
1757
+ }
1758
+
1759
+ export function hasMypyConfig(cwd: string): boolean {
1760
+ for (const dir of walkUpDirs(cwd)) {
1761
+ for (const cfg of MYPY_CONFIGS) {
1762
+ const cfgPath = path.join(dir, cfg);
1763
+ if (!fs.existsSync(cfgPath)) continue;
1764
+ if (cfg === "setup.cfg") {
1765
+ try {
1766
+ if (fs.readFileSync(cfgPath, "utf-8").includes("[mypy]")) return true;
1767
+ } catch {}
1768
+ continue;
1769
+ }
1770
+ if (cfg === "pyproject.toml") {
1771
+ try {
1772
+ if (fs.readFileSync(cfgPath, "utf-8").includes("[tool.mypy]"))
1773
+ return true;
1774
+ } catch {}
1775
+ continue;
1776
+ }
1777
+ return true;
1778
+ }
1779
+ }
1780
+ return false;
1781
+ }
1782
+
1783
+ export function hasYamllintConfig(cwd: string): boolean {
1784
+ for (const dir of walkUpDirs(cwd)) {
1785
+ for (const cfg of YAMLLINT_CONFIGS) {
1786
+ const cfgPath = path.join(dir, cfg);
1787
+ if (!fs.existsSync(cfgPath)) continue;
1788
+ if (cfg === "pyproject.toml") {
1789
+ try {
1790
+ const content = fs.readFileSync(cfgPath, "utf-8");
1791
+ if (content.includes("[tool.yamllint]")) return true;
1792
+ } catch {}
1793
+ continue;
1794
+ }
1795
+ if (cfg === "setup.cfg" || cfg === "tox.ini") {
1796
+ try {
1797
+ const content = fs.readFileSync(cfgPath, "utf-8");
1798
+ if (content.includes("[yamllint]")) return true;
1799
+ } catch {}
1800
+ continue;
1801
+ }
1802
+ return true;
1803
+ }
1804
+ }
1805
+
1806
+ for (const dir of walkUpDirs(cwd)) {
1807
+ for (const depFile of ["requirements.txt", "Pipfile", "pyproject.toml"]) {
1808
+ const depPath = path.join(dir, depFile);
1809
+ if (!fs.existsSync(depPath)) continue;
1810
+ try {
1811
+ const content = fs.readFileSync(depPath, "utf-8").toLowerCase();
1812
+ if (content.includes("yamllint")) return true;
1813
+ } catch {}
1814
+ }
1815
+ }
1816
+
1817
+ return false;
1818
+ }
1819
+
1820
+ export function hasMarkdownlintConfig(cwd: string): boolean {
1821
+ return walkUpDirs(cwd).some((dir) =>
1822
+ MARKDOWNLINT_CONFIGS.some((cfg) => fs.existsSync(path.join(dir, cfg))),
1823
+ );
1824
+ }
1825
+
1826
+ export function hasPrettierConfig(cwd: string): boolean {
1827
+ for (const dir of walkUpDirs(cwd)) {
1828
+ if (PRETTIER_CONFIGS.some((cfg) => fs.existsSync(path.join(dir, cfg))))
1829
+ return true;
1830
+ const pkgPath = path.join(dir, "package.json");
1831
+ if (fs.existsSync(pkgPath)) {
1832
+ try {
1833
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
1834
+ if (Object.hasOwn(pkg, "prettier")) return true;
1835
+ } catch {}
1836
+ }
1837
+ }
1838
+ return false;
1839
+ }
1840
+
1841
+ export function hasBlackConfig(cwd: string): boolean {
1842
+ for (const dir of walkUpDirs(cwd)) {
1843
+ const pyproject = path.join(dir, "pyproject.toml");
1844
+ if (fs.existsSync(pyproject)) {
1845
+ try {
1846
+ if (fs.readFileSync(pyproject, "utf-8").includes("[tool.black]"))
1847
+ return true;
1848
+ } catch {}
1849
+ }
1850
+ }
1851
+
1852
+ for (const dir of walkUpDirs(cwd)) {
1853
+ for (const depFile of ["requirements.txt", "Pipfile"]) {
1854
+ const depPath = path.join(dir, depFile);
1855
+ if (!fs.existsSync(depPath)) continue;
1856
+ try {
1857
+ if (fs.readFileSync(depPath, "utf-8").toLowerCase().includes("black"))
1858
+ return true;
1859
+ } catch {}
1860
+ }
1861
+ }
1862
+
1863
+ return false;
1864
+ }
1865
+
1866
+ export function hasRuffConfig(cwd: string): boolean {
1867
+ for (const dir of walkUpDirs(cwd)) {
1868
+ for (const cfg of RUFF_PROJECT_CONFIGS) {
1869
+ if (fs.existsSync(path.join(dir, cfg))) return true;
1870
+ }
1871
+ const pyproject = path.join(dir, "pyproject.toml");
1872
+ if (fs.existsSync(pyproject)) {
1873
+ try {
1874
+ if (fs.readFileSync(pyproject, "utf-8").includes("[tool.ruff]"))
1875
+ return true;
1876
+ } catch {}
1877
+ }
1878
+ }
1879
+ return false;
1880
+ }
1881
+
1882
+ export function hasGolangciConfig(cwd: string): boolean {
1883
+ return walkUpDirs(cwd).some((dir) =>
1884
+ GOLANGCI_CONFIGS.some((cfg) => fs.existsSync(path.join(dir, cfg))),
1885
+ );
1886
+ }
1887
+
1888
+ export function hasClangFormatConfig(cwd: string): boolean {
1889
+ return walkUpDirs(cwd).some((dir) =>
1890
+ [".clang-format", "_clang-format"].some((cfg) =>
1891
+ fs.existsSync(path.join(dir, cfg)),
1892
+ ),
1893
+ );
1894
+ }
1895
+
1896
+ export function hasPhpCsFixerConfig(cwd: string): boolean {
1897
+ return walkUpDirs(cwd).some((dir) =>
1898
+ [".php-cs-fixer.php", ".php-cs-fixer.dist.php"].some((cfg) =>
1899
+ fs.existsSync(path.join(dir, cfg)),
1900
+ ),
1901
+ );
1902
+ }
1903
+
1904
+ export function hasStyluaConfig(cwd: string): boolean {
1905
+ return walkUpDirs(cwd).some((dir) =>
1906
+ ["stylua.toml", ".stylua.toml"].some((cfg) =>
1907
+ fs.existsSync(path.join(dir, cfg)),
1908
+ ),
1909
+ );
1910
+ }
1911
+
1912
+ export function hasOcamlformatConfig(cwd: string): boolean {
1913
+ return walkUpDirs(cwd).some((dir) =>
1914
+ fs.existsSync(path.join(dir, ".ocamlformat")),
1915
+ );
1916
+ }
1917
+
1918
+ export function hasGoogleJavaFormatConfig(cwd: string): boolean {
1919
+ // google-java-format has no standard config file — gate on .editorconfig
1920
+ // with indent_size defined (common Java project signal) or explicit opt-in marker.
1921
+ return walkUpDirs(cwd).some(
1922
+ (dir) =>
1923
+ fs.existsSync(path.join(dir, ".google-java-format")) ||
1924
+ fs.existsSync(path.join(dir, ".editorconfig")),
1925
+ );
1926
+ }
1927
+
1928
+ export function hasCljfmtConfig(cwd: string): boolean {
1929
+ return walkUpDirs(cwd).some((dir) =>
1930
+ [".cljfmt.edn", "cljfmt.edn", ".cljfmt"].some((cfg) =>
1931
+ fs.existsSync(path.join(dir, cfg)),
1932
+ ),
1933
+ );
1934
+ }
1935
+
1936
+ export function hasCmakeFormatConfig(cwd: string): boolean {
1937
+ return walkUpDirs(cwd).some((dir) =>
1938
+ [
1939
+ ".cmake-format",
1940
+ ".cmake-format.yaml",
1941
+ ".cmake-format.yml",
1942
+ ".cmake-format.json",
1943
+ ".cmake-format.py",
1944
+ "cmake-format.yaml",
1945
+ "cmake-format.yml",
1946
+ ].some((cfg) => fs.existsSync(path.join(dir, cfg))),
1947
+ );
1948
+ }
1949
+
1950
+ export function hasPhpstanConfig(cwd: string): boolean {
1951
+ return walkUpDirs(cwd).some((dir) =>
1952
+ PHPSTAN_CONFIGS.some((cfg) => fs.existsSync(path.join(dir, cfg))),
1953
+ );
1954
+ }
1955
+
1956
+ const DETEKT_CONFIGS = [
1957
+ "detekt.yml",
1958
+ ".detekt.yml",
1959
+ path.join("config", "detekt", "detekt.yml"),
1960
+ path.join("detekt", "detekt.yml"),
1961
+ ];
1962
+
1963
+ export function hasDetektConfig(cwd: string): boolean {
1964
+ for (const dir of walkUpDirs(cwd)) {
1965
+ if (DETEKT_CONFIGS.some((cfg) => fs.existsSync(path.join(dir, cfg))))
1966
+ return true;
1967
+ }
1968
+ return false;
1969
+ }
1970
+
1971
+ export function hasStandardrbConfig(cwd: string): boolean {
1972
+ for (const dir of walkUpDirs(cwd)) {
1973
+ const gemfile = path.join(dir, "Gemfile");
1974
+ if (fs.existsSync(gemfile)) {
1975
+ try {
1976
+ if (fs.readFileSync(gemfile, "utf-8").includes("standard")) return true;
1977
+ } catch {}
1978
+ }
1979
+ }
1980
+ return false;
1981
+ }
1982
+
1983
+ export function getRubocopCommand(cwd: string): {
1984
+ cmd: string;
1985
+ args: string[];
1986
+ } {
1987
+ const gemfile = path.join(cwd, "Gemfile");
1988
+ if (fs.existsSync(gemfile)) {
1989
+ try {
1990
+ const content = fs.readFileSync(gemfile, "utf-8");
1991
+ if (content.includes("rubocop")) {
1992
+ return { cmd: "bundle", args: ["exec", "rubocop"] };
1993
+ }
1994
+ } catch {}
1995
+ }
1996
+ return { cmd: "rubocop", args: [] };
1997
+ }
1998
+
1999
+ export function hasVitePlusConfig(cwd: string): boolean {
2000
+ for (const dir of walkUpDirs(cwd)) {
2001
+ if (fs.existsSync(path.join(dir, "vite-plus.json"))) return true;
2002
+ const pkgPath = path.join(dir, "package.json");
2003
+ if (fs.existsSync(pkgPath)) {
2004
+ try {
2005
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")) as {
2006
+ dependencies?: Record<string, string>;
2007
+ devDependencies?: Record<string, string>;
2008
+ };
2009
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
2010
+ if (deps["vite-plus"] || deps["@voidzero-dev/vite-plus-core"]) {
2011
+ return true;
2012
+ }
2013
+ } catch {}
2014
+ }
2015
+ for (const cfg of VITE_CONFIGS) {
2016
+ const cfgPath = path.join(dir, cfg);
2017
+ if (!fs.existsSync(cfgPath)) continue;
2018
+ try {
2019
+ const content = fs.readFileSync(cfgPath, "utf-8");
2020
+ if (content.includes("vite-plus")) return true;
2021
+ } catch {}
2022
+ }
2023
+ }
2024
+ return false;
2025
+ }
2026
+
2027
+ export function hasOxlintConfig(cwd: string): boolean {
2028
+ for (const dir of walkUpDirsUntilPackageJson(cwd)) {
2029
+ if (
2030
+ fs.existsSync(path.join(dir, ".oxlintrc.json")) ||
2031
+ fs.existsSync(path.join(dir, "oxlint.json"))
2032
+ )
2033
+ return true;
2034
+ }
2035
+ return hasVitePlusConfig(cwd);
2036
+ }
2037
+
2038
+ export function getPreferredJstsLintRunners(
2039
+ context: JstsLintPolicyContext,
2040
+ ): JstsLintRunnerName[] {
2041
+ if (context.hasEslintConfig) return ["eslint"];
2042
+ if (context.hasOxlintConfig) return ["oxlint"];
2043
+ if (context.hasBiomeConfig) return ["biome-check-json"];
2044
+ return ["oxlint", "biome-check-json"];
2045
+ }
2046
+
2047
+ export function getJstsLintPolicy(
2048
+ context: JstsLintPolicyContext,
2049
+ ): JstsLintPolicy {
2050
+ const hasEslint = !!context.hasEslintConfig;
2051
+ const hasOxlint = !!context.hasOxlintConfig;
2052
+ const hasBiome = !!context.hasBiomeConfig;
2053
+ return {
2054
+ hasEslintConfig: hasEslint,
2055
+ hasOxlintConfig: hasOxlint,
2056
+ hasBiomeConfig: hasBiome,
2057
+ preferredRunners: getPreferredJstsLintRunners({
2058
+ hasEslintConfig: hasEslint,
2059
+ hasOxlintConfig: hasOxlint,
2060
+ hasBiomeConfig: hasBiome,
2061
+ }),
2062
+ hasExplicitNonBiomeLinter: hasEslint || hasOxlint,
2063
+ };
2064
+ }
2065
+
2066
+ export function getJstsLintPolicyForCwd(cwd: string): JstsLintPolicy {
2067
+ return getJstsLintPolicy({
2068
+ hasEslintConfig: hasEslintConfig(cwd),
2069
+ hasOxlintConfig: hasOxlintConfig(cwd),
2070
+ hasBiomeConfig: hasBiomeConfig(cwd),
2071
+ });
2072
+ }