@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,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
- }