pilotswarm 0.0.1 → 0.4.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 (149) hide show
  1. package/README.md +37 -1
  2. package/mcp/README.md +484 -0
  3. package/mcp/dist/bin/pilotswarm-mcp.d.ts +3 -0
  4. package/mcp/dist/bin/pilotswarm-mcp.d.ts.map +1 -0
  5. package/mcp/dist/bin/pilotswarm-mcp.js +367 -0
  6. package/mcp/dist/bin/pilotswarm-mcp.js.map +1 -0
  7. package/mcp/dist/src/auth.d.ts +7 -0
  8. package/mcp/dist/src/auth.d.ts.map +1 -0
  9. package/mcp/dist/src/auth.js +99 -0
  10. package/mcp/dist/src/auth.js.map +1 -0
  11. package/mcp/dist/src/context.d.ts +48 -0
  12. package/mcp/dist/src/context.d.ts.map +1 -0
  13. package/mcp/dist/src/context.js +83 -0
  14. package/mcp/dist/src/context.js.map +1 -0
  15. package/mcp/dist/src/index.d.ts +4 -0
  16. package/mcp/dist/src/index.d.ts.map +1 -0
  17. package/mcp/dist/src/index.js +3 -0
  18. package/mcp/dist/src/index.js.map +1 -0
  19. package/mcp/dist/src/prompts/skills.d.ts +4 -0
  20. package/mcp/dist/src/prompts/skills.d.ts.map +1 -0
  21. package/mcp/dist/src/prompts/skills.js +11 -0
  22. package/mcp/dist/src/prompts/skills.js.map +1 -0
  23. package/mcp/dist/src/resources/agents.d.ts +4 -0
  24. package/mcp/dist/src/resources/agents.d.ts.map +1 -0
  25. package/mcp/dist/src/resources/agents.js +64 -0
  26. package/mcp/dist/src/resources/agents.js.map +1 -0
  27. package/mcp/dist/src/resources/facts.d.ts +4 -0
  28. package/mcp/dist/src/resources/facts.d.ts.map +1 -0
  29. package/mcp/dist/src/resources/facts.js +125 -0
  30. package/mcp/dist/src/resources/facts.js.map +1 -0
  31. package/mcp/dist/src/resources/models.d.ts +4 -0
  32. package/mcp/dist/src/resources/models.d.ts.map +1 -0
  33. package/mcp/dist/src/resources/models.js +43 -0
  34. package/mcp/dist/src/resources/models.js.map +1 -0
  35. package/mcp/dist/src/resources/sessions.d.ts +4 -0
  36. package/mcp/dist/src/resources/sessions.d.ts.map +1 -0
  37. package/mcp/dist/src/resources/sessions.js +190 -0
  38. package/mcp/dist/src/resources/sessions.js.map +1 -0
  39. package/mcp/dist/src/resources/subscriptions.d.ts +9 -0
  40. package/mcp/dist/src/resources/subscriptions.d.ts.map +1 -0
  41. package/mcp/dist/src/resources/subscriptions.js +157 -0
  42. package/mcp/dist/src/resources/subscriptions.js.map +1 -0
  43. package/mcp/dist/src/server.d.ts +4 -0
  44. package/mcp/dist/src/server.d.ts.map +1 -0
  45. package/mcp/dist/src/server.js +59 -0
  46. package/mcp/dist/src/server.js.map +1 -0
  47. package/mcp/dist/src/tools/agents.d.ts +4 -0
  48. package/mcp/dist/src/tools/agents.d.ts.map +1 -0
  49. package/mcp/dist/src/tools/agents.js +317 -0
  50. package/mcp/dist/src/tools/agents.js.map +1 -0
  51. package/mcp/dist/src/tools/facts.d.ts +4 -0
  52. package/mcp/dist/src/tools/facts.d.ts.map +1 -0
  53. package/mcp/dist/src/tools/facts.js +151 -0
  54. package/mcp/dist/src/tools/facts.js.map +1 -0
  55. package/mcp/dist/src/tools/models.d.ts +4 -0
  56. package/mcp/dist/src/tools/models.d.ts.map +1 -0
  57. package/mcp/dist/src/tools/models.js +256 -0
  58. package/mcp/dist/src/tools/models.js.map +1 -0
  59. package/mcp/dist/src/tools/sessions.d.ts +4 -0
  60. package/mcp/dist/src/tools/sessions.d.ts.map +1 -0
  61. package/mcp/dist/src/tools/sessions.js +606 -0
  62. package/mcp/dist/src/tools/sessions.js.map +1 -0
  63. package/mcp/dist/src/util/command.d.ts +52 -0
  64. package/mcp/dist/src/util/command.d.ts.map +1 -0
  65. package/mcp/dist/src/util/command.js +78 -0
  66. package/mcp/dist/src/util/command.js.map +1 -0
  67. package/package.json +81 -6
  68. package/tui/README.md +35 -0
  69. package/tui/bin/tui.js +30 -0
  70. package/tui/plugins/.mcp.json +7 -0
  71. package/tui/plugins/plugin.json +13 -0
  72. package/tui/plugins/session-policy.json +8 -0
  73. package/tui/src/app.js +850 -0
  74. package/tui/src/auth/cli.js +111 -0
  75. package/tui/src/auth/entra-auth.js +218 -0
  76. package/tui/src/bootstrap-env.js +176 -0
  77. package/tui/src/embedded-workers.js +79 -0
  78. package/tui/src/http-transport-host.js +106 -0
  79. package/tui/src/index.js +340 -0
  80. package/tui/src/node-sdk-transport.js +1793 -0
  81. package/tui/src/platform.js +984 -0
  82. package/tui/src/plugin-config.js +239 -0
  83. package/tui/src/portal.js +7 -0
  84. package/tui/src/version.js +7 -0
  85. package/tui/tui-splash.txt +11 -0
  86. package/ui/core/README.md +6 -0
  87. package/ui/core/src/commands.js +93 -0
  88. package/ui/core/src/context-usage.js +212 -0
  89. package/ui/core/src/controller.js +6104 -0
  90. package/ui/core/src/formatting.js +1036 -0
  91. package/ui/core/src/history.js +932 -0
  92. package/ui/core/src/index.js +13 -0
  93. package/ui/core/src/layout.js +332 -0
  94. package/ui/core/src/reducer.js +1935 -0
  95. package/ui/core/src/selectors.js +5409 -0
  96. package/ui/core/src/session-errors.js +14 -0
  97. package/ui/core/src/session-tree.js +151 -0
  98. package/ui/core/src/state.js +248 -0
  99. package/ui/core/src/store.js +23 -0
  100. package/ui/core/src/system-titles.js +24 -0
  101. package/ui/core/src/themes/catppuccin-mocha.js +56 -0
  102. package/ui/core/src/themes/cobalt2.js +56 -0
  103. package/ui/core/src/themes/dark-high-contrast.js +56 -0
  104. package/ui/core/src/themes/daylight.js +62 -0
  105. package/ui/core/src/themes/dracula.js +56 -0
  106. package/ui/core/src/themes/github-dark.js +56 -0
  107. package/ui/core/src/themes/github-light.js +59 -0
  108. package/ui/core/src/themes/gruvbox-dark.js +56 -0
  109. package/ui/core/src/themes/hacker-x-matrix.js +56 -0
  110. package/ui/core/src/themes/hacker-x-orion-prime.js +56 -0
  111. package/ui/core/src/themes/helpers.js +79 -0
  112. package/ui/core/src/themes/high-contrast-mono.js +59 -0
  113. package/ui/core/src/themes/index.js +52 -0
  114. package/ui/core/src/themes/light-high-contrast.js +62 -0
  115. package/ui/core/src/themes/noctis-obscuro.js +56 -0
  116. package/ui/core/src/themes/noctis.js +56 -0
  117. package/ui/core/src/themes/paper-ink.js +62 -0
  118. package/ui/core/src/themes/solarized-ops.js +59 -0
  119. package/ui/core/src/themes/terminal-green.js +59 -0
  120. package/ui/core/src/themes/tokyo-night.js +56 -0
  121. package/ui/react/README.md +5 -0
  122. package/ui/react/src/chat-status.js +39 -0
  123. package/ui/react/src/components.js +1989 -0
  124. package/ui/react/src/index.js +4 -0
  125. package/ui/react/src/platform.js +15 -0
  126. package/ui/react/src/use-controller-state.js +38 -0
  127. package/ui/react/src/web-app.js +4390 -0
  128. package/web/README.md +198 -0
  129. package/web/api/router.js +196 -0
  130. package/web/api/ws.js +152 -0
  131. package/web/auth/authz/engine.js +204 -0
  132. package/web/auth/config.js +115 -0
  133. package/web/auth/index.js +175 -0
  134. package/web/auth/normalize/entra.js +22 -0
  135. package/web/auth/providers/entra.js +76 -0
  136. package/web/auth/providers/none.js +24 -0
  137. package/web/auth.js +10 -0
  138. package/web/bin/serve.js +53 -0
  139. package/web/config.js +20 -0
  140. package/web/dist/app.js +469 -0
  141. package/web/dist/assets/index-CBgQQk-j.css +1 -0
  142. package/web/dist/assets/index-DMefB7Wb.js +24 -0
  143. package/web/dist/assets/msal-CytV9RFv.js +7 -0
  144. package/web/dist/assets/pilotswarm-DyBlqVTY.js +87 -0
  145. package/web/dist/assets/react-BZwsW8pe.js +1 -0
  146. package/web/dist/index.html +16 -0
  147. package/web/runtime.js +454 -0
  148. package/web/server.js +276 -0
  149. package/index.js +0 -1
