@pellux/goodvibes-agent 0.1.116 → 1.0.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 (285) hide show
  1. package/CHANGELOG.md +48 -661
  2. package/README.md +64 -57
  3. package/bin/goodvibes-agent.ts +16 -2
  4. package/dist/package/main.js +199180 -192185
  5. package/docs/README.md +15 -8
  6. package/docs/channels-remote-and-api.md +50 -0
  7. package/docs/connected-host.md +59 -0
  8. package/docs/getting-started.md +61 -50
  9. package/docs/knowledge-artifacts-and-multimodal.md +91 -0
  10. package/docs/project-planning.md +79 -0
  11. package/docs/providers-and-routing.md +46 -0
  12. package/docs/release-and-publishing.md +48 -11
  13. package/docs/tools-and-commands.md +123 -0
  14. package/docs/voice-and-live-tts.md +51 -0
  15. package/package.json +3 -6
  16. package/src/agent/behavior-discovery-summary.ts +1 -3
  17. package/src/agent/channel-delivery.ts +201 -0
  18. package/src/agent/media-generation.ts +159 -0
  19. package/src/agent/memory-prompt.ts +0 -1
  20. package/src/agent/note-registry.ts +329 -0
  21. package/src/agent/operator-actions.ts +343 -0
  22. package/src/agent/persona-discovery.ts +1 -3
  23. package/src/agent/persona-registry.ts +19 -16
  24. package/src/agent/record-labels.ts +107 -0
  25. package/src/agent/reminder-schedule-format.ts +33 -24
  26. package/src/agent/reminder-schedule.ts +27 -26
  27. package/src/agent/routine-registry.ts +54 -14
  28. package/src/agent/routine-schedule-args.ts +2 -1
  29. package/src/agent/routine-schedule-format.ts +77 -53
  30. package/src/agent/routine-schedule-promotion.ts +35 -33
  31. package/src/agent/routine-schedule-receipts.ts +29 -27
  32. package/src/agent/runtime-profile-starters.ts +5 -5
  33. package/src/agent/runtime-profile.ts +91 -12
  34. package/src/agent/skill-registry.ts +210 -32
  35. package/src/cli/agent-knowledge-args.ts +5 -1
  36. package/src/cli/agent-knowledge-command.ts +40 -34
  37. package/src/cli/agent-knowledge-format.ts +80 -67
  38. package/src/cli/agent-knowledge-methods.ts +1 -1
  39. package/src/cli/agent-knowledge-runtime.ts +32 -26
  40. package/src/cli/bundle-command.ts +14 -9
  41. package/src/cli/config-overrides.ts +37 -36
  42. package/src/cli/entrypoint.ts +16 -7
  43. package/src/cli/external-runtime.ts +10 -4
  44. package/src/cli/help.ts +54 -29
  45. package/src/cli/local-library-command.ts +203 -83
  46. package/src/cli/management-commands.ts +105 -68
  47. package/src/cli/management.ts +55 -74
  48. package/src/cli/memory-command.ts +66 -32
  49. package/src/cli/parser.ts +37 -3
  50. package/src/cli/profiles-command.ts +166 -31
  51. package/src/cli/provider-auth-routes.ts +2 -1
  52. package/src/cli/routines-command.ts +176 -40
  53. package/src/cli/service-posture.ts +16 -16
  54. package/src/cli/status.ts +56 -135
  55. package/src/core/conversation-message-snapshot.ts +131 -0
  56. package/src/input/agent-workspace-access-command-editor-submission.ts +149 -0
  57. package/src/input/agent-workspace-access-command-editors.ts +170 -0
  58. package/src/input/agent-workspace-activation.ts +35 -8
  59. package/src/input/agent-workspace-basic-command-editor-submission.ts +351 -94
  60. package/src/input/agent-workspace-basic-command-editors.ts +502 -9
  61. package/src/input/agent-workspace-categories.ts +423 -51
  62. package/src/input/agent-workspace-channel-command-editor-submission.ts +83 -0
  63. package/src/input/agent-workspace-channel-command-editors.ts +65 -0
  64. package/src/input/agent-workspace-channels.ts +35 -2
  65. package/src/input/agent-workspace-command-editor.ts +41 -3
  66. package/src/input/agent-workspace-config-reader.ts +16 -0
  67. package/src/input/agent-workspace-delegation-editor-submission.ts +55 -0
  68. package/src/input/agent-workspace-editors.ts +166 -1
  69. package/src/input/agent-workspace-knowledge-command-editor-submission.ts +132 -0
  70. package/src/input/agent-workspace-knowledge-command-editors.ts +110 -0
  71. package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
  72. package/src/input/agent-workspace-learned-behavior.ts +67 -0
  73. package/src/input/agent-workspace-library-command-editor-submission.ts +50 -0
  74. package/src/input/agent-workspace-library-command-editors.ts +35 -0
  75. package/src/input/agent-workspace-local-operations.ts +218 -0
  76. package/src/input/agent-workspace-local-selection.ts +75 -0
  77. package/src/input/agent-workspace-mcp-command-editor-submission.ts +95 -0
  78. package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
  79. package/src/input/agent-workspace-media-command-editors.ts +27 -0
  80. package/src/input/agent-workspace-memory-command-editor-submission.ts +232 -0
  81. package/src/input/agent-workspace-memory-command-editors.ts +180 -0
  82. package/src/input/agent-workspace-memory-editor.ts +2 -2
  83. package/src/input/agent-workspace-navigation.ts +81 -0
  84. package/src/input/agent-workspace-notify-editor-submission.ts +121 -0
  85. package/src/input/agent-workspace-operations-command-editor-submission.ts +208 -0
  86. package/src/input/agent-workspace-operations-command-editors.ts +196 -0
  87. package/src/input/agent-workspace-panel-route.ts +43 -0
  88. package/src/input/agent-workspace-provider-command-editor-submission.ts +155 -0
  89. package/src/input/agent-workspace-provider-command-editors.ts +93 -0
  90. package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
  91. package/src/input/agent-workspace-requirements.ts +11 -0
  92. package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
  93. package/src/input/agent-workspace-search.ts +169 -0
  94. package/src/input/agent-workspace-secret-editor-submission.ts +153 -0
  95. package/src/input/agent-workspace-session-command-editor-submission.ts +199 -0
  96. package/src/input/agent-workspace-session-command-editors.ts +249 -0
  97. package/src/input/agent-workspace-setup.ts +37 -16
  98. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +101 -0
  99. package/src/input/agent-workspace-skill-bundle-command-editors.ts +110 -0
  100. package/src/input/agent-workspace-snapshot.ts +162 -21
  101. package/src/input/agent-workspace-task-command-editor-submission.ts +61 -0
  102. package/src/input/agent-workspace-task-command-editors.ts +47 -0
  103. package/src/input/agent-workspace-token.ts +18 -2
  104. package/src/input/agent-workspace-types.ts +212 -4
  105. package/src/input/agent-workspace-voice-media.ts +4 -7
  106. package/src/input/agent-workspace-web-research-editor.ts +104 -0
  107. package/src/input/agent-workspace-workplan-editor-submission.ts +153 -0
  108. package/src/input/agent-workspace.ts +178 -240
  109. package/src/input/command-registry.ts +5 -2
  110. package/src/input/commands/agent-runtime-profile-runtime.ts +126 -37
  111. package/src/input/commands/agent-skills-runtime.ts +149 -72
  112. package/src/input/commands/agent-workspace-runtime.ts +23 -6
  113. package/src/input/commands/brief-runtime.ts +55 -25
  114. package/src/input/commands/channels-runtime.ts +147 -33
  115. package/src/input/commands/compat-runtime.ts +32 -0
  116. package/src/input/commands/delegation-runtime.ts +29 -23
  117. package/src/input/commands/experience-runtime.ts +6 -4
  118. package/src/input/commands/guidance-runtime.ts +4 -4
  119. package/src/input/commands/health-runtime.ts +149 -126
  120. package/src/input/commands/knowledge.ts +82 -60
  121. package/src/input/commands/local-provider-runtime.ts +43 -21
  122. package/src/input/commands/local-runtime.ts +138 -16
  123. package/src/input/commands/local-setup-review.ts +10 -20
  124. package/src/input/commands/mcp-runtime.ts +63 -46
  125. package/src/input/commands/notify-runtime.ts +38 -9
  126. package/src/input/commands/operator-actions-runtime.ts +138 -0
  127. package/src/input/commands/operator-runtime.ts +6 -76
  128. package/src/input/commands/personas-runtime.ts +46 -31
  129. package/src/input/commands/planning-runtime.ts +1 -1
  130. package/src/input/commands/platform-access-runtime.ts +35 -98
  131. package/src/input/commands/product-runtime.ts +2 -2
  132. package/src/input/commands/provider-accounts-runtime.ts +60 -39
  133. package/src/input/commands/qrcode-runtime.ts +50 -9
  134. package/src/input/commands/recall-bundle.ts +11 -11
  135. package/src/input/commands/recall-capture.ts +13 -11
  136. package/src/input/commands/recall-query.ts +29 -21
  137. package/src/input/commands/recall-review.ts +2 -1
  138. package/src/input/commands/routines-runtime.ts +99 -45
  139. package/src/input/commands/runtime-services.ts +1 -14
  140. package/src/input/commands/schedule-runtime.ts +33 -20
  141. package/src/input/commands/security-runtime.ts +6 -11
  142. package/src/input/commands/session-content.ts +80 -78
  143. package/src/input/commands/session-workflow.ts +134 -104
  144. package/src/input/commands/session.ts +3 -174
  145. package/src/input/commands/shell-core.ts +38 -17
  146. package/src/input/commands/subscription-runtime.ts +54 -185
  147. package/src/input/commands/support-bundle-runtime.ts +53 -0
  148. package/src/input/commands/tasks-runtime.ts +21 -21
  149. package/src/input/commands/work-plan-runtime.ts +33 -8
  150. package/src/input/commands.ts +6 -2
  151. package/src/input/feed-context-factory.ts +2 -2
  152. package/src/input/file-picker.ts +3 -2
  153. package/src/input/handler-command-route.ts +4 -7
  154. package/src/input/handler-content-actions.ts +89 -26
  155. package/src/input/handler-feed-routes.ts +19 -12
  156. package/src/input/handler-feed.ts +6 -5
  157. package/src/input/handler-interactions.ts +9 -36
  158. package/src/input/handler-modal-stack.ts +3 -3
  159. package/src/input/handler-onboarding.ts +52 -91
  160. package/src/input/handler-shortcuts.ts +16 -7
  161. package/src/input/handler.ts +50 -23
  162. package/src/input/input-history.ts +5 -6
  163. package/src/input/keybindings.ts +27 -16
  164. package/src/input/mcp-workspace.ts +90 -26
  165. package/src/input/onboarding/handler-onboarding-routes.ts +3 -1
  166. package/src/input/onboarding/onboarding-wizard-apply.ts +62 -0
  167. package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
  168. package/src/input/onboarding/onboarding-wizard-helpers.ts +1 -1
  169. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +223 -55
  170. package/src/input/onboarding/onboarding-wizard-steps.ts +59 -35
  171. package/src/input/onboarding/onboarding-wizard-types.ts +4 -1
  172. package/src/input/onboarding/onboarding-wizard.ts +87 -2
  173. package/src/input/profile-picker-modal.ts +31 -12
  174. package/src/input/session-picker-modal.ts +21 -4
  175. package/src/input/settings-modal-agent-policy.ts +6 -6
  176. package/src/input/settings-modal-behavior.ts +0 -3
  177. package/src/input/settings-modal-subscriptions.ts +3 -3
  178. package/src/input/settings-modal-types.ts +2 -13
  179. package/src/input/settings-modal.ts +20 -66
  180. package/src/input/submission-intent.ts +0 -1
  181. package/src/input/submission-router.ts +3 -3
  182. package/src/main.ts +18 -10
  183. package/src/panels/approval-panel.ts +8 -8
  184. package/src/panels/automation-control-panel.ts +4 -4
  185. package/src/panels/base-panel.ts +1 -1
  186. package/src/panels/builtin/agent.ts +1 -1
  187. package/src/panels/builtin/operations.ts +3 -12
  188. package/src/panels/builtin/session.ts +32 -24
  189. package/src/panels/builtin/shared.ts +7 -7
  190. package/src/panels/cost-tracker-panel.ts +1 -1
  191. package/src/panels/docs-panel.ts +5 -3
  192. package/src/panels/index.ts +0 -1
  193. package/src/panels/knowledge-panel.ts +6 -5
  194. package/src/panels/memory-panel.ts +7 -6
  195. package/src/panels/panel-list-panel.ts +36 -80
  196. package/src/panels/project-planning-panel.ts +19 -12
  197. package/src/panels/provider-account-snapshot.ts +51 -25
  198. package/src/panels/provider-accounts-panel.ts +33 -18
  199. package/src/panels/provider-health-domains.ts +48 -7
  200. package/src/panels/provider-health-panel.ts +5 -4
  201. package/src/panels/qr-panel.ts +44 -20
  202. package/src/panels/schedule-panel.ts +9 -17
  203. package/src/panels/security-panel.ts +8 -8
  204. package/src/panels/session-browser-panel.ts +10 -10
  205. package/src/panels/subscription-panel.ts +6 -6
  206. package/src/panels/system-messages-panel.ts +3 -3
  207. package/src/panels/tasks-panel.ts +21 -14
  208. package/src/panels/tool-inspector-panel.ts +19 -12
  209. package/src/panels/work-plan-panel.ts +5 -5
  210. package/src/planning/project-planning-coordinator.ts +3 -3
  211. package/src/provider-auth-route-display.ts +9 -0
  212. package/src/renderer/agent-workspace-style.ts +34 -0
  213. package/src/renderer/agent-workspace.ts +254 -57
  214. package/src/renderer/autocomplete-overlay.ts +25 -6
  215. package/src/renderer/block-actions.ts +1 -3
  216. package/src/renderer/bookmark-modal.ts +19 -4
  217. package/src/renderer/buffer.ts +4 -2
  218. package/src/renderer/context-inspector.ts +50 -13
  219. package/src/renderer/diff.ts +1 -1
  220. package/src/renderer/file-picker-overlay.ts +19 -6
  221. package/src/renderer/help-overlay.ts +112 -34
  222. package/src/renderer/history-search-overlay.ts +19 -4
  223. package/src/renderer/live-tail-modal.ts +27 -5
  224. package/src/renderer/mcp-workspace.ts +176 -43
  225. package/src/renderer/model-picker-overlay.ts +58 -3
  226. package/src/renderer/model-workspace.ts +104 -22
  227. package/src/renderer/onboarding/onboarding-wizard.ts +20 -4
  228. package/src/renderer/process-modal.ts +27 -6
  229. package/src/renderer/profile-picker-modal.ts +21 -5
  230. package/src/renderer/search-overlay.ts +25 -5
  231. package/src/renderer/selection-modal-overlay.ts +46 -14
  232. package/src/renderer/session-picker-modal.ts +18 -1
  233. package/src/renderer/settings-modal-helpers.ts +2 -40
  234. package/src/renderer/settings-modal.ts +88 -55
  235. package/src/renderer/system-message.ts +1 -1
  236. package/src/renderer/tool-call.ts +20 -11
  237. package/src/runtime/agent-runtime-events.ts +129 -0
  238. package/src/runtime/bootstrap-command-context.ts +7 -1
  239. package/src/runtime/bootstrap-command-parts.ts +11 -8
  240. package/src/runtime/bootstrap-core.ts +23 -65
  241. package/src/runtime/bootstrap-hook-bridge.ts +7 -18
  242. package/src/runtime/bootstrap-shell.ts +46 -8
  243. package/src/runtime/bootstrap.ts +36 -46
  244. package/src/runtime/connected-host-auth.ts +47 -0
  245. package/src/runtime/diagnostics/panels/index.ts +2 -2
  246. package/src/runtime/diagnostics/panels/panel-resources.ts +1 -1
  247. package/src/runtime/diagnostics/panels/policy.ts +7 -7
  248. package/src/runtime/index.ts +2 -5
  249. package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
  250. package/src/runtime/onboarding/apply.ts +288 -74
  251. package/src/runtime/onboarding/derivation.ts +10 -13
  252. package/src/runtime/onboarding/snapshot.ts +2 -17
  253. package/src/runtime/onboarding/types.ts +34 -20
  254. package/src/runtime/onboarding/verify.ts +105 -4
  255. package/src/runtime/operator-token-cleanup.ts +3 -3
  256. package/src/runtime/services.ts +95 -11
  257. package/src/runtime/store/selectors/index.ts +3 -3
  258. package/src/runtime/store/state.ts +1 -4
  259. package/src/runtime/surface-feature-flags.ts +41 -6
  260. package/src/runtime/ui-read-models.ts +3 -4
  261. package/src/runtime/ui-services.ts +6 -6
  262. package/src/shell/blocking-input.ts +2 -1
  263. package/src/shell/service-settings-sync.ts +7 -7
  264. package/src/shell/ui-openers.ts +17 -38
  265. package/src/tools/agent-analysis-registry-policy.ts +0 -1
  266. package/src/tools/agent-channel-send-tool.ts +133 -0
  267. package/src/tools/agent-context-policy.ts +1 -1
  268. package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
  269. package/src/tools/agent-knowledge-tool.ts +170 -0
  270. package/src/tools/agent-local-registry-tool.ts +279 -73
  271. package/src/tools/agent-media-generate-tool.ts +133 -0
  272. package/src/tools/agent-notify-tool.ts +143 -0
  273. package/src/tools/agent-operator-action-tool.ts +137 -0
  274. package/src/tools/agent-operator-briefing-tool.ts +217 -0
  275. package/src/tools/agent-reminder-schedule-tool.ts +237 -0
  276. package/src/tools/agent-tool-policy-guard.ts +13 -15
  277. package/src/tools/agent-work-plan-tool.ts +256 -0
  278. package/src/version.ts +1 -1
  279. package/docs/connected-services.md +0 -51
  280. package/src/cli/package-verification.ts +0 -274
  281. package/src/input/commands/policy-dispatch.ts +0 -339
  282. package/src/input/commands/policy.ts +0 -17
  283. package/src/panels/panel-picker.ts +0 -105
  284. package/src/panels/policy-panel.ts +0 -308
  285. package/src/renderer/panel-picker-overlay.ts +0 -202
