@selesai/code 0.5.13 → 0.5.15

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 (297) hide show
  1. package/README.md +186 -0
  2. package/dist/bun/cli.d.ts.map +1 -1
  3. package/dist/bun/cli.js +2 -0
  4. package/dist/bun/cli.js.map +1 -1
  5. package/dist/cli/args.d.ts.map +1 -1
  6. package/dist/cli/args.js +3 -1
  7. package/dist/cli/args.js.map +1 -1
  8. package/dist/cli/list-models.d.ts +2 -2
  9. package/dist/cli/list-models.d.ts.map +1 -1
  10. package/dist/cli/list-models.js +3 -3
  11. package/dist/cli/list-models.js.map +1 -1
  12. package/dist/config.d.ts +1 -1
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js +2 -2
  15. package/dist/config.js.map +1 -1
  16. package/dist/core/agent-session-auto-handoff.test.d.ts.map +1 -1
  17. package/dist/core/agent-session-auto-handoff.test.js +2 -2
  18. package/dist/core/agent-session-auto-handoff.test.js.map +1 -1
  19. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  20. package/dist/core/agent-session-runtime.js +3 -0
  21. package/dist/core/agent-session-runtime.js.map +1 -1
  22. package/dist/core/agent-session-services.d.ts +3 -6
  23. package/dist/core/agent-session-services.d.ts.map +1 -1
  24. package/dist/core/agent-session-services.js +23 -9
  25. package/dist/core/agent-session-services.js.map +1 -1
  26. package/dist/core/agent-session.d.ts +28 -10
  27. package/dist/core/agent-session.d.ts.map +1 -1
  28. package/dist/core/agent-session.js +136 -90
  29. package/dist/core/agent-session.js.map +1 -1
  30. package/dist/core/auth-storage.d.ts +20 -98
  31. package/dist/core/auth-storage.d.ts.map +1 -1
  32. package/dist/core/auth-storage.js +59 -251
  33. package/dist/core/auth-storage.js.map +1 -1
  34. package/dist/core/built-in-extensions.d.ts +3 -0
  35. package/dist/core/built-in-extensions.d.ts.map +1 -0
  36. package/dist/core/built-in-extensions.js +3 -0
  37. package/dist/core/built-in-extensions.js.map +1 -0
  38. package/dist/core/cache-stats.d.ts +2 -2
  39. package/dist/core/cache-stats.d.ts.map +1 -1
  40. package/dist/core/cache-stats.js +1 -1
  41. package/dist/core/cache-stats.js.map +1 -1
  42. package/dist/core/compaction/branch-summarization.d.ts +8 -2
  43. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  44. package/dist/core/compaction/branch-summarization.js +8 -11
  45. package/dist/core/compaction/branch-summarization.js.map +1 -1
  46. package/dist/core/compaction/compaction.d.ts +19 -3
  47. package/dist/core/compaction/compaction.d.ts.map +1 -1
  48. package/dist/core/compaction/compaction.js +63 -26
  49. package/dist/core/compaction/compaction.js.map +1 -1
  50. package/dist/core/compaction/utils.d.ts +1 -1
  51. package/dist/core/compaction/utils.d.ts.map +1 -1
  52. package/dist/core/compaction/utils.js +6 -17
  53. package/dist/core/compaction/utils.js.map +1 -1
  54. package/dist/core/extensions/loader.d.ts.map +1 -1
  55. package/dist/core/extensions/loader.js +21 -4
  56. package/dist/core/extensions/loader.js.map +1 -1
  57. package/dist/core/extensions/runner.d.ts +4 -1
  58. package/dist/core/extensions/runner.d.ts.map +1 -1
  59. package/dist/core/extensions/runner.js +39 -0
  60. package/dist/core/extensions/runner.js.map +1 -1
  61. package/dist/core/extensions/types.d.ts +24 -3
  62. package/dist/core/extensions/types.d.ts.map +1 -1
  63. package/dist/core/extensions/types.js.map +1 -1
  64. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  65. package/dist/core/extensions/wrapper.js +22 -3
  66. package/dist/core/extensions/wrapper.js.map +1 -1
  67. package/dist/core/keybindings.d.ts +8 -3
  68. package/dist/core/keybindings.d.ts.map +1 -1
  69. package/dist/core/keybindings.js +7 -3
  70. package/dist/core/keybindings.js.map +1 -1
  71. package/dist/core/llama/client.d.ts +61 -0
  72. package/dist/core/llama/client.d.ts.map +1 -0
  73. package/dist/core/llama/client.js +302 -0
  74. package/dist/core/llama/client.js.map +1 -0
  75. package/dist/core/llama/huggingface.d.ts +23 -0
  76. package/dist/core/llama/huggingface.d.ts.map +1 -0
  77. package/dist/core/llama/huggingface.js +141 -0
  78. package/dist/core/llama/huggingface.js.map +1 -0
  79. package/dist/core/llama/index.d.ts +3 -0
  80. package/dist/core/llama/index.d.ts.map +1 -0
  81. package/dist/core/llama/index.js +208 -0
  82. package/dist/core/llama/index.js.map +1 -0
  83. package/dist/core/llama/provider.d.ts +10 -0
  84. package/dist/core/llama/provider.d.ts.map +1 -0
  85. package/dist/core/llama/provider.js +102 -0
  86. package/dist/core/llama/provider.js.map +1 -0
  87. package/dist/core/llama/ui.d.ts +42 -0
  88. package/dist/core/llama/ui.d.ts.map +1 -0
  89. package/dist/core/llama/ui.js +416 -0
  90. package/dist/core/llama/ui.js.map +1 -0
  91. package/dist/core/model-config.d.ts +513 -0
  92. package/dist/core/model-config.d.ts.map +1 -0
  93. package/dist/core/model-config.js +272 -0
  94. package/dist/core/model-config.js.map +1 -0
  95. package/dist/core/model-registry.d.ts +20 -126
  96. package/dist/core/model-registry.d.ts.map +1 -1
  97. package/dist/core/model-registry.js +62 -780
  98. package/dist/core/model-registry.js.map +1 -1
  99. package/dist/core/model-resolver.d.ts +6 -6
  100. package/dist/core/model-resolver.d.ts.map +1 -1
  101. package/dist/core/model-resolver.js +21 -18
  102. package/dist/core/model-resolver.js.map +1 -1
  103. package/dist/core/model-runtime.d.ts +83 -0
  104. package/dist/core/model-runtime.d.ts.map +1 -0
  105. package/dist/core/model-runtime.js +451 -0
  106. package/dist/core/model-runtime.js.map +1 -0
  107. package/dist/core/models-store.d.ts +17 -0
  108. package/dist/core/models-store.d.ts.map +1 -0
  109. package/dist/core/models-store.js +45 -0
  110. package/dist/core/models-store.js.map +1 -0
  111. package/dist/core/package-manager.d.ts.map +1 -1
  112. package/dist/core/package-manager.js +7 -2
  113. package/dist/core/package-manager.js.map +1 -1
  114. package/dist/core/prompt-templates.d.ts +1 -0
  115. package/dist/core/prompt-templates.d.ts.map +1 -1
  116. package/dist/core/prompt-templates.js +4 -4
  117. package/dist/core/prompt-templates.js.map +1 -1
  118. package/dist/core/provider-composer.d.ts +55 -0
  119. package/dist/core/provider-composer.d.ts.map +1 -0
  120. package/dist/core/provider-composer.js +375 -0
  121. package/dist/core/provider-composer.js.map +1 -0
  122. package/dist/core/radius.d.ts +2 -0
  123. package/dist/core/radius.d.ts.map +1 -0
  124. package/dist/core/radius.js +2 -0
  125. package/dist/core/radius.js.map +1 -0
  126. package/dist/core/remote-catalog-provider.d.ts +5 -0
  127. package/dist/core/remote-catalog-provider.d.ts.map +1 -0
  128. package/dist/core/remote-catalog-provider.js +102 -0
  129. package/dist/core/remote-catalog-provider.js.map +1 -0
  130. package/dist/core/resource-loader.d.ts.map +1 -1
  131. package/dist/core/resource-loader.js +1 -0
  132. package/dist/core/resource-loader.js.map +1 -1
  133. package/dist/core/runtime-credentials.d.ts +17 -0
  134. package/dist/core/runtime-credentials.d.ts.map +1 -0
  135. package/dist/core/runtime-credentials.js +42 -0
  136. package/dist/core/runtime-credentials.js.map +1 -0
  137. package/dist/core/sdk.d.ts +4 -7
  138. package/dist/core/sdk.d.ts.map +1 -1
  139. package/dist/core/sdk.js +18 -25
  140. package/dist/core/sdk.js.map +1 -1
  141. package/dist/core/session-manager.d.ts +12 -7
  142. package/dist/core/session-manager.d.ts.map +1 -1
  143. package/dist/core/session-manager.js +103 -27
  144. package/dist/core/session-manager.js.map +1 -1
  145. package/dist/core/system-prompt.d.ts.map +1 -1
  146. package/dist/core/system-prompt.js +2 -12
  147. package/dist/core/system-prompt.js.map +1 -1
  148. package/dist/core/tools/read.d.ts.map +1 -1
  149. package/dist/core/tools/read.js +1 -1
  150. package/dist/core/tools/read.js.map +1 -1
  151. package/dist/core/usage-totals.d.ts +19 -0
  152. package/dist/core/usage-totals.d.ts.map +1 -0
  153. package/dist/core/usage-totals.js +52 -0
  154. package/dist/core/usage-totals.js.map +1 -0
  155. package/dist/extensions/index.d.ts +3 -0
  156. package/dist/extensions/index.d.ts.map +1 -0
  157. package/dist/extensions/index.js +9 -0
  158. package/dist/extensions/index.js.map +1 -0
  159. package/dist/extensions/llama/client.d.ts +61 -0
  160. package/dist/extensions/llama/client.d.ts.map +1 -0
  161. package/dist/extensions/llama/client.js +309 -0
  162. package/dist/extensions/llama/client.js.map +1 -0
  163. package/dist/extensions/llama/huggingface.d.ts +23 -0
  164. package/dist/extensions/llama/huggingface.d.ts.map +1 -0
  165. package/dist/extensions/llama/huggingface.js +146 -0
  166. package/dist/extensions/llama/huggingface.js.map +1 -0
  167. package/dist/extensions/llama/index.d.ts +3 -0
  168. package/dist/extensions/llama/index.d.ts.map +1 -0
  169. package/dist/extensions/llama/index.js +211 -0
  170. package/dist/extensions/llama/index.js.map +1 -0
  171. package/dist/extensions/llama/provider.d.ts +10 -0
  172. package/dist/extensions/llama/provider.d.ts.map +1 -0
  173. package/dist/extensions/llama/provider.js +106 -0
  174. package/dist/extensions/llama/provider.js.map +1 -0
  175. package/dist/extensions/llama/ui.d.ts +42 -0
  176. package/dist/extensions/llama/ui.d.ts.map +1 -0
  177. package/dist/extensions/llama/ui.js +420 -0
  178. package/dist/extensions/llama/ui.js.map +1 -0
  179. package/dist/extensions/package.json +4 -3
  180. package/dist/extensions/pi-powerline-footer/README.md +1 -1
  181. package/dist/extensions/pi-powerline-footer/guide.ts +227 -0
  182. package/dist/extensions/pi-powerline-footer/index.ts +88 -55
  183. package/dist/extensions/pi-powerline-footer/tests/editor-responsiveness.test.ts +0 -78
  184. package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +102 -0
  185. package/dist/extensions/pi-powerline-footer/welcome.ts +65 -30
  186. package/dist/extensions/tokenin-onboarding.ts +3 -3
  187. package/dist/index.d.ts +4 -3
  188. package/dist/index.d.ts.map +1 -1
  189. package/dist/index.js +3 -3
  190. package/dist/index.js.map +1 -1
  191. package/dist/main.d.ts.map +1 -1
  192. package/dist/main.js +21 -19
  193. package/dist/main.js.map +1 -1
  194. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  195. package/dist/modes/interactive/components/assistant-message.js +22 -10
  196. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  197. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  198. package/dist/modes/interactive/components/custom-editor.js +1 -1
  199. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  200. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  201. package/dist/modes/interactive/components/footer.js +24 -23
  202. package/dist/modes/interactive/components/footer.js.map +1 -1
  203. package/dist/modes/interactive/components/login-dialog.d.ts +5 -5
  204. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  205. package/dist/modes/interactive/components/login-dialog.js +17 -8
  206. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  207. package/dist/modes/interactive/components/model-selector.d.ts +11 -4
  208. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  209. package/dist/modes/interactive/components/model-selector.js +72 -41
  210. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  211. package/dist/modes/interactive/components/oauth-selector.d.ts +4 -4
  212. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  213. package/dist/modes/interactive/components/oauth-selector.js +14 -27
  214. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  215. package/dist/modes/interactive/components/tree-selector.d.ts +5 -0
  216. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  217. package/dist/modes/interactive/components/tree-selector.js +49 -12
  218. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  219. package/dist/modes/interactive/interactive-mode.d.ts +8 -5
  220. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  221. package/dist/modes/interactive/interactive-mode.js +253 -225
  222. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  223. package/dist/modes/rpc/rpc-client.d.ts +4 -0
  224. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  225. package/dist/modes/rpc/rpc-client.js +7 -0
  226. package/dist/modes/rpc/rpc-client.js.map +1 -1
  227. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  228. package/dist/modes/rpc/rpc-mode.js +6 -2
  229. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  230. package/dist/modes/rpc/rpc-types.d.ts +11 -0
  231. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  232. package/dist/modes/rpc/rpc-types.js.map +1 -1
  233. package/dist/package-manager-cli.d.ts.map +1 -1
  234. package/dist/package-manager-cli.js +67 -5
  235. package/dist/package-manager-cli.js.map +1 -1
  236. package/dist/skills/caveman/SKILL.md +1 -0
  237. package/dist/skills/handoff/SKILL.md +1 -0
  238. package/dist/skills/handoff-text/SKILL.md +1 -0
  239. package/dist/skills/implanger/SKILL.md +1 -0
  240. package/dist/skills/improve-codebase/SKILL.md +1 -0
  241. package/dist/skills/planger/SKILL.md +1 -0
  242. package/dist/skills/ponytail/SKILL.md +1 -0
  243. package/dist/skills/ponytail-audit/SKILL.md +1 -0
  244. package/dist/skills/ponytail-debt/SKILL.md +1 -0
  245. package/dist/skills/ponytail-gain/SKILL.md +1 -0
  246. package/dist/skills/ponytail-help/SKILL.md +1 -0
  247. package/dist/skills/ponytail-review/SKILL.md +1 -0
  248. package/dist/skills/workflow-creation/SKILL.md +1 -0
  249. package/dist/utils/clipboard-native.d.ts +1 -0
  250. package/dist/utils/clipboard-native.d.ts.map +1 -1
  251. package/dist/utils/clipboard-native.js.map +1 -1
  252. package/dist/utils/clipboard.d.ts +2 -0
  253. package/dist/utils/clipboard.d.ts.map +1 -1
  254. package/dist/utils/clipboard.js +13 -0
  255. package/dist/utils/clipboard.js.map +1 -1
  256. package/package.json +26 -5
  257. package/dist/agents/architect.md +0 -217
  258. package/dist/agents/builder.md +0 -120
  259. package/dist/agents/commentator.md +0 -130
  260. package/dist/agents/explorer.md +0 -51
  261. package/dist/agents/recapper.md +0 -26
  262. package/dist/extensions/gitignore-guard.ts +0 -132
  263. package/dist/extensions/hooks/claude-codex-hooks.json +0 -44
  264. package/dist/extensions/hooks/copilot-hooks.json +0 -21
  265. package/dist/extensions/hooks/ponytail-activate.js +0 -91
  266. package/dist/extensions/hooks/ponytail-config.js +0 -122
  267. package/dist/extensions/hooks/ponytail-instructions.js +0 -94
  268. package/dist/extensions/hooks/ponytail-mode-tracker.js +0 -55
  269. package/dist/extensions/hooks/ponytail-runtime.js +0 -68
  270. package/dist/extensions/hooks/ponytail-statusline.ps1 +0 -21
  271. package/dist/extensions/hooks/ponytail-statusline.sh +0 -12
  272. package/dist/extensions/hooks/ponytail-subagent.js +0 -22
  273. package/dist/extensions/pi-extension/index.js +0 -189
  274. package/dist/extensions/pi-extension/package.json +0 -8
  275. package/dist/extensions/pi-extension/test/extension.test.js +0 -167
  276. package/dist/extensions/pi-extension/test/helpers.test.js +0 -92
  277. package/dist/extensions/pi-powerline-footer/welcome-dismiss.ts +0 -34
  278. package/dist/extensions/pi-subagents/agents/context-builder.md +0 -46
  279. package/dist/extensions/pi-subagents/agents/delegate.md +0 -12
  280. package/dist/extensions/pi-subagents/agents/oracle.md +0 -73
  281. package/dist/extensions/pi-subagents/agents/planner.md +0 -55
  282. package/dist/extensions/pi-subagents/agents/reviewer.md +0 -79
  283. package/dist/extensions/pi-subagents/agents/scout.md +0 -50
  284. package/dist/extensions/pi-subagents/agents/worker.md +0 -55
  285. package/dist/extensions/pi-subagents/src/runs/shared/spawn-timing.ts +0 -46
  286. package/dist/extensions/pi-subagents/test/unit/spawn-timing.test.ts +0 -45
  287. package/dist/extensions/pi-subagents/tsconfig.runner.json +0 -17
  288. package/dist/extensions/ponytail-pi-extension/index.js +0 -189
  289. package/dist/extensions/ponytail-pi-extension/package.json +0 -8
  290. package/dist/extensions/ponytail-pi-extension/test/extension.test.js +0 -167
  291. package/dist/extensions/ponytail-pi-extension/test/helpers.test.js +0 -92
  292. package/dist/extensions/prototype.ts +0 -713
  293. package/dist/extensions/question.ts +0 -350
  294. package/dist/extensions/quick.ts +0 -630
  295. package/dist/extensions/tps-tracker.ts +0 -280
  296. package/dist/extensions/workflow/task-validators.ts +0 -69
  297. package/dist/skills/selesai-default/SKILL.md +0 -16
