wepscli 0.1.0 → 0.1.5

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 (43) hide show
  1. package/README.md +9 -8
  2. package/dist/WEPSCLI-shell/agent-runtime-events.js +194 -0
  3. package/dist/WEPSCLI-shell/agent-runtime-events.js.map +1 -0
  4. package/dist/WEPSCLI-shell/agent-runtime-helpers.js +117 -0
  5. package/dist/WEPSCLI-shell/agent-runtime-helpers.js.map +1 -0
  6. package/dist/WEPSCLI-shell/agent-runtime-types.js +1 -0
  7. package/dist/WEPSCLI-shell/agent-runtime-types.js.map +1 -0
  8. package/dist/WEPSCLI-shell/agent-runtime.js +42 -294
  9. package/dist/WEPSCLI-shell/agent-runtime.js.map +1 -1
  10. package/dist/WEPSCLI-shell/approval-overlay.js.map +1 -1
  11. package/dist/WEPSCLI-shell/chat-components.js +13 -6
  12. package/dist/WEPSCLI-shell/chat-components.js.map +1 -1
  13. package/dist/WEPSCLI-shell/file-change-preview.js.map +1 -1
  14. package/dist/WEPSCLI-shell/runtime-recovery.js.map +1 -1
  15. package/dist/WEPSCLI-shell/session-consistency.js +9 -0
  16. package/dist/WEPSCLI-shell/session-consistency.js.map +1 -0
  17. package/dist/WEPSCLI-shell/shell-app.js +169 -259
  18. package/dist/WEPSCLI-shell/shell-app.js.map +1 -1
  19. package/dist/WEPSCLI-shell/shell-keyboard.js +135 -0
  20. package/dist/WEPSCLI-shell/shell-keyboard.js.map +1 -0
  21. package/dist/WEPSCLI-shell/shell-modes.js.map +1 -1
  22. package/dist/WEPSCLI-shell/shell-prompt-controller.js +93 -0
  23. package/dist/WEPSCLI-shell/shell-prompt-controller.js.map +1 -0
  24. package/dist/WEPSCLI-shell/shell-status.js +16 -0
  25. package/dist/WEPSCLI-shell/shell-status.js.map +1 -0
  26. package/dist/WEPSCLI-shell/skill-commands.js +7 -0
  27. package/dist/WEPSCLI-shell/skill-commands.js.map +1 -0
  28. package/dist/WEPSCLI-shell/slash-commands.js +68 -7
  29. package/dist/WEPSCLI-shell/slash-commands.js.map +1 -1
  30. package/dist/WEPSCLI-shell/tool-approval.js.map +1 -1
  31. package/dist/WEPSCLI-shell/tool-diff.js.map +1 -1
  32. package/dist/WEPSCLI-shell/tool-file-changes.js.map +1 -1
  33. package/dist/WEPSCLI-shell/tool-message-detail.js.map +1 -1
  34. package/dist/WEPSCLI-shell/tool-messages.js.map +1 -1
  35. package/dist/WEPSCLI-shell/transcript-panel.js.map +1 -1
  36. package/dist/config.js +3 -0
  37. package/dist/config.js.map +1 -1
  38. package/dist/main.js.map +1 -1
  39. package/dist/skills/skill-add-flow.js +88 -0
  40. package/dist/skills/skill-add-flow.js.map +1 -0
  41. package/dist/skills/skill-service.js +123 -0
  42. package/dist/skills/skill-service.js.map +1 -0
  43. package/package.json +28 -24
@@ -6,18 +6,24 @@ import { createTextNode as _$createTextNode } from "@opentui/solid";
6
6
  import { insertNode as _$insertNode } from "@opentui/solid";
7
7
  import { setProp as _$setProp } from "@opentui/solid";
8
8
  import { createElement as _$createElement } from "@opentui/solid";
