ultimate-pi 0.18.1 → 0.19.1

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 (325) hide show
  1. package/.agents/skills/harness-debate-plan/SKILL.md +1 -1
  2. package/.agents/skills/harness-decisions/SKILL.md +1 -2
  3. package/.agents/skills/harness-governor/SKILL.md +6 -5
  4. package/.agents/skills/web-retrieval/SKILL.md +163 -0
  5. package/.agents/skills/wiki-autoresearch/SKILL.md +6 -6
  6. package/.pi/PACKAGING.md +4 -4
  7. package/.pi/SYSTEM.md +75 -123
  8. package/.pi/agents/harness/incident-recorder.md +0 -1
  9. package/.pi/agents/harness/planning/decompose.md +0 -2
  10. package/.pi/agents/harness/planning/execution-plan-author.md +0 -2
  11. package/.pi/agents/harness/planning/hypothesis-validator.md +0 -2
  12. package/.pi/agents/harness/planning/hypothesis.md +0 -2
  13. package/.pi/agents/harness/planning/implementation-researcher.md +1 -3
  14. package/.pi/agents/harness/planning/plan-adversary.md +0 -2
  15. package/.pi/agents/harness/planning/plan-evaluator.md +1 -3
  16. package/.pi/agents/harness/planning/planning-context.md +0 -2
  17. package/.pi/agents/harness/planning/review-integrator.md +0 -2
  18. package/.pi/agents/harness/planning/sprint-contract-auditor.md +0 -2
  19. package/.pi/agents/harness/planning/stack-researcher.md +5 -3
  20. package/.pi/agents/harness/reviewing/adversary.md +0 -2
  21. package/.pi/agents/harness/reviewing/evaluator.md +0 -2
  22. package/.pi/agents/harness/reviewing/tie-breaker.md +0 -2
  23. package/.pi/agents/harness/running/executor.md +0 -2
  24. package/.pi/agents/harness/sentrux-bootstrap.md +0 -1
  25. package/.pi/agents/harness/sentrux-steward.md +0 -2
  26. package/.pi/agents/harness/trace-librarian.md +0 -1
  27. package/.pi/agents/harness/web-retrieval/web-answerer.md +35 -0
  28. package/.pi/agents/harness/web-retrieval/web-criteria-verifier.md +28 -0
  29. package/.pi/agents/harness/web-retrieval/web-gap-analyzer.md +31 -0
  30. package/.pi/agents/harness/web-retrieval/web-query-expander-fast.md +34 -0
  31. package/.pi/agents/harness/web-retrieval/web-query-expander.md +60 -0
  32. package/.pi/agents/harness/web-retrieval/web-summarizer.md +18 -0
  33. package/.pi/extensions/agt-kill-switch.ts +57 -0
  34. package/.pi/extensions/agt-prompt-guard.ts +32 -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 +6 -6
  39. package/.pi/extensions/harness-ask-user.ts +7 -7
  40. package/.pi/extensions/harness-debate-tools.ts +26 -42
  41. package/.pi/extensions/harness-lens.ts +94 -0
  42. package/.pi/extensions/harness-plan-approval.ts +11 -11
  43. package/.pi/extensions/harness-run-context.ts +1070 -876
  44. package/.pi/extensions/harness-subagent-governance.ts +8 -0
  45. package/.pi/extensions/harness-subagent-submit.ts +34 -163
  46. package/.pi/extensions/harness-subagents.ts +3 -3
  47. package/.pi/extensions/harness-telemetry.ts +2 -2
  48. package/.pi/extensions/harness-web-guard.ts +2 -1
  49. package/.pi/extensions/harness-web-tools.ts +691 -53
  50. package/.pi/extensions/policy-gate.ts +25 -5
  51. package/.pi/extensions/sentrux-rules-sync.ts +1 -1
  52. package/.pi/extensions/subagent-governance.ts +92 -0
  53. package/.pi/extensions/trace-recorder.ts +1 -1
  54. package/.pi/extensions/{ultimate-pi-vcc.ts → vcc-compaction.ts} +1 -1
  55. package/.pi/harness/README.md +6 -2
  56. package/.pi/harness/agents.manifest.json +46 -25
  57. package/.pi/harness/agents.policy.yaml +309 -0
  58. package/.pi/harness/docs/adrs/0030-inhouse-vcc-compaction.md +1 -1
  59. package/.pi/harness/docs/adrs/0035-plan-phase-review-gate.md +1 -1
  60. package/.pi/harness/docs/adrs/0045-harness-lens-minimal-contract.md +49 -0
  61. package/.pi/harness/docs/adrs/0046-agt-policy-engine.md +51 -0
  62. package/.pi/harness/docs/adrs/0047-agt-layered-security.md +39 -0
  63. package/.pi/harness/docs/adrs/0048-tool-call-hook-order.md +25 -0
  64. package/.pi/harness/docs/adrs/0049-agents-policy-manifest.md +36 -0
  65. package/.pi/harness/docs/adrs/0050-agentic-web-retrieval-stack.md +46 -0
  66. package/.pi/harness/docs/adrs/README.md +5 -0
  67. package/.pi/harness/docs/harness-web-search.md +97 -0
  68. package/.pi/harness/env.harness.template +9 -1
  69. package/.pi/harness/evolution/README.md +1 -2
  70. package/.pi/harness/examples/agents.policy.project.yaml +19 -0
  71. package/.pi/harness/examples/policies/custom-deny-bash.yaml +9 -0
  72. package/.pi/harness/examples/web-heuristic-angles.project.yaml +22 -0
  73. package/.pi/harness/policies/bash-denylists.yaml +5 -0
  74. package/.pi/harness/policies/defaults.yaml +51 -0
  75. package/.pi/harness/policies/orchestrator.yaml +18 -0
  76. package/.pi/harness/policies/phases.yaml +10 -0
  77. package/.pi/harness/policies/roles.yaml +5 -0
  78. package/.pi/harness/policies/web-guard.yaml +5 -0
  79. package/.pi/harness/policies/workflow-sequences.yaml +9 -0
  80. package/.pi/harness/sentrux/architecture.manifest.json +26 -4
  81. package/.pi/harness/specs/observation.schema.json +2 -1
  82. package/.pi/harness/web-heuristic-angles.json +278 -0
  83. package/.pi/harness/web-heuristic-angles.yaml +182 -0
  84. package/.pi/lib/agents-policy.d.mts +70 -0
  85. package/.pi/lib/agents-policy.mjs +331 -0
  86. package/.pi/lib/agents-policy.ts +19 -0
  87. package/.pi/lib/agt/audit-run-sink.ts +52 -0
  88. package/.pi/lib/agt/build-evaluation-context.ts +285 -0
  89. package/.pi/lib/agt/config.ts +28 -0
  90. package/.pi/lib/agt/delegation.ts +69 -0
  91. package/.pi/lib/agt/evaluate-policy.ts +56 -0
  92. package/.pi/lib/agt/identity-registry.ts +41 -0
  93. package/.pi/lib/agt/index.ts +55 -0
  94. package/.pi/lib/agt/kill-switch-state.ts +11 -0
  95. package/.pi/lib/agt/legacy-evaluate.ts +101 -0
  96. package/.pi/lib/agt/policy-engine.ts +154 -0
  97. package/.pi/lib/agt/rings.ts +21 -0
  98. package/.pi/lib/agt/sre-hooks.ts +45 -0
  99. package/.pi/lib/agt/trust-run-store.ts +26 -0
  100. package/.pi/lib/agt/workflow-history.ts +29 -0
  101. package/.pi/lib/agt-governance-active.ts +14 -0
  102. package/.pi/lib/agt-tool-guard.ts +78 -0
  103. package/.pi/lib/ask-user/dialog.ts +314 -0
  104. package/.pi/{extensions/lib → lib}/debate-bus-core.ts +10 -10
  105. package/.pi/{extensions/lib → lib}/debate-bus-state.ts +1 -1
  106. package/.pi/{extensions/lib → lib}/extension-load-guard.ts +13 -2
  107. package/.pi/lib/harness-agt-tool-guard.ts +5 -0
  108. package/.pi/{extensions/lib → lib}/harness-artifact-gate.ts +1 -1
  109. package/.pi/lib/harness-debate-core-deps.ts +14 -0
  110. package/.pi/lib/harness-debate-workflow-deps.ts +43 -0
  111. package/.pi/lib/harness-lens/.gitattributes +1 -0
  112. package/.pi/lib/harness-lens/clients/edit-autopatch.ts +88 -0
  113. package/.pi/lib/harness-lens/clients/file-kinds.ts +380 -0
  114. package/.pi/lib/harness-lens/clients/file-time.ts +215 -0
  115. package/.pi/lib/harness-lens/clients/file-utils.ts +484 -0
  116. package/.pi/lib/harness-lens/clients/format-service.ts +276 -0
  117. package/.pi/lib/harness-lens/clients/formatters.ts +1000 -0
  118. package/.pi/lib/harness-lens/clients/git-guard.ts +31 -0
  119. package/.pi/lib/harness-lens/clients/indent-retarget.ts +90 -0
  120. package/.pi/lib/harness-lens/clients/installer/index.ts +2368 -0
  121. package/.pi/lib/harness-lens/clients/latency-logger.ts +80 -0
  122. package/.pi/lib/harness-lens/clients/lens-config.ts +43 -0
  123. package/.pi/lib/harness-lens/clients/lens-events.ts +164 -0
  124. package/.pi/lib/harness-lens/clients/lsp/aggregation.ts +91 -0
  125. package/.pi/lib/harness-lens/clients/lsp/client.ts +1466 -0
  126. package/.pi/lib/harness-lens/clients/lsp/config.ts +216 -0
  127. package/.pi/lib/harness-lens/clients/lsp/edits.ts +297 -0
  128. package/.pi/lib/harness-lens/clients/lsp/index.ts +1355 -0
  129. package/.pi/lib/harness-lens/clients/lsp/interactive-install.ts +424 -0
  130. package/.pi/lib/harness-lens/clients/lsp/language.ts +223 -0
  131. package/.pi/lib/harness-lens/clients/lsp/launch.ts +939 -0
  132. package/.pi/lib/harness-lens/clients/lsp/lsp-index.ts +11 -0
  133. package/.pi/lib/harness-lens/clients/lsp/path-utils.ts +12 -0
  134. package/.pi/lib/harness-lens/clients/lsp/server-strategies.ts +81 -0
  135. package/.pi/lib/harness-lens/clients/lsp/server.ts +1971 -0
  136. package/.pi/lib/harness-lens/clients/path-utils.ts +182 -0
  137. package/.pi/lib/harness-lens/clients/pipeline.ts +360 -0
  138. package/.pi/lib/harness-lens/clients/project-profile.ts +117 -0
  139. package/.pi/lib/harness-lens/clients/runtime-agent-end.ts +112 -0
  140. package/.pi/lib/harness-lens/clients/runtime-config.ts +33 -0
  141. package/.pi/lib/harness-lens/clients/runtime-coordinator.ts +186 -0
  142. package/.pi/lib/harness-lens/clients/runtime-tool-result.ts +171 -0
  143. package/.pi/lib/harness-lens/clients/safe-spawn.ts +339 -0
  144. package/.pi/lib/harness-lens/clients/secrets-scanner.ts +214 -0
  145. package/.pi/lib/harness-lens/clients/tool-policy.ts +2072 -0
  146. package/.pi/lib/harness-lens/clients/types.ts +59 -0
  147. package/.pi/lib/harness-lens/clients/widget-state.ts +283 -0
  148. package/.pi/lib/harness-lens/index.ts +532 -0
  149. package/.pi/lib/harness-lens/tools/lsp-diagnostics.ts +706 -0
  150. package/.pi/lib/harness-lens/tools/lsp-navigation.ts +1246 -0
  151. package/.pi/{extensions/lib → lib}/harness-posthog.ts +3 -0
  152. package/.pi/lib/harness-run-context-responses.ts +9 -0
  153. package/.pi/lib/harness-run-context.ts +0 -2
  154. package/.pi/{extensions/lib/spawn-policy.ts → lib/harness-spawn-policy.ts} +1 -0
  155. package/.pi/{extensions/lib → lib}/harness-spawn-topology.ts +1 -1
  156. package/.pi/lib/harness-subagent-auth.ts +81 -0
  157. package/.pi/{extensions/lib → lib}/harness-subagent-precheck.ts +10 -7
  158. package/.pi/{extensions/lib → lib}/harness-subagent-submit-pipeline.ts +3 -3
  159. package/.pi/lib/harness-subagent-submit-register.ts +163 -0
  160. package/.pi/{extensions/lib → lib}/harness-subagent-submit-registry.ts +1 -37
  161. package/.pi/{extensions/lib → lib}/harness-subagents-bridge.ts +74 -14
  162. package/.pi/{extensions/lib → lib}/harness-subprocess-bootstrap.ts +1 -1
  163. package/.pi/lib/harness-web/artifacts.ts +200 -0
  164. package/.pi/lib/harness-web/cache.ts +369 -0
  165. package/.pi/{extensions/lib → lib}/harness-web/run-cli.ts +42 -2
  166. package/.pi/{extensions/lib → lib}/plan-approval/create-plan.ts +2 -2
  167. package/.pi/{extensions/lib → lib}/plan-approval/format-plan.ts +2 -2
  168. package/.pi/{extensions/lib → lib}/plan-approval/plan-review.ts +162 -201
  169. package/.pi/{extensions/lib → lib}/plan-approval/render.ts +1 -1
  170. package/.pi/{extensions/lib → lib}/plan-approval/resolve-disk.ts +2 -2
  171. package/.pi/{extensions/lib → lib}/plan-approval/types.ts +1 -1
  172. package/.pi/{extensions/lib → lib}/plan-approval/validate.ts +3 -3
  173. package/.pi/{extensions/lib → lib}/plan-debate-envelope.ts +1 -1
  174. package/.pi/{extensions/lib → lib}/plan-debate-gate.ts +1 -1
  175. package/.pi/{extensions/lib → lib}/plan-debate-lane.ts +1 -4
  176. package/.pi/{extensions/lib → lib}/plan-messenger.ts +1 -1
  177. package/.pi/prompts/harness-plan.md +2 -1
  178. package/.pi/prompts/harness-setup.md +40 -65
  179. package/.pi/scripts/README.md +2 -5
  180. package/.pi/scripts/gen-web-heuristic-angles-json.mjs +24 -0
  181. package/.pi/scripts/generate-agents-policy-yaml.mjs +148 -0
  182. package/.pi/scripts/harness-agents-manifest.mjs +60 -3
  183. package/.pi/scripts/harness-agt-doctor.ts +36 -0
  184. package/.pi/scripts/harness-cli-verify.sh +14 -2
  185. package/.pi/scripts/harness-verify.mjs +191 -39
  186. package/.pi/scripts/harness-web-policy-guard.mjs +3 -3
  187. package/.pi/scripts/harness-web.py +218 -15
  188. package/.pi/scripts/harness_web/deep_search.py +55 -0
  189. package/.pi/scripts/harness_web/evidence_bundle.py +47 -0
  190. package/.pi/scripts/harness_web/find_similar.py +88 -0
  191. package/.pi/scripts/harness_web/heuristic_angles_shipped.py +85 -0
  192. package/.pi/scripts/harness_web/heuristic_config.py +251 -0
  193. package/.pi/scripts/harness_web/highlights.py +47 -0
  194. package/.pi/scripts/harness_web/multi_search.py +59 -0
  195. package/.pi/scripts/harness_web/output.py +24 -0
  196. package/.pi/scripts/harness_web/query_angles.py +116 -0
  197. package/.pi/scripts/harness_web/rank.py +163 -0
  198. package/.pi/scripts/harness_web/scrape.py +30 -0
  199. package/.pi/scripts/tests/test_harness_web_heuristic_config.py +132 -0
  200. package/.pi/scripts/tests/test_harness_web_query_angles.py +45 -0
  201. package/.pi/scripts/tests/test_harness_web_rank.py +56 -0
  202. package/.pi/scripts/validate-plan-dag.mjs +65 -74
  203. package/.pi/scripts/vendor-pi-vcc-settings.stub.ts +2 -2
  204. package/.pi/scripts/vendor-sync-pi-vcc.sh +1 -1
  205. package/.pi/skills/architecture/broker-domain/SKILL.md +65 -0
  206. package/.pi/skills/architecture/cqrs/SKILL.md +63 -0
  207. package/.pi/skills/architecture/event-driven/SKILL.md +60 -0
  208. package/.pi/skills/architecture/hexagonal-ports-adapters/SKILL.md +66 -0
  209. package/.pi/skills/architecture/layered/SKILL.md +68 -0
  210. package/.pi/skills/architecture/microkernel/SKILL.md +62 -0
  211. package/.pi/skills/architecture/microservices/SKILL.md +64 -0
  212. package/.pi/skills/architecture/modular-monolith/SKILL.md +65 -0
  213. package/.pi/skills/architecture/orchestration-driven-soa/SKILL.md +61 -0
  214. package/.pi/skills/architecture/pipeline/SKILL.md +63 -0
  215. package/.pi/skills/architecture/service-based/SKILL.md +64 -0
  216. package/.pi/skills/architecture/service-mesh/SKILL.md +60 -0
  217. package/.pi/skills/architecture/space-based/SKILL.md +60 -0
  218. package/.pi/skills/ast-grep/SKILL.md +40 -321
  219. package/.pi/skills/delivery/debugging-discipline/SKILL.md +36 -0
  220. package/.pi/skills/delivery/documentation-update/SKILL.md +33 -0
  221. package/.pi/skills/delivery/requirements-to-implementation/SKILL.md +34 -0
  222. package/.pi/skills/delivery/risk-based-verification/SKILL.md +43 -0
  223. package/.pi/skills/delivery/tradeoff-analysis/SKILL.md +34 -0
  224. package/.pi/skills/engineering/api-contract-design/SKILL.md +38 -0
  225. package/.pi/skills/engineering/cohesion-coupling/SKILL.md +43 -0
  226. package/.pi/skills/engineering/complexity-control/SKILL.md +31 -0
  227. package/.pi/skills/engineering/defensive-programming/SKILL.md +38 -0
  228. package/.pi/skills/engineering/dependency-management/SKILL.md +29 -0
  229. package/.pi/skills/engineering/domain-modeling/SKILL.md +32 -0
  230. package/.pi/skills/engineering/error-handling/SKILL.md +37 -0
  231. package/.pi/skills/engineering/legacy-code-seams/SKILL.md +35 -0
  232. package/.pi/skills/engineering/naming-and-intent/SKILL.md +29 -0
  233. package/.pi/skills/engineering/refactoring-safe-evolution/SKILL.md +35 -0
  234. package/.pi/skills/engineering/routine-function-design/SKILL.md +34 -0
  235. package/.pi/skills/engineering/small-change-discipline/SKILL.md +35 -0
  236. package/.pi/skills/lsp-navigation/SKILL.md +89 -0
  237. package/.pi/skills/quality/code-review-self-check/SKILL.md +35 -0
  238. package/.pi/skills/quality/privacy-data-handling/SKILL.md +26 -0
  239. package/.pi/skills/quality/security-review/SKILL.md +34 -0
  240. package/.pi/skills/quality/test-strategy/SKILL.md +33 -0
  241. package/.pi/skills/quality/testability-design/SKILL.md +33 -0
  242. package/.pi/skills/systems/concurrency-safety/SKILL.md +32 -0
  243. package/.pi/skills/systems/data-modeling-migrations/SKILL.md +31 -0
  244. package/.pi/skills/systems/observability-instrumentation/SKILL.md +32 -0
  245. package/.pi/skills/systems/performance-measurement/SKILL.md +35 -0
  246. package/.pi/skills/systems/reliability-design/SKILL.md +32 -0
  247. package/.sentrux/rules.toml +20 -4
  248. package/AGENTS.md +7 -2
  249. package/CHANGELOG.md +20 -0
  250. package/README.md +3 -12
  251. package/THIRD_PARTY_NOTICES.md +12 -21
  252. package/package.json +17 -7
  253. package/vendor/pi-subagents/src/agents.ts +45 -1
  254. package/vendor/pi-subagents/src/subagents.ts +866 -811
  255. package/vendor/pi-vcc/src/core/brief.ts +68 -99
  256. package/vendor/pi-vcc/src/core/settings.ts +2 -2
  257. package/.agents/skills/caveman/SKILL.md +0 -67
  258. package/.agents/skills/scrapling-web/SKILL.md +0 -98
  259. package/.pi/agents/harness/meta-optimizer.md +0 -36
  260. package/.pi/extensions/00-posthog-network-bootstrap.ts +0 -11
  261. package/.pi/extensions/lib/ask-user/dialog.ts +0 -260
  262. package/.pi/extensions/lib/harness-subagent-auth.ts +0 -207
  263. package/.pi/extensions/lib/harness-subagent-policy.ts +0 -236
  264. package/.pi/extensions/pi-model-router-harness.ts +0 -42
  265. package/.pi/harness/evolution/meta-optimizer.mjs +0 -99
  266. package/.pi/harness/specs/router-tuning-proposal.schema.json +0 -114
  267. package/.pi/model-router.example.json +0 -36
  268. package/.pi/prompts/harness-critic.md +0 -10
  269. package/.pi/prompts/harness-eval.md +0 -10
  270. package/.pi/prompts/harness-router-tune.md +0 -52
  271. package/.pi/scripts/harness-generate-model-router.mjs +0 -327
  272. package/.pi/scripts/harness-model-router-routing.test.mjs +0 -97
  273. package/.pi/scripts/harness-sync-model-router.mjs +0 -97
  274. package/.pi/scripts/harness_web/__pycache__/__init__.cpython-314.pyc +0 -0
  275. package/.pi/scripts/harness_web/__pycache__/config.cpython-314.pyc +0 -0
  276. package/.pi/scripts/harness_web/__pycache__/output.cpython-314.pyc +0 -0
  277. package/.pi/scripts/harness_web/__pycache__/scrape.cpython-314.pyc +0 -0
  278. package/.pi/scripts/harness_web/__pycache__/search.cpython-314.pyc +0 -0
  279. package/.pi/scripts/harness_web/__pycache__/search_ddg.cpython-314.pyc +0 -0
  280. package/.pi/scripts/harness_web/__pycache__/search_searxng.cpython-314.pyc +0 -0
  281. package/.pi/scripts/vendor-sync-pi-model-router.sh +0 -47
  282. package/vendor/pi-model-router/.prettierignore +0 -4
  283. package/vendor/pi-model-router/.prettierrc +0 -5
  284. package/vendor/pi-model-router/AGENTS.md +0 -39
  285. package/vendor/pi-model-router/LICENSE +0 -21
  286. package/vendor/pi-model-router/README.md +0 -99
  287. package/vendor/pi-model-router/UPSTREAM_PIN.md +0 -10
  288. package/vendor/pi-model-router/docs/ARCHITECTURE.md +0 -54
  289. package/vendor/pi-model-router/extensions/commands.ts +0 -720
  290. package/vendor/pi-model-router/extensions/config.ts +0 -348
  291. package/vendor/pi-model-router/extensions/constants.ts +0 -1
  292. package/vendor/pi-model-router/extensions/index.ts +0 -478
  293. package/vendor/pi-model-router/extensions/provider.ts +0 -580
  294. package/vendor/pi-model-router/extensions/routing.ts +0 -564
  295. package/vendor/pi-model-router/extensions/state.ts +0 -52
  296. package/vendor/pi-model-router/extensions/types.ts +0 -95
  297. package/vendor/pi-model-router/extensions/ui.ts +0 -144
  298. package/vendor/pi-model-router/model-router.example.json +0 -48
  299. package/vendor/pi-model-router/package.json +0 -48
  300. package/vendor/pi-model-router/tsconfig.json +0 -16
  301. /package/.pi/{prompts → harness/docs}/planning-rubrics.md +0 -0
  302. /package/.pi/{extensions/lib → lib}/ask-user/fallback.ts +0 -0
  303. /package/.pi/{extensions/lib → lib}/ask-user/render.ts +0 -0
  304. /package/.pi/{extensions/lib → lib}/ask-user/schema.ts +0 -0
  305. /package/.pi/{extensions/lib → lib}/ask-user/types.ts +0 -0
  306. /package/.pi/{extensions/lib → lib}/ask-user/validate-core.mjs +0 -0
  307. /package/.pi/{extensions/lib → lib}/ask-user/validate.ts +0 -0
  308. /package/.pi/{extensions/lib → lib}/harness-cocoindex-refresh.ts +0 -0
  309. /package/.pi/{extensions/lib → lib}/harness-paths.ts +0 -0
  310. /package/.pi/{extensions/lib → lib}/harness-spawn-budget.ts +0 -0
  311. /package/.pi/{extensions/lib → lib}/harness-vcc-settings.ts +0 -0
  312. /package/.pi/{extensions/lib → lib}/plan-approval/dialog.ts +0 -0
  313. /package/.pi/{extensions/lib → lib}/plan-approval/schema.ts +0 -0
  314. /package/.pi/{extensions/lib → lib}/plan-approval-readiness.ts +0 -0
  315. /package/.pi/{extensions/lib → lib}/plan-debate-eligibility.ts +0 -0
  316. /package/.pi/{extensions/lib → lib}/plan-debate-focus.ts +0 -0
  317. /package/.pi/{extensions/lib → lib}/plan-debate-id.ts +0 -0
  318. /package/.pi/{extensions/lib → lib}/plan-debate-lanes.ts +0 -0
  319. /package/.pi/{extensions/lib → lib}/plan-debate-round-status.ts +0 -0
  320. /package/.pi/{extensions/lib → lib}/plan-debate-write-guard.ts +0 -0
  321. /package/.pi/{extensions/lib → lib}/plan-review-gate.ts +0 -0
  322. /package/.pi/{extensions/lib → lib}/plan-review-integrator-rules.ts +0 -0
  323. /package/.pi/{extensions/lib → lib}/plan-scope-guard.ts +0 -0
  324. /package/.pi/{extensions/lib → lib}/posthog-client.ts +0 -0
  325. /package/.pi/{extensions/lib → lib}/posthog-node.d.ts +0 -0
