@selesai/code 0.9.15 → 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 (267) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +6 -9
  3. package/dist/defaults/models.json +2 -2
  4. package/dist/defaults/settings.json +3 -7
  5. package/dist/extensions/package.json +2 -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/package.json +3 -3
  193. package/dist/extensions/auto-model/auto-model.test.ts +0 -18
  194. package/dist/extensions/auto-model/classifier.ts +0 -23
  195. package/dist/extensions/auto-model/config.ts +0 -51
  196. package/dist/extensions/auto-model/index.ts +0 -22
  197. package/dist/extensions/auto-model/lifecycle.test.ts +0 -24
  198. package/dist/extensions/pi-powerline-footer/.github/workflows/test.yml +0 -27
  199. package/dist/extensions/pi-powerline-footer/CHANGELOG.md +0 -687
  200. package/dist/extensions/pi-powerline-footer/CONTRIBUTING.md +0 -16
  201. package/dist/extensions/pi-powerline-footer/README.md +0 -513
  202. package/dist/extensions/pi-powerline-footer/banner.png +0 -0
  203. package/dist/extensions/pi-powerline-footer/bash-mode/completion.ts +0 -586
  204. package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +0 -583
  205. package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +0 -204
  206. package/dist/extensions/pi-powerline-footer/bash-mode/shell-session.ts +0 -286
  207. package/dist/extensions/pi-powerline-footer/bash-mode/transcript.ts +0 -108
  208. package/dist/extensions/pi-powerline-footer/bash-mode/types.ts +0 -60
  209. package/dist/extensions/pi-powerline-footer/cd-command.ts +0 -190
  210. package/dist/extensions/pi-powerline-footer/colors.ts +0 -70
  211. package/dist/extensions/pi-powerline-footer/context-usage.ts +0 -156
  212. package/dist/extensions/pi-powerline-footer/currency-rates.ts +0 -142
  213. package/dist/extensions/pi-powerline-footer/editor-composition.ts +0 -32
  214. package/dist/extensions/pi-powerline-footer/editor-performance.ts +0 -99
  215. package/dist/extensions/pi-powerline-footer/git-status.ts +0 -329
  216. package/dist/extensions/pi-powerline-footer/guide.ts +0 -220
  217. package/dist/extensions/pi-powerline-footer/icons.ts +0 -190
  218. package/dist/extensions/pi-powerline-footer/index.ts +0 -3428
  219. package/dist/extensions/pi-powerline-footer/lifecycle.ts +0 -10
  220. package/dist/extensions/pi-powerline-footer/package.json +0 -49
  221. package/dist/extensions/pi-powerline-footer/paths.ts +0 -22
  222. package/dist/extensions/pi-powerline-footer/powerline-config.ts +0 -446
  223. package/dist/extensions/pi-powerline-footer/presets.ts +0 -102
  224. package/dist/extensions/pi-powerline-footer/queue/store.ts +0 -313
  225. package/dist/extensions/pi-powerline-footer/queue/types.ts +0 -53
  226. package/dist/extensions/pi-powerline-footer/render-scheduler.ts +0 -29
  227. package/dist/extensions/pi-powerline-footer/segments.ts +0 -570
  228. package/dist/extensions/pi-powerline-footer/separators.ts +0 -57
  229. package/dist/extensions/pi-powerline-footer/session-usage.ts +0 -44
  230. package/dist/extensions/pi-powerline-footer/shortcuts.ts +0 -61
  231. package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +0 -1905
  232. package/dist/extensions/pi-powerline-footer/tests/cd-command.test.ts +0 -144
  233. package/dist/extensions/pi-powerline-footer/tests/context-usage.test.ts +0 -156
  234. package/dist/extensions/pi-powerline-footer/tests/custom-items.test.ts +0 -344
  235. package/dist/extensions/pi-powerline-footer/tests/editor-composition.test.ts +0 -48
  236. package/dist/extensions/pi-powerline-footer/tests/editor-performance.test.ts +0 -43
  237. package/dist/extensions/pi-powerline-footer/tests/editor-responsiveness.test.ts +0 -136
  238. package/dist/extensions/pi-powerline-footer/tests/fixed-editor-border.test.ts +0 -39
  239. package/dist/extensions/pi-powerline-footer/tests/git-optional-locks.test.ts +0 -64
  240. package/dist/extensions/pi-powerline-footer/tests/git-status.test.ts +0 -92
  241. package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +0 -129
  242. package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +0 -116
  243. package/dist/extensions/pi-powerline-footer/tests/mode-aware-trigger.test.ts +0 -79
  244. package/dist/extensions/pi-powerline-footer/tests/paths.test.ts +0 -63
  245. package/dist/extensions/pi-powerline-footer/tests/queue-store.test.ts +0 -65
  246. package/dist/extensions/pi-powerline-footer/tests/quit-cursor.test.ts +0 -14
  247. package/dist/extensions/pi-powerline-footer/tests/remaining-regressions.test.ts +0 -173
  248. package/dist/extensions/pi-powerline-footer/tests/session-usage.test.ts +0 -47
  249. package/dist/extensions/pi-powerline-footer/tests/stash-shortcut.test.ts +0 -37
  250. package/dist/extensions/pi-powerline-footer/tests/thinking-segment.test.ts +0 -76
  251. package/dist/extensions/pi-powerline-footer/tests/token-stats.test.ts +0 -249
  252. package/dist/extensions/pi-powerline-footer/tests/tps.test.ts +0 -39
  253. package/dist/extensions/pi-powerline-footer/tests/usage-display.test.ts +0 -237
  254. package/dist/extensions/pi-powerline-footer/tests/welcome.test.ts +0 -199
  255. package/dist/extensions/pi-powerline-footer/tests/working-vibes.test.ts +0 -317
  256. package/dist/extensions/pi-powerline-footer/theme.example.json +0 -24
  257. package/dist/extensions/pi-powerline-footer/theme.json +0 -12
  258. package/dist/extensions/pi-powerline-footer/theme.ts +0 -241
  259. package/dist/extensions/pi-powerline-footer/token-stats.ts +0 -304
  260. package/dist/extensions/pi-powerline-footer/tsconfig.json +0 -13
  261. package/dist/extensions/pi-powerline-footer/types.ts +0 -235
  262. package/dist/extensions/pi-powerline-footer/welcome-dismiss.ts +0 -34
  263. package/dist/extensions/pi-powerline-footer/welcome.ts +0 -709
  264. package/dist/extensions/pi-powerline-footer/working-vibes.ts +0 -721
  265. package/dist/extensions/preview-tools-disabled.test.ts +0 -37
  266. package/dist/extensions/preview-tools-disabled.ts +0 -11
  267. package/dist/themes/powerline-footer/theme.json +0 -33