@@ -13,6 +13,21 @@ import { BookmarkModal } from '../input/bookmark-modal.ts';
13
13
  import type { BookmarkEntry } from '@pellux/goodvibes-sdk/platform/bookmarks';
14
14
  import { getOverlayContentBudget, getStableOverlayContentRows } from './overlay-viewport.ts';
15
15
 
16
+ const BOOKMARK_MODAL_TITLE = 'Bookmarks';
17
+ const BOOKMARK_MODAL_EMPTY_MESSAGE = 'No bookmarks - use Ctrl+B to bookmark a block';
18
+ const BOOKMARK_MODAL_COLUMNS = ' Key Label Time';
19
+ const BOOKMARK_MODAL_HINTS = ['Up/Down Navigate', 'Enter Jump', 'o Open file', 'd Remove', 'Esc Close'];
20
+
21
+ export function renderBookmarkModalPackageText(): string {
22
+ return [
23
+ BOOKMARK_MODAL_TITLE,
24
+ BOOKMARK_MODAL_EMPTY_MESSAGE,
25
+ BOOKMARK_MODAL_COLUMNS.trim(),
26
+ '[<start>-<end> of <count>]',
27
+ ...BOOKMARK_MODAL_HINTS,
28
+ ].join('\n');
29
+ }
30
+
16
31
  // ---------------------------------------------------------------------------
