@selesai/code 0.9.14 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +6 -9
  3. package/dist/defaults/models.json +79 -29
  4. package/dist/defaults/settings.json +3 -7
  5. package/dist/extensions/package.json +3 -4
  6. package/dist/extensions/pi-zentui/.editorconfig +12 -0
  7. package/dist/extensions/pi-zentui/.github/CODEOWNERS +1 -0
  8. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/bug_report.yml +110 -0
  9. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/config.yml +8 -0
  10. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/config_question.yml +56 -0
  11. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/feature_request.yml +63 -0
  12. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/rendering_issue.yml +106 -0
  13. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/runtime_detection.yml +103 -0
  14. package/dist/extensions/pi-zentui/.github/dependabot.yml +47 -0
  15. package/dist/extensions/pi-zentui/.github/pull_request_template.md +34 -0
  16. package/dist/extensions/pi-zentui/.github/release.yml +18 -0
  17. package/dist/extensions/pi-zentui/.github/workflows/ci.yml +79 -0
  18. package/dist/extensions/pi-zentui/.github/workflows/publish.yml +63 -0
  19. package/dist/extensions/pi-zentui/AGENTS.md +107 -0
  20. package/dist/extensions/pi-zentui/CONTRIBUTING.md +68 -0
  21. package/dist/extensions/pi-zentui/LICENSE +21 -0
  22. package/dist/extensions/pi-zentui/README.md +188 -0
  23. package/dist/extensions/pi-zentui/SECURITY.md +18 -0
  24. package/dist/extensions/pi-zentui/assets/main-cover.png +0 -0
  25. package/dist/extensions/pi-zentui/assets/screenshots/editor-accent-rail.png +0 -0
  26. package/dist/extensions/pi-zentui/assets/screenshots/editor-minimalist.png +0 -0
  27. package/dist/extensions/pi-zentui/assets/screenshots/editor-opencode-copy-friendly.png +0 -0
  28. package/dist/extensions/pi-zentui/assets/screenshots/editor-opencode.png +0 -0
  29. package/dist/extensions/pi-zentui/assets/screenshots/user-message-compact.png +0 -0
  30. package/dist/extensions/pi-zentui/assets/screenshots/user-message-framed-copy-friendly.png +0 -0
  31. package/dist/extensions/pi-zentui/assets/screenshots/user-message-framed.png +0 -0
  32. package/dist/extensions/pi-zentui/assets/screenshots/user-message-labeled.png +0 -0
  33. package/dist/extensions/pi-zentui/assets/zentui.png +0 -0
  34. package/dist/extensions/pi-zentui/biome.json +31 -0
  35. package/dist/extensions/pi-zentui/docs/configuration.md +509 -0
  36. package/dist/extensions/pi-zentui/docs/footer-format.md +162 -0
  37. package/dist/extensions/pi-zentui/extensions/zentui/accent-rail-editor.ts +99 -0
  38. package/dist/extensions/pi-zentui/extensions/zentui/accent-rail-layout-patch.ts +457 -0
  39. package/dist/extensions/pi-zentui/extensions/zentui/completion-menu.ts +157 -0
  40. package/dist/extensions/pi-zentui/extensions/zentui/config.ts +2100 -0
  41. package/dist/extensions/pi-zentui/extensions/zentui/editor-metadata-format.ts +321 -0
  42. package/dist/extensions/pi-zentui/extensions/zentui/editor-transfer.ts +73 -0
  43. package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +89 -0
  44. package/dist/extensions/pi-zentui/extensions/zentui/footer-format.ts +364 -0
  45. package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +122 -0
  46. package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +812 -0
  47. package/dist/extensions/pi-zentui/extensions/zentui/format.ts +568 -0
  48. package/dist/extensions/pi-zentui/extensions/zentui/git.ts +384 -0
  49. package/dist/extensions/pi-zentui/extensions/zentui/icons.ts +252 -0
  50. package/dist/extensions/pi-zentui/extensions/zentui/index.ts +1406 -0
  51. package/dist/extensions/pi-zentui/extensions/zentui/interaction-summary.ts +1049 -0
  52. package/dist/extensions/pi-zentui/extensions/zentui/live-context.ts +75 -0
  53. package/dist/extensions/pi-zentui/extensions/zentui/minimalist-editor.ts +463 -0
  54. package/dist/extensions/pi-zentui/extensions/zentui/package-version.ts +651 -0
  55. package/dist/extensions/pi-zentui/extensions/zentui/project-refresh.ts +120 -0
  56. package/dist/extensions/pi-zentui/extensions/zentui/project-state.ts +59 -0
  57. package/dist/extensions/pi-zentui/extensions/zentui/prototype-patch-registry.ts +166 -0
  58. package/dist/extensions/pi-zentui/extensions/zentui/repository-root.ts +98 -0
  59. package/dist/extensions/pi-zentui/extensions/zentui/runtime.ts +841 -0
  60. package/dist/extensions/pi-zentui/extensions/zentui/selector-border.ts +93 -0
  61. package/dist/extensions/pi-zentui/extensions/zentui/session-lifecycle.ts +60 -0
  62. package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +1796 -0
  63. package/dist/extensions/pi-zentui/extensions/zentui/settings-previews.ts +128 -0
  64. package/dist/extensions/pi-zentui/extensions/zentui/state.ts +94 -0
  65. package/dist/extensions/pi-zentui/extensions/zentui/style.ts +424 -0
  66. package/dist/extensions/pi-zentui/extensions/zentui/telemetry.ts +82 -0
  67. package/dist/extensions/pi-zentui/extensions/zentui/ui.ts +1290 -0
  68. package/dist/extensions/pi-zentui/extensions/zentui/user-message-osc.ts +248 -0
  69. package/dist/extensions/pi-zentui/extensions/zentui/user-message-styles.ts +215 -0
  70. package/dist/extensions/pi-zentui/extensions/zentui/user-message.ts +206 -0
  71. package/dist/extensions/pi-zentui/extensions/zentui/working-line-messages.ts +24 -0
  72. package/dist/extensions/pi-zentui/extensions/zentui/working-line-spinners.ts +54 -0
  73. package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +1433 -0
  74. package/dist/extensions/pi-zentui/package-lock.json +5311 -0
  75. package/dist/extensions/pi-zentui/package.json +60 -0
  76. package/dist/extensions/pi-zentui/test/accent-rail-editor.test.ts +232 -0
  77. package/dist/extensions/pi-zentui/test/accent-rail-layout-patch.test.ts +669 -0
  78. package/dist/extensions/pi-zentui/test/completion-menu.test.ts +265 -0
  79. package/dist/extensions/pi-zentui/test/config.test.ts +3119 -0
  80. package/dist/extensions/pi-zentui/test/editor-metadata-format.test.ts +405 -0
  81. package/dist/extensions/pi-zentui/test/editor-transfer.test.ts +260 -0
  82. package/dist/extensions/pi-zentui/test/editor-viewport-indicators.test.ts +1286 -0
  83. package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +7196 -0
  84. package/dist/extensions/pi-zentui/test/extension-status.test.ts +182 -0
  85. package/dist/extensions/pi-zentui/test/fixtures/starship-nerd-font-symbols.toml +172 -0
  86. package/dist/extensions/pi-zentui/test/footer-format.test.ts +431 -0
  87. package/dist/extensions/pi-zentui/test/footer-layout.test.ts +113 -0
  88. package/dist/extensions/pi-zentui/test/footer-repository-path.test.ts +123 -0
  89. package/dist/extensions/pi-zentui/test/format.test.ts +865 -0
  90. package/dist/extensions/pi-zentui/test/git.test.ts +212 -0
  91. package/dist/extensions/pi-zentui/test/icons.test.ts +69 -0
  92. package/dist/extensions/pi-zentui/test/interaction-summary.test.ts +1402 -0
  93. package/dist/extensions/pi-zentui/test/live-context-integration.test.ts +378 -0
  94. package/dist/extensions/pi-zentui/test/live-context.test.ts +139 -0
  95. package/dist/extensions/pi-zentui/test/minimalist-editor.test.ts +811 -0
  96. package/dist/extensions/pi-zentui/test/package-version.test.ts +321 -0
  97. package/dist/extensions/pi-zentui/test/project-refresh.test.ts +250 -0
  98. package/dist/extensions/pi-zentui/test/project-state.test.ts +159 -0
  99. package/dist/extensions/pi-zentui/test/repository-root.test.ts +108 -0
  100. package/dist/extensions/pi-zentui/test/responsive-dependencies.test.ts +825 -0
  101. package/dist/extensions/pi-zentui/test/runtime.test.ts +183 -0
  102. package/dist/extensions/pi-zentui/test/session-lifecycle.test.ts +40 -0
  103. package/dist/extensions/pi-zentui/test/settings-command.test.ts +1381 -0
  104. package/dist/extensions/pi-zentui/test/settings-previews.test.ts +347 -0
  105. package/dist/extensions/pi-zentui/test/state.test.ts +84 -0
  106. package/dist/extensions/pi-zentui/test/telemetry-lifecycle-integration.test.ts +276 -0
  107. package/dist/extensions/pi-zentui/test/telemetry.test.ts +105 -0
  108. package/dist/extensions/pi-zentui/test/turn-summary-lifecycle.integration.test.ts +327 -0
  109. package/dist/extensions/pi-zentui/test/user-message-osc.test.ts +164 -0
  110. package/dist/extensions/pi-zentui/test/user-message-styles.test.ts +292 -0
  111. package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +730 -0
  112. package/dist/extensions/pi-zentui/test/working-line-messages.test.ts +34 -0
  113. package/dist/extensions/pi-zentui/test/working-line.test.ts +1990 -0
  114. package/dist/extensions/pi-zentui/tsconfig.json +16 -0
  115. package/dist/extensions/tps.test.ts +38 -0
  116. package/dist/extensions/{pi-powerline-footer/tps.ts → tps.ts} +4 -0
  117. package/dist/skills/design-references/SKILL.md +31 -0
  118. package/dist/skills/design-references/references/clickhouse/DESIGN.md +544 -0
  119. package/dist/themes/adventure.json +84 -0
  120. package/dist/themes/adwaita-dark.json +84 -0
  121. package/dist/themes/arcoiris.json +84 -0
  122. package/dist/themes/arthur.json +84 -0
  123. package/dist/themes/atom.json +84 -0
  124. package/dist/themes/aura.json +84 -0
  125. package/dist/themes/black-metal-bathory.json +84 -0
  126. package/dist/themes/black-metal-burzum.json +84 -0
  127. package/dist/themes/black-metal-khold.json +84 -0
  128. package/dist/themes/box.json +84 -0
  129. package/dist/themes/brogrammer.json +84 -0
  130. package/dist/themes/carbonfox.json +84 -0
  131. package/dist/themes/catppuccin-mocha.json +84 -0
  132. package/dist/themes/citruszest.json +84 -0
  133. package/dist/themes/cursor-dark.json +84 -0
  134. package/dist/themes/cutie-pro.json +84 -0
  135. package/dist/themes/dark-modern.json +84 -0
  136. package/dist/themes/dark-pastel.json +84 -0
  137. package/dist/themes/dimmed-monokai.json +84 -0
  138. package/dist/themes/doom-peacock.json +84 -0
  139. package/dist/themes/dracula-plus.json +84 -0
  140. package/dist/themes/dracula.json +80 -0
  141. package/dist/themes/earthsong.json +84 -0
  142. package/dist/themes/everforest-dark-hard.json +84 -0
  143. package/dist/themes/everforest-dark.json +80 -0
  144. package/dist/themes/fahrenheit.json +84 -0
  145. package/dist/themes/flatland.json +84 -0
  146. package/dist/themes/flexoki-dark.json +84 -0
  147. package/dist/themes/front-end-delight.json +84 -0
  148. package/dist/themes/fun-forrest.json +84 -0
  149. package/dist/themes/galizur.json +84 -0
  150. package/dist/themes/github-dark-colorblind.json +84 -0
  151. package/dist/themes/github-dark-high-contrast.json +84 -0
  152. package/dist/themes/glacier.json +84 -0
  153. package/dist/themes/gruber-darker.json +84 -0
  154. package/dist/themes/gruvbox-dark-hard.json +84 -0
  155. package/dist/themes/gruvbox-dark.json +84 -0
  156. package/dist/themes/gruvbox-material.json +84 -0
  157. package/dist/themes/guezwhoz.json +84 -0
  158. package/dist/themes/hacktober.json +84 -0
  159. package/dist/themes/hardcore.json +84 -0
  160. package/dist/themes/havn-skumring.json +84 -0
  161. package/dist/themes/ic-orange-ppl.json +84 -0
  162. package/dist/themes/iterm2-smoooooth.json +84 -0
  163. package/dist/themes/iterm2-tango-dark.json +84 -0
  164. package/dist/themes/japanesque.json +84 -0
  165. package/dist/themes/jellybeans.json +84 -0
  166. package/dist/themes/kanagawa-wave.json +84 -0
  167. package/dist/themes/kurokula.json +84 -0
  168. package/dist/themes/later-this-evening.json +84 -0
  169. package/dist/themes/lovelace.json +84 -0
  170. package/dist/themes/material-darker.json +84 -0
  171. package/dist/themes/matte-black.json +84 -0
  172. package/dist/themes/mellow.json +84 -0
  173. package/dist/themes/miasma.json +84 -0
  174. package/dist/themes/nord.json +80 -0
  175. package/dist/themes/nvim-dark.json +84 -0
  176. package/dist/themes/one-dark.json +80 -0
  177. package/dist/themes/popping-and-locking.json +84 -0
  178. package/dist/themes/rose-pine.json +80 -0
  179. package/dist/themes/sea-shells.json +84 -0
  180. package/dist/themes/sleepy-hollow.json +84 -0
  181. package/dist/themes/smyck.json +84 -0
  182. package/dist/themes/solarized-dark.json +80 -0
  183. package/dist/themes/tokyo-night.json +80 -0
  184. package/dist/themes/tomorrow-night-bright.json +84 -0
  185. package/dist/themes/tomorrow-night-burns.json +84 -0
  186. package/dist/themes/tomorrow-night.json +84 -0
  187. package/dist/themes/twilight.json +84 -0
  188. package/dist/themes/vague.json +84 -0
  189. package/dist/themes/vesper.json +84 -0
  190. package/dist/themes/xcode-dark-hc.json +84 -0
  191. package/docs/custom-provider.md +0 -1
  192. package/docs/plans/auto-model-routing-extension.md +490 -0
  193. package/package.json +3 -3
  194. package/dist/extensions/pi-powerline-footer/.github/workflows/test.yml +0 -27
  195. package/dist/extensions/pi-powerline-footer/CHANGELOG.md +0 -687
  196. package/dist/extensions/pi-powerline-footer/CONTRIBUTING.md +0 -16
  197. package/dist/extensions/pi-powerline-footer/README.md +0 -513
  198. package/dist/extensions/pi-powerline-footer/banner.png +0 -0
  199. package/dist/extensions/pi-powerline-footer/bash-mode/completion.ts +0 -586
  200. package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +0 -583
  201. package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +0 -204
  202. package/dist/extensions/pi-powerline-footer/bash-mode/shell-session.ts +0 -286
  203. package/dist/extensions/pi-powerline-footer/bash-mode/transcript.ts +0 -108
  204. package/dist/extensions/pi-powerline-footer/bash-mode/types.ts +0 -60
  205. package/dist/extensions/pi-powerline-footer/cd-command.ts +0 -190
  206. package/dist/extensions/pi-powerline-footer/colors.ts +0 -70
  207. package/dist/extensions/pi-powerline-footer/context-usage.ts +0 -156
  208. package/dist/extensions/pi-powerline-footer/currency-rates.ts +0 -142
  209. package/dist/extensions/pi-powerline-footer/editor-composition.ts +0 -32
  210. package/dist/extensions/pi-powerline-footer/editor-performance.ts +0 -99
  211. package/dist/extensions/pi-powerline-footer/git-status.ts +0 -329
  212. package/dist/extensions/pi-powerline-footer/guide.ts +0 -220
  213. package/dist/extensions/pi-powerline-footer/icons.ts +0 -190
  214. package/dist/extensions/pi-powerline-footer/index.ts +0 -3428
  215. package/dist/extensions/pi-powerline-footer/lifecycle.ts +0 -10
  216. package/dist/extensions/pi-powerline-footer/package.json +0 -49
  217. package/dist/extensions/pi-powerline-footer/paths.ts +0 -22
  218. package/dist/extensions/pi-powerline-footer/powerline-config.ts +0 -446
  219. package/dist/extensions/pi-powerline-footer/presets.ts +0 -102
  220. package/dist/extensions/pi-powerline-footer/queue/store.ts +0 -313
  221. package/dist/extensions/pi-powerline-footer/queue/types.ts +0 -53
  222. package/dist/extensions/pi-powerline-footer/render-scheduler.ts +0 -29
  223. package/dist/extensions/pi-powerline-footer/segments.ts +0 -570
  224. package/dist/extensions/pi-powerline-footer/separators.ts +0 -57
  225. package/dist/extensions/pi-powerline-footer/session-usage.ts +0 -44
  226. package/dist/extensions/pi-powerline-footer/shortcuts.ts +0 -61
  227. package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +0 -1905
  228. package/dist/extensions/pi-powerline-footer/tests/cd-command.test.ts +0 -144
  229. package/dist/extensions/pi-powerline-footer/tests/context-usage.test.ts +0 -156
  230. package/dist/extensions/pi-powerline-footer/tests/custom-items.test.ts +0 -344
  231. package/dist/extensions/pi-powerline-footer/tests/editor-composition.test.ts +0 -48
  232. package/dist/extensions/pi-powerline-footer/tests/editor-performance.test.ts +0 -43
  233. package/dist/extensions/pi-powerline-footer/tests/editor-responsiveness.test.ts +0 -136
  234. package/dist/extensions/pi-powerline-footer/tests/fixed-editor-border.test.ts +0 -39
  235. package/dist/extensions/pi-powerline-footer/tests/git-optional-locks.test.ts +0 -64
  236. package/dist/extensions/pi-powerline-footer/tests/git-status.test.ts +0 -92
  237. package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +0 -129
  238. package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +0 -116
  239. package/dist/extensions/pi-powerline-footer/tests/mode-aware-trigger.test.ts +0 -79
  240. package/dist/extensions/pi-powerline-footer/tests/paths.test.ts +0 -63
  241. package/dist/extensions/pi-powerline-footer/tests/queue-store.test.ts +0 -65
  242. package/dist/extensions/pi-powerline-footer/tests/quit-cursor.test.ts +0 -14
  243. package/dist/extensions/pi-powerline-footer/tests/remaining-regressions.test.ts +0 -173
  244. package/dist/extensions/pi-powerline-footer/tests/session-usage.test.ts +0 -47
  245. package/dist/extensions/pi-powerline-footer/tests/stash-shortcut.test.ts +0 -37
  246. package/dist/extensions/pi-powerline-footer/tests/thinking-segment.test.ts +0 -76
  247. package/dist/extensions/pi-powerline-footer/tests/token-stats.test.ts +0 -249
  248. package/dist/extensions/pi-powerline-footer/tests/tps.test.ts +0 -39
  249. package/dist/extensions/pi-powerline-footer/tests/usage-display.test.ts +0 -237
  250. package/dist/extensions/pi-powerline-footer/tests/welcome.test.ts +0 -199
  251. package/dist/extensions/pi-powerline-footer/tests/working-vibes.test.ts +0 -317
  252. package/dist/extensions/pi-powerline-footer/theme.example.json +0 -24
  253. package/dist/extensions/pi-powerline-footer/theme.json +0 -12
  254. package/dist/extensions/pi-powerline-footer/theme.ts +0 -241
  255. package/dist/extensions/pi-powerline-footer/token-stats.ts +0 -304
  256. package/dist/extensions/pi-powerline-footer/tsconfig.json +0 -13
  257. package/dist/extensions/pi-powerline-footer/types.ts +0 -235
  258. package/dist/extensions/pi-powerline-footer/welcome-dismiss.ts +0 -34
  259. package/dist/extensions/pi-powerline-footer/welcome.ts +0 -709
  260. package/dist/extensions/pi-powerline-footer/working-vibes.ts +0 -721
  261. package/dist/extensions/preview-tools-disabled.test.ts +0 -37
  262. package/dist/extensions/preview-tools-disabled.ts +0 -11
  263. package/dist/themes/powerline-footer/theme.json +0 -33
@@ -1,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
- });