@@ -1,570 +0,0 @@
1
- import { hostname as osHostname } from "node:os";
2
- import { basename } from "node:path";
3
- import { visibleWidth } from "@earendil-works/pi-tui";
4
- import type { BuiltinStatusLineSegmentId, RenderedSegment, SegmentContext, SemanticColor, StatusLineSegment, StatusLineSegmentId } from "./types.ts";
5
- import { normalizeCompactExtensionStatus, normalizeExtensionStatusValue } from "./powerline-config.ts";
6
- import { fg, rainbow, applyColor } from "./theme.ts";
7
- import { getIcons, SEP_DOT, getThinkingText } from "./icons.ts";
8
- import { formatUsdCost } from "./currency-rates.ts";
9
- import { getGitRemoteHost } from "./git-status.ts";
10
- import type { IconSet } from "./icons.ts";
11
- import type { GitHost } from "./git-status.ts";
12
-
13
- function color(ctx: SegmentContext, semantic: SemanticColor, text: string): string {
14
- return fg(ctx.theme, semantic, text, ctx.colors);
15
- }
16
-
17
- // ═══════════════════════════════════════════════════════════════════════════
18
- // Helpers
19
- // ═══════════════════════════════════════════════════════════════════════════
20
-
21
- function withIcon(icon: string, text: string): string {
22
- return icon ? `${icon} ${text}` : text;
23
- }
24
-
25
- function formatTokens(n: number): string {
26
- if (n < 1000) return n.toString();
27
- if (n < 10000) return `${(n / 1000).toFixed(1)}k`;
28
- if (n < 1000000) return `${Math.round(n / 1000)}k`;
29
- if (n < 10000000) return `${(n / 1000000).toFixed(1)}M`;
30
- return `${Math.round(n / 1000000)}M`;
31
- }
32
-
33
- function formatDuration(ms: number): string {
34
- const seconds = Math.floor(ms / 1000);
35
- const minutes = Math.floor(seconds / 60);
36
- const hours = Math.floor(minutes / 60);
37
-
38
- if (hours > 0) return `${hours}h${minutes % 60}m`;
39
- if (minutes > 0) return `${minutes}m${seconds % 60}s`;
40
- return `${seconds}s`;
41
- }
42
-
43
- // ═══════════════════════════════════════════════════════════════════════════
44
- // Segment Implementations
45
- // ═══════════════════════════════════════════════════════════════════════════
46
-
47
- const modelSegment: StatusLineSegment = {
48
- id: "model",
49
- render(ctx) {
50
- const icons = getIcons();
51
- const opts = ctx.options.model ?? {};
52
-
53
- let modelName = ctx.model?.name || ctx.model?.id || "no-model";
54
- if (opts.display === "qualified" && ctx.model?.id) {
55
- const provider = ctx.model.provider || ctx.model.providerId || ctx.model.providerName;
56
- modelName = provider && !ctx.model.id.includes("/") ? `${provider}/${ctx.model.id}` : ctx.model.id;
57
- } else if (modelName.startsWith("Claude ")) {
58
- modelName = modelName.slice(7);
59
- }
60
-
61
- let content = withIcon(icons.model, modelName);
62
-
63
- if (opts.showThinkingLevel !== false && ctx.model?.reasoning) {
64
- const level = ctx.thinkingLevel || "off";
65
- if (level !== "off") {
66
- const thinkingText = getThinkingText(level);
67
- if (thinkingText) {
68
- content += `${SEP_DOT}${thinkingText}`;
69
- }
70
- }
71
- }
72
-
73
- return { content: color(ctx, "model", content), visible: true };
74
- },
75
- };
76
-
77
- const shellModeSegment: StatusLineSegment = {
78
- id: "shell_mode",
79
- render(ctx) {
80
- if (!ctx.shellModeActive) {
81
- return { content: "", visible: false };
82
- }
83
-
84
- const shellName = ctx.shellName ?? "shell";
85
- const state = ctx.shellRunning ? "run" : "idle";
86
- const cwd = ctx.shellCwd ? basename(ctx.shellCwd) : null;
87
- const parts = [shellName, state];
88
- if (cwd) {
89
- parts.push(cwd);
90
- }
91
-
92
- return { content: color(ctx, "shellMode", parts.join(SEP_DOT)), visible: true };
93
- },
94
- };
95
-
96
- const pathSegment: StatusLineSegment = {
97
- id: "path",
98
- render(ctx) {
99
- const icons = getIcons();
100
- const opts = ctx.options.path ?? {};
101
- const mode = opts.mode ?? "basename";
102
-
103
- let pwd = ctx.shellModeActive && ctx.shellCwd ? ctx.shellCwd : (ctx.cwd ?? process.cwd());
104
- const home = process.env.HOME || process.env.USERPROFILE;
105
-
106
- if (mode === "basename") {
107
- // Just the last directory component (cross-platform)
108
- pwd = basename(pwd) || pwd;
109
- } else {
110
- // Abbreviate home directory for abbreviated/full modes
111
- if (home && pwd.startsWith(home)) {
112
- pwd = `~${pwd.slice(home.length)}`;
113
- }
114
-
115
- // Strip /work/ prefix (common in containers)
116
- if (pwd.startsWith("/work/")) {
117
- pwd = pwd.slice(6);
118
- }
119
-
120
- // Truncate if too long (only for abbreviated mode)
121
- if (mode === "abbreviated") {
122
- const maxLen = opts.maxLength ?? 40;
123
- if (pwd.length > maxLen) {
124
- pwd = `…${pwd.slice(-(maxLen - 1))}`;
125
- }
126
- }
127
- }
128
-
129
- const content = withIcon(icons.folder, pwd);
130
- return { content: color(ctx, "path", content), visible: true };
131
- },
132
- };
133
-
134
- /**
135
- * Icon for the branch label: the origin remote's host logo when hostIcon is
136
- * enabled and a remote is known, otherwise the plain branch icon. An
137
- * unrecognized remote falls back to the generic git logo.
138
- */
139
- function resolveBranchIcon(icons: IconSet, hostIcon: boolean): string {
140
- if (!hostIcon) return icons.branch;
141
- const host = getGitRemoteHost();
142
- const byHost: Record<GitHost, string> = {
143
- github: icons.github,
144
- gitlab: icons.gitlab,
145
- bitbucket: icons.bitbucket,
146
- other: icons.git,
147
- };
148
- return host ? byHost[host] : icons.branch;
149
- }
150
-
151
- const gitSegment: StatusLineSegment = {
152
- id: "git",
153
- render(ctx) {
154
- const icons = getIcons();
155
- const opts = ctx.options.git ?? {};
156
- const { branch, staged, unstaged, untracked } = ctx.git;
157
- const gitStatus = (staged > 0 || unstaged > 0 || untracked > 0)
158
- ? { staged, unstaged, untracked }
159
- : null;
160
-
161
- if (!branch && !gitStatus) return { content: "", visible: false };
162
-
163
- const isDirty = gitStatus && (gitStatus.staged > 0 || gitStatus.unstaged > 0 || gitStatus.untracked > 0);
164
- const showBranch = opts.showBranch !== false;
165
- const branchColor: SemanticColor = isDirty ? "gitDirty" : "gitClean";
166
-
167
- // Build content - color branch separately from indicators
168
- let content = "";
169
- if (showBranch && branch) {
170
- // Color just the branch name (icon + branch text)
171
- const branchIcon = resolveBranchIcon(icons, opts.hostIcon === true);
172
- content = color(ctx, branchColor, withIcon(branchIcon, branch));
173
- }
174
-
175
- // Add status indicators (each with their own color, not wrapped)
176
- if (gitStatus) {
177
- const indicators: string[] = [];
178
- if (opts.showUnstaged !== false && gitStatus.unstaged > 0) {
179
- indicators.push(applyColor(ctx.theme, "warning", `*${gitStatus.unstaged}`));
180
- }
181
- if (opts.showStaged !== false && gitStatus.staged > 0) {
182
- indicators.push(applyColor(ctx.theme, "success", `+${gitStatus.staged}`));
183
- }
184
- if (opts.showUntracked !== false && gitStatus.untracked > 0) {
185
- indicators.push(applyColor(ctx.theme, "muted", `?${gitStatus.untracked}`));
186
- }
187
- if (indicators.length > 0) {
188
- const indicatorText = indicators.join(" ");
189
- if (!content && showBranch === false) {
190
- // No branch shown, color the git icon with branch color
191
- content = color(ctx, branchColor, icons.git ? `${icons.git} ` : "") + indicatorText;
192
- } else {
193
- content += content ? ` ${indicatorText}` : indicatorText;
194
- }
195
- }
196
- }
197
-
198
- if (!content) return { content: "", visible: false };
199
-
200
- return { content, visible: true };
201
- },
202
- };
203
-
204
- const thinkingSegment: StatusLineSegment = {
205
- id: "thinking",
206
- render(ctx) {
207
- const level = ctx.thinkingLevel || "off";
208
-
209
- const levelText: Record<string, string> = {
210
- off: "off",
211
- minimal: "min",
212
- low: "low",
213
- medium: "med",
214
- high: "high",
215
- xhigh: "xhigh",
216
- };
217
- const label = levelText[level] || level;
218
- const content = `think:${label}`;
219
-
220
- if (level === "high" || level === "xhigh" || level === "max") {
221
- return { content: rainbow(content), visible: true };
222
- }
223
-
224
- if (level === "minimal") {
225
- return { content: color(ctx, "thinkingMinimal", content), visible: true };
226
- }
227
- if (level === "low") {
228
- return { content: color(ctx, "thinkingLow", content), visible: true };
229
- }
230
- if (level === "medium") {
231
- return { content: color(ctx, "thinkingMedium", content), visible: true };
232
- }
233
-
234
- return { content: color(ctx, "thinking", content), visible: true };
235
- },
236
- };
237
-
238
- const subagentsSegment: StatusLineSegment = {
239
- id: "subagents",
240
- render() {
241
- // Note: pi-mono doesn't have subagent tracking built-in
242
- // This would require extension state management
243
- // For now, return not visible
244
- return { content: "", visible: false };
245
- },
246
- };
247
-
248
- const queueSegment: StatusLineSegment = {
249
- id: "queue",
250
- render(ctx) {
251
- const summary = ctx.queueSummary;
252
- const parts: string[] = [];
253
-
254
- if (summary.compacting && summary.queueCount > 0) {
255
- parts.push(`compact q ${summary.queueCount}`);
256
- } else if (summary.queueCount > 0) {
257
- parts.push(`q ${summary.queueCount}`);
258
- }
259
-
260
-
261
- if (summary.blockedCount > 0) {
262
- parts.push(`blocked ${summary.blockedCount}`);
263
- }
264
-
265
- if (parts.length === 0) return { content: "", visible: false };
266
- return { content: color(ctx, "queue", parts.join(SEP_DOT)), visible: true };
267
- },
268
- };
269
-
270
- const tokenInSegment: StatusLineSegment = {
271
- id: "token_in",
272
- render(ctx) {
273
- const icons = getIcons();
274
- const { input } = ctx.usageStats;
275
- if (!input) return { content: "", visible: false };
276
-
277
- const content = withIcon(icons.input, formatTokens(input));
278
- return { content: color(ctx, "tokens", content), visible: true };
279
- },
280
- };
281
-
282
- const tokenOutSegment: StatusLineSegment = {
283
- id: "token_out",
284
- render(ctx) {
285
- const icons = getIcons();
286
- const { output } = ctx.usageStats;
287
- if (!output) return { content: "", visible: false };
288
-
289
- const content = withIcon(icons.output, formatTokens(output));
290
- return { content: color(ctx, "tokens", content), visible: true };
291
- },
292
- };
293
-
294
- const tokenTotalSegment: StatusLineSegment = {
295
- id: "token_total",
296
- render(ctx) {
297
- const icons = getIcons();
298
- const { input, output, cacheRead, cacheWrite } = ctx.usageStats;
299
- const total = input + output + cacheRead + cacheWrite;
300
- if (!total) return { content: "", visible: false };
301
-
302
- const content = withIcon(icons.tokens, formatTokens(total));
303
- return { content: color(ctx, "tokens", content), visible: true };
304
- },
305
- };
306
-
307
- const costSegment: StatusLineSegment = {
308
- id: "cost",
309
- render(ctx) {
310
- const cost = ctx.usageStats.cost + (ctx.usageStats.subagentCost ?? 0);
311
- const usingSubscription = ctx.usingSubscription;
312
-
313
- if (!cost && !usingSubscription) {
314
- return { content: "", visible: false };
315
- }
316
-
317
- const reportedCost = cost > 0 ? formatUsdCost(cost, ctx.options.cost?.currency) : null;
318
- if (!usingSubscription) {
319
- return reportedCost
320
- ? { content: color(ctx, "cost", reportedCost), visible: true }
321
- : { content: "", visible: false };
322
- }
323
-
324
- const subscriptionDisplay = ctx.options.cost?.subscriptionDisplay ?? "subscription";
325
- if (subscriptionDisplay === "reported-cost" && reportedCost) {
326
- return { content: color(ctx, "cost", reportedCost), visible: true };
327
- }
328
- if (subscriptionDisplay === "both" && reportedCost) {
329
- return { content: color(ctx, "cost", `${reportedCost} (sub)`), visible: true };
330
- }
331
-
332
- return { content: color(ctx, "cost", "(sub)"), visible: true };
333
- },
334
- };
335
-
336
- const contextPctSegment: StatusLineSegment = {
337
- id: "context_pct",
338
- render(ctx) {
339
- if (ctx.customCompactionEnabled) return { content: "", visible: false };
340
-
341
- const icons = getIcons();
342
- const { contextTokens, contextPercent, contextWindow } = ctx;
343
-
344
- const autoIcon = ctx.autoCompactEnabled && icons.auto ? ` ${icons.auto}` : "";
345
- const percentOnly = ctx.options.context?.format === "percent";
346
- const hasKnownUsage = contextTokens !== null && contextPercent !== null;
347
- const approximate = ctx.contextApproximate ? "~" : "";
348
- // "full" (default): tokens/window + one-decimal percentage + auto-compact icon.
349
- // "percent": bare rounded percentage, threshold-colored, no icons.
350
- const text = percentOnly
351
- ? (hasKnownUsage ? `${approximate}${Math.round(contextPercent)}%` : "?")
352
- : hasKnownUsage
353
- ? `${approximate}${formatTokens(contextTokens)}/${formatTokens(contextWindow)} (${contextPercent.toFixed(1)}%)${autoIcon}`
354
- : `?/${formatTokens(contextWindow)}${autoIcon}`;
355
-
356
- // Icon outside color, text inside - use semantic colors for thresholds
357
- let content: string;
358
- const colored = (semantic: "context" | "contextWarn" | "contextError") =>
359
- percentOnly ? color(ctx, semantic, text) : withIcon(icons.context, color(ctx, semantic, text));
360
- if (hasKnownUsage && contextPercent > 90) {
361
- content = colored("contextError");
362
- } else if (hasKnownUsage && contextPercent > 70) {
363
- content = colored("contextWarn");
364
- } else {
365
- content = colored("context");
366
- }
367
-
368
- return { content, visible: true };
369
- },
370
- };
371
-
372
- const contextTotalSegment: StatusLineSegment = {
373
- id: "context_total",
374
- render(ctx) {
375
- if (ctx.customCompactionEnabled) return { content: "", visible: false };
376
-
377
- const icons = getIcons();
378
- const window = ctx.contextWindow;
379
- if (!window) return { content: "", visible: false };
380
-
381
- return {
382
- content: color(ctx, "context", withIcon(icons.context, formatTokens(window))),
383
- visible: true,
384
- };
385
- },
386
- };
387
-
388
- const timeSpentSegment: StatusLineSegment = {
389
- id: "time_spent",
390
- render(ctx) {
391
- const icons = getIcons();
392
- const elapsed = Date.now() - ctx.sessionStartTime;
393
- if (elapsed < 1000) return { content: "", visible: false };
394
-
395
- return { content: withIcon(icons.time, formatDuration(elapsed)), visible: true };
396
- },
397
- };
398
-
399
- const timeSegment: StatusLineSegment = {
400
- id: "time",
401
- render(ctx) {
402
- const icons = getIcons();
403
- const opts = ctx.options.time ?? {};
404
- const now = new Date();
405
-
406
- let hours = now.getHours();
407
- let suffix = "";
408
- if (opts.format === "12h") {
409
- suffix = hours >= 12 ? "pm" : "am";
410
- hours = hours % 12 || 12;
411
- }
412
-
413
- const mins = now.getMinutes().toString().padStart(2, "0");
414
- let timeStr = `${hours}:${mins}`;
415
- if (opts.showSeconds) {
416
- timeStr += `:${now.getSeconds().toString().padStart(2, "0")}`;
417
- }
418
- timeStr += suffix;
419
-
420
- return { content: withIcon(icons.time, timeStr), visible: true };
421
- },
422
- };
423
-
424
- const sessionSegment: StatusLineSegment = {
425
- id: "session",
426
- render(ctx) {
427
- const icons = getIcons();
428
- const sessionId = ctx.sessionId;
429
- const display = sessionId?.slice(0, 8) || "new";
430
-
431
- return { content: withIcon(icons.session, display), visible: true };
432
- },
433
- };
434
-
435
- const hostnameSegment: StatusLineSegment = {
436
- id: "hostname",
437
- render() {
438
- const icons = getIcons();
439
- const name = osHostname().split(".")[0];
440
- return { content: withIcon(icons.host, name), visible: true };
441
- },
442
- };
443
-
444
- const cacheReadSegment: StatusLineSegment = {
445
- id: "cache_read",
446
- render(ctx) {
447
- const icons = getIcons();
448
- const { cacheRead, input } = ctx.usageStats;
449
- if (!cacheRead) return { content: "", visible: false };
450
-
451
- const format = ctx.options.cache_read?.format ?? "tokens";
452
- const hitRate = input + cacheRead > 0
453
- ? ((cacheRead / (input + cacheRead)) * 100).toFixed(0)
454
- : "0";
455
-
456
- let content: string;
457
- if (format === "percent") {
458
- content = [icons.cache, `${hitRate}%`].filter(Boolean).join(" ");
459
- } else {
460
- const tokens = [icons.cache, icons.input, formatTokens(cacheRead)].filter(Boolean).join(" ");
461
- content = format === "both" ? `${tokens} (${hitRate}%)` : tokens;
462
- }
463
- return { content: color(ctx, "tokens", content), visible: true };
464
- },
465
- };
466
-
467
- const cacheWriteSegment: StatusLineSegment = {
468
- id: "cache_write",
469
- render(ctx) {
470
- const icons = getIcons();
471
- const { cacheWrite } = ctx.usageStats;
472
- if (!cacheWrite) return { content: "", visible: false };
473
-
474
- const parts = [icons.cache, icons.output, formatTokens(cacheWrite)].filter(Boolean);
475
- const content = parts.join(" ");
476
- return { content: color(ctx, "tokens", content), visible: true };
477
- },
478
- };
479
-
480
- const extensionStatusesSegment: StatusLineSegment = {
481
- id: "extension_statuses",
482
- render(ctx) {
483
- const statuses = ctx.extensionStatuses;
484
- if (!statuses || statuses.size === 0) return { content: "", visible: false };
485
-
486
- // Join compact statuses with a separator
487
- // Skip: empty strings, notification-style ("[...") shown above editor,
488
- // and strings that are only ANSI codes with no visible text.
489
- // Also skip statuses explicitly elevated into dedicated custom segments.
490
- const parts: string[] = [];
491
- for (const [statusKey, value] of statuses.entries()) {
492
- if (ctx.hiddenExtensionStatusKeys.has(statusKey)) continue;
493
- const normalized = value ? normalizeCompactExtensionStatus(value) : null;
494
- if (normalized) {
495
- parts.push(normalized);
496
- }
497
- }
498
-
499
- if (parts.length === 0) return { content: "", visible: false };
500
-
501
- // Statuses already have their own styling applied by the extensions
502
- const content = parts.join(` ${SEP_DOT} `);
503
- return { content, visible: true };
504
- },
505
- };
506
-
507
- // ═══════════════════════════════════════════════════════════════════════════
508
- // Segment Registry
509
- // ═══════════════════════════════════════════════════════════════════════════
510
-
511
- export const SEGMENTS: Record<BuiltinStatusLineSegmentId, StatusLineSegment> = {
512
- model: modelSegment,
513
- shell_mode: shellModeSegment,
514
- path: pathSegment,
515
- git: gitSegment,
516
- thinking: thinkingSegment,
517
- subagents: subagentsSegment,
518
- queue: queueSegment,
519
- token_in: tokenInSegment,
520
- token_out: tokenOutSegment,
521
- token_total: tokenTotalSegment,
522
- cost: costSegment,
523
- context_pct: contextPctSegment,
524
- context_total: contextTotalSegment,
525
- time_spent: timeSpentSegment,
526
- time: timeSegment,
527
- session: sessionSegment,
528
- hostname: hostnameSegment,
529
- cache_read: cacheReadSegment,
530
- cache_write: cacheWriteSegment,
531
- extension_statuses: extensionStatusesSegment,
532
- };
533
-
534
- function renderCustomSegment(id: `custom:${string}`, ctx: SegmentContext): RenderedSegment {
535
- const customItemId = id.slice("custom:".length);
536
- const custom = ctx.customItemsById.get(customItemId);
537
- if (!custom) return { content: "", visible: false };
538
-
539
- const rawStatus = ctx.extensionStatuses.get(custom.statusKey);
540
- const normalizedStatus = rawStatus ? normalizeExtensionStatusValue(rawStatus) : null;
541
- if (!normalizedStatus) {
542
- return custom.hideWhenMissing ? { content: "", visible: false } : { content: custom.prefix ?? custom.id, visible: true };
543
- }
544
-
545
- let content = normalizedStatus;
546
- if (custom.prefix) {
547
- content = `${custom.prefix}${SEP_DOT}${content}`;
548
- }
549
- if (custom.color) {
550
- content = applyColor(ctx.theme, custom.color, content);
551
- }
552
-
553
- return { content, visible: true };
554
- }
555
-
556
- function isCustomSegmentId(id: StatusLineSegmentId): id is `custom:${string}` {
557
- return id.startsWith("custom:");
558
- }
559
-
560
- export function renderSegment(id: StatusLineSegmentId, ctx: SegmentContext): RenderedSegment {
561
- if (isCustomSegmentId(id)) {
562
- return renderCustomSegment(id, ctx);
563
- }
564
-
565
- const segment = SEGMENTS[id];
566
- if (!segment) {
567
- return { content: "", visible: false };
568
- }
569
- return segment.render(ctx);
570
- }
@@ -1,57 +0,0 @@
1
- import type { SeparatorDef, StatusLineSeparatorStyle } from "./types.ts";
2
- import { getSeparatorChars } from "./icons.ts";
3
-
4
- export function getSeparator(style: StatusLineSeparatorStyle): SeparatorDef {
5
- const chars = getSeparatorChars();
6
-
7
- switch (style) {
8
- case "powerline":
9
- return {
10
- left: chars.powerlineLeft,
11
- right: chars.powerlineRight,
12
- endCaps: {
13
- left: chars.powerlineRight,
14
- right: chars.powerlineLeft,
15
- useBgAsFg: true,
16
- },
17
- };
18
-
19
- case "powerline-thin":
20
- return {
21
- left: chars.powerlineThinLeft,
22
- right: chars.powerlineThinRight,
23
- endCaps: {
24
- left: chars.powerlineRight,
25
- right: chars.powerlineLeft,
26
- useBgAsFg: true,
27
- },
28
- };
29
-
30
- case "slash":
31
- return { left: ` ${chars.slash} `, right: ` ${chars.slash} ` };
32
-
33
- case "pipe":
34
- return { left: ` ${chars.pipe} `, right: ` ${chars.pipe} ` };
35
-
36
- case "block":
37
- return { left: chars.block, right: chars.block };
38
-
39
- case "none":
40
- return { left: chars.space, right: chars.space };
41
-
42
- case "ascii":
43
- return { left: chars.asciiLeft, right: chars.asciiRight };
44
-
45
- case "dot":
46
- return { left: chars.dot, right: chars.dot };
47
-
48
- case "chevron":
49
- return { left: "›", right: "‹" };
50
-
51
- case "star":
52
- return { left: "✦", right: "✦" };
53
-
54
- default:
55
- return getSeparator("powerline-thin");
56
- }
57
- }
@@ -1,44 +0,0 @@
1
- import type { UsageStats } from "./types.ts";
2
-
3
- type RecordLike = Record<string, unknown>;
4
-
5
- function isRecord(value: unknown): value is RecordLike {
6
- return value !== null && typeof value === "object" && !Array.isArray(value);
7
- }
8
-
9
- function finiteNumber(value: unknown): value is number {
10
- return typeof value === "number" && Number.isFinite(value);
11
- }
12
-
13
- /**
14
- * Extract the aggregate usage reported by a completed foreground subagent run.
15
- * Child agents use separate sessions, so their assistant messages are not in
16
- * the parent session branch. The parent records this aggregate on its
17
- * `subagent` tool result instead.
18
- */
19
- function readUsage(value: unknown): UsageStats | null {
20
- if (!isRecord(value)) return null;
21
-
22
- const { input, output, cacheRead, cacheWrite, cost } = value;
23
- if (![input, output, cacheRead, cacheWrite, cost].every(finiteNumber)) return null;
24
-
25
- return { input, output, cacheRead, cacheWrite, cost } as UsageStats;
26
- }
27
-
28
- export function readSubagentToolResultUsage(message: unknown): UsageStats | null {
29
- if (!isRecord(message) || message.role !== "toolResult" || message.toolName !== "subagent") {
30
- return null;
31
- }
32
-
33
- const details = isRecord(message.details) ? message.details : null;
34
- return readUsage(details?.totalChildUsage);
35
- }
36
-
37
- /** Read usage emitted when an asynchronous subagent run completes. */
38
- export function readAsyncSubagentUsage(event: unknown): { runId: string; sessionId: string; usage: UsageStats } | null {
39
- if (!isRecord(event) || typeof event.sessionId !== "string") return null;
40
-
41
- const runId = typeof event.runId === "string" ? event.runId : typeof event.id === "string" ? event.id : null;
42
- const usage = readUsage(event.totalChildUsage);
43
- return runId && usage ? { runId, sessionId: event.sessionId, usage } : null;
44
- }