9
- import { useKeyboard, useRenderer, useTerminalDimensions } from "@opentui/solid";
10
- import { batch, createEffect, createMemo, createSignal, onMount } from "solid-js";
9
+ import { useRenderer, useTerminalDimensions } from "@opentui/solid";
10
+ import { createEffect, createMemo, createSignal, onMount } from "solid-js";
11
11
  import { SessionHistoryService } from "../session-history/session-history-service.js";
12
12
  import { getAgentDir } from "../config.js";
13
13
  import { ApprovalOverlay } from "./approval-overlay.js";
14
14
  import { WepsAgentRuntime } from "./agent-runtime.js";
15
15
  import { writeShellDebugLog } from "./debug-log.js";
16
- import { executeSlashCommand } from "./slash-commands.js";
17
- import { createIdleRuntimeState, runtimeStateTone } from "./runtime-status.js";
18
- import { applyShellModePrompt, getShellMode, nextShellMode, shellModeSwitchMessage } from "./shell-modes.js";
16
+ import { useShellKeyboard } from "./shell-keyboard.js";
17
+ import { getSessionApprovalRequest, nextFocusRegionForSession } from "./session-consistency.js";
18
+ import { createSkillAddFlow } from "../skills/skill-add-flow.js";
19
+ import { formatInstalledSkillsSummary, listInstalledSkills } from "../skills/skill-service.js";
20
+ import { createShellPromptController } from "./shell-prompt-controller.js";
21
+ import { buildSkillSlashCommands } from "./skill-commands.js";
22
+ import { createIdleRuntimeState } from "./runtime-status.js";
23
+ import { runtimeStatusColor } from "./shell-status.js";
24
+ import { getShellMode, nextShellMode, shellModeSwitchMessage } from "./shell-modes.js";
19
25
  import { wepscliShellTheme as theme } from "./theme.js";
20
- import { formatTimestamp, INITIAL_SESSIONS, overlayDescription, overlayTitle, truncate, wrapIndex } from "./helpers.js";
26
+ import { formatTimestamp, INITIAL_SESSIONS, overlayDescription, overlayTitle, truncate } from "./helpers.js";
21
27
  import { createProviderAddFlow } from "./provider-add-flow.js";
22
28
  import { appendSessionMessage, insertSessionMessageBefore, patchSessionMessage, toggleSessionMessage as toggleSessionMessageState } from "./transcript-state.js";
23
29
  import { TranscriptPanel } from "./transcript-panel.js";
