@selesai/code 0.9.14 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +6 -9
  3. package/dist/defaults/models.json +79 -29
  4. package/dist/defaults/settings.json +3 -7
  5. package/dist/extensions/package.json +3 -4
  6. package/dist/extensions/pi-zentui/.editorconfig +12 -0
  7. package/dist/extensions/pi-zentui/.github/CODEOWNERS +1 -0
  8. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/bug_report.yml +110 -0
  9. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/config.yml +8 -0
  10. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/config_question.yml +56 -0
  11. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/feature_request.yml +63 -0
  12. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/rendering_issue.yml +106 -0
  13. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/runtime_detection.yml +103 -0
  14. package/dist/extensions/pi-zentui/.github/dependabot.yml +47 -0
  15. package/dist/extensions/pi-zentui/.github/pull_request_template.md +34 -0
  16. package/dist/extensions/pi-zentui/.github/release.yml +18 -0
  17. package/dist/extensions/pi-zentui/.github/workflows/ci.yml +79 -0
  18. package/dist/extensions/pi-zentui/.github/workflows/publish.yml +63 -0
  19. package/dist/extensions/pi-zentui/AGENTS.md +107 -0
  20. package/dist/extensions/pi-zentui/CONTRIBUTING.md +68 -0
  21. package/dist/extensions/pi-zentui/LICENSE +21 -0
  22. package/dist/extensions/pi-zentui/README.md +188 -0
  23. package/dist/extensions/pi-zentui/SECURITY.md +18 -0
  24. package/dist/extensions/pi-zentui/assets/main-cover.png +0 -0
  25. package/dist/extensions/pi-zentui/assets/screenshots/editor-accent-rail.png +0 -0
  26. package/dist/extensions/pi-zentui/assets/screenshots/editor-minimalist.png +0 -0
  27. package/dist/extensions/pi-zentui/assets/screenshots/editor-opencode-copy-friendly.png +0 -0
  28. package/dist/extensions/pi-zentui/assets/screenshots/editor-opencode.png +0 -0
  29. package/dist/extensions/pi-zentui/assets/screenshots/user-message-compact.png +0 -0
  30. package/dist/extensions/pi-zentui/assets/screenshots/user-message-framed-copy-friendly.png +0 -0
  31. package/dist/extensions/pi-zentui/assets/screenshots/user-message-framed.png +0 -0
  32. package/dist/extensions/pi-zentui/assets/screenshots/user-message-labeled.png +0 -0
  33. package/dist/extensions/pi-zentui/assets/zentui.png +0 -0
  34. package/dist/extensions/pi-zentui/biome.json +31 -0
  35. package/dist/extensions/pi-zentui/docs/configuration.md +509 -0
  36. package/dist/extensions/pi-zentui/docs/footer-format.md +162 -0
  37. package/dist/extensions/pi-zentui/extensions/zentui/accent-rail-editor.ts +99 -0
  38. package/dist/extensions/pi-zentui/extensions/zentui/accent-rail-layout-patch.ts +457 -0
  39. package/dist/extensions/pi-zentui/extensions/zentui/completion-menu.ts +157 -0
  40. package/dist/extensions/pi-zentui/extensions/zentui/config.ts +2100 -0
  41. package/dist/extensions/pi-zentui/extensions/zentui/editor-metadata-format.ts +321 -0
  42. package/dist/extensions/pi-zentui/extensions/zentui/editor-transfer.ts +73 -0
  43. package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +89 -0
  44. package/dist/extensions/pi-zentui/extensions/zentui/footer-format.ts +364 -0
  45. package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +122 -0
  46. package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +812 -0
  47. package/dist/extensions/pi-zentui/extensions/zentui/format.ts +568 -0
  48. package/dist/extensions/pi-zentui/extensions/zentui/git.ts +384 -0
  49. package/dist/extensions/pi-zentui/extensions/zentui/icons.ts +252 -0
  50. package/dist/extensions/pi-zentui/extensions/zentui/index.ts +1406 -0
  51. package/dist/extensions/pi-zentui/extensions/zentui/interaction-summary.ts +1049 -0
  52. package/dist/extensions/pi-zentui/extensions/zentui/live-context.ts +75 -0
  53. package/dist/extensions/pi-zentui/extensions/zentui/minimalist-editor.ts +463 -0
  54. package/dist/extensions/pi-zentui/extensions/zentui/package-version.ts +651 -0
  55. package/dist/extensions/pi-zentui/extensions/zentui/project-refresh.ts +120 -0
  56. package/dist/extensions/pi-zentui/extensions/zentui/project-state.ts +59 -0
  57. package/dist/extensions/pi-zentui/extensions/zentui/prototype-patch-registry.ts +166 -0
  58. package/dist/extensions/pi-zentui/extensions/zentui/repository-root.ts +98 -0
  59. package/dist/extensions/pi-zentui/extensions/zentui/runtime.ts +841 -0
  60. package/dist/extensions/pi-zentui/extensions/zentui/selector-border.ts +93 -0
  61. package/dist/extensions/pi-zentui/extensions/zentui/session-lifecycle.ts +60 -0
  62. package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +1796 -0
  63. package/dist/extensions/pi-zentui/extensions/zentui/settings-previews.ts +128 -0
  64. package/dist/extensions/pi-zentui/extensions/zentui/state.ts +94 -0
  65. package/dist/extensions/pi-zentui/extensions/zentui/style.ts +424 -0
  66. package/dist/extensions/pi-zentui/extensions/zentui/telemetry.ts +82 -0
  67. package/dist/extensions/pi-zentui/extensions/zentui/ui.ts +1290 -0
  68. package/dist/extensions/pi-zentui/extensions/zentui/user-message-osc.ts +248 -0
  69. package/dist/extensions/pi-zentui/extensions/zentui/user-message-styles.ts +215 -0
  70. package/dist/extensions/pi-zentui/extensions/zentui/user-message.ts +206 -0
  71. package/dist/extensions/pi-zentui/extensions/zentui/working-line-messages.ts +24 -0
  72. package/dist/extensions/pi-zentui/extensions/zentui/working-line-spinners.ts +54 -0
  73. package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +1433 -0
  74. package/dist/extensions/pi-zentui/package-lock.json +5311 -0
  75. package/dist/extensions/pi-zentui/package.json +60 -0
  76. package/dist/extensions/pi-zentui/test/accent-rail-editor.test.ts +232 -0
  77. package/dist/extensions/pi-zentui/test/accent-rail-layout-patch.test.ts +669 -0
  78. package/dist/extensions/pi-zentui/test/completion-menu.test.ts +265 -0
  79. package/dist/extensions/pi-zentui/test/config.test.ts +3119 -0
  80. package/dist/extensions/pi-zentui/test/editor-metadata-format.test.ts +405 -0
  81. package/dist/extensions/pi-zentui/test/editor-transfer.test.ts +260 -0
  82. package/dist/extensions/pi-zentui/test/editor-viewport-indicators.test.ts +1286 -0
  83. package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +7196 -0
  84. package/dist/extensions/pi-zentui/test/extension-status.test.ts +182 -0
  85. package/dist/extensions/pi-zentui/test/fixtures/starship-nerd-font-symbols.toml +172 -0
  86. package/dist/extensions/pi-zentui/test/footer-format.test.ts +431 -0
  87. package/dist/extensions/pi-zentui/test/footer-layout.test.ts +113 -0
  88. package/dist/extensions/pi-zentui/test/footer-repository-path.test.ts +123 -0
  89. package/dist/extensions/pi-zentui/test/format.test.ts +865 -0
  90. package/dist/extensions/pi-zentui/test/git.test.ts +212 -0
  91. package/dist/extensions/pi-zentui/test/icons.test.ts +69 -0
  92. package/dist/extensions/pi-zentui/test/interaction-summary.test.ts +1402 -0
  93. package/dist/extensions/pi-zentui/test/live-context-integration.test.ts +378 -0
  94. package/dist/extensions/pi-zentui/test/live-context.test.ts +139 -0
  95. package/dist/extensions/pi-zentui/test/minimalist-editor.test.ts +811 -0
  96. package/dist/extensions/pi-zentui/test/package-version.test.ts +321 -0
  97. package/dist/extensions/pi-zentui/test/project-refresh.test.ts +250 -0
  98. package/dist/extensions/pi-zentui/test/project-state.test.ts +159 -0
  99. package/dist/extensions/pi-zentui/test/repository-root.test.ts +108 -0
  100. package/dist/extensions/pi-zentui/test/responsive-dependencies.test.ts +825 -0
  101. package/dist/extensions/pi-zentui/test/runtime.test.ts +183 -0
  102. package/dist/extensions/pi-zentui/test/session-lifecycle.test.ts +40 -0
  103. package/dist/extensions/pi-zentui/test/settings-command.test.ts +1381 -0
  104. package/dist/extensions/pi-zentui/test/settings-previews.test.ts +347 -0
  105. package/dist/extensions/pi-zentui/test/state.test.ts +84 -0
  106. package/dist/extensions/pi-zentui/test/telemetry-lifecycle-integration.test.ts +276 -0
  107. package/dist/extensions/pi-zentui/test/telemetry.test.ts +105 -0
  108. package/dist/extensions/pi-zentui/test/turn-summary-lifecycle.integration.test.ts +327 -0
  109. package/dist/extensions/pi-zentui/test/user-message-osc.test.ts +164 -0
  110. package/dist/extensions/pi-zentui/test/user-message-styles.test.ts +292 -0
  111. package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +730 -0
  112. package/dist/extensions/pi-zentui/test/working-line-messages.test.ts +34 -0
  113. package/dist/extensions/pi-zentui/test/working-line.test.ts +1990 -0
  114. package/dist/extensions/pi-zentui/tsconfig.json +16 -0
  115. package/dist/extensions/tps.test.ts +38 -0
  116. package/dist/extensions/{pi-powerline-footer/tps.ts → tps.ts} +4 -0
  117. package/dist/skills/design-references/SKILL.md +31 -0
  118. package/dist/skills/design-references/references/clickhouse/DESIGN.md +544 -0
  119. package/dist/themes/adventure.json +84 -0
  120. package/dist/themes/adwaita-dark.json +84 -0
  121. package/dist/themes/arcoiris.json +84 -0
  122. package/dist/themes/arthur.json +84 -0
  123. package/dist/themes/atom.json +84 -0
  124. package/dist/themes/aura.json +84 -0
  125. package/dist/themes/black-metal-bathory.json +84 -0
  126. package/dist/themes/black-metal-burzum.json +84 -0
  127. package/dist/themes/black-metal-khold.json +84 -0
  128. package/dist/themes/box.json +84 -0
  129. package/dist/themes/brogrammer.json +84 -0
  130. package/dist/themes/carbonfox.json +84 -0
  131. package/dist/themes/catppuccin-mocha.json +84 -0
  132. package/dist/themes/citruszest.json +84 -0
  133. package/dist/themes/cursor-dark.json +84 -0
  134. package/dist/themes/cutie-pro.json +84 -0
  135. package/dist/themes/dark-modern.json +84 -0
  136. package/dist/themes/dark-pastel.json +84 -0
  137. package/dist/themes/dimmed-monokai.json +84 -0
  138. package/dist/themes/doom-peacock.json +84 -0
  139. package/dist/themes/dracula-plus.json +84 -0
  140. package/dist/themes/dracula.json +80 -0
  141. package/dist/themes/earthsong.json +84 -0
  142. package/dist/themes/everforest-dark-hard.json +84 -0
  143. package/dist/themes/everforest-dark.json +80 -0
  144. package/dist/themes/fahrenheit.json +84 -0
  145. package/dist/themes/flatland.json +84 -0
  146. package/dist/themes/flexoki-dark.json +84 -0
  147. package/dist/themes/front-end-delight.json +84 -0
  148. package/dist/themes/fun-forrest.json +84 -0
  149. package/dist/themes/galizur.json +84 -0
  150. package/dist/themes/github-dark-colorblind.json +84 -0
  151. package/dist/themes/github-dark-high-contrast.json +84 -0
  152. package/dist/themes/glacier.json +84 -0
  153. package/dist/themes/gruber-darker.json +84 -0
  154. package/dist/themes/gruvbox-dark-hard.json +84 -0
  155. package/dist/themes/gruvbox-dark.json +84 -0
  156. package/dist/themes/gruvbox-material.json +84 -0
  157. package/dist/themes/guezwhoz.json +84 -0
  158. package/dist/themes/hacktober.json +84 -0
  159. package/dist/themes/hardcore.json +84 -0
  160. package/dist/themes/havn-skumring.json +84 -0
  161. package/dist/themes/ic-orange-ppl.json +84 -0
  162. package/dist/themes/iterm2-smoooooth.json +84 -0
  163. package/dist/themes/iterm2-tango-dark.json +84 -0
  164. package/dist/themes/japanesque.json +84 -0
  165. package/dist/themes/jellybeans.json +84 -0
  166. package/dist/themes/kanagawa-wave.json +84 -0
  167. package/dist/themes/kurokula.json +84 -0
  168. package/dist/themes/later-this-evening.json +84 -0
  169. package/dist/themes/lovelace.json +84 -0
  170. package/dist/themes/material-darker.json +84 -0
  171. package/dist/themes/matte-black.json +84 -0
  172. package/dist/themes/mellow.json +84 -0
  173. package/dist/themes/miasma.json +84 -0
  174. package/dist/themes/nord.json +80 -0
  175. package/dist/themes/nvim-dark.json +84 -0
  176. package/dist/themes/one-dark.json +80 -0
  177. package/dist/themes/popping-and-locking.json +84 -0
  178. package/dist/themes/rose-pine.json +80 -0
  179. package/dist/themes/sea-shells.json +84 -0
  180. package/dist/themes/sleepy-hollow.json +84 -0
  181. package/dist/themes/smyck.json +84 -0
  182. package/dist/themes/solarized-dark.json +80 -0
  183. package/dist/themes/tokyo-night.json +80 -0
  184. package/dist/themes/tomorrow-night-bright.json +84 -0
  185. package/dist/themes/tomorrow-night-burns.json +84 -0
  186. package/dist/themes/tomorrow-night.json +84 -0
  187. package/dist/themes/twilight.json +84 -0
  188. package/dist/themes/vague.json +84 -0
  189. package/dist/themes/vesper.json +84 -0
  190. package/dist/themes/xcode-dark-hc.json +84 -0
  191. package/docs/custom-provider.md +0 -1
  192. package/docs/plans/auto-model-routing-extension.md +490 -0
  193. package/package.json +3 -3
  194. package/dist/extensions/pi-powerline-footer/.github/workflows/test.yml +0 -27
  195. package/dist/extensions/pi-powerline-footer/CHANGELOG.md +0 -687
  196. package/dist/extensions/pi-powerline-footer/CONTRIBUTING.md +0 -16
  197. package/dist/extensions/pi-powerline-footer/README.md +0 -513
  198. package/dist/extensions/pi-powerline-footer/banner.png +0 -0
  199. package/dist/extensions/pi-powerline-footer/bash-mode/completion.ts +0 -586
  200. package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +0 -583
  201. package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +0 -204
  202. package/dist/extensions/pi-powerline-footer/bash-mode/shell-session.ts +0 -286
  203. package/dist/extensions/pi-powerline-footer/bash-mode/transcript.ts +0 -108
  204. package/dist/extensions/pi-powerline-footer/bash-mode/types.ts +0 -60
  205. package/dist/extensions/pi-powerline-footer/cd-command.ts +0 -190
  206. package/dist/extensions/pi-powerline-footer/colors.ts +0 -70
  207. package/dist/extensions/pi-powerline-footer/context-usage.ts +0 -156
  208. package/dist/extensions/pi-powerline-footer/currency-rates.ts +0 -142
  209. package/dist/extensions/pi-powerline-footer/editor-composition.ts +0 -32
  210. package/dist/extensions/pi-powerline-footer/editor-performance.ts +0 -99
  211. package/dist/extensions/pi-powerline-footer/git-status.ts +0 -329
  212. package/dist/extensions/pi-powerline-footer/guide.ts +0 -220
  213. package/dist/extensions/pi-powerline-footer/icons.ts +0 -190
  214. package/dist/extensions/pi-powerline-footer/index.ts +0 -3428
  215. package/dist/extensions/pi-powerline-footer/lifecycle.ts +0 -10
  216. package/dist/extensions/pi-powerline-footer/package.json +0 -49
  217. package/dist/extensions/pi-powerline-footer/paths.ts +0 -22
  218. package/dist/extensions/pi-powerline-footer/powerline-config.ts +0 -446
  219. package/dist/extensions/pi-powerline-footer/presets.ts +0 -102
  220. package/dist/extensions/pi-powerline-footer/queue/store.ts +0 -313
  221. package/dist/extensions/pi-powerline-footer/queue/types.ts +0 -53
  222. package/dist/extensions/pi-powerline-footer/render-scheduler.ts +0 -29
  223. package/dist/extensions/pi-powerline-footer/segments.ts +0 -570
  224. package/dist/extensions/pi-powerline-footer/separators.ts +0 -57
  225. package/dist/extensions/pi-powerline-footer/session-usage.ts +0 -44
  226. package/dist/extensions/pi-powerline-footer/shortcuts.ts +0 -61
  227. package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +0 -1905
  228. package/dist/extensions/pi-powerline-footer/tests/cd-command.test.ts +0 -144
  229. package/dist/extensions/pi-powerline-footer/tests/context-usage.test.ts +0 -156
  230. package/dist/extensions/pi-powerline-footer/tests/custom-items.test.ts +0 -344
  231. package/dist/extensions/pi-powerline-footer/tests/editor-composition.test.ts +0 -48
  232. package/dist/extensions/pi-powerline-footer/tests/editor-performance.test.ts +0 -43
  233. package/dist/extensions/pi-powerline-footer/tests/editor-responsiveness.test.ts +0 -136
  234. package/dist/extensions/pi-powerline-footer/tests/fixed-editor-border.test.ts +0 -39
  235. package/dist/extensions/pi-powerline-footer/tests/git-optional-locks.test.ts +0 -64
  236. package/dist/extensions/pi-powerline-footer/tests/git-status.test.ts +0 -92
  237. package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +0 -129
  238. package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +0 -116
  239. package/dist/extensions/pi-powerline-footer/tests/mode-aware-trigger.test.ts +0 -79
  240. package/dist/extensions/pi-powerline-footer/tests/paths.test.ts +0 -63
  241. package/dist/extensions/pi-powerline-footer/tests/queue-store.test.ts +0 -65
  242. package/dist/extensions/pi-powerline-footer/tests/quit-cursor.test.ts +0 -14
  243. package/dist/extensions/pi-powerline-footer/tests/remaining-regressions.test.ts +0 -173
  244. package/dist/extensions/pi-powerline-footer/tests/session-usage.test.ts +0 -47
  245. package/dist/extensions/pi-powerline-footer/tests/stash-shortcut.test.ts +0 -37
  246. package/dist/extensions/pi-powerline-footer/tests/thinking-segment.test.ts +0 -76
  247. package/dist/extensions/pi-powerline-footer/tests/token-stats.test.ts +0 -249
  248. package/dist/extensions/pi-powerline-footer/tests/tps.test.ts +0 -39
  249. package/dist/extensions/pi-powerline-footer/tests/usage-display.test.ts +0 -237
  250. package/dist/extensions/pi-powerline-footer/tests/welcome.test.ts +0 -199
  251. package/dist/extensions/pi-powerline-footer/tests/working-vibes.test.ts +0 -317
  252. package/dist/extensions/pi-powerline-footer/theme.example.json +0 -24
  253. package/dist/extensions/pi-powerline-footer/theme.json +0 -12
  254. package/dist/extensions/pi-powerline-footer/theme.ts +0 -241
  255. package/dist/extensions/pi-powerline-footer/token-stats.ts +0 -304
  256. package/dist/extensions/pi-powerline-footer/tsconfig.json +0 -13
  257. package/dist/extensions/pi-powerline-footer/types.ts +0 -235
  258. package/dist/extensions/pi-powerline-footer/welcome-dismiss.ts +0 -34
  259. package/dist/extensions/pi-powerline-footer/welcome.ts +0 -709
  260. package/dist/extensions/pi-powerline-footer/working-vibes.ts +0 -721
  261. package/dist/extensions/preview-tools-disabled.test.ts +0 -37
  262. package/dist/extensions/preview-tools-disabled.ts +0 -11
  263. package/dist/themes/powerline-footer/theme.json +0 -33
