@selesai/code 0.9.15 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/CHANGELOG.md +27 -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/modes/interactive/components/settings-selector.d.ts +1 -1
  118. package/dist/modes/interactive/interactive-mode.js +1 -1
  119. package/dist/skills/design-references/SKILL.md +31 -0
  120. package/dist/skills/design-references/references/clickhouse/DESIGN.md +544 -0
  121. package/dist/themes/adventure.json +84 -0
  122. package/dist/themes/adwaita-dark.json +84 -0
  123. package/dist/themes/arcoiris.json +84 -0
  124. package/dist/themes/arthur.json +84 -0
  125. package/dist/themes/atom.json +84 -0
  126. package/dist/themes/aura.json +84 -0
  127. package/dist/themes/black-metal-bathory.json +84 -0
  128. package/dist/themes/black-metal-burzum.json +84 -0
  129. package/dist/themes/black-metal-khold.json +84 -0
  130. package/dist/themes/box.json +84 -0
  131. package/dist/themes/brogrammer.json +84 -0
  132. package/dist/themes/carbonfox.json +84 -0
  133. package/dist/themes/catppuccin-mocha.json +84 -0
  134. package/dist/themes/citruszest.json +84 -0
  135. package/dist/themes/cursor-dark.json +84 -0
  136. package/dist/themes/cutie-pro.json +84 -0
  137. package/dist/themes/dark-modern.json +84 -0
  138. package/dist/themes/dark-pastel.json +84 -0
  139. package/dist/themes/dimmed-monokai.json +84 -0
  140. package/dist/themes/doom-peacock.json +84 -0
  141. package/dist/themes/dracula-plus.json +84 -0
  142. package/dist/themes/dracula.json +80 -0
  143. package/dist/themes/earthsong.json +84 -0
  144. package/dist/themes/everforest-dark-hard.json +84 -0
  145. package/dist/themes/everforest-dark.json +80 -0
  146. package/dist/themes/fahrenheit.json +84 -0
  147. package/dist/themes/flatland.json +84 -0
  148. package/dist/themes/flexoki-dark.json +84 -0
  149. package/dist/themes/front-end-delight.json +84 -0
  150. package/dist/themes/fun-forrest.json +84 -0
  151. package/dist/themes/galizur.json +84 -0
  152. package/dist/themes/github-dark-colorblind.json +84 -0
  153. package/dist/themes/github-dark-high-contrast.json +84 -0
  154. package/dist/themes/glacier.json +84 -0
  155. package/dist/themes/gruber-darker.json +84 -0
  156. package/dist/themes/gruvbox-dark-hard.json +84 -0
  157. package/dist/themes/gruvbox-dark.json +84 -0
  158. package/dist/themes/gruvbox-material.json +84 -0
  159. package/dist/themes/guezwhoz.json +84 -0
  160. package/dist/themes/hacktober.json +84 -0
  161. package/dist/themes/hardcore.json +84 -0
  162. package/dist/themes/havn-skumring.json +84 -0
  163. package/dist/themes/ic-orange-ppl.json +84 -0
  164. package/dist/themes/iterm2-smoooooth.json +84 -0
  165. package/dist/themes/iterm2-tango-dark.json +84 -0
  166. package/dist/themes/japanesque.json +84 -0
  167. package/dist/themes/jellybeans.json +84 -0
  168. package/dist/themes/kanagawa-wave.json +84 -0
  169. package/dist/themes/kurokula.json +84 -0
  170. package/dist/themes/later-this-evening.json +84 -0
  171. package/dist/themes/lovelace.json +84 -0
  172. package/dist/themes/material-darker.json +84 -0
  173. package/dist/themes/matte-black.json +84 -0
  174. package/dist/themes/mellow.json +84 -0
  175. package/dist/themes/miasma.json +84 -0
  176. package/dist/themes/nord.json +80 -0
  177. package/dist/themes/nvim-dark.json +84 -0
  178. package/dist/themes/one-dark.json +80 -0
  179. package/dist/themes/popping-and-locking.json +84 -0
  180. package/dist/themes/rose-pine.json +80 -0
  181. package/dist/themes/sea-shells.json +84 -0
  182. package/dist/themes/sleepy-hollow.json +84 -0
  183. package/dist/themes/smyck.json +84 -0
  184. package/dist/themes/solarized-dark.json +80 -0
  185. package/dist/themes/tokyo-night.json +80 -0
  186. package/dist/themes/tomorrow-night-bright.json +84 -0
  187. package/dist/themes/tomorrow-night-burns.json +84 -0
  188. package/dist/themes/tomorrow-night.json +84 -0
  189. package/dist/themes/twilight.json +84 -0
  190. package/dist/themes/vague.json +84 -0
  191. package/dist/themes/vesper.json +84 -0
  192. package/dist/themes/xcode-dark-hc.json +84 -0
  193. package/docs/custom-provider.md +0 -1
  194. package/package.json +3 -3
  195. package/dist/extensions/auto-model/auto-model.test.ts +0 -18
  196. package/dist/extensions/auto-model/classifier.ts +0 -23
  197. package/dist/extensions/auto-model/config.ts +0 -51
  198. package/dist/extensions/auto-model/index.ts +0 -22
  199. package/dist/extensions/auto-model/lifecycle.test.ts +0 -24
  200. package/dist/extensions/pi-powerline-footer/.github/workflows/test.yml +0 -27
  201. package/dist/extensions/pi-powerline-footer/CHANGELOG.md +0 -687
  202. package/dist/extensions/pi-powerline-footer/CONTRIBUTING.md +0 -16
  203. package/dist/extensions/pi-powerline-footer/README.md +0 -513
  204. package/dist/extensions/pi-powerline-footer/banner.png +0 -0
  205. package/dist/extensions/pi-powerline-footer/bash-mode/completion.ts +0 -586
  206. package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +0 -583
  207. package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +0 -204
  208. package/dist/extensions/pi-powerline-footer/bash-mode/shell-session.ts +0 -286
  209. package/dist/extensions/pi-powerline-footer/bash-mode/transcript.ts +0 -108
  210. package/dist/extensions/pi-powerline-footer/bash-mode/types.ts +0 -60
  211. package/dist/extensions/pi-powerline-footer/cd-command.ts +0 -190
  212. package/dist/extensions/pi-powerline-footer/colors.ts +0 -70
  213. package/dist/extensions/pi-powerline-footer/context-usage.ts +0 -156
  214. package/dist/extensions/pi-powerline-footer/currency-rates.ts +0 -142
  215. package/dist/extensions/pi-powerline-footer/editor-composition.ts +0 -32
  216. package/dist/extensions/pi-powerline-footer/editor-performance.ts +0 -99
  217. package/dist/extensions/pi-powerline-footer/git-status.ts +0 -329
  218. package/dist/extensions/pi-powerline-footer/guide.ts +0 -220
  219. package/dist/extensions/pi-powerline-footer/icons.ts +0 -190
  220. package/dist/extensions/pi-powerline-footer/index.ts +0 -3428
  221. package/dist/extensions/pi-powerline-footer/lifecycle.ts +0 -10
  222. package/dist/extensions/pi-powerline-footer/package.json +0 -49
  223. package/dist/extensions/pi-powerline-footer/paths.ts +0 -22
  224. package/dist/extensions/pi-powerline-footer/powerline-config.ts +0 -446
  225. package/dist/extensions/pi-powerline-footer/presets.ts +0 -102
  226. package/dist/extensions/pi-powerline-footer/queue/store.ts +0 -313
  227. package/dist/extensions/pi-powerline-footer/queue/types.ts +0 -53
  228. package/dist/extensions/pi-powerline-footer/render-scheduler.ts +0 -29
  229. package/dist/extensions/pi-powerline-footer/segments.ts +0 -570
  230. package/dist/extensions/pi-powerline-footer/separators.ts +0 -57
  231. package/dist/extensions/pi-powerline-footer/session-usage.ts +0 -44
  232. package/dist/extensions/pi-powerline-footer/shortcuts.ts +0 -61
  233. package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +0 -1905
  234. package/dist/extensions/pi-powerline-footer/tests/cd-command.test.ts +0 -144
  235. package/dist/extensions/pi-powerline-footer/tests/context-usage.test.ts +0 -156
  236. package/dist/extensions/pi-powerline-footer/tests/custom-items.test.ts +0 -344
  237. package/dist/extensions/pi-powerline-footer/tests/editor-composition.test.ts +0 -48
  238. package/dist/extensions/pi-powerline-footer/tests/editor-performance.test.ts +0 -43
  239. package/dist/extensions/pi-powerline-footer/tests/editor-responsiveness.test.ts +0 -136
  240. package/dist/extensions/pi-powerline-footer/tests/fixed-editor-border.test.ts +0 -39
  241. package/dist/extensions/pi-powerline-footer/tests/git-optional-locks.test.ts +0 -64
  242. package/dist/extensions/pi-powerline-footer/tests/git-status.test.ts +0 -92
  243. package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +0 -129
  244. package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +0 -116
  245. package/dist/extensions/pi-powerline-footer/tests/mode-aware-trigger.test.ts +0 -79
  246. package/dist/extensions/pi-powerline-footer/tests/paths.test.ts +0 -63
  247. package/dist/extensions/pi-powerline-footer/tests/queue-store.test.ts +0 -65
  248. package/dist/extensions/pi-powerline-footer/tests/quit-cursor.test.ts +0 -14
  249. package/dist/extensions/pi-powerline-footer/tests/remaining-regressions.test.ts +0 -173
  250. package/dist/extensions/pi-powerline-footer/tests/session-usage.test.ts +0 -47
  251. package/dist/extensions/pi-powerline-footer/tests/stash-shortcut.test.ts +0 -37
  252. package/dist/extensions/pi-powerline-footer/tests/thinking-segment.test.ts +0 -76
  253. package/dist/extensions/pi-powerline-footer/tests/token-stats.test.ts +0 -249
  254. package/dist/extensions/pi-powerline-footer/tests/tps.test.ts +0 -39
  255. package/dist/extensions/pi-powerline-footer/tests/usage-display.test.ts +0 -237
  256. package/dist/extensions/pi-powerline-footer/tests/welcome.test.ts +0 -199
  257. package/dist/extensions/pi-powerline-footer/tests/working-vibes.test.ts +0 -317
  258. package/dist/extensions/pi-powerline-footer/theme.example.json +0 -24
  259. package/dist/extensions/pi-powerline-footer/theme.json +0 -12
  260. package/dist/extensions/pi-powerline-footer/theme.ts +0 -241
  261. package/dist/extensions/pi-powerline-footer/token-stats.ts +0 -304
  262. package/dist/extensions/pi-powerline-footer/tsconfig.json +0 -13
  263. package/dist/extensions/pi-powerline-footer/types.ts +0 -235
  264. package/dist/extensions/pi-powerline-footer/welcome-dismiss.ts +0 -34
  265. package/dist/extensions/pi-powerline-footer/welcome.ts +0 -709
  266. package/dist/extensions/pi-powerline-footer/working-vibes.ts +0 -721
  267. package/dist/extensions/preview-tools-disabled.test.ts +0 -37
  268. package/dist/extensions/preview-tools-disabled.ts +0 -11
  269. 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
- }