@herbertgao/pi-extensions 2026.8.3 → 2026.8.5

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 (408) hide show
  1. package/README.md +45 -4
  2. package/examples/pi-footer.json +275 -0
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +10 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/README.md +10 -2
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +5 -8
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
  14. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  15. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
  16. package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
  17. package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
  18. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
  19. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +15 -2
  21. package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
  22. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
  25. package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
  26. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
  27. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  28. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
  29. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
  30. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
  31. package/node_modules/@narumitw/pi-btw/README.md +8 -7
  32. package/node_modules/@narumitw/pi-btw/package.json +2 -2
  33. package/node_modules/@narumitw/pi-btw/src/menu.ts +1 -0
  34. package/node_modules/pi-footer/CHANGELOG.md +145 -0
  35. package/node_modules/pi-footer/LICENSE +21 -0
  36. package/node_modules/pi-footer/README.md +354 -0
  37. package/node_modules/pi-footer/package.json +68 -0
  38. package/node_modules/pi-footer/src/cache.ts +106 -0
  39. package/node_modules/pi-footer/src/colors.ts +237 -0
  40. package/node_modules/pi-footer/src/config.ts +202 -0
  41. package/node_modules/pi-footer/src/event-widgets.ts +41 -0
  42. package/node_modules/pi-footer/src/extension-statuses.ts +102 -0
  43. package/node_modules/pi-footer/src/git.ts +135 -0
  44. package/node_modules/pi-footer/src/index.ts +268 -0
  45. package/node_modules/pi-footer/src/metrics.ts +141 -0
  46. package/node_modules/pi-footer/src/presets.ts +383 -0
  47. package/node_modules/pi-footer/src/render.ts +114 -0
  48. package/node_modules/pi-footer/src/separators.ts +52 -0
  49. package/node_modules/pi-footer/src/types.ts +122 -0
  50. package/node_modules/pi-footer/src/ui/color-level-confirm.ts +22 -0
  51. package/node_modules/pi-footer/src/ui/color-options.ts +195 -0
  52. package/node_modules/pi-footer/src/ui/config-lifecycle.ts +61 -0
  53. package/node_modules/pi-footer/src/ui/edit-colors.ts +13 -0
  54. package/node_modules/pi-footer/src/ui/events.ts +35 -0
  55. package/node_modules/pi-footer/src/ui/extension-status-picker.ts +60 -0
  56. package/node_modules/pi-footer/src/ui/extension-statuses.ts +73 -0
  57. package/node_modules/pi-footer/src/ui/fields.ts +252 -0
  58. package/node_modules/pi-footer/src/ui/global-menu.ts +126 -0
  59. package/node_modules/pi-footer/src/ui/helpers.ts +44 -0
  60. package/node_modules/pi-footer/src/ui/layout.ts +17 -0
  61. package/node_modules/pi-footer/src/ui/line-list.ts +19 -0
  62. package/node_modules/pi-footer/src/ui/model.ts +64 -0
  63. package/node_modules/pi-footer/src/ui/navigation.ts +29 -0
  64. package/node_modules/pi-footer/src/ui/option-edit.ts +93 -0
  65. package/node_modules/pi-footer/src/ui/overlay-render.ts +103 -0
  66. package/node_modules/pi-footer/src/ui/screen-context.ts +18 -0
  67. package/node_modules/pi-footer/src/ui/screen-controller.ts +27 -0
  68. package/node_modules/pi-footer/src/ui/screen-render.ts +32 -0
  69. package/node_modules/pi-footer/src/ui/screen-state.ts +20 -0
  70. package/node_modules/pi-footer/src/ui/screen.ts +243 -0
  71. package/node_modules/pi-footer/src/ui/screens/add-widget.ts +92 -0
  72. package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +52 -0
  73. package/node_modules/pi-footer/src/ui/screens/controller.ts +12 -0
  74. package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +68 -0
  75. package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +115 -0
  76. package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +61 -0
  77. package/node_modules/pi-footer/src/ui/screens/global.ts +75 -0
  78. package/node_modules/pi-footer/src/ui/screens/line-list.ts +108 -0
  79. package/node_modules/pi-footer/src/ui/screens/main.ts +99 -0
  80. package/node_modules/pi-footer/src/ui/screens/terminal.ts +121 -0
  81. package/node_modules/pi-footer/src/ui/screens/widget-list.ts +154 -0
  82. package/node_modules/pi-footer/src/ui/terminal-menu.ts +32 -0
  83. package/node_modules/pi-footer/src/ui/theme.ts +48 -0
  84. package/node_modules/pi-footer/src/ui/title-bar.ts +100 -0
  85. package/node_modules/pi-footer/src/ui/widget-actions.ts +76 -0
  86. package/node_modules/pi-footer/src/ui.ts +66 -0
  87. package/node_modules/pi-footer/src/widgets/context.ts +28 -0
  88. package/node_modules/pi-footer/src/widgets/core/active-tools.ts +17 -0
  89. package/node_modules/pi-footer/src/widgets/core/context-window.ts +24 -0
  90. package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +19 -0
  91. package/node_modules/pi-footer/src/widgets/core/cwd.ts +108 -0
  92. package/node_modules/pi-footer/src/widgets/core/event.ts +34 -0
  93. package/node_modules/pi-footer/src/widgets/core/external-status.ts +102 -0
  94. package/node_modules/pi-footer/src/widgets/core/model-provider.ts +20 -0
  95. package/node_modules/pi-footer/src/widgets/core/model.ts +35 -0
  96. package/node_modules/pi-footer/src/widgets/core/provider.ts +17 -0
  97. package/node_modules/pi-footer/src/widgets/core/session-name.ts +17 -0
  98. package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +17 -0
  99. package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +17 -0
  100. package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +17 -0
  101. package/node_modules/pi-footer/src/widgets/git/branch.ts +81 -0
  102. package/node_modules/pi-footer/src/widgets/git/clean.ts +19 -0
  103. package/node_modules/pi-footer/src/widgets/git/deletions.ts +17 -0
  104. package/node_modules/pi-footer/src/widgets/git/diff.ts +38 -0
  105. package/node_modules/pi-footer/src/widgets/git/insertions.ts +17 -0
  106. package/node_modules/pi-footer/src/widgets/git/remote.ts +17 -0
  107. package/node_modules/pi-footer/src/widgets/git/root.ts +17 -0
  108. package/node_modules/pi-footer/src/widgets/git/sha.ts +17 -0
  109. package/node_modules/pi-footer/src/widgets/git/staged.ts +17 -0
  110. package/node_modules/pi-footer/src/widgets/git/status.ts +19 -0
  111. package/node_modules/pi-footer/src/widgets/git/unstaged.ts +17 -0
  112. package/node_modules/pi-footer/src/widgets/git/untracked.ts +17 -0
  113. package/node_modules/pi-footer/src/widgets/instance.ts +118 -0
  114. package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +18 -0
  115. package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +32 -0
  116. package/node_modules/pi-footer/src/widgets/layout/separator.ts +57 -0
  117. package/node_modules/pi-footer/src/widgets/layout/spacer.ts +31 -0
  118. package/node_modules/pi-footer/src/widgets/options.ts +122 -0
  119. package/node_modules/pi-footer/src/widgets/project/runtime.ts +361 -0
  120. package/node_modules/pi-footer/src/widgets/registry.ts +223 -0
  121. package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +17 -0
  122. package/node_modules/pi-footer/src/widgets/session/compactions.ts +17 -0
  123. package/node_modules/pi-footer/src/widgets/session/elapsed.ts +18 -0
  124. package/node_modules/pi-footer/src/widgets/session/last-activity.ts +18 -0
  125. package/node_modules/pi-footer/src/widgets/session/messages.ts +19 -0
  126. package/node_modules/pi-footer/src/widgets/session/session-id.ts +17 -0
  127. package/node_modules/pi-footer/src/widgets/session/session-start.ts +18 -0
  128. package/node_modules/pi-footer/src/widgets/session/tool-results.ts +17 -0
  129. package/node_modules/pi-footer/src/widgets/session/total-messages.ts +19 -0
  130. package/node_modules/pi-footer/src/widgets/session/total-time.ts +18 -0
  131. package/node_modules/pi-footer/src/widgets/session/user-messages.ts +17 -0
  132. package/node_modules/pi-footer/src/widgets/store.ts +26 -0
  133. package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +55 -0
  134. package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +18 -0
  135. package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +18 -0
  136. package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +108 -0
  137. package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +26 -0
  138. package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +29 -0
  139. package/node_modules/pi-footer/src/widgets/tokens/context.ts +29 -0
  140. package/node_modules/pi-footer/src/widgets/tokens/cost.ts +51 -0
  141. package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +25 -0
  142. package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +18 -0
  143. package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +25 -0
  144. package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +18 -0
  145. package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +20 -0
  146. package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +25 -0
  147. package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +18 -0
  148. package/node_modules/pi-footer/src/widgets/types.ts +181 -0
  149. package/node_modules/pi-footer/src/widgets/utils/colors.ts +11 -0
  150. package/node_modules/pi-footer/src/widgets/utils/context.ts +112 -0
  151. package/node_modules/pi-footer/src/widgets/utils/session.ts +19 -0
  152. package/node_modules/pi-footer/src/widgets/utils/token-format.ts +78 -0
  153. package/node_modules/pi-lens/CHANGELOG.md +2440 -954
  154. package/node_modules/pi-lens/README.md +13 -0
  155. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
  156. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
  157. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
  158. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
  159. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
  160. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
  161. package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
  162. package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
  163. package/node_modules/pi-lens/dist/clients/biome-client.js +29 -25
  164. package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
  165. package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
  166. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
  167. package/node_modules/pi-lens/dist/clients/bus-publish.js +10 -3
  168. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
  169. package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
  170. package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
  171. package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
  172. package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
  173. package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
  174. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
  175. package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
  176. package/node_modules/pi-lens/dist/clients/console-guard-install.js +14 -0
  177. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
  178. package/node_modules/pi-lens/dist/clients/dead-code-client.js +2 -1
  179. package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
  180. package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
  181. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
  182. package/node_modules/pi-lens/dist/clients/dependency-checker.js +347 -75
  183. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +61 -11
  184. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  185. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +63 -20
  186. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
  187. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
  188. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
  189. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
  190. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
  191. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1085 -681
  192. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
  193. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
  194. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
  195. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
  196. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
  197. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
  198. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
  199. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
  200. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
  201. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
  202. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
  203. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +14 -6
  204. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
  205. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
  206. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
  207. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
  208. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
  209. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
  210. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
  211. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
  212. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
  213. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
  214. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
  215. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
  216. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +277 -26
  217. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
  218. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
  219. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  220. package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
  221. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
  222. package/node_modules/pi-lens/dist/clients/extension-log.js +139 -0
  223. package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
  224. package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
  225. package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
  226. package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
  227. package/node_modules/pi-lens/dist/clients/format-events-publish.js +16 -3
  228. package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
  229. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
  230. package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
  231. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
  232. package/node_modules/pi-lens/dist/clients/git-guard.js +681 -11
  233. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
  234. package/node_modules/pi-lens/dist/clients/go-client.js +2 -1
  235. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +8 -0
  236. package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
  237. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
  238. package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
  239. package/node_modules/pi-lens/dist/clients/installer/index.js +390 -99
  240. package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
  241. package/node_modules/pi-lens/dist/clients/jscpd-client.js +31 -54
  242. package/node_modules/pi-lens/dist/clients/knip-client.js +15 -36
  243. package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
  244. package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
  245. package/node_modules/pi-lens/dist/clients/latency-logger.js +23 -1
  246. package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
  247. package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
  248. package/node_modules/pi-lens/dist/clients/lens-events.js +27 -4
  249. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  250. package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
  251. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +30 -0
  252. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
  253. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +1 -1
  254. package/node_modules/pi-lens/dist/clients/lsp/client.js +493 -48
  255. package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
  256. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +86 -0
  257. package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
  258. package/node_modules/pi-lens/dist/clients/lsp/index.js +1807 -780
  259. package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
  260. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  261. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
  262. package/node_modules/pi-lens/dist/clients/lsp/server.js +196 -32
  263. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
  264. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
  265. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
  266. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +123 -32
  267. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
  268. package/node_modules/pi-lens/dist/clients/mcp/session.js +216 -8
  269. package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
  270. package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
  271. package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
  272. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
  273. package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
  274. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
  275. package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
  276. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
  277. package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
  278. package/node_modules/pi-lens/dist/clients/pipeline.js +47 -13
  279. package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
  280. package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
  281. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
  282. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
  283. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
  284. package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
  285. package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
  286. package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
  287. package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
  288. package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
  289. package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
  290. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
  291. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
  292. package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
  293. package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
  294. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
  295. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
  296. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
  297. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
  298. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
  299. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
  300. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
  301. package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
  302. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +107 -73
  303. package/node_modules/pi-lens/dist/clients/runtime-context.js +36 -12
  304. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +74 -6
  305. package/node_modules/pi-lens/dist/clients/runtime-session.js +405 -117
  306. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
  307. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +218 -23
  308. package/node_modules/pi-lens/dist/clients/runtime-turn.js +237 -64
  309. package/node_modules/pi-lens/dist/clients/rust-client.js +2 -1
  310. package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
  311. package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -1
  312. package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
  313. package/node_modules/pi-lens/dist/clients/sg-runner.js +284 -174
  314. package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
  315. package/node_modules/pi-lens/dist/clients/smells-rollup.js +224 -0
  316. package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
  317. package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
  318. package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
  319. package/node_modules/pi-lens/dist/clients/startup-timing.js +1 -1
  320. package/node_modules/pi-lens/dist/clients/test-runner-client.js +38 -8
  321. package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
  322. package/node_modules/pi-lens/dist/clients/tool-policy.js +388 -39
  323. package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
  324. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
  325. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
  326. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
  327. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
  328. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
  329. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
  330. package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
  331. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
  332. package/node_modules/pi-lens/dist/clients/warm-attach.js +33 -13
  333. package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
  334. package/node_modules/pi-lens/dist/clients/word-index.js +391 -64
  335. package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
  336. package/node_modules/pi-lens/dist/index.js +49361 -36888
  337. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
  338. package/node_modules/pi-lens/dist/mcp/server.js +211 -44
  339. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
  340. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +200 -36
  341. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +153 -40
  342. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
  343. package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
  344. package/node_modules/pi-lens/docs/agent-guide.md +2 -2
  345. package/node_modules/pi-lens/docs/agent-tools.md +21 -10
  346. package/node_modules/pi-lens/docs/analysisall.md +192 -0
  347. package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
  348. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +5 -2
  349. package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
  350. package/node_modules/pi-lens/docs/environment-variables.md +44 -0
  351. package/node_modules/pi-lens/docs/fable.md +27 -0
  352. package/node_modules/pi-lens/docs/features.md +31 -2
  353. package/node_modules/pi-lens/docs/globalconfig.md +45 -1
  354. package/node_modules/pi-lens/docs/language-coverage.md +4 -3
  355. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
  356. package/node_modules/pi-lens/docs/mcp.md +37 -4
  357. package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
  358. package/node_modules/pi-lens/docs/settings.md +12 -1
  359. package/node_modules/pi-lens/docs/tools.md +4 -1
  360. package/node_modules/pi-lens/docs/usage.md +18 -0
  361. package/node_modules/pi-lens/package.json +10 -10
  362. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
  363. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
  364. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
  365. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
  366. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
  367. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
  368. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
  369. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
  370. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
  371. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
  372. package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
  373. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
  374. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +55 -2
  375. package/node_modules/pi-mcp-adapter/CHANGELOG.md +25 -0
  376. package/node_modules/pi-mcp-adapter/README.md +34 -3
  377. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
  378. package/node_modules/pi-mcp-adapter/commands.ts +15 -7
  379. package/node_modules/pi-mcp-adapter/direct-tools.ts +54 -7
  380. package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
  381. package/node_modules/pi-mcp-adapter/index.ts +8 -3
  382. package/node_modules/pi-mcp-adapter/init.ts +29 -5
  383. package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
  384. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
  385. package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
  386. package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
  387. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
  388. package/node_modules/pi-mcp-adapter/metadata-cache.ts +27 -4
  389. package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
  390. package/node_modules/pi-mcp-adapter/package.json +3 -3
  391. package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
  392. package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
  393. package/node_modules/pi-mcp-adapter/server-manager.ts +36 -7
  394. package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
  395. package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
  396. package/node_modules/pi-mcp-adapter/tool-metadata.ts +42 -4
  397. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
  398. package/node_modules/pi-mcp-adapter/types.ts +93 -27
  399. package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
  400. package/package.json +17 -13
  401. package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
  402. package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
  403. package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
  404. package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
  405. package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
  406. package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
  407. package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
  408. package/node_modules/pi-lens/dist/clients/types.js +0 -11