@@ -1,10 +0,0 @@
1
- export function shouldShowStartupWelcome(reason: unknown, welcomeEnabled: boolean): boolean {
2
- return reason === "startup" && welcomeEnabled;
3
- }
4
-
5
- export function isStaleExtensionContextError(error: unknown): boolean {
6
- return error instanceof Error && (
7
- error.message.includes("This extension instance is stale")
8
- || error.message.includes("This extension ctx is stale")
9
- );
10
- }
@@ -1,49 +0,0 @@
1
- {
2
- "name": "pi-powerline-footer",
3
- "version": "0.13.0",
4
- "description": "Powerline-style status bar extension for pi coding agent",
5
- "type": "module",
6
- "files": [
7
- "*.ts",
8
- "*.md",
9
- "*.json",
10
- "!progress.md",
11
- "bash-mode/**/*.ts",
12
- "queue/**/*.ts"
13
- ],
14
- "keywords": [
15
- "pi-package",
16
- "pi",
17
- "coding-agent",
18
- "powerline",
19
- "status-bar",
20
- "extension"
21
- ],
22
- "author": "Nico Bailon",
23
- "license": "MIT",
24
- "repository": {
25
- "type": "git",
26
- "url": "git+https://github.com/nicobailon/pi-powerline-footer.git"
27
- },
28
- "scripts": {
29
- "typecheck": "tsc --noEmit",
30
- "test": "node --experimental-strip-types --test tests/**/*.test.ts"
31
- },
32
- "peerDependencies": {
33
- "@earendil-works/pi-ai": ">=0.84.1 <0.85.0",
34
- "@selesai/code": "*",
35
- "@earendil-works/pi-tui": ">=0.84.1 <0.85.0"
36
- },
37
- "devDependencies": {
38
- "@earendil-works/pi-ai": "^0.84.1",
39
- "@selesai/code": "^0.1.0",
40
- "@earendil-works/pi-tui": "^0.84.1",
41
- "@types/node": "24.13.3",
42
- "typescript": "5.9.3"
43
- },
44
- "pi": {
45
- "extensions": [
46
- "./index.ts"
47
- ]
48
- }
49
- }
@@ -1,22 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { homedir } from "node:os";
3
- import { join } from "node:path";
4
- import { CONFIG_DIR_NAME, getAgentDir } from "@selesai/code";
5
-
6
- export function getHomeDir(): string {
7
- return process.env.HOME || process.env.USERPROFILE || homedir();
8
- }
9
-
10
- export function getAgentPath(...segments: string[]): string {
11
- return join(getAgentDir(), ...segments);
12
- }
13
-
14
- export function getLegacyPiPath(...segments: string[]): string {
15
- return join(getHomeDir(), CONFIG_DIR_NAME, ...segments);
16
- }
17
-
18
- export function getAgentSessionDirs(): string[] {
19
- const primary = getAgentPath("sessions");
20
- const legacy = getLegacyPiPath("sessions");
21
- return existsSync(legacy) && legacy !== primary ? [primary, legacy] : [primary];
22
- }
@@ -1,446 +0,0 @@
1
- import { visibleWidth } from "@earendil-works/pi-tui";
2
- import { normalizeCostCurrency } from "./currency-rates.ts";
3
- import { BUILTIN_STATUS_LINE_SEGMENT_IDS } from "./types.ts";
4
- import type { ColorValue, CustomItemPosition, CustomStatusItem, PowerlinePlacement, PresetDef, StatusLineLayout, StatusLinePreset, StatusLineSegmentId, StatusLineSegmentOptions, StatusLineSeparatorStyle } from "./types.ts";
5
-
6
- export interface PowerlineConfig {
7
- preset: StatusLinePreset;
8
- customItems: CustomStatusItem[];
9
- fixedEditorPromptGlyph: string;
10
- disabledSegments: StatusLineSegmentId[];
11
- invalidDisabledSegments: string[];
12
- layout: StatusLineLayout | null;
13
- invalidLayoutSegments: string[];
14
- separator: StatusLineSeparatorStyle | null;
15
- segmentOptions: StatusLineSegmentOptions;
16
- placement: PowerlinePlacement;
17
- invalidPlacement: string | null;
18
- welcome: boolean;
19
- stashSharpSShortcut: boolean;
20
- }
21
-
22
- function isRecord(value: unknown): value is Record<string, unknown> {
23
- return typeof value === "object" && value !== null && !Array.isArray(value);
24
- }
25
-
26
- function normalizePreset(value: unknown, presets: readonly StatusLinePreset[]): StatusLinePreset | null {
27
- if (typeof value !== "string") return null;
28
- const normalized = value.trim().toLowerCase();
29
- return (presets as readonly string[]).includes(normalized) ? (normalized as StatusLinePreset) : null;
30
- }
31
-
32
- function normalizePlacement(value: unknown): { placement: PowerlinePlacement; invalidPlacement: string | null } {
33
- if (value === undefined) return { placement: "above", invalidPlacement: null };
34
-
35
- const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
36
- if (normalized === "above" || normalized === "below") {
37
- return { placement: normalized, invalidPlacement: null };
38
- }
39
-
40
- return {
41
- placement: "above",
42
- invalidPlacement: typeof value === "string" ? value.trim() : String(value),
43
- };
44
- }
45
-
46
- const SEPARATOR_STYLES = [
47
- "powerline",
48
- "powerline-thin",
49
- "slash",
50
- "pipe",
51
- "block",
52
- "none",
53
- "ascii",
54
- "dot",
55
- "chevron",
56
- "star",
57
- ] as const satisfies readonly StatusLineSeparatorStyle[];
58
-
59
- function normalizeSeparator(value: unknown): StatusLineSeparatorStyle | null {
60
- if (typeof value !== "string") return null;
61
- const normalized = value.trim().toLowerCase();
62
- return (SEPARATOR_STYLES as readonly string[]).includes(normalized)
63
- ? (normalized as StatusLineSeparatorStyle)
64
- : null;
65
- }
66
-
67
- function normalizeCustomItemId(value: unknown): string | null {
68
- if (typeof value !== "string") return null;
69
- const normalized = value.trim();
70
- if (!normalized) return null;
71
- return /^[a-zA-Z0-9_-]+$/.test(normalized) ? normalized : null;
72
- }
73
-
74
- function normalizeCustomItemPosition(value: unknown): CustomItemPosition {
75
- if (value === "left" || value === "right" || value === "secondary") return value;
76
- return "right";
77
- }
78
-
79
- function normalizeCustomColor(value: unknown): ColorValue | undefined {
80
- if (typeof value !== "string") return undefined;
81
- const normalized = value.trim();
82
- return normalized ? (normalized as ColorValue) : undefined;
83
- }
84
-
85
- function normalizeCustomPrefix(value: unknown): string | undefined {
86
- if (typeof value !== "string") return undefined;
87
- const normalized = value.trim();
88
- return normalized ? normalized : undefined;
89
- }
90
-
91
-
92
- function normalizeCustomStatusItem(raw: unknown, idOverride?: string): CustomStatusItem | null {
93
- if (!isRecord(raw)) return null;
94
- const id = normalizeCustomItemId(idOverride ?? raw.id);
95
- if (!id) return null;
96
-
97
- const statusKey = typeof raw.statusKey === "string" && raw.statusKey.trim() ? raw.statusKey.trim() : id;
98
-
99
- return {
100
- id,
101
- statusKey,
102
- position: normalizeCustomItemPosition(raw.position),
103
- color: normalizeCustomColor(raw.color),
104
- prefix: normalizeCustomPrefix(raw.prefix),
105
- hideWhenMissing: raw.hideWhenMissing !== false,
106
- excludeFromExtensionStatuses: raw.excludeFromExtensionStatuses !== false,
107
- };
108
- }
109
-
110
- function normalizeCustomItems(raw: unknown): CustomStatusItem[] {
111
- const normalized: CustomStatusItem[] = [];
112
-
113
- if (Array.isArray(raw)) {
114
- for (const entry of raw) {
115
- const item = normalizeCustomStatusItem(entry);
116
- if (item) normalized.push(item);
117
- }
118
- } else if (isRecord(raw)) {
119
- for (const [id, entry] of Object.entries(raw)) {
120
- const item = normalizeCustomStatusItem(entry, id);
121
- if (item) normalized.push(item);
122
- }
123
- }
124
-
125
- const deduped = new Map<string, CustomStatusItem>();
126
- for (const item of normalized) {
127
- deduped.set(item.id, item);
128
- }
129
-
130
- return [...deduped.values()];
131
- }
132
-
133
- const BUILTIN_STATUS_LINE_SEGMENT_ID_SET = new Set<string>(BUILTIN_STATUS_LINE_SEGMENT_IDS);
134
-
135
- function normalizeStatusLineSegmentId(value: unknown, customItemIds: ReadonlySet<string>): StatusLineSegmentId | null {
136
- if (typeof value !== "string") return null;
137
-
138
- const normalized = value.trim();
139
- if (BUILTIN_STATUS_LINE_SEGMENT_ID_SET.has(normalized)) {
140
- return normalized as StatusLineSegmentId;
141
- }
142
-
143
- const customId = normalized.startsWith("custom:")
144
- ? normalizeCustomItemId(normalized.slice("custom:".length))
145
- : null;
146
- return customId && customItemIds.has(customId) ? `custom:${customId}` : null;
147
- }
148
-
149
- function normalizeDisabledSegments(
150
- raw: unknown,
151
- customItems: readonly CustomStatusItem[],
152
- ): { disabledSegments: StatusLineSegmentId[]; invalidDisabledSegments: string[] } {
153
- if (!Array.isArray(raw)) return { disabledSegments: [], invalidDisabledSegments: [] };
154
-
155
- const disabledSegments: StatusLineSegmentId[] = [];
156
- const invalidDisabledSegments: string[] = [];
157
- const customItemIds = new Set(customItems.map((item) => item.id));
158
- const seen = new Set<StatusLineSegmentId>();
159
-
160
- for (const entry of raw) {
161
- const segmentId = normalizeStatusLineSegmentId(entry, customItemIds);
162
- if (!segmentId) {
163
- invalidDisabledSegments.push(typeof entry === "string" ? entry.trim() : String(entry));
164
- } else if (!seen.has(segmentId)) {
165
- seen.add(segmentId);
166
- disabledSegments.push(segmentId);
167
- }
168
- }
169
-
170
- return { disabledSegments, invalidDisabledSegments };
171
- }
172
-
173
- function normalizeLayout(
174
- raw: unknown,
175
- customItems: readonly CustomStatusItem[],
176
- ): { layout: StatusLineLayout | null; invalidLayoutSegments: string[] } {
177
- if (!isRecord(raw)) return { layout: null, invalidLayoutSegments: [] };
178
-
179
- const layout: StatusLineLayout = {};
180
- const invalidLayoutSegments: string[] = [];
181
- const customItemIds = new Set(customItems.map((item) => item.id));
182
- const globallyPlaced = new Set<StatusLineSegmentId>();
183
-
184
- for (const row of ["left", "right", "secondary"] as const) {
185
- const entries = raw[row];
186
- if (!Array.isArray(entries)) continue;
187
-
188
- const segments: StatusLineSegmentId[] = [];
189
- const seen = new Set<StatusLineSegmentId>();
190
- for (const entry of entries) {
191
- const segmentId = normalizeStatusLineSegmentId(entry, customItemIds);
192
- if (!segmentId) {
193
- invalidLayoutSegments.push(`${row}:${typeof entry === "string" ? entry.trim() : String(entry)}`);
194
- } else if (!seen.has(segmentId)) {
195
- seen.add(segmentId);
196
- if (globallyPlaced.has(segmentId)) {
197
- invalidLayoutSegments.push(`${row}:${segmentId}`);
198
- } else {
199
- globallyPlaced.add(segmentId);
200
- segments.push(segmentId);
201
- }
202
- }
203
- }
204
- layout[row] = segments;
205
- }
206
-
207
- return Object.keys(layout).length > 0
208
- ? { layout, invalidLayoutSegments }
209
- : { layout: null, invalidLayoutSegments };
210
- }
211
-
212
- function normalizeSegmentOptions(raw: Record<string, unknown>): StatusLineSegmentOptions {
213
- const options: StatusLineSegmentOptions = {};
214
-
215
- if (isRecord(raw.model)) {
216
- options.model = {
217
- ...(typeof raw.model.showThinkingLevel === "boolean" ? { showThinkingLevel: raw.model.showThinkingLevel } : {}),
218
- ...(raw.model.display === "name" || raw.model.display === "qualified" ? { display: raw.model.display } : {}),
219
- };
220
- }
221
-
222
- if (isRecord(raw.path)) {
223
- options.path = {
224
- ...(raw.path.mode === "basename" || raw.path.mode === "abbreviated" || raw.path.mode === "full" ? { mode: raw.path.mode } : {}),
225
- ...(typeof raw.path.maxLength === "number" && Number.isFinite(raw.path.maxLength) && raw.path.maxLength > 0
226
- ? { maxLength: Math.floor(raw.path.maxLength) }
227
- : {}),
228
- };
229
- }
230
-
231
- if (isRecord(raw.git)) {
232
- options.git = {
233
- ...(typeof raw.git.showBranch === "boolean" ? { showBranch: raw.git.showBranch } : {}),
234
- ...(typeof raw.git.showStaged === "boolean" ? { showStaged: raw.git.showStaged } : {}),
235
- ...(typeof raw.git.showUnstaged === "boolean" ? { showUnstaged: raw.git.showUnstaged } : {}),
236
- ...(typeof raw.git.showUntracked === "boolean" ? { showUntracked: raw.git.showUntracked } : {}),
237
- ...(raw.git.polling === "full" || raw.git.polling === "branch" || raw.git.polling === "off" ? { polling: raw.git.polling } : {}),
238
- ...(typeof raw.git.hostIcon === "boolean" ? { hostIcon: raw.git.hostIcon } : {}),
239
- };
240
- }
241
-
242
- if (isRecord(raw.time)) {
243
- options.time = {
244
- ...(raw.time.format === "12h" || raw.time.format === "24h" ? { format: raw.time.format } : {}),
245
- ...(typeof raw.time.showSeconds === "boolean" ? { showSeconds: raw.time.showSeconds } : {}),
246
- };
247
- }
248
-
249
- if (isRecord(raw.cost)) {
250
- const currency = normalizeCostCurrency(raw.cost.currency);
251
- options.cost = {
252
- ...(raw.cost.subscriptionDisplay === "subscription"
253
- || raw.cost.subscriptionDisplay === "reported-cost"
254
- || raw.cost.subscriptionDisplay === "both"
255
- ? { subscriptionDisplay: raw.cost.subscriptionDisplay }
256
- : {}),
257
- ...(currency ? { currency } : {}),
258
- };
259
- }
260
-
261
- if (isRecord(raw.context)) {
262
- options.context = {
263
- ...(raw.context.format === "full" || raw.context.format === "percent" ? { format: raw.context.format } : {}),
264
- };
265
- }
266
-
267
- if (isRecord(raw.cache_read)) {
268
- options.cache_read = {
269
- ...(raw.cache_read.format === "tokens" || raw.cache_read.format === "percent" || raw.cache_read.format === "both"
270
- ? { format: raw.cache_read.format }
271
- : {}),
272
- };
273
- }
274
-
275
- return options;
276
- }
277
-
278
- export function mergeSegmentOptions(
279
- defaults: StatusLineSegmentOptions = {},
280
- overrides: StatusLineSegmentOptions = {},
281
- ): StatusLineSegmentOptions {
282
- return {
283
- ...defaults,
284
- ...overrides,
285
- model: { ...defaults.model, ...overrides.model },
286
- path: { ...defaults.path, ...overrides.path },
287
- git: { ...defaults.git, ...overrides.git },
288
- time: { ...defaults.time, ...overrides.time },
289
- cost: { ...defaults.cost, ...overrides.cost },
290
- context: { ...defaults.context, ...overrides.context },
291
- cache_read: { ...defaults.cache_read, ...overrides.cache_read },
292
- };
293
- }
294
-
295
- export function parsePowerlineConfig(value: unknown, presets: readonly StatusLinePreset[]): PowerlineConfig {
296
- const defaultConfig: PowerlineConfig = {
297
- preset: "default",
298
- customItems: [],
299
- fixedEditorPromptGlyph: "",
300
- disabledSegments: [],
301
- invalidDisabledSegments: [],
302
- layout: null,
303
- invalidLayoutSegments: [],
304
- separator: null,
305
- segmentOptions: {},
306
- placement: "above",
307
- invalidPlacement: null,
308
- welcome: true,
309
- stashSharpSShortcut: false,
310
- };
311
-
312
- const directPreset = normalizePreset(value, presets);
313
- if (directPreset) return { ...defaultConfig, preset: directPreset };
314
-
315
- if (!isRecord(value)) return defaultConfig;
316
-
317
- const customItems = normalizeCustomItems(value.customItems);
318
- const { disabledSegments, invalidDisabledSegments } = normalizeDisabledSegments(value.disabledSegments, customItems);
319
- const { layout, invalidLayoutSegments } = normalizeLayout(value.layout, customItems);
320
- const { placement, invalidPlacement } = normalizePlacement(value.placement);
321
-
322
- return {
323
- preset: normalizePreset(value.preset, presets) ?? defaultConfig.preset,
324
- customItems,
325
- fixedEditorPromptGlyph: typeof value.fixedEditorPromptGlyph === "string" ? value.fixedEditorPromptGlyph : defaultConfig.fixedEditorPromptGlyph,
326
- disabledSegments,
327
- invalidDisabledSegments,
328
- layout,
329
- invalidLayoutSegments,
330
- separator: normalizeSeparator(value.separator),
331
- segmentOptions: normalizeSegmentOptions(value),
332
- placement,
333
- invalidPlacement,
334
- welcome: value.welcome !== false,
335
- stashSharpSShortcut: value.stashSharpSShortcut === true,
336
- };
337
- }
338
-
339
- export function mergeSegmentsWithCustomItems(
340
- presetDef: PresetDef,
341
- customItems: readonly CustomStatusItem[],
342
- options: {
343
- layout?: StatusLineLayout | null;
344
- disabledSegments?: readonly StatusLineSegmentId[];
345
- } = {},
346
- ): {
347
- leftSegments: StatusLineSegmentId[];
348
- rightSegments: StatusLineSegmentId[];
349
- secondarySegments: StatusLineSegmentId[];
350
- } {
351
- const layout = options.layout ?? null;
352
- const explicitlyPlaced = new Set([
353
- ...(layout?.left ?? []),
354
- ...(layout?.right ?? []),
355
- ...(layout?.secondary ?? []),
356
- ]);
357
- const disabled = new Set(options.disabledSegments ?? []);
358
-
359
- const buildRow = (
360
- position: CustomItemPosition,
361
- configured: StatusLineSegmentId[] | undefined,
362
- presetSegments: readonly StatusLineSegmentId[],
363
- ): StatusLineSegmentId[] => {
364
- const segments = configured !== undefined
365
- ? [...configured]
366
- : presetSegments.filter((id) => !explicitlyPlaced.has(id));
367
-
368
- if (configured === undefined) {
369
- for (const item of customItems) {
370
- const segmentId: StatusLineSegmentId = `custom:${item.id}`;
371
- if (item.position === position && !explicitlyPlaced.has(segmentId)) {
372
- segments.push(segmentId);
373
- }
374
- }
375
- }
376
-
377
- return segments.filter((id) => !disabled.has(id));
378
- };
379
-
380
- return {
381
- leftSegments: buildRow("left", layout?.left, presetDef.leftSegments),
382
- rightSegments: buildRow("right", layout?.right, presetDef.rightSegments),
383
- secondarySegments: buildRow("secondary", layout?.secondary, presetDef.secondarySegments ?? []),
384
- };
385
- }
386
-
387
- export function nextPowerlineSettingWithPreset(existingPowerlineSetting: unknown, preset: StatusLinePreset): unknown {
388
- if (!isRecord(existingPowerlineSetting)) {
389
- return preset;
390
- }
391
- return { ...existingPowerlineSetting, preset };
392
- }
393
-
394
- export function nextPowerlineSettingWithOptions(
395
- existingPowerlineSetting: unknown,
396
- updates: Partial<Pick<PowerlineConfig, "welcome" | "stashSharpSShortcut" | "placement">>,
397
- currentPreset: StatusLinePreset,
398
- ): unknown {
399
- if (!isRecord(existingPowerlineSetting)) {
400
- return { preset: currentPreset, ...updates };
401
- }
402
- return { ...existingPowerlineSetting, ...updates };
403
- }
404
-
405
- export function collectHiddenExtensionStatusKeys(customItems: readonly CustomStatusItem[]): Set<string> {
406
- const hidden = new Set<string>();
407
- for (const item of customItems) {
408
- if (item.excludeFromExtensionStatuses) hidden.add(item.statusKey);
409
- }
410
- return hidden;
411
- }
412
-
413
- export function isNotificationExtensionStatus(value: string): boolean {
414
- return value.trimStart().startsWith("[");
415
- }
416
-
417
- export function getNotificationExtensionStatuses(
418
- statuses: ReadonlyMap<string, string>,
419
- hiddenKeys: ReadonlySet<string>,
420
- ): string[] {
421
- const notifications: string[] = [];
422
- for (const [statusKey, value] of statuses.entries()) {
423
- if (hiddenKeys.has(statusKey) || !value || !isNotificationExtensionStatus(value)) {
424
- continue;
425
- }
426
- notifications.push(value);
427
- }
428
- return notifications;
429
- }
430
-
431
- export function normalizeExtensionStatusValue(value: string): string | null {
432
- if (!value || visibleWidth(value) <= 0) {
433
- return null;
434
- }
435
-
436
- const stripped = value.replace(/(\x1b\[[0-9;]*m|\s|·|[|])+$/, "");
437
- return visibleWidth(stripped) > 0 ? stripped : null;
438
- }
439
-
440
- export function normalizeCompactExtensionStatus(value: string): string | null {
441
- if (isNotificationExtensionStatus(value)) {
442
- return null;
443
- }
444
-
445
- return normalizeExtensionStatusValue(value);
446
- }
@@ -1,102 +0,0 @@
1
- import type { ColorScheme, PresetDef, StatusLinePreset } from "./types.ts";
2
- import { getDefaultColors } from "./theme.ts";
3
-
4
- // Get base colors from theme.ts (single source of truth)
5
- const DEFAULT_COLORS: ColorScheme = getDefaultColors();
6
-
7
- // Minimal - more muted, less colorful
8
- const MINIMAL_COLORS: ColorScheme = {
9
- ...DEFAULT_COLORS,
10
- model: "text",
11
- path: "text",
12
- gitClean: "dim",
13
- };
14
-
15
- // Nerd - vibrant colors
16
- const NERD_COLORS: ColorScheme = {
17
- ...DEFAULT_COLORS,
18
- model: "accent",
19
- path: "success",
20
- tokens: "muted",
21
- cost: "warning",
22
- };
23
-
24
- export const PRESETS: Record<StatusLinePreset, PresetDef> = {
25
- default: {
26
- leftSegments: ["model", "thinking", "shell_mode", "path", "git", "queue", "context_pct", "cache_read", "cost"],
27
- rightSegments: [],
28
- secondarySegments: ["extension_statuses"],
29
- separator: "powerline-thin",
30
- colors: DEFAULT_COLORS,
31
- segmentOptions: {
32
- model: { showThinkingLevel: false },
33
- path: { mode: "basename" },
34
- git: { showBranch: true, showStaged: true, showUnstaged: true, showUntracked: true },
35
- },
36
- },
37
-
38
- minimal: {
39
- leftSegments: ["shell_mode", "path", "git"],
40
- rightSegments: ["context_pct"],
41
- separator: "slash",
42
- colors: MINIMAL_COLORS,
43
- segmentOptions: {
44
- path: { mode: "basename" },
45
- git: { showBranch: true, showStaged: false, showUnstaged: false, showUntracked: false },
46
- },
47
- },
48
-
49
- compact: {
50
- leftSegments: ["model", "shell_mode", "git"],
51
- rightSegments: ["queue", "cost", "context_pct"],
52
- separator: "powerline-thin",
53
- colors: DEFAULT_COLORS,
54
- segmentOptions: {
55
- model: { showThinkingLevel: false },
56
- git: { showBranch: true, showStaged: true, showUnstaged: true, showUntracked: false },
57
- },
58
- },
59
-
60
- full: {
61
- leftSegments: ["hostname", "model", "thinking", "shell_mode", "path", "git", "queue", "subagents"],
62
- rightSegments: ["token_in", "token_out", "cache_read", "cost", "context_pct", "time_spent", "time", "extension_statuses"],
63
- separator: "powerline",
64
- colors: DEFAULT_COLORS,
65
- segmentOptions: {
66
- model: { showThinkingLevel: false },
67
- path: { mode: "abbreviated", maxLength: 50 },
68
- git: { showBranch: true, showStaged: true, showUnstaged: true, showUntracked: true },
69
- time: { format: "24h", showSeconds: false },
70
- },
71
- },
72
-
73
- nerd: {
74
- leftSegments: ["hostname", "model", "thinking", "shell_mode", "path", "git", "queue", "session", "subagents"],
75
- rightSegments: ["token_in", "token_out", "cache_read", "cache_write", "cost", "context_pct", "context_total", "time_spent", "time", "extension_statuses"],
76
- separator: "powerline",
77
- colors: NERD_COLORS,
78
- segmentOptions: {
79
- model: { showThinkingLevel: false },
80
- path: { mode: "abbreviated", maxLength: 60 },
81
- git: { showBranch: true, showStaged: true, showUnstaged: true, showUntracked: true },
82
- time: { format: "24h", showSeconds: true },
83
- },
84
- },
85
-
86
- ascii: {
87
- leftSegments: ["model", "shell_mode", "path", "git"],
88
- rightSegments: ["queue", "token_total", "cost", "context_pct"],
89
- separator: "ascii",
90
- colors: MINIMAL_COLORS,
91
- segmentOptions: {
92
- model: { showThinkingLevel: true },
93
- path: { mode: "abbreviated", maxLength: 40 },
94
- git: { showBranch: true, showStaged: true, showUnstaged: true, showUntracked: true },
95
- },
96
- },
97
-
98
- };
99
-
100
- export function getPreset(name: StatusLinePreset): PresetDef {
101
- return PRESETS[name] ?? PRESETS.default;
102
- }