package/tui/src/app.js ADDED
@@ -0,0 +1,850 @@
1
+ import React from "react";
2
+ import { useInput, useStdin } from "ink";
3
+ import { UiPlatformProvider, SharedPilotSwarmApp } from "pilotswarm/ui-react";
4
+ import { UI_COMMANDS } from "pilotswarm/ui-core";
5
+ import { PILOTSWARM_CLI_VERSION_LABEL } from "./version.js";
6
+
7
+ const MOUSE_INPUT_PATTERN = /\u001b\[<(\d+);(\d+);(\d+)([mM])/gu;
8
+ const MOUSE_INPUT_FRAGMENT_PATTERN = /(?:\u001b)?\[<\d+;\d+;\d+[mM]/u;
9
+ const MOUSE_INPUT_CHUNK_PATTERN = /(?:\u001b?\[<|<)?\d+;\d+;\d+[mM]?/u;
10
+
11
+ export function isMouseInputSequence(input = "") {
12
+ const value = String(input || "");
13
+ return /^\u001b\[<\d+;\d+;\d+[mM]$/u.test(value)
14
+ || MOUSE_INPUT_FRAGMENT_PATTERN.test(value);
15
+ }
16
+
17
+ function looksLikeMouseInputFragment(input = "") {
18
+ const value = String(input || "");
19
+ if (!value) return false;
20
+ return value.includes("[<")
21
+ || value.startsWith("<")
22
+ || MOUSE_INPUT_CHUNK_PATTERN.test(value);
23
+ }
24
+
25
+ export function parseMouseInputSequences(input = "") {
26
+ const events = [];
27
+ MOUSE_INPUT_PATTERN.lastIndex = 0;
28
+ for (const match of String(input || "").matchAll(MOUSE_INPUT_PATTERN)) {
29
+ const code = Number(match[1]);
30
+ if (!Number.isFinite(code) || code >= 64) continue;
31
+ const buttonCode = code & 3;
32
+ const isRelease = match[4] === "m";
33
+ const isMotion = Boolean(code & 32);
34
+ const button = buttonCode === 0
35
+ ? "left"
36
+ : buttonCode === 1
37
+ ? "middle"
38
+ : buttonCode === 2
39
+ ? "right"
40
+ : "none";
41
+ events.push({
42
+ button,
43
+ type: isRelease ? "up" : (isMotion ? "drag" : "down"),
44
+ x: Math.max(0, Number(match[2]) - 1),
45
+ y: Math.max(0, Number(match[3]) - 1),
46
+ });
47
+ }
48
+ return events;
49
+ }
50
+
51
+ function formatCopyStatus(result) {
52
+ if (!result?.attempted) return null;
53
+ return result.copied ? "Copied to the clipboard" : "Clipboard copy failed";
54
+ }
55
+
56
+ export function getQuitPendingStatus(mode) {
57
+ return mode === "remote"
58
+ ? "Still quitting... disconnecting remote client"
59
+ : "Still quitting... stopping local workers";
60
+ }
61
+
62
+ export function isPlainShortcutKey(key = {}) {
63
+ return !key.ctrl && !key.meta;
64
+ }
65
+
66
+ export function PilotSwarmTuiApp({ controller, platform, onRequestExit }) {
67
+ const { stdin } = useStdin();
68
+ const mouseInputRef = React.useRef({ ignoreUntil: 0 });
69
+ const quitStateRef = React.useRef({
70
+ armedUntil: 0,
71
+ timer: null,
72
+ previousStatus: "",
73
+ });
74
+ const transientStatusRef = React.useRef({
75
+ timer: null,
76
+ previousStatus: "",
77
+ });
78
+ const exitingRef = React.useRef(false);
79
+ const [, forceRender] = React.useState(0);
80
+
81
+ const clearTransientStatus = React.useCallback((restoreStatus = false) => {
82
+ const transientStatus = transientStatusRef.current;
83
+ if (transientStatus.timer) {
84
+ clearTimeout(transientStatus.timer);
85
+ transientStatus.timer = null;
86
+ }
87
+ if (restoreStatus) {
88
+ controller.setStatus(transientStatus.previousStatus || "Connected");
89
+ }
90
+ }, [controller]);
91
+
92
+ const flashTransientStatus = React.useCallback((text, durationMs = 2400) => {
93
+ if (!text) return;
94
+ const transientStatus = transientStatusRef.current;
95
+ const hadActiveFlash = Boolean(transientStatus.timer);
96
+ const previousStatus = hadActiveFlash
97
+ ? transientStatus.previousStatus
98
+ : controller.getState().ui.statusText;
99
+ clearTransientStatus(false);
100
+ transientStatus.previousStatus = previousStatus;
101
+ controller.setStatus(text);
102
+ transientStatus.timer = setTimeout(() => {
103
+ transientStatus.timer = null;
104
+ if (controller.getState().ui.statusText === text) {
105
+ controller.setStatus(transientStatus.previousStatus || "Connected");
106
+ }
107
+ }, durationMs);
108
+ }, [clearTransientStatus, controller]);
109
+
110
+ const clearQuitArm = React.useCallback((restoreStatus = false) => {
111
+ const quitState = quitStateRef.current;
112
+ if (quitState.timer) {
113
+ clearTimeout(quitState.timer);
114
+ quitState.timer = null;
115
+ }
116
+ const wasArmed = quitState.armedUntil > Date.now();
117
+ quitState.armedUntil = 0;
118
+ if (restoreStatus && wasArmed) {
119
+ const currentStatus = controller.getState().ui.statusText;
120
+ if (currentStatus === "Press q to quit, or continue navigating") {
121
+ controller.setStatus(quitState.previousStatus || "Connected");
122
+ }
123
+ }
124
+ }, [controller]);
125
+
126
+ const armQuit = React.useCallback(() => {
127
+ clearQuitArm(false);
128
+ const quitState = quitStateRef.current;
129
+ quitState.previousStatus = controller.getState().ui.statusText;
130
+ quitState.armedUntil = Date.now() + 1500;
131
+ controller.setStatus("Press q to quit, or continue navigating");
132
+ quitState.timer = setTimeout(() => {
133
+ clearQuitArm(true);
134
+ }, 1500);
135
+ }, [clearQuitArm, controller]);
136
+
137
+ const requestExit = React.useCallback(() => {
138
+ if (exitingRef.current) return;
139
+ exitingRef.current = true;
140
+ clearQuitArm(false);
141
+ controller.setStatus("Quitting...");
142
+
143
+ const slowTimer = setTimeout(() => {
144
+ controller.setStatus(getQuitPendingStatus(controller.getState().connection.mode));
145
+ }, 300);
146
+
147
+ Promise.resolve(onRequestExit?.())
148
+ .catch(() => {})
149
+ .finally(() => {
150
+ clearTimeout(slowTimer);
151
+ });
152
+ }, [clearQuitArm, controller, onRequestExit]);
153
+
154
+ React.useEffect(() => {
155
+ let mounted = true;
156
+ controller.start().catch((error) => {
157
+ if (mounted) {
158
+ const message = error?.message || String(error);
159
+ controller.dispatch({
160
+ type: "connection/error",
161
+ error: message,
162
+ statusText: `Startup failed: ${message}`,
163
+ });
164
+ }
165
+ });
166
+ return () => {
167
+ mounted = false;
168
+ if (exitingRef.current) return;
169
+ clearQuitArm(false);
170
+ clearTransientStatus(false);
171
+ controller.stop().catch(() => {});
172
+ };
173
+ }, [clearQuitArm, clearTransientStatus, controller]);
174
+
175
+ React.useEffect(() => {
176
+ if (typeof platform.setRenderInvalidator !== "function") return undefined;
177
+ platform.setRenderInvalidator(() => {
178
+ forceRender((value) => value + 1);
179
+ });
180
+ return () => {
181
+ platform.setRenderInvalidator(null);
182
+ };
183
+ }, [platform]);
184
+
185
+ React.useEffect(() => {
186
+ if (!stdin || !process.stdout?.isTTY) return undefined;
187
+ const enableMouse = "\u001b[?1000h\u001b[?1002h\u001b[?1006h";
188
+ const disableMouse = "\u001b[?1000l\u001b[?1002l\u001b[?1006l";
189
+
190
+ try {
191
+ process.stdout.write(enableMouse);
192
+ } catch {}
193
+
194
+ const onData = (chunk) => {
195
+ const events = parseMouseInputSequences(chunk?.toString?.("utf8") || "");
196
+ if (events.length === 0) return;
197
+ mouseInputRef.current.ignoreUntil = Date.now() + 120;
198
+ if (controller.getState().ui.modal) {
199
+ platform.clearPointerSelection?.();
200
+ return;
201
+ }
202
+ for (const event of events) {
203
+ if (event.type === "down" && event.button === "left") {
204
+ platform.beginPointerSelection?.(event.x, event.y);
205
+ continue;
206
+ }
207
+ if (event.type === "drag") {
208
+ platform.updatePointerSelection?.(event.x, event.y);
209
+ continue;
210
+ }
211
+ if (event.type === "up") {
212
+ const result = platform.finalizePointerSelection?.() || null;
213
+ const status = formatCopyStatus(result);
214
+ if (status) flashTransientStatus(status);
215
+ }
216
+ }
217
+ };
218
+
219
+ stdin.on("data", onData);
220
+ return () => {
221
+ try {
222
+ stdin.off("data", onData);
223
+ } catch {}
224
+ platform.clearPointerSelection?.();
225
+ try {
226
+ process.stdout.write(disableMouse);
227
+ } catch {}
228
+ };
229
+ }, [controller, flashTransientStatus, platform, stdin]);
230
+
231
+ useInput((input, key) => {
232
+ const focus = controller.getState().ui.focusRegion;
233
+ const modal = controller.getState().ui.modal;
234
+ const inspectorTab = controller.getState().ui.inspectorTab;
235
+ const fullscreenPane = controller.getState().ui.fullscreenPane || null;
236
+ const plainShortcut = isPlainShortcutKey(key);
237
+ const matchesCtrlKey = (name, controlChar) => key.ctrl
238
+ && (key.name === name || input === name || input === controlChar);
239
+ const isCtrlU = matchesCtrlKey("u", "\u0015");
240
+ const isCtrlD = matchesCtrlKey("d", "\u0004");
241
+ const isCtrlE = matchesCtrlKey("e", "\u0005");
242
+ const isCtrlG = matchesCtrlKey("g", "\u0007");
243
+ const isCtrlJ = matchesCtrlKey("j", "\n");
244
+ const isCtrlA = matchesCtrlKey("a", "\u0001");
245
+ const isShiftN = input === "N" || (key.shift && key.name === "n");
246
+ const isShiftD = input === "D" || (key.shift && key.name === "d");
247
+ const isShiftT = !key.ctrl && !key.meta && !key.alt && (input === "T" || (key.shift && key.name === "t"));
248
+ const isShiftA = !key.ctrl && !key.meta && !key.alt && (input === "A" || (key.shift && key.name === "a"));
249
+ const isAltBackspace = key.meta && (key.backspace || key.delete || key.name === "backspace" || key.name === "delete");
250
+ const isAltLeftWord = key.meta && (key.leftArrow || key.name === "left" || input === "b" || input === "B");
251
+ const isAltRightWord = key.meta && (key.rightArrow || key.name === "right" || input === "f" || input === "F");
252
+
253
+ if (
254
+ isMouseInputSequence(input)
255
+ || (
256
+ mouseInputRef.current.ignoreUntil > Date.now()
257
+ && looksLikeMouseInputFragment(input)
258
+ )
259
+ ) {
260
+ return;
261
+ }
262
+
263
+ if (key.ctrl && input === "c") {
264
+ requestExit();
265
+ return;
266
+ }
267
+ if (focus !== "prompt" && input === "q" && quitStateRef.current.armedUntil > Date.now()) {
268
+ clearQuitArm(false);
269
+ requestExit();
270
+ return;
271
+ }
272
+ if (focus !== "prompt" && input === "q") {
273
+ clearQuitArm(false);
274
+ requestExit();
275
+ return;
276
+ }
277
+
278
+ if (key.name !== "escape") {
279
+ clearQuitArm(true);
280
+ }
281
+
282
+ // ── Admin Console keybindings ─────────────────────────
283
+ // The Admin Console replaces the workspace, so its input
284
+ // handling must run before the modal/normal path. The GHCP key
285
+ // editor overlay (`adminGhcpKey.editing`) acts like a modal:
286
+ // route every printable / navigation key into the cursor-aware
287
+ // controller mutators, and only Esc / Enter exit composition.
288
+ const adminState = controller.getState().admin;
289
+ const adminVisible = Boolean(adminState?.visible);
290
+ const adminEditing = Boolean(adminState?.ghcpKey?.editing);
291
+ const adminSaving = Boolean(adminState?.ghcpKey?.saving);
292
+ if (adminEditing) {
293
+ // Block all input while saving so we don't enqueue mutations
294
+ // against a stale draft. Esc still cancels in case the
295
+ // network call hangs.
296
+ if (key.escape) {
297
+ if (adminSaving) return;
298
+ controller.cancelAdminEditGhcpKey();
299
+ return;
300
+ }
301
+ if (adminSaving) return;
302
+ if (key.return) {
303
+ controller.handleCommand(UI_COMMANDS.ADMIN_SAVE_GHCP_KEY).catch(() => {});
304
+ return;
305
+ }
306
+ if (key.leftArrow) { controller.moveAdminGhcpKeyCursor(-1); return; }
307
+ if (key.rightArrow) { controller.moveAdminGhcpKeyCursor(1); return; }
308
+ if (key.home) { controller.moveAdminGhcpKeyCursorToBoundary("start"); return; }
309
+ if (key.end) { controller.moveAdminGhcpKeyCursorToBoundary("end"); return; }
310
+ if (key.backspace || key.delete) { controller.deleteAdminGhcpKeyChar(); return; }
311
+ if (!key.ctrl && !key.meta && input) {
312
+ controller.insertAdminGhcpKeyText(input);
313
+ }
314
+ return;
315
+ }
316
+ if (adminVisible) {
317
+ if (key.escape) {
318
+ controller.handleCommand(UI_COMMANDS.CLOSE_ADMIN_CONSOLE).catch(() => {});
319
+ return;
320
+ }
321
+ if (plainShortcut && input === "e") {
322
+ controller.handleCommand(UI_COMMANDS.ADMIN_BEGIN_EDIT_GHCP_KEY).catch(() => {});
323
+ return;
324
+ }
325
+ if (plainShortcut && input === "c" && adminState?.profile?.githubCopilotKeySet) {
326
+ controller.handleCommand(UI_COMMANDS.ADMIN_CLEAR_GHCP_KEY).catch(() => {});
327
+ return;
328
+ }
329
+ if (plainShortcut && input === "r") {
330
+ controller.handleCommand(UI_COMMANDS.ADMIN_REFRESH_PROFILE).catch(() => {});
331
+ return;
332
+ }
333
+ // Everything else is swallowed — the console is a modal-like
334
+ // surface and we don't want session/chat shortcuts firing
335
+ // behind it.
336
+ return;
337
+ }
338
+
339
+ if (modal) {
340
+ if (isShiftT && modal.type === "themePicker") {
341
+ controller.handleCommand(UI_COMMANDS.CLOSE_MODAL).catch(() => {});
342
+ return;
343
+ }
344
+ if (modal.type === "confirm") {
345
+ if (key.escape || input === "n" || input === "q") {
346
+ controller.handleCommand(UI_COMMANDS.CLOSE_MODAL).catch(() => {});
347
+ return;
348
+ }
349
+ if (key.return || input === "y") {
350
+ controller.handleCommand(UI_COMMANDS.MODAL_CONFIRM).catch(() => {});
351
+ return;
352
+ }
353
+ return;
354
+ }
355
+ if (modal.type === "renameSession" || modal.type === "artifactUpload" || modal.type === "sessionGroupName") {
356
+ if (key.escape) {
357
+ controller.handleCommand(UI_COMMANDS.CLOSE_MODAL).catch(() => {});
358
+ return;
359
+ }
360
+ if (key.return) {
361
+ controller.handleCommand(UI_COMMANDS.MODAL_CONFIRM).catch(() => {});
362
+ return;
363
+ }
364
+ if (key.leftArrow) {
365
+ if (modal.type === "renameSession") controller.moveRenameSessionCursor(-1);
366
+ else if (modal.type === "sessionGroupName") controller.moveSessionGroupNameCursor(-1);
367
+ else controller.moveArtifactUploadCursor(-1);
368
+ return;
369
+ }
370
+ if (key.rightArrow) {
371
+ if (modal.type === "renameSession") controller.moveRenameSessionCursor(1);
372
+ else if (modal.type === "sessionGroupName") controller.moveSessionGroupNameCursor(1);
373
+ else controller.moveArtifactUploadCursor(1);
374
+ return;
375
+ }
376
+ if (key.home) {
377
+ if (modal.type === "renameSession") controller.moveRenameSessionCursorToBoundary("start");
378
+ else if (modal.type === "sessionGroupName") controller.moveSessionGroupNameCursorToBoundary("start");
379
+ else controller.moveArtifactUploadCursorToBoundary("start");
380
+ return;
381
+ }
382
+ if (key.end) {
383
+ if (modal.type === "renameSession") controller.moveRenameSessionCursorToBoundary("end");
384
+ else if (modal.type === "sessionGroupName") controller.moveSessionGroupNameCursorToBoundary("end");
385
+ else controller.moveArtifactUploadCursorToBoundary("end");
386
+ return;
387
+ }
388
+ if (key.backspace || key.delete) {
389
+ if (modal.type === "renameSession") controller.deleteRenameSessionChar();
390
+ else if (modal.type === "sessionGroupName") controller.deleteSessionGroupNameChar();
391
+ else controller.deleteArtifactUploadChar();
392
+ return;
393
+ }
394
+ if (!key.ctrl && !key.meta && input) {
395
+ if (modal.type === "renameSession") controller.insertRenameSessionText(input);
396
+ else if (modal.type === "sessionGroupName") controller.insertSessionGroupNameText(input);
397
+ else controller.insertArtifactUploadText(input);
398
+ }
399
+ return;
400
+ }
401
+ if (key.escape || input === "q" || (modal.type === "artifactPicker" && input === "a")) {
402
+ controller.handleCommand(UI_COMMANDS.CLOSE_MODAL).catch(() => {});
403
+ return;
404
+ }
405
+ if (key.tab && key.shift) {
406
+ controller.handleCommand(UI_COMMANDS.MODAL_PANE_PREV).catch(() => {});
407
+ return;
408
+ }
409
+ if (key.tab) {
410
+ controller.handleCommand(UI_COMMANDS.MODAL_PANE_NEXT).catch(() => {});
411
+ return;
412
+ }
413
+ if (modal.type === "sessionOwnerFilter") {
414
+ if (input === " ") {
415
+ controller.handleCommand(UI_COMMANDS.MODAL_CONFIRM).catch(() => {});
416
+ return;
417
+ }
418
+ if (key.return) {
419
+ return;
420
+ }
421
+ }
422
+ if (key.return) {
423
+ controller.handleCommand(UI_COMMANDS.MODAL_CONFIRM).catch(() => {});
424
+ return;
425
+ }
426
+ if (key.upArrow || input === "k") {
427
+ controller.handleCommand(UI_COMMANDS.MODAL_PREV).catch(() => {});
428
+ return;
429
+ }
430
+ if (key.downArrow || input === "j") {
431
+ controller.handleCommand(UI_COMMANDS.MODAL_NEXT).catch(() => {});
432
+ return;
433
+ }
434
+ return;
435
+ }
436
+
437
+ if (focus !== "prompt" && isShiftT) {
438
+ controller.handleCommand(UI_COMMANDS.OPEN_THEME_PICKER).catch(() => {});
439
+ return;
440
+ }
441
+
442
+ if (focus !== "prompt" && isShiftA) {
443
+ // Shift+A toggles the per-user Admin Console (settings +
444
+ // GitHub Copilot key). Plain `a` is reserved for the
445
+ // artifact picker, so the open shortcut needs the shift
446
+ // modifier.
447
+ controller.handleCommand(UI_COMMANDS.OPEN_ADMIN_CONSOLE).catch(() => {});
448
+ return;
449
+ }
450
+
451
+ if (key.tab && key.shift) {
452
+ controller.handleCommand(UI_COMMANDS.FOCUS_PREV).catch(() => {});
453
+ return;
454
+ }
455
+ if (key.tab) {
456
+ if (focus === "prompt" && controller.acceptPromptReferenceAutocomplete()) {
457
+ return;
458
+ }
459
+ controller.handleCommand(UI_COMMANDS.FOCUS_NEXT).catch(() => {});
460
+ return;
461
+ }
462
+ if (key.escape && focus === "inspector" && inspectorTab === "files" && controller.getState().files.fullscreen) {
463
+ controller.handleCommand(UI_COMMANDS.TOGGLE_FILE_PREVIEW_FULLSCREEN).catch(() => {});
464
+ return;
465
+ }
466
+ if (key.escape && fullscreenPane) {
467
+ if (focus === "prompt") {
468
+ const promptEdit = controller.getState().ui.promptEdit;
469
+ if (promptEdit?.phase === "queued") {
470
+ controller.exitPendingPromptEdit({ restoreDraft: true });
471
+ return;
472
+ }
473
+ if (promptEdit) {
474
+ if (controller.cancelSelectedOutboxPrompt) {
475
+ controller.cancelSelectedOutboxPrompt().catch(() => {});
476
+ } else {
477
+ controller.cancelSelectedPendingPrompt();
478
+ }
479
+ return;
480
+ }
481
+ if (!controller.getState().ui.prompt) {
482
+ if (controller.cancelLatestQueuedOutbox) {
483
+ controller.cancelLatestQueuedOutbox().catch(() => {});
484
+ }
485
+ return;
486
+ }
487
+ controller.setPrompt("");
488
+ controller.setFocus(fullscreenPane);
489
+ return;
490
+ }
491
+ controller.handleCommand(UI_COMMANDS.TOGGLE_PANE_FULLSCREEN).catch(() => {});
492
+ return;
493
+ }
494
+ if (key.escape && focus === "sessions" && controller.getState().sessions.selectMode) {
495
+ controller.handleCommand(UI_COMMANDS.CLEAR_SESSION_SELECTION).catch(() => {});
496
+ return;
497
+ }
498
+ if (key.escape) {
499
+ if (focus === "prompt") {
500
+ const promptEdit = controller.getState().ui.promptEdit;
501
+ if (promptEdit?.phase === "queued") {
502
+ controller.exitPendingPromptEdit({ restoreDraft: true });
503
+ return;
504
+ }
505
+ if (promptEdit) {
506
+ if (controller.cancelSelectedOutboxPrompt) {
507
+ controller.cancelSelectedOutboxPrompt().catch(() => {});
508
+ } else {
509
+ controller.cancelSelectedPendingPrompt();
510
+ }
511
+ return;
512
+ }
513
+ if (!controller.getState().ui.prompt) {
514
+ if (controller.cancelLatestQueuedOutbox) {
515
+ controller.cancelLatestQueuedOutbox().catch(() => {});
516
+ }
517
+ return;
518
+ }
519
+ controller.setPrompt("");
520
+ controller.handleCommand(UI_COMMANDS.FOCUS_SESSIONS).catch(() => {});
521
+ return;
522
+ }
523
+ controller.handleCommand(UI_COMMANDS.FOCUS_SESSIONS).catch(() => {});
524
+ armQuit();
525
+ return;
526
+ }
527
+
528
+ if (focus !== "prompt" && isShiftN) {
529
+ controller.handleCommand(UI_COMMANDS.OPEN_MODEL_PICKER).catch(() => {});
530
+ return;
531
+ }
532
+
533
+ if (focus === "chat" && (input === "e" || isCtrlE)) {
534
+ controller.handleCommand(UI_COMMANDS.EXPAND_HISTORY).catch(() => {});
535
+ return;
536
+ }
537
+
538
+ if (focus === "sessions" && input === "f") {
539
+ controller.handleCommand(UI_COMMANDS.OPEN_SESSION_FILTER).catch(() => {});
540
+ return;
541
+ }
542
+
543
+ if (focus === "inspector" && inspectorTab === "logs" && input === "t") {
544
+ controller.handleCommand(UI_COMMANDS.TOGGLE_LOG_TAIL).catch(() => {});
545
+ return;
546
+ }
547
+ if (focus === "inspector" && inspectorTab === "logs" && input === "f") {
548
+ controller.handleCommand(UI_COMMANDS.OPEN_LOG_FILTER).catch(() => {});
549
+ return;
550
+ }
551
+ if (focus === "inspector" && inspectorTab === "stats" && input === "f") {
552
+ controller.handleCommand(UI_COMMANDS.TOGGLE_STATS_VIEW).catch(() => {});
553
+ return;
554
+ }
555
+ if (focus === "inspector" && inspectorTab === "files" && input === "f") {
556
+ controller.handleCommand(UI_COMMANDS.OPEN_FILES_FILTER).catch(() => {});
557
+ return;
558
+ }
559
+ if (focus === "inspector" && inspectorTab === "files" && input === "u") {
560
+ controller.handleCommand(UI_COMMANDS.OPEN_ARTIFACT_UPLOAD).catch(() => {});
561
+ return;
562
+ }
563
+ if (focus === "inspector" && inspectorTab === "files" && input === "a") {
564
+ controller.handleCommand(UI_COMMANDS.DOWNLOAD_SELECTED_FILE).catch(() => {});
565
+ return;
566
+ }
567
+ if (focus === "inspector" && inspectorTab === "files" && input === "x") {
568
+ controller.handleCommand(UI_COMMANDS.DELETE_SELECTED_FILE).catch(() => {});
569
+ return;
570
+ }
571
+ if (focus === "inspector" && inspectorTab === "history" && input === "f") {
572
+ controller.handleCommand(UI_COMMANDS.OPEN_HISTORY_FORMAT).catch(() => {});
573
+ return;
574
+ }
575
+ if (focus === "inspector" && inspectorTab === "history" && input === "r") {
576
+ controller.handleCommand(UI_COMMANDS.REFRESH_EXECUTION_HISTORY).catch(() => {});
577
+ return;
578
+ }
579
+ if (focus === "inspector" && inspectorTab === "history" && input === "a") {
580
+ controller.handleCommand(UI_COMMANDS.EXPORT_EXECUTION_HISTORY).catch(() => {});
581
+ return;
582
+ }
583
+ if (focus === "inspector" && inspectorTab === "files" && input === "v") {
584
+ controller.handleCommand(UI_COMMANDS.TOGGLE_FILE_PREVIEW_FULLSCREEN).catch(() => {});
585
+ return;
586
+ }
587
+ if (focus !== "prompt" && input === "v") {
588
+ controller.handleCommand(UI_COMMANDS.TOGGLE_PANE_FULLSCREEN).catch(() => {});
589
+ return;
590
+ }
591
+ if (focus === "inspector" && inspectorTab === "files" && plainShortcut && input === "o") {
592
+ controller.handleCommand(UI_COMMANDS.OPEN_SELECTED_FILE).catch(() => {});
593
+ return;
594
+ }
595
+ if (focus === "inspector" && inspectorTab === "files" && !controller.getState().files.fullscreen) {
596
+ if (key.upArrow || input === "k") {
597
+ controller.handleCommand(UI_COMMANDS.MOVE_FILE_UP).catch(() => {});
598
+ return;
599
+ }
600
+ if (key.downArrow || input === "j") {
601
+ controller.handleCommand(UI_COMMANDS.MOVE_FILE_DOWN).catch(() => {});
602
+ return;
603
+ }
604
+ }
605
+
606
+ if (focus !== "prompt" && input === "[") {
607
+ controller.handleCommand(UI_COMMANDS.GROW_LEFT_PANE).catch(() => {});
608
+ return;
609
+ }
610
+ if (focus !== "prompt" && input === "]") {
611
+ controller.handleCommand(UI_COMMANDS.GROW_RIGHT_PANE).catch(() => {});
612
+ return;
613
+ }
614
+ if (focus !== "prompt" && input === "{") {
615
+ controller.handleCommand(UI_COMMANDS.SHRINK_SESSION_PANE).catch(() => {});
616
+ return;
617
+ }
618
+ if (focus !== "prompt" && input === "}") {
619
+ controller.handleCommand(UI_COMMANDS.GROW_SESSION_PANE).catch(() => {});
620
+ return;
621
+ }
622
+
623
+ if (focus === "prompt") {
624
+ const promptEdit = controller.getState().ui.promptEdit;
625
+ const selectedQueuedOutbox = promptEdit?.phase === "queued";
626
+ const selectedReadOnlyOutbox = selectedQueuedOutbox || promptEdit?.phase === "cancelling";
627
+ if (isCtrlA) {
628
+ controller.handleCommand(UI_COMMANDS.OPEN_ARTIFACT_UPLOAD).catch(() => {});
629
+ return;
630
+ }
631
+ if (selectedReadOnlyOutbox) {
632
+ if (key.upArrow) {
633
+ controller.movePromptCursorVertical(-1);
634
+ return;
635
+ }
636
+ if (key.downArrow) {
637
+ controller.movePromptCursorVertical(1);
638
+ return;
639
+ }
640
+ if (selectedQueuedOutbox && input === "d") {
641
+ if (controller.cancelSelectedOutboxPrompt) {
642
+ controller.cancelSelectedOutboxPrompt().catch(() => {});
643
+ }
644
+ return;
645
+ }
646
+ if (key.backspace || key.delete) {
647
+ return;
648
+ }
649
+ if (key.return) {
650
+ controller.exitPendingPromptEdit({ restoreDraft: true });
651
+ return;
652
+ }
653
+ return;
654
+ }
655
+ if (key.return && key.meta) {
656
+ controller.insertPromptText("\n");
657
+ return;
658
+ }
659
+ if (key.return) {
660
+ controller.handleCommand(UI_COMMANDS.SEND_PROMPT).catch(() => {});
661
+ return;
662
+ }
663
+ if (isAltLeftWord) {
664
+ controller.movePromptCursorWord(-1);
665
+ return;
666
+ }
667
+ if (isAltRightWord) {
668
+ controller.movePromptCursorWord(1);
669
+ return;
670
+ }
671
+ if (key.leftArrow) {
672
+ controller.movePromptCursor(-1);
673
+ return;
674
+ }
675
+ if (key.rightArrow) {
676
+ controller.movePromptCursor(1);
677
+ return;
678
+ }
679
+ if (key.upArrow) {
680
+ controller.movePromptCursorVertical(-1);
681
+ return;
682
+ }
683
+ if (key.downArrow) {
684
+ controller.movePromptCursorVertical(1);
685
+ return;
686
+ }
687
+ if (isAltBackspace) {
688
+ controller.deletePromptWordBackward();
689
+ return;
690
+ }
691
+ if (key.backspace || key.delete) {
692
+ controller.deletePromptChar();
693
+ return;
694
+ }
695
+ if (isCtrlJ) {
696
+ controller.insertPromptText("\n");
697
+ return;
698
+ }
699
+ if (!key.ctrl && !key.meta && input) {
700
+ controller.insertPromptText(input);
701
+ }
702
+ return;
703
+ }
704
+
705
+ if (plainShortcut && input === "p") {
706
+ controller.handleCommand(UI_COMMANDS.FOCUS_PROMPT).catch(() => {});
707
+ return;
708
+ }
709
+ if (plainShortcut && input === "n") {
710
+ controller.handleCommand(UI_COMMANDS.NEW_SESSION).catch(() => {});
711
+ return;
712
+ }
713
+ if (plainShortcut && input === "r") {
714
+ controller.handleCommand(UI_COMMANDS.REFRESH).catch(() => {});
715
+ return;
716
+ }
717
+ if (plainShortcut && input === "a") {
718
+ controller.handleCommand(UI_COMMANDS.OPEN_ARTIFACT_PICKER).catch(() => {});
719
+ return;
720
+ }
721
+ if (plainShortcut && input === "m") {
722
+ controller.handleCommand(UI_COMMANDS.CYCLE_INSPECTOR_TAB).catch(() => {});
723
+ return;
724
+ }
725
+ if (plainShortcut && input === "c") {
726
+ controller.handleCommand(UI_COMMANDS.CANCEL_SESSION).catch(() => {});
727
+ return;
728
+ }
729
+ if (plainShortcut && input === "d") {
730
+ controller.handleCommand(UI_COMMANDS.DONE_SESSION).catch(() => {});
731
+ return;
732
+ }
733
+ if (plainShortcut && isShiftD) {
734
+ controller.handleCommand(UI_COMMANDS.DELETE_SESSION).catch(() => {});
735
+ return;
736
+ }
737
+ if (focus !== "prompt" && (input === "h" || key.leftArrow)) {
738
+ if (focus === "inspector" && key.leftArrow) {
739
+ controller.handleCommand(UI_COMMANDS.PREV_INSPECTOR_TAB).catch(() => {});
740
+ return;
741
+ }
742
+ controller.handleCommand(UI_COMMANDS.FOCUS_LEFT).catch(() => {});
743
+ return;
744
+ }
745
+ if (focus !== "prompt" && (input === "l" || key.rightArrow)) {
746
+ if (focus === "inspector" && key.rightArrow) {
747
+ controller.handleCommand(UI_COMMANDS.NEXT_INSPECTOR_TAB).catch(() => {});
748
+ return;
749
+ }
750
+ controller.handleCommand(UI_COMMANDS.FOCUS_RIGHT).catch(() => {});
751
+ return;
752
+ }
753
+
754
+ if (focus === "sessions") {
755
+ if (isCtrlG) {
756
+ controller.handleCommand(UI_COMMANDS.OPEN_MOVE_TO_GROUP).catch(() => {});
757
+ return;
758
+ }
759
+ if (key.upArrow || input === "k") {
760
+ controller.handleCommand(UI_COMMANDS.MOVE_SESSION_UP).catch(() => {});
761
+ return;
762
+ }
763
+ if (key.downArrow || input === "j") {
764
+ controller.handleCommand(UI_COMMANDS.MOVE_SESSION_DOWN).catch(() => {});
765
+ return;
766
+ }
767
+ if (isCtrlU || key.pageUp) {
768
+ controller.handleCommand(UI_COMMANDS.PAGE_UP).catch(() => {});
769
+ return;
770
+ }
771
+ if (isCtrlD || key.pageDown) {
772
+ controller.handleCommand(UI_COMMANDS.PAGE_DOWN).catch(() => {});
773
+ return;
774
+ }
775
+ if (input === "+" || input === "=") {
776
+ controller.handleCommand(UI_COMMANDS.EXPAND_SESSION).catch(() => {});
777
+ return;
778
+ }
779
+ if (input === "-") {
780
+ controller.handleCommand(UI_COMMANDS.COLLAPSE_SESSION).catch(() => {});
781
+ return;
782
+ }
783
+ if (plainShortcut && input === "t") {
784
+ controller.handleCommand(UI_COMMANDS.OPEN_RENAME_SESSION).catch(() => {});
785
+ return;
786
+ }
787
+ if (plainShortcut && input === "P") {
788
+ controller.handleCommand(UI_COMMANDS.PIN_SESSION).catch(() => {});
789
+ return;
790
+ }
791
+ if (plainShortcut && input === "V") {
792
+ controller.handleCommand(UI_COMMANDS.TOGGLE_SELECT_MODE).catch(() => {});
793
+ return;
794
+ }
795
+ if (input === " " && controller.getState().sessions.selectMode) {
796
+ controller.handleCommand(UI_COMMANDS.TOGGLE_SESSION_SELECTION).catch(() => {});
797
+ return;
798
+ }
799
+ }
800
+
801
+ if (focus === "chat" || focus === "inspector" || focus === "activity") {
802
+ if (focus === "chat" && plainShortcut && input === "s") {
803
+ controller.handleCommand(UI_COMMANDS.TOGGLE_CHAT_VIEW).catch(() => {});
804
+ return;
805
+ }
806
+ if (key.upArrow || input === "k") {
807
+ controller.handleCommand(UI_COMMANDS.SCROLL_UP).catch(() => {});
808
+ return;
809
+ }
810
+ if (key.downArrow || input === "j") {
811
+ controller.handleCommand(UI_COMMANDS.SCROLL_DOWN).catch(() => {});
812
+ return;
813
+ }
814
+ if (isCtrlU || key.pageUp) {
815
+ controller.handleCommand(UI_COMMANDS.PAGE_UP).catch(() => {});
816
+ return;
817
+ }
818
+ if (isCtrlD || key.pageDown) {
819
+ controller.handleCommand(UI_COMMANDS.PAGE_DOWN).catch(() => {});
820
+ return;
821
+ }
822
+ if (input === "g") {
823
+ controller.handleCommand(UI_COMMANDS.SCROLL_TOP).catch(() => {});
824
+ return;
825
+ }
826
+ if (input === "G" || (key.shift && input === "g")) {
827
+ controller.handleCommand(UI_COMMANDS.SCROLL_BOTTOM).catch(() => {});
828
+ return;
829
+ }
830
+ }
831
+
832
+ if (focus === "inspector") {
833
+ if (key.leftArrow) {
834
+ controller.handleCommand(UI_COMMANDS.PREV_INSPECTOR_TAB).catch(() => {});
835
+ return;
836
+ }
837
+ if (key.rightArrow) {
838
+ controller.handleCommand(UI_COMMANDS.NEXT_INSPECTOR_TAB).catch(() => {});
839
+ return;
840
+ }
841
+ }
842
+
843
+ });
844
+
845
+ return React.createElement(UiPlatformProvider, { platform },
846
+ React.createElement(SharedPilotSwarmApp, {
847
+ controller,
848
+ versionLabel: PILOTSWARM_CLI_VERSION_LABEL,
849
+ }));
850
+ }