@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.
- package/CHANGELOG.md +27 -0
- package/README.md +6 -9
- package/dist/defaults/models.json +79 -29
- package/dist/defaults/settings.json +3 -7
- package/dist/extensions/package.json +3 -4
- package/dist/extensions/pi-zentui/.editorconfig +12 -0
- package/dist/extensions/pi-zentui/.github/CODEOWNERS +1 -0
- package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/bug_report.yml +110 -0
- package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/config_question.yml +56 -0
- package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/feature_request.yml +63 -0
- package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/rendering_issue.yml +106 -0
- package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/runtime_detection.yml +103 -0
- package/dist/extensions/pi-zentui/.github/dependabot.yml +47 -0
- package/dist/extensions/pi-zentui/.github/pull_request_template.md +34 -0
- package/dist/extensions/pi-zentui/.github/release.yml +18 -0
- package/dist/extensions/pi-zentui/.github/workflows/ci.yml +79 -0
- package/dist/extensions/pi-zentui/.github/workflows/publish.yml +63 -0
- package/dist/extensions/pi-zentui/AGENTS.md +107 -0
- package/dist/extensions/pi-zentui/CONTRIBUTING.md +68 -0
- package/dist/extensions/pi-zentui/LICENSE +21 -0
- package/dist/extensions/pi-zentui/README.md +188 -0
- package/dist/extensions/pi-zentui/SECURITY.md +18 -0
- package/dist/extensions/pi-zentui/assets/main-cover.png +0 -0
- package/dist/extensions/pi-zentui/assets/screenshots/editor-accent-rail.png +0 -0
- package/dist/extensions/pi-zentui/assets/screenshots/editor-minimalist.png +0 -0
- package/dist/extensions/pi-zentui/assets/screenshots/editor-opencode-copy-friendly.png +0 -0
- package/dist/extensions/pi-zentui/assets/screenshots/editor-opencode.png +0 -0
- package/dist/extensions/pi-zentui/assets/screenshots/user-message-compact.png +0 -0
- package/dist/extensions/pi-zentui/assets/screenshots/user-message-framed-copy-friendly.png +0 -0
- package/dist/extensions/pi-zentui/assets/screenshots/user-message-framed.png +0 -0
- package/dist/extensions/pi-zentui/assets/screenshots/user-message-labeled.png +0 -0
- package/dist/extensions/pi-zentui/assets/zentui.png +0 -0
- package/dist/extensions/pi-zentui/biome.json +31 -0
- package/dist/extensions/pi-zentui/docs/configuration.md +509 -0
- package/dist/extensions/pi-zentui/docs/footer-format.md +162 -0
- package/dist/extensions/pi-zentui/extensions/zentui/accent-rail-editor.ts +99 -0
- package/dist/extensions/pi-zentui/extensions/zentui/accent-rail-layout-patch.ts +457 -0
- package/dist/extensions/pi-zentui/extensions/zentui/completion-menu.ts +157 -0
- package/dist/extensions/pi-zentui/extensions/zentui/config.ts +2100 -0
- package/dist/extensions/pi-zentui/extensions/zentui/editor-metadata-format.ts +321 -0
- package/dist/extensions/pi-zentui/extensions/zentui/editor-transfer.ts +73 -0
- package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +89 -0
- package/dist/extensions/pi-zentui/extensions/zentui/footer-format.ts +364 -0
- package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +122 -0
- package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +812 -0
- package/dist/extensions/pi-zentui/extensions/zentui/format.ts +568 -0
- package/dist/extensions/pi-zentui/extensions/zentui/git.ts +384 -0
- package/dist/extensions/pi-zentui/extensions/zentui/icons.ts +252 -0
- package/dist/extensions/pi-zentui/extensions/zentui/index.ts +1406 -0
- package/dist/extensions/pi-zentui/extensions/zentui/interaction-summary.ts +1049 -0
- package/dist/extensions/pi-zentui/extensions/zentui/live-context.ts +75 -0
- package/dist/extensions/pi-zentui/extensions/zentui/minimalist-editor.ts +463 -0
- package/dist/extensions/pi-zentui/extensions/zentui/package-version.ts +651 -0
- package/dist/extensions/pi-zentui/extensions/zentui/project-refresh.ts +120 -0
- package/dist/extensions/pi-zentui/extensions/zentui/project-state.ts +59 -0
- package/dist/extensions/pi-zentui/extensions/zentui/prototype-patch-registry.ts +166 -0
- package/dist/extensions/pi-zentui/extensions/zentui/repository-root.ts +98 -0
- package/dist/extensions/pi-zentui/extensions/zentui/runtime.ts +841 -0
- package/dist/extensions/pi-zentui/extensions/zentui/selector-border.ts +93 -0
- package/dist/extensions/pi-zentui/extensions/zentui/session-lifecycle.ts +60 -0
- package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +1796 -0
- package/dist/extensions/pi-zentui/extensions/zentui/settings-previews.ts +128 -0
- package/dist/extensions/pi-zentui/extensions/zentui/state.ts +94 -0
- package/dist/extensions/pi-zentui/extensions/zentui/style.ts +424 -0
- package/dist/extensions/pi-zentui/extensions/zentui/telemetry.ts +82 -0
- package/dist/extensions/pi-zentui/extensions/zentui/ui.ts +1290 -0
- package/dist/extensions/pi-zentui/extensions/zentui/user-message-osc.ts +248 -0
- package/dist/extensions/pi-zentui/extensions/zentui/user-message-styles.ts +215 -0
- package/dist/extensions/pi-zentui/extensions/zentui/user-message.ts +206 -0
- package/dist/extensions/pi-zentui/extensions/zentui/working-line-messages.ts +24 -0
- package/dist/extensions/pi-zentui/extensions/zentui/working-line-spinners.ts +54 -0
- package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +1433 -0
- package/dist/extensions/pi-zentui/package-lock.json +5311 -0
- package/dist/extensions/pi-zentui/package.json +60 -0
- package/dist/extensions/pi-zentui/test/accent-rail-editor.test.ts +232 -0
- package/dist/extensions/pi-zentui/test/accent-rail-layout-patch.test.ts +669 -0
- package/dist/extensions/pi-zentui/test/completion-menu.test.ts +265 -0
- package/dist/extensions/pi-zentui/test/config.test.ts +3119 -0
- package/dist/extensions/pi-zentui/test/editor-metadata-format.test.ts +405 -0
- package/dist/extensions/pi-zentui/test/editor-transfer.test.ts +260 -0
- package/dist/extensions/pi-zentui/test/editor-viewport-indicators.test.ts +1286 -0
- package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +7196 -0
- package/dist/extensions/pi-zentui/test/extension-status.test.ts +182 -0
- package/dist/extensions/pi-zentui/test/fixtures/starship-nerd-font-symbols.toml +172 -0
- package/dist/extensions/pi-zentui/test/footer-format.test.ts +431 -0
- package/dist/extensions/pi-zentui/test/footer-layout.test.ts +113 -0
- package/dist/extensions/pi-zentui/test/footer-repository-path.test.ts +123 -0
- package/dist/extensions/pi-zentui/test/format.test.ts +865 -0
- package/dist/extensions/pi-zentui/test/git.test.ts +212 -0
- package/dist/extensions/pi-zentui/test/icons.test.ts +69 -0
- package/dist/extensions/pi-zentui/test/interaction-summary.test.ts +1402 -0
- package/dist/extensions/pi-zentui/test/live-context-integration.test.ts +378 -0
- package/dist/extensions/pi-zentui/test/live-context.test.ts +139 -0
- package/dist/extensions/pi-zentui/test/minimalist-editor.test.ts +811 -0
- package/dist/extensions/pi-zentui/test/package-version.test.ts +321 -0
- package/dist/extensions/pi-zentui/test/project-refresh.test.ts +250 -0
- package/dist/extensions/pi-zentui/test/project-state.test.ts +159 -0
- package/dist/extensions/pi-zentui/test/repository-root.test.ts +108 -0
- package/dist/extensions/pi-zentui/test/responsive-dependencies.test.ts +825 -0
- package/dist/extensions/pi-zentui/test/runtime.test.ts +183 -0
- package/dist/extensions/pi-zentui/test/session-lifecycle.test.ts +40 -0
- package/dist/extensions/pi-zentui/test/settings-command.test.ts +1381 -0
- package/dist/extensions/pi-zentui/test/settings-previews.test.ts +347 -0
- package/dist/extensions/pi-zentui/test/state.test.ts +84 -0
- package/dist/extensions/pi-zentui/test/telemetry-lifecycle-integration.test.ts +276 -0
- package/dist/extensions/pi-zentui/test/telemetry.test.ts +105 -0
- package/dist/extensions/pi-zentui/test/turn-summary-lifecycle.integration.test.ts +327 -0
- package/dist/extensions/pi-zentui/test/user-message-osc.test.ts +164 -0
- package/dist/extensions/pi-zentui/test/user-message-styles.test.ts +292 -0
- package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +730 -0
- package/dist/extensions/pi-zentui/test/working-line-messages.test.ts +34 -0
- package/dist/extensions/pi-zentui/test/working-line.test.ts +1990 -0
- package/dist/extensions/pi-zentui/tsconfig.json +16 -0
- package/dist/extensions/tps.test.ts +38 -0
- package/dist/extensions/{pi-powerline-footer/tps.ts → tps.ts} +4 -0
- package/dist/skills/design-references/SKILL.md +31 -0
- package/dist/skills/design-references/references/clickhouse/DESIGN.md +544 -0
- package/dist/themes/adventure.json +84 -0
- package/dist/themes/adwaita-dark.json +84 -0
- package/dist/themes/arcoiris.json +84 -0
- package/dist/themes/arthur.json +84 -0
- package/dist/themes/atom.json +84 -0
- package/dist/themes/aura.json +84 -0
- package/dist/themes/black-metal-bathory.json +84 -0
- package/dist/themes/black-metal-burzum.json +84 -0
- package/dist/themes/black-metal-khold.json +84 -0
- package/dist/themes/box.json +84 -0
- package/dist/themes/brogrammer.json +84 -0
- package/dist/themes/carbonfox.json +84 -0
- package/dist/themes/catppuccin-mocha.json +84 -0
- package/dist/themes/citruszest.json +84 -0
- package/dist/themes/cursor-dark.json +84 -0
- package/dist/themes/cutie-pro.json +84 -0
- package/dist/themes/dark-modern.json +84 -0
- package/dist/themes/dark-pastel.json +84 -0
- package/dist/themes/dimmed-monokai.json +84 -0
- package/dist/themes/doom-peacock.json +84 -0
- package/dist/themes/dracula-plus.json +84 -0
- package/dist/themes/dracula.json +80 -0
- package/dist/themes/earthsong.json +84 -0
- package/dist/themes/everforest-dark-hard.json +84 -0
- package/dist/themes/everforest-dark.json +80 -0
- package/dist/themes/fahrenheit.json +84 -0
- package/dist/themes/flatland.json +84 -0
- package/dist/themes/flexoki-dark.json +84 -0
- package/dist/themes/front-end-delight.json +84 -0
- package/dist/themes/fun-forrest.json +84 -0
- package/dist/themes/galizur.json +84 -0
- package/dist/themes/github-dark-colorblind.json +84 -0
- package/dist/themes/github-dark-high-contrast.json +84 -0
- package/dist/themes/glacier.json +84 -0
- package/dist/themes/gruber-darker.json +84 -0
- package/dist/themes/gruvbox-dark-hard.json +84 -0
- package/dist/themes/gruvbox-dark.json +84 -0
- package/dist/themes/gruvbox-material.json +84 -0
- package/dist/themes/guezwhoz.json +84 -0
- package/dist/themes/hacktober.json +84 -0
- package/dist/themes/hardcore.json +84 -0
- package/dist/themes/havn-skumring.json +84 -0
- package/dist/themes/ic-orange-ppl.json +84 -0
- package/dist/themes/iterm2-smoooooth.json +84 -0
- package/dist/themes/iterm2-tango-dark.json +84 -0
- package/dist/themes/japanesque.json +84 -0
- package/dist/themes/jellybeans.json +84 -0
- package/dist/themes/kanagawa-wave.json +84 -0
- package/dist/themes/kurokula.json +84 -0
- package/dist/themes/later-this-evening.json +84 -0
- package/dist/themes/lovelace.json +84 -0
- package/dist/themes/material-darker.json +84 -0
- package/dist/themes/matte-black.json +84 -0
- package/dist/themes/mellow.json +84 -0
- package/dist/themes/miasma.json +84 -0
- package/dist/themes/nord.json +80 -0
- package/dist/themes/nvim-dark.json +84 -0
- package/dist/themes/one-dark.json +80 -0
- package/dist/themes/popping-and-locking.json +84 -0
- package/dist/themes/rose-pine.json +80 -0
- package/dist/themes/sea-shells.json +84 -0
- package/dist/themes/sleepy-hollow.json +84 -0
- package/dist/themes/smyck.json +84 -0
- package/dist/themes/solarized-dark.json +80 -0
- package/dist/themes/tokyo-night.json +80 -0
- package/dist/themes/tomorrow-night-bright.json +84 -0
- package/dist/themes/tomorrow-night-burns.json +84 -0
- package/dist/themes/tomorrow-night.json +84 -0
- package/dist/themes/twilight.json +84 -0
- package/dist/themes/vague.json +84 -0
- package/dist/themes/vesper.json +84 -0
- package/dist/themes/xcode-dark-hc.json +84 -0
- package/docs/custom-provider.md +0 -1
- package/docs/plans/auto-model-routing-extension.md +490 -0
- package/package.json +3 -3
- package/dist/extensions/pi-powerline-footer/.github/workflows/test.yml +0 -27
- package/dist/extensions/pi-powerline-footer/CHANGELOG.md +0 -687
- package/dist/extensions/pi-powerline-footer/CONTRIBUTING.md +0 -16
- package/dist/extensions/pi-powerline-footer/README.md +0 -513
- package/dist/extensions/pi-powerline-footer/banner.png +0 -0
- package/dist/extensions/pi-powerline-footer/bash-mode/completion.ts +0 -586
- package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +0 -583
- package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +0 -204
- package/dist/extensions/pi-powerline-footer/bash-mode/shell-session.ts +0 -286
- package/dist/extensions/pi-powerline-footer/bash-mode/transcript.ts +0 -108
- package/dist/extensions/pi-powerline-footer/bash-mode/types.ts +0 -60
- package/dist/extensions/pi-powerline-footer/cd-command.ts +0 -190
- package/dist/extensions/pi-powerline-footer/colors.ts +0 -70
- package/dist/extensions/pi-powerline-footer/context-usage.ts +0 -156
- package/dist/extensions/pi-powerline-footer/currency-rates.ts +0 -142
- package/dist/extensions/pi-powerline-footer/editor-composition.ts +0 -32
- package/dist/extensions/pi-powerline-footer/editor-performance.ts +0 -99
- package/dist/extensions/pi-powerline-footer/git-status.ts +0 -329
- package/dist/extensions/pi-powerline-footer/guide.ts +0 -220
- package/dist/extensions/pi-powerline-footer/icons.ts +0 -190
- package/dist/extensions/pi-powerline-footer/index.ts +0 -3428
- package/dist/extensions/pi-powerline-footer/lifecycle.ts +0 -10
- package/dist/extensions/pi-powerline-footer/package.json +0 -49
- package/dist/extensions/pi-powerline-footer/paths.ts +0 -22
- package/dist/extensions/pi-powerline-footer/powerline-config.ts +0 -446
- package/dist/extensions/pi-powerline-footer/presets.ts +0 -102
- package/dist/extensions/pi-powerline-footer/queue/store.ts +0 -313
- package/dist/extensions/pi-powerline-footer/queue/types.ts +0 -53
- package/dist/extensions/pi-powerline-footer/render-scheduler.ts +0 -29
- package/dist/extensions/pi-powerline-footer/segments.ts +0 -570
- package/dist/extensions/pi-powerline-footer/separators.ts +0 -57
- package/dist/extensions/pi-powerline-footer/session-usage.ts +0 -44
- package/dist/extensions/pi-powerline-footer/shortcuts.ts +0 -61
- package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +0 -1905
- package/dist/extensions/pi-powerline-footer/tests/cd-command.test.ts +0 -144
- package/dist/extensions/pi-powerline-footer/tests/context-usage.test.ts +0 -156
- package/dist/extensions/pi-powerline-footer/tests/custom-items.test.ts +0 -344
- package/dist/extensions/pi-powerline-footer/tests/editor-composition.test.ts +0 -48
- package/dist/extensions/pi-powerline-footer/tests/editor-performance.test.ts +0 -43
- package/dist/extensions/pi-powerline-footer/tests/editor-responsiveness.test.ts +0 -136
- package/dist/extensions/pi-powerline-footer/tests/fixed-editor-border.test.ts +0 -39
- package/dist/extensions/pi-powerline-footer/tests/git-optional-locks.test.ts +0 -64
- package/dist/extensions/pi-powerline-footer/tests/git-status.test.ts +0 -92
- package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +0 -129
- package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +0 -116
- package/dist/extensions/pi-powerline-footer/tests/mode-aware-trigger.test.ts +0 -79
- package/dist/extensions/pi-powerline-footer/tests/paths.test.ts +0 -63
- package/dist/extensions/pi-powerline-footer/tests/queue-store.test.ts +0 -65
- package/dist/extensions/pi-powerline-footer/tests/quit-cursor.test.ts +0 -14
- package/dist/extensions/pi-powerline-footer/tests/remaining-regressions.test.ts +0 -173
- package/dist/extensions/pi-powerline-footer/tests/session-usage.test.ts +0 -47
- package/dist/extensions/pi-powerline-footer/tests/stash-shortcut.test.ts +0 -37
- package/dist/extensions/pi-powerline-footer/tests/thinking-segment.test.ts +0 -76
- package/dist/extensions/pi-powerline-footer/tests/token-stats.test.ts +0 -249
- package/dist/extensions/pi-powerline-footer/tests/tps.test.ts +0 -39
- package/dist/extensions/pi-powerline-footer/tests/usage-display.test.ts +0 -237
- package/dist/extensions/pi-powerline-footer/tests/welcome.test.ts +0 -199
- package/dist/extensions/pi-powerline-footer/tests/working-vibes.test.ts +0 -317
- package/dist/extensions/pi-powerline-footer/theme.example.json +0 -24
- package/dist/extensions/pi-powerline-footer/theme.json +0 -12
- package/dist/extensions/pi-powerline-footer/theme.ts +0 -241
- package/dist/extensions/pi-powerline-footer/token-stats.ts +0 -304
- package/dist/extensions/pi-powerline-footer/tsconfig.json +0 -13
- package/dist/extensions/pi-powerline-footer/types.ts +0 -235
- package/dist/extensions/pi-powerline-footer/welcome-dismiss.ts +0 -34
- package/dist/extensions/pi-powerline-footer/welcome.ts +0 -709
- package/dist/extensions/pi-powerline-footer/working-vibes.ts +0 -721
- package/dist/extensions/preview-tools-disabled.test.ts +0 -37
- package/dist/extensions/preview-tools-disabled.ts +0 -11
- package/dist/themes/powerline-footer/theme.json +0 -33
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { Editor } from "@earendil-works/pi-tui";
|
|
3
|
-
import { createInlineSkillAutocompleteProvider } from "../../../extensions/inline-skills.ts";
|
|
4
|
-
import { createInlineSubagentAutocompleteProvider } from "../../../extensions/pi-subagents/src/slash/inline-subagents.ts";
|
|
5
|
-
import { ModeAwareAutocompleteProvider, BashAutocompleteProvider, OneOffBashAutocompleteProvider } from "../bash-mode/completion.ts";
|
|
6
|
-
|
|
7
|
-
const fakeTui = { requestRender() {}, terminal: { rows: 50 } } as never;
|
|
8
|
-
const pi = {
|
|
9
|
-
getCommands: () => [
|
|
10
|
-
{ name: "skill:grill-me", description: "Grill", source: "skill", sourceInfo: { path: "C:/x/grill.md" } },
|
|
11
|
-
{ name: "skill:research", description: "Research", source: "skill", sourceInfo: { path: "C:/x/research.md" } },
|
|
12
|
-
],
|
|
13
|
-
on: () => {},
|
|
14
|
-
} as never;
|
|
15
|
-
|
|
16
|
-
const baseProvider: any = {
|
|
17
|
-
triggerCharacters: [],
|
|
18
|
-
async getSuggestions() {
|
|
19
|
-
return null;
|
|
20
|
-
},
|
|
21
|
-
applyCompletion(lines: string[], cursorLine: number, cursorCol: number) {
|
|
22
|
-
return { lines, cursorLine, cursorCol };
|
|
23
|
-
},
|
|
24
|
-
shouldTriggerFileCompletion() {
|
|
25
|
-
return true;
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const popupValues = (e: Editor) =>
|
|
30
|
-
(e as any).autocompleteState ? ((e as any).autocompleteList?.items ?? []).map((i: any) => i.value) : null;
|
|
31
|
-
|
|
32
|
-
async function typeInto(e: Editor, text: string) {
|
|
33
|
-
for (const ch of text) {
|
|
34
|
-
(e as any).insertCharacter(ch);
|
|
35
|
-
await (e as any).autocompleteRequestTask;
|
|
36
|
-
await new Promise((r) => setTimeout(r, 80));
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function buildChain() {
|
|
41
|
-
let provider: any = baseProvider;
|
|
42
|
-
for (const wrap of [
|
|
43
|
-
(c: any) => createInlineSkillAutocompleteProvider(pi, c),
|
|
44
|
-
(c: any) => createInlineSubagentAutocompleteProvider({ baseCwd: process.cwd() }, c),
|
|
45
|
-
]) {
|
|
46
|
-
provider = wrap(provider);
|
|
47
|
-
}
|
|
48
|
-
provider.triggerCharacters = [...new Set(["$", "#"])];
|
|
49
|
-
return provider;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
describe("powerline ModeAware wrapper must forward trigger characters", () => {
|
|
53
|
-
it("keeps $ after wrapping and shows skills for the user's three inputs", async () => {
|
|
54
|
-
const chain = buildChain();
|
|
55
|
-
// powerline: editor.installAutocompleteProvider(new ModeAware(chain, bash, oneOffBash))
|
|
56
|
-
const wrapped = new ModeAwareAutocompleteProvider(chain, new BashAutocompleteProvider(), new OneOffBashAutocompleteProvider(), () => false);
|
|
57
|
-
|
|
58
|
-
const e = new Editor(fakeTui as any, { borderColor: "x" } as any);
|
|
59
|
-
e.setAutocompleteProvider(wrapped as any);
|
|
60
|
-
console.log("triggerChars after wrap:", (e as any).autocompleteTriggerCharacters);
|
|
61
|
-
expect((e as any).autocompleteTriggerCharacters).toContain("$");
|
|
62
|
-
|
|
63
|
-
await typeInto(e, "$gril");
|
|
64
|
-
console.log("'$gril' ->", JSON.stringify(popupValues(e)));
|
|
65
|
-
expect(popupValues(e) ?? []).toContain("$grill-me");
|
|
66
|
-
|
|
67
|
-
const e2 = new Editor(fakeTui as any, { borderColor: "x" } as any);
|
|
68
|
-
e2.setAutocompleteProvider(wrapped as any);
|
|
69
|
-
await typeInto(e2, "asdf $gril");
|
|
70
|
-
console.log("'asdf $gril' ->", JSON.stringify(popupValues(e2)));
|
|
71
|
-
expect(popupValues(e2) ?? []).toContain("$grill-me");
|
|
72
|
-
|
|
73
|
-
const e3 = new Editor(fakeTui as any, { borderColor: "x" } as any);
|
|
74
|
-
e3.setAutocompleteProvider(wrapped as any);
|
|
75
|
-
await typeInto(e3, "asdf #builder $gril");
|
|
76
|
-
console.log("'asdf #builder $gril' ->", JSON.stringify(popupValues(e3)));
|
|
77
|
-
expect(popupValues(e3) ?? []).toContain("$grill-me");
|
|
78
|
-
});
|
|
79
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import { join } from "node:path";
|
|
6
|
-
import { getAgentPath, getAgentSessionDirs, getLegacyPiPath } from "../paths.ts";
|
|
7
|
-
|
|
8
|
-
function withTemporaryPathEnv(run: (home: string, agentDir: string) => void): void {
|
|
9
|
-
const root = mkdtempSync(join(tmpdir(), "powerline-paths-"));
|
|
10
|
-
const home = join(root, "home");
|
|
11
|
-
const agentDir = join(root, "custom-agent");
|
|
12
|
-
const originalHome = process.env.HOME;
|
|
13
|
-
const originalUserProfile = process.env.USERPROFILE;
|
|
14
|
-
const originalAgentDir = process.env.SELESAI_CODING_AGENT_DIR;
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
mkdirSync(home, { recursive: true });
|
|
18
|
-
process.env.HOME = home;
|
|
19
|
-
delete process.env.USERPROFILE;
|
|
20
|
-
run(home, agentDir);
|
|
21
|
-
} finally {
|
|
22
|
-
if (originalHome === undefined) delete process.env.HOME;
|
|
23
|
-
else process.env.HOME = originalHome;
|
|
24
|
-
if (originalUserProfile === undefined) delete process.env.USERPROFILE;
|
|
25
|
-
else process.env.USERPROFILE = originalUserProfile;
|
|
26
|
-
if (originalAgentDir === undefined) delete process.env.SELESAI_CODING_AGENT_DIR;
|
|
27
|
-
else process.env.SELESAI_CODING_AGENT_DIR = originalAgentDir;
|
|
28
|
-
rmSync(root, { recursive: true, force: true });
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
test("agent dir uses non-empty SELESAI_CODING_AGENT_DIR", () => {
|
|
33
|
-
withTemporaryPathEnv((_home, agentDir) => {
|
|
34
|
-
process.env.SELESAI_CODING_AGENT_DIR = agentDir;
|
|
35
|
-
|
|
36
|
-
assert.equal(getAgentPath("settings.json"), join(agentDir, "settings.json"));
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("agent dir falls back to HOME config dir agent for empty env values", () => {
|
|
41
|
-
withTemporaryPathEnv((home) => {
|
|
42
|
-
process.env.SELESAI_CODING_AGENT_DIR = "";
|
|
43
|
-
|
|
44
|
-
assert.equal(getAgentPath("settings.json"), join(home, ".selesai", "agent", "settings.json"));
|
|
45
|
-
assert.equal(getAgentPath("sessions"), join(home, ".selesai", "agent", "sessions"));
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test("agent sessions include legacy config-dir sessions only when it exists", () => {
|
|
50
|
-
withTemporaryPathEnv((home, agentDir) => {
|
|
51
|
-
process.env.SELESAI_CODING_AGENT_DIR = agentDir;
|
|
52
|
-
assert.deepEqual(getAgentSessionDirs(), [join(agentDir, "sessions")]);
|
|
53
|
-
|
|
54
|
-
mkdirSync(join(home, ".selesai", "sessions"), { recursive: true });
|
|
55
|
-
assert.deepEqual(getAgentSessionDirs(), [join(agentDir, "sessions"), join(home, ".selesai", "sessions")]);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("legacy path resolves under the fork config dir name", () => {
|
|
60
|
-
withTemporaryPathEnv((home) => {
|
|
61
|
-
assert.equal(getLegacyPiPath("sessions"), join(home, ".selesai", "sessions"));
|
|
62
|
-
});
|
|
63
|
-
});
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { existsSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
|
-
import { PowerlineQueueStore, currentQueueContext, formatQueueDeliveryText, parseCompactQueuedPrompt } from "../queue/store.ts";
|
|
7
|
-
|
|
8
|
-
function withStore(fn: (store: PowerlineQueueStore, dir: string) => void): void {
|
|
9
|
-
const dir = mkdtempSync(join(tmpdir(), "powerline-queue-"));
|
|
10
|
-
try {
|
|
11
|
-
fn(new PowerlineQueueStore(join(dir, "inbox.jsonl"), join(dir, "projects.json")), dir);
|
|
12
|
-
} finally {
|
|
13
|
-
rmSync(dir, { recursive: true, force: true });
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
test("queue store summarizes queued and blocked prompts", () => withStore((store) => {
|
|
18
|
-
const cwd = "/tmp/project-a";
|
|
19
|
-
store.add({ text: "run after compact", source: { cwd, sessionId: "s1" }, target: { kind: "current-session" }, intent: "post-compact", now: 100 });
|
|
20
|
-
const blocked = store.add({ text: "retry me", source: { cwd, sessionId: "s1" }, target: { kind: "project", cwd }, intent: "follow-up", now: 101 });
|
|
21
|
-
store.update(blocked.id, { status: "blocked" });
|
|
22
|
-
|
|
23
|
-
assert.deepEqual(store.summarize(currentQueueContext(cwd, "s1"), true), {
|
|
24
|
-
queueCount: 2,
|
|
25
|
-
blockedCount: 1,
|
|
26
|
-
compacting: true,
|
|
27
|
-
leadingText: "retry me",
|
|
28
|
-
leadingIntent: "follow-up",
|
|
29
|
-
leadingStatus: "blocked",
|
|
30
|
-
});
|
|
31
|
-
}));
|
|
32
|
-
|
|
33
|
-
test("current-session targets stay scoped to the source session when known", () => withStore((store) => {
|
|
34
|
-
store.add({ text: "session only", source: { cwd: "/tmp/project", sessionId: "s1" }, target: { kind: "current-session" }, intent: "post-compact" });
|
|
35
|
-
assert.equal(store.activeItems(currentQueueContext("/tmp/project", "s1")).length, 1);
|
|
36
|
-
assert.equal(store.activeItems(currentQueueContext("/tmp/project", "s2")).length, 0);
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
|
-
test("queue delivery returns the prompt text", () => {
|
|
40
|
-
assert.equal(formatQueueDeliveryText({
|
|
41
|
-
id: "a1b2c3d4", text: "check logs", createdAt: 1000, updatedAt: 1000,
|
|
42
|
-
source: { cwd: "/tmp/project" }, target: { kind: "current-session" }, intent: "follow-up", status: "queued",
|
|
43
|
-
}), "check logs");
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test("parseCompactQueuedPrompt treats /compact suffix as queued prompt text", () => {
|
|
47
|
-
assert.equal(parseCompactQueuedPrompt("/compact great lets proceed"), "great lets proceed");
|
|
48
|
-
assert.equal(parseCompactQueuedPrompt(" /compact great lets proceed "), "great lets proceed");
|
|
49
|
-
assert.equal(parseCompactQueuedPrompt("/compact"), null);
|
|
50
|
-
assert.equal(parseCompactQueuedPrompt("/compactness great lets proceed"), null);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test("queue store clears items from active summary", () => withStore((store) => {
|
|
54
|
-
const item = store.add({ text: "queued prompt", source: { cwd: "/tmp/project" }, target: { kind: "current-session" }, intent: "post-compact" });
|
|
55
|
-
assert.equal(store.summarize(currentQueueContext("/tmp/project"), false).queueCount, 1);
|
|
56
|
-
store.clear(item.id);
|
|
57
|
-
assert.equal(store.summarize(currentQueueContext("/tmp/project"), false).queueCount, 0);
|
|
58
|
-
}));
|
|
59
|
-
|
|
60
|
-
test("queue store times out instead of stealing an existing lock", () => withStore((store, dir) => {
|
|
61
|
-
const lockPath = join(dir, "inbox.jsonl.lock");
|
|
62
|
-
mkdirSync(lockPath);
|
|
63
|
-
assert.throws(() => store.add({ text: "blocked write", source: { cwd: "/tmp/project" }, target: { kind: "project", cwd: "/tmp/project" }, intent: "follow-up" }), /Timed out waiting for Powerline queue store lock/);
|
|
64
|
-
assert.equal(existsSync(lockPath), true);
|
|
65
|
-
}));
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { shouldShowStartupWelcome, isStaleExtensionContextError } from "../lifecycle.ts";
|
|
4
|
-
|
|
5
|
-
test("startup welcome only shows on startup when enabled", () => {
|
|
6
|
-
assert.equal(shouldShowStartupWelcome("startup", true), true);
|
|
7
|
-
assert.equal(shouldShowStartupWelcome("reload", true), false);
|
|
8
|
-
assert.equal(shouldShowStartupWelcome("startup", false), false);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
test("stale extension context errors are recognized", () => {
|
|
12
|
-
assert.equal(isStaleExtensionContextError(new Error("This extension instance is stale")), true);
|
|
13
|
-
assert.equal(isStaleExtensionContextError(new Error("other")), false);
|
|
14
|
-
});
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { readFileSync } from "node:fs";
|
|
4
|
-
import { NERD_ICONS } from "../icons.ts";
|
|
5
|
-
import { isStaleExtensionContextError, shouldShowStartupWelcome } from "../lifecycle.ts";
|
|
6
|
-
import { __resetCurrencyRatesForTest, __setCurrencyRatesForTest } from "../currency-rates.ts";
|
|
7
|
-
import { renderSegment } from "../segments.ts";
|
|
8
|
-
import type { SegmentContext } from "../types.ts";
|
|
9
|
-
|
|
10
|
-
const source = readFileSync(new URL("../index.ts", import.meta.url), "utf-8");
|
|
11
|
-
const originalNerdFonts = process.env.POWERLINE_NERD_FONTS;
|
|
12
|
-
process.env.POWERLINE_NERD_FONTS = "0";
|
|
13
|
-
|
|
14
|
-
test.after(() => {
|
|
15
|
-
if (originalNerdFonts === undefined) {
|
|
16
|
-
delete process.env.POWERLINE_NERD_FONTS;
|
|
17
|
-
} else {
|
|
18
|
-
process.env.POWERLINE_NERD_FONTS = originalNerdFonts;
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
function plainThemeText(_color: string, text: string): string {
|
|
23
|
-
return text;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function stripAnsi(text: string): string {
|
|
27
|
-
return text.replace(/\x1b\[[0-9;]*m/g, "");
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function createSegmentContext(overrides: Partial<SegmentContext> = {}): SegmentContext {
|
|
31
|
-
return {
|
|
32
|
-
model: { id: "claude-sonnet-4", name: "Claude Sonnet 4" },
|
|
33
|
-
thinkingLevel: "off",
|
|
34
|
-
sessionId: undefined,
|
|
35
|
-
cwd: "/tmp/project",
|
|
36
|
-
usageStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, subagentCost: 0 },
|
|
37
|
-
contextTokens: 0,
|
|
38
|
-
contextPercent: 0,
|
|
39
|
-
contextWindow: 0,
|
|
40
|
-
contextApproximate: false,
|
|
41
|
-
autoCompactEnabled: true,
|
|
42
|
-
customCompactionEnabled: false,
|
|
43
|
-
usingSubscription: false,
|
|
44
|
-
sessionStartTime: Date.now(),
|
|
45
|
-
shellModeActive: false,
|
|
46
|
-
shellRunning: false,
|
|
47
|
-
shellName: null,
|
|
48
|
-
shellCwd: null,
|
|
49
|
-
git: { branch: null, staged: 0, unstaged: 0, untracked: 0 },
|
|
50
|
-
extensionStatuses: new Map(),
|
|
51
|
-
hiddenExtensionStatusKeys: new Set(),
|
|
52
|
-
customItemsById: new Map(),
|
|
53
|
-
options: {},
|
|
54
|
-
theme: { fg: plainThemeText },
|
|
55
|
-
colors: {},
|
|
56
|
-
...overrides,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
test("model segment can show provider-qualified ids", () => {
|
|
61
|
-
const normal = renderSegment("model", createSegmentContext());
|
|
62
|
-
const qualified = renderSegment("model", createSegmentContext({
|
|
63
|
-
model: { id: "claude-sonnet-4", name: "Claude Sonnet 4", provider: "anthropic" },
|
|
64
|
-
options: { model: { display: "qualified" } },
|
|
65
|
-
}));
|
|
66
|
-
const alreadyQualified = renderSegment("model", createSegmentContext({
|
|
67
|
-
model: { id: "openai/gpt-4.1", name: "GPT 4.1", provider: "openai" },
|
|
68
|
-
options: { model: { display: "qualified" } },
|
|
69
|
-
}));
|
|
70
|
-
|
|
71
|
-
assert.equal(stripAnsi(normal.content), "Sonnet 4");
|
|
72
|
-
assert.equal(stripAnsi(qualified.content), "anthropic/claude-sonnet-4");
|
|
73
|
-
assert.equal(stripAnsi(alreadyQualified.content), "openai/gpt-4.1");
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("cost segment supports subscription display modes and converted currencies", () => {
|
|
77
|
-
__setCurrencyRatesForTest({ CNY: 7.2 });
|
|
78
|
-
|
|
79
|
-
const subscription = renderSegment("cost", createSegmentContext({
|
|
80
|
-
usingSubscription: true,
|
|
81
|
-
usageStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0.42, subagentCost: 0 },
|
|
82
|
-
}));
|
|
83
|
-
const reportedCost = renderSegment("cost", createSegmentContext({
|
|
84
|
-
usingSubscription: true,
|
|
85
|
-
usageStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0.42, subagentCost: 0 },
|
|
86
|
-
options: { cost: { subscriptionDisplay: "reported-cost" } },
|
|
87
|
-
}));
|
|
88
|
-
const both = renderSegment("cost", createSegmentContext({
|
|
89
|
-
usingSubscription: true,
|
|
90
|
-
usageStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0.42, subagentCost: 0 },
|
|
91
|
-
options: { cost: { subscriptionDisplay: "both" } },
|
|
92
|
-
}));
|
|
93
|
-
const zeroReported = renderSegment("cost", createSegmentContext({
|
|
94
|
-
usingSubscription: true,
|
|
95
|
-
options: { cost: { subscriptionDisplay: "reported-cost" } },
|
|
96
|
-
}));
|
|
97
|
-
const zeroBoth = renderSegment("cost", createSegmentContext({
|
|
98
|
-
usingSubscription: true,
|
|
99
|
-
options: { cost: { subscriptionDisplay: "both" } },
|
|
100
|
-
}));
|
|
101
|
-
const withSubagentCost = renderSegment("cost", createSegmentContext({
|
|
102
|
-
usageStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0.42, subagentCost: 0.58 },
|
|
103
|
-
}));
|
|
104
|
-
const convertedCurrency = renderSegment("cost", createSegmentContext({
|
|
105
|
-
usageStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 1, subagentCost: 0.25 },
|
|
106
|
-
options: { cost: { currency: "CNY" } },
|
|
107
|
-
}));
|
|
108
|
-
|
|
109
|
-
__resetCurrencyRatesForTest();
|
|
110
|
-
|
|
111
|
-
assert.deepEqual(subscription, { content: "(sub)", visible: true });
|
|
112
|
-
assert.deepEqual(reportedCost, { content: "$0.42", visible: true });
|
|
113
|
-
assert.deepEqual(both, { content: "$0.42 (sub)", visible: true });
|
|
114
|
-
assert.deepEqual(zeroReported, { content: "(sub)", visible: true });
|
|
115
|
-
assert.deepEqual(zeroBoth, { content: "(sub)", visible: true });
|
|
116
|
-
assert.deepEqual(withSubagentCost, { content: "$1.00", visible: true });
|
|
117
|
-
assert.deepEqual(convertedCurrency, { content: "¥9.00", visible: true });
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
test("context segment shows used tokens, maximum, and percentage", () => {
|
|
121
|
-
const context = renderSegment("context_pct", createSegmentContext({
|
|
122
|
-
contextTokens: 4_500,
|
|
123
|
-
contextPercent: 1.7,
|
|
124
|
-
contextWindow: 272_000,
|
|
125
|
-
}));
|
|
126
|
-
|
|
127
|
-
assert.equal(stripAnsi(context.content), "4.5k/272k (1.7%)");
|
|
128
|
-
assert.equal(context.visible, true);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test("Nerd Font context icon uses stable database glyph", () => {
|
|
132
|
-
assert.equal(NERD_ICONS.context, "\uF1C0");
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
test("startup welcome predicate respects powerline.welcome false", () => {
|
|
136
|
-
assert.equal(shouldShowStartupWelcome("startup", true), true);
|
|
137
|
-
assert.equal(shouldShowStartupWelcome("startup", false), false);
|
|
138
|
-
assert.equal(shouldShowStartupWelcome("resume", true), false);
|
|
139
|
-
// Guide display modes take precedence; the welcome overlay only shows when the
|
|
140
|
-
// guide is off and shouldShowStartupWelcome passes.
|
|
141
|
-
assert.match(source, /setupCustomEditor\(ctx\);/);
|
|
142
|
-
assert.match(source, /if \(guideDisplayMode === "full"\)/);
|
|
143
|
-
assert.match(source, /else if \(shouldShowStartupWelcome\(event\.reason, config\.welcome\)\)/);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
test("stale ctx guard handles old and new Pi messages on agent_end", () => {
|
|
147
|
-
assert.equal(isStaleExtensionContextError(new Error("This extension instance is stale after session replacement or reload.")), true);
|
|
148
|
-
assert.equal(isStaleExtensionContextError(new Error("This extension ctx is stale after session replacement or reload.")), true);
|
|
149
|
-
assert.equal(isStaleExtensionContextError(new Error("ctx.hasUI failed for another reason")), false);
|
|
150
|
-
assert.match(source, /let hasUI = false;\r?\n\s+try \{\r?\n\s+hasUI = Boolean\(ctx\.hasUI\);/);
|
|
151
|
-
assert.match(source, /if \(!isStaleExtensionContextError\(error\)\) throw error;\r?\n\s+currentCtx = null;\r?\n\s+return;/);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test("post-compaction queue delivery does not read ctx.cwd from the delayed callback", () => {
|
|
155
|
-
assert.match(source, /const queueContext = getQueueContext\(ctx\);\r?\n\s+const scheduledGeneration = sessionGeneration;\r?\n\s+queueDeliveryTimer = setTimeout/);
|
|
156
|
-
assert.match(source, /if \(scheduledGeneration !== sessionGeneration\) return;\r?\n\s+try \{\r?\n\s+const item = queueStore\.queuedDeliveryItems\(queueContext, "post-compact"\)\[0\];/);
|
|
157
|
-
assert.match(source, /catch \(error\) \{\r?\n\s+if \(!isStaleExtensionContextError\(error\)\) throw error;\r?\n\s+currentCtx = null;/);
|
|
158
|
-
assert.match(source, /if \(isStaleExtensionContextError\(error\)\) \{\r?\n\s+if \(!sent\) queueStore\.update\(item\.id, \{ status: "queued", error: undefined \}\);/);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
test("editor-adjacent widgets cache queue and last-prompt work", () => {
|
|
162
|
-
assert.match(source, /const QUEUE_SUMMARY_CACHE_TTL_MS = 250;/);
|
|
163
|
-
assert.match(source, /queueSummaryCache = null;\r?\n\s+requestImmediateStatusRender/);
|
|
164
|
-
assert.match(source, /lastPromptRenderCache\.source === lastUserPrompt/);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
test("unknown context estimates are event-scoped and cleared before compaction", () => {
|
|
168
|
-
assert.match(source, /approximateContextUsage = event\.reason === "reload" \? estimateUnknownContextUsage\(ctx\) : null;/);
|
|
169
|
-
assert.match(source, /unknownCoreFallback: approximateContextUsage,/);
|
|
170
|
-
assert.match(source, /contextApproximate = coreContextUsage\?\.contextTokens === null && approximateContextUsage !== null;/);
|
|
171
|
-
assert.match(source, /pi\.on\("session_before_compact", async \(_event, ctx\) => \{\r?\n\s+powerlineCompacting = true;\r?\n\s+currentCtx = ctx;\r?\n\s+isStreaming = false;\r?\n\s+liveAssistantUsage = null;\r?\n\s+approximateContextUsage = null;\r?\n\s+coreContextUsageCache\.reset\(\);/);
|
|
172
|
-
assert.match(source, /pi\.on\("session_compact", async \(event, ctx\) => \{\r?\n\s+powerlineCompacting = false;\r?\n\s+currentCtx = ctx;\r?\n\s+isStreaming = false;\r?\n\s+liveAssistantUsage = null;\r?\n\s+approximateContextUsage = estimateUnknownContextUsage\(ctx\);\r?\n\s+coreContextUsageCache\.reset\(\);/);
|
|
173
|
-
});
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { readAsyncSubagentUsage, readSubagentToolResultUsage } from "../session-usage.ts";
|
|
4
|
-
|
|
5
|
-
test("reads all completed subagent usage from a parent tool result", () => {
|
|
6
|
-
assert.deepEqual(
|
|
7
|
-
readSubagentToolResultUsage({
|
|
8
|
-
role: "toolResult",
|
|
9
|
-
toolName: "subagent",
|
|
10
|
-
details: {
|
|
11
|
-
totalChildUsage: {
|
|
12
|
-
input: 120,
|
|
13
|
-
output: 34,
|
|
14
|
-
cacheRead: 56,
|
|
15
|
-
cacheWrite: 7,
|
|
16
|
-
cost: 0.123,
|
|
17
|
-
turns: 2,
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
}),
|
|
21
|
-
{ input: 120, output: 34, cacheRead: 56, cacheWrite: 7, cost: 0.123 },
|
|
22
|
-
);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test("reads asynchronous completion usage", () => {
|
|
26
|
-
assert.deepEqual(
|
|
27
|
-
readAsyncSubagentUsage({
|
|
28
|
-
id: "async-123",
|
|
29
|
-
sessionId: "parent-session",
|
|
30
|
-
totalChildUsage: { input: 20, output: 8, cacheRead: 13, cacheWrite: 0, cost: 0.02 },
|
|
31
|
-
}),
|
|
32
|
-
{
|
|
33
|
-
runId: "async-123",
|
|
34
|
-
sessionId: "parent-session",
|
|
35
|
-
usage: { input: 20, output: 8, cacheRead: 13, cacheWrite: 0, cost: 0.02 },
|
|
36
|
-
},
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("does not treat other tool results or malformed usage as child usage", () => {
|
|
41
|
-
assert.equal(readSubagentToolResultUsage({ role: "toolResult", toolName: "bash", details: {} }), null);
|
|
42
|
-
assert.equal(readSubagentToolResultUsage({
|
|
43
|
-
role: "toolResult",
|
|
44
|
-
toolName: "subagent",
|
|
45
|
-
details: { totalChildUsage: { input: 1, output: 2 } },
|
|
46
|
-
}), null);
|
|
47
|
-
});
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { readFileSync } from "node:fs";
|
|
4
|
-
import { matchesStashShortcutInput } from "../shortcuts.ts";
|
|
5
|
-
|
|
6
|
-
const source = readFileSync(new URL("../index.ts", import.meta.url), "utf-8");
|
|
7
|
-
|
|
8
|
-
test("stash shortcut matches Alt+S encodings without consuming literal sharp-S by default", () => {
|
|
9
|
-
assert.equal(matchesStashShortcutInput("ß"), false);
|
|
10
|
-
assert.equal(matchesStashShortcutInput("ß", { includePrintableSharpS: true }), true);
|
|
11
|
-
|
|
12
|
-
for (const data of [
|
|
13
|
-
"\x1bs",
|
|
14
|
-
"\x1bS",
|
|
15
|
-
"\x1b[115;3u",
|
|
16
|
-
"\x1b[83;3u",
|
|
17
|
-
"\x1b[27;3;115~",
|
|
18
|
-
"\x1b[27;3;83~",
|
|
19
|
-
]) {
|
|
20
|
-
assert.equal(matchesStashShortcutInput(data), true, data);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
assert.equal(matchesStashShortcutInput("s"), false);
|
|
24
|
-
assert.equal(matchesStashShortcutInput("\x1b[115;5u"), false);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test("stash shortcut stays in terminal/editor fallback routing", () => {
|
|
28
|
-
assert.doesNotMatch(source, /pi\.registerShortcut\("alt\+s"/);
|
|
29
|
-
assert.match(source, /matchesStashShortcutInput\(data, \{ includePrintableSharpS: config\.stashSharpSShortcut \}\)/);
|
|
30
|
-
assert.match(source, /ctx\.ui\.onTerminalInput\(\(data: string\) =>/);
|
|
31
|
-
assert.match(source, /if \(isStashShortcutInput\(data\)\)/);
|
|
32
|
-
assert.match(source, /function stashOrRestoreEditorText\(ctx: any\): void/);
|
|
33
|
-
assert.match(source, /function isPromptHistoryShortcutInput\(data: string\): boolean/);
|
|
34
|
-
assert.match(source, /matchesConfiguredShortcut\(data, resolvedShortcuts\.stashHistory\)/);
|
|
35
|
-
assert.doesNotMatch(source, /data === "\\x1b\\b"/);
|
|
36
|
-
assert.doesNotMatch(source, /data === "\\x1b\\x7f"/);
|
|
37
|
-
});
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { renderSegment } from "../segments.ts";
|
|
4
|
-
import { rainbow } from "../theme.ts";
|
|
5
|
-
import type { ColorScheme, SegmentContext, ThemeLike } from "../types.ts";
|
|
6
|
-
|
|
7
|
-
function hexAnsi(hex: `#${string}`): string {
|
|
8
|
-
const value = hex.slice(1);
|
|
9
|
-
const r = parseInt(value.slice(0, 2), 16);
|
|
10
|
-
const g = parseInt(value.slice(2, 4), 16);
|
|
11
|
-
const b = parseInt(value.slice(4, 6), 16);
|
|
12
|
-
return `\x1b[38;2;${r};${g};${b}m`;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function createSegmentContext(thinkingLevel: string, colors: ColorScheme): SegmentContext {
|
|
16
|
-
return {
|
|
17
|
-
model: undefined,
|
|
18
|
-
thinkingLevel,
|
|
19
|
-
sessionId: undefined,
|
|
20
|
-
usageStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, subagentCost: 0 },
|
|
21
|
-
contextTokens: 0,
|
|
22
|
-
contextPercent: 0,
|
|
23
|
-
contextWindow: 0,
|
|
24
|
-
contextApproximate: false,
|
|
25
|
-
autoCompactEnabled: true,
|
|
26
|
-
customCompactionEnabled: false,
|
|
27
|
-
usingSubscription: false,
|
|
28
|
-
sessionStartTime: Date.now(),
|
|
29
|
-
shellModeActive: false,
|
|
30
|
-
shellRunning: false,
|
|
31
|
-
shellName: null,
|
|
32
|
-
shellCwd: null,
|
|
33
|
-
git: { branch: null, staged: 0, unstaged: 0, untracked: 0 },
|
|
34
|
-
extensionStatuses: new Map(),
|
|
35
|
-
hiddenExtensionStatusKeys: new Set(),
|
|
36
|
-
customItemsById: new Map(),
|
|
37
|
-
options: {},
|
|
38
|
-
theme: {
|
|
39
|
-
fg() {
|
|
40
|
-
throw new Error("unexpected theme color lookup in thinking segment test");
|
|
41
|
-
},
|
|
42
|
-
} satisfies ThemeLike,
|
|
43
|
-
colors,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
test("thinking segment uses per-level colors for off through medium", () => {
|
|
48
|
-
const colors: ColorScheme = {
|
|
49
|
-
thinking: "#111111",
|
|
50
|
-
thinkingMinimal: "#222222",
|
|
51
|
-
thinkingLow: "#333333",
|
|
52
|
-
thinkingMedium: "#444444",
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const off = renderSegment("thinking", createSegmentContext("off", colors));
|
|
56
|
-
const minimal = renderSegment("thinking", createSegmentContext("minimal", colors));
|
|
57
|
-
const low = renderSegment("thinking", createSegmentContext("low", colors));
|
|
58
|
-
const medium = renderSegment("thinking", createSegmentContext("medium", colors));
|
|
59
|
-
|
|
60
|
-
assert.equal(off.content, `${hexAnsi("#111111")}think:off\x1b[0m`);
|
|
61
|
-
assert.equal(minimal.content, `${hexAnsi("#222222")}think:min\x1b[0m`);
|
|
62
|
-
assert.equal(low.content, `${hexAnsi("#333333")}think:low\x1b[0m`);
|
|
63
|
-
assert.equal(medium.content, `${hexAnsi("#444444")}think:med\x1b[0m`);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test("thinking segment uses rainbow styling for high through max", () => {
|
|
67
|
-
const colors: ColorScheme = { thinking: "#111111" };
|
|
68
|
-
|
|
69
|
-
for (const level of ["high", "xhigh", "max"]) {
|
|
70
|
-
const rendered = renderSegment("thinking", createSegmentContext(level, colors));
|
|
71
|
-
assert.deepEqual(rendered, {
|
|
72
|
-
content: rainbow(`think:${level}`),
|
|
73
|
-
visible: true,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
});
|