@herbertgao/pi-extensions 2026.9.5 → 2026.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/README.md +5 -22
  2. package/THIRD_PARTY_NOTICES.md +0 -4
  3. package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +1 -1
  4. package/node_modules/@herbertgao/sol-pi/package.json +1 -1
  5. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/index.ts +0 -5
  6. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/state.ts +3 -1
  7. package/node_modules/@narumitw/pi-btw/README.md +2 -0
  8. package/node_modules/@narumitw/pi-btw/dist/index.ts +284 -365
  9. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
  10. package/node_modules/@narumitw/pi-btw/package.json +52 -53
  11. package/node_modules/@narumitw/pi-btw/src/bring-to-main.ts +494 -547
  12. package/node_modules/@narumitw/pi-btw/src/btw.ts +814 -856
  13. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +673 -674
  14. package/node_modules/@narumitw/pi-btw/src/keybindings.ts +228 -270
  15. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +309 -318
  16. package/node_modules/@narumitw/pi-btw/src/menu.ts +416 -454
  17. package/node_modules/@narumitw/pi-btw/src/settings.ts +203 -219
  18. package/node_modules/@narumitw/pi-btw/src/side-thread.ts +173 -201
  19. package/node_modules/@narumitw/pi-btw/src/text.ts +21 -23
  20. package/node_modules/@narumitw/pi-btw/src/transcript-markdown.ts +65 -0
  21. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +611 -662
  22. package/node_modules/pi-lens/CHANGELOG.md +239 -0
  23. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +3 -0
  24. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +30 -4
  25. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +2 -0
  26. package/node_modules/pi-lens/dist/clients/bash-file-access.js +26 -0
  27. package/node_modules/pi-lens/dist/clients/biome-client.js +1 -1
  28. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +403 -35
  29. package/node_modules/pi-lens/dist/clients/bootstrap.js +6 -1
  30. package/node_modules/pi-lens/dist/clients/cascade-format.js +200 -4
  31. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +10 -2
  32. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +16 -1
  33. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +12 -1
  34. package/node_modules/pi-lens/dist/clients/dispatch/ast-grep-catalog.js +65 -0
  35. package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +28 -0
  36. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +9 -2
  37. package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +222 -0
  38. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +389 -13
  39. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +175 -25
  40. package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +62 -0
  41. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +21 -41
  42. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +6 -1
  43. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +7 -7
  44. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -12
  45. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +2 -21
  46. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +1 -1
  47. package/node_modules/pi-lens/dist/clients/dispatch/types.js +4 -1
  48. package/node_modules/pi-lens/dist/clients/disposition-publish.js +1 -0
  49. package/node_modules/pi-lens/dist/clients/effective-config.js +1 -1
  50. package/node_modules/pi-lens/dist/clients/file-kinds.js +26 -1
  51. package/node_modules/pi-lens/dist/clients/file-role.js +21 -21
  52. package/node_modules/pi-lens/dist/clients/file-utils.js +156 -10
  53. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +56 -9
  54. package/node_modules/pi-lens/dist/clients/format-service.js +39 -21
  55. package/node_modules/pi-lens/dist/clients/formatters.js +60 -13
  56. package/node_modules/pi-lens/dist/clients/generation-guard.js +2 -2
  57. package/node_modules/pi-lens/dist/clients/git-guard.js +11 -3
  58. package/node_modules/pi-lens/dist/clients/indent-retarget.js +78 -8
  59. package/node_modules/pi-lens/dist/clients/installer/index.js +333 -114
  60. package/node_modules/pi-lens/dist/clients/instance-reaper-state.js +7 -0
  61. package/node_modules/pi-lens/dist/clients/instance-reaper.js +20 -10
  62. package/node_modules/pi-lens/dist/clients/language-profile.js +23 -1
  63. package/node_modules/pi-lens/dist/clients/language-registry.js +5 -1
  64. package/node_modules/pi-lens/dist/clients/latency-logger.js +4 -1
  65. package/node_modules/pi-lens/dist/clients/lens-engine.js +12 -3
  66. package/node_modules/pi-lens/dist/clients/lens-events.js +2 -1
  67. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +79 -0
  68. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +2 -1
  69. package/node_modules/pi-lens/dist/clients/lsp/client.js +25 -21
  70. package/node_modules/pi-lens/dist/clients/lsp/config.js +2 -2
  71. package/node_modules/pi-lens/dist/clients/lsp/index.js +7 -0
  72. package/node_modules/pi-lens/dist/clients/lsp/launch.js +16 -4
  73. package/node_modules/pi-lens/dist/clients/lsp/server.js +19 -2
  74. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +2 -1
  75. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +5 -3
  76. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +12 -8
  77. package/node_modules/pi-lens/dist/clients/mcp/session.js +36 -4
  78. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +24 -7
  79. package/node_modules/pi-lens/dist/clients/observed-mutation.js +40 -2
  80. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +23 -3
  81. package/node_modules/pi-lens/dist/clients/opengrep-client.js +110 -15
  82. package/node_modules/pi-lens/dist/clients/path-utils.js +123 -3
  83. package/node_modules/pi-lens/dist/clients/performance-report.js +5 -1
  84. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +1 -1
  85. package/node_modules/pi-lens/dist/clients/pipeline.js +74 -5
  86. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +37 -5
  87. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +145 -18
  88. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +4 -1
  89. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +59 -3
  90. package/node_modules/pi-lens/dist/clients/project-lens-config.js +45 -2
  91. package/node_modules/pi-lens/dist/clients/read-guard.js +70 -11
  92. package/node_modules/pi-lens/dist/clients/resource-sampler.js +147 -9
  93. package/node_modules/pi-lens/dist/clients/ruff-client.js +1 -1
  94. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +33 -5
  95. package/node_modules/pi-lens/dist/clients/runtime-context.js +6 -2
  96. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +77 -7
  97. package/node_modules/pi-lens/dist/clients/runtime-session.js +21 -2
  98. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +43 -12
  99. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +330 -85
  100. package/node_modules/pi-lens/dist/clients/runtime-turn.js +147 -17
  101. package/node_modules/pi-lens/dist/clients/safe-spawn.js +258 -5
  102. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +6 -0
  103. package/node_modules/pi-lens/dist/clients/session-event-guard.js +50 -5
  104. package/node_modules/pi-lens/dist/clients/sg-runner.js +23 -11
  105. package/node_modules/pi-lens/dist/clients/sgconfig.js +79 -2
  106. package/node_modules/pi-lens/dist/clients/test-runner-client.js +6 -41
  107. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +43 -0
  108. package/node_modules/pi-lens/dist/clients/tool-agreement.js +423 -0
  109. package/node_modules/pi-lens/dist/clients/tool-config.js +50 -0
  110. package/node_modules/pi-lens/dist/clients/tool-cwd.js +47 -76
  111. package/node_modules/pi-lens/dist/clients/tool-policy.js +163 -7
  112. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +120 -0
  113. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +127 -189
  114. package/node_modules/pi-lens/dist/clients/widget-state.js +98 -0
  115. package/node_modules/pi-lens/dist/clients/word-index.js +5 -2
  116. package/node_modules/pi-lens/dist/clients/write-ordering-guard.js +7 -0
  117. package/node_modules/pi-lens/dist/index.js +27416 -24153
  118. package/node_modules/pi-lens/dist/mcp/server.js +5 -2
  119. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +7 -1
  120. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +168 -53
  121. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +244 -72
  122. package/node_modules/pi-lens/docs/audit1.md +5 -5
  123. package/node_modules/pi-lens/docs/dependencies.md +11 -0
  124. package/node_modules/pi-lens/docs/dispositions.md +52 -0
  125. package/node_modules/pi-lens/docs/environment-variables.md +2 -2
  126. package/node_modules/pi-lens/docs/features.md +4 -4
  127. package/node_modules/pi-lens/docs/globalconfig.md +3 -1
  128. package/node_modules/pi-lens/docs/language-coverage.md +1 -0
  129. package/node_modules/pi-lens/docs/pi-lens-fixer.md +53 -94
  130. package/node_modules/pi-lens/docs/pi-lens-investigator.md +43 -38
  131. package/node_modules/pi-lens/docs/pi-lens-retro.md +97 -0
  132. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +53 -66
  133. package/node_modules/pi-lens/docs/release-qa-baseline.md +22 -0
  134. package/node_modules/pi-lens/docs/settings.md +7 -4
  135. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +1 -1
  136. package/node_modules/pi-lens/docs/usage.md +2 -0
  137. package/node_modules/pi-lens/package.json +1 -1
  138. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +24 -12
  139. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +40 -0
  140. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +15 -0
  141. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +7 -0
  142. package/package.json +6 -17
  143. package/examples/pi-footer.json +0 -275
  144. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +0 -70
  145. package/node_modules/@czottmann/pi-automode/LICENSE.md +0 -21
  146. package/node_modules/@czottmann/pi-automode/README.md +0 -215
  147. package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +0 -39
  148. package/node_modules/@czottmann/pi-automode/docs/adr/ADR-001-permission-precedence-and-trust-boundaries.md +0 -46
  149. package/node_modules/@czottmann/pi-automode/docs/adr/ADR-002-global-config-in-extension-data-directory.md +0 -60
  150. package/node_modules/@czottmann/pi-automode/docs/adr/INDEX.md +0 -6
  151. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +0 -428
  152. package/node_modules/@czottmann/pi-automode/docs/configuration.md +0 -171
  153. package/node_modules/@czottmann/pi-automode/docs/defaults.md +0 -178
  154. package/node_modules/@czottmann/pi-automode/docs/diagnostics.md +0 -90
  155. package/node_modules/@czottmann/pi-automode/docs/observability-logging.md +0 -160
  156. package/node_modules/@czottmann/pi-automode/examples/automode.local.json +0 -44
  157. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/bash.ts +0 -692
  158. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +0 -852
  159. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +0 -904
  160. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +0 -228
  161. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +0 -954
  162. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +0 -429
  163. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/log.ts +0 -173
  164. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/model-selector.ts +0 -113
  165. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/model.ts +0 -13
  166. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/paths.ts +0 -297
  167. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/permissions.ts +0 -623
  168. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/state.ts +0 -102
  169. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/transcript.ts +0 -236
  170. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/types.ts +0 -205
  171. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/utils.ts +0 -54
  172. package/node_modules/@czottmann/pi-automode/extensions/auto-mode.ts +0 -26
  173. package/node_modules/@czottmann/pi-automode/package.json +0 -61
  174. package/node_modules/@czottmann/pi-automode/skills/automode-diagnostics/SKILL.md +0 -63
  175. package/node_modules/@luxusai/pi-hindsight/CHANGELOG.md +0 -376
  176. package/node_modules/@luxusai/pi-hindsight/README.md +0 -92
  177. package/node_modules/@luxusai/pi-hindsight/docs/adr/001-memory-lifecycle-and-scope.md +0 -56
  178. package/node_modules/@luxusai/pi-hindsight/docs/adr/002-explicit-routing-strategy-seam.md +0 -169
  179. package/node_modules/@luxusai/pi-hindsight/docs/adr/003-tui-memory-mode-vocabulary.md +0 -107
  180. package/node_modules/@luxusai/pi-hindsight/docs/adr/004-lifeos-dual-bank-design.md +0 -89
  181. package/node_modules/@luxusai/pi-hindsight/docs/adr/005-domain-banks-and-agent-first-surface.md +0 -189
  182. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark.webp +0 -0
  183. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark@2x.webp +0 -0
  184. package/node_modules/@luxusai/pi-hindsight/docs/coding-memory-evaluation.md +0 -54
  185. package/node_modules/@luxusai/pi-hindsight/docs/compatibility.md +0 -75
  186. package/node_modules/@luxusai/pi-hindsight/docs/hindsight-core-functions.md +0 -300
  187. package/node_modules/@luxusai/pi-hindsight/docs/mission-and-mental-model-quality.md +0 -158
  188. package/node_modules/@luxusai/pi-hindsight/docs/next-opt-out-design.md +0 -164
  189. package/node_modules/@luxusai/pi-hindsight/docs/risky-memory-modes.md +0 -139
  190. package/node_modules/@luxusai/pi-hindsight/docs/starter-mental-model-suggestions.md +0 -74
  191. package/node_modules/@luxusai/pi-hindsight/docs/surface-reference.md +0 -261
  192. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-operations.ts +0 -151
  193. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-selection.ts +0 -18
  194. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-settings-presenter.ts +0 -46
  195. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-templates.ts +0 -383
  196. package/node_modules/@luxusai/pi-hindsight/extensions/banks/banking.ts +0 -240
  197. package/node_modules/@luxusai/pi-hindsight/extensions/banks/knowledge-page-seed.ts +0 -176
  198. package/node_modules/@luxusai/pi-hindsight/extensions/banks/retain-strategies.ts +0 -178
  199. package/node_modules/@luxusai/pi-hindsight/extensions/client/client-retry.ts +0 -41
  200. package/node_modules/@luxusai/pi-hindsight/extensions/client/client.ts +0 -386
  201. package/node_modules/@luxusai/pi-hindsight/extensions/client/fetch-compat.ts +0 -39
  202. package/node_modules/@luxusai/pi-hindsight/extensions/client/timeout.ts +0 -34
  203. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-defaults.ts +0 -140
  204. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-model.ts +0 -61
  205. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-registry.ts +0 -925
  206. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-field-paths.ts +0 -247
  207. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-normalize.ts +0 -547
  208. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-writer.ts +0 -480
  209. package/node_modules/@luxusai/pi-hindsight/extensions/config/config.ts +0 -182
  210. package/node_modules/@luxusai/pi-hindsight/extensions/config/setup-gate.ts +0 -86
  211. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-execute.ts +0 -1010
  212. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-parse.ts +0 -175
  213. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-plan.ts +0 -425
  214. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-presentation.ts +0 -210
  215. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-sessions.ts +0 -817
  216. package/node_modules/@luxusai/pi-hindsight/extensions/index.ts +0 -25
  217. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/git-seed.ts +0 -319
  218. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-recall.ts +0 -196
  219. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-retain.ts +0 -189
  220. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-runtime.ts +0 -65
  221. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle.ts +0 -260
  222. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/mental-models.ts +0 -244
  223. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/observation-scopes.ts +0 -59
  224. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-cleanup.ts +0 -82
  225. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-visibility.ts +0 -45
  226. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall.ts +0 -331
  227. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-cursor.ts +0 -354
  228. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-job-builder.ts +0 -66
  229. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-receipts.ts +0 -132
  230. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain.ts +0 -228
  231. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-bank-template-operations.ts +0 -74
  232. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-config-operations.ts +0 -16
  233. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-control-operations.ts +0 -648
  234. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-diagnostics-operations.ts +0 -105
  235. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-identity.ts +0 -68
  236. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-service.ts +0 -71
  237. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-types.ts +0 -13
  238. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-recall-operations.ts +0 -162
  239. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-retain-operations.ts +0 -131
  240. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-scope.ts +0 -104
  241. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-session-operations.ts +0 -37
  242. package/node_modules/@luxusai/pi-hindsight/extensions/operations/operation-catalog.ts +0 -893
  243. package/node_modules/@luxusai/pi-hindsight/extensions/operations/reflect-presenter.ts +0 -35
  244. package/node_modules/@luxusai/pi-hindsight/extensions/operations/scope-migrate.ts +0 -177
  245. package/node_modules/@luxusai/pi-hindsight/extensions/operations/tools.ts +0 -7
  246. package/node_modules/@luxusai/pi-hindsight/extensions/queue/flush-presenter.ts +0 -21
  247. package/node_modules/@luxusai/pi-hindsight/extensions/queue/jsonl-queue-store.ts +0 -112
  248. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-delivery.ts +0 -111
  249. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-lock.ts +0 -214
  250. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-operations.ts +0 -70
  251. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue.ts +0 -392
  252. package/node_modules/@luxusai/pi-hindsight/extensions/tui/bank-template-presentation.ts +0 -45
  253. package/node_modules/@luxusai/pi-hindsight/extensions/tui/commands.ts +0 -9
  254. package/node_modules/@luxusai/pi-hindsight/extensions/tui/guided-setup.ts +0 -860
  255. package/node_modules/@luxusai/pi-hindsight/extensions/tui/prefill-input.ts +0 -105
  256. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-flow.ts +0 -189
  257. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-server-probe.ts +0 -299
  258. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-actions.ts +0 -201
  259. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-facts.ts +0 -47
  260. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-render.ts +0 -239
  261. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-types.ts +0 -50
  262. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui.ts +0 -259
  263. package/node_modules/@luxusai/pi-hindsight/extensions/tui/tool-presenters.ts +0 -77
  264. package/node_modules/@luxusai/pi-hindsight/extensions/types.ts +0 -534
  265. package/node_modules/@luxusai/pi-hindsight/extensions/utils/diagnostics.ts +0 -319
  266. package/node_modules/@luxusai/pi-hindsight/extensions/utils/messages.ts +0 -325
  267. package/node_modules/@luxusai/pi-hindsight/extensions/utils/sanitize.ts +0 -42
  268. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-memory-meta.ts +0 -244
  269. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-operations.ts +0 -56
  270. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session.ts +0 -50
  271. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-fields.ts +0 -167
  272. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-health.ts +0 -207
  273. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status.ts +0 -120
  274. package/node_modules/@luxusai/pi-hindsight/extensions/version.ts +0 -8
  275. package/node_modules/@luxusai/pi-hindsight/package.json +0 -118
  276. package/node_modules/@luxusai/pi-hindsight/skills/hindsight-memory-doctor/SKILL.md +0 -75