@@ -0,0 +1,216 @@
1
+ /**
2
+ * LSP Configuration for pi-lens
3
+ *
4
+ * Allows users to define custom LSP servers via configuration.
5
+ *
6
+ * Config file: .pi/harness/.lens/lsp.json
7
+ *
8
+ * Example:
9
+ * {
10
+ * "servers": {
11
+ * "my-server": {
12
+ * "name": "My Custom LSP",
13
+ * "extensions": [".myext"],
14
+ * "command": "my-lsp-server",
15
+ * "args": ["--stdio"],
16
+ * "rootMarkers": ["package.json"]
17
+ * }
18
+ * }
19
+ * }
20
+ */
21
+
22
+ import fs from "node:fs/promises";
23
+ import path from "node:path";
24
+ import { launchLSP } from "./launch.js";
25
+ import {
26
+ createRootDetector,
27
+ LSP_SERVERS,
28
+ type LSPServerInfo,
29
+ } from "./server.js";
30
+
31
+ // --- Types ---
32
+
33
+ export interface CustomServerConfig {
34
+ name: string;
35
+ extensions: string[];
36
+ command: string;
37
+ args?: string[];
38
+ rootMarkers?: string[];
39
+ env?: Record<string, string>;
40
+ }
41
+
42
+ export interface LSPConfig {
43
+ servers?: Record<string, CustomServerConfig>;
44
+ disabledServers?: string[];
45
+ /** Files to open at session start to seed lazy LSP indexing (e.g., clangd). */
46
+ warmFiles?: string[];
47
+ }
48
+
49
+ interface RegisteredLSPConfig {
50
+ customServers: LSPServerInfo[];
51
+ disabledServerIds: Set<string>;
52
+ }
53
+
54
+ // --- Config Loading ---
55
+
56
+ const CONFIG_PATHS = [".pi/harness/.lens/lsp.json", "pi-lsp.json"];
57
+
58
+ /**
59
+ * Load LSP configuration from file
60
+ */
61
+ export async function loadLSPConfig(cwd: string): Promise<LSPConfig> {
62
+ let dir = path.resolve(cwd);
63
+ while (true) {
64
+ for (const configPath of CONFIG_PATHS) {
65
+ const fullPath = path.join(dir, configPath);
66
+ try {
67
+ const content = await fs.readFile(fullPath, "utf-8");
68
+ const config = JSON.parse(content) as LSPConfig;
69
+ return config;
70
+ } catch {
71
+ // File doesn't exist or is invalid, try next
72
+ }
73
+ }
74
+
75
+ const parent = path.dirname(dir);
76
+ if (parent === dir) break;
77
+ dir = parent;
78
+ }
79
+
80
+ return {};
81
+ }
82
+
83
+ // --- Custom Server Factory ---
84
+
85
+ /**
86
+ * Create LSPServerInfo from user configuration
87
+ */
88
+ export function createCustomServer(
89
+ config: CustomServerConfig,
90
+ id: string,
91
+ ): LSPServerInfo {
92
+ return {
93
+ id,
94
+ name: config.name,
95
+ extensions: config.extensions,
96
+ root: config.rootMarkers
97
+ ? createRootDetector(config.rootMarkers)
98
+ : async () => process.cwd(),
99
+ async spawn(root) {
100
+ const proc = await launchLSP(config.command, config.args ?? ["--stdio"], {
101
+ cwd: root,
102
+ env: config.env ? { ...process.env, ...config.env } : process.env,
103
+ });
104
+ return { process: proc };
105
+ },
106
+ };
107
+ }
108
+
109
+ // --- Registry Management ---
110
+
111
+ const EMPTY_CONFIG: RegisteredLSPConfig = {
112
+ customServers: [],
113
+ disabledServerIds: new Set(),
114
+ };
115
+
116
+ const workspaceConfigs = new Map<string, RegisteredLSPConfig>();
117
+ /** In-flight config initialization promises to prevent duplicate concurrent loads */
118
+ const configInFlight = new Map<string, Promise<void>>();
119
+
120
+ function normalizeWorkspacePath(cwd: string): string {
121
+ return path.resolve(cwd);
122
+ }
123
+
124
+ function isSameOrChildPath(filePath: string, candidateRoot: string): boolean {
125
+ if (filePath === candidateRoot) return true;
126
+ return filePath.startsWith(`${candidateRoot}${path.sep}`);
127
+ }
128
+
129
+ function getConfigForFile(filePath: string): RegisteredLSPConfig {
130
+ const resolvedFilePath = path.resolve(filePath);
131
+ let bestMatch: { root: string; config: RegisteredLSPConfig } | undefined;
132
+
133
+ for (const [root, config] of workspaceConfigs) {
134
+ if (!isSameOrChildPath(resolvedFilePath, root)) continue;
135
+ if (!bestMatch || root.length > bestMatch.root.length) {
136
+ bestMatch = { root, config };
137
+ }
138
+ }
139
+
140
+ return bestMatch?.config ?? EMPTY_CONFIG;
141
+ }
142
+
143
+ /**
144
+ * Initialize LSP configuration (call at session start)
145
+ * Deduplicates concurrent calls for the same workspace.
146
+ */
147
+ export async function initLSPConfig(cwd: string): Promise<void> {
148
+ const normalizedCwd = normalizeWorkspacePath(cwd);
149
+
150
+ const existing = configInFlight.get(normalizedCwd);
151
+ if (existing) return existing;
152
+
153
+ const promise = (async () => {
154
+ const config = await loadLSPConfig(cwd);
155
+ const customServers: LSPServerInfo[] = [];
156
+ const disabledServerIds = new Set(config.disabledServers ?? []);
157
+
158
+ if (config.servers) {
159
+ for (const [id, serverConfig] of Object.entries(config.servers)) {
160
+ try {
161
+ const server = createCustomServer(serverConfig, id);
162
+ customServers.push(server);
163
+ } catch {
164
+ // pi-lens-ignore: missing-error-propagation — per-server registration, skip bad entries
165
+ }
166
+ }
167
+ }
168
+
169
+ workspaceConfigs.set(normalizedCwd, {
170
+ customServers,
171
+ disabledServerIds,
172
+ });
173
+ })();
174
+
175
+ configInFlight.set(normalizedCwd, promise);
176
+ try {
177
+ await promise;
178
+ } finally {
179
+ configInFlight.delete(normalizedCwd);
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Get all available servers (built-in + custom, minus disabled)
185
+ */
186
+ export function getAllServers(filePath?: string): LSPServerInfo[] {
187
+ const config = filePath ? getConfigForFile(filePath) : EMPTY_CONFIG;
188
+ const all = [...LSP_SERVERS, ...config.customServers];
189
+ return all.filter((s) => !config.disabledServerIds.has(s.id));
190
+ }
191
+
192
+ /**
193
+ * Check if a server is disabled
194
+ */
195
+ export function isServerDisabled(serverId: string, filePath?: string): boolean {
196
+ const config = filePath ? getConfigForFile(filePath) : EMPTY_CONFIG;
197
+ return config.disabledServerIds.has(serverId);
198
+ }
199
+
200
+ // --- Override getServersForFile to include custom servers
201
+
202
+ export function getServersForFileWithConfig(filePath: string): LSPServerInfo[] {
203
+ const ext = path.extname(filePath).toLowerCase();
204
+ const base = path.basename(filePath).toLowerCase();
205
+ return getAllServers(filePath).filter((server) => {
206
+ const extensions = server.extensions.map((value) => value.toLowerCase());
207
+ return extensions.includes(ext) || extensions.includes(base);
208
+ });
209
+ }
210
+
211
+ export function resetLSPConfigStateForTests(): void {
212
+ workspaceConfigs.clear();
213
+ }
214
+
215
+ // Re-export with config support
216
+ export { getAllServers as getServersForFile };
@@ -0,0 +1,297 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { uriToPath } from "./path-utils.js";
4
+
5
+ export interface LSPPosition {
6
+ line: number;
7
+ character: number;
8
+ }
9
+
10
+ export interface LSPRange {
11
+ start: LSPPosition;
12
+ end: LSPPosition;
13
+ }
14
+
15
+ export interface LSPTextEdit {
16
+ range: LSPRange;
17
+ newText: string;
18
+ }
19
+
20
+ interface TextDocumentEdit {
21
+ textDocument: { uri: string };
22
+ edits: unknown[];
23
+ }
24
+
25
+ interface CreateFileOp {
26
+ kind: "create";
27
+ uri: string;
28
+ }
29
+
30
+ interface RenameFileOp {
31
+ kind: "rename";
32
+ oldUri: string;
33
+ newUri: string;
34
+ }
35
+
36
+ interface DeleteFileOp {
37
+ kind: "delete";
38
+ uri: string;
39
+ }
40
+
41
+ export interface AppliedWorkspaceEdit {
42
+ descriptions: string[];
43
+ files: string[];
44
+ }
45
+
46
+ function isPosition(value: unknown): value is LSPPosition {
47
+ return (
48
+ typeof value === "object" &&
49
+ value !== null &&
50
+ typeof (value as { line?: unknown }).line === "number" &&
51
+ typeof (value as { character?: unknown }).character === "number"
52
+ );
53
+ }
54
+
55
+ function isRange(value: unknown): value is LSPRange {
56
+ return (
57
+ typeof value === "object" &&
58
+ value !== null &&
59
+ isPosition((value as { start?: unknown }).start) &&
60
+ isPosition((value as { end?: unknown }).end)
61
+ );
62
+ }
63
+
64
+ function isTextEdit(value: unknown): value is LSPTextEdit {
65
+ return (
66
+ typeof value === "object" &&
67
+ value !== null &&
68
+ isRange((value as { range?: unknown }).range) &&
69
+ typeof (value as { newText?: unknown }).newText === "string"
70
+ );
71
+ }
72
+
73
+ function isTextDocumentEdit(value: unknown): value is TextDocumentEdit {
74
+ return (
75
+ typeof value === "object" &&
76
+ value !== null &&
77
+ typeof (value as { textDocument?: { uri?: unknown } }).textDocument?.uri ===
78
+ "string" &&
79
+ Array.isArray((value as { edits?: unknown }).edits)
80
+ );
81
+ }
82
+
83
+ function comparePosition(a: LSPPosition, b: LSPPosition): number {
84
+ return a.line === b.line ? a.character - b.character : a.line - b.line;
85
+ }
86
+
87
+ function formatRange(range: LSPRange): string {
88
+ return `${range.start.line + 1}:${range.start.character + 1}-${range.end.line + 1}:${range.end.character + 1}`;
89
+ }
90
+
91
+ export function rangesOverlap(a: LSPRange, b: LSPRange): boolean {
92
+ return (
93
+ comparePosition(a.start, b.end) < 0 && comparePosition(b.start, a.end) < 0
94
+ );
95
+ }
96
+
97
+ export function applyTextEditsToString(
98
+ content: string,
99
+ edits: LSPTextEdit[],
100
+ ): string {
101
+ const sortedEdits = [...edits].sort((a, b) => {
102
+ const lineDelta = b.range.start.line - a.range.start.line;
103
+ return lineDelta !== 0
104
+ ? lineDelta
105
+ : b.range.start.character - a.range.start.character;
106
+ });
107
+
108
+ for (let index = 0; index < sortedEdits.length - 1; index++) {
109
+ const later = sortedEdits[index]?.range;
110
+ const earlier = sortedEdits[index + 1]?.range;
111
+ if (later && earlier && comparePosition(earlier.end, later.start) > 0) {
112
+ throw new Error(
113
+ `overlapping LSP edits: ${formatRange(earlier)} conflicts with ${formatRange(later)}`,
114
+ );
115
+ }
116
+ }
117
+
118
+ const lines = content.split("\n");
119
+ for (const edit of sortedEdits) {
120
+ const { start, end } = edit.range;
121
+ if (start.line === end.line) {
122
+ const line = lines[start.line] ?? "";
123
+ lines[start.line] =
124
+ line.slice(0, start.character) +
125
+ edit.newText +
126
+ line.slice(end.character);
127
+ continue;
128
+ }
129
+
130
+ const startLine = lines[start.line] ?? "";
131
+ const endLine = lines[end.line] ?? "";
132
+ const replacement =
133
+ startLine.slice(0, start.character) +
134
+ edit.newText +
135
+ endLine.slice(end.character);
136
+ lines.splice(
137
+ start.line,
138
+ end.line - start.line + 1,
139
+ ...replacement.split("\n"),
140
+ );
141
+ }
142
+
143
+ return lines.join("\n");
144
+ }
145
+
146
+ export function flattenWorkspaceTextEdits(edit: {
147
+ changes?: Record<string, unknown[]>;
148
+ documentChanges?: unknown[];
149
+ }): Map<string, LSPTextEdit[]> {
150
+ const out = new Map<string, LSPTextEdit[]>();
151
+ const push = (uri: string, edits: unknown[]) => {
152
+ const textEdits = edits.filter(isTextEdit);
153
+ if (textEdits.length === 0) return;
154
+ const existing = out.get(uri);
155
+ if (existing) existing.push(...textEdits);
156
+ else out.set(uri, [...textEdits]);
157
+ };
158
+
159
+ for (const [uri, edits] of Object.entries(edit.changes ?? {})) {
160
+ push(uri, edits);
161
+ }
162
+
163
+ for (const change of edit.documentChanges ?? []) {
164
+ if (isTextDocumentEdit(change)) {
165
+ push(change.textDocument.uri, change.edits);
166
+ }
167
+ }
168
+
169
+ return out;
170
+ }
171
+
172
+ function relativeToCwd(filePath: string, cwd: string): string {
173
+ const rel = path.relative(cwd, filePath) || path.basename(filePath);
174
+ return rel.replace(/\\/g, "/");
175
+ }
176
+
177
+ export function summarizeWorkspaceEdit(
178
+ edit: {
179
+ changes?: Record<string, unknown[]>;
180
+ documentChanges?: unknown[];
181
+ },
182
+ cwd: string,
183
+ ): string[] {
184
+ const lines: string[] = [];
185
+ const textEditsByUri = flattenWorkspaceTextEdits(edit);
186
+ for (const [uri, edits] of textEditsByUri) {
187
+ lines.push(
188
+ `Apply ${edits.length} edit(s) to ${relativeToCwd(uriToPath(uri), cwd)}`,
189
+ );
190
+ }
191
+ for (const change of edit.documentChanges ?? []) {
192
+ if (typeof change !== "object" || change === null || !("kind" in change))
193
+ continue;
194
+ const kind = (change as { kind?: unknown }).kind;
195
+ if (kind === "create" && typeof (change as CreateFileOp).uri === "string") {
196
+ lines.push(
197
+ `Create ${relativeToCwd(uriToPath((change as CreateFileOp).uri), cwd)}`,
198
+ );
199
+ } else if (
200
+ kind === "rename" &&
201
+ typeof (change as RenameFileOp).oldUri === "string" &&
202
+ typeof (change as RenameFileOp).newUri === "string"
203
+ ) {
204
+ lines.push(
205
+ `Rename ${relativeToCwd(uriToPath((change as RenameFileOp).oldUri), cwd)} → ${relativeToCwd(uriToPath((change as RenameFileOp).newUri), cwd)}`,
206
+ );
207
+ } else if (
208
+ kind === "delete" &&
209
+ typeof (change as DeleteFileOp).uri === "string"
210
+ ) {
211
+ lines.push(
212
+ `Delete ${relativeToCwd(uriToPath((change as DeleteFileOp).uri), cwd)}`,
213
+ );
214
+ }
215
+ }
216
+ return lines;
217
+ }
218
+
219
+ export async function applyWorkspaceEdit(
220
+ edit: {
221
+ changes?: Record<string, unknown[]>;
222
+ documentChanges?: unknown[];
223
+ },
224
+ cwd: string,
225
+ ): Promise<AppliedWorkspaceEdit> {
226
+ const descriptions: string[] = [];
227
+ const touchedFiles = new Set<string>();
228
+ const textEditsByUri = flattenWorkspaceTextEdits(edit);
229
+
230
+ try {
231
+ for (const [uri, edits] of textEditsByUri) {
232
+ const filePath = uriToPath(uri);
233
+ const content = await fs.readFile(filePath, "utf-8");
234
+ const updated = applyTextEditsToString(content, edits);
235
+ await fs.writeFile(filePath, updated, "utf-8");
236
+ touchedFiles.add(filePath);
237
+ descriptions.push(
238
+ `Applied ${edits.length} edit(s) to ${relativeToCwd(filePath, cwd)}`,
239
+ );
240
+ }
241
+
242
+ for (const change of edit.documentChanges ?? []) {
243
+ if (typeof change !== "object" || change === null || !("kind" in change))
244
+ continue;
245
+ const kind = (change as { kind?: unknown }).kind;
246
+ if (
247
+ kind === "create" &&
248
+ typeof (change as CreateFileOp).uri === "string"
249
+ ) {
250
+ const filePath = uriToPath((change as CreateFileOp).uri);
251
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
252
+ await fs
253
+ .writeFile(filePath, "", { flag: "wx" })
254
+ .catch(async (err: unknown) => {
255
+ if ((err as { code?: string }).code !== "EEXIST") throw err;
256
+ });
257
+ touchedFiles.add(filePath);
258
+ descriptions.push(`Created ${relativeToCwd(filePath, cwd)}`);
259
+ } else if (
260
+ kind === "rename" &&
261
+ typeof (change as RenameFileOp).oldUri === "string" &&
262
+ typeof (change as RenameFileOp).newUri === "string"
263
+ ) {
264
+ const oldPath = uriToPath((change as RenameFileOp).oldUri);
265
+ const newPath = uriToPath((change as RenameFileOp).newUri);
266
+ await fs.mkdir(path.dirname(newPath), { recursive: true });
267
+ await fs.rename(oldPath, newPath);
268
+ touchedFiles.add(oldPath);
269
+ touchedFiles.add(newPath);
270
+ descriptions.push(
271
+ `Renamed ${relativeToCwd(oldPath, cwd)} → ${relativeToCwd(newPath, cwd)}`,
272
+ );
273
+ } else if (
274
+ kind === "delete" &&
275
+ typeof (change as DeleteFileOp).uri === "string"
276
+ ) {
277
+ const filePath = uriToPath((change as DeleteFileOp).uri);
278
+ await fs.rm(filePath, { recursive: true, force: true });
279
+ touchedFiles.add(filePath);
280
+ descriptions.push(`Deleted ${relativeToCwd(filePath, cwd)}`);
281
+ }
282
+ }
283
+ } catch (err) {
284
+ const already = [...touchedFiles];
285
+ if (already.length > 0) {
286
+ const alreadyList = already
287
+ .map((f) => ` • ${relativeToCwd(f, cwd)}`)
288
+ .join("\n");
289
+ throw new Error(
290
+ `Workspace edit failed mid-application — ${already.length} file(s) already written, no rollback performed:\n${alreadyList}\nCause: ${err instanceof Error ? err.message : String(err)}`,
291
+ );
292
+ }
293
+ throw err;
294
+ }
295
+
296
+ return { descriptions, files: [...touchedFiles] };
297
+ }