17
32
  // Helpers
18
33
  // ---------------------------------------------------------------------------
@@ -60,7 +75,7 @@ export function renderBookmarkModal(
60
75
  const relSelected = Math.max(0, modal.selectedIndex - modal.scrollOffset);
61
76
 
62
77
  const items = visible.length === 0
63
- ? [{ label: 'No bookmarks - use Ctrl+B to bookmark a block', selected: false }]
78
+ ? [{ label: BOOKMARK_MODAL_EMPTY_MESSAGE, selected: false }]
64
79
  : visible.map((entry, i) => ({
65
80
  label: entryLabel(entry),
66
81
  selected: i === relSelected,
@@ -68,7 +83,7 @@ export function renderBookmarkModal(
68
83
  const sections: import('./modal-factory.ts').ModalSection[] = [
69
84
  {
70
85
  type: 'text',
71
- content: ' Key Label Time',
86
+ content: BOOKMARK_MODAL_COLUMNS,
72
87
  style: { dim: true },
73
88
  },
74
89
  { type: 'separator' },
@@ -90,11 +105,11 @@ export function renderBookmarkModal(
90
105
 
91
106
  return ModalFactory.createModal(
92
107
  {
93
- title: `Bookmarks ${totalStr}`,
108
+ title: `${BOOKMARK_MODAL_TITLE} ${totalStr}`,
94
109
  width: 80,
95
110
  targetContentRows,
96
111
  sections,
97
- hints: ['Up/Down Navigate', 'Enter Jump', 'o Open file', 'd Remove', 'Esc Close'],
112
+ hints: BOOKMARK_MODAL_HINTS,
98
113
  },
99
114
  width,
100
115
  );
@@ -1,5 +1,7 @@
1
1
  import { type Line, type Cell, createEmptyLine, createEmptyCell } from '../types/grid.ts';
2
2
 
3
+ const CELL_KEYS: readonly (keyof Cell)[] = ['char', 'fg', 'bg', 'bold', 'dim', 'underline', 'italic', 'strikethrough', 'link'];
4
+
3
5
  /**
4
6
  * TerminalBuffer - Represents a 2D grid of styled cells.
5
7
  * Tracks a per-row dirty bitmap so the diff engine can skip rows that were
@@ -21,8 +23,8 @@ export class TerminalBuffer {
21
23
  // already matches the current cell value (idempotent write).
22
24
  const current = this.cells[y][x]!;
23
25
  let changed = false;
24
- for (const k in cell) {
25
- if ((cell as unknown as Record<string, unknown>)[k] !== (current as unknown as Record<string, unknown>)[k]) {
26
+ for (const key of CELL_KEYS) {
27
+ if (Object.prototype.hasOwnProperty.call(cell, key) && cell[key] !== current[key]) {
26
28
  changed = true;
27
29
  break;
28
30
  }
@@ -3,6 +3,45 @@ import { ModalFactory } from './modal-factory.ts';
3
3
  import type { ConversationManager } from '../core/conversation';
4
4
  import { getOverlayContentBudget, getOverlaySurfaceMetrics, getStableOverlayContentRows } from './overlay-viewport.ts';
5
5
 
6
+ const CONTEXT_INSPECTOR_TITLE = 'Context Inspector';
7
+ const CONTEXT_INSPECTOR_EMPTY_MESSAGE = 'No messages in conversation yet.';
8
+ const CONTEXT_INSPECTOR_CLOSE_HINTS = ['[Esc] Close'];
9
+ const CONTEXT_INSPECTOR_CONTEXT_HINTS = ['[*] >10% of context', '[Esc] Close'];
10
+ const CONTEXT_INSPECTOR_FULL_WARNING = 'WARNING: context is 80%+ full. Open Agent Workspace -> Conversation -> Compact conversation.';
11
+ const CONTEXT_INSPECTOR_COMPACT_GUIDANCE = 'Open Agent Workspace -> Conversation -> Compact conversation to summarise and reduce context size.';
12
+
13
+ function contextInspectorTotalText(
14
+ totalTokens: string,
15
+ messageCount: string | number,
16
+ capacity: string | null = null,
17
+ ): string {
18
+ const capacityStr = capacity ? ` | Capacity: ${totalTokens} / ${capacity}` : '';
19
+ return `Total: ~${totalTokens} tokens (${messageCount} messages)${capacityStr}`;
20
+ }
21
+
22
+ function contextInspectorOlderMessagesText(count: string | number): string {
23
+ return `(${count} older messages not shown)`;
24
+ }
25
+
26
+ function contextInspectorCompactionHintText(count: string | number, percent: string): string {
27
+ const suffix = typeof count === 'number' && count === 1 ? '' : 's';
28
+ return `Compaction hint: ${count} message${suffix} use ${percent} of context.`;
29
+ }
30
+
31
+ export function renderContextInspectorPackageText(): string {
32
+ return [
33
+ CONTEXT_INSPECTOR_TITLE,
34
+ CONTEXT_INSPECTOR_EMPTY_MESSAGE,
35
+ contextInspectorTotalText('<tokens>', '<messages>', '<window> (<percent>)'),
36
+ CONTEXT_INSPECTOR_FULL_WARNING,
37
+ contextInspectorOlderMessagesText('<count>'),
38
+ contextInspectorCompactionHintText('<count>', '<percent>'),
39
+ CONTEXT_INSPECTOR_COMPACT_GUIDANCE,
40
+ ...CONTEXT_INSPECTOR_CLOSE_HINTS,
41
+ ...CONTEXT_INSPECTOR_CONTEXT_HINTS,
42
+ ].join('\n');
43
+ }
44
+
6
45
  // ─── ContextInspectorModal ────────────────────────────────────────────────────
7
46
 
8
47
  /**
@@ -67,14 +106,14 @@ export function renderContextInspector(
67
106
 
68
107
  if (messages.length === 0) {
69
108
  return ModalFactory.createModal({
70
- title: 'Context Inspector',
109
+ title: CONTEXT_INSPECTOR_TITLE,
71
110
  width: metrics.boxWidth,
72
111
  margin: metrics.margin,
73
112
  targetContentRows,
74
113
  sections: [
75
- { type: 'text', content: 'No messages in conversation yet.' },
114
+ { type: 'text', content: CONTEXT_INSPECTOR_EMPTY_MESSAGE },
76
115
  ],
77
- hints: ['[Esc] Close'],
116
+ hints: CONTEXT_INSPECTOR_CLOSE_HINTS,
78
117
  }, width);
79
118
  }
80
119
 
@@ -139,19 +178,17 @@ export function renderContextInspector(
139
178
  const sections: import('./modal-factory.ts').ModalSection[] = [];
140
179
 
141
180
  // Summary header
142
- const capacityStr = contextWindow > 0
143
- ? ` | Capacity: ${fmtN(totalTokens)} / ${fmtN(contextWindow)} (${fmtPct(totalTokens / contextWindow)})`
144
- : '';
181
+ const capacityStr = contextWindow > 0 ? `${fmtN(contextWindow)} (${fmtPct(totalTokens / contextWindow)})` : null;
145
182
  sections.push({
146
183
  type: 'text',
147
- content: `Total: ~${fmtN(totalTokens)} tokens (${messages.length} messages)${capacityStr}`,
184
+ content: contextInspectorTotalText(fmtN(totalTokens), messages.length, capacityStr),
148
185
  style: { bold: true },
149
186
  });
150
187
 
151
188
  if (contextWindow > 0 && totalTokens / contextWindow >= 0.80) {
152
189
  sections.push({
153
190
  type: 'text',
154
- content: 'WARNING: context is 80%+ full. Run /compact to free space.',
191
+ content: CONTEXT_INSPECTOR_FULL_WARNING,
155
192
  style: { fg: '#ff9900', bold: true },
156
193
  });
157
194
  }
@@ -169,7 +206,7 @@ export function renderContextInspector(
169
206
  if (startOffset > 0) {
170
207
  sections.push({
171
208
  type: 'text',
172
- content: `(${startOffset} older messages not shown)`,
209
+ content: contextInspectorOlderMessagesText(startOffset),
173
210
  style: { dim: true },
174
211
  });
175
212
  }
@@ -198,22 +235,22 @@ export function renderContextInspector(
198
235
  );
199
236
  sections.push({
200
237
  type: 'text',
201
- content: `Compaction hint: ${largeMsgs.length} message${largeMsgs.length > 1 ? 's' : ''} use ${largePct} of context.`,
238
+ content: contextInspectorCompactionHintText(largeMsgs.length, largePct),
202
239
  style: { fg: '#00ffcc' },
203
240
  });
204
241
  sections.push({
205
242
  type: 'text',
206
- content: 'Run /compact to summarise and reduce context size.',
243
+ content: CONTEXT_INSPECTOR_COMPACT_GUIDANCE,
207
244
  style: { dim: true },
208
245
  });
209
246
  }
210
247
 
211
248
  return ModalFactory.createModal({
212
- title: 'Context Inspector',
249
+ title: CONTEXT_INSPECTOR_TITLE,
213
250
  width: metrics.boxWidth,
214
251
  margin: metrics.margin,
215
252
  targetContentRows,
216
253
  sections,
217
- hints: ['[*] >10% of context', '[Esc] Close'],
254
+ hints: CONTEXT_INSPECTOR_CONTEXT_HINTS,
218
255
  }, width);
219
256
  }
@@ -31,7 +31,7 @@ export class DiffEngine {
31
31
  for (let y = 0; y < newBuffer.height; y++) {
32
32
  // Skip rows that were not written in either the old or new buffer.
33
33
  // If neither side touched the row, both must match the prior frame:
34
- // old row was never written this frame (clean) and new row is also
34
+ // old row had no write this frame (clean) and new row is also
35
35
  // clean, so the on-screen content is still correct. No diff needed.
36
36
  const newDirty = newBuffer.dirtyRows[y] ?? false;
37
37
  const oldDirty = oldBuffer ? (oldBuffer.dirtyRows[y] ?? false) : true;
@@ -11,6 +11,20 @@ import {
11
11
  } from './overlay-box.ts';
12
12
  import { getOverlaySurfaceMetrics } from './overlay-viewport.ts';
13
13
 
14
+ const FILE_PICKER_TITLE = 'Select File';
15
+ const FILE_PICKER_SEARCH_PREFIX = '@ ';
16
+ const FILE_PICKER_EMPTY_MESSAGE = 'No matching files';
17
+ const FILE_PICKER_HINTS = '[Up/Down] Navigate [/] Search [Enter] Select [Esc] Cancel';
18
+
19
+ export function renderFilePickerPackageText(): string {
20
+ return [
21
+ FILE_PICKER_TITLE,
22
+ FILE_PICKER_SEARCH_PREFIX.trim(),
23
+ FILE_PICKER_EMPTY_MESSAGE,
24
+ FILE_PICKER_HINTS,
25
+ ].join('\n');
26
+ }
27
+
14
28
  /**
15
29
  * Render the file picker modal as Line[] for overlay in the viewport.
16
30
  * Shows a bordered box with fuzzy-matched file results.
@@ -37,13 +51,13 @@ export function renderFilePickerOverlay(
37
51
 
38
52
  // Title bar
39
53
  const titleLine = createOverlayFilledBorderLine(width, layout, OVERLAY_GLYPHS.topLeft, OVERLAY_GLYPHS.horizontal, OVERLAY_GLYPHS.topRight, borderFg, DEFAULT_OVERLAY_PALETTE.titleBg);
40
- putOverlayText(titleLine, layout.margin + 2, layout.width - 4, 'Select File', { fg: titleFg, bold: true });
54
+ putOverlayText(titleLine, layout.margin + 2, layout.width - 4, FILE_PICKER_TITLE, { fg: titleFg, bold: true });
41
55
  lines.push(titleLine);
42
56
 
43
57
  // Search input
44
58
  const queryDisplay = picker.query || '';
45
59
  const searchLine = createOverlayContentLine(width, layout, borderFg, DEFAULT_OVERLAY_PALETTE.inputBg);
46
- const searchPrefix = '@ ';
60
+ const searchPrefix = FILE_PICKER_SEARCH_PREFIX;
47
61
  const queryText = fitDisplay(`${queryDisplay}${picker.searchFocused ? OVERLAY_GLYPHS.cursor : ''}`, Math.max(0, contentW - getDisplayWidth(searchPrefix)));
48
62
  putOverlayText(searchLine, layout.margin + 2, getDisplayWidth(searchPrefix), searchPrefix, { fg: picker.searchFocused ? bodyFg : mutedFg });
49
63
  putOverlayText(searchLine, layout.margin + 2 + getDisplayWidth(searchPrefix), contentW - getDisplayWidth(searchPrefix), queryText, { fg: picker.query.length > 0 || picker.searchFocused ? bodyFg : mutedFg });
@@ -54,8 +68,8 @@ export function renderFilePickerOverlay(
54
68
 
55
69
  // Results
56
70
  if (picker.results.length === 0) {
57
- const noResults = createOverlayContentLine(width, layout, borderFg, DEFAULT_OVERLAY_PALETTE.bodyBg);
58
- putOverlayText(noResults, layout.margin + 2, contentW, fitDisplay('No matching files', contentW), { fg: '244', dim: true });
71
+ const noResults = createOverlayContentLine(width, layout, borderFg, DEFAULT_OVERLAY_PALETTE.bodyBg);
72
+ putOverlayText(noResults, layout.margin + 2, contentW, fitDisplay(FILE_PICKER_EMPTY_MESSAGE, contentW), { fg: '244', dim: true });
59
73
  lines.push(noResults);
60
74
  } else {
61
75
  const maxVisible = metrics.contentRows;
@@ -92,9 +106,8 @@ export function renderFilePickerOverlay(
92
106
  }
93
107
 
94
108
  // Bottom border with hints
95
- const hints = '[Up/Down] Navigate [/] Search [Enter] Select [Esc] Cancel';
96
109
  const bottomLine = createOverlayFilledBorderLine(width, layout, OVERLAY_GLYPHS.bottomLeft, OVERLAY_GLYPHS.horizontal, OVERLAY_GLYPHS.bottomRight, borderFg, DEFAULT_OVERLAY_PALETTE.sectionBg);
97
- putOverlayText(bottomLine, layout.margin + 2, layout.width - 4, truncateDisplay(hints, layout.width - 4), { fg: mutedFg, dim: true });
110
+ putOverlayText(bottomLine, layout.margin + 2, layout.width - 4, truncateDisplay(FILE_PICKER_HINTS, layout.width - 4), { fg: mutedFg, dim: true });
98
111
  lines.push(bottomLine);
99
112
 
100
113
  return lines;
@@ -12,6 +12,105 @@ import { getOverlaySurfaceMetrics } from './overlay-viewport.ts';
12
12
  import { getVisibleWindow } from './surface-layout.ts';
13
13
  import { logger } from '@pellux/goodvibes-sdk/platform/utils';
14
14
 
15
+ const FEATURED_HELP_COMMANDS: Array<[name: string, argHint: string, desc: string]> = [
16
+ ['agent', '', 'Open workspace; press / there to search every action'],
17
+ ['agent', 'setup', 'Open setup workspace for local behavior, channels, and voice'],
18
+ ['agent', 'knowledge', 'Open isolated Agent Knowledge workspace actions'],
19
+ ['agent', 'voice-media', 'Open voice, image, browser, and media workspace actions'],
20
+ ['setup', '', 'Open Agent setup with current settings preloaded'],
21
+ ['knowledge', 'status', 'Inspect isolated Agent Knowledge readiness'],
22
+ ['memory', '', 'Manage Agent-local memory records'],
23
+ ['personas', '', 'Manage serial Agent operating personas'],
24
+ ['skills', '', 'Manage Agent-local skills and bundles'],
25
+ ['routines', '', 'Manage reusable main-conversation routines'],
26
+ ['workplan', '', 'Inspect shared work-plan state'],
27
+ ['approval', '', 'Review and explicitly act on approvals'],
28
+ ['automation', '', 'Run confirmed connected-host automation actions'],
29
+ ['schedule', 'remind', 'Create confirmed reminders or inspect schedules'],
30
+ ['delegate', '', 'Explicitly hand build/fix/review work to GoodVibes TUI'],
31
+ ['mcp', '', 'Inspect MCP servers and tool readiness'],
32
+ ['provider', '', 'Choose provider or model family'],
33
+ ['model', '', 'Select the active model route'],
34
+ ['subscription', '', 'Review provider logins and subscriptions'],
35
+ ['secrets', '', 'Manage secret references without printing values'],
36
+ ['bundle', 'inspect', 'Inspect Agent support bundles from the TUI'],
37
+ ['compat', '', 'Inspect connected-host and Agent Knowledge compatibility'],
38
+ ['health', '', 'Run Agent runtime and setup diagnostics'],
39
+ ];
40
+
41
+ const HELP_OVERLAY_STATIC_TEXT = [
42
+ 'Core Navigation',
43
+ 'Scroll / history recall',
44
+ 'Scroll by full page',
45
+ 'Search conversation (Ctrl+F)',
46
+ 'Prompt And Editing',
47
+ 'Submit message',
48
+ 'Insert newline',
49
+ 'Paste (image priority)',
50
+ 'Undo / redo',
51
+ 'Overlays And Workspace',
52
+ 'Toggle help',
53
+ 'Full keyboard shortcuts',
54
+ 'Open the Agent operator workspace',
55
+ 'Search all Agent workspace actions',
56
+ 'Open selected action or form',
57
+ 'Quick Start',
58
+ 'Available Slash Commands',
59
+ 'More Commands',
60
+ 'Essentials',
61
+ 'Show this help overlay',
62
+ 'Keyboard shortcut reference',
63
+ 'Select LLM model',
64
+ 'Clear conversation',
65
+ ] as const;
66
+
67
+ const SHORTCUT_OVERLAY_STATIC_TEXT = [
68
+ 'Navigation',
69
+ 'Scroll / history recall',
70
+ 'Scroll by full page',
71
+ 'Jump to start / end of line',
72
+ 'Search conversation',
73
+ 'Scroll conversation or hovered panel',
74
+ 'Editing',
75
+ 'Submit message',
76
+ 'Insert newline',
77
+ 'Open file picker',
78
+ 'Slash command mode',
79
+ 'Paste (image priority)',
80
+ 'Undo / redo',
81
+ 'Clear prompt',
82
+ 'Delete word backward',
83
+ 'Kill to end of line',
84
+ 'Move to start of line',
85
+ 'Next error / line end',
86
+ 'Actions',
87
+ 'Collapse/expand block',
88
+ 'Bookmark block',
89
+ 'Copy block to clipboard',
90
+ 'Block file save disabled; copy or export',
91
+ 'Copy selection',
92
+ 'Process monitor',
93
+ 'Help overlay',
94
+ 'Exit',
95
+ 'Workspace',
96
+ 'Swap focus between input and active Agent workspace',
97
+ 'Open the Agent operator workspace',
98
+ 'Cycle Agent workspace category forward',
99
+ 'Cycle Agent workspace category backward',
100
+ 'Config: /keybindings to list and customize',
101
+ ] as const;
102
+
103
+ export function renderHelpOverlayPackageText(): string {
104
+ return [
105
+ ...HELP_OVERLAY_STATIC_TEXT,
106
+ ...FEATURED_HELP_COMMANDS.flatMap(([name, argHint, desc]) => [
107
+ argHint ? `/${name} ${argHint}` : `/${name}`,
108
+ desc,
109
+ ]),
110
+ ...SHORTCUT_OVERLAY_STATIC_TEXT,
111
+ ].join('\n');
112
+ }
113
+
15
114
  function toModalSections(rows: readonly string[]): import('./modal-factory.ts').ModalSection[] {
16
115
  return rows.map((row) => {
17
116
  if (row === '') return { type: 'spacer' as const };
@@ -57,37 +156,16 @@ export function renderHelpOverlay(
57
156
  ` ${kb('paste').padEnd(20)} Paste (image priority)`,
58
157
  ` ${(kb('undo') + ' / ' + kb('redo')).padEnd(20)} Undo / redo`,
59
158
  '',
60
- ' Overlays And Panels',
159
+ ' Overlays And Workspace',
61
160
  ' ' + '\u2500'.repeat(40),
62
161
  ` ${'?'.padEnd(20)} Toggle help`,
63
162
  ` ${'/shortcuts'.padEnd(20)} Full keyboard shortcuts`,
64
- ` ${kb('panel-picker').padEnd(20)} Open or focus the panel workspace`,
163
+ ` ${kb('panel-picker').padEnd(20)} Open the Agent operator workspace`,
164
+ ` ${'Workspace /'.padEnd(20)} Search all Agent workspace actions`,
165
+ ` ${'Workspace Enter'.padEnd(20)} Open selected action or form`,
65
166
  '',
66
167
  ];
67
168
 
68
- // Featured commands shown in the Quick Start section.
69
- // Each entry is [commandName, subcommandOrArgHint, description].
70
- // Commands not registered in the live registry are omitted at render time.
71
- const FEATURED_COMMANDS: Array<[name: string, argHint: string, desc: string]> = [
72
- ['agent', '', 'Open the Agent operator workspace'],
73
- ['setup', '', 'Open Agent setup with current settings preloaded'],
74
- ['knowledge', 'status', 'Inspect isolated Agent Knowledge readiness'],
75
- ['memory', '', 'Manage local Agent memory records'],
76
- ['personas', '', 'Manage serial Agent operating personas'],
77
- ['agent-skills', '', 'Manage local Agent skills and bundles'],
78
- ['routines', '', 'Manage reusable main-conversation routines'],
79
- ['workplan', '', 'Inspect shared work-plan state'],
80
- ['approval', '', 'Review and explicitly act on approvals'],
81
- ['schedule', 'remind', 'Create confirmed reminders or inspect schedules'],
82
- ['delegate', '', 'Explicitly hand build/fix/review work to GoodVibes TUI'],
83
- ['mcp', '', 'Inspect MCP servers and tool readiness'],
84
- ['provider', '', 'Choose provider or model family'],
85
- ['model', '', 'Select the active model route'],
86
- ['subscription', '', 'Review provider logins and subscriptions'],
87
- ['secrets', '', 'Manage secret references without printing values'],
88
- ['health', '', 'Run Agent runtime and setup diagnostics'],
89
- ];
90
-
91
169
  // Build command rows from featured list, filtering out unregistered commands.
92
170
  function featuredRow(name: string, argHint: string, desc: string): string {
93
171
  const invocation = argHint ? `/${name} ${argHint}` : `/${name}`;
@@ -96,7 +174,7 @@ export function renderHelpOverlay(
96
174
 
97
175
  const quickStartRows: string[] = [];
98
176
  try {
99
- for (const [name, argHint, desc] of FEATURED_COMMANDS) {
177
+ for (const [name, argHint, desc] of FEATURED_HELP_COMMANDS) {
100
178
  if (!hasCommand(name)) continue; // omit if not in live registry
101
179
  quickStartRows.push(featuredRow(name, argHint, desc));
102
180
  }
@@ -105,7 +183,7 @@ export function renderHelpOverlay(
105
183
  // unfiltered quick-start list so /help remains reachable.
106
184
  logger.warn(`[help-overlay] registry traversal error during command filter; using unfiltered list: ${err}`);
107
185
  quickStartRows.length = 0;
108
- for (const [name, argHint, desc] of FEATURED_COMMANDS) {
186
+ for (const [name, argHint, desc] of FEATURED_HELP_COMMANDS) {
109
187
  quickStartRows.push(featuredRow(name, argHint, desc));
110
188
  }
111
189
  }
@@ -123,7 +201,7 @@ export function renderHelpOverlay(
123
201
  'knowledge',
124
202
  'memory',
125
203
  'personas',
126
- 'agent-skills',
204
+ 'skills',
127
205
  'routines',
128
206
  'workplan',
129
207
  'approval',
@@ -238,7 +316,7 @@ export function renderShortcutsOverlay(
238
316
  row(kb('clear-prompt'), 'Clear prompt'),
239
317
  row(kb('delete-word'), 'Delete word backward'),
240
318
  row(kb('kill-line'), 'Kill to end of line'),
241
- row(kb('apply-diff-line-start'), 'Delegate diff changes / line start'),
319
+ row(kb('line-start'), 'Move to start of line'),
242
320
  row(kb('next-error-line-end'), 'Next error / line end'),
243
321
  '',
244
322
  ' Actions',
@@ -252,12 +330,12 @@ export function renderShortcutsOverlay(
252
330
  row('?', 'Help overlay'),
253
331
  row(`${kb('clear-cancel')} x2`, 'Exit'),
254
332
  '',
255
- ' Panels',
333
+ ' Workspace',
256
334
  ' ' + '\u2500'.repeat(40),
257
- row('Tab', 'Swap focus between input and panel workspace'),
258
- row(kb('panel-picker'), 'Open / focus / hide panel workspace'),
259
- row(kb('panel-tab-next'), 'Next workspace panel tab'),
260
- row(kb('panel-tab-prev'), 'Previous workspace panel tab'),
335
+ row('Tab', 'Swap focus between input and active Agent workspace'),
336
+ row(kb('panel-picker'), 'Open the Agent operator workspace'),
337
+ row(kb('panel-tab-next'), 'Cycle Agent workspace category forward'),
338
+ row(kb('panel-tab-prev'), 'Cycle Agent workspace category backward'),
261
339
  '',
262
340
  ` Config: /keybindings to list and customize`,
263
341
  ];
@@ -3,6 +3,22 @@ import { getDisplayWidth } from '../utils/terminal-width.ts';
3
3
  import type { HistorySearch } from '../input/input-history.ts';
4
4
  import { createBottomBarLine, writeBottomBarText } from './bottom-bar.ts';
5
5
 
6
+ const HISTORY_SEARCH_PREFIX = '(reverse-i-search)`';
7
+ const HISTORY_SEARCH_FAILED_PREFIX = '(failed reverse-i-search)`';
8
+ const HISTORY_SEARCH_QUERY_SUFFIX = "': ";
9
+
10
+ function historySearchLabel(prefix: string, query: string): string {
11
+ return prefix + query + HISTORY_SEARCH_QUERY_SUFFIX;
12
+ }
13
+
14
+ export function renderHistorySearchOverlayPackageText(): string {
15
+ return [
16
+ historySearchLabel(HISTORY_SEARCH_PREFIX, '<query>'),
17
+ historySearchLabel(HISTORY_SEARCH_FAILED_PREFIX, '<query>'),
18
+ '<matched-command-text>',
19
+ ].join('\n');
20
+ }
21
+
6
22
  /**
7
23
  * Truncate `text` to at most `maxWidth` display columns, then pad with spaces
8
24
  * to exactly `maxWidth` columns. CJK/emoji wide characters count as 2 columns.
@@ -42,13 +58,12 @@ export function renderHistorySearchOverlay(
42
58
  const noMatch = historySearch.query.length > 0 && !hasMatch;
43
59
 
44
60
  const prefix = noMatch
45
- ? '(failed reverse-i-search)`'
46
- : '(reverse-i-search)`';
47
- const queryPart = historySearch.query + "': ";
61
+ ? HISTORY_SEARCH_FAILED_PREFIX
62
+ : HISTORY_SEARCH_PREFIX;
48
63
  const matchText = hasMatch ? match?.entry ?? '' : '';
49
64
 
50
65
  // Build the display string
51
- const label = prefix + queryPart;
66
+ const label = historySearchLabel(prefix, historySearch.query);
52
67
  const full = truncateToWidth(label + matchText, width);
53
68
 
54
69
  const line = createBottomBarLine(width, { fg: '#000000', bg: '#00ffcc' });
@@ -4,6 +4,28 @@ import type { ProcessManager } from '@pellux/goodvibes-sdk/platform/tools';
4
4
  import type { ProcessEntry } from './process-modal.ts';
5
5
  import { getOverlaySurfaceMetrics, getStableOverlayContentRows } from './overlay-viewport.ts';
6
6
 
7
+ const LIVE_TAIL_PROCESS_TAG = '[exec]';
8
+ const LIVE_TAIL_EMPTY_OUTPUT = '(no output yet)';
9
+ const LIVE_TAIL_SCROLL_HINT = '[Up/Down] Scroll';
10
+ const LIVE_TAIL_HINTS = [LIVE_TAIL_SCROLL_HINT, '[k] Stop process', '[Esc] Back'];
11
+
12
+ function liveTailTitle(label: string): string {
13
+ return `${LIVE_TAIL_PROCESS_TAG} ${label}`;
14
+ }
15
+
16
+ function liveTailScrollInfo(start: string | number, end: string | number, total: string | number): string {
17
+ return ` Lines ${start}-${end} of ${total} ${LIVE_TAIL_SCROLL_HINT}`;
18
+ }
19
+
20
+ export function renderLiveTailModalPackageText(): string {
21
+ return [
22
+ liveTailTitle('<process>'),
23
+ LIVE_TAIL_EMPTY_OUTPUT,
24
+ liveTailScrollInfo('<start>', '<end>', '<total>'),
25
+ ...LIVE_TAIL_HINTS,
26
+ ].join('\n');
27
+ }
28
+
7
29
  export interface LiveTailModalDeps {
8
30
  readonly processManager: Pick<ProcessManager, 'stop' | 'getOutput'>;
9
31
  }
@@ -45,7 +67,7 @@ export class LiveTailModal {
45
67
  const output = this.deps.processManager.getOutput(this.entry.id);
46
68
  if (!output) return '';
47
69
  const combined = [output.stdout, output.stderr].filter(Boolean).join('\n').trim();
48
- return combined || '(no output yet)';
70
+ return combined || LIVE_TAIL_EMPTY_OUTPUT;
49
71
  }
50
72
  }
51
73
 
@@ -74,9 +96,9 @@ export function renderLiveTailModal(
74
96
  const visibleLines = allLines.slice(startIdx, endIdx);
75
97
 
76
98
  const maxLabelW = Math.max(20, width - 30);
77
- const title = `[exec] ${entry.label.slice(0, maxLabelW)}`;
99
+ const title = liveTailTitle(entry.label.slice(0, maxLabelW));
78
100
  const scrollInfo = totalLines > maxOutputLines
79
- ? ` Lines ${startIdx + 1}-${Math.min(endIdx, totalLines)} of ${totalLines} [Up/Down] Scroll`
101
+ ? liveTailScrollInfo(startIdx + 1, Math.min(endIdx, totalLines), totalLines)
80
102
  : '';
81
103
 
82
104
  const sections: import('./modal-factory.ts').ModalSection[] = [];
@@ -84,7 +106,7 @@ export function renderLiveTailModal(
84
106
  sections.push({ type: 'text', content: scrollInfo });
85
107
  sections.push({ type: 'separator' });
86
108
  }
87
- sections.push({ type: 'text', content: visibleLines.join('\n') || '(no output yet)' });
109
+ sections.push({ type: 'text', content: visibleLines.join('\n') || LIVE_TAIL_EMPTY_OUTPUT });
88
110
 
89
111
  return ModalFactory.createModal({
90
112
  title,
@@ -92,6 +114,6 @@ export function renderLiveTailModal(
92
114
  margin: 2,
93
115
  targetContentRows,
94
116
  sections,
95
- hints: ['[Up/Down] Scroll', '[k] Stop process', '[Esc] Back'],
117
+ hints: LIVE_TAIL_HINTS,
96
118
  }, width);
97
119
  }