@@ -1,904 +0,0 @@
1
- import {
2
- existsSync,
3
- linkSync,
4
- lstatSync,
5
- mkdirSync,
6
- readFileSync,
7
- unlinkSync,
8
- writeFileSync,
9
- } from "node:fs";
10
- import { dirname, resolve } from "node:path";
11
- import {
12
- DEFAULT_ALLOW,
13
- DEFAULT_ALLOW_INSIDE_WORKING_DIRECTORY,
14
- DEFAULT_CLASSIFIER_TIMEOUT_MS,
15
- DEFAULT_CLASSIFY_READ_ONLY_TOOLS,
16
- DEFAULT_DENIED_PATHS,
17
- DEFAULT_ENVIRONMENT,
18
- DEFAULT_FAST_CLASSIFIER_MAX_TOKENS,
19
- DEFAULT_HARD_DENY,
20
- DEFAULT_LOG_CONFIG,
21
- DEFAULT_MAX_TOOL_TRANSCRIPT_TOKENS,
22
- DEFAULT_MAX_USER_TRANSCRIPT_TOKENS,
23
- DEFAULT_PROTECTED_PATHS,
24
- DEFAULT_SOFT_DENY,
25
- MAX_CLASSIFIER_TIMEOUT_MS,
26
- PI_GLOBAL_SETTINGS,
27
- PI_LEGACY_GLOBAL_SETTINGS,
28
- PI_PROJECT_LOCAL_SETTINGS,
29
- PI_PROJECT_SHARED_SETTINGS,
30
- } from "./constants.ts";
31
- import {
32
- MAX_WILDCARD_PATTERN_LENGTH,
33
- parseToolPattern,
34
- } from "./permissions.ts";
35
- import type {
36
- AutoModeSettings,
37
- ClassifierReasoningLevel,
38
- ConfigLoadResult,
39
- EffectiveConfig,
40
- LoadedSettingsFile,
41
- LogConfig,
42
- SettingsFile,
43
- SettingsSources,
44
- ToolPattern,
45
- } from "./types.ts";
46
- import { hasOwn, stringArray } from "./utils.ts";
47
-
48
- export type GlobalConfigPreparation = {
49
- status: "current" | "migrated" | "conflict" | "failed";
50
- activePath: string;
51
- diagnostic?: string;
52
- notification?: string;
53
- writeBlockedReason?: string;
54
- };
55
-
56
- export type PrepareGlobalConfigOptions = {
57
- currentPath?: string;
58
- legacyPath?: string;
59
- moveFile?: (source: string, destination: string) => void;
60
- unlinkFile?: (path: string) => void;
61
- };
62
-
63
- function sameFileIdentity(firstPath: string, secondPath: string): boolean {
64
- try {
65
- const first = lstatSync(firstPath);
66
- const second = lstatSync(secondPath);
67
- return first.dev === second.dev && first.ino === second.ino;
68
- } catch {
69
- return false;
70
- }
71
- }
72
-
73
- function cleanupPublishedDestination(
74
- destination: string,
75
- unlinkFile: (path: string) => void,
76
- originalError: unknown,
77
- ): AggregateError | undefined {
78
- try {
79
- unlinkFile(destination);
80
- return undefined;
81
- } catch (cleanupError) {
82
- return new AggregateError(
83
- [originalError, cleanupError],
84
- `Could not clean up interrupted Auto Mode config migration at ${destination}`,
85
- );
86
- }
87
- }
88
-
89
- function moveFileWithoutOverwrite(
90
- source: string,
91
- destination: string,
92
- unlinkFile: (path: string) => void,
93
- ): void {
94
- linkSync(source, destination);
95
- try {
96
- unlinkFile(source);
97
- } catch (error) {
98
- throw cleanupPublishedDestination(destination, unlinkFile, error) ?? error;
99
- }
100
- }
101
-
102
- function globalConfigFailure(
103
- currentPath: string,
104
- legacyPath: string,
105
- error: unknown,
106
- writeBlockedReason?: string,
107
- ): GlobalConfigPreparation {
108
- const message =
109
- `Could not move Auto Mode config from ${legacyPath} to ${currentPath}: ${
110
- error instanceof Error ? error.message : String(error)
111
- }. Using the legacy config for this session${
112
- writeBlockedReason ? "; global config writes are disabled" : ""
113
- }.`;
114
- return {
115
- status: "failed",
116
- activePath: legacyPath,
117
- diagnostic: message,
118
- notification: message,
119
- writeBlockedReason,
120
- };
121
- }
122
-
123
- function globalConfigConflict(
124
- currentPath: string,
125
- legacyPath: string,
126
- ): GlobalConfigPreparation {
127
- const message =
128
- `Auto Mode config conflict: using ${currentPath}; legacy config ${legacyPath} is ignored and was not changed.`;
129
- return {
130
- status: "conflict",
131
- activePath: currentPath,
132
- diagnostic: message,
133
- notification: message,
134
- };
135
- }
136
-
137
- /** Select one global config path for this runtime and migrate a legacy file when possible. */
138
- export function prepareGlobalConfig(
139
- options: PrepareGlobalConfigOptions = {},
140
- ): GlobalConfigPreparation {
141
- const currentPath = options.currentPath ?? PI_GLOBAL_SETTINGS[0];
142
- const legacyPath = options.legacyPath ?? PI_LEGACY_GLOBAL_SETTINGS;
143
- const currentExists = existsSync(currentPath);
144
- const legacyExists = existsSync(legacyPath);
145
- const unlinkFile = options.unlinkFile ?? unlinkSync;
146
-
147
- if (currentExists && legacyExists) {
148
- if (!sameFileIdentity(currentPath, legacyPath)) {
149
- return globalConfigConflict(currentPath, legacyPath);
150
- }
151
- try {
152
- unlinkFile(legacyPath);
153
- return {
154
- status: "migrated",
155
- activePath: currentPath,
156
- notification:
157
- `Completed interrupted Auto Mode config migration from ${legacyPath} to ${currentPath}.`,
158
- };
159
- } catch (error) {
160
- const cleanupError = cleanupPublishedDestination(
161
- currentPath,
162
- unlinkFile,
163
- error,
164
- );
165
- return globalConfigFailure(
166
- currentPath,
167
- legacyPath,
168
- cleanupError ?? error,
169
- cleanupError?.message,
170
- );
171
- }
172
- }
173
- if (currentExists || !legacyExists) {
174
- return { status: "current", activePath: currentPath };
175
- }
176
-
177
- try {
178
- mkdirSync(dirname(currentPath), { recursive: true });
179
- const moveFile = options.moveFile ??
180
- ((source: string, destination: string) =>
181
- moveFileWithoutOverwrite(source, destination, unlinkFile));
182
- moveFile(legacyPath, currentPath);
183
- return {
184
- status: "migrated",
185
- activePath: currentPath,
186
- notification: `Moved Auto Mode config from ${legacyPath} to ${currentPath}.`,
187
- };
188
- } catch (error) {
189
- if (existsSync(currentPath)) {
190
- if (!sameFileIdentity(currentPath, legacyPath)) {
191
- return globalConfigConflict(currentPath, legacyPath);
192
- }
193
- const cleanupError = cleanupPublishedDestination(
194
- currentPath,
195
- unlinkFile,
196
- error,
197
- );
198
- if (cleanupError) {
199
- return globalConfigFailure(
200
- currentPath,
201
- legacyPath,
202
- cleanupError,
203
- cleanupError.message,
204
- );
205
- }
206
- }
207
- return globalConfigFailure(currentPath, legacyPath, error);
208
- }
209
- }
210
-
211
- function readSettingsFile(path: string): LoadedSettingsFile | undefined {
212
- if (!existsSync(path)) return undefined;
213
- try {
214
- const settings = JSON.parse(readFileSync(path, "utf8")) as SettingsFile;
215
- return {
216
- path,
217
- settings,
218
- diagnostics: validateSettingsFile(settings, path),
219
- };
220
- } catch (error) {
221
- return {
222
- path,
223
- diagnostics: [
224
- `${path}: invalid JSON (${
225
- error instanceof Error ? error.message : String(error)
226
- })`,
227
- ],
228
- };
229
- }
230
- }
231
-
232
- function validateStringArraySetting(
233
- value: unknown,
234
- source: string,
235
- key: string,
236
- diagnostics: string[],
237
- ): void {
238
- if (value === undefined) return;
239
- if (!Array.isArray(value)) {
240
- diagnostics.push(`${source}: ${key} must be an array of strings`);
241
- return;
242
- }
243
- for (const [index, entry] of value.entries()) {
244
- if (typeof entry !== "string" || entry.trim() === "") {
245
- diagnostics.push(
246
- `${source}: ${key}[${index}] must be a non-empty string`,
247
- );
248
- }
249
- }
250
- if (value.length > 0 && !value.includes("$defaults")) {
251
- diagnostics.push(
252
- `${source}: ${key} omits "$defaults" and replaces the built-in ${key} rules`,
253
- );
254
- }
255
- }
256
-
257
- /** Validate config shape and emit human-readable diagnostics for `/automode config`. */
258
- export function validateSettingsFile(
259
- settings: SettingsFile,
260
- source: string,
261
- ): string[] {
262
- const diagnostics: string[] = [];
263
- const root = settings as Record<string, unknown>;
264
- for (const key of Object.keys(root)) {
265
- if (key !== "autoMode" && key !== "permissions") {
266
- diagnostics.push(`${source}: unknown top-level key ${key}`);
267
- }
268
- }
269
-
270
- if (settings.autoMode !== undefined) {
271
- if (
272
- !settings.autoMode ||
273
- typeof settings.autoMode !== "object" ||
274
- Array.isArray(settings.autoMode)
275
- ) {
276
- diagnostics.push(`${source}: autoMode must be an object`);
277
- } else {
278
- const autoMode = settings.autoMode as Record<string, unknown>;
279
- const knownAutoMode = new Set([
280
- "enabled",
281
- "classifierModel",
282
- "classifierReasoningLevel",
283
- "classifierTimeoutMs",
284
- "classifyReadOnlyTools",
285
- "fastClassifierMaxTokens",
286
- "allowInsideWorkingDirectory",
287
- "deniedPaths",
288
- "maxUserTranscriptTokens",
289
- "maxToolTranscriptTokens",
290
- "environment",
291
- "allow",
292
- "protectedPaths",
293
- "soft_deny",
294
- "softDeny",
295
- "hard_deny",
296
- "hardDeny",
297
- "log",
298
- ]);
299
- for (const key of Object.keys(autoMode)) {
300
- if (!knownAutoMode.has(key)) {
301
- diagnostics.push(`${source}: unknown autoMode key ${key}`);
302
- }
303
- }
304
- if (
305
- hasOwn(autoMode, "enabled") && typeof autoMode.enabled !== "boolean"
306
- ) {
307
- diagnostics.push(`${source}: autoMode.enabled must be a boolean`);
308
- }
309
- if (
310
- hasOwn(autoMode, "classifierModel") &&
311
- typeof autoMode.classifierModel !== "string"
312
- ) {
313
- diagnostics.push(
314
- `${source}: autoMode.classifierModel must be a provider/model string`,
315
- );
316
- }
317
- if (
318
- hasOwn(autoMode, "classifierReasoningLevel") &&
319
- !isClassifierReasoningLevel(autoMode.classifierReasoningLevel)
320
- ) {
321
- diagnostics.push(
322
- `${source}: autoMode.classifierReasoningLevel must be one of low, medium, high, xhigh, max`,
323
- );
324
- }
325
- if (
326
- hasOwn(autoMode, "classifierTimeoutMs") &&
327
- (!Number.isInteger(autoMode.classifierTimeoutMs) ||
328
- (autoMode.classifierTimeoutMs as number) < 1000 ||
329
- (autoMode.classifierTimeoutMs as number) > MAX_CLASSIFIER_TIMEOUT_MS)
330
- ) {
331
- diagnostics.push(
332
- `${source}: autoMode.classifierTimeoutMs must be an integer from 1000 through ${MAX_CLASSIFIER_TIMEOUT_MS}`,
333
- );
334
- }
335
- if (
336
- hasOwn(autoMode, "classifyReadOnlyTools") &&
337
- typeof autoMode.classifyReadOnlyTools !== "boolean"
338
- ) {
339
- diagnostics.push(
340
- `${source}: autoMode.classifyReadOnlyTools must be a boolean`,
341
- );
342
- }
343
- if (
344
- hasOwn(autoMode, "fastClassifierMaxTokens") &&
345
- (!Number.isInteger(autoMode.fastClassifierMaxTokens) ||
346
- (autoMode.fastClassifierMaxTokens as number) < 16)
347
- ) {
348
- diagnostics.push(
349
- `${source}: autoMode.fastClassifierMaxTokens must be an integer of at least 16`,
350
- );
351
- }
352
- if (
353
- hasOwn(autoMode, "allowInsideWorkingDirectory") &&
354
- typeof autoMode.allowInsideWorkingDirectory !== "boolean"
355
- ) {
356
- diagnostics.push(
357
- `${source}: autoMode.allowInsideWorkingDirectory must be a boolean`,
358
- );
359
- }
360
- validateDeniedPathsSetting(
361
- autoMode.deniedPaths,
362
- source,
363
- diagnostics,
364
- );
365
- for (
366
- const key of [
367
- "maxUserTranscriptTokens",
368
- "maxToolTranscriptTokens",
369
- ] as const
370
- ) {
371
- if (
372
- hasOwn(autoMode, key) &&
373
- (!Number.isInteger(autoMode[key]) || Number(autoMode[key]) < 32)
374
- ) {
375
- diagnostics.push(
376
- `${source}: autoMode.${key} must be an integer of at least 32`,
377
- );
378
- }
379
- }
380
- validateStringArraySetting(
381
- autoMode.environment,
382
- source,
383
- "autoMode.environment",
384
- diagnostics,
385
- );
386
- validateStringArraySetting(
387
- autoMode.allow,
388
- source,
389
- "autoMode.allow",
390
- diagnostics,
391
- );
392
- validateStringArraySetting(
393
- autoMode.protectedPaths,
394
- source,
395
- "autoMode.protectedPaths",
396
- diagnostics,
397
- );
398
- validateStringArraySetting(
399
- autoMode.soft_deny ?? autoMode.softDeny,
400
- source,
401
- "autoMode.soft_deny",
402
- diagnostics,
403
- );
404
- validateStringArraySetting(
405
- autoMode.hard_deny ?? autoMode.hardDeny,
406
- source,
407
- "autoMode.hard_deny",
408
- diagnostics,
409
- );
410
- if (hasOwn(autoMode, "log")) {
411
- validateLogSetting(autoMode.log, source, diagnostics);
412
- }
413
- }
414
- }
415
-
416
- if (settings.permissions !== undefined) {
417
- if (
418
- !settings.permissions ||
419
- typeof settings.permissions !== "object" ||
420
- Array.isArray(settings.permissions)
421
- ) {
422
- diagnostics.push(`${source}: permissions must be an object`);
423
- } else {
424
- const permissions = settings.permissions as Record<string, unknown>;
425
- for (const key of Object.keys(permissions)) {
426
- if (key !== "deny" && key !== "ask" && key !== "allow") {
427
- diagnostics.push(`${source}: unknown permissions key ${key}`);
428
- }
429
- }
430
- for (const key of ["deny", "ask", "allow"] as const) {
431
- const value = permissions[key];
432
- if (value === undefined) continue;
433
- if (!Array.isArray(value)) {
434
- diagnostics.push(
435
- `${source}: permissions.${key} must be an array of tool patterns`,
436
- );
437
- continue;
438
- }
439
- for (const [index, entry] of value.entries()) {
440
- if (typeof entry !== "string" || !parseToolPattern(entry)) {
441
- diagnostics.push(
442
- `${source}: permissions.${key}[${index}] must be a tool pattern string`,
443
- );
444
- } else if (entry.length > MAX_WILDCARD_PATTERN_LENGTH) {
445
- diagnostics.push(
446
- `${source}: permissions.${key}[${index}] must be at most ${MAX_WILDCARD_PATTERN_LENGTH} characters`,
447
- );
448
- }
449
- }
450
- }
451
- }
452
- }
453
-
454
- return diagnostics;
455
- }
456
-
457
- type RuleAccumulator = {
458
- defaults: string[];
459
- includeDefaults: boolean;
460
- entries: string[];
461
- };
462
-
463
- function createRuleAccumulator(defaults: string[]): RuleAccumulator {
464
- return { defaults, includeDefaults: true, entries: [] };
465
- }
466
-
467
- function applyRuleSetting(
468
- accumulator: RuleAccumulator,
469
- value: unknown,
470
- acceptEntry: (entry: string) => boolean = () => true,
471
- ): void {
472
- const entries = stringArray(value);
473
- if (!entries) return;
474
- // Any entry that stringArray or acceptEntry drops marks the list malformed.
475
- // Fail conservative: keep defaults rather than replace them with a partial list.
476
- let malformed = Array.isArray(value) && value.length !== entries.length;
477
- for (const entry of entries) {
478
- if (entry === "$defaults") continue;
479
- if (acceptEntry(entry)) {
480
- accumulator.entries.push(entry);
481
- } else {
482
- malformed = true;
483
- }
484
- }
485
- accumulator.includeDefaults = entries.includes("$defaults") || malformed;
486
- }
487
-
488
- function finalizeRuleSetting(accumulator: RuleAccumulator): string[] {
489
- const base = accumulator.includeDefaults ? accumulator.defaults : [];
490
- return [...new Set([...base, ...accumulator.entries])];
491
- }
492
-
493
- function validateLogSetting(
494
- value: unknown,
495
- source: string,
496
- diagnostics: string[],
497
- ): void {
498
- if (!value || typeof value !== "object" || Array.isArray(value)) {
499
- diagnostics.push(`${source}: autoMode.log must be an object`);
500
- return;
501
- }
502
- const log = value as Record<string, unknown>;
503
- if (hasOwn(log, "enabled") && typeof log.enabled !== "boolean") {
504
- diagnostics.push(`${source}: autoMode.log.enabled must be a boolean`);
505
- }
506
- if (
507
- hasOwn(log, "classifierIo") && typeof log.classifierIo !== "boolean"
508
- ) {
509
- diagnostics.push(`${source}: autoMode.log.classifierIo must be a boolean`);
510
- }
511
- }
512
-
513
- function mergeLog(
514
- base: LogConfig,
515
- patch: Partial<LogConfig> | undefined,
516
- ): LogConfig {
517
- if (!patch) return base;
518
- return {
519
- enabled: typeof patch.enabled === "boolean" ? patch.enabled : base.enabled,
520
- classifierIo: typeof patch.classifierIo === "boolean" ? patch.classifierIo : base.classifierIo,
521
- };
522
- }
523
-
524
- /**
525
- * Validate `deniedPaths`: an array of non-empty path patterns. Unlike the
526
- * `$defaults` rule lists there is no built-in default list, so `$defaults` is
527
- * a no-op (accepted for consistency with the other rule lists) and omitting
528
- * it is not a diagnostic.
529
- */
530
- function validateDeniedPathsSetting(
531
- value: unknown,
532
- source: string,
533
- diagnostics: string[],
534
- ): void {
535
- if (value === undefined) return;
536
- if (!Array.isArray(value)) {
537
- diagnostics.push(`${source}: deniedPaths must be an array of strings`);
538
- return;
539
- }
540
- for (const [index, entry] of value.entries()) {
541
- if (entry === "$defaults") continue;
542
- if (typeof entry !== "string" || entry.trim() === "") {
543
- diagnostics.push(
544
- `${source}: deniedPaths[${index}] must be a non-empty path pattern`,
545
- );
546
- continue;
547
- }
548
- if (entry.length > MAX_WILDCARD_PATTERN_LENGTH) {
549
- diagnostics.push(
550
- `${source}: deniedPaths[${index}] must be at most ${MAX_WILDCARD_PATTERN_LENGTH} characters`,
551
- );
552
- continue;
553
- }
554
- if (!DENIED_PATH_PATTERN_PREFIX.test(entry)) {
555
- diagnostics.push(
556
- `${source}: deniedPaths[${index}] "${entry}" can never match a resolved absolute path; start it with *, ~, $HOME, \${HOME}, or / (e.g. "**/${entry}")`,
557
- );
558
- }
559
- }
560
- }
561
-
562
- /**
563
- * A pattern can only match a resolved absolute path when it starts with a
564
- * form that anchors it: a leading `/`, a home expansion (`~`, `$HOME`,
565
- * `${HOME}`), or a `*` wildcard that absorbs the leading slash. Anything else
566
- * (e.g. `config.json` or `src/secret.txt`) matches only against the bare
567
- * relative name, which the matcher never sees.
568
- */
569
- const DENIED_PATH_PATTERN_PREFIX =
570
- /^(?:\/|~(?:\/|$)|\$HOME(?:\/|$)|\$\{HOME\}(?:\/|$)|\*)/;
571
-
572
- const CLASSIFIER_REASONING_LEVELS = new Set<ClassifierReasoningLevel>([
573
- "low",
574
- "medium",
575
- "high",
576
- "xhigh",
577
- "max",
578
- ]);
579
-
580
- export function isClassifierReasoningLevel(
581
- value: unknown,
582
- ): value is ClassifierReasoningLevel {
583
- return typeof value === "string" &&
584
- CLASSIFIER_REASONING_LEVELS.has(value as ClassifierReasoningLevel);
585
- }
586
-
587
- function validTranscriptBudget(value: unknown): value is number {
588
- return Number.isInteger(value) && Number(value) >= 32;
589
- }
590
-
591
- function validFastClassifierBudget(value: unknown): value is number {
592
- return Number.isInteger(value) && Number(value) >= 16;
593
- }
594
-
595
- function validClassifierTimeout(value: unknown): value is number {
596
- return Number.isInteger(value) &&
597
- Number(value) >= 1000 &&
598
- Number(value) <= MAX_CLASSIFIER_TIMEOUT_MS;
599
- }
600
-
601
- function applyAutoModeScalars(
602
- base: EffectiveConfig,
603
- settings: AutoModeSettings | undefined,
604
- ): EffectiveConfig {
605
- if (!settings) return base;
606
- return {
607
- ...base,
608
- enabled: typeof settings.enabled === "boolean" ? settings.enabled : base.enabled,
609
- classifierModel: settings.classifierModel ?? base.classifierModel,
610
- classifierReasoningLevel: isClassifierReasoningLevel(
611
- settings.classifierReasoningLevel,
612
- )
613
- ? settings.classifierReasoningLevel
614
- : base.classifierReasoningLevel,
615
- classifyReadOnlyTools: typeof settings.classifyReadOnlyTools === "boolean"
616
- ? settings.classifyReadOnlyTools
617
- : base.classifyReadOnlyTools,
618
- allowInsideWorkingDirectory:
619
- typeof settings.allowInsideWorkingDirectory === "boolean"
620
- ? settings.allowInsideWorkingDirectory
621
- : base.allowInsideWorkingDirectory,
622
- fastClassifierMaxTokens: validFastClassifierBudget(
623
- settings.fastClassifierMaxTokens,
624
- )
625
- ? settings.fastClassifierMaxTokens
626
- : base.fastClassifierMaxTokens,
627
- classifierTimeoutMs: validClassifierTimeout(settings.classifierTimeoutMs)
628
- ? settings.classifierTimeoutMs
629
- : base.classifierTimeoutMs,
630
- maxUserTranscriptTokens: validTranscriptBudget(
631
- settings.maxUserTranscriptTokens,
632
- )
633
- ? settings.maxUserTranscriptTokens
634
- : base.maxUserTranscriptTokens,
635
- maxToolTranscriptTokens: validTranscriptBudget(
636
- settings.maxToolTranscriptTokens,
637
- )
638
- ? settings.maxToolTranscriptTokens
639
- : base.maxToolTranscriptTokens,
640
- log: mergeLog(base.log, settings.log),
641
- };
642
- }
643
-
644
- function appendPermissionPatterns(
645
- target: ToolPattern[],
646
- settings: SettingsFile | undefined,
647
- key: "deny" | "ask" | "allow",
648
- ): void {
649
- const values = stringArray(settings?.permissions?.[key]);
650
- if (!values) return;
651
- for (const value of values) {
652
- if (value.length > MAX_WILDCARD_PATTERN_LENGTH) continue;
653
- const pattern = parseToolPattern(value);
654
- if (pattern) target.push(pattern);
655
- }
656
- }
657
-
658
- /**
659
- * Merge settings with Claude Code-style precedence using Pi-owned config files.
660
- *
661
- * Important details:
662
- * - shared project `.pi/automode.json` contributes `permissions.deny` and
663
- * `permissions.ask` but not `permissions.allow` or `autoMode`, so checked-in
664
- * config can only add permission barriers;
665
- * - global, project-local, and inline `autoMode` settings combine additively across scopes;
666
- * - omitting `$defaults` in any scope for a rule list means "replace built-ins" for that list.
667
- */
668
- export function buildEffectiveConfigFromSources(
669
- sources: SettingsSources = {},
670
- ): EffectiveConfig {
671
- let config: EffectiveConfig = {
672
- enabled: true,
673
- classifyReadOnlyTools: DEFAULT_CLASSIFY_READ_ONLY_TOOLS,
674
- allowInsideWorkingDirectory: DEFAULT_ALLOW_INSIDE_WORKING_DIRECTORY,
675
- deniedPaths: [...DEFAULT_DENIED_PATHS],
676
- fastClassifierMaxTokens: DEFAULT_FAST_CLASSIFIER_MAX_TOKENS,
677
- classifierTimeoutMs: DEFAULT_CLASSIFIER_TIMEOUT_MS,
678
- maxUserTranscriptTokens: DEFAULT_MAX_USER_TRANSCRIPT_TOKENS,
679
- maxToolTranscriptTokens: DEFAULT_MAX_TOOL_TRANSCRIPT_TOKENS,
680
- environment: [...DEFAULT_ENVIRONMENT],
681
- allow: [...DEFAULT_ALLOW],
682
- protectedPaths: [...DEFAULT_PROTECTED_PATHS],
683
- softDeny: [...DEFAULT_SOFT_DENY],
684
- hardDeny: [...DEFAULT_HARD_DENY],
685
- permissionDeny: [],
686
- permissionAsk: [],
687
- permissionAllow: [],
688
- log: { ...DEFAULT_LOG_CONFIG },
689
- };
690
-
691
- const globalSettings = sources.globalSettings ?? [];
692
- const projectLocalSettings = sources.projectLocalSettings ?? [];
693
- const projectSharedSettings = sources.projectSharedSettings ?? [];
694
- const inlineSettings = sources.inlineSettings ?? [];
695
-
696
- const configurableSettings = [
697
- ...globalSettings,
698
- ...projectLocalSettings,
699
- ...inlineSettings,
700
- ];
701
- const environment = createRuleAccumulator(DEFAULT_ENVIRONMENT);
702
- const allow = createRuleAccumulator(DEFAULT_ALLOW);
703
- const protectedPaths = createRuleAccumulator(DEFAULT_PROTECTED_PATHS);
704
- const deniedPaths = createRuleAccumulator(DEFAULT_DENIED_PATHS);
705
- const softDeny = createRuleAccumulator(DEFAULT_SOFT_DENY);
706
- const hardDeny = createRuleAccumulator(DEFAULT_HARD_DENY);
707
-
708
- for (const settings of configurableSettings) {
709
- config = applyAutoModeScalars(config, settings.autoMode);
710
- applyRuleSetting(environment, settings.autoMode?.environment);
711
- applyRuleSetting(allow, settings.autoMode?.allow);
712
- applyRuleSetting(protectedPaths, settings.autoMode?.protectedPaths);
713
- applyRuleSetting(
714
- deniedPaths,
715
- settings.autoMode?.deniedPaths,
716
- (entry) => entry.length <= MAX_WILDCARD_PATTERN_LENGTH,
717
- );
718
- applyRuleSetting(
719
- softDeny,
720
- settings.autoMode?.soft_deny ?? settings.autoMode?.softDeny,
721
- );
722
- applyRuleSetting(
723
- hardDeny,
724
- settings.autoMode?.hard_deny ?? settings.autoMode?.hardDeny,
725
- );
726
- }
727
-
728
- config = {
729
- ...config,
730
- environment: finalizeRuleSetting(environment),
731
- allow: finalizeRuleSetting(allow),
732
- protectedPaths: finalizeRuleSetting(protectedPaths),
733
- deniedPaths: finalizeRuleSetting(deniedPaths),
734
- softDeny: finalizeRuleSetting(softDeny),
735
- hardDeny: finalizeRuleSetting(hardDeny),
736
- };
737
-
738
- for (
739
- const settings of [
740
- ...globalSettings,
741
- ...projectSharedSettings,
742
- ...projectLocalSettings,
743
- ...inlineSettings,
744
- ]
745
- ) {
746
- appendPermissionPatterns(config.permissionDeny, settings, "deny");
747
- appendPermissionPatterns(config.permissionAsk, settings, "ask");
748
- }
749
- for (
750
- const settings of [
751
- ...globalSettings,
752
- ...projectLocalSettings,
753
- ...inlineSettings,
754
- ]
755
- ) {
756
- appendPermissionPatterns(config.permissionAllow, settings, "allow");
757
- }
758
-
759
- return config;
760
- }
761
-
762
- function loadedSettingsToSettings(
763
- files: Array<LoadedSettingsFile | undefined>,
764
- ): SettingsFile[] {
765
- return files.flatMap((file) => (file?.settings ? [file.settings] : []));
766
- }
767
-
768
- function loadedSettingsDiagnostics(
769
- files: Array<LoadedSettingsFile | undefined>,
770
- ): string[] {
771
- return files.flatMap((file) => file?.diagnostics ?? []);
772
- }
773
-
774
- function ignoredSharedAllowDiagnostics(
775
- files: Array<LoadedSettingsFile | undefined>,
776
- ): string[] {
777
- return files.flatMap((file) => {
778
- const permissions = file?.settings?.permissions;
779
- if (!file || !permissions || !hasOwn(permissions, "allow")) return [];
780
- return [
781
- `${file.path}: permissions.allow is ignored in shared project config. Use a user-owned config source instead`,
782
- ];
783
- });
784
- }
785
-
786
- /** Load config from disk and environment variables, including diagnostics for `/automode config`. Project files require explicit trust. */
787
- export function loadEffectiveConfigWithDiagnostics(
788
- cwd: string,
789
- projectTrusted = false,
790
- globalSettingsPath = PI_GLOBAL_SETTINGS[0],
791
- ): ConfigLoadResult {
792
- const inlineSettings: SettingsFile[] = [];
793
- const diagnostics: string[] = [];
794
- if (process.env.PI_AUTOMODE_SETTINGS_JSON) {
795
- try {
796
- const parsed = JSON.parse(
797
- process.env.PI_AUTOMODE_SETTINGS_JSON,
798
- ) as SettingsFile;
799
- inlineSettings.push(parsed);
800
- diagnostics.push(
801
- ...validateSettingsFile(parsed, "PI_AUTOMODE_SETTINGS_JSON"),
802
- );
803
- } catch (error) {
804
- diagnostics.push(
805
- `PI_AUTOMODE_SETTINGS_JSON: invalid JSON (${
806
- error instanceof Error ? error.message : String(error)
807
- })`,
808
- );
809
- }
810
- }
811
-
812
- const globalFiles = [readSettingsFile(globalSettingsPath)];
813
- const projectLocalPaths = PI_PROJECT_LOCAL_SETTINGS.map((file) =>
814
- resolve(cwd, file)
815
- );
816
- const projectSharedPaths = PI_PROJECT_SHARED_SETTINGS.map((file) =>
817
- resolve(cwd, file)
818
- );
819
- const projectLocalFiles = projectTrusted
820
- ? projectLocalPaths.map(readSettingsFile)
821
- : [];
822
- const projectSharedFiles = projectTrusted
823
- ? projectSharedPaths.map(readSettingsFile)
824
- : [];
825
- if (!projectTrusted) {
826
- for (
827
- const path of [...projectLocalPaths, ...projectSharedPaths].filter(
828
- existsSync,
829
- )
830
- ) {
831
- diagnostics.push(`${path}: ignored because project is not trusted`);
832
- }
833
- }
834
- const fileDiagnostics = loadedSettingsDiagnostics([
835
- ...globalFiles,
836
- ...projectLocalFiles,
837
- ...projectSharedFiles,
838
- ]);
839
- const sharedAllowDiagnostics = ignoredSharedAllowDiagnostics(
840
- projectSharedFiles,
841
- );
842
-
843
- return {
844
- config: buildEffectiveConfigFromSources({
845
- globalSettings: loadedSettingsToSettings(globalFiles),
846
- projectLocalSettings: loadedSettingsToSettings(projectLocalFiles),
847
- projectSharedSettings: loadedSettingsToSettings(projectSharedFiles),
848
- inlineSettings,
849
- }),
850
- diagnostics: [
851
- ...fileDiagnostics,
852
- ...sharedAllowDiagnostics,
853
- ...diagnostics,
854
- ],
855
- };
856
- }
857
-
858
- /** Load config from disk and environment variables. Exported for tests and diagnostics. */
859
- export function loadEffectiveConfig(
860
- cwd: string,
861
- projectTrusted = false,
862
- globalSettingsPath = PI_GLOBAL_SETTINGS[0],
863
- ): EffectiveConfig {
864
- return loadEffectiveConfigWithDiagnostics(
865
- cwd,
866
- projectTrusted,
867
- globalSettingsPath,
868
- ).config;
869
- }
870
-
871
- function readWritableSettingsFile(path: string): SettingsFile {
872
- if (!existsSync(path)) return {};
873
- const parsed = JSON.parse(readFileSync(path, "utf8")) as unknown;
874
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
875
- throw new Error(`${path}: root must be a JSON object`);
876
- }
877
- const settings = parsed as SettingsFile;
878
- if (
879
- settings.autoMode !== undefined &&
880
- (!settings.autoMode ||
881
- typeof settings.autoMode !== "object" ||
882
- Array.isArray(settings.autoMode))
883
- ) {
884
- throw new Error(`${path}: autoMode must be a JSON object`);
885
- }
886
- return settings;
887
- }
888
-
889
- /** Persist the global default classifier model while preserving other settings. */
890
- export function writeGlobalClassifierModel(
891
- classifierModel: string,
892
- path = PI_GLOBAL_SETTINGS[0],
893
- ): void {
894
- const settings = readWritableSettingsFile(path);
895
- const next: SettingsFile = {
896
- ...settings,
897
- autoMode: {
898
- ...settings.autoMode,
899
- classifierModel,
900
- },
901
- };
902
- mkdirSync(dirname(path), { recursive: true });
903
- writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
904
- }