@@ -27,7 +33,6 @@ export function WEPSCLIShellApp(props) {
27
33
  const renderer = useRenderer();
28
34
  const dimensions = useTerminalDimensions();
29
35
  const sessionHistory = new SessionHistoryService();
30
- const [view, setView] = createSignal("home");
31
36
  const [focusRegion, setFocusRegionState] = createSignal("composer");
32
37
  const [overlay, setOverlay] = createSignal(undefined);
33
38
  const [profiles, setProfiles] = createSignal(props.profileService.listProfiles());
@@ -42,6 +47,7 @@ export function WEPSCLIShellApp(props) {
42
47
  const [runtimeStateBySession, setRuntimeStateBySession] = createSignal({});
43
48
  const [approvalRequests, setApprovalRequests] = createSignal([]);
44
49
  const [approvalDecisionIndex, setApprovalDecisionIndex] = createSignal(0);
50
+ const [skillSlashCommands, setSkillSlashCommands] = createSignal([]);
45
51
  const runtime = new WepsAgentRuntime(props.profileService, {
46
52
  appendMessage: (sessionId, message) => appendTranscriptMessage(sessionId, message),
47
53
  insertMessageBefore: (sessionId, beforeMessageId, message) => insertTranscriptMessageBefore(sessionId, beforeMessageId, message),
@@ -87,34 +93,47 @@ export function WEPSCLIShellApp(props) {
87
93
  },
88
94
  onClose: () => setFocusRegion("composer")
89
95
  });
96
+ const skillAddFlow = createSkillAddFlow({
97
+ onInstalled: async ({
98
+ skill,
99
+ targetDir,
100
+ diagnostics
101
+ }) => {
102
+ const session = currentSession();
103
+ const sessionId = session?.id ?? createdTransientSessionId();
104
+ const warnings = diagnostics.filter(diagnostic => diagnostic.type === "warning").length;
105
+ const reloaded = session ? await runtime.reload(session.id, selectionForSession(session), {
106
+ runtimeSessionFile: session.runtimeSessionFile
107
+ }) : false;
108
+ await reloadSkillSlashCommands();
109
+ pushChatMessage(sessionId, {
110
+ id: `skill-add:${skill.name}:${Date.now()}`,
111
+ role: "system",
112
+ content: reloaded ? `Installed skill ${skill.name} and reloaded the current session.\nPath: ${targetDir}${warnings > 0 ? `\nWarnings: ${warnings}` : ""}` : `Installed skill ${skill.name}.\nPath: ${targetDir}${warnings > 0 ? `\nWarnings: ${warnings}` : ""}`,
113
+ time: new Date().toLocaleTimeString("en-US", {
114
+ hour: "2-digit",
115
+ minute: "2-digit"
116
+ }),
117
+ kind: "status"
118
+ });
119
+ },
120
+ onClose: () => setFocusRegion("composer")
121
+ });
90
122
  const viewport = createMemo(() => ({
91
123
  width: dimensions().width || 120,
92
124
  height: dimensions().height || 32
93
125
  }));
94
- const showSidebar = createMemo(() => false);
95
- const shellReady = createMemo(() => profiles().length > 0);
96
126
  const activeShellMode = createMemo(() => getShellMode(shellMode()));
97
- const activeProfile = createMemo(() => {
98
- const current = selection().profileId;
99
- return current ? profiles().find(profile => profile.id === current) : profiles()[0];
100
- });
127
+ const activeProfile = createMemo(() => selection().profileId ? profiles().find(profile => profile.id === selection().profileId) : profiles()[0]);
101
128
  const activeModel = createMemo(() => selection().modelId ?? activeProfile()?.models[0]?.id);
102
129
  const currentSession = createMemo(() => {
103
- const currentId = activeSessionId();
104
- const list = sessions();
130
+ const currentId = activeSessionId(),
131
+ list = sessions();
105
132
  return currentId ? list.find(item => item.id === currentId) : list.find(item => item.state === "active") ?? list[0];
106
133
  });
107
- const currentMessages = createMemo(() => {
108
- const session = currentSession();
109
- return session ? messagesBySession()[session.id] ?? [] : [];
110
- });
111
- const currentRuntimeState = createMemo(() => {
112
- const session = currentSession();
113
- return session ? runtimeStateBySession()[session.id] ?? createIdleRuntimeState() : createIdleRuntimeState();
114
- });
115
- const activeApproval = createMemo(() => approvalRequests()[0]);
116
- const recentSessions = createMemo(() => sessions().slice(0, 5));
117
- const sessionSummary = createMemo(() => currentSession()?.summary ?? "No session selected yet.");
134
+ const currentMessages = createMemo(() => currentSession() ? messagesBySession()[currentSession().id] ?? [] : []);
135
+ const currentRuntimeState = createMemo(() => currentSession() ? runtimeStateBySession()[currentSession().id] ?? createIdleRuntimeState() : createIdleRuntimeState());
136
+ const activeApproval = createMemo(() => getSessionApprovalRequest(approvalRequests(), currentSession()?.id));
118
137
  const overlayOptions = createMemo(() => {
119
138
  switch (overlay()) {
120
139
  case "provider":
@@ -156,6 +175,7 @@ export function WEPSCLIShellApp(props) {
156
175
  });
157
176
  onMount(() => {
158
177
  writeShellDebugLog(`mounted chat shell profiles=${profiles().length} sessions=${sessionHistory.listSessions().length}`);
178
+ void reloadSkillSlashCommands();
159
179
  setTimeout(() => composerRef?.focus(), 10);
160
180
  });
161
181
  createEffect(() => {
@@ -179,132 +199,38 @@ export function WEPSCLIShellApp(props) {
179
199
  }
180
200
  writeShellDebugLog(`effect session=${session.id} messages=${currentMessages().length}`);
181
201
  });
182
- useKeyboard(evt => {
183
- writeShellDebugLog(`key name=${evt.name} focus=${focusRegion()} overlay=${overlay() ?? "none"}`);
184
- if (evt.ctrl && evt.name === "c") {
185
- evt.preventDefault();
186
- exitShell();
187
- return;
188
- }
189
- if (evt.ctrl && evt.name === ".") {
190
- evt.preventDefault();
191
- void abortActiveRequest();
192
- return;
193
- }
194
- if (activeApproval()) {
195
- if (evt.name === "escape") {
196
- evt.preventDefault();
197
- resolveActiveApproval("cancel");
198
- return;
199
- }
200
- if (evt.name === "left" || evt.name === "up") {
201
- evt.preventDefault();
202
- setApprovalDecisionIndex(current => wrapIndex(current, -1, 3));
203
- return;
204
- }
205
- if (evt.name === "right" || evt.name === "down") {
206
- evt.preventDefault();
207
- setApprovalDecisionIndex(current => wrapIndex(current, 1, 3));
208
- return;
209
- }
210
- if (evt.name === "return") {
211
- evt.preventDefault();
212
- resolveActiveApproval(["allow", "reject", "cancel"][approvalDecisionIndex()] ?? "cancel");
213
- return;
214
- }
215
- return;
216
- }
217
- if (providerAddFlow.isActive()) {
218
- if (evt.name === "escape") {
219
- evt.preventDefault();
220
- providerAddFlow.back();
221
- return;
222
- }
223
- if (providerAddFlow.isPickerStep()) {
224
- if (evt.name === "up") {
225
- evt.preventDefault();
226
- providerAddFlow.moveSelection(-1);
227
- return;
228
- }
229
- if (evt.name === "down") {
230
- evt.preventDefault();
231
- providerAddFlow.moveSelection(1);
232
- return;
233
- }
234
- if (evt.name === "return") {
235
- evt.preventDefault();
236
- providerAddFlow.confirmSelection();
237
- return;
238
- }
239
- }
240
- return;
241
- }
242
- if (overlay()) {
243
- if (evt.name === "escape") {
244
- evt.preventDefault();
245
- closeOverlay();
246
- return;
247
- }
248
- if (evt.name === "up") {
249
- evt.preventDefault();
250
- setOverlayIndex(wrapIndex(overlayIndex(), -1, overlayOptions().length));
251
- return;
252
- }
253
- if (evt.name === "down") {
254
- evt.preventDefault();
255
- setOverlayIndex(wrapIndex(overlayIndex(), 1, overlayOptions().length));
256
- return;
257
- }
258
- if (evt.name === "return") {
259
- evt.preventDefault();
260
- const target = overlayOptions()[overlayIndex()];
261
- if (target) activateAction(target.id);
262
- return;
263
- }
264
- return;
265
- }
266
- if (evt.option && evt.name === "m") {
267
- evt.preventDefault();
268
- applyShellMode(nextShellMode(shellMode()));
269
- return;
270
- }
271
- if (evt.option && ["1", "2", "3", "4"].includes(evt.name)) {
272
- evt.preventDefault();
273
- const nextMode = {
274
- "1": "agent",
275
- "2": "plan",
276
- "3": "read-only",
277
- "4": "auto-approve"
278
- }[evt.name];
279
- if (nextMode) {
280
- applyShellMode(nextMode);
281
- }
282
- return;
283
- }
284
- if (evt.name === "tab") {
285
- evt.preventDefault();
286
- cycleFocus();
287
- return;
288
- }
289
- if (focusRegion() === "main") {
290
- if (evt.name === "up") {
291
- evt.preventDefault();
292
- return;
293
- }
294
- if (evt.name === "down") {
295
- evt.preventDefault();
296
- return;
297
- }
298
- if (evt.name === "escape") {
299
- evt.preventDefault();
300
- setFocusRegion("composer");
301
- return;
202
+ useShellKeyboard({
203
+ focusRegion,
204
+ overlay,
205
+ overlayIndex,
206
+ overlayOptionsLength: () => overlayOptions().length,
207
+ hasActiveApproval: () => Boolean(activeApproval()),
208
+ approvalDecisionIndex,
209
+ providerAddFlowActive: () => providerAddFlow.isActive(),
210
+ providerAddFlowPickerStep: () => providerAddFlow.isPickerStep(),
211
+ skillAddFlowActive: () => skillAddFlow.isActive(),
212
+ composerValue: () => composerRef?.value ?? composerValue()
213
+ }, {
214
+ exitShell,
215
+ abortActiveRequest,
216
+ resolveActiveApproval,
217
+ setApprovalDecisionIndex: updater => setApprovalDecisionIndex(updater),
218
+ providerAddBack: () => providerAddFlow.back(),
219
+ providerAddMoveSelection: delta => providerAddFlow.moveSelection(delta),
220
+ providerAddConfirmSelection: () => providerAddFlow.confirmSelection(),
221
+ closeSkillAddFlow: () => skillAddFlow.close(),
222
+ closeOverlay,
223
+ setOverlayIndex: updater => setOverlayIndex(updater),
224
+ activateOverlaySelection: () => {
225
+ const target = overlayOptions()[overlayIndex()];
226
+ if (target) {
227
+ activateAction(target.id);
302
228
  }
303
- }
304
- if (focusRegion() === "composer" && evt.name === "escape" && !(composerRef?.value ?? "").trim()) {
305
- evt.preventDefault();
306
- exitShell();
307
- }
229
+ },
230
+ applyNextShellMode: () => applyShellMode(nextShellMode(shellMode())),
231
+ applyMode: modeId => applyShellMode(modeId),
232
+ cycleFocus,
233
+ setFocusRegion
308
234
  });
309
235
  function exitShell() {
310
236
  if (closed) return;
@@ -343,6 +269,10 @@ export function WEPSCLIShellApp(props) {
343
269
  setProfiles(props.profileService.listProfiles());
344
270
  setSelection(props.profileService.getActiveSelection());
345
271
  }
272
+ async function reloadSkillSlashCommands() {
273
+ const result = await listInstalledSkills();
274
+ setSkillSlashCommands(buildSkillSlashCommands(result.skills));
275
+ }
346
276
  function updateRuntimeState(sessionId, state) {
347
277
  setRuntimeStateBySession(current => ({
348
278
  ...current,
@@ -422,10 +352,12 @@ export function WEPSCLIShellApp(props) {
422
352
  }
423
353
  function openApprovalRequest(request) {
424
354
  writeShellDebugLog(`approval open tool=${request.toolName} id=${request.id}`);
425
- setOverlay(undefined);
426
355
  setApprovalRequests(current => [...current, request]);
427
- setApprovalDecisionIndex(0);
428
- setFocusRegion("overlay");
356
+ if (currentSession()?.id === request.sessionId) {
357
+ setOverlay(undefined);
358
+ setApprovalDecisionIndex(0);
359
+ setFocusRegion("overlay");
360
+ }
429
361
  requestRender();
430
362
  }
431
363
  function closeApprovalRequest(requestId) {
@@ -462,12 +394,6 @@ export function WEPSCLIShellApp(props) {
462
394
  const index = order.indexOf(focusRegion());
463
395
  setFocusRegion(order[(index + 1) % order.length] ?? "composer");
464
396
  }
465
- function setShellView(next) {
466
- batch(() => {
467
- setView(next);
468
- });
469
- setFocusRegion("composer");
470
- }
471
397
  function openOverlay(kind) {
472
398
  if (kind === "model" && profiles().length === 0) {
473
399
  return;
@@ -495,9 +421,18 @@ export function WEPSCLIShellApp(props) {
495
421
  }
496
422
  function openProviderAdd() {
497
423
  setOverlay(undefined);
424
+ skillAddFlow.close();
498
425
  providerAddFlow.open();
499
426
  setFocusRegion("overlay");
500
427
  }
428
+ function openSkillAdd() {
429
+ setOverlay(undefined);
430
+ if (providerAddFlow.isActive()) {
431
+ providerAddFlow.close();
432
+ }
433
+ skillAddFlow.open();
434
+ setFocusRegion("overlay");
435
+ }
501
436
  function ensureSessionTranscript(sessionId) {
502
437
  setMessagesBySession(current => {
503
438
  if (current[sessionId]) return current;
@@ -586,6 +521,16 @@ export function WEPSCLIShellApp(props) {
586
521
  reloadProviderState();
587
522
  }
588
523
  function activateSession(sessionId) {
524
+ cancelTranscriptAutoScroll();
525
+ if (providerAddFlow.isActive()) {
526
+ providerAddFlow.close();
527
+ }
528
+ if (skillAddFlow.isActive()) {
529
+ skillAddFlow.close();
530
+ }
531
+ setOverlay(undefined);
532
+ setOverlayIndex(0);
533
+ setApprovalDecisionIndex(0);
589
534
  setActiveSessionId(sessionId);
590
535
  ensureSessionTranscript(sessionId);
591
536
  syncActiveSelectionFromSession(sessionId);
@@ -593,7 +538,6 @@ export function WEPSCLIShellApp(props) {
593
538
  if (session) {
594
539
  sessionHistory.markActive(sessionId);
595
540
  setSessions(sessionHistory.listSessions());
596
- setShellView("home");
597
541
  void runtime.syncSelection(sessionId, selectionForSession(session));
598
542
  if (session.runtimeSessionFile) {
599
543
  void runtime.loadSession(sessionId, selectionForSession(session), {
@@ -601,7 +545,8 @@ export function WEPSCLIShellApp(props) {
601
545
  });
602
546
  }
603
547
  }
604
- closeOverlay();
548
+ setFocusRegion(nextFocusRegionForSession(sessionId, approvalRequests()));
549
+ scheduleTranscriptScrollToBottom();
605
550
  }
606
551
  function startNewSession() {
607
552
  const provider = activeProfile();
@@ -644,87 +589,67 @@ export function WEPSCLIShellApp(props) {
644
589
  });
645
590
  requestRender();
646
591
  }
647
- function handleComposerSubmit(value) {
648
- const trimmed = value.trim();
649
- writeShellDebugLog(`handleComposerSubmit raw=${JSON.stringify(value)} trimmed=${JSON.stringify(trimmed)}`);
650
- if (!trimmed) return;
651
- const session = currentSession() ?? startNewSession();
652
- writeShellDebugLog(`handleComposerSubmit currentSession=${session?.id ?? "none"}`);
653
- if (!session) {
654
- writeShellDebugLog("handleComposerSubmit no current session available");
655
- return;
656
- }
657
- if (trimmed.startsWith("/")) {
658
- writeShellDebugLog(`handleComposerSubmit slash=${trimmed}`);
659
- runSlashCommand(trimmed);
660
- return;
661
- }
662
- ensureSessionTranscript(session.id);
592
+ function updatePromptSession(session, trimmed, sessionSelection) {
663
593
  sessionHistory.updateSession(session.id, {
664
594
  title: `Chat: ${truncate(trimmed, 24)}`,
665
595
  summary: `Last prompt: ${truncate(trimmed, 60)}`,
666
- providerProfileId: selectionForSession(session).profileId,
596
+ providerProfileId: sessionSelection.profileId,
667
597
  providerLabel: activeProfile()?.label,
668
- modelId: selectionForSession(session).modelId,
598
+ modelId: sessionSelection.modelId,
669
599
  lastPrompt: trimmed,
670
600
  state: "active"
671
601
  });
672
602
  setSessions(sessionHistory.listSessions());
673
- writeShellDebugLog(`handleComposerSubmit sessionUpdated=${session.id}`);
674
- setComposerValue("");
675
- composerRef?.setText?.("");
676
- composerRef?.focus();
677
- void runtime.prompt(session.id, applyShellModePrompt(shellMode(), trimmed), selectionForSession(session), {
678
- runtimeSessionFile: session.runtimeSessionFile
679
- });
680
- requestRender();
681
603
  }
682
- function runSlashCommand(commandId) {
683
- executeSlashCommand(commandId, {
684
- startNewSession,
685
- openOverlay,
686
- openProviderAdd,
687
- compactCurrentSession,
688
- abortActiveRequest: () => {
689
- void abortActiveRequest();
690
- },
691
- setMode: modeId => applyShellMode(modeId),
692
- getCurrentMode: () => shellMode(),
693
- getStatusSummary: () => {
694
- const session = currentSession();
695
- const runtimeState = currentRuntimeState();
696
- return ["Current shell status:", `Session: ${session?.title ?? "none"}`, `Mode: ${activeShellMode().label}`, `Provider: ${activeProfile()?.label ?? "none"}`, `Model: ${activeModel() ?? "none"}`, `Runtime: ${runtimeState.label}`, `Agent dir: ${getAgentDir()}`].join("\n");
697
- },
698
- queuePromptTemplate: (title, prompt, summary) => {
699
- const session = currentSession() ?? startNewSession();
700
- pushChatMessage(session.id, {
701
- id: `${session.id}:system:${Date.now()}`,
702
- role: "system",
703
- content: `${title}: ${summary}`,
704
- time: new Date().toLocaleTimeString("en-US", {
705
- hour: "2-digit",
706
- minute: "2-digit"
707
- })
708
- });
709
- setComposerValue(prompt);
710
- composerRef?.setText?.(prompt);
711
- composerRef?.focus();
712
- setFocusRegion("composer");
713
- },
714
- pushTimeline: message => {
715
- const session = currentSession() ?? startNewSession();
716
- pushChatMessage(session.id, {
717
- id: `${session.id}:system:${Date.now()}`,
718
- role: "system",
719
- content: message,
720
- time: new Date().toLocaleTimeString("en-US", {
721
- hour: "2-digit",
722
- minute: "2-digit"
723
- })
724
- });
725
- }
604
+ async function reloadCurrentSessionResources() {
605
+ const session = currentSession();
606
+ if (!session) {
607
+ promptController.pushSystemMessage(createdTransientSessionId(), "No active session is ready to reload yet.");
608
+ return;
609
+ }
610
+ const reloaded = await runtime.reload(session.id, selectionForSession(session), {
611
+ runtimeSessionFile: session.runtimeSessionFile
726
612
  });
727
- }
613
+ await reloadSkillSlashCommands();
614
+ promptController.pushSystemMessage(session.id, reloaded ? "Reloaded skills, prompts, and extensions for the current session." : "Resource reload did not complete successfully.");
615
+ }
616
+ const promptController = createShellPromptController({
617
+ currentSession,
618
+ startNewSession,
619
+ createdTransientSessionId,
620
+ selectionForSession,
621
+ ensureSessionTranscript,
622
+ updatePromptSession,
623
+ pushChatMessage,
624
+ setComposerValue,
625
+ setComposerText: value => composerRef?.setText?.(value),
626
+ focusComposer: () => composerRef?.focus(),
627
+ setFocusRegionComposer: () => setFocusRegion("composer"),
628
+ requestRender,
629
+ runtimePrompt: (sessionId, text, sessionSelection, runtimeSessionFile) => {
630
+ void runtime.prompt(sessionId, text, sessionSelection, {
631
+ runtimeSessionFile
632
+ });
633
+ },
634
+ reloadCurrentSessionResources,
635
+ openSkillAdd,
636
+ openOverlay,
637
+ openProviderAdd,
638
+ compactCurrentSession,
639
+ abortActiveRequest: () => {
640
+ void abortActiveRequest();
641
+ },
642
+ applyShellMode,
643
+ getCurrentMode: () => shellMode(),
644
+ getStatusSummary: () => {
645
+ const session = currentSession();
646
+ return ["Current shell status:", `Session: ${session?.title ?? "none"}`, `Mode: ${activeShellMode().label}`, `Provider: ${activeProfile()?.label ?? "none"}`, `Model: ${activeModel() ?? "none"}`, `Runtime: ${currentRuntimeState().label}`, `Agent dir: ${getAgentDir()}`].join("\n");
647
+ },
648
+ showSkillsSummary: async () => {
649
+ promptController.pushSystemMessage(currentSession()?.id ?? createdTransientSessionId(), await formatInstalledSkillsSummary());
650
+ },
651
+ getAdditionalSlashCommands: skillSlashCommands
652
+ });
728
653
  function activateAction(actionId) {
729
654
  if (actionId === "session:new") {
730
655
  startNewSession();
@@ -839,30 +764,7 @@ export function WEPSCLIShellApp(props) {
839
764
  ensureSessionTranscript(created.id);
840
765
  return created.id;
841
766
  }
842
- const topStatus = createMemo(() => {
843
- const profile = activeProfile();
844
- const runtimeState = currentRuntimeState();
845
- if (!profile) return runtimeState.label;
846
- return `${profile.label} · ${activeModel() ?? "no model"} · ${runtimeState.label}`;
847
- });
848
- const sidebarSummary = createMemo(() => {
849
- if (!shellReady()) return "Complete onboarding to connect a provider.";
850
- return activeProfile()?.baseUrl ?? "Provider ready.";
851
- });
852
- function runtimeStatusColor(state) {
853
- switch (runtimeStateTone(state)) {
854
- case "accent":
855
- return theme.accent;
856
- case "warning":
857
- return theme.warning;
858
- case "danger":
859
- return theme.danger;
860
- case "success":
861
- return theme.success;
862
- case "muted":
863
- return theme.muted;
864
- }
865
- }
767
+ const topStatus = createMemo(() => `${activeProfile()?.label ?? "none"} · ${activeModel() ?? "no model"} · ${currentRuntimeState().label}`);
866
768
  return (() => {
867
769
  var _el$ = _$createElement("box"),
868
770
  _el$2 = _$createElement("box"),
@@ -973,8 +875,15 @@ export function WEPSCLIShellApp(props) {
973
875
  onFocus: () => setFocusRegion("composer"),
974
876
  onInput: value => setComposerValue(value),
975
877
  onModeClick: () => applyShellMode(nextShellMode(shellMode())),
976
- onSubmit: handleComposerSubmit,
977
- onSelectSlashCommand: runSlashCommand
878
+ get onSubmit() {
879
+ return promptController.handleComposerSubmit;
880
+ },
881
+ get onSelectSlashCommand() {
882
+ return promptController.runSlashCommand;
883
+ },
884
+ get slashCommands() {
885
+ return skillSlashCommands();
886
+ }
978
887
  }), _el$0);
979
888
  _$insert(_el$, (() => {
980
889
  var _c$2 = _$memo(() => !!overlay());
@@ -1012,6 +921,7 @@ export function WEPSCLIShellApp(props) {
1012
921
  }) : null;
1013
922
  })(), _el$0);
1014
923
  _$insert(_el$, () => providerAddFlow.render(), _el$0);
924
+ _$insert(_el$, () => skillAddFlow.render(), _el$0);
1015
925
  _$insertNode(_el$0, _el$1);
1016
926
  _$setProp(_el$0, "flexShrink", 0);
1017
927
  _$setProp(_el$0, "paddingLeft", 1);