nastech-tui 0.0.1

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 (424) hide show
  1. package/.prettierrc +11 -0
  2. package/README.md +346 -0
  3. package/eslint.config.mjs +111 -0
  4. package/package.json +51 -0
  5. package/packages/nastech-ink/ambient.d.ts +83 -0
  6. package/packages/nastech-ink/index.d.ts +40 -0
  7. package/packages/nastech-ink/index.js +1 -0
  8. package/packages/nastech-ink/nastech-ink/ambient.d.ts +83 -0
  9. package/packages/nastech-ink/nastech-ink/index.d.ts +40 -0
  10. package/packages/nastech-ink/nastech-ink/index.js +1 -0
  11. package/packages/nastech-ink/nastech-ink/package.json +54 -0
  12. package/packages/nastech-ink/nastech-ink/src/bootstrap/state.ts +9 -0
  13. package/packages/nastech-ink/nastech-ink/src/entry-exports.ts +32 -0
  14. package/packages/nastech-ink/nastech-ink/src/hooks/use-stderr.ts +15 -0
  15. package/packages/nastech-ink/nastech-ink/src/hooks/use-stdout.ts +15 -0
  16. package/packages/nastech-ink/nastech-ink/src/ink/Ansi.tsx +435 -0
  17. package/packages/nastech-ink/nastech-ink/src/ink/app-mouse.test.ts +123 -0
  18. package/packages/nastech-ink/nastech-ink/src/ink/bidi.ts +145 -0
  19. package/packages/nastech-ink/nastech-ink/src/ink/cache-eviction.ts +45 -0
  20. package/packages/nastech-ink/nastech-ink/src/ink/clearTerminal.ts +68 -0
  21. package/packages/nastech-ink/nastech-ink/src/ink/colorize.test.ts +60 -0
  22. package/packages/nastech-ink/nastech-ink/src/ink/colorize.ts +277 -0
  23. package/packages/nastech-ink/nastech-ink/src/ink/components/AlternateScreen.tsx +133 -0
  24. package/packages/nastech-ink/nastech-ink/src/ink/components/App.tsx +830 -0
  25. package/packages/nastech-ink/nastech-ink/src/ink/components/AppContext.ts +20 -0
  26. package/packages/nastech-ink/nastech-ink/src/ink/components/Box.tsx +294 -0
  27. package/packages/nastech-ink/nastech-ink/src/ink/components/Button.tsx +236 -0
  28. package/packages/nastech-ink/nastech-ink/src/ink/components/ClockContext.tsx +133 -0
  29. package/packages/nastech-ink/nastech-ink/src/ink/components/CursorAdvanceContext.ts +35 -0
  30. package/packages/nastech-ink/nastech-ink/src/ink/components/CursorDeclarationContext.ts +28 -0
  31. package/packages/nastech-ink/nastech-ink/src/ink/components/ErrorOverview.tsx +130 -0
  32. package/packages/nastech-ink/nastech-ink/src/ink/components/Link.tsx +38 -0
  33. package/packages/nastech-ink/nastech-ink/src/ink/components/Newline.tsx +43 -0
  34. package/packages/nastech-ink/nastech-ink/src/ink/components/NoSelect.tsx +73 -0
  35. package/packages/nastech-ink/nastech-ink/src/ink/components/RawAnsi.tsx +61 -0
  36. package/packages/nastech-ink/nastech-ink/src/ink/components/ScrollBox.tsx +290 -0
  37. package/packages/nastech-ink/nastech-ink/src/ink/components/Spacer.tsx +23 -0
  38. package/packages/nastech-ink/nastech-ink/src/ink/components/StdinContext.ts +25 -0
  39. package/packages/nastech-ink/nastech-ink/src/ink/components/TerminalFocusContext.tsx +63 -0
  40. package/packages/nastech-ink/nastech-ink/src/ink/components/TerminalSizeContext.tsx +7 -0
  41. package/packages/nastech-ink/nastech-ink/src/ink/components/Text.test.ts +38 -0
  42. package/packages/nastech-ink/nastech-ink/src/ink/components/Text.tsx +336 -0
  43. package/packages/nastech-ink/nastech-ink/src/ink/constants.ts +6 -0
  44. package/packages/nastech-ink/nastech-ink/src/ink/cursor.ts +5 -0
  45. package/packages/nastech-ink/nastech-ink/src/ink/devtools.ts +2 -0
  46. package/packages/nastech-ink/nastech-ink/src/ink/dom.ts +495 -0
  47. package/packages/nastech-ink/nastech-ink/src/ink/events/click-event.ts +38 -0
  48. package/packages/nastech-ink/nastech-ink/src/ink/events/cmd-shortcuts.test.ts +65 -0
  49. package/packages/nastech-ink/nastech-ink/src/ink/events/dispatcher.ts +242 -0
  50. package/packages/nastech-ink/nastech-ink/src/ink/events/emitter.ts +40 -0
  51. package/packages/nastech-ink/nastech-ink/src/ink/events/event-handlers.ts +84 -0
  52. package/packages/nastech-ink/nastech-ink/src/ink/events/event.ts +11 -0
  53. package/packages/nastech-ink/nastech-ink/src/ink/events/focus-event.ts +18 -0
  54. package/packages/nastech-ink/nastech-ink/src/ink/events/input-event.ts +176 -0
  55. package/packages/nastech-ink/nastech-ink/src/ink/events/keyboard-event.ts +57 -0
  56. package/packages/nastech-ink/nastech-ink/src/ink/events/mouse-event.ts +18 -0
  57. package/packages/nastech-ink/nastech-ink/src/ink/events/paste-event.ts +10 -0
  58. package/packages/nastech-ink/nastech-ink/src/ink/events/resize-event.ts +12 -0
  59. package/packages/nastech-ink/nastech-ink/src/ink/events/terminal-event.ts +107 -0
  60. package/packages/nastech-ink/nastech-ink/src/ink/events/terminal-focus-event.ts +19 -0
  61. package/packages/nastech-ink/nastech-ink/src/ink/focus.ts +219 -0
  62. package/packages/nastech-ink/nastech-ink/src/ink/frame.ts +124 -0
  63. package/packages/nastech-ink/nastech-ink/src/ink/get-max-width.ts +27 -0
  64. package/packages/nastech-ink/nastech-ink/src/ink/global.d.ts +1 -0
  65. package/packages/nastech-ink/nastech-ink/src/ink/hit-test.test.ts +38 -0
  66. package/packages/nastech-ink/nastech-ink/src/ink/hit-test.ts +224 -0
  67. package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-animation-frame.ts +62 -0
  68. package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-app.ts +9 -0
  69. package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-cursor-advance.ts +33 -0
  70. package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-declared-cursor.ts +75 -0
  71. package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-external-process.ts +27 -0
  72. package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-input.ts +95 -0
  73. package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-interval.ts +71 -0
  74. package/packages/nastech-ink/package.json +57 -0
  75. package/packages/nastech-ink/src/bootstrap/state.ts +9 -0
  76. package/packages/nastech-ink/src/entry-exports.ts +32 -0
  77. package/packages/nastech-ink/src/hooks/use-stderr.ts +15 -0
  78. package/packages/nastech-ink/src/hooks/use-stdout.ts +15 -0
  79. package/packages/nastech-ink/src/ink/Ansi.tsx +435 -0
  80. package/packages/nastech-ink/src/ink/app-mouse.test.ts +123 -0
  81. package/packages/nastech-ink/src/ink/app-rawmode-mouse.test.ts +91 -0
  82. package/packages/nastech-ink/src/ink/bidi.ts +145 -0
  83. package/packages/nastech-ink/src/ink/cache-eviction.ts +45 -0
  84. package/packages/nastech-ink/src/ink/clearTerminal.ts +68 -0
  85. package/packages/nastech-ink/src/ink/colorize.test.ts +60 -0
  86. package/packages/nastech-ink/src/ink/colorize.ts +277 -0
  87. package/packages/nastech-ink/src/ink/components/AlternateScreen.tsx +133 -0
  88. package/packages/nastech-ink/src/ink/components/App.tsx +855 -0
  89. package/packages/nastech-ink/src/ink/components/AppContext.ts +20 -0
  90. package/packages/nastech-ink/src/ink/components/Box.tsx +294 -0
  91. package/packages/nastech-ink/src/ink/components/Button.tsx +236 -0
  92. package/packages/nastech-ink/src/ink/components/ClockContext.tsx +133 -0
  93. package/packages/nastech-ink/src/ink/components/CursorAdvanceContext.ts +35 -0
  94. package/packages/nastech-ink/src/ink/components/CursorDeclarationContext.ts +28 -0
  95. package/packages/nastech-ink/src/ink/components/ErrorOverview.tsx +130 -0
  96. package/packages/nastech-ink/src/ink/components/Link.tsx +38 -0
  97. package/packages/nastech-ink/src/ink/components/Newline.tsx +43 -0
  98. package/packages/nastech-ink/src/ink/components/NoSelect.tsx +73 -0
  99. package/packages/nastech-ink/src/ink/components/RawAnsi.tsx +61 -0
  100. package/packages/nastech-ink/src/ink/components/ScrollBox.tsx +290 -0
  101. package/packages/nastech-ink/src/ink/components/Spacer.tsx +23 -0
  102. package/packages/nastech-ink/src/ink/components/StdinContext.ts +25 -0
  103. package/packages/nastech-ink/src/ink/components/TerminalFocusContext.tsx +63 -0
  104. package/packages/nastech-ink/src/ink/components/TerminalSizeContext.tsx +7 -0
  105. package/packages/nastech-ink/src/ink/components/Text.test.ts +38 -0
  106. package/packages/nastech-ink/src/ink/components/Text.tsx +336 -0
  107. package/packages/nastech-ink/src/ink/constants.ts +6 -0
  108. package/packages/nastech-ink/src/ink/cursor.ts +5 -0
  109. package/packages/nastech-ink/src/ink/devtools.ts +2 -0
  110. package/packages/nastech-ink/src/ink/dom.ts +495 -0
  111. package/packages/nastech-ink/src/ink/events/click-event.ts +38 -0
  112. package/packages/nastech-ink/src/ink/events/cmd-shortcuts.test.ts +65 -0
  113. package/packages/nastech-ink/src/ink/events/dispatcher.ts +242 -0
  114. package/packages/nastech-ink/src/ink/events/emitter.ts +40 -0
  115. package/packages/nastech-ink/src/ink/events/event-handlers.ts +84 -0
  116. package/packages/nastech-ink/src/ink/events/event.ts +11 -0
  117. package/packages/nastech-ink/src/ink/events/focus-event.ts +18 -0
  118. package/packages/nastech-ink/src/ink/events/input-event.ts +176 -0
  119. package/packages/nastech-ink/src/ink/events/keyboard-event.ts +57 -0
  120. package/packages/nastech-ink/src/ink/events/mouse-event.ts +18 -0
  121. package/packages/nastech-ink/src/ink/events/paste-event.ts +10 -0
  122. package/packages/nastech-ink/src/ink/events/resize-event.ts +12 -0
  123. package/packages/nastech-ink/src/ink/events/terminal-event.ts +107 -0
  124. package/packages/nastech-ink/src/ink/events/terminal-focus-event.ts +19 -0
  125. package/packages/nastech-ink/src/ink/focus.ts +219 -0
  126. package/packages/nastech-ink/src/ink/frame.ts +124 -0
  127. package/packages/nastech-ink/src/ink/get-max-width.ts +27 -0
  128. package/packages/nastech-ink/src/ink/global.d.ts +1 -0
  129. package/packages/nastech-ink/src/ink/hit-test.test.ts +38 -0
  130. package/packages/nastech-ink/src/ink/hit-test.ts +224 -0
  131. package/packages/nastech-ink/src/ink/hooks/use-animation-frame.ts +62 -0
  132. package/packages/nastech-ink/src/ink/hooks/use-app.ts +9 -0
  133. package/packages/nastech-ink/src/ink/hooks/use-cursor-advance.ts +33 -0
  134. package/packages/nastech-ink/src/ink/hooks/use-declared-cursor.ts +75 -0
  135. package/packages/nastech-ink/src/ink/hooks/use-external-process.ts +27 -0
  136. package/packages/nastech-ink/src/ink/hooks/use-input.ts +95 -0
  137. package/packages/nastech-ink/src/ink/hooks/use-interval.ts +71 -0
  138. package/packages/nastech-ink/src/ink/hooks/use-search-highlight.ts +56 -0
  139. package/packages/nastech-ink/src/ink/hooks/use-selection.ts +101 -0
  140. package/packages/nastech-ink/src/ink/hooks/use-stdin.ts +9 -0
  141. package/packages/nastech-ink/src/ink/hooks/use-tab-status.ts +71 -0
  142. package/packages/nastech-ink/src/ink/hooks/use-terminal-focus.ts +18 -0
  143. package/packages/nastech-ink/src/ink/hooks/use-terminal-title.ts +34 -0
  144. package/packages/nastech-ink/src/ink/hooks/use-terminal-viewport.ts +100 -0
  145. package/packages/nastech-ink/src/ink/hyperlinkHover.ts +52 -0
  146. package/packages/nastech-ink/src/ink/ink-cursor-advance.test.ts +234 -0
  147. package/packages/nastech-ink/src/ink/ink-resize.test.ts +50 -0
  148. package/packages/nastech-ink/src/ink/ink.tsx +2705 -0
  149. package/packages/nastech-ink/src/ink/instances.ts +10 -0
  150. package/packages/nastech-ink/src/ink/layout/engine.ts +6 -0
  151. package/packages/nastech-ink/src/ink/layout/geometry.ts +98 -0
  152. package/packages/nastech-ink/src/ink/layout/node.ts +145 -0
  153. package/packages/nastech-ink/src/ink/layout/yoga.ts +313 -0
  154. package/packages/nastech-ink/src/ink/line-width-cache.ts +38 -0
  155. package/packages/nastech-ink/src/ink/log-update.test.ts +223 -0
  156. package/packages/nastech-ink/src/ink/log-update.ts +752 -0
  157. package/packages/nastech-ink/src/ink/lru.ts +14 -0
  158. package/packages/nastech-ink/src/ink/measure-element.ts +23 -0
  159. package/packages/nastech-ink/src/ink/measure-text.ts +50 -0
  160. package/packages/nastech-ink/src/ink/node-cache.ts +53 -0
  161. package/packages/nastech-ink/src/ink/optimizer.ts +99 -0
  162. package/packages/nastech-ink/src/ink/output.ts +845 -0
  163. package/packages/nastech-ink/src/ink/parse-keypress.test.ts +133 -0
  164. package/packages/nastech-ink/src/ink/parse-keypress.ts +848 -0
  165. package/packages/nastech-ink/src/ink/reconciler.ts +382 -0
  166. package/packages/nastech-ink/src/ink/render-border.ts +206 -0
  167. package/packages/nastech-ink/src/ink/render-node-to-output.ts +1582 -0
  168. package/packages/nastech-ink/src/ink/render-to-screen.ts +236 -0
  169. package/packages/nastech-ink/src/ink/renderer.ts +169 -0
  170. package/packages/nastech-ink/src/ink/root.ts +204 -0
  171. package/packages/nastech-ink/src/ink/screen.ts +1590 -0
  172. package/packages/nastech-ink/src/ink/searchHighlight.ts +91 -0
  173. package/packages/nastech-ink/src/ink/selection.test.ts +82 -0
  174. package/packages/nastech-ink/src/ink/selection.ts +1143 -0
  175. package/packages/nastech-ink/src/ink/squash-text-nodes.ts +74 -0
  176. package/packages/nastech-ink/src/ink/stringWidth.ts +341 -0
  177. package/packages/nastech-ink/src/ink/styles.ts +750 -0
  178. package/packages/nastech-ink/src/ink/supports-hyperlinks.ts +51 -0
  179. package/packages/nastech-ink/src/ink/tabstops.ts +44 -0
  180. package/packages/nastech-ink/src/ink/terminal-focus-state.ts +52 -0
  181. package/packages/nastech-ink/src/ink/terminal-querier.ts +222 -0
  182. package/packages/nastech-ink/src/ink/terminal.test.ts +15 -0
  183. package/packages/nastech-ink/src/ink/terminal.ts +299 -0
  184. package/packages/nastech-ink/src/ink/termio/ansi.ts +75 -0
  185. package/packages/nastech-ink/src/ink/termio/csi.ts +334 -0
  186. package/packages/nastech-ink/src/ink/termio/dec.ts +99 -0
  187. package/packages/nastech-ink/src/ink/termio/esc.ts +69 -0
  188. package/packages/nastech-ink/src/ink/termio/osc.test.ts +191 -0
  189. package/packages/nastech-ink/src/ink/termio/osc.ts +724 -0
  190. package/packages/nastech-ink/src/ink/termio/parser.ts +467 -0
  191. package/packages/nastech-ink/src/ink/termio/sgr.ts +362 -0
  192. package/packages/nastech-ink/src/ink/termio/tokenize.test.ts +185 -0
  193. package/packages/nastech-ink/src/ink/termio/tokenize.ts +350 -0
  194. package/packages/nastech-ink/src/ink/termio/types.ts +230 -0
  195. package/packages/nastech-ink/src/ink/termio.ts +42 -0
  196. package/packages/nastech-ink/src/ink/useTerminalNotification.ts +110 -0
  197. package/packages/nastech-ink/src/ink/warn.ts +15 -0
  198. package/packages/nastech-ink/src/ink/widest-line.ts +22 -0
  199. package/packages/nastech-ink/src/ink/wrap-text.test.ts +17 -0
  200. package/packages/nastech-ink/src/ink/wrap-text.ts +144 -0
  201. package/packages/nastech-ink/src/ink/wrapAnsi.ts +13 -0
  202. package/packages/nastech-ink/src/native-ts/yoga-layout/enums.ts +112 -0
  203. package/packages/nastech-ink/src/native-ts/yoga-layout/index.ts +2326 -0
  204. package/packages/nastech-ink/src/utils/debug.ts +6 -0
  205. package/packages/nastech-ink/src/utils/earlyInput.ts +131 -0
  206. package/packages/nastech-ink/src/utils/env.ts +66 -0
  207. package/packages/nastech-ink/src/utils/envUtils.ts +13 -0
  208. package/packages/nastech-ink/src/utils/execFileNoThrow.test.ts +146 -0
  209. package/packages/nastech-ink/src/utils/execFileNoThrow.ts +115 -0
  210. package/packages/nastech-ink/src/utils/fullscreen.ts +3 -0
  211. package/packages/nastech-ink/src/utils/intl.ts +87 -0
  212. package/packages/nastech-ink/src/utils/log.ts +7 -0
  213. package/packages/nastech-ink/src/utils/semver.ts +57 -0
  214. package/packages/nastech-ink/src/utils/sliceAnsi.ts +106 -0
  215. package/packages/nastech-ink/text-input.d.ts +2 -0
  216. package/packages/nastech-ink/text-input.js +1 -0
  217. package/scripts/build.mjs +61 -0
  218. package/scripts/profile-tui.mjs +121 -0
  219. package/src/__tests__/activeSessionSwitcher.test.ts +157 -0
  220. package/src/__tests__/appChromeStatusRule.test.tsx +84 -0
  221. package/src/__tests__/appChromeStatusRuleDevCredits.test.tsx +73 -0
  222. package/src/__tests__/approvalAction.test.ts +50 -0
  223. package/src/__tests__/asCommandDispatch.test.ts +27 -0
  224. package/src/__tests__/blockLayout.test.ts +122 -0
  225. package/src/__tests__/clipboard.test.ts +369 -0
  226. package/src/__tests__/constants.test.ts +53 -0
  227. package/src/__tests__/createGatewayEventHandler.test.ts +1091 -0
  228. package/src/__tests__/createSlashHandler.test.ts +822 -0
  229. package/src/__tests__/creditsCommand.test.ts +144 -0
  230. package/src/__tests__/cursorDriftRegression.test.ts +114 -0
  231. package/src/__tests__/details.test.ts +115 -0
  232. package/src/__tests__/emoji.test.ts +64 -0
  233. package/src/__tests__/externalLink.test.ts +144 -0
  234. package/src/__tests__/forceTruecolor.test.ts +191 -0
  235. package/src/__tests__/gatewayClient.test.ts +394 -0
  236. package/src/__tests__/gatewayRecovery.test.ts +47 -0
  237. package/src/__tests__/markdown.test.ts +331 -0
  238. package/src/__tests__/mathUnicode.test.ts +293 -0
  239. package/src/__tests__/memoryMonitor.test.ts +102 -0
  240. package/src/__tests__/messageLine.test.ts +19 -0
  241. package/src/__tests__/messages.test.ts +92 -0
  242. package/src/__tests__/orchestratorPromptSession.test.ts +64 -0
  243. package/src/__tests__/osc52.test.ts +67 -0
  244. package/src/__tests__/parentLog.test.ts +75 -0
  245. package/src/__tests__/paths.test.ts +70 -0
  246. package/src/__tests__/platform.test.ts +556 -0
  247. package/src/__tests__/precisionWheel.test.ts +44 -0
  248. package/src/__tests__/prompt.test.ts +31 -0
  249. package/src/__tests__/providers.test.ts +65 -0
  250. package/src/__tests__/reasoning.test.ts +76 -0
  251. package/src/__tests__/rpc.test.ts +27 -0
  252. package/src/__tests__/scroll.test.ts +99 -0
  253. package/src/__tests__/slashParity.test.ts +123 -0
  254. package/src/__tests__/spawnHistoryStore.test.ts +46 -0
  255. package/src/__tests__/stateIsolation.test.ts +46 -0
  256. package/src/__tests__/statusBarTicker.test.ts +18 -0
  257. package/src/__tests__/statusRule.test.ts +32 -0
  258. package/src/__tests__/streamingMarkdown.test.ts +121 -0
  259. package/src/__tests__/subagentTree.test.ts +407 -0
  260. package/src/__tests__/syntax.test.ts +45 -0
  261. package/src/__tests__/terminalModes.test.ts +39 -0
  262. package/src/__tests__/terminalParity.test.ts +77 -0
  263. package/src/__tests__/terminalSetup.test.ts +386 -0
  264. package/src/__tests__/termux.test.ts +35 -0
  265. package/src/__tests__/termuxComposerLayout.test.ts +40 -0
  266. package/src/__tests__/text.test.ts +233 -0
  267. package/src/__tests__/textInputBurstInput.test.ts +40 -0
  268. package/src/__tests__/textInputCursorSourceOfTruth.test.ts +50 -0
  269. package/src/__tests__/textInputFastEcho.test.ts +200 -0
  270. package/src/__tests__/textInputLineNav.test.ts +55 -0
  271. package/src/__tests__/textInputPassThrough.test.ts +59 -0
  272. package/src/__tests__/textInputRightClick.test.ts +48 -0
  273. package/src/__tests__/textInputWrap.test.ts +151 -0
  274. package/src/__tests__/theme.test.ts +311 -0
  275. package/src/__tests__/turnControllerNotice.test.ts +43 -0
  276. package/src/__tests__/turnStore.test.ts +66 -0
  277. package/src/__tests__/useCompletion.test.ts +35 -0
  278. package/src/__tests__/useComposerState.test.ts +59 -0
  279. package/src/__tests__/useConfigSync.test.ts +460 -0
  280. package/src/__tests__/useInputHandlers.test.ts +77 -0
  281. package/src/__tests__/useQueue.test.ts +28 -0
  282. package/src/__tests__/useSessionLifecycle.test.ts +60 -0
  283. package/src/__tests__/useVirtualHistoryHeights.test.ts +39 -0
  284. package/src/__tests__/viewport.test.ts +58 -0
  285. package/src/__tests__/viewportStore.test.ts +85 -0
  286. package/src/__tests__/virtualHeights.test.ts +96 -0
  287. package/src/__tests__/virtualHistoryClamp.test.ts +19 -0
  288. package/src/__tests__/virtualHistoryOffsetCache.test.ts +282 -0
  289. package/src/__tests__/wheelAccel.test.ts +138 -0
  290. package/src/app/createGatewayEventHandler.ts +833 -0
  291. package/src/app/createSlashHandler.ts +130 -0
  292. package/src/app/delegationStore.ts +77 -0
  293. package/src/app/gatewayContext.tsx +19 -0
  294. package/src/app/gatewayRecovery.ts +35 -0
  295. package/src/app/inputSelectionStore.ts +15 -0
  296. package/src/app/interfaces.ts +394 -0
  297. package/src/app/overlayStore.ts +53 -0
  298. package/src/app/scroll.ts +71 -0
  299. package/src/app/setupHandoff.ts +54 -0
  300. package/src/app/slash/commands/core.ts +648 -0
  301. package/src/app/slash/commands/credits.ts +57 -0
  302. package/src/app/slash/commands/debug.ts +48 -0
  303. package/src/app/slash/commands/ops.ts +717 -0
  304. package/src/app/slash/commands/session.ts +554 -0
  305. package/src/app/slash/commands/setup.ts +20 -0
  306. package/src/app/slash/registry.ts +20 -0
  307. package/src/app/slash/types.ts +21 -0
  308. package/src/app/spawnHistoryStore.ts +159 -0
  309. package/src/app/turnController.ts +866 -0
  310. package/src/app/turnStore.ts +85 -0
  311. package/src/app/uiStore.ts +44 -0
  312. package/src/app/useComposerState.ts +367 -0
  313. package/src/app/useConfigSync.ts +288 -0
  314. package/src/app/useInputHandlers.ts +576 -0
  315. package/src/app/useLongRunToolCharms.ts +69 -0
  316. package/src/app/useMainApp.ts +1039 -0
  317. package/src/app/useSessionLifecycle.ts +366 -0
  318. package/src/app/useSubmission.ts +429 -0
  319. package/src/app.tsx +25 -0
  320. package/src/banner.ts +93 -0
  321. package/src/components/activeSessionSwitcher.tsx +635 -0
  322. package/src/components/agentsOverlay.tsx +1073 -0
  323. package/src/components/appChrome.tsx +554 -0
  324. package/src/components/appLayout.tsx +444 -0
  325. package/src/components/appOverlays.tsx +254 -0
  326. package/src/components/branding.tsx +466 -0
  327. package/src/components/fpsOverlay.tsx +30 -0
  328. package/src/components/helpHint.tsx +73 -0
  329. package/src/components/markdown.tsx +1119 -0
  330. package/src/components/maskedPrompt.tsx +34 -0
  331. package/src/components/messageLine.tsx +237 -0
  332. package/src/components/modelPicker.tsx +527 -0
  333. package/src/components/overlayControls.tsx +50 -0
  334. package/src/components/pluginsHub.tsx +238 -0
  335. package/src/components/prompts.tsx +276 -0
  336. package/src/components/queuedMessages.tsx +64 -0
  337. package/src/components/sessionPicker.tsx +227 -0
  338. package/src/components/skillsHub.tsx +308 -0
  339. package/src/components/streamingAssistant.tsx +110 -0
  340. package/src/components/streamingMarkdown.tsx +174 -0
  341. package/src/components/textInput.tsx +1340 -0
  342. package/src/components/themed.tsx +30 -0
  343. package/src/components/thinking.tsx +1224 -0
  344. package/src/components/todoPanel.tsx +93 -0
  345. package/src/config/env.ts +64 -0
  346. package/src/config/limits.ts +13 -0
  347. package/src/config/timing.ts +6 -0
  348. package/src/content/charms.ts +1 -0
  349. package/src/content/faces.ts +17 -0
  350. package/src/content/fortunes.ts +30 -0
  351. package/src/content/hotkeys.ts +37 -0
  352. package/src/content/placeholders.ts +13 -0
  353. package/src/content/setup.ts +17 -0
  354. package/src/content/verbs.ts +38 -0
  355. package/src/domain/blockLayout.ts +146 -0
  356. package/src/domain/details.ts +76 -0
  357. package/src/domain/messages.ts +91 -0
  358. package/src/domain/paths.ts +16 -0
  359. package/src/domain/providers.ts +11 -0
  360. package/src/domain/roles.ts +9 -0
  361. package/src/domain/slash.ts +10 -0
  362. package/src/domain/usage.ts +3 -0
  363. package/src/domain/viewport.ts +51 -0
  364. package/src/entry.tsx +104 -0
  365. package/src/gatewayClient.ts +730 -0
  366. package/src/gatewayTypes.ts +568 -0
  367. package/src/hooks/useCompletion.ts +112 -0
  368. package/src/hooks/useGitBranch.ts +72 -0
  369. package/src/hooks/useInputHistory.ts +11 -0
  370. package/src/hooks/useQueue.ts +76 -0
  371. package/src/hooks/useVirtualHistory.ts +554 -0
  372. package/src/lib/circularBuffer.ts +48 -0
  373. package/src/lib/clipboard.ts +182 -0
  374. package/src/lib/editor.test.ts +74 -0
  375. package/src/lib/editor.ts +47 -0
  376. package/src/lib/emoji.ts +55 -0
  377. package/src/lib/externalCli.ts +16 -0
  378. package/src/lib/externalLink.ts +435 -0
  379. package/src/lib/forceTruecolor.ts +60 -0
  380. package/src/lib/fpsStore.ts +51 -0
  381. package/src/lib/fuzzy.test.ts +109 -0
  382. package/src/lib/fuzzy.ts +177 -0
  383. package/src/lib/gracefulExit.ts +47 -0
  384. package/src/lib/history.ts +82 -0
  385. package/src/lib/inputMetrics.ts +203 -0
  386. package/src/lib/liveProgress.test.ts +116 -0
  387. package/src/lib/liveProgress.ts +79 -0
  388. package/src/lib/mathUnicode.ts +770 -0
  389. package/src/lib/memory.test.ts +155 -0
  390. package/src/lib/memory.ts +188 -0
  391. package/src/lib/memoryMonitor.ts +109 -0
  392. package/src/lib/messages.test.ts +29 -0
  393. package/src/lib/messages.ts +8 -0
  394. package/src/lib/openExternalUrl.test.ts +217 -0
  395. package/src/lib/openExternalUrl.ts +158 -0
  396. package/src/lib/osc52.ts +73 -0
  397. package/src/lib/parentLog.ts +57 -0
  398. package/src/lib/perfPane.tsx +107 -0
  399. package/src/lib/platform.ts +409 -0
  400. package/src/lib/precisionWheel.ts +48 -0
  401. package/src/lib/prompt.ts +35 -0
  402. package/src/lib/reasoning.ts +55 -0
  403. package/src/lib/rpc.ts +41 -0
  404. package/src/lib/subagentTree.ts +355 -0
  405. package/src/lib/syntax.ts +117 -0
  406. package/src/lib/terminalModes.ts +51 -0
  407. package/src/lib/terminalParity.ts +78 -0
  408. package/src/lib/terminalSetup.ts +444 -0
  409. package/src/lib/termux.ts +29 -0
  410. package/src/lib/text.test.ts +18 -0
  411. package/src/lib/text.ts +339 -0
  412. package/src/lib/todo.test.ts +21 -0
  413. package/src/lib/todo.ts +9 -0
  414. package/src/lib/viewportStore.ts +124 -0
  415. package/src/lib/virtualHeights.ts +145 -0
  416. package/src/lib/wheelAccel.ts +190 -0
  417. package/src/protocol/interpolation.ts +3 -0
  418. package/src/protocol/paste.ts +1 -0
  419. package/src/theme.ts +589 -0
  420. package/src/types/nastech-ink.d.ts +176 -0
  421. package/src/types.ts +212 -0
  422. package/tsconfig.build.json +9 -0
  423. package/tsconfig.json +19 -0
  424. package/vitest.config.ts +7 -0