@@ -0,0 +1,227 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+
4
+ export type GuideMode = "full" | "compact" | "off";
5
+ export type GuideDisplayMode = "full" | "compact" | "none";
6
+
7
+ export interface GuideFeature {
8
+ id: string;
9
+ section?: string;
10
+ title: string;
11
+ example: string;
12
+ introducedIn: string;
13
+ }
14
+
15
+ export interface GuidePreferences {
16
+ mode: GuideMode;
17
+ lastSeenVersion?: string;
18
+ }
19
+
20
+ export interface GuidePreferencesUpdate {
21
+ mode?: GuideMode;
22
+ lastSeenVersion?: string | null;
23
+ }
24
+
25
+ // Keep this map useful, not exhaustive: each row points at a real command, shortcut,
26
+ // or prompt from the root README without reproducing the README in the overlay.
27
+ export const GUIDE_FEATURES: readonly GuideFeature[] = [
28
+ {
29
+ id: "workflow-task",
30
+ section: "Start here",
31
+ title: "Task",
32
+ example: '/workflow-task "<goal>"',
33
+ introducedIn: "0.5.13",
34
+ },
35
+ {
36
+ id: "workflow-quick",
37
+ section: "Start here",
38
+ title: "Quick",
39
+ example: '/workflow-quick "<goal>"',
40
+ introducedIn: "0.5.13",
41
+ },
42
+ {
43
+ id: "workflow-prototype",
44
+ section: "Start here",
45
+ title: "Prototype",
46
+ example: '/workflow-prototype "<goal>"',
47
+ introducedIn: "0.5.13",
48
+ },
49
+ {
50
+ id: "settings",
51
+ section: "Start here",
52
+ title: "Settings",
53
+ example: "/settings · /guide compact|off|reset · /reload",
54
+ introducedIn: "0.5.13",
55
+ },
56
+ {
57
+ id: "subagents",
58
+ section: "Delegate / research",
59
+ title: "Plan",
60
+ example: '"Ask the architect to challenge this plan..."',
61
+ introducedIn: "0.5.13",
62
+ },
63
+ {
64
+ id: "parallel-review",
65
+ section: "Delegate / research",
66
+ title: "Review",
67
+ example: '"Run parallel commentator agents for correctness..."',
68
+ introducedIn: "0.5.13",
69
+ },
70
+ {
71
+ id: "web-research",
72
+ section: "Delegate / research",
73
+ title: "Research",
74
+ example: '"Ask the researcher to research <topic> and cite sources."',
75
+ introducedIn: "0.5.13",
76
+ },
77
+ {
78
+ id: "skills",
79
+ section: "Delegate / research",
80
+ title: "Skills",
81
+ example: "/skill:*",
82
+ introducedIn: "0.5.13",
83
+ },
84
+ {
85
+ id: "recovery",
86
+ section: "Recover",
87
+ title: "Recover",
88
+ example: "/undo · /tree · /fork · /cp <hash>",
89
+ introducedIn: "0.5.13",
90
+ },
91
+ {
92
+ id: "handoff",
93
+ section: "Recover",
94
+ title: "Handoff",
95
+ example: "/handoff-new Next: fix <problem>; start with a coding plan.",
96
+ introducedIn: "0.5.13",
97
+ },
98
+ ];
99
+
100
+ export const GUIDE_DISMISS_HINT = "Press any key to continue";
101
+
102
+ export const GUIDE_COMPACT_LINES = [
103
+ "/guide full · /settings",
104
+ '/workflow-task "<goal>" · /workflow-quick "<goal>"',
105
+ '/workflow-prototype "<goal>"',
106
+ '"Ask the researcher to research <topic>."',
107
+ "/skill:* · /undo · /handoff-new <next focus>",
108
+ ] as const;
109
+
110
+ export type GuideLine =
111
+ | { kind: "section"; text: string }
112
+ | { kind: "feature"; feature: GuideFeature };
113
+
114
+ export function getGuideFeatureLines(features: readonly GuideFeature[] = GUIDE_FEATURES): GuideLine[] {
115
+ return features.flatMap((feature, index) => {
116
+ const previous = features[index - 1];
117
+ const section = feature.section && feature.section !== previous?.section
118
+ ? [{ kind: "section" as const, text: feature.section }]
119
+ : [];
120
+ return [...section, { kind: "feature" as const, feature }];
121
+ });
122
+ }
123
+
124
+ function isRecord(value: unknown): value is Record<string, unknown> {
125
+ return typeof value === "object" && value !== null && !Array.isArray(value);
126
+ }
127
+
128
+ function isGuideMode(value: unknown): value is GuideMode {
129
+ return value === "full" || value === "compact" || value === "off";
130
+ }
131
+
132
+ function versionParts(version: string): [number, number, number] {
133
+ const match = version.trim().match(/^(\d+)\.(\d+)\.(\d+)/);
134
+ return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : [0, 0, 0];
135
+ }
136
+
137
+ export function compareGuideVersions(left: string, right: string): number {
138
+ const leftParts = versionParts(left);
139
+ const rightParts = versionParts(right);
140
+ for (let i = 0; i < leftParts.length; i++) {
141
+ if (leftParts[i] !== rightParts[i]) return leftParts[i] - rightParts[i];
142
+ }
143
+ return 0;
144
+ }
145
+
146
+ export function getGuidePreferences(settings: unknown): GuidePreferences {
147
+ const raw = isRecord(settings) && isRecord(settings.selesaiGuide)
148
+ ? settings.selesaiGuide
149
+ : {};
150
+
151
+ return {
152
+ mode: isGuideMode(raw.mode) ? raw.mode : "full",
153
+ lastSeenVersion: typeof raw.lastSeenVersion === "string" ? raw.lastSeenVersion : undefined,
154
+ };
155
+ }
156
+
157
+ export function getNewGuideFeatures(
158
+ lastSeenVersion: string | undefined,
159
+ currentVersion: string,
160
+ features: readonly GuideFeature[] = GUIDE_FEATURES,
161
+ ): GuideFeature[] {
162
+ if (!lastSeenVersion || compareGuideVersions(lastSeenVersion, currentVersion) >= 0) {
163
+ return [];
164
+ }
165
+
166
+ return features.filter((feature) => (
167
+ compareGuideVersions(feature.introducedIn, lastSeenVersion) > 0
168
+ && compareGuideVersions(feature.introducedIn, currentVersion) <= 0
169
+ ));
170
+ }
171
+
172
+ export function resolveGuideDisplayMode(options: {
173
+ reason: string;
174
+ hasUI: boolean;
175
+ guideMode: GuideMode;
176
+ }): GuideDisplayMode {
177
+ if (!options.hasUI || options.reason === "reload" || options.guideMode === "off") {
178
+ return "none";
179
+ }
180
+
181
+ return options.guideMode === "compact" ? "compact" : "full";
182
+ }
183
+
184
+ function readSettingsFile(settingsPath: string): Record<string, unknown> | null {
185
+ if (!existsSync(settingsPath)) return {};
186
+
187
+ try {
188
+ const parsed = JSON.parse(readFileSync(settingsPath, "utf8"));
189
+ return isRecord(parsed) ? parsed : null;
190
+ } catch {
191
+ return null;
192
+ }
193
+ }
194
+
195
+ export function saveGuidePreferences(
196
+ update: GuidePreferencesUpdate,
197
+ settingsPath: string,
198
+ ): boolean {
199
+ const settings = readSettingsFile(settingsPath);
200
+ if (!settings) return false;
201
+
202
+ const current = getGuidePreferences(settings);
203
+ const next: Record<string, unknown> = {
204
+ mode: update.mode ?? current.mode,
205
+ };
206
+ const lastSeenVersion = update.lastSeenVersion === null
207
+ ? undefined
208
+ : update.lastSeenVersion ?? current.lastSeenVersion;
209
+ if (lastSeenVersion) next.lastSeenVersion = lastSeenVersion;
210
+
211
+ settings.selesaiGuide = next;
212
+
213
+ try {
214
+ mkdirSync(dirname(settingsPath), { recursive: true });
215
+ writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
216
+ return true;
217
+ } catch {
218
+ return false;
219
+ }
220
+ }
221
+
222
+ export function markGuideVersionSeen(
223
+ version: string,
224
+ settingsPath: string,
225
+ ): boolean {
226
+ return saveGuidePreferences({ lastSeenVersion: version }, settingsPath);
227
+ }
@@ -3,6 +3,7 @@ import {
3
3
  CONFIG_DIR_NAME,
4
4
  getAgentDir,
5
5
  getSettingsPath,
6
+ VERSION,
6
7
  type ExtensionAPI,
7
8
  type ReadonlyFooterDataProvider,
8
9
  type Theme,
@@ -33,7 +34,7 @@ import { renderSegment } from "./segments.ts";
33
34
  import { getGitStatus, invalidateGitStatus, invalidateGitBranch } from "./git-status.ts";
34
35
  import { ansi, getFgAnsiCode } from "./colors.ts";
35
36
  import { WelcomeComponent, WelcomeHeader, discoverLoadedCounts, getRecentSessions } from "./welcome.ts";
36
- import { createWelcomeDismissScheduler } from "./welcome-dismiss.ts";
37
+ import { getGuidePreferences, getNewGuideFeatures, markGuideVersionSeen, resolveGuideDisplayMode, saveGuidePreferences, type GuideFeature } from "./guide.ts";
37
38
  import { createRenderScheduler } from "./render-scheduler.ts";
38
39
  import { readCoreContextUsage } from "./context-usage.ts";
39
40
  import { renderFixedEditorCluster } from "./fixed-editor/cluster.ts";
@@ -940,6 +941,7 @@ export default function powerlineFooter(pi: ExtensionAPI) {
940
941
  let fixedWidgetContainerBelow: any = null;
941
942
  let stashShortcutInputUnsubscribe: (() => void) | null = null;
942
943
  let dismissWelcomeOverlay: (() => void) | null = null;
944
+ let guideOverlayBlocking = false;
943
945
  let welcomeHeaderActive = false;
944
946
  let welcomeOverlayShouldDismiss = false;
945
947
  let lastUserPrompt = "";
@@ -963,11 +965,6 @@ export default function powerlineFooter(pi: ExtensionAPI) {
963
965
 
964
966
  const getShellPath = () => process.env.SHELL || "/bin/sh";
965
967
  const getShellCwd = () => shellSession?.state.cwd ?? currentCtx?.cwd ?? process.cwd();
966
- const welcomeDismissScheduler = createWelcomeDismissScheduler({
967
- dismiss: (ctx: unknown) => dismissWelcome(ctx),
968
- getGeneration: () => sessionGeneration,
969
- isEnabled: () => enabled,
970
- });
971
968
 
972
969
  const statusRenderScheduler = createRenderScheduler(() => {
973
970
  const msSinceInput = Date.now() - lastEditorInputAt;
@@ -1187,6 +1184,13 @@ export default function powerlineFooter(pi: ExtensionAPI) {
1187
1184
  stashedEditorText = null;
1188
1185
 
1189
1186
  const settings = readSettings(ctx.cwd);
1187
+ const guidePreferences = getGuidePreferences(settings);
1188
+ const guideDisplayMode = resolveGuideDisplayMode({
1189
+ reason: event.reason,
1190
+ hasUI: ctx.hasUI,
1191
+ guideMode: guidePreferences.mode,
1192
+ });
1193
+ const newGuideFeatures = getNewGuideFeatures(guidePreferences.lastSeenVersion, VERSION);
1190
1194
  bashModeSettings = parseBashModeSettings(settings);
1191
1195
  resolvedShortcuts = resolveShortcutConfig(settings);
1192
1196
  showLastPrompt = settings.showLastPrompt !== false;
@@ -1210,12 +1214,10 @@ export default function powerlineFooter(pi: ExtensionAPI) {
1210
1214
 
1211
1215
  if (enabled && ctx.hasUI) {
1212
1216
  setupCustomEditor(ctx);
1213
- if (event.reason === "startup") {
1214
- if (settings.quietStartup === true) {
1215
- setupWelcomeHeader(ctx);
1216
- } else {
1217
- setupWelcomeOverlay(ctx);
1218
- }
1217
+ if (guideDisplayMode === "full") {
1218
+ setupWelcomeOverlay(ctx, newGuideFeatures);
1219
+ } else if (guideDisplayMode === "compact") {
1220
+ setupWelcomeHeader(ctx, newGuideFeatures);
1219
1221
  } else {
1220
1222
  dismissWelcome(ctx);
1221
1223
  }
@@ -1228,9 +1230,9 @@ export default function powerlineFooter(pi: ExtensionAPI) {
1228
1230
  completedAsyncSubagentUsage.clear();
1229
1231
  dismissWelcomeOverlay?.();
1230
1232
  dismissWelcomeOverlay = null;
1233
+ guideOverlayBlocking = false;
1231
1234
  welcomeHeaderActive = false;
1232
1235
  welcomeOverlayShouldDismiss = false;
1233
- welcomeDismissScheduler.cancel();
1234
1236
  statusRenderScheduler.cancel();
1235
1237
  restoreFooterStatusRepaintHook?.();
1236
1238
  restoreFooterStatusRepaintHook = null;
@@ -1333,13 +1335,12 @@ export default function powerlineFooter(pi: ExtensionAPI) {
1333
1335
  }
1334
1336
  });
1335
1337
 
1336
- // Track streaming state (footer only shows status during streaming)
1337
- // Also dismiss welcome when agent starts responding (handles `p "command"` case)
1338
+ // Track streaming state (footer only shows status during streaming).
1339
+ // A visible guide remains blocking until its own key handler dismisses it.
1338
1340
  pi.on("agent_start", async (_event, ctx) => {
1339
1341
  isStreaming = true;
1340
1342
  liveAssistantUsage = null;
1341
1343
  onVibeAgentStart();
1342
- dismissWelcome(ctx);
1343
1344
  currentCtx = ctx;
1344
1345
  });
1345
1346
 
@@ -1372,9 +1373,8 @@ export default function powerlineFooter(pi: ExtensionAPI) {
1372
1373
  requestImmediateStatusRender({ deferDuringTyping: false });
1373
1374
  });
1374
1375
 
1375
- // Also dismiss on tool calls (agent is working) + refresh vibe if rate limit allows
1376
+ // Refresh vibe if rate limit allows. The guide is dismissed only by its input handler.
1376
1377
  pi.on("tool_call", async (event, ctx) => {
1377
- dismissWelcome(ctx);
1378
1378
  if (ctx.hasUI) {
1379
1379
  // Extract recent agent context from session for richer vibe generation
1380
1380
  const agentContext = getRecentAgentContext(ctx);
@@ -1409,7 +1409,7 @@ export default function powerlineFooter(pi: ExtensionAPI) {
1409
1409
  }
1410
1410
 
1411
1411
  function dismissWelcome(ctx: any) {
1412
- welcomeDismissScheduler.cancel();
1412
+ if (guideOverlayBlocking) return;
1413
1413
 
1414
1414
  if (dismissWelcomeOverlay) {
1415
1415
  dismissWelcomeOverlay();
@@ -1424,9 +1424,20 @@ export default function powerlineFooter(pi: ExtensionAPI) {
1424
1424
  }
1425
1425
  }
1426
1426
 
1427
- function scheduleDismissWelcome(ctx: any) {
1428
- if (!dismissWelcomeOverlay && welcomeOverlayShouldDismiss && !welcomeHeaderActive) return;
1429
- welcomeDismissScheduler.schedule(ctx);
1427
+ function closeWelcome(ctx: any): void {
1428
+ dismissWelcomeOverlay?.();
1429
+ dismissWelcomeOverlay = null;
1430
+ guideOverlayBlocking = false;
1431
+ if (welcomeHeaderActive) {
1432
+ welcomeHeaderActive = false;
1433
+ ctx.ui.setHeader(undefined);
1434
+ }
1435
+ welcomeOverlayShouldDismiss = false;
1436
+ }
1437
+
1438
+ function currentGuideFeatures(ctx: any): GuideFeature[] {
1439
+ const preferences = getGuidePreferences(readSettings(ctx.cwd));
1440
+ return getNewGuideFeatures(preferences.lastSeenVersion, VERSION);
1430
1441
  }
1431
1442
 
1432
1443
  function addStashHistoryEntry(text: string): void {
@@ -1696,6 +1707,50 @@ export default function powerlineFooter(pi: ExtensionAPI) {
1696
1707
  requestStatusRender();
1697
1708
  });
1698
1709
 
1710
+ pi.registerCommand("guide", {
1711
+ description: "Open the recurring Selesai feature guide",
1712
+ handler: async (args, ctx) => {
1713
+ if (!ctx.hasUI) return;
1714
+
1715
+ const mode = args?.trim().toLowerCase() ?? "";
1716
+ if (mode !== "" && !["full", "compact", "off", "reset"].includes(mode)) {
1717
+ ctx.ui.notify("Usage: /guide [full|compact|off|reset]", "warning");
1718
+ return;
1719
+ }
1720
+
1721
+ if (!enabled) {
1722
+ ctx.ui.notify("Powerline is disabled; use /powerline first", "info");
1723
+ return;
1724
+ }
1725
+
1726
+ if (mode === "off") {
1727
+ const saved = saveGuidePreferences({ mode: "off" }, getSettingsPath());
1728
+ closeWelcome(ctx);
1729
+ ctx.ui.notify(saved ? "Guide hidden for future sessions" : "Guide hidden for this session; settings.json was not updated", saved ? "info" : "warning");
1730
+ return;
1731
+ }
1732
+
1733
+ if (mode === "compact") {
1734
+ const saved = saveGuidePreferences({ mode: "compact" }, getSettingsPath());
1735
+ closeWelcome(ctx);
1736
+ setupWelcomeHeader(ctx, currentGuideFeatures(ctx));
1737
+ if (!saved) ctx.ui.notify("Compact guide enabled for this session; settings.json was not updated", "warning");
1738
+ return;
1739
+ }
1740
+
1741
+ if (mode === "full" || mode === "reset") {
1742
+ const saved = saveGuidePreferences({
1743
+ mode: "full",
1744
+ ...(mode === "reset" ? { lastSeenVersion: null } : {}),
1745
+ }, getSettingsPath());
1746
+ if (!saved) ctx.ui.notify("Guide opened, but settings.json was not updated", "warning");
1747
+ }
1748
+
1749
+ closeWelcome(ctx);
1750
+ setupWelcomeOverlay(ctx, currentGuideFeatures(ctx));
1751
+ },
1752
+ });
1753
+
1699
1754
  // Command to toggle/configure
1700
1755
  pi.registerCommand("powerline", {
1701
1756
  description: "Configure powerline status (toggle, preset)",
@@ -1718,7 +1773,6 @@ export default function powerlineFooter(pi: ExtensionAPI) {
1718
1773
  dismissWelcomeOverlay = null;
1719
1774
  welcomeHeaderActive = false;
1720
1775
  welcomeOverlayShouldDismiss = false;
1721
- welcomeDismissScheduler.cancel();
1722
1776
  getPromptHistoryState().savedPromptHistory = [];
1723
1777
  stashedEditorText = null;
1724
1778
  ctx.ui.setStatus("stash", undefined);
@@ -2520,7 +2574,6 @@ export default function powerlineFooter(pi: ExtensionAPI) {
2520
2574
  }
2521
2575
  if (isStashShortcutInput(data)) {
2522
2576
  stashOrRestoreEditorText(ctx);
2523
- scheduleDismissWelcome(ctx);
2524
2577
  tuiRef?.requestRender();
2525
2578
  return { consume: true };
2526
2579
  }
@@ -2531,7 +2584,6 @@ export default function powerlineFooter(pi: ExtensionAPI) {
2531
2584
  }
2532
2585
 
2533
2586
  runPowerlineShortcut(ctx, powerlineShortcutAction);
2534
- scheduleDismissWelcome(ctx);
2535
2587
  tuiRef?.requestRender();
2536
2588
  return { consume: true };
2537
2589
  })
@@ -2634,14 +2686,12 @@ export default function powerlineFooter(pi: ExtensionAPI) {
2634
2686
 
2635
2687
  if (isStashShortcutInput(data)) {
2636
2688
  stashOrRestoreEditorText(ctx);
2637
- scheduleDismissWelcome(ctx);
2638
2689
  return;
2639
2690
  }
2640
2691
 
2641
2692
  const powerlineShortcutAction = getPowerlineShortcutAction(data);
2642
2693
  if (powerlineShortcutAction) {
2643
2694
  runPowerlineShortcut(ctx, powerlineShortcutAction);
2644
- scheduleDismissWelcome(ctx);
2645
2695
  return;
2646
2696
  }
2647
2697
 
@@ -2658,7 +2708,6 @@ export default function powerlineFooter(pi: ExtensionAPI) {
2658
2708
 
2659
2709
  attachAutocompleteProvider();
2660
2710
  const followUpText = keybindings.matches(data, "app.message.followUp") ? getCurrentEditorText(ctx, editor) : "";
2661
- scheduleDismissWelcome(ctx);
2662
2711
  originalHandleInput(data);
2663
2712
  if (hasNonWhitespaceText(followUpText) && !hasNonWhitespaceText(getCurrentEditorText(ctx, editor))) {
2664
2713
  followSubmittedEditorToBottom();
@@ -2746,14 +2795,15 @@ export default function powerlineFooter(pi: ExtensionAPI) {
2746
2795
  }
2747
2796
  }
2748
2797
 
2749
- function setupWelcomeHeader(ctx: any) {
2798
+ function setupWelcomeHeader(ctx: any, newFeatures: GuideFeature[] = []) {
2750
2799
  const modelName = ctx.model?.name || ctx.model?.id || "No model";
2751
2800
  const providerName = ctx.model?.provider || "Unknown";
2752
2801
  const loadedCounts = discoverLoadedCounts();
2753
2802
  const recentSessions = getRecentSessions(3);
2754
2803
 
2755
- const header = new WelcomeHeader(modelName, providerName, recentSessions, loadedCounts);
2804
+ const header = new WelcomeHeader(modelName, providerName, recentSessions, loadedCounts, "compact", newFeatures);
2756
2805
  welcomeHeaderActive = true;
2806
+ markGuideVersionSeen(VERSION, getSettingsPath());
2757
2807
 
2758
2808
  ctx.ui.setHeader(() => {
2759
2809
  return {
@@ -2767,7 +2817,7 @@ export default function powerlineFooter(pi: ExtensionAPI) {
2767
2817
  });
2768
2818
  }
2769
2819
 
2770
- function setupWelcomeOverlay(ctx: any) {
2820
+ function setupWelcomeOverlay(ctx: any, newFeatures: GuideFeature[] = []) {
2771
2821
  const modelName = ctx.model?.name || ctx.model?.id || "No model";
2772
2822
  const providerName = ctx.model?.provider || "Unknown";
2773
2823
  const loadedCounts = discoverLoadedCounts();
@@ -2782,45 +2832,29 @@ export default function powerlineFooter(pi: ExtensionAPI) {
2782
2832
  return;
2783
2833
  }
2784
2834
 
2785
- const sessionEvents = ctx.sessionManager?.getBranch?.() ?? [];
2786
- const hasActivity = sessionEvents.some((entry: unknown) => {
2787
- if (!isRecord(entry)) return false;
2788
- if (entry.type === "tool_call" || entry.type === "tool_result") return true;
2789
- return entry.type === "message" && isRecord(entry.message) && entry.message.role === "assistant";
2790
- });
2791
- if (hasActivity) {
2792
- return;
2793
- }
2794
-
2795
2835
  ctx.ui.custom(
2796
- (tui: any, _theme: any, _keybindings: any, done: (result: void) => void) => {
2836
+ (_tui: any, _theme: any, _keybindings: any, done: (result: void) => void) => {
2797
2837
  const welcome = new WelcomeComponent(
2798
2838
  modelName,
2799
2839
  providerName,
2800
2840
  recentSessions,
2801
2841
  loadedCounts,
2842
+ "full",
2843
+ newFeatures,
2802
2844
  );
2845
+ markGuideVersionSeen(VERSION, getSettingsPath());
2846
+ guideOverlayBlocking = true;
2803
2847
 
2804
- let countdown = 30;
2805
2848
  let dismissed = false;
2806
- let interval: ReturnType<typeof setInterval> | null = null;
2807
2849
 
2808
2850
  const dismiss = () => {
2809
2851
  if (dismissed) return;
2810
2852
  dismissed = true;
2811
- if (interval) clearInterval(interval);
2812
2853
  dismissWelcomeOverlay = null;
2854
+ guideOverlayBlocking = false;
2813
2855
  done();
2814
2856
  };
2815
2857
 
2816
- interval = setInterval(() => {
2817
- if (dismissed) return;
2818
- countdown--;
2819
- welcome.setCountdown(countdown);
2820
- tui.requestRender();
2821
- if (countdown <= 0) dismiss();
2822
- }, 1000);
2823
-
2824
2858
  dismissWelcomeOverlay = dismiss;
2825
2859
 
2826
2860
  if (welcomeOverlayShouldDismiss) {
@@ -2829,13 +2863,12 @@ export default function powerlineFooter(pi: ExtensionAPI) {
2829
2863
  }
2830
2864
 
2831
2865
  return {
2832
- focused: false,
2866
+ focused: true,
2833
2867
  invalidate: () => welcome.invalidate(),
2834
2868
  render: (width: number) => welcome.render(width),
2835
2869
  handleInput: () => dismiss(),
2836
2870
  dispose: () => {
2837
2871
  dismissed = true;
2838
- if (interval) clearInterval(interval);
2839
2872
  },
2840
2873
  };
2841
2874
  },
@@ -1,6 +1,5 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { createWelcomeDismissScheduler } from "../welcome-dismiss.ts";
4
3
  import { createRenderScheduler } from "../render-scheduler.ts";
5
4
 
6
5
  test("render scheduler coalesces pending status renders", () => {
@@ -101,80 +100,3 @@ test("render scheduler cancels pending status renders", () => {
101
100
  globalThis.clearTimeout = originalClearTimeout;
102
101
  }
103
102
  });
104
-
105
- test("welcome dismissal scheduler coalesces pending work", () => {
106
- const originalSetTimeout = globalThis.setTimeout;
107
- const callbacks: Array<() => void> = [];
108
- let dismissCount = 0;
109
-
110
- globalThis.setTimeout = ((callback: () => void) => {
111
- callbacks.push(callback);
112
- return { id: callbacks.length } as ReturnType<typeof setTimeout>;
113
- }) as typeof setTimeout;
114
-
115
- try {
116
- const scheduler = createWelcomeDismissScheduler({
117
- dismiss: () => { dismissCount += 1; },
118
- getGeneration: () => 1,
119
- isEnabled: () => true,
120
- });
121
-
122
- scheduler.schedule(undefined);
123
- scheduler.schedule(undefined);
124
- scheduler.schedule(undefined);
125
-
126
- assert.equal(callbacks.length, 1);
127
- callbacks[0]?.();
128
- assert.equal(dismissCount, 1);
129
- } finally {
130
- globalThis.setTimeout = originalSetTimeout;
131
- }
132
- });
133
-
134
- test("welcome dismissal scheduler cancels stale pending work", () => {
135
- const originalSetTimeout = globalThis.setTimeout;
136
- const originalClearTimeout = globalThis.clearTimeout;
137
- const callbacks: Array<() => void> = [];
138
- const cleared = new Set<object>();
139
- let dismissCount = 0;
140
- let generation = 1;
141
- let enabled = true;
142
-
143
- globalThis.setTimeout = ((callback: () => void) => {
144
- const handle = { id: callbacks.length + 1 };
145
- callbacks.push(callback);
146
- return handle as ReturnType<typeof setTimeout>;
147
- }) as typeof setTimeout;
148
- globalThis.clearTimeout = ((handle?: ReturnType<typeof setTimeout>) => {
149
- if (handle && typeof handle === "object") {
150
- cleared.add(handle);
151
- }
152
- }) as typeof clearTimeout;
153
-
154
- try {
155
- const scheduler = createWelcomeDismissScheduler({
156
- dismiss: () => { dismissCount += 1; },
157
- getGeneration: () => generation,
158
- isEnabled: () => enabled,
159
- });
160
-
161
- scheduler.schedule(undefined);
162
- generation += 1;
163
- callbacks[0]?.();
164
- assert.equal(dismissCount, 0);
165
-
166
- scheduler.schedule(undefined);
167
- enabled = false;
168
- callbacks[1]?.();
169
- assert.equal(dismissCount, 0);
170
-
171
- enabled = true;
172
- scheduler.schedule(undefined);
173
- scheduler.cancel();
174
- assert.equal(cleared.size, 1);
175
- assert.equal(dismissCount, 0);
176
- } finally {
177
- globalThis.setTimeout = originalSetTimeout;
178
- globalThis.clearTimeout = originalClearTimeout;
179
- }
180
- });