@@ -0,0 +1,237 @@
1
+ import type { Theme, ThemeColor } from "@earendil-works/pi-coding-agent";
2
+ import { Chalk } from "chalk";
3
+
4
+ const chalk = {
5
+ level: 2,
6
+ c: new Chalk({ level: 2 }),
7
+ };
8
+
9
+ export const COLOR_LEVEL_VALUES = ["truecolor", "ansi256", "ansi16", "none"] as const;
10
+
11
+ export type ColorLevel = (typeof COLOR_LEVEL_VALUES)[number];
12
+ export type ColorName =
13
+ | "default"
14
+ | "black"
15
+ | "red"
16
+ | "green"
17
+ | "yellow"
18
+ | "blue"
19
+ | "magenta"
20
+ | "cyan"
21
+ | "white"
22
+ | "brightBlack"
23
+ | "brightRed"
24
+ | "brightGreen"
25
+ | "brightYellow"
26
+ | "brightBlue"
27
+ | "brightMagenta"
28
+ | "brightCyan"
29
+ | "brightWhite"
30
+ | `ansi256:${number}`
31
+ | `pi:${ThemeColor}`;
32
+
33
+ interface ColorChoice {
34
+ label: string;
35
+ value: ColorName;
36
+ }
37
+
38
+ export const STANDARD_COLORS: ColorChoice[] = [
39
+ { label: "Default", value: "default" },
40
+ { label: "Black", value: "black" },
41
+ { label: "Red", value: "red" },
42
+ { label: "Green", value: "green" },
43
+ { label: "Yellow", value: "yellow" },
44
+ { label: "Blue", value: "blue" },
45
+ { label: "Magenta", value: "magenta" },
46
+ { label: "Cyan", value: "cyan" },
47
+ { label: "White", value: "white" },
48
+ { label: "Bright Black", value: "brightBlack" },
49
+ { label: "Bright Red", value: "brightRed" },
50
+ { label: "Bright Green", value: "brightGreen" },
51
+ { label: "Bright Yellow", value: "brightYellow" },
52
+ { label: "Bright Blue", value: "brightBlue" },
53
+ { label: "Bright Magenta", value: "brightMagenta" },
54
+ { label: "Bright Cyan", value: "brightCyan" },
55
+ { label: "Bright White", value: "brightWhite" },
56
+ ];
57
+
58
+ const PI_THEME_COLORS: ThemeColor[] = [
59
+ "accent",
60
+ "border",
61
+ "borderAccent",
62
+ "borderMuted",
63
+ "success",
64
+ "error",
65
+ "warning",
66
+ "muted",
67
+ "dim",
68
+ "text",
69
+ "thinkingText",
70
+ "userMessageText",
71
+ "customMessageText",
72
+ "customMessageLabel",
73
+ "toolTitle",
74
+ "toolOutput",
75
+ "toolDiffAdded",
76
+ "toolDiffRemoved",
77
+ "toolDiffContext",
78
+ "syntaxType",
79
+ "thinkingOff",
80
+ "thinkingMinimal",
81
+ "thinkingLow",
82
+ "thinkingMedium",
83
+ "thinkingHigh",
84
+ "thinkingXhigh",
85
+ "bashMode",
86
+ ];
87
+
88
+ const PI_FOREGROUND_COLORS: ColorChoice[] = PI_THEME_COLORS.map((color) => ({
89
+ label: `Pi ${themeColorDisplayName(color)}`,
90
+ value: `pi:${color}`,
91
+ }));
92
+
93
+ export const FOREGROUND_COLORS: ColorChoice[] = [...STANDARD_COLORS, ...PI_FOREGROUND_COLORS];
94
+
95
+ const ANSI16_FG: Record<
96
+ Exclude<ColorName, `ansi256:${number}` | `pi:${ThemeColor}`>,
97
+ [number, number]
98
+ > = {
99
+ default: [39, 49],
100
+ black: [30, 40],
101
+ red: [31, 41],
102
+ green: [32, 42],
103
+ yellow: [33, 43],
104
+ blue: [34, 44],
105
+ magenta: [35, 45],
106
+ cyan: [36, 46],
107
+ white: [37, 47],
108
+ brightBlack: [90, 100],
109
+ brightRed: [91, 101],
110
+ brightGreen: [92, 102],
111
+ brightYellow: [93, 103],
112
+ brightBlue: [94, 104],
113
+ brightMagenta: [95, 105],
114
+ brightCyan: [96, 106],
115
+ brightWhite: [97, 107],
116
+ };
117
+
118
+ export function normalizeColor(value: unknown): ColorName | undefined {
119
+ if (typeof value !== "string") return undefined;
120
+ if (STANDARD_COLORS.some((color) => color.value === value)) return value as ColorName;
121
+ if (PI_FOREGROUND_COLORS.some((color) => color.value === value)) return value as ColorName;
122
+ const match = /^ansi256:([0-9]{1,3})$/.exec(value);
123
+ if (!match) return undefined;
124
+ const code = Number(match[1]);
125
+ return Number.isInteger(code) && code >= 0 && code <= 255
126
+ ? (`ansi256:${code}` as ColorName)
127
+ : undefined;
128
+ }
129
+
130
+ export function colorDisplayName(color: ColorName | undefined): string {
131
+ if (!color || color === "default") return "Default";
132
+ if (color.startsWith("ansi256:")) return `ANSI256 ${color.slice("ansi256:".length)}`;
133
+ if (color.startsWith("pi:"))
134
+ return PI_FOREGROUND_COLORS.find((entry) => entry.value === color)?.label ?? color;
135
+ return STANDARD_COLORS.find((entry) => entry.value === color)?.label ?? color;
136
+ }
137
+
138
+ export function ansi256Digits(color: ColorName | undefined): string {
139
+ return color?.startsWith("ansi256:") ? String(Number(color.slice("ansi256:".length))) : "0";
140
+ }
141
+
142
+ export function appendAnsi256Digit(color: ColorName | undefined, digit: string): ColorName {
143
+ const digits = ansi256Digits(color);
144
+ const next = Number(`${digits}${digit}`);
145
+ return `ansi256:${Math.min(255, next)}`;
146
+ }
147
+
148
+ export function deleteAnsi256Digit(color: ColorName | undefined): ColorName {
149
+ const digits = ansi256Digits(color) || "0";
150
+ const next = digits.slice(0, -1);
151
+ return `ansi256:${next === "" ? 0 : Number(next)}`;
152
+ }
153
+
154
+ function useColorLevel(level: ColorLevel) {
155
+ const next = level === "truecolor" ? 3 : level === "ansi256" ? 2 : 1;
156
+ if (chalk.level === next) return;
157
+ chalk.level = next;
158
+ chalk.c = new Chalk({ level: next });
159
+ }
160
+
161
+ export function applyColors(
162
+ text: string,
163
+ foreground: ColorName | undefined,
164
+ background: ColorName | undefined,
165
+ bold: boolean | undefined,
166
+ level: ColorLevel,
167
+ theme?: Theme,
168
+ ): string {
169
+ if (level === "none") return text;
170
+ useColorLevel(level);
171
+
172
+ let output = text;
173
+ if (foreground && foreground !== "default")
174
+ output = applyOne(output, foreground, false, level, theme);
175
+ if (background && background !== "default") output = applyOne(output, background, true, level);
176
+ if (bold) output = chalk.c.bold(output);
177
+ return output;
178
+ }
179
+
180
+ // The styleable color bag shared by widget options and the context conditional-color helper.
181
+ // fg/bg are validated ColorName (color picker); the warning/danger quartet is raw text input
182
+ // (kind: "text" properties) normalized via normalizeColor() at read time.
183
+ export interface ConditionalColorFields {
184
+ fg?: ColorName;
185
+ bg?: ColorName;
186
+ warningFg?: string;
187
+ warningBg?: string;
188
+ dangerFg?: string;
189
+ dangerBg?: string;
190
+ }
191
+
192
+ export function resetAnsi256Colors<T extends ConditionalColorFields>(options: T): T {
193
+ const next = { ...options };
194
+ if (next.fg?.startsWith("ansi256:")) next.fg = "default";
195
+ if (next.bg?.startsWith("ansi256:")) next.bg = "default";
196
+ if (next.warningFg?.startsWith("ansi256:")) next.warningFg = "default";
197
+ if (next.warningBg?.startsWith("ansi256:")) next.warningBg = "default";
198
+ if (next.dangerFg?.startsWith("ansi256:")) next.dangerFg = "default";
199
+ if (next.dangerBg?.startsWith("ansi256:")) next.dangerBg = "default";
200
+ return next;
201
+ }
202
+
203
+ function themeColorDisplayName(color: ThemeColor): string {
204
+ return color.replace(/([A-Z])/g, " $1").replace(/^./, (char) => char.toUpperCase());
205
+ }
206
+
207
+ function applyOne(
208
+ text: string,
209
+ color: ColorName,
210
+ background: boolean,
211
+ level: ColorLevel,
212
+ theme?: Theme,
213
+ ): string {
214
+ if (color.startsWith("pi:")) {
215
+ if (background || !theme) return text;
216
+ return theme.fg(color.slice("pi:".length) as ThemeColor, text);
217
+ }
218
+ if (color.startsWith("ansi256:")) {
219
+ const code = Number(color.slice("ansi256:".length));
220
+ if (level === "ansi256" || level === "truecolor") {
221
+ return background ? chalk.c.bgAnsi256(code)(text) : chalk.c.ansi256(code)(text);
222
+ }
223
+ return text;
224
+ }
225
+
226
+ const codes = ANSI16_FG[color as Exclude<ColorName, `ansi256:${number}` | `pi:${ThemeColor}`>];
227
+ if (!codes || color === "default") return text;
228
+ return `\x1b[${background ? codes[1] : codes[0]}m${text}\x1b[${background ? 49 : 39}m`;
229
+ }
230
+
231
+ // Keep the escape byte out of a regex literal so oxlint's no-control-regex rule does not
232
+ // flag the intentional ANSI matcher. RegExp still receives the ESC sequence at runtime.
233
+ const ANSI_ESCAPE_PATTERN = new RegExp(String.raw`\x1B\[[0-?]*[ -/]*[@-~]`, "g");
234
+
235
+ export function stripAnsi(text: string): string {
236
+ return text.replace(ANSI_ESCAPE_PATTERN, "");
237
+ }
@@ -0,0 +1,202 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+
4
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
5
+
6
+ import type { ColorLevel } from "./colors.js";
7
+ import { COLOR_LEVEL_VALUES, normalizeColor } from "./colors.js";
8
+ import {
9
+ cloneExtensionStatusRow,
10
+ DEFAULT_EXTENSION_STATUS_ROW,
11
+ normalizeExtensionStatusRow,
12
+ } from "./extension-statuses.js";
13
+ import {
14
+ PRESET_DEFINITIONS,
15
+ type Preset,
16
+ type PresetDefinition,
17
+ type PresetWidget,
18
+ } from "./presets.js";
19
+ import { SEPARATOR_VALUES, type SeparatorStyle } from "./separators.js";
20
+ import type {
21
+ IconMode,
22
+ StatuslineConfig,
23
+ StatuslineSettings,
24
+ TerminalOptions,
25
+ TerminalWidthMode,
26
+ WidgetEntry,
27
+ } from "./types.js";
28
+ import { ICON_MODE_VALUES, isRecord, TERMINAL_WIDTH_MODE_VALUES } from "./types.js";
29
+ import { registry, type WidgetType } from "./widgets/registry.js";
30
+
31
+ export const STATUS_KEY = "pi-footer";
32
+
33
+ const CONFIG_ENV = "PI_FOOTER_CONFIG";
34
+ const DEFAULT_CONFIG_PATH = join(getAgentDir(), "extensions", "pi-footer.json");
35
+ const SEPARATORS = new Set<SeparatorStyle>(SEPARATOR_VALUES);
36
+
37
+ const DEFAULT_TERMINAL_OPTIONS: TerminalOptions = {
38
+ widthMode: "full",
39
+ colorLevel: "ansi256",
40
+ };
41
+
42
+ export const DEFAULT_CONFIG: StatuslineConfig = {
43
+ version: 1,
44
+ enabled: true,
45
+ preset: "default",
46
+ lines: linesForPreset("default"),
47
+ separator: "dot",
48
+ separatorFg: "default",
49
+ separatorBg: "default",
50
+ iconMode: "emoji",
51
+ minimalist: false,
52
+ terminal: DEFAULT_TERMINAL_OPTIONS,
53
+ extensionStatusRow: DEFAULT_EXTENSION_STATUS_ROW,
54
+ };
55
+
56
+ export function getConfigPath(): string {
57
+ return process.env[CONFIG_ENV] ?? DEFAULT_CONFIG_PATH;
58
+ }
59
+
60
+ function linesForPreset(preset: Preset): WidgetEntry[][] {
61
+ return PRESET_DEFINITIONS[preset].lines.map((line) => widgetsFromPresetLine(line));
62
+ }
63
+
64
+ export function configWithPreset(config: StatuslineConfig, preset: Preset): StatuslineConfig {
65
+ const definition: PresetDefinition = PRESET_DEFINITIONS[preset];
66
+ return {
67
+ ...config,
68
+ preset,
69
+ lines: linesForPreset(preset),
70
+ separator: definition.separator ?? config.separator,
71
+ iconMode: definition.iconMode ?? config.iconMode,
72
+ terminal: { ...config.terminal, ...definition.terminal },
73
+ };
74
+ }
75
+
76
+ function widgetsFromPresetLine(line: readonly PresetWidget[]): WidgetEntry[] {
77
+ return line.map((widget) => registry.createEntry(widget.type, widget.options));
78
+ }
79
+
80
+ export function normalizeConfig(input: unknown): StatuslineConfig {
81
+ if (!isRecord(input)) return cloneConfig(DEFAULT_CONFIG);
82
+
83
+ const preset = isPreset(input.preset) ? input.preset : DEFAULT_CONFIG.preset;
84
+ const lines = normalizeLines(input.lines, preset);
85
+
86
+ return {
87
+ version: 1,
88
+ enabled: typeof input.enabled === "boolean" ? input.enabled : DEFAULT_CONFIG.enabled,
89
+ preset,
90
+ lines,
91
+ separator: isSeparatorStyle(input.separator)
92
+ ? input.separator
93
+ : (PRESET_DEFINITIONS[preset].separator ?? DEFAULT_CONFIG.separator),
94
+ separatorFg: normalizeColor(input.separatorFg) ?? DEFAULT_CONFIG.separatorFg,
95
+ separatorBg: normalizeColor(input.separatorBg) ?? DEFAULT_CONFIG.separatorBg,
96
+ iconMode: isIconMode(input.iconMode) ? input.iconMode : DEFAULT_CONFIG.iconMode,
97
+ minimalist:
98
+ typeof input.minimalist === "boolean" ? input.minimalist : DEFAULT_CONFIG.minimalist,
99
+ terminal: normalizeTerminalOptions(input.terminal, PRESET_DEFINITIONS[preset].terminal),
100
+ extensionStatusRow: normalizeExtensionStatusRow(input.extensionStatusRow),
101
+ };
102
+ }
103
+
104
+ export function cloneSettings(settings: StatuslineSettings): StatuslineSettings {
105
+ return {
106
+ ...settings,
107
+ terminal: { ...settings.terminal },
108
+ extensionStatusRow: cloneExtensionStatusRow(settings.extensionStatusRow),
109
+ };
110
+ }
111
+
112
+ export function cloneConfig(config: StatuslineConfig): StatuslineConfig {
113
+ return {
114
+ ...cloneSettings(config),
115
+ lines: config.lines.map((line) =>
116
+ line.map((widget) => ({ ...widget, options: { ...widget.options } })),
117
+ ),
118
+ };
119
+ }
120
+
121
+ export async function loadConfig(path = getConfigPath()): Promise<StatuslineConfig> {
122
+ try {
123
+ const raw = await readFile(path, "utf8");
124
+ return normalizeConfig(JSON.parse(raw));
125
+ } catch (error) {
126
+ if (isNodeError(error) && error.code === "ENOENT") {
127
+ return cloneConfig(DEFAULT_CONFIG);
128
+ }
129
+ throw error;
130
+ }
131
+ }
132
+
133
+ export async function saveConfig(config: StatuslineConfig, path = getConfigPath()): Promise<void> {
134
+ await mkdir(dirname(path), { recursive: true });
135
+ await writeFile(path, `${JSON.stringify(normalizeConfig(config), null, 2)}\n`, "utf8");
136
+ }
137
+
138
+ function normalizeLines(linesValue: unknown, preset: Preset): WidgetEntry[][] {
139
+ if (!Array.isArray(linesValue)) return linesForPreset(preset);
140
+ return linesValue.map((line) => normalizeWidgets(line));
141
+ }
142
+
143
+ function normalizeWidgets(value: unknown): WidgetEntry[] {
144
+ if (!Array.isArray(value)) return [];
145
+
146
+ const widgets: WidgetEntry[] = [];
147
+ for (const item of value) {
148
+ if (!isRecord(item) || typeof item.type !== "string") continue;
149
+ const spec = registry.maybeSpec(item.type);
150
+ if (!spec) continue;
151
+ widgets.push({
152
+ id:
153
+ typeof item.id === "string" && item.id.length > 0
154
+ ? item.id
155
+ : registry.createEntry(spec.type as WidgetType).id,
156
+ type: spec.type as WidgetType,
157
+ enabled: typeof item.enabled === "boolean" ? item.enabled : true,
158
+ options: registry.normalizeOptions(
159
+ spec.type as WidgetType,
160
+ isRecord(item.options) ? item.options : {},
161
+ ),
162
+ });
163
+ }
164
+
165
+ return widgets;
166
+ }
167
+
168
+ function normalizeTerminalOptions(
169
+ value: unknown,
170
+ defaults: Partial<TerminalOptions> = {},
171
+ ): TerminalOptions {
172
+ const base = { ...DEFAULT_TERMINAL_OPTIONS, ...defaults };
173
+ if (!isRecord(value)) return base;
174
+ return {
175
+ widthMode:
176
+ typeof value.widthMode === "string" &&
177
+ TERMINAL_WIDTH_MODE_VALUES.includes(value.widthMode as TerminalWidthMode)
178
+ ? (value.widthMode as TerminalWidthMode)
179
+ : base.widthMode,
180
+ colorLevel:
181
+ typeof value.colorLevel === "string" &&
182
+ COLOR_LEVEL_VALUES.includes(value.colorLevel as ColorLevel)
183
+ ? (value.colorLevel as ColorLevel)
184
+ : base.colorLevel,
185
+ };
186
+ }
187
+
188
+ export function isPreset(value: unknown): value is Preset {
189
+ return typeof value === "string" && Object.hasOwn(PRESET_DEFINITIONS, value);
190
+ }
191
+
192
+ function isSeparatorStyle(value: unknown): value is SeparatorStyle {
193
+ return typeof value === "string" && SEPARATORS.has(value as SeparatorStyle);
194
+ }
195
+
196
+ function isIconMode(value: unknown): value is IconMode {
197
+ return typeof value === "string" && ICON_MODE_VALUES.includes(value as IconMode);
198
+ }
199
+
200
+ function isNodeError(error: unknown): error is NodeJS.ErrnoException {
201
+ return error instanceof Error && "code" in error;
202
+ }
@@ -0,0 +1,41 @@
1
+ import { isRecord } from "./types.js";
2
+
3
+ export const UPDATE_EVENT_WIDGET_EVENT = "pi-footer:update-widget";
4
+
5
+ interface UpdateEventWidgetPayload {
6
+ widgetId: string;
7
+ value: string | null;
8
+ }
9
+
10
+ const EVENT_WIDGET_ID_PREFIX = "event_";
11
+
12
+ export function createEventWidgetId(): string {
13
+ return `${EVENT_WIDGET_ID_PREFIX}${Math.random().toString(36).slice(2, 10)}`;
14
+ }
15
+
16
+ export class EventWidgetValues {
17
+ private readonly valuesById = new Map<string, string>();
18
+
19
+ get values(): ReadonlyMap<string, string> {
20
+ return this.valuesById;
21
+ }
22
+
23
+ update(payload: unknown): boolean {
24
+ if (!isUpdatePayload(payload)) return false;
25
+ if (payload.value === null) {
26
+ return this.valuesById.delete(payload.widgetId);
27
+ }
28
+ const previous = this.valuesById.get(payload.widgetId);
29
+ this.valuesById.set(payload.widgetId, payload.value);
30
+ return previous !== payload.value;
31
+ }
32
+ }
33
+
34
+ function isUpdatePayload(value: unknown): value is UpdateEventWidgetPayload {
35
+ if (!isRecord(value)) return false;
36
+ return (
37
+ typeof value.widgetId === "string" &&
38
+ value.widgetId.length > 0 &&
39
+ (typeof value.value === "string" || value.value === null)
40
+ );
41
+ }
@@ -0,0 +1,102 @@
1
+ import { isRecord } from "./types.js";
2
+
3
+ export const EMPTY_STATUS_LABEL = "[Empty status]";
4
+
5
+ export interface ExtensionStatusRowConfig {
6
+ hiddenKeys: string[];
7
+ knownKeys: string[];
8
+ }
9
+
10
+ interface ExtensionStatusEntry {
11
+ key: string;
12
+ value: string;
13
+ published: boolean;
14
+ }
15
+
16
+ export type GetExtensionStatuses = () => ReadonlyMap<string, string>;
17
+
18
+ export const DEFAULT_EXTENSION_STATUS_ROW: ExtensionStatusRowConfig = {
19
+ hiddenKeys: [],
20
+ knownKeys: [],
21
+ };
22
+
23
+ export const EMPTY_EXTENSION_STATUSES: ReadonlyMap<string, string> = new Map<string, string>();
24
+
25
+ export function extensionStatusEntries(
26
+ statuses: ReadonlyMap<string, string>,
27
+ ownStatusKey: string,
28
+ ): ExtensionStatusEntry[] {
29
+ return [...statuses.entries()]
30
+ .filter(([key, value]) => key !== ownStatusKey && value.length > 0)
31
+ .sort(([left], [right]) => left.localeCompare(right))
32
+ .map(([key, value]) => ({ key, value, published: true }));
33
+ }
34
+
35
+ export function allExtensionStatusEntries(
36
+ statuses: ReadonlyMap<string, string>,
37
+ rowConfig: ExtensionStatusRowConfig,
38
+ ownStatusKey: string,
39
+ ): ExtensionStatusEntry[] {
40
+ const keys = new Set([...statuses.keys(), ...rowConfig.hiddenKeys, ...rowConfig.knownKeys]);
41
+ return [...keys]
42
+ .filter((key) => key.length > 0 && key !== ownStatusKey)
43
+ .sort((left, right) => left.localeCompare(right))
44
+ .map((key) => {
45
+ const value = statuses.get(key) ?? "";
46
+ return {
47
+ key,
48
+ value: value.length > 0 ? value : EMPTY_STATUS_LABEL,
49
+ published: value.length > 0,
50
+ };
51
+ });
52
+ }
53
+
54
+ export function visibleExtensionStatusRowEntries(
55
+ statuses: ReadonlyMap<string, string>,
56
+ hiddenKeys: readonly string[],
57
+ ownStatusKey: string,
58
+ ): ExtensionStatusEntry[] {
59
+ const hidden = new Set(hiddenKeys);
60
+ return extensionStatusEntries(statuses, ownStatusKey).filter((entry) => !hidden.has(entry.key));
61
+ }
62
+
63
+ export function toggleExtensionStatusRowKey(
64
+ rowConfig: ExtensionStatusRowConfig,
65
+ key: string,
66
+ ): ExtensionStatusRowConfig {
67
+ const hidden = new Set(rowConfig.hiddenKeys);
68
+ const known = new Set(rowConfig.knownKeys);
69
+ known.add(key);
70
+ if (hidden.has(key)) hidden.delete(key);
71
+ else hidden.add(key);
72
+ return {
73
+ hiddenKeys: sortedKeys(hidden),
74
+ knownKeys: sortedKeys(known),
75
+ };
76
+ }
77
+
78
+ export function normalizeExtensionStatusRow(value: unknown): ExtensionStatusRowConfig {
79
+ if (!isRecord(value)) return cloneExtensionStatusRow(DEFAULT_EXTENSION_STATUS_ROW);
80
+ return {
81
+ hiddenKeys: normalizeKeyList(value.hiddenKeys),
82
+ knownKeys: normalizeKeyList(value.knownKeys),
83
+ };
84
+ }
85
+
86
+ export function cloneExtensionStatusRow(value: ExtensionStatusRowConfig): ExtensionStatusRowConfig {
87
+ return {
88
+ hiddenKeys: [...value.hiddenKeys],
89
+ knownKeys: [...value.knownKeys],
90
+ };
91
+ }
92
+
93
+ function normalizeKeyList(value: unknown): string[] {
94
+ if (!Array.isArray(value)) return [];
95
+ return sortedKeys(
96
+ new Set(value.filter((key): key is string => typeof key === "string" && key.length > 0)),
97
+ );
98
+ }
99
+
100
+ function sortedKeys(keys: Iterable<string>): string[] {
101
+ return [...keys].sort((left, right) => left.localeCompare(right));
102
+ }