@@ -0,0 +1,717 @@
1
+ import type {
2
+ BrowserManageResponse,
3
+ CommandsCatalogResponse,
4
+ DelegationPauseResponse,
5
+ ProcessStopResponse,
6
+ ReloadEnvResponse,
7
+ ReloadMcpResponse,
8
+ RollbackDiffResponse,
9
+ RollbackListResponse,
10
+ RollbackRestoreResponse,
11
+ SlashExecResponse,
12
+ SpawnTreeListResponse,
13
+ SpawnTreeLoadResponse,
14
+ ToolsConfigureResponse
15
+ } from '../../../gatewayTypes.js'
16
+ import type { PanelSection } from '../../../types.js'
17
+ import { applyDelegationStatus, getDelegationState } from '../../delegationStore.js'
18
+ import { patchOverlayState } from '../../overlayStore.js'
19
+ import { getSpawnHistory, pushDiskSnapshot, setDiffPair, type SpawnSnapshot } from '../../spawnHistoryStore.js'
20
+ import type { SlashCommand } from '../types.js'
21
+
22
+ interface SkillInfo {
23
+ category?: string
24
+ description?: string
25
+ name?: string
26
+ path?: string
27
+ }
28
+
29
+ interface SkillsListResponse {
30
+ skills?: Record<string, string[]>
31
+ }
32
+
33
+ interface SkillsInspectResponse {
34
+ info?: SkillInfo
35
+ }
36
+
37
+ interface SkillsSearchResponse {
38
+ results?: { description?: string; name: string }[]
39
+ }
40
+
41
+ interface SkillsInstallResponse {
42
+ installed?: boolean
43
+ name?: string
44
+ }
45
+
46
+ interface SkillsBrowseItem {
47
+ description?: string
48
+ name: string
49
+ source?: string
50
+ trust?: string
51
+ }
52
+
53
+ interface SkillsBrowseResponse {
54
+ items?: SkillsBrowseItem[]
55
+ page?: number
56
+ total?: number
57
+ total_pages?: number
58
+ }
59
+
60
+ interface SkillsReloadResponse {
61
+ output?: string
62
+ }
63
+
64
+ export const opsCommands: SlashCommand[] = [
65
+ {
66
+ help: 'stop background processes',
67
+ name: 'stop',
68
+ run: (_arg, ctx) => {
69
+ ctx.gateway
70
+ .rpc<ProcessStopResponse>('process.stop', {})
71
+ .then(
72
+ ctx.guarded<ProcessStopResponse>(r => {
73
+ const killed = Number(r.killed ?? 0)
74
+ const noun = killed === 1 ? 'process' : 'processes'
75
+ ctx.transcript.sys(`stopped ${killed} background ${noun}`)
76
+ })
77
+ )
78
+ .catch(ctx.guardedErr)
79
+ }
80
+ },
81
+
82
+ {
83
+ aliases: ['reload_mcp'],
84
+ help: 'reload MCP servers in the live session (warns about prompt cache invalidation)',
85
+ name: 'reload-mcp',
86
+ run: (arg, ctx) => {
87
+ // Parse arg: `now` / `always` skip the confirmation gate.
88
+ // `always` additionally persists approvals.mcp_reload_confirm=false.
89
+ const a = (arg || '').trim().toLowerCase()
90
+ const params: { session_id: string | null; confirm?: boolean; always?: boolean } = {
91
+ session_id: ctx.sid
92
+ }
93
+ if (a === 'now' || a === 'approve' || a === 'once' || a === 'yes') {
94
+ params.confirm = true
95
+ } else if (a === 'always') {
96
+ params.confirm = true
97
+ params.always = true
98
+ }
99
+
100
+ ctx.gateway
101
+ .rpc<ReloadMcpResponse>('reload.mcp', params)
102
+ .then(
103
+ ctx.guarded<ReloadMcpResponse>(r => {
104
+ if (r.status === 'confirm_required') {
105
+ ctx.transcript.sys(r.message || '/reload-mcp requires confirmation')
106
+ return
107
+ }
108
+ if (r.status === 'reloaded') {
109
+ ctx.transcript.sys(
110
+ params.always
111
+ ? 'MCP servers reloaded · future /reload-mcp will run without confirmation'
112
+ : 'MCP servers reloaded'
113
+ )
114
+ return
115
+ }
116
+ ctx.transcript.sys('reload complete')
117
+ })
118
+ )
119
+ .catch(ctx.guardedErr)
120
+ }
121
+ },
122
+
123
+ {
124
+ help: 're-read ~/.nastech/.env into the running gateway (CLI parity)',
125
+ name: 'reload',
126
+ run: (_arg, ctx) => {
127
+ ctx.gateway
128
+ .rpc<ReloadEnvResponse>('reload.env', {})
129
+ .then(
130
+ ctx.guarded<ReloadEnvResponse>(r => {
131
+ const n = Number(r.updated ?? 0)
132
+ const noun = n === 1 ? 'var' : 'vars'
133
+
134
+ ctx.transcript.sys(`reloaded .env (${n} ${noun} updated)`)
135
+ })
136
+ )
137
+ .catch(ctx.guardedErr)
138
+ }
139
+ },
140
+
141
+ {
142
+ help: 'manage browser CDP connection [connect|disconnect|status]',
143
+ name: 'browser',
144
+ run: (arg, ctx) => {
145
+ const [rawAction = 'status', ...rest] = arg.trim().split(/\s+/).filter(Boolean)
146
+ const action = rawAction.toLowerCase()
147
+
148
+ if (!['connect', 'disconnect', 'status'].includes(action)) {
149
+ return ctx.transcript.sys(
150
+ 'usage: /browser [connect|disconnect|status] [url] · persistent: set browser.cdp_url in config.yaml'
151
+ )
152
+ }
153
+
154
+ const sid = ctx.sid ?? null
155
+ const url = action === 'connect' ? rest.join(' ').trim() || 'http://127.0.0.1:9222' : undefined
156
+
157
+ if (url) {
158
+ ctx.transcript.sys(`checking Chromium-family browser remote debugging at ${url}...`)
159
+ }
160
+
161
+ ctx.gateway
162
+ .rpc<BrowserManageResponse>('browser.manage', { action, session_id: sid, ...(url && { url }) })
163
+ .then(
164
+ ctx.guarded<BrowserManageResponse>(r => {
165
+ // Without a session we can't subscribe to streamed
166
+ // browser.progress events, so flush the bundled list.
167
+ if (!sid) {
168
+ r.messages?.forEach(message => ctx.transcript.sys(message))
169
+ }
170
+
171
+ if (action === 'status') {
172
+ return ctx.transcript.sys(
173
+ r.connected
174
+ ? `browser connected: ${r.url || '(url unavailable)'}`
175
+ : 'browser not connected (try /browser connect <url> or set browser.cdp_url in config.yaml)'
176
+ )
177
+ }
178
+
179
+ if (action === 'disconnect') {
180
+ return ctx.transcript.sys('browser disconnected')
181
+ }
182
+
183
+ if (r.connected) {
184
+ ctx.transcript.sys('Browser connected to live Chromium-family browser via CDP')
185
+ ctx.transcript.sys(`Endpoint: ${r.url || '(url unavailable)'}`)
186
+ ctx.transcript.sys('next browser tool call will use this CDP endpoint')
187
+ }
188
+ })
189
+ )
190
+ .catch(ctx.guardedErr)
191
+ }
192
+ },
193
+
194
+ {
195
+ help: 'list, diff, or restore checkpoints',
196
+ name: 'rollback',
197
+ run: (arg, ctx) => {
198
+ if (!ctx.sid) {
199
+ return ctx.transcript.sys('no active session — nothing to rollback')
200
+ }
201
+
202
+ const trimmed = arg.trim()
203
+ const [first = '', ...rest] = trimmed.split(/\s+/).filter(Boolean)
204
+ const lower = first.toLowerCase()
205
+
206
+ if (!trimmed || lower === 'list' || lower === 'ls') {
207
+ return ctx.gateway
208
+ .rpc<RollbackListResponse>('rollback.list', { session_id: ctx.sid })
209
+ .then(
210
+ ctx.guarded<RollbackListResponse>(r => {
211
+ if (!r.enabled) {
212
+ return ctx.transcript.sys('checkpoints are not enabled')
213
+ }
214
+
215
+ const checkpoints = r.checkpoints ?? []
216
+
217
+ if (!checkpoints.length) {
218
+ return ctx.transcript.sys('no checkpoints found')
219
+ }
220
+
221
+ ctx.transcript.panel('Rollback checkpoints', [
222
+ {
223
+ rows: checkpoints.map((c, idx) => [
224
+ `${idx + 1}. ${c.hash.slice(0, 10)}`,
225
+ [c.timestamp, c.message].filter(Boolean).join(' · ') || '(no metadata)'
226
+ ])
227
+ }
228
+ ])
229
+ })
230
+ )
231
+ .catch(ctx.guardedErr)
232
+ }
233
+
234
+ if (lower === 'diff') {
235
+ const hash = rest[0]
236
+
237
+ if (!hash) {
238
+ return ctx.transcript.sys('usage: /rollback diff <checkpoint>')
239
+ }
240
+
241
+ return ctx.gateway
242
+ .rpc<RollbackDiffResponse>('rollback.diff', { hash, session_id: ctx.sid })
243
+ .then(
244
+ ctx.guarded<RollbackDiffResponse>(r => {
245
+ const body = (r.rendered || r.diff || '').trim()
246
+
247
+ if (!body && !r.stat) {
248
+ return ctx.transcript.sys('no changes since this checkpoint')
249
+ }
250
+
251
+ const text = [r.stat || '', body].filter(Boolean).join('\n\n')
252
+ ctx.transcript.page(text, 'Rollback diff')
253
+ })
254
+ )
255
+ .catch(ctx.guardedErr)
256
+ }
257
+
258
+ const hash = first
259
+ const filePath = rest.join(' ').trim()
260
+
261
+ return ctx.gateway
262
+ .rpc<RollbackRestoreResponse>('rollback.restore', {
263
+ ...(filePath ? { file_path: filePath } : {}),
264
+ hash,
265
+ session_id: ctx.sid
266
+ })
267
+ .then(
268
+ ctx.guarded<RollbackRestoreResponse>(r => {
269
+ if (!r.success) {
270
+ return ctx.transcript.sys(`rollback failed: ${r.error || r.message || 'unknown error'}`)
271
+ }
272
+
273
+ const target = filePath || 'workspace'
274
+ const detail = r.reason || r.message || r.restored_to || 'restored'
275
+ ctx.transcript.sys(`rollback restored ${target}: ${detail}`)
276
+
277
+ if ((r.history_removed ?? 0) > 0) {
278
+ ctx.transcript.setHistoryItems(prev => ctx.transcript.trimLastExchange(prev))
279
+ }
280
+ })
281
+ )
282
+ .catch(ctx.guardedErr)
283
+ }
284
+ },
285
+
286
+ {
287
+ aliases: ['tasks'],
288
+ help: 'open the spawn-tree dashboard (live audit + kill/pause controls)',
289
+ name: 'agents',
290
+ run: (arg, ctx) => {
291
+ const sub = arg.trim().toLowerCase()
292
+
293
+ // Stay compatible with the gateway `/agents [pause|resume|status]` CLI —
294
+ // explicit subcommands skip the overlay and act directly so scripts and
295
+ // multi-step flows can drive it without entering interactive mode.
296
+ if (sub === 'pause' || sub === 'resume' || sub === 'unpause') {
297
+ const paused = sub === 'pause'
298
+ ctx.gateway.gw
299
+ .request<DelegationPauseResponse>('delegation.pause', { paused })
300
+ .then(r => {
301
+ applyDelegationStatus({ paused: r?.paused })
302
+ ctx.transcript.sys(`delegation · ${r?.paused ? 'paused' : 'resumed'}`)
303
+ })
304
+ .catch(ctx.guardedErr)
305
+
306
+ return
307
+ }
308
+
309
+ if (sub === 'status') {
310
+ const d = getDelegationState()
311
+ ctx.transcript.sys(
312
+ `delegation · ${d.paused ? 'paused' : 'active'} · caps d${d.maxSpawnDepth ?? '?'}/${d.maxConcurrentChildren ?? '?'}`
313
+ )
314
+
315
+ return
316
+ }
317
+
318
+ patchOverlayState({ agents: true, agentsInitialHistoryIndex: 0 })
319
+ }
320
+ },
321
+
322
+ {
323
+ help: 'replay a completed spawn tree · `/replay [N|last|list|load <path>]`',
324
+ name: 'replay',
325
+ run: (arg, ctx) => {
326
+ const history = getSpawnHistory()
327
+ const raw = arg.trim()
328
+ const lower = raw.toLowerCase()
329
+
330
+ // ── Disk-backed listing ─────────────────────────────────────
331
+ if (lower === 'list' || lower === 'ls') {
332
+ ctx.gateway
333
+ .rpc<SpawnTreeListResponse>('spawn_tree.list', {
334
+ limit: 30,
335
+ session_id: ctx.sid ?? 'default'
336
+ })
337
+ .then(
338
+ ctx.guarded<SpawnTreeListResponse>(r => {
339
+ const entries = r.entries ?? []
340
+
341
+ if (!entries.length) {
342
+ return ctx.transcript.sys('no archived spawn trees on disk for this session')
343
+ }
344
+
345
+ const rows: [string, string][] = entries.map(e => {
346
+ const ts = e.finished_at ? new Date(e.finished_at * 1000).toLocaleString() : '?'
347
+ const label = e.label || `${e.count} subagents`
348
+
349
+ return [`${ts} · ${e.count}×`, `${label}\n ${e.path}`]
350
+ })
351
+
352
+ ctx.transcript.panel('Archived spawn trees', [{ rows }])
353
+ })
354
+ )
355
+ .catch(ctx.guardedErr)
356
+
357
+ return
358
+ }
359
+
360
+ // ── Disk-backed load by path ─────────────────────────────────
361
+ if (lower.startsWith('load ')) {
362
+ const path = raw.slice(5).trim()
363
+
364
+ if (!path) {
365
+ return ctx.transcript.sys('usage: /replay load <path>')
366
+ }
367
+
368
+ ctx.gateway
369
+ .rpc<SpawnTreeLoadResponse>('spawn_tree.load', { path })
370
+ .then(
371
+ ctx.guarded<SpawnTreeLoadResponse>(r => {
372
+ if (!r.subagents?.length) {
373
+ return ctx.transcript.sys('snapshot empty or unreadable')
374
+ }
375
+
376
+ // Push onto the in-memory history so the overlay picks it up
377
+ // by index 1 just like any other snapshot.
378
+ pushDiskSnapshot(r, path)
379
+ patchOverlayState({ agents: true, agentsInitialHistoryIndex: 1 })
380
+ })
381
+ )
382
+ .catch(ctx.guardedErr)
383
+
384
+ return
385
+ }
386
+
387
+ // ── In-memory nav (same-session) ─────────────────────────────
388
+ if (!history.length) {
389
+ return ctx.transcript.sys('no completed spawn trees this session · try /replay list')
390
+ }
391
+
392
+ let index = 1
393
+
394
+ if (raw && lower !== 'last') {
395
+ const parsed = parseInt(raw, 10)
396
+
397
+ if (Number.isNaN(parsed) || parsed < 1 || parsed > history.length) {
398
+ return ctx.transcript.sys(`replay: index out of range 1..${history.length} · use /replay list for disk`)
399
+ }
400
+
401
+ index = parsed
402
+ }
403
+
404
+ patchOverlayState({ agents: true, agentsInitialHistoryIndex: index })
405
+ }
406
+ },
407
+
408
+ {
409
+ help: 'diff two completed spawn trees · `/replay-diff <baseline> <candidate>` (indexes from /replay list or history N)',
410
+ name: 'replay-diff',
411
+ run: (arg, ctx) => {
412
+ const parts = arg.trim().split(/\s+/).filter(Boolean)
413
+
414
+ if (parts.length !== 2) {
415
+ return ctx.transcript.sys('usage: /replay-diff <a> <b> (e.g. /replay-diff 1 2 for last two)')
416
+ }
417
+
418
+ const [a, b] = parts
419
+ const history = getSpawnHistory()
420
+
421
+ const resolve = (token: string): null | SpawnSnapshot => {
422
+ const n = parseInt(token!, 10)
423
+
424
+ if (Number.isFinite(n) && n >= 1 && n <= history.length) {
425
+ return history[n - 1] ?? null
426
+ }
427
+
428
+ return null
429
+ }
430
+
431
+ const baseline = resolve(a!)
432
+ const candidate = resolve(b!)
433
+
434
+ if (!baseline || !candidate) {
435
+ return ctx.transcript.sys(`replay-diff: could not resolve indices · history has ${history.length} entries`)
436
+ }
437
+
438
+ setDiffPair({ baseline, candidate })
439
+ patchOverlayState({ agents: true, agentsInitialHistoryIndex: 0 })
440
+ }
441
+ },
442
+
443
+ {
444
+ aliases: ['reload_skills'],
445
+ help: 're-scan installed skills in the live TUI gateway',
446
+ name: 'reload-skills',
447
+ run: (_arg, ctx) => {
448
+ ctx.gateway
449
+ .rpc<SkillsReloadResponse>('skills.reload', {})
450
+ .then(
451
+ ctx.guarded<SkillsReloadResponse>(r => {
452
+ ctx.transcript.page(r.output || 'skills reloaded', 'Reload Skills')
453
+ ctx.gateway
454
+ .rpc<CommandsCatalogResponse>('commands.catalog', {})
455
+ .then(
456
+ ctx.guarded<CommandsCatalogResponse>(catalog => {
457
+ if (!catalog?.pairs) {
458
+ return
459
+ }
460
+
461
+ ctx.local.setCatalog({
462
+ canon: (catalog.canon ?? {}) as Record<string, string>,
463
+ categories: catalog.categories ?? [],
464
+ pairs: catalog.pairs as [string, string][],
465
+ skillCount: (catalog.skill_count ?? 0) as number,
466
+ sub: (catalog.sub ?? {}) as Record<string, string[]>
467
+ })
468
+ })
469
+ )
470
+ .catch(() => {})
471
+ })
472
+ )
473
+ .catch(ctx.guardedErr)
474
+ }
475
+ },
476
+
477
+ {
478
+ help: 'browse, inspect, install skills',
479
+ name: 'skills',
480
+ run: (arg, ctx, cmd) => {
481
+ const text = arg.trim()
482
+
483
+ if (!text) {
484
+ return patchOverlayState({ skillsHub: true })
485
+ }
486
+
487
+ const [sub, ...rest] = text.split(/\s+/)
488
+ const query = rest.join(' ').trim()
489
+ const { rpc } = ctx.gateway
490
+ const { panel, sys } = ctx.transcript
491
+ const runViaSlashWorker = () => {
492
+ ctx.gateway.gw
493
+ .request<SlashExecResponse>('slash.exec', { command: cmd.slice(1), session_id: ctx.sid })
494
+ .then(r => {
495
+ if (ctx.stale()) {
496
+ return
497
+ }
498
+
499
+ const body = r?.output || '/skills: no output'
500
+ const formatted = r?.warning ? `warning: ${r.warning}\n${body}` : body
501
+ const long = formatted.length > 180 || formatted.split('\n').filter(Boolean).length > 2
502
+
503
+ long ? ctx.transcript.page(formatted, 'Skills') : ctx.transcript.sys(formatted)
504
+ })
505
+ .catch(ctx.guardedErr)
506
+ }
507
+
508
+ if (sub === 'list') {
509
+ rpc<SkillsListResponse>('skills.manage', { action: 'list' })
510
+ .then(
511
+ ctx.guarded<SkillsListResponse>(r => {
512
+ const cats = Object.entries(r.skills ?? {}).sort()
513
+
514
+ if (!cats.length) {
515
+ return sys('no skills available')
516
+ }
517
+
518
+ panel(
519
+ 'Skills',
520
+ cats.map<PanelSection>(([title, items]) => ({ items, title }))
521
+ )
522
+ })
523
+ )
524
+ .catch(ctx.guardedErr)
525
+
526
+ return
527
+ }
528
+
529
+ if (sub === 'inspect') {
530
+ if (!query) {
531
+ return sys('usage: /skills inspect <name>')
532
+ }
533
+
534
+ rpc<SkillsInspectResponse>('skills.manage', { action: 'inspect', query })
535
+ .then(
536
+ ctx.guarded<SkillsInspectResponse>(r => {
537
+ const info = r.info ?? {}
538
+
539
+ if (!info.name) {
540
+ return sys(`unknown skill: ${query}`)
541
+ }
542
+
543
+ const rows: [string, string][] = [
544
+ ['Name', String(info.name)],
545
+ ['Category', String(info.category ?? '')],
546
+ ['Path', String(info.path ?? '')]
547
+ ]
548
+
549
+ const sections: PanelSection[] = [{ rows }]
550
+
551
+ if (info.description) {
552
+ sections.push({ text: String(info.description) })
553
+ }
554
+
555
+ panel('Skill', sections)
556
+ })
557
+ )
558
+ .catch(ctx.guardedErr)
559
+
560
+ return
561
+ }
562
+
563
+ if (sub === 'search') {
564
+ if (!query) {
565
+ return sys('usage: /skills search <query>')
566
+ }
567
+
568
+ rpc<SkillsSearchResponse>('skills.manage', { action: 'search', query })
569
+ .then(
570
+ ctx.guarded<SkillsSearchResponse>(r => {
571
+ const results = r.results ?? []
572
+
573
+ if (!results.length) {
574
+ return sys(`no results for: ${query}`)
575
+ }
576
+
577
+ panel(`Search: ${query}`, [{ rows: results.map(s => [s.name, s.description ?? '']) }])
578
+ })
579
+ )
580
+ .catch(ctx.guardedErr)
581
+
582
+ return
583
+ }
584
+
585
+ if (sub === 'install') {
586
+ if (!query) {
587
+ return sys('usage: /skills install <name or url>')
588
+ }
589
+
590
+ sys(`installing ${query}…`)
591
+
592
+ rpc<SkillsInstallResponse>('skills.manage', { action: 'install', query })
593
+ .then(
594
+ ctx.guarded<SkillsInstallResponse>(r =>
595
+ sys(r.installed ? `installed ${r.name ?? query}` : 'install failed')
596
+ )
597
+ )
598
+ .catch(ctx.guardedErr)
599
+
600
+ return
601
+ }
602
+
603
+ if (sub === 'browse') {
604
+ const pageNum = query ? parseInt(query, 10) : 1
605
+
606
+ if (Number.isNaN(pageNum) || pageNum < 1) {
607
+ return sys('usage: /skills browse [page] (page must be a positive number)')
608
+ }
609
+
610
+ sys('fetching community skills (scans 6 sources, may take ~15s)…')
611
+
612
+ rpc<SkillsBrowseResponse>('skills.manage', { action: 'browse', page: pageNum })
613
+ .then(
614
+ ctx.guarded<SkillsBrowseResponse>(r => {
615
+ const items = r.items ?? []
616
+
617
+ if (!items.length) {
618
+ return sys(`no skills on page ${pageNum}${r.total ? ` (total ${r.total})` : ''}`)
619
+ }
620
+
621
+ const rows: [string, string][] = items.map(s => [
622
+ s.trust ? `${s.name} · ${s.trust}` : s.name,
623
+ String(s.description ?? '').slice(0, 160)
624
+ ])
625
+
626
+ const footer: string[] = []
627
+
628
+ if (r.page && r.total_pages) {
629
+ footer.push(`page ${r.page} of ${r.total_pages}`)
630
+ }
631
+
632
+ if (r.total) {
633
+ footer.push(`${r.total} skills total`)
634
+ }
635
+
636
+ if (r.page && r.total_pages && r.page < r.total_pages) {
637
+ footer.push(`/skills browse ${r.page + 1} for more`)
638
+ }
639
+
640
+ panel(`Browse Skills${pageNum > 1 ? ` — p${pageNum}` : ''}`, [
641
+ { rows },
642
+ ...(footer.length ? [{ text: footer.join(' · ') }] : [])
643
+ ])
644
+ })
645
+ )
646
+ .catch(ctx.guardedErr)
647
+
648
+ return
649
+ }
650
+
651
+ runViaSlashWorker()
652
+ }
653
+ },
654
+
655
+ {
656
+ help: 'enable or disable tools (client-side history reset on change)',
657
+ name: 'tools',
658
+ run: (arg, ctx, cmd) => {
659
+ const [subcommand, ...names] = arg.trim().split(/\s+/).filter(Boolean)
660
+
661
+ if (subcommand !== 'disable' && subcommand !== 'enable') {
662
+ ctx.gateway.gw
663
+ .request<SlashExecResponse>('slash.exec', { command: cmd.slice(1), session_id: ctx.sid })
664
+ .then(r => {
665
+ if (ctx.stale()) {
666
+ return
667
+ }
668
+
669
+ const body = r?.output || '/tools: no output'
670
+ const text = r?.warning ? `warning: ${r.warning}\n${body}` : body
671
+ const long = text.length > 180 || text.split('\n').filter(Boolean).length > 2
672
+
673
+ long ? ctx.transcript.page(text, 'Tools') : ctx.transcript.sys(text)
674
+ })
675
+ .catch(ctx.guardedErr)
676
+
677
+ return
678
+ }
679
+
680
+ if (!names.length) {
681
+ ctx.transcript.sys(`usage: /tools ${subcommand} <name> [name ...]`)
682
+ ctx.transcript.sys(`built-in toolset: /tools ${subcommand} web`)
683
+ ctx.transcript.sys(`MCP tool: /tools ${subcommand} github:create_issue`)
684
+
685
+ return
686
+ }
687
+
688
+ ctx.gateway
689
+ .rpc<ToolsConfigureResponse>('tools.configure', { action: subcommand, names, session_id: ctx.sid })
690
+ .then(
691
+ ctx.guarded<ToolsConfigureResponse>(r => {
692
+ if (r.info) {
693
+ ctx.session.setSessionStartedAt(Date.now())
694
+ ctx.session.resetVisibleHistory(r.info)
695
+ }
696
+
697
+ if (r.changed?.length) {
698
+ ctx.transcript.sys(`${subcommand === 'disable' ? 'disabled' : 'enabled'}: ${r.changed.join(', ')}`)
699
+ }
700
+
701
+ if (r.unknown?.length) {
702
+ ctx.transcript.sys(`unknown toolsets: ${r.unknown.join(', ')}`)
703
+ }
704
+
705
+ if (r.missing_servers?.length) {
706
+ ctx.transcript.sys(`missing MCP servers: ${r.missing_servers.join(', ')}`)
707
+ }
708
+
709
+ if (r.reset) {
710
+ ctx.transcript.sys('session reset. new tool configuration is active.')
711
+ }
712
+ })
713
+ )
714
+ .catch(ctx.guardedErr)
715
+ }
716
+ }
717
+ ]