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