@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,144 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import { existsSync, mkdirSync, writeFileSync, mkdtempSync, readFileSync, realpathSync } from "node:fs";
4
- import { tmpdir } from "node:os";
5
- import { join } from "node:path";
6
- import { SessionManager } from "@selesai/code";
7
- import { getCdArgumentCompletions, resolveCdTarget, runCdCommand } from "../cd-command.ts";
8
-
9
- const cdCommandSource = readFileSync(new URL("../cd-command.ts", import.meta.url), "utf-8");
10
- const indexSource = readFileSync(new URL("../index.ts", import.meta.url), "utf-8");
11
-
12
- test("resolveCdTarget supports current, relative, home, and invalid directories", () => {
13
- const root = mkdtempSync(join(tmpdir(), "powerline-cd-root-"));
14
- const home = mkdtempSync(join(tmpdir(), "powerline-cd-home-"));
15
- const child = join(root, "child");
16
- const homeProject = join(home, "project");
17
- mkdirSync(child);
18
- mkdirSync(homeProject);
19
- writeFileSync(join(root, "file.txt"), "not a directory");
20
-
21
- assert.equal(resolveCdTarget("", root, home), null);
22
- assert.deepEqual(resolveCdTarget("child", root, home), { path: child });
23
- assert.deepEqual(resolveCdTarget("~/project", root, home), { path: homeProject });
24
- assert.deepEqual(resolveCdTarget("My\\ Folder", root, home), { error: `Directory does not exist: ${join(root, "My Folder")}` });
25
- assert.match(resolveCdTarget("missing", root, home)?.error ?? "", /Directory does not exist:/);
26
- assert.match(resolveCdTarget("file.txt", root, home)?.error ?? "", /Not a directory:/);
27
- });
28
-
29
- test("cd argument completions suggest directories and quick picks", () => {
30
- const root = mkdtempSync(join(tmpdir(), "powerline-cd-complete-root-"));
31
- const home = mkdtempSync(join(tmpdir(), "powerline-cd-complete-home-"));
32
- mkdirSync(join(root, "child"));
33
- mkdirSync(join(root, "My Folder"));
34
- writeFileSync(join(root, "file.txt"), "not a directory");
35
- mkdirSync(join(home, "project"));
36
-
37
- const rootCompletions = getCdArgumentCompletions("", root, home);
38
- assert.ok(rootCompletions.some((item) => item.value === "../" && item.label === "../"));
39
- assert.ok(rootCompletions.some((item) => item.value === "~/" && item.label === "~/"));
40
- assert.ok(rootCompletions.some((item) => item.value === "child/" && item.label === "child/"));
41
- assert.ok(rootCompletions.some((item) => item.value === "My\\ Folder/" && item.label === "My Folder/"));
42
- assert.ok(!rootCompletions.some((item) => item.label === "file.txt"));
43
-
44
- if (process.platform !== "win32") {
45
- assert.deepEqual(
46
- getCdArgumentCompletions("~/p", root, home).map((item) => item.value),
47
- ["~/project/"],
48
- );
49
- }
50
- });
51
-
52
- test("cd command cleans up the forked session when switching is cancelled", async () => {
53
- const originalCwd = process.cwd();
54
- const root = mkdtempSync(join(tmpdir(), "powerline-cd-command-root-"));
55
- const target = join(root, "target");
56
- const sessionDir = join(root, "sessions");
57
- mkdirSync(target);
58
-
59
- const session = SessionManager.create(root, sessionDir);
60
- session.appendMessage({ role: "user", content: "hello", timestamp: Date.now() });
61
- session.appendMessage({ role: "assistant", content: "hi", timestamp: Date.now(), usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } } });
62
-
63
- let switchedPath: string | undefined;
64
- const notifications: Array<{ message: string; type?: string }> = [];
65
- const ctx = {
66
- cwd: root,
67
- sessionManager: {
68
- getSessionFile: () => session.getSessionFile(),
69
- },
70
- ui: {
71
- notify(message: string, type?: string) {
72
- notifications.push({ message, type });
73
- },
74
- },
75
- waitForIdle: async () => {},
76
- switchSession: async (path: string) => {
77
- switchedPath = path;
78
- return { cancelled: true };
79
- },
80
- } as any;
81
-
82
- try {
83
- await runCdCommand("target", ctx);
84
- } finally {
85
- process.chdir(originalCwd);
86
- }
87
-
88
- assert.equal(process.cwd(), originalCwd);
89
- assert.ok(switchedPath, "switchSession should receive the forked session path");
90
- assert.equal(existsSync(switchedPath!), false);
91
- assert.deepEqual(notifications.at(-1), { message: "Directory change cancelled", type: "warning" });
92
- });
93
-
94
- test("cd command preserves the target session after a post-apply switch error", async () => {
95
- const originalCwd = process.cwd();
96
- const root = mkdtempSync(join(tmpdir(), "powerline-cd-post-apply-root-"));
97
- const target = join(root, "target");
98
- const sessionDir = join(root, "sessions");
99
- mkdirSync(target);
100
-
101
- const session = SessionManager.create(root, sessionDir);
102
- session.appendMessage({ role: "user", content: "hello", timestamp: Date.now() });
103
- session.appendMessage({ role: "assistant", content: "hi", timestamp: Date.now(), usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } } });
104
-
105
- let switchedPath: string | undefined;
106
- const ctx = {
107
- cwd: root,
108
- sessionManager: {
109
- getSessionFile: () => session.getSessionFile(),
110
- },
111
- ui: {
112
- notify() {},
113
- },
114
- waitForIdle: async () => {},
115
- switchSession: async (path: string, options: { withSession?: (ctx: any) => Promise<void> }) => {
116
- switchedPath = path;
117
- await options.withSession?.({
118
- cwd: target,
119
- ui: {
120
- notify() {},
121
- setTitle() {},
122
- },
123
- });
124
- throw new Error("post-apply failure");
125
- },
126
- } as any;
127
-
128
- try {
129
- await runCdCommand("target", ctx);
130
- assert.equal(realpathSync(process.cwd()), realpathSync(target));
131
- assert.ok(switchedPath, "switchSession should receive the forked session path");
132
- assert.equal(existsSync(switchedPath!), true);
133
- } finally {
134
- process.chdir(originalCwd);
135
- }
136
- });
137
-
138
- test("cd command uses session replacement instead of private cwd mutation", () => {
139
- assert.match(indexSource, /registerCdCommand\(pi, \(\) => currentCtx\?\.cwd \?\? process\.cwd\(\)\)/);
140
- assert.match(cdCommandSource, /SessionManager\.forkFrom\(sessionFile, resolved\.path\)/);
141
- assert.match(cdCommandSource, /ctx\.switchSession\(nextSessionFile!/);
142
- assert.match(cdCommandSource, /process\.chdir\(nextCtx\.cwd\)/);
143
- assert.doesNotMatch(cdCommandSource, /ctx\.cwd\s*=/);
144
- });
@@ -1,156 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import { CoreContextUsageCache, estimateInitialContextTokens, estimateUnknownContextUsage, readCoreContextUsage, resolveDisplayContextUsage } from "../context-usage.ts";
4
-
5
- test("readCoreContextUsage returns Pi context estimates for branch summaries", () => {
6
- const usage = readCoreContextUsage({
7
- getContextUsage() {
8
- return { tokens: 1250, contextWindow: 5000, percent: 25 };
9
- },
10
- });
11
-
12
- assert.deepEqual(usage, {
13
- contextTokens: 1250,
14
- contextWindow: 5000,
15
- contextPercent: 25,
16
- });
17
- });
18
-
19
- test("readCoreContextUsage computes percent when Pi returns only token totals", () => {
20
- const usage = readCoreContextUsage({
21
- getContextUsage() {
22
- return { tokens: 1000, contextWindow: 4000 };
23
- },
24
- });
25
-
26
- assert.deepEqual(usage, {
27
- contextTokens: 1000,
28
- contextWindow: 4000,
29
- contextPercent: 25,
30
- });
31
- });
32
-
33
- test("readCoreContextUsage preserves Pi's post-compaction unknown state", () => {
34
- const usage = readCoreContextUsage({
35
- getContextUsage() {
36
- return { tokens: null, contextWindow: 5000, percent: null };
37
- },
38
- });
39
-
40
- assert.deepEqual(usage, {
41
- contextTokens: null,
42
- contextWindow: 5000,
43
- contextPercent: null,
44
- });
45
- });
46
-
47
- test("readCoreContextUsage ignores unknown or unusable estimates", () => {
48
- assert.equal(readCoreContextUsage({}), null);
49
- assert.equal(readCoreContextUsage({ getContextUsage: () => undefined }), null);
50
- assert.equal(readCoreContextUsage({ getContextUsage: () => ({ tokens: undefined, contextWindow: 5000, percent: null }) }), null);
51
- assert.equal(readCoreContextUsage({ getContextUsage: () => ({ tokens: 100, contextWindow: 0, percent: 0 }) }), null);
52
- });
53
-
54
- test("core context usage cache reuses a leaf and supports explicit invalidation", () => {
55
- const cache = new CoreContextUsageCache();
56
- let leafId = "leaf-1";
57
- let tokens = 100;
58
- let reads = 0;
59
- const ctx = {
60
- sessionManager: { getLeafId: () => leafId },
61
- getContextUsage() {
62
- reads += 1;
63
- return { tokens, contextWindow: 1000, percent: tokens / 10 };
64
- },
65
- };
66
-
67
- assert.equal(cache.get(ctx)?.contextTokens, 100);
68
- tokens = 200;
69
- assert.equal(cache.get(ctx)?.contextTokens, 100);
70
- assert.equal(reads, 1);
71
-
72
- cache.reset();
73
- assert.equal(cache.get(ctx)?.contextTokens, 200);
74
- leafId = "leaf-2";
75
- tokens = 300;
76
- assert.equal(cache.get(ctx)?.contextTokens, 300);
77
- assert.equal(reads, 3);
78
- });
79
-
80
- test("resolveDisplayContextUsage preserves unknown core usage over assistant fallback usage", () => {
81
- assert.deepEqual(resolveDisplayContextUsage({
82
- coreContextUsage: { contextTokens: null, contextWindow: 5000, contextPercent: null },
83
- unknownCoreFallback: null,
84
- fallbackContextTokens: 4000,
85
- fallbackContextWindow: 5000,
86
- }), {
87
- contextTokens: null,
88
- contextWindow: 5000,
89
- contextPercent: null,
90
- });
91
- });
92
-
93
- test("resolveDisplayContextUsage uses the approximate estimate for unknown core usage", () => {
94
- const reloadEstimate = { contextTokens: 1000, contextWindow: 5000, contextPercent: 20 };
95
- assert.equal(resolveDisplayContextUsage({
96
- coreContextUsage: { contextTokens: null, contextWindow: 5000, contextPercent: null },
97
- unknownCoreFallback: reloadEstimate,
98
- fallbackContextTokens: 4000,
99
- fallbackContextWindow: 5000,
100
- }), reloadEstimate);
101
- });
102
-
103
- test("resolveDisplayContextUsage computes assistant fallback usage when Pi has no current estimate", () => {
104
- assert.deepEqual(resolveDisplayContextUsage({
105
- coreContextUsage: null,
106
- unknownCoreFallback: null,
107
- fallbackContextTokens: 1000,
108
- fallbackContextWindow: 4000,
109
- }), {
110
- contextTokens: 1000,
111
- contextWindow: 4000,
112
- contextPercent: 25,
113
- });
114
- });
115
-
116
- test("estimateUnknownContextUsage estimates the active compacted context", () => {
117
- const usage = estimateUnknownContextUsage({
118
- getContextUsage: () => ({ tokens: null, contextWindow: 100, percent: null }),
119
- getSystemPrompt: () => "12345678",
120
- sessionManager: {
121
- buildContextEntries: () => [
122
- { type: "compaction", summary: "12345678", tokensBefore: 90, timestamp: "2026-08-08T00:00:00.000Z" },
123
- { type: "message", message: { role: "user", content: "1234", timestamp: 0 } },
124
- ],
125
- },
126
- });
127
-
128
- assert.deepEqual(usage, {
129
- contextTokens: 5,
130
- contextWindow: 100,
131
- contextPercent: 5,
132
- });
133
- });
134
-
135
- test("estimateUnknownContextUsage skips sessions with known core usage", () => {
136
- let entryReads = 0;
137
- assert.equal(estimateUnknownContextUsage({
138
- getContextUsage: () => ({ tokens: 25, contextWindow: 100, percent: 25 }),
139
- getSystemPrompt: () => "12345678",
140
- sessionManager: {
141
- buildContextEntries() {
142
- entryReads += 1;
143
- return [];
144
- },
145
- },
146
- }), null);
147
- assert.equal(entryReads, 0);
148
- });
149
-
150
- test("estimateInitialContextTokens uses Pi's conservative character estimate", () => {
151
- assert.equal(estimateInitialContextTokens({}), null);
152
- assert.equal(estimateInitialContextTokens({ getSystemPrompt: () => "" }), null);
153
- assert.equal(estimateInitialContextTokens({ getSystemPrompt: () => " " }), null);
154
- assert.equal(estimateInitialContextTokens({ getSystemPrompt: () => "1234" }), 1);
155
- assert.equal(estimateInitialContextTokens({ getSystemPrompt: () => "12345" }), 2);
156
- });
@@ -1,344 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import { collectHiddenExtensionStatusKeys, getNotificationExtensionStatuses, normalizeExtensionStatusValue, parsePowerlineConfig, mergeSegmentOptions, mergeSegmentsWithCustomItems, nextPowerlineSettingWithOptions, nextPowerlineSettingWithPreset, normalizeCompactExtensionStatus } from "../powerline-config.ts";
4
- import { getSeparator } from "../separators.ts";
5
- import { PRESETS } from "../presets.ts";
6
-
7
- test("fixed custom preset is removed in favor of powerline.layout", () => {
8
- assert.equal("custom" in PRESETS, false);
9
- });
10
-
11
- test("parsePowerlineConfig supports object config with custom items", () => {
12
- const config = parsePowerlineConfig(
13
- {
14
- preset: "compact",
15
- customItems: [
16
- { id: "ci", statusKey: "ci-status", position: "right", prefix: "CI" },
17
- { id: "review", position: "secondary", hideWhenMissing: false },
18
- ],
19
- },
20
- ["default", "compact"],
21
- );
22
-
23
- assert.equal(config.preset, "compact");
24
- assert.equal(config.customItems.length, 2);
25
- assert.equal(config.customItems[0].id, "ci");
26
- assert.equal(config.customItems[0].statusKey, "ci-status");
27
- assert.equal(config.customItems[1].statusKey, "review");
28
- assert.equal(config.customItems[1].hideWhenMissing, false);
29
- assert.deepEqual(config.disabledSegments, []);
30
- assert.deepEqual(config.invalidDisabledSegments, []);
31
- assert.equal(config.layout, null);
32
- assert.deepEqual(config.invalidLayoutSegments, []);
33
- assert.equal(config.separator, null);
34
- assert.equal(config.placement, "above");
35
- assert.equal(config.invalidPlacement, null);
36
- assert.equal(config.welcome, true);
37
- assert.equal(config.stashSharpSShortcut, false);
38
- });
39
-
40
- test("parsePowerlineConfig supports disabled segments", () => {
41
- const config = parsePowerlineConfig(
42
- {
43
- preset: "default",
44
- customItems: [{ id: "ci" }],
45
- disabledSegments: [
46
- "queue",
47
- "cost",
48
- " extension_statuses ",
49
- "custom:ci",
50
- "cost",
51
- "unknown",
52
- "custom:missing",
53
- 123,
54
- ],
55
- },
56
- ["default", "compact"],
57
- );
58
-
59
- assert.deepEqual(config.disabledSegments, ["queue", "cost", "extension_statuses", "custom:ci"]);
60
- assert.deepEqual(config.invalidDisabledSegments, ["unknown", "custom:missing", "123"]);
61
- });
62
-
63
- test("parsePowerlineConfig supports partial explicit layout rows", () => {
64
- const config = parsePowerlineConfig(
65
- {
66
- preset: "default",
67
- customItems: [{ id: "ci" }],
68
- layout: {
69
- left: ["model", "custom:ci", "model", "unknown", 123],
70
- right: ["model", "cost"],
71
- secondary: [],
72
- },
73
- },
74
- ["default", "compact"],
75
- );
76
-
77
- assert.deepEqual(config.layout, {
78
- left: ["model", "custom:ci"],
79
- right: ["cost"],
80
- secondary: [],
81
- });
82
- assert.deepEqual(config.invalidLayoutSegments, ["left:unknown", "left:123", "right:model"]);
83
- });
84
-
85
- test("parsePowerlineConfig preserves reporter layout groups", () => {
86
- const config = parsePowerlineConfig(
87
- {
88
- preset: "default",
89
- layout: {
90
- left: ["model"],
91
- right: ["path"],
92
- secondary: ["thinking"],
93
- },
94
- },
95
- ["default", "compact"],
96
- );
97
-
98
- assert.deepEqual(config.layout, {
99
- left: ["model"],
100
- right: ["path"],
101
- secondary: ["thinking"],
102
- });
103
- assert.deepEqual(config.invalidLayoutSegments, []);
104
- assert.deepEqual(
105
- mergeSegmentsWithCustomItems(PRESETS.default, config.customItems, {
106
- layout: config.layout,
107
- disabledSegments: config.disabledSegments,
108
- }),
109
- {
110
- leftSegments: ["model"],
111
- rightSegments: ["path"],
112
- secondarySegments: ["thinking"],
113
- },
114
- );
115
- });
116
-
117
- test("parsePowerlineConfig supports separator overrides", () => {
118
- const config = parsePowerlineConfig(
119
- { preset: "default", separator: " chevron " },
120
- ["default", "compact"],
121
- );
122
- const invalid = parsePowerlineConfig(
123
- { preset: "default", separator: "sparkle" },
124
- ["default", "compact"],
125
- );
126
-
127
- assert.equal(config.separator, "chevron");
128
- assert.equal(invalid.separator, null);
129
- });
130
-
131
- test("configured separators resolve independently of presets", () => {
132
- const presetSeparator = getSeparator(PRESETS.default.separator).left;
133
- const configuredSeparator = getSeparator("chevron").left;
134
-
135
- assert.notEqual(configuredSeparator, presetSeparator);
136
- assert.equal(configuredSeparator, "›");
137
- });
138
-
139
- test("parsePowerlineConfig validates primary powerline placement", () => {
140
- const below = parsePowerlineConfig(
141
- { preset: "compact", placement: "below" },
142
- ["default", "compact"],
143
- );
144
- const invalid = parsePowerlineConfig(
145
- { preset: "compact", placement: "sideways" },
146
- ["default", "compact"],
147
- );
148
-
149
- assert.equal(below.placement, "below");
150
- assert.equal(below.invalidPlacement, null);
151
- assert.equal(invalid.placement, "above");
152
- assert.equal(invalid.invalidPlacement, "sideways");
153
- });
154
-
155
-
156
-
157
-
158
- test("parsePowerlineConfig supports welcome and legacy sharp-S settings", () => {
159
- const config = parsePowerlineConfig(
160
- { preset: "compact", welcome: false, stashSharpSShortcut: true },
161
- ["default", "compact"],
162
- );
163
- const shorthand = parsePowerlineConfig("compact", ["default", "compact"]);
164
-
165
- assert.equal(config.welcome, false);
166
- assert.equal(config.stashSharpSShortcut, true);
167
- assert.equal(shorthand.welcome, true);
168
- assert.equal(shorthand.stashSharpSShortcut, false);
169
- });
170
- test("parsePowerlineConfig extracts supported segment options", () => {
171
- const config = parsePowerlineConfig(
172
- {
173
- preset: "default",
174
- model: { showThinkingLevel: true, display: "qualified" },
175
- path: { mode: "full", maxLength: 120 },
176
- git: { showBranch: false, showStaged: false, showUnstaged: true, showUntracked: false, polling: "branch", hostIcon: true },
177
- time: { format: "12h", showSeconds: true },
178
- cost: { subscriptionDisplay: "both", currency: "cny" },
179
- },
180
- ["default", "compact"],
181
- );
182
- const invalidCurrency = parsePowerlineConfig(
183
- { cost: { currency: "BTC" } },
184
- ["default", "compact"],
185
- );
186
-
187
- assert.deepEqual(config.segmentOptions, {
188
- model: { showThinkingLevel: true, display: "qualified" },
189
- path: { mode: "full", maxLength: 120 },
190
- git: { showBranch: false, showStaged: false, showUnstaged: true, showUntracked: false, polling: "branch", hostIcon: true },
191
- time: { format: "12h", showSeconds: true },
192
- cost: { subscriptionDisplay: "both", currency: "CNY" },
193
- });
194
- assert.deepEqual(invalidCurrency.segmentOptions, { cost: {} });
195
- });
196
-
197
- test("mergeSegmentOptions lets user config override preset segment defaults", () => {
198
- assert.deepEqual(
199
- mergeSegmentOptions(
200
- { path: { mode: "basename", maxLength: 20 }, git: { showBranch: true, showUntracked: true } },
201
- { path: { mode: "full" }, git: { showUntracked: false }, cost: { subscriptionDisplay: "reported-cost" } },
202
- ),
203
- {
204
- model: {},
205
- path: { mode: "full", maxLength: 20 },
206
- git: { showBranch: true, showUntracked: false },
207
- time: {},
208
- cost: { subscriptionDisplay: "reported-cost" },
209
- context: {},
210
- cache_read: {},
211
- },
212
- );
213
- });
214
-
215
- test("mergeSegmentsWithCustomItems appends custom segment ids by position", () => {
216
- const merged = mergeSegmentsWithCustomItems(
217
- {
218
- leftSegments: ["path"],
219
- rightSegments: ["git"],
220
- secondarySegments: ["extension_statuses"],
221
- separator: "powerline",
222
- },
223
- [
224
- { id: "ci", statusKey: "ci", position: "left", hideWhenMissing: true, excludeFromExtensionStatuses: true },
225
- { id: "timer", statusKey: "timer", position: "right", hideWhenMissing: true, excludeFromExtensionStatuses: true },
226
- { id: "review", statusKey: "review", position: "secondary", hideWhenMissing: true, excludeFromExtensionStatuses: true },
227
- ],
228
- );
229
-
230
- assert.deepEqual(merged.leftSegments, ["path", "custom:ci"]);
231
- assert.deepEqual(merged.rightSegments, ["git", "custom:timer"]);
232
- assert.deepEqual(merged.secondarySegments, ["extension_statuses", "custom:review"]);
233
- });
234
-
235
- test("mergeSegmentsWithCustomItems filters disabled segment ids", () => {
236
- const merged = mergeSegmentsWithCustomItems(
237
- {
238
- leftSegments: ["path", "model"],
239
- rightSegments: ["git", "cost"],
240
- secondarySegments: ["extension_statuses"],
241
- separator: "powerline",
242
- },
243
- [
244
- { id: "ci", statusKey: "ci", position: "left", hideWhenMissing: true, excludeFromExtensionStatuses: true },
245
- { id: "timer", statusKey: "timer", position: "right", hideWhenMissing: true, excludeFromExtensionStatuses: true },
246
- { id: "review", statusKey: "review", position: "secondary", hideWhenMissing: true, excludeFromExtensionStatuses: true },
247
- ],
248
- { disabledSegments: ["model", "cost", "custom:ci", "custom:review"] },
249
- );
250
-
251
- assert.deepEqual(merged.leftSegments, ["path"]);
252
- assert.deepEqual(merged.rightSegments, ["git", "custom:timer"]);
253
- assert.deepEqual(merged.secondarySegments, ["extension_statuses"]);
254
- });
255
-
256
- test("mergeSegmentsWithCustomItems applies partial layout rows before disabled filtering", () => {
257
- const merged = mergeSegmentsWithCustomItems(
258
- {
259
- leftSegments: ["path", "model"],
260
- rightSegments: ["git", "cost", "extension_statuses"],
261
- secondarySegments: ["extension_statuses"],
262
- separator: "powerline",
263
- },
264
- [
265
- { id: "ci", statusKey: "ci", position: "right", hideWhenMissing: true, excludeFromExtensionStatuses: true },
266
- { id: "review", statusKey: "review", position: "secondary", hideWhenMissing: true, excludeFromExtensionStatuses: true },
267
- ],
268
- {
269
- layout: {
270
- left: ["model", "custom:ci", "extension_statuses"],
271
- secondary: [],
272
- },
273
- disabledSegments: ["model"],
274
- },
275
- );
276
-
277
- assert.deepEqual(merged.leftSegments, ["custom:ci", "extension_statuses"]);
278
- assert.deepEqual(merged.rightSegments, ["git", "cost"]);
279
- assert.deepEqual(merged.secondarySegments, []);
280
- });
281
-
282
- test("nextPowerlineSettingWithPreset preserves object settings", () => {
283
- const updated = nextPowerlineSettingWithPreset({ preset: "default", customItems: [{ id: "ci" }] }, "compact");
284
- if (typeof updated !== "object" || updated === null || Array.isArray(updated)) {
285
- assert.fail("expected an object powerline setting");
286
- }
287
- if (!("preset" in updated)) {
288
- assert.fail("expected preset to be preserved on the updated powerline setting");
289
- }
290
- if (!("customItems" in updated)) {
291
- assert.fail("expected customItems to be preserved on the updated powerline setting");
292
- }
293
-
294
- assert.equal(updated.preset, "compact");
295
- assert.deepEqual(updated.customItems, [{ id: "ci" }]);
296
- });
297
-
298
- test("nextPowerlineSettingWithOptions preserves object settings", () => {
299
- const updated = nextPowerlineSettingWithOptions(
300
- { preset: "default", customItems: [{ id: "ci" }] },
301
- { placement: "below" },
302
- "compact",
303
- );
304
-
305
- assert.deepEqual(updated, { preset: "default", customItems: [{ id: "ci" }], placement: "below" });
306
- });
307
-
308
- test("nextPowerlineSettingWithOptions converts string presets to object settings", () => {
309
- assert.deepEqual(nextPowerlineSettingWithOptions("compact", { placement: "below" }, "compact"), {
310
- preset: "compact",
311
- placement: "below",
312
- });
313
- });
314
-
315
- test("collectHiddenExtensionStatusKeys includes default custom status keys", () => {
316
- const hidden = collectHiddenExtensionStatusKeys([
317
- { id: "ci", statusKey: "ci-status", position: "right", hideWhenMissing: true, excludeFromExtensionStatuses: true },
318
- { id: "review", statusKey: "review", position: "secondary", hideWhenMissing: true, excludeFromExtensionStatuses: false },
319
- ]);
320
-
321
- assert.equal(hidden.has("ci-status"), true);
322
- assert.equal(hidden.has("review"), false);
323
- });
324
-
325
- test("normalizeCompactExtensionStatus strips baked-in trailing separators", () => {
326
- assert.equal(normalizeCompactExtensionStatus("CI ok · "), "CI ok");
327
- assert.equal(normalizeCompactExtensionStatus("CI ok | "), "CI ok");
328
- assert.equal(normalizeCompactExtensionStatus("[notice] queued"), null);
329
- });
330
-
331
- test("normalizeExtensionStatusValue keeps notification-style statuses renderable for custom items", () => {
332
- assert.equal(normalizeExtensionStatusValue("[review] queued · "), "[review] queued");
333
- });
334
-
335
- test("getNotificationExtensionStatuses skips promoted hidden status keys", () => {
336
- const statuses = new Map<string, string>([
337
- ["ci-status", "[ci] queued"],
338
- ["review", "[review] running"],
339
- ["plain", "plain status"],
340
- ]);
341
- const hidden = new Set(["ci-status"]);
342
-
343
- assert.deepEqual(getNotificationExtensionStatuses(statuses, hidden), ["[review] running"]);
344
- });
@@ -1,48 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import type { AutocompleteProvider } from "@earendil-works/pi-tui";
4
- import { getEditorAutocompleteProvider, passAutocompleteProviderThroughPreviousEditor } from "../editor-composition.ts";
5
-
6
- function baseProvider(): AutocompleteProvider {
7
- return {
8
- async getSuggestions() {
9
- return {
10
- prefix: "@",
11
- items: [{ label: "base", value: "base" }],
12
- };
13
- },
14
- applyCompletion(lines, cursorLine, cursorCol) {
15
- return { lines, cursorLine, cursorCol };
16
- },
17
- };
18
- }
19
-
20
- test("previous editor autocomplete provider wrappers are preserved", async () => {
21
- const previousEditor = {
22
- autocompleteProvider: undefined as AutocompleteProvider | undefined,
23
- setAutocompleteProvider(provider: AutocompleteProvider) {
24
- this.autocompleteProvider = {
25
- async getSuggestions(lines, cursorLine, cursorCol, context) {
26
- const suggestions = await provider.getSuggestions(lines, cursorLine, cursorCol, context);
27
- return {
28
- ...suggestions,
29
- items: [...suggestions.items, { label: "previous-wrapper", value: "previous-wrapper" }],
30
- };
31
- },
32
- applyCompletion: provider.applyCompletion.bind(provider),
33
- };
34
- },
35
- };
36
-
37
- const composed = passAutocompleteProviderThroughPreviousEditor(baseProvider(), previousEditor);
38
- const suggestions = await composed.getSuggestions(["@"], 0, 1, { signal: new AbortController().signal });
39
-
40
- assert.equal(composed, previousEditor.autocompleteProvider);
41
- assert.deepEqual(suggestions.items.map((item) => item.label), ["base", "previous-wrapper"]);
42
- });
43
-
44
- test("autocomplete provider detection rejects incomplete editor providers", () => {
45
- assert.equal(getEditorAutocompleteProvider({ autocompleteProvider: {} }), undefined);
46
- assert.equal(getEditorAutocompleteProvider({ autocompleteProvider: { getSuggestions() {} } }), undefined);
47
- assert.ok(getEditorAutocompleteProvider({ autocompleteProvider: baseProvider